Salome HOME
Initial merge of branch 'BR_HYDRO_IMPS_2016' into BR_PORTING_OCCT_7
[modules/hydro.git] / src / HYDROGUI / HYDROGUI_ListModel.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_ListModel.h"
20
21 #include "HYDROGUI_DataObject.h"
22
23 #include <SUIT_Session.h>
24 #include <SUIT_ResourceMgr.h>
25
26 #include <QMimeData>
27
28 const QString OBJ_LIST_MIME_TYPE = "application/hydro.objects.list";
29
30
31 /**
32   Constructor.
33   @param theParent the parent object
34 */
35 HYDROGUI_ListModel::HYDROGUI_ListModel( QObject* theParent )
36  : QAbstractListModel( theParent ), myIsDecorationEnabled( true )
37 {
38   // Get resource manager
39   SUIT_ResourceMgr* aResMgr = 0;
40   SUIT_Session* aSession = SUIT_Session::session();
41   if ( aSession ) {
42     aResMgr = SUIT_Session::session()->resourceMgr();
43   }
44
45   // Define eye icon and empty icon
46   myEmpty = QPixmap( 16, 16 );
47   myEmpty.fill( Qt::transparent );
48   if ( aResMgr ) {
49     myEye = aResMgr->loadPixmap( "HYDRO", tr( "EYE_ICO" ) );
50   } else {
51     myEye = QPixmap( 16, 16 );
52     myEye.fill( Qt::black );
53   }
54 }
55
56 /**
57   Destructor.
58 */
59 HYDROGUI_ListModel::~HYDROGUI_ListModel()
60 {
61 }
62
63 void HYDROGUI_ListModel::setBackgroundColor(int theInd, QColor theColor)
64 {
65   myColoredRow[theInd] = theColor;
66 }
67
68 void HYDROGUI_ListModel::clearAllBackgroundColors()
69 {
70   myColoredRow.clear();
71 }
72
73 QColor HYDROGUI_ListModel::getBackgroundColor(int theInd) const
74 {
75   if (myColoredRow.count( theInd ))
76     return myColoredRow[theInd];
77   else
78     return QColor();
79 }
80
81 /**
82 */
83 QVariant HYDROGUI_ListModel::data( const QModelIndex &theIndex, int theRole ) const
84 {
85   QVariant aVariant;
86
87   int aRow = theIndex.row();
88   int aColumn = theIndex.column();
89
90   switch( theRole )
91   {
92   case Qt::DisplayRole:
93     {
94       if( aColumn==0 && aRow >=0 && aRow < myObjects.count() )
95         return myObjects.at( aRow ).first->GetName();
96       else
97         return QVariant();
98     }
99     break;
100   case Qt::BackgroundRole:
101     {
102       if( aColumn==0 && aRow >=0 && aRow < myObjects.count() && myColoredRow.contains(aRow))
103       {
104         QBrush aBackgr(myColoredRow[aRow]);
105         return aBackgr;
106       }
107       else
108         return QVariant();
109     }
110
111   case Qt::DecorationRole:
112     {
113       if( myIsDecorationEnabled && 
114           aColumn==0 && aRow >=0 && aRow < myObjects.count() ) {
115         bool isVisible = isObjectVisible( aRow );
116         if( isVisible )
117           return myEye;
118         else
119           return myEmpty;
120       }
121       return QVariant();
122     }
123     break;
124
125   case HYDROGUI_VisibleRole:
126     {
127       bool isVisible = isObjectVisible( aRow );
128       return QVariant( isVisible ).toString();
129     }
130     break;
131   case HYDROGUI_EntryRole:
132     {
133       if( aColumn==0 && aRow >=0 && aRow < myObjects.count() ) {
134         aVariant = HYDROGUI_DataObject::dataObjectEntry( myObjects.at( aRow ).first );
135       }
136     }
137     break;
138   }
139
140   return aVariant;
141 }
142
143 /**
144 */
145 int HYDROGUI_ListModel::rowCount( const QModelIndex &theParent ) const
146 {
147   return myObjects.count();
148 }
149
150 /**
151   Set objects list.
152   @param theObjects the list of pairs (object; object visibility)
153 */
154 void HYDROGUI_ListModel::setObjects( const Object2VisibleList& theObjects )
155 {
156   beginResetModel();
157   myObjects = theObjects;
158   endResetModel();
159 }
160
161 /**
162   Get objects list.
163   @return the list of objects ordered according to the model
164 */
165 HYDROGUI_ListModel::ObjectList HYDROGUI_ListModel::getObjects() const
166 {
167   ObjectList anObjects;
168
169   foreach( const Object2Visible& anItem, myObjects ) {
170     anObjects << anItem.first;
171   }
172
173   return anObjects;
174 }
175
176 /**
177   Add the object to the end of the list.
178   @param theObjects the pair (object; visibility)
179 */
180 void HYDROGUI_ListModel::addObject( const Object2Visible& theObject )
181 {
182   beginResetModel();
183   myObjects << theObject;
184   endResetModel();
185 }
186
187 /**
188   Remove the object from the list.
189   @param theObjectName the name of the object to remove
190 */
191 void HYDROGUI_ListModel::removeObjectByName( const QString& theObjectName )
192 {
193   Object2Visible anItem;
194   foreach( anItem, myObjects ) {
195     if ( anItem.first->GetName() == theObjectName ) {
196       break;
197     }
198   }
199
200   beginResetModel();
201   myObjects.removeAll(anItem);
202   endResetModel();
203 }
204
205
206 /**
207   Check if the object is visible.
208   @param theIndex the object index
209   @return true if the object is visible, false - otherwise
210 */
211 bool HYDROGUI_ListModel::isObjectVisible( int theIndex ) const
212 {
213   bool isVisible = false;
214
215   if ( theIndex >= 0 && theIndex < myObjects.count() ) {
216     isVisible = myObjects.at( theIndex ).second;
217   }
218
219   return isVisible;
220 }
221
222 /**
223 */
224 QVariant HYDROGUI_ListModel::headerData( int theSection,
225                                          Qt::Orientation theOrientation,
226                                          int theRole ) const
227 {
228   if( theOrientation==Qt::Horizontal && theRole==Qt::DisplayRole )
229   {
230     switch( theSection )
231     {
232     case 0:
233       return tr( "VISIBLE" );
234     case 1:
235       return tr( "OBJECT_NAME" );
236     };
237   }
238   return QVariant();
239 }
240
241 /**
242 */
243 Qt::ItemFlags HYDROGUI_ListModel::flags( const QModelIndex& theIndex ) const
244 {
245   Qt::ItemFlags aDefaultFlags = QAbstractListModel::flags( theIndex );
246   if( theIndex.isValid() )
247     return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | aDefaultFlags;
248   else
249     return Qt::ItemIsDropEnabled | aDefaultFlags;
250 }
251
252 /**
253 */
254 QMimeData* HYDROGUI_ListModel::mimeData( const QModelIndexList& theIndexes ) const
255 {
256   QMimeData* aMimeData = new QMimeData();
257   QByteArray anEncodedData;
258   QDataStream aStream( &anEncodedData, QIODevice::WriteOnly );
259
260   QList<int> anIdsList = getIds( theIndexes, true );
261   foreach( int anId, anIdsList )
262     aStream << anId;
263
264   aMimeData->setData( OBJ_LIST_MIME_TYPE, anEncodedData );
265   return aMimeData;
266 }
267
268 /**
269 */
270 QStringList HYDROGUI_ListModel::mimeTypes() const
271 {
272   QStringList aTypes;
273   aTypes << OBJ_LIST_MIME_TYPE;
274   return aTypes;
275 }
276
277 /**
278 */
279 bool HYDROGUI_ListModel::dropMimeData( const QMimeData* theData, Qt::DropAction theAction,
280                                        int theRow, int theColumn, const QModelIndex& theParent )
281 {
282   if( theAction == Qt::IgnoreAction)
283     return true;
284
285   if( !theData->hasFormat( OBJ_LIST_MIME_TYPE ))
286     return false;
287
288   if( theColumn > 0 )
289     return false;
290
291   // TODO: to disable drop between items use: int aDropItemId = theParent.row();
292   int aDropItemId = theParent.isValid() ? theParent.row() : theRow;
293
294   QByteArray anEncodedData = theData->data( OBJ_LIST_MIME_TYPE );
295   QDataStream aStream( &anEncodedData, QIODevice::ReadOnly );
296   QList<int> anIdsList;
297   while( !aStream.atEnd() )
298   {
299     int anId;
300     aStream >> anId;
301     anIdsList << anId;
302   }
303
304   move( anIdsList, DragAndDrop, false, aDropItemId ); //TODO set visibility?
305   return true;
306 }
307
308 /**
309 */
310 Qt::DropActions HYDROGUI_ListModel::supportedDropActions() const
311 {
312   return Qt::MoveAction | Qt::CopyAction;
313 }
314
315 /**
316 */
317 Qt::DropActions HYDROGUI_ListModel::supportedDragActions() const
318 {
319   // Set the supported drag actions for the items in the model
320   return Qt::MoveAction | Qt::CopyAction;
321 }
322
323 /**
324   Get list of ids by the list model indexes.
325   @param theIsToSort defines if the list of ids should be sorted in ascending order
326   @return the list of ids
327 */
328 QList<int> HYDROGUI_ListModel::getIds( const QModelIndexList& theIndexes, 
329                                        bool theIsToSort ) const
330 {
331   QList<int> anIds;
332   foreach( const QModelIndex& anIndex, theIndexes ) {
333     anIds << anIndex.row();
334   }
335
336   if ( theIsToSort ) {
337     qSort( anIds );
338   }
339
340   return anIds;
341 }
342
343 /**
344   Move the item.
345   @param theItem the item id to move
346   @param theType the move operation type
347   @param theIsVisibleOnly indicates if do move relating to the visible objects only
348   @param theDropItem the drop item id ( used for drag and drop obly )
349   @return true in case of success
350 */
351 bool HYDROGUI_ListModel::move( const int theItem, const OpType theType,
352                                bool theIsVisibleOnly, const int theDropItem )
353 {
354   bool aRes = false;
355   if ( theItem < 0 || theItem >= myObjects.count() ) {
356     return aRes;
357   }
358
359   int aDestinationIndex = -1;
360   bool isInsertBefore = false;
361
362   switch ( theType ) {
363     case Up:
364       isInsertBefore = true;
365       if ( theItem > 0 ) {
366         aDestinationIndex = theItem - 1;
367         if ( theIsVisibleOnly ) {
368           while ( aDestinationIndex >= 0 && !isObjectVisible( aDestinationIndex ) ) {
369             aDestinationIndex--;
370           }
371         }
372       }
373       break;
374     case Down:
375       if ( theItem < myObjects.count() - 1 ) {
376         aDestinationIndex = theItem + 1;
377         if ( theIsVisibleOnly ) {
378           while ( aDestinationIndex < myObjects.count() && !isObjectVisible( aDestinationIndex ) ) {
379             aDestinationIndex++;
380           }
381         }
382       }
383       break;
384     case Top:
385       isInsertBefore = true;
386       if ( theItem > 0 ) {
387         aDestinationIndex = 0;
388       }
389       break;
390     case Bottom:
391       if ( theItem < myObjects.count() - 1 ) {
392         aDestinationIndex = myObjects.count() - 1;
393       }
394       break;
395     case DragAndDrop:
396       if ( theItem > theDropItem ) {
397         isInsertBefore = true;
398         aDestinationIndex = theDropItem;
399       } else {
400         aDestinationIndex = theDropItem - 1;
401       }
402       break;
403   }
404
405   if ( aDestinationIndex >= 0 && aDestinationIndex < myObjects.count() ) {
406     int aDestinationRow = isInsertBefore ? aDestinationIndex : aDestinationIndex + 1;
407     if ( beginMoveRows( QModelIndex(), theItem, theItem, QModelIndex(), aDestinationRow ) ) {
408       myPrevObjects = myObjects;
409       myObjects.move( theItem, aDestinationIndex );
410       endMoveRows();
411       aRes = true;
412     }
413   }
414         
415   return aRes;
416 }
417
418 /**
419   Move the items.
420   @param theItems the list of item ids to move
421   @param theType the move operation type
422   @param theIsVisibleOnly indicates if do move relating to the visible objects only
423   @param theDropItem the drop item id ( used for drag and drop obly )
424   @return true in case of success
425 */
426 bool HYDROGUI_ListModel::move( const QList<int>& theItems, const OpType theType, 
427                                bool theIsVisibleOnly, const int theDropItem )
428 {
429   bool aRes = true;
430
431   QListIterator<int> anIt( theItems );
432   int aDragShift = 0;
433
434   switch ( theType ) {
435     case Top:
436     case Down:
437       // reverse order
438       anIt.toBack();
439       while ( anIt.hasPrevious() ) {
440         int anId = anIt.previous();
441         if ( theType == Top ) {
442           anId += aDragShift;
443           aDragShift++;
444         }
445         if ( !move( anId, theType, theIsVisibleOnly, theDropItem ) ) {
446           aRes = false;
447           break;
448         }
449       }
450       break;
451     case Bottom:
452     case Up:
453       // direct order
454       while ( anIt.hasNext() ) {
455         int anId = anIt.next();
456         if ( theType == Bottom ) {
457           anId -= aDragShift;
458           aDragShift++;
459         }
460         if ( !move( anId, theType, theIsVisibleOnly, theDropItem ) ) {
461           aRes = false;
462           break;
463         }
464       }
465       break;
466     case DragAndDrop:
467       // direct order
468       aRes = isDragAndDropAllowed( theItems, theDropItem );
469       if ( aRes ) {
470         int aDropShift = 0;
471         int aDropItem = theDropItem;
472         while ( anIt.hasNext() ) {
473           int anId = anIt.next();
474           aDropItem = theDropItem + aDropShift;
475           if ( anId > aDropItem ) {
476             aDragShift = 0;
477             aDropShift++;
478           } else {
479             anId -= aDragShift;
480             if ( ( aDropItem - anId ) != 1 ) {
481               aDragShift++;
482             }
483           }
484           move( anId, theType, theIsVisibleOnly, aDropItem );
485         }
486       }
487       break;
488     default:
489       aRes = false;
490   }
491   
492   return aRes;
493 }
494
495 /**
496   Check if drag and drop operation allowed.
497   @param theItems the list of dragged item ids
498   @param theDropItem the drop item id
499   @return true if drag and drop allowed
500 */
501 bool HYDROGUI_ListModel::isDragAndDropAllowed( const QList<int>& theItems, 
502                                                const int theDropItem ) const
503 {
504   bool isAllowed = false;
505
506   if ( theDropItem >= 0 && 
507        // TODO: to disable drop between items use: theDropItem < myObjects.count()
508        theDropItem <= myObjects.count() &&
509        !theItems.empty() && theItems.count() < myObjects.count() &&
510        !theItems.contains( theDropItem )) {
511     isAllowed = true;
512   }
513
514   return isAllowed;
515 }
516
517 /**
518   Enable/disable decoration (eye icon).
519   @param theIsToEnable if true - the decoration will be enabled
520 */
521 void HYDROGUI_ListModel::setDecorationEnabled( const bool theIsToEnable )
522 {
523   myIsDecorationEnabled = theIsToEnable;
524 }
525
526 void HYDROGUI_ListModel::undoLastMove()
527 {
528   beginResetModel();
529   myObjects = myPrevObjects;
530   endResetModel();
531 }