Salome HOME
updated copyright message
[modules/gui.git] / src / QDS / QDS_ComboBox.cxx
1 // Copyright (C) 2007-2023  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 #include "QDS_ComboBox.h"
24
25 #include <QtxComboBox.h>
26
27 #include <TColStd_HArray1OfInteger.hxx>
28 #include <TColStd_HArray1OfExtendedString.hxx>
29
30 #include <QLineEdit>
31
32 /*
33   \class QDS_ComboBox
34   
35   \brief Datum with control corresponding to the combo box. 
36
37   This control is used for datum with enumerable values.
38   It can be used for datum which has type of value 'List'. 
39
40   Each item of the combobox is defined by two properties: integer identifier 
41   and string name. All operations on items are performed via identifier.
42 */
43
44 /*!
45   \brief Constructor. 
46
47   Create combobox datum object with datum identifier \a id 
48   and parent widget \a parent. 
49
50   Parameter \a flags defines behaviour of datum and set of created
51   subwidgets. Default value of this parameter is QDS::All.
52
53   Parameter \a comp specifies the component name which will be used
54   when searching the dictionary item.
55
56   \param id datum identifier
57   \param parent parent widget
58   \param flags datum flags
59   \param comp component
60 */
61 QDS_ComboBox::QDS_ComboBox( const QString& id, QWidget* parent, const int flags, const QString& comp )
62 : QDS_Datum( id, parent, flags, comp )
63 {
64 }
65
66 /*!
67   \brief Destructor.
68 */
69 QDS_ComboBox::~QDS_ComboBox()
70 {
71 }
72
73 /*!
74   \brief Check if combo box allows text editing.
75   \return \c true if combo box is editable
76 */
77 bool QDS_ComboBox::editable() const
78 {
79   if ( comboBox() && comboBox()->lineEdit() )
80     return !comboBox()->lineEdit()->isReadOnly();
81   else
82     return false;
83 }
84
85 /*!
86   \brief Enable/disable text editing.
87   \param on if \c true, combo box is made editable
88 */
89 void QDS_ComboBox::setEditable( const bool on )
90 {
91   QComboBox* aCombo = comboBox();
92   if ( aCombo )
93     aCombo->setEditable( on );
94   if ( aCombo && aCombo->lineEdit() )
95   {
96     aCombo->lineEdit()->setReadOnly( !on );
97     aCombo->setValidator(0);
98     if ( on )
99       aCombo->setValidator( validator() );
100   }
101 }
102
103 /*!
104   \brief Get number of items in the combo box.
105
106   \param total if \c false, only visible items are taken into account, 
107         otherwise get total number of items
108   \return number of items
109 */
110 int QDS_ComboBox::count( bool total ) const
111 {
112   if ( total )
113     return myValue.count();
114   else if ( comboBox() )
115     return comboBox()->count();
116   else
117     return 0;
118 }
119
120 /*!
121   \brief Get items identifiers.
122
123   \param ids returned list of items IDs
124   \param total if \c false, only visible items are taken into account, 
125         otherwise get total number of items
126 */
127 void QDS_ComboBox::values( QList<int>& ids, bool total ) const
128 {
129   ids.clear();
130   for ( QIntList::const_iterator it = myDataIds.begin(); it != myDataIds.end(); ++it )
131     if ( total || ( myState.contains( *it ) && myState[*it] ) )
132       ids.append( *it );
133 }
134
135 /*!
136   \brief Get the current item ID as integer value.
137   \return current item ID converted to integer
138 */
139 int QDS_ComboBox::integerValue() const
140 {
141   QComboBox* cb = comboBox();
142   QString cur = getString();
143   if ( cb && cb->count() > 0 && cb->currentIndex() >= 0 )
144     cur = cb->itemText( cb->currentIndex() );
145
146   if ( cb && cur == getString() )
147     return getId( cb->currentIndex() );
148   else
149     return getId( getString() );
150 }
151
152 /*!
153   \brief Get the current item ID as double value.
154   \return current item ID converted to double
155 */
156 double QDS_ComboBox::doubleValue() const
157 {
158   initDatum();
159
160   QComboBox* cb = comboBox();
161   QString cur = getString();
162   if ( cb && cb->count() > 0 && cb->currentIndex() >= 0 )
163     cur = cb->itemText( cb->currentIndex() );
164
165   if ( cb && cur == getString() )
166     return getId( cb->currentIndex() );
167   else
168     return getId( getString() );
169 }
170
171 /*!
172   \brief Set the current item acording to the specified \a id.
173   \param id item ID
174 */
175 void QDS_ComboBox::setIntegerValue( const int id )
176 {
177   initDatum();
178
179   if ( myValue.contains( id ) )
180     setString( myValue[id] );
181   else 
182     setString( "" );
183 }
184
185 /*!
186   \brief Set the current item acording to the specified \a id.
187   \overload
188
189   Integer part of \a val is used as new current ID.
190   
191   \param val item ID
192 */
193 void QDS_ComboBox::setDoubleValue( const double val )
194 {
195   initDatum();
196
197   int id = (int)val;
198   if ( myValue.contains( id ) )
199     setString( myValue[id] );
200   else if ( id == -1 )
201     setString( "" );
202 }
203
204 /*!
205   \brief Get visibility state of the item specified by \a id.
206   \param id item ID
207   \return item visibility state
208 */
209 bool QDS_ComboBox::state( const int id ) const
210 {
211   bool state = false;
212   if ( myState.contains( id ) )
213     state = myState[id];
214   return state;
215 }
216
217 /*!
218   \brief Set the visibility state of the item specified by \a id.
219
220   If \a id is -1 then specified state will be set to all items.
221
222   If \a append is set to \c true, keep current status for other items,
223   otherwise status of other items is cleared.
224
225   \param on new visibility state
226   \param id item ID
227   \param append if \c true, keep original status for other items
228 */
229 void QDS_ComboBox::setState( const bool on, const int id, const bool append )
230 {
231   QList<int> lst;
232   if ( id < 0 )
233   {
234     for ( IdStateMap::Iterator it = myState.begin(); it != myState.end(); ++it )
235       lst.append( it.key() );
236   }
237   else
238     lst.append( id );
239
240   setState( on, lst, append );
241 }
242
243 /*!
244   \brief Set the visibility state of items specified by \a ids.
245
246   If \a append is set to \c true, keep current status for other items,
247   otherwise status of other items is cleared.
248
249   \param on new visibility state
250   \param ids items IDs list
251   \param append if \c true, keep original status for other items
252 */
253 void QDS_ComboBox::setState( const bool on, const QList<int>& ids, const bool append )
254 {
255   initDatum();
256
257   if ( ids.isEmpty() && append )
258     return;
259
260   bool changed = false;
261
262   QMap<int, int> aMap;
263   for ( int i = 0; i < ids.count(); i++ )
264     aMap.insert( ids.at( i ), 0 );
265
266   for ( IdStateMap::Iterator it = myState.begin(); it != myState.end(); ++it )
267   {
268     if ( aMap.contains( it.key() ) )
269     {
270       if ( it.value() != on )
271       {
272         it.value() = on;
273         changed = true;
274       }
275     }
276     else if ( !append && it.value() == on )
277     {
278       it.value() = !on;
279       changed = true;
280     }
281   }
282   if ( changed )
283     updateComboBox();
284 }
285
286 /*!
287   \brief Set the custom user items into the combo box.
288
289   User items like standard dictionary list items will be added
290   into the combobox. This function allows user to override
291   items.
292
293   \param ids items IDs
294   \param names items names
295 */
296 void QDS_ComboBox::setValues( const QList<int>& ids, const QStringList& names )
297 {
298   initDatum();
299
300   if ( ids.count() != names.count() )
301     return;
302
303   myUserIds = ids;
304   myUserNames = names;
305
306   //unitSystemChanged( "" );
307 }
308
309 /*!
310   \brief Set the custom user items into the combo box.
311   \overload
312
313   User items like standard dictionary list items will be added
314   into the combobox. This function allows user to override
315   items.
316
317   Uses (0, 1, 2 ... ) as items IDs.
318
319   \param names items names
320 */
321 void QDS_ComboBox::setValues( const QStringList& names )
322 {
323   initDatum();
324
325   QList< int > ids;
326   for ( int i = 0, n = names.count(); i < n; i++ )
327     ids.append( i );
328   setValues( ids, names );
329 }
330
331 /*!
332   \brief Reset the datum.
333
334   Set the active item as item with default ID. If default ID is not defined
335   then the first item is used.
336 */
337 void QDS_ComboBox::reset()
338 {
339   QString aDefValue = defaultValue();
340   if ( !aDefValue.isEmpty() )
341     setString( aDefValue );
342   else
343     setIntegerValue( getId( 0 ) );
344 }
345
346 /*!
347   \brief Convert string to integer value.
348   \param str item
349   \return item ID or -1 if not found
350 */
351 int QDS_ComboBox::stringToValue( const QString& str ) const
352 {
353   return getId( str );
354 }
355
356 /*!
357   \brief Convert integer to string value.
358   \param val item ID 
359   \return item value or empty string if \c val is invalid
360 */
361 QString QDS_ComboBox::valueToString( const int val ) const
362 {
363   QString str;
364   if ( myValue.contains( val ) )
365     str = myValue[val];
366   return str;
367 }
368
369 /*!
370   \brief Get string from the combo box.
371   \return string value
372 */
373 QString QDS_ComboBox::getString() const
374 {
375   QString res;
376   QtxComboBox* cb = comboBox();
377   if ( cb )
378   {
379     if ( !cb->isEditable() )
380     {
381       if ( !cb->isCleared() )
382         res = cb->currentText(); 
383     }
384     else
385       res = cb->lineEdit()->text();
386   }
387   return res;
388 }
389
390 /*!
391   \brief Set the string value to the combo box widget.
392   \param txt string value
393 */
394 void QDS_ComboBox::setString( const QString& txt )
395 {
396   QtxComboBox* cb = comboBox();
397   if ( !cb )
398     return;
399
400   bool isClear = cb->isCleared();
401   
402   int idx = -1;
403   for ( int i = 0; i < cb->count() && idx == -1; i++ )
404     if ( cb->itemText( i ) == txt )
405       idx = i;
406
407   int old = cb->currentIndex();
408   if ( idx != -1 )
409     cb->setCurrentIndex( idx );
410   else if ( txt.isEmpty() )
411   {
412     if ( !cb->isEditable() )
413       cb->setCleared( true );
414     else
415       cb->lineEdit()->setText( txt );
416   }
417   if ( isClear != txt.isEmpty() || ( !isClear && old != cb->currentIndex() ) || isClear != cb->isCleared() )
418   {
419     onParamChanged();
420     QString str = getString();
421     emit activated( integerValue() );
422     emit activated( str );
423     emit paramChanged();
424     emit paramChanged( str );
425   }
426 }
427
428 /*!
429   \brief Get combo box widget.
430   \return internal combo box widget
431 */
432 QtxComboBox* QDS_ComboBox::comboBox() const
433 {
434   return ::qobject_cast<QtxComboBox*>( controlWidget() );
435 }
436
437 /*!
438   \brief Create internal combo box as control widget.
439   \param parent parent widget
440   \return created combo box widget
441 */
442 QWidget* QDS_ComboBox::createControl( QWidget* parent )
443 {
444   QtxComboBox* cb = new QtxComboBox( parent );
445   cb->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ) );
446   connect( cb, SIGNAL( editTextChanged( const QString& ) ), this,
447            SLOT( onTextChanged( const QString& ) ) );
448   connect( cb, SIGNAL( activated( int ) ), this, SLOT( onActivated( int ) ) );
449   return cb;
450 }
451
452 /*!
453   \brief Process notification about active units system changing.
454
455   Update combobox contents.
456   
457   \param system new active units system
458 */
459 void QDS_ComboBox::unitSystemChanged( const QString& system )
460 {
461   QDS_Datum::unitSystemChanged( system );
462
463   Handle(TColStd_HArray1OfInteger) anIds;
464   Handle(TColStd_HArray1OfExtendedString) aValues, anIcons;
465
466   Handle(DDS_DicItem) aDicItem = dicItem();
467   if ( !aDicItem.IsNull() )
468     aDicItem->GetListOfValues( aValues, anIds, anIcons );
469
470   myValue.clear();
471   myIcons.clear();
472   myDataIds.clear();
473
474   QMap<int, QString> userMap;
475   QIntList::iterator iIt = myUserIds.begin();
476   QStringList::iterator sIt = myUserNames.begin();
477   for ( ; iIt != myUserIds.end() && sIt != myUserNames.end(); ++iIt, ++sIt )
478     userMap.insert( *iIt, *sIt );
479
480   if ( !anIds.IsNull() && !aValues.IsNull() &&
481        anIds->Length() == aValues->Length() )
482   {
483     for ( int i = anIds->Lower(); i <= anIds->Upper(); i++ )
484     {
485       QString aValue;
486       QPixmap aPixmap;
487       int id = anIds->Value( i );
488       if ( userMap.contains( id  ) )
489         aValue = userMap[id];
490       else
491       {
492         aValue = toQString( aValues->Value( i ) );
493         if ( !anIcons.IsNull() && i <= anIcons->Upper() )
494         {
495           QString anIconId = toQString( anIcons->Value( i ) );
496           if ( anIconId != "" )
497             aPixmap = QPixmap( anIconId );
498         }
499       }
500
501       myDataIds.append( id );
502       myValue.insert( id, aValue );
503       myState.insert( id, true );
504       if ( !aPixmap.isNull() )
505         myIcons.insert( id, aPixmap );
506     }
507   }
508
509   for ( iIt = myUserIds.begin(); iIt != myUserIds.end(); ++iIt )
510   {
511     int id = *iIt;
512     if ( !myValue.contains( id  ) )
513     {
514       myDataIds.append( id );
515       myValue.insert( id, userMap[id] );
516     }
517   }
518
519   QIntList del, add;
520   for ( IdStateMap::Iterator it1 = myState.begin(); it1 != myState.end(); ++it1 )
521     if ( !myValue.contains( it1.key() ) )
522       del.append( it1.key() );
523
524   for ( IdValueMap::Iterator it2 = myValue.begin(); it2 != myValue.end(); ++it2 )
525     if ( !myState.contains( it2.key() ) )
526       add.append( it2.key() );
527
528   for ( QIntList::iterator iter1 = del.begin(); iter1 != del.end(); ++iter1 )
529     myState.remove( *iter1 );
530
531   for ( QIntList::iterator iter2 = add.begin(); iter2 != add.end(); ++iter2 )
532     myState.insert( *iter2, true );
533
534   updateComboBox();
535 }
536
537 /*!
538   \brief Called when text in the combo box (editable) is modified by the user.
539   \param txt current text in the combo box editor (not used)
540 */
541 void QDS_ComboBox::onTextChanged( const QString& /*txt*/ )
542 {
543   onParamChanged();
544   emit paramChanged();
545   QString str = getString();
546   emit paramChanged( str );
547 }
548
549 /*!
550   \brief Called when combo box item is activated.
551   \param idx index of teh item being activated
552 */
553 void QDS_ComboBox::onActivated( int idx )
554 {
555   if ( comboBox() )
556   {
557     int ind = comboBox()->currentIndex();
558     comboBox()->setCurrentIndex( -1 );
559     comboBox()->setCurrentIndex( ind );
560   }
561
562   int id = getId( idx );
563   if ( id != -1 )
564   {
565     onParamChanged();
566     QString str = getString();
567     emit activated( id );
568     emit activated( str );
569     emit paramChanged();
570     emit paramChanged( str );
571   }
572 }
573
574 /*!
575   \brief Update combo box.
576 */
577 void QDS_ComboBox::updateComboBox()
578 {
579   QtxComboBox* cb = comboBox();
580
581   int curId = -1;
582
583   bool isClear = false;
584
585   if ( cb )
586   {
587     isClear = cb->isCleared();
588
589     curId = getId( cb->currentIndex() );
590     cb->clear();
591   }
592
593   myIndex.clear();
594
595   int idx = 0;
596   for ( QIntList::const_iterator it = myDataIds.begin(); it != myDataIds.end(); ++it )
597   {
598     int id = *it;
599     if ( !myValue.contains( id ) || !myState.contains( id ) || !myState[id] )
600       continue;
601
602     myIndex.insert( id, idx++ );
603     if ( cb )
604     {
605       if ( myIcons.contains( id ) )
606         cb->addItem( QIcon(myIcons[id]), myValue[id] );
607       else
608         cb->addItem( myValue[id] );
609     }
610   }
611
612   if ( cb && cb->count() )
613   {
614     cb->setFont( cb->font() );
615     cb->updateGeometry();
616
617     if ( isClear )
618       cb->setItemText( cb->currentIndex(), "" );
619     else
620     {
621       if ( getIndex( curId ) != -1 )
622         cb->setCurrentIndex( getIndex( curId ) );
623       if ( curId != getId( cb->currentIndex() ) )
624         onActivated( cb->currentIndex() );
625     }
626   }
627 }
628
629 /*!
630   \brief Get index of the combo box item according to its identifier.
631   \param id item ID
632   \return item index or -1 if not found
633 */
634 int QDS_ComboBox::getIndex( const int id ) const
635 {
636   int idx = -1;
637   if ( myIndex.contains( id ) )
638     idx = myIndex[id];
639   return idx;
640 }
641
642 /*!
643   \brief Get index of the combo box item.
644   \param str combo box item
645   \return item index or -1 if not found
646 */
647 int QDS_ComboBox::getIndex( const QString& str ) const
648 {
649   int idx = -1;
650   QComboBox* cb = comboBox();
651   if ( cb )
652   {
653     for ( int i = 0; i < cb->count() && idx == -1; i++ )
654       if ( cb->itemText( i ) == str )
655         idx = i;
656   }
657   return idx;
658 }
659
660 /*!
661   \brief Get item identifier according to the specified index.
662   \param idx item index
663   \return item ID or -1 if index is out of range
664 */
665 int QDS_ComboBox::getId( const int idx ) const
666 {
667   int id = -1;
668   IdIndexMap::ConstIterator it = myIndex.begin();
669   for (; it != myIndex.end() && id == -1; ++it )
670     if ( it.value() == idx )
671       id = it.key();
672   return id;
673 }
674
675 /*!
676   \brief Get item identifier.
677   \param str combo box item
678   \return item ID or -1 if not found
679 */
680 int QDS_ComboBox::getId( const QString& str ) const
681 {
682   int id = -1;
683   int candidate = -1;
684   IdValueMap::ConstIterator it = myValue.begin();
685   for (; it != myValue.end() && id == -1; ++it )
686   {
687     if ( it.value() == str )
688     {
689       if ( state( it.key() ) )
690         id = it.key();
691       else
692         candidate = it.key();
693     }
694   }
695   if ( id == -1 )
696     id = candidate;
697
698   return id;
699 }
700
701 /*!
702   \fn void QDS_ComboBox::activated( int id );
703   \brief Emitted when the current item in the combo box is changed.
704   \param id current item ID
705 */
706
707 /*!
708   \fn void QDS_ComboBox::activated( const QString& txt );
709   \brief Emitted when the current item in the combo box is changed.
710   \param txt current item text
711 */