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