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