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