File indexing completed on 2024-05-19 16:16:49

0001 /*
0002     SPDX-FileCopyrightText: 2008-2018 Thomas Baumgart <tbaumgart@kde.org>
0003     SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "config-kmymoney.h"
0008 #include "kaccounttemplateselector.h"
0009 
0010 // ----------------------------------------------------------------------------
0011 // QT Includes
0012 
0013 #include <QDir>
0014 #include <QTimer>
0015 #include <QList>
0016 #include <QTreeWidget>
0017 #include <QStandardPaths>
0018 #include <QDebug>
0019 
0020 // ----------------------------------------------------------------------------
0021 // KDE Includes
0022 
0023 #include <KTextEdit>
0024 
0025 // ----------------------------------------------------------------------------
0026 // Project Includes
0027 
0028 #include "mymoneyutils.h"
0029 
0030 #include "ui_kaccounttemplateselector.h"
0031 
0032 #include <mymoneytemplate.h>
0033 
0034 class KAccountTemplateSelectorPrivate
0035 {
0036     Q_DISABLE_COPY(KAccountTemplateSelectorPrivate)
0037 
0038 public:
0039     KAccountTemplateSelectorPrivate() :
0040         ui(new Ui::KAccountTemplateSelector),
0041         id(0)
0042     {
0043     }
0044 
0045     ~KAccountTemplateSelectorPrivate()
0046     {
0047         delete ui;
0048     }
0049 
0050 #ifndef KMM_DESIGNER
0051     QTreeWidgetItem* hierarchyItem(const QString& parent, const QString& name)
0052     {
0053         if (!m_templateHierarchy.contains(parent)
0054                 || m_templateHierarchy[parent] == 0) {
0055             QRegExp exp("(.*):(.*)");
0056             if (exp.indexIn(parent) != -1)
0057                 m_templateHierarchy[parent] = hierarchyItem(exp.cap(1), exp.cap(2));
0058         }
0059         QTreeWidgetItem *item = new QTreeWidgetItem(m_templateHierarchy[parent]);
0060         item->setText(0, name);
0061         return item;
0062     }
0063 
0064     void loadHierarchy()
0065     {
0066         m_templateHierarchy.clear();
0067         QTreeWidgetItemIterator it(ui->m_groupList, QTreeWidgetItemIterator::Selected);
0068         QTreeWidgetItem* it_v;
0069         while ((it_v = *it) != 0) {
0070             m_templates[it_v->data(0, Qt::UserRole).toInt()].hierarchy(m_templateHierarchy);
0071             ++it;
0072         }
0073 
0074         // I need to think about this some more. The code works and shows
0075         // the current account hierarchy. It might be useful, to show
0076         // existing accounts dimmed and the new ones in bold or so.
0077 #if 0
0078 
0079         // add the hierarchy from the MyMoneyFile object
0080         QList<MyMoneyAccount> aList;
0081         QList<MyMoneyAccount>::const_iterator it_a;
0082         auto file = MyMoneyFile::instance();
0083         file->accountList(aList);
0084         if (aList.count() > 0) {
0085             m_templateHierarchy[file->accountToCategory(file->asset().id(), true)] = 0;
0086             m_templateHierarchy[file->accountToCategory(file->liability().id(), true)] = 0;
0087             m_templateHierarchy[file->accountToCategory(file->income().id(), true)] = 0;
0088             m_templateHierarchy[file->accountToCategory(file->expense().id(), true)] = 0;
0089             m_templateHierarchy[file->accountToCategory(file->equity().id(), true)] = 0;
0090         }
0091 
0092         for (it_a = aList.begin(); it_a != aList.end(); ++it_a) {
0093             m_templateHierarchy[file->accountToCategory((*it_a).id(), true)] = 0;
0094         }
0095 #endif
0096 
0097         ui->m_accountList->clear();
0098 
0099         QRegExp exp("(.*):(.*)");
0100         for (QMap<QString, QTreeWidgetItem*>::iterator it_h = m_templateHierarchy.begin(); it_h != m_templateHierarchy.end(); ++it_h) {
0101             if (exp.indexIn(it_h.key()) == -1) {
0102                 (*it_h) = new QTreeWidgetItem(ui->m_accountList);
0103                 (*it_h)->setText(0, it_h.key());
0104             } else {
0105                 (*it_h) = hierarchyItem(exp.cap(1), exp.cap(2));
0106             }
0107             (*it_h)->setExpanded(true);
0108         }
0109 
0110         ui->m_description->clear();
0111         if (ui->m_groupList->currentItem()) {
0112             ui->m_description->setText(m_templates[ui->m_groupList->currentItem()->data(0, Qt::UserRole).toInt()].longDescription());
0113         }
0114     }
0115 
0116     QList<MyMoneyTemplate> selectedTemplates() const
0117     {
0118         QList<MyMoneyTemplate> list;
0119         QTreeWidgetItemIterator it(ui->m_groupList, QTreeWidgetItemIterator::Selected);
0120         QTreeWidgetItem* it_v;
0121         while ((it_v = *it) != 0) {
0122             list << m_templates[it_v->data(0, Qt::UserRole).toInt()];
0123             ++it;
0124         }
0125         return list;
0126     }
0127 #endif
0128 
0129 
0130 
0131 public:
0132     Ui::KAccountTemplateSelector    *ui;
0133     QMap<QString, QTreeWidgetItem*>  m_templateHierarchy;
0134 #ifndef KMM_DESIGNER
0135     QMap<int, MyMoneyTemplate>       m_templates;
0136     // a map of country name or country name (language name) -> localeId (lang_country) so be careful how you use it
0137     QMap<QString, QString>           countries;
0138     QString                          currentLocaleId;
0139     QMap<QString, QString>::iterator it_m;
0140     QStringList                      dirlist;
0141     int                              id;
0142 #endif
0143 };
0144 
0145 KAccountTemplateSelector::KAccountTemplateSelector(QWidget* parent) :
0146     QWidget(parent),
0147     d_ptr(new KAccountTemplateSelectorPrivate)
0148 {
0149     Q_D(KAccountTemplateSelector);
0150     d->ui->setupUi(this);
0151     d->ui->m_accountList->header()->hide();
0152     d->ui->m_groupList->setSelectionMode(QAbstractItemView::ExtendedSelection);
0153     connect(d->ui->m_groupList, &QTreeWidget::itemSelectionChanged, this, &KAccountTemplateSelector::slotLoadHierarchy);
0154 
0155     // kick off loading of account template data
0156     QTimer::singleShot(0, this, SLOT(slotLoadTemplateList()));
0157 }
0158 
0159 KAccountTemplateSelector::~KAccountTemplateSelector()
0160 {
0161     Q_D(KAccountTemplateSelector);
0162     delete d;
0163 }
0164 
0165 void KAccountTemplateSelector::slotLoadTemplateList()
0166 {
0167 #ifndef KMM_DESIGNER
0168     Q_D(KAccountTemplateSelector);
0169     QStringList dirs;
0170 
0171     // get list of template subdirs and scan them for the list of subdirs
0172     if (MyMoneyUtils::isRunningAsAppImage()) {
0173         // according to https://docs.appimage.org/packaging-guide/ingredients.html#open-source-applications
0174         // QStandardPaths::AppDataLocation is unreliable on AppImages, so apply workaround here in case we fail to find templates
0175         // watch out for QStringBuilder here; for yet unknown reason it causes segmentation fault on startup
0176         const auto templateDirList = QString("%1%2").arg(QCoreApplication::applicationDirPath(), QLatin1String("/../share/kmymoney/templates/"));
0177         if (QFile::exists(templateDirList)) {
0178             d->dirlist.append(templateDirList);
0179         } else {
0180             qWarning() << "Template directory was not found in the following location:" << templateDirList;
0181         }
0182     } else {
0183 #if defined(Q_OS_MAC)
0184         d->dirlist = QStandardPaths::locateAll(QStandardPaths::DataLocation, "kmymoney/templates", QStandardPaths::LocateDirectory);
0185 #else
0186         d->dirlist = QStandardPaths::locateAll(QStandardPaths::DataLocation, "templates", QStandardPaths::LocateDirectory);
0187 #endif
0188     }
0189 
0190     if (d->dirlist.isEmpty()) {
0191         qWarning("Template files were not found in any of the following QStandardPaths::AppDataLocation:");
0192         for (const auto& standardPath : QStandardPaths::standardLocations(QStandardPaths::AppDataLocation))
0193             qWarning() << standardPath;
0194     } else {
0195         qDebug() << "Found template dir(s):" << d->dirlist;
0196     }
0197 
0198     QStringList::iterator it;
0199     for (it = d->dirlist.begin(); it != d->dirlist.end(); ++it) {
0200         QDir dir(*it);
0201         dirs = dir.entryList(QStringList("*"), QDir::Dirs);
0202         QStringList::iterator it_d;
0203         for (it_d = dirs.begin(); it_d != dirs.end(); ++it_d) {
0204             // we don't care about . and ..
0205             if ((*it_d) == ".." || (*it_d) == "." || (*it_d) == "C")
0206                 continue;
0207             QLocale templateLocale(*it_d);
0208             if (templateLocale.language() != QLocale::C) {
0209                 QString country = QLocale().countryToString(templateLocale.country());
0210                 QString lang = QLocale().languageToString(templateLocale.language());
0211                 if (d->countries.contains(country)) {
0212                     if (d->countries[country] != *it_d) {
0213                         QString otherName = d->countries[country];
0214                         QLocale otherTemplateLocale(otherName);
0215                         QString otherCountry = QLocale().countryToString(otherTemplateLocale.country());
0216                         QString otherLang = QLocale().languageToString(otherTemplateLocale.language());
0217                         d->countries.remove(country);
0218                         d->countries[QString("%1 (%2)").arg(otherCountry, otherLang)] = otherName;
0219                         d->countries[QString("%1 (%2)").arg(country, lang)] = *it_d;
0220                         // retain the item corresponding to the current locale
0221                         if (QLocale().country() == templateLocale.country()) {
0222                             d->currentLocaleId = *it_d;
0223                         }
0224                     }
0225                 } else {
0226                     d->countries[country] = *it_d;
0227                     // retain the item corresponding to the current locale
0228                     if (QLocale().country() == templateLocale.country()) {
0229                         d->currentLocaleId = *it_d;
0230                     }
0231                 }
0232             } else {
0233                 qDebug("'%s/%s' not scanned", qPrintable(*it), qPrintable(*it_d));
0234             }
0235         }
0236     }
0237 
0238     // now that we know, what we can get at max, we scan everything
0239     // and parse the templates into memory
0240     d->ui->m_groupList->clear();
0241     d->m_templates.clear();
0242     d->it_m = d->countries.begin();
0243     d->id = 1;
0244     if (d->it_m != d->countries.end())
0245         QTimer::singleShot(0, this, SLOT(slotLoadCountry()));
0246     else {
0247         d->loadHierarchy();
0248     }
0249 #endif
0250 }
0251 
0252 void KAccountTemplateSelector::slotLoadCountry()
0253 {
0254 #ifndef KMM_DESIGNER
0255     Q_D(KAccountTemplateSelector);
0256     QTreeWidgetItem *parent = new QTreeWidgetItem(d->ui->m_groupList);
0257     parent->setText(0, d->it_m.key());
0258     parent->setFlags(parent->flags() & ~Qt::ItemIsSelectable);
0259     for (QStringList::iterator it = d->dirlist.begin(); it != d->dirlist.end(); ++it) {
0260         QDir dir(QString("%1/%2").arg(*it).arg(*(d->it_m)));
0261         if (dir.exists()) {
0262             QStringList files = dir.entryList(QStringList("*"), QDir::Files);
0263             for (QStringList::iterator it_f = files.begin(); it_f != files.end(); ++it_f) {
0264                 MyMoneyTemplate templ(QUrl::fromUserInput(QString("%1/%2").arg(dir.canonicalPath(), *it_f)));
0265                 d->m_templates[d->id] = templ;
0266                 QTreeWidgetItem *item = new QTreeWidgetItem(parent);
0267                 item->setText(0, templ.title());
0268                 item->setText(1, templ.shortDescription());
0269                 item->setData(0, Qt::UserRole, QString("%1").arg(d->id));
0270                 ++d->id;
0271             }
0272         }
0273     }
0274     // make visible the templates of the current locale
0275     if (d->it_m.value() == d->currentLocaleId) {
0276         d->ui->m_groupList->setCurrentItem(parent);
0277         d->ui->m_groupList->expandItem(parent);
0278         d->ui->m_groupList->scrollToItem(parent, QTreeView::PositionAtTop);
0279     }
0280     ++d->it_m;
0281     if (d->it_m != d->countries.end())
0282         QTimer::singleShot(0, this, SLOT(slotLoadCountry()));
0283     else {
0284         d->loadHierarchy();
0285     }
0286 #endif
0287 
0288 }
0289 
0290 void KAccountTemplateSelector::slotLoadHierarchy()
0291 {
0292 #ifndef KMM_DESIGNER
0293     Q_D(KAccountTemplateSelector);
0294     d->loadHierarchy();
0295 #endif
0296 }
0297 
0298 QList<MyMoneyTemplate> KAccountTemplateSelector::selectedTemplates() const
0299 {
0300 #ifndef KMM_DESIGNER
0301     Q_D(const KAccountTemplateSelector);
0302     return d->selectedTemplates();
0303 #else
0304     return QList<MyMoneyTemplate>();
0305 #endif
0306 }
0307