Salome HOME
Merge from V6_main 01/04/2013
[modules/gui.git] / src / Qtx / QtxActionMenuMgr.cxx
1 // Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File:      QtxActionMenuMgr.cxx
24 // Author:    Alexander SOLOVYOV, Sergey TELKOV
25 //
26 #include "QtxActionMenuMgr.h"
27
28 #include "QtxAction.h"
29
30 #include <QMenu>
31 #include <QMenuBar>
32 #include <QWidget>
33 #include <QMainWindow>
34
35 /*!
36   \class QtxActionMenuMgr::MenuNode
37   \brief Represents a menu item inside main menu structure.
38   \internal
39 */
40
41 class QtxActionMenuMgr::MenuNode
42 {
43 public:
44   MenuNode();
45   MenuNode( MenuNode*, const int, const int, const int );
46   ~MenuNode();
47   
48   MenuNode* parent;       //!< parent menu node
49   int       id;           //!< menu nodeID
50   int       idx;          //!< menu node index 
51   int       group;        //!< menu group ID
52   bool      visible;      //!< visibility status
53   int       emptyEnabled; //!< enable empty menu flag
54   NodeList  children;     //!< children menu nodes list
55 };
56
57 /*!
58   \brief Default constructor.
59   \internal
60 */
61 QtxActionMenuMgr::MenuNode::MenuNode()
62   : parent( 0 ), id( -1 ), idx( -1 ), group( -1 ), visible( true ), emptyEnabled( 0 )
63 {
64 }
65
66 /*!
67   \brief Constructor.
68   \internal
69   \param p parent menu node
70   \param _id menu node ID
71   \param _idx menu node index
72   \param _group menu node group ID
73 */
74 QtxActionMenuMgr::MenuNode::MenuNode( MenuNode* p,
75                                       const int _id,
76                                       const int _idx,
77                                       const int _group )
78 : parent( p ), id( _id ), idx( _idx ), group( _group ), visible( true ), emptyEnabled( 0 )
79 {
80   if ( p )
81     p->children.append( this );
82 }
83
84 /*!
85   \brief Destructor.
86   \internal
87 */
88 QtxActionMenuMgr::MenuNode::~MenuNode()
89 {
90   for ( NodeList::iterator it = children.begin(); it != children.end(); ++it )
91     delete *it;
92 }
93
94 /*!
95   \class QtxActionMenuMgr
96   \brief Main menu actions manager.
97
98   Menu manager allows using of set of action for automatic generating of
99   application main menu and dynamic update of its contents.
100
101   Use insert(), append() and remove() methods to create main menu.
102   Methods show(), hide() allow displaying/erasing of specified menu items.
103
104   Actions can be grouped with help of group identificator. Inside the popup
105   or main menu bar menu items are ordered by the group identifier (ascending).
106
107   Menu manager automatically optimizes the menu by removing extra separators,
108   hiding empty popup menus etc.
109 */
110
111 /*!
112   \brief Constructor.
113   \param p parent main window
114 */
115 QtxActionMenuMgr::QtxActionMenuMgr( QMainWindow* p )
116 : QtxActionMgr( p ), 
117   myRoot( new MenuNode() ),
118   myMenu( p ? p->menuBar() : 0 )
119 {
120   if ( myMenu ) {
121     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
122   }
123 }
124
125 /*!
126   \brief Constructor.
127   \param mw menu widget
128   \param p parent object
129 */
130 QtxActionMenuMgr::QtxActionMenuMgr( QWidget* mw, QObject* p )
131 : QtxActionMgr( p ), 
132   myRoot( new MenuNode() ),
133   myMenu( mw )
134 {
135   if ( myMenu ) {
136     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
137   }
138 }
139
140 /*!
141   \brief Destructor.
142 */
143 QtxActionMenuMgr::~QtxActionMenuMgr()
144 {
145   for ( MenuMap::Iterator itr = myMenus.begin(); itr != myMenus.end(); ++itr )
146   {
147     QPointer<QAction> a = itr.value();
148     delete a->menu();
149     delete a;
150   }
151
152   delete myRoot;
153 }
154
155 /*!
156   \brief Check if an action with \a actId identifier is visible to
157   the parent action with \a place identifier.
158   \param actId action ID
159   \param place some parent action ID
160   \return \c true if an action is visible to the parent
161   \sa setVisible()
162 */
163 bool QtxActionMenuMgr::isVisible( const int actId, const int place ) const
164 {
165   MenuNode* node = find( actId, place );
166   return node && node->visible;
167 }
168
169 /*!
170   \brief Set action's visibility flag.
171   \param actId action ID
172   \param place some parent action ID
173   \param v new visibility state
174   \sa isVisible()
175 */
176 void QtxActionMenuMgr::setVisible( const int actId, const int place, const bool v )
177 {
178   MenuNode* node = find( actId, place );
179   if ( node )
180     node->visible = v;
181 }
182
183 /*!
184   \brief Insert action to the menu.
185
186   Insert an action to the named menu. The \a menus parameter represents 
187   the menu name: it can be a sequence of strings, separated by '|' symbol.
188   For example, "File|Edit" means \c File->Edit submenu.
189   If submenu doesn't exist, it will be created.
190
191   \param id action ID
192   \param menus menu name
193   \param group group ID
194   \param idx menu index inside the menu group
195   \return action ID
196 */
197 int QtxActionMenuMgr::insert( const int id, const QString& menus, const int group, const int idx )
198 {
199   return insert( id, menus.split( "|", QString::SkipEmptyParts ), group, idx );
200 }
201
202 /*!
203   \brief Insert action to the menu.
204
205   Insert an action to the named menu. The \a menus parameter represents 
206   the menu name: it can be a sequence of strings, separated by '|' symbol.
207   For example, "File|Edit" means \c File->Edit submenu.
208   If submenu doesn't exist, it will be created.
209
210   \param a action
211   \param menus menu name
212   \param group group ID
213   \param idx menu index inside the menu group
214   \return action ID
215 */
216 int QtxActionMenuMgr::insert( QAction* a, const QString& menus, const int group, const int idx )
217 {
218   return insert( a, menus.split( "|", QString::SkipEmptyParts ), group, idx );
219 }
220
221 /*!
222   \brief Insert action to the menu.
223
224   Insert an action to the named menu. The \a menus parameter represents 
225   the menu names list.
226   For example, string list consisting from two items "File" and "Edit"
227   means \c File->Edit submenu.
228   If submenu doesn't exist, it will be created.
229
230   \param id action ID
231   \param menus menu names list
232   \param group group ID
233   \param idx menu index inside the menu group
234   \return action ID
235 */
236 int QtxActionMenuMgr::insert( const int id, const QStringList& menus, const int group, const int idx )
237 {
238   int pId = createMenu( menus, -1 );
239   if ( pId == -1 )
240     return -1;
241
242   return insert( id, pId, group, idx );
243 }
244
245 /*!
246   \brief Insert action to the menu.
247
248   Insert an action to the named menu. The \a menus parameter represents 
249   the menu names list.
250   For example, string list consisting from two items "File" and "Edit"
251   means \c File->Edit submenu.
252   If submenu doesn't exist, it will be created.
253
254   \param a action
255   \param menus menu names list
256   \param group group ID
257   \param idx menu index inside the menu group
258   \return action ID
259 */
260 int QtxActionMenuMgr::insert( QAction* a, const QStringList& menus, const int group, const int idx )
261 {
262   int pId = createMenu( menus, -1 );
263   if ( pId == -1 )
264     return -1;
265
266   return insert( a, pId, group, idx );
267 }
268
269 /*!
270   \brief Insert action to the menu.
271   \param id action ID
272   \param pId parent menu action ID
273   \param group group ID
274   \param idx menu index inside the menu group
275   \return action ID
276 */
277 int QtxActionMenuMgr::insert( const int id, const int pId, const int group, const int idx )
278 {
279   if ( id == -1 )
280     return -1;
281
282   MenuNode* pNode = pId == -1 ? myRoot : find( pId );
283   if ( !pNode )
284     return -1;
285
286   MenuNode* node = new MenuNode( pNode, id, idx, group );
287
288   triggerUpdate( pNode->id, false );
289
290   return node->id;
291 }
292
293 /*!
294   \brief Insert action to the menu.
295   \param a action
296   \param pId parent menu action ID
297   \param group group ID
298   \param idx menu index inside the menu group
299   \return action ID
300 */
301 int QtxActionMenuMgr::insert( QAction* a, const int pId, const int group, const int idx )
302 {
303   return insert( registerAction( a ), pId, group, idx );
304 }
305
306 /*!
307   \brief Create and insert menu item action to the menu.
308   \param title menu text
309   \param pId parent menu action ID
310   \param group group ID
311   \param id action ID
312   \param idx menu index inside the menu group
313   \return action ID
314 */
315 int QtxActionMenuMgr::insert( const QString& title, const int pId, const int group, const int id, const int idx )
316 {
317   MenuNode* pNode = pId == -1 ? myRoot : find( pId );
318   if ( !pNode )
319     return -1;
320
321   MenuNode* eNode = id == -1 ? 0 : find( id );
322
323   int fid = -1;
324   for ( NodeList::iterator it = pNode->children.begin(); it != pNode->children.end() && fid == -1; ++it )
325   {
326     if ( myMenus.contains( (*it)->id ) &&
327          clearTitle( myMenus[(*it)->id]->text() ) == clearTitle( title ) )
328       fid = (*it)->id;
329   }
330
331   if ( fid != -1 )
332     return fid;
333
334   int gid = (id == -1 || eNode ) ? generateId() : id;
335
336   QMenu* menu = new QMenu( 0 );
337   QAction* ma = menu->menuAction();
338   ma->setText( title );
339
340   connect( ma->menu(), SIGNAL( aboutToShow() ), this, SLOT( onAboutToShow() ) );
341   connect( ma->menu(), SIGNAL( aboutToHide() ), this, SLOT( onAboutToHide() ) );
342
343   MenuNode* node = new MenuNode( pNode, myMenus.insert( gid, ma ).key(), idx, group );
344
345   triggerUpdate( pNode->id, false );
346
347   return node->id;
348 }
349
350 /*!
351   \brief Create and insert menu item action to the menu.
352
353   Insert an action to the named menu. The \a menus parameter represents 
354   the menu name: it can be a sequence of strings, separated by '|' symbol.
355   For example, "File|Edit" means \c File->Edit submenu.
356   If submenu doesn't exist, it will be created.
357
358   \param title menu text
359   \param menus menu name
360   \param group group ID
361   \param id action ID
362   \param idx menu index inside the menu group
363   \return action ID
364 */
365 int QtxActionMenuMgr::insert( const QString& title, const QString& menus, const int group, const int id, const int idx )
366 {
367   return insert( title, menus.split( "|", QString::SkipEmptyParts ), group, id, idx );
368 }
369
370 /*!
371   \brief Create and insert menu item action to the menu.
372
373   Insert an action to the named menu. The \a menus parameter represents 
374   the menu names list.
375   For example, string list consisting from two items "File" and "Edit"
376   means \c File->Edit submenu.
377   If submenu doesn't exist, it will be created.
378
379   \param title menu text
380   \param menus menu names list
381   \param group group ID
382   \param id action ID
383   \param idx menu index inside the menu group
384   \return action ID
385 */
386 int QtxActionMenuMgr::insert( const QString& title, const QStringList& menus, const int group, const int id, const int idx )
387 {
388   int pId = createMenu( menus, -1 );
389   return insert( title, pId, group, id, idx );
390 }
391
392 /*!
393   \brief Create and add menu item action to the end of menu.
394   \param title menu text
395   \param pId parent menu action ID
396   \param group group ID
397   \param id action ID
398   \return action ID
399 */
400 int QtxActionMenuMgr::append( const QString& title, const int pId, const int group, const int id )
401 {
402   return insert( title, pId, group, id );
403 }
404
405 /*!
406   \brief Create and add menu item action to the end of menu.
407   \param id action ID
408   \param pId parent menu action ID
409   \param group group ID
410   \return action ID
411 */
412 int QtxActionMenuMgr::append( const int id, const int pId, const int group )
413 {
414   return insert( id, pId, group );
415 }
416
417 /*!
418   \brief Create and add menu item action to the end of menu.
419   \param a action
420   \param pId parent menu action ID
421   \param group group ID
422   \return action ID
423 */
424 int QtxActionMenuMgr::append( QAction* a, const int pId, const int group )
425 {
426   return insert( a, pId, group );
427 }
428
429 /*!
430   \brief Create and add menu item action to the beginning of menu.
431   \param title menu text
432   \param pId parent menu action ID
433   \param group group ID
434   \param id action ID
435   \return action ID
436 */
437 int QtxActionMenuMgr::prepend( const QString& title, const int pId, const int group, const int id )
438 {
439   return insert( title, pId, group, id, 0 );
440 }
441
442 /*!
443   \brief Create and add menu item action to the beginning of menu.
444   \param id action ID
445   \param pId parent menu action ID
446   \param group group ID
447   \return action ID
448 */
449 int QtxActionMenuMgr::prepend( const int id, const int pId, const int group )
450 {
451   return insert( id, pId, group, 0 );
452 }
453
454 /*!
455   \brief Create and add menu item action to the beginning of menu.
456   \param a action
457   \param pId parent menu action ID
458   \param group group ID
459   \return action ID
460 */
461 int QtxActionMenuMgr::prepend( QAction* a, const int pId, const int group )
462 {
463   return insert( a, pId, group, 0 );
464 }
465
466 /*!
467   \brief Remove menu item with given \a id.
468   \param id menu action ID
469 */
470 void QtxActionMenuMgr::remove( const int id )
471 {
472   removeMenu( id, 0 );
473   update();
474 }
475
476 /*!
477   \brief Remove menu item with given \a id.
478   \param id menu action ID
479   \param pId parent menu action ID
480   \param group group ID
481 */
482 void QtxActionMenuMgr::remove( const int id, const int pId, const int group )
483 {
484   MenuNode* pNode = pId == -1 ? myRoot : find( pId );
485   if ( !pNode )
486     return;
487
488   NodeList delNodes;
489   for ( NodeList::iterator it = pNode->children.begin(); it != pNode->children.end(); ++it )
490   {
491     if ( (*it)->id == id && ( (*it)->group == group || group == -1 ) )
492       delNodes.append( *it );
493   }
494
495   QWidget* mW = menuWidget( pNode );
496   for ( NodeList::iterator itr = delNodes.begin(); itr != delNodes.end(); ++itr )
497   {
498     int id = (*itr)->id;
499     if( mW && menuAction( id ) )
500     {
501       mW->removeAction( menuAction( id ) );
502       myMenus.remove( id );
503     }
504     else if( mW && itemAction( id ) )
505       mW->removeAction( itemAction( id ) );
506     pNode->children.removeAll( *itr );
507   }
508
509   triggerUpdate( pNode->id, false );
510 }
511
512 /*!
513   \brief Show menu item with given \a id.
514   \param id menu action ID
515   \sa hide()
516 */
517 void QtxActionMenuMgr::show( const int id )
518 {
519   setShown( id, true );
520 }
521
522 /*!
523   \brief Hide menu item with given \a id.
524   \param id menu action ID
525   \sa show()
526 */
527 void QtxActionMenuMgr::hide( const int id )
528 {
529   setShown( id, false );
530 }
531
532 /*!
533   \brief Get visibility status for menu item with given \a id.
534   \param id menu action ID
535   \return \c true if an item is shown
536   \sa setShown()
537 */
538 bool QtxActionMenuMgr::isShown( const int id ) const
539 {
540   bool res = false;
541   MenuNode* node = find( id );
542   if ( node )
543     res = node->visible;
544   return res;
545 }
546
547 /*!
548   \brief Set visibility status for menu item with given \a id.
549   \param id menu action ID
550   \param on new visibility status
551   \sa isShown()
552 */
553 void QtxActionMenuMgr::setShown( const int id, const bool on )
554 {
555   NodeList aNodes;
556   find( id, aNodes );
557
558   for ( NodeList::iterator it = aNodes.begin(); it != aNodes.end(); ++it )
559   {
560     if ( (*it)->visible != on )
561     {
562       (*it)->visible = on;
563       triggerUpdate( (*it)->parent ? (*it)->parent->id : myRoot->id, false );
564     }
565   }
566 }
567
568 /*!
569   \brief Change menu title for the action with given \a id.
570   \param id menu action ID
571   \param title new menu title
572 */
573 void QtxActionMenuMgr::change( const int id, const QString& title )
574 {
575   QAction* a = menuAction( id );
576   if ( a )
577     a->setText( title );
578 }
579
580 /*!
581   \brief Called when the submenu is about to show.
582   
583   Emits the signal menuAboutToShow(QMenu*).
584 */
585 void QtxActionMenuMgr::onAboutToShow()
586 {
587   QMenu* m = ::qobject_cast<QMenu*>( sender() );
588   if ( m )
589     emit menuAboutToShow( m );
590 }
591
592 /*!
593   \brief Called when the submenu is about to hide.
594   
595   Emits the signal menuAboutToHide(QMenu*).
596 */
597 void QtxActionMenuMgr::onAboutToHide()
598 {
599   QMenu* m = ::qobject_cast<QMenu*>( sender() );
600   if ( m )
601     emit menuAboutToHide( m );
602 }
603
604 /*!
605   \brief Called when the corresponding menu object is destroyed.
606
607   Clears internal pointer to menu to disable crashes.
608   
609   \param obj (menu) object being destroyed
610 */
611 void QtxActionMenuMgr::onDestroyed( QObject* obj )
612 {
613   if ( myMenu == obj )
614     myMenu = 0;
615 }
616
617
618 /*!
619   \fn void QtxActionMenuMgr::menuAboutToShow( QMenu* m )
620   \brief Emitted when the menu is about to be shown.
621   \param m menu being shown
622 */
623
624 /*!
625   \fn void QtxActionMenuMgr::menuAboutToHide( QMenu* m )
626   \brief Emitted when the menu is about to be hidden.
627   \param m menu being hidden
628 */
629
630 /*!
631   \brief Get the menu widget.
632   \return menu widget (QMenuBar)
633 */
634 QWidget* QtxActionMenuMgr::menuWidget() const
635 {
636   return myMenu;
637 }
638
639 /*!
640   \brief Assign new menu widget to the menu manager.
641   \param mw new menu widget
642 */
643 void QtxActionMenuMgr::setMenuWidget( QWidget* mw )
644 {
645   if ( myMenu == mw )
646     return;
647
648   if ( myMenu )
649     disconnect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
650
651   myMenu = mw;
652
653   if ( myMenu )
654     connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
655
656   triggerUpdate( -1, true );
657 }
658
659 /*!
660   \brief Search menu node.
661   \param id menu action ID
662   \param pId parent menu item ID
663   \param rec if \c true perform recursive search
664   \return menu node or 0 if it is not found
665 */
666 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int id, const int pId, const bool rec ) const
667 {
668   return find( id, find( pId ), rec );
669 }
670
671 /*!
672   \brief Search menu node.
673   \param id menu action ID
674   \param startNode start menu node (if 0, search from root node)
675   \param rec if \c true perform recursive search
676   \return menu node or 0 if it is not found
677 */
678 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int id, MenuNode* startNode, const bool rec ) const
679 {
680   MenuNode* node = 0;
681   MenuNode* start = startNode ? startNode : myRoot;
682   for ( NodeList::iterator it = start->children.begin(); it != start->children.end() && !node; ++it )
683   {
684     if ( (*it)->id == id )
685       node = *it;
686     else if ( rec )
687       node = find( id, *it, rec );
688   }
689   return node;
690 }
691
692 /*!
693   \brief Search recursively all menu nodes with given \a id.
694   \param id menu action ID
695   \param NodeList resulting list of menu nodes
696   \param startNode start menu node (if 0, search from root node)
697   \return \c true if at least one node is found
698 */
699 bool QtxActionMenuMgr::find( const int id, NodeList& lst, MenuNode* startNode ) const
700 {
701   MenuNode* start = startNode ? startNode : myRoot;
702   for ( NodeList::iterator it = start->children.begin(); it != start->children.end(); ++it )
703   {
704     MenuNode* node = *it;
705     if ( node->id == id )
706       lst.prepend( node );
707
708     find( id, lst, node );
709   }
710   return !lst.isEmpty();
711 }
712
713 /*!
714   \brief Search menu node.
715   \param title menu item title
716   \param pId parent menu item ID
717   \param rec if \c true perform recursive search
718   \return menu node or 0 if it is not found
719 */
720 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const QString& title, const int pId, const bool rec ) const
721 {
722   return find( title, find( pId ), rec );
723 }
724
725 /*!
726   \brief Search recursively all menu nodes with given \a title.
727   \param title menu item title
728   \param NodeList resulting list of menu nodes
729   \param startNode start menu node (if 0, search from root node)
730   \return \c true if at least one node is found
731 */
732 bool QtxActionMenuMgr::find( const QString& title, NodeList& lst, MenuNode* startNode ) const
733 {
734   MenuNode* start = startNode ? startNode : myRoot;
735   for ( NodeList::iterator it = start->children.begin(); it != start->children.end(); ++it )
736   {
737     QAction* a = itemAction( (*it)->id );
738     if ( !a )
739       a = menuAction( (*it)->id );
740     if ( a && clearTitle( a->text() ) == clearTitle( title ) )
741       lst.prepend( *it );
742
743     find( title, lst, *it );
744   }
745   return !lst.isEmpty();
746 }
747
748 /*!
749   \brief Search menu node.
750   \param title menu item title
751   \param startNode start menu node (if 0, search from root node)
752   \param rec if \c true perform recursive search
753   \return menu node or 0 if it is not found
754 */
755 QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const QString& title, MenuNode* startNode, const bool rec ) const
756 {
757   MenuNode* node = 0;
758   MenuNode* start = startNode ? startNode : myRoot;
759   for ( NodeList::iterator it = start->children.begin(); it != start->children.end() && !node; ++it )
760   {
761     QAction* a = itemAction( (*it)->id );
762     if ( !a )
763       a = menuAction( (*it)->id );
764     if ( a && clearTitle( a->text() ) == clearTitle( title ) )
765       node = *it;
766     if ( !node && rec )
767       node = find( title, *it, rec );
768   }
769   return node;
770 }
771
772 /*!
773   \brief Find menu item by given ID (one-level only).
774   \param id menu action ID
775   \param pid parent meun item ID
776   \return id (>0) on success or -1 if menu item is not found
777 */
778 int QtxActionMenuMgr::findId( const int id, const int pid ) const
779 {
780   MenuNode* start = pid != -1 ? find( pid ) : myRoot;
781   if ( start )
782   {
783     for ( NodeList::iterator it = start->children.begin(); it != start->children.end(); ++it )
784     {
785       if ( (*it)->id == id )
786         return id;
787     }
788   }
789   return -1;
790 }
791
792 /*!
793   \brief Removes menu node (with all its children).
794   \param id menu action ID
795   \param startNode parent menu node which search starts from (if 0, search starts from root)
796 */
797 void QtxActionMenuMgr::removeMenu( const int id, MenuNode* startNode )
798 {
799   MenuNode* start = startNode ? startNode : myRoot;
800   for ( NodeList::iterator it = start->children.begin(); it != start->children.end(); ++it )
801   {
802     if ( (*it)->id == id )
803       start->children.removeAll( *it );
804     else
805       removeMenu( id, *it );
806   }
807 }
808
809 /*!
810   \brief Get action by \a id.
811   \param id action ID
812   \return action or 0 if \a id is invalid
813 */
814 QAction* QtxActionMenuMgr::itemAction( const int id ) const
815 {
816   return action( id );
817 }
818
819 /*!
820   \brief Get submenu action by \a id.
821   \param id submenu ID
822   \return submenu action or 0 if action is not found
823 */
824 QAction* QtxActionMenuMgr::menuAction( const int id ) const
825 {
826   QAction* a = 0;
827
828   if ( myMenus.contains( id ) )
829     a = myMenus[id];
830
831   return a;
832 }
833
834 /*!
835   \brief Get submenu action by \a id.
836   \param id submenu ID
837   \return submenu action or 0 if it is not found
838 */
839 int QtxActionMenuMgr::menuActionId( QAction* a ) const
840 {
841   int id = -1;
842   for ( MenuMap::ConstIterator itr = myMenus.begin(); itr != myMenus.end() && id == -1; ++itr )
843   {
844     if ( itr.value() == a )
845       id = itr.key();
846   }
847   return id;
848 }
849
850 /*!
851   \brief Update menu.
852
853   Does nothing if update is disabled.
854
855   \param startNode start menu item to be updated
856   \param rec if \c true, perform recursive update
857   \param updParent if \c true update also parent item (without recursion)
858
859   \sa isUpdatesEnabled() and setUpdatesEnabled()
860 */
861 void QtxActionMenuMgr::updateMenu( MenuNode* startNode, const bool rec, const bool updParent )
862 {
863   if ( !isUpdatesEnabled() )
864     return;
865
866   MenuNode* node = startNode ? startNode : myRoot;
867
868   QWidget* mw = menuWidget( node );
869   if ( !mw )
870     return;
871
872   bool filled = checkWidget( mw );
873
874   // first remove all own actions and collect foreign ones
875   QMap< QAction*, QList<QAction*> > foreign;
876   QAction* a;
877   QAction* preva = 0;
878   QListIterator<QAction*> ait( mw->actions() ); ait.toBack();
879   while ( ait.hasPrevious() )
880   {
881     a = ait.previous();
882     if ( ownAction( a, node ) )
883     {
884       preva = a;
885       mw->removeAction( a );     // remove own actions
886     }
887     else
888     {
889       foreign[preva].prepend(a); // do not yet remove foreign actions
890     }
891   }
892   // now only foreign actions should stay in the menu, thus remove them also
893   QMap< QAction*, QList<QAction*> >::Iterator formapit;
894   for( formapit = foreign.begin(); formapit != foreign.end(); ++formapit )
895   {
896     QMutableListIterator<QAction*> foralit( formapit.value() );
897     while ( foralit.hasNext() )
898     {
899       a = foralit.next();
900       if ( !mw->actions().contains( a ) )
901         foralit.remove();
902     }
903   }
904   QList<QAction*> alist = mw->actions();
905   foreach( a, alist ) mw->removeAction( a );
906
907   // collect all registered menus by group id
908   QMap<int, NodeList> idMap;
909   for ( NodeList::iterator it2 = node->children.begin(); it2 != node->children.end(); ++it2 )
910   {
911     NodeList& lst = idMap[(*it2)->group];
912     int idx = (*it2)->idx;
913     if ( idx < 0 || idx >= (int)lst.count() )
914       lst.append( *it2 );
915     else
916       lst.insert( idx, *it2 );
917   }
918
919   QIntList groups = idMap.keys();
920   qSort( groups );
921
922   groups.removeAll( -1 );
923   groups.append( -1 );
924
925   // rebuild menu: 1. add all registered actions
926   for ( QIntList::const_iterator gIt = groups.begin(); gIt != groups.end(); ++gIt )
927   {
928     if ( !idMap.contains( *gIt ) )
929       continue;
930
931     const NodeList& lst = idMap[*gIt];
932     for ( NodeList::const_iterator iter = lst.begin(); iter != lst.end(); ++iter )
933     {
934       MenuNode* node = *iter;
935       if ( !node ) continue;
936
937       if ( rec )
938         updateMenu( node, rec, false );
939
940       MenuNode* par = node->parent;
941       if ( !isVisible( node->id, par ? par->id : -1 ) )
942         continue;
943
944       bool isMenu = false;
945       QAction* a = itemAction( node->id );
946       if ( !a )
947       {
948         isMenu = true;
949         a = menuAction( node->id );
950       }
951       if ( !a ) continue;
952
953       if ( !isMenu || !a->menu()->isEmpty() || node->emptyEnabled > 0 )
954         mw->addAction( a );
955     }
956   }
957
958   // rebuild menu: 2. insert back all foreign actions
959   for( formapit = foreign.begin(); formapit != foreign.end(); ++formapit ) {
960     preva = formapit.key();
961     foreach( a, formapit.value() )
962       mw->insertAction( preva, a );
963   }
964   
965   // remove extra separators
966   simplifySeparators( mw );
967
968   // update parent menu if necessary
969   if ( updParent && node->parent && filled != checkWidget( mw ) )
970     updateMenu( node->parent, false );
971 }
972
973 /*!
974   \brief Internal update.
975   
976   Customizes the menu update processing.
977 */
978 void QtxActionMenuMgr::internalUpdate()
979 {
980   if ( !isUpdatesEnabled() )
981     return;
982
983   updateMenu();
984   myUpdateIds.clear();
985 }
986
987 /*!
988   \brief Check if action belongs to the menu manager
989   \internal
990   \param a action being checked
991   \param node parent menu node
992   \return \c true if action belongs to the menu \a node
993 */
994 bool QtxActionMenuMgr::ownAction( QAction* a, MenuNode* node ) const
995 {
996   for ( NodeList::const_iterator iter = node->children.begin(); iter != node->children.end(); ++iter )
997   {
998     QAction* mya = itemAction( (*iter)->id );
999     if ( !mya )
1000       mya = menuAction( (*iter)->id );
1001     if ( mya && mya == a )
1002       return true;
1003   }
1004   return false;
1005 }
1006
1007 /*!
1008   \brief Check if menu widget has any actions.
1009   \param wid widget to be checked
1010   \return \c true if widget contains action(s)
1011 */
1012 bool QtxActionMenuMgr::checkWidget( QWidget* wid ) const
1013 {
1014   if ( !wid )
1015     return false;
1016
1017   return !wid->actions().isEmpty();
1018 }
1019
1020 /*!
1021   \brief Get menu widget for the given \a node.
1022   \param node menu node
1023   \return popup menu or main menu corresponding to the menu node
1024   (or 0 if it is not found)
1025 */
1026 QWidget* QtxActionMenuMgr::menuWidget( MenuNode* node ) const
1027 {
1028   if ( !node || node == myRoot )
1029      return myMenu;
1030
1031   if ( !myMenus.contains( node->id ) || !myMenus[node->id] )
1032     return 0;
1033
1034   return myMenus[node->id]->menu();
1035 }
1036
1037 /*!
1038   \brief Remove extra separators from menu widget.
1039   \param wid menu widget to be processed
1040 */
1041 void QtxActionMenuMgr::simplifySeparators( QWidget* wid )
1042 {
1043   Qtx::simplifySeparators( wid, false );
1044 }
1045
1046 /*!
1047   \brief Remove special symbols (&) from string to get clear menu title.
1048   \param txt string to be processed
1049   \return clear title
1050 */
1051 QString QtxActionMenuMgr::clearTitle( const QString& txt ) const
1052 {
1053   QString res = txt;
1054
1055   for ( int i = 0; i < (int)res.length(); i++ )
1056   {
1057     if ( res.at( i ) == '&' )
1058       res.remove( i--, 1 );
1059   }
1060
1061   return res;
1062 }
1063
1064 /*!
1065   \brief Create and inserts menu item recursively.
1066   \param lst list of menu names
1067   \param pId parent menu item ID
1068   \return created menu item ID (last in the chain)
1069 */
1070 int QtxActionMenuMgr::createMenu( const QStringList& lst, const int pId )
1071 {
1072   if ( lst.isEmpty() )
1073     return -1;
1074
1075   QStringList sl( lst );
1076
1077   QString title = sl.last().trimmed();
1078   sl.removeLast();
1079
1080   int parentId = sl.isEmpty() ? pId : createMenu( sl, pId );
1081
1082   return insert( title, parentId, -1 );
1083 }
1084
1085 /*!
1086   \brief Load actions description from the file.
1087   \param fname file name
1088   \param r action reader
1089   \return \c true on success and \c false on error
1090 */
1091 bool QtxActionMenuMgr::load( const QString& fname, QtxActionMgr::Reader& r )
1092 {
1093   MenuCreator cr( &r, this );
1094   return r.read( fname, cr );
1095 }
1096
1097 /*!
1098   \brief Check if the parent menu contains menu item with given \a title.
1099   \param title menu title
1100   \param pid parent menu item ID
1101   \return \c true if parent menu item contains such child item
1102 */
1103 bool QtxActionMenuMgr::containsMenu( const QString& title, const int pid, const bool rec ) const
1104 {
1105   return (bool)find( title, pid, rec );
1106 }
1107
1108 /*!
1109   \brief Check if the parent menu contains menu item with given \a id.
1110   \param id menu item ID
1111   \param pid parent menu item ID
1112   \return \c true if parent menu item contains such child item
1113 */
1114 bool QtxActionMenuMgr::containsMenu( const int id, const int pid, const bool rec ) const
1115 {
1116   return (bool)find( id, pid, rec );
1117 }
1118
1119 /*!
1120   \brief Get menu by the specified identifier.
1121   \param id menu item ID
1122   \return menu pointer or 0 if menu is not found
1123 */
1124 QMenu* QtxActionMenuMgr::findMenu( const int id ) const
1125 {
1126   QMenu* m = 0;
1127   QAction* a = menuAction( id );
1128   if ( a )
1129     m = a->menu();
1130   return m;
1131 }
1132
1133 /*!
1134   \brief Get menu by the title.
1135   \param title menu text
1136   \param pid parent menu item ID (to start search)
1137   \param rec if \c true, perform recursive update
1138   \return menu pointer or 0 if menu is not found
1139 */
1140 QMenu* QtxActionMenuMgr::findMenu( const QString& title, const int pid, const bool rec ) const
1141 {
1142   QMenu* m = 0;
1143   MenuNode* node = find( title, pid, rec );
1144   if ( node )
1145   {
1146     QAction* a = menuAction( node->id );
1147     if ( a )
1148       m = a->menu();
1149   }
1150   return m;
1151 }
1152
1153 /*!
1154   \brief Check if empty menu is enabled
1155   \param id menu item ID
1156   \return \c true if empty menu is enabled
1157 */
1158 bool QtxActionMenuMgr::isEmptyEnabled( const int id ) const
1159 {
1160   MenuNode* node = find( id );
1161   if ( node && menuAction( id ) )
1162     return node->emptyEnabled > 0;
1163   
1164   return false;
1165 }
1166
1167 /*!
1168   \brief Enable/disable empty menu
1169   \param id menu item ID
1170   \param enable if \c true, empty menu will be enabled, otherwise empty menu will be disabled
1171 */
1172 void QtxActionMenuMgr::setEmptyEnabled( const int id, const bool enable )
1173 {
1174   MenuNode* node = find( id );
1175   if ( node && menuAction( id ) ) {
1176     int old = node->emptyEnabled;
1177     node->emptyEnabled += enable ? 1 : -1;
1178     if ( ( old <= 0 && enable ) || ( old > 0 && !enable ) ) // update menu only if enabled state has been changed
1179       updateMenu( node, true, true );
1180   }
1181 }
1182
1183 /*!
1184   \brief Perform delayed menu update.
1185   \param id menu item ID
1186   \param rec if \c true, perform recursive update
1187 */
1188 void QtxActionMenuMgr::triggerUpdate( const int id, const bool rec )
1189 {
1190   bool isRec = rec;
1191   if ( myUpdateIds.contains( id ) )
1192     isRec = isRec || myUpdateIds[ id ];
1193   myUpdateIds.insert( id, isRec );
1194
1195   QtxActionMgr::triggerUpdate();
1196 }
1197
1198 /*!
1199   \brief Called when delayed content update is performed.
1200
1201   Customizes the content update operation.
1202 */
1203 void QtxActionMenuMgr::updateContent()
1204 {
1205   // Warning: For correct updating it is necessary to update the most enclosed submenu in first turn
1206   //          because not updated empty submenu will be skipped. Now the submenus are iterated in
1207   //          ascending order according to their identifiers. For a submenus with automatically generated 
1208   //          identifiers this will work correctly since the uppermost submenus have the biggest number
1209   //          (identifiers are generated by decrementing 1 starting from -1). In general, if any submenu
1210   //          have positive identifiers this method might not work correctly. In this case it would be
1211   //          necessary to improve this method and to add preliminary sorting a submenus by depth of an 
1212   //          enclosure.
1213   for ( QMap<int, bool>::const_iterator it = myUpdateIds.constBegin(); it != myUpdateIds.constEnd(); ++it )
1214   {
1215     MenuNode* node = it.key() == -1 ? myRoot : find( it.key() );
1216     if ( node )
1217       updateMenu( node, it.value() );
1218   }
1219   myUpdateIds.clear();
1220 }
1221
1222 /*!
1223   \class QtxActionMenuMgr::MenuCreator
1224   \brief Menu actions creator.
1225
1226   Used by Reader to create actions by reading descriptions from the file
1227   and fill in the action manager with the actions.
1228 */
1229
1230 /*!
1231   \brief Constructor.
1232   \param r menu actions reader
1233   \param mgr menu manager
1234 */
1235 QtxActionMenuMgr::MenuCreator::MenuCreator( QtxActionMgr::Reader* r, QtxActionMenuMgr* mgr )
1236 : QtxActionMgr::Creator( r ),
1237   myMgr( mgr )
1238 {
1239 }
1240
1241 /*!
1242   \brief Destructor.
1243 */
1244 QtxActionMenuMgr::MenuCreator::~MenuCreator()
1245 {
1246 }
1247
1248 /*!
1249   \brief Create and append to the action manager a new action.
1250   \param tag item tag name
1251   \param subMenu \c true if this item is submenu
1252   \param attr attributes map
1253   \param pId parent action ID
1254   \return menu action ID
1255 */
1256 int QtxActionMenuMgr::MenuCreator::append( const QString& tag, const bool subMenu,
1257                                            const ItemAttributes& attr, const int pId )
1258 {
1259   if( !myMgr || !reader() )
1260     return -1;
1261
1262   QString label   = reader()->option( "label",     "label"     ),
1263           id      = reader()->option( "id",        "id"        ),
1264           pos     = reader()->option( "pos",       "pos"       ),
1265           group   = reader()->option( "group",     "group"     ),
1266           tooltip = reader()->option( "tooltip",   "tooltip"   ),
1267           sep     = reader()->option( "separator", "separator" ),
1268           accel   = reader()->option( "accel",     "accel"     ),
1269           icon    = reader()->option( "icon",      "icon"      ),
1270           toggle  = reader()->option( "toggle",    "toggle"    );
1271
1272   int res = -1, actId = intValue( attr, id, -1 );
1273
1274   if( subMenu )
1275     res = myMgr->insert( strValue( attr, label ), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
1276   else if( tag==sep )
1277     res = myMgr->insert( separator(), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
1278   else
1279   {
1280     QIcon set;
1281     QPixmap pix;
1282     QString name = strValue( attr, icon );
1283     if( !name.isEmpty() && loadPixmap( name, pix ) )
1284       set = QIcon( pix );
1285
1286     QtxAction* newAct = new QtxAction( strValue( attr, tooltip ), set,
1287                                        strValue( attr, label ),
1288                                        QKeySequence( strValue( attr, accel ) ),
1289                                        myMgr );
1290     newAct->setToolTip( strValue( attr, tooltip ) );
1291     QString toggleact = strValue( attr, toggle );
1292     newAct->setCheckable( !toggleact.isEmpty() );
1293     newAct->setChecked( toggleact.toLower() == "true" );
1294
1295     connect( newAct );
1296     int aid = myMgr->registerAction( newAct, actId );
1297     res = myMgr->insert( aid, pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
1298   }
1299
1300   return res;
1301 }