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