00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kxmlguiclient.h"
00021 #include "kxmlguiversionhandler_p.h"
00022 #include "kxmlguifactory.h"
00023 #include "kxmlguibuilder.h"
00024
00025 #include <QtCore/QDir>
00026 #include <QtCore/QFile>
00027 #include <QtXml/QDomDocument>
00028 #include <QtCore/QTextIStream>
00029 #include <QtCore/QRegExp>
00030 #include <QtCore/QPointer>
00031
00032 #include <kcomponentdata.h>
00033 #include <kstandarddirs.h>
00034 #include <kdebug.h>
00035 #include <kauthorized.h>
00036
00037 #include "kaction.h"
00038 #include "kactioncollection.h"
00039
00040 #include <assert.h>
00041
00042 class KXMLGUIClientPrivate
00043 {
00044 public:
00045 KXMLGUIClientPrivate()
00046 {
00047 m_componentData = KGlobal::mainComponent();
00048 m_parent = 0L;
00049 m_builder = 0L;
00050 m_actionCollection = 0;
00051 }
00052 ~KXMLGUIClientPrivate()
00053 {
00054 }
00055
00056 bool mergeXML( QDomElement &base, const QDomElement &additive,
00057 KActionCollection *actionCollection );
00058
00059 QDomElement findMatchingElement( const QDomElement &base,
00060 const QDomElement &additive );
00061
00062 KComponentData m_componentData;
00063
00064 QDomDocument m_doc;
00065 KActionCollection *m_actionCollection;
00066 QDomDocument m_buildDocument;
00067 QPointer<KXMLGUIFactory> m_factory;
00068 KXMLGUIClient *m_parent;
00069
00070 QList<KXMLGUIClient*> m_children;
00071 KXMLGUIBuilder *m_builder;
00072 QString m_xmlFile;
00073 QString m_localXMLFile;
00074
00075
00076 QMap<QString,KXMLGUIClient::StateChange> m_actionsStateMap;
00077 };
00078
00079
00080 KXMLGUIClient::KXMLGUIClient()
00081 : d( new KXMLGUIClientPrivate )
00082 {
00083 }
00084
00085 KXMLGUIClient::KXMLGUIClient( KXMLGUIClient *parent )
00086 : d( new KXMLGUIClientPrivate )
00087 {
00088 parent->insertChildClient( this );
00089 }
00090
00091 KXMLGUIClient::~KXMLGUIClient()
00092 {
00093 if ( d->m_parent )
00094 d->m_parent->removeChildClient( this );
00095
00096 foreach (KXMLGUIClient* client, d->m_children) {
00097 assert( client->d->m_parent == this );
00098 client->d->m_parent = 0;
00099 }
00100
00101 delete d->m_actionCollection;
00102 delete d;
00103 }
00104
00105 QAction *KXMLGUIClient::action( const char *name ) const
00106 {
00107 QAction* act = actionCollection()->action( name );
00108 if ( !act ) {
00109 foreach (KXMLGUIClient* client, d->m_children) {
00110 act = client->actionCollection()->action( name );
00111 if ( act )
00112 break;
00113 }
00114 }
00115 return act;
00116 }
00117
00118 KActionCollection *KXMLGUIClient::actionCollection() const
00119 {
00120 if ( !d->m_actionCollection )
00121 {
00122 d->m_actionCollection = new KActionCollection( this );
00123 d->m_actionCollection->setObjectName( "KXMLGUIClient-KActionCollection" );
00124 }
00125 return d->m_actionCollection;
00126 }
00127
00128 QAction *KXMLGUIClient::action( const QDomElement &element ) const
00129 {
00130 static const QString &attrName = KGlobal::staticQString( "name" );
00131 return actionCollection()->action( qPrintable(element.attribute( attrName )) );
00132 }
00133
00134 KComponentData KXMLGUIClient::componentData() const
00135 {
00136 return d->m_componentData;
00137 }
00138
00139 QDomDocument KXMLGUIClient::domDocument() const
00140 {
00141 return d->m_doc;
00142 }
00143
00144 QString KXMLGUIClient::xmlFile() const
00145 {
00146 return d->m_xmlFile;
00147 }
00148
00149 QString KXMLGUIClient::localXMLFile() const
00150 {
00151 if ( !d->m_localXMLFile.isEmpty() )
00152 return d->m_localXMLFile;
00153
00154 if ( !QDir::isRelativePath(d->m_xmlFile) )
00155 return QString();
00156
00157 return KStandardDirs::locateLocal( "data", componentData().componentName() + '/' + d->m_xmlFile );
00158 }
00159
00160
00161 void KXMLGUIClient::reloadXML()
00162 {
00163 QString file( xmlFile() );
00164 if ( !file.isEmpty() )
00165 setXMLFile( file );
00166 }
00167
00168 void KXMLGUIClient::setComponentData(const KComponentData &componentData)
00169 {
00170 d->m_componentData = componentData;
00171 actionCollection()->setComponentData( componentData );
00172 if ( d->m_builder )
00173 d->m_builder->setBuilderClient( this );
00174 }
00175
00176 void KXMLGUIClient::setXMLFile( const QString& _file, bool merge, bool setXMLDoc )
00177 {
00178
00179 if ( !_file.isNull() )
00180 d->m_xmlFile = _file;
00181
00182 if ( !setXMLDoc )
00183 return;
00184
00185 QString file = _file;
00186 if ( QDir::isRelativePath(file) )
00187 {
00188 QString doc;
00189
00190 const QString filter = componentData().componentName() + '/' + _file;
00191 const QStringList allFiles = componentData().dirs()->findAllResources("data", filter) +
00192 componentData().dirs()->findAllResources("data", _file);
00193
00194 if (!allFiles.isEmpty())
00195 file = findMostRecentXMLFile(allFiles, doc);
00196
00197 if ( file.isEmpty() )
00198 {
00199
00200
00201
00202
00203
00204
00205 if ( !_file.isEmpty() )
00206 kWarning() << "cannot find .rc file" << _file << "in" << filter;
00207
00208 setXML( QString(), true );
00209 return;
00210 }
00211 else if ( !doc.isEmpty() )
00212 {
00213 setXML( doc, merge );
00214 return;
00215 }
00216 }
00217
00218 QString xml = KXMLGUIFactory::readConfigFile( file );
00219 setXML( xml, merge );
00220 }
00221
00222 void KXMLGUIClient::setLocalXMLFile( const QString &file )
00223 {
00224 d->m_localXMLFile = file;
00225 }
00226
00227 void KXMLGUIClient::setXML( const QString &document, bool merge )
00228 {
00229 QDomDocument doc;
00230 doc.setContent( document );
00231 setDOMDocument( doc, merge );
00232 }
00233
00234 void KXMLGUIClient::setDOMDocument( const QDomDocument &document, bool merge )
00235 {
00236 Q_ASSERT(!document.isNull());
00237
00238 if ( merge && !d->m_doc.isNull() )
00239 {
00240 QDomElement base = d->m_doc.documentElement();
00241
00242 QDomElement e = document.documentElement();
00243
00244
00245 d->mergeXML(base, e, actionCollection());
00246
00247
00248
00249 base = d->m_doc.documentElement();
00250
00251
00252 if ( base.isNull() )
00253 d->m_doc = document;
00254 }
00255 else
00256 {
00257 d->m_doc = document;
00258 }
00259
00260 setXMLGUIBuildDocument( QDomDocument() );
00261 }
00262
00263 bool KXMLGUIClientPrivate::mergeXML( QDomElement &base, const QDomElement &additive, KActionCollection *actionCollection )
00264 {
00265 static const QString &tagAction = KGlobal::staticQString( "Action" );
00266 static const QString &tagMerge = KGlobal::staticQString( "Merge" );
00267 static const QString &tagSeparator = KGlobal::staticQString( "Separator" );
00268 static const QString &attrName = KGlobal::staticQString( "name" );
00269 static const QString &attrAppend = KGlobal::staticQString( "append" );
00270 static const QString &attrWeakSeparator = KGlobal::staticQString( "weakSeparator" );
00271 static const QString &tagMergeLocal = KGlobal::staticQString( "MergeLocal" );
00272 static const QString &tagText = KGlobal::staticQString( "text" );
00273 static const QString &attrAlreadyVisited = KGlobal::staticQString( "alreadyVisited" );
00274 static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" );
00275 static const QString &attrOne = KGlobal::staticQString( "1" );
00276
00277
00278
00279
00280
00281
00282 if ( additive.attribute(attrNoMerge) == attrOne )
00283 {
00284 base.parentNode().replaceChild(additive, base);
00285 return true;
00286 }
00287
00288 QString tag;
00289
00290
00291 QDomNode n = base.firstChild();
00292 while ( !n.isNull() )
00293 {
00294 QDomElement e = n.toElement();
00295 n = n.nextSibling();
00296 if (e.isNull())
00297 continue;
00298
00299 tag = e.tagName();
00300
00301
00302
00303 if ( tag == tagAction )
00304 {
00305 QByteArray name = e.attribute( attrName ).toUtf8();
00306 if ( !actionCollection->action( name.constData() ) ||
00307 (!KAuthorized::authorizeKAction(name)))
00308 {
00309
00310 base.removeChild( e );
00311 continue;
00312 }
00313 }
00314
00315
00316
00317 else if ( tag == tagSeparator )
00318 {
00319 e.setAttribute( attrWeakSeparator, (uint)1 );
00320
00321
00322
00323
00324 QDomElement prev = e.previousSibling().toElement();
00325 if ( prev.isNull() ||
00326 ( prev.tagName() == tagSeparator && !prev.attribute( attrWeakSeparator ).isNull() ) ||
00327 ( prev.tagName() == tagText ) )
00328 {
00329
00330 base.removeChild( e );
00331 continue;
00332 }
00333 }
00334
00335
00336
00337
00338 else if ( tag == tagMergeLocal )
00339 {
00340 QDomNode it = additive.firstChild();
00341 while ( !it.isNull() )
00342 {
00343 QDomElement newChild = it.toElement();
00344 it = it.nextSibling();
00345 if (newChild.isNull() )
00346 continue;
00347
00348 if ( newChild.tagName() == tagText )
00349 continue;
00350
00351 if ( newChild.attribute( attrAlreadyVisited ) == attrOne )
00352 continue;
00353
00354 QString itAppend( newChild.attribute( attrAppend ) );
00355 QString elemName( e.attribute( attrName ) );
00356
00357 if ( ( itAppend.isNull() && elemName.isEmpty() ) ||
00358 ( itAppend == elemName ) )
00359 {
00360
00361
00362
00363 QDomElement matchingElement = findMatchingElement( newChild, base );
00364 if ( matchingElement.isNull() || newChild.tagName() == tagSeparator )
00365 base.insertBefore( newChild, e );
00366 }
00367 }
00368
00369 base.removeChild( e );
00370 continue;
00371 }
00372
00373
00374
00375
00376
00377 else if ( tag != tagMerge )
00378 {
00379
00380 if ( tag == tagText )
00381 continue;
00382
00383 QDomElement matchingElement = findMatchingElement( e, additive );
00384
00385 if ( !matchingElement.isNull() )
00386 {
00387 matchingElement.setAttribute( attrAlreadyVisited, (uint)1 );
00388
00389 if ( mergeXML( e, matchingElement, actionCollection ) )
00390 {
00391 base.removeChild( e );
00392 continue;
00393 }
00394
00395
00396 const QDomNamedNodeMap attribs = matchingElement.attributes();
00397 const uint attribcount = attribs.count();
00398
00399 for(uint i = 0; i < attribcount; ++i)
00400 {
00401 const QDomNode node = attribs.item(i);
00402 e.setAttribute(node.nodeName(), node.nodeValue());
00403 }
00404
00405 continue;
00406 }
00407 else
00408 {
00409
00410
00411
00412
00413
00414 if ( mergeXML( e, QDomElement(), actionCollection ) )
00415 base.removeChild( e );
00416 continue;
00417 }
00418 }
00419 }
00420
00421
00422
00423 n = additive.firstChild();
00424 while ( !n.isNull() )
00425 {
00426 QDomElement e = n.toElement();
00427 n = n.nextSibling();
00428 if (e.isNull())
00429 continue;
00430
00431 QDomElement matchingElement = findMatchingElement( e, base );
00432
00433 if ( matchingElement.isNull() )
00434 {
00435 base.appendChild( e );
00436 }
00437 }
00438
00439
00440
00441 QDomElement last = base.lastChild().toElement();
00442 if ( (last.tagName() == tagSeparator) && (!last.attribute( attrWeakSeparator ).isNull()) )
00443 {
00444 base.removeChild( last );
00445 }
00446
00447
00448
00449
00450 bool deleteMe = true;
00451
00452 n = base.firstChild();
00453 while ( !n.isNull() )
00454 {
00455 QDomElement e = n.toElement();
00456 n = n.nextSibling();
00457 if (e.isNull())
00458 continue;
00459
00460 tag = e.tagName();
00461
00462 if ( tag == tagAction )
00463 {
00464
00465
00466
00467 if ( actionCollection->action( e.attribute( attrName ).toUtf8().constData() ) )
00468 {
00469 deleteMe = false;
00470 break;
00471 }
00472 }
00473 else if ( tag == tagSeparator )
00474 {
00475
00476
00477
00478 QString weakAttr = e.attribute( attrWeakSeparator );
00479 if ( weakAttr.isEmpty() || weakAttr.toInt() != 1 )
00480 {
00481 deleteMe = false;
00482 break;
00483 }
00484 }
00485
00486
00487 else if ( tag == tagMerge )
00488 {
00489
00490
00491 continue;
00492 }
00493
00494
00495 else if ( tag == tagText )
00496 {
00497 continue;
00498 }
00499
00500
00501
00502
00503
00504 else
00505 {
00506 deleteMe = false;
00507 break;
00508 }
00509 }
00510
00511 return deleteMe;
00512 }
00513
00514 QDomElement KXMLGUIClientPrivate::findMatchingElement( const QDomElement &base, const QDomElement &additive )
00515 {
00516 static const QString &tagAction = KGlobal::staticQString( "Action" );
00517 static const QString &tagMergeLocal = KGlobal::staticQString( "MergeLocal" );
00518 static const QString &attrName = KGlobal::staticQString( "name" );
00519
00520 QDomNode n = additive.firstChild();
00521 while ( !n.isNull() )
00522 {
00523 QDomElement e = n.toElement();
00524 n = n.nextSibling();
00525 if (e.isNull())
00526 continue;
00527
00528
00529 if ( ( e.tagName() == tagAction ) || ( e.tagName() == tagMergeLocal ) )
00530 {
00531 continue;
00532 }
00533
00534
00535 if ( ( e.tagName() == base.tagName() ) &&
00536 ( e.attribute( attrName ) == base.attribute( attrName ) ) )
00537 {
00538 return e;
00539 }
00540 }
00541
00542
00543 return QDomElement();
00544 }
00545
00546 void KXMLGUIClient::setXMLGUIBuildDocument( const QDomDocument &doc )
00547 {
00548 d->m_buildDocument = doc;
00549 }
00550
00551 QDomDocument KXMLGUIClient::xmlguiBuildDocument() const
00552 {
00553 return d->m_buildDocument;
00554 }
00555
00556 void KXMLGUIClient::setFactory( KXMLGUIFactory *factory )
00557 {
00558 d->m_factory = factory;
00559 }
00560
00561 KXMLGUIFactory *KXMLGUIClient::factory() const
00562 {
00563 return d->m_factory;
00564 }
00565
00566 KXMLGUIClient *KXMLGUIClient::parentClient() const
00567 {
00568 return d->m_parent;
00569 }
00570
00571 void KXMLGUIClient::insertChildClient( KXMLGUIClient *child )
00572 {
00573 if ( child->d->m_parent )
00574 child->d->m_parent->removeChildClient( child );
00575 d->m_children.append( child );
00576 child->d->m_parent = this;
00577 }
00578
00579 void KXMLGUIClient::removeChildClient( KXMLGUIClient *child )
00580 {
00581 assert( d->m_children.contains( child ) );
00582 d->m_children.removeAll( child );
00583 child->d->m_parent = 0;
00584 }
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594 QList<KXMLGUIClient*> KXMLGUIClient::childClients()
00595 {
00596 return d->m_children;
00597 }
00598
00599 void KXMLGUIClient::setClientBuilder( KXMLGUIBuilder *builder )
00600 {
00601 d->m_builder = builder;
00602 if ( builder )
00603 builder->setBuilderComponentData( componentData() );
00604 }
00605
00606 KXMLGUIBuilder *KXMLGUIClient::clientBuilder() const
00607 {
00608 return d->m_builder;
00609 }
00610
00611 void KXMLGUIClient::plugActionList( const QString &name, const QList<QAction*> &actionList )
00612 {
00613 if ( !d->m_factory )
00614 return;
00615
00616 d->m_factory->plugActionList( this, name, actionList );
00617 }
00618
00619 void KXMLGUIClient::unplugActionList( const QString &name )
00620 {
00621 if ( !d->m_factory )
00622 return;
00623
00624 d->m_factory->unplugActionList( this, name );
00625 }
00626
00627 QString KXMLGUIClient::findMostRecentXMLFile( const QStringList &files, QString &doc )
00628 {
00629 KXmlGuiVersionHandler versionHandler(files);
00630 doc = versionHandler.finalDocument();
00631 return versionHandler.finalFile();
00632 }
00633
00634 void KXMLGUIClient::addStateActionEnabled(const QString& state,
00635 const QString& action)
00636 {
00637 StateChange stateChange = getActionsToChangeForState(state);
00638
00639 stateChange.actionsToEnable.append( action );
00640
00641
00642 d->m_actionsStateMap.insert( state, stateChange );
00643 }
00644
00645
00646 void KXMLGUIClient::addStateActionDisabled(const QString& state,
00647 const QString& action)
00648 {
00649 StateChange stateChange = getActionsToChangeForState(state);
00650
00651 stateChange.actionsToDisable.append( action );
00652
00653
00654 d->m_actionsStateMap.insert( state, stateChange );
00655 }
00656
00657
00658 KXMLGUIClient::StateChange KXMLGUIClient::getActionsToChangeForState(const QString& state)
00659 {
00660 return d->m_actionsStateMap[state];
00661 }
00662
00663
00664 void KXMLGUIClient::stateChanged(const QString &newstate, KXMLGUIClient::ReverseStateChange reverse)
00665 {
00666 StateChange stateChange = getActionsToChangeForState(newstate);
00667
00668 bool setTrue = (reverse == StateNoReverse);
00669 bool setFalse = !setTrue;
00670
00671
00672
00673 for ( QStringList::Iterator it = stateChange.actionsToEnable.begin();
00674 it != stateChange.actionsToEnable.end(); ++it ) {
00675
00676 QAction *action = actionCollection()->action(qPrintable((*it)));
00677 if (action) action->setEnabled(setTrue);
00678 }
00679
00680
00681
00682 for ( QStringList::Iterator it = stateChange.actionsToDisable.begin();
00683 it != stateChange.actionsToDisable.end(); ++it ) {
00684
00685 QAction *action = actionCollection()->action(qPrintable((*it)));
00686 if (action) action->setEnabled(setFalse);
00687 }
00688
00689 }
00690
00691 void KXMLGUIClient::beginXMLPlug( QWidget* w )
00692 {
00693 actionCollection()->addAssociatedWidget( w );
00694 foreach (KXMLGUIClient* client, d->m_children)
00695 client->beginXMLPlug( w );
00696 }
00697
00698 void KXMLGUIClient::endXMLPlug()
00699 {
00700 }
00701
00702 void KXMLGUIClient::prepareXMLUnplug( QWidget * w )
00703 {
00704 actionCollection()->removeAssociatedWidget( w );
00705 foreach (KXMLGUIClient* client, d->m_children)
00706 client->prepareXMLUnplug( w );
00707 }
00708
00709 void KXMLGUIClient::virtual_hook( int, void* )
00710 { }