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