diff --git a/plugins/import/skrooge_import_afb120/skgimportpluginafb120.cpp b/plugins/import/skrooge_import_afb120/skgimportpluginafb120.cpp index efd1b740c..d9feea84f 100644 --- a/plugins/import/skrooge_import_afb120/skgimportpluginafb120.cpp +++ b/plugins/import/skrooge_import_afb120/skgimportpluginafb120.cpp @@ -1,213 +1,212 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ /** @file * This file is Skrooge plugin for AFB120 import / export. - * http://jerome.girod.perso.sfr.fr/finance/afb120x.php * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgimportpluginafb120.h" #include #include #include #include #include "skgbankincludes.h" #include "skgimportexportmanager.h" #include "skgservices.h" #include "skgtraces.h" /** * This plugin factory. */ K_PLUGIN_FACTORY(SKGImportPluginAFB120Factory, registerPlugin();) SKGImportPluginAFB120::SKGImportPluginAFB120(QObject* iImporter, const QVariantList& iArg) : SKGImportPlugin(iImporter) { SKGTRACEINFUNC(10) Q_UNUSED(iArg) } SKGImportPluginAFB120::~SKGImportPluginAFB120() = default; bool SKGImportPluginAFB120::isImportPossible() { SKGTRACEINFUNC(10) return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("AFB120") || m_importer->getFileNameExtension() == QStringLiteral("CFO")); } double SKGImportPluginAFB120::toAmount(const QString& iAmount, int iNbDecimal) { QString amount = iAmount; QChar lastChar = amount.right(1).at(0); int codeAscii = lastChar.toLatin1(); int sign = (codeAscii > 79 && codeAscii != 123 ? -1 : 1); if (codeAscii == 123 || codeAscii == 125) { amount[amount.count() - 1] = '0'; } else { bool ok = false; amount[amount.count() - 1] = QChar(codeAscii + QChar('1').toLatin1() - QString(sign == -1 ? QStringLiteral("0x4A") : QStringLiteral("0x41")).toUInt(&ok, 16)); } return static_cast(sign) * SKGServices::stringToDouble(amount) / qPow(10, iNbDecimal); } SKGError SKGImportPluginAFB120::importFile() { if (m_importer == nullptr) { return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters")); } SKGError err; SKGTRACEINFUNCRC(2, err) // Begin transaction err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "AFB120"), 2); IFOK(err) { // Open file IFOK(err) { QFile file(m_importer->getLocalFileName()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); } else { // Read lines QStringList lines; { QTextStream stream(&file); if (!m_importer->getCodec().isEmpty()) { stream.setCodec(m_importer->getCodec().toLatin1().constData()); } while (!stream.atEnd()) { // Read line QString line = stream.readLine().trimmed(); if (!line.isEmpty()) { lines.push_back(line); } } } // close file file.close(); // Step 1 done IFOKDO(err, m_importer->getDocument()->stepForward(1)) // Read lines SKGAccountObject account; SKGUnitObject unit; QString bankName = QStringLiteral("AFB120"); QString inititalAmount; int nb = lines.count(); IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), nb)) for (int i = 0; i < nb && !err; ++i) { // Read line const QString& line = lines.at(i); if (!line.isEmpty()) { if (line.startsWith(QLatin1String("01"))) { // Previous balance QString accountNumber = line.mid(22 - 1, 11); inititalAmount = line.mid(91 - 1, 14); SKGObjectBase::SKGListSKGObjectBase listAccount; err = m_importer->getDocument()->getObjects(QStringLiteral("v_account"), "t_number='" % accountNumber % '\'', listAccount); IFOK(err) { if (listAccount.count() == 1) { // Yes ! Only one account found account = listAccount.at(0); err = m_importer->getDocument()->sendMessage(i18nc("An information message", "Using account '%1' for import", account.getName())); } else { if (listAccount.count() > 1) { err = m_importer->getDocument()->sendMessage(i18nc("An information message", "More than one possible account found.")); } SKGBankObject bank(m_importer->getDocument()); IFOKDO(err, bank.setName(bankName)) IFOKDO(err, bank.setNumber(bankName)) if (!err && bank.load().isFailed()) { err = bank.save(); } IFOKDO(err, bank.addAccount(account)) IFOKDO(err, account.setName(accountNumber)) IFOKDO(err, account.setNumber(accountNumber)) IFOKDO(err, account.setType(SKGAccountObject::CURRENT)) if (!err && account.load().isFailed()) { err = account.save(); } IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Default account '%1' created for import", accountNumber))) } } } else if (line.startsWith(QLatin1String("04"))) { // Operation QString unitCode = line.mid(17 - 1, 3); QString nbDecimal = line.mid(20 - 1, 1); // QString codeMode = line.mid(33 - 1, 2); QString dateJJMMAA = line.mid(35 - 1, 6); QString comment = line.mid(49 - 1, 31).trimmed(); QString amount = line.mid(91 - 1, 14); // Initialize balance if (unit.getID() == 0) { err = SKGUnitObject::createCurrencyUnit(m_importer->getDocument(), unitCode, unit); if (account.getNbOperation() > 1) { IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has not been set because some operations are already existing", account.getName()), SKGDocument::Warning)) } else { // Set initial balance IFOKDO(err, account.setInitialBalance(toAmount(inititalAmount, SKGServices::stringToInt(nbDecimal)), unit)) IFOKDO(err, account.save()) IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has been set with AFB120 file content", account.getName()))) } } SKGOperationObject operation; IFOKDO(err, account.addOperation(operation, true)) IFOKDO(err, operation.setDate(SKGServices::stringToTime(SKGServices::dateToSqlString(dateJJMMAA, QStringLiteral("DDMMYYYY"))).date())) IFOKDO(err, operation.setUnit(unit)) IFOKDO(err, operation.setAttribute(QStringLiteral("t_imported"), QStringLiteral("T"))) IFOKDO(err, operation.setComment(comment)) QByteArray hash = QCryptographicHash::hash(line.toUtf8(), QCryptographicHash::Md5); IFOKDO(err, operation.setImportID(QStringLiteral("AFB120-") % hash.toHex())) IFOKDO(err, operation.save(false)) SKGSubOperationObject subop; IFOKDO(err, operation.addSubOperation(subop)) IFOKDO(err, subop.setComment(comment)) IFOKDO(err, subop.setQuantity(toAmount(amount, SKGServices::stringToInt(nbDecimal)))) IFOKDO(err, subop.save(false, false)) } } IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) // Step 2 done IFOKDO(err, m_importer->getDocument()->stepForward(2)) } } } SKGENDTRANSACTION(m_importer->getDocument(), err) return err; } QString SKGImportPluginAFB120::getMimeTypeFilter() const { return "*.afb120 *.cfo|" % i18nc("A file format", "AFB120 file (cfomb)"); } #include diff --git a/skgbankmodeler/sources/org.kde.skrooge-source-msn.desktop b/skgbankmodeler/sources/org.kde.skrooge-source-msn.desktop index 01bc46a6a..79ac45cc2 100644 --- a/skgbankmodeler/sources/org.kde.skrooge-source-msn.desktop +++ b/skgbankmodeler/sources/org.kde.skrooge-source-msn.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=MSN Name[ca]=MSN Name[ca@valencia]=MSN Name[cs]=MSN Name[de]=MSN Name[en_GB]=MSN Name[es]=MSN Name[fr]=MSN Name[gl]=MSN Name[it]=MSN Name[nl]=MSN Name[pl]=MSN Name[pt]=MSN Name[sv]=MSN Name[uk]=MSN Name[x-test]=xxMSNxx Comment=You can get the list of available quotes from MSN.
Then, enter the code of the expected quote (the xxx in the url like https://www.msn.com/en-us/money/stockdetails/xxx). Comment[ca]=Podeu obtenir la llista de les cotitzacions disponibles a MSN.
Després introduïu el codi de la cotització esperada (les «xxx» a l'URL com aquest https://www.msn.com/en-us/money/stockdetails/xxx). Comment[ca@valencia]=Podeu obtindre la llista de les cotitzacions disponibles a MSN.
Després introduïu el codi de la cotització esperada (les «xxx» a l'URL com aquest https://www.msn.com/en-us/money/stockdetails/xxx). Comment[en_GB]=You can get the list of available quotes from MSN.
Then, enter the code of the expected quote (the xxx in the URL like https://www.msn.com/en-us/money/stockdetails/xxx). Comment[es]=Puede obtener la lista de las cotizaciones disponibles de MSN.
A continuación, introduzca el símbolo de la cotización esperada (el xxx del url, como en https://www.msn.com/en-us/money/stockdetails/xxx). Comment[fr]=Vous pouvez récupérer la liste des cotation disponibles depuis MSN.
Ensuite, saisissez la cotation souhaitée (le xxx dans l'adresse comme celle-ci https://www.msn.com/en-us/money/stockdetails/xxx). Comment[gl]=Pode obter a lista de cotizacións dispoñíbeis de MSN.
A continuación escriba a cotización esperada (o xxx dos URL coa forma https://www.msn.com/en-us/money/stockdetails/xxx). Comment[it]=Puoi ottenere un elenco delle quotazioni disponibili da MSN.
Digita, quindi, il codice della quotazione desiderata (la xxx nell'URL, tipo https://www.msn.com/en-us/money/stockdetails/xxx). Comment[nl]=U kunt de lijst met namen van beschikbare koersen ophalen uit MSN.
Voer dan de naam van de gevraagde koers in (de xxx in de url zoals deze https://www.msn.com/en-us/money/stockdetails/xxx/). Comment[pl]=Wykaz dostępnych wycen można pobrać z MSN.
Następnie podaj żądaną wycenę(trzy iksy xxx w adresie url tak jak w https://www.msn.com/en-us/money/stockdetails/xxx). Comment[pt]=Poderá obter a lista de cotações disponíveis a partir do MSN.
Depois, indique a cotação esperada (o 'xxx' num URL como o seguinte: https://msn.com/en-us/money/stockdetails/xxx/). Comment[sv]=Listan över tillgängliga kursnoteringar kan hämtas från MSN.
Skriv därefter in förväntad kurs (xxx i webbadressen https://www.msn.com/en-us/money/stockdetails/xxx). Comment[uk]=Список доступних курсів можна отримати з MSN.
Далі, введіть потрібний курс (xxx у адресі, наприклад https://www.msn.com/en-us/money/stockdetails/xxx). Comment[x-test]=xxYou can get the list of available quotes from MSN.
Then, enter the code of the expected quote (the xxx in the url like https://www.msn.com/en-us/money/stockdetails/xxx).xx Encoding=UTF-8 Icon=skrooge Type=Service X-KDE-ServiceTypes=skrooge/source X-Krunner-ID=msn X-KDE-PluginInfo-Author=Stephane MANKOWSKI,miraks X-KDE-PluginInfo-Email=stephane@mankowski.fr X-KDE-PluginInfo-Name=MSN X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=https://skrooge.org/ X-KDE-PluginInfo-Category=Plugins X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true #The url or the command line to get the list of accounts in the standard output, something like this: #%1 will be replaced by the internet code of the unit #%2 will be replaced by the current day in format yyyy-MM-dd #%3 will be replaced by the previous date in format yyyy-MM-dd #Example: # X-SKROOGE-url=https://server/?s=%1 # or # X-SKROOGE-script=mydownloadscript %1 #This parameter is MANDATORY -X-SKROOGE-url=http://www.msn.com/en-us/money/stockdetails/%1 +X-SKROOGE-url=https://www.msn.com/en-us/money/stockdetails/%1 #The mode (HTML or CSV or CSVR). In HTML mode, only one value will be extracted from downloaded page. In CSV mode, a value per line will be extracted. CSVR means CSV in reverse mode X-SKROOGE-mode=HTML #Regular expression to capture the price of the quote #This parameter is not MANDATORY. X-SKROOGE-price="currentvalue">([^<]*)< #Regular expression to capture the date of the quote #This parameter is not MANDATORY. #X-SKROOGE-date=%1,([^,]*),.* #The date format #X-SKROOGE-dateformat=yyyy-MM-dd diff --git a/skgbankmodeler/sources/org.kde.skrooge-source-ratesapi.desktop b/skgbankmodeler/sources/org.kde.skrooge-source-ratesapi.desktop index 731457a9c..93a05c6b8 100644 --- a/skgbankmodeler/sources/org.kde.skrooge-source-ratesapi.desktop +++ b/skgbankmodeler/sources/org.kde.skrooge-source-ratesapi.desktop @@ -1,68 +1,68 @@ [Desktop Entry] Name=RatesAPI Name[ca]=RatesAPI Name[cs]=RatesAPI Name[en_GB]=RatesAPI Name[es]=RatesAPI Name[fr]=RatesAPI Name[gl]=RatesAPI Name[it]=RatesAPI Name[nl]=RatesAPI Name[pl]=RatesAPI Name[pt]=RatesAPI Name[sv]=RatesAPI Name[uk]=RatesAPI Name[x-test]=xxRatesAPIxx -Comment=You can get the list of available quotes from RatesAPI.
Then, enter the expected quote in format CURRENCY/BASE (eg. USD/EUR). -Comment[ca]=Podeu obtenir la llista de les cotitzacions disponibles a RatesAPI.
Després introduïu la cotització desitjada en format DIVISA/BASE (p. ex. USD/EUR). -Comment[en_GB]=You can get the list of available quotes from RatesAPI.
Then, enter the expected quote in format CURRENCY/BASE (eg. USD/EUR). -Comment[es]=Puede obtener la lista de las cotizaciones disponibles de RatesAPI.
A continuación, introduzca la cotización esperada con el formato DIVISA/BASE (por ejemplo, USD/EUR). -Comment[fr]=Vous pouvez récupérer la liste des cotation disponibles depuis RatesAPI.
Ensuite, saisissez le symbole de la cotation souhaitée au format DEVISE/BASE (par ex. USD/EUR). -Comment[gl]=Pode obter a lista de cotizacións dispoñíbeis de RatesAPI.
A continuación escriba a cotización esperada no formato DIVISA/BASE (p. ex. USD/EUR). -Comment[it]=Puoi ottenere l'elenco delle quotazioni disponibili da RatesAPI.
Digita, quindi, la quotazione attesa nel formato VALUTA/BASE (es. USD/EUR). -Comment[nl]=U kunt de lijst met namen van beschikbare koersen ophalen uit RatesAPI.
Voer dan de gewenste koers in in het formaat VALUTA/BASIS (bijv. USD/EUR). -Comment[pl]=Możesz pobrać wykaz dostępnych wycen z <RatesAPI.
Następnie podaj symbol żądanej wyceny w postaci WALUTA DOCELOWA/WALUTA ODNIESIENIA (np. USD/EUR). -Comment[pt]=Poderá obter a lista de cotações disponíveis a partir do RatesAPI.
Depois, indique o símbolo das cotações esperadas no formato MOEDA/BASE (p.ex., USD/EUR). -Comment[sv]=Listan över tillgängliga kursnoteringar kan hämtas från RatesAPI.
Skriv därefter in förväntad kursnotering på formatet VALUTA/BAS (t.ex. USD/EUR). -Comment[uk]=Ви можете отримати список доступних курсів з сайта RatesAPI.
Далі, введіть потрібний вам курс у форматі ВАЛЮТА/ЕТАЛОН (наприклад USD/EUR). -Comment[x-test]=xxYou can get the list of available quotes from RatesAPI.
Then, enter the expected quote in format CURRENCY/BASE (eg. USD/EUR).xx +Comment=You can get the list of available quotes from RatesAPI.
Then, enter the expected quote in format CURRENCY/BASE (eg. USD/EUR). +Comment[ca]=Podeu obtenir la llista de les cotitzacions disponibles a RatesAPI.
Després introduïu la cotització desitjada en format DIVISA/BASE (p. ex. USD/EUR). +Comment[en_GB]=You can get the list of available quotes from RatesAPI.
Then, enter the expected quote in format CURRENCY/BASE (eg. USD/EUR). +Comment[es]=Puede obtener la lista de las cotizaciones disponibles de RatesAPI.
A continuación, introduzca la cotización esperada con el formato DIVISA/BASE (por ejemplo, USD/EUR). +Comment[fr]=Vous pouvez récupérer la liste des cotation disponibles depuis RatesAPI.
Ensuite, saisissez le symbole de la cotation souhaitée au format DEVISE/BASE (par ex. USD/EUR). +Comment[gl]=Pode obter a lista de cotizacións dispoñíbeis de RatesAPI.
A continuación escriba a cotización esperada no formato DIVISA/BASE (p. ex. USD/EUR). +Comment[it]=Puoi ottenere l'elenco delle quotazioni disponibili da RatesAPI.
Digita, quindi, la quotazione attesa nel formato VALUTA/BASE (es. USD/EUR). +Comment[nl]=U kunt de lijst met namen van beschikbare koersen ophalen uit RatesAPI.
Voer dan de gewenste koers in in het formaat VALUTA/BASIS (bijv. USD/EUR). +Comment[pl]=Możesz pobrać wykaz dostępnych wycen z <RatesAPI.
Następnie podaj symbol żądanej wyceny w postaci WALUTA DOCELOWA/WALUTA ODNIESIENIA (np. USD/EUR). +Comment[pt]=Poderá obter a lista de cotações disponíveis a partir do RatesAPI.
Depois, indique o símbolo das cotações esperadas no formato MOEDA/BASE (p.ex., USD/EUR). +Comment[sv]=Listan över tillgängliga kursnoteringar kan hämtas från RatesAPI.
Skriv därefter in förväntad kursnotering på formatet VALUTA/BAS (t.ex. USD/EUR). +Comment[uk]=Ви можете отримати список доступних курсів з сайта RatesAPI.
Далі, введіть потрібний вам курс у форматі ВАЛЮТА/ЕТАЛОН (наприклад USD/EUR). +Comment[x-test]=xxYou can get the list of available quotes from RatesAPI.
Then, enter the expected quote in format CURRENCY/BASE (eg. USD/EUR).xx Encoding=UTF-8 Icon=skrooge Type=Service X-KDE-ServiceTypes=skrooge/source X-Krunner-ID=ratesapi X-KDE-PluginInfo-Author=Stephane MANKOWSKI,miraks X-KDE-PluginInfo-Email=stephane@mankowski.fr X-KDE-PluginInfo-Name=RatesAPI X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=https://skrooge.org/ X-KDE-PluginInfo-Category=Plugins X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true #The url or the command line to get the list of accounts in the standard output, something like this: #%1 will be replaced by the internet code of the unit #%2 will be replaced by the current day in format yyyy-MM-dd #%3 will be replaced by the previous date in format yyyy-MM-dd #Example: # X-SKROOGE-url=https://server/?s=%1 # or # X-SKROOGE-script=mydownloadscript %1 #This parameter is MANDATORY X-SKROOGE-script=skrooge-ratesapi.py %1 #The mode (HTML or CSV or CSVR). In HTML mode, only one value will be extracted from downloaded page. In CSV mode, a value per line will be extracted. CSVR means CSV in reverse mode X-SKROOGE-mode=CSV #Regular expression to capture the price of the quote #This parameter is not MANDATORY. X-SKROOGE-price=[^,]*,([^,]*) #Regular expression to capture the date of the quote #This parameter is not MANDATORY. X-SKROOGE-date=([^,]*),.* #The date format X-SKROOGE-dateformat=yyyy-MM-dd diff --git a/skgbasegui/skgmainpanel.h b/skgbasegui/skgmainpanel.h index 491d82e05..d733397d1 100644 --- a/skgbasegui/skgmainpanel.h +++ b/skgbasegui/skgmainpanel.h @@ -1,558 +1,558 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #ifndef SKGMAINPANEL_H #define SKGMAINPANEL_H /** @file * This file defines a main panel. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include #include #include #include "skgbasegui_export.h" #include "skgbasegui_settings.h" #include "skginterfaceplugin.h" #include "skgobjectbase.h" #include "skgtabpage.h" #include "skgtabwidget.h" #include "ui_skgmainpanel_base.h" #include "ui_skgmainpanel_pref.h" class SKGDocument; class SKGMainPanelPrivate; class QSplashScreen; class KMessageWidget; class QListWidgetItem; /** * This class serves as the main window. It handles the * menus, toolbars, and status bars. */ class SKGBASEGUI_EXPORT SKGMainPanel : public KXmlGuiWindow { Q_OBJECT public: /** * Default Constructor * @param iSplashScreen the splash screen * @param iDocument the document to manage data */ explicit SKGMainPanel(QSplashScreen* iSplashScreen, SKGDocument* iDocument); /** * Default Destructor */ ~SKGMainPanel() override; /** * Return the document of the main panel * @return document of the main panel */ SKGDocument* getDocument() const; /** * Creates a modal file dialog and returns the selected filename or an empty string if none was chosen. * A confirmation message is displayed if needed * @param iStartDir this can either be * @li the URL of the directory to start in. * @li a QUrl) to start in the current working directory, or the last directory where a file has been selected. * @li an URL starting with 'kfiledialog:///\' to start in the directory last used by a filedialog in the same application that specified the same keyword. * @li an URL starting with 'kfiledialog:///\?global' to start in the directory last used by a filedialog in any application that specified the same keyword. * @param iFilter a shell glob or a mime-type-filter that specifies which files to display. The preferred option is to set a list of mimetype names, see setMimeFilter() for details. Otherwise you can set the text to be displayed for the each glob, and provide multiple globs, see setFilter() for details. * @param iParent the widget the dialog will be centered on initially. * @param iCodec a valid QString to get the codec or nullptr. * @return the file name */ static QString getSaveFileName(const QString& iStartDir, const QString& iFilter, QWidget* iParent, QString* iCodec = nullptr); /** * Display an error message * @param iError the error * @param iNotifyIfNoError to launch a notification even if there is no error * @return the message widget */ static KMessageWidget* displayErrorMessage(const SKGError& iError, bool iNotifyIfNoError = false); /** * Display an error message * @param iError the error * @param iAction the additional action to add * @param iNotifyIfNoError to launch a notification even if there is no error * @return the message widget */ static KMessageWidget* displayErrorMessage(const SKGError& iError, QAction* iAction, bool iNotifyIfNoError = false); /** * Fill a widget with distinct values * @param iWidgets the widgets * @param iDoc document * @param iTable table * @param iAttribut attribute * @param iWhereClause where clause * @param iAddoperators to add operators (=upper, =lower to the list) */ static void fillWithDistinctValue( const QList& iWidgets, SKGDocument* iDoc, const QString& iTable, const QString& iAttribut, const QString& iWhereClause, bool iAddoperators = false); /** * Return main panel */ static SKGMainPanel* getMainPanel(); /** * Get main config groupe * @return main config groupe */ static KConfigGroup getMainConfigGroup(); /** * Convert a QDate into a QString based on application settings. * @param iDate the date * @return the converted QString */ // cppcheck-suppress passedByValue static QString dateToString(QDate iDate); /** * Get the first selected object * @return first selected object */ SKGObjectBase getFirstSelectedObject() const; /** * Get the current selection * @return selected objects */ SKGObjectBase::SKGListSKGObjectBase getSelectedObjects() const; /** * Get the number of selected object * @return number of selected objects */ int getNbSelectedObjects() const; /** * To know if the widget having the selection has the focus * Default implementation is based on mainWidget * @return true of false */ bool hasSelectionWithFocus(); /** * To know if the closure of the application is authorized * @return true if close is authorized else false */ bool queryClose() override; /** * To know if the closure of the file is authorized * @return true if close is authorized else false */ bool queryFileClose(); /** * Return the plugin number by index * @param iIndex the index of the plugin * @return the plugin pointer. Can be nullptr. Mustn't be deleted */ SKGInterfacePlugin* getPluginByIndex(int iIndex); /** * Return the plugin number by name * @param iName the name of the plugin * @return the plugin pointer. Can be nullptr. Mustn't be deleted */ SKGInterfacePlugin* getPluginByName(const QString& iName); /** * Get the label for normal message in status bar * @return the label */ QLabel* statusNormalMessage() const; /** * Get the current splash screen. nullptr if the splash screen is closed. * @return the splash screen */ QSplashScreen* splashScreen() const; /** * Set the main widget * @param iWidget the widget to display when all pages are closed */ void setMainWidget(QWidget* iWidget); /** * Get the tab widget. * @return the tab widget */ SKGTabWidget* getTabWidget() const; /** * Get the history item of the current page * @return the history item */ SKGTabPage::SKGPageHistoryItem currentPageHistoryItem() const; /** * Get the index of the current page * @return index of the current page */ int currentPageIndex() const; /** * Get the current page * @return the current page */ SKGTabPage* currentPage() const; /** * Get a index of a page * @param iPage the page * @return the index (-1 if not found) */ int pageIndex(SKGTabPage* iPage) const; /** * Get a page * @param iIndex an index * @return the page */ SKGTabPage* page(int iIndex) const; /** * Get then number of pages * @return the number of pages */ int countPages() const; /** * Register a global action * @param iIdentifier identifier of the action * @param iAction action pointer * @param iAddInCollection to add or not the action in the main panel collection * @param iListOfTable list of table where this action must be enabled (empty list means all) * You can also add only one item like this to set the list dynamically: * query:the sql condition on sqlite_master * @param iMinSelection the minimum number of selected item to enable the action * 0 : no need selection but need a page opened containing a table * -1 : no need selection and need a page opened (not containing a table) * -2 : no need selection and no need a page opened * @param iMaxSelection the maximum number of selected item to enable the action (-1 = infinite) * @param iRanking the ranking to sort actions in contextual menus * @param iRanking the ranking to sort actions in contextual menus * -1: automatic by creation order * 0: not in contextual menu * @param iSelectionMustHaveFocus the action will be activated only if the widget containing the selection has the focus * * Actions can be set in differents groups by changing hundred: * 0 to 99 is a group * 100 to 200 is another group */ void registerGlobalAction(const QString& iIdentifier, QAction* iAction, bool iAddInCollection = true, const QStringList& iListOfTable = QStringList(), int iMinSelection = -2, int iMaxSelection = -1, int iRanking = -1, bool iSelectionMustHaveFocus = false); /** * Get a registered global action * @param iIdentifier identifier of the action * @param iWarnIfNotExist warn if the action does not exist * @return action pointer */ QPointer getGlobalAction(const QString& iIdentifier, bool iWarnIfNotExist = true); /** * Get a list of actions enable for a contextual menu * @param iTable the table * @return the list of actions */ QList > getActionsForContextualMenu(const QString& iTable); /** * Get all registered global actions * @return actions */ QMap > getGlobalActions() const; /** * Get the tips of days * @return the tips of days */ QStringList getTipsOfDay() const; /** * Get the tip of days * @return the tip of days */ QString getTipOfDay() const; /** * Define if the document must be saved when closed * @param iSaveOnClose the save on close mode */ void setSaveOnClose(bool iSaveOnClose); /** * Get all advice * @return the list of advice */ SKGAdviceList getAdvice() const; public Q_SLOTS: /** * Display a message * @param iMessage the message * @param iType the type * @param iAction the associated action * @return the message widget */ KMessageWidget* displayMessage(const QString& iMessage, SKGDocument::MessageType iType = SKGDocument::Information, const QString& iAction = QString()); /** * Display an error message * @param iMessage the error message. If the message is "", the data of the sender will be used * @return the message widget */ KMessageWidget* displayErrorMessage(const QString& iMessage = QString()); /** * This function is called when the application is launched again with new arguments * @param iArgument the arguments * @return the rest of arguments to treat */ QStringList processArguments(const QStringList& iArgument); /** * Set the current page * @param iIndex the current page */ void setCurrentPage(int iIndex); /** * Set the context item visibility * @param iPage index of the page in the pages chooser * @param iVisibility the visibility */ void setContextVisibility(int iPage, bool iVisibility); /** * Set the context item visibility * @param iItem item in the pages chooser * @param iVisibility the visibility */ void setContextVisibility(QListWidgetItem* iItem, bool iVisibility); /** * Open a plugin in a page described in sender()->data() * @return true if the url has been opened */ bool openPage(); /** * Open a plugin in a page * @param iUrl the url like this "skg://plugin_name/?param1=value1¶m2=value2&..." to open a special page * or "skg://action" to trigger an action - * or "http://..." to open a web page + * or "https://..." to open a web page * @param iNewPage to open a new page or not * @return true if the url has been opened */ bool openPage(const QUrl& iUrl, bool iNewPage = true); /** * Open a plugin in a page * @param iUrl the url like this "skg://plugin_name/?param1=value1¶m2=value2&..." to open a special page * or "skg://action/?param1=value1¶m2=value2&..." to trigger an action, parameters can be found as property on the sender QAction - * or "http://..." to open a web page + * or "https://..." to open a web page * if empty then url will be get from sender()->data() * @param iNewPage to open a new page or not * @return true if the url has been opened */ bool openPage(const QString& iUrl, bool iNewPage = true); /** * Open a plugin in a page * @param iPage index of the page in the pages chooser * @param iNewPage to open a new page or not * @return the opened tab */ SKGTabPage* openPage(int iPage, bool iNewPage = true); /** * Open a plugin in a page * @param plugin the plugin * @param index index of the tab to replace or -1 to add a new one * @param parameters parameters of the plugin * @param title title of the page * @param iID id of the new page * @param iSetCurrent to set the new page as the current one * @return the opened tab */ SKGTabPage* openPage(SKGInterfacePlugin* plugin, int index = -1, const QString& parameters = QString(), const QString& title = QString(), const QString& iID = QString(), bool iSetCurrent = true); /** * Switch the pin state of the page * @param iWidget the page to remove (nullptr means current one) */ void switchPinPage(QWidget* iWidget); /** * Close a page * @param iIndex the page to remove (-1 means current one) */ void closePageByIndex(int iIndex = -1); /** * Close a page * @param iWidget the page to remove (nullptr means current one) * @param iForce to close pinned pages too */ void closePage(QWidget* iWidget, bool iForce = false); /** * Close the current page */ void closeCurrentPage(); /** * Close all other pages * @param iWidget the page to keep (nullptr means current one) */ void closeAllOtherPages(QWidget* iWidget); /** * Close all pages * @param iForce to close pinned pages too */ void closeAllPages(bool iForce = false); /** * Force the refresh of the object. */ void refresh(); /** * Send notifications corresponding to the transaction. * @param iTransaction the transaction identifier */ void notify(int iTransaction = 0); /** * Open settings panel on dedicated page. * @param iPluginName the name of the plugin (pluginInterface->objectName()) */ void optionsPreferences(const QString& iPluginName = QString()); /** * Unregister a global action * @param iAction action pointer */ void unRegisterGlobalAction(QObject* iAction); Q_SIGNALS: /** * This signal is sent when a new page is opened. */ void pageOpened(); /** * This signal is sent when a new page is opened. */ void pageClosed(); /** * This signal is sent when the current page changes. */ void currentPageChanged(); /** * This signal is sent when settings changes. */ void settingsChanged(); /** * This signal is sent when selection changes. */ void selectionChanged(); protected: /** * event * @param e event */ void changeEvent(QEvent* e) override; /** * Event filtering * @param iObject object * @param iEvent event * @return In your reimplementation of this function, if you want to filter the event out, i.e. stop it being handled further, return true; otherwise return false. */ bool eventFilter(QObject* iObject, QEvent* iEvent) override; private Q_SLOTS: KMessageWidget* getMessageWidget(const QString& iMessage, SKGDocument::MessageType iType, const QString& iAction, bool iAutoKillOnClick); void showMenu(QPoint iPos); void onSettingsChanged(); void onCancelCurrentAction(); void onQuitAction(); void addTab(); void onBeforeOpenContext(); void onOpenContext(); void saveDefaultState(); void resetDefaultState(); void overwriteBookmarkState(); void enableEditor(); void onPrevious(); void onNext(); void onReopenLastClosed(); void onFullScreen(); void onShowPreviousMenu(); void onShowNextMenu(); void onZoomChanged(); void onShowMenuBar(); void onShowButtonMenu(); void onHideContextItem(); void onShowAllContextItems(); void onLockDocks(); void onUnlockDocks(); void onConfigureNotifications(); void onClearMessages(); void onMigrateToSQLCipher(); private: Q_DISABLE_COPY(SKGMainPanel) void setupActions(); SKGMainPanelPrivate* const d; }; #endif // SKGMAINPANEL_H diff --git a/skgbasegui/skgtablewithgraph.cpp b/skgbasegui/skgtablewithgraph.cpp index 4035fc897..41735a3a2 100644 --- a/skgbasegui/skgtablewithgraph.cpp +++ b/skgbasegui/skgtablewithgraph.cpp @@ -1,2850 +1,2850 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ /** @file * A table with graph with more features. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgtablewithgraph.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "skgcolorbutton.h" #include "skgcombobox.h" #include "skggraphicsscene.h" #include "skgmainpanel.h" #include "skgservices.h" #include "skgtraces.h" #include "skgtreemap.h" /** * Data identifier for value */ static const int DATA_VALUE = 12; /** * Data identifier for color */ static const int DATA_COLOR_H = 11; /** * Data identifier for color */ static const int DATA_COLOR_S = 12; /** * Data identifier for color */ static const int DATA_COLOR_V = 13; /** * Data identifier for Z value */ static const int DATA_Z_VALUE = 14; /** * Data identifier for mode */ static const int DATA_MODE = 15; /** * Alpha value */ static const int ALPHA = 200; /** * Box size */ static const double BOX_SIZE = 120.0; /** * Box margin */ static const double BOX_MARGIN = 10.0; #define cloneAction(MENU, ACTION, SLOTTOCALL) \ auto act = (MENU)->addAction((ACTION)->icon(), (ACTION)->text()); \ act->setCheckable(true); \ act->setChecked((ACTION)->isChecked()); \ connect(act, &QAction::triggered, this, SLOTTOCALL); \ connect(act, &QAction::toggled, ACTION, &QAction::setChecked); \ connect(ACTION, &QAction::toggled, act, &QAction::setChecked); SKGTableWithGraph::SKGTableWithGraph() : SKGTableWithGraph(nullptr) {} SKGTableWithGraph::SKGTableWithGraph(QWidget* iParent) : QWidget(iParent), m_scene(nullptr), m_additionalInformation(NONE), m_nbVirtualColumns(0), m_selectable(true), m_toolBarVisible(true), m_graphTypeVisible(true), m_limitVisible(true), m_averageVisible(true), m_linearRegressionVisible(true), m_paretoVisible(false), m_legendVisible(false), m_graphVisible(true), m_tableVisible(true), m_textVisible(false), m_zeroVisible(true), m_decimalsVisible(true), m_shadow(true), m_mainMenu(nullptr), m_indexSum(-1), m_indexAverage(-1), m_indexMin(-1), m_indexLinearRegression(-1), m_sortOrder(Qt::AscendingOrder), m_sortColumn(0) { m_axisColor = Qt::gray; m_gridColor = Qt::lightGray; m_minColor = Qt::red; m_maxColor = Qt::green; m_paretoColor = Qt::darkRed; m_averageColor = Qt::blue; m_tendencyColor = Qt::darkYellow; m_backgroundColor = Qt::white; m_textColor = Qt::black; m_NegativeColor = KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText); m_WhiteColor = QBrush(Qt::white); ui.setupUi(this); ui.kTextEdit->hide(); ui.kFilterEdit->setPlaceholderText(i18n("Search")); m_displayMode = new SKGComboBox(this); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar-stacked")), i18nc("Noun, a type of graph, with bars stacked upon each other", "Stack of lines"), static_cast(STACK)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar-stacked")), i18nc("Noun, a type of graph, with bars stacked upon each other", "Stack of columns"), static_cast(STACKCOLUMNS)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar")), i18nc("Noun, a type of graph, with bars placed besides each other", "Histogram"), static_cast(HISTOGRAM)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-scatter")), i18nc("Noun, a type of graph with only points", "Point"), static_cast(POINT)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-line")), i18nc("Noun, a type of graph with only lines", "Line"), static_cast(LINE)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-area-stacked")), i18nc("Noun, a type of graph, with lines stacked upon each other", "Stacked area"), static_cast(STACKAREA)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("skg-chart-bubble")), i18nc("Noun, a type of graph, with bubbles", "Bubble"), static_cast(BUBBLE)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-pie")), i18nc("Noun, a type of graph that looks like a sliced pie", "Pie"), static_cast(PIE)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-ring")), i18nc("Noun, a type of graph that looks like concentric slices of a pie (a la filelight)", "Concentric pie"), static_cast(CONCENTRICPIE)); m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("map-flat")), i18nc("Noun, a type of graph that looks treemap", "Treemap"), static_cast(TREEMAP)); ui.graphicView->addToolbarWidget(m_displayMode); ui.kShow->addItem(QStringLiteral("table"), i18n("Table"), QStringLiteral("view-list-details"), QString(), QString(), QStringLiteral("text"), QStringLiteral("graph"), QString(), Qt::META + Qt::Key_T); ui.kShow->addItem(QStringLiteral("graph"), i18n("Graph"), QStringLiteral("office-chart-pie"), QString(), QString(), QStringLiteral("text"), QStringLiteral("table"), QString(), Qt::META + Qt::Key_G); ui.kShow->addItem(QStringLiteral("text"), i18n("Text"), QStringLiteral("view-list-text"), QString(), QString(), QStringLiteral("table;graph"), QStringLiteral("table;graph"), QString(), Qt::META + Qt::Key_R); ui.kShow->setDefaultState(QStringLiteral("table;graph")); connect(ui.kShow, &SKGShow::stateChanged, this, &SKGTableWithGraph::onDisplayModeChanged, Qt::QueuedConnection); m_timer.setSingleShot(true); connect(&m_timer, &QTimer::timeout, this, &SKGTableWithGraph::refresh, Qt::QueuedConnection); m_timerRedraw.setSingleShot(true); connect(&m_timerRedraw, &QTimer::timeout, this, &SKGTableWithGraph::redrawGraph, Qt::QueuedConnection); // Build contextual menu ui.kTable->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui.kTable, &SKGTableWidget::customContextMenuRequested, this, &SKGTableWithGraph::showMenu); m_mainMenu = new QMenu(ui.kTable); QAction* actExport = m_mainMenu->addAction(SKGServices::fromTheme(QStringLiteral("document-export")), i18nc("Noun, user action", "Export...")); connect(actExport, &QAction::triggered, this, &SKGTableWithGraph::onExport); // Add graph mode in menu getGraphContextualMenu()->addSeparator(); auto displayModeMenu = new SKGComboBox(this); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar-stacked")), i18nc("Noun, a type of graph, with bars stacked upon each other", "Stack of lines"), static_cast(STACK)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar-stacked")), i18nc("Noun, a type of graph, with bars stacked upon each other", "Stack of columns"), static_cast(STACKCOLUMNS)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar")), i18nc("Noun, a type of graph, with bars placed besides each other", "Histogram"), static_cast(HISTOGRAM)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-scatter")), i18nc("Noun, a type of graph with only points", "Point"), static_cast(POINT)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-line")), i18nc("Noun, a type of graph with only lines", "Line"), static_cast(LINE)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-area-stacked")), i18nc("Noun, a type of graph, with lines stacked upon each other", "Stacked area"), static_cast(STACKAREA)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("skg-chart-bubble")), i18nc("Noun, a type of graph, with bubbles", "Bubble"), static_cast(BUBBLE)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-pie")), i18nc("Noun, a type of graph that looks like a sliced pie", "Pie"), static_cast(PIE)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-ring")), i18nc("Noun, a type of graph that looks like concentric slices of a pie (a la filelight)", "Concentric pie"), static_cast(CONCENTRICPIE)); displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("map-flat")), i18nc("Noun, a type of graph that looks treemap", "Treemap"), static_cast(TREEMAP)); m_displayModeWidget = new QWidgetAction(this); m_displayModeWidget->setDefaultWidget(displayModeMenu); getGraphContextualMenu()->addAction(m_displayModeWidget); connect(displayModeMenu, static_cast(&SKGComboBox::currentIndexChanged), m_displayMode, &SKGComboBox::setCurrentIndex); connect(m_displayMode, static_cast(&SKGComboBox::currentIndexChanged), displayModeMenu, &SKGComboBox::setCurrentIndex); // Reset Colors QAction* resetColorsAction = m_mainMenu->addAction(SKGServices::fromTheme(QStringLiteral("format-fill-color")), i18nc("Noun, user action", "Reset default colors")); connect(resetColorsAction, &QAction::triggered, this, &SKGTableWithGraph::resetColors); getGraphContextualMenu()->addAction(resetColorsAction); // Add item in menu of the graph m_allPositiveMenu = getGraphContextualMenu()->addAction(SKGServices::fromTheme(QStringLiteral("go-up-search")), i18nc("Noun, user action", "All values in positive")); if (m_allPositiveMenu != nullptr) { m_allPositiveMenu->setCheckable(true); } connect(m_allPositiveMenu, &QAction::toggled, this, &SKGTableWithGraph::redrawGraphDelayed, Qt::QueuedConnection); // Add item in menu of the graph m_actShowLimits = getGraphContextualMenu()->addAction(i18nc("Noun, user action", "Show limits")); if (m_actShowLimits != nullptr) { m_actShowLimits->setCheckable(true); m_actShowLimits->setChecked(m_limitVisible); connect(m_actShowLimits, &QAction::triggered, this, &SKGTableWithGraph::switchLimitsVisibility); // Clone action on table contextual menu cloneAction(m_mainMenu, m_actShowLimits, &SKGTableWithGraph::switchLimitsVisibility) } // Add item in menu of the graph m_actShowAverage = getGraphContextualMenu()->addAction(i18nc("Noun, user action", "Show average")); if (m_actShowAverage != nullptr) { m_actShowAverage->setCheckable(true); m_actShowAverage->setChecked(m_averageVisible); connect(m_actShowAverage, &QAction::triggered, this, &SKGTableWithGraph::switchAverageVisibility); // Clone action on table contextual menu cloneAction(m_mainMenu, m_actShowAverage, &SKGTableWithGraph::switchAverageVisibility) } // Add item in menu of the graph m_actShowLinearRegression = getGraphContextualMenu()->addAction(i18nc("Noun, user action", "Show tendency line")); if (m_actShowLinearRegression != nullptr) { m_actShowLinearRegression->setCheckable(true); m_actShowLinearRegression->setChecked(m_linearRegressionVisible); connect(m_actShowLinearRegression, &QAction::triggered, this, &SKGTableWithGraph::switchLinearRegressionVisibility); // Clone action on table contextual menu cloneAction(m_mainMenu, m_actShowLinearRegression, &SKGTableWithGraph::switchLinearRegressionVisibility) } // Add item in menu of the graph m_actShowPareto = getGraphContextualMenu()->addAction(i18nc("Noun, user action", "Show Pareto curve")); if (m_actShowPareto != nullptr) { m_actShowPareto->setCheckable(true); m_actShowPareto->setChecked(m_paretoVisible); connect(m_actShowPareto, &QAction::triggered, this, &SKGTableWithGraph::switchParetoVisibility); } // Add item in menu of the graph m_actShowLegend = getGraphContextualMenu()->addAction(SKGServices::fromTheme(QStringLiteral("help-contents")), i18nc("Noun, user action", "Show legend")); if (m_actShowLegend != nullptr) { m_actShowLegend->setCheckable(true); m_actShowLegend->setChecked(m_legendVisible); connect(m_actShowLegend, &QAction::triggered, this, &SKGTableWithGraph::switchLegendVisibility); } // Add item in menu of the graph m_actShowZero = getGraphContextualMenu()->addAction(SKGServices::fromTheme(QStringLiteral("labplot-xy-plot-two-axes-centered-origin")), i18nc("Noun, user action", "Show origin")); if (m_actShowZero != nullptr) { m_actShowZero->setCheckable(true); m_actShowZero->setChecked(m_zeroVisible); connect(m_actShowZero, &QAction::triggered, this, &SKGTableWithGraph::swithOriginVisibility); } // Add item in menu of the graph m_actShowDecimal = getGraphContextualMenu()->addAction(SKGServices::fromTheme(QStringLiteral("format-precision-less")), i18nc("Noun, user action", "Show decimals")); if (m_actShowDecimal != nullptr) { m_actShowDecimal->setCheckable(true); m_actShowDecimal->setChecked(m_decimalsVisible); connect(m_actShowDecimal, &QAction::triggered, this, &SKGTableWithGraph::swithDecimalsVisibility); // Clone action on table contextual menu cloneAction(m_mainMenu, m_actShowDecimal, &SKGTableWithGraph::swithDecimalsVisibility) } // Set headers parameters QHeaderView* verticalHeader = ui.kTable->verticalHeader(); if (verticalHeader != nullptr) { verticalHeader->hide(); verticalHeader->setDefaultSectionSize(verticalHeader->minimumSectionSize()); // verticalHeader->setSectionResizeMode(QHeaderView::ResizeToContents); } ui.kTable->setSortingEnabled(false); // sort must be enable for refresh method QHeaderView* horizontalHeader = ui.kTable->horizontalHeader(); if (horizontalHeader != nullptr) { horizontalHeader->setSectionResizeMode(QHeaderView::ResizeToContents); horizontalHeader->show(); horizontalHeader->setSortIndicatorShown(true); horizontalHeader->setSortIndicator(m_sortColumn, m_sortOrder); connect(horizontalHeader, &QHeaderView::sortIndicatorChanged, this, &SKGTableWithGraph::refresh); } connect(ui.kTable->horizontalScrollBar(), &QScrollBar::valueChanged, this, &SKGTableWithGraph::onHorizontalScrollBarChanged); connect(m_displayMode, static_cast(&SKGComboBox::currentTextChanged), this, &SKGTableWithGraph::redrawGraphDelayed, Qt::QueuedConnection); connect(ui.graphicView, &SKGGraphicsView::resized, this, &SKGTableWithGraph::redrawGraphDelayed, Qt::QueuedConnection); connect(ui.kTable, &SKGTableWidget::cellDoubleClicked, this, &SKGTableWithGraph::onDoubleClick); connect(ui.kTable, &SKGTableWidget::itemSelectionChanged, this, &SKGTableWithGraph::onSelectionChanged); connect(ui.kFilterEdit, &QLineEdit::textChanged, this, &SKGTableWithGraph::onFilterModified); #ifdef SKG_WEBENGINE connect(ui.kTextEdit, &SKGWebView::linkClicked, this, &SKGTableWithGraph::onLinkClicked); #else ui.kTextEdit->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); connect(ui.kTextEdit, &SKGWebView::linkClicked, this, &SKGTableWithGraph::onLinkClicked); #endif } SKGTableWithGraph::~SKGTableWithGraph() { delete m_scene; m_scene = nullptr; m_mainMenu = nullptr; m_actShowLimits = nullptr; m_actShowLinearRegression = nullptr; m_actShowPareto = nullptr; m_displayModeWidget = nullptr; m_displayMode = nullptr; } void SKGTableWithGraph::onHorizontalScrollBarChanged(int iValue) { QHeaderView* verticalHeader = ui.kTable->verticalHeader(); if (verticalHeader != nullptr) { verticalHeader->setVisible(iValue > 0); } } bool SKGTableWithGraph::isGraphVisible() const { return m_graphVisible; } bool SKGTableWithGraph::isTableVisible() const { return m_tableVisible; } bool SKGTableWithGraph::isTextReportVisible() const { return m_textVisible; } SKGShow* SKGTableWithGraph::getShowWidget() const { return ui.kShow; } void SKGTableWithGraph::setAverageColor(const QColor& iColor) { m_averageColor = iColor; } void SKGTableWithGraph::setAxisColor(const QColor& iColor) { m_axisColor = iColor; } void SKGTableWithGraph::setGridColor(const QColor& iColor) { m_gridColor = iColor; } void SKGTableWithGraph::setMaxColor(const QColor& iColor) { m_maxColor = iColor; } void SKGTableWithGraph::setParetoColor(const QColor& iColor) { m_paretoColor = iColor; } void SKGTableWithGraph::setMinColor(const QColor& iColor) { m_minColor = iColor; } void SKGTableWithGraph::setTendencyColor(const QColor& iColor) { m_tendencyColor = iColor; } void SKGTableWithGraph::setOutlineColor(const QColor& iColor) { m_outlineColor = iColor; } void SKGTableWithGraph::setBackgroundColor(const QColor& iColor) { m_backgroundColor = iColor; } void SKGTableWithGraph::setTextColor(const QColor& iColor) { m_textColor = iColor; } void SKGTableWithGraph::setAntialiasing(bool iAntialiasing) { ui.graphicView->setAntialiasing(iAntialiasing); } void SKGTableWithGraph::setGraphTypeSelectorVisible(bool iVisible) { if (m_graphTypeVisible != iVisible) { m_graphTypeVisible = iVisible; if (m_displayMode != nullptr) { m_displayMode->setVisible(m_graphTypeVisible); } if (m_displayModeWidget != nullptr) { m_displayModeWidget->setVisible(m_graphTypeVisible); } Q_EMIT modified(); } } bool SKGTableWithGraph::isGraphTypeSelectorVisible() const { return m_graphTypeVisible; } bool SKGTableWithGraph::switchLimitsVisibility() { m_limitVisible = !m_limitVisible; refresh(); return m_limitVisible; } bool SKGTableWithGraph::switchAverageVisibility() { m_averageVisible = !m_averageVisible; refresh(); return m_averageVisible; } bool SKGTableWithGraph::switchLegendVisibility() { m_legendVisible = !m_legendVisible; redrawGraphDelayed(); return m_legendVisible; } bool SKGTableWithGraph::swithOriginVisibility() { m_zeroVisible = !m_zeroVisible; redrawGraphDelayed(); return m_zeroVisible; } bool SKGTableWithGraph::swithDecimalsVisibility() { m_decimalsVisible = !m_decimalsVisible; refresh(); return m_decimalsVisible; } bool SKGTableWithGraph::switchLinearRegressionVisibility() { m_linearRegressionVisible = !m_linearRegressionVisible; refresh(); return m_linearRegressionVisible; } bool SKGTableWithGraph::switchParetoVisibility() { m_paretoVisible = !m_paretoVisible; redrawGraphDelayed(); return m_paretoVisible; } QMenu* SKGTableWithGraph::getTableContextualMenu() const { return m_mainMenu; } QMenu* SKGTableWithGraph::getGraphContextualMenu() const { return ui.graphicView->getContextualMenu(); } void SKGTableWithGraph::showMenu(const QPoint iPos) { if (m_mainMenu != nullptr) { m_mainMenu->popup(ui.kTable->mapToGlobal(iPos)); } } QTableWidget* SKGTableWithGraph::table() const { return ui.kTable; } SKGGraphicsView* SKGTableWithGraph::graph() const { return ui.graphicView; } SKGWebView* SKGTableWithGraph::textReport() const { return ui.kTextEdit; } SKGStringListList SKGTableWithGraph::getTable() { // Build table SKGStringListList output; // Get header names int nb2 = ui.kTable->rowCount(); int nb = ui.kTable->columnCount(); output.reserve(nb2 + 1); QStringList cols; cols.reserve(2 * nb); for (int i = 0; i < nb; ++i) { cols.append(ui.kTable->horizontalHeaderItem(i)->text()); cols.append(i18n("%1 (raw)", ui.kTable->horizontalHeaderItem(i)->text())); } output.append(cols); // Get content for (int i = 0; i < nb2; ++i) { QStringList row; row.reserve(nb); for (int j = 0; j < nb; j++) { auto* button = qobject_cast(ui.kTable->cellWidget(i, j)); if (button != nullptr) { row.append(button->text()); row.append(button->color().toRgb().name()); } else { row.append(ui.kTable->item(i, j)->text()); QString raw = ui.kTable->item(i, j)->data(DATA_VALUE).toString(); if (raw.isEmpty()) { raw = ui.kTable->item(i, j)->text(); } row.append(raw); } } output.append(row); } return output; } QString SKGTableWithGraph::getState() { SKGTRACEINFUNC(10) QDomDocument doc(QStringLiteral("SKGML")); QDomElement root = doc.createElement(QStringLiteral("parameters")); doc.appendChild(root); if (ui.graphicView->isVisible() && ui.kTable->isVisible()) { root.setAttribute(QStringLiteral("splitterState"), QString(ui.splitter->saveState().toHex())); } root.setAttribute(QStringLiteral("graphMode"), SKGServices::intToString(static_cast(getGraphType()))); root.setAttribute(QStringLiteral("allPositive"), m_allPositiveMenu->isChecked() ? QStringLiteral("Y") : QStringLiteral("N")); root.setAttribute(QStringLiteral("filter"), ui.kFilterEdit->text()); root.setAttribute(QStringLiteral("limitVisible"), m_limitVisible ? QStringLiteral("Y") : QStringLiteral("N")); root.setAttribute(QStringLiteral("averageVisible"), m_averageVisible ? QStringLiteral("Y") : QStringLiteral("N")); root.setAttribute(QStringLiteral("linearRegressionVisible"), m_linearRegressionVisible ? QStringLiteral("Y") : QStringLiteral("N")); root.setAttribute(QStringLiteral("paretoVisible"), m_paretoVisible ? QStringLiteral("Y") : QStringLiteral("N")); root.setAttribute(QStringLiteral("legendVisible"), m_legendVisible ? QStringLiteral("Y") : QStringLiteral("N")); root.setAttribute(QStringLiteral("zeroVisible"), m_zeroVisible ? QStringLiteral("Y") : QStringLiteral("N")); root.setAttribute(QStringLiteral("decimalsVisible"), m_decimalsVisible ? QStringLiteral("Y") : QStringLiteral("N")); QMapIterator i(m_mapTitleColor); while (i.hasNext()) { i.next(); QDomElement color = doc.createElement(QStringLiteral("color")); root.appendChild(color); color.setAttribute(QStringLiteral("key"), i.key()); color.setAttribute(QStringLiteral("value"), i.value().name()); } QHeaderView* horizontalHeader = ui.kTable->horizontalHeader(); root.setAttribute(QStringLiteral("sortOrder"), SKGServices::intToString(horizontalHeader->sortIndicatorOrder())); root.setAttribute(QStringLiteral("sortColumn"), SKGServices::intToString(horizontalHeader->sortIndicatorSection())); root.setAttribute(QStringLiteral("graphicViewState"), ui.graphicView->getState()); root.setAttribute(QStringLiteral("web"), ui.kTextEdit->getState()); root.setAttribute(QStringLiteral("show"), ui.kShow->getState()); if (ui.kTable->stickHorizontal()) { root.setAttribute(QStringLiteral("stickH"), QStringLiteral("Y")); } if (ui.kTable->stickVertical()) { root.setAttribute(QStringLiteral("stickV"), QStringLiteral("Y")); } return doc.toString(); } void SKGTableWithGraph::setState(const QString& iState) { SKGTRACEINFUNC(10) m_timer.stop(); m_timerRedraw.stop(); QDomDocument doc(QStringLiteral("SKGML")); doc.setContent(iState); QDomElement root = doc.documentElement(); m_mapTitleColor.clear(); QDomNodeList colors = root.elementsByTagName(QStringLiteral("color")); int nb = colors.count(); for (int i = 0; i < nb; ++i) { QDomElement c = colors.at(i).toElement(); m_mapTitleColor[c.attribute(QStringLiteral("key"))] = QColor(c.attribute(QStringLiteral("value"))); } QString splitterStateString = root.attribute(QStringLiteral("splitterState")); if (!splitterStateString.isEmpty()) { ui.splitter->restoreState(QByteArray::fromHex(splitterStateString.toLatin1())); } QString graphModeString = root.attribute(QStringLiteral("graphMode")); QString allPositiveString = root.attribute(QStringLiteral("allPositive")); QString limitVisibleString = root.attribute(QStringLiteral("limitVisible")); QString averageVisibleString = root.attribute(QStringLiteral("averageVisible")); QString legendVisibleString = root.attribute(QStringLiteral("legendVisible")); QString zeroVisibleString = root.attribute(QStringLiteral("zeroVisible")); QString decimalsVisibleString = root.attribute(QStringLiteral("decimalsVisible")); QString linearRegressionVisibleString = root.attribute(QStringLiteral("linearRegressionVisible")); QString paretorVisibleString = root.attribute(QStringLiteral("paretoVisible")); QString sortOrderString = root.attribute(QStringLiteral("sortOrder")); QString sortColumnString = root.attribute(QStringLiteral("sortColumn")); QString graphicViewStateString = root.attribute(QStringLiteral("graphicViewState")); QString webString = root.attribute(QStringLiteral("web")); QString showString = root.attribute(QStringLiteral("show")); ui.kTable->setStickHorizontal(root.attribute(QStringLiteral("stickH")) == QStringLiteral("Y")); ui.kTable->setStickVertical(root.attribute(QStringLiteral("stickV")) == QStringLiteral("Y")); // Default value in case of reset if (graphModeString.isEmpty()) { graphModeString = SKGServices::intToString(LINE); } if (allPositiveString.isEmpty()) { allPositiveString = 'N'; } if (limitVisibleString.isEmpty()) { limitVisibleString = 'Y'; } if (averageVisibleString.isEmpty()) { averageVisibleString = 'Y'; } if (legendVisibleString.isEmpty()) { legendVisibleString = 'N'; } if (linearRegressionVisibleString.isEmpty()) { linearRegressionVisibleString = 'Y'; } if (paretorVisibleString.isEmpty()) { paretorVisibleString = 'N'; } if (sortOrderString.isEmpty()) { sortOrderString = '0'; } if (sortColumnString.isEmpty()) { sortColumnString = '0'; } // Set setGraphType(SKGTableWithGraph::HISTOGRAM); if (m_displayMode != nullptr) { m_displayMode->setCurrentIndex(1); } setGraphType(static_cast(SKGServices::stringToInt(graphModeString))); m_allPositiveMenu->setChecked(allPositiveString == QStringLiteral("Y")); ui.kFilterEdit->setText(root.attribute(QStringLiteral("filter"))); m_limitVisible = (limitVisibleString == QStringLiteral("Y")); if (m_actShowLimits != nullptr) { m_actShowLimits->setChecked(m_limitVisible); } m_averageVisible = (averageVisibleString == QStringLiteral("Y")); if (m_actShowAverage != nullptr) { m_actShowAverage->setChecked(m_averageVisible); } m_legendVisible = (legendVisibleString == QStringLiteral("Y")); if (m_actShowLegend != nullptr) { m_actShowLegend->setChecked(m_legendVisible); } m_zeroVisible = (zeroVisibleString != QStringLiteral("N")); if (m_actShowZero != nullptr) { m_actShowZero->setChecked(m_zeroVisible); } m_decimalsVisible = (decimalsVisibleString != QStringLiteral("N")); if (m_actShowDecimal != nullptr) { m_actShowDecimal->setChecked(m_decimalsVisible); } m_linearRegressionVisible = (linearRegressionVisibleString == QStringLiteral("Y")); if (m_actShowLinearRegression != nullptr) { m_actShowLinearRegression->setChecked(m_linearRegressionVisible); } m_paretoVisible = (paretorVisibleString == QStringLiteral("Y")); if (m_actShowPareto != nullptr) { m_actShowPareto->setChecked(m_paretoVisible); } ui.kTable->setColumnCount(SKGServices::stringToInt(sortColumnString) + 1); QHeaderView* horizontalHeader = ui.kTable->horizontalHeader(); if (horizontalHeader != nullptr) { bool previous = horizontalHeader->blockSignals(true); horizontalHeader->setSortIndicator(SKGServices::stringToInt(sortColumnString), static_cast(SKGServices::stringToInt(sortOrderString))); horizontalHeader->blockSignals(previous); } ui.graphicView->setState(graphicViewStateString); ui.kTextEdit->setState(webString); if (!showString.isEmpty()) { ui.kShow->setState(showString); } } void SKGTableWithGraph::setFilterVisibility(bool iVisibility) const { ui.kToolbar->setVisible(iVisibility); } void SKGTableWithGraph::onFilterModified() { m_timerRedraw.stop(); m_timer.start(300); } void SKGTableWithGraph::onDisplayModeChanged() { QStringList mode = SKGServices::splitCSVLine(ui.kShow->getState()); // Hide all if (m_scene != nullptr) { m_scene->clear(); delete m_scene; } m_scene = new SKGGraphicsScene(); ui.graphicView->setScene(m_scene); ui.graphicView->hide(); ui.kTextEdit->hide(); bool p = ui.kTable->blockSignals(true); ui.kTable->hide(); ui.kTable->blockSignals(p); m_graphVisible = false; m_tableVisible = false; m_textVisible = false; m_mapItemGraphic.clear(); // Show needed widget if (mode.contains(QStringLiteral("table"))) { ui.kTable->show(); m_tableVisible = true; } if (mode.contains(QStringLiteral("graph"))) { ui.graphicView->show(); m_graphVisible = true; redrawGraphDelayed(); } if (mode.contains(QStringLiteral("text"))) { QTimer::singleShot(100, Qt::CoarseTimer, ui.kTextEdit, &SKGWebView::show); m_textVisible = true; redrawText(); } } void SKGTableWithGraph::setData(const SKGStringListList& iData, const SKGServices::SKGUnitInfo& iPrimaryUnit, const SKGServices::SKGUnitInfo& iSecondaryUnit, SKGTableWithGraph::DisplayAdditionalFlag iAdditionalInformation, int iNbVirtualColumn) { SKGTRACEINFUNC(10) m_data = iData; m_primaryUnit = iPrimaryUnit; m_secondaryUnit = iSecondaryUnit; m_additionalInformation = iAdditionalInformation; m_nbVirtualColumns = iNbVirtualColumn; onFilterModified(); } SKGTableWithGraph::DisplayAdditionalFlag SKGTableWithGraph::getAdditionalDisplayMode() const { return m_additionalInformation; } QStringList SKGTableWithGraph::getSumItems(const QString& iString) const { QStringList output; QString current = iString; int index = -1; do { output.insert(0, current); index = current.lastIndexOf(OBJECTSEPARATOR); if (index != -1) { current = current.left(index); } } while (index != -1); return output; } void SKGTableWithGraph::addSums(SKGStringListList& ioTable, int& iNblines) { SKGTRACEINFUNC(10) int nbCols = -1; if (!ioTable.isEmpty()) { nbCols = ioTable.at(0).count(); } // Create a list of sums lines associated to the index where to add them QMap sums; for (int i = 1; i < nbCols; ++i) { QStringList previousHeaderSum; QList sum; QList nbElem; for (int j = 1; j < iNblines; ++j) { double indextoadd = j + 1 - 0.01; QStringList currentHeaderSum = getSumItems(ioTable.at(j).at(0)); if (previousHeaderSum.isEmpty()) { previousHeaderSum = currentHeaderSum; } int nb = qMax(currentHeaderSum.count(), previousHeaderSum.count()); for (int k = 0; k < nb; ++k) { QString chString = currentHeaderSum.value(k); QString phString = previousHeaderSum.value(k); if (chString != phString) { // Add this sum if (nbElem.value(k) > 1) { QStringList thisSum; if (i == 1) { thisSum.push_back(phString); } else { thisSum = sums[indextoadd]; } thisSum.push_back(SKGServices::doubleToString(sum.value(k))); sums[indextoadd] = thisSum; indextoadd -= 0.01; } // Reset sum if (k < sum.count()) { sum[k] = 0; } else { sum.push_back(0); } if (k < nbElem.count()) { nbElem[k] = 0; } else { nbElem.push_back(0); } } if (j < iNblines - 1) { sum = sum.mid(0, previousHeaderSum.count()); nbElem = nbElem.mid(0, previousHeaderSum.count()); QString valstring = ioTable.at(j).at(i); double v = 0.0; if (!valstring.isEmpty()) { v = SKGServices::stringToDouble(valstring); } if (k < sum.count()) { sum[k] += v; } else { sum.push_back(v); } if (k < nbElem.count()) { nbElem[k] = nbElem[k] + 1; } else { nbElem.push_back(1); } } } previousHeaderSum = currentHeaderSum; } } // Add all sums in table QList keys = sums.keys(); std::sort(keys.begin(), keys.end()); int nbkey = keys.count(); for (int i = nbkey - 1; i >= 0; --i) { double key = keys.at(i); ioTable.insert(key, sums[key]); m_sumRows.insert(key, true); ++iNblines; } } void SKGTableWithGraph::refresh() { SKGTRACEINFUNC(10) QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Set parameters for static method QHeaderView* horizontalHeader = ui.kTable->horizontalHeader(); m_sortOrder = horizontalHeader->sortIndicatorOrder(); m_sortColumn = horizontalHeader->sortIndicatorSection(); SKGStringListList groupedTable = m_data; int nbCols = -1; if (!groupedTable.isEmpty()) { nbCols = groupedTable.at(0).count(); } int nbRealCols = nbCols; // Create filtered table { // Build list of criterias SKGServices::SKGSearchCriteriaList criterias = SKGServices::stringToSearchCriterias(ui.kFilterEdit->text()); // Check all lines int nblists = criterias.count(); if ((nblists != 0) && (nbCols != 0)) { for (int i = groupedTable.count() - 1; i >= 1; --i) { // The first line is not filtered because it is the title QStringList line = groupedTable.at(i); // Get title of the line const QString& val = line.at(0); // Filtered bool ok = false; for (int l = 0; l < nblists; ++l) { QStringList words = criterias[l].words; QChar mode = criterias[l].mode; int nbwords = words.count(); bool validateAllWords = true; for (int w = 0; validateAllWords && w < nbwords; ++w) { validateAllWords = val.contains(words.at(w), Qt::CaseInsensitive); } if (mode == '+') { ok |= validateAllWords; } else if (mode == '-' && validateAllWords) { ok = false; } } if (!ok) { groupedTable.removeAt(i); } } } } // Initialise sumRows int nblines = groupedTable.count(); m_sumRows.clear(); for (int j = 0; j < nblines; ++j) { m_sumRows.push_back(false); } // Compute sums line if ((m_additionalInformation & SUM) != 0u) { QStringList sums; sums.reserve(nbCols + 1); sums.push_back(QString()); for (int i = 1; i < nbCols; ++i) { double sum = 0; for (int j = 1; j < nblines; ++j) { QString valstring = groupedTable.at(j).at(i); if (!valstring.isEmpty()) { sum += SKGServices::stringToDouble(valstring); } } sums.push_back(SKGServices::doubleToString(sum)); } groupedTable.push_back(sums); m_sumRows.push_back(true); ++nblines; } // Compute sub sums lines if ((m_additionalInformation & SUM) != 0u && m_sortColumn == 0 && m_sortOrder == Qt::AscendingOrder) { addSums(groupedTable, nblines); } // Compute sum and average column m_indexSum = -1; m_indexAverage = -1; m_indexMin = -1; m_indexLinearRegression = -1; if (nbCols > 2 && ((m_additionalInformation & SUM) != 0u || (m_averageVisible && (m_additionalInformation & AVERAGE) != 0u) || (m_limitVisible && (m_additionalInformation & LIMITS) != 0u))) { SKGTRACEINFUNC(10) // Add title QStringList newLine = groupedTable.at(0); if ((m_additionalInformation & SUM) != 0u) { m_indexSum = newLine.count(); newLine.push_back(i18nc("Noun, the numerical sum of a list of values", "Sum")); } if (m_averageVisible && (m_additionalInformation & AVERAGE) != 0u) { m_indexAverage = newLine.count(); newLine.push_back(i18nc("Noun, the numerical average of a list of values", "Average")); } if (m_limitVisible && (m_additionalInformation & LIMITS) != 0u) { m_indexMin = newLine.count(); newLine.push_back(i18nc("Noun, the minimum value of a list of values", "Min")); newLine.push_back(i18nc("Noun, the maximum value of a list of values", "Max")); } if (m_linearRegressionVisible) { m_indexLinearRegression = newLine.count(); newLine.push_back(i18nc("Noun", "Tendency line")); } groupedTable.replace(0, newLine); for (int i = 1; i < nblines; ++i) { QStringList newLine2 = groupedTable.at(i); double sumy = 0; double sumx = 0; double sumx2 = 0; double sumxy = 0; double min = 10e20; double max = -10e20; int nbVals = 0; for (int j = 1; j < nbCols - m_nbVirtualColumns; ++j) { const QString& valstring = newLine2.at(j); if (!valstring.isEmpty()) { double v = SKGServices::stringToDouble(valstring); sumx += j; sumx2 += j * j; sumy += v; sumxy += j * v; min = qMin(min, v); max = qMax(max, v); ++nbVals; } } if ((m_additionalInformation & SUM) != 0u) { newLine2.push_back(SKGServices::doubleToString(sumy)); } if (m_averageVisible && (m_additionalInformation & AVERAGE) != 0u) { if (nbVals != 0) { newLine2.push_back(SKGServices::doubleToString(sumy / nbVals)); } else { newLine2.push_back(QStringLiteral("0")); } } if (m_limitVisible && (m_additionalInformation & LIMITS) != 0u) { if (nbVals != 0) { newLine2.push_back(SKGServices::doubleToString(min)); newLine2.push_back(SKGServices::doubleToString(max)); } else { newLine2.push_back(QStringLiteral("0")); newLine2.push_back(QStringLiteral("0")); } } if (nbVals != 0) { double s2x = sumx2 / nbVals - sumx * sumx / (nbVals * nbVals); double sxy = sumxy / nbVals - (sumx / nbVals) * (sumy / nbVals); double a = (s2x != 0.0 ? sxy / s2x : 0.0); double b = sumy / nbVals - a * sumx / nbVals; newLine2.push_back("y=" % SKGServices::doubleToString(a) % "*x+" % SKGServices::doubleToString(b)); } else { newLine2.push_back(QStringLiteral("y=0")); } groupedTable.replace(i, newLine2); } if (m_linearRegressionVisible) { ++nbCols; } if ((m_additionalInformation & SUM) != 0u) { ++nbCols; } if (m_averageVisible && (m_additionalInformation & AVERAGE) != 0u) { ++nbCols; } if (m_limitVisible && (m_additionalInformation & LIMITS) != 0u) { nbCols += 2; } } // Sort lines if (m_sortColumn != 0 || m_sortOrder != Qt::AscendingOrder) { SKGTRACEINFUNC(10) // Extract title and sums if (!groupedTable.isEmpty()) { QStringList fist = groupedTable.takeFirst(); QStringList last; if ((m_additionalInformation & SUM) != 0u && !groupedTable.isEmpty()) { last = groupedTable.takeLast(); } // Sort QCollator comp; comp.setCaseSensitivity(Qt::CaseInsensitive); std::sort(groupedTable.begin(), groupedTable.end(), [&](const QStringList & s1, const QStringList & s2) { if (m_sortColumn >= s1.count()) { m_sortColumn = s1.count() - 1; } if (m_sortColumn >= 0) { const QString& v1 = s1.at(m_sortColumn); const QString& v2 = s2.at(m_sortColumn); if (m_sortColumn == 0) { int v = comp.compare(v1, v2); return (m_sortOrder != 0u ? v > 0 : v < 0); } double vd1 = SKGServices::stringToDouble(v1); double vd2 = SKGServices::stringToDouble(v2); return (m_sortOrder != 0u ? vd1 > vd2 : vd1 < vd2); } return false; }); // Add title and sums groupedTable.insert(0, fist); if ((m_additionalInformation & SUM) != 0u) { groupedTable.push_back(last); } } } IFSKGTRACEL(10) { QStringList dump = SKGServices::tableToDump(groupedTable, SKGServices::DUMP_TEXT); int nbl = dump.count(); for (int i = 0; i < nbl; ++i) { SKGTRACE << dump.at(i) << endl; } } // Fill table { SKGTRACEINFUNC(10) int nbRealColumns = nbRealCols - m_nbVirtualColumns; ui.kTable->hide(); QHeaderView* hHeader = ui.kTable->horizontalHeader(); if (hHeader != nullptr) { hHeader->setSectionResizeMode(QHeaderView::Fixed); // Needed to improve performances of setHorizontalHeaderLabels } ui.kTable->clear(); ui.kTable->setRowCount(nblines - 1); ui.kTable->setColumnCount(nbCols); for (int i = 0; i < nblines; ++i) { const QStringList& line = groupedTable.at(i); if (i == 0) { SKGTRACEINFUNC(10) // Set header ui.kTable->setHorizontalHeaderLabels(line); } else { for (int j = 0; j < nbCols; ++j) { const QString& val = line.at(j); QTableWidgetItem* item; if (j == 0) { // Create the line header if (m_sumRows.at(i)) { item = new QTableWidgetItem(val.isEmpty() ? i18nc("Noun, the numerical sum of a list of values", "Sum") : i18nc("Noun, the numerical sum of a list of values", "Sum of '%1'", val)); item->setData(1, val); QFont f = item->font(); f.setBold(true); item->setFont(f); if (m_indexLinearRegression != -1) { item->setData(DATA_VALUE, line.at(m_indexLinearRegression)); } ui.kTable->setItem(i - 1, j, item); // Set header ui.kTable->setVerticalHeaderItem(i - 1, new QTableWidgetItem(*item)); } else { // New color selector QColor defaultColor; int color_h = (240 + 360 * (i - 1) / nblines) % 360; // First color is blue to be compliant with min (red) and max (green) defaultColor = QColor::fromHsv(color_h, 255, 255); QColor color; if (m_mapTitleColor.contains(val)) { color = m_mapTitleColor[val]; } else { color = defaultColor; m_mapTitleColor[val] = color; } auto colorSelector = new SKGColorButton(this); colorSelector->setText(val); colorSelector->setToolTip(val); colorSelector->setColor(color); colorSelector->setDefaultColor(defaultColor); connect(colorSelector, &SKGColorButton::changed, this, &SKGTableWithGraph::onChangeColor); ui.kTable->setCellWidget(i - 1, 0, colorSelector); // Set header auto itemTmp = new QTableWidgetItem(val); itemTmp->setBackground(color); itemTmp->setToolTip(val); ui.kTable->setVerticalHeaderItem(i - 1, itemTmp); } } else { // Add a value QString tooltip = line.at(0) % '\n' % groupedTable.at(0).at(j); if (!val.isEmpty()) { if (j == m_indexLinearRegression) { // A linear regression value item = new QTableWidgetItem(val); } else { // A single value double vald = SKGServices::stringToDouble(val); QString vals = SKGServices::toCurrencyString(vald, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0); tooltip += '\n' % vals; item = new QTableWidgetItem(vals); item->setToolTip(tooltip); if (!m_secondaryUnit.Symbol.isEmpty() && (m_secondaryUnit.Value != 0.0)) { item->setToolTip(tooltip % '\n' % SKGServices::toCurrencyString(vald / m_secondaryUnit.Value, m_secondaryUnit.Symbol, m_decimalsVisible ? m_secondaryUnit.NbDecimal : 0)); } item->setData(DATA_VALUE, vald); item->setTextAlignment(Qt::AlignRight); if (vald < 0) { item->setForeground(m_NegativeColor); } if (m_sumRows.at(i)) { QFont f = item->font(); f.setBold(true); item->setFont(f); } } } else { // An empty value item = new QTableWidgetItem(QString()); item->setToolTip(tooltip); item->setData(DATA_VALUE, ""); } item->setFlags((j >= nbRealColumns && j != m_indexSum) || ui.kTable->horizontalHeaderItem(j)->text() == QStringLiteral("0000") ? Qt::NoItemFlags : Qt::ItemIsEnabled | Qt::ItemIsSelectable); ui.kTable->setItem(i - 1, j, item); } } } } // Refresh graphic view redrawGraph(); // Refresh text area redrawText(); if (hHeader != nullptr) { hHeader->setSectionResizeMode(QHeaderView::ResizeToContents); } if (m_tableVisible) { ui.kTable->show(); } if ((hHeader != nullptr) && (hHeader->count() != 0)) { hHeader->resizeSection(0, 100); hHeader->setSectionResizeMode(0, QHeaderView::Interactive); } } QApplication::restoreOverrideCursor(); } void SKGTableWithGraph::onChangeColor() { auto* colorButton = qobject_cast(sender()); if (colorButton != nullptr) { m_mapTitleColor[colorButton->text()] = colorButton->color(); refresh(); } } void SKGTableWithGraph::resetColors() { m_mapTitleColor.clear(); refresh(); } void SKGTableWithGraph::onSelectionChanged() { _SKGTRACEINFUNC(10) if (m_graphVisible) { // Unset color on previous selection int nbRow = ui.kTable->rowCount(); int nbCol = ui.kTable->columnCount(); for (int r = 0; r < nbRow; ++r) { for (int c = 0; c < nbCol; ++c) { QTableWidgetItem* previous = ui.kTable->item(r, c); if (previous != nullptr) { QGraphicsItem* val = m_mapItemGraphic.value(previous); if (val != nullptr) { auto* graphicItem = qgraphicsitem_cast(val); if (graphicItem != nullptr) { QColor color = QColor::fromHsv(graphicItem->data(DATA_COLOR_H).toInt(), graphicItem->data(DATA_COLOR_S).toInt(), graphicItem->data(DATA_COLOR_V).toInt()); color.setAlpha(ALPHA); if (graphicItem->data(DATA_MODE).toInt() == 1) { QPen pen = graphicItem->pen(); pen.setColor(color); graphicItem->setPen(pen); } else { graphicItem->setBrush(QBrush(color)); } graphicItem->setZValue(graphicItem->data(DATA_Z_VALUE).toReal()); if (graphicItem->isSelected()) { graphicItem->setSelected(false); } } } } } } // Set highlight color on current selection QList selected = ui.kTable->selectedItems(); int nb = selected.count(); for (int i = 0; i < nb; ++i) { QTableWidgetItem* current = selected.at(i); if (current != nullptr) { QGraphicsItem* val = m_mapItemGraphic.value(current); auto* graphicItem = qgraphicsitem_cast(val); if (graphicItem != nullptr) { if (graphicItem->data(DATA_MODE).toInt() == 1) { QPen pen = graphicItem->pen(); pen.setColor(QApplication::palette().color(QPalette::Highlight)); graphicItem->setPen(pen); } else { graphicItem->setBrush(QBrush(QApplication::palette().color(QPalette::Highlight))); } graphicItem->setZValue(15); graphicItem->setSelected(true); graphicItem->ensureVisible(); } } } } emit selectionChanged(); } void SKGTableWithGraph::onDoubleClickGraph() { if (m_scene != nullptr) { // Get selection QList selectedGraphItems = m_scene->selectedItems(); if (!selectedGraphItems.isEmpty()) { Q_EMIT cellDoubleClicked(selectedGraphItems[0]->data(1).toInt(), selectedGraphItems[0]->data(2).toInt()); } } } void SKGTableWithGraph::onDoubleClick(int row, int column) { Q_EMIT cellDoubleClicked(row, column); } void SKGTableWithGraph::onLinkClicked(const QUrl& url) { - QString path = url.toString().remove(QStringLiteral("http://linkclicked/")); + QString path = url.toString().remove(QStringLiteral("https://linkclicked/")); QStringList items = SKGServices::splitCSVLine(path, ','); if (items.count() == 2) { Q_EMIT cellDoubleClicked(SKGServices::stringToInt(items[0]), SKGServices::stringToInt(items[1])); } } void SKGTableWithGraph::onSelectionChangedInGraph() { _SKGTRACEINFUNC(10) if (m_scene != nullptr) { bool previous = ui.kTable->blockSignals(true); ui.kTable->clearSelection(); // Get selection QList selectedGraphItems = m_scene->selectedItems(); int nb = selectedGraphItems.count(); for (int i = 0; i < nb; ++i) { ui.kTable->setCurrentCell(selectedGraphItems.at(i)->data(1).toInt(), selectedGraphItems.at(i)->data(2).toInt(), QItemSelectionModel::Select); } ui.kTable->blockSignals(previous); previous = m_scene->blockSignals(true); onSelectionChanged(); m_scene->blockSignals(previous); } } void SKGTableWithGraph::redrawGraphDelayed() { m_timerRedraw.start(300); } void SKGTableWithGraph::redrawText() { if (!m_textVisible) { return; } SKGTRACEINFUNC(10) QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QString html = QStringLiteral("" "" "" "" "" "" "" "" "" ""); // Dump header int nbCols = ui.kTable->columnCount(); for (int i = 0; i < nbCols; ++i) { QTableWidgetItem* item = ui.kTable->horizontalHeaderItem(i); if (item != nullptr) { html += R"("; } } html += QStringLiteral(""); // Dump values int nbLines = ui.kTable->rowCount(); for (int j = 0; j < nbLines; ++j) { html += QStringLiteral("'; for (int i = 0; i < nbCols; ++i) { QTableWidgetItem* item = ui.kTable->item(j, i); if (item != nullptr) { bool red = (item->data(DATA_VALUE).toDouble() < 0); html += QStringLiteral(""; } else { auto* colorButton = qobject_cast(ui.kTable->cellWidget(j, i)); if (colorButton != nullptr) { html += ""; } } } html += QStringLiteral(""); } html += QStringLiteral("
)" % item->text() % "
") % (red ? "" : ""); if ((item->flags()&Qt::ItemIsSelectable) != 0u) { - html += ""; + html += ""; } html += item->text(); if ((item->flags()&Qt::ItemIsSelectable) != 0u) { html += QStringLiteral(""); } html += QString(red ? QStringLiteral("") : QString()) % "" % colorButton->text() % "
"); html += QStringLiteral(""); ui.kTextEdit->setHtml(html); QApplication::restoreOverrideCursor(); } void SKGTableWithGraph::redrawGraph() { SKGTRACEINFUNC(10) m_mapItemGraphic.clear(); if (!m_graphVisible) { return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); ui.graphicView->hide(); ui.kTable->hide(); // Recreate scene if (m_scene != nullptr) { SKGTRACEINFUNC(10) m_scene->clear(); delete m_scene; } m_scene = new SKGGraphicsScene(); { SKGTRACEINFUNC(10) m_scene->setBackgroundBrush(m_backgroundColor); // Get current selection int crow = ui.kTable->currentRow(); int ccolumn = ui.kTable->currentColumn(); // Get nb columns and rows int nbRows = ui.kTable->rowCount(); int nbRealRows = nbRows; for (int posy = 0; posy < nbRows; ++posy) { if (m_sumRows.at(posy + 1)) { --nbRealRows; } } int nbColumns = getNbColumns(false); int nbRealColumns = nbColumns - m_nbVirtualColumns; // Get graphic mode GraphType mode = getGraphType(); // Get in positive bool inPositive = false; if (mode == STACK || mode == STACKCOLUMNS || mode == HISTOGRAM || mode == POINT || mode == LINE || mode == STACKAREA) { m_allPositiveMenu->setEnabled(true); inPositive = (m_allPositiveMenu->isChecked()); } else { m_allPositiveMenu->setEnabled(false); if (mode == CONCENTRICPIE || mode == PIE || mode == TREEMAP) { inPositive = true; } } // Get show origin bool showOrigin = true; if (mode == HISTOGRAM || mode == POINT || mode == LINE) { m_actShowZero->setEnabled(true); showOrigin = (m_actShowZero->isChecked()); } else { m_actShowZero->setEnabled(false); } // Compute y limits double minLimit = (showOrigin ? 0 : 9999999); double maxLimit = (showOrigin ? 0 : -9999999); int nbLevel = 0; SKGTRACEL(3) << "mode=" << static_cast(mode) << endl; SKGTRACEL(3) << "nb rows =" << nbRows << endl; SKGTRACEL(3) << "nb real rows =" << nbRealRows << endl; SKGTRACEL(3) << "nb columns =" << nbColumns << endl; SKGTRACEL(3) << "nb real columns=" << nbRealColumns << endl; SKGTRACEL(3) << "selected row =" << crow << endl; SKGTRACEL(3) << "selected column=" << ccolumn << endl; if (mode == STACK) { // STACK for (int posx = 0; posx < nbRows; ++posx) { if (!m_sumRows[posx + 1]) { double sumPositive = 0; double sumNegative = 0; for (int posy = 0; posy < nbColumns; ++posy) { QTableWidgetItem* tableItem = ui.kTable->item(posx, posy); if (tableItem != nullptr) { QVariant valQ = tableItem->data(DATA_VALUE); if (valQ.type() == QVariant::Double) { double val = valQ.toDouble(); if (inPositive || val >= 0) { sumPositive += qAbs(val); } else { sumNegative += val; } } } } minLimit = qMin(minLimit, sumNegative); maxLimit = qMax(maxLimit, sumPositive); } } } else if (mode == STACKAREA || mode == STACKCOLUMNS || mode == PIE || mode == CONCENTRICPIE || mode == TREEMAP) { // STACKAREA or STACKCOLUMNS or PIE or CONCENTRICPIE for (int posy = 0; posy < nbColumns; ++posy) { double sumPositive = 0; double sumNegative = 0; for (int posx = 0; posx < nbRows; ++posx) { if (!m_sumRows[posx + 1]) { QTableWidgetItem* tableItem = ui.kTable->item(posx, posy); if (tableItem != nullptr) { QVariant valQ = tableItem->data(DATA_VALUE); if (valQ.type() == QVariant::Double) { double val = valQ.toDouble(); if (inPositive || val >= 0) { sumPositive += qAbs(val); } else { sumNegative += val; } } } } } if (mode == TREEMAP) { // TREEMAP minLimit = 0; maxLimit = qAbs(sumNegative) + sumPositive; } else { minLimit = qMin(minLimit, sumNegative); maxLimit = qMax(maxLimit, sumPositive); } } } else if (mode == HISTOGRAM || mode == POINT || mode == LINE) { // HISTOGRAM or POINTS or LINES for (int posx = 0; posx < nbRows; ++posx) { if (!m_sumRows[posx + 1]) { for (int posy = 0; posy < nbColumns; ++posy) { QTableWidgetItem* tableItem = ui.kTable->item(posx, posy); if (tableItem != nullptr) { QVariant valQ = tableItem->data(DATA_VALUE); if (valQ.type() == QVariant::Double) { double val = valQ.toDouble(); if (inPositive) { maxLimit = qMax(maxLimit, qAbs(val)); minLimit = qMin(minLimit, qAbs(val)); } else { minLimit = qMin(minLimit, val); maxLimit = qMax(maxLimit, val); } } } } } } } else if (mode == BUBBLE) { for (int posx = 0; posx < nbRows; ++posx) { if (!m_sumRows[posx + 1]) { for (int posy = 0; posy < nbColumns; ++posy) { QTableWidgetItem* tableItem = ui.kTable->item(posx, posy); if (tableItem != nullptr) { QVariant valQ = tableItem->data(DATA_VALUE); if (valQ.type() == QVariant::Double) { double val = valQ.toDouble(); maxLimit = qMax(maxLimit, qAbs(val)); } } } } } } if (mode == CONCENTRICPIE) { for (int posx = 0; posx < nbRows; ++posx) { auto* btn = qobject_cast(ui.kTable->cellWidget(posx, 0)); if (btn != nullptr) { QString xname = btn->text(); QStringList vals = xname.split(OBJECTSEPARATOR); int nbvals = vals.count(); nbLevel = qMax(nbLevel, nbvals - 1); } } } // Compute double yorigin = 0.0; double widthItem = 10; double maxX = 0; double margin = 0; double marginLeft = 0; double xstep = 0.0; double radius = 0.0; int jstep = qMax(computeStepSize(nbColumns, 10), static_cast(1.0)); double ystep = computeStepSize(maxLimit - minLimit, 10); if (mode != BUBBLE && mode != PIE && mode != CONCENTRICPIE && mode != TREEMAP && ystep != 0) { double newMinLimit = ystep * qRound(minLimit / ystep); minLimit = (minLimit - newMinLimit < -EPSILON ? newMinLimit - ystep : newMinLimit); double newMaxLimit = ystep * qRound(maxLimit / ystep); maxLimit = (maxLimit - newMaxLimit > EPSILON ? newMaxLimit + ystep : newMaxLimit); } if ((nbRealRows != 0) && ystep != 0) { QRect vSize = ui.graphicView->rect(); if (mode == STACK) { margin = (maxLimit - minLimit) * 0.3; if (!showOrigin) { if (minLimit > 0) { yorigin = ystep * (qRound(minLimit / ystep)); } else if (maxLimit < 0) { yorigin = ystep * (qRound(maxLimit / ystep)); } } widthItem = (maxLimit - minLimit) / (nbRealRows + 1); widthItem = widthItem * (static_cast(vSize.width())) / (static_cast(vSize.height())); xstep = widthItem * (nbRealRows + 1); marginLeft = widthItem; maxX = widthItem * nbRealRows + margin; } else if (mode == HISTOGRAM || mode == POINT || mode == LINE || mode == STACKAREA || mode == STACKCOLUMNS) { margin = (maxLimit - minLimit) * 0.3; if (!showOrigin) { if (minLimit > 0) { yorigin = ystep * (qRound(minLimit / ystep)); } else if (maxLimit < 0) { yorigin = ystep * (qRound(maxLimit / ystep)); } } widthItem = (maxLimit - minLimit) / ((nbRealRows + 1) * (nbColumns - 1)); widthItem = widthItem * (static_cast(vSize.width())) / (static_cast(vSize.height())); xstep = widthItem * (nbRealRows + 1); marginLeft = qMin(jstep * xstep, widthItem * (nbColumns - 1)); maxX = xstep * (nbColumns - 1); if (mode == POINT || mode == LINE || mode == STACKAREA) { maxX -= xstep; } // radius = qMax(margin / 100.0, qMin(width, qMin(ystep / 4, jstep * xstep / 4))); radius = qMax(margin / 200, qMin(widthItem, qMin(ystep / 8, jstep * xstep / 8))); } else if (mode == BUBBLE) { margin = ystep * nbRealRows * 0.3; widthItem = ystep * nbRealRows / (nbRealRows * (nbColumns - 1)); widthItem = widthItem * (static_cast(vSize.width())) / (static_cast(vSize.height())); xstep = widthItem * (nbRealRows + 1); marginLeft = jstep * xstep; maxX = widthItem * (nbColumns - 1) * (nbRealRows + 1); } } SKGTRACEL(3) << "minLimit=" << minLimit << endl; SKGTRACEL(3) << "maxLimit=" << maxLimit << endl; SKGTRACEL(3) << "ystep=" << ystep << endl; SKGTRACEL(3) << "yorigin=" << yorigin << endl; SKGTRACEL(3) << "width=" << widthItem << endl; SKGTRACEL(3) << "maxX=" << maxX << endl; SKGTRACEL(3) << "margin=" << margin << endl; SKGTRACEL(3) << "xstep=" << xstep << endl; SKGTRACEL(3) << "marginLeft=" << marginLeft << endl; // Initialise pens QPen axisPen = QPen(Qt::SolidLine); axisPen.setColor(m_axisColor); axisPen.setWidthF(margin / 30.0); axisPen.setJoinStyle(Qt::RoundJoin); QPen gridPen = QPen(Qt::SolidLine); gridPen.setColor(m_gridColor); gridPen.setWidthF(margin / 100.0); QPen dotPen = QPen(Qt::SolidLine); // WARNING: Qt::DotLine is very bad for performances dotPen.setWidthF(margin / 50.0); QPen outlinePen(m_outlineColor); outlinePen.setWidthF(margin / 500.0); // Set rect int nbColInMode2 = (nbColumns > 2 ? sqrt(nbColumns - 1) + .5 : 1); m_scene->setItemIndexMethod(QGraphicsScene::NoIndex); SKGTRACEL(10) << "Items:" << nbRealRows << "x" << (nbColumns - 1) << "=" << nbRealRows*(nbColumns - 1) << endl; SKGTRACEL(10) << "nbColInMode2=" << nbColInMode2 << endl; // Compute treemap QMap treemap; if (mode == TREEMAP) { SKGTreeMap treemapobj(QString(), 0, 0, 0, BOX_SIZE, BOX_SIZE); for (int j = 1; j < nbColumns; ++j) { SKGTreeMap treemapobj2; for (int xx = 0; xx < nbRows; ++xx) { auto* colorButton = qobject_cast(ui.kTable->cellWidget(xx, 0)); if (colorButton != nullptr) { // Get cell value QTableWidgetItem* tableItem = ui.kTable->item(xx, j); if (tableItem != nullptr) { QVariant valQ = tableItem->data(DATA_VALUE); if (valQ.type() == QVariant::Double) { double val = qAbs(valQ.toDouble()); QString id = SKGServices::intToString(xx) % QStringLiteral("-") % SKGServices::intToString(j); treemapobj2.addChild(SKGTreeMap(id, val)); } } } } treemapobj.addChild(treemapobj2); } treemapobj.compute(); treemap = treemapobj.getAllTilesById(); } // Redraw scene double x0 = 0; double x1 = 0; double ymin = (showOrigin || mode == STACK || mode == STACKCOLUMNS ? 0 : 9999999); double ymax = (showOrigin || mode == STACK || mode == STACKCOLUMNS ? 0 : -9999999); int posx = 0; int nbRowsDrawed = 0; QMap previousStackedPlus; QMap previousStackedMoins; QMap paretoPoints; for (int xx = 0; xx < nbRows; ++xx) { auto* colorButton = qobject_cast(ui.kTable->cellWidget(xx, 0)); if (colorButton != nullptr) { QString xname = colorButton->text(); QColor initialColor = colorButton->color(); double yPlus = 0; double yMoins = 0; int jprevious = -1; ++nbRowsDrawed; for (int j = 1; j < nbColumns; ++j) { // Get cell value QTableWidgetItem* tableItem = ui.kTable->item(xx, j); if (tableItem != nullptr) { QVariant valQ = tableItem->data(DATA_VALUE); if (valQ.type() == QVariant::Double) { double val = valQ.toDouble(); QString valstring = tableItem->text(); if (inPositive) { val = qAbs(val); } int color_h; int color_s; int color_v; initialColor.getHsv(&color_h, &color_s, &color_v); color_s = color_s - color_s * 155 / 255 * (nbColumns - 1 - j) / nbColumns; color_v = color_v - color_v * 155 / 255 * (nbColumns - 1 - j) / nbColumns; if (j >= nbRealColumns) { // Yellow color_h = 60; color_s = 255; color_v = 255; } QColor color; if (posx == crow && j == ccolumn) { color = QApplication::palette().color(QPalette::Highlight); } else { color = QColor::fromHsv(color_h, color_s, color_v); } color.setAlpha(ALPHA); QBrush brush(color); QGraphicsItem* graphItem = nullptr; if (mode == STACK) { // STACK if (val >= 0) { graphItem = m_scene->addRect(widthItem * posx, -yPlus - qAbs(val), widthItem, qAbs(val), outlinePen, brush); yPlus += qAbs(val); if (yPlus > ymax) { ymax = yPlus; } } else { graphItem = m_scene->addRect(widthItem * posx, -yMoins, widthItem, -val, outlinePen, brush); yMoins += val; if (yMoins < ymin) { ymin = yMoins; } } if (graphItem != nullptr) { graphItem->setZValue(5 + nbColumns - j); } } if (mode == STACKAREA) { // STACKAREA // Empty cells are ignored if (j == 1) { x0 = widthItem * (j - 1) * (nbRealRows + 1); } if (j == nbColumns - m_nbVirtualColumns - 1) { x1 = widthItem * (j - 1) * (nbRealRows + 1); } if (!valstring.isEmpty()) { if (jprevious == -1) { jprevious = j; } QTableWidgetItem* tableItem2 = ui.kTable->item(xx, jprevious); if (tableItem2 != nullptr) { QString val2string = tableItem->text(); if (!val2string.isEmpty()) { double val2 = tableItem2->data(DATA_VALUE).toDouble(); if (j == jprevious) { val2 = 0; } if (inPositive) { val2 = qAbs(val2); } if (val > EPSILON || (qAbs(val) <= EPSILON && val2 >= EPSILON)) { double xp = widthItem * (jprevious - 1) * (nbRealRows + 1) - (jprevious == j ? widthItem / 20.0 : 0); double xn = widthItem * (j - 1) * (nbRealRows + 1); double yp = (previousStackedPlus.contains(jprevious) ? previousStackedPlus[jprevious] : -val2); double yn = previousStackedPlus[j] - val; QPolygonF polygon; polygon << QPointF(xp, yp + val2) << QPointF(xp, yp) << QPointF(xn, yn) << QPointF(xn, previousStackedPlus[j]); graphItem = m_scene->addPolygon(polygon, outlinePen, brush); previousStackedPlus[j] = yn; if (-yn > ymax) { ymax = -yn; } } else { double xp = widthItem * (jprevious - 1) * (nbRealRows + 1) - (jprevious == j ? widthItem / 20.0 : 0); double xn = widthItem * (j - 1) * (nbRealRows + 1); double yp = (previousStackedMoins.contains(jprevious) ? previousStackedMoins[jprevious] : -val2); double yn = previousStackedMoins[j] - val; QPolygonF polygon; polygon << QPointF(xp, yp + val2) << QPointF(xp, yp) << QPointF(xn, yn) << QPointF(xn, previousStackedMoins[j]); graphItem = m_scene->addPolygon(polygon, outlinePen, brush); previousStackedMoins[j] = yn; if (-yn < ymin) { ymin = -yn; } } jprevious = j; } } } } if (mode == STACKCOLUMNS) { // STACKCOLUMNS if (val >= 0) { graphItem = m_scene->addRect(widthItem * (j - 1) * (nbRealRows + 1), -previousStackedPlus[j] - qAbs(val), widthItem * (nbRealRows + 1), qAbs(val), outlinePen, brush); previousStackedPlus[j] += qAbs(val); if (previousStackedPlus[j] > ymax) { ymax = previousStackedPlus[j]; } } else { graphItem = m_scene->addRect(widthItem * (j - 1) * (nbRealRows + 1), -previousStackedMoins[j], widthItem * (nbRealRows + 1), -val, outlinePen, brush); previousStackedMoins[j] += val; if (previousStackedMoins[j] < ymin) { ymin = previousStackedMoins[j]; } } if (graphItem != nullptr) { graphItem->setZValue(5 + nbRows - posx); } } else if (mode == HISTOGRAM) { // HISTOGRAM if (j == 1) { x0 = widthItem * ((j - 1) * (nbRealRows + 1) + posx) + widthItem; } if (j == nbColumns - m_nbVirtualColumns - 1) { x1 = widthItem * ((j - 1) * (nbRealRows + 1) + posx) + widthItem; } graphItem = m_scene->addRect(widthItem * ((j - 1) * (nbRealRows + 1) + posx) + widthItem / 2.0, (val < 0 ? -yorigin : -val), widthItem, (val < 0 ? yorigin - val : -yorigin + val), outlinePen, brush); if (val > ymax) { ymax = val; } if (val < ymin) { ymin = val; } } else if (mode == POINT) { // POINTS double xmin = widthItem * (j - 1) * (nbRealRows + 1) - radius; if (j == 1) { x0 = xmin + radius; } if (j == nbColumns - m_nbVirtualColumns - 1) { x1 = xmin + radius; } graphItem = drawPoint(xmin, -val, radius, posx, brush); if (val > ymax) { ymax = val; } if (val < ymin) { ymin = val; } } else if (mode == LINE) { // LINES // Empty cells are ignored if (j == 1) { x0 = widthItem * (j - 1) * (nbRealRows + 1); } if (j == nbColumns - m_nbVirtualColumns - 1) { x1 = widthItem * (j - 1) * (nbRealRows + 1); } if (!valstring.isEmpty()) { if (jprevious == -1) { jprevious = j; } // Create pen color.setAlpha(255); QPen pen = QPen(color); pen.setWidthF(radius); pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::RoundJoin); QTableWidgetItem* tableItem2 = ui.kTable->item(xx, jprevious); if (tableItem2 != nullptr) { QString val2string = tableItem->text(); if (!val2string.isEmpty()) { double val2 = tableItem2->data(DATA_VALUE).toDouble(); if (inPositive) { val2 = qAbs(val2); } QGraphicsLineItem* line = m_scene->addLine(widthItem * (jprevious - 1) * (nbRealRows + 1) - (jprevious == j ? widthItem / 20.0 : 0), -val2, widthItem * (j - 1) * (nbRealRows + 1), -val, pen); line->setZValue(10); if (isShadowVisible()) { auto line_shadow = new QGraphicsDropShadowEffect(); line_shadow->setOffset(3); line->setGraphicsEffect(line_shadow); } graphItem = drawPoint(widthItem * (j - 1) * (nbRealRows + 1) - radius * 0.7, -val, radius * 0.7, posx % 5, brush); if (isShadowVisible()) { auto line_shadow = new QGraphicsDropShadowEffect(); line_shadow->setOffset(3); line->setGraphicsEffect(line_shadow); } graphItem->setZValue(20); if (val > ymax) { ymax = val; } if (val < ymin) { ymin = val; } jprevious = j; } } } } else if (mode == BUBBLE) { // BUBBLE ymin = 0; ymax = ystep * nbRealRows; radius = sqrt(qAbs(val) / maxLimit) * qMin(ystep, jstep * xstep); double xmin = widthItem * (j - 1) * (nbRealRows + 1) - radius; graphItem = m_scene->addEllipse(xmin, -ystep * posx - radius, 2 * radius, 2 * radius, outlinePen, brush); if (graphItem != nullptr) { graphItem->setZValue(5 + 5 * (maxLimit - qAbs(val)) / maxLimit); } } else if (mode == PIE || mode == CONCENTRICPIE) { // PIE val = qAbs(val); // Compute absolute sum of the column double previousSum = 0; for (int x2 = 0; x2 < nbRows; ++x2) { if (!m_sumRows[x2 + 1]) { QTableWidgetItem* tabItem = ui.kTable->item(x2, j); if (tabItem != nullptr) { double absVal = qAbs(tabItem->data(DATA_VALUE).toDouble()); if (x2 < xx) { previousSum += absVal; } } } } if (maxLimit != 0.0) { int nbvals = xname.split(OBJECTSEPARATOR).count(); double step = 0; double p = 0; if (mode == CONCENTRICPIE) { step = 100.0 / static_cast(nbLevel + 1); p = step * (nbLevel + 1 - nbvals); } QPainterPath path; path.moveTo(BOX_SIZE * ((j - 1) % nbColInMode2) + BOX_SIZE / 2.0, BOX_SIZE * floor((j - 1) / nbColInMode2) + BOX_SIZE / 2.0); path.arcTo(BOX_SIZE * ((j - 1) % nbColInMode2) + BOX_MARGIN + p / 2.0, BOX_SIZE * floor((j - 1) / nbColInMode2) + BOX_MARGIN + p / 2.0, BOX_SIZE - 2 * BOX_MARGIN - p, BOX_SIZE - 2 * BOX_MARGIN - p, 90.0 - 360.0 * previousSum / maxLimit, -360.0 * val / maxLimit); path.closeSubpath(); if (mode == CONCENTRICPIE && nbvals <= nbLevel + 1 && nbvals > 1) { p = step * (nbLevel + 1 - nbvals + 1); QPainterPath path2; path2.addEllipse(BOX_SIZE * ((j - 1) % nbColInMode2) + BOX_MARGIN + p / 2.0, BOX_SIZE * floor((j - 1) / nbColInMode2) + BOX_MARGIN + p / 2.0, BOX_SIZE - 2 * BOX_MARGIN - p, BOX_SIZE - 2 * BOX_MARGIN - p); path -= path2; } graphItem = m_scene->addPath(path, outlinePen, brush); } } else if (mode == TREEMAP) { // TREEMAP QString id = SKGServices::intToString(xx) % QStringLiteral("-") % SKGServices::intToString(j); SKGTreeMap tm = treemap[id]; graphItem = m_scene->addRect(tm.getX(), tm.getY(), tm.getW(), tm.getH(), outlinePen, brush); } // Compute pareto points if (mode == POINT || mode == LINE) { paretoPoints[widthItem * (j - 1) * (nbRealRows + 1)] = val; } else if (mode == HISTOGRAM) { paretoPoints[widthItem * ((j - 1) * (nbRealRows + 1) + posx) + widthItem] = val; } if (graphItem != nullptr) { if (graphItem->zValue() == 0) { graphItem->setZValue(5); } graphItem->setToolTip(tableItem->toolTip()); bool isSelect = (isSelectable() && ((tableItem->flags() & Qt::ItemIsEnabled) != 0u)); graphItem->setFlag(QGraphicsItem::ItemIsSelectable, isSelect); if (isSelect) { graphItem->setCursor(Qt::PointingHandCursor); } graphItem->setData(1, xx); graphItem->setData(2, j); graphItem->setData(DATA_COLOR_H, color_h); graphItem->setData(DATA_COLOR_S, color_s); graphItem->setData(DATA_COLOR_V, color_v); graphItem->setData(DATA_Z_VALUE, graphItem->zValue()); m_mapItemGraphic[tableItem] = graphItem; } } } else { SKGTRACE << "WARNING: cell " << posx << "," << j << " null" << endl; } } ++posx; } } // Draw axis double scaleText = 0.0; auto nbRowInMode2 = static_cast(((nbColumns - 1) / nbColInMode2)); if (nbRealRows != 0) { if (mode == TREEMAP) { // TREEMAP } else if (mode == PIE || mode == CONCENTRICPIE) { // PIE if (nbRowInMode2 * nbColInMode2 < nbColumns - 1) { ++nbRowInMode2; } for (int i = 0; i <= nbColInMode2; ++i) { QGraphicsLineItem* item = m_scene->addLine(BOX_SIZE * i, 0, BOX_SIZE * i, BOX_SIZE * nbRowInMode2); item->setPen(axisPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); } for (int i = 0; i <= nbRowInMode2; ++i) { QGraphicsLineItem* item = m_scene->addLine(0, BOX_SIZE * i, BOX_SIZE * nbColInMode2, BOX_SIZE * i); item->setPen(axisPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); } for (int j = 1; j < nbColumns; ++j) { // Get column name QString yname = ui.kTable->horizontalHeaderItem(j)->text(); QGraphicsTextItem* textItem = m_scene->addText(yname); textItem->setDefaultTextColor(m_textColor); textItem->setPos(BOX_SIZE * ((j - 1) % nbColInMode2) + 2, BOX_SIZE * floor((j - 1) / nbColInMode2) + 2); textItem->setScale(.5); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); textItem->setZValue(20); } } else { // STACK & HISTOGRAMM QGraphicsLineItem* item; // Compute scale text if (mode != BUBBLE) { QGraphicsTextItem* t = m_scene->addText(SKGServices::toCurrencyString(-qMax(qAbs(ymax), qAbs(ymin)), m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); QRectF bRect = t->boundingRect(); scaleText = 0.8 * qMin(marginLeft / bRect.width(), qMin(marginLeft / bRect.height(), ystep / bRect.height())); m_scene->removeItem(t); } else { maxLimit = ystep * nbRealRows; } if (ystep > 0) { // Draw int i = 1; for (double posy = yorigin + ystep ; posy <= maxLimit ; posy = posy + ystep) { item = m_scene->addLine(0, -posy, maxX, -posy); item->setPen(gridPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(1); QGraphicsTextItem* textItem; if (mode == BUBBLE) { auto* cel = qobject_cast(ui.kTable->cellWidget(i, 0)); ++i; if (cel == nullptr) { cel = qobject_cast(ui.kTable->cellWidget(i, 0)); ++i; } textItem = m_scene->addText(cel != nullptr ? cel->text() : QString()); } else { textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); } QRectF textRect = textItem->boundingRect(); if (scaleText == 0) { scaleText = 0.8 * qMin(marginLeft / textRect.width(), qMin(marginLeft / textRect.height(), ystep / textRect.height())); } textItem->setDefaultTextColor(m_textColor); textItem->setScale(scaleText); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); textItem->setZValue(20); double delta = scaleText * textRect.height() / 2.0; textItem->setPos(-marginLeft, -posy - delta); } i = 0; for (double posy = yorigin ; (posy == yorigin) || posy >= minLimit; posy = posy - ystep) { item = m_scene->addLine(0, -posy, maxX, -posy); item->setPen(gridPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(1); QGraphicsTextItem* textItem; if (mode == BUBBLE) { auto* cel = qobject_cast(ui.kTable->cellWidget(i, 0)); textItem = m_scene->addText(cel != nullptr ? cel->text() : QString()); } else { textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); } QRectF textRect = textItem->boundingRect(); if (scaleText == 0) { scaleText = 0.8 * qMin(marginLeft / textRect.width(), qMin(marginLeft / textRect.height(), ystep / textRect.height())); } textItem->setDefaultTextColor(m_textColor); textItem->setScale(scaleText); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); textItem->setZValue(20); double delta = scaleText * textRect.height() / 2.0; textItem->setPos(-marginLeft, -posy - delta); } } if (mode == HISTOGRAM || mode == POINT || mode == LINE || mode == BUBBLE || mode == STACKAREA || mode == STACKCOLUMNS) { // Line y for (int j = 1; j < nbColumns; j += jstep) { QString yname = ui.kTable->horizontalHeaderItem(j)->text(); double posx2 = xstep + (j - 2) * xstep; QGraphicsTextItem* textItem = m_scene->addText(yname); QRectF textRect = textItem->boundingRect(); if (scaleText == 0) { scaleText = 0.8 * qMin(marginLeft / textRect.width(), qMin(marginLeft / textRect.height(), ystep / textRect.height())); } textItem->setDefaultTextColor(m_textColor); textItem->setScale(scaleText); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); textItem->setZValue(20); double delta = scaleText * textRect.height() / 2.0; textItem->setRotation(90); textItem->moveBy(textRect.height() *scaleText, 0); textItem->setPos(posx2 + delta, -yorigin); QGraphicsLineItem* graphicItem = m_scene->addLine(posx2, qMax(-yorigin, -minLimit), posx2, qMin(-yorigin, -maxLimit)); graphicItem->setPen(gridPen); graphicItem->setFlag(QGraphicsItem::ItemIsSelectable, false); } } // Axis x if (yorigin == 0.0) { item = m_scene->addLine(0, -yorigin, maxX, -yorigin, axisPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(2); } // Rect { QGraphicsRectItem* graphicItem = m_scene->addRect(0, qMax(-yorigin, -minLimit), maxX, qMin(-yorigin, -maxLimit) - (qMax(-yorigin, -minLimit)), axisPen); graphicItem->setFlag(QGraphicsItem::ItemIsSelectable, false); graphicItem->setZValue(2); } } } // Draw Average bool lineCondition = (mode == HISTOGRAM || mode == POINT || mode == LINE) && (scaleText != 0.0) && nbRowsDrawed == 1; int averageCol = getAverageColumnIndex(); if (m_averageVisible && lineCondition && averageCol != -1) { QTableWidgetItem* tableItem = ui.kTable->item(0, averageCol); if (tableItem != nullptr) { double posy = tableItem->data(DATA_VALUE).toDouble(); if (inPositive) { posy = qAbs(posy); } QGraphicsLineItem* item = m_scene->addLine(0, -posy, maxX, -posy); dotPen.setColor(m_averageColor); item->setPen(dotPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(1); item->setToolTip(tableItem->toolTip()); QGraphicsTextItem* textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); QRectF textRect = textItem->boundingRect(); double delta = scaleText * textRect.height() / 2.0; textItem->setPos(maxX, -posy - delta); textItem->setDefaultTextColor(m_averageColor); textItem->setScale(scaleText); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); textItem->setZValue(20); textItem->setToolTip(tableItem->toolTip()); } } // Draw Min & Max limits int minCol = getMinColumnIndex(); if (lineCondition && minCol != -1) { // Min { QTableWidgetItem* tableItem = ui.kTable->item(0, minCol); if (tableItem != nullptr) { double posy = tableItem->data(DATA_VALUE).toDouble(); if (inPositive) { posy = qAbs(posy); } QGraphicsLineItem* item = m_scene->addLine(0, -posy, maxX, -posy); dotPen.setColor(m_minColor); item->setPen(dotPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(1); item->setToolTip(tableItem->toolTip()); QGraphicsTextItem* textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); QRectF textRect = textItem->boundingRect(); double delta = scaleText * textRect.height() / 2.0; textItem->setPos(maxX, -posy - delta); textItem->setDefaultTextColor(m_minColor); textItem->setScale(scaleText); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); textItem->setZValue(20); textItem->setToolTip(tableItem->toolTip()); } } // Max { QTableWidgetItem* tableItem = ui.kTable->item(0, minCol + 1); if (tableItem != nullptr) { double posy = tableItem->data(DATA_VALUE).toDouble(); if (inPositive) { posy = qAbs(posy); } QGraphicsLineItem* item = m_scene->addLine(0, -posy, maxX, -posy); dotPen.setColor(m_maxColor); item->setPen(dotPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(1); item->setToolTip(tableItem->toolTip()); QGraphicsTextItem* textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); QRectF textRect = textItem->boundingRect(); double delta = scaleText * textRect.height() / 2.0; textItem->setPos(maxX, -posy - delta); textItem->setDefaultTextColor(m_maxColor); textItem->setScale(scaleText); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); textItem->setZValue(20); textItem->setToolTip(tableItem->toolTip()); } } } // Draw Linear Regression if (m_linearRegressionVisible && lineCondition && m_indexLinearRegression != -1) { QTableWidgetItem* tableItem = ui.kTable->item(0, m_indexLinearRegression); if (tableItem != nullptr) { QString f = tableItem->text(); QScriptEngine myEngine; QString f0 = f; f0 = f0.remove(QStringLiteral("y=")); f0 = f0.replace('x', '1'); f0 = f0.replace(',', '.'); // Replace comma by a point in case of typo if (inPositive) { f0 = "Math.abs(" % f0 % ')'; } double y0 = myEngine.evaluate(f0).toNumber(); QString f1 = f; f1 = f1.remove(QStringLiteral("y=")); f1 = f1.replace('x', SKGServices::intToString(nbRealColumns - 1)); f1 = f1.replace(',', '.'); // Replace comma by a point in case of typo if (inPositive) { f1 = "Math.abs(" % f1 % ')'; } double y1 = myEngine.evaluate(f1).toNumber(); QGraphicsLineItem* item = m_scene->addLine(x0, -y0, x1, -y1); dotPen.setColor(m_tendencyColor); item->setPen(dotPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(1); item->setToolTip(f); } } // Draw pareto if (lineCondition && m_paretoVisible && !paretoPoints.isEmpty()) { // Compute the sum double sum = 0.0; QList list = paretoPoints.keys(); for (auto xp1 : qAsConst(list)) { sum += paretoPoints[xp1]; } // Draw the second axis for (int i = 0 ; i <= 100 ; i = i + 10) { double posy = (maxLimit - minLimit) * static_cast(i) / 100.0 + minLimit; QGraphicsTextItem* textItem = m_scene->addText(SKGServices::intToString(i) % "%"); QRectF textRect = textItem->boundingRect(); textItem->setDefaultTextColor(m_paretoColor); textItem->setScale(scaleText); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); textItem->setZValue(19); double delta = scaleText * textRect.height() / 2.0; textItem->setPos(maxX, -posy - delta); } // Draw the curve double x00 = -1; double y00 = -1; double csum = 0.0; for (auto xp2 : qAsConst(list)) { csum += paretoPoints[xp2]; if (x00 != -1) { QGraphicsLineItem* item = m_scene->addLine(x00, -((maxLimit - minLimit) * y00 / sum + minLimit), xp2, -((maxLimit - minLimit) * csum / sum + minLimit)); dotPen.setColor(m_paretoColor); item->setPen(dotPen); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(1); } x00 = xp2; y00 = csum; } } // Draw legend if (m_legendVisible) { QPointF legendPosition; double maxY; if (mode == TREEMAP) { scaleText = 0.2; margin = BOX_SIZE / 10; legendPosition = QPointF(BOX_SIZE + margin, 0); maxY = BOX_SIZE * nbRowInMode2; } else if (mode == PIE || mode == CONCENTRICPIE) { if (nbRowInMode2 * nbColInMode2 < nbColumns - 1) { ++nbRowInMode2; } scaleText = 0.2; margin = BOX_SIZE / 10; legendPosition = QPointF(BOX_SIZE * nbColInMode2 + margin, 0); maxY = BOX_SIZE * nbRowInMode2; } else { legendPosition = QPointF(maxX + margin, qMin(-yorigin, -maxLimit) - margin / 6); maxY = qMax(-yorigin, -minLimit); } addLegend(legendPosition, margin / 4, scaleText, maxY); } } { SKGTRACEINFUNC(10) m_scene->setSceneRect(QRectF()); ui.graphicView->setScene(m_scene); ui.graphicView->show(); if (m_tableVisible) { ui.kTable->show(); } ui.graphicView->initializeZoom(); // Add selection event on scene connect(m_scene, &SKGGraphicsScene::selectionChanged, this, &SKGTableWithGraph::onSelectionChangedInGraph, Qt::QueuedConnection); connect(m_scene, &SKGGraphicsScene::doubleClicked, this, &SKGTableWithGraph::onDoubleClickGraph, Qt::QueuedConnection); } QApplication::restoreOverrideCursor(); } int SKGTableWithGraph::getNbColumns(bool iWithComputed) const { int nbColumns = ui.kTable->columnCount(); if (!iWithComputed) { if (m_indexMin != -1) { nbColumns -= 2; } if (m_indexAverage != -1) { --nbColumns; } if (m_indexSum != -1) { --nbColumns; } if (m_indexLinearRegression != -1) { --nbColumns; } } return nbColumns; } int SKGTableWithGraph::getAverageColumnIndex() const { return m_indexAverage; } int SKGTableWithGraph::getMinColumnIndex() const { return m_indexMin; } double SKGTableWithGraph::computeStepSize(double iRange, double iTargetSteps) { // Calculate an initial guess at step size double tempStep = iRange / iTargetSteps; // Get the magnitude of the step size double mag = floor(log10(tempStep)); double magPow = pow(static_cast(10.0), mag); // Calculate most significant digit of the new step size double magMsd = static_cast(tempStep / magPow + .5); // promote the MSD to either 1, 2, or 5 if (magMsd > 5.0) { magMsd = 10.0; } else if (magMsd > 2.0) { magMsd = 5.0; } else if (magMsd > 1.0) { magMsd = 2.0; } return magMsd * magPow; } void SKGTableWithGraph::addLegend(const QPointF iPosition, double iSize, double iScaleText, double iMaxY) { SKGTRACEINFUNC(10) QPen outlinePen(m_outlineColor); outlinePen.setWidthF(iScaleText * 4.0 / 500.0); if (m_scene != nullptr) { GraphType mode = getGraphType(); int nbRows = ui.kTable->rowCount(); int nbRealRows = nbRows; for (int posy = 0; posy < nbRows; ++posy) { if (m_sumRows.at(posy + 1)) { --nbRealRows; } } int nbColumns = getNbColumns(false); if (nbColumns > 1) { double margin = 1.2; double currentYPos = iPosition.y(); double currentXPos = iPosition.x(); double currentXMaxSize = 0; for (int i = 0; i < nbRows; ++i) { auto* btn = qobject_cast(ui.kTable->cellWidget(i, 0)); if (btn != nullptr) { // Get title QString title = btn->text(); // Build brush QColor color1; QTableWidgetItem* it = ui.kTable->item(i, 1); if (it != nullptr) { QGraphicsItem* graphicItem = m_mapItemGraphic.value(it); if (graphicItem != nullptr) { // Draw box color1 = QColor::fromHsv(graphicItem->data(DATA_COLOR_H).toInt(), graphicItem->data(DATA_COLOR_S).toInt(), graphicItem->data(DATA_COLOR_V).toInt()); color1.setAlpha(ALPHA); } } QColor color2; it = ui.kTable->item(i, nbColumns - 1 - m_nbVirtualColumns); if (it != nullptr) { QGraphicsItem* graphicItem = m_mapItemGraphic.value(it); if (graphicItem != nullptr) { // Draw box color2 = QColor::fromHsv(graphicItem->data(DATA_COLOR_H).toInt(), graphicItem->data(DATA_COLOR_S).toInt(), graphicItem->data(DATA_COLOR_V).toInt()); color2.setAlpha(ALPHA); } } QLinearGradient grandient(currentXPos, currentYPos, currentXPos + 2.0 * iSize, currentYPos); grandient.setColorAt(0, color1); grandient.setColorAt(1, color2); // Draw legend item QGraphicsItem* item = nullptr; if (mode == POINT || mode == LINE) { item = drawPoint(currentXPos, currentYPos + iSize / 2.0, iSize / 2.0, mode == POINT ? i : (i % 5), QBrush(grandient)); } else if (mode == BUBBLE) { item = m_scene->addEllipse(currentXPos, currentYPos, iSize, iSize, outlinePen, QBrush(grandient)); } else if (mode == PIE || mode == CONCENTRICPIE) { QPainterPath path; path.moveTo(currentXPos + iSize / 2.0, currentYPos + iSize / 2.0); path.arcTo(currentXPos, currentYPos, iSize, iSize, 45, 270); path.closeSubpath(); if (mode == CONCENTRICPIE) { QPainterPath path2; double p = iSize / 3.0; path2.addEllipse(currentXPos + p, currentYPos + p, iSize - 2.0 * p, iSize - 2.0 * p); path -= path2; } item = m_scene->addPath(path, outlinePen, QBrush(grandient)); } else { item = m_scene->addRect(currentXPos, currentYPos, iSize, iSize, outlinePen, QBrush(grandient)); } if (item != nullptr) { item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setToolTip(title); // Set shadow if (isShadowVisible()) { auto ellipse_shadow = new QGraphicsDropShadowEffect(); ellipse_shadow->setOffset(3); item->setGraphicsEffect(ellipse_shadow); } } // Draw text QGraphicsTextItem* textItem = m_scene->addText(title); textItem->setDefaultTextColor(m_textColor); textItem->setScale(iScaleText); textItem->setPos(currentXPos + margin * iSize, currentYPos + iSize / 2.0 - textItem->boundingRect().height()*iScaleText / 2.0); textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); // Compute next position currentYPos += margin * iSize; QRectF textRect = textItem->boundingRect(); currentXMaxSize = qMax(currentXMaxSize, static_cast(iScaleText * textRect.width())); if (currentYPos > iMaxY) { currentYPos = iPosition.y(); currentXPos += currentXMaxSize + 2 * margin * iSize; currentXMaxSize = 0; } } } } } } void SKGTableWithGraph::addArrow(const QPointF iPeak, double iSize, double iArrowAngle, double iDegree) { if (m_scene != nullptr) { QPolygonF pol; double radian = 3.14 * iArrowAngle / 360.0; pol << QPointF(0, 0) << QPointF(iSize * cos(radian), iSize * sin(radian)) << QPointF(iSize * cos(radian), -iSize * sin(radian)) << QPointF(0, 0); QGraphicsPolygonItem* item = m_scene->addPolygon(pol, QPen(m_axisColor, iSize / 20.0), QBrush(m_axisColor)); item->setRotation(iDegree); item->moveBy(iPeak.x(), iPeak.y()); item->setFlag(QGraphicsItem::ItemIsSelectable, false); item->setZValue(2); } } QGraphicsItem* SKGTableWithGraph::drawPoint(qreal iX, qreal iY, qreal iRadius, int iMode, const QBrush& iBrush) { QGraphicsItem* graphItem = nullptr; int nbMode = 13; if (m_scene != nullptr) { QPen outlinePen(m_outlineColor); outlinePen.setWidthF(iRadius / 10); QPen pen; if ((iMode % nbMode) <= 4) { pen = QPen(iBrush.color()); const QGradient* grad = iBrush.gradient(); if (grad != nullptr) { auto stops = grad->stops(); auto stop = stops.last(); pen = QPen(stop.second); } pen.setWidthF(iRadius / 10); } switch (iMode % nbMode) { case 0: { graphItem = m_scene->addEllipse(iX, iY - iRadius, 2 * iRadius, 2 * iRadius, pen, m_WhiteColor); break; } case 1: { graphItem = m_scene->addRect(iX, iY - iRadius, 2 * iRadius, 2 * iRadius, pen, m_WhiteColor); break; } case 2: { QPolygonF polygon; polygon << QPointF(iX + iRadius, iY + iRadius) << QPointF(iX + 2 * iRadius, iY - iRadius) << QPointF(iX, iY - iRadius) << QPointF(iX + iRadius, iY + iRadius); graphItem = m_scene->addPolygon(polygon, pen, m_WhiteColor); break; } case 3: { QPolygonF polygon; polygon << QPointF(iX, iY) << QPointF(iX + iRadius, iY + iRadius) << QPointF(iX + 2 * iRadius, iY) << QPointF(iX + iRadius, iY - iRadius) << QPointF(iX, iY); graphItem = m_scene->addPolygon(polygon, pen, m_WhiteColor); break; } case 4: { QPolygonF polygon; polygon << QPointF(iX + iRadius, iY - iRadius) << QPointF(iX + 2 * iRadius, iY + iRadius) << QPointF(iX, iY + iRadius) << QPointF(iX + iRadius, iY - iRadius); graphItem = m_scene->addPolygon(polygon, pen, m_WhiteColor); break; } case 5: { graphItem = m_scene->addEllipse(iX, iY - iRadius, 2 * iRadius, 2 * iRadius, outlinePen, iBrush); break; } case 6: { graphItem = m_scene->addRect(iX, iY - iRadius, 2 * iRadius, 2 * iRadius, outlinePen, iBrush); break; } case 7: { QPolygonF polygon; polygon << QPointF(iX, iY - iRadius) << QPointF(iX + 2 * iRadius, iY + iRadius) << QPointF(iX + 2 * iRadius, iY - iRadius) << QPointF(iX, iY + iRadius); graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); break; } case 8: { QPolygonF polygon; polygon << QPointF(iX + iRadius, iY + iRadius) << QPointF(iX + 2 * iRadius, iY - iRadius) << QPointF(iX, iY - iRadius) << QPointF(iX + iRadius, iY + iRadius); graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); break; } case 9: { QPolygonF polygon; polygon << QPointF(iX, iY) << QPointF(iX + iRadius, iY + iRadius) << QPointF(iX + 2 * iRadius, iY) << QPointF(iX + iRadius, iY - iRadius) << QPointF(iX, iY); graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); break; } case 10: { QPolygonF polygon; polygon << QPointF(iX, iY - iRadius) << QPointF(iX + 2 * iRadius, iY - iRadius) << QPointF(iX, iY + iRadius) << QPointF(iX + 2 * iRadius, iY + iRadius); graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); break; } case 11: { QPolygonF polygon; polygon << QPointF(iX + iRadius, iY - iRadius) << QPointF(iX + 2 * iRadius, iY + iRadius) << QPointF(iX, iY + iRadius) << QPointF(iX + iRadius, iY - iRadius); graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); break; } case 12: default: { QPainterPath path; path.addEllipse(iX, iY - iRadius, 2 * iRadius, 2 * iRadius); path.closeSubpath(); QPainterPath path2; path2.addEllipse(iX + iRadius / 2.0, iY - iRadius / 2.0, iRadius, iRadius); path -= path2; graphItem = m_scene->addPath(path, outlinePen, iBrush); break; } } } if ((graphItem != nullptr) && (iMode % nbMode) <= 4) { graphItem->setData(DATA_MODE, 1); } return graphItem; } SKGError SKGTableWithGraph::exportInFile(const QString& iFileName) { SKGError err; _SKGTRACEINFUNCRC(10, err) QString lastCodecUsed = QTextCodec::codecForLocale()->name(); QString extension = QFileInfo(iFileName).suffix().toUpper(); if (extension == QStringLiteral("CSV")) { // Write file QSaveFile file(iFileName); if (!file.open(QIODevice::WriteOnly)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", iFileName)); } else { QTextStream out(&file); out.setCodec(lastCodecUsed.toLatin1().constData()); QStringList dump = SKGServices::tableToDump(getTable(), SKGServices::DUMP_CSV); int nbl = dump.count(); for (int i = 0; i < nbl; ++i) { out << dump.at(i) << endl; } // Close file file.commit(); } } else { // Write file QSaveFile file(iFileName); if (!file.open(QIODevice::WriteOnly)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", iFileName)); } else { QTextStream out(&file); out.setCodec(lastCodecUsed.toLatin1().constData()); QStringList dump = SKGServices::tableToDump(getTable(), SKGServices::DUMP_TEXT); int nbl = dump.count(); for (int i = 0; i < nbl; ++i) { out << dump.at(i) << endl; } // Close file file.commit(); } } return err; } void SKGTableWithGraph::onExport() { _SKGTRACEINFUNC(10) SKGError err; QString fileName = SKGMainPanel::getSaveFileName(QStringLiteral("kfiledialog:///IMPEXP"), QStringLiteral("text/csv text/plain"), this); if (!fileName.isEmpty()) { err = exportInFile(fileName); SKGMainPanel::displayErrorMessage(err); QDesktopServices::openUrl(QUrl(fileName)); } } void SKGTableWithGraph::setGraphType(SKGTableWithGraph::GraphType iType) { if (m_displayMode != nullptr) { auto newIndex = m_displayMode->findData(static_cast(iType)); if (m_displayMode->currentIndex() != newIndex) { m_displayMode->setCurrentIndex(newIndex); Q_EMIT modified(); } } } SKGTableWithGraph::GraphType SKGTableWithGraph::getGraphType() const { GraphType mode = static_cast(m_displayMode->itemData(m_displayMode->currentIndex()).toInt()); return mode; } void SKGTableWithGraph::setSelectable(bool iSelectable) { if (m_selectable != iSelectable) { m_selectable = iSelectable; Q_EMIT modified(); } } bool SKGTableWithGraph::isSelectable() const { return m_selectable; } void SKGTableWithGraph::setShadowVisible(bool iShadow) { if (m_shadow != iShadow) { m_shadow = iShadow; Q_EMIT modified(); } } bool SKGTableWithGraph::isShadowVisible() const { return m_shadow; } diff --git a/skgbasegui/skgwebview.cpp b/skgbasegui/skgwebview.cpp index 8648106bf..42820f5a2 100644 --- a/skgbasegui/skgwebview.cpp +++ b/skgbasegui/skgwebview.cpp @@ -1,347 +1,347 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ /** @file * A web viewer with more features. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgwebview.h" #include #include #include #include #include #include #include #include #include #ifdef SKG_WEBENGINE #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "skgmainpanel.h" #include "skgtraces.h" #ifdef SKG_WEBENGINE class SKGWebEnginePage : public QWebEnginePage { Q_DISABLE_COPY(SKGWebEnginePage) public: explicit SKGWebEnginePage(QObject* p = nullptr) : QWebEnginePage(p) {} virtual bool acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame) override { if (type == QWebEnginePage::NavigationTypeLinkClicked) { - if (url.toString().startsWith(QLatin1String("http://linkclicked/"))) { + if (url.toString().startsWith(QLatin1String("https://linkclicked/"))) { SKGWebView* v = qobject_cast(this->view()); if (v) { v->emitLinkClicked(url); return false; } } else { SKGMainPanel::getMainPanel()->openPage(url); return false; } } return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); } }; SKGWebView::SKGWebView(QWidget* iParent, const char* name, bool iWithContextualMenu) : QWebEngineView(iParent), m_ContextualMenu(iWithContextualMenu) { setObjectName(name); setPage(new SKGWebEnginePage(this)); if (m_ContextualMenu) { this->installEventFilter(this); page()->installEventFilter(this); } connect(this, &SKGWebView::fileExporter, this, [](const QString & iFileName) { QDesktopServices::openUrl(QUrl::fromLocalFile(iFileName)); }); } void SKGWebView::emitLinkClicked(const QUrl& iURL) { Q_EMIT linkClicked(iURL); } #else SKGWebView::SKGWebView(QWidget* iParent, const char* name) : QWebView(iParent) { setObjectName(name); this->installEventFilter(this); page()->installEventFilter(this); connect(this, &SKGWebView::fileExporter, this, [](const QString & iFileName) { QDesktopServices::openUrl(QUrl::fromLocalFile(iFileName)); }); connect(this, &SKGWebView::linkClicked, this, [ = ](const QUrl & val) { SKGMainPanel::getMainPanel()->openPage(val); }); this->page()->setForwardUnsupportedContent(true); connect(this->page(), &QWebPage::unsupportedContent, this, [ = ](QNetworkReply * reply) { openReply(reply); }); connect(this->page(), &QWebPage::downloadRequested, this, [ = ](const QNetworkRequest & request) { QNetworkAccessManager manager; openReply(manager.get(request)); }); } void SKGWebView::openReply(QNetworkReply* reply) { QString fileName = QDir::tempPath() + '/' + "export.csv"; QFile file(fileName); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { file.write(reply->readAll()); file.close(); } QDesktopServices::openUrl(QUrl::fromLocalFile(fileName)); reply->deleteLater(); } #endif SKGWebView::~SKGWebView() = default; QString SKGWebView::getState() { SKGTRACEINFUNC(10) QDomDocument doc(QStringLiteral("SKGML")); QDomElement root = doc.createElement(QStringLiteral("parameters")); doc.appendChild(root); root.setAttribute(QStringLiteral("zoomFactor"), SKGServices::intToString(qMax(qRound(30.0 * log10(zoomFactor())), -10))); return doc.toString(); } void SKGWebView::setState(const QString& iState) { SKGTRACEINFUNC(10) QDomDocument doc(QStringLiteral("SKGML")); doc.setContent(iState); QDomElement root = doc.documentElement(); QString zoomPosition = root.attribute(QStringLiteral("zoomFactor")); if (zoomPosition.isEmpty()) { zoomPosition = '0'; } double z = qPow(10, (static_cast(SKGServices::stringToInt(zoomPosition)) / 30.0)); setZoomFactor(z); emit zoomChanged(z); } void SKGWebView::contextMenuEvent(QContextMenuEvent* iEvent) { if (iEvent != nullptr) { auto menu = new QMenu(this); #ifdef SKG_WEBENGINE menu->addAction(pageAction(QWebEnginePage::Copy)); #else menu->addAction(pageAction(QWebPage::Copy)); #endif QAction* actPrint = menu->addAction(SKGServices::fromTheme(QStringLiteral("printer")), i18nc("Action", "Print...")); connect(actPrint, &QAction::triggered, this, &SKGWebView::onPrint); menu->addAction(KStandardAction::printPreview(this, SLOT(onPrintPreview()), this)); QAction* actExport = menu->addAction(SKGServices::fromTheme(QStringLiteral("document-export")), i18nc("Noun, user action", "Export...")); connect(actExport, &QAction::triggered, this, &SKGWebView::onExport); menu->popup(this->mapToGlobal(iEvent->pos())); iEvent->accept(); } } bool SKGWebView::eventFilter(QObject* iObject, QEvent* iEvent) { SKGTRACEINFUNC(10) if ((iEvent != nullptr) && iEvent->type() == QEvent::Wheel) { auto* e = dynamic_cast(iEvent); if (e != nullptr) { if (e->orientation() == Qt::Vertical && ((QApplication::keyboardModifiers() &Qt::ControlModifier) != 0u)) { int numDegrees = e->delta() / 8; int numTicks = numDegrees / 15; if (numTicks > 0) { onZoomIn(); } else { onZoomOut(); } e->setAccepted(true); return true; } } } return QWidget::eventFilter(iObject, iEvent); } void SKGWebView::onZoomIn() { _SKGTRACEINFUNC(10) int z = qMin(static_cast(qRound(30.0 * log10(zoomFactor()))) + 1, 10); setZoomFactor(qPow(10, static_cast(z) / 30.0)); emit zoomChanged(z); } void SKGWebView::onZoomOut() { _SKGTRACEINFUNC(10) int z = qMax(static_cast(qRound(30.0 * log10(zoomFactor()))) - 1, -10); setZoomFactor(qPow(10, static_cast(z) / 30.0)); emit zoomChanged(z); } void SKGWebView::onZoomOriginal() { _SKGTRACEINFUNC(10) setZoomFactor(0); emit zoomChanged(0); } void SKGWebView::exportInFile(const QString& iFileName) { QString extension = QFileInfo(iFileName).suffix().toUpper(); if (extension == QStringLiteral("ODT")) { #ifdef SKG_WEBENGINE page()->toHtml([ = ](const QString & result) { QTextDocument doc; QTextDocumentWriter docWriter(iFileName); doc.setHtml(result); docWriter.write(&doc); emit fileExporter(iFileName); }); #else QTextDocument doc; QTextDocumentWriter docWriter(iFileName); doc.setHtml(page()->mainFrame()->toHtml()); docWriter.write(&doc); emit fileExporter(iFileName); #endif } else if (extension == QStringLiteral("PDF")) { #ifdef SKG_WEBENGINE page()->printToPdf(iFileName); connect(page(), &QWebEnginePage::pdfPrintingFinished, this, &SKGWebView::fileExporter); #else QPrinter printer; printer.setOutputFileName(iFileName); print(&printer); emit fileExporter(iFileName); #endif } else if (extension == QStringLiteral("HTML") || extension == QStringLiteral("HTM")) { #ifdef SKG_WEBENGINE page()->toHtml([ = ](const QString & result) { QSaveFile file(iFileName); if (file.open(QIODevice::WriteOnly)) { QTextStream out(&file); out << result; // Close file file.commit(); emit fileExporter(iFileName); } }); #else QSaveFile file(iFileName); if (file.open(QIODevice::WriteOnly)) { QTextStream out(&file); out << page()->mainFrame()->toHtml(); // Close file file.commit(); emit fileExporter(iFileName); } #endif } else { QImage image(this->size(), QImage::Format_ARGB32); QPainter painter(&image); this->render(&painter); painter.end(); image.save(iFileName); emit fileExporter(iFileName); } } void SKGWebView::onExport() { _SKGTRACEINFUNC(10) QString fileName = SKGMainPanel::getSaveFileName(QStringLiteral("kfiledialog:///IMPEXP"), QStringLiteral("application/pdf text/html application/vnd.oasis.opendocument.text image/png image/jpeg image/gif image/tiff"), this); if (fileName.isEmpty()) { return; } exportInFile(fileName); } void SKGWebView::onPrintPreview() { SKGTRACEINFUNC(10) QPointer dialog = new QPrintPreviewDialog(this); #ifdef SKG_WEBENGINE // TODO(SMI): QWebEngine connect(dialog.data(), &QPrintPreviewDialog::paintRequested, page(), [&](QPrinter * printer) { page()->print(printer, [](bool) {}); }); #else connect(dialog.data(), &QPrintPreviewDialog::paintRequested, this, &SKGWebView::print); #endif dialog->exec(); } void SKGWebView::onPrint() { _SKGTRACEINFUNC(10) QPointer dialog = new QPrintDialog(&m_printer, this); if (dialog->exec() == QDialog::Accepted) { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); #ifdef SKG_WEBENGINE page()->print(&m_printer, [](bool) {}); #else print(&m_printer); #endif QApplication::restoreOverrideCursor(); } } diff --git a/skgbasemodeler/skgerror.h b/skgbasemodeler/skgerror.h index 94df17d14..5e4bcb624 100644 --- a/skgbasemodeler/skgerror.h +++ b/skgbasemodeler/skgerror.h @@ -1,260 +1,260 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #ifndef SKGERROR_H #define SKGERROR_H /** @file * This file defines classes SKGError and macros. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include #include #include "skgbasemodeler_export.h" #include "skgdefine.h" /** * To facilitate the error management */ #define IFKO(ERROR) \ if (Q_UNLIKELY(ERROR)) /** * To facilitate the error management */ #define IFOK(ERROR) \ if (Q_LIKELY(!(ERROR))) /** * To facilitate the error management */ #define IFOKDO(ERROR, ACTION) \ IFOK(ERROR) {(ERROR) = ACTION;} /** * This class manages errors */ class SKGBASEMODELER_EXPORT SKGError final : public QObject { Q_OBJECT /** * Return code of the error */ Q_PROPERTY(int returnCode READ getReturnCode WRITE setReturnCode NOTIFY modified) /** * Message of the error */ Q_PROPERTY(QString message READ getMessage WRITE setMessage NOTIFY modified) /** * Action of the error */ Q_PROPERTY(QString action READ getAction WRITE setAction NOTIFY modified) /** * To know if it is a success of the error */ Q_PROPERTY(bool succeeded READ isSucceeded NOTIFY modified) /** * To know if it is a failure of the error */ Q_PROPERTY(bool failed READ isFailed NOTIFY modified) public: /** * Constructor */ explicit SKGError(); /** * Copy constructor * @param iError the error to copy */ SKGError(const SKGError& iError); /** * Move constructor * @param iError the error to copy */ SKGError(SKGError&& iError) noexcept; /** * Constructor * @param iRc the error code * @param iMessage the error message - * @param iAction the error action. This is normally a recovery action (example: skg://file_save, http://google.com, ...) + * @param iAction the error action. This is normally a recovery action (example: skg://file_save, https://google.com, ...) */ SKGError(int iRc, QString iMessage, QString iAction = QString()); /** * Destructor */ ~SKGError() override; /** * Operator affectation * @param iError the error to copy */ SKGError& operator= (const SKGError& iError); /** * To know if this is an error or not. Equivalent to @see isSucceeded * @return true or false */ bool operator!() const; /** * To know if this is an error or not. Equivalent to @see isFailed * @return true or false */ operator bool() const; /** * To know if it is an error or not * @return true: It is an error * false: It is not an error (it could be a warning) */ bool isFailed() const; /** * To know if it is an error or not * @return true: It is not an error (it could be a warning) * false: It is an error */ bool isSucceeded() const; /** * To know if it is a warning or not * @return true: It is a warning * false: It is not a warning */ bool isWarning() const; /** * Return the return code associated to this error * @return 0 : It is not an error (SUCCEEDED) * <0: It is just a warning (SUCCEEDED) * >0: It is not error (FAILED) */ int getReturnCode() const; /** * Return the message associated to this error * @return the message */ QString getMessage() const; /** * Return the full message associated to this error * @return the full message */ QString getFullMessage() const; /** * Return the full message with historical associated to this error * @return the full message */ QString getFullMessageWithHistorical() const; /** * Return the size of the historical associated to this error * @return the size */ int getHistoricalSize() const; /** * Return the action associated to this error * @return the action */ QString getAction() const; /** * Return previous error associated to this SKGError in the historical * @return previous error (null if not exist) * WARNING: this pointer mustn't be deleted */ SKGError* getPreviousError() const; public Q_SLOTS: /** * Set the return code associated to this error * @param iReturnCode the return code * 0 : It is not an error (SUCCEEDED) * <0: It is just a warning (SUCCEEDED) * >0: It is not error (FAILED) * @return itself to facilitate usage */ SKGError& setReturnCode(int iReturnCode); /** * Set the message associated to this error * @param iMessage the message * @return itself to facilitate usage */ SKGError& setMessage(const QString& iMessage); /** * Set the action associated to this error * @param iAction the action * @return itself to facilitate usage */ SKGError& setAction(const QString& iAction); /** * Add a new historical message to the current error. * @param iRc the error code * @param iMessage the error message - * @param iAction the error action. This is normally a recovery action (example: skg://file_save, http://google.com, ...) + * @param iAction the error action. This is normally a recovery action (example: skg://file_save, https://google.com, ...) * @return itself to facilitate usage */ SKGError& addError(int iRc, const QString& iMessage, const QString& iAction = QString()); /** * Add a new historical message to the current error. * @param iError the error * @return itself to facilitate usage */ SKGError& addError(const SKGError& iError); Q_SIGNALS: /** * This signal is launched when the error is modified */ void modified(); private: /** * the return code of the error * 0 : It is not an error (SUCCEEDED) * <0: It is just a warning (SUCCEEDED) * >0: It is not error (FAILED) */ int m_rc{0}; /** * the message of the error */ QString m_message; /** * the action of the error */ QString m_action; /** * the previous error on this branch */ SKGError* m_previousError{nullptr}; }; #endif // SKGERROR_H diff --git a/skrooge/main.cpp b/skrooge/main.cpp index 70d934911..458e011f6 100644 --- a/skrooge/main.cpp +++ b/skrooge/main.cpp @@ -1,218 +1,218 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ /** @file * This file defines the main of skrooge. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgmainpanel.h" #include "skgdocumentbank.h" #include "skgtraces.h" #include #include #include #include #include #include #include #include /** * To compute the version */ #define VER1_(x) #x /** * To compute the version */ #define VER_(x) VER1_(x) /** * To compute the version */ #define VER VER_(SKGVERSION) void SKGMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { Q_UNUSED(context) switch (type) { case QtDebugMsg: SKGTRACEL(1) << "DEBUG: " << msg << endl; break; case QtWarningMsg: SKGTRACE << "WARNING: " << msg << endl; break; case QtCriticalMsg: SKGTRACE << "CRITICAL: " << msg << endl; break; case QtFatalMsg: SKGTRACE << "FATAL: " << msg << endl; abort(); default: SKGTRACE << "INFO: " << msg << endl; break; } } /** * The main of the application * @param argc number of arguments * @param argv arguments * @return return code */ int main(int argc, char** argv) { qInstallMessageHandler(SKGMessageOutput); if (!SKGServices::getEnvVariable(QStringLiteral("SKGHIGHDPI")).isEmpty()) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); } QApplication app(argc, argv); QIcon appIcon = SKGServices::fromTheme(QStringLiteral("skrooge")); if (!appIcon.isNull()) { app.setWindowIcon(appIcon); } // Migration kf4 => kf5 Kdelibs4ConfigMigrator migrate(QStringLiteral("skrooge")); migrate.setConfigFiles(QStringList() << QStringLiteral("skroogerc")); migrate.migrate(); // To use CPU instead CPU for QML (needed for printing) qputenv("QMLSCENE_DEVICE", "softwarecontext"); #ifdef SKG_WEBENGINE // DrKonqi // https://www.dvratil.cz/2018/10/drkonqi-and-qtwebengine/ const auto chromiumFlags = qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"); if (!chromiumFlags.contains("disable-in-process-stack-traces")) { qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromiumFlags + " --disable-in-process-stack-traces"); } #endif KLocalizedString::setApplicationDomain("skrooge"); KAboutData about(QStringLiteral("skrooge"), i18nc("The name of the application", "Skrooge"), QStringLiteral(VER), i18nc("The description of the application", "Personal finances management made simple"), KAboutLicense::GPL_V3, i18nc("Fullname", "(c) 2007-%1 Stephane MANKOWSKI & Guillaume DE BURE", QDate::currentDate().toString(QStringLiteral("yyyy"))), QLatin1String(""), - QStringLiteral("http://skrooge.org")); + QStringLiteral("https://skrooge.org")); about.addAuthor(i18nc("Fullname", "Stephane MANKOWSKI"), i18nc("A job description", "Architect & Developer"), QStringLiteral("stephane@mankowski.fr"), QLatin1String("") , QStringLiteral("miraks") ); about.addAuthor(i18nc("Fullname", "Guillaume DE BURE"), i18nc("A job description", "Developer"), QStringLiteral("guillaume.debure@gmail.com"), QLatin1String("") , QStringLiteral("willy9") ); about.addAuthor(i18nc("Fullname", "Siddharth SHARMA"), i18nc("A job description", "Developer - Google Summer Of Code 2010"), QStringLiteral("siddharth.kde@gmail.com"), QLatin1String("") , QStringLiteral("h4xordood") ); about.setOtherText(i18nc("The description of the application", "The application name is inspired by Charles Dicken's tale A Christmas Carol, where the main character, Ebenezer Scrooge, a grumpy old narrow man, gets visited by three ghosts who change the way he sees the world, in a good way.")); about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); about.setOrganizationDomain("kde.org"); about.addCredit(QStringLiteral("vicnet, noidea, rbruce, JesusM, schunka, SylvaiNN, Wolf, Hizoka, neutron68, blep0, BigaAl, steffie, skierpage ..."), i18nc("Reason of the about/credit", "Users helping us to improve this application")); KAboutData::setApplicationData(about); QApplication::setApplicationName(about.componentName()); QApplication::setOrganizationDomain(about.organizationDomain()); QApplication::setApplicationVersion(about.version()); QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); parser.addPositionalArgument(QStringLiteral("URL"), i18nc("Application argument", "Document to open")); QCommandLineOption envOption(QStringList() << QStringLiteral("e") << QStringLiteral("env"), i18nc("Application argument", "Display environment variables used by this application.")); parser.addOption(envOption); about.setupCommandLine(&parser); parser.process(app); about.processCommandLine(&parser); if (parser.isSet(envOption)) { SKGTRACESUITE << parser.helpText() << endl; SKGTRACESUITE << i18nc("Help", "Environment variables:") << endl; SKGTRACESUITE << i18nc("Help, do not translate x", " %1: To enable traces. x is the level of traces expected. This enables the debug mode too.", "export SKGTRACE=x") << endl; SKGTRACESUITE << i18nc("Help", " %1: To enable the profiling. This enables the debug mode too.", "export SKGTRACEPERFO=1") << endl; SKGTRACESUITE << i18nc("Help do not translate x", " %1: To dump sql order taking more than x ms.", "export SKGTRACESQL=x") << endl; SKGTRACESUITE << i18nc("Help", " %1: To enable the high DPI mode.", "export SKGHIGHDPI=1") << endl; return 0; } // Manage unicity KDBusService service(SKGServices::getEnvVariable(QStringLiteral("SKGNOTUNIQUE")).isEmpty() ? KDBusService::Unique : KDBusService::Multiple); QObject::connect(&service, &KDBusService::activateRequested, &service, [ = ](const QStringList & arguments, const QString & workingDirectory) { Q_UNUSED(workingDirectory) SKGMainPanel::getMainPanel()->processArguments(arguments); }); // Creating a main panel on a bank document SKGDocumentBank doc; if (!SKGServices::getEnvVariable(QStringLiteral("SKGTEST")).isEmpty()) { QTimer::singleShot(5000, Qt::CoarseTimer, &app, &QApplication::quit); } // Build list of arguments QStringList argument = parser.positionalArguments(); // Creation splash screen QSplashScreen* m_splash = nullptr; KConfigGroup pref = SKGMainPanel::getMainConfigGroup(); if (pref.readEntry("show_splash_screen", true)) { QString splashPathRelativePath = KAboutData::applicationData().componentName() % "/images/splash.png"; QString splashPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, splashPathRelativePath.toLatin1()); if (!splashPath.isEmpty()) { QPixmap pix(splashPath); m_splash = new QSplashScreen(pix); if (m_splash != nullptr) { m_splash->setMask(pix.createMaskFromColor(Qt::blue)); m_splash->show(); m_splash->showMessage(i18nc("Splash screen message", "Loading ..."), Qt::AlignLeft, QColor(221, 130, 8)); // krazy:exclude=qmethods } } else { SKGTRACE << "WARNING: Splash screen (" << splashPathRelativePath << ") not found !" << endl; } } // First instance QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); #ifdef SKG_WEBENGINE QApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); #endif auto m_widget = new SKGMainPanel(m_splash, &doc); m_widget->processArguments(argument); m_widget->setUnifiedTitleAndToolBarOnMac(true); m_widget->show(); if (m_splash != nullptr) { SKGTRACEINFUNC(1) m_splash->clearMessage(); m_splash->finish(m_widget); } int rc = QApplication::exec(); // krazy:exclude=crashy delete m_splash; SKGTraces::dumpProfilingStatistics(); return rc; } diff --git a/skroogeconvert/main.cpp b/skroogeconvert/main.cpp index a69378202..bc2bf4c5c 100644 --- a/skroogeconvert/main.cpp +++ b/skroogeconvert/main.cpp @@ -1,202 +1,202 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ /** @file * This file defines the main of skroogeconvert. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgdocumentbank.h" #include "skgimportexportmanager.h" #include "skgtraces.h" #include "skgtransactionmng.h" #include #include #include #include #include #include #include /** * To compute the version */ #define VER1_(x) #x /** * To compute the version */ #define VER_(x) VER1_(x) /** * To compute the version */ #define VER VER_(SKGVERSION) /** * The main of the application * @param argc number of arguments * @param argv arguments * @return return code */ int main(int argc, char** argv) { KLocalizedString::setApplicationDomain("skrooge"); KAboutData about(QStringLiteral("skroogeconvert"), i18nc("The name of the application", "Skrooge Convert"), QStringLiteral(VER), i18nc("The description of the application", "A conversion tool for financial files (KMyMoney, GnuCash, Skrooge, ...)"), KAboutLicense::GPL_V3, i18nc("Fullname", "(c) 2007-%1 Stephane MANKOWSKI & Guillaume DE BURE", QDate::currentDate().toString(QStringLiteral("yyyy"))), QString(), - QStringLiteral("http://skrooge.org")); + QStringLiteral("https://skrooge.org")); about.addAuthor(i18nc("Fullname", "Stephane MANKOWSKI"), i18nc("A job description", "Architect & Developer"), QStringLiteral("stephane@mankowski.fr"), QString() , QStringLiteral("miraks") ); about.addAuthor(i18nc("Fullname", "Guillaume DE BURE"), i18nc("A job description", "Developer"), QStringLiteral("guillaume.debure@gmail.com"), QString() , QStringLiteral("willy9") ); about.setOtherText(i18nc("The description of the application", "The application name is inspired by Charles Dicken's tale A Christmas Carol, where the main character, Ebenezer Scrooge, a grumpy old narrow man, gets visited by three ghosts who change the way he sees the world, in a good way.")); about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); KAboutData::setApplicationData(about); QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(about.componentName()); QCoreApplication::setOrganizationDomain(about.organizationDomain()); QCoreApplication::setApplicationVersion(about.version()); QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("in"), i18nc("Application argument", "Input file. Supported formats:\n%1", SKGImportExportManager::getImportMimeTypeFilter(false)), QStringLiteral("file"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("out"), i18nc("Application argument", "Output file. Supported formats:\n%1", SKGImportExportManager::getExportMimeTypeFilter(false)), QStringLiteral("file"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("param"), i18nc("Application argument", "Name of a parameter"), QStringLiteral("name"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("value"), i18nc("Application argument", "Value of a parameter"), QStringLiteral("value"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("param_export"), i18nc("Application argument", "Name of a parameter for export"), QStringLiteral("name_export"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("value_export"), i18nc("Application argument", "Value of a parameter for export"), QStringLiteral("value_export"))); about.setupCommandLine(&parser); parser.process(app); about.processCommandLine(&parser); // Build list of arguments SKGError err; if (!parser.isSet(QStringLiteral("in"))) { err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Missing -in option")); } else if (!parser.isSet(QStringLiteral("out"))) { err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Missing -out option")); } // Initialisation of a bank document SKGDocumentBank document; document.setComputeBalances(false); IFOKDO(err, document.initialize()) IFOK(err) { // Import QString file = parser.value(QStringLiteral("in")); QFileInfo fi(file); if (fi.exists()) { file = fi.absoluteFilePath(); } SKGImportExportManager imp(&document, QUrl::fromLocalFile(file)); QMap parameters = imp.getImportParameters(); QStringList params = parser.values(QStringLiteral("param")); QStringList values = parser.values(QStringLiteral("value")); int nb = qMin(params.count(), values.count()); for (int i = 0; i < nb; ++i) { parameters[params.at(i)] = values.at(i); } imp.setImportParameters(parameters); SKGTRACESEPARATOR; SKGTRACE << i18nc("Title of a console trace section", " Import parameters") << endl; QMapIterator i(imp.getImportParameters()); while (i.hasNext()) { i.next(); SKGTRACE << " " << i.key() << "=" << i.value() << endl; } SKGTRACESEPARATOR; SKGTRACE << i18nc("Title of a console trace section", " Imported file:") << imp.getFileName().toDisplayString() << endl; if (fi.suffix().toUpper() == QStringLiteral("SKG") || fi.suffix().toUpper() == QStringLiteral("SQLITE") || fi.suffix().toUpper() == QStringLiteral("SQLCIPHER")) { err = document.load(file, parameters[QStringLiteral("password")]); } else { SKGBEGINTRANSACTION(document, QStringLiteral("IMPORT"), err) IFOKDO(err, imp.importFile()) } } IFOK(err) { // Export QString file = parser.value(QStringLiteral("out")); QFileInfo fi(file); if (fi.exists()) { file = fi.absoluteFilePath(); } SKGImportExportManager exp(&document, QUrl::fromLocalFile(file)); QMap parameters = exp.getExportParameters(); QStringList params = parser.values(QStringLiteral("param_export")); QStringList values = parser.values(QStringLiteral("value_export")); int nb = qMin(params.count(), values.count()); for (int i = 0; i < nb; ++i) { parameters[params.at(i)] = values.at(i); } exp.setExportParameters(parameters); SKGTRACESEPARATOR; SKGTRACE << i18nc("Title of a console trace section", " Export parameters") << endl; QMapIterator i(exp.getExportParameters()); while (i.hasNext()) { i.next(); SKGTRACE << " " << i.key() << "=" << i.value() << endl; } SKGTRACESEPARATOR; SKGTRACE << i18nc("Title of a console trace section", " Exported file:") << exp.getFileName().toDisplayString() << endl; if (fi.suffix().toUpper() == QStringLiteral("SKG")) { err = document.saveAs(file, true); } else { err = exp.exportFile(); } } // Dump getMessages SKGDocument::SKGMessageList oMessages; IFOKDO(err, document.getMessages(document.getCurrentTransaction(), oMessages)) int nb = oMessages.count(); if (nb > 0) { SKGTRACESEPARATOR; for (int i = 0; i < nb; ++i) { SKGTRACE << oMessages.at(i).Text << endl; } } IFKO(err) { SKGTRACESUITE << err.getFullMessageWithHistorical() << endl; SKGTRACESEPARATOR; SKGTRACE << i18nc("Title of a console trace section", " FAILED") << endl; SKGTRACESEPARATOR; } else { SKGTRACESEPARATOR; SKGTRACE << i18nc("Title of a console trace section", " SUCCESSFUL") << endl; SKGTRACESEPARATOR; } SKGTraces::dumpProfilingStatistics(); return err.getReturnCode(); } diff --git a/tests/skgbankmodelertest/skgtestbudget.cpp b/tests/skgbankmodelertest/skgtestbudget.cpp index eaade6237..784572a72 100644 --- a/tests/skgbankmodelertest/skgtestbudget.cpp +++ b/tests/skgbankmodelertest/skgtestbudget.cpp @@ -1,159 +1,159 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ /** @file * This file is a test script. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgtestmacro.h" #include "skgbankincludes.h" #include "skgimportexportmanager.h" /** * The main function of the unit test * @param argc the number of arguments * @param argv the list of arguments */ int main(int argc, char** argv) { Q_UNUSED(argc) Q_UNUSED(argv) // Init test SKGINITTEST(true) // ============================================================================ { // Import SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.load()"), document1.load(SKGTest::getTestPath(QStringLiteral("IN")) % "skgtestbudget/budget.skg"), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("BUDGET_CREATION"), err) SKGTESTERROR(QStringLiteral("BUDGET.createAutomaticBudget"), SKGBudgetObject::createAutomaticBudget(&document1, 2010, 2010, true, true), true) SKGTESTERROR(QStringLiteral("BUDGET.balanceBudget"), SKGBudgetObject::balanceBudget(&document1, 2010), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("BUDGETRULE_CREATION"), err) SKGBudgetRuleObject br(&document1); SKGTESTERROR(QStringLiteral("BUDGETRULE.enableYearCondition"), br.enableYearCondition(true), true) SKGTESTBOOL("BUDGETRULE.isYearConditionEnabled", br.isYearConditionEnabled(), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.enableYearCondition"), br.enableYearCondition(false), true) SKGTESTBOOL("BUDGETRULE.isYearConditionEnabled", br.isYearConditionEnabled(), false) SKGTESTERROR(QStringLiteral("BUDGETRULE.setBudgetYear"), br.setBudgetYear(2010), true) SKGTEST(QStringLiteral("BUDGETRULE.getBudgetYear"), br.getBudgetYear(), 2010) SKGTESTERROR(QStringLiteral("BUDGETRULE.enableMonthCondition"), br.enableMonthCondition(true), true) SKGTESTBOOL("BUDGETRULE.isMonthConditionEnabled", br.isMonthConditionEnabled(), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.enableMonthCondition"), br.enableMonthCondition(false), true) SKGTESTBOOL("BUDGETRULE.isMonthConditionEnabled", br.isMonthConditionEnabled(), false) SKGTESTERROR(QStringLiteral("BUDGETRULE.setBudgetMonth"), br.setBudgetMonth(10), true) SKGTEST(QStringLiteral("BUDGETRULE.getBudgetMonth"), br.getBudgetMonth(), 10) SKGTESTERROR(QStringLiteral("BUDGETRULE.setOrder"), br.setOrder(1.0), true) SKGTEST(QStringLiteral("BUDGETRULE.getOrder"), br.getOrder(), 1.0) SKGTESTERROR(QStringLiteral("BUDGETRULE.setOrder"), br.setOrder(-1), true) SKGTEST(QStringLiteral("BUDGETRULE.getOrder"), br.getOrder(), 1.0) SKGTESTERROR(QStringLiteral("BUDGETRULE.enableCategoryCondition"), br.enableCategoryCondition(true), true) SKGTESTBOOL("BUDGETRULE.isCategoryConditionEnabled", br.isCategoryConditionEnabled(), true) SKGCategoryObject cat; SKGTESTERROR(QStringLiteral("BUDGETRULE.enableCategoryChange"), br.enableCategoryChange(br.isCategoryChangeEnabled()), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.getBudgetCategory"), br.getBudgetCategory(cat), false) SKGTESTERROR(QStringLiteral("BUDGETRULE.createPathCategory"), SKGCategoryObject::createPathCategory(&document1, QStringLiteral("category_55 > category_57"), cat), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.removeBudgetCategory"), br.removeBudgetCategory(), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.setBudgetCategory"), br.setBudgetCategory(cat), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.setCondition"), br.setCondition(SKGBudgetRuleObject::NEGATIVE), true) SKGTEST(QStringLiteral("BUDGETRULE.getCondition"), static_cast(br.getCondition()), static_cast(SKGBudgetRuleObject::NEGATIVE)) SKGTESTERROR(QStringLiteral("BUDGETRULE.setCondition"), br.setCondition(SKGBudgetRuleObject::POSITIVE), true) SKGTEST(QStringLiteral("BUDGETRULE.getCondition"), static_cast(br.getCondition()), static_cast(SKGBudgetRuleObject::POSITIVE)) SKGTESTERROR(QStringLiteral("BUDGETRULE.setCondition"), br.setCondition(SKGBudgetRuleObject::ALL), true) SKGTEST(QStringLiteral("BUDGETRULE.getCondition"), static_cast(br.getCondition()), static_cast(SKGBudgetRuleObject::ALL)) SKGTESTERROR(QStringLiteral("BUDGETRULE.setQuantity"), br.setQuantity(100, false), true) SKGTEST(QStringLiteral("BUDGETRULE.getQuantity"), br.getQuantity(), 100) SKGTESTBOOL("BUDGETRULE.isAbolute", br.isAbolute(), false) SKGTESTERROR(QStringLiteral("BUDGETRULE.setTransfer"), br.setTransfer(SKGBudgetRuleObject::CURRENT), true) SKGTEST(QStringLiteral("BUDGETRULE.getTransferMode"), static_cast(br.getTransferMode()), static_cast(SKGBudgetRuleObject::CURRENT)) SKGTESTERROR(QStringLiteral("BUDGETRULE.setTransfer"), br.setTransfer(SKGBudgetRuleObject::NEXT), true) SKGTEST(QStringLiteral("BUDGETRULE.getTransferMode"), static_cast(br.getTransferMode()), static_cast(SKGBudgetRuleObject::NEXT)) SKGTESTERROR(QStringLiteral("BUDGETRULE.save"), br.save(), true) SKGBudgetRuleObject br2 = br; SKGBudgetRuleObject br3(br); SKGBudgetRuleObject br4(static_cast(br)); SKGBudgetRuleObject br5(SKGObjectBase(&document1, QStringLiteral("xxx"), br.getID())); SKGTESTERROR(QStringLiteral("BUDGETRULE.processAllRules"), SKGBudgetRuleObject::processAllRules(&document1), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.setTransfer"), br.setTransfer(SKGBudgetRuleObject::YEAR), true) SKGTEST(QStringLiteral("BUDGETRULE.getTransferMode"), static_cast(br.getTransferMode()), static_cast(SKGBudgetRuleObject::YEAR)) SKGTESTERROR(QStringLiteral("BUDGETRULE.save"), br.save(), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.processAllRules"), SKGBudgetRuleObject::processAllRules(&document1), true) } SKGTESTERROR(QStringLiteral("document1.saveAs()"), document1.saveAs(SKGTest::getTestPath(QStringLiteral("OUT")) % "skgtestbudget/budget.skg", true), true) } // ============================================================================ { // Import SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.load()"), document1.load(SKGTest::getTestPath(QStringLiteral("IN")) % "skgtestbudget/320323.skg"), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("BUDGET_PROCESS"), err) SKGTESTERROR(QStringLiteral("BUDGETRULE.processAllRules"), SKGBudgetRuleObject::processAllRules(&document1), true) } document1.dump(DUMPBUDGET); bool check = false; document1.existObjects(QStringLiteral("v_budget_display"), QStringLiteral("t_PERIOD='2013-02' AND t_CATEGORY='Alimentation' AND f_budgeted_modified=300"), check); SKGTESTBOOL("BUDGETRULE.Alimentation 2013-02 300", check, true) document1.existObjects(QStringLiteral("v_budget_display"), QStringLiteral("t_PERIOD='2013-02' AND t_CATEGORY='Loisirs' AND f_budgeted_modified=2100"), check); SKGTESTBOOL("BUDGETRULE.Loisirs 2013-02 2100", check, true) SKGObjectBase bo; SKGTESTERROR(QStringLiteral("document1.getObject()"), document1.getObject(QStringLiteral("v_budget_display"), QStringLiteral("t_PERIOD='2013-02' AND t_CATEGORY='Loisirs' AND f_budgeted_modified=2100"), bo), true) SKGBudgetObject b(bo); SKGTEST(QStringLiteral("BUDGET.getBudgetedAmount"), b.getBudgetedAmount(), 300) SKGCategoryObject cat; SKGTESTERROR(QStringLiteral("BUDGET.getCategory"), b.getCategory(cat), true) - SKGTEST(QStringLiteral("BUDGET.getModificationReasons"), b.getModificationReasons().remove(","), QStringLiteral("Transfer of -1800 from ' 2013-01 2000.0' to 'Loisirs 2013-02 300.0' due to the rule 'All 100.0% Next Loisirs'")) + SKGTEST(QStringLiteral("BUDGET.getModificationReasons"), b.getModificationReasons().remove(','), QStringLiteral("Transfer of -1800 from ' 2013-01 2000.0' to 'Loisirs 2013-02 300.0' due to the rule 'All 100.0% Next Loisirs'")) SKGTESTERROR(QStringLiteral("BUDGET.removeCategory"), b.removeCategory(), true) SKGTESTERROR(QStringLiteral("BUDGET.enableSubCategoriesInclusion"), b.enableSubCategoriesInclusion(true), true) SKGTESTBOOL(QStringLiteral("BUDGET.isSubCategoriesInclusionEnabled"), b.isSubCategoriesInclusionEnabled(), true) SKGTESTERROR(QStringLiteral("BUDGET.enableSubCategoriesInclusion"), b.enableSubCategoriesInclusion(false), true) SKGTESTBOOL(QStringLiteral("BUDGET.isSubCategoriesInclusionEnabled"), b.isSubCategoriesInclusionEnabled(), false) SKGBudgetObject bu; } // End test SKGENDTEST() } diff --git a/tests/skgmyapplitest/main.cpp b/tests/skgmyapplitest/main.cpp index de3863e84..63108b756 100644 --- a/tests/skgmyapplitest/main.cpp +++ b/tests/skgmyapplitest/main.cpp @@ -1,80 +1,80 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ /** @file * This file defines the main of SKGMyAppliTest. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skguniqueapplication.h" #include "skgdocument.h" #include "skgtraces.h" #include #include #include /** * The main of the application * @param argc number of arguments * @param argv arguments * @return return code */ int main(int argc, char** argv) { KAboutData about("skgmyapplitest", 0, ki18nc("The name of the application", "SKGMyAppliTest"), "0.1.0", ki18nc("The description of the application", "Blablabla"), KAboutLicense::GPL_V3, i18nc("Fullname", "(c) 2007-%1 Stephane MANKOWSKI & Guillaume DE BURE", QDate::currentDate().toString(QStringLiteral("yyyy"))), "", - "http://skrooge.org"); + "https://skrooge.org"); about.addAuthor(ki18nc("Fullname", "Stephane MANKOWSKI"), ki18nc("A job description", "Architect & Developer"), "stephane@mankowski.fr"); about.setOtherText(ki18nc("The description of the application", "An application test.")); QApplication app(argc, argv); QCommandLineParser parser; KAboutData::setApplicationData(aboutData); app.setApplicationName(aboutData.componentName()); app.setApplicationDisplayName(aboutData.displayName()); app.setOrganizationDomain(aboutData.organizationDomain()); app.setApplicationVersion(aboutData.version()); parser.addVersionOption(); parser.addHelpOption(); //PORTING SCRIPT: adapt aboutdata variable if necessary aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("+[URL]"), i18nc("Application argument", "Document to open"))); int rc = 0; if (!SKGUniqueApplication::start()) { fprintf(stderr, "SKGMyAppliTest is already running!\n"); } else { // Creating a main panel on a generic document SKGDocument doc; SKGUniqueApplication kApp(&doc); rc = kApp.exec(); // krazy:exclude=crashy } SKGTraces::dumpProfilingStatistics(); return rc; }