QGIS API Documentation  3.6.0-Noosa (5873452)
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  default:
186  return QVariant();
187  }
188 }
189 
190 bool QgsLayoutModel::setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole )
191 {
192  Q_UNUSED( role );
193 
194  if ( !index.isValid() )
195  return false;
196 
197  QgsLayoutItem *item = itemFromIndex( index );
198  if ( !item )
199  {
200  return false;
201  }
202 
203  switch ( index.column() )
204  {
205  case Visibility:
206  //first column is item visibility
207  item->setVisibility( value.toBool() );
208  return true;
209 
210  case LockStatus:
211  //second column is item lock state
212  item->setLocked( value.toBool() );
213  return true;
214 
215  case ItemId:
216  //last column is item id
217  item->setId( value.toString() );
218  return true;
219  }
220 
221  return false;
222 }
223 
224 QVariant QgsLayoutModel::headerData( int section, Qt::Orientation orientation, int role ) const
225 {
226  switch ( role )
227  {
228  case Qt::DisplayRole:
229  {
230  if ( section == ItemId )
231  {
232  return tr( "Item" );
233  }
234  return QVariant();
235  }
236 
237  case Qt::DecorationRole:
238  {
239  if ( section == Visibility )
240  {
241  return qVariantFromValue( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowAllLayersGray.svg" ) ) );
242  }
243  else if ( section == LockStatus )
244  {
245  return qVariantFromValue( QgsApplication::getThemeIcon( QStringLiteral( "/lockedGray.svg" ) ) );
246  }
247 
248  return QVariant();
249  }
250 
251  case Qt::TextAlignmentRole:
252  return Qt::AlignLeft & Qt::AlignVCenter;
253 
254  default:
255  return QAbstractItemModel::headerData( section, orientation, role );
256  }
257 
258 }
259 
261 {
262  return Qt::MoveAction;
263 }
264 
265 QStringList QgsLayoutModel::mimeTypes() const
266 {
267  QStringList types;
268  types << QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" );
269  return types;
270 }
271 
272 QMimeData *QgsLayoutModel::mimeData( const QModelIndexList &indexes ) const
273 {
274  QMimeData *mimeData = new QMimeData();
275  QByteArray encodedData;
276 
277  QDataStream stream( &encodedData, QIODevice::WriteOnly );
278 
279  for ( const QModelIndex &index : indexes )
280  {
281  if ( index.isValid() && index.column() == ItemId )
282  {
283  QgsLayoutItem *item = itemFromIndex( index );
284  if ( !item )
285  {
286  continue;
287  }
288  QString text = item->uuid();
289  stream << text;
290  }
291  }
292 
293  mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ), encodedData );
294  return mimeData;
295 }
296 
298 {
299  return item1->zValue() > item2->zValue();
300 }
301 
302 bool QgsLayoutModel::dropMimeData( const QMimeData *data,
303  Qt::DropAction action, int row, int column, const QModelIndex &parent )
304 {
305  if ( column != ItemId )
306  {
307  return false;
308  }
309 
310  if ( action == Qt::IgnoreAction )
311  {
312  return true;
313  }
314 
315  if ( !data->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ) ) )
316  {
317  return false;
318  }
319 
320  if ( parent.isValid() )
321  {
322  return false;
323  }
324 
325  int beginRow = row != -1 ? row : rowCount( QModelIndex() );
326 
327  QByteArray encodedData = data->data( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ) );
328  QDataStream stream( &encodedData, QIODevice::ReadOnly );
329  QList<QgsLayoutItem *> droppedItems;
330  int rows = 0;
331 
332  while ( !stream.atEnd() )
333  {
334  QString text;
335  stream >> text;
336  QgsLayoutItem *item = mLayout->itemByUuid( text );
337  if ( item )
338  {
339  droppedItems << item;
340  ++rows;
341  }
342  }
343 
344  if ( droppedItems.empty() )
345  {
346  //no dropped items
347  return false;
348  }
349 
350  //move dropped items
351 
352  //first sort them by z-order
353  std::sort( droppedItems.begin(), droppedItems.end(), zOrderDescending );
354 
355  //calculate position in z order list to drop items at
356  int destPos = 0;
357  if ( beginRow < rowCount() )
358  {
359  QgsLayoutItem *itemBefore = mItemsInScene.at( beginRow );
360  destPos = mItemZList.indexOf( itemBefore );
361  }
362  else
363  {
364  //place items at end
365  destPos = mItemZList.size();
366  }
367 
368  //calculate position to insert moved rows to
369  int insertPos = destPos;
370  for ( QgsLayoutItem *item : qgis::as_const( droppedItems ) )
371  {
372  int listPos = mItemZList.indexOf( item );
373  if ( listPos == -1 )
374  {
375  //should be impossible
376  continue;
377  }
378 
379  if ( listPos < destPos )
380  {
381  insertPos--;
382  }
383  }
384 
385  //remove rows from list
386  auto itemIt = droppedItems.begin();
387  for ( ; itemIt != droppedItems.end(); ++itemIt )
388  {
389  mItemZList.removeOne( *itemIt );
390  }
391 
392  //insert items
393  itemIt = droppedItems.begin();
394  for ( ; itemIt != droppedItems.end(); ++itemIt )
395  {
396  mItemZList.insert( insertPos, *itemIt );
397  insertPos++;
398  }
399 
400  rebuildSceneItemList();
401 
402  mLayout->updateZValues( true );
403 
404  return true;
405 }
406 
407 bool QgsLayoutModel::removeRows( int row, int count, const QModelIndex &parent )
408 {
409  Q_UNUSED( count );
410  if ( parent.isValid() )
411  {
412  return false;
413  }
414 
415  if ( row >= rowCount() )
416  {
417  return false;
418  }
419 
420  //do nothing - moves are handled by the dropMimeData method
421  return true;
422 }
423 
425 void QgsLayoutModel::clear()
426 {
427  //totally reset model
428  beginResetModel();
429  mItemZList.clear();
430  refreshItemsInScene();
431  endResetModel();
432 }
433 
434 int QgsLayoutModel::zOrderListSize() const
435 {
436  return mItemZList.size();
437 }
438 
439 void QgsLayoutModel::rebuildZList()
440 {
441  QList<QgsLayoutItem *> sortedList;
442  //rebuild the item z order list based on the current zValues of items in the scene
443 
444  //get items in descending zValue order
445  const QList<QGraphicsItem *> itemList = mLayout->items( Qt::DescendingOrder );
446  for ( QGraphicsItem *item : itemList )
447  {
448  if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
449  {
450  if ( layoutItem->type() != QgsLayoutItemRegistry::LayoutPage )
451  {
452  sortedList.append( layoutItem );
453  }
454  }
455  }
456 
457  mItemZList = sortedList;
458  rebuildSceneItemList();
459 }
461 
462 void QgsLayoutModel::rebuildSceneItemList()
463 {
464  //step through the z list and rebuild the items in scene list,
465  //emitting signals as required
466  int row = 0;
467  const QList< QGraphicsItem * > items = mLayout->items();
468  for ( QgsLayoutItem *item : qgis::as_const( mItemZList ) )
469  {
470  if ( item->type() == QgsLayoutItemRegistry::LayoutPage || !items.contains( item ) )
471  {
472  //item not in scene, skip it
473  continue;
474  }
475 
476  int sceneListPos = mItemsInScene.indexOf( item );
477  if ( sceneListPos == row )
478  {
479  //already in list in correct position, nothing to do
480 
481  }
482  else if ( sceneListPos != -1 )
483  {
484  //in list, but in wrong spot
485  beginMoveRows( QModelIndex(), sceneListPos, sceneListPos, QModelIndex(), row );
486  mItemsInScene.removeAt( sceneListPos );
487  mItemsInScene.insert( row, item );
488  endMoveRows();
489  }
490  else
491  {
492  //needs to be inserted into list
493  beginInsertRows( QModelIndex(), row, row );
494  mItemsInScene.insert( row, item );
495  endInsertRows();
496  }
497  row++;
498  }
499 }
501 void QgsLayoutModel::addItemAtTop( QgsLayoutItem *item )
502 {
503  mItemZList.push_front( item );
504  refreshItemsInScene();
505  item->setZValue( mItemZList.size() );
506 }
507 
508 void QgsLayoutModel::removeItem( QgsLayoutItem *item )
509 {
510  if ( !item )
511  {
512  //nothing to do
513  return;
514  }
515 
516  int pos = mItemZList.indexOf( item );
517  if ( pos == -1 )
518  {
519  //item not in z list, nothing to do
520  return;
521  }
522 
523  //need to get QModelIndex of item
524  QModelIndex itemIndex = indexForItem( item );
525  if ( !itemIndex.isValid() )
526  {
527  //removing an item not in the scene (e.g., deleted item)
528  //we need to remove it from the list, but don't need to call
529  //beginRemoveRows or endRemoveRows since the item was not used by the model
530  mItemZList.removeAt( pos );
531  refreshItemsInScene();
532  return;
533  }
534 
535  //remove item from model
536  int row = itemIndex.row();
537  beginRemoveRows( QModelIndex(), row, row );
538  mItemZList.removeAt( pos );
539  refreshItemsInScene();
540  endRemoveRows();
541 }
542 
543 void QgsLayoutModel::setItemRemoved( QgsLayoutItem *item )
544 {
545  if ( !item )
546  {
547  //nothing to do
548  return;
549  }
550 
551  int pos = mItemZList.indexOf( item );
552  if ( pos == -1 )
553  {
554  //item not in z list, nothing to do
555  return;
556  }
557 
558  //need to get QModelIndex of item
559  QModelIndex itemIndex = indexForItem( item );
560  if ( !itemIndex.isValid() )
561  {
562  return;
563  }
564 
565  //removing item
566  int row = itemIndex.row();
567  beginRemoveRows( QModelIndex(), row, row );
568  mLayout->removeItem( item );
569  refreshItemsInScene();
570  endRemoveRows();
571 }
572 
573 #if 0
574 void QgsLayoutModel::setItemRestored( QgsComposerItem *item )
575 {
576  if ( !item )
577  {
578  //nothing to do
579  return;
580  }
581 
582  int pos = mItemZList.indexOf( item );
583  if ( pos == -1 )
584  {
585  //item not in z list, nothing to do
586  return;
587  }
588 
589  item->setIsRemoved( false );
590  rebuildSceneItemList();
591 }
592 #endif
593 
594 void QgsLayoutModel::updateItemDisplayName( QgsLayoutItem *item )
595 {
596  if ( !item )
597  {
598  //nothing to do
599  return;
600  }
601 
602  //need to get QModelIndex of item
603  QModelIndex itemIndex = indexForItem( item, ItemId );
604  if ( !itemIndex.isValid() )
605  {
606  return;
607  }
608 
609  //emit signal for item id change
610  emit dataChanged( itemIndex, itemIndex );
611 }
612 
613 void QgsLayoutModel::updateItemLockStatus( QgsLayoutItem *item )
614 {
615  if ( !item )
616  {
617  //nothing to do
618  return;
619  }
620 
621  //need to get QModelIndex of item
622  QModelIndex itemIndex = indexForItem( item, LockStatus );
623  if ( !itemIndex.isValid() )
624  {
625  return;
626  }
627 
628  //emit signal for item lock status change
629  emit dataChanged( itemIndex, itemIndex );
630 }
631 
632 void QgsLayoutModel::updateItemVisibility( QgsLayoutItem *item )
633 {
634  if ( !item )
635  {
636  //nothing to do
637  return;
638  }
639 
640  //need to get QModelIndex of item
641  QModelIndex itemIndex = indexForItem( item, Visibility );
642  if ( !itemIndex.isValid() )
643  {
644  return;
645  }
646 
647  //emit signal for item visibility change
648  emit dataChanged( itemIndex, itemIndex );
649 }
650 
651 void QgsLayoutModel::updateItemSelectStatus( QgsLayoutItem *item )
652 {
653  if ( !item )
654  {
655  //nothing to do
656  return;
657  }
658 
659  //need to get QModelIndex of item
660  QModelIndex itemIndex = indexForItem( item, ItemId );
661  if ( !itemIndex.isValid() )
662  {
663  return;
664  }
665 
666  //emit signal for item visibility change
667  emit dataChanged( itemIndex, itemIndex );
668 }
669 
670 bool QgsLayoutModel::reorderItemUp( QgsLayoutItem *item )
671 {
672  if ( !item )
673  {
674  return false;
675  }
676 
677  if ( mItemsInScene.at( 0 ) == item )
678  {
679  //item is already topmost item present in scene, nothing to do
680  return false;
681  }
682 
683  //move item in z list
684  QMutableListIterator<QgsLayoutItem *> it( mItemZList );
685  if ( ! it.findNext( item ) )
686  {
687  //can't find item in z list, nothing to do
688  return false;
689  }
690 
691  const QList< QGraphicsItem * > sceneItems = mLayout->items();
692 
693  it.remove();
694  while ( it.hasPrevious() )
695  {
696  //search through item z list to find previous item which is present in the scene
697  it.previous();
698  if ( it.value() && sceneItems.contains( it.value() ) )
699  {
700  break;
701  }
702  }
703  it.insert( item );
704 
705  //also move item in scene items z list and notify of model changes
706  QModelIndex itemIndex = indexForItem( item );
707  if ( !itemIndex.isValid() )
708  {
709  return true;
710  }
711 
712  //move item up in scene list
713  int row = itemIndex.row();
714  beginMoveRows( QModelIndex(), row, row, QModelIndex(), row - 1 );
715  refreshItemsInScene();
716  endMoveRows();
717  return true;
718 }
719 
720 bool QgsLayoutModel::reorderItemDown( QgsLayoutItem *item )
721 {
722  if ( !item )
723  {
724  return false;
725  }
726 
727  if ( mItemsInScene.last() == item )
728  {
729  //item is already lowest item present in scene, nothing to do
730  return false;
731  }
732 
733  //move item in z list
734  QMutableListIterator<QgsLayoutItem *> it( mItemZList );
735  if ( ! it.findNext( item ) )
736  {
737  //can't find item in z list, nothing to do
738  return false;
739  }
740 
741  const QList< QGraphicsItem * > sceneItems = mLayout->items();
742  it.remove();
743  while ( it.hasNext() )
744  {
745  //search through item z list to find next item which is present in the scene
746  //(deleted items still exist in the z list so that they can be restored to their correct stacking order,
747  //but since they are not in the scene they should be ignored here)
748  it.next();
749  if ( it.value() && sceneItems.contains( it.value() ) )
750  {
751  break;
752  }
753  }
754  it.insert( item );
755 
756  //also move item in scene items z list and notify of model changes
757  QModelIndex itemIndex = indexForItem( item );
758  if ( !itemIndex.isValid() )
759  {
760  return true;
761  }
762 
763  //move item down in scene list
764  int row = itemIndex.row();
765  beginMoveRows( QModelIndex(), row, row, QModelIndex(), row + 2 );
766  refreshItemsInScene();
767  endMoveRows();
768  return true;
769 }
770 
771 bool QgsLayoutModel::reorderItemToTop( QgsLayoutItem *item )
772 {
773  if ( !item || !mItemsInScene.contains( item ) )
774  {
775  return false;
776  }
777 
778  if ( mItemsInScene.at( 0 ) == item )
779  {
780  //item is already topmost item present in scene, nothing to do
781  return false;
782  }
783 
784  //move item in z list
785  QMutableListIterator<QgsLayoutItem *> it( mItemZList );
786  if ( it.findNext( item ) )
787  {
788  it.remove();
789  }
790  mItemZList.push_front( item );
791 
792  //also move item in scene items z list and notify of model changes
793  QModelIndex itemIndex = indexForItem( item );
794  if ( !itemIndex.isValid() )
795  {
796  return true;
797  }
798 
799  //move item to top
800  int row = itemIndex.row();
801  beginMoveRows( QModelIndex(), row, row, QModelIndex(), 0 );
802  refreshItemsInScene();
803  endMoveRows();
804  return true;
805 }
806 
807 bool QgsLayoutModel::reorderItemToBottom( QgsLayoutItem *item )
808 {
809  if ( !item || !mItemsInScene.contains( item ) )
810  {
811  return false;
812  }
813 
814  if ( mItemsInScene.last() == item )
815  {
816  //item is already lowest item present in scene, nothing to do
817  return false;
818  }
819 
820  //move item in z list
821  QMutableListIterator<QgsLayoutItem *> it( mItemZList );
822  if ( it.findNext( item ) )
823  {
824  it.remove();
825  }
826  mItemZList.push_back( item );
827 
828  //also move item in scene items z list and notify of model changes
829  QModelIndex itemIndex = indexForItem( item );
830  if ( !itemIndex.isValid() )
831  {
832  return true;
833  }
834 
835  //move item to bottom
836  int row = itemIndex.row();
837  beginMoveRows( QModelIndex(), row, row, QModelIndex(), rowCount() );
838  refreshItemsInScene();
839  endMoveRows();
840  return true;
841 }
842 
843 QgsLayoutItem *QgsLayoutModel::findItemAbove( QgsLayoutItem *item ) const
844 {
845  //search item z list for selected item
846  QListIterator<QgsLayoutItem *> it( mItemZList );
847  it.toBack();
848  if ( it.findPrevious( item ) )
849  {
850  //move position to before selected item
851  while ( it.hasPrevious() )
852  {
853  //now find previous item, since list is sorted from lowest->highest items
854  if ( it.hasPrevious() && !it.peekPrevious()->isGroupMember() )
855  {
856  return it.previous();
857  }
858  it.previous();
859  }
860  }
861  return nullptr;
862 }
863 
864 QgsLayoutItem *QgsLayoutModel::findItemBelow( QgsLayoutItem *item ) const
865 {
866  //search item z list for selected item
867  QListIterator<QgsLayoutItem *> it( mItemZList );
868  if ( it.findNext( item ) )
869  {
870  //return next item (list is sorted from lowest->highest items)
871  while ( it.hasNext() )
872  {
873  if ( !it.peekNext()->isGroupMember() )
874  {
875  return it.next();
876  }
877  it.next();
878  }
879  }
880  return nullptr;
881 }
882 
883 QList<QgsLayoutItem *> &QgsLayoutModel::zOrderList()
884 {
885  return mItemZList;
886 }
887 
889 
890 Qt::ItemFlags QgsLayoutModel::flags( const QModelIndex &index ) const
891 {
892  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
893 
894  if ( ! index.isValid() )
895  {
896  return flags | Qt::ItemIsDropEnabled;
897  }
898 
899  switch ( index.column() )
900  {
901  case Visibility:
902  case LockStatus:
903  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
904  case ItemId:
905  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
906  default:
907  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
908  }
909 }
910 
911 QModelIndex QgsLayoutModel::indexForItem( QgsLayoutItem *item, const int column )
912 {
913  if ( !item )
914  {
915  return QModelIndex();
916  }
917 
918  int row = mItemsInScene.indexOf( item );
919  if ( row == -1 )
920  {
921  //not found
922  return QModelIndex();
923  }
924 
925  return index( row, column );
926 }
927 
929 void QgsLayoutModel::setSelected( const QModelIndex &index )
930 {
931  QgsLayoutItem *item = itemFromIndex( index );
932  if ( !item )
933  {
934  return;
935  }
936 
937  mLayout->setSelectedItem( item );
938 }
940 
941 //
942 // QgsLayoutProxyModel
943 //
944 
946  : QSortFilterProxyModel( parent )
947  , mLayout( layout )
948  , mItemTypeFilter( QgsLayoutItemRegistry::LayoutItem )
949 {
950  if ( mLayout )
951  setSourceModel( mLayout->itemsModel() );
952 
953  // TODO doesn't seem to work correctly - not updated when item changes
954  setDynamicSortFilter( true );
955  setSortLocaleAware( true );
956  sort( QgsLayoutModel::ItemId );
957 }
958 
959 bool QgsLayoutProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
960 {
961  //sort by item id
962  const QgsLayoutItem *item1 = itemFromSourceIndex( left );
963  const QgsLayoutItem *item2 = itemFromSourceIndex( right );
964  if ( !item1 )
965  return false;
966 
967  if ( !item2 )
968  return true;
969 
970  return QString::localeAwareCompare( item1->displayName(), item2->displayName() ) < 0;
971 }
972 
973 QgsLayoutItem *QgsLayoutProxyModel::itemFromSourceIndex( const QModelIndex &sourceIndex ) const
974 {
975  if ( !mLayout )
976  return nullptr;
977 
978  //get column corresponding to an index from the source model
979  QVariant itemAsVariant = sourceModel()->data( sourceIndex, Qt::UserRole + 1 );
980  return qobject_cast<QgsLayoutItem *>( itemAsVariant.value<QObject *>() );
981 }
982 
984 {
985  mItemTypeFilter = filter;
986  invalidate();
987 }
988 
989 void QgsLayoutProxyModel::setExceptedItemList( const QList< QgsLayoutItem *> &items )
990 {
991  if ( mExceptedList == items )
992  return;
993 
994  mExceptedList = items;
995  invalidateFilter();
996 }
997 
998 bool QgsLayoutProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
999 {
1000  //get QgsComposerItem corresponding to row
1001  QModelIndex index = sourceModel()->index( sourceRow, 0, sourceParent );
1002  QgsLayoutItem *item = itemFromSourceIndex( index );
1003 
1004  if ( !item )
1005  return false;
1006 
1007  // specific exceptions
1008  if ( mExceptedList.contains( item ) )
1009  return false;
1010 
1011  // filter by type
1012  if ( mItemTypeFilter != QgsLayoutItemRegistry::LayoutItem && item->type() != mItemTypeFilter )
1013  return false;
1014 
1015  return true;
1016 }
1017 
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:157
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:236
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: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)
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:135
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