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