Warning, file /office/skrooge/skgbasegui/skghtmlboardwidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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 a generic Skrooge plugin for html reports.set
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skghtmlboardwidget.h"
0012 
0013 #include <qdom.h>
0014 #include <qfileinfo.h>
0015 #include <qqmlcontext.h>
0016 #include <qquickitem.h>
0017 #include <qquickwidget.h>
0018 #include <qtemporaryfile.h>
0019 #include <qtoolbutton.h>
0020 #include <qwidgetaction.h>
0021 #include <qregularexpression.h>
0022 
0023 #include <klocalizedstring.h>
0024 #include <kcolorscheme.h>
0025 #include <klineedit.h>
0026 
0027 #include "skgdocument.h"
0028 #include "skgmainpanel.h"
0029 #include "skgreport.h"
0030 #include "skgtraces.h"
0031 
0032 SKGHtmlBoardWidget::SKGHtmlBoardWidget(QWidget* iParent, SKGDocument* iDocument, const QString& iTitle, const QString& iTemplate, QStringList  iTablesRefreshing, SKGSimplePeriodEdit::Modes iOptions, const QStringList& iAttributesForFilter)
0033     : SKGBoardWidget(iParent, iDocument, iTitle), m_Quick(nullptr), m_Report(iDocument->getReport()),  m_Text(nullptr), m_Template(iTemplate), m_TablesRefreshing(std::move(iTablesRefreshing)), m_refreshNeeded(false), m_period(nullptr), m_filter(nullptr), m_attributes(iAttributesForFilter)
0034 {
0035     SKGTRACEINFUNC(10)
0036     // Create menu
0037     if (iOptions != SKGSimplePeriodEdit::NONE) {
0038         setContextMenuPolicy(Qt::ActionsContextMenu);
0039 
0040         m_period = new SKGSimplePeriodEdit(this);
0041         m_period->setMode(iOptions);
0042 
0043         QDate date = QDate::currentDate();
0044         QStringList list;
0045         // TODO(Stephane MANKOWSKI): v_operation_display must be generic
0046         getDocument()->getDistinctValues(QStringLiteral("v_operation_display"), QStringLiteral("MIN(d_DATEMONTH)"), QStringLiteral("d_date<=CURRENT_DATE"), list);
0047         if (!list.isEmpty()) {
0048             if (!list[0].isEmpty()) {
0049                 date = SKGServices::periodToDate(list[0]);
0050             }
0051         }
0052         m_period->setFirstDate(date);
0053 
0054         auto periodEditWidget = new QWidgetAction(this);
0055         periodEditWidget->setDefaultWidget(m_period);
0056 
0057         addAction(periodEditWidget);
0058     }
0059     if (m_attributes.count()) {
0060         setContextMenuPolicy(Qt::ActionsContextMenu);
0061 
0062         m_filter = new KLineEdit(this);
0063         m_filter->setPlaceholderText(i18nc("A place holder for a filter field", "Filter"));
0064 
0065         auto filterEditWidget = new QWidgetAction(this);
0066         filterEditWidget->setDefaultWidget(m_filter);
0067 
0068         addAction(filterEditWidget);
0069     }
0070 
0071     // Enrich with tips of the day
0072     m_Report->setTipsOfDay(SKGMainPanel::getMainPanel()->getTipsOfDay());
0073 
0074     // Create main widget
0075     QString ext = QFileInfo(iTemplate).suffix().toLower();
0076     if (ext == QStringLiteral("qml")) {
0077         // Mode QML
0078         m_Quick = new QQuickWidget(this);
0079         m_Quick->setResizeMode(QQuickWidget::SizeViewToRootObject);
0080         m_Quick->setClearColor(Qt::transparent);
0081         m_Quick->setAttribute(Qt::WA_AlwaysStackOnTop);
0082         m_Quick->setAttribute(Qt::WA_TranslucentBackground);
0083         m_Quick->setObjectName(QStringLiteral("m_Quick"));
0084         QSizePolicy newSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
0085         newSizePolicy.setHorizontalStretch(0);
0086         newSizePolicy.setVerticalStretch(0);
0087         m_Quick->setSizePolicy(newSizePolicy);
0088 
0089         // Set properties
0090         QVariantHash mapping = m_Report->getContextProperty();
0091         auto keys = mapping.keys();
0092         for (const auto& k : qAsConst(keys)) {
0093             m_Quick->rootContext()->setContextProperty(k, mapping[k]);
0094         }
0095         m_Quick->rootContext()->setContextProperty(QStringLiteral("periodWidget"), m_period);
0096         m_Quick->rootContext()->setContextProperty(QStringLiteral("filterWidget"), m_filter);
0097         m_Quick->rootContext()->setContextProperty(QStringLiteral("panel"), SKGMainPanel::getMainPanel());
0098 
0099         setMainWidget(m_Quick);
0100     } else {
0101         // Mode template HTML
0102         m_Text = new QLabel(this);
0103         m_Text->setObjectName(QStringLiteral("m_Text"));
0104         m_Text->setTextFormat(Qt::RichText);
0105         m_Text->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop);
0106         m_Text->setTextInteractionFlags(Qt::TextBrowserInteraction);
0107         connect(m_Text, &QLabel::linkActivated, this, [ = ](const QString & val) {
0108             SKGMainPanel::getMainPanel()->openPage(val);
0109         });
0110 
0111         setMainWidget(m_Text);
0112     }
0113 
0114     // Connects
0115     connect(getDocument(), &SKGDocument::tableModified, this, &SKGHtmlBoardWidget::dataModified, Qt::QueuedConnection);
0116     if (m_period != nullptr) {
0117         connect(m_period, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, [ = ]() {
0118             this->dataModified();
0119         });
0120     }
0121     if (m_filter != nullptr) {
0122         connect(m_filter, &KLineEdit::textChanged, this, &SKGHtmlBoardWidget::onTextFilterChanged);
0123     }
0124     connect(SKGMainPanel::getMainPanel(), &SKGMainPanel::currentPageChanged, this, &SKGHtmlBoardWidget::pageChanged, Qt::QueuedConnection);
0125 }
0126 
0127 SKGHtmlBoardWidget::~SKGHtmlBoardWidget()
0128 {
0129     SKGTRACEINFUNC(10)
0130     m_period = nullptr;
0131     m_filter = nullptr;
0132     if (m_Report != nullptr) {
0133         delete m_Report;
0134         m_Report = nullptr;
0135     }
0136     if (m_Quick != nullptr) {
0137         m_Quick->setParent(nullptr); // To be sure that deletion is well managed
0138         m_Quick->rootObject()->deleteLater();
0139     }
0140 }
0141 
0142 QString SKGHtmlBoardWidget::getState()
0143 {
0144     SKGTRACEINFUNC(10)
0145     QDomDocument doc(QStringLiteral("SKGML"));
0146     doc.setContent(SKGBoardWidget::getState());
0147     QDomElement root = doc.documentElement();
0148 
0149     // Get state
0150     if (m_period != nullptr) {
0151         root.setAttribute(QStringLiteral("period"), m_period->text());
0152     }
0153     if (m_filter != nullptr) {
0154         root.setAttribute(QStringLiteral("filter"), m_filter->text());
0155     }
0156     return doc.toString();
0157 }
0158 
0159 void SKGHtmlBoardWidget::setState(const QString& iState)
0160 {
0161     SKGTRACEINFUNC(10)
0162     SKGBoardWidget::setState(iState);
0163 
0164     QDomDocument doc(QStringLiteral("SKGML"));
0165     doc.setContent(iState);
0166     QDomElement root = doc.documentElement();
0167 
0168     // Set state
0169     if (m_period != nullptr) {
0170         QString oldMode = root.attribute(QStringLiteral("previousMonth"));
0171         if (oldMode.isEmpty()) {
0172             QString period = root.attribute(QStringLiteral("period"));
0173             if (!period.isEmpty() && m_period->contains(period)) {
0174                 m_period->setText(period);
0175             }
0176         } else {
0177             m_period->setText(oldMode == QStringLiteral("N") ? i18nc("The current month", "Current month") : i18nc("The month before the current month", "Last month"));
0178         }
0179     }
0180     if (m_filter != nullptr) {
0181         QString filter = root.attribute(QStringLiteral("filter"));
0182         m_filter->setText(filter);
0183     }
0184     dataModified(QLatin1String(""), 0);
0185 }
0186 
0187 void SKGHtmlBoardWidget::setPointSize(int iPointSize) const
0188 {
0189     if (m_Report != nullptr) {
0190         m_Report->setPointSize(iPointSize);
0191     }
0192 }
0193 
0194 void SKGHtmlBoardWidget::pageChanged()
0195 {
0196     if (m_refreshNeeded) {
0197         dataModified();
0198     }
0199 }
0200 
0201 void SKGHtmlBoardWidget::onTextFilterChanged(const QString& iFilter)
0202 {
0203     auto tooltip = i18nc("Tooltip", "<html><head/><body><p>Searching is case-insensitive. So table, Table, and TABLE are all the same.<br/>"
0204                          "If you just put a word or series of words in the search box, the application will filter the table to keep all lines having these words (logical operator AND). <br/>"
0205                          "If you want to add (logical operator OR) some lines, you must prefix your word by '+'.<br/>"
0206                          "If you want to remove (logical operator NOT) some lines, you must prefix your word by '-'.<br/>"
0207                          "If you want to search only on some columns, you must prefix your word by the beginning of column name like: col1:word.<br/>"
0208                          "If you want to search only on one column, you must prefix your word by the column name and a dot like: col1.:word.<br/>"
0209                          "If you want to use the character ':' in value, you must specify the column name like this: col1:value:rest.<br/>"
0210                          "If you want to search for a phrase or something that contains spaces, you must put it in quotes, like: 'yes, this is a phrase'.</p>"
0211                          "<p>You can also use operators '&lt;', '&gt;', '&lt;=', '&gt;=', '=' and '#' (for regular expression).</p>"
0212                          "<p><span style=\"font-weight:600; text-decoration: underline;\">Examples:</span><br/>"
0213                          "+val1 +val2 =&gt; Keep lines containing val1 OR val2<br/>"
0214                          "+val1 -val2 =&gt; Keep lines containing val1 but NOT val2<br/>"
0215                          "'abc def' =&gt; Keep lines containing the sentence 'abc def' <br/>"
0216                          "'-att:abc def' =&gt; Remove lines having a column name starting by abc and containing 'abc def' <br/>"
0217                          "abc:def =&gt; Keep lines having a column name starting by abc and containing def<br/>"
0218                          ":abc:def =&gt; Keep lines containing 'abc:def'<br/>"
0219                          "Date&gt;2015-03-01 =&gt; Keep lines where at least one attribute starting by Date is greater than 2015-03-01<br/>"
0220                          "Date.&gt;2015-03-01 =&gt; Keep lines where at the Date attribute is greater than 2015-03-01<br/>"
0221                          "Amount&lt;10 =&gt;Keep lines where at least one attribute starting by Amount is less than 10<br/>"
0222                          "Amount=10 =&gt;Keep lines where at least one attribute starting by Amount is equal to 10<br/>"
0223                          "Amount&lt;=10 =&gt;Keep lines where at least one attribute starting by Amount is less or equal to 10<br/>"
0224                          "abc#^d.*f$ =&gt; Keep lines having a column name starting by abc and matching the regular expression ^d.*f$</p>"
0225                          "<span style=\"font-weight:600; text-decoration: underline;\">Your filter is understood like this:</span><br/>"
0226                          "%1</body></html>", SKGServices::searchCriteriasToWhereClause(SKGServices::stringToSearchCriterias(iFilter), m_attributes, getDocument(), true));
0227     m_filter->setToolTip(tooltip);
0228 
0229     SKGHtmlBoardWidget::dataModified();
0230 }
0231 
0232 void SKGHtmlBoardWidget::dataModified(const QString& iTableName, int iIdTransaction)
0233 {
0234     SKGTRACEINFUNC(10)
0235     Q_UNUSED(iIdTransaction)
0236 
0237     // Set title
0238     QString period = (m_period != nullptr ? m_period->period() : QLatin1String(""));
0239     QString title = getOriginalTitle();
0240     if (title.contains(QStringLiteral("%1"))) {
0241         setMainTitle(title.arg(period));
0242     }
0243     QString filterSQL;
0244     QString filterTXT;
0245     if (m_Report != nullptr) {
0246         m_Report->setPeriod((m_period != nullptr ? m_period->period() : SKGServices::dateToPeriod(QDate::currentDate(), QStringLiteral("M"))));
0247         if (m_filter) {
0248             filterSQL = SKGServices::searchCriteriasToWhereClause(SKGServices::stringToSearchCriterias(m_filter->text()),
0249                         m_attributes,
0250                         getDocument(),
0251                         false);
0252             filterTXT = SKGServices::searchCriteriasToWhereClause(SKGServices::stringToSearchCriterias(m_filter->text()),
0253                         m_attributes,
0254                         getDocument(),
0255                         true);
0256             m_Report->setSqlFilter(filterSQL);
0257         }
0258     }
0259 
0260     if (m_TablesRefreshing.isEmpty() || m_TablesRefreshing.contains(iTableName) || iTableName.isEmpty()) {
0261         // Is this the current page
0262         SKGTabPage* page = SKGTabPage::parentTabPage(this);
0263         if (page != nullptr && page != SKGMainPanel::getMainPanel()->currentPage()) {
0264             // No, we memorize that we have to compute later the report
0265             m_refreshNeeded = true;
0266             return;
0267         }
0268 
0269         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0270         if (m_Quick != nullptr) {
0271             m_Quick->rootContext()->setContextProperty(QStringLiteral("filterWidget_sql"), filterSQL);
0272             m_Quick->rootContext()->setContextProperty(QStringLiteral("filterWidget_txt"), filterTXT);
0273 
0274             // Set the source
0275             if (!m_Quick->source().isValid()) {
0276                 m_Quick->setSource(QUrl::fromLocalFile(m_Template));
0277 
0278                 QQuickItem* root = m_Quick->rootObject();
0279                 if (root != nullptr) {
0280                     connect(root, &QQuickItem::widthChanged, this, [ = ]() {
0281                         m_Quick->setMinimumSize(QSize(root->width(), root->height()));
0282                     });
0283                     connect(root, &QQuickItem::heightChanged, this, [ = ]() {
0284                         m_Quick->setMinimumSize(QSize(root->width(), root->height()));
0285                     });
0286                     m_Quick->setMinimumSize(QSize(root->width(), root->height()));
0287                     m_Quick->setMinimumSize(QSize(root->width(), root->height()));
0288                 }
0289             } else {
0290                 // Clean the cache to trigger the modification
0291                 m_Report->cleanCache();
0292             }
0293 
0294             m_refreshNeeded = false;
0295         }
0296 
0297         if (m_Text != nullptr) {
0298             // Clean the cache to trigger the modification
0299             m_Report->cleanCache();
0300 
0301             QString stream;
0302             SKGError err = SKGReport::getReportFromTemplate(m_Report, m_Template, stream);
0303             IFKO(err) stream = err.getFullMessage();
0304             stream = stream.remove(QRegularExpression(QStringLiteral("<img[^>]*/>")));
0305 
0306             // Remove color of hyperlinks
0307             KColorScheme scheme(QPalette::Normal, KColorScheme::Window);
0308             auto color = scheme.foreground(KColorScheme::NormalText).color().name().right(6);
0309             stream = stream.replace(QStringLiteral("<a href"), QStringLiteral("<a style=\"color: #") + color + ";\" href");
0310 
0311             m_Text->setText(stream);
0312 
0313             m_refreshNeeded = false;
0314         }
0315         QApplication::restoreOverrideCursor();
0316     }
0317 
0318     // TODO(Stephane MANKOWSKI): No widget if no account (must not be hardcoded)
0319     bool exist = false;
0320     getDocument()->existObjects(QStringLiteral("account"), QLatin1String(""), exist);
0321     if (parentWidget() != nullptr) {
0322         setVisible(exist);
0323     }
0324 }
0325 #include "skghtmlboardwidget.h"