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