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