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