diff --git a/kmymoney/converter/mymoneygncreader.cpp b/kmymoney/converter/mymoneygncreader.cpp index 49e7ca76c..9bc30b860 100644 --- a/kmymoney/converter/mymoneygncreader.cpp +++ b/kmymoney/converter/mymoneygncreader.cpp @@ -1,2651 +1,2652 @@ /*************************************************************************** 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); + 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/converter/mymoneystatementreader.cpp b/kmymoney/converter/mymoneystatementreader.cpp index 1e5611d56..0a91d6302 100644 --- a/kmymoney/converter/mymoneystatementreader.cpp +++ b/kmymoney/converter/mymoneystatementreader.cpp @@ -1,1547 +1,1547 @@ /*************************************************************************** mymoneystatementreader.cpp ------------------- begin : Mon Aug 30 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio Ace Jones ***************************************************************************/ /*************************************************************************** * * * 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 "mymoneystatementreader.h" #include // ---------------------------------------------------------------------------- // QT Headers #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Headers #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Headers #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneyprice.h" #include "mymoneytransactionfilter.h" #include "mymoneypayee.h" #include "mymoneystatement.h" #include "kmymoneyglobalsettings.h" #include "transactioneditor.h" #include "stdtransactioneditor.h" #include "kmymoneyedit.h" #include "kaccountselectdlg.h" #include "transactionmatcher.h" #include "kenterscheduledlg.h" #include "kmymoney.h" #include "kmymoneyaccountcombo.h" #include "accountsmodel.h" #include "models.h" #include "existingtransactionmatchfinder.h" #include "scheduledtransactionmatchfinder.h" #include "dialogenums.h" using namespace eMyMoney; bool matchNotEmpty(const QString &l, const QString &r) { return !l.isEmpty() && QString::compare(l, r, Qt::CaseInsensitive) == 0; } class MyMoneyStatementReader::Private { public: Private() : transactionsCount(0), transactionsAdded(0), transactionsMatched(0), transactionsDuplicate(0), scannedCategories(false) {} const QString& feeId(const MyMoneyAccount& invAcc); const QString& interestId(const MyMoneyAccount& invAcc); QString interestId(const QString& name); QString expenseId(const QString& name); QString feeId(const QString& name); void assignUniqueBankID(MyMoneySplit& s, const MyMoneyStatement::Transaction& t_in); void setupPrice(MyMoneySplit &s, const MyMoneyAccount &splitAccount, const MyMoneyAccount &transactionAccount, const QDate &postDate); MyMoneyAccount lastAccount; MyMoneyAccount m_account; MyMoneyAccount m_brokerageAccount; QList transactions; QList payees; int transactionsCount; int transactionsAdded; int transactionsMatched; int transactionsDuplicate; QMap uniqIds; QMap securitiesBySymbol; QMap securitiesByName; bool m_skipCategoryMatching; private: void scanCategories(QString& id, const MyMoneyAccount& invAcc, const MyMoneyAccount& parentAccount, const QString& defaultName); /** * This method tries to figure out the category to be used for fees and interest * from previous transactions in the given @a investmentAccount and returns the * ids of those categories in @a feesId and @a interestId. The last used category * will be returned. */ void previouslyUsedCategories(const QString& investmentAccount, QString& feesId, QString& interestId); QString nameToId(const QString&name, MyMoneyAccount& parent); private: QString m_feeId; QString m_interestId; bool scannedCategories; }; const QString& MyMoneyStatementReader::Private::feeId(const MyMoneyAccount& invAcc) { scanCategories(m_feeId, invAcc, MyMoneyFile::instance()->expense(), i18n("_Fees")); return m_feeId; } const QString& MyMoneyStatementReader::Private::interestId(const MyMoneyAccount& invAcc) { scanCategories(m_interestId, invAcc, MyMoneyFile::instance()->income(), i18n("_Dividend")); return m_interestId; } QString MyMoneyStatementReader::Private::nameToId(const QString& name, MyMoneyAccount& parent) { // Adapted from KMyMoneyApp::createAccount(MyMoneyAccount& newAccount, MyMoneyAccount& parentAccount, MyMoneyAccount& brokerageAccount, MyMoneyMoney openingBal) // Needed to find/create category:sub-categories MyMoneyFile* file = MyMoneyFile::instance(); QString id = file->categoryToAccount(name, Account::Unknown); // if it does not exist, we have to create it if (id.isEmpty()) { MyMoneyAccount newAccount; MyMoneyAccount parentAccount = parent; newAccount.setName(name) ; int pos; // check for ':' in the name and use it as separator for a hierarchy while ((pos = newAccount.name().indexOf(MyMoneyFile::AccountSeperator)) != -1) { QString part = newAccount.name().left(pos); QString remainder = newAccount.name().mid(pos + 1); const MyMoneyAccount& existingAccount = file->subAccountByName(parentAccount, part); if (existingAccount.id().isEmpty()) { newAccount.setName(part); newAccount.setAccountType(parentAccount.accountType()); file->addAccount(newAccount, parentAccount); parentAccount = newAccount; } else { parentAccount = existingAccount; } newAccount.setParentAccountId(QString()); // make sure, there's no parent newAccount.clearId(); // and no id set for adding newAccount.removeAccountIds(); // and no sub-account ids newAccount.setName(remainder); }//end while newAccount.setAccountType(parentAccount.accountType()); // make sure we have a currency. If none is assigned, we assume base currency if (newAccount.currencyId().isEmpty()) newAccount.setCurrencyId(file->baseCurrency().id()); file->addAccount(newAccount, parentAccount); id = newAccount.id(); } return id; } QString MyMoneyStatementReader::Private::expenseId(const QString& name) { MyMoneyAccount parent = MyMoneyFile::instance()->expense(); return nameToId(name, parent); } QString MyMoneyStatementReader::Private::interestId(const QString& name) { MyMoneyAccount parent = MyMoneyFile::instance()->income(); return nameToId(name, parent); } QString MyMoneyStatementReader::Private::feeId(const QString& name) { MyMoneyAccount parent = MyMoneyFile::instance()->expense(); return nameToId(name, parent); } void MyMoneyStatementReader::Private::previouslyUsedCategories(const QString& investmentAccount, QString& feesId, QString& interestId) { feesId.clear(); interestId.clear(); MyMoneyFile* file = MyMoneyFile::instance(); try { MyMoneyAccount acc = file->account(investmentAccount); MyMoneyTransactionFilter filter(investmentAccount); filter.setReportAllSplits(false); // since we assume an investment account here, we need to collect the stock accounts as well filter.addAccount(acc.accountList()); QList< QPair > list; file->transactionList(list, filter); QList< QPair >::const_iterator it_t; for (it_t = list.constBegin(); it_t != list.constEnd(); ++it_t) { const MyMoneyTransaction& t = (*it_t).first; MyMoneySplit s = (*it_t).second; MyMoneyAccount acc = file->account(s.accountId()); // stock split shouldn't be fee or interest bacause it won't play nice with dissectTransaction // it was caused by processTransactionEntry adding splits in wrong order != with manual transaction entering if (acc.accountGroup() == Account::Expense || acc.accountGroup() == Account::Income) { foreach (auto sNew , t.splits()) { acc = file->account(sNew.accountId()); if (acc.accountGroup() != Account::Expense && // shouldn't be fee acc.accountGroup() != Account::Income && // shouldn't be interest (sNew.value() != sNew.shares() || // shouldn't be checking account... (sNew.value() == sNew.shares() && sNew.price() != MyMoneyMoney::ONE))) { // ...but sometimes it may look like checking account s = sNew; break; } } } MyMoneySplit assetAccountSplit; QList feeSplits; QList interestSplits; MyMoneySecurity security; MyMoneySecurity currency; eMyMoney::Split::InvestmentTransactionType transactionType; KMyMoneyUtils::dissectTransaction(t, s, assetAccountSplit, feeSplits, interestSplits, security, currency, transactionType); if (!feeSplits.isEmpty()) { feesId = feeSplits.first().accountId(); if (!interestId.isEmpty()) break; } if (!interestSplits.isEmpty()) { interestId = interestSplits.first().accountId(); if (!feesId.isEmpty()) break; } } } catch (const MyMoneyException &) { } } void MyMoneyStatementReader::Private::scanCategories(QString& id, const MyMoneyAccount& invAcc, const MyMoneyAccount& parentAccount, const QString& defaultName) { if (!scannedCategories) { previouslyUsedCategories(invAcc.id(), m_feeId, m_interestId); scannedCategories = true; } if (id.isEmpty()) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyAccount acc = file->accountByName(defaultName); // if it does not exist, we have to create it if (acc.id().isEmpty()) { MyMoneyAccount parent = parentAccount; acc.setName(defaultName); acc.setAccountType(parent.accountType()); acc.setCurrencyId(parent.currencyId()); file->addAccount(acc, parent); } id = acc.id(); } } void MyMoneyStatementReader::Private::assignUniqueBankID(MyMoneySplit& s, const MyMoneyStatement::Transaction& t_in) { if (! t_in.m_strBankID.isEmpty()) { // make sure that id's are unique from this point on by appending a -# // postfix if needed QString base(t_in.m_strBankID); QString hash(base); int idx = 1; for (;;) { QMap::const_iterator it; it = uniqIds.constFind(hash); if (it == uniqIds.constEnd()) { uniqIds[hash] = true; break; } hash = QString("%1-%2").arg(base).arg(idx); ++idx; } s.setBankID(hash); } } void MyMoneyStatementReader::Private::setupPrice(MyMoneySplit &s, const MyMoneyAccount &splitAccount, const MyMoneyAccount &transactionAccount, const QDate &postDate) { if (transactionAccount.currencyId() != splitAccount.currencyId()) { // a currency converstion is needed assume that split has already a proper value MyMoneyFile* file = MyMoneyFile::instance(); MyMoneySecurity toCurrency = file->security(splitAccount.currencyId()); MyMoneySecurity fromCurrency = file->security(transactionAccount.currencyId()); // get the price for the transaction's date const MyMoneyPrice &price = file->price(fromCurrency.id(), toCurrency.id(), postDate); // if the price is valid calculate the shares if (price.isValid()) { const int fract = splitAccount.fraction(toCurrency); const MyMoneyMoney &shares = s.value() * price.rate(toCurrency.id()); s.setShares(shares.convert(fract)); qDebug("Setting second split shares to %s", qPrintable(s.shares().formatMoney(toCurrency.id(), 2))); } else { qDebug("No price entry was found to convert from '%s' to '%s' on '%s'", qPrintable(fromCurrency.tradingSymbol()), qPrintable(toCurrency.tradingSymbol()), qPrintable(postDate.toString(Qt::ISODate))); } } } MyMoneyStatementReader::MyMoneyStatementReader() : d(new Private), m_userAbort(false), m_autoCreatePayee(false), m_ft(0), m_progressCallback(0) { m_askPayeeCategory = KMyMoneyGlobalSettings::askForPayeeCategory(); } MyMoneyStatementReader::~MyMoneyStatementReader() { delete d; } bool MyMoneyStatementReader::anyTransactionAdded() const { return (d->transactionsAdded != 0) ? true : false; } void MyMoneyStatementReader::setAutoCreatePayee(bool create) { m_autoCreatePayee = create; } void MyMoneyStatementReader::setAskPayeeCategory(bool ask) { m_askPayeeCategory = ask; } bool MyMoneyStatementReader::import(const MyMoneyStatement& s, QStringList& messages) { // // For testing, save the statement to an XML file // (uncomment this line) // //MyMoneyStatement::writeXMLFile(s, "Imported.Xml"); // // Select the account // d->m_account = MyMoneyAccount(); d->m_brokerageAccount = MyMoneyAccount(); m_ft = new MyMoneyFileTransaction(); d->m_skipCategoryMatching = s.m_skipCategoryMatching; // if the statement source left some information about // the account, we use it to get the current data of it if (!s.m_accountId.isEmpty()) { try { d->m_account = MyMoneyFile::instance()->account(s.m_accountId); } catch (const MyMoneyException &) { qDebug("Received reference '%s' to unknown account in statement", qPrintable(s.m_accountId)); } } if (d->m_account.id().isEmpty()) { d->m_account.setName(s.m_strAccountName); d->m_account.setNumber(s.m_strAccountNumber); switch (s.m_eType) { case MyMoneyStatement::etCheckings: d->m_account.setAccountType(Account::Checkings); break; case MyMoneyStatement::etSavings: d->m_account.setAccountType(Account::Savings); break; case MyMoneyStatement::etInvestment: //testing support for investment statements! //m_userAbort = true; //KMessageBox::error(kmymoney, i18n("This is an investment statement. These are not supported currently."), i18n("Critical Error")); d->m_account.setAccountType(Account::Investment); break; case MyMoneyStatement::etCreditCard: d->m_account.setAccountType(Account::CreditCard); break; default: d->m_account.setAccountType(Account::Unknown); break; } // we ask the user only if we have some transactions to process if (!m_userAbort && s.m_listTransactions.count() > 0) m_userAbort = ! selectOrCreateAccount(Select, d->m_account); } // see if we need to update some values stored with the account if (d->m_account.value("lastStatementBalance") != s.m_closingBalance.toString() || d->m_account.value("lastImportedTransactionDate") != s.m_dateEnd.toString(Qt::ISODate)) { if (s.m_closingBalance != MyMoneyMoney::autoCalc) { d->m_account.setValue("lastStatementBalance", s.m_closingBalance.toString()); if (s.m_dateEnd.isValid()) { d->m_account.setValue("lastImportedTransactionDate", s.m_dateEnd.toString(Qt::ISODate)); } } try { MyMoneyFile::instance()->modifyAccount(d->m_account); } catch (const MyMoneyException &) { qDebug("Updating account in MyMoneyStatementReader::startImport failed"); } } if (!d->m_account.name().isEmpty()) messages += i18n("Importing statement for account %1", d->m_account.name()); else if (s.m_listTransactions.count() == 0) messages += i18n("Importing statement without transactions"); qDebug("Importing statement for '%s'", qPrintable(d->m_account.name())); // // Process the securities // signalProgress(0, s.m_listSecurities.count(), "Importing Statement ..."); int progress = 0; QList::const_iterator it_s = s.m_listSecurities.begin(); while (it_s != s.m_listSecurities.end()) { processSecurityEntry(*it_s); signalProgress(++progress, 0); ++it_s; } signalProgress(-1, -1); // // Process the transactions // if (!m_userAbort) { try { qDebug("Processing transactions (%s)", qPrintable(d->m_account.name())); signalProgress(0, s.m_listTransactions.count(), "Importing Statement ..."); int progress = 0; QList::const_iterator it_t = s.m_listTransactions.begin(); while (it_t != s.m_listTransactions.end() && !m_userAbort) { processTransactionEntry(*it_t); signalProgress(++progress, 0); ++it_t; } qDebug("Processing transactions done (%s)", qPrintable(d->m_account.name())); } catch (const MyMoneyException &e) { if (e.what() == "USERABORT") m_userAbort = true; else qDebug("Caught exception from processTransactionEntry() not caused by USERABORT: %s", qPrintable(e.what())); } signalProgress(-1, -1); } // // process price entries // if (!m_userAbort) { try { signalProgress(0, s.m_listPrices.count(), "Importing Statement ..."); QList slist = MyMoneyFile::instance()->securityList(); QList::const_iterator it_s; for (it_s = slist.constBegin(); it_s != slist.constEnd(); ++it_s) { d->securitiesBySymbol[(*it_s).tradingSymbol()] = *it_s; d->securitiesByName[(*it_s).name()] = *it_s; } int progress = 0; QList::const_iterator it_p = s.m_listPrices.begin(); while (it_p != s.m_listPrices.end()) { processPriceEntry(*it_p); signalProgress(++progress, 0); ++it_p; } } catch (const MyMoneyException &e) { if (e.what() == "USERABORT") m_userAbort = true; else qDebug("Caught exception from processPriceEntry() not caused by USERABORT: %s", qPrintable(e.what())); } signalProgress(-1, -1); } bool rc = false; // delete all payees created in vain int payeeCount = d->payees.count(); QList::const_iterator it_p; for (it_p = d->payees.constBegin(); it_p != d->payees.constEnd(); ++it_p) { try { MyMoneyFile::instance()->removePayee(*it_p); --payeeCount; } catch (const MyMoneyException &) { // if we can't delete it, it must be in use which is ok for us } } if (s.m_closingBalance.isAutoCalc()) { messages += i18n(" Statement balance is not contained in statement."); } else { messages += i18n(" Statement balance on %1 is reported to be %2", s.m_dateEnd.toString(Qt::ISODate), s.m_closingBalance.formatMoney("", 2)); } messages += i18n(" Transactions"); messages += i18np(" %1 processed", " %1 processed", d->transactionsCount); messages += i18ncp("x transactions have been added", " %1 added", " %1 added", d->transactionsAdded); messages += i18np(" %1 matched", " %1 matched", d->transactionsMatched); messages += i18np(" %1 duplicate", " %1 duplicates", d->transactionsDuplicate); messages += i18n(" Payees"); messages += i18ncp("x transactions have been created", " %1 created", " %1 created", payeeCount); messages += QString(); // remove the Don't ask again entries KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group(QString::fromLatin1("Notification Messages")); QStringList::ConstIterator it; for (it = m_dontAskAgain.constBegin(); it != m_dontAskAgain.constEnd(); ++it) { grp.deleteEntry(*it); } config->sync(); m_dontAskAgain.clear(); rc = !m_userAbort; // finish the transaction if (rc) m_ft->commit(); delete m_ft; m_ft = 0; qDebug("Importing statement for '%s' done", qPrintable(d->m_account.name())); return rc; } void MyMoneyStatementReader::processPriceEntry(const MyMoneyStatement::Price& p_in) { MyMoneyFile* file = MyMoneyFile::instance(); QString currency = file->baseCurrency().id(); QString security; if (!p_in.m_strCurrency.isEmpty()) { security = p_in.m_strSecurity; currency = p_in.m_strCurrency; } else if (d->securitiesBySymbol.contains(p_in.m_strSecurity)) { security = d->securitiesBySymbol[p_in.m_strSecurity].id(); currency = file->security(file->security(security).tradingCurrency()).id(); } else if (d->securitiesByName.contains(p_in.m_strSecurity)) { security = d->securitiesByName[p_in.m_strSecurity].id(); currency = file->security(file->security(security).tradingCurrency()).id(); } else return; MyMoneyPrice price(security, currency, p_in.m_date, p_in.m_amount, p_in.m_sourceName.isEmpty() ? i18n("Prices Importer") : p_in.m_sourceName); MyMoneyFile::instance()->addPrice(price); } void MyMoneyStatementReader::processSecurityEntry(const MyMoneyStatement::Security& sec_in) { // For a security entry, we will just make sure the security exists in the // file. It will not get added to the investment account until it's called // for in a transaction. MyMoneyFile* file = MyMoneyFile::instance(); // check if we already have the security // In a statement, we do not know what type of security this is, so we will // not use type as a matching factor. MyMoneySecurity security; QList list = file->securityList(); QList::ConstIterator it = list.constBegin(); while (it != list.constEnd() && security.id().isEmpty()) { if (matchNotEmpty(sec_in.m_strSymbol, (*it).tradingSymbol()) || matchNotEmpty(sec_in.m_strName, (*it).name())) { security = *it; } ++it; } // if the security was not found, we have to create it while not forgetting // to setup the type if (security.id().isEmpty()) { security.setName(sec_in.m_strName); security.setTradingSymbol(sec_in.m_strSymbol); security.setTradingCurrency(file->baseCurrency().id()); security.setValue("kmm-security-id", sec_in.m_strId); security.setValue("kmm-online-source", "Stooq"); security.setSecurityType(Security::Stock); MyMoneyFileTransaction ft; try { file->addSecurity(security); ft.commit(); qDebug() << "Created " << security.name() << " with id " << security.id(); } catch (const MyMoneyException &e) { KMessageBox::error(0, i18n("Error creating security record: %1", e.what()), i18n("Error")); } } else { qDebug() << "Found " << security.name() << " with id " << security.id(); } } void MyMoneyStatementReader::processTransactionEntry(const MyMoneyStatement::Transaction& statementTransactionUnderImport) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyTransaction transactionUnderImport; QString dbgMsg; dbgMsg = QString("Process on: '%1', id: '%3', amount: '%2', fees: '%4'") .arg(statementTransactionUnderImport.m_datePosted.toString(Qt::ISODate)) .arg(statementTransactionUnderImport.m_amount.formatMoney("", 2)) .arg(statementTransactionUnderImport.m_strBankID) .arg(statementTransactionUnderImport.m_fees.formatMoney("", 2)); qDebug("%s", qPrintable(dbgMsg)); // mark it imported for the view transactionUnderImport.setImported(); // TODO (Ace) We can get the commodity from the statement!! // Although then we would need UI to verify transactionUnderImport.setCommodity(d->m_account.currencyId()); transactionUnderImport.setPostDate(statementTransactionUnderImport.m_datePosted); transactionUnderImport.setMemo(statementTransactionUnderImport.m_strMemo); MyMoneySplit s1; MyMoneySplit s2; MyMoneySplit sFees; MyMoneySplit sBrokerage; s1.setMemo(statementTransactionUnderImport.m_strMemo); s1.setValue(statementTransactionUnderImport.m_amount + statementTransactionUnderImport.m_fees); s1.setShares(s1.value()); s1.setNumber(statementTransactionUnderImport.m_strNumber); // set these values if a transfer split is needed at the very end. MyMoneyMoney transfervalue; // If the user has chosen to import into an investment account, determine the correct account to use MyMoneyAccount thisaccount = d->m_account; QString brokerageactid; if (thisaccount.accountType() == Account::Investment) { // determine the brokerage account brokerageactid = d->m_account.value("kmm-brokerage-account").toUtf8(); if (brokerageactid.isEmpty()) { brokerageactid = file->accountByName(statementTransactionUnderImport.m_strBrokerageAccount).id(); } if (brokerageactid.isEmpty()) { brokerageactid = file->nameToAccount(statementTransactionUnderImport.m_strBrokerageAccount); } if (brokerageactid.isEmpty()) { brokerageactid = file->nameToAccount(thisaccount.brokerageName()); } if (brokerageactid.isEmpty()) { brokerageactid = SelectBrokerageAccount(); } // find the security transacted, UNLESS this transaction didn't // involve any security. if ((statementTransactionUnderImport.m_eAction != MyMoneyStatement::Transaction::eaNone) // eaInterest transactions MAY have a security. // && (t_in.m_eAction != MyMoneyStatement::Transaction::eaInterest) && (statementTransactionUnderImport.m_eAction != MyMoneyStatement::Transaction::eaFees)) { // the correct account is the stock account which matches two criteria: // (1) it is a sub-account of the selected investment account, and // (2a) the symbol of the underlying security matches the security of the // transaction, or // (2b) the name of the security matches the name of the security of the transaction. // search through each subordinate account bool found = false; QStringList accounts = thisaccount.accountList(); QStringList::const_iterator it_account = accounts.constBegin(); QString currencyid; while (!found && it_account != accounts.constEnd()) { currencyid = file->account(*it_account).currencyId(); MyMoneySecurity security = file->security(currencyid); if (matchNotEmpty(statementTransactionUnderImport.m_strSymbol, security.tradingSymbol()) || matchNotEmpty(statementTransactionUnderImport.m_strSecurity, security.name())) { thisaccount = file->account(*it_account); found = true; } ++it_account; } // If there was no stock account under the m_acccount investment account, // add one using the security. if (!found) { // The security should always be available, because the statement file // should separately list all the securities referred to in the file, // and when we found a security, we added it to the file. if (statementTransactionUnderImport.m_strSecurity.isEmpty()) { KMessageBox::information(0, i18n("This imported statement contains investment transactions with no security. These transactions will be ignored."), i18n("Security not found"), QString("BlankSecurity")); return; } else { MyMoneySecurity security; QList list = MyMoneyFile::instance()->securityList(); QList::ConstIterator it = list.constBegin(); while (it != list.constEnd() && security.id().isEmpty()) { if (matchNotEmpty(statementTransactionUnderImport.m_strSymbol, (*it).tradingSymbol()) || matchNotEmpty(statementTransactionUnderImport.m_strSecurity, (*it).name())) { security = *it; } ++it; } if (!security.id().isEmpty()) { thisaccount = MyMoneyAccount(); thisaccount.setName(security.name()); thisaccount.setAccountType(Account::Stock); thisaccount.setCurrencyId(security.id()); currencyid = thisaccount.currencyId(); file->addAccount(thisaccount, d->m_account); qDebug() << Q_FUNC_INFO << ": created account " << thisaccount.id() << " for security " << statementTransactionUnderImport.m_strSecurity << " under account " << d->m_account.id(); } // this security does not exist in the file. else { // This should be rare. A statement should have a security entry for any // of the securities referred to in the transactions. The only way to get // here is if that's NOT the case. int ret = KMessageBox::warningContinueCancel(0, i18n("
This investment account does not contain the \"%1\" security.
" "
Transactions involving this security will be ignored.
", statementTransactionUnderImport.m_strSecurity), i18n("Security not found"), KStandardGuiItem::cont(), KStandardGuiItem::cancel()); if (ret == KMessageBox::Cancel) { m_userAbort = true; } return; } } } // Don't update price if there is no price information contained in the transaction if (statementTransactionUnderImport.m_eAction != MyMoneyStatement::Transaction::eaCashDividend && statementTransactionUnderImport.m_eAction != MyMoneyStatement::Transaction::eaShrsin && statementTransactionUnderImport.m_eAction != MyMoneyStatement::Transaction::eaShrsout) { // update the price, while we're here. in the future, this should be // an option QString basecurrencyid = file->baseCurrency().id(); const MyMoneyPrice &price = file->price(currencyid, basecurrencyid, statementTransactionUnderImport.m_datePosted, true); if (!price.isValid() && ((!statementTransactionUnderImport.m_amount.isZero() && !statementTransactionUnderImport.m_shares.isZero()) || !statementTransactionUnderImport.m_price.isZero())) { MyMoneyPrice newprice; if (!statementTransactionUnderImport.m_price.isZero()) { newprice = MyMoneyPrice(currencyid, basecurrencyid, statementTransactionUnderImport.m_datePosted, statementTransactionUnderImport.m_price.abs(), i18n("Statement Importer")); } else { newprice = MyMoneyPrice(currencyid, basecurrencyid, statementTransactionUnderImport.m_datePosted, (statementTransactionUnderImport.m_amount / statementTransactionUnderImport.m_shares).abs(), i18n("Statement Importer")); } file->addPrice(newprice); } } } s1.setAccountId(thisaccount.id()); d->assignUniqueBankID(s1, statementTransactionUnderImport); if (statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaReinvestDividend) { s1.setAction(MyMoneySplit::ActionReinvestDividend); s1.setShares(statementTransactionUnderImport.m_shares); if (!statementTransactionUnderImport.m_price.isZero()) { s1.setPrice(statementTransactionUnderImport.m_price); } else { if (statementTransactionUnderImport.m_shares.isZero()) { KMessageBox::information(0, i18n("This imported statement contains investment transactions with no share amount. These transactions will be ignored."), i18n("No share amount provided"), QString("BlankAmount")); return; } MyMoneyMoney total = -statementTransactionUnderImport.m_amount - statementTransactionUnderImport.m_fees; s1.setPrice((total / statementTransactionUnderImport.m_shares).convertPrecision(file->security(thisaccount.currencyId()).pricePrecision())); } s2.setMemo(statementTransactionUnderImport.m_strMemo); if (statementTransactionUnderImport.m_strInterestCategory.isEmpty()) s2.setAccountId(d->interestId(thisaccount)); else s2.setAccountId(d->interestId(statementTransactionUnderImport.m_strInterestCategory)); s2.setShares(-statementTransactionUnderImport.m_amount - statementTransactionUnderImport.m_fees); s2.setValue(s2.shares()); } else if (statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaCashDividend) { // Cash dividends require setting 2 splits to get all of the information // in. Split #1 will be the income split, and we'll set it to the first // income account. This is a hack, but it's needed in order to get the // amount into the transaction. if (statementTransactionUnderImport.m_strInterestCategory.isEmpty()) s1.setAccountId(d->interestId(thisaccount)); else {// Ensure category sub-accounts are dealt with properly s1.setAccountId(d->interestId(statementTransactionUnderImport.m_strInterestCategory)); } s1.setShares(-statementTransactionUnderImport.m_amount - statementTransactionUnderImport.m_fees); s1.setValue(-statementTransactionUnderImport.m_amount - statementTransactionUnderImport.m_fees); // Split 2 will be the zero-amount investment split that serves to // mark this transaction as a cash dividend and note which stock account // it belongs to. s2.setMemo(statementTransactionUnderImport.m_strMemo); s2.setAction(MyMoneySplit::ActionDividend); s2.setAccountId(thisaccount.id()); /* at this point any fees have been taken into account already * so don't deduct them again. * BUG 322381 */ transfervalue = statementTransactionUnderImport.m_amount; } else if (statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaInterest) { if (statementTransactionUnderImport.m_strInterestCategory.isEmpty()) s1.setAccountId(d->interestId(thisaccount)); else {// Ensure category sub-accounts are dealt with properly if (statementTransactionUnderImport.m_amount.isPositive()) s1.setAccountId(d->interestId(statementTransactionUnderImport.m_strInterestCategory)); else s1.setAccountId(d->expenseId(statementTransactionUnderImport.m_strInterestCategory)); } s1.setShares(-statementTransactionUnderImport.m_amount - statementTransactionUnderImport.m_fees); s1.setValue(-statementTransactionUnderImport.m_amount - statementTransactionUnderImport.m_fees); /// *********** Add split as per Div ********** // Split 2 will be the zero-amount investment split that serves to // mark this transaction as a cash dividend and note which stock account // it belongs to. s2.setMemo(statementTransactionUnderImport.m_strMemo); s2.setAction(MyMoneySplit::ActionInterestIncome); s2.setAccountId(thisaccount.id()); transfervalue = statementTransactionUnderImport.m_amount; } else if (statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaFees) { if (statementTransactionUnderImport.m_strInterestCategory.isEmpty()) s1.setAccountId(d->feeId(thisaccount)); else// Ensure category sub-accounts are dealt with properly s1.setAccountId(d->feeId(statementTransactionUnderImport.m_strInterestCategory)); s1.setShares(statementTransactionUnderImport.m_amount); s1.setValue(statementTransactionUnderImport.m_amount); transfervalue = statementTransactionUnderImport.m_amount; } else if ((statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaBuy) || (statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaSell)) { s1.setAction(MyMoneySplit::ActionBuyShares); if (!statementTransactionUnderImport.m_price.isZero()) { s1.setPrice(statementTransactionUnderImport.m_price.abs()); } else if (!statementTransactionUnderImport.m_shares.isZero()) { MyMoneyMoney total = statementTransactionUnderImport.m_amount + statementTransactionUnderImport.m_fees.abs(); s1.setPrice((total / statementTransactionUnderImport.m_shares).abs().convertPrecision(file->security(thisaccount.currencyId()).pricePrecision())); } if (statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaBuy) s1.setShares(statementTransactionUnderImport.m_shares.abs()); else s1.setShares(-statementTransactionUnderImport.m_shares.abs()); s1.setValue(-(statementTransactionUnderImport.m_amount + statementTransactionUnderImport.m_fees.abs())); transfervalue = statementTransactionUnderImport.m_amount; } else if ((statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaShrsin) || (statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaShrsout)) { s1.setValue(MyMoneyMoney()); s1.setShares(statementTransactionUnderImport.m_shares); s1.setAction(MyMoneySplit::ActionAddShares); } else if (statementTransactionUnderImport.m_eAction == MyMoneyStatement::Transaction::eaNone) { // User is attempting to import a non-investment transaction into this // investment account. This is not supportable the way KMyMoney is // written. However, if a user has an associated brokerage account, // we can stuff the transaction there. QString brokerageactid = d->m_account.value("kmm-brokerage-account").toUtf8(); if (brokerageactid.isEmpty()) { brokerageactid = file->accountByName(d->m_account.brokerageName()).id(); } if (! brokerageactid.isEmpty()) { s1.setAccountId(brokerageactid); d->assignUniqueBankID(s1, statementTransactionUnderImport); // Needed to satisfy the bankid check below. thisaccount = file->account(brokerageactid); } else { // Warning!! Your transaction is being thrown away. } } if (!statementTransactionUnderImport.m_fees.isZero()) { sFees.setMemo(i18n("(Fees) %1", statementTransactionUnderImport.m_strMemo)); sFees.setValue(statementTransactionUnderImport.m_fees); sFees.setShares(statementTransactionUnderImport.m_fees); sFees.setAccountId(d->feeId(thisaccount)); } } else { // For non-investment accounts, just use the selected account // Note that it is perfectly reasonable to import an investment statement into a non-investment account // if you really want. The investment-specific information, such as number of shares and action will // be discarded in that case. s1.setAccountId(d->m_account.id()); d->assignUniqueBankID(s1, statementTransactionUnderImport); } QString payeename = statementTransactionUnderImport.m_strPayee; if (!payeename.isEmpty()) { qDebug() << QLatin1String("Start matching payee") << payeename; QString payeeid; try { QList pList = file->payeeList(); QList::const_iterator it_p; QMap matchMap; for (it_p = pList.constBegin(); it_p != pList.constEnd(); ++it_p) { bool ignoreCase; QStringList keys; QStringList::const_iterator it_s; const MyMoneyPayee::payeeMatchType matchType = (*it_p).matchData(ignoreCase, keys); switch (matchType) { case MyMoneyPayee::matchDisabled: break; case MyMoneyPayee::matchName: case MyMoneyPayee::matchNameExact: keys << QString("%1").arg(QRegExp::escape((*it_p).name())); if(matchType == MyMoneyPayee::matchNameExact) { keys.clear(); keys << QString("^%1$").arg(QRegExp::escape((*it_p).name())); } // intentional fall through case MyMoneyPayee::matchKey: for (it_s = keys.constBegin(); it_s != keys.constEnd(); ++it_s) { QRegExp exp(*it_s, ignoreCase ? Qt::CaseInsensitive : Qt::CaseSensitive); if (exp.indexIn(payeename) != -1) { qDebug("Found match with '%s' on '%s'", qPrintable(payeename), qPrintable((*it_p).name())); matchMap[exp.matchedLength()] = (*it_p).id(); } } break; } } // at this point we can have several scenarios: // a) multiple matches // b) a single match // c) no match at all // // for c) we just do nothing, for b) we take the one we found // in case of a) we take the one with the largest matchedLength() // which happens to be the last one in the map if (matchMap.count() > 1) { qDebug("Multiple matches"); QMap::const_iterator it_m = matchMap.constEnd(); --it_m; payeeid = *it_m; } else if (matchMap.count() == 1) { qDebug("Single matches"); payeeid = *(matchMap.constBegin()); } // if we did not find a matching payee, we throw an exception and try to create it if (payeeid.isEmpty()) throw MYMONEYEXCEPTION("payee not matched"); s1.setPayeeId(payeeid); } catch (const MyMoneyException &) { MyMoneyPayee payee; int rc = KMessageBox::Yes; if (m_autoCreatePayee == false) { // Ask the user if that is what he intended to do? QString msg = i18n("Do you want to add \"%1\" as payee/receiver?\n\n", payeename); msg += i18n("Selecting \"Yes\" will create the payee, \"No\" will skip " "creation of a payee record and remove the payee information " "from this transaction. Selecting \"Cancel\" aborts the import " "operation.\n\nIf you select \"No\" here and mark the \"Do not ask " "again\" checkbox, the payee information for all following transactions " "referencing \"%1\" will be removed.", payeename); QString askKey = QString("Statement-Import-Payee-") + payeename; if (!m_dontAskAgain.contains(askKey)) { m_dontAskAgain += askKey; } rc = KMessageBox::questionYesNoCancel(0, msg, i18n("New payee/receiver"), KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel(), askKey); } if (rc == KMessageBox::Yes) { // for now, we just add the payee to the pool and turn // on simple name matching, so that future transactions // with the same name don't get here again. // // In the future, we could open a dialog and ask for // all the other attributes of the payee, but since this // is called in the context of an automatic procedure it // might distract the user. payee.setName(payeename); payee.setMatchData(MyMoneyPayee::matchKey, true, QStringList() << QString("^%1$").arg(QRegExp::escape(payeename))); if (m_askPayeeCategory) { // We use a QPointer because the dialog may get deleted // during exec() if the parent of the dialog gets deleted. // In that case the guarded ptr will reset to 0. QPointer dialog = new QDialog(kmymoney); dialog->setWindowTitle(i18n("Default Category for Payee")); dialog->setModal(true); QWidget *mainWidget = new QWidget; QVBoxLayout *topcontents = new QVBoxLayout(mainWidget); //add in caption? and account combo here QLabel *label1 = new QLabel(i18n("Please select a default category for payee '%1'", payeename)); topcontents->addWidget(label1); auto filterProxyModel = new AccountNamesFilterProxyModel(this); filterProxyModel->setHideEquityAccounts(!KMyMoneyGlobalSettings::expertMode()); filterProxyModel->addAccountGroup(QVector {Account::Asset, Account::Liability, Account::Equity, Account::Income, Account::Expense}); auto const model = Models::instance()->accountsModel(); filterProxyModel->setSourceModel(model); filterProxyModel->setSourceColumns(model->getColumns()); filterProxyModel->sort((int)eAccountsModel::Column::Account); QPointer accountCombo = new KMyMoneyAccountCombo(filterProxyModel); topcontents->addWidget(accountCombo); mainWidget->setLayout(topcontents); QVBoxLayout *mainLayout = new QVBoxLayout; QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::No|QDialogButtonBox::Yes); dialog->setLayout(mainLayout); mainLayout->addWidget(mainWidget); dialog->connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); dialog->connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); mainLayout->addWidget(buttonBox); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Yes), KGuiItem(i18n("Save Category"))); KGuiItem::assign(buttonBox->button(QDialogButtonBox::No), KGuiItem(i18n("No Category"))); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KGuiItem(i18n("Abort"))); int result = dialog->exec(); QString accountId; if (accountCombo && !accountCombo->getSelected().isEmpty()) { accountId = accountCombo->getSelected(); } delete dialog; //if they hit yes instead of no, then grab setting of account combo if (result == QDialog::Accepted) { payee.setDefaultAccountId(accountId); } else if (result != QDialog::Rejected) { //add cancel button? and throw exception like below throw MYMONEYEXCEPTION("USERABORT"); } } try { file->addPayee(payee); qDebug("Payee '%s' created", qPrintable(payee.name())); d->payees << payee; payeeid = payee.id(); s1.setPayeeId(payeeid); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to add payee/receiver"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } else if (rc == KMessageBox::No) { s1.setPayeeId(QString()); } else { throw MYMONEYEXCEPTION("USERABORT"); } } if (thisaccount.accountType() != Account::Stock) { // // Fill in other side of the transaction (category/etc) based on payee // // Note, this logic is lifted from KLedgerView::slotPayeeChanged(), // however this case is more complicated, because we have an amount and // a memo. We just don't have the other side of the transaction. // // We'll search for the most recent transaction in this account with // this payee. If this reference transaction is a simple 2-split // transaction, it's simple. If it's a complex split, and the amounts // are different, we have a problem. Somehow we have to balance the // transaction. For now, we'll leave it unbalanced, and let the user // handle it. // const MyMoneyPayee& payeeObj = MyMoneyFile::instance()->payee(payeeid); if (statementTransactionUnderImport.m_listSplits.isEmpty() && payeeObj.defaultAccountEnabled()) { MyMoneyAccount splitAccount = file->account(payeeObj.defaultAccountId()); MyMoneySplit s; s.setReconcileFlag(eMyMoney::Split::State::Cleared); s.clearId(); s.setBankID(QString()); s.setShares(-s1.shares()); s.setValue(-s1.value()); s.setAccountId(payeeObj.defaultAccountId()); s.setMemo(transactionUnderImport.memo()); s.setPayeeId(payeeid); d->setupPrice(s, splitAccount, d->m_account, statementTransactionUnderImport.m_datePosted); transactionUnderImport.addSplit(s); file->addVATSplit(transactionUnderImport, d->m_account, splitAccount, statementTransactionUnderImport.m_amount); } else if (statementTransactionUnderImport.m_listSplits.isEmpty() && !d->m_skipCategoryMatching) { MyMoneyTransactionFilter filter(thisaccount.id()); filter.addPayee(payeeid); QList list = file->transactionList(filter); if (!list.empty()) { // Default to using the most recent transaction as the reference MyMoneyTransaction t_old = list.last(); // if there is more than one matching transaction, try to be a little // smart about which one we take. for now, we'll see if there's one // with the same VALUE as our imported transaction, and if so take that one. if (list.count() > 1) { QList::ConstIterator it_trans = list.constEnd(); if (it_trans != list.constBegin()) --it_trans; while (it_trans != list.constBegin()) { MyMoneySplit s = (*it_trans).splitByAccount(thisaccount.id()); if (s.value() == s1.value()) { // keep searching if this transaction references a closed account if (!MyMoneyFile::instance()->referencesClosedAccount(*it_trans)) { t_old = *it_trans; break; } } --it_trans; } // check constBegin, just in case if (it_trans == list.constBegin()) { MyMoneySplit s = (*it_trans).splitByAccount(thisaccount.id()); if (s.value() == s1.value()) { t_old = *it_trans; } } } // Only copy the splits if the transaction found does not reference a closed account if (!MyMoneyFile::instance()->referencesClosedAccount(t_old)) { QList::ConstIterator it_split; for (it_split = t_old.splits().constBegin(); it_split != t_old.splits().constEnd(); ++it_split) { // We don't need the split that covers this account, // we just need the other ones. if ((*it_split).accountId() != thisaccount.id()) { MyMoneySplit s(*it_split); s.setReconcileFlag(eMyMoney::Split::State::NotReconciled); s.clearId(); s.setBankID(QString()); s.removeMatch(); if (t_old.splits().count() == 2) { s.setShares(-s1.shares()); s.setValue(-s1.value()); s.setMemo(s1.memo()); } MyMoneyAccount splitAccount = file->account(s.accountId()); qDebug("Adding second split to %s(%s)", qPrintable(splitAccount.name()), qPrintable(s.accountId())); d->setupPrice(s, splitAccount, d->m_account, statementTransactionUnderImport.m_datePosted); transactionUnderImport.addSplit(s); } } } } } } } s1.setReconcileFlag(statementTransactionUnderImport.m_reconcile); // Add the 'account' split if it's needed if (! transfervalue.isZero()) { // in case the transaction has a reference to the brokerage account, we use it // but if brokerageactid has already been set, keep that. if (!statementTransactionUnderImport.m_strBrokerageAccount.isEmpty() && brokerageactid.isEmpty()) { brokerageactid = file->nameToAccount(statementTransactionUnderImport.m_strBrokerageAccount); } if (brokerageactid.isEmpty()) { brokerageactid = file->accountByName(statementTransactionUnderImport.m_strBrokerageAccount).id(); } // There is no BrokerageAccount so have to nowhere to put this split. if (!brokerageactid.isEmpty()) { sBrokerage.setMemo(statementTransactionUnderImport.m_strMemo); sBrokerage.setValue(transfervalue); sBrokerage.setShares(transfervalue); sBrokerage.setAccountId(brokerageactid); sBrokerage.setReconcileFlag(statementTransactionUnderImport.m_reconcile); MyMoneyAccount splitAccount = file->account(sBrokerage.accountId()); d->setupPrice(sBrokerage, splitAccount, d->m_account, statementTransactionUnderImport.m_datePosted); } } if (!(sBrokerage == MyMoneySplit())) transactionUnderImport.addSplit(sBrokerage); if (!(sFees == MyMoneySplit())) transactionUnderImport.addSplit(sFees); if (!(s2 == MyMoneySplit())) transactionUnderImport.addSplit(s2); transactionUnderImport.addSplit(s1); if ((statementTransactionUnderImport.m_eAction != MyMoneyStatement::Transaction::eaReinvestDividend) && (statementTransactionUnderImport.m_eAction != MyMoneyStatement::Transaction::eaCashDividend) && (statementTransactionUnderImport.m_eAction != MyMoneyStatement::Transaction::eaInterest) ) { //****************************************** // process splits //****************************************** QList::const_iterator it_s; for (it_s = statementTransactionUnderImport.m_listSplits.begin(); it_s != statementTransactionUnderImport.m_listSplits.end(); ++it_s) { MyMoneySplit s3; s3.setAccountId((*it_s).m_accountId); MyMoneyAccount acc = file->account(s3.accountId()); s3.setPayeeId(s1.payeeId()); s3.setMemo((*it_s).m_strMemo); s3.setShares((*it_s).m_amount); s3.setValue((*it_s).m_amount); s3.setReconcileFlag((*it_s).m_reconcile); d->setupPrice(s3, acc, d->m_account, statementTransactionUnderImport.m_datePosted); transactionUnderImport.addSplit(s3); } } // Add the transaction try { // check for matches already stored in the engine TransactionMatchFinder::MatchResult result; TransactionMatcher matcher(thisaccount); d->transactionsCount++; ExistingTransactionMatchFinder existingTrMatchFinder(KMyMoneyGlobalSettings::matchInterval()); result = existingTrMatchFinder.findMatch(transactionUnderImport, s1); if (result != TransactionMatchFinder::MatchNotFound) { MyMoneyTransaction matchedTransaction = existingTrMatchFinder.getMatchedTransaction(); if (!matchedTransaction.isImported() || result == TransactionMatchFinder::MatchPrecise) { // don't match with just imported transaction MyMoneySplit matchedSplit = existingTrMatchFinder.getMatchedSplit(); handleMatchingOfExistingTransaction(matcher, matchedTransaction, matchedSplit, transactionUnderImport, s1, result); return; } } addTransaction(transactionUnderImport); ScheduledTransactionMatchFinder scheduledTrMatchFinder(thisaccount, KMyMoneyGlobalSettings::matchInterval()); result = scheduledTrMatchFinder.findMatch(transactionUnderImport, s1); if (result != TransactionMatchFinder::MatchNotFound) { MyMoneySplit matchedSplit = scheduledTrMatchFinder.getMatchedSplit(); MyMoneySchedule matchedSchedule = scheduledTrMatchFinder.getMatchedSchedule(); handleMatchingOfScheduledTransaction(matcher, matchedSchedule, matchedSplit, transactionUnderImport, s1); return; } } catch (const MyMoneyException &e) { QString message(i18n("Problem adding or matching imported transaction with id '%1': %2", statementTransactionUnderImport.m_strBankID, e.what())); qDebug("%s", qPrintable(message)); int result = KMessageBox::warningContinueCancel(0, message); if (result == KMessageBox::Cancel) throw MYMONEYEXCEPTION("USERABORT"); } } QString MyMoneyStatementReader::SelectBrokerageAccount() { if (d->m_brokerageAccount.id().isEmpty()) { d->m_brokerageAccount.setAccountType(Account::Checkings); if (!m_userAbort) m_userAbort = ! selectOrCreateAccount(Select, d->m_brokerageAccount); } return d->m_brokerageAccount.id(); } bool MyMoneyStatementReader::selectOrCreateAccount(const SelectCreateMode /*mode*/, MyMoneyAccount& account) { bool result = false; MyMoneyFile* file = MyMoneyFile::instance(); QString accountId; // Try to find an existing account in the engine which matches this one. // There are two ways to be a "matching account". The account number can // match the statement account OR the "StatementKey" property can match. // Either way, we'll update the "StatementKey" property for next time. QString accountNumber = account.number(); if (! accountNumber.isEmpty()) { // Get a list of all accounts QList accounts; file->accountList(accounts); // Iterate through them QList::const_iterator it_account = accounts.constBegin(); while (it_account != accounts.constEnd()) { if ( ((*it_account).value("StatementKey") == accountNumber) || ((*it_account).number() == accountNumber) ) { MyMoneyAccount newAccount((*it_account).id(), account); account = newAccount; accountId = (*it_account).id(); break; } ++it_account; } } QString msg = i18n("You have downloaded a statement for the following account:

"); msg += i18n(" - Account Name: %1", account.name()) + "
"; msg += i18n(" - Account Type: %1", MyMoneyAccount::accountTypeToString(account.accountType())) + "
"; msg += i18n(" - Account Number: %1", account.number()) + "
"; msg += "
"; if (!account.name().isEmpty()) { if (!accountId.isEmpty()) msg += i18n("Do you want to import transactions to this account?"); else msg += i18n("KMyMoney cannot determine which of your accounts to use. You can " "create a new account by pressing the Create button " "or select another one manually from the selection box below."); } else { msg += i18n("No account information has been found in the selected statement file. " "Please select an account using the selection box in the dialog or " "create a new account by pressing the Create button."); } eDialogs::Category type; if (account.accountType() == Account::Checkings) { type = eDialogs::Category::checking; } else if (account.accountType() == Account::Savings) { type = eDialogs::Category::savings; } else if (account.accountType() == Account::Investment) { type = eDialogs::Category::investment; } else if (account.accountType() == Account::CreditCard) { type = eDialogs::Category::creditCard; } else { type = static_cast(eDialogs::Category::asset | eDialogs::Category::liability); } QPointer accountSelect = new KAccountSelectDlg(type, "StatementImport", kmymoney); accountSelect->setHeader(i18n("Import transactions")); accountSelect->setDescription(msg); accountSelect->setAccount(account, accountId); accountSelect->setMode(false); accountSelect->showAbortButton(true); accountSelect->hideQifEntry(); QString accname; bool done = false; while (!done) { if (accountSelect->exec() == QDialog::Accepted && !accountSelect->selectedAccount().isEmpty()) { result = true; done = true; accountId = accountSelect->selectedAccount(); account = file->account(accountId); if (! accountNumber.isEmpty() && account.value("StatementKey") != accountNumber) { account.setValue("StatementKey", accountNumber); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyAccount(account); ft.commit(); accname = account.name(); } catch (const MyMoneyException &) { qDebug("Updating account in MyMoneyStatementReader::selectOrCreateAccount failed"); } } } else { if (accountSelect->aborted()) //throw MYMONEYEXCEPTION("USERABORT"); done = true; else KMessageBox::error(0, QLatin1String("") + i18n("You must select an account, create a new one, or press the Abort button.") + QLatin1String("")); } } delete accountSelect; return result; } const MyMoneyAccount& MyMoneyStatementReader::account() const { return d->m_account; } void MyMoneyStatementReader::setProgressCallback(void(*callback)(int, int, const QString&)) { m_progressCallback = callback; } void MyMoneyStatementReader::signalProgress(int current, int total, const QString& msg) { if (m_progressCallback != 0) (*m_progressCallback)(current, total, msg); } void MyMoneyStatementReader::handleMatchingOfExistingTransaction(TransactionMatcher & matcher, MyMoneyTransaction matchedTransaction, MyMoneySplit matchedSplit, MyMoneyTransaction & importedTransaction, const MyMoneySplit & importedSplit, const TransactionMatchFinder::MatchResult & matchResult) { switch (matchResult) { case TransactionMatchFinder::MatchNotFound: break; case TransactionMatchFinder::MatchDuplicate: d->transactionsDuplicate++; qDebug("Detected transaction duplicate"); break; case TransactionMatchFinder::MatchImprecise: case TransactionMatchFinder::MatchPrecise: addTransaction(importedTransaction); qDebug("Detected as match to transaction '%s'", qPrintable(matchedTransaction.id())); matcher.match(matchedTransaction, matchedSplit, importedTransaction, importedSplit, true); d->transactionsMatched++; break; } } void MyMoneyStatementReader::handleMatchingOfScheduledTransaction(TransactionMatcher & matcher, MyMoneySchedule matchedSchedule, MyMoneySplit matchedSplit, const MyMoneyTransaction & importedTransaction, const MyMoneySplit & importedSplit) { QPointer editor; if (askUserToEnterScheduleForMatching(matchedSchedule, importedSplit, importedTransaction)) { KEnterScheduleDlg dlg(0, matchedSchedule); editor = dlg.startEdit(); if (editor) { MyMoneyTransaction torig; try { // in case the amounts of the scheduled transaction and the // imported transaction differ, we need to update the amount // using the transaction editor. if (matchedSplit.shares() != importedSplit.shares() && !matchedSchedule.isFixed()) { // for now this only works with regular transactions and not // for investment transactions. As of this, we don't have // scheduled investment transactions anyway. auto se = dynamic_cast(editor.data()); if (se) { // the following call will update the amount field in the // editor and also adjust a possible VAT assignment. Make // sure to use only the absolute value of the amount, because // the editor keeps the sign in a different position (deposit, // withdrawal tab) - kMyMoneyEdit* amount = dynamic_cast(se->haveWidget("amount")); + KMyMoneyEdit* amount = dynamic_cast(se->haveWidget("amount")); if (amount) { amount->setValue(importedSplit.shares().abs()); se->slotUpdateAmount(importedSplit.shares().abs().toString()); // we also need to update the matchedSplit variable to // have the modified share/value. matchedSplit.setShares(importedSplit.shares()); matchedSplit.setValue(importedSplit.value()); } } } editor->createTransaction(torig, dlg.transaction(), dlg.transaction().splits().isEmpty() ? MyMoneySplit() : dlg.transaction().splits().front(), true); QString newId; if (editor->enterTransactions(newId, false, true)) { if (!newId.isEmpty()) { torig = MyMoneyFile::instance()->transaction(newId); matchedSchedule.setLastPayment(torig.postDate()); } matchedSchedule.setNextDueDate(matchedSchedule.nextPayment(matchedSchedule.nextDueDate())); MyMoneyFile::instance()->modifySchedule(matchedSchedule); } // now match the two transactions matcher.match(torig, matchedSplit, importedTransaction, importedSplit); d->transactionsMatched++; } catch (const MyMoneyException &e) { // make sure we get rid of the editor before // the KEnterScheduleDlg is destroyed delete editor; throw e; // rethrow } } // delete the editor delete editor; } } void MyMoneyStatementReader::addTransaction(MyMoneyTransaction& transaction) { MyMoneyFile* file = MyMoneyFile::instance(); file->addTransaction(transaction); d->transactionsAdded++; } bool MyMoneyStatementReader::askUserToEnterScheduleForMatching(const MyMoneySchedule& matchedSchedule, const MyMoneySplit& importedSplit, const MyMoneyTransaction & importedTransaction) const { QString scheduleName = matchedSchedule.name(); int currencyDenom = d->m_account.fraction(MyMoneyFile::instance()->currency(d->m_account.currencyId())); QString splitValue = importedSplit.value().formatMoney(currencyDenom); QString payeeName = MyMoneyFile::instance()->payee(importedSplit.payeeId()).name(); QString questionMsg = i18n("KMyMoney has found a scheduled transaction which matches an imported transaction.
" "Schedule name: %1
" "Transaction: %2 %3
" "Do you want KMyMoney to enter this schedule now so that the transaction can be matched?", scheduleName, splitValue, payeeName); // check that dates are within user's setting const int gap = std::abs(matchedSchedule.transaction().postDate().toJulianDay() - importedTransaction.postDate().toJulianDay()); if (gap > KMyMoneyGlobalSettings::matchInterval()) questionMsg = i18np("KMyMoney has found a scheduled transaction which matches an imported transaction.
" "Schedule name: %2
" "Transaction: %3 %4
" "The transaction dates are one day apart.
" "Do you want KMyMoney to enter this schedule now so that the transaction can be matched?", "KMyMoney has found a scheduled transaction which matches an imported transaction.
" "Schedule name: %2
" "Transaction: %3 %4
" "The transaction dates are %1 days apart.
" "Do you want KMyMoney to enter this schedule now so that the transaction can be matched?", gap ,scheduleName, splitValue, payeeName); const int userAnswer = KMessageBox::questionYesNo(0, QLatin1String("") + questionMsg + QLatin1String(""), i18n("Schedule found")); return (userAnswer == KMessageBox::Yes); } diff --git a/kmymoney/dialogs/CMakeLists.txt b/kmymoney/dialogs/CMakeLists.txt index 49470ca3a..19fc361d8 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/kaccounttemplateselectordecl.ui ../widgets/transactionsortoptiondecl.ui + ../widgets/kaccounttemplateselector.ui ../widgets/transactionsortoption.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/investactivities.cpp b/kmymoney/dialogs/investactivities.cpp index a1c11f565..1537786d1 100644 --- a/kmymoney/dialogs/investactivities.cpp +++ b/kmymoney/dialogs/investactivities.cpp @@ -1,939 +1,939 @@ /*************************************************************************** investactivities.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 "investactivities.h" // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "investtransactioneditor.h" #include "mymoneymoney.h" #include "kmymoneycategory.h" #include "kmymoneyedit.h" #include "kmymoneyaccountselector.h" #include "kmymoneycompletion.h" #include #include "mymoneyfile.h" #include "mymoneysplit.h" #include "mymoneyaccount.h" #include "mymoneysecurity.h" #include "dialogenums.h" #include "mymoneyenums.h" using namespace Invest; using namespace KMyMoneyRegister; class Invest::ActivityPrivate { Q_DISABLE_COPY(ActivityPrivate) public: ActivityPrivate() { } InvestTransactionEditor *m_parent; QMap m_priceInfo; bool m_memoChanged; QString m_memoText; }; Activity::Activity(InvestTransactionEditor* editor) : d_ptr(new ActivityPrivate) { Q_D(Activity); d->m_memoChanged = false; d->m_parent = editor; } Activity::~Activity() { Q_D(Activity); delete d; } bool& Activity::memoChanged() { Q_D(Activity); return d->m_memoChanged; } QString& Activity::memoText() { Q_D(Activity); return d->m_memoText; } bool Activity::isComplete(QString& reason) const { Q_D(const Activity); Q_UNUSED(reason) auto rc = false; auto security = dynamic_cast(haveWidget("security")); if (!security->currentText().isEmpty()) { rc = (security->selector()->contains(security->currentText()) || (isMultiSelection() && d->m_memoChanged)); } return rc; } QWidget* Activity::haveWidget(const QString& name) const { Q_D(const Activity); return d->m_parent->haveWidget(name); } bool Activity::haveAssetAccount() const { auto cat = dynamic_cast(haveWidget("asset-account")); auto rc = true; if (!isMultiSelection()) rc = !cat->currentText().isEmpty(); if (rc && !cat->currentText().isEmpty()) { rc = cat->selector()->contains(cat->currentText()); } return rc; } bool Activity::haveCategoryAndAmount(const QString& category, const QString& amount, bool optional) const { Q_D(const Activity); auto cat = dynamic_cast(haveWidget(category)); auto rc = true; if (!cat->currentText().isEmpty()) { rc = cat->selector()->contains(cat->currentText()) || cat->isSplitTransaction(); if (rc && !amount.isEmpty() && !isMultiSelection()) { if (cat->isSplitTransaction()) { QList::const_iterator split; QList::const_iterator splitEnd; if (category == "fee-account") { split = d->m_parent->feeSplits().cbegin(); splitEnd = d->m_parent->feeSplits().cend(); } else if (category == "interest-account") { split = d->m_parent->interestSplits().cbegin(); splitEnd = d->m_parent->interestSplits().cend(); } for (; split != splitEnd; ++split) { if ((*split).value().isZero()) rc = false; } } else { - MyMoneyMoney value = dynamic_cast(haveWidget(amount))->value(); + MyMoneyMoney value = dynamic_cast(haveWidget(amount))->value(); rc = !value.isZero(); } } } else if (!isMultiSelection() && !optional) { rc = false; } return rc; } bool Activity::haveFees(bool optional) const { return haveCategoryAndAmount("fee-account", "fee-amount", optional); } bool Activity::haveInterest(bool optional) const { return haveCategoryAndAmount("interest-account", "interest-amount", optional); } bool Activity::haveShares() const { - kMyMoneyEdit* amount = dynamic_cast(haveWidget("shares")); + KMyMoneyEdit* amount = dynamic_cast(haveWidget("shares")); if (isMultiSelection() && amount->value().isZero()) return true; return !amount->value().isZero(); } bool Activity::havePrice() const { - kMyMoneyEdit* amount = dynamic_cast(haveWidget("price")); + KMyMoneyEdit* amount = dynamic_cast(haveWidget("price")); if (isMultiSelection() && amount->value().isZero()) return true; return !amount->value().isZero(); } bool Activity::isMultiSelection() const { Q_D(const Activity); return d->m_parent->isMultiSelection(); } -bool Activity::createCategorySplits(const MyMoneyTransaction& t, KMyMoneyCategory* cat, kMyMoneyEdit* amount, MyMoneyMoney factor, QList&splits, const QList& osplits) const +bool Activity::createCategorySplits(const MyMoneyTransaction& t, KMyMoneyCategory* cat, KMyMoneyEdit* amount, MyMoneyMoney factor, QList&splits, const QList& osplits) const { Q_D(const Activity); auto rc = true; if (!isMultiSelection() || (isMultiSelection() && !cat->currentText().isEmpty())) { if (!cat->isSplitTransaction()) { splits.clear(); MyMoneySplit s1; QString categoryId; categoryId = cat->selectedItem(); if (!categoryId.isEmpty()) { s1.setAccountId(categoryId); s1.setValue(amount->value() * factor); if (!s1.value().isZero()) { rc = d->m_parent->setupPrice(t, s1); } splits.append(s1); } } else { splits = osplits; } } return rc; } void Activity::createAssetAccountSplit(MyMoneySplit& split, const MyMoneySplit& stockSplit) const { auto cat = dynamic_cast(haveWidget("asset-account")); if (!isMultiSelection() || (isMultiSelection() && !cat->currentText().isEmpty())) { QString categoryId; categoryId = cat->selectedItem(); split.setAccountId(categoryId); } split.setMemo(stockSplit.memo()); } MyMoneyMoney Activity::sumSplits(const MyMoneySplit& s0, const QList& feeSplits, const QList& interestSplits) const { MyMoneyMoney total; total = s0.value(); QList::const_iterator it_s; for (it_s = feeSplits.begin(); it_s != feeSplits.end(); ++it_s) { total += (*it_s).value(); } for (it_s = interestSplits.begin(); it_s != interestSplits.end(); ++it_s) { total += (*it_s).value(); } return total; } void Activity::setLabelText(const QString& idx, const QString& txt) const { auto w = dynamic_cast(haveWidget(idx)); if (w) { w->setText(txt); } else { if (KMyMoneySettings::transactionForm()) { // labels are only used in the transaction form qDebug("Unknown QLabel named '%s'", qPrintable(idx)); } } } void Activity::preloadAssetAccount() { Q_D(Activity); KMyMoneyCategory* cat; cat = dynamic_cast(haveWidget("asset-account")); if (cat->isVisible()) { if (cat->currentText().isEmpty()) { MyMoneyAccount acc = MyMoneyFile::instance()->accountByName(i18n("%1 (Brokerage)", d->m_parent->account().name())); if (!acc.id().isEmpty()) { bool blocked = cat->signalsBlocked(); // block signals, so that the focus does not go crazy cat->blockSignals(true); cat->completion()->setSelected(acc.id()); cat->slotItemSelected(acc.id()); cat->blockSignals(blocked); } } } } void Activity::setWidgetVisibility(const QStringList& widgetIds, bool visible) const { for (QStringList::const_iterator it_w = widgetIds.constBegin(); it_w != widgetIds.constEnd(); ++it_w) { auto w = haveWidget(*it_w); if (w) { if (visible) { w->show(); } else { w->hide(); } } } } eDialogs::PriceMode Activity::priceMode() const { Q_D(const Activity); return d->m_parent->priceMode(); } QString Activity::priceLabel() const { QString label; if (priceMode() == eDialogs::PriceMode::Price) { label = i18n("Price"); } else if (priceMode() == eDialogs::PriceMode::PricePerShare) { label = i18n("Price/share"); } else if (priceMode() == eDialogs::PriceMode::PricePerTransaction) { label = i18n("Transaction amount"); } return label; } Buy::Buy(InvestTransactionEditor* editor) : Activity(editor) { } Buy::~Buy() { } eMyMoney::Split::InvestmentTransactionType Buy::type() const { return eMyMoney::Split::InvestmentTransactionType::BuyShares; } void Buy::showWidgets() const { static const QStringList visibleWidgetIds = QStringList() << "asset-account" << "shares" << "price" << "total" << "interest-account" << "fee-account"; setWidgetVisibility(visibleWidgetIds, true); setLabelText("interest-amount-label", i18n("Interest")); setLabelText("interest-label", i18n("Interest")); setLabelText("fee-label", i18n("Fees")); setLabelText("asset-label", i18n("Account")); setLabelText("shares-label", i18n("Shares")); if (dynamic_cast(haveWidget("price-label"))) setLabelText("price-label", priceLabel()); setLabelText("total-label", i18nc("Total value", "Total")); } bool Buy::isComplete(QString& reason) const { auto rc = Activity::isComplete(reason); rc &= haveAssetAccount(); rc &= haveFees(true); rc &= haveShares(); rc &= havePrice(); return rc; } bool Buy::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_D(Activity); Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); QString reason; if (!isComplete(reason)) return false; - auto sharesEdit = dynamic_cast(haveWidget("shares")); - auto priceEdit = dynamic_cast(haveWidget("price")); + auto sharesEdit = dynamic_cast(haveWidget("shares")); + auto priceEdit = dynamic_cast(haveWidget("price")); s0.setAction(eMyMoney::Split::InvestmentTransactionType::BuyShares); MyMoneyMoney shares = s0.shares(); MyMoneyMoney price; if (!s0.shares().isZero()) price = (s0.value() / s0.shares()).reduce(); if (!isMultiSelection() || (isMultiSelection() && !sharesEdit->value().isZero())) { shares = sharesEdit->value().abs(); s0.setShares(shares); s0.setValue((shares * price).reduce()); s0.setPrice(price); } if (!isMultiSelection() || (isMultiSelection() && !priceEdit->value().isZero())) { price = priceEdit->value().abs(); if (priceMode() == eDialogs::PriceMode::PricePerTransaction) { s0.setValue(price.reduce()); if (!s0.shares().isZero()) s0.setPrice((price / s0.shares()).reduce()); } else { s0.setValue((shares * price).reduce()); s0.setPrice(price); } } - if (!createCategorySplits(t, dynamic_cast(haveWidget("fee-account")), dynamic_cast(haveWidget("fee-amount")), MyMoneyMoney::ONE, feeSplits, m_feeSplits)) + if (!createCategorySplits(t, dynamic_cast(haveWidget("fee-account")), dynamic_cast(haveWidget("fee-amount")), MyMoneyMoney::ONE, feeSplits, m_feeSplits)) return false; createAssetAccountSplit(assetAccountSplit, s0); MyMoneyMoney total = sumSplits(s0, feeSplits, QList()); // Clear any leftover value from previous Dividend. interestSplits.clear(); assetAccountSplit.setValue(-total); if (!d->m_parent->setupPrice(t, assetAccountSplit)) return false; return true; } Sell::Sell(InvestTransactionEditor* editor) : Activity(editor) { } Sell::~Sell() { } eMyMoney::Split::InvestmentTransactionType Sell::type() const { return eMyMoney::Split::InvestmentTransactionType::SellShares; } void Sell::showWidgets() const { Q_D(const Activity); static const QStringList visibleWidgetIds = QStringList() << "asset-account" << "interest-amount" << "shares" << "price" << "total" << "interest-account" << "fee-account"; setWidgetVisibility(visibleWidgetIds, true); - auto shareEdit = dynamic_cast(haveWidget("shares")); + auto shareEdit = dynamic_cast(haveWidget("shares")); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(d->m_parent->security().smallestAccountFraction())); setLabelText("interest-amount-label", i18n("Interest")); setLabelText("interest-label", i18n("Interest")); setLabelText("fee-label", i18n("Fees")); setLabelText("asset-label", i18n("Account")); setLabelText("shares-label", i18n("Shares")); if (dynamic_cast(haveWidget("price-label"))) setLabelText("price-label", priceLabel()); setLabelText("total-label", i18nc("Total value", "Total")); } bool Sell::isComplete(QString& reason) const { auto rc = Activity::isComplete(reason); rc &= haveAssetAccount(); rc &= haveFees(true); rc &= haveInterest(true); rc &= haveShares(); rc &= havePrice(); return rc; } bool Sell::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_D(Activity); Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); QString reason; if (!isComplete(reason)) return false; - auto sharesEdit = dynamic_cast(haveWidget("shares")); - auto priceEdit = dynamic_cast(haveWidget("price")); + auto sharesEdit = dynamic_cast(haveWidget("shares")); + auto priceEdit = dynamic_cast(haveWidget("price")); s0.setAction(eMyMoney::Split::InvestmentTransactionType::BuyShares); MyMoneyMoney shares = s0.shares(); MyMoneyMoney price; if (!s0.shares().isZero()) price = (s0.value() / s0.shares()).reduce(); if (!isMultiSelection() || (isMultiSelection() && !sharesEdit->value().isZero())) { shares = -sharesEdit->value().abs(); s0.setShares(shares); s0.setValue((shares * price).reduce()); s0.setPrice(price); } if (!isMultiSelection() || (isMultiSelection() && !priceEdit->value().isZero())) { price = priceEdit->value().abs(); if (priceMode() == eDialogs::PriceMode::PricePerTransaction) { price = -price; s0.setValue(price.reduce()); if (!s0.shares().isZero()) s0.setPrice((price / s0.shares()).reduce()); } else { s0.setValue((shares * price).reduce()); s0.setPrice(price); } } - if (!createCategorySplits(t, dynamic_cast(haveWidget("fee-account")), dynamic_cast(haveWidget("fee-amount")), MyMoneyMoney::ONE, feeSplits, m_feeSplits)) + if (!createCategorySplits(t, dynamic_cast(haveWidget("fee-account")), dynamic_cast(haveWidget("fee-amount")), MyMoneyMoney::ONE, feeSplits, m_feeSplits)) return false; - if (!createCategorySplits(t, dynamic_cast(haveWidget("interest-account")), dynamic_cast(haveWidget("interest-amount")), MyMoneyMoney::MINUS_ONE, interestSplits, m_interestSplits)) + if (!createCategorySplits(t, dynamic_cast(haveWidget("interest-account")), dynamic_cast(haveWidget("interest-amount")), MyMoneyMoney::MINUS_ONE, interestSplits, m_interestSplits)) return false; createAssetAccountSplit(assetAccountSplit, s0); MyMoneyMoney total = sumSplits(s0, feeSplits, interestSplits); assetAccountSplit.setValue(-total); if (!d->m_parent->setupPrice(t, assetAccountSplit)) return false; return true; } Div::Div(InvestTransactionEditor* editor) : Activity(editor) { } Div::~Div() { } eMyMoney::Split::InvestmentTransactionType Div::type() const { return eMyMoney::Split::InvestmentTransactionType::Dividend; } void Div::showWidgets() const { static const QStringList visibleWidgetIds = QStringList() << "asset-account" << "interest-amount" << "total" << "interest-account" << "fee-account"; setWidgetVisibility(visibleWidgetIds, true); static const QStringList hiddenWidgetIds = QStringList() << "shares" << "price"; setWidgetVisibility(hiddenWidgetIds, false); setLabelText("interest-amount-label", i18n("Interest")); setLabelText("interest-label", i18n("Interest")); setLabelText("fee-label", i18n("Fees")); setLabelText("asset-label", i18n("Account")); setLabelText("total-label", i18nc("Total value", "Total")); } bool Div::isComplete(QString& reason) const { Q_UNUSED(reason) auto rc = Activity::isComplete(reason); rc &= haveAssetAccount(); rc &= haveCategoryAndAmount("interest-account", QString(), false); rc &= haveInterest(false); return rc; } bool Div::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_D(Activity); Q_UNUSED(m_feeSplits); Q_UNUSED(security); Q_UNUSED(currency); QString reason; if (!isComplete(reason)) return false; s0.setAction(eMyMoney::Split::InvestmentTransactionType::Dividend); // for dividends, we only use the stock split as a marker MyMoneyMoney shares; s0.setShares(shares); s0.setValue(shares); s0.setPrice(MyMoneyMoney::ONE); - if (!createCategorySplits(t, dynamic_cast(haveWidget("fee-account")), dynamic_cast(haveWidget("fee-amount")), MyMoneyMoney::ONE, feeSplits, m_feeSplits)) + if (!createCategorySplits(t, dynamic_cast(haveWidget("fee-account")), dynamic_cast(haveWidget("fee-amount")), MyMoneyMoney::ONE, feeSplits, m_feeSplits)) return false; - if (!createCategorySplits(t, dynamic_cast(haveWidget("interest-account")), dynamic_cast(haveWidget("interest-amount")), MyMoneyMoney::MINUS_ONE, interestSplits, m_interestSplits)) + if (!createCategorySplits(t, dynamic_cast(haveWidget("interest-account")), dynamic_cast(haveWidget("interest-amount")), MyMoneyMoney::MINUS_ONE, interestSplits, m_interestSplits)) return false; createAssetAccountSplit(assetAccountSplit, s0); MyMoneyMoney total = sumSplits(s0, feeSplits, interestSplits); assetAccountSplit.setValue(-total); if (!d->m_parent->setupPrice(t, assetAccountSplit)) return false; return true; } Reinvest::Reinvest(InvestTransactionEditor* editor) : Activity(editor) { } Reinvest::~Reinvest() { } eMyMoney::Split::InvestmentTransactionType Reinvest::type() const { return eMyMoney::Split::InvestmentTransactionType::ReinvestDividend; } void Reinvest::showWidgets() const { Q_D(const Activity); static const QStringList visibleWidgetIds = QStringList() << "price" << "interest-account"; setWidgetVisibility(visibleWidgetIds, true); - auto shareEdit = dynamic_cast(haveWidget("shares")); + auto shareEdit = dynamic_cast(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(d->m_parent->security().smallestAccountFraction())); - kMyMoneyEdit* intAmount = dynamic_cast(haveWidget("interest-amount")); + KMyMoneyEdit* intAmount = dynamic_cast(haveWidget("interest-amount")); intAmount->hide(); setLabelText("interest-amount-label", QString()); intAmount->setValue(MyMoneyMoney()); setLabelText("interest-label", i18n("Interest")); setLabelText("shares-label", i18n("Shares")); if (dynamic_cast(haveWidget("price-label"))) setLabelText("price-label", priceLabel()); setLabelText("total-label", i18nc("Total value", "Total")); } bool Reinvest::isComplete(QString& reason) const { auto rc = Activity::isComplete(reason); rc &= haveCategoryAndAmount("interest-account", QString(), false); rc &= haveShares(); rc &= havePrice(); return rc; } bool Reinvest::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_D(Activity); Q_UNUSED(assetAccountSplit); Q_UNUSED(security); Q_UNUSED(currency); Q_UNUSED(m_feeSplits) QString reason; if (!isComplete(reason)) return false; - auto sharesEdit = dynamic_cast(haveWidget("shares")); - auto priceEdit = dynamic_cast(haveWidget("price")); + auto sharesEdit = dynamic_cast(haveWidget("shares")); + auto priceEdit = dynamic_cast(haveWidget("price")); s0.setAction(eMyMoney::Split::InvestmentTransactionType::ReinvestDividend); MyMoneyMoney shares = s0.shares(); MyMoneyMoney price; if (!s0.shares().isZero()) price = (s0.value() / s0.shares()).reduce(); if (!isMultiSelection() || (isMultiSelection() && !sharesEdit->value().isZero())) { shares = sharesEdit->value().abs(); s0.setShares(shares); s0.setValue((shares * price).reduce()); s0.setPrice(price); } if (!isMultiSelection() || (isMultiSelection() && !priceEdit->value().isZero())) { price = priceEdit->value().abs(); if (priceMode() == eDialogs::PriceMode::PricePerTransaction) { s0.setValue(price.reduce()); if (!s0.shares().isZero()) s0.setPrice((price / s0.shares()).reduce()); } else { s0.setValue((shares * price).reduce()); s0.setPrice(price); } } - if (!createCategorySplits(t, dynamic_cast(haveWidget("interest-account")), dynamic_cast(haveWidget("interest-amount")), MyMoneyMoney::MINUS_ONE, interestSplits, m_interestSplits)) + if (!createCategorySplits(t, dynamic_cast(haveWidget("interest-account")), dynamic_cast(haveWidget("interest-amount")), MyMoneyMoney::MINUS_ONE, interestSplits, m_interestSplits)) return false; if (interestSplits.count() != 1) { qDebug("more or less than one interest split in Reinvest::createTransaction. Not created."); return false; } MyMoneySplit& s1 = interestSplits[0]; MyMoneyMoney total = sumSplits(s0, feeSplits, QList()); s1.setValue(-total); if (!d->m_parent->setupPrice(t, s1)) return false; return true; } Add::Add(InvestTransactionEditor* editor) : Activity(editor) { } Add::~Add() { } eMyMoney::Split::InvestmentTransactionType Add::type() const { return eMyMoney::Split::InvestmentTransactionType::AddShares; } void Add::showWidgets() const { Q_D(const Activity); - auto shareEdit = dynamic_cast(haveWidget("shares")); + auto shareEdit = dynamic_cast(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(d->m_parent->security().smallestAccountFraction())); setLabelText("shares-label", i18n("Shares")); } bool Add::isComplete(QString& reason) const { auto rc = Activity::isComplete(reason); rc &= haveShares(); return rc; } bool Add::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(t); Q_UNUSED(assetAccountSplit); Q_UNUSED(m_feeSplits); Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); QString reason; if (!isComplete(reason)) return false; - auto sharesEdit = dynamic_cast(haveWidget("shares")); + auto sharesEdit = dynamic_cast(haveWidget("shares")); s0.setAction(eMyMoney::Split::InvestmentTransactionType::AddShares); s0.setShares(sharesEdit->value().abs()); s0.setValue(MyMoneyMoney()); s0.setPrice(MyMoneyMoney()); assetAccountSplit.setValue(MyMoneyMoney());// Clear any leftover value from previous Dividend. feeSplits.clear(); interestSplits.clear(); return true; } Remove::Remove(InvestTransactionEditor* editor) : Activity(editor) { } Remove::~Remove() { } eMyMoney::Split::InvestmentTransactionType Remove::type() const { return eMyMoney::Split::InvestmentTransactionType::RemoveShares; } void Remove::showWidgets() const { Q_D(const Activity); - auto shareEdit = dynamic_cast(haveWidget("shares")); + auto shareEdit = dynamic_cast(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(d->m_parent->security().smallestAccountFraction())); setLabelText("shares-label", i18n("Shares")); } bool Remove::isComplete(QString& reason) const { auto rc = Activity::isComplete(reason); rc &= haveShares(); return rc; } bool Remove::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(t); Q_UNUSED(assetAccountSplit); Q_UNUSED(m_feeSplits); Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); QString reason; if (!isComplete(reason)) return false; - auto sharesEdit = dynamic_cast(haveWidget("shares")); + auto sharesEdit = dynamic_cast(haveWidget("shares")); s0.setAction(eMyMoney::Split::InvestmentTransactionType::AddShares); s0.setShares(-(sharesEdit->value().abs())); s0.setValue(MyMoneyMoney()); s0.setPrice(MyMoneyMoney()); assetAccountSplit.setValue(MyMoneyMoney());// Clear any leftover value from previous Dividend. feeSplits.clear(); interestSplits.clear(); return true; } Invest::Split::Split(InvestTransactionEditor* editor) : Activity(editor) { } Invest::Split::~Split() { } eMyMoney::Split::InvestmentTransactionType Invest::Split::type() const { return eMyMoney::Split::InvestmentTransactionType::SplitShares; } void Invest::Split::showWidgets() const { // TODO do we need a special split ratio widget? // TODO maybe yes, currently the precision is the one of the fraction and might differ from it - auto shareEdit = dynamic_cast(haveWidget("shares")); + auto shareEdit = dynamic_cast(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(-1); setLabelText("shares-label", i18n("Ratio 1/")); } bool Invest::Split::isComplete(QString& reason) const { auto rc = Activity::isComplete(reason); rc &= haveShares(); return rc; } bool Invest::Split::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(t); Q_UNUSED(assetAccountSplit); Q_UNUSED(m_feeSplits); Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); - auto sharesEdit = dynamic_cast(haveWidget("shares")); + auto sharesEdit = dynamic_cast(haveWidget("shares")); KMyMoneyCategory* cat; cat = dynamic_cast(haveWidget("interest-account")); cat->parentWidget()->hide(); cat = dynamic_cast(haveWidget("fee-account")); cat->parentWidget()->hide(); s0.setAction(eMyMoney::Split::InvestmentTransactionType::SplitShares); s0.setShares(sharesEdit->value().abs()); s0.setValue(MyMoneyMoney()); s0.setPrice(MyMoneyMoney()); feeSplits.clear(); interestSplits.clear(); return true; } IntInc::IntInc(InvestTransactionEditor* editor) : Activity(editor) { } IntInc::~IntInc() { } eMyMoney::Split::InvestmentTransactionType IntInc::type() const { return eMyMoney::Split::InvestmentTransactionType::InterestIncome; } void IntInc::showWidgets() const { static const QStringList visibleWidgetIds = QStringList() << "asset-account" << "interest-amount" << "total" << "interest-account" << "fee-account"; setWidgetVisibility(visibleWidgetIds, true); static const QStringList hiddenWidgetIds = QStringList() << "shares" << "price" << "fee-amount"; setWidgetVisibility(hiddenWidgetIds, false); setLabelText("interest-amount-label", i18n("Interest")); setLabelText("interest-label", i18n("Interest")); setLabelText("fee-label", i18n("Fees")); setLabelText("asset-label", i18n("Account")); setLabelText("total-label", i18nc("Total value", "Total")); } bool IntInc::isComplete(QString& reason) const { Q_UNUSED(reason) auto rc = Activity::isComplete(reason); rc &= haveAssetAccount(); rc &= haveCategoryAndAmount("interest-account", QString(), false); rc &= haveInterest(false); return rc; } bool IntInc::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_D(Activity); Q_UNUSED(security); Q_UNUSED(currency); QString reason; if (!isComplete(reason)) return false; s0.setAction(eMyMoney::Split::InvestmentTransactionType::InterestIncome); // for dividends, we only use the stock split as a marker MyMoneyMoney shares; s0.setShares(shares); s0.setValue(shares); s0.setPrice(MyMoneyMoney::ONE); - if (!createCategorySplits(t, dynamic_cast(haveWidget("fee-account")), dynamic_cast(haveWidget("fee-amount")), MyMoneyMoney::ONE, feeSplits, m_feeSplits)) + if (!createCategorySplits(t, dynamic_cast(haveWidget("fee-account")), dynamic_cast(haveWidget("fee-amount")), MyMoneyMoney::ONE, feeSplits, m_feeSplits)) return false; - if (!createCategorySplits(t, dynamic_cast(haveWidget("interest-account")), dynamic_cast(haveWidget("interest-amount")), MyMoneyMoney::MINUS_ONE, interestSplits, m_interestSplits)) + if (!createCategorySplits(t, dynamic_cast(haveWidget("interest-account")), dynamic_cast(haveWidget("interest-amount")), MyMoneyMoney::MINUS_ONE, interestSplits, m_interestSplits)) return false; createAssetAccountSplit(assetAccountSplit, s0); MyMoneyMoney total = sumSplits(s0, feeSplits, interestSplits); assetAccountSplit.setValue(-total); if (!d->m_parent->setupPrice(t, assetAccountSplit)) return false; return true; } diff --git a/kmymoney/dialogs/investactivities.h b/kmymoney/dialogs/investactivities.h index 195cd38f2..91291558a 100644 --- a/kmymoney/dialogs/investactivities.h +++ b/kmymoney/dialogs/investactivities.h @@ -1,195 +1,195 @@ /*************************************************************************** investactivities.h ---------- 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. * * * ***************************************************************************/ #ifndef INVESTACTIVITIES_H #define INVESTACTIVITIES_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes class QWidget; class QStringList; -class kMyMoneyEdit; +class KMyMoneyEdit; class KMyMoneyCategory; class MyMoneyMoney; class MyMoneySplit; class MyMoneyTransaction; class MyMoneySecurity; class InvestTransactionEditor; namespace eMyMoney { namespace Split { enum class InvestmentTransactionType; } } namespace eDialogs { enum class PriceMode; } template class QList; template class QMap; namespace Invest { class ActivityPrivate; class Activity { Q_DISABLE_COPY(Activity) public: virtual eMyMoney::Split::InvestmentTransactionType type() const = 0; virtual void showWidgets() const = 0; virtual bool isComplete(QString& reason) const = 0; /** * Create a transaction @p t based on the split @p s0 and the data contained * in the widgets. In multiselection mode, @p assetAccountSplit, @p feeSplits, @p * interestSplits, @p security and @p currency are taken from the original * transaction and should be used as well. * * @return @p true if creation was successful, @p false otherwise */ virtual bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) = 0; virtual void preloadAssetAccount(); virtual ~Activity(); bool &memoChanged(); QString& memoText(); protected: explicit Activity(InvestTransactionEditor* editor); QWidget* haveWidget(const QString& name) const; bool haveAssetAccount() const; bool haveFees(bool optional = false) const; bool haveInterest(bool optional = false) const; bool haveShares() const; bool havePrice() const; bool isMultiSelection() const; QString priceLabel() const; - bool createCategorySplits(const MyMoneyTransaction& t, KMyMoneyCategory* cat, kMyMoneyEdit* amount, MyMoneyMoney factor, QList&splits, const QList& osplits) const; + bool createCategorySplits(const MyMoneyTransaction& t, KMyMoneyCategory* cat, KMyMoneyEdit* amount, MyMoneyMoney factor, QList&splits, const QList& osplits) const; void createAssetAccountSplit(MyMoneySplit& split, const MyMoneySplit& stockSplit) const; MyMoneyMoney sumSplits(const MyMoneySplit& s0, const QList& feeSplits, const QList& interestSplits) const; bool haveCategoryAndAmount(const QString& category, const QString& amount, bool optional) const; void setLabelText(const QString& idx, const QString& txt) const; void setWidgetVisibility(const QStringList& widgetIds, bool visible) const; eDialogs::PriceMode priceMode() const; protected: ActivityPrivate* d_ptr; Q_DECLARE_PRIVATE(Activity) }; class Buy : public Activity { public: explicit Buy(InvestTransactionEditor* editor); ~Buy() override; eMyMoney::Split::InvestmentTransactionType type() const override; void showWidgets() const override; bool isComplete(QString& reason) const override; bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) override; }; class Sell : public Activity { public: explicit Sell(InvestTransactionEditor* editor); ~Sell() override; eMyMoney::Split::InvestmentTransactionType type() const override; void showWidgets() const override; bool isComplete(QString& reason) const override; bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) override; }; class Div : public Activity { public: explicit Div(InvestTransactionEditor* editor); ~Div() override; eMyMoney::Split::InvestmentTransactionType type() const override; void showWidgets() const override; bool isComplete(QString& reason) const override; bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) override; }; class Reinvest : public Activity { public: explicit Reinvest(InvestTransactionEditor* editor); ~Reinvest() override; eMyMoney::Split::InvestmentTransactionType type() const override; void showWidgets() const override; bool isComplete(QString& reason) const override; bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) override; }; class Add : public Activity { public: explicit Add(InvestTransactionEditor* editor); ~Add() override; eMyMoney::Split::InvestmentTransactionType type() const override; void showWidgets() const override; bool isComplete(QString& reason) const override; bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) override; }; class Remove : public Activity { public: explicit Remove(InvestTransactionEditor* editor); ~Remove() override; eMyMoney::Split::InvestmentTransactionType type() const override; void showWidgets() const override; bool isComplete(QString& reason) const override; bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) override; }; class Split : public Activity { public: explicit Split(InvestTransactionEditor* editor); ~Split() override; eMyMoney::Split::InvestmentTransactionType type() const override; void showWidgets() const override; bool isComplete(QString& reason) const override; bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) override; }; class IntInc : public Activity { public: explicit IntInc(InvestTransactionEditor* editor); ~IntInc() override; eMyMoney::Split::InvestmentTransactionType type() const override; void showWidgets() const override; bool isComplete(QString& reason) const override; bool createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, QList& feeSplits, QList& m_feeSplits, QList& interestSplits, QList& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) override; }; } // namespace Invest #endif // INVESTACTIVITIES_H diff --git a/kmymoney/dialogs/investtransactioneditor.cpp b/kmymoney/dialogs/investtransactioneditor.cpp index e1a25eb3f..9946a72e7 100644 --- a/kmymoney/dialogs/investtransactioneditor.cpp +++ b/kmymoney/dialogs/investtransactioneditor.cpp @@ -1,1221 +1,1223 @@ /*************************************************************************** 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 "selectedtransactions.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))) { + Q_Q(InvestTransactionEditor); + if (KMyMoneyCategory* cat = dynamic_cast(q->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)); + KMyMoneyEdit* amount = dynamic_cast(q->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) { + if ((w = q->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")); + auto label = dynamic_cast(q->haveWidget("price-label")); if (label) { - auto sharesEdit = dynamic_cast(haveWidget("shares")); - auto priceEdit = dynamic_cast(haveWidget("price")); + auto sharesEdit = dynamic_cast(q->haveWidget("shares")); + auto priceEdit = dynamic_cast(q->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; + 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(0, false); + auto asset = new KMyMoneyCategory(false, nullptr); 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(0, true); + auto fees = new KMyMoneyCategory(true, nullptr); 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(0, true); + auto interest = new KMyMoneyCategory(true, nullptr); 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; + 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); + connect(value, &KMyMoneyEdit::textChanged, this, &InvestTransactionEditor::slotUpdateButtonState); + connect(value, &KMyMoneyEdit::valueChanged, this, &InvestTransactionEditor::slotUpdateTotalAmount); - value = new kMyMoneyEdit; + 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); + connect(value, &KMyMoneyEdit::textChanged, this, &InvestTransactionEditor::slotUpdateButtonState); + connect(value, &KMyMoneyEdit::valueChanged, this, &InvestTransactionEditor::slotUpdateTotalAmount); - value = new kMyMoneyEdit; + 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); + connect(value, &KMyMoneyEdit::textChanged, this, &InvestTransactionEditor::slotUpdateButtonState); + connect(value, &KMyMoneyEdit::valueChanged, this, &InvestTransactionEditor::slotUpdateTotalAmount); - value = new kMyMoneyEdit; + 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); + 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")); + 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) +void InvestTransactionEditor::loadEditWidgets(eWidgets::eRegister::Action) { loadEditWidgets(); } void InvestTransactionEditor::loadEditWidgets() { Q_D(InvestTransactionEditor); QString id; - auto postDate = dynamic_cast(haveWidget("postdate")); + 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; + 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")); + 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 = dynamic_cast(haveWidget("fee-amount")); value->setValue(d->subtotal(d->m_feeSplits)); // interest amount - value = dynamic_cast(haveWidget("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 = 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())); + dynamic_cast(haveWidget("shares"))->setPrecision(MyMoneyMoney::denomToPrec(d->m_security.smallestAccountFraction())); } else { - dynamic_cast(haveWidget("shares"))->setPrecision(-1); + 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")); + 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 if (d->m_editWidgets.isEmpty()) return; 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"]); + 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/investtransactioneditor.h b/kmymoney/dialogs/investtransactioneditor.h index e38eae9fe..dcc04f04e 100644 --- a/kmymoney/dialogs/investtransactioneditor.h +++ b/kmymoney/dialogs/investtransactioneditor.h @@ -1,155 +1,155 @@ /*************************************************************************** investtransactioneditor.h ---------- 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. * * * ***************************************************************************/ #ifndef INVESTTRANSACTIONEDITOR_H #define INVESTTRANSACTIONEDITOR_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "transactioneditor.h" class MyMoneyMoney; class MyMoneySecurity; namespace eDialogs { enum class PriceMode; } namespace KMyMoneyRegister { class InvestTransaction; } namespace eMyMoney { namespace Split { enum class InvestmentTransactionType; } } class InvestTransactionEditorPrivate; class InvestTransactionEditor : public TransactionEditor { Q_OBJECT Q_DISABLE_COPY(InvestTransactionEditor) public: InvestTransactionEditor(); explicit InvestTransactionEditor(TransactionEditorContainer* regForm, KMyMoneyRegister::InvestTransaction* item, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate); ~InvestTransactionEditor() override; /** * This method returns information about the completeness of the data * entered. This can be used to control the availability of the * 'Enter transaction' action. * * @retval true if entering the transaction into the engine * @retval false if not enough information is present to enter the * transaction into the engine * * @param reason will be filled with a string about the reason why the * completeness is not reached. Empty if the return value * is @c true. * * @sa transactionDataSufficient() */ bool isComplete(QString& reason) const override; QWidget* firstWidget() const override; bool fixTransactionCommodity(const MyMoneyAccount& /* account */) override; void totalAmount(MyMoneyMoney& amount) const; bool setupPrice(const MyMoneyTransaction& t, MyMoneySplit& split); /** * This method creates a transaction based on the contents of the current widgets, * the splits in m_split in single selection mode or an existing transaction/split * and the contents of the widgets in multi selection mode. * * The split referencing the current account is returned as the first split in the * transaction's split list. * * @param t reference to created transaction * @param torig the original transaction * @param sorig the original split * * @param skipPriceDialog if @p true the user will not be requested for price information * (defaults to @p false) * * @return @p false if aborted by user, @p true otherwise * * @note Usually not used directly. If unsure, use enterTransactions() instead. */ bool createTransaction(MyMoneyTransaction& t, const MyMoneyTransaction& torig, const MyMoneySplit& sorig, bool skipPriceDialog = false) override; eDialogs::PriceMode priceMode() const; MyMoneySecurity security() const; QList feeSplits() const; QList interestSplits() const; protected slots: void slotCreateSecurity(const QString& name, QString& id); void slotCreateFeeCategory(const QString& name, QString& id); void slotCreateInterestCategory(const QString& name, QString& id); int slotEditInterestSplits(); int slotEditFeeSplits(); void slotReloadEditWidgets(); void slotUpdateActivity(eMyMoney::Split::InvestmentTransactionType); void slotUpdateSecurity(const QString& stockId); void slotUpdateInterestCategory(const QString& id); void slotUpdateInterestVisibility(const QString&); void slotUpdateFeeCategory(const QString& id); void slotUpdateFeeVisibility(const QString&); void slotUpdateTotalAmount(); void slotTransactionContainerGeometriesUpdated(); void slotUpdateInvestMemoState(); protected: /** * This method creates all necessary widgets for this transaction editor. * All signals will be connected to the relevant slots. */ void createEditWidgets() override; /** * This method (re-)loads the widgets with the transaction information * contained in @a m_transaction and @a m_split. * * @param action preset the edit wigdets for @a action if no transaction * is present */ - void loadEditWidgets(KMyMoneyRegister::Action action) override; + void loadEditWidgets(eWidgets::eRegister::Action action) override; void loadEditWidgets() override; void setupFinalWidgets() override; private: Q_DECLARE_PRIVATE(InvestTransactionEditor) }; #endif // INVESTTRANSACTIONEDITOR_H diff --git a/kmymoney/dialogs/kcategoryreassigndlg.cpp b/kmymoney/dialogs/kcategoryreassigndlg.cpp index cc24712ea..3b68f7df6 100644 --- a/kmymoney/dialogs/kcategoryreassigndlg.cpp +++ b/kmymoney/dialogs/kcategoryreassigndlg.cpp @@ -1,107 +1,107 @@ /*************************************************************************** kcategoryreassigndlg.cpp ------------------- copyright : (C) 2007 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 "kcategoryreassigndlg.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kcategoryreassigndlg.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "kmymoneycategory.h" #include "kmymoneyaccountselector.h" KCategoryReassignDlg::KCategoryReassignDlg(QWidget* parent) : QDialog(parent), ui(new Ui::KCategoryReassignDlg) { ui->setupUi(this); - auto mandatory = new kMandatoryFieldGroup(this); + auto mandatory = new KMandatoryFieldGroup(this); mandatory->add(ui->m_category); mandatory->setOkButton(ui->buttonBox->button(QDialogButtonBox::Ok)); } KCategoryReassignDlg::~KCategoryReassignDlg() { delete ui; } QString KCategoryReassignDlg::show(const MyMoneyAccount& category) { if (category.id().isEmpty()) return QString(); // no payee available? nothing can be selected... AccountSet set; set.addAccountGroup(eMyMoney::Account::Income); set.addAccountGroup(eMyMoney::Account::Expense); set.load(ui->m_category->selector()); // remove the category we are about to delete ui->m_category->selector()->removeItem(category.id()); // make sure the available categories have the same currency QStringList list; QStringList::const_iterator it_a; ui->m_category->selector()->itemList(list); for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { MyMoneyAccount acc = MyMoneyFile::instance()->account(*it_a); if (acc.currencyId() != category.currencyId()) ui->m_category->selector()->removeItem(*it_a); } // reload the list ui->m_category->selector()->itemList(list); // if there is no category for reassignment left, we bail out if (list.isEmpty()) { KMessageBox::sorry(this, QString("") + i18n("At least one transaction/schedule still references the category %1. However, at least one category with the same currency must exist so that the transactions/schedules can be reassigned.", category.name()) + QString("")); return QString(); } // execute dialog and if aborted, return empty string if (this->exec() == QDialog::Rejected) return QString(); // otherwise return index of selected payee return ui->m_category->selectedItem(); } void KCategoryReassignDlg::accept() { // force update of payeeCombo ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); if (ui->m_category->selectedItem().isEmpty()) KMessageBox::information(this, i18n("This dialog does not allow new categories to be created. Please pick a category from the list."), i18n("Category creation")); else QDialog::accept(); } diff --git a/kmymoney/dialogs/kcurrencycalculator.cpp b/kmymoney/dialogs/kcurrencycalculator.cpp index fbcc5d6d1..2f2744949 100644 --- a/kmymoney/dialogs/kcurrencycalculator.cpp +++ b/kmymoney/dialogs/kcurrencycalculator.cpp @@ -1,384 +1,385 @@ /*************************************************************************** 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); + 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/kcurrencycalculator.ui b/kmymoney/dialogs/kcurrencycalculator.ui index 5cc298eef..a4b72c361 100644 --- a/kmymoney/dialogs/kcurrencycalculator.ui +++ b/kmymoney/dialogs/kcurrencycalculator.ui @@ -1,400 +1,400 @@ KCurrencyCalculator true 0 0 387 268 Exchange Rate/Price Editor true QFrame::NoFrame QFrame::Plain 0 0 0 0 Amount false xxx false Qt::Horizontal QSizePolicy::Expanding 80 20 Date false xxx false Qt::Horizontal QSizePolicy::Expanding 145 20 Convert 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false to Qt::AlignCenter false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false - + true 0 0 0 0 QFrame::NoFrame QFrame::Plain 0 0 0 0 To a&mount buttonGroup1 Exchange &rate / Price buttonGroup1 QFrame::NoFrame QFrame::Plain 0 0 0 0 Date false - + xx xx false - + Update price history true Qt::Vertical QSizePolicy::Expanding 20 16 QDialogButtonBox::Cancel|QDialogButtonBox::Ok - kMyMoneyDateInput + KMyMoneyDateInput QWidget
kmymoneydateinput.h
1
- kMyMoneyEdit + KMyMoneyEdit QWidget
kmymoneyedit.h
m_amountButton m_rateButton m_updateButton buttonBox accepted() KCurrencyCalculator accept() 189 233 193 133 buttonBox rejected() KCurrencyCalculator reject() 189 233 193 133
diff --git a/kmymoney/dialogs/keditscheduledlg.cpp b/kmymoney/dialogs/keditscheduledlg.cpp index c83f3e396..157307c60 100644 --- a/kmymoney/dialogs/keditscheduledlg.cpp +++ b/kmymoney/dialogs/keditscheduledlg.cpp @@ -1,661 +1,664 @@ /*************************************************************************** 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 "tabbar.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" +#include "widgetenums.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 = 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())); + ui->m_paymentInformationLayout->insertWidget(2, ui->m_form->getTabBar(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->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; + 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; + eWidgets::eRegister::Action action = eWidgets::eRegister::Action::Withdrawal; switch (d->m_schedule.type()) { case Schedule::Type::Deposit: - action = KMyMoneyRegister::ActionDeposit; + action = eWidgets::eRegister::Action::Deposit; break; case Schedule::Type::Bill: - action = KMyMoneyRegister::ActionWithdrawal; + action = eWidgets::eRegister::Action::Withdrawal; editor->setPaymentMethod(d->m_schedule.paymentType()); break; case Schedule::Type::Transfer: - action = KMyMoneyRegister::ActionTransfer; + action = eWidgets::eRegister::Action::Transfer; 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; + action = eWidgets::eRegister::Action::Transfer; else if (isDeposit) - action = KMyMoneyRegister::ActionDeposit; + action = eWidgets::eRegister::Action::Deposit; } 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()); + 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")); + KMyMoneyDateInput* dateEdit = dynamic_cast(editor->haveWidget("postdate")); if (dateEdit) - connect(dateEdit, &kMyMoneyDateInput::dateChanged, this, &KEditScheduleDlg::slotPostDateChanged); + 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: + switch (static_cast(tabbar->currentIndex())) { + case eWidgets::eRegister::Action::Deposit: d->m_schedule.setType(Schedule::Type::Deposit); break; default: - case KMyMoneyRegister::ActionWithdrawal: + case eWidgets::eRegister::Action::Withdrawal: d->m_schedule.setType(Schedule::Type::Bill); break; - case KMyMoneyRegister::ActionTransfer: + case eWidgets::eRegister::Action::Transfer: 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); + d->ui->m_register->resize((int)eWidgets::eTransaction::Column::Detail); + d->ui->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1); 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")); + 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")); + 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")); + 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")); + 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")); + 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) { + eWidgets::eRegister::Action action = static_cast(index); + if (action != eWidgets::eRegister::Action::Withdrawal) { 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) { + if (action != eWidgets::eRegister::Action::Deposit) { 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 0b686493f..9c43c0e98 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 -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.h
1
KMyMoneyOccurrencePeriodCombo QWidget -
kmymoneymvccombo.h
+
kmymoneyoccurrenceperiodcombo.h
1
KMyMoneyTransactionForm::TransactionForm QWidget
transactionform.h
- kMyMoneyDateInput + 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 8fe3f430a..533a842ef 100644 --- a/kmymoney/dialogs/kenterscheduledlg.cpp +++ b/kmymoney/dialogs/kenterscheduledlg.cpp @@ -1,379 +1,384 @@ /*************************************************************************** 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 "tabbar.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" +#include "widgetenums.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()); + KMyMoneyTransactionForm::TabBar* tabbar = d->ui->m_form->getTabBar(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); + d->ui->m_register->resize((int)eWidgets::eTransaction::Column::Detail); + d->ui->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1); 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; + eWidgets::eRegister::Action action = eWidgets::eRegister::Action::Withdrawal; switch (d->m_schedule.type()) { case eMyMoney::Schedule::Type::Transfer: - action = KMyMoneyRegister::ActionTransfer; + action = eWidgets::eRegister::Action::Transfer; break; case eMyMoney::Schedule::Type::Deposit: - action = KMyMoneyRegister::ActionDeposit; + action = eWidgets::eRegister::Action::Deposit; break; case eMyMoney::Schedule::Type::LoanPayment: switch (d->m_schedule.paymentType()) { case eMyMoney::Schedule::PaymentType::DirectDeposit: case eMyMoney::Schedule::PaymentType::ManualDeposit: - action = KMyMoneyRegister::ActionDeposit; + action = eWidgets::eRegister::Action::Deposit; 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); + 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()); + 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")); + 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/kequitypriceupdatedlg.cpp b/kmymoney/dialogs/kequitypriceupdatedlg.cpp index 19871db2c..3db7d5464 100644 --- a/kmymoney/dialogs/kequitypriceupdatedlg.cpp +++ b/kmymoney/dialogs/kequitypriceupdatedlg.cpp @@ -1,805 +1,805 @@ /*************************************************************************** kequitypriceupdatedlg.cpp - description ------------------- begin : Mon Sep 1 2003 copyright : (C) 2000-2003 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio Ace Jones (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 "kequitypriceupdatedlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kequitypriceupdatedlg.h" #include "kmymoney.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneysecurity.h" #include "mymoneyprice.h" #include "webpricequote.h" #include "kequitypriceupdateconfdlg.h" #include "dialogenums.h" #define WEBID_COL 0 #define NAME_COL 1 #define PRICE_COL 2 #define DATE_COL 3 #define KMMID_COL 4 #define SOURCE_COL 5 class KEquityPriceUpdateDlgPrivate { Q_DISABLE_COPY(KEquityPriceUpdateDlgPrivate) Q_DECLARE_PUBLIC(KEquityPriceUpdateDlg) public: KEquityPriceUpdateDlgPrivate(KEquityPriceUpdateDlg *qq) : q_ptr(qq), ui(new Ui::KEquityPriceUpdateDlg) { } ~KEquityPriceUpdateDlgPrivate() { delete ui; } void init(const QString& securityId) { Q_Q(KEquityPriceUpdateDlg); ui->setupUi(q); m_fUpdateAll = false; QStringList headerList; headerList << i18n("ID") << i18nc("Equity name", "Name") << i18n("Price") << i18n("Date"); ui->lvEquityList->header()->setSortIndicator(0, Qt::AscendingOrder); ui->lvEquityList->setColumnWidth(NAME_COL, 125); // This is a "get it up and running" hack. Will replace this in the future. headerList << i18nc("Internal identifier", "Internal ID") << i18nc("Online quote source", "Source"); ui->lvEquityList->setColumnWidth(KMMID_COL, 0); ui->lvEquityList->setHeaderLabels(headerList); ui->lvEquityList->setSelectionMode(QAbstractItemView::MultiSelection); ui->lvEquityList->setAllColumnsShowFocus(true); ui->btnUpdateAll->setEnabled(false); auto file = MyMoneyFile::instance(); // // Add each price pair that we know about // // send in securityId == "XXX YYY" to get a single-shot update for XXX to YYY. // for consistency reasons, this accepts the same delimiters as WebPriceQuote::launch() QRegExp splitrx("([0-9a-z\\.]+)[^a-z0-9]+([0-9a-z\\.]+)", Qt::CaseInsensitive); MyMoneySecurityPair currencyIds; if (splitrx.indexIn(securityId) != -1) { currencyIds = MyMoneySecurityPair(splitrx.cap(1), splitrx.cap(2)); } MyMoneyPriceList prices = file->priceList(); for (MyMoneyPriceList::ConstIterator it_price = prices.constBegin(); it_price != prices.constEnd(); ++it_price) { const MyMoneySecurityPair& pair = it_price.key(); if (file->security(pair.first).isCurrency() && (securityId.isEmpty() || (pair == currencyIds))) { const MyMoneyPriceEntries& entries = (*it_price); if (entries.count() > 0 && entries.begin().key() <= QDate::currentDate()) { addPricePair(pair, false); ui->btnUpdateAll->setEnabled(true); } } } // // Add each investment // QList securities = file->securityList(); for (QList::const_iterator it = securities.constBegin(); it != securities.constEnd(); ++it) { if (!(*it).isCurrency() && (securityId.isEmpty() || ((*it).id() == securityId)) && !(*it).value("kmm-online-source").isEmpty() ) { addInvestment(*it); ui->btnUpdateAll->setEnabled(true); } } // if list is empty, add the request price pair if (ui->lvEquityList->invisibleRootItem()->childCount() == 0) { addPricePair(currencyIds, true); } q->connect(ui->btnUpdateSelected, &QAbstractButton::clicked, q, &KEquityPriceUpdateDlg::slotUpdateSelectedClicked); q->connect(ui->btnUpdateAll, &QAbstractButton::clicked, q, &KEquityPriceUpdateDlg::slotUpdateAllClicked); - q->connect(ui->m_fromDate, &kMyMoneyDateInput::dateChanged, q, &KEquityPriceUpdateDlg::slotDateChanged); - q->connect(ui->m_toDate, &kMyMoneyDateInput::dateChanged, q, &KEquityPriceUpdateDlg::slotDateChanged); + q->connect(ui->m_fromDate, &KMyMoneyDateInput::dateChanged, q, &KEquityPriceUpdateDlg::slotDateChanged); + q->connect(ui->m_toDate, &KMyMoneyDateInput::dateChanged, q, &KEquityPriceUpdateDlg::slotDateChanged); q->connect(&m_webQuote, &WebPriceQuote::csvquote, q, &KEquityPriceUpdateDlg::slotReceivedCSVQuote); q->connect(&m_webQuote, &WebPriceQuote::quote, q, &KEquityPriceUpdateDlg::slotReceivedQuote); q->connect(&m_webQuote, &WebPriceQuote::failed, q, &KEquityPriceUpdateDlg::slotQuoteFailed); q->connect(&m_webQuote, &WebPriceQuote::status, q, &KEquityPriceUpdateDlg::logStatusMessage); q->connect(&m_webQuote, &WebPriceQuote::error, q, &KEquityPriceUpdateDlg::logErrorMessage); q->connect(ui->lvEquityList, &QTreeWidget::itemSelectionChanged, q, &KEquityPriceUpdateDlg::slotUpdateSelection); q->connect(ui->btnConfigure, &QAbstractButton::clicked, q, &KEquityPriceUpdateDlg::slotConfigureClicked); if (!securityId.isEmpty()) { ui->btnUpdateSelected->hide(); ui->btnUpdateAll->hide(); // delete layout1; QTimer::singleShot(100, q, SLOT(slotUpdateAllClicked())); } // Hide OK button until we have received the first update ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); q->slotUpdateSelection(); // previous versions of this dialog allowed to store a "Don't ask again" switch. // Since we don't support it anymore, we just get rid of it KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group("Notification Messages"); grp.deleteEntry("KEquityPriceUpdateDlg::slotQuoteFailed::Price Update Failed"); grp.sync(); grp = config->group("Equity Price Update"); int policyValue = grp.readEntry("PriceUpdatingPolicy", (int)eDialogs::UpdatePrice::Missing); if (policyValue > (int)eDialogs::UpdatePrice::Ask || policyValue < (int)eDialogs::UpdatePrice::All) m_updatingPricePolicy = eDialogs::UpdatePrice::Missing; else m_updatingPricePolicy = static_cast(policyValue); } void addPricePair(const MyMoneySecurityPair& pair, bool dontCheckExistance) { auto file = MyMoneyFile::instance(); const auto symbol = QString::fromLatin1("%1 > %2").arg(pair.first, pair.second); const auto id = QString::fromLatin1("%1 %2").arg(pair.first, pair.second); // Check that the pair does not already exist if (ui->lvEquityList->findItems(id, Qt::MatchExactly, KMMID_COL).empty()) { const MyMoneyPrice &pr = file->price(pair.first, pair.second); if (pr.source() != QLatin1String("KMyMoney")) { bool keep = true; if ((pair.first == file->baseCurrency().id()) || (pair.second == file->baseCurrency().id())) { const QString& foreignCurrency = file->foreignCurrency(pair.first, pair.second); // check that the foreign currency is still in use QList::const_iterator it_a; QList list; file->accountList(list); for (it_a = list.constBegin(); !dontCheckExistance && it_a != list.constEnd(); ++it_a) { // if it's an account denominated in the foreign currency // keep it if (((*it_a).currencyId() == foreignCurrency) && !(*it_a).isClosed()) break; // if it's an investment traded in the foreign currency // keep it if ((*it_a).isInvest() && !(*it_a).isClosed()) { MyMoneySecurity sec = file->security((*it_a).currencyId()); if (sec.tradingCurrency() == foreignCurrency) break; } } // if it is in use, it_a is not equal to list.end() if (it_a == list.constEnd() && !dontCheckExistance) keep = false; } if (keep) { auto item = new QTreeWidgetItem(); item->setText(WEBID_COL, symbol); item->setText(NAME_COL, i18n("%1 units in %2", pair.first, pair.second)); if (pr.isValid()) { MyMoneySecurity fromCurrency = file->currency(pair.second); MyMoneySecurity toCurrency = file->currency(pair.first); item->setText(PRICE_COL, pr.rate(pair.second).formatMoney(fromCurrency.tradingSymbol(), toCurrency.pricePrecision())); item->setText(DATE_COL, pr.date().toString(Qt::ISODate)); } item->setText(KMMID_COL, id); item->setText(SOURCE_COL, "KMyMoney Currency"); // This string value should not be localized ui->lvEquityList->invisibleRootItem()->addChild(item); } } } } void addInvestment(const MyMoneySecurity& inv) { const auto id = inv.id(); // Check that the pair does not already exist if (ui->lvEquityList->findItems(id, Qt::MatchExactly, KMMID_COL).empty()) { auto file = MyMoneyFile::instance(); // check that the security is still in use QList::const_iterator it_a; QList list; file->accountList(list); for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { if ((*it_a).isInvest() && ((*it_a).currencyId() == inv.id()) && !(*it_a).isClosed()) break; } // if it is in use, it_a is not equal to list.end() if (it_a != list.constEnd()) { QString webID; WebPriceQuoteSource onlineSource(inv.value("kmm-online-source")); if (onlineSource.m_webIDBy == WebPriceQuoteSource::identifyBy::IdentificationNumber) webID = inv.value("kmm-security-id"); // insert ISIN number... else if (onlineSource.m_webIDBy == WebPriceQuoteSource::identifyBy::Name) webID = inv.name(); // ...or name... else webID = inv.tradingSymbol(); // ...or symbol QTreeWidgetItem* item = new QTreeWidgetItem(); item->setForeground(WEBID_COL, KColorScheme(QPalette::Normal).foreground(KColorScheme::NormalText)); if (webID.isEmpty()) { webID = i18n("[No identifier]"); item->setForeground(WEBID_COL, KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText)); } item->setText(WEBID_COL, webID); item->setText(NAME_COL, inv.name()); MyMoneySecurity currency = file->currency(inv.tradingCurrency()); const MyMoneyPrice &pr = file->price(id.toUtf8(), inv.tradingCurrency()); if (pr.isValid()) { item->setText(PRICE_COL, pr.rate(currency.id()).formatMoney(currency.tradingSymbol(), inv.pricePrecision())); item->setText(DATE_COL, pr.date().toString(Qt::ISODate)); } item->setText(KMMID_COL, id); if (inv.value("kmm-online-quote-system") == "Finance::Quote") item->setText(SOURCE_COL, QString("Finance::Quote %1").arg(inv.value("kmm-online-source"))); else item->setText(SOURCE_COL, inv.value("kmm-online-source")); ui->lvEquityList->invisibleRootItem()->addChild(item); // If this investment is denominated in a foreign currency, ensure that // the appropriate price pair is also on the list if (currency.id() != file->baseCurrency().id()) { addPricePair(MyMoneySecurityPair(currency.id(), file->baseCurrency().id()), false); } } } } KEquityPriceUpdateDlg *q_ptr; Ui::KEquityPriceUpdateDlg *ui; bool m_fUpdateAll; eDialogs::UpdatePrice m_updatingPricePolicy; WebPriceQuote m_webQuote; }; KEquityPriceUpdateDlg::KEquityPriceUpdateDlg(QWidget *parent, const QString& securityId) : QDialog(parent), d_ptr(new KEquityPriceUpdateDlgPrivate(this)) { Q_D(KEquityPriceUpdateDlg); d->init(securityId); } KEquityPriceUpdateDlg::KEquityPriceUpdateDlg(QWidget *parent) : KEquityPriceUpdateDlg(parent, QString()) { } KEquityPriceUpdateDlg::~KEquityPriceUpdateDlg() { Q_D(KEquityPriceUpdateDlg); auto config = KSharedConfig::openConfig(); auto grp = config->group("Equity Price Update"); grp.writeEntry("PriceUpdatingPolicy", static_cast(d->m_updatingPricePolicy)); grp.sync(); delete d; } void KEquityPriceUpdateDlg::logErrorMessage(const QString& message) { logStatusMessage(QString("") + message + QString("")); } void KEquityPriceUpdateDlg::logStatusMessage(const QString& message) { Q_D(KEquityPriceUpdateDlg); d->ui->lbStatus->append(message); } MyMoneyPrice KEquityPriceUpdateDlg::price(const QString& id) const { Q_D(const KEquityPriceUpdateDlg); MyMoneyPrice price; QTreeWidgetItem* item = nullptr; QList foundItems = d->ui->lvEquityList->findItems(id, Qt::MatchExactly, KMMID_COL); if (! foundItems.empty()) item = foundItems.at(0); if (item) { MyMoneyMoney rate(item->text(PRICE_COL)); if (!rate.isZero()) { QString id = item->text(KMMID_COL).toUtf8(); // if the ID has a space, then this is TWO ID's, so it's a currency quote if (id.contains(" ")) { QStringList ids = id.split(' ', QString::SkipEmptyParts); QString fromid = ids[0].toUtf8(); QString toid = ids[1].toUtf8(); price = MyMoneyPrice(fromid, toid, QDate().fromString(item->text(DATE_COL), Qt::ISODate), rate, item->text(SOURCE_COL)); } else // otherwise, it's a security quote { MyMoneySecurity security = MyMoneyFile::instance()->security(id); price = MyMoneyPrice(id, security.tradingCurrency(), QDate().fromString(item->text(DATE_COL), Qt::ISODate), rate, item->text(SOURCE_COL)); } } } return price; } void KEquityPriceUpdateDlg::storePrices() { Q_D(KEquityPriceUpdateDlg); // update the new prices into the equities auto file = MyMoneyFile::instance(); QString name; MyMoneyFileTransaction ft; try { for (auto i = 0; i < d->ui->lvEquityList->invisibleRootItem()->childCount(); ++i) { QTreeWidgetItem* item = d->ui->lvEquityList->invisibleRootItem()->child(i); // turn on signals before we modify the last entry in the list file->blockSignals(i < d->ui->lvEquityList->invisibleRootItem()->childCount() - 1); MyMoneyMoney rate(item->text(PRICE_COL)); if (!rate.isZero()) { QString id = item->text(KMMID_COL); QString fromid; QString toid; // if the ID has a space, then this is TWO ID's, so it's a currency quote if (id.contains(QLatin1Char(' '))) { QStringList ids = id.split(QLatin1Char(' '), QString::SkipEmptyParts); fromid = ids.at(0); toid = ids.at(1); name = QString::fromLatin1("%1 --> %2").arg(fromid, toid); } else { // otherwise, it's a security quote MyMoneySecurity security = file->security(id); name = security.name(); fromid = id; toid = security.tradingCurrency(); } // TODO (Ace) Better handling of the case where there is already a price // for this date. Currently, it just overrides the old value. Really it // should check to see if the price is the same and prompt the user. file->addPrice(MyMoneyPrice(fromid, toid, QDate::fromString(item->text(DATE_COL), Qt::ISODate), rate, item->text(SOURCE_COL))); } } ft.commit(); } catch (const MyMoneyException &) { qDebug("Unable to add price information for %s", qPrintable(name)); } } void KEquityPriceUpdateDlg::slotConfigureClicked() { Q_D(KEquityPriceUpdateDlg); QPointer dlg = new EquityPriceUpdateConfDlg(d->m_updatingPricePolicy); if (dlg->exec() == QDialog::Accepted) d->m_updatingPricePolicy = dlg->policy(); delete dlg; } void KEquityPriceUpdateDlg::slotUpdateSelection() { Q_D(KEquityPriceUpdateDlg); // Only enable the update button if there is a selection d->ui->btnUpdateSelected->setEnabled(false); if (! d->ui->lvEquityList->selectedItems().empty()) d->ui->btnUpdateSelected->setEnabled(true); } void KEquityPriceUpdateDlg::slotUpdateSelectedClicked() { Q_D(KEquityPriceUpdateDlg); // disable sorting while the update is running to maintain the current order of items on which // the update process depends and which could be changed with sorting enabled due to the updated values d->ui->lvEquityList->setSortingEnabled(false); auto item = d->ui->lvEquityList->invisibleRootItem()->child(0); auto skipCnt = 1; while (item && !item->isSelected()) { item = d->ui->lvEquityList->invisibleRootItem()->child(skipCnt); ++skipCnt; } d->m_webQuote.setDate(d->ui->m_fromDate->date(), d->ui->m_toDate->date()); if (item) { d->ui->prgOnlineProgress->setMaximum(1 + d->ui->lvEquityList->invisibleRootItem()->childCount()); d->ui->prgOnlineProgress->setValue(skipCnt); d->m_webQuote.launch(item->text(WEBID_COL), item->text(KMMID_COL), item->text(SOURCE_COL)); } else { logErrorMessage("No security selected."); } } void KEquityPriceUpdateDlg::slotUpdateAllClicked() { Q_D(KEquityPriceUpdateDlg); // disable sorting while the update is running to maintain the current order of items on which // the update process depends and which could be changed with sorting enabled due to the updated values d->ui->lvEquityList->setSortingEnabled(false); QTreeWidgetItem* item = d->ui->lvEquityList->invisibleRootItem()->child(0); if (item) { d->ui->prgOnlineProgress->setMaximum(1 + d->ui->lvEquityList->invisibleRootItem()->childCount()); d->ui->prgOnlineProgress->setValue(1); d->m_fUpdateAll = true; d->m_webQuote.launch(item->text(WEBID_COL), item->text(KMMID_COL), item->text(SOURCE_COL)); } else { logErrorMessage("Security list is empty."); } } void KEquityPriceUpdateDlg::slotDateChanged() { Q_D(KEquityPriceUpdateDlg); d->ui->m_fromDate->blockSignals(true); d->ui->m_toDate->blockSignals(true); if (d->ui->m_toDate->date() > QDate::currentDate()) d->ui->m_toDate->setDate(QDate::currentDate()); if (d->ui->m_toDate->date() < d->ui->m_fromDate->date()) d->ui->m_fromDate->setDate(d->ui->m_toDate->date()); d->ui->m_fromDate->blockSignals(false); d->ui->m_toDate->blockSignals(false); } void KEquityPriceUpdateDlg::slotQuoteFailed(const QString& _kmmID, const QString& _webID) { Q_D(KEquityPriceUpdateDlg); auto foundItems = d->ui->lvEquityList->findItems(_kmmID, Qt::MatchExactly, KMMID_COL); QTreeWidgetItem* item = nullptr; if (! foundItems.empty()) item = foundItems.at(0); // Give the user some options int result; if (_kmmID.contains(" ")) { result = KMessageBox::warningContinueCancel(this, i18n("Failed to retrieve an exchange rate for %1 from %2. It will be skipped this time.", _webID, item->text(SOURCE_COL)), i18n("Price Update Failed")); } else { result = KMessageBox::questionYesNoCancel(this, QString("%1").arg(i18n("Failed to retrieve a quote for %1 from %2. Press No to remove the online price source from this security permanently, Yes to continue updating this security during future price updates or Cancel to stop the current update operation.", _webID, item->text(SOURCE_COL))), i18n("Price Update Failed"), KStandardGuiItem::yes(), KStandardGuiItem::no()); } if (result == KMessageBox::No) { // Disable price updates for this security MyMoneyFileTransaction ft; try { // Get this security (by ID) MyMoneySecurity security = MyMoneyFile::instance()->security(_kmmID.toUtf8()); // Set the quote source to blank security.setValue("kmm-online-source", QString()); security.setValue("kmm-online-quote-system", QString()); // Re-commit the security MyMoneyFile::instance()->modifySecurity(security); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::error(this, QString("") + i18n("Cannot update security %1: %2", _webID, e.what()) + QString(""), i18n("Price Update Failed")); } } // As long as the user doesn't want to cancel, move on! if (result != KMessageBox::Cancel) { QTreeWidgetItem* next = nullptr; d->ui->prgOnlineProgress->setValue(d->ui->prgOnlineProgress->value() + 1); item->setSelected(false); // launch the NEXT one ... in case of m_fUpdateAll == false, we // need to parse the list to find the next selected one next = d->ui->lvEquityList->invisibleRootItem()->child(d->ui->lvEquityList->invisibleRootItem()->indexOfChild(item) + 1); if (!d->m_fUpdateAll) { while (next && !next->isSelected()) { d->ui->prgOnlineProgress->setValue(d->ui->prgOnlineProgress->value() + 1); next = d->ui->lvEquityList->invisibleRootItem()->child(d->ui->lvEquityList->invisibleRootItem()->indexOfChild(next) + 1); } } if (next) { d->m_webQuote.launch(next->text(WEBID_COL), next->text(KMMID_COL), next->text(SOURCE_COL)); } else { finishUpdate(); } } else { finishUpdate(); } } void KEquityPriceUpdateDlg::slotReceivedCSVQuote(const QString& _kmmID, const QString& _webID, MyMoneyStatement& st) { Q_D(KEquityPriceUpdateDlg); auto foundItems = d->ui->lvEquityList->findItems(_kmmID, Qt::MatchExactly, KMMID_COL); QTreeWidgetItem* item = nullptr; if (! foundItems.empty()) item = foundItems.at(0); QTreeWidgetItem* next = nullptr; if (item) { auto file = MyMoneyFile::instance(); MyMoneySecurity fromCurrency, toCurrency; if (!_kmmID.contains(QLatin1Char(' '))) { try { toCurrency = MyMoneyFile::instance()->security(_kmmID); fromCurrency = MyMoneyFile::instance()->security(toCurrency.tradingCurrency()); } catch (const MyMoneyException &) { fromCurrency = toCurrency = MyMoneySecurity(); } } else { QRegExp splitrx("([0-9a-z\\.]+)[^a-z0-9]+([0-9a-z\\.]+)", Qt::CaseInsensitive); if (splitrx.indexIn(_kmmID) != -1) { try { fromCurrency = MyMoneyFile::instance()->security(splitrx.cap(2).toUtf8()); toCurrency = MyMoneyFile::instance()->security(splitrx.cap(1).toUtf8()); } catch (const MyMoneyException &) { fromCurrency = toCurrency = MyMoneySecurity(); } } } if (d->m_updatingPricePolicy != eDialogs::UpdatePrice::All) { QStringList qSources = WebPriceQuote::quoteSources(); for (auto it = st.m_listPrices.begin(); it != st.m_listPrices.end();) { MyMoneyPrice storedPrice = file->price(toCurrency.id(), fromCurrency.id(), (*it).m_date, true); bool priceValid = storedPrice.isValid(); if (!priceValid) ++it; else { switch(d->m_updatingPricePolicy) { case eDialogs::UpdatePrice::Missing: it = st.m_listPrices.erase(it); break; case eDialogs::UpdatePrice::Downloaded: if (!qSources.contains(storedPrice.source())) it = st.m_listPrices.erase(it); else ++it; break; case eDialogs::UpdatePrice::SameSource: if (storedPrice.source().compare((*it).m_sourceName) != 0) it = st.m_listPrices.erase(it); else ++it; break; case eDialogs::UpdatePrice::Ask: { int result = KMessageBox::questionYesNoCancel(this, i18n("For %1 on %2 price %3 already exists.
" "Do you want to replace it with %4?", storedPrice.from(), storedPrice.date().toString(Qt::ISODate), QString().setNum(storedPrice.rate(storedPrice.to()).toDouble(), 'g', 10), QString().setNum((*it).m_amount.toDouble(), 'g', 10)), i18n("Price Already Exists")); switch(result) { case KStandardGuiItem::Yes: ++it; break; case KStandardGuiItem::No: it = st.m_listPrices.erase(it); break; default: case KStandardGuiItem::Cancel: finishUpdate(); return; break; } break; } default: ++it; break; } } } } if (!st.m_listPrices.isEmpty()) { kmymoney->slotStatementImport(st, true); MyMoneyStatement::Price priceClass; if (st.m_listPrices.first().m_date > st.m_listPrices.last().m_date) priceClass = st.m_listPrices.first(); else priceClass = st.m_listPrices.last(); QDate latestDate = QDate::fromString(item->text(DATE_COL),Qt::ISODate); if (latestDate <= priceClass.m_date && priceClass.m_amount.isPositive()) { item->setText(PRICE_COL, priceClass.m_amount.formatMoney(fromCurrency.tradingSymbol(), toCurrency.pricePrecision())); item->setText(DATE_COL, priceClass.m_date.toString(Qt::ISODate)); item->setText(SOURCE_COL, priceClass.m_sourceName); } logStatusMessage(i18n("Price for %1 updated (id %2)", _webID, _kmmID)); // make sure to make OK button available } d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); d->ui->prgOnlineProgress->setValue(d->ui->prgOnlineProgress->value() + 1); item->setSelected(false); // launch the NEXT one ... in case of m_fUpdateAll == false, we // need to parse the list to find the next selected one next = d->ui->lvEquityList->invisibleRootItem()->child(d->ui->lvEquityList->invisibleRootItem()->indexOfChild(item) + 1); if (!d->m_fUpdateAll) { while (next && !next->isSelected()) { d->ui->prgOnlineProgress->setValue(d->ui->prgOnlineProgress->value() + 1); next = d->ui->lvEquityList->invisibleRootItem()->child(d->ui->lvEquityList->invisibleRootItem()->indexOfChild(next) + 1); } } } else { logErrorMessage(i18n("Received a price for %1 (id %2), but this symbol is not on the list. Aborting entire update.", _webID, _kmmID)); } if (next) { d->m_webQuote.launch(next->text(WEBID_COL), next->text(KMMID_COL), next->text(SOURCE_COL)); } else { finishUpdate(); } } void KEquityPriceUpdateDlg::slotReceivedQuote(const QString& _kmmID, const QString& _webID, const QDate& _date, const double& _price) { Q_D(KEquityPriceUpdateDlg); auto foundItems = d->ui->lvEquityList->findItems(_kmmID, Qt::MatchExactly, KMMID_COL); QTreeWidgetItem* item = nullptr; if (! foundItems.empty()) item = foundItems.at(0); QTreeWidgetItem* next = 0; if (item) { if (_price > 0.0f && _date.isValid()) { QDate date = _date; if (date > QDate::currentDate()) date = QDate::currentDate(); MyMoneyMoney price = MyMoneyMoney::ONE; QString id = _kmmID.toUtf8(); MyMoneySecurity fromCurrency, toCurrency; if (_kmmID.contains(" ") == 0) { MyMoneySecurity security = MyMoneyFile::instance()->security(id); QString factor = security.value("kmm-online-factor"); if (!factor.isEmpty()) { price = price * MyMoneyMoney(factor); } try { toCurrency = MyMoneyFile::instance()->security(id); fromCurrency = MyMoneyFile::instance()->security(toCurrency.tradingCurrency()); } catch (const MyMoneyException &) { fromCurrency = toCurrency = MyMoneySecurity(); } } else { QRegExp splitrx("([0-9a-z\\.]+)[^a-z0-9]+([0-9a-z\\.]+)", Qt::CaseInsensitive); if (splitrx.indexIn(_kmmID) != -1) { try { fromCurrency = MyMoneyFile::instance()->security(splitrx.cap(2).toUtf8()); toCurrency = MyMoneyFile::instance()->security(splitrx.cap(1).toUtf8()); } catch (const MyMoneyException &) { fromCurrency = toCurrency = MyMoneySecurity(); } } } price *= MyMoneyMoney(_price, MyMoneyMoney::precToDenom(toCurrency.pricePrecision())); item->setText(PRICE_COL, price.formatMoney(fromCurrency.tradingSymbol(), toCurrency.pricePrecision())); item->setText(DATE_COL, date.toString(Qt::ISODate)); logStatusMessage(i18n("Price for %1 updated (id %2)", _webID, _kmmID)); // make sure to make OK button available d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } else { logErrorMessage(i18n("Received an invalid price for %1, unable to update.", _webID)); } d->ui->prgOnlineProgress->setValue(d->ui->prgOnlineProgress->value() + 1); item->setSelected(false); // launch the NEXT one ... in case of m_fUpdateAll == false, we // need to parse the list to find the next selected one next = d->ui->lvEquityList->invisibleRootItem()->child(d->ui->lvEquityList->invisibleRootItem()->indexOfChild(item) + 1); if (!d->m_fUpdateAll) { while (next && !next->isSelected()) { d->ui->prgOnlineProgress->setValue(d->ui->prgOnlineProgress->value() + 1); next = d->ui->lvEquityList->invisibleRootItem()->child(d->ui->lvEquityList->invisibleRootItem()->indexOfChild(next) + 1); } } } else { logErrorMessage(i18n("Received a price for %1 (id %2), but this symbol is not on the list. Aborting entire update.", _webID, _kmmID)); } if (next) { d->m_webQuote.launch(next->text(WEBID_COL), next->text(KMMID_COL), next->text(SOURCE_COL)); } else { finishUpdate(); } } void KEquityPriceUpdateDlg::finishUpdate() { Q_D(KEquityPriceUpdateDlg); // we've run past the end, reset to the default value. d->m_fUpdateAll = false; // force progress bar to show 100% d->ui->prgOnlineProgress->setValue(d->ui->prgOnlineProgress->maximum()); // re-enable the sorting that was disabled during the update process d->ui->lvEquityList->setSortingEnabled(true); } // 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 WEBID_COL #undef NAME_COL #undef PRICE_COL #undef DATE_COL #undef KMMID_COL #undef SOURCE_COL diff --git a/kmymoney/dialogs/kequitypriceupdatedlg.ui b/kmymoney/dialogs/kequitypriceupdatedlg.ui index ea17a2d6e..c07429a2f 100644 --- a/kmymoney/dialogs/kequitypriceupdatedlg.ui +++ b/kmymoney/dialogs/kequitypriceupdatedlg.ui @@ -1,197 +1,197 @@ Kevin Tambascio <ktambascio@users.sourceforge.net> KEquityPriceUpdateDlg 0 0 537 482 Update Stock and Currency Prices 350 0 List of known Equities, and the date they were last updated on. QFrame::StyledPanel QFrame::Plain true false true true 1 From - + To - + Configure Qt::Horizontal QSizePolicy::Expanding 21 20 Update All Update Selected Status: false true QDialogButtonBox::Cancel|QDialogButtonBox::Ok KTextEdit QTextEdit
ktextedit.h
- kMyMoneyDateInput + KMyMoneyDateInput QWidget
kmymoneydateinput.h
1
buttonBox accepted() KEquityPriceUpdateDlg accept() 268 458 268 240 buttonBox rejected() KEquityPriceUpdateDlg reject() 268 458 268 240
diff --git a/kmymoney/dialogs/kfindtransactiondlg.ui b/kmymoney/dialogs/kfindtransactiondlg.ui index a1176f5e0..c2b539018 100644 --- a/kmymoney/dialogs/kfindtransactiondlg.ui +++ b/kmymoney/dialogs/kfindtransactiondlg.ui @@ -1,960 +1,960 @@ KFindTransactionDlg 0 0 771 481 Search transactions true Criteria Define your search criteria false 0 3 0 Text Qt::Vertical QSizePolicy::Expanding 16 83 1 0 300 0 true Contains Does not contain Treat text as regular expression Case sensitive Account 6 11 11 11 11 - + Date Qt::Horizontal QSizePolicy::Expanding 40 20 Qt::Vertical QSizePolicy::Expanding 20 30 Amount QFrame::NoFrame QFrame::Sunken 0 0 0 0 Search this amount ButtonGroup1 Search amount in the range ButtonGroup1 - + 100 0 false to false - + 100 0 from false - + 100 0 Qt::Horizontal QSizePolicy::Expanding 16 16 Qt::Vertical QSizePolicy::Expanding 16 75 Category - + Tag 6 11 11 11 11 6 0 0 0 0 false Tag Select transactions without tags 6 0 0 0 0 Select all Select none Qt::Vertical QSizePolicy::Expanding 0 42 Payee 6 11 11 11 11 6 0 0 0 0 false Payee Select transactions without payees 6 0 0 0 0 Select all Select none Qt::Vertical QSizePolicy::Expanding 0 42 Details Validity false Type false All states Not reconciled Cleared Reconciled All types Payments Deposits Transfers Any transaction Valid transaction Invalid transaction State false Qt::Horizontal QSizePolicy::Expanding 130 20 QFrame::NoFrame Search this number ButtonGroup2 0 0 Search number in range ButtonGroup2 false to false - + - + from false - + Qt::Horizontal QSizePolicy::Expanding 20 20 Qt::Vertical QSizePolicy::Expanding 16 16 text Qt::AlignTop false 75 true false Qt::Vertical QSizePolicy::Expanding 20 24 Result 0 12 F Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Reset KLineEdit QLineEdit
klineedit.h
KComboBox QComboBox
kcombobox.h
- kMyMoneyLineEdit + KMyMoneyLineEdit QWidget
../widgets/kmymoneylineedit.h
- kMyMoneyAccountSelector + KMyMoneyAccountSelector QWidget
../widgets/kmymoneyaccountselector.h
- kMyMoneyEdit + KMyMoneyEdit QWidget
kmymoneyedit.h
1
KMyMoneyRegister::Register QWidget
register.h
m_tabWidget m_criteriaTab m_textEdit m_textNegate m_caseSensitive m_regExp m_amountButton m_amountRangeButton m_tagsView m_emptyTagsButton m_allTagsButton m_clearTagsButton m_payeesView m_emptyPayeesButton m_allPayeesButton m_clearPayeesButton m_typeBox m_stateBox m_validityBox m_nrButton m_nrRangeButton
diff --git a/kmymoney/dialogs/kfindtransactiondlg_p.h b/kmymoney/dialogs/kfindtransactiondlg_p.h index 30e9ef7a8..46ba60320 100644 --- a/kmymoney/dialogs/kfindtransactiondlg_p.h +++ b/kmymoney/dialogs/kfindtransactiondlg_p.h @@ -1,627 +1,633 @@ /*************************************************************************** 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" +#include "widgetenums.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}; + QList cols { + eWidgets::eTransaction::Column::Date, + eWidgets::eTransaction::Column::Account, + eWidgets::eTransaction::Column::Detail, + eWidgets::eTransaction::Column::ReconcileFlag, + eWidgets::eTransaction::Column::Payment, + eWidgets::eTransaction::Column::Deposit}; 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::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); + 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); + 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); + 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/kgeneratesqldlg.cpp b/kmymoney/dialogs/kgeneratesqldlg.cpp index 8e3a20e06..2e2516535 100644 --- a/kmymoney/dialogs/kgeneratesqldlg.cpp +++ b/kmymoney/dialogs/kgeneratesqldlg.cpp @@ -1,340 +1,340 @@ /*************************************************************************** kgeneratesqldlg.cpp ------------------- copyright : (C) 2009 by Tony Bloomfield (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 "kgeneratesqldlg.h" // ---------------------------------------------------------------------------- // Std Includes #include // ---------------------------------------------------------------------------- // System includes // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kgeneratesqldlg.h" #include "mymoneyfile.h" #include "storage/mymoneyseqaccessmgr.h" #include "kguiutils.h" #include "misc/platformtools.h" #include "mymoneydbdriver.h" #include "mymoneydbdef.h" class KGenerateSqlDlgPrivate { Q_DISABLE_COPY(KGenerateSqlDlgPrivate) Q_DECLARE_PUBLIC(KGenerateSqlDlg) public: KGenerateSqlDlgPrivate(KGenerateSqlDlg *qq) : q_ptr(qq), ui(new Ui::KGenerateSqlDlg) { } ~KGenerateSqlDlgPrivate() { delete ui; } void init() { Q_Q(KGenerateSqlDlg); ui->setupUi(q); m_createTablesButton = ui->buttonBox->addButton(i18n("Create Tables"), QDialogButtonBox::ButtonRole::AcceptRole); m_saveSqlButton = ui->buttonBox->addButton(i18n("Save SQL"), QDialogButtonBox::ButtonRole::ActionRole); Q_ASSERT(m_createTablesButton); Q_ASSERT(m_saveSqlButton); q->connect(ui->buttonBox, &QDialogButtonBox::accepted, q, &QDialog::accept); q->connect(ui->buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); initializeForm(); } void initializeForm() { Q_Q(KGenerateSqlDlg); m_requiredFields = nullptr; // at this point, we don't know which fields are required, so disable everything but the list m_saveSqlButton->setEnabled(false); m_createTablesButton->setEnabled(false); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui->urlSqlite->clear(); ui->textDbName->clear(); ui->textHostName->clear(); ui->textPassword->clear(); ui->textUserName->clear(); ui->textSQL->clear(); ui->urlSqlite->setEnabled(false); ui->textDbName->setEnabled(false); ui->textHostName->setEnabled(false); ui->textPassword->setEnabled(false); ui->textUserName->setEnabled(false); ui->textSQL->setEnabled(false); q->connect(ui->buttonBox->button(QDialogButtonBox::Help), &QPushButton::clicked, q, &KGenerateSqlDlg::slotHelp); } QString selectedDriver() { auto drivers = ui->listDrivers->selectedItems(); if (drivers.count() != 1) { return QString(); } return drivers[0]->text().section(' ', 0, 0); } KGenerateSqlDlg *q_ptr; Ui::KGenerateSqlDlg *ui; QPushButton *m_createTablesButton; QPushButton *m_saveSqlButton; QList m_supportedDrivers; //MyMoneyDbDrivers m_map; - std::unique_ptr m_requiredFields; + std::unique_ptr m_requiredFields; bool m_sqliteSelected; QExplicitlySharedDataPointer m_dbDriver; QString m_dbName; MyMoneySeqAccessMgr* m_storage; bool m_mustDetachStorage; }; KGenerateSqlDlg::KGenerateSqlDlg(QWidget *parent) : QDialog(parent), d_ptr(new KGenerateSqlDlgPrivate(this)) { Q_D(KGenerateSqlDlg); d->init(); } KGenerateSqlDlg::~KGenerateSqlDlg() { Q_D(KGenerateSqlDlg); delete d; } int KGenerateSqlDlg::exec() { Q_D(KGenerateSqlDlg); // list drivers supported by KMM auto map = MyMoneyDbDriver::driverMap(); // list drivers installed on system auto list = QSqlDatabase::drivers(); // join the two QStringList::Iterator it = list.begin(); while (it != list.end()) { QString dname = *it; if (map.keys().contains(dname)) { // only keep if driver is supported dname = dname + " - " + map[dname]; d->m_supportedDrivers.append(dname); } ++it; } if (d->m_supportedDrivers.count() == 0) { // why does KMessageBox not have a standard dialog with Help button? if ((KMessageBox::questionYesNo(this, i18n("In order to use a database, you need to install some additional software. Click Help for more information"), i18n("No Qt SQL Drivers"), KStandardGuiItem::help(), KStandardGuiItem::cancel())) == KMessageBox::Yes) { // Yes stands in for help here KHelpClient::invokeHelp("details.database.usage"); } return (1); } d->ui->listDrivers->clear(); d->ui->listDrivers->addItems(d->m_supportedDrivers); connect(d->ui->listDrivers, &QListWidget::itemSelectionChanged, this, &KGenerateSqlDlg::slotdriverSelected); return (QDialog::exec()); } void KGenerateSqlDlg::slotcreateTables() { Q_D(KGenerateSqlDlg); if (d->m_sqliteSelected) { d->m_dbName = d->ui->urlSqlite->text(); } else { d->m_dbName = d->ui->textDbName->text(); } // check that the database has been pre-created { // all queries etc. must be in a block - see 'remove database' API doc Q_ASSERT(!d->selectedDriver().isEmpty()); QSqlDatabase dbase = QSqlDatabase::addDatabase(d->selectedDriver(), "creation"); dbase.setHostName(d->ui->textHostName->text()); dbase.setDatabaseName(d->m_dbName); dbase.setUserName(d->ui->textUserName->text()); dbase.setPassword(d->ui->textPassword->text()); if (!dbase.open()) { KMessageBox::error(this, i18n("Unable to open database.\n" "You must use an SQL CREATE DATABASE statement before creating the tables.\n") ); return; } QSqlQuery q(dbase); QString message(i18n("Tables successfully created")); QStringList commands = d->ui->textSQL->toPlainText().split('\n'); QStringList::ConstIterator cit; for (cit = commands.constBegin(); cit != commands.constEnd(); ++cit) { if (!(*cit).isEmpty()) { //qDebug() << "exec" << *cit; q.prepare(*cit); if (!q.exec()) { QSqlError e = q.lastError(); message = i18n("Creation failed executing statement" "\nExecuted: %1" "\nError No %2: %3", q.executedQuery(), e.number(), e.text()); break; } } } KMessageBox::information(this, message); } QSqlDatabase::removeDatabase("creation"); auto okButton = d->ui->buttonBox->button(QDialogButtonBox::Ok); Q_ASSERT(okButton); okButton->setEnabled(true); } void KGenerateSqlDlg::slotsaveSQL() { Q_D(KGenerateSqlDlg); auto fileName = QFileDialog::getSaveFileName( this, i18n("Select output file"), QString(), QString()); if (fileName.isEmpty()) return; QFile out(fileName); if (!out.open(QIODevice::WriteOnly)) return; QTextStream s(&out); MyMoneyDbDef db; s << d->ui->textSQL->toPlainText(); out.close(); auto okButton = d->ui->buttonBox->button(QDialogButtonBox::Ok); Q_ASSERT(okButton); okButton->setEnabled(true); } void KGenerateSqlDlg::slotdriverSelected() { Q_D(KGenerateSqlDlg); const auto driverName = d->selectedDriver(); if (driverName.isEmpty()) { d->initializeForm(); return; } d->m_dbDriver = MyMoneyDbDriver::create(driverName); if (!d->m_dbDriver->isTested()) { int rc = KMessageBox::warningContinueCancel(0, i18n("Database type %1 has not been fully tested in a KMyMoney environment.\n" "Please make sure you have adequate backups of your data.\n" "Please report any problems to the developer mailing list at " "kmymoney-devel@kde.org", driverName), ""); if (rc == KMessageBox::Cancel) { d->ui->listDrivers->clearSelection(); d->initializeForm(); return; } } - d->m_requiredFields.reset(new kMandatoryFieldGroup(this)); + d->m_requiredFields.reset(new KMandatoryFieldGroup(this)); // currently, only sqlite need an external file if (d->m_dbDriver->requiresExternalFile()) { d->m_sqliteSelected = true; d->ui->urlSqlite->setMode(KFile::Mode::File); d->ui->urlSqlite->setEnabled(true); d->m_requiredFields->add(d->ui->urlSqlite); d->ui->textDbName->setEnabled(false); d->ui->textHostName->setEnabled(false); d->ui->textUserName->setEnabled(false); } else { // not sqlite3 d->m_sqliteSelected = false; d->ui->urlSqlite->setEnabled(false); d->ui->textDbName->setEnabled(true); d->ui->textHostName->setEnabled(true); d->ui->textUserName->setEnabled(true); d->m_requiredFields->add(d->ui->textDbName); d->m_requiredFields->add(d->ui->textHostName); d->m_requiredFields->add(d->ui->textUserName); d->ui->textDbName->setText("KMyMoney"); d->ui->textHostName->setText("localhost"); d->ui->textUserName->setText(""); d->ui->textUserName->setText(platformTools::osUsername()); d->ui->textPassword->setText(""); } d->ui->textPassword->setEnabled(d->m_dbDriver->isPasswordSupported()); d->m_requiredFields->setOkButton(d->m_createTablesButton); d->ui->textSQL->setEnabled(true); // check if we have a storage; if not, create a skeleton one // we need a storage for MyMoneyDbDef to generate standard accounts d->m_storage = new MyMoneySeqAccessMgr; d->m_mustDetachStorage = true; try { MyMoneyFile::instance()->attachStorage(d->m_storage); } catch (const MyMoneyException &) { d->m_mustDetachStorage = false; // there is already a storage attached } MyMoneyDbDef db; d->ui->textSQL->setText (db.generateSQL(d->m_dbDriver)); if (d->m_mustDetachStorage) { MyMoneyFile::instance()->detachStorage(); } delete d->m_storage; d->m_saveSqlButton->setEnabled(true); connect(d->m_saveSqlButton, &QPushButton::clicked, this, &KGenerateSqlDlg::slotsaveSQL); connect(d->m_createTablesButton, &QPushButton::clicked, this, &KGenerateSqlDlg::slotcreateTables); } void KGenerateSqlDlg::slotHelp() { KHelpClient::invokeHelp("details.database.generatesql"); } diff --git a/kmymoney/dialogs/kmymoneysplittable.cpp b/kmymoney/dialogs/kmymoneysplittable.cpp index 1bd2a04e6..a170fb26a 100644 --- a/kmymoney/dialogs/kmymoneysplittable.cpp +++ b/kmymoney/dialogs/kmymoneysplittable.cpp @@ -1,1085 +1,1085 @@ /*************************************************************************** kmymoneysplittable.cpp - description ------------------- begin : Thu Jan 10 2002 copyright : (C) 2000-2002 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 "kmymoneysplittable.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "mymoneyaccount.h" #include "mymoneyfile.h" #include "mymoneyprice.h" #include "kmymoneyedit.h" #include "kmymoneycategory.h" #include "kmymoneyaccountselector.h" #include "kmymoneylineedit.h" #include "mymoneysecurity.h" #include "kmymoneyglobalsettings.h" #include "kcurrencycalculator.h" #include "mymoneyutils.h" #include "icons.h" using namespace Icons; class KMyMoneySplitTablePrivate { Q_DISABLE_COPY(KMyMoneySplitTablePrivate) public: KMyMoneySplitTablePrivate() : m_currentRow(0), m_maxRows(0), m_precision(2), m_editCategory(0), m_editMemo(0), m_editAmount(0) { } ~KMyMoneySplitTablePrivate() { } /// the currently selected row (will be printed as selected) int m_currentRow; /// the number of rows filled with data int m_maxRows; MyMoneyTransaction m_transaction; MyMoneyAccount m_account; MyMoneySplit m_split; MyMoneySplit m_hiddenSplit; /** * This member keeps the precision for the values */ int m_precision; /** * This member keeps a pointer to the context menu */ QMenu* m_contextMenu; /// keeps the QAction of the delete entry in the context menu QAction* m_contextMenuDelete; /// keeps the QAction of the duplicate entry in the context menu QAction* m_contextMenuDuplicate; /** * This member contains a pointer to the input widget for the category. * The widget will be created and destroyed dynamically in createInputWidgets() * and destroyInputWidgets(). */ QPointer m_editCategory; /** * This member contains a pointer to the input widget for the memo. * The widget will be created and destroyed dynamically in createInputWidgets() * and destroyInputWidgets(). */ - QPointer m_editMemo; + QPointer m_editMemo; /** * This member contains a pointer to the input widget for the amount. * The widget will be created and destroyed dynamically in createInputWidgets() * and destroyInputWidgets(). */ - QPointer m_editAmount; + QPointer m_editAmount; /** * This member keeps the tab order for the above widgets */ QWidgetList m_tabOrderWidgets; QPointer m_registerButtonFrame; QPointer m_registerEnterButton; QPointer m_registerCancelButton; QMap m_priceInfo; }; KMyMoneySplitTable::KMyMoneySplitTable(QWidget *parent) : QTableWidget(parent), d_ptr(new KMyMoneySplitTablePrivate) { Q_D(KMyMoneySplitTable); // used for custom coloring with the help of the application's stylesheet setObjectName(QLatin1String("splittable")); // setup the transactions table setRowCount(1); setColumnCount(3); QStringList labels; labels << i18n("Category") << i18n("Memo") << i18n("Amount"); setHorizontalHeaderLabels(labels); setSelectionMode(QAbstractItemView::SingleSelection); setSelectionBehavior(QAbstractItemView::SelectRows); int left, top, right, bottom; getContentsMargins(&left, &top, &right, &bottom); setContentsMargins(0, top, right, bottom); setFont(KMyMoneyGlobalSettings::listCellFont()); setAlternatingRowColors(true); verticalHeader()->hide(); horizontalHeader()->setSectionsMovable(false); horizontalHeader()->setFont(KMyMoneyGlobalSettings::listHeaderFont()); KConfigGroup grp = KSharedConfig::openConfig()->group("SplitTable"); QByteArray columns; columns = grp.readEntry("HeaderState", columns); horizontalHeader()->restoreState(columns); horizontalHeader()->setStretchLastSection(true); setShowGrid(KMyMoneyGlobalSettings::showGrid()); setEditTriggers(QAbstractItemView::NoEditTriggers); // setup the context menu d->m_contextMenu = new QMenu(this); d->m_contextMenu->setTitle(i18n("Split Options")); d->m_contextMenu->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewFinancialTransfer])); d->m_contextMenu->addAction(QIcon::fromTheme(g_Icons[Icon::DocumentEdit]), i18n("Edit..."), this, SLOT(slotStartEdit())); d->m_contextMenuDuplicate = d->m_contextMenu->addAction(QIcon::fromTheme(g_Icons[Icon::EditCopy]), i18nc("To duplicate a split", "Duplicate"), this, SLOT(slotDuplicateSplit())); d->m_contextMenuDelete = d->m_contextMenu->addAction(QIcon::fromTheme(g_Icons[Icon::EditDelete]), i18n("Delete..."), this, SLOT(slotDeleteSplit())); connect(this, &QAbstractItemView::clicked, this, static_cast(&KMyMoneySplitTable::slotSetFocus)); connect(this, &KMyMoneySplitTable::transactionChanged, this, &KMyMoneySplitTable::slotUpdateData); installEventFilter(this); } KMyMoneySplitTable::~KMyMoneySplitTable() { Q_D(KMyMoneySplitTable); auto grp = KSharedConfig::openConfig()->group("SplitTable"); QByteArray columns = horizontalHeader()->saveState(); grp.writeEntry("HeaderState", columns); grp.sync(); delete d; } int KMyMoneySplitTable::currentRow() const { Q_D(const KMyMoneySplitTable); return d->m_currentRow; } void KMyMoneySplitTable::setup(const QMap& priceInfo, int precision) { Q_D(KMyMoneySplitTable); d->m_priceInfo = priceInfo; d->m_precision = precision; } bool KMyMoneySplitTable::eventFilter(QObject *o, QEvent *e) { Q_D(KMyMoneySplitTable); // MYMONEYTRACER(tracer); QKeyEvent *k = static_cast(e); bool rc = false; int row = currentRow(); int lines = viewport()->height() / rowHeight(0); if (e->type() == QEvent::KeyPress && !isEditMode()) { rc = true; switch (k->key()) { case Qt::Key_Up: if (row) slotSetFocus(model()->index(row - 1, 0)); break; case Qt::Key_Down: if (row < d->m_transaction.splits().count() - 1) slotSetFocus(model()->index(row + 1, 0)); break; case Qt::Key_Home: slotSetFocus(model()->index(0, 0)); break; case Qt::Key_End: slotSetFocus(model()->index(d->m_transaction.splits().count() - 1, 0)); break; case Qt::Key_PageUp: if (lines) { while (lines-- > 0 && row) --row; slotSetFocus(model()->index(row, 0)); } break; case Qt::Key_PageDown: if (row < d->m_transaction.splits().count() - 1) { while (lines-- > 0 && row < d->m_transaction.splits().count() - 1) ++row; slotSetFocus(model()->index(row, 0)); } break; case Qt::Key_Delete: slotDeleteSplit(); break; case Qt::Key_Return: case Qt::Key_Enter: if (row < d->m_transaction.splits().count() - 1 && KMyMoneyGlobalSettings::enterMovesBetweenFields()) { slotStartEdit(); } else emit returnPressed(); break; case Qt::Key_Escape: emit escapePressed(); break; case Qt::Key_F2: slotStartEdit(); break; default: rc = true; // duplicate split if (Qt::Key_C == k->key() && Qt::ControlModifier == k->modifiers()) { slotDuplicateSplit(); // new split } else if (Qt::Key_Insert == k->key() && Qt::ControlModifier == k->modifiers()) { slotSetFocus(model()->index(d->m_transaction.splits().count() - 1, 0)); slotStartEdit(); } else if (k->text()[ 0 ].isPrint()) { KMyMoneyCategory* cat = createEditWidgets(false); if (cat) { - kMyMoneyLineEdit *le = qobject_cast(cat->lineEdit()); + KMyMoneyLineEdit *le = qobject_cast(cat->lineEdit()); if (le) { // make sure, the widget receives the key again // and does not select the text this time le->setText(k->text()); le->end(false); le->deselect(); le->skipSelectAll(true); le->setFocus(); } } } break; } } else if (e->type() == QEvent::KeyPress && isEditMode()) { bool terminate = true; rc = true; switch (k->key()) { // suppress the F2 functionality to start editing in inline edit mode case Qt::Key_F2: // suppress the cursor movement in inline edit mode case Qt::Key_Up: case Qt::Key_Down: case Qt::Key_PageUp: case Qt::Key_PageDown: break; case Qt::Key_Return: case Qt::Key_Enter: // we cannot call the slot directly, as it destroys the caller of // this method :-( So we let the event handler take care of calling // the respective slot using a timeout. For a KLineEdit derived object // it could be, that at this point the user selected a value from // a completion list. In this case, we close the completion list and // do not end editing of the transaction. if (o->inherits("KLineEdit")) { KLineEdit* le = dynamic_cast(o); KCompletionBox* box = le->completionBox(false); if (box && box->isVisible()) { terminate = false; le->completionBox(false)->hide(); } } // in case we have the 'enter moves focus between fields', we need to simulate // a TAB key when the object 'o' points to the category or memo field. if (KMyMoneyGlobalSettings::enterMovesBetweenFields()) { if (o == d->m_editCategory->lineEdit() || o == d->m_editMemo) { terminate = false; QKeyEvent evt(e->type(), Qt::Key_Tab, k->modifiers(), QString(), k->isAutoRepeat(), k->count()); QApplication::sendEvent(o, &evt); } } if (terminate) { QTimer::singleShot(0, this, SLOT(slotEndEditKeyboard())); } break; case Qt::Key_Escape: // we cannot call the slot directly, as it destroys the caller of // this method :-( So we let the event handler take care of calling // the respective slot using a timeout. QTimer::singleShot(0, this, SLOT(slotCancelEdit())); break; default: rc = false; break; } } else if (e->type() == QEvent::KeyRelease && !isEditMode()) { // for some reason, we only see a KeyRelease event of the Menu key // here. In other locations (e.g. Register::eventFilter()) we see // a KeyPress event. Strange. (ipwizard - 2008-05-10) switch (k->key()) { case Qt::Key_Menu: // if the very last entry is selected, the delete // operation is not available otherwise it is d->m_contextMenuDelete->setEnabled( row < d->m_transaction.splits().count() - 1); d->m_contextMenuDuplicate->setEnabled( row < d->m_transaction.splits().count() - 1); d->m_contextMenu->exec(QCursor::pos()); rc = true; break; default: break; } } // if the event has not been processed here, forward it to // the base class implementation if it's not a key event if (rc == false) { if (e->type() != QEvent::KeyPress && e->type() != QEvent::KeyRelease) { rc = QTableWidget::eventFilter(o, e); } } return rc; } void KMyMoneySplitTable::slotSetFocus(const QModelIndex& index) { slotSetFocus(index, Qt::LeftButton); } void KMyMoneySplitTable::slotSetFocus(const QModelIndex& index, int button) { Q_D(KMyMoneySplitTable); MYMONEYTRACER(tracer); auto row = index.row(); // adjust row to used area if (row > d->m_transaction.splits().count() - 1) row = d->m_transaction.splits().count() - 1; if (row < 0) row = 0; // make sure the row will be on the screen scrollTo(model()->index(row, 0)); if (isEditMode()) { // in edit mode? if (isEditSplitValid() && KMyMoneyGlobalSettings::focusChangeIsEnter()) endEdit(false/*keyboard driven*/, false/*set focus to next row*/); else slotCancelEdit(); } if (button == Qt::LeftButton) { // left mouse button if (row != currentRow()) { // setup new current row and update visible selection selectRow(row); slotUpdateData(d->m_transaction); } } else if (button == Qt::RightButton) { // context menu is only available when cursor is on // an existing transaction or the first line after this area if (row == index.row()) { // setup new current row and update visible selection selectRow(row); slotUpdateData(d->m_transaction); // if the very last entry is selected, the delete // operation is not available otherwise it is d->m_contextMenuDelete->setEnabled( row < d->m_transaction.splits().count() - 1); d->m_contextMenuDuplicate->setEnabled( row < d->m_transaction.splits().count() - 1); d->m_contextMenu->exec(QCursor::pos()); } } } void KMyMoneySplitTable::mousePressEvent(QMouseEvent* e) { slotSetFocus(indexAt(e->pos()), e->button()); } /* turn off QTable behaviour */ void KMyMoneySplitTable::mouseReleaseEvent(QMouseEvent* /* e */) { } void KMyMoneySplitTable::mouseDoubleClickEvent(QMouseEvent *e) { Q_D(KMyMoneySplitTable); MYMONEYTRACER(tracer); int col = columnAt(e->pos().x()); slotSetFocus(model()->index(rowAt(e->pos().y()), col), e->button()); createEditWidgets(false); QLineEdit* editWidget = 0; //krazy:exclude=qmethods switch (col) { case 0: editWidget = d->m_editCategory->lineEdit(); break; case 1: editWidget = d->m_editMemo; break; case 2: editWidget = d->m_editAmount->lineedit(); break; default: break; } if (editWidget) { editWidget->setFocus(); editWidget->selectAll(); } } void KMyMoneySplitTable::selectRow(int row) { Q_D(KMyMoneySplitTable); MYMONEYTRACER(tracer); if (row > d->m_maxRows) row = d->m_maxRows; d->m_currentRow = row; QTableWidget::selectRow(row); QList list = getSplits(d->m_transaction); if (row < list.count()) d->m_split = list[row]; else d->m_split = MyMoneySplit(); } void KMyMoneySplitTable::setRowCount(int irows) { QTableWidget::setRowCount(irows); // determine row height according to the edit widgets // we use the category widget as the base QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); int height = fm.lineSpacing() + 6; #if 0 // recalculate row height hint KMyMoneyCategory cat; height = qMax(cat.sizeHint().height(), height); #endif verticalHeader()->setUpdatesEnabled(false); for (auto i = 0; i < irows; ++i) verticalHeader()->resizeSection(i, height); verticalHeader()->setUpdatesEnabled(true); } void KMyMoneySplitTable::setTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s, const MyMoneyAccount& acc) { Q_D(KMyMoneySplitTable); MYMONEYTRACER(tracer); d->m_transaction = t; d->m_account = acc; d->m_hiddenSplit = s; selectRow(0); slotUpdateData(d->m_transaction); } MyMoneyTransaction KMyMoneySplitTable::transaction() const { Q_D(const KMyMoneySplitTable); return d->m_transaction; } QList KMyMoneySplitTable::getSplits(const MyMoneyTransaction& t) const { Q_D(const KMyMoneySplitTable); // get list of splits QList list = t.splits(); // and ignore the one that should be hidden QList::Iterator it; for (it = list.begin(); it != list.end(); ++it) { if ((*it).id() == d->m_hiddenSplit.id()) { list.erase(it); break; } } return list; } void KMyMoneySplitTable::slotUpdateData(const MyMoneyTransaction& t) { Q_D(KMyMoneySplitTable); MYMONEYTRACER(tracer); unsigned long numRows = 0; QTableWidgetItem* textItem; QList list = getSplits(t); updateTransactionTableSize(); // fill the part that is used by transactions QList::Iterator it; for (it = list.begin(); it != list.end(); ++it) { QString colText; MyMoneyMoney value = (*it).value(); if (!(*it).accountId().isEmpty()) { try { colText = MyMoneyFile::instance()->accountToCategory((*it).accountId()); } catch (const MyMoneyException &) { qDebug("Unexpected exception in KMyMoneySplitTable::slotUpdateData()"); } } QString amountTxt = value.formatMoney(d->m_account.fraction()); if (value == MyMoneyMoney::autoCalc) { amountTxt = i18n("will be calculated"); } if (colText.isEmpty() && (*it).memo().isEmpty() && value.isZero()) amountTxt.clear(); unsigned width = fontMetrics().width(amountTxt); - kMyMoneyEdit* valfield = new kMyMoneyEdit(); + KMyMoneyEdit* valfield = new KMyMoneyEdit(); valfield->setMinimumWidth(width); width = valfield->minimumSizeHint().width(); delete valfield; textItem = item(numRows, 0); if (textItem) textItem->setText(colText); else setItem(numRows, 0, new QTableWidgetItem(colText)); textItem = item(numRows, 1); if (textItem) textItem->setText((*it).memo()); else setItem(numRows, 1, new QTableWidgetItem((*it).memo())); textItem = item(numRows, 2); if (textItem) textItem->setText(amountTxt); else setItem(numRows, 2, new QTableWidgetItem(amountTxt)); item(numRows, 2)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); ++numRows; } // now clean out the remainder of the table while (numRows < static_cast(rowCount())) { for (auto i = 0 ; i < 3; ++i) { textItem = item(numRows, i); if (textItem) textItem->setText(""); else setItem(numRows, i, new QTableWidgetItem("")); } item(numRows, 2)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); ++numRows; } } void KMyMoneySplitTable::updateTransactionTableSize() { Q_D(KMyMoneySplitTable); // get current size of transactions table int tableHeight = height(); int splitCount = d->m_transaction.splits().count() - 1; if (splitCount < 0) splitCount = 0; // see if we need some extra lines to fill the current size with the grid int numExtraLines = (tableHeight / rowHeight(0)) - splitCount; if (numExtraLines < 2) numExtraLines = 2; setRowCount(splitCount + numExtraLines); d->m_maxRows = splitCount; } void KMyMoneySplitTable::resizeEvent(QResizeEvent* ev) { QTableWidget::resizeEvent(ev); if (!isEditMode()) { // update the size of the transaction table only if a split is not being edited // otherwise the height of the editors would be altered in an undesired way updateTransactionTableSize(); } } void KMyMoneySplitTable::slotDuplicateSplit() { Q_D(KMyMoneySplitTable); MYMONEYTRACER(tracer); QList list = getSplits(d->m_transaction); if (d->m_currentRow < list.count()) { MyMoneySplit split = list[d->m_currentRow]; split.clearId(); try { d->m_transaction.addSplit(split); emit transactionChanged(d->m_transaction); } catch (const MyMoneyException &e) { qDebug("Cannot duplicate split: %s", qPrintable(e.what())); } } } void KMyMoneySplitTable::slotDeleteSplit() { Q_D(KMyMoneySplitTable); MYMONEYTRACER(tracer); QList list = getSplits(d->m_transaction); if (d->m_currentRow < list.count()) { if (KMessageBox::warningContinueCancel(this, i18n("You are about to delete the selected split. " "Do you really want to continue?"), i18n("KMyMoney") ) == KMessageBox::Continue) { try { d->m_transaction.removeSplit(list[d->m_currentRow]); // if we removed the last split, select the previous if (d->m_currentRow && d->m_currentRow == list.count() - 1) selectRow(d->m_currentRow - 1); else selectRow(d->m_currentRow); emit transactionChanged(d->m_transaction); } catch (const MyMoneyException &e) { qDebug("Cannot remove split: %s", qPrintable(e.what())); } } } } KMyMoneyCategory* KMyMoneySplitTable::slotStartEdit() { MYMONEYTRACER(tracer); return createEditWidgets(true); } void KMyMoneySplitTable::slotEndEdit() { endEdit(false); } void KMyMoneySplitTable::slotEndEditKeyboard() { endEdit(true); } void KMyMoneySplitTable::endEdit(bool keyboardDriven, bool setFocusToNextRow) { Q_D(KMyMoneySplitTable); auto file = MyMoneyFile::instance(); MYMONEYTRACER(tracer); MyMoneySplit s1 = d->m_split; if (!isEditSplitValid()) { KMessageBox::information(this, i18n("You need to assign a category to this split before it can be entered."), i18n("Enter split"), "EnterSplitWithEmptyCategory"); d->m_editCategory->setFocus(); return; } bool needUpdate = false; if (d->m_editCategory->selectedItem() != d->m_split.accountId()) { s1.setAccountId(d->m_editCategory->selectedItem()); needUpdate = true; } if (d->m_editMemo->text() != d->m_split.memo()) { s1.setMemo(d->m_editMemo->text()); needUpdate = true; } if (d->m_editAmount->value() != d->m_split.value()) { s1.setValue(d->m_editAmount->value()); needUpdate = true; } if (needUpdate) { if (!s1.value().isZero()) { MyMoneyAccount cat = file->account(s1.accountId()); if (cat.currencyId() != d->m_transaction.commodity()) { MyMoneySecurity fromCurrency, toCurrency; MyMoneyMoney fromValue, toValue; fromCurrency = file->security(d->m_transaction.commodity()); toCurrency = file->security(cat.currencyId()); // determine the fraction required for this category int fract = toCurrency.smallestAccountFraction(); if (cat.accountType() == eMyMoney::Account::Cash) fract = toCurrency.smallestCashFraction(); // display only positive values to the user fromValue = s1.value().abs(); // if we had a price info in the beginning, we use it here if (d->m_priceInfo.find(cat.currencyId()) != d->m_priceInfo.end()) { toValue = (fromValue * d->m_priceInfo[cat.currencyId()]).convert(fract); } // if the shares are still 0, we need to change that if (toValue.isZero()) { const MyMoneyPrice &price = MyMoneyFile::instance()->price(fromCurrency.id(), toCurrency.id()); // 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, d->m_transaction.postDate(), fract, this); if (calc->exec() == QDialog::Rejected) { delete calc; return; } else { s1.setShares((s1.value() * calc->price()).convert(fract)); delete calc; } } else { s1.setShares(s1.value()); } } else s1.setShares(s1.value()); d->m_split = s1; try { if (d->m_split.id().isEmpty()) { d->m_transaction.addSplit(d->m_split); } else { d->m_transaction.modifySplit(d->m_split); } emit transactionChanged(d->m_transaction); } catch (const MyMoneyException &e) { qDebug("Cannot add/modify split: %s", qPrintable(e.what())); } } this->setFocus(); destroyEditWidgets(); if (setFocusToNextRow) { slotSetFocus(model()->index(currentRow() + 1, 0)); } // if we still have more splits, we start editing right away // in case we have selected 'enter moves between fields' if (keyboardDriven && currentRow() < d->m_transaction.splits().count() - 1 && KMyMoneyGlobalSettings::enterMovesBetweenFields()) { slotStartEdit(); } } void KMyMoneySplitTable::slotCancelEdit() { MYMONEYTRACER(tracer); if (isEditMode()) { destroyEditWidgets(); this->setFocus(); } } bool KMyMoneySplitTable::isEditMode() const { Q_D(const KMyMoneySplitTable); // while the edit widgets exist we're in edit mode return d->m_editAmount || d->m_editMemo || d->m_editCategory; } bool KMyMoneySplitTable::isEditSplitValid() const { Q_D(const KMyMoneySplitTable); return isEditMode() && !d->m_editCategory->selectedItem().isEmpty(); } void KMyMoneySplitTable::destroyEditWidgets() { MYMONEYTRACER(tracer); Q_D(KMyMoneySplitTable); emit editFinished(); disconnect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, this, &KMyMoneySplitTable::slotLoadEditWidgets); destroyEditWidget(d->m_currentRow, 0); destroyEditWidget(d->m_currentRow, 1); destroyEditWidget(d->m_currentRow, 2); destroyEditWidget(d->m_currentRow + 1, 0); } void KMyMoneySplitTable::destroyEditWidget(int r, int c) { if (QWidget* cw = cellWidget(r, c)) cw->hide(); removeCellWidget(r, c); } KMyMoneyCategory* KMyMoneySplitTable::createEditWidgets(bool setFocus) { MYMONEYTRACER(tracer); emit editStarted(); Q_D(KMyMoneySplitTable); auto cellFont = KMyMoneyGlobalSettings::listCellFont(); d->m_tabOrderWidgets.clear(); // create the widgets - d->m_editAmount = new kMyMoneyEdit(0); + d->m_editAmount = new KMyMoneyEdit(0); d->m_editAmount->setFont(cellFont); d->m_editAmount->setResetButtonVisible(false); d->m_editAmount->setPrecision(d->m_precision); d->m_editCategory = new KMyMoneyCategory(); d->m_editCategory->setPlaceholderText(i18n("Category")); d->m_editCategory->setFont(cellFont); connect(d->m_editCategory, SIGNAL(createItem(QString,QString&)), this, SIGNAL(createCategory(QString,QString&))); connect(d->m_editCategory, SIGNAL(objectCreation(bool)), this, SIGNAL(objectCreation(bool))); - d->m_editMemo = new kMyMoneyLineEdit(0, false, Qt::AlignLeft | Qt::AlignVCenter); + d->m_editMemo = new KMyMoneyLineEdit(0, false, Qt::AlignLeft | Qt::AlignVCenter); d->m_editMemo->setPlaceholderText(i18n("Memo")); d->m_editMemo->setFont(cellFont); // create buttons for the mouse users d->m_registerButtonFrame = new QFrame(this); d->m_registerButtonFrame->setContentsMargins(0, 0, 0, 0); d->m_registerButtonFrame->setAutoFillBackground(true); QHBoxLayout* l = new QHBoxLayout(d->m_registerButtonFrame); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); d->m_registerEnterButton = new QPushButton(QIcon::fromTheme(g_Icons[Icon::DialogOK]) , QString(), d->m_registerButtonFrame); d->m_registerCancelButton = new QPushButton(QIcon::fromTheme(g_Icons[Icon::DialogCancel]) , QString(), d->m_registerButtonFrame); l->addWidget(d->m_registerEnterButton); l->addWidget(d->m_registerCancelButton); l->addStretch(2); connect(d->m_registerEnterButton.data(), &QAbstractButton::clicked, this, &KMyMoneySplitTable::slotEndEdit); connect(d->m_registerCancelButton.data(), &QAbstractButton::clicked, this, &KMyMoneySplitTable::slotCancelEdit); // setup tab order addToTabOrder(d->m_editCategory); addToTabOrder(d->m_editMemo); addToTabOrder(d->m_editAmount); addToTabOrder(d->m_registerEnterButton); addToTabOrder(d->m_registerCancelButton); if (!d->m_split.accountId().isEmpty()) { d->m_editCategory->setSelectedItem(d->m_split.accountId()); } else { // check if the transaction is balanced or not. If not, // assign the remainder to the amount. MyMoneyMoney diff; QList list = d->m_transaction.splits(); QList::ConstIterator it_s; for (it_s = list.constBegin(); it_s != list.constEnd(); ++it_s) { if (!(*it_s).accountId().isEmpty()) diff += (*it_s).value(); } d->m_split.setValue(-diff); } d->m_editMemo->loadText(d->m_split.memo()); // don't allow automatically calculated values to be modified if (d->m_split.value() == MyMoneyMoney::autoCalc) { d->m_editAmount->setEnabled(false); d->m_editAmount->loadText("will be calculated"); } else d->m_editAmount->setValue(d->m_split.value()); setCellWidget(d->m_currentRow, 0, d->m_editCategory); setCellWidget(d->m_currentRow, 1, d->m_editMemo); setCellWidget(d->m_currentRow, 2, d->m_editAmount); setCellWidget(d->m_currentRow + 1, 0, d->m_registerButtonFrame); // load e.g. the category widget with the account list slotLoadEditWidgets(); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, this, &KMyMoneySplitTable::slotLoadEditWidgets); foreach (QWidget* w, d->m_tabOrderWidgets) { if (w) { w->installEventFilter(this); } } if (setFocus) { d->m_editCategory->lineEdit()->setFocus(); d->m_editCategory->lineEdit()->selectAll(); } // resize the rows so the added edit widgets would fit appropriately resizeRowsToContents(); return d->m_editCategory; } void KMyMoneySplitTable::slotLoadEditWidgets() { Q_D(KMyMoneySplitTable); // reload category widget auto categoryId = d->m_editCategory->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); // remove the accounts with invalid types at this point aSet.removeAccountType(eMyMoney::Account::CertificateDep); aSet.removeAccountType(eMyMoney::Account::Investment); aSet.removeAccountType(eMyMoney::Account::Stock); aSet.removeAccountType(eMyMoney::Account::MoneyMarket); aSet.load(d->m_editCategory->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()) d->m_editCategory->selector()->removeItem(d->m_account.id()); if (!categoryId.isEmpty()) d->m_editCategory->setSelectedItem(categoryId); } void KMyMoneySplitTable::addToTabOrder(QWidget* w) { Q_D(KMyMoneySplitTable); if (w) { while (w->focusProxy()) w = w->focusProxy(); d->m_tabOrderWidgets.append(w); } } bool KMyMoneySplitTable::focusNextPrevChild(bool next) { MYMONEYTRACER(tracer); Q_D(KMyMoneySplitTable); auto rc = false; if (isEditMode()) { QWidget *w = 0; 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]; if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) { // qDebug("Selecting '%s' as focus", w->className()); w->setFocus(); rc = true; } } } else rc = QTableWidget::focusNextPrevChild(next); return rc; } diff --git a/kmymoney/dialogs/knewaccountdlg.cpp b/kmymoney/dialogs/knewaccountdlg.cpp index 84182dbd1..3d2d8eda2 100644 --- a/kmymoney/dialogs/knewaccountdlg.cpp +++ b/kmymoney/dialogs/knewaccountdlg.cpp @@ -1,916 +1,916 @@ /*************************************************************************** knewaccountdlg.cpp ------------------- copyright : (C) 2000 by Michael Edwardes 2004 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 "knewaccountdlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Headers #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_knewaccountdlg.h" #include "kmymoneyedit.h" #include "kmymoneydateinput.h" #include #include "mymoneyfile.h" #include "mymoneyinstitution.h" #include "mymoneyaccount.h" #include "kmymoneyglobalsettings.h" #include "kmymoneycurrencyselector.h" #include "knewbankdlg.h" #include "models.h" #include "accountsmodel.h" #include "hierarchyfilterproxymodel.h" using namespace eMyMoney; class KNewAccountDlgPrivate { Q_DISABLE_COPY(KNewAccountDlgPrivate) Q_DECLARE_PUBLIC(KNewAccountDlg) public: KNewAccountDlgPrivate(KNewAccountDlg *qq) : q_ptr(qq), ui(new Ui::KNewAccountDlg) { } ~KNewAccountDlgPrivate() { delete ui; } void init() { Q_Q(KNewAccountDlg); ui->setupUi(q); auto file = MyMoneyFile::instance(); // initialize the m_parentAccount member QVector filterAccountGroup {m_account.accountGroup()}; switch (m_account.accountGroup()) { case Account::Asset: m_parentAccount = file->asset(); break; case Account::Liability: m_parentAccount = file->liability(); break; case Account::Income: m_parentAccount = file->income(); break; case Account::Expense: m_parentAccount = file->expense(); break; case Account::Equity: m_parentAccount = file->equity(); break; default: qDebug("Seems we have an account that hasn't been mapped to the top five"); if (m_categoryEditor) { m_parentAccount = file->income(); filterAccountGroup[0] = Account::Income; } else { m_parentAccount = file->asset(); filterAccountGroup[0] = Account::Asset; } } ui->m_amountGroup->setId(ui->m_grossAmount, 0); ui->m_amountGroup->setId(ui->m_netAmount, 1); // the proxy filter model m_filterProxyModel = new HierarchyFilterProxyModel(q); m_filterProxyModel->setHideClosedAccounts(true); m_filterProxyModel->setHideEquityAccounts(!KMyMoneyGlobalSettings::expertMode()); m_filterProxyModel->addAccountGroup(filterAccountGroup); m_filterProxyModel->setCurrentAccountId(m_account.id()); auto const model = Models::instance()->accountsModel(); m_filterProxyModel->setSourceModel(model); m_filterProxyModel->setSourceColumns(model->getColumns()); m_filterProxyModel->setDynamicSortFilter(true); ui->m_parentAccounts->setModel(m_filterProxyModel); ui->m_parentAccounts->sortByColumn((int)eAccountsModel::Column::Account, Qt::AscendingOrder); ui->m_subAccountLabel->setText(i18n("Is a sub account")); ui->accountNameEdit->setText(m_account.name()); ui->descriptionEdit->setText(m_account.description()); ui->typeCombo->setEnabled(true); // load the price mode combo ui->m_priceMode->insertItem(i18nc("default price mode", "(default)"), 0); ui->m_priceMode->insertItem(i18n("Price per share"), 1); ui->m_priceMode->insertItem(i18n("Total for all shares"), 2); int priceMode = 0; if (m_account.accountType() == Account::Investment) { ui->m_priceMode->setEnabled(true); if (!m_account.value("priceMode").isEmpty()) priceMode = m_account.value("priceMode").toInt(); } ui->m_priceMode->setCurrentItem(priceMode); bool haveMinBalance = false; bool haveMaxCredit = false; if (!m_account.openingDate().isValid()) { m_account.setOpeningDate(KMyMoneyGlobalSettings::firstFiscalDate()); } ui->m_openingDateEdit->setDate(m_account.openingDate()); handleOpeningBalanceCheckbox(m_account.currencyId()); if (m_categoryEditor) { // get rid of the tabs that are not used for categories int tab = ui->m_tab->indexOf(ui->m_institutionTab); if (tab != -1) ui->m_tab->removeTab(tab); tab = ui->m_tab->indexOf(ui->m_limitsTab); if (tab != -1) ui->m_tab->removeTab(tab); //m_qlistviewParentAccounts->setEnabled(true); ui->accountNoEdit->setEnabled(false); ui->m_institutionBox->hide(); ui->m_qcheckboxNoVat->hide(); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Income), (int)Account::Income); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Expense), (int)Account::Expense); // Hardcoded but acceptable - if above we set the default to income do the same here switch (m_account.accountType()) { case Account::Expense: ui->typeCombo->setCurrentItem(MyMoneyAccount::accountTypeToString(Account::Expense), false); break; case Account::Income: default: ui->typeCombo->setCurrentItem(MyMoneyAccount::accountTypeToString(Account::Income), false); break; } ui->m_currency->setEnabled(true); if (m_isEditing) { ui->typeCombo->setEnabled(false); ui->m_currency->setDisabled(MyMoneyFile::instance()->isReferenced(m_account)); } ui->m_qcheckboxPreferred->hide(); ui->m_qcheckboxTax->setChecked(m_account.value("Tax").toLower() == "yes"); ui->m_costCenterRequiredCheckBox->setChecked(m_account.isCostCenterRequired()); loadVatAccounts(); } else { // get rid of the tabs that are not used for accounts int taxtab = ui->m_tab->indexOf(ui->m_taxTab); if (taxtab != -1) { ui->m_vatCategory->setText(i18n("VAT account")); ui->m_qcheckboxTax->setChecked(m_account.value("Tax") == "Yes"); loadVatAccounts(); } else { ui->m_tab->removeTab(taxtab); } ui->m_costCenterRequiredCheckBox->hide(); switch (m_account.accountType()) { case Account::Savings: case Account::Cash: haveMinBalance = true; break; case Account::Checkings: haveMinBalance = true; haveMaxCredit = true; break; case Account::CreditCard: haveMaxCredit = true; break; default: // no limit available, so we might get rid of the tab int tab = ui->m_tab->indexOf(ui->m_limitsTab); if (tab != -1) ui->m_tab->removeTab(tab); // don't try to hide the widgets we just wiped // in the next step haveMaxCredit = haveMinBalance = true; break; } if (!haveMaxCredit) { ui->m_maxCreditLabel->setEnabled(false); ui->m_maxCreditLabel->hide(); ui->m_maxCreditEarlyEdit->hide(); ui->m_maxCreditAbsoluteEdit->hide(); } if (!haveMinBalance) { ui->m_minBalanceLabel->setEnabled(false); ui->m_minBalanceLabel->hide(); ui->m_minBalanceEarlyEdit->hide(); ui->m_minBalanceAbsoluteEdit->hide(); } QString typeString = MyMoneyAccount::accountTypeToString(m_account.accountType()); if (m_isEditing) { if (m_account.isLiquidAsset()) { ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Checkings), (int)Account::Checkings); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Savings), (int)Account::Savings); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Cash), (int)Account::Cash); } else { ui->typeCombo->addItem(typeString, (int)m_account.accountType()); // Once created, accounts of other account types are not // allowed to be changed. ui->typeCombo->setEnabled(false); } // Once created, a currency cannot be changed if it is referenced. ui->m_currency->setDisabled(MyMoneyFile::instance()->isReferenced(m_account)); } else { ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Checkings), (int)Account::Checkings); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Savings), (int)Account::Savings); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Cash), (int)Account::Cash); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::CreditCard), (int)Account::CreditCard); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Loan), (int)Account::Loan); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Investment), (int)Account::Investment); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Asset), (int)Account::Asset); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Liability), (int)Account::Liability); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Stock), (int)Account::Stock); /* ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::CertificateDep), (int)Account::CertificateDep); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::MoneyMarket), (int)Account::MoneyMarket); ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Currency), (int)Account::Currency); */ // Do not create account types that are not supported // by the current engine. if (m_account.accountType() == Account::Unknown || m_account.accountType() == Account::CertificateDep || m_account.accountType() == Account::MoneyMarket || m_account.accountType() == Account::Currency) typeString = MyMoneyAccount::accountTypeToString(Account::Checkings); } ui->typeCombo->setCurrentItem(typeString, false); if (m_account.isInvest()) ui->m_institutionBox->hide(); ui->accountNoEdit->setText(m_account.number()); ui->m_qcheckboxPreferred->setChecked(m_account.value("PreferredAccount") == "Yes"); ui->m_qcheckboxNoVat->setChecked(m_account.value("NoVat") == "Yes"); loadKVP("iban", ui->ibanEdit); loadKVP("minBalanceAbsolute", ui->m_minBalanceAbsoluteEdit); loadKVP("minBalanceEarly", ui->m_minBalanceEarlyEdit); loadKVP("maxCreditAbsolute", ui->m_maxCreditAbsoluteEdit); loadKVP("maxCreditEarly", ui->m_maxCreditEarlyEdit); // reverse the sign for display purposes if (!ui->m_maxCreditAbsoluteEdit->lineedit()->text().isEmpty()) ui->m_maxCreditAbsoluteEdit->setValue(ui->m_maxCreditAbsoluteEdit->value()*MyMoneyMoney::MINUS_ONE); if (!ui->m_maxCreditEarlyEdit->lineedit()->text().isEmpty()) ui->m_maxCreditEarlyEdit->setValue(ui->m_maxCreditEarlyEdit->value()*MyMoneyMoney::MINUS_ONE); loadKVP("lastNumberUsed", ui->m_lastCheckNumberUsed); if (m_account.isInvest()) { ui->typeCombo->setEnabled(false); ui->m_qcheckboxPreferred->hide(); ui->m_currencyText->hide(); ui->m_currency->hide(); } else { // use the old field and override a possible new value if (!MyMoneyMoney(m_account.value("minimumBalance")).isZero()) { ui->m_minBalanceAbsoluteEdit->setValue(MyMoneyMoney(m_account.value("minimumBalance"))); } } // ui->m_qcheckboxTax->hide(); TODO should only be visible for VAT category/account } ui->m_currency->setSecurity(file->currency(m_account.currencyId())); // Load the institutions // then the accounts QString institutionName; try { if (m_isEditing && !m_account.institutionId().isEmpty()) institutionName = file->institution(m_account.institutionId()).name(); else institutionName.clear(); } catch (const MyMoneyException &e) { qDebug("exception in init for account dialog: %s", qPrintable(e.what())); } if (m_account.isInvest()) ui->m_parentAccounts->setEnabled(false); if (!m_categoryEditor) q->slotLoadInstitutions(institutionName); ui->accountNameEdit->setFocus(); q->connect(ui->buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); q->connect(ui->buttonBox, &QDialogButtonBox::accepted, q, &KNewAccountDlg::okClicked); q->connect(ui->m_parentAccounts->selectionModel(), &QItemSelectionModel::selectionChanged, q, &KNewAccountDlg::slotSelectionChanged); q->connect(ui->m_qbuttonNew, &QAbstractButton::clicked, q, &KNewAccountDlg::slotNewClicked); q->connect(ui->typeCombo, static_cast(&QComboBox::currentIndexChanged), q, &KNewAccountDlg::slotAccountTypeChanged); q->connect(ui->accountNameEdit, &QLineEdit::textChanged, q, &KNewAccountDlg::slotCheckFinished); q->connect(ui->m_vatCategory, &QAbstractButton::toggled, q, &KNewAccountDlg::slotVatChanged); q->connect(ui->m_vatAssignment, &QAbstractButton::toggled, q, &KNewAccountDlg::slotVatAssignmentChanged); q->connect(ui->m_vatCategory, &QAbstractButton::toggled, q, &KNewAccountDlg::slotCheckFinished); q->connect(ui->m_vatAssignment, &QAbstractButton::toggled, q, &KNewAccountDlg::slotCheckFinished); - q->connect(ui->m_vatRate, &kMyMoneyEdit::textChanged, q, &KNewAccountDlg::slotCheckFinished); + q->connect(ui->m_vatRate, &KMyMoneyEdit::textChanged, q, &KNewAccountDlg::slotCheckFinished); q->connect(ui->m_vatAccount, &KMyMoneySelector::stateChanged, q, &KNewAccountDlg::slotCheckFinished); q->connect(ui->m_currency, static_cast(&QComboBox::activated), q, &KNewAccountDlg::slotCheckCurrency); - q->connect(ui->m_minBalanceEarlyEdit, &kMyMoneyEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMinBalanceAbsoluteEdit); - q->connect(ui->m_minBalanceAbsoluteEdit, &kMyMoneyEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMinBalanceEarlyEdit); - q->connect(ui->m_maxCreditEarlyEdit, &kMyMoneyEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMaxCreditAbsoluteEdit); - q->connect(ui->m_maxCreditAbsoluteEdit, &kMyMoneyEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMaxCreditEarlyEdit); + q->connect(ui->m_minBalanceEarlyEdit, &KMyMoneyEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMinBalanceAbsoluteEdit); + q->connect(ui->m_minBalanceAbsoluteEdit, &KMyMoneyEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMinBalanceEarlyEdit); + q->connect(ui->m_maxCreditEarlyEdit, &KMyMoneyEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMaxCreditAbsoluteEdit); + q->connect(ui->m_maxCreditAbsoluteEdit, &KMyMoneyEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMaxCreditEarlyEdit); q->connect(ui->m_qcomboboxInstitutions, static_cast(&QComboBox::activated), q, &KNewAccountDlg::slotLoadInstitutions); auto parentIndex = m_filterProxyModel->getSelectedParentAccountIndex(); ui->m_parentAccounts->expand(parentIndex); ui->m_parentAccounts->selectionModel()->select(parentIndex, QItemSelectionModel::SelectCurrent); ui->m_parentAccounts->scrollTo(parentIndex, QAbstractItemView::PositionAtTop); ui->m_vatCategory->setChecked(false); ui->m_vatAssignment->setChecked(false); // make sure our account does not have an id and no parent assigned // and certainly no children in case we create a new account if (!m_isEditing) { m_account.clearId(); m_account.setParentAccountId(QString()); m_account.removeAccountIds(); } else { if (!m_account.value("VatRate").isEmpty()) { ui->m_vatCategory->setChecked(true); ui->m_vatRate->setValue(MyMoneyMoney(m_account.value("VatRate"))*MyMoneyMoney(100, 1)); } else { if (!m_account.value("VatAccount").isEmpty()) { QString accId = m_account.value("VatAccount").toLatin1(); try { // make sure account exists MyMoneyFile::instance()->account(accId); ui->m_vatAssignment->setChecked(true); ui->m_vatAccount->setSelected(accId); ui->m_grossAmount->setChecked(true); if (m_account.value("VatAmount") == "Net") ui->m_netAmount->setChecked(true); } catch (const MyMoneyException &) { } } } } q->slotVatChanged(ui->m_vatCategory->isChecked()); q->slotVatAssignmentChanged(ui->m_vatAssignment->isChecked()); q->slotCheckFinished(); - auto requiredFields = new kMandatoryFieldGroup(q); + auto requiredFields = new KMandatoryFieldGroup(q); requiredFields->setOkButton(ui->buttonBox->button(QDialogButtonBox::Ok)); // button to be enabled when all fields present requiredFields->add(ui->accountNameEdit); } - void loadKVP(const QString& key, kMyMoneyEdit* widget) + void loadKVP(const QString& key, KMyMoneyEdit* widget) { if (!widget) return; if (m_account.value(key).isEmpty()) { widget->clearText(); } else { widget->setValue(MyMoneyMoney(m_account.value(key))); } } void loadKVP(const QString& key, KLineEdit* widget) { if (!widget) return; widget->setText(m_account.value(key)); } void storeKVP(const QString& key, const QString& text, const QString& value) { if (text.isEmpty()) m_account.deletePair(key); else m_account.setValue(key, value); } void storeKVP(const QString& key, QCheckBox* widget) { if (widget) { if(widget->isChecked()) { m_account.setValue(key, "Yes");; } else { m_account.deletePair(key); } } } - void storeKVP(const QString& key, kMyMoneyEdit* widget) + void storeKVP(const QString& key, KMyMoneyEdit* widget) { storeKVP(key, widget->lineedit()->text(), widget->text()); } void storeKVP(const QString& key, KLineEdit* widget) { storeKVP(key, widget->text(), widget->text()); } void loadVatAccounts() { QList list; MyMoneyFile::instance()->accountList(list); QList::Iterator it; QStringList loadListExpense; QStringList loadListIncome; QStringList loadListAsset; QStringList loadListLiability; for (it = list.begin(); it != list.end(); ++it) { if (!(*it).value("VatRate").isEmpty()) { if ((*it).accountType() == Account::Expense) loadListExpense += (*it).id(); else if ((*it).accountType() == Account::Income) loadListIncome += (*it).id(); else if ((*it).accountType() == Account::Asset) loadListAsset += (*it).id(); else if ((*it).accountType() == Account::Liability) loadListLiability += (*it).id(); } } AccountSet vatSet; if (!loadListAsset.isEmpty()) vatSet.load(ui->m_vatAccount, i18n("Asset"), loadListAsset, true); if (!loadListLiability.isEmpty()) vatSet.load(ui->m_vatAccount, i18n("Liability"), loadListLiability, false); if (!loadListIncome.isEmpty()) vatSet.load(ui->m_vatAccount, i18n("Income"), loadListIncome, false); if (!loadListExpense.isEmpty()) vatSet.load(ui->m_vatAccount, i18n("Expense"), loadListExpense, false); } - void adjustEditWidgets(kMyMoneyEdit* dst, kMyMoneyEdit* src, char mode, int corr) + void adjustEditWidgets(KMyMoneyEdit* dst, KMyMoneyEdit* src, char mode, int corr) { MyMoneyMoney factor(corr, 1); if (m_account.accountGroup() == Account::Asset) factor = -factor; switch (mode) { case '<': if (src->value()*factor < dst->value()*factor) dst->setValue(src->value()); break; case '>': if (src->value()*factor > dst->value()*factor) dst->setValue(src->value()); break; } } void handleOpeningBalanceCheckbox(const QString ¤cyId) { if (m_account.accountType() == Account::Equity) { // check if there is another opening balance account with the same currency bool isOtherOpenBalancingAccount = false; QList list; MyMoneyFile::instance()->accountList(list); QList::Iterator it; for (it = list.begin(); it != list.end(); ++it) { if (it->id() == m_account.id() || currencyId != it->currencyId() || it->accountType() != Account::Equity) continue; if (it->value("OpeningBalanceAccount") == "Yes") { isOtherOpenBalancingAccount = true; break; } } if (!isOtherOpenBalancingAccount) { bool isOpenBalancingAccount = m_account.value("OpeningBalanceAccount") == "Yes"; ui->m_qcheckboxOpeningBalance->setChecked(isOpenBalancingAccount); if (isOpenBalancingAccount) { // let only allow state change if no transactions are assigned to this account bool hasTransactions = MyMoneyFile::instance()->transactionCount(m_account.id()) != 0; ui->m_qcheckboxOpeningBalance->setEnabled(!hasTransactions); if (hasTransactions) ui->m_qcheckboxOpeningBalance->setToolTip(i18n("Option has been disabled because there are transactions assigned to this account")); } } else { ui->m_qcheckboxOpeningBalance->setChecked(false); ui->m_qcheckboxOpeningBalance->setEnabled(false); ui->m_qcheckboxOpeningBalance->setToolTip(i18n("Option has been disabled because there is another account flagged to be an opening balance account for this currency")); } } else { ui->m_qcheckboxOpeningBalance->setVisible(false); } } KNewAccountDlg *q_ptr; Ui::KNewAccountDlg *ui; MyMoneyAccount m_account; MyMoneyAccount m_parentAccount; HierarchyFilterProxyModel *m_filterProxyModel; bool m_categoryEditor; bool m_isEditing; }; KNewAccountDlg::KNewAccountDlg(const MyMoneyAccount& account, bool isEditing, bool categoryEditor, QWidget *parent, const QString& title) : QDialog(parent), d_ptr(new KNewAccountDlgPrivate(this)) { Q_D(KNewAccountDlg); d->m_account = account; d->m_categoryEditor = categoryEditor; d->m_isEditing = isEditing; if (!title.isEmpty()) setWindowTitle(title); d->init(); } MyMoneyMoney KNewAccountDlg::openingBalance() const { Q_D(const KNewAccountDlg); return d->ui->m_openingBalanceEdit->value(); } void KNewAccountDlg::setOpeningBalance(const MyMoneyMoney& balance) { Q_D(KNewAccountDlg); d->ui->m_openingBalanceEdit->setValue(balance); } void KNewAccountDlg::setOpeningBalanceShown(bool shown) { Q_D(KNewAccountDlg); d->ui->m_openingBalanceLabel->setVisible(shown); d->ui->m_openingBalanceEdit->setVisible(shown); } void KNewAccountDlg::setOpeningDateShown(bool shown) { Q_D(KNewAccountDlg); d->ui->m_openingDateLabel->setVisible(shown); d->ui->m_openingDateEdit->setVisible(shown); } void KNewAccountDlg::okClicked() { Q_D(KNewAccountDlg); auto file = MyMoneyFile::instance(); QString accountNameText = d->ui->accountNameEdit->text(); if (accountNameText.isEmpty()) { KMessageBox::error(this, i18n("You have not specified a name.\nPlease fill in this field.")); d->ui->accountNameEdit->setFocus(); return; } MyMoneyAccount parent = parentAccount(); if (parent.name().length() == 0) { KMessageBox::error(this, i18n("Please select a parent account.")); return; } if (!d->m_categoryEditor) { QString institutionNameText = d->ui->m_qcomboboxInstitutions->currentText(); if (institutionNameText != i18n("(No Institution)")) { try { auto file = MyMoneyFile::instance(); QList list = file->institutionList(); QList::ConstIterator institutionIterator; for (institutionIterator = list.constBegin(); institutionIterator != list.constEnd(); ++institutionIterator) { if ((*institutionIterator).name() == institutionNameText) d->m_account.setInstitutionId((*institutionIterator).id()); } } catch (const MyMoneyException &e) { qDebug("Exception in account institution set: %s", qPrintable(e.what())); } } else { d->m_account.setInstitutionId(QString()); } } d->m_account.setName(accountNameText); d->m_account.setNumber(d->ui->accountNoEdit->text()); d->storeKVP("iban", d->ui->ibanEdit); d->storeKVP("minBalanceAbsolute", d->ui->m_minBalanceAbsoluteEdit); d->storeKVP("minBalanceEarly", d->ui->m_minBalanceEarlyEdit); // the figures for credit line with reversed sign if (!d->ui->m_maxCreditAbsoluteEdit->lineedit()->text().isEmpty()) d->ui->m_maxCreditAbsoluteEdit->setValue(d->ui->m_maxCreditAbsoluteEdit->value()*MyMoneyMoney::MINUS_ONE); if (!d->ui->m_maxCreditEarlyEdit->lineedit()->text().isEmpty()) d->ui->m_maxCreditEarlyEdit->setValue(d->ui->m_maxCreditEarlyEdit->value()*MyMoneyMoney::MINUS_ONE); d->storeKVP("maxCreditAbsolute", d->ui->m_maxCreditAbsoluteEdit); d->storeKVP("maxCreditEarly", d->ui->m_maxCreditEarlyEdit); if (!d->ui->m_maxCreditAbsoluteEdit->lineedit()->text().isEmpty()) d->ui->m_maxCreditAbsoluteEdit->setValue(d->ui->m_maxCreditAbsoluteEdit->value()*MyMoneyMoney::MINUS_ONE); if (!d->ui->m_maxCreditEarlyEdit->lineedit()->text().isEmpty()) d->ui->m_maxCreditEarlyEdit->setValue(d->ui->m_maxCreditEarlyEdit->value()*MyMoneyMoney::MINUS_ONE); d->storeKVP("lastNumberUsed", d->ui->m_lastCheckNumberUsed); // delete a previous version of the minimumbalance information d->storeKVP("minimumBalance", QString(), QString()); Account acctype; if (!d->m_categoryEditor) { acctype = static_cast(d->ui->typeCombo->currentData().toInt()); // If it's a loan, check if the parent is asset or liability. In // case of asset, we change the account type to be AssetLoan if (acctype == Account::Loan && parent.accountGroup() == Account::Asset) acctype = Account::AssetLoan; } else { acctype = parent.accountGroup(); QString newName; if (!MyMoneyFile::instance()->isStandardAccount(parent.id())) { newName = MyMoneyFile::instance()->accountToCategory(parent.id()) + MyMoneyFile::AccountSeperator; } newName += accountNameText; if (!file->categoryToAccount(newName, acctype).isEmpty() && (file->categoryToAccount(newName, acctype) != d->m_account.id())) { KMessageBox::error(this, QString("") + i18n("A category named %1 already exists. You cannot create a second category with the same name.", newName) + QString("")); return; } } d->m_account.setAccountType(acctype); d->m_account.setDescription(d->ui->descriptionEdit->toPlainText()); d->m_account.setOpeningDate(d->ui->m_openingDateEdit->date()); if (!d->m_categoryEditor) { d->m_account.setCurrencyId(d->ui->m_currency->security().id()); d->storeKVP("PreferredAccount", d->ui->m_qcheckboxPreferred); d->storeKVP("NoVat", d->ui->m_qcheckboxNoVat); if (d->ui->m_minBalanceAbsoluteEdit->isVisible()) { d->m_account.setValue("minimumBalance", d->ui->m_minBalanceAbsoluteEdit->value().toString()); } } else { if (KMyMoneyGlobalSettings::hideUnusedCategory() && !d->m_isEditing) { KMessageBox::information(this, i18n("You have selected to suppress the display of unused categories in the KMyMoney configuration dialog. The category you just created will therefore only be shown if it is used. Otherwise, it will be hidden in the accounts/categories view."), i18n("Hidden categories"), "NewHiddenCategory"); } d->m_account.setCostCenterRequired(d->ui->m_costCenterRequiredCheckBox->isChecked()); } d->storeKVP("Tax", d->ui->m_qcheckboxTax); if (d->ui->m_qcheckboxOpeningBalance->isChecked()) d->m_account.setValue("OpeningBalanceAccount", "Yes"); else d->m_account.deletePair("OpeningBalanceAccount"); d->m_account.deletePair("VatAccount"); d->m_account.deletePair("VatAmount"); d->m_account.deletePair("VatRate"); if (d->ui->m_vatCategory->isChecked()) { d->m_account.setValue("VatRate", (d->ui->m_vatRate->value().abs() / MyMoneyMoney(100, 1)).toString()); } else { if (d->ui->m_vatAssignment->isChecked() && !d->ui->m_vatAccount->selectedItems().isEmpty()) { d->m_account.setValue("VatAccount", d->ui->m_vatAccount->selectedItems().first()); if (d->ui->m_netAmount->isChecked()) d->m_account.setValue("VatAmount", "Net"); } } accept(); } MyMoneyAccount KNewAccountDlg::account() { Q_D(KNewAccountDlg); // assign the right currency to the account d->m_account.setCurrencyId(d->ui->m_currency->security().id()); // and the price mode switch (d->ui->m_priceMode->currentItem()) { case 0: d->m_account.deletePair("priceMode"); break; case 1: case 2: d->m_account.setValue("priceMode", QString("%1").arg(d->ui->m_priceMode->currentItem())); break; } return d->m_account; } MyMoneyAccount KNewAccountDlg::parentAccount() const { Q_D(const KNewAccountDlg); return d->m_parentAccount; } void KNewAccountDlg::slotSelectionChanged(const QItemSelection ¤t, const QItemSelection &previous) { Q_UNUSED(previous) Q_D(KNewAccountDlg); if (!current.indexes().empty()) { QVariant account = d->ui->m_parentAccounts->model()->data(current.indexes().front(), (int)eAccountsModel::Role::Account); if (account.isValid()) { d->m_parentAccount = account.value(); d->ui->m_subAccountLabel->setText(i18n("Is a sub account of %1", d->m_parentAccount.name())); } } } void KNewAccountDlg::slotLoadInstitutions(const QString& name) { Q_D(KNewAccountDlg); d->ui->m_qcomboboxInstitutions->clear(); QString bic; // Are we forcing the user to use institutions? d->ui->m_qcomboboxInstitutions->addItem(i18n("(No Institution)")); d->ui->m_bicValue->setText(" "); d->ui->ibanEdit->setEnabled(false); d->ui->accountNoEdit->setEnabled(false); try { auto file = MyMoneyFile::instance(); QList list = file->institutionList(); QList::ConstIterator institutionIterator; for (institutionIterator = list.constBegin(); institutionIterator != list.constEnd(); ++institutionIterator) { if ((*institutionIterator).name() == name) { d->ui->ibanEdit->setEnabled(true); d->ui->accountNoEdit->setEnabled(true); d->ui->m_bicValue->setText((*institutionIterator).value("bic")); } d->ui->m_qcomboboxInstitutions->addItem((*institutionIterator).name()); } d->ui->m_qcomboboxInstitutions->setCurrentItem(name, false); } catch (const MyMoneyException &e) { qDebug("Exception in institution load: %s", qPrintable(e.what())); } } void KNewAccountDlg::slotNewClicked() { MyMoneyInstitution institution; QPointer dlg = new KNewBankDlg(institution, this); if (dlg->exec()) { MyMoneyFileTransaction ft; try { auto file = MyMoneyFile::instance(); institution = dlg->institution(); file->addInstitution(institution); ft.commit(); slotLoadInstitutions(institution.name()); } catch (const MyMoneyException &) { KMessageBox::information(this, i18n("Cannot add institution")); } } delete dlg; } void KNewAccountDlg::slotAccountTypeChanged(int index) { Q_D(KNewAccountDlg); Account oldType; auto type = d->ui->typeCombo->itemData(index).value(); try { oldType = d->m_account.accountType(); if (oldType != type) { d->m_account.setAccountType(type); // update the account group displayed in the accounts hierarchy d->m_filterProxyModel->clear(); d->m_filterProxyModel->addAccountGroup(QVector {d->m_account.accountGroup()}); } } catch (const MyMoneyException &) { qWarning("Unexpected exception in KNewAccountDlg::slotAccountTypeChanged()"); } } void KNewAccountDlg::slotCheckFinished() { Q_D(KNewAccountDlg); auto showButton = true; if (d->ui->accountNameEdit->text().length() == 0) { showButton = false; } if (d->ui->m_vatCategory->isChecked() && d->ui->m_vatRate->value() <= MyMoneyMoney()) { showButton = false; } else { if (d->ui->m_vatAssignment->isChecked() && d->ui->m_vatAccount->selectedItems().isEmpty()) showButton = false; } d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(showButton); } void KNewAccountDlg::slotVatChanged(bool state) { Q_D(KNewAccountDlg); if (state) { d->ui->m_vatCategoryFrame->show(); d->ui->m_vatAssignmentFrame->hide(); } else { d->ui->m_vatCategoryFrame->hide(); if (!d->m_account.isAssetLiability()) { d->ui->m_vatAssignmentFrame->show(); } } } void KNewAccountDlg::slotVatAssignmentChanged(bool state) { Q_D(KNewAccountDlg); d->ui->m_vatAccount->setEnabled(state); d->ui->m_amountGroupBox->setEnabled(state); } void KNewAccountDlg::slotAdjustMinBalanceAbsoluteEdit(const QString&) { Q_D(KNewAccountDlg); d->adjustEditWidgets(d->ui->m_minBalanceAbsoluteEdit, d->ui->m_minBalanceEarlyEdit, '<', -1); } void KNewAccountDlg::slotAdjustMinBalanceEarlyEdit(const QString&) { Q_D(KNewAccountDlg); d->adjustEditWidgets(d->ui->m_minBalanceEarlyEdit, d->ui->m_minBalanceAbsoluteEdit, '>', -1); } void KNewAccountDlg::slotAdjustMaxCreditAbsoluteEdit(const QString&) { Q_D(KNewAccountDlg); d->adjustEditWidgets(d->ui->m_maxCreditAbsoluteEdit, d->ui->m_maxCreditEarlyEdit, '>', 1); } void KNewAccountDlg::slotAdjustMaxCreditEarlyEdit(const QString&) { Q_D(KNewAccountDlg); d->adjustEditWidgets(d->ui->m_maxCreditEarlyEdit, d->ui->m_maxCreditAbsoluteEdit, '<', 1); } void KNewAccountDlg::slotCheckCurrency(int index) { Q_D(KNewAccountDlg); Q_UNUSED(index) d->handleOpeningBalanceCheckbox(d->ui->m_currency->security().id()); } void KNewAccountDlg::addTab(QWidget* w, const QString& name) { Q_D(KNewAccountDlg); if (w) { w->setParent(d->ui->m_tab); d->ui->m_tab->addTab(w, name); } } diff --git a/kmymoney/dialogs/knewaccountdlg.ui b/kmymoney/dialogs/knewaccountdlg.ui index 98648e07b..714ebd365 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 + KMyMoneyEdit QWidget
../widgets/kmymoneyedit.h
1
KMyMoneyGeneralCombo QWidget -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.h
1
- kMyMoneyDateInput + KMyMoneyDateInput QWidget
kmymoneydateinput.h
1
KMyMoneySecuritySelector KComboBox
kmymoneycurrencyselector.h
1
KMyMoneyAccountTreeView QTreeView
kmymoneyaccounttreeview.h
- kMyMoneyAccountSelector + 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/knewbankdlg.cpp b/kmymoney/dialogs/knewbankdlg.cpp index ccd40f6a9..99078185b 100644 --- a/kmymoney/dialogs/knewbankdlg.cpp +++ b/kmymoney/dialogs/knewbankdlg.cpp @@ -1,123 +1,123 @@ /*************************************************************************** knewbankdlg.cpp ------------------- copyright : (C) 2000 by Michael Edwardes email : mte@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 "knewbankdlg.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_knewbankdlg.h" #include "mymoneyinstitution.h" class KNewBankDlgPrivate { Q_DISABLE_COPY(KNewBankDlgPrivate) public: KNewBankDlgPrivate() : ui(new Ui::KNewBankDlg) { } ~KNewBankDlgPrivate() { delete ui; } Ui::KNewBankDlg *ui; MyMoneyInstitution m_institution; }; KNewBankDlg::KNewBankDlg(MyMoneyInstitution& institution, QWidget *parent) : QDialog(parent), d_ptr(new KNewBankDlgPrivate) { Q_D(KNewBankDlg); d->ui->setupUi(this); d->m_institution = institution; setModal(true); d->ui->nameEdit->setFocus(); d->ui->nameEdit->setText(institution.name()); d->ui->cityEdit->setText(institution.city()); d->ui->streetEdit->setText(institution.street()); d->ui->postcodeEdit->setText(institution.postcode()); d->ui->telephoneEdit->setText(institution.telephone()); d->ui->bicEdit->setText(institution.value("bic")); d->ui->sortCodeEdit->setText(institution.sortcode()); connect(d->ui->buttonBox, &QDialogButtonBox::accepted, this, &KNewBankDlg::okClicked); connect(d->ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(d->ui->nameEdit, &QLineEdit::textChanged, this, &KNewBankDlg::institutionNameChanged); institutionNameChanged(d->ui->nameEdit->text()); - auto requiredFields = new kMandatoryFieldGroup(this); + auto requiredFields = new KMandatoryFieldGroup(this); requiredFields->setOkButton(d->ui->buttonBox->button(QDialogButtonBox::Ok)); // button to be enabled when all fields present requiredFields->add(d->ui->nameEdit); } void KNewBankDlg::institutionNameChanged(const QString &_text) { Q_D(KNewBankDlg); d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!_text.isEmpty()); } KNewBankDlg::~KNewBankDlg() { Q_D(KNewBankDlg); delete d; } void KNewBankDlg::okClicked() { Q_D(KNewBankDlg); if (d->ui->nameEdit->text().isEmpty()) { KMessageBox::information(this, i18n("The institution name field is empty. Please enter the name."), i18n("Adding New Institution")); d->ui->nameEdit->setFocus(); return; } d->m_institution.setName(d->ui->nameEdit->text()); d->m_institution.setTown(d->ui->cityEdit->text()); d->m_institution.setStreet(d->ui->streetEdit->text()); d->m_institution.setPostcode(d->ui->postcodeEdit->text()); d->m_institution.setTelephone(d->ui->telephoneEdit->text()); d->m_institution.setValue("bic", d->ui->bicEdit->text()); d->m_institution.setSortcode(d->ui->sortCodeEdit->text()); accept(); } const MyMoneyInstitution& KNewBankDlg::institution() { Q_D(KNewBankDlg); return d->m_institution; } diff --git a/kmymoney/dialogs/knewequityentrydlg.cpp b/kmymoney/dialogs/knewequityentrydlg.cpp index c5076024d..f5ef3760a 100644 --- a/kmymoney/dialogs/knewequityentrydlg.cpp +++ b/kmymoney/dialogs/knewequityentrydlg.cpp @@ -1,149 +1,149 @@ /*************************************************************************** knewequityentrydlg.cpp - description ------------------- begin : Tue Jan 29 2002 copyright : (C) 2000-2002 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 "knewequityentrydlg.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "ui_knewequityentrydlg.h" #include "kmymoneyedit.h" #include "mymoneymoney.h" class KNewEquityEntryDlgPrivate { Q_DISABLE_COPY(KNewEquityEntryDlgPrivate) Q_DECLARE_PUBLIC(KNewEquityEntryDlg) public: KNewEquityEntryDlgPrivate() : ui(new Ui::KNewEquityEntryDlg) { } ~KNewEquityEntryDlgPrivate() { delete ui; } KNewEquityEntryDlg *q_ptr; Ui::KNewEquityEntryDlg *ui; QString m_strSymbolName; QString m_strName; int m_fraction; }; KNewEquityEntryDlg::KNewEquityEntryDlg(QWidget *parent) : QDialog(parent), d_ptr(new KNewEquityEntryDlgPrivate) { Q_D(KNewEquityEntryDlg); d->m_fraction = 0; d->ui->setupUi(this); setModal(true); d->ui->edtFraction->setCalculatorButtonVisible(false); d->ui->edtFraction->setPrecision(0); d->ui->edtFraction->loadText("100"); connect(d->ui->buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this, &KNewEquityEntryDlg::onOKClicked); - connect(d->ui->edtFraction, &kMyMoneyEdit::textChanged, this, &KNewEquityEntryDlg::slotDataChanged); + connect(d->ui->edtFraction, &KMyMoneyEdit::textChanged, this, &KNewEquityEntryDlg::slotDataChanged); connect(d->ui->edtMarketSymbol, &QLineEdit::textChanged, this, &KNewEquityEntryDlg::slotDataChanged); connect(d->ui->edtEquityName, &QLineEdit::textChanged, this, &KNewEquityEntryDlg::slotDataChanged); // add icons to buttons slotDataChanged(); d->ui->edtEquityName->setFocus(); } KNewEquityEntryDlg::~KNewEquityEntryDlg() { Q_D(KNewEquityEntryDlg); delete d; } /** No descriptions */ void KNewEquityEntryDlg::onOKClicked() { Q_D(KNewEquityEntryDlg); d->m_strSymbolName = d->ui->edtMarketSymbol->text(); d->m_strName = d->ui->edtEquityName->text(); d->m_fraction = d->ui->edtFraction->value().abs().formatMoney("", 0, false).toUInt(); accept(); } void KNewEquityEntryDlg::setSymbolName(const QString& str) { Q_D(KNewEquityEntryDlg); d->m_strSymbolName = str; d->ui->edtMarketSymbol->setText(d->m_strSymbolName); } QString KNewEquityEntryDlg::symbolName() const { Q_D(const KNewEquityEntryDlg); return d->m_strSymbolName; } void KNewEquityEntryDlg::setName(const QString& str) { Q_D(KNewEquityEntryDlg); d->m_strName = str; d->ui->edtEquityName->setText(d->m_strName); } QString KNewEquityEntryDlg::name() const { Q_D(const KNewEquityEntryDlg); return d->m_strName; } int KNewEquityEntryDlg::fraction() const { Q_D(const KNewEquityEntryDlg); return d->m_fraction; } void KNewEquityEntryDlg::slotDataChanged() { Q_D(KNewEquityEntryDlg); auto okEnabled = true; if (!d->ui->edtFraction->value().isPositive() || d->ui->edtMarketSymbol->text().isEmpty() || d->ui->edtEquityName->text().isEmpty()) okEnabled = false; d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(okEnabled); } diff --git a/kmymoney/dialogs/knewequityentrydlg.ui b/kmymoney/dialogs/knewequityentrydlg.ui index 125d5be29..c21847c27 100644 --- a/kmymoney/dialogs/knewequityentrydlg.ui +++ b/kmymoney/dialogs/knewequityentrydlg.ui @@ -1,197 +1,197 @@ KNewEquityEntryDlg 0 0 417 208 New Equity true Equity Name: false Investment Type: false Trading Symbol of the stock or mutual fund, not required. Trading Symbol of the stock or mutual fund, not required. 0 0 1 / Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false - + Market Symbol: false Smallest fraction: false Stock Mutual Fund Bond Name of the company, or mutual fund. Name of the company, or mutual fund. Qt::Vertical QSizePolicy::Expanding 20 16 QDialogButtonBox::Cancel|QDialogButtonBox::Ok KComboBox QComboBox
kcombobox.h
KLineEdit QLineEdit
klineedit.h
- kMyMoneyEdit + KMyMoneyEdit QWidget
kmymoneyedit.h
1
edtEquityName edtMarketSymbol cmbInvestmentType buttonBox rejected() KNewEquityEntryDlg reject() 208 184 208 103
diff --git a/kmymoney/dialogs/konlinetransferform.cpp b/kmymoney/dialogs/konlinetransferform.cpp index 7fc4b654c..8058419aa 100644 --- a/kmymoney/dialogs/konlinetransferform.cpp +++ b/kmymoney/dialogs/konlinetransferform.cpp @@ -1,331 +1,331 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 Christian Dávid * (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. * * 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 "konlinetransferform.h" #include "ui_konlinetransferform.h" #include #include #include #include #include #include #include #include "kguiutils.h" #include "onlinetasks/interfaces/ui/ionlinejobedit.h" #include "mymoneyfile.h" #include "mymoney/onlinejobadministration.h" #include "onlinejob.h" #include "tasks/onlinetask.h" #include "accountsmodel.h" #include "models/models.h" #include "icons/icons.h" using namespace Icons; kOnlineTransferForm::kOnlineTransferForm(QWidget *parent) : QDialog(parent), ui(new Ui::kOnlineTransferForm), m_onlineJobEditWidgets(QList()), - m_requiredFields(new kMandatoryFieldGroup(this)) + m_requiredFields(new KMandatoryFieldGroup(this)) { ui->setupUi(this); ui->unsupportedIcon->setPixmap(QIcon::fromTheme(g_Icons[Icon::DialogInformation]).pixmap(style()->pixelMetric(QStyle::PM_MessageBoxIconSize))); // The ui designer fills the QScrollArea with a QWidget. Remove it so we can simply check for .widget() == nullptr // if it contains a valid widget delete ui->creditTransferEdit->takeWidget(); OnlineBankingAccountNamesFilterProxyModel* accountsModel = new OnlineBankingAccountNamesFilterProxyModel(this); auto const model = Models::instance()->accountsModel(); accountsModel->setSourceModel(model); ui->originAccount->setModel(accountsModel); ui->convertMessage->hide(); ui->convertMessage->setWordWrap(true); auto edits = onlineJobAdministration::instance()->onlineJobEdits(); std::for_each(edits.constBegin(), edits.constEnd(), [this](onlineJobAdministration::onlineJobEditOffer in) {this->loadOnlineJobEditPlugin(in);}); // Message Widget for read only jobs m_duplicateJob = KStandardAction::copy(this); connect(m_duplicateJob, &QAction::triggered, this, &kOnlineTransferForm::duplicateCurrentJob); ui->headMessage->hide(); ui->headMessage->setWordWrap(true); ui->headMessage->setCloseButtonVisible(false); ui->headMessage->addAction(m_duplicateJob); connect(ui->transferTypeSelection, static_cast(&QComboBox::currentIndexChanged), this, &kOnlineTransferForm::convertCurrentJob); connect(ui->buttonAbort, &QAbstractButton::clicked, this, &kOnlineTransferForm::reject); connect(ui->buttonSend, &QAbstractButton::clicked, this, &kOnlineTransferForm::sendJob); connect(ui->buttonEnque, &QAbstractButton::clicked, this, &kOnlineTransferForm::accept); - connect(m_requiredFields, static_cast(&kMandatoryFieldGroup::stateChanged), ui->buttonEnque, &QPushButton::setEnabled); + connect(m_requiredFields, static_cast(&KMandatoryFieldGroup::stateChanged), ui->buttonEnque, &QPushButton::setEnabled); connect(ui->originAccount, &KMyMoneyAccountCombo::accountSelected, this, &kOnlineTransferForm::accountChanged); accountChanged(); setJobReadOnly(false); m_requiredFields->add(ui->originAccount); m_requiredFields->setOkButton(ui->buttonSend); } void kOnlineTransferForm::loadOnlineJobEditPlugin(const onlineJobAdministration::onlineJobEditOffer& pluginDesc) { try { std::unique_ptr loader{new QPluginLoader(pluginDesc.fileName, this)}; QObject* plugin = loader->instance(); if (!plugin) { qWarning() << "Could not load plugin for online job editor from file \"" << pluginDesc.fileName << "\"."; return; } // Cast to KPluginFactory KPluginFactory* pluginFactory = qobject_cast< KPluginFactory* >(plugin); if (!pluginFactory) { qWarning() << "Could not create plugin factory for online job editor in file \"" << pluginDesc.fileName << "\"."; return; } IonlineJobEdit* widget = pluginFactory->create(pluginDesc.pluginKeyword, this); if (!widget) { qWarning() << "Could not create online job editor in file \"" << pluginDesc.fileName << "\"."; return; } // directly load the first widget into QScrollArea bool showWidget = true; if (!m_onlineJobEditWidgets.isEmpty()) { widget->setEnabled(false); showWidget = false; } m_onlineJobEditWidgets.append(widget); ui->transferTypeSelection->addItem(pluginDesc.name); m_requiredFields->add(widget); if (showWidget) showEditWidget(widget); } catch (MyMoneyException& e) { qWarning("Error while loading a plugin (IonlineJobEdit)."); } } void kOnlineTransferForm::convertCurrentJob(const int& index) { Q_ASSERT(index < m_onlineJobEditWidgets.count()); IonlineJobEdit* widget = m_onlineJobEditWidgets.at(index); // Vars set by onlineJobAdministration::convertBest onlineTaskConverter::convertType convertType; QString userMessage; widget->setOnlineJob(onlineJobAdministration::instance()->convertBest(activeOnlineJob(), widget->supportedOnlineTasks(), convertType, userMessage)); if (convertType == onlineTaskConverter::convertImpossible && userMessage.isEmpty()) userMessage = i18n("During the change of the order your previous entries could not be converted."); if (!userMessage.isEmpty()) { switch (convertType) { case onlineTaskConverter::convertionLossyMajor: ui->convertMessage->setMessageType(KMessageWidget::Warning); break; case onlineTaskConverter::convertImpossible: case onlineTaskConverter::convertionLossyMinor: ui->convertMessage->setMessageType(KMessageWidget::Information); break; case onlineTaskConverter::convertionLoseless: break; } ui->convertMessage->setText(userMessage); ui->convertMessage->animatedShow(); } showEditWidget(widget); } void kOnlineTransferForm::duplicateCurrentJob() { IonlineJobEdit* widget = qobject_cast< IonlineJobEdit* >(ui->creditTransferEdit->widget()); if (widget == 0) return; onlineJob duplicate(QString(), activeOnlineJob()); widget->setOnlineJob(duplicate); } void kOnlineTransferForm::accept() { emit acceptedForSave(activeOnlineJob()); QDialog::accept(); } void kOnlineTransferForm::sendJob() { emit acceptedForSend(activeOnlineJob()); QDialog::accept(); } void kOnlineTransferForm::reject() { QDialog::reject(); } bool kOnlineTransferForm::setOnlineJob(const onlineJob job) { QString name; try { name = job.task()->taskName(); } catch (const onlineJob::emptyTask&) { return false; } setCurrentAccount(job.responsibleAccount()); if (showEditWidget(name)) { IonlineJobEdit* widget = qobject_cast(ui->creditTransferEdit->widget()); if (widget != 0) { // This can happen if there are no widgets const bool ret = widget->setOnlineJob(job); setJobReadOnly(!job.isEditable()); return ret; } } return false; } void kOnlineTransferForm::accountChanged() { const QString accountId = ui->originAccount->getSelected(); try { ui->orderAccountBalance->setValue(MyMoneyFile::instance()->balance(accountId)); } catch (const MyMoneyException&) { // @todo this can happen until the selection allows to select correct accounts only ui->orderAccountBalance->setText(""); } foreach (IonlineJobEdit* widget, m_onlineJobEditWidgets) widget->setOriginAccount(accountId); checkNotSupportedWidget(); } bool kOnlineTransferForm::checkEditWidget() { return checkEditWidget(qobject_cast(ui->creditTransferEdit->widget())); } bool kOnlineTransferForm::checkEditWidget(IonlineJobEdit* widget) { if (widget != 0 && onlineJobAdministration::instance()->isJobSupported(ui->originAccount->getSelected(), widget->supportedOnlineTasks())) { return true; } return false; } /** @todo auto set another widget if a loseless convert is possible */ void kOnlineTransferForm::checkNotSupportedWidget() { if (!checkEditWidget()) { ui->displayStack->setCurrentIndex(0); } else { ui->displayStack->setCurrentIndex(1); } } void kOnlineTransferForm::setCurrentAccount(const QString& accountId) { ui->originAccount->setSelected(accountId); } onlineJob kOnlineTransferForm::activeOnlineJob() const { IonlineJobEdit* widget = qobject_cast(ui->creditTransferEdit->widget()); if (widget == 0) return onlineJob(); return widget->getOnlineJob(); } void kOnlineTransferForm::setJobReadOnly(const bool& readOnly) { ui->originAccount->setDisabled(readOnly); ui->transferTypeSelection->setDisabled(readOnly); if (readOnly) { ui->headMessage->setMessageType(KMessageWidget::Information); if (activeOnlineJob().sendDate().isValid()) ui->headMessage->setText(i18n("This credit-transfer was sent to your bank at %1 therefore cannot be edited anymore. You may create a copy for editing.").arg(activeOnlineJob().sendDate().toString(Qt::DefaultLocaleShortDate))); else ui->headMessage->setText(i18n("This credit-transfer is not editable. You may create a copy for editing.")); if (this->isHidden()) ui->headMessage->show(); else ui->headMessage->animatedShow(); } else { ui->headMessage->animatedHide(); } } bool kOnlineTransferForm::showEditWidget(const QString& onlineTaskName) { int index = 0; foreach (IonlineJobEdit* widget, m_onlineJobEditWidgets) { if (widget->supportedOnlineTasks().contains(onlineTaskName)) { ui->transferTypeSelection->setCurrentIndex(index); showEditWidget(widget); return true; } ++index; } return false; } void kOnlineTransferForm::showEditWidget(IonlineJobEdit* widget) { Q_CHECK_PTR(widget); QWidget* oldWidget = ui->creditTransferEdit->takeWidget(); if (oldWidget != 0) { // This is true at the first call of showEditWidget() and if there are no widgets. oldWidget->setEnabled(false); disconnect(qobject_cast(oldWidget), &IonlineJobEdit::readOnlyChanged, this, &kOnlineTransferForm::setJobReadOnly); } widget->setEnabled(true); ui->creditTransferEdit->setWidget(widget); setJobReadOnly(widget->isReadOnly()); widget->show(); connect(widget, &IonlineJobEdit::readOnlyChanged, this, &kOnlineTransferForm::setJobReadOnly); checkNotSupportedWidget(); m_requiredFields->changed(); } kOnlineTransferForm::~kOnlineTransferForm() { ui->creditTransferEdit->takeWidget(); qDeleteAll(m_onlineJobEditWidgets); delete ui; delete m_duplicateJob; } diff --git a/kmymoney/dialogs/konlinetransferform.h b/kmymoney/dialogs/konlinetransferform.h index e4bc2d9ba..2ed09ea90 100644 --- a/kmymoney/dialogs/konlinetransferform.h +++ b/kmymoney/dialogs/konlinetransferform.h @@ -1,137 +1,137 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 Christian Dávid * (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. * * 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 KONLINETRANSFERFORM_H #define KONLINETRANSFERFORM_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoney/onlinejob.h" #include "mymoney/onlinejobadministration.h" class IonlineJobEdit; -class kMandatoryFieldGroup; +class KMandatoryFieldGroup; namespace Ui { class kOnlineTransferForm; } /** * @brief The kOnlineTransferForm class * * @todo Disable Send/Enque button if no task is shown. * @todo If this dialog is shown a second time without setting a onlineJob it, it shows the previous content. * Fix this by creating the IonlineJobEdit widgets on demand and destroying them afterwards. */ class kOnlineTransferForm : public QDialog { Q_OBJECT public: kOnlineTransferForm(QWidget *parent = nullptr); virtual ~kOnlineTransferForm(); signals: /** @brief The user wants this job to be saved */ void acceptedForSave(onlineJob); /** @brief User wants to send the onlineJob directly */ void acceptedForSend(onlineJob); public slots: virtual void accept(); virtual void reject(); /** @brief sets the current origin account */ virtual void setCurrentAccount(const QString& accountId); /** * @brief Sets an onlineTransfer to edit * * @return true if there is widget which supports editing this onlineJob */ virtual bool setOnlineJob(const onlineJob); void duplicateCurrentJob(); private slots: /** @brief Slot for account selection box */ void accountChanged(); /** * @brief Slot to change job type * @param index of KComboBox (== index of selected widget in m_onlineJobEditWidgets) */ void convertCurrentJob(const int& index); /** @brief Slot for send button */ void sendJob(); /** * @brief Load a plugin */ void loadOnlineJobEditPlugin(const onlineJobAdministration::onlineJobEditOffer& plugin); /** @{ */ /** * @brief Activates the onlineJobEdit widget */ bool showEditWidget(const QString& onlineTaskName); void showEditWidget(IonlineJobEdit* widget); /** @} */ /** * @brief Shows warning if checkEditWidget() == false */ void checkNotSupportedWidget(); void setJobReadOnly(const bool&); private: /** * @brief returns the currently edited onlineJob * Can be a null job */ onlineJob activeOnlineJob() const; Ui::kOnlineTransferForm* ui; QList m_onlineJobEditWidgets; - kMandatoryFieldGroup* m_requiredFields; + KMandatoryFieldGroup* m_requiredFields; QAction* m_duplicateJob; /** * @brief Checks if widget can edit any task the selected account supports */ bool checkEditWidget(IonlineJobEdit* widget); /** * @brief Checks current widget * @see checkEditWidget( IonlineJobEdit* widget ) */ bool checkEditWidget(); void editWidgetChanged(); }; #endif // KONLINETRANSFERFORM_H diff --git a/kmymoney/dialogs/konlinetransferform.ui b/kmymoney/dialogs/konlinetransferform.ui index 56acbe416..bd4f851dd 100644 --- a/kmymoney/dialogs/konlinetransferform.ui +++ b/kmymoney/dialogs/konlinetransferform.ui @@ -1,301 +1,301 @@ kOnlineTransferForm 0 0 624 617 false Order Account Account Balance - + false false false true Credit Transfer 0 0 333 250 0 Qt::Vertical 20 54 Qt::Horizontal 0 20 QLayout::SetMaximumSize 0 0 300 16777215 <html><head/><body><p>This account does not support online banking.</p><p>If you think this is an error, please check if the plugin for this type of credit transfer and your online banking plugins are available.</p></body></html> true Qt::Horizontal 0 20 Qt::Vertical 20 53 QFrame::NoFrame QFrame::Plain true Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 0 0 75 16 Qt::Horizontal 40 20 Enqueue false Send Abort - kMyMoneyEdit + KMyMoneyEdit QFrame
kmymoneyedit.h
1
KMyMoneyAccountCombo KComboBox
kmymoneyaccountcombo.h
KComboBox QComboBox
kcombobox.h
KMessageWidget QWidget
KMessageWidget
1
buttonSend buttonEnque buttonAbort
diff --git a/kmymoney/dialogs/kpayeereassigndlg.cpp b/kmymoney/dialogs/kpayeereassigndlg.cpp index 364341c0d..02eb0be06 100644 --- a/kmymoney/dialogs/kpayeereassigndlg.cpp +++ b/kmymoney/dialogs/kpayeereassigndlg.cpp @@ -1,124 +1,124 @@ /*************************************************************************** kpayeereassigndlg.cpp ------------------- copyright : (C) 2005 by Andreas Nicolai (C) 2007 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 "kpayeereassigndlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kpayeereassigndlg.h" #include /** This lookup table needs to be in sync with KPayeeReassignDlg::OperationType enum */ static const char * labelText[KPayeeReassignDlg::TypeCount] = { I18N_NOOP("To be able to merge previous selected payees, please select a payee from the list below or create a new one."), I18N_NOOP("The transactions associated with the selected payees need to be re-assigned to a different payee before the selected payees can be deleted. Please select a payee from the list below."), }; class KPayeeReassignDlgPrivate { Q_DISABLE_COPY(KPayeeReassignDlgPrivate) public: KPayeeReassignDlgPrivate() : ui(new Ui::KPayeeReassignDlg) { } ~KPayeeReassignDlgPrivate() { delete ui; } Ui::KPayeeReassignDlg *ui; KPayeeReassignDlg::OperationType m_type; }; KPayeeReassignDlg::KPayeeReassignDlg(KPayeeReassignDlg::OperationType type, QWidget* parent) : QDialog(parent), d_ptr(new KPayeeReassignDlgPrivate) { Q_D(KPayeeReassignDlg); d->ui->setupUi(this); d->m_type = type; - auto mandatory = new kMandatoryFieldGroup(this); + auto mandatory = new KMandatoryFieldGroup(this); mandatory->add(d->ui->payeeCombo); mandatory->setOkButton(d->ui->buttonBox->button(QDialogButtonBox::Ok)); d->ui->textLabel1->setText(i18n(labelText[d->m_type])); } KPayeeReassignDlg::~KPayeeReassignDlg() { Q_D(KPayeeReassignDlg); delete d; } QString KPayeeReassignDlg::show(const QList& payeeslist) { Q_D(KPayeeReassignDlg); if (payeeslist.isEmpty()) return QString(); // no payee available? nothing can be selected... d->ui->payeeCombo->loadPayees(payeeslist); // execute dialog and if aborted, return empty string if (this->exec() == QDialog::Rejected) return QString(); // allow to return the text (new payee) if type is Merge if (d->m_type == TypeMerge && d->ui->payeeCombo->selectedItem().isEmpty()) return d->ui->payeeCombo->lineEdit()->text(); // otherwise return index of selected payee return d->ui->payeeCombo->selectedItem(); } bool KPayeeReassignDlg::addToMatchList() const { Q_D(const KPayeeReassignDlg); return d->ui->m_copyToMatchList->isChecked(); } void KPayeeReassignDlg::accept() { Q_D(KPayeeReassignDlg); // force update of d->ui->payeeCombo d->ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); if (d->m_type == TypeDelete && d->ui->payeeCombo->selectedItem().isEmpty()) { KMessageBox::information(this, i18n("This dialog does not allow new payees to be created. Please pick a payee from the list."), i18n("Payee creation")); } else { QDialog::accept(); } } diff --git a/kmymoney/dialogs/kpayeereassigndlg.ui b/kmymoney/dialogs/kpayeereassigndlg.ui index 6924cb0cd..1c7888e9d 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 -
kmymoneymvccombo.h
+
kmymoneypayeecombo.h
1
buttonBox accepted() KPayeeReassignDlg accept() 239 283 117 309 buttonBox rejected() KPayeeReassignDlg reject() 456 292 418 308
diff --git a/kmymoney/dialogs/kselectdatabasedlg.cpp b/kmymoney/dialogs/kselectdatabasedlg.cpp index 8a10b139b..026798c94 100644 --- a/kmymoney/dialogs/kselectdatabasedlg.cpp +++ b/kmymoney/dialogs/kselectdatabasedlg.cpp @@ -1,232 +1,232 @@ /*************************************************************************** kselectdatabasedlg.cpp ------------------- copyright : (C) 2005 by Tony Bloomfield (C) 2017 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 "kselectdatabasedlg.h" #include // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kselectdatabasedlg.h" #include "kguiutils.h" #include "storage/mymoneystoragesql.h" #include "misc/platformtools.h" KSelectDatabaseDlg::KSelectDatabaseDlg(int openMode, QUrl openURL, QWidget *) : m_widget(new Ui::KSelectDatabaseDlg()) , m_mode(openMode) , m_url(openURL) - , m_requiredFields(new kMandatoryFieldGroup(this)) + , m_requiredFields(new KMandatoryFieldGroup(this)) , m_sqliteSelected(false) { m_widget->setupUi(this); connect(m_widget->buttonBox, &QDialogButtonBox::accepted, this, &KSelectDatabaseDlg::accept); connect(m_widget->buttonBox, &QDialogButtonBox::rejected, this, &KSelectDatabaseDlg::reject); connect(m_widget->buttonBox->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &KSelectDatabaseDlg::slotHelp); m_requiredFields->setOkButton(m_widget->buttonBox->button(QDialogButtonBox::Ok)); m_widget->checkPreLoad->setEnabled(openMode == QIODevice::ReadWrite); } KSelectDatabaseDlg::~KSelectDatabaseDlg() { } bool KSelectDatabaseDlg::checkDrivers() { QString driverName; if (m_url != QUrl()) { driverName = QUrlQuery(m_url).queryItemValue("driver"); } // list drivers supported by KMM QMap map = MyMoneyDbDriver::driverMap(); // list drivers installed on system QStringList list = QSqlDatabase::drivers(); // clear out the current list of drivers while(m_widget->databaseTypeCombo->count()) { m_widget->databaseTypeCombo->removeItem(0); } // join the two QStringList::Iterator it = list.begin(); bool driverSupported = false; while (it != list.end()) { QString dname = *it; if (map.keys().contains(dname)) { // only keep if driver is supported m_widget->databaseTypeCombo->addItem(map[dname], dname); if (driverName == dname) { driverSupported = true; } } it++; } if (!driverName.isEmpty() && !driverSupported) { KMessageBox::error(0, i18n("Qt SQL driver %1 is no longer installed on your system", driverName), ""); return false; } if (m_widget->databaseTypeCombo->count() == 0) { // why does KMessageBox not have a standard dialog with Help button? if ((KMessageBox::questionYesNo(this, i18n("In order to use a database, you need to install some additional software. Click Help for more information"), i18n("No Qt SQL Drivers"), KStandardGuiItem::help(), KStandardGuiItem::cancel())) == KMessageBox::Yes) { // Yes stands in for help here KHelpClient::invokeHelp("details.database.usage"); } return false; } return true; } int KSelectDatabaseDlg::exec() { m_requiredFields->removeAll(); if (m_url == QUrl()) { m_widget->textDbName->setText(QLatin1String("KMyMoney")); m_widget->textHostName->setText(QLatin1String("localhost")); m_widget->textUserName->setText(QString()); m_widget->textUserName->setText(platformTools::osUsername()); m_widget->textPassword->setText(QString()); connect(m_widget->databaseTypeCombo, static_cast(&QComboBox::currentIndexChanged), this, &KSelectDatabaseDlg::slotDriverSelected); m_widget->checkPreLoad->setChecked(false); // ensure a driver gets selected; pre-select the first one if (m_widget->databaseTypeCombo->count() != 0) { m_widget->databaseTypeCombo->setCurrentIndex(0); slotDriverSelected(0); } } else { // fill in the fixed data from the URL QString driverName = QUrlQuery(m_url).queryItemValue("driver"); int idx = m_widget->databaseTypeCombo->findData(driverName); m_widget->databaseTypeCombo->setCurrentIndex(idx); QString dbName = m_url.path().right(m_url.path().length() - 1); // remove separator slash m_widget->textDbName->setText(dbName); m_widget->textHostName->setText(m_url.host()); m_widget->textUserName->setText(m_url.userName()); // disable all but the password field, coz that's why we're here m_widget->textDbName->setEnabled(false); m_widget->urlSqlite->setEnabled(false); m_widget->databaseTypeCombo->setEnabled(false); m_widget->textHostName->setEnabled(false); m_widget->textUserName->setEnabled(false); m_widget->textPassword->setEnabled(true); m_widget->textPassword->setFocus(); // set password required m_requiredFields->add(m_widget->textPassword); m_widget->checkPreLoad->setChecked(false); m_sqliteSelected = !m_widget->urlSqlite->text().isEmpty(); } return QDialog::exec(); } const QUrl KSelectDatabaseDlg::selectedURL() { QUrl url; url.setScheme("sql"); url.setUserName(m_widget->textUserName->text()); url.setPassword(m_widget->textPassword->text()); url.setHost(m_widget->textHostName->text()); if (m_sqliteSelected) url.setPath('/' + m_widget->urlSqlite->url().path()); else url.setPath('/' + m_widget->textDbName->text()); QString qs = QString("driver=%1") .arg(m_widget->databaseTypeCombo->currentData().toString()); if (m_widget->checkPreLoad->isChecked()) qs.append("&options=loadAll"); if (!m_widget->textPassword->text().isEmpty()) qs.append("&secure=yes"); url.setQuery(qs); return (url); } void KSelectDatabaseDlg::slotDriverSelected(int idx) { QExplicitlySharedDataPointer dbDriver = MyMoneyDbDriver::create(m_widget->databaseTypeCombo->itemData(idx).toString()); if (!dbDriver->isTested()) { int rc = KMessageBox::warningContinueCancel(0, i18n("Database type %1 has not been fully tested in a KMyMoney environment.\n" "Please make sure you have adequate backups of your data.\n" "Please report any problems to the developer mailing list at " "kmymoney-devel@kde.org", m_widget->databaseTypeCombo->currentText()), ""); if (rc == KMessageBox::Cancel) { return; } } m_requiredFields->removeAll(); if (dbDriver->requiresExternalFile()) { // currently, only sqlite requres an external file m_sqliteSelected = true; if (m_mode == QIODevice::WriteOnly) { m_widget->urlSqlite->setMode(KFile::Mode::File); } else { m_widget->urlSqlite->setMode(KFile::Mode::File | KFile::Mode::ExistingOnly); } m_widget->textDbName->setEnabled(false); m_widget->urlSqlite->setEnabled(true); // sqlite databases do not react to host/user/password; // file system permissions must be used m_widget->textHostName->setEnabled(false); m_widget->textUserName->setEnabled(false); // setup required fields last because the required widgets must be enabled m_requiredFields->add(m_widget->urlSqlite); } else { // not sqlite3 m_sqliteSelected = false; m_widget->textDbName->setEnabled(true); m_widget->urlSqlite->setEnabled(false); m_widget->textUserName->setEnabled(true); m_widget->textHostName->setEnabled(true); // setup required fields last because the required widgets must be enabled m_requiredFields->add(m_widget->textDbName); m_requiredFields->add(m_widget->textHostName); m_requiredFields->add(m_widget->textUserName); } m_widget->textPassword->setEnabled(dbDriver->isPasswordSupported()); } void KSelectDatabaseDlg::slotHelp() { KHelpClient::invokeHelp("details.database.selectdatabase"); } diff --git a/kmymoney/dialogs/kselectdatabasedlg.h b/kmymoney/dialogs/kselectdatabasedlg.h index 70fcdbc24..76bee3cfa 100644 --- a/kmymoney/dialogs/kselectdatabasedlg.h +++ b/kmymoney/dialogs/kselectdatabasedlg.h @@ -1,74 +1,74 @@ /*************************************************************************** kselectdatabase.h ------------------- copyright : (C) 2005 by Tony Bloomfield (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 KSELECTDATABASEDLG_H #define KSELECTDATABASEDLG_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes namespace Ui { class KSelectDatabaseDlg; } -class kMandatoryFieldGroup; +class KMandatoryFieldGroup; class KSelectDatabaseDlg : public QDialog { Q_OBJECT public: explicit KSelectDatabaseDlg(int openMode, QUrl openURL = QUrl(), QWidget *parent = nullptr); ~KSelectDatabaseDlg(); /** * Check whether we have required database drivers * @return - false, no drivers available, true, can proceed */ bool checkDrivers(); /** * Return URL of database * @return - pseudo-URL of database selected by user */ const QUrl selectedURL(); /** * Execute the database selection dialog * @return - as QDialog::exec() */ int exec() override; public slots: void slotDriverSelected(int idx); void slotHelp(); private: Ui::KSelectDatabaseDlg* m_widget; int m_mode; QUrl m_url; - kMandatoryFieldGroup* m_requiredFields; + KMandatoryFieldGroup* m_requiredFields; bool m_sqliteSelected; }; #endif diff --git a/kmymoney/dialogs/kselecttransactionsdlg.cpp b/kmymoney/dialogs/kselecttransactionsdlg.cpp index ac94e5df0..7ca39d435 100644 --- a/kmymoney/dialogs/kselecttransactionsdlg.cpp +++ b/kmymoney/dialogs/kselecttransactionsdlg.cpp @@ -1,202 +1,205 @@ /*************************************************************************** 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 "selectedtransaction.h" +#include "selectedtransactions.h" +#include "mymoneysplit.h" #include "mymoneytransaction.h" +#include "transaction.h" #include "kmymoneyglobalsettings.h" +#include "widgetenums.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); + d->ui->m_register->resize((int)eWidgets::eTransaction::Column::Detail, 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); + d->ui->m_register->resize((int)eWidgets::eTransaction::Column::Detail, 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 8db113738..a1220554d 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 -
transactionsortoptionimpl.h
+
transactionsortoption.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/ksplittransactiondlg.ui b/kmymoney/dialogs/ksplittransactiondlg.ui index a105ebd81..067aa31f9 100644 --- a/kmymoney/dialogs/ksplittransactiondlg.ui +++ b/kmymoney/dialogs/ksplittransactiondlg.ui @@ -1,364 +1,364 @@ KSplitTransactionDlg 0 0 656 408 Split transaction true 6 0 0 0 0 6 0 0 0 0 ArrowCursor 0 0 QFrame::StyledPanel QFrame::Raised 6 1 1 1 1 Qt::Horizontal QSizePolicy::Expanding 0 16 0 0 0 0 1 0 0 120 15 <b>11,00<b> Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 0 0 120 15 <b>111,00<b> Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 75 true Unassigned Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 75 true Sum of splits Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 0 0 120 15 100,00 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 150 0 75 true Transaction amount Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false QFrame::HLine QFrame::Sunken 0 0 15 0 32767 80 QFrame::NoFrame QFrame::Raised Qt::Vertical QDialogButtonBox::Cancel|QDialogButtonBox::Ok - kMyMoneySplitTable + KMyMoneySplitTable QWidget
../dialogs/kmymoneysplittable.h
buttonBox accepted() KSplitTransactionDlg accept() 607 203 327 203 buttonBox rejected() KSplitTransactionDlg reject() 607 203 327 203
diff --git a/kmymoney/dialogs/ktagreassigndlg.cpp b/kmymoney/dialogs/ktagreassigndlg.cpp index 283c038af..8eb4f1ff3 100644 --- a/kmymoney/dialogs/ktagreassigndlg.cpp +++ b/kmymoney/dialogs/ktagreassigndlg.cpp @@ -1,79 +1,79 @@ /*************************************************************************** ktagreassigndlg.cpp ------------------- copyright : (C) 2011 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 "ktagreassigndlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_ktagreassigndlg.h" #include KTagReassignDlg::KTagReassignDlg(QWidget* parent) : QDialog(parent), ui(new Ui::KTagReassignDlg) { ui->setupUi(this); - auto mandatory = new kMandatoryFieldGroup(this); + auto mandatory = new KMandatoryFieldGroup(this); mandatory->add(ui->tagCombo); mandatory->setOkButton(ui->buttonBox->button(QDialogButtonBox::Ok)); } KTagReassignDlg::~KTagReassignDlg() { delete ui; } QString KTagReassignDlg::show(const QList& tagslist) { if (tagslist.isEmpty()) return QString(); // no tag available? nothing can be selected... ui->tagCombo->loadTags(tagslist); // execute dialog and if aborted, return empty string if (this->exec() == QDialog::Rejected) return QString(); // otherwise return index of selected tag return ui->tagCombo->selectedItem(); } void KTagReassignDlg::accept() { // force update of ui->tagCombo ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); if (ui->tagCombo->selectedItem().isEmpty()) { KMessageBox::information(this, i18n("This dialog does not allow new tags to be created. Please pick a tag from the list."), i18n("Tag creation")); } else { QDialog::accept(); } } diff --git a/kmymoney/dialogs/ktagreassigndlg.ui b/kmymoney/dialogs/ktagreassigndlg.ui index 8d25aeeb9..89d0a6917 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 -
kmymoneymvccombo.h
+
kmymoneytagcombo.h
1
buttonBox accepted() KTagReassignDlg accept() 313 286 76 308 buttonBox rejected() KTagReassignDlg reject() 462 291 392 308
diff --git a/kmymoney/dialogs/kupdatestockpricedlg.ui b/kmymoney/dialogs/kupdatestockpricedlg.ui index 5c1e6fa5b..71e7e1a1d 100644 --- a/kmymoney/dialogs/kupdatestockpricedlg.ui +++ b/kmymoney/dialogs/kupdatestockpricedlg.ui @@ -1,224 +1,224 @@ KUpdateStockPriceDlg 0 0 457 194 New price entry - + Qt::Horizontal QSizePolicy::Expanding 71 21 0 0 Qt::Horizontal QSizePolicy::Expanding 40 20 Currency false Date: false Security false 0 0 Qt::Horizontal QSizePolicy::Expanding 40 20 Qt::Vertical QSizePolicy::Expanding 31 40 QFrame::HLine QFrame::Sunken QDialogButtonBox::Cancel|QDialogButtonBox::Ok KComboBox QComboBox
kcombobox.h
- kMyMoneyDateInput + KMyMoneyDateInput QWidget
kmymoneydateinput.h
1
KMyMoneySecuritySelector KComboBox
kmymoneycurrencyselector.h
1
m_buttonBox accepted() KUpdateStockPriceDlg accept() 228 170 228 96 m_buttonBox rejected() KUpdateStockPriceDlg reject() 228 170 228 96
diff --git a/kmymoney/dialogs/settings/ksettingsgeneral.cpp b/kmymoney/dialogs/settings/ksettingsgeneral.cpp index 7c0eb2373..dd7a40c5d 100644 --- a/kmymoney/dialogs/settings/ksettingsgeneral.cpp +++ b/kmymoney/dialogs/settings/ksettingsgeneral.cpp @@ -1,143 +1,143 @@ /*************************************************************************** ksettingsgeneral.cpp -------------------- copyright : (C) 2005 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 "ksettingsgeneral.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "ui_ksettingsgeneral.h" #include "kmymoneydateinput.h" #include "models.h" #include "accountsmodel.h" #include "mymoneymoney.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" class KSettingsGeneralPrivate { Q_DISABLE_COPY(KSettingsGeneralPrivate) public: KSettingsGeneralPrivate() : ui(new Ui::KSettingsGeneral) { } ~KSettingsGeneralPrivate() { delete ui; } Ui::KSettingsGeneral *ui; bool initialHideZeroBalanceEquities; }; KSettingsGeneral::KSettingsGeneral(QWidget* parent) : QWidget(parent), d_ptr(new KSettingsGeneralPrivate) { Q_D(KSettingsGeneral); d->ui->setupUi(this); // hide the internally used date field d->ui->kcfg_StartDate->hide(); // setup connections, so that the sort optios get loaded once the edit fields are filled connect(d->ui->kcfg_StartDate, &QDateTimeEdit::dateChanged, this, &KSettingsGeneral::slotLoadStartDate); // setup connections, so that changes by the user are forwarded to the (hidden) edit fields - connect(d->ui->m_startDateEdit, &kMyMoneyDateInput::dateChanged, d->ui->kcfg_StartDate, &QDateTimeEdit::setDate); + connect(d->ui->m_startDateEdit, &KMyMoneyDateInput::dateChanged, d->ui->kcfg_StartDate, &QDateTimeEdit::setDate); connect(d->ui->choosePath, &QAbstractButton::pressed, this, &KSettingsGeneral::slotChooseLogPath); d->initialHideZeroBalanceEquities = d->ui->kcfg_HideZeroBalanceEquities->isChecked(); } KSettingsGeneral::~KSettingsGeneral() { Q_D(KSettingsGeneral); delete d; } void KSettingsGeneral::slotChooseLogPath() { Q_D(KSettingsGeneral); QString filePath = QFileDialog::getExistingDirectory(this, i18n("Choose file path"), QDir::homePath()); d->ui->kcfg_logPath->setText(filePath); slotUpdateLogTypes(); } void KSettingsGeneral::slotLoadStartDate(const QDate&) { Q_D(KSettingsGeneral); // only need this once disconnect(d->ui->kcfg_StartDate, &QDateTimeEdit::dateChanged, this, &KSettingsGeneral::slotLoadStartDate); d->ui->m_startDateEdit->setDate(d->ui->kcfg_StartDate->date()); } void KSettingsGeneral::slotUpdateLogTypes() { Q_D(KSettingsGeneral); bool enable = d->ui->kcfg_logPath->text().isEmpty() ? false : true; d->ui->kcfg_logImportedStatements->setEnabled(enable); d->ui->kcfg_logOfxTransactions->setEnabled(enable); if (!enable) { d->ui->kcfg_logImportedStatements->setChecked(enable); d->ui->kcfg_logOfxTransactions->setChecked(enable); } } void KSettingsGeneral::showEvent(QShowEvent *event) { Q_UNUSED(event) QWidget::showEvent(event); slotUpdateLogTypes(); } void KSettingsGeneral::slotUpdateEquitiesVisibility() { Q_D(KSettingsGeneral); if (d->initialHideZeroBalanceEquities == d->ui->kcfg_HideZeroBalanceEquities->isChecked()) // setting hasn't been changed, so return return; d->initialHideZeroBalanceEquities = d->ui->kcfg_HideZeroBalanceEquities->isChecked(); AccountsModel* accountsModel = Models::instance()->accountsModel(); // items' model for accounts' page InstitutionsModel* institutionsModel = Models::instance()->institutionsModel(); // items' model for institutions' page auto file = MyMoneyFile::instance(); QList accountsList; file->accountList(accountsList); foreach (const auto account, accountsList) { if (account.isInvest() && account.balance().isZero()) { // search only for zero balance stocks if (d->initialHideZeroBalanceEquities) { accountsModel->slotObjectRemoved(eMyMoney::File::Object::Account, account.id()); // remove item from accounts' page institutionsModel->slotObjectRemoved(eMyMoney::File::Object::Account, account.id()); // remove item from institutions' page } else { accountsModel->slotObjectAdded(eMyMoney::File::Object::Account, dynamic_cast(&account)); // add item to accounts' page institutionsModel->slotObjectAdded(eMyMoney::File::Object::Account, dynamic_cast(&account)); // add item to institutions' page } } } } diff --git a/kmymoney/dialogs/settings/ksettingsgeneral.ui b/kmymoney/dialogs/settings/ksettingsgeneral.ui index cdf64c162..e71a5b04c 100644 --- a/kmymoney/dialogs/settings/ksettingsgeneral.ui +++ b/kmymoney/dialogs/settings/ksettingsgeneral.ui @@ -1,709 +1,709 @@ KSettingsGeneral 0 0 600 499 General Settings 3 Global Startup options Show splash screen Autosave options Autosave periodically false 60 minutes false Qt::Horizontal QSizePolicy::Expanding 308 22 Autosave when file is modified upon close Setup number of backups to keep (local file only) Whenever the current data is saved into a local file, KMyMoney keeps the selected number of previous states of the file. Set it to 0 to turn the feature off. true Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 1 Number of backups to keep (0=off) Qt::Horizontal 40 20 Fiscal Year Your fiscal year starts on false 366 January February March April May June July August September October November December Qt::Horizontal QSizePolicy::Expanding 282 17 External programs Calculator Qt::Vertical QSizePolicy::Expanding 20 20 Views Startup page options Start with ho&mepage true Start with last selected view Type of the KMyMoney view List true Tree Tabbed Show title bar on each page Synchronize account selection of ledger and investment view Qt::Vertical QSizePolicy::Expanding 20 319 Filter Accounts / Categories This option hides all categories in the categories view that are not used in at least a single transaction. They are still shown in the category selection lists. Do not show unused categories This option hides all accounts that have been closed by the user in views and selection lists. You can use <b>View/Show all accounts</b> to temporarily show hidden accounts in the views. Do not show closed accounts Show equity accounts This option will display the categories in the accounts view also. Show categories in the accounts list view Do not show zero balance equities Schedules This option hides all finished schedules in the schedules view. Do not show finished schedules Transactions Do not show transactions prior to false - + Qt::Horizontal QSizePolicy::Expanding 63 20 This option hides all reconciled transactions in the ledger view. Do not show reconciled transactions Qt::Vertical QSizePolicy::Expanding 20 30 Support Logging Log path true 0 0 32 32 ... Qt::Horizontal 40 20 On choosing a log file path please keep in mind that log files may contain sensitive data (e.g. passwords in clear-text etc). true Log imported statements Log OFX transactions Qt::Vertical 20 255 Qt::Vertical 20 40 KComboBox QComboBox
kcombobox.h
KLineEdit QLineEdit
klineedit.h
- kMyMoneyDateInput + KMyMoneyDateInput QFrame
kmymoneydateinput.h
1
kcfg_AutoSaveFile toggled(bool) m_periodFrame setEnabled(bool) 103 274 188 263
diff --git a/kmymoney/dialogs/settings/ksettingsregister.ui b/kmymoney/dialogs/settings/ksettingsregister.ui index be5a9ad9f..8030f44ce 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 -
transactionsortoptionimpl.h
+
transactionsortoption.h
KComboBox QComboBox
kcombobox.h
QTabWidget QTabWidget
ktabwidget.h
diff --git a/kmymoney/dialogs/stdtransactioneditor.cpp b/kmymoney/dialogs/stdtransactioneditor.cpp index eb25b4d4e..e8d2d3e69 100644 --- a/kmymoney/dialogs/stdtransactioneditor.cpp +++ b/kmymoney/dialogs/stdtransactioneditor.cpp @@ -1,1614 +1,1624 @@ /*************************************************************************** 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 "tabbar.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 "transaction.h" #include "transactionform.h" +#include "mymoneytransactionfilter.h" #include "kmymoneyglobalsettings.h" #include "transactioneditorcontainer.h" #include "ksplittransactiondlg.h" #include "kcurrencycalculator.h" #include "kselecttransactionsdlg.h" +#include "widgetenums.h" +using namespace eWidgets; 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(0, true); + auto category = new KMyMoneyCategory(true, nullptr); 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; + auto number = new KMyMoneyLineEdit; number->setPlaceholderText(i18n("Number")); number->setObjectName(QLatin1String("Number")); d->m_editWidgets["number"] = number; - connect(number, &kMyMoneyLineEdit::lineChanged, this, &StdTransactionEditor::slotNumberChanged); + connect(number, &KMyMoneyLineEdit::lineChanged, this, &StdTransactionEditor::slotNumberChanged); // number->installEventFilter(this); } - auto postDate = new kMyMoneyDateInput; + auto postDate = new KMyMoneyDateInput; d->m_editWidgets["postdate"] = postDate; postDate->setObjectName(QLatin1String("PostDate")); - connect(postDate, &kMyMoneyDateInput::dateChanged, this, &StdTransactionEditor::slotUpdateButtonState); + connect(postDate, &KMyMoneyDateInput::dateChanged, this, &StdTransactionEditor::slotUpdateButtonState); postDate->setDate(QDate()); - auto value = new kMyMoneyEdit; + 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); + connect(value, &KMyMoneyEdit::valueChanged, this, &StdTransactionEditor::slotUpdateAmount); + connect(value, &KMyMoneyEdit::textChanged, this, &StdTransactionEditor::slotUpdateButtonState); - value = new kMyMoneyEdit; + 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); + connect(value, &KMyMoneyEdit::valueChanged, this, &StdTransactionEditor::slotUpdatePayment); + connect(value, &KMyMoneyEdit::textChanged, this, &StdTransactionEditor::slotUpdateButtonState); - value = new kMyMoneyEdit; + 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); + connect(value, &KMyMoneyEdit::valueChanged, this, &StdTransactionEditor::slotUpdateDeposit); + connect(value, &KMyMoneyEdit::textChanged, this, &StdTransactionEditor::slotUpdateButtonState); - auto cashflow = new KMyMoneyCashFlowCombo(0, d->m_account.accountGroup()); + auto cashflow = new KMyMoneyCashFlowCombo(d->m_account.accountGroup(), nullptr); 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; + auto 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); + tabbar->copyTabs(form->getTabBar()); + connect(tabbar, &KMyMoneyTransactionForm::TabBar::tabCurrentChanged, this, &StdTransactionEditor::slotUpdateAction); + connect(tabbar, &KMyMoneyTransactionForm::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) +void StdTransactionEditor::loadEditWidgets(eRegister::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()); + 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); + dynamic_cast(d->m_editWidgets["postdate"])->setDate(d->m_lastPostDate); else - dynamic_cast(d->m_editWidgets["postdate"])->setDate(QDate::currentDate()); + dynamic_cast(d->m_editWidgets["postdate"])->setDate(QDate::currentDate()); if ((w = haveWidget("number")) != 0) { - dynamic_cast(w)->loadText(d->m_split.number()); + 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 + && 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 + && action != eRegister::Action::Deposit // 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 + cashflow->setDirection(!d->m_split.value().isPositive() ? eRegister::CashFlowDirection::Payment : eRegister::CashFlowDirection::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()); + 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(""); + 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()); + 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) { + auto tabbar = dynamic_cast(haveWidget("tabbar")); + if (action == eRegister::Action::None) { if (tabbar) { - action = static_cast(tabbar->currentIndex()); + action = static_cast(tabbar->currentIndex()); } } - if (action != KMyMoneyRegister::ActionNone) { + if (action != eRegister::Action::None) { QLabel *categoryLabel = dynamic_cast(w); - if (action == KMyMoneyRegister::ActionTransfer) { + if (action == eRegister::Action::Transfer) { 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); + if (action == eRegister::Action::Deposit || (action == eRegister::Action::Transfer && d->m_split.value().isPositive())) + cashflow->setDirection(eRegister::CashFlowDirection::Deposit); else - cashflow->setDirection(KMyMoneyRegister::Payment); + cashflow->setDirection(eRegister::CashFlowDirection::Payment); } if (tabbar) { - tabbar->setCurrentIndex(action); + tabbar->setCurrentIndex((int)action); } } } } else { - TabBar* tabbar = dynamic_cast(haveWidget("tabbar")); + auto 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); + tabbar->setCurrentIndex(d->m_split.value().isNegative() ? (int)eRegister::Action::Withdrawal : (int)eRegister::Action::Deposit); } else { - tabbar->setCurrentIndex(KMyMoneyRegister::ActionTransfer); + tabbar->setCurrentIndex((int)eRegister::Action::Transfer); } } } } else { // isMultiSelection() - dynamic_cast(d->m_editWidgets["postdate"])->loadDate(QDate()); + 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(); + 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(); + dynamic_cast(w)->loadText(""); + dynamic_cast(w)->setAllowEmpty(); } - slotUpdateAction(action); + slotUpdateAction((int)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); + loadEditWidgets(eRegister::Action::None); } QWidget* StdTransactionEditor::firstWidget() const { Q_D(const StdTransactionEditor); QWidget* w = 0; - if (d->m_initialAction != KMyMoneyRegister::ActionNone) { + if (d->m_initialAction != eRegister::Action::None) { 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; + 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)); + 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"]); + 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")); + 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; + eRegister::Action action; + action = d->m_split.shares().isNegative() ? eRegister::Action::Withdrawal : eRegister::Action::Deposit; if (d->m_transaction.splitCount() == 2) { MyMoneyAccount acc = MyMoneyFile::instance()->account(otherSplit.accountId()); if (acc.isAssetLiability()) - action = KMyMoneyRegister::ActionTransfer; + action = eRegister::Action::Transfer; } // now setup the widgets with the new data but keep the date - QDate date = dynamic_cast(d->m_editWidgets["postdate"])->date(); + QDate date = dynamic_cast(d->m_editWidgets["postdate"])->date(); loadEditWidgets(action); - dynamic_cast(d->m_editWidgets["postdate"])->setDate(date); + 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")); + auto 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: + case (int)eRegister::Action::Deposit: categoryLabel->setText(i18n("Category")); - cashflow->setDirection(KMyMoneyRegister::Deposit); + cashflow->setDirection(eRegister::CashFlowDirection::Deposit); break; - case KMyMoneyRegister::ActionTransfer: + case (int)eRegister::Action::Transfer: if (d->m_split.shares().isNegative()) { - cashflow->setDirection(KMyMoneyRegister::Payment); + cashflow->setDirection(eRegister::CashFlowDirection::Payment); categoryLabel->setText(i18n("Transfer to")); } else { - cashflow->setDirection(KMyMoneyRegister::Deposit); + cashflow->setDirection(eRegister::CashFlowDirection::Deposit); categoryLabel->setText(i18n("Transfer from")); } - tabbar->setCurrentIndex(KMyMoneyRegister::ActionTransfer); + tabbar->setCurrentIndex((int)eRegister::Action::Transfer); slotUpdateCashFlow(cashflow->direction()); break; - case KMyMoneyRegister::ActionWithdrawal: + case (int)eRegister::Action::Withdrawal: categoryLabel->setText(i18n("Category")); - cashflow->setDirection(KMyMoneyRegister::Payment); + cashflow->setDirection(eRegister::CashFlowDirection::Payment); break; } resizeForm(); } } -void StdTransactionEditor::slotUpdateCashFlow(KMyMoneyRegister::CashFlowDirection dir) +void StdTransactionEditor::slotUpdateCashFlow(eRegister::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")); + auto tabbar = dynamic_cast(haveWidget("tabbar")); if (!tabbar) return; // no transaction form if (categoryLabel->text() != i18n("Category")) { - tabbar->setCurrentIndex(KMyMoneyRegister::ActionTransfer); - if (dir == KMyMoneyRegister::Deposit) { + tabbar->setCurrentIndex((int)eRegister::Action::Transfer); + if (dir == eRegister::CashFlowDirection::Deposit) { categoryLabel->setText(i18n("Transfer from")); } else { categoryLabel->setText(i18n("Transfer to")); } resizeForm(); } else { - if (dir == KMyMoneyRegister::Deposit) - tabbar->setCurrentIndex(KMyMoneyRegister::ActionDeposit); + if (dir == eRegister::CashFlowDirection::Deposit) + tabbar->setCurrentIndex((int)eRegister::Action::Deposit); else - tabbar->setCurrentIndex(KMyMoneyRegister::ActionWithdrawal); + tabbar->setCurrentIndex((int)eRegister::Action::Withdrawal); } } } 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"]); + auto 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); + tabbar->setTabEnabled((int)eRegister::Action::Transfer, true); + tabbar->setTabEnabled((int)eRegister::Action::Deposit, true); + tabbar->setTabEnabled((int)eRegister::Action::Withdrawal, 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); + tabbar->setCurrentIndex((int)eRegister::Action::Transfer); + tabbar->setTabEnabled((int)eRegister::Action::Deposit, false); + tabbar->setTabEnabled((int)eRegister::Action::Withdrawal, false); } KMyMoneyCashFlowCombo* cashflow = dynamic_cast(d->m_editWidgets["cashflow"]); if (val.isZero()) { - if (cashflow && (cashflow->direction() == KMyMoneyRegister::Deposit)) { + if (cashflow && (cashflow->direction() == eRegister::CashFlowDirection::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); + cashflow->setDirection(eRegister::CashFlowDirection::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); + if (tabbar->currentIndex() == (int)eRegister::Action::Transfer) { + tabbar->setCurrentIndex(val.isPositive() ? (int)eRegister::Action::Withdrawal : (int)eRegister::Action::Deposit); } - tabbar->setTabEnabled(KMyMoneyRegister::ActionTransfer, false); + tabbar->setTabEnabled((int)eRegister::Action::Transfer, 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(); + dynamic_cast(d->m_editWidgets["deposit"])->setValue(val.abs()); + dynamic_cast(d->m_editWidgets["payment"])->clearText(); } else { - dynamic_cast(d->m_editWidgets["deposit"])->clearText(); + 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(); + dynamic_cast(d->m_editWidgets["payment"])->setValue(val.abs()); + dynamic_cast(d->m_editWidgets["deposit"])->clearText(); } else { - dynamic_cast(d->m_editWidgets["payment"])->clearText(); + 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) { + if (cashflow->direction() == eRegister::CashFlowDirection::Payment) { categoryLabel->setText(i18n("Transfer to")); } } else { slotUpdateCashFlow(cashflow->direction()); } - dynamic_cast(d->m_editWidgets["amount"])->setValue(val.abs()); + dynamic_cast(d->m_editWidgets["amount"])->setValue(val.abs()); } else { if (categoryLabel->text() != i18n("Category")) { - if (cashflow->direction() == KMyMoneyRegister::Payment) { + if (cashflow->direction() == eRegister::CashFlowDirection::Payment) { categoryLabel->setText(i18n("Transfer to")); } else { categoryLabel->setText(i18n("Transfer from")); - cashflow->setDirection(KMyMoneyRegister::Deposit); // editing with +ve shows 'from' not 'pay to' + cashflow->setDirection(eRegister::CashFlowDirection::Deposit); // editing with +ve shows 'from' not 'pay to' } } - dynamic_cast(d->m_editWidgets["amount"])->setValue(val.abs()); + 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"]); + 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); + 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")); + auto 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) + if (cashflow && cashflow->direction() != eRegister::CashFlowDirection::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) + if (cashflow->direction() == eRegister::CashFlowDirection::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"]); + 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")); + 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; + eRegister::CashFlowDirection dir = eRegister::CashFlowDirection::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; + dir = eRegister::CashFlowDirection::Deposit; } } if (payment) { if (payment->lineedit()->text().length() != 0) { isValidAmount = true; - dir = KMyMoneyRegister::Payment; + dir = eRegister::CashFlowDirection::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; + if (dir == eRegister::CashFlowDirection::Unknown) + dir = eRegister::CashFlowDirection::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, + dir == eRegister::CashFlowDirection::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"]); + auto amount = dynamic_cast(d->m_editWidgets["amount"]); // if both fields do not contain changes -> no need to update - if (cashflow->direction() != KMyMoneyRegister::Unknown + if (cashflow->direction() != eRegister::CashFlowDirection::Unknown && !amount->lineedit()->text().isEmpty()) updateValue = true; value = amount->value(); - if (cashflow->direction() == KMyMoneyRegister::Payment) + if (cashflow->direction() == eRegister::CashFlowDirection::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"]); + 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"]); + 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")); + 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/stdtransactioneditor.h b/kmymoney/dialogs/stdtransactioneditor.h index 7bcc1ffd2..cde3ce83f 100644 --- a/kmymoney/dialogs/stdtransactioneditor.h +++ b/kmymoney/dialogs/stdtransactioneditor.h @@ -1,162 +1,162 @@ /*************************************************************************** stdtransactioneditor.h ---------- 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. * * * ***************************************************************************/ #ifndef STDTRANSACTIONEDITOR_H #define STDTRANSACTIONEDITOR_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "transactioneditor.h" class MyMoneyMoney; -namespace KMyMoneyRegister { enum CashFlowDirection : int; } +namespace eWidgets { namespace eRegister { enum class CashFlowDirection; } } class StdTransactionEditorPrivate; class StdTransactionEditor : public TransactionEditor { Q_OBJECT public: StdTransactionEditor(); explicit StdTransactionEditor(TransactionEditorContainer* regForm, KMyMoneyRegister::Transaction* item, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate); ~StdTransactionEditor() override; bool isComplete(QString& reason) const override; QWidget* firstWidget() const override; /** * This method creates a transaction based on the contents of the current widgets, * the splits in m_split in single selection mode or an existing transaction/split * and the contents of the widgets in multi selection mode. * * The split referencing the current account is returned as the first split in the * transaction's split list. * * @param t reference to created transaction * @param torig the original transaction * @param sorig the original split * @param skipPriceDialog if @p true the user will not be requested for price information * (defaults to @p false) * * @return @p false if aborted by user, @p true otherwise * * @note Usually not used directly. If unsure, use enterTransactions() instead. */ bool createTransaction(MyMoneyTransaction& t, const MyMoneyTransaction& torig, const MyMoneySplit& sorig, bool skipPriceDialog = false) override; public slots: int slotEditSplits() override; void slotUpdateAmount(const QString&); protected slots: void slotReloadEditWidgets(); void slotUpdatePayment(const QString&); void slotUpdateDeposit(const QString&); void slotUpdateCategory(const QString&); void slotUpdatePayee(const QString&); //void slotUpdateTag(const QString&); - void slotUpdateCashFlow(KMyMoneyRegister::CashFlowDirection); + void slotUpdateCashFlow(eWidgets::eRegister::CashFlowDirection); void slotCreateCategory(const QString&, QString&); void slotUpdateAction(int action); void slotUpdateAccount(const QString& id) override; protected: /** * This method creates all necessary widgets for this transaction editor. * All signals will be connected to the relevant slots. */ void createEditWidgets() override; /** * This method (re-)loads the widgets with the transaction information * contained in @a m_transaction and @a m_split. * * @param action preset the edit wigdets for @a action if no transaction * is present */ - void loadEditWidgets(KMyMoneyRegister::Action action) override; + void loadEditWidgets(eWidgets::eRegister::Action action) override; void loadEditWidgets() override; void setupCategoryWidget(QString&); void updateAmount(const MyMoneyMoney& value); bool isTransfer(const QString& accId1, const QString& accId2) const; void checkPayeeInSplit(MyMoneySplit& s, const QString& payeeId); /** * This method fills the editor widgets with the last transaction * that can be found for payee @a payeeId in the account @a m_account. */ void autoFill(const QString& payeeId); /** * Extracts the amount of the transaction from the widgets depending * if form or register based input method is used. * Returns if an amount has been found in @a update. * * @param update pointer to update information flag * @return amount of transaction (deposit positive, payment negative) */ MyMoneyMoney amountFromWidget(bool* update = 0) const; /** * Create or update a VAT split */ void updateVAT(bool amountChanged = true); MyMoneyMoney removeVatSplit(); /** * This method adds a VAT split to transaction @a tr if necessary. * * @param tr transaction that the split should be added to * @param amount Amount to be used for the calculation. Depending upon the * setting of the resp. category, this value is treated as * either gross or net value. * @retval false VAT split has not been added * @retval true VAT split has been added */ bool addVatSplit(MyMoneyTransaction& tr, const MyMoneyMoney& amount); void setupFinalWidgets() override; /** * This method returns the sum of all splits of transaction @a t that * reference account m_account. */ MyMoneyMoney shares(const MyMoneyTransaction& t) const; private: Q_DECLARE_PRIVATE(StdTransactionEditor) }; #endif diff --git a/kmymoney/dialogs/transactioneditor.cpp b/kmymoney/dialogs/transactioneditor.cpp index ef85fb9a7..6d4849586 100644 --- a/kmymoney/dialogs/transactioneditor.cpp +++ b/kmymoney/dialogs/transactioneditor.cpp @@ -1,825 +1,830 @@ /*************************************************************************** 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 "tabbar.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 "transaction.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_initialAction = eWidgets::eRegister::Action::None; 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); + dynamic_cast(w)->setPrecision(prec); } } } -void TransactionEditor::setup(QWidgetList& tabOrderWidgets, const MyMoneyAccount& account, KMyMoneyRegister::Action action) +void TransactionEditor::setup(QWidgetList& tabOrderWidgets, const MyMoneyAccount& account, eWidgets::eRegister::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()); + auto tabbar = dynamic_cast(w); + if ((tabbar) && (action == eWidgets::eRegister::Action::None)) { + 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); + setup(tabOrderWidgets, account, eWidgets::eRegister::Action::None); } void TransactionEditor::setup(QWidgetList& tabOrderWidgets) { - setup(tabOrderWidgets, MyMoneyAccount(), KMyMoneyRegister::ActionNone); + setup(tabOrderWidgets, MyMoneyAccount(), eWidgets::eRegister::Action::None); } 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 + // 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()); + 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")); + 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")); + 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")); + 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); + KMyMoneyRegister::SelectedTransaction st(t, s, QString()); 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)); + QMetaObject::invokeMethod(form, "resize", Qt::QueuedConnection, QGenericReturnArgument(), Q_ARG(int, (int)eWidgets::eTransactionForm::Column::Value1)); } } diff --git a/kmymoney/dialogs/transactioneditor.h b/kmymoney/dialogs/transactioneditor.h index b65c43c32..658a147ab 100644 --- a/kmymoney/dialogs/transactioneditor.h +++ b/kmymoney/dialogs/transactioneditor.h @@ -1,327 +1,328 @@ /*************************************************************************** transactioneditor.h ---------- 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. * * * ***************************************************************************/ #ifndef TRANSACTIONEDITOR_H #define TRANSACTIONEDITOR_H // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes class QDate; class TransactionEditorContainer; class KMyMoneyCategory; class MyMoneySplit; class MyMoneyTransaction; class MyMoneyAccount; template class QList; +namespace eWidgets { namespace eRegister { enum class Action; } } + namespace KMyMoneyRegister { - enum Action : int; class SelectedTransactions; class Transaction; } namespace eMyMoney { namespace Schedule { enum class Occurrence; enum class PaymentType; } namespace Split { enum class InvestmentTransactionType; } } class TransactionEditorPrivate; class TransactionEditor : public QObject { Q_OBJECT Q_DISABLE_COPY(TransactionEditor) public: TransactionEditor(); explicit TransactionEditor(TransactionEditorPrivate &dd, TransactionEditorContainer* regForm, KMyMoneyRegister::Transaction* item, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate); virtual ~TransactionEditor(); /** * This method is used as a helper because virtual methods cannot be * called within a constructor. Thus setup() should be called immediately * after a TransactionEditor() object or one of its derivatives is * constructed. The parameter @a account identifies the account that * is currently opened in the calling ledger view. * * This account will not be included in category sets. The default is * no account so all will be shown. I have no idea anymore, what I * tried to say with the first sentence above. :( Maybe this is crap. * * @param tabOrderWidgets QWidgetList which will be filled with the pointers * to the editWidgets in their tab order * @param account account that is currently shown in the calling ledger view * @param action default action (defaults to ActionNone). */ - void setup(QWidgetList& tabOrderWidgets, const MyMoneyAccount& account, KMyMoneyRegister::Action action); + void setup(QWidgetList& tabOrderWidgets, const MyMoneyAccount& account, eWidgets::eRegister::Action action); void setup(QWidgetList& tabOrderWidgets, const MyMoneyAccount& account); void setup(QWidgetList& tabOrderWidgets); /** * Enter the transactions into the ledger. In case of a newly created * transaction @a newId contains the assigned id. In case @a askForSchedule * is true (the default), the user will be asked if he wants to enter new * transactions with a post date in the future into the ledger or rather * create a schedule for them. In case @a suppressBalanceWarnings is @p false * (the default) a warning will be displayed when the balance crosses the minimum * or maximum balance settings for the account. */ virtual bool enterTransactions(QString& newId, bool askForSchedule = true, bool suppressBalanceWarnings = false); /** * This method creates a transaction based on the contents of the current widgets, * the splits in m_split in single selection mode or an existing transaction/split * and the contents of the widgets in multi selection mode. * * The split referencing the current account is returned as the first split in the * transaction's split list. * * @param t reference to created transaction * @param torig the original transaction * @param sorig the original split * @param skipPriceDialog if @p true the user will not be requested for price information * (defaults to @p false) * * @return @p false if aborted by user, @p true otherwise * * @note Usually not used directly. If unsure, use enterTransactions() instead. */ virtual bool createTransaction(MyMoneyTransaction& t, const MyMoneyTransaction& torig, const MyMoneySplit& sorig, bool skipPriceDialog = false) = 0; /** * This method returns information about the completeness of the data * entered. This can be used to control the availability of the * 'Enter transaction' action. * * @retval true if entering the transaction into the engine * @retval false if not enough information is present to enter the * transaction into the engine * * @param reason will be filled with a string about the reason why the * completeness is not reached. Empty if the return value * is @c true. * * @sa transactionDataSufficient() */ virtual bool isComplete(QString& reason) const = 0; /** * This method returns information if the editor is started with multiple transactions * being selected or not. * * @retval false only a single transaction was selected when the editor was started * @retval true multiple transactions were selected when the editor was started */ virtual bool isMultiSelection() const; virtual bool fixTransactionCommodity(const MyMoneyAccount& account); virtual bool canAssignNumber() const; virtual void assignNextNumber(); /** * Returns a pointer to the widget that should receive * the focus after the editor has been started. */ virtual QWidget* firstWidget() const = 0; /** * Returns a pointer to a widget by name */ QWidget* haveWidget(const QString& name) const; void setTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s); bool eventFilter(QObject* o, QEvent* e) override; MyMoneyAccount account() const; void clearFinalWidgets(); void addFinalWidget(const QWidget*); void setScheduleInfo(const QString& si); void setPaymentMethod(eMyMoney::Schedule::PaymentType pm); public slots: void slotReloadEditWidgets(); /** * The default implementation returns QDialog::Rejected */ virtual int slotEditSplits(); /** * Modify the account which the transaction should be based on. The * initial value for the account is passed during setup(). * * @param id of the account to be used */ virtual void slotUpdateAccount(const QString& id); protected: virtual void createEditWidgets() = 0; virtual void setupFinalWidgets() = 0; - virtual void loadEditWidgets(KMyMoneyRegister::Action action) = 0; + virtual void loadEditWidgets(eWidgets::eRegister::Action action) = 0; virtual void loadEditWidgets() = 0; void setupCategoryWidget(KMyMoneyCategory* category, const QList& splits, QString& categoryId, const char* splitEditSlot, bool allowObjectCreation = true); void resizeForm(); /** * This method sets the precision of the value widgets to reflect * the account in m_account. If m_account has no id, the precision * defaults to 2. */ void setupPrecision(); protected slots: void slotUpdateButtonState(); void slotUpdateMemoState(); void slotUpdateAccount(); void slotNumberChanged(const QString&); signals: /** * This signal is sent out by the destructor to inform other entities * that editing has been finished. The parameter @a t contains the list * of transactions that were processed. */ void finishEdit(const KMyMoneyRegister::SelectedTransactions& t); /** * This signal is sent out whenever enough data is present to enter the * transaction into the ledger. This signal can be used to control the * KAction which implements entering the transaction. * * @sa isComplete() * * @param state @a true if enough data is present, @a false otherwise. */ void transactionDataSufficient(bool state); /** * 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 */ bool createPayee(const QString& txt, QString& id); /** * This signal is sent out, when a new category needs to be created * Depending on the setting of either a payment or deposit, the parent * account will be preset to Expense or Income. * * @param account reference to account info. Will be filled by called slot * @param parent reference to parent account */ void createCategory(MyMoneyAccount& account, const MyMoneyAccount& parent); /** * This signal is sent out, when a new tag needs to be created * @param txt The name of the tag to be created * @param id A connected slot should store the id of the created object in this variable */ void createTag(const QString& txt, QString& id); /** * This signal is sent out, when a new security (e.g. stock )needs to be created * @a Parent should be the investment account under which the security account * will be created. * * @param account reference to account info. Will be filled by called slot * @param parent reference to parent account */ void createSecurity(MyMoneyAccount& account, const MyMoneyAccount& parent); /** * Signal is emitted, if any of the widgets enters (@a state equals @a true) * or leaves (@a state equals @a false) object creation mode. * * @param state Enter (@a true) or leave (@a false) object creation */ void objectCreation(bool state); void statusMsg(const QString& txt); void statusProgress(int cnt, int base); /** * This signal is sent out for each newly added transaction * * @param date the post date of the newly created transaction */ void lastPostDateUsed(const QDate& date); /** * This signal is sent out, if the user decides to schedule the transaction @a t * rather then adding it to the ledger right away. */ void scheduleTransaction(const MyMoneyTransaction& t, eMyMoney::Schedule::Occurrence occurrence); /** * This signal is sent out, if the user double clicks the number field */ void assignNumber(); /** * This signal is sent out, if the user has pressed the ESC key. */ void escapePressed(int msec = 100); /** * This signal is sent out, if the user has pressed the Return or Enter * key and asks to end editing the transaction */ void returnPressed(int msec = 100); /** * This signal is sent out, if any of the balance warning levels * for @p account has been reached. @p msg contains the message text. * @p parent points to the parent widget to be used for the warning message box. */ void balanceWarning(QWidget* parent, const MyMoneyAccount& account, const QString& msg); void operationTypeChanged(int index); protected: QScopedPointer d_ptr; TransactionEditor(TransactionEditorPrivate &dd); private: Q_DECLARE_PRIVATE(TransactionEditor) }; #endif diff --git a/kmymoney/dialogs/transactioneditor_p.h b/kmymoney/dialogs/transactioneditor_p.h index 3c6d35500..83c2b4e2f 100644 --- a/kmymoney/dialogs/transactioneditor_p.h +++ b/kmymoney/dialogs/transactioneditor_p.h @@ -1,124 +1,126 @@ /*************************************************************************** transactioneditor_p.h ---------- 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. * * * ***************************************************************************/ #ifndef TRANSACTIONEDITOR_P_H #define TRANSACTIONEDITOR_P_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneylineedit.h" #include "kmymoneyutils.h" #include "mymoneyaccount.h" #include "mymoneyenums.h" #include "mymoneyfile.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "register.h" #include "registeritem.h" -#include "selectedtransaction.h" +#include "selectedtransactions.h" #include "transactioneditor.h" +#include "qwidgetcontainer.h" +#include "widgetenums.h" class MyMoneyMoney; class TransactionEditorContainer; namespace KMyMoneyRegister { class Transaction; } class TransactionEditorPrivate { Q_DISABLE_COPY(TransactionEditorPrivate) Q_DECLARE_PUBLIC(TransactionEditor) public: TransactionEditorPrivate(TransactionEditor *qq) : q_ptr(qq) { } ~TransactionEditorPrivate() { } void init() { m_paymentMethod = eMyMoney::Schedule::PaymentType::Any; m_regForm = 0; m_item = 0; - m_initialAction = KMyMoneyRegister::ActionNone; + m_initialAction = eWidgets::eRegister::Action::None; m_openEditSplits = false; m_memoChanged = false; } /** * If a new or an edited transaction has a valid number, keep it with the account */ void keepNewNumber(const MyMoneyTransaction& tr) { Q_Q(TransactionEditor); // verify that new number, possibly containing alpha, is valid auto txn = tr; auto file = MyMoneyFile::instance(); if (!txn.splits().isEmpty()) { QString number = txn.splits().first().number(); if (KMyMoneyUtils::numericPart(number) > 0) { // numeric is valid - auto numberEdit = dynamic_cast(q->haveWidget("number")); + auto numberEdit = dynamic_cast(q->haveWidget("number")); if (numberEdit) { numberEdit->loadText(number); MyMoneySplit split = txn.splits().first(); split.setNumber(number); txn.modifySplit(split); m_account.setValue("lastNumberUsed", number); file->modifyAccount(m_account); } } } } TransactionEditor *q_ptr; QString m_scheduleInfo; eMyMoney::Schedule::PaymentType m_paymentMethod; QString m_memoText; QList m_splits; KMyMoneyRegister::SelectedTransactions m_transactions; QList m_finalEditWidgets; TransactionEditorContainer* m_regForm; KMyMoneyRegister::Transaction* m_item; KMyMoneyRegister::QWidgetContainer m_editWidgets; MyMoneyAccount m_account; MyMoneyTransaction m_transaction; MyMoneySplit m_split; QDate m_lastPostDate; QMap m_priceInfo; - KMyMoneyRegister::Action m_initialAction; + eWidgets::eRegister::Action m_initialAction; bool m_openEditSplits; bool m_memoChanged; }; #endif // KMERGETRANSACTIONSDLG_H diff --git a/kmymoney/kmymoney.cpp b/kmymoney/kmymoney.cpp index e497c5c4f..030a1e4fd 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; + 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 = 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)); + d->m_selectedTransactions.append(KMyMoneyRegister::SelectedTransaction(transactionSplit.first, transactionSplit.second, QString())); } // 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 6232bd60f..04efb8400 100644 --- a/kmymoney/kmymoney.h +++ b/kmymoney/kmymoney.h @@ -1,1473 +1,1475 @@ /*************************************************************************** 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 "selectedtransaction.h" +#include "selectedtransactions.h" +#include "mymoneysplit.h" +#include "mymoneytransaction.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/payeeidentifier/ibanandbic/widgets/bicvalidator.cpp b/kmymoney/payeeidentifier/ibanandbic/widgets/bicvalidator.cpp index 427ae0528..86b7751b8 100644 --- a/kmymoney/payeeidentifier/ibanandbic/widgets/bicvalidator.cpp +++ b/kmymoney/payeeidentifier/ibanandbic/widgets/bicvalidator.cpp @@ -1,65 +1,66 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 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 . */ #include "bicvalidator.h" #include #include "payeeidentifier/ibanandbic/ibanbic.h" +#include "widgetenums.h" bicValidator::bicValidator(QObject* parent) : QValidator(parent) { } QValidator::State bicValidator::validate(QString &string, int&) const { for (int i = 0; i < qMin(string.length(), 6); ++i) { if (!string.at(i).isLetter()) return Invalid; if (string.at(i).isLower()) string[i] = string.at(i).toUpper(); } for (int i = 6; i < string.length(); ++i) { if (!string.at(i).isLetterOrNumber()) return Invalid; if (string.at(i).isLower()) string[i] = string.at(i).toUpper(); } if (string.length() > 11) return Invalid; else if (string.length() == 8 || string.length() == 11) { return Acceptable; } return Intermediate; } -QPair< KMyMoneyValidationFeedback::MessageType, QString > bicValidator::validateWithMessage(const QString& string) +QPair< eWidgets::ValidationFeedback::MessageType, QString > bicValidator::validateWithMessage(const QString& string) { // Do not show an error message if no BIC is given. if (string.length() != 8 && string.length() != 11) - return QPair< KMyMoneyValidationFeedback::MessageType, QString >(KMyMoneyValidationFeedback::Error, i18n("A valid BIC is 8 or 11 characters long.")); + return QPair< eWidgets::ValidationFeedback::MessageType, QString >(eWidgets::ValidationFeedback::MessageType::Error, i18n("A valid BIC is 8 or 11 characters long.")); if (payeeIdentifiers::ibanBic::isBicAllocated(string) == payeeIdentifiers::ibanBic::bicNotAllocated) - return QPair< KMyMoneyValidationFeedback::MessageType, QString >(KMyMoneyValidationFeedback::Error, i18n("The given BIC is not assigned to any credit institute.")); + return QPair< eWidgets::ValidationFeedback::MessageType, QString >(eWidgets::ValidationFeedback::MessageType::Error, i18n("The given BIC is not assigned to any credit institute.")); - return QPair< KMyMoneyValidationFeedback::MessageType, QString >(KMyMoneyValidationFeedback::None, QString()); + return QPair< eWidgets::ValidationFeedback::MessageType, QString >(eWidgets::ValidationFeedback::MessageType::None, QString()); } diff --git a/kmymoney/payeeidentifier/ibanandbic/widgets/bicvalidator.h b/kmymoney/payeeidentifier/ibanandbic/widgets/bicvalidator.h index 0ddf7c3e6..2f16e7ab2 100644 --- a/kmymoney/payeeidentifier/ibanandbic/widgets/bicvalidator.h +++ b/kmymoney/payeeidentifier/ibanandbic/widgets/bicvalidator.h @@ -1,37 +1,39 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 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 BICVALIDATOR_H #define BICVALIDATOR_H #include #include "payeeidentifier_iban_bic_widgets_export.h" #include "kmymoneyvalidationfeedback.h" +namespace eWidgets { namespace ValidationFeedback { enum class MessageType; } } + class PAYEEIDENTIFIER_IBAN_BIC_WIDGETS_EXPORT bicValidator : public QValidator { Q_OBJECT public: explicit bicValidator(QObject* parent = 0); virtual QValidator::State validate(QString& , int&) const; - static QPair validateWithMessage(const QString&); + static QPair validateWithMessage(const QString&); }; #endif // BICVALIDATOR_H diff --git a/kmymoney/payeeidentifier/ibanandbic/widgets/ibanvalidator.cpp b/kmymoney/payeeidentifier/ibanandbic/widgets/ibanvalidator.cpp index 3abdf8dee..5cfa1e6b0 100644 --- a/kmymoney/payeeidentifier/ibanandbic/widgets/ibanvalidator.cpp +++ b/kmymoney/payeeidentifier/ibanandbic/widgets/ibanvalidator.cpp @@ -1,82 +1,84 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 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 . */ #include "ibanvalidator.h" #include "../ibanbic.h" #include +#include "widgetenums.h" + ibanValidator::ibanValidator(QObject* parent) : QValidator(parent) { } QValidator::State ibanValidator::validate(QString& string, int&) const { // Check country code and set it uppercase if (string.length() >= 1) { if (!string.at(0).isLetter()) return Invalid; if (string.at(0).isLower()) string[0] = string.at(0).toUpper(); } if (string.length() >= 2) { if (!string.at(1).isLetterOrNumber()) return Invalid; if (string.at(1).isLower()) string[1] = string.at(1).toUpper(); } // Check rest of the iban int characterCount = qMin(string.length(), 2); for (int i = 2; i < string.length(); ++i) { if (string.at(i).isLetterOrNumber()) { ++characterCount; } else if (!string.at(i).isSpace()) { return Invalid; } } if (characterCount > 32) return Invalid; if (characterCount > 5) { return Acceptable; } return Intermediate; } -QPair< KMyMoneyValidationFeedback::MessageType, QString > ibanValidator::validateWithMessage(const QString& string) +QPair< eWidgets::ValidationFeedback::MessageType, QString > ibanValidator::validateWithMessage(const QString& string) { // string.length() > 32 should not happen because all line edits should have this validator installed if (string.length() < 5) - return QPair< KMyMoneyValidationFeedback::MessageType, QString >(KMyMoneyValidationFeedback::Error, i18n("This IBAN is too short.")); + return QPair< eWidgets::ValidationFeedback::MessageType, QString >(eWidgets::ValidationFeedback::MessageType::Error, i18n("This IBAN is too short.")); if (!payeeIdentifiers::ibanBic::validateIbanChecksum(payeeIdentifiers::ibanBic::ibanToElectronic(string))) - return QPair< KMyMoneyValidationFeedback::MessageType, QString >(KMyMoneyValidationFeedback::Warning, i18n("This IBAN is invalid.")); + return QPair< eWidgets::ValidationFeedback::MessageType, QString >(eWidgets::ValidationFeedback::MessageType::Warning, i18n("This IBAN is invalid.")); - return QPair< KMyMoneyValidationFeedback::MessageType, QString >(KMyMoneyValidationFeedback::None, QString()); + return QPair< eWidgets::ValidationFeedback::MessageType, QString >(eWidgets::ValidationFeedback::MessageType::None, QString()); } void ibanValidator::fixup(QString& string) const { string = payeeIdentifiers::ibanBic::ibanToPaperformat(string); } diff --git a/kmymoney/payeeidentifier/ibanandbic/widgets/ibanvalidator.h b/kmymoney/payeeidentifier/ibanandbic/widgets/ibanvalidator.h index 838fc20da..fa42e4cf0 100644 --- a/kmymoney/payeeidentifier/ibanandbic/widgets/ibanvalidator.h +++ b/kmymoney/payeeidentifier/ibanandbic/widgets/ibanvalidator.h @@ -1,41 +1,43 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 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 IBANVALIDATOR_H #define IBANVALIDATOR_H #include "payeeidentifier_iban_bic_widgets_export.h" #include #include "kmymoneyvalidationfeedback.h" +namespace eWidgets { namespace ValidationFeedback { enum class MessageType; } } + class PAYEEIDENTIFIER_IBAN_BIC_WIDGETS_EXPORT ibanValidator : public QValidator { Q_OBJECT public: explicit ibanValidator(QObject* parent = 0); virtual State validate(QString& , int&) const; State validate(const QString&) const; virtual void fixup(QString&) const; - static QPair validateWithMessage(const QString&); + static QPair validateWithMessage(const QString&); }; #endif // IBANVALIDATOR_H diff --git a/kmymoney/payeeidentifier/ibanandbic/widgets/kbicedit.cpp b/kmymoney/payeeidentifier/ibanandbic/widgets/kbicedit.cpp index 7d17e9f68..8fe23681c 100644 --- a/kmymoney/payeeidentifier/ibanandbic/widgets/kbicedit.cpp +++ b/kmymoney/payeeidentifier/ibanandbic/widgets/kbicedit.cpp @@ -1,119 +1,120 @@ /* * 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 . */ #include "kbicedit.h" #include #include #include #include #include #include #include "../bicmodel.h" #include "bicvalidator.h" +#include "kmymoneyvalidationfeedback.h" class bicItemDelegate : public QStyledItemDelegate { public: explicit bicItemDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) {} void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; private: inline QFont getSmallFont(const QStyleOptionViewItem& option) const; }; KBicEdit::KBicEdit(QWidget* parent) : KLineEdit(parent) { QCompleter* completer = new QCompleter(this); bicModel* model = new bicModel(this); completer->setModel(model); m_popupDelegate = new bicItemDelegate(this); completer->popup()->setItemDelegate(m_popupDelegate); setCompleter(completer); bicValidator *const validator = new bicValidator(this); setValidator(validator); } KBicEdit::~KBicEdit() { delete m_popupDelegate; } QFont bicItemDelegate::getSmallFont(const QStyleOptionViewItem& option) const { QFont smallFont = option.font; smallFont.setPointSize(0.9*smallFont.pointSize()); return smallFont; } QSize bicItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); QFontMetrics metrics(option.font); QFontMetrics smallMetrics(getSmallFont(option)); const QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); const int margin = style->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; // A bic has maximal 11 characters. So we guess, we want to display 11 characters. The name of the institution has to adapt to what is given return QSize(metrics.width(QLatin1Char('X')) + 2*margin, metrics.lineSpacing() + smallMetrics.lineSpacing() + smallMetrics.leading() + 2*margin); } /** * @todo enable eliding (use QFontMetrics::elidedText() ) */ void bicItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); // Background QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); const int margin = style->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; const QRect textArea = QRect(opt.rect.x() + margin, opt.rect.y() + margin, opt.rect.width() - 2 * margin, opt.rect.height() - 2 * margin); // Paint name painter->save(); QFont smallFont = getSmallFont(opt); QFontMetrics metrics(opt.font); QFontMetrics smallMetrics(smallFont); QRect nameRect = style->alignedRect(opt.direction, Qt::AlignBottom, QSize(textArea.width(), smallMetrics.lineSpacing()), textArea); painter->setFont(smallFont); style->drawItemText(painter, nameRect, Qt::AlignBottom, QApplication::palette(), true, index.model()->data(index, bicModel::InstitutionNameRole).toString(), option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Mid); painter->restore(); // Paint BIC painter->save(); QFont normal = painter->font(); normal.setBold(true); painter->setFont(normal); QRect bicRect = style->alignedRect(opt.direction, Qt::AlignTop, QSize(textArea.width(), metrics.lineSpacing()), textArea); const QString bic = index.model()->data(index, Qt::DisplayRole).toString(); style->drawItemText(painter, bicRect, Qt::AlignTop, QApplication::palette(), true, bic, option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text); painter->restore(); } diff --git a/kmymoney/payeeidentifier/ibanandbic/widgets/kbicedit.h b/kmymoney/payeeidentifier/ibanandbic/widgets/kbicedit.h index 9109d61e7..c4694db31 100644 --- a/kmymoney/payeeidentifier/ibanandbic/widgets/kbicedit.h +++ b/kmymoney/payeeidentifier/ibanandbic/widgets/kbicedit.h @@ -1,42 +1,41 @@ /* * 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 KBICEDIT_H #define KBICEDIT_H #include #include #include "payeeidentifier_iban_bic_widgets_export.h" -#include "kmymoneyvalidationfeedback.h" class QAbstractItemDelegate; class PAYEEIDENTIFIER_IBAN_BIC_WIDGETS_EXPORT KBicEdit : public KLineEdit { Q_OBJECT public: - KBicEdit(QWidget* parent = 0); + explicit KBicEdit(QWidget* parent = 0); virtual ~KBicEdit(); private: QAbstractItemDelegate* m_popupDelegate; }; #endif // KBICEDIT_H diff --git a/kmymoney/payeeidentifier/ibanandbic/widgets/kibanlineedit.cpp b/kmymoney/payeeidentifier/ibanandbic/widgets/kibanlineedit.cpp index 82c10b2ab..eb52af829 100644 --- a/kmymoney/payeeidentifier/ibanandbic/widgets/kibanlineedit.cpp +++ b/kmymoney/payeeidentifier/ibanandbic/widgets/kibanlineedit.cpp @@ -1,34 +1,34 @@ /* * 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 . */ #include "kibanlineedit.h" #include "ibanvalidator.h" - +#include "kmymoneyvalidationfeedback.h" KIbanLineEdit::KIbanLineEdit(QWidget* parent) : KLineEdit(parent) { ibanValidator *const validatorPtr = new ibanValidator; setValidator(validatorPtr); } const ibanValidator* KIbanLineEdit::validator() const { return qobject_cast(KLineEdit::validator()); } diff --git a/kmymoney/payeeidentifier/ibanandbic/widgets/kibanlineedit.h b/kmymoney/payeeidentifier/ibanandbic/widgets/kibanlineedit.h index bb98a7223..73523439a 100644 --- a/kmymoney/payeeidentifier/ibanandbic/widgets/kibanlineedit.h +++ b/kmymoney/payeeidentifier/ibanandbic/widgets/kibanlineedit.h @@ -1,39 +1,37 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 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 KIBANLINEEDIT_H #define KIBANLINEEDIT_H #include "payeeidentifier_iban_bic_widgets_export.h" #include -#include "kmymoneyvalidationfeedback.h" - class ibanValidator; class PAYEEIDENTIFIER_IBAN_BIC_WIDGETS_EXPORT KIbanLineEdit : public KLineEdit { Q_OBJECT public: - KIbanLineEdit(QWidget* parent); + explicit KIbanLineEdit(QWidget* parent); const ibanValidator* validator() const; }; #endif // KIBANLINEEDIT_H diff --git a/kmymoney/plugins/interfaces/kmmviewinterface.cpp b/kmymoney/plugins/interfaces/kmmviewinterface.cpp index 8b2e7b9e7..bb60d72b0 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 "selectedtransaction.h" +#include "selectedtransactions.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/kbanking/dialogs/kbpickstartdate.ui b/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.ui index 9112b0f09..7719c3938 100644 --- a/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.ui +++ b/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.ui @@ -1,186 +1,186 @@ KBPickStartDate 0 0 511 404 Pick Start Date true QFrame::Panel QFrame::Raised Qt::ScrollBarAlwaysOff QTextEdit::AutoAll Start Date &No date (let the bank determine the start date) &Last Update QFrame::Panel QFrame::Sunken false F&irst possible date QFrame::Panel QFrame::Sunken false Pick &date true - + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok - kMyMoneyDateInput + KMyMoneyDateInput QWidget
kmymoneydateinput.h
1
noDateButton lastUpdateButton firstDateButton pickDateButton pickDateButton toggled(bool) pickDateEdit setEnabled(bool) 119 308 490 308 buttonBox accepted() KBPickStartDate accept() 366 380 131 402 buttonBox rejected() KBPickStartDate reject() 450 379 374 401
diff --git a/kmymoney/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui b/kmymoney/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui index 457b46803..83664a9e5 100644 --- a/kmymoney/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui +++ b/kmymoney/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui @@ -1,528 +1,528 @@ KOnlineBankingStatusDecl 0 0 634 338 0 Account Details STATUS: false KLed::Off 10 0 Unavailable false Qt::Horizontal QSizePolicy::Expanding 41 21 BANK/BROKER: false 0 0 75 true &lt;Not configured&gt; false ACCOUNT: false 0 0 75 true &lt;Not configured&gt; false Store password false true true Qt::Vertical 20 143 OFX Details Header Version false Qt::Horizontal QSizePolicy::Expanding 150 21 Identify as false OFX <CLIENTUID> for institutions that require it Client UID Qt::Vertical 20 195 Import Details Start date of import QFrame::NoFrame QFrame::Plain 0 QLayout::SetMinimumSize Last &update buttonGroup2 false To&day minus true buttonGroup2 180 days false Pi&ck date buttonGroup2 - + false Qt::Horizontal 214 20 1 0 Payee's name is based on contents of the OFX tag false PAYEEID NAME MEMO Qt::Vertical 20 0 KComboBox QComboBox
kcombobox.h
KLineEdit QLineEdit
klineedit.h
KLed QWidget
kled.h
QTabWidget QTabWidget
ktabwidget.h
1
- kMyMoneyDateInput + KMyMoneyDateInput QFrame
kmymoneydateinput.h
1
m_pickDateRB toggled(bool) m_specificDate setEnabled(bool) 91 318 202 314 m_todayRB toggled(bool) m_numdaysSpin setEnabled(bool) 91 280 268 275 m_storePassword toggled(bool) m_password setEnabled(bool) 158 289 263 289
diff --git a/kmymoney/plugins/onlinetasks/interfaces/ui/ionlinejobedit.h b/kmymoney/plugins/onlinetasks/interfaces/ui/ionlinejobedit.h index c27abd2cf..810d3d42b 100644 --- a/kmymoney/plugins/onlinetasks/interfaces/ui/ionlinejobedit.h +++ b/kmymoney/plugins/onlinetasks/interfaces/ui/ionlinejobedit.h @@ -1,106 +1,106 @@ /* * 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 IONLINEJOBEDIT_H #define IONLINEJOBEDIT_H #include #include #include "mymoney/onlinejob.h" #include "mymoneymoney.h" class MyMoneyMoney; /** * @brief Interface for widgets editing onlineTasks * * @since 4.8.0 */ class IonlineJobEdit : public QWidget { Q_OBJECT public: explicit IonlineJobEdit(QWidget* parent = 0, QVariantList args = QVariantList()) : QWidget(parent) { Q_UNUSED(args); } /** * @brief Reads interface and creates an onlineJob * * An null onlineJob can be returned. */ virtual onlineJob getOnlineJob() const = 0; /** * @brief Checks if the user input would generate a valid onlineJob */ virtual bool isValid() const = 0; /** * @brief List of supported onlineTasks * * Returns a list of all task ids which can be edited with this * widget. */ virtual QStringList supportedOnlineTasks() const = 0; /** * @brief Returns true if this widget is editable */ virtual bool isReadOnly() const = 0; public slots: /** * @brief Set an onlineJob to edit * * If the task is not compatible to the widget, return false. Do not throw * exceptions (and catch all of them). * * @return false if setting was not possible */ virtual bool setOnlineJob(const onlineJob&) = 0; virtual void setOriginAccount(const QString&) = 0; virtual void showAllErrorMessages(const bool) {} signals: /** * @brief Emitted if a job which transfers money changed it's value */ void transferValueChanged(MyMoneyMoney); /** * @brief Emitted if a job got valid or invalid * * @param valid status of onlineJob.isValid() */ void validityChanged(bool valid); /** * @brief Emitted if widget was set or unset read only */ void readOnlyChanged(bool); }; -Q_DECLARE_INTERFACE(IonlineJobEdit, "org.kmymoney.plugin.ionlinejobedit"); +Q_DECLARE_INTERFACE(IonlineJobEdit, "org.kmymoney.plugin.ionlinejobedit") #endif // IONLINEJOBEDIT_H diff --git a/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.cpp b/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.cpp index 2b971ffdf..5be658a5b 100644 --- a/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.cpp +++ b/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.cpp @@ -1,534 +1,535 @@ /* 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 "sepacredittransferedit.h" #include "ui_sepacredittransferedit.h" #include #include #include #include #include "kguiutils.h" #include "mymoney/payeeidentifiermodel.h" #include "onlinetasks/sepa/tasks/sepaonlinetransfer.h" #include "payeeidentifier/ibanandbic/widgets/ibanvalidator.h" #include "payeeidentifier/ibanandbic/widgets/bicvalidator.h" #include "payeeidentifier/payeeidentifiertyped.h" #include "misc/charvalidator.h" #include "payeeidentifier/ibanandbic/ibanbic.h" #include "styleditemdelegateforwarder.h" #include "payeeidentifier/ibanandbic/widgets/ibanbicitemdelegate.h" #include "onlinejobtyped.h" #include "mymoneyaccount.h" +#include "widgetenums.h" class ibanBicCompleterDelegate : public StyledItemDelegateForwarder { Q_OBJECT public: ibanBicCompleterDelegate(QObject *parent) : StyledItemDelegateForwarder(parent) {} protected: virtual QAbstractItemDelegate* getItemDelegate(const QModelIndex &index) const { static QPointer defaultDelegate; static QPointer ibanBicDelegate; const bool ibanBicRequested = index.model()->data(index, payeeIdentifierModel::isPayeeIdentifier).toBool(); QAbstractItemDelegate* delegate = (ibanBicRequested) ? ibanBicDelegate : defaultDelegate; if (delegate == 0) { if (ibanBicRequested) { // Use this->parent() as parent because "this" is const ibanBicDelegate = new ibanBicItemDelegate(this->parent()); delegate = ibanBicDelegate; } else { // Use this->parent() as parent because "this" is const defaultDelegate = new QStyledItemDelegate(this->parent()); delegate = defaultDelegate; } connectSignals(delegate, Qt::UniqueConnection); } Q_CHECK_PTR(delegate); return delegate; } }; class payeeIdentifierCompleterPopup : public QTreeView { Q_OBJECT public: payeeIdentifierCompleterPopup(QWidget* parent = 0) : QTreeView(parent) { setRootIsDecorated(false); setAlternatingRowColors(true); setAnimated(true); setHeaderHidden(true); setUniformRowHeights(false); expandAll(); } }; class ibanBicFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: enum roles { payeeIban = payeeIdentifierModel::payeeIdentifierUserRole, /**< electornic IBAN of payee */ }; ibanBicFilterProxyModel(QObject* parent = 0) : QSortFilterProxyModel(parent) {} virtual QVariant data(const QModelIndex &index, int role) const { if (role == payeeIban) { if (!index.isValid()) return QVariant(); try { payeeIdentifierTyped iban = payeeIdentifierTyped( index.model()->data(index, payeeIdentifierModel::payeeIdentifier).value() ); return iban->electronicIban(); } catch (payeeIdentifier::exception&) { return QVariant(); } } return QSortFilterProxyModel::data(index, role); } virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { if (!source_parent.isValid()) return true; QModelIndex index = source_parent.model()->index(source_row, 0, source_parent); return (source_parent.model()->data(index, payeeIdentifierModel::payeeIdentifierType).toString() == payeeIdentifiers::ibanBic::staticPayeeIdentifierIid()); } }; class ibanBicCompleter : public QCompleter { Q_OBJECT public: ibanBicCompleter(QObject* parent = 0); Q_SIGNALS: void activatedName(const QString& name) const; void highlightedName(const QString& name) const; void activatedBic(const QString& bic) const; void highlightedBic(const QString& bic) const; void activatedIban(const QString& iban) const; void highlightedIban(const QString& iban) const; private Q_SLOTS: void slotActivated(const QModelIndex& index) const; void slotHighlighted(const QModelIndex& index) const; }; ibanBicCompleter::ibanBicCompleter(QObject *parent) : QCompleter(parent) { connect(this, SIGNAL(activated(QModelIndex)), SLOT(slotActivated(QModelIndex))); connect(this, SIGNAL(highlighted(QModelIndex)), SLOT(slotHighlighted(QModelIndex))); } void ibanBicCompleter::slotActivated(const QModelIndex &index) const { if (!index.isValid()) return; emit activatedName(index.model()->data(index, payeeIdentifierModel::payeeName).toString()); try { payeeIdentifierTyped iban = payeeIdentifierTyped( index.model()->data(index, payeeIdentifierModel::payeeIdentifier).value() ); emit activatedIban(iban->electronicIban()); emit activatedBic(iban->storedBic()); } catch (payeeIdentifier::exception&) { } } void ibanBicCompleter::slotHighlighted(const QModelIndex &index) const { if (!index.isValid()) return; emit highlightedName(index.model()->data(index, payeeIdentifierModel::payeeName).toString()); try { payeeIdentifierTyped iban = payeeIdentifierTyped( index.model()->data(index, payeeIdentifierModel::payeeIdentifier).value() ); emit highlightedIban(iban->electronicIban()); emit highlightedBic(iban->storedBic()); } catch (payeeIdentifier::exception&) { } } sepaCreditTransferEdit::sepaCreditTransferEdit(QWidget *parent, QVariantList args) : IonlineJobEdit(parent, args), ui(new Ui::sepaCreditTransferEdit), m_onlineJob(onlineJobTyped()), - m_requiredFields(new kMandatoryFieldGroup(this)), + m_requiredFields(new KMandatoryFieldGroup(this)), m_readOnly(false), m_showAllErrors(false) { ui->setupUi(this); m_requiredFields->add(ui->beneficiaryIban); m_requiredFields->add(ui->value); // Other required fields are set in updateSettings() connect(m_requiredFields, SIGNAL(stateChanged(bool)), this, SLOT(requiredFieldsCompleted(bool))); connect(ui->beneficiaryName, SIGNAL(textChanged(QString)), this, SLOT(beneficiaryNameChanged(QString))); connect(ui->beneficiaryIban, SIGNAL(textChanged(QString)), this, SLOT(beneficiaryIbanChanged(QString))); connect(ui->beneficiaryBankCode, SIGNAL(textChanged(QString)), this, SLOT(beneficiaryBicChanged(QString))); connect(ui->value, SIGNAL(valueChanged(QString)), this, SLOT(valueChanged())); connect(ui->sepaReference, SIGNAL(textChanged(QString)), this, SLOT(endToEndReferenceChanged(QString))); connect(ui->purpose, SIGNAL(textChanged()), this, SLOT(purposeChanged())); connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(updateEveryStatus())); connect(ui->beneficiaryName, SIGNAL(textChanged(QString)), this, SIGNAL(onlineJobChanged())); connect(ui->beneficiaryIban, SIGNAL(textChanged(QString)), this, SIGNAL(onlineJobChanged())); connect(ui->beneficiaryBankCode, SIGNAL(textChanged(QString)), this, SIGNAL(onlineJobChanged())); connect(ui->value, SIGNAL(valueChanged(QString)), this, SIGNAL(onlineJobChanged())); connect(ui->sepaReference, SIGNAL(textChanged(QString)), this, SIGNAL(onlineJobChanged())); connect(ui->purpose, SIGNAL(textChanged()), this, SIGNAL(onlineJobChanged())); // Connect signals for read only connect(this, SIGNAL(readOnlyChanged(bool)), ui->beneficiaryName, SLOT(setReadOnly(bool))); connect(this, SIGNAL(readOnlyChanged(bool)), ui->beneficiaryIban, SLOT(setReadOnly(bool))); connect(this, SIGNAL(readOnlyChanged(bool)), ui->beneficiaryBankCode, SLOT(setReadOnly(bool))); connect(this, SIGNAL(readOnlyChanged(bool)), ui->value, SLOT(setReadOnly(bool))); connect(this, SIGNAL(readOnlyChanged(bool)), ui->sepaReference, SLOT(setReadOnly(bool))); connect(this, SIGNAL(readOnlyChanged(bool)), ui->purpose, SLOT(setReadOnly(bool))); // Create models for completers payeeIdentifierModel* identModel = new payeeIdentifierModel(this); identModel->setTypeFilter(payeeIdentifiers::ibanBic::staticPayeeIdentifierIid()); ibanBicFilterProxyModel* filterModel = new ibanBicFilterProxyModel(this); filterModel->setSourceModel(identModel); KDescendantsProxyModel* descendantsModel = new KDescendantsProxyModel(this); descendantsModel->setSourceModel(filterModel); // Set completers popup and bind them to the corresponding fields { // Beneficiary name field ibanBicCompleter* completer = new ibanBicCompleter(this); completer->setModel(descendantsModel); completer->setCompletionRole(payeeIdentifierModel::payeeName); completer->setCaseSensitivity(Qt::CaseInsensitive); connect(completer, SIGNAL(activatedIban(QString)), ui->beneficiaryIban, SLOT(setText(QString))); connect(completer, SIGNAL(activatedBic(QString)), ui->beneficiaryBankCode, SLOT(setText(QString))); ui->beneficiaryName->setCompleter(completer); QAbstractItemView *itemView = new payeeIdentifierCompleterPopup(); completer->setPopup(itemView); // setPopup() resets the delegate itemView->setItemDelegate(new ibanBicCompleterDelegate(this)); } { // IBAN field ibanBicCompleter* ibanCompleter = new ibanBicCompleter(this); ibanCompleter->setModel(descendantsModel); ibanCompleter->setCompletionRole(ibanBicFilterProxyModel::payeeIban); ibanCompleter->setCaseSensitivity(Qt::CaseInsensitive); connect(ibanCompleter, SIGNAL(activatedName(QString)), ui->beneficiaryName, SLOT(setText(QString))); connect(ibanCompleter, SIGNAL(activatedBic(QString)), ui->beneficiaryBankCode, SLOT(setText(QString))); ui->beneficiaryIban->setCompleter(ibanCompleter); QAbstractItemView *itemView = new payeeIdentifierCompleterPopup(); ibanCompleter->setPopup(itemView); // setPopup() resets the delegate itemView->setItemDelegate(new ibanBicCompleterDelegate(this)); } } sepaCreditTransferEdit::~sepaCreditTransferEdit() { delete ui; } void sepaCreditTransferEdit::showEvent(QShowEvent* event) { updateEveryStatus(); QWidget::showEvent(event); } void sepaCreditTransferEdit::showAllErrorMessages(const bool state) { if (m_showAllErrors != state) { m_showAllErrors = state; updateEveryStatus(); } } onlineJobTyped sepaCreditTransferEdit::getOnlineJobTyped() const { onlineJobTyped sepaJob(m_onlineJob); sepaJob.task()->setValue(ui->value->value()); sepaJob.task()->setPurpose(ui->purpose->toPlainText()); sepaJob.task()->setEndToEndReference(ui->sepaReference->text()); payeeIdentifiers::ibanBic accIdent; accIdent.setOwnerName(ui->beneficiaryName->text()); accIdent.setIban(ui->beneficiaryIban->text()); accIdent.setBic(ui->beneficiaryBankCode->text()); sepaJob.task()->setBeneficiary(accIdent); return sepaJob; } void sepaCreditTransferEdit::setOnlineJob(const onlineJobTyped& job) { m_onlineJob = job; updateSettings(); setReadOnly(!job.isEditable()); ui->purpose->setText(job.task()->purpose()); ui->sepaReference->setText(job.task()->endToEndReference()); ui->value->setValue(job.task()->value()); ui->beneficiaryName->setText(job.task()->beneficiaryTyped().ownerName()); ui->beneficiaryIban->setText(job.task()->beneficiaryTyped().paperformatIban()); ui->beneficiaryBankCode->setText(job.task()->beneficiaryTyped().storedBic()); } bool sepaCreditTransferEdit::setOnlineJob(const onlineJob& job) { if (!job.isNull() && job.task()->taskName() == sepaOnlineTransfer::name()) { setOnlineJob(onlineJobTyped(job)); return true; } return false; } void sepaCreditTransferEdit::setOriginAccount(const QString& accountId) { m_onlineJob.task()->setOriginAccount(accountId); updateSettings(); } void sepaCreditTransferEdit::updateEveryStatus() { beneficiaryNameChanged(ui->beneficiaryName->text()); beneficiaryIbanChanged(ui->beneficiaryIban->text()); beneficiaryBicChanged(ui->beneficiaryBankCode->text()); purposeChanged(); valueChanged(); endToEndReferenceChanged(ui->sepaReference->text()); } void sepaCreditTransferEdit::setReadOnly(const bool& readOnly) { // Only set writeable if it changes something and if it is possible if (readOnly != m_readOnly && (readOnly == true || getOnlineJobTyped().isEditable())) { m_readOnly = readOnly; emit readOnlyChanged(m_readOnly); } } void sepaCreditTransferEdit::updateSettings() { QSharedPointer settings = taskSettings(); // Reference ui->sepaReference->setMaxLength(settings->endToEndReferenceLength()); if (settings->endToEndReferenceLength() == 0) ui->sepaReference->setEnabled(false); else ui->sepaReference->setEnabled(true); // Purpose ui->purpose->setAllowedChars(settings->allowedChars()); ui->purpose->setMaxLineLength(settings->purposeLineLength()); ui->purpose->setMaxLines(settings->purposeMaxLines()); if (settings->purposeMinLength()) m_requiredFields->add(ui->purpose); else m_requiredFields->remove(ui->purpose); // Beneficiary Name ui->beneficiaryName->setValidator(new charValidator(ui->beneficiaryName, settings->allowedChars())); ui->beneficiaryName->setMaxLength(settings->recipientNameLineLength()); if (settings->recipientNameMinLength() != 0) m_requiredFields->add(ui->beneficiaryName); else m_requiredFields->remove(ui->beneficiaryName); updateEveryStatus(); } void sepaCreditTransferEdit::beneficiaryIbanChanged(const QString& iban) { // Check IBAN - QPair answer = ibanValidator::validateWithMessage(iban); + QPair answer = ibanValidator::validateWithMessage(iban); if (m_showAllErrors || iban.length() > 5 || (!ui->beneficiaryIban->hasFocus() && !iban.isEmpty())) ui->feedbackIban->setFeedback(answer.first, answer.second); else ui->feedbackIban->removeFeedback(); // Check if BIC is mandatory QSharedPointer settings = taskSettings(); QString payeeIban; try { payeeIdentifier ident = getOnlineJobTyped().task()->originAccountIdentifier(); payeeIban = ident.data()->electronicIban(); } catch (payeeIdentifier::exception&) { } if (settings->isBicMandatory(payeeIban, iban)) { m_requiredFields->add(ui->beneficiaryBankCode); beneficiaryBicChanged(ui->beneficiaryBankCode->text()); } else { m_requiredFields->remove(ui->beneficiaryBankCode); beneficiaryBicChanged(ui->beneficiaryBankCode->text()); } } void sepaCreditTransferEdit::beneficiaryBicChanged(const QString& bic) { if (bic.isEmpty() && !ui->beneficiaryIban->text().isEmpty()) { QSharedPointer settings = taskSettings(); const payeeIdentifier payee = getOnlineJobTyped().task()->originAccountIdentifier(); QString iban; try { iban = payee.data()->electronicIban(); } catch (payeeIdentifier::badCast&) { } if (settings->isBicMandatory(iban , ui->beneficiaryIban->text())) { - ui->feedbackBic->setFeedback(KMyMoneyValidationFeedback::Error, i18n("For this beneficiary's country the BIC is mandatory.")); + ui->feedbackBic->setFeedback(eWidgets::ValidationFeedback::MessageType::Error, i18n("For this beneficiary's country the BIC is mandatory.")); return; } } - QPair answer = bicValidator::validateWithMessage(bic); + QPair answer = bicValidator::validateWithMessage(bic); if (m_showAllErrors || bic.length() >= 8 || (!ui->beneficiaryBankCode->hasFocus() && !bic.isEmpty())) ui->feedbackBic->setFeedback(answer.first, answer.second); else ui->feedbackBic->removeFeedback(); } void sepaCreditTransferEdit::beneficiaryNameChanged(const QString& name) { QSharedPointer settings = taskSettings(); if (name.length() < settings->recipientNameMinLength() && (m_showAllErrors || (!ui->beneficiaryName->hasFocus() && !name.isEmpty()))) { - ui->feedbackName->setFeedback(KMyMoneyValidationFeedback::Error, i18np("A beneficiary name is needed.", "The beneficiary name must be at least %1 characters long", + ui->feedbackName->setFeedback(eWidgets::ValidationFeedback::MessageType::Error, i18np("A beneficiary name is needed.", "The beneficiary name must be at least %1 characters long", settings->recipientNameMinLength() )); } else { ui->feedbackName->removeFeedback(); } } void sepaCreditTransferEdit::valueChanged() { if ((!ui->value->isValid() && (m_showAllErrors || (!ui->value->hasFocus() && ui->value->value().toDouble() != 0))) || (!ui->value->value().isPositive() && ui->value->value().toDouble() != 0)) { - ui->feedbackAmount->setFeedback(KMyMoneyValidationFeedback::Error, i18n("A positive amount to transfer is needed.")); + ui->feedbackAmount->setFeedback(eWidgets::ValidationFeedback::MessageType::Error, i18n("A positive amount to transfer is needed.")); return; } if (!ui->value->isValid()) return; const MyMoneyAccount account = getOnlineJob().responsibleMyMoneyAccount(); const MyMoneyMoney expectedBalance = account.balance() - ui->value->value(); if (expectedBalance < MyMoneyMoney(account.value("maxCreditAbsolute"))) { - ui->feedbackAmount->setFeedback(KMyMoneyValidationFeedback::Warning, i18n("After this credit transfer the account's balance will be below your credit limit.")); + ui->feedbackAmount->setFeedback(eWidgets::ValidationFeedback::MessageType::Warning, i18n("After this credit transfer the account's balance will be below your credit limit.")); } else if (expectedBalance < MyMoneyMoney(account.value("minBalanceAbsolute"))) { - ui->feedbackAmount->setFeedback(KMyMoneyValidationFeedback::Information, i18n("After this credit transfer the account's balance will be below the minimal balance.")); + ui->feedbackAmount->setFeedback(eWidgets::ValidationFeedback::MessageType::Information, i18n("After this credit transfer the account's balance will be below the minimal balance.")); } else { ui->feedbackAmount->removeFeedback(); } } void sepaCreditTransferEdit::endToEndReferenceChanged(const QString& reference) { QSharedPointer settings = taskSettings(); if (settings->checkEndToEndReferenceLength(reference) == validators::tooLong) { - ui->feedbackReference->setFeedback(KMyMoneyValidationFeedback::Error, i18np("The end-to-end reference cannot contain more than one character.", + ui->feedbackReference->setFeedback(eWidgets::ValidationFeedback::MessageType::Error, i18np("The end-to-end reference cannot contain more than one character.", "The end-to-end reference cannot contain more than %1 characters.", settings->endToEndReferenceLength() )); } else { ui->feedbackReference->removeFeedback(); } } void sepaCreditTransferEdit::purposeChanged() { const QString purpose = ui->purpose->toPlainText(); QSharedPointer settings = taskSettings(); QString message; if (!settings->checkPurposeLineLength(purpose)) message = i18np("The maximal line length of %1 character per line is exceeded.", "The maximal line length of %1 characters per line is exceeded.", settings->purposeLineLength()) .append('\n'); if (!settings->checkPurposeCharset(purpose)) message.append(i18n("The purpose can only contain the letters A-Z, spaces and ':?.,-()+ and /")).append('\n'); if (!settings->checkPurposeMaxLines(purpose)) { message.append(i18np("In the purpose only a single line is allowed.", "The purpose cannot contain more than %1 lines.", settings->purposeMaxLines())) .append('\n'); } else if (settings->checkPurposeLength(purpose) == validators::tooShort) { message.append(i18np("A purpose is needed.", "The purpose must be at least %1 characters long.", settings->purposeMinLength())) .append('\n'); } // Remove the last '\n' message.chop(1); if (!message.isEmpty()) { - ui->feedbackPurpose->setFeedback(KMyMoneyValidationFeedback::Error, message); + ui->feedbackPurpose->setFeedback(eWidgets::ValidationFeedback::MessageType::Error, message); } else { ui->feedbackPurpose->removeFeedback(); } } QSharedPointer< const sepaOnlineTransfer::settings > sepaCreditTransferEdit::taskSettings() { return getOnlineJobTyped().constTask()->getSettings(); } #include "sepacredittransferedit.moc" diff --git a/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.h b/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.h index 8956e834c..b62aa7941 100644 --- a/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.h +++ b/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.h @@ -1,126 +1,126 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2013-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 SEPACREDITTRANSFEREDIT_H #define SEPACREDITTRANSFEREDIT_H #include #include "mymoney/onlinejobtyped.h" #include "onlinetasks/sepa/tasks/sepaonlinetransfer.h" #include "onlinetasks/interfaces/ui/ionlinejobedit.h" -class kMandatoryFieldGroup; +class KMandatoryFieldGroup; namespace Ui { class sepaCreditTransferEdit; } /** * @brief Widget to edit sepaOnlineTransfer */ class sepaCreditTransferEdit : public IonlineJobEdit { Q_OBJECT Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged); Q_PROPERTY(onlineJob job READ getOnlineJob WRITE setOnlineJob); Q_INTERFACES(IonlineJobEdit); public: explicit sepaCreditTransferEdit(QWidget *parent = 0, QVariantList args = QVariantList()); ~sepaCreditTransferEdit(); onlineJobTyped getOnlineJobTyped() const; onlineJob getOnlineJob() const { return getOnlineJobTyped(); } QStringList supportedOnlineTasks() const { return QStringList(sepaOnlineTransfer::name()); } QString label() const { return i18n("SEPA Credit Transfer"); }; bool isValid() const { return getOnlineJobTyped().isValid(); }; bool isReadOnly() const { return m_readOnly; } virtual void showAllErrorMessages(const bool); virtual void showEvent(QShowEvent*); signals: void onlineJobChanged(); void readOnlyChanged(bool); public slots: void setOnlineJob(const onlineJobTyped &job); bool setOnlineJob(const onlineJob& job); void setOriginAccount(const QString& accountId); void setReadOnly(const bool&); private slots: void updateSettings(); void updateEveryStatus(); /** @{ * These slots are called when the corosponding field is changed * to start the validation. */ void purposeChanged(); void beneficiaryIbanChanged(const QString& iban); void beneficiaryBicChanged(const QString& bic); void beneficiaryNameChanged(const QString& name); void valueChanged(); void endToEndReferenceChanged(const QString& reference); /** @} */ /** * @brief Convenient slot to emit validityChanged() * * A default implementation to emit validityChanged() based on getOnlineJob().isValid(). * This is useful if you use @a kMandatoryFieldsGroup in your widget. Just connect kMandatoryFieldsGroup::stateChanged(bool) * to this slot. * * @param status if false, validityChanged(false) is emitted without further checks. */ void requiredFieldsCompleted(const bool& status = true) { if (status) { emit validityChanged(getOnlineJobTyped().isValid()); } else { emit validityChanged(false); } } private: Ui::sepaCreditTransferEdit *ui; onlineJobTyped m_onlineJob; - kMandatoryFieldGroup* m_requiredFields; + KMandatoryFieldGroup* m_requiredFields; bool m_readOnly; bool m_showAllErrors; QSharedPointer taskSettings(); }; #endif // SEPACREDITTRANSFEREDIT_H diff --git a/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.ui b/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.ui index 6424793b5..273a0340c 100644 --- a/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.ui +++ b/kmymoney/plugins/onlinetasks/sepa/ui/sepacredittransferedit.ui @@ -1,223 +1,223 @@ sepaCreditTransferEdit 0 0 340 438 Beneficiary &Name beneficiaryName IBAN BIC Amount Qt::Horizontal 40 20 - + 100 0 false End-to-end reference sepaReference Purpose purpose 0 0 70 30 true QTextEdit::FixedColumnWidth 27 false label purpose label_4 label_2 beneficiaryName label_bic label_iban beneficiaryBankCode feedbackName feedbackBic feedbackAmount feedbackReference feedbackPurpose sepaReference feedbackIban beneficiaryIban KLineEdit QLineEdit
klineedit.h
- kMyMoneyEdit + KMyMoneyEdit QFrame
kmymoneyedit.h
1
KMyMoneyTextEdit QTextEdit
kmymoneytextedit.h
KBicEdit QLineEdit
payeeidentifier/ibanandbic/widgets/kbicedit.h
validatorFeedback(KMyMoneyValidationFeedback::MessageType,QString)
KMyMoneyValidationFeedback QWidget
kmymoneyvalidationfeedback.h
1 setFeedback(KMyMoneyValidationFeedback::MessageType,QString)
KIbanLineEdit QLineEdit
payeeidentifier/ibanandbic/widgets/kibanlineedit.h
validatorFeedback(KMyMoneyValidationFeedback::MessageType,QString)
beneficiaryName beneficiaryIban beneficiaryBankCode sepaReference purpose
diff --git a/kmymoney/plugins/printcheck/CMakeLists.txt b/kmymoney/plugins/printcheck/CMakeLists.txt index 74c5aa3cd..a2965aece 100644 --- a/kmymoney/plugins/printcheck/CMakeLists.txt +++ b/kmymoney/plugins/printcheck/CMakeLists.txt @@ -1,71 +1,72 @@ # 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 9d407f00e..2ec6a18ed 100644 --- a/kmymoney/plugins/printcheck/printcheck.cpp +++ b/kmymoney/plugins/printcheck/printcheck.cpp @@ -1,238 +1,239 @@ /*************************************************************************** * 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 6b937d1d4..a47ce9e30 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 "selectedtransaction.h" +#include "selectedtransactions.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/plugins/qif/export/kexportdlgdecl.ui b/kmymoney/plugins/qif/export/kexportdlgdecl.ui index a9af5182c..0651e9460 100644 --- a/kmymoney/plugins/qif/export/kexportdlgdecl.ui +++ b/kmymoney/plugins/qif/export/kexportdlgdecl.ui @@ -1,459 +1,459 @@ KExportDlgDecl 0 0 655 468 QIF Export true true 6 11 11 11 11 6 0 0 0 0 0 0 From this dialog you are able to export transactions to a quicken compatible file, (known as a QIF file, because of the extension). Please enter the path to the QIF file or select it by clicking on the Browse button. You can choose the file's path, the account and the format of the QIF file (profile). Choose Account to export all the transactions between the specified dates or just categories. You can also limit the transactions that are exported by start and ending date. Once you have pressed the Export button a message box will appear when the export has completed detailing how many transactions, categories and payees were exported. Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true QFrame::HLine QFrame::Raised 6 0 0 0 0 0 0 File to export to: false &Browse... Account to export false 6 0 0 0 0 QIF Profile false 6 0 0 0 0 0 0 200 0 false Contents to Export 6 11 11 11 11 Account Categories Date Range 6 11 11 11 11 - + 0 0 50 0 End on: false 0 0 50 0 Start on: false - + 0 0 Qt::Horizontal QSizePolicy::Expanding 230 16 QFrame::HLine QFrame::Sunken 6 0 0 0 0 Qt::Horizontal QSizePolicy::Expanding 451 0 &Export true true Cancel true KLineEdit QLineEdit
klineedit.h
KComboBox QComboBox
kcombobox.h
- kMyMoneyDateInput + KMyMoneyDateInput QWidget
../widgets/kmymoneydateinput.h
KMyMoneyAccountCombo QWidget
../widgets/kmymoneyaccountcombo.h
diff --git a/kmymoney/views/kbudgetview.ui b/kmymoney/views/kbudgetview.ui index fbf1d34fb..7f2704faa 100644 --- a/kmymoney/views/kbudgetview.ui +++ b/kmymoney/views/kbudgetview.ui @@ -1,356 +1,356 @@ KBudgetView 0 0 840 302 1 1 Qt::TabFocus Qt::Horizontal false 1 1 Your budgets Creates a new budget Use this to create a new empty budget. New Rename the current selected budget. Use this to start renaming the selected budget. Ren Delete the current selected budget Use this to delete the selected budget. Del true false true Name Year Qt::RichText Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false Accepts the entered values and stores the budget. Use this to store the modified data. Upd false Revert budget to last saved state. Use this to discard the modified data. Res Hide unused budget categories true 5 0 Assignments Account false false 1 0 Qt::NoFocus Total false - + Qt::NoFocus false false true Include subaccounts Qt::Horizontal QSizePolicy::Expanding 21 20 KLineEdit QLineEdit
klineedit.h
KMyMoneyAccountTreeView QTreeView
kmymoneyaccounttreeview.h
- kMyMoneyEdit + KMyMoneyEdit QFrame
kmymoneyedit.h
1
KBudgetValues QWidget
kbudgetvalues.h
m_collapseButton clicked() m_accountTree collapseAll() 494 138 561 48 m_expandButton clicked() m_accountTree expandAll() 547 138 561 48
diff --git a/kmymoney/views/kgloballedgerview.cpp b/kmymoney/views/kgloballedgerview.cpp index d505afb3c..6b8009d08 100644 --- a/kmymoney/views/kgloballedgerview.cpp +++ b/kmymoney/views/kgloballedgerview.cpp @@ -1,1640 +1,1658 @@ /*************************************************************************** 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 "tabbar.h" #include "register.h" #include "transactioneditor.h" -#include "selectedtransaction.h" +#include "selectedtransactions.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 "fancydategroupmarkers.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; + eWidgets::eRegister::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; + d->m_action = eWidgets::eRegister::Action::None; // 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->getTabBar(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))); + connect(m_form, SIGNAL(newTransaction(eWidgets::eRegister::Action)), this, SLOT(slotNewTransaction(eWidgets::eRegister::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() == KMyMoneyRegister::PostDateSort) { + if (m_register->primarySortKey() == eWidgets::SortField::PostDate) { // 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 KMyMoneyRegister::PostDateSort: - statement = new KMyMoneyRegister::StatementGroupMarker(m_register, KMyMoneyRegister::Deposit, reconciliationDate, i18n("Statement Details")); + case eWidgets::SortField::PostDate: + statement = new KMyMoneyRegister::StatementGroupMarker(m_register, eWidgets::eRegister::CashFlowDirection::Deposit, reconciliationDate, i18n("Statement Details")); m_register->sortItems(); break; - 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")); + case eWidgets::SortField::Type: + dStatement = new KMyMoneyRegister::StatementGroupMarker(m_register, eWidgets::eRegister::CashFlowDirection::Deposit, reconciliationDate, i18n("Statement Deposit Details")); + pStatement = new KMyMoneyRegister::StatementGroupMarker(m_register, eWidgets::eRegister::CashFlowDirection::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); + m_register->resize((int)eWidgets::eTransaction::Column::Detail); + m_form->resize((int)eWidgets::eTransactionForm::Column::Value1); 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) +void KGlobalLedgerView::slotNewTransaction(eWidgets::eRegister::Action id) { if (!m_inEditMode) { d->m_action = id; emit newTransaction(); } } void KGlobalLedgerView::slotNewTransaction() { - slotNewTransaction(KMyMoneyRegister::ActionNone); + slotNewTransaction(eWidgets::eRegister::Action::None); } void KGlobalLedgerView::setupDefaultAction() { switch (m_account.accountType()) { case eMyMoney::Account::Asset: case eMyMoney::Account::AssetLoan: case eMyMoney::Account::Savings: - d->m_action = KMyMoneyRegister::ActionDeposit; + d->m_action = eWidgets::eRegister::Action::Deposit; break; default: - d->m_action = KMyMoneyRegister::ActionWithdrawal; + d->m_action = eWidgets::eRegister::Action::Withdrawal; 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())) { + if ((d->m_action == eWidgets::eRegister::Action::None) && (!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) { + if (d->m_action == eWidgets::eRegister::Action::None) { 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; + d->m_action = eWidgets::eRegister::Action::None; } } 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 2ebb7e859..c3f137529 100644 --- a/kmymoney/views/kgloballedgerview.h +++ b/kmymoney/views/kgloballedgerview.h @@ -1,409 +1,415 @@ /*************************************************************************** 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 "selectedtransaction.h" +#include "selectedtransactions.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; } +namespace eWidgets { namespace eRegister { enum class Action; } } /** * 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 = MyMoneyAccount(), const QDate& reconciliationDate = QDate(), const MyMoneyMoney& endingBalance = MyMoneyMoney()); + 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(); /** * 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 slotNewTransaction(eWidgets::eRegister::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 3d317a4c3..4b48fa58e 100644 --- a/kmymoney/views/khomeview.cpp +++ b/kmymoney/views/khomeview.cpp @@ -1,2063 +1,2065 @@ /*************************************************************************** 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.cpp b/kmymoney/views/kmymoneyview.cpp index 1a90e4fab..4abf89047 100644 --- a/kmymoney/views/kmymoneyview.cpp +++ b/kmymoney/views/kmymoneyview.cpp @@ -1,2325 +1,2325 @@ /*************************************************************************** kmymoneyview.cpp ------------------- 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. * * * ***************************************************************************/ #include "kmymoneyview.h" // ---------------------------------------------------------------------------- // Std Includes #include // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #ifdef KF5Activities_FOUND #include #endif // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyglobalsettings.h" #include "kmymoneytitlelabel.h" #include #include "kcurrencyeditdlg.h" #include "mymoneyseqaccessmgr.h" #include "mymoneydatabasemgr.h" #include "imymoneystorageformat.h" #include "mymoneystoragebin.h" #include "mymoneyexception.h" #include "mymoneystoragexml.h" #include "mymoneystoragesql.h" #include "mymoneygncreader.h" #include "mymoneystorageanon.h" #include "khomeview.h" #include "kaccountsview.h" #include "kcategoriesview.h" #include "kinstitutionsview.h" #include "kpayeesview.h" #include "ktagsview.h" #include "kscheduledview.h" #include "kgloballedgerview.h" #include "simpleledgerview.h" #include "kinvestmentview.h" #include "kreportsview.h" #include "kbudgetview.h" #include "kforecastview.h" #include "konlinejoboutbox.h" #include "kmymoney.h" #include "models.h" #include "accountsmodel.h" #include "equitiesmodel.h" #include "securitiesmodel.h" #include "icons.h" #include "amountedit.h" #include "kmymoneyaccounttreeview.h" #include "accountsviewproxymodel.h" #include "mymoneybudget.h" #include "mymoneyprice.h" #include "mymoneyschedule.h" #include "mymoneysplit.h" #include "mymoneyaccount.h" #include "kmymoneyedit.h" #include "mymoneyfile.h" #include "mymoneysecurity.h" #include "mymoneyreport.h" #include "mymoneyenums.h" using namespace Icons; using namespace eMyMoney; static constexpr KCompressionDevice::CompressionType const& COMPRESSION_TYPE = KCompressionDevice::GZip; static constexpr char recoveryKeyId[] = "0xD2B08440"; typedef void(KMyMoneyView::*KMyMoneyViewFunc)(); KMyMoneyView::KMyMoneyView(KMyMoneyApp *kmymoney) : KPageWidget(nullptr), m_header(0), m_inConstructor(true), m_fileOpen(false), m_fmode(QFileDevice::ReadUser | QFileDevice::WriteUser), m_lastViewSelected(0) #ifdef KF5Activities_FOUND , m_activityResourceInstance(0) #endif { // this is a workaround for the bug in KPageWidget that causes the header to be shown // for a short while during page switch which causes a kind of bouncing of the page's // content and if the page's content is at it's minimum size then during a page switch // the main window's size is also increased to fit the header that is shown for a sort // period - reading the code in kpagewidget.cpp we know that the header should be at (1,1) // in a grid layout so if we find it there remove it for good to avoid the described issues QGridLayout* gridLayout = qobject_cast(layout()); if (gridLayout) { QLayoutItem* headerItem = gridLayout->itemAtPosition(1, 1); // make sure that we remove only the header - we avoid surprises if the header is not at (1,1) in the layout if (headerItem && qobject_cast(headerItem->widget()) != NULL) { gridLayout->removeItem(headerItem); // after we remove the KPageWidget standard header replace it with our own title label m_header = new KMyMoneyTitleLabel(this); m_header->setObjectName("titleLabel"); m_header->setMinimumSize(QSize(100, 30)); m_header->setRightImageFile("pics/titlelabel_background.png"); m_header->setVisible(KMyMoneyGlobalSettings::showTitleBar()); gridLayout->addWidget(m_header, 1, 1); } } newStorage(); m_model = new KPageWidgetModel(this); // cannot be parentless, otherwise segfaults at exit connect(kmymoney, SIGNAL(fileLoaded(QUrl)), this, SLOT(slotRefreshViews())); // let the accounts model know which account is being currently reconciled connect(this, SIGNAL(reconciliationStarts(MyMoneyAccount,QDate,MyMoneyMoney)), Models::instance()->accountsModel(), SLOT(slotReconcileAccount(MyMoneyAccount,QDate,MyMoneyMoney))); // Page 0 m_homeView = new KHomeView(); viewFrames[View::Home] = m_model->addPage(m_homeView, i18n("Home")); viewFrames[View::Home]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewHome])); connect(m_homeView, SIGNAL(ledgerSelected(QString,QString)), this, SLOT(slotLedgerSelected(QString,QString))); connect(m_homeView, SIGNAL(scheduleSelected(QString)), this, SLOT(slotScheduleSelected(QString))); connect(m_homeView, SIGNAL(reportSelected(QString)), this, SLOT(slotShowReport(QString))); connect(m_homeView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection); // Page 1 m_institutionsView = new KInstitutionsView; viewFrames[View::Institutions] = m_model->addPage(m_institutionsView, i18n("Institutions")); viewFrames[View::Institutions]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewInstitutions])); connect(m_institutionsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView); connect(m_institutionsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection); // Page 2 m_accountsView = new KAccountsView; viewFrames[View::Accounts] = m_model->addPage(m_accountsView, i18n("Accounts")); viewFrames[View::Accounts]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewAccounts])); connect(m_accountsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView); connect(m_accountsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection); // Page 3 m_scheduledView = new KScheduledView(); //this is to solve the way long strings are handled differently among versions of KPageWidget viewFrames[View::Schedules] = m_model->addPage(m_scheduledView, i18n("Scheduled transactions")); viewFrames[View::Schedules]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewSchedules])); connect(m_scheduledView, SIGNAL(scheduleSelected(MyMoneySchedule)), kmymoney, SLOT(slotSelectSchedule(MyMoneySchedule))); connect(m_scheduledView, SIGNAL(openContextMenu()), kmymoney, SLOT(slotShowScheduleContextMenu())); connect(m_scheduledView, SIGNAL(enterSchedule()), kmymoney, SLOT(slotScheduleEnter())); connect(m_scheduledView, SIGNAL(skipSchedule()), kmymoney, SLOT(slotScheduleSkip())); connect(m_scheduledView, SIGNAL(editSchedule()), kmymoney, SLOT(slotScheduleEdit())); connect(m_scheduledView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 4 m_categoriesView = new KCategoriesView; viewFrames[View::Categories] = m_model->addPage(m_categoriesView, i18n("Categories")); viewFrames[View::Categories]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewCategories])); connect(m_categoriesView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView); connect(m_categoriesView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection); // Page 5 m_tagsView = new KTagsView; viewFrames[View::Tags] = m_model->addPage(m_tagsView, i18n("Tags")); viewFrames[View::Tags]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewTags])); connect(m_tagsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView); connect(m_tagsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection); // Page 6 m_payeesView = new KPayeesView(); viewFrames[View::Payees] = m_model->addPage(m_payeesView, i18n("Payees")); viewFrames[View::Payees]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewPayees])); connect(kmymoney, SIGNAL(payeeCreated(QString)), m_payeesView, SLOT(slotSelectPayeeAndTransaction(QString))); connect(kmymoney, SIGNAL(payeeRename()), m_payeesView, SLOT(slotRenameButtonCliked())); connect(m_payeesView, SIGNAL(openContextMenu(MyMoneyObject)), kmymoney, SLOT(slotShowPayeeContextMenu())); connect(m_payeesView, SIGNAL(selectObjects(QList)), kmymoney, SLOT(slotSelectPayees(QList))); connect(m_payeesView, SIGNAL(transactionSelected(QString,QString)), this, SLOT(slotLedgerSelected(QString,QString))); connect(m_payeesView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 7 m_ledgerView = new KGlobalLedgerView(); viewFrames[View::Ledgers] = m_model->addPage(m_ledgerView, i18n("Ledgers")); viewFrames[View::Ledgers]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewLedgers])); connect(m_ledgerView, SIGNAL(accountSelected(MyMoneyObject)), kmymoney, SLOT(slotSelectAccount(MyMoneyObject))); connect(m_ledgerView, SIGNAL(openContextMenu()), kmymoney, SLOT(slotShowTransactionContextMenu())); connect(m_ledgerView, SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), kmymoney, SLOT(slotSelectTransactions(KMyMoneyRegister::SelectedTransactions))); connect(m_ledgerView, SIGNAL(newTransaction()), kmymoney, SLOT(slotTransactionsNew())); connect(m_ledgerView, SIGNAL(cancelOrEndEdit(bool&)), kmymoney, SLOT(slotTransactionsCancelOrEnter(bool&))); connect(m_ledgerView, SIGNAL(startEdit()), kmymoney, SLOT(slotTransactionsEdit())); connect(m_ledgerView, SIGNAL(endEdit()), kmymoney, SLOT(slotTransactionsEnter())); connect(m_ledgerView, SIGNAL(toggleReconciliationFlag()), kmymoney, SLOT(slotToggleReconciliationFlag())); connect(this, SIGNAL(reconciliationStarts(MyMoneyAccount,QDate,MyMoneyMoney)), m_ledgerView, SLOT(slotSetReconcileAccount(MyMoneyAccount,QDate,MyMoneyMoney))); connect(kmymoney, SIGNAL(selectAllTransactions()), m_ledgerView, SLOT(slotSelectAllTransactions())); connect(m_ledgerView, &KAccountsView::aboutToShow, this, &KMyMoneyView::resetViewSelection); // Page 8 m_investmentView = new KInvestmentView(kmymoney, this); viewFrames[View::Investments] = m_model->addPage(m_investmentView, i18n("Investments")); viewFrames[View::Investments]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewInvestment])); // Page 9 m_reportsView = new KReportsView(); viewFrames[View::Reports] = m_model->addPage(m_reportsView, i18n("Reports")); viewFrames[View::Reports]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewReports])); connect(m_reportsView, SIGNAL(ledgerSelected(QString,QString)), this, SLOT(slotLedgerSelected(QString,QString))); connect(m_reportsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection); // Page 10 m_budgetView = new KBudgetView(kmymoney); viewFrames[View::Budget] = m_model->addPage(m_budgetView, i18n("Budgets")); viewFrames[View::Budget]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewBudgets])); connect(m_budgetView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView); connect(m_budgetView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection); // Page 11 m_forecastView = new KForecastView(); viewFrames[View::Forecast] = m_model->addPage(m_forecastView, i18n("Forecast")); viewFrames[View::Forecast]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewForecast])); // Page 12 m_onlineJobOutboxView = new KOnlineJobOutbox(); viewFrames[View::OnlineJobOutbox] = m_model->addPage(m_onlineJobOutboxView, i18n("Outbox")); viewFrames[View::OnlineJobOutbox]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewOutbox])); connect(m_onlineJobOutboxView, SIGNAL(sendJobs(QList)), kmymoney, SLOT(slotOnlineJobSend(QList))); connect(m_onlineJobOutboxView, SIGNAL(editJob(QString)), kmymoney, SLOT(slotEditOnlineJob(QString))); connect(m_onlineJobOutboxView, SIGNAL(newCreditTransfer()), kmymoney, SLOT(slotNewOnlineTransfer())); connect(m_onlineJobOutboxView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); connect(m_onlineJobOutboxView, SIGNAL(showContextMenu(onlineJob)), kmymoney, SLOT(slotShowOnlineJobContextMenu())); SimpleLedgerView* view = new SimpleLedgerView(kmymoney, this); KPageWidgetItem* frame = m_model->addPage(view, i18n("New ledger")); frame->setIcon(QIcon::fromTheme(g_Icons[Icon::DocumentProperties])); //set the model setModel(m_model); setCurrentPage(viewFrames[View::Home]); connect(this, SIGNAL(currentPageChanged(QModelIndex,QModelIndex)), this, SLOT(slotCurrentPageChanged(QModelIndex,QModelIndex))); updateViewType(); m_inConstructor = false; // add fast switching of main views through Ctrl + NUM_X struct pageInfo { View view; KMyMoneyViewFunc callback; QString text; QKeySequence shortcut = QKeySequence(); }; const QVector pageInfos { {View::Home, &KMyMoneyView::slotShowHomePage, i18n("Show home page"), Qt::CTRL + Qt::Key_1}, {View::Institutions, &KMyMoneyView::slotShowInstitutionsPage, i18n("Show institutions page"), Qt::CTRL + Qt::Key_2}, {View::Accounts, &KMyMoneyView::slotShowAccountsPage, i18n("Show accounts page"), Qt::CTRL + Qt::Key_3}, {View::Schedules, &KMyMoneyView::slotShowSchedulesPage, i18n("Show scheduled transactions page"), Qt::CTRL + Qt::Key_4}, {View::Categories, &KMyMoneyView::slotShowCategoriesPage, i18n("Show categories page"), Qt::CTRL + Qt::Key_5}, {View::Tags, &KMyMoneyView::slotShowTagsPage, i18n("Show tags page"), }, {View::Payees, &KMyMoneyView::slotShowPayeesPage, i18n("Show payees page"), Qt::CTRL + Qt::Key_6}, {View::Ledgers, &KMyMoneyView::slotShowLedgersPage, i18n("Show ledgers page"), Qt::CTRL + Qt::Key_7}, {View::Investments, &KMyMoneyView::slotShowInvestmentsPage, i18n("Show investments page"), Qt::CTRL + Qt::Key_8}, {View::Reports, &KMyMoneyView::slotShowReportsPage, i18n("Show reports page"), Qt::CTRL + Qt::Key_9}, {View::Budget, &KMyMoneyView::slotShowBudgetPage, i18n("Show budget page"), }, {View::Forecast, &KMyMoneyView::slotShowForecastPage, i18n("Show forecast page"), }, {View::OnlineJobOutbox, &KMyMoneyView::slotShowOutboxPage, i18n("Show outbox page") } }; QHash lutActions; auto aC = kmymoney->actionCollection(); auto pageCount = 0; foreach (const pageInfo info, pageInfos) { auto 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(QLatin1String("ShowPage") + QString::number(pageCount++)); a->setText(info.text); connect(a, &QAction::triggered, this, info.callback); lutActions.insert(info.view, a); // store QAction's pointer for later processing if (!info.shortcut.isEmpty()) aC->setDefaultShortcut(a, info.shortcut); } aC->addActions(lutActions.values()); // Initialize kactivities resource instance #ifdef KF5Activities_FOUND m_activityResourceInstance = new KActivities::ResourceInstance(window()->winId(), this); connect(kmymoney, SIGNAL(fileLoaded(QUrl)), m_activityResourceInstance, SLOT(setUri(QUrl))); #endif } KMyMoneyView::~KMyMoneyView() { KMyMoneyGlobalSettings::setLastViewSelected(m_lastViewSelected); removeStorage(); } void KMyMoneyView::slotShowHomePage() { showPage(viewFrames[View::Home]); } void KMyMoneyView::slotShowInstitutionsPage() { showPage(viewFrames[View::Institutions]); m_institutionsView->setDefaultFocus(); } void KMyMoneyView::slotShowAccountsPage() { showPage(viewFrames[View::Accounts]); m_accountsView->setDefaultFocus(); } void KMyMoneyView::slotShowSchedulesPage() { showPage(viewFrames[View::Schedules]); m_scheduledView->setDefaultFocus(); } void KMyMoneyView::slotShowCategoriesPage() { showPage(viewFrames[View::Categories]); m_categoriesView->setDefaultFocus(); } void KMyMoneyView::slotShowTagsPage() { showPage(viewFrames[View::Tags]); m_tagsView->setDefaultFocus(); } void KMyMoneyView::slotShowPayeesPage() { showPage(viewFrames[View::Payees]); m_payeesView->setDefaultFocus(); } void KMyMoneyView::slotShowLedgersPage() { showPage(viewFrames[View::Ledgers]); m_ledgerView->setDefaultFocus(); } void KMyMoneyView::slotShowInvestmentsPage() { showPage(viewFrames[View::Investments]); m_investmentView->setDefaultFocus(); } void KMyMoneyView::slotShowReportsPage() { showPage(viewFrames[View::Reports]); m_reportsView->setDefaultFocus(); } void KMyMoneyView::slotShowBudgetPage() { showPage(viewFrames[View::Budget]); m_budgetView->setDefaultFocus(); } void KMyMoneyView::slotShowForecastPage() { showPage(viewFrames[View::Forecast]); m_forecastView->setDefaultFocus(); } void KMyMoneyView::slotShowOutboxPage() { showPage(viewFrames[View::OnlineJobOutbox]); m_onlineJobOutboxView->setDefaultFocus(); } void KMyMoneyView::showTitleBar(bool show) { if (m_header) m_header->setVisible(show); } void KMyMoneyView::updateViewType() { // set the face type KPageView::FaceType faceType = KPageView::List; switch (KMyMoneySettings::viewType()) { case 0: faceType = KPageView::List; break; case 1: faceType = KPageView::Tree; break; case 2: faceType = KPageView::Tabbed; break; } if (faceType != KMyMoneyView::faceType()) { setFaceType(faceType); if (faceType == KPageView::Tree) { QList views = findChildren(); foreach (QTreeView * view, views) { if (view && (view->parent() == this)) { view->setRootIsDecorated(false); break; } } } } } void KMyMoneyView::slotAccountTreeViewChanged(const eAccountsModel::Column column, const bool show) { QVector proxyModels {m_institutionsView->getProxyModel(), m_accountsView->getProxyModel(), m_categoriesView->getProxyModel(), m_budgetView->getProxyModel()}; for (auto i = proxyModels.count() - 1; i >= 0; --i) { // weed out unloaded views if (!proxyModels.at(i)) proxyModels.removeAt(i); } QString question; if (show) question = i18n("Do you want to show %1 column on every loaded view?", AccountsModel::getHeaderName(column)); else question = i18n("Do you want to hide %1 column on every loaded view?", AccountsModel::getHeaderName(column)); if (proxyModels.count() == 1 || // no need to ask what to do with other views because they aren't loaded KMessageBox::questionYesNo(this, question, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("ShowColumnOnEveryView")) == KMessageBox::Yes) { Models::instance()->accountsModel()->setColumnVisibility(column, show); Models::instance()->institutionsModel()->setColumnVisibility(column, show); foreach(AccountsViewProxyModel *proxyModel, proxyModels) { if (!proxyModel) continue; proxyModel->setColumnVisibility(column, show); proxyModel->invalidate(); } } } bool KMyMoneyView::showPageHeader() const { return false; } void KMyMoneyView::showPage(KPageWidgetItem* pageItem) { // reset all selected items before showing the selected view // but not while we're in our own constructor if (!m_inConstructor && pageItem != currentPage()) { kmymoney->slotResetSelections(); } // pretend we're in the constructor to avoid calling the // above resets. For some reason which I don't know the details // of, KJanusWidget::showPage() calls itself recursively. This // screws up the action handling, as items could have been selected // in the meantime. We prevent this by setting the m_inConstructor // to true and reset it to the previos value when we leave this method. bool prevConstructor = m_inConstructor; m_inConstructor = true; setCurrentPage(pageItem); m_inConstructor = prevConstructor; if (!m_inConstructor) { // fixup some actions that are dependant on the view // this does not work during construction kmymoney->slotUpdateActions(); } } bool KMyMoneyView::canPrint() { bool rc = ( viewFrames[View::Reports] == currentPage() || viewFrames[View::Home] == currentPage() ); return rc; } bool KMyMoneyView::canCreateTransactions(const KMyMoneyRegister::SelectedTransactions& /* list */, QString& tooltip) const { // we can only create transactions in the ledger view so // we check that this is the active page bool rc = (viewFrames[View::Ledgers] == currentPage()); if (rc) rc = m_ledgerView->canCreateTransactions(tooltip); else tooltip = i18n("Creating transactions can only be performed in the ledger view"); return rc; } bool KMyMoneyView::canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { // we can only modify transactions in the ledger view so // we check that this is the active page bool rc = (viewFrames[View::Ledgers] == currentPage()); if (rc) { rc = m_ledgerView->canModifyTransactions(list, tooltip); } else { tooltip = i18n("Modifying transactions can only be performed in the ledger view"); } return rc; } bool KMyMoneyView::canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { // we can only duplicate transactions in the ledger view so // we check that this is the active page bool rc = (viewFrames[View::Ledgers] == currentPage()); if (rc) { rc = m_ledgerView->canDuplicateTransactions(list, tooltip); } else { tooltip = i18n("Duplicating transactions can only be performed in the ledger view"); } return rc; } bool KMyMoneyView::canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { bool rc; // we can only edit transactions in the ledger view so // we check that this is the active page if ((rc = canModifyTransactions(list, tooltip)) == true) { tooltip = i18n("Edit the current selected transactions"); rc = m_ledgerView->canEditTransactions(list, tooltip); } return rc; } bool KMyMoneyView::createNewTransaction() { bool rc = false; KMyMoneyRegister::SelectedTransactions list; QString txt; if (canCreateTransactions(list, txt)) { rc = m_ledgerView->selectEmptyTransaction(); } return rc; } TransactionEditor* KMyMoneyView::startEdit(const KMyMoneyRegister::SelectedTransactions& list) { TransactionEditor* editor = 0; QString txt; if (canEditTransactions(list, txt) || canCreateTransactions(list, txt)) { editor = m_ledgerView->startEdit(list); } return editor; } void KMyMoneyView::newStorage(storageTypeE t) { removeStorage(); MyMoneyFile* file = MyMoneyFile::instance(); if (t == Memory) file->attachStorage(new MyMoneySeqAccessMgr); else file->attachStorage(new MyMoneyDatabaseMgr); } void KMyMoneyView::removeStorage() { MyMoneyFile* file = MyMoneyFile::instance(); IMyMoneyStorage* p = file->storage(); if (p != 0) { file->detachStorage(p); delete p; } } void KMyMoneyView::enableViewsIfFileOpen() { // call set enabled only if the state differs to avoid widgets 'bouncing on the screen' while doing this if (viewFrames[View::Accounts]->isEnabled() != m_fileOpen) viewFrames[View::Accounts]->setEnabled(m_fileOpen); if (viewFrames[View::Institutions]->isEnabled() != m_fileOpen) viewFrames[View::Institutions]->setEnabled(m_fileOpen); if (viewFrames[View::Schedules]->isEnabled() != m_fileOpen) viewFrames[View::Schedules]->setEnabled(m_fileOpen); if (viewFrames[View::Categories]->isEnabled() != m_fileOpen) viewFrames[View::Categories]->setEnabled(m_fileOpen); if (viewFrames[View::Payees]->isEnabled() != m_fileOpen) viewFrames[View::Payees]->setEnabled(m_fileOpen); if (viewFrames[View::Tags]->isEnabled() != m_fileOpen) viewFrames[View::Tags]->setEnabled(m_fileOpen); if (viewFrames[View::Budget]->isEnabled() != m_fileOpen) viewFrames[View::Budget]->setEnabled(m_fileOpen); if (viewFrames[View::Ledgers]->isEnabled() != m_fileOpen) viewFrames[View::Ledgers]->setEnabled(m_fileOpen); if (viewFrames[View::Investments]->isEnabled() != m_fileOpen) viewFrames[View::Investments]->setEnabled(m_fileOpen); if (viewFrames[View::Reports]->isEnabled() != m_fileOpen) viewFrames[View::Reports]->setEnabled(m_fileOpen); if (viewFrames[View::Forecast]->isEnabled() != m_fileOpen) viewFrames[View::Forecast]->setEnabled(m_fileOpen); if (viewFrames[View::OnlineJobOutbox]->isEnabled() != m_fileOpen) viewFrames[View::OnlineJobOutbox]->setEnabled(m_fileOpen); emit viewStateChanged(m_fileOpen); } void KMyMoneyView::slotLedgerSelected(const QString& _accId, const QString& transaction) { MyMoneyAccount acc = MyMoneyFile::instance()->account(_accId); QString accId(_accId); switch (acc.accountType()) { case Account::Stock: // if a stock account is selected, we show the // the corresponding parent (investment) account acc = MyMoneyFile::instance()->account(acc.parentAccountId()); accId = acc.id(); // intentional fall through case Account::Checkings: case Account::Savings: case Account::Cash: case Account::CreditCard: case Account::Loan: case Account::Asset: case Account::Liability: case Account::AssetLoan: case Account::Income: case Account::Expense: case Account::Investment: case Account::Equity: setCurrentPage(viewFrames[View::Ledgers]); m_ledgerView->slotSelectAccount(accId, transaction); break; case Account::CertificateDep: case Account::MoneyMarket: case Account::Currency: qDebug("No ledger view available for account type %d", (int)acc.accountType()); break; default: qDebug("Unknown account type %d in KMyMoneyView::slotLedgerSelected", (int)acc.accountType()); break; } } void KMyMoneyView::slotPayeeSelected(const QString& payee, const QString& account, const QString& transaction) { showPage(viewFrames[View::Payees]); m_payeesView->slotSelectPayeeAndTransaction(payee, account, transaction); } void KMyMoneyView::slotTagSelected(const QString& tag, const QString& account, const QString& transaction) { showPage(viewFrames[View::Tags]); m_tagsView->slotSelectTagAndTransaction(tag, account, transaction); } void KMyMoneyView::slotScheduleSelected(const QString& scheduleId) { MyMoneySchedule sched = MyMoneyFile::instance()->schedule(scheduleId); kmymoney->slotSelectSchedule(sched); } void KMyMoneyView::slotShowReport(const QString& reportid) { showPage(viewFrames[View::Reports]); m_reportsView->slotOpenReport(reportid); } void KMyMoneyView::slotShowReport(const MyMoneyReport& report) { showPage(viewFrames[View::Reports]); m_reportsView->slotOpenReport(report); } bool KMyMoneyView::fileOpen() { return m_fileOpen; } void KMyMoneyView::closeFile() { if (m_reportsView) m_reportsView->slotCloseAll(); // disconnect the signals disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded, Models::instance()->accountsModel(), &AccountsModel::slotObjectAdded); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectModified, Models::instance()->accountsModel(), &AccountsModel::slotObjectModified); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved, Models::instance()->accountsModel(), &AccountsModel::slotObjectRemoved); disconnect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged, Models::instance()->accountsModel(), &AccountsModel::slotBalanceOrValueChanged); disconnect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged, Models::instance()->accountsModel(), &AccountsModel::slotBalanceOrValueChanged); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded, Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectAdded); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectModified, Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectModified); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved, Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectRemoved); disconnect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged, Models::instance()->institutionsModel(), &AccountsModel::slotBalanceOrValueChanged); disconnect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged, Models::instance()->institutionsModel(), &AccountsModel::slotBalanceOrValueChanged); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded, Models::instance()->equitiesModel(), &EquitiesModel::slotObjectAdded); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectModified, Models::instance()->equitiesModel(), &EquitiesModel::slotObjectModified); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved, Models::instance()->equitiesModel(), &EquitiesModel::slotObjectRemoved); disconnect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged, Models::instance()->equitiesModel(), &EquitiesModel::slotBalanceOrValueChanged); disconnect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged, Models::instance()->equitiesModel(), &EquitiesModel::slotBalanceOrValueChanged); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded, Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectAdded); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectModified, Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectModified); disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved, Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectRemoved); disconnect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, m_homeView, &KHomeView::slotLoadView); // notify the models that the file is going to be closed (we should have something like dataChanged that reaches the models first) Models::instance()->fileClosed(); emit kmmFilePlugin(preClose); if (isDatabase()) MyMoneyFile::instance()->storage()->close(); // to log off a database user newStorage(); slotShowHomePage(); emit kmmFilePlugin(postClose); m_fileOpen = false; emit fileClosed(); } void KMyMoneyView::ungetString(QIODevice *qfile, char *buf, int len) { buf = &buf[len-1]; while (len--) { qfile->ungetChar(*buf--); } } bool KMyMoneyView::readFile(const QUrl &url) { QString filename; m_fileOpen = false; bool isEncrypted = false; IMyMoneyStorageFormat* pReader = 0; if (!url.isValid()) { qDebug("Invalid URL '%s'", qPrintable(url.url())); return false; } // disconnect the current storga manager from the engine MyMoneyFile::instance()->detachStorage(); if (url.scheme() == QLatin1String("sql")) { // handle reading of database m_fileType = KmmDb; // get rid of the mode parameter which is now redundant QUrl newUrl(url); QUrlQuery query(url); query.removeQueryItem("mode"); newUrl.setQuery(query); return (openDatabase(newUrl)); // on error, any message will have been displayed } IMyMoneyStorage *storage = new MyMoneySeqAccessMgr; if (url.isLocalFile()) { filename = url.toLocalFile(); } else { // TODO: port to kf5 (NetAccess) #if 0 if (!KIO::NetAccess::download(url, filename, 0)) { KMessageBox::detailedError(this, i18n("Error while loading file '%1'.", url.url()), KIO::NetAccess::lastErrorString(), i18n("File access error")); return false; } #endif } // let's glimps into the file to figure out, if it's one // of the old (uncompressed) or new (compressed) files. QFile file(filename); QFileInfo info(file); if (!info.isFile()) { QString msg = i18n("

%1 is not a KMyMoney file.

", filename); KMessageBox::error(this, msg, i18n("Filetype Error")); return false; } m_fmode = QFileDevice::ReadUser | QFileDevice::WriteUser; m_fmode |= info.permissions(); bool rc = true; // There's a problem with the KFilterDev and KGPGFile classes: // One supports the at(n) member but not ungetch() together with // read() and the other does not provide an at(n) method but // supports read() that considers the ungetch() buffer. QFile // supports everything so this is not a problem. We solve the problem // for now by keeping track of which method can be used. bool haveAt = true; emit kmmFilePlugin(preOpen); if (file.open(QIODevice::ReadOnly)) { QByteArray hdr(2, '\0'); int cnt; cnt = file.read(hdr.data(), 2); file.close(); if (cnt == 2) { QIODevice* qfile = nullptr; if (QString(hdr) == QString("\037\213")) { // gzipped? qfile = new KCompressionDevice(filename, COMPRESSION_TYPE); } else if (QString(hdr) == QString("--") // PGP ASCII armored? || QString(hdr) == QString("\205\001") // PGP binary? || QString(hdr) == QString("\205\002")) { // PGP binary? if (KGPGFile::GPGAvailable()) { qfile = new KGPGFile(filename); haveAt = false; isEncrypted = true; } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("GPG is not available for decryption of file %1", filename))); qfile = new QFile(file.fileName()); } } else { // we can't use file directly, as we delete qfile later on qfile = new QFile(file.fileName()); } if (qfile->open(QIODevice::ReadOnly)) { try { hdr.resize(8); if (qfile->read(hdr.data(), 8) == 8) { if (haveAt) qfile->seek(0); else ungetString(qfile, hdr.data(), 8); // Ok, we got the first block of 8 bytes. Read in the two // unsigned long int's by preserving endianess. This is // achieved by reading them through a QDataStream object qint32 magic0, magic1; QDataStream s(&hdr, QIODevice::ReadOnly); s >> magic0; s >> magic1; // If both magic numbers match (we actually read in the // text 'KMyMoney' then we assume a binary file and // construct a reader for it. Otherwise, we construct // an XML reader object. // // The expression magic0 < 30 is only used to create // a binary reader if we assume an old binary file. This // should be removed at some point. An alternative is to // check the beginning of the file against an pattern // of the XML file (e.g. '?read(hdr.data(), 70) == 70) { if (haveAt) qfile->seek(0); else ungetString(qfile, hdr.data(), 70); QRegExp kmyexp(""); QRegExp gncexp("attachStorage(storage); loadAllCurrencies(); // currency list required for gnc MyMoneyFile::instance()->detachStorage(storage); pReader = new MyMoneyGncReader; m_fileType = GncXML; } } } if (pReader) { pReader->setProgressCallback(&KMyMoneyView::progressCallback); pReader->readFile(qfile, dynamic_cast(storage)); } else { if (m_fileType == KmmBinary) { KMessageBox::sorry(this, QString("%1"). arg(i18n("File %1 contains the old binary format used by KMyMoney. Please use an older version of KMyMoney (0.8.x) that still supports this format to convert it to the new XML based format.", filename))); } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("File %1 contains an unknown file format.", filename))); } rc = false; } } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("Cannot read from file %1.", filename))); rc = false; } } catch (const MyMoneyException &e) { KMessageBox::sorry(this, QString("%1"). arg(i18n("Cannot load file %1. Reason: %2", filename, e.what()))); rc = false; } if (pReader) { pReader->setProgressCallback(0); delete pReader; } qfile->close(); } else { KGPGFile *gpgFile = qobject_cast(qfile); if (gpgFile && !gpgFile->errorToString().isEmpty()) { KMessageBox::sorry(this, QString("%1"). arg(i18n("The following error was encountered while decrypting file %1: %2", filename, gpgFile->errorToString()))); } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("File %1 not found.", filename))); } rc = false; } delete qfile; } } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("File %1 not found.", filename))); rc = false; } // things are finished, now we connect the storage to the engine // which forces a reload of the cache in the engine with those // objects that are cached MyMoneyFile::instance()->attachStorage(storage); if (rc == false) return rc; // encapsulate transactions to the engine to be able to commit/rollback MyMoneyFileTransaction ft; // make sure we setup the encryption key correctly if (isEncrypted && MyMoneyFile::instance()->value("kmm-encryption-key").isEmpty()) { MyMoneyFile::instance()->setValue("kmm-encryption-key", KMyMoneyGlobalSettings::gpgRecipientList().join(",")); } // make sure we setup the name of the base accounts in translated form try { MyMoneyFile *file = MyMoneyFile::instance(); checkAccountName(file->asset(), i18n("Asset")); checkAccountName(file->liability(), i18n("Liability")); checkAccountName(file->income(), i18n("Income")); checkAccountName(file->expense(), i18n("Expense")); checkAccountName(file->equity(), i18n("Equity")); ft.commit(); } catch (const MyMoneyException &) { } // if a temporary file was constructed by NetAccess::download, // then it will be removed with the next call. Otherwise, it // stays untouched on the local filesystem // TODO: port to KF5 (NetAccess) //KIO::NetAccess::removeTempFile(filename); return initializeStorage(); } void KMyMoneyView::checkAccountName(const MyMoneyAccount& _acc, const QString& name) const { MyMoneyFile* file = MyMoneyFile::instance(); if (_acc.name() != name) { MyMoneyAccount acc(_acc); acc.setName(name); file->modifyAccount(acc); } } bool KMyMoneyView::openDatabase(const QUrl &url) { m_fileOpen = false; // open the database IMyMoneySerialize* pStorage = dynamic_cast(MyMoneyFile::instance()->storage()); MyMoneyDatabaseMgr* pDBMgr = 0; if (! pStorage) { pDBMgr = new MyMoneyDatabaseMgr; pStorage = dynamic_cast(pDBMgr); } QExplicitlySharedDataPointer reader = pStorage->connectToDatabase(url); QUrl dbURL(url); bool retry = true; while (retry) { switch (reader->open(dbURL, QIODevice::ReadWrite)) { case 0: // opened okay retry = false; break; case 1: // permanent error KMessageBox::detailedError(this, i18n("Cannot open database %1\n", dbURL.toDisplayString()), reader->lastError()); if (pDBMgr) { removeStorage(); delete pDBMgr; } return false; case -1: // retryable error if (KMessageBox::warningYesNo(this, reader->lastError(), PACKAGE) == KMessageBox::No) { if (pDBMgr) { removeStorage(); delete pDBMgr; } return false; } else { QUrlQuery query(dbURL); const QString optionKey = QLatin1String("options"); QString options = query.queryItemValue(optionKey); if(!options.isEmpty()) { options += QLatin1Char(','); } options += QLatin1String("override"); query.removeQueryItem(QLatin1String("mode")); query.removeQueryItem(optionKey); query.addQueryItem(optionKey, options); dbURL.setQuery(query); } } } if (pDBMgr) { removeStorage(); MyMoneyFile::instance()->attachStorage(pDBMgr); } // single user mode; read some of the data into memory // FIXME - readFile no longer relevant? // tried removing it but then got no indication that loading was complete // also, didn't show home page reader->setProgressCallback(&KMyMoneyView::progressCallback); if (!reader->readFile()) { KMessageBox::detailedError(0, i18n("An unrecoverable error occurred while reading the database"), reader->lastError().toLatin1(), i18n("Database malfunction")); return false; } m_fileOpen = true; reader->setProgressCallback(0); return initializeStorage(); } bool KMyMoneyView::initializeStorage() { bool blocked = MyMoneyFile::instance()->signalsBlocked(); MyMoneyFile::instance()->blockSignals(true); // we check, if we have any currency in the file. If not, we load // all the default currencies we know. MyMoneyFileTransaction ft; try { updateCurrencyNames(); ft.commit(); } catch (const MyMoneyException &) { MyMoneyFile::instance()->blockSignals(blocked); return false; } // make sure, we have a base currency and all accounts are // also assigned to a currency. QString baseId; try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug() << e.what(); } if (baseId.isEmpty()) { // Stay in this endless loop until we have a base currency, // as without it the application does not work anymore. while (baseId.isEmpty()) { selectBaseCurrency(); try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug() << e.what(); } } } else { // in some odd intermediate cases there could be files out there // that have a base currency set, but still have accounts that // do not have a base currency assigned. This call will take // care of it. We can safely remove it later. // // Another work-around for this scenario is to remove the base // currency setting from the XML file by removing the line // // // // and restart the application with this file. This will force to // run the above loop. selectBaseCurrency(); } // setup the standard precision AmountEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); - kMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); + KMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); KSharedConfigPtr config = KSharedConfig::openConfig(); KPageWidgetItem* page; KConfigGroup grp = config->group("General Options"); if (KMyMoneyGlobalSettings::startLastViewSelected() != 0) page = viewFrames.value(static_cast(KMyMoneyGlobalSettings::lastViewSelected())); else page = viewFrames[View::Home]; // For debugging purposes, we can turn off the automatic fix manually // by setting the entry in kmymoneyrc to true grp = config->group("General Options"); if (grp.readEntry("SkipFix", false) != true) { MyMoneyFileTransaction ft; try { // Check if we have to modify the file before we allow to work with it IMyMoneyStorage* s = MyMoneyFile::instance()->storage(); while (s->fileFixVersion() < s->currentFixVersion()) { qDebug("%s", qPrintable((QString("testing fileFixVersion %1 < %2").arg(s->fileFixVersion()).arg(s->currentFixVersion())))); switch (s->fileFixVersion()) { case 0: fixFile_0(); s->setFileFixVersion(1); break; case 1: fixFile_1(); s->setFileFixVersion(2); break; case 2: fixFile_2(); s->setFileFixVersion(3); break; case 3: fixFile_3(); s->setFileFixVersion(4); break; // add new levels above. Don't forget to increase currentFixVersion() for all // the storage backends this fix applies to default: throw MYMONEYEXCEPTION(i18n("Unknown fix level in input file")); } } ft.commit(); } catch (const MyMoneyException &) { MyMoneyFile::instance()->blockSignals(blocked); return false; } } else { qDebug("Skipping automatic transaction fix!"); } MyMoneyFile::instance()->blockSignals(blocked); // FIXME: we need to check, if it's necessary to have this // automatic funcitonality // if there's no asset account, then automatically start the // new account wizard // kmymoney->createInitialAccount(); m_fileOpen = true; emit kmmFilePlugin(postOpen); Models::instance()->fileOpened(); // connect the needed signals connect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded, Models::instance()->accountsModel(), &AccountsModel::slotObjectAdded); connect(MyMoneyFile::instance(), &MyMoneyFile::objectModified, Models::instance()->accountsModel(), &AccountsModel::slotObjectModified); connect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved, Models::instance()->accountsModel(), &AccountsModel::slotObjectRemoved); connect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged, Models::instance()->accountsModel(), &AccountsModel::slotBalanceOrValueChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged, Models::instance()->accountsModel(), &AccountsModel::slotBalanceOrValueChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded, Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectAdded); connect(MyMoneyFile::instance(), &MyMoneyFile::objectModified, Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectModified); connect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved, Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectRemoved); connect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged, Models::instance()->institutionsModel(), &AccountsModel::slotBalanceOrValueChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged, Models::instance()->institutionsModel(), &AccountsModel::slotBalanceOrValueChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded, Models::instance()->equitiesModel(), &EquitiesModel::slotObjectAdded); connect(MyMoneyFile::instance(), &MyMoneyFile::objectModified, Models::instance()->equitiesModel(), &EquitiesModel::slotObjectModified); connect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved, Models::instance()->equitiesModel(), &EquitiesModel::slotObjectRemoved); connect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged, Models::instance()->equitiesModel(), &EquitiesModel::slotBalanceOrValueChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged, Models::instance()->equitiesModel(), &EquitiesModel::slotBalanceOrValueChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded, Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectAdded); connect(MyMoneyFile::instance(), &MyMoneyFile::objectModified, Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectModified); connect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved, Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectRemoved); // inform everyone about new data MyMoneyFile::instance()->preloadCache(); MyMoneyFile::instance()->forceDataChanged(); // views can wait since they are going to be refresed in slotRefreshViews connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, m_homeView, &KHomeView::slotLoadView); // if we currently see a different page, then select the right one if (page != currentPage()) { showPage(page); } emit fileOpened(); return true; } void KMyMoneyView::saveToLocalFile(const QString& localFile, IMyMoneyStorageFormat* pWriter, bool plaintext, const QString& keyList) { // Check GPG encryption bool encryptFile = true; bool encryptRecover = false; if (!keyList.isEmpty()) { if (!KGPGFile::GPGAvailable()) { KMessageBox::sorry(this, i18n("GPG does not seem to be installed on your system. Please make sure that GPG can be found using the standard search path. This time, encryption is disabled."), i18n("GPG not found")); encryptFile = false; } else { if (KMyMoneyGlobalSettings::encryptRecover()) { encryptRecover = true; if (!KGPGFile::keyAvailable(QString(recoveryKeyId))) { KMessageBox::sorry(this, i18n("

You have selected to encrypt your data also with the KMyMoney recover key, but the key with id

%1

has not been found in your keyring at this time. Please make sure to import this key into your keyring. You can find it on the KMyMoney web-site. This time your data will not be encrypted with the KMyMoney recover key.

", QString(recoveryKeyId)), i18n("GPG Key not found")); encryptRecover = false; } } for(const QString& key: keyList.split(',', QString::SkipEmptyParts)) { if (!KGPGFile::keyAvailable(key)) { KMessageBox::sorry(this, i18n("

You have specified to encrypt your data for the user-id

%1.

Unfortunately, a valid key for this user-id was not found in your keyring. Please make sure to import a valid key for this user-id. This time, encryption is disabled.

", key), i18n("GPG Key not found")); encryptFile = false; break; } } if (encryptFile == true) { QString msg = i18n("

You have configured to save your data in encrypted form using GPG. Make sure you understand that you might lose all your data if you encrypt it, but cannot decrypt it later on. If unsure, answer No.

"); if (KMessageBox::questionYesNo(this, msg, i18n("Store GPG encrypted"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "StoreEncrypted") == KMessageBox::No) { encryptFile = false; } } } } // Create a temporary file if needed QString writeFile = localFile; QTemporaryFile tmpFile; if (QFile::exists(localFile)) { tmpFile.open(); writeFile = tmpFile.fileName(); tmpFile.close(); } /** * @brief Automatically restore settings when scope is left */ struct restorePreviousSettingsHelper { restorePreviousSettingsHelper() : m_signalsWereBlocked{MyMoneyFile::instance()->signalsBlocked()} { MyMoneyFile::instance()->blockSignals(true); } ~restorePreviousSettingsHelper() { MyMoneyFile::instance()->blockSignals(m_signalsWereBlocked); } const bool m_signalsWereBlocked; } restoreHelper; MyMoneyFileTransaction ft; MyMoneyFile::instance()->deletePair("kmm-encryption-key"); std::unique_ptr device; if (!keyList.isEmpty() && encryptFile && !plaintext) { std::unique_ptr kgpg = std::unique_ptr(new KGPGFile{writeFile}); if (kgpg) { for(const QString& key: keyList.split(',', QString::SkipEmptyParts)) { kgpg->addRecipient(key.toLatin1()); } if (encryptRecover) { kgpg->addRecipient(recoveryKeyId); } MyMoneyFile::instance()->setValue("kmm-encryption-key", keyList); device = std::unique_ptr(kgpg.release()); } } else { QFile *file = new QFile(writeFile); // The second parameter of KCompressionDevice means that KCompressionDevice will delete the QFile object device = std::unique_ptr(new KCompressionDevice{file, true, (plaintext) ? KCompressionDevice::None : COMPRESSION_TYPE}); } ft.commit(); if (!device || !device->open(QIODevice::WriteOnly)) { throw MYMONEYEXCEPTION(i18n("Unable to open file '%1' for writing.", localFile)); } pWriter->setProgressCallback(&KMyMoneyView::progressCallback); pWriter->writeFile(device.get(), dynamic_cast(MyMoneyFile::instance()->storage())); device->close(); // Check for errors if possible, only possible for KGPGFile QFileDevice *fileDevice = qobject_cast(device.get()); if (fileDevice && fileDevice->error() != QFileDevice::NoError) { throw MYMONEYEXCEPTION(i18n("Failure while writing to '%1'", localFile)); } if (writeFile != localFile) { // This simple comparison is possible because the strings are equal if no temporary file was created. // If a temporary file was created, it is made in a way that the name is definitely different. So no // symlinks etc. have to be evaluated. if (!QFile::remove(localFile) || !QFile::rename(writeFile, localFile)) throw MYMONEYEXCEPTION(i18n("Failure while writing to '%1'", localFile)); } QFile::setPermissions(localFile, m_fmode); pWriter->setProgressCallback(0); } bool KMyMoneyView::saveFile(const QUrl &url, const QString& keyList) { QString filename = url.path(); if (!fileOpen()) { KMessageBox::error(this, i18n("Tried to access a file when it has not been opened")); return false; } emit kmmFilePlugin(preSave); std::unique_ptr storageWriter; // If this file ends in ".ANON.XML" then this should be written using the // anonymous writer. bool plaintext = filename.right(4).toLower() == ".xml"; if (filename.right(9).toLower() == ".anon.xml") { //! @todo C++14: use std::make_unique, also some lines below storageWriter = std::unique_ptr(new MyMoneyStorageANON); } else { storageWriter = std::unique_ptr(new MyMoneyStorageXML); } // actually, url should be the parameter to this function // but for now, this would involve too many changes bool rc = true; try { if (! url.isValid()) { throw MYMONEYEXCEPTION(i18n("Malformed URL '%1'", url.url())); } if (url.isLocalFile()) { filename = url.toLocalFile(); try { const unsigned int nbak = KMyMoneyGlobalSettings::autoBackupCopies(); if (nbak) { KBackup::numberedBackupFile(filename, QString(), QString::fromLatin1("~"), nbak); } saveToLocalFile(filename, storageWriter.get(), plaintext, keyList); } catch (const MyMoneyException &) { throw MYMONEYEXCEPTION(i18n("Unable to write changes to '%1'", filename)); } } else { QTemporaryFile tmpfile; tmpfile.open(); // to obtain the name tmpfile.close(); saveToLocalFile(tmpfile.fileName(), storageWriter.get(), plaintext, keyList); // TODO: port to kf5 //if (!KIO::NetAccess::upload(tmpfile.fileName(), url, 0)) // throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'", url.toDisplayString())); } m_fileType = KmmXML; } catch (const MyMoneyException &e) { KMessageBox::error(this, e.what()); MyMoneyFile::instance()->setDirty(); rc = false; } emit kmmFilePlugin(postSave); return rc; } bool KMyMoneyView::saveAsDatabase(const QUrl &url) { bool rc = false; if (!fileOpen()) { KMessageBox::error(this, i18n("Tried to access a file when it has not been opened")); return (rc); } MyMoneyStorageSql *writer = new MyMoneyStorageSql(dynamic_cast(MyMoneyFile::instance()->storage()), url); bool canWrite = false; switch (writer->open(url, QIODevice::WriteOnly)) { case 0: canWrite = true; break; case -1: // dbase already has data, see if he wants to clear it out if (KMessageBox::warningContinueCancel(0, i18n("Database contains data which must be removed before using Save As.\n" "Do you wish to continue?"), "Database not empty") == KMessageBox::Continue) { if (writer->open(url, QIODevice::WriteOnly, true) == 0) canWrite = true; } else { delete writer; return false; } break; } if (canWrite) { writer->setProgressCallback(&KMyMoneyView::progressCallback); if (!writer->writeFile()) { KMessageBox::detailedError(0, i18n("An unrecoverable error occurred while writing to the database.\n" "It may well be corrupt."), writer->lastError().toLatin1(), i18n("Database malfunction")); rc = false; } writer->setProgressCallback(0); rc = true; } else { KMessageBox::detailedError(this, i18n("Cannot open or create database %1.\n" "Retry Save As Database and click Help" " for further info.", url.toDisplayString()), writer->lastError()); } delete writer; return (rc); } bool KMyMoneyView::dirty() { if (!fileOpen()) return false; return MyMoneyFile::instance()->dirty(); } bool KMyMoneyView::startReconciliation(const MyMoneyAccount& account, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance) { bool ok = true; // we cannot reconcile standard accounts if (MyMoneyFile::instance()->isStandardAccount(account.id())) ok = false; // check if we can reconcile this account // it makes sense for asset and liability accounts only if (ok == true) { if (account.isAssetLiability()) { showPage(viewFrames[View::Ledgers]); // prepare reconciliation mode emit reconciliationStarts(account, reconciliationDate, endingBalance); } else { ok = false; } } return ok; } void KMyMoneyView::finishReconciliation(const MyMoneyAccount& /* account */) { emit reconciliationStarts(MyMoneyAccount(), QDate(), MyMoneyMoney()); } void KMyMoneyView::newFile() { closeFile(); m_fileType = KmmXML; // assume native type until saved m_fileOpen = true; } void KMyMoneyView::slotSetBaseCurrency(const MyMoneySecurity& baseCurrency) { if (!baseCurrency.id().isEmpty()) { QString baseId; try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); } if (baseCurrency.id() != baseId) { MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->setBaseCurrency(baseCurrency); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot set %1 as base currency: %2", baseCurrency.name(), e.what()), i18n("Set base currency")); } } AmountEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); - kMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); + KMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); } } void KMyMoneyView::selectBaseCurrency() { MyMoneyFile* file = MyMoneyFile::instance(); // check if we have a base currency. If not, we need to select one QString baseId; try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); } if (baseId.isEmpty()) { QPointer dlg = new KCurrencyEditDlg(this); connect(dlg, SIGNAL(selectBaseCurrency(MyMoneySecurity)), this, SLOT(slotSetBaseCurrency(MyMoneySecurity))); dlg->exec(); delete dlg; } try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); } if (!baseId.isEmpty()) { // check that all accounts have a currency QList list; file->accountList(list); QList::Iterator it; // don't forget those standard accounts list << file->asset(); list << file->liability(); list << file->income(); list << file->expense(); list << file->equity(); for (it = list.begin(); it != list.end(); ++it) { QString cid; try { if (!(*it).currencyId().isEmpty() || (*it).currencyId().length() != 0) cid = MyMoneyFile::instance()->currency((*it).currencyId()).id(); } catch (const MyMoneyException &) { } if (cid.isEmpty()) { (*it).setCurrencyId(baseId); MyMoneyFileTransaction ft; try { file->modifyAccount(*it); ft.commit(); } catch (const MyMoneyException &e) { qDebug("Unable to setup base currency in account %s (%s): %s", qPrintable((*it).name()), qPrintable((*it).id()), qPrintable(e.what())); } } } } } void KMyMoneyView::updateCurrencyNames() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; QList storedCurrencies = MyMoneyFile::instance()->currencyList(); QList availableCurrencies = MyMoneyFile::instance()->availableCurrencyList(); QStringList currencyIDs; foreach (auto currency, availableCurrencies) currencyIDs.append(currency.id()); try { foreach (auto currency, storedCurrencies) { int i = currencyIDs.indexOf(currency.id()); if (i != -1 && availableCurrencies.at(i).name() != currency.name()) { currency.setName(availableCurrencies.at(i).name()); file->modifyCurrency(currency); } } ft.commit(); } catch (const MyMoneyException &e) { qDebug("Error %s updating currency names", qPrintable(e.what())); } } void KMyMoneyView::loadAllCurrencies() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; if (!file->currencyList().isEmpty()) return; QMap ancientCurrencies = file->ancientCurrencies(); try { foreach (auto currency, file->availableCurrencyList()) { file->addCurrency(currency); MyMoneyPrice price = ancientCurrencies.value(currency, MyMoneyPrice()); if (price != MyMoneyPrice()) file->addPrice(price); } ft.commit(); } catch (const MyMoneyException &e) { qDebug("Error %s loading currency", qPrintable(e.what())); } } void KMyMoneyView::viewAccountList(const QString& /*selectAccount*/) { if (viewFrames[View::Accounts] != currentPage()) showPage(viewFrames[View::Accounts]); m_accountsView->show(); } void KMyMoneyView::slotRefreshViews() { // turn off sync between ledger and investment view disconnect(m_investmentView, SIGNAL(accountSelected(MyMoneyObject)), m_ledgerView, SLOT(slotSelectAccount(MyMoneyObject))); disconnect(m_ledgerView, SIGNAL(accountSelected(MyMoneyObject)), m_investmentView, SLOT(slotSelectAccount(MyMoneyObject))); // TODO turn sync between ledger and investment view if selected by user if (KMyMoneyGlobalSettings::syncLedgerInvestment()) { connect(m_investmentView, SIGNAL(accountSelected(MyMoneyObject)), m_ledgerView, SLOT(slotSelectAccount(MyMoneyObject))); connect(m_ledgerView, SIGNAL(accountSelected(MyMoneyObject)), m_investmentView, SLOT(slotSelectAccount(MyMoneyObject))); } showTitleBar(KMyMoneyGlobalSettings::showTitleBar()); m_accountsView->refresh(); m_institutionsView->refresh(); m_categoriesView->refresh(); m_payeesView->slotLoadPayees(); m_tagsView->refresh(); m_ledgerView->slotLoadView(); m_budgetView->refresh(); m_homeView->slotLoadView(); m_investmentView->slotLoadView(); m_reportsView->slotLoadView(); m_forecastView->slotLoadForecast(); m_scheduledView->slotReloadView(); } void KMyMoneyView::slotShowTransactionDetail(bool detailed) { KMyMoneyGlobalSettings::setShowRegisterDetailed(detailed); slotRefreshViews(); } void KMyMoneyView::progressCallback(int current, int total, const QString& msg) { kmymoney->progressCallback(current, total, msg); } void KMyMoneyView::slotCurrentPageChanged(const QModelIndex current, const QModelIndex) { // remember the current page m_lastViewSelected = current.row(); // set the current page's title in the header if (m_header) m_header->setText(m_model->data(current, KPageModel::HeaderRole).toString()); } /* DO NOT ADD code to this function or any of it's called ones. Instead, create a new function, fixFile_n, and modify the initializeStorage() logic above to call it */ void KMyMoneyView::fixFile_3() { // make sure each storage object contains a (unique) id MyMoneyFile::instance()->storageId(); } void KMyMoneyView::fixFile_2() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyTransactionFilter filter; filter.setReportAllSplits(false); QList transactionList; file->transactionList(transactionList, filter); // scan the transactions and modify transactions with two splits // which reference an account and a category to have the memo text // of the account. QList::Iterator it_t; int count = 0; for (it_t = transactionList.begin(); it_t != transactionList.end(); ++it_t) { if ((*it_t).splitCount() == 2) { QString accountId; QString categoryId; QString accountMemo; QString categoryMemo; const QList& splits = (*it_t).splits(); QList::const_iterator it_s; for (it_s = splits.constBegin(); it_s != splits.constEnd(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); if (acc.isIncomeExpense()) { categoryId = (*it_s).id(); categoryMemo = (*it_s).memo(); } else { accountId = (*it_s).id(); accountMemo = (*it_s).memo(); } } if (!accountId.isEmpty() && !categoryId.isEmpty() && accountMemo != categoryMemo) { MyMoneyTransaction t(*it_t); MyMoneySplit s(t.splitById(categoryId)); s.setMemo(accountMemo); t.modifySplit(s); file->modifyTransaction(t); ++count; } } } qDebug("%d transactions fixed in fixFile_2", count); } void KMyMoneyView::fixFile_1() { // we need to fix reports. If the account filter list contains // investment accounts, we need to add the stock accounts to the list // as well if we don't have the expert mode enabled if (!KMyMoneyGlobalSettings::expertMode()) { try { QList reports = MyMoneyFile::instance()->reportList(); QList::iterator it_r; for (it_r = reports.begin(); it_r != reports.end(); ++it_r) { QStringList list; (*it_r).accounts(list); 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() == Account::Investment) { foreach (const auto accountID, acc.accountList()) { if (!list.contains(accountID)) { missing.append(accountID); } } } } if (!missing.isEmpty()) { (*it_r).addAccount(missing); MyMoneyFile::instance()->modifyReport(*it_r); } } } catch (const MyMoneyException &) { } } } #if 0 if (!m_accountsView->allItemsSelected()) { // retrieve a list of selected accounts QStringList list; 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.begin(); it_a != list.end(); ++it_a) { MyMoneyAccount acc = MyMoneyFile::instance()->account(*it_a); if (acc.accountType() == Account::Investment) { foreach (const auto accountID, acc.accountList()) { if (!list.contains(accountID)) { missing.append(accountID); } } } } list += missing; } m_filter.addAccount(list); } #endif void KMyMoneyView::fixFile_0() { /* (Ace) I am on a crusade against file fixups. Whenever we have to fix the * file, it is really a warning. So I'm going to print a debug warning, and * then go track them down when I see them to figure out how they got saved * out needing fixing anyway. */ MyMoneyFile* file = MyMoneyFile::instance(); QList accountList; file->accountList(accountList); QList::Iterator it_a; QList scheduleList = file->scheduleList(); QList::Iterator it_s; MyMoneyAccount equity = file->equity(); MyMoneyAccount asset = file->asset(); bool equityListEmpty = equity.accountList().count() == 0; for (it_a = accountList.begin(); it_a != accountList.end(); ++it_a) { if ((*it_a).accountType() == Account::Loan || (*it_a).accountType() == Account::AssetLoan) { fixLoanAccount_0(*it_a); } // until early before 0.8 release, the equity account was not saved to // the file. If we have an equity account with no sub-accounts but // find and equity account that has equity() as it's parent, we reparent // this account. Need to move it to asset() first, because otherwise // MyMoneyFile::reparent would act as NOP. if (equityListEmpty && (*it_a).accountType() == Account::Equity) { if ((*it_a).parentAccountId() == equity.id()) { MyMoneyAccount acc = *it_a; // tricky, force parent account to be empty so that we really // can re-parent it acc.setParentAccountId(QString()); file->reparentAccount(acc, equity); qDebug() << Q_FUNC_INFO << " fixed account " << acc.id() << " reparented to " << equity.id(); } } } for (it_s = scheduleList.begin(); it_s != scheduleList.end(); ++it_s) { fixSchedule_0(*it_s); } fixTransactions_0(); } void KMyMoneyView::fixSchedule_0(MyMoneySchedule sched) { MyMoneyTransaction t = sched.transaction(); QList splitList = t.splits(); QList::ConstIterator it_s; bool updated = false; try { // Check if the splits contain valid data and set it to // be valid. for (it_s = splitList.constBegin(); it_s != splitList.constEnd(); ++it_s) { // the first split is always the account on which this transaction operates // and if the transaction commodity is not set, we take this if (it_s == splitList.constBegin() && t.commodity().isEmpty()) { qDebug() << Q_FUNC_INFO << " " << t.id() << " has no commodity"; try { MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); t.setCommodity(acc.currencyId()); updated = true; } catch (const MyMoneyException &) { } } // make sure the account exists. If not, remove the split try { MyMoneyFile::instance()->account((*it_s).accountId()); } catch (const MyMoneyException &) { qDebug() << Q_FUNC_INFO << " " << sched.id() << " " << (*it_s).id() << " removed, because account '" << (*it_s).accountId() << "' does not exist."; t.removeSplit(*it_s); updated = true; } if ((*it_s).reconcileFlag() != eMyMoney::Split::State::NotReconciled) { qDebug() << Q_FUNC_INFO << " " << sched.id() << " " << (*it_s).id() << " should be 'not reconciled'"; MyMoneySplit split = *it_s; split.setReconcileDate(QDate()); split.setReconcileFlag(eMyMoney::Split::State::NotReconciled); t.modifySplit(split); updated = true; } // the schedule logic used to operate only on the value field. // This is now obsolete. if ((*it_s).shares().isZero() && !(*it_s).value().isZero()) { MyMoneySplit split = *it_s; split.setShares(split.value()); t.modifySplit(split); updated = true; } } // If there have been changes, update the schedule and // the engine data. if (updated) { sched.setTransaction(t); MyMoneyFile::instance()->modifySchedule(sched); } } catch (const MyMoneyException &e) { qWarning("Unable to update broken schedule: %s", qPrintable(e.what())); } } void KMyMoneyView::fixLoanAccount_0(MyMoneyAccount acc) { if (acc.value("final-payment").isEmpty() || acc.value("term").isEmpty() || acc.value("periodic-payment").isEmpty() || acc.value("loan-amount").isEmpty() || acc.value("interest-calculation").isEmpty() || acc.value("schedule").isEmpty() || acc.value("fixed-interest").isEmpty()) { KMessageBox::information(this, i18n("

The account \"%1\" was previously created as loan account but some information is missing.

The new loan wizard will be started to collect all relevant information.

Please use KMyMoney version 0.8.7 or later and earlier than version 0.9 to correct the problem.

" , acc.name()), i18n("Account problem")); throw MYMONEYEXCEPTION("Fix LoanAccount0 not supported anymore"); } } void KMyMoneyView::createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount) { // Add the schedule only if one exists // // Remember to modify the first split to reference the newly created account if (!newSchedule.name().isEmpty()) { MyMoneyFileTransaction ft; 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!"); } // now search the split that does not have an account reference // and set it up to be the one of the account we just added // to the account pool. Note: the schedule code used to leave // this always the first split, but the loan code leaves it as // the second one. So I thought, searching is a good alternative .... QList::ConstIterator it_s; for (it_s = t.splits().constBegin(); it_s != t.splits().constEnd(); ++it_s) { if ((*it_s).accountId().isEmpty()) { MyMoneySplit s = (*it_s); s.setAccountId(newAccount.id()); t.modifySplit(s); break; } } newSchedule.setTransaction(t); MyMoneyFile::instance()->addSchedule(newSchedule); // in case of a loan account, we keep a reference to this // schedule in the account if (newAccount.isLoan()) { newAccount.setValue("schedule", newSchedule.id()); MyMoneyFile::instance()->modifyAccount(newAccount); } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to add schedule: %1", e.what())); } } } void KMyMoneyView::fixTransactions_0() { MyMoneyFile* file = MyMoneyFile::instance(); QList scheduleList = file->scheduleList(); MyMoneyTransactionFilter filter; filter.setReportAllSplits(false); QList transactionList; file->transactionList(transactionList, filter); QList::Iterator it_x; QStringList interestAccounts; KMSTATUS(i18n("Fix transactions")); kmymoney->slotStatusProgressBar(0, scheduleList.count() + transactionList.count()); int cnt = 0; // scan the schedules to find interest accounts for (it_x = scheduleList.begin(); it_x != scheduleList.end(); ++it_x) { MyMoneyTransaction t = (*it_x).transaction(); QList::ConstIterator it_s; QStringList accounts; bool hasDuplicateAccounts = false; for (it_s = t.splits().constBegin(); it_s != t.splits().constEnd(); ++it_s) { if (accounts.contains((*it_s).accountId())) { hasDuplicateAccounts = true; qDebug() << Q_FUNC_INFO << " " << t.id() << " has multiple splits with account " << (*it_s).accountId(); } else { accounts << (*it_s).accountId(); } if ((*it_s).action() == MyMoneySplit::ActionInterest) { if (interestAccounts.contains((*it_s).accountId()) == 0) { interestAccounts << (*it_s).accountId(); } } } if (hasDuplicateAccounts) { fixDuplicateAccounts_0(t); } ++cnt; if (!(cnt % 10)) kmymoney->slotStatusProgressBar(cnt); } // scan the transactions and modify loan transactions QList::Iterator it_t; for (it_t = transactionList.begin(); it_t != transactionList.end(); ++it_t) { const char *defaultAction = 0; QList splits = (*it_t).splits(); QList::Iterator it_s; QStringList accounts; // check if base commodity is set. if not, set baseCurrency if ((*it_t).commodity().isEmpty()) { qDebug() << Q_FUNC_INFO << " " << (*it_t).id() << " has no base currency"; (*it_t).setCommodity(file->baseCurrency().id()); file->modifyTransaction(*it_t); } bool isLoan = false; // Determine default action if ((*it_t).splitCount() == 2) { // check for transfer int accountCount = 0; MyMoneyMoney val; for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); if (acc.accountGroup() == Account::Asset || acc.accountGroup() == Account::Liability) { val = (*it_s).value(); accountCount++; if (acc.accountType() == Account::Loan || acc.accountType() == Account::AssetLoan) isLoan = true; } else break; } if (accountCount == 2) { if (isLoan) defaultAction = MyMoneySplit::ActionAmortization; else defaultAction = MyMoneySplit::ActionTransfer; } else { if (val.isNegative()) defaultAction = MyMoneySplit::ActionWithdrawal; else defaultAction = MyMoneySplit::ActionDeposit; } } isLoan = false; for (it_s = splits.begin(); defaultAction == 0 && it_s != splits.end(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); MyMoneyMoney val = (*it_s).value(); if (acc.accountGroup() == Account::Asset || acc.accountGroup() == Account::Liability) { if (!val.isPositive()) defaultAction = MyMoneySplit::ActionWithdrawal; else defaultAction = MyMoneySplit::ActionDeposit; } } #if 0 // Check for correct actions in transactions referencing credit cards bool needModify = false; // The action fields are actually not used anymore in the ledger view logic // so we might as well skip this whole thing here! for (it_s = splits.begin(); needModify == false && it_s != splits.end(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); MyMoneyMoney val = (*it_s).value(); if (acc.accountType() == Account::CreditCard) { if (val < 0 && (*it_s).action() != MyMoneySplit::ActionWithdrawal && (*it_s).action() != MyMoneySplit::ActionTransfer) needModify = true; if (val >= 0 && (*it_s).action() != MyMoneySplit::ActionDeposit && (*it_s).action() != MyMoneySplit::ActionTransfer) needModify = true; } } // (Ace) Extended the #endif down to cover this conditional, because as-written // it will ALWAYS be skipped. if (needModify == true) { for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { (*it_s).setAction(defaultAction); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); } splits = (*it_t).splits(); // update local copy qDebug("Fixed credit card assignment in %s", (*it_t).id().data()); } #endif // Check for correct assignment of ActionInterest in all splits // and check if there are any duplicates in this transactions for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { MyMoneyAccount splitAccount = file->account((*it_s).accountId()); if (!accounts.contains((*it_s).accountId())) { accounts << (*it_s).accountId(); } // if this split references an interest account, the action // must be of type ActionInterest if (interestAccounts.contains((*it_s).accountId())) { if ((*it_s).action() != MyMoneySplit::ActionInterest) { qDebug() << Q_FUNC_INFO << " " << (*it_t).id() << " contains an interest account (" << (*it_s).accountId() << ") but does not have ActionInterest"; (*it_s).setAction(MyMoneySplit::ActionInterest); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); qDebug("Fixed interest action in %s", qPrintable((*it_t).id())); } // if it does not reference an interest account, it must not be // of type ActionInterest } else { if ((*it_s).action() == MyMoneySplit::ActionInterest) { qDebug() << Q_FUNC_INFO << " " << (*it_t).id() << " does not contain an interest account so it should not have ActionInterest"; (*it_s).setAction(defaultAction); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); qDebug("Fixed interest action in %s", qPrintable((*it_t).id())); } } // check that for splits referencing an account that has // the same currency as the transactions commodity the value // and shares field are the same. if ((*it_t).commodity() == splitAccount.currencyId() && (*it_s).value() != (*it_s).shares()) { qDebug() << Q_FUNC_INFO << " " << (*it_t).id() << " " << (*it_s).id() << " uses the transaction currency, but shares != value"; (*it_s).setShares((*it_s).value()); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); } // fix the shares and values to have the correct fraction if (!splitAccount.isInvest()) { try { int fract = splitAccount.fraction(); if ((*it_s).shares() != (*it_s).shares().convert(fract)) { qDebug("adjusting fraction in %s,%s", qPrintable((*it_t).id()), qPrintable((*it_s).id())); (*it_s).setShares((*it_s).shares().convert(fract)); (*it_s).setValue((*it_s).value().convert(fract)); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); } } catch (const MyMoneyException &) { qDebug("Missing security '%s', split not altered", qPrintable(splitAccount.currencyId())); } } } ++cnt; if (!(cnt % 10)) kmymoney->slotStatusProgressBar(cnt); } kmymoney->slotStatusProgressBar(-1, -1); } void KMyMoneyView::fixDuplicateAccounts_0(MyMoneyTransaction& t) { qDebug("Duplicate account in transaction %s", qPrintable(t.id())); } void KMyMoneyView::slotPrintView() { if (viewFrames[View::Reports] == currentPage()) m_reportsView->slotPrintView(); else if (viewFrames[View::Home] == currentPage()) m_homeView->slotPrintView(); } void KMyMoneyView::resetViewSelection(const View) { emit aboutToChangeView(); } void KMyMoneyView::connectView(const View view) { KMyMoneyAccountTreeView *treeView; switch (view) { case View::Accounts: disconnect(m_accountsView, &KAccountsView::aboutToShow, this, &KMyMoneyView::connectView); treeView = m_accountsView->getTreeView(); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectAccount); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectInstitution); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectInvestment); connect(treeView, &KMyMoneyAccountTreeView::openObject, kmymoney, &KMyMoneyApp::slotAccountOpen); connect(treeView, &KMyMoneyAccountTreeView::openContextMenu, kmymoney, &KMyMoneyApp::slotShowAccountContextMenu); connect(treeView, &KMyMoneyAccountTreeView::columnToggled , this, &KMyMoneyView::slotAccountTreeViewChanged); connect(Models::instance()->accountsModel(), &AccountsModel::netWorthChanged, m_accountsView, &KAccountsView::slotNetWorthChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, m_accountsView, &KAccountsView::refresh); break; case View::Institutions: disconnect(m_institutionsView, &KInstitutionsView::aboutToShow, this, &KMyMoneyView::connectView); treeView = m_institutionsView->getTreeView(); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectAccount); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectInstitution); connect(treeView, &KMyMoneyAccountTreeView::openObject, kmymoney, &KMyMoneyApp::slotAccountOpen); connect(treeView, &KMyMoneyAccountTreeView::openObject, kmymoney, &KMyMoneyApp::slotInstitutionEdit); connect(treeView, &KMyMoneyAccountTreeView::openContextMenu, kmymoney, &KMyMoneyApp::slotShowAccountContextMenu); connect(treeView, &KMyMoneyAccountTreeView::openContextMenu, kmymoney, &KMyMoneyApp::slotShowInstitutionContextMenu); connect(treeView, &KMyMoneyAccountTreeView::columnToggled , this, &KMyMoneyView::slotAccountTreeViewChanged); connect(Models::instance()->institutionsModel(), &AccountsModel::netWorthChanged, m_institutionsView, &KInstitutionsView::slotNetWorthChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, m_institutionsView, &KInstitutionsView::refresh); break; case View::Categories: disconnect(m_categoriesView, &KCategoriesView::aboutToShow, this, &KMyMoneyView::connectView); treeView = m_categoriesView->getTreeView(); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectAccount); connect(treeView, &KMyMoneyAccountTreeView::openObject, kmymoney, &KMyMoneyApp::slotAccountOpen); connect(treeView, &KMyMoneyAccountTreeView::openContextMenu, kmymoney, &KMyMoneyApp::slotShowAccountContextMenu); connect(treeView, &KMyMoneyAccountTreeView::columnToggled , this, &KMyMoneyView::slotAccountTreeViewChanged); connect(Models::instance()->institutionsModel(), &AccountsModel::profitChanged, m_categoriesView, &KCategoriesView::slotProfitChanged); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, m_categoriesView, &KCategoriesView::refresh); break; case View::Tags: disconnect(m_tagsView, &KTagsView::aboutToShow, this, &KMyMoneyView::connectView); connect(kmymoney, &KMyMoneyApp::tagCreated, m_tagsView, static_cast(&KTagsView::slotSelectTagAndTransaction)); connect(kmymoney, &KMyMoneyApp::slotTagRename, m_tagsView, &KTagsView::slotRenameButtonCliked); connect(m_tagsView, &KTagsView::tagNewClicked, kmymoney, static_cast(&KMyMoneyApp::slotTagNew)); connect(m_tagsView, &KTagsView::tagDeleteClicked, kmymoney, static_cast(&KMyMoneyApp::slotTagDelete)); connect(m_tagsView, &KTagsView::openContextMenu, kmymoney, &KMyMoneyApp::slotShowTagContextMenu); connect(m_tagsView, &KTagsView::selectObjects, kmymoney, &KMyMoneyApp::slotSelectTags); connect(m_tagsView, &KTagsView::transactionSelected, this, &KMyMoneyView::slotLedgerSelected); connect(m_tagsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::aboutToChangeView); break; case View::Budget: disconnect(m_budgetView, &KBudgetView::aboutToShow, this, &KMyMoneyView::connectView); treeView = m_budgetView->getTreeView(); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectAccount); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectInstitution); connect(treeView, &KMyMoneyAccountTreeView::selectObject, kmymoney, &KMyMoneyApp::slotSelectInvestment); connect(treeView, &KMyMoneyAccountTreeView::openObject, kmymoney, &KMyMoneyApp::slotAccountOpen); connect(treeView, &KMyMoneyAccountTreeView::openContextMenu, kmymoney, &KMyMoneyApp::slotShowAccountContextMenu); connect(m_budgetView, &KBudgetView::openContextMenu, kmymoney, &KMyMoneyApp::slotShowBudgetContextMenu); connect(m_budgetView, &KBudgetView::selectObjects, kmymoney, &KMyMoneyApp::slotSelectBudget); connect(kmymoney, &KMyMoneyApp::budgetRename, m_budgetView, &KBudgetView::slotStartRename); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, m_budgetView, &KBudgetView::refresh); connect(treeView, &KMyMoneyAccountTreeView::columnToggled , this, &KMyMoneyView::slotAccountTreeViewChanged); break; default: break; } } diff --git a/kmymoney/views/kmymoneyview.h b/kmymoney/views/kmymoneyview.h index c0a65f3ed..6e40e0651 100644 --- a/kmymoney/views/kmymoneyview.h +++ b/kmymoney/views/kmymoneyview.h @@ -1,622 +1,623 @@ /*************************************************************************** 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 "selectedtransaction.h" +#include "selectedtransactions.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 dacb02b78..46c370864 100644 --- a/kmymoney/views/kpayeesview.cpp +++ b/kmymoney/views/kpayeesview.cpp @@ -1,989 +1,993 @@ /*************************************************************************** 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" +#include "transaction.h" +#include "widgetenums.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; + QList cols { + eWidgets::eTransaction::Column::Date, + eWidgets::eTransaction::Column::Account, + eWidgets::eTransaction::Column::Detail, + eWidgets::eTransaction::Column::ReconcileFlag, + eWidgets::eTransaction::Column::Payment, + eWidgets::eTransaction::Column::Deposit}; m_register->setupRegister(MyMoneyAccount(), cols); m_register->setSelectionMode(QTableWidget::SingleSelection); - m_register->setDetailsColumnType(KMyMoneyRegister::AccountFirst); + m_register->setDetailsColumnType(eWidgets::eRegister::DetailColumn::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 a8effa180..753115e1d 100644 --- a/kmymoney/views/kscheduledview.cpp +++ b/kmymoney/views/kscheduledview.cpp @@ -1,612 +1,614 @@ /*************************************************************************** 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 a59a61409..f081fb9b0 100644 --- a/kmymoney/views/ktagsview.cpp +++ b/kmymoney/views/ktagsview.cpp @@ -1,608 +1,611 @@ /*************************************************************************** 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" +#include "transaction.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 6595a2d0d..e86335a73 100644 --- a/kmymoney/views/ktagsview_p.h +++ b/kmymoney/views/ktagsview_p.h @@ -1,181 +1,183 @@ /*************************************************************************** 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" +#include "widgetenums.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 + QList { eWidgets::eTransaction::Column::Date, + eWidgets::eTransaction::Column::Account, + eWidgets::eTransaction::Column::Detail, + eWidgets::eTransaction::Column::ReconcileFlag, + eWidgets::eTransaction::Column::Payment, + eWidgets::eTransaction::Column::Deposit }); ui->m_register->setSelectionMode(QTableWidget::SingleSelection); - ui->m_register->setDetailsColumnType(KMyMoneyRegister::AccountFirst); + ui->m_register->setDetailsColumnType(eWidgets::eRegister::DetailColumn::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/views/newspliteditor.cpp b/kmymoney/views/newspliteditor.cpp index 5c942b736..8e2e86cb5 100644 --- a/kmymoney/views/newspliteditor.cpp +++ b/kmymoney/views/newspliteditor.cpp @@ -1,381 +1,382 @@ /*************************************************************************** newspliteditor.cpp ------------------- begin : Sat Apr 9 2016 copyright : (C) 2016 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 "newspliteditor.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes +#include "creditdebithelper.h" #include "kmymoneyutils.h" #include "kmymoneyaccountcombo.h" #include "models.h" #include "accountsmodel.h" #include "costcentermodel.h" #include "ledgermodel.h" #include "splitmodel.h" #include "mymoneyaccount.h" #include "ui_newspliteditor.h" #include "widgethintframe.h" #include "ledgerview.h" #include "icons/icons.h" using namespace Icons; struct NewSplitEditor::Private { Private(NewSplitEditor* parent) : ui(new Ui_NewSplitEditor) , accountsModel(new AccountNamesFilterProxyModel(parent)) , costCenterModel(new QSortFilterProxyModel(parent)) , splitModel(0) , accepted(false) , costCenterRequired(false) , costCenterOk(false) , showValuesInverted(false) { accountsModel->setObjectName("AccountNamesFilterProxyModel"); costCenterModel->setObjectName("SortedCostCenterModel"); statusModel.setObjectName("StatusModel"); costCenterModel->setSortLocaleAware(true); costCenterModel->setSortCaseSensitivity(Qt::CaseInsensitive); createStatusEntry(eMyMoney::Split::State::NotReconciled); createStatusEntry(eMyMoney::Split::State::Cleared); createStatusEntry(eMyMoney::Split::State::Reconciled); // createStatusEntry(eMyMoney::Split::State::Frozen); } void createStatusEntry(eMyMoney::Split::State status); bool checkForValidSplit(bool doUserInteraction = true); bool costCenterChanged(int costCenterIndex); bool categoryChanged(const QString& accountId); bool numberChanged(const QString& newNumber); bool amountChanged(CreditDebitHelper* valueHelper); Ui_NewSplitEditor* ui; AccountNamesFilterProxyModel* accountsModel; QSortFilterProxyModel* costCenterModel; SplitModel* splitModel; bool accepted; bool costCenterRequired; bool costCenterOk; bool showValuesInverted; QStandardItemModel statusModel; QString transactionSplitId; MyMoneyAccount counterAccount; MyMoneyAccount category; CreditDebitHelper* amountHelper; }; void NewSplitEditor::Private::createStatusEntry(eMyMoney::Split::State status) { QStandardItem* p = new QStandardItem(KMyMoneyUtils::reconcileStateToString(status, true)); p->setData((int)status); statusModel.appendRow(p); } bool NewSplitEditor::Private::checkForValidSplit(bool doUserInteraction) { QStringList infos; bool rc = true; if(!costCenterChanged(ui->costCenterCombo->currentIndex())) { infos << ui->costCenterCombo->toolTip(); rc = false; } if(doUserInteraction) { /// @todo add dialog here that shows the @a infos } return rc; } bool NewSplitEditor::Private::costCenterChanged(int costCenterIndex) { bool rc = true; WidgetHintFrame::hide(ui->costCenterCombo, i18n("The cost center this transaction should be assigned to.")); if(costCenterIndex != -1) { if(costCenterRequired && ui->costCenterCombo->currentText().isEmpty()) { WidgetHintFrame::show(ui->costCenterCombo, i18n("A cost center assignment is required for a transaction in the selected category.")); rc = false; } } return rc; } bool NewSplitEditor::Private::categoryChanged(const QString& accountId) { bool rc = true; if(!accountId.isEmpty()) { try { QModelIndex index = Models::instance()->accountsModel()->accountById(accountId); category = Models::instance()->accountsModel()->data(index, (int)eAccountsModel::Role::Account).value(); const bool isIncomeExpense = category.isIncomeExpense(); ui->costCenterCombo->setEnabled(isIncomeExpense); ui->costCenterLabel->setEnabled(isIncomeExpense); ui->numberEdit->setDisabled(isIncomeExpense); ui->numberLabel->setDisabled(isIncomeExpense); costCenterRequired = category.isCostCenterRequired(); rc &= costCenterChanged(ui->costCenterCombo->currentIndex()); } catch (MyMoneyException &e) { qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO; } } return rc; } bool NewSplitEditor::Private::numberChanged(const QString& newNumber) { bool rc = true; WidgetHintFrame::hide(ui->numberEdit, i18n("The check number used for this transaction.")); if(!newNumber.isEmpty()) { const LedgerModel* model = Models::instance()->ledgerModel(); QModelIndexList list = model->match(model->index(0, 0), (int)eLedgerModel::Role::Number, QVariant(newNumber), -1, // all splits Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive)); foreach(QModelIndex index, list) { if(model->data(index, (int)eLedgerModel::Role::AccountId) == ui->accountCombo->getSelected() && model->data(index, (int)eLedgerModel::Role::TransactionSplitId) != transactionSplitId) { WidgetHintFrame::show(ui->numberEdit, i18n("The check number %1 has already been used in this account.").arg(newNumber)); rc = false; break; } } } return rc; } bool NewSplitEditor::Private::amountChanged(CreditDebitHelper* valueHelper) { Q_UNUSED(valueHelper); bool rc = true; return rc; } NewSplitEditor::NewSplitEditor(QWidget* parent, const QString& counterAccountId) : QFrame(parent, Qt::FramelessWindowHint /* | Qt::X11BypassWindowManagerHint */) , d(new Private(this)) { SplitView* view = qobject_cast(parent->parentWidget()); Q_ASSERT(view != 0); d->splitModel = qobject_cast(view->model()); QModelIndex index = Models::instance()->accountsModel()->accountById(counterAccountId); d->counterAccount = Models::instance()->accountsModel()->data(index, (int)eAccountsModel::Role::Account).value(); d->ui->setupUi(this); d->ui->enterButton->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogOK])); d->ui->cancelButton->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogCancel])); d->accountsModel->addAccountGroup(QVector {eMyMoney::Account::Asset, eMyMoney::Account::Liability, eMyMoney::Account::Income, eMyMoney::Account::Expense, eMyMoney::Account::Equity}); d->accountsModel->setHideEquityAccounts(false); auto const model = Models::instance()->accountsModel(); d->accountsModel->setSourceModel(model); d->accountsModel->setSourceColumns(model->getColumns()); d->accountsModel->sort((int)eAccountsModel::Column::Account); d->ui->accountCombo->setModel(d->accountsModel); d->costCenterModel->setSortRole(Qt::DisplayRole); d->costCenterModel->setSourceModel(Models::instance()->costCenterModel()); d->costCenterModel->sort((int)eAccountsModel::Column::Account); d->ui->costCenterCombo->setEditable(true); d->ui->costCenterCombo->setModel(d->costCenterModel); d->ui->costCenterCombo->setModelColumn(0); d->ui->costCenterCombo->completer()->setFilterMode(Qt::MatchContains); WidgetHintFrameCollection* frameCollection = new WidgetHintFrameCollection(this); frameCollection->addFrame(new WidgetHintFrame(d->ui->costCenterCombo)); frameCollection->addFrame(new WidgetHintFrame(d->ui->numberEdit, WidgetHintFrame::Warning)); frameCollection->addWidget(d->ui->enterButton); d->amountHelper = new CreditDebitHelper(this, d->ui->amountEditCredit, d->ui->amountEditDebit); connect(d->ui->numberEdit, SIGNAL(textChanged(QString)), this, SLOT(numberChanged(QString))); connect(d->ui->costCenterCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(costCenterChanged(int))); connect(d->ui->accountCombo, SIGNAL(accountSelected(QString)), this, SLOT(categoryChanged(QString))); connect(d->amountHelper, SIGNAL(valueChanged()), this, SLOT(amountChanged())); connect(d->ui->cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject())); connect(d->ui->enterButton, SIGNAL(clicked(bool)), this, SLOT(acceptEdit())); } NewSplitEditor::~NewSplitEditor() { } void NewSplitEditor::setShowValuesInverted(bool inverse) { d->showValuesInverted = inverse; } bool NewSplitEditor::showValuesInverted() { return d->showValuesInverted; } bool NewSplitEditor::accepted() const { return d->accepted; } void NewSplitEditor::acceptEdit() { if(d->checkForValidSplit()) { d->accepted = true; emit done(); } } void NewSplitEditor::reject() { emit done(); } void NewSplitEditor::keyPressEvent(QKeyEvent* e) { if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) { switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: { if(focusWidget() == d->ui->cancelButton) { reject(); } else { if(d->ui->enterButton->isEnabled()) { d->ui->enterButton->click(); } return; } } break; case Qt::Key_Escape: reject(); break; default: e->ignore(); return; } } else { e->ignore(); } } QString NewSplitEditor::accountId() const { return d->ui->accountCombo->getSelected(); } void NewSplitEditor::setAccountId(const QString& id) { d->ui->accountCombo->clearEditText(); d->ui->accountCombo->setSelected(id); } QString NewSplitEditor::memo() const { return d->ui->memoEdit->toPlainText(); } void NewSplitEditor::setMemo(const QString& memo) { d->ui->memoEdit->setPlainText(memo); } MyMoneyMoney NewSplitEditor::amount() const { return d->amountHelper->value(); } void NewSplitEditor::setAmount(MyMoneyMoney value) { d->amountHelper->setValue(value); } QString NewSplitEditor::costCenterId() const { const int row = d->ui->costCenterCombo->currentIndex(); QModelIndex index = d->ui->costCenterCombo->model()->index(row, 0); return d->ui->costCenterCombo->model()->data(index, CostCenterModel::CostCenterIdRole).toString(); } void NewSplitEditor::setCostCenterId(const QString& id) { QModelIndex index = Models::indexById(d->costCenterModel, CostCenterModel::CostCenterIdRole, id); if(index.isValid()) { d->ui->costCenterCombo->setCurrentIndex(index.row()); } } QString NewSplitEditor::number() const { return d->ui->numberEdit->text(); } void NewSplitEditor::setNumber(const QString& number) { d->ui->numberEdit->setText(number); } QString NewSplitEditor::splitId() const { return d->transactionSplitId; } void NewSplitEditor::numberChanged(const QString& newNumber) { d->numberChanged(newNumber); } void NewSplitEditor::categoryChanged(const QString& accountId) { d->categoryChanged(accountId); } void NewSplitEditor::costCenterChanged(int costCenterIndex) { d->costCenterChanged(costCenterIndex); } void NewSplitEditor::amountChanged() { d->amountChanged(d->amountHelper); } diff --git a/kmymoney/views/newtransactioneditor.cpp b/kmymoney/views/newtransactioneditor.cpp index c4fc3f153..d3c04f640 100644 --- a/kmymoney/views/newtransactioneditor.cpp +++ b/kmymoney/views/newtransactioneditor.cpp @@ -1,699 +1,700 @@ /*************************************************************************** newtransactioneditor.cpp ------------------- begin : Sat Aug 8 2015 copyright : (C) 2015 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 "newtransactioneditor.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes +#include "creditdebithelper.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "kmymoneyutils.h" #include "kmymoneyaccountcombo.h" #include "models.h" #include "accountsmodel.h" #include "costcentermodel.h" #include "ledgermodel.h" #include "splitmodel.h" #include "payeesmodel.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "ui_newtransactioneditor.h" #include "splitdialog.h" #include "widgethintframe.h" #include "icons/icons.h" using namespace Icons; Q_GLOBAL_STATIC(QDate, lastUsedPostDate) class NewTransactionEditor::Private { public: Private(NewTransactionEditor* parent) : ui(new Ui_NewTransactionEditor) , accountsModel(new AccountNamesFilterProxyModel(parent)) , costCenterModel(new QSortFilterProxyModel(parent)) , payeesModel(new QSortFilterProxyModel(parent)) , accepted(false) , costCenterRequired(false) { accountsModel->setObjectName("NewTransactionEditor::accountsModel"); costCenterModel->setObjectName("SortedCostCenterModel"); payeesModel->setObjectName("SortedPayeesModel"); statusModel.setObjectName("StatusModel"); splitModel.setObjectName("SplitModel"); costCenterModel->setSortLocaleAware(true); costCenterModel->setSortCaseSensitivity(Qt::CaseInsensitive); payeesModel->setSortLocaleAware(true); payeesModel->setSortCaseSensitivity(Qt::CaseInsensitive); createStatusEntry(eMyMoney::Split::State::NotReconciled); createStatusEntry(eMyMoney::Split::State::Cleared); createStatusEntry(eMyMoney::Split::State::Reconciled); // createStatusEntry(eMyMoney::Split::State::Frozen); } void createStatusEntry(eMyMoney::Split::State status); void updateWidgetState(); bool checkForValidTransaction(bool doUserInteraction = true); bool isDatePostOpeningDate(const QDate& date, const QString& accountId); bool postdateChanged(const QDate& date); bool costCenterChanged(int costCenterIndex); bool categoryChanged(const QString& accountId); bool numberChanged(const QString& newNumber); bool valueChanged(CreditDebitHelper* valueHelper); Ui_NewTransactionEditor* ui; AccountNamesFilterProxyModel* accountsModel; QSortFilterProxyModel* costCenterModel; QSortFilterProxyModel* payeesModel; bool accepted; bool costCenterRequired; bool costCenterOk; SplitModel splitModel; QStandardItemModel statusModel; QString transactionSplitId; MyMoneyAccount account; MyMoneyTransaction transaction; MyMoneySplit split; CreditDebitHelper* amountHelper; }; void NewTransactionEditor::Private::createStatusEntry(eMyMoney::Split::State status) { QStandardItem* p = new QStandardItem(KMyMoneyUtils::reconcileStateToString(status, true)); p->setData((int)status); statusModel.appendRow(p); } void NewTransactionEditor::Private::updateWidgetState() { // just in case it is disabled we turn it on ui->costCenterCombo->setEnabled(true); // setup the category/account combo box. If we have a split transaction, we disable the // combo box altogether. Changes can only be made via the split dialog editor bool blocked = false; QModelIndex index; // update the category combo box ui->accountCombo->setEnabled(true); switch(splitModel.rowCount()) { case 0: ui->accountCombo->setSelected(QString()); break; case 1: index = splitModel.index(0, 0); ui->accountCombo->setSelected(splitModel.data(index, (int)eLedgerModel::Role::AccountId).toString()); break; default: index = splitModel.index(0, 0); blocked = ui->accountCombo->lineEdit()->blockSignals(true); ui->accountCombo->lineEdit()->setText(i18n("Split transaction")); ui->accountCombo->setDisabled(true); ui->accountCombo->lineEdit()->blockSignals(blocked); ui->costCenterCombo->setDisabled(true); ui->costCenterLabel->setDisabled(true); break; } ui->accountCombo->hidePopup(); // update the costcenter combo box if(ui->costCenterCombo->isEnabled()) { // extract the cost center index = splitModel.index(0, 0); QModelIndexList ccList = costCenterModel->match(costCenterModel->index(0, 0), CostCenterModel::CostCenterIdRole, splitModel.data(index, (int)eLedgerModel::Role::CostCenterId), 1, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive)); if (ccList.count() > 0) { index = ccList.front(); ui->costCenterCombo->setCurrentIndex(index.row()); } } } bool NewTransactionEditor::Private::checkForValidTransaction(bool doUserInteraction) { QStringList infos; bool rc = true; if(!postdateChanged(ui->dateEdit->date())) { infos << ui->dateEdit->toolTip(); rc = false; } if(!costCenterChanged(ui->costCenterCombo->currentIndex())) { infos << ui->costCenterCombo->toolTip(); rc = false; } if(doUserInteraction) { /// @todo add dialog here that shows the @a infos about the problem } return rc; } bool NewTransactionEditor::Private::isDatePostOpeningDate(const QDate& date, const QString& accountId) { bool rc = true; try { MyMoneyAccount account = MyMoneyFile::instance()->account(accountId); const bool isIncomeExpense = account.isIncomeExpense(); // we don't check for categories if(!isIncomeExpense) { if(date < account.openingDate()) rc = false; } } catch (MyMoneyException &e) { qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO; } return rc; } bool NewTransactionEditor::Private::postdateChanged(const QDate& date) { bool rc = true; WidgetHintFrame::hide(ui->dateEdit, i18n("The posting date of the transaction.")); // collect all account ids QStringList accountIds; accountIds << account.id(); for(int row = 0; row < splitModel.rowCount(); ++row) { QModelIndex index = splitModel.index(row, 0); accountIds << splitModel.data(index, (int)eLedgerModel::Role::AccountId).toString();; } Q_FOREACH(QString accountId, accountIds) { if(!isDatePostOpeningDate(date, accountId)) { MyMoneyAccount account = MyMoneyFile::instance()->account(accountId); WidgetHintFrame::show(ui->dateEdit, i18n("The posting date is prior to the opening date of account %1.").arg(account.name())); rc = false; break; } } return rc; } bool NewTransactionEditor::Private::costCenterChanged(int costCenterIndex) { bool rc = true; WidgetHintFrame::hide(ui->costCenterCombo, i18n("The cost center this transaction should be assigned to.")); if(costCenterIndex != -1) { if(costCenterRequired && ui->costCenterCombo->currentText().isEmpty()) { WidgetHintFrame::show(ui->costCenterCombo, i18n("A cost center assignment is required for a transaction in the selected category.")); rc = false; } if(rc == true && splitModel.rowCount() == 1) { QModelIndex index = costCenterModel->index(costCenterIndex, 0); QString costCenterId = costCenterModel->data(index, CostCenterModel::CostCenterIdRole).toString(); index = splitModel.index(0, 0); splitModel.setData(index, costCenterId, (int)eLedgerModel::Role::CostCenterId); } } return rc; } bool NewTransactionEditor::Private::categoryChanged(const QString& accountId) { bool rc = true; if(!accountId.isEmpty() && splitModel.rowCount() <= 1) { try { MyMoneyAccount category = MyMoneyFile::instance()->account(accountId); const bool isIncomeExpense = category.isIncomeExpense(); ui->costCenterCombo->setEnabled(isIncomeExpense); ui->costCenterLabel->setEnabled(isIncomeExpense); costCenterRequired = category.isCostCenterRequired(); rc &= costCenterChanged(ui->costCenterCombo->currentIndex()); rc &= postdateChanged(ui->dateEdit->date()); // make sure we have a split in the model bool newSplit = false; if(splitModel.rowCount() == 0) { splitModel.addEmptySplitEntry(); newSplit = true; } const QModelIndex index = splitModel.index(0, 0); splitModel.setData(index, accountId, (int)eLedgerModel::Role::AccountId); if(newSplit) { costCenterChanged(ui->costCenterCombo->currentIndex()); if(amountHelper->haveValue()) { splitModel.setData(index, QVariant::fromValue(-amountHelper->value()), (int)eLedgerModel::Role::SplitValue); /// @todo make sure to convert initial value to shares according to price information splitModel.setData(index, QVariant::fromValue(-amountHelper->value()), (int)eLedgerModel::Role::SplitShares); } } /// @todo we need to make sure to support multiple currencies here } catch (MyMoneyException &e) { qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO; } } return rc; } bool NewTransactionEditor::Private::numberChanged(const QString& newNumber) { bool rc = true; WidgetHintFrame::hide(ui->numberEdit, i18n("The check number used for this transaction.")); if(!newNumber.isEmpty()) { const LedgerModel* model = Models::instance()->ledgerModel(); QModelIndexList list = model->match(model->index(0, 0), (int)eLedgerModel::Role::Number, QVariant(newNumber), -1, // all splits Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive)); foreach(QModelIndex index, list) { if(model->data(index, (int)eLedgerModel::Role::AccountId) == account.id() && model->data(index, (int)eLedgerModel::Role::TransactionSplitId) != transactionSplitId) { WidgetHintFrame::show(ui->numberEdit, i18n("The check number %1 has already been used in this account.").arg(newNumber)); rc = false; break; } } } return rc; } bool NewTransactionEditor::Private::valueChanged(CreditDebitHelper* valueHelper) { bool rc = true; if(valueHelper->haveValue() && splitModel.rowCount() <= 1) { rc = false; try { MyMoneyMoney shares; if(splitModel.rowCount() == 1) { const QModelIndex index = splitModel.index(0, 0); splitModel.setData(index, QVariant::fromValue(-amountHelper->value()), (int)eLedgerModel::Role::SplitValue); /// @todo make sure to support multiple currencies splitModel.setData(index, QVariant::fromValue(-amountHelper->value()), (int)eLedgerModel::Role::SplitShares); } else { /// @todo ask what to do: if the rest of the splits is the same amount we could simply reverse the sign /// of all splits, otherwise we could ask if the user wants to start the split editor or anything else. } rc = true; } catch (MyMoneyException &e) { qDebug() << "Ooops: somwthing went wrong in" << Q_FUNC_INFO; } } return rc; } NewTransactionEditor::NewTransactionEditor(QWidget* parent, const QString& accountId) : QFrame(parent, Qt::FramelessWindowHint /* | Qt::X11BypassWindowManagerHint */) , d(new Private(this)) { auto const model = Models::instance()->accountsModel(); // extract account information from model const auto index = model->accountById(accountId); d->account = model->data(index, (int)eAccountsModel::Role::Account).value(); d->ui->setupUi(this); d->accountsModel->addAccountGroup(QVector {eMyMoney::Account::Asset, eMyMoney::Account::Liability, eMyMoney::Account::Income, eMyMoney::Account::Expense, eMyMoney::Account::Equity}); d->accountsModel->setHideEquityAccounts(false); d->accountsModel->setSourceModel(model); d->accountsModel->setSourceColumns(model->getColumns()); d->accountsModel->sort((int)eAccountsModel::Column::Account); d->ui->accountCombo->setModel(d->accountsModel); d->costCenterModel->setSortRole(Qt::DisplayRole); d->costCenterModel->setSourceModel(Models::instance()->costCenterModel()); d->costCenterModel->sort(0); d->ui->costCenterCombo->setEditable(true); d->ui->costCenterCombo->setModel(d->costCenterModel); d->ui->costCenterCombo->setModelColumn(0); d->ui->costCenterCombo->completer()->setFilterMode(Qt::MatchContains); d->payeesModel->setSortRole(Qt::DisplayRole); d->payeesModel->setSourceModel(Models::instance()->payeesModel()); d->payeesModel->sort(0); d->ui->payeeEdit->setEditable(true); d->ui->payeeEdit->setModel(d->payeesModel); d->ui->payeeEdit->setModelColumn(0); d->ui->payeeEdit->completer()->setFilterMode(Qt::MatchContains); d->ui->enterButton->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogOK])); d->ui->cancelButton->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogCancel])); d->ui->statusCombo->setModel(&d->statusModel); d->ui->dateEdit->setDisplayFormat(QLocale().dateFormat(QLocale::ShortFormat)); d->ui->amountEditCredit->setAllowEmpty(true); d->ui->amountEditDebit->setAllowEmpty(true); d->amountHelper = new CreditDebitHelper(this, d->ui->amountEditCredit, d->ui->amountEditDebit); WidgetHintFrameCollection* frameCollection = new WidgetHintFrameCollection(this); frameCollection->addFrame(new WidgetHintFrame(d->ui->dateEdit)); frameCollection->addFrame(new WidgetHintFrame(d->ui->costCenterCombo)); frameCollection->addFrame(new WidgetHintFrame(d->ui->numberEdit, WidgetHintFrame::Warning)); frameCollection->addWidget(d->ui->enterButton); connect(d->ui->numberEdit, SIGNAL(textChanged(QString)), this, SLOT(numberChanged(QString))); connect(d->ui->costCenterCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(costCenterChanged(int))); connect(d->ui->accountCombo, SIGNAL(accountSelected(QString)), this, SLOT(categoryChanged(QString))); connect(d->ui->dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(postdateChanged(QDate))); connect(d->amountHelper, SIGNAL(valueChanged()), this, SLOT(valueChanged())); connect(d->ui->cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject())); connect(d->ui->enterButton, SIGNAL(clicked(bool)), this, SLOT(acceptEdit())); connect(d->ui->splitEditorButton, SIGNAL(clicked(bool)), this, SLOT(editSplits())); // setup tooltip // setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); } NewTransactionEditor::~NewTransactionEditor() { } bool NewTransactionEditor::accepted() const { return d->accepted; } void NewTransactionEditor::acceptEdit() { if(d->checkForValidTransaction()) { d->accepted = true; emit done(); } } void NewTransactionEditor::reject() { emit done(); } void NewTransactionEditor::keyPressEvent(QKeyEvent* e) { if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) { switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: { if(focusWidget() == d->ui->cancelButton) { reject(); } else { if(d->ui->enterButton->isEnabled()) { d->ui->enterButton->click(); } return; } } break; case Qt::Key_Escape: reject(); break; default: e->ignore(); return; } } else { e->ignore(); } } void NewTransactionEditor::loadTransaction(const QString& id) { const LedgerModel* model = Models::instance()->ledgerModel(); const QString transactionId = model->transactionIdFromTransactionSplitId(id); if(id.isEmpty()) { d->transactionSplitId.clear(); d->transaction = MyMoneyTransaction(); if(lastUsedPostDate()->isValid()) { d->ui->dateEdit->setDate(*lastUsedPostDate()); } else { d->ui->dateEdit->setDate(QDate::currentDate()); } bool blocked = d->ui->accountCombo->lineEdit()->blockSignals(true); d->ui->accountCombo->lineEdit()->clear(); d->ui->accountCombo->lineEdit()->blockSignals(blocked); } else { // find which item has this id and set is as the current item QModelIndexList list = model->match(model->index(0, 0), (int)eLedgerModel::Role::TransactionId, QVariant(transactionId), -1, // all splits Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive)); Q_FOREACH(QModelIndex index, list) { // the selected split? const QString transactionSplitId = model->data(index, (int)eLedgerModel::Role::TransactionSplitId).toString(); if(transactionSplitId == id) { d->transactionSplitId = id; d->transaction = model->data(index, (int)eLedgerModel::Role::Transaction).value(); d->split = model->data(index, (int)eLedgerModel::Role::Split).value(); d->ui->dateEdit->setDate(model->data(index, (int)eLedgerModel::Role::PostDate).toDate()); d->ui->payeeEdit->lineEdit()->setText(model->data(index, (int)eLedgerModel::Role::PayeeName).toString()); d->ui->memoEdit->clear(); d->ui->memoEdit->insertPlainText(model->data(index, (int)eLedgerModel::Role::Memo).toString()); d->ui->memoEdit->moveCursor(QTextCursor::Start); d->ui->memoEdit->ensureCursorVisible(); // The calculator for the amount field can simply be added as an icon to the line edit widget. // See http://stackoverflow.com/questions/11381865/how-to-make-an-extra-icon-in-qlineedit-like-this howto do it d->ui->amountEditCredit->setText(model->data(model->index(index.row(), (int)eLedgerModel::Column::Payment)).toString()); d->ui->amountEditDebit->setText(model->data(model->index(index.row(), (int)eLedgerModel::Column::Deposit)).toString()); d->ui->numberEdit->setText(model->data(index, (int)eLedgerModel::Role::Number).toString()); d->ui->statusCombo->setCurrentIndex(model->data(index, (int)eLedgerModel::Role::Number).toInt()); QModelIndexList stList = d->statusModel.match(d->statusModel.index(0, 0), Qt::UserRole+1, model->data(index, (int)eLedgerModel::Role::Reconciliation).toInt()); if(stList.count()) { QModelIndex stIndex = stList.front(); d->ui->statusCombo->setCurrentIndex(stIndex.row()); } } else { d->splitModel.addSplit(transactionSplitId); } } d->updateWidgetState(); } // set focus to payee edit once we return to event loop QMetaObject::invokeMethod(d->ui->payeeEdit, "setFocus", Qt::QueuedConnection); } void NewTransactionEditor::numberChanged(const QString& newNumber) { d->numberChanged(newNumber); } void NewTransactionEditor::categoryChanged(const QString& accountId) { d->categoryChanged(accountId); } void NewTransactionEditor::costCenterChanged(int costCenterIndex) { d->costCenterChanged(costCenterIndex); } void NewTransactionEditor::postdateChanged(const QDate& date) { d->postdateChanged(date); } void NewTransactionEditor::valueChanged() { d->valueChanged(d->amountHelper); } void NewTransactionEditor::editSplits() { SplitModel splitModel; splitModel.deepCopy(d->splitModel, true); // create an empty split at the end splitModel.addEmptySplitEntry(); QPointer splitDialog = new SplitDialog(d->account, transactionAmount(), this); splitDialog->setModel(&splitModel); int rc = splitDialog->exec(); if(splitDialog && (rc == QDialog::Accepted)) { // remove that empty split again before we update the splits splitModel.removeEmptySplitEntry(); // copy the splits model contents d->splitModel.deepCopy(splitModel, true); // update the transaction amount d->amountHelper->setValue(splitDialog->transactionAmount()); d->updateWidgetState(); QWidget *next = d->ui->tagComboBox; if(d->ui->costCenterCombo->isEnabled()) { next = d->ui->costCenterCombo; } next->setFocus(); } if(splitDialog) { splitDialog->deleteLater(); } } MyMoneyMoney NewTransactionEditor::transactionAmount() const { return d->amountHelper->value(); } void NewTransactionEditor::saveTransaction() { MyMoneyTransaction t; if(!d->transactionSplitId.isEmpty()) { t = d->transaction; } else { // we keep the date when adding a new transaction // for the next new one *lastUsedPostDate() = d->ui->dateEdit->date(); } QList splits = t.splits(); // first remove the splits that are gone QList::iterator it; for(it = splits.begin(); it != splits.end(); ++it) { if((*it).id() == d->split.id()) { continue; } int row; for(row = 0; row < d->splitModel.rowCount(); ++row) { QModelIndex index = d->splitModel.index(row, 0); if(d->splitModel.data(index, (int)eLedgerModel::Role::SplitId).toString() == (*it).id()) { break; } } // if the split is not in the model, we get rid of it if(d->splitModel.rowCount() == row) { t.removeSplit(*it); } } MyMoneyFileTransaction ft; try { // new we update the split we are opened for MyMoneySplit sp(d->split); sp.setNumber(d->ui->numberEdit->text()); sp.setMemo(d->ui->memoEdit->toPlainText()); sp.setShares(d->amountHelper->value()); if(t.commodity().isEmpty()) { t.setCommodity(d->account.currencyId()); sp.setValue(d->amountHelper->value()); } else { /// @todo check that the transactions commodity is the same /// as the one of the account this split references. If /// that is not the case, the next statement would create /// a problem sp.setValue(d->amountHelper->value()); } if(sp.reconcileFlag() != eMyMoney::Split::State::Reconciled && !sp.reconcileDate().isValid() && d->ui->statusCombo->currentIndex() == (int)eMyMoney::Split::State::Reconciled) { sp.setReconcileDate(QDate::currentDate()); } sp.setReconcileFlag(static_cast(d->ui->statusCombo->currentIndex())); // sp.setPayeeId(d->ui->payeeEdit->cu) if(sp.id().isEmpty()) { t.addSplit(sp); } else { t.modifySplit(sp); } t.setPostDate(d->ui->dateEdit->date()); // now update and add what we have in the model const SplitModel * model = &d->splitModel; for(int row = 0; row < model->rowCount(); ++row) { QModelIndex index = model->index(row, 0); MyMoneySplit s; const QString splitId = model->data(index, (int)eLedgerModel::Role::SplitId).toString(); if(!SplitModel::isNewSplitId(splitId)) { s = t.splitById(splitId); } s.setNumber(model->data(index, (int)eLedgerModel::Role::Number).toString()); s.setMemo(model->data(index, (int)eLedgerModel::Role::Memo).toString()); s.setAccountId(model->data(index, (int)eLedgerModel::Role::AccountId).toString()); s.setShares(model->data(index, (int)eLedgerModel::Role::SplitShares).value()); s.setValue(model->data(index, (int)eLedgerModel::Role::SplitValue).value()); s.setCostCenterId(model->data(index, (int)eLedgerModel::Role::CostCenterId).toString()); s.setPayeeId(model->data(index, (int)eLedgerModel::Role::PayeeId).toString()); // reconcile flag and date if(s.id().isEmpty()) { t.addSplit(s); } else { t.modifySplit(s); } } if(t.id().isEmpty()) { MyMoneyFile::instance()->addTransaction(t); } else { MyMoneyFile::instance()->modifyTransaction(t); } ft.commit(); } catch (const MyMoneyException &e) { qDebug() << Q_FUNC_INFO << "something went wrong" << e.what(); } } diff --git a/kmymoney/widgets/CMakeLists.txt b/kmymoney/widgets/CMakeLists.txt index 5e6b5d072..ec1825a9f 100644 --- a/kmymoney/widgets/CMakeLists.txt +++ b/kmymoney/widgets/CMakeLists.txt @@ -1,201 +1,229 @@ ########### 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 - scheduledtransaction.h selectedtransaction.h stdtransactiondownloaded.h + kmymoneytitlelabel.h kmymoneywizard.h register.h registeritem.h groupmarker.h fancydategroupmarker.h + scheduledtransaction.h selectedtransaction.h selectedtransactions.h stdtransactiondownloaded.h stdtransactionmatched.h transactioneditorcontainer.h - transactionform.h transaction.h transactionsortoptionimpl.h - reporttabimpl.h reportcontrolimpl.h + transactionform.h transaction.h investtransaction.h stdtransaction.h + transactionsortoption.h reporttabimpl.h reportcontrolimpl.h kmymoneyvalidationfeedback.h onlinejobmessagesview.h kmymoneydateedit.h - amountedit.h + amountedit.h creditdebithelper.h ) ########### Shared widget library ########### set(kmm_widgets_sources kmymoneydateinput.cpp kmymoneyvalidationfeedback.cpp styleditemdelegateforwarder.cpp kmymoneyedit.cpp kmymoneylineedit.cpp kmymoneytextedit.cpp + kmymoneytextedithighlighter.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 + creditdebithelper.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 + kmymoneywizardpage.cpp kpricetreeitem.cpp registeritem.cpp + registerfilter.cpp scheduledtransaction.cpp selectedtransaction.cpp + selectedtransactions.cpp stdtransactiondownloaded.cpp stdtransactionmatched.cpp transactionform.cpp - transactionsortoptionimpl.cpp + tabbar.cpp + transactionformitemdelegate.cpp + transactionsortoption.cpp ) # sources that contain the KMM_DESIGNER flag set (_critial_common_sources kaccounttemplateselector.cpp kmymoneycurrencyselector.cpp kmymoneyaccountcompletion.cpp kmymoneycategory.cpp + groupmarker.cpp + groupmarkers.cpp + fancydategroupmarker.cpp + fancydategroupmarkers.cpp register.cpp + itemptrvector.cpp + qwidgetcontainer.cpp + registeritemdelegate.cpp transaction.cpp + stdtransaction.cpp + investtransaction.cpp transactioneditorcontainer.cpp ) set (kmymoney_base_UI - kbudgetvaluesdecl.ui transactionsortoptiondecl.ui kaccounttemplateselectordecl.ui + kbudgetvalues.ui transactionsortoption.ui kaccounttemplateselector.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 + kmymoneybriefschedule.ui reportcontrol.ui reporttabgeneral.ui reporttabrowcolquery.ui reporttabrowcolpivot.ui reporttabrange.ui reporttabchart.ui reporttabcapitalgain.ui reporttabperformance.ui - daterangedlgdecl.ui + daterangedlg.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 1f7cfd123..a93705e16 100644 --- a/kmymoney/widgets/amountedit.cpp +++ b/kmymoney/widgets/amountedit.cpp @@ -1,591 +1,555 @@ /*************************************************************************** amountedit.cpp ------------------- copyright : (C) 2016 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 "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 +class AmountEditPrivate { + Q_DISABLE_COPY(AmountEditPrivate) + Q_DECLARE_PUBLIC(AmountEdit) + public: - Private(AmountEdit* q) - : m_q(q) - , m_allowEmpty(false) + AmountEditPrivate(AmountEdit* qq) : + q_ptr(qq), + m_allowEmpty(false) { - m_calculatorFrame = new QFrame(m_q); + Q_Q(AmountEdit); + m_calculatorFrame = new QFrame(q); m_calculatorFrame->setWindowFlags(Qt::Popup); m_calculatorFrame->setFrameStyle(QFrame::Panel | QFrame::Raised); m_calculatorFrame->setLineWidth(3); - m_calculator = new kMyMoneyCalculator(m_calculatorFrame); + m_calculator = new KMyMoneyCalculator(m_calculatorFrame); m_calculatorFrame->hide(); } - AmountEdit* m_q; + void init() + { + Q_Q(AmountEdit); + // Yes, just a simple double validator ! + auto validator = new AmountValidator(q); + q->setValidator(validator); + q->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + + int height = q->sizeHint().height(); + int btnSize = q->sizeHint().height() - 5; + + m_calculatorButton = new QToolButton(q); + m_calculatorButton->setIcon(QIcon::fromTheme(g_Icons[Icon::AccessoriesCalculator])); + m_calculatorButton->setCursor(Qt::ArrowCursor); + m_calculatorButton->setStyleSheet("QToolButton { border: none; padding: 2px}"); + m_calculatorButton->setFixedSize(btnSize, btnSize); + m_calculatorButton->show(); + + int frameWidth = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + q->setStyleSheet(QString("QLineEdit { padding-right: %1px }") + .arg(btnSize - frameWidth)); + q->setMinimumHeight(height); + + q->connect(m_calculatorButton, &QAbstractButton::clicked, q, &AmountEdit::slotCalculatorOpen); + + KSharedConfig::Ptr kconfig = KSharedConfig::openConfig(); + KConfigGroup grp = kconfig->group("General Options"); + if (grp.readEntry("DontShowCalculatorButton", false) == true) + q->setCalculatorButtonVisible(false); + + q->connect(q, &QLineEdit::textChanged, q, &AmountEdit::theTextChanged); + q->connect(m_calculator, &KMyMoneyCalculator::signalResultAvailable, q, &AmountEdit::slotCalculatorResult); + q->connect(m_calculator, &KMyMoneyCalculator::signalQuit, q, &AmountEdit::slotCalculatorClose); + } + + /** + * Internal helper function for value() and ensureFractionalPart(). + */ + void ensureFractionalPart(QString& s) const + { + s = MyMoneyMoney(s).formatMoney(QString(), m_prec, false); + } + + /** + * 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(AmountEdit); + m_calculator->setInitialValues(q->text(), k); + + auto h = m_calculatorFrame->height(); + auto 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 + auto 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(); + } + + AmountEdit* q_ptr; QFrame* m_calculatorFrame; - kMyMoneyCalculator* m_calculator; + 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 + /** + * This holds the number of precision to be used + * when no other information (e.g. from account) + * is available. + * + * @sa setStandardPrecision() + */ + static int standardPrecision; }; +int AmountEditPrivate::standardPrecision = 2; - - - -int AmountEdit::standardPrecision = 2; - -AmountEdit::AmountEdit(QWidget *parent, const int prec) - : QLineEdit(parent) - , d(new Private(this)) +AmountEdit::AmountEdit(QWidget *parent, const int prec) : + QLineEdit(parent), + d_ptr(new AmountEditPrivate(this)) { + Q_D(AmountEdit); d->m_prec = prec; if (prec < -1 || prec > 20) { - d->m_prec = standardPrecision; + d->m_prec = AmountEditPrivate::standardPrecision; } - init(); + d->init(); } -AmountEdit::AmountEdit(const MyMoneySecurity& sec, QWidget *parent) - : QLineEdit(parent) - , d(new Private(this)) +AmountEdit::AmountEdit(const MyMoneySecurity& sec, QWidget *parent) : + QLineEdit(parent), + d_ptr(new AmountEditPrivate(this)) { + Q_D(AmountEdit); d->m_prec = MyMoneyMoney::denomToPrec(sec.smallestAccountFraction()); - init(); + d->init(); } AmountEdit::~AmountEdit() { + Q_D(AmountEdit); + delete d; } void AmountEdit::setStandardPrecision(int prec) { if (prec >= 0 && prec < 20) { - standardPrecision = prec; + AmountEditPrivate::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_D(AmountEdit); 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) { + Q_D(AmountEdit); 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) { + Q_D(AmountEdit); 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); + d->calculatorOpen(event); break; default: QLineEdit::keyPressEvent(event); break; } } void AmountEdit::setPrecision(const int prec) { + Q_D(AmountEdit); if (prec >= -1 && prec <= 20) { if (prec != d->m_prec) { d->m_prec = prec; // update current display setValue(value()); } } } int AmountEdit::precision() const { + Q_D(const AmountEdit); return d->m_prec; } - bool AmountEdit::isValid() const { return !(text().isEmpty()); } +QString AmountEdit::numericalText() const +{ + return value().toString(); +} + MyMoneyMoney AmountEdit::value() const { + Q_D(const AmountEdit); MyMoneyMoney money(text()); if (d->m_prec != -1) money = money.convert(MyMoneyMoney::precToDenom(d->m_prec)); return money; } void AmountEdit::setValue(const MyMoneyMoney& value) { + Q_D(AmountEdit); // load the value into the widget but don't use thousandsSeparators - setText(value.formatMoney("", d->m_prec, false)); + setText(value.formatMoney(QString(), d->m_prec, false)); } void AmountEdit::setText(const QString& txt) { + Q_D(AmountEdit); d->m_text = txt; if (isEnabled() && !txt.isEmpty()) - ensureFractionalPart(d->m_text); + d->ensureFractionalPart(d->m_text); QLineEdit::setText(d->m_text); #if 0 m_resetButton->setEnabled(false); #endif } void AmountEdit::resetText() { + Q_D(AmountEdit); #if 0 setText(d->m_text); m_resetButton->setEnabled(false); #endif } void AmountEdit::theTextChanged(const QString & theText) { + Q_D(AmountEdit); QLocale locale; QString dec = locale.groupSeparator(); QString l_text = theText; QString nsign, psign; nsign = locale.negativeSign(); psign = locale.positiveSign(); - int i = 0; + auto 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("", 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(); + Q_D(AmountEdit); + d->calculatorOpen(0); } void AmountEdit::slotCalculatorClose() { + Q_D(AmountEdit); if (d->m_calculator != 0) { d->m_calculatorFrame->hide(); } } void AmountEdit::slotCalculatorResult() { + Q_D(AmountEdit); 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) { + Q_D(AmountEdit); d->m_calculatorButton->setVisible(show); } void AmountEdit::setAllowEmpty(bool allowed) { + Q_D(AmountEdit); d->m_allowEmpty = allowed; } bool AmountEdit::isEmptyAllowed() const { + Q_D(const AmountEdit); return d->m_allowEmpty; } bool AmountEdit::isCalculatorButtonVisible() const { + Q_D(const AmountEdit); 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) +void AmountEdit::ensureFractionalPart() { - 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."; - } + Q_D(AmountEdit); + QString s(text()); + d->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); } diff --git a/kmymoney/widgets/amountedit.h b/kmymoney/widgets/amountedit.h index b7bd13892..704385dd2 100644 --- a/kmymoney/widgets/amountedit.h +++ b/kmymoney/widgets/amountedit.h @@ -1,281 +1,209 @@ /*************************************************************************** amountedit.h ------------------- copyright : (C) 2016 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 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 AmountEditPrivate; class KMM_WIDGETS_EXPORT AmountEdit : public QLineEdit { Q_OBJECT + Q_DISABLE_COPY(AmountEdit) + 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 = 0, const int prec = -2); - explicit AmountEdit(const MyMoneySecurity& eq, QWidget *parent = 0); + explicit AmountEdit(QWidget* parent = nullptr, const int prec = -2); + explicit AmountEdit(const MyMoneySecurity& eq, QWidget* parent = nullptr); 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 - { - return value().toString(); - } + QString numericalText() const; /** * 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(); +protected: + /** + * This method ensures that the text version contains a + * fractional part. + */ + void ensureFractionalPart(); /** - * Retruns the value of the widget that is filled. - * A credit is retruned as negative, a debit as positive value. + * Overridden to support calculator button. */ - MyMoneyMoney value() const; + virtual void resizeEvent(QResizeEvent* event) override; /** - * 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. + * Overridden to support ensureFractionalPart(). */ - void setValue(const MyMoneyMoney& value); + virtual void focusOutEvent(QFocusEvent* event) override; /** - * This method returns true if at least one - * of the two widgets is filled with text. - * It returns false if both widgets are empty. + * Overridden to support calculator button. */ - bool haveValue() const; - -Q_SIGNALS: - void valueChanged(); - -private Q_SLOTS: - void creditChanged(); - void debitChanged(); - -private: - void widgetChanged(AmountEdit* src, AmountEdit* dst); + virtual void keyPressEvent(QKeyEvent* event) override; private: - QPointer m_credit; - QPointer m_debit; + AmountEditPrivate * const d_ptr; + Q_DECLARE_PRIVATE(AmountEdit) }; #endif diff --git a/kmymoney/widgets/creditdebithelper.cpp b/kmymoney/widgets/creditdebithelper.cpp new file mode 100644 index 000000000..5a87ac09d --- /dev/null +++ b/kmymoney/widgets/creditdebithelper.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + creditdebithelper.cpp + ------------------- + copyright : (C) 2016 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 "creditdebithelper.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "amountedit.h" + +class CreditDebitHelperPrivate +{ + Q_DISABLE_COPY(CreditDebitHelperPrivate) + Q_DECLARE_PUBLIC(CreditDebitHelper) + +public: + CreditDebitHelperPrivate(CreditDebitHelper *qq) : + q_ptr(qq) + { + } + + void 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(); + } + Q_Q(CreditDebitHelper); + emit q->valueChanged(); + } + + CreditDebitHelper *q_ptr; + QPointer m_credit; + QPointer m_debit; +}; + +CreditDebitHelper::CreditDebitHelper(QObject* parent, AmountEdit* credit, AmountEdit* debit) : + QObject(parent), + d_ptr(new CreditDebitHelperPrivate(this)) +{ + Q_D(CreditDebitHelper); + d->m_credit = credit; + d->m_debit = debit; + connect(d->m_credit.data(), &AmountEdit::valueChanged, this, &CreditDebitHelper::creditChanged); + connect(d->m_debit.data(), &AmountEdit::valueChanged, this, &CreditDebitHelper::debitChanged); +} + +CreditDebitHelper::~CreditDebitHelper() +{ + Q_D(CreditDebitHelper); + delete d; +} + +void CreditDebitHelper::creditChanged() +{ + Q_D(CreditDebitHelper); + d->widgetChanged(d->m_credit, d->m_debit); +} + +void CreditDebitHelper::debitChanged() +{ + Q_D(CreditDebitHelper); + d->widgetChanged(d->m_debit, d->m_credit); +} + +bool CreditDebitHelper::haveValue() const +{ + Q_D(const CreditDebitHelper); + return (!d->m_credit->text().isEmpty()) || (!d->m_debit->text().isEmpty()); +} + +MyMoneyMoney CreditDebitHelper::value() const +{ + Q_D(const CreditDebitHelper); + MyMoneyMoney value; + if(d->m_credit && d->m_debit) { + if(!d->m_credit->text().isEmpty()) { + value = -d->m_credit->value(); + } else { + value = d->m_debit->value(); + } + } else { + qWarning() << "CreditDebitHelper::value() called with no objects attached. Zero returned."; + } + return value; +} + +void CreditDebitHelper::setValue(const MyMoneyMoney& value) +{ + Q_D(CreditDebitHelper); + if(d->m_credit && d->m_debit) { + if(value.isNegative()) { + d->m_credit->setValue(-value); + d->m_debit->clear(); + } else { + d->m_debit->setValue(value); + d->m_credit->clear(); + } + } else { + qWarning() << "CreditDebitHelper::setValue() called with no objects attached. Skipped."; + } +} diff --git a/kmymoney/widgets/creditdebithelper.h b/kmymoney/widgets/creditdebithelper.h new file mode 100644 index 000000000..20f2ad744 --- /dev/null +++ b/kmymoney/widgets/creditdebithelper.h @@ -0,0 +1,81 @@ +/*************************************************************************** + creditdebithelper.h + ------------------- + copyright : (C) 2016 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 CREDITDEBITHELPER_H +#define CREDITDEBITHELPER_H + +#include + +#include "kmm_widgets_export.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +class AmountEdit; +class MyMoneyMoney; + +class CreditDebitHelperPrivate; +class KMM_WIDGETS_EXPORT CreditDebitHelper : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(CreditDebitHelper) + +public: + explicit CreditDebitHelper(QObject* parent, AmountEdit* credit, AmountEdit* debit); + ~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: + CreditDebitHelperPrivate * const d_ptr; + Q_DECLARE_PRIVATE(CreditDebitHelper) +}; + +#endif diff --git a/kmymoney/widgets/daterangedlg.cpp b/kmymoney/widgets/daterangedlg.cpp index fc489fd16..e04f9d03e 100644 --- a/kmymoney/widgets/daterangedlg.cpp +++ b/kmymoney/widgets/daterangedlg.cpp @@ -1,124 +1,158 @@ /*************************************************************************** daterange.cpp ------------------- copyright : (C) 2003, 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 - #include "daterangedlg.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "mymoneytransactionfilter.h" +#include "mymoneyenums.h" #include "kmymoneydateinput.h" -#include "kmymoneymvccombo.h" -#include "ui_daterangedlgdecl.h" +#include "ui_daterangedlg.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, static_cast(&KMyMoneyPeriodCombo::currentIndexChanged), q, &DateRangeDlg::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), - m_ui(new Ui::DateRangeDlgDecl) + QWidget(parent), + d_ptr(new DateRangeDlgPrivate(this)) { - m_ui->setupUi(this); - setupDatePage(); + Q_D(DateRangeDlg); + d->ui->setupUi(this); + d->setupDatePage(); } DateRangeDlg::~DateRangeDlg() { - delete m_ui; + Q_D(DateRangeDlg); + delete d; } void DateRangeDlg::slotReset() { - 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())); - + Q_D(DateRangeDlg); + d->ui->m_dateRange->setCurrentItem(TransactionFilter::Date::All); setDateRange(TransactionFilter::Date::All); } void DateRangeDlg::slotDateRangeSelectedByUser() { - setDateRange(m_ui->m_dateRange->currentItem()); + Q_D(DateRangeDlg); + setDateRange(d->ui->m_dateRange->currentItem()); } void DateRangeDlg::setDateRange(const QDate& from, const QDate& to) { - m_ui->m_fromDate->loadDate(from); - m_ui->m_toDate->loadDate(to); - m_ui->m_dateRange->setCurrentItem(TransactionFilter::Date::UserDefined); - emit rangeChanged(); + 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(); } void DateRangeDlg::setDateRange(TransactionFilter::Date idx) { - m_ui->m_dateRange->setCurrentItem(idx); + Q_D(DateRangeDlg); + d->ui->m_dateRange->setCurrentItem(idx); switch (idx) { case TransactionFilter::Date::All: - m_ui->m_fromDate->loadDate(QDate()); - m_ui->m_toDate->loadDate(QDate()); + d->ui->m_fromDate->loadDate(QDate()); + d->ui->m_toDate->loadDate(QDate()); break; case TransactionFilter::Date::UserDefined: break; default: - 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); + 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); break; } emit rangeChanged(); } TransactionFilter::Date DateRangeDlg::dateRange() const { - return m_ui->m_dateRange->currentItem(); + Q_D(const DateRangeDlg); + return d->ui->m_dateRange->currentItem(); } void DateRangeDlg::slotDateChanged() { - m_ui->m_dateRange->blockSignals(true); - m_ui->m_dateRange->setCurrentItem(TransactionFilter::Date::UserDefined); - m_ui->m_dateRange->blockSignals(false); + Q_D(DateRangeDlg); + d->ui->m_dateRange->blockSignals(true); + d->ui->m_dateRange->setCurrentItem(TransactionFilter::Date::UserDefined); + d->ui->m_dateRange->blockSignals(false); } QDate DateRangeDlg::fromDate() const { - return m_ui->m_fromDate->date(); + Q_D(const DateRangeDlg); + return d->ui->m_fromDate->date(); } QDate DateRangeDlg::toDate() const { - return m_ui->m_toDate->date(); + Q_D(const DateRangeDlg); + return d->ui->m_toDate->date(); } diff --git a/kmymoney/widgets/daterangedlg.h b/kmymoney/widgets/daterangedlg.h index 66e9dd3c5..aae9b0135 100644 --- a/kmymoney/widgets/daterangedlg.h +++ b/kmymoney/widgets/daterangedlg.h @@ -1,88 +1,80 @@ /*************************************************************************** daterange.h ------------------- copyright : (C) 2003 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. * * * ***************************************************************************/ #ifndef DATERANGEDLG_H #define DATERANGEDLG_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include // ---------------------------------------------------------------------------- // KDE Includes -#include - // ---------------------------------------------------------------------------- // Project Includes -#include "mymoneytransactionfilter.h" -#include "mymoneyenums.h" - -namespace Ui -{ -class DateRangeDlgDecl; -} +namespace eMyMoney { namespace TransactionFilter { enum class Date; } } +class DateRangeDlgPrivate; class DateRangeDlg : public QWidget { Q_OBJECT + Q_DISABLE_COPY(DateRangeDlg) public: - DateRangeDlg(QWidget *parent = 0); + explicit DateRangeDlg(QWidget* parent = nullptr); ~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: - void setupDatePage(); - - Ui::DateRangeDlgDecl* m_ui; - QDate m_startDates[(int)eMyMoney::TransactionFilter::Date::LastDateItem]; - QDate m_endDates[(int)eMyMoney::TransactionFilter::Date::LastDateItem]; + DateRangeDlgPrivate * const d_ptr; + Q_DECLARE_PRIVATE(DateRangeDlg) }; #endif diff --git a/kmymoney/widgets/daterangedlgdecl.ui b/kmymoney/widgets/daterangedlg.ui similarity index 83% rename from kmymoney/widgets/daterangedlgdecl.ui rename to kmymoney/widgets/daterangedlg.ui index 10f23f465..16133e444 100644 --- a/kmymoney/widgets/daterangedlgdecl.ui +++ b/kmymoney/widgets/daterangedlg.ui @@ -1,63 +1,63 @@ - DateRangeDlgDecl - + DateRangeDlg + Range false From false - + To false - + KMyMoneyPeriodCombo QWidget -
kmymoneymvccombo.h
+
kmymoneyperiodcombo.h
1
- kMyMoneyDateInput + KMyMoneyDateInput QWidget
kmymoneydateinput.h
diff --git a/kmymoney/widgets/fancydategroupmarker.cpp b/kmymoney/widgets/fancydategroupmarker.cpp new file mode 100644 index 000000000..60709e707 --- /dev/null +++ b/kmymoney/widgets/fancydategroupmarker.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + 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" + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include + +// ---------------------------------------------------------------------------- +// Project Includes +#include "groupmarker.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/transactioneditorcontainer.cpp b/kmymoney/widgets/fancydategroupmarker.h similarity index 57% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/fancydategroupmarker.h index d4fd1f27a..7f0181aad 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/fancydategroupmarker.h @@ -1,28 +1,54 @@ /*************************************************************************** - transactioneditorcontainer.cpp + fancydategroupmarker.h ---------- - begin : Wed Jun 07 2006 + 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 "transactioneditorcontainer.h" +#ifndef FANCYDATEGROUPMARKER_H +#define FANCYDATEGROUPMARKER_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "groupmarker.h" + +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/transactioneditorcontainer.cpp b/kmymoney/widgets/fancydategroupmarker_p.h similarity index 70% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/fancydategroupmarker_p.h index d4fd1f27a..6843c2868 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/fancydategroupmarker_p.h @@ -1,28 +1,44 @@ /*************************************************************************** - transactioneditorcontainer.cpp - ---------- - begin : Wed Jun 07 2006 + 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. * * * ***************************************************************************/ -#include "transactioneditorcontainer.h" +#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/fancydategroupmarkers.cpp b/kmymoney/widgets/fancydategroupmarkers.cpp new file mode 100644 index 000000000..5461e2378 --- /dev/null +++ b/kmymoney/widgets/fancydategroupmarkers.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + fancydategroupmarkers.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 "fancydategroupmarkers.h" +#include "fancydategroupmarker_p.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include +#include +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneyglobalsettings.h" +#include "widgetenums.h" + +using namespace KMyMoneyRegister; +using namespace eMyMoney; + +namespace KMyMoneyRegister { + class StatementGroupMarkerPrivate : public FancyDateGroupMarkerPrivate + { + public: + eWidgets::eRegister::CashFlowDirection m_dir; + }; +} + +StatementGroupMarker::StatementGroupMarker(Register* parent, eWidgets::eRegister::CashFlowDirection dir, const QDate& date, const QString& txt) : + FancyDateGroupMarker(*new StatementGroupMarkerPrivate, parent, date, txt) +{ + Q_D(StatementGroupMarker); + d->m_dir = dir; + d->m_showDate = true; +} + +StatementGroupMarker::~StatementGroupMarker() +{ +} + +eWidgets::eRegister::CashFlowDirection StatementGroupMarker::sortType() const +{ + Q_D(const StatementGroupMarker); + return d->m_dir; +} + +int StatementGroupMarker::sortSamePostDate() const +{ + return 3; +} + +FiscalYearGroupMarker::FiscalYearGroupMarker(Register* parent, const QDate& date, const QString& txt) : + FancyDateGroupMarker(parent, date, txt) +{ +} + +FiscalYearGroupMarker::~FiscalYearGroupMarker() +{ +} + +const char* FiscalYearGroupMarker::className() +{ + return "FiscalYearGroupMarker"; +} + +int FiscalYearGroupMarker::sortSamePostDate() const +{ + return 1; +} + +SimpleDateGroupMarker::SimpleDateGroupMarker(Register* parent, const QDate& date, const QString& txt) : + FancyDateGroupMarker(parent, date, txt) +{ +} + +SimpleDateGroupMarker::~SimpleDateGroupMarker() +{ +} + +int SimpleDateGroupMarker::rowHeightHint() const +{ + Q_D(const FancyDateGroupMarker); + if (!d->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)); + + if (d->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(); +} diff --git a/kmymoney/widgets/fancydategroupmarkers.h b/kmymoney/widgets/fancydategroupmarkers.h new file mode 100644 index 000000000..ff1be63ec --- /dev/null +++ b/kmymoney/widgets/fancydategroupmarkers.h @@ -0,0 +1,86 @@ +/*************************************************************************** + fancydategroupmarkers.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 FANCYDATEGROUPMARKERS_H +#define FANCYDATEGROUPMARKERS_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "fancydategroupmarker.h" + +namespace eWidgets { enum class SortField; + namespace Transaction { enum class Column; } + namespace Register { enum class DetailColumn;} } +namespace eMyMoney { enum class Account; } + +namespace KMyMoneyRegister +{ + class Register; + + class StatementGroupMarkerPrivate; + class StatementGroupMarker : public FancyDateGroupMarker + { + Q_DISABLE_COPY(StatementGroupMarker) + + public: + explicit StatementGroupMarker(Register* getParent, eWidgets::eRegister::CashFlowDirection dir, const QDate& date, const QString& txt); + ~StatementGroupMarker() override; + + eWidgets::eRegister::CashFlowDirection sortType() const override; + int sortSamePostDate() const override; + + private: + Q_DECLARE_PRIVATE(StatementGroupMarker) + }; + + + class SimpleDateGroupMarker : public FancyDateGroupMarker + { + Q_DISABLE_COPY(SimpleDateGroupMarker) + + public: + explicit SimpleDateGroupMarker(Register* getParent, const QDate& date, const QString& txt); + ~SimpleDateGroupMarker() override; + + void paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; + int rowHeightHint() const; + const char* className() override; + }; + + 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; + }; + +} // namespace + +#endif diff --git a/kmymoney/widgets/fixedcolumntreeview.cpp b/kmymoney/widgets/fixedcolumntreeview.cpp index ecf840ea5..c985a0f74 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 (int i = 0; i < rows; ++i) { + for (auto 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))); + connect(verticalScrollBar(), &QAbstractSlider::valueChanged, d->parent->verticalScrollBar(), &QAbstractSlider::setValue); + connect(d->parent->verticalScrollBar(), &QAbstractSlider::valueChanged, verticalScrollBar(), &QAbstractSlider::setValue); // 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))); + connect(d->parent, &QTreeView::expanded, this, &FixedColumnTreeView::onExpanded); + connect(this, &QTreeView::expanded, this, &FixedColumnTreeView::onExpanded); + connect(d->parent, &QTreeView::collapsed, this, &FixedColumnTreeView::onCollapsed); + connect(this, &QTreeView::collapsed, this, &FixedColumnTreeView::onCollapsed); // 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))); + connect(d->parent->header(), &QHeaderView::sortIndicatorChanged, this, &FixedColumnTreeView::updateSortIndicator); + connect(header(), &QHeaderView::sortIndicatorChanged, this, &FixedColumnTreeView::updateSortIndicator); // 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())); + connect(this, &QAbstractItemView::activated, d->parent, &QAbstractItemView::activated); + connect(this, &QAbstractItemView::clicked, d->parent, &QAbstractItemView::clicked); + connect(this, &QAbstractItemView::doubleClicked, d->parent, &QAbstractItemView::doubleClicked); + connect(this, &QAbstractItemView::entered, d->parent, &QAbstractItemView::entered); + connect(this, &QAbstractItemView::pressed, d->parent, &QAbstractItemView::pressed); + connect(this, &QAbstractItemView::viewportEntered, d->parent, &QAbstractItemView::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))); + connect(d->parent->header(), &QHeaderView::sectionResized, this, &FixedColumnTreeView::updateSectionWidth); // 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))); + connect(this, &QWidget::customContextMenuRequested, d->parent, &QWidget::customContextMenuRequested); } // 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 d7b40ccc6..7fe2c13d3 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: - FixedColumnTreeView(QTreeView *parent); + explicit 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); - bool eventFilter(QObject *object, QEvent *event); + bool viewportEvent(QEvent *event) override; + bool eventFilter(QObject *object, QEvent *event) override; 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 new file mode 100644 index 000000000..bfd816ab1 --- /dev/null +++ b/kmymoney/widgets/groupmarker.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** + 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" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "widgetenums.h" + +using namespace KMyMoneyRegister; + +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; +} + +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((int)eWidgets::eTransaction::Column::Date)); + cellRect.setWidth(d->m_parent->horizontalHeader()->sectionSize((int)eWidgets::eTransaction::Column::Date)); + 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/transactioneditorcontainer.h b/kmymoney/widgets/groupmarker.h similarity index 51% copy from kmymoney/widgets/transactioneditorcontainer.h copy to kmymoney/widgets/groupmarker.h index 754caf189..103810eaa 100644 --- a/kmymoney/widgets/transactioneditorcontainer.h +++ b/kmymoney/widgets/groupmarker.h @@ -1,70 +1,69 @@ /*************************************************************************** - transactioneditorcontainer.h + groupmarker.h ---------- - begin : Wed Jun 07 2006 + 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 TRANSACTIONEDITORCONTAINER_H -#define TRANSACTIONEDITORCONTAINER_H +#ifndef GROUPMARKER_H +#define GROUPMARKER_H // ---------------------------------------------------------------------------- // QT Includes -#include -#include - // ---------------------------------------------------------------------------- // KDE Includes - // ---------------------------------------------------------------------------- // Project Includes +#include "registeritem.h" + namespace KMyMoneyRegister { -class Transaction; -} +class Register; -typedef enum { - ProtectNone = 0, - ProtectTransfer, - ProtectNonTransfer, - ProtectAll -} ProtectedAction; - -class TransactionEditorContainer : public QTableWidget +class GroupMarkerPrivate; +class GroupMarker : public RegisterItem { - Q_OBJECT + Q_DISABLE_COPY(GroupMarker) public: - TransactionEditorContainer(QWidget* parent) : QTableWidget(parent) {} + explicit GroupMarker(Register* getParent, const QString& txt); + ~GroupMarker() override; + + void setText(const QString& txt); + 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; - 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; + int rowHeightHint() const override; -signals: - void geometriesUpdated(); + bool matches(const RegisterFilter&) const override; + int sortSamePostDate() const override; + void setErroneous(bool condition = true); -protected slots: - void updateGeometries() { - QTableWidget::updateGeometries(); - emit geometriesUpdated(); - } +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 new file mode 100644 index 000000000..908874936 --- /dev/null +++ b/kmymoney/widgets/groupmarker_p.h @@ -0,0 +1,114 @@ +/*************************************************************************** + 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/groupmarkers.cpp b/kmymoney/widgets/groupmarkers.cpp new file mode 100644 index 000000000..3e431ed69 --- /dev/null +++ b/kmymoney/widgets/groupmarkers.cpp @@ -0,0 +1,237 @@ +/*************************************************************************** + groupmarkers.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 "groupmarkers.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "groupmarker.h" +#include "groupmarker_p.h" + +#include "itemptrvector.h" +#include "mymoneyaccount.h" +#include "mymoneyenums.h" +#include "widgetenums.h" + +using namespace KMyMoneyRegister; +using namespace eWidgets; +using namespace eMyMoney; + +namespace KMyMoneyRegister +{ + class TypeGroupMarkerPrivate : public GroupMarkerPrivate + { + public: + eRegister::CashFlowDirection m_dir; + }; +} + +TypeGroupMarker::TypeGroupMarker(Register* parent, eRegister::CashFlowDirection dir, Account accType) : + GroupMarker(*new TypeGroupMarkerPrivate, parent, QString()) +{ + Q_D(TypeGroupMarker); + d->m_dir = dir; + switch (dir) { + case eRegister::CashFlowDirection::Deposit: + d->m_txt = i18nc("Deposits onto account", "Deposits"); + if (accType == Account::CreditCard) { + d->m_txt = i18nc("Payments towards credit card", "Payments"); + } + break; + case eRegister::CashFlowDirection::Payment: + d->m_txt = i18nc("Payments made from account", "Payments"); + if (accType == Account::CreditCard) { + d->m_txt = i18nc("Payments made with credit card", "Charges"); + } + break; + default: + qDebug("Unknown CashFlowDirection %d for TypeGroupMarker constructor", (int)dir); + break; + } +} + +TypeGroupMarker::~TypeGroupMarker() +{ +} + +eRegister::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()) +{ + Q_D(ReconcileGroupMarker); + d->m_state = state; + switch (state) { + case eMyMoney::Split::State::NotReconciled: + d->m_txt = i18nc("Reconcile state 'Not reconciled'", "Not reconciled"); + break; + case eMyMoney::Split::State::Cleared: + d->m_txt = i18nc("Reconcile state 'Cleared'", "Cleared"); + break; + case eMyMoney::Split::State::Reconciled: + d->m_txt = i18nc("Reconcile state 'Reconciled'", "Reconciled"); + break; + case eMyMoney::Split::State::Frozen: + d->m_txt = i18nc("Reconcile state 'Frozen'", "Frozen"); + break; + default: + d->m_txt = i18nc("Unknown reconcile state", "Unknown"); + break; + } +} + +ReconcileGroupMarker::~ReconcileGroupMarker() +{ +} + +eMyMoney::Split::State ReconcileGroupMarker::sortReconcileState() const +{ + Q_D(const ReconcileGroupMarker); + return d->m_state; +} + +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(eRegister::DetailColumn::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; + eTransaction::Column m_lastCol; + QList m_sortOrder; + QRect m_lastRepaintRect; + eRegister::DetailColumn m_detailsColumnType; + + }; +} diff --git a/kmymoney/widgets/groupmarkers.h b/kmymoney/widgets/groupmarkers.h new file mode 100644 index 000000000..de1f09790 --- /dev/null +++ b/kmymoney/widgets/groupmarkers.h @@ -0,0 +1,98 @@ +/*************************************************************************** + groupmarkers.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 GROUPMARKERS_H +#define GROUPMARKERS_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "groupmarker.h" + +namespace eWidgets { enum class SortField; + namespace Transaction { enum class Column; } + namespace Register { enum class DetailColumn;} } +namespace eMyMoney { enum class Account; } + +namespace KMyMoneyRegister +{ + class RegisterItem; + + class TypeGroupMarkerPrivate; + class TypeGroupMarker : public GroupMarker + { + Q_DISABLE_COPY(TypeGroupMarker) + + public: + explicit TypeGroupMarker(Register* getParent, eWidgets::eRegister::CashFlowDirection dir, eMyMoney::Account accType); + ~TypeGroupMarker() override; + + eWidgets::eRegister::CashFlowDirection sortType() const override; + + private: + Q_DECLARE_PRIVATE(TypeGroupMarker) + }; + + class PayeeGroupMarker : public GroupMarker + { + Q_DISABLE_COPY(PayeeGroupMarker) + + public: + explicit PayeeGroupMarker(Register* getParent, const QString& name); + ~PayeeGroupMarker() override; + + const QString& sortPayee() const override; + }; + + class CategoryGroupMarker : public GroupMarker + { + Q_DISABLE_COPY(CategoryGroupMarker) + + public: + explicit CategoryGroupMarker(Register* getParent, const QString& category); + ~CategoryGroupMarker() override; + + const QString& sortCategory() const override; + const QString sortSecurity() const override; + const char* className() override; + }; + + 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; + + private: + Q_DECLARE_PRIVATE(ReconcileGroupMarker) + }; + +} // namespace + +#endif diff --git a/kmymoney/widgets/investtransaction.cpp b/kmymoney/widgets/investtransaction.cpp new file mode 100644 index 000000000..250375d4c --- /dev/null +++ b/kmymoney/widgets/investtransaction.cpp @@ -0,0 +1,934 @@ +/*************************************************************************** + 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 + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneypayeecombo.h" +#include "transaction.h" + +#include "mymoneyutils.h" +#include "mymoneytransaction.h" +#include "mymoneysplit.h" +#include "mymoneyfile.h" +#include "register.h" +#include "transactionform.h" +#include "kmymoneylineedit.h" +#include "kmymoneyedit.h" +#include "kmymoneycombo.h" +#include "investtransactioneditor.h" +#include "kmymoneyutils.h" + +#include "kmymoneyglobalsettings.h" +#include "widgetenums.h" + +using namespace eWidgets; +using namespace KMyMoneyRegister; +using namespace KMyMoneyTransactionForm; + +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 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 (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + txt = i18n("Activity"); + break; + + case (int)eTransactionForm::Column::Value1: + align |= Qt::AlignLeft; + fieldEditable = true; + activity(txt, d->m_transactionType); + break; + + case (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + txt = i18n("Date"); + break; + + case (int)eTransactionForm::Column::Value2: + 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 (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + txt = i18n("Security"); + break; + + case (int)eTransactionForm::Column::Value1: + align |= Qt::AlignLeft; + fieldEditable = true; + if (d->m_account.isInvest()) + txt = d->m_security.name(); + break; + + case (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + if (haveShares()) { + txt = i18n("Shares"); + } else if (haveSplitRatio()) { + txt = i18n("Ratio"); + } + break; + + case (int)eTransactionForm::Column::Value2: + 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 (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + if (haveAssetAccount()) + txt = i18n("Account"); + break; + + case (int)eTransactionForm::Column::Value1: + align |= Qt::AlignLeft; + if ((fieldEditable = haveAssetAccount()) == true) { + txt = MyMoneyFile::instance()->accountToCategory(d->m_assetAccountSplit.accountId()); + } + break; + + case (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + if (havePrice()) + txt = i18n("Price/share"); + break; + + case (int)eTransactionForm::Column::Value2: + 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 (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + if (haveFees()) + txt = i18n("Fees"); + break; + + case (int)eTransactionForm::Column::Value1: + align |= Qt::AlignLeft; + if ((fieldEditable = haveFees()) == true) { + txt = d->m_feeCategory; + } + break; + + case (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + if (haveFees() && !d->m_feeCategory.isEmpty()) + txt = i18n("Fee Amount"); + break; + + case (int)eTransactionForm::Column::Value2: + 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 (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + if (haveInterest()) + txt = i18n("Interest"); + break; + + case (int)eTransactionForm::Column::Value1: + align |= Qt::AlignLeft; + if ((fieldEditable = haveInterest()) == true) { + txt = d->m_interestCategory; + } + break; + + case (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + if (haveInterest() && !d->m_interestCategory.isEmpty()) + txt = i18n("Interest"); + break; + + case (int)eTransactionForm::Column::Value2: + 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 (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + txt = i18n("Memo"); + break; + + case (int)eTransactionForm::Column::Value1: + 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 (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + if (haveAmount()) + txt = i18nc("Total balance", "Total"); + break; + + case (int)eTransactionForm::Column::Value2: + 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 (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + txt = i18n("Status"); + break; + + case (int)eTransactionForm::Column::Value2: + 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 (int)eTransaction::Column::Date: + align |= Qt::AlignLeft; + txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); + break; + + case (int)eTransaction::Column::Detail: + align |= Qt::AlignLeft; + activity(txt, d->m_transactionType); + break; + + case (int)eTransaction::Column::Security: + align |= Qt::AlignLeft; + if (d->m_account.isInvest()) + txt = d->m_security.name(); + break; + + case (int)eTransaction::Column::ReconcileFlag: + align |= Qt::AlignHCenter; + txt = reconcileState(false); + break; + + case (int)eTransaction::Column::Quantity: + 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 (int)eTransaction::Column::Price: + 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 (int)eTransaction::Column::Value: + 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 (int)eTransaction::Column::Balance: + 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 (int)eTransaction::Column::Detail: + 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 (int)eTransaction::Column::Quantity: + 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 (int)eTransaction::Column::Detail: + 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 (int)eTransaction::Column::Quantity: + 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 (int)eTransaction::Column::Detail: + 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 (int)eTransaction::Column::Quantity: + 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 (int)eTransaction::Column::Detail: + 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, (int)eTransactionForm::Column::Value1, editWidgets["activity"]); + arrangeWidget(d->m_form, 0, (int)eTransactionForm::Column::Value2, editWidgets["postdate"]); + arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Value1, editWidgets["security"]); + arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Value2, editWidgets["shares"]); + arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Value1, editWidgets["asset-account"]); + arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Value2, editWidgets["price"]); + arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Value1, editWidgets["fee-account"]->parentWidget()); + arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Value2, editWidgets["fee-amount"]); + arrangeWidget(d->m_form, 4, (int)eTransactionForm::Column::Value1, editWidgets["interest-account"]->parentWidget()); + arrangeWidget(d->m_form, 4, (int)eTransactionForm::Column::Value2, editWidgets["interest-amount"]); + arrangeWidget(d->m_form, 5, (int)eTransactionForm::Column::Value1, editWidgets["memo"]); + arrangeWidget(d->m_form, 5, (int)eTransactionForm::Column::Value2, editWidgets["total"]); + arrangeWidget(d->m_form, 6, (int)eTransactionForm::Column::Value2, editWidgets["status"]); + + // arrange dynamic labels + arrangeWidget(d->m_form, 0, (int)eTransactionForm::Column::Label1, editWidgets["activity-label"]); + arrangeWidget(d->m_form, 0, (int)eTransactionForm::Column::Label2, editWidgets["postdate-label"]); + arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Label1, editWidgets["security-label"]); + arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Label2, editWidgets["shares-label"]); + arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Label1, editWidgets["asset-label"]); + arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Label2, editWidgets["price-label"]); + arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Label1, editWidgets["fee-label"]); + arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Label2, editWidgets["fee-amount-label"]); + arrangeWidget(d->m_form, 4, (int)eTransactionForm::Column::Label1, editWidgets["interest-label"]); + arrangeWidget(d->m_form, 4, (int)eTransactionForm::Column::Label2, editWidgets["interest-amount-label"]); + arrangeWidget(d->m_form, 5, (int)eTransactionForm::Column::Label1, editWidgets["memo-label"]); + arrangeWidget(d->m_form, 5, (int)eTransactionForm::Column::Label2, editWidgets["total-label"]); + arrangeWidget(d->m_form, 6, (int)eTransactionForm::Column::Label2, 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, (int)eTransactionForm::Column::Value1))); + + // date + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(0, (int)eTransactionForm::Column::Value2))); + + // security + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, (int)eTransactionForm::Column::Value1))); + + // shares + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, (int)eTransactionForm::Column::Value2))); + + // account + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(2, (int)eTransactionForm::Column::Value1))); + + // price + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(2, (int)eTransactionForm::Column::Value2))); + + // 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, (int)eTransactionForm::Column::Value1); + tabOrderWidgets.append(focusWidget(w)); + w = w->findChild("splitButton"); + if (w) + tabOrderWidgets.append(w); + + // fee amount + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(3, (int)eTransactionForm::Column::Value2))); + + // the same applies for the interest categories + w = d->m_form->cellWidget(4, (int)eTransactionForm::Column::Value1); + tabOrderWidgets.append(focusWidget(w)); + w = w->findChild("splitButton"); + if (w) + tabOrderWidgets.append(w); + + // interest amount + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(4, (int)eTransactionForm::Column::Value2))); + + // memo + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(5, (int)eTransactionForm::Column::Value1))); + + // state + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(6, (int)eTransactionForm::Column::Value2))); +} + +void InvestTransaction::arrangeWidgetsInRegister(QMap& editWidgets) +{ + Q_D(InvestTransaction); + if (!d->m_parent) + return; + + setupRegisterPalette(editWidgets); + + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Date, editWidgets["postdate"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Security, editWidgets["security"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Detail, editWidgets["activity"]); + arrangeWidget(d->m_parent, d->m_startRow + 1, (int)eTransaction::Column::Detail, editWidgets["asset-account"]); + arrangeWidget(d->m_parent, d->m_startRow + 2, (int)eTransaction::Column::Detail, editWidgets["interest-account"]->parentWidget()); + arrangeWidget(d->m_parent, d->m_startRow + 3, (int)eTransaction::Column::Detail, editWidgets["fee-account"]->parentWidget()); + arrangeWidget(d->m_parent, d->m_startRow + 4, (int)eTransaction::Column::Detail, editWidgets["memo"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Quantity, editWidgets["shares"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Price, editWidgets["price"]); + arrangeWidget(d->m_parent, d->m_startRow + 2, (int)eTransaction::Column::Quantity, editWidgets["interest-amount"]); + arrangeWidget(d->m_parent, d->m_startRow + 3, (int)eTransaction::Column::Quantity, editWidgets["fee-amount"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Value, editWidgets["total"]); + arrangeWidget(d->m_parent, d->m_startRow + 1, (int)eTransaction::Column::Date, 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, (int)eTransaction::Column::Date))); + // security + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Security))); + // activity + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Detail))); + // shares + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Quantity))); + // price + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Price))); + // asset account + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 1, (int)eTransaction::Column::Detail))); + + // 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, (int)eTransaction::Column::Detail); // 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, (int)eTransaction::Column::Quantity))); + + w = d->m_parent->cellWidget(d->m_startRow + 3, (int)eTransaction::Column::Detail); // 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, (int)eTransaction::Column::Quantity))); + + // memo + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 4, (int)eTransaction::Column::Detail))); + + // status + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 1, (int)eTransaction::Column::Date))); +} + +eRegister::Action InvestTransaction::actionType() const +{ + return eRegister::Action::None; +} + +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 new file mode 100644 index 000000000..17cc2be5a --- /dev/null +++ b/kmymoney/widgets/investtransaction.h @@ -0,0 +1,103 @@ +/*************************************************************************** + investtransaction.h - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-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 INVESTTRANSACTION_H +#define INVESTTRANSACTION_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "transaction.h" + +namespace eMyMoney { namespace Split { enum class InvestmentTransactionType; } } + +namespace KMyMoneyRegister +{ + + class InvestTransactionPrivate; + class InvestTransaction : public Transaction + { + Q_DISABLE_COPY(InvestTransaction) + + public: + explicit InvestTransaction(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + ~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; + eWidgets::eRegister::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: + Q_DECLARE_PRIVATE(InvestTransaction) + }; +} // namespace + +#endif diff --git a/kmymoney/widgets/scheduledtransaction.cpp b/kmymoney/widgets/investtransaction_p.h similarity index 50% copy from kmymoney/widgets/scheduledtransaction.cpp copy to kmymoney/widgets/investtransaction_p.h index fe711c57e..ec51a46fd 100644 --- a/kmymoney/widgets/scheduledtransaction.cpp +++ b/kmymoney/widgets/investtransaction_p.h @@ -1,49 +1,60 @@ /*************************************************************************** - scheduledtransaction.cpp + investtransaction_p.h - description ------------------- - begin : Tue Aug 19 2008 - copyright : (C) 2008 by Thomas Baumgart - email : Thomas Baumgart + 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 "scheduledtransaction.h" +#ifndef INVESTTRANSACTION_P_H +#define INVESTTRANSACTION_P_H + +#include "transaction_p.h" // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneyglobalsettings.h" -#include "transaction.h" +#include "mymoneyenums.h" +#include "mymoneymoney.h" +#include "mymoneysecurity.h" +#include "mymoneysplit.h" using namespace KMyMoneyRegister; -using namespace KMyMoneyTransactionForm; - -StdTransactionScheduled::StdTransactionScheduled(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - StdTransaction(parent, transaction, split, uniqueId) -{ - // setup initial size - setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); -} -bool StdTransactionScheduled::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) +namespace KMyMoneyRegister { - bool rc = Transaction::paintRegisterCellSetup(painter, option, index); - option.palette.setCurrentColorGroup(QPalette::Disabled); - return rc; + 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/itemptrvector.cpp b/kmymoney/widgets/itemptrvector.cpp new file mode 100644 index 000000000..e32e61eb8 --- /dev/null +++ b/kmymoney/widgets/itemptrvector.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + itemptrvector.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 "itemptrvector.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "register.h" +#include "registeritem.h" +#include "mymoneymoney.h" +#include "widgetenums.h" + +using namespace eWidgets; +using namespace KMyMoneyRegister; + +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; + int rc = 0; + bool ok1, ok2; + qulonglong n1, n2; + + for (it = sortOrder.begin(); it != sortOrder.end(); ++it) { + SortField sortField = static_cast(*it); + switch (qAbs(static_cast(sortField))) { + case (int)SortField::PostDate: + rc = i2->sortPostDate().daysTo(i1->sortPostDate()); + break; + + case (int)SortField::EntryDate: + rc = i2->sortEntryDate().daysTo(i1->sortEntryDate()); + break; + + case (int)SortField::Payee: + rc = QString::localeAwareCompare(i1->sortPayee(), i2->sortPayee()); + break; + + case (int)SortField::Value: + if (i1->sortValue() == i2->sortValue()) + rc = 0; + else if (i1->sortValue() < i2->sortValue()) + rc = -1; + else + rc = 1; + break; + + case (int)SortField::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)SortField::EntryOrder: + rc = qstrcmp(i1->sortEntryOrder().toLatin1(), i2->sortEntryOrder().toLatin1()); + break; + + case (int)SortField::Type: + rc = (int)i1->sortType() - (int)i2->sortType(); + break; + + case (int)SortField::Category: + rc = QString::localeAwareCompare(i1->sortCategory(), i2->sortCategory()); + break; + + case (int)SortField::ReconcileState: + rc = static_cast(i1->sortReconcileState()) - static_cast(i2->sortReconcileState()); + break; + + case (int)SortField::Security: + rc = QString::localeAwareCompare(i1->sortSecurity(), i2->sortSecurity()); + break; + + default: + qDebug("Invalid sort key %d", (int)*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; + } + } + + if (rc == 0) { + rc = qstrcmp(i1->sortEntryOrder().toLatin1(), i2->sortEntryOrder().toLatin1()); + } + + return rc < 0; +} diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/itemptrvector.h similarity index 68% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/itemptrvector.h index d4fd1f27a..4ac156f0e 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/itemptrvector.h @@ -1,28 +1,49 @@ /*************************************************************************** - transactioneditorcontainer.cpp + itemptrvector.h ---------- - begin : Wed Jun 07 2006 + 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 "transactioneditorcontainer.h" +#ifndef ITEMPTRVECTOR_H +#define ITEMPTRVECTOR_H // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +namespace KMyMoneyRegister +{ + class RegisterItem; + class ItemPtrVector : public QVector + { + public: + void sort(); + + protected: + /** + * sorter's compare routine. Returns true if i1 < i2 + */ + static bool item_cmp(RegisterItem* i1, RegisterItem* i2); + }; +} // namespace + +#endif diff --git a/kmymoney/widgets/kaccounttemplateselector.cpp b/kmymoney/widgets/kaccounttemplateselector.cpp index deda48d86..5e1bccec0 100644 --- a/kmymoney/widgets/kaccounttemplateselector.cpp +++ b/kmymoney/widgets/kaccounttemplateselector.cpp @@ -1,277 +1,288 @@ /*************************************************************************** kaccounttemplateselector.cpp - description ------------------- begin : Tue Feb 5 2008 copyright : (C) 2008 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 "kaccounttemplateselector.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes + +#include "ui_kaccounttemplateselector.h" + #include -class KAccountTemplateSelector::Private +class KAccountTemplateSelectorPrivate { + Q_DISABLE_COPY(KAccountTemplateSelectorPrivate) + public: - Private(KAccountTemplateSelector* p) : - id(0) + KAccountTemplateSelectorPrivate() : + ui(new Ui::KAccountTemplateSelector), + 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* KAccountTemplateSelector::Private::hierarchyItem(const QString& parent, const QString& name) +QTreeWidgetItem* 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 KAccountTemplateSelector::Private::loadHierarchy() +void loadHierarchy() { m_templateHierarchy.clear(); - QTreeWidgetItemIterator it(m_parent->m_groupList, QTreeWidgetItemIterator::Selected); + QTreeWidgetItemIterator it(ui->m_groupList, QTreeWidgetItemIterator::Selected); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { - m_templates[it_v->data(0, IdRole).toInt()].hierarchy(m_templateHierarchy); + m_templates[it_v->data(0, Qt::UserRole).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; - MyMoneyFile* file = MyMoneyFile::instance(); + auto 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 - m_parent->m_accountList->clear(); + ui->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(m_parent->m_accountList); + (*it_m) = new QTreeWidgetItem(ui->m_accountList); (*it_m)->setText(0, it_m.key()); } else { (*it_m) = hierarchyItem(exp.cap(1), exp.cap(2)); } (*it_m)->setExpanded(true); } - 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()); + 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()); } } -QList KAccountTemplateSelector::Private::selectedTemplates() const +QList selectedTemplates() const { QList list; - QTreeWidgetItemIterator it(m_parent->m_groupList, QTreeWidgetItemIterator::Selected); + QTreeWidgetItemIterator it(ui->m_groupList, QTreeWidgetItemIterator::Selected); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { - list << m_templates[it_v->data(0, IdRole).toInt()]; + list << m_templates[it_v->data(0, Qt::UserRole).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) : - KAccountTemplateSelectorDecl(parent), - d(new Private(this)) + QWidget(parent), + d_ptr(new KAccountTemplateSelectorPrivate) { - m_accountList->header()->hide(); - m_groupList->setSelectionMode(QAbstractItemView::ExtendedSelection); - connect(m_groupList, SIGNAL(itemSelectionChanged()), this, SLOT(slotLoadHierarchy())); + 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); // 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 - m_groupList->clear(); + d->ui->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 - QTreeWidgetItem *parent = new QTreeWidgetItem(m_groupList); + Q_D(KAccountTemplateSelector); + QTreeWidgetItem *parent = new QTreeWidgetItem(d->ui->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, IdRole, QString("%1").arg(d->id)); + item->setData(0, Qt::UserRole, QString("%1").arg(d->id)); ++d->id; } } } // make visible the templates of the current locale if (d->it_m.value() == d->currentLocaleId) { - m_groupList->setCurrentItem(parent); - m_groupList->expandItem(parent); - m_groupList->scrollToItem(parent, QTreeView::PositionAtTop); + d->ui->m_groupList->setCurrentItem(parent); + d->ui->m_groupList->expandItem(parent); + d->ui->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 a5f5b7e5d..09b5d592a 100644 --- a/kmymoney/widgets/kaccounttemplateselector.h +++ b/kmymoney/widgets/kaccounttemplateselector.h @@ -1,73 +1,61 @@ /*************************************************************************** kaccounttemplateselector.h - description ------------------- begin : Tue Feb 5 2008 copyright : (C) 2008 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 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 KAccountTemplateSelector : public KAccountTemplateSelectorDecl +class KAccountTemplateSelectorPrivate; +class KAccountTemplateSelector : public QWidget { Q_OBJECT + Q_DISABLE_COPY(KAccountTemplateSelector) public: - enum KAccountTemplateSelectorItemRoles { - IdRole = Qt::UserRole, /**< The id is stored in this role in column 0 as a string.*/ - }; - - KAccountTemplateSelector(QWidget* parent = 0); + explicit KAccountTemplateSelector(QWidget* parent = nullptr); ~KAccountTemplateSelector(); QList selectedTemplates() const; private slots: void slotLoadHierarchy(); void slotLoadCountry(); void slotLoadTemplateList(); private: - /// \internal d-pointer class. - class Private; - /// \internal d-pointer instance. - Private* const d; + KAccountTemplateSelectorPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KAccountTemplateSelector) }; #endif diff --git a/kmymoney/widgets/kaccounttemplateselectordecl.ui b/kmymoney/widgets/kaccounttemplateselector.ui similarity index 95% rename from kmymoney/widgets/kaccounttemplateselectordecl.ui rename to kmymoney/widgets/kaccounttemplateselector.ui index 62282a5b9..11c02788b 100644 --- a/kmymoney/widgets/kaccounttemplateselectordecl.ui +++ b/kmymoney/widgets/kaccounttemplateselector.ui @@ -1,84 +1,84 @@ - KAccountTemplateSelectorDecl - + KAccountTemplateSelector + 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 baa30328d..e80074ef4 100644 --- a/kmymoney/widgets/kbudgetvalues.cpp +++ b/kmymoney/widgets/kbudgetvalues.cpp @@ -1,345 +1,380 @@ /*************************************************************************** kbudgetvalues - description ------------------- begin : Wed Nov 28 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 "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) : - KBudgetValuesDecl(parent), - m_currentTab(m_monthlyButton) + QWidget(parent), + d_ptr(new KBudgetValuesPrivate) { - 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; + 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; // fill with standard labels - 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(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); + 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)); + + connect(d->ui->m_amountMonthly, &KMyMoneyEdit::valueChanged, this, &KBudgetValues::slotNeedUpdate); + connect(d->ui->m_amountYearly, &KMyMoneyEdit::valueChanged, this, &KBudgetValues::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], &KMyMoneyEdit::valueChanged, this, &KBudgetValues::slotNeedUpdate); + d->m_field[i]->installEventFilter(this); } - 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())); + connect(d->ui->m_clearButton, &QAbstractButton::clicked, this, &KBudgetValues::slotClearAllValues); + connect(d->ui->m_periodGroup, static_cast(&QButtonGroup::buttonClicked), this, &KBudgetValues::slotChangePeriod); + connect(this, &KBudgetValues::valuesChanged, this, &KBudgetValues::slotUpdateClearButton); KGuiItem clearItem(KStandardGuiItem::clear()); - KGuiItem::assign(m_clearButton, clearItem); - m_clearButton->setText(""); - m_clearButton->setToolTip(clearItem.toolTip()); + KGuiItem::assign(d->ui->m_clearButton, clearItem); + d->ui->m_clearButton->setText(QString()); + d->ui->m_clearButton->setToolTip(clearItem.toolTip()); } - KBudgetValues::~KBudgetValues() { + Q_D(KBudgetValues); + delete d; } bool KBudgetValues::eventFilter(QObject* o, QEvent* e) { - bool rc = false; + auto 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 (int i = 0; i < 12; ++i) - m_field[i]->setValue(MyMoneyMoney()); - m_amountMonthly->setValue(MyMoneyMoney()); - m_amountYearly->setValue(MyMoneyMoney()); + 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()); blockSignals(false); } void KBudgetValues::slotClearAllValues() { - 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()); + 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()); } 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 = m_periodGroup->button(id); - fillMonthLabels(); + QWidget *tab = d->ui->m_periodGroup->button(id); + d->fillMonthLabels(); MyMoneyMoney newValue; - 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(); + 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(); 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("", 2)) + QString(""), i18nc("Auto assignment (caption)", "Auto assignment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { - 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(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); } } } - } 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(); + } 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(); } 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("", 2)) + QString(""), i18nc("Auto assignment (caption)", "Auto assignment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { - 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(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); } } } - } 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(); + } 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(); if (newValue.isZero()) { - if (m_currentTab == m_monthlyButton) { - newValue = m_amountMonthly->value(); - } else if (m_currentTab == m_yearlyButton) { - newValue = (m_amountYearly->value() / MyMoneyMoney(12, 1)).convert(); + 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 (!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("", 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); + 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); } } } } slotNeedUpdate(); - m_currentTab = tab; + d->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; - m_budgetDate = budget.budgetStart(); + d->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: - m_monthlyButton->setChecked(true); - slotChangePeriod(m_periodGroup->id(m_monthlyButton)); - m_amountMonthly->setValue(budgetAccount.period(m_budgetDate).amount()); + 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()); break; case MyMoneyBudget::AccountGroup::eYearly: - m_yearlyButton->setChecked(true); - slotChangePeriod(m_periodGroup->id(m_yearlyButton)); - m_amountYearly->setValue(budgetAccount.period(m_budgetDate).amount()); + 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()); break; case MyMoneyBudget::AccountGroup::eMonthByMonth: - 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()); + 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()); date = date.addMonths(1); } break; } slotUpdateClearButton(); blockSignals(false); } void KBudgetValues::budgetValues(const MyMoneyBudget& budget, MyMoneyBudget::AccountGroup& budgetAccount) { + Q_D(KBudgetValues); MyMoneyBudget::PeriodGroup period; - m_budgetDate = budget.budgetStart(); - period.setStartDate(m_budgetDate); + d->m_budgetDate = budget.budgetStart(); + period.setStartDate(d->m_budgetDate); QDate date; budgetAccount.clearPeriods(); - int tab = m_periodGroup->checkedId(); - if (tab == m_periodGroup->id(m_monthlyButton)) { + int tab = d->ui->m_periodGroup->checkedId(); + if (tab == d->ui->m_periodGroup->id(d->ui->m_monthlyButton)) { budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eMonthly); - period.setAmount(m_amountMonthly->value()); - budgetAccount.addPeriod(m_budgetDate, period); - } else if (tab == m_periodGroup->id(m_yearlyButton)) { + 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)) { budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eYearly); - period.setAmount(m_amountYearly->value()); - budgetAccount.addPeriod(m_budgetDate, period); - } else if (tab == m_periodGroup->id(m_individualButton)) { + 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)) { budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eMonthByMonth); - date.setDate(m_budgetDate.year(), m_budgetDate.month(), m_budgetDate.day()); - for (int i = 0; i < 12; ++i) { + date.setDate(d->m_budgetDate.year(), d->m_budgetDate.month(), d->m_budgetDate.day()); + for (auto i = 0; i < 12; ++i) { period.setStartDate(date); - period.setAmount(m_field[i]->value()); + period.setAmount(d->m_field[i]->value()); budgetAccount.addPeriod(date, period); date = date.addMonths(1); } } } void KBudgetValues::slotUpdateClearButton() { - 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(); + 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(); } } - m_clearButton->setEnabled(rc); + d->ui->m_clearButton->setEnabled(rc); } diff --git a/kmymoney/widgets/kbudgetvalues.h b/kmymoney/widgets/kbudgetvalues.h index 3f8c039f0..c9148df25 100644 --- a/kmymoney/widgets/kbudgetvalues.h +++ b/kmymoney/widgets/kbudgetvalues.h @@ -1,93 +1,80 @@ /*************************************************************************** kbudgetvalues - description ------------------- begin : Wed Nov 28 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. * * * ***************************************************************************/ #ifndef KBUDGETVALUES_H #define KBUDGETVALUES_H // ---------------------------------------------------------------------------- // QT Includes -#include -class QLabel; +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "ui_kbudgetvaluesdecl.h" -#include -class kMyMoneyEdit; +#include "mymoneybudget.h" /** * @author Thomas Baumgart */ -class KBudgetValuesDecl : public QWidget, public Ui::KBudgetValuesDecl -{ -public: - KBudgetValuesDecl(QWidget *parent) : QWidget(parent) { - setupUi(this); - } -}; - -class KBudgetValues : public KBudgetValuesDecl +class KBudgetValuesPrivate; +class KBudgetValues : public QWidget { Q_OBJECT + Q_DISABLE_COPY(KBudgetValues) + public: - KBudgetValues(QWidget* parent = 0); + explicit KBudgetValues(QWidget* parent = nullptr); ~KBudgetValues(); void setBudgetValues(const MyMoneyBudget& budget, const MyMoneyBudget::AccountGroup& budgetAccount); void budgetValues(const MyMoneyBudget& budget, MyMoneyBudget::AccountGroup& budgetAccount); void clear(); -private: - void enableMonths(bool enabled); - void fillMonthLabels(); +signals: + void valuesChanged(); 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); + bool eventFilter(QObject* o, QEvent* e) override; private: - kMyMoneyEdit* m_field[12]; - QLabel* m_label[12]; - QWidget* m_currentTab; - QDate m_budgetDate; - -signals: - void valuesChanged(); + KBudgetValuesPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KBudgetValues) }; #endif diff --git a/kmymoney/widgets/kbudgetvaluesdecl.ui b/kmymoney/widgets/kbudgetvalues.ui similarity index 90% rename from kmymoney/widgets/kbudgetvaluesdecl.ui rename to kmymoney/widgets/kbudgetvalues.ui index b954fdd5f..7a2db0e0e 100644 --- a/kmymoney/widgets/kbudgetvaluesdecl.ui +++ b/kmymoney/widgets/kbudgetvalues.ui @@ -1,383 +1,383 @@ - KBudgetValuesDecl - + KBudgetValues + 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 + 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 49d449c09..d15ae4ad8 100644 --- a/kmymoney/widgets/kguiutils.cpp +++ b/kmymoney/widgets/kguiutils.cpp @@ -1,254 +1,297 @@ /*************************************************************************** kguiutils.cpp - description ------------------- begin : Fri Jan 27 2006 copyright : (C) 2006 Tony Bloomfield email : Tony Bloomfield + (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 "kguiutils.h" // ---------------------------------------------------------------------------- // QT Includes // No need for QDateEdit, QSpinBox, etc., since these always return values #include #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) +class KMandatoryFieldGroupPrivate +{ + Q_DISABLE_COPY(KMandatoryFieldGroupPrivate) + +public: + KMandatoryFieldGroupPrivate() : + m_okButton(0), + m_enabled(true) + { + } + + QList m_widgets; + QPushButton* m_okButton; + bool m_enabled; +}; + +KMandatoryFieldGroup::KMandatoryFieldGroup(QObject *parent) : + QObject(parent), + d_ptr(new KMandatoryFieldGroupPrivate) { - if (!m_widgets.contains(widget)) { +} + +KMandatoryFieldGroup::~KMandatoryFieldGroup() +{ + Q_D(KMandatoryFieldGroup); + delete d; +} + +void KMandatoryFieldGroup::add(QWidget *widget) +{ + Q_D(KMandatoryFieldGroup); + if (!d->m_widgets.contains(widget)) { if (qobject_cast(widget)) connect(qobject_cast(widget), - SIGNAL(clicked()), - this, SLOT(changed())); + &QCheckBox::clicked, + this, &KMandatoryFieldGroup::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())); + connect(lineedit, &QLineEdit::textChanged, this, &KMandatoryFieldGroup::changed); } else { - connect(combo, SIGNAL(highlighted(int)), this, SLOT(changed())); + connect(combo, static_cast(&QComboBox::highlighted), this, &KMandatoryFieldGroup::changed); } } - else if (qobject_cast(widget)) { - kMyMoneyEdit* amount = qobject_cast(widget); + 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())); + connect(lineedit, &QLineEdit::textChanged, this, &KMandatoryFieldGroup::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())); + &QLineEdit::textChanged, + this, &KMandatoryFieldGroup::changed); } else if (qobject_cast(widget)) connect(qobject_cast(widget), - SIGNAL(valueChanged(QString)), - this, SLOT(changed())); + static_cast(&QSpinBox::valueChanged), + this, &KMandatoryFieldGroup::changed); else if (qobject_cast(widget)) connect(qobject_cast(widget), - SIGNAL(itemSelectionChanged()), - this, SLOT(changed())); + &QListWidget::itemSelectionChanged, + this, &KMandatoryFieldGroup::changed); else if (qobject_cast(widget)) connect(qobject_cast(widget), - SIGNAL(textChanged(QString)), - this, SLOT(changed())); + &KUrlRequester::textChanged, + this, &KMandatoryFieldGroup::changed); else if (qobject_cast(widget)) connect(qobject_cast(widget), - SIGNAL(textChanged()), - this, SLOT(changed())); + &KMyMoneyTextEdit::textChanged, + this, &KMandatoryFieldGroup::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); + d->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); + d->m_widgets.append(widget); changed(); } } -void kMandatoryFieldGroup::removeAll() +void KMandatoryFieldGroup::removeAll() { - while(!m_widgets.isEmpty()) { - remove(m_widgets.at(0)); + Q_D(KMandatoryFieldGroup); + while(!d->m_widgets.isEmpty()) { + remove(d->m_widgets.at(0)); } } -void kMandatoryFieldGroup::remove(QWidget *widget) +void KMandatoryFieldGroup::remove(QWidget *widget) { + Q_D(KMandatoryFieldGroup); widget->setPalette(QApplication::palette()); - m_widgets.removeOne(widget); + d->m_widgets.removeOne(widget); changed(); } -void kMandatoryFieldGroup::setOkButton(QPushButton *button) +void KMandatoryFieldGroup::setOkButton(QPushButton *button) { - if (m_okButton && m_okButton != button) - m_okButton->setEnabled(true); - m_okButton = button; + Q_D(KMandatoryFieldGroup); + if (d->m_okButton && d->m_okButton != button) + d->m_okButton->setEnabled(true); + d->m_okButton = button; changed(); } -void kMandatoryFieldGroup::changed() +void KMandatoryFieldGroup::changed() { + Q_D(KMandatoryFieldGroup); bool enable = true; QList::ConstIterator i; - for (i = m_widgets.constBegin(); i != m_widgets.constEnd(); ++i) { + for (i = d->m_widgets.constBegin(); i != d->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") { + 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; + if (d->m_okButton) + d->m_okButton->setEnabled(enable); + d->m_enabled = enable; emit stateChanged(); emit stateChanged(enable); } -void kMandatoryFieldGroup::clear() +bool KMandatoryFieldGroup::isEnabled() const +{ + Q_D(const KMandatoryFieldGroup); + return d->m_enabled; +} + +void KMandatoryFieldGroup::clear() { + Q_D(KMandatoryFieldGroup); QList::Iterator i; - for (i = m_widgets.begin(); i != m_widgets.end(); ++i) + for (i = d->m_widgets.begin(); i != d->m_widgets.end(); ++i) (*i)->setPalette(QApplication::palette()); - m_widgets.clear(); - if (m_okButton) { - m_okButton->setEnabled(true); - m_okButton = 0; - m_enabled = true; + d->m_widgets.clear(); + if (d->m_okButton) { + d->m_okButton->setEnabled(true); + d->m_okButton = 0; + d->m_enabled = true; } } diff --git a/kmymoney/widgets/kguiutils.h b/kmymoney/widgets/kguiutils.h index a881d0cba..c1e2ba782 100644 --- a/kmymoney/widgets/kguiutils.h +++ b/kmymoney/widgets/kguiutils.h @@ -1,101 +1,101 @@ /*************************************************************************** kguiutils.h - description ------------------- begin : Fri Jan 27 2006 copyright : (C) 2006 Tony Bloomfield email : Tony Bloomfield + (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 KGUIUTILS_H #define KGUIUTILS_H // ---------------------------------------------------------------------------- // QT Includes #include -#include -class QWidget; -class QPushButton; // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmm_widgets_export.h" +class QWidget; +class QPushButton; + /** * @author Tony Bloomfield */ -class KMM_WIDGETS_EXPORT kMandatoryFieldGroup : public QObject +class KMandatoryFieldGroupPrivate; +class KMM_WIDGETS_EXPORT KMandatoryFieldGroup : public QObject { Q_OBJECT + Q_DISABLE_COPY(KMandatoryFieldGroup) public: - kMandatoryFieldGroup(QObject *parent) : - QObject(parent), m_okButton(0), m_enabled(true) {} + explicit KMandatoryFieldGroup(QObject *parent); + ~KMandatoryFieldGroup(); /** * This method adds a widget to the list of mandatory fields for the current dialog * * @param widget pointer to the widget to be added */ void add(QWidget *widget); /** * This method removes a widget form the list of mandatory fields for the current dialog * * @param widget pointer to the widget to be removed */ void remove(QWidget *widget); /** * This method removes all widgets from the list of mandatory fields for the current dialog */ void removeAll(); /** * This method designates the button to be enabled when all mandatory fields * have been completed * * @param button pointer to the 'ok' button */ void setOkButton(QPushButton *button); /** * This method returns if all requirements for the mandatory group * have been fulfilled (@p true) or not (@p false). */ - bool isEnabled() const { - return m_enabled; - } + bool isEnabled() const; public slots: void clear(); /** * Force update of ok button */ void changed(); signals: void stateChanged(); void stateChanged(bool state); private: - QList m_widgets; - QPushButton* m_okButton; - bool m_enabled; + KMandatoryFieldGroupPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMandatoryFieldGroup) }; #endif // KGUIUTILS_H diff --git a/kmymoney/widgets/kmymoneyaccountcombo.cpp b/kmymoney/widgets/kmymoneyaccountcombo.cpp index 666da24d2..708c6493a 100644 --- a/kmymoney/widgets/kmymoneyaccountcombo.cpp +++ b/kmymoney/widgets/kmymoneyaccountcombo.cpp @@ -1,357 +1,356 @@ /*************************************************************************** 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 + (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 "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 (int i = 0; i < m_q->model()->rowCount(QModelIndex()); ++i) { + for (auto 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))); + connect(lineEdit(), &QLineEdit::textEdited, this, &KMyMoneyAccountCombo::makeCompletion); } } 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))); + connect(d->m_popupView, &QAbstractItemView::activated, this, &KMyMoneyAccountCombo::selectItem); if(isEditable()) { - connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(makeCompletion(QString))); + connect(lineEdit(), &QLineEdit::textEdited, this, &KMyMoneyAccountCombo::makeCompletion); } else { - connect(this, SIGNAL(activated(int)), SLOT(activated())); + connect(this, static_cast(&KMyMoneyAccountCombo::KComboBox::activated), this, &KMyMoneyAccountCombo::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 a6e2a9cb2..fa358a965 100644 --- a/kmymoney/widgets/kmymoneyaccountcombo.h +++ b/kmymoney/widgets/kmymoneyaccountcombo.h @@ -1,165 +1,168 @@ /*************************************************************************** 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 + (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 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 + Q_DISABLE_COPY(KMyMoneyAccountCombo) + public: - explicit KMyMoneyAccountCombo(QSortFilterProxyModel *model, QWidget *parent = 0); - explicit KMyMoneyAccountCombo(QWidget *parent = 0); + explicit KMyMoneyAccountCombo(QSortFilterProxyModel *model, QWidget* parent = nullptr); + explicit KMyMoneyAccountCombo(QWidget* parent = nullptr); ~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); + bool eventFilter(QObject* o, QEvent* e) override; public slots: void expandAll(); void collapseAll(); - virtual void showPopup(); - virtual void hidePopup(); + void showPopup() override; + void hidePopup() override; protected: - virtual void wheelEvent(QWheelEvent *ev); + void wheelEvent(QWheelEvent *ev) override; protected slots: void activated(); - void makeCompletion(const QString& txt); + void makeCompletion(const QString& txt) override; 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.cpp b/kmymoney/widgets/kmymoneyaccountcompletion.cpp index 079213ade..0e1ef4ea8 100644 --- a/kmymoney/widgets/kmymoneyaccountcompletion.cpp +++ b/kmymoney/widgets/kmymoneyaccountcompletion.cpp @@ -1,106 +1,126 @@ /*************************************************************************** kmymoneyaccountcompletion.cpp - 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 + (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 "kmymoneyaccountcompletion.h" +#include "kmymoneycompletion_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" +#include "kmymoneyaccountselector.h" -kMyMoneyAccountCompletion::kMyMoneyAccountCompletion(QWidget *parent) : - kMyMoneyCompletion(parent) +KMyMoneyAccountCompletion::KMyMoneyAccountCompletion(QWidget *parent) : + KMyMoneyCompletion(parent) { - delete m_selector; - m_selector = new kMyMoneyAccountSelector(this, 0, false); - m_selector->listView()->setFocusProxy(parent); - layout()->addWidget(m_selector); + Q_D(KMyMoneyCompletion); + delete d->m_selector; + d->m_selector = new KMyMoneyAccountSelector(this, 0, false); + d->m_selector->listView()->setFocusProxy(parent); + layout()->addWidget(d->m_selector); #ifndef KMM_DESIGNER // Default is to show all accounts // FIXME We should leave this also to the caller AccountSet set; set.addAccountGroup(eMyMoney::Account::Asset); set.addAccountGroup(eMyMoney::Account::Liability); set.addAccountGroup(eMyMoney::Account::Income); set.addAccountGroup(eMyMoney::Account::Expense); set.load(selector()); #endif - connectSignals(m_selector, m_selector->listView()); + connectSignals(d->m_selector, d->m_selector->listView()); +} + +KMyMoneyAccountCompletion::~KMyMoneyAccountCompletion() +{ +} + +QStringList KMyMoneyAccountCompletion::accountList(const QList& list = QList()) const +{ + return selector()->accountList(list); } -kMyMoneyAccountCompletion::~kMyMoneyAccountCompletion() +QStringList KMyMoneyAccountCompletion::accountList() const { + return accountList(QList()); } -void kMyMoneyAccountCompletion::slotMakeCompletion(const QString& txt) +KMyMoneyAccountSelector* KMyMoneyAccountCompletion::selector() const +{ + Q_D(const KMyMoneyCompletion); +// return nullptr; + return dynamic_cast(d->m_selector); + } + +void KMyMoneyAccountCompletion::slotMakeCompletion(const QString& txt) { + Q_D(KMyMoneyCompletion); // if(txt.isEmpty() || txt.length() == 0) // return; - int cnt = 0; + auto cnt = 0; if (txt.contains(MyMoneyFile::AccountSeperator) == 0) { - m_lastCompletion = QRegExp(QRegExp::escape(txt), Qt::CaseInsensitive); + d->m_lastCompletion = QRegExp(QRegExp::escape(txt), Qt::CaseInsensitive); cnt = selector()->slotMakeCompletion(txt); } 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()) + ".*"; } pattern += '$'; - m_lastCompletion = QRegExp(pattern, Qt::CaseInsensitive); - cnt = selector()->slotMakeCompletion(m_lastCompletion); + d->m_lastCompletion = QRegExp(pattern, Qt::CaseInsensitive); + cnt = selector()->slotMakeCompletion(d->m_lastCompletion); // if we don't have a match, we try it again, but this time // we add a wildcard for the top level if (cnt == 0) { pattern = pattern.insert(1, QString(".*") + MyMoneyFile::AccountSeperator); - m_lastCompletion = QRegExp(pattern, Qt::CaseInsensitive); - cnt = selector()->slotMakeCompletion(m_lastCompletion); + d->m_lastCompletion = QRegExp(pattern, Qt::CaseInsensitive); + cnt = selector()->slotMakeCompletion(d->m_lastCompletion); } } - if (m_parent && m_parent->isVisible() && !isVisible() && cnt) + if (d->m_parent && d->m_parent->isVisible() && !isVisible() && cnt) show(false); else { if (cnt != 0) { adjustSize(); } else { hide(); } } } diff --git a/kmymoney/widgets/kmymoneyaccountcompletion.h b/kmymoney/widgets/kmymoneyaccountcompletion.h index 0111affc8..dd0ed87fb 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" +namespace eMyMoney { enum class Account; } + +class KMyMoneyAccountSelector; + /** * @author Thomas Baumgart */ -class kMyMoneyAccountCompletion : public kMyMoneyCompletion +class KMyMoneyAccountCompletion : public KMyMoneyCompletion { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyAccountCompletion) + public: - kMyMoneyAccountCompletion(QWidget *parent = 0); - virtual ~kMyMoneyAccountCompletion(); + explicit KMyMoneyAccountCompletion(QWidget* parent = nullptr); + ~KMyMoneyAccountCompletion() override; - QStringList accountList(const QList& list = QList()) const { - return selector()->accountList(list); - } + QStringList accountList(const QList& list) const; + QStringList accountList() const; /** - * reimplemented from kMyMoneyCompletion + * reimplemented from KMyMoneyCompletion */ - kMyMoneyAccountSelector* selector() const { - return dynamic_cast(m_selector); - } + KMyMoneyAccountSelector* selector() const; public slots: void slotMakeCompletion(const QString& txt); }; #endif diff --git a/kmymoney/widgets/kmymoneyaccountselector.cpp b/kmymoney/widgets/kmymoneyaccountselector.cpp index bbb792146..55f9bb566 100644 --- a/kmymoney/widgets/kmymoneyaccountselector.cpp +++ b/kmymoney/widgets/kmymoneyaccountselector.cpp @@ -1,487 +1,585 @@ /*************************************************************************** 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 + (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 "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" +#include "widgetenums.h" using namespace Icons; using namespace eMyMoney; -kMyMoneyAccountSelector::kMyMoneyAccountSelector(QWidget *parent, Qt::WindowFlags flags, const bool createButtons) : - KMyMoneySelector(parent, flags), +class KMyMoneyAccountSelectorPrivate : public KMyMoneySelectorPrivate +{ + Q_DISABLE_COPY(KMyMoneyAccountSelectorPrivate) + +public: + KMyMoneyAccountSelectorPrivate(KMyMoneyAccountSelector *qq) : + KMyMoneySelectorPrivate(qq), 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); - m_allAccountsButton = new QPushButton(this); - m_allAccountsButton->setObjectName("m_allAccountsButton"); - m_allAccountsButton->setText(i18nc("Select all accounts", "All")); - buttonLayout->addWidget(m_allAccountsButton); + 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_incomeCategoriesButton = new QPushButton(this); - m_incomeCategoriesButton->setObjectName("m_incomeCategoriesButton"); - m_incomeCategoriesButton->setText(i18n("Income")); - buttonLayout->addWidget(m_incomeCategoriesButton); + d->m_incomeCategoriesButton = new QPushButton(this); + d->m_incomeCategoriesButton->setObjectName("m_incomeCategoriesButton"); + d->m_incomeCategoriesButton->setText(i18n("Income")); + buttonLayout->addWidget(d->m_incomeCategoriesButton); - m_expenseCategoriesButton = new QPushButton(this); - m_expenseCategoriesButton->setObjectName("m_expenseCategoriesButton"); - m_expenseCategoriesButton->setText(i18n("Expense")); - buttonLayout->addWidget(m_expenseCategoriesButton); + d->m_expenseCategoriesButton = new QPushButton(this); + d->m_expenseCategoriesButton->setObjectName("m_expenseCategoriesButton"); + d->m_expenseCategoriesButton->setText(i18n("Expense")); + buttonLayout->addWidget(d->m_expenseCategoriesButton); - m_noAccountButton = new QPushButton(this); - m_noAccountButton->setObjectName("m_noAccountButton"); - m_noAccountButton->setText(i18nc("No account", "None")); - buttonLayout->addWidget(m_noAccountButton); + 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); QSpacerItem* spacer = new QSpacerItem(0, 67, QSizePolicy::Minimum, QSizePolicy::Expanding); buttonLayout->addItem(spacer); - m_layout->addLayout(buttonLayout); + d->m_layout->addLayout(buttonLayout); - 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())); + 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); } } -kMyMoneyAccountSelector::~kMyMoneyAccountSelector() +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::removeButtons() +void KMyMoneyAccountSelector::slotDeselectAllAccounts() { - delete m_allAccountsButton; - delete m_incomeCategoriesButton; - delete m_expenseCategoriesButton; - delete m_noAccountButton; + selectAllItems(false); } -void kMyMoneyAccountSelector::selectCategories(const bool income, const bool expense) +void KMyMoneyAccountSelector::selectCategories(const bool income, const bool expense) { - QTreeWidgetItemIterator it_v(m_treeWidget); + Q_D(KMyMoneyAccountSelector); + QTreeWidgetItemIterator it_v(d->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::setSelectionMode(QTreeWidget::SelectionMode mode) +void KMyMoneyAccountSelector::slotSelectIncomeCategories() +{ + selectCategories(true, false); +} + +void KMyMoneyAccountSelector::slotSelectExpenseCategories() { - m_incomeCategoriesButton->setHidden(mode == QTreeWidget::MultiSelection); - m_expenseCategoriesButton->setHidden(mode == QTreeWidget::MultiSelection); + 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); KMyMoneySelector::setSelectionMode(mode); } -QStringList kMyMoneyAccountSelector::accountList(const QList& filterList) const +QStringList KMyMoneyAccountSelector::accountList(const QList& filterList) const { + Q_D(const KMyMoneyAccountSelector); QStringList list; - QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); while (*it) { - QVariant id = (*it)->data(0, KMyMoneySelector::IdRole); + QVariant id = (*it)->data(0, (int)eWidgets::Selector::Role::Id); MyMoneyAccount acc = MyMoneyFile::instance()->account(id.toString()); if (filterList.count() == 0 || filterList.contains(acc.accountType())) list << id.toString(); it++; } return list; } -bool kMyMoneyAccountSelector::match(const QRegExp& exp, QTreeWidgetItem* item) const +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; + return exp.indexIn(item->data(0, (int)eWidgets::Selector::Role::Key).toString().mid(1)) != -1; } -bool kMyMoneyAccountSelector::contains(const QString& txt) const +bool KMyMoneyAccountSelector::contains(const QString& txt) const { - QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); + Q_D(const KMyMoneyAccountSelector); + QTreeWidgetItemIterator it(d->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) { + if (exp.indexIn(it_v->data(0, (int)eWidgets::Selector::Role::Key).toString().mid(1)) != -1) { return true; } it++; } return false; } -AccountSet::AccountSet() : +class AccountSetPrivate +{ + Q_DISABLE_COPY(AccountSetPrivate) + +public: + AccountSetPrivate() : 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) { - 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; + 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; } else if (group == Account::Liability) { - m_typeList << Account::CreditCard; - m_typeList << Account::Loan; - m_typeList << Account::Liability; + d->m_typeList << Account::CreditCard; + d->m_typeList << Account::Loan; + d->m_typeList << Account::Liability; } else if (group == Account::Income) { - m_typeList << Account::Income; + d->m_typeList << Account::Income; } else if (group == Account::Expense) { - m_typeList << Account::Expense; + d->m_typeList << Account::Expense; } else if (group == Account::Equity) { - m_typeList << Account::Equity; + d->m_typeList << Account::Equity; } } void AccountSet::addAccountType(Account type) { - m_typeList << type; + Q_D(AccountSet); + d->m_typeList << type; } void AccountSet::removeAccountType(Account type) { - int index = m_typeList.indexOf(type); + Q_D(AccountSet); + int index = d->m_typeList.indexOf(type); if (index != -1) { - m_typeList.removeAt(index); + d->m_typeList.removeAt(index); } } void AccountSet::clear() { - m_typeList.clear(); + Q_D(AccountSet); + d->m_typeList.clear(); } -int AccountSet::load(kMyMoneyAccountSelector* selector) +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 (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)) + 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)) typeMask |= eDialogs::Category::asset; - if (m_typeList.contains(Account::CreditCard) - || m_typeList.contains(Account::Loan) - || m_typeList.contains(Account::Liability)) + if (d->m_typeList.contains(Account::CreditCard) + || d->m_typeList.contains(Account::Loan) + || d->m_typeList.contains(Account::Liability)) typeMask |= eDialogs::Category::liability; - if (m_typeList.contains(Account::Income)) + if (d->m_typeList.contains(Account::Income)) typeMask |= eDialogs::Category::income; - if (m_typeList.contains(Account::Expense)) + if (d->m_typeList.contains(Account::Expense)) typeMask |= eDialogs::Category::expense; - if (m_typeList.contains(Account::Equity)) + if (d->m_typeList.contains(Account::Equity)) typeMask |= eDialogs::Category::equity; selector->clear(); QTreeWidget* lv = selector->listView(); - m_count = 0; + d->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")); - m_favorites = selector->newItem(i18n("Favorites"), key); + d->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); } - m_favorites->setIcon(0, QIcon(accountPixmap)); + d->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) { - ++m_count; + ++d->m_count; key = QString("B%1").arg(i18n("Asset")); item = selector->newItem(i18n("Asset accounts"), key); - item->setIcon(0, m_file->asset().accountPixmap()); - list = m_file->asset().accountList(); + item->setIcon(0, d->m_file->asset().accountPixmap()); + list = d->m_file->asset().accountList(); } if ((typeMask & mask & eDialogs::Category::liability) != 0) { - ++m_count; + ++d->m_count; key = QString("C%1").arg(i18n("Liability")); item = selector->newItem(i18n("Liability accounts"), key); - item->setIcon(0, m_file->liability().accountPixmap()); - list = m_file->liability().accountList(); + item->setIcon(0, d->m_file->liability().accountPixmap()); + list = d->m_file->liability().accountList(); } if ((typeMask & mask & eDialogs::Category::income) != 0) { - ++m_count; + ++d->m_count; key = QString("D%1").arg(i18n("Income")); item = selector->newItem(i18n("Income categories"), key); - item->setIcon(0, m_file->income().accountPixmap()); - list = m_file->income().accountList(); + item->setIcon(0, d->m_file->income().accountPixmap()); + list = d->m_file->income().accountList(); if (selector->selectionMode() == QTreeWidget::MultiSelection) { - selector->m_incomeCategoriesButton->show(); + selector->d_func()->m_incomeCategoriesButton->show(); } } if ((typeMask & mask & eDialogs::Category::expense) != 0) { - ++m_count; + ++d->m_count; key = QString("E%1").arg(i18n("Expense")); item = selector->newItem(i18n("Expense categories"), key); - item->setIcon(0, m_file->expense().accountPixmap()); - list = m_file->expense().accountList(); + item->setIcon(0, d->m_file->expense().accountPixmap()); + list = d->m_file->expense().accountList(); if (selector->selectionMode() == QTreeWidget::MultiSelection) { - selector->m_expenseCategoriesButton->show(); + selector->d_func()->m_expenseCategoriesButton->show(); } } if ((typeMask & mask & eDialogs::Category::equity) != 0) { - ++m_count; + ++d->m_count; key = QString("F%1").arg(i18n("Equity")); item = selector->newItem(i18n("Equity accounts"), key); - item->setIcon(0, m_file->equity().accountPixmap()); - list = m_file->equity().accountList(); + item->setIcon(0, d->m_file->equity().accountPixmap()); + list = d->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 = m_file->account(*it_l); - ++m_count; + const MyMoneyAccount& acc = d->m_file->account(*it_l); + ++d->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" - && m_typeList.contains(acc.accountType())) { - selector->newItem(m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap());; + && d->m_typeList.contains(acc.accountType())) { + selector->newItem(d->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 (!m_typeList.contains(acc.accountType())) { + if (!d->m_typeList.contains(acc.accountType())) { selector->setSelectable(subItem, false); } subItem->sortChildren(1, Qt::AscendingOrder); } } item->sortChildren(1, Qt::AscendingOrder); } } - m_favorites->sortChildren(1, Qt::AscendingOrder); + d->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 (m_favorites->childCount() == 0 || selector->selectionMode() == QTreeWidget::MultiSelection) { - delete m_favorites; - m_favorites = 0; + if (d->m_favorites->childCount() == 0 || selector->selectionMode() == QTreeWidget::MultiSelection) { + delete d->m_favorites; + d->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) +int AccountSet::load(KMyMoneyAccountSelector* selector, const QString& baseName, const QList& accountIdList, const bool clear) { + Q_D(AccountSet); int count = 0; QTreeWidgetItem* item = 0; - m_typeList.clear(); + d->m_typeList.clear(); if (clear) { - m_count = 0; + d->m_count = 0; selector->clear(); } item = selector->newItem(baseName); - ++m_count; + ++d->m_count; QList::ConstIterator it; for (it = accountIdList.constBegin(); it != accountIdList.constEnd(); ++it) { - const MyMoneyAccount& acc = m_file->account(*it); + const MyMoneyAccount& acc = d->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()); - ++m_count; + ++d->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::loadSubAccounts(kMyMoneyAccountSelector* selector, QTreeWidgetItem* parent, const QString& key, const QStringList& list) +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 = m_file->account(*it_l); + const MyMoneyAccount& acc = d->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; - ++m_count; + ++d->m_count; QTreeWidgetItem* item = selector->newItem(parent, acc.name(), tmpKey, acc.id()); item->setIcon(0, acc.accountPixmap()); if (acc.value("PreferredAccount") == "Yes" - && m_typeList.contains(acc.accountType())) { - selector->newItem(m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap()); + && d->m_typeList.contains(acc.accountType())) { + selector->newItem(d->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 (!m_typeList.contains(acc.accountType())) { + if (!d->m_typeList.contains(acc.accountType())) { selector->setSelectable(item, false); } item->sortChildren(1, Qt::AscendingOrder); } } return count; } bool AccountSet::includeAccount(const MyMoneyAccount& acc) { - if (m_typeList.contains(acc.accountType())) + Q_D(AccountSet); + if (d->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 = m_file->account(*it_acc); + MyMoneyAccount account = d->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 9f7a7feaf..ce577a497 100644 --- a/kmymoney/widgets/kmymoneyaccountselector.h +++ b/kmymoney/widgets/kmymoneyaccountselector.h @@ -1,202 +1,189 @@ /*************************************************************************** kmymoneyaccountselector.h ------------------- begin : Thu Sep 18 2003 copyright : (C) 2003 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 KMYMONEYACCOUNTSELECTOR_H #define KMYMONEYACCOUNTSELECTOR_H // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes -class QPushButton; - // ---------------------------------------------------------------------------- // Project Includes -#include -#include "mymoneyenums.h" +#include "kmymoneyselector.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 kMyMoneyAccountSelector : public KMyMoneySelector +class KMyMoneyAccountSelectorPrivate; +class KMyMoneyAccountSelector : public KMyMoneySelector { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyAccountSelector) + public: friend class AccountSet; - explicit kMyMoneyAccountSelector(QWidget *parent = 0, Qt::WindowFlags flags = 0, const bool createButtons = true); - virtual ~kMyMoneyAccountSelector(); + explicit KMyMoneyAccountSelector(QWidget* parent = nullptr, Qt::WindowFlags flags = 0, const bool createButtons = true); + ~KMyMoneyAccountSelector() override; /** * 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 = QList()) const; + QStringList accountList(const QList& list) const; + QStringList accountList() 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() { - selectAllItems(true); - }; + void slotSelectAllAccounts(); /** * This slot deselects all items that are currently in * the account list of the widget. */ - void slotDeselectAllAccounts() { - selectAllItems(false); - }; + void slotDeselectAllAccounts(); 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() { - selectCategories(true, false); - }; + void slotSelectIncomeCategories(); /** * This slot selects all expense categories */ - void slotSelectExpenseCategories() { - selectCategories(false, true); - }; + void slotSelectExpenseCategories(); -protected: - QPushButton* m_allAccountsButton; - QPushButton* m_noAccountButton; - QPushButton* m_incomeCategoriesButton; - QPushButton* m_expenseCategoriesButton; - QList m_typeList; - QStringList m_accountList; +private: + Q_DECLARE_PRIVATE(KMyMoneyAccountSelector) }; - +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 load(KMyMoneyAccountSelector* selector); + int load(KMyMoneyAccountSelector* selector, const QString& baseName, const QList& accountIdList, const bool clear = false); - int count() const { - return m_count; - } + int count() const; - void setHideClosedAccounts(bool _bool) { - m_hideClosedAccounts = _bool; - } - bool isHidingClosedAccounts() const { - return m_hideClosedAccounts; - } + void setHideClosedAccounts(bool _bool); + bool isHidingClosedAccounts() const; protected: - int loadSubAccounts(kMyMoneyAccountSelector* selector, QTreeWidgetItem* parent, const QString& key, const QStringList& list); + int loadSubAccounts(KMyMoneyAccountSelector* selector, QTreeWidgetItem* parent, const QString& key, const QStringList& list); bool includeAccount(const MyMoneyAccount& acc); private: - int m_count; - MyMoneyFile* m_file; - QList m_typeList; - QTreeWidgetItem* m_favorites; - bool m_hideClosedAccounts; + AccountSetPrivate * const d_ptr; + Q_DECLARE_PRIVATE(AccountSet) }; + #endif diff --git a/kmymoney/widgets/kmymoneyaccounttreeview.cpp b/kmymoney/widgets/kmymoneyaccounttreeview.cpp index e9e865539..673f554f5 100644 --- a/kmymoney/widgets/kmymoneyaccounttreeview.cpp +++ b/kmymoney/widgets/kmymoneyaccounttreeview.cpp @@ -1,227 +1,257 @@ /*************************************************************************** * 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" -KMyMoneyAccountTreeView::KMyMoneyAccountTreeView(QWidget *parent) - : QTreeView(parent), m_view(View::None) +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)) { 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() { - if (m_view != View::None) { - auto grp = KSharedConfig::openConfig()->group(getConfGrpName(m_view)); + Q_D(KMyMoneyAccountTreeView); + if (d->m_view != View::None) { + auto grp = KSharedConfig::openConfig()->group(d->getConfGrpName(d->m_view)); const auto columns = header()->saveState(); grp.writeEntry("HeaderState", columns); QList visColumns; - foreach (const auto column, m_model->getVisibleColumns()) + foreach (const auto column, d->m_model->getVisibleColumns()) visColumns.append(static_cast(column)); grp.writeEntry("ColumnsSelection", visColumns); grp.sync(); } + delete d; } AccountsViewProxyModel *KMyMoneyAccountTreeView::init(View view) { - m_view = view; + Q_D(KMyMoneyAccountTreeView); + d->m_view = view; if (view != View::Budget) - m_model = new AccountsViewProxyModel(this); + d->m_model = new AccountsViewProxyModel(this); else - m_model = new BudgetViewProxyModel(this); + d->m_model = new BudgetViewProxyModel(this); - m_model->addAccountGroup(getVisibleGroups(view)); + d->m_model->addAccountGroup(d->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, readVisibleColumns(view)) { - m_model->setColumnVisibility(column, true); - accountsModel->setColumnVisibility(column, true); - institutionsModel->setColumnVisibility(column, true); + foreach (const auto column, d->readVisibleColumns(view)) { + d->m_model->setColumnVisibility(column, true); + accountsModel->setColumnVisibility(column, true); + institutionsModel->setColumnVisibility(column, true); } - m_model->setSourceModel(sourceModel); - m_model->setSourceColumns(sourceModel->getColumns()); - setModel(m_model); + d->m_model->setSourceModel(sourceModel); + d->m_model->setSourceColumns(sourceModel->getColumns()); + setModel(d->m_model); - connect(this->header(), &QWidget::customContextMenuRequested, m_model, &AccountsViewProxyModel::slotColumnsMenu); - connect(m_model, &AccountsViewProxyModel::columnToggled, this, &KMyMoneyAccountTreeView::columnToggled); + connect(this->header(), &QWidget::customContextMenuRequested, d->m_model, &AccountsViewProxyModel::slotColumnsMenu); + connect(d->m_model, &AccountsViewProxyModel::columnToggled, this, &KMyMoneyAccountTreeView::columnToggled); // restore the headers - const auto grp = KSharedConfig::openConfig()->group(getConfGrpName(view)); + const auto grp = KSharedConfig::openConfig()->group(d->getConfGrpName(view)); const auto columnNames = grp.readEntry("HeaderState", QByteArray()); header()->restoreState(columnNames); - return m_model; + return d->m_model; } void KMyMoneyAccountTreeView::mouseDoubleClickEvent(QMouseEvent *event) { - openIndex(currentIndex()); + Q_D(KMyMoneyAccountTreeView); + d->openIndex(currentIndex()); event->accept(); } void KMyMoneyAccountTreeView::keyPressEvent(QKeyEvent *event) { + Q_D(KMyMoneyAccountTreeView); if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { - openIndex(currentIndex()); + d->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 ecc05fb32..9125fb660 100644 --- a/kmymoney/widgets/kmymoneyaccounttreeview.h +++ b/kmymoney/widgets/kmymoneyaccounttreeview.h @@ -1,103 +1,96 @@ /*************************************************************************** * 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 -#include "mymoneyenums.h" -#include "viewenums.h" - -namespace eAccountsModel { - enum class Column; -} - -class AccountsViewProxyModel; class MyMoneyObject; +class AccountsViewProxyModel; + +namespace eAccountsModel { enum class Column; } +enum class View; /** * 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: - KMyMoneyAccountTreeView(QWidget *parent = nullptr); + explicit KMyMoneyAccountTreeView(QWidget* parent = nullptr); ~KMyMoneyAccountTreeView(); AccountsViewProxyModel *init(View view); protected: - void mouseDoubleClickEvent(QMouseEvent *event); - void keyPressEvent(QKeyEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; protected slots: void customContextMenuRequested(const QPoint); - void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override; 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: - 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; + KMyMoneyAccountTreeViewPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMyMoneyAccountTreeView) }; #endif // KMYMONEYACCOUNTTREEVIEW_H diff --git a/kmymoney/widgets/kmymoneyactivitycombo.cpp b/kmymoney/widgets/kmymoneyactivitycombo.cpp new file mode 100644 index 000000000..68a1d5fe2 --- /dev/null +++ b/kmymoney/widgets/kmymoneyactivitycombo.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + 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, &KMyMoneyMVCCombo::itemSelected, this, &KMyMoneyActivityCombo::slotSetActivity); +} + +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 new file mode 100644 index 000000000..d6c561588 --- /dev/null +++ b/kmymoney/widgets/kmymoneyactivitycombo.h @@ -0,0 +1,68 @@ +/*************************************************************************** + 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.cpp b/kmymoney/widgets/kmymoneybriefschedule.cpp index e6a4317b0..bb2837f9e 100644 --- a/kmymoney/widgets/kmymoneybriefschedule.cpp +++ b/kmymoney/widgets/kmymoneybriefschedule.cpp @@ -1,185 +1,211 @@ /*************************************************************************** kmymoneybriefschedule.cpp - 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 + (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 "kmymoneybriefschedule.h" // ---------------------------------------------------------------------------- // QT Includes +#include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include -#include // ---------------------------------------------------------------------------- // Project Includes +#include "ui_kmymoneybriefschedule.h" + #include "mymoneymoney.h" #include "mymoneyaccount.h" #include "mymoneyschedule.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "kmymoneyutils.h" #include "icons/icons.h" using namespace Icons; -KMyMoneyBriefSchedule::KMyMoneyBriefSchedule(QWidget *parent) - : kScheduleBriefWidget(parent/*,name, Qt::WStyle_Customize | Qt::WStyle_NoBorder*/), - m_index(0) -{ - m_nextButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ArrowRight])); - m_prevButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ArrowLeft])); - - connect(m_prevButton, SIGNAL(clicked()), this, SLOT(slotPrevClicked())); - connect(m_nextButton, SIGNAL(clicked()), this, SLOT(slotNextClicked())); - connect(m_closeButton, SIGNAL(clicked()), this, SLOT(hide())); - connect(m_skipButton, SIGNAL(clicked()), this, SLOT(slotSkipClicked())); - connect(m_buttonEnter, SIGNAL(clicked()), this, SLOT(slotEnterClicked())); - - KGuiItem skipGuiItem(i18n("&Skip"), - QIcon::fromTheme(g_Icons[Icon::MediaSeekForward]), - i18n("Skip this transaction"), - i18n("Use this button to skip this transaction")); - KGuiItem::assign(m_skipButton, skipGuiItem); - - KGuiItem enterGuiItem(i18n("&Enter"), - QIcon::fromTheme(g_Icons[Icon::KeyEnter]), - i18n("Record this transaction into the register"), - i18n("Use this button to record this transaction")); - KGuiItem::assign(m_buttonEnter, enterGuiItem); -} - -KMyMoneyBriefSchedule::~KMyMoneyBriefSchedule() +class KMyMoneyBriefSchedulePrivate { -} + Q_DISABLE_COPY(KMyMoneyBriefSchedulePrivate) -void KMyMoneyBriefSchedule::setSchedules(QList list, const QDate& date) -{ - m_scheduleList = list; - m_date = date; +public: + KMyMoneyBriefSchedulePrivate() : + ui(new Ui::KMyMoneyBriefSchedule), + m_index(0) + { + } - m_index = 0; - if (list.count() >= 1) { - loadSchedule(); + ~KMyMoneyBriefSchedulePrivate() + { + delete ui; } -} -void KMyMoneyBriefSchedule::loadSchedule() -{ - try { - if (m_index < m_scheduleList.count()) { - MyMoneySchedule sched = m_scheduleList[m_index]; - - m_indexLabel->setText(i18n("%1 of %2", m_index + 1, m_scheduleList.count())); - m_name->setText(sched.name()); - m_type->setText(KMyMoneyUtils::scheduleTypeToString(sched.type())); - m_account->setText(sched.account().name()); - QString text; - MyMoneyMoney amount = sched.transaction().splitByAccount(sched.account().id()).value(); - amount = amount.abs(); - - if (sched.willEnd()) { - int transactions = sched.paymentDates(m_date, sched.endDate()).count() - 1; - text = i18np("Payment on %2 for %3 with %1 transaction remaining occurring %4.", - "Payment on %2 for %3 with %1 transactions remaining occurring %4.", - transactions, - QLocale().toString(m_date, QLocale::ShortFormat), - amount.formatMoney(sched.account().fraction()), - i18n(sched.occurrenceToString().toLatin1())); - } else { - text = i18n("Payment on %1 for %2 occurring %3.", - QLocale().toString(m_date, QLocale::ShortFormat), - amount.formatMoney(sched.account().fraction()), - i18n(sched.occurrenceToString().toLatin1())); - } + void loadSchedule() + { + try { + if (m_index < m_scheduleList.count()) { + MyMoneySchedule sched = m_scheduleList[m_index]; + + ui->m_indexLabel->setText(i18n("%1 of %2", m_index + 1, m_scheduleList.count())); + ui->m_name->setText(sched.name()); + ui->m_type->setText(KMyMoneyUtils::scheduleTypeToString(sched.type())); + ui->m_account->setText(sched.account().name()); + QString text; + MyMoneyMoney amount = sched.transaction().splitByAccount(sched.account().id()).value(); + amount = amount.abs(); + + if (sched.willEnd()) { + int transactions = sched.paymentDates(m_date, sched.endDate()).count() - 1; + text = i18np("Payment on %2 for %3 with %1 transaction remaining occurring %4.", + "Payment on %2 for %3 with %1 transactions remaining occurring %4.", + transactions, + QLocale().toString(m_date, QLocale::ShortFormat), + amount.formatMoney(sched.account().fraction()), + i18n(sched.occurrenceToString().toLatin1())); + } else { + text = i18n("Payment on %1 for %2 occurring %3.", + QLocale().toString(m_date, QLocale::ShortFormat), + amount.formatMoney(sched.account().fraction()), + i18n(sched.occurrenceToString().toLatin1())); + } - if (m_date < QDate::currentDate()) { - if (sched.isOverdue()) { - QDate startD = (sched.lastPayment().isValid()) ? - sched.lastPayment() : - sched.startDate(); + if (m_date < QDate::currentDate()) { + if (sched.isOverdue()) { + QDate startD = (sched.lastPayment().isValid()) ? + sched.lastPayment() : + sched.startDate(); - if (m_date.isValid()) - startD = m_date; + if (m_date.isValid()) + startD = m_date; - int days = startD.daysTo(QDate::currentDate()); - int transactions = sched.paymentDates(startD, QDate::currentDate()).count(); + int days = startD.daysTo(QDate::currentDate()); + int transactions = sched.paymentDates(startD, QDate::currentDate()).count(); - text += "
"; - text += i18np("%1 day overdue", "%1 days overdue", days); - text += QString(" "); - text += i18np("(%1 occurrence.)", "(%1 occurrences.)", transactions); - text += ""; + text += "
"; + text += i18np("%1 day overdue", "%1 days overdue", days); + text += QString(" "); + text += i18np("(%1 occurrence.)", "(%1 occurrences.)", transactions); + text += ""; + } } - } - m_details->setText(text); + ui->m_details->setText(text); - m_prevButton->setEnabled(true); - m_nextButton->setEnabled(true); - m_skipButton->setEnabled(sched.occurrencePeriod() != eMyMoney::Schedule::Occurrence::Once); + ui->m_prevButton->setEnabled(true); + ui->m_nextButton->setEnabled(true); + ui->m_skipButton->setEnabled(sched.occurrencePeriod() != eMyMoney::Schedule::Occurrence::Once); - if (m_index == 0) - m_prevButton->setEnabled(false); - if (m_index == (m_scheduleList.count() - 1)) - m_nextButton->setEnabled(false); + if (m_index == 0) + ui->m_prevButton->setEnabled(false); + if (m_index == (m_scheduleList.count() - 1)) + ui->m_nextButton->setEnabled(false); + } + } catch (const MyMoneyException &) { } - } catch (const MyMoneyException &) { + } + + + Ui::KMyMoneyBriefSchedule *ui; + QList m_scheduleList; + int m_index; + QDate m_date; + +}; + +KMyMoneyBriefSchedule::KMyMoneyBriefSchedule(QWidget *parent) : + QWidget(parent), + d_ptr(new KMyMoneyBriefSchedulePrivate) +{ + Q_D(KMyMoneyBriefSchedule); + d->ui->setupUi(this); + d->ui->m_nextButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ArrowRight])); + d->ui->m_prevButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ArrowLeft])); + d->ui->m_skipButton->setIcon(QIcon::fromTheme(g_Icons[Icon::MediaSeekForward])); + d->ui->m_buttonEnter->setIcon(QIcon::fromTheme(g_Icons[Icon::KeyEnter])); + + connect(d->ui->m_prevButton, &QAbstractButton::clicked, this, &KMyMoneyBriefSchedule::slotPrevClicked); + connect(d->ui->m_nextButton, &QAbstractButton::clicked, this, &KMyMoneyBriefSchedule::slotNextClicked); + connect(d->ui->m_closeButton, &QAbstractButton::clicked, this, &QWidget::hide); + connect(d->ui->m_skipButton, &QAbstractButton::clicked, this, &KMyMoneyBriefSchedule::slotSkipClicked); + connect(d->ui->m_buttonEnter, &QAbstractButton::clicked, this, &KMyMoneyBriefSchedule::slotEnterClicked); +} + +KMyMoneyBriefSchedule::~KMyMoneyBriefSchedule() +{ + Q_D(KMyMoneyBriefSchedule); + delete d; +} + +void KMyMoneyBriefSchedule::setSchedules(QList list, const QDate& date) +{ + Q_D(KMyMoneyBriefSchedule); + d->m_scheduleList = list; + d->m_date = date; + + d->m_index = 0; + if (list.count() >= 1) { + d->loadSchedule(); } } void KMyMoneyBriefSchedule::slotPrevClicked() { - if (m_index >= 1) { - --m_index; - loadSchedule(); + Q_D(KMyMoneyBriefSchedule); + if (d->m_index >= 1) { + --d->m_index; + d->loadSchedule(); } } void KMyMoneyBriefSchedule::slotNextClicked() { - if (m_index < (m_scheduleList.count() - 1)) { - m_index++; - loadSchedule(); + Q_D(KMyMoneyBriefSchedule); + if (d->m_index < (d->m_scheduleList.count() - 1)) { + d->m_index++; + d->loadSchedule(); } } void KMyMoneyBriefSchedule::slotEnterClicked() { + Q_D(KMyMoneyBriefSchedule); hide(); - emit enterClicked(m_scheduleList[m_index], m_date); + emit enterClicked(d->m_scheduleList[d->m_index], d->m_date); } void KMyMoneyBriefSchedule::slotSkipClicked() { + Q_D(KMyMoneyBriefSchedule); hide(); - emit skipClicked(m_scheduleList[m_index], m_date); + emit skipClicked(d->m_scheduleList[d->m_index], d->m_date); } diff --git a/kmymoney/widgets/kmymoneybriefschedule.h b/kmymoney/widgets/kmymoneybriefschedule.h index 1809402a6..83695a274 100644 --- a/kmymoney/widgets/kmymoneybriefschedule.h +++ b/kmymoney/widgets/kmymoneybriefschedule.h @@ -1,80 +1,72 @@ /*************************************************************************** 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" +class QDate; +class MyMoneySchedule; + +template class QList; /** *@author Michael Edwardes */ -class kScheduleBriefWidget : public QWidget, public Ui::kScheduleBriefWidget -{ -public: - kScheduleBriefWidget(QWidget *parent) : QWidget(parent) { - setupUi(this); - } -}; - -class KMyMoneyBriefSchedule : public kScheduleBriefWidget +class KMyMoneyBriefSchedulePrivate; +class KMyMoneyBriefSchedule : public QWidget { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyBriefSchedule) + public: - KMyMoneyBriefSchedule(QWidget *parent = 0); + explicit KMyMoneyBriefSchedule(QWidget* parent = nullptr); ~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(); + KMyMoneyBriefSchedulePrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMyMoneyBriefSchedule) }; #endif diff --git a/kmymoney/widgets/kmymoneybriefschedule.ui b/kmymoney/widgets/kmymoneybriefschedule.ui new file mode 100644 index 000000000..fe0424f4c --- /dev/null +++ b/kmymoney/widgets/kmymoneybriefschedule.ui @@ -0,0 +1,376 @@ + + + KMyMoneyBriefSchedule + + + + 0 + 0 + 341 + 339 + + + + Schedules + + + + 6 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + QFrame::WinPanel + + + QFrame::Raised + + + 2 + + + 100 + + + + 6 + + + 11 + + + 11 + + + 11 + + + 11 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ... + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 0 + + + + + + + + n of n + + + false + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 70 + 0 + + + + + + + + ... + + + + + + + + + + + + 60 + 0 + + + + + 60 + 32767 + + + + Name: + + + false + + + + + + + + 60 + 0 + + + + + 60 + 32767 + + + + Type: + + + false + + + + + + + true + + + true + + + + + + + true + + + true + + + + + + + true + + + true + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + Account: + + + false + + + + + + + + + true + + + true + + + 1 + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 35 + 0 + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Record this transaction into the register + + + Use this button to record this transaction + + + Enter... + + + + + + + Skip this transaction + + + Use this button to skip this transaction + + + &Skip + + + + + + + &Close + + + 13 + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 43 + 0 + + + + + + + + + + + + + + + KTextEdit + QTextEdit +
ktextedit.h
+
+ + KLineEdit + QLineEdit +
klineedit.h
+
+
+ + +
diff --git a/kmymoney/widgets/kmymoneycalculator.cpp b/kmymoney/widgets/kmymoneycalculator.cpp index 022444543..00ba05c33 100644 --- a/kmymoney/widgets/kmymoneycalculator.cpp +++ b/kmymoney/widgets/kmymoneycalculator.cpp @@ -1,453 +1,568 @@ /*************************************************************************** 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 + (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 "kmymoneycalculator.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -kMyMoneyCalculator::kMyMoneyCalculator(QWidget* parent) - : QFrame(parent) +class KMyMoneyCalculatorPrivate { - m_comma = QLocale().decimalPoint(); - m_clearOperandOnDigit = false; + Q_DISABLE_COPY(KMyMoneyCalculatorPrivate) + +public: + KMyMoneyCalculatorPrivate() + { + } + + /** + * 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; +}; + +KMyMoneyCalculator::KMyMoneyCalculator(QWidget* parent) : + QFrame(parent), + d_ptr(new KMyMoneyCalculatorPrivate) +{ + Q_D(KMyMoneyCalculator); + d->m_comma = QLocale().decimalPoint(); + d->m_clearOperandOnDigit = false; QGridLayout* grid = new QGridLayout(this); - display = new QLabel(this); + d->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(); + palette.setColor(d->display->backgroundRole(), QColor("#BDFFB4")); + d->display->setPalette(palette); + + d->display->setFrameStyle(QFrame::Panel | QFrame::Sunken); + d->display->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + grid->addWidget(d->display, 0, 0, 1, 5); + + d->buttons[0] = new QPushButton("0", this); + d->buttons[1] = new QPushButton("1", this); + d->buttons[2] = new QPushButton("2", this); + d->buttons[3] = new QPushButton("3", this); + d->buttons[4] = new QPushButton("4", this); + d->buttons[5] = new QPushButton("5", this); + d->buttons[6] = new QPushButton("6", this); + d->buttons[7] = new QPushButton("7", this); + d->buttons[8] = new QPushButton("8", this); + d->buttons[9] = new QPushButton("9", this); + d->buttons[KMyMoneyCalculatorPrivate::PLUS] = new QPushButton("+", this); + d->buttons[KMyMoneyCalculatorPrivate::MINUS] = new QPushButton("-", this); + d->buttons[KMyMoneyCalculatorPrivate::STAR] = new QPushButton("X", this); + d->buttons[KMyMoneyCalculatorPrivate::COMMA] = new QPushButton(d->m_comma, this); + d->buttons[KMyMoneyCalculatorPrivate::EQUAL] = new QPushButton("=", this); + d->buttons[KMyMoneyCalculatorPrivate::SLASH] = new QPushButton("/", this); + d->buttons[KMyMoneyCalculatorPrivate::CLEAR] = new QPushButton("C", this); + d->buttons[KMyMoneyCalculatorPrivate::CLEARALL] = new QPushButton("AC", this); + d->buttons[KMyMoneyCalculatorPrivate::PLUSMINUS] = new QPushButton("+-", this); + d->buttons[KMyMoneyCalculatorPrivate::PERCENT] = new QPushButton("%", this); + + grid->addWidget(d->buttons[7], 1, 0); + grid->addWidget(d->buttons[8], 1, 1); + grid->addWidget(d->buttons[9], 1, 2); + grid->addWidget(d->buttons[4], 2, 0); + grid->addWidget(d->buttons[5], 2, 1); + grid->addWidget(d->buttons[6], 2, 2); + grid->addWidget(d->buttons[1], 3, 0); + grid->addWidget(d->buttons[2], 3, 1); + grid->addWidget(d->buttons[3], 3, 2); + grid->addWidget(d->buttons[0], 4, 1); + + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::COMMA], 4, 0); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::PLUS], 3, 3); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::MINUS], 4, 3); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::STAR], 3, 4); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::SLASH], 4, 4); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::EQUAL], 4, 2); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::PLUSMINUS], 2, 3); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::PERCENT], 2, 4); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::CLEAR], 1, 3); + grid->addWidget(d->buttons[KMyMoneyCalculatorPrivate::CLEARALL], 1, 4); + + d->buttons[KMyMoneyCalculatorPrivate::EQUAL]->setFocus(); + + d->op1 = 0.0; + d->stackedOp = d->op = d->op0 = 0; + d->operand.clear(); changeDisplay("0"); // connect the digit signals through a signal mapper QSignalMapper* mapper = new QSignalMapper(this); - for (int i = 0; i < 10; ++i) { - mapper->setMapping(buttons[i], i); - connect(buttons[i], SIGNAL(clicked()), mapper, SLOT(map())); + for (auto i = 0; i < 10; ++i) { + mapper->setMapping(d->buttons[i], i); + connect(d->buttons[i], &QAbstractButton::clicked, mapper, static_cast(&QSignalMapper::map)); } - connect(mapper, SIGNAL(mapped(int)), this, SLOT(digitClicked(int))); + connect(mapper, static_cast(&QSignalMapper::mapped), this, &KMyMoneyCalculator::digitClicked); // 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())); + for (int i = KMyMoneyCalculatorPrivate::PLUS; i <= KMyMoneyCalculatorPrivate::EQUAL; ++i) { + mapper->setMapping(d->buttons[i], i); + connect(d->buttons[i], &QAbstractButton::clicked, mapper, static_cast(&QSignalMapper::map)); } - connect(mapper, SIGNAL(mapped(int)), this, SLOT(calculationClicked(int))); + connect(mapper, static_cast(&QSignalMapper::mapped), this, &KMyMoneyCalculator::calculationClicked); // 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 (int i = 0; i < MAX_BUTTONS; ++i) { - buttons[i]->setMinimumSize(40, 30); - buttons[i]->setMaximumSize(40, 30); + connect(d->buttons[KMyMoneyCalculatorPrivate::COMMA], &QAbstractButton::clicked, this, &KMyMoneyCalculator::commaClicked); + connect(d->buttons[KMyMoneyCalculatorPrivate::PLUSMINUS], &QAbstractButton::clicked, this, &KMyMoneyCalculator::plusminusClicked); + connect(d->buttons[KMyMoneyCalculatorPrivate::PERCENT], &QAbstractButton::clicked, this, &KMyMoneyCalculator::percentClicked); + connect(d->buttons[KMyMoneyCalculatorPrivate::CLEAR], &QAbstractButton::clicked, this, &KMyMoneyCalculator::clearClicked); + connect(d->buttons[KMyMoneyCalculatorPrivate::CLEARALL], &QAbstractButton::clicked, this, &KMyMoneyCalculator::clearAllClicked); + + for (auto i = 0; i < KMyMoneyCalculatorPrivate::MAX_BUTTONS; ++i) { + d->buttons[i]->setMinimumSize(40, 30); + d->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() +KMyMoneyCalculator::~KMyMoneyCalculator() { + Q_D(KMyMoneyCalculator); + delete d; } -void kMyMoneyCalculator::digitClicked(int button) +void KMyMoneyCalculator::digitClicked(int button) { - if (m_clearOperandOnDigit) { - operand.clear(); - m_clearOperandOnDigit = false; + Q_D(KMyMoneyCalculator); + if (d->m_clearOperandOnDigit) { + d->operand.clear(); + d->m_clearOperandOnDigit = false; } - operand += QChar(button + 0x30); - if (operand.length() > 16) - operand = operand.left(16); - changeDisplay(operand); + d->operand += QChar(button + 0x30); + if (d->operand.length() > 16) + d->operand = d->operand.left(16); + changeDisplay(d->operand); } -void kMyMoneyCalculator::commaClicked() +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); + Q_D(KMyMoneyCalculator); + if (d->operand.length() == 0) + d->operand = '0'; + if (d->operand.contains('.', Qt::CaseInsensitive) == 0) + d->operand.append('.'); + + if (d->operand.length() > 16) + d->operand = d->operand.left(16); + changeDisplay(d->operand); } -void kMyMoneyCalculator::plusminusClicked() +void KMyMoneyCalculator::plusminusClicked() { - if (operand.length() == 0 && m_result.length() > 0) - operand = m_result; + Q_D(KMyMoneyCalculator); + if (d->operand.length() == 0 && d->m_result.length() > 0) + d->operand = d->m_result; - if (operand.length() > 0) { - if (operand.indexOf('-') != -1) - operand.remove('-'); + if (d->operand.length() > 0) { + if (d->operand.indexOf('-') != -1) + d->operand.remove('-'); else - operand.prepend('-'); - changeDisplay(operand); + d->operand.prepend('-'); + changeDisplay(d->operand); } } -void kMyMoneyCalculator::calculationClicked(int button) +void KMyMoneyCalculator::calculationClicked(int button) { - if (operand.length() == 0 && op != 0 && button == EQUAL) { - op = 0; - m_result = normalizeString(op1); - changeDisplay(m_result); + Q_D(KMyMoneyCalculator); + if (d->operand.length() == 0 && d->op != 0 && button == KMyMoneyCalculatorPrivate::EQUAL) { + d->op = 0; + d->m_result = normalizeString(d->op1); + changeDisplay(d->m_result); - } else if (operand.length() > 0 && op != 0) { + } else if (d->operand.length() > 0 && d->op != 0) { // perform operation - double op2 = operand.toDouble(); + double op2 = d->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; + if ((d->op == KMyMoneyCalculatorPrivate::PLUS || d->op == KMyMoneyCalculatorPrivate::MINUS) && (button == KMyMoneyCalculatorPrivate::STAR || button == KMyMoneyCalculatorPrivate::SLASH)) { + d->op0 = d->op1; + d->stackedOp = d->op; + d->op = 0; } - switch (op) { - case PLUS: - op2 = op1 + op2; + switch (d->op) { + case KMyMoneyCalculatorPrivate::PLUS: + op2 = d->op1 + op2; break; - case MINUS: - op2 = op1 - op2; + case KMyMoneyCalculatorPrivate::MINUS: + op2 = d->op1 - op2; break; - case STAR: - op2 = op1 * op2; + case KMyMoneyCalculatorPrivate::STAR: + op2 = d->op1 * op2; break; - case SLASH: + case KMyMoneyCalculatorPrivate::SLASH: if (op2 == 0.0) error = true; else - op2 = op1 / op2; + op2 = d->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; + if (d->stackedOp && button != KMyMoneyCalculatorPrivate::STAR && button != KMyMoneyCalculatorPrivate::SLASH) { + switch (d->stackedOp) { + case KMyMoneyCalculatorPrivate::PLUS: + op2 = d->op0 + op2; break; - case MINUS: - op2 = op0 - op2; + case KMyMoneyCalculatorPrivate::MINUS: + op2 = d->op0 - op2; break; } - stackedOp = 0; + d->stackedOp = 0; } if (error) { - op = 0; + d->op = 0; changeDisplay("Error"); - operand.clear(); + d->operand.clear(); } else { - op1 = op2; - m_result = normalizeString(op1); - changeDisplay(m_result); + d->op1 = op2; + d->m_result = normalizeString(d->op1); + changeDisplay(d->m_result); } - } else if (operand.length() > 0 && op == 0) { - op1 = operand.toDouble(); - m_result = normalizeString(op1); - changeDisplay(m_result); + } else if (d->operand.length() > 0 && d->op == 0) { + d->op1 = d->operand.toDouble(); + d->m_result = normalizeString(d->op1); + changeDisplay(d->m_result); } - if (button != EQUAL) { - op = button; + if (button != KMyMoneyCalculatorPrivate::EQUAL) { + d->op = button; } else { - op = 0; + d->op = 0; emit signalResultAvailable(); } - operand.clear(); + d->operand.clear(); } -QString kMyMoneyCalculator::normalizeString(const double& val) +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() +void KMyMoneyCalculator::clearClicked() { - if (operand.length() > 0) { - operand = operand.left(operand.length() - 1); + Q_D(KMyMoneyCalculator); + if (d->operand.length() > 0) { + d->operand = d->operand.left(d->operand.length() - 1); } - if (operand.length() == 0) + if (d->operand.length() == 0) changeDisplay("0"); else - changeDisplay(operand); + changeDisplay(d->operand); } -void kMyMoneyCalculator::clearAllClicked() +void KMyMoneyCalculator::clearAllClicked() { - operand.clear(); - op = 0; + Q_D(KMyMoneyCalculator); + d->operand.clear(); + d->op = 0; changeDisplay("0"); } -void kMyMoneyCalculator::percentClicked() +void KMyMoneyCalculator::percentClicked() { - if (op != 0) { - double op2 = operand.toDouble(); - switch (op) { - case PLUS: - case MINUS: - op2 = (op1 * op2) / 100; + Q_D(KMyMoneyCalculator); + if (d->op != 0) { + double op2 = d->operand.toDouble(); + switch (d->op) { + case KMyMoneyCalculatorPrivate::PLUS: + case KMyMoneyCalculatorPrivate::MINUS: + op2 = (d->op1 * op2) / 100; break; - case STAR: - case SLASH: + case KMyMoneyCalculatorPrivate::STAR: + case KMyMoneyCalculatorPrivate::SLASH: op2 /= 100; break; } - operand = normalizeString(op2); - changeDisplay(operand); + d->operand = normalizeString(op2); + changeDisplay(d->operand); } } -const QString kMyMoneyCalculator::result() const +QString KMyMoneyCalculator::result() const { - QString txt = m_result; - txt.replace(QRegExp("\\."), m_comma); + Q_D(const KMyMoneyCalculator); + auto txt = d->m_result; + txt.replace(QRegExp("\\."), d->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) +void KMyMoneyCalculator::setComma(const QChar ch) +{ + Q_D(KMyMoneyCalculator); + d->m_comma = ch; +} + +void KMyMoneyCalculator::changeDisplay(const QString& str) { - QString txt = str; - txt.replace(QRegExp("\\."), m_comma); - display->setText("" + txt + ""); + Q_D(KMyMoneyCalculator); + auto txt = str; + txt.replace(QRegExp("\\."), d->m_comma); + d->display->setText("" + txt + ""); } -void kMyMoneyCalculator::keyPressEvent(QKeyEvent* ev) +void KMyMoneyCalculator::keyPressEvent(QKeyEvent* ev) { + Q_D(KMyMoneyCalculator); 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; + if (d->m_clearOperandOnDigit) { + d->operand.clear(); + d->m_clearOperandOnDigit = false; } button = ev->key() - Qt::Key_0; break; case Qt::Key_Plus: - button = PLUS; + button = KMyMoneyCalculatorPrivate::PLUS; break; case Qt::Key_Minus: - button = MINUS; + button = KMyMoneyCalculatorPrivate::MINUS; break; case Qt::Key_Comma: case Qt::Key_Period: - if (m_clearOperandOnDigit) { - operand.clear(); - m_clearOperandOnDigit = false; + if (d->m_clearOperandOnDigit) { + d->operand.clear(); + d->m_clearOperandOnDigit = false; } - button = COMMA; + button = KMyMoneyCalculatorPrivate::COMMA; break; case Qt::Key_Slash: - button = SLASH; + button = KMyMoneyCalculatorPrivate::SLASH; break; case Qt::Key_Backspace: - button = CLEAR; + button = KMyMoneyCalculatorPrivate::CLEAR; if(ev->modifiers() & Qt::ShiftModifier) { - button = CLEARALL; + button = KMyMoneyCalculatorPrivate::CLEARALL; } break; case Qt::Key_Asterisk: - button = STAR; + button = KMyMoneyCalculatorPrivate::STAR; break; case Qt::Key_Return: case Qt::Key_Enter: case Qt::Key_Equal: - button = EQUAL; + button = KMyMoneyCalculatorPrivate::EQUAL; break; case Qt::Key_Escape: emit signalQuit(); break; case Qt::Key_Percent: - button = PERCENT; + button = KMyMoneyCalculatorPrivate::PERCENT; break; default: ev->ignore(); break; } if (button != -1) - buttons[button]->animateClick(); + d->buttons[button]->animateClick(); - m_clearOperandOnDigit = false; + d->m_clearOperandOnDigit = false; } -void kMyMoneyCalculator::setInitialValues(const QString& value, QKeyEvent* ev) +void KMyMoneyCalculator::setInitialValues(const QString& value, QKeyEvent* ev) { + Q_D(KMyMoneyCalculator); bool negative = false; // setup operand - operand = value; + d->operand = value; // TODO: port this to kf5 - //operand.replace(QRegExp(QString('\\') + ""/* TODO: port to kf5 - KLocale::global()->thousandsSeparator()*/), QChar()); - operand.replace(QRegExp(QString('\\') + m_comma), "."); - if (operand.contains('(')) { + //operand.replace(QRegExp(QString('\\') + QString()/* TODO: port to kf5 - KLocale::global()->thousandsSeparator()*/), QChar()); + d->operand.replace(QRegExp(QString('\\') + d->m_comma), "."); + if (d->operand.contains('(')) { negative = true; - operand.remove('('); - operand.remove(')'); + d->operand.remove('('); + d->operand.remove(')'); } - if (operand.contains('-')) { + if (d->operand.contains('-')) { negative = true; - operand.remove('-'); + d->operand.remove('-'); } - if (operand.isEmpty()) - operand = '0'; + if (d->operand.isEmpty()) + d->operand = '0'; else if (negative) - operand = QString("-%1").arg(operand); + d->operand = QString("-%1").arg(d->operand); - changeDisplay(operand); + changeDisplay(d->operand); // and operation - op = 0; + d->op = 0; if (ev) keyPressEvent(ev); else - m_clearOperandOnDigit = true; + d->m_clearOperandOnDigit = true; } diff --git a/kmymoney/widgets/kmymoneycalculator.h b/kmymoney/widgets/kmymoneycalculator.h index 24fb4da41..1c9de37c7 100644 --- a/kmymoney/widgets/kmymoneycalculator.h +++ b/kmymoney/widgets/kmymoneycalculator.h @@ -1,253 +1,174 @@ /*************************************************************************** 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 + (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 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 +class KMyMoneyCalculatorPrivate; +class KMM_WIDGETS_EXPORT KMyMoneyCalculator : public QFrame { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyCalculator) + public: - kMyMoneyCalculator(QWidget* parent = 0); - ~kMyMoneyCalculator(); + explicit KMyMoneyCalculator(QWidget* parent = nullptr); + ~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; + 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; - }; + void setComma(const QChar 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 + * 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); + void keyPressEvent(QKeyEvent* ev) override; /** * 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; + KMyMoneyCalculatorPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMyMoneyCalculator) }; #endif diff --git a/kmymoney/widgets/kmymoneycashflowcombo.cpp b/kmymoney/widgets/kmymoneycashflowcombo.cpp new file mode 100644 index 000000000..46ced42d9 --- /dev/null +++ b/kmymoney/widgets/kmymoneycashflowcombo.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + 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 "mymoneyenums.h" +#include "widgetenums.h" + +using namespace eWidgets; +using namespace eMyMoney; + +class KMyMoneyCashFlowComboPrivate : public KMyMoneyMVCComboPrivate +{ + Q_DISABLE_COPY(KMyMoneyCashFlowComboPrivate) + +public: + KMyMoneyCashFlowComboPrivate() : + m_dir(eRegister::CashFlowDirection::Unknown) + { + } + + eRegister::CashFlowDirection m_dir; +}; + +KMyMoneyCashFlowCombo::KMyMoneyCashFlowCombo(Account accountType, QWidget* parent) : + KMyMoneyMVCCombo(*new KMyMoneyCashFlowComboPrivate, false, parent) +{ + addItem(" ", QVariant((int)eRegister::CashFlowDirection::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((int)eRegister::CashFlowDirection::Payment)); + addItem(i18nc("Activity for expense categories", "Paid"), QVariant((int)eRegister::CashFlowDirection::Deposit)); + } else { + addItem(i18n("Pay to"), QVariant((int)eRegister::CashFlowDirection::Payment)); + addItem(i18n("From"), QVariant((int)eRegister::CashFlowDirection::Deposit)); + } + + connect(this, &KMyMoneyMVCCombo::itemSelected, this, &KMyMoneyCashFlowCombo::slotSetDirection); +} + +KMyMoneyCashFlowCombo::~KMyMoneyCashFlowCombo() +{ +} + +void KMyMoneyCashFlowCombo::setDirection(eRegister::CashFlowDirection dir) +{ + Q_D(KMyMoneyCashFlowCombo); + d->m_dir = dir; + QString num; + setSelectedItem(num.setNum((int)dir)); +} + +eRegister::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 = (int)eRegister::CashFlowDirection::Deposit; i <= (int)eRegister::CashFlowDirection::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((int)eRegister::CashFlowDirection::Unknown), Qt::UserRole, Qt::MatchExactly)); +} diff --git a/kmymoney/widgets/kmymoneycashflowcombo.h b/kmymoney/widgets/kmymoneycashflowcombo.h new file mode 100644 index 000000000..79465dcf7 --- /dev/null +++ b/kmymoney/widgets/kmymoneycashflowcombo.h @@ -0,0 +1,72 @@ +/*************************************************************************** + 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 eWidgets { namespace eRegister { enum class CashFlowDirection; } } + +/** + * @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(eWidgets::eRegister::CashFlowDirection dir); + eWidgets::eRegister::CashFlowDirection direction() const; + + void removeDontCare(); + +protected slots: + void slotSetDirection(const QString& id); + +signals: + void directionSelected(eWidgets::eRegister::CashFlowDirection); + +private: + Q_DECLARE_PRIVATE(KMyMoneyCashFlowCombo) +}; + +#endif diff --git a/kmymoney/widgets/kmymoneycategory.cpp b/kmymoney/widgets/kmymoneycategory.cpp index d4fae5612..e47b2d935 100644 --- a/kmymoney/widgets/kmymoneycategory.cpp +++ b/kmymoney/widgets/kmymoneycategory.cpp @@ -1,224 +1,256 @@ /*************************************************************************** kmymoneycategory.cpp - description ------------------- begin : Mon Jul 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 "kmymoneycategory.h" +#include "kmymoneycombo_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include +#include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes +#include "kmymoneyaccountselector.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "kmymoneyaccountcompletion.h" #include "icons/icons.h" using namespace Icons; -class KMyMoneyCategory::Private +class KMyMoneyCategoryPrivate : public KMyMoneyComboPrivate { public: - Private() : + KMyMoneyCategoryPrivate() : splitButton(0), frame(0), recursive(false), - isSplit(false) {} + isSplit(false) + { + } QPushButton* splitButton; QFrame* frame; bool recursive; bool isSplit; }; -KMyMoneyCategory::KMyMoneyCategory(QWidget* parent, bool splitButton) : - KMyMoneyCombo(true, parent), - d(new Private) +KMyMoneyCategory::KMyMoneyCategory(bool splitButton, QWidget* parent) : + KMyMoneyCombo(*new KMyMoneyCategoryPrivate, true, parent) { + Q_D(KMyMoneyCategory); if (splitButton) { - d->frame = new QFrame(0); + d->frame = new QFrame(nullptr); // 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); } - 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))); + d->m_completion = new KMyMoneyAccountCompletion(this); + connect(d->m_completion, &KMyMoneyCompletion::itemSelected, this, &KMyMoneyCategory::slotItemSelected); + connect(this, &QComboBox::editTextChanged, d->m_completion, &KMyMoneyCompletion::slotMakeCompletion); } 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 +KMyMoneyAccountSelector* KMyMoneyCategory::selector() const { - return dynamic_cast(KMyMoneyCombo::selector()); + 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); - m_completion->hide(); + d->m_completion->hide(); - if (m_id != id) { - m_id = id; + if (d->m_id != id) { + d->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(parent, false) + KMyMoneyCategory(false, parent) { } 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 e764d4827..ee5d1f33d 100644 --- a/kmymoney/widgets/kmymoneycategory.h +++ b/kmymoney/widgets/kmymoneycategory.h @@ -1,194 +1,193 @@ /*************************************************************************** kmymoneycategory.h ------------------- begin : Mon Jul 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 KMYMONEYCATEGORY_H #define KMYMONEYCATEGORY_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes -class QPushButton; - // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneycombo.h" -class kMyMoneyAccountSelector; +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 + * 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(QWidget* parent = 0, bool splitButton = false); - - virtual ~KMyMoneyCategory(); + explicit KMyMoneyCategory(bool splitButton = false, QWidget* parent = nullptr); + ~KMyMoneyCategory() override; /** * This member returns a pointer to the completion object. * * @return pointer to completion's selector object */ - kMyMoneyAccountSelector* selector() const; + 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 = QString()) { - KMyMoneyCombo::setCurrentText(txt); - } + void setCurrentText(const QString& txt); + void setCurrentText(); - bool eventFilter(QObject *o, QEvent *ev); + bool eventFilter(QObject *o, QEvent *ev) override; protected: /** * Reimplemented to support protected category text ("split transactions") * * @sa focusIn() */ - virtual void focusInEvent(QFocusEvent* ev); + void focusInEvent(QFocusEvent* ev) override; /** * Reimplemented to support protected category text ("split transactions") */ - virtual void focusOutEvent(QFocusEvent* ev); + void focusOutEvent(QFocusEvent* ev) override; /** * set the widgets text area based on the item with the given @a id. */ - virtual void setCurrentTextById(const QString& id); + void setCurrentTextById(const QString& id) override; public slots: - virtual void slotItemSelected(const QString& id); + void slotItemSelected(const QString& id) override; 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: - /// \internal d-pointer class. - class Private; - /// \internal d-pointer instance. - Private* const d; + Q_DECLARE_PRIVATE(KMyMoneyCategory) }; class KMyMoneySecurity : public KMyMoneyCategory { Q_OBJECT + Q_DISABLE_COPY(KMyMoneySecurity) + public: - KMyMoneySecurity(QWidget* parent = 0); - virtual ~KMyMoneySecurity(); + explicit KMyMoneySecurity(QWidget* parent = nullptr); + ~KMyMoneySecurity() override; /** * overridden for internal reasons, no API change */ - void setCurrentText(const QString& txt = QString()) { - KMyMoneyCategory::setCurrentText(txt); - } + void setCurrentText(const QString& txt); + void setCurrentText(); protected: /** * set the widgets text area based on the item with the given @a id. */ - virtual void setCurrentTextById(const QString& id); + void setCurrentTextById(const QString& id) override; }; #endif diff --git a/kmymoney/widgets/kmymoneycombo.cpp b/kmymoney/widgets/kmymoneycombo.cpp index d0949c314..6a2b8ac66 100644 --- a/kmymoney/widgets/kmymoneycombo.cpp +++ b/kmymoney/widgets/kmymoneycombo.cpp @@ -1,332 +1,382 @@ /*************************************************************************** kmymoneycombo.cpp - description ------------------- begin : Mon Mar 12 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 "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 *w) : - KComboBox(w), - m_completion(0), - m_edit(0), - m_canCreateObjects(false), - m_inFocusOutEvent(false) +KMyMoneyCombo::KMyMoneyCombo(QWidget *parent) : + KComboBox(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) +KMyMoneyCombo::KMyMoneyCombo(bool rw, QWidget *parent) : + KComboBox(rw, parent), + d_ptr(new KMyMoneyComboPrivate) { + Q_D(KMyMoneyCombo); if (rw) { - m_edit = new kMyMoneyLineEdit(this, "combo edit"); - setLineEdit(m_edit); + d->m_edit = new KMyMoneyLineEdit(this, "combo edit"); + setLineEdit(d->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); } - m_completion->hide(); + d->m_completion->hide(); - if (m_id != id) { - m_id = id; + if (d->m_id != id) { + d->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) { - m_edit = new kMyMoneyLineEdit(this, "combo edit"); - setLineEdit(m_edit); - m_edit->setPalette(palette()); + d->m_edit = new KMyMoneyLineEdit(this, "combo edit"); + setLineEdit(d->m_edit); + d->m_edit->setPalette(palette()); } else { - m_edit = 0; + d->m_edit = 0; } } void KMyMoneyCombo::setPlaceholderText(const QString& hint) const { - if (m_edit) - m_edit->setPlaceholderText(hint); + Q_D(const KMyMoneyCombo); + if (d->m_edit) + d->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 (!m_edit) { - if (m_completion) { + if (!d->m_edit) { + if (d->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()) && !m_completion->isVisible()) { - m_completion->setVisible(true); + if (((!isEditable() || isInArrowArea(e->globalPos())) && selector()->itemList().count()) && !d->m_completion->isVisible()) { + d->m_completion->setVisible(true); } - if (m_timer.isActive()) { - m_timer.stop(); - m_completion->slotMakeCompletion(""); + if (d->m_timer.isActive()) { + d->m_timer.stop(); + d->m_completion->slotMakeCompletion(QString()); // 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"); - m_timer.setSingleShot(true); - m_timer.start(grp.readEntry("DoubleClickInterval", 400)); + d->m_timer.setSingleShot(true); + d->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)) - m_completion->setVisible(true); + d->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)) { - m_canCreateObjects = true; + d->m_canCreateObjects = true; } } void KMyMoneyCombo::disconnectNotify(const QMetaMethod & signal) { + Q_D(KMyMoneyCombo); if (signal != QMetaMethod::fromSignal(&KMyMoneyCombo::createItem)) { - m_canCreateObjects = false; + d->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 (m_inFocusOutEvent) { + if (d->m_inFocusOutEvent) { KComboBox::focusOutEvent(e); return; } - m_inFocusOutEvent = true; + d->m_inFocusOutEvent = true; if (isEditable() && !currentText().isEmpty()) { - if (m_canCreateObjects) { - if (!m_completion->selector()->contains(currentText())) { + if (d->m_canCreateObjects) { + if (!d->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 - m_id = id; + d->m_id = id; setCurrentTextById(id); // make sure the completion does not show through - m_completion->hide(); + d->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 (!m_completion->selector()->contains(currentText())) { + } else if (!d->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 = m_id; - m_id.clear(); + QString id = d->m_id; + d->m_id.clear(); if (!id.isEmpty()) - emit itemSelected(m_id); + emit itemSelected(d->m_id); update(); } - m_inFocusOutEvent = false; + d->m_inFocusOutEvent = false; } KMyMoneySelector* KMyMoneyCombo::selector() const { - return m_completion->selector(); + Q_D(const KMyMoneyCombo); + return d->m_completion->selector(); } -kMyMoneyCompletion* KMyMoneyCombo::completion() const +KMyMoneyCompletion* KMyMoneyCombo::completion() const { - return m_completion; + Q_D(const KMyMoneyCombo); + return d->m_completion; } void KMyMoneyCombo::selectedItems(QStringList& list) const { + Q_D(const KMyMoneyCombo); if (lineEdit() && lineEdit()->text().length() == 0) { list.clear(); } else { - m_completion->selector()->selectedItems(list); + d->m_completion->selector()->selectedItems(list); } } +QString KMyMoneyCombo::selectedItem() const +{ + Q_D(const KMyMoneyCombo); + return d->m_id; +} + void KMyMoneyCombo::setSelectedItem(const QString& id) { - m_completion->selector()->setSelected(id, true); + Q_D(KMyMoneyCombo); + d->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 9eb52c4c5..d4f282ebb 100644 --- a/kmymoney/widgets/kmymoneycombo.h +++ b/kmymoney/widgets/kmymoneycombo.h @@ -1,207 +1,176 @@ /*************************************************************************** kmymoneycombo.h - description ------------------- begin : Mon Mar 12 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. * * * ***************************************************************************/ #ifndef KMYMONEYCOMBO_H #define KMYMONEYCOMBO_H // ---------------------------------------------------------------------------- // QT Includes -#include -#include -#include - // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes -class kMyMoneyCompletion; +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: - KMyMoneyCombo(QWidget *w = 0); - explicit KMyMoneyCombo(bool rw, QWidget *w = 0); + explicit KMyMoneyCombo(QWidget *parent = nullptr); + explicit KMyMoneyCombo(bool rw = false, QWidget *parent = nullptr); + virtual ~KMyMoneyCombo(); /** * 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. + * @return pointer to KMyMoneyCompletion or derivative. */ - kMyMoneyCompletion* completion() const; + 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 { - return m_id; - } + 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); /** * 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) { - m_canCreateObjects = !suppress; - } + void setSuppressObjectCreation(bool suppress); /** * overridden for internal reasons, no API change */ - void setCurrentText(const QString& txt = QString()) { - KComboBox::setItemText(KComboBox::currentIndex(), txt); - } + void setCurrentText(const QString& txt); + void setCurrentText(); /** * Overridden to support our own completion box */ - QSize sizeHint() const; + QSize sizeHint() const override; protected slots: virtual void slotItemSelected(const QString& id); protected: /** * reimplemented to support our own popup widget */ - void mousePressEvent(QMouseEvent *e); + void mousePressEvent(QMouseEvent *e) override; /** * reimplemented to support our own popup widget */ - void keyPressEvent(QKeyEvent *e); + void keyPressEvent(QKeyEvent *e) override; /** * reimplemented to support our own popup widget */ - void paintEvent(QPaintEvent *); + void paintEvent(QPaintEvent *) override; /** * reimplemented to support detection of new items */ - void focusOutEvent(QFocusEvent*); + void focusOutEvent(QFocusEvent*) override; /** * 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); + void connectNotify(const QMetaMethod & signal) override; /** * Overridden for internal reasons, no API change */ - void disconnectNotify(const QMetaMethod & signal); + void disconnectNotify(const QMetaMethod & signal) override; protected: - /** - * 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; + KMyMoneyComboPrivate * const d_ptr; + KMyMoneyCombo(KMyMoneyComboPrivate &dd, bool rw = false, QWidget *parent = 0); signals: void itemSelected(const QString& id); void objectCreation(bool); void createItem(const QString&, QString&); private: - 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; + Q_DECLARE_PRIVATE(KMyMoneyCombo) }; - - - #endif diff --git a/kmymoney/widgets/kmymoneycombo_p.h b/kmymoney/widgets/kmymoneycombo_p.h new file mode 100644 index 000000000..b56683224 --- /dev/null +++ b/kmymoney/widgets/kmymoneycombo_p.h @@ -0,0 +1,81 @@ +/*************************************************************************** + kmymoneycombo_p.h - description + ------------------- + begin : Mon Mar 12 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. * + * * + ***************************************************************************/ + +#ifndef KMYMONEYCOMBO_P_H +#define KMYMONEYCOMBO_P_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.cpp b/kmymoney/widgets/kmymoneycompletion.cpp index 1baee18d6..34446c5db 100644 --- a/kmymoney/widgets/kmymoneycompletion.cpp +++ b/kmymoney/widgets/kmymoneycompletion.cpp @@ -1,316 +1,342 @@ /*************************************************************************** kmymoneycompletion.cpp - 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 + (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 "kmymoneycompletion.h" +#include "kmymoneycompletion_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include #include "kmymoneycombo.h" +#include "widgetenums.h" -const int kMyMoneyCompletion::MAX_ITEMS = 16; - -kMyMoneyCompletion::kMyMoneyCompletion(QWidget *parent) : - QWidget(parent) +KMyMoneyCompletion::KMyMoneyCompletion(QWidget *parent) : + QWidget(parent), + d_ptr(new KMyMoneyCompletionPrivate) { + Q_D(KMyMoneyCompletion); setWindowFlags(Qt::ToolTip); // make it look like the Qt completer QVBoxLayout *completionLayout = new QVBoxLayout(this); completionLayout->setContentsMargins(0, 0, 0, 0); completionLayout->setSpacing(0); - m_parent = parent; - m_selector = new KMyMoneySelector(this); - m_selector->listView()->setFocusProxy(parent); - completionLayout->addWidget(m_selector); + d->m_parent = parent; + d->m_selector = new KMyMoneySelector(this); + d->m_selector->listView()->setFocusProxy(parent); + completionLayout->addWidget(d->m_selector); // to handle the keyboard events received by this widget in the same way as // the keyboard events received by the other widgets installEventFilter(this); - connectSignals(m_selector, m_selector->listView()); + connectSignals(d->m_selector, d->m_selector->listView()); +} + +void KMyMoneyCompletion::connectSignals(QWidget* widget, QTreeWidget* lv) +{ + Q_D(KMyMoneyCompletion); + d->m_widget = widget; + d->m_lv = lv; + connect(lv, &QTreeWidget::itemActivated, this, &KMyMoneyCompletion::slotItemSelected); + connect(lv, &QTreeWidget::itemClicked, this, &KMyMoneyCompletion::slotItemSelected); } -void kMyMoneyCompletion::connectSignals(QWidget* widget, QTreeWidget* lv) +KMyMoneyCompletion::KMyMoneyCompletion(KMyMoneyCompletionPrivate &dd, QWidget* parent) : + QWidget(parent), + d_ptr(&dd) { - m_widget = widget; - m_lv = lv; - connect(lv, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*,int))); - connect(lv, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*,int))); } -kMyMoneyCompletion::~kMyMoneyCompletion() +KMyMoneyCompletion::~KMyMoneyCompletion() { + Q_D(KMyMoneyCompletion); + delete d; } -void kMyMoneyCompletion::adjustSize() +void KMyMoneyCompletion::adjustSize() { - QTreeWidgetItemIterator it(m_lv, QTreeWidgetItemIterator::NotHidden); + Q_D(KMyMoneyCompletion); + QTreeWidgetItemIterator it(d->m_lv, QTreeWidgetItemIterator::NotHidden); int count = 0; while (*it) { ++count; ++it; } adjustSize(count); } -void kMyMoneyCompletion::adjustSize(const int count) +void KMyMoneyCompletion::adjustSize(const int count) { - int w = m_widget->sizeHint().width(); - if (m_parent && w < m_parent->width()) - w = m_parent->width(); + Q_D(KMyMoneyCompletion); + int w = d->m_widget->sizeHint().width(); + if (d->m_parent && w < d->m_parent->width()) + w = d->m_parent->width(); const int minimumWidth = fontMetrics().width(QLatin1Char('W')) * 15; w = qMax(w, minimumWidth); int h = 0; - QTreeWidgetItemIterator it(m_lv, QTreeWidgetItemIterator::NotHidden); + QTreeWidgetItemIterator it(d->m_lv, QTreeWidgetItemIterator::NotHidden); QTreeWidgetItem* item = *it; if (item) // the +1 in the next statement avoids the display of a scroll bar if count < MAX_ITEMS. - h = item->treeWidget()->visualItemRect(item).height() * (count > MAX_ITEMS - 1 ? MAX_ITEMS : count + 1); + h = item->treeWidget()->visualItemRect(item).height() * (count > KMyMoneyCompletionPrivate::MAX_ITEMS - 1 ? KMyMoneyCompletionPrivate::MAX_ITEMS : count + 1); resize(w, h); - if (m_parent) { + if (d->m_parent) { // the code of this basic block is taken from KCompletionBox::show() // and modified to our local needs QRect screenSize = QApplication::desktop()->availableGeometry(parentWidget()); - QPoint orig = m_parent->mapToGlobal(QPoint(0, m_parent->height())); + QPoint orig = d->m_parent->mapToGlobal(QPoint(0, d->m_parent->height())); int x = orig.x(); int y = orig.y(); if (x + width() > screenSize.right()) x = screenSize.right() - width(); // check for the maximum height here to avoid flipping // of the completion box from top to bottom of the // edit widget. The offset (y) is certainly based // on the actual height. if (item) { - if ((y + item->treeWidget()->visualItemRect(item).height() * MAX_ITEMS) > screenSize.bottom()) - y = y - height() - m_parent->height(); + if ((y + item->treeWidget()->visualItemRect(item).height() * KMyMoneyCompletionPrivate::MAX_ITEMS) > screenSize.bottom()) + y = y - height() - d->m_parent->height(); } move(x, y); } } -void kMyMoneyCompletion::showEvent(QShowEvent* e) +void KMyMoneyCompletion::showEvent(QShowEvent* e) { show(true); QWidget::showEvent(e); } -void kMyMoneyCompletion::show(bool presetSelected) +void KMyMoneyCompletion::show(bool presetSelected) { - if (!m_id.isEmpty() && presetSelected) - m_selector->setSelected(m_id); + Q_D(KMyMoneyCompletion); + if (!d->m_id.isEmpty() && presetSelected) + d->m_selector->setSelected(d->m_id); adjustSize(); - if (m_parent) { - m_parent->installEventFilter(this); + if (d->m_parent) { + d->m_parent->installEventFilter(this); // make sure to install the filter for the combobox lineedit as well // We have do this here because QObject::installEventFilter() is not // declared virtual and we have no chance to override it in KMyMoneyCombo - KMyMoneyCombo* c = dynamic_cast(m_parent); + KMyMoneyCombo* c = dynamic_cast(d->m_parent); if (c && c->lineEdit()) { c->lineEdit()->installEventFilter(this); } } QWidget::show(); // make sure that the parent is the input context's focus widget instead of the selector's list //if (qApp->inputContext()->focusWidget() == m_selector->listView()) //qApp->inputContext()->setFocusWidget(m_parent); } -void kMyMoneyCompletion::hide() +void KMyMoneyCompletion::hide() { - if (m_parent) { - m_parent->removeEventFilter(this); + Q_D(KMyMoneyCompletion); + if (d->m_parent) { + d->m_parent->removeEventFilter(this); // make sure to uninstall the filter for the combobox lineedit as well // We have do this here because QObject::installEventFilter() is not // declared virtual and we have no chance to override it in KMyMoneyCombo - KMyMoneyCombo* c = dynamic_cast(m_parent); + KMyMoneyCombo* c = dynamic_cast(d->m_parent); if (c && c->lineEdit()) { c->lineEdit()->removeEventFilter(this); } } QWidget::hide(); } -bool kMyMoneyCompletion::eventFilter(QObject* o, QEvent* e) +bool KMyMoneyCompletion::eventFilter(QObject* o, QEvent* e) { - KMyMoneyCombo *c = dynamic_cast(m_parent); - if (o == m_parent || (c && o == c->lineEdit()) || o == this) { + Q_D(KMyMoneyCompletion); + KMyMoneyCombo *c = dynamic_cast(d->m_parent); + if (o == d->m_parent || (c && o == c->lineEdit()) || o == this) { if (isVisible()) { #ifdef Q_OS_WIN32 //krazy:exclude=cpp // hide the completer only if the focus was not lost because of windows activation or the activated window is not an application window if (e->type() == QEvent::FocusOut && (static_cast(e)->reason() != Qt::ActiveWindowFocusReason || QApplication::activeWindow() == 0)) { #else if (e->type() == QEvent::FocusOut) { #endif hide(); } if (e->type() == QEvent::KeyPress) { QTreeWidgetItem* item = 0; QKeyEvent* ev = static_cast(e); switch (ev->key()) { case Qt::Key_Tab: case Qt::Key_Backtab: - slotItemSelected(m_lv->currentItem(), 0); + slotItemSelected(d->m_lv->currentItem(), 0); break; case Qt::Key_Down: case Qt::Key_PageDown: - item = m_lv->currentItem(); + item = d->m_lv->currentItem(); while (item) { - item = m_lv->itemBelow(item); - if (item && selector()->match(m_lastCompletion, item)) + item = d->m_lv->itemBelow(item); + if (item && selector()->match(d->m_lastCompletion, item)) break; } if (item) { - m_lv->setCurrentItem(item); - m_lv->scrollToItem(item); + d->m_lv->setCurrentItem(item); + d->m_lv->scrollToItem(item); } ev->accept(); return true; case Qt::Key_Up: case Qt::Key_PageUp: - item = m_lv->currentItem(); + item = d->m_lv->currentItem(); while (item) { - item = m_lv->itemAbove(item); - if (item && selector()->match(m_lastCompletion, item)) + item = d->m_lv->itemAbove(item); + if (item && selector()->match(d->m_lastCompletion, item)) break; } if (item) { - m_lv->setCurrentItem(item); + d->m_lv->setCurrentItem(item); // make sure, we always see a possible (non-selectable) group item - if (m_lv->itemAbove(item)) - item = m_lv->itemAbove(item); - m_lv->scrollToItem(item); + if (d->m_lv->itemAbove(item)) + item = d->m_lv->itemAbove(item); + d->m_lv->scrollToItem(item); } ev->accept(); return true; case Qt::Key_Escape: hide(); ev->accept(); return true; case Qt::Key_Enter: case Qt::Key_Return: - slotItemSelected(m_lv->currentItem(), 0); + slotItemSelected(d->m_lv->currentItem(), 0); ev->accept(); return true; case Qt::Key_Home: case Qt::Key_End: if (ev->modifiers() & Qt::ControlModifier) { - item = m_lv->currentItem(); + item = d->m_lv->currentItem(); if (ev->key() == Qt::Key_Home) { - while (item && m_lv->itemAbove(item)) { - item = m_lv->itemAbove(item); + while (item && d->m_lv->itemAbove(item)) { + item = d->m_lv->itemAbove(item); } - while (item && !selector()->match(m_lastCompletion, item)) { - item = m_lv->itemBelow(item); + while (item && !selector()->match(d->m_lastCompletion, item)) { + item = d->m_lv->itemBelow(item); } } else { - while (item && m_lv->itemBelow(item)) { - item = m_lv->itemBelow(item); + while (item && d->m_lv->itemBelow(item)) { + item = d->m_lv->itemBelow(item); } - while (item && !selector()->match(m_lastCompletion, item)) { - item = m_lv->itemAbove(item); + while (item && !selector()->match(d->m_lastCompletion, item)) { + item = d->m_lv->itemAbove(item); } } if (item) { - m_lv->setCurrentItem(item); + d->m_lv->setCurrentItem(item); // make sure, we always see a possible (non-selectable) group item - if (m_lv->itemAbove(item)) - item = m_lv->itemAbove(item); - m_lv->scrollToItem(item); + if (d->m_lv->itemAbove(item)) + item = d->m_lv->itemAbove(item); + d->m_lv->scrollToItem(item); } ev->accept(); return true; } break; default: break; } } } } return QWidget::eventFilter(o, e); } -void kMyMoneyCompletion::slotMakeCompletion(const QString& txt) +void KMyMoneyCompletion::slotMakeCompletion(const QString& txt) { - int cnt = selector()->slotMakeCompletion(txt.trimmed()); + Q_D(KMyMoneyCompletion); + auto cnt = selector()->slotMakeCompletion(txt.trimmed()); - if (m_parent && m_parent->isVisible() && !isVisible() && cnt) + if (d->m_parent && d->m_parent->isVisible() && !isVisible() && cnt) show(false); else { if (cnt != 0) { adjustSize(); } else { hide(); } } } -void kMyMoneyCompletion::slotItemSelected(QTreeWidgetItem *item, int) +void KMyMoneyCompletion::slotItemSelected(QTreeWidgetItem *item, int) { + Q_D(KMyMoneyCompletion); if (item && item->flags().testFlag(Qt::ItemIsSelectable)) { - QString id = item->data(0, KMyMoneySelector::IdRole).toString(); + QString id = item->data(0, (int)eWidgets::Selector::Role::Id).toString(); // hide the widget, so we can debug the slots that are connect // to the signal we emit very soon hide(); - m_id = id; + d->m_id = id; emit itemSelected(id); } } -void kMyMoneyCompletion::setSelected(const QString& id) +void KMyMoneyCompletion::setSelected(const QString& id) +{ + Q_D(KMyMoneyCompletion); + d->m_id = id; + d->m_selector->setSelected(id, true); +} + +KMyMoneySelector* KMyMoneyCompletion::selector() const { - m_id = id; - m_selector->setSelected(id, true); + Q_D(const KMyMoneyCompletion); + return d->m_selector; } diff --git a/kmymoney/widgets/kmymoneycompletion.h b/kmymoney/widgets/kmymoneycompletion.h index d88cae575..a985913e9 100644 --- a/kmymoney/widgets/kmymoneycompletion.h +++ b/kmymoney/widgets/kmymoneycompletion.h @@ -1,122 +1,117 @@ /*************************************************************************** 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 + (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 KMYMONEYCOMPLETION_H #define KMYMONEYCOMPLETION_H // ---------------------------------------------------------------------------- // QT Includes #include -#include -class QTreeWidgetItem; -class QTreeWidget; // ---------------------------------------------------------------------------- // KDE Includes - // ---------------------------------------------------------------------------- // Project Includes +class QTreeWidgetItem; +class QTreeWidget; + class KMyMoneySelector; /** * @author Thomas Baumgart */ -class kMyMoneyCompletion : public QWidget +class KMyMoneyCompletionPrivate; +class KMyMoneyCompletion : public QWidget { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyCompletion) + public: - kMyMoneyCompletion(QWidget *parent = 0); - virtual ~kMyMoneyCompletion(); + explicit KMyMoneyCompletion(QWidget* parent = nullptr); + 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; - } + KMyMoneySelector* selector() const; 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 + * Reimplemented from KMyMoneyAccountSelector to get events from the viewport (to hide * this widget on mouse-click, Escape-presses, etc. */ - virtual bool eventFilter(QObject *, QEvent *); + bool eventFilter(QObject *, QEvent *) override; /** * Re-implemented for internal reasons. API is unaffected. */ - virtual void showEvent(QShowEvent*); + void showEvent(QShowEvent*) override; /** * 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; - + KMyMoneyCompletionPrivate * const d_ptr; + KMyMoneyCompletion(KMyMoneyCompletionPrivate &dd, QWidget* parent = nullptr); + Q_DECLARE_PRIVATE(KMyMoneyCompletion) }; #endif diff --git a/kmymoney/widgets/kmymoneyaccountcompletion.h b/kmymoney/widgets/kmymoneycompletion_p.h similarity index 67% copy from kmymoney/widgets/kmymoneyaccountcompletion.h copy to kmymoney/widgets/kmymoneycompletion_p.h index 0111affc8..a0a33fbdd 100644 --- a/kmymoney/widgets/kmymoneyaccountcompletion.h +++ b/kmymoney/widgets/kmymoneycompletion_p.h @@ -1,66 +1,60 @@ /*************************************************************************** - kmymoneyaccountcompletion.h - description + kmymoneycompletion_p.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 + (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 KMYMONEYACCOUNTCOMPLETION_H -#define KMYMONEYACCOUNTCOMPLETION_H +#ifndef KMYMONEYCOMPLETION_P_H +#define KMYMONEYCOMPLETION_P_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneyaccountselector.h" -#include "kmymoneycompletion.h" +class QWidget; +class QTreeWidget; +class KMyMoneySelector; -/** - * @author Thomas Baumgart - */ -class kMyMoneyAccountCompletion : public kMyMoneyCompletion +class KMyMoneyCompletionPrivate { - Q_OBJECT -public: - - kMyMoneyAccountCompletion(QWidget *parent = 0); - virtual ~kMyMoneyAccountCompletion(); + Q_DISABLE_COPY(KMyMoneyCompletionPrivate) - QStringList accountList(const QList& list = QList()) const { - return selector()->accountList(list); - } - - /** - * reimplemented from kMyMoneyCompletion - */ - kMyMoneyAccountSelector* selector() const { - return dynamic_cast(m_selector); +public: + KMyMoneyCompletionPrivate() + { } -public slots: - void slotMakeCompletion(const QString& txt); + QWidget* m_parent; + QWidget* m_widget; + QString m_id; + QTreeWidget* m_lv; + KMyMoneySelector* m_selector; + QRegExp m_lastCompletion; + static const int MAX_ITEMS = 16; }; #endif diff --git a/kmymoney/widgets/kmymoneycurrencyselector.cpp b/kmymoney/widgets/kmymoneycurrencyselector.cpp index 55327949b..bc71a814d 100644 --- a/kmymoney/widgets/kmymoneycurrencyselector.cpp +++ b/kmymoney/widgets/kmymoneycurrencyselector.cpp @@ -1,171 +1,215 @@ /*************************************************************************** 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 + (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 "kmymoneycurrencyselector.h" // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes - // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" +#include "mymoneysecurity.h" #include "icons/icons.h" using namespace Icons; -KMyMoneySecuritySelector::KMyMoneySecuritySelector(QWidget *parent) : - KComboBox(parent), +class KMyMoneySecuritySelectorPrivate +{ + Q_DISABLE_COPY(KMyMoneySecuritySelectorPrivate) + Q_DECLARE_PUBLIC(KMyMoneySecuritySelector) + +public: + enum displayItemE { + Symbol = 0, + FullName + }; + + enum displayTypeE { + TypeCurrencies = 0x01, + TypeSecurities = 0x02, + TypeAll = 0x03 + }; + + KMyMoneySecuritySelectorPrivate(KMyMoneySecuritySelector *qq): + q_ptr(qq), m_displayItem(FullName), m_selectedItemId(0), m_displayOnly(false), m_displayType(TypeAll) -{ - // update(QString()); -} + { + } -KMyMoneySecuritySelector::~KMyMoneySecuritySelector() -{ -} + void selectDisplayItem(displayItemE item) + { + Q_Q(KMyMoneySecuritySelector); + m_displayItem = item; + q->update(QString()); + } + + void setDisplayType(displayTypeE type) + { + m_displayType = type; + } + + KMyMoneySecuritySelector *q_ptr; + MyMoneySecurity m_currency; + displayItemE m_displayItem; + int m_selectedItemId; + bool m_displayOnly; + displayTypeE m_displayType; + QList m_list; +}; -void KMyMoneySecuritySelector::selectDisplayItem(KMyMoneySecuritySelector::displayItemE item) +KMyMoneySecuritySelector::KMyMoneySecuritySelector(QWidget *parent) : + KComboBox(parent), + d_ptr(new KMyMoneySecuritySelectorPrivate(this)) { - m_displayItem = item; - update(QString()); + // update(QString()); } -void KMyMoneySecuritySelector::setDisplayType(displayTypeE type) +KMyMoneySecuritySelector::~KMyMoneySecuritySelector() { - m_displayType = type; + Q_D(KMyMoneySecuritySelector); + delete d; } void KMyMoneySecuritySelector::update(const QString& id) { + Q_D(KMyMoneySecuritySelector); MyMoneySecurity curr = MyMoneyFile::instance()->baseCurrency(); QString baseCurrency = curr.id(); if (!id.isEmpty()) - curr = m_currency; + curr = d->m_currency; this->clear(); - m_list.clear(); - if (m_displayType & TypeCurrencies) - m_list += MyMoneyFile::instance()->currencyList(); - if (m_displayType & TypeSecurities) - m_list += MyMoneyFile::instance()->securityList(); + d->m_list.clear(); + if (d->m_displayType & KMyMoneySecuritySelectorPrivate::TypeCurrencies) + d->m_list += MyMoneyFile::instance()->currencyList(); + if (d->m_displayType & KMyMoneySecuritySelectorPrivate::TypeSecurities) + d->m_list += MyMoneyFile::instance()->securityList(); // sort - qSort(m_list); + qSort(d->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 = m_list.constBegin(); it != m_list.constEnd(); ++it) { + for (it = d->m_list.constBegin(); it != d->m_list.constEnd(); ++it) { QString display; - switch (m_displayItem) { + switch (d->m_displayItem) { default: - case FullName: + case KMyMoneySecuritySelectorPrivate::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: + case KMyMoneySecuritySelectorPrivate::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; - m_currency = (*it); + d->m_currency = (*it); } itemId++; } setCurrentIndex(m_selectedItemId); } const MyMoneySecurity& KMyMoneySecuritySelector::security() const { + Q_D(const KMyMoneySecuritySelector); int index = currentIndex(); - if ((0 <= index) && (index < m_list.size())) - return m_list[index]; + if ((0 <= index) && (index < d->m_list.size())) + return d->m_list[index]; else - return m_currency; + return d->m_currency; } void KMyMoneySecuritySelector::setSecurity(const MyMoneySecurity& currency) { - m_currency = currency; + Q_D(KMyMoneySecuritySelector); + d->m_currency = currency; update(QString("x")); } KMyMoneyCurrencySelector::KMyMoneyCurrencySelector(QWidget *parent) : - KMyMoneySecuritySelector(parent) + KMyMoneySecuritySelector(parent) +{ + Q_D(KMyMoneySecuritySelector); + d->setDisplayType(KMyMoneySecuritySelectorPrivate::TypeCurrencies); +} + +KMyMoneyCurrencySelector::~KMyMoneyCurrencySelector() { - setDisplayType(TypeCurrencies); } diff --git a/kmymoney/widgets/kmymoneycurrencyselector.h b/kmymoney/widgets/kmymoneycurrencyselector.h index 26e78601e..2ac2a4ac5 100644 --- a/kmymoney/widgets/kmymoneycurrencyselector.h +++ b/kmymoney/widgets/kmymoneycurrencyselector.h @@ -1,89 +1,74 @@ /*************************************************************************** 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 + (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 KMYMONEYCURRENCYSELECTOR_H #define KMYMONEYCURRENCYSELECTOR_H // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneysecurity.h" /** * @author Thomas Baumgart */ - +class KMyMoneySecuritySelectorPrivate; class KMyMoneySecuritySelector : public KComboBox { - Q_OBJECT + Q_OBJECT + Q_DISABLE_COPY(KMyMoneySecuritySelector) 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 = 0); +public: + explicit KMyMoneySecuritySelector(QWidget* parent = nullptr); 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); -private: - MyMoneySecurity m_currency; - displayItemE m_displayItem; - int m_selectedItemId; - bool m_displayOnly; - displayTypeE m_displayType; - QList m_list; +protected: + KMyMoneySecuritySelectorPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMyMoneySecuritySelector) }; class KMyMoneyCurrencySelector : public KMyMoneySecuritySelector { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyCurrencySelector) + public: - KMyMoneyCurrencySelector(QWidget *parent = 0); - virtual ~KMyMoneyCurrencySelector() {} + explicit KMyMoneyCurrencySelector(QWidget* parent = nullptr); + ~KMyMoneyCurrencySelector() override; }; #endif diff --git a/kmymoney/widgets/kmymoneydateedit.h b/kmymoney/widgets/kmymoneydateedit.h index b3789de7f..c47eb2697 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 = 0); + explicit KMyMoneyDateEdit(QWidget* parent = nullptr); }; #endif // KMYMONEYDATEEDIT_H diff --git a/kmymoney/widgets/kmymoneydateinput.cpp b/kmymoney/widgets/kmymoneydateinput.cpp index caaa4b65a..197b743bd 100644 --- a/kmymoney/widgets/kmymoneydateinput.cpp +++ b/kmymoney/widgets/kmymoneydateinput.cpp @@ -1,366 +1,367 @@ /*************************************************************************** kmymoneydateinput.cpp ------------------- copyright : (C) 2000 by Michael Edwardes email : mte@users.sourceforge.net 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 "kmymoneydateinput.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "icons/icons.h" using namespace Icons; namespace { const int DATE_POPUP_TIMEOUT = 1500; const QDate INVALID_DATE = QDate(1800, 1, 1); } void KMyMoney::OldDateEdit::keyPressEvent(QKeyEvent* k) { if ((lineEdit()->text().isEmpty() || lineEdit()->selectedText() == lineEdit()->text()) && QChar(k->key()).isDigit()) { // the line edit is empty which means that the date was cleared // or the whole text is selected and a digit character was entered // (the same meaning as clearing the date) - in this case set the date // to the current date and let the editor do the actual work setDate(QDate::currentDate()); setSelectedSection(QDateTimeEdit::DaySection); // start as when focused in if the date was cleared } QDateEdit::keyPressEvent(k); } void KMyMoney::OldDateEdit::focusInEvent(QFocusEvent * event) { QDateEdit::focusInEvent(event); setSelectedSection(QDateTimeEdit::DaySection); } bool KMyMoney::OldDateEdit::event(QEvent* e) { - // make sure that we keep the current date setting of a kMyMoneyDateInput object + // make sure that we keep the current date setting of a KMyMoneyDateInput object // across the QDateEdit::event(FocusOutEvent) bool rc; - kMyMoneyDateInput* p = dynamic_cast(parentWidget()); + KMyMoneyDateInput* p = dynamic_cast(parentWidget()); if (e->type() == QEvent::FocusOut && p) { QDate d = p->date(); rc = QDateEdit::event(e); if (d.isValid()) d = p->date(); p->loadDate(d); } else { rc = QDateEdit::event(e); } return rc; } bool KMyMoney::OldDateEdit::focusNextPrevChild(bool next) { Q_UNUSED(next) return true; } -struct kMyMoneyDateInput::Private { +struct KMyMoneyDateInput::Private { QDateEdit *m_dateEdit; KDatePicker *m_datePicker; QDate m_date; QDate m_prevDate; Qt::AlignmentFlag m_qtalignment; QWidget *m_dateFrame; QPushButton *m_dateButton; KPassivePopup *m_datePopup; }; -kMyMoneyDateInput::kMyMoneyDateInput(QWidget *parent, Qt::AlignmentFlag flags) +KMyMoneyDateInput::KMyMoneyDateInput(QWidget *parent, Qt::AlignmentFlag flags) : QWidget(parent), d(new Private) { d->m_qtalignment = flags; d->m_date = QDate::currentDate(); QHBoxLayout *dateInputLayout = new QHBoxLayout(this); dateInputLayout->setSpacing(0); dateInputLayout->setContentsMargins(0, 0, 0, 0); d->m_dateEdit = new KMyMoney::OldDateEdit(d->m_date, this); dateInputLayout->addWidget(d->m_dateEdit, 3); setFocusProxy(d->m_dateEdit); d->m_dateEdit->installEventFilter(this); // To get d->m_dateEdit's FocusIn/Out and some KeyPress events // we use INVALID_DATE as a special value for multi transaction editing d->m_dateEdit->setMinimumDate(INVALID_DATE); d->m_dateEdit->setSpecialValueText(QLatin1String(" ")); d->m_datePopup = new KPassivePopup(d->m_dateEdit); d->m_datePopup->setObjectName("datePopup"); d->m_datePopup->setTimeout(DATE_POPUP_TIMEOUT); d->m_datePopup->setView(new QLabel(QLocale().toString(d->m_date), d->m_datePopup)); d->m_dateFrame = new QWidget(this); dateInputLayout->addWidget(d->m_dateFrame); QVBoxLayout *dateFrameVBoxLayout = new QVBoxLayout(d->m_dateFrame); dateFrameVBoxLayout->setMargin(0); dateFrameVBoxLayout->setContentsMargins(0, 0, 0, 0); d->m_dateFrame->setWindowFlags(Qt::Popup); d->m_dateFrame->hide(); d->m_dateEdit->setDisplayFormat(QLocale().dateFormat(QLocale::ShortFormat)); d->m_datePicker = new KDatePicker(d->m_date, d->m_dateFrame); dateFrameVBoxLayout->addWidget(d->m_datePicker); // Let the date picker have a close button (Added in 3.1) d->m_datePicker->setCloseButton(true); // the next line is a try to add an icon to the button d->m_dateButton = new QPushButton(QIcon::fromTheme(g_Icons[Icon::ViewCalendarDay]), QString(), this); dateInputLayout->addWidget(d->m_dateButton); - connect(d->m_dateButton, SIGNAL(clicked()), SLOT(toggleDatePicker())); - connect(d->m_dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(slotDateChosenRef(QDate))); - connect(d->m_datePicker, SIGNAL(dateSelected(QDate)), this, SLOT(slotDateChosen(QDate))); - connect(d->m_datePicker, SIGNAL(dateEntered(QDate)), this, SLOT(slotDateChosen(QDate))); - connect(d->m_datePicker, SIGNAL(dateSelected(QDate)), d->m_dateFrame, SLOT(hide())); + connect(d->m_dateButton, &QAbstractButton::clicked, this, &KMyMoneyDateInput::toggleDatePicker); + connect(d->m_dateEdit, &QDateTimeEdit::dateChanged, this, &KMyMoneyDateInput::slotDateChosenRef); + connect(d->m_datePicker, &KDatePicker::dateSelected, this, &KMyMoneyDateInput::slotDateChosen); + connect(d->m_datePicker, &KDatePicker::dateEntered, this, &KMyMoneyDateInput::slotDateChosen); + connect(d->m_datePicker, &KDatePicker::dateSelected, d->m_dateFrame, &QWidget::hide); } -void kMyMoneyDateInput::markAsBadDate(bool bad, const QColor& color) +void KMyMoneyDateInput::markAsBadDate(bool bad, const QColor& color) { // the next line knows a bit about the internals of QAbstractSpinBox QLineEdit* le = d->m_dateEdit->findChild(); //krazy:exclude=qclasses if (le) { QPalette palette = this->palette(); le->setPalette(palette); if (bad) { palette.setColor(foregroundRole(), color); le->setPalette(palette); } } } -void kMyMoneyDateInput::showEvent(QShowEvent* event) +void KMyMoneyDateInput::showEvent(QShowEvent* event) { // don't forget the standard behaviour ;-) QWidget::showEvent(event); // If the widget is shown, the size must be fixed a little later // to be appropriate. I saw this in some other places and the only // way to solve this problem is to postpone the setup of the size // to the time when the widget is on the screen. QTimer::singleShot(50, this, SLOT(fixSize())); } -void kMyMoneyDateInput::fixSize() +void KMyMoneyDateInput::fixSize() { // According to a hint in the documentation of KDatePicker::sizeHint() // 28 pixels should be added in each direction to obtain a better // display of the month button. I decided, (22,14) is good // enough and save some space on the screen (ipwizard) d->m_dateFrame->setFixedSize(d->m_datePicker->sizeHint() + QSize(22, 14)); } -kMyMoneyDateInput::~kMyMoneyDateInput() +KMyMoneyDateInput::~KMyMoneyDateInput() { delete d->m_dateFrame; delete d->m_datePopup; delete d; } -void kMyMoneyDateInput::toggleDatePicker() +void KMyMoneyDateInput::toggleDatePicker() { int w = d->m_dateFrame->width(); int h = d->m_dateFrame->height(); if (d->m_dateFrame->isVisible()) { d->m_dateFrame->hide(); } else { QPoint tmpPoint = mapToGlobal(d->m_dateButton->geometry().bottomRight()); // usually, the datepicker widget is shown underneath the d->m_dateEdit widget // if it does not fit on the screen, we show it above this widget if (tmpPoint.y() + h > QApplication::desktop()->height()) { tmpPoint.setY(tmpPoint.y() - h - d->m_dateButton->height()); } if ((d->m_qtalignment == Qt::AlignRight && tmpPoint.x() + w <= QApplication::desktop()->width()) || (tmpPoint.x() - w < 0)) { d->m_dateFrame->setGeometry(tmpPoint.x() - width(), tmpPoint.y(), w, h); } else { tmpPoint.setX(tmpPoint.x() - w); d->m_dateFrame->setGeometry(tmpPoint.x(), tmpPoint.y(), w, h); } if (d->m_date.isValid() && d->m_date != INVALID_DATE) { d->m_datePicker->setDate(d->m_date); } else { d->m_datePicker->setDate(QDate::currentDate()); } d->m_dateFrame->show(); } } /** Overriding QWidget::keyPressEvent * * increments/decrements the date upon +/- or Up/Down key input * sets the date to current date when the 'T' key is pressed */ -void kMyMoneyDateInput::keyPressEvent(QKeyEvent * k) +void KMyMoneyDateInput::keyPressEvent(QKeyEvent * k) { QKeySequence today(i18nc("Enter todays date into date input widget", "T")); switch (k->key()) { case Qt::Key_Equal: case Qt::Key_Plus: slotDateChosen(d->m_date.addDays(1)); k->accept(); break; case Qt::Key_Minus: slotDateChosen(d->m_date.addDays(-1)); k->accept(); break; default: if (today == QKeySequence(k->key()) || k->key() == Qt::Key_T) { slotDateChosen(QDate::currentDate()); k->accept(); } break; } k->ignore(); // signal that the key event was not handled } /** * This function receives all events that are sent to focusWidget(). * Some KeyPress events are intercepted and passed to keyPressEvent. * Otherwise they would be consumed by QDateEdit. */ -bool kMyMoneyDateInput::eventFilter(QObject *, QEvent *e) +bool KMyMoneyDateInput::eventFilter(QObject *, QEvent *e) { if (e->type() == QEvent::FocusIn) { #ifndef Q_OS_MAC d->m_datePopup->show(mapToGlobal(QPoint(0, height()))); #endif // select the date section, but we need to delay it a bit } else if (e->type() == QEvent::FocusOut) { #ifndef Q_OS_MAC d->m_datePopup->hide(); #endif } else if (e->type() == QEvent::KeyPress) { if (QKeyEvent *k = dynamic_cast(e)) { keyPressEvent(k); if (k->isAccepted()) return true; // signal that the key event was handled } } return false; // Don't filter the event } -void kMyMoneyDateInput::slotDateChosenRef(const QDate& date) +void KMyMoneyDateInput::slotDateChosenRef(const QDate& date) { if (date.isValid()) { emit dateChanged(date); d->m_date = date; #ifndef Q_OS_MAC QLabel *lbl = static_cast(d->m_datePopup->view()); lbl->setText(QLocale().toString(date)); lbl->adjustSize(); if (d->m_datePopup->isVisible() || hasFocus()) d->m_datePopup->show(mapToGlobal(QPoint(0, height()))); // Repaint #endif } } -void kMyMoneyDateInput::slotDateChosen(QDate date) +void KMyMoneyDateInput::slotDateChosen(QDate date) { if (date.isValid()) { // the next line implies a call to slotDateChosenRef() above d->m_dateEdit->setDate(date); } else { d->m_dateEdit->setDate(INVALID_DATE); } } -QDate kMyMoneyDateInput::date() const +QDate KMyMoneyDateInput::date() const { QDate rc = d->m_dateEdit->date(); if (rc == INVALID_DATE) rc = QDate(); return rc; } -void kMyMoneyDateInput::setDate(QDate date) +void KMyMoneyDateInput::setDate(QDate date) { slotDateChosen(date); } -void kMyMoneyDateInput::loadDate(const QDate& date) +void KMyMoneyDateInput::loadDate(const QDate& date) { d->m_date = d->m_prevDate = date; blockSignals(true); slotDateChosen(date); blockSignals(false); } -void kMyMoneyDateInput::resetDate() +void KMyMoneyDateInput::resetDate() { setDate(d->m_prevDate); } -void kMyMoneyDateInput::setMaximumDate(const QDate& max) +void KMyMoneyDateInput::setMaximumDate(const QDate& max) { d->m_dateEdit->setMaximumDate(max); } -QWidget* kMyMoneyDateInput::focusWidget() const +QWidget* KMyMoneyDateInput::focusWidget() const { QWidget* w = d->m_dateEdit; while (w->focusProxy()) w = w->focusProxy(); return w; } /* -void kMyMoneyDateInput::setRange(const QDate & min, const QDate & max) +void KMyMoneyDateInput::setRange(const QDate & min, const QDate & max) { d->m_dateEdit->setDateRange(min, max); } */ diff --git a/kmymoney/widgets/kmymoneydateinput.h b/kmymoney/widgets/kmymoneydateinput.h index 0e815c346..254a78806 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 = 0) : QDateEdit(date, parent) {} + explicit OldDateEdit(const QDate& date, QWidget* parent = nullptr) : 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 +class KMM_WIDGETS_EXPORT KMyMoneyDateInput : public QWidget { Q_OBJECT Q_PROPERTY(QDate date READ date WRITE setDate STORED false) public: - explicit kMyMoneyDateInput(QWidget *parent = 0, Qt::AlignmentFlag flags = Qt::AlignLeft); - ~kMyMoneyDateInput(); + explicit KMyMoneyDateInput(QWidget* parent = nullptr, 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); - void showEvent(QShowEvent* event); + void keyPressEvent(QKeyEvent* k) override; + void showEvent(QShowEvent* event) override; /** To intercept events sent to focusWidget() */ - bool eventFilter(QObject *o, QEvent *e); + bool eventFilter(QObject *o, QEvent *e) override; 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 f46a623ed..44d715fdd 100644 --- a/kmymoney/widgets/kmymoneyedit.cpp +++ b/kmymoney/widgets/kmymoneyedit.cpp @@ -1,588 +1,694 @@ /*************************************************************************** kmymoneyedit.cpp ------------------- copyright : (C) 2000 by Michael Edwardes 2004 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 "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) +KMyMoneyMoneyValidator::KMyMoneyMoneyValidator(QObject * parent) : + QDoubleValidator(parent) { setLocale(QLocale::c()); } -kMyMoneyMoneyValidator::kMyMoneyMoneyValidator(double bottom, double top, int decimals, - QObject * parent) : - QDoubleValidator(bottom, top, decimals, parent) +KMyMoneyMoneyValidator::KMyMoneyMoneyValidator(double bottom, double top, int decimals, + 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 +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 }; -void kMyMoneyEdit::init() +class KMyMoneyEditPrivate { - QHBoxLayout *editLayout = new QHBoxLayout(this); - editLayout->setSpacing(0); - editLayout->setContentsMargins(0, 0, 0, 0); + Q_DISABLE_COPY(KMyMoneyEditPrivate) + Q_DECLARE_PUBLIC(KMyMoneyEdit) - allowEmpty = false; - m_edit = new kMyMoneyLineEdit(this, true); - m_edit->installEventFilter(this); - setFocusProxy(m_edit); - editLayout->addWidget(m_edit); +public: + KMyMoneyEditPrivate(KMyMoneyEdit *qq) : + q_ptr(qq) + { + } - // Yes, just a simple double validator ! - kMyMoneyMoneyValidator *validator = new kMyMoneyMoneyValidator(this); - m_edit->setValidator(validator); - m_edit->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + ~KMyMoneyEditPrivate() + { + } - m_calculatorFrame = new QWidget; - QVBoxLayout *calculatorFrameVBoxLayout = new QVBoxLayout(m_calculatorFrame); - calculatorFrameVBoxLayout->setMargin(0); - m_calculatorFrame->setWindowFlags(Qt::Popup); + 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(); - m_calculator = new kMyMoneyCalculator(m_calculatorFrame); - calculatorFrameVBoxLayout->addWidget(m_calculator); - m_calculatorFrame->hide(); + 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, &QLineEdit::textChanged, q, &KMyMoneyEdit::theTextChanged); + q->connect(m_calculator, &KMyMoneyCalculator::signalResultAvailable, q, &KMyMoneyEdit::slotCalculatorResult); + q->connect(m_calcButton, &QAbstractButton::clicked, q, &KMyMoneyEdit::slotCalculatorOpen); + q->connect(m_resetButton, &QAbstractButton::clicked, q, &KMyMoneyEdit::resetText); + } - m_calcButton = new QPushButton(QIcon::fromTheme(g_Icons[Icon::AccessoriesCalculator]), QString(), this); - m_calcButton->setFocusProxy(m_edit); - editLayout->addWidget(m_calcButton); + /** + * 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); + } - 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); + /** + * 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); + } + } + } - KSharedConfigPtr kconfig = KSharedConfig::openConfig(); - KConfigGroup grp = kconfig->group("General Options"); - if (grp.readEntry("DontShowCalculatorButton", false) == true) - setCalculatorButtonVisible(false); + /** + * 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(); + } + + 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; +}; - 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())); +int KMyMoneyEditPrivate::standardPrecision = 2; + +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(); } -void kMyMoneyEdit::setValidator(const QValidator* v) +KMyMoneyEdit::KMyMoneyEdit(const MyMoneySecurity& sec, QWidget *parent) : + QWidget(parent), + d_ptr(new KMyMoneyEditPrivate(this)) { - m_edit->setValidator(v); + Q_D(KMyMoneyEdit); + d->m_prec = MyMoneyMoney::denomToPrec(sec.smallestAccountFraction()); + d->init(); } -kMyMoneyEdit::~kMyMoneyEdit() +void KMyMoneyEdit::setStandardPrecision(int prec) { + if (prec >= 0 && prec < 20) { + KMyMoneyEditPrivate::standardPrecision = prec; + } } -KLineEdit* kMyMoneyEdit::lineedit() const +void KMyMoneyEdit::setValidator(const QValidator* v) { - return m_edit; + Q_D(KMyMoneyEdit); + d->m_edit->setValidator(v); } -void kMyMoneyEdit::setPrecision(const int prec) +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); +} + +void KMyMoneyEdit::setPrecision(const int prec) +{ + Q_D(KMyMoneyEdit); if (prec >= -1 && prec <= 20) { - if (prec != m_prec) { - m_prec = prec; + if (prec != d->m_prec) { + d->m_prec = prec; // update current display setValue(value()); } } } -bool kMyMoneyEdit::isValid() const +int KMyMoneyEdit::precision() const { - return !(m_edit->text().isEmpty()); + Q_D(const KMyMoneyEdit); + return d->m_prec; } -MyMoneyMoney kMyMoneyEdit::value() const +bool KMyMoneyEdit::isValid() const { - QString txt = m_edit->text(); - ensureFractionalPart(txt); + Q_D(const KMyMoneyEdit); + return !(d->m_edit->text().isEmpty()); +} + +MyMoneyMoney KMyMoneyEdit::value() const +{ + Q_D(const KMyMoneyEdit); + auto txt = d->m_edit->text(); + d->ensureFractionalPart(txt); MyMoneyMoney money(txt); - if (m_prec != -1) - money = money.convert(MyMoneyMoney::precToDenom(m_prec)); + if (d->m_prec != -1) + money = money.convert(MyMoneyMoney::precToDenom(d->m_prec)); return money; } -void kMyMoneyEdit::setValue(const MyMoneyMoney& value) +void KMyMoneyEdit::setValue(const MyMoneyMoney& value) { + Q_D(KMyMoneyEdit); // load the value into the widget but don't use thousandsSeparators - QString txt = value.formatMoney("", m_prec, false); + auto txt = value.formatMoney(QString(), d->m_prec, false); loadText(txt); } -void kMyMoneyEdit::loadText(const QString& txt) +void KMyMoneyEdit::loadText(const QString& txt) { - m_edit->setText(txt); + Q_D(KMyMoneyEdit); + d->m_edit->setText(txt); if (isEnabled() && !txt.isEmpty()) - ensureFractionalPart(); - m_text = m_edit->text(); - m_resetButton->setEnabled(false); + d->ensureFractionalPart(); + d->m_text = d->m_edit->text(); + d->m_resetButton->setEnabled(false); } -void kMyMoneyEdit::clearText() +void KMyMoneyEdit::clearText() { - m_text.clear(); - m_edit->setText(m_text); + Q_D(KMyMoneyEdit); + d->m_text.clear(); + d->m_edit->setText(d->m_text); } -void kMyMoneyEdit::resetText() +void KMyMoneyEdit::setText(const QString& txt) { - m_edit->setText(m_text); - m_resetButton->setEnabled(false); + setValue(MyMoneyMoney(txt)); } -void kMyMoneyEdit::theTextChanged(const QString & theText) +void KMyMoneyEdit::resetText() { - QString d = QLocale().decimalPoint(); + Q_D(KMyMoneyEdit); + d->m_edit->setText(d->m_text); + d->m_resetButton->setEnabled(false); +} + +void KMyMoneyEdit::theTextChanged(const QString & theText) +{ + Q_D(KMyMoneyEdit); + QString txt = 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 = ""; + psign = QString(); #endif - int i = 0; + auto i = 0; if (isEnabled()) { - QValidator::State state = m_edit->validator()->validate(l_text, i); + QValidator::State state = d->m_edit->validator()->validate(l_text, i); if (state == QValidator::Intermediate) { if (l_text.length() == 1) { - if (l_text != d && l_text != nsign && l_text != psign) + if (l_text != txt && l_text != nsign && l_text != psign) state = QValidator::Invalid; } } if (state == QValidator::Invalid) - m_edit->setText(previousText); + d->m_edit->setText(d->previousText); else { - previousText = l_text; - emit textChanged(m_edit->text()); - m_resetButton->setEnabled(true); - } - } -} - -void kMyMoneyEdit::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); -} - -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); + d->previousText = l_text; + emit textChanged(d->m_edit->text()); + d->m_resetButton->setEnabled(true); } } } -bool kMyMoneyEdit::eventFilter(QObject * /* o */ , QEvent *e) +bool KMyMoneyEdit::eventFilter(QObject * /* o */ , QEvent *event) { - bool rc = false; + Q_D(KMyMoneyEdit); + auto rc = false; // we want to catch some keys that are usually handled by // the base class (e.g. '+', '-', etc.) - if (e->type() == QEvent::KeyPress) { - QKeyEvent *k = static_cast(e); + if (event->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast(event); rc = true; switch (k->key()) { case Qt::Key_Plus: case Qt::Key_Minus: - if (m_edit->hasSelectedText()) { - m_edit->cut(); + if (d->m_edit->hasSelectedText()) { + d->m_edit->cut(); } - if (m_edit->text().length() == 0) { + if (d->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 (m_edit->cursorPosition() == 0 && m_edit->text()[0] != '-') { + if (d->m_edit->cursorPosition() == 0 && d->m_edit->text()[0] != '-') { rc = false; break; } } // intentional fall through case Qt::Key_Slash: case Qt::Key_Asterisk: case Qt::Key_Percent: - if (m_edit->hasSelectedText()) { + if (d->m_edit->hasSelectedText()) { // remove the selected text - m_edit->cut(); + d->m_edit->cut(); } - calculatorOpen(k); + d->calculatorOpen(k); break; default: rc = false; break; } - } else if (e->type() == QEvent::FocusOut) { - if (!m_edit->text().isEmpty() || !allowEmpty) - ensureFractionalPart(); + } else if (event->type() == QEvent::FocusOut) { + if (!d->m_edit->text().isEmpty() || !d->allowEmpty) + d->ensureFractionalPart(); - if (MyMoneyMoney(m_edit->text()) != MyMoneyMoney(m_text) - && !m_calculator->isVisible()) { - emit valueChanged(m_edit->text()); + if (MyMoneyMoney(d->m_edit->text()) != MyMoneyMoney(d->m_text) + && !d->m_calculator->isVisible()) { + emit valueChanged(d->m_edit->text()); } - m_text = m_edit->text(); + d->m_text = d->m_edit->text(); } return rc; } -void kMyMoneyEdit::slotCalculatorOpen() +void KMyMoneyEdit::slotCalculatorOpen() { - calculatorOpen(0); + Q_D(KMyMoneyEdit); + d->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() +void KMyMoneyEdit::slotCalculatorResult() { + Q_D(KMyMoneyEdit); QString result; - if (m_calculator != 0) { - m_calculatorFrame->hide(); - m_edit->setText(m_calculator->result()); - ensureFractionalPart(); - emit valueChanged(m_edit->text()); - m_text = m_edit->text(); + 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(); } } -QWidget* kMyMoneyEdit::focusWidget() const +QWidget* KMyMoneyEdit::focusWidget() const { - QWidget* w = m_edit; + Q_D(const KMyMoneyEdit); + QWidget* w = d->m_edit; while (w->focusProxy()) w = w->focusProxy(); return w; } -void kMyMoneyEdit::setCalculatorButtonVisible(const bool show) +void KMyMoneyEdit::setCalculatorButtonVisible(const bool show) { - m_calcButton->setVisible(show); + Q_D(KMyMoneyEdit); + d->m_calcButton->setVisible(show); } -void kMyMoneyEdit::setResetButtonVisible(const bool show) +void KMyMoneyEdit::setResetButtonVisible(const bool show) { - m_resetButton->setVisible(show); + Q_D(KMyMoneyEdit); + d->m_resetButton->setVisible(show); } -void kMyMoneyEdit::setAllowEmpty(bool allowed) +void KMyMoneyEdit::setAllowEmpty(bool allowed) { - allowEmpty = allowed; + Q_D(KMyMoneyEdit); + d->allowEmpty = allowed; } -bool kMyMoneyEdit::isCalculatorButtonVisible() const +bool KMyMoneyEdit::isCalculatorButtonVisible() const { - return m_calcButton->isVisible(); + Q_D(const KMyMoneyEdit); + return d->m_calcButton->isVisible(); } -bool kMyMoneyEdit::isResetButtonVisible() const +bool KMyMoneyEdit::isResetButtonVisible() const { - return m_resetButton->isVisible(); + Q_D(const KMyMoneyEdit); + return d->m_resetButton->isVisible(); } -bool kMyMoneyEdit::isEmptyAllowed() const +bool KMyMoneyEdit::isEmptyAllowed() const { - return allowEmpty; + Q_D(const KMyMoneyEdit); + return d->allowEmpty; } -void kMyMoneyEdit::setPlaceholderText(const QString& hint) const +void KMyMoneyEdit::setPlaceholderText(const QString& hint) const { - if (m_edit) - m_edit->setPlaceholderText(hint); + Q_D(const KMyMoneyEdit); + if (d->m_edit) + d->m_edit->setPlaceholderText(hint); } -bool kMyMoneyEdit::isReadOnly() const +bool KMyMoneyEdit::isReadOnly() const { - if (m_edit) - return m_edit->isReadOnly(); + Q_D(const KMyMoneyEdit); + if (d->m_edit) + return d->m_edit->isReadOnly(); return false; } -void kMyMoneyEdit::setReadOnly(bool readOnly) +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 (m_edit) - m_edit->QLineEdit::setReadOnly(readOnly); //krazy:exclude=qclasses + if (d->m_edit) + d->m_edit->QLineEdit::setReadOnly(readOnly); //krazy:exclude=qclasses } diff --git a/kmymoney/widgets/kmymoneyedit.h b/kmymoney/widgets/kmymoneyedit.h index 37a585ff1..19efa26c6 100644 --- a/kmymoney/widgets/kmymoneyedit.h +++ b/kmymoney/widgets/kmymoneyedit.h @@ -1,257 +1,205 @@ /*************************************************************************** kmymoneyedit.h ------------------- copyright : (C) 2000 by Michael Edwardes + (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 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 +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 */ - kMyMoneyMoneyValidator(QObject * parent); + explicit KMyMoneyMoneyValidator(QObject * parent); /** * Constuct a locale-aware KDoubleValidator for range [@p bottom,@p * top] and a precision of @p digits after the decimal * point. */ - kMyMoneyMoneyValidator(double bottom, double top, int decimals, - QObject * parent); + explicit KMyMoneyMoneyValidator(double bottom, double top, int decimals, + QObject * parent); /** * Destructs the validator. */ - virtual ~kMyMoneyMoneyValidator() {} + ~KMyMoneyMoneyValidator(); /** Overloaded for internal reasons. The API is not affected. */ - virtual QValidator::State validate(QString & input, int & pos) const; + QValidator::State validate(QString & input, int & pos) const override; }; /** * 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 KMM_WIDGETS_EXPORT kMyMoneyEdit : public QWidget +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 = 0, const int prec = -2); - explicit kMyMoneyEdit(const MyMoneySecurity& eq, QWidget *parent = 0); - ~kMyMoneyEdit(); + explicit KMyMoneyEdit(QWidget* parent = nullptr, const int prec = -2); + explicit KMyMoneyEdit(const MyMoneySecurity& eq, QWidget* parent = nullptr); + ~KMyMoneyEdit(); MyMoneyMoney value() const; void setValue(const MyMoneyMoney& value); bool isValid() const; - virtual bool eventFilter(QObject * , QEvent *); + virtual bool eventFilter(QObject *watched, QEvent *event) override; /** * 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 { - return value().toString(); - }; - - void setMinimumWidth(int w) { - m_edit->setMinimumWidth(w); - }; + QString text() const; + void setMinimumWidth(int 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() { - return m_prec; - }; + int precision() const; 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) { - setValue(MyMoneyMoney(txt)); - }; + 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); 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 new file mode 100644 index 000000000..230b678f8 --- /dev/null +++ b/kmymoney/widgets/kmymoneyfrequencycombo.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + 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, static_cast(&QComboBox::currentIndexChanged), this, &KMyMoneyFrequencyCombo::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 new file mode 100644 index 000000000..5ad74a3d6 --- /dev/null +++ b/kmymoney/widgets/kmymoneyfrequencycombo.h @@ -0,0 +1,78 @@ +/*************************************************************************** + 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 new file mode 100644 index 000000000..865e592f5 --- /dev/null +++ b/kmymoney/widgets/kmymoneygeneralcombo.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + 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, static_cast(&QComboBox::highlighted), this, &KMyMoneyGeneralCombo::slotChangeItem); +} + +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 new file mode 100644 index 000000000..f07168665 --- /dev/null +++ b/kmymoney/widgets/kmymoneygeneralcombo.h @@ -0,0 +1,69 @@ +/*************************************************************************** + 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/kmymoneylineedit.cpp b/kmymoney/widgets/kmymoneylineedit.cpp index 8e901f8b0..c9841dbc5 100644 --- a/kmymoney/widgets/kmymoneylineedit.cpp +++ b/kmymoney/widgets/kmymoneylineedit.cpp @@ -1,164 +1,164 @@ /*************************************************************************** kmymoneylineedit.cpp - description ------------------- begin : Wed May 9 2001 copyright : (C) 2001 by Michael Edwardes Javier Campos Morales Felix Rodriguez ***************************************************************************/ /*************************************************************************** * * * 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 "kmymoneylineedit.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -class kMyMoneyLineEdit::Private +class KMyMoneyLineEdit::Private { public: /** * This member keeps the initial value. It is used during * resetText() to set the widgets text back to this initial value * and as comparison during focusOutEvent() to emit the lineChanged * signal if the current text is different. */ QString m_text; /** * This member keeps the status if overriding the numeric keypad comma key * is requested or not. */ bool m_forceMonetaryDecimalSymbol; bool skipSelectAll; }; -kMyMoneyLineEdit::kMyMoneyLineEdit(QWidget *w, bool forceMonetaryDecimalSymbol, Qt::Alignment alignment) : +KMyMoneyLineEdit::KMyMoneyLineEdit(QWidget *w, bool forceMonetaryDecimalSymbol, Qt::Alignment alignment) : KLineEdit(w), d(new Private) { d->m_forceMonetaryDecimalSymbol = forceMonetaryDecimalSymbol; setAlignment(alignment); skipSelectAll(false); } -kMyMoneyLineEdit::~kMyMoneyLineEdit() +KMyMoneyLineEdit::~KMyMoneyLineEdit() { delete d; } -void kMyMoneyLineEdit::skipSelectAll(bool skipIt) +void KMyMoneyLineEdit::skipSelectAll(bool skipIt) { d->skipSelectAll = skipIt; } -void kMyMoneyLineEdit::resetText() +void KMyMoneyLineEdit::resetText() { setText(d->m_text); } -void kMyMoneyLineEdit::loadText(const QString& text) +void KMyMoneyLineEdit::loadText(const QString& text) { d->m_text = text; setText(text); } -void kMyMoneyLineEdit::focusOutEvent(QFocusEvent *ev) +void KMyMoneyLineEdit::focusOutEvent(QFocusEvent *ev) { // if the current text is not in the list of // possible completions, we have a new payee // and signal that to the outside world. if (text() != d->m_text) { emit lineChanged(text()); } KLineEdit::focusOutEvent(ev); // force update of hint if (text().isEmpty()) repaint(); } -void kMyMoneyLineEdit::focusInEvent(QFocusEvent *ev) +void KMyMoneyLineEdit::focusInEvent(QFocusEvent *ev) { KLineEdit::focusInEvent(ev); // select the text so it can be edited by the user - only if the widget // is not focused after a popup is closed (which could be the completer // of the KMyMoneyCombo). // // Delay that selection until the application is idle to prevent a // recursive loop which otherwise entered when the focus is set to this // widget using the mouse. (bko #259369) if (ev->reason() != Qt::PopupFocusReason && ev->reason() != Qt::ActiveWindowFocusReason) { if (!d->skipSelectAll) QTimer::singleShot(0, this, SLOT(selectAll())); d->skipSelectAll = false; } } -void kMyMoneyLineEdit::keyReleaseEvent(QKeyEvent* k) +void KMyMoneyLineEdit::keyReleaseEvent(QKeyEvent* k) { if (d->m_forceMonetaryDecimalSymbol) { if (k->modifiers() & Qt::KeypadModifier) { if (k->key() == Qt::Key_Comma || k->key() == Qt::Key_Period) { if (QLocale().decimalPoint() == QLatin1Char(',')) { QKeyEvent newk(k->type(), Qt::Key_Comma, k->modifiers(), ",", k->isAutoRepeat(), k->count()); KLineEdit::keyReleaseEvent(&newk); k->accept(); return; } if (QLocale().decimalPoint() == QLatin1Char('.')) { QKeyEvent newk(k->type(), Qt::Key_Comma, k->modifiers(), ".", k->isAutoRepeat(), k->count()); KLineEdit::keyReleaseEvent(&newk); k->accept(); return; } } } } KLineEdit::keyReleaseEvent(k); } -void kMyMoneyLineEdit::keyPressEvent(QKeyEvent* k) +void KMyMoneyLineEdit::keyPressEvent(QKeyEvent* k) { if (d->m_forceMonetaryDecimalSymbol) { if (k->modifiers() & Qt::KeypadModifier) { if (k->key() == Qt::Key_Comma || k->key() == Qt::Key_Period) { if (QLocale().decimalPoint() == QLatin1Char(',')) { QKeyEvent newk(k->type(), Qt::Key_Comma, k->modifiers(), ",", k->isAutoRepeat(), k->count()); KLineEdit::keyPressEvent(&newk); k->accept(); return; } if (QLocale().decimalPoint() == QLatin1Char('.')) { QKeyEvent newk(k->type(), Qt::Key_Period, k->modifiers(), ".", k->isAutoRepeat(), k->count()); KLineEdit::keyPressEvent(&newk); k->accept(); return; } } } } KLineEdit::keyPressEvent(k); } diff --git a/kmymoney/widgets/kmymoneylineedit.h b/kmymoney/widgets/kmymoneylineedit.h index c7f9576a5..ea7d1e6af 100644 --- a/kmymoney/widgets/kmymoneylineedit.h +++ b/kmymoney/widgets/kmymoneylineedit.h @@ -1,122 +1,122 @@ /*************************************************************************** kmymoneylineedit.h - description ------------------- begin : Wed May 9 2001 copyright : (C) 2001 by Michael Edwardes 2006 by Thomas Baumgart Javier Campos Morales Felix Rodriguez ****************************************************************************/ /*************************************************************************** * * * 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 KMYMONEYLINEEDIT_H #define KMYMONEYLINEEDIT_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "kmm_widgets_export.h" /** * This class represents a special verson of a KLineEdit object that * supports the display of a hint if the display area is empty. It also * overrides the comma key on the numeric keypad with the currently * selected monetaryDecimalSymbol if selected during creation of the object. * * @author Michael Edwardes * @author Thomas Baumgart */ class QFocusEvent; class QKeyEvent; -class KMM_WIDGETS_EXPORT kMyMoneyLineEdit : public KLineEdit +class KMM_WIDGETS_EXPORT KMyMoneyLineEdit : public KLineEdit { Q_OBJECT public: /** * @param w pointer to parent * @param forceMonetaryDecimalSymbol if @a true, the numeric keypad comma key will have a fixed * value and does not follow the keyboard layout (default: @p false) * @param alignment Controls the alignment of the text. Default is Qt::AlignLeft | Qt::AlignVCenter. * See Qt::AlignmentFlags for other possible values. */ - explicit kMyMoneyLineEdit(QWidget *w = 0, bool forceMonetaryDecimalSymbol = false, Qt::Alignment alignment = (Qt::AlignLeft | Qt::AlignVCenter)); - ~kMyMoneyLineEdit(); + explicit KMyMoneyLineEdit(QWidget *w = 0, bool forceMonetaryDecimalSymbol = false, Qt::Alignment alignment = (Qt::AlignLeft | Qt::AlignVCenter)); + ~KMyMoneyLineEdit(); /** * This method is used to set the value of the widget back to * the one passed using loadText(). */ void resetText(); /** * Do not select the text upon the next focus in event if * @p skipIt is set to @p true. When object is constructed, * the default is @p false. */ void skipSelectAll(bool skipIt); public slots: void loadText(const QString& text); signals: /** * This signal is emitted when the focus leaves the object and the text * has been changed. The new text is passed as @a str. */ void lineChanged(const QString& str); protected: - void focusOutEvent(QFocusEvent *ev); - void focusInEvent(QFocusEvent *ev); + void focusOutEvent(QFocusEvent *ev) override; + void focusInEvent(QFocusEvent *ev) override; /** * Overridden so that the period key on the numeric keypad always sends * out the currently selected monetary decimal symbol instead of the * key defined by the keymap. * * Example: If you have set the keymap (keyboard layout) as English, then * the numeric keypad will send a period but if you have set the keymap to * be German, the same key will send a comma. * * @param ev pointer to current QKeyEvent */ - void keyPressEvent(QKeyEvent* ev); + void keyPressEvent(QKeyEvent* ev) override; /** * Overridden so that the period key on the numeric keypad always sends * out the currently selected monetary decimal symbol instead of the * key defined by the keymap. * * Example: If you have set the keymap (keyboard layout) as English, then * the numeric keypad will send a period but if you have set the keymap to * be German, the same key will send a comma. * * @param ev pointer to current QKeyEvent */ - void keyReleaseEvent(QKeyEvent* ev); + void keyReleaseEvent(QKeyEvent* ev) override; private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; }; #endif diff --git a/kmymoney/widgets/kmymoneymvccombo.cpp b/kmymoney/widgets/kmymoneymvccombo.cpp index 88e882900..08ba2f887 100644 --- a/kmymoney/widgets/kmymoneymvccombo.cpp +++ b/kmymoney/widgets/kmymoneymvccombo.cpp @@ -1,820 +1,306 @@ /*************************************************************************** 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" - -using namespace Icons; -using namespace eMyMoney; -class KMyMoneyMVCCombo::Private +KMyMoneyMVCCombo::KMyMoneyMVCCombo(QWidget* parent) : + KComboBox(parent), + d_ptr(new KMyMoneyMVCComboPrivate) { -public: - Private() : - m_canCreateObjects(false), - m_inFocusOutEvent(false), - m_completer(0) {} - - /** - * Flag to control object creation. Use - * KMyMoneyMVCCombo::setSuppressObjectCreation() - * to modify it's setting. Defaults to @a false. - */ - bool m_canCreateObjects; + view()->setAlternatingRowColors(true); + connect(this, static_cast(&KMyMoneyMVCCombo::KComboBox::activated), this, &KMyMoneyMVCCombo::activated); +} - /** - * Flag to check whether a focusOutEvent processing is underway or not - */ - bool m_inFocusOutEvent; +KMyMoneyMVCCombo::KMyMoneyMVCCombo(bool editable, QWidget* parent) : + KComboBox(editable, parent), + d_ptr(new KMyMoneyMVCComboPrivate) +{ + Q_D(KMyMoneyMVCCombo); + d->m_completer = new QCompleter(this); + d->m_completer->setCaseSensitivity(Qt::CaseInsensitive); + d->m_completer->setModel(model()); + setCompleter(d->m_completer); - QCompleter *m_completer; -}; + // setSubstringSearch(!KMyMoneySettings::stringMatchFromStart()); + view()->setAlternatingRowColors(true); + setInsertPolicy(QComboBox::NoInsert); // don't insert new objects due to object creation + connect(this, static_cast(&KMyMoneyMVCCombo::KComboBox::activated), this, &KMyMoneyMVCCombo::activated); +} -KMyMoneyMVCCombo::KMyMoneyMVCCombo(QWidget* parent) : - KComboBox(parent), - d(new Private) +KMyMoneyMVCCombo::KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, QWidget* parent) : + KComboBox(parent), + d_ptr(&dd) { view()->setAlternatingRowColors(true); - connect(this, SIGNAL(activated(int)), SLOT(activated(int))); + connect(this, static_cast(&KMyMoneyMVCCombo::KComboBox::activated), this, &KMyMoneyMVCCombo::activated); } -KMyMoneyMVCCombo::KMyMoneyMVCCombo(bool editable, QWidget* parent) : - KComboBox(editable, parent), - d(new Private) +KMyMoneyMVCCombo::KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, bool editable, QWidget* parent) : + KComboBox(editable, parent), + d_ptr(&dd) { + 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))); + connect(this, static_cast(&KMyMoneyMVCCombo::KComboBox::activated), this, &KMyMoneyMVCCombo::activated); } 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 +QString KMyMoneyMVCCombo::selectedItem() const { + Q_D(const KMyMoneyMVCCombo); QVariant data = itemData(currentIndex()); if (data.isValid()) - m_id = data.toString(); + d->m_id = data.toString(); else - m_id.clear(); - return m_id; + d->m_id.clear(); + return d->m_id; } void KMyMoneyMVCCombo::setSelectedItem(const QString& id) { - m_id = id; - setCurrentIndex(findData(QVariant(m_id))); + Q_D(KMyMoneyMVCCombo); + d->m_id = id; + setCurrentIndex(findData(QVariant(d->m_id))); } void KMyMoneyMVCCombo::activated(int index) { + Q_D(KMyMoneyMVCCombo); QVariant data = itemData(index); if (data.isValid()) { - m_id = data.toString(); - emit itemSelected(m_id); + d->m_id = data.toString(); + emit itemSelected(d->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 = m_id; - m_id.clear(); + QString id = d->m_id; + d->m_id.clear(); if (!id.isEmpty()) - emit itemSelected(m_id); + emit itemSelected(d->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 - m_id = id; + d->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 1f28ffcb7..9453acb2c 100644 --- a/kmymoney/widgets/kmymoneymvccombo.h +++ b/kmymoney/widgets/kmymoneymvccombo.h @@ -1,486 +1,156 @@ /*************************************************************************** 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: - KMyMoneyMVCCombo(QWidget* parent = 0); - explicit KMyMoneyMVCCombo(bool editable, QWidget* parent = 0); - ~KMyMoneyMVCCombo(); + explicit KMyMoneyMVCCombo(QWidget* parent = nullptr); + explicit KMyMoneyMVCCombo(bool editable, QWidget* parent = nullptr); + virtual ~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; + 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*); + void focusOutEvent(QFocusEvent*) override; /** * 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); + void connectNotify(const QMetaMethod & signal) override; /** * Overridden for internal reasons, no API change */ - void disconnectNotify(const QMetaMethod & signal); + void disconnectNotify(const QMetaMethod & signal) override; /** * overridden for internal reasons, no API change */ - void setCurrentText(const QString& txt = QString()) { - KComboBox::setItemText(KComboBox::currentIndex(), txt); - } + void setCurrentText(const QString& txt); + void setCurrentText(); 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: - /** - * 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); + KMyMoneyMVCComboPrivate * const d_ptr; + KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, QWidget* parent = nullptr); + KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, bool editable, QWidget* parent = nullptr); private: - QString m_tagId; + Q_DECLARE_PRIVATE(KMyMoneyMVCCombo) }; -/** - * 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/dialogs/kselectdatabasedlg.h b/kmymoney/widgets/kmymoneymvccombo_p.h similarity index 50% copy from kmymoney/dialogs/kselectdatabasedlg.h copy to kmymoney/widgets/kmymoneymvccombo_p.h index 70fcdbc24..4f12198e6 100644 --- a/kmymoney/dialogs/kselectdatabasedlg.h +++ b/kmymoney/widgets/kmymoneymvccombo_p.h @@ -1,74 +1,66 @@ /*************************************************************************** - kselectdatabase.h + kmymoneymvccombo_p.h - description ------------------- - copyright : (C) 2005 by Tony Bloomfield + 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 KSELECTDATABASEDLG_H -#define KSELECTDATABASEDLG_H +#ifndef KMYMONEYMVCCOMBO_P_H +#define KMYMONEYMVCCOMBO_P_H + // ---------------------------------------------------------------------------- // QT Includes -#include +#include // ---------------------------------------------------------------------------- // KDE Includes -#include - // ---------------------------------------------------------------------------- // Project Includes -namespace Ui { class KSelectDatabaseDlg; } +class QCompleter; -class kMandatoryFieldGroup; -class KSelectDatabaseDlg : public QDialog +class KMyMoneyMVCComboPrivate { - Q_OBJECT public: - explicit KSelectDatabaseDlg(int openMode, QUrl openURL = QUrl(), QWidget *parent = nullptr); - ~KSelectDatabaseDlg(); + KMyMoneyMVCComboPrivate() : + m_canCreateObjects(false), + m_inFocusOutEvent(false), + m_completer(nullptr) + { + } /** - * Check whether we have required database drivers - * @return - false, no drivers available, true, can proceed - */ - bool checkDrivers(); + * Flag to control object creation. Use + * KMyMoneyMVCCombo::setSuppressObjectCreation() + * to modify it's setting. Defaults to @a false. + */ + bool m_canCreateObjects; /** - * Return URL of database - * @return - pseudo-URL of database selected by user - */ - const QUrl selectedURL(); + * Flag to check whether a focusOutEvent processing is underway or not + */ + bool m_inFocusOutEvent; + QCompleter *m_completer; /** - * Execute the database selection dialog - * @return - as QDialog::exec() - */ - int exec() override; - -public slots: - void slotDriverSelected(int idx); - void slotHelp(); - -private: - Ui::KSelectDatabaseDlg* m_widget; - int m_mode; - QUrl m_url; - kMandatoryFieldGroup* m_requiredFields; - bool m_sqliteSelected; + * This is just a cache to be able to implement the old interface. + */ + mutable QString m_id; }; #endif diff --git a/kmymoney/widgets/kpricetreeitem.h b/kmymoney/widgets/kmymoneyoccurrencecombo.cpp similarity index 57% copy from kmymoney/widgets/kpricetreeitem.h copy to kmymoney/widgets/kmymoneyoccurrencecombo.cpp index f526735c6..1baa8844c 100644 --- a/kmymoney/widgets/kpricetreeitem.h +++ b/kmymoney/widgets/kmymoneyoccurrencecombo.cpp @@ -1,52 +1,48 @@ /*************************************************************************** - kpricetreeitem.h - description + kmymoneyoccurrencecombo.cpp - description ------------------- - begin : Sun Jul 18 2010 - copyright : (C) 2010 by Alvaro Soliverez - email : asoliverez@kde.org - + 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 KPRICETREEITEM_H -#define KPRICETREEITEM_H +#include "kmymoneyoccurrencecombo.h" // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes - // ---------------------------------------------------------------------------- // Project Includes +#include "mymoneyenums.h" -class KPriceTreeItem : public QTreeWidgetItem -{ -public: - KPriceTreeItem(QTreeWidget* parent); - - bool operator<(const QTreeWidgetItem &otherItem) const; +using namespace eMyMoney; - enum PriceItemDataRole { - ScheduleIdRole = Qt::UserRole, - OrderRole = Qt::UserRole + 1 - }; - - enum ePriceColumns { ePriceCommodity = 0, ePriceStockName, ePriceCurrency, ePriceDate, ePricePrice, ePriceSource }; +KMyMoneyOccurrenceCombo::KMyMoneyOccurrenceCombo(QWidget* parent) : + KMyMoneyGeneralCombo(parent) +{ +} -}; +KMyMoneyOccurrenceCombo::~KMyMoneyOccurrenceCombo() +{ +} -#endif // KPRICETREEITEM_H +Schedule::Occurrence KMyMoneyOccurrenceCombo::currentItem() const +{ + return static_cast(KMyMoneyGeneralCombo::currentItem()); +} diff --git a/kmymoney/widgets/kmymoneywizard_p.h b/kmymoney/widgets/kmymoneyoccurrencecombo.h similarity index 53% copy from kmymoney/widgets/kmymoneywizard_p.h copy to kmymoney/widgets/kmymoneyoccurrencecombo.h index ac714825b..8aae66dab 100644 --- a/kmymoney/widgets/kmymoneywizard_p.h +++ b/kmymoney/widgets/kmymoneyoccurrencecombo.h @@ -1,54 +1,55 @@ /*************************************************************************** - kmymoneywizard_p.h + kmymoneyoccurrencecombo.h - description ------------------- - copyright : (C) 2006 by Thomas Baumagrt - email : ipwizard@users.sourceforge.net + 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 KMYMONEYWIZARD_P_H -#define KMYMONEYWIZARD_P_H +#ifndef KMYMONEYOCCURRENCECOMBO_H +#define KMYMONEYOCCURRENCECOMBO_H // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "kmymoneygeneralcombo.h" + +namespace eMyMoney { namespace Schedule { enum class Occurrence; } } + /** - * @author Thomas Baumgart (C) 2006 - * - * This class represents a helper object required - * to be able to use Qt's signal/slot mechanism within - * the KMyMoneyWizardPage object which cannot be - * derived from QObject directly. - */ -class KMyMoneyWizardPagePrivate : public QObject + * 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: - /** - * Constructor - */ - explicit KMyMoneyWizardPagePrivate(QObject* parent); + Q_DISABLE_COPY(KMyMoneyOccurrenceCombo) - void emitCompleteStateChanged(void); +public: + explicit KMyMoneyOccurrenceCombo(QWidget* parent = nullptr); + ~KMyMoneyOccurrenceCombo() override; -signals: - void completeStateChanged(void); + eMyMoney::Schedule::Occurrence currentItem() const; }; #endif diff --git a/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.cpp b/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.cpp new file mode 100644 index 000000000..c4ed2dec7 --- /dev/null +++ b/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + 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/kmymoneywizard_p.h b/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.h similarity index 56% copy from kmymoney/widgets/kmymoneywizard_p.h copy to kmymoney/widgets/kmymoneyoccurrenceperiodcombo.h index ac714825b..cae01b8b2 100644 --- a/kmymoney/widgets/kmymoneywizard_p.h +++ b/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.h @@ -1,54 +1,51 @@ /*************************************************************************** - kmymoneywizard_p.h + kmymoneyoccurrenceperiodcombo.h - description ------------------- - copyright : (C) 2006 by Thomas Baumagrt - email : ipwizard@users.sourceforge.net + 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 KMYMONEYWIZARD_P_H -#define KMYMONEYWIZARD_P_H +#ifndef KMYMONEYOCCURRENCEPERIODCOMBO_H +#define KMYMONEYOCCURRENCEPERIODCOMBO_H // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "kmymoneyoccurrencecombo.h" + /** - * @author Thomas Baumgart (C) 2006 - * - * This class represents a helper object required - * to be able to use Qt's signal/slot mechanism within - * the KMyMoneyWizardPage object which cannot be - * derived from QObject directly. - */ -class KMyMoneyWizardPagePrivate : public QObject + * This class implements an occurrence period selector + * + * @author Colin Wright + */ +class KMM_WIDGETS_EXPORT KMyMoneyOccurrencePeriodCombo : public KMyMoneyOccurrenceCombo { Q_OBJECT -public: - /** - * Constructor - */ - explicit KMyMoneyWizardPagePrivate(QObject* parent); + Q_DISABLE_COPY(KMyMoneyOccurrencePeriodCombo) - void emitCompleteStateChanged(void); +public: + explicit KMyMoneyOccurrencePeriodCombo(QWidget* parent = nullptr); + ~KMyMoneyOccurrencePeriodCombo() override; -signals: - void completeStateChanged(void); }; #endif diff --git a/kmymoney/widgets/kmymoneypayeecombo.cpp b/kmymoney/widgets/kmymoneypayeecombo.cpp new file mode 100644 index 000000000..c4b10c887 --- /dev/null +++ b/kmymoney/widgets/kmymoneypayeecombo.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + 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 + +// ---------------------------------------------------------------------------- +// 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 new file mode 100644 index 000000000..a6177bfe0 --- /dev/null +++ b/kmymoney/widgets/kmymoneypayeecombo.h @@ -0,0 +1,66 @@ +/*************************************************************************** + 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 new file mode 100644 index 000000000..84989de47 --- /dev/null +++ b/kmymoney/widgets/kmymoneyperiodcombo.cpp @@ -0,0 +1,108 @@ +/*************************************************************************** + 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 new file mode 100644 index 000000000..7a4638836 --- /dev/null +++ b/kmymoney/widgets/kmymoneyperiodcombo.h @@ -0,0 +1,70 @@ +/*************************************************************************** + 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 new file mode 100644 index 000000000..3f74982cf --- /dev/null +++ b/kmymoney/widgets/kmymoneyreconcilecombo.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + 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, &KMyMoneyMVCCombo::itemSelected, this, &KMyMoneyReconcileCombo::slotSetState); +} + +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/kmymoneywizard_p.h b/kmymoney/widgets/kmymoneyreconcilecombo.h similarity index 52% copy from kmymoney/widgets/kmymoneywizard_p.h copy to kmymoney/widgets/kmymoneyreconcilecombo.h index ac714825b..f6a0011bd 100644 --- a/kmymoney/widgets/kmymoneywizard_p.h +++ b/kmymoney/widgets/kmymoneyreconcilecombo.h @@ -1,54 +1,60 @@ /*************************************************************************** - kmymoneywizard_p.h + kmymoneyreconcilecombo.h - description ------------------- - copyright : (C) 2006 by Thomas Baumagrt - email : ipwizard@users.sourceforge.net + 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 KMYMONEYWIZARD_P_H -#define KMYMONEYWIZARD_P_H +#ifndef KMYMONEYRECONCILECOMBO_H +#define KMYMONEYRECONCILECOMBO_H // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "kmymoneymvccombo.h" + +namespace eMyMoney { namespace Split { enum class State; } } + /** - * @author Thomas Baumgart (C) 2006 - * - * This class represents a helper object required - * to be able to use Qt's signal/slot mechanism within - * the KMyMoneyWizardPage object which cannot be - * derived from QObject directly. + * @author Thomas Baumgart + * This class implements a combo box with the possible states for + * reconciliation. */ -class KMyMoneyWizardPagePrivate : public QObject + +class KMM_WIDGETS_EXPORT KMyMoneyReconcileCombo : public KMyMoneyMVCCombo { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyReconcileCombo) + public: - /** - * Constructor - */ - explicit KMyMoneyWizardPagePrivate(QObject* parent); + explicit KMyMoneyReconcileCombo(QWidget *w = 0); + ~KMyMoneyReconcileCombo() override; - void emitCompleteStateChanged(void); + void setState(eMyMoney::Split::State state); + eMyMoney::Split::State state() const; + void removeDontCare(); -signals: - void completeStateChanged(void); +protected slots: + void slotSetState(const QString&); }; #endif diff --git a/kmymoney/widgets/kmymoneyselector.cpp b/kmymoney/widgets/kmymoneyselector.cpp index fc03aa88a..6cb342a0e 100644 --- a/kmymoney/widgets/kmymoneyselector.cpp +++ b/kmymoney/widgets/kmymoneyselector.cpp @@ -1,525 +1,570 @@ /*************************************************************************** kmymoneyselector.cpp ------------------- begin : Thu Jun 29 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 "kmymoneyselector.h" +#include "kmymoneyselector_p.h" // ---------------------------------------------------------------------------- // QT Includes -#include -#include #include #include -#include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyglobalsettings.h" +#include "widgetenums.h" + +using namespace eWidgets; KMyMoneySelector::KMyMoneySelector(QWidget *parent, Qt::WindowFlags flags) : - QWidget(parent, flags) + QWidget(parent, flags), + d_ptr(new KMyMoneySelectorPrivate(this)) { - setAutoFillBackground(true); - - 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); + Q_D(KMyMoneySelector); + d->init(); +} - connect(m_treeWidget, SIGNAL(itemPressed(QTreeWidgetItem*,int)), this, SLOT(slotItemPressed(QTreeWidgetItem*,int))); - connect(m_treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SIGNAL(stateChanged())); +KMyMoneySelector::KMyMoneySelector(KMyMoneySelectorPrivate &dd, QWidget* parent, Qt::WindowFlags flags) : + QWidget(parent, flags), + d_ptr(&dd) +{ + Q_D(KMyMoneySelector); + d->init(); } KMyMoneySelector::~KMyMoneySelector() { + Q_D(KMyMoneySelector); + delete d; } void KMyMoneySelector::clear() { - m_treeWidget->clear(); + Q_D(KMyMoneySelector); + d->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) { - if (m_selMode != mode) { - m_selMode = mode; + Q_D(KMyMoneySelector); + if (d->m_selMode != mode) { + d->m_selMode = mode; clear(); // make sure, it's either Multi or Single if (mode != QTreeWidget::MultiSelection) { - 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*))); + d->m_selMode = QTreeWidget::SingleSelection; + connect(d->m_treeWidget, &QTreeWidget::itemSelectionChanged, this, &KMyMoneySelector::stateChanged); + connect(d->m_treeWidget, &QTreeWidget::itemActivated, this, &KMyMoneySelector::slotItemSelected); + connect(d->m_treeWidget, &QTreeWidget::itemClicked, this, &KMyMoneySelector::slotItemSelected); } else { - 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*))); + disconnect(d->m_treeWidget, &QTreeWidget::itemSelectionChanged, this, &KMyMoneySelector::stateChanged); + disconnect(d->m_treeWidget, &QTreeWidget::itemActivated, this, &KMyMoneySelector::slotItemSelected); + disconnect(d->m_treeWidget, &QTreeWidget::itemClicked, this, &KMyMoneySelector::slotItemSelected); } } QWidget::update(); } +QTreeWidget::SelectionMode KMyMoneySelector::selectionMode() const +{ + Q_D(const KMyMoneySelector); + return d->m_selMode; +} + void KMyMoneySelector::slotItemSelected(QTreeWidgetItem *item) { - if (m_selMode == QTreeWidget::SingleSelection) { + Q_D(KMyMoneySelector); + if (d->m_selMode == QTreeWidget::SingleSelection) { if (item && item->flags().testFlag(Qt::ItemIsSelectable)) { - emit itemSelected(item->data(0, IdRole).toString()); + emit itemSelected(item->data(0, (int)Selector::Role::Id).toString()); } } } QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after, const QString& key, const QString& id) { - QTreeWidgetItem* item = new QTreeWidgetItem(m_treeWidget, after); + Q_D(KMyMoneySelector); + QTreeWidgetItem* item = new QTreeWidgetItem(d->m_treeWidget, after); item->setText(0, name); - item->setData(0, KeyRole, key); - item->setData(0, IdRole, id); + item->setData(0, (int)Selector::Role::Key, key); + item->setData(0, (int)Selector::Role::Id, 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) { - QTreeWidgetItem* item = new QTreeWidgetItem(m_treeWidget); + Q_D(KMyMoneySelector); + QTreeWidgetItem* item = new QTreeWidgetItem(d->m_treeWidget); item->setText(0, name); - item->setData(0, KeyRole, key); - item->setData(0, IdRole, id); + item->setData(0, (int)Selector::Role::Key, key); + item->setData(0, (int)Selector::Role::Id, id); item->setText(1, key); // hidden, but used for sorting item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); - if (m_selMode == QTreeWidget::MultiSelection) { + if (d->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->setData(0, (int)Selector::Role::Key, key); + item->setData(0, (int)Selector::Role::Id, id); item->setText(1, key); // hidden, but used for sorting item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); - if (m_selMode == QTreeWidget::MultiSelection) { + if (d->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) { - QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); + Q_D(KMyMoneySelector); + QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; // scan items while ((it_v = *it) != 0) { - if (it_v->data(0, IdRole).toString() == itemId) { + if (it_v->data(0, (int)Selector::Role::Id).toString() == itemId) { setSelectable(it_v, !protect); break; } ++it; } } QTreeWidgetItem* KMyMoneySelector::item(const QString& id) const { - QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); + Q_D(const KMyMoneySelector); + QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { - if (it_v->data(0, IdRole).toString() == id) + if (it_v->data(0, (int)Selector::Role::Id).toString() == id) break; ++it; } return it_v; } bool KMyMoneySelector::allItemsSelected() const { - QTreeWidgetItem* rootItem = m_treeWidget->invisibleRootItem(); + Q_D(const KMyMoneySelector); + QTreeWidgetItem* rootItem = d->m_treeWidget->invisibleRootItem(); - if (m_selMode == QTreeWidget::SingleSelection) + if (d->m_selMode == QTreeWidget::SingleSelection) return false; - for (int i = 0; i < rootItem->childCount(); ++i) { + for (auto 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 (int i = 0; i < item->childCount(); ++i) { + for (auto 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(m_treeWidget); + QTreeWidgetItemIterator it(d->m_treeWidget); while ((it_v = *it) != 0) { - if (id == it_v->data(0, IdRole).toString()) { + if (id == it_v->data(0, (int)Selector::Role::Id).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(m_treeWidget, QTreeWidgetItemIterator::NotSelectable); + it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::NotSelectable); while ((it_v = *it) != 0) { if (it_v->childCount() == 0) delete it_v; it++; } } void KMyMoneySelector::selectAllItems(const bool state) { - selectAllSubItems(m_treeWidget->invisibleRootItem(), state); + Q_D(KMyMoneySelector); + selectAllSubItems(d->m_treeWidget->invisibleRootItem(), state); emit stateChanged(); } void KMyMoneySelector::selectItems(const QStringList& itemList, const bool state) { - selectSubItems(m_treeWidget->invisibleRootItem(), itemList, state); + Q_D(KMyMoneySelector); + selectSubItems(d->m_treeWidget->invisibleRootItem(), itemList, state); emit stateChanged(); } void KMyMoneySelector::selectSubItems(QTreeWidgetItem* item, const QStringList& itemList, const bool state) { - for (int i = 0; i < item->childCount(); ++i) { + for (auto i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); - if (child->flags().testFlag(Qt::ItemIsUserCheckable) && itemList.contains(child->data(0, IdRole).toString())) { + if (child->flags().testFlag(Qt::ItemIsUserCheckable) && itemList.contains(child->data(0, (int)Selector::Role::Id).toString())) { child->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } selectSubItems(child, itemList, state); } emit stateChanged(); } void KMyMoneySelector::selectAllSubItems(QTreeWidgetItem* item, const bool state) { - for (int i = 0; i < item->childCount(); ++i) { + for (auto 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 (m_selMode == QTreeWidget::SingleSelection) { - QTreeWidgetItem* it_c = m_treeWidget->currentItem(); + if (d->m_selMode == QTreeWidget::SingleSelection) { + QTreeWidgetItem* it_c = d->m_treeWidget->currentItem(); if (it_c != 0) - list << it_c->data(0, IdRole).toString(); + list << it_c->data(0, (int)Selector::Role::Id).toString(); } else { - QTreeWidgetItem* rootItem = m_treeWidget->invisibleRootItem(); - for (int i = 0; i < rootItem->childCount(); ++i) { + QTreeWidgetItem* rootItem = d->m_treeWidget->invisibleRootItem(); + for (auto 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(); + list << child->data(0, (int)Selector::Role::Id).toString(); } selectedItems(list, child); } } } void KMyMoneySelector::selectedItems(QStringList& list, QTreeWidgetItem* item) const { - for (int i = 0; i < item->childCount(); ++i) { + for (auto 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(); + list << child->data(0, (int)Selector::Role::Id).toString(); } selectedItems(list, child); } } void KMyMoneySelector::itemList(QStringList& list) const { - QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); + Q_D(const KMyMoneySelector); + QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { - list << it_v->data(0, IdRole).toString(); + list << it_v->data(0, (int)Selector::Role::Id).toString(); it++; } } void KMyMoneySelector::setSelected(const QString& id, const bool state) { - QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); + Q_D(const KMyMoneySelector); + QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* item; QTreeWidgetItem* it_visible = 0; while ((item = *it) != 0) { - if (item->data(0, IdRole).toString() == id) { + if (item->data(0, (int)Selector::Role::Id).toString() == id) { if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { item->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } - m_treeWidget->setCurrentItem(item); + d->m_treeWidget->setCurrentItem(item); if (!it_visible) it_visible = item; } it++; } // make sure the first one found is visible if (it_visible) - m_treeWidget->scrollToItem(it_visible); + d->m_treeWidget->scrollToItem(it_visible); +} + +QTreeWidget* KMyMoneySelector::listView() const +{ + Q_D(const KMyMoneySelector); + return d->m_treeWidget; } 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) { - QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); + Q_D(KMyMoneySelector); + QTreeWidgetItemIterator it(d->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(m_treeWidget, QTreeWidgetItemIterator::Selectable); + it = QTreeWidgetItemIterator(d->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 (int i = 0; hide && i < it_v->childCount(); ++i) { + for (auto 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 (m_selMode == QTreeWidget::SingleSelection) { + if (d->m_selMode == QTreeWidget::SingleSelection) { if (firstMatch) { - m_treeWidget->setCurrentItem(firstMatch); - m_treeWidget->scrollToItem(firstMatch); + d->m_treeWidget->setCurrentItem(firstMatch); + d->m_treeWidget->scrollToItem(firstMatch); } else - m_treeWidget->clearSelection(); + d->m_treeWidget->clearSelection(); } // Get the number of visible nodes for the return code - int cnt = 0; + auto cnt = 0; - it = QTreeWidgetItemIterator(m_treeWidget, QTreeWidgetItemIterator::Selectable | QTreeWidgetItemIterator::NotHidden); + it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::Selectable | QTreeWidgetItemIterator::NotHidden); while ((it_v = *it) != 0) { cnt++; it++; } return cnt; } bool KMyMoneySelector::contains(const QString& txt) const { - QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); + Q_D(const KMyMoneySelector); + QTreeWidgetItemIterator it(d->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 = m_treeWidget->visualItemRect(item); - QRect rect = m_treeWidget->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, m_treeWidget); - if (rect.contains(m_treeWidget->mapFromGlobal(QCursor::pos()))) { + 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()))) { // 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 ba073c029..e7daace95 100644 --- a/kmymoney/widgets/kmymoneyselector.h +++ b/kmymoney/widgets/kmymoneyselector.h @@ -1,371 +1,364 @@ /*************************************************************************** kmymoneyselector.h ------------------- begin : Thu Jun 29 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 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 = 0, Qt::WindowFlags flags = 0); + explicit KMyMoneySelector(QWidget* parent = nullptr, 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 { - return m_selMode; - } + QTreeWidget::SelectionMode selectionMode() const; /** * 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 { - return m_treeWidget; - }; + QTreeWidget* listView() const; /** * 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 = QString(), const QString& id = QString()); + QTreeWidgetItem* newItem(const QString& name, const QString& key, const QString& id); + QTreeWidgetItem* newItem(const QString& name, const QString& key); + QTreeWidgetItem* newItem(const QString& name); /** * 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 = QString(), const QString& id = QString()); + 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); /** * 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() { - selectAllItems(true); - }; + void slotSelectAllItems(); /** * This slot deselects all items that are currently in * the item list of the widget. */ - void slotDeselectAllItems() { - selectAllItems(false); - }; + void slotDeselectAllItems(); 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: - QTreeWidget* m_treeWidget; - QStringList m_itemList; - QString m_baseName; - QTreeWidget::SelectionMode m_selMode; - QHBoxLayout* m_layout; + KMyMoneySelectorPrivate * const d_ptr; + KMyMoneySelector(KMyMoneySelectorPrivate &dd, QWidget* parent = nullptr, Qt::WindowFlags flags = 0); + +private: + Q_DECLARE_PRIVATE(KMyMoneySelector) }; #endif diff --git a/kmymoney/widgets/kmymoneyselector_p.h b/kmymoney/widgets/kmymoneyselector_p.h new file mode 100644 index 000000000..079d33973 --- /dev/null +++ b/kmymoney/widgets/kmymoneyselector_p.h @@ -0,0 +1,93 @@ +/*************************************************************************** + kmymoneyselector_p.h + ------------------- + begin : Thu Jun 29 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 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 new file mode 100644 index 000000000..cf9799b97 --- /dev/null +++ b/kmymoney/widgets/kmymoneytagcombo.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + 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 + +// ---------------------------------------------------------------------------- +// 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 new file mode 100644 index 000000000..92621ab88 --- /dev/null +++ b/kmymoney/widgets/kmymoneytagcombo.h @@ -0,0 +1,71 @@ +/*************************************************************************** + 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 f5e667d03..3974bac71 100644 --- a/kmymoney/widgets/kmymoneytextedit.cpp +++ b/kmymoney/widgets/kmymoneytextedit.cpp @@ -1,226 +1,192 @@ /* This file is part of KMyMoney, A Personal Finance Manager by KDE Copyright (C) 2013 Christian Dávid + (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. 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 +#include "kmymoneytextedithighlighter.h" -/* The Higligther */ +#include -KMyMoneyTextEditHighlighter::KMyMoneyTextEditHighlighter(QTextEdit * parent) - : Highlighter(parent), - m_allowedChars(QString("")), - m_maxLines(-1), - m_maxLineLength(-1), - m_maxLength(-1) +class KMyMoneyTextEditPrivate { + Q_DISABLE_COPY(KMyMoneyTextEditPrivate) + Q_DECLARE_PUBLIC(KMyMoneyTextEdit) -} - -void KMyMoneyTextEditHighlighter::setAllowedChars(const QString& chars) -{ - m_allowedChars = chars; - rehighlight(); -} +public: + KMyMoneyTextEditPrivate(KMyMoneyTextEdit *qq) : + q_ptr(qq), + m_maxLength(-1), + m_maxLineLength(-1), + m_maxLines(-1), + m_allowedChars(QString(QString())), + m_highligther(0) + { + } -void KMyMoneyTextEditHighlighter::setMaxLength(const int& length) -{ - m_maxLength = length; - rehighlight(); -} + bool isEventAllowed(QKeyEvent* e) const + { + Q_Q(const KMyMoneyTextEdit); + 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 (q->textCursor().hasSelection()) + return true; + + const QString plainText = q->toPlainText(); + + if (m_maxLength != -1 && plainText.length() >= m_maxLength) + return false; + if (m_maxLineLength != -1 && q->textCursor().block().length() - 1 >= m_maxLineLength) + return false; + } else if (m_maxLines != -1 && text.at(0) == '\r' && q->toPlainText().count('\n') + 1 >= m_maxLines) { + // Does this work on non-linux OSes as well? + return false; + } + } + return true; + } -void KMyMoneyTextEditHighlighter::setMaxLines(const int& lines) -{ - m_maxLines = lines; - rehighlight(); -} + KMyMoneyTextEdit *q_ptr; + int m_maxLength; + int m_maxLineLength; + int m_maxLines; + QString m_allowedChars; + KMyMoneyTextEditHighlighter* m_highligther; +}; -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 (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("")), - m_highligther(0) +KMyMoneyTextEdit::KMyMoneyTextEdit(QWidget* parent) : + KTextEdit(parent), + d_ptr(new KMyMoneyTextEditPrivate(this)) { + Q_D(KMyMoneyTextEdit); setWordWrapMode(QTextOption::ManualWrap); - m_highligther = new KMyMoneyTextEditHighlighter(this); + d->m_highligther = new KMyMoneyTextEditHighlighter(this); } -bool KMyMoneyTextEdit::isEventAllowed(QKeyEvent* e) const +KMyMoneyTextEdit::~KMyMoneyTextEdit() { - 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; + Q_D(KMyMoneyTextEdit); + delete d; } bool KMyMoneyTextEdit::isValid() const { + Q_D(const KMyMoneyTextEdit); const QString text = toPlainText(); - if (m_maxLength != -1 && text.length() >= m_maxLength) + if (d->m_maxLength != -1 && text.length() >= d->m_maxLength) return false; const QStringList lines = text.split('\n'); - if (m_maxLines != -1 && lines.count() >= m_maxLines) { + if (d->m_maxLines != -1 && lines.count() >= d->m_maxLines) { return false; } - if (m_maxLineLength != -1) { + if (d->m_maxLineLength != -1) { foreach (QString line, lines) { - if (line.length() > m_maxLineLength) + if (line.length() > d->m_maxLineLength) return false; } } const int length = text.length(); - for (int i = 0; i < length; ++i) { - if (!m_allowedChars.contains(text.at(i))) + for (auto i = 0; i < length; ++i) { + if (!d->m_allowedChars.contains(text.at(i))) return false; } return true; } void KMyMoneyTextEdit::keyReleaseEvent(QKeyEvent* e) { - if (isEventAllowed(e)) + Q_D(KMyMoneyTextEdit); + if (d->isEventAllowed(e)) KTextEdit::keyReleaseEvent(e); } void KMyMoneyTextEdit::keyPressEvent(QKeyEvent* e) { - if (isEventAllowed(e)) + Q_D(KMyMoneyTextEdit); + if (d->isEventAllowed(e)) KTextEdit::keyPressEvent(e); } int KMyMoneyTextEdit::maxLength() const { - return m_maxLength; + Q_D(const KMyMoneyTextEdit); + return d->m_maxLength; } void KMyMoneyTextEdit::setMaxLength(const int& maxLength) { - m_maxLength = maxLength; - m_highligther->setMaxLength(m_maxLength); + Q_D(KMyMoneyTextEdit); + d->m_maxLength = maxLength; + d->m_highligther->setMaxLength(d->m_maxLength); } int KMyMoneyTextEdit::maxLineLength() const { - return m_maxLineLength; + Q_D(const KMyMoneyTextEdit); + return d->m_maxLineLength; } void KMyMoneyTextEdit::setMaxLineLength(const int& maxLineLength) { - m_maxLineLength = maxLineLength; - m_highligther->setMaxLineLength(maxLineLength); + Q_D(KMyMoneyTextEdit); + d->m_maxLineLength = maxLineLength; + d->m_highligther->setMaxLineLength(maxLineLength); } int KMyMoneyTextEdit::maxLines() const { - return m_maxLines; + Q_D(const KMyMoneyTextEdit); + return d->m_maxLines; } void KMyMoneyTextEdit::setMaxLines(const int& maxLines) { - m_maxLines = maxLines; - m_highligther->setMaxLines(maxLines); + Q_D(KMyMoneyTextEdit); + d->m_maxLines = maxLines; + d->m_highligther->setMaxLines(maxLines); } QString KMyMoneyTextEdit::allowedChars() const { - return m_allowedChars; + Q_D(const KMyMoneyTextEdit); + return d->m_allowedChars; } void KMyMoneyTextEdit::setAllowedChars(const QString& allowedChars) { - m_allowedChars = allowedChars; - m_highligther->setAllowedChars(allowedChars); + Q_D(KMyMoneyTextEdit); + d->m_allowedChars = allowedChars; + d->m_highligther->setAllowedChars(allowedChars); } diff --git a/kmymoney/widgets/kmymoneytextedit.h b/kmymoney/widgets/kmymoneytextedit.h index a1f24a097..8080a20c9 100644 --- a/kmymoney/widgets/kmymoneytextedit.h +++ b/kmymoney/widgets/kmymoneytextedit.h @@ -1,92 +1,88 @@ /* This file is part of KMyMoney, A Personal Finance Manager by KDE Copyright (C) 2013 Christian Dávid + (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. 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 +#ifndef KMYMONEYTEXTEDITHIGHLIGHTER_H +#define KMYMONEYTEXTEDITHIGHLIGHTER_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 KMyMoneyTextEditPrivate; class KMM_WIDGETS_EXPORT KMyMoneyTextEdit : public KTextEdit { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyTextEdit) /** * @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); + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) public: - KMyMoneyTextEdit(QWidget* parent = 0); + explicit KMyMoneyTextEdit(QWidget* parent = nullptr); + ~KMyMoneyTextEdit(); 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); + void setReadOnly(bool) override; protected: - virtual void keyReleaseEvent(QKeyEvent* e); - virtual void keyPressEvent(QKeyEvent* e); + virtual void keyReleaseEvent(QKeyEvent* e) override; + virtual void keyPressEvent(QKeyEvent* e) override; private: - bool isEventAllowed(QKeyEvent* e) const; - int m_maxLength; - int m_maxLineLength; - int m_maxLines; - QString m_allowedChars; - KMyMoneyTextEditHighlighter* m_highligther; - + KMyMoneyTextEditPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMyMoneyTextEdit) }; -#endif // KMYMONEYTEXTEDIT_H +#endif // KMYMONEYTEXTEDITHIGHLIGHTER_H diff --git a/kmymoney/widgets/kmymoneytextedithighlighter.cpp b/kmymoney/widgets/kmymoneytextedithighlighter.cpp new file mode 100644 index 000000000..c2bb80acc --- /dev/null +++ b/kmymoney/widgets/kmymoneytextedithighlighter.cpp @@ -0,0 +1,123 @@ +/* + This file is part of KMyMoney, A Personal Finance Manager by KDE + Copyright (C) 2013 Christian Dávid + (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. + + 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 "kmymoneytextedithighlighter.h" + +/* The Higligther */ + +class KMyMoneyTextEditHighlighterPrivate +{ + Q_DISABLE_COPY(KMyMoneyTextEditHighlighterPrivate) + +public: + KMyMoneyTextEditHighlighterPrivate() : + m_allowedChars(QString(QString())), + m_maxLines(-1), + m_maxLineLength(-1), + m_maxLength(-1) + { + } + + QString m_allowedChars; + int m_maxLines; + int m_maxLineLength; + int m_maxLength; +}; + + +KMyMoneyTextEditHighlighter::KMyMoneyTextEditHighlighter(QTextEdit * parent) : + Highlighter(parent), + d_ptr(new KMyMoneyTextEditHighlighterPrivate) +{ +} + +KMyMoneyTextEditHighlighter::~KMyMoneyTextEditHighlighter() +{ + Q_D(KMyMoneyTextEditHighlighter); + delete d; +} + +void KMyMoneyTextEditHighlighter::setAllowedChars(const QString& chars) +{ + Q_D(KMyMoneyTextEditHighlighter); + m_allowedChars = chars; + rehighlight(); +} + +void KMyMoneyTextEditHighlighter::setMaxLength(const int& length) +{ + Q_D(KMyMoneyTextEditHighlighter); + m_maxLength = length; + rehighlight(); +} + +void KMyMoneyTextEditHighlighter::setMaxLines(const int& lines) +{ + Q_D(KMyMoneyTextEditHighlighter); + m_maxLines = lines; + rehighlight(); +} + +void KMyMoneyTextEditHighlighter::setMaxLineLength(const int& length) +{ + Q_D(KMyMoneyTextEditHighlighter); + m_maxLineLength = length; + rehighlight(); +} + +void KMyMoneyTextEditHighlighter::highlightBlock(const QString& text) +{ + Q_D(KMyMoneyTextEditHighlighter); + // 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) { + 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; + } +} diff --git a/kmymoney/widgets/kmymoneytextedit_p.h b/kmymoney/widgets/kmymoneytextedithighlighter.h similarity index 73% rename from kmymoney/widgets/kmymoneytextedit_p.h rename to kmymoney/widgets/kmymoneytextedithighlighter.h index 118f2f889..9404177e4 100644 --- a/kmymoney/widgets/kmymoneytextedit_p.h +++ b/kmymoney/widgets/kmymoneytextedithighlighter.h @@ -1,44 +1,51 @@ /* This file is part of KMyMoney, A Personal Finance Manager by KDE Copyright (C) 2013 Christian Dávid + (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. 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 KMYMONEYTEXTEDITHIGHLIGHTER_H -#define KMYMONEYTEXTEDITHIGHLIGHTER_H +#ifndef KMYMONEYTEXTEDIT_H +#define KMYMONEYTEXTEDIT_H #include +class KMyMoneyTextEditHighlighterPrivate; class KMyMoneyTextEditHighlighter : public Sonnet::Highlighter { + Q_DISABLE_COPY(KMyMoneyTextEditHighlighter) + public: - KMyMoneyTextEditHighlighter(QTextEdit* parent = 0); + explicit KMyMoneyTextEditHighlighter(QTextEdit* parent = nullptr); + ~KMyMoneyTextEditHighlighter(); void setAllowedChars(const QString& chars); void setMaxLength(const int& length); void setMaxLines(const int& lines); void setMaxLineLength(const int& length); protected: virtual void highlightBlock(const QString& text); private: + KMyMoneyTextEditHighlighterPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMyMoneyTextEditHighlighter) QString m_allowedChars; int m_maxLines; int m_maxLineLength; int m_maxLength; }; -#endif // KMYMONEYTEXTEDITHIGHLIGHTER_H +#endif // KMYMONEYTEXTEDIT_H diff --git a/kmymoney/widgets/kmymoneytitlelabel.cpp b/kmymoney/widgets/kmymoneytitlelabel.cpp index c954afede..8c1ee5bcb 100644 --- a/kmymoney/widgets/kmymoneytitlelabel.cpp +++ b/kmymoney/widgets/kmymoneytitlelabel.cpp @@ -1,104 +1,162 @@ /*************************************************************************** kmymoneytitlelabel.cpp ------------------- begin : Sun Feb 05 2005 copyright : (C) 2005 by Ace Jones email : acejones@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 "kmymoneytitlelabel.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include +#include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes +class KMyMoneyTitleLabelPrivate +{ + Q_DISABLE_COPY(KMyMoneyTitleLabelPrivate) + +public: + KMyMoneyTitleLabelPrivate() + { + } + + QImage m_leftImage; + QImage m_rightImage; + QColor m_bgColor; + QString m_text; + + QString m_leftImageFile; + QString m_rightImageFile; +}; + KMyMoneyTitleLabel::KMyMoneyTitleLabel(QWidget *parent) : - QLabel(parent) + QLabel(parent), + d_ptr(new KMyMoneyTitleLabelPrivate) { - m_bgColor = KColorScheme(isEnabled() ? QPalette::Active : QPalette::Inactive, KColorScheme::Selection).background(KColorScheme::NormalBackground).color(); + Q_D(KMyMoneyTitleLabel); + d->m_bgColor = KColorScheme(isEnabled() ? QPalette::Active : QPalette::Inactive, KColorScheme::Selection).background(KColorScheme::NormalBackground).color(); setFont(QFontDatabase::systemFont(QFontDatabase::TitleFont)); } KMyMoneyTitleLabel::~KMyMoneyTitleLabel() { + Q_D(KMyMoneyTitleLabel); + delete d; +} + +void KMyMoneyTitleLabel::setBgColor(const QColor& _color) +{ + Q_D(KMyMoneyTitleLabel); + d->m_bgColor = _color; } void KMyMoneyTitleLabel::setLeftImageFile(const QString& _file) { - m_leftImageFile = _file; - QString lfullpath = QStandardPaths::locate(QStandardPaths::DataLocation, m_leftImageFile); - m_leftImage.load(lfullpath); + Q_D(KMyMoneyTitleLabel); + d->m_leftImageFile = _file; + QString lfullpath = QStandardPaths::locate(QStandardPaths::DataLocation, d->m_leftImageFile); + d->m_leftImage.load(lfullpath); } void KMyMoneyTitleLabel::setRightImageFile(const QString& _file) { - m_rightImageFile = _file; - QString rfullpath = QStandardPaths::locate(QStandardPaths::DataLocation, m_rightImageFile); - m_rightImage.load(rfullpath); - if (m_rightImage.height() < 30) + Q_D(KMyMoneyTitleLabel); + d->m_rightImageFile = _file; + QString rfullpath = QStandardPaths::locate(QStandardPaths::DataLocation, d->m_rightImageFile); + d->m_rightImage.load(rfullpath); + if (d->m_rightImage.height() < 30) setMinimumHeight(30); else { - setMinimumHeight(m_rightImage.height()); - setMaximumHeight(m_rightImage.height()); + setMinimumHeight(d->m_rightImage.height()); + setMaximumHeight(d->m_rightImage.height()); } } +QString KMyMoneyTitleLabel::leftImageFile() const +{ + Q_D(const KMyMoneyTitleLabel); + return d->m_leftImageFile; +} + +QString KMyMoneyTitleLabel::rightImageFile() const +{ + Q_D(const KMyMoneyTitleLabel); + return d->m_rightImageFile; +} + +QColor KMyMoneyTitleLabel::bgColor() const +{ + Q_D(const KMyMoneyTitleLabel); + return d->m_bgColor; +} + +QString KMyMoneyTitleLabel::text() const +{ + Q_D(const KMyMoneyTitleLabel); + return d->m_text; +} + void KMyMoneyTitleLabel::paintEvent(QPaintEvent *e) { + Q_D(KMyMoneyTitleLabel); QLabel::paintEvent(e); QPainter painter(this); QRect cr = contentsRect(); // prepare the pixmap QImage output(cr.width(), cr.height(), QImage::Format_RGB32); - output.fill(m_bgColor.rgb()); + output.fill(d->m_bgColor.rgb()); QPixmap result = QPixmap::fromImage(output); - QPixmap overlay = QPixmap::fromImage(m_rightImage); + QPixmap overlay = QPixmap::fromImage(d->m_rightImage); QPainter pixmapPainter(&result); - pixmapPainter.drawPixmap(cr.width() - m_rightImage.width(), 0, overlay, 0, 0, overlay.width(), overlay.height()); - overlay = QPixmap::fromImage(m_leftImage); + pixmapPainter.drawPixmap(cr.width() - d->m_rightImage.width(), 0, overlay, 0, 0, overlay.width(), overlay.height()); + overlay = QPixmap::fromImage(d->m_leftImage); pixmapPainter.drawPixmap(0, 0, overlay, 0, 0, overlay.width(), overlay.height()); // first draw pixmap style()->drawItemPixmap(&painter, contentsRect(), alignment(), result); // then draw text on top with a larger font (relative to the pixmap size) and with the appropriate color QFont font = painter.font(); font.setPointSizeF(qMax(result.height() / static_cast(2.5), font.pointSizeF())); painter.setFont(font); painter.setPen(KColorScheme(QPalette::Active, KColorScheme::Selection).foreground(KColorScheme::NormalText).color()); - style()->drawItemText(&painter, contentsRect(), alignment(), palette(), isEnabled(), QString(" ") + m_text); + style()->drawItemText(&painter, contentsRect(), alignment(), palette(), isEnabled(), QString(" ") + d->m_text); } void KMyMoneyTitleLabel::setText(const QString& txt) { - m_text = txt; - m_text.replace('\n', QLatin1String(" ")); + Q_D(KMyMoneyTitleLabel); + d->m_text = txt; + d->m_text.replace('\n', QLatin1String(" ")); update(); } diff --git a/kmymoney/widgets/kmymoneytitlelabel.h b/kmymoney/widgets/kmymoneytitlelabel.h index 82a962e0c..390b29268 100644 --- a/kmymoney/widgets/kmymoneytitlelabel.h +++ b/kmymoney/widgets/kmymoneytitlelabel.h @@ -1,85 +1,72 @@ /*************************************************************************** kmymoneytitlelabel.h ------------------- begin : Sun Feb 05 2005 copyright : (C) 2005 by Ace Jones email : acejones@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. * * * ***************************************************************************/ #ifndef KMYMONEYTITLELABEL_H #define KMYMONEYTITLELABEL_H // ---------------------------------------------------------------------------- // QT Includes #include -#include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes /** * @author ace jones */ +class KMyMoneyTitleLabelPrivate; class KMyMoneyTitleLabel : public QLabel { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyTitleLabel) 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: - KMyMoneyTitleLabel(QWidget *parent = 0); + explicit KMyMoneyTitleLabel(QWidget* parent = nullptr); ~KMyMoneyTitleLabel(); - void setBgColor(const QColor& _color) { - m_bgColor = _color; - } + void setBgColor(const QColor& _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; - } + QString leftImageFile() const; + QString rightImageFile() const; + QColor bgColor() const; + QString text() const; public slots: virtual void setText(const QString& txt); protected: void updatePixmap(); - void paintEvent(QPaintEvent *); + void paintEvent(QPaintEvent *) override; private: - QImage m_leftImage; - QImage m_rightImage; - QColor m_bgColor; - QString m_text; - - QString m_leftImageFile; - QString m_rightImageFile; + KMyMoneyTitleLabelPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMyMoneyTitleLabel) }; #endif diff --git a/kmymoney/widgets/kmymoneyvalidationfeedback.cpp b/kmymoney/widgets/kmymoneyvalidationfeedback.cpp index aa3a18cad..92a597578 100644 --- a/kmymoney/widgets/kmymoneyvalidationfeedback.cpp +++ b/kmymoney/widgets/kmymoneyvalidationfeedback.cpp @@ -1,99 +1,111 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 Christian Dávid + * (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. * * 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 "kmymoneyvalidationfeedback.h" #include "ui_kmymoneyvalidationfeedback.h" #include #include "icons/icons.h" +#include "widgetenums.h" +using namespace eWidgets; using namespace Icons; -class KMyMoneyValidationFeedback::Private +class KMyMoneyValidationFeedbackPrivate { + Q_DISABLE_COPY(KMyMoneyValidationFeedbackPrivate) + public: - KMyMoneyValidationFeedback::MessageType type; -}; + KMyMoneyValidationFeedbackPrivate() : + ui(new Ui::KMyMoneyValidationFeedback) + { + } + + ~KMyMoneyValidationFeedbackPrivate() + { + delete ui; + } + Ui::KMyMoneyValidationFeedback *ui; + ValidationFeedback::MessageType type; +}; KMyMoneyValidationFeedback::KMyMoneyValidationFeedback(QWidget *parent) : QWidget(parent), - ui(new Ui::KMyMoneyValidationFeedback), - d_ptr(new Private) + d_ptr(new KMyMoneyValidationFeedbackPrivate) { - ui->setupUi(this); + Q_D(KMyMoneyValidationFeedback); + d->ui->setupUi(this); setHidden(true); QSizePolicy newSizePolicy = sizePolicy(); newSizePolicy.setControlType(QSizePolicy::Label); newSizePolicy.setHorizontalPolicy(QSizePolicy::MinimumExpanding); newSizePolicy.setVerticalPolicy(QSizePolicy::Fixed); setSizePolicy(newSizePolicy); } KMyMoneyValidationFeedback::~KMyMoneyValidationFeedback() { - Q_D(); - - delete ui; + Q_D(KMyMoneyValidationFeedback); delete d; } /** * @todo Set icon size according to text size */ -void KMyMoneyValidationFeedback::setFeedback(KMyMoneyValidationFeedback::MessageType type, QString message) +void KMyMoneyValidationFeedback::setFeedback(ValidationFeedback::MessageType type, QString message) { - Q_D(); + Q_D(KMyMoneyValidationFeedback); d->type = type; - if (type == None) { - if (message.isEmpty() || message == ui->label->text()) + if (type == ValidationFeedback::MessageType::None) { + if (message.isEmpty() || message == d->ui->label->text()) setHidden(true); } else { setHidden(false); - ui->label->setText(message); + d->ui->label->setText(message); QIcon icon; switch (type) { - case Error: + case ValidationFeedback::MessageType::Error: icon = QIcon::fromTheme(g_Icons[Icon::DialogError]); break; - case Positive: - case Information: + case ValidationFeedback::MessageType::Positive: + case ValidationFeedback::MessageType::Information: icon = QIcon::fromTheme(g_Icons[Icon::DialogInformation]); break; - case Warning: + case ValidationFeedback::MessageType::Warning: default: icon = QIcon::fromTheme(g_Icons[Icon::DialogWarning]); } - ui->icon->setPixmap(icon.pixmap(24)); + d->ui->icon->setPixmap(icon.pixmap(24)); } } void KMyMoneyValidationFeedback::removeFeedback() { setHidden(true); } -void KMyMoneyValidationFeedback::removeFeedback(KMyMoneyValidationFeedback::MessageType type, QString message) +void KMyMoneyValidationFeedback::removeFeedback(ValidationFeedback::MessageType type, QString message) { - Q_D(); - - if (d->type == type && ui->label->text() == message) + Q_D(KMyMoneyValidationFeedback); + if (d->type == type && d->ui->label->text() == message) removeFeedback(); } diff --git a/kmymoney/widgets/kmymoneyvalidationfeedback.h b/kmymoney/widgets/kmymoneyvalidationfeedback.h index 52c6fa05e..6e697b438 100644 --- a/kmymoney/widgets/kmymoneyvalidationfeedback.h +++ b/kmymoney/widgets/kmymoneyvalidationfeedback.h @@ -1,77 +1,66 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 Christian Dávid + * (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. * * 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; -} +namespace eWidgets { namespace ValidationFeedback { enum class MessageType; } } +class KMyMoneyValidationFeedbackPrivate; class KMM_WIDGETS_EXPORT KMyMoneyValidationFeedback : public QWidget { Q_OBJECT + Q_DISABLE_COPY(KMyMoneyValidationFeedback) public: - KMyMoneyValidationFeedback(QWidget *parent = 0); + explicit KMyMoneyValidationFeedback(QWidget* parent = nullptr); ~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); + void removeFeedback(eWidgets::ValidationFeedback::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); + void setFeedback(eWidgets::ValidationFeedback::MessageType type, QString message); private: - Ui::KMyMoneyValidationFeedback* ui; - - class Private; - Private* d_ptr; - Q_DECLARE_PRIVATE(); + KMyMoneyValidationFeedbackPrivate * const d_ptr; + Q_DECLARE_PRIVATE(KMyMoneyValidationFeedback) }; #endif // KMYMONEYVALIDATIONFEEDBACK_H diff --git a/kmymoney/widgets/kmymoneywizard.cpp b/kmymoney/widgets/kmymoneywizard.cpp index 6c783af93..7b3a7380c 100644 --- a/kmymoney/widgets/kmymoneywizard.cpp +++ b/kmymoney/widgets/kmymoneywizard.cpp @@ -1,393 +1,339 @@ /*************************************************************************** kmymoneywizard.cpp ------------------- copyright : (C) 2006 by Thomas Baumagrt 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 "kmymoneywizard_p.h" - // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes +#include "kmymoneywizardpage.h" #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("", m_stepFrame)); + m_stepLayout->addWidget(new QLabel(QString(), 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())); + connect(m_backButton, &QAbstractButton::clicked, this, &KMyMoneyWizard::backButtonClicked); + connect(m_nextButton, &QAbstractButton::clicked, this, &KMyMoneyWizard::nextButtonClicked); + connect(m_cancelButton, &QAbstractButton::clicked, this, &QDialog::reject); + connect(m_finishButton, &QAbstractButton::clicked, this, &KMyMoneyWizard::accept); + connect(m_helpButton, &QAbstractButton::clicked, this, &KMyMoneyWizard::helpButtonClicked); +} + +KMyMoneyWizard::~KMyMoneyWizard() +{ } 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); } } +QList KMyMoneyWizard::historyPages() const +{ + return m_history; +} + 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::setHelpContext(const QString& ctx) +{ + m_helpContext = ctx; +} + 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; - bool rc = currentPage->isComplete(); + auto 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 7389b1f48..4d5e9519f 100644 --- a/kmymoney/widgets/kmymoneywizard.h +++ b/kmymoney/widgets/kmymoneywizard.h @@ -1,550 +1,297 @@ /*************************************************************************** kmymoneywizard.h ------------------- copyright : (C) 2006 by Thomas Baumagrt 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. * * * ***************************************************************************/ #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 KNewUserGeneralDecl - * as all other dialogs. The class definition of KNewUserGeneral will - * look like this: - * - * @code - * 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) : - * 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; -}; - +class KMyMoneyWizardPage; /** * @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 = 0, const char* name = 0, bool modal = false, Qt::WindowFlags flags = 0); + * KNewUserWizard(QWidget* parent = nullptr, 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 + Q_DISABLE_COPY(KMyMoneyWizard) + 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; - } + QList historyPages() const; /** * 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; - } + void setHelpContext(const QString& ctx); - virtual ~KMyMoneyWizard() {} + 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) */ - KMyMoneyWizard(QWidget *parent = 0, bool modal = false, Qt::WindowFlags f = 0); + explicit KMyMoneyWizard(QWidget* parent = nullptr, 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/kmymoneywizard_p.h b/kmymoney/widgets/kmymoneywizard_p.h index ac714825b..2e365e93c 100644 --- a/kmymoney/widgets/kmymoneywizard_p.h +++ b/kmymoney/widgets/kmymoneywizard_p.h @@ -1,54 +1,55 @@ /*************************************************************************** kmymoneywizard_p.h ------------------- copyright : (C) 2006 by Thomas Baumagrt 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. * * * ***************************************************************************/ #ifndef KMYMONEYWIZARD_P_H #define KMYMONEYWIZARD_P_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes /** * @author Thomas Baumgart (C) 2006 * * This class represents a helper object required * to be able to use Qt's signal/slot mechanism within * the KMyMoneyWizardPage object which cannot be * derived from QObject directly. */ class KMyMoneyWizardPagePrivate : public QObject { Q_OBJECT public: /** * Constructor */ explicit KMyMoneyWizardPagePrivate(QObject* parent); void emitCompleteStateChanged(void); signals: void completeStateChanged(void); }; #endif diff --git a/kmymoney/widgets/kmymoneywizardpage.cpp b/kmymoney/widgets/kmymoneywizardpage.cpp new file mode 100644 index 000000000..9efe5f8e7 --- /dev/null +++ b/kmymoney/widgets/kmymoneywizardpage.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + kmymoneywizardpage.cpp + ------------------- + copyright : (C) 2006 by Thomas Baumagrt + 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 "kmymoneywizardpage.h" +#include "kmymoneywizard_p.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kguiutils.h" +#include "kmymoneywizard.h" + +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(); +} + +KMyMoneyWizardPage::~KMyMoneyWizardPage() +{ +} + +QObject* KMyMoneyWizardPage::object() const +{ + return d; +} + +QWidget* KMyMoneyWizardPage::initialFocusWidget() const +{ + return nullptr; +} + +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(); +} + +unsigned int KMyMoneyWizardPage::step() const +{ + return m_step; +} + +QWidget* KMyMoneyWizardPage::widget() const +{ + return m_widget; +} + +QString KMyMoneyWizardPage::helpContext() const +{ + return QString(); +} diff --git a/kmymoney/widgets/kmymoneywizard.h b/kmymoney/widgets/kmymoneywizardpage.h similarity index 50% copy from kmymoney/widgets/kmymoneywizard.h copy to kmymoney/widgets/kmymoneywizardpage.h index 7389b1f48..ae7dcdaf2 100644 --- a/kmymoney/widgets/kmymoneywizard.h +++ b/kmymoney/widgets/kmymoneywizardpage.h @@ -1,550 +1,289 @@ /*************************************************************************** - kmymoneywizard.h + kmymoneywizardpage.h ------------------- copyright : (C) 2006 by Thomas Baumagrt 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. * * * ***************************************************************************/ -#ifndef KMYMONEYWIZARD_H -#define KMYMONEYWIZARD_H +#ifndef KMYMONEYWIZARDPAGE_H +#define KMYMONEYWIZARDPAGE_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; +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 KNewUserGeneralDecl + * The ui designer generated class should have the name KNewUserGeneral * as all other dialogs. The class definition of KNewUserGeneral will * look like this: * * @code - * class KNewUserGeneral : public KNewUserGeneralDecl, public KNewUserPage + * class KNewUserGeneral : public KNewUserGeneral, 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) : - * KNewUserGeneralDecl(parent), + * KNewUserGeneral(parent), * KNewUserPage(1, this, parent, name) * { - * kMandatoryFieldGroup* mandatoryGroup = new kMandatoryFieldGroup(this); + * 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; - } + unsigned int step() const; /** * 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; - } + QWidget* widget() const; /** * 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 QWidget* initialFocusWidget() const; 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() {} + virtual ~KMyMoneyWizardPage(); protected: /** * Constructor (kept protected, so that one cannot create such an object directly) */ - KMyMoneyWizardPage(unsigned int step, QWidget* widget); + explicit 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; + 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 = 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) - */ - 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/kpricetreeitem.h b/kmymoney/widgets/kpricetreeitem.h index f526735c6..f6e07dcf3 100644 --- a/kmymoney/widgets/kpricetreeitem.h +++ b/kmymoney/widgets/kpricetreeitem.h @@ -1,52 +1,53 @@ /*************************************************************************** kpricetreeitem.h - description ------------------- begin : Sun Jul 18 2010 copyright : (C) 2010 by Alvaro Soliverez email : asoliverez@kde.org + (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 KPRICETREEITEM_H #define KPRICETREEITEM_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes class KPriceTreeItem : public QTreeWidgetItem { public: - KPriceTreeItem(QTreeWidget* parent); + explicit KPriceTreeItem(QTreeWidget* parent); bool operator<(const QTreeWidgetItem &otherItem) const; enum PriceItemDataRole { ScheduleIdRole = Qt::UserRole, OrderRole = Qt::UserRole + 1 }; enum ePriceColumns { ePriceCommodity = 0, ePriceStockName, ePriceCurrency, ePriceDate, ePricePrice, ePriceSource }; }; #endif // KPRICETREEITEM_H diff --git a/kmymoney/widgets/kschedulebriefwidget.ui b/kmymoney/widgets/kschedulebriefwidget.ui deleted file mode 100644 index 468a08ac1..000000000 --- a/kmymoney/widgets/kschedulebriefwidget.ui +++ /dev/null @@ -1,322 +0,0 @@ - - - - - - kScheduleBriefWidget - - - - 0 - 0 - 341 - 339 - - - - Schedules - - - - 1 - - - 6 - - - - - QFrame::WinPanel - - - QFrame::Raised - - - 2 - - - 100 - - - - 11 - - - 6 - - - - - 0 - - - 6 - - - - - ... - - - - - - - - 40 - 0 - - - - QSizePolicy::Expanding - - - Qt::Horizontal - - - - - - - n of n - - - false - - - - - - - - 70 - 0 - - - - QSizePolicy::Expanding - - - Qt::Horizontal - - - - - - - ... - - - - - - - - - - - - 60 - 0 - - - - - 60 - 32767 - - - - Name: - - - false - - - - - - - - 60 - 0 - - - - - 60 - 32767 - - - - Type: - - - false - - - - - - - true - - - true - - - - - - - true - - - true - - - - - - - true - - - true - - - - - - - - 0 - 5 - 0 - 0 - - - - - 60 - 0 - - - - Account: - - - false - - - - - - - - - true - - - 1 - - - true - - - - - - - 0 - - - 6 - - - - - - 35 - 0 - - - - QSizePolicy::Expanding - - - Qt::Horizontal - - - - - - - 0 - - - 6 - - - - - Enter... - - - - - - - &Skip - - - - - - - &Close - - - 13 - - - - - - - - - - 43 - 0 - - - - QSizePolicy::Expanding - - - Qt::Horizontal - - - - - - - - - - - - - - KLineEdit - QLineEdit -
klineedit.h
-
- - KTextEdit - QTextEdit -
ktextedit.h
-
-
-
diff --git a/kmymoney/widgets/ktagcontainer.cpp b/kmymoney/widgets/ktagcontainer.cpp new file mode 100644 index 000000000..8f4478731 --- /dev/null +++ b/kmymoney/widgets/ktagcontainer.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** + 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 "ktaglabel.h" +#include "mymoneytag.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, &KMyMoneyMVCCombo::lostFocus, this, &KTagContainer::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, &KTagLabel::clicked, this, &KTagContainer::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 new file mode 100644 index 000000000..8e7d30302 --- /dev/null +++ b/kmymoney/widgets/ktagcontainer.h @@ -0,0 +1,72 @@ +/*************************************************************************** + 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 new file mode 100644 index 000000000..e87ceaadf --- /dev/null +++ b/kmymoney/widgets/ktaglabel.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** + 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, &QAbstractButton::clicked, this, &KTagLabel::clicked); + //this->setFrameStyle(QFrame::Panel | QFrame::Plain); +} diff --git a/kmymoney/widgets/kmymoneywizard_p.h b/kmymoney/widgets/ktaglabel.h similarity index 58% copy from kmymoney/widgets/kmymoneywizard_p.h copy to kmymoney/widgets/ktaglabel.h index ac714825b..cb8abc763 100644 --- a/kmymoney/widgets/kmymoneywizard_p.h +++ b/kmymoney/widgets/ktaglabel.h @@ -1,54 +1,56 @@ /*************************************************************************** - kmymoneywizard_p.h + ktaglabel.h - description ------------------- - copyright : (C) 2006 by Thomas Baumagrt - email : ipwizard@users.sourceforge.net + 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 KMYMONEYWIZARD_P_H -#define KMYMONEYWIZARD_P_H +#ifndef KTAGLABEL_H +#define KTAGLABEL_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes /** - * @author Thomas Baumgart (C) 2006 + * 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 * - * This class represents a helper object required - * to be able to use Qt's signal/slot mechanism within - * the KMyMoneyWizardPage object which cannot be - * derived from QObject directly. + * @author Alessandro Russo */ -class KMyMoneyWizardPagePrivate : public QObject +class KTagLabel : public QFrame { Q_OBJECT -public: - /** - * Constructor - */ - explicit KMyMoneyWizardPagePrivate(QObject* parent); + Q_DISABLE_COPY(KTagLabel) - void emitCompleteStateChanged(void); +public: + explicit KTagLabel(const QString& id, const QString& name, QWidget* parent = nullptr); signals: - void completeStateChanged(void); + void clicked(bool); + +private: + QString m_tagId; }; #endif diff --git a/kmymoney/widgets/ktreewidgetfilterlinewidget.cpp b/kmymoney/widgets/ktreewidgetfilterlinewidget.cpp index e8513d620..d29d9f49d 100644 --- a/kmymoney/widgets/ktreewidgetfilterlinewidget.cpp +++ b/kmymoney/widgets/ktreewidgetfilterlinewidget.cpp @@ -1,42 +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 . */ #include "ktreewidgetfilterlinewidget.h" -#include #include #include KTreeWidgetFilterLineWidget::KTreeWidgetFilterLineWidget(QWidget* parent, QTreeWidget* treeWidget) : KTreeWidgetSearchLineWidget(parent, treeWidget) { } void KTreeWidgetFilterLineWidget::createWidgets() { KTreeWidgetSearchLineWidget::createWidgets(); // The layout pointer is stored in the private class, so we do not have access to it directly // => use findChild() QLabel* label = findChild(); if (!label) return; label->setText(i18nc("Filter widget label", "Fi<er:")); } diff --git a/kmymoney/widgets/ktreewidgetfilterlinewidget.h b/kmymoney/widgets/ktreewidgetfilterlinewidget.h index d8f96be10..321a7ed9a 100644 --- a/kmymoney/widgets/ktreewidgetfilterlinewidget.h +++ b/kmymoney/widgets/ktreewidgetfilterlinewidget.h @@ -1,41 +1,42 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2015 Christian David + * (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. * * 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 = 0, QTreeWidget *treeWidget = 0); + explicit KTreeWidgetFilterLineWidget(QWidget* parent = nullptr, 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.cpp b/kmymoney/widgets/onlinejobmessagesview.cpp index edfe825fc..0d75b163c 100644 --- a/kmymoney/widgets/onlinejobmessagesview.cpp +++ b/kmymoney/widgets/onlinejobmessagesview.cpp @@ -1,41 +1,41 @@ /* * 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 . */ #include "onlinejobmessagesview.h" #include "ui_onlinejobmessagesview.h" onlineJobMessagesView::onlineJobMessagesView(QWidget* parent) : QWidget(parent), ui(new Ui::onlineJobMessageView) { ui->setupUi(this); - connect(ui->closeButton, SIGNAL(pressed()), this, SLOT(close())); + connect(ui->closeButton, &QAbstractButton::pressed, this, &QWidget::close); } void onlineJobMessagesView::setModel(QAbstractItemModel* model) { ui->tableView->setModel(model); // Enlarge the description column ui->tableView->setColumnWidth(2, 4*ui->tableView->columnWidth(2)); } onlineJobMessagesView::~onlineJobMessagesView() { delete ui; } diff --git a/kmymoney/widgets/onlinejobmessagesview.h b/kmymoney/widgets/onlinejobmessagesview.h index 7645912d4..78c53f52a 100644 --- a/kmymoney/widgets/onlinejobmessagesview.h +++ b/kmymoney/widgets/onlinejobmessagesview.h @@ -1,45 +1,47 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2015 Christian Dávid + * (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. * * 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 onlineJobMessageView; } class KMM_WIDGETS_EXPORT onlineJobMessagesView : public QWidget { - Q_OBJECT + Q_OBJECT + Q_DISABLE_COPY(onlineJobMessagesView) public: - onlineJobMessagesView(QWidget* parent = 0); + explicit onlineJobMessagesView(QWidget* parent = nullptr); ~onlineJobMessagesView(); void setModel(QAbstractItemModel* model); protected: Ui::onlineJobMessageView* ui; }; #endif // ONLINEJOBMESSAGESVIEW_H diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/qwidgetcontainer.cpp similarity index 59% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/qwidgetcontainer.cpp index d4fd1f27a..5070b727f 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/qwidgetcontainer.cpp @@ -1,28 +1,59 @@ /*************************************************************************** - transactioneditorcontainer.cpp - ---------- - begin : Wed Jun 07 2006 + qwidgetcontainer.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 "transactioneditorcontainer.h" +#include "qwidgetcontainer.h" // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +using namespace KMyMoneyRegister; + +QWidgetContainer::QWidgetContainer() +{ +} + +QWidget* QWidgetContainer::haveWidget(const QString& name) const +{ + QWidgetContainer::const_iterator it_w; + it_w = find(name); + if (it_w != end()) + return *it_w; + return 0; +} + +void QWidgetContainer::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(); + } + } +} diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/qwidgetcontainer.h similarity index 69% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/qwidgetcontainer.h index d4fd1f27a..ae7388c0a 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/qwidgetcontainer.h @@ -1,28 +1,47 @@ /*************************************************************************** - transactioneditorcontainer.cpp + qwidgetcontainer.h ---------- - begin : Wed Jun 07 2006 + 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 "transactioneditorcontainer.h" +#ifndef QWIDGETCONTAINER_H +#define QWIDGETCONTAINER_H // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +class QString; +class QWidget; + +namespace KMyMoneyRegister +{ + struct QWidgetContainer : public QMap + { + Q_DISABLE_COPY(QWidgetContainer) + QWidgetContainer(); + QWidget* haveWidget(const QString& name) const; + void removeOrphans(); + }; +} // namespace + +#endif diff --git a/kmymoney/widgets/register.cpp b/kmymoney/widgets/register.cpp index ffd65d362..582aa8fa2 100644 --- a/kmymoney/widgets/register.cpp +++ b/kmymoney/widgets/register.cpp @@ -1,2129 +1,1884 @@ /*************************************************************************** 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 "selectedtransactions.h" #include "scheduledtransaction.h" #include "kmymoneyglobalsettings.h" +#include "mymoneymoney.h" #include "mymoneyfile.h" +#include "groupmarkers.h" +#include "fancydategroupmarkers.h" +#include "registeritemdelegate.h" +#include "itemptrvector.h" #include "mymoneyenums.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 -}; +#include "widgetenums.h" using namespace KMyMoneyRegister; +using namespace eWidgets; 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; +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(eRegister::DetailColumn::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; + eTransaction::Column m_lastCol; + QList m_sortOrder; + QRect m_lastRepaintRect; + eRegister::DetailColumn m_detailsColumnType; + + }; + + Register::Register(QWidget *parent) : + TransactionEditorContainer(parent), + d_ptr(new RegisterPrivate) + { + 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((int)eTransaction::Column::LastColumn); + setSelectionBehavior(QAbstractItemView::SelectRows); + setAcceptDrops(true); + setShowGrid(false); + setContextMenuPolicy(Qt::DefaultContextMenu); + + setHorizontalHeaderItem((int)eTransaction::Column::Number, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Date, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Account, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Security, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Detail, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::ReconcileFlag, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Payment, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Deposit, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Quantity, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Price, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Value, new QTableWidgetItem()); + setHorizontalHeaderItem((int)eTransaction::Column::Balance, new QTableWidgetItem()); + + // keep the following list in sync with KMyMoneyRegister::Column in transaction.h + horizontalHeaderItem((int)eTransaction::Column::Number)->setText(i18nc("Cheque Number", "No.")); + horizontalHeaderItem((int)eTransaction::Column::Date)->setText(i18n("Date")); + horizontalHeaderItem((int)eTransaction::Column::Account)->setText(i18n("Account")); + horizontalHeaderItem((int)eTransaction::Column::Security)->setText(i18n("Security")); + horizontalHeaderItem((int)eTransaction::Column::Detail)->setText(i18n("Details")); + horizontalHeaderItem((int)eTransaction::Column::ReconcileFlag)->setText(i18n("C")); + horizontalHeaderItem((int)eTransaction::Column::Payment)->setText(i18n("Payment")); + horizontalHeaderItem((int)eTransaction::Column::Deposit)->setText(i18n("Deposit")); + horizontalHeaderItem((int)eTransaction::Column::Quantity)->setText(i18n("Quantity")); + horizontalHeaderItem((int)eTransaction::Column::Price)->setText(i18n("Price")); + horizontalHeaderItem((int)eTransaction::Column::Value)->setText(i18n("Value")); + horizontalHeaderItem((int)eTransaction::Column::Balance)->setText(i18n("Balance")); + + verticalHeader()->hide(); + + horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); + horizontalHeader()->setSortIndicatorShown(false); + horizontalHeader()->setSectionsMovable(false); + horizontalHeader()->setSectionsClickable(false); + horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, &QTableWidget::cellClicked, this, static_cast(&Register::selectItem)); + connect(this, &QTableWidget::cellDoubleClicked, this, &Register::slotDoubleClicked); + } + + 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; + setUpdatesEnabled(false); + + for (auto i = 0; i < (int)eTransaction::Column::LastColumn; ++i) + hideColumn(i); + + d->m_needInitialColumnResize = true; + + d->m_lastCol = static_cast(0); + QList::const_iterator it_c; + for (it_c = cols.begin(); it_c != cols.end(); ++it_c) { + if ((*it_c) > eTransaction::Column::LastColumn) + continue; + showColumn((int)*it_c); + if (*it_c > d->m_lastCol) + d->m_lastCol = *it_c; + } - std::sort(begin(), end(), item_cmp); + setUpdatesEnabled(true); } -} -bool ItemPtrVector::item_cmp(RegisterItem* i1, RegisterItem* i2) -{ - 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) { - TransactionSortField sortField = static_cast(*it); - switch (qAbs(static_cast(sortField))) { - case PostDateSort: - rc = i2->sortPostDate().daysTo(i1->sortPostDate()); - break; + void Register::setupRegister(const MyMoneyAccount& account, bool showAccountColumn) + { + Q_D(Register); + d->m_account = account; + setUpdatesEnabled(false); - case EntryDateSort: - rc = i2->sortEntryDate().daysTo(i1->sortEntryDate()); - break; + for (auto i = 0; i < (int)eTransaction::Column::LastColumn; ++i) + hideColumn(i); - case PayeeSort: - rc = QString::localeAwareCompare(i1->sortPayee(), i2->sortPayee()); - break; + horizontalHeaderItem((int)eTransaction::Column::Payment)->setText(i18nc("Payment made from account", "Payment")); + horizontalHeaderItem((int)eTransaction::Column::Deposit)->setText(i18nc("Deposit into account", "Deposit")); - case ValueSort: - if (i1->sortValue() == i2->sortValue()) - rc = 0; - else if (i1->sortValue() < i2->sortValue()) - rc = -1; - else - rc = 1; - break; + if (account.id().isEmpty()) { + setUpdatesEnabled(true); + return; + } - 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; + d->m_needInitialColumnResize = true; - case EntryOrderSort: - rc = qstrcmp(i1->sortEntryOrder().toLatin1(), i2->sortEntryOrder().toLatin1()); - break; + // turn on standard columns + showColumn((int)eTransaction::Column::Date); + showColumn((int)eTransaction::Column::Detail); + showColumn((int)eTransaction::Column::ReconcileFlag); - case TypeSort: - rc = i1->sortType() - i2->sortType(); + // balance + switch (account.accountType()) { + case Account::Stock: break; - - case CategorySort: - rc = QString::localeAwareCompare(i1->sortCategory(), i2->sortCategory()); + default: + showColumn((int)eTransaction::Column::Balance); break; + } - case ReconcileStateSort: - rc = static_cast(i1->sortReconcileState()) - static_cast(i2->sortReconcileState()); + // 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((int)eTransaction::Column::Number); break; - case SecuritySort: - rc = QString::localeAwareCompare(i1->sortSecurity(), i2->sortSecurity()); + case Account::Checkings: + case Account::CreditCard: + showColumn((int)eTransaction::Column::Number); break; default: - qDebug("Invalid sort key %d", *it); + hideColumn((int)eTransaction::Column::Number); 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; - } + switch (account.accountType()) { + case Account::Income: + case Account::Expense: + showAccountColumn = true; + break; + default: + break; } - // the items differ for this sort key so we can return a result - if (rc != 0) { - return (*it < 0) ? rc >= 0 : rc < 0; + if (showAccountColumn) + showColumn((int)eTransaction::Column::Account); + + // Security, activity, payment, deposit, amount, price and value column + switch (account.accountType()) { + default: + showColumn((int)eTransaction::Column::Payment); + showColumn((int)eTransaction::Column::Deposit); + break; + + case Account::Investment: + showColumn((int)eTransaction::Column::Security); + showColumn((int)eTransaction::Column::Quantity); + showColumn((int)eTransaction::Column::Price); + showColumn((int)eTransaction::Column::Value); + break; } - } - if (rc == 0) { - rc = qstrcmp(i1->sortEntryOrder().toLatin1(), i2->sortEntryOrder().toLatin1()); - } + // headings + switch (account.accountType()) { + case Account::CreditCard: + horizontalHeaderItem((int)eTransaction::Column::Payment)->setText(i18nc("Payment made with credit card", "Charge")); + horizontalHeaderItem((int)eTransaction::Column::Deposit)->setText(i18nc("Payment towards credit card", "Payment")); + break; + case Account::Asset: + case Account::AssetLoan: + horizontalHeaderItem((int)eTransaction::Column::Payment)->setText(i18nc("Decrease of asset/liability value", "Decrease")); + horizontalHeaderItem((int)eTransaction::Column::Deposit)->setText(i18nc("Increase of asset/liability value", "Increase")); + break; + case Account::Liability: + case Account::Loan: + horizontalHeaderItem((int)eTransaction::Column::Payment)->setText(i18nc("Increase of asset/liability value", "Increase")); + horizontalHeaderItem((int)eTransaction::Column::Deposit)->setText(i18nc("Decrease of asset/liability value", "Decrease")); + break; + case Account::Income: + case Account::Expense: + horizontalHeaderItem((int)eTransaction::Column::Payment)->setText(i18n("Income")); + horizontalHeaderItem((int)eTransaction::Column::Deposit)->setText(i18n("Expense")); + break; - return rc < 0; -} + default: + break; + } -GroupMarker::GroupMarker(Register *parent, const QString& txt) : - RegisterItem(parent), - m_txt(txt), - m_showDate(false), - m_erroneous(false) -{ - int h; - if (m_parent) { - h = m_parent->rowHeightHint(); - } else { - QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); - h = fm.lineSpacing() + 6; - } + d->m_lastCol = eTransaction::Column::Balance; - if (m_bg && (m_bg->height() != h)) { - delete m_bg; - m_bg = 0; + setUpdatesEnabled(true); } - // 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); + bool Register::focusNextPrevChild(bool next) + { + return QFrame::focusNextPrevChild(next); } - ++m_bgRefCnt; -} - -GroupMarker::~GroupMarker() -{ - --m_bgRefCnt; - if (!m_bgRefCnt) { - delete m_bg; - m_bg = 0; + void Register::setSortOrder(const QString& order) + { + Q_D(Register); + const QStringList orderList = order.split(',', QString::SkipEmptyParts); + QStringList::const_iterator it; + d->m_sortOrder.clear(); + for (it = orderList.constBegin(); it != orderList.constEnd(); ++it) { + d->m_sortOrder << static_cast((*it).toInt()); + } } -} - -void GroupMarker::paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) -{ - 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)); + const QList& Register::sortOrder() const + { + Q_D(const Register); + return d->m_sortOrder; + } - 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); + void Register::sortItems() + { + Q_D(Register); + if (d->m_items.count() == 0) + return; - // now write the text - painter->setPen(option.palette.color(isErroneous() ? QPalette::HighlightedText : QPalette::Text)); - QFont font = painter->font(); - font.setBold(true); - painter->setFont(font); + // sort the array of pointers to the transactions + d->m_items.sort(); - painter->drawText(cellRect, Qt::AlignVCenter | Qt::AlignCenter, m_txt); + // 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]; + if (!item) + continue; - cellRect.setHeight(m_bg->height()); + if (!d->m_firstItem) + d->m_firstItem = item; + d->m_lastItem = item; + if (prev) + prev->setNextItem(item); + item->setPrevItem(prev); + item->setNextItem(0); + prev = item; + } - // now it's time to draw the background - painter->drawPixmap(cellRect, *m_bg); + // update the balance visibility settings + item = d->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(); + } - // 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)); + // force update of the item index (row to item array) + d->m_listsDirty = true; } - 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 -{ - if (!m_visible) - return 0; + eTransaction::Column Register::lastCol() const + { + Q_D(const Register); + return d->m_lastCol; + } - return m_bg->height(); -} + SortField Register::primarySortKey() const + { + Q_D(const Register); + if (!d->m_sortOrder.isEmpty()) + return static_cast(d->m_sortOrder.first()); + return SortField::Unknown; + } -StatementGroupMarker::StatementGroupMarker(Register* parent, CashFlowDirection dir, const QDate& date, const QString& txt) : - FancyDateGroupMarker(parent, date, txt), - m_dir(dir) -{ - m_showDate = true; -} -FancyDateGroupMarker::FancyDateGroupMarker(Register* parent, const QDate& date, const QString& txt) : - GroupMarker(parent, txt), - m_date(date) -{ -} + void Register::clear() + { + Q_D(Register); + d->m_firstErroneous = d->m_lastErroneous = 0; + d->m_ensureVisibleItem = 0; -FiscalYearGroupMarker::FiscalYearGroupMarker(Register* parent, const QDate& date, const QString& txt) : - FancyDateGroupMarker(parent, date, txt) -{ -} + d->m_items.clear(); -SimpleDateGroupMarker::SimpleDateGroupMarker(Register* parent, const QDate& date, const QString& txt) : - FancyDateGroupMarker(parent, date, txt) -{ -} + RegisterItem* p; + while ((p = firstItem()) != 0) { + delete p; + } -int SimpleDateGroupMarker::rowHeightHint() const -{ - if (!m_visible) - return 0; + d->m_firstItem = d->m_lastItem = 0; - return RegisterItem::rowHeightHint() / 2; -} + d->m_listsDirty = true; + d->m_selectAnchor = 0; + d->m_focusItem = 0; -void SimpleDateGroupMarker::paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) -{ - QRect cellRect = option.rect; - painter->save(); - cellRect.setWidth(m_parent->viewport()->width()); - cellRect.setHeight(m_parent->rowHeight(index.row() + m_startRow)); - - 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(); -} +#ifndef KMM_DESIGNER + // recalculate row height hint + QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); + d->m_rowHeightHint = fm.lineSpacing() + 6; +#endif -TypeGroupMarker::TypeGroupMarker(Register* parent, CashFlowDirection dir, Account accType) : - GroupMarker(parent), - m_dir(dir) -{ - switch (dir) { - case Deposit: - m_txt = i18nc("Deposits onto account", "Deposits"); - if (accType == Account::CreditCard) { - m_txt = i18nc("Payments towards credit card", "Payments"); - } - break; - case Payment: - m_txt = i18nc("Payments made from account", "Payments"); - if (accType == Account::CreditCard) { - m_txt = i18nc("Payments made with credit card", "Charges"); - } - break; - default: - qDebug("Unknown CashFlowDirection %d for TypeGroupMarker constructor", dir); - break; + d->m_needInitialColumnResize = true; + d->m_needResize = true; + updateRegister(true); } -} -PayeeGroupMarker::PayeeGroupMarker(Register* parent, const QString& name) : - GroupMarker(parent, name) -{ -} - -CategoryGroupMarker::CategoryGroupMarker(Register* parent, const QString& category) : - GroupMarker(parent, category) -{ -} + void Register::insertItemAfter(RegisterItem*p, RegisterItem* prev) + { + Q_D(Register); + RegisterItem* next = 0; + if (!prev) + prev = lastItem(); -ReconcileGroupMarker::ReconcileGroupMarker(Register* parent, eMyMoney::Split::State state) : - GroupMarker(parent), - m_state(state) -{ - switch (state) { - case eMyMoney::Split::State::NotReconciled: - m_txt = i18nc("Reconcile state 'Not reconciled'", "Not reconciled"); - break; - case eMyMoney::Split::State::Cleared: - m_txt = i18nc("Reconcile state 'Cleared'", "Cleared"); - break; - case eMyMoney::Split::State::Reconciled: - m_txt = i18nc("Reconcile state 'Reconciled'", "Reconciled"); - break; - case eMyMoney::Split::State::Frozen: - m_txt = i18nc("Reconcile state 'Frozen'", "Frozen"); - break; - default: - m_txt = i18nc("Unknown reconcile state", "Unknown"); - break; + 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 (prev == d->m_lastItem) + d->m_lastItem = p; + + d->m_listsDirty = true; + d->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); + + if (!d->m_firstItem) + d->m_firstItem = p; + d->m_lastItem = p; + d->m_listsDirty = true; + d->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(); + + // 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); + if (-1 != i) { + d->m_items[i] = 0; + } + d->m_listsDirty = true; + d->m_needResize = true; } -} - -RegisterItemDelegate::RegisterItemDelegate(Register *parent) : QStyledItemDelegate(parent), m_register(parent) -{ -} -RegisterItemDelegate::~RegisterItemDelegate() -{ -} + RegisterItem* Register::firstItem() const + { + Q_D(const Register); + return d->m_firstItem; + } -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); + RegisterItem* Register::nextItem(RegisterItem* item) const + { + return item->nextItem(); } -} -Register::Register(QWidget *parent) : - TransactionEditorContainer(parent), - 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) -{ - // 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))); -} + RegisterItem* Register::lastItem() const + { + Q_D(const Register); + return d->m_lastItem; + } -Register::~Register() -{ - clear(); -} + void Register::setupItemIndex(int rowCount) + { + Q_D(Register); + // setup index array + d->m_itemIndex.clear(); + d->m_itemIndex.reserve(rowCount); -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; + // 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]; + if (!item) + continue; + if (!d->m_firstItem) + d->m_firstItem = item; + d->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); + } } } - return QTableWidget::eventFilter(o, e); -} - -void Register::setupRegister(const MyMoneyAccount& account, const QList& cols) -{ - m_account = account; - setUpdatesEnabled(false); - - for (int i = 0; i < MaxColumns; ++i) - hideColumn(i); - m_needInitialColumnResize = true; + 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]; + if (!item) + continue; + if (item->isVisible()) { + item->setAlternate(alternate); + alternate ^= true; + } + } + } - 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 > m_lastCol) - m_lastCol = *it_c; + 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(); + } } - setUpdatesEnabled(true); -} + void Register::updateRegister(bool forceUpdateRowHeight) + { + Q_D(Register); + if (d->m_listsDirty || forceUpdateRowHeight) { + // don't get in here recursively + d->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]; + 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; + } + } -void Register::setupRegister(const MyMoneyAccount& account, bool showAccountColumn) -{ - m_account = account; - setUpdatesEnabled(false); + updateAlternate(); - for (int i = 0; i < MaxColumns; ++i) - hideColumn(i); + // create item index + setupItemIndex(rowCount); - horizontalHeaderItem(PaymentColumn)->setText(i18nc("Payment made from account", "Payment")); - horizontalHeaderItem(DepositColumn)->setText(i18nc("Deposit into account", "Deposit")); + bool needUpdateHeaders = (QTableWidget::rowCount() != rowCount) | forceUpdateRowHeight; - if (account.id().isEmpty()) { - setUpdatesEnabled(true); - return; - } - - 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; - } - - m_lastCol = BalanceColumn; - - setUpdatesEnabled(true); -} + // setup QTable. Make sure to suppress screen updates for now + setRowCount(rowCount); -bool Register::focusNextPrevChild(bool next) -{ - return QFrame::focusNextPrevChild(next); -} + // 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) { + RegisterItem* item = itemAtRow(i); + if (item->isVisible()) { + showRow(i); + } else { + hideRow(i); + } + verticalHeader()->resizeSection(i, item->rowHeightHint()); + } + verticalHeader()->setUpdatesEnabled(true); + } -void Register::setSortOrder(const QString& order) -{ - const QStringList orderList = order.split(',', QString::SkipEmptyParts); - QStringList::const_iterator it; - m_sortOrder.clear(); - for (it = orderList.constBegin(); it != orderList.constEnd(); ++it) { - m_sortOrder << static_cast((*it).toInt()); - } -} + // force resizeing of the columns if necessary + if (d->m_needInitialColumnResize) { + QTimer::singleShot(0, this, SLOT(resize())); + d->m_needInitialColumnResize = false; + } else { + update(); -void Register::sortItems() -{ - if (m_items.count() == 0) - return; - - // sort the array of pointers to the transactions - m_items.sort(); - - // update the next/prev item chains - RegisterItem* prev = 0; - RegisterItem* item; - m_firstItem = m_lastItem = 0; - for (QVector::size_type i = 0; i < m_items.size(); ++i) { - item = m_items[i]; - if (!item) - continue; - - 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 = m_lastItem; - bool showBalance = true; - while (item) { - Transaction* t = dynamic_cast(item); - if (t) { - t->setShowBalance(showBalance); - if (!t->isVisible()) { - showBalance = false; + // 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())); } } - item = item->prevItem(); } - // force update of the item index (row to item array) - m_listsDirty = true; -} - -TransactionSortField Register::primarySortKey() const -{ - if (!m_sortOrder.isEmpty()) - return static_cast(m_sortOrder.first()); - return UnknownSort; -} - - -void Register::clear() -{ - m_firstErroneous = m_lastErroneous = 0; - m_ensureVisibleItem = 0; - - m_items.clear(); + int Register::rowHeightHint() const + { + Q_D(const Register); + if (!d->m_rowHeightHint) { + qDebug("Register::rowHeightHint(): m_rowHeightHint is zero!!"); + } + return d->m_rowHeightHint; + } - RegisterItem* p; - while ((p = firstItem()) != 0) { - delete p; + void Register::focusInEvent(QFocusEvent* ev) + { + Q_D(const Register); + QTableWidget::focusInEvent(ev); + if (d->m_focusItem) { + d->m_focusItem->setFocus(true, false); + } } - m_firstItem = m_lastItem = 0; + bool Register::event(QEvent* event) + { + if (event->type() == QEvent::ToolTip) { + QHelpEvent *helpEvent = static_cast(event); - m_listsDirty = true; - m_selectAnchor = 0; - m_focusItem = 0; + // 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; -#ifndef KMM_DESIGNER - // recalculate row height hint - QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); - m_rowHeightHint = fm.lineSpacing() + 6; -#endif + int col = columnAt(helpEvent->x()); + RegisterItem* item = itemAtRow(row); + if (!item) + return true; - m_needInitialColumnResize = true; - m_needResize = true; - updateRegister(true); -} + row = row - item->startRow(); -void Register::insertItemAfter(RegisterItem*p, RegisterItem* prev) -{ - RegisterItem* next = 0; - if (!prev) - prev = lastItem(); + QString msg; + QRect rect; + if (!item->maybeTip(helpEvent->pos(), row, col, rect, msg)) + return true; - if (prev) { - next = prev->nextItem(); - prev->setNextItem(p); + if (!msg.isEmpty()) { + QToolTip::showText(helpEvent->globalPos(), msg); + } else { + QToolTip::hideText(); + event->ignore(); + } + return true; + } + return TransactionEditorContainer::event(event); } - if (next) - next->setPrevItem(p); - p->setPrevItem(prev); - p->setNextItem(next); - - if (!m_firstItem) - m_firstItem = p; - if (!m_lastItem) - m_lastItem = p; + void Register::focusOutEvent(QFocusEvent* ev) + { + Q_D(Register); + if (d->m_focusItem) { + d->m_focusItem->setFocus(false, false); + } + QTableWidget::focusOutEvent(ev); + } - if (prev == m_lastItem) - m_lastItem = p; + void Register::resizeEvent(QResizeEvent* ev) + { + TransactionEditorContainer::resizeEvent(ev); + resize((int)eTransaction::Column::Detail, true); + } - m_listsDirty = true; - m_needResize = true; -} + void Register::resize() + { + resize((int)eTransaction::Column::Detail); + } -void Register::addItem(RegisterItem* p) -{ - RegisterItem* q = lastItem(); - if (q) - q->setNextItem(p); - p->setPrevItem(q); - p->setNextItem(0); - - m_items.append(p); - - if (!m_firstItem) - m_firstItem = p; - m_lastItem = p; - m_listsDirty = true; - m_needResize = true; -} + void Register::resize(int col, bool force) + { + Q_D(Register); + if (!d->m_needResize && !force) + return; -void Register::removeItem(RegisterItem* p) -{ - // 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 == 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 = m_items.indexOf(p); - if (-1 != i) { - m_items[i] = 0; - } - m_listsDirty = true; - m_needResize = true; -} + d->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 -RegisterItem* Register::firstItem() const -{ - return m_firstItem; -} + // check which space we need + if (columnWidth((int)eTransaction::Column::Number)) + adjustColumn((int)eTransaction::Column::Number); + if (columnWidth((int)eTransaction::Column::Account)) + adjustColumn((int)eTransaction::Column::Account); + if (columnWidth((int)eTransaction::Column::Payment)) + adjustColumn((int)eTransaction::Column::Payment); + if (columnWidth((int)eTransaction::Column::Deposit)) + adjustColumn((int)eTransaction::Column::Deposit); + if (columnWidth((int)eTransaction::Column::Quantity)) + adjustColumn((int)eTransaction::Column::Quantity); + if (columnWidth((int)eTransaction::Column::Balance)) + adjustColumn((int)eTransaction::Column::Balance); + if (columnWidth((int)eTransaction::Column::Price)) + adjustColumn((int)eTransaction::Column::Price); + if (columnWidth((int)eTransaction::Column::Value)) + adjustColumn((int)eTransaction::Column::Value); + + // 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((int)eTransaction::Column::Payment)) + ewidth = columnWidth((int)eTransaction::Column::Payment); + if (ewidth < columnWidth((int)eTransaction::Column::Deposit)) + ewidth = columnWidth((int)eTransaction::Column::Deposit); + if (ewidth < columnWidth((int)eTransaction::Column::Quantity)) + ewidth = columnWidth((int)eTransaction::Column::Quantity); + if (dwidth < columnWidth((int)eTransaction::Column::Balance)) + dwidth = columnWidth((int)eTransaction::Column::Balance); + if (ewidth < columnWidth((int)eTransaction::Column::Price)) + ewidth = columnWidth((int)eTransaction::Column::Price); + if (dwidth < columnWidth((int)eTransaction::Column::Value)) + dwidth = columnWidth((int)eTransaction::Column::Value); + int swidth = columnWidth((int)eTransaction::Column::Security); + if (swidth > 0) { + adjustColumn((int)eTransaction::Column::Security); + swidth = columnWidth((int)eTransaction::Column::Security); + } -RegisterItem* Register::nextItem(RegisterItem* item) const -{ - return item->nextItem(); -} + adjustColumn((int)eTransaction::Column::Date); -RegisterItem* Register::lastItem() const -{ - return m_lastItem; -} +#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()) { + QPushButton *pushButton = new QPushButton; + const int pushButtonSpacing = pushButton->sizeHint().width() + 5; + setColumnWidth((int)eTransaction::Column::Date, columnWidth((int)eTransaction::Column::Date) + 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((int)eTransaction::Column::Security) + 40; + } + delete pushButton; + } +#endif -void Register::setupItemIndex(int rowCount) -{ - // setup index array - m_itemIndex.clear(); - m_itemIndex.reserve(rowCount); - - // fill index array - rowCount = 0; - RegisterItem* prev = 0; - 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 (!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) { - m_itemIndex.push_back(item); + if (columnWidth((int)eTransaction::Column::Payment)) + setColumnWidth((int)eTransaction::Column::Payment, ewidth); + if (columnWidth((int)eTransaction::Column::Deposit)) + setColumnWidth((int)eTransaction::Column::Deposit, ewidth); + if (columnWidth((int)eTransaction::Column::Quantity)) + setColumnWidth((int)eTransaction::Column::Quantity, ewidth); + if (columnWidth((int)eTransaction::Column::Balance)) + setColumnWidth((int)eTransaction::Column::Balance, dwidth); + if (columnWidth((int)eTransaction::Column::Price)) + setColumnWidth((int)eTransaction::Column::Price, ewidth); + if (columnWidth((int)eTransaction::Column::Value)) + setColumnWidth((int)eTransaction::Column::Value, dwidth); + + if (columnWidth((int)eTransaction::Column::ReconcileFlag)) + setColumnWidth((int)eTransaction::Column::ReconcileFlag, 20); + + if (swidth > 0) + setColumnWidth((int)eTransaction::Column::Security, 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 -void Register::updateAlternate() const -{ - bool alternate = false; - 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; + for (auto i = 0; i < columnCount(); ++i) { + if (i == col) + continue; + + w -= columnWidth(i); } + setColumnWidth(col, w); } -} -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::forceUpdateLists() + { + Q_D(Register); + d->m_listsDirty = true; } -} -void Register::updateRegister(bool forceUpdateRowHeight) -{ - if (m_listsDirty || forceUpdateRowHeight) { - // don't get in here recursively - 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 < m_items.size(); ++i) { - RegisterItem* item = m_items[i]; + 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 (int)eTransaction::Column::Date: + 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]; if (!item) continue; - item->setStartRow(rowCount); - item->setNeedResize(); - rowCount += item->numRowsRegister(); - - if (item->isErroneous()) { - if (!m_firstErroneous) - m_firstErroneous = item; - m_lastErroneous = item; + 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; + } } } - updateAlternate(); - - // create item index - setupItemIndex(rowCount); + return w; +#endif + } - bool needUpdateHeaders = (QTableWidget::rowCount() != rowCount) | forceUpdateRowHeight; + void Register::adjustColumn(int col) + { + setColumnWidth(col, minimumColumnWidth(col)); + } - // setup QTable. Make sure to suppress screen updates for now - setRowCount(rowCount); + void Register::clearSelection() + { + unselectItems(); + TransactionEditorContainer::clearSelection(); + } - // 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 (int i = 0; i < rowCount; ++i) { - RegisterItem* item = itemAtRow(i); - if (item->isVisible()) { - showRow(i); - } else { - hideRow(i); + 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; + + RegisterItem* firstItem; + RegisterItem* lastItem; + firstItem = lastItem = 0; + for (int i = start; i <= end; ++i) { + RegisterItem* const item = d->m_items[i]; + if (item) { + if (selected != item->isSelected()) { + if (!firstItem) + firstItem = item; + item->setSelected(selected); + lastItem = item; } - verticalHeader()->resizeSection(i, item->rowHeightHint()); } - verticalHeader()->setUpdatesEnabled(true); } + } - // force resizeing of the columns if necessary - if (m_needInitialColumnResize) { - QTimer::singleShot(0, this, SLOT(resize())); - 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())); + RegisterItem* Register::itemAtRow(int row) const + { + Q_D(const Register); + if (row >= 0 && row < d->m_itemIndex.size()) { + return d->m_itemIndex[row]; } + return 0; } -} -int Register::rowHeightHint() const -{ - if (!m_rowHeightHint) { - qDebug("Register::rowHeightHint(): m_rowHeightHint is zero!!"); - } - return m_rowHeightHint; -} - -void Register::focusInEvent(QFocusEvent* ev) -{ - QTableWidget::focusInEvent(ev); - 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(); + 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]; + if (!item) + continue; + if (row >= item->startRow() && row < (item->startRow() + item->numRowsRegister())) + return i; } - return true; + return -1; } - return TransactionEditorContainer::event(event); -} -void Register::focusOutEvent(QFocusEvent* ev) -{ - 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) -{ - if (!m_needResize && !force) - return; - - 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 (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; + 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 (t) { + QString id; + if (t->isScheduled()) + id = t->transaction().id(); + SelectedTransaction s(t->transaction(), t->split(), id); + list << s; } - 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) + for (auto i = 0; i < d->m_items.size(); ++i) { + RegisterItem* const item = d->m_items[i]; + // make sure, we don't include the focus item twice + if (item == d->m_focusItem) continue; - setColumnWidth(i, w * colWidth / 100); - } - } -#endif - - for (int i = 0; i < columnCount(); ++i) { - if (i == col) - continue; - - w -= columnWidth(i); - } - setColumnWidth(col, w); -} - -int Register::minimumColumnWidth(int col) -{ - 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 (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 (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; } } - if (w < minWidth) { - w = minWidth; - break; - } } } - return w; -#endif -} - -void Register::adjustColumn(int col) -{ - setColumnWidth(col, minimumColumnWidth(col)); -} - -void Register::clearSelection() -{ - unselectItems(); - TransactionEditorContainer::clearSelection(); -} + QList Register::selectedItems() const + { + Q_D(const Register); + QList list; -void Register::doSelectItems(int from, int to, bool selected) -{ - 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 > (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 = m_items[i]; - if (item) { - if (selected != item->isSelected()) { - if (!firstItem) - firstItem = item; - item->setSelected(selected); - lastItem = item; + RegisterItem* item = d->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); + auto cnt = 0; + RegisterItem* item = d->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; + return; + } + d->m_mouseButton = e->button(); + d->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; + + // 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(); } -} -RegisterItem* Register::itemAtRow(int row) const -{ - if (row >= 0 && row < m_itemIndex.size()) { - return m_itemIndex[row]; + void Register::unselectItems(int from, int to) + { + doSelectItems(from, to, false); } - return 0; -} -int Register::rowToIndex(int row) const -{ - 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; + void Register::selectItems(int from, int to) + { + doSelectItems(from, to, true); } - return -1; -} -void Register::selectedTransactions(SelectedTransactions& list) const -{ - 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; - } - } + void Register::selectItem(int row, int col) + { + Q_D(Register); + if (row >= 0 && row < d->m_itemIndex.size()) { + RegisterItem* item = d->m_itemIndex[row]; - 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 == m_focusItem) - continue; - if (item && item->isSelected() && item->isVisible()) { + // 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; + 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) { - QString id; - if (t->isScheduled()) - id = t->transaction().id(); - SelectedTransaction s(t->transaction(), t->split(), id); - list << s; + if (!id.isEmpty()) { + if (t && col == (int)eTransaction::Column::ReconcileFlag && selectedItemsCount() == 1 && !t->isScheduled()) + emit reconcileStateColumnClicked(t); + } else { + emit emptyItemSelected(); + } } } } -} - -QList Register::selectedItems() const -{ - QList list; - RegisterItem* item = m_firstItem; - while (item) { - if (item && item->isSelected() && item->isVisible()) { - list << item; - } - item = item->nextItem(); + void Register::setAnchorItem(RegisterItem* anchorItem) + { + Q_D(Register); + d->m_selectAnchor = anchorItem; } - return list; -} - -int Register::selectedItemsCount() const -{ - int cnt = 0; - RegisterItem* item = m_firstItem; - while (item) { - if (item->isSelected() && item->isVisible()) - ++cnt; - item = item->nextItem(); - } - return cnt; -} - -void Register::mouseReleaseEvent(QMouseEvent *e) -{ - 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 (m_ignoreNextButtonRelease) { - m_ignoreNextButtonRelease = false; - return; - } - m_mouseButton = e->button(); - m_modifiers = QApplication::keyboardModifiers(); - QTableWidget::mouseReleaseEvent(e); -} - -void Register::contextMenuEvent(QContextMenuEvent *e) -{ - if (e->reason() == QContextMenuEvent::Mouse) { - // since mouse release event is not called, we need - // to reset the mouse button and the modifiers here - 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::selectItem(int row, int col) -{ - 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()) { - 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(); + bool Register::setFocusItem(RegisterItem* focusItem) + { + Q_D(Register); + if (focusItem && focusItem->canHaveFocus()) { + if (d->m_focusItem) { + d->m_focusItem->setFocus(false); + } + Transaction* item = dynamic_cast(focusItem); + if (d->m_focusItem != focusItem && item) { + emit focusChanged(item); } - } - } -} -void Register::setAnchorItem(RegisterItem* anchorItem) -{ - m_selectAnchor = anchorItem; -} + d->m_focusItem = focusItem; + d->m_focusItem->setFocus(true); + if (d->m_listsDirty) + updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm()); + ensureItemVisible(d->m_focusItem); + return true; + } else + return false; + } -bool Register::setFocusItem(RegisterItem* focusItem) -{ - if (focusItem && focusItem->canHaveFocus()) { - if (m_focusItem) { - m_focusItem->setFocus(false); + bool Register::setFocusToTop() + { + Q_D(Register); + RegisterItem* rgItem = d->m_firstItem; + while (rgItem) { + if (setFocusItem(rgItem)) + return true; + rgItem = rgItem->nextItem(); } - Transaction* item = dynamic_cast(focusItem); - if (m_focusItem != focusItem && item) { - emit focusChanged(item); - } - - m_focusItem = focusItem; - m_focusItem->setFocus(true); - if (m_listsDirty) - updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm()); - ensureItemVisible(m_focusItem); - return true; - } else return false; -} - -bool Register::setFocusToTop() -{ - RegisterItem* rgItem = m_firstItem; - while (rgItem) { - if (setFocusItem(rgItem)) - return true; - rgItem = rgItem->nextItem(); } - return false; -} -void Register::selectItem(RegisterItem* item, bool dontChangeSelections) -{ - if (!item) - return; - - Qt::MouseButtons buttonState = m_mouseButton; - Qt::KeyboardModifiers modifiers = m_modifiers; - m_mouseButton = Qt::NoButton; - m_modifiers = Qt::NoModifier; - - 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)) - || (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) - m_selectAnchor = item; - } + void Register::selectItem(RegisterItem* item, bool dontChangeSelections) + { + Q_D(Register); + if (!item) + return; - 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; + Qt::MouseButtons buttonState = d->m_mouseButton; + Qt::KeyboardModifiers modifiers = d->m_modifiers; + d->m_mouseButton = Qt::NoButton; + d->m_modifiers = Qt::NoModifier; - case Qt::ShiftModifier: - if (scheduledTransactionSelected || typeid(*item) == typeid(StdTransactionScheduled)) - okToSelect = false; + if (d->m_selectionMode == NoSelection) + return; + + if (item->isSelectable()) { + QString id = item->id(); + QList itemList = selectedItems(); + bool okToSelect = true; + auto 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)) { + if ((cnt != 1) || ((cnt == 1) && !item->isSelected())) { emit aboutToSelectItem(item, okToSelect); if (okToSelect) { // pointer 'item' might have changed. reconstruct it. item = itemById(id); unselectItems(); - selectItems(rowToIndex(m_selectAnchor->startRow()), rowToIndex(item->startRow())); + item->setSelected(true); setFocusItem(item); } - break; + } + if (okToSelect) + d->m_selectAnchor = item; + } + + if (d->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())); + 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; } } - } 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); - m_selectAnchor = item; + SelectedTransactions list(this); + emit transactionsSelected(list); } } - if (okToSelect) { - SelectedTransactions list(this); - emit transactionsSelected(list); - } } -} - -void Register::ensureItemVisible(RegisterItem* item) -{ - if (!item) - return; - m_ensureVisibleItem = item; - QTimer::singleShot(0, this, SLOT(slotEnsureItemVisible())); -} + void Register::ensureItemVisible(RegisterItem* item) + { + Q_D(Register); + if (!item) + return; -void Register::slotDoubleClicked(int row, int) -{ - if (row >= 0 && row < m_itemIndex.size()) { - RegisterItem* p = m_itemIndex[row]; - if (p->isSelectable()) { - m_ignoreNextButtonRelease = true; - // double click to start editing only works if the focus - // item is among the selected ones - if (!focusItem()) { - setFocusItem(p); - if (m_selectionMode != NoSelection) - p->setSelected(true); - } + d->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 (p->isSelectable()) { + d->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) + p->setSelected(true); + } - 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())); + if (d->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() -{ - // if clear() has been called since the timer was - // started, we just ignore the call - 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(m_ensureVisibleItem->startRow() + m_ensureVisibleItem->numRowsRegister() - 1, DetailColumn)); - scrollTo(model()->index(m_ensureVisibleItem->startRow(), DetailColumn)); -} + 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) + return; -TransactionSortField KMyMoneyRegister::textToSortOrder(const QString& text) -{ - for (int idx = 1; idx < static_cast(MaxSortFields); ++idx) { - if (text == i18n(sortOrderText[idx])) { - return static_cast(idx); - } + // 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, (int)eTransaction::Column::Detail)); + scrollTo(model()->index(d->m_ensureVisibleItem->startRow(), (int)eTransaction::Column::Detail)); } - return UnknownSort; -} -const QString KMyMoneyRegister::sortOrderToText(TransactionSortField idx) -{ - if (idx < PostDateSort || idx >= MaxSortFields) - idx = UnknownSort; - return i18n(sortOrderText[idx]); -} + QString Register::text(int /*row*/, int /*col*/) const + { + return QString("a"); + } -QString Register::text(int /*row*/, int /*col*/) const -{ - return QString("a"); -} + QWidget* Register::createEditor(int /*row*/, int /*col*/, bool /*initFromCell*/) const + { + return 0; + } -QWidget* Register::createEditor(int /*row*/, int /*col*/, bool /*initFromCell*/) const -{ - return 0; -} + void Register::setCellContentFromEditor(int /*row*/, int /*col*/) + { + } -void Register::setCellContentFromEditor(int /*row*/, int /*col*/) -{ -} + void Register::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/) + { + } -void Register::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/) -{ -} + RegisterItem* Register::focusItem() const + { + Q_D(const Register); + return d->m_focusItem; + } -void Register::arrangeEditWidgets(QMap& editWidgets, KMyMoneyRegister::Transaction* t) -{ - t->arrangeWidgetsInRegister(editWidgets); - ensureItemVisible(t); - // updateContents(); -} + RegisterItem* Register::anchorItem() const + { + Q_D(const Register); + return d->m_selectAnchor; + } -void Register::tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const -{ - t->tabOrderInRegister(tabOrderWidgets); -} + void Register::arrangeEditWidgets(QMap& editWidgets, KMyMoneyRegister::Transaction* t) + { + t->arrangeWidgetsInRegister(editWidgets); + ensureItemVisible(t); + // updateContents(); + } -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; + void Register::tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const + { + t->tabOrderInRegister(tabOrderWidgets); } - // 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); + 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()); } - // 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 -{ - if (id.isEmpty()) - return m_lastItem; + RegisterItem* Register::itemById(const QString& id) const + { + Q_D(const Register); + if (id.isEmpty()) + return d->m_lastItem; - 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; + for (QVector::size_type i = 0; i < d->m_items.size(); ++i) { + RegisterItem* item = d->m_items[i]; + if (!item) + continue; + if (item->id() == id) + return item; + } + return 0; } - return 0; -} -void Register::handleItemChange(RegisterItem* old, bool shift, bool control) -{ - if (m_selectionMode == MultiSelection) { - if (shift) { - selectRange(m_selectAnchor ? m_selectAnchor : old, - m_focusItem, false, true, (m_selectAnchor && !control) ? true : false); - } else if (!control) { - selectItem(m_focusItem, false); + void Register::handleItemChange(RegisterItem* old, bool shift, bool control) + { + Q_D(Register); + if (d->m_selectionMode == MultiSelection) { + if (shift) { + selectRange(d->m_selectAnchor ? d->m_selectAnchor : old, + d->m_focusItem, false, true, (d->m_selectAnchor && !control) ? true : false); + } else if (!control) { + selectItem(d->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; + 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 (!found) - swap = true; - } - if (swap) { - item = from; - from = to; - to = item; - if (!includeFirst) - to = to->prevItem(); + if (swap) { + item = from; + from = to; + to = item; + if (!includeFirst) + to = to->prevItem(); - } else if (!includeFirst) { - from = from->nextItem(); - } + } else if (!includeFirst) { + from = from->nextItem(); + } - if (clearSel) { - for (item = firstItem(); item; item = item->nextItem()) { - if (item->isSelected() && item->isVisible()) { - item->setSelected(false); + 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); + 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; } - if (item == to) - break; } -} -void Register::scrollPage(int key, Qt::KeyboardModifiers modifiers) -{ - RegisterItem* oldFocusItem = m_focusItem; - - // make sure we have a focus item - if (!m_focusItem) - setFocusItem(m_firstItem); - if (!m_focusItem && m_firstItem) - setFocusItem(m_firstItem->nextItem()); - if (!m_focusItem) - return; - - 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()); + void Register::scrollPage(int key, Qt::KeyboardModifiers modifiers) + { + Q_D(Register); + RegisterItem* oldFocusItem = d->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) + return; + + RegisterItem* item = d->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; 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()); + break; + + case Qt::Key_End: + item = d->m_lastItem; while ((!item->isSelectable() || !item->isVisible()) && item->prevItem()) item = item->prevItem(); - } - break; + break; + } - case Qt::Key_Up: - if (item->prevItem()) { - do { - item = item->prevItem(); - } while ((!item->isSelectable() || !item->isVisible()) && item->prevItem()); + // 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(); } - 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 = m_firstItem; - while ((!item->isSelectable() || !item->isVisible()) && item->nextItem()) - item = item->nextItem(); - break; - - case Qt::Key_End: - 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; + + 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) + selectItem(item); + } - if (!(modifiers & Qt::ShiftModifier) || !m_selectAnchor) - m_selectAnchor = item; + void Register::keyPressEvent(QKeyEvent* ev) + { + Q_D(Register); + switch (ev->key()) { + case Qt::Key_Space: + if (d->m_selectionMode != NoSelection) { + // get the state out of the event ... + d->m_modifiers = ev->modifiers(); + // ... and pretend that we have pressed the left mouse button ;) + d->m_mouseButton = Qt::LeftButton; + selectItem(d->m_focusItem); + } + break; - setFocusItem(item); + 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; - 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); + default: + QTableWidget::keyPressEvent(ev); + break; + } } - if (m_focusItem && !m_focusItem->isSelected() && m_selectionMode == SingleSelection) - selectItem(item); + 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; + } -void Register::keyPressEvent(QKeyEvent* ev) -{ - switch (ev->key()) { - case Qt::Key_Space: - if (m_selectionMode != NoSelection) { - // get the state out of the event ... - m_modifiers = ev->modifiers(); - // ... and pretend that we have pressed the left mouse button ;) - 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; - } -} + 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; -Transaction* Register::transactionFactory(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) -{ - Transaction* t = 0; - MyMoneySplit s = split; + 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; - if (parent->account() == MyMoneyAccount()) { - t = new KMyMoneyRegister::StdTransaction(parent, transaction, s, uniqueId); + case Account::CertificateDep: + case Account::MoneyMarket: + case Account::Stock: + default: + qDebug("Register::transactionFactory: invalid accountTypeE %d", (int)parent->account().accountType()); + break; + } 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 SortField::PostDate: + case SortField::EntryDate: + 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, eRegister::CashFlowDirection::Deposit, d->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) + balance = -balance; + auto txt = i18n("Online Statement Balance: %1", balance.formatMoney(d->m_account.fraction())); + + KMyMoneyRegister::StatementGroupMarker *p = new KMyMoneyRegister::StatementGroupMarker(this, eRegister::CashFlowDirection::Deposit, QDate::fromString(d->m_account.value("lastImportedTransactionDate"), Qt::ISODate), txt); + + p->setErroneous(!MyMoneyFile::instance()->hasMatchingOnlineBalance(d->m_account)); + } -void Register::addGroupMarkers() -{ - 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 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 (m_account.lastReconciliationDate().isValid()) - new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, m_account.lastReconciliationDate(), i18n("Last reconciliation")); - - 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; - QString txt = i18n("Online Statement Balance: %1", balance.formatMoney(m_account.fraction())); - - KMyMoneyRegister::StatementGroupMarker *p = new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, QDate::fromString(m_account.value("lastImportedTransactionDate"), Qt::ISODate), txt); - - 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; - 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")); + case SortField::Type: + if (KMyMoneyGlobalSettings::showFancyMarker()) { + new KMyMoneyRegister::TypeGroupMarker(this, eRegister::CashFlowDirection::Deposit, d->m_account.accountType()); + new KMyMoneyRegister::TypeGroupMarker(this, eRegister::CashFlowDirection::Payment, d->m_account.accountType()); + } + break; - } 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 SortField::ReconcileState: + 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 KMyMoneyRegister::TypeSort: - if (KMyMoneyGlobalSettings::showFancyMarker()) { - new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Deposit, m_account.accountType()); - new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Payment, m_account.accountType()); - } - break; - - 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 KMyMoneyRegister::PayeeSort: - if (KMyMoneyGlobalSettings::showFancyMarker()) { - while (p) { - t = dynamic_cast(p); - if (t) { - list[t->sortPayee()] = 1; + case SortField::Payee: + if (KMyMoneyGlobalSettings::showFancyMarker()) { + while (p) { + t = dynamic_cast(p); + if (t) { + list[t->sortPayee()] = 1; + } + p = p->nextItem(); } - p = p->nextItem(); - } - for (it = list.constBegin(); it != list.constEnd(); ++it) { - name = it.key(); - if (name.isEmpty()) { - name = i18nc("Unknown payee", "Unknown"); + for (it = list.constBegin(); it != list.constEnd(); ++it) { + name = it.key(); + if (name.isEmpty()) { + name = i18nc("Unknown payee", "Unknown"); + } + new KMyMoneyRegister::PayeeGroupMarker(this, name); } - new KMyMoneyRegister::PayeeGroupMarker(this, name); } - } - break; - - case KMyMoneyRegister::CategorySort: - if (KMyMoneyGlobalSettings::showFancyMarker()) { - while (p) { - t = dynamic_cast(p); - if (t) { - list[t->sortCategory()] = 1; + break; + + case SortField::Category: + if (KMyMoneyGlobalSettings::showFancyMarker()) { + while (p) { + t = dynamic_cast(p); + if (t) { + list[t->sortCategory()] = 1; + } + p = p->nextItem(); } - p = p->nextItem(); - } - for (it = list.constBegin(); it != list.constEnd(); ++it) { - name = it.key(); - if (name.isEmpty()) { - name = i18nc("Unknown category", "Unknown"); + for (it = list.constBegin(); it != list.constEnd(); ++it) { + name = it.key(); + if (name.isEmpty()) { + name = i18nc("Unknown category", "Unknown"); + } + new KMyMoneyRegister::CategoryGroupMarker(this, name); } - new KMyMoneyRegister::CategoryGroupMarker(this, name); } - } - break; - - case KMyMoneyRegister::SecuritySort: - if (KMyMoneyGlobalSettings::showFancyMarker()) { - while (p) { - t = dynamic_cast(p); - if (t) { - list[t->sortSecurity()] = 1; + break; + + case SortField::Security: + if (KMyMoneyGlobalSettings::showFancyMarker()) { + while (p) { + t = dynamic_cast(p); + if (t) { + list[t->sortSecurity()] = 1; + } + p = p->nextItem(); } - p = p->nextItem(); - } - for (it = list.constBegin(); it != list.constEnd(); ++it) { - name = it.key(); - if (name.isEmpty()) { - name = i18nc("Unknown security", "Unknown"); + for (it = list.constBegin(); it != list.constEnd(); ++it) { + name = it.key(); + if (name.isEmpty()) { + name = i18nc("Unknown security", "Unknown"); + } + new KMyMoneyRegister::CategoryGroupMarker(this, name); } - new KMyMoneyRegister::CategoryGroupMarker(this, name); } - } - break; + break; - default: // no markers supported - 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::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; + } } -} -DetailsColumnType Register::getDetailsColumnType() const -{ - return m_detailsColumnType; -} + void Register::setLedgerLensForced(bool forced) + { + Q_D(Register); + d->m_ledgerLensForced = forced; + } -void Register::setDetailsColumnType(DetailsColumnType detailsColumnType) -{ - m_detailsColumnType = detailsColumnType; + 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; + } + + eRegister::DetailColumn Register::getDetailsColumnType() const + { + Q_D(const Register); + return d->m_detailsColumnType; + } + + void Register::setDetailsColumnType(eRegister::DetailColumn detailsColumnType) + { + Q_D(Register); + d->m_detailsColumnType = detailsColumnType; + } } diff --git a/kmymoney/widgets/register.h b/kmymoney/widgets/register.h index 5a1fb1456..c939a4795 100644 --- a/kmymoney/widgets/register.h +++ b/kmymoney/widgets/register.h @@ -1,660 +1,363 @@ /*************************************************************************** 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 - // ---------------------------------------------------------------------------- // 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 "selectedtransaction.h" +class MyMoneyAccount; +class MyMoneySplit; class MyMoneyTransaction; -namespace KMyMoneyRegister -{ +namespace eWidgets { enum class SortField; + namespace eTransaction { enum class Column; } + namespace eRegister { enum class DetailColumn;} } +namespace eMyMoney { enum class Account; } -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 GroupMarker : public RegisterItem -{ -public: - 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; - } - - virtual const char* className() { - return "GroupMarker"; - } - - 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 FancyDateGroupMarker : public GroupMarker -{ -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: - 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; -}; - -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; - virtual const char* className() { - return "SimpleDateGroupMarker"; - } -}; - -class TypeGroupMarker : public GroupMarker -{ -public: - TypeGroupMarker(Register* parent, CashFlowDirection dir, eMyMoney::Account accType); - CashFlowDirection sortType() const { - return m_dir; - } -private: - CashFlowDirection m_dir; -}; - -class FiscalYearGroupMarker : public FancyDateGroupMarker -{ -public: - 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 -{ -public: - PayeeGroupMarker(Register* parent, const QString& name); - const QString& sortPayee() const { - return m_txt; - } -}; - -class CategoryGroupMarker : public GroupMarker -{ -public: - CategoryGroupMarker(Register* parent, const QString& category); - const QString& sortCategory() const { - return m_txt; - } - const QString sortSecurity() const { - return m_txt; - } - - virtual const char* className() { - return "CategoryGroupMarker"; - } -}; - -class ReconcileGroupMarker : public GroupMarker -{ -public: - ReconcileGroupMarker(Register* parent, eMyMoney::Split::State state); - virtual eMyMoney::Split::State sortReconcileState() const { - return m_state; - } -private: - eMyMoney::Split::State m_state; -}; +template class QList; - -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 +namespace KMyMoneyRegister { - Q_OBJECT + class RegisterItem; + class Transaction; + class SelectedTransactions; -public: - explicit RegisterItemDelegate(Register *parent); - ~RegisterItemDelegate(); + class RegisterPrivate; + class Register : public TransactionEditorContainer + { + Q_OBJECT + Q_DISABLE_COPY(Register) - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + friend class Transaction; + friend class StdTransaction; + friend class InvestTransaction; + public: + explicit Register(QWidget* parent = nullptr); + virtual ~Register(); -private: - Register *m_register; -}; - -class Register : public TransactionEditorContainer -{ - Q_OBJECT - - friend class Transaction; - friend class StdTransaction; - friend class InvestTransaction; - -public: - Register(QWidget *parent = 0); - virtual ~Register(); - - /** + /** * add the item @a p to the register */ - void addItem(RegisterItem* p); + void addItem(RegisterItem* p); - /** + /** * insert the item @a p into the register after item @a q */ - void insertItemAfter(RegisterItem* p, RegisterItem* q); + void insertItemAfter(RegisterItem* p, RegisterItem* q); - /** + /** * remove the item @p from the register */ - void removeItem(RegisterItem* p); + void removeItem(RegisterItem* p); - /** + /** * This method returns a list of pointers to all selected items * in the register * * @retval QList */ - QList selectedItems() const; + 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; + 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); + 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 { - return m_focusItem; - } - RegisterItem* anchorItem() const { - return m_selectAnchor; - } + RegisterItem* focusItem() const; + RegisterItem* anchorItem() const; - /** + /** * set focus to specific item. * @return true if the item got focus */ - bool setFocusItem(RegisterItem* focusItem); + bool setFocusItem(RegisterItem* focusItem); - void setAnchorItem(RegisterItem* anchorItem); + void setAnchorItem(RegisterItem* anchorItem); - /** + /** * Set focus to the first focussable item * @return true if a focussable item was found */ - bool setFocusToTop(); + 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); + 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 clear(); - void updateRegister(bool forceUpdateRowHeight = false); + void updateRegister(bool forceUpdateRowHeight = false); - /** + /** * Assign all visible items an alternate background color */ - void updateAlternate() const; + void updateAlternate() const; - /** + /** * make sure, we only show a single marker in a row * through hiding unused ones */ - void suppressAdjacentMarkers(); + void suppressAdjacentMarkers(); - /** + /** * Adjusts column @a col so that all data fits in width. */ - void adjustColumn(int col); + 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); + 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 setupRegister(const MyMoneyAccount& account, const QList& cols); - void setSortOrder(const QString& order); - const QList& sortOrder() const { - return m_sortOrder; - } - TransactionSortField primarySortKey() const; - void sortItems(); + void setSortOrder(const QString& order); + const QList& sortOrder() const; + eWidgets::SortField 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 { - return m_lastCol; - } + eWidgets::eTransaction::Column lastCol() const; - 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; + 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 resize(int col, bool force = false); - void forceUpdateLists() { - m_listsDirty = true; - } + void forceUpdateLists(); - void ensureItemVisible(RegisterItem* item); + void ensureItemVisible(RegisterItem* item); - void arrangeEditWidgets(QMap& editWidgets, Transaction* t); - void removeEditWidgets(QMap& editWidgets); - void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const; + void arrangeEditWidgets(QMap& editWidgets, Transaction* t) override; + void removeEditWidgets(QMap& editWidgets) override; + void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const; - int rowHeightHint() const; + int rowHeightHint() const; - void clearSelection(); + 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); + static Transaction* transactionFactory(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - const MyMoneyAccount& account() const { - return m_account; - } + const MyMoneyAccount& account() const; - /** + /** * This method creates group marker items and adds them to the register */ - void addGroupMarkers(); + 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 removeUnwantedGroupMarkers(); - void setLedgerLensForced(bool forced = true) { - m_ledgerLensForced = forced; - } + void setLedgerLensForced(bool forced = true); + bool ledgerLens() const; - /** + /** * 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) { - m_selectionMode = mode; - } + void setSelectionMode(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) { - m_usedWithEditor = value; - } + void setUsedWithEditor(bool value); - DetailsColumnType getDetailsColumnType() const; - void setDetailsColumnType(DetailsColumnType detailsColumnType); + eWidgets::eRegister::DetailColumn getDetailsColumnType() const; + void setDetailsColumnType(eWidgets::eRegister::DetailColumn detailsColumnType); -protected: + protected: - void mouseReleaseEvent(QMouseEvent *e); - void contextMenuEvent(QContextMenuEvent *e); + void mouseReleaseEvent(QMouseEvent *e) override; + void contextMenuEvent(QContextMenuEvent *e) override; - 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; + void unselectItems(int from = -1, int to = -1); + void selectItems(int from, int to); + void doSelectItems(int from, int to, bool selected); + int selectedItemsCount() const; - bool event(QEvent*); - void focusOutEvent(QFocusEvent*); - void focusInEvent(QFocusEvent*); - void keyPressEvent(QKeyEvent*); - virtual void resizeEvent(QResizeEvent* ev); + bool event(QEvent*) override; + void focusOutEvent(QFocusEvent*) override; + void focusInEvent(QFocusEvent*) override; + void keyPressEvent(QKeyEvent*) override; + void resizeEvent(QResizeEvent* ev) override; - int rowToIndex(int row) const; - void setupItemIndex(int rowCount); + 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); + 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; + RegisterItem* itemById(const QString& id) const; - /** + /** * Override logic and use standard QFrame behaviour */ - bool focusNextPrevChild(bool next); + bool focusNextPrevChild(bool next) override; - bool eventFilter(QObject* o, QEvent* e); + bool eventFilter(QObject* o, QEvent* e) override; - void handleItemChange(RegisterItem* old, bool shift, bool control); + void handleItemChange(RegisterItem* old, bool shift, bool control); - void selectRange(RegisterItem* from, RegisterItem* to, bool invert, bool includeFirst, bool clearSel); + 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); + int minimumColumnWidth(int col); -protected slots: - void resize(); + protected slots: + void resize(); - void selectItem(int row, int col); - void slotEnsureItemVisible(); - void slotDoubleClicked(int row, int); + void selectItem(int row, int col); + void slotEnsureItemVisible(); + void slotDoubleClicked(int row, int); -signals: - void transactionsSelected(const KMyMoneyRegister::SelectedTransactions& list); - /** + 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); + 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(); + 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 aboutToSelectItem(KMyMoneyRegister::RegisterItem* item, bool& okToSelect); - void editTransaction(); + 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); + void reconcileStateColumnClicked(KMyMoneyRegister::Transaction* item); - /** + /** * This signal is sent out, if an item without a transaction id has been selected. */ - void emptyItemSelected(); + void emptyItemSelected(); - /** + /** * This signal is sent out, if the user selects an item with the right mouse button */ - void openContextMenu(); + 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: - 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; -}; + void itemAdded(RegisterItem* item); + + private: + RegisterPrivate * const d_ptr; + Q_DECLARE_PRIVATE(Register) + }; } // namespace #endif diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/registerfilter.cpp similarity index 69% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/registerfilter.cpp index d4fd1f27a..df0149649 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/registerfilter.cpp @@ -1,28 +1,38 @@ /*************************************************************************** - transactioneditorcontainer.cpp - ---------- - begin : Wed Jun 07 2006 - copyright : (C) 2006 by Thomas Baumgart + registerfilter.cpp - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-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 "transactioneditorcontainer.h" +#include "registerfilter.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "widgetenums.h" + +namespace KMyMoneyRegister +{ + RegisterFilter::RegisterFilter(const QString &t, eWidgets::eRegister::ItemState s) : + state(s), text(t) + { + } +} diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/registerfilter.h similarity index 61% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/registerfilter.h index d4fd1f27a..4958783e3 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/registerfilter.h @@ -1,28 +1,49 @@ /*************************************************************************** - transactioneditorcontainer.cpp - ---------- - begin : Wed Jun 07 2006 - copyright : (C) 2006 by Thomas Baumgart + registerfilter.h - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-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 "transactioneditorcontainer.h" +#ifndef REGISTERFILTER_H +#define REGISTERFILTER_H // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +namespace eWidgets { namespace eRegister { enum class ItemState; } } + +namespace KMyMoneyRegister +{ + /** + * Used to filter items from the register. + */ + struct RegisterFilter { + explicit RegisterFilter(const QString &t, eWidgets::eRegister::ItemState s); + + eWidgets::eRegister::ItemState state; + QString text; + }; + +} // namespace + +#endif diff --git a/kmymoney/widgets/registeritem.cpp b/kmymoney/widgets/registeritem.cpp index 553cb616b..71d61a3ed 100644 --- a/kmymoney/widgets/registeritem.cpp +++ b/kmymoney/widgets/registeritem.cpp @@ -1,128 +1,300 @@ /*************************************************************************** registeritem.cpp - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-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 "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" +#include "widgetenums.h" using namespace KMyMoneyRegister; -QDate RegisterItem::nullDate; -QString RegisterItem::nullString; -MyMoneyMoney RegisterItem::nullValue; +QDate RegisterItemPrivate::nullDate; +QString RegisterItemPrivate::nullString; +MyMoneyMoney RegisterItemPrivate::nullValue; -RegisterItem::RegisterItem() : - m_parent(0), - m_prev(0), - m_next(0), - m_alternate(false), - m_needResize(false), - m_visible(false) +RegisterItem::RegisterItem(Register* parent) : + d_ptr(new RegisterItemPrivate) { - init(); + Q_D(RegisterItem); + d->m_parent = parent; + parent->addItem(this); } -RegisterItem::RegisterItem(Register* parent) : - m_parent(parent), - m_prev(0), - m_next(0), - m_alternate(false), - m_needResize(false), - m_visible(false) -{ - init(); +RegisterItem::RegisterItem(RegisterItemPrivate &dd, Register* parent) : + d_ptr(&dd) +{ + Q_D(RegisterItem); + d->m_parent = parent; parent->addItem(this); } -void RegisterItem::init() +RegisterItem::~RegisterItem() { - m_startRow = 0; - m_rowsRegister = 1; - m_rowsForm = 1; - m_visible = true; + Q_D(RegisterItem); + d->m_parent->removeItem(this); + delete d; } -RegisterItem::~RegisterItem() +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; +} + +eWidgets::eRegister::CashFlowDirection RegisterItem::sortType() const +{ + return eWidgets::eRegister::CashFlowDirection::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 { - m_parent->removeItem(this); + 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(); } void RegisterItem::setParent(Register* parent) { - m_parent = 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; } void RegisterItem::setNumRowsRegister(int rows) { - if (rows != m_rowsRegister) { - m_rowsRegister = rows; - if (m_parent) - m_parent->forceUpdateLists(); + Q_D(RegisterItem); + if (rows != d->m_rowsRegister) { + d->m_rowsRegister = rows; + if (d->m_parent) + d->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) { - if (m_visible == visible) + Q_D(RegisterItem); + if (d->m_visible == visible) return false; - m_visible = visible; + d->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) { - if (markVisible(visible) && m_parent) { - int numRows = m_parent->rowCount(); + Q_D(RegisterItem); + if (markVisible(visible) && d->m_parent) { + int numRows = d->m_parent->rowCount(); if (visible) { for (int i = startRow(); i < startRow() + numRowsRegister(); ++i) { if (numRows > i) { - m_parent->showRow(i); - m_parent->setRowHeight(i, rowHeightHint()); + d->m_parent->showRow(i); + d->m_parent->setRowHeight(i, rowHeightHint()); } } } else { for (int i = startRow(); i < startRow() + numRowsRegister(); ++i) { if (numRows > i) { - m_parent->hideRow(i); + d->m_parent->hideRow(i); } } } } } int RegisterItem::rowHeightHint() const { - if (!m_visible) + Q_D(const RegisterItem); + if (!d->m_visible) return 0; - if (m_parent) { - return m_parent->rowHeightHint(); + if (d->m_parent) { + return d->m_parent->rowHeightHint(); } QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); return fm.lineSpacing() + 6; } diff --git a/kmymoney/widgets/registeritem.h b/kmymoney/widgets/registeritem.h index adea42ccc..d1feb5cf8 100644 --- a/kmymoney/widgets/registeritem.h +++ b/kmymoney/widgets/registeritem.h @@ -1,306 +1,202 @@ /*************************************************************************** registeritem.h - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-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 REGISTERITEM_H #define REGISTERITEM_H // ---------------------------------------------------------------------------- // QT Includes +#include #include -#include -#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "mymoneyobject.h" -#include "mymoneymoney.h" -#include "mymoneyenums.h" +class QDate; +class QPainter; +class QPoint; +class QRect; +class QStyleOptionViewItem; +class QModelIndex; class MyMoneyMoney; -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; +namespace eMyMoney { namespace Split { enum class State; } } +namespace eWidgets { namespace eRegister { enum class CashFlowDirection; } } -/** - * @author Thomas Baumgart - */ -class RegisterItem +namespace KMyMoneyRegister { -public: - RegisterItem(); - RegisterItem(Register* parent); - virtual ~RegisterItem(); - - virtual const char* className() = 0; - - virtual bool isSelectable() const = 0; - virtual bool isSelected() const { - return false; - } - virtual void setSelected(bool /* selected*/) {} - - virtual bool canHaveFocus() const = 0; - virtual bool hasFocus() const { - return false; - } - virtual bool hasEditorOpen() const { - return false; - } - - 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 { - return nullDate; - } - virtual int sortSamePostDate() const = 0; - 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; - } + struct RegisterFilter; + class Register; /** + * @author Thomas Baumgart + */ + class RegisterItemPrivate; + class RegisterItem + { + Q_DISABLE_COPY(RegisterItem) + + public: + explicit RegisterItem(Register* getParent); + virtual ~RegisterItem(); + + virtual const char* className() = 0; + + virtual bool isSelectable() const = 0; + virtual bool isSelected() const; + virtual void setSelected(bool /* selected*/); + + virtual bool canHaveFocus() const = 0; + virtual bool hasFocus() const; + virtual bool hasEditorOpen() const; + + virtual void setFocus(bool /*focus*/, bool updateLens = true); + + virtual bool isErroneous() const = 0; + + // helper functions used for sorting + virtual QDate sortPostDate() const; + 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 eWidgets::eRegister::CashFlowDirection sortType() const; + virtual const QString& sortCategory() const; + virtual eMyMoney::Split::State sortReconcileState() const; + virtual const QString sortSecurity() const; + + /** * 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) { - m_startRow = row; - } - int startRow() const { - return m_startRow; - } - virtual int rowHeightHint() const; + virtual void setStartRow(int row); + int startRow() const; + 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); + 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) { - m_rowsForm = rows; - } + virtual void setNumRowsForm(int rows); - /** + /** * This method returns the number of rows required to display this item * in a Register */ - virtual int numRowsRegister() const { - return m_rowsRegister; - } + virtual int numRowsRegister() const; - /** + /** * This method returns the number of rows required to display this item * in a Form */ - virtual int numRowsForm() const { - return m_rowsForm; - } - virtual int numColsForm() const { - return 1; - } + virtual int numRowsForm() const; + virtual int numColsForm() const; - /** + /** * 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) { - m_alternate = alternate; - } + virtual void setAlternate(bool 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 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 { - return MyMoneyObject::emptyId(); - } + virtual const QString& id() const; - /** + /** * Sets the parent of this item to be the register @p parent * * @param parent pointer to register */ - void setParent(Register* parent); + void setParent(Register* getParent); - /** + /** * This member returns a pointer to the parent object * * @retval pointer to Register */ - Register* parent() const { - return m_parent; - } + Register* getParent() const; - void setNeedResize() { - m_needResize = true; - } + void setNeedResize(); - bool isVisible() const { - return m_visible; - } + bool isVisible() const; - /** + /** * Marks the item visible depending on @a visible and * updates the underlying register object */ - virtual void setVisible(bool visible); + 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) { - 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; + virtual bool markVisible(bool visible); - /** + void setNextItem(RegisterItem* p); + void setPrevItem(RegisterItem* p); + RegisterItem* nextItem() const; + RegisterItem* prevItem() const; + + 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 */) { - return false; - } - -protected: - /// This method serves as helper for all constructors - void init(); - -protected: - 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: - static QDate nullDate; - static QString nullString; - static MyMoneyMoney nullValue; -}; + virtual bool maybeTip(const QPoint& /* relpos */, int /* row */, int /* col */, QRect& /* r */, QString& /* msg */); + + protected: + RegisterItemPrivate * const d_ptr; + RegisterItem(RegisterItemPrivate &dd, Register *parent); + private: + Q_DECLARE_PRIVATE(RegisterItem) + }; } // namespace #endif diff --git a/kmymoney/widgets/registeritem_p.h b/kmymoney/widgets/registeritem_p.h new file mode 100644 index 000000000..8e2018ee3 --- /dev/null +++ b/kmymoney/widgets/registeritem_p.h @@ -0,0 +1,79 @@ +/*************************************************************************** + registeritem_p.h - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-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 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/scheduledtransaction.cpp b/kmymoney/widgets/registeritemdelegate.cpp similarity index 58% copy from kmymoney/widgets/scheduledtransaction.cpp copy to kmymoney/widgets/registeritemdelegate.cpp index fe711c57e..b62b3fb72 100644 --- a/kmymoney/widgets/scheduledtransaction.cpp +++ b/kmymoney/widgets/registeritemdelegate.cpp @@ -1,49 +1,53 @@ /*************************************************************************** - scheduledtransaction.cpp + registeritemdelegate.cpp - description ------------------- - begin : Tue Aug 19 2008 - copyright : (C) 2008 by Thomas Baumgart + 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 "scheduledtransaction.h" +#include "registeritemdelegate.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneyglobalsettings.h" -#include "transaction.h" +#include "register.h" +#include "registeritem.h" using namespace KMyMoneyRegister; -using namespace KMyMoneyTransactionForm; -StdTransactionScheduled::StdTransactionScheduled(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - StdTransaction(parent, transaction, split, uniqueId) +RegisterItemDelegate::RegisterItemDelegate(Register *parent) : + QStyledItemDelegate(parent), + m_register(parent) { - // setup initial size - setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); } -bool StdTransactionScheduled::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) +RegisterItemDelegate::~RegisterItemDelegate() { - bool rc = Transaction::paintRegisterCellSetup(painter, option, index); - option.palette.setCurrentColorGroup(QPalette::Disabled); - return rc; } - +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); + } +} diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/registeritemdelegate.h similarity index 61% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/registeritemdelegate.h index d4fd1f27a..10c9c4084 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/registeritemdelegate.h @@ -1,28 +1,57 @@ /*************************************************************************** - transactioneditorcontainer.cpp + registeritemdelegate.h ---------- - begin : Wed Jun 07 2006 + 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 "transactioneditorcontainer.h" +#ifndef REGISTERITEMDELEGATE_H +#define REGISTERITEMDELEGATE_H // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +class QPainter; +class QModelIndex; +class QStyleOptionViewItem; + +namespace KMyMoneyRegister +{ + class Register; + class RegisterItemDelegate : public QStyledItemDelegate + { + Q_OBJECT + Q_DISABLE_COPY(RegisterItemDelegate) + + public: + explicit RegisterItemDelegate(Register *parent); + ~RegisterItemDelegate(); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + + private: + Register *m_register; + }; + +} // namespace + +#endif diff --git a/kmymoney/widgets/registersearchline.cpp b/kmymoney/widgets/registersearchline.cpp index 72846484d..9c60099ca 100644 --- a/kmymoney/widgets/registersearchline.cpp +++ b/kmymoney/widgets/registersearchline.cpp @@ -1,253 +1,256 @@ /*************************************************************************** registersearchline.cpp ------------------- copyright : (C) 2006 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 "registersearchline.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyutils.h" #include "register.h" #include "registeritem.h" -#include "transaction.h" +#include "registerfilter.h" #include "icons/icons.h" +#include "widgetenums.h" +using namespace eWidgets; using namespace KMyMoneyRegister; using namespace Icons; class RegisterSearchLine::RegisterSearchLinePrivate { public: RegisterSearchLinePrivate() : reg(0), combo(0), queuedSearches(0), - status(RegisterFilter::Any) {} + status(eRegister::ItemState::Any) {} Register* reg; KComboBox* combo; QString search; int queuedSearches; - RegisterFilter::ItemState status; + eRegister::ItemState status; }; RegisterSearchLine::RegisterSearchLine(QWidget* parent, Register* reg) : KLineEdit(parent), d(new RegisterSearchLinePrivate) { setClearButtonEnabled(true); init(reg); } void RegisterSearchLine::init(Register *reg) { if (!parentWidget()->layout()) parentWidget()->setLayout(new QHBoxLayout); parentWidget()->layout()->addWidget(this); d->reg = reg; - connect(this, SIGNAL(textChanged(QString)), this, SLOT(queueSearch(QString))); + connect(this, &QLineEdit::textChanged, this, &RegisterSearchLine::queueSearch); QLabel* label = new QLabel(i18nc("label for status combo", "Stat&us"), parentWidget()); parentWidget()->layout()->addWidget(label); d->combo = new KComboBox(parentWidget()); parentWidget()->layout()->addWidget(d->combo); // don't change the order of the following lines unless updating // the case labels in RegisterSearchLine::itemMatches() at the same time - d->combo->insertItem(RegisterFilter::Any, QIcon::fromTheme(g_Icons[Icon::SystemRun]), i18n("Any status")); - d->combo->insertItem(RegisterFilter::Imported, QIcon::fromTheme(g_Icons[Icon::DocumentImport]), i18n("Imported")); - d->combo->insertItem(RegisterFilter::Matched, KMyMoneyUtils::overlayIcon(g_Icons[Icon::ViewFinancialTransfer], g_Icons[Icon::DocumentImport]), i18n("Matched")); - d->combo->insertItem(RegisterFilter::Erroneous, QIcon::fromTheme(g_Icons[Icon::TaskAttention]), i18n("Erroneous")); - d->combo->insertItem(RegisterFilter::NotMarked, i18n("Not marked")); - d->combo->insertItem(RegisterFilter::NotReconciled, i18n("Not reconciled")); - d->combo->insertItem(RegisterFilter::Cleared, i18nc("Reconciliation state 'Cleared'", "Cleared")); - d->combo->setCurrentIndex(RegisterFilter::Any); - connect(d->combo, SIGNAL(activated(int)), this, SLOT(slotStatusChanged(int))); - connect(this, SIGNAL(clearButtonClicked()), this, SLOT(reset())); + d->combo->insertItem((int)eRegister::ItemState::Any, QIcon::fromTheme(g_Icons[Icon::SystemRun]), i18n("Any status")); + d->combo->insertItem((int)eRegister::ItemState::Imported, QIcon::fromTheme(g_Icons[Icon::DocumentImport]), i18n("Imported")); + d->combo->insertItem((int)eRegister::ItemState::Matched, KMyMoneyUtils::overlayIcon(g_Icons[Icon::ViewFinancialTransfer], g_Icons[Icon::DocumentImport]), i18n("Matched")); + d->combo->insertItem((int)eRegister::ItemState::Erroneous, QIcon::fromTheme(g_Icons[Icon::TaskAttention]), i18n("Erroneous")); + d->combo->insertItem((int)eRegister::ItemState::NotMarked, i18n("Not marked")); + d->combo->insertItem((int)eRegister::ItemState::NotReconciled, i18n("Not reconciled")); + d->combo->insertItem((int)eRegister::ItemState::Cleared, i18nc("Reconciliation state 'Cleared'", "Cleared")); + d->combo->setCurrentIndex((int)eRegister::ItemState::Any); + connect(d->combo, static_cast(&QComboBox::activated), this, &RegisterSearchLine::slotStatusChanged); + connect(this, &KLineEdit::clearButtonClicked, this, &RegisterSearchLine::reset); label->setBuddy(d->combo); if (reg) { - connect(reg, SIGNAL(destroyed()), this, SLOT(registerDestroyed())); - connect(reg, SIGNAL(itemAdded(RegisterItem*)), this, SLOT(itemAdded(RegisterItem*))); + connect(reg, &QObject::destroyed, this, &RegisterSearchLine::registerDestroyed); + connect(reg, &Register::itemAdded, this, &RegisterSearchLine::itemAdded); } else { setEnabled(false); } } RegisterSearchLine::~RegisterSearchLine() { delete d; } void RegisterSearchLine::setRegister(Register* reg) { if (d->reg) { - disconnect(d->reg, SIGNAL(destroyed()), this, SLOT(registerDestroyed())); - disconnect(d->reg, SIGNAL(itemAdded(RegisterItem*)), this, SLOT(itemAdded(RegisterItem*))); + disconnect(d->reg, &QObject::destroyed, this, &RegisterSearchLine::registerDestroyed); + disconnect(d->reg, &Register::itemAdded, this, &RegisterSearchLine::itemAdded); } d->reg = reg; if (reg) { - connect(reg, SIGNAL(destroyed()), this, SLOT(registerDestroyed())); - connect(reg, SIGNAL(itemAdded(RegisterItem*)), this, SLOT(itemAdded(RegisterItem*))); + connect(reg, &QObject::destroyed, this, &RegisterSearchLine::registerDestroyed); + connect(reg, &Register::itemAdded, this, &RegisterSearchLine::itemAdded); } setEnabled(reg != 0); } void RegisterSearchLine::slotStatusChanged(int status) { - d->status = static_cast(status); + d->status = static_cast(status); updateSearch(); } void RegisterSearchLine::queueSearch(const QString& search) { d->queuedSearches++; d->search = search; QTimer::singleShot(200, this, SLOT(activateSearch())); } void RegisterSearchLine::activateSearch() { --(d->queuedSearches); if (d->queuedSearches == 0) updateSearch(d->search); } void RegisterSearchLine::updateSearch(const QString& s) { if (!d->reg) return; d->search = s.isNull() ? text() : s; // keep track of the current focus item RegisterItem* focusItem = d->reg->focusItem(); bool enabled = d->reg->updatesEnabled(); d->reg->setUpdatesEnabled(false); bool scrollBarVisible = d->reg->verticalScrollBar()->isVisible(); RegisterFilter filter(d->search, d->status); RegisterItem* p = d->reg->firstItem(); for (; p; p = p->nextItem()) { p->setVisible(p->matches(filter)); } d->reg->suppressAdjacentMarkers(); d->reg->updateAlternate(); d->reg->setUpdatesEnabled(enabled); // if focus item is still visible, then make sure we have // it on screen if (focusItem && focusItem->isVisible()) { d->reg->update(); d->reg->ensureItemVisible(focusItem); } // if the scrollbar's visibility changed, we need to resize the contents if (scrollBarVisible != d->reg->verticalScrollBar()->isVisible()) { - d->reg->resize(DetailColumn); + d->reg->resize((int)eTransaction::Column::Detail); } } void RegisterSearchLine::reset() { clear(); } void RegisterSearchLine::itemAdded(RegisterItem* item) const { item->setVisible(item->matches(RegisterFilter(text(), d->status))); } void RegisterSearchLine::registerDestroyed() { d->reg = 0; setEnabled(false); } class RegisterSearchLineWidget::RegisterSearchLineWidgetPrivate { public: RegisterSearchLineWidgetPrivate() : reg(0), searchLine(0) {} Register* reg; RegisterSearchLine* searchLine; }; RegisterSearchLineWidget::RegisterSearchLineWidget(Register* reg, QWidget* parent) : QWidget(parent), d(new RegisterSearchLineWidgetPrivate) { d->reg = reg; createWidgets(); } RegisterSearchLineWidget::~RegisterSearchLineWidget() { delete d; } RegisterSearchLine* RegisterSearchLineWidget::createSearchLine(Register* reg) { if (!d->searchLine) d->searchLine = new RegisterSearchLine(this, reg); return d->searchLine; } void RegisterSearchLineWidget::createWidgets() { QHBoxLayout *searchLineLayout = new QHBoxLayout(this); searchLineLayout->setSpacing(0); searchLineLayout->setContentsMargins(0, 0, 0, 0); QLabel *label = new QLabel(i18nc("Filter widget label", "Fi<er:"), this); searchLineLayout->addWidget(label); d->searchLine = createSearchLine(d->reg); d->searchLine->show(); label->setBuddy(d->searchLine); label->show(); } RegisterSearchLine* RegisterSearchLineWidget::searchLine() const { return d->searchLine; } diff --git a/kmymoney/widgets/registersearchline.h b/kmymoney/widgets/registersearchline.h index 4a434bebb..1a2405e0e 100644 --- a/kmymoney/widgets/registersearchline.h +++ b/kmymoney/widgets/registersearchline.h @@ -1,140 +1,141 @@ /*************************************************************************** registersearchline.h ------------------- begin : Sun Jan 14 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 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 = 0, Register* reg = 0); + explicit RegisterSearchLine(QWidget* parent = nullptr, 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 = 0); + explicit RegisterSearchLineWidget(Register* reg = 0, QWidget* parent = nullptr); /** * 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/reportcontrolimpl.cpp b/kmymoney/widgets/reportcontrolimpl.cpp index cc2ce1b2a..ab61a542e 100644 --- a/kmymoney/widgets/reportcontrolimpl.cpp +++ b/kmymoney/widgets/reportcontrolimpl.cpp @@ -1,30 +1,36 @@ /* This file is part of the KDE project Copyright (C) 2009 Alvaro Soliverez + (C) 2017 by Łukasz Wojniłowicz 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 "reportcontrolimpl.h" #include "ui_reportcontrol.h" -ReportControl::ReportControl(QWidget *parent) - : QWidget(parent) +ReportControl::ReportControl(QWidget *parent) : + QWidget(parent), + ui(new Ui::ReportControl) { - ui = new Ui::ReportControl; ui->setupUi(this); } +ReportControl::~ReportControl() +{ + delete ui; +} + diff --git a/kmymoney/widgets/reportcontrolimpl.h b/kmymoney/widgets/reportcontrolimpl.h index 88bb9d03e..6d651dbe6 100644 --- a/kmymoney/widgets/reportcontrolimpl.h +++ b/kmymoney/widgets/reportcontrolimpl.h @@ -1,34 +1,39 @@ /* This file is part of the KDE project Copyright (C) 2009 Alvaro Soliverez + (C) 2017 by Łukasz Wojniłowicz 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 REPORTCONTROLIMPL_H #define REPORTCONTROLIMPL_H #include namespace Ui { class ReportControl; } class ReportControl : public QWidget { Q_OBJECT + Q_DISABLE_COPY(ReportControl) + public: - ReportControl(QWidget *parent); + explicit ReportControl(QWidget *parent); + ~ReportControl(); + Ui::ReportControl* ui; }; #endif /* REPORTCONTROLIMPL_H */ diff --git a/kmymoney/widgets/reporttabcapitalgain.ui b/kmymoney/widgets/reporttabcapitalgain.ui index d316c8e19..3d6a4fd4a 100644 --- a/kmymoney/widgets/reporttabcapitalgain.ui +++ b/kmymoney/widgets/reporttabcapitalgain.ui @@ -1,136 +1,136 @@ ReportTabCapitalGain 0 0 441 166 Report Tab <p>On this tab, you set the basic properties of this report.</p> Settlement period Terms separator - + Before this date investments are counted as long-term investments. Show short-term and long-term capital gains Sum investments Time in days between the settlement date and the transaction date. 30 3 Hide Totals Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Expanding 20 20 - kMyMoneyDateInput + KMyMoneyDateInput QWidget
kmymoneydateinput.h
diff --git a/kmymoney/widgets/reporttabchart.ui b/kmymoney/widgets/reporttabchart.ui index 0539510d9..77c4392ab 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 -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.h
1
m_checkCHGridLines m_checkSVGridLines m_checkValues m_checkShowChart m_logYaxis m_lineWidth
diff --git a/kmymoney/widgets/reporttabimpl.cpp b/kmymoney/widgets/reporttabimpl.cpp index f8b0b1053..419e39073 100644 --- a/kmymoney/widgets/reporttabimpl.cpp +++ b/kmymoney/widgets/reporttabimpl.cpp @@ -1,297 +1,330 @@ /* This file is part of the KDE project Copyright (C) 2009 Laurent Montel + (C) 2017 by Łukasz Wojniłowicz 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 "reporttabimpl.h" #include #include "daterangedlg.h" #include "ui_reporttabgeneral.h" #include "ui_reporttabrowcolpivot.h" #include "ui_reporttabrowcolquery.h" #include "ui_reporttabchart.h" #include "ui_reporttabrange.h" #include "ui_reporttabcapitalgain.h" #include "ui_reporttabperformance.h" #include "mymoney/mymoneyreport.h" ReportTabGeneral::ReportTabGeneral(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabGeneral; ui->setupUi(this); } ReportTabGeneral::~ReportTabGeneral() { delete ui; } ReportTabRowColPivot::ReportTabRowColPivot(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabRowColPivot; ui->setupUi(this); } ReportTabRowColPivot::~ReportTabRowColPivot() { delete ui; } ReportTabRowColQuery::ReportTabRowColQuery(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabRowColQuery; ui->setupUi(this); ui->buttonGroup1->setExclusive(false); ui->buttonGroup1->setId(ui->m_checkMemo, 0); ui->buttonGroup1->setId(ui->m_checkShares, 1); ui->buttonGroup1->setId(ui->m_checkPrice, 2); ui->buttonGroup1->setId(ui->m_checkReconciled, 3); ui->buttonGroup1->setId(ui->m_checkAccount, 4); ui->buttonGroup1->setId(ui->m_checkNumber, 5); ui->buttonGroup1->setId(ui->m_checkPayee, 6); ui->buttonGroup1->setId(ui->m_checkCategory, 7); ui->buttonGroup1->setId(ui->m_checkAction, 8); ui->buttonGroup1->setId(ui->m_checkBalance, 9); - connect(ui->m_checkHideTransactions, SIGNAL(toggled(bool)), this, SLOT(slotHideTransactionsChanged(bool))); + connect(ui->m_checkHideTransactions, &QAbstractButton::toggled, this, &ReportTabRowColQuery::slotHideTransactionsChanged); } void ReportTabRowColQuery::slotHideTransactionsChanged(bool checked) { if (checked) // toggle m_checkHideSplitDetails only if it's mandatory ui->m_checkHideSplitDetails->setChecked(checked); ui->m_checkHideSplitDetails->setEnabled(!checked); // hiding transactions without hiding splits isn't allowed } ReportTabRowColQuery::~ReportTabRowColQuery() { delete ui; } ReportTabChart::ReportTabChart(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabChart; ui->setupUi(this); ui->m_comboType->addItem(i18nc("type of graphic chart", "Line"), MyMoneyReport::eChartLine); ui->m_comboType->addItem(i18nc("type of graphic chart", "Bar"), MyMoneyReport::eChartBar); ui->m_comboType->addItem(i18nc("type of graphic chart", "Stacked Bar"), MyMoneyReport::eChartStackedBar); ui->m_comboType->addItem(i18nc("type of graphic chart", "Pie"), MyMoneyReport::eChartPie); ui->m_comboType->addItem(i18nc("type of graphic chart", "Ring"), MyMoneyReport::eChartRing); - connect(ui->m_comboType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChartTypeChanged(int))); + connect(ui->m_comboType, static_cast(&QComboBox::currentIndexChanged), this, &ReportTabChart::slotChartTypeChanged); emit ui->m_comboType->currentIndexChanged(ui->m_comboType->currentIndex()); } ReportTabChart::~ReportTabChart() { delete ui; } void ReportTabChart::slotChartTypeChanged(int index) { if (index == MyMoneyReport::EChartType::eChartPie || index == MyMoneyReport::EChartType::eChartRing) { ui->m_checkCHGridLines->setText(i18n("Show circular grid lines")); ui->m_checkSVGridLines->setText(i18n("Show sagittal grid lines")); ui->m_logYaxis->setChecked(false); ui->m_logYaxis->setEnabled(false); } else { ui->m_checkCHGridLines->setText(i18n("Show horizontal grid lines")); ui->m_checkSVGridLines->setText(i18n("Show vertical grid lines")); ui->m_logYaxis->setEnabled(true); } } ReportTabRange::ReportTabRange(QWidget *parent) : QWidget(parent), ui(new Ui::ReportTabRange) { ui->setupUi(this); m_dateRange = new DateRangeDlg; ui->dateRangeGrid->addWidget(m_dateRange, 0, 0, 1, 2); - connect(ui->m_yLabelsPrecision, SIGNAL(valueChanged(int)), this, SLOT(slotYLabelsPrecisionChanged(int))); + connect(ui->m_yLabelsPrecision, static_cast(&QSpinBox::valueChanged), this, &ReportTabRange::slotYLabelsPrecisionChanged); emit ui->m_yLabelsPrecision->valueChanged(ui->m_yLabelsPrecision->value()); - connect(ui->m_dataRangeStart, SIGNAL(editingFinished()), this, SLOT(slotEditingFinishedStart())); - connect(ui->m_dataRangeEnd, SIGNAL(editingFinished()), this, SLOT(slotEditingFinishedEnd())); - connect(ui->m_dataMajorTick, SIGNAL(editingFinished()), this, SLOT(slotEditingFinishedMajor())); - connect(ui->m_dataMinorTick, SIGNAL(editingFinished()), this, SLOT(slotEditingFinishedMinor())); - connect(ui->m_dataLock, SIGNAL(currentIndexChanged(int)), this, SLOT(slotDataLockChanged(int))); + connect(ui->m_dataRangeStart, &QLineEdit::editingFinished, this, &ReportTabRange::slotEditingFinishedStart); + connect(ui->m_dataRangeEnd, &QLineEdit::editingFinished, this, &ReportTabRange::slotEditingFinishedEnd); + connect(ui->m_dataMajorTick, &QLineEdit::editingFinished, this, &ReportTabRange::slotEditingFinishedMajor); + connect(ui->m_dataMinorTick, &QLineEdit::editingFinished, this, &ReportTabRange::slotEditingFinishedMinor); + connect(ui->m_dataLock, static_cast(&QComboBox::currentIndexChanged), this, &ReportTabRange::slotDataLockChanged); emit ui->m_dataLock->currentIndexChanged(ui->m_dataLock->currentIndex()); } ReportTabRange::~ReportTabRange() { delete ui; } void ReportTabRange::setRangeLogarythmic(bool set) { // major and minor tick have no influence if axis is logarithmic so hide them if (set) { ui->lblDataMajorTick->hide(); ui->lblDataMinorTick->hide(); ui->m_dataMajorTick->hide(); ui->m_dataMinorTick->hide(); } else { ui->lblDataMajorTick->show(); ui->lblDataMinorTick->show(); ui->m_dataMajorTick->show(); ui->m_dataMinorTick->show(); } } void ReportTabRange::slotEditingFinished(EDimension dim) { qreal dataRangeStart = locale().toDouble(ui->m_dataRangeStart->text()); qreal dataRangeEnd = locale().toDouble(ui->m_dataRangeEnd->text()); qreal dataMajorTick = locale().toDouble(ui->m_dataMajorTick->text()); qreal dataMinorTick = locale().toDouble(ui->m_dataMinorTick->text()); if (dataRangeEnd < dataRangeStart) { // end must be higher than start if (dim == eRangeEnd) { ui->m_dataRangeStart->setText(ui->m_dataRangeEnd->text()); dataRangeStart = dataRangeEnd; } else { ui->m_dataRangeEnd->setText(ui->m_dataRangeStart->text()); dataRangeEnd = dataRangeStart; } } if ((dataRangeStart != 0 || dataRangeEnd != 0)) { // if data range isn't going to be reset if ((dataRangeEnd - dataRangeStart) < dataMajorTick) // major tick cannot be greater than data range dataMajorTick = dataRangeEnd - dataRangeStart; if (dataMajorTick != 0 && // if major tick isn't going to be reset dataMajorTick < (dataRangeEnd - dataRangeStart) * 0.01) // constraint major tick to be greater or equal to 1% of data range dataMajorTick = (dataRangeEnd - dataRangeStart) * 0.01; // that should produce more than 256 Y labels in KReportChartView::slotNeedUpdate //set precision of major tick to be greater by 1 ui->m_dataMajorTick->setText(locale().toString(dataMajorTick, 'f', ui->m_yLabelsPrecision->value() + 1).remove(locale().groupSeparator()).remove(QRegularExpression("0+$")).remove(QRegularExpression("\\" + locale().decimalPoint() + "$"))); } if (dataMajorTick < dataMinorTick) { // major tick must be higher than minor if (dim == eMinorTick) { ui->m_dataMajorTick->setText(ui->m_dataMinorTick->text()); dataMajorTick = dataMinorTick; } else { ui->m_dataMinorTick->setText(ui->m_dataMajorTick->text()); dataMinorTick = dataMajorTick; } } if (dataMinorTick < dataMajorTick * 0.1) { // constraint minor tick to be greater or equal to 10% of major tick, and set precision to be greater by 1 dataMinorTick = dataMajorTick * 0.1; ui->m_dataMinorTick->setText(locale().toString(dataMinorTick, 'f', ui->m_yLabelsPrecision->value() + 1).remove(locale().groupSeparator()).remove(QRegularExpression("0+$")).remove(QRegularExpression("\\" + locale().decimalPoint() + "$"))); } } void ReportTabRange::slotEditingFinishedStart() { slotEditingFinished(eRangeStart); } void ReportTabRange::slotEditingFinishedEnd() { slotEditingFinished(eRangeEnd); } void ReportTabRange::slotEditingFinishedMajor() { slotEditingFinished(eMajorTick); } void ReportTabRange::slotEditingFinishedMinor() { slotEditingFinished(eMinorTick); } void ReportTabRange::slotYLabelsPrecisionChanged(const int& value) { ui->m_dataRangeStart->setValidator(0); ui->m_dataRangeEnd->setValidator(0); ui->m_dataMajorTick->setValidator(0); ui->m_dataMinorTick->setValidator(0); MyDoubleValidator *dblVal = new MyDoubleValidator(value); ui->m_dataRangeStart->setValidator(dblVal); ui->m_dataRangeEnd->setValidator(dblVal); MyDoubleValidator *dblVal2 = new MyDoubleValidator(value + 1); ui->m_dataMajorTick->setValidator(dblVal2); ui->m_dataMinorTick->setValidator(dblVal2); } void ReportTabRange::slotDataLockChanged(int index) { if (index == MyMoneyReport::dataOptionE::automatic) { ui->m_dataRangeStart->setText(QStringLiteral("0")); ui->m_dataRangeEnd->setText(QStringLiteral("0")); ui->m_dataMajorTick->setText(QStringLiteral("0")); ui->m_dataMinorTick->setText(QStringLiteral("0")); ui->m_dataRangeStart->setEnabled(false); ui->m_dataRangeEnd->setEnabled(false); ui->m_dataMajorTick->setEnabled(false); ui->m_dataMinorTick->setEnabled(false); } else { ui->m_dataRangeStart->setEnabled(true); ui->m_dataRangeEnd->setEnabled(true); ui->m_dataMajorTick->setEnabled(true); ui->m_dataMinorTick->setEnabled(true); } } ReportTabCapitalGain::ReportTabCapitalGain(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabCapitalGain; ui->setupUi(this); - connect(ui->m_investmentSum, SIGNAL(currentIndexChanged(int)), this, SLOT(slotInvestmentSumChanged(int))); + connect(ui->m_investmentSum, static_cast(&QComboBox::currentIndexChanged), this, &ReportTabCapitalGain::slotInvestmentSumChanged); } ReportTabCapitalGain::~ReportTabCapitalGain() { delete ui; } void ReportTabCapitalGain::slotInvestmentSumChanged(int index) { Q_UNUSED(index); if (ui->m_investmentSum->currentData() == MyMoneyReport::eSumOwned) { ui->m_settlementPeriod->setValue(0); ui->m_settlementPeriod->setEnabled(false); ui->m_showSTLTCapitalGains->setChecked(false); ui->m_showSTLTCapitalGains->setEnabled(false); ui->m_termSeparator->setEnabled(false); } else { ui->m_settlementPeriod->setEnabled(true); ui->m_showSTLTCapitalGains->setEnabled(true); ui->m_termSeparator->setEnabled(true); } } ReportTabPerformance::ReportTabPerformance(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabPerformance; ui->setupUi(this); } ReportTabPerformance::~ReportTabPerformance() { delete ui; } + +MyDoubleValidator::MyDoubleValidator(int decimals, QObject * parent) : + QDoubleValidator(0, 0, decimals, parent) +{ +} + +QValidator::State MyDoubleValidator::validate(QString &s, int &i) const +{ + Q_UNUSED(i); + if (s.isEmpty() || s == "-") { + return QValidator::Intermediate; + } + + QChar decimalPoint = locale().decimalPoint(); + + if(s.indexOf(decimalPoint) != -1) { + int charsAfterPoint = s.length() - s.indexOf(decimalPoint) - 1; + + if (charsAfterPoint > decimals()) { + return QValidator::Invalid; + } + } + + bool ok; + locale().toDouble(s, &ok); + + if (ok) { + return QValidator::Acceptable; + } else { + return QValidator::Invalid; + } +} diff --git a/kmymoney/widgets/reporttabimpl.h b/kmymoney/widgets/reporttabimpl.h index 31cf7ded5..d0dbcf776 100644 --- a/kmymoney/widgets/reporttabimpl.h +++ b/kmymoney/widgets/reporttabimpl.h @@ -1,153 +1,147 @@ /* This file is part of the KDE project Copyright (C) 2009 Laurent Montel + (C) 2017 by Łukasz Wojniłowicz 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 REPORTTABIMPL_H #define REPORTTABIMPL_H #include #include class DateRangeDlg; namespace Ui { class ReportTabGeneral; class ReportTabRowColPivot; class ReportTabRowColQuery; class ReportTabChart; class ReportTabRange; class ReportTabCapitalGain; class ReportTabPerformance; } class ReportTabGeneral : public QWidget { + Q_DISABLE_COPY(ReportTabGeneral) + public: - ReportTabGeneral(QWidget *parent); - virtual ~ReportTabGeneral(); + explicit ReportTabGeneral(QWidget *parent); + ~ReportTabGeneral(); + Ui::ReportTabGeneral* ui; }; class ReportTabRowColPivot : public QWidget { + Q_DISABLE_COPY(ReportTabRowColPivot) + public: - ReportTabRowColPivot(QWidget *parent); - virtual ~ReportTabRowColPivot(); + explicit ReportTabRowColPivot(QWidget *parent); + ~ReportTabRowColPivot(); + Ui::ReportTabRowColPivot* ui; }; class ReportTabRowColQuery : public QWidget { Q_OBJECT + Q_DISABLE_COPY(ReportTabRowColQuery) + public: - ReportTabRowColQuery(QWidget *parent); - virtual ~ReportTabRowColQuery(); + explicit ReportTabRowColQuery(QWidget *parent); + ~ReportTabRowColQuery(); + Ui::ReportTabRowColQuery* ui; private slots: void slotHideTransactionsChanged(bool checked); }; class ReportTabChart : public QWidget { Q_OBJECT + Q_DISABLE_COPY(ReportTabChart) + public: - ReportTabChart(QWidget *parent); - virtual ~ReportTabChart(); + explicit ReportTabChart(QWidget *parent); + ~ReportTabChart(); + Ui::ReportTabChart* ui; + private slots: void slotChartTypeChanged(int index); }; class ReportTabRange : public QWidget { Q_OBJECT + Q_DISABLE_COPY(ReportTabRange) + public: - ReportTabRange(QWidget *parent); - virtual ~ReportTabRange(); + explicit ReportTabRange(QWidget *parent); + ~ReportTabRange(); + Ui::ReportTabRange* ui; DateRangeDlg *m_dateRange; void setRangeLogarythmic(bool set); private: enum EDimension { eRangeStart = 0, eRangeEnd, eMajorTick, eMinorTick}; private slots: void slotEditingFinished(EDimension dim); void slotEditingFinishedStart(); void slotEditingFinishedEnd(); void slotEditingFinishedMajor(); void slotEditingFinishedMinor(); void slotYLabelsPrecisionChanged(const int &value); void slotDataLockChanged(int index); }; class ReportTabCapitalGain : public QWidget { Q_OBJECT + Q_DISABLE_COPY(ReportTabCapitalGain) + public: - ReportTabCapitalGain(QWidget *parent); - virtual ~ReportTabCapitalGain(); + explicit ReportTabCapitalGain(QWidget *parent); + ~ReportTabCapitalGain(); + Ui::ReportTabCapitalGain* ui; + private slots: void slotInvestmentSumChanged(int index); }; class ReportTabPerformance : public QWidget { public: - ReportTabPerformance(QWidget *parent); - virtual ~ReportTabPerformance(); + explicit ReportTabPerformance(QWidget *parent); + ~ReportTabPerformance(); + Ui::ReportTabPerformance* ui; }; class MyDoubleValidator : public QDoubleValidator { public: - MyDoubleValidator(int decimals, QObject * parent = 0) : - QDoubleValidator(0, 0, decimals, parent) - { - } - - QValidator::State validate(QString &s, int &i) const - { - Q_UNUSED(i); - if (s.isEmpty() || s == "-") { - return QValidator::Intermediate; - } - - QChar decimalPoint = locale().decimalPoint(); - - if(s.indexOf(decimalPoint) != -1) { - int charsAfterPoint = s.length() - s.indexOf(decimalPoint) - 1; - - if (charsAfterPoint > decimals()) { - return QValidator::Invalid; - } - } - - bool ok; - locale().toDouble(s, &ok); - - if (ok) { - return QValidator::Acceptable; - } else { - return QValidator::Invalid; - } - } + explicit MyDoubleValidator(int decimals, QObject * parent = 0); + + QValidator::State validate(QString &s, int &i) const; }; #endif /* REPORTTABIMPL_H */ diff --git a/kmymoney/widgets/reporttabrowcolpivot.ui b/kmymoney/widgets/reporttabrowcolpivot.ui index 9cf6e4fae..e1f22ba38 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 -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.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 fe711c57e..51dfb3b2f 100644 --- a/kmymoney/widgets/scheduledtransaction.cpp +++ b/kmymoney/widgets/scheduledtransaction.cpp @@ -1,49 +1,80 @@ /*************************************************************************** scheduledtransaction.cpp ------------------- begin : Tue Aug 19 2008 copyright : (C) 2008 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 "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) { - bool rc = Transaction::paintRegisterCellSetup(painter, option, index); + auto 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 ec9dfb254..4e8d79cc5 100644 --- a/kmymoney/widgets/scheduledtransaction.h +++ b/kmymoney/widgets/scheduledtransaction.h @@ -1,73 +1,58 @@ /*************************************************************************** scheduledtransaction.h ------------------- begin : Tue Aug 19 2008 copyright : (C) 2008 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 SCHEDULEDTRANSACTION_H #define SCHEDULEDTRANSACTION_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "transaction.h" - -namespace KMyMoneyTransactionForm -{ -class TransactionForm; -} // namespace +#include "stdtransaction.h" namespace KMyMoneyRegister { -class StdTransactionScheduled : public StdTransaction -{ -public: - StdTransactionScheduled(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - virtual ~StdTransactionScheduled() {} - - virtual const char* className() { - return "StdTransactionScheduled"; - } + class StdTransactionScheduled : public StdTransaction + { + public: + explicit StdTransactionScheduled(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + ~StdTransactionScheduled() override; - virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); + const char* className(); - bool isSelectable() const { - return true; - } - bool canHaveFocus() const { - return true; - } - virtual bool isScheduled() const { - return true; - } + bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; - virtual int sortSamePostDate() const { - return 4; - } + bool isSelectable() const override; + bool canHaveFocus() const override; + bool isScheduled() const override; + int sortSamePostDate() const override; -// 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 f436500fd..45e31431f 100644 --- a/kmymoney/widgets/selectedtransaction.cpp +++ b/kmymoney/widgets/selectedtransaction.cpp @@ -1,85 +1,129 @@ /*************************************************************************** selectedtransaction.cpp - description ------------------- begin : Fri Jun 2008 copyright : (C) 2000-2008 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 "selectedtransaction.h" // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "register.h" -#include "mymoneysplit.h" #include "mymoneyfile.h" +#include "mymoneyaccount.h" +#include "mymoneysplit.h" +#include "mymoneytransaction.h" +#include "mymoneyexception.h" + +using namespace KMyMoneyRegister; 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; +} + +MyMoneyTransaction SelectedTransaction::transaction() const +{ + Q_D(const SelectedTransaction); + return d->m_transaction; +} + +MyMoneySplit& SelectedTransaction::split() +{ + Q_D(SelectedTransaction); + return d->m_split; +} + +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(); +} + +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 332da4680..57192981d 100644 --- a/kmymoney/widgets/selectedtransaction.h +++ b/kmymoney/widgets/selectedtransaction.h @@ -1,101 +1,94 @@ /*************************************************************************** selectedtransaction.h - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-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 SELECTEDTRANSACTION_H #define SELECTEDTRANSACTION_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "mymoneytransaction.h" -#include "mymoneysplit.h" +class QString; +class MyMoneySplit; +class MyMoneyTransaction; namespace KMyMoneyRegister { - -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; - } - - /** + 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(); + MyMoneyTransaction transaction() const; + + MyMoneySplit& split(); + MyMoneySplit split() const; + + bool isScheduled() const; + QString scheduleId() const; + + /** * 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; - -private: - MyMoneyTransaction m_transaction; - MyMoneySplit m_split; - QString m_scheduleId; -}; + int warnLevel() const; -class Register; - -class SelectedTransactions: public QList -{ -public: - SelectedTransactions() {} - SelectedTransactions(const Register* r); + private: + SelectedTransactionPrivate* d_ptr; + Q_DECLARE_PRIVATE(SelectedTransaction) + }; - /** - * @return the highest warnLevel of all transactions in the list - */ - int warnLevel() const; + inline void swap(SelectedTransaction& first, SelectedTransaction& second) // krazy:exclude=inline + { + using std::swap; + swap(first.d_ptr, second.d_ptr); + } - bool canModify() const; - bool canDuplicate() const; -}; + inline SelectedTransaction::SelectedTransaction(SelectedTransaction && other) : SelectedTransaction() // krazy:exclude=inline + { + swap(*this, other); + } + inline SelectedTransaction & SelectedTransaction::operator=(SelectedTransaction other) // krazy:exclude=inline + { + swap(*this, other); + return *this; + } } // namespace #endif diff --git a/kmymoney/widgets/selectedtransaction.cpp b/kmymoney/widgets/selectedtransactions.cpp similarity index 70% copy from kmymoney/widgets/selectedtransaction.cpp copy to kmymoney/widgets/selectedtransactions.cpp index f436500fd..d3feba723 100644 --- a/kmymoney/widgets/selectedtransaction.cpp +++ b/kmymoney/widgets/selectedtransactions.cpp @@ -1,85 +1,59 @@ /*************************************************************************** selectedtransaction.cpp - description ------------------- begin : Fri Jun 2008 copyright : (C) 2000-2008 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 "selectedtransaction.h" +#include "selectedtransactions.h" // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "register.h" -#include "mymoneysplit.h" -#include "mymoneyfile.h" - -namespace KMyMoneyRegister -{ -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; -} +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; } - -} // namespace diff --git a/kmymoney/widgets/scheduledtransaction.cpp b/kmymoney/widgets/selectedtransactions.h similarity index 56% copy from kmymoney/widgets/scheduledtransaction.cpp copy to kmymoney/widgets/selectedtransactions.h index fe711c57e..f8c658d24 100644 --- a/kmymoney/widgets/scheduledtransaction.cpp +++ b/kmymoney/widgets/selectedtransactions.h @@ -1,49 +1,57 @@ /*************************************************************************** - scheduledtransaction.cpp + selectedtransactions.h - description ------------------- - begin : Tue Aug 19 2008 - copyright : (C) 2008 by Thomas Baumgart + begin : Tue Jun 13 2006 + copyright : (C) 2000-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 "scheduledtransaction.h" +#ifndef SELECTEDTRANSACTIONS_H +#define SELECTEDTRANSACTIONS_H // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneyglobalsettings.h" -#include "transaction.h" - -using namespace KMyMoneyRegister; -using namespace KMyMoneyTransactionForm; +#include "selectedtransaction.h" -StdTransactionScheduled::StdTransactionScheduled(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - StdTransaction(parent, transaction, split, uniqueId) +namespace KMyMoneyRegister { - // setup initial size - setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); -} + class Register; -bool StdTransactionScheduled::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) -{ - bool rc = Transaction::paintRegisterCellSetup(painter, option, index); - option.palette.setCurrentColorGroup(QPalette::Disabled); - return rc; -} + 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 new file mode 100644 index 000000000..db03601d5 --- /dev/null +++ b/kmymoney/widgets/stdtransaction.cpp @@ -0,0 +1,699 @@ +/*************************************************************************** + 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" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneypayeecombo.h" +#include "kmymoneycombo.h" +#include "kmymoneytagcombo.h" +#include "tabbar.h" +#include "ktagcontainer.h" +#include "mymoneytransaction.h" +#include "mymoneysplit.h" +#include "mymoneyfile.h" +#include "register.h" +#include "transactionform.h" +#include "kmymoneylineedit.h" +#include "kmymoneyutils.h" +#ifndef KMM_DESIGNER +#include "stdtransactioneditor.h" +#endif + +#include "kmymoneyglobalsettings.h" +#include "widgetenums.h" + +using namespace eWidgets; +using namespace KMyMoneyRegister; +using namespace KMyMoneyTransactionForm; + +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 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; + } +} + +eRegister::Action StdTransaction::actionType() const +{ + Q_D(const StdTransaction); + eRegister::Action action = eRegister::Action::None; + + // 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() ? eRegister::Action::Withdrawal : eRegister::Action::Deposit; + break; + } + } + // otherwise, it's a transfer + if (it_s == d->m_transaction.splits().end()) + action = eRegister::Action::Transfer; + + return action; +} + +void StdTransaction::loadTab(TransactionForm* form) +{ + Q_D(StdTransaction); + KMyMoneyTransactionForm::TabBar* bar = form->getTabBar(); + bar->setSignalEmission(eTabBar::SignalEmission::Never); + for (auto i = 0; i < bar->count(); ++i) { + bar->setTabEnabled(i, true); + } + + if (d->m_transaction.splitCount() > 0) { + bar->setCurrentIndex((int)actionType()); + } + bar->setSignalEmission(eTabBar::SignalEmission::Always); +} + +int StdTransaction::numColsForm() const +{ + return 4; +} + +void StdTransaction::setupForm(TransactionForm* form) +{ + Transaction::setupForm(form); + form->setSpan(4, (int)eTransactionForm::Column::Value1, 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 (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + txt = i18n("Account"); + break; + } + break; + + case 1: + switch (col) { + case (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + txt = d->m_payeeHeader; + break; + + case (int)eTransactionForm::Column::Value1: + align |= Qt::AlignLeft; + txt = d->m_payee; + break; + + case (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + if (haveNumberField()) + txt = i18n("Number"); + break; + + case (int)eTransactionForm::Column::Value2: + align |= Qt::AlignRight; + if (haveNumberField()) + txt = d->m_split.number(); + break; + } + break; + + case 2: + switch (col) { + case (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + txt = d->m_categoryHeader; + break; + + case (int)eTransactionForm::Column::Value1: + 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 (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + txt = i18n("Date"); + break; + + case (int)eTransactionForm::Column::Value2: + align |= Qt::AlignRight; + if (d->m_transaction != MyMoneyTransaction()) + txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); + break; + } + break; + + case 3: + switch (col) { + case (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + txt = i18n("Tags"); + break; + + case (int)eTransactionForm::Column::Value1: + 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 (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + txt = i18n("Amount"); + break; + + case (int)eTransactionForm::Column::Value2: + 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 (int)eTransactionForm::Column::Label1: + align |= Qt::AlignLeft; + txt = i18n("Memo"); + break; + + case (int)eTransactionForm::Column::Value1: + 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 (int)eTransactionForm::Column::Label2: + align |= Qt::AlignLeft; + txt = i18n("Status"); + break; + + case (int)eTransactionForm::Column::Value2: + align |= Qt::AlignRight; + txt = reconcileState(); + break; + } + } + + // } + if (col == (int)eTransactionForm::Column::Value2 && row == 1) { + return haveNumberField(); + } + return (col == (int)eTransactionForm::Column::Value1 && row < 5) || (col == (int)eTransactionForm::Column::Value2 && 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 (int)eTransaction::Column::Number: + align |= Qt::AlignLeft; + if (haveNumberField()) + txt = d->m_split.number(); + break; + + case (int)eTransaction::Column::Date: + align |= Qt::AlignLeft; + txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); + break; + + case (int)eTransaction::Column::Detail: + switch (d->m_parent->getDetailsColumnType()) { + case eRegister::DetailColumn::PayeeFirst: + txt = d->m_payee; + break; + case eRegister::DetailColumn::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 (int)eTransaction::Column::ReconcileFlag: + align |= Qt::AlignHCenter; + txt = reconcileState(false); + break; + + case (int)eTransaction::Column::Payment: + 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 (int)eTransaction::Column::Deposit: + 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 (int)eTransaction::Column::Balance: + align |= Qt::AlignRight; + if (d->m_showBalance) + txt = d->m_balance.formatMoney(d->m_account.fraction()); + else + txt = "----"; + break; + + case (int)eTransaction::Column::Account: + // 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 (int)eTransaction::Column::Detail: + switch (d->m_parent->getDetailsColumnType()) { + case eRegister::DetailColumn::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 eRegister::DetailColumn::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 (int)eTransaction::Column::Detail: + 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, (int)eTransactionForm::Column::Label1, editWidgets["account-label"]); + arrangeWidget(d->m_form, 0, (int)eTransactionForm::Column::Value1, editWidgets["account"]); + arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Label1, editWidgets["cashflow"]); + arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Value1, editWidgets["payee"]); + arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Label1, editWidgets["category-label"]); + arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Value1, editWidgets["category"]->parentWidget()); + arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Label1, editWidgets["tag-label"]); + arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Value1, editWidgets["tag"]); + arrangeWidget(d->m_form, 4, (int)eTransactionForm::Column::Label1, editWidgets["memo-label"]); + arrangeWidget(d->m_form, 4, (int)eTransactionForm::Column::Value1, editWidgets["memo"]); + if (haveNumberField()) { + arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Label2, editWidgets["number-label"]); + arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Value2, editWidgets["number"]); + } + arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Label2, editWidgets["date-label"]); + arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Value2, editWidgets["postdate"]); + arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Label2, editWidgets["amount-label"]); + arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Value2, editWidgets["amount"]); + arrangeWidget(d->m_form, 5, (int)eTransactionForm::Column::Label2, editWidgets["status-label"]); + arrangeWidget(d->m_form, 5, (int)eTransactionForm::Column::Value2, 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()); + } + + auto form = dynamic_cast(d->m_form); + auto 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->getTabBar()->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, (int)eTransactionForm::Column::Value1))); + } else if (*it_s == "cashflow") { + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, (int)eTransactionForm::Column::Label1))); + } else if (*it_s == "payee") { + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, (int)eTransactionForm::Column::Value1))); + } 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, (int)eTransactionForm::Column::Value1); + 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, (int)eTransactionForm::Column::Value1))); + } else if (*it_s == "memo") { + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(4, (int)eTransactionForm::Column::Value1))); + } else if (*it_s == "number") { + if (haveNumberField()) { + if ((w = focusWidget(d->m_form->cellWidget(1, (int)eTransactionForm::Column::Value2)))) + tabOrderWidgets.append(w); + } + } else if (*it_s == "date") { + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(2, (int)eTransactionForm::Column::Value2))); + } else if (*it_s == "amount") { + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(3, (int)eTransactionForm::Column::Value2))); + } else if (*it_s == "state") { + tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(5, (int)eTransactionForm::Column::Value2))); + } + ++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, (int)eTransaction::Column::Number, editWidgets["number"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Date, editWidgets["postdate"]); + arrangeWidget(d->m_parent, d->m_startRow + 1, (int)eTransaction::Column::Date, editWidgets["status"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Detail, editWidgets["payee"]); + arrangeWidget(d->m_parent, d->m_startRow + 1, (int)eTransaction::Column::Detail, editWidgets["category"]->parentWidget()); + arrangeWidget(d->m_parent, d->m_startRow + 2, (int)eTransaction::Column::Detail, editWidgets["tag"]); + arrangeWidget(d->m_parent, d->m_startRow + 3, (int)eTransaction::Column::Detail, editWidgets["memo"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Payment, editWidgets["payment"]); + arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Deposit, 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, (int)eTransaction::Column::Number)))) + tabOrderWidgets.append(w); + } + } else if (*it_s == "date") { + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Date))); + } else if (*it_s == "payee") { + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Detail))); + } 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, (int)eTransaction::Column::Detail); + 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, (int)eTransaction::Column::Detail))); + } else if (*it_s == "memo") { + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 3, (int)eTransaction::Column::Detail))); + } else if (*it_s == "payment") { + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Payment))); + } else if (*it_s == "deposit") { + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Deposit))); + } else if (*it_s == "state") { + tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 1, (int)eTransaction::Column::Date))); + } + ++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 new file mode 100644 index 000000000..d474f155b --- /dev/null +++ b/kmymoney/widgets/stdtransaction.h @@ -0,0 +1,88 @@ +/*************************************************************************** + stdtransaction.h - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-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 STDTRANSACTION_H +#define STDTRANSACTION_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "transaction.h" + +namespace KMyMoneyRegister +{ + class StdTransactionPrivate; + class StdTransaction : public Transaction + { + Q_DISABLE_COPY(StdTransaction) + + public: + explicit StdTransaction(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + ~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; + eWidgets::eRegister::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); + }; +} // namespace + +#endif diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/stdtransaction_p.h similarity index 65% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/stdtransaction_p.h index d4fd1f27a..7d2ed0620 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/stdtransaction_p.h @@ -1,28 +1,41 @@ /*************************************************************************** - transactioneditorcontainer.cpp - ---------- - begin : Wed Jun 07 2006 - copyright : (C) 2006 by Thomas Baumgart - email : Thomas Baumgart + 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. * * * ***************************************************************************/ -#include "transactioneditorcontainer.h" +#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 22dca1a64..36b22e149 100644 --- a/kmymoney/widgets/stdtransactiondownloaded.cpp +++ b/kmymoney/widgets/stdtransactiondownloaded.cpp @@ -1,67 +1,87 @@ /*************************************************************************** stdtransactiondownloaded.cpp ------------------- begin : Sun May 11 2008 copyright : (C) 2008 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 "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) + StdTransaction(parent, transaction, split, uniqueId) +{ +} + +StdTransactionDownloaded::~StdTransactionDownloaded() +{ +} + +const char* StdTransactionDownloaded::className() { + return "StdTransactionDownloaded"; } bool StdTransactionDownloaded::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - bool rc = Transaction::paintRegisterCellSetup(painter, option, index); + auto 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) { } -bool InvestTransactionDownloaded::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) +InvestTransactionDownloaded::~InvestTransactionDownloaded() +{ +} +const char* InvestTransactionDownloaded::className() +{ + return "InvestTransactionDownloaded"; +} + +bool InvestTransactionDownloaded::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - bool rc = Transaction::paintRegisterCellSetup(painter, option, index); + auto 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 b0cd377fd..466388461 100644 --- a/kmymoney/widgets/stdtransactiondownloaded.h +++ b/kmymoney/widgets/stdtransactiondownloaded.h @@ -1,69 +1,62 @@ /*************************************************************************** stdtransactiondownloaded.h ------------------- begin : Sun May 11 2008 copyright : (C) 2008 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 STDTRANSACTIONDOWNLOADED_H #define STDTRANSACTIONDOWNLOADED_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "transaction.h" - -namespace KMyMoneyTransactionForm -{ -class TransactionForm; -} // namespace +#include "stdtransaction.h" +#include "investtransaction.h" namespace KMyMoneyRegister { -class StdTransactionDownloaded : public StdTransaction -{ -public: - StdTransactionDownloaded(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - virtual ~StdTransactionDownloaded() {} + class StdTransactionDownloaded : public StdTransaction + { + public: + explicit StdTransactionDownloaded(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + ~StdTransactionDownloaded() override; - virtual const char* className() { - return "StdTransactionDownloaded"; - } + const char* className() override; - virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); -}; + bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; + }; -class InvestTransactionDownloaded : public InvestTransaction -{ -public: - InvestTransactionDownloaded(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - virtual ~InvestTransactionDownloaded() {} + class InvestTransactionDownloaded : public InvestTransaction + { + public: + explicit InvestTransactionDownloaded(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + ~InvestTransactionDownloaded() override; - virtual const char* className() { - return "InvestTransactionDownloaded"; - } + const char* className() override; - virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); -}; + bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; + }; } // namespace #endif diff --git a/kmymoney/widgets/stdtransactionmatched.cpp b/kmymoney/widgets/stdtransactionmatched.cpp index 311559aa7..1edda3c35 100644 --- a/kmymoney/widgets/stdtransactionmatched.cpp +++ b/kmymoney/widgets/stdtransactionmatched.cpp @@ -1,183 +1,206 @@ /*************************************************************************** stdtransactionmatched.cpp ------------------- begin : Sat May 11 2008 copyright : (C) 2008 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 "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" +#include "widgetenums.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) { - bool rc = Transaction::paintRegisterCellSetup(painter, option, index); + auto 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 >= m_rowsRegister - m_additionalRows) { + if (row >= RegisterItem::numRowsRegister() - m_additionalRows) { // make row relative to the last three rows - row += m_additionalRows - m_rowsRegister; + row += m_additionalRows - RegisterItem::numRowsRegister(); // remove anything that had been added by the standard method - txt = ""; + txt = QString(); // and we draw this information in italics if (painter) { QFont font = painter->font(); font.setItalic(true); painter->setFont(font); } - MyMoneyTransaction matchedTransaction = m_split.matchedTransaction(); + MyMoneyTransaction matchedTransaction = d->m_split.matchedTransaction(); MyMoneySplit matchedSplit; try { - matchedSplit = matchedTransaction.splitById(m_split.value("kmm-match-split")); + matchedSplit = matchedTransaction.splitById(d->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() == m_account.id()) { + if ((*it_s).accountId() == d->m_account.id()) { importedValue += (*it_s).shares(); } } QDate postDate; QString memo; switch (row) { case 0: - if (painter && col == DetailColumn) + if (painter && col == (int)eWidgets::eTransaction::Column::Detail) 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: + case (int)eWidgets::eTransaction::Column::Date: align |= Qt::AlignLeft; txt = i18n("Bank entry:"); break; - case DetailColumn: + case (int)eWidgets::eTransaction::Column::Detail: 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: + case (int)eWidgets::eTransaction::Column::Payment: align |= Qt::AlignRight; if (importedValue.isNegative()) { - txt = (-importedValue).formatMoney(m_account.fraction()); + txt = (-importedValue).formatMoney(d->m_account.fraction()); } break; - case DepositColumn: + case (int)eWidgets::eTransaction::Column::Deposit: align |= Qt::AlignRight; if (!importedValue.isNegative()) { - txt = importedValue.formatMoney(m_account.fraction()); + txt = importedValue.formatMoney(d->m_account.fraction()); } break; } break; case 2: switch (col) { - case DateColumn: + case (int)eWidgets::eTransaction::Column::Date: align |= Qt::AlignLeft; txt = i18n("Your entry:"); break; - case DetailColumn: + case (int)eWidgets::eTransaction::Column::Detail: align |= Qt::AlignLeft; - postDate = m_transaction.postDate(); - if (!m_split.value("kmm-orig-postdate").isEmpty()) { - postDate = QDate::fromString(m_split.value("kmm-orig-postdate"), Qt::ISODate); + 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); } - memo = m_split.memo(); + memo = d->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: + case (int)eWidgets::eTransaction::Column::Payment: align |= Qt::AlignRight; - if (m_split.value().isNegative()) { - txt = (-m_split.value(m_transaction.commodity(), m_splitCurrencyId)).formatMoney(m_account.fraction()); + 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: + case (int)eWidgets::eTransaction::Column::Deposit: align |= Qt::AlignRight; - if (!m_split.value().isNegative()) { - txt = m_split.value(m_transaction.commodity(), m_splitCurrencyId).formatMoney(m_account.fraction()); + if (!d->m_split.value().isNegative()) { + txt = d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId).formatMoney(d->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 58813ebec..e232dcb26 100644 --- a/kmymoney/widgets/stdtransactionmatched.h +++ b/kmymoney/widgets/stdtransactionmatched.h @@ -1,72 +1,67 @@ /*************************************************************************** stdtransactionmatched.h ------------------- begin : Sat May 31 2008 copyright : (C) 2008 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 STDTRANSACTIONMATCHED_H #define STDTRANSACTIONMATCHED_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "transaction.h" +#include "stdtransaction.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: - StdTransactionMatched(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - virtual ~StdTransactionMatched() {} + public: + explicit StdTransactionMatched(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + ~StdTransactionMatched() override; - virtual const char* className() override { - return "StdTransactionMatched"; - } + const char* className() override; - virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; + 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 { - return StdTransaction::numRowsRegister(expanded) + m_additionalRows; - } + int numRowsRegister(bool expanded) const override; - /** + /** * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() */ - int numRowsRegister() const override { - return StdTransaction::numRowsRegister(); - } -}; + int numRowsRegister() const override; + }; } // namespace #endif diff --git a/kmymoney/widgets/styleditemdelegateforwarder.cpp b/kmymoney/widgets/styleditemdelegateforwarder.cpp index 0c02b1930..0ae6676b8 100644 --- a/kmymoney/widgets/styleditemdelegateforwarder.cpp +++ b/kmymoney/widgets/styleditemdelegateforwarder.cpp @@ -1,63 +1,63 @@ /* * 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 . */ #include "styleditemdelegateforwarder.h" StyledItemDelegateForwarder::StyledItemDelegateForwarder(QObject* parent) : QAbstractItemDelegate(parent) { } void StyledItemDelegateForwarder::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { getItemDelegate(index)->paint(painter, option, index); } QSize StyledItemDelegateForwarder::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QAbstractItemDelegate* delegate = getItemDelegate(index); Q_CHECK_PTR(delegate); return delegate->sizeHint(option, index); } QWidget* StyledItemDelegateForwarder::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { return getItemDelegate(index)->createEditor(parent, option, index); } void StyledItemDelegateForwarder::setEditorData(QWidget* editor, const QModelIndex& index) const { getItemDelegate(index)->setEditorData(editor, index); } void StyledItemDelegateForwarder::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { getItemDelegate(index)->setModelData(editor, model, index); } void StyledItemDelegateForwarder::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const { getItemDelegate(index)->updateEditorGeometry(editor, option, index); } void StyledItemDelegateForwarder::connectSignals(QAbstractItemDelegate* delegate, Qt::ConnectionType type) const { - connect(delegate, SIGNAL(commitData(QWidget*)), this, SIGNAL(commitData(QWidget*)), type); - connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), this, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), type); - connect(delegate, SIGNAL(sizeHintChanged(QModelIndex)), this, SIGNAL(sizeHintChanged(QModelIndex)), type); + connect(delegate, &QAbstractItemDelegate::commitData, this, &QAbstractItemDelegate::commitData, type); + connect(delegate, &QAbstractItemDelegate::closeEditor, this, &QAbstractItemDelegate::closeEditor, type); + connect(delegate, &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemDelegate::sizeHintChanged, type); } diff --git a/kmymoney/widgets/tabbar.cpp b/kmymoney/widgets/tabbar.cpp new file mode 100644 index 000000000..61d2acd9c --- /dev/null +++ b/kmymoney/widgets/tabbar.cpp @@ -0,0 +1,200 @@ +/*************************************************************************** + tabbar.cpp + ------------------- + begin : Sun May 14 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 "tabbar.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "widgetenums.h" + +using namespace eWidgets; +using namespace KMyMoneyTransactionForm; + +namespace KMyMoneyTransactionForm +{ + class TabBarPrivate + { + Q_DISABLE_COPY(TabBarPrivate) + + public: + TabBarPrivate() : + m_signalType(eTabBar::SignalEmission::Normal) + { + } + + eTabBar::SignalEmission 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; + }; +} + +TabBar::TabBar(QWidget* parent) : + QTabBar(parent), + d_ptr(new TabBarPrivate) +{ + Q_D(TabBar); + connect(this, &QTabBar::currentChanged, this, &TabBar::slotTabCurrentChanged); +} + +TabBar::~TabBar() +{ + Q_D(TabBar); + delete d; +} + +eTabBar::SignalEmission TabBar::setSignalEmission(eTabBar::SignalEmission type) +{ + Q_D(TabBar); + eTabBar::SignalEmission _type = d->m_signalType; + d->m_signalType = type; + return _type; +} + +int TabBar::currentIndex() const +{ + Q_D(const TabBar); + QMap::const_iterator it; + int id = QTabBar::currentIndex(); + for (it = d->m_idMap.constBegin(); it != d->m_idMap.constEnd(); ++it) { + if (*it == id) { + return it.key(); + } + } + return -1; +} + +void TabBar::setCurrentIndex(int id) +{ + Q_D(TabBar); + if (d->m_signalType != eTabBar::SignalEmission::Normal) + blockSignals(true); + + if (d->m_idMap.contains(id)) { + QTabBar::setCurrentIndex(d->m_idMap[id]); + } + + if (d->m_signalType != eTabBar::SignalEmission::Normal) + blockSignals(false); + + if (d->m_signalType == eTabBar::SignalEmission::Always) + emit currentChanged(d->m_idMap[id]); +} + +void TabBar::setTabEnabled(int id, bool enable) +{ + Q_D(TabBar); + if (d->m_idMap.contains(id)) { + QTabBar::setTabEnabled(d->m_idMap[id], enable); + } +} + +void TabBar::insertTab(int id, const QString& title) +{ + Q_D(TabBar); + int newId = QTabBar::insertTab(id, title); + d->m_idMap[id] = newId; +} + +void TabBar::insertTab(int id) +{ + insertTab(id, QString()); +} + +void TabBar::slotTabCurrentChanged(int id) +{ + Q_D(TabBar); + QMap::const_iterator it; + for (it = d->m_idMap.constBegin(); it != d->m_idMap.constEnd(); ++it) { + if (*it == id) { + emit tabCurrentChanged(it.key()); + break; + } + } + if (it == d->m_idMap.constEnd()) + emit tabCurrentChanged(id); +} + +void TabBar::showEvent(QShowEvent* event) +{ + Q_D(TabBar); + // make sure we don't emit a signal when simply showing the widget + if (d->m_signalType != eTabBar::SignalEmission::Normal) + blockSignals(true); + + QTabBar::showEvent(event); + + if (d->m_signalType != eTabBar::SignalEmission::Normal) + blockSignals(false); +} + +void TabBar::copyTabs(const TabBar* otabbar) +{ + Q_D(TabBar); + // remove all existing tabs + while (count()) { + removeTab(0); + } + + // now create new ones. copy text, icon and identifier + d->m_idMap = otabbar->d_func()->m_idMap; + + for (auto 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) + 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()); + } +} diff --git a/kmymoney/widgets/tabbar.h b/kmymoney/widgets/tabbar.h new file mode 100644 index 000000000..e53063a18 --- /dev/null +++ b/kmymoney/widgets/tabbar.h @@ -0,0 +1,98 @@ +/*************************************************************************** + tabbar.h + ---------- + begin : Sun May 14 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 TABBAR_H +#define TABBAR_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +namespace eWidgets { namespace eTabBar { enum class SignalEmission; } } + +namespace KMyMoneyTransactionForm +{ + /** + * @author Thomas Baumgart + */ + class TabBarPrivate; + class TabBar : public QTabBar + { + Q_OBJECT + Q_DISABLE_COPY(TabBar) + + public: + explicit TabBar(QWidget* parent = nullptr); + ~TabBar(); + + eWidgets::eTabBar::SignalEmission setSignalEmission(eWidgets::eTabBar::SignalEmission type); + + void copyTabs(const TabBar* otabbar); + + void insertTab(int id, const QString& title); + void insertTab(int id); + + 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 id); + + /** + * overridden for internal reasons, API not changed + */ + void showEvent(QShowEvent* event) override; + + protected: + void mousePressEvent(QMouseEvent* event) override; + + 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: + TabBarPrivate * const d_ptr; + Q_DECLARE_PRIVATE(TabBar) + }; + +} // namespace + +#endif diff --git a/kmymoney/widgets/transaction.cpp b/kmymoney/widgets/transaction.cpp index 664461028..60825cbf0 100644 --- a/kmymoney/widgets/transaction.cpp +++ b/kmymoney/widgets/transaction.cpp @@ -1,2201 +1,931 @@ /*************************************************************************** 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 "registerfilter.h" +#include "tabbar.h" #include "kmymoneyglobalsettings.h" +#include "widgetenums.h" +using namespace eWidgets; 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(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - 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) + RegisterItem(*new TransactionPrivate, parent) { - MyMoneyFile* file = MyMoneyFile::instance(); + 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); +} - // load the account - if (!m_split.accountId().isEmpty()) - m_account = file->account(m_split.accountId()); +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); +} - // 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"); - } +Transaction::~Transaction() +{ +} - // 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(); - } - } +const char* Transaction::className() +{ + return "Transaction"; +} + +bool Transaction::isSelectable() const +{ + return true; +} - // load the currency - if (!m_transaction.id().isEmpty()) - m_splitCurrencyId = m_account.currencyId(); +bool Transaction::isSelected() const +{ + Q_D(const Transaction); + return d->m_selected; +} - // check if transaction is erroneous or not - m_erroneous = !m_transaction.splitSum().isZero(); +void Transaction::setSelected(bool selected) +{ + Q_D(Transaction); + if (!selected || (selected && isVisible())) + d->m_selected = selected; +} - if (!m_uniqueId.isEmpty()) { - m_uniqueId += '-'; - QString id; - id.setNum(uniqueId); - m_uniqueId += id.rightJustified(3, '0'); - } +bool Transaction::canHaveFocus() const +{ + return true; +} + +bool Transaction::hasFocus() const +{ + Q_D(const Transaction); + return d->m_focus; +} + +bool Transaction::hasEditorOpen() const +{ + Q_D(const Transaction); + return d->m_inEdit; +} + +bool Transaction::isScheduled() const +{ + return false; } void Transaction::setFocus(bool focus, bool updateLens) { - if (focus != m_focus) { - m_focus = focus; + Q_D(Transaction); + if (focus != d->m_focus) { + d->m_focus = focus; } if (updateLens) { if (KMyMoneyGlobalSettings::ledgerLens() || !KMyMoneyGlobalSettings::transactionForm() || KMyMoneyGlobalSettings::showRegisterDetailed() - || m_parent->m_ledgerLensForced) { + || d->m_parent->ledgerLens()) { 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; +} + +eRegister::CashFlowDirection Transaction::sortType() const +{ + Q_D(const Transaction); + return d->m_split.shares().isNegative() ? eRegister::CashFlowDirection::Payment : eRegister::CashFlowDirection::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 (m_reducedIntensity) { + if (d->m_reducedIntensity) { option.palette.setColor(QPalette::Text, option.palette.color(QPalette::Disabled, QPalette::Text)); } - if (m_selected) { + if (d->m_selected) { option.state |= QStyle::State_Selected; } else { option.state &= ~QStyle::State_Selected; } - if (m_focus) { + if (d->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) { + } else if (index.column() == (int)eTransaction::Column::LastColumn - 1) { option.viewItemPosition = QStyleOptionViewItem::End; } else { option.viewItemPosition = QStyleOptionViewItem::Middle; } // do we need to switch to the error color? - if (m_erroneous) { + if (d->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 = m_balance.isNegative(); - if (m_account.accountGroup() == eMyMoney::Account::Liability && !m_balance.isZero()) + if (index.column() == (int)eTransaction::Column::Balance) { + bool showNegative = d->m_balance.isNegative(); + if (d->m_account.accountGroup() == eMyMoney::Account::Liability && !d->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 = m_parent->visualRect(previousRowItem); + optionSibling.rect = d->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 (m_inRegisterEdit) { + if (d->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 += m_parent->visualRect(index.sibling(i, index.column())).height(); + extraHeight += d->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 (m_focus && index.column() == DetailColumn) { + if (d->m_focus && index.column() == (int)eTransaction::Column::Detail) { option.state |= QStyle::State_HasFocus; style->drawPrimitive(QStyle::PE_FrameFocusRect, &option, painter, widget); } option.rect = old; } } else { - if (m_alternate) { + if (d->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 (m_transaction != MyMoneyTransaction() && !m_inRegisterEdit) { + if (d->m_transaction != MyMoneyTransaction() && !d->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 (m_selected) + if (d->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, m_selected ? QPalette::HighlightedText : QPalette::Text); + style->drawItemText(painter, option.rect.adjusted(2, 0, -2, 0), option.displayAlignment, option.palette, true, txt, d->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 (m_erroneous) { + if (index.row() == startRow() && index.column() == (int)eTransaction::Column::Detail) { + if (d->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*/) { - if (m_formRowHeight < 0) { - m_formRowHeight = formRowHeight(); + Q_D(Transaction); + if (d->m_formRowHeight < 0) { + d->m_formRowHeight = formRowHeight(); } - return m_formRowHeight; + return d->m_formRowHeight; } int Transaction::formRowHeight() const { - if (m_formRowHeight < 0) { + Q_D(const Transaction); + if (d->m_formRowHeight < 0) { // determine the height of the objects in the table - kMyMoneyDateInput dateInput; - KMyMoneyCategory category(0, true); + KMyMoneyDateInput dateInput; + KMyMoneyCategory category(true, nullptr); return qMax(dateInput.sizeHint().height(), category.sizeHint().height()); } - return m_formRowHeight; + return d->m_formRowHeight; } void Transaction::setupForm(TransactionForm* form) { - m_form = form; + Q_D(Transaction); + d->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) { - if (!m_form) + Q_D(Transaction); + if (!d->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 && !m_inEdit) { + if (editField && !d->m_inEdit) { painter->fillRect(textRect, option.palette.alternateBase()); } - if (!m_inEdit) + if (!d->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) { - QPalette palette = m_parent->palette(); + Q_D(Transaction); + QPalette palette = d->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 = m_parent->palette(); + QPalette palette = d->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 { - bool rc = true; - switch (m_account.accountType()) { + Q_D(const Transaction); + auto rc = 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: 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) { - if (col != DetailColumn) + Q_D(Transaction); + if (col != (int)eTransaction::Column::Detail) return false; - if (!m_erroneous && m_transaction.splitCount() < 3) + if (!d->m_erroneous && d->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 = m_parent->visualRect(m_parent->model()->index(m_startRow + 1, col)); + r = d->m_parent->visualRect(d->m_parent->model()->index(d->m_startRow + 1, col)); r.setBottom(r.bottom() + (numRowsRegister() - 1)*r.height()); - if (r.contains(cpos) && m_erroneous) { - if (m_transaction.splits().count() < 2) { + if (r.contains(cpos) && d->m_erroneous) { + if (d->m_transaction.splits().count() < 2) { msg = QString("%1").arg(i18n("Transaction is missing a category assignment.")); } else { - 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))); + 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))); } 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) && m_transaction.splitCount() > 2) { - MyMoneyFile* file = MyMoneyFile::instance(); + if (row == 1 && r.contains(cpos) && d->m_transaction.splitCount() > 2) { + auto file = MyMoneyFile::instance(); QList::const_iterator it_s; QString txt; - const MyMoneySecurity& sec = file->security(m_transaction.commodity()); + const MyMoneySecurity& sec = file->security(d->m_transaction.commodity()); MyMoneyMoney factor(1, 1); - if (!m_split.value().isNegative()) + if (!d->m_split.value().isNegative()) factor = -factor; - for (it_s = m_transaction.splits().constBegin(); it_s != m_transaction.splits().constEnd(); ++it_s) { - if (*it_s == m_split) + for (it_s = d->m_transaction.splits().constBegin(); it_s != d->m_transaction.splits().constEnd(); ++it_s) { + if (*it_s == d->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 { - QString txt = KMyMoneyUtils::reconcileStateToString(m_split.reconcileFlag(), text); + Q_D(const Transaction); + auto txt = KMyMoneyUtils::reconcileStateToString(d->m_split.reconcileFlag(), text); if ((text == true) && (txt == i18nc("Unknown reconciliation state", "Unknown")) - && (m_transaction == MyMoneyTransaction())) + && (d->m_transaction == MyMoneyTransaction())) txt.clear(); return txt; } void Transaction::startEditMode() { - m_inEdit = true; + Q_D(Transaction); + d->m_inEdit = true; // hide the original tabbar since the edit tabbar will be added - KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(m_form); - form->tabBar()->setVisible(false); + KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(d->m_form); + form->getTabBar()->setVisible(false); // only update the number of lines displayed if we edit inside the register - if (m_inRegisterEdit) + if (d->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(m_form); - form->tabBar()->setVisible(true); + KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(d->m_form); + form->getTabBar()->setVisible(true); // make sure we reset the row height of all the transaction's rows because it could have been changed during edit - if (m_parent) { - for (int i = 0; i < numRowsRegister(); ++i) - m_parent->setRowHeight(m_startRow + i, m_parent->rowHeightHint()); + if (d->m_parent) { + for (auto i = 0; i < numRowsRegister(); ++i) + d->m_parent->setRowHeight(d->m_startRow + i, d->m_parent->rowHeightHint()); } - m_inEdit = false; - m_inRegisterEdit = false; + d->m_inEdit = false; + d->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 { - return m_inEdit ? formRowHeight() : RegisterItem::rowHeightHint(); + Q_D(const Transaction); + return d->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: + case eRegister::ItemState::Imported: if (!transaction().isImported()) return false; break; - case RegisterFilter::Matched: + case eRegister::ItemState::Matched: if (!split().isMatched()) return false; break; - case RegisterFilter::Erroneous: + case eRegister::ItemState::Erroneous: if (transaction().splitSum().isZero()) return false; break; - case RegisterFilter::NotMarked: + case eRegister::ItemState::NotMarked: if (split().reconcileFlag() != eMyMoney::Split::State::NotReconciled) return false; break; - case RegisterFilter::NotReconciled: + case eRegister::ItemState::NotReconciled: if (split().reconcileFlag() != eMyMoney::Split::State::NotReconciled && split().reconcileFlag() != eMyMoney::Split::State::Cleared) return false; break; - case RegisterFilter::Cleared: + case eRegister::ItemState::Cleared: if (split().reconcileFlag() != eMyMoney::Split::State::Cleared) return false; break; } } // check if the text matches - if (filter.text.isEmpty() || m_transaction.splitCount() == 0) + if (filter.text.isEmpty() || d->m_transaction.splitCount() == 0) return true; - MyMoneyFile* file = MyMoneyFile::instance(); + auto file = MyMoneyFile::instance(); - const QList&list = m_transaction.splits(); + const QList&list = d->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 (int i = 0; i < t.count(); i++) { + for (auto 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(m_account.fraction(), false); + QString r = (*it_s).value().formatMoney(d->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) { - m_showBalance = 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; } 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->m_showBalance) + if (!t->d_func()->m_showBalance) break; - t->m_showBalance = false; + t->d_func()->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->m_showBalance) || !t) { - m_showBalance = true; + if ((t && t->d_func()->m_showBalance) || !t) { + d->m_showBalance = true; p = prevItem(); while (p && p->isVisible()) { t = dynamic_cast(p); if (t) { - if (t->m_showBalance) + if (t->d_func()->m_showBalance) break; - t->m_showBalance = true; + t->d_func()->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 574e21745..571d68898 100644 --- a/kmymoney/widgets/transaction.h +++ b/kmymoney/widgets/transaction.h @@ -1,453 +1,215 @@ /*************************************************************************** transaction.h - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-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 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; -namespace KMyMoneyTransactionForm -{ -class TransactionForm; -} // namespace +class MyMoneySplit; +class MyMoneyTransaction; -namespace KMyMoneyRegister -{ +template class QMap; + +namespace KMyMoneyTransactionForm { class TransactionForm; } +namespace eWidgets { namespace eRegister { enum class Action; } } -// 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 +namespace KMyMoneyRegister { -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; - } - - /** + class SelectedTransactions; + // keep the following list in sync with code in the constructor + // of KMyMoneyRegister::Register in register.cpp + + class TransactionPrivate; + class Transaction : public RegisterItem + { + Q_DISABLE_COPY(Transaction) + + public: + explicit Transaction(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + 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 eWidgets::eRegister::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 */); + + /** * 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 eWidgets::eRegister::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 { - return RegisterItem::numRowsRegister(); - } + int numRowsRegister() const override; - 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 { - Q_UNUSED(row) return 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) { - Q_UNUSED(row); Q_UNUSED(show) - } + virtual void setShowRowInForm(int row, bool show); - virtual void setReducedIntensity(bool reduced) { - m_reducedIntensity = reduced; - } + virtual void setReducedIntensity(bool 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; - - 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 const char* className() { - return "StdTransaction"; - } - - 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 { - return 4; - } - - 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 singleLineMemo(QString& txt, const MyMoneySplit& split) const; - 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; - } + virtual void setupPalette(const QPalette& palette, QMap& editWidgets); - 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; -}; + 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 + private: + Q_DECLARE_PRIVATE(Transaction) + }; } // namespace #endif diff --git a/kmymoney/widgets/transaction_p.h b/kmymoney/widgets/transaction_p.h new file mode 100644 index 000000000..546a1f7d9 --- /dev/null +++ b/kmymoney/widgets/transaction_p.h @@ -0,0 +1,134 @@ +/*************************************************************************** + 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 "registeritem_p.h" + +// ---------------------------------------------------------------------------- +// 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" + +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 d4fd1f27a..9e380fe60 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/transactioneditorcontainer.cpp @@ -1,28 +1,42 @@ /*************************************************************************** transactioneditorcontainer.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 "transactioneditorcontainer.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +TransactionEditorContainer::TransactionEditorContainer(QWidget* parent) : QTableWidget(parent) +{ +} + +TransactionEditorContainer::~TransactionEditorContainer() +{ +} + +void TransactionEditorContainer::updateGeometries() +{ + QTableWidget::updateGeometries(); + emit geometriesUpdated(); +} diff --git a/kmymoney/widgets/transactioneditorcontainer.h b/kmymoney/widgets/transactioneditorcontainer.h index 754caf189..ec042c831 100644 --- a/kmymoney/widgets/transactioneditorcontainer.h +++ b/kmymoney/widgets/transactioneditorcontainer.h @@ -1,70 +1,58 @@ /*************************************************************************** transactioneditorcontainer.h ---------- 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. * * * ***************************************************************************/ #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; +namespace KMyMoneyRegister { class Transaction; } class TransactionEditorContainer : public QTableWidget { Q_OBJECT + Q_DISABLE_COPY(TransactionEditorContainer) public: - TransactionEditorContainer(QWidget* parent) : QTableWidget(parent) {} + explicit TransactionEditorContainer(QWidget* parent); + virtual ~TransactionEditorContainer(); 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() { - QTableWidget::updateGeometries(); - emit geometriesUpdated(); - } + void updateGeometries(); }; #endif diff --git a/kmymoney/widgets/transactionform.cpp b/kmymoney/widgets/transactionform.cpp index abfd98120..5215a651d 100644 --- a/kmymoney/widgets/transactionform.cpp +++ b/kmymoney/widgets/transactionform.cpp @@ -1,452 +1,392 @@ /*************************************************************************** transactionform.cpp ------------------- begin : Sun May 14 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 "transactionform.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include +#include +#include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes +#include "transactionformitemdelegate.h" +#include "tabbar.h" #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); - } -} +#include "widgetenums.h" -void TabBar::insertTab(int id, const QString& title) -{ - int newId = QTabBar::insertTab(id, title); - m_idMap[id] = newId; -} +using namespace eWidgets; +using namespace KMyMoneyTransactionForm; -void TabBar::slotTabCurrentChanged(int id) +namespace KMyMoneyTransactionForm { - QMap::const_iterator it; - for (it = m_idMap.constBegin(); it != m_idMap.constEnd(); ++it) { - if (*it == id) { - emit tabCurrentChanged(it.key()); - break; + class TransactionFormPrivate + { + Q_DISABLE_COPY(TransactionFormPrivate) + + public: + TransactionFormPrivate() : + m_transaction(nullptr), + m_tabBar(nullptr) + { } - } - 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 (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 (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); + KMyMoneyRegister::Transaction *m_transaction; + KMyMoneyTransactionForm::TabBar *m_tabBar; + TransactionFormItemDelegate *m_itemDelegate; + }; } TransactionForm::TransactionForm(QWidget *parent) : TransactionEditorContainer(parent), - m_transaction(0), - m_tabBar(0) + d_ptr(new TransactionFormPrivate) { - m_itemDelegate = new TransactionFormItemDelegate(this); + Q_D(TransactionForm); + d->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); } +TransactionForm::~TransactionForm() +{ + Q_D(TransactionForm); + delete d; +} + bool TransactionForm::focusNextPrevChild(bool next) { return QFrame::focusNextPrevChild(next); } void TransactionForm::clear() { slotSetTransaction(0); } void TransactionForm::enableTabBar(bool b) { - m_tabBar->setEnabled(b); + Q_D(TransactionForm); + d->m_tabBar->setEnabled(b); +} + +void TransactionForm::contentsMousePressEvent(QMouseEvent* ev) +{ + ev->ignore(); } +void TransactionForm::contentsMouseMoveEvent(QMouseEvent* ev) +{ + ev->ignore(); +} + +void TransactionForm::contentsMouseReleaseEvent(QMouseEvent* ev) +{ + ev->ignore(); +} + +void TransactionForm::contentsMouseDoubleClickEvent(QMouseEvent* ev) +{ + ev->ignore(); +} + +void TransactionForm::keyPressEvent(QKeyEvent* ev) +{ + ev->ignore(); +} + + void TransactionForm::slotSetTransaction(KMyMoneyRegister::Transaction* transaction) { - m_transaction = transaction; + Q_D(TransactionForm); + d->m_transaction = transaction; setUpdatesEnabled(false); - if (m_transaction) { + if (d->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); + d->m_transaction->setupForm(this); } else { setRowCount(5); setColumnCount(1); } - kMyMoneyDateInput dateInput; - KMyMoneyCategory category(nullptr, true); + KMyMoneyDateInput dateInput; + KMyMoneyCategory category(true, nullptr); // 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 (int i = 0; i < rowCount(); ++i) { - setItemDelegateForRow(i, m_itemDelegate); + for (auto i = 0; i < rowCount(); ++i) { + setItemDelegateForRow(i, d->m_itemDelegate); } // force resizeing of the columns - QMetaObject::invokeMethod(this, "resize", Qt::QueuedConnection, QGenericReturnArgument(), Q_ARG(int, ValueColumn1)); + QMetaObject::invokeMethod(this, "resize", Qt::QueuedConnection, QGenericReturnArgument(), Q_ARG(int, (int)eTransactionForm::Column::Value1)); } void TransactionForm::paintCell(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) { - if (m_transaction) { - m_transaction->paintFormCell(painter, option, index); + Q_D(TransactionForm); + if (d->m_transaction) { + d->m_transaction->paintFormCell(painter, option, index); } } -TabBar* TransactionForm::tabBar(QWidget* parent) +void TransactionForm::setCurrentCell(int, int) +{ +} + +KMyMoneyTransactionForm::TabBar* TransactionForm::getTabBar(QWidget* parent) { - if (!m_tabBar && parent) { + Q_D(TransactionForm); + if (!d->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); + d->m_tabBar = new TabBar(parent); + d->m_tabBar->setSignalEmission(eTabBar::SignalEmission::Always); 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))); + sizePolicy.setHeightForWidth(d->m_tabBar->sizePolicy().hasHeightForWidth()); + d->m_tabBar->setSizePolicy(sizePolicy); + connect(d->m_tabBar, &TabBar::tabCurrentChanged, this, &TransactionForm::slotActionSelected); } - return m_tabBar; + return d->m_tabBar; } void TransactionForm::slotActionSelected(int id) { - emit newTransaction(static_cast(id)); + emit newTransaction(static_cast(id)); } void TransactionForm::setupForm(const MyMoneyAccount& acc) { - bool blocked = m_tabBar->blockSignals(true); + Q_D(TransactionForm); + bool blocked = d->m_tabBar->blockSignals(true); // remove all tabs from the tabbar - while (m_tabBar->count()) - m_tabBar->removeTab(0); + while (d->m_tabBar->count()) + d->m_tabBar->removeTab(0); - m_tabBar->show(); + d->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")); + d->m_tabBar->insertTab((int)eRegister::Action::Deposit, i18n("&Deposit")); + d->m_tabBar->insertTab((int)eRegister::Action::Transfer, i18n("&Transfer")); + d->m_tabBar->insertTab((int)eRegister::Action::Withdrawal, 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")); + d->m_tabBar->insertTab((int)eRegister::Action::Deposit, i18n("&Payment")); + d->m_tabBar->insertTab((int)eRegister::Action::Transfer, i18n("&Transfer")); + d->m_tabBar->insertTab((int)eRegister::Action::Withdrawal, 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")); + d->m_tabBar->insertTab((int)eRegister::Action::Deposit, i18n("&Decrease")); + d->m_tabBar->insertTab((int)eRegister::Action::Transfer, i18n("&Transfer")); + d->m_tabBar->insertTab((int)eRegister::Action::Withdrawal, 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")); + d->m_tabBar->insertTab((int)eRegister::Action::Deposit, i18n("&Increase")); + d->m_tabBar->insertTab((int)eRegister::Action::Transfer, i18n("&Transfer")); + d->m_tabBar->insertTab((int)eRegister::Action::Withdrawal, i18n("&Decrease")); break; case eMyMoney::Account::Income: case eMyMoney::Account::Expense: case eMyMoney::Account::Investment: case eMyMoney::Account::Stock: - m_tabBar->hide(); + d->m_tabBar->hide(); break; } - m_tabBar->blockSignals(blocked); + d->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 (int i = 0; i < nc; ++i) { + if (nc >= (int)eTransactionForm::Column::Label1 && columnWidth((int)eTransactionForm::Column::Label1)) + adjustColumn(eTransactionForm::Column::Label1); + if (nc >= (int)eTransactionForm::Column::Value1 && columnWidth((int)eTransactionForm::Column::Value1)) + adjustColumn(eTransactionForm::Column::Value1); + if (nc >= (int)eTransactionForm::Column::Label2 && columnWidth((int)eTransactionForm::Column::Label2)) + adjustColumn(eTransactionForm::Column::Label2); + if (nc >= (int)eTransactionForm::Column::Value2 && columnWidth((int)eTransactionForm::Column::Value2)) + adjustColumn(eTransactionForm::Column::Value2); + + for (auto 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) +void TransactionForm::paintFocus(QPainter* /*p*/, const QRect& /*cr*/) { +} + +void TransactionForm::adjustColumn(eTransactionForm::Column col) +{ + Q_D(TransactionForm); 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; + if (col == eTransactionForm::Column::Value2) { + KMyMoneyDateInput dateInput; + KMyMoneyEdit valInput; w = qMax(dateInput.sizeHint().width(), valInput.sizeHint().width()); } - if (m_transaction) { + if (d->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); + d->m_transaction->formCellText(txt, align, i, static_cast(col), 0); + QWidget* cw = cellWidget(i, (int)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); + if ((int)col < columnCount()) + setColumnWidth((int)col, w); } void TransactionForm::arrangeEditWidgets(QMap& editWidgets, KMyMoneyRegister::Transaction* t) { t->arrangeWidgetsInForm(editWidgets); - resize(ValueColumn1); + resize((int)eTransactionForm::Column::Value1); } 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); + resize((int)eTransactionForm::Column::Value1); // 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 96400ef2d..760c08423 100644 --- a/kmymoney/widgets/transactionform.h +++ b/kmymoney/widgets/transactionform.h @@ -1,242 +1,136 @@ /*************************************************************************** transactionform.h ---------- begin : Sun May 14 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 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 eWidgets { namespace eRegister { enum class Action; } + namespace eTransactionForm { enum class Column; } } namespace KMyMoneyTransactionForm { - -/** - * @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 = 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: + class TabBar; /** - * 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 = 0); - virtual ~TransactionForm() {} + class TransactionFormPrivate; + class TransactionForm : public TransactionEditorContainer + { + Q_OBJECT + Q_DISABLE_COPY(TransactionForm) - /** + public: + explicit TransactionForm(QWidget* parent = nullptr); + ~TransactionForm(); + + /** * Override the QTable member function to avoid display of focus */ - void paintFocus(QPainter* /*p*/, const QRect& /*cr*/) {} + void paintFocus(QPainter* /*p*/, const QRect& /*cr*/); - void adjustColumn(Column col); - void clear(); + void adjustColumn(eWidgets::eTransactionForm::Column col); + void clear(); - void paintCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index); + 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; + void arrangeEditWidgets(QMap& editWidgets, KMyMoneyRegister::Transaction* t) override; + void removeEditWidgets(QMap& editWidgets) override; + void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const override; - /** + /** * reimplemented to prevent normal cell selection behavior */ - void setCurrentCell(int, int) {} + void setCurrentCell(int, int); - TabBar* tabBar(QWidget* parent = 0); + TabBar* getTabBar(QWidget* parent = nullptr); - void setupForm(const MyMoneyAccount& acc); + void setupForm(const MyMoneyAccount& acc); - void enableTabBar(bool b); + void enableTabBar(bool b); -protected: + protected: - /** + /** * reimplemented to prevent normal mouse press behavior */ - void contentsMousePressEvent(QMouseEvent* ev) { - ev->ignore(); - } + void contentsMousePressEvent(QMouseEvent* ev); - /** + /** * reimplemented to prevent normal mouse move behavior */ - void contentsMouseMoveEvent(QMouseEvent* ev) { - ev->ignore(); - } + void contentsMouseMoveEvent(QMouseEvent* ev); - /** + /** * reimplemented to prevent normal mouse release behavior */ - void contentsMouseReleaseEvent(QMouseEvent* ev) { - ev->ignore(); - } + void contentsMouseReleaseEvent(QMouseEvent* ev); - /** + /** * reimplemented to prevent normal mouse double click behavior */ - void contentsMouseDoubleClickEvent(QMouseEvent* ev) { - ev->ignore(); - } + void contentsMouseDoubleClickEvent(QMouseEvent* ev); - /** + /** * reimplemented to prevent normal keyboard behavior */ - void keyPressEvent(QKeyEvent* ev) { - ev->ignore(); - } + void keyPressEvent(QKeyEvent* ev) override; - /** + /** * Override logic and use standard QFrame behaviour */ - bool focusNextPrevChild(bool next); + bool focusNextPrevChild(bool next) override; -public slots: - void slotSetTransaction(KMyMoneyRegister::Transaction* item); - void resize(int col); + public slots: + void slotSetTransaction(KMyMoneyRegister::Transaction* item); + void resize(int col); -protected slots: - /** + protected slots: + /** * Helper method to convert @a int into @a KMyMoneyRegister::Action */ - void slotActionSelected(int); + void slotActionSelected(int); -signals: - /** + 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; -}; - + void newTransaction(eWidgets::eRegister::Action id); + private: + TransactionFormPrivate * const d_ptr; + Q_DECLARE_PRIVATE(TransactionForm) + }; } // namespace #endif diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/transactionformitemdelegate.cpp similarity index 63% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/transactionformitemdelegate.cpp index d4fd1f27a..e03fc61c9 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/transactionformitemdelegate.cpp @@ -1,28 +1,41 @@ /*************************************************************************** - transactioneditorcontainer.cpp - ---------- - begin : Wed Jun 07 2006 + transactionformitemdelegate.cpp + ------------------- + begin : Sun May 14 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 "transactioneditorcontainer.h" +#include "transactionformitemdelegate.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "transactionform.h" + +using namespace KMyMoneyTransactionForm; + +TransactionFormItemDelegate::TransactionFormItemDelegate(TransactionForm *parent) : QStyledItemDelegate(parent), m_transactionForm(parent) +{ +} + +void TransactionFormItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + m_transactionForm->paintCell(painter, option, index); +} diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/transactionformitemdelegate.h similarity index 61% copy from kmymoney/widgets/transactioneditorcontainer.cpp copy to kmymoney/widgets/transactionformitemdelegate.h index d4fd1f27a..a5aab124e 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/transactionformitemdelegate.h @@ -1,28 +1,51 @@ /*************************************************************************** - transactioneditorcontainer.cpp + transactionformitemdelegate.h ---------- - begin : Wed Jun 07 2006 + begin : Sun May 14 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 "transactioneditorcontainer.h" +#ifndef TRANSACTIONFORMITEMDELEGATE_H +#define TRANSACTIONFORMITEMDELEGATE_H // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +namespace KMyMoneyTransactionForm +{ + class TransactionForm; + class TransactionFormItemDelegate : public QStyledItemDelegate + { + Q_OBJECT + Q_DISABLE_COPY(TransactionFormItemDelegate) + + public: + explicit TransactionFormItemDelegate(TransactionForm *parent); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + + private: + TransactionForm *m_transactionForm; + }; +} // namespace + +#endif diff --git a/kmymoney/widgets/transactionsortoptionimpl.cpp b/kmymoney/widgets/transactionsortoption.cpp similarity index 73% rename from kmymoney/widgets/transactionsortoptionimpl.cpp rename to kmymoney/widgets/transactionsortoption.cpp index 71e5149f5..c6dfd0911 100644 --- a/kmymoney/widgets/transactionsortoptionimpl.cpp +++ b/kmymoney/widgets/transactionsortoption.cpp @@ -1,294 +1,324 @@ /* This file is part of the KDE project Copyright (C) 2009 Laurent Montel + (C) 2017 by Łukasz Wojniłowicz 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 "transactionsortoptionimpl.h" +#include "transactionsortoption.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "register.h" #include "icons/icons.h" +#include "widgetenums.h" -#include "ui_transactionsortoptiondecl.h" +#include "ui_transactionsortoption.h" +using namespace eWidgets; using namespace Icons; -TransactionSortOption::TransactionSortOption(QWidget *parent) - : QWidget(parent) +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) { - 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*))); + connect(qApp, &QApplication::focusChanged, + this, &TransactionSortOption::slotFocusChanged); +} + +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(KMyMoneyRegister::EntryDateSort)) { + if (abs(val) == static_cast(SortField::EntryDate)) { 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(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; + if (selectedMap.find(static_cast(SortField::EntryDate)) != selectedMap.end() + && selectedMap.find(static_cast(SortField::EntryOrder)) == selectedMap.end()) { + int val = dateSign * static_cast(SortField::EntryOrder); + selectedMap[static_cast(SortField::EntryOrder)] = true; last = addEntry(ui->m_selectedList, last, val); } // fill available list QMap::const_iterator it_m; - for (int i = static_cast(KMyMoneyRegister::PostDateSort); - i < static_cast(KMyMoneyRegister::MaxSortFields); ++i) { + for (int i = static_cast(SortField::PostDate); + i < static_cast(SortField::MaxFields); ++i) { // Never add EntryDateSort - if (i == static_cast(KMyMoneyRegister::EntryDateSort)) + if (i == static_cast(SortField::EntryDate)) 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(KMyMoneyRegister::ValueSort)) + if (i == static_cast(SortField::Value)) 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) { - QString txt = KMyMoneyRegister::sortOrderToText(static_cast(abs(idx))); + auto txt = 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); - QListWidgetItem* item = p->item(row); + auto 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; - QListWidgetItem* item = dynamic_cast(ui->m_selectedList->item(0)); + auto item = dynamic_cast(ui->m_selectedList->item(0)); while (item) { - int option = KMyMoneyRegister::textToSortOrder(item->text()); + auto option = textToSortOrder(item->text()); // if we look at the EntryOrderSort option, we have to make // sure, that the EntryDateSort is prepended - if (option == KMyMoneyRegister::EntryOrderSort) { - rc += QString::number(static_cast(KMyMoneyRegister::EntryDateSort) * item->data(Qt::UserRole).toInt()) + ','; + if (option == SortField::EntryOrder) { + rc += QString::number(static_cast(SortField::EntryDate) * item->data(Qt::UserRole).toInt()) + ','; } - rc += QString::number(KMyMoneyRegister::textToSortOrder(item->text()) * item->data(Qt::UserRole).toInt()); + rc += QString::number((int)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() { - QListWidgetItem* item = ui->m_availableList->currentItem(); + auto 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() { - QListWidgetItem* item = ui->m_selectedList->currentItem(); + auto 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) { - QListWidgetItem* next = ui->m_availableList->item(ui->m_availableList->row(item) + 1); + auto 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) { - QListWidgetItem* next = ui->m_selectedList->item(ui->m_selectedList->row(item) + 1); + auto 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() { - QListWidgetItem *item = ui->m_selectedList->currentItem(); - QListWidgetItem *prev = ui->m_selectedList->item(ui->m_selectedList->row(item) - 1); + auto item = ui->m_selectedList->currentItem(); + auto 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() { - QListWidgetItem *item = ui->m_selectedList->currentItem(); - QListWidgetItem *next = ui->m_selectedList->item(ui->m_selectedList->row(item) + 1); + auto item = ui->m_selectedList->currentItem(); + auto 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()); } } + +SortField TransactionSortOption::textToSortOrder(const QString& text) +{ + for (auto idx = 1; idx < static_cast(SortField::MaxFields); ++idx) { + if (text == i18n(sortOrderText[idx])) { + return static_cast(idx); + } + } + return SortField::Unknown; +} + +QString TransactionSortOption::sortOrderToText(SortField idx) +{ + if ((int)idx < (int)SortField::PostDate || (int)idx >= (int)SortField::MaxFields) + idx = SortField::Unknown; + return i18n(sortOrderText[(int)idx]); +} diff --git a/kmymoney/widgets/transactionsortoptionimpl.h b/kmymoney/widgets/transactionsortoption.h similarity index 82% rename from kmymoney/widgets/transactionsortoptionimpl.h rename to kmymoney/widgets/transactionsortoption.h index e3f76227a..824599945 100644 --- a/kmymoney/widgets/transactionsortoptionimpl.h +++ b/kmymoney/widgets/transactionsortoption.h @@ -1,71 +1,76 @@ /* This file is part of the KDE project Copyright (C) 2009 Laurent Montel + (C) 2017 by Łukasz Wojniłowicz 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 TransactionSortOptionDecl; -} + +namespace Ui { class TransactionSortOption; } +namespace eWidgets { enum class SortField; } class QListWidget; class QListWidgetItem; class TransactionSortOption : public QWidget { Q_OBJECT + Q_DISABLE_COPY(TransactionSortOption) + public: - TransactionSortOption(QWidget *parent); + explicit 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: - Ui::TransactionSortOptionDecl *ui; + static eWidgets::SortField textToSortOrder(const QString& text); + static QString sortOrderToText(eWidgets::SortField idx); + Ui::TransactionSortOption *ui; }; #endif /* TRANSACTIONSORTOPTIONIMPL_H */ diff --git a/kmymoney/widgets/transactionsortoptiondecl.ui b/kmymoney/widgets/transactionsortoption.ui similarity index 98% rename from kmymoney/widgets/transactionsortoptiondecl.ui rename to kmymoney/widgets/transactionsortoption.ui index b25aa053d..7422dad0e 100644 --- a/kmymoney/widgets/transactionsortoptiondecl.ui +++ b/kmymoney/widgets/transactionsortoption.ui @@ -1,209 +1,209 @@ Thomas Baumgart <ipwizard@users.sourceforge.net> - TransactionSortOptionDecl - + TransactionSortOption + 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/widgetenums.h b/kmymoney/widgets/widgetenums.h new file mode 100644 index 000000000..1fc9cce26 --- /dev/null +++ b/kmymoney/widgets/widgetenums.h @@ -0,0 +1,134 @@ +/*************************************************************************** + 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 + +#include + +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 + }; + + namespace eTransaction { + enum class Column { + Number = 0, + Date, + Account, + Security, + Detail, + ReconcileFlag, + Payment, + Deposit, + Quantity, + Price, + Value, + Balance, + // insert new values above this line + LastColumn + }; + } + + namespace eTransactionForm { + enum class Column { + Label1 = 0, + Value1, + Label2, + Value2, + // insert new values above this line + LastColumn + }; + } + + namespace eTabBar { + enum class SignalEmission { + Normal = 0, // standard signal behaviour + Never, // don't signal selection of a tab at all + Always // always signal selection of a tab + }; + } + + namespace eRegister { + enum class ItemState { + Any, + Imported, + Matched, + Erroneous, + NotMarked, + NotReconciled, + Cleared + }; + + enum class Action { + None = -1, + Check = 0, + /* these should be values which qt 3.3 never uses for QTab: + * qt starts upwards from 0 + */ + Deposit = 12201, + Transfer = 12202, + Withdrawal = 12203, + Atm, + // insert new values above this line + LastAction + }; + + enum class CashFlowDirection { + Deposit = 0, //< transaction is deposit + Payment, //< transaction is payment + Unknown //< transaction cashflow is unknown + }; + + enum class DetailColumn { + 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 + }; + } + + namespace ValidationFeedback { + enum class MessageType { + None, + Positive, + Information, + Warning, + Error + }; + } + + namespace Selector { + enum class Role { + Id = Qt::UserRole, /**< The id is stored in this role in column 0 as a string.*/ + Key = Qt::UserRole + 1, /**< The key is stored in this role in column 0 as a string.*/ + }; + } + +} + +#endif diff --git a/kmymoney/wizards/endingbalancedlg/checkingstatementinfowizardpagedecl.ui b/kmymoney/wizards/endingbalancedlg/checkingstatementinfowizardpagedecl.ui index 95d7ffaeb..fdc48132f 100644 --- a/kmymoney/wizards/endingbalancedlg/checkingstatementinfowizardpagedecl.ui +++ b/kmymoney/wizards/endingbalancedlg/checkingstatementinfowizardpagedecl.ui @@ -1,177 +1,177 @@ CheckingStatementInfoWizardPageDecl 6 34 577 388 Statement Information true Qt::Vertical QSizePolicy::Expanding 20 50 6 0 Qt::Horizontal QSizePolicy::Expanding 42 0 0 0 Statement date: false - + Starting balance of this statement: false - + false Ending balance of this statement: false - + Qt::Horizontal QSizePolicy::Expanding 46 0 Qt::Vertical QSizePolicy::Expanding 16 87 1 false diff --git a/kmymoney/wizards/endingbalancedlg/interestchargecheckingswizardpagedecl.ui b/kmymoney/wizards/endingbalancedlg/interestchargecheckingswizardpagedecl.ui index 403d6cba2..f93d6be78 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 -
kmymoneymvccombo.h
+
kmymoneypayeecombo.h
1
diff --git a/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.cpp b/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.cpp index 34ee3ed63..33c314e3d 100644 --- a/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.cpp +++ b/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.cpp @@ -1,389 +1,391 @@ /*************************************************************************** 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 f20fa6ac6..cfb39d841 100644 --- a/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.h +++ b/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.h @@ -1,112 +1,114 @@ /*************************************************************************** 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 1ab6ea801..ed7675402 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 + KMyMoneyDateInput QFrame
kmymoneydateinput.h
KMyMoneyCategory KComboBox
kmymoneycategory.h
- kMyMoneyEdit + KMyMoneyEdit QFrame
kmymoneyedit.h
KMyMoneyPayeeCombo KComboBox -
kmymoneymvccombo.h
+
kmymoneypayeecombo.h
1
- kMyMoneyAccountSelector + KMyMoneyAccountSelector QWidget
kmymoneyaccountselector.h
diff --git a/kmymoney/wizards/newaccountwizard/kaccounttypepagedecl.ui b/kmymoney/wizards/newaccountwizard/kaccounttypepagedecl.ui index 6d748cfaa..ee0ce6f41 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 + KMyMoneyDateInput QFrame
kmymoneydateinput.h
KMyMoneySecuritySelector KComboBox
kmymoneycurrencyselector.h
1
- kMyMoneyEdit + KMyMoneyEdit QFrame
kmymoneyedit.h
KMyMoneyGeneralCombo KComboBox -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.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 af6ea984e..7e47a7e8b 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 -
kmymoneymvccombo.h
+
kmymoneypayeecombo.h
1
KMyMoneyFrequencyCombo QWidget -
kmymoneymvccombo.h
+
kmymoneyfrequencycombo.h
1
diff --git a/kmymoney/wizards/newaccountwizard/kloandetailspagedecl.ui b/kmymoney/wizards/newaccountwizard/kloandetailspagedecl.ui index 221ee4da9..4413e2b03 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 -
kmymoneymvccombo.h
+
kmymoneyfrequencycombo.h
1
diff --git a/kmymoney/wizards/newaccountwizard/kloanpayoutpagedecl.ui b/kmymoney/wizards/newaccountwizard/kloanpayoutpagedecl.ui index 76b4edafe..989212b91 100644 --- a/kmymoney/wizards/newaccountwizard/kloanpayoutpagedecl.ui +++ b/kmymoney/wizards/newaccountwizard/kloanpayoutpagedecl.ui @@ -1,191 +1,191 @@ KLoanPayoutPageDecl 0 0 600 384 Qt::NoFocus If this loan is for an asset, such as a car or a house, you can create the asset account now. An asset account represents the total value of an asset. The money from this loan will be transferred into the asset account you create or select. If this loan is a 'consumer loan' (money to use however you want), you can use a checking account instead. true Do not create payout transaction QFrame::NoFrame QFrame::Raised 0 Refinance existing loan Check this if you are refinancing a loan that already exists in KMyMoney Asset Account false 1 0 1 0 Create Loan account false false 1 0 1 0 Date of payment false 40 20 QSizePolicy::Expanding Qt::Horizontal - + 20 20 QSizePolicy::Expanding Qt::Vertical m_noPayoutTransaction toggled(bool) m_payoutDetailFrame setDisabled(bool) m_refinanceLoan toggled(bool) m_assetAccount setDisabled(bool) m_refinanceLoan toggled(bool) m_loanAccount setEnabled(bool) m_refinanceLoan toggled(bool) m_createAssetButton setDisabled(bool) diff --git a/kmymoney/wizards/newaccountwizard/kloanschedulepagedecl.ui b/kmymoney/wizards/newaccountwizard/kloanschedulepagedecl.ui index 5081386d5..e1907d35d 100644 --- a/kmymoney/wizards/newaccountwizard/kloanschedulepagedecl.ui +++ b/kmymoney/wizards/newaccountwizard/kloanschedulepagedecl.ui @@ -1,141 +1,141 @@ KLoanSchedulePageDecl 0 0 545 370 Qt::NoFocus KMyMoney creates a schedule for this payment and reminds you whenever a payment must be made. Please select the account to/from which payments will be made and the category the interest will be assigned to.<p> If you selected to record all payments this date has already been supplied. If you selected to record only this years payments, then the <b>First payment due date</b> is the date of the first payment made in this year. true Interest category false The category the interest part of the payment will be assigned to. If you borrow money you usually have to pay interest, so this should be an expense category. If you lend the money, you receive the interest. In this case, select an income category here. Payment account false Select the account from which you make your periodical payments or where you receive regular payments in case you lend the money. In most cases, this is a checking account. First payment due on false 61 20 QSizePolicy::Expanding Qt::Horizontal - + 41 20 QSizePolicy::Preferred Qt::Horizontal 20 21 QSizePolicy::Expanding Qt::Vertical diff --git a/kmymoney/wizards/newaccountwizard/knewaccountwizard.cpp b/kmymoney/wizards/newaccountwizard/knewaccountwizard.cpp index 0c20bffa6..5a22b5c2a 100644 --- a/kmymoney/wizards/newaccountwizard/knewaccountwizard.cpp +++ b/kmymoney/wizards/newaccountwizard/knewaccountwizard.cpp @@ -1,1673 +1,1675 @@ /*************************************************************************** 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 +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/knewaccountwizard_p.h b/kmymoney/wizards/newaccountwizard/knewaccountwizard_p.h index e163b0a3d..aef223b75 100644 --- a/kmymoney/wizards/newaccountwizard/knewaccountwizard_p.h +++ b/kmymoney/wizards/newaccountwizard/knewaccountwizard_p.h @@ -1,454 +1,455 @@ /*************************************************************************** knewaccountwizard_p.h ------------------- begin : Tue Sep 25 2007 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. * * * ***************************************************************************/ #ifndef KNEWACCOUNTWIZARD_P_H #define KNEWACCOUNTWIZARD_P_H // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes +#include "kmymoneywizardpage.h" #include #include #include #include #include #include "accountsproxymodel.h" #include "ui_kinstitutionpagedecl.h" #include "ui_kaccounttypepagedecl.h" #include "ui_kbrokeragepagedecl.h" #include "ui_kschedulepagedecl.h" #include "ui_kgeneralloaninfopagedecl.h" #include "ui_kloandetailspagedecl.h" #include "ui_kloanpaymentpagedecl.h" #include "ui_kloanschedulepagedecl.h" #include "ui_kloanpayoutpagedecl.h" #include "ui_khierarchypagedecl.h" #include "ui_kaccountsummarypagedecl.h" class Wizard; class MyMoneyInstitution; namespace NewAccountWizard { class KInstitutionPageDecl : public QWidget, public Ui::KInstitutionPageDecl { public: KInstitutionPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class InstitutionPage : public KInstitutionPageDecl, public WizardPage { Q_OBJECT public: InstitutionPage(Wizard* parent); ~InstitutionPage(); KMyMoneyWizardPage* nextPage(void) const; QWidget* initialFocusWidget(void) const { return m_institutionComboBox; } /** * Returns the information about an institution if entered by * the user. If the id field is empty, then he did not enter * such information. */ const MyMoneyInstitution& institution(void) const; void selectExistingInstitution(const QString& id); private slots: void slotLoadWidgets(void); void slotNewInstitution(void); void slotSelectInstitution(int index); private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; }; class KAccountTypePageDecl : public QWidget, public Ui::KAccountTypePageDecl { public: KAccountTypePageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class AccountTypePage : public KAccountTypePageDecl, public WizardPage { Q_OBJECT public: AccountTypePage(Wizard* parent); virtual bool isComplete(void) const; KMyMoneyWizardPage* nextPage(void) const; QWidget* initialFocusWidget(void) const { return m_accountName; } eMyMoney::Account accountType(void) const; const MyMoneyAccount& parentAccount(void); bool allowsParentAccount(void) const; const MyMoneySecurity& currency(void) const; void setAccount(const MyMoneyAccount& acc); private: void hideShowPages(eMyMoney::Account i) const; void priceWarning(bool); private slots: void slotUpdateType(int i); void slotUpdateCurrency(void); void slotUpdateConversionRate(const QString&); void slotGetOnlineQuote(void); void slotPriceWarning(void); private: bool m_showPriceWarning; }; class KBrokeragePageDecl : public QWidget, public Ui::KBrokeragePageDecl { public: KBrokeragePageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class BrokeragePage : public KBrokeragePageDecl, public WizardPage { Q_OBJECT public: BrokeragePage(Wizard* parent); KMyMoneyWizardPage* nextPage(void) const; void enterPage(void); QWidget* initialFocusWidget(void) const { return m_createBrokerageButton; } private slots: void slotLoadWidgets(void); }; class KSchedulePageDecl : public QWidget, public Ui::KSchedulePageDecl { public: KSchedulePageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class CreditCardSchedulePage : public KSchedulePageDecl, public WizardPage { Q_OBJECT public: CreditCardSchedulePage(Wizard* parent); KMyMoneyWizardPage* nextPage(void) const; virtual bool isComplete(void) const; void enterPage(void); QWidget* initialFocusWidget(void) const { return m_reminderCheckBox; } private slots: void slotLoadWidgets(void); }; class KGeneralLoanInfoPageDecl : public QWidget, public Ui::KGeneralLoanInfoPageDecl { public: KGeneralLoanInfoPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class GeneralLoanInfoPage : public KGeneralLoanInfoPageDecl, public WizardPage { Q_OBJECT public: GeneralLoanInfoPage(Wizard* parent); KMyMoneyWizardPage* nextPage(void) const; virtual bool isComplete(void) const; void enterPage(void); const MyMoneyAccount& parentAccount(void); QWidget* initialFocusWidget(void) const { return m_loanDirection; } /** * Returns @p true if the user decided to record all payments, @p false otherwise. */ bool recordAllPayments(void) const; private slots: void slotLoadWidgets(void); private: bool m_firstTime; }; class KLoanDetailsPageDecl : public QWidget, public Ui::KLoanDetailsPageDecl { public: KLoanDetailsPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class LoanDetailsPage : public KLoanDetailsPageDecl, public WizardPage { Q_OBJECT public: LoanDetailsPage(Wizard* parent); void enterPage(void); KMyMoneyWizardPage* nextPage(void) const; virtual bool isComplete(void) const; QWidget* initialFocusWidget(void) const { return m_paymentDue; } /** * This method returns the number of payments depending on * the settings of m_termAmount and m_termUnit widgets */ int term(void) const; private: /** * This method is used to update the term widgets * according to the length of the given @a term. * The term is also converted into a string and returned. */ QString updateTermWidgets(const double term); private: bool m_needCalculate; private slots: void slotValuesChanged(void); void slotCalculate(void); }; class KLoanPaymentPageDecl : public QWidget, public Ui::KLoanPaymentPageDecl { public: KLoanPaymentPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class LoanPaymentPage : public KLoanPaymentPageDecl, public WizardPage { Q_OBJECT public: LoanPaymentPage(Wizard* parent); ~LoanPaymentPage(); KMyMoneyWizardPage* nextPage(void) const; void enterPage(void); /** * This method returns the sum of the additional fees */ MyMoneyMoney additionalFees(void) const; /** * This method returns the base payment, that's principal and interest */ MyMoneyMoney basePayment(void) const; /** * This method returns the splits that make up the additional fees in @p list. * @note The splits may contain assigned ids which the caller must remove before * adding the splits to a MyMoneyTransaction object. */ void additionalFeesSplits(QList& list); protected slots: void slotAdditionalFees(void); protected: void updateAmounts(void); private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; }; class KLoanSchedulePageDecl : public QWidget, public Ui::KLoanSchedulePageDecl { public: KLoanSchedulePageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class LoanSchedulePage : public KLoanSchedulePageDecl, public WizardPage { Q_OBJECT public: LoanSchedulePage(Wizard* parent); void enterPage(void); KMyMoneyWizardPage* nextPage(void) const; /** * This method returns the due date of the first payment to be recorded. */ QDate firstPaymentDueDate(void) const; QWidget* initialFocusWidget(void) const { return m_interestCategory; } private slots: void slotLoadWidgets(void); void slotCreateCategory(const QString& name, QString& id); }; class KLoanPayoutPageDecl : public QWidget, public Ui::KLoanPayoutPageDecl { public: KLoanPayoutPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class LoanPayoutPage : public KLoanPayoutPageDecl, public WizardPage { Q_OBJECT public: LoanPayoutPage(Wizard* parent); void enterPage(void); virtual bool isComplete(void) const; KMyMoneyWizardPage* nextPage(void) const; QWidget* initialFocusWidget(void) const { return m_noPayoutTransaction; } - const QString& payoutAccountId(void) const; + QString payoutAccountId(void) const; private slots: void slotLoadWidgets(void); void slotCreateAssetAccount(void); void slotButtonsToggled(void); }; class HierarchyFilterProxyModel : public AccountsProxyModel { Q_OBJECT public: HierarchyFilterProxyModel(QObject *parent = 0); protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const; }; class KHierarchyPageDecl : public QWidget, public Ui::KHierarchyPageDecl { public: KHierarchyPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class HierarchyPage : public KHierarchyPageDecl, public WizardPage { Q_OBJECT public: HierarchyPage(Wizard* parent); void enterPage(void); KMyMoneyWizardPage* nextPage(void) const; QWidget* initialFocusWidget(void) const { return m_parentAccounts; } const MyMoneyAccount& parentAccount(void); bool isComplete(void) const; protected slots: void parentAccountChanged(); private: HierarchyFilterProxyModel *m_filterProxyModel; MyMoneyAccount m_parentAccount; }; class KAccountSummaryPageDecl : public QWidget, public Ui::KAccountSummaryPageDecl { public: KAccountSummaryPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class AccountSummaryPage : public KAccountSummaryPageDecl, public WizardPage { Q_OBJECT public: AccountSummaryPage(Wizard* parent); void enterPage(void); QWidget* initialFocusWidget(void) const { return m_dataList; } }; } // namespace #endif diff --git a/kmymoney/wizards/newaccountwizard/kschedulepagedecl.ui b/kmymoney/wizards/newaccountwizard/kschedulepagedecl.ui index 3c34d4023..f452af628 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 -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.h
1
KMyMoneyPayeeCombo QWidget -
kmymoneymvccombo.h
+
kmymoneypayeecombo.h
1
KLineEdit QLineEdit
klineedit.h
diff --git a/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.cpp b/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.cpp index bb61c533e..e29cf2f74 100644 --- a/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.cpp +++ b/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.cpp @@ -1,151 +1,151 @@ /*************************************************************************** kinvestmentdetailswizardpage - description ------------------- begin : Sun Jun 27 2010 copyright : (C) 2010 by Fernando Vilas email : kmymoney-devel@kde.org (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 "kinvestmentdetailswizardpage.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kinvestmentdetailswizardpage.h" #include "mymoneymoney.h" #include "mymoneyfile.h" #include "mymoneysecurity.h" KInvestmentDetailsWizardPage::KInvestmentDetailsWizardPage(QWidget *parent) : QWizardPage(parent), ui(new Ui::KInvestmentDetailsWizardPage) { ui->setupUi(this); ui->m_fraction->setPrecision(0); ui->m_fraction->setValue(MyMoneyMoney(100, 1)); - kMyMoneyMoneyValidator* fractionValidator = new kMyMoneyMoneyValidator(1, 100000, 0, this); + KMyMoneyMoneyValidator* fractionValidator = new KMyMoneyMoneyValidator(1, 100000, 0, this); ui->m_fraction->setValidator(fractionValidator); // load the price mode combo ui->m_priceMode->insertItem(i18nc("default price mode", "(default)"), 0); ui->m_priceMode->insertItem(i18n("Price per share"), 1); ui->m_priceMode->insertItem(i18n("Total for all shares"), 2); // load the widget with the available currencies ui->m_tradingCurrencyEdit->update(QString()); // Register the fields with the QWizard and connect the // appropriate signals to update the "Next" button correctly registerField("investmentName", ui->m_investmentName); connect(ui->m_investmentName, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); registerField("investmentIdentification", ui->m_investmentIdentification); connect(ui->m_investmentIdentification, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); registerField("investmentSymbol", ui->m_investmentSymbol); connect(ui->m_investmentSymbol, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); registerField("tradingCurrencyEdit", ui->m_tradingCurrencyEdit, "security"); registerField("tradingMarket", ui->m_tradingMarket, "currentText", SIGNAL(currentIndexChanged(QString))); ui->m_roundingMethod->addItem(i18nc("Rounding method", "Round"), AlkValue::RoundRound); ui->m_roundingMethod->addItem(i18nc("Rounding method", "Ceil"), AlkValue::RoundCeil); ui->m_roundingMethod->addItem(i18nc("Rounding method", "Floor"), AlkValue::RoundFloor); ui->m_roundingMethod->addItem(i18nc("Rounding method", "Truncate"), AlkValue::RoundTruncate); registerField("roundingMethod", ui->m_roundingMethod, "currentData", SIGNAL(currentIndexChanged(int))); registerField("fraction", ui->m_fraction, "value", SIGNAL(textChanged())); - connect(ui->m_fraction, &kMyMoneyEdit::textChanged, + connect(ui->m_fraction, &KMyMoneyEdit::textChanged, this, &QWizardPage::completeChanged); registerField("pricePrecision", ui->m_pricePrecision, "value", SIGNAL(valueChanged())); } KInvestmentDetailsWizardPage::~KInvestmentDetailsWizardPage() { delete ui; } /** * Set the values based on the @param security */ void KInvestmentDetailsWizardPage::init2(const MyMoneySecurity& security) { MyMoneySecurity tradingCurrency = MyMoneyFile::instance()->currency(security.tradingCurrency()); ui->m_investmentSymbol->setText(security.tradingSymbol()); ui->m_tradingMarket->setCurrentIndex(ui->m_tradingMarket->findText(security.tradingMarket(), Qt::MatchExactly)); if (security.roundingMethod() == AlkValue::RoundNever) ui->m_roundingMethod->setCurrentIndex(0); else ui->m_roundingMethod->setCurrentIndex(ui->m_roundingMethod->findData(security.roundingMethod())); ui->m_fraction->setValue(MyMoneyMoney(security.smallestAccountFraction(), 1)); ui->m_pricePrecision->setValue(security.pricePrecision()); ui->m_tradingCurrencyEdit->setSecurity(tradingCurrency); ui->m_investmentIdentification->setText(security.value("kmm-security-id")); } /** * Update the "Next" button */ bool KInvestmentDetailsWizardPage::isComplete() const { return (!ui->m_investmentName->text().isEmpty() && !ui->m_investmentSymbol->text().isEmpty() && !ui->m_fraction->value().isZero()); } int KInvestmentDetailsWizardPage::priceMode() const { return ui->m_priceMode->currentItem(); } void KInvestmentDetailsWizardPage::setCurrentPriceMode(int mode) { ui->m_priceMode->setCurrentItem(mode); } void KInvestmentDetailsWizardPage::loadName(const QString& name) { ui->m_investmentName->loadText(name); } void KInvestmentDetailsWizardPage::setName(const QString& name) { ui->m_investmentName->setText(name); } void KInvestmentDetailsWizardPage::setPriceModeEnabled(bool enabled) { ui->m_priceMode->setEnabled(enabled); } void KInvestmentDetailsWizardPage::setupInvestmentSymbol() { ui->m_investmentSymbol->setFocus(); connect(ui->m_investmentSymbol, SIGNAL(lineChanged(QString)), this, SIGNAL(checkForExistingSymbol(QString))); } diff --git a/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.h b/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.h index 2f52e2051..94825b7e9 100644 --- a/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.h +++ b/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.h @@ -1,78 +1,78 @@ /*************************************************************************** kinvestmentdetailswizardpage - description ------------------- begin : Sun Jun 27 2010 copyright : (C) 2010 by Fernando Vilas email : kmymoney-devel@kde.org (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 KINVESTMENTDETAILSWIZARDPAGE_H #define KINVESTMENTDETAILSWIZARDPAGE_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes class MyMoneySecurity; namespace Ui { class KInvestmentDetailsWizardPage; } /** * This class implements the investment details page of the * @ref KNewInvestmentWizard. */ class KInvestmentDetailsWizardPage : public QWizardPage { Q_OBJECT public: explicit KInvestmentDetailsWizardPage(QWidget *parent = nullptr); ~KInvestmentDetailsWizardPage(); void init2(const MyMoneySecurity& security); /** * Overload isComplete to handle the required fields */ bool isComplete() const; /** * Functions to control or read the m_priceMode widget */ int priceMode() const; void setCurrentPriceMode(int mode); void setPriceModeEnabled(bool enabled); /** * load or set the name of the m_investmentName item widget. The difference - * can be seen in the @ref kMyMoneyLineEdit type. + * can be seen in the @ref KMyMoneyLineEdit type. */ void loadName(const QString& name); void setName(const QString& name); void setupInvestmentSymbol(); signals: void checkForExistingSymbol(const QString& symbol); private: Ui::KInvestmentDetailsWizardPage *ui; }; #endif diff --git a/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.ui b/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.ui index 69fce367c..b1c8bb56b 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 + KMyMoneyEdit QWidget
kmymoneyedit.h
- kMyMoneyLineEdit + KMyMoneyLineEdit QWidget
../widgets/kmymoneylineedit.h
KMyMoneyGeneralCombo QWidget -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.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/newinvestmentwizard/konlineupdatewizardpage.cpp b/kmymoney/wizards/newinvestmentwizard/konlineupdatewizardpage.cpp index 92165bb98..e32f89404 100644 --- a/kmymoney/wizards/newinvestmentwizard/konlineupdatewizardpage.cpp +++ b/kmymoney/wizards/newinvestmentwizard/konlineupdatewizardpage.cpp @@ -1,129 +1,129 @@ /*************************************************************************** konlineupdatewizardpage - description ------------------- begin : Sun Jun 27 2010 copyright : (C) 2010 by Fernando Vilas email : kmymoney-devel@kde.org (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 "konlineupdatewizardpage.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "ui_konlineupdatewizardpage.h" #include "mymoneymoney.h" #include "mymoneysecurity.h" #include "webpricequote.h" KOnlineUpdateWizardPage::KOnlineUpdateWizardPage(QWidget *parent) : QWizardPage(parent), ui(new Ui::KOnlineUpdateWizardPage) { ui->setupUi(this); ui->m_onlineFactor->setValue(MyMoneyMoney::ONE); ui->m_onlineFactor->setPrecision(4); // make ui->m_onlineSourceCombo sortable QSortFilterProxyModel* proxy = new QSortFilterProxyModel(ui->m_onlineSourceCombo); proxy->setSourceModel(ui->m_onlineSourceCombo->model()); proxy->setSortCaseSensitivity(Qt::CaseInsensitive); ui->m_onlineSourceCombo->model()->setParent(proxy); ui->m_onlineSourceCombo->setModel(proxy); // Connect signals-slots connect(ui->m_useFinanceQuote, &QAbstractButton::toggled, this, &KOnlineUpdateWizardPage::slotSourceChanged); // Register the fields with the QWizard and connect the // appropriate signals to update the "Next" button correctly registerField("onlineFactor", ui->m_onlineFactor, "value"); registerField("onlineSourceCombo", ui->m_onlineSourceCombo, "currentText", SIGNAL(currentIndexChanged(QString))); registerField("useFinanceQuote", ui->m_useFinanceQuote); connect(ui->m_onlineSourceCombo, static_cast(&QComboBox::currentIndexChanged), this, &KOnlineUpdateWizardPage::slotCheckPage); - connect(ui->m_onlineFactor, &kMyMoneyEdit::textChanged, + connect(ui->m_onlineFactor, &KMyMoneyEdit::textChanged, this, &QWizardPage::completeChanged); connect(ui->m_onlineSourceCombo, static_cast(&QComboBox::activated), this, &QWizardPage::completeChanged); connect(ui->m_useFinanceQuote, &QAbstractButton::toggled, this, &QWizardPage::completeChanged); } KOnlineUpdateWizardPage::~KOnlineUpdateWizardPage() { delete ui; } /** * Set the values based on the @param security */ void KOnlineUpdateWizardPage::init2(const MyMoneySecurity& security) { int idx; if (security.value("kmm-online-quote-system") == "Finance::Quote") { FinanceQuoteProcess p; ui->m_useFinanceQuote->setChecked(true); idx = ui->m_onlineSourceCombo->findText(p.niceName(security.value("kmm-online-source"))); } else { idx = ui->m_onlineSourceCombo->findText(security.value("kmm-online-source")); } // in case we did not find the entry, we use the empty one if (idx == -1) idx = ui->m_onlineSourceCombo->findText(QString()); ui->m_onlineSourceCombo->setCurrentIndex(idx); if (!security.value("kmm-online-factor").isEmpty()) ui->m_onlineFactor->setValue(MyMoneyMoney(security.value("kmm-online-factor"))); } /** * Update the "Next" button */ bool KOnlineUpdateWizardPage::isComplete() const { return !(ui->m_onlineFactor->isEnabled() && ui->m_onlineFactor->value().isZero()); } bool KOnlineUpdateWizardPage::isOnlineFactorEnabled() const { return ui->m_onlineFactor->isEnabled(); } void KOnlineUpdateWizardPage::slotCheckPage(const QString& txt) { ui->m_onlineFactor->setEnabled(!txt.isEmpty()); } void KOnlineUpdateWizardPage::slotSourceChanged(bool useFQ) { ui->m_onlineSourceCombo->clear(); ui->m_onlineSourceCombo->insertItem(0, QString()); if (useFQ) { ui->m_onlineSourceCombo->addItems(WebPriceQuote::quoteSources(WebPriceQuote::FinanceQuote)); } else { ui->m_onlineSourceCombo->addItems(WebPriceQuote::quoteSources()); } ui->m_onlineSourceCombo->model()->sort(0); } diff --git a/kmymoney/wizards/newinvestmentwizard/konlineupdatewizardpage.ui b/kmymoney/wizards/newinvestmentwizard/konlineupdatewizardpage.ui index 4c9c64ffb..20b1a3209 100644 --- a/kmymoney/wizards/newinvestmentwizard/konlineupdatewizardpage.ui +++ b/kmymoney/wizards/newinvestmentwizard/konlineupdatewizardpage.ui @@ -1,151 +1,151 @@ KOnlineUpdateWizardPage Online Update Select an online source and click <b>Finish</b> to store the investment data. If you do not want to use online updates, just leave the data as is. Qt::AlignTop true 0 0 Use Finance::Quote false Online Source false 180 0 Factor false - + 371 20 QSizePolicy::Expanding Qt::Horizontal 20 181 QSizePolicy::Expanding Qt::Vertical KComboBox QComboBox
kcombobox.h
m_useFinanceQuote m_onlineSourceCombo m_onlineFactor
diff --git a/kmymoney/wizards/newloanwizard/assetaccountwizardpage.ui b/kmymoney/wizards/newloanwizard/assetaccountwizardpage.ui index 4411f5236..024257b01 100644 --- a/kmymoney/wizards/newloanwizard/assetaccountwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/assetaccountwizardpage.ui @@ -1,127 +1,127 @@ AssetAccountWizardPage Asset Account If this loan is for an asset, such as a car or a house, you can create the asset account now. An asset account represents the total value of an asset. The money from this loan will be transferred into the asset account you create or select. If this loan is a 'consumer loan' (money to use however you want), you can use a checking account instead. Qt::AlignTop true 20 21 QSizePolicy::Expanding Qt::Vertical Do not create payout transaction - + 466 20 QSizePolicy::Expanding Qt::Horizontal Press this to create a new asset account Date of payment false - + 20 16 QSizePolicy::Expanding Qt::Vertical diff --git a/kmymoney/wizards/newloanwizard/durationwizardpage.ui b/kmymoney/wizards/newloanwizard/durationwizardpage.ui index a2c41f7c7..d4b110d97 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 -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.h
1
diff --git a/kmymoney/wizards/newloanwizard/effectivedatewizardpage.ui b/kmymoney/wizards/newloanwizard/effectivedatewizardpage.ui index 1a90c6bdd..7e67c6c9b 100644 --- a/kmymoney/wizards/newloanwizard/effectivedatewizardpage.ui +++ b/kmymoney/wizards/newloanwizard/effectivedatewizardpage.ui @@ -1,104 +1,104 @@ EffectiveDateWizardPage Effective date 11 6 1 Qt::AlignTop true 1 Qt::AlignTop true 0 30 QSizePolicy::Expanding Qt::Vertical 0 6 When should the changes become active? false - + 0 87 QSizePolicy::Expanding Qt::Vertical diff --git a/kmymoney/wizards/newloanwizard/finalpaymentwizardpage.ui b/kmymoney/wizards/newloanwizard/finalpaymentwizardpage.ui index e06e232e8..b75247989 100644 --- a/kmymoney/wizards/newloanwizard/finalpaymentwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/finalpaymentwizardpage.ui @@ -1,249 +1,249 @@ FinalPaymentWizardPage Final amortization payment 11 6 Please enter the amount of a final amortization payment or leave the field empty to calculate it. Qt::AlignTop true 0 16 QSizePolicy::Expanding Qt::Vertical 0 6 Final amortization payment false - + 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 diff --git a/kmymoney/wizards/newloanwizard/firstpaymentwizardpage.ui b/kmymoney/wizards/newloanwizard/firstpaymentwizardpage.ui index 12e9164f8..303445682 100644 --- a/kmymoney/wizards/newloanwizard/firstpaymentwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/firstpaymentwizardpage.ui @@ -1,104 +1,104 @@ FirstPaymentWizardPage Date of first payment 11 6 1 Qt::AlignTop true 0 78 QSizePolicy::Expanding Qt::Vertical 0 6 First payment due on false - + 0 28 QSizePolicy::Expanding Qt::Vertical Qt::AlignBottom true diff --git a/kmymoney/wizards/newloanwizard/interestcategorywizardpage.ui b/kmymoney/wizards/newloanwizard/interestcategorywizardpage.ui index 6981c931e..1c6d611ea 100644 --- a/kmymoney/wizards/newloanwizard/interestcategorywizardpage.ui +++ b/kmymoney/wizards/newloanwizard/interestcategorywizardpage.ui @@ -1,105 +1,105 @@ InterestCategoryWizardPage Interest category 11 6 Please select the category you want to assign the interest payments to or create a new category. Qt::AlignTop true 0 38 QSizePolicy::Expanding Qt::Vertical - + 0 6 264 0 QSizePolicy::Expanding Qt::Horizontal 0 22 QSizePolicy::Expanding Qt::Vertical diff --git a/kmymoney/wizards/newloanwizard/interesteditwizardpage.ui b/kmymoney/wizards/newloanwizard/interesteditwizardpage.ui index d08118c34..468c53f65 100644 --- a/kmymoney/wizards/newloanwizard/interesteditwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/interesteditwizardpage.ui @@ -1,169 +1,169 @@ InterestEditWizardPage Interest Rate 11 6 Please enter the interest rate or leave the field empty to calculate it. Qt::AlignTop true 0 29 QSizePolicy::Expanding Qt::Vertical 0 6 101 0 QSizePolicy::Expanding Qt::Horizontal 0 6 - + 1 false Current rate false Interest rate false 101 0 QSizePolicy::Expanding Qt::Horizontal 0 29 QSizePolicy::Expanding Qt::Vertical KMyMoney either calculates the new interest rate or the amount for principal and interest. If you know the interest rate then enter it here. If KMyMoney should calculate this value for you, then leave the field blank. Qt::AlignTop true diff --git a/kmymoney/wizards/newloanwizard/interestwizardpage.ui b/kmymoney/wizards/newloanwizard/interestwizardpage.ui index 5987a4fd3..970f330ad 100644 --- a/kmymoney/wizards/newloanwizard/interestwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/interestwizardpage.ui @@ -1,249 +1,249 @@ InterestWizardPage Interest 11 6 Please enter the interest rate or leave the field empty to calculate it. Qt::AlignTop true 0 16 QSizePolicy::Expanding Qt::Vertical 0 6 Interest rate false - + 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 diff --git a/kmymoney/wizards/newloanwizard/keditloanwizard.cpp b/kmymoney/wizards/newloanwizard/keditloanwizard.cpp index 2305dcf2d..a4a6df3b7 100644 --- a/kmymoney/wizards/newloanwizard/keditloanwizard.cpp +++ b/kmymoney/wizards/newloanwizard/keditloanwizard.cpp @@ -1,544 +1,545 @@ /*************************************************************************** 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 61bfd5d69..855b7cf28 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 + KMyMoneyLineEdit QWidget
../widgets/kmymoneylineedit.h
90 25 0 5 5 image0
- kMyMoneyAccountSelector + KMyMoneyAccountSelector QWidget
../widgets/kmymoneyaccountselector.h
80 80 0 5 5 image0
KComboBox QComboBox
kcombobox.h
KMyMoneyGeneralCombo QWidget -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.h
1
89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000041d49444154388d8d95c18b1c4514c67fbb29e5b54ea05b26da1d36b08d09a6c508138cb8c13d647141163de49043bca9f1628eb9294810f24f88c15b728b07617358888785d980427290b4a8d00b19a8821da8228cf42316ac87eed9247ac95c9aeaeef9eaabdffb5ebf85f178ccfcf7f5b79fec07a7280a08a008f25ceb7c29e5da573716e65a0be3f198ef6f5ededff9b9e1b34faf90882092a1ea21428b9220ffbf46054055b19386ba6970ae6665b5e0d2c5eb0b066067bbe1f21757c88f5504eff0c192189081303a5582b16854fccce2fe041f00cdd0a8103b874556405476b62d972ec2c2b90ff3fd8df5cb946549d80b34aea15ccaa94625450e2d0e558b460b7371a734b5e027251a155545678ab62d7eea90b4c504078908c107acb354cb251b1f8f08b1c18631aabbb4d18211404804f22545863561d2f060fb34aa8a18d0086204e72c46a32292e1a796f278c1da7a859d8da9a737506db04d0a24103d8949480b214d3bbee952a05c1d536f9d2684160c744f048311543d459eb1b65ee1754cedae13a64abd5d6127053c9d0281f22d4f75c6a2118a5c09272d7ab740638bf4ef2dd2174032d0d8d04c6fe19c32bedd8bc65e342aa3f785e2784bfd6bc1bd9db24b05505616a4edd67d5a8c1801a03a9962754c504bf34b8986acfb9be91cac7d54315a15541b36b134f74bd2dc53940115a5381e087bc25c6f71be831a8baa234cc1ee3eeb74ed83116beb2b8849d108a3730e8ce27e2bfb82419a7b407ac7c222fd0e6280a8078ce64e47ef54ac9d5be9aaae0162e0de568a44a18d3df90845d919c4f01463406340a44f40efb43a99b371618488e2670d1a2d7ee6f1d3be39342104e937ed85fb86599c33e9ee0be9a004946259d8b858909880d59aa00daa0d3280b5f39e34074c4b3a5434029a76a9e9f5cc0163b5c80024158a1cfc4cb1d3061d5834066cb0a403c008599a70f6bc27b8d03106ea3ae91987278c5b143b015547224ab5e24185cd9b81665273e7b6e7cecd82ddbf8098002de94049737bc09828cf3036f31c3735c8b0e98eb704672f348c7f7c93cd1fb2ae905119ff54e0dff3542b0e9dd983dcea2cc5eea6cf303e74ec687af5c41b6fa38f5e203c7ec8e1238ef85831a214afef21060ea781ec68247b65c6916353cc8b73d1ae79354606af2a930709aa118da1636c274dd7e3774b884a79aa6b5711a538d33cf9c01c1c9b83bc6a1f55c91ccb23b05b1deb43a7decdaffefd2862f61366aa8449c69e33642f012f2bb208fa98eeda1f3346f076c8eeef19475e9be115423364f78f84105a8643d34d90cfbfdcd8cf872589a45dc7f54cf3134a36f4e495c23f9da8db15bccb708d4014f2ca512cb5d4f7c13e84300d5cff6eb39b202bab053bdb9674f0648609427d0f8402ddeac30f4fcd3ced3efc3b422d406c09b340bedc1571e1bfc3d44dc281634c5fe5e758e7395cfbe6d6c130fd17d92372d277c159b70000000049454e44ae426082
diff --git a/kmymoney/wizards/newloanwizard/loanamountwizardpage.ui b/kmymoney/wizards/newloanwizard/loanamountwizardpage.ui index 5730439aa..1d3ed3ac4 100644 --- a/kmymoney/wizards/newloanwizard/loanamountwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/loanamountwizardpage.ui @@ -1,248 +1,248 @@ LoanAmountWizardPage Loan amount 11 6 1 Qt::AlignTop true 0 15 QSizePolicy::Expanding Qt::Vertical 0 6 Loan amount false - + 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 diff --git a/kmymoney/wizards/newloanwizard/namewizardpage.ui b/kmymoney/wizards/newloanwizard/namewizardpage.ui index 1b7598bca..c6e54b87c 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 -
kmymoneymvccombo.h
+
kmymoneypayeecombo.h
1
diff --git a/kmymoney/wizards/newloanwizard/paymenteditwizardpage.ui b/kmymoney/wizards/newloanwizard/paymenteditwizardpage.ui index 29ac9dfb8..beadaa252 100644 --- a/kmymoney/wizards/newloanwizard/paymenteditwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/paymenteditwizardpage.ui @@ -1,169 +1,169 @@ PaymentEditWizardPage Payment 11 6 Please enter the amount you pay for principal and interest or leave the field empty to calculate it. Qt::AlignTop true 0 21 QSizePolicy::Expanding Qt::Vertical 0 6 100 0 QSizePolicy::Expanding Qt::Horizontal 0 6 - + Current amount false 1 false Principal and interest false 100 0 QSizePolicy::Expanding Qt::Horizontal 0 19 QSizePolicy::Expanding Qt::Vertical KMyMoney either calculates the new interest rate or the amount for principal and interest. If you know the amount for principal and interest then enter it here. If KMyMoney should calculate this value for you, then leave the field blank. Qt::AlignVCenter true diff --git a/kmymoney/wizards/newloanwizard/paymentfrequencywizardpage.ui b/kmymoney/wizards/newloanwizard/paymentfrequencywizardpage.ui index 93ad9744a..cdcdd073b 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 -
kmymoneymvccombo.h
+
kmymoneyfrequencycombo.h
KComboBox QComboBox
kcombobox.h
diff --git a/kmymoney/wizards/newloanwizard/paymentwizardpage.ui b/kmymoney/wizards/newloanwizard/paymentwizardpage.ui index 77b04b9e1..32354e83c 100644 --- a/kmymoney/wizards/newloanwizard/paymentwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/paymentwizardpage.ui @@ -1,249 +1,249 @@ PaymentWizardPage Payment 11 6 Please enter the amount you pay for principal and interest or leave the field empty to calculate it. Qt::AlignTop true 0 16 QSizePolicy::Expanding Qt::Vertical 0 6 Principal and interest false - + 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 diff --git a/kmymoney/wizards/newloanwizard/schedulewizardpage.ui b/kmymoney/wizards/newloanwizard/schedulewizardpage.ui index 6b4f49f54..e814e6221 100644 --- a/kmymoney/wizards/newloanwizard/schedulewizardpage.ui +++ b/kmymoney/wizards/newloanwizard/schedulewizardpage.ui @@ -1,108 +1,108 @@ ScheduleWizardPage Schedule 11 6 KMyMoney will create a schedule for this payment and reminds you whenever a payment must be made.<p> If you selected to record all payments this date has already been supplied. If you selected to record only this years payments, then the <b>First payment due date</b> is the date of the first payment made in this year. Qt::AlignTop true 0 23 QSizePolicy::Expanding Qt::Vertical 0 6 - + - + First payment due on: false Make payment from/to: Qt::AlignTop false 0 24 QSizePolicy::Expanding Qt::Vertical diff --git a/kmymoney/wizards/newloanwizard/variableinterestdatewizardpage.ui b/kmymoney/wizards/newloanwizard/variableinterestdatewizardpage.ui index 5e7bae226..d5761af5f 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 -
kmymoneymvccombo.h
+
kmymoneygeneralcombo.h
1
diff --git a/kmymoney/wizards/newuserwizard/kaccountpagedecl.ui b/kmymoney/wizards/newuserwizard/kaccountpagedecl.ui index c444bde83..470b87142 100644 --- a/kmymoney/wizards/newuserwizard/kaccountpagedecl.ui +++ b/kmymoney/wizards/newuserwizard/kaccountpagedecl.ui @@ -1,256 +1,256 @@ KAccountPageDecl 0 0 600 481 Qt::NoFocus A typical KMyMoney user maintains a checking account with some institution and uses it to receive money or pay bills. If this is not the case, please deselect the checkbox below and continue on the next page. true I have a checking account that I want to manage with KMyMoney QFrame::NoFrame QFrame::Plain 0 The name of the institution that issued the account. Each institution has a routing number. Enter it here. If uncertain, leave the field blank. You can modify it later. Name of the institution false Routing number false In general the institution that issued the account also issued a number for it. Enter it here. If uncertain, you can modify this entry later. Enter a descriptive name that will be used by KMyMoney to identify your checking account. Opening date false - + Enter the date from when on you plan to keep track of the transactions in that account. This is usually the date of the last statement. If uncertain, leave as is. 81 20 QSizePolicy::Expanding Qt::Horizontal Number of the account false Name of the account false Opening balance false xxx false - + false Enter the opening balance of the account. This is usually the ending balance of the last statement. If uncertain, leave as is. 200 20 QSizePolicy::Expanding Qt::Horizontal Qt::NoFocus The fields below allow you to enter some information about your checking account. Once this wizard finishes, the account will be created for you within KMyMoney. true 20 16 QSizePolicy::Expanding Qt::Vertical m_accountNameEdit m_accountNumberEdit m_openingDateEdit m_openingBalanceEdit m_institutionNameEdit m_institutionNumberEdit m_haveCheckingAccountButton m_haveCheckingAccountButton toggled(bool) m_accountDataFrame setEnabled(bool) diff --git a/kmymoney/wizards/newuserwizard/knewuserwizard_p.h b/kmymoney/wizards/newuserwizard/knewuserwizard_p.h index eb4ecfdcb..2766cd493 100644 --- a/kmymoney/wizards/newuserwizard/knewuserwizard_p.h +++ b/kmymoney/wizards/newuserwizard/knewuserwizard_p.h @@ -1,179 +1,180 @@ /*************************************************************************** knewuserwizard_p.h ------------------- begin : Sat Feb 18 2006 copyright : (C) 2006 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 KNEWUSERWIZARD_P_H #define KNEWUSERWIZARD_P_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // Project Includes #include +#include "kmymoneywizardpage.h" #include "ui_kintropagedecl.h" #include "ui_kaccountpagedecl.h" #include "ui_kpreferencepagedecl.h" #include "ui_kfilepagedecl.h" #include "userinfo.h" #include "currency.h" #include "accounts.h" #include "mymoneytemplate.h" #include "mymoneycontact.h" class Wizard; class KJob; namespace NewUserWizard { class KIntroPageDecl : public QWidget, public Ui::KIntroPageDecl { public: KIntroPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class IntroPage : public KIntroPageDecl, public WizardPage { Q_OBJECT public: IntroPage(Wizard* parent); KMyMoneyWizardPage* nextPage(void) const; }; /** * Wizard page collecting information about the user * * @author Thomas Baumgart */ class GeneralPage : public UserInfo, public WizardPage { Q_OBJECT public: GeneralPage(Wizard* parent); KMyMoneyWizardPage* nextPage(void) const; protected slots: void slotLoadFromAddressBook(void); void slotContactFetched(const ContactData &identity); private: MyMoneyContact *m_contact; }; /** * Wizard page collecting information about the base currency * * @author Thomas Baumgart */ class CurrencyPage : public Currency, public WizardPage { Q_OBJECT public: CurrencyPage(Wizard* parent); void enterPage(void); KMyMoneyWizardPage* nextPage(void) const; }; /** * Wizard page collecting information about the checking account */ class KAccountPageDecl : public QWidget, public Ui::KAccountPageDecl { public: KAccountPageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class AccountPage : public KAccountPageDecl, public WizardPage { Q_OBJECT public: AccountPage(Wizard* parent); KMyMoneyWizardPage* nextPage(void) const; virtual bool isComplete(void) const; }; /** * Wizard page collecting information about the account templates. * * @author Thomas Baumgart */ class CategoriesPage : public Accounts, public WizardPage { Q_OBJECT public: CategoriesPage(Wizard* parent); KMyMoneyWizardPage* nextPage(void) const; QList selectedTemplates(void) const; }; /** * Wizard page to allow changing the preferences during setup * * @author Thomas Baumgart */ class KPreferencePageDecl : public QWidget, public Ui::KPreferencePageDecl { public: KPreferencePageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class PreferencePage : public KPreferencePageDecl, public WizardPage { Q_OBJECT public: PreferencePage(Wizard* parent); KMyMoneyWizardPage* nextPage(void) const; }; /** * Wizard page to allow selecting the filename * * @author Thomas Baumgart */ class KFilePageDecl : public QWidget, public Ui::KFilePageDecl { public: KFilePageDecl(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class FilePage : public KFilePageDecl, public WizardPage { Q_OBJECT public: FilePage(Wizard* parent); virtual bool isComplete(void) const; }; } // namespace #endif