00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kxmlguifactory.h"
00022 #include "kxmlguifactory_p.h"
00023 #include "kxmlguiclient.h"
00024 #include "kxmlguibuilder.h"
00025
00026 #include <assert.h>
00027
00028 #include <QtCore/QDir>
00029 #include <QtXml/QDomDocument>
00030 #include <QtCore/QFile>
00031 #include <QtCore/QTextIStream>
00032 #include <QtGui/QWidget>
00033 #include <QtCore/QDate>
00034 #include <QtCore/QVariant>
00035 #include <QTextCodec>
00036
00037 #include <kdebug.h>
00038 #include <kcomponentdata.h>
00039 #include <kglobal.h>
00040 #include <kshortcut.h>
00041 #include <kstandarddirs.h>
00042
00043 #include "kaction.h"
00044 #include "kshortcutsdialog.h"
00045 #include "kactioncollection.h"
00046
00047 using namespace KXMLGUI;
00048
00049 class KXMLGUIFactoryPrivate : public BuildState
00050 {
00051 public:
00052 KXMLGUIFactoryPrivate()
00053 {
00054 static const QString &defaultMergingName = KGlobal::staticQString( "<default>" );
00055 static const QString &actionList = KGlobal::staticQString( "actionlist" );
00056 static const QString &name = KGlobal::staticQString( "name" );
00057
00058 m_rootNode = new ContainerNode( 0L, QString(), 0L );
00059 m_defaultMergingName = defaultMergingName;
00060 tagActionList = actionList;
00061 attrName = name;
00062 }
00063 ~KXMLGUIFactoryPrivate()
00064 {
00065 delete m_rootNode;
00066 }
00067
00068 void pushState()
00069 {
00070 m_stateStack.push( *this );
00071 }
00072
00073 void popState()
00074 {
00075 BuildState::operator=( m_stateStack.pop() );
00076 }
00077
00078 QWidget *findRecursive( KXMLGUI::ContainerNode *node, bool tag );
00079 QList<QWidget*> findRecursive( KXMLGUI::ContainerNode *node, const QString &tagName );
00080 void applyActionProperties( const QDomElement &element );
00081 void configureAction( QAction *action, const QDomNamedNodeMap &attributes );
00082 void configureAction( QAction *action, const QDomAttr &attribute );
00083
00084 ContainerNode *m_rootNode;
00085
00086 QString m_defaultMergingName;
00087
00088
00089
00090
00091 QString m_containerName;
00092
00093
00094
00095
00096 QList<KXMLGUIClient*> m_clients;
00097
00098 QString tagActionList;
00099
00100 QString attrName;
00101
00102 BuildStateStack m_stateStack;
00103 };
00104
00105 QString KXMLGUIFactory::readConfigFile( const QString &filename, const KComponentData &_componentData )
00106 {
00107 KComponentData componentData = _componentData.isValid() ? _componentData : KGlobal::mainComponent();
00108 QString xml_file;
00109
00110 if (!QDir::isRelativePath(filename))
00111 xml_file = filename;
00112 else
00113 {
00114 xml_file = KStandardDirs::locate("data", componentData.componentName() + '/' + filename);
00115 if ( !QFile::exists( xml_file ) )
00116 xml_file = KStandardDirs::locate( "data", filename );
00117 }
00118
00119 QFile file( xml_file );
00120 if ( !file.open( QIODevice::ReadOnly ) )
00121 {
00122 kError(240) << "No such XML file " << filename << endl;
00123 return QString();
00124 }
00125
00126 QByteArray buffer(file.readAll());
00127 return QString::fromUtf8(buffer.constData(), buffer.size());
00128 }
00129
00130 bool KXMLGUIFactory::saveConfigFile( const QDomDocument& doc,
00131 const QString& filename, const KComponentData &_componentData )
00132 {
00133 KComponentData componentData = _componentData.isValid() ? _componentData : KGlobal::mainComponent();
00134 QString xml_file(filename);
00135
00136 if (QDir::isRelativePath(xml_file))
00137 xml_file = KStandardDirs::locateLocal("data", componentData.componentName() + '/' + filename);
00138
00139 QFile file( xml_file );
00140 if ( !file.open( QIODevice::WriteOnly ) )
00141 {
00142 kError(240) << "Could not write to " << filename << endl;
00143 return false;
00144 }
00145
00146
00147 QTextStream ts(&file);
00148 ts.setCodec( QTextCodec::codecForName( "UTF-8" ) );
00149 ts << doc;
00150
00151 file.close();
00152 return true;
00153 }
00154
00158 static void removeDOMComments( QDomNode &node )
00159 {
00160 QDomNode n = node.firstChild();
00161 while ( !n.isNull() )
00162 {
00163 if ( n.nodeType() == QDomNode::CommentNode )
00164 {
00165 QDomNode tmp = n;
00166 n = n.nextSibling();
00167 node.removeChild( tmp );
00168 }
00169 else
00170 {
00171 QDomNode tmp = n;
00172 n = n.nextSibling();
00173 removeDOMComments( tmp );
00174 }
00175 }
00176 }
00177
00178 KXMLGUIFactory::KXMLGUIFactory( KXMLGUIBuilder *builder, QObject *parent )
00179 : QObject( parent ),d(new KXMLGUIFactoryPrivate)
00180 {
00181 d->builder = builder;
00182 d->guiClient = 0;
00183 if ( d->builder )
00184 {
00185 d->builderContainerTags = d->builder->containerTags();
00186 d->builderCustomTags = d->builder->customTags();
00187 }
00188 }
00189
00190 KXMLGUIFactory::~KXMLGUIFactory()
00191 {
00192 delete d;
00193 }
00194
00195 void KXMLGUIFactory::addClient( KXMLGUIClient *client )
00196 {
00197
00198 static const QString &actionPropElementName = KGlobal::staticQString( "ActionProperties" );
00199
00200 if ( client->factory() ) {
00201 if ( client->factory() == this )
00202 return;
00203 else
00204 client->factory()->removeClient( client );
00205 }
00206
00207 d->pushState();
00208
00209
00210
00211 d->guiClient = client;
00212
00213
00214 if ( !d->m_clients.contains( client ) )
00215 d->m_clients.append( client );
00216 else
00217 kDebug(260) << "XMLGUI client already added " << client;
00218
00219
00220
00221
00222 client->beginXMLPlug( d->builder->widget() );
00223
00224
00225
00226
00227 QDomDocument doc = client->xmlguiBuildDocument();
00228 if ( doc.documentElement().isNull() )
00229 doc = client->domDocument();
00230
00231 QDomElement docElement = doc.documentElement();
00232
00233 d->m_rootNode->index = -1;
00234
00235
00236
00237 d->clientName = docElement.attribute( d->attrName );
00238 d->clientBuilder = client->clientBuilder();
00239
00240 if ( d->clientBuilder )
00241 {
00242 d->clientBuilderContainerTags = d->clientBuilder->containerTags();
00243 d->clientBuilderCustomTags = d->clientBuilder->customTags();
00244 }
00245 else
00246 {
00247 d->clientBuilderContainerTags.clear();
00248 d->clientBuilderCustomTags.clear();
00249 }
00250
00251
00252
00253 QDomElement actionPropElement = docElement.namedItem( actionPropElementName ).toElement();
00254 if ( actionPropElement.isNull() )
00255 actionPropElement = docElement.namedItem( actionPropElementName.toLower() ).toElement();
00256
00257 if ( !actionPropElement.isNull() )
00258 d->applyActionProperties( actionPropElement );
00259
00260 BuildHelper( *d, d->m_rootNode ).build( docElement );
00261
00262
00263 client->setFactory( this );
00264
00265
00266
00267
00268
00269 d->builder->finalizeGUI( d->guiClient );
00270
00271
00272 d->BuildState::reset();
00273
00274 client->endXMLPlug();
00275
00276 d->popState();
00277
00278 emit clientAdded( client );
00279
00280
00281 foreach (KXMLGUIClient *child, client->childClients())
00282 addClient( child );
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 }
00297
00298 void KXMLGUIFactory::removeClient( KXMLGUIClient *client )
00299 {
00300
00301
00302
00303 if ( !client || client->factory() != this )
00304 return;
00305
00306
00307 d->m_clients.removeAll( client );
00308
00309
00310
00311 const QList<KXMLGUIClient*> childClients(client->childClients());
00312 foreach (KXMLGUIClient *child, childClients)
00313 removeClient(child);
00314
00315
00316
00317 d->pushState();
00318
00319
00320
00321 d->guiClient = client;
00322 d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00323 d->clientBuilder = client->clientBuilder();
00324
00325 client->setFactory( 0L );
00326
00327
00328
00329
00330 QDomDocument doc = client->xmlguiBuildDocument();
00331 if ( doc.documentElement().isNull() )
00332 {
00333 doc = client->domDocument().cloneNode( true ).toDocument();
00334 client->setXMLGUIBuildDocument( doc );
00335 }
00336
00337 d->m_rootNode->destruct( doc.documentElement(), *d );
00338
00339
00340 d->BuildState::reset();
00341
00342
00343 client->prepareXMLUnplug( d->builder->widget() );
00344
00345 d->popState();
00346
00347 emit clientRemoved( client );
00348 }
00349
00350 QList<KXMLGUIClient*> KXMLGUIFactory::clients() const
00351 {
00352 return d->m_clients;
00353 }
00354
00355 QWidget *KXMLGUIFactory::container( const QString &containerName, KXMLGUIClient *client,
00356 bool useTagName )
00357 {
00358 d->pushState();
00359 d->m_containerName = containerName;
00360 d->guiClient = client;
00361
00362 QWidget *result = d->findRecursive( d->m_rootNode, useTagName );
00363
00364 d->guiClient = 0L;
00365 d->m_containerName.clear();
00366
00367 d->popState();
00368
00369 return result;
00370 }
00371
00372 QList<QWidget*> KXMLGUIFactory::containers( const QString &tagName )
00373 {
00374 return d->findRecursive( d->m_rootNode, tagName );
00375 }
00376
00377 void KXMLGUIFactory::reset()
00378 {
00379 d->m_rootNode->reset();
00380
00381 d->m_rootNode->clearChildren();
00382 }
00383
00384 void KXMLGUIFactory::resetContainer( const QString &containerName, bool useTagName )
00385 {
00386 if ( containerName.isEmpty() )
00387 return;
00388
00389 ContainerNode *container = d->m_rootNode->findContainer( containerName, useTagName );
00390
00391 if ( !container )
00392 return;
00393
00394 ContainerNode *parent = container->parent;
00395 if ( !parent )
00396 return;
00397
00398
00399
00400 parent->removeChild( container );
00401 }
00402
00403 QWidget *KXMLGUIFactoryPrivate::findRecursive( KXMLGUI::ContainerNode *node, bool tag )
00404 {
00405 if ( ( ( !tag && node->name == m_containerName ) ||
00406 ( tag && node->tagName == m_containerName ) ) &&
00407 ( !guiClient || node->client == guiClient ) )
00408 return node->container;
00409
00410 foreach (ContainerNode* child, node->children)
00411 {
00412 QWidget *cont = findRecursive( child, tag );
00413 if ( cont )
00414 return cont;
00415 }
00416
00417 return 0L;
00418 }
00419
00420
00421 static inline bool equals(const QString& str1, const char* str2)
00422 {
00423 return str1.compare(QLatin1String(str2), Qt::CaseInsensitive) == 0;
00424 }
00425 static inline bool equals(const QString& str1, const QString& str2)
00426 {
00427 return str1.compare(str2, Qt::CaseInsensitive) == 0;
00428 }
00429
00430
00431 QList<QWidget*> KXMLGUIFactoryPrivate::findRecursive( KXMLGUI::ContainerNode *node,
00432 const QString &tagName )
00433 {
00434 QList<QWidget*> res;
00435
00436 if ( equals(node->tagName, tagName) )
00437 res.append( node->container );
00438
00439 foreach (KXMLGUI::ContainerNode* child, node->children)
00440 res << findRecursive( child, tagName );
00441
00442 return res;
00443 }
00444
00445 void KXMLGUIFactory::plugActionList( KXMLGUIClient *client, const QString &name,
00446 const QList<QAction*> &actionList )
00447 {
00448 d->pushState();
00449 d->guiClient = client;
00450 d->actionListName = name;
00451 d->actionList = actionList;
00452 d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00453
00454 d->m_rootNode->plugActionList( *d );
00455
00456 d->BuildState::reset();
00457 d->popState();
00458 }
00459
00460 void KXMLGUIFactory::unplugActionList( KXMLGUIClient *client, const QString &name )
00461 {
00462 d->pushState();
00463 d->guiClient = client;
00464 d->actionListName = name;
00465 d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00466
00467 d->m_rootNode->unplugActionList( *d );
00468
00469 d->BuildState::reset();
00470 d->popState();
00471 }
00472
00473 void KXMLGUIFactoryPrivate::applyActionProperties( const QDomElement &actionPropElement )
00474 {
00475 for (QDomNode n = actionPropElement.firstChild();
00476 !n.isNull(); n = n.nextSibling() )
00477 {
00478 QDomElement e = n.toElement();
00479 if ( !equals(e.tagName(), "action") )
00480 continue;
00481
00482 QAction *action = guiClient->action( e );
00483 if ( !action )
00484 continue;
00485
00486 configureAction( action, e.attributes() );
00487 }
00488 }
00489
00490 void KXMLGUIFactoryPrivate::configureAction( QAction *action, const QDomNamedNodeMap &attributes )
00491 {
00492 for ( uint i = 0; i < attributes.length(); i++ )
00493 {
00494 QDomAttr attr = attributes.item( i ).toAttr();
00495 if ( attr.isNull() )
00496 continue;
00497
00498 configureAction( action, attr );
00499 }
00500 }
00501
00502 void KXMLGUIFactoryPrivate::configureAction( QAction *action, const QDomAttr &attribute )
00503 {
00504 static const QString &attrShortcut = KGlobal::staticQString( "shortcut" );
00505
00506 QString attrName = attribute.name();
00507
00508 if ( equals(attrName, "accel") )
00509 attrName = attrShortcut;
00510
00511
00512 if ( equals(attrName, "name") )
00513 return;
00514
00515 if ( equals(attrName, "icon") ) {
00516 action->setIcon( KIcon( attribute.value() ) );
00517 return;
00518 }
00519
00520 QVariant propertyValue;
00521
00522 QVariant::Type propertyType = action->property( attrName.toLatin1() ).type();
00523
00524 if ( propertyType == QVariant::Int ) {
00525 propertyValue = QVariant( attribute.value().toInt() );
00526 } else if ( propertyType == QVariant::UInt ) {
00527 propertyValue = QVariant( attribute.value().toUInt() );
00528 } else if ( propertyType == QVariant::UserType && action->property( attrName.toLatin1() ).userType() == qMetaTypeId<KShortcut>() ) {
00529
00530 if (KAction* ka = qobject_cast<KAction*>(action)) {
00531 if (attrName=="globalShortcut") {
00532 ka->setGlobalShortcut(KShortcut(attribute.value()), KAction::ActiveShortcut);
00533 } else {
00534 ka->setShortcut(KShortcut(attribute.value()), KAction::ActiveShortcut);
00535 }
00536 return;
00537 }
00538 propertyValue = KShortcut( attribute.value() );
00539 } else {
00540 propertyValue = QVariant( attribute.value() );
00541 }
00542 if (!action->setProperty( attrName.toLatin1(), propertyValue )) {
00543 kWarning() << "Error: Unknown action property " << attrName << " will be ignored!";
00544 }
00545 }
00546
00547
00548 int KXMLGUIFactory::configureShortcuts(bool letterCutsOk , bool bSaveSettings )
00549 {
00550 KShortcutsDialog dlg(KShortcutsEditor::AllActions,
00551 letterCutsOk ? KShortcutsEditor::LetterShortcutsAllowed : KShortcutsEditor::LetterShortcutsDisallowed,
00552 qobject_cast<QWidget*>(parent()));
00553 foreach (KXMLGUIClient *client, d->m_clients)
00554 {
00555 if(client && !client->xmlFile().isEmpty())
00556 dlg.addCollection( client->actionCollection() );
00557 }
00558 return dlg.configure(bSaveSettings);
00559 }
00560
00561 QDomElement KXMLGUIFactory::actionPropertiesElement( QDomDocument& doc )
00562 {
00563 const QString tagActionProp = QLatin1String("ActionProperties");
00564
00565 QDomElement elem;
00566 QDomNode it = doc.documentElement().firstChild();
00567 for( ; !it.isNull(); it = it.nextSibling() ) {
00568 QDomElement e = it.toElement();
00569 if( e.tagName() == tagActionProp ) {
00570 elem = e;
00571 break;
00572 }
00573 }
00574
00575
00576 if( elem.isNull() ) {
00577 elem = doc.createElement( tagActionProp );
00578 doc.documentElement().appendChild( elem );
00579 }
00580 return elem;
00581 }
00582
00583 QDomElement KXMLGUIFactory::findActionByName( QDomElement& elem, const QString& sName, bool create )
00584 {
00585 static const QString& attrName = KGlobal::staticQString( "name" );
00586 static const QString& tagAction = KGlobal::staticQString( "Action" );
00587 for( QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling() ) {
00588 QDomElement e = it.toElement();
00589 if( e.attribute( attrName ) == sName )
00590 return e;
00591 }
00592
00593 if( create ) {
00594 QDomElement act_elem = elem.ownerDocument().createElement( tagAction );
00595 act_elem.setAttribute( attrName, sName );
00596 elem.appendChild( act_elem );
00597 return act_elem;
00598 }
00599 return QDomElement();
00600 }
00601
00602 #include "kxmlguifactory.moc"
00603
00604
00605