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

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007 * This file defines classes SKGObjectModelBase.
0008 *
0009 * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgobjectmodelbase.h"
0012 
0013 #include <kcolorscheme.h>
0014 #include <kformat.h>
0015 #include <klocalizedstring.h>
0016 
0017 #include <qapplication.h>
0018 #include <qcolor.h>
0019 #include <qdir.h>
0020 #include <qicon.h>
0021 #include <qmimedata.h>
0022 
0023 #include "skgdocument.h"
0024 #include "skgmainpanel.h"
0025 #include "skgnodeobject.h"
0026 #include "skgpropertyobject.h"
0027 #include "skgtraces.h"
0028 #include "skgtransactionmng.h"
0029 
0030 SKGObjectModelBase::SKGObjectModelBase(SKGDocument* iDocument,
0031                                        const QString& iTable,
0032                                        QString  iWhereClause,
0033                                        QWidget* iParent,
0034                                        QString  iParentAttribute,
0035                                        bool iResetOnCreation)
0036     : QAbstractItemModel(iParent), m_isResetRealyNeeded(iResetOnCreation), m_cache(new QMap<QString, QVariant>()), m_document(iDocument),
0037       m_whereClause(std::move(iWhereClause)),
0038       m_parentAttribute(std::move(iParentAttribute)),
0039       m_doctransactionTable(false), m_nodeTable(false), m_parametersTable(false),
0040       m_refreshBlocked(false)
0041 {
0042     SKGTRACEINFUNC(1)
0043 
0044     setTable(iTable);
0045 
0046     connect(m_document, &SKGDocument::tableModified, this, &SKGObjectModelBase::dataModified);
0047     if (SKGMainPanel::getMainPanel() != nullptr) {
0048         connect(SKGMainPanel::getMainPanel(), &SKGMainPanel::currentPageChanged, this, &SKGObjectModelBase::pageChanged, Qt::QueuedConnection);
0049     }
0050 }
0051 
0052 SKGObjectModelBase::~SKGObjectModelBase()
0053 {
0054     SKGTRACEINFUNC(1)
0055     clear();
0056     m_document = nullptr;
0057     delete m_cache;
0058     m_cache = nullptr;
0059 }
0060 
0061 void SKGObjectModelBase::clear()
0062 {
0063     SKGTRACEINFUNC(1)
0064     QHashIterator<int, SKGObjectBase*> i(m_objectsHashTable);
0065     while (i.hasNext()) {
0066         i.next();
0067         SKGObjectBase* val = i.value();
0068         delete val;
0069         val = nullptr;
0070     }
0071 
0072     m_listObjects.clear();
0073     m_parentChildRelations.clear();
0074     m_childParentRelations.clear();
0075     m_objectsHashTable.clear();
0076     m_objectsHashTableRows.clear();
0077 }
0078 
0079 SKGDocument::SKGModelTemplateList SKGObjectModelBase::getSchemas() const
0080 {
0081     return m_listSchema;
0082 }
0083 
0084 void SKGObjectModelBase::setSupportedAttributes(const QStringList& iListAttribute)
0085 {
0086     SKGTRACEINFUNC(1)
0087     m_listSupported.clear();
0088     m_listVisibility.clear();
0089     m_listSize.clear();
0090 
0091     QStringList l = iListAttribute;
0092     if (!m_listSchema.isEmpty()) {
0093         l += SKGServices::splitCSVLine(m_listSchema.at(0).schema);
0094     }
0095 
0096     QStringList attributesAvailablesTmp;
0097     if (!m_listSchema.isEmpty()) {
0098         attributesAvailablesTmp = SKGServices::splitCSVLine(m_listSchema.at(0).schema);
0099     }
0100     int nb = attributesAvailablesTmp.count();
0101     QStringList attributesAvailables;
0102     attributesAvailables.reserve(nb);
0103     for (int i = 0; i < nb; ++i) {
0104         attributesAvailables.push_back(attributesAvailablesTmp.at(i).split('|').at(0));
0105     }
0106 
0107     nb = l.count();
0108     for (int i = 0; i < nb; ++i) {
0109         QStringList values = l.at(i).split('|');
0110         int nbValues = values.count();
0111         const QString& att = values.at(0);
0112 
0113         if (nbValues > 0 && !m_listSupported.contains(att) && attributesAvailables.contains(att)) {
0114             m_listSupported.push_back(att);
0115             bool visible = true;
0116             if (nbValues > 1) {
0117                 visible = (i == 0 || values.at(1) == QStringLiteral("Y"));    // First column is always visible to support grouping
0118             }
0119             m_listVisibility.push_back(visible);
0120             if (nbValues > 2) {
0121                 m_listSize.push_back(SKGServices::stringToInt(values.at(2)));
0122             } else {
0123                 m_listSize.push_back(-1);
0124             }
0125         }
0126     }
0127 
0128     m_isResetRealyNeeded = true;
0129 }
0130 
0131 bool SKGObjectModelBase::setFilter(const QString& iWhereClause)
0132 {
0133     if (iWhereClause != m_whereClause) {
0134         m_isResetRealyNeeded = true;
0135     }
0136     m_whereClause = iWhereClause;
0137     return m_isResetRealyNeeded;
0138 }
0139 
0140 void SKGObjectModelBase::setTable(const QString& iTable)
0141 {
0142     if (iTable != m_table) {
0143         if (!m_table.isEmpty()) {
0144             m_isResetRealyNeeded = true;
0145         }
0146         m_table = iTable;
0147         m_realTable = SKGServices::getRealTable(m_table);
0148         if (m_document != nullptr) {
0149             m_listSchema = m_document->getDisplaySchemas(m_realTable);
0150         }
0151     }
0152 }
0153 
0154 QString SKGObjectModelBase::getTable() const
0155 {
0156     return m_table;
0157 }
0158 
0159 void SKGObjectModelBase::setGroupBy(const QString& iAttribute)
0160 {
0161     if (iAttribute != m_groupby) {
0162         m_isResetRealyNeeded = true;
0163         m_groupby = iAttribute;
0164     }
0165 }
0166 
0167 QString SKGObjectModelBase::getGroupBy() const
0168 {
0169     return m_groupby;
0170 }
0171 
0172 QString SKGObjectModelBase::getParentChildAttribute() const
0173 {
0174     return m_parentAttribute;
0175 }
0176 
0177 QString SKGObjectModelBase::getRealTable() const
0178 {
0179     return m_realTable;
0180 }
0181 
0182 QString SKGObjectModelBase::getWhereClause() const
0183 {
0184     return m_whereClause;
0185 }
0186 
0187 SKGDocument* SKGObjectModelBase::getDocument() const
0188 {
0189     return m_document;
0190 }
0191 
0192 void SKGObjectModelBase::buidCache()
0193 {
0194     SKGTRACEINFUNC(1)
0195     m_doctransactionTable = (getRealTable() == QStringLiteral("doctransaction"));
0196     m_nodeTable = (getRealTable() == QStringLiteral("node"));
0197     m_parametersTable = (getRealTable() == QStringLiteral("parameters"));
0198 
0199     // Get std colors
0200     KColorScheme scheme(QPalette::Normal);
0201     m_fontNegativeColor = QVariant::fromValue(scheme.foreground(KColorScheme::NegativeText).color());
0202 }
0203 
0204 bool SKGObjectModelBase::blockRefresh(bool iBlocked)
0205 {
0206     bool previous = m_refreshBlocked;
0207     m_refreshBlocked = iBlocked;
0208     return previous;
0209 }
0210 
0211 bool SKGObjectModelBase::isRefreshBlocked()
0212 {
0213     return m_refreshBlocked;
0214 }
0215 
0216 void SKGObjectModelBase::refresh()
0217 {
0218     if (!m_isResetRealyNeeded || isRefreshBlocked()) {
0219         return;
0220     }
0221     SKGTRACEIN(1, "SKGObjectModelBase::refresh-" % getTable())
0222     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0223     beginResetModel();
0224     Q_EMIT beforeReset();
0225     m_cache->clear();
0226     {
0227         _SKGTRACEINFUNC(10)
0228         clear();
0229         m_listAttibutes.clear();
0230         m_listAttributeTypes.clear();
0231         /*beginRemoveRows(QModelIndex(), 0, rowCount(QModelIndex())-1);
0232         endRemoveRows();*/
0233     }
0234 
0235     {
0236         _SKGTRACEINFUNC(10)
0237         QStringList listAttibutesTmp;
0238         if (m_document != nullptr && m_document->getAttributesList(m_table, listAttibutesTmp).isSucceeded()) {
0239             m_isResetRealyNeeded = false;
0240             if (!listAttibutesTmp.isEmpty()) {
0241                 // Filter attributes
0242                 int nb = m_listSupported.count();
0243                 if (nb == 0) {
0244                     setSupportedAttributes(QStringList());
0245                     nb = m_listSupported.count();
0246                 }
0247                 for (int i = 0 ; i < nb ; ++i) {
0248                     QString att = m_listSupported.at(i);
0249                     if (listAttibutesTmp.contains(att) || att.startsWith(QLatin1String("p_"))) {
0250                         m_listAttibutes.push_back(att);
0251                         if (att.startsWith(QLatin1String("t_")) || att.startsWith(QLatin1String("p_")) ||
0252                             att == QLatin1String("d_DATEWEEK") || att == QLatin1String("d_DATEMONTH") || att == QLatin1String("d_DATEQUARTER") || att == QLatin1String("d_DATESEMESTER") || att == QLatin1String("d_DATEYEAR")) {
0253                             m_listAttributeTypes.push_back(SKGServices::TEXT);
0254                         } else if (att.startsWith(QLatin1String("f_"))) {
0255                             m_listAttributeTypes.push_back(SKGServices::FLOAT);
0256                         } else if (att.startsWith(QLatin1String("i_"))) {
0257                             m_listAttributeTypes.push_back(SKGServices::INTEGER);
0258                         } else if (att.startsWith(QLatin1String("d_"))) {
0259                             m_listAttributeTypes.push_back(SKGServices::DATE);
0260                         } else {
0261                             m_listAttributeTypes.push_back(SKGServices::OTHER);
0262                         }
0263                     }
0264                 }
0265 
0266                 // Remove double
0267                 nb = m_listAttibutes.count();
0268                 for (int i = nb - 1 ; i >= 0 ; --i) {
0269                     QString att = m_listAttibutes.at(i);
0270                     if (att.contains(QStringLiteral("_REAL"))) {
0271                         att.replace(QStringLiteral("_REAL"), QStringLiteral("_"));
0272                         int p = m_listAttibutes.indexOf(att);
0273                         if (p == -1) {
0274                             att = att.toLower();
0275                             p = m_listAttibutes.indexOf(att);
0276                         }
0277                         if (p != -1) {
0278                             m_listAttibutes.removeAt(p);
0279                             m_listAttributeTypes.removeAt(p);
0280                             if (p < i) {
0281                                 --i;
0282                             }
0283                         }
0284                     }
0285                 }
0286             }
0287 
0288             // Get objects
0289             QString wc = m_whereClause;
0290             if (!m_groupby.isEmpty()) {
0291                 if (wc.isEmpty()) {
0292                     wc = QStringLiteral("1=1");
0293                 }
0294                 if (m_groupby.startsWith(QLatin1String("p_"))) {
0295                     wc += " ORDER BY (SELECT t_value FROM parameters WHERE t_uuid_parent=" % getTable() % ".id||'-" % getRealTable() % "' AND t_name='" % m_groupby % "')";
0296                 } else {
0297                     wc += " ORDER BY " % m_groupby;
0298                 }
0299             }
0300             if (m_document != nullptr) {
0301                 m_document->getObjects(m_table, wc, m_listObjects);
0302             }
0303 
0304             // Initialize object to treat
0305             QString currentGroup;
0306             int currentGoupId = 0;
0307             bool groupByDate = (m_groupby.startsWith(QLatin1String("d_")) &&
0308                                 m_groupby != QLatin1String("d_DATEWEEK") &&
0309                                 m_groupby != QLatin1String("d_DATEMONTH") &&
0310                                 m_groupby != QLatin1String("d_DATEQUARTER") &&
0311                                 m_groupby != QLatin1String("d_DATESEMESTER") &&
0312                                 m_groupby != QLatin1String("d_DATEYEAR"));
0313 
0314             bool groupByNum = (m_groupby.startsWith(QLatin1String("f_")) || m_groupby.startsWith(QLatin1String("i_")));
0315             int nb = m_listObjects.count();
0316             SKGTRACEL(1) << nb << " objects found" << SKGENDL;
0317             for (int t = 0; t < nb; ++t) {
0318                 auto c = new SKGObjectBase(m_listObjects.at(t));
0319                 int id = (c != nullptr ? c->getID() : 0);
0320 
0321                 int idparent = 0;
0322                 if (m_groupby.isEmpty()) {
0323                     // Grouping by tree
0324                     if (!m_parentAttribute.isEmpty()) {
0325                         int idp = SKGServices::stringToInt(c->getAttribute(m_parentAttribute));
0326                         if (idp > 0) {
0327                             idparent = idp;
0328                         }
0329                     }
0330                 } else {
0331                     // Grouping on specified attribute
0332                     QString att = getAttributeForGrouping(*c, m_groupby);
0333                     if (groupByDate) {
0334                         QDate date = SKGServices::stringToTime(att).date();
0335                         if (date == QDate::currentDate()) {
0336                             att = i18nc("A group name for grouping by date attibute", "Today");
0337                         } else if (date > QDate::currentDate()) {
0338                             // In future
0339                             if (date < QDate::currentDate().addDays(7)) {
0340                                 att = i18nc("A group name for grouping by date attibute", "Next 7 days");
0341                             } else if (date < QDate::currentDate().addDays(15)) {
0342                                 att = i18nc("A group name for grouping by date attibute", "Next 15 days");
0343                             } else if (date < QDate::currentDate().addMonths(1)) {
0344                                 att = i18nc("A group name for grouping by date attibute", "Next month");
0345                             } else if (date < QDate::currentDate().addMonths(3)) {
0346                                 att = i18nc("A group name for grouping by date attibute", "Next 3 months");
0347                             } else if (date < QDate::currentDate().addMonths(6)) {
0348                                 att = i18nc("A group name for grouping by date attibute", "Next 6 months");
0349                             } else if (date < QDate::currentDate().addYears(1)) {
0350                                 att = i18nc("A group name for grouping by date attibute", "Next year");
0351                             } else if (date < QDate::currentDate().addYears(3)) {
0352                                 att = i18nc("A group name for grouping by date attibute", "Next 3 years");
0353                             } else {
0354                                 att = i18nc("A group name for grouping by date attibute", "Far away in the future");
0355                             }
0356                         } else {
0357                             // In the past
0358                             if (date > QDate::currentDate().addDays(-7)) {
0359                                 att = i18nc("A group name for grouping by date attibute", "Last 7 days");
0360                             } else if (date > QDate::currentDate().addDays(-15)) {
0361                                 att = i18nc("A group name for grouping by date attibute", "Last 15 days");
0362                             } else if (date > QDate::currentDate().addMonths(-1)) {
0363                                 att = i18nc("A group name for grouping by date attibute", "Last month");
0364                             } else if (date > QDate::currentDate().addMonths(-3)) {
0365                                 att = i18nc("A group name for grouping by date attibute", "Last 3 months");
0366                             } else if (date > QDate::currentDate().addMonths(-6)) {
0367                                 att = i18nc("A group name for grouping by date attibute", "Last 6 months");
0368                             } else if (date > QDate::currentDate().addYears(-1)) {
0369                                 att = i18nc("A group name for grouping by date attibute", "Last year");
0370                             } else if (date > QDate::currentDate().addYears(-3)) {
0371                                 att = i18nc("A group name for grouping by date attibute", "Last 3 years");
0372                             } else {
0373                                 att = i18nc("A group name for grouping by date attibute", "Far away in the past");
0374                             }
0375                         }
0376                     } else if (groupByNum) {
0377                         if (att != QChar(8734)) {
0378                             double d = SKGServices::stringToDouble(att);
0379                             if (d > 10000.0) {
0380                                 att = i18nc("A group name for grouping by numerical attibute", "> 10000");
0381                             } else if (d > 1000.0) {
0382                                 att = i18nc("A group name for grouping by numerical attibute", "> 1000");
0383                             } else if (d > 100.0) {
0384                                 att = i18nc("A group name for grouping by numerical attibute", "> 100");
0385                             } else if (d > 10.0) {
0386                                 att = i18nc("A group name for grouping by numerical attibute", "> 10");
0387                             } else if (d > 0.0) {
0388                                 att = i18nc("A group name for grouping by numerical attibute", "> 0");
0389                             } else if (d < -10000.0) {
0390                                 att = i18nc("A group name for grouping by numerical attibute", "< -10000");
0391                             } else if (d < -1000.0) {
0392                                 att = i18nc("A group name for grouping by numerical attibute", "< -1000");
0393                             } else if (d < -100.0) {
0394                                 att = i18nc("A group name for grouping by numerical attibute", "< -100");
0395                             } else if (d < -10.0) {
0396                                 att = i18nc("A group name for grouping by numerical attibute", "< -10");
0397                             } else if (d < 0.0) {
0398                                 att = i18nc("A group name for grouping by numerical attibute", "< 0");
0399                             } else {
0400                                 att = i18nc("A group name for grouping by numerical attibute", "= 0");
0401                             }
0402                         }
0403                     }
0404                     if (att != currentGroup || currentGoupId == 0) {
0405                         // Addition of a new group
0406                         currentGroup = att;
0407                         currentGoupId--;
0408                         auto group = new SKGObjectBase(m_document, QLatin1String(""), currentGoupId);
0409                         group->setAttribute(QStringLiteral("t_title"), currentGroup);
0410 
0411                         m_childParentRelations.insert(currentGoupId, 0);
0412 
0413                         QList<int> childrensids = m_parentChildRelations.value(0);
0414                         childrensids.push_back(currentGoupId);
0415 
0416                         m_parentChildRelations.insert(0, childrensids);
0417                         m_objectsHashTableRows.insert(currentGoupId, childrensids.count() - 1);
0418                         m_objectsHashTable.insert(currentGoupId, group);
0419                     }
0420 
0421                     idparent = currentGoupId;
0422                 }
0423 
0424                 m_childParentRelations.insert(id, idparent);
0425 
0426                 QList<int> childrensids = m_parentChildRelations.value(idparent);
0427                 childrensids.push_back(id);
0428 
0429                 m_parentChildRelations.insert(idparent, childrensids);
0430                 m_objectsHashTableRows.insert(id, childrensids.count() - 1);
0431                 m_objectsHashTable.insert(id, c);
0432             }
0433         }
0434 
0435         // Build cache
0436         buidCache();
0437     }
0438     endResetModel();
0439     Q_EMIT afterReset();
0440 
0441     QApplication::restoreOverrideCursor();
0442 }
0443 
0444 bool SKGObjectModelBase::hasChildren(const QModelIndex& iParent) const
0445 {
0446     if (iParent.column() > 0) {
0447         return false;
0448     }
0449     _SKGTRACEINFUNC(10)
0450 
0451     if (iParent.isValid() && m_parentAttribute.isEmpty() && m_groupby.isEmpty()) {
0452         return false;
0453     }
0454     return QAbstractItemModel::hasChildren(iParent);
0455 }
0456 
0457 int SKGObjectModelBase::rowCount(const QModelIndex& iParent) const
0458 {
0459     if (iParent.column() > 0) {
0460         return 0;
0461     }
0462     _SKGTRACEINFUNC(10)
0463 
0464     int idParent = 0;
0465     if (iParent.isValid()) {
0466         idParent = iParent.internalId();
0467     }
0468 
0469     return m_parentChildRelations.value(idParent).count();
0470 }
0471 
0472 int SKGObjectModelBase::columnCount(const QModelIndex& iParent) const
0473 {
0474     Q_UNUSED(iParent)
0475     return m_listAttibutes.count();
0476 }
0477 
0478 QModelIndex SKGObjectModelBase::index(int row, int column, const QModelIndex& iParent) const
0479 {
0480     if (!hasIndex(row, column, iParent)) {
0481         return {};
0482     }
0483     _SKGTRACEINFUNC(10)
0484 
0485     int idParent = 0;
0486     if (iParent.isValid()) {
0487         idParent = iParent.internalId();
0488     }
0489 
0490     int idChild = m_parentChildRelations.value(idParent).at(row);
0491 
0492     // SKGTRACE << table << "-" << idParent << "(" << row << ") ==> " << idChild << SKGENDL;
0493     return (idChild != 0 ? createIndex(row, column, idChild) : QModelIndex());
0494 }
0495 
0496 QModelIndex SKGObjectModelBase::parent(const QModelIndex& iIndex) const
0497 {
0498     if (!iIndex.isValid()) {
0499         return {};
0500     }
0501     _SKGTRACEINFUNC(10)
0502 
0503     int idChild = 0;
0504     if (iIndex.isValid()) {
0505         idChild = iIndex.internalId();
0506     }
0507 
0508     int idParent = m_childParentRelations.value(idChild);
0509     int row = m_objectsHashTableRows.value(idParent);
0510     // SKGTRACE << table << "-" << idChild << "(" << row << ") <== " << idParent << SKGENDL;
0511     return idParent != 0 ? createIndex(row, 0, idParent) : QModelIndex();
0512 }
0513 
0514 int SKGObjectModelBase::getIndexAttribute(const QString& iAttributeName) const
0515 {
0516     int output = m_listAttibutes.indexOf(iAttributeName);
0517     if (output == -1) {
0518         SKGTRACE << "[" << iAttributeName << "] not found in [" << getRealTable() << "]" << SKGENDL;
0519     }
0520     return output;
0521 }
0522 
0523 QString SKGObjectModelBase::getAttribute(int iIndex) const
0524 {
0525     return m_listAttibutes.value(iIndex);
0526 }
0527 
0528 SKGServices::AttributeType SKGObjectModelBase::getAttributeType(int iIndex) const
0529 {
0530     return m_listAttributeTypes.value(iIndex);
0531 }
0532 
0533 QVariant SKGObjectModelBase::headerData(int iSection, Qt::Orientation iOrientation, int iRole) const
0534 {
0535     _SKGTRACEINFUNC(10)
0536 
0537     if (iOrientation == Qt::Horizontal) {
0538         if (iRole == Qt::DisplayRole) {
0539             QString att;
0540             if (iSection >= 0 && iSection < m_listAttibutes.count()) {
0541                 att = m_listAttibutes.at(iSection);
0542             } else {
0543                 att = SKGServices::intToString(iSection);
0544             }
0545 
0546             return getDocument()->getDisplay(getTable() % '.' % att).remove(getTable() % '.');
0547         }
0548         if (iRole == Qt::UserRole) {
0549             QString att;
0550             if (iSection >= 0 && iSection < m_listAttibutes.count()) {
0551                 att = m_listAttibutes.at(iSection);
0552             } else {
0553                 att = SKGServices::intToString(iSection);
0554             }
0555 
0556             int indexAtt = m_listSupported.indexOf(att);
0557 
0558             att = getDocument()->getDisplay(getTable() % '.' % att).remove(getTable() % '.');
0559 
0560 
0561             if (indexAtt >= 0 && indexAtt < m_listVisibility.count()) {
0562                 bool visible = m_listVisibility.at(indexAtt);
0563                 att += QStringLiteral("|") % (visible ? QStringLiteral("Y") : QStringLiteral("N"));
0564                 if (indexAtt >= 0 && indexAtt < m_listSize.count()) {
0565                     att += '|' % SKGServices::intToString(m_listSize.at(indexAtt));
0566                 }
0567             }
0568             return att;
0569         }
0570         if (iRole == Qt::DecorationRole) {
0571             QString att;
0572             if (iSection >= 0 && iSection < m_listAttibutes.count()) {
0573                 att = m_listAttibutes.at(iSection);
0574             } else {
0575                 att = SKGServices::intToString(iSection);
0576             }
0577 
0578             return getDocument()->getIcon(getTable() % '.' % att);
0579         }
0580     }
0581     return QVariant();
0582 }
0583 
0584 SKGObjectBase SKGObjectModelBase::getObject(const QModelIndex& iIndex) const
0585 {
0586     SKGObjectBase* obj = getObjectPointer(iIndex);
0587     SKGObjectBase output;
0588     if (obj != nullptr) {
0589         output = *obj;
0590     }
0591     return output;
0592 }
0593 
0594 SKGObjectBase* SKGObjectModelBase::getObjectPointer(const QModelIndex& iIndex) const
0595 {
0596     _SKGTRACEINFUNC(10)
0597     return m_objectsHashTable.value(iIndex.internalId());
0598 }
0599 
0600 QVariant SKGObjectModelBase::data(const QModelIndex& iIndex, int iRole) const
0601 {
0602     if (!iIndex.isValid()) {
0603         return QVariant();
0604     }
0605     _SKGTRACEINFUNC(10)
0606     // Build cache id
0607     QString idcache = getObjectPointer(iIndex)->getUniqueID() %
0608                       "-" % SKGServices::intToString(iIndex.row()) %
0609                       "-" % SKGServices::intToString(iIndex.column()) %
0610                       "-" % SKGServices::intToString(iRole);
0611 
0612     // Check cache
0613     if (!m_cache->contains(idcache)) {
0614         // Compute value
0615         m_cache->insert(idcache, computeData(iIndex, iRole));
0616     }
0617 
0618     return m_cache->value(idcache);
0619 }
0620 
0621 QVariant SKGObjectModelBase::computeData(const QModelIndex& iIndex, int iRole) const
0622 {
0623     if (!iIndex.isValid()) {
0624         return QVariant();
0625     }
0626     _SKGTRACEINFUNC(10)
0627 
0628     switch (iRole) {
0629     case Qt::BackgroundRole: {
0630         SKGObjectBase* obj = getObjectPointer(iIndex);
0631         if (obj->getTable().isEmpty()) {
0632             // This is a group
0633             return QApplication::palette().brush(QPalette::Button);
0634         }
0635         break;
0636     }
0637     case Qt::DisplayRole:
0638     case Qt::EditRole:
0639     case Qt::UserRole: {
0640         SKGObjectBase* obj = getObjectPointer(iIndex);
0641         QString att = m_listAttibutes.at(iIndex.column());
0642         if (obj->getTable().isEmpty()) {
0643             // This is a group
0644             if (iIndex.column() == 0) {
0645                 if (iRole == Qt::UserRole) {
0646                     return -obj->getID();   // For sorting
0647                 }
0648                 int nb = rowCount(iIndex);
0649 
0650                 // Is it possible to compute sums of f_CURRENTAMOUNT ?
0651                 int posAmount = m_listAttibutes.indexOf(QStringLiteral("f_CURRENTAMOUNT"));
0652                 if (posAmount == -1) {
0653                     posAmount = m_listAttibutes.indexOf(QStringLiteral("f_REALCURRENTAMOUNT"));
0654                 }
0655                 if (posAmount != -1 && nb > 0) {
0656                     // Compute sums
0657                     double sum = 0.0;
0658                     double average = 0.0;
0659                     double min = std::numeric_limits<double>::max();
0660                     double max = -std::numeric_limits<double>::max();
0661                     for (int i = 0; i < nb ; ++i) {
0662                         double amount = data(SKGObjectModelBase::index(i, posAmount, iIndex), Qt::UserRole).toDouble();
0663                         sum += amount;
0664                         max = qMax(max, amount);
0665                         min = qMin(min, amount);
0666                     }
0667                     average = sum / nb;
0668 
0669                     return i18nc("How to display a grouping title. Here \"title (count) Sum= [min , average , max]\"", "%1: %2 (%3) Sum=%4 [%5 , %6 , %7]",
0670                                  getDocument()->getDisplay(m_groupby),
0671                                  obj->getAttribute(QStringLiteral("t_title")),
0672                                  nb,
0673                                  formatMoney(sum),
0674                                  formatMoney(min),
0675                                  formatMoney(average),
0676                                  formatMoney(max));
0677                 }
0678                 return i18nc("How to display a grouping title. Here \"title (count)\"", "%1: %2 (%3)",
0679                              getDocument()->getDisplay(m_groupby),
0680                              obj->getAttribute(QStringLiteral("t_title")),
0681                              nb);
0682             }
0683             return "";
0684         }
0685         QString val;
0686         if (att.startsWith(QLatin1String("p_"))) {
0687             // This is a property
0688             val = obj->getProperty(att.right(att.count() - 2));
0689         } else {
0690             // This is a real attribute
0691             val = obj->getAttribute(att);
0692 
0693             switch (getAttributeType(iIndex.column())) {
0694             case SKGServices::FLOAT: {
0695                 double dval = SKGServices::stringToDouble(val);
0696                 return dval;
0697             }
0698             case SKGServices::INTEGER: {
0699                 return SKGServices::stringToInt(val);
0700             }
0701             case SKGServices::DATE: {
0702                 QDate dval = SKGServices::stringToTime(val).date();
0703                 if (iRole == Qt::DisplayRole) {
0704                     return SKGMainPanel::dateToString(dval);
0705                 }
0706                 if (iRole == Qt::UserRole) {
0707                     return dval;
0708                 }
0709             }
0710             default: {
0711             }
0712             }
0713 
0714             if (m_doctransactionTable && att == QStringLiteral("t_savestep")) {
0715                 return "";
0716             }
0717         }
0718 
0719         // return val+"("+SKGServices::intToString(rowCount(index))+")";
0720         return val;
0721     }
0722 
0723     case Qt::FontRole: {
0724         SKGObjectBase* obj = getObjectPointer(iIndex);
0725         // Text color
0726         if (obj->getTable().isEmpty()) {
0727             // This is a group
0728             if (iIndex.column() == 0) {
0729                 QFont f;
0730                 f.setBold(true);
0731 
0732                 return QVariant::fromValue(f);
0733             }
0734             return "";
0735         }
0736 
0737         int propertyId = data(iIndex, 101).toInt();
0738         if (propertyId != 0) {
0739             SKGPropertyObject p(m_document, propertyId);
0740             if (!p.getUrl().scheme().isEmpty()) {
0741                 QFont f;
0742                 f.setUnderline(true);
0743 
0744                 return QVariant::fromValue(f);
0745             }
0746         }
0747         break;
0748     }
0749     case Qt::TextColorRole: {
0750         // Text color
0751         if (getAttributeType(iIndex.column()) == SKGServices::FLOAT) {
0752             QVariant value_displayed = SKGObjectModelBase::data(iIndex, Qt::UserRole);
0753             bool ok = false;
0754             double value_double = value_displayed.toDouble(&ok);
0755             if (ok && value_double < 0) {
0756                 return m_fontNegativeColor;
0757             }
0758         }
0759 
0760         int propertyId = data(iIndex, 101).toInt();
0761         if (propertyId != 0) {
0762             SKGPropertyObject p(m_document, propertyId);
0763             if (!p.getUrl().scheme().isEmpty()) {
0764                 KColorScheme scheme(QPalette::Normal);
0765                 return QVariant::fromValue(scheme.foreground(KColorScheme::ActiveText).color());
0766             }
0767         }
0768         break;
0769     }
0770     case Qt::TextAlignmentRole: {
0771         // Text alignment
0772         SKGServices::AttributeType attType = getAttributeType(iIndex.column());
0773         return static_cast<int>(Qt::AlignVCenter | (attType == SKGServices::FLOAT || attType == SKGServices::INTEGER ? Qt::AlignRight : Qt::AlignLeft));
0774     }
0775     case Qt::DecorationRole: {
0776         // Decoration
0777         SKGObjectBase* obj = getObjectPointer(iIndex);
0778         QString att = m_listAttibutes.at(iIndex.column());
0779         if (obj->getTable().isEmpty()) {
0780             // This is a group
0781             if (iIndex.column() == 0) {
0782                 return QVariant::fromValue(SKGServices::fromTheme(QStringLiteral("arrow-right")));
0783             };
0784             return "";
0785         }
0786         if (iIndex.column() == 0 && m_nodeTable) {
0787             SKGNodeObject node(*obj);
0788             return QVariant::fromValue(node.getIcon());
0789         }
0790         if (iIndex.column() == 0 && m_doctransactionTable) {
0791             return QVariant::fromValue(SKGServices::fromTheme(obj->getAttribute(QStringLiteral("t_mode")) == QStringLiteral("U") ? QStringLiteral("edit-undo") : QStringLiteral("edit-redo")));
0792         }
0793         if (m_doctransactionTable && att == QStringLiteral("t_savestep")) {
0794             if (obj->getAttribute(QStringLiteral("t_savestep")) == QStringLiteral("Y")) {
0795                 return QVariant::fromValue(SKGServices::fromTheme(QStringLiteral("document-save")));
0796             }
0797         }
0798 
0799         break;
0800     }
0801     case Qt::ToolTipRole: {
0802         // Tooltip
0803         QString toolTipString;
0804         SKGObjectBase* obj = getObjectPointer(iIndex);
0805 
0806         if (obj != nullptr && m_document != nullptr) {
0807             if (m_doctransactionTable) {
0808                 SKGDocument::SKGMessageList msg;
0809                 m_document->getMessages(obj->getID(), msg);
0810                 int nbMessages = msg.count();
0811                 if (nbMessages > 0) {
0812                     for (int i = 0; i < nbMessages; ++i) {
0813                         if (i != 0) {
0814                             toolTipString += '\n';
0815                         }
0816                         toolTipString += msg.at(i).Text;
0817                     }
0818                 }
0819             } else if (getAttributeType(iIndex.column()) == SKGServices::DATE) {
0820                 QString att = m_listAttibutes.at(iIndex.column());
0821                 QString val = obj->getAttribute(att);
0822 
0823                 QDate dval = SKGServices::stringToTime(val).date();
0824                 QString fancyDate = QLocale().toString(dval, QLocale::LongFormat);
0825                 QString shortDate = QLocale().toString(dval, QLocale::ShortFormat);
0826 
0827                 if (shortDate != fancyDate) {
0828                     toolTipString = shortDate;
0829                 }
0830             }
0831 
0832             // Add properties
0833             QStringList props = obj->getProperties();
0834             if (!props.isEmpty() && !toolTipString.isEmpty()) {
0835                 toolTipString += '\n';
0836             }
0837             for (const auto& prop : qAsConst(props)) {
0838                 if (!toolTipString.isEmpty()) {
0839                     toolTipString += '\n';
0840                 }
0841                 toolTipString += i18nc("To display a property and its value", "%1=%2", prop, obj->getProperty(prop));
0842             }
0843 
0844             // Add UUID
0845             IFSKGTRACEL(1) {
0846                 toolTipString += '\n' % obj->getUniqueID();
0847             }
0848         }
0849         return toolTipString;
0850     }
0851     case 99: {
0852         SKGObjectBase* obj = getObjectPointer(iIndex);
0853         if (obj != nullptr) {
0854             return SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_sortorder")));
0855         }
0856         break;
0857     }
0858     case 101: {
0859         SKGObjectBase* obj = getObjectPointer(iIndex);
0860         // Is it a URL ?
0861         QString att = m_listAttibutes.at(iIndex.column());
0862         if (att.startsWith(QLatin1String("p_"))) {
0863             SKGPropertyObject p(obj->getPropertyObject(att.right(att.count() - 2)));
0864             if (!p.getUrl().scheme().isEmpty()) {
0865                 return QVariant::fromValue(p.getID());
0866             }
0867         } else if (m_parametersTable && att == QStringLiteral("t_value")) {
0868             SKGPropertyObject p(*obj);
0869             if (!p.getUrl().scheme().isEmpty()) {
0870                 return QVariant::fromValue(p.getID());
0871             }
0872         }
0873         return 0;
0874     }
0875     default : {
0876     }
0877     }
0878 
0879     return QVariant();
0880 }
0881 
0882 Qt::ItemFlags SKGObjectModelBase::flags(const QModelIndex& iIndex) const
0883 {
0884     _SKGTRACEINFUNC(10)
0885 
0886     Qt::ItemFlags f = QAbstractItemModel::flags(iIndex) | Qt::ItemIsDropEnabled;
0887 
0888     if (iIndex.isValid()) {
0889         f |= Qt::ItemIsUserCheckable;
0890     }
0891 
0892     if (m_nodeTable && iIndex.isValid()) {
0893         f |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
0894     }
0895 
0896     if (iIndex.isValid()) {
0897         QString att = m_listAttibutes.at(iIndex.column());
0898         if (att.toLower() == att || !getDocument()->getRealAttribute(att).isEmpty()) {
0899             f |= Qt::ItemIsEditable;
0900         }
0901         SKGObjectBase* obj = getObjectPointer(iIndex);
0902         if (obj->getTable().isEmpty()) {
0903             f = Qt::ItemIsEnabled;
0904         }
0905     }
0906 
0907     return f;
0908 }
0909 
0910 bool SKGObjectModelBase::setData(const QModelIndex& iIndex, const QVariant& iValue, int iRole)
0911 {
0912     if (!iIndex.isValid()) {
0913         return false;
0914     }
0915 
0916     if (iRole == Qt::EditRole) {
0917         SKGError err;
0918         if (m_nodeTable) {
0919             SKGNodeObject obj(getObject(iIndex));
0920             QString name = iValue.toString();
0921             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Bookmark update '%1'", name), err)
0922             IFOKDO(err, err = obj.setName(name))
0923             IFOKDO(err, obj.save())
0924         } else {
0925             SKGObjectBase obj(getObject(iIndex));
0926 
0927             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Update object"), err)
0928             SKGObjectBase obj2(obj.getDocument(), obj.getRealTable(), obj.getID());  // To be sure this is not a complex object
0929             QString att = m_listAttibutes.at(iIndex.column());
0930             IFOKDO(err, obj2.setAttribute(att, att.startsWith(QLatin1String("d_")) && iValue.canConvert<QDateTime>() ? SKGServices::dateToSqlString(iValue.toDateTime()) : iValue.toString()))
0931             IFOKDO(err, obj2.save())
0932         }
0933 
0934         SKGMainPanel::displayErrorMessage(err);
0935         return !err;
0936     }
0937     return QAbstractItemModel::setData(iIndex, iValue, iRole);
0938 }
0939 
0940 Qt::DropActions SKGObjectModelBase::supportedDragActions() const
0941 {
0942     return (m_nodeTable ? Qt::MoveAction : Qt::IgnoreAction);
0943 }
0944 
0945 Qt::DropActions SKGObjectModelBase::supportedDropActions() const
0946 {
0947     return Qt::MoveAction | Qt::LinkAction | Qt::CopyAction;
0948 }
0949 
0950 QStringList SKGObjectModelBase::mimeTypes() const
0951 {
0952     QStringList types;
0953     types << "application/skg." % getRealTable() % ".ids";
0954     types << QStringLiteral("application/data");
0955     types << QStringLiteral("text/uri-list");
0956     return types;
0957 }
0958 
0959 QMimeData* SKGObjectModelBase::mimeData(const QModelIndexList& iIndexes) const
0960 {
0961     auto md = new QMimeData();
0962     QByteArray encodedData;
0963 
0964     QDataStream stream(&encodedData, QIODevice::WriteOnly);
0965 
0966     QString t = getTable();
0967     for (const auto& idx : qAsConst(iIndexes)) {
0968         if (idx.isValid() && idx.column() == 0) {
0969             SKGObjectBase obj = getObject(idx);
0970             t = obj.getRealTable();
0971             stream << t;
0972             stream << obj.getID();
0973         }
0974     }
0975 
0976     md->setData("application/skg." % t % ".ids", encodedData);
0977     return md;
0978 }
0979 
0980 bool SKGObjectModelBase::dropMimeData(const QMimeData* iData,
0981                                       Qt::DropAction iAction,
0982                                       int iRow, int iColumn,
0983                                       const QModelIndex& iParent)
0984 {
0985     Q_UNUSED(iRow)
0986     if (iAction == Qt::IgnoreAction) {
0987         return true;
0988     }
0989     if ((iData == nullptr) || !(iData->hasFormat(QStringLiteral("application/skg.node.ids")) || iData->hasUrls())) {
0990         return false;    // TODO(Stephane MANKOWSKI): accept all
0991     }
0992     if (iColumn > 0) {
0993         return false;
0994     }
0995 
0996     SKGError err;
0997     // Drop files
0998     if (iData->hasUrls() && iParent.isValid() && getRealTable() != QStringLiteral("node")) {
0999         QList<QUrl> urls = iData->urls();
1000         int nb = urls.count();
1001         {
1002             SKGObjectBase obj(getObject(iParent));
1003 
1004             SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Property creation"), err, nb)
1005             for (int i = 0; !err && i < nb; ++i) {
1006                 QString name = i18n("File");
1007                 int idx = 1;
1008                 while (!err && !obj.getProperty(name).isEmpty()) {
1009                     idx++;
1010                     name = i18n("File") % " (" % SKGServices::intToString(idx) % ')';
1011                 }
1012                 QString f = urls.at(i).toLocalFile();
1013                 err = obj.setProperty(name, f, iAction == Qt::LinkAction ? QLatin1String("") : f);
1014                 if (!err && iAction == Qt::MoveAction) {
1015                     QFile(f).remove();
1016                 }
1017                 IFOKDO(err, getDocument()->stepForward(i + 1))
1018             }
1019         }
1020     } else if (iData->hasFormat(QStringLiteral("application/skg.node.ids"))) {
1021         // Drop nodes
1022         QByteArray encodedData = iData->data(QStringLiteral("application/skg.node.ids"));
1023         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1024         QStringList newItems;
1025 
1026         QModelIndex parentIndex = iParent;
1027         SKGNodeObject parentNode;
1028         if (parentIndex.isValid()) {
1029             parentNode = getObject(parentIndex);
1030             if (!parentNode.isFolder()) {
1031                 // The parent is not a directory
1032                 parentNode.getParentNode(parentNode);
1033                 parentIndex = parentIndex.parent();
1034             }
1035         }
1036         {
1037             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Move bookmark"), err)
1038 
1039             double min = 0;
1040             double max = 0;
1041             if (iRow >= 1) {
1042                 QModelIndex previousIndex = SKGObjectModelBase::index(iRow - 1, 0, parentIndex);
1043                 SKGNodeObject previousObject(getObject(previousIndex));
1044                 min = previousObject.getOrder();
1045             }
1046 
1047             if (iRow >= rowCount(parentIndex)) {
1048                 max = min + 1;
1049             } else {
1050                 QModelIndex nextIndex = SKGObjectModelBase::index(iRow, 0, parentIndex);
1051                 SKGNodeObject nextObject(getObject(nextIndex));
1052                 max = nextObject.getOrder();
1053             }
1054             if (max <= min) {
1055                 max = min + 1;
1056             }
1057 
1058             while (!stream.atEnd() && !err) {
1059                 int o_id;
1060                 QString o_table;
1061                 stream >> o_table;
1062                 stream >> o_id;
1063 
1064                 // Set parent
1065                 SKGNodeObject child(getDocument(), o_id);
1066                 err = child.load();
1067                 QString oldName = child.getDisplayName();
1068                 IFOK(err) {
1069                     if (parentIndex.isValid()) {
1070                         err = child.setParentNode(parentNode);
1071                     } else {
1072                         err = child.removeParentNode();
1073                     }
1074                 }
1075 
1076                 // Set order
1077                 IFOKDO(err, child.setOrder((min + max) / 2.0))
1078 
1079                 // Save
1080                 IFOKDO(err, child.save())
1081 
1082                 // Send message
1083                 IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The bookmark '%1' has been moved to '%2'", oldName, child.getDisplayName()), SKGDocument::Hidden))
1084             }
1085         }
1086     }
1087     SKGMainPanel::displayErrorMessage(err);
1088     return !err;
1089 }
1090 
1091 void SKGObjectModelBase::pageChanged()
1092 {
1093     if (m_isResetRealyNeeded) {
1094         dataModified(QLatin1String(""), 0);
1095     }
1096 }
1097 
1098 void SKGObjectModelBase::dataModified(const QString& iTableName, int iIdTransaction)
1099 {
1100     if (getTable() == iTableName || iTableName.isEmpty()) {
1101         SKGTRACEINFUNC(1)
1102         SKGTRACEL(1) << "getTable=" << getRealTable() << SKGENDL;
1103         SKGTRACEL(1) << "Parameters=" << iTableName << " , " << iIdTransaction << SKGENDL;
1104         SKGTabPage* page = SKGTabPage::parentTabPage(qobject_cast< QWidget* >(this->QObject::parent()));
1105         SKGTabPage* cpage = SKGMainPanel::getMainPanel() != nullptr ? SKGMainPanel::getMainPanel()->currentPage() : nullptr;
1106         if (page != nullptr && page != cpage) {
1107             m_isResetRealyNeeded = true;
1108             return;
1109         }
1110 
1111         // Full refresh
1112         m_isResetRealyNeeded = true;
1113 
1114         // Refresh model
1115         refresh();
1116     }
1117 }
1118 
1119 QString SKGObjectModelBase::getAttributeForGrouping(const SKGObjectBase& iObject, const QString& iAttribute) const
1120 {
1121     if (iAttribute.startsWith(QLatin1String("p_"))) {
1122         // This is a property
1123         return iObject.getProperty(iAttribute.right(iAttribute.count() - 2));
1124     }
1125     // This is a real attribute
1126     return iObject.getAttribute(iAttribute);
1127 }
1128 
1129 QString SKGObjectModelBase::formatMoney(double iValue) const
1130 {
1131     return SKGServices::doubleToString(iValue);
1132 }