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