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 '<', '>', '<=', '>=', '=' and '#' (for regular expression).</p>" 0212 "<p><span style=\"font-weight:600; text-decoration: underline;\">Examples:</span><br/>" 0213 "+val1 +val2 => Keep lines containing val1 OR val2<br/>" 0214 "+val1 -val2 => Keep lines containing val1 but NOT val2<br/>" 0215 "'abc def' => Keep lines containing the sentence 'abc def' <br/>" 0216 "'-att:abc def' => Remove lines having a column name starting by abc and containing 'abc def' <br/>" 0217 "abc:def => Keep lines having a column name starting by abc and containing def<br/>" 0218 ":abc:def => Keep lines containing 'abc:def'<br/>" 0219 "Date>2015-03-01 => Keep lines where at least one attribute starting by Date is greater than 2015-03-01<br/>" 0220 "Date.>2015-03-01 => Keep lines where at the Date attribute is greater than 2015-03-01<br/>" 0221 "Amount<10 =>Keep lines where at least one attribute starting by Amount is less than 10<br/>" 0222 "Amount=10 =>Keep lines where at least one attribute starting by Amount is equal to 10<br/>" 0223 "Amount<=10 =>Keep lines where at least one attribute starting by Amount is less or equal to 10<br/>" 0224 "abc#^d.*f$ => 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"