Salome HOME
Join modifications from branch BR_DEBUG_3_2_0b1
[modules/gui.git] / src / Qtx / QtxMRUAction.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/ or email : webmaster.salome@opencascade.com
18 //
19 // File:      QtxMRUAction.cxx
20 // Author:    Sergey TELKOV
21
22 #include "QtxMRUAction.h"
23
24 #include "QtxResourceMgr.h"
25
26 #include <qpopupmenu.h>
27
28 /*!
29         Name: QtxMRUAction [public]
30         Desc: Constructs an MRU action with given parent and name.
31 */
32
33 QtxMRUAction::QtxMRUAction( QObject* parent, const char* name )
34 : QtxAction( "Most Recently Used", "Most Recently Used", 0, parent, name ),
35 myVisCount( 5 ),
36 myPopupMode( SubMenu ),
37 myInsertMode( MoveFirst )
38 {
39 }
40
41 /*!
42         Name: QtxMRUAction [public]
43         Desc: This constructor creates an action with the following properties: the
44                     description text, the menu text and.  It is a child of given parent and
45         named specified name.
46 */
47
48 QtxMRUAction::QtxMRUAction( const QString& text, const QString& menuText, QObject* parent, const char* name )
49 : QtxAction( text, menuText, 0, parent, name ),
50 myVisCount( 5 ),
51 myPopupMode( SubMenu ),
52 myInsertMode( MoveFirst )
53 {
54 }
55
56 /*!
57         Name: QtxMRUAction [public]
58         Desc: This constructor creates an action with the following properties: the
59                     description text, the menu text, the icon or iconset icon and keyboard
60         accelerator. It is a child of given parent and named specified name.
61 */
62
63 QtxMRUAction::QtxMRUAction( const QString& text, const QIconSet& icon, const QString& menuText, QObject* parent, const char* name )
64 : QtxAction( text, icon, menuText, 0, parent, name ),
65 myVisCount( 5 ),
66 myPopupMode( SubMenu ),
67 myInsertMode( MoveFirst )
68 {
69 }
70
71 /*!
72         Name: ~QtxMRUAction [public]
73         Desc: This destructor removes all added popup items.
74 */
75
76 QtxMRUAction::~QtxMRUAction()
77 {
78   for ( ItemsMap::ConstIterator iIt = myItems.begin(); iIt != myItems.end(); ++iIt )
79     removeFrom( iIt.key() );
80
81   for ( MenusMap::ConstIterator mIt = myMenus.begin(); mIt != myMenus.end(); ++mIt )
82     removeFrom( mIt.key() );
83 }
84
85 /*!
86         Name: insertMode [public]
87         Desc: Returns the insert mode.
88 */
89
90 int QtxMRUAction::insertMode() const
91 {
92   return myInsertMode;
93 }
94
95 /*!
96         Name: setInsertMode [public]
97         Desc: Returns the insert mode. Can be following values:
98       MoveFirst - place the specified link to the first position in any case
99       MoveLast  - place the specified link to the last position in any case
100       AddFirst  - if inserted link doesn't exist then add to the first position
101       AddLast   - if inserted link doesn't exist then add to the lase position
102 */
103
104 void QtxMRUAction::setInsertMode( const int mode )
105 {
106   myInsertMode = mode;
107 }
108
109 /*!
110         Name: popupMode [public]
111         Desc: Returns the popup mode.
112 */
113
114 int QtxMRUAction::popupMode() const
115 {
116   return myPopupMode;
117 }
118
119 /*!
120         Name: setPopupMode [public]
121         Desc: Set the popup mode. If this mode is 'Items' then method "addTo" creates the
122         items in the specified popup menu.  If mode is 'SubMenu' then items will be
123         create in sub popup menu which will be placed in specified popup.
124 */
125
126 void QtxMRUAction::setPopupMode( const int mode )
127 {
128   myPopupMode = mode;
129 }
130
131 /*!
132         Name: count [public]
133         Desc: Returns the number of links.
134 */
135
136 int QtxMRUAction::count() const
137 {
138   return myLinks.count();
139 }
140
141 /*!
142         Name: isEmpty [public]
143         Desc: Returns 'true' if there is no links.
144 */
145
146 bool QtxMRUAction::isEmpty() const
147 {
148   return myLinks.isEmpty();
149 }
150
151 /*!
152         Name: visibleCount [public]
153         Desc: Returns the number of first links which will be added to popup menu.
154         If 'visibleCount' less than 1 then all links will be used.
155 */
156
157 int QtxMRUAction::visibleCount() const
158 {
159   return myVisCount;
160 }
161
162 /*!
163         Name: setVisibleCount [public]
164         Desc: Sets the number of links which will be used in popup menu.
165 */
166
167 void QtxMRUAction::setVisibleCount( int num )
168 {
169   if ( myVisCount == num )
170     return;
171
172   myVisCount = num;
173
174   updateState();
175 }
176
177 /*!
178         Name: insert [public]
179         Desc: Insert the link according to the insert mode.
180 */
181
182 void QtxMRUAction::insert( const QString& link )
183 {
184   if ( myLinks.contains( link ) && ( insertMode() == AddFirst || insertMode() == AddLast ) )
185     return;
186
187   myLinks.remove( link );
188
189   switch ( insertMode() )
190   {
191   case AddFirst:
192   case MoveFirst:
193     myLinks.prepend( link );
194     break;
195   case AddLast:
196   case MoveLast:
197     myLinks.append( link );
198     break;
199   }
200
201   updateState();
202 }
203
204 /*!
205         Name: remove [public]
206         Desc: Removes link with specified index.
207 */
208
209 void QtxMRUAction::remove( const int idx )
210 {
211   if ( idx < 0 || idx >= (int)myLinks.count() )
212     return;
213
214   myLinks.remove( myLinks.at( idx ) );
215
216   updateState();
217 }
218
219 /*!
220         Name: remove [public]
221         Desc: Removes specified link.
222 */
223
224 void QtxMRUAction::remove( const QString& link )
225 {
226   if ( myLinks.remove( link ) )
227     updateState();
228 }
229
230 /*!
231         Name: item [public]
232         Desc: Returns the link with specified index.
233 */
234
235 QString QtxMRUAction::item( const int idx ) const
236 {
237   QString res;
238   if ( idx >= 0 && idx < (int)myLinks.count() )
239     res = myLinks[idx];
240   return res;
241 }
242
243 /*!
244         Name: find [public]
245         Desc: Find specified link. If link exists then returns index otherwise -1 returned.
246 */
247
248 int QtxMRUAction::find( const QString& link ) const
249 {
250   return myLinks.findIndex( link );
251 }
252
253 /*!
254         Name: contains [public]
255         Desc: Returns 'true' if given link exist.
256 */
257
258 bool QtxMRUAction::contains( const QString& link ) const
259 {
260   return myLinks.contains( link );
261 }
262
263 /*!
264         Name: addTo [public]
265         Desc: Add the MRU links to the end of specified popup according to the popup mode.
266 */
267
268 bool QtxMRUAction::addTo( QWidget* wid )
269 {
270   if ( !wid || !wid->inherits( "QPopupMenu" ) )
271     return false;
272
273   QPopupMenu* pm = (QPopupMenu*)wid;
274   checkPopup( pm );
275
276   int mode = popupMode();
277
278   if ( ( mode == Items && myItems.contains( pm ) ) ||
279        ( mode == SubMenu && myMenus.contains( pm ) ) )
280     return false;
281
282   bool exist = myItems.contains( pm ) || myMenus.contains( pm );
283
284   if ( mode == SubMenu && !QtxAction::addTo( wid ) )
285     return false;
286
287   if ( mode == Items )
288   {
289     myItems.insert( pm, Item() );
290     myItems[pm].pId = myItems[pm].nId -1;
291     connect( pm, SIGNAL( activated( int ) ), this, SLOT( onActivated( int ) ) );
292   }
293   else if ( mode == SubMenu )
294   {
295     myMenus.insert( pm, new QPopupMenu( pm ) );
296     setPopup( pm, pm->idAt( pm->count() - 1 ), myMenus[pm] );
297     connect( myMenus[pm], SIGNAL( activated( int ) ), this, SLOT( onActivated( int ) ) );
298   }
299
300   if ( !exist )
301   {
302     connect( pm, SIGNAL( aboutToShow() ), this, SLOT( onAboutToShow() ) );
303     connect( pm, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
304   }
305
306   return insertLinks( pm, mode );
307 }
308
309 /*!
310         Name: addTo [public]
311         Desc: Add the MRU links to the specified popup at given index according to the popup mode.
312 */
313
314 bool QtxMRUAction::addTo( QWidget* wid, const int idx )
315 {
316   if ( !QtxAction::addTo( wid, idx ) )
317     return false;
318
319   QPopupMenu* pm = (QPopupMenu*)wid;
320
321   removeLinks( pm, popupMode() );
322   insertLinks( pm, popupMode(), idx );
323
324   return true;
325 }
326
327 /*!
328         Name: removeFrom [public]
329         Desc: Removes all MRU links from specified popup.
330 */
331
332 bool QtxMRUAction::removeFrom( QWidget* wid )
333 {
334   QtxAction::removeFrom( wid );
335
336   QPopupMenu* pm = (QPopupMenu*)wid;
337   if ( !wid || !wid->inherits( "QPopupMenu" ) )
338     return false;
339
340   if ( myItems.contains( pm ) )
341   {
342     removeLinks( pm, Items );
343     myItems.remove( pm );
344     disconnect( pm, SIGNAL( activated( int ) ), this, SLOT( onActivated( int ) ) );
345   }
346   if ( myMenus.contains( pm ) )
347   {
348     removeLinks( pm, SubMenu );
349     delete myMenus[pm];
350     myMenus.remove( pm );
351   }
352
353   disconnect( pm, SIGNAL( aboutToShow() ), this, SLOT( onAboutToShow() ) );
354   disconnect( pm, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
355
356   return true;
357 }
358
359 /*!
360         Name: loadLinks [public]
361         Desc: Load the MRU links from specified resource manager section.
362         If parameter 'clear' is 'true' then link list will be cleared first.
363 */
364
365 void QtxMRUAction::loadLinks( QtxResourceMgr* resMgr, const QString& section, const bool clear )
366 {
367   if ( !resMgr || section.isEmpty() )
368     return;
369
370   if ( clear )
371     myLinks.clear();
372
373   QString itemPrefix( "item_" );
374
375   QMap<QString, int> map;
376   for ( QStringList::const_iterator itr = myLinks.begin(); itr != myLinks.end(); ++ itr )
377     map.insert( *itr, 0 );
378
379   QStringList items = resMgr->parameters( section );
380   for ( QStringList::const_iterator it = items.begin(); it != items.end(); ++it )
381   {
382     if ( !(*it).startsWith( itemPrefix ) )
383       continue;
384
385     QString link = resMgr->stringValue( section, *it, QString::null );
386     if ( link.isEmpty() || map.contains( link ) )
387       continue;
388
389     myLinks.append( link );
390     map.insert( link, 0 );
391   }
392
393   updateState();
394 }
395
396 /*!
397         Name: saveLinks [public]
398         Desc: Save the MRU links into specified resource manager section.
399         If parameter 'clear' is 'true' then section will be cleared first.
400 */
401
402 void QtxMRUAction::saveLinks( QtxResourceMgr* resMgr, const QString& section, const bool clear ) const
403 {
404   if ( !resMgr || section.isEmpty() )
405     return;
406
407   if ( clear )
408     resMgr->remove( section );
409
410   QStringList lst;
411   QMap<QString, int> map;
412   for ( QStringList::const_iterator itr = myLinks.begin(); itr != myLinks.end(); ++itr )
413     map.insert( *lst.append( *itr ), 0 );
414
415   QString itemPrefix( "item_" );
416   QStringList items = resMgr->parameters( section );
417   for ( QStringList::const_iterator it = items.begin(); it != items.end(); ++it )
418   {
419     if ( !(*it).startsWith( itemPrefix ) )
420       continue;
421
422     QString link = resMgr->stringValue( section, *it, QString::null );
423     if ( !link.isEmpty() && !map.contains( link ) )
424       map.insert( *lst.append( link ), 0 );
425
426     resMgr->remove( section, *it );
427   }
428
429   int counter = 0;
430   for ( QStringList::const_iterator iter = lst.begin(); iter != lst.end(); ++iter, counter++ )
431     resMgr->setValue( section, itemPrefix + QString().sprintf( "%03d", counter ), *iter );
432 }
433
434 /*!
435         Name: setEnabled [public slot]
436         Desc: Enable or disable all popup items with MRU links.
437 */
438
439 void QtxMRUAction::setEnabled( bool on )
440 {
441   QtxAction::setEnabled( on );
442
443   for ( ItemsMap::ConstIterator iter = myItems.begin(); iter != myItems.end(); ++iter )
444     for ( QIntList::const_iterator it = iter.data().idList.begin(); it != iter.data().idList.end(); ++it )
445       iter.key()->setItemEnabled( *it, on );
446 }
447
448 /*!
449         Name: onAboutToShow [private slots]
450         Desc: Enable or disable sub menu item according to number of MRU links
451         in sub popup when parent popup is shown.
452 */
453
454 void QtxMRUAction::onAboutToShow()
455 {
456   const QObject* obj = sender();
457   if ( obj && obj->inherits( "QPopupMenu" ) )
458   {
459     QPopupMenu* pm = (QPopupMenu*)obj;
460     if ( myMenus.contains( pm ) )
461       pm->setItemEnabled( findId( pm, myMenus[pm]), isEnabled() && myMenus[pm] && myMenus[pm]->count() );
462   }
463 }
464
465 /*!
466         Name: onActivated [private slot]
467         Desc: Process popup item activation and emit signal activated with selected MRU link.
468 */
469
470 void QtxMRUAction::onActivated( int id )
471 {
472   const QObject* obj = sender();
473   if ( !obj->inherits( "QPopupMenu" ) )
474     return;
475
476   QPopupMenu* pm = (QPopupMenu*)obj;
477
478   QString link;
479   if ( ( myItems.contains( pm ) && myItems[pm].idList.contains( id ) ) ||
480        ( myMenus.contains( (QPopupMenu*)pm->parent() ) && myMenus[(QPopupMenu*)pm->parent()] == pm ) )
481     link = pm->text( id );
482
483   if ( !link.isEmpty() )
484     emit activated( link );
485 }
486
487 /*!
488         Name: onDestroyed [private slot]
489         Desc: Removes deleted popup menu from internal data structures.
490 */
491
492 void QtxMRUAction::onDestroyed( QObject* obj )
493 {
494   if ( !obj )
495     return;
496
497   myItems.remove( (QPopupMenu*)obj );
498   myMenus.remove( (QPopupMenu*)obj );
499 }
500
501 /*!
502         Name: updateState [private]
503         Desc: Updates the state of all popup menus which contains MRU link items.
504 */
505
506 void QtxMRUAction::updateState()
507 {
508   for ( ItemsMap::ConstIterator iIt = myItems.begin(); iIt != myItems.end(); ++iIt )
509     updatePopup( iIt.key(), Items );
510
511   for ( MenusMap::ConstIterator mIt = myMenus.begin(); mIt != myMenus.end(); ++mIt )
512     updatePopup( mIt.key(), SubMenu );
513 }
514
515 /*!
516         Name: checkPopup [private]
517         Desc: Check consistency the popup content and internal datas.
518         Synchronize internal data structures with popup content.
519 */
520
521 void QtxMRUAction::checkPopup( QPopupMenu* pm )
522 {
523   if ( myItems.contains( pm ) )
524   {
525     bool found = true;
526     for ( QIntList::const_iterator it = myItems[pm].idList.begin(); it != myItems[pm].idList.end() && found; ++it )
527       found = pm->indexOf( *it ) != -1;
528     if ( !found )
529     {
530       removeLinks( pm, Items );
531       myItems.remove( pm );
532       disconnect( pm, SIGNAL( activated( int ) ), this, SLOT( onActivated( int ) ) );
533     }
534   }
535   if ( myMenus.contains( pm ) )
536   {
537     int id = findId( pm, myMenus[pm] );
538     if ( id == -1 )
539     {
540       delete myMenus[pm];
541       myMenus.remove( pm );
542     }
543   }
544
545   if ( !myItems.contains( pm ) && !myMenus.contains( pm ) )
546     disconnect( pm, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
547 }
548
549 /*!
550         Name: updatePopup [private]
551         Desc: Updates the MRU link items state in the specified popup menu.
552 */
553
554 void QtxMRUAction::updatePopup( QPopupMenu* pm, const int mode )
555 {
556   if ( !pm )
557     return;
558
559   int idx = -1;
560   if ( mode == Items && myItems.contains( pm ) )
561   {
562     if ( !myItems[pm].idList.isEmpty() )
563       idx = pm->indexOf( myItems[pm].idList.first() );
564     else
565     {
566       int pIdx = pm->indexOf( myItems[pm].pId );
567       int nIdx = pm->indexOf( myItems[pm].nId );
568       if ( pIdx != -1 )
569         idx = pIdx + 1;
570       else if ( nIdx != -1 )
571         idx = nIdx - 1;
572     }
573   }
574
575   removeLinks( pm, mode );
576   insertLinks( pm, mode, idx );
577 }
578
579 /*!
580         Name: removeLinks [private]
581         Desc: Removes MRU link items from specified popup.
582 */
583
584 bool QtxMRUAction::removeLinks( QPopupMenu* pm, const int mode )
585 {
586   if ( !pm )
587     return false;
588
589   if ( mode == SubMenu && myMenus.contains( pm ) )
590     myMenus[pm]->clear();
591   else if ( mode == Items && myItems.contains( pm ) )
592   {
593     for ( QIntList::const_iterator it = myItems[pm].idList.begin(); it != myItems[pm].idList.end(); ++it )
594       pm->removeItem( *it );
595     myItems[pm].idList.clear();
596   }
597
598   return true;
599 }
600
601 /*!
602         Name: insertLinks [private]
603         Desc: Inserts MRU link items to the specified popup.
604 */
605
606 bool QtxMRUAction::insertLinks( QPopupMenu* pm, const int mode, const int idx )
607 {
608   if ( !pm )
609     return false;
610
611   int count = visibleCount() < 0 ? myLinks.count() : visibleCount();
612   bool isOn = isEnabled();
613   if ( mode == SubMenu && myMenus.contains( pm ) )
614   {
615     for ( QStringList::const_iterator it = myLinks.begin(); it != myLinks.end() && count > 0; ++it, count-- )
616     {
617       int id = myMenus[pm]->insertItem( *it, -1 );
618       myMenus[pm]->setItemEnabled( id, isOn );
619     }
620   }
621   else if ( mode == Items )
622   {
623     QIntList ids;
624     int index = idx;
625     for ( QStringList::const_iterator it = myLinks.begin(); it != myLinks.end() && count > 0; ++it, count--  )
626     {
627       ids.append( pm->insertItem( *it, -1, index ) );
628       pm->setItemEnabled( ids.last(), isOn );
629       if ( index >= 0 )
630         index++;
631     }
632     myItems[pm].idList = ids;
633     if ( !myItems[pm].idList.isEmpty() )
634     {
635       myItems[pm].pId = pm->idAt( pm->indexOf( myItems[pm].idList.first() ) - 1 );
636       myItems[pm].nId = pm->idAt( pm->indexOf( myItems[pm].idList.first() ) + 1 );
637     }
638   }
639   return true;
640 }
641
642 /*!
643         Name: findId [private]
644         Desc: Returns identificator of popup item which contains sub popup 'pm' in the popup 'cont'.
645 */
646
647 int QtxMRUAction::findId( QPopupMenu* cont, QPopupMenu* pm ) const
648 {
649   if ( !cont || !pm )
650     return -1;
651
652   int id = -1;
653
654   for ( int i = 0; i < (int)cont->count() && id == -1; i++ )
655   {
656     QMenuData* md = 0;
657     QMenuItem* item = cont->findItem( cont->idAt( i ), &md );
658     if ( item && md == cont && item->popup() == pm )
659       id = item->id();
660   }
661   return id;
662 }