00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "actioncollection.h"
00021 #include "action.h"
00022 #include "manager.h"
00023
00024 #include <QtCore/QHash>
00025 #include <QtCore/QStringList>
00026 #include <QtCore/QPointer>
00027 #include <QtCore/QIODevice>
00028 #include <QtCore/QFile>
00029 #include <QtCore/QFileInfo>
00030 #include <QtXml/QDomAttr>
00031
00032 #include <kicon.h>
00033 #include <klocalizedstring.h>
00034
00035 using namespace Kross;
00036
00037 namespace Kross {
00038
00040 class ActionCollection::Private
00041 {
00042 public:
00043 QPointer<ActionCollection> parent;
00044 QHash< QString, QPointer<ActionCollection> > collections;
00045 QStringList collectionnames;
00046
00047 QList< Action* > actionList;
00048 QHash< QString, Action* > actionMap;
00049
00050 QString text;
00051 QString description;
00052 QString iconname;
00053 bool enabled;
00054 bool blockupdated;
00055
00056 Private(ActionCollection* const p) : parent(p) {}
00057 };
00058
00059 }
00060
00061 ActionCollection::ActionCollection(const QString& name, ActionCollection* parent)
00062 : QObject(0)
00063 , d( new Private(0) )
00064 {
00065 setObjectName(name);
00066 d->text = name;
00067 d->enabled = true;
00068 d->blockupdated = false;
00069
00070 setParentCollection(parent);
00071 }
00072
00073 ActionCollection::~ActionCollection()
00074 {
00075 if ( d->parent ) {
00076 emit d->parent->collectionToBeRemoved(this, d->parent);
00077 d->parent->unregisterCollection( objectName() );
00078 emit d->parent->collectionRemoved( this, d->parent );
00079 }
00080 delete d;
00081 }
00082
00083 QString ActionCollection::name() const { return objectName(); }
00084
00085 QString ActionCollection::text() const { return d->text; }
00086 void ActionCollection::setText(const QString& text) { d->text = text; emit dataChanged(this); emitUpdated(); }
00087
00088 QString ActionCollection::description() const { return d->description; }
00089 void ActionCollection::setDescription(const QString& description) { d->description = description; emit dataChanged(this); emitUpdated(); }
00090
00091 QString ActionCollection::iconName() const { return d->iconname; }
00092 void ActionCollection::setIconName(const QString& iconname) { d->iconname = iconname; emit dataChanged(this); }
00093 QIcon ActionCollection::icon() const { return KIcon(d->iconname); }
00094
00095 bool ActionCollection::isEnabled() const { return d->enabled; }
00096 void ActionCollection::setEnabled(bool enabled) { d->enabled = enabled; emit dataChanged(this); emitUpdated(); }
00097
00098 ActionCollection* ActionCollection::parentCollection() const
00099 {
00100 return d->parent;
00101 }
00102
00103 void ActionCollection::setParentCollection( ActionCollection *parent )
00104 {
00105 if ( d->parent ) {
00106 emit d->parent->collectionToBeRemoved(this, d->parent);
00107 d->parent->unregisterCollection( objectName() );
00108 setParent( 0 );
00109 emit d->parent->collectionRemoved( this, d->parent );
00110 d->parent = 0;
00111 }
00112 setParent(0);
00113 if ( parent ) {
00114 emit parent->collectionToBeInserted(this, parent);
00115 setParent( parent );
00116 d->parent = parent;
00117 parent->registerCollection( this );
00118 emit parent->collectionInserted( this, parent );
00119 }
00120 emitUpdated();
00121 }
00122
00123 bool ActionCollection::hasCollection(const QString& name) const
00124 {
00125 return d->collections.contains(name);
00126 }
00127
00128 ActionCollection* ActionCollection::collection(const QString& name) const
00129 {
00130 return d->collections.contains(name) ? d->collections[name] : QPointer<ActionCollection>(0);
00131 }
00132
00133 QStringList ActionCollection::collections() const
00134 {
00135 return d->collectionnames;
00136 }
00137
00138 void ActionCollection::registerCollection(ActionCollection* collection)
00139 {
00140 Q_ASSERT(collection);
00141 const QString name = collection->objectName();
00142
00143 d->collections.insert(name, collection);
00144 d->collectionnames.append(name);
00145 connectSignals(collection, true);
00146 emitUpdated();
00147 }
00148
00149 void ActionCollection::unregisterCollection(const QString& name)
00150 {
00151 if( ! d->collections.contains(name) )
00152 return;
00153 ActionCollection* collection = d->collections[name];
00154 d->collectionnames.removeAll(name);
00155 d->collections.remove(name);
00156 connectSignals(collection, false);
00157 emitUpdated();
00158 }
00159
00160 QList<Action*> ActionCollection::actions() const
00161 {
00162 return d->actionList;
00163 }
00164
00165 Action* ActionCollection::action(const QString& name) const
00166 {
00167 return d->actionMap.contains(name) ? d->actionMap[name] : 0;
00168 }
00169
00170 void ActionCollection::addAction(Action* action)
00171 {
00172 Q_ASSERT( action && ! action->objectName().isEmpty() );
00173 addAction(action->objectName(), action);
00174 }
00175
00176 void ActionCollection::addAction(const QString& name, Action* action)
00177 {
00178 Q_ASSERT( action && ! name.isEmpty() );
00179 emit actionToBeInserted(action, this);
00180 if( d->actionMap.contains(name) )
00181 d->actionList.removeAll( d->actionMap[name] );
00182 d->actionMap.insert(name, action);
00183 d->actionList.append(action);
00184 action->setParent(this);
00185 connectSignals(action, true);
00186 emit actionInserted(action, this);
00187 emitUpdated();
00188 }
00189
00190 void ActionCollection::removeAction(const QString& name)
00191 {
00192 if( ! d->actionMap.contains(name) )
00193 return;
00194 Action* action = d->actionMap[name];
00195 connectSignals(action, false);
00196 emit actionToBeRemoved(action, this);
00197 d->actionList.removeAll(action);
00198 d->actionMap.remove(name);
00199
00200 action->setParent( 0 );
00201 emit actionRemoved(action, this);
00202 emitUpdated();
00203 }
00204
00205 void ActionCollection::removeAction(Action* action)
00206 {
00207 Q_ASSERT( action && ! action->objectName().isEmpty() );
00208 if( ! d->actionMap.contains(action->objectName()) ) {
00209 Q_ASSERT( ! d->actionList.contains(action) );
00210 return;
00211 }
00212 removeAction( action->objectName() );
00213 }
00214
00215 void ActionCollection::connectSignals(Action *action, bool conn)
00216 {
00217 if ( conn ) {
00218 connect(action, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
00219 connect(action, SIGNAL(updated()), this, SLOT(emitUpdated()));
00220 } else {
00221 disconnect(action, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
00222 disconnect(action, SIGNAL(updated()), this, SLOT(emitUpdated()));
00223 }
00224 }
00225
00226 void ActionCollection::connectSignals(ActionCollection *collection, bool conn)
00227 {
00228 if ( conn ) {
00229 connect(collection, SIGNAL(dataChanged(Action*)), this, SIGNAL(dataChanged(Action*)));
00230 connect(collection, SIGNAL(dataChanged(ActionCollection*)), this, SIGNAL(dataChanged(ActionCollection*)));
00231
00232 connect(collection, SIGNAL(collectionToBeInserted(ActionCollection*, ActionCollection*)), this, SIGNAL(collectionToBeInserted(ActionCollection*, ActionCollection*)));
00233 connect(collection, SIGNAL(collectionInserted(ActionCollection*, ActionCollection*)), this, SIGNAL(collectionInserted(ActionCollection*, ActionCollection*)));
00234 connect(collection, SIGNAL(collectionToBeRemoved(ActionCollection*, ActionCollection*)), this, SIGNAL(collectionToBeRemoved(ActionCollection*, ActionCollection*)));
00235 connect(collection, SIGNAL(collectionRemoved(ActionCollection*, ActionCollection*)), this, SIGNAL(collectionRemoved(ActionCollection*, ActionCollection*)));
00236
00237 connect(collection, SIGNAL(actionToBeInserted(Action*, ActionCollection*)), this, SIGNAL(actionToBeInserted(Action*, ActionCollection*)));
00238 connect(collection, SIGNAL(actionInserted(Action*, ActionCollection*)), this, SIGNAL(actionInserted(Action*, ActionCollection*)));
00239 connect(collection, SIGNAL(actionToBeRemoved(Action*, ActionCollection*)), this, SIGNAL(actionToBeRemoved(Action*, ActionCollection*)));
00240 connect(collection, SIGNAL(actionRemoved(Action*, ActionCollection*)), this, SIGNAL(actionRemoved(Action*, ActionCollection*)));
00241 connect(collection, SIGNAL(updated()), this, SLOT(emitUpdated()));
00242 } else {
00243 disconnect(collection, SIGNAL(dataChanged(ActionCollection*)), this, SIGNAL(dataChanged(ActionCollection*)));
00244
00245 disconnect(collection, SIGNAL(collectionToBeInserted(ActionCollection*, ActionCollection*)), this, SIGNAL(collectionToBeInserted(ActionCollection*, ActionCollection*)));
00246 disconnect(collection, SIGNAL(collectionInserted(ActionCollection*, ActionCollection*)), this, SIGNAL(collectionInserted(ActionCollection*, ActionCollection*)));
00247 disconnect(collection, SIGNAL(collectionToBeRemoved(ActionCollection*, ActionCollection*)), this, SIGNAL(collectionToBeRemoved(ActionCollection*, ActionCollection*)));
00248 disconnect(collection, SIGNAL(collectionRemoved(ActionCollection*, ActionCollection*)), this, SIGNAL(collectionRemoved(ActionCollection*, ActionCollection*)));
00249
00250 disconnect(collection, SIGNAL(actionToBeInserted(Action*, ActionCollection*)), this, SIGNAL(actionToBeInserted(Action*, ActionCollection*)));
00251 disconnect(collection, SIGNAL(actionInserted(Action*, ActionCollection*)), this, SIGNAL(actionInserted(Action*, ActionCollection*)));
00252 disconnect(collection, SIGNAL(actionToBeRemoved(Action*, ActionCollection*)), this, SIGNAL(actionToBeRemoved(Action*, ActionCollection*)));
00253 disconnect(collection, SIGNAL(actionRemoved(Action*, ActionCollection*)), this, SIGNAL(actionRemoved(Action*, ActionCollection*)));
00254 disconnect(collection, SIGNAL(updated()), this, SLOT(emitUpdated()));
00255 }
00256 }
00257
00258 void ActionCollection::emitUpdated()
00259 {
00260 if (!d->blockupdated) emit updated();
00261 }
00262
00263
00264
00265
00266
00267
00268 bool ActionCollection::readXml(const QDomElement& element, const QDir& directory)
00269 {
00270 return readXml(element, QStringList(directory.absolutePath()));
00271 }
00272
00273 bool ActionCollection::readXml(const QDomElement& element, const QStringList& searchPath)
00274 {
00275 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00276 krossdebug( QString("ActionCollection::readXml tagName=\"%1\"").arg(element.tagName()) );
00277 #endif
00278
00279 d->blockupdated = true;
00280 bool ok = true;
00281 QDomNodeList list = element.childNodes();
00282 const int size = list.size();
00283 for(int i = 0; i < size; ++i) {
00284 QDomElement elem = list.item(i).toElement();
00285 if( elem.isNull() ) continue;
00286
00287 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00288 krossdebug( QString(" ActionCollection::readXml child=%1 tagName=\"%2\"").arg(i).arg(elem.tagName()) );
00289 #endif
00290
00291 if( elem.tagName() == "collection") {
00292 const QString name = elem.attribute("name");
00293 const QByteArray text = elem.attribute("text").toUtf8();
00294 const QByteArray description = elem.attribute("comment").toUtf8();
00295 const QString iconname = elem.attribute("icon");
00296 bool enabled = QVariant(elem.attribute("enabled","true")).toBool();
00297 ActionCollection* c = d->collections.contains(name) ? d->collections[name] : QPointer<ActionCollection>(0);
00298 if( ! c )
00299 c = new ActionCollection(name, this);
00300
00301 c->setText( text.isEmpty() ? name : i18n( text ) );
00302 c->setDescription( description.isEmpty() ? c->text() : i18n( description ) );
00303 c->setIconName( iconname );
00304
00305 if( ! enabled )
00306 c->setEnabled(false);
00307 if( ! c->readXml(elem, searchPath) )
00308 ok = false;
00309 }
00310 else if( elem.tagName() == "script") {
00311 QString name = elem.attribute("name");
00312 Action* a = dynamic_cast< Action* >( action(name) );
00313 if( a ) {
00314 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00315 krossdebug( QString(" ActionCollection::readXml Updating Action \"%1\"").arg(a->objectName()) );
00316 #endif
00317 }
00318 else {
00319 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00320 krossdebug( QString(" ActionCollection::readXml Creating Action \"%1\"").arg(name) );
00321 #endif
00322
00323 a = new Action(this, name);
00324 addAction(name, a);
00325 connect(a, SIGNAL( started(Kross::Action*) ), &Manager::self(), SIGNAL( started(Kross::Action*)) );
00326 connect(a, SIGNAL( finished(Kross::Action*) ), &Manager::self(), SIGNAL( finished(Kross::Action*) ));
00327 }
00328 a->fromDomElement(elem, searchPath);
00329 }
00330
00331 }
00332
00333 d->blockupdated = false;
00334 emitUpdated();
00335 return ok;
00336 }
00337
00338 bool ActionCollection::readXml(QIODevice* device, const QDir& directory)
00339 {
00340 return readXml(device, QStringList(directory.absolutePath()));
00341 }
00342
00343 bool ActionCollection::readXml(QIODevice* device, const QStringList& searchPath)
00344 {
00345 QString errMsg;
00346 int errLine, errCol;
00347 QDomDocument document;
00348 bool ok = document.setContent(device, false, &errMsg, &errLine, &errCol);
00349 if( ! ok ) {
00350 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00351 krosswarning( QString("ActionCollection::readXml Error at line %1 in col %2: %3").arg(errLine).arg(errCol).arg(errMsg) );
00352 #endif
00353 return false;
00354 }
00355 return readXml(document.documentElement(), searchPath);
00356 }
00357
00358 bool ActionCollection::readXmlFile(const QString& file)
00359 {
00360 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00361 krossdebug( QString("ActionCollection::readXmlFile file=\"%1\"").arg(file) );
00362 #endif
00363
00364 QFile f(file);
00365 if( ! f.open(QIODevice::ReadOnly) ) {
00366 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00367 krosswarning( QString("ActionCollection::readXmlFile reading file \"%1\" failed.").arg(file) );
00368 #endif
00369 return false;
00370 }
00371 bool ok = readXml(&f, QFileInfo(file).dir());
00372 f.close();
00373
00374 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00375 if( ! ok )
00376 krosswarning( QString("ActionCollection::readXmlFile parsing XML content of file \"%1\" failed.").arg(file) );
00377 #endif
00378 return ok;
00379 }
00380
00381
00382
00383
00384
00385
00386 QDomElement ActionCollection::writeXml()
00387 {
00388 return writeXml(QStringList());
00389 }
00390
00391 QDomElement ActionCollection::writeXml(const QStringList& searchPath)
00392 {
00393 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00394 krossdebug( QString("ActionCollection::writeXml collection.objectName=\"%1\"").arg(objectName()) );
00395 #endif
00396
00397 QDomDocument document;
00398 QDomElement element = document.createElement("collection");
00399 if( ! objectName().isNull() )
00400 element.setAttribute("name", objectName());
00401 if( ! text().isNull() && text() != objectName() )
00402 element.setAttribute("text", text());
00403 if( ! d->description.isNull() )
00404 element.setAttribute("comment", d->description);
00405 if( ! d->iconname.isNull() )
00406 element.setAttribute("icon", d->iconname);
00407 if( ! d->enabled )
00408 element.setAttribute("enabled", d->enabled);
00409
00410 foreach(Action* a, actions()) {
00411 Q_ASSERT(a);
00412 #ifdef KROSS_ACTIONCOLLECTION_DEBUG
00413 krossdebug( QString(" ActionCollection::writeXml action.objectName=\"%1\" action.file=\"%2\"").arg(a->objectName()).arg(a->file()) );
00414 #endif
00415 QDomElement e = a->toDomElement(searchPath);
00416 if( ! e.isNull() )
00417 element.appendChild(e);
00418 }
00419
00420 foreach(const QString &name, d->collectionnames) {
00421 ActionCollection* c = d->collections[name];
00422 if( ! c ) continue;
00423 QDomElement e = c->writeXml(searchPath);
00424 if( ! e.isNull() )
00425 element.appendChild(e);
00426 }
00427
00428 return element;
00429 }
00430
00431 bool ActionCollection::writeXml(QIODevice* device, int indent)
00432 {
00433 return writeXml(device, indent, QStringList());
00434 }
00435
00436 bool ActionCollection::writeXml(QIODevice* device, int indent, const QStringList& searchPath)
00437 {
00438 QDomDocument document;
00439 QDomElement root = document.createElement("KrossScripting");
00440
00441 foreach(Action* a, actions()) {
00442 QDomElement e = a->toDomElement(searchPath);
00443 if( ! e.isNull() )
00444 root.appendChild(e);
00445 }
00446
00447 foreach(const QString &name, d->collectionnames) {
00448 ActionCollection* c = d->collections[name];
00449 if( ! c ) continue;
00450 QDomElement e = c->writeXml(searchPath);
00451 if( ! e.isNull() )
00452 root.appendChild(e);
00453 }
00454
00455 document.appendChild(root);
00456 return device->write( document.toByteArray(indent) ) != -1;
00457 }
00458
00459 #include "actioncollection.moc"