Kross

actioncollection.cpp
1 /***************************************************************************
2  * actioncollection.cpp
3  * This file is part of the KDE project
4  * copyright (C)2004-2006 by Sebastian Sauer ([email protected])
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  * You should have received a copy of the GNU Library General Public License
15  * along with this program; see the file COPYING. If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  ***************************************************************************/
19 
20 #include "actioncollection.h"
21 #include "manager.h"
22 #include "kross_debug.h"
23 
24 #include <QHash>
25 #include <QStringList>
26 #include <QPointer>
27 #include <QIODevice>
28 #include <QFile>
29 #include <QFileInfo>
30 #include <QDomAttr>
31 
32 #include <klocalizedstring.h>
33 
34 using namespace Kross;
35 
36 namespace Kross
37 {
38 
39 /// \internal d-pointer class.
40 class ActionCollection::Private
41 {
42 public:
45  QStringList collectionnames;
46 
47  QList< Action * > actionList;
49 
50  QString text;
52  QString iconname;
53  bool enabled;
54  bool blockupdated;
55 
56  Private(ActionCollection *const p) : parent(p) {}
57 };
58 
59 }
60 
62  : QObject(nullptr)
63  , d(new Private(nullptr))
64 {
65  setObjectName(name);
66  d->text = name;
67  d->enabled = true;
68  d->blockupdated = false;
69 
70  setParentCollection(parent);
71 }
72 
74 {
75  if (d->parent) {
76  emit d->parent->collectionToBeRemoved(this, d->parent);
77  d->parent->unregisterCollection(objectName());
78  emit d->parent->collectionRemoved(this, d->parent);
79  }
80  delete d;
81 }
82 
84 {
85  return objectName();
86 }
87 
89 {
90  return d->text;
91 }
93 {
94  d->text = text;
95  emit dataChanged(this);
96  emitUpdated();
97 }
98 
100 {
101  return d->description;
102 }
104 {
105  d->description = description;
106  emit dataChanged(this);
107  emitUpdated();
108 }
109 
111 {
112  return d->iconname;
113 }
115 {
116  d->iconname = iconname;
117  emit dataChanged(this);
118 }
120 {
121  return QIcon::fromTheme(d->iconname);
122 }
123 
125 {
126  return d->enabled;
127 }
129 {
130  d->enabled = enabled;
131  emit dataChanged(this);
132  emitUpdated();
133 }
134 
136 {
137  return d->parent;
138 }
139 
141 {
142  if (d->parent) {
143  emit d->parent->collectionToBeRemoved(this, d->parent);
144  d->parent->unregisterCollection(objectName());
145  setParent(nullptr);
146  emit d->parent->collectionRemoved(this, d->parent);
147  d->parent = nullptr;
148  }
149  setParent(nullptr);
150  if (parent) {
151  emit parent->collectionToBeInserted(this, parent);
152  setParent(parent);
153  d->parent = parent;
154  parent->registerCollection(this);
155  emit parent->collectionInserted(this, parent);
156  }
157  emitUpdated();
158 }
159 
161 {
162  return d->collections.contains(name);
163 }
164 
166 {
167  return d->collections.contains(name) ? d->collections[name] : QPointer<ActionCollection>(nullptr);
168 }
169 
171 {
172  return d->collectionnames;
173 }
174 
175 void ActionCollection::registerCollection(ActionCollection *collection)
176 {
177  Q_ASSERT(collection);
178  const QString name = collection->objectName();
179  //Q_ASSERT( !name.isNull() );
180  if (!d->collections.contains(name)) {
181  d->collections.insert(name, collection);
182  d->collectionnames.append(name);
183  }
184  connectSignals(collection, true);
185  emitUpdated();
186 }
187 
188 void ActionCollection::unregisterCollection(const QString &name)
189 {
190  if (! d->collections.contains(name)) {
191  return;
192  }
193  ActionCollection *collection = d->collections[name];
194  d->collectionnames.removeAll(name);
195  d->collections.remove(name);
196  connectSignals(collection, false);
197  emitUpdated();
198 }
199 
200 QList<Action *> ActionCollection::actions() const
201 {
202  return d->actionList;
203 }
204 
206 {
207  return d->actionMap.contains(name) ? d->actionMap[name] : nullptr;
208 }
209 
210 void ActionCollection::addAction(Action *action)
211 {
212  Q_ASSERT(action && ! action->objectName().isEmpty());
213  addAction(action->objectName(), action);
214 }
215 
216 void ActionCollection::addAction(const QString &name, Action *action)
217 {
218  Q_ASSERT(action && ! name.isEmpty());
219  emit actionToBeInserted(action, this);
220  if (d->actionMap.contains(name)) {
221  d->actionList.removeAll(d->actionMap[name]);
222  }
223  d->actionMap.insert(name, action);
224  d->actionList.append(action);
225  action->setParent(this); // in case it is not set
226  connectSignals(action, true);
227  emit actionInserted(action, this);
228  emitUpdated();
229 }
230 
231 void ActionCollection::removeAction(const QString &name)
232 {
233  if (! d->actionMap.contains(name)) {
234  return;
235  }
236  Action *action = d->actionMap[name];
237  connectSignals(action, false);
238  emit actionToBeRemoved(action, this);
239  d->actionList.removeAll(action);
240  d->actionMap.remove(name);
241  //krossdebug( QString("ActionCollection::removeAction: %1 %2").arg(action->name()).arg(action->parent()->objectName()) );
242  action->setParent(nullptr);
243  emit actionRemoved(action, this);
244  emitUpdated();
245 }
246 
247 void ActionCollection::removeAction(Action *action)
248 {
249  Q_ASSERT(action && ! action->objectName().isEmpty());
250  if (! d->actionMap.contains(action->objectName())) {
251  Q_ASSERT(! d->actionList.contains(action));
252  return;
253  }
254  removeAction(action->objectName());
255 }
256 
257 void ActionCollection::connectSignals(Action *action, bool conn)
258 {
259  if (conn) {
260  connect(action, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
261  connect(action, SIGNAL(updated()), this, SLOT(emitUpdated()));
262  } else {
263  disconnect(action, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
264  disconnect(action, SIGNAL(updated()), this, SLOT(emitUpdated()));
265  }
266 }
267 
268 void ActionCollection::connectSignals(ActionCollection *collection, bool conn)
269 {
270  if (conn) {
271  connect(collection, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
272  connect(collection, SIGNAL(dataChanged(ActionCollection*)), this, SIGNAL(dataChanged(ActionCollection*)));
273 
278 
280  connect(collection, SIGNAL(actionInserted(Action*,ActionCollection*)), this, SIGNAL(actionInserted(Action*,ActionCollection*)));
282  connect(collection, SIGNAL(actionRemoved(Action*,ActionCollection*)), this, SIGNAL(actionRemoved(Action*,ActionCollection*)));
283  connect(collection, SIGNAL(updated()), this, SLOT(emitUpdated()));
284  } else {
285  disconnect(collection, SIGNAL(dataChanged(ActionCollection*)), this, SIGNAL(dataChanged(ActionCollection*)));
286 
291 
293  disconnect(collection, SIGNAL(actionInserted(Action*,ActionCollection*)), this, SIGNAL(actionInserted(Action*,ActionCollection*)));
295  disconnect(collection, SIGNAL(actionRemoved(Action*,ActionCollection*)), this, SIGNAL(actionRemoved(Action*,ActionCollection*)));
296  disconnect(collection, SIGNAL(updated()), this, SLOT(emitUpdated()));
297  }
298 }
299 
300 void ActionCollection::emitUpdated()
301 {
302  if (!d->blockupdated) {
303  emit updated();
304  }
305 }
306 
307 /*********************************************************************
308  * Unserialize from XML / QIODevice / file / resource to child
309  * ActionCollection's and Action's this ActionCollection has.
310  */
311 
312 bool ActionCollection::readXml(const QDomElement &element, const QDir &directory)
313 {
314  return readXml(element, QStringList(directory.absolutePath()));
315 }
316 
317 bool ActionCollection::readXml(const QDomElement &element, const QStringList &searchPath)
318 {
319 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
320  qCDebug(KROSS_LOG) << "ActionCollection::readXml tagName=\"" << element.tagName() << "\"";
321 #endif
322 
323  d->blockupdated = true; // block updated() signals and emit it only once if everything is done
324  bool ok = true;
325  QDomNodeList list = element.childNodes();
326  const int size = list.size();
327  for (int i = 0; i < size; ++i) {
328  QDomElement elem = list.item(i).toElement();
329  if (elem.isNull()) {
330  continue;
331  }
332 
333 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
334  qCDebug(KROSS_LOG) << " ActionCollection::readXml child=" <<
335  i << " tagName=\"" << elem.tagName() << "\"";
336 #endif
337 
338  if (elem.tagName() == "collection") {
339  const QString name = elem.attribute("name");
340  const QByteArray text = elem.attribute("text").toUtf8();
341  const QByteArray description = elem.attribute("comment").toUtf8();
342  const QString iconname = elem.attribute("icon");
343  bool enabled = QVariant(elem.attribute("enabled", "true")).toBool();
344  ActionCollection *c = d->collections.contains(name) ? d->collections[name] : QPointer<ActionCollection>(nullptr);
345  if (! c) {
346  c = new ActionCollection(name, this);
347  }
348 
349  c->setText(text.isEmpty() ? name : i18nd(KLocalizedString::applicationDomain().constData(), text.constData()));
350  c->setDescription(description.isEmpty() ? c->text() : i18nd(KLocalizedString::applicationDomain().constData(), description.constData()));
351  c->setIconName(iconname);
352 
353  if (! enabled) {
354  c->setEnabled(false);
355  }
356  if (! c->readXml(elem, searchPath)) {
357  ok = false;
358  }
359  } else if (elem.tagName() == "script") {
360  QString name = elem.attribute("name");
361  Action *a = dynamic_cast< Action * >(action(name));
362  if (a) {
363 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
364  qCDebug(KROSS_LOG) << " ActionCollection::readXml Updating Action " << a->objectName();
365 #endif
366  } else {
367 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
368  qCDebug(KROSS_LOG) << " ActionCollection::readXml Creating Action " << name;
369 #endif
370 
371  a = new Action(this, name);
372  addAction(name, a);
373  connect(a, SIGNAL(started(Kross::Action*)), &Manager::self(), SIGNAL(started(Kross::Action*)));
374  connect(a, SIGNAL(finished(Kross::Action*)), &Manager::self(), SIGNAL(finished(Kross::Action*)));
375  }
376  a->fromDomElement(elem, searchPath);
377  }
378  //else if( ! fromXml(elem) ) ok = false;
379  }
380 
381  d->blockupdated = false; // unblock signals
382  emitUpdated();
383  return ok;
384 }
385 
386 bool ActionCollection::readXml(QIODevice *device, const QDir &directory)
387 {
388  return readXml(device, QStringList(directory.absolutePath()));
389 }
390 
391 bool ActionCollection::readXml(QIODevice *device, const QStringList &searchPath)
392 {
393  QString errMsg;
394  int errLine, errCol;
395  QDomDocument document;
396  bool ok = document.setContent(device, false, &errMsg, &errLine, &errCol);
397  if (! ok) {
398 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
399  qCWarning(KROSS_LOG) << QStringLiteral("ActionCollection::readXml Error at line %1 in col %2: %3")
400  .arg(errLine).arg(errCol).arg(errMsg);
401 #endif
402  return false;
403  }
404  return readXml(document.documentElement(), searchPath);
405 }
406 
408 {
409 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
410  qCDebug(KROSS_LOG) << "ActionCollection::readXmlFile file=" << file;
411 #endif
412 
413  QFile f(file);
414  if (! f.open(QIODevice::ReadOnly)) {
415 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
416  qCWarning(KROSS_LOG) << "ActionCollection::readXmlFile failed to read file " << file;
417 #endif
418  return false;
419  }
420  bool ok = readXml(&f, QFileInfo(file).dir());
421  f.close();
422 
423 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
424  if (! ok) {
425  qCWarning(KROSS_LOG) << "ActionCollection::readXmlFile failed to parse XML content of file " << file;
426  }
427 #endif
428  return ok;
429 }
430 
431 /*********************************************************************
432  * Serialize from child ActionCollection's and Action's this
433  * ActionCollection has to XML / QIODevice / file / resource.
434  */
435 
437 {
438  return writeXml(QStringList());
439 }
440 
442 {
443 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
444  qCDebug(KROSS_LOG) << "ActionCollection::writeXml collection.objectName=" << objectName();
445 #endif
446 
447  QDomDocument document;
448  QDomElement element = document.createElement("collection");
449  if (! objectName().isNull()) {
450  element.setAttribute("name", objectName());
451  }
452  if (! text().isNull() && text() != objectName()) {
453  element.setAttribute("text", text());
454  }
455  if (! d->description.isNull()) {
456  element.setAttribute("comment", d->description);
457  }
458  if (! d->iconname.isNull()) {
459  element.setAttribute("icon", d->iconname);
460  }
461  if (! d->enabled) {
462  element.setAttribute("enabled", d->enabled);
463  }
464 
465  foreach (Action *a, actions()) {
466  Q_ASSERT(a);
467 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
468  qCDebug(KROSS_LOG) << " ActionCollection::writeXml action.objectName=" <<
469  a->objectName() << " action.file=" << a->file();
470 #endif
471  QDomElement e = a->toDomElement(searchPath);
472  if (! e.isNull()) {
473  element.appendChild(e);
474  }
475  }
476 
477  foreach (const QString &name, d->collectionnames) {
478  ActionCollection *c = d->collections[name];
479  if (! c) {
480  continue;
481  }
482  QDomElement e = c->writeXml(searchPath);
483  if (! e.isNull()) {
484  element.appendChild(e);
485  }
486  }
487 
488  return element;
489 }
490 
491 bool ActionCollection::writeXml(QIODevice *device, int indent)
492 {
493  return writeXml(device, indent, QStringList());
494 }
495 
496 bool ActionCollection::writeXml(QIODevice *device, int indent, const QStringList &searchPath)
497 {
498  QDomDocument document;
499  QDomElement root = document.createElement("KrossScripting");
500 
501  foreach (Action *a, actions()) {
502  QDomElement e = a->toDomElement(searchPath);
503  if (! e.isNull()) {
504  root.appendChild(e);
505  }
506  }
507 
508  foreach (const QString &name, d->collectionnames) {
509  ActionCollection *c = d->collections[name];
510  if (! c) {
511  continue;
512  }
513  QDomElement e = c->writeXml(searchPath);
514  if (! e.isNull()) {
515  root.appendChild(e);
516  }
517  }
518 
519  document.appendChild(root);
520  return device->write(document.toByteArray(indent)) != -1;
521 }
522 
void actionRemoved(Action *child, ActionCollection *parent)
This signal is emitted after child has been removed from parent.
ActionCollection * parentCollection() const
QDomNode item(int index) const const
bool hasCollection(const QString &name) const
QDomNode appendChild(const QDomNode &newChild)
void setText(const QString &text)
Set the display text to text .
QString attribute(const QString &name, const QString &defValue) const const
Definition: action.cpp:36
void actionToBeInserted(Action *child, ActionCollection *parent)
This signal is emitted just before child is added to parent.
bool readXmlFile(const QString &file)
Read the XML from the file file .
bool isEnabled() const
Return the enable this ActionCollection has.
bool isEmpty() const const
QDomElement toDomElement() const
Definition: action.cpp:220
void actionInserted(Action *child, ActionCollection *parent)
This signal is emitted after child has been added to parent.
QString file() const
Definition: action.cpp:366
QDomElement documentElement() const const
void actionToBeRemoved(Action *child, ActionCollection *parent)
This signal is emitted before child is removed from parent.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QDomNodeList childNodes() const const
void setDescription(const QString &description)
Set the optional description for this ActionCollection.
void collectionRemoved(ActionCollection *child, ActionCollection *parent)
This signal is emitted after child has been removed from parent.
QDomElement toElement() const const
void collectionInserted(ActionCollection *child, ActionCollection *parent)
This signal is emitted after child has been added to parent.
void dataChanged(Action *)
This signal is emitted when the data of a child action is changed.
void setParentCollection(ActionCollection *parent)
Set the parent to parent. NOTE: Do not use setParent().
void setAttribute(const QString &name, const QString &value)
void setObjectName(const QString &name)
bool isEmpty() const const
const char * constData() const const
static QByteArray applicationDomain()
void setEnabled(bool enabled)
Enable or disable this ActionCollection.
ActionCollection * collection(const QString &name) const
void setIconName(const QString &iconname)
Set the name of the icon to iconname .
void fromDomElement(const QDomElement &element)
Method to read settings from the QDomElement element that contains details about e.g.
Definition: action.cpp:154
virtual bool open(QIODevice::OpenMode mode) override
QStringList collections() const
bool readXml(const QDomElement &element, const QDir &directory=QDir())
Load child Action and ActionCollection instances this collection has from the element ...
void setParent(QObject *parent)
bool isNull() const const
static Manager & self()
Return the Manager instance.
Definition: manager.cpp:74
QString absolutePath() const const
virtual void close() override
ActionCollection(const QString &name, ActionCollection *parent=nullptr)
Constructor.
QString description() const
qint64 write(const char *data, qint64 maxSize)
void updated()
This signal is emitted if the content of the ActionCollection was changed.
QIcon fromTheme(const QString &name)
QString tagName() const const
The Action class is an abstract container to deal with scripts like a single standalone script file...
Definition: action.h:95
int size() const const
QDomElement createElement(const QString &tagName)
Action * action(const QString &name) const
QString i18nd(const char *domain, const char *text, const TYPE &arg...)
The ActionCollection class manages collections of Action instances.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
void collectionToBeRemoved(ActionCollection *child, ActionCollection *parent)
This signal is emitted before child is removed from parent.
~ActionCollection() override
Destructor.
QByteArray toByteArray(int indent) const const
void collectionToBeInserted(ActionCollection *child, ActionCollection *parent)
This signal is emitted just before child is added to parent.
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Wed May 12 2021 22:58:32 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.