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