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