Salome HOME
refs #550: fix crash when myObject is NULL
[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
22 /**
23   Constructor.
24   @param theParent the parent object
25 */
26 HYDROGUI_PriorityTableModel::HYDROGUI_PriorityTableModel( QObject* theParent )
27  : QAbstractTableModel( theParent )
28 {
29 }
30
31 /**
32   Destructor.
33 */
34 HYDROGUI_PriorityTableModel::~HYDROGUI_PriorityTableModel()
35 {
36 }
37
38 /**
39  */
40 Qt::ItemFlags   HYDROGUI_PriorityTableModel::flags( const QModelIndex & theIndex ) const
41 {
42   return Qt::ItemIsSelectable   | Qt::ItemIsEditable | Qt::ItemIsEnabled;
43 }
44
45 /**
46  */
47 QVariant HYDROGUI_PriorityTableModel::data( const QModelIndex &theIndex, int theRole ) const
48 {
49   QVariant aVariant;
50
51   int aRow = theIndex.row();
52   int aColumn = theIndex.column();
53
54   if ( !theIndex.isValid() || aRow > myRules.size() ) {
55     return aVariant;
56   }
57   
58   if ( theRole == Qt::DisplayRole ) {
59     HYDROData_CustomRule aRule = myRules.at(aRow);
60
61     if ( aColumn == 0 ) {
62       aVariant = aRule.Object1->GetName();
63     } else if ( aColumn == 1 ) {
64       aVariant = priorityToString( aRule.Priority );
65     } else if ( aColumn == 2 ) {
66       aVariant = aRule.Object2->GetName();
67     } else if ( aColumn == 3 ) {
68       aVariant = mergeTypeToString( aRule.MergeType );
69     }
70   } else if ( theRole == Qt::EditRole ) {
71     HYDROData_CustomRule aRule = myRules.at(aRow);
72
73     if ( aColumn == 0 ) {
74       aVariant = aRule.Object1->GetName();
75     } else if ( aColumn == 1 ) {
76       aVariant = aRule.Priority;
77     } else if ( aColumn == 2 ) {
78       aVariant = aRule.Object2->GetName();
79     } else if ( aColumn == 3 ) {
80       aVariant = aRule.MergeType;
81     }
82   } else if ( theRole ==  Qt::UserRole ) {
83     if ( aColumn == 0 || aColumn == 2 ) {
84       QStringList aNames;
85       HYDROData_CustomRule aRule = myRules.at( aRow );
86       Handle(HYDROData_Object) aUsedObject = aColumn == 0 ? aRule.Object2 : aRule.Object1;
87       if ( !aUsedObject.IsNull() ) {
88         aNames = getAvailablePairs( aUsedObject );
89       }
90       aVariant = aNames;
91     } else if ( aColumn == 1 ) {
92       QMap<QString, QVariant> aMap;
93       aMap.insert( priorityToString( LESS ), LESS );
94       aMap.insert( priorityToString( GREATER ), GREATER );
95       aVariant = QVariant( aMap );
96     } else if ( aColumn == 3 ) {
97       QMap<QString, QVariant> aMap;
98       aMap.insert( mergeTypeToString( HYDROData_Zone::Merge_Object ), HYDROData_Zone::Merge_Object );
99       aMap.insert( mergeTypeToString( HYDROData_Zone::Merge_ZMIN ), HYDROData_Zone::Merge_ZMIN );
100       aMap.insert( mergeTypeToString( HYDROData_Zone::Merge_ZMAX ), HYDROData_Zone::Merge_ZMAX );
101       aVariant = QVariant( aMap );
102     } 
103   } else if ( theRole == Qt::TextAlignmentRole ) {
104     aVariant = Qt::AlignCenter;
105   }
106
107   return aVariant;
108 }
109
110 /**
111  */
112 bool HYDROGUI_PriorityTableModel::setData( const QModelIndex & theIndex, const QVariant & theValue, int theRole )
113 {
114   int aRow = theIndex.row();
115   if ( !theIndex.isValid() || aRow > myRules.size() ) {
116     return false;
117   }
118
119   bool aRes = false;
120
121   if ( theRole ==  Qt::EditRole ) {
122     int aColumn = theIndex.column();
123
124     if ( aColumn == 0 || aColumn == 2 ) {
125       Handle(HYDROData_Object) anObject;
126       QString anObjName = theValue.toString();
127       foreach ( const Handle(HYDROData_Object) anObj, myObjects ) {
128         if ( anObj->GetName() == anObjName ) {
129           anObject = anObj;
130           break;
131         }
132       }
133       if ( !anObject.IsNull() ) {
134         HYDROData_CustomRule anEditedRule = myRules[aRow];
135         
136         if ( aColumn == 0 ) {
137           anEditedRule.Object1 = anObject;
138         } else {
139           anEditedRule.Object2 = anObject;
140         }
141         
142         if ( !isUsed( anEditedRule.Object1, anEditedRule.Object2 ) ) {
143           myRules[aRow] = anEditedRule;
144           aRes = true;
145         } else {
146           emit showError( tr("ALREADY_EXISTS") );
147         }
148       }
149     } else if ( aColumn == 1 ) {
150       myRules[aRow].Priority = (HYDROData_PriorityType)theValue.toInt();
151     } else if ( aColumn == 3 ) {
152       myRules[aRow].MergeType = (HYDROData_Zone::MergeAltitudesType)theValue.toInt();;
153     }
154   }
155
156   return aRes;
157 }
158
159 /**
160  */
161 int HYDROGUI_PriorityTableModel::rowCount( const QModelIndex &theParent ) const
162 {
163   return myRules.count();
164 }
165
166 /**
167  */
168 int HYDROGUI_PriorityTableModel::columnCount( const QModelIndex &theParent ) const
169 {
170   return 4;
171 }
172
173 /**
174   Set rules.
175   @param theRules the list of rules
176 */
177 void HYDROGUI_PriorityTableModel::setRules( const HYDROData_ListOfRules& theRules )
178 {
179   beginResetModel();
180   myRules = theRules;
181   endResetModel();
182 }
183
184 /**
185   Get rules.
186   @return the list of rules
187 */
188 HYDROData_ListOfRules HYDROGUI_PriorityTableModel::getRules() const
189 {
190   return myRules;
191 }
192
193 /**
194 */
195 QVariant HYDROGUI_PriorityTableModel::headerData( int theSection,
196                                                   Qt::Orientation theOrientation,
197                                                   int theRole ) const
198 {
199   QVariant aData;
200
201   if ( theRole != Qt::DisplayRole ) {
202     return aData;
203   }
204
205   if ( theOrientation == Qt::Horizontal ) {
206     switch( theSection )
207     {
208     case 0:
209       aData = tr( "OBJECT1" );
210       break;
211     case 1:
212       aData = tr( "PRIORITY" );
213       break;
214     case 2:
215       aData = tr( "OBJECT2" );
216       break;
217     case 3:
218       aData = tr( "BATHYMETRY" );
219       break;
220     };
221   } else if ( theOrientation == Qt::Vertical ) {
222     aData = theSection + 1;
223   }
224
225   return aData;
226 }
227
228 /**
229   Set objects which could be used for rules definition.
230   @param theObjects the ordered list of objects
231  */
232 void HYDROGUI_PriorityTableModel::setObjects( const QList<Handle(HYDROData_Object)>& theObjects )
233 {
234   myObjects = theObjects;
235     
236   beginResetModel();
237
238   // Remove rules which use objects which are no longer available
239   QStringList aNames = getAvailableObjectNames();
240   QMutableListIterator<HYDROData_CustomRule> anIter( myRules );
241   while ( anIter.hasNext() ) {
242     HYDROData_CustomRule aRule = anIter.next();
243      if ( !aNames.contains( aRule.Object1->GetName() ) || 
244           !aNames.contains( aRule.Object2->GetName() ) ) {
245       anIter.remove();
246     }
247   }
248
249   endResetModel();
250 }
251
252 /**
253  Create new rule.
254  @return true if a rule has been created successfully
255  */
256 bool HYDROGUI_PriorityTableModel::createNewRule() 
257 {
258   if ( !canCreateNewRule() ) {
259     return false;
260   }
261   
262   // Existing pairs of object indexes
263   QStringList aNames = getAvailableObjectNames();
264   QList< QPair<int, int> > aRules;
265   foreach( const HYDROData_CustomRule& aRule, getRules() ) {
266     int anIndex1 = aNames.indexOf( aRule.Object1->GetName() );
267     int anIndex2 = aNames.indexOf( aRule.Object2->GetName() );
268     if ( anIndex1 >= 0 && anIndex2 >= 0 ) {
269       aRules << QPair<int, int>( anIndex1, anIndex2 );
270       aRules << QPair<int, int>( anIndex2, anIndex1 );
271     }
272   }
273     
274   // Try to find:
275   // 1. the new pair of objects 
276   // 2. the priority type corresponding to the objects order
277   Handle(HYDROData_Object) anObject1, anObject2;
278   HYDROData_PriorityType aPriorityType = GREATER;
279
280   int aNbObjects = myObjects.count();
281   for ( int anIndex1 = 0; anIndex1 < aNbObjects; anIndex1++ ) {
282     bool isFound = false;
283
284     for ( int anIndex2 = 0; anIndex2 < aNbObjects; anIndex2++ ) {
285       if ( anIndex1 == anIndex2 ) {
286         continue;
287       }
288
289       if ( !aRules.contains( QPair<int, int>( anIndex1, anIndex2 ) ) ) {
290         anObject1 = myObjects.at( anIndex1 );
291         anObject2 = myObjects.at( anIndex2 );
292         if ( anIndex1 > anIndex2 ) {
293           aPriorityType = LESS;
294         }
295         isFound = true;
296         break;
297       }
298     }
299
300     if ( isFound ) {
301       break;
302     }
303   }
304
305   // Create a new rule
306   bool isCreated = false;
307
308   if ( !anObject1.IsNull() && !anObject2.IsNull() ) {
309     HYDROData_CustomRule aNewRule;
310     aNewRule.Object1 = anObject1;
311     aNewRule.Object2 = anObject2;
312     aNewRule.Priority = aPriorityType;
313     aNewRule.MergeType = HYDROData_Zone::Merge_ZMIN;
314
315     beginResetModel();
316     myRules << aNewRule;
317     endResetModel();
318
319     isCreated = true;
320   }
321
322   return isCreated;
323 }
324
325 /**
326  Check if a new rule can be created.
327  @return true if a new rule could be created
328  */
329 bool HYDROGUI_PriorityTableModel::canCreateNewRule() const
330 {
331   int aNbObjects = myObjects.count();
332
333   return ( myRules.count() < (aNbObjects - 1)*aNbObjects/2 );
334 }
335
336 /**
337  */
338 bool HYDROGUI_PriorityTableModel::removeRows ( int theRow, int theCount, const QModelIndex & theParent )
339 {
340   if ( myRules.isEmpty() || theRow < 0 || theRow >= myRules.count() ) {
341     return false;
342   }
343
344   int aLastRow = theRow + theCount - 1;
345   if ( aLastRow > myRules.count() ) {
346     aLastRow = myRules.count() - 1;
347   }
348
349   beginRemoveRows( theParent, theRow, aLastRow );
350
351   // Remove the corresponding rules
352   for ( int i = 1; i <= theCount; i++ ) {
353     myRules.removeAt( theRow );
354   }
355
356   endRemoveRows();
357
358   return true;
359 }
360
361 /**
362  Remove all data from the model.
363  @return true if at least one row is removed
364  */
365 bool HYDROGUI_PriorityTableModel::removeAll()
366 {
367   return removeRows( 0, rowCount() );
368 }
369
370 /**
371  Get available pairs for the given object.
372  @param theObject the object
373  @return the list of object names
374  */
375 QStringList HYDROGUI_PriorityTableModel::getAvailablePairs(  const Handle(HYDROData_Object)& theObject ) const
376 {
377   QStringList aNames;
378
379   if ( !theObject.IsNull() ) {
380     aNames = getAvailableObjectNames();
381     aNames.removeAll( theObject->GetName() );
382   }
383
384   return aNames;
385 }
386
387 /**
388  Remove the rows.
389  @params theRows the list of rows to remove
390  @return true if at least one row is removed
391  */
392 bool HYDROGUI_PriorityTableModel::removeRows ( const QList<int> theRows )
393 {
394   QList<int> aSortedRows = theRows;
395   qSort( aSortedRows );
396
397   int aRowToRemove = -1;
398   int aNbRemoved = 0;
399   foreach ( int aRow, aSortedRows ) {
400     aRowToRemove = aRow - aNbRemoved;
401     if ( removeRow( aRowToRemove ) ) {
402       aNbRemoved++;
403     }
404   }
405
406   return ( aNbRemoved > 0 );
407 }
408
409 /**
410  Get priority string representation.
411  @param thePriority the priority
412  */
413 QString HYDROGUI_PriorityTableModel::priorityToString( const int thePriority ) const
414 {
415   switch( thePriority )
416   {
417   case LESS:
418     return tr( "LESS" );
419   case GREATER:
420     return tr( "GREATER" );
421   default:
422     return QString();
423   };
424 }
425
426 /**
427  Get merge type string representation.
428  @param theMergeType the merge type
429  */
430 QString HYDROGUI_PriorityTableModel::mergeTypeToString( const int theMergeType ) const
431 {
432   switch( theMergeType )
433   {
434   case HYDROData_Zone::Merge_Object:
435     return tr( "PRIORITY" );
436   case HYDROData_Zone::Merge_ZMIN:
437     return tr( "ZMIN" );
438   case HYDROData_Zone::Merge_ZMAX:
439     return tr( "ZMAX" );
440   default:
441     return QString();
442   };
443 }
444
445 /**
446   Check if the given pair of objects is already used.
447   @return true if the pair is used
448  */
449 bool HYDROGUI_PriorityTableModel::isUsed( const Handle(HYDROData_Object)& theObj1, 
450                                           const Handle(HYDROData_Object)& theObj2 ) const
451 {
452   bool isUsed = false;
453
454   foreach ( const HYDROData_CustomRule& aRule, myRules ) {
455     if ( ( aRule.Object1->GetName() == theObj1->GetName() && 
456            aRule.Object2->GetName() == theObj2->GetName() ) ||
457          ( aRule.Object1->GetName() == theObj2->GetName() && 
458            aRule.Object2->GetName() == theObj1->GetName() ) ) {
459       isUsed = true;
460       break;
461     }
462   }       
463
464   return isUsed;
465 }
466
467 /**
468   Get available object names.
469   @return the list of object names
470  */
471 QStringList HYDROGUI_PriorityTableModel::getAvailableObjectNames() const
472 {
473   QStringList aNames;
474
475   foreach ( const Handle(HYDROData_Object) anObj, myObjects ) {
476     if ( !anObj.IsNull() ) {
477       aNames << anObj->GetName();
478     }
479   }
480
481   return aNames;
482 }