QGIS API Documentation 3.36.0-Maidenhead (09951dc0acf)
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 "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
337 while ( !stream.atEnd() )
338 {
339 QString text;
340 stream >> text;
341 QgsLayoutItem *item = mLayout->itemByUuid( text );
342 if ( item )
343 {
344 droppedItems << item;
345 }
346 }
347
348 if ( droppedItems.empty() )
349 {
350 //no dropped items
351 return false;
352 }
353
354 //move dropped items
355
356 //first sort them by z-order
357 std::sort( droppedItems.begin(), droppedItems.end(), zOrderDescending );
358
359 //calculate position in z order list to drop items at
360 int destPos = 0;
361 if ( beginRow < rowCount() )
362 {
363 QgsLayoutItem *itemBefore = mItemsInScene.at( beginRow - 1 );
364 destPos = mItemZList.indexOf( itemBefore );
365 }
366 else
367 {
368 //place items at end
369 destPos = mItemZList.size();
370 }
371
372 //calculate position to insert moved rows to
373 int insertPos = destPos;
374 for ( QgsLayoutItem *item : std::as_const( droppedItems ) )
375 {
376 int listPos = mItemZList.indexOf( item );
377 if ( listPos == -1 )
378 {
379 //should be impossible
380 continue;
381 }
382
383 if ( listPos < destPos )
384 {
385 insertPos--;
386 }
387 }
388
389 //remove rows from list
390 auto itemIt = droppedItems.begin();
391 for ( ; itemIt != droppedItems.end(); ++itemIt )
392 {
393 mItemZList.removeOne( *itemIt );
394 }
395
396 //insert items
397 itemIt = droppedItems.begin();
398 for ( ; itemIt != droppedItems.end(); ++itemIt )
399 {
400 mItemZList.insert( insertPos, *itemIt );
401 insertPos++;
402 }
403
404 rebuildSceneItemList();
405
406 mLayout->updateZValues( true );
407
408 return true;
409}
410
411bool QgsLayoutModel::removeRows( int row, int count, const QModelIndex &parent )
412{
413 Q_UNUSED( count )
414 if ( parent.isValid() )
415 {
416 return false;
417 }
418
419 if ( row >= rowCount() )
420 {
421 return false;
422 }
423
424 //do nothing - moves are handled by the dropMimeData method
425 return true;
426}
427
429void QgsLayoutModel::clear()
430{
431 //totally reset model
432 beginResetModel();
433 mItemZList.clear();
434 refreshItemsInScene();
435 endResetModel();
436}
437
438int QgsLayoutModel::zOrderListSize() const
439{
440 return mItemZList.size();
441}
442
443void QgsLayoutModel::rebuildZList()
444{
445 QList<QgsLayoutItem *> sortedList;
446 //rebuild the item z order list based on the current zValues of items in the scene
447
448 //get items in descending zValue order
449 const QList<QGraphicsItem *> itemList = mLayout->items( Qt::DescendingOrder );
450 for ( QGraphicsItem *item : itemList )
451 {
452 if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
453 {
454 if ( layoutItem->type() != QgsLayoutItemRegistry::LayoutPage )
455 {
456 sortedList.append( layoutItem );
457 }
458 }
459 }
460
461 mItemZList = sortedList;
462 rebuildSceneItemList();
463}
465
466void QgsLayoutModel::rebuildSceneItemList()
467{
468 //step through the z list and rebuild the items in scene list,
469 //emitting signals as required
470 int row = 0;
471 const QList< QGraphicsItem * > items = mLayout->items();
472 for ( QgsLayoutItem *item : std::as_const( mItemZList ) )
473 {
474 if ( item->type() == QgsLayoutItemRegistry::LayoutPage || !items.contains( item ) )
475 {
476 //item not in scene, skip it
477 continue;
478 }
479
480 int sceneListPos = mItemsInScene.indexOf( item );
481 if ( sceneListPos == row )
482 {
483 //already in list in correct position, nothing to do
484
485 }
486 else if ( sceneListPos != -1 )
487 {
488 //in list, but in wrong spot
489 beginMoveRows( QModelIndex(), sceneListPos + 1, sceneListPos + 1, QModelIndex(), row + 1 );
490 mItemsInScene.removeAt( sceneListPos );
491 mItemsInScene.insert( row, item );
492 endMoveRows();
493 }
494 else
495 {
496 //needs to be inserted into list
497 beginInsertRows( QModelIndex(), row + 1, row + 1 );
498 mItemsInScene.insert( row, item );
499 endInsertRows();
500 }
501 row++;
502 }
503}
505void QgsLayoutModel::addItemAtTop( QgsLayoutItem *item )
506{
507 mItemZList.push_front( item );
508 refreshItemsInScene();
509 item->setZValue( mItemZList.size() );
510}
511
512void QgsLayoutModel::removeItem( QgsLayoutItem *item )
513{
514 if ( !item )
515 {
516 //nothing to do
517 return;
518 }
519
520 int pos = mItemZList.indexOf( item );
521 if ( pos == -1 )
522 {
523 //item not in z list, nothing to do
524 return;
525 }
526
527 //need to get QModelIndex of item
528 QModelIndex itemIndex = indexForItem( item );
529 if ( !itemIndex.isValid() )
530 {
531 //removing an item not in the scene (e.g., deleted item)
532 //we need to remove it from the list, but don't need to call
533 //beginRemoveRows or endRemoveRows since the item was not used by the model
534 mItemZList.removeAt( pos );
535 refreshItemsInScene();
536 return;
537 }
538
539 //remove item from model
540 int row = itemIndex.row();
541 beginRemoveRows( QModelIndex(), row, row );
542 mItemZList.removeAt( pos );
543 refreshItemsInScene();
544 endRemoveRows();
545}
546
547void QgsLayoutModel::setItemRemoved( QgsLayoutItem *item )
548{
549 if ( !item )
550 {
551 //nothing to do
552 return;
553 }
554
555 int pos = mItemZList.indexOf( item );
556 if ( pos == -1 )
557 {
558 //item not in z list, nothing to do
559 return;
560 }
561
562 //need to get QModelIndex of item
563 QModelIndex itemIndex = indexForItem( item );
564 if ( !itemIndex.isValid() )
565 {
566 return;
567 }
568
569 //removing item
570 int row = itemIndex.row();
571 beginRemoveRows( QModelIndex(), row, row );
572 mLayout->removeItem( item );
573 refreshItemsInScene();
574 endRemoveRows();
575}
576
577void QgsLayoutModel::updateItemDisplayName( QgsLayoutItem *item )
578{
579 if ( !item )
580 {
581 //nothing to do
582 return;
583 }
584
585 //need to get QModelIndex of item
586 QModelIndex itemIndex = indexForItem( item, ItemId );
587 if ( !itemIndex.isValid() )
588 {
589 return;
590 }
591
592 //emit signal for item id change
593 emit dataChanged( itemIndex, itemIndex );
594}
595
596void QgsLayoutModel::updateItemLockStatus( QgsLayoutItem *item )
597{
598 if ( !item )
599 {
600 //nothing to do
601 return;
602 }
603
604 //need to get QModelIndex of item
605 QModelIndex itemIndex = indexForItem( item, LockStatus );
606 if ( !itemIndex.isValid() )
607 {
608 return;
609 }
610
611 //emit signal for item lock status change
612 emit dataChanged( itemIndex, itemIndex );
613}
614
615void QgsLayoutModel::updateItemVisibility( QgsLayoutItem *item )
616{
617 if ( !item )
618 {
619 //nothing to do
620 return;
621 }
622
623 //need to get QModelIndex of item
624 QModelIndex itemIndex = indexForItem( item, Visibility );
625 if ( !itemIndex.isValid() )
626 {
627 return;
628 }
629
630 //emit signal for item visibility change
631 emit dataChanged( itemIndex, itemIndex );
632}
633
634void QgsLayoutModel::updateItemSelectStatus( QgsLayoutItem *item )
635{
636 if ( !item )
637 {
638 //nothing to do
639 return;
640 }
641
642 //need to get QModelIndex of item
643 QModelIndex itemIndex = indexForItem( item, ItemId );
644 if ( !itemIndex.isValid() )
645 {
646 return;
647 }
648
649 //emit signal for item visibility change
650 emit dataChanged( itemIndex, itemIndex );
651}
652
653bool QgsLayoutModel::reorderItemUp( QgsLayoutItem *item )
654{
655 if ( !item )
656 {
657 return false;
658 }
659
660 if ( mItemsInScene.at( 0 ) == item )
661 {
662 //item is already topmost item present in scene, nothing to do
663 return false;
664 }
665
666 //move item in z list
667 QMutableListIterator<QgsLayoutItem *> it( mItemZList );
668 if ( ! it.findNext( item ) )
669 {
670 //can't find item in z list, nothing to do
671 return false;
672 }
673
674 const QList< QGraphicsItem * > sceneItems = mLayout->items();
675
676 it.remove();
677 while ( it.hasPrevious() )
678 {
679 //search through item z list to find previous item which is present in the scene
680 it.previous();
681 if ( it.value() && sceneItems.contains( it.value() ) )
682 {
683 break;
684 }
685 }
686 it.insert( item );
687
688 //also move item in scene items z list and notify of model changes
689 QModelIndex itemIndex = indexForItem( item );
690 if ( !itemIndex.isValid() )
691 {
692 return true;
693 }
694
695 //move item up in scene list
696 int row = itemIndex.row();
697 beginMoveRows( QModelIndex(), row, row, QModelIndex(), row - 1 );
698 refreshItemsInScene();
699 endMoveRows();
700 return true;
701}
702
703bool QgsLayoutModel::reorderItemDown( QgsLayoutItem *item )
704{
705 if ( !item )
706 {
707 return false;
708 }
709
710 if ( mItemsInScene.last() == item )
711 {
712 //item is already lowest item present in scene, nothing to do
713 return false;
714 }
715
716 //move item in z list
717 QMutableListIterator<QgsLayoutItem *> it( mItemZList );
718 if ( ! it.findNext( item ) )
719 {
720 //can't find item in z list, nothing to do
721 return false;
722 }
723
724 const QList< QGraphicsItem * > sceneItems = mLayout->items();
725 it.remove();
726 while ( it.hasNext() )
727 {
728 //search through item z list to find next item which is present in the scene
729 //(deleted items still exist in the z list so that they can be restored to their correct stacking order,
730 //but since they are not in the scene they should be ignored here)
731 it.next();
732 if ( it.value() && sceneItems.contains( it.value() ) )
733 {
734 break;
735 }
736 }
737 it.insert( item );
738
739 //also move item in scene items z list and notify of model changes
740 QModelIndex itemIndex = indexForItem( item );
741 if ( !itemIndex.isValid() )
742 {
743 return true;
744 }
745
746 //move item down in scene list
747 int row = itemIndex.row();
748 beginMoveRows( QModelIndex(), row, row, QModelIndex(), row + 2 );
749 refreshItemsInScene();
750 endMoveRows();
751 return true;
752}
753
754bool QgsLayoutModel::reorderItemToTop( QgsLayoutItem *item )
755{
756 if ( !item || !mItemsInScene.contains( item ) )
757 {
758 return false;
759 }
760
761 if ( mItemsInScene.at( 0 ) == item )
762 {
763 //item is already topmost item present in scene, nothing to do
764 return false;
765 }
766
767 //move item in z list
768 QMutableListIterator<QgsLayoutItem *> it( mItemZList );
769 if ( it.findNext( item ) )
770 {
771 it.remove();
772 }
773 mItemZList.push_front( item );
774
775 //also move item in scene items z list and notify of model changes
776 QModelIndex itemIndex = indexForItem( item );
777 if ( !itemIndex.isValid() )
778 {
779 return true;
780 }
781
782 //move item to top
783 int row = itemIndex.row();
784 beginMoveRows( QModelIndex(), row, row, QModelIndex(), 1 );
785 refreshItemsInScene();
786 endMoveRows();
787 return true;
788}
789
790bool QgsLayoutModel::reorderItemToBottom( QgsLayoutItem *item )
791{
792 if ( !item || !mItemsInScene.contains( item ) )
793 {
794 return false;
795 }
796
797 if ( mItemsInScene.last() == item )
798 {
799 //item is already lowest item present in scene, nothing to do
800 return false;
801 }
802
803 //move item in z list
804 QMutableListIterator<QgsLayoutItem *> it( mItemZList );
805 if ( it.findNext( item ) )
806 {
807 it.remove();
808 }
809 mItemZList.push_back( item );
810
811 //also move item in scene items z list and notify of model changes
812 QModelIndex itemIndex = indexForItem( item );
813 if ( !itemIndex.isValid() )
814 {
815 return true;
816 }
817
818 //move item to bottom
819 int row = itemIndex.row();
820 beginMoveRows( QModelIndex(), row, row, QModelIndex(), rowCount() );
821 refreshItemsInScene();
822 endMoveRows();
823 return true;
824}
825
826QgsLayoutItem *QgsLayoutModel::findItemAbove( QgsLayoutItem *item ) const
827{
828 //search item z list for selected item
829 QListIterator<QgsLayoutItem *> it( mItemZList );
830 it.toBack();
831 if ( it.findPrevious( item ) )
832 {
833 //move position to before selected item
834 while ( it.hasPrevious() )
835 {
836 //now find previous item, since list is sorted from lowest->highest items
837 if ( it.hasPrevious() && !it.peekPrevious()->isGroupMember() )
838 {
839 return it.previous();
840 }
841 it.previous();
842 }
843 }
844 return nullptr;
845}
846
847QgsLayoutItem *QgsLayoutModel::findItemBelow( QgsLayoutItem *item ) const
848{
849 //search item z list for selected item
850 QListIterator<QgsLayoutItem *> it( mItemZList );
851 if ( it.findNext( item ) )
852 {
853 //return next item (list is sorted from lowest->highest items)
854 while ( it.hasNext() )
855 {
856 if ( !it.peekNext()->isGroupMember() )
857 {
858 return it.next();
859 }
860 it.next();
861 }
862 }
863 return nullptr;
864}
865
866QList<QgsLayoutItem *> &QgsLayoutModel::zOrderList()
867{
868 return mItemZList;
869}
870
872
873Qt::ItemFlags QgsLayoutModel::flags( const QModelIndex &index ) const
874{
875 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
876
877 if ( ! index.isValid() )
878 {
879 return flags | Qt::ItemIsDropEnabled;
880 }
881
882 if ( index.row() == 0 )
883 {
884 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
885 }
886 else
887 {
888 switch ( index.column() )
889 {
890 case Visibility:
891 case LockStatus:
892 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
893 case ItemId:
894 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
895 default:
896 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
897 }
898 }
899}
900
901QModelIndex QgsLayoutModel::indexForItem( QgsLayoutItem *item, const int column )
902{
903 if ( !item )
904 {
905 return QModelIndex();
906 }
907
908 int row = mItemsInScene.indexOf( item );
909 if ( row == -1 )
910 {
911 //not found
912 return QModelIndex();
913 }
914
915 return index( row + 1, column );
916}
917
919void QgsLayoutModel::setSelected( const QModelIndex &index )
920{
922 if ( !item )
923 {
924 return;
925 }
926
927 // find top level group this item is contained within, and mark the group as selected
928 QgsLayoutItemGroup *group = item->parentGroup();
929 while ( group && group->parentGroup() )
930 {
931 group = group->parentGroup();
932 }
933
934 // but the actual main selected item is the item itself (allows editing of item properties)
935 mLayout->setSelectedItem( item );
936
937 if ( group && group != item )
938 group->setSelected( true );
939}
941
942//
943// QgsLayoutProxyModel
944//
945
947 : QSortFilterProxyModel( parent )
948 , mLayout( layout )
949 , mItemTypeFilter( QgsLayoutItemRegistry::LayoutItem )
950{
951 if ( mLayout )
952 setSourceModel( mLayout->itemsModel() );
953
954 setDynamicSortFilter( true );
955 setSortLocaleAware( true );
957}
958
959bool QgsLayoutProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
960{
961 const QString leftText = sourceModel()->data( left, Qt::DisplayRole ).toString();
962 const QString rightText = sourceModel()->data( right, Qt::DisplayRole ).toString();
963 if ( leftText.isEmpty() )
964 return true;
965 if ( rightText.isEmpty() )
966 return false;
967
968 //sort by item id
969 const QgsLayoutItem *item1 = itemFromSourceIndex( left );
970 const QgsLayoutItem *item2 = itemFromSourceIndex( right );
971 if ( !item1 )
972 return false;
973
974 if ( !item2 )
975 return true;
976
977 return QString::localeAwareCompare( item1->displayName(), item2->displayName() ) < 0;
978}
979
980QgsLayoutItem *QgsLayoutProxyModel::itemFromSourceIndex( const QModelIndex &sourceIndex ) const
981{
982 if ( !mLayout )
983 return nullptr;
984
985 //get column corresponding to an index from the source model
986 QVariant itemAsVariant = sourceModel()->data( sourceIndex, Qt::UserRole + 1 );
987 return qobject_cast<QgsLayoutItem *>( itemAsVariant.value<QObject *>() );
988}
989
991{
992 mAllowEmpty = allowEmpty;
993 invalidateFilter();
994}
995
997{
998 return mAllowEmpty;
999}
1000
1001void QgsLayoutProxyModel::setItemFlags( QgsLayoutItem::Flags flags )
1002{
1003 mItemFlags = flags;
1004 invalidateFilter();
1005}
1006
1007QgsLayoutItem::Flags QgsLayoutProxyModel::itemFlags() const
1008{
1009 return mItemFlags;
1010}
1011
1013{
1014 mItemTypeFilter = filter;
1015 invalidate();
1016}
1017
1018void QgsLayoutProxyModel::setExceptedItemList( const QList< QgsLayoutItem *> &items )
1019{
1020 if ( mExceptedList == items )
1021 return;
1022
1023 mExceptedList = items;
1024 invalidateFilter();
1025}
1026
1027bool QgsLayoutProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
1028{
1029 //get QgsComposerItem corresponding to row
1030 QModelIndex index = sourceModel()->index( sourceRow, 0, sourceParent );
1031 QgsLayoutItem *item = itemFromSourceIndex( index );
1032
1033 if ( !item )
1034 return mAllowEmpty;
1035
1036 // specific exceptions
1037 if ( mExceptedList.contains( item ) )
1038 return false;
1039
1040 // filter by type
1041 if ( mItemTypeFilter != QgsLayoutItemRegistry::LayoutItem && item->type() != mItemTypeFilter )
1042 return false;
1043
1044 if ( mItemFlags && !( item->itemFlags() & mItemFlags ) )
1045 {
1046 return false;
1047 }
1048
1049 return true;
1050}
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
@ 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)