KDEUI
kxmlguifactory.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1999,2000 Simon Hausmann <hausmann@kde.org> 00003 Copyright (C) 2000 Kurt Granroth <granroth@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 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 enum ShortcutOption { SetActiveShortcut = 1, SetDefaultShortcut = 2}; 00053 00054 KXMLGUIFactoryPrivate() 00055 { 00056 static const QString &defaultMergingName = KGlobal::staticQString( "<default>" ); 00057 static const QString &actionList = KGlobal::staticQString( "actionlist" ); 00058 static const QString &name = KGlobal::staticQString( "name" ); 00059 00060 m_rootNode = new ContainerNode( 0L, QString(), 0L ); 00061 m_defaultMergingName = defaultMergingName; 00062 tagActionList = actionList; 00063 attrName = name; 00064 } 00065 ~KXMLGUIFactoryPrivate() 00066 { 00067 delete m_rootNode; 00068 } 00069 00070 void pushState() 00071 { 00072 m_stateStack.push( *this ); 00073 } 00074 00075 void popState() 00076 { 00077 BuildState::operator=( m_stateStack.pop() ); 00078 } 00079 00080 bool emptyState() const { return m_stateStack.isEmpty(); } 00081 00082 QWidget *findRecursive( KXMLGUI::ContainerNode *node, bool tag ); 00083 QList<QWidget*> findRecursive( KXMLGUI::ContainerNode *node, const QString &tagName ); 00084 void applyActionProperties( const QDomElement &element, 00085 ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut ); 00086 void configureAction( QAction *action, const QDomNamedNodeMap &attributes, 00087 ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut ); 00088 void configureAction( QAction *action, const QDomAttr &attribute, 00089 ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut ); 00090 00091 QDomDocument shortcutSchemeDoc(KXMLGUIClient *client); 00092 void applyShortcutScheme(KXMLGUIClient *client, const QList<QAction*>& actions, const QDomDocument& scheme); 00093 void refreshActionProperties(KXMLGUIClient *client, const QList<QAction*>& actions, const QDomDocument& doc); 00094 void saveDefaultActionProperties(const QList<QAction*>& actions); 00095 00096 ContainerNode *m_rootNode; 00097 00098 QString m_defaultMergingName; 00099 00100 /* 00101 * Contains the container which is searched for in ::container . 00102 */ 00103 QString m_containerName; 00104 00105 /* 00106 * List of all clients 00107 */ 00108 QList<KXMLGUIClient*> m_clients; 00109 00110 QString tagActionList; 00111 00112 QString attrName; 00113 00114 BuildStateStack m_stateStack; 00115 }; 00116 00117 QString KXMLGUIFactory::readConfigFile( const QString &filename, const KComponentData &_componentData ) 00118 { 00119 QString xml_file; 00120 00121 if (!QDir::isRelativePath(filename)) 00122 xml_file = filename; 00123 else 00124 { 00125 KComponentData componentData = _componentData.isValid() ? _componentData : KGlobal::mainComponent(); 00126 xml_file = KStandardDirs::locate("data", componentData.componentName() + '/' + filename); 00127 if ( !QFile::exists( xml_file ) ) 00128 xml_file = KStandardDirs::locate( "data", filename ); 00129 } 00130 00131 QFile file( xml_file ); 00132 if ( xml_file.isEmpty() || !file.open( QIODevice::ReadOnly ) ) 00133 { 00134 kError(240) << "No such XML file" << filename; 00135 return QString(); 00136 } 00137 00138 QByteArray buffer(file.readAll()); 00139 return QString::fromUtf8(buffer.constData(), buffer.size()); 00140 } 00141 00142 bool KXMLGUIFactory::saveConfigFile( const QDomDocument& doc, 00143 const QString& filename, const KComponentData &_componentData ) 00144 { 00145 KComponentData componentData = _componentData.isValid() ? _componentData : KGlobal::mainComponent(); 00146 QString xml_file(filename); 00147 00148 if (QDir::isRelativePath(xml_file)) 00149 xml_file = KStandardDirs::locateLocal("data", componentData.componentName() + '/' + filename); 00150 00151 QFile file( xml_file ); 00152 if ( xml_file.isEmpty() || !file.open( QIODevice::WriteOnly ) ) 00153 { 00154 kError(240) << "Could not write to" << filename; 00155 return false; 00156 } 00157 00158 // write out our document 00159 QTextStream ts(&file); 00160 ts.setCodec( QTextCodec::codecForName( "UTF-8" ) ); 00161 ts << doc; 00162 00163 file.close(); 00164 return true; 00165 } 00166 00170 static void removeDOMComments( QDomNode &node ) 00171 { 00172 QDomNode n = node.firstChild(); 00173 while ( !n.isNull() ) 00174 { 00175 if ( n.nodeType() == QDomNode::CommentNode ) 00176 { 00177 QDomNode tmp = n; 00178 n = n.nextSibling(); 00179 node.removeChild( tmp ); 00180 } 00181 else 00182 { 00183 QDomNode tmp = n; 00184 n = n.nextSibling(); 00185 removeDOMComments( tmp ); 00186 } 00187 } 00188 } 00189 00190 KXMLGUIFactory::KXMLGUIFactory( KXMLGUIBuilder *builder, QObject *parent ) 00191 : QObject( parent ),d(new KXMLGUIFactoryPrivate) 00192 { 00193 d->builder = builder; 00194 d->guiClient = 0; 00195 if ( d->builder ) 00196 { 00197 d->builderContainerTags = d->builder->containerTags(); 00198 d->builderCustomTags = d->builder->customTags(); 00199 } 00200 } 00201 00202 KXMLGUIFactory::~KXMLGUIFactory() 00203 { 00204 foreach (KXMLGUIClient *client, d->m_clients) { 00205 client->setFactory ( 0L ); 00206 } 00207 delete d; 00208 } 00209 00210 void KXMLGUIFactory::addClient( KXMLGUIClient *client ) 00211 { 00212 //kDebug(260) << client; 00213 if ( client->factory() ) { 00214 if ( client->factory() == this ) 00215 return; 00216 else 00217 client->factory()->removeClient( client ); //just in case someone does stupid things ;-) 00218 } 00219 00220 if (d->emptyState()) 00221 emit makingChanges(true); 00222 d->pushState(); 00223 00224 // QTime dt; dt.start(); 00225 00226 d->guiClient = client; 00227 00228 // add this client to our client list 00229 if ( !d->m_clients.contains( client ) ) 00230 d->m_clients.append( client ); 00231 else 00232 kDebug(260) << "XMLGUI client already added " << client; 00233 00234 // Tell the client that plugging in is process and 00235 // let it know what builder widget its mainwindow shortcuts 00236 // should be attached to. 00237 client->beginXMLPlug( d->builder->widget() ); 00238 00239 // try to use the build document for building the client's GUI, as the build document 00240 // contains the correct container state information (like toolbar positions, sizes, etc.) . 00241 // if there is non available, then use the "real" document. 00242 QDomDocument doc = client->xmlguiBuildDocument(); 00243 if ( doc.documentElement().isNull() ) 00244 doc = client->domDocument(); 00245 00246 QDomElement docElement = doc.documentElement(); 00247 00248 d->m_rootNode->index = -1; 00249 00250 // cache some variables 00251 00252 d->clientName = docElement.attribute( d->attrName ); 00253 d->clientBuilder = client->clientBuilder(); 00254 00255 if ( d->clientBuilder ) 00256 { 00257 d->clientBuilderContainerTags = d->clientBuilder->containerTags(); 00258 d->clientBuilderCustomTags = d->clientBuilder->customTags(); 00259 } 00260 else 00261 { 00262 d->clientBuilderContainerTags.clear(); 00263 d->clientBuilderCustomTags.clear(); 00264 } 00265 00266 // load shortcut schemes, user-defined shortcuts and other action properties 00267 d->saveDefaultActionProperties(client->actionCollection()->actions()); 00268 if (!doc.isNull()) 00269 d->refreshActionProperties(client, client->actionCollection()->actions(), doc); 00270 00271 BuildHelper( *d, d->m_rootNode ).build( docElement ); 00272 00273 // let the client know that we built its GUI. 00274 client->setFactory( this ); 00275 00276 // call the finalizeGUI method, to fix up the positions of toolbars for example. 00277 // ### FIXME : obey client builder 00278 // --- Well, toolbars have a bool "positioned", so it doesn't really matter, 00279 // if we call positionYourself on all of them each time. (David) 00280 d->builder->finalizeGUI( d->guiClient ); 00281 00282 // reset some variables, for safety 00283 d->BuildState::reset(); 00284 00285 client->endXMLPlug(); 00286 00287 d->popState(); 00288 00289 emit clientAdded( client ); 00290 00291 // build child clients 00292 foreach (KXMLGUIClient *child, client->childClients()) 00293 addClient( child ); 00294 00295 if (d->emptyState()) 00296 emit makingChanges(false); 00297 /* 00298 QString unaddedActions; 00299 foreach (KActionCollection* ac, KActionCollection::allCollections()) 00300 foreach (QAction* action, ac->actions()) 00301 if (action->associatedWidgets().isEmpty()) 00302 unaddedActions += action->objectName() + ' '; 00303 00304 if (!unaddedActions.isEmpty()) 00305 kWarning() << "The following actions are not plugged into the gui (shortcuts will not work): " << unaddedActions; 00306 */ 00307 00308 // kDebug() << "addClient took " << dt.elapsed(); 00309 } 00310 00311 void KXMLGUIFactory::refreshActionProperties() 00312 { 00313 foreach (KXMLGUIClient *client, d->m_clients) 00314 { 00315 d->guiClient = client; 00316 QDomDocument doc = client->xmlguiBuildDocument(); 00317 if ( doc.documentElement().isNull() ) 00318 { 00319 client->reloadXML(); 00320 doc = client->domDocument(); 00321 } 00322 d->refreshActionProperties(client, client->actionCollection()->actions(), doc); 00323 } 00324 d->guiClient = 0; 00325 } 00326 00327 static QString currentShortcutScheme() 00328 { 00329 const KConfigGroup cg = KGlobal::config()->group("Shortcut Schemes"); 00330 return cg.readEntry("Current Scheme", "Default"); 00331 } 00332 00333 // Find the right ActionProperties element, otherwise return null element 00334 static QDomElement findActionPropertiesElement(const QDomDocument& doc) 00335 { 00336 const QLatin1String tagActionProp("ActionProperties"); 00337 const QString schemeName = currentShortcutScheme(); 00338 QDomElement e = doc.documentElement().firstChildElement(); 00339 for( ; !e.isNull(); e = e.nextSiblingElement() ) { 00340 if (QString::compare(e.tagName(), tagActionProp, Qt::CaseInsensitive) == 0 00341 && (e.attribute("scheme", "Default") == schemeName) ) { 00342 return e; 00343 } 00344 } 00345 return QDomElement(); 00346 } 00347 00348 void KXMLGUIFactoryPrivate::refreshActionProperties(KXMLGUIClient *client, const QList<QAction*>& actions, const QDomDocument& doc) 00349 { 00350 // try to find and apply shortcuts schemes 00351 QDomDocument scheme = shortcutSchemeDoc(client); 00352 applyShortcutScheme(client, actions, scheme); 00353 00354 // try to find and apply user-defined shortcuts 00355 const QDomElement actionPropElement = findActionPropertiesElement(doc); 00356 if ( !actionPropElement.isNull() ) 00357 applyActionProperties( actionPropElement ); 00358 } 00359 00360 void KXMLGUIFactoryPrivate::saveDefaultActionProperties(const QList<QAction *>& actions) 00361 { 00362 // This method is called every time the user activated a new 00363 // kxmlguiclient. We only want to execute the following code only once in 00364 // the lifetime of an action. 00365 foreach (QAction *action, actions) { 00366 // Skip actions we have seen already. 00367 if (action->property("_k_DefaultShortcut").isValid()) continue; 00368 00369 if (KAction* kaction = qobject_cast<KAction*>(action)) { 00370 // Check if the default shortcut is set 00371 KShortcut defaultShortcut = kaction->shortcut(KAction::DefaultShortcut); 00372 KShortcut activeShortcut = kaction->shortcut(KAction::ActiveShortcut); 00373 //kDebug() << kaction->objectName() << "default=" << defaultShortcut.toString() << "active=" << activeShortcut.toString(); 00374 00375 // Check if we have an empty default shortcut and an non empty 00376 // custom shortcut. This should only happen if a developer called 00377 // QAction::setShortcut on an KAction. Print out a warning and 00378 // correct the mistake 00379 if ((!activeShortcut.isEmpty()) && defaultShortcut.isEmpty()) { 00380 kError(240) << "Shortcut for KAction " << kaction->objectName() << kaction->text() << "set with QShortcut::setShortcut()! See KAction documentation."; 00381 kaction->setProperty("_k_DefaultShortcut", activeShortcut); 00382 } else { 00383 kaction->setProperty("_k_DefaultShortcut", defaultShortcut); 00384 } 00385 } 00386 else 00387 { 00388 // A QAction used with KXMLGUI? Set our property and ignore it. 00389 kError(240) << "Attempt to use QAction" << action->objectName() << "with KXMLGUIFactory!"; 00390 action->setProperty("_k_DefaultShortcut", KShortcut()); 00391 } 00392 00393 } 00394 } 00395 00396 void KXMLGUIFactory::changeShortcutScheme(const QString &scheme) 00397 { 00398 kDebug(260) << "Changing shortcut scheme to" << scheme; 00399 KConfigGroup cg = KGlobal::config()->group( "Shortcut Schemes" ); 00400 cg.writeEntry("Current Scheme", scheme); 00401 00402 refreshActionProperties(); 00403 } 00404 00405 void KXMLGUIFactory::forgetClient( KXMLGUIClient *client ) 00406 { 00407 d->m_clients.removeAll( client ); 00408 } 00409 00410 void KXMLGUIFactory::removeClient( KXMLGUIClient *client ) 00411 { 00412 //kDebug(260) << client; 00413 00414 // don't try to remove the client's GUI if we didn't build it 00415 if ( !client || client->factory() != this ) 00416 return; 00417 00418 if (d->emptyState()) 00419 emit makingChanges(true); 00420 00421 // remove this client from our client list 00422 d->m_clients.removeAll( client ); 00423 00424 // remove child clients first (create a copy of the list just in case the 00425 // original list is modified directly or indirectly in removeClient()) 00426 const QList<KXMLGUIClient*> childClients(client->childClients()); 00427 foreach (KXMLGUIClient *child, childClients) 00428 removeClient(child); 00429 00430 //kDebug(260) << "calling removeRecursive"; 00431 00432 d->pushState(); 00433 00434 // cache some variables 00435 00436 d->guiClient = client; 00437 d->clientName = client->domDocument().documentElement().attribute( d->attrName ); 00438 d->clientBuilder = client->clientBuilder(); 00439 00440 client->setFactory( 0L ); 00441 00442 // if we don't have a build document for that client, yet, then create one by 00443 // cloning the original document, so that saving container information in the 00444 // DOM tree does not touch the original document. 00445 QDomDocument doc = client->xmlguiBuildDocument(); 00446 if ( doc.documentElement().isNull() ) 00447 { 00448 doc = client->domDocument().cloneNode( true ).toDocument(); 00449 client->setXMLGUIBuildDocument( doc ); 00450 } 00451 00452 d->m_rootNode->destruct( doc.documentElement(), *d ); 00453 00454 // reset some variables 00455 d->BuildState::reset(); 00456 00457 // This will destruct the KAccel object built around the given widget. 00458 client->prepareXMLUnplug( d->builder->widget() ); 00459 00460 d->popState(); 00461 00462 if (d->emptyState()) 00463 emit makingChanges(false); 00464 00465 emit clientRemoved( client ); 00466 } 00467 00468 QList<KXMLGUIClient*> KXMLGUIFactory::clients() const 00469 { 00470 return d->m_clients; 00471 } 00472 00473 QWidget *KXMLGUIFactory::container( const QString &containerName, KXMLGUIClient *client, 00474 bool useTagName ) 00475 { 00476 d->pushState(); 00477 d->m_containerName = containerName; 00478 d->guiClient = client; 00479 00480 QWidget *result = d->findRecursive( d->m_rootNode, useTagName ); 00481 00482 d->guiClient = 0L; 00483 d->m_containerName.clear(); 00484 00485 d->popState(); 00486 00487 return result; 00488 } 00489 00490 QList<QWidget*> KXMLGUIFactory::containers( const QString &tagName ) 00491 { 00492 return d->findRecursive( d->m_rootNode, tagName ); 00493 } 00494 00495 void KXMLGUIFactory::reset() 00496 { 00497 d->m_rootNode->reset(); 00498 00499 d->m_rootNode->clearChildren(); 00500 } 00501 00502 void KXMLGUIFactory::resetContainer( const QString &containerName, bool useTagName ) 00503 { 00504 if ( containerName.isEmpty() ) 00505 return; 00506 00507 ContainerNode *container = d->m_rootNode->findContainer( containerName, useTagName ); 00508 00509 if ( !container ) 00510 return; 00511 00512 ContainerNode *parent = container->parent; 00513 if ( !parent ) 00514 return; 00515 00516 // resetInternal( container ); 00517 00518 parent->removeChild( container ); 00519 } 00520 00521 QWidget *KXMLGUIFactoryPrivate::findRecursive( KXMLGUI::ContainerNode *node, bool tag ) 00522 { 00523 if ( ( ( !tag && node->name == m_containerName ) || 00524 ( tag && node->tagName == m_containerName ) ) && 00525 ( !guiClient || node->client == guiClient ) ) 00526 return node->container; 00527 00528 foreach (ContainerNode* child, node->children) 00529 { 00530 QWidget *cont = findRecursive( child, tag ); 00531 if ( cont ) 00532 return cont; 00533 } 00534 00535 return 0L; 00536 } 00537 00538 // Case insensitive equality without calling toLower which allocates a new string 00539 static inline bool equals(const QString& str1, const char* str2) 00540 { 00541 return str1.compare(QLatin1String(str2), Qt::CaseInsensitive) == 0; 00542 } 00543 static inline bool equals(const QString& str1, const QString& str2) 00544 { 00545 return str1.compare(str2, Qt::CaseInsensitive) == 0; 00546 } 00547 00548 00549 QList<QWidget*> KXMLGUIFactoryPrivate::findRecursive( KXMLGUI::ContainerNode *node, 00550 const QString &tagName ) 00551 { 00552 QList<QWidget*> res; 00553 00554 if ( equals(node->tagName, tagName) ) 00555 res.append( node->container ); 00556 00557 foreach (KXMLGUI::ContainerNode* child, node->children) 00558 res << findRecursive( child, tagName ); 00559 00560 return res; 00561 } 00562 00563 void KXMLGUIFactory::plugActionList( KXMLGUIClient *client, const QString &name, 00564 const QList<QAction*> &actionList ) 00565 { 00566 d->pushState(); 00567 d->guiClient = client; 00568 d->actionListName = name; 00569 d->actionList = actionList; 00570 d->clientName = client->domDocument().documentElement().attribute( d->attrName ); 00571 00572 d->m_rootNode->plugActionList( *d ); 00573 00574 // Load shortcuts for these new actions 00575 d->saveDefaultActionProperties(actionList); 00576 d->refreshActionProperties(client, actionList, client->domDocument()); 00577 00578 d->BuildState::reset(); 00579 d->popState(); 00580 } 00581 00582 void KXMLGUIFactory::unplugActionList( KXMLGUIClient *client, const QString &name ) 00583 { 00584 d->pushState(); 00585 d->guiClient = client; 00586 d->actionListName = name; 00587 d->clientName = client->domDocument().documentElement().attribute( d->attrName ); 00588 00589 d->m_rootNode->unplugActionList( *d ); 00590 00591 d->BuildState::reset(); 00592 d->popState(); 00593 } 00594 00595 void KXMLGUIFactoryPrivate::applyActionProperties( const QDomElement &actionPropElement, 00596 ShortcutOption shortcutOption ) 00597 { 00598 for (QDomElement e = actionPropElement.firstChildElement(); 00599 !e.isNull(); e = e.nextSiblingElement()) { 00600 if ( !equals(e.tagName(), "action") ) 00601 continue; 00602 00603 QAction *action = guiClient->action( e ); 00604 if ( !action ) 00605 continue; 00606 00607 configureAction( action, e.attributes(), shortcutOption ); 00608 } 00609 } 00610 00611 void KXMLGUIFactoryPrivate::configureAction( QAction *action, const QDomNamedNodeMap &attributes, 00612 ShortcutOption shortcutOption ) 00613 { 00614 for ( uint i = 0; i < attributes.length(); i++ ) 00615 { 00616 QDomAttr attr = attributes.item( i ).toAttr(); 00617 if ( attr.isNull() ) 00618 continue; 00619 00620 configureAction( action, attr, shortcutOption ); 00621 } 00622 } 00623 00624 void KXMLGUIFactoryPrivate::configureAction( QAction *action, const QDomAttr &attribute, 00625 ShortcutOption shortcutOption ) 00626 { 00627 static const QString &attrShortcut = KGlobal::staticQString( "shortcut" ); 00628 00629 QString attrName = attribute.name(); 00630 // If the attribute is a deprecated "accel", change to "shortcut". 00631 if ( equals(attrName, "accel") ) 00632 attrName = attrShortcut; 00633 00634 // No need to re-set name, particularly since it's "objectName" in Qt4 00635 if ( equals(attrName, "name") ) 00636 return; 00637 00638 if ( equals(attrName, "icon") ) { 00639 action->setIcon( KIcon( attribute.value() ) ); 00640 return; 00641 } 00642 00643 QVariant propertyValue; 00644 00645 QVariant::Type propertyType = action->property( attrName.toLatin1() ).type(); 00646 00647 if ( propertyType == QVariant::Int ) { 00648 propertyValue = QVariant( attribute.value().toInt() ); 00649 } else if ( propertyType == QVariant::UInt ) { 00650 propertyValue = QVariant( attribute.value().toUInt() ); 00651 } else if ( propertyType == QVariant::UserType && action->property( attrName.toLatin1() ).userType() == qMetaTypeId<KShortcut>() ) { 00652 // Setting the shortcut by property also sets the default shortcut (which is incorrect), so we have to do it directly 00653 if (KAction* ka = qobject_cast<KAction*>(action)) { 00654 if (attrName=="globalShortcut") { 00655 ka->setGlobalShortcut(KShortcut(attribute.value()), KAction::ActiveShortcut); 00656 } else { 00657 ka->setShortcut(KShortcut(attribute.value()), KAction::ActiveShortcut); 00658 } 00659 if (shortcutOption & KXMLGUIFactoryPrivate::SetDefaultShortcut) 00660 ka->setShortcut(KShortcut(attribute.value()), KAction::DefaultShortcut); 00661 return; 00662 } 00663 propertyValue = KShortcut( attribute.value() ); 00664 } else { 00665 propertyValue = QVariant( attribute.value() ); 00666 } 00667 if (!action->setProperty( attrName.toLatin1(), propertyValue )) { 00668 kWarning() << "Error: Unknown action property " << attrName << " will be ignored!"; 00669 } 00670 } 00671 00672 QDomDocument KXMLGUIFactoryPrivate::shortcutSchemeDoc(KXMLGUIClient *client) 00673 { 00674 // Get the name of the current shorcut scheme 00675 KConfigGroup cg = KGlobal::config()->group( "Shortcut Schemes" ); 00676 QString schemeName = cg.readEntry("Current Scheme", "Default"); 00677 00678 QDomDocument doc; 00679 if (schemeName != "Default") 00680 { 00681 // Find the document for the shortcut scheme using both current application path 00682 // and current xmlguiclient path but making a preference to app path 00683 QString schemeFileName = KStandardDirs::locateLocal("data", 00684 client->componentData().componentName() + '/' + 00685 client->componentData().componentName() + schemeName.toLower() + "shortcuts.rc" ); 00686 00687 QFile schemeFile(schemeFileName); 00688 if (schemeFile.open(QIODevice::ReadOnly)) 00689 { 00690 // kDebug(260) << "Found shortcut scheme" << schemeFileName; 00691 doc.setContent(&schemeFile); 00692 schemeFile.close(); 00693 } 00694 } 00695 return doc; 00696 } 00697 00698 void KXMLGUIFactoryPrivate::applyShortcutScheme(KXMLGUIClient *client, const QList<QAction*> &actions, const QDomDocument& scheme) 00699 { 00700 static const QString &actionPropElementName = KGlobal::staticQString( "ActionProperties" ); 00701 00702 KConfigGroup cg = KGlobal::config()->group( "Shortcut Schemes" ); 00703 QString schemeName = cg.readEntry("Current Scheme", "Default"); 00704 00705 //First clear all existing shortcuts 00706 if (schemeName != "Default") { 00707 foreach (QAction *action, actions) { 00708 if (KAction *kaction = qobject_cast<KAction*>(action)) { 00709 kaction->setShortcut(KShortcut(), KAction::ActiveShortcut); 00710 // We clear the default shortcut as well because the shortcut scheme will set its own defaults 00711 kaction->setShortcut(KShortcut(), KAction::DefaultShortcut); 00712 } 00713 else 00714 action->setProperty("shortcut", KShortcut()); 00715 } 00716 } else { 00717 // apply saved default shortcuts 00718 foreach (QAction *action, actions) { 00719 if (KAction *kaction = qobject_cast<KAction*>(action)) { 00720 QVariant savedDefaultShortcut = kaction->property("_k_DefaultShortcut"); 00721 if (savedDefaultShortcut.isValid()) { 00722 KShortcut shortcut = savedDefaultShortcut.value<KShortcut>(); 00723 //kDebug() << "scheme said" << shortcut.toString() << "for action" << kaction->objectName(); 00724 kaction->setShortcut(shortcut, KAction::ActiveShortcut); 00725 kaction->setShortcut(shortcut, KAction::DefaultShortcut); 00726 continue; 00727 } 00728 } 00729 action->setProperty("shortcut", KShortcut()); 00730 } 00731 } 00732 00733 if (scheme.isNull()) 00734 return; 00735 00736 QDomElement docElement = scheme.documentElement(); 00737 QDomElement actionPropElement = docElement.namedItem( actionPropElementName ).toElement(); 00738 00739 //Check if we really have the shortcut configuration here 00740 if (!actionPropElement.isNull()) { 00741 kDebug(260) << "Applying shortcut scheme for XMLGUI client" << client->componentData().componentName(); 00742 00743 //Apply all shortcuts we have 00744 applyActionProperties(actionPropElement, KXMLGUIFactoryPrivate::SetDefaultShortcut); 00745 } else { 00746 kDebug(260) << "Invalid shortcut scheme file"; 00747 } 00748 } 00749 00750 int KXMLGUIFactory::configureShortcuts(bool letterCutsOk , bool bSaveSettings ) 00751 { 00752 KShortcutsDialog dlg(KShortcutsEditor::AllActions, 00753 letterCutsOk ? KShortcutsEditor::LetterShortcutsAllowed : KShortcutsEditor::LetterShortcutsDisallowed, 00754 qobject_cast<QWidget*>(parent())); 00755 foreach (KXMLGUIClient *client, d->m_clients) { 00756 if(client) { 00757 dlg.addCollection(client->actionCollection()); 00758 } 00759 } 00760 return dlg.configure(bSaveSettings); 00761 } 00762 00763 // Find or create 00764 QDomElement KXMLGUIFactory::actionPropertiesElement( QDomDocument& doc ) 00765 { 00766 // first, lets see if we have existing properties 00767 QDomElement elem = findActionPropertiesElement(doc); 00768 00769 // if there was none, create one 00770 if(elem.isNull()) { 00771 elem = doc.createElement(QLatin1String("ActionProperties")); 00772 elem.setAttribute("scheme", currentShortcutScheme()); 00773 doc.documentElement().appendChild( elem ); 00774 } 00775 return elem; 00776 } 00777 00778 QDomElement KXMLGUIFactory::findActionByName( QDomElement& elem, const QString& sName, bool create ) 00779 { 00780 static const QString& attrName = KGlobal::staticQString( "name" ); 00781 static const QString& tagAction = KGlobal::staticQString( "Action" ); 00782 for( QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling() ) { 00783 QDomElement e = it.toElement(); 00784 if( e.attribute( attrName ) == sName ) 00785 return e; 00786 } 00787 00788 if( create ) { 00789 QDomElement act_elem = elem.ownerDocument().createElement( tagAction ); 00790 act_elem.setAttribute( attrName, sName ); 00791 elem.appendChild( act_elem ); 00792 return act_elem; 00793 } 00794 return QDomElement(); 00795 } 00796 00797 #include "kxmlguifactory.moc" 00798 00799 /* vim: et sw=4 00800 */
KDE 4.6 API Reference