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