Salome HOME
refs #618
[modules/hydro.git] / src / HYDROGUI / HYDROGUI_PriorityTableModel.cxx
1 // Copyright (C) 2014-2015  EDF-R&D
2 // This library is free software; you can redistribute it and/or
3 // modify it under the terms of the GNU Lesser General Public
4 // License as published by the Free Software Foundation; either
5 // version 2.1 of the License, or (at your option) any later version.
6 //
7 // This library is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 // Lesser General Public License for more details.
11 //
12 // You should have received a copy of the GNU Lesser General Public
13 // License along with this library; if not, write to the Free Software
14 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
15 //
16 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
17 //
18
19 #include "HYDROGUI_PriorityTableModel.h"
20
21 #include <HYDROData_LandCover.h>
22 #include <HYDROGUI_DataObject.h>
23
24 /**
25   Constructor.
26   @param theParent the parent object
27 */
28 HYDROGUI_PriorityTableModel::HYDROGUI_PriorityTableModel( QObject* theParent )
29  : QAbstractTableModel( theParent ),
30    myColumnCount( 4 )
31 {
32 }
33
34 /**
35   Destructor.
36 */
37 HYDROGUI_PriorityTableModel::~HYDROGUI_PriorityTableModel()
38 {
39 }
40
41 /**
42  */
43 Qt::ItemFlags   HYDROGUI_PriorityTableModel::flags( const QModelIndex & theIndex ) const
44 {
45   return Qt::ItemIsSelectable   | Qt::ItemIsEditable | Qt::ItemIsEnabled;
46 }
47
48 /**
49  */
50 QVariant HYDROGUI_PriorityTableModel::data( const QModelIndex &theIndex, int theRole ) const
51 {
52   QVariant aVariant;
53
54   int aRow = theIndex.row();
55   int aColumn = theIndex.column();
56
57   if ( !theIndex.isValid() || aRow > myRules.size() ) {
58     return aVariant;
59   }
60   
61   if ( theRole == Qt::DisplayRole ) {
62     HYDROData_CustomRule aRule = myRules.at(aRow);
63
64     if ( aColumn == 0 ) {
65       aVariant = aRule.Object1->GetName();
66     } else if ( aColumn == 1 ) {
67       aVariant = priorityToString( aRule.Priority );
68     } else if ( aColumn == 2 ) {
69       aVariant = aRule.Object2->GetName();
70     } else if ( aColumn == 3 ) {
71       aVariant = mergeTypeToString( aRule.MergeType );
72     }
73   } else if ( theRole == Qt::EditRole ) {
74     HYDROData_CustomRule aRule = myRules.at(aRow);
75
76     if ( aColumn == 0 ) {
77       aVariant = aRule.Object1->GetName();
78     } else if ( aColumn == 1 ) {
79       aVariant = aRule.Priority;
80     } else if ( aColumn == 2 ) {
81       aVariant = aRule.Object2->GetName();
82     } else if ( aColumn == 3 ) {
83       aVariant = aRule.MergeType;
84     }
85   } else if ( theRole ==  Qt::UserRole ) {
86     if ( aColumn == 0 || aColumn == 2 ) {
87       QStringList aNames;
88       HYDROData_CustomRule aRule = myRules.at( aRow );
89       Handle(HYDROData_Entity) aUsedObject = aColumn == 0 ? aRule.Object2 : aRule.Object1;
90       if ( !aUsedObject.IsNull() ) {
91         aNames = getAvailablePairs( aUsedObject );
92       }
93       aVariant = aNames;
94     } else if ( aColumn == 1 ) {
95       QMap<QString, QVariant> aMap;
96       aMap.insert( priorityToString( LESS ), LESS );
97       aMap.insert( priorityToString( GREATER ), GREATER );
98       aVariant = QVariant( aMap );
99     } else if ( aColumn == 3 ) {
100       QMap<QString, QVariant> aMap;
101       aMap.insert( mergeTypeToString( HYDROData_Zone::Merge_Object ), HYDROData_Zone::Merge_Object );
102       aMap.insert( mergeTypeToString( HYDROData_Zone::Merge_ZMIN ), HYDROData_Zone::Merge_ZMIN );
103       aMap.insert( mergeTypeToString( HYDROData_Zone::Merge_ZMAX ), HYDROData_Zone::Merge_ZMAX );
104       aVariant = QVariant( aMap );
105     } 
106   } else if ( theRole == Qt::TextAlignmentRole ) {
107     aVariant = Qt::AlignCenter;
108   }
109
110   return aVariant;
111 }
112
113 /**
114  */
115 bool HYDROGUI_PriorityTableModel::setData( const QModelIndex & theIndex, const QVariant & theValue, int theRole )
116 {
117   int aRow = theIndex.row();
118   if ( !theIndex.isValid() || aRow > myRules.size() ) {
119     return false;
120   }
121
122   bool aRes = false;
123   
124   if ( theRole ==  Qt::EditRole ) {
125     bool aRuleChanged = false;
126     myPrevRules = myRules;
127
128     int aColumn = theIndex.column();
129
130     if ( aColumn == 0 || aColumn == 2 ) {
131       Handle(HYDROData_Entity) anObject;
132       QString anObjName = theValue.toString();
133       foreach ( const Handle(HYDROData_Entity) anObj, myObjects ) {
134         if ( anObj->GetName() == anObjName ) {
135           anObject = anObj;
136           break;
137         }
138       }
139       if ( !anObject.IsNull() ) {
140         HYDROData_CustomRule anEditedRule = myRules[aRow];
141         
142         QString anEntryNew = HYDROGUI_DataObject::dataObjectEntry( anObject );
143         if ( aColumn == 0 ) {
144           QString anEntryOld = HYDROGUI_DataObject::dataObjectEntry( anEditedRule.Object1 );
145           if ( anEntryOld != anEntryNew )
146             aRuleChanged = true;
147           anEditedRule.Object1 = anObject;
148         } else {
149           QString anEntryOld = HYDROGUI_DataObject::dataObjectEntry( anEditedRule.Object2 );
150           if ( anEntryOld != anEntryNew )
151             aRuleChanged = true;
152           anEditedRule.Object2 = anObject;
153         }
154         
155         if ( !isUsed( anEditedRule.Object1, anEditedRule.Object2 ) ) {
156           myRules[aRow] = anEditedRule;
157           aRes = true;
158         } else {
159           aRuleChanged = false;
160           emit showError( tr("ALREADY_EXISTS") );
161         }
162       }
163     } else if ( aColumn == 1 ) {
164       HYDROData_PriorityType aNewPriority = (HYDROData_PriorityType)theValue.toInt();
165       if ( myRules[aRow].Priority != aNewPriority )
166         aRuleChanged = true;
167       myRules[aRow].Priority = aNewPriority;
168     } else if ( aColumn == 3 ) {
169       HYDROData_Zone::MergeType aNewMergeType = (HYDROData_Zone::MergeType)theValue.toInt();
170       if ( myRules[aRow].MergeType != aNewMergeType )
171         aRuleChanged = true;
172       myRules[aRow].MergeType = aNewMergeType;
173     }
174
175     if ( aRuleChanged )
176       emit ruleChanged();
177   }
178
179   return aRes;
180 }
181
182 /**
183  */
184 int HYDROGUI_PriorityTableModel::rowCount( const QModelIndex &theParent ) const
185 {
186   return myRules.count();
187 }
188
189 /**
190  */
191 int HYDROGUI_PriorityTableModel::columnCount( const QModelIndex &theParent ) const
192 {
193   return myColumnCount;
194 }
195
196 /**
197   Set rules.
198   @param theRules the list of rules
199 */
200 void HYDROGUI_PriorityTableModel::setRules( const HYDROData_ListOfRules& theRules )
201 {
202   beginResetModel();
203   myPrevRules = myRules;
204   myRules = theRules;
205   endResetModel();
206 }
207
208 /**
209   Get rules.
210   @return the list of rules
211 */
212 HYDROData_ListOfRules HYDROGUI_PriorityTableModel::getRules() const
213 {
214   return myRules;
215 }
216
217 /**
218 */
219 QVariant HYDROGUI_PriorityTableModel::headerData( int theSection,
220                                                   Qt::Orientation theOrientation,
221                                                   int theRole ) const
222 {
223   QVariant aData;
224
225   if ( theRole != Qt::DisplayRole ) {
226     return aData;
227   }
228
229   if ( theOrientation == Qt::Horizontal ) {
230     switch( theSection )
231     {
232     case 0:
233       aData = tr( "OBJECT1" );
234       break;
235     case 1:
236       aData = tr( "PRIORITY" );
237       break;
238     case 2:
239       aData = tr( "OBJECT2" );
240       break;
241     case 3:
242       {
243         if ( getObjectsKind() != KIND_LAND_COVER )
244           aData = tr( "BATHYMETRY" );
245       }
246       break;
247     };
248   } else if ( theOrientation == Qt::Vertical ) {
249     aData = theSection + 1;
250   }
251
252   return aData;
253 }
254
255 /**
256   Set objects which could be used for rules definition.
257   @param theObjects the ordered list of objects
258  */
259 void HYDROGUI_PriorityTableModel::setObjects( const QList<Handle(HYDROData_Entity)>& theObjects )
260 {
261   myObjects = theObjects;
262     
263   myPrevRules = myRules;
264
265   beginResetModel();
266
267   // Remove rules which use objects which are no longer available
268   QStringList aNames = getAvailableObjectNames();
269   QMutableListIterator<HYDROData_CustomRule> anIter( myRules );
270   while ( anIter.hasNext() ) {
271     HYDROData_CustomRule aRule = anIter.next();
272      if ( !aNames.contains( aRule.Object1->GetName() ) || 
273           !aNames.contains( aRule.Object2->GetName() ) ) {
274       anIter.remove();
275     }
276   }
277
278   endResetModel();
279 }
280
281 /**
282  Create new rule.
283  @return true if a rule has been created successfully
284  */
285 bool HYDROGUI_PriorityTableModel::createNewRule() 
286 {
287   if ( !canCreateNewRule() ) {
288     return false;
289   }
290   
291   // Existing pairs of object indexes
292   QStringList aNames = getAvailableObjectNames();
293   QList< QPair<int, int> > aRules;
294   foreach( const HYDROData_CustomRule& aRule, getRules() ) {
295     int anIndex1 = aNames.indexOf( aRule.Object1->GetName() );
296     int anIndex2 = aNames.indexOf( aRule.Object2->GetName() );
297     if ( anIndex1 >= 0 && anIndex2 >= 0 ) {
298       aRules << QPair<int, int>( anIndex1, anIndex2 );
299       aRules << QPair<int, int>( anIndex2, anIndex1 );
300     }
301   }
302     
303   // Try to find:
304   // 1. the new pair of objects 
305   // 2. the priority type corresponding to the objects order
306   Handle(HYDROData_Entity) anObject1, anObject2;
307   HYDROData_PriorityType aPriorityType = GREATER;
308
309   int aNbObjects = myObjects.count();
310   for ( int anIndex1 = 0; anIndex1 < aNbObjects; anIndex1++ ) {
311     bool isFound = false;
312
313     for ( int anIndex2 = 0; anIndex2 < aNbObjects; anIndex2++ ) {
314       if ( anIndex1 == anIndex2 ) {
315         continue;
316       }
317
318       if ( !aRules.contains( QPair<int, int>( anIndex1, anIndex2 ) ) ) {
319         anObject1 = myObjects.at( anIndex1 );
320         anObject2 = myObjects.at( anIndex2 );
321         if ( anIndex1 > anIndex2 ) {
322           aPriorityType = LESS;
323         }
324         isFound = true;
325         break;
326       }
327     }
328
329     if ( isFound ) {
330       break;
331     }
332   }
333
334   // Create a new rule
335   bool isCreated = false;
336
337   if ( !anObject1.IsNull() && !anObject2.IsNull() ) {
338     HYDROData_CustomRule aNewRule;
339     aNewRule.Object1 = anObject1;
340     aNewRule.Object2 = anObject2;
341     aNewRule.Priority = aPriorityType;
342     aNewRule.MergeType = HYDROData_Zone::Merge_ZMIN;
343
344     myPrevRules = myRules;
345
346     beginResetModel();
347     myRules << aNewRule;
348     endResetModel();
349
350     isCreated = true;
351   }
352
353   return isCreated;
354 }
355
356 /**
357  Check if a new rule can be created.
358  @return true if a new rule could be created
359  */
360 bool HYDROGUI_PriorityTableModel::canCreateNewRule() const
361 {
362   int aNbObjects = myObjects.count();
363
364   return ( myRules.count() < (aNbObjects - 1)*aNbObjects/2 );
365 }
366
367 /**
368  */
369 bool HYDROGUI_PriorityTableModel::removeRows ( int theRow, int theCount, const QModelIndex & theParent )
370 {
371   if ( myRules.isEmpty() || theRow < 0 || theRow >= myRules.count() ) {
372     return false;
373   }
374
375   int aLastRow = theRow + theCount - 1;
376   if ( aLastRow > myRules.count() ) {
377     aLastRow = myRules.count() - 1;
378   }
379
380   myPrevRules = myRules;
381
382   beginRemoveRows( theParent, theRow, aLastRow );
383
384   // Remove the corresponding rules
385   for ( int i = 1; i <= theCount; i++ ) {
386     myRules.removeAt( theRow );
387   }
388
389   endRemoveRows();
390
391   return true;
392 }
393
394 /**
395  Remove all data from the model.
396  @return true if at least one row is removed
397  */
398 bool HYDROGUI_PriorityTableModel::removeAll()
399 {
400   return removeRows( 0, rowCount() );
401 }
402
403 /**
404  Get available pairs for the given object.
405  @param theObject the object
406  @return the list of object names
407  */
408 QStringList HYDROGUI_PriorityTableModel::getAvailablePairs(  const Handle(HYDROData_Entity)& theObject ) const
409 {
410   QStringList aNames;
411
412   if ( !theObject.IsNull() ) {
413     aNames = getAvailableObjectNames();
414     aNames.removeAll( theObject->GetName() );
415   }
416
417   return aNames;
418 }
419
420 /**
421  Remove the rows.
422  @params theRows the list of rows to remove
423  @return true if at least one row is removed
424  */
425 bool HYDROGUI_PriorityTableModel::removeRows ( const QList<int> theRows )
426 {
427   QList<int> aSortedRows = theRows;
428   qSort( aSortedRows );
429
430   int aRowToRemove = -1;
431   int aNbRemoved = 0;
432   foreach ( int aRow, aSortedRows ) {
433     aRowToRemove = aRow - aNbRemoved;
434     if ( removeRow( aRowToRemove ) ) {
435       aNbRemoved++;
436     }
437   }
438
439   return ( aNbRemoved > 0 );
440 }
441
442 /**
443  Get priority string representation.
444  @param thePriority the priority
445  */
446 QString HYDROGUI_PriorityTableModel::priorityToString( const int thePriority ) const
447 {
448   switch( thePriority )
449   {
450   case LESS:
451     return tr( "LESS" );
452   case GREATER:
453     return tr( "GREATER" );
454   default:
455     return QString();
456   };
457 }
458
459 /**
460  Get merge type string representation.
461  @param theMergeType the merge type
462  */
463 QString HYDROGUI_PriorityTableModel::mergeTypeToString( const int theMergeType ) const
464 {
465   switch( theMergeType )
466   {
467   case HYDROData_Zone::Merge_Object:
468     return tr( "PRIORITY" );
469   case HYDROData_Zone::Merge_ZMIN:
470     return tr( "ZMIN" );
471   case HYDROData_Zone::Merge_ZMAX:
472     return tr( "ZMAX" );
473   default:
474     return QString();
475   };
476 }
477
478 /**
479   Check if the given pair of objects is already used.
480   @return true if the pair is used
481  */
482 bool HYDROGUI_PriorityTableModel::isUsed( const Handle(HYDROData_Entity)& theObj1, 
483                                           const Handle(HYDROData_Entity)& theObj2 ) const
484 {
485   bool isUsed = false;
486
487   foreach ( const HYDROData_CustomRule& aRule, myRules ) {
488     if ( ( aRule.Object1->GetName() == theObj1->GetName() && 
489            aRule.Object2->GetName() == theObj2->GetName() ) ||
490          ( aRule.Object1->GetName() == theObj2->GetName() && 
491            aRule.Object2->GetName() == theObj1->GetName() ) ) {
492       isUsed = true;
493       break;
494     }
495   }       
496
497   return isUsed;
498 }
499
500 /**
501   Get available object names.
502   @return the list of object names
503  */
504 QStringList HYDROGUI_PriorityTableModel::getAvailableObjectNames() const
505 {
506   QStringList aNames;
507
508   foreach ( const Handle(HYDROData_Entity) anObj, myObjects ) {
509     if ( !anObj.IsNull() ) {
510       aNames << anObj->GetName();
511     }
512   }
513
514   return aNames;
515 }
516
517 /**
518   Set number of columns in the table.
519   @param theColumnCount the number of columns
520  */
521 void HYDROGUI_PriorityTableModel::setColumnCount( int theColumnCount )
522 {
523   myColumnCount = theColumnCount;
524 }
525
526 /**
527  Undo last change in the list of priority rules.
528  */
529 void HYDROGUI_PriorityTableModel::undoLastChange()
530 {
531   beginResetModel();
532   myRules = myPrevRules;
533   endResetModel();
534   reset();
535 }
536
537 /**
538   Get type of objects for which rules are defined assuming,
539   that all objects in the list have the same type.
540   @return the type of objects
541  */
542 const ObjectKind HYDROGUI_PriorityTableModel::getObjectsKind() const
543 {
544   if ( myObjects.isEmpty() )
545     return KIND_UNKNOWN;
546   
547   return myObjects.at( 0 )->GetKind();
548 }