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