Salome HOME
Merge from V5_1_main 14/05/2010
[modules/smesh.git] / src / StdMeshersGUI / StdMeshersGUI_DistrTable.cxx
1 //  Copyright (C) 2007-2010  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
23 // File   : StdMeshersGUI_DistrTable.cxx
24 // Author : Open CASCADE S.A.S.
25 // SMESH includes
26 //
27 #include "StdMeshersGUI_DistrTable.h"
28
29 #include <SMESHGUI_SpinBox.h>
30
31 // Qt incldues
32 #include <QItemDelegate>
33 #include <QTableWidget>
34 #include <QPushButton>
35 #include <QVBoxLayout>
36 #include <QHBoxLayout>
37
38 #define SPACING 6
39
40 /*!
41   \brief Sort list of points by ascending order.
42   \internal
43 */
44 static void sortData( QList<double>& d )
45 {
46   typedef QPair<double, double> Pair;
47   QList<Pair> pairs;
48   for ( int i = 0; i < d.count() / 2; i++ )
49     pairs.append( Pair( d[i*2], d[i*2+1] ) );
50   
51   qSort( pairs );
52
53   d.clear();
54
55   Pair p;
56   double prevX = 0.0, prevY = 0.0;
57
58   d.append( prevX );
59   d.append( pairs.count() > 0 ? pairs[0].second : prevY );
60
61   foreach( p, pairs ) {
62     if ( p.first > prevX ) {
63       d.append( p.first  );
64       d.append( p.second );
65       prevY = p.second;
66     }
67     prevX = p.first;
68   }
69
70   if ( prevX < 1.0 ) {
71     d.append( 1.0 );
72     d.append( prevY );
73   }
74 }
75
76 /*!
77   \class StdMeshersGUI_DistrTableFrame::SpinBoxDelegate
78   \brief Custom item delegate (uses double spin box to edit table item)
79   \internal
80 */
81
82 class StdMeshersGUI_DistrTableFrame::SpinBoxDelegate : public QItemDelegate
83 {
84 public:
85   SpinBoxDelegate( StdMeshersGUI_DistrTableFrame::Table* );
86   ~SpinBoxDelegate();
87
88   QWidget* createEditor( QWidget*,
89                          const QStyleOptionViewItem&,
90                          const QModelIndex& ) const;
91   void     setEditorData( QWidget*, const QModelIndex&) const;
92   void     setModelData( QWidget*, QAbstractItemModel*, 
93                          const QModelIndex& ) const;
94   void     updateEditorGeometry( QWidget*,
95                                  const QStyleOptionViewItem&, 
96                                  const QModelIndex& ) const;
97
98 private:
99   StdMeshersGUI_DistrTableFrame::Table* myTable;
100 };
101
102 /*!
103   \class StdMeshersGUI_DistrTableFrame::Table
104   \brief Table function widget
105   \internal
106 */
107
108 class StdMeshersGUI_DistrTableFrame::Table : public QTableWidget
109 {
110 private:
111   struct EditorData
112   { 
113     int r, c;
114     SMESHGUI_SpinBox* sb;
115     EditorData() { reset(); }
116     void reset() { r = -1; c = -1; sb = 0; }
117   };
118
119 public:
120   Table( QWidget*, int = 2 );
121   ~Table();
122
123   QList<double> data();
124   void          setData( const QList<double>& );
125
126   double        value( int, int ) const;
127   void          setValue( int, int, double );
128
129   double        argMinimum( int ) const;
130   double        argMaximum( int ) const;
131   double        argStep( int ) const;
132   double        funcMinimum( int ) const;
133   double        funcMaximum( int ) const;
134   double        funcStep( int ) const;
135
136   void          setFuncMinValue( double );
137
138   QSize         sizeHint() const;
139
140   void          addRow();
141   void          deleteRow();
142
143   void          setEditor( int, int, SMESHGUI_SpinBox* );
144
145 protected:
146   void          closeEditor( QWidget*, QAbstractItemDelegate::EndEditHint );
147
148 private:
149   void          setUpRows( bool = false );
150   QSize         cachedSizeHint() const;
151   void          setCachedSizeHint( const QSize& ) const;
152   QList<int>    selectedRows();
153
154 private:
155   double        myFuncMin;
156   QSize         myCachedSizeHint;
157   EditorData    myEditorData;
158 };
159
160 // ---
161 // StdMeshersGUI_DistrTableFrame::SpinBoxDelegate implementation
162 // ---
163
164 StdMeshersGUI_DistrTableFrame::SpinBoxDelegate::
165 SpinBoxDelegate( StdMeshersGUI_DistrTableFrame::Table* parent )
166   : QItemDelegate( parent ), myTable( parent )
167 {
168 }
169
170 StdMeshersGUI_DistrTableFrame::SpinBoxDelegate::
171 ~SpinBoxDelegate()
172 {
173 }
174
175 QWidget* 
176 StdMeshersGUI_DistrTableFrame::SpinBoxDelegate::
177 createEditor( QWidget* parent,
178               const QStyleOptionViewItem& /*option*/,
179               const QModelIndex& index ) const
180 {
181   SMESHGUI_SpinBox* sb = new SMESHGUI_SpinBox( parent );
182   
183   sb->setAcceptNames(false); // No Notebook variables allowed
184   double aMin = index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? 
185                   myTable->argMinimum( index.row() ) : 
186                   myTable->funcMinimum( index.row() );
187   double aMax = index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? 
188                   myTable->argMaximum( index.row() ) : 
189                   myTable->funcMaximum( index.row() );
190   double aStep = index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? 
191                      myTable->argStep( index.row() ) : 
192                      myTable->funcStep( index.row() );
193   sb->RangeStepAndValidator( aMin, aMax, aStep, "parametric_precision" );
194   sb->setFrame(false);
195
196   myTable->setEditor( index.row(), index.column(), sb );  
197   return sb;
198 }
199
200 void 
201 StdMeshersGUI_DistrTableFrame::SpinBoxDelegate::
202 setEditorData( QWidget* editor, const QModelIndex& index ) const
203 {
204   QString value = index.model()->data(index, Qt::DisplayRole).toString();
205   SMESHGUI_SpinBox* sb = static_cast<SMESHGUI_SpinBox*>(editor);
206
207   bool bOk = false;
208   double v = value.toDouble( &bOk );
209   if ( !bOk ) v = sb->minimum();
210
211   sb->setValue( v );
212 }
213
214 void
215 StdMeshersGUI_DistrTableFrame::SpinBoxDelegate::
216 setModelData( QWidget* editor, QAbstractItemModel* model, 
217               const QModelIndex& index ) const
218 {
219   SMESHGUI_SpinBox* sb = static_cast<SMESHGUI_SpinBox*>(editor);
220   model->setData( index, QString::number( sb->value() ), Qt::DisplayRole );
221 }
222
223 void 
224 StdMeshersGUI_DistrTableFrame::SpinBoxDelegate::
225 updateEditorGeometry( QWidget* editor,
226                       const QStyleOptionViewItem& option, 
227                       const QModelIndex& /*index*/ ) const
228 {
229   editor->setGeometry( option.rect );
230 }
231
232 // ---
233 // StdMeshersGUI_DistrTableFrame::Table implementation
234 // ---
235
236 StdMeshersGUI_DistrTableFrame::Table::
237 Table( QWidget* parent, int rows )
238   : QTableWidget( parent ), myFuncMin( 0.0 )
239 {
240   setItemDelegate( new StdMeshersGUI_DistrTableFrame::SpinBoxDelegate( this ) );
241
242   setColumnCount( 2 );
243
244   QStringList labs;
245   labs << "t" << "f(t)";
246   setHorizontalHeaderLabels( labs );
247
248   while( rows-- )
249     addRow();
250
251   setUpRows( true );
252 }
253
254 void
255 StdMeshersGUI_DistrTableFrame::Table::
256 setEditor( int r, int c, SMESHGUI_SpinBox* sb )
257 {
258   myEditorData.r  = r;
259   myEditorData.c  = c;
260   myEditorData.sb = sb;
261 }
262
263 StdMeshersGUI_DistrTableFrame::Table::
264 ~Table()
265 {
266 }
267
268 QList<double>
269 StdMeshersGUI_DistrTableFrame::Table::
270 data()
271 {
272   closePersistentEditor( currentItem() );
273
274   QList<double> d;
275   for ( int r = 0; r < rowCount(); r++ ) {
276     d.append( value( r, ArgColumn ) );
277     d.append( value( r, FuncColumn ) );
278   }
279   return d;
280 }
281
282 void
283 StdMeshersGUI_DistrTableFrame::Table::
284 setData( const QList<double>& d )
285 {
286   closePersistentEditor( currentItem() );
287
288   setRowCount( d.count() / 2 );
289   for ( int r = 0; r < rowCount(); r++ ) {
290     setValue( r, ArgColumn,  d[r*2]   );
291     setValue( r, FuncColumn, d[r*2+1] );
292   }
293 }
294
295 double
296 StdMeshersGUI_DistrTableFrame::Table::
297 value( int r, int c ) const
298 {
299   if ( r < 0 || r > rowCount() || c < 0 || c > columnCount() || !item( r, c ) )
300     return 0.0;
301
302   return item( r, c )->text().toDouble();
303 }
304
305 void
306 StdMeshersGUI_DistrTableFrame::Table::
307 setValue( int r, int c, double v )
308 {
309   if ( r < 0 || r > rowCount() || c < 0 || c > columnCount() )
310     return;
311
312   if ( c == FuncColumn && v < funcMinimum( r ) )
313     v = funcMinimum( r ); // correct func value according to the valid min value
314   if ( c == FuncColumn && v > funcMaximum( r ) )
315     v = funcMaximum( r ); // correct func value according to the valid max value
316   else if ( r == ArgColumn && v < argMinimum( r ) )
317     v = argMinimum( r );  // correct arg  value according to the valid min value
318   else if ( r == ArgColumn && v > argMaximum( r ) )
319     v = argMaximum( r );  // correct arg  value according to the valid max value
320
321   if ( !item( r, c ) )
322     setItem( r, c, new QTableWidgetItem );
323   item( r, c )->setText( QString::number( v ) );
324 }
325
326 double
327 StdMeshersGUI_DistrTableFrame::Table::
328 argMinimum( int r ) const
329 {
330   // for the first row the minimum value is always 0.0
331   // for the other rows the minumum value is the above row's value
332   double val = 0.0;
333   if ( r > 0 && r < rowCount() )
334     val = value( r-1, ArgColumn );
335   return val;
336 }
337
338 double
339 StdMeshersGUI_DistrTableFrame::Table::
340 argMaximum( int r ) const
341 {
342   // for the last row the maximum value is always 1.0
343   // for the other rows the maxumum value is the below row's value
344   double val = 1.0;
345   if ( r >= 0 && r < rowCount()-1 ) {
346     val = value( r+1, ArgColumn );
347   }
348   return val;
349 }
350
351 double
352 StdMeshersGUI_DistrTableFrame::Table::
353 argStep( int /*r*/ ) const
354 {
355   // correct this to provide more smart behaviour if needed
356   return 0.1;
357 }
358
359 double
360 StdMeshersGUI_DistrTableFrame::Table::
361 funcMinimum( int /*r*/ ) const
362 {
363   // correct this to provide more smart behaviour if needed
364   return myFuncMin;
365 }
366
367 double
368 StdMeshersGUI_DistrTableFrame::Table::
369 funcMaximum( int /*r*/ ) const
370 {
371   // correct this to provide more smart behaviour if needed
372   return 1e20;
373 }
374
375 double
376 StdMeshersGUI_DistrTableFrame::Table::
377 funcStep( int /*r*/ ) const
378 {
379   // correct this to provide more smart behaviour if needed
380   return 1.0;
381 }
382
383 void
384 StdMeshersGUI_DistrTableFrame::Table::
385 setFuncMinValue( double val )
386 {
387   myFuncMin = val;
388
389   QTableWidgetItem* i = currentItem();
390   if ( i && 
391        i->row()    == myEditorData.r && 
392        i->column() == myEditorData.c && 
393        i->column() == FuncColumn     &&
394        myEditorData.sb ) {
395     myEditorData.sb->setMinimum( myFuncMin );
396   }
397   else {
398     closePersistentEditor( currentItem() );
399   }
400
401   for ( int r = 0; r < rowCount(); r++ ) {
402     double v = item( r, FuncColumn )->text().toDouble();
403     if ( v < myFuncMin ) 
404       item( r, FuncColumn )->setText( QString::number( myFuncMin ) );
405   }
406 }
407
408 QSize
409 StdMeshersGUI_DistrTableFrame::Table::
410 sizeHint() const
411 {
412   if( cachedSizeHint().isValid() )
413     return cachedSizeHint();
414
415   QSize sh = QTableWidget::sizeHint();
416   if( sh.width() < 400 )
417     sh.setWidth( 400 );
418   if( sh.height() < 200 )
419     sh.setHeight( 200 );
420
421   setCachedSizeHint( sh );
422   return sh;
423 }
424
425 void
426 StdMeshersGUI_DistrTableFrame::Table::
427 addRow()
428 {
429   int r = currentRow() >= 0 ? currentRow() : ( rowCount() > 0 ? rowCount() - 1 : 0 );
430   insertRow( r );
431
432   double argMin  = argMinimum( r );
433   double funcMin = funcMinimum( r );
434   
435   setItem( r, ArgColumn,  new QTableWidgetItem( QString::number( argMin ) ) );
436   setItem( r, FuncColumn, new QTableWidgetItem( QString::number( funcMin ) ) );
437 }
438
439 void
440 StdMeshersGUI_DistrTableFrame::Table::
441 deleteRow()
442 {
443   QList<int> selRows = selectedRows();
444   for ( int r = selRows.count()-1; r >= 0; r-- )
445     removeRow( r );
446 }
447
448 void
449 StdMeshersGUI_DistrTableFrame::Table::
450 closeEditor( QWidget* editor, QAbstractItemDelegate::EndEditHint hint )
451 {
452   myEditorData.reset();
453   QTableWidget::closeEditor( editor, hint );
454 }
455
456 void
457 StdMeshersGUI_DistrTableFrame::Table::
458 setUpRows( bool autoset )
459 {
460   if ( rowCount() < 1 )
461     return;
462   if ( autoset ) {
463     double s = argMaximum( rowCount()-1 ) / rowCount();
464     for ( int r = 0; r < rowCount()-1; r++ )
465       setValue( r, ArgColumn, r * s );
466     setValue( rowCount()-1, ArgColumn, argMaximum( rowCount()-1 ) );
467   }
468   else {
469     // TODO
470   }
471 }
472
473 QSize
474 StdMeshersGUI_DistrTableFrame::Table::
475 cachedSizeHint() const
476 {
477   return myCachedSizeHint;
478 }
479
480 void
481 StdMeshersGUI_DistrTableFrame::Table::
482 setCachedSizeHint( const QSize& s ) const
483 {
484   Table* that = const_cast<Table*>( this );
485   that->myCachedSizeHint = s;
486 }
487
488 QList<int>
489 StdMeshersGUI_DistrTableFrame::Table::
490 selectedRows()
491 {
492   QList<int> l;
493   QList<QTableWidgetItem*> selItems = selectedItems();
494   QTableWidgetItem* i;
495   foreach( i, selItems )
496     if ( !l.contains( i->row() ) ) l.append( i->row() );
497   qSort( l );
498   return l;
499 }
500
501 /*!
502   \class StdMeshersGUI_DistrTableFrame
503   \brief Distribution table widget
504 */
505
506 StdMeshersGUI_DistrTableFrame::
507 StdMeshersGUI_DistrTableFrame( QWidget* parent )
508   : QWidget( parent )
509 {
510   QVBoxLayout* main = new QVBoxLayout( this );
511   main->setMargin( 0 );
512   main->setSpacing( 0 );
513
514   // ---
515   myTable = new Table( this );
516   connect( myTable, SIGNAL( valueChanged( int, int ) ), this, SIGNAL( valueChanged( int, int ) ) );
517   
518   // ---
519   QWidget* aButFrame = new QWidget( this );
520   QHBoxLayout* butLay = new QHBoxLayout( aButFrame );
521   butLay->setContentsMargins( 0, SPACING, 0, SPACING );
522   butLay->setSpacing( SPACING );
523
524   myButtons[ InsertRowBtn ] = new QPushButton( tr( "SMESH_INSERT_ROW" ), aButFrame );
525   myButtons[ RemoveRowBtn ] = new QPushButton( tr( "SMESH_REMOVE_ROW" ), aButFrame );
526
527   butLay->addWidget( myButtons[ InsertRowBtn ] );
528   butLay->addWidget( myButtons[ RemoveRowBtn ] );
529   butLay->addStretch();
530
531   // ---
532   main->addWidget( myTable );
533   main->addWidget( aButFrame );
534   
535   // ---
536   connect( myButtons[ InsertRowBtn ], SIGNAL( clicked() ), this, SLOT( onInsert() ) );
537   connect( myButtons[ RemoveRowBtn ], SIGNAL( clicked() ), this, SLOT( onRemove() ) );
538   connect( myTable, SIGNAL( currentCellChanged( int, int, int, int ) ),
539            this,    SIGNAL( currentChanged( int, int ) ) );
540   connect( myTable, SIGNAL( cellChanged( int, int ) ),
541            this,    SIGNAL( valueChanged( int, int ) ) );
542 }
543
544 StdMeshersGUI_DistrTableFrame::
545 ~StdMeshersGUI_DistrTableFrame()
546 {
547 }
548
549 void
550 StdMeshersGUI_DistrTableFrame::
551 showButton( const TableButton b, const bool on )
552 {
553   if ( button( b ) ) button( b )->setVisible( on );
554 }
555
556 bool
557 StdMeshersGUI_DistrTableFrame::
558 isButtonShown( const TableButton b ) const
559 {
560   return button( b ) ? button( b )->isVisible() : false;
561 }
562   
563 void
564 StdMeshersGUI_DistrTableFrame::
565 data( DataArray& array ) const
566 {
567   QList<double> d = myTable->data();
568   sortData( d );
569
570   array.length( d.count() );
571   for ( int i = 0; i < d.count(); i++ )
572     array[i] = d[i];
573 }
574
575 void
576 StdMeshersGUI_DistrTableFrame::
577 setData( const DataArray& array )
578 {
579   QList<double> d;
580   for ( int i = 0; i < array.length(); i++ )
581     d.append( array[i] );
582
583   sortData( d );
584   myTable->setData( d );
585 }
586
587 void
588 StdMeshersGUI_DistrTableFrame::
589 setFuncMinValue( double v )
590 {
591   myTable->setFuncMinValue( v );
592 }
593
594 QPushButton*
595 StdMeshersGUI_DistrTableFrame::
596 button( const TableButton b ) const
597 {
598   return myButtons.contains( b ) ? myButtons[ b ] : 0;
599 }
600
601 void
602 StdMeshersGUI_DistrTableFrame::
603 onInsert()
604 {
605   myTable->addRow();
606 }
607
608 void
609 StdMeshersGUI_DistrTableFrame::
610 onRemove()
611 {
612   myTable->deleteRow();
613 }