QGIS API Documentation  3.2.0-Bonn (bc43194)
qgslayoutmodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutmodel.cpp
3  ------------------
4  begin : October 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgslayoutmodel.h"
19 #include "qgslayout.h"
20 #include "qgsapplication.h"
21 #include "qgslogger.h"
22 #include <QApplication>
23 #include <QGraphicsItem>
24 #include <QDomDocument>
25 #include <QDomElement>
26 #include <QMimeData>
27 #include <QSettings>
28 #include <QMessageBox>
29 #include <QIcon>
30 
31 QgsLayoutModel::QgsLayoutModel( QgsLayout *layout, QObject *parent )
32  : QAbstractItemModel( parent )
33  , mLayout( layout )
34 {
35 
36 }
37 
39 {
40  //try to return the QgsLayoutItem corresponding to a QModelIndex
41  if ( !index.isValid() )
42  {
43  return nullptr;
44  }
45 
46  QgsLayoutItem *item = static_cast<QgsLayoutItem *>( index.internalPointer() );
47  return item;
48 }
49 
50 QModelIndex QgsLayoutModel::index( int row, int column,
51  const QModelIndex &parent ) const
52 {
53  if ( column < 0 || column >= columnCount() )
54  {
55  //column out of bounds
56  return QModelIndex();
57  }
58 
59  if ( !parent.isValid() && row >= 0 && row < mItemsInScene.size() )
60  {
61  //return an index for the layout item at this position
62  return createIndex( row, column, mItemsInScene.at( row ) );
63  }
64 
65  //only top level supported for now
66  return QModelIndex();
67 }
68 
69 void QgsLayoutModel::refreshItemsInScene()
70 {
71  mItemsInScene.clear();
72 
73  const QList< QGraphicsItem * > items = mLayout->items();
74  //filter paper items from list
75  //TODO - correctly handle grouped item z order placement
76  for ( QgsLayoutItem *item : qgis::as_const( mItemZList ) )
77  {
78  if ( item->type() != QgsLayoutItemRegistry::LayoutPage && items.contains( item ) )
79  {
80  mItemsInScene.push_back( item );
81  }
82  }
83 }
84 
85 QModelIndex QgsLayoutModel::parent( const QModelIndex &index ) const
86 {
87  Q_UNUSED( index );
88 
89  //all items are top level for now
90  return QModelIndex();
91 }
92 
93 int QgsLayoutModel::rowCount( const QModelIndex &parent ) const
94 {
95  if ( !parent.isValid() )
96  {
97  return mItemsInScene.size();
98  }
99 
100  QGraphicsItem *parentItem = itemFromIndex( parent );
101 
102  if ( !parentItem )
103  {
104  return mItemsInScene.size();
105  }
106  else
107  {
108  //no children for now
109  return 0;
110  }
111 }
112 
113 int QgsLayoutModel::columnCount( const QModelIndex &parent ) const
114 {
115  Q_UNUSED( parent );
116  return 3;
117 }
118 
119 QVariant QgsLayoutModel::data( const QModelIndex &index, int role ) const
120 {
121  if ( !index.isValid() )
122  return QVariant();
123 
124  QgsLayoutItem *item = itemFromIndex( index );
125  if ( !item )
126  {
127  return QVariant();
128  }
129 
130  switch ( role )
131  {
132  case Qt::DisplayRole:
133  if ( index.column() == ItemId )
134  {
135  return item->displayName();
136  }
137  else
138  {
139  return QVariant();
140  }
141 
142  case Qt::DecorationRole:
143  if ( index.column() == ItemId )
144  {
145  return item->icon();
146  }
147  else
148  {
149  return QVariant();
150  }
151 
152  case Qt::EditRole:
153  if ( index.column() == ItemId )
154  {
155  return item->id();
156  }
157  else
158  {
159  return QVariant();
160  }
161 
162  case Qt::UserRole:
163  //store item uuid in userrole so we can later get the QModelIndex for a specific item
164  return item->uuid();
165  case Qt::UserRole+1:
166  //user role stores reference in column object
167  return qVariantFromValue( qobject_cast<QObject *>( item ) );
168 
169  case Qt::TextAlignmentRole:
170  return Qt::AlignLeft & Qt::AlignVCenter;
171 
172  case Qt::CheckStateRole:
173  switch ( index.column() )
174  {
175  case Visibility:
176  //column 0 is visibility of item
177  return item->isVisible() ? Qt::Checked : Qt::Unchecked;
178  case LockStatus:
179  //column 1 is locked state of item
180  return item->isLocked() ? Qt::Checked : Qt::Unchecked;
181  default:
182  return QVariant();
183  }
184 
185  case Qt::FontRole:
186  if ( index.column() == ItemId && item->isSelected() )
187  {
188  //draw name of selected items in bold
189  QFont boldFont;
190  boldFont.setBold( true );
191  return boldFont;
192  }
193  return QVariant();
194 
195  default:
196  return QVariant();
197  }
198 }
199 
200 bool QgsLayoutModel::setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole )
201 {
202  Q_UNUSED( role );
203 
204  if ( !index.isValid() )
205  return false;
206 
207  QgsLayoutItem *item = itemFromIndex( index );
208  if ( !item )
209  {
210  return false;
211  }
212 
213  switch ( index.column() )
214  {
215  case Visibility:
216  //first column is item visibility
217  item->setVisibility( value.toBool() );
218  return true;
219 
220  case LockStatus:
221  //second column is item lock state
222  item->setLocked( value.toBool() );
223  return true;
224 
225  case ItemId:
226  //last column is item id
227  item->setId( value.toString() );
228  return true;
229  }
230 
231  return false;
232 }
233 
234 QVariant QgsLayoutModel::headerData( int section, Qt::Orientation orientation, int role ) const
235 {
236  switch ( role )
237  {
238  case Qt::DisplayRole:
239  {
240  if ( section == ItemId )
241  {
242  return tr( "Item" );
243  }
244  return QVariant();
245  }
246 
247  case Qt::DecorationRole:
248  {
249  if ( section == Visibility )
250  {
251  return qVariantFromValue( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowAllLayersGray.svg" ) ) );
252  }
253  else if ( section == LockStatus )
254  {
255  return qVariantFromValue( QgsApplication::getThemeIcon( QStringLiteral( "/lockedGray.svg" ) ) );
256  }
257 
258  return QVariant();
259  }
260 
261  case Qt::TextAlignmentRole:
262  return Qt::AlignLeft & Qt::AlignVCenter;
263 
264  default:
265  return QAbstractItemModel::headerData( section, orientation, role );
266  }
267 
268 }
269 
271 {
272  return Qt::MoveAction;
273 }
274 
275 QStringList QgsLayoutModel::mimeTypes() const
276 {
277  QStringList types;
278  types << QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" );
279  return types;
280 }
281 
282 QMimeData *QgsLayoutModel::mimeData( const QModelIndexList &indexes ) const
283 {
284  QMimeData *mimeData = new QMimeData();
285  QByteArray encodedData;
286 
287  QDataStream stream( &encodedData, QIODevice::WriteOnly );
288 
289  for ( const QModelIndex &index : indexes )
290  {
291  if ( index.isValid() && index.column() == ItemId )
292  {
293  QgsLayoutItem *item = itemFromIndex( index );
294  if ( !item )
295  {
296  continue;
297  }
298  QString text = item->uuid();
299  stream << text;
300  }
301  }
302 
303  mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ), encodedData );
304  return mimeData;
305 }
306 
308 {
309  return item1->zValue() > item2->zValue();
310 }
311 
312 bool QgsLayoutModel::dropMimeData( const QMimeData *data,
313  Qt::DropAction action, int row, int column, const QModelIndex &parent )
314 {
315  if ( column != ItemId )
316  {
317  return false;
318  }
319 
320  if ( action == Qt::IgnoreAction )
321  {
322  return true;
323  }
324 
325  if ( !data->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ) ) )
326  {
327  return false;
328  }
329 
330  if ( parent.isValid() )
331  {
332  return false;
333  }
334 
335  int beginRow = row != -1 ? row : rowCount( QModelIndex() );
336 
337  QByteArray encodedData = data->data( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ) );
338  QDataStream stream( &encodedData, QIODevice::ReadOnly );
339  QList<QgsLayoutItem *> droppedItems;
340  int rows = 0;
341 
342  while ( !stream.atEnd() )
343  {
344  QString text;
345  stream >> text;
346  QgsLayoutItem *item = mLayout->itemByUuid( text );
347  if ( item )
348  {
349  droppedItems << item;
350  ++rows;
351  }
352  }
353 
354  if ( droppedItems.empty() )
355  {
356  //no dropped items
357  return false;
358  }
359 
360  //move dropped items
361 
362  //first sort them by z-order
363  std::sort( droppedItems.begin(), droppedItems.end(), zOrderDescending );
364 
365  //calculate position in z order list to drop items at
366  int destPos = 0;
367  if ( beginRow < rowCount() )
368  {
369  QgsLayoutItem *itemBefore = mItemsInScene.at( beginRow );
370  destPos = mItemZList.indexOf( itemBefore );
371  }
372  else
373  {
374  //place items at end
375  destPos = mItemZList.size();
376  }
377 
378  //calculate position to insert moved rows to
379  int insertPos = destPos;
380  for ( QgsLayoutItem *item : qgis::as_const( droppedItems ) )
381  {
382  int listPos = mItemZList.indexOf( item );
383  if ( listPos == -1 )
384  {
385  //should be impossible
386  continue;
387  }
388 
389  if ( listPos < destPos )
390  {
391  insertPos--;
392  }
393  }
394 
395  //remove rows from list
396  auto itemIt = droppedItems.begin();
397  for ( ; itemIt != droppedItems.end(); ++itemIt )
398  {
399  mItemZList.removeOne( *itemIt );
400  }
401 
402  //insert items
403  itemIt = droppedItems.begin();
404  for ( ; itemIt != droppedItems.end(); ++itemIt )
405  {
406  mItemZList.insert( insertPos, *itemIt );
407  insertPos++;
408  }
409 
410  rebuildSceneItemList();
411 
412  mLayout->updateZValues( true );
413 
414  return true;
415 }
416 
417 bool QgsLayoutModel::removeRows( int row, int count, const QModelIndex &parent )
418 {
419  Q_UNUSED( count );
420  if ( parent.isValid() )
421  {
422  return false;
423  }
424 
425  if ( row >= rowCount() )
426  {
427  return false;
428  }
429 
430  //do nothing - moves are handled by the dropMimeData method
431  return true;
432 }
433 
435 void QgsLayoutModel::clear()
436 {
437  //totally reset model
438  beginResetModel();
439  mItemZList.clear();
440  refreshItemsInScene();
441  endResetModel();
442 }
443 
444 int QgsLayoutModel::zOrderListSize() const
445 {
446  return mItemZList.size();
447 }
448 
449 void QgsLayoutModel::rebuildZList()
450 {
451  QList<QgsLayoutItem *> sortedList;
452  //rebuild the item z order list based on the current zValues of items in the scene
453 
454  //get items in descending zValue order
455  const QList<QGraphicsItem *> itemList = mLayout->items( Qt::DescendingOrder );
456  for ( QGraphicsItem *item : itemList )
457  {
458  if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
459  {
460  if ( layoutItem->type() != QgsLayoutItemRegistry::LayoutPage )
461  {
462  sortedList.append( layoutItem );
463  }
464  }
465  }
466 
467  mItemZList = sortedList;
468  rebuildSceneItemList();
469 }
471 
472 void QgsLayoutModel::rebuildSceneItemList()
473 {
474  //step through the z list and rebuild the items in scene list,
475  //emitting signals as required
476  int row = 0;
477  const QList< QGraphicsItem * > items = mLayout->items();
478  for ( QgsLayoutItem *item : qgis::as_const( mItemZList ) )
479  {
480  if ( item->type() == QgsLayoutItemRegistry::LayoutPage || !items.contains( item ) )
481  {
482  //item not in scene, skip it
483  continue;
484  }
485 
486  int sceneListPos = mItemsInScene.indexOf( item );
487  if ( sceneListPos == row )
488  {
489  //already in list in correct position, nothing to do
490 
491  }
492  else if ( sceneListPos != -1 )
493  {
494  //in list, but in wrong spot
495  beginMoveRows( QModelIndex(), sceneListPos, sceneListPos, QModelIndex(), row );
496  mItemsInScene.removeAt( sceneListPos );
497  mItemsInScene.insert( row, item );
498  endMoveRows();
499  }
500  else
501  {
502  //needs to be inserted into list
503  beginInsertRows( QModelIndex(), row, row );
504  mItemsInScene.insert( row, item );
505  endInsertRows();
506  }
507  row++;
508  }
509 }
511 void QgsLayoutModel::addItemAtTop( QgsLayoutItem *item )
512 {
513  mItemZList.push_front( item );
514  refreshItemsInScene();
515  item->setZValue( mItemZList.size() );
516 }
517 
518 void QgsLayoutModel::removeItem( QgsLayoutItem *item )
519 {
520  if ( !item )
521  {
522  //nothing to do
523  return;
524  }
525 
526  int pos = mItemZList.indexOf( item );
527  if ( pos == -1 )
528  {
529  //item not in z list, nothing to do
530  return;
531  }
532 
533  //need to get QModelIndex of item
534  QModelIndex itemIndex = indexForItem( item );
535  if ( !itemIndex.isValid() )
536  {
537  //removing an item not in the scene (e.g., deleted item)
538  //we need to remove it from the list, but don't need to call
539  //beginRemoveRows or endRemoveRows since the item was not used by the model
540  mItemZList.removeAt( pos );
541  refreshItemsInScene();
542  return;
543  }
544 
545  //remove item from model
546  int row = itemIndex.row();
547  beginRemoveRows( QModelIndex(), row, row );
548  mItemZList.removeAt( pos );
549  refreshItemsInScene();
550  endRemoveRows();
551 }
552 
553 void QgsLayoutModel::setItemRemoved( QgsLayoutItem *item )
554 {
555  if ( !item )
556  {
557  //nothing to do
558  return;
559  }
560 
561  int pos = mItemZList.indexOf( item );
562  if ( pos == -1 )
563  {
564  //item not in z list, nothing to do
565  return;
566  }
567 
568  //need to get QModelIndex of item
569  QModelIndex itemIndex = indexForItem( item );
570  if ( !itemIndex.isValid() )
571  {
572  return;
573  }
574 
575  //removing item
576  int row = itemIndex.row();
577  beginRemoveRows( QModelIndex(), row, row );
578  mLayout->removeItem( item );
579  refreshItemsInScene();
580  endRemoveRows();
581 }
582 
583 #if 0
584 void QgsLayoutModel::setItemRestored( QgsComposerItem *item )
585 {
586  if ( !item )
587  {
588  //nothing to do
589  return;
590  }
591 
592  int pos = mItemZList.indexOf( item );
593  if ( pos == -1 )
594  {
595  //item not in z list, nothing to do
596  return;
597  }
598 
599  item->setIsRemoved( false );
600  rebuildSceneItemList();
601 }
602 #endif
603 
604 void QgsLayoutModel::updateItemDisplayName( QgsLayoutItem *item )
605 {
606  if ( !item )
607  {
608  //nothing to do
609  return;
610  }
611 
612  //need to get QModelIndex of item
613  QModelIndex itemIndex = indexForItem( item, ItemId );
614  if ( !itemIndex.isValid() )
615  {
616  return;
617  }
618 
619  //emit signal for item id change
620  emit dataChanged( itemIndex, itemIndex );
621 }
622 
623 void QgsLayoutModel::updateItemLockStatus( QgsLayoutItem *item )
624 {
625  if ( !item )
626  {
627  //nothing to do
628  return;
629  }
630 
631  //need to get QModelIndex of item
632  QModelIndex itemIndex = indexForItem( item, LockStatus );
633  if ( !itemIndex.isValid() )
634  {
635  return;
636  }
637 
638  //emit signal for item lock status change
639  emit dataChanged( itemIndex, itemIndex );
640 }
641 
642 void QgsLayoutModel::updateItemVisibility( QgsLayoutItem *item )
643 {
644  if ( !item )
645  {
646  //nothing to do
647  return;
648  }
649 
650  //need to get QModelIndex of item
651  QModelIndex itemIndex = indexForItem( item, Visibility );
652  if ( !itemIndex.isValid() )
653  {
654  return;
655  }
656 
657  //emit signal for item visibility change
658  emit dataChanged( itemIndex, itemIndex );
659 }
660 
661 void QgsLayoutModel::updateItemSelectStatus( QgsLayoutItem *item )
662 {
663  if ( !item )
664  {
665  //nothing to do
666  return;
667  }
668 
669  //need to get QModelIndex of item
670  QModelIndex itemIndex = indexForItem( item, ItemId );
671  if ( !itemIndex.isValid() )
672  {
673  return;
674  }
675 
676  //emit signal for item visibility change
677  emit dataChanged( itemIndex, itemIndex );
678 }
679 
680 bool QgsLayoutModel::reorderItemUp( QgsLayoutItem *item )
681 {
682  if ( !item )
683  {
684  return false;
685  }
686 
687  if ( mItemsInScene.at( 0 ) == item )
688  {
689  //item is already topmost item present in scene, nothing to do
690  return false;
691  }
692 
693  //move item in z list
694  QMutableListIterator<QgsLayoutItem *> it( mItemZList );
695  if ( ! it.findNext( item ) )
696  {
697  //can't find item in z list, nothing to do
698  return false;
699  }
700 
701  const QList< QGraphicsItem * > sceneItems = mLayout->items();
702 
703  it.remove();
704  while ( it.hasPrevious() )
705  {
706  //search through item z list to find previous item which is present in the scene
707  it.previous();
708  if ( it.value() && sceneItems.contains( it.value() ) )
709  {
710  break;
711  }
712  }
713  it.insert( item );
714 
715  //also move item in scene items z list and notify of model changes
716  QModelIndex itemIndex = indexForItem( item );
717  if ( !itemIndex.isValid() )
718  {
719  return true;
720  }
721 
722  //move item up in scene list
723  int row = itemIndex.row();
724  beginMoveRows( QModelIndex(), row, row, QModelIndex(), row - 1 );
725  refreshItemsInScene();
726  endMoveRows();
727  return true;
728 }
729 
730 bool QgsLayoutModel::reorderItemDown( QgsLayoutItem *item )
731 {
732  if ( !item )
733  {
734  return false;
735  }
736 
737  if ( mItemsInScene.last() == item )
738  {
739  //item is already lowest item present in scene, nothing to do
740  return false;
741  }
742 
743  //move item in z list
744  QMutableListIterator<QgsLayoutItem *> it( mItemZList );
745  if ( ! it.findNext( item ) )
746  {
747  //can't find item in z list, nothing to do
748  return false;
749  }
750 
751  const QList< QGraphicsItem * > sceneItems = mLayout->items();
752  it.remove();
753  while ( it.hasNext() )
754  {
755  //search through item z list to find next item which is present in the scene
756  //(deleted items still exist in the z list so that they can be restored to their correct stacking order,
757  //but since they are not in the scene they should be ignored here)
758  it.next();
759  if ( it.value() && sceneItems.contains( it.value() ) )
760  {
761  break;
762  }
763  }
764  it.insert( item );
765 
766  //also move item in scene items z list and notify of model changes
767  QModelIndex itemIndex = indexForItem( item );
768  if ( !itemIndex.isValid() )
769  {
770  return true;
771  }
772 
773  //move item down in scene list
774  int row = itemIndex.row();
775  beginMoveRows( QModelIndex(), row, row, QModelIndex(), row + 2 );
776  refreshItemsInScene();
777  endMoveRows();
778  return true;
779 }
780 
781 bool QgsLayoutModel::reorderItemToTop( QgsLayoutItem *item )
782 {
783  if ( !item || !mItemsInScene.contains( item ) )
784  {
785  return false;
786  }
787 
788  if ( mItemsInScene.at( 0 ) == item )
789  {
790  //item is already topmost item present in scene, nothing to do
791  return false;
792  }
793 
794  //move item in z list
795  QMutableListIterator<QgsLayoutItem *> it( mItemZList );
796  if ( it.findNext( item ) )
797  {
798  it.remove();
799  }
800  mItemZList.push_front( item );
801 
802  //also move item in scene items z list and notify of model changes
803  QModelIndex itemIndex = indexForItem( item );
804  if ( !itemIndex.isValid() )
805  {
806  return true;
807  }
808 
809  //move item to top
810  int row = itemIndex.row();
811  beginMoveRows( QModelIndex(), row, row, QModelIndex(), 0 );
812  refreshItemsInScene();
813  endMoveRows();
814  return true;
815 }
816 
817 bool QgsLayoutModel::reorderItemToBottom( QgsLayoutItem *item )
818 {
819  if ( !item || !mItemsInScene.contains( item ) )
820  {
821  return false;
822  }
823 
824  if ( mItemsInScene.last() == item )
825  {
826  //item is already lowest item present in scene, nothing to do
827  return false;
828  }
829 
830  //move item in z list
831  QMutableListIterator<QgsLayoutItem *> it( mItemZList );
832  if ( it.findNext( item ) )
833  {
834  it.remove();
835  }
836  mItemZList.push_back( item );
837 
838  //also move item in scene items z list and notify of model changes
839  QModelIndex itemIndex = indexForItem( item );
840  if ( !itemIndex.isValid() )
841  {
842  return true;
843  }
844 
845  //move item to bottom
846  int row = itemIndex.row();
847  beginMoveRows( QModelIndex(), row, row, QModelIndex(), rowCount() );
848  refreshItemsInScene();
849  endMoveRows();
850  return true;
851 }
852 
853 QgsLayoutItem *QgsLayoutModel::findItemAbove( QgsLayoutItem *item ) const
854 {
855  //search item z list for selected item
856  QListIterator<QgsLayoutItem *> it( mItemZList );
857  it.toBack();
858  if ( it.findPrevious( item ) )
859  {
860  //move position to before selected item
861  while ( it.hasPrevious() )
862  {
863  //now find previous item, since list is sorted from lowest->highest items
864  if ( it.hasPrevious() && !it.peekPrevious()->isGroupMember() )
865  {
866  return it.previous();
867  }
868  it.previous();
869  }
870  }
871  return nullptr;
872 }
873 
874 QgsLayoutItem *QgsLayoutModel::findItemBelow( QgsLayoutItem *item ) const
875 {
876  //search item z list for selected item
877  QListIterator<QgsLayoutItem *> it( mItemZList );
878  if ( it.findNext( item ) )
879  {
880  //return next item (list is sorted from lowest->highest items)
881  while ( it.hasNext() )
882  {
883  if ( !it.peekNext()->isGroupMember() )
884  {
885  return it.next();
886  }
887  it.next();
888  }
889  }
890  return nullptr;
891 }
892 
893 QList<QgsLayoutItem *> &QgsLayoutModel::zOrderList()
894 {
895  return mItemZList;
896 }
897 
899 
900 Qt::ItemFlags QgsLayoutModel::flags( const QModelIndex &index ) const
901 {
902  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
903 
904  if ( ! index.isValid() )
905  {
906  return flags | Qt::ItemIsDropEnabled;
907  }
908 
909  switch ( index.column() )
910  {
911  case Visibility:
912  case LockStatus:
913  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
914  case ItemId:
915  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
916  default:
917  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
918  }
919 }
920 
921 QModelIndex QgsLayoutModel::indexForItem( QgsLayoutItem *item, const int column )
922 {
923  if ( !item )
924  {
925  return QModelIndex();
926  }
927 
928  int row = mItemsInScene.indexOf( item );
929  if ( row == -1 )
930  {
931  //not found
932  return QModelIndex();
933  }
934 
935  return index( row, column );
936 }
937 
939 void QgsLayoutModel::setSelected( const QModelIndex &index )
940 {
941  QgsLayoutItem *item = itemFromIndex( index );
942  if ( !item )
943  {
944  return;
945  }
946 
947  mLayout->setSelectedItem( item );
948 }
950 
951 //
952 // QgsLayoutProxyModel
953 //
954 
956  : QSortFilterProxyModel( parent )
957  , mLayout( layout )
958  , mItemTypeFilter( QgsLayoutItemRegistry::LayoutItem )
959 {
960  if ( mLayout )
961  setSourceModel( mLayout->itemsModel() );
962 
963  // TODO doesn't seem to work correctly - not updated when item changes
964  setDynamicSortFilter( true );
965  setSortLocaleAware( true );
966  sort( QgsLayoutModel::ItemId );
967 }
968 
969 bool QgsLayoutProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
970 {
971  //sort by item id
972  const QgsLayoutItem *item1 = itemFromSourceIndex( left );
973  const QgsLayoutItem *item2 = itemFromSourceIndex( right );
974  if ( !item1 )
975  return false;
976 
977  if ( !item2 )
978  return true;
979 
980  return QString::localeAwareCompare( item1->displayName(), item2->displayName() ) < 0;
981 }
982 
983 QgsLayoutItem *QgsLayoutProxyModel::itemFromSourceIndex( const QModelIndex &sourceIndex ) const
984 {
985  if ( !mLayout )
986  return nullptr;
987 
988  //get column corresponding to an index from the source model
989  QVariant itemAsVariant = sourceModel()->data( sourceIndex, Qt::UserRole + 1 );
990  return qobject_cast<QgsLayoutItem *>( itemAsVariant.value<QObject *>() );
991 }
992 
994 {
995  mItemTypeFilter = filter;
996  invalidate();
997 }
998 
999 void QgsLayoutProxyModel::setExceptedItemList( const QList< QgsLayoutItem *> &items )
1000 {
1001  if ( mExceptedList == items )
1002  return;
1003 
1004  mExceptedList = items;
1005  invalidateFilter();
1006 }
1007 
1008 bool QgsLayoutProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
1009 {
1010  //get QgsComposerItem corresponding to row
1011  QModelIndex index = sourceModel()->index( sourceRow, 0, sourceParent );
1012  QgsLayoutItem *item = itemFromSourceIndex( index );
1013 
1014  if ( !item )
1015  return false;
1016 
1017  // specific exceptions
1018  if ( mExceptedList.contains( item ) )
1019  return false;
1020 
1021  // filter by type
1022  if ( mItemTypeFilter != QgsLayoutItemRegistry::LayoutItem && item->type() != mItemTypeFilter )
1023  return false;
1024 
1025  return true;
1026 }
1027 
QgsLayoutProxyModel(QgsLayout *layout, QObject *parent=nullptr)
Constructor for QgsLayoutProxyModelm, attached to the specified layout.
virtual QIcon icon() const
Returns the item&#39;s icon.
bool setData(const QModelIndex &index, const QVariant &value, int role) override
void setSelectedItem(QgsLayoutItem *item)
Clears any selected items and sets item as the current selection.
Definition: qgslayout.cpp:155
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
QgsLayoutItem * itemFromIndex(const QModelIndex &index) const
Returns the QgsLayoutItem corresponding to a QModelIndex index, if possible.
Base class for graphical items within a QgsLayout.
int type() const override
Returns a unique graphics item type identifier.
QVariant data(const QModelIndex &index, int role) const override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
QgsLayoutItem * itemByUuid(const QString &uuid, bool includeTemplateUuids=false) const
Returns the layout item with matching uuid unique identifier, or a nullptr if a matching item could n...
Definition: qgslayout.cpp:234
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QModelIndex indexForItem(QgsLayoutItem *item, int column=0)
Returns the QModelIndex corresponding to a QgsLayoutItem item and column, if possible.
Qt::ItemFlags flags(const QModelIndex &index) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Qt::DropActions supportedDropActions() const override
void setExceptedItemList(const QList< QgsLayoutItem * > &items)
Sets a list of specific items to exclude from the model.
QMimeData * mimeData(const QModelIndexList &indexes) const override
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
void updateZValues(bool addUndoCommands=true)
Resets the z-values of items based on their position in the internal z order list.
Definition: qgslayout.cpp:896
virtual void setId(const QString &id)
Set the item&#39;s id name.
QStringList mimeTypes() const override
void setLocked(bool locked)
Sets whether the item is locked, preventing mouse interactions with the item.
Item visibility checkbox.
bool zOrderDescending(QgsLayoutItem *item1, QgsLayoutItem *item2)
QString id() const
Returns the item&#39;s ID name.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
void setFilterType(QgsLayoutItemRegistry::ItemType filter)
Sets the item type filter.
virtual void setVisibility(bool visible)
Sets whether the item is visible.
Item lock status checkbox.
QgsLayoutItem * itemFromSourceIndex(const QModelIndex &sourceIndex) const
Returns the QgsLayoutItem corresponding to an index from the source QgsLayoutModel model...
QgsLayoutModel * itemsModel()
Returns the items model attached to the layout.
Definition: qgslayout.cpp:133
virtual QString displayName() const
Gets item display name.
QModelIndex parent(const QModelIndex &index) const override
QgsLayoutModel(QgsLayout *layout, QObject *parent=nullptr)
Constructor for a QgsLayoutModel attached to the specified layout.
Registry of available layout item types.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
virtual QString uuid() const
Returns the item identification string.
bool isLocked() const
Returns true if the item is locked, and cannot be interacted with using the mouse.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override