QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgslayoutitemslistview.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemslistview.cpp
3 --------------------------
4 Date : October 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgslayout.h"
18#include "qgslayoutmodel.h"
20#include "qgslayoutview.h"
21#include "qgslayoutitemgroup.h"
22#include <QHeaderView>
23#include <QMenu>
24#include <QMouseEvent>
25
26
28 : QSortFilterProxyModel( parent )
29 , mModel( model )
30{
31 setSourceModel( mModel );
32}
33
35{
36 return mModel->itemFromIndex( mapToSource( index ) );
37}
38
39QModelIndex QgsLayoutItemsListViewModel::indexForItem( QgsLayoutItem *item, const int column ) const
40{
41 return mapFromSource( mModel->indexForItem( item, column ) );
42}
43
44void QgsLayoutItemsListViewModel::setSelected( const QModelIndex &index )
45{
46 mModel->setSelected( mapToSource( index ) );
47}
48
49bool QgsLayoutItemsListViewModel::filterAcceptsRow( int sourceRow, const QModelIndex & ) const
50{
51 if ( sourceRow == 0 )
52 return false; // hide empty null item row
53 return true;
54}
55
56QVariant QgsLayoutItemsListViewModel::data( const QModelIndex &index, int role ) const
57{
58 if ( !index.isValid() )
59 return QVariant();
60
61 QgsLayoutItem *item = itemFromIndex( index );
62 if ( !item )
63 {
64 return QVariant();
65 }
66
67 if ( role == Qt::FontRole )
68 {
69 if ( index.column() == QgsLayoutModel::ItemId && item->isSelected() )
70 {
71 //draw name of selected items in bold
72 QFont boldFont;
73 boldFont.setBold( true );
74 return boldFont;
75 }
76 }
77
78 return QSortFilterProxyModel::data( index, role );
79}
80
81
82//
83// QgsLayoutItemsListView
84//
85
87 : QTreeView( parent )
88 , mDesigner( designer )
89{
90 setColumnWidth( 0, 30 );
91 setColumnWidth( 1, 30 );
92 setDragEnabled( true );
93 setAcceptDrops( true );
94 setDropIndicatorShown( true );
95 setDragDropMode( QAbstractItemView::InternalMove );
96 setContextMenuPolicy( Qt::CustomContextMenu );
97 setIndentation( 0 );
98
99 // Allow multi selection from the list view
100 setSelectionMode( QAbstractItemView::ExtendedSelection );
101 setSelectionBehavior( QAbstractItemView::SelectRows );
102
103 connect( this, &QWidget::customContextMenuRequested, this, &QgsLayoutItemsListView::showContextMenu );
104 connect( mDesigner->view(), &QgsLayoutView::itemFocused, this, &QgsLayoutItemsListView::onItemFocused );
105}
106
108{
109 mLayout = layout;
110 mModel = new QgsLayoutItemsListViewModel( layout->itemsModel(), this );
111 setModel( mModel );
112
113 header()->setSectionResizeMode( 0, QHeaderView::Fixed );
114 header()->setSectionResizeMode( 1, QHeaderView::Fixed );
115 setColumnWidth( 0, Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'x' ) * 4 );
116 setColumnWidth( 1, Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'x' ) * 4 );
117 header()->setSectionsMovable( false );
118
119 connect( selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsLayoutItemsListView::updateSelection );
120}
121
123{
124 if ( event->key() == Qt::Key_Space )
125 {
126 const auto constSelectedIndexes = selectionModel()->selectedIndexes();
127 if ( !constSelectedIndexes.isEmpty() )
128 {
129 const bool isFirstItemVisible = mModel->itemFromIndex( constSelectedIndexes[0] )->isVisible();
130
131 for ( const QModelIndex &index : constSelectedIndexes )
132 {
133 if ( QgsLayoutItem *item = mModel->itemFromIndex( index ) )
134 {
135 item->setVisibility( !isFirstItemVisible );
136 }
137 }
138 //return here, because otherwise it will just invert visibility for each item instead setting all the same
139 return;
140 }
141 }
142
143 QTreeView::keyPressEvent( event );
144}
145
146void QgsLayoutItemsListView::updateSelection()
147{
148 // Do nothing if we are currently updating the selection
149 // because user has selected/deselected some items in the
150 // graphics view
151 if ( !mModel || mUpdatingFromView )
152 return;
153
154 // Set the updating flag
155 mUpdatingSelection = true;
156
157 // Deselect all items from the layout (prevent firing signals, selection will be changed)
158 whileBlocking( mLayout )->deselectAll();
159
160 // Build the list of selected items
161 QList<QgsLayoutItem *> selectedItems;
162 for ( const QModelIndex &index : selectionModel()->selectedIndexes() )
163 {
164 if ( QgsLayoutItem *item = mModel->itemFromIndex( index ) )
165 {
166 selectedItems << item;
167 }
168 }
169
170
171 bool itemSelected = false;
172
173 // Check if the clicked item was selected or deselected
174 if ( selectionModel()->isSelected( selectionModel()->currentIndex() ) )
175 {
176 // It it was selected, set it as the layout's selected item;
177 // This will show the item properties
178 QgsLayoutItem *currentItem = mModel->itemFromIndex( selectionModel()->currentIndex() );
179 mLayout->setSelectedItem( currentItem );
180 itemSelected = true;
181 }
182 for ( QgsLayoutItem *item : selectedItems )
183 {
184 if ( !itemSelected )
185 {
186 // If clicked item was actually deselected, set the first selected item in the list
187 // as the layout's selected item
188 mLayout->setSelectedItem( item );
189 itemSelected = true;
190 }
191 else
192 {
193 item->setSelected( true );
194 }
195
196 // find top level group this item is contained within, and mark the group as selected
197 QgsLayoutItemGroup *group = item->parentGroup();
198 while ( group && group->parentGroup() )
199 {
200 group = group->parentGroup();
201 }
202 if ( group && group != item )
203 group->setSelected( true );
204
205 }
206 // Reset the updating flag
207 mUpdatingSelection = false;
208}
209
210void QgsLayoutItemsListView::onItemFocused( QgsLayoutItem *focusedItem )
211{
212 // Do nothing if we are in the middle of selecting items in the layoutView
213 if ( !mModel || mUpdatingSelection )
214 return;
215
216 // Set the updating flag
217 mUpdatingFromView = true;
218
219 // Deselect all items in list
220 clearSelection();
221
222 // Set the current index to the focused item
223 QModelIndex index = mModel->indexForItem( focusedItem );
224 if ( index.isValid() )
225 {
226 setCurrentIndex( index );
227 }
228
229 // Select rows in the item list for every selected items in the graphics view
230 const QList< QgsLayoutItem *> selectedItems = mLayout->selectedLayoutItems();
231 for ( QgsLayoutItem *item : selectedItems )
232 {
233 const QModelIndex firstCol = mModel->indexForItem( item );
234 if ( firstCol.isValid() )
235 {
236 // Select the whole row
237 QItemSelection selection;
238 selection.select( firstCol, firstCol.siblingAtColumn( mModel->columnCount( firstCol.parent() ) - 1 ) );
239 selectionModel()->select( selection, QItemSelectionModel::Select );
240 }
241 }
242 // Reset the updating flag
243 mUpdatingFromView = false;
244}
245
246void QgsLayoutItemsListView::showContextMenu( QPoint point )
247{
248 if ( !mModel )
249 return;
250 const QModelIndex index = indexAt( point );
251 QgsLayoutItem *item = mModel->itemFromIndex( index );
252 if ( !item )
253 return;
254
255 QMenu *menu = new QMenu( this );
256
257 QAction *copyAction = new QAction( tr( "Copy Item" ), menu );
258 connect( copyAction, &QAction::triggered, this, [this, item]()
259 {
260 mDesigner->view()->copyItems( QList< QgsLayoutItem * >() << item, QgsLayoutView::ClipboardCopy );
261 } );
262 menu->addAction( copyAction );
263 QAction *deleteAction = new QAction( tr( "Delete Item" ), menu );
264 connect( deleteAction, &QAction::triggered, this, [this, item]()
265 {
266 mDesigner->view()->deleteItems( QList< QgsLayoutItem * >() << item );
267 } );
268 menu->addAction( deleteAction );
269 menu->addSeparator();
270
271 QAction *itemPropertiesAction = new QAction( tr( "Item Properties…" ), menu );
272 connect( itemPropertiesAction, &QAction::triggered, this, [this, item]()
273 {
274 mDesigner->showItemOptions( item, true );
275 } );
276 menu->addAction( itemPropertiesAction );
277
278 menu->popup( mapToGlobal( point ) );
279}
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:4927
A common interface for layout designer dialogs and widgets.
virtual void showItemOptions(QgsLayoutItem *item, bool bringPanelToFront=true)=0
Shows the configuration widget for the specified layout item.
virtual QgsLayoutView * view()=0
Returns the layout view utilized by the designer.
A container for grouping several QgsLayoutItems.
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 void setVisibility(bool visible)
Sets whether the item is visible.
Model for the layout items list view.
QgsLayoutItem * itemFromIndex(const QModelIndex &index) const
Returns the layout item listed at the specified index.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
QgsLayoutItemsListViewModel(QgsLayoutModel *model, QObject *parent)
constructor
void setSelected(const QModelIndex &index)
Sets the selected index.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QModelIndex indexForItem(QgsLayoutItem *item, const int column=0) const
Returns the model index matching the specified layout item.
void setCurrentLayout(QgsLayout *layout)
Sets the current layout.
QgsLayoutItemsListView(QWidget *parent, QgsLayoutDesignerInterface *designer)
Constructor for QgsLayoutItemsListView.
void keyPressEvent(QKeyEvent *event) override
A model for items attached to a layout.
QgsLayoutItem * itemFromIndex(const QModelIndex &index) const
Returns the QgsLayoutItem corresponding to a QModelIndex index, if possible.
@ ItemId
Item ID.
QModelIndex indexForItem(QgsLayoutItem *item, int column=0)
Returns the QModelIndex corresponding to a QgsLayoutItem item and column, if possible.
void itemFocused(QgsLayoutItem *item)
Emitted when an item is "focused" in the view, i.e.
@ ClipboardCopy
Copy items.
Definition: qgslayoutview.h:72
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.
Definition: qgslayout.cpp:146
QList< QgsLayoutItem * > selectedLayoutItems(bool includeLockedItems=true)
Returns list of selected layout items.
Definition: qgslayout.cpp:151
void setSelectedItem(QgsLayoutItem *item)
Clears any selected items and sets item as the current selection.
Definition: qgslayout.cpp:168
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:5111