File indexing completed on 2024-03-24 15:37:24
0001 /*************************************************************************** 0002 * actioncollection.cpp 0003 * This file is part of the KDE project 0004 * copyright (C)2004-2006 by Sebastian Sauer (mail@dipe.org) 0005 * 0006 * This program is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Library General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 * Library General Public License for more details. 0014 * You should have received a copy of the GNU Library General Public License 0015 * along with this program; see the file COPYING. If not, write to 0016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 ***************************************************************************/ 0019 0020 #include "actioncollection.h" 0021 #include "manager.h" 0022 #include "kross_debug.h" 0023 0024 #include <QHash> 0025 #include <QStringList> 0026 #include <QPointer> 0027 #include <QIODevice> 0028 #include <QFile> 0029 #include <QFileInfo> 0030 #include <QDomAttr> 0031 0032 #include <klocalizedstring.h> 0033 0034 using namespace Kross; 0035 0036 namespace Kross 0037 { 0038 0039 /// \internal d-pointer class. 0040 class ActionCollection::Private 0041 { 0042 public: 0043 QPointer<ActionCollection> parent; 0044 QHash< QString, QPointer<ActionCollection> > collections; 0045 QStringList collectionnames; 0046 0047 QList< Action * > actionList; 0048 QHash< QString, Action * > actionMap; 0049 0050 QString text; 0051 QString description; 0052 QString iconname; 0053 bool enabled; 0054 bool blockupdated; 0055 0056 Private(ActionCollection *const p) : parent(p) {} 0057 }; 0058 0059 } 0060 0061 ActionCollection::ActionCollection(const QString &name, ActionCollection *parent) 0062 : QObject(nullptr) 0063 , d(new Private(nullptr)) 0064 { 0065 setObjectName(name); 0066 d->text = name; 0067 d->enabled = true; 0068 d->blockupdated = false; 0069 0070 setParentCollection(parent); 0071 } 0072 0073 ActionCollection::~ActionCollection() 0074 { 0075 if (d->parent) { 0076 emit d->parent->collectionToBeRemoved(this, d->parent); 0077 d->parent->unregisterCollection(objectName()); 0078 emit d->parent->collectionRemoved(this, d->parent); 0079 } 0080 delete d; 0081 } 0082 0083 QString ActionCollection::name() const 0084 { 0085 return objectName(); 0086 } 0087 0088 QString ActionCollection::text() const 0089 { 0090 return d->text; 0091 } 0092 void ActionCollection::setText(const QString &text) 0093 { 0094 d->text = text; 0095 emit dataChanged(this); 0096 emitUpdated(); 0097 } 0098 0099 QString ActionCollection::description() const 0100 { 0101 return d->description; 0102 } 0103 void ActionCollection::setDescription(const QString &description) 0104 { 0105 d->description = description; 0106 emit dataChanged(this); 0107 emitUpdated(); 0108 } 0109 0110 QString ActionCollection::iconName() const 0111 { 0112 return d->iconname; 0113 } 0114 void ActionCollection::setIconName(const QString &iconname) 0115 { 0116 d->iconname = iconname; 0117 emit dataChanged(this); 0118 } 0119 QIcon ActionCollection::icon() const 0120 { 0121 return QIcon::fromTheme(d->iconname); 0122 } 0123 0124 bool ActionCollection::isEnabled() const 0125 { 0126 return d->enabled; 0127 } 0128 void ActionCollection::setEnabled(bool enabled) 0129 { 0130 d->enabled = enabled; 0131 emit dataChanged(this); 0132 emitUpdated(); 0133 } 0134 0135 ActionCollection *ActionCollection::parentCollection() const 0136 { 0137 return d->parent; 0138 } 0139 0140 void ActionCollection::setParentCollection(ActionCollection *parent) 0141 { 0142 if (d->parent) { 0143 emit d->parent->collectionToBeRemoved(this, d->parent); 0144 d->parent->unregisterCollection(objectName()); 0145 setParent(nullptr); 0146 emit d->parent->collectionRemoved(this, d->parent); 0147 d->parent = nullptr; 0148 } 0149 setParent(nullptr); 0150 if (parent) { 0151 emit parent->collectionToBeInserted(this, parent); 0152 setParent(parent); 0153 d->parent = parent; 0154 parent->registerCollection(this); 0155 emit parent->collectionInserted(this, parent); 0156 } 0157 emitUpdated(); 0158 } 0159 0160 bool ActionCollection::hasCollection(const QString &name) const 0161 { 0162 return d->collections.contains(name); 0163 } 0164 0165 ActionCollection *ActionCollection::collection(const QString &name) const 0166 { 0167 return d->collections.contains(name) ? d->collections[name] : QPointer<ActionCollection>(nullptr); 0168 } 0169 0170 QStringList ActionCollection::collections() const 0171 { 0172 return d->collectionnames; 0173 } 0174 0175 void ActionCollection::registerCollection(ActionCollection *collection) 0176 { 0177 Q_ASSERT(collection); 0178 const QString name = collection->objectName(); 0179 //Q_ASSERT( !name.isNull() ); 0180 if (!d->collections.contains(name)) { 0181 d->collections.insert(name, collection); 0182 d->collectionnames.append(name); 0183 } 0184 connectSignals(collection, true); 0185 emitUpdated(); 0186 } 0187 0188 void ActionCollection::unregisterCollection(const QString &name) 0189 { 0190 if (! d->collections.contains(name)) { 0191 return; 0192 } 0193 ActionCollection *collection = d->collections[name]; 0194 d->collectionnames.removeAll(name); 0195 d->collections.remove(name); 0196 connectSignals(collection, false); 0197 emitUpdated(); 0198 } 0199 0200 QList<Action *> ActionCollection::actions() const 0201 { 0202 return d->actionList; 0203 } 0204 0205 Action *ActionCollection::action(const QString &name) const 0206 { 0207 return d->actionMap.contains(name) ? d->actionMap[name] : nullptr; 0208 } 0209 0210 void ActionCollection::addAction(Action *action) 0211 { 0212 Q_ASSERT(action && ! action->objectName().isEmpty()); 0213 addAction(action->objectName(), action); 0214 } 0215 0216 void ActionCollection::addAction(const QString &name, Action *action) 0217 { 0218 Q_ASSERT(action && ! name.isEmpty()); 0219 emit actionToBeInserted(action, this); 0220 if (d->actionMap.contains(name)) { 0221 d->actionList.removeAll(d->actionMap[name]); 0222 } 0223 d->actionMap.insert(name, action); 0224 d->actionList.append(action); 0225 action->setParent(this); // in case it is not set 0226 connectSignals(action, true); 0227 emit actionInserted(action, this); 0228 emitUpdated(); 0229 } 0230 0231 void ActionCollection::removeAction(const QString &name) 0232 { 0233 if (! d->actionMap.contains(name)) { 0234 return; 0235 } 0236 Action *action = d->actionMap[name]; 0237 connectSignals(action, false); 0238 emit actionToBeRemoved(action, this); 0239 d->actionList.removeAll(action); 0240 d->actionMap.remove(name); 0241 //krossdebug( QString("ActionCollection::removeAction: %1 %2").arg(action->name()).arg(action->parent()->objectName()) ); 0242 action->setParent(nullptr); 0243 emit actionRemoved(action, this); 0244 emitUpdated(); 0245 } 0246 0247 void ActionCollection::removeAction(Action *action) 0248 { 0249 Q_ASSERT(action && ! action->objectName().isEmpty()); 0250 if (! d->actionMap.contains(action->objectName())) { 0251 Q_ASSERT(! d->actionList.contains(action)); 0252 return; 0253 } 0254 removeAction(action->objectName()); 0255 } 0256 0257 void ActionCollection::connectSignals(Action *action, bool conn) 0258 { 0259 if (conn) { 0260 connect(action, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*))); 0261 connect(action, SIGNAL(updated()), this, SLOT(emitUpdated())); 0262 } else { 0263 disconnect(action, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*))); 0264 disconnect(action, SIGNAL(updated()), this, SLOT(emitUpdated())); 0265 } 0266 } 0267 0268 void ActionCollection::connectSignals(ActionCollection *collection, bool conn) 0269 { 0270 if (conn) { 0271 connect(collection, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*))); 0272 connect(collection, SIGNAL(dataChanged(ActionCollection*)), this, SIGNAL(dataChanged(ActionCollection*))); 0273 0274 connect(collection, SIGNAL(collectionToBeInserted(ActionCollection*,ActionCollection*)), this, SIGNAL(collectionToBeInserted(ActionCollection*,ActionCollection*))); 0275 connect(collection, SIGNAL(collectionInserted(ActionCollection*,ActionCollection*)), this, SIGNAL(collectionInserted(ActionCollection*,ActionCollection*))); 0276 connect(collection, SIGNAL(collectionToBeRemoved(ActionCollection*,ActionCollection*)), this, SIGNAL(collectionToBeRemoved(ActionCollection*,ActionCollection*))); 0277 connect(collection, SIGNAL(collectionRemoved(ActionCollection*,ActionCollection*)), this, SIGNAL(collectionRemoved(ActionCollection*,ActionCollection*))); 0278 0279 connect(collection, SIGNAL(actionToBeInserted(Action*,ActionCollection*)), this, SIGNAL(actionToBeInserted(Action*,ActionCollection*))); 0280 connect(collection, SIGNAL(actionInserted(Action*,ActionCollection*)), this, SIGNAL(actionInserted(Action*,ActionCollection*))); 0281 connect(collection, SIGNAL(actionToBeRemoved(Action*,ActionCollection*)), this, SIGNAL(actionToBeRemoved(Action*,ActionCollection*))); 0282 connect(collection, SIGNAL(actionRemoved(Action*,ActionCollection*)), this, SIGNAL(actionRemoved(Action*,ActionCollection*))); 0283 connect(collection, SIGNAL(updated()), this, SLOT(emitUpdated())); 0284 } else { 0285 disconnect(collection, SIGNAL(dataChanged(ActionCollection*)), this, SIGNAL(dataChanged(ActionCollection*))); 0286 0287 disconnect(collection, SIGNAL(collectionToBeInserted(ActionCollection*,ActionCollection*)), this, SIGNAL(collectionToBeInserted(ActionCollection*,ActionCollection*))); 0288 disconnect(collection, SIGNAL(collectionInserted(ActionCollection*,ActionCollection*)), this, SIGNAL(collectionInserted(ActionCollection*,ActionCollection*))); 0289 disconnect(collection, SIGNAL(collectionToBeRemoved(ActionCollection*,ActionCollection*)), this, SIGNAL(collectionToBeRemoved(ActionCollection*,ActionCollection*))); 0290 disconnect(collection, SIGNAL(collectionRemoved(ActionCollection*,ActionCollection*)), this, SIGNAL(collectionRemoved(ActionCollection*,ActionCollection*))); 0291 0292 disconnect(collection, SIGNAL(actionToBeInserted(Action*,ActionCollection*)), this, SIGNAL(actionToBeInserted(Action*,ActionCollection*))); 0293 disconnect(collection, SIGNAL(actionInserted(Action*,ActionCollection*)), this, SIGNAL(actionInserted(Action*,ActionCollection*))); 0294 disconnect(collection, SIGNAL(actionToBeRemoved(Action*,ActionCollection*)), this, SIGNAL(actionToBeRemoved(Action*,ActionCollection*))); 0295 disconnect(collection, SIGNAL(actionRemoved(Action*,ActionCollection*)), this, SIGNAL(actionRemoved(Action*,ActionCollection*))); 0296 disconnect(collection, SIGNAL(updated()), this, SLOT(emitUpdated())); 0297 } 0298 } 0299 0300 void ActionCollection::emitUpdated() 0301 { 0302 if (!d->blockupdated) { 0303 emit updated(); 0304 } 0305 } 0306 0307 /********************************************************************* 0308 * Unserialize from XML / QIODevice / file / resource to child 0309 * ActionCollection's and Action's this ActionCollection has. 0310 */ 0311 0312 bool ActionCollection::readXml(const QDomElement &element, const QDir &directory) 0313 { 0314 return readXml(element, QStringList(directory.absolutePath())); 0315 } 0316 0317 bool ActionCollection::readXml(const QDomElement &element, const QStringList &searchPath) 0318 { 0319 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0320 qCDebug(KROSS_LOG) << "ActionCollection::readXml tagName=\"" << element.tagName() << "\""; 0321 #endif 0322 0323 d->blockupdated = true; // block updated() signals and emit it only once if everything is done 0324 bool ok = true; 0325 QDomNodeList list = element.childNodes(); 0326 const int size = list.size(); 0327 for (int i = 0; i < size; ++i) { 0328 QDomElement elem = list.item(i).toElement(); 0329 if (elem.isNull()) { 0330 continue; 0331 } 0332 0333 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0334 qCDebug(KROSS_LOG) << " ActionCollection::readXml child=" << 0335 i << " tagName=\"" << elem.tagName() << "\""; 0336 #endif 0337 0338 if (elem.tagName() == "collection") { 0339 const QString name = elem.attribute("name"); 0340 const QByteArray text = elem.attribute("text").toUtf8(); 0341 const QByteArray description = elem.attribute("comment").toUtf8(); 0342 const QString iconname = elem.attribute("icon"); 0343 bool enabled = QVariant(elem.attribute("enabled", "true")).toBool(); 0344 ActionCollection *c = d->collections.contains(name) ? d->collections[name] : QPointer<ActionCollection>(nullptr); 0345 if (! c) { 0346 c = new ActionCollection(name, this); 0347 } 0348 0349 c->setText(text.isEmpty() ? name : i18nd(KLocalizedString::applicationDomain().constData(), text.constData())); 0350 c->setDescription(description.isEmpty() ? c->text() : i18nd(KLocalizedString::applicationDomain().constData(), description.constData())); 0351 c->setIconName(iconname); 0352 0353 if (! enabled) { 0354 c->setEnabled(false); 0355 } 0356 if (! c->readXml(elem, searchPath)) { 0357 ok = false; 0358 } 0359 } else if (elem.tagName() == "script") { 0360 QString name = elem.attribute("name"); 0361 Action *a = dynamic_cast< Action * >(action(name)); 0362 if (a) { 0363 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0364 qCDebug(KROSS_LOG) << " ActionCollection::readXml Updating Action " << a->objectName(); 0365 #endif 0366 } else { 0367 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0368 qCDebug(KROSS_LOG) << " ActionCollection::readXml Creating Action " << name; 0369 #endif 0370 0371 a = new Action(this, name); 0372 addAction(name, a); 0373 connect(a, SIGNAL(started(Kross::Action*)), &Manager::self(), SIGNAL(started(Kross::Action*))); 0374 connect(a, SIGNAL(finished(Kross::Action*)), &Manager::self(), SIGNAL(finished(Kross::Action*))); 0375 } 0376 a->fromDomElement(elem, searchPath); 0377 } 0378 //else if( ! fromXml(elem) ) ok = false; 0379 } 0380 0381 d->blockupdated = false; // unblock signals 0382 emitUpdated(); 0383 return ok; 0384 } 0385 0386 bool ActionCollection::readXml(QIODevice *device, const QDir &directory) 0387 { 0388 return readXml(device, QStringList(directory.absolutePath())); 0389 } 0390 0391 bool ActionCollection::readXml(QIODevice *device, const QStringList &searchPath) 0392 { 0393 QString errMsg; 0394 int errLine, errCol; 0395 QDomDocument document; 0396 bool ok = document.setContent(device, false, &errMsg, &errLine, &errCol); 0397 if (! ok) { 0398 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0399 qCWarning(KROSS_LOG) << QStringLiteral("ActionCollection::readXml Error at line %1 in col %2: %3") 0400 .arg(errLine).arg(errCol).arg(errMsg); 0401 #endif 0402 return false; 0403 } 0404 return readXml(document.documentElement(), searchPath); 0405 } 0406 0407 bool ActionCollection::readXmlFile(const QString &file) 0408 { 0409 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0410 qCDebug(KROSS_LOG) << "ActionCollection::readXmlFile file=" << file; 0411 #endif 0412 0413 QFile f(file); 0414 if (! f.open(QIODevice::ReadOnly)) { 0415 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0416 qCWarning(KROSS_LOG) << "ActionCollection::readXmlFile failed to read file " << file; 0417 #endif 0418 return false; 0419 } 0420 bool ok = readXml(&f, QFileInfo(file).dir()); 0421 f.close(); 0422 0423 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0424 if (! ok) { 0425 qCWarning(KROSS_LOG) << "ActionCollection::readXmlFile failed to parse XML content of file " << file; 0426 } 0427 #endif 0428 return ok; 0429 } 0430 0431 /********************************************************************* 0432 * Serialize from child ActionCollection's and Action's this 0433 * ActionCollection has to XML / QIODevice / file / resource. 0434 */ 0435 0436 QDomElement ActionCollection::writeXml() 0437 { 0438 return writeXml(QStringList()); 0439 } 0440 0441 QDomElement ActionCollection::writeXml(const QStringList &searchPath) 0442 { 0443 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0444 qCDebug(KROSS_LOG) << "ActionCollection::writeXml collection.objectName=" << objectName(); 0445 #endif 0446 0447 QDomDocument document; 0448 QDomElement element = document.createElement("collection"); 0449 if (! objectName().isNull()) { 0450 element.setAttribute("name", objectName()); 0451 } 0452 if (! text().isNull() && text() != objectName()) { 0453 element.setAttribute("text", text()); 0454 } 0455 if (! d->description.isNull()) { 0456 element.setAttribute("comment", d->description); 0457 } 0458 if (! d->iconname.isNull()) { 0459 element.setAttribute("icon", d->iconname); 0460 } 0461 if (! d->enabled) { 0462 element.setAttribute("enabled", d->enabled); 0463 } 0464 0465 foreach (Action *a, actions()) { 0466 Q_ASSERT(a); 0467 #ifdef KROSS_ACTIONCOLLECTION_DEBUG 0468 qCDebug(KROSS_LOG) << " ActionCollection::writeXml action.objectName=" << 0469 a->objectName() << " action.file=" << a->file(); 0470 #endif 0471 QDomElement e = a->toDomElement(searchPath); 0472 if (! e.isNull()) { 0473 element.appendChild(e); 0474 } 0475 } 0476 0477 foreach (const QString &name, d->collectionnames) { 0478 ActionCollection *c = d->collections[name]; 0479 if (! c) { 0480 continue; 0481 } 0482 QDomElement e = c->writeXml(searchPath); 0483 if (! e.isNull()) { 0484 element.appendChild(e); 0485 } 0486 } 0487 0488 return element; 0489 } 0490 0491 bool ActionCollection::writeXml(QIODevice *device, int indent) 0492 { 0493 return writeXml(device, indent, QStringList()); 0494 } 0495 0496 bool ActionCollection::writeXml(QIODevice *device, int indent, const QStringList &searchPath) 0497 { 0498 QDomDocument document; 0499 QDomElement root = document.createElement("KrossScripting"); 0500 0501 foreach (Action *a, actions()) { 0502 QDomElement e = a->toDomElement(searchPath); 0503 if (! e.isNull()) { 0504 root.appendChild(e); 0505 } 0506 } 0507 0508 foreach (const QString &name, d->collectionnames) { 0509 ActionCollection *c = d->collections[name]; 0510 if (! c) { 0511 continue; 0512 } 0513 QDomElement e = c->writeXml(searchPath); 0514 if (! e.isNull()) { 0515 root.appendChild(e); 0516 } 0517 } 0518 0519 document.appendChild(root); 0520 return device->write(document.toByteArray(indent)) != -1; 0521 } 0522 0523 #include "moc_actioncollection.cpp"