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