Salome HOME
Merge remote-tracking branch 'origin/vsr/fix_single_study_pb' into pre/fix_single_study
[modules/gui.git] / src / SalomeApp / SalomeApp_ListView.cxx
1 // Copyright (C) 2007-2014  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 // File   : SalomeApp_ListView.cxx
24 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
25 //
26 #include "SalomeApp_ListView.h"
27 #include "SalomeApp_Application.h"
28
29 #include "SUIT_ResourceMgr.h"
30 #include "SUIT_Session.h"
31
32 #include <QValidator>
33 #include <QToolButton>
34 #include <QPixmap>
35 #include <QHeaderView>
36 #include <QKeyEvent>
37
38 #include <TColStd_ListOfInteger.hxx>
39 #include <TColStd_ListOfReal.hxx>
40
41 #include <TColStd_ListIteratorOfListOfInteger.hxx>
42 #include <TColStd_ListIteratorOfListOfReal.hxx>
43
44 /*!
45   Used for resizing editing widget
46 */
47 void computeEditGeometry(SalomeApp_ListViewItem* theItem,
48                          SalomeApp_EntityEdit*   theWidget)
49 {
50   if (!theItem)
51     return;
52   QTreeWidget* aListView = theItem->treeWidget();
53   int anEditColumn = theItem->getEditedColumn();
54   if (anEditColumn < 0)
55     return;
56
57   int aX = 0, aY = 0, aW = 0, aH = 0;
58
59   QRect aRect = aListView->visualItemRect(theItem);
60   aX = aListView->header()->sectionViewportPosition(anEditColumn);
61   if (aX < 0)
62     aX = 0; // THIS CAN BE REMOVED
63   QSize aSize = theWidget->getControl()->sizeHint();
64   aH = qMax(aSize.height() , aRect.height() );
65   aY = aRect.y() - ((aH - aRect.height()) / 2);
66   //aW = aListView->columnWidth(anEditColumn); // CAN SUBSTITUTE NEXT 3 ROWS
67   aW = aListView->viewport()->width() - aX;
68   if (aW < 0)
69     aW = 0;
70   theWidget->setGeometry(aX, aY, aW, aH);
71 }
72
73 /*!
74   Constructor
75 */
76 SalomeApp_ListView::SalomeApp_ListView( QWidget* parent )
77   : QTreeWidget/*QtxListView*/( parent )
78 {
79   myMouseEnabled = true;
80   myEditingEnabled = false;
81   setSelectionMode(QAbstractItemView::SingleSelection);
82   setRootIsDecorated(false);
83   setAllColumnsShowFocus(false);
84 //  header()->setClickEnabled(false);
85   header()->setMovable(false);
86
87   myEditedItem = 0;
88   myEdit = 0;
89
90   viewport()->installEventFilter(this);
91
92   connect(this, SIGNAL(itemSelectionChanged()),
93           this, SLOT(onSelectionChanged()));
94   connect(header(), SIGNAL(sizeChange(int, int, int)),
95           this,     SLOT(onHeaderSizeChange(int, int, int)));
96 }
97
98 /*!
99   Destructor
100 */
101 SalomeApp_ListView::~SalomeApp_ListView()
102 {
103   if (myEdit) {
104     delete myEdit;
105   }
106   myEdit = 0;
107   myEditedItem = 0;
108 }
109
110 /*!
111   Updates all data viewer
112 */
113 void SalomeApp_ListView::updateViewer()
114 {
115   // temporary disconnecting selection changed SIGNAL
116   blockSignals(true);
117   QTreeWidgetItemIterator it( this );
118   SalomeApp_ListViewItem* aRoot = (SalomeApp_ListViewItem*)(*it);
119   if (aRoot)
120     aRoot->updateAllLevels();
121   update( contentsRect() );//updateContents();
122   // connecting again selection changed SIGNAL
123   blockSignals(false);
124   emit itemSelectionChanged();
125 }
126
127 /*!
128   Updates currently selected item(s)
129 */
130 void SalomeApp_ListView::updateSelected()
131 {
132   // temporary disconnecting selection changed SIGNAL
133   blockSignals(true);
134   SalomeApp_ListViewItem* aChild = (SalomeApp_ListViewItem*)(selectedItems().first());
135   if (aChild)
136     aChild->updateAllLevels();
137   update( contentsRect() );//updateContents();
138   // connecting again selection changed SIGNAL
139   blockSignals(false);
140   emit itemSelectionChanged();
141 }
142
143 /*!
144   Returns popup client type
145 */
146 QString SalomeApp_ListView::popupClientType() const
147 {
148   return "SalomeApp_ListView";
149 }
150
151 /*!
152   Fills popup menu with items
153 */
154 void SalomeApp_ListView::contextMenuPopup( QMenu* aPopup )
155 {
156   if (aPopup) {
157     // add items here...
158   }
159 }
160
161 /*!
162   Clears view
163 */
164 void SalomeApp_ListView::clear()
165 {
166   if (myEdit) {
167     delete myEdit;
168     myEdit = 0;
169     myEditedItem = 0;
170   }
171   QTreeWidget::clear();
172 }
173
174 /*!
175   \return true if mouse events are enabled
176 */
177 bool SalomeApp_ListView::isMouseEnabled()
178 {
179   return myMouseEnabled;
180 }
181
182 /*!
183   Enables/disables mouse events (excluding MouseMove)
184 */
185 void SalomeApp_ListView::enableMouse(bool enable)
186 {
187   myMouseEnabled = enable;
188 }
189
190 /*!
191   Event filter
192 */
193 bool SalomeApp_ListView::eventFilter(QObject* object, QEvent* event)
194 {
195   if (object == viewport() &&
196        (event->type() == QEvent::MouseButtonPress   ||
197         event->type() == QEvent::MouseButtonRelease ||
198         event->type() == QEvent::MouseButtonDblClick)  &&
199       !isMouseEnabled())
200     return true;
201   else
202     return QTreeWidget::eventFilter(object, event);
203 }
204
205 /*!
206   Setting editing of items availbale/not available
207 */
208 void SalomeApp_ListView::enableEditing(bool theFlag)
209 {
210   myEditingEnabled = theFlag;
211   if (!myEditingEnabled) {
212     if (myEdit) {
213       delete myEdit;
214       myEdit = 0;
215       myEditedItem = 0;
216     }
217   }
218 }
219
220 /*!
221   Says if editing is enabled
222 */
223 bool SalomeApp_ListView::isEnableEditing()
224 {
225   return myEditingEnabled;
226 }
227
228 /*!
229   Calls finishEditing(true)...
230 */
231 void SalomeApp_ListView::accept()
232 {
233   finishEditing(true);
234 }
235
236 /*!
237   Slot, called when selection changed in List Viewer
238 */
239 void SalomeApp_ListView::onSelectionChanged()
240 {
241   if (myEdit) {
242     finishEditing(true);
243     delete myEdit;
244     myEdit = 0;
245     if (myEditedItem && !myEditedItem->isAccepted()) {
246       delete myEditedItem;
247       update( contentsRect() );//updateContents();
248     }
249     myEditedItem = 0;
250   }
251   // editing is allowed in Single Selection Mode only
252   if (selectionMode() != QAbstractItemView::SingleSelection || !isEnableEditing())
253     return;
254   SalomeApp_ListViewItem* anItem = (SalomeApp_ListViewItem*)(selectedItems().first());
255   if (anItem) {
256     if (!anItem->isEditable())
257       return;
258     myEdit = anItem->startEditing();
259     if (myEdit) {
260       connect(myEdit, SIGNAL(returnPressed()), this, SLOT(onEditOk()));
261       connect(myEdit, SIGNAL(escapePressed()), this, SLOT(onEditCancel()));
262       myEditedItem = anItem;
263       myEdit->show();
264       myEdit->setFocus();
265     }
266   }
267 }
268
269 /*!
270   Called when Data Viewer is resized
271 */
272 void SalomeApp_ListView::resizeEvent( QResizeEvent * e)
273 {
274   QTreeWidget::resizeEvent(e);
275   int aW = columnWidth(columnCount()-1);
276   int aX = header()->sectionPosition(columnCount()-1);
277   if (aW < width() - frameWidth() * 2 - aX - 1)
278     setColumnWidth(columnCount()-1, width() - frameWidth() * 2 - aX - 1);
279   update( contentsRect() );//updateContents();
280 }
281
282 /*!
283   Slot, called when columns sizes are changed
284 */
285 void SalomeApp_ListView::onHeaderSizeChange(int, int, int)
286 {
287   int aW = columnWidth(columnCount()-1);
288   int aX = header()->sectionPosition(columnCount()-1);
289   if (aW < width() - frameWidth() * 2 - aX - 1)
290     setColumnWidth(columnCount()-1, width() - frameWidth() * 2 - aX - 1);
291 }
292
293 /*!
294   Handler for paint event
295 */
296 void SalomeApp_ListView::viewportPaintEvent(QPaintEvent* e)
297 {
298   QTreeWidget::paintEvent(e);
299   if (myEditedItem && myEdit) {
300     computeEditGeometry(myEditedItem, myEdit);
301   }
302 }
303
304 /*!
305   Called when user finishes in editing of item
306 */
307 void SalomeApp_ListView::onEditOk()
308 {
309   finishEditing(true);
310 }
311
312 /*!
313   Called when user cancels item editing
314 */
315 void SalomeApp_ListView::onEditCancel()
316 {
317   finishEditing(false);
318 }
319
320 /*!
321   Finishes editing of entity
322 */
323 UpdateType SalomeApp_ListView::finishEditing(bool ok)
324 {
325   UpdateType aNeedsUpdate = utCancel;
326   if (myEditedItem && myEdit)
327   {
328     disconnect(myEdit, SIGNAL(returnPressed()), this, SLOT(onEditOk()));
329     disconnect(myEdit, SIGNAL(escapePressed()), this, SLOT(onEditCancel()));
330     myEditedItem->setAccepted(true);
331     if (ok) {
332       aNeedsUpdate = myEditedItem->finishEditing(myEdit);
333       if (aNeedsUpdate == utCancel) {
334         // something to do here on Cancel...
335       }
336       else {
337         // something to do here on OK...
338       }
339       // updating contents
340       switch (aNeedsUpdate) {
341       case utUpdateItem:
342         {
343           if (myEditedItem)
344             myEditedItem->updateAllLevels();
345           break;
346         }
347       case utUpdateParent:
348         {
349           if (myEditedItem) {
350             SalomeApp_ListViewItem* aParent = (SalomeApp_ListViewItem*)(myEditedItem->parent());
351             if (aParent)
352               aParent->updateAllLevels();
353             else
354               myEditedItem->updateAllLevels();
355           }
356           break;
357         }
358       case utUpdateViewer:
359         {
360           updateViewer();
361           break;
362         }
363       case utUpdateAll:
364         {
365           // doing the same as for utUpdateViewer here
366           // descendants can add extra processing
367           updateViewer();
368           break;
369         }
370       default:
371         break;
372       }
373     }
374   }
375
376   // hide <myEdit> widget
377   if (myEdit) {
378     myEdit->hide();
379   }
380
381   return aNeedsUpdate;
382 }
383
384 /*!
385   \return current tooltip for list view
386   \retval valid rect in success
387 */
388 QRect SalomeApp_ListView::tip(QPoint aPos,
389                               QString& aText,
390                               QRect& dspRect,
391                               QFont& dspFnt) const
392 {
393   QRect result( -1, -1, -1, -1 );
394   SalomeApp_ListViewItem* aItem = (SalomeApp_ListViewItem*)itemAt( aPos );
395   if ( aItem ) {
396     for (int i = 0; i < columnCount(); i++) {
397       QRect aItemRect = aItem->itemRect(i);
398       QRect aTextRect = aItem->textRect(i);
399       if ( !aItem->text(i).isEmpty() &&
400            ( aItemRect.width()  > header()->sectionSize(i) ||
401              aTextRect.left()   < 0 ||
402              aTextRect.top()    < 0 ||
403              aTextRect.right()  > viewport()->width() ||
404              aTextRect.bottom() > viewport()->height() ) ) {
405         // calculating tip data
406         aText   = aItem->tipText();
407         dspRect = aItem->tipRect();
408         dspFnt  = font();
409         if (dspRect.isValid()) {
410           result  = QRect(QPoint(0, aItemRect.top()),
411                           QSize(viewport()->width(), aItemRect.height()));
412         }
413       }
414     }
415   }
416   return result;
417 }
418
419 /*!
420   Constructor
421 */
422 SalomeApp_ListViewItem::SalomeApp_ListViewItem(SalomeApp_ListView* parent) :
423 QTreeWidgetItem( parent )
424 {
425   init();
426 }
427
428 /*!
429   Constructor
430 */
431 SalomeApp_ListViewItem::SalomeApp_ListViewItem(SalomeApp_ListView*     parent,
432                                                SalomeApp_ListViewItem* after) :
433 QTreeWidgetItem( parent, after )
434 {
435   init();
436 }
437
438 /*!
439   Constructor
440 */
441 SalomeApp_ListViewItem::SalomeApp_ListViewItem(SalomeApp_ListView*     parent,
442                                                const QStringList&    theStrings,
443                                                const bool        theEditable) :
444 QTreeWidgetItem(parent, theStrings)
445 {
446   init();
447   setEditable(theEditable);
448 }
449
450 /*!
451   Constructor
452 */
453 SalomeApp_ListViewItem::SalomeApp_ListViewItem(SalomeApp_ListViewItem* parent,
454                                                const QStringList&    theString,
455                                                const bool        theEditable) :
456 QTreeWidgetItem(parent, theString)
457 {
458   init();
459   setEditable(theEditable);
460 }
461
462 /*!
463   Constructor
464 */
465 SalomeApp_ListViewItem::SalomeApp_ListViewItem(SalomeApp_ListViewItem* parent,
466                                                SalomeApp_ListViewItem* after,
467                                                const QString&    theName,
468                                                const bool        theEditable) :
469 QTreeWidgetItem(parent, after)
470 {
471   setData(0,Qt::DisplayRole,QVariant(theName));
472   init();
473   setEditable(theEditable);
474 }
475
476 /*!
477   Constructor
478 */
479 SalomeApp_ListViewItem::SalomeApp_ListViewItem(SalomeApp_ListView*     parent,
480                                                SalomeApp_ListViewItem* after,
481                                                const QString&    theName,
482                                                const bool        theEditable) :
483 QTreeWidgetItem(parent, after)
484 {
485   setData(0,Qt::DisplayRole,QVariant(theName));
486   init();
487   setEditable(theEditable);
488 }
489
490 /*!
491   Constructor
492 */
493 SalomeApp_ListViewItem::SalomeApp_ListViewItem(SalomeApp_ListViewItem* parent,
494                                                SalomeApp_ListViewItem* after,
495                                                const QString&    theName,
496                                                const QString&    theValue,
497                                                const bool        theEditable) :
498 QTreeWidgetItem(parent, after)
499 {
500   setData(0,Qt::DisplayRole,QVariant(theName));
501   setData(1,Qt::DisplayRole,QVariant(theValue));
502   init();
503   setEditable(theEditable);
504 }
505
506 /*!
507   Constructor
508 */
509 SalomeApp_ListViewItem::SalomeApp_ListViewItem(SalomeApp_ListView*     parent,
510                                                SalomeApp_ListViewItem* after,
511                                                const QString&    theName,
512                                                const QString&    theValue,
513                                                const bool        theEditable) :
514 QTreeWidgetItem(parent, after)
515 {
516   setData(0,Qt::DisplayRole,QVariant(theName));
517   setData(1,Qt::DisplayRole,QVariant(theValue));
518   init();
519   setEditable(theEditable);
520 }
521
522 /*!
523   Destructor
524 */
525 SalomeApp_ListViewItem::~SalomeApp_ListViewItem()
526 {
527 }
528
529 /*!
530   Initialization
531 */
532 void SalomeApp_ListViewItem::init()
533 {
534   myEditable    = false;
535   myAccepted    = true;
536   myEditingType = (int)SalomeApp_EntityEdit::etLineEdit;
537   myValueType   = (int)SalomeApp_EntityEdit::vtString;
538   myButtons     = 0;
539   myUserType    = -1;
540 }
541
542 /*!
543   Returns the depth of this item
544 */
545 int SalomeApp_ListViewItem::depth() const
546 {
547   int aDepth = 0;
548   QTreeWidgetItem* aParent = parent();
549   while ( aParent ) {
550     aParent = aParent->parent();
551     aDepth++;
552   }
553   return aDepth;
554 }
555
556 /*!
557   \return text in the first column
558 */
559 QString SalomeApp_ListViewItem::getName() const
560 {
561   return ( treeWidget()->columnCount() > 0 ) ? text(0) : QString("");
562 }
563
564 /*!
565   Sets text in the first column
566 */
567 UpdateType SalomeApp_ListViewItem::setName(const QString& theName)
568 {
569   UpdateType aNeedsUpdate = utCancel;
570   if (treeWidget()->columnCount() > 0) {
571     setText(0, theName);
572     aNeedsUpdate = utNone;
573   }
574   return aNeedsUpdate;
575 }
576
577 /*!
578   \return text in the second column
579 */
580 QString SalomeApp_ListViewItem::getValue() const
581 {
582   return ( treeWidget()->columnCount() > 1 ) ? text(1) : QString("");
583 }
584
585 /*!
586   Sets text in the second column
587 */
588 UpdateType SalomeApp_ListViewItem::setValue(const QString& theValue)
589 {
590   UpdateType aNeedsUpdate = utCancel;
591   if (treeWidget()->columnCount() > 1) {
592     setText(1, theValue);
593     aNeedsUpdate = utNone;
594   }
595   return aNeedsUpdate;
596 }
597
598 /*!
599   \return full path to the entity from the root
600 */
601 QString SalomeApp_ListViewItem::fullName()
602 {
603   QString aFullName = getName();
604   SalomeApp_ListViewItem* aParent = (SalomeApp_ListViewItem*)parent();
605   while(aParent != NULL) {
606     aFullName = aParent->getName() + QString(".") + aFullName;
607     aParent = (SalomeApp_ListViewItem*)(aParent->parent());
608   }
609   return aFullName;
610 }
611
612 /*!
613   expands all entities beginning from this level
614 */
615 void SalomeApp_ListViewItem::openAllLevels()
616 {
617   setExpanded(true);
618   QTreeWidgetItemIterator it( this );
619   SalomeApp_ListViewItem* aChild = (SalomeApp_ListViewItem*)(*it);
620   while( aChild ) {
621     aChild->openAllLevels();
622     ++it;
623     aChild = (SalomeApp_ListViewItem*)(*it);
624   }
625 }
626
627 /*!
628   update all entites beginning from this level
629 */
630 void SalomeApp_ListViewItem::updateAllLevels()
631 {
632   QTreeWidgetItemIterator it( this );
633   SalomeApp_ListViewItem* aChild = (SalomeApp_ListViewItem*)(*it);
634   while( aChild ) {
635     aChild->updateAllLevels();
636     ++it;
637     aChild = (SalomeApp_ListViewItem*)(*it);
638   }
639 }
640
641 /*!
642   \return true if entity is editable
643 */
644 bool SalomeApp_ListViewItem::isEditable() const
645 {
646   return myEditable;
647 }
648
649 /*!
650   Sets editable flag fo the entity
651 */
652 void SalomeApp_ListViewItem::setEditable(bool theEditable)
653 {
654   myEditable = theEditable;
655 }
656
657 /*!
658   \return true if entitiy is accepted after editing
659 */
660 bool SalomeApp_ListViewItem::isAccepted() const
661 {
662   return myAccepted;
663 }
664
665 /*!
666   Sets entitiy accepted or not after editing
667 */
668 void SalomeApp_ListViewItem::setAccepted(bool theAccepted)
669 {
670   myAccepted = theAccepted;
671 }
672
673 /*!
674  \retval type of edit control (default is edit box)
675       \li 0 - edit box
676       \li 1 - combo box
677       \li 2 - editable combo box
678 */
679 int SalomeApp_ListViewItem::getEditingType()
680 {
681   return myEditingType;
682 }
683
684 /*!
685  \retval type of edit control (negative value means none)
686      \li 0 - edit box
687      \li 1 - combo box
688      \li 2 - editable combo box
689 */
690 void SalomeApp_ListViewItem::setEditingType(const int type)
691 {
692   myEditingType = type;
693 }
694
695 /*! \retval edited column, default is last column
696     negative value means there are no editable columns
697 */
698 int SalomeApp_ListViewItem::getEditedColumn()
699 {
700   return treeWidget()->columnCount()-1;
701 }
702
703 /*!
704   \retval type of edited value (string, int, double)
705    default is string
706 */
707 int SalomeApp_ListViewItem::getValueType()
708 {
709   return myValueType;
710 }
711
712 /*!
713   Sets type of edited value
714 */
715 void SalomeApp_ListViewItem::setValueType(const int valueType)
716 {
717   myValueType = valueType;
718 }
719
720 /*!
721   Sets type of edited value
722 */
723 int SalomeApp_ListViewItem::getUserType()
724 {
725   return myUserType;
726 }
727
728 /*!
729   Sets type of edited value
730 */
731 void SalomeApp_ListViewItem::setUserType(const int userType)
732 {
733   myUserType = userType;
734 }
735
736 /*!
737   \return buttons for editing widget (Apply (V), Cancel (X))
738    default is both buttons
739 */
740 int SalomeApp_ListViewItem::getButtons()
741 {
742   return myButtons;
743 }
744
745 /*!
746   Sets buttons for editing widget (Apply (V), Cancel (X))
747 */
748 void SalomeApp_ListViewItem::setButtons(const int buttons)
749 {
750   myButtons = buttons;
751 }
752
753 /*!
754   Creates control for editing and fills it with values
755 */
756 SalomeApp_EntityEdit* SalomeApp_ListViewItem::startEditing()
757 {
758   SalomeApp_EntityEdit* aWidget = 0;
759   QTreeWidget* aListView = treeWidget();
760   if (aListView) {
761     if (!isEditable())
762       return 0;
763     int anEditType   = getEditingType();
764     int aValueType   = getValueType();
765     int aButtons     = getButtons();
766     int anEditColumn = getEditedColumn();
767     if (anEditColumn < 0 || anEditType < 0)
768       return 0;
769     aWidget = new SalomeApp_EntityEdit(aListView->viewport(),
770                                  anEditType,
771                                  aValueType,
772                                  aButtons & SalomeApp_EntityEdit::btApply,
773                                  aButtons & SalomeApp_EntityEdit::btCancel);
774     computeEditGeometry(this, aWidget);
775
776     fillWidgetWithValues(aWidget);
777   }
778   return aWidget;
779 }
780
781 /*!
782   Fills widget with initial values (list or single value)
783 */
784 void SalomeApp_ListViewItem::fillWidgetWithValues(SalomeApp_EntityEdit* theWidget)
785 {
786   int anEditColumn = getEditedColumn();
787   if (theWidget && anEditColumn >= 0 && !text(anEditColumn).isEmpty())
788     theWidget->insertItem(text(anEditColumn), true);
789 }
790
791 /*!
792   Finishes editing of entity
793 */
794 UpdateType SalomeApp_ListViewItem::finishEditing(SalomeApp_EntityEdit* theWidget)
795 {
796   UpdateType aNeedsUpdate = utCancel;
797   try {
798     if (theWidget) {
799       int anEditColumn = getEditedColumn();
800       switch (anEditColumn) {
801       case 0:
802         aNeedsUpdate = setName(theWidget->getText());
803         break;
804       case 1:
805         aNeedsUpdate = setValue(theWidget->getText());
806         break;
807       default:
808         break;
809       }
810     }
811   }
812   catch (...) {
813     MESSAGE( "System error has been caught - SalomeApp_ListViewItem::finishEditing" )
814   }
815   return aNeedsUpdate;
816 }
817
818 /*!
819   Calculates rectangle which should contain item's tip
820 */
821 QRect SalomeApp_ListViewItem::tipRect()
822 {
823   QRect aRect = QRect(-1, -1, -1, -1);
824   QRect aItemRect = treeWidget()->visualItemRect(this);
825   if ( !aItemRect.isValid() )
826     return aItemRect;
827
828   QString aTip = tipText();
829   if (!aTip.isEmpty()) {
830     QRect aRect0 = textRect(0);
831     QFont aFont(treeWidget()->font());
832     QFontMetrics fm(aFont);
833     int iw = fm.width(aTip);
834     aRect = QRect(QPoint(aRect0.x() < 0 ? 0 : aRect0.x(),
835                          aRect0.y()),
836                   QSize (iw,
837                          aRect0.height()));
838   }
839   return aRect;
840 }
841
842 /*!
843   \return text for tooltip
844 */
845 QString SalomeApp_ListViewItem::tipText()
846 {
847   QString aText = getName();
848   if (!getValue().isEmpty())
849     aText += QString(" : ") + getValue();
850   return aText;
851 }
852
853 /*!
854   Calculates rect of item text in viewport coordinates
855 */
856 QRect SalomeApp_ListViewItem::textRect(const int column) const
857 {
858   QRect aItemRect = treeWidget()->visualItemRect( this );
859   if ( !aItemRect.isValid() )
860     return aItemRect;
861
862   QFont aFont(treeWidget()->font());
863   QFontMetrics fm(aFont);
864
865   int decorWidth  = ( treeWidget()->rootIsDecorated() ) ?
866                     ( treeWidget()->indentation() * (depth() + 1) ) :
867                     ( treeWidget()->indentation() *  depth() );
868   int pixmapWidth = ( !icon(column).isNull() ) ?
869                       treeWidget()->iconSize().width() + 2 :
870                       1;
871   int prevWidth = 0;
872   for (int i = 0; i < column; i++)
873     prevWidth += treeWidget()->header()->sectionSize(i);
874   int ix = prevWidth   +
875            pixmapWidth +
876            ((column == 0) ? decorWidth : 0);
877   int iy = aItemRect.y();
878   int iw = fm.width(text(column));
879   int ih = aItemRect.height();
880   if (!icon(column).isNull()) {
881     iy += 1;
882     ih -= 2;
883   }
884   ix -= treeWidget()->contentsRect().left();
885
886   QRect theResult(QPoint(ix, iy), QSize(iw, ih));
887   return theResult;
888 }
889
890 /*!
891   Calculates rect of item data in viewport coordinates
892 */
893 QRect SalomeApp_ListViewItem::itemRect(const int column) const
894 {
895   QRect aItemRect = treeWidget()->visualItemRect( this );
896   if ( !aItemRect.isValid() )
897     return aItemRect;
898
899   QFont aFont(treeWidget()->font());
900   QFontMetrics fm(aFont);
901
902   int decorWidth  = ( treeWidget()->rootIsDecorated() ) ?
903                     ( treeWidget()->indentation() * (depth() + 1) ) :
904                     ( treeWidget()->indentation() *  depth() );
905   int pixmapWidth = ( !icon(column).isNull() ) ?
906                       treeWidget()->iconSize().width() + 2 :
907                       0;
908   int prevWidth = 0;
909   for (int i = 0; i < column; i++)
910     prevWidth += treeWidget()->header()->sectionSize(i);
911   int ix = prevWidth;
912   int iy = aItemRect.y();
913   int iw = pixmapWidth +
914            2 +
915            ((column == 0) ? decorWidth : 0) +
916            fm.width(text(column));
917   int ih = aItemRect.height();
918   ix -= treeWidget()->contentsRect().left();
919
920   QRect theResult(QPoint(ix, iy), QSize(iw, ih));
921   return theResult;
922 }
923
924 /*!
925   Constructor
926 */
927 SalomeApp_EditBox::SalomeApp_EditBox(QWidget* parent) :
928 QLineEdit(parent)
929 {
930 }
931
932 /*!
933   Event filter for key pressing
934 */
935 void SalomeApp_EditBox::keyPressEvent( QKeyEvent *e )
936 {
937   if ( e->key() == Qt::Key_Escape )
938     emit escapePressed();
939   else
940     QLineEdit::keyPressEvent( e );
941   e->accept();
942 }
943
944
945 /*!
946   Constructor
947 */
948 SalomeApp_ComboBox::SalomeApp_ComboBox(bool rw, QWidget* parent, const char* name) :
949 QComboBox(parent)
950 {
951   setEditable( rw );
952   setObjectName( name );
953 }
954
955 /*!
956   Searches item in list and returns its index
957 */
958 int SalomeApp_ComboBox::findItem(const QString& theText)
959 {
960   for (int i = 0; i < count(); i++)
961     if (itemText(i) == theText)
962       return i;
963   return -1;
964 }
965
966 /*!
967   Adds item in combo box
968 */
969 void SalomeApp_ComboBox::insertItem(const QString& theValue,
970                                     int            theIndex)
971 {
972   if (duplicatesEnabled() || findItem(theValue) < 0)
973     QComboBox::insertItem(theIndex, theValue);
974 }
975
976 /*!
977   Adds list of items in combo box
978 */
979 void SalomeApp_ComboBox::insertList(const QStringList& theList)
980 {
981   for (unsigned i = 0; i < theList.count(); i++)
982     insertItem(theList[i]);
983 }
984
985 /*!
986   Adds item in combo box
987 */
988 void SalomeApp_ComboBox::insertItem(const int theValue)
989 {
990   int aNum;
991   bool bOk;
992   for (int i = 0; i < count(); i++) {
993     aNum = itemText(i).toInt(&bOk);
994     if (bOk) {
995       if (aNum > theValue || (aNum == theValue && duplicatesEnabled())) {
996         insertItem(QString::number(theValue),i);
997         return;
998       }
999     }
1000   }
1001   insertItem(QString::number(theValue));
1002 }
1003
1004 /*!
1005   Adds list of items in combo box
1006 */
1007 void SalomeApp_ComboBox::insertList(const TColStd_ListOfInteger& theList)
1008 {
1009   for (TColStd_ListIteratorOfListOfInteger aIter(theList); aIter.More(); aIter.Next())
1010     insertItem(aIter.Value());
1011 }
1012
1013 /*!
1014   Adds item in combo box
1015 */
1016 void SalomeApp_ComboBox::insertItem(const double theValue)
1017 {
1018   double aNum;
1019   bool bOk;
1020   for (int i = 0; i < count(); i++) {
1021     aNum = itemText(i).toDouble(&bOk);
1022     if (bOk) {
1023       if (aNum > theValue || (aNum == theValue && duplicatesEnabled())) {
1024         insertItem(QString::number(theValue), i);
1025         return;
1026       }
1027     }
1028   }
1029   insertItem(QString::number(theValue));
1030 }
1031
1032 /*!
1033   Adds list of items in combo box
1034 */
1035 void SalomeApp_ComboBox::insertList(const TColStd_ListOfReal& theList)
1036 {
1037   for (TColStd_ListIteratorOfListOfReal aIter(theList); aIter.More(); aIter.Next())
1038     insertItem(aIter.Value());
1039 }
1040
1041 #include <qlayout.h>
1042
1043 #define MIN_COMBO_WIDTH     1
1044 #define MIN_EDIT_WIDTH      1
1045
1046 /*!
1047   Constructor
1048 */
1049 SalomeApp_EntityEdit::SalomeApp_EntityEdit(QWidget* parent,
1050                                int      controlType,
1051                                int      valueType,
1052                                bool     butApply,
1053                                bool     butCancel) :
1054 QWidget(parent),
1055 myEdit(0),
1056 myCombo(0),
1057 myApplyBtn(0),
1058 myCancelBtn(0)
1059 {
1060   SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
1061   SUIT_ResourceMgr* mgr = app ? app->resourceMgr() : NULL;
1062
1063   QHBoxLayout* aTopLayout = new QHBoxLayout(this);
1064   aTopLayout->setAlignment( Qt::AlignTop );
1065   aTopLayout->setSpacing( 0 );
1066   aTopLayout->setMargin( 1 );
1067   if (controlType != etLineEdit &&
1068       controlType != etComboBox &&
1069       controlType != etComboEdit)
1070     controlType = etLineEdit;
1071   if (controlType == etComboBox || controlType == etComboEdit) {
1072     // this is an editable combo box
1073     myCombo = new SalomeApp_ComboBox(controlType == etComboEdit, this);
1074     myCombo->setMinimumSize(MIN_COMBO_WIDTH, 0);
1075     myCombo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
1076                                        QSizePolicy::Fixed));
1077     // no insertions
1078     myCombo->setInsertPolicy(QComboBox::NoInsert);
1079     // no duplicates enabled by default
1080     myCombo->setDuplicatesEnabled(false);
1081     aTopLayout->addWidget(myCombo);
1082     // connect signals
1083     connect(myCombo, SIGNAL(activated(const QString&)), this, SLOT(onComboActivated(const QString&)));
1084     connect(myCombo, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChanged(const QString&)));
1085   }
1086   else {
1087     // and this is an edit box
1088     myEdit = new SalomeApp_EditBox(this);
1089     myEdit->setMinimumSize(MIN_EDIT_WIDTH, 0);
1090     myEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
1091                                       QSizePolicy::Fixed));
1092     aTopLayout->addWidget(myEdit);
1093     connect(myEdit, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChanged(const QString&)));
1094     connect(myEdit, SIGNAL(returnPressed()), this, SLOT(onApply()));
1095     connect(myEdit, SIGNAL(escapePressed()), this, SLOT(onCancel()));
1096   }
1097   if (valueType != vtString &&
1098       valueType != vtInteger &&
1099       valueType != vtDouble)
1100     valueType = vtString;
1101   if (valueType == vtInteger)
1102     setValidator(new QIntValidator(this));
1103   else if (valueType == vtDouble)
1104     setValidator(new QDoubleValidator(this));
1105   if (butApply) {
1106     // Apply button (V)
1107     myApplyBtn = new QToolButton(this);
1108
1109     QPixmap anIcon;
1110     if( mgr )
1111       anIcon = mgr->loadPixmap( "SalomeApp", tr( "ICON_APPLY" ), false );
1112
1113     myApplyBtn->setIcon(anIcon);
1114     myApplyBtn->setEnabled(false);
1115     myApplyBtn->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
1116     myApplyBtn->setMinimumSize(16, 16);
1117     myApplyBtn->setMaximumSize(16, 20);
1118     aTopLayout->addWidget(myApplyBtn);
1119     connect(myApplyBtn, SIGNAL(clicked()), this, SLOT(onApply()));
1120   }
1121   if (butCancel) {
1122     // Cancel button (X)
1123     myCancelBtn = new QToolButton(this);
1124     QPixmap anIcon;
1125     if( mgr )
1126       anIcon = mgr->loadPixmap( "SalomeApp", tr( "ICON_CANCEL" ), false );
1127     myCancelBtn->setIcon(anIcon);
1128     myCancelBtn->setEnabled(false);
1129     myCancelBtn->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
1130     myCancelBtn->setMinimumSize(16, 16);
1131     myCancelBtn->setMaximumSize(16, 20);
1132     aTopLayout->addWidget(myCancelBtn);
1133     connect(myCancelBtn, SIGNAL(clicked()), this, SLOT(onCancel()));
1134   }
1135 }
1136
1137 /*!
1138   Destructor
1139 */
1140 SalomeApp_EntityEdit::~SalomeApp_EntityEdit()
1141 {
1142 }
1143
1144 /*!
1145   Clears edit/combo box
1146 */
1147 void SalomeApp_EntityEdit::clear()
1148 {
1149   if (myEdit)
1150     myEdit->clear();
1151   if (myCombo)
1152     myCombo->clear();
1153 }
1154
1155 /*!
1156   \return current text in edit box or combo box
1157 */
1158 QString SalomeApp_EntityEdit::getText()
1159 {
1160   if (myEdit)
1161     return myEdit->text();
1162   else if (myCombo)
1163     return myCombo->currentText();
1164   else
1165     return "";
1166 }
1167
1168 /*!
1169   Sets text
1170 */
1171 void SalomeApp_EntityEdit::setText(const QString& theText)
1172 {
1173   myString = theText;
1174   if (myEdit)
1175     myEdit->setText(theText);
1176   if (myCombo) {
1177     int aFound = myCombo->findItem(theText);
1178     if (aFound >= 0) {
1179       myCombo->setCurrentIndex(aFound);
1180       onTextChanged(theText);
1181     }
1182   }
1183 }
1184
1185 /*!
1186   Adds item in combo box, sets it current if theSetCurrent is true
1187 */
1188 void SalomeApp_EntityEdit::insertItem(const QString& theValue,
1189                                 bool           theSetCurrent,
1190                                 int            theOrder)
1191 {
1192   if (myCombo) {
1193     int aIndexAt = -1;
1194     if (theOrder == atTop)
1195       aIndexAt = 0;
1196     else if (theOrder == atBeforeCurrent && myCombo->count() > 0)
1197       aIndexAt = myCombo->currentIndex();
1198     else if (theOrder == atAfterCurrent &&
1199              myCombo->count() > 0 &&
1200              myCombo->currentIndex() < myCombo->count()-1)
1201       aIndexAt = myCombo->currentIndex() + 1;
1202     myCombo->insertItem(theValue, aIndexAt);
1203   }
1204   if (theSetCurrent)
1205     setText(theValue);
1206 }
1207
1208 /*!
1209   Adds items in combo box, sets item theCurrent as current
1210 */
1211 void SalomeApp_EntityEdit::insertList(const QStringList& theList,
1212                                 const int          theCurrent)
1213 {
1214   if (myCombo)
1215     myCombo->insertList(theList);
1216   if (theCurrent >= 0 && theCurrent < (int)theList.count())
1217     setText(theList[theCurrent]);
1218 }
1219
1220 /*!
1221   Adds item in combo box, sets it current if theSetCurrent is true
1222 */
1223 void SalomeApp_EntityEdit::insertItem(const int theValue,
1224                                 bool      theSetCurrent)
1225 {
1226   if (myCombo) {
1227     myCombo->insertItem(theValue);
1228   }
1229   if (theSetCurrent)
1230     setText(QString::number(theValue));
1231 }
1232
1233 /*!
1234   Adds items in combo box, sets item theCurrent as current
1235 */
1236 void SalomeApp_EntityEdit::insertList(const TColStd_ListOfInteger& theList,
1237                                 const int                    theCurrent)
1238 {
1239   if (myCombo)
1240     myCombo->insertList(theList);
1241
1242   TColStd_ListIteratorOfListOfInteger aIter(theList);
1243   for (unsigned i = 0; aIter.More(); aIter.Next(), i++) {
1244     if (theCurrent == i) {
1245       setText(QString::number(aIter.Value()));
1246       break;
1247     }
1248   }
1249 }
1250
1251 /*!
1252   Adds item in combo box, sets it current if theSetCurrent is true
1253 */
1254 void SalomeApp_EntityEdit::insertItem(const double theValue,
1255                                 bool         theSetCurrent)
1256 {
1257   if (myCombo) {
1258     myCombo->insertItem(theValue);
1259   }
1260   if (theSetCurrent)
1261     setText(QString::number(theValue));
1262 }
1263
1264 /*!
1265   Adds items in combo box, sets item theCurrent as current
1266 */
1267 void SalomeApp_EntityEdit::insertList(const TColStd_ListOfReal& theList,
1268                                 const int                 theCurrent)
1269 {
1270   if (myCombo)
1271     myCombo->insertList(theList);
1272
1273   TColStd_ListIteratorOfListOfReal aIter(theList);
1274   for (unsigned i = 0; aIter.More(); aIter.Next(), i++) {
1275     if (theCurrent == i) {
1276       setText(QString::number(aIter.Value()));
1277       break;
1278     }
1279   }
1280 }
1281
1282 /*! 
1283   \return actual widget
1284 */
1285 QWidget* SalomeApp_EntityEdit::getControl()
1286 {
1287   if (myEdit)
1288     return myEdit;
1289   else if (myCombo)
1290     return myCombo;
1291   else
1292     return 0;
1293 }
1294
1295 /*!
1296   redirect focus to corresponding widget
1297 */
1298 void SalomeApp_EntityEdit::setFocus()
1299 {
1300   if (myEdit) {
1301     myEdit->setFocus();
1302     //myEdit->selectAll();
1303   }
1304   else if (myCombo && myCombo->isEditable()) {
1305     myCombo->setFocus();
1306     //myCombo->lineEdit()->selectAll();
1307   }
1308 }
1309
1310 /*!
1311   Sets validator for the control
1312 */
1313 void SalomeApp_EntityEdit::setValidator(const QValidator* theValidator)
1314 {
1315   if (myEdit)
1316     myEdit->setValidator(theValidator);
1317   if (myCombo)
1318     myCombo->setValidator(theValidator);
1319 }
1320
1321 /*!
1322   Event filter for KeyPress event
1323 */
1324 void SalomeApp_EntityEdit::keyPressEvent( QKeyEvent * e)
1325 {
1326   if ( (e->key() == Qt::Key_Enter ||
1327         e->key() == Qt::Key_Return ) )
1328     onApply();
1329   else if (e->key() == Qt::Key_Escape)
1330     onCancel();
1331 }
1332
1333 /*!
1334   Called when item activated in combo box
1335 */
1336 void SalomeApp_EntityEdit::onComboActivated(const QString& theText)
1337 {
1338   onTextChanged(theText);
1339 }
1340
1341 /*!
1342   Slot, called when text changed in line edit
1343 */
1344 void SalomeApp_EntityEdit::onTextChanged(const QString& theText)
1345 {
1346   if (myApplyBtn)
1347     myApplyBtn->setEnabled(!(theText == myString));
1348   if (myCancelBtn)
1349     myCancelBtn->setEnabled(!(theText == myString));
1350 }
1351
1352 /*!
1353   Slot, called when user presses Cancel button
1354 */
1355 void SalomeApp_EntityEdit::onCancel()
1356 {
1357   setText(myString);
1358   if (myApplyBtn)
1359     myApplyBtn->setEnabled(false);
1360   if (myCancelBtn)
1361     myCancelBtn->setEnabled(false);
1362   emit escapePressed();
1363 }
1364
1365 /*!
1366   Slot, called when user presses Apply button
1367 */
1368 void SalomeApp_EntityEdit::onApply()
1369 {
1370   myString = getText();
1371   if (myApplyBtn)
1372     myApplyBtn->setEnabled(false);
1373   if (myCancelBtn)
1374     myCancelBtn->setEnabled(false);
1375   emit returnPressed();
1376 }
1377
1378 /*!
1379   Shows/hides buttons
1380 */
1381 void SalomeApp_EntityEdit::showButtons(bool show)
1382 {
1383   if (myApplyBtn)
1384     show ? myApplyBtn->show()  : myApplyBtn->hide();
1385   if (myCancelBtn)
1386     show ? myCancelBtn->show() : myCancelBtn->hide();
1387 }
1388
1389 /*!
1390   Enables/disables data duplication (for combo box)
1391 */
1392 void SalomeApp_EntityEdit::setDuplicatesEnabled(bool enabled)
1393 {
1394   if (myCombo)
1395     myCombo->setDuplicatesEnabled(enabled);
1396 }