Salome HOME
Join modifications from branch BR_DEBUG_3_2_0b1
[modules/gui.git] / src / Qtx / QtxActionMgr.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
2 // 
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either 
6 // version 2.1 of the License.
7 // 
8 // This library is distributed in the hope that it will be useful 
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public  
14 // License along with this library; if not, write to the Free Software 
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // File:      QtxActionMgr.cxx
20 // Author:    Alexander SOLOVYEV, Sergey TELKOV
21
22 #include "Qtx.h"
23 #include "QtxActionMgr.h"
24 #include "QtxAction.h"
25
26 #include <qwidget.h>
27 #include <qtoolbar.h>
28 #include <qpopupmenu.h>
29 #include <qwidgetlist.h>
30 #include <qobjectlist.h>
31 #include <qfile.h>
32 #include <qdom.h>
33
34 static QAction* qtx_separator_action = 0;
35
36 void qtxSeparatorActionCleanup()
37 {
38   delete qtx_separator_action;
39   qtx_separator_action = 0;
40 }
41
42 /*!
43         Class: QtxActionMenuMgr::SeparatorAction
44         Level: Internal
45 */
46
47 class QtxActionMgr::SeparatorAction : public QtxAction
48 {
49 public:
50   SeparatorAction( QObject* = 0 );
51   virtual ~SeparatorAction();
52
53   virtual bool addTo( QWidget* );
54   virtual bool removeFrom( QWidget* );
55
56 private:
57   QMap<QPopupMenu*, QIntList>  myMenus;
58   QMap<QToolBar*, QWidgetList> myTools;
59 };
60
61 /*!
62   Constructor
63 */
64 QtxActionMgr::SeparatorAction::SeparatorAction( QObject* parent )
65 : QtxAction( parent )
66 {
67 }
68
69 /*!
70   Destructor
71 */
72 QtxActionMgr::SeparatorAction::~SeparatorAction()
73 {
74 }
75
76 /*!
77   Adds action to widget
78   \param wid - widget
79 */
80 bool QtxActionMgr::SeparatorAction::addTo( QWidget* wid )
81 {
82   if ( !wid )
83     return false;
84
85   bool res = true;
86   if ( wid->inherits( "QPopupMenu" ) )
87   {
88     QPopupMenu* popup = (QPopupMenu*)wid;
89     myMenus[popup].append( popup->insertSeparator() );
90   }
91   else if ( wid->inherits( "QToolBar" ) )
92   {
93     QToolBar* tb = (QToolBar*)wid;
94     tb->addSeparator();
95     myTools[tb].append( (QWidget*)tb->children()->getLast() );
96   }
97   else
98     res = false;
99
100   return res;
101 }
102
103 /*!
104   Removes action from widget
105   \param wid - widget
106 */
107 bool QtxActionMgr::SeparatorAction::removeFrom( QWidget* wid )
108 {
109   if ( !wid )
110     return false;
111
112   bool res = true;
113   if ( wid->inherits( "QPopupMenu" ) )
114   {
115     QPopupMenu* popup = (QPopupMenu*)wid;
116     if ( myMenus.contains( popup ) )
117     {
118       const QIntList& list = myMenus[popup];
119       for ( QIntList::const_iterator it = list.begin(); it != list.end(); ++it )
120         popup->removeItem( *it );
121
122       myMenus.remove( popup );
123     }
124   }
125   else if ( wid->inherits( "QToolBar" ) )
126   {
127     QToolBar* tb = (QToolBar*)wid;
128     if ( myTools.contains( tb ) )
129     {
130       QMap<QObject*, int> childMap;
131       if ( tb->children() )
132       {
133         for ( QObjectListIt it( *tb->children() ); it.current(); ++it )
134           childMap.insert( it.current(), 0 );
135       }
136       const QWidgetList& list = myTools[tb];
137       for ( QWidgetListIt it( list ); it.current(); ++it )
138       {
139         if ( childMap.contains( it.current() ) )
140         delete it.current();
141       }
142
143       myTools.remove( tb );
144     }
145   }
146   else
147     res = false;
148
149   return res;
150 }
151
152 /*!
153         Class: QtxActionMgr
154         Level: Public
155 */
156
157 /*!
158   Constructor
159 */
160 QtxActionMgr::QtxActionMgr( QObject* parent )
161 : QObject( parent ),
162 myUpdate( true )
163 {
164 }
165
166 /*!
167   Destructor
168 */
169 QtxActionMgr::~QtxActionMgr()
170 {
171 }
172
173 /*!
174   Stores action in internal map
175   If action with such id is registered already, then it will be unregistered
176   \param a - action to be registered
177   \param userId - proposed id (if it is less than 0, then id will be generated automatically)
178 */
179 int QtxActionMgr::registerAction( QAction* a, const int userId )
180 {
181   if ( !a )
182     return -1;
183
184   int theId = userId < 0 ? generateId() : userId;
185
186   if ( contains( theId ) )
187     unRegisterAction( theId );
188
189   int cur = actionId( a );
190   if ( cur != -1 )
191   {
192     if ( userId == -1 )
193       return cur;
194     else
195       unRegisterAction( cur );
196   }
197
198   myActions.insert( theId, a );
199
200   return theId;
201 }
202
203 /*!
204   Removes action from internal map
205   \param id - action id
206 */
207 void QtxActionMgr::unRegisterAction( const int id )
208 {
209   if( contains( id ) )
210     myActions.remove( id );
211 }
212
213 /*!
214   \return action by id
215   \param id - action id
216 */
217 QAction* QtxActionMgr::action( const int id ) const
218 {
219   if ( contains( id ) )
220     return myActions[ id ];
221   else
222     return 0;
223 }
224
225 /*!
226   \return id by action
227   \param a - action
228 */
229 int QtxActionMgr::actionId( const QAction* a ) const
230 {
231   if ( !a )
232     return -1;
233
234   int theId = -1;
235   for ( ActionMap::ConstIterator it = myActions.begin(); it != myActions.end() && theId == -1; ++it )
236   {
237     if ( it.data() == a )
238       theId = it.key();
239   }
240
241   return theId;
242 }
243
244 /*!
245   \return true if internal map contains such id
246   \param id - action id
247 */
248 bool QtxActionMgr::contains( const int id ) const
249 {
250   return myActions.contains( id );
251 }
252
253 /*!
254   \return count of actions in internal map
255 */
256 int QtxActionMgr::count() const
257 {
258   return myActions.count();
259 }
260
261 /*!
262   \return true if internal map is empty
263 */
264 bool QtxActionMgr::isEmpty() const
265 {
266   return myActions.isEmpty();
267 }
268
269 /*!
270   Fills list with ids of registered actions
271 */
272 void QtxActionMgr::idList( QIntList& lst ) const
273 {
274   lst = myActions.keys();
275 }
276
277 /*!
278   \return true if updates are enabled
279 */
280 bool QtxActionMgr::isUpdatesEnabled() const
281 {
282   return myUpdate;
283 }
284
285 /*!
286   Enables/disables updates
287   \param upd - new state
288 */
289 void QtxActionMgr::setUpdatesEnabled( const bool upd )
290 {
291   myUpdate = upd;
292 }
293
294 /*!
295   \return true if action is visible (by default \return always true)
296 */
297 bool QtxActionMgr::isVisible( const int, const int ) const
298 {
299   return true;
300 }
301
302 /*!
303   Sets visibility of action (by default, empty implementation)
304 */
305 void QtxActionMgr::setVisible( const int, const int, const bool )
306 {
307 }
308
309 /*!
310   Updates actions, check isUpdatesEnabled() and call internalUpdate()
311   \sa isUpdatesEnabled(), internalUpdate()
312 */
313 void QtxActionMgr::update()
314 {
315   if ( isUpdatesEnabled() )
316     internalUpdate();
317 }
318
319 /*!
320   Real update (to be redefined in successors)
321 */
322 void QtxActionMgr::internalUpdate()
323 {
324 }
325
326 /*!
327   \return global free id
328 */
329 int QtxActionMgr::generateId() const
330 {
331   static int id = -1;
332   return --id;
333 }
334
335 /*!
336   \return true if action is enabled
337   \param id - action id
338 */
339 bool QtxActionMgr::isEnabled( const int id ) const
340 {
341   QAction* a = action( id );
342   if ( a )
343     return a->isEnabled();
344   else
345     return false;
346 }
347
348 /*!
349   Enables/disables action
350   \param id - action id
351   \param en - new state
352 */
353 void QtxActionMgr::setEnabled( const int id, const bool en )
354 {
355   QAction* a = action( id );
356   if ( a )
357     a->setEnabled( en );
358 }
359
360 /*!
361   \return action for separator
362   If this action doesn't exist, then it will be created
363   \param individual - if it is false, then action will be shared, otherwise it will be created on every call
364 */
365 QAction* QtxActionMgr::separator( const bool individual )
366 {
367   if ( individual )
368     return new SeparatorAction();
369
370   if ( !qtx_separator_action )
371   {
372     qtx_separator_action = new SeparatorAction();
373     qAddPostRoutine( qtxSeparatorActionCleanup );
374   }
375   return qtx_separator_action;
376 }
377
378 /*!
379         Class: QtxActionMgr::Reader
380         Level: Public
381 */
382
383 /*!
384   Constructor
385 */
386 QtxActionMgr::Reader::Reader()
387 {
388 }
389
390 /*!
391   Destructor
392 */
393 QtxActionMgr::Reader::~Reader()
394 {
395 }
396
397 /*!
398   \return list of options
399 */
400 QStringList QtxActionMgr::Reader::options() const
401 {
402   return myOptions.keys();
403 }
404
405 /*!
406   \return value of option
407   \param name - option name
408   \param def - default option value (is returned, if there is no such option)
409 */
410 QString QtxActionMgr::Reader::option( const QString& name, const QString& def ) const
411 {
412   if( myOptions.contains( name ) )
413     return myOptions[ name ];
414   else
415     return def;
416 }
417
418 /*!
419   Sets value of option
420   \param name - option name
421   \param value - option value
422 */
423 void QtxActionMgr::Reader::setOption( const QString& name, const QString& value )
424 {
425   myOptions[ name ] = value;
426 }
427
428
429 /*!
430   Constructor
431 */
432 QtxActionMgr::XMLReader::XMLReader( const QString& root,
433                                     const QString& item,
434                                     const QString& dir )
435 : Reader()
436 {
437   setOption( QString( "root_tag" ),  root );
438   setOption( QString( "menu_item" ), item );
439   setOption( QString( "icons_dir" ), dir  );
440   setOption( QString( "id" ),        QString( "item-id" ) );
441   setOption( QString( "pos" ),       QString( "pos-id" ) );
442   setOption( QString( "group" ),     QString( "group-id" ) );
443   setOption( QString( "label" ),     QString( "label-id" ) );
444   setOption( QString( "tooltip" ),   QString( "tooltip-id" ) );
445   setOption( QString( "accel" ),     QString( "accel-id" ) );
446   setOption( QString( "separator" ), QString( "separator" ) );
447   setOption( QString( "icon" ),      QString( "icon-id" ) );
448   setOption( QString( "toggle" ),    QString( "toggle-id" ) );
449 }
450
451 /*!
452   Destructor
453 */
454 QtxActionMgr::XMLReader::~XMLReader()
455 {
456 }
457
458 /*!
459   Reads file and fills action manager with help of creator
460   \param fname - file name
461   \param cr - creator
462 */
463 bool QtxActionMgr::XMLReader::read( const QString& fname, Creator& cr ) const
464 {
465   bool res = false;  
466
467 #ifndef QT_NO_DOM
468
469   QFile file( fname );
470   if ( !file.open( IO_ReadOnly ) )
471     return res;
472
473   QDomDocument doc;
474
475   res = doc.setContent( &file );
476   file.close();
477
478   if ( !res )
479     return res;
480
481   QString root = option( "root_tag" );
482   for( QDomNode cur = doc.documentElement(); !cur.isNull(); )
483   {
484     if( cur.isElement() && isNodeSimilar( cur, root ) )
485       read( cur, -1, cr );
486     else if( cur.hasChildNodes() )
487     {
488       cur = cur.firstChild();
489       continue;
490     }
491
492     while( !cur.isNull() && cur.nextSibling().isNull() )
493       cur = cur.parentNode();
494     if( !cur.isNull() )
495       cur = cur.nextSibling();
496   }
497
498 #endif
499
500   return res;
501 }
502
503 /*!
504   Create item by xml node
505   \param parent_node - parent node
506   \param parent_id - parent id
507   \param cr - creator
508 */
509 void QtxActionMgr::XMLReader::read( const QDomNode& parent_node,
510                                     const int parent_id,
511                                     Creator& cr ) const
512 {
513   if( parent_node.isNull() )
514     return;
515
516   QStringList items = QStringList::split( "|", option( QString( "menu_item" ) ) );
517
518   const QDomNodeList& children = parent_node.childNodes();
519   for( int i=0, n=children.count(); i<n; i++ )
520   {
521     QDomNode node = children.item( i );
522     //QString n = node.nodeName();
523     if( node.isElement() /*&& node.hasAttributes()*/ &&
524         ( items.contains( node.nodeName() ) || node.nodeName()==option( "separator" ) ) )
525     {
526       QDomNamedNodeMap map = node.attributes();
527       ItemAttributes attrs;
528
529       for( int i=0, n=map.count(); i<n; i++ )
530         if( map.item( i ).isAttr() )
531         {
532           QDomAttr a = map.item( i ).toAttr();
533           attrs.insert( a.name(), a.value() );
534         }
535
536       int newId = cr.append( node.nodeName(), node.hasChildNodes(), attrs, parent_id );
537       if( node.hasChildNodes() )
538         read( node, newId, cr );
539     }
540   }
541 }
542
543 /*!
544   \return true if node satisfies pattern
545 */
546 bool QtxActionMgr::XMLReader::isNodeSimilar( const QDomNode& node,
547                                              const QString& pattern ) const
548 {
549   if( node.nodeName()==pattern )
550     return true;
551   
552   QDomDocument temp;
553   QString mes;
554   temp.setContent( pattern, true, &mes );
555
556   const QDomNamedNodeMap &temp_map = temp.documentElement().attributes(),
557                          &cur_map = node.attributes();
558   bool ok = temp_map.count()>0;
559   for( int i=0, n=temp_map.count(); i<n && ok; i++ )
560   {
561     QDomAttr a = temp_map.item( i ).toAttr(),
562              b = cur_map.namedItem( a.name() ).toAttr();
563     ok = !b.isNull() && a.name()==b.name() && a.value()==b.value();
564   }
565
566   return ok;
567 }
568
569
570 /*!
571   \return integer value by attributes
572   \param attrs - attributes
573   \param name - name of attribute
574   \param def - default value (is returned on fail)
575 */
576 int QtxActionMgr::Creator::intValue( const ItemAttributes& attrs,
577                                      const QString& name, int def )
578 {
579   if( attrs.contains( name ) )
580   {
581     bool ok; 
582     int res = attrs[ name ].toInt( &ok );
583     if( ok )
584       return res;
585   }
586   return def;
587 }
588
589 /*!
590   \return string value by attributes
591   \param attrs - attributes
592   \param name - name of attribute
593   \param def - default value (is returned on fail)
594 */
595 QString QtxActionMgr::Creator::strValue( const ItemAttributes& attrs,
596                                          const QString& name,
597                                          const QString& def  )
598 {
599   if( attrs.contains( name ) )
600     return attrs[ name ];
601   else
602     return def;
603 }
604
605 /*!
606    Constructor
607 */
608 QtxActionMgr::Creator::Creator( QtxActionMgr::Reader* r )
609 : myReader( r )
610 {
611 }
612
613 /*!
614    Destructor
615 */
616 QtxActionMgr::Creator::~Creator()
617 {
618 }
619
620 /*!
621   \return corresponding reader
622 */
623 QtxActionMgr::Reader* QtxActionMgr::Creator::reader() const
624 {
625   return myReader;
626 }
627
628 /*!
629   Connects action to some slots (default implementation is empty)
630 */
631 void QtxActionMgr::Creator::connect( QAction* ) const
632 {
633 }
634
635 /*!
636   Loads pixmap 
637   \param fname - file name
638   \param pix - to return loaded pixmap
639 */
640 bool QtxActionMgr::Creator::loadPixmap( const QString& fname, QPixmap& pix ) const
641 {
642   if( !reader() )
643     return false;
644
645   QStringList dirlist = QStringList::split( ";", reader()->option( "icons_dir", "." ) );
646   QStringList::const_iterator anIt = dirlist.begin(),
647                               aLast = dirlist.end();
648   bool res = false;
649   for( ; anIt!=aLast && !res; anIt++ )
650     res = pix.load( Qtx::addSlash( *anIt ) + fname );
651
652   return res;
653 }