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