QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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.
QgsLayoutItem * itemFromIndex(const QModelIndex &index) const
Returns the QgsLayoutItem corresponding to a QModelIndex index, if possible.
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:156
virtual QIcon icon() const
Returns the item&#39;s icon.
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
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
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.
QgsLayoutItem * itemFromSourceIndex(const QModelIndex &sourceIndex) const
Returns the QgsLayoutItem corresponding to an index from the source QgsLayoutModel model...
Qt::DropActions supportedDropActions() const override
void setExceptedItemList(const QList< QgsLayoutItem * > &items)
Sets a list of specific items to exclude from the model.
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:235
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:898
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)
virtual QString uuid() const
Returns the item identification string.
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.
QgsLayoutModel * itemsModel()
Returns the items model attached to the layout.
Definition: qgslayout.cpp:134
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 displayName() const
Gets item display name.
QString id() const
Returns the item&#39;s ID name.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
bool isLocked() const
Returns true if the item is locked, and cannot be interacted with using the mouse.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override