Salome HOME
Initial version
[modules/gui.git] / src / Qtx / QtxActionMenuMgr.cxx
1 // File:      QtxActionMenuMgr.cxx
2 // Author:    Alexander SOLOVYEV, Sergey TELKOV
3
4 #include "QtxActionMenuMgr.h"
5
6 #include "QtxAction.h"
7
8 #include <qwidget.h>
9 #include <qmenubar.h>
10 #include <qpopupmenu.h>
11 #include <qwidgetlist.h>
12 #include <qobjectlist.h>
13 #include <qmainwindow.h>
14 #include <qfile.h>
15 #include <qdom.h>
16
17 /*!
18         Class: QtxActionMenuMgr::MenuAction
19         Level: Internal
20 */
21
22 class QtxActionMenuMgr::MenuAction : public QtxAction
23 {
24 public:
25   MenuAction( const QString&, const QString&, QObject* );
26   virtual ~MenuAction();
27
28   virtual bool addTo( QWidget* );
29
30   virtual bool removeFrom( QWidget* );
31
32   QPopupMenu*  popup() const;
33
34 private:
35   int          myId;
36   QPopupMenu*  myPopup;
37 };
38
39 QtxActionMenuMgr::MenuAction::MenuAction( const QString& text, const QString& menuText, QObject* parent )
40 : QtxAction( text, menuText, 0, parent ),
41 myId( -1 ),
42 myPopup( 0 )
43 {
44   myPopup = new QPopupMenu();
45 }
46
47 QtxActionMenuMgr::MenuAction::~MenuAction()
48 {
49   delete myPopup;
50 }
51
52 bool QtxActionMenuMgr::MenuAction::addTo( QWidget* w )
53 {
54   if ( myId != -1 || !w )
55     return false;
56
57   if ( !w->inherits( "QPopupMenu" ) && !w->inherits( "QMenuBar" ) )
58     return false;
59
60   if ( w->inherits( "QPopupMenu" ) && QAction::addTo( w ) )
61   {
62     QPopupMenu* pm = (QPopupMenu*)w;
63     myId = pm->idAt( pm->count() - 1 );
64     setPopup( pm, myId, myPopup );
65   }
66   else if ( w->inherits( "QMenuBar" ) )
67   {
68     QMenuBar* mb = (QMenuBar*)w;
69     myId = iconSet().isNull() ? mb->insertItem( menuText(), myPopup ) :
70                                 mb->insertItem( iconSet(), menuText(), myPopup );
71     mb->setItemEnabled( myId, isEnabled() );
72   }
73   else
74     return false;
75
76   return true;
77 }
78
79 bool QtxActionMenuMgr::MenuAction::removeFrom( QWidget* w )
80 {
81   if ( w->inherits( "QPopupMenu" ) && QAction::removeFrom( w ) )
82     myId = -1;
83   else if ( w->inherits( "QMenuBar" ) )
84   {
85     QMenuBar* mb = (QMenuBar*)w;
86     mb->removeItem( myId );
87     myId = -1;
88   }
89
90   return myId == -1;
91 }
92
93 QPopupMenu* QtxActionMenuMgr::MenuAction::popup() const
94 {
95   return myPopup;
96 }
97
98 /*!
99         Class: QtxActionMenuMgr
100         Level: Public
101 */
102
103
104 QtxActionMenuMgr::QtxActionMenuMgr( QMainWindow* p )
105 : QtxActionMgr( p ),
106 myMenu( p ? p->menuBar() : 0 )
107 {
108   myRoot.id = -1;
109   myRoot.group = -1;
110
111   if ( myMenu )
112     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
113 }
114
115 QtxActionMenuMgr::QtxActionMenuMgr( QWidget* mw, QObject* p )
116 : QtxActionMgr( p ),
117 myMenu( mw )
118 {
119   myRoot.id = -1;
120   myRoot.group = -1;
121
122   if ( myMenu )
123     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
124 }
125
126 QtxActionMenuMgr::~QtxActionMenuMgr()
127 {
128   for ( NodeListIterator it( myRoot.children ); it.current(); ++it )
129   {
130     QAction* a = itemAction( it.current()->id );
131     if ( !a )
132       a = menuAction( it.current()->id );
133
134     if ( a )
135       a->removeFrom( myMenu );
136   }
137
138   for ( MenuMap::Iterator itr = myMenus.begin(); itr != myMenus.end(); ++itr )
139     delete itr.data();
140 }
141
142 bool QtxActionMenuMgr::isVisible( const int actId, const int place ) const
143 {
144   MenuNode* node = find( actId, place );
145   return node && node->visible;
146 }
147
148 void QtxActionMenuMgr::setVisible( const int actId, const int place, const bool v )
149 {
150   MenuNode* node = find( actId, place );
151   if ( node )
152     node->visible = v;
153 }
154
155 int QtxActionMenuMgr::insert( const int id, const QString& menus, const int group, const int idx )
156 {
157   return insert( id, QStringList::split( "|", menus ), group, idx );
158 }
159
160 int QtxActionMenuMgr::insert( QAction* a, const QString& menus, const int group, const int idx )
161 {
162   return insert( a, QStringList::split( "|", menus ), group, idx );
163 }
164
165 int QtxActionMenuMgr::insert( const int id, const QStringList& menus, const int group, const int idx )
166 {
167   int pId = createMenu( menus, -1 );
168   if ( pId == -1 )
169     return -1;
170
171   return insert( id, pId, group, idx );
172 }
173
174 int QtxActionMenuMgr::insert( QAction* a, const QStringList& menus, const int group, const int idx )
175 {
176   int pId = createMenu( menus, -1 );
177   if ( pId == -1 )
178     return -1;
179
180   return insert( a, pId, group, idx );
181 }
182
183 int QtxActionMenuMgr::insert( const int id, const int pId, const int group, const int idx )
184 {
185   if ( id == -1 )
186     return -1;
187
188   MenuNode* pNode = pId == -1 ? &myRoot : find( pId );
189   if ( !pNode )
190     return -1;
191
192   MenuNode* node = new MenuNode( pNode );
193   node->id = id;
194   node->group = group;
195
196   if ( idx < 0 || idx >= (int)pNode->children.count() )
197     pNode->children.append( node );
198   else
199     pNode->children.insert( idx, node );
200
201   updateMenu( pNode, false );
202
203   return node->id;
204 }
205
206 int QtxActionMenuMgr::insert( QAction* a, const int pId, const int group, const int idx )
207 {
208   return insert( registerAction( a ), pId, group, idx );
209 }
210
211 int QtxActionMenuMgr::insert( const QString& title, const int pId, const int group, const int idx )
212 {
213   MenuNode* pNode = pId == -1 ? &myRoot : find( pId );
214   if ( !pNode )
215     return -1;
216
217   int id = -1;
218   for ( NodeListIterator it( pNode->children ); it.current() && id == -1; ++it )
219   {
220     if ( myMenus.contains( it.current()->id ) &&
221          clearTitle( myMenus[it.current()->id]->menuText() ) == clearTitle( title ) )
222       id = it.current()->id;
223   }
224
225   if ( id != -1 )
226     return id;
227
228   MenuAction* ma = new MenuAction( clearTitle( title ), title, this );
229
230   MenuNode* node = new MenuNode( pNode );
231   node->group = group;
232   node->id = myMenus.insert( generateId(), ma ).key();
233
234   if ( idx < 0 || idx >= (int)pNode->children.count() )
235     pNode->children.append( node );
236   else
237     pNode->children.insert( idx, node );
238
239   updateMenu( pNode, false );
240
241   return node->id;
242 }
243
244 int QtxActionMenuMgr::insert( const QString& title, const QString& menus, const int group, const int idx )
245 {
246   return insert( title, QStringList::split( "|", menus ), group, idx );
247 }
248
249 int QtxActionMenuMgr::insert( const QString& title, const QStringList& menus, const int group, const int idx )
250 {
251   int pId = createMenu( menus, -1 );
252   return insert( title, pId, group, idx );
253 }
254
255 int QtxActionMenuMgr::append( const QString& title, const int pId, const int group )
256 {
257   return insert( title, pId, group );
258 }
259
260 int QtxActionMenuMgr::append( const int id, const int pId, const int group )
261 {
262   return insert( id, pId, group );
263 }
264
265 int QtxActionMenuMgr::append( QAction* a, const int pId, const int group )
266 {
267   return insert( a, pId, group );
268 }
269
270 int QtxActionMenuMgr::prepend( const QString& title, const int pId, const int group )
271 {
272   return insert( title, pId, group, 0 );
273 }
274
275 int QtxActionMenuMgr::prepend( const int id, const int pId, const int group )
276 {
277   return insert( id, pId, group, 0 );
278 }
279
280 int QtxActionMenuMgr::prepend( QAction* a, const int pId, const int group )
281 {
282   return insert( a, pId, group, 0 );
283 }
284
285 void QtxActionMenuMgr::remove( const int id )
286 {
287   removeMenu( id, 0 );
288   update();
289 }
290
291 void QtxActionMenuMgr::remove( const int id, const int pId, const int group )
292 {
293   MenuNode* pNode = find( pId );
294   if ( !pNode )
295     return;
296
297   NodeList delNodes;
298   for ( NodeListIterator it( pNode->children ); it.current(); ++it )
299   {
300     if ( it.current()->id == id && ( it.current()->group == group || group == -1 ) )
301       delNodes.append( it.current() );
302   }
303
304   for ( NodeListIterator itr( delNodes ); itr.current(); ++itr )
305     pNode->children.remove( itr.current() );
306
307   updateMenu( pNode, false );
308 }
309
310 void QtxActionMenuMgr::show( const int id )
311 {
312   setShown( id, true );
313 }
314
315 void QtxActionMenuMgr::hide( const int id )
316 {
317   setShown( id, false );
318 }
319
320 bool QtxActionMenuMgr::isShown( const int id ) const
321 {
322   bool res = false;
323   MenuNode* node = find( id );
324   if ( node )
325     res = node->visible;
326   return res;
327 }
328
329 void QtxActionMenuMgr::setShown( const int id, const bool on )
330 {
331   NodeList aNodes;
332   find( id, aNodes );
333
334   QMap<MenuNode*, int> updMap;
335   for ( NodeListIterator it( aNodes ); it.current(); ++it )
336   {
337     if ( it.current()->visible != on )
338     {
339       it.current()->visible = on;
340       updMap.insert( it.current()->parent, 0 );
341     }
342   }
343
344   for ( QMap<MenuNode*, int>::ConstIterator itr = updMap.begin(); itr != updMap.end(); ++itr )
345     updateMenu( itr.key(), false );
346 }
347
348 void QtxActionMenuMgr::onDestroyed( QObject* obj )
349 {
350   if ( myMenu == obj )
351     myMenu = 0;
352 }
353
354 void QtxActionMenuMgr::setWidget( QWidget* mw )
355 {
356   if ( myMenu == mw )
357     return;
358
359   if ( myMenu )
360     disconnect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
361
362   myMenu = mw;
363
364   if ( myMenu )
365     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
366 }
367
368 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int actId, const int pId ) const
369 {
370   return find( actId, find( pId ) );
371 }
372
373 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int id, MenuNode* startNode ) const
374 {
375   MenuNode* node = 0;
376   MenuNode* start = startNode ? startNode : (MenuNode*)&myRoot;
377   for ( NodeListIterator it( start->children ); it.current() && !node; ++it )
378   {
379     if ( it.current()->id == id )
380       node = it.current();
381     else
382       node = find( id, it.current() );
383   }
384   return node;
385 }
386
387 bool QtxActionMenuMgr::find( const int id, NodeList& lst, MenuNode* startNode ) const
388 {
389   MenuNode* start = startNode ? startNode : (MenuNode*)&myRoot;
390   for ( NodeListIterator it( start->children ); it.current(); ++it )
391   {
392     if ( it.current()->id == id )
393       lst.prepend( it.current() );
394
395     find( id, lst, it.current() );
396   }
397   return !lst.isEmpty();
398 }
399
400 void QtxActionMenuMgr::removeMenu( const int id, MenuNode* startNode )
401 {
402   MenuNode* start = startNode ? startNode : &myRoot;
403   for ( NodeListIterator it( start->children ); it.current(); ++it )
404   {
405     if ( it.current()->id == id )
406       start->children.remove( it.current() );
407     else
408       removeMenu( id, it.current() );
409   }
410 }
411
412 QAction* QtxActionMenuMgr::itemAction( const int id ) const
413 {
414   return action( id );
415 }
416
417 QtxActionMenuMgr::MenuAction* QtxActionMenuMgr::menuAction( const int id ) const
418 {
419   MenuAction* a = 0;
420
421   if ( myMenus.contains( id ) )
422     a = myMenus[id];
423
424   return a;
425 }
426
427 void QtxActionMenuMgr::updateMenu( MenuNode* startNode, const bool rec, const bool updParent )
428 {
429   if ( !isUpdatesEnabled() )
430     return;
431
432   MenuNode* node = startNode ? startNode : &myRoot;
433
434   QWidget* mw = menuWidget( node );
435   if ( !mw )
436     return;
437
438   bool filled = checkWidget( mw );
439
440   for ( NodeListIterator it1( node->children ); it1.current(); ++it1 )
441   {
442     QAction* a = itemAction( it1.current()->id );
443     if ( !a )
444       a = menuAction( it1.current()->id );
445
446     if ( a )
447       a->removeFrom( mw );
448   }
449
450   if ( node != &myRoot )
451   {
452     if ( mw->inherits( "QMenuBar" ) )
453       ((QMenuBar*)mw)->clear();
454     else if ( mw->inherits( "QPopupMenu" ) )
455       ((QPopupMenu*)mw)->clear();
456   }
457
458   QMap<int, NodeList> idMap;
459   for ( NodeListIterator it2( node->children ); it2.current(); ++it2 )
460   {
461     MenuNode* par = it2.current()->parent;
462     if ( isVisible( it2.current()->id, par ? par->id : -1 ) )
463     {
464       NodeList& lst = idMap[it2.current()->group];
465       lst.append( it2.current() );
466     }
467   }
468
469   QIntList groups = idMap.keys();
470   qHeapSort( groups );
471
472   groups.remove( -1 );
473   groups.append( -1 );
474
475   for ( QIntList::const_iterator gIt = groups.begin(); gIt != groups.end(); ++gIt )
476   {
477     if ( !idMap.contains( *gIt ) )
478       continue;
479
480     const NodeList& lst = idMap[*gIt];
481     for ( NodeListIterator iter( lst ); iter.current(); ++iter )
482     {
483       if ( rec )
484         updateMenu( iter.current(), rec, false );
485
486       QAction* a = itemAction( iter.current()->id );
487       if ( a )
488         a->addTo( mw );
489       else
490       {
491         MenuAction* ma = menuAction( iter.current()->id );
492         if ( ma && ma->popup() && ma->popup()->count() )
493           ma->addTo( mw );
494       }
495     }
496   }
497
498   simplifySeparators( mw );
499
500   if ( updParent && node->parent && filled != checkWidget( mw ) )
501     updateMenu( node->parent, false );
502 }
503
504 void QtxActionMenuMgr::internalUpdate()
505 {
506   if ( isUpdatesEnabled() )
507     updateMenu();
508 }
509
510 bool QtxActionMenuMgr::checkWidget( QWidget* wid ) const
511 {
512   if ( !wid )
513     return false;
514
515   QMenuData* md = 0;
516   if ( wid->inherits( "QPopupMenu" ) )
517     md = (QPopupMenu*)wid;
518   else if ( wid->inherits( "QMenuBar" ) )
519     md = (QMenuBar*)wid;
520
521   return md->count();
522 }
523
524 QWidget* QtxActionMenuMgr::menuWidget( MenuNode* node) const
525 {
526   if ( !node || node == &myRoot )
527      return myMenu;
528
529   if ( !myMenus.contains( node->id ) || !myMenus[node->id] )
530     return 0;
531
532   return myMenus[node->id]->popup();
533 }
534
535 void QtxActionMenuMgr::simplifySeparators( QWidget* wid )
536 {
537   if ( wid && wid->inherits( "QPopupMenu" ) )
538     Qtx::simplifySeparators( (QPopupMenu*)wid, false );
539 }
540
541 QString QtxActionMenuMgr::clearTitle( const QString& txt ) const
542 {
543   QString res = txt;
544
545   for ( int i = 0; i < (int)res.length(); i++ )
546   {
547     if ( res.at( i ) == '&' )
548       res.remove( i--, 1 );
549   }
550
551   return res;
552 }
553
554 int QtxActionMenuMgr::createMenu( const QStringList& lst, const int pId )
555 {
556   if ( lst.isEmpty() )
557     return -1;
558
559   QStringList sl( lst );
560
561   QString title = sl.last().stripWhiteSpace();
562   sl.remove( sl.fromLast() );
563
564   int parentId = sl.isEmpty() ? pId : createMenu( sl, pId );
565
566   return insert( title, parentId, -1 );
567 }
568
569 bool QtxActionMenuMgr::load( const QString& fname, QtxActionMgr::Reader& r )
570 {
571   MenuCreator cr( &r, this );
572   return r.read( fname, cr );
573 }
574
575
576 /*!
577         Class: QtxActionMenuMgr::MenuCreator
578         Level: Public
579 */
580
581 QtxActionMenuMgr::MenuCreator::MenuCreator( QtxActionMgr::Reader* r,
582                                             QtxActionMenuMgr* mgr )
583 : QtxActionMgr::Creator( r ),
584   myMgr( mgr )
585 {
586 }
587
588 QtxActionMenuMgr::MenuCreator::~MenuCreator()
589 {
590 }
591
592 int QtxActionMenuMgr::MenuCreator::append( const QString& tag, const bool subMenu,
593                                            const ItemAttributes& attr, const int pId )
594 {
595   if( !myMgr || !reader() )
596     return -1;
597
598   QString label   = reader()->option( "label",     "label"     ),
599           id      = reader()->option( "id",        "id"        ),
600           pos     = reader()->option( "pos",       "pos"       ),
601           group   = reader()->option( "group",     "group"     ),
602           tooltip = reader()->option( "tooltip",   "tooltip"   ),
603           sep     = reader()->option( "separator", "separator" ),
604           accel   = reader()->option( "accel",     "accel"     ),
605           icon    = reader()->option( "icon",      "icon"      ),
606           toggle  = reader()->option( "toggle",    "toggle"    );
607
608   int res = -1, actId = intValue( attr, id, -1 );
609
610   if( subMenu )
611     res = myMgr->insert( strValue( attr, label ), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
612   else if( tag==sep )
613     res = myMgr->insert( separator(), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
614   else
615   {
616     QPixmap pix; QIconSet set;
617     QString name = strValue( attr, icon );
618     if( !name.isEmpty() && loadPixmap( name, pix ) )
619       set = QIconSet( pix );
620
621     QtxAction* newAct = new QtxAction( strValue( attr, tooltip ), set,
622                                        strValue( attr, label ), 
623                                        QKeySequence( strValue( attr, accel ) ),
624                                        myMgr );
625     newAct->setToolTip( strValue( attr, tooltip ) );
626     QString toggleact = strValue( attr, toggle );
627     newAct->setToggleAction( !toggleact.isEmpty() );
628     newAct->setOn( toggleact.lower()=="true" );
629         
630     connect( newAct );
631     int aid = myMgr->registerAction( newAct, actId );
632     res = myMgr->insert( aid, pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
633   }
634
635   return res;
636 }