diff --git a/kmymoney/dialogs/CMakeLists.txt b/kmymoney/dialogs/CMakeLists.txt --- a/kmymoney/dialogs/CMakeLists.txt +++ b/kmymoney/dialogs/CMakeLists.txt @@ -22,7 +22,6 @@ kequitypriceupdatedlg.cpp kequitypriceupdateconfdlg.cpp kfindtransactiondlg.cpp - kgpgkeyselectiondlg.cpp kloadtemplatedlg.cpp kmergetransactionsdlg.cpp kmymoneyfileinfodlg.cpp @@ -42,6 +41,7 @@ transactioneditor.cpp stdtransactioneditor.cpp transactionmatcher.cpp + ksaveasquestion.cpp ) set(dialogs_HEADERS @@ -59,7 +59,6 @@ kenterscheduledlg.ui kequitypriceupdatedlg.ui kequitypriceupdateconfdlg.ui kfindtransactiondlg.ui - kgpgkeyselectiondlg.ui kloadtemplatedlg.ui kmymoneyfileinfodlg.ui kmymoneypricedlg.ui knewaccountdlg.ui knewbankdlg.ui @@ -69,6 +68,7 @@ ksortoptiondlg.ui ksplitcorrectiondlg.ui ksplittransactiondlg.ui ktemplateexportdlg.ui kupdatestockpricedlg.ui + ksaveasquestion.ui ) ki18n_wrap_ui(libdialogs_a_SOURCES ${dialogs_UI} ) diff --git a/kmymoney/dialogs/kgpgkeyselectiondlg.h b/kmymoney/dialogs/kgpgkeyselectiondlg.h deleted file mode 100644 --- a/kmymoney/dialogs/kgpgkeyselectiondlg.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2008-2018 Thomas Baumgart - * Copyright 2017-2018 Ł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 KGPGKEYSELECTIONDLG_H -#define KGPGKEYSELECTIONDLG_H - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -/** - * @author Thomas Baumgart - */ -class KGpgKeySelectionDlgPrivate; -class KGpgKeySelectionDlg : public QDialog -{ - Q_OBJECT - Q_DISABLE_COPY(KGpgKeySelectionDlg) - -public: - - explicit KGpgKeySelectionDlg(QWidget* parent = nullptr); - ~KGpgKeySelectionDlg(); - - /** - * preset the key selector with the keys contained in @a keyList. - * The key contained in @a defaultKey is made the current selection. - */ - void setSecretKeys(const QStringList& keyList, const QString& defaultKey); - - /** - * preset the additional key list with the given key ids in @a list - */ - void setAdditionalKeys(const QStringList& list); - - /** - * Returns the selected secret key. In case "No encryption" is selected, - * the string is empty. - */ - QString secretKey() const; - - /** - * Returns the list of keys currently listed in the KEditListWidget - */ - QStringList additionalKeys() const; - -protected Q_SLOTS: - void slotIdChanged(); - void slotKeyListChanged(); - -private: - KGpgKeySelectionDlgPrivate * const d_ptr; - Q_DECLARE_PRIVATE(KGpgKeySelectionDlg) -}; - -#endif diff --git a/kmymoney/dialogs/kgpgkeyselectiondlg.cpp b/kmymoney/dialogs/kgpgkeyselectiondlg.cpp deleted file mode 100644 --- a/kmymoney/dialogs/kgpgkeyselectiondlg.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2008-2018 Thomas Baumgart - * Copyright 2017-2018 Ł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 "kgpgkeyselectiondlg.h" - -// ---------------------------------------------------------------------------- -// QT Includes -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include -#include - -class KGpgKeySelectionDlgPrivate -{ - Q_DISABLE_COPY(KGpgKeySelectionDlgPrivate) - -public: - KGpgKeySelectionDlgPrivate() - : ui(new Ui::KGpgKeySelectionDlg) - , needCheckList(true) - , listOk(false) - , checkCount(0) - { - } - - ~KGpgKeySelectionDlgPrivate() - { - delete ui; - } - - Ui::KGpgKeySelectionDlg* ui; - bool needCheckList; - bool listOk; - int checkCount; -}; - - -KGpgKeySelectionDlg::KGpgKeySelectionDlg(QWidget *parent) : - QDialog(parent), - d_ptr(new KGpgKeySelectionDlgPrivate) -{ - Q_D(KGpgKeySelectionDlg); - d->ui->setupUi(this); - connect(d->ui->m_secretKey, SIGNAL(currentIndexChanged(int)), this, SLOT(slotIdChanged())); - connect(d->ui->m_listWidget, &KEditListWidget::changed, this, &KGpgKeySelectionDlg::slotIdChanged); - connect(d->ui->m_listWidget, &KEditListWidget::added, this, &KGpgKeySelectionDlg::slotKeyListChanged); - connect(d->ui->m_listWidget, &KEditListWidget::removed, this, &KGpgKeySelectionDlg::slotKeyListChanged); -} - -KGpgKeySelectionDlg::~KGpgKeySelectionDlg() -{ - Q_D(KGpgKeySelectionDlg); - delete d; -} - -void KGpgKeySelectionDlg::setSecretKeys(const QStringList& keyList, const QString& defaultKey) -{ - static constexpr char recoveryKeyId[] = "59B0F826D2B08440"; - - Q_D(KGpgKeySelectionDlg); - d->ui->m_secretKey->addItem(i18n("No encryption")); - - foreach(auto key, keyList) { - QStringList fields = key.split(':', QString::SkipEmptyParts); - if (fields[0] != recoveryKeyId) { - // replace parenthesis in name field with brackets - auto name = fields[1]; - name.replace('(', "["); - name.replace(')', "]"); - name = QString("%1 (0x%2)").arg(name).arg(fields[0]); - d->ui->m_secretKey->addItem(name); - if (name.contains(defaultKey)) { - d->ui->m_secretKey->setCurrentText(name); - } - } - } -} - -QString KGpgKeySelectionDlg::secretKey() const -{ - Q_D(const KGpgKeySelectionDlg); - const bool enabled = (d->ui->m_secretKey->currentIndex() != 0); - QString key; - if (enabled) { - key = d->ui->m_secretKey->currentText(); - } - return key; -} - -void KGpgKeySelectionDlg::setAdditionalKeys(const QStringList& list) -{ - Q_D(KGpgKeySelectionDlg); - d->ui->m_listWidget->clear(); - d->ui->m_listWidget->insertStringList(list); - slotKeyListChanged(); -} - -QStringList KGpgKeySelectionDlg::additionalKeys() const -{ - Q_D(const KGpgKeySelectionDlg); - return d->ui->m_listWidget->items(); -} - -#if 0 -void KGpgKeySelectionDlg::slotShowHelp() -{ - QString anchor = m_helpAnchor[m_criteriaTab->currentPage()]; - if (anchor.isEmpty()) - anchor = QString("details.search"); - - KHelpClient::invokeHelp(anchor); -} -#endif - -void KGpgKeySelectionDlg::slotKeyListChanged() -{ - Q_D(KGpgKeySelectionDlg); - d->needCheckList = true; - slotIdChanged(); -} - -void KGpgKeySelectionDlg::slotIdChanged() -{ - Q_D(KGpgKeySelectionDlg); - // this looks a bit awkward. Here's why: KGPGFile::keyAvailable() starts - // an external task and processes UI events while it waits for the external - // process to finish. Thus, the first time we get here, the external process - // is started and the user may press a second key which calls this routine - // again. - // - // The second invocation is counted, but the check is not started until the - // first one finishes. Once the external process finishes, we check if we - // were called in the meantime and restart the check. - if (++d->checkCount == 1) { - const bool enabled = (d->ui->m_secretKey->currentIndex() != 0); - d->ui->m_listWidget->setEnabled(enabled); - d->ui->m_keyLed->setState(enabled ? KLed::On : KLed::Off); - while (enabled) { - // first we check the current edit field if filled - bool keysOk = true; - if (!d->ui->m_listWidget->currentText().isEmpty()) { - keysOk = KGPGFile::keyAvailable(d->ui->m_listWidget->currentText()); - } - - // if it is available, then scan the current list if we need to - if (keysOk) { - if (d->needCheckList) { - QStringList keys = d->ui->m_listWidget->items(); - QStringList::const_iterator it_s; - for (it_s = keys.constBegin(); keysOk && it_s != keys.constEnd(); ++it_s) { - if (!KGPGFile::keyAvailable(*it_s)) - keysOk = false; - } - d->listOk = keysOk; - d->needCheckList = false; - - } else { - keysOk = d->listOk; - } - } - - // did we receive some more requests to check? - if (d->checkCount > 1) { - d->checkCount = 1; - continue; - } - - if (!d->ui->m_listWidget->items().isEmpty()) { - d->ui->m_keyLed->setState(static_cast(keysOk ? KLed::On : KLed::Off)); - } else { - d->ui->m_keyLed->setState(KLed::On); - } - break; - } - - --d->checkCount; - d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!enabled || (d->ui->m_keyLed->state() == KLed::On)); - } -} diff --git a/kmymoney/dialogs/kgpgkeyselectiondlg.ui b/kmymoney/dialogs/kgpgkeyselectiondlg.ui deleted file mode 100644 --- a/kmymoney/dialogs/kgpgkeyselectiondlg.ui +++ /dev/null @@ -1,139 +0,0 @@ - - - KGpgKeySelectionDlg - - - - 0 - 0 - 575 - 480 - - - - Select additional keys - - - true - - - true - - - - - - You have configured KMyMoney to save your data secured with GPG. Please choose the key you want to use for encryption of your data. - - - true - - - - - - - - - - Add additional keys here - - - - - - - Enter the id of the key you want to use for data encryption. This can either be an e-mail address or the hexadecimal key id. In case of the key id, do not forget the leading 0x. - - - - - - - - - - - - Keys for all of the above user ids found - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - KEditListWidget - QWidget -
keditlistwidget.h
-
- - KLed - QWidget -
kled.h
-
-
- - buttonBox - - - - - buttonBox - accepted() - KGpgKeySelectionDlg - accept() - - - 244 - 415 - - - 157 - 274 - - - - - buttonBox - rejected() - KGpgKeySelectionDlg - reject() - - - 312 - 415 - - - 286 - 274 - - - - -
diff --git a/kmymoney/dialogs/ksaveasquestion.h b/kmymoney/dialogs/ksaveasquestion.h new file mode 100644 --- /dev/null +++ b/kmymoney/dialogs/ksaveasquestion.h @@ -0,0 +1,39 @@ +/* + * Copyright 2018 Ł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 KSAVEASQUESTION_H +#define KSAVEASQUESTION_H + +#include + +namespace Ui { class KSaveAsQuestion; } +namespace eKMyMoney { enum class StorageType; } + +class KSaveAsQuestion : public QDialog +{ + Q_DISABLE_COPY(KSaveAsQuestion) + +public: + explicit KSaveAsQuestion(QVector filetypes, QWidget* parent = nullptr); + ~KSaveAsQuestion(); + eKMyMoney::StorageType fileType() const; + +private: + Ui::KSaveAsQuestion * const ui; +}; + +#endif diff --git a/kmymoney/dialogs/ksaveasquestion.cpp b/kmymoney/dialogs/ksaveasquestion.cpp new file mode 100644 --- /dev/null +++ b/kmymoney/dialogs/ksaveasquestion.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2018 Ł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 "ksaveasquestion.h" +#include "kmymoneyenums.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "ui_ksaveasquestion.h" + +KSaveAsQuestion::KSaveAsQuestion(QVector fileTypes, QWidget* parent) : + QDialog(parent), + ui(new Ui::KSaveAsQuestion) +{ + ui->setupUi(this); + for (const auto& fileType : fileTypes) { + switch (fileType) { + case eKMyMoney::StorageType::XML: + ui->fileType->addItem(i18n("XML"), static_cast(fileType)); + break; + case eKMyMoney::StorageType::SQL: + ui->fileType->addItem(i18n("SQL"), static_cast(fileType)); + + break; + default: + break; + } + } + const auto ixXML = ui->fileType->findData(static_cast(eKMyMoney::StorageType::XML)); + ui->fileType->setCurrentIndex(ixXML != -1 ? ixXML : 0); +} + +KSaveAsQuestion::~KSaveAsQuestion() +{ + delete ui; +} + +eKMyMoney::StorageType KSaveAsQuestion::fileType() const +{ + return static_cast(ui->fileType->currentData().toInt()); +} diff --git a/kmymoney/dialogs/ksaveasquestion.ui b/kmymoney/dialogs/ksaveasquestion.ui new file mode 100644 --- /dev/null +++ b/kmymoney/dialogs/ksaveasquestion.ui @@ -0,0 +1,75 @@ + + + KSaveAsQuestion + + + + 0 + 0 + 456 + 136 + + + + Save storage as... + + + Choose XML if unsure. + + + + + + <html><head/><body><p align="center">Availability of storage types depends on plugins enabled in the settings.<br/>If you don't know what to choose here, then <span style=" font-weight:600;">XML</span> is your best choice.</p></body></html> + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + buttonBox + accepted() + KSaveAsQuestion + accept() + + + 320 + 272 + + + 320 + 150 + + + + + buttonBox + rejected() + KSaveAsQuestion + reject() + + + 320 + 272 + + + 320 + 150 + + + + + diff --git a/kmymoney/kmymoney.h b/kmymoney/kmymoney.h --- a/kmymoney/kmymoney.h +++ b/kmymoney/kmymoney.h @@ -65,6 +65,7 @@ template class onlineJobTyped; typedef void (*KMyMoneyAppCallback)(int, int, const QString &); +namespace eKMyMoney { enum class FileAction; } namespace eDialogs { enum class ScheduleResultCode; } namespace eMenu { enum class Action; enum class Menu; } @@ -245,21 +246,6 @@ void slotStatusProgressDone(); public: - enum fileActions { - preOpen, postOpen, preSave, postSave, preClose, postClose - }; - - // 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; - /** * 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. @@ -299,16 +285,6 @@ */ 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(). @@ -422,7 +398,9 @@ void slotCheckSchedules(); +#ifdef KMM_DEBUG void resizeEvent(QResizeEvent*) final override; +#endif void createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount); @@ -435,41 +413,8 @@ void slotFileInfoDialog(); - /** */ - void slotFileNew(); - - /** open a file and load it into the document*/ - void slotFileOpen(); - bool isFileOpenedInAnotherInstance(const QUrl &url); - /** opens a file from the recent files menu */ - - void slotFileOpenRecent(const QUrl &url); - - /** - * 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(); - - /** 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(); /** @@ -575,18 +520,11 @@ */ void slotToolsStartKCalc(); - void slotResetSelections(); - /** * Brings up the new account wizard and saves the information. */ void slotAccountNew(MyMoneyAccount&); - /** - * This method updates all KAction items to the current state. - */ - void slotUpdateActions(); - void webConnect(const QString& sourceUrl, const QByteArray &asn_id); void webConnect(const QUrl url) { webConnect(url.path(), QByteArray()); } @@ -616,14 +554,6 @@ */ bool isProcessingDate(const QDate& date) const final override; - /** - * 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(); - Q_SIGNALS: /** * This signal is emitted when a new file is loaded. In the case file @@ -687,6 +617,21 @@ */ /// \internal d-pointer instance. Private* d; + +public Q_SLOTS: + bool slotFileNew(); + void slotFileOpen(); + bool slotFileOpenRecent(const QUrl &url); + bool slotFileSave(); + bool slotFileSaveAs(); + bool 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(); }; extern KMyMoneyApp *kmymoney; diff --git a/kmymoney/kmymoney.cpp b/kmymoney/kmymoney.cpp --- a/kmymoney/kmymoney.cpp +++ b/kmymoney/kmymoney.cpp @@ -94,6 +94,7 @@ #include "dialogs/kequitypriceupdatedlg.h" #include "dialogs/kmymoneyfileinfodlg.h" #include "dialogs/knewbankdlg.h" +#include "dialogs/ksaveasquestion.h" #include "wizards/newinvestmentwizard/knewinvestmentwizard.h" #include "dialogs/knewaccountdlg.h" #include "dialogs/editpersonaldatadlg.h" @@ -105,7 +106,6 @@ #include "wizards/endingbalancedlg/kendingbalancedlg.h" #include "dialogs/kbalancechartdlg.h" #include "dialogs/kloadtemplatedlg.h" -#include "dialogs/kgpgkeyselectiondlg.h" #include "dialogs/ktemplateexportdlg.h" #include "dialogs/transactionmatcher.h" #include "wizards/newuserwizard/knewuserwizard.h" @@ -177,6 +177,7 @@ #include "dialogenums.h" #include "viewenums.h" #include "menuenums.h" +#include "kmymoneyenums.h" #include "misc/platformtools.h" @@ -188,7 +189,6 @@ using namespace Icons; using namespace eMenu; -static constexpr KCompressionDevice::CompressionType const& COMPRESSION_TYPE = KCompressionDevice::GZip; //static constexpr char recoveryKeyId[] = "0xD2B08440"; static constexpr char recoveryKeyId[] = "59B0F826D2B08440"; @@ -210,15 +210,10 @@ public: Private(KMyMoneyApp *app) : q(app), - m_statementXMLindex(0), - m_balanceWarning(0), m_backupState(backupStateE::BACKUP_IDLE), m_backupResult(0), m_backupMount(0), m_ignoreBackupExitCode(false), - m_fileOpen(false), - m_fmode(QFileDevice::ReadUser | QFileDevice::WriteUser), - m_fileType(KMyMoneyApp::KmmXML), m_myMoneyView(nullptr), m_startDialog(false), m_progressBar(nullptr), @@ -228,9 +223,6 @@ m_progressTimer(nullptr), m_autoSavePeriod(0), m_inAutoSaving(false), - m_saveEncrypted(nullptr), - m_additionalKeyLabel(nullptr), - m_additionalKeyButton(nullptr), m_recentFiles(nullptr), #ifdef KF5Holidays_FOUND m_holidayRegion(nullptr), @@ -247,24 +239,26 @@ } - 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); + struct storageInfo { + eKMyMoney::StorageType type {eKMyMoney::StorageType::None}; + bool isOpened {false}; + QUrl url; + }; + storageInfo m_storageInfo; /** * The public interface. */ KMyMoneyApp * const q; - int m_statementXMLindex; - KBalanceWarning* m_balanceWarning; - /** the configuration object of the application */ KSharedConfigPtr m_config; @@ -295,19 +289,11 @@ */ bool m_ignoreBackupExitCode; - bool m_fileOpen; - QFileDevice::Permissions m_fmode; - - KMyMoneyApp::fileTypeE m_fileType; - 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; @@ -325,17 +311,9 @@ int m_autoSavePeriod; bool m_inAutoSaving; - // 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 @@ -565,43 +543,34 @@ Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectRemoved); } - /** - * 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() + bool askAboutSaving() { - const auto blocked = MyMoneyFile::instance()->blockSignals(true); - - updateAccountNames(); - updateCurrencyNames(); - selectBaseCurrency(); - - // setup the standard precision - AmountEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); - KMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); - - if (!applyFileFixes()) - return false; - - MyMoneyFile::instance()->blockSignals(blocked); - - emit q->kmmFilePlugin(KMyMoneyApp::postOpen); - - Models::instance()->fileOpened(); - connectStorageToModels(); - - // inform everyone about new data - MyMoneyFile::instance()->forceDataChanged(); - - q->slotCheckSchedules(); - - m_myMoneyView->slotFileOpened(); - - onlineJobAdministration::instance()->updateActions(); + const auto isFileNotSaved = q->actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Save)))->isEnabled(); + const auto isNewFileNotSaved = m_storageInfo.isOpened && m_storageInfo.url.isEmpty(); + auto fileNeedsToBeSaved = false; + + if (isFileNotSaved && KMyMoneySettings::autoSaveOnClose()) { + fileNeedsToBeSaved = true; + } else if (isFileNotSaved || isNewFileNotSaved) { + switch (KMessageBox::warningYesNoCancel(q, i18n("The file has been changed, save it?"))) { + case KMessageBox::ButtonCode::Yes: + fileNeedsToBeSaved = true; + break; + case KMessageBox::ButtonCode::No: + fileNeedsToBeSaved = false; + break; + case KMessageBox::ButtonCode::Cancel: + default: + return false; + break; + } + } + if (fileNeedsToBeSaved) { + if (isFileNotSaved) + return q->slotFileSave(); + else if (isNewFileNotSaved) + return q->slotFileSaveAs(); + } return true; } @@ -696,202 +665,15 @@ } } - /** - * 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 openXMLFile(const QUrl &url) - { - // open the database - auto pStorage = MyMoneyFile::instance()->storage(); - if (!pStorage) - pStorage = new MyMoneyStorageMgr; - - auto rc = false; - auto pluginFound = false; - for (const auto& plugin : pPlugins.storage) { - if (plugin->formatName().compare(QLatin1String("XML")) == 0) { - rc = plugin->open(pStorage, url); - pluginFound = true; - break; - } - } - - if(!pluginFound) - KMessageBox::error(q, i18n("Couldn't find suitable plugin to read your storage.")); - - if(!rc) { - removeStorage(); - return false; - } - - if (pStorage) { - MyMoneyFile::instance()->detachStorage(); - MyMoneyFile::instance()->attachStorage(pStorage); - } - - m_fileType = KMyMoneyApp::KmmXML; - return true; - } - - bool isGNCFile(const QUrl &url) - { - if (!url.isValid()) - throw MYMONEYEXCEPTION(QString::fromLatin1("Invalid URL %1").arg(qPrintable(url.url()))); - if (!url.isLocalFile()) - return false; - - const auto fileName = url.toLocalFile(); - const auto sFileToShort = QString::fromLatin1("File %1 is too short.").arg(fileName); - - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) - throw MYMONEYEXCEPTION(QString::fromLatin1("Cannot read the file: %1").arg(fileName)); - - QByteArray qbaFileHeader(2, '\0'); - if (file.read(qbaFileHeader.data(), 2) != 2) - throw MYMONEYEXCEPTION(sFileToShort); - - file.close(); - - QIODevice* qfile = nullptr; - QString sFileHeader(qbaFileHeader); - if (sFileHeader == QString("\037\213")) // gzipped? - qfile = new KCompressionDevice(fileName, COMPRESSION_TYPE); - else - return false; - - if (!qfile->open(QIODevice::ReadOnly)) { - delete qfile; - throw MYMONEYEXCEPTION(QString::fromLatin1("Cannot read the file: %1").arg(fileName)); - } - - // Scan the first 70 bytes to see if we find something - // we know. For now, we support our own XML format and - // GNUCash XML format. If the file is smaller, then it - // contains no valid data and we reject it anyway. - qbaFileHeader.resize(70); - if (qfile->read(qbaFileHeader.data(), 70) != 70) - throw MYMONEYEXCEPTION(sFileToShort); - - QString txt(qbaFileHeader); - - qfile->close(); - delete qfile; - - QRegExp gncexp("formatName().compare(QLatin1String("GNC")) == 0) { - pReader = plugin->reader(); - break; - } - } - if (!pReader) { - KMessageBox::error(q, i18n("Couldn't find suitable plugin to read your storage.")); - return false; - } - m_fileType = KMyMoneyApp::GncXML; - - // disconnect the current storga manager from the engine - MyMoneyFile::instance()->detachStorage(); - - // create a new empty storage object - auto storage = new MyMoneyStorageMgr; - - QIODevice* qfile = new KCompressionDevice(url.toLocalFile(), COMPRESSION_TYPE); - pReader->setProgressCallback(&KMyMoneyApp::progressCallback); - pReader->readFile(qfile, storage); - pReader->setProgressCallback(0); - delete pReader; - - // attach the storage before reading the file, since the online - // onlineJobAdministration object queries the engine during - // loading. - MyMoneyFile::instance()->attachStorage(storage); - - qfile->close(); - delete qfile; - return true; - } - - /** - * 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 &url) - { - // open the database - auto pStorage = MyMoneyFile::instance()->storage(); - if (!pStorage) - pStorage = new MyMoneyStorageMgr; - - auto rc = false; - auto pluginFound = false; - for (const auto& plugin : pPlugins.storage) { - if (plugin->formatName().compare(QLatin1String("SQL")) == 0) { - rc = plugin->open(pStorage, url); - pluginFound = true; - break; - } - } - - if(!pluginFound) - KMessageBox::error(q, i18n("Couldn't find suitable plugin to read your storage.")); - - if(!rc) { - removeStorage(); - return false; - } - - if (pStorage) { - MyMoneyFile::instance()->detachStorage(); - MyMoneyFile::instance()->attachStorage(pStorage); - } - - m_fileType = KMyMoneyApp::KmmDb; - return true; - } - - /** - * Close the currently opened file and create an empty new file. - * - * @see MyMoneyFile - */ - void newFile() - { - closeFile(); - m_fileType = KMyMoneyApp::KmmXML; // assume native type until saved - m_fileOpen = true; - } - /** * 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() { - if (!m_fileOpen) + if (!m_storageInfo.isOpened) return false; return MyMoneyFile::instance()->dirty(); @@ -1351,8 +1133,19 @@ qDebug("Duplicate account in transaction %s", qPrintable(t.id())); } - - + /** + * 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(); + void updateActions(); + bool canFileSaveAs() const; + bool canUpdateAllAccounts() const; + void fileAction(eKMyMoney::FileAction action); }; KMyMoneyApp::KMyMoneyApp(QWidget* parent) : @@ -1377,8 +1170,6 @@ MyMoneyTransactionFilter::setFiscalYearStart(KMyMoneySettings::firstFiscalMonth(), KMyMoneySettings::firstFiscalDay()); - updateCaption(true); - QFrame* frame = new QFrame; frame->setFrameStyle(QFrame::NoFrame); // values for margin (11) and spacing(6) taken from KDialog implementation @@ -1401,23 +1192,15 @@ pActions = initActions(); pMenus = initMenus(); - d->newStorage(); d->m_myMoneyView = new KMyMoneyView; layout->addWidget(d->m_myMoneyView, 10); - connect(d->m_myMoneyView, &KMyMoneyView::aboutToChangeView, this, &KMyMoneyApp::slotResetSelections); connect(d->m_myMoneyView, &KMyMoneyView::viewActivated, this, &KMyMoneyApp::slotViewSelected); - connect(d->m_myMoneyView, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), - this, SLOT(slotUpdateActions())); - connect(d->m_myMoneyView, &KMyMoneyView::statusMsg, this, &KMyMoneyApp::slotStatusMsg); connect(d->m_myMoneyView, &KMyMoneyView::statusProgress, this, &KMyMoneyApp::slotStatusProgressBar); - connect(this, &KMyMoneyApp::fileLoaded, d->m_myMoneyView, &KMyMoneyView::slotRefreshViews); - // Initialize kactivities resource instance #ifdef KF5Activities_FOUND d->m_activityResourceInstance = new KActivities::ResourceInstance(window()->winId(), this); - connect(this, &KMyMoneyApp::fileLoaded, d->m_activityResourceInstance, &KActivities::ResourceInstance::setUri); #endif const auto viewActions = d->m_myMoneyView->actionsToBeConnected(); @@ -1434,7 +1217,6 @@ KMyMoneyPlugin::pluginHandling(KMyMoneyPlugin::Action::Load, pPlugins, this, guiFactory()); onlineJobAdministration::instance()->setOnlinePlugins(pPlugins.extended); d->m_myMoneyView->setOnlinePlugins(pPlugins.online); - d->m_myMoneyView->setStoragePlugins(pPlugins.storage); setCentralWidget(frame); @@ -1456,22 +1238,15 @@ 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(QString()); // kickstart date change timer slotDateChanged(); - - connect(this, SIGNAL(fileLoaded(QUrl)), onlineJobAdministration::instance(), SLOT(updateOnlineTaskProperties())); - + d->fileAction(eKMyMoney::FileAction::Closed); } KMyMoneyApp::~KMyMoneyApp() @@ -1502,7 +1277,7 @@ QUrl KMyMoneyApp::lastOpenedURL() { - QUrl url = d->m_startDialog ? QUrl() : d->m_fileName; + QUrl url = d->m_startDialog ? QUrl() : d->m_storageInfo.url; if (!url.isValid()) { url = QUrl::fromUserInput(readLastUsedFile()); @@ -1589,7 +1364,7 @@ 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::saveAs(this, &KMyMoneyApp::slotFileSaveAs, aC); KStandardAction::close(this, &KMyMoneyApp::slotFileClose, aC); KStandardAction::quit(this, &KMyMoneyApp::slotFileQuit, aC); lutActions.insert(Action::Print, KStandardAction::print(this, &KMyMoneyApp::slotPrintView, aC)); @@ -1973,41 +1748,22 @@ d->m_startDialog = grp.readEntry("StartDialog", true); } +#ifdef KMM_DEBUG void KMyMoneyApp::resizeEvent(QResizeEvent* ev) { KMainWindow::resizeEvent(ev); - updateCaption(true); -} - -int KMyMoneyApp::askSaveOnClose() -{ - int ans; - if (KMyMoneySettings::autoSaveOnClose()) { - ans = KMessageBox::Yes; - } else { - ans = KMessageBox::warningYesNoCancel(this, i18n("The file has been changed, save it?")); - } - return ans; + d->updateCaption(); } +#endif bool KMyMoneyApp::queryClose() { if (!isReady()) return false; - if (d->dirty()) { - int ans = askSaveOnClose(); + if (!slotFileClose()) + return false; - 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; } @@ -2140,109 +1896,19 @@ // MyMoneyFile::instance()->preloadCache(); } -void KMyMoneyApp::slotFileNew() -{ - KMSTATUS(i18n("Creating new document...")); - - slotFileClose(); - - if (!d->m_fileOpen) { - // next line required until we move all file handling out of KMyMoneyView - d->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 - auto 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(); - KMyMoneySettings::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(); - if (d->openXMLFile(d->m_fileName)) { - d->m_fileOpen = true; - d->initializeStorage(); - } - 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->closeFile(); - } - if (wizard->startSettingsAfterFinished()) - slotSettings(); - } else { - // next line required until we move all file handling out of KMyMoneyView - d->closeFile(); - } - delete wizard; - updateCaption(); - - emit fileLoaded(d->m_fileName); - } -} - bool KMyMoneyApp::isDatabase() { - return (d->m_fileOpen && ((d->m_fileType == KmmDb))); + return (d->m_storageInfo.isOpened && ((d->m_storageInfo.type == eKMyMoney::StorageType::SQL))); } bool KMyMoneyApp::isNativeFile() { - return (d->m_fileOpen && (d->m_fileType < MaxNativeFileType)); + return (d->m_storageInfo.isOpened && (d->m_storageInfo.type == eKMyMoney::StorageType::SQL || d->m_storageInfo.type == eKMyMoney::StorageType::XML)); } bool KMyMoneyApp::fileOpen() const { - return d->m_fileOpen; + return d->m_storageInfo.isOpened; } KMyMoneyAppCallback KMyMoneyApp::progressCallback() @@ -2255,35 +1921,6 @@ d->consistencyCheck(alwaysDisplayResult); } -// General open -void KMyMoneyApp::slotFileOpen() -{ - KMSTATUS(i18n("Open a file.")); - - QString prevDir = readLastUsedDir(); - QString fileExtensions; - fileExtensions.append(i18n("KMyMoney files (*.kmy *.xml)")); - fileExtensions.append(QLatin1String(";;")); - - for (const auto& plugin : pPlugins.storage) { - const auto fileExtension = plugin->fileExtension(); - if (!fileExtension.isEmpty()) { - fileExtensions.append(fileExtension); - fileExtensions.append(QLatin1String(";;")); - } - } - fileExtensions.append(i18n("All files (*)")); - - QPointer dialog = new QFileDialog(this, QString(), prevDir, fileExtensions); - dialog->setFileMode(QFileDialog::ExistingFile); - dialog->setAcceptMode(QFileDialog::AcceptOpen); - - if (dialog->exec() == QDialog::Accepted && dialog != nullptr) { - slotFileOpenRecent(dialog->selectedUrls().first()); - } - delete dialog; -} - bool KMyMoneyApp::isImportableFile(const QUrl &url) { bool result = false; @@ -2331,174 +1968,6 @@ return false; } -void KMyMoneyApp::slotFileOpenRecent(const QUrl &url) -{ - KMSTATUS(i18n("Loading file...")); - if (isFileOpenedInAnotherInstance(url)) { - KMessageBox::sorry(this, i18n("

File %1 is already opened in another instance of KMyMoney

", url.toDisplayString(QUrl::PreferLocalFile)), i18n("Duplicate open")); - return; - } - - if (url.scheme() != QLatin1String("sql") && !KMyMoneyUtils::fileExists(url)) { - 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")); - return; - } - - if (d->m_fileOpen) - slotFileClose(); - - if (d->m_fileOpen) - return; - - try { - auto isOpened = false; - if (url.scheme() == QLatin1String("sql")) - isOpened = d->openDatabase(url); - else if (d->isGNCFile(url)) - isOpened = d->openGNCFile(url); - else - isOpened = d->openXMLFile(url); - - if (!isOpened) - return; - - d->m_fileOpen = true; - if (!d->initializeStorage()) { - d->m_fileOpen = false; - return; - } - - if (isNativeFile()) { - d->m_fileName = url; - updateCaption(); - writeLastUsedFile(url.toDisplayString(QUrl::PreferLocalFile)); - /* Don't use url variable after KRecentFilesAction::addUrl - * as it might delete it. - * More in API reference to this method - */ - d->m_recentFiles->addUrl(url); - } else { - d->m_fileName = QUrl(); // imported files have no filename - } - - } catch (const MyMoneyException &e) { - KMessageBox::sorry(this, i18n("Cannot open file as requested. Error was: %1", QString::fromLatin1(e.what()))); - } - updateCaption(); - emit fileLoaded(d->m_fileName); -} - -bool KMyMoneyApp::slotFileSave() -{ - // if there's nothing changed, there's no need to save anything - if (!d->dirty()) - return true; - - bool rc = false; - - KMSTATUS(i18n("Saving file...")); - - if (d->m_fileName.isEmpty()) - return false; - - d->consistencyCheck(false); - - setEnabled(false); - QString format; - switch (d->m_fileType) { - case KMyMoneyApp::KmmXML: - case KMyMoneyApp::GncXML: - format = QStringLiteral("XML"); - break; - case KMyMoneyApp::KmmDb: - format = QStringLiteral("SQL"); - break; - default: - return false; - } - - auto pluginFound = false; - for (const auto& plugin : pPlugins.storage) { - if (plugin->formatName().compare(format) == 0) { - rc = plugin->save(d->m_fileName); - pluginFound = true; - break; - } - } - if(!pluginFound) - KMessageBox::error(this, i18n("Couldn't find suitable plugin to save your storage.")); - - setEnabled(true); - - d->m_autoSaveTimer->stop(); - - updateCaption(); - return rc; -} - -void KMyMoneyApp::slotFileCloseWindow() -{ - KMSTATUS(i18n("Closing window...")); - - if (d->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->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() { @@ -2525,7 +1994,7 @@ #ifdef KMM_DEBUG void KMyMoneyApp::slotFileFileInfo() { - if (!d->m_fileOpen) { + if (!d->m_storageInfo.isOpened) { KMessageBox::information(this, i18n("No KMyMoneyFile open")); return; } @@ -2620,7 +2089,7 @@ void KMyMoneyApp::slotFileViewPersonal() { - if (!d->m_fileOpen) { + if (!d->m_storageInfo.isOpened) { KMessageBox::information(this, i18n("No KMyMoneyFile open")); return; } @@ -2759,10 +2228,11 @@ { if(dialogName.compare(QLatin1String("Plugins")) == 0) { KMyMoneyPlugin::pluginHandling(KMyMoneyPlugin::Action::Reorganize, pPlugins, this, guiFactory()); + actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::SaveAs)))->setEnabled(d->canFileSaveAs()); onlineJobAdministration::instance()->updateActions(); onlineJobAdministration::instance()->setOnlinePlugins(pPlugins.extended); d->m_myMoneyView->setOnlinePlugins(pPlugins.online); - d->m_myMoneyView->setStoragePlugins(pPlugins.storage); + d->updateActions(); return; } MyMoneyTransactionFilter::setFiscalYearStart(KMyMoneySettings::firstFiscalMonth(), KMyMoneySettings::firstFiscalDay()); @@ -2848,12 +2318,12 @@ - if (d->m_fileName.isEmpty()) + if (d->m_storageInfo.url.isEmpty()) return; - if (!d->m_fileName.isLocalFile()) { + if (!d->m_storageInfo.url.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("The current implementation of the backup functionality only supports local files as source files. Your current source file is '%1'.", d->m_storageInfo.url.url()), i18n("Local files only")); return; @@ -2899,9 +2369,9 @@ bool KMyMoneyApp::slotBackupWriteFile() { - QFileInfo fi(d->m_fileName.fileName()); + QFileInfo fi(d->m_storageInfo.url.fileName()); QString today = QDate::currentDate().toString("-yyyy-MM-dd.") + fi.suffix(); - QString backupfile = d->m_mountpoint + '/' + d->m_fileName.fileName(); + QString backupfile = d->m_mountpoint + '/' + d->m_storageInfo.url.fileName(); KMyMoneyUtils::appendCorrectFileExt(backupfile, today); // check if file already exists and ask what to do @@ -2917,10 +2387,10 @@ 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); + d->m_proc << (QDir::toNativeSeparators(d->m_storageInfo.url.toLocalFile()) + "+ nul") << QDir::toNativeSeparators(backupfile); #else d->m_proc << "cp" << "-f"; - d->m_proc << d->m_fileName.toLocalFile() << backupfile; + d->m_proc << d->m_storageInfo.url.toLocalFile() << backupfile; #endif d->m_backupState = BACKUP_COPYING; d->m_proc.start(); @@ -3362,110 +2832,79 @@ d->m_myMoneyView->slotPrintView(); } -void KMyMoneyApp::updateCaption(bool skipActions) +void KMyMoneyApp::Private::updateCaption() { - QString caption; - - caption = d->m_fileName.fileName(); - - if (caption.isEmpty() && d->m_myMoneyView && d->m_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; - } + auto caption = m_storageInfo.url.isEmpty() && m_myMoneyView && m_storageInfo.isOpened ? + i18n("Untitled") : + m_storageInfo.url.fileName(); #ifdef KMM_DEBUG - caption += QString(" (%1 x %2)").arg(width()).arg(height()); + caption += QString(" (%1 x %2)").arg(q->width()).arg(q->height()); #endif - setCaption(caption, modified); - - if (!skipActions) { - d->m_myMoneyView->enableViewsIfFileOpen(d->m_fileOpen); - slotUpdateActions(); - } + q->setCaption(caption, MyMoneyFile::instance()->dirty()); } -void KMyMoneyApp::slotUpdateActions() +void KMyMoneyApp::Private::updateActions() { - const auto file = MyMoneyFile::instance(); - const bool fileOpen = d->m_fileOpen; - const bool modified = file->dirty(); -// const bool importRunning = (d->m_smtReader != 0); - auto aC = actionCollection(); - - // ************* - // Disabling actions based on conditions - // ************* + const QVector actions { - 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 && !isDatabase()))}, - {qMakePair(Action::FileInformation, fileOpen)}, - {qMakePair(Action::FileImportTemplate, fileOpen/* && !importRunning*/)}, - {qMakePair(Action::FileExportTemplate, fileOpen/* && !importRunning*/)}, + Action::FilePersonalData, Action::FileInformation, Action::FileImportTemplate, Action::FileExportTemplate, #ifdef KMM_DEBUG - {qMakePair(Action::FileDump, fileOpen)}, + Action::FileDump, #endif - {qMakePair(Action::EditFindTransaction, fileOpen)}, - {qMakePair(Action::ToolCurrencies, fileOpen)}, - {qMakePair(Action::ToolPrices, fileOpen)}, - {qMakePair(Action::ToolUpdatePrices, fileOpen)}, - {qMakePair(Action::ToolConsistency, fileOpen)}, - {qMakePair(Action::NewAccount, fileOpen)}, - {qMakePair(Action::NewCategory, fileOpen)}, - {qMakePair(Action::AccountCreditTransfer, onlineJobAdministration::instance()->canSendCreditTransfer())}, - {qMakePair(Action::NewInstitution, fileOpen)}, -// {qMakePair(Action::TransactionNew, (fileOpen && d->m_myMoneyView->canCreateTransactions(KMyMoneyRegister::SelectedTransactions(), tooltip)))}, - {qMakePair(Action::NewSchedule, fileOpen)}, -// {qMakePair(Action::CurrencyNew, fileOpen)}, -// {qMakePair(Action::PriceNew, fileOpen)}, - }; + Action::EditFindTransaction, Action::NewCategory, Action::ToolCurrencies, Action::ToolPrices, Action::ToolUpdatePrices, + Action::ToolConsistency, Action::ToolPerformance, Action::NewAccount, Action::NewInstitution, Action::NewSchedule + }; - for (const auto& a : actionStates) - pActions[a.first]->setEnabled(a.second); - } + for (const auto &action : actions) + pActions[action]->setEnabled(m_storageInfo.isOpened); + pActions[Action::FileBackup]->setEnabled(m_storageInfo.isOpened && m_storageInfo.type == eKMyMoney::StorageType::XML); - // ************* - // 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()); + auto aC = q->actionCollection(); + aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::SaveAs)))->setEnabled(canFileSaveAs()); + aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Close)))->setEnabled(m_storageInfo.isOpened); + pActions[eMenu::Action::UpdateAllAccounts]->setEnabled(canUpdateAllAccounts()); } -void KMyMoneyApp::slotResetSelections() +bool KMyMoneyApp::Private::canFileSaveAs() const { - d->m_myMoneyView->slotObjectSelected(MyMoneyAccount()); - d->m_myMoneyView->slotObjectSelected(MyMoneyInstitution()); - d->m_myMoneyView->slotObjectSelected(MyMoneySchedule()); - d->m_myMoneyView->slotObjectSelected(MyMoneyTag()); - d->m_myMoneyView->slotSelectByVariant(QVariantList {QVariant::fromValue(KMyMoneyRegister::SelectedTransactions())}, eView::Intent::SelectRegisterTransactions); - slotUpdateActions(); + return (m_storageInfo.isOpened && + (!pPlugins.storage.isEmpty() && + !(pPlugins.storage.count() == 1 && pPlugins.storage.first()->storageType() == eKMyMoney::StorageType::GNC))); } -void KMyMoneyApp::slotDataChanged() +bool KMyMoneyApp::Private::canUpdateAllAccounts() const { - // 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 + const auto file = MyMoneyFile::instance(); + auto rc = false; + if (!file->storageAttached()) + return rc; + + QList accList; + file->accountList(accList); + QList::const_iterator it_a; + auto it_p = pPlugins.online.constEnd(); + for (it_a = accList.constBegin(); (it_p == pPlugins.online.constEnd()) && (it_a != accList.constEnd()); ++it_a) { + if ((*it_a).hasOnlineMapping()) { + // check if provider is available + it_p = pPlugins.online.constFind((*it_a).onlineBankingSettings().value("provider").toLower()); + if (it_p != pPlugins.online.constEnd()) { + QStringList protocols; + (*it_p)->protocols(protocols); + if (!protocols.isEmpty()) { + rc = true; + break; + } + } + } } - updateCaption(); + return rc; +} + +void KMyMoneyApp::slotDataChanged() +{ + d->fileAction(eKMyMoney::FileAction::Changed); } void KMyMoneyApp::slotCurrencyDialog() @@ -3485,7 +2924,6 @@ void KMyMoneyApp::slotFileConsistencyCheck() { d->consistencyCheck(true); - updateCaption(); } void KMyMoneyApp::Private::consistencyCheck(bool alwaysDisplayResult) @@ -3624,7 +3062,6 @@ rc = eDialogs::ScheduleResultCode::Enter; } } - updateCaption(); } } @@ -3691,17 +3128,17 @@ QString KMyMoneyApp::filename() const { - return d->m_fileName.url(); + return d->m_storageInfo.url.url(); } QUrl KMyMoneyApp::filenameURL() const { - return d->m_fileName; + return d->m_storageInfo.url; } void KMyMoneyApp::writeFilenameURL(const QUrl &url) { - d->m_fileName = url; + d->m_storageInfo.url = url; } void KMyMoneyApp::addToRecentFiles(const QUrl& url) @@ -3779,12 +3216,12 @@ //KStartupInfo::setNewStartupId(this, asn_id); // Make sure we have an open file - if (! d->m_fileOpen && + if (! d->m_storageInfo.isOpened && KMessageBox::warningContinueCancel(this, i18n("You must first select a KMyMoney file before you can import a statement.")) == KMessageBox::Continue) slotFileOpen(); // only continue if the user really did open a file. - if (d->m_fileOpen) { + if (d->m_storageInfo.isOpened) { KMSTATUS(i18n("Importing a statement via Web Connect")); // remove the statement files @@ -3946,57 +3383,365 @@ #endif } -KMStatus::KMStatus(const QString &text) +bool KMyMoneyApp::slotFileNew() { - m_prevText = kmymoney->slotStatusMsg(text); + KMSTATUS(i18n("Creating new document...")); + + if (!slotFileClose()) + return false; + + NewUserWizard::Wizard wizard; + if (wizard.exec() != QDialog::Accepted) + return false; + + d->m_storageInfo.isOpened = true; + d->m_storageInfo.type = eKMyMoney::StorageType::None; + d->m_storageInfo.url = QUrl(); + + try { + auto storage = new MyMoneyStorageMgr; + MyMoneyFile::instance()->attachStorage(storage); + + MyMoneyFileTransaction ft; + auto file = MyMoneyFile::instance(); + // 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 + auto 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 + for (auto &tmpl : wizard.templates()) + tmpl.importTemplate(progressCallback); + + ft.commit(); + KMyMoneySettings::setFirstTimeRun(false); + + d->fileAction(eKMyMoney::FileAction::Opened); + if (actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::SaveAs)))->isEnabled()) + slotFileSaveAs(); + } catch (const MyMoneyException & e) { + slotFileClose(); + d->removeStorage(); + KMessageBox::detailedError(this, i18n("Couldn't create a new file."), e.what()); + return false; + } + + if (wizard.startSettingsAfterFinished()) + slotSettings(); + return true; } -KMStatus::~KMStatus() +void KMyMoneyApp::slotFileOpen() { - kmymoney->slotStatusMsg(m_prevText); + KMSTATUS(i18n("Open a file.")); + + const QVector desiredFileExtensions {eKMyMoney::StorageType::XML, eKMyMoney::StorageType::GNC}; + QString fileExtensions; + for (const auto &extension : desiredFileExtensions) { + for (const auto &plugin : pPlugins.storage) { + if (plugin->storageType() == extension) { + fileExtensions += plugin->fileExtension() + QLatin1String(";;"); + break; + } + } + } + + if (fileExtensions.isEmpty()) { + KMessageBox::error(this, i18n("Couldn't find any plugin for opening storage.")); + return; + } + + fileExtensions.append(i18n("All files (*)")); + + QPointer dialog = new QFileDialog(this, QString(), readLastUsedDir(), fileExtensions); + dialog->setFileMode(QFileDialog::ExistingFile); + dialog->setAcceptMode(QFileDialog::AcceptOpen); + + if (dialog->exec() == QDialog::Accepted && dialog != nullptr) + slotFileOpenRecent(dialog->selectedUrls().first()); + delete dialog; } -void KMyMoneyApp::Private::unlinkStatementXML() +bool KMyMoneyApp::slotFileOpenRecent(const QUrl &url) { - 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])); + KMSTATUS(i18n("Loading file...")); + + if (!url.isValid()) + throw MYMONEYEXCEPTION(QString::fromLatin1("Invalid URL %1").arg(qPrintable(url.url()))); + + if (isFileOpenedInAnotherInstance(url)) { + KMessageBox::sorry(this, i18n("

File %1 is already opened in another instance of KMyMoney

", url.toDisplayString(QUrl::PreferLocalFile)), i18n("Duplicate open")); + return false; + } + + if (url.scheme() != QLatin1String("sql") && !KMyMoneyUtils::fileExists(url)) { + 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")); + return false; + } + + if (d->m_storageInfo.isOpened) + if (!slotFileClose()) + return false; + + // open the database + d->m_storageInfo.type = eKMyMoney::StorageType::None; + for (auto &plugin : pPlugins.storage) { + try { + if (auto pStorage = plugin->open(url)) { + MyMoneyFile::instance()->attachStorage(pStorage); + d->m_storageInfo.type = plugin->storageType(); + if (plugin->storageType() != eKMyMoney::StorageType::GNC) { + d->m_storageInfo.url = url; + writeLastUsedFile(url.toDisplayString(QUrl::PreferLocalFile)); + /* Don't use url variable after KRecentFilesAction::addUrl + * as it might delete it. + * More in API reference to this method + */ + d->m_recentFiles->addUrl(url); + } + d->m_storageInfo.isOpened = true; + break; + } + } catch (const MyMoneyException &e) { + KMessageBox::sorry(this, i18n("Cannot open file as requested. Error was: %1", QString::fromLatin1(e.what()))); + return false; + } } - m_statementXMLindex = 0; + + if(d->m_storageInfo.type == eKMyMoney::StorageType::None) { + KMessageBox::error(this, i18n("Could not read your data source. Please check the KMyMoney settings that the necessary plugin is enabled.")); + return false; + } + + d->fileAction(eKMyMoney::FileAction::Opened); + return true; } -void KMyMoneyApp::Private::closeFile() +bool KMyMoneyApp::slotFileSave() { - m_myMoneyView->slotObjectSelected(MyMoneyAccount()); - m_myMoneyView->slotObjectSelected(MyMoneyInstitution()); - m_myMoneyView->slotObjectSelected(MyMoneySchedule()); - m_myMoneyView->slotObjectSelected(MyMoneyTag()); - m_myMoneyView->slotSelectByVariant(QVariantList {QVariant::fromValue(KMyMoneyRegister::SelectedTransactions())}, eView::Intent::SelectRegisterTransactions); + KMSTATUS(i18n("Saving file...")); + + for (const auto& plugin : pPlugins.storage) { + if (plugin->storageType() == d->m_storageInfo.type) { + d->consistencyCheck(false); + try { + if (plugin->save(d->m_storageInfo.url)) { + d->fileAction(eKMyMoney::FileAction::Saved); + return true; + } + return false; + } catch (const MyMoneyException &e) { + KMessageBox::detailedError(this, i18n("Failed to save your storage."), e.what()); + return false; + } + } + } + + KMessageBox::error(this, i18n("Couldn't find suitable plugin to save your storage.")); + return false; +} + +bool KMyMoneyApp::slotFileSaveAs() +{ + KMSTATUS(i18n("Saving file as....")); + + QVector availableFileTypes; + for (const auto& plugin : pPlugins.storage) { + switch (plugin->storageType()) { + case eKMyMoney::StorageType::GNC: + break; + default: + availableFileTypes.append(plugin->storageType()); + break; + } + } - m_myMoneyView->finishReconciliation(MyMoneyAccount()); + auto chosenFileType = eKMyMoney::StorageType::None; + switch (availableFileTypes.count()) { + case 0: + KMessageBox::error(this, i18n("Couldn't find any plugin for saving storage.")); + return false; + case 1: + chosenFileType = availableFileTypes.first(); + break; + default: + { + KSaveAsQuestion dlg(availableFileTypes, this); + if (dlg.exec() != QDialog::Accepted) + return false; + chosenFileType = dlg.fileType(); + } + } - m_myMoneyView->slotFileClosed(); + for (const auto &plugin : pPlugins.storage) { + if (chosenFileType == plugin->storageType()) { + try { + d->consistencyCheck(false); + if (plugin->saveAs()) { + d->fileAction(eKMyMoney::FileAction::Saved); + d->m_storageInfo.type = plugin->storageType(); + return true; + } + } catch (const MyMoneyException &e) { + KMessageBox::detailedError(this, i18n("Failed to save your storage."), e.what()); + } + } + } + return false; +} - disconnectStorageFromModels(); +bool KMyMoneyApp::slotFileClose() +{ + if (!d->m_storageInfo.isOpened) + return true; - // 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(); + if (!d->askAboutSaving()) + return false; - emit q->kmmFilePlugin(KMyMoneyApp::preClose); - if (q->isDatabase()) - MyMoneyFile::instance()->storage()->close(); // to log off a database user - newStorage(); + d->fileAction(eKMyMoney::FileAction::Closing); - emit q->kmmFilePlugin(postClose); - m_fileOpen = false; + d->removeStorage(); - m_fileName = QUrl(); - q->updateCaption(); + d->m_storageInfo = KMyMoneyApp::Private::storageInfo(); - // just create a new balance warning object - delete m_balanceWarning; - m_balanceWarning = new KBalanceWarning(q); + d->fileAction(eKMyMoney::FileAction::Closed); + return true; +} + +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; - emit q->fileLoaded(m_fileName); + 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::Private::fileAction(eKMyMoney::FileAction action) +{ + switch(action) { + case eKMyMoney::FileAction::Opened: + q->actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Save)))->setEnabled(false); + updateAccountNames(); + updateCurrencyNames(); + selectBaseCurrency(); + + // setup the standard precision + AmountEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); + KMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); + + applyFileFixes(); + Models::instance()->fileOpened(); + connectStorageToModels(); + // inform everyone about new data + MyMoneyFile::instance()->forceDataChanged(); + updateActions(); + m_myMoneyView->slotFileOpened(); + onlineJobAdministration::instance()->updateActions(); + m_myMoneyView->enableViewsIfFileOpen(m_storageInfo.isOpened); + m_myMoneyView->slotRefreshViews(); + onlineJobAdministration::instance()->updateOnlineTaskProperties(); + q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KMyMoneyApp::slotDataChanged); + +#ifdef KF5Activities_FOUND + m_activityResourceInstance->setUri(m_storageInfo.url); +#endif + break; + + case eKMyMoney::FileAction::Saved: + q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KMyMoneyApp::slotDataChanged); + q->actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Save)))->setEnabled(false); + m_autoSaveTimer->stop(); + break; + + case eKMyMoney::FileAction::Closing: + disconnect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KMyMoneyApp::slotDataChanged); + m_myMoneyView->slotFileClosed(); + // 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(); + break; + + case eKMyMoney::FileAction::Closed: + q->disconnect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KMyMoneyApp::slotDataChanged); + disconnectStorageFromModels(); + q->actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Save)))->setEnabled(false); + m_myMoneyView->enableViewsIfFileOpen(m_storageInfo.isOpened); + updateActions(); + break; + + case eKMyMoney::FileAction::Changed: + q->disconnect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KMyMoneyApp::slotDataChanged); + q->actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Save)))->setEnabled(true && !m_storageInfo.url.isEmpty()); + // 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 (m_autoSaveEnabled && !m_autoSaveTimer->isActive()) { + m_autoSaveTimer->setSingleShot(true); + m_autoSaveTimer->start(m_autoSavePeriod * 60 * 1000); //miliseconds + } + pActions[eMenu::Action::UpdateAllAccounts]->setEnabled(canUpdateAllAccounts()); + break; + + default: + break; + } + + updateCaption(); +} + +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])); + } } diff --git a/kmymoney/kmymoneyenums.h b/kmymoney/kmymoneyenums.h new file mode 100644 --- /dev/null +++ b/kmymoney/kmymoneyenums.h @@ -0,0 +1,42 @@ +/* + * Copyright 2018 Ł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 KMYMONEYENUMS_H +#define KMYMONEYENUMS_H + +#include + +namespace eKMyMoney { + enum class FileAction { + Opened, + Saved, + Closing, + Closed, + Changed + }; + + enum class StorageType { + None, + XML, + SQL, + GNC + }; + + inline uint qHash(const StorageType key, uint seed) { return ::qHash(static_cast(key), seed); } + +} +#endif diff --git a/kmymoney/main.cpp b/kmymoney/main.cpp --- a/kmymoney/main.cpp +++ b/kmymoney/main.cpp @@ -304,7 +304,6 @@ kmymoney->slotFileQuit(); } - kmymoney->updateCaption(); kmymoney->centralWidget()->setEnabled(true); kmymoney->show(); splash.reset(); diff --git a/kmymoney/mymoney/onlinejobadministration.cpp b/kmymoney/mymoney/onlinejobadministration.cpp --- a/kmymoney/mymoney/onlinejobadministration.cpp +++ b/kmymoney/mymoney/onlinejobadministration.cpp @@ -388,6 +388,9 @@ registerAllOnlineTasks(); } + if (!MyMoneyFile::instance()->storageAttached()) + return false; + // Check if any plugin supports a loaded online task foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, *m_onlinePlugins) { QList accounts; @@ -412,6 +415,9 @@ registerAllOnlineTasks(); } + if (!MyMoneyFile::instance()->storageAttached()) + return false; + foreach (onlineTask* task, m_onlineTasks) { // Check if a online task has the correct type if (dynamic_cast(task) != 0) { diff --git a/kmymoney/plugins/appinterface.h b/kmymoney/plugins/appinterface.h --- a/kmymoney/plugins/appinterface.h +++ b/kmymoney/plugins/appinterface.h @@ -60,14 +60,11 @@ virtual void writeLastUsedFile(const QString& fileName) = 0; virtual void slotFileOpenRecent(const QUrl &url) = 0; virtual void addToRecentFiles(const QUrl& url) = 0; - virtual void updateCaption(bool skipActions = false) = 0; - virtual QTimer* autosaveTimer() = 0; virtual KMyMoneyAppCallback progressCallback() = 0; virtual void writeLastUsedDir(const QString &directory) = 0; virtual QString readLastUsedDir() const = 0; virtual void consistencyCheck(bool alwaysDisplayResult) = 0; - Q_SIGNALS: void kmmFilePlugin(unsigned int); }; diff --git a/kmymoney/plugins/csv/export/csvexporter.cpp b/kmymoney/plugins/csv/export/csvexporter.cpp --- a/kmymoney/plugins/csv/export/csvexporter.cpp +++ b/kmymoney/plugins/csv/export/csvexporter.cpp @@ -16,8 +16,7 @@ */ #include "csvexporter.h" -#include "csvexportdlg.h" -#include "csvwriter.h" + // ---------------------------------------------------------------------------- // QT Includes @@ -35,6 +34,10 @@ // ---------------------------------------------------------------------------- // Project Includes +#include "csvexportdlg.h" +#include "csvwriter.h" +#include "viewinterface.h" + CSVExporter::CSVExporter(QObject *parent, const QVariantList &args) : KMyMoneyPlugin::Plugin(parent, "csvexporter"/*must be the same as X-KDE-PluginInfo-Name*/) { @@ -53,9 +56,11 @@ void CSVExporter::createActions() { - m_action = actionCollection()->addAction("file_export_csv"); + const auto &kpartgui = QStringLiteral("file_export_csv"); + m_action = actionCollection()->addAction(kpartgui); m_action->setText(i18n("&CSV...")); connect(m_action, &QAction::triggered, this, &CSVExporter::slotCsvExport); + connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action(qPrintable(kpartgui)), &QAction::setEnabled); } void CSVExporter::slotCsvExport() diff --git a/kmymoney/plugins/csv/import/csvimporter.cpp b/kmymoney/plugins/csv/import/csvimporter.cpp --- a/kmymoney/plugins/csv/import/csvimporter.cpp +++ b/kmymoney/plugins/csv/import/csvimporter.cpp @@ -36,6 +36,7 @@ #include "core/csvimportercore.h" #include "csvwizard.h" #include "statementinterface.h" +#include "viewinterface.h" CSVImporter::CSVImporter(QObject *parent, const QVariantList &args) : KMyMoneyPlugin::Plugin(parent, "csvimporter"/*must be the same as X-KDE-PluginInfo-Name*/) @@ -55,9 +56,11 @@ void CSVImporter::createActions() { - m_action = actionCollection()->addAction("file_import_csv"); + const auto &kpartgui = QStringLiteral("file_import_csv"); + m_action = actionCollection()->addAction(kpartgui); m_action->setText(i18n("CSV...")); connect(m_action, &QAction::triggered, this, &CSVImporter::startWizardRun); + connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action(qPrintable(kpartgui)), &QAction::setEnabled); } void CSVImporter::startWizardRun() diff --git a/kmymoney/plugins/gnc/import/CMakeLists.txt b/kmymoney/plugins/gnc/import/CMakeLists.txt --- a/kmymoney/plugins/gnc/import/CMakeLists.txt +++ b/kmymoney/plugins/gnc/import/CMakeLists.txt @@ -33,5 +33,6 @@ kmm_plugin PRIVATE KF5::Completion + KF5::Archive Alkimia::alkimia ) diff --git a/kmymoney/plugins/gnc/import/gncimporter.h b/kmymoney/plugins/gnc/import/gncimporter.h --- a/kmymoney/plugins/gnc/import/gncimporter.h +++ b/kmymoney/plugins/gnc/import/gncimporter.h @@ -38,10 +38,11 @@ explicit GNCImporter(QObject *parent, const QVariantList &args); ~GNCImporter() override; - bool open(MyMoneyStorageMgr *storage, const QUrl &url) override; + MyMoneyStorageMgr *open(const QUrl &url) override; bool save(const QUrl &url) override; - IMyMoneyOperationsFormat* reader() override; - QString formatName() const override; + bool saveAs() override; + + eKMyMoney::StorageType storageType() const override; QString fileExtension() const override; }; diff --git a/kmymoney/plugins/gnc/import/gncimporter.cpp b/kmymoney/plugins/gnc/import/gncimporter.cpp --- a/kmymoney/plugins/gnc/import/gncimporter.cpp +++ b/kmymoney/plugins/gnc/import/gncimporter.cpp @@ -29,6 +29,7 @@ #include #include #include +#include // ---------------------------------------------------------------------------- // Project Includes @@ -39,9 +40,12 @@ #include "mymoneyfile.h" #include "mymoneyexception.h" #include "mymoneystoragemgr.h" +#include "kmymoneyenums.h" class MyMoneyStatement; +static constexpr KCompressionDevice::CompressionType const& COMPRESSION_TYPE = KCompressionDevice::GZip; + GNCImporter::GNCImporter(QObject *parent, const QVariantList &args) : KMyMoneyPlugin::Plugin(parent, "gncimporter"/*must be the same as X-KDE-PluginInfo-Name*/) { @@ -56,27 +60,82 @@ qDebug("Plugins: gncimporter unloaded"); } -bool GNCImporter::open(MyMoneyStorageMgr *storage, const QUrl &url) -{ - Q_UNUSED(url) - Q_UNUSED(storage) - return false; +MyMoneyStorageMgr *GNCImporter::open(const QUrl &url) +{ + if (url.scheme() == QLatin1String("sql")) + return nullptr; + + if (!url.isLocalFile()) + return nullptr; + + const auto fileName = url.toLocalFile(); + const auto sFileToShort = QString::fromLatin1("File %1 is too short.").arg(fileName); + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + throw MYMONEYEXCEPTION(QString::fromLatin1("Cannot read the file: %1").arg(fileName)); + + QByteArray qbaFileHeader(2, '\0'); + if (file.read(qbaFileHeader.data(), 2) != 2) + throw MYMONEYEXCEPTION(sFileToShort); + + file.close(); + + QIODevice* qfile = nullptr; + QString sFileHeader(qbaFileHeader); + if (sFileHeader == QString("\037\213")) // gzipped? + qfile = new KCompressionDevice(fileName, COMPRESSION_TYPE); + else + return nullptr; + + if (!qfile->open(QIODevice::ReadOnly)) { + delete qfile; + throw MYMONEYEXCEPTION(QString::fromLatin1("Cannot read the file: %1").arg(fileName)); + } + + // Scan the first 70 bytes to see if we find something + // we know. For now, we support our own XML format and + // GNUCash XML format. If the file is smaller, then it + // contains no valid data and we reject it anyway. + qbaFileHeader.resize(70); + if (qfile->read(qbaFileHeader.data(), 70) != 70) + throw MYMONEYEXCEPTION(sFileToShort); + + QString txt(qbaFileHeader); + + QRegExp gncexp("seek(0); + + auto storage = new MyMoneyStorageMgr; + pReader.setProgressCallback(appInterface()->progressCallback()); + pReader.readFile(qfile, storage); + pReader.setProgressCallback(0); + + qfile->close(); + delete qfile; + return storage; } bool GNCImporter::save(const QUrl &url) { Q_UNUSED(url) return false; } -IMyMoneyOperationsFormat* GNCImporter::reader() +bool GNCImporter::saveAs() { - return new MyMoneyGncReader; + return false; } -QString GNCImporter::formatName() const +eKMyMoney::StorageType GNCImporter::storageType() const { - return QStringLiteral("GNC"); + return eKMyMoney::StorageType::GNC; } QString GNCImporter::fileExtension() const diff --git a/kmymoney/plugins/gnc/import/mymoneygncreader.h b/kmymoney/plugins/gnc/import/mymoneygncreader.h --- a/kmymoney/plugins/gnc/import/mymoneygncreader.h +++ b/kmymoney/plugins/gnc/import/mymoneygncreader.h @@ -833,7 +833,7 @@ #endif // _GNCFILEANON public: MyMoneyGncReader(); - virtual ~MyMoneyGncReader(); + ~MyMoneyGncReader() override; /** * Import a GnuCash XML file * @@ -848,6 +848,7 @@ void writeFile(QIODevice*, MyMoneyStorageMgr*) final override { return ; } // dummy entry needed by kmymoneywiew. we will not be writing + void setProgressCallback(void(*callback)(int, int, const QString&)) final override; #else void readFile(QString, QString); #endif // _GNCFILEANON @@ -916,7 +917,6 @@ void postMessage(const QString&, const unsigned int, const char *, const char *); void postMessage(const QString&, const unsigned int, const char *, const char *, const char *); void postMessage(const QString&, const unsigned int, const QStringList&); - void setProgressCallback(void(*callback)(int, int, const QString&)) final override; void signalProgress(int current, int total, const QString& = ""); /** user options */ /** diff --git a/kmymoney/plugins/icalendar/export/icalendarexporter.cpp b/kmymoney/plugins/icalendar/export/icalendarexporter.cpp --- a/kmymoney/plugins/icalendar/export/icalendarexporter.cpp +++ b/kmymoney/plugins/icalendar/export/icalendarexporter.cpp @@ -37,6 +37,7 @@ #include "schedulestoicalendar.h" #include "pluginsettings.h" +#include "viewinterface.h" struct iCalendarExporter::Private { QAction* m_action; @@ -83,9 +84,11 @@ if (!icalFilePath.isEmpty()) actionName = i18n("Schedules to iCalendar [%1]", icalFilePath); - d->m_action = actionCollection()->addAction("file_export_icalendar"); + const auto &kpartgui = QStringLiteral("file_export_icalendar"); + d->m_action = actionCollection()->addAction(kpartgui); d->m_action->setText(actionName); connect(d->m_action, &QAction::triggered, this, &iCalendarExporter::slotFirstExport); + connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action(qPrintable(kpartgui)), &QAction::setEnabled); } iCalendarExporter::~iCalendarExporter() diff --git a/kmymoney/plugins/icalendar/export/schedulestoicalendar.cpp b/kmymoney/plugins/icalendar/export/schedulestoicalendar.cpp --- a/kmymoney/plugins/icalendar/export/schedulestoicalendar.cpp +++ b/kmymoney/plugins/icalendar/export/schedulestoicalendar.cpp @@ -244,6 +244,9 @@ void KMMSchedulesToiCalendar::exportToFile(const QString& filePath, bool settingsChaged) { + if (!MyMoneyFile::instance()->storageAttached()) + return; + QFile icsFile(filePath); icsFile.open(QIODevice::ReadOnly); diff --git a/kmymoney/plugins/interfaces/kmmappinterface.h b/kmymoney/plugins/interfaces/kmmappinterface.h --- a/kmymoney/plugins/interfaces/kmmappinterface.h +++ b/kmymoney/plugins/interfaces/kmmappinterface.h @@ -63,8 +63,6 @@ void writeLastUsedFile(const QString& fileName) override; void slotFileOpenRecent(const QUrl &url) override; void addToRecentFiles(const QUrl& url) override; - void updateCaption(bool skipActions = false) override; - QTimer* autosaveTimer() override; KMyMoneyAppCallback progressCallback() override; void writeLastUsedDir(const QString &directory) override; QString readLastUsedDir() const override; diff --git a/kmymoney/plugins/interfaces/kmmappinterface.cpp b/kmymoney/plugins/interfaces/kmmappinterface.cpp --- a/kmymoney/plugins/interfaces/kmmappinterface.cpp +++ b/kmymoney/plugins/interfaces/kmmappinterface.cpp @@ -81,16 +81,6 @@ m_app->addToRecentFiles(url); } -void KMyMoneyPlugin::KMMAppInterface::updateCaption(bool skipActions) -{ - m_app->updateCaption(skipActions); -} - -QTimer* KMyMoneyPlugin::KMMAppInterface::autosaveTimer() -{ - return m_app->autosaveTimer(); -} - KMyMoneyAppCallback KMyMoneyPlugin::KMMAppInterface::progressCallback() { return m_app->progressCallback(); diff --git a/kmymoney/plugins/kmymoneyplugin.h b/kmymoney/plugins/kmymoneyplugin.h --- a/kmymoney/plugins/kmymoneyplugin.h +++ b/kmymoney/plugins/kmymoneyplugin.h @@ -48,6 +48,8 @@ namespace KMyMoneyPlugin { class StatementInterface; } namespace KMyMoneyPlugin { class ViewInterface; } +namespace eKMyMoney { enum class StorageType; } + /** * @defgroup KMyMoneyPlugin * @@ -295,7 +297,7 @@ * @param url URL of the file * @return true if successfully opened */ - virtual bool open(MyMoneyStorageMgr *storage, const QUrl &url) = 0; + virtual MyMoneyStorageMgr *open(const QUrl &url) = 0; /** * @brief Saves storage into file @@ -305,16 +307,17 @@ virtual bool save(const QUrl &url) = 0; /** - * @brief Returns storage reader - * @return storage reader + * @brief Saves storage into file + * @param url URL of the file + * @return true if successfully saved */ - virtual IMyMoneyOperationsFormat* reader(); + virtual bool saveAs() = 0; /** * @brief Storage identifier * @return Storage identifier */ - virtual QString formatName() const = 0; + virtual eKMyMoney::StorageType storageType() const = 0; virtual QString fileExtension() const = 0; }; diff --git a/kmymoney/plugins/kmymoneyplugin.cpp b/kmymoney/plugins/kmymoneyplugin.cpp --- a/kmymoney/plugins/kmymoneyplugin.cpp +++ b/kmymoney/plugins/kmymoneyplugin.cpp @@ -112,8 +112,3 @@ KMyMoneyPlugin::ImporterPlugin::~ImporterPlugin() { } - -IMyMoneyOperationsFormat* KMyMoneyPlugin::StoragePlugin::reader() -{ - return nullptr; -} diff --git a/kmymoney/plugins/ofx/import/ofximporter.cpp b/kmymoney/plugins/ofx/import/ofximporter.cpp --- a/kmymoney/plugins/ofx/import/ofximporter.cpp +++ b/kmymoney/plugins/ofx/import/ofximporter.cpp @@ -48,6 +48,7 @@ #include "mymoneystatement.h" #include "statementinterface.h" #include "importinterface.h" +#include "viewinterface.h" #include "ui_importoption.h" //#define DEBUG_LIBOFX @@ -109,9 +110,11 @@ void OFXImporter::createActions() { - QAction *action = actionCollection()->addAction("file_import_ofx"); - action->setText(i18n("OFX...")); - connect(action, &QAction::triggered, this, static_cast(&OFXImporter::slotImportFile)); + const auto &kpartgui = QStringLiteral("file_import_ofx"); + auto a = actionCollection()->addAction(kpartgui); + a->setText(i18n("OFX...")); + connect(a, &QAction::triggered, this, static_cast(&OFXImporter::slotImportFile)); + connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action(qPrintable(kpartgui)), &QAction::setEnabled); } void OFXImporter::slotImportFile() diff --git a/kmymoney/plugins/qif/export/qifexporter.cpp b/kmymoney/plugins/qif/export/qifexporter.cpp --- a/kmymoney/plugins/qif/export/qifexporter.cpp +++ b/kmymoney/plugins/qif/export/qifexporter.cpp @@ -33,6 +33,7 @@ #include "kexportdlg.h" #include "mymoneyqifwriter.h" +#include "viewinterface.h" QIFExporter::QIFExporter(QObject *parent, const QVariantList &args) : KMyMoneyPlugin::Plugin(parent, "qifexporter"/*must be the same as X-KDE-PluginInfo-Name*/) @@ -53,9 +54,11 @@ void QIFExporter::createActions() { - m_action = actionCollection()->addAction("file_export_qif"); + const auto &kpartgui = QStringLiteral("file_export_qif"); + m_action = actionCollection()->addAction(kpartgui); m_action->setText(i18n("QIF...")); connect(m_action, &QAction::triggered, this, &QIFExporter::slotQifExport); + connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action(qPrintable(kpartgui)), &QAction::setEnabled); } diff --git a/kmymoney/plugins/qif/import/qifimporter.cpp b/kmymoney/plugins/qif/import/qifimporter.cpp --- a/kmymoney/plugins/qif/import/qifimporter.cpp +++ b/kmymoney/plugins/qif/import/qifimporter.cpp @@ -34,6 +34,7 @@ #include "kimportdlg.h" #include "mymoneyqifreader.h" #include "statementinterface.h" +#include "viewinterface.h" class MyMoneyStatement; @@ -55,9 +56,11 @@ void QIFImporter::createActions() { - m_action = actionCollection()->addAction("file_import_qif"); + const auto &kpartgui = QStringLiteral("file_import_qif"); + m_action = actionCollection()->addAction(kpartgui); m_action->setText(i18n("QIF...")); connect(m_action, &QAction::triggered, this, &QIFImporter::slotQifImport); + connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action(qPrintable(kpartgui)), &QAction::setEnabled); } void QIFImporter::slotQifImport() diff --git a/kmymoney/plugins/sql/sqlstorage.h b/kmymoney/plugins/sql/sqlstorage.h --- a/kmymoney/plugins/sql/sqlstorage.h +++ b/kmymoney/plugins/sql/sqlstorage.h @@ -42,9 +42,10 @@ QAction *m_saveAsDBaction; QAction *m_generateDB; - bool open(MyMoneyStorageMgr *storage, const QUrl &url) override; + MyMoneyStorageMgr *open(const QUrl &url) override; bool save(const QUrl &url) override; - QString formatName() const override; + bool saveAs() override; + eKMyMoney::StorageType storageType() const override; QString fileExtension() const override; protected: @@ -60,11 +61,9 @@ * @retval true save operation was successful */ bool saveAsDatabase(const QUrl &url); - bool saveDatabase(const QUrl &url); private Q_SLOTS: void slotOpenDatabase(); - void slotSaveAsDatabase(); void slotGenerateSql(); }; diff --git a/kmymoney/plugins/sql/sqlstorage.cpp b/kmymoney/plugins/sql/sqlstorage.cpp --- a/kmymoney/plugins/sql/sqlstorage.cpp +++ b/kmymoney/plugins/sql/sqlstorage.cpp @@ -44,9 +44,10 @@ #include "mymoneyfile.h" #include "mymoneystoragesql.h" #include "mymoneyexception.h" -//#include "mymoneystoragemgr.h" +#include "mymoneystoragemgr.h" #include "icons.h" #include "kmymoneysettings.h" +#include "kmymoneyenums.h" using namespace Icons; @@ -66,8 +67,12 @@ qDebug("Plugins: sqlstorage unloaded"); } -bool SQLStorage::open(MyMoneyStorageMgr *storage, const QUrl &url) +MyMoneyStorageMgr *SQLStorage::open(const QUrl &url) { + if (url.scheme() != QLatin1String("sql")) + return nullptr; + + auto storage = new MyMoneyStorageMgr; auto reader = std::make_unique(storage, url); QUrl dbURL(url); @@ -81,10 +86,12 @@ KMessageBox::detailedError(nullptr, i18n("Cannot open database %1\n", dbURL.toDisplayString()), reader->lastError()); - return false; + delete storage; + return nullptr; case -1: // retryable error if (KMessageBox::warningYesNo(nullptr, reader->lastError(), PACKAGE) == KMessageBox::No) { - return false; + delete storage; + return nullptr; } else { QUrlQuery query(dbURL); const QString optionKey = QLatin1String("options"); @@ -110,25 +117,90 @@ i18n("An unrecoverable error occurred while reading the database"), reader->lastError().toLatin1(), i18n("Database malfunction")); - return false; + delete storage; + return nullptr; } // reader->setProgressCallback(0); - return true; + return storage; } bool SQLStorage::save(const QUrl &url) { - return saveDatabase(url); + auto rc = false; + if (!appInterface()->fileOpen()) { + KMessageBox::error(nullptr, i18n("Tried to access a file when it has not been opened")); + return (rc); + } + auto writer = new MyMoneyStorageSql(MyMoneyFile::instance()->storage(), url); + writer->open(url, QIODevice::WriteOnly); +// writer->setProgressCallback(&KMyMoneyView::progressCallback); + if (!writer->writeFile()) { + KMessageBox::detailedError(nullptr, + i18n("An unrecoverable error occurred while writing to the database.\n" + "It may well be corrupt."), + writer->lastError().toLatin1(), + i18n("Database malfunction")); + rc = false; + } else { + rc = true; + } + writer->setProgressCallback(0); + delete writer; + return rc; +} + +bool SQLStorage::saveAs() +{ + auto rc = false; + QUrl oldUrl; + // in event of it being a database, ensure that all data is read into storage for saveas + if (appInterface()->isDatabase()) + oldUrl = appInterface()->filenameURL().isEmpty() ? appInterface()->lastOpenedURL() : appInterface()->filenameURL(); + + QPointer dialog = new KSelectDatabaseDlg(QIODevice::WriteOnly); + QUrl url = oldUrl; + if (!dialog->checkDrivers()) { + delete dialog; + return rc; + } + + 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(nullptr, i18n("Cannot save to current database.")); + } else { + try { + rc = saveAsDatabase(url); + } catch (const MyMoneyException &e) { + KMessageBox::sorry(nullptr, i18n("Cannot save to current database: %1", QString::fromLatin1(e.what()))); + } + } + } + delete dialog; + + if (rc) { + //KRecentFilesAction *p = dynamic_cast(action("file_open_recent")); + //if(p) + appInterface()->addToRecentFiles(url); + appInterface()->writeLastUsedFile(url.toDisplayString(QUrl::PreferLocalFile)); + appInterface()->writeFilenameURL(url); + } + return rc; } -QString SQLStorage::formatName() const +eKMyMoney::StorageType SQLStorage::storageType() const { - return QStringLiteral("SQL"); + return eKMyMoney::StorageType::SQL; } QString SQLStorage::fileExtension() const { - return QString(); + return i18n("Database files (*.db *.sql)"); } void SQLStorage::createActions() @@ -138,11 +210,6 @@ m_openDBaction->setIcon(Icons::get(Icon::SVNUpdate)); connect(m_openDBaction, &QAction::triggered, this, &SQLStorage::slotOpenDatabase); - m_saveAsDBaction = actionCollection()->addAction("saveas_database"); - m_saveAsDBaction->setText(i18n("Save as database...")); - m_saveAsDBaction->setIcon(Icons::get(Icon::FileArchiver)); - connect(m_saveAsDBaction, &QAction::triggered, this, &SQLStorage::slotSaveAsDatabase); - m_generateDB = actionCollection()->addAction("tools_generate_sql"); m_generateDB->setText(i18n("Generate Database SQL")); connect(m_generateDB, &QAction::triggered, this, &SQLStorage::slotGenerateSql); @@ -197,51 +264,6 @@ delete dialog; } -void SQLStorage::slotSaveAsDatabase() -{ - bool rc = false; - QUrl oldUrl; - // in event of it being a database, ensure that all data is read into storage for saveas - if (appInterface()->isDatabase()) - oldUrl = appInterface()->filenameURL().isEmpty() ? appInterface()->lastOpenedURL() : appInterface()->filenameURL(); - - QPointer dialog = new KSelectDatabaseDlg(QIODevice::WriteOnly); - QUrl url = oldUrl; - if (!dialog->checkDrivers()) { - delete dialog; - return; - } - - 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(nullptr, i18n("Cannot save to current database.")); - } else { - try { - rc = saveAsDatabase(url); - } catch (const MyMoneyException &e) { - KMessageBox::sorry(nullptr, i18n("Cannot save to current database: %1", QString::fromLatin1(e.what()))); - } - } - } - delete dialog; - - if (rc) { - //KRecentFilesAction *p = dynamic_cast(action("file_open_recent")); - //if(p) - appInterface()->addToRecentFiles(url); - appInterface()->writeLastUsedFile(url.toDisplayString(QUrl::PreferLocalFile)); - } - appInterface()->autosaveTimer()->stop(); - appInterface()->updateCaption(); - return; -} - void SQLStorage::slotGenerateSql() { QPointer editor = new KGenerateSqlDlg(nullptr); @@ -272,7 +294,7 @@ } if (canWrite) { delete writer; - saveDatabase(url); + save(url); return true; } else { KMessageBox::detailedError(nullptr, @@ -284,31 +306,6 @@ } } -bool SQLStorage::saveDatabase(const QUrl &url) -{ - auto rc = false; - if (!appInterface()->fileOpen()) { - KMessageBox::error(nullptr, i18n("Tried to access a file when it has not been opened")); - return (rc); - } - auto writer = new MyMoneyStorageSql(MyMoneyFile::instance()->storage(), url); - writer->open(url, QIODevice::WriteOnly); -// writer->setProgressCallback(&KMyMoneyView::progressCallback); - if (!writer->writeFile()) { - KMessageBox::detailedError(nullptr, - i18n("An unrecoverable error occurred while writing to the database.\n" - "It may well be corrupt."), - writer->lastError().toLatin1(), - i18n("Database malfunction")); - rc = false; - } else { - rc = true; - } - writer->setProgressCallback(0); - delete writer; - return rc; -} - K_PLUGIN_FACTORY_WITH_JSON(SQLStorageFactory, "sqlstorage.json", registerPlugin();) #include "sqlstorage.moc" diff --git a/kmymoney/plugins/xml/CMakeLists.txt b/kmymoney/plugins/xml/CMakeLists.txt --- a/kmymoney/plugins/xml/CMakeLists.txt +++ b/kmymoney/plugins/xml/CMakeLists.txt @@ -32,9 +32,6 @@ kmymoney_common ) -install(FILES xmlstorage.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/xmlstorage") - # install(FILES kmymoney-xmlstorageplugin.desktop # DESTINATION ${SERVICETYPES_INSTALL_DIR} # ) diff --git a/kmymoney/plugins/xml/mymoneystoragexml.h b/kmymoney/plugins/xml/mymoneystoragexml.h --- a/kmymoney/plugins/xml/mymoneystoragexml.h +++ b/kmymoney/plugins/xml/mymoneystoragexml.h @@ -77,8 +77,11 @@ Writing = 1 /**< version to be used when writing a file */ }; -protected: - void setProgressCallback(void(*callback)(int, int, const QString&)) override; + void readFile(QIODevice* s, MyMoneyStorageMgr* storage) override; + void writeFile(QIODevice* s, MyMoneyStorageMgr* storage) override; + void setProgressCallback(void(*callback)(int, int, const QString&)) override; + + protected: void signalProgress(int current, int total, const QString& = ""); /** @@ -144,9 +147,6 @@ virtual QDomElement writeKeyValuePairs(const QMap pairs); - virtual void readFile(QIODevice* s, MyMoneyStorageMgr* storage) override; - virtual void writeFile(QIODevice* s, MyMoneyStorageMgr* storage) override; - bool readUserInformation(const QDomElement& userElement); void readPricePair(const QDomElement& pricePair); diff --git a/kmymoney/plugins/xml/xmlstorage.h b/kmymoney/plugins/xml/xmlstorage.h --- a/kmymoney/plugins/xml/xmlstorage.h +++ b/kmymoney/plugins/xml/xmlstorage.h @@ -43,9 +43,10 @@ QAction *m_saveAsXMLaction; - bool open(MyMoneyStorageMgr *storage, const QUrl &url) override; + MyMoneyStorageMgr *open(const QUrl &url) override; bool save(const QUrl &url) override; - QString formatName() const override; + bool saveAs() override; + eKMyMoney::StorageType storageType() const override; QString fileExtension() const override; private: @@ -70,10 +71,6 @@ void saveToLocalFile(const QString& localFile, IMyMoneyOperationsFormat* pWriter, bool plaintext, const QString& keyList); QString m_encryptionKeys; -private Q_SLOTS: - void slotSaveAsXML(); - - }; #endif diff --git a/kmymoney/plugins/xml/xmlstorage.cpp b/kmymoney/plugins/xml/xmlstorage.cpp --- a/kmymoney/plugins/xml/xmlstorage.cpp +++ b/kmymoney/plugins/xml/xmlstorage.cpp @@ -45,6 +45,7 @@ #include "appinterface.h" #include "viewinterface.h" #include "mymoneyfile.h" +#include "mymoneystoragemgr.h" #include "mymoneyexception.h" #include "mymoneystoragebin.h" #include "mymoneystoragexml.h" @@ -54,6 +55,7 @@ #include "kmymoneyutils.h" #include "kgpgfile.h" #include "kgpgkeyselectiondlg.h" +#include "kmymoneyenums.h" using namespace Icons; @@ -66,8 +68,6 @@ { Q_UNUSED(args) setComponentName("xmlstorage", i18n("XML storage")); - setXMLFile("xmlstorage.rc"); - createActions(); // For information, announce that we have been loaded. qDebug("Plugins: xmlstorage loaded"); } @@ -77,10 +77,10 @@ qDebug("Plugins: xmlstorage unloaded"); } -bool XMLStorage::open(MyMoneyStorageMgr *storage, const QUrl &url) +MyMoneyStorageMgr *XMLStorage::open(const QUrl &url) { - if (!url.isValid()) - throw MYMONEYEXCEPTION(QString::fromLatin1("Invalid URL %1").arg(qPrintable(url.url()))); + if (url.scheme() == QLatin1String("sql")) + return nullptr; QString fileName; auto downloadedFile = false; @@ -187,27 +187,20 @@ else ungetString(qfile, qbaFileHeader.data(), 70); - IMyMoneyOperationsFormat* pReader = nullptr; QRegExp kmyexp(""); QByteArray txt(qbaFileHeader, 70); - if (kmyexp.indexIn(txt) != -1) { - pReader = new MyMoneyStorageXML; - } else { - throw MYMONEYEXCEPTION(QString::fromLatin1("%1").arg(i18n("File %1 contains an unknown file format.", fileName))); - } - - // disconnect the current storga manager from the engine - MyMoneyFile::instance()->detachStorage(); + if (kmyexp.indexIn(txt) == -1) + return nullptr; // attach the storage before reading the file, since the online // onlineJobAdministration object queries the engine during // loading. - MyMoneyFile::instance()->attachStorage(storage); - pReader->setProgressCallback(appInterface()->progressCallback()); - pReader->readFile(qfile, storage); - pReader->setProgressCallback(0); - delete pReader; + auto storage = new MyMoneyStorageMgr; + MyMoneyStorageXML pReader; + pReader.setProgressCallback(appInterface()->progressCallback()); + pReader.readFile(qfile, storage); + pReader.setProgressCallback(0); qfile->close(); delete qfile; @@ -218,13 +211,19 @@ if (downloadedFile) QFile::remove(fileName); - // 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", KMyMoneySettings::gpgRecipientList().join(",")); - ft.commit(); - return true; + if (isEncrypted) { + MyMoneyFile::instance()->attachStorage(storage); + if (MyMoneyFile::instance()->value("kmm-encryption-key").isEmpty()) { + // encapsulate transactions to the engine to be able to commit/rollback + MyMoneyFileTransaction ft; + MyMoneyFile::instance()->setValue("kmm-encryption-key", KMyMoneySettings::gpgRecipientList().join(",")); + ft.commit(); + } + MyMoneyFile::instance()->detachStorage(); + } + + return storage; } bool XMLStorage::save(const QUrl &url) @@ -249,7 +248,7 @@ QString keyList; if (!appInterface()->filenameURL().isEmpty()) keyList = MyMoneyFile::instance()->value("kmm-encryption-key"); - else + if (keyList.isEmpty()) keyList = m_encryptionKeys; // actually, url should be the parameter to this function @@ -268,8 +267,9 @@ KBackup::numberedBackupFile(filename, QString(), QStringLiteral("~"), nbak); } saveToLocalFile(filename, storageWriter.get(), plaintext, keyList); - } catch (const MyMoneyException &) { - throw MYMONEYEXCEPTION(QString::fromLatin1("Unable to write changes to '%1'").arg(filename)); + } catch (const MyMoneyException &e) { + qWarning("Unable to write changes to: %s\nReason: %s", qPrintable(filename), e.what()); + throw; } } else { @@ -295,22 +295,92 @@ return rc; } -QString XMLStorage::formatName() const +bool XMLStorage::saveAs() { - return QStringLiteral("XML"); + auto rc = false; + QStringList m_additionalGpgKeys; + m_encryptionKeys.clear(); + + QString selectedKeyName; + if (KGPGFile::GPGAvailable() && KMyMoneySettings::writeDataEncrypted()) { + // fill the secret key list and combo box + QStringList keyList; + KGPGFile::secretKeyList(keyList); + + QPointer dlg = new KGpgKeySelectionDlg(nullptr); + dlg->setSecretKeys(keyList, KMyMoneySettings::gpgRecipient()); + dlg->setAdditionalKeys(KMyMoneySettings::gpgRecipientList()); + rc = dlg->exec(); + if ((rc == QDialog::Accepted) && (dlg != 0)) { + m_additionalGpgKeys = dlg->additionalKeys(); + selectedKeyName = dlg->secretKey(); + } + delete dlg; + if (rc != QDialog::Accepted) { + return rc; + } + } + + QString prevDir; // don't prompt file name if not a native file + if (appInterface()->isNativeFile()) + prevDir = appInterface()->readLastUsedDir(); + + QPointer dlg = + new QFileDialog(nullptr, 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); + + if (dlg->exec() == QDialog::Accepted && dlg != 0) { + QUrl newURL = dlg->selectedUrls().first(); + if (!newURL.fileName().isEmpty()) { + appInterface()->consistencyCheck(false); + 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); + + // 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 = save(newURL); + else { + appInterface()->writeFilenameURL(newURL); + QRegExp keyExp(".* \\((.*)\\)"); + if (keyExp.indexIn(selectedKeyName) != -1) { + m_encryptionKeys = keyExp.cap(1); + if (!m_additionalGpgKeys.isEmpty()) { + if (!m_encryptionKeys.isEmpty()) + m_encryptionKeys.append(QLatin1Char(',')); + m_encryptionKeys.append(m_additionalGpgKeys.join(QLatin1Char(','))); + } + } + rc = save(newURL); + appInterface()->addToRecentFiles(newURL); + //write the directory used for this file as the default one for next time. + appInterface()->writeLastUsedDir(newURL.toDisplayString(QUrl::RemoveFilename | QUrl::PreferLocalFile | QUrl::StripTrailingSlash)); + appInterface()->writeLastUsedFile(newName); + } + } + } + (*appInterface()->progressCallback())(0,0, i18nc("Application is ready to use", "Ready.")); + delete dlg; + return rc; } -QString XMLStorage::fileExtension() const +eKMyMoney::StorageType XMLStorage::storageType() const { - return QString(); + return eKMyMoney::StorageType::XML; } -void XMLStorage::createActions() +QString XMLStorage::fileExtension() const { - m_saveAsXMLaction = actionCollection()->addAction("saveas_xml"); - m_saveAsXMLaction->setText(i18n("Save as XML...")); - m_saveAsXMLaction->setIcon(Icons::get(Icon::FileArchiver)); - connect(m_saveAsXMLaction, &QAction::triggered, this, &XMLStorage::slotSaveAsXML); + return i18n("KMyMoney files (*.kmy *.xml)"); } void XMLStorage::ungetString(QIODevice *qfile, char *buf, int len) @@ -433,85 +503,6 @@ pWriter->setProgressCallback(0); } -void XMLStorage::slotSaveAsXML() -{ - bool rc = false; - QStringList m_additionalGpgKeys; - m_encryptionKeys.clear(); - - QString selectedKeyName; - if (KGPGFile::GPGAvailable() && KMyMoneySettings::writeDataEncrypted()) { - // fill the secret key list and combo box - QStringList keyList; - KGPGFile::secretKeyList(keyList); - - QPointer dlg = new KGpgKeySelectionDlg(nullptr); - dlg->setSecretKeys(keyList, KMyMoneySettings::gpgRecipient()); - dlg->setAdditionalKeys(KMyMoneySettings::gpgRecipientList()); - rc = dlg->exec(); - if ((rc == QDialog::Accepted) && (dlg != 0)) { - m_additionalGpgKeys = dlg->additionalKeys(); - selectedKeyName = dlg->secretKey(); - } - delete dlg; - if (rc != QDialog::Accepted) { - return; - } - } - - QString prevDir; // don't prompt file name if not a native file - if (appInterface()->isNativeFile()) - prevDir = appInterface()->readLastUsedDir(); - - QPointer dlg = - new QFileDialog(nullptr, 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); - - if (dlg->exec() == QDialog::Accepted && dlg != 0) { - QUrl newURL = dlg->selectedUrls().first(); - if (!newURL.fileName().isEmpty()) { - appInterface()->consistencyCheck(false); - 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); - appInterface()->addToRecentFiles(newURL); - - // 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 = save(newURL); - else { - appInterface()->writeFilenameURL(newURL); - QRegExp keyExp(".* \\((.*)\\)"); - if (keyExp.indexIn(selectedKeyName) != -1) { - m_encryptionKeys = keyExp.cap(1); - if (!m_additionalGpgKeys.isEmpty()) { - if (!m_encryptionKeys.isEmpty()) - m_encryptionKeys.append(QLatin1Char(',')); - m_encryptionKeys.append(m_additionalGpgKeys.join(QLatin1Char(','))); - } - } - rc = save(newURL); - //write the directory used for this file as the default one for next time. - appInterface()->writeLastUsedDir(newURL.toDisplayString(QUrl::RemoveFilename | QUrl::PreferLocalFile | QUrl::StripTrailingSlash)); - appInterface()->writeLastUsedFile(newName); - } - appInterface()->autosaveTimer()->stop(); - } - } - (*appInterface()->progressCallback())(0,0, i18nc("Application is ready to use", "Ready.")); - delete dlg; - appInterface()->updateCaption(); -} - K_PLUGIN_FACTORY_WITH_JSON(XMLStorageFactory, "xmlstorage.json", registerPlugin();) #include "xmlstorage.moc" diff --git a/kmymoney/plugins/xml/xmlstorage.rc b/kmymoney/plugins/xml/xmlstorage.rc deleted file mode 100644 --- a/kmymoney/plugins/xml/xmlstorage.rc +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/kmymoney/views/kaccountsview.cpp b/kmymoney/views/kaccountsview.cpp --- a/kmymoney/views/kaccountsview.cpp +++ b/kmymoney/views/kaccountsview.cpp @@ -128,25 +128,6 @@ const auto file = MyMoneyFile::instance(); - if (d->m_onlinePlugins) { - QList accList; - file->accountList(accList); - QList::const_iterator it_a; - auto 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).hasOnlineMapping()) { - // check if provider is available - it_p = d->m_onlinePlugins->constFind((*it_a).onlineBankingSettings().value("provider").toLower()); - if (it_p != d->m_onlinePlugins->constEnd()) { - QStringList protocols; - (*it_p)->protocols(protocols); - if (!protocols.isEmpty()) - pActions[eMenu::Action::UpdateAllAccounts]->setEnabled(true); - } - } - } - } - if (typeid(obj) != typeid(MyMoneyAccount) && (obj.id().isEmpty() && d->m_currentAccount.id().isEmpty())) // do not disable actions that were already disabled) return; @@ -157,7 +138,8 @@ eMenu::Action::NewAccount, eMenu::Action::EditAccount, eMenu::Action::DeleteAccount, eMenu::Action::CloseAccount, eMenu::Action::ReopenAccount, eMenu::Action::ChartAccountBalance, - eMenu::Action::UnmapOnlineAccount, eMenu::Action::MapOnlineAccount, eMenu::Action::UpdateAccount + eMenu::Action::UnmapOnlineAccount, eMenu::Action::MapOnlineAccount, + eMenu::Action::UpdateAccount }; for (const auto& a : actionsToBeDisabled) diff --git a/kmymoney/views/kgloballedgerview.cpp b/kmymoney/views/kgloballedgerview.cpp --- a/kmymoney/views/kgloballedgerview.cpp +++ b/kmymoney/views/kgloballedgerview.cpp @@ -127,6 +127,10 @@ } break; + case eView::Action::DisableViewDepenedendActions: + pActions[Action::SelectAllTransactions]->setEnabled(false); + break; + default: break; } @@ -183,6 +187,7 @@ emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions); } + pActions[Action::SelectAllTransactions]->setEnabled(true); // don't forget base class implementation QWidget::showEvent(event); } @@ -264,7 +269,7 @@ Action::CancelTransaction, Action::DeleteTransaction, Action::MatchTransaction, Action::AcceptTransaction, Action::DuplicateTransaction, Action::ToggleReconciliationFlag, Action::MarkCleared, Action::GoToAccount, Action::GoToPayee, Action::AssignTransactionsNumber, Action::NewScheduledTransaction, - Action::CombineTransactions, Action::SelectAllTransactions, Action::CopySplits, + Action::CombineTransactions, Action::CopySplits, }; for (const auto& a : actionsToBeDisabled) @@ -279,7 +284,6 @@ pMenus[Menu::MarkTransaction]->setEnabled(false); pMenus[Menu::MarkTransactionContext]->setEnabled(false); - pActions[Action::SelectAllTransactions]->setEnabled(true); if (!d->m_selectedTransactions.isEmpty() && !d->m_selectedTransactions.first().isScheduled()) { // enable 'delete transaction' only if at least one of the // selected transactions does not reference a closed account @@ -669,6 +673,10 @@ if (variant.count() == 2) slotLedgerSelected(variant.at(0).toString(), variant.at(1).toString()); break; + case eView::Intent::SelectRegisterTransactions: + if (variant.count() == 1) + updateLedgerActions(variant.at(0).value()); + break; default: break; } diff --git a/kmymoney/views/khomeview.cpp b/kmymoney/views/khomeview.cpp --- a/kmymoney/views/khomeview.cpp +++ b/kmymoney/views/khomeview.cpp @@ -58,6 +58,7 @@ void KHomeView::executeCustomAction(eView::Action action) { + Q_D(KHomeView); switch(action) { case eView::Action::Refresh: refresh(); @@ -67,6 +68,10 @@ slotPrintView(); break; + case eView::Action::CleanupBeforeFileClose: + d->m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); + break; + default: break; } diff --git a/kmymoney/views/khomeview_p.h b/kmymoney/views/khomeview_p.h --- a/kmymoney/views/khomeview_p.h +++ b/kmymoney/views/khomeview_p.h @@ -418,8 +418,9 @@ m_view->setZoomFactor(KMyMoneySettings::zoomFactor()); QList list; - MyMoneyFile::instance()->accountList(list); - if (list.count() == 0) { + if (MyMoneyFile::instance()->storage()) + MyMoneyFile::instance()->accountList(list); + if (list.isEmpty()) { m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); } else { //clear the forecast flag so it will be reloaded diff --git a/kmymoney/views/kmymoneyview.h b/kmymoney/views/kmymoneyview.h --- a/kmymoney/views/kmymoneyview.h +++ b/kmymoney/views/kmymoneyview.h @@ -44,7 +44,6 @@ namespace eAccountsModel { enum class Column; } namespace eMenu { enum class Action; } namespace KMyMoneyPlugin { class OnlinePlugin; } -namespace KMyMoneyPlugin { class StoragePlugin; } namespace eDialogs { enum class ScheduleResultCode; } namespace eView { enum class Intent; } namespace eView { enum class Action; } @@ -91,40 +90,15 @@ class KMyMoneyView : public KPageWidget { Q_OBJECT -public: - // file actions for plugin - enum fileActions { - preOpen, postOpen, preSave, postSave, preClose, postClose - }; - private: - enum menuID { - AccountNew = 1, - AccountOpen, - AccountReconcile, - AccountEdit, - AccountDelete, - AccountOnlineMap, - AccountOnlineUpdate, - AccountOfxConnect, - CategoryNew - }; - - enum storageTypeE { - Memory = 0, - Database - } _storageType; KPageWidgetModel* m_model; QHash viewFrames; QHash viewBases; KMyMoneyTitleLabel* m_header; - QMap* m_storagePlugins; - -private: void viewAccountList(const QString& selectAccount); // Show the accounts view void createSchedule(MyMoneySchedule s, MyMoneyAccount& a); @@ -174,7 +148,6 @@ void slotAccountTreeViewChanged(const eAccountsModel::Column column, const bool show); void setOnlinePlugins(QMap& plugins); - void setStoragePlugins(QMap& plugins); // TODO: remove that function /** @@ -297,36 +270,22 @@ */ void slotContextMenuRequested(const MyMoneyObject& obj); -protected Q_SLOTS: - /** - * eventually replace this with KMyMoneyApp::slotCurrencySetBase(). - * it contains the same code - * - * @deprecated - */ - void slotSetBaseCurrency(const MyMoneySecurity& baseCurrency); - private: /** * Internal method used by slotAccountNew() and slotAccountCategory(). */ void accountNew(const bool createCategory); - void resetViewSelection(const View); + void resetViewSelection(); Q_SIGNALS: /** * This signal is emitted whenever a view is selected. * The parameter @p view is identified as one of KMyMoneyView::viewID. */ void viewActivated(View 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); diff --git a/kmymoney/views/kmymoneyview.cpp b/kmymoney/views/kmymoneyview.cpp --- a/kmymoney/views/kmymoneyview.cpp +++ b/kmymoney/views/kmymoneyview.cpp @@ -76,13 +76,15 @@ #include "securitiesmodel.h" #include "icons.h" #include "amountedit.h" +#include "onlinejobadministration.h" #include "kmymoneyaccounttreeview.h" #include "accountsviewproxymodel.h" #include "mymoneyprice.h" #include "mymoneyschedule.h" #include "mymoneysplit.h" #include "mymoneyaccount.h" #include "mymoneyinstitution.h" +#include "mymoneytag.h" #include "kmymoneyedit.h" #include "mymoneyfile.h" #include "mymoneysecurity.h" @@ -98,8 +100,7 @@ KMyMoneyView::KMyMoneyView() : KPageWidget(nullptr), - m_header(0), - m_storagePlugins(nullptr) + m_header(0) { // 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 @@ -201,10 +202,14 @@ static_cast(viewBases[View::NewLedgers])->openFavoriteLedgers(); #endif switchToDefaultView(); + slotObjectSelected(MyMoneyAccount()); // in order to enable update all accounts on file reload } void KMyMoneyView::slotFileClosed() { + slotShowHomePage(); + if (viewBases.contains(View::Home)) + viewBases[View::Home]->executeCustomAction(eView::Action::CleanupBeforeFileClose); if (viewBases.contains(View::Reports)) viewBases[View::Reports]->executeCustomAction(eView::Action::CleanupBeforeFileClose); @@ -215,7 +220,10 @@ #ifdef ENABLE_UNFINISHEDFEATURES static_cast(viewBases[View::NewLedgers])->closeLedgers(); #endif - slotShowHomePage(); + + pActions[eMenu::Action::Print]->setEnabled(false); + pActions[eMenu::Action::AccountCreditTransfer]->setEnabled(false); + pActions[eMenu::Action::UpdateAllAccounts]->setEnabled(false); } void KMyMoneyView::slotShowHomePage() @@ -374,11 +382,6 @@ viewBases[View::OnlineJobOutbox]->slotSelectByVariant(QVariantList {QVariant::fromValue(static_cast(&plugins))}, eView::Intent::SetOnlinePlugins); } -void KMyMoneyView::setStoragePlugins(QMap& plugins) -{ - m_storagePlugins = &plugins; -} - eDialogs::ScheduleResultCode KMyMoneyView::enterSchedule(MyMoneySchedule& schedule, bool autoEnter, bool extendedKeys) { return static_cast(viewBases[View::Schedules])->enterSchedule(schedule, autoEnter, extendedKeys); @@ -500,14 +503,14 @@ return; setCurrentPage(viewFrames[idView]); - pActions[eMenu::Action::Print]->setEnabled(canPrint()); - emit aboutToChangeView(); + resetViewSelection(); } bool KMyMoneyView::canPrint() { - return ((viewFrames.contains(View::Reports) && viewFrames[View::Reports] == currentPage()) || - (viewFrames.contains(View::Home) && viewFrames[View::Home] == currentPage()) + return (MyMoneyFile::instance()->storageAttached() && + ((viewFrames.contains(View::Reports) && viewFrames[View::Reports] == currentPage()) || + (viewFrames.contains(View::Home) && viewFrames[View::Home] == currentPage())) ); } @@ -550,30 +553,6 @@ static_cast(viewBases[View::Ledgers])->slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); } -void KMyMoneyView::slotSetBaseCurrency(const MyMoneySecurity& baseCurrency) -{ - if (!baseCurrency.id().isEmpty()) { - QString baseId; - try { - baseId = MyMoneyFile::instance()->baseCurrency().id(); - } catch (const MyMoneyException &e) { - qDebug("%s", 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(), QString::fromLatin1(e.what())), i18n("Set base currency")); - } - } - AmountEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); - KMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); - } -} - void KMyMoneyView::viewAccountList(const QString& /*selectAccount*/) { if (viewFrames[View::Accounts] != currentPage()) @@ -604,17 +583,23 @@ if (m_header) m_header->setText(m_model->data(current, KPageModel::HeaderRole).toString()); + const auto view = currentPage(); // remember the selected view if there is a real change if (previous.isValid()) { - const KPageWidgetItem* view = currentPage(); QHash::const_iterator it; for(it = viewFrames.cbegin(); it != viewFrames.cend(); ++it) { if ((*it) == view) { emit viewActivated(it.key()); break; } } } + + if (viewBases.contains(View::Ledgers) && view != viewFrames.value(View::Ledgers)) + viewBases[View::Ledgers]->executeCustomAction(eView::Action::DisableViewDepenedendActions); + + pActions[eMenu::Action::Print]->setEnabled(canPrint()); + pActions[eMenu::Action::AccountCreditTransfer]->setEnabled(onlineJobAdministration::instance()->canSendCreditTransfer()); } void KMyMoneyView::createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount) @@ -668,9 +653,15 @@ viewBases[View::Home]->executeCustomAction(eView::Action::Print); } -void KMyMoneyView::resetViewSelection(const View) +void KMyMoneyView::resetViewSelection() { - emit aboutToChangeView(); + if (!MyMoneyFile::instance()->storageAttached()) + return; + slotObjectSelected(MyMoneyAccount()); + slotObjectSelected(MyMoneyInstitution()); + slotObjectSelected(MyMoneySchedule()); + slotObjectSelected(MyMoneyTag()); + slotSelectByVariant(QVariantList {QVariant::fromValue(KMyMoneyRegister::SelectedTransactions())}, eView::Intent::SelectRegisterTransactions); } void KMyMoneyView::slotOpenObjectRequested(const MyMoneyObject& obj) @@ -786,8 +777,11 @@ break; case eView::Intent::SelectRegisterTransactions: - if (variant.count() == 1) + if (variant.count() == 1) { emit transactionsSelected(variant.at(0).value()); // for plugins + if (viewBases.contains(View::Ledgers)) + viewBases[View::Ledgers]->slotSelectByVariant(variant, intent); + } break; case eView::Intent::AccountReconciled: @@ -808,7 +802,7 @@ { switch (action) { case eView::Action::AboutToShow: - emit aboutToChangeView(); + resetViewSelection(); break; case eView::Action::SwitchView: showPage(view); diff --git a/kmymoney/views/viewenums.h b/kmymoney/views/viewenums.h --- a/kmymoney/views/viewenums.h +++ b/kmymoney/views/viewenums.h @@ -65,7 +65,8 @@ EditInstitution, EditSchedule, CleanupBeforeFileClose, - InitializeAfterFileOpen + InitializeAfterFileOpen, + DisableViewDepenedendActions }; } diff --git a/kmymoney/wizards/newuserwizard/CMakeLists.txt b/kmymoney/wizards/newuserwizard/CMakeLists.txt --- a/kmymoney/wizards/newuserwizard/CMakeLists.txt +++ b/kmymoney/wizards/newuserwizard/CMakeLists.txt @@ -5,15 +5,14 @@ kaccountpage.cpp kcategoriespage.cpp kcurrencypage.cpp - kfilepage.cpp kgeneralpage.cpp kintropage.cpp kpreferencepage.cpp ) set (libnewuserwizard_a_UI kaccountpage.ui kcurrencypage.ui - kfilepage.ui kgeneralpage.ui + kgeneralpage.ui kintropage.ui kpreferencepage.ui kpasswordpage.ui ) diff --git a/kmymoney/wizards/newuserwizard/kcurrencypage.cpp b/kmymoney/wizards/newuserwizard/kcurrencypage.cpp --- a/kmymoney/wizards/newuserwizard/kcurrencypage.cpp +++ b/kmymoney/wizards/newuserwizard/kcurrencypage.cpp @@ -69,7 +69,7 @@ QList::const_iterator it; QString localCurrency(QLocale().currencySymbol(QLocale::CurrencyIsoCode)); - QString baseCurrency = MyMoneyFile::instance()->baseCurrency().id(); + QString baseCurrency = MyMoneyFile::instance()->storageAttached() ? MyMoneyFile::instance()->baseCurrency().id() : QString(); ui->m_currencyList->clear(); diff --git a/kmymoney/wizards/newuserwizard/kfilepage.h b/kmymoney/wizards/newuserwizard/kfilepage.h deleted file mode 100644 --- a/kmymoney/wizards/newuserwizard/kfilepage.h +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************** - kfilepage.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 KFILEPAGE_H -#define KFILEPAGE_H - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "wizardpage.h" - -namespace NewUserWizard -{ - class Wizard; - /** - * Wizard page to allow selecting the filename - * - * @author Thomas Baumgart - */ - class FilePagePrivate; - class FilePage : public QWidget, public WizardPage - { - Q_OBJECT - Q_DISABLE_COPY(FilePage) - - public: - explicit FilePage(Wizard* parent); - ~FilePage() override; - - bool isComplete() const override; - - private: - Q_DECLARE_PRIVATE_D(WizardPage::d_ptr, FilePage) - friend class Wizard; - }; - -} // namespace - -#endif diff --git a/kmymoney/wizards/newuserwizard/kfilepage.cpp b/kmymoney/wizards/newuserwizard/kfilepage.cpp deleted file mode 100644 --- a/kmymoney/wizards/newuserwizard/kfilepage.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/*************************************************************************** - kfilepage.cpp - ------------------- - begin : Sat Feb 18 2006 - copyright : (C) 2006 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 "kfilepage.h" -#include "kfilepage_p.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "ui_kfilepage.h" -#include "knewuserwizard.h" -#include "kguiutils.h" - -namespace NewUserWizard -{ - FilePage::FilePage(Wizard* wizard) : - QWidget(wizard), - WizardPage(*new FilePagePrivate(wizard), stepCount++, this, wizard) - { - Q_D(FilePage); - d->ui->setupUi(this); - d->m_mandatoryGroup->add(d->ui->m_dataFileEdit->lineEdit()); - connect(d->m_mandatoryGroup, static_cast(&KMandatoryFieldGroup::stateChanged), object(), &KMyMoneyWizardPagePrivate::completeStateChanged); - - KUser user; - QString folder = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - if (folder.isEmpty() || !QDir().exists(folder)) - folder = QDir::homePath(); - d->ui->m_dataFileEdit->setStartDir(QUrl::fromLocalFile(folder)); - d->ui->m_dataFileEdit->setUrl(QUrl::fromLocalFile(folder + QLatin1Char('/') + user.loginName() + QLatin1String(".kmy"))); - d->ui->m_dataFileEdit->setFilter(i18n("*.kmy *.xml|KMyMoney files (*.kmy *.xml);;*|All files (*)")); - d->ui->m_dataFileEdit->setMode(KFile::File); - } - - FilePage::~FilePage() - { - } - - bool FilePage::isComplete() const - { - Q_D(const FilePage); - //! @todo Allow to overwrite files - bool rc = d->m_mandatoryGroup->isEnabled(); - d->ui->m_existingFileLabel->hide(); - d->ui->m_finishLabel->show(); - if (rc) { - // if a filename is present, check that - // a) the file does not exist - // b) the directory does exist - // c) the file is stored locally (because we cannot check previous conditions if it is not) - const QUrl fullPath = d->ui->m_dataFileEdit->url(); - QFileInfo directory{fullPath.adjusted(QUrl::RemoveFilename).toLocalFile()}; - qDebug() << "Selected fileptah: " << fullPath << " " << directory.absoluteFilePath() << " dir: " << directory.isDir(); - rc = false; - if (!fullPath.isValid() || !fullPath.isLocalFile()) { - d->ui->m_dataFileEdit->setToolTip(i18n("The path has to be valid and cannot be on a remote location.")); - } else if (QFileInfo::exists(fullPath.toLocalFile())) { - d->ui->m_dataFileEdit->setToolTip(i18n("The file exists already. Please create a new file.")); - } else if (!directory.isDir()) { - d->ui->m_dataFileEdit->setToolTip(i18n("The destination directory does not exist or cannot be written to.")); - } else { - d->ui->m_dataFileEdit->setToolTip(""); - rc = true; - } - - d->ui->m_existingFileLabel->setHidden(rc); - d->ui->m_finishLabel->setVisible(rc); - } - return rc; - } - -} diff --git a/kmymoney/wizards/newuserwizard/kfilepage.ui b/kmymoney/wizards/newuserwizard/kfilepage.ui deleted file mode 100644 --- a/kmymoney/wizards/newuserwizard/kfilepage.ui +++ /dev/null @@ -1,92 +0,0 @@ - - - KFilePage - - - - 0 - 0 - 602 - 350 - - - - - - - Qt::NoFocus - - - KMyMoney will store your financial data in a file on the disk. A standard filename within your user environment will be the default. This is just provided for convenience and you can choose any other location here. - - - true - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 30 - - - - - - - - Either the currently selected file exists or the selected directory does not exist. Please make sure, that - -<ul> -<li>the selected directory exists and</li> -<li>the filename is not currently used in this directory.</li> -</ul> - - - Qt::RichText - - - true - - - - - - - Qt::NoFocus - - - This finishes the setup of your KMyMoney environment. You can now press the Finish button and start using KMyMoney to record your financial transactions. - - - Qt::RichText - - - true - - - - - - - - - KUrlRequester - QFrame -
kurlrequester.h
-
-
- - -
diff --git a/kmymoney/wizards/newuserwizard/kfilepage_p.h b/kmymoney/wizards/newuserwizard/kfilepage_p.h deleted file mode 100644 --- a/kmymoney/wizards/newuserwizard/kfilepage_p.h +++ /dev/null @@ -1,57 +0,0 @@ -/*************************************************************************** - kfilepage_p.h - ------------------- - begin : Sat Feb 18 2006 - copyright : (C) 2006 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 KFILEPAGE_P_H -#define KFILEPAGE_P_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "ui_kfilepage.h" -#include "wizardpage_p.h" - -namespace NewUserWizard -{ - class Wizard; - - class FilePagePrivate : public WizardPagePrivate - { - Q_DISABLE_COPY(FilePagePrivate) - - public: - explicit FilePagePrivate(QObject* parent) : - WizardPagePrivate(parent), - ui(new Ui::KFilePage) - { - } - - ~FilePagePrivate() - { - delete ui; - } - - Ui::KFilePage *ui; - }; -} -#endif diff --git a/kmymoney/wizards/newuserwizard/knewuserwizard.h b/kmymoney/wizards/newuserwizard/knewuserwizard.h --- a/kmymoney/wizards/newuserwizard/knewuserwizard.h +++ b/kmymoney/wizards/newuserwizard/knewuserwizard.h @@ -23,7 +23,6 @@ // QT Includes #include -#include // ---------------------------------------------------------------------------- // Project Includes @@ -59,7 +58,6 @@ friend class AccountPage; friend class CategoriesPage; friend class PreferencePage; - friend class FilePage; Q_OBJECT Q_DISABLE_COPY(Wizard) @@ -72,11 +70,6 @@ */ MyMoneyPayee user() const; - /** - * Returns the URL that the user has chosen to store the file - */ - QUrl url() const; - /** * Returns the information about an institution if entered by * the user. If the name field is empty, then he did not enter diff --git a/kmymoney/wizards/newuserwizard/knewuserwizard.cpp b/kmymoney/wizards/newuserwizard/knewuserwizard.cpp --- a/kmymoney/wizards/newuserwizard/knewuserwizard.cpp +++ b/kmymoney/wizards/newuserwizard/knewuserwizard.cpp @@ -37,8 +37,6 @@ #include "kaccountpage_p.h" #include "kcategoriespage.h" #include "kcurrencypage.h" -#include "kfilepage.h" -#include "kfilepage_p.h" #include "kgeneralpage.h" #include "kintropage.h" #include "kpreferencepage.h" @@ -73,7 +71,6 @@ addStep(i18n("Personal Data")); addStep(i18n("Select Currency")); addStep(i18n("Select Accounts")); - addStep(i18n("Set preferences")); addStep(i18nc("Finish the wizard", "Finish")); if (isFirstTime) @@ -83,7 +80,6 @@ d->m_accountPage = new AccountPage(this); d->m_categoriesPage = new CategoriesPage(this); d->m_preferencePage = new PreferencePage(this); - d->m_filePage = new FilePage(this); d->m_accountPage->d_func()->ui->m_haveCheckingAccountButton->setChecked(true); if (isFirstTime) @@ -104,12 +100,6 @@ return d->m_generalPage->user(); } - QUrl Wizard::url() const - { - Q_D(const Wizard); - return d->m_filePage->d_func()->ui->m_dataFileEdit->url(); - } - MyMoneyInstitution Wizard::institution() const { Q_D(const Wizard); diff --git a/kmymoney/wizards/newuserwizard/knewuserwizard_p.h b/kmymoney/wizards/newuserwizard/knewuserwizard_p.h --- a/kmymoney/wizards/newuserwizard/knewuserwizard_p.h +++ b/kmymoney/wizards/newuserwizard/knewuserwizard_p.h @@ -36,7 +36,6 @@ class AccountPage; class CategoriesPage; class PreferencePage; - class FilePage; class WizardPrivate : public KMyMoneyWizardPrivate { @@ -50,8 +49,7 @@ m_currencyPage(nullptr), m_accountPage(nullptr), m_categoriesPage(nullptr), - m_preferencePage(nullptr), - m_filePage(nullptr) + m_preferencePage(nullptr) { } @@ -66,7 +64,6 @@ AccountPage* m_accountPage; CategoriesPage* m_categoriesPage; PreferencePage* m_preferencePage; - FilePage* m_filePage; }; } // namespace diff --git a/kmymoney/wizards/newuserwizard/kpreferencepage.h b/kmymoney/wizards/newuserwizard/kpreferencepage.h --- a/kmymoney/wizards/newuserwizard/kpreferencepage.h +++ b/kmymoney/wizards/newuserwizard/kpreferencepage.h @@ -49,8 +49,6 @@ explicit PreferencePage(Wizard* parent); ~PreferencePage() override; - KMyMoneyWizardPage* nextPage() const override; - private: Q_DECLARE_PRIVATE_D(WizardPage::d_ptr, PreferencePage) friend class Wizard; diff --git a/kmymoney/wizards/newuserwizard/kpreferencepage.cpp b/kmymoney/wizards/newuserwizard/kpreferencepage.cpp --- a/kmymoney/wizards/newuserwizard/kpreferencepage.cpp +++ b/kmymoney/wizards/newuserwizard/kpreferencepage.cpp @@ -32,7 +32,6 @@ #include "knewuserwizard.h" #include "knewuserwizard_p.h" -#include "kfilepage.h" namespace NewUserWizard { @@ -48,10 +47,4 @@ { } - KMyMoneyWizardPage* PreferencePage::nextPage() const - { - Q_D(const PreferencePage); - return d->m_wizard->d_func()->m_filePage; - } - }