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       myObjects.move( theItem, aDestinationIndex );
376       endMoveRows();
377       aRes = true;
378     }
379   }
380         
381   return aRes;
382 }
383
384 /**
385   Move the items.
386   @param theItems the list of item ids to move
387   @param theType the move operation type
388   @param theIsVisibleOnly indicates if do move relating to the visible objects only
389   @param theDropItem the drop item id ( used for drag and drop obly )
390   @return true in case of success
391 */
392 bool HYDROGUI_ListModel::move( const QList<int>& theItems, const OpType theType, 
393                                bool theIsVisibleOnly, const int theDropItem )
394 {
395   bool aRes = true;
396
397   QListIterator<int> anIt( theItems );
398   int aDragShift = 0;
399
400   switch ( theType ) {
401     case Top:
402     case Down:
403       // reverse order
404       anIt.toBack();
405       while ( anIt.hasPrevious() ) {
406         int anId = anIt.previous();
407         if ( theType == Top ) {
408           anId += aDragShift;
409           aDragShift++;
410         }
411         if ( !move( anId, theType, theIsVisibleOnly, theDropItem ) ) {
412           aRes = false;
413           break;
414         }
415       }
416       break;
417     case Bottom:
418     case Up:
419       // direct order
420       while ( anIt.hasNext() ) {
421         int anId = anIt.next();
422         if ( theType == Bottom ) {
423           anId -= aDragShift;
424           aDragShift++;
425         }
426         if ( !move( anId, theType, theIsVisibleOnly, theDropItem ) ) {
427           aRes = false;
428           break;
429         }
430       }
431       break;
432     case DragAndDrop:
433       // direct order
434       aRes = isDragAndDropAllowed( theItems, theDropItem );
435       if ( aRes ) {
436         int aDropShift = 0;
437         int aDropItem = theDropItem;
438         while ( anIt.hasNext() ) {
439           int anId = anIt.next();
440           aDropItem = theDropItem + aDropShift;
441           if ( anId > aDropItem ) {
442             aDragShift = 0;
443             aDropShift++;
444           } else {
445             anId -= aDragShift;
446             if ( ( aDropItem - anId ) != 1 ) {
447               aDragShift++;
448             }
449           }
450           move( anId, theType, theIsVisibleOnly, aDropItem );
451         }
452       }
453       break;
454     default:
455       aRes = false;
456   }
457   
458   return aRes;
459 }
460
461 /**
462   Check if drag and drop operation allowed.
463   @param theItems the list of dragged item ids
464   @param theDropItem the drop item id
465   @return true if drag and drop allowed
466 */
467 bool HYDROGUI_ListModel::isDragAndDropAllowed( const QList<int>& theItems, 
468                                                const int theDropItem ) const
469 {
470   bool isAllowed = false;
471
472   if ( theDropItem >= 0 && 
473        // TODO: to disable drop between items use: theDropItem < myObjects.count()
474        theDropItem <= myObjects.count() &&
475        !theItems.empty() && theItems.count() < myObjects.count() &&
476        !theItems.contains( theDropItem )) {
477     isAllowed = true;
478   }
479
480   return isAllowed;
481 }
482
483 /**
484   Enable/disable decoration (eye icon).
485   @param theIsToEnable if true - the decoration will be enabled
486 */
487 void HYDROGUI_ListModel::setDecorationEnabled( const bool theIsToEnable )
488 {
489   myIsDecorationEnabled = theIsToEnable;
490 }