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