1 /***************************************************************************
2  * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3  * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4  * SPDX-License-Identifier: GPL-3.0-or-later
5  ***************************************************************************/
6 /** @file
7  * This file implements classes SKGNodeObject.
8  *
9  * @author Stephane MANKOWSKI / Guillaume DE BURE
10  */
11 #include "skgnodeobject.h"
12 
13 #include <klocalizedstring.h>
14 #include <qicon.h>
15 
16 #include "skgdefine.h"
17 #include "skgdocument.h"
18 #include "skgtraces.h"
19 
SKGNodeObject()20 SKGNodeObject::SKGNodeObject(): SKGNodeObject(nullptr)
21 {}
22 
SKGNodeObject(SKGDocument * iDocument,int iID)23 SKGNodeObject::SKGNodeObject(SKGDocument* iDocument, int iID): SKGNamedObject(iDocument, QStringLiteral("v_node"), iID), opened(false)
24 {}
25 
26 SKGNodeObject::~SKGNodeObject()
27     = default;
28 
SKGNodeObject(const SKGNodeObject & iObject)29 SKGNodeObject::SKGNodeObject(const SKGNodeObject& iObject) : SKGNamedObject(iObject), opened(false)
30 {}
31 
SKGNodeObject(const SKGObjectBase & iObject)32 SKGNodeObject::SKGNodeObject(const SKGObjectBase& iObject) : SKGNamedObject(iObject.getDocument(), QStringLiteral("v_node"), iObject.getID()), opened(false)
33 {}
34 
operator =(const SKGObjectBase & iObject)35 SKGNodeObject& SKGNodeObject::operator= (const SKGObjectBase& iObject)
36 {
37     copyFrom(iObject);
38     return *this;
39 }
40 
operator =(const SKGNodeObject & iObject)41 SKGNodeObject& SKGNodeObject::operator= (const SKGNodeObject& iObject)
42 {
43     copyFrom(iObject);
44     return *this;
45 }
46 
setName(const QString & iName)47 SKGError SKGNodeObject::setName(const QString& iName)
48 {
49     SKGError err;
50     if (iName.contains(OBJECTSEPARATOR)) {
51         err = SKGError(ERR_FAIL, i18nc("Error message: an invalid character was found", "The name '%1' is invalid : the '%2' character is forbidden ", iName, QString(OBJECTSEPARATOR)));
52     } else {
53         err = SKGNamedObject::setName(iName);
54     }
55     return err;
56 }
57 
getWhereclauseId() const58 QString SKGNodeObject::getWhereclauseId() const
59 {
60     // Could we use the id
61     QString output = SKGObjectBase::getWhereclauseId();  // clazy:exclude=skipped-base-method
62     if (output.isEmpty()) {
63         if (!(getAttribute(QStringLiteral("t_name")).isEmpty())) {
64             output = "t_name='" % SKGServices::stringToSqlString(getAttribute(QStringLiteral("t_name"))) % '\'';
65         }
66         QString rd_node_id = getAttribute(QStringLiteral("rd_node_id"));
67         if (!output.isEmpty()) {
68             output += QStringLiteral(" AND ");
69         }
70         if (rd_node_id.isEmpty()) {
71             output += QStringLiteral("(rd_node_id=0 OR rd_node_id IS NULL OR rd_node_id='')");
72         } else {
73             output += "rd_node_id=" % rd_node_id;
74         }
75     }
76     return output;
77 }
78 
getFullName() const79 QString SKGNodeObject::getFullName() const
80 {
81     return getAttribute(QStringLiteral("t_fullname"));
82 }
83 
setData(const QString & iData)84 SKGError SKGNodeObject::setData(const QString& iData)
85 {
86     return setAttribute(QStringLiteral("t_data"), iData);
87 }
88 
getData() const89 QString SKGNodeObject::getData() const
90 {
91     return getAttribute(QStringLiteral("t_data"));
92 }
93 
isFolder() const94 bool SKGNodeObject::isFolder() const
95 {
96     return getData().isEmpty();
97 }
98 
setIcon(const QString & iIcon)99 SKGError SKGNodeObject::setIcon(const QString& iIcon)
100 {
101     return setAttribute(QStringLiteral("t_icon"), iIcon);
102 }
103 
getIcon() const104 QIcon SKGNodeObject::getIcon() const
105 {
106     QStringList overlay;
107     if (isAutoStart()) {
108         overlay.push_back(QStringLiteral("media-playback-start"));
109     }
110     return SKGServices::fromTheme(getAttribute(QStringLiteral("t_icon")), overlay);
111 }
112 
setAutoStart(bool iAutoStart)113 SKGError SKGNodeObject::setAutoStart(bool iAutoStart)
114 {
115     return setAttribute(QStringLiteral("t_autostart"), iAutoStart ? QStringLiteral("Y") : QStringLiteral("N"));
116 }
117 
isAutoStart() const118 bool SKGNodeObject::isAutoStart() const
119 {
120     return (getAttribute(QStringLiteral("t_autostart")) == QStringLiteral("Y"));
121 }
122 
setOrder(double iOrder)123 SKGError SKGNodeObject::setOrder(double iOrder)
124 {
125     SKGError err;
126     double order = iOrder;
127     if (order == -1) {
128         order = 1;
129         SKGStringListList result;
130         err = getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT max(f_sortorder) from node"), result);
131         if (!err && result.count() == 2) {
132             order = SKGServices::stringToDouble(result.at(1).at(0)) + 1;
133         }
134     }
135     IFOKDO(err, setAttribute(QStringLiteral("f_sortorder"), SKGServices::doubleToString(order)))
136     return err;
137 }
138 
getOrder() const139 double SKGNodeObject::getOrder() const
140 {
141     return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_sortorder")));
142 }
143 
createPathNode(SKGDocument * iDocument,const QString & iFullPath,SKGNodeObject & oNode,bool iRenameIfAlreadyExist)144 SKGError SKGNodeObject::createPathNode(SKGDocument* iDocument,
145                                        const QString& iFullPath,
146                                        SKGNodeObject& oNode,
147                                        bool iRenameIfAlreadyExist)
148 {
149     SKGError err;
150     SKGTRACEINFUNCRC(10, err)
151     SKGTRACEL(10) << "Input parameter [iFullPath]=" << iFullPath << SKGENDL;
152     // Check if node is already existing
153     if (!iRenameIfAlreadyExist && iDocument != nullptr) {
154         iDocument->getObject(QStringLiteral("v_node"), "t_fullname='" % SKGServices::stringToSqlString(iFullPath) % '\'', oNode);
155     }
156     if (oNode.getID() == 0) {
157         // No, we have to create it
158         // Search node separator
159         int posSeparator = iFullPath.lastIndexOf(OBJECTSEPARATOR);
160         if (posSeparator == -1) {
161             oNode = SKGNodeObject(iDocument);
162             err = oNode.setName(iFullPath);
163 
164             // Check if already existing
165             if (!err && iRenameIfAlreadyExist) {
166                 int index = 1;
167                 while (!err && oNode.exist()) {
168                     index++;
169                     err = oNode.setName(iFullPath % " (" % SKGServices::intToString(index) % ')');
170                 }
171             }
172 
173             IFOKDO(err, oNode.setIcon(QStringLiteral("folder-orange")))
174             IFOKDO(err, oNode.setOrder(-1))
175             IFOKDO(err, oNode.save())
176         } else {
177             // Get first and second parts of the branch
178             QString first = iFullPath.mid(0, posSeparator);
179             QString second = iFullPath.mid(posSeparator + QString(OBJECTSEPARATOR).length(), iFullPath.length() - posSeparator - QString(OBJECTSEPARATOR).length());
180 
181             // Get first node
182             SKGNodeObject FirstNode;
183             err = SKGNodeObject::createPathNode(iDocument, first, FirstNode);
184 
185             IFOK(err) {
186                 // Get second node
187                 err = FirstNode.addNode(oNode);
188 
189                 // Add second under first
190                 IFOKDO(err, oNode.setName(second))
191 
192                 // Check if already existing
193                 if (!err && iRenameIfAlreadyExist) {
194                     int index = 2;
195                     while (!err && oNode.exist()) {
196                         err = oNode.setName(second % " (" % SKGServices::intToString(index) % ')');
197                         ++index;
198                     }
199                 }
200 
201                 // save
202                 IFOKDO(err, oNode.setIcon(QStringLiteral("folder-orange")))
203                 IFOKDO(err, oNode.setOrder(-1))
204                 IFOKDO(err, oNode.save())
205             }
206         }
207     }
208 
209     return err;
210 }
211 
addNode(SKGNodeObject & oNode)212 SKGError SKGNodeObject::addNode(SKGNodeObject& oNode)
213 {
214     SKGError err;
215     SKGTRACEINFUNCRC(10, err)
216     if (getID() == 0) {
217         err = SKGError(ERR_FAIL, i18nc("Error message: Something failed because of a database issue", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGNodeObject::addNode")));
218     } else {
219         oNode = SKGNodeObject(getDocument());
220         err = oNode.setAttribute(QStringLiteral("rd_node_id"), SKGServices::intToString(getID()));
221     }
222     return err;
223 }
224 
removeParentNode()225 SKGError SKGNodeObject::removeParentNode()
226 {
227     return setAttribute(QStringLiteral("rd_node_id"), QLatin1String(""));
228 }
229 
setParentNode(const SKGNodeObject & iNode)230 SKGError SKGNodeObject::setParentNode(const SKGNodeObject& iNode)
231 {
232     SKGError err;
233     SKGTRACEINFUNCRC(10, err)
234     if (iNode.getID() == 0) {
235         err = SKGError(ERR_FAIL, i18nc("Error message: Something failed because of a database issue", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGNodeObject::setParentNode")));
236     } else {
237         // Check if it is a loop
238         SKGNodeObject current = iNode;
239         do {
240             if (current == *this) {
241                 err = SKGError(ERR_FAIL, i18nc("Error message: Loops are forbidden in Skrooge data structures", "You cannot create a loop, ie parent and child with the same name. For example, A > A is a loop. A > B > A is another kind of loop"));
242             } else {
243                 SKGNodeObject parentNode;
244                 current.getParentNode(parentNode);
245                 current = parentNode;
246             }
247         } while (!err && current.getID() != 0);
248 
249         IFOKDO(err, setAttribute(QStringLiteral("rd_node_id"), SKGServices::intToString(iNode.getID())))
250     }
251     return err;
252 }
253 
getParentNode(SKGNodeObject & oNode) const254 SKGError SKGNodeObject::getParentNode(SKGNodeObject& oNode) const
255 {
256     SKGError err;
257     QString parent_id = getAttribute(QStringLiteral("rd_node_id"));
258     if (!parent_id.isEmpty()) {
259         err = getDocument()->getObject(QStringLiteral("v_node"), "id=" % parent_id, oNode);
260     } else {
261         oNode = SKGNodeObject();
262     }
263     return err;
264 }
265 
getNodes(SKGListSKGObjectBase & oNodeList) const266 SKGError SKGNodeObject::getNodes(SKGListSKGObjectBase& oNodeList) const
267 {
268     return getDocument()->getObjects(QStringLiteral("v_node"), "rd_node_id=" % SKGServices::intToString(getID()) % " ORDER BY f_sortorder, t_name", oNodeList);
269 }
270 
271 
272