Salome HOME
Copyright update 2020
[modules/gui.git] / src / Qtx / QtxPopupMgr.cxx
1 // Copyright (C) 2007-2020  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, or (at your option) any later version.
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:      QtxPopupMgr.cxx
24 // Author:    Alexander SOLOVYOV, Sergey TELKOV
25 //
26 #include "QtxPopupMgr.h"
27 #include "QtxAction.h"
28 #include "QtxEvalExpr.h"
29 #include <QList>
30 #include <QMenu>
31 #include <QVariant>
32
33 bool operator<( const QList<QVariant>& v1, const QList<QVariant>& v2 )
34 {
35   QList<QVariant>::const_iterator anIt1 = v1.begin(), aLast1 = v1.end(),
36     anIt2 = v2.begin(), aLast2 = v2.end();
37   for ( ; anIt1 != aLast1 && anIt2 != aLast2;  anIt1++, anIt2++ )
38   {
39     if ( (*anIt1) != (*anIt2) )
40       return (*anIt1) < (*anIt2);
41   }
42   return anIt1 == aLast1 && anIt2 != aLast2;
43 }
44
45 /*!
46   \class QtxPopupMgr::PopupCreator
47   \internal
48   \brief Popup menu actions creator.
49
50   Used by Reader to create actions by reading descriptions from the file
51   and fill in the action manager with the actions.
52 */
53
54 class QtxPopupMgr::PopupCreator : public QtxActionMgr::Creator
55 {
56 public:
57   PopupCreator( QtxActionMgr::Reader*, QtxPopupMgr* );
58   virtual ~PopupCreator();
59
60   virtual int     append( const QString&, const bool,
61                           const ItemAttributes&, const int );
62
63   virtual QString rule( const ItemAttributes&, 
64                         const QtxPopupMgr::RuleType = VisibleRule ) const;
65
66 private:
67   QtxPopupMgr*    myMgr;
68 };
69
70 /*!
71   \brief Constructor.
72   \param r menu action reader
73   \param mgr popup menu manager
74 */
75 QtxPopupMgr::PopupCreator::PopupCreator( QtxActionMgr::Reader* r,
76                                          QtxPopupMgr* mgr )
77 : QtxActionMgr::Creator( r ),
78   myMgr( mgr )
79 {
80 }
81
82 /*!
83   \brief Destructor.
84 */
85 QtxPopupMgr::PopupCreator::~PopupCreator()
86 {
87 }
88
89 /*!
90   \brief Create and append new action to the action manager.
91   \param tag item tag name
92   \param subMenu \c true if this item is submenu
93   \param attr attributes map
94   \param pId parent action ID
95   \return menu action ID
96 */
97 int QtxPopupMgr::PopupCreator::append( const QString& tag, const bool subMenu,
98                                        const ItemAttributes& attr, const int pId )
99 {
100   if ( !myMgr || !reader() )
101     return -1;
102
103   QString label   = reader()->option( "label",     "label"     ),
104           id      = reader()->option( "id",        "id"        ),
105           pos     = reader()->option( "pos",       "pos"       ),
106           group   = reader()->option( "group",     "group"     ),
107           tooltip = reader()->option( "tooltip",   "tooltip"   ),
108           sep     = reader()->option( "separator", "separator" ),
109           accel   = reader()->option( "accel",     "accel"     ),
110           icon    = reader()->option( "icon",      "icon"      ),
111           toggle  = reader()->option( "toggle",    "toggle"    );
112
113   QtxActionMenuMgr* mgr = myMgr;
114
115   int res = -1, actId = intValue( attr, id, -1 );;
116   if ( subMenu )
117     res = mgr->insert( strValue( attr, label ), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
118   else if ( tag == sep )
119     res = mgr->insert( separator(), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
120   else
121   {
122     QIcon set;
123     QPixmap pix;
124     QString name = strValue( attr, icon );
125     if( !name.isEmpty() )
126     {
127       if ( loadPixmap( name, pix ) )
128         set = QIcon( pix );
129     }
130
131     QString actLabel = strValue( attr, label );
132     QtxAction* newAct = new QtxAction( strValue( attr, tooltip ), set, actLabel,
133                                        QKeySequence( strValue( attr, accel ) ),
134                                        myMgr );
135     newAct->setToolTip( strValue( attr, tooltip ) );
136     QString toggleact = strValue( attr, toggle );
137     bool isToggle = !toggleact.isEmpty();
138     newAct->setCheckable( isToggle );
139     newAct->setChecked( toggleact.toLower() == "true" );
140         
141     connect( newAct );
142     int aid = mgr->registerAction( newAct, actId ); 
143     QString arule = rule( attr, QtxPopupMgr::VisibleRule );
144     if ( !arule.isEmpty() )
145       myMgr->setRule( newAct, arule, QtxPopupMgr::VisibleRule );
146     arule = rule( attr, QtxPopupMgr::EnableRule );
147     if ( !arule.isEmpty() )
148       myMgr->setRule( newAct, arule, QtxPopupMgr::EnableRule );
149     arule = rule( attr, QtxPopupMgr::ToggleRule );
150     if ( isToggle && !arule.isEmpty() )
151       myMgr->setRule( newAct, arule, QtxPopupMgr::ToggleRule );
152     res = mgr->insert( aid, pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
153   }
154
155   return res;
156 }
157
158 /*!
159   \brief Get the rule for the menu item.
160
161   Default implementation returns empty rule.
162
163   \param attr attributes map
164   \param ruleType rule type (QtxPopupMgr::RuleType)
165   \return rule for the menu item corresponding to the rule type
166 */
167 QString QtxPopupMgr::PopupCreator::rule( const ItemAttributes& /*attr*/, 
168                                          const QtxPopupMgr::RuleType /*ruleType*/ ) const
169 {
170   return QString();
171 }
172
173 /*!
174   \class QtxPopupMgr
175   \brief Popup menu manager.
176
177   Menu manager allows using of set of action for automatic generating of
178   application context popup menu by reuquest and dynamic update of its
179   contents.
180
181   Use insert() methods to add menu items to the popup menu.
182
183   The visibility, enable and toggle state of the menu item is controlled
184   by the syntaxic rules, which can be set with setRule() methods.
185   The rules are parsed automatically with help of QtxEvalParser class.
186
187   QtxPopupSelection class is used as back-end for getting value of each
188   parameter found in the rule by the expression parser.
189   Use setSelection() and selection() to set/get the selection instance
190   for the popup menu manager.
191   
192   Popup menu manager automatically optimizes the menu by removing 
193   extra separators, hiding empty popup submenus etc.
194 */
195
196 /*!
197   \brief Constructor.
198   \param object parent object
199 */
200 QtxPopupMgr::QtxPopupMgr( QObject* parent )
201 : QtxActionMenuMgr( 0, parent ),
202   mySelection( 0 )
203 {
204 }
205
206 /*!
207   \brief Constructor.
208   \param popup popup menu
209   \param object parent object
210 */
211 QtxPopupMgr::QtxPopupMgr( QMenu* popup, QObject* parent )
212 : QtxActionMenuMgr( popup, parent ),
213   mySelection( 0 )
214 {
215 }
216
217 /*!
218   \brief Destructor.
219 */
220 QtxPopupMgr::~QtxPopupMgr()
221 {
222 }
223
224 /*!
225   \brief Get popup menu.
226   \return popup menu
227 */
228 QMenu* QtxPopupMgr::menu() const
229 {
230   return ::qobject_cast<QMenu*>( menuWidget() );
231 }
232
233 /*!
234   \brief Get popup menu.
235   \param menu popup menu
236 */
237 void QtxPopupMgr::setMenu( QMenu* menu )
238 {
239   setMenuWidget( menu );
240 }
241
242 /*!
243   \brief Get selection.
244   \return current selection object
245 */
246 QtxPopupSelection* QtxPopupMgr::selection() const
247 {
248   return mySelection;
249 }
250
251 /*!
252   \brief Set selection.
253   \param sel new selection object
254 */
255 void QtxPopupMgr::setSelection( QtxPopupSelection* sel )
256 {
257   if ( mySelection == sel )
258     return;
259
260   delete mySelection;
261
262   mySelection = sel;
263
264   if ( mySelection ) {
265     mySelection->setParent( this );
266     mySelection->setPopupMgr( this );
267   }
268
269   connect( mySelection, SIGNAL( destroyed( QObject* ) ), 
270            this,        SLOT( onSelectionDestroyed( QObject* ) ) );
271
272   QtxActionMgr::triggerUpdate();
273 }
274
275 /*!
276   \brief Register an action and return its identifier.
277
278   If \a id is less than 0, the identifier for the action is generated automatically.
279   If action with given \a id is already registered, it will be re-registered.
280   If required \a id is already in use, new identifier is generatied; in this case
281   returning value will different from required one.
282
283   \param act action to be registered
284   \param id action ID
285   \param rule syntax rule
286   \param ruleType rule type (QtxPopupMgr::RuleType)
287   \return action ID (the same as \a id or generated one)
288 */
289 int QtxPopupMgr::registerAction( QAction* act, const int id, const QString& rule, const QtxPopupMgr::RuleType ruleType )
290 {
291   int _id = QtxActionMenuMgr::registerAction( act, id );
292   setRule( act, rule, ruleType );
293   return _id;
294 }
295
296 /*!
297   \brief Unregister action from internal map.
298   \param id action ID
299 */
300 void QtxPopupMgr::unRegisterAction( const int id )
301 {
302   QAction* a = action( id );
303   if ( myRules.contains( a ) )
304   {
305     for ( ExprMap::iterator it = myRules[a].begin(); it != myRules[a].end(); ++it )
306       delete it.value();
307   }
308   myRules.remove( a );
309
310   remove( id );
311
312   QtxActionMenuMgr::unRegisterAction( id );
313 }
314
315 /*!
316   \brief Insert action to the popup menu manager.
317   \param id action ID
318   \param pId parent menu action ID
319   \param rule syntax rule
320   \param ruleType rule type (QtxPopupMgr::RuleType)
321   \return action ID
322 */
323 int QtxPopupMgr::insertAction( const int id, const int pId, const QString& rule, const RuleType ruleType )
324 {
325   int res = QtxActionMenuMgr::insert( id, pId, -1 );
326   setRule( action( id ), rule, ruleType );
327   return res;
328 }
329
330 /*!
331   \brief Insert action to the popup menu manager.
332   \param a action
333   \param pId parent menu action ID
334   \param rule syntax rule
335   \param ruleType rule type (QtxPopupMgr::RuleType)
336   \return action ID
337 */
338 int QtxPopupMgr::insertAction( QAction* a, const int pId, const QString& rule, const RuleType ruleType )
339 {
340   int res = QtxActionMenuMgr::insert( a, pId, -1 );
341   setRule( a, rule, ruleType );
342   return res;
343 }
344
345 /*!
346   \return true if action has rule of given type
347   \param a - action
348   \param t - rule type
349 */
350 bool QtxPopupMgr::hasRule( QAction* a, const RuleType t ) const
351 {
352   return a ? expression( a, t, false )!=0 : false;
353 }
354
355 /*!
356   \return true if action with given id has rule of given type
357   \param id - action id
358   \param t - rule type
359 */
360 bool QtxPopupMgr::hasRule( const int id, const RuleType t ) const
361 {
362   return hasRule( action( id ), t );
363 }
364
365 /*!
366   \brief Get rule of type \a type for the action \a a.
367   \param a action
368   \param ruleType rule type (QtxPopupMgr::RuleType)
369   \return rule of required type
370 */
371 QString QtxPopupMgr::rule( QAction* a, const RuleType ruleType ) const
372 {
373   QString rule;
374   QtxEvalExpr* expr = expression( a, ruleType );
375   if ( expr )
376     rule = expr->expression();
377   return rule;
378 }
379
380 /*!
381   \brief Get rule of type \a type for the action \a id.
382   \param id action ID
383   \param ruleType rule type (QtxPopupMgr::RuleType)
384   \return rule of required type
385 */
386 QString QtxPopupMgr::rule( const int id, const RuleType ruleType ) const
387 {
388   return rule( action( id ), ruleType );
389 }
390
391 /*!
392   \brief Set rule of type \a type for the action \a a.
393   \param a action
394   \param rule rule
395   \param ruleType rule type (QtxPopupMgr::RuleType)
396   \return rule of required type
397 */
398 void QtxPopupMgr::setRule( QAction* a, const QString& rule, const RuleType ruleType )
399 {
400   if ( !a )
401     return;
402
403   QtxEvalExpr* expr = expression( a, ruleType, true );
404
405   expr->setExpression( rule );
406 }
407
408 /*!
409   \brief Set rule of type \a type for the action \a id.
410   \param id action ID
411   \param rule rule
412   \param ruleType rule type (QtxPopupMgr::RuleType)
413   \return rule of required type
414 */
415 void QtxPopupMgr::setRule( const int id, const QString& rule, const RuleType ruleType )
416 {
417   setRule( action( id ), rule, ruleType );
418 }
419
420 /*!
421   \brief Calculate an expression.
422   \param p expression parser
423   \return \c true if parser has finished work without errors
424 */
425 bool QtxPopupMgr::result( QtxEvalParser* p ) const
426 {
427   bool res = false;
428   if ( p )
429   {
430     QVariant vv = p->calculate();
431     res = p->error() == QtxEvalExpr::OK &&
432           ( ( vv.type() == QVariant::Int && vv.toInt() != 0 ) ||
433             ( vv.type() == QVariant::Bool && vv.toBool() ) );
434   }
435   return res;
436 }
437
438 /*!
439   \brief Fill the parser with parameters of the expression.
440
441   The values of the parameters are given from the selection object
442   (QtxPopupSelection).
443   
444   \param p expression parser
445   \param returning list of parameters names which are not retrieved from the selection
446   \sa selection()
447 */
448 void QtxPopupMgr::setParameters( QtxEvalParser* p, QStringList& specific ) const
449 {
450   if ( !p || !mySelection )
451     return;
452
453   QStringList params = p->parameters();
454   for ( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
455   {
456     QVariant v = parameter( *it );
457     if ( v.isValid() )
458       p->setParameter( *it, v );
459     else
460       specific.append( *it );
461   }
462 }
463
464 /*!
465   \brief Check the rule for the action.
466   \param act action
467   \param ruleType rule type (QtxPopupMgr::RuleType)
468   \return \c true if current selection satisfies the action rule
469 */
470 bool QtxPopupMgr::isSatisfied( QAction* act, const RuleType ruleType ) const
471 {
472   if ( !act )
473     return false;
474
475   QtxEvalExpr* exp = expression( act, ruleType );
476   if ( !exp )
477     return true;
478
479   bool res = false;
480
481   QtxEvalParser* p = exp->parser();
482
483   QStringList specific;
484   p->clearParameters();
485   setParameters( p, specific );
486
487   QMap<QList<QVariant>, int> aCorteges;
488   if ( !specific.isEmpty() )
489   {
490     if ( mySelection )
491     {
492       res = false;
493       for ( int i = 0; i < mySelection->count() && !res; i++ )
494       {
495         QList<QVariant> c;
496         for ( QStringList::const_iterator anIt1 = specific.begin(); anIt1 != specific.end(); ++anIt1 )
497           c.append( parameter( *anIt1, i ) );
498         aCorteges.insert( c, 0 );
499       }
500       for ( QMap<QList<QVariant>, int>::const_iterator anIt = aCorteges.begin(); anIt  != aCorteges.end(); ++anIt )
501       {
502         const QList<QVariant>& aCortege = anIt.key();
503         QStringList::const_iterator anIt1 = specific.begin(), aLast1 = specific.end();
504         QList<QVariant>::const_iterator anIt2 = aCortege.begin();
505         for ( ; anIt1 != aLast1; anIt1++, anIt2++ )
506           p->setParameter( *anIt1, *anIt2 );
507         res = res || result( p );
508       }
509     }
510     else
511       res = false;
512   }
513   else
514     res = result( p );
515
516   return res;
517 }
518
519 /*!
520   \brief Check if the menu item is visible.
521   \param id action ID
522   \param place some parent action ID
523   \return \c true if the action is visible
524 */
525 bool QtxPopupMgr::isVisible( const int id, const int place ) const
526 {
527   return QtxActionMenuMgr::isVisible( id, place ) && ( !hasRule( id ) || isSatisfied( action( id ) ) );
528 }
529
530 /*!
531   \brief Perform internal update of the popup menu according 
532   to the current selection.
533 */
534 void QtxPopupMgr::internalUpdate()
535 {
536   myCache.clear();
537
538   for ( RuleMap::iterator it = myRules.begin(); it != myRules.end(); ++it )
539   {
540     ExprMap& map = it.value();
541     if ( it.key()->isCheckable() && map.contains( ToggleRule ) &&
542          !map[ToggleRule]->expression().isEmpty() )
543       it.key()->setChecked( isSatisfied( it.key(), ToggleRule ) );
544   }
545
546   QtxActionMenuMgr::internalUpdate();
547
548   myCache.clear();
549 }
550
551 /*!
552   \brief Update popup according to the current selection.
553 */
554 void QtxPopupMgr::updateMenu()
555 {
556   internalUpdate();
557 }
558
559 /*!
560   \brief Get an syntax expression for the action according to the specified rule type.
561   \param a action
562   \param ruleType rule type (QtxPopupMgr::RuleType)
563   \param create if \c true an expression does not exist, create it
564   \return syntax expression
565 */
566 QtxEvalExpr* QtxPopupMgr::expression( QAction* a, const RuleType ruleType, const bool create ) const
567 {
568   QtxEvalExpr* res = 0;
569
570   QtxPopupMgr* that = (QtxPopupMgr*)this;
571   RuleMap& ruleMap = that->myRules;
572   if ( !ruleMap.contains( a ) && create )
573     ruleMap.insert( a, ExprMap() );
574
575   if ( ruleMap.contains( a ) )
576   {
577     ExprMap& exprMap = ruleMap[a];
578     if ( exprMap.contains( ruleType ) )
579       res = exprMap[ruleType];
580     else if ( create )
581       exprMap.insert( ruleType, res = new QtxEvalExpr() );
582   }
583
584   return res;
585 }
586
587 /*!
588   \brief Load actions description from the file.
589   \param fname file name
590   \param r action reader
591   \return \c true on success and \c false on error
592 */
593 bool QtxPopupMgr::load( const QString& fname, QtxActionMgr::Reader& r )
594 {
595   PopupCreator cr( &r, this );
596   return r.read( fname, cr );
597 }
598
599 /*
600   \brief Get the specified parameter value.
601   \param name parameter name
602   \param idx additional index used when used parameters with same names 
603   \return parameter value
604   \sa selection()
605 */
606 QVariant QtxPopupMgr::parameter( const QString& name, const int idx ) const
607 {
608   QVariant val;
609   QString cacheName = name + ( idx >= 0 ? QString( "_%1" ).arg( idx ) : QString() );
610   if ( myCache.contains( cacheName ) )
611     val = myCache[cacheName];
612   else
613   {
614     if ( selection() )
615       val = idx < 0 ? selection()->parameter( name ) : 
616                       selection()->parameter( idx, name );
617     if ( val.isValid() )
618     {
619       QtxPopupMgr* that = (QtxPopupMgr*)this;
620       that->myCache.insert( cacheName, val );
621     }
622   }
623   return val;
624 }
625
626 /*!
627   \brief Called when selection is destroyed.
628   
629   Prevents crashes when the selection object is destroyed outside the
630   popup manager.
631
632   \param o selection object being destroyed
633 */
634 void QtxPopupMgr::onSelectionDestroyed( QObject* o )
635 {
636   if ( o == mySelection )
637     mySelection = 0;
638 }
639
640 /*!
641   \class QtxPopupSelection
642   \brief This class is a part of the popup menu management system. 
643
644   The QtxPopupSelection class is used as back-end for getting value
645   of each parameter found in the rule by the expression parser.
646   
647   For example, it can be used for the analyzing of the currently 
648   selected objects and defining the values of the parameters used
649   in the rules syntax expression. Rules, in their turn, define
650   each action state - visibility, enabled and toggled state.
651 */
652
653 /*!
654   \brief Constructor.
655 */
656 QtxPopupSelection::QtxPopupSelection()
657   : QObject( 0 ),
658     myPopupMgr( 0 )
659 {
660 }
661
662 /*!
663   \brief Destructor.
664 */
665 QtxPopupSelection::~QtxPopupSelection()
666 {
667 }
668
669 /*!
670   \brief Get an option value.
671   \param optName option name
672   \return option value or empty string if option is not found
673 */
674 QString QtxPopupSelection::option( const QString& optName ) const
675 {
676   QString opt;
677   if ( myOptions.contains( optName ) )
678     opt = myOptions[optName];
679   return opt;
680 }
681
682 /*!
683   \brief Set an option value.
684   \param optName option name
685   \param opt option value
686 */
687 void QtxPopupSelection::setOption( const QString& optName, const QString& opt )
688 {
689   myOptions.insert( optName, opt );
690 }
691
692 QtxPopupMgr* QtxPopupSelection::popupMgr() const
693 {
694   return myPopupMgr;
695 }
696
697 void QtxPopupSelection::setPopupMgr( QtxPopupMgr* pm )
698 {
699   myPopupMgr = pm;
700 }
701
702 /*!
703   \brief Get the parameter value.
704   \param str parameter name
705   \return parameter value
706 */
707 QVariant QtxPopupSelection::parameter( const QString& str ) const
708 {
709   if ( str == selCountParam() )
710     return count();
711   else if ( str.startsWith( equalityParam() ) )
712   {
713     QtxEvalSetSets::ValueSet set;
714     QString par = str.mid( equalityParam().length() );
715
716     QtxPopupMgr* pMgr = popupMgr();
717     for ( int i = 0; i < (int)count(); i++ )
718     {
719       QVariant v = pMgr ? pMgr->parameter( par, i ) : parameter( i, par );
720       if ( v.isValid() )
721         QtxEvalSetSets::add( set, v );
722       else
723         return QVariant();
724     }
725     return set;
726   }
727   else
728     return QVariant();
729 }
730
731 /*!
732   \brief Get symbol which detects the name of the parameter list.
733   \return equality symbol (by default, "$")
734 */
735 QString QtxPopupSelection::equalityParam() const
736 {
737   QString str = option( "equality" );
738   if ( str.isEmpty() )
739     str = "$";
740   return str;
741 }
742
743 /*!
744   \brief Get name of the parameter, specifing number of selected objects
745   \return parameter name (by default, "selcount")
746 */
747 QString QtxPopupSelection::selCountParam() const
748 {
749   QString str = option( "selcount" );
750   if ( str.isEmpty() )
751     str = "selcount";
752   return str;
753 }
754
755 /*!
756   \fn int QtxPopupSelection::count() const;
757   \brief Get number of the selected objects.
758   \return nb of selected objects
759 */
760
761 /*!
762   \fn QVariant QtxPopupSelection::parameter( const int idx, const QString& name ) const;
763   \brief Get value of the parameter which is of list type
764   \param idx parameter index
765   \param name parameter name
766   \return parameter value
767 */