Salome HOME
Join modifications from branch OCC_debug_for_3_2_0b1
[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 #include <qvaluelist.h>
35
36 // VSR: Uncomment this #define in order to allow dynamic menus support
37 // (emit signals when popup menu is pre-activated)
38 // Currently this support is disabled.
39 //#define ENABLE_DYNAMIC_MENU
40  
41 /*!
42         Service functions
43         Level: Internal
44 */
45 namespace {
46   QValueList<int> prepareIds( const QWidget* w )
47   {
48     QValueList<int> l;
49     const QMenuData* md = 0;
50     if ( w->inherits( "QMenuBar" ) )
51       md = dynamic_cast<const QMenuData*>( w );
52     else if ( w->inherits( "QPopupMenu" ) )
53       md = dynamic_cast<const QMenuData*>( w );
54     if ( md ) {
55       for ( int i=0; i < md->count(); i++ )
56         l.append( md->idAt( i ) );
57     }
58     return l;
59   }
60
61   int getNewId( const QWidget* w, const QValueList<int>& l, bool retId = true )
62   {
63     const QMenuData* md = 0;
64     if ( w->inherits( "QMenuBar" ) )
65       md = dynamic_cast<const QMenuData*>( w );
66     else if ( w->inherits( "QPopupMenu" ) )
67       md = dynamic_cast<const QMenuData*>( w );
68     if ( md ) {
69       for ( int i=0, j=0; i < md->count() && j < l.count(); i++, j++ )
70         if ( md->idAt( i ) != l[ j ] ) return retId ? md->idAt( i ) : i;
71       if ( md->count() > l.count() ) return retId ? md->idAt( md->count()-1 ) : md->count()-1;
72     }
73     return -1;
74   }
75
76   void dumpMenu( QWidget* w, bool before )
77   {
78     QMenuData* md = dynamic_cast<QMenuData*>( w );
79     if ( !w ) return;
80     printf(">>> start dump menu (%s) >>>\n", before ? "before" : "after" );
81     for( int i = 0; i < md->count(); i++ )
82       printf("%d: %d: %s\n",i,md->idAt(i),md->text(md->idAt(i)).latin1() );
83     printf("<<< end dump menu (%s) <<<\n", before ? "before" : "after" );
84   }
85 };
86
87 /*!
88         Class: QtxActionMenuMgr::MenuAction
89         Level: Internal
90 */
91
92 class QtxActionMenuMgr::MenuAction : public QtxAction
93 {
94 public:
95   MenuAction( const QString&, const QString&, QObject*, const int = -1, const bool = false );
96   virtual ~MenuAction();
97
98   virtual bool addTo( QWidget* );
99
100   virtual bool removeFrom( QWidget* );
101
102   QPopupMenu*  popup() const;
103
104 private:
105   int                myId;
106   QPopupMenu*        myPopup;
107   bool               myEmptyEnabled;
108   QMap<QWidget*,int> myIds;
109 };
110
111
112 /*!
113   Constructor for menu action
114   \param text - description text
115   \param menutext - menu text
116   \param parent - parent object
117   \param id - integer identificator of action
118   \param allowEmpty - if it is true, it makes possible to add this action with empty popup to menu
119 */
120
121 QtxActionMenuMgr::MenuAction::MenuAction( const QString& text,
122                                           const QString& menuText,
123                                           QObject*       parent,
124                                           const int      id,
125                                           const bool     allowEmpty )
126 : QtxAction( text, menuText, 0, parent ),
127   myId( id ),
128   myPopup( 0 ),
129   myEmptyEnabled( allowEmpty )
130 {
131   myPopup = new QPopupMenu();
132 }
133
134 /*!
135   Destructor: deletes internal popup
136 */
137 QtxActionMenuMgr::MenuAction::~MenuAction()
138 {
139   delete myPopup;
140 }
141
142 /*!
143   Adds action to widget, for example, to popup menu or menu bar
144 */
145 bool QtxActionMenuMgr::MenuAction::addTo( QWidget* w )
146 {
147   if ( !w ) 
148     return false;  // bad widget
149
150   if ( !w->inherits( "QPopupMenu" ) && !w->inherits( "QMenuBar" ) )
151     return false;  // not allowed widget type
152
153   if ( myIds.find( w ) != myIds.end() )
154     return false;  // already added
155
156   if ( !myPopup )
157     return false;  // bad own popup menu
158
159   if ( !myEmptyEnabled && !myPopup->count() )
160     return false;  // not allowed empty menu
161
162   if ( w->inherits( "QPopupMenu" )  ) {
163     QValueList<int> l = prepareIds( w );
164     int idx;
165     if ( QtxAction::addTo( w ) && ( idx = getNewId( w, l, false ) ) != -1 ) {
166       QPopupMenu* pm = (QPopupMenu*)w;
167       myIds[ w ] = pm->idAt( idx );
168       if ( myId != -1 ) 
169         pm->setId( idx, myId );
170       setPopup( pm, myId != -1 ? myId : myIds[ w ], myPopup );
171     }
172   }
173   else if ( w->inherits( "QMenuBar" ) ) {
174     QValueList<int> l = prepareIds( w );
175     int idx;
176     if ( QtxAction::addTo( w ) && ( idx = getNewId( w, l, false ) ) != -1 ) {
177       QMenuBar* mb = (QMenuBar*)w;
178       myIds[ w ] = mb->idAt( idx );
179       if ( myId != -1 ) 
180         mb->setId( idx, myId );
181       setPopup( mb, myId != -1 ? myId : myIds[ w ], myPopup );
182     }
183   }
184   else
185     return false;
186
187   return true;
188 }
189
190 /*!
191   Removes action from widget, for example, from popup menu or menu bar
192 */
193 bool QtxActionMenuMgr::MenuAction::removeFrom( QWidget* w )
194 {
195   if ( !w ) 
196     return false;  // bad widget
197
198   if ( !w->inherits( "QPopupMenu" ) && !w->inherits( "QMenuBar" ) )
199     return false;  // not allowed widget type
200
201   if ( myIds.find( w ) == myIds.end() )
202     return false;  // not yet added
203
204   if ( w->inherits( "QPopupMenu" ) ) {
205     if ( myId != -1 ) {
206       QPopupMenu* pm = (QPopupMenu*)w;
207       int idx = pm->indexOf( myId );
208       if ( idx != -1 ) pm->setId( idx, myIds[ w ] );
209     }
210     myIds.remove( w );
211     return QtxAction::removeFrom( w );;
212   }
213   else if ( w->inherits( "QMenuBar" ) )
214   {
215     if ( myId != -1 ) {
216       QMenuBar* mb = (QMenuBar*)w;
217       int idx = mb->indexOf( myId );
218       if ( idx != -1 ) mb->setId( idx, myIds[ w ] );
219     }
220     myIds.remove( w );
221     return QtxAction::removeFrom( w );
222   }
223   return false;
224 }
225
226 /*!
227   \return internal popup of action
228 */
229 QPopupMenu* QtxActionMenuMgr::MenuAction::popup() const
230 {
231   return myPopup;
232 }
233
234 /*!
235         Class: QtxActionMenuMgr
236         Level: Public
237 */
238 QtxActionMenuMgr::QtxActionMenuMgr( QMainWindow* p )
239 : QtxActionMgr( p ),
240   myMenu( p ? p->menuBar() : 0 )
241 {
242   myRoot.id = -1;
243   myRoot.group = -1;
244
245   if ( myMenu ) {
246     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
247 #ifdef ENABLE_DYNAMIC_MENU
248     if ( myMenu->inherits( "QMenuBar" ) )
249       connect( myMenu, SIGNAL( highlighted( int ) ), this, SLOT( onHighlighted( int ) ) );
250 #endif
251   }
252 }
253
254 /*!
255   Constructor
256 */
257 QtxActionMenuMgr::QtxActionMenuMgr( QWidget* mw, QObject* p )
258 : QtxActionMgr( p ),
259   myMenu( mw )
260 {
261   myRoot.id = -1;
262   myRoot.group = -1;
263
264   if ( myMenu )
265     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
266 }
267
268 /*!
269   Destructor
270 */
271 QtxActionMenuMgr::~QtxActionMenuMgr()
272 {
273   for ( NodeListIterator it( myRoot.children ); it.current() && myMenu; ++it )
274   {
275     QAction* a = itemAction( it.current()->id );
276     if ( !a )
277       a = menuAction( it.current()->id );
278
279     if ( a )
280       a->removeFrom( myMenu );
281   }
282
283   for ( MenuMap::Iterator itr = myMenus.begin(); itr != myMenus.end(); ++itr )
284     delete itr.data();
285 }
286
287 /*!
288   \return whether menu item corresponding to action is visible
289   \param actId - identificator of action
290   \param place - identificator of some parent action
291 */
292 bool QtxActionMenuMgr::isVisible( const int actId, const int place ) const
293 {
294   MenuNode* node = find( actId, place );
295   return node && node->visible;
296 }
297
298 /*!
299   Sets visible state of action
300   \param actId - identificator of action
301   \param place - identificator of some parent action
302   \param v - visibility state
303 */
304 void QtxActionMenuMgr::setVisible( const int actId, const int place, const bool v )
305 {
306   MenuNode* node = find( actId, place );
307   if ( node )
308     node->visible = v;
309 }
310
311 /*!
312   Insert action as children menu item 
313   \param id - identificator of action
314   \param menus - few names of parent menu items, separated by '|'. It means sequence of menu items,
315   for example "File|Edit" means File->Edit submenu. If submenu doesn't exist, it will be created.
316   \param group - group identificator
317   \param idx - index inside Qt menu
318 */
319 int QtxActionMenuMgr::insert( const int id, const QString& menus, const int group, const int idx )
320 {
321   return insert( id, QStringList::split( "|", menus ), group, idx );
322 }
323
324 /*!
325   Insert action as children menu item 
326   \param a - action
327   \param menus - few names of parent menu items, separated by '|'. It means sequence of menu items,
328   for example "File|Edit" means File->Edit submenu. If submenu doesn't exist, it will be created.
329   \param group - group identificator
330   \param idx - index inside Qt menu
331 */
332 int QtxActionMenuMgr::insert( QAction* a, const QString& menus, const int group, const int idx )
333 {
334   return insert( a, QStringList::split( "|", menus ), group, idx );
335 }
336
337 /*!
338   Insert action as children menu item 
339   \param id - identificator of action
340   \param menus - list of names of parent menu items, separated by |. It means sequence of menu items,
341   for example "File|Edit" means File->Edit submenu. If submenu doesn't exist, it will be created.
342   \param group - group identificator
343   \param idx - index inside Qt menu
344 */
345 int QtxActionMenuMgr::insert( const int id, const QStringList& menus, const int group, const int idx )
346 {
347   int pId = createMenu( menus, -1 );
348   if ( pId == -1 )
349     return -1;
350
351   return insert( id, pId, group, idx );
352 }
353
354 /*!
355   Insert action as children menu item 
356   \param a - action
357   \param menus - list of names of parent menu items. It means sequence of menu items,
358   for example "File|Edit" means File->Edit submenu. If submenu doesn't exist, it will be created.
359   \param group - group identificator
360   \param idx - index inside Qt menu
361 */
362 int QtxActionMenuMgr::insert( QAction* a, const QStringList& menus, const int group, const int idx )
363 {
364   int pId = createMenu( menus, -1 );
365   if ( pId == -1 )
366     return -1;
367
368   return insert( a, pId, group, idx );
369 }
370
371 /*!
372   Insert action as children menu item 
373   \param id - identificator of action
374   \param pId - identificator of action corresponding to parent menu item
375   \param group - group identificator
376   \param idx - index inside Qt menu
377 */
378 int QtxActionMenuMgr::insert( const int id, const int pId, const int group, const int idx )
379 {
380   if ( id == -1 )
381     return -1;
382
383   MenuNode* pNode = pId == -1 ? &myRoot : find( pId );
384   if ( !pNode )
385     return -1;
386
387   MenuNode* node = new MenuNode( pNode );
388   node->id = id;
389   node->idx = idx;
390   node->group = group;
391
392   pNode->children.append( node );
393
394   updateMenu( pNode, false );
395
396   return node->id;
397 }
398
399 /*!
400   Insert action as children menu item 
401   \param a - action
402   \param pId - identificator of action corresponding to parent menu item
403   \param group - group identificator
404   \param idx - index inside Qt menu
405 */
406 int QtxActionMenuMgr::insert( QAction* a, const int pId, const int group, const int idx )
407 {
408   return insert( registerAction( a ), pId, group, idx );
409 }
410
411 /*!
412   Create and insert action as children menu item 
413   \return identificator of inserted action
414   \param title - menu text of action
415   \param pId - identificator of action corresponding to parent menu item
416   \param group - group identificator
417   \param id - identificator of new action
418   \param idx - index inside Qt menu
419   \param allowEmpty - indicates, that it is possible to add this action with empty popup menu to other menu
420 */
421 int QtxActionMenuMgr::insert( const QString& title, const int pId, const int group, const int id, const int idx, const bool allowEmpty )
422 {
423   MenuNode* pNode = pId == -1 ? &myRoot : find( pId );
424   if ( !pNode )
425     return -1;
426
427   MenuNode* eNode = id == -1 ? 0 : find( id );
428
429   int fid = -1;
430   for ( NodeListIterator it( pNode->children ); it.current() && fid == -1; ++it )
431   {
432     if ( myMenus.contains( it.current()->id ) &&
433          clearTitle( myMenus[it.current()->id]->menuText() ) == clearTitle( title ) )
434       fid = it.current()->id;
435   }
436
437   if ( fid != -1 )
438     return fid;
439
440   int gid = (id == -1 || eNode ) ? generateId() : id;
441
442   MenuAction* ma = new MenuAction( clearTitle( title ), title, this, gid, allowEmpty );
443 #ifdef ENABLE_DYNAMIC_MENU
444   connect( ma->popup(), SIGNAL( highlighted( int ) ), this, SLOT( onHighlighted( int ) ) );
445 #endif
446
447   MenuNode* node = new MenuNode( pNode );
448   node->group = group;
449   node->idx = idx;
450   node->id = myMenus.insert( gid, ma ).key();
451
452   pNode->children.append( node );
453
454   updateMenu( pNode, false );
455
456   return node->id;
457 }
458
459 /*!
460   Create and insert action as children menu item 
461   \return identificator of inserted action
462   \param title - menu text of action
463   \param menus - string list of parents' menu texts, separated by |
464   \param group - group identificator
465   \param id - identificator of new action
466   \param idx - index inside Qt menu
467   \param allowEmpty - indicates, that it is possible to add this action with empty popup menu to other menu
468 */
469 int QtxActionMenuMgr::insert( const QString& title, const QString& menus, const int group, const int id, const int idx, const bool allowEmpty )
470 {
471   return insert( title, QStringList::split( "|", menus ), group, id, idx, allowEmpty );
472 }
473
474 /*!
475   Create and insert action as children menu item 
476   \return identificator of inserted action
477   \param title - menu text of action
478   \param menus - list of parents menu items
479   \param group - group identificator
480   \param id - identificator of new action
481   \param idx - index inside Qt menu
482   \param allowEmpty - indicates, that it is possible to add this action with empty popup menu to other menu
483 */
484 int QtxActionMenuMgr::insert( const QString& title, const QStringList& menus, const int group, const int id, const int idx, const bool allowEmpty )
485 {
486   int pId = createMenu( menus, -1 );
487   return insert( title, pId, group, id, idx, allowEmpty );
488 }
489
490 /*!
491   Create and append action as last children
492   \return identificator of inserted action
493   \param title - menu text of action
494   \param pId - id of action corresponding to parent menu item
495   \param group - group identificator
496   \param id - identificator of new action
497   \param allowEmpty - indicates, that it is possible to add this action with empty popup menu to other menu
498 */
499 int QtxActionMenuMgr::append( const QString& title, const int pId, const int group, const int id, const bool allowEmpty )
500 {
501   return insert( title, pId, group, id, allowEmpty );
502 }
503
504 /*!
505   Create and append action as last children
506   \return identificator of inserted action
507   \param id - identificator of existing action
508   \param pId - id of action corresponding to parent menu item
509   \param group - group identificator
510 */
511 int QtxActionMenuMgr::append( const int id, const int pId, const int group )
512 {
513   return insert( id, pId, group );
514 }
515
516 /*!
517   Create and append action as last children
518   \return identificator of inserted action
519   \param a - action
520   \param pId - id of action corresponding to parent menu item
521   \param group - group identificator
522 */
523 int QtxActionMenuMgr::append( QAction* a, const int pId, const int group )
524 {
525   return insert( a, pId, group );
526 }
527
528 /*!
529   Create and insert action as first children
530   \return identificator of inserted action
531   \param title - menu text of action
532   \param pId - id of action corresponding to parent menu item
533   \param group - group identificator
534   \param id - identificator of new action
535   \param allowEmpty - indicates, that it is possible to add this action with empty popup menu to other menu
536 */
537 int QtxActionMenuMgr::prepend( const QString& title, const int pId, const int group, const int id, const bool allowEmpty )
538 {
539   return insert( title, pId, group, id, 0, allowEmpty );
540 }
541
542 /*!
543   Create and insert action as last children
544   \return identificator of inserted action
545   \param id - identificator of existing action
546   \param pId - id of action corresponding to parent menu item
547   \param group - group identificator
548 */
549 int QtxActionMenuMgr::prepend( const int id, const int pId, const int group )
550 {
551   return insert( id, pId, group, 0 );
552 }
553
554 /*!
555   Create and insert action as last children
556   \return identificator of inserted action
557   \param a - action
558   \param pId - id of action corresponding to parent menu item
559   \param group - group identificator
560 */
561 int QtxActionMenuMgr::prepend( QAction* a, const int pId, const int group )
562 {
563   return insert( a, pId, group, 0 );
564 }
565
566 /*!
567   Removes menu item corresponding to action
568   \param id - identificator of action
569 */
570 void QtxActionMenuMgr::remove( const int id )
571 {
572   removeMenu( id, 0 );
573   update();
574 }
575
576 /*!
577   Removes menu item
578   \param id - identificator of action
579   \param pId - identificator of action corresponding to parent menu item
580   \param group - group identificator
581 */
582 void QtxActionMenuMgr::remove( const int id, const int pId, const int group )
583 {
584   MenuNode* pNode = pId == -1 ? &myRoot : find( pId );
585   if ( !pNode )
586     return;
587
588   NodeList delNodes;
589   for ( NodeListIterator it( pNode->children ); it.current(); ++it )
590   {
591     if ( it.current()->id == id && ( it.current()->group == group || group == -1 ) )
592       delNodes.append( it.current() );
593   }
594
595   for ( NodeListIterator itr( delNodes ); itr.current(); ++itr )
596     pNode->children.remove( itr.current() );
597
598   updateMenu( pNode, false );
599 }
600
601 /*!
602   Shows menu item corresponding to action
603   \param id - identificator of action
604 */
605 void QtxActionMenuMgr::show( const int id )
606 {
607   setShown( id, true );
608 }
609
610 /*!
611   Hides menu item corresponding to action
612   \param id - identificator of action
613 */
614 void QtxActionMenuMgr::hide( const int id )
615 {
616   setShown( id, false );
617 }
618
619 /*!
620   \return shown status of menu item corresponding to action
621   \param id - identificator of action
622 */
623 bool QtxActionMenuMgr::isShown( const int id ) const
624 {
625   bool res = false;
626   MenuNode* node = find( id );
627   if ( node )
628     res = node->visible;
629   return res;
630 }
631
632 /*!
633   Sets shown status of menu item corresponding to action
634   \param id - identificator of action
635   \param on - new shown status
636 */
637 void QtxActionMenuMgr::setShown( const int id, const bool on )
638 {
639   NodeList aNodes;
640   find( id, aNodes );
641
642   QMap<MenuNode*, int> updMap;
643   for ( NodeListIterator it( aNodes ); it.current(); ++it )
644   {
645     if ( it.current()->visible != on )
646     {
647       it.current()->visible = on;
648       updMap.insert( it.current()->parent, 0 );
649     }
650   }
651
652   for ( QMap<MenuNode*, int>::ConstIterator itr = updMap.begin(); itr != updMap.end(); ++itr )
653     updateMenu( itr.key(), false );
654 }
655
656 /*!
657   SLOT: called when corresponding menu is destroyed, clears internal pointer to menu
658 */
659 void QtxActionMenuMgr::onDestroyed( QObject* obj )
660 {
661   if ( myMenu == obj )
662     myMenu = 0;
663 }
664
665 /*!
666   SLOT: called when menu item is highlighted
667 */
668 void QtxActionMenuMgr::onHighlighted( int id )
669 {
670   const QObject* snd = sender();
671   int pid = 0, realId;
672   if ( myMenu && snd == myMenu )
673     pid = -1;
674   else {
675     for ( MenuMap::Iterator itr = myMenus.begin(); itr != myMenus.end(); ++itr ) {
676       if ( itr.data()->popup() && itr.data()->popup() == snd )
677         pid = itr.key();
678     }
679   }
680   if ( pid ) {
681     realId = findId( id, pid );
682     if ( realId != -1 ) {
683       bool updatesEnabled = isUpdatesEnabled();
684       setUpdatesEnabled( false );
685       emit menuHighlighted( pid, realId );
686       setUpdatesEnabled( updatesEnabled );
687       updateMenu( find( realId ) );
688     }
689   }
690 }
691
692 /*!
693   Assignes new menu with manager
694   \param mw - new menu
695 */
696 void QtxActionMenuMgr::setWidget( QWidget* mw )
697 {
698   if ( myMenu == mw )
699     return;
700
701   if ( myMenu )
702     disconnect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
703
704   myMenu = mw;
705
706   if ( myMenu )
707     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
708 }
709
710 /*!
711   \return menu node by it's place description
712   \param actId - identificator of action
713   \param pId - identificator of action corresponding to start menu item
714   \param rec - recursive search
715 */
716 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int actId, const int pId, const bool rec ) const
717 {
718   return find( actId, find( pId ), rec );
719 }
720
721 /*!
722   \return menu node by it's place description
723   \param actId - identificator of action
724   \param startNode - start menu item
725   \param rec - recursive search
726 */
727 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int id, MenuNode* startNode, const bool rec ) const
728 {
729   MenuNode* node = 0;
730   MenuNode* start = startNode ? startNode : (MenuNode*)&myRoot;
731   for ( NodeListIterator it( start->children ); it.current() && !node; ++it )
732   {
733     if ( it.current()->id == id )
734       node = it.current();
735     else if ( rec )
736       node = find( id, it.current(), rec );
737   }
738   return node;
739 }
740
741 /*!
742   Finds menu node
743   \return true if at least one node is found
744   \param id - identificator of action
745   \param lst - list to be filled with found nodes
746   \param startNode - start menu item
747 */
748 bool QtxActionMenuMgr::find( const int id, NodeList& lst, MenuNode* startNode ) const
749 {
750   MenuNode* start = startNode ? startNode : (MenuNode*)&myRoot;
751   for ( NodeListIterator it( start->children ); it.current(); ++it )
752   {
753     if ( it.current()->id == id )
754       lst.prepend( it.current() );
755
756     find( id, lst, it.current() );
757   }
758   return !lst.isEmpty();
759 }
760
761 /*!
762   Finds menu node
763   \return menu node
764   \param title - menu text of searched node
765   \param pId - id of action corresponding to start menu item
766   \param rec - recursive searching
767 */
768 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const QString& title, const int pId, const bool rec ) const
769 {
770   return find( title, find( pId ), rec );
771 }
772
773 /*!
774   Finds menu node
775   \return true if at least one node is found
776   \param title - menu text of node
777   \param lst - list to be filled with found nodes
778   \param startNode - start menu item
779 */
780 bool QtxActionMenuMgr::find( const QString& title, NodeList& lst, MenuNode* startNode ) const
781 {
782   MenuNode* start = startNode ? startNode : (MenuNode*)&myRoot;
783   for ( NodeListIterator it( start->children ); it.current(); ++it )
784   {
785     QAction* a = itemAction( it.current()->id );
786     if ( !a )
787       a = menuAction( it.current()->id );
788     if ( a && clearTitle( a->menuText() ) == clearTitle( title ) )
789       lst.prepend( it.current() );
790
791     find( title, lst, it.current() );
792   }
793   return !lst.isEmpty();
794 }
795
796 /*!
797   Finds menu node
798   \return menu node
799   \param title - menu text of searched node
800   \param startNode - start menu item
801   \param rec - recursive searching
802 */
803 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const QString& title, MenuNode* startNode, const bool rec ) const
804 {
805   MenuNode* node = 0;
806   MenuNode* start = startNode ? startNode : (MenuNode*)&myRoot;
807   for ( NodeListIterator it( start->children ); it.current() && !node; ++it )
808   {
809     QAction* a = itemAction( it.current()->id );
810     if ( !a )
811       a = menuAction( it.current()->id );
812     if ( a && clearTitle( a->menuText() ) == clearTitle( title ) )
813       node = it.current();
814     if ( !node && rec )
815       node = find( title, it.current(), rec );
816   }
817   return node;
818 }
819
820 /*!
821   Find id among children
822   \return id (>0) if on success or -1 on fail
823   \param id - id to be searched
824   \param pid - id of parent, among children of that 'id' must be searched
825 */
826 int QtxActionMenuMgr::findId( const int id, const int pid ) const
827 {
828   MenuNode* start = pid != -1 ? find( pid ) : (MenuNode*)&myRoot;
829   if ( start ) {
830     for ( NodeListIterator it( start->children ); it.current(); ++it ) {
831       if ( it.current()->id == id ) return id;
832     }
833   }
834   return -1;
835 }
836
837 /*!
838   Removes child
839   \param id - id of child to be removed
840   \param startNode - parent menu item
841 */
842 void QtxActionMenuMgr::removeMenu( const int id, MenuNode* startNode )
843 {
844   MenuNode* start = startNode ? startNode : &myRoot;
845   for ( NodeListIterator it( start->children ); it.current(); ++it )
846   {
847     if ( it.current()->id == id )
848       start->children.remove( it.current() );
849     else
850       removeMenu( id, it.current() );
851   }
852 }
853
854 /*!
855   \return menu item action by id
856   \param id - id of action
857 */
858 QAction* QtxActionMenuMgr::itemAction( const int id ) const
859 {
860   return action( id );
861 }
862
863 /*!
864   \return menu action by id
865   \param id - id of action
866 */
867 QtxActionMenuMgr::MenuAction* QtxActionMenuMgr::menuAction( const int id ) const
868 {
869   MenuAction* a = 0;
870
871   if ( myMenus.contains( id ) )
872     a = myMenus[id];
873
874   return a;
875 }
876
877 /*!
878   Updates menu ( isUpdatesEnabled() must return true )
879   \param startNode - first menu item to be updated
880   \param rec - recursive update
881   \param updParent - update also parent item (without recursion)
882   \sa isUpdatesEnabled()
883 */
884 void QtxActionMenuMgr::updateMenu( MenuNode* startNode, const bool rec, const bool updParent )
885 {
886   if ( !isUpdatesEnabled() )
887     return;
888
889   MenuNode* node = startNode ? startNode : &myRoot;
890
891   QWidget* mw = menuWidget( node );
892   if ( !mw )
893     return;
894
895   bool filled = checkWidget( mw );
896
897   for ( NodeListIterator it1( node->children ); it1.current(); ++it1 )
898   {
899     QAction* a = itemAction( it1.current()->id );
900     if ( !a )
901       a = menuAction( it1.current()->id );
902
903     if ( a )
904       a->removeFrom( mw );
905   }
906   /* VSR: commented to allow direct creating of menus by calling insertItem() methods
907   if ( mw->inherits( "QMenuBar" ) )
908     ((QMenuBar*)mw)->clear();
909   else if ( mw->inherits( "QPopupMenu" ) )
910     ((QPopupMenu*)mw)->clear();
911   */
912   QMap<int, NodeList> idMap;
913   for ( NodeListIterator it2( node->children ); it2.current(); ++it2 )
914   {
915     NodeList& lst = idMap[it2.current()->group];
916     int idx = it2.current()->idx;
917     if ( idx < 0 || idx >= lst.count() )
918       lst.append( it2.current() );
919     else
920       lst.insert( idx, it2.current() );
921   }
922
923   QIntList groups = idMap.keys();
924   qHeapSort( groups );
925
926   groups.remove( -1 );
927   groups.append( -1 );
928
929   for ( QIntList::const_iterator gIt = groups.begin(); gIt != groups.end(); ++gIt )
930   {
931     if ( !idMap.contains( *gIt ) )
932       continue;
933
934     const NodeList& lst = idMap[*gIt];
935     for ( NodeListIterator iter( lst ); iter.current(); ++iter )
936     {
937       MenuNode* par = iter.current()->parent;
938       if ( !isVisible( iter.current()->id, par ? par->id : -1 ) )
939         continue;
940
941       if ( rec )
942         updateMenu( iter.current(), rec, false );
943
944       QAction* a = itemAction( iter.current()->id );
945       if ( !a )
946         a = menuAction( iter.current()->id );
947       if ( a )
948         a->addTo( mw );
949     }
950   }
951
952   simplifySeparators( mw );
953
954   if ( updParent && node->parent && filled != checkWidget( mw ) )
955     updateMenu( node->parent, false );
956 }
957
958 /*!
959   Updates menu (virtual variant). To be redefined for custom activity on menu updating
960 */
961 void QtxActionMenuMgr::internalUpdate()
962 {
963   if ( isUpdatesEnabled() )
964     updateMenu();
965 }
966
967 /*!
968   \return true if widget is non-empty menu
969   \param wid - widget to be checked
970 */
971 bool QtxActionMenuMgr::checkWidget( QWidget* wid ) const
972 {
973   if ( !wid )
974     return false;
975
976   QMenuData* md = 0;
977   if ( wid->inherits( "QPopupMenu" ) )
978     md = (QPopupMenu*)wid;
979   else if ( wid->inherits( "QMenuBar" ) )
980     md = (QMenuBar*)wid;
981
982   return md ? md->count() : false;
983 }
984
985 /*!
986   \return popup of menu item
987   \param node - menu item
988 */
989 QWidget* QtxActionMenuMgr::menuWidget( MenuNode* node) const
990 {
991   if ( !node || node == &myRoot )
992      return myMenu;
993
994   if ( !myMenus.contains( node->id ) || !myMenus[node->id] )
995     return 0;
996
997   return myMenus[node->id]->popup();
998 }
999
1000 /*!
1001   Removes excess separators of menu 
1002   \param wid - menu to be processed
1003 */
1004 void QtxActionMenuMgr::simplifySeparators( QWidget* wid )
1005 {
1006   if ( wid && wid->inherits( "QPopupMenu" ) )
1007     Qtx::simplifySeparators( (QPopupMenu*)wid, false );
1008 }
1009
1010 /*!
1011   Removes special symbols (&) from string
1012   \param txt - string to be processed
1013   \return clear variant of string
1014 */
1015 QString QtxActionMenuMgr::clearTitle( const QString& txt ) const
1016 {
1017   QString res = txt;
1018
1019   for ( int i = 0; i < (int)res.length(); i++ )
1020   {
1021     if ( res.at( i ) == '&' )
1022       res.remove( i--, 1 );
1023   }
1024
1025   return res;
1026 }
1027
1028 /*!
1029   Creates and inserts many menu items 
1030   \param lst - list of menu texts
1031   \param pId - id of action corresponding to parent menu item
1032 */
1033 int QtxActionMenuMgr::createMenu( const QStringList& lst, const int pId )
1034 {
1035   if ( lst.isEmpty() )
1036     return -1;
1037
1038   QStringList sl( lst );
1039
1040   QString title = sl.last().stripWhiteSpace();
1041   sl.remove( sl.fromLast() );
1042
1043   int parentId = sl.isEmpty() ? pId : createMenu( sl, pId );
1044
1045   return insert( title, parentId, -1 );
1046 }
1047
1048 /*!
1049   Loads actions description from file
1050   \param fname - name of file
1051   \param r - reader of file
1052   \return true on success
1053 */
1054 bool QtxActionMenuMgr::load( const QString& fname, QtxActionMgr::Reader& r )
1055 {
1056   MenuCreator cr( &r, this );
1057   return r.read( fname, cr );
1058 }
1059
1060 /*!
1061   \return true if item has such child
1062   \param title - menu text of child
1063   \param pid - id of action corresponding to item
1064 */
1065 bool QtxActionMenuMgr::containsMenu( const QString& title, const int pid ) const
1066 {
1067   return (bool)find( title, pid, false );
1068 }
1069
1070 /*!
1071   \return true if item has such child
1072   \param id - id of action corresponding to child
1073   \param pid - id of action corresponding to item
1074 */
1075 bool QtxActionMenuMgr::containsMenu( const int id, const int pid ) const
1076 {
1077   return (bool)find( id, pid, false );
1078 }
1079
1080
1081 /*!
1082   Constructor
1083   \param r - menu reader
1084   \param mgr - menu manager
1085 */
1086 QtxActionMenuMgr::MenuCreator::MenuCreator( QtxActionMgr::Reader* r,
1087                                             QtxActionMenuMgr* mgr )
1088 : QtxActionMgr::Creator( r ),
1089   myMgr( mgr )
1090 {
1091 }
1092
1093 /*!
1094   Destructor
1095 */
1096 QtxActionMenuMgr::MenuCreator::~MenuCreator()
1097 {
1098 }
1099
1100 /*!
1101   Appends new menu items
1102   \param tag - tag of item
1103   \param subMenu - it has submenu
1104   \param attr - list of attributes
1105   \param pId - id of action corresponding to parent item
1106 */
1107 int QtxActionMenuMgr::MenuCreator::append( const QString& tag, const bool subMenu,
1108                                            const ItemAttributes& attr, const int pId )
1109 {
1110   if( !myMgr || !reader() )
1111     return -1;
1112
1113   QString label   = reader()->option( "label",     "label"     ),
1114           id      = reader()->option( "id",        "id"        ),
1115           pos     = reader()->option( "pos",       "pos"       ),
1116           group   = reader()->option( "group",     "group"     ),
1117           tooltip = reader()->option( "tooltip",   "tooltip"   ),
1118           sep     = reader()->option( "separator", "separator" ),
1119           accel   = reader()->option( "accel",     "accel"     ),
1120           icon    = reader()->option( "icon",      "icon"      ),
1121           toggle  = reader()->option( "toggle",    "toggle"    );
1122
1123   int res = -1, actId = intValue( attr, id, -1 );
1124
1125   if( subMenu )
1126     res = myMgr->insert( strValue( attr, label ), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
1127   else if( tag==sep )
1128     res = myMgr->insert( separator(), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
1129   else
1130   {
1131     QPixmap pix; QIconSet set;
1132     QString name = strValue( attr, icon );
1133     if( !name.isEmpty() && loadPixmap( name, pix ) )
1134       set = QIconSet( pix );
1135
1136     QtxAction* newAct = new QtxAction( strValue( attr, tooltip ), set,
1137                                        strValue( attr, label ), 
1138                                        QKeySequence( strValue( attr, accel ) ),
1139                                        myMgr );
1140     newAct->setToolTip( strValue( attr, tooltip ) );
1141     QString toggleact = strValue( attr, toggle );
1142     newAct->setToggleAction( !toggleact.isEmpty() );
1143     newAct->setOn( toggleact.lower()=="true" );
1144         
1145     connect( newAct );
1146     int aid = myMgr->registerAction( newAct, actId );
1147     res = myMgr->insert( aid, pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
1148   }
1149
1150   return res;
1151 }