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