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 "action.h"
22 #include "manager.h"
23 #include "kross_debug.h"
24 
25 #include <QHash>
26 #include <QStringList>
27 #include <QPointer>
28 #include <QIODevice>
29 #include <QFile>
30 #include <QFileInfo>
31 #include <QDomAttr>
32 
33 #include <klocalizedstring.h>
34 
35 using namespace Kross;
36 
37 namespace Kross
38 {
39 
41 class ActionCollection::Private
42 {
43 public:
46  QStringList collectionnames;
47 
48  QList< Action * > actionList;
50 
51  QString text;
53  QString iconname;
54  bool enabled;
55  bool blockupdated;
56 
57  Private(ActionCollection *const p) : parent(p) {}
58 };
59 
60 }
61 
63  : QObject(nullptr)
64  , d(new Private(nullptr))
65 {
66  setObjectName(name);
67  d->text = name;
68  d->enabled = true;
69  d->blockupdated = false;
70 
71  setParentCollection(parent);
72 }
73 
75 {
76  if (d->parent) {
77  emit d->parent->collectionToBeRemoved(this, d->parent);
78  d->parent->unregisterCollection(objectName());
79  emit d->parent->collectionRemoved(this, d->parent);
80  }
81  delete d;
82 }
83 
85 {
86  return objectName();
87 }
88 
90 {
91  return d->text;
92 }
94 {
95  d->text = text;
96  emit dataChanged(this);
97  emitUpdated();
98 }
99 
101 {
102  return d->description;
103 }
105 {
106  d->description = description;
107  emit dataChanged(this);
108  emitUpdated();
109 }
110 
112 {
113  return d->iconname;
114 }
116 {
117  d->iconname = iconname;
118  emit dataChanged(this);
119 }
121 {
122  return QIcon::fromTheme(d->iconname);
123 }
124 
126 {
127  return d->enabled;
128 }
130 {
131  d->enabled = enabled;
132  emit dataChanged(this);
133  emitUpdated();
134 }
135 
137 {
138  return d->parent;
139 }
140 
142 {
143  if (d->parent) {
144  emit d->parent->collectionToBeRemoved(this, d->parent);
145  d->parent->unregisterCollection(objectName());
146  setParent(nullptr);
147  emit d->parent->collectionRemoved(this, d->parent);
148  d->parent = nullptr;
149  }
150  setParent(nullptr);
151  if (parent) {
152  emit parent->collectionToBeInserted(this, parent);
153  setParent(parent);
154  d->parent = parent;
155  parent->registerCollection(this);
156  emit parent->collectionInserted(this, parent);
157  }
158  emitUpdated();
159 }
160 
162 {
163  return d->collections.contains(name);
164 }
165 
167 {
168  return d->collections.contains(name) ? d->collections[name] : QPointer<ActionCollection>(nullptr);
169 }
170 
172 {
173  return d->collectionnames;
174 }
175 
176 void ActionCollection::registerCollection(ActionCollection *collection)
177 {
178  Q_ASSERT(collection);
179  const QString name = collection->objectName();
180  //Q_ASSERT( !name.isNull() );
181  if (!d->collections.contains(name)) {
182  d->collections.insert(name, collection);
183  d->collectionnames.append(name);
184  }
185  connectSignals(collection, true);
186  emitUpdated();
187 }
188 
189 void ActionCollection::unregisterCollection(const QString &name)
190 {
191  if (! d->collections.contains(name)) {
192  return;
193  }
194  ActionCollection *collection = d->collections[name];
195  d->collectionnames.removeAll(name);
196  d->collections.remove(name);
197  connectSignals(collection, false);
198  emitUpdated();
199 }
200 
201 QList<Action *> ActionCollection::actions() const
202 {
203  return d->actionList;
204 }
205 
207 {
208  return d->actionMap.contains(name) ? d->actionMap[name] : nullptr;
209 }
210 
211 void ActionCollection::addAction(Action *action)
212 {
213  Q_ASSERT(action && ! action->objectName().isEmpty());
214  addAction(action->objectName(), action);
215 }
216 
217 void ActionCollection::addAction(const QString &name, Action *action)
218 {
219  Q_ASSERT(action && ! name.isEmpty());
220  emit actionToBeInserted(action, this);
221  if (d->actionMap.contains(name)) {
222  d->actionList.removeAll(d->actionMap[name]);
223  }
224  d->actionMap.insert(name, action);
225  d->actionList.append(action);
226  action->setParent(this); // in case it is not set
227  connectSignals(action, true);
228  emit actionInserted(action, this);
229  emitUpdated();
230 }
231 
232 void ActionCollection::removeAction(const QString &name)
233 {
234  if (! d->actionMap.contains(name)) {
235  return;
236  }
237  Action *action = d->actionMap[name];
238  connectSignals(action, false);
239  emit actionToBeRemoved(action, this);
240  d->actionList.removeAll(action);
241  d->actionMap.remove(name);
242  //krossdebug( QString("ActionCollection::removeAction: %1 %2").arg(action->name()).arg(action->parent()->objectName()) );
243  action->setParent(nullptr);
244  emit actionRemoved(action, this);
245  emitUpdated();
246 }
247 
248 void ActionCollection::removeAction(Action *action)
249 {
250  Q_ASSERT(action && ! action->objectName().isEmpty());
251  if (! d->actionMap.contains(action->objectName())) {
252  Q_ASSERT(! d->actionList.contains(action));
253  return;
254  }
255  removeAction(action->objectName());
256 }
257 
258 void ActionCollection::connectSignals(Action *action, bool conn)
259 {
260  if (conn) {
261  connect(action, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
262  connect(action, SIGNAL(updated()), this, SLOT(emitUpdated()));
263  } else {
264  disconnect(action, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
265  disconnect(action, SIGNAL(updated()), this, SLOT(emitUpdated()));
266  }
267 }
268 
269 void ActionCollection::connectSignals(ActionCollection *collection, bool conn)
270 {
271  if (conn) {
272  connect(collection, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
273  connect(collection, SIGNAL(dataChanged(ActionCollection*)), this, SIGNAL(dataChanged(ActionCollection*)));
274 
279 
281  connect(collection, SIGNAL(actionInserted(Action*,ActionCollection*)), this, SIGNAL(actionInserted(Action*,ActionCollection*)));
283  connect(collection, SIGNAL(actionRemoved(Action*,ActionCollection*)), this, SIGNAL(actionRemoved(Action*,ActionCollection*)));
284  connect(collection, SIGNAL(updated()), this, SLOT(emitUpdated()));
285  } else {
286  disconnect(collection, SIGNAL(dataChanged(ActionCollection*)), this, SIGNAL(dataChanged(ActionCollection*)));
287 
292 
294  disconnect(collection, SIGNAL(actionInserted(Action*,ActionCollection*)), this, SIGNAL(actionInserted(Action*,ActionCollection*)));
296  disconnect(collection, SIGNAL(actionRemoved(Action*,ActionCollection*)), this, SIGNAL(actionRemoved(Action*,ActionCollection*)));
297  disconnect(collection, SIGNAL(updated()), this, SLOT(emitUpdated()));
298  }
299 }
300 
301 void ActionCollection::emitUpdated()
302 {
303  if (!d->blockupdated) {
304  emit updated();
305  }
306 }
307 
308 /*********************************************************************
309  * Unserialize from XML / QIODevice / file / resource to child
310  * ActionCollection's and Action's this ActionCollection has.
311  */
312 
313 bool ActionCollection::readXml(const QDomElement &element, const QDir &directory)
314 {
315  return readXml(element, QStringList(directory.absolutePath()));
316 }
317 
318 bool ActionCollection::readXml(const QDomElement &element, const QStringList &searchPath)
319 {
320 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
321  qCDebug(KROSS_LOG) << "ActionCollection::readXml tagName=\"" << element.tagName() << "\"";
322 #endif
323 
324  d->blockupdated = true; // block updated() signals and emit it only once if everything is done
325  bool ok = true;
326  QDomNodeList list = element.childNodes();
327  const int size = list.size();
328  for (int i = 0; i < size; ++i) {
329  QDomElement elem = list.item(i).toElement();
330  if (elem.isNull()) {
331  continue;
332  }
333 
334 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
335  qCDebug(KROSS_LOG) << " ActionCollection::readXml child=" <<
336  i << " tagName=\"" << elem.tagName() << "\"";
337 #endif
338 
339  if (elem.tagName() == "collection") {
340  const QString name = elem.attribute("name");
341  const QByteArray text = elem.attribute("text").toUtf8();
342  const QByteArray description = elem.attribute("comment").toUtf8();
343  const QString iconname = elem.attribute("icon");
344  bool enabled = QVariant(elem.attribute("enabled", "true")).toBool();
345  ActionCollection *c = d->collections.contains(name) ? d->collections[name] : QPointer<ActionCollection>(nullptr);
346  if (! c) {
347  c = new ActionCollection(name, this);
348  }
349 
350  c->setText(text.isEmpty() ? name : i18nd(KLocalizedString::applicationDomain().constData(), text.constData()));
351  c->setDescription(description.isEmpty() ? c->text() : i18nd(KLocalizedString::applicationDomain().constData(), description.constData()));
352  c->setIconName(iconname);
353 
354  if (! enabled) {
355  c->setEnabled(false);
356  }
357  if (! c->readXml(elem, searchPath)) {
358  ok = false;
359  }
360  } else if (elem.tagName() == "script") {
361  QString name = elem.attribute("name");
362  Action *a = dynamic_cast< Action * >(action(name));
363  if (a) {
364 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
365  qCDebug(KROSS_LOG) << " ActionCollection::readXml Updating Action " << a->objectName();
366 #endif
367  } else {
368 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
369  qCDebug(KROSS_LOG) << " ActionCollection::readXml Creating Action " << name;
370 #endif
371 
372  a = new Action(this, name);
373  addAction(name, a);
374  connect(a, SIGNAL(started(Kross::Action*)), &Manager::self(), SIGNAL(started(Kross::Action*)));
375  connect(a, SIGNAL(finished(Kross::Action*)), &Manager::self(), SIGNAL(finished(Kross::Action*)));
376  }
377  a->fromDomElement(elem, searchPath);
378  }
379  //else if( ! fromXml(elem) ) ok = false;
380  }
381 
382  d->blockupdated = false; // unblock signals
383  emitUpdated();
384  return ok;
385 }
386 
387 bool ActionCollection::readXml(QIODevice *device, const QDir &directory)
388 {
389  return readXml(device, QStringList(directory.absolutePath()));
390 }
391 
392 bool ActionCollection::readXml(QIODevice *device, const QStringList &searchPath)
393 {
394  QString errMsg;
395  int errLine, errCol;
396  QDomDocument document;
397  bool ok = document.setContent(device, false, &errMsg, &errLine, &errCol);
398  if (! ok) {
399 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
400  qCWarning(KROSS_LOG) << QStringLiteral("ActionCollection::readXml Error at line %1 in col %2: %3")
401  .arg(errLine).arg(errCol).arg(errMsg);
402 #endif
403  return false;
404  }
405  return readXml(document.documentElement(), searchPath);
406 }
407 
409 {
410 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
411  qCDebug(KROSS_LOG) << "ActionCollection::readXmlFile file=" << file;
412 #endif
413 
414  QFile f(file);
415  if (! f.open(QIODevice::ReadOnly)) {
416 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
417  qCWarning(KROSS_LOG) << "ActionCollection::readXmlFile failed to read file " << file;
418 #endif
419  return false;
420  }
421  bool ok = readXml(&f, QFileInfo(file).dir());
422  f.close();
423 
424 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
425  if (! ok) {
426  qCWarning(KROSS_LOG) << "ActionCollection::readXmlFile failed to parse XML content of file " << file;
427  }
428 #endif
429  return ok;
430 }
431 
432 /*********************************************************************
433  * Serialize from child ActionCollection's and Action's this
434  * ActionCollection has to XML / QIODevice / file / resource.
435  */
436 
438 {
439  return writeXml(QStringList());
440 }
441 
443 {
444 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
445  qCDebug(KROSS_LOG) << "ActionCollection::writeXml collection.objectName=" << objectName();
446 #endif
447 
448  QDomDocument document;
449  QDomElement element = document.createElement("collection");
450  if (! objectName().isNull()) {
451  element.setAttribute("name", objectName());
452  }
453  if (! text().isNull() && text() != objectName()) {
454  element.setAttribute("text", text());
455  }
456  if (! d->description.isNull()) {
457  element.setAttribute("comment", d->description);
458  }
459  if (! d->iconname.isNull()) {
460  element.setAttribute("icon", d->iconname);
461  }
462  if (! d->enabled) {
463  element.setAttribute("enabled", d->enabled);
464  }
465 
466  foreach (Action *a, actions()) {
467  Q_ASSERT(a);
468 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
469  qCDebug(KROSS_LOG) << " ActionCollection::writeXml action.objectName=" <<
470  a->objectName() << " action.file=" << a->file();
471 #endif
472  QDomElement e = a->toDomElement(searchPath);
473  if (! e.isNull()) {
474  element.appendChild(e);
475  }
476  }
477 
478  foreach (const QString &name, d->collectionnames) {
479  ActionCollection *c = d->collections[name];
480  if (! c) {
481  continue;
482  }
483  QDomElement e = c->writeXml(searchPath);
484  if (! e.isNull()) {
485  element.appendChild(e);
486  }
487  }
488 
489  return element;
490 }
491 
492 bool ActionCollection::writeXml(QIODevice *device, int indent)
493 {
494  return writeXml(device, indent, QStringList());
495 }
496 
497 bool ActionCollection::writeXml(QIODevice *device, int indent, const QStringList &searchPath)
498 {
499  QDomDocument document;
500  QDomElement root = document.createElement("KrossScripting");
501 
502  foreach (Action *a, actions()) {
503  QDomElement e = a->toDomElement(searchPath);
504  if (! e.isNull()) {
505  root.appendChild(e);
506  }
507  }
508 
509  foreach (const QString &name, d->collectionnames) {
510  ActionCollection *c = d->collections[name];
511  if (! c) {
512  continue;
513  }
514  QDomElement e = c->writeXml(searchPath);
515  if (! e.isNull()) {
516  root.appendChild(e);
517  }
518  }
519 
520  document.appendChild(root);
521  return device->write(document.toByteArray(indent)) != -1;
522 }
523 
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.
virtual ~ActionCollection()
Destructor.
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.
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-2020 The KDE developers.
Generated on Tue Sep 29 2020 23:06:34 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.