kdeui Library API Documentation

kxmlguifactory.cpp

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., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, 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 <qfile.h> 00029 #include <qtextstream.h> 00030 #include <qwidget.h> 00031 #include <qdatetime.h> 00032 #include <qvariant.h> 00033 00034 #include <kaction.h> 00035 #include <kdebug.h> 00036 #include <kinstance.h> 00037 #include <kglobal.h> 00038 #include <kshortcut.h> 00039 #include <kstandarddirs.h> 00040 #include <kkeydialog.h> 00041 00042 using namespace KXMLGUI; 00043 00044 /* 00045 * TODO: - make more use of QValueList instead of QPtrList 00046 */ 00047 00048 class KXMLGUIFactoryPrivate : public BuildState 00049 { 00050 public: 00051 KXMLGUIFactoryPrivate() 00052 { 00053 static const QString &defaultMergingName = KGlobal::staticQString( "<default>" ); 00054 static const QString &actionList = KGlobal::staticQString( "actionlist" ); 00055 static const QString &name = KGlobal::staticQString( "name" ); 00056 00057 m_rootNode = new ContainerNode( 0L, QString::null, 0L ); 00058 m_defaultMergingName = defaultMergingName; 00059 tagActionList = actionList; 00060 attrName = name; 00061 } 00062 ~KXMLGUIFactoryPrivate() 00063 { 00064 delete m_rootNode; 00065 } 00066 00067 void pushState() 00068 { 00069 m_stateStack.push( *this ); 00070 } 00071 00072 void popState() 00073 { 00074 BuildState::operator=( m_stateStack.pop() ); 00075 } 00076 00077 ContainerNode *m_rootNode; 00078 00079 QString m_defaultMergingName; 00080 00081 /* 00082 * Contains the container which is searched for in ::container . 00083 */ 00084 QString m_containerName; 00085 00086 /* 00087 * List of all clients 00088 */ 00089 QPtrList<KXMLGUIClient> m_clients; 00090 00091 QString tagActionList; 00092 00093 QString attrName; 00094 00095 BuildStateStack m_stateStack; 00096 }; 00097 00098 QString KXMLGUIFactory::readConfigFile( const QString &filename, const KInstance *instance ) 00099 { 00100 return readConfigFile( filename, false, instance ); 00101 } 00102 00103 QString KXMLGUIFactory::readConfigFile( const QString &filename, bool never_null, const KInstance *_instance ) 00104 { 00105 const KInstance *instance = _instance ? _instance : KGlobal::instance(); 00106 QString xml_file; 00107 00108 if (filename[0] == '/') 00109 xml_file = filename; 00110 else 00111 { 00112 xml_file = locate("data", QString::fromLatin1(instance->instanceName() + '/' ) + filename); 00113 if ( !QFile::exists( xml_file ) ) 00114 xml_file = locate( "data", filename ); 00115 } 00116 00117 QFile file( xml_file ); 00118 if ( !file.open( IO_ReadOnly ) ) 00119 { 00120 kdError(240) << "No such XML file " << filename << endl; 00121 if ( never_null ) 00122 return QString::fromLatin1( "<!DOCTYPE kpartgui>\n<kpartgui name=\"empty\">\n</kpartgui>" ); 00123 else 00124 return QString::null; 00125 } 00126 00127 #if QT_VERSION <= 0x030302 00128 // Work around bug in QString::fromUtf8 (which calls strlen). 00129 QByteArray buffer(file.size() + 1); 00130 buffer = file.readAll(); 00131 if(!buffer.isEmpty()) 00132 buffer[ buffer.size() - 1 ] = '\0'; 00133 else 00134 return QString::null; 00135 #else 00136 QByteArray buffer(file.readAll()); 00137 #endif 00138 return QString::fromUtf8(buffer.data(), buffer.size()); 00139 } 00140 00141 bool KXMLGUIFactory::saveConfigFile( const QDomDocument& doc, 00142 const QString& filename, const KInstance *_instance ) 00143 { 00144 const KInstance *instance = _instance ? _instance : KGlobal::instance(); 00145 QString xml_file(filename); 00146 00147 if (xml_file[0] != '/') 00148 xml_file = locateLocal("data", QString::fromLatin1( instance->instanceName() + '/' ) 00149 + filename); 00150 00151 QFile file( xml_file ); 00152 if ( !file.open( IO_WriteOnly ) ) 00153 { 00154 kdError(240) << "Could not write to " << filename << endl; 00155 return false; 00156 } 00157 00158 // write out our document 00159 QTextStream ts(&file); 00160 ts.setEncoding( QTextStream::UnicodeUTF8 ); 00161 ts << doc; 00162 00163 file.close(); 00164 return true; 00165 } 00166 00167 QString KXMLGUIFactory::documentToXML( const QDomDocument& doc ) 00168 { 00169 QString str; 00170 QTextStream ts(&str, IO_WriteOnly); 00171 ts.setEncoding( QTextStream::UnicodeUTF8 ); 00172 ts << doc; 00173 return str; 00174 } 00175 00176 QString KXMLGUIFactory::elementToXML( const QDomElement& elem ) 00177 { 00178 QString str; 00179 QTextStream ts(&str, IO_WriteOnly); 00180 ts.setEncoding( QTextStream::UnicodeUTF8 ); 00181 ts << elem; 00182 return str; 00183 } 00184 00185 void KXMLGUIFactory::removeDOMComments( QDomNode &node ) 00186 { 00187 QDomNode n = node.firstChild(); 00188 while ( !n.isNull() ) 00189 { 00190 if ( n.nodeType() == QDomNode::CommentNode ) 00191 { 00192 QDomNode tmp = n; 00193 n = n.nextSibling(); 00194 node.removeChild( tmp ); 00195 } 00196 else 00197 { 00198 QDomNode tmp = n; 00199 n = n.nextSibling(); 00200 removeDOMComments( tmp ); 00201 } 00202 } 00203 } 00204 00205 KXMLGUIFactory::KXMLGUIFactory( KXMLGUIBuilder *builder, QObject *parent, const char *name ) 00206 : QObject( parent, name ) 00207 { 00208 d = new KXMLGUIFactoryPrivate; 00209 d->builder = builder; 00210 d->guiClient = 0; 00211 if ( d->builder ) 00212 { 00213 d->builderContainerTags = d->builder->containerTags(); 00214 d->builderCustomTags = d->builder->customTags(); 00215 } 00216 } 00217 00218 KXMLGUIFactory::~KXMLGUIFactory() 00219 { 00220 delete d; 00221 } 00222 00223 void KXMLGUIFactory::addClient( KXMLGUIClient *client ) 00224 { 00225 kdDebug(129) << "KXMLGUIFactory::addClient( " << client << " )" << endl; // ellis 00226 static const QString &actionPropElementName = KGlobal::staticQString( "ActionProperties" ); 00227 00228 if ( client->factory() ) { 00229 if ( client->factory() == this ) 00230 return; 00231 else 00232 client->factory()->removeClient( client ); //just in case someone does stupid things ;-) 00233 } 00234 00235 d->pushState(); 00236 00237 // QTime dt; dt.start(); 00238 00239 d->guiClient = client; 00240 00241 // add this client to our client list 00242 if ( d->m_clients.containsRef( client ) == 0 ) 00243 d->m_clients.append( client ); 00244 else 00245 kdDebug(129) << "XMLGUI client already added " << client << endl; 00246 00247 // Tell the client that plugging in is process and 00248 // let it know what builder widget its mainwindow shortcuts 00249 // should be attached to. 00250 client->beginXMLPlug( d->builder->widget() ); 00251 00252 // try to use the build document for building the client's GUI, as the build document 00253 // contains the correct container state information (like toolbar positions, sizes, etc.) . 00254 // if there is non available, then use the "real" document. 00255 QDomDocument doc = client->xmlguiBuildDocument(); 00256 if ( doc.documentElement().isNull() ) 00257 doc = client->domDocument(); 00258 00259 QDomElement docElement = doc.documentElement(); 00260 00261 d->m_rootNode->index = -1; 00262 00263 // cache some variables 00264 00265 d->clientName = docElement.attribute( d->attrName ); 00266 d->clientBuilder = client->clientBuilder(); 00267 00268 if ( d->clientBuilder ) 00269 { 00270 d->clientBuilderContainerTags = d->clientBuilder->containerTags(); 00271 d->clientBuilderCustomTags = d->clientBuilder->customTags(); 00272 } 00273 else 00274 { 00275 d->clientBuilderContainerTags.clear(); 00276 d->clientBuilderCustomTags.clear(); 00277 } 00278 00279 // process a possibly existing actionproperties section 00280 00281 QDomElement actionPropElement = docElement.namedItem( actionPropElementName ).toElement(); 00282 if ( actionPropElement.isNull() ) 00283 actionPropElement = docElement.namedItem( actionPropElementName.lower() ).toElement(); 00284 00285 if ( !actionPropElement.isNull() ) 00286 applyActionProperties( actionPropElement ); 00287 00288 BuildHelper( *d, d->m_rootNode ).build( docElement ); 00289 00290 // let the client know that we built its GUI. 00291 client->setFactory( this ); 00292 00293 // call the finalizeGUI method, to fix up the positions of toolbars for example. 00294 // ### FIXME : obey client builder 00295 // --- Well, toolbars have a bool "positioned", so it doesn't really matter, 00296 // if we call positionYourself on all of them each time. (David) 00297 d->builder->finalizeGUI( d->guiClient ); 00298 00299 // reset some variables, for safety 00300 d->BuildState::reset(); 00301 00302 client->endXMLPlug(); 00303 00304 d->popState(); 00305 00306 emit clientAdded( client ); 00307 00308 // build child clients 00309 if ( client->childClients()->count() > 0 ) 00310 { 00311 const QPtrList<KXMLGUIClient> *children = client->childClients(); 00312 QPtrListIterator<KXMLGUIClient> childIt( *children ); 00313 for (; childIt.current(); ++childIt ) 00314 addClient( childIt.current() ); 00315 } 00316 00317 // kdDebug() << "addClient took " << dt.elapsed() << endl; 00318 } 00319 00320 void KXMLGUIFactory::removeClient( KXMLGUIClient *client ) 00321 { 00322 kdDebug(129) << "KXMLGUIFactory::removeClient( " << client << " )" << endl; // ellis 00323 00324 // don't try to remove the client's GUI if we didn't build it 00325 if ( !client || client->factory() != this ) 00326 return; 00327 00328 // remove this client from our client list 00329 d->m_clients.removeRef( client ); 00330 00331 // remove child clients first 00332 if ( client->childClients()->count() > 0 ) 00333 { 00334 const QPtrList<KXMLGUIClient> *children = client->childClients(); 00335 QPtrListIterator<KXMLGUIClient> childIt( *children ); 00336 childIt.toLast(); 00337 for (; childIt.current(); --childIt ) 00338 removeClient( childIt.current() ); 00339 } 00340 00341 kdDebug(1002) << "KXMLGUIFactory::removeServant, calling removeRecursive" << endl; 00342 00343 d->pushState(); 00344 00345 // cache some variables 00346 00347 d->guiClient = client; 00348 d->clientName = client->domDocument().documentElement().attribute( d->attrName ); 00349 d->clientBuilder = client->clientBuilder(); 00350 00351 client->setFactory( 0L ); 00352 00353 // if we don't have a build document for that client, yet, then create one by 00354 // cloning the original document, so that saving container information in the 00355 // DOM tree does not touch the original document. 00356 QDomDocument doc = client->xmlguiBuildDocument(); 00357 if ( doc.documentElement().isNull() ) 00358 { 00359 doc = client->domDocument().cloneNode( true ).toDocument(); 00360 client->setXMLGUIBuildDocument( doc ); 00361 } 00362 00363 d->m_rootNode->destruct( doc.documentElement(), *d ); 00364 00365 d->builder->finalizeGUI( d->guiClient ); //JoWenn 00366 00367 // reset some variables 00368 d->BuildState::reset(); 00369 00370 // This will destruct the KAccel object built around the given widget. 00371 client->prepareXMLUnplug( d->builder->widget() ); 00372 00373 d->popState(); 00374 00375 emit clientRemoved( client ); 00376 } 00377 00378 QPtrList<KXMLGUIClient> KXMLGUIFactory::clients() const 00379 { 00380 return d->m_clients; 00381 } 00382 00383 QWidget *KXMLGUIFactory::container( const QString &containerName, KXMLGUIClient *client, 00384 bool useTagName ) 00385 { 00386 d->pushState(); 00387 d->m_containerName = containerName; 00388 d->guiClient = client; 00389 00390 QWidget *result = findRecursive( d->m_rootNode, useTagName ); 00391 00392 d->guiClient = 0L; 00393 d->m_containerName = QString::null; 00394 00395 d->popState(); 00396 00397 return result; 00398 } 00399 00400 QPtrList<QWidget> KXMLGUIFactory::containers( const QString &tagName ) 00401 { 00402 return findRecursive( d->m_rootNode, tagName ); 00403 } 00404 00405 void KXMLGUIFactory::reset() 00406 { 00407 d->m_rootNode->reset(); 00408 00409 d->m_rootNode->clearChildren(); 00410 } 00411 00412 void KXMLGUIFactory::resetContainer( const QString &containerName, bool useTagName ) 00413 { 00414 if ( containerName.isEmpty() ) 00415 return; 00416 00417 ContainerNode *container = d->m_rootNode->findContainer( containerName, useTagName ); 00418 00419 if ( !container ) 00420 return; 00421 00422 ContainerNode *parent = container->parent; 00423 if ( !parent ) 00424 return; 00425 00426 // resetInternal( container ); 00427 00428 parent->removeChild( container ); 00429 } 00430 00431 QWidget *KXMLGUIFactory::findRecursive( KXMLGUI::ContainerNode *node, bool tag ) 00432 { 00433 if ( ( ( !tag && node->name == d->m_containerName ) || 00434 ( tag && node->tagName == d->m_containerName ) ) && 00435 ( !d->guiClient || node->client == d->guiClient ) ) 00436 return node->container; 00437 00438 QPtrListIterator<ContainerNode> it( node->children ); 00439 for (; it.current(); ++it ) 00440 { 00441 QWidget *cont = findRecursive( it.current(), tag ); 00442 if ( cont ) 00443 return cont; 00444 } 00445 00446 return 0L; 00447 } 00448 00449 QPtrList<QWidget> KXMLGUIFactory::findRecursive( KXMLGUI::ContainerNode *node, 00450 const QString &tagName ) 00451 { 00452 QPtrList<QWidget> res; 00453 00454 if ( node->tagName == tagName.lower() ) 00455 res.append( node->container ); 00456 00457 QPtrListIterator<KXMLGUI::ContainerNode> it( node->children ); 00458 for (; it.current(); ++it ) 00459 { 00460 QPtrList<QWidget> lst = findRecursive( it.current(), tagName ); 00461 QPtrListIterator<QWidget> wit( lst ); 00462 for (; wit.current(); ++wit ) 00463 res.append( wit.current() ); 00464 } 00465 00466 return res; 00467 } 00468 00469 void KXMLGUIFactory::plugActionList( KXMLGUIClient *client, const QString &name, 00470 const QPtrList<KAction> &actionList ) 00471 { 00472 d->pushState(); 00473 d->guiClient = client; 00474 d->actionListName = name; 00475 d->actionList = actionList; 00476 d->clientName = client->domDocument().documentElement().attribute( d->attrName ); 00477 00478 d->m_rootNode->plugActionList( *d ); 00479 00480 d->BuildState::reset(); 00481 d->popState(); 00482 } 00483 00484 void KXMLGUIFactory::unplugActionList( KXMLGUIClient *client, const QString &name ) 00485 { 00486 d->pushState(); 00487 d->guiClient = client; 00488 d->actionListName = name; 00489 d->clientName = client->domDocument().documentElement().attribute( d->attrName ); 00490 00491 d->m_rootNode->unplugActionList( *d ); 00492 00493 d->BuildState::reset(); 00494 d->popState(); 00495 } 00496 00497 void KXMLGUIFactory::applyActionProperties( const QDomElement &actionPropElement ) 00498 { 00499 static const QString &tagAction = KGlobal::staticQString( "action" ); 00500 00501 for (QDomNode n = actionPropElement.firstChild(); 00502 !n.isNull(); n = n.nextSibling() ) 00503 { 00504 QDomElement e = n.toElement(); 00505 if ( e.tagName().lower() != tagAction ) 00506 continue; 00507 00508 KAction *action = d->guiClient->action( e ); 00509 if ( !action ) 00510 continue; 00511 00512 configureAction( action, e.attributes() ); 00513 } 00514 } 00515 00516 void KXMLGUIFactory::configureAction( KAction *action, const QDomNamedNodeMap &attributes ) 00517 { 00518 for ( uint i = 0; i < attributes.length(); i++ ) 00519 { 00520 QDomAttr attr = attributes.item( i ).toAttr(); 00521 if ( attr.isNull() ) 00522 continue; 00523 00524 configureAction( action, attr ); 00525 } 00526 } 00527 00528 void KXMLGUIFactory::configureAction( KAction *action, const QDomAttr &attribute ) 00529 { 00530 static const QString &attrShortcut = KGlobal::staticQString( "shortcut" ); 00531 00532 QString attrName = attribute.name(); 00533 // If the attribute is a deprecated "accel", change to "shortcut". 00534 if ( attrName.lower() == "accel" ) 00535 attrName = attrShortcut; 00536 00537 QVariant propertyValue; 00538 00539 QVariant::Type propertyType = action->property( attrName.latin1() ).type(); 00540 00541 if ( propertyType == QVariant::Int ) 00542 propertyValue = QVariant( attribute.value().toInt() ); 00543 else if ( propertyType == QVariant::UInt ) 00544 propertyValue = QVariant( attribute.value().toUInt() ); 00545 else 00546 propertyValue = QVariant( attribute.value() ); 00547 00548 action->setProperty( attrName.latin1(), propertyValue ); 00549 } 00550 00551 00552 int KXMLGUIFactory::configureShortcuts(bool bAllowLetterShortcuts , bool bSaveSettings ) 00553 { 00554 KKeyDialog dlg( bAllowLetterShortcuts, dynamic_cast<QWidget*>(parent()) ); 00555 QPtrListIterator<KXMLGUIClient> it( d->m_clients ); 00556 KXMLGUIClient *client; 00557 while( (client=it.current()) !=0 ) 00558 { 00559 ++it; 00560 if(!client->xmlFile().isEmpty()) 00561 dlg.insert( client->actionCollection() ); 00562 } 00563 return dlg.configure(bSaveSettings); 00564 } 00565 00566 QDomElement KXMLGUIFactory::actionPropertiesElement( QDomDocument& doc ) 00567 { 00568 const QString tagActionProp = QString::fromLatin1("ActionProperties"); 00569 // first, lets see if we have existing properties 00570 QDomElement elem; 00571 QDomNode it = doc.documentElement().firstChild(); 00572 for( ; !it.isNull(); it = it.nextSibling() ) { 00573 QDomElement e = it.toElement(); 00574 if( e.tagName() == tagActionProp ) { 00575 elem = e; 00576 break; 00577 } 00578 } 00579 00580 // if there was none, create one 00581 if( elem.isNull() ) { 00582 elem = doc.createElement( tagActionProp ); 00583 doc.documentElement().appendChild( elem ); 00584 } 00585 return elem; 00586 } 00587 00588 QDomElement KXMLGUIFactory::findActionByName( QDomElement& elem, const QString& sName, bool create ) 00589 { 00590 static const QString& attrName = KGlobal::staticQString( "name" ); 00591 static const QString& tagAction = KGlobal::staticQString( "Action" ); 00592 for( QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling() ) { 00593 QDomElement e = it.toElement(); 00594 if( e.attribute( attrName ) == sName ) 00595 return e; 00596 } 00597 00598 if( create ) { 00599 QDomElement act_elem = elem.ownerDocument().createElement( tagAction ); 00600 act_elem.setAttribute( attrName, sName ); 00601 elem.appendChild( act_elem ); 00602 return act_elem; 00603 } 00604 return QDomElement(); 00605 } 00606 00607 void KXMLGUIFactory::virtual_hook( int, void* ) 00608 { /*BASE::virtual_hook( id, data );*/ } 00609 00610 #include "kxmlguifactory.moc" 00611 00612 /* vim: et sw=4 00613 */
KDE Logo
This file is part of the documentation for kdeui Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 17 11:27:33 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003