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, 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 QString errorMsg;
00231 int errorLine, errorColumn;
00232
00233
00234 bool result = document.isEmpty() || doc.setContent(document, &errorMsg, &errorLine, &errorColumn);
00235 if ( result ) {
00236 setDOMDocument( doc, merge );
00237 } else {
00238 #ifdef NDEBUG
00239 kError() << "Error parsing XML document:" << errorMsg << "at line" << errorLine << "column" << errorColumn;
00240 #else
00241 kFatal() << "Error parsing XML document:" << errorMsg << "at line" << errorLine << "column" << errorColumn;
00242 #endif
00243 }
00244 }
00245
00246 void KXMLGUIClient::setDOMDocument( const QDomDocument &document, bool merge )
00247 {
00248 if ( merge && !d->m_doc.isNull() )
00249 {
00250 QDomElement base = d->m_doc.documentElement();
00251
00252 QDomElement e = document.documentElement();
00253
00254
00255 d->mergeXML(base, e, actionCollection());
00256
00257
00258
00259 base = d->m_doc.documentElement();
00260
00261
00262
00263
00264 if ( base.isNull() )
00265 d->m_doc = document;
00266 }
00267 else
00268 {
00269 d->m_doc = document;
00270 }
00271
00272 setXMLGUIBuildDocument( QDomDocument() );
00273 }
00274
00275 bool KXMLGUIClientPrivate::mergeXML( QDomElement &base, QDomElement &additive, KActionCollection *actionCollection )
00276 {
00277 static const QString &tagAction = KGlobal::staticQString( "Action" );
00278 static const QString &tagMerge = KGlobal::staticQString( "Merge" );
00279 static const QString &tagSeparator = KGlobal::staticQString( "Separator" );
00280 static const QString &attrName = KGlobal::staticQString( "name" );
00281 static const QString &attrAppend = KGlobal::staticQString( "append" );
00282 static const QString &attrWeakSeparator = KGlobal::staticQString( "weakSeparator" );
00283 static const QString &tagMergeLocal = KGlobal::staticQString( "MergeLocal" );
00284 static const QString &tagText = KGlobal::staticQString( "text" );
00285 static const QString &attrAlreadyVisited = KGlobal::staticQString( "alreadyVisited" );
00286 static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" );
00287 static const QString &attrOne = KGlobal::staticQString( "1" );
00288
00289
00290
00291
00292
00293
00294 if ( additive.attribute(attrNoMerge) == attrOne )
00295 {
00296 base.parentNode().replaceChild(additive, base);
00297 base = additive;
00298 } else {
00299 QString tag;
00300
00301
00302 QDomNode n = base.firstChild();
00303 while ( !n.isNull() )
00304 {
00305 QDomElement e = n.toElement();
00306 n = n.nextSibling();
00307 if (e.isNull())
00308 continue;
00309
00310 tag = e.tagName();
00311
00312
00313
00314 if ( tag == tagAction )
00315 {
00316 const QString name = e.attribute(attrName);
00317 if (!actionCollection->action(name) ||
00318 !KAuthorized::authorizeKAction(name))
00319 {
00320
00321 base.removeChild( e );
00322 continue;
00323 }
00324 }
00325
00326
00327
00328 else if ( tag == tagSeparator )
00329 {
00330 e.setAttribute( attrWeakSeparator, (uint)1 );
00331
00332
00333
00334
00335 QDomElement prev = e.previousSibling().toElement();
00336 if ( prev.isNull() ||
00337 ( prev.tagName() == tagSeparator && !prev.attribute( attrWeakSeparator ).isNull() ) ||
00338 ( prev.tagName() == tagText ) )
00339 {
00340
00341 base.removeChild( e );
00342 continue;
00343 }
00344 }
00345
00346
00347
00348
00349 else if ( tag == tagMergeLocal )
00350 {
00351 QDomNode it = additive.firstChild();
00352 while ( !it.isNull() )
00353 {
00354 QDomElement newChild = it.toElement();
00355 it = it.nextSibling();
00356 if (newChild.isNull() )
00357 continue;
00358
00359 if ( newChild.tagName() == tagText )
00360 continue;
00361
00362 if ( newChild.attribute( attrAlreadyVisited ) == attrOne )
00363 continue;
00364
00365 QString itAppend( newChild.attribute( attrAppend ) );
00366 QString elemName( e.attribute( attrName ) );
00367
00368 if ( ( itAppend.isNull() && elemName.isEmpty() ) ||
00369 ( itAppend == elemName ) )
00370 {
00371
00372
00373
00374 QDomElement matchingElement = findMatchingElement( newChild, base );
00375 if ( matchingElement.isNull() || newChild.tagName() == tagSeparator )
00376 base.insertBefore( newChild, e );
00377 }
00378 }
00379
00380 base.removeChild( e );
00381 continue;
00382 }
00383
00384
00385
00386
00387
00388 else if ( tag != tagMerge )
00389 {
00390
00391 if ( tag == tagText )
00392 continue;
00393
00394 QDomElement matchingElement = findMatchingElement( e, additive );
00395
00396 if ( !matchingElement.isNull() )
00397 {
00398 matchingElement.setAttribute( attrAlreadyVisited, (uint)1 );
00399
00400 if ( mergeXML( e, matchingElement, actionCollection ) )
00401 {
00402 base.removeChild( e );
00403 additive.removeChild(matchingElement);
00404 continue;
00405 }
00406
00407
00408 const QDomNamedNodeMap attribs = matchingElement.attributes();
00409 const uint attribcount = attribs.count();
00410
00411 for(uint i = 0; i < attribcount; ++i)
00412 {
00413 const QDomNode node = attribs.item(i);
00414 e.setAttribute(node.nodeName(), node.nodeValue());
00415 }
00416
00417 continue;
00418 }
00419 else
00420 {
00421
00422
00423
00424
00425
00426 QDomElement dummy;
00427 if ( mergeXML( e, dummy, actionCollection ) )
00428 base.removeChild( e );
00429 continue;
00430 }
00431 }
00432 }
00433
00434
00435
00436 n = additive.firstChild();
00437 while ( !n.isNull() )
00438 {
00439 QDomElement e = n.toElement();
00440 n = n.nextSibling();
00441 if (e.isNull())
00442 continue;
00443
00444 QDomElement matchingElement = findMatchingElement( e, base );
00445
00446 if ( matchingElement.isNull() )
00447 {
00448 base.appendChild( e );
00449 }
00450 }
00451
00452
00453
00454 QDomElement last = base.lastChild().toElement();
00455 if ( (last.tagName() == tagSeparator) && (!last.attribute( attrWeakSeparator ).isNull()) )
00456 {
00457 base.removeChild( last );
00458 }
00459 }
00460
00461
00462
00463
00464 bool deleteMe = true;
00465
00466 QDomNode n = base.firstChild();
00467 while ( !n.isNull() )
00468 {
00469 QDomElement e = n.toElement();
00470 n = n.nextSibling();
00471 if (e.isNull())
00472 continue;
00473
00474 const QString tag = e.tagName();
00475
00476 if ( tag == tagAction )
00477 {
00478
00479
00480
00481 if (actionCollection->action(e.attribute(attrName))) {
00482 deleteMe = false;
00483 break;
00484 }
00485 }
00486 else if ( tag == tagSeparator )
00487 {
00488
00489
00490
00491 QString weakAttr = e.attribute( attrWeakSeparator );
00492 if ( weakAttr.isEmpty() || weakAttr.toInt() != 1 )
00493 {
00494 deleteMe = false;
00495 break;
00496 }
00497 }
00498
00499 else if ( tag == tagMerge )
00500 {
00501 continue;
00502 }
00503
00504
00505 else if ( tag == tagText )
00506 {
00507 continue;
00508 }
00509
00510
00511
00512
00513
00514 else
00515 {
00516 deleteMe = false;
00517 break;
00518 }
00519 }
00520
00521 return deleteMe;
00522 }
00523
00524 QDomElement KXMLGUIClientPrivate::findMatchingElement( const QDomElement &base, const QDomElement &additive )
00525 {
00526 static const QString &tagAction = KGlobal::staticQString( "Action" );
00527 static const QString &tagMergeLocal = KGlobal::staticQString( "MergeLocal" );
00528 static const QString &attrName = KGlobal::staticQString( "name" );
00529
00530 QDomNode n = additive.firstChild();
00531 while ( !n.isNull() )
00532 {
00533 QDomElement e = n.toElement();
00534 n = n.nextSibling();
00535 if (e.isNull())
00536 continue;
00537
00538
00539 if ( ( e.tagName() == tagAction ) || ( e.tagName() == tagMergeLocal ) )
00540 {
00541 continue;
00542 }
00543
00544
00545 if ( ( e.tagName() == base.tagName() ) &&
00546 ( e.attribute( attrName ) == base.attribute( attrName ) ) )
00547 {
00548 return e;
00549 }
00550 }
00551
00552
00553 return QDomElement();
00554 }
00555
00556 void KXMLGUIClient::setXMLGUIBuildDocument( const QDomDocument &doc )
00557 {
00558 d->m_buildDocument = doc;
00559 }
00560
00561 QDomDocument KXMLGUIClient::xmlguiBuildDocument() const
00562 {
00563 return d->m_buildDocument;
00564 }
00565
00566 void KXMLGUIClient::setFactory( KXMLGUIFactory *factory )
00567 {
00568 d->m_factory = factory;
00569 }
00570
00571 KXMLGUIFactory *KXMLGUIClient::factory() const
00572 {
00573 return d->m_factory;
00574 }
00575
00576 KXMLGUIClient *KXMLGUIClient::parentClient() const
00577 {
00578 return d->m_parent;
00579 }
00580
00581 void KXMLGUIClient::insertChildClient( KXMLGUIClient *child )
00582 {
00583 if ( child->d->m_parent )
00584 child->d->m_parent->removeChildClient( child );
00585 d->m_children.append( child );
00586 child->d->m_parent = this;
00587 }
00588
00589 void KXMLGUIClient::removeChildClient( KXMLGUIClient *child )
00590 {
00591 assert( d->m_children.contains( child ) );
00592 d->m_children.removeAll( child );
00593 child->d->m_parent = 0;
00594 }
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604 QList<KXMLGUIClient*> KXMLGUIClient::childClients()
00605 {
00606 return d->m_children;
00607 }
00608
00609 void KXMLGUIClient::setClientBuilder( KXMLGUIBuilder *builder )
00610 {
00611 d->m_builder = builder;
00612 if ( builder )
00613 builder->setBuilderComponentData( componentData() );
00614 }
00615
00616 KXMLGUIBuilder *KXMLGUIClient::clientBuilder() const
00617 {
00618 return d->m_builder;
00619 }
00620
00621 void KXMLGUIClient::plugActionList( const QString &name, const QList<QAction*> &actionList )
00622 {
00623 if ( !d->m_factory )
00624 return;
00625
00626 d->m_factory->plugActionList( this, name, actionList );
00627 }
00628
00629 void KXMLGUIClient::unplugActionList( const QString &name )
00630 {
00631 if ( !d->m_factory )
00632 return;
00633
00634 d->m_factory->unplugActionList( this, name );
00635 }
00636
00637 QString KXMLGUIClient::findMostRecentXMLFile( const QStringList &files, QString &doc )
00638 {
00639 KXmlGuiVersionHandler versionHandler(files);
00640 doc = versionHandler.finalDocument();
00641 return versionHandler.finalFile();
00642 }
00643
00644 void KXMLGUIClient::addStateActionEnabled(const QString& state,
00645 const QString& action)
00646 {
00647 StateChange stateChange = getActionsToChangeForState(state);
00648
00649 stateChange.actionsToEnable.append( action );
00650
00651
00652 d->m_actionsStateMap.insert( state, stateChange );
00653 }
00654
00655
00656 void KXMLGUIClient::addStateActionDisabled(const QString& state,
00657 const QString& action)
00658 {
00659 StateChange stateChange = getActionsToChangeForState(state);
00660
00661 stateChange.actionsToDisable.append( action );
00662
00663
00664 d->m_actionsStateMap.insert( state, stateChange );
00665 }
00666
00667
00668 KXMLGUIClient::StateChange KXMLGUIClient::getActionsToChangeForState(const QString& state)
00669 {
00670 return d->m_actionsStateMap[state];
00671 }
00672
00673
00674 void KXMLGUIClient::stateChanged(const QString &newstate, KXMLGUIClient::ReverseStateChange reverse)
00675 {
00676 StateChange stateChange = getActionsToChangeForState(newstate);
00677
00678 bool setTrue = (reverse == StateNoReverse);
00679 bool setFalse = !setTrue;
00680
00681
00682
00683 for ( QStringList::const_iterator it = stateChange.actionsToEnable.constBegin();
00684 it != stateChange.actionsToEnable.constEnd(); ++it ) {
00685
00686 QAction *action = actionCollection()->action(qPrintable((*it)));
00687 if (action) action->setEnabled(setTrue);
00688 }
00689
00690
00691
00692 for ( QStringList::const_iterator it = stateChange.actionsToDisable.constBegin();
00693 it != stateChange.actionsToDisable.constEnd(); ++it ) {
00694
00695 QAction *action = actionCollection()->action(qPrintable((*it)));
00696 if (action) action->setEnabled(setFalse);
00697 }
00698
00699 }
00700
00701 void KXMLGUIClient::beginXMLPlug( QWidget* w )
00702 {
00703 actionCollection()->addAssociatedWidget( w );
00704 foreach (KXMLGUIClient* client, d->m_children)
00705 client->beginXMLPlug( w );
00706 }
00707
00708 void KXMLGUIClient::endXMLPlug()
00709 {
00710 }
00711
00712 void KXMLGUIClient::prepareXMLUnplug( QWidget * w )
00713 {
00714 actionCollection()->removeAssociatedWidget( w );
00715 foreach (KXMLGUIClient* client, d->m_children)
00716 client->prepareXMLUnplug( w );
00717 }
00718
00719 void KXMLGUIClient::virtual_hook( int, void* )
00720 { }