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