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