File indexing completed on 2024-06-16 04:47:26
0001 /*************************************************************************** 0002 * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr 0003 * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 ***************************************************************************/ 0006 /** @file 0007 * This file is Skrooge plugin to generate categories. 0008 * 0009 * @author Stephane MANKOWSKI / Guillaume DE BURE 0010 */ 0011 #include "skgcategoriespluginwidget.h" 0012 0013 #include <klocalizedstring.h> 0014 #include <qaction.h> 0015 0016 #include <qdom.h> 0017 #include <qevent.h> 0018 #include <qgraphicsscene.h> 0019 #include <qheaderview.h> 0020 #include <qwidget.h> 0021 0022 #include "skgcategoryobject.h" 0023 #include "skgdocumentbank.h" 0024 #include "skgmainpanel.h" 0025 #include "skgobjectmodel.h" 0026 #include "skgsortfilterproxymodel.h" 0027 #include "skgtraces.h" 0028 #include "skgtransactionmng.h" 0029 0030 SKGCategoriesPluginWidget::SKGCategoriesPluginWidget(QWidget* iParent, SKGDocumentBank* iDocument) 0031 : SKGTabPage(iParent, iDocument) 0032 { 0033 SKGTRACEINFUNC(10) 0034 if (iDocument == nullptr) { 0035 return; 0036 } 0037 0038 ui.setupUi(this); 0039 0040 // Define action 0041 QStringList overlaycategories; 0042 overlaycategories.push_back(QStringLiteral("view-categories")); 0043 if (SKGMainPanel::getMainPanel() != nullptr) { 0044 auto addCategoryAction = new QAction(SKGServices::fromTheme(QStringLiteral("list-add"), overlaycategories), i18nc("Verb", "Add category"), this); 0045 connect(addCategoryAction, &QAction::triggered, this, &SKGCategoriesPluginWidget::onAddCategory); 0046 SKGMainPanel::getMainPanel()->registerGlobalAction(QStringLiteral("edit_add_category"), addCategoryAction, true, QStringList() << QStringLiteral("category"), 1, -1, 450); 0047 } 0048 0049 // Set show widget 0050 ui.kCategoriesView->getShowWidget()->addGroupedItem(QStringLiteral("all"), i18n("All"), QLatin1String(""), QLatin1String(""), QLatin1String(""), Qt::META + Qt::Key_A); 0051 ui.kCategoriesView->getShowWidget()->addGroupedItem(QStringLiteral("opened"), i18n("Opened"), QStringLiteral("vcs-normal"), QStringLiteral("t_close='N'"), QLatin1String(""), Qt::META + Qt::Key_O); 0052 ui.kCategoriesView->getShowWidget()->addGroupedItem(QStringLiteral("closed"), i18n("Closed"), QStringLiteral("vcs-conflicting"), QStringLiteral("t_close='Y'"), QLatin1String(""), Qt::META + Qt::Key_C); 0053 ui.kCategoriesView->getShowWidget()->addGroupedItem(QStringLiteral("income"), i18n("Income"), QStringLiteral("list-add"), QStringLiteral("f_REALCURRENTAMOUNT>=0"), QLatin1String(""), Qt::META + Qt::Key_Plus); 0054 ui.kCategoriesView->getShowWidget()->addGroupedItem(QStringLiteral("expenditure"), i18n("Expenditure"), QStringLiteral("list-remove"), QStringLiteral("f_REALCURRENTAMOUNT<=0"), QLatin1String(""), Qt::META + Qt::Key_Minus); 0055 ui.kCategoriesView->getShowWidget()->addGroupedItem(QStringLiteral("highlighted"), i18n("Highlighted"), QStringLiteral("bookmarks"), QStringLiteral("t_HASBOOKMARKEDCHILD<>'N'"), QLatin1String(""), Qt::META + Qt::Key_H); 0056 ui.kCategoriesView->getShowWidget()->setDefaultState(QStringLiteral("all")); 0057 0058 ui.kAdd->setIcon(SKGServices::fromTheme(QStringLiteral("list-add"), overlaycategories)); 0059 ui.kAdd->setToolTip(i18nc("An help", "Add a sub category to the selected one")); 0060 ui.kAdd->setMaximumWidth(ui.kAdd->height()); 0061 0062 ui.kNameLbl->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_name")))); 0063 0064 ui.kModifyCategoryButton->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok"))); 0065 ui.kDeleteUnusedButton->setIcon(SKGServices::fromTheme(QStringLiteral("edit-delete"))); 0066 0067 ui.kCategoriesView->setModel(new SKGObjectModel(qobject_cast<SKGDocumentBank*>(getDocument()), QStringLiteral("v_category_display"), QStringLiteral("1=0"), this, QStringLiteral("rd_category_id"), false)); 0068 ui.kCategoriesView->getView()->setRootIsDecorated(true); 0069 ui.kCategoriesView->getView()->setAnimated(false); // Not compatible with double click 0070 ui.kCategoriesView->getView()->resizeColumnToContents(0); 0071 ui.kCategoriesView->getView()->header()->setStretchLastSection(false); 0072 0073 connect(getDocument(), &SKGDocument::tableModified, this, &SKGCategoriesPluginWidget::dataModified, Qt::QueuedConnection); 0074 connect(ui.kCategoriesView->getView(), &SKGTreeView::clickEmptyArea, this, &SKGCategoriesPluginWidget::cleanEditor); 0075 connect(ui.kCategoriesView->getView(), &SKGTreeView::doubleClicked, SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("open")).data(), &QAction::trigger); 0076 connect(ui.kCategoriesView->getView(), &SKGTreeView::selectionChangedDelayed, this, [ = ] {this->onSelectionChanged();}); 0077 0078 connect(ui.kModifyCategoryButton, &QPushButton::clicked, this, &SKGCategoriesPluginWidget::onUpdateCategory); 0079 connect(ui.kNameInput1, &QLineEdit::textChanged, this, &SKGCategoriesPluginWidget::onEditorModified); 0080 connect(ui.kNameInput2, &QLineEdit::textChanged, this, &SKGCategoriesPluginWidget::onEditorModified); 0081 connect(ui.kDeleteUnusedButton, &QPushButton::clicked, this, &SKGCategoriesPluginWidget::onDeleteUnused); 0082 connect(ui.kAdd, &QPushButton::clicked, this, &SKGCategoriesPluginWidget::onAddCategory); 0083 0084 // Set Event filters to catch CTRL+ENTER or SHIFT+ENTER 0085 this->installEventFilter(this); 0086 0087 dataModified(QLatin1String(""), 0); 0088 } 0089 0090 SKGCategoriesPluginWidget::~SKGCategoriesPluginWidget() 0091 { 0092 SKGTRACEINFUNC(10) 0093 } 0094 0095 bool SKGCategoriesPluginWidget::eventFilter(QObject* iObject, QEvent* iEvent) 0096 { 0097 if ((iEvent != nullptr) && iEvent->type() == QEvent::KeyPress) { 0098 auto* keyEvent = dynamic_cast<QKeyEvent*>(iEvent); 0099 if (keyEvent && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && iObject == this) { 0100 if ((QApplication::keyboardModifiers() & Qt::ControlModifier) != 0u && ui.kAdd->isEnabled()) { 0101 ui.kAdd->click(); 0102 } else if ((QApplication::keyboardModifiers() &Qt::ShiftModifier) != 0u && ui.kModifyCategoryButton->isEnabled()) { 0103 ui.kModifyCategoryButton->click(); 0104 } 0105 } 0106 } 0107 0108 return SKGTabPage::eventFilter(iObject, iEvent); 0109 } 0110 0111 void SKGCategoriesPluginWidget::dataModified(const QString& iTableName, int iIdTransaction, bool iLightTransaction) 0112 { 0113 SKGTRACEINFUNC(10) 0114 Q_UNUSED(iIdTransaction) 0115 0116 if (!iLightTransaction) { 0117 if (iTableName == QStringLiteral("category") || iTableName.isEmpty()) { 0118 // Set completions 0119 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kNameInput1, getDocument(), QStringLiteral("category"), QStringLiteral("t_name"), QLatin1String(""), true); 0120 0121 onSelectionChanged(); 0122 } 0123 } 0124 } 0125 0126 void SKGCategoriesPluginWidget::onSelectionChanged() 0127 { 0128 SKGTRACEINFUNC(10) 0129 // Clean widgets 0130 int nb = ui.kLayout->count(); 0131 for (int i = 0; i < nb; ++i) { 0132 QLayoutItem* item = ui.kLayout->itemAt(0); 0133 if (item != nullptr) { 0134 ui.kLayout->removeItem(item); 0135 delete item->widget(); 0136 delete item; 0137 } 0138 } 0139 0140 ui.kNameInput2->setText(QLatin1String("")); 0141 0142 int nbSelect = getNbSelectedObjects(); 0143 if (nbSelect == 1) { 0144 SKGCategoryObject obj(getFirstSelectedObject()); 0145 ui.kNameInput1->setText(obj.getName()); 0146 0147 SKGCategoryObject parentCat; 0148 obj.getParentCategory(parentCat); 0149 QString parentName = parentCat.getFullName(); 0150 0151 QStringList items = SKGServices::splitCSVLine(parentName, QString(OBJECTSEPARATOR).trimmed().at(0)); 0152 int nbItems = items.count(); 0153 QString fullname; 0154 for (int i = 0; i < nbItems; ++i) { 0155 auto btn = new QPushButton(ui.kFrame); 0156 btn->setFlat(true); 0157 btn->setText(items.at(i).trimmed()); 0158 if (!fullname.isEmpty()) { 0159 fullname += OBJECTSEPARATOR; 0160 } 0161 fullname += items.at(i).trimmed(); 0162 btn->setProperty("FULLNAME", fullname); 0163 connect(btn, &QPushButton::clicked, this, &SKGCategoriesPluginWidget::changeSelection); 0164 ui.kLayout->addWidget(btn); 0165 0166 auto lbl = new QLabel(ui.kFrame); 0167 lbl->setText(OBJECTSEPARATOR); 0168 ui.kLayout->addWidget(lbl); 0169 } 0170 } else if (nbSelect > 1) { 0171 ui.kNameInput1->setText(NOUPDATE); 0172 } 0173 0174 onEditorModified(); 0175 Q_EMIT selectionChanged(); 0176 } 0177 0178 void SKGCategoriesPluginWidget::changeSelection() 0179 { 0180 QString fullname = sender()->property("FULLNAME").toString(); 0181 SKGObjectBase::SKGListSKGObjectBase list; 0182 getDocument()->getObjects(QStringLiteral("v_category"), "t_fullname='" % SKGServices::stringToSqlString(fullname) % '\'', list); 0183 0184 if (!list.isEmpty()) { 0185 ui.kCategoriesView->getView()->selectObject(list.at(0).getUniqueID()); 0186 onSelectionChanged(); 0187 } 0188 } 0189 0190 QString SKGCategoriesPluginWidget::getState() 0191 { 0192 SKGTRACEINFUNC(10) 0193 QDomDocument doc(QStringLiteral("SKGML")); 0194 QDomElement root = doc.createElement(QStringLiteral("parameters")); 0195 doc.appendChild(root); 0196 0197 // Memorize table settings 0198 root.setAttribute(QStringLiteral("view"), ui.kCategoriesView->getState()); 0199 return doc.toString(); 0200 } 0201 0202 void SKGCategoriesPluginWidget::setState(const QString& iState) 0203 { 0204 SKGTRACEINFUNC(10) 0205 QDomDocument doc(QStringLiteral("SKGML")); 0206 doc.setContent(iState); 0207 QDomElement root = doc.documentElement(); 0208 0209 ui.kCategoriesView->setFilter(SKGServices::fromTheme(root.attribute(QStringLiteral("title_icon"))), root.attribute(QStringLiteral("title")), root.attribute(QStringLiteral("whereClause"))); 0210 ui.kCategoriesView->setState(root.attribute(QStringLiteral("view"))); 0211 } 0212 0213 QString SKGCategoriesPluginWidget::getDefaultStateAttribute() 0214 { 0215 return QStringLiteral("SKGCATEGORIES_DEFAULT_PARAMETERS"); 0216 } 0217 0218 QWidget* SKGCategoriesPluginWidget::mainWidget() 0219 { 0220 return ui.kCategoriesView->getView(); 0221 } 0222 0223 void SKGCategoriesPluginWidget::onEditorModified() 0224 { 0225 _SKGTRACEINFUNC(10) 0226 int nb = getNbSelectedObjects(); 0227 ui.kNameInput1->setVisible(nb >= 1); 0228 ui.kCatSeparator->setVisible(nb >= 1); 0229 ui.kModifyCategoryButton->setEnabled(!ui.kNameInput1->text().isEmpty() && nb >= 1); 0230 ui.kAdd->setEnabled(!ui.kNameInput2->text().isEmpty()); 0231 } 0232 0233 void SKGCategoriesPluginWidget::onAddCategory() 0234 { 0235 SKGError err; 0236 _SKGTRACEINFUNCRC(10, err) 0237 SKGCategoryObject cat; 0238 QString name = ui.kNameInput2->text(); 0239 if (name.isEmpty()) { 0240 name = i18nc("Noun, default name for a new category", "New category"); 0241 } 0242 { 0243 // Get Selection 0244 SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects(); 0245 0246 int nb = selection.count(); 0247 SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Category creation '%1'", name), err) 0248 0249 if (nb == 1) { 0250 SKGCategoryObject parentCat(selection[0]); 0251 name = parentCat.getFullName() % OBJECTSEPARATOR % name; 0252 } 0253 IFOKDO(err, SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), name, cat, false, true)) 0254 0255 // Send message 0256 IFOKDO(err, cat.getDocument()->sendMessage(i18nc("An information message", "The category '%1' has been created", cat.getDisplayName()), SKGDocument::Hidden)) 0257 } 0258 0259 // status bar 0260 IFOK(err) { 0261 ui.kCategoriesView->getView()->selectObject(cat.getUniqueID()); 0262 err = SKGError(0, i18nc("Successful message after an user action", "Category '%1' created", name)); 0263 } else { 0264 err.addError(ERR_FAIL, i18nc("Error message", "Category creation failed")); 0265 } 0266 0267 // Display error 0268 SKGMainPanel::displayErrorMessage(err, true); 0269 } 0270 0271 void SKGCategoriesPluginWidget::onUpdateCategory() 0272 { 0273 SKGError err; 0274 _SKGTRACEINFUNCRC(10, err) 0275 // Get Selection 0276 SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects(); 0277 0278 int nb = selection.count(); 0279 { 0280 QString name = ui.kNameInput1->text(); 0281 SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Category update"), err, nb) 0282 if (nb > 1 && name != NOUPDATE && !name.startsWith(QLatin1String("="))) { 0283 getDocument()->sendMessage(i18nc("Information message", "You tried to modify all names of selected categories. Categories have been merged.")); 0284 0285 // Do the merge 0286 SKGCategoryObject catObj1(selection[0]); 0287 for (int i = 1; !err && i < nb; ++i) { 0288 SKGCategoryObject catObj(selection.at(i)); 0289 0290 // Send message 0291 IFOKDO(err, catObj.getDocument()->sendMessage(i18nc("An information message", "The category '%1' has been merged with category '%2'", catObj1.getDisplayName(), catObj.getDisplayName()), SKGDocument::Hidden)) 0292 0293 IFOKDO(err, catObj1.merge(catObj)) 0294 0295 IFOKDO(err, getDocument()->stepForward(i)) 0296 } 0297 0298 // Change selection for the rest of the transaction 0299 selection.clear(); 0300 selection.push_back(catObj1); 0301 nb = 1; 0302 } 0303 0304 for (int i = 0; !err && i < nb; ++i) { 0305 // Modification of object 0306 SKGCategoryObject cat(selection.at(i)); 0307 err = cat.setName(name); 0308 IFOKDO(err, cat.save()) 0309 0310 // Send message 0311 IFOKDO(err, cat.getDocument()->sendMessage(i18nc("An information message", "The category '%1' has been updated", cat.getDisplayName()), SKGDocument::Hidden)) 0312 } 0313 } 0314 // status bar 0315 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Category updated"))) 0316 else { 0317 err.addError(ERR_FAIL, i18nc("Error message", "Category update failed")); 0318 } 0319 0320 // Display error 0321 SKGMainPanel::displayErrorMessage(err, true); 0322 0323 // Set focus on table 0324 ui.kCategoriesView->getView()->setFocus(); 0325 } 0326 0327 void SKGCategoriesPluginWidget::onDeleteUnused() 0328 { 0329 QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("clean_delete_unused_categories")); 0330 if (act != nullptr) { 0331 act->trigger(); 0332 } 0333 } 0334 0335 void SKGCategoriesPluginWidget::cleanEditor() 0336 { 0337 if (getNbSelectedObjects() == 0) { 0338 ui.kNameInput1->setText(QLatin1String("")); 0339 ui.kNameInput2->setText(QLatin1String("")); 0340 } 0341 } 0342 void SKGCategoriesPluginWidget::activateEditor() 0343 { 0344 ui.kNameInput1->setFocus(); 0345 } 0346 0347 bool SKGCategoriesPluginWidget::isEditor() 0348 { 0349 return true; 0350 } 0351 0352