]> SALOME platform Git repositories - modules/gui.git/blob - src/Qtx/QtxPopupMgr.cxx
Salome HOME
37d0cbbc1421fcd2484c899a7dcd0ecb7d5b7909
[modules/gui.git] / src / Qtx / QtxPopupMgr.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
2 // 
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either 
6 // version 2.1 of the License.
7 // 
8 // This library is distributed in the hope that it will be useful 
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public  
14 // License along with this library; if not, write to the Free Software 
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/
18 //
19
20 #include "QtxPopupMgr.h"
21 #include "QtxListOfOperations.h"
22 #include "QtxStdOperations.h"
23 #include "QtxAction.h"
24
25 #include <qpopupmenu.h>
26 #include <qdatetime.h>
27
28
29 /*!
30   \return value of global parameter (depending on whole selection, but not dependending on one object of selection)
31   \param str - name of parameter
32
33   By default, it returns count of selected objects ("selcount") and list of parameters ("$<name>")
34 */
35 QtxValue QtxPopupMgr::Selection::globalParam( const QString& str ) const
36 {
37   if( str==selCountParam() )
38     return count();
39
40   else if( str[0]==equality() )
41   {
42     QtxSets::ValueSet set;
43     QString par = str.mid( 1 );
44
45     for( int i=0, n=count(); i<n; i++ )
46     {
47       QtxValue v = param( i, par );
48       if( v.isValid() )
49         QtxSets::add( set, v );
50       else
51         return QtxValue();      
52     }
53     return set;
54   }
55
56   else
57     return QtxValue();
58 }
59
60 /*!
61   \return symbole to detect name of parameter list
62 */
63 QChar QtxPopupMgr::Selection::equality() const
64 {
65   return defEquality();
66 }
67
68 /*!
69   \return name of parameter for count of selected objects
70 */
71 QString QtxPopupMgr::Selection::selCountParam() const
72 {
73   return defSelCountParam();
74 }
75
76 /*!
77   \return default symbole to detect name of parameter list
78 */
79 QChar QtxPopupMgr::Selection::defEquality()
80 {
81     return '$';
82 }
83
84 /*!
85   \return default name of parameter for count of selected objects
86 */
87 QString QtxPopupMgr::Selection::defSelCountParam()
88 {
89     return "selcount";
90 }
91
92
93
94 /*!
95   \class QtxCacheSelection
96
97   Special selection class, that caches parameter values.
98   Every parameter during popup building is calculated only one time,
99   although it may be included to many rules. After calculation
100   it is stored in internal map
101 */
102
103 class QtxCacheSelection : public QtxPopupMgr::Selection
104 {
105 public:
106   QtxCacheSelection( QtxPopupMgr::Selection* );
107   virtual ~QtxCacheSelection();
108
109   virtual int      count() const;
110   virtual QtxValue param( const int, const QString& ) const;
111   virtual QtxValue globalParam( const QString& ) const;
112
113 private:
114   typedef QMap< QString, QtxValue >  CacheMap;
115
116   QtxPopupMgr::Selection*    mySel;
117   CacheMap                   myParamCache;
118 };
119
120 /*!
121   Constructor
122   \param sel - base selection used for parameter calculation
123 */
124 QtxCacheSelection::QtxCacheSelection( QtxPopupMgr::Selection* sel )
125 : mySel( sel )
126 {
127 }
128
129 /*!
130   Destructor
131 */
132 QtxCacheSelection::~QtxCacheSelection()
133 {
134 }
135
136 /*!
137   \return count of selected objects
138 */
139 int QtxCacheSelection::count() const
140 {
141   return mySel ? mySel->count() : 0;
142 }
143
144 /*!
145   Calculates and caches parameters.
146   Already calculated parameters are returned without calculation
147   \return parameter value
148   \param i - index of selected object
149   \param name - name of parameter
150 */
151 QtxValue QtxCacheSelection::param( const int i, const QString& name ) const
152 {
153   QString param_name = name + "#####" + QString::number( i );
154   if( myParamCache.contains( param_name ) )
155     return myParamCache[ param_name ];
156   else
157   {
158     QtxValue v;
159     if( mySel )
160       v = mySel->param( i, name );
161     if( v.isValid() )
162       ( ( CacheMap& )myParamCache ).insert( param_name, v );
163     return v;
164   }
165 }
166
167 /*!
168   Calculates and caches global parameters.
169   Already calculated parameters are returned without calculation
170   \return parameter value
171   \param name - name of parameter
172 */
173 QtxValue QtxCacheSelection::globalParam( const QString& name ) const
174 {
175   if( myParamCache.contains( name ) )
176     return myParamCache[ name ];
177   else
178   {
179     QtxValue v;
180     if( mySel )
181       v = mySel->globalParam( name );
182     if( v.isValid() )
183       ( ( CacheMap& )myParamCache ).insert( name, v );
184     return v;
185   }
186 }
187
188
189
190
191 /*!
192   Constructor
193   \param mgr - popup manager
194 */
195 QtxPopupMgr::Operations::Operations( QtxPopupMgr* mgr )
196 : QtxStrings(),
197   myPopupMgr( mgr )
198 {
199     QStringList aList;
200     aList.append( "every" );
201     aList.append( "any" );
202     aList.append( "onlyone" );
203     addOperations( aList );
204
205     myParser = new QtxParser( mgr->myOperations );
206 }
207
208 /*!
209   Destructor
210   Deletes internal parser
211 */
212 QtxPopupMgr::Operations::~Operations()
213 {
214     delete myParser;
215 }
216
217 /*!
218     \return priority of popup operation 'op'.
219     \param isBin indicate whether the operation is binary
220 */
221 int QtxPopupMgr::Operations::prior( const QString& op, bool isBin ) const
222 {
223     if( !isBin && ( op=="every" || op=="any" || op=="onlyone" ) )
224         return 1;
225     else
226         return QtxStrings::prior( op, isBin );
227
228 }
229
230 /*!
231     Calculates result of operation
232     \return one of error states
233     \param op - name of operation
234     \param v1 - first operation argument (must be used also to store result)
235     \param v2 - second operation argument
236 */
237 QtxParser::Error QtxPopupMgr::Operations::calculate
238     ( const QString& op, QtxValue& v1, QtxValue& v2 ) const
239 {
240     int ind = -1;
241     if( op=="every" )
242         ind = 0;
243     else if( op=="any" )
244         ind = 1;
245     else if( op=="onlyone" )
246         ind = 2;
247
248     if( ind>=0 && ind<=2 )
249     {
250         QString val_name = op + "(" + v2.toString() + ")";
251         QtxParser::Error err = QtxParser::OK;
252
253         if( !myValues.contains( val_name ) )
254         {
255             myParser->setExpr( v2.toString() );
256             QStringList params, specific;
257             myParser->paramsList( params );
258
259             myParser->clear();
260             myPopupMgr->setParams( myParser, specific );
261
262             QtxPopupMgr::Selection* sel = myPopupMgr->myCurrentSelection;
263
264             int global_result = 0;
265             if( sel )
266                 for( int i=0, n=sel->count(); i<n; i++ )
267                 {
268                     QStringList::const_iterator anIt = specific.begin(),
269                                                 aLast = specific.end();
270                     for( ; anIt!=aLast; anIt++ )
271                     {
272                         QtxValue v = sel->param( i, *anIt );
273                         if( v.isValid() )
274                             myParser->set( *anIt, v );
275                         else
276                             return QtxParser::InvalidToken;
277                     }
278
279                     QtxValue res = myParser->calculate();
280                     err = myParser->lastError();
281                     if( err==QtxParser::OK )
282                         if( res.type()==QVariant::Bool )
283                         {
284                             if( res.toBool() )
285                                 global_result++;
286                             if( ind==2 && global_result>1 )
287                                 break;
288                         }
289                         else
290                             return QtxParser::InvalidResult;
291                     else
292                         return err;
293                 }
294
295             QtxValue& vv = ( QtxValue&  )myValues[ val_name ];
296             vv = ( ind==0 && global_result==sel->count() ) ||
297                  ( ind==1 ) ||
298                  ( ind==2 && global_result==1 );
299         }
300
301         v2 = myValues[ val_name ];
302
303         return err;
304     }
305     else
306         return QtxStrings::calculate( op, v1, v2 );
307 }
308
309 /*!
310   Clears internal map of values
311 */
312 void QtxPopupMgr::Operations::clear()
313 {
314     myValues.clear();
315 }
316
317
318
319
320
321
322
323
324 /*!
325   Constructor
326 */
327 QtxPopupMgr::QtxPopupMgr( QPopupMenu* popup, QObject* parent )
328 : QtxActionMenuMgr( popup, parent ),
329   myCurrentSelection( 0 )
330 {
331     createOperations();
332 }
333
334 /*!
335   Destructor
336 */
337 QtxPopupMgr::~QtxPopupMgr()
338 {
339 }
340
341 /*!
342   Creates popup operations instance
343 */
344 void QtxPopupMgr::createOperations()
345 {
346     myOperations = new QtxListOfOperations;
347     myOperations->prepend( "logic",   new QtxLogic(),           0 );
348     myOperations->prepend( "arithm",  new QtxArithmetics(),    50 );
349     myOperations->append( "strings", new QtxStrings(),       100 );
350     myOperations->append( "sets",    new QtxSets(),          150 );
351     myOperations->append( "custom",  new Operations( this ), 200 );
352 }
353
354 /*!
355   Additional version of registerAction
356   \param act - action to be registered
357   \param visible - rule for visibility state
358   \param toggle - rule for toggle on state
359   \param id - proposed id (if it is less than 0, then id will be generated automatically)
360 */
361 int QtxPopupMgr::registerAction( QAction* act,
362                                  const QString& visible,
363                                  const QString& toggle,
364                                  const int id )
365 {
366     int _id = QtxActionMenuMgr::registerAction( act, id );
367     setRule( _id, visible, true );
368     setRule( _id, toggle, false );
369     return _id;
370 }
371
372 /*!
373   Removes action from internal map
374   \param id - action id
375 */
376 void QtxPopupMgr::unRegisterAction( const int id )
377 {
378     QAction* act = action( id );
379
380     myVisibility.remove( act );
381     myToggle.remove( act );
382
383     remove( id );
384     //QtxActionMenuMgr::unRegisterAction( id );
385 }
386
387 /*!
388   \return true if manager has rule for action
389   \param act - action
390   \param visibility - if it is true, then rule for "visibility" is checked, otherwise - for "toggle"
391 */
392 bool QtxPopupMgr::hasRule( QAction* act, bool visibility ) const
393 {
394     return map( visibility ).contains( act );
395 }
396
397 /*!
398   \return true if manager has rule for action
399   \param id - action id
400   \param visibility - if it is true, then rule for "visibility" is checked, otherwise - for "toggle"
401 */
402 bool QtxPopupMgr::hasRule( const int id, bool visibility ) const
403 {
404     return hasRule( action( id ), visibility );
405 }
406
407 /*!
408   Sets new rule for action
409   \param act - action
410   \param rule - string expression of rule
411   \param visibility - if it is true, then rule for "visibility" will be set, otherwise - for "toggle"
412 */
413 void QtxPopupMgr::setRule( QAction* act, const QString& rule, bool visibility )
414 {
415     if( !act || rule.isEmpty() )
416         return;
417
418     if( !hasRule( act, visibility ) )
419     {
420         QtxParser* p = new QtxParser( myOperations, rule );
421         if( p->lastError()==QtxParser::OK )
422             map( visibility ).insert( act, p );
423         else
424             delete p;
425     }
426     else
427     {
428         QtxParser* p = map( visibility )[ act ];
429         p->setExpr( rule );
430         if( p->lastError()!=QtxParser::OK )
431             p->setExpr( QString() );
432     }
433 }
434
435 /*!
436   Sets new rule for action
437   \param id - action id
438   \param rule - string expression of rule
439   \param visibility - if it is true, then rule for "visibility" will be set, otherwise - for "toggle"
440 */
441 void QtxPopupMgr::setRule( const int id, const QString& rule, bool visibility )
442 {
443     setRule( action( id ), rule, visibility );
444 }
445
446 /*!
447   \return true if parser has finished work without errors
448   \param p - parser
449 */
450 bool result( QtxParser* p )
451 {
452     bool res = false;
453     if( p )
454     {
455         QtxValue vv = p->calculate();
456         res = p->lastError()==QtxParser::OK &&
457             ( ( vv.type()==QVariant::Int && vv.toInt()!=0 ) ||
458               ( vv.type()==QVariant::Bool && vv.toBool() ) );
459     }
460     return res;
461 }
462
463 /*!
464   Fills parser parameters with help of Selection::globalParam() method
465   \param p - parser
466   \param specific - list will be filled with names of parameters depending on selection objects (not global)
467 */
468 void QtxPopupMgr::setParams( QtxParser* p, QStringList& specific ) const
469 {
470     if( !p || !myCurrentSelection )
471         return;
472
473     QStringList params;
474
475     p->paramsList( params );
476     QStringList::const_iterator anIt = params.begin(),
477                                 aLast = params.end();
478     for( ; anIt!=aLast; anIt++ )
479     {
480       QtxValue v = myCurrentSelection->globalParam( *anIt );
481       if( v.isValid() )
482         p->set( *anIt, v );
483       else
484         specific.append( *anIt );
485     }
486 }
487
488 /*!
489   \return true if 'v1'<'v2'
490   This function can work with many types of values
491 */
492 bool operator<( const QtxValue& v1, const QtxValue& v2 )
493 {
494   QVariant::Type t1 = v1.type(), t2 = v2.type();
495   if( t1==t2 )
496   {
497     switch( t1 )
498     {
499     case QVariant::Int:
500       return v1.toInt() < v2.toInt();
501       
502     case QVariant::Double:
503       return v1.toDouble() < v2.toDouble();
504
505     case QVariant::CString:
506     case QVariant::String:
507       return v1.toString() < v2.toString();
508
509     case QVariant::StringList:
510     case QVariant::List:
511     {
512       const QValueList<QtxValue>& aList1 = v1.toList(), aList2 = v2.toList();
513       QValueList<QtxValue>::const_iterator
514         anIt1 = aList1.begin(), aLast1 = aList1.end(),
515         anIt2 = aList2.begin(), aLast2 = aList2.end();
516       for( ; anIt1!=aLast1 && anIt2!=aLast2; anIt1++, anIt2++ )
517         if( (*anIt1)!=(*anIt2) )
518           return (*anIt1)<(*anIt2);
519
520       return anIt1==aLast1 && anIt2!=aLast2;
521     }
522
523     default:
524       return v1.toString()<v2.toString();
525     }
526   }
527   else
528     return t1<t2;
529 }
530
531 /*!
532   \return true if rule of action is satisfied on current selection
533   \param act - action
534   \param visibility - what rule is checked: for visibility(true) or for toggle(false)
535 */
536 bool QtxPopupMgr::isSatisfied( QAction* act, bool visibility ) const
537 {
538   QString menu = act->menuText();
539
540   bool res = false;
541   if( !act )
542     return res;
543
544   if( hasRule( act, visibility ) )
545   {
546     QtxParser* p = map( visibility )[ act ];
547     QStringList specific;
548     p->clear();
549     ( ( Operations* )myOperations->operations( "custom" ) )->clear();
550
551     setParams( p, specific );
552
553     QMap<QValueList<QtxValue>,int> aCorteges;
554     QValueList<QtxValue> c;
555
556     if( specific.count()>0 )
557       if( myCurrentSelection )
558       {
559         res = false;
560
561         for( int i=0, n=myCurrentSelection->count(); i<n && !res; i++ )
562         {
563           QStringList::const_iterator anIt1 = specific.begin(), aLast1 = specific.end();
564           c.clear();
565           for( ; anIt1!=aLast1; anIt1++ )
566             c.append( myCurrentSelection->param( i, *anIt1 ) );
567           aCorteges.insert( c, 0 );
568         }
569         
570         //qDebug( QString( "%1 corteges" ).arg( aCorteges.count() ) );
571         QMap<QValueList<QtxValue>,int>::const_iterator anIt = aCorteges.begin(), aLast = aCorteges.end();
572         for( ; anIt!=aLast; anIt++ )
573         {
574           QStringList::const_iterator anIt1 = specific.begin(), aLast1 = specific.end();
575           const QValueList<QtxValue>& aCortege = anIt.key();
576           QValueList<QtxValue>::const_iterator anIt2 = aCortege.begin();
577           for( ; anIt1!=aLast1; anIt1++, anIt2++ )
578             p->set( *anIt1, *anIt2 );
579           res = res || result( p );
580         }
581
582         /*
583         for( int i=0, n=myCurrentSelection->count(); i<n && !res; i++ )
584         {
585           QStringList::const_iterator anIt1 = specific.begin(), aLast1 = specific.end();
586           for( ; anIt1!=aLast1; anIt1++ )
587             p->set( *anIt1, myCurrentSelection->param( i, *anIt1 ) );
588           res = res || result( p );
589         }*/
590       }
591       else
592         res = false;
593     else
594       res = result( p );
595   }
596
597   return res;
598 }
599
600 /*!
601   \return true if item corresponding to action is visible
602   \param actId - action id
603   \param place - index of place
604 */
605 bool QtxPopupMgr::isVisible( const int actId, const int place ) const
606 {
607     bool res = QtxActionMenuMgr::isVisible( actId, place );
608     QAction* act = action( actId );
609     if( hasRule( act, true ) )
610         res = res && isSatisfied( act, true );
611     return res;
612 }
613
614 /*!
615   Updates popup according to selection
616   \param p - popup menu
617   \param sel - selection
618 */
619 void QtxPopupMgr::updatePopup( QPopupMenu* p, Selection* sel )
620 {
621   QTime t1 = QTime::currentTime();
622
623   if( !p || !sel )
624     return;
625
626   myCurrentSelection = new QtxCacheSelection( sel );
627   RulesMap::iterator anIt = myToggle.begin(),
628                             aLast = myToggle.end();
629   for( ; anIt!=aLast; anIt++ )
630     if( anIt.key()->isToggleAction() && hasRule( anIt.key(), false ) )
631       anIt.key()->setOn( isSatisfied( anIt.key(), false ) );
632
633   setWidget( ( QWidget* )p );
634   updateMenu();
635   QTime t2 = QTime::currentTime();
636   qDebug( QString( "update popup time = %1 msecs" ).arg( t1.msecsTo( t2 ) ) );
637   qDebug( QString( "number of objects = %1" ).arg( myCurrentSelection->count() ) );
638
639   delete myCurrentSelection;
640 }
641
642 /*!
643   \return reference to map of rules
644   \param visibility - type of map: visibility of toggle
645 */
646 QtxPopupMgr::RulesMap& QtxPopupMgr::map( bool visibility ) const
647 {
648     return ( RulesMap& )( visibility ? myVisibility : myToggle );
649 }
650
651 /*!
652   Loads actions description from file
653   \param fname - name of file
654   \param r - reader of file
655   \return true on success
656 */
657 bool QtxPopupMgr::load( const QString& fname, QtxActionMgr::Reader& r )
658 {
659   PopupCreator cr( &r, this );
660   return r.read( fname, cr );
661 }
662
663
664
665
666 /*!
667   Constructor
668   \param r - menu reader
669   \param mgr - menu manager
670 */
671 QtxPopupMgr::PopupCreator::PopupCreator( QtxActionMgr::Reader* r,
672                                          QtxPopupMgr* mgr )
673 : QtxActionMgr::Creator( r ),
674   myMgr( mgr )
675 {
676 }
677
678 /*!
679   Destructor
680 */QtxPopupMgr::PopupCreator::~PopupCreator()
681 {
682 }
683
684
685 /*!
686   Appends new menu items
687   \param tag - tag of item
688   \param subMenu - it has submenu
689   \param attr - list of attributes
690   \param pId - id of action corresponding to parent item
691 */
692 int QtxPopupMgr::PopupCreator::append( const QString& tag, const bool subMenu,
693                                        const ItemAttributes& attr, const int pId )
694 {
695   if( !myMgr || !reader() )
696     return -1;
697
698   QString label   = reader()->option( "label",     "label"     ),
699           id      = reader()->option( "id",        "id"        ),
700           pos     = reader()->option( "pos",       "pos"       ),
701           group   = reader()->option( "group",     "group"     ),
702           tooltip = reader()->option( "tooltip",   "tooltip"   ),
703           sep     = reader()->option( "separator", "separator" ),
704           accel   = reader()->option( "accel",     "accel"     ),
705           icon    = reader()->option( "icon",      "icon"      ),
706           toggle  = reader()->option( "toggle",    "toggle"    );
707
708   int res = -1, actId = intValue( attr, id, -1 );;
709   if( subMenu )
710     res = myMgr->insert( strValue( attr, label ), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
711   else if( tag==sep )
712     res = myMgr->insert( separator(), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
713   else //if( !myMgr->contains( actId ) )
714   {
715     QPixmap pix; QIconSet set;
716     QString name = strValue( attr, icon );
717     if( !name.isEmpty() )
718     {
719       if( loadPixmap( name, pix ) )
720         set = QIconSet( pix );
721     }
722
723     QString actLabel = strValue( attr, label );
724     QtxAction* newAct = new QtxAction( strValue( attr, tooltip ), set, actLabel,
725                                        QKeySequence( strValue( attr, accel ) ),
726                                        myMgr );
727     newAct->setToolTip( strValue( attr, tooltip ) );
728     QString toggleact = strValue( attr, toggle );
729     bool isToggle = !toggleact.isEmpty();
730     newAct->setToggleAction( isToggle );
731     newAct->setOn( toggleact.lower()=="true" );
732         
733     connect( newAct );
734     int aid = myMgr->registerAction( newAct, visibleRule( attr ), 
735                                      isToggle ? toggleRule( attr ) : QString::null,
736                                      actId );
737     res = myMgr->insert( aid, pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );
738   }
739
740   return res;
741 }
742
743 /*!
744   \return visibility rule by attributes
745   Default implementation is empty
746 */
747 QString QtxPopupMgr::PopupCreator::visibleRule( const ItemAttributes& ) const
748 {
749   return QString::null;
750 }
751
752 /*!
753   \return toggle rule by attributes
754   Default implementation is empty
755 */
756 QString QtxPopupMgr::PopupCreator::toggleRule( const ItemAttributes& ) const
757 {
758   return QString::null;
759 }