QGIS API Documentation 3.99.0-Master (a26b91b364d)
qgslayoutmapwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutmapwidget.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 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19#include "qgslayoutmapwidget.h"
20#include "moc_qgslayoutmapwidget.cpp"
22#include "qgslayoutitemmap.h"
23#include "qgsproject.h"
25#include "qgslayout.h"
26#include "qgslayertree.h"
27#include "qgsmapcanvas.h"
29#include "qgslayoutundostack.h"
30#include "qgslayoutatlas.h"
32#include "qgsguiutils.h"
33#include "qgsbookmarkmodel.h"
36#include "qgsmaplayermodel.h"
37#include "qgsfillsymbol.h"
38
39#include <QMenu>
40#include <QMessageBox>
41#include <QStringListModel>
42
44 : QgsLayoutItemBaseWidget( nullptr, item )
45 , mMapItem( item )
46 , mMapCanvas( mapCanvas )
47{
48 Q_ASSERT( mMapItem );
49
50 setupUi( this );
51 connect( mScaleLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mScaleLineEdit_editingFinished );
52 connect( mActionSetToCanvasExtent, &QAction::triggered, this, &QgsLayoutMapWidget::setToMapCanvasExtent );
53 connect( mActionViewExtentInCanvas, &QAction::triggered, this, &QgsLayoutMapWidget::viewExtentInCanvas );
54 connect( mActionSetToCanvasScale, &QAction::triggered, this, &QgsLayoutMapWidget::setToMapCanvasScale );
55 connect( mActionViewScaleInCanvas, &QAction::triggered, this, &QgsLayoutMapWidget::viewScaleInCanvas );
56 connect( mActionUpdatePreview, &QAction::triggered, this, &QgsLayoutMapWidget::updatePreview );
57 connect( mFollowVisibilityPresetCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mFollowVisibilityPresetCheckBox_stateChanged );
58 connect( mKeepLayerListCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mKeepLayerListCheckBox_stateChanged );
59 connect( mKeepLayerStylesCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mKeepLayerStylesCheckBox_stateChanged );
60 connect( mDrawCanvasItemsCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mDrawCanvasItemsCheckBox_stateChanged );
61 connect( mOverviewBlendModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::mOverviewBlendModeComboBox_currentIndexChanged );
62 connect( mOverviewInvertCheckbox, &QCheckBox::toggled, this, &QgsLayoutMapWidget::mOverviewInvertCheckbox_toggled );
63 connect( mOverviewCenterCheckbox, &QCheckBox::toggled, this, &QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled );
64 connect( mOverviewPositionComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::overviewStackingChanged );
65 connect( mOverviewStackingLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsLayoutMapWidget::overviewStackingLayerChanged );
66 connect( mXMinLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mXMinLineEdit_editingFinished );
67 connect( mXMaxLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mXMaxLineEdit_editingFinished );
68 connect( mYMinLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mYMinLineEdit_editingFinished );
69 connect( mYMaxLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mYMaxLineEdit_editingFinished );
70 connect( mAtlasMarginRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasMarginRadio_toggled );
71 connect( mAtlasCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mAtlasCheckBox_toggled );
72 connect( mAtlasMarginSpinBox, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &QgsLayoutMapWidget::mAtlasMarginSpinBox_valueChanged );
73 connect( mAtlasFixedScaleRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasFixedScaleRadio_toggled );
74 connect( mAtlasPredefinedScaleRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasPredefinedScaleRadio_toggled );
75 connect( mAddGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mAddGridPushButton_clicked );
76 connect( mRemoveGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mRemoveGridPushButton_clicked );
77 connect( mCopyGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mCopyGridPushButton_clicked );
78 connect( mGridUpButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridUpButton_clicked );
79 connect( mGridDownButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridDownButton_clicked );
80 connect( mGridListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mGridListWidget_currentItemChanged );
81 connect( mGridListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mGridListWidget_itemChanged );
82 connect( mGridPropertiesButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridPropertiesButton_clicked );
83 connect( mAddOverviewPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mAddOverviewPushButton_clicked );
84 connect( mRemoveOverviewPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mRemoveOverviewPushButton_clicked );
85 connect( mOverviewUpButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mOverviewUpButton_clicked );
86 connect( mOverviewDownButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mOverviewDownButton_clicked );
87 connect( mOverviewCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mOverviewCheckBox_toggled );
88 connect( mOverviewListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged );
89 connect( mOverviewListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_itemChanged );
90 connect( mActionLabelSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showLabelSettings );
91 connect( mActionClipSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showClipSettings );
92
93 connect( mTemporalCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mTemporalCheckBox_toggled );
94 connect( mStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsLayoutMapWidget::updateTemporalExtent );
95 connect( mEndDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsLayoutMapWidget::updateTemporalExtent );
96
97 mZLowerSpin->setClearValueMode( QgsDoubleSpinBox::ClearValueMode::MinimumValue, tr( "Not set" ) );
98 mZUpperSpin->setClearValueMode( QgsDoubleSpinBox::ClearValueMode::MinimumValue, tr( "Not set" ) );
99 connect( mElevationRangeCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mElevationRangeCheckBox_toggled );
100 connect( mZLowerSpin, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutMapWidget::updateZRange );
101 connect( mZUpperSpin, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutMapWidget::updateZRange );
102
103 mStartDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() );
104 mEndDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() );
105 mStartDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
106 mEndDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
107
108 connect( mActionMoveContent, &QAction::triggered, this, &QgsLayoutMapWidget::switchToMoveContentTool );
109 setPanelTitle( tr( "Map Properties" ) );
110 mMapRotationSpinBox->setClearValue( 0 );
111
112 mDockToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
113
114 mLayersMenu = new QMenu( this );
115 QToolButton *btnLayers = new QToolButton( this );
116 btnLayers->setAutoRaise( true );
117 btnLayers->setToolTip( tr( "Set Map Extent to Layer Extent" ) );
118 btnLayers->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLayers.svg" ) ) );
119 btnLayers->setPopupMode( QToolButton::InstantPopup );
120 btnLayers->setMenu( mLayersMenu );
121
122 mDockToolbar->insertWidget( mActionMoveContent, btnLayers );
123 connect( mLayersMenu, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowLayersMenu );
124
125 mBookmarkMenu = new QMenu( this );
126 QToolButton *btnBookmarks = new QToolButton( this );
127 btnBookmarks->setAutoRaise( true );
128 btnBookmarks->setToolTip( tr( "Set Map Extent to Bookmark Extent" ) );
129 btnBookmarks->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowBookmarks.svg" ) ) );
130 btnBookmarks->setPopupMode( QToolButton::InstantPopup );
131 btnBookmarks->setMenu( mBookmarkMenu );
132
133 mDockToolbar->insertWidget( mActionMoveContent, btnBookmarks );
134 connect( mBookmarkMenu, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowBookmarkMenu );
135
136 //add widget for general composer item properties
137 mItemPropertiesWidget = new QgsLayoutItemPropertiesWidget( this, item );
138 mainLayout->addWidget( mItemPropertiesWidget );
139
140 mScaleLineEdit->setValidator( new QDoubleValidator( mScaleLineEdit ) );
141
142 mXMinLineEdit->setValidator( new QDoubleValidator( mXMinLineEdit ) );
143 mXMaxLineEdit->setValidator( new QDoubleValidator( mXMaxLineEdit ) );
144 mYMinLineEdit->setValidator( new QDoubleValidator( mYMinLineEdit ) );
145 mYMaxLineEdit->setValidator( new QDoubleValidator( mYMaxLineEdit ) );
146
147 mOverviewPositionComboBox->addItem( tr( "Below Map" ), QgsLayoutItemMapItem::StackBelowMap );
148 mOverviewPositionComboBox->addItem( tr( "Below Map Layer" ), QgsLayoutItemMapItem::StackBelowMapLayer );
149 mOverviewPositionComboBox->addItem( tr( "Above Map Layer" ), QgsLayoutItemMapItem::StackAboveMapLayer );
150 mOverviewPositionComboBox->addItem( tr( "Below Map Labels" ), QgsLayoutItemMapItem::StackBelowMapLabels );
151 mOverviewPositionComboBox->addItem( tr( "Above Map Labels" ), QgsLayoutItemMapItem::StackAboveMapLabels );
152
153 blockAllSignals( true );
154
155 mCrsSelector->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
156 mCrsSelector->setNotSetText( tr( "Use Project CRS" ) );
157 mCrsSelector->setDialogTitle( tr( "Map Item CRS" ) );
158
159 mOverviewFrameStyleButton->setSymbolType( Qgis::SymbolType::Fill );
160
161 // follow preset combo
162 mFollowVisibilityPresetCombo->setModel( new QStringListModel( mFollowVisibilityPresetCombo ) );
163 connect( mFollowVisibilityPresetCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::followVisibilityPresetSelected );
164 connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsLayoutMapWidget::onMapThemesChanged );
165 onMapThemesChanged();
166
167 // keep layers from preset button
168 QMenu *menuKeepLayers = new QMenu( this );
169 mLayerListFromPresetButton->setMenu( menuKeepLayers );
170 mLayerListFromPresetButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowAllLayers.svg" ) ) );
171 mLayerListFromPresetButton->setToolTip( tr( "Set layer list from a map theme" ) );
172 connect( menuKeepLayers, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu );
173
174 connect( item, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
175
176 connect( &item->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutMapWidget::atlasLayerChanged );
177 if ( QgsLayoutAtlas *atlas = layoutAtlas() )
178 {
179 connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutMapWidget::compositionAtlasToggled );
180 compositionAtlasToggled( atlas->enabled() );
181 }
182
183 mOverviewFrameMapComboBox->setCurrentLayout( item->layout() );
184 mOverviewFrameMapComboBox->setItemType( QgsLayoutItemRegistry::LayoutMap );
185 mOverviewFrameStyleButton->registerExpressionContextGenerator( item );
186
187 connect( mOverviewFrameMapComboBox, &QgsLayoutItemComboBox::itemChanged, this, &QgsLayoutMapWidget::overviewMapChanged );
188 connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsLayoutMapWidget::mapCrsChanged );
189 connect( mOverviewFrameStyleButton, &QgsSymbolButton::changed, this, &QgsLayoutMapWidget::overviewSymbolChanged );
190
191 mOverviewFrameStyleButton->registerExpressionContextGenerator( item );
192 mOverviewFrameStyleButton->setLayer( coverageLayer() );
193 if ( item->layout() )
194 {
195 connect( &item->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, mOverviewFrameStyleButton, &QgsSymbolButton::setLayer );
196 }
197
198
213
214 updateGuiElements();
215 loadGridEntries();
216 loadOverviewEntries();
217
218 connect( mMapRotationSpinBox, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutMapWidget::rotationChanged );
219 connect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
221
222 blockAllSignals( false );
223}
224
226{
227 if ( mItemPropertiesWidget )
228 mItemPropertiesWidget->setMasterLayout( masterLayout );
229}
230
231void QgsLayoutMapWidget::setReportTypeString( const QString &string )
232{
233 mReportTypeString = string;
234 mAtlasCheckBox->setTitle( tr( "Controlled by %1" ).arg( string == tr( "atlas" ) ? tr( "Atlas" ) : tr( "Report" ) ) );
235 mAtlasPredefinedScaleRadio->setToolTip( tr( "Use one of the predefined scales of the project where the %1 feature best fits." ).arg( string ) );
236
237 if ( mClipWidget )
238 mClipWidget->setReportTypeString( string );
239 if ( mLabelWidget )
240 mLabelWidget->setReportTypeString( string );
241}
242
248
250{
251 if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
252 return false;
253
254 if ( mMapItem )
255 {
256 disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
257 disconnect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
258 disconnect( mMapItem, &QgsLayoutItemMap::mapRotationChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
259 }
260
261 mMapItem = qobject_cast<QgsLayoutItemMap *>( item );
262 mItemPropertiesWidget->setItem( mMapItem );
263 if ( mLabelWidget )
264 mLabelWidget->setItem( mMapItem );
265 if ( mClipWidget )
266 mClipWidget->setItem( mMapItem );
267
268 if ( mMapItem )
269 {
270 connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
271 connect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
273 mOverviewFrameStyleButton->registerExpressionContextGenerator( mMapItem );
274 }
275
276 updateGuiElements();
277
278 return true;
279}
280
282{
283 updateDataDefinedButton( mScaleDDBtn );
284 updateDataDefinedButton( mMapRotationDDBtn );
285 updateDataDefinedButton( mXMinDDBtn );
286 updateDataDefinedButton( mYMinDDBtn );
287 updateDataDefinedButton( mXMaxDDBtn );
288 updateDataDefinedButton( mYMaxDDBtn );
289 updateDataDefinedButton( mAtlasMarginDDBtn );
290 updateDataDefinedButton( mStylePresetsDDBtn );
291 updateDataDefinedButton( mLayersDDBtn );
292 updateDataDefinedButton( mCRSDDBtn );
293 updateDataDefinedButton( mStartDateTimeDDBtn );
294 updateDataDefinedButton( mEndDateTimeDDBtn );
295}
296
297void QgsLayoutMapWidget::compositionAtlasToggled( bool atlasEnabled )
298{
299 if ( atlasEnabled && mMapItem && mMapItem->layout() && mMapItem->layout()->reportContext().layer()
300 && mMapItem->layout()->reportContext().layer()->wkbType() != Qgis::WkbType::NoGeometry )
301 {
302 mAtlasCheckBox->setEnabled( true );
303 }
304 else
305 {
306 mAtlasCheckBox->setEnabled( false );
307 mAtlasCheckBox->setChecked( false );
308 }
309}
310
311void QgsLayoutMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu()
312{
313 // this menu is for the case when setting "keep layers" and "keep layer styles"
314 // and the preset configuration is copied. The preset is not followed further.
315
316 QMenu *menu = qobject_cast<QMenu *>( sender() );
317 if ( !menu )
318 return;
319
320 menu->clear();
321 const auto constMapThemes = QgsProject::instance()->mapThemeCollection()->mapThemes();
322 for ( const QString &presetName : constMapThemes )
323 {
324 menu->addAction( presetName, this, &QgsLayoutMapWidget::keepLayersVisibilityPresetSelected );
325 }
326
327 if ( menu->actions().isEmpty() )
328 menu->addAction( tr( "No presets defined" ) )->setEnabled( false );
329}
330
331void QgsLayoutMapWidget::followVisibilityPresetSelected( int currentIndex )
332{
333 if ( !mMapItem )
334 return;
335
336 if ( mBlockThemeComboChanges != 0 )
337 return;
338
339 if ( currentIndex == -1 )
340 return; // doing combo box model reset
341
342 QString presetName;
343 if ( currentIndex != 0 )
344 {
345 presetName = mFollowVisibilityPresetCombo->currentText();
346 }
347
348 if ( presetName == mMapItem->followVisibilityPresetName() )
349 return;
350
351 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
352 mFollowVisibilityPresetCheckBox->setChecked( true );
353 mMapItem->setFollowVisibilityPresetName( presetName );
354 mMapItem->layout()->undoStack()->endCommand();
355
356 mMapItem->invalidateCache();
357}
358
359void QgsLayoutMapWidget::keepLayersVisibilityPresetSelected()
360{
361 QAction *action = qobject_cast<QAction *>( sender() );
362 if ( !action )
363 return;
364
365 const QString presetName = action->text();
366 const QList<QgsMapLayer *> lst = orderedPresetVisibleLayers( presetName );
367 if ( mMapItem )
368 {
369 mKeepLayerListCheckBox->setChecked( true );
370 mMapItem->setLayers( lst );
371
372 mKeepLayerStylesCheckBox->setChecked( true );
373
374 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
375 mMapItem->setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( presetName ) );
376 mMapItem->layout()->undoStack()->endCommand();
377
378 mMapItem->invalidateCache();
379 }
380}
381
382void QgsLayoutMapWidget::onMapThemesChanged()
383{
384 if ( QStringListModel *model = qobject_cast<QStringListModel *>( mFollowVisibilityPresetCombo->model() ) )
385 {
386 mBlockThemeComboChanges++;
387 QStringList lst;
388 lst.append( tr( "(none)" ) );
390 model->setStringList( lst );
391
392 // select the previously selected item again
393 const int presetModelIndex = mFollowVisibilityPresetCombo->findText( mMapItem->followVisibilityPresetName() );
394 mFollowVisibilityPresetCombo->blockSignals( true );
395 mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none
396 mFollowVisibilityPresetCombo->blockSignals( false );
397 mBlockThemeComboChanges--;
398 }
399}
400
401void QgsLayoutMapWidget::mapCrsChanged( const QgsCoordinateReferenceSystem &crs )
402{
403 if ( !mMapItem )
404 {
405 return;
406 }
407
408 if ( mMapItem->presetCrs() == crs )
409 return;
410
411 // try to reproject to maintain extent
412 const QgsCoordinateReferenceSystem oldCrs = mMapItem->crs();
413
414 bool updateExtent = false;
415 QgsRectangle newExtent;
416 try
417 {
418 QgsCoordinateTransform xForm( oldCrs, crs.isValid() ? crs : QgsProject::instance()->crs(), QgsProject::instance() );
420 const QgsRectangle prevExtent = mMapItem->extent();
421 newExtent = xForm.transformBoundingBox( prevExtent );
422 updateExtent = true;
423 }
424 catch ( QgsCsException & )
425 {
426 //transform failed, don't update extent
427 }
428
429 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map CRS" ) );
430 mMapItem->setCrs( crs );
431 if ( updateExtent )
432 mMapItem->zoomToExtent( newExtent );
433 mMapItem->layout()->undoStack()->endCommand();
434 mMapItem->invalidateCache();
435}
436
437void QgsLayoutMapWidget::overviewSymbolChanged()
438{
439 QgsLayoutItemMapOverview *overview = currentOverview();
440 if ( !overview )
441 return;
442
443 mMapItem->beginCommand( tr( "Change Overview Style" ), QgsLayoutItem::UndoOverviewStyle );
444 overview->setFrameSymbol( mOverviewFrameStyleButton->clonedSymbol<QgsFillSymbol>() );
445 mMapItem->endCommand();
446 mMapItem->invalidateCache();
447}
448
449void QgsLayoutMapWidget::showLabelSettings()
450{
451 mLabelWidget = new QgsLayoutMapLabelingWidget( mMapItem );
452
453 if ( !mReportTypeString.isEmpty() )
454 mLabelWidget->setReportTypeString( mReportTypeString );
455
456 openPanel( mLabelWidget );
457}
458
459void QgsLayoutMapWidget::showClipSettings()
460{
461 mClipWidget = new QgsLayoutMapClippingWidget( mMapItem );
462 if ( !mReportTypeString.isEmpty() )
463 mClipWidget->setReportTypeString( mReportTypeString );
464 openPanel( mClipWidget );
465}
466
467void QgsLayoutMapWidget::switchToMoveContentTool()
468{
469 if ( mInterface )
471}
472
473void QgsLayoutMapWidget::aboutToShowLayersMenu()
474{
475 mLayersMenu->clear();
476
477 if ( !mMapLayerModel )
478 {
479 mMapLayerModel = new QgsMapLayerProxyModel( this );
481 }
482
483 if ( mMapLayerModel->rowCount() == 0 )
484 {
485 QAction *action = new QAction( tr( "No spatial layers available" ) );
486 action->setEnabled( false );
487 mLayersMenu->addAction( action );
488 return;
489 }
490
491 for ( int i = 0; i < mMapLayerModel->rowCount(); ++i )
492 {
493 const QModelIndex index = mMapLayerModel->index( i, 0 );
494 const QIcon icon = qvariant_cast<QIcon>( mMapLayerModel->data( index, Qt::DecorationRole ) );
495 const QString text = mMapLayerModel->data( index, Qt::DisplayRole ).toString();
496 const QString tooltip = mMapLayerModel->data( index, Qt::ToolTipRole ).toString();
497 const QString layerId = mMapLayerModel->data( index, static_cast<int>( QgsMapLayerModel::CustomRole::LayerId ) ).toString();
498
499 QAction *action = new QAction( icon, text, mLayersMenu );
500 action->setToolTip( tooltip );
501 connect( action, &QAction::triggered, this, [this, layerId] {
502 if ( QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerId ) )
503 {
504 setToCustomExtent( QgsReferencedRectangle( layer->extent(), layer->crs() ) );
505 }
506 } );
507 mLayersMenu->addAction( action );
508 }
509}
510
511void QgsLayoutMapWidget::aboutToShowBookmarkMenu()
512{
513 mBookmarkMenu->clear();
514
515 // query the bookmarks now? or once during widget creation... Hmm. Either way, there's potentially a
516 // delay if there's LOTS of bookmarks. Let's avoid the cost until bookmarks are actually required.
517 if ( !mBookmarkModel )
518 {
519 mBookmarkModel = new QgsBookmarkManagerProxyModel( QgsApplication::bookmarkManager(), QgsProject::instance()->bookmarkManager(), this );
520 }
521
522 if ( mBookmarkModel->rowCount() == 0 )
523 {
524 QAction *action = new QAction( tr( "No bookmarks available" ) );
525 action->setEnabled( false );
526 mBookmarkMenu->addAction( action );
527 return;
528 }
529
530 QMap<QString, QMenu *> groupMenus;
531 for ( int i = 0; i < mBookmarkModel->rowCount(); ++i )
532 {
533 const QString group = mBookmarkModel->data( mBookmarkModel->index( i, 0 ), static_cast<int>( QgsBookmarkManagerModel::CustomRole::Group ) ).toString();
534 QMenu *destMenu = mBookmarkMenu;
535 if ( !group.isEmpty() )
536 {
537 destMenu = groupMenus.value( group );
538 if ( !destMenu )
539 {
540 destMenu = new QMenu( group, mBookmarkMenu );
541 groupMenus[group] = destMenu;
542 }
543 }
544 QAction *action = new QAction( mBookmarkModel->data( mBookmarkModel->index( i, 0 ), static_cast<int>( QgsBookmarkManagerModel::CustomRole::Name ) ).toString(), mBookmarkMenu );
545 const QgsReferencedRectangle extent = mBookmarkModel->data( mBookmarkModel->index( i, 0 ), static_cast<int>( QgsBookmarkManagerModel::CustomRole::Extent ) ).value<QgsReferencedRectangle>();
546 connect( action, &QAction::triggered, this, [this, extent] {
547 setToCustomExtent( extent );
548 } );
549 destMenu->addAction( action );
550 }
551
552 QStringList groupKeys = groupMenus.keys();
553 groupKeys.sort( Qt::CaseInsensitive );
554 for ( int i = 0; i < groupKeys.count(); ++i )
555 {
556 if ( mBookmarkMenu->actions().value( i ) )
557 mBookmarkMenu->insertMenu( mBookmarkMenu->actions().at( i ), groupMenus.value( groupKeys.at( i ) ) );
558 else
559 mBookmarkMenu->addMenu( groupMenus.value( groupKeys.at( i ) ) );
560 }
561}
562
563void QgsLayoutMapWidget::setToCustomExtent( const QgsReferencedRectangle &referencedExtent )
564{
565 if ( !mMapItem || referencedExtent.isEmpty() )
566 {
567 return;
568 }
569
570 QgsRectangle extent = referencedExtent;
571 if ( referencedExtent.crs() != mMapItem->crs() )
572 {
573 try
574 {
575 QgsCoordinateTransform coordinateTransform( referencedExtent.crs(), mMapItem->crs(), QgsProject::instance() );
576 coordinateTransform.setBallparkTransformsAreAppropriate( true );
577 extent = coordinateTransform.transformBoundingBox( extent );
578 }
579 catch ( QgsCsException & )
580 {
581 //transform failed, better not proceed
582 return;
583 }
584 }
585
586 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
587 mMapItem->zoomToExtent( extent );
588 mMapItem->layout()->undoStack()->endCommand();
589}
590
591void QgsLayoutMapWidget::mTemporalCheckBox_toggled( bool checked )
592{
593 if ( !mMapItem )
594 {
595 return;
596 }
597
598 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Temporal Range" ) );
599 mMapItem->setIsTemporal( checked );
600 mMapItem->layout()->undoStack()->endCommand();
601
602 if ( checked )
603 {
604 whileBlocking( mStartDateTime )->setDateTime( mMapItem->temporalRange().begin() );
605 whileBlocking( mEndDateTime )->setDateTime( mMapItem->temporalRange().end() );
606 }
607
608 updatePreview();
609}
610
611void QgsLayoutMapWidget::updateTemporalExtent()
612{
613 if ( !mMapItem )
614 {
615 return;
616 }
617
618 const QDateTime begin = mStartDateTime->dateTime();
619 const QDateTime end = mEndDateTime->dateTime();
620 const QgsDateTimeRange range = QgsDateTimeRange( begin, end, true, begin == end );
621
622 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Temporal Range" ) );
623 mMapItem->setTemporalRange( range );
624 mMapItem->layout()->undoStack()->endCommand();
625
626 updatePreview();
627}
628
629void QgsLayoutMapWidget::mElevationRangeCheckBox_toggled( bool checked )
630{
631 if ( !mMapItem )
632 {
633 return;
634 }
635
636 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Z Range" ) );
637 mMapItem->setZRangeEnabled( checked );
638 mMapItem->layout()->undoStack()->endCommand();
639
640 updatePreview();
641}
642
643void QgsLayoutMapWidget::updateZRange()
644{
645 if ( !mMapItem )
646 {
647 return;
648 }
649
650 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Z Range" ) );
651 double zLower = mZLowerSpin->value();
652 if ( zLower == mZLowerSpin->clearValue() )
653 zLower = std::numeric_limits<double>::lowest();
654 double zUpper = mZUpperSpin->value();
655 if ( zUpper == mZUpperSpin->clearValue() )
656 zUpper = std::numeric_limits<double>::max();
657
658 mMapItem->setZRange( QgsDoubleRange( zLower, zUpper ) );
659 mMapItem->layout()->undoStack()->endCommand();
660
661 updatePreview();
662}
663
664void QgsLayoutMapWidget::mAtlasCheckBox_toggled( bool checked )
665{
666 if ( !mMapItem )
667 {
668 return;
669 }
670
671 mAtlasFixedScaleRadio->setEnabled( checked );
672 mAtlasMarginRadio->setEnabled( checked );
673
674 if ( mAtlasMarginRadio->isEnabled() && mAtlasMarginRadio->isChecked() )
675 {
676 mAtlasMarginSpinBox->setEnabled( true );
677 }
678 else
679 {
680 mAtlasMarginSpinBox->setEnabled( false );
681 }
682
683 mAtlasPredefinedScaleRadio->setEnabled( checked );
684
685 if ( checked )
686 {
687 //check atlas coverage layer type
688 if ( mMapItem->layout() )
689 {
690 toggleAtlasScalingOptionsByLayerType();
691 }
692 }
693
694 // disable predefined scales if none are defined
695 if ( !hasPredefinedScales() )
696 {
697 mAtlasPredefinedScaleRadio->setEnabled( false );
698 }
699
700 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Atlas Driven" ) );
701 mMapItem->setAtlasDriven( checked );
702 mMapItem->layout()->undoStack()->endCommand();
703 updateMapForAtlas();
704}
705
706void QgsLayoutMapWidget::updateMapForAtlas()
707{
708 //update map if in atlas preview mode
709 if ( mMapItem->atlasDriven() )
710 {
711 mMapItem->refresh();
712 }
713 else
714 {
715 //redraw map
716 mMapItem->invalidateCache();
717 }
718}
719
720void QgsLayoutMapWidget::mAtlasMarginRadio_toggled( bool checked )
721{
722 mAtlasMarginSpinBox->setEnabled( checked );
723
724 if ( checked && mMapItem )
725 {
726 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
727 mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Auto );
728 mMapItem->layout()->undoStack()->endCommand();
729 updateMapForAtlas();
730 }
731}
732
733void QgsLayoutMapWidget::mAtlasMarginSpinBox_valueChanged( int value )
734{
735 if ( !mMapItem )
736 {
737 return;
738 }
739
740 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Margin" ), QgsLayoutItem::UndoAtlasMargin );
741 mMapItem->setAtlasMargin( value / 100. );
742 mMapItem->layout()->undoStack()->endCommand();
743 updateMapForAtlas();
744}
745
746void QgsLayoutMapWidget::mAtlasFixedScaleRadio_toggled( bool checked )
747{
748 if ( !mMapItem )
749 {
750 return;
751 }
752
753 if ( checked )
754 {
755 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
756 mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Fixed );
757 mMapItem->layout()->undoStack()->endCommand();
758 updateMapForAtlas();
759 }
760}
761
762void QgsLayoutMapWidget::mAtlasPredefinedScaleRadio_toggled( bool checked )
763{
764 if ( !mMapItem )
765 {
766 return;
767 }
768
769 if ( hasPredefinedScales() )
770 {
771 if ( checked )
772 {
773 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Scales" ) );
774 mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Predefined );
775 mMapItem->layout()->undoStack()->endCommand();
776 updateMapForAtlas();
777 }
778 }
779 else
780 {
781 // restore to fixed scale if no predefined scales exist
782 whileBlocking( mAtlasFixedScaleRadio )->setChecked( true );
783 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
784 mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Fixed );
785 mMapItem->layout()->undoStack()->endCommand();
786 }
787}
788
789void QgsLayoutMapWidget::mScaleLineEdit_editingFinished()
790{
791 if ( !mMapItem )
792 {
793 return;
794 }
795
796 bool conversionSuccess = false;
797 const double scaleDenominator = QLocale().toDouble( mScaleLineEdit->text(), &conversionSuccess );
798 if ( !conversionSuccess )
799 {
800 return;
801 }
802
803 if ( qgsDoubleNear( scaleDenominator, mMapItem->scale() ) )
804 return;
805
806 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Scale" ) );
807 mMapItem->setScale( scaleDenominator );
808 mMapItem->layout()->undoStack()->endCommand();
809}
810
811void QgsLayoutMapWidget::rotationChanged( double value )
812{
813 if ( !mMapItem )
814 {
815 return;
816 }
817
818 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Rotation" ), QgsLayoutItem::UndoMapRotation );
819 mMapItem->setMapRotation( value );
820 mMapItem->layout()->undoStack()->endCommand();
821 mMapItem->invalidateCache();
822}
823
824void QgsLayoutMapWidget::setToMapCanvasExtent()
825{
826 if ( !mMapItem )
827 {
828 return;
829 }
830
831 QgsRectangle newExtent = mMapCanvas->mapSettings().visibleExtent();
832
833 //transform?
834 if ( mMapCanvas->mapSettings().destinationCrs()
835 != mMapItem->crs() )
836 {
837 try
838 {
839 QgsCoordinateTransform xForm( mMapCanvas->mapSettings().destinationCrs(), mMapItem->crs(), QgsProject::instance() );
840 xForm.setBallparkTransformsAreAppropriate( true );
841 newExtent = xForm.transformBoundingBox( newExtent );
842 }
843 catch ( QgsCsException & )
844 {
845 //transform failed, better not proceed
846 return;
847 }
848 }
849
850 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
851 mMapItem->zoomToExtent( newExtent );
852 mMapItem->layout()->undoStack()->endCommand();
853}
854
855void QgsLayoutMapWidget::setToMapCanvasScale()
856{
857 if ( !mMapItem )
858 {
859 return;
860 }
861
862 const double newScale = mMapCanvas->scale();
863
864 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Scale" ) );
865 mMapItem->setScale( newScale );
866 mMapItem->layout()->undoStack()->endCommand();
867}
868
869void QgsLayoutMapWidget::viewExtentInCanvas()
870{
871 if ( !mMapItem )
872 {
873 return;
874 }
875
876 const QgsRectangle currentMapExtent = mMapItem->extent();
877
878 if ( !currentMapExtent.isEmpty() )
879 {
880 try
881 {
882 mMapCanvas->setReferencedExtent( QgsReferencedRectangle( currentMapExtent, mMapItem->crs() ) );
883 }
884 catch ( QgsCsException & )
885 {
886 //transform failed, better not proceed
887 return;
888 }
889 mMapCanvas->refresh();
890 }
891}
892
893void QgsLayoutMapWidget::viewScaleInCanvas()
894{
895 if ( !mMapItem )
896 {
897 return;
898 }
899
900 const double currentScale = mMapItem->scale();
901 mMapCanvas->zoomScale( currentScale, true );
902}
903
904void QgsLayoutMapWidget::mXMinLineEdit_editingFinished()
905{
906 updateComposerExtentFromGui();
907}
908
909void QgsLayoutMapWidget::mXMaxLineEdit_editingFinished()
910{
911 updateComposerExtentFromGui();
912}
913
914void QgsLayoutMapWidget::mYMinLineEdit_editingFinished()
915{
916 updateComposerExtentFromGui();
917}
918
919void QgsLayoutMapWidget::mYMaxLineEdit_editingFinished()
920{
921 updateComposerExtentFromGui();
922}
923
924void QgsLayoutMapWidget::updateGuiElements()
925{
926 if ( !mMapItem )
927 {
928 return;
929 }
930
931 blockAllSignals( true );
932 mLabel->setText( mMapItem->displayName() );
933
934 whileBlocking( mCrsSelector )->setCrs( mMapItem->presetCrs() );
935
936 //width, height, scale
937 const double scale = mMapItem->scale();
938
939 //round scale to an appropriate number of decimal places
940 if ( scale >= 10000 )
941 {
942 //round scale to integer if it's greater than 10000
943 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 0 ) );
944 }
945 else if ( scale >= 10 )
946 {
947 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 3 ) );
948 }
949 else if ( scale >= 1 )
950 {
951 //don't round scale if it's less than 10, instead use 4 decimal places
952 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 4 ) );
953 }
954 else
955 {
956 //if scale < 1 then use 10 decimal places
957 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 10 ) );
958 }
959
960 //composer map extent
961 const QgsRectangle composerMapExtent = mMapItem->extent();
962 mXMinLineEdit->setText( QLocale().toString( composerMapExtent.xMinimum(), 'f', 3 ) );
963 mXMaxLineEdit->setText( QLocale().toString( composerMapExtent.xMaximum(), 'f', 3 ) );
964 mYMinLineEdit->setText( QLocale().toString( composerMapExtent.yMinimum(), 'f', 3 ) );
965 mYMaxLineEdit->setText( QLocale().toString( composerMapExtent.yMaximum(), 'f', 3 ) );
966
967 mMapRotationSpinBox->setValue( mMapItem->mapRotation( QgsLayoutObject::OriginalValue ) );
968
969 // follow preset checkbox
970 mFollowVisibilityPresetCheckBox->setCheckState(
971 mMapItem->followVisibilityPreset() ? Qt::Checked : Qt::Unchecked
972 );
973 const int presetModelIndex = mFollowVisibilityPresetCombo->findText( mMapItem->followVisibilityPresetName() );
974 mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none
975
976 //keep layer list checkbox
977 if ( mMapItem->keepLayerSet() )
978 {
979 mKeepLayerListCheckBox->setCheckState( Qt::Checked );
980 }
981 else
982 {
983 mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
984 }
985
986 mKeepLayerStylesCheckBox->setEnabled( mMapItem->keepLayerSet() );
987 mKeepLayerStylesCheckBox->setCheckState( mMapItem->keepLayerStyles() ? Qt::Checked : Qt::Unchecked );
988
989 //draw canvas items
990 if ( mMapItem->drawAnnotations() )
991 {
992 mDrawCanvasItemsCheckBox->setCheckState( Qt::Checked );
993 }
994 else
995 {
996 mDrawCanvasItemsCheckBox->setCheckState( Qt::Unchecked );
997 }
998
999 //atlas controls
1000 mAtlasCheckBox->setChecked( mMapItem->atlasDriven() );
1001 mAtlasMarginSpinBox->setValue( static_cast<int>( mMapItem->atlasMargin( QgsLayoutObject::OriginalValue ) * 100 ) );
1002
1003 mAtlasFixedScaleRadio->setEnabled( mMapItem->atlasDriven() );
1004 mAtlasFixedScaleRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Fixed );
1005 mAtlasMarginSpinBox->setEnabled( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto );
1006 mAtlasMarginRadio->setEnabled( mMapItem->atlasDriven() );
1007 mAtlasMarginRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto );
1008 mAtlasPredefinedScaleRadio->setEnabled( mMapItem->atlasDriven() );
1009 mAtlasPredefinedScaleRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Predefined );
1010
1011 if ( mMapItem->atlasDriven() )
1012 {
1013 toggleAtlasScalingOptionsByLayerType();
1014 }
1015 // disable predefined scales if none are defined
1016 if ( !hasPredefinedScales() )
1017 {
1018 mAtlasPredefinedScaleRadio->setEnabled( false );
1019 }
1020
1021 mTemporalCheckBox->setChecked( mMapItem->isTemporal() );
1022 mTemporalCheckBox->setCollapsed( !mMapItem->isTemporal() );
1023 mStartDateTime->setEnabled( mMapItem->isTemporal() );
1024 mEndDateTime->setEnabled( mMapItem->isTemporal() );
1025 if ( mMapItem->isTemporal() )
1026 {
1027 mStartDateTime->setDateTime( mMapItem->temporalRange().begin() );
1028 mEndDateTime->setDateTime( mMapItem->temporalRange().end() );
1029 }
1030
1031 whileBlocking( mElevationRangeCheckBox )->setChecked( mMapItem->zRangeEnabled() );
1032 mElevationRangeCheckBox->setCollapsed( !mMapItem->zRangeEnabled() );
1033 if ( mMapItem->zRange().lower() != std::numeric_limits<double>::lowest() )
1034 whileBlocking( mZLowerSpin )->setValue( mMapItem->zRange().lower() );
1035 else
1036 whileBlocking( mZLowerSpin )->clear();
1037 if ( mMapItem->zRange().upper() != std::numeric_limits<double>::max() )
1038 whileBlocking( mZUpperSpin )->setValue( mMapItem->zRange().upper() );
1039 else
1040 whileBlocking( mZUpperSpin )->clear();
1041
1043 loadGridEntries();
1044 loadOverviewEntries();
1045
1046 blockAllSignals( false );
1047}
1048
1049void QgsLayoutMapWidget::toggleAtlasScalingOptionsByLayerType()
1050{
1051 if ( !mMapItem )
1052 {
1053 return;
1054 }
1055
1056 //get atlas coverage layer
1057 QgsVectorLayer *layer = coverageLayer();
1058 if ( !layer )
1059 {
1060 return;
1061 }
1062
1064 {
1065 //For point layers buffer setting makes no sense, so set "fixed scale" on and disable margin control
1066 if ( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto )
1067 mAtlasFixedScaleRadio->setChecked( true );
1068 mAtlasMarginRadio->setEnabled( false );
1069 }
1070 else
1071 {
1072 //Not a point layer, so enable changes to fixed scale control
1073 mAtlasMarginRadio->setEnabled( true );
1074 }
1075}
1076
1077void QgsLayoutMapWidget::updateComposerExtentFromGui()
1078{
1079 if ( !mMapItem )
1080 {
1081 return;
1082 }
1083
1084 double xmin, ymin, xmax, ymax;
1085 bool conversionSuccess;
1086
1087 xmin = QLocale().toDouble( mXMinLineEdit->text(), &conversionSuccess );
1088 if ( !conversionSuccess )
1089 return;
1090 xmax = QLocale().toDouble( mXMaxLineEdit->text(), &conversionSuccess );
1091 if ( !conversionSuccess )
1092 return;
1093 ymin = QLocale().toDouble( mYMinLineEdit->text(), &conversionSuccess );
1094 if ( !conversionSuccess )
1095 return;
1096 ymax = QLocale().toDouble( mYMaxLineEdit->text(), &conversionSuccess );
1097 if ( !conversionSuccess )
1098 return;
1099
1100 const QgsRectangle newExtent( xmin, ymin, xmax, ymax );
1101
1102 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
1103 mMapItem->setExtent( newExtent );
1104 mMapItem->layout()->undoStack()->endCommand();
1105}
1106
1107void QgsLayoutMapWidget::blockAllSignals( bool b )
1108{
1109 mScaleLineEdit->blockSignals( b );
1110 mXMinLineEdit->blockSignals( b );
1111 mXMaxLineEdit->blockSignals( b );
1112 mYMinLineEdit->blockSignals( b );
1113 mYMaxLineEdit->blockSignals( b );
1114 mDrawCanvasItemsCheckBox->blockSignals( b );
1115 mOverviewFrameMapComboBox->blockSignals( b );
1116 mOverviewFrameStyleButton->blockSignals( b );
1117 mOverviewBlendModeComboBox->blockSignals( b );
1118 mOverviewInvertCheckbox->blockSignals( b );
1119 mOverviewCenterCheckbox->blockSignals( b );
1120 mAtlasCheckBox->blockSignals( b );
1121 mAtlasMarginSpinBox->blockSignals( b );
1122 mAtlasFixedScaleRadio->blockSignals( b );
1123 mAtlasMarginRadio->blockSignals( b );
1124 mFollowVisibilityPresetCheckBox->blockSignals( b );
1125 mFollowVisibilityPresetCombo->blockSignals( b );
1126 mKeepLayerListCheckBox->blockSignals( b );
1127 mKeepLayerStylesCheckBox->blockSignals( b );
1128 mActionSetToCanvasExtent->blockSignals( b );
1129 mActionUpdatePreview->blockSignals( b );
1130 mTemporalCheckBox->blockSignals( b );
1131 mStartDateTime->blockSignals( b );
1132 mEndDateTime->blockSignals( b );
1133
1134 blockOverviewItemsSignals( b );
1135}
1136
1137void QgsLayoutMapWidget::updatePreview()
1138{
1139 if ( !mMapItem )
1140 {
1141 return;
1142 }
1143 mMapItem->refresh();
1144}
1145
1146void QgsLayoutMapWidget::mFollowVisibilityPresetCheckBox_stateChanged( int state )
1147{
1148 if ( !mMapItem )
1149 {
1150 return;
1151 }
1152
1153 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
1154 if ( state == Qt::Checked )
1155 {
1156 mMapItem->setFollowVisibilityPreset( true );
1157
1158 // mutually exclusive with keeping custom layer list
1159 mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
1160 mKeepLayerStylesCheckBox->setCheckState( Qt::Unchecked );
1161
1162 mMapItem->invalidateCache();
1163 }
1164 else
1165 {
1166 mMapItem->setFollowVisibilityPreset( false );
1167 }
1168 mMapItem->layout()->undoStack()->endCommand();
1169}
1170
1171void QgsLayoutMapWidget::mKeepLayerListCheckBox_stateChanged( int state )
1172{
1173 if ( !mMapItem )
1174 {
1175 return;
1176 }
1177
1178 // update map
1179 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Map Preset Changed" ) );
1180 storeCurrentLayerSet();
1181 mMapItem->setKeepLayerSet( state == Qt::Checked );
1182 if ( state == Qt::Unchecked )
1183 {
1184 mMapItem->setLayers( QList<QgsMapLayer *>() );
1185 }
1186 mMapItem->layout()->undoStack()->endCommand();
1187
1188 // update gui
1189 if ( state == Qt::Checked )
1190 {
1191 // mutually exclusive with following a preset
1192 mFollowVisibilityPresetCheckBox->setCheckState( Qt::Unchecked );
1193 }
1194 else
1195 {
1196 mKeepLayerStylesCheckBox->setChecked( Qt::Unchecked );
1197 mMapItem->invalidateCache();
1198 }
1199
1200 mKeepLayerStylesCheckBox->setEnabled( state == Qt::Checked );
1201}
1202
1203void QgsLayoutMapWidget::mKeepLayerStylesCheckBox_stateChanged( int state )
1204{
1205 if ( !mMapItem )
1206 {
1207 return;
1208 }
1209
1210 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
1211 if ( state == Qt::Checked )
1212 {
1213 mMapItem->storeCurrentLayerStyles();
1214 mMapItem->setKeepLayerStyles( true );
1215 }
1216 else
1217 {
1218 mMapItem->setLayerStyleOverrides( QMap<QString, QString>() );
1219 mMapItem->setKeepLayerStyles( false );
1220 }
1221 mMapItem->layout()->undoStack()->endCommand();
1222}
1223
1224void QgsLayoutMapWidget::mDrawCanvasItemsCheckBox_stateChanged( int state )
1225{
1226 if ( !mMapItem )
1227 {
1228 return;
1229 }
1230
1231 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Map Item" ) );
1232 mMapItem->setDrawAnnotations( state == Qt::Checked );
1233 mMapItem->invalidateCache();
1234 mMapItem->layout()->undoStack()->endCommand();
1235}
1236
1237void QgsLayoutMapWidget::atlasLayerChanged( QgsVectorLayer *layer )
1238{
1239 if ( !layer || layer->wkbType() == Qgis::WkbType::NoGeometry )
1240 {
1241 //geometryless layer, disable atlas control
1242 mAtlasCheckBox->setChecked( false );
1243 mAtlasCheckBox->setEnabled( false );
1244 return;
1245 }
1246 else
1247 {
1248 mAtlasCheckBox->setEnabled( true );
1249 }
1250
1251 // enable or disable fixed scale control based on layer type
1252 if ( mAtlasCheckBox->isChecked() )
1253 toggleAtlasScalingOptionsByLayerType();
1254}
1255
1256bool QgsLayoutMapWidget::hasPredefinedScales() const
1257{
1258 // first look at project's scales
1259 const QVector<double> scales( QgsProject::instance()->viewSettings()->mapScales() );
1260 const bool hasProjectScales( QgsProject::instance()->viewSettings()->useProjectScales() );
1261 if ( !hasProjectScales || scales.isEmpty() )
1262 {
1263 // default to global map tool scales
1264 const QgsSettings settings;
1265 const QStringList scales = QgsSettingsRegistryCore::settingsMapScales->value();
1266 return !scales.isEmpty() && !scales[0].isEmpty();
1267 }
1268 return true;
1269}
1270
1271void QgsLayoutMapWidget::mAddGridPushButton_clicked()
1272{
1273 if ( !mMapItem )
1274 {
1275 return;
1276 }
1277
1278 const QString itemName = tr( "Grid %1" ).arg( mMapItem->grids()->size() + 1 );
1279 QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( itemName, mMapItem );
1280 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Add Map Grid" ) );
1281 mMapItem->grids()->addGrid( grid );
1282 mMapItem->layout()->undoStack()->endCommand();
1283 mMapItem->updateBoundingRect();
1284 mMapItem->update();
1285
1286 addGridListItem( grid->id(), grid->name() );
1287 mGridListWidget->setCurrentRow( 0 );
1288 mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr );
1289}
1290
1291void QgsLayoutMapWidget::mRemoveGridPushButton_clicked()
1292{
1293 QListWidgetItem *item = mGridListWidget->currentItem();
1294 if ( !item )
1295 {
1296 return;
1297 }
1298
1299 mMapItem->beginCommand( tr( "Remove Grid" ) );
1300 mMapItem->grids()->removeGrid( item->data( Qt::UserRole ).toString() );
1301 QListWidgetItem *delItem = mGridListWidget->takeItem( mGridListWidget->row( item ) );
1302 delete delItem;
1303 mMapItem->endCommand();
1304 mMapItem->updateBoundingRect();
1305 mMapItem->update();
1306}
1307
1308void QgsLayoutMapWidget::mCopyGridPushButton_clicked()
1309{
1310 QListWidgetItem *item = mGridListWidget->currentItem();
1311 if ( !item )
1312 {
1313 return;
1314 }
1315
1316 QgsLayoutItemMapGrid *sourceGrid = mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() );
1317 if ( !sourceGrid )
1318 {
1319 return;
1320 }
1321 int i = 0;
1322 QString itemName = tr( "%1 - Copy" ).arg( sourceGrid->name() );
1323 QList<QgsLayoutItemMapGrid *> grids = mMapItem->grids()->asList();
1324 while ( true )
1325 {
1326 const auto it = std::find_if( grids.begin(), grids.end(), [&itemName]( const QgsLayoutItemMapGrid *grd ) { return grd->name() == itemName; } );
1327 if ( it != grids.end() )
1328 {
1329 i++;
1330 itemName = tr( "%1 - Copy %2" ).arg( sourceGrid->name() ).arg( i );
1331 continue;
1332 }
1333 break;
1334 }
1335 QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( itemName, mMapItem );
1336 grid->copyProperties( sourceGrid );
1337
1338 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Duplicate Map Grid" ) );
1339 mMapItem->grids()->addGrid( grid );
1340 mMapItem->layout()->undoStack()->endCommand();
1341 mMapItem->updateBoundingRect();
1342 mMapItem->update();
1343
1344 addGridListItem( grid->id(), grid->name() );
1345 mGridListWidget->setCurrentRow( 0 );
1346 mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr );
1347}
1348
1349void QgsLayoutMapWidget::mGridUpButton_clicked()
1350{
1351 QListWidgetItem *item = mGridListWidget->currentItem();
1352 if ( !item )
1353 {
1354 return;
1355 }
1356
1357 const int row = mGridListWidget->row( item );
1358 if ( row < 1 )
1359 {
1360 return;
1361 }
1362 mGridListWidget->takeItem( row );
1363 mGridListWidget->insertItem( row - 1, item );
1364 mGridListWidget->setCurrentItem( item );
1365 mMapItem->beginCommand( tr( "Move Grid Up" ) );
1366 mMapItem->grids()->moveGridUp( item->data( Qt::UserRole ).toString() );
1367 mMapItem->endCommand();
1368 mMapItem->update();
1369}
1370
1371void QgsLayoutMapWidget::mGridDownButton_clicked()
1372{
1373 QListWidgetItem *item = mGridListWidget->currentItem();
1374 if ( !item )
1375 {
1376 return;
1377 }
1378
1379 const int row = mGridListWidget->row( item );
1380 if ( mGridListWidget->count() <= row )
1381 {
1382 return;
1383 }
1384 mGridListWidget->takeItem( row );
1385 mGridListWidget->insertItem( row + 1, item );
1386 mGridListWidget->setCurrentItem( item );
1387 mMapItem->beginCommand( tr( "Move Grid Down" ) );
1388 mMapItem->grids()->moveGridDown( item->data( Qt::UserRole ).toString() );
1389 mMapItem->endCommand();
1390 mMapItem->update();
1391}
1392
1393QgsLayoutItemMapGrid *QgsLayoutMapWidget::currentGrid()
1394{
1395 if ( !mMapItem )
1396 {
1397 return nullptr;
1398 }
1399
1400 QListWidgetItem *item = mGridListWidget->currentItem();
1401 if ( !item )
1402 {
1403 return nullptr;
1404 }
1405 return mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() );
1406}
1407
1408void QgsLayoutMapWidget::mGridListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem * )
1409{
1410 mGridPropertiesButton->setEnabled( static_cast<bool>( current ) );
1411}
1412
1413void QgsLayoutMapWidget::mGridListWidget_itemChanged( QListWidgetItem *item )
1414{
1415 if ( !mMapItem )
1416 {
1417 return;
1418 }
1419
1420 QgsLayoutItemMapGrid *grid = mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() );
1421 if ( !grid )
1422 {
1423 return;
1424 }
1425
1426 mMapItem->beginCommand( tr( "Rename Grid" ) );
1427 grid->setName( item->text() );
1428 mMapItem->endCommand();
1429}
1430
1431void QgsLayoutMapWidget::mGridPropertiesButton_clicked()
1432{
1433 if ( !mMapItem )
1434 {
1435 return;
1436 }
1437 QgsLayoutItemMapGrid *grid = currentGrid();
1438 if ( !grid )
1439 {
1440 return;
1441 }
1442
1443 QgsLayoutMapGridWidget *w = new QgsLayoutMapGridWidget( grid, mMapItem );
1444 w->setDesignerInterface( mInterface );
1445 openPanel( w );
1446}
1447
1448QListWidgetItem *QgsLayoutMapWidget::addGridListItem( const QString &id, const QString &name )
1449{
1450 QListWidgetItem *item = new QListWidgetItem( name, nullptr );
1451 item->setData( Qt::UserRole, id );
1452 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
1453 mGridListWidget->insertItem( 0, item );
1454 return item;
1455}
1456
1457void QgsLayoutMapWidget::loadGridEntries()
1458{
1459 //save selection
1460 QSet<QString> selectedIds;
1461 const QList<QListWidgetItem *> itemSelection = mGridListWidget->selectedItems();
1462 QList<QListWidgetItem *>::const_iterator sIt = itemSelection.constBegin();
1463 for ( ; sIt != itemSelection.constEnd(); ++sIt )
1464 {
1465 selectedIds.insert( ( *sIt )->data( Qt::UserRole ).toString() );
1466 }
1467
1468 mGridListWidget->clear();
1469 if ( !mMapItem )
1470 {
1471 return;
1472 }
1473 //load all composer grids into list widget
1474 const QList<QgsLayoutItemMapGrid *> grids = mMapItem->grids()->asList();
1475 QList<QgsLayoutItemMapGrid *>::const_iterator gridIt = grids.constBegin();
1476 for ( ; gridIt != grids.constEnd(); ++gridIt )
1477 {
1478 QListWidgetItem *item = addGridListItem( ( *gridIt )->id(), ( *gridIt )->name() );
1479 if ( selectedIds.contains( ( *gridIt )->id() ) )
1480 {
1481 item->setSelected( true );
1482 mGridListWidget->setCurrentItem( item );
1483 }
1484 }
1485
1486 if ( mGridListWidget->currentItem() )
1487 {
1488 mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr );
1489 }
1490 else
1491 {
1492 mGridListWidget_currentItemChanged( nullptr, nullptr );
1493 }
1494}
1495
1496void QgsLayoutMapWidget::mAddOverviewPushButton_clicked()
1497{
1498 if ( !mMapItem )
1499 {
1500 return;
1501 }
1502
1503 const QString itemName = tr( "Overview %1" ).arg( mMapItem->overviews()->size() + 1 );
1504 QgsLayoutItemMapOverview *overview = new QgsLayoutItemMapOverview( itemName, mMapItem );
1505 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Add Map Overview" ) );
1506 mMapItem->overviews()->addOverview( overview );
1507 mMapItem->layout()->undoStack()->endCommand();
1508 mMapItem->invalidateCache();
1509
1510 addOverviewListItem( overview->id(), overview->name() );
1511
1512 mOverviewListWidget->setCurrentRow( 0 );
1513}
1514
1515void QgsLayoutMapWidget::mRemoveOverviewPushButton_clicked()
1516{
1517 QListWidgetItem *item = mOverviewListWidget->currentItem();
1518 if ( !item )
1519 {
1520 return;
1521 }
1522 mMapItem->beginCommand( tr( "Remove Map Overview" ) );
1523 mMapItem->overviews()->removeOverview( item->data( Qt::UserRole ).toString() );
1524 mMapItem->endCommand();
1525 QListWidgetItem *delItem = mOverviewListWidget->takeItem( mOverviewListWidget->row( item ) );
1526 delete delItem;
1527 mMapItem->invalidateCache();
1528}
1529
1530void QgsLayoutMapWidget::mOverviewUpButton_clicked()
1531{
1532 QListWidgetItem *item = mOverviewListWidget->currentItem();
1533 if ( !item )
1534 {
1535 return;
1536 }
1537
1538 const int row = mOverviewListWidget->row( item );
1539 if ( row < 1 )
1540 {
1541 return;
1542 }
1543 mOverviewListWidget->takeItem( row );
1544 mOverviewListWidget->insertItem( row - 1, item );
1545 mOverviewListWidget->setCurrentItem( item );
1546 mMapItem->beginCommand( tr( "Move Overview Up" ) );
1547 mMapItem->overviews()->moveOverviewUp( item->data( Qt::UserRole ).toString() );
1548 mMapItem->endCommand();
1549 mMapItem->invalidateCache();
1550}
1551
1552void QgsLayoutMapWidget::mOverviewDownButton_clicked()
1553{
1554 QListWidgetItem *item = mOverviewListWidget->currentItem();
1555 if ( !item )
1556 {
1557 return;
1558 }
1559
1560 const int row = mOverviewListWidget->row( item );
1561 if ( mOverviewListWidget->count() <= row )
1562 {
1563 return;
1564 }
1565 mOverviewListWidget->takeItem( row );
1566 mOverviewListWidget->insertItem( row + 1, item );
1567 mOverviewListWidget->setCurrentItem( item );
1568 mMapItem->beginCommand( tr( "Move Overview Down" ) );
1569 mMapItem->overviews()->moveOverviewDown( item->data( Qt::UserRole ).toString() );
1570 mMapItem->endCommand();
1571 mMapItem->invalidateCache();
1572}
1573
1574QgsLayoutItemMapOverview *QgsLayoutMapWidget::currentOverview()
1575{
1576 if ( !mMapItem )
1577 {
1578 return nullptr;
1579 }
1580
1581 QListWidgetItem *item = mOverviewListWidget->currentItem();
1582 if ( !item )
1583 {
1584 return nullptr;
1585 }
1586
1587 return mMapItem->overviews()->overview( item->data( Qt::UserRole ).toString() );
1588}
1589
1590void QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous )
1591{
1592 Q_UNUSED( previous )
1593 if ( !current )
1594 {
1595 mOverviewCheckBox->setEnabled( false );
1596 return;
1597 }
1598
1599 mOverviewCheckBox->setEnabled( true );
1600 setOverviewItems( mMapItem->overviews()->overview( current->data( Qt::UserRole ).toString() ) );
1601}
1602
1603void QgsLayoutMapWidget::mOverviewListWidget_itemChanged( QListWidgetItem *item )
1604{
1605 if ( !mMapItem )
1606 {
1607 return;
1608 }
1609
1610 QgsLayoutItemMapOverview *overview = mMapItem->overviews()->overview( item->data( Qt::UserRole ).toString() );
1611 if ( !overview )
1612 {
1613 return;
1614 }
1615
1616 mMapItem->beginCommand( QStringLiteral( "Rename Overview" ) );
1617 overview->setName( item->text() );
1618 mMapItem->endCommand();
1619 if ( item->isSelected() )
1620 {
1621 //update checkbox title if item is current item
1622 mOverviewCheckBox->setTitle( tr( "Draw \"%1\" overview" ).arg( overview->name() ) );
1623 }
1624}
1625
1626void QgsLayoutMapWidget::setOverviewItemsEnabled( bool enabled )
1627{
1628 mOverviewFrameMapLabel->setEnabled( enabled );
1629 mOverviewFrameMapComboBox->setEnabled( enabled );
1630 mOverviewFrameStyleLabel->setEnabled( enabled );
1631 mOverviewFrameStyleButton->setEnabled( enabled );
1632 mOverviewBlendModeLabel->setEnabled( enabled );
1633 mOverviewBlendModeComboBox->setEnabled( enabled );
1634 mOverviewInvertCheckbox->setEnabled( enabled );
1635 mOverviewCenterCheckbox->setEnabled( enabled );
1636 mOverviewPositionComboBox->setEnabled( enabled );
1637
1638 const QgsLayoutItemMapItem::StackingPosition currentStackingPos = static_cast<QgsLayoutItemMapItem::StackingPosition>( mOverviewPositionComboBox->currentData().toInt() );
1639 mOverviewStackingLayerComboBox->setEnabled( enabled && ( currentStackingPos == QgsLayoutItemMapItem::StackAboveMapLayer || currentStackingPos == QgsLayoutItemMapItem::StackBelowMapLayer ) );
1640}
1641
1642void QgsLayoutMapWidget::blockOverviewItemsSignals( const bool block )
1643{
1644 mOverviewFrameMapComboBox->blockSignals( block );
1645 mOverviewFrameStyleButton->blockSignals( block );
1646 mOverviewBlendModeComboBox->blockSignals( block );
1647 mOverviewInvertCheckbox->blockSignals( block );
1648 mOverviewCenterCheckbox->blockSignals( block );
1649 mOverviewPositionComboBox->blockSignals( block );
1650 mOverviewStackingLayerComboBox->blockSignals( block );
1651}
1652
1653void QgsLayoutMapWidget::setOverviewItems( QgsLayoutItemMapOverview *overview )
1654{
1655 if ( !overview )
1656 {
1657 return;
1658 }
1659
1660 blockOverviewItemsSignals( true );
1661
1662 mOverviewCheckBox->setTitle( tr( "Draw \"%1\" overview" ).arg( overview->name() ) );
1663 mOverviewCheckBox->setChecked( overview->enabled() );
1664
1665 //overview frame
1666 mOverviewFrameMapComboBox->setItem( overview->linkedMap() );
1667
1668 //overview frame blending mode
1669 mOverviewBlendModeComboBox->setBlendMode( overview->blendMode() );
1670 //overview inverted
1671 mOverviewInvertCheckbox->setChecked( overview->inverted() );
1672 //center overview
1673 mOverviewCenterCheckbox->setChecked( overview->centered() );
1674
1675 mOverviewPositionComboBox->setCurrentIndex( mOverviewPositionComboBox->findData( overview->stackingPosition() ) );
1676 mOverviewStackingLayerComboBox->setLayer( overview->stackingLayer() );
1677 mOverviewStackingLayerComboBox->setEnabled( mOverviewPositionComboBox->isEnabled() && ( overview->stackingPosition() == QgsLayoutItemMapItem::StackAboveMapLayer || overview->stackingPosition() == QgsLayoutItemMapItem::StackBelowMapLayer ) );
1678
1679 mOverviewFrameStyleButton->setSymbol( overview->frameSymbol()->clone() );
1680
1681 blockOverviewItemsSignals( false );
1682}
1683
1684void QgsLayoutMapWidget::storeCurrentLayerSet()
1685{
1686 if ( !mMapItem )
1687 return;
1688
1689 QList<QgsMapLayer *> layers = mMapCanvas->mapSettings().layers();
1690
1691 mMapItem->setLayers( layers );
1692
1693 if ( mMapItem->keepLayerStyles() )
1694 {
1695 // also store styles associated with the layers
1696 mMapItem->storeCurrentLayerStyles();
1697 }
1698}
1699
1700QList<QgsMapLayer *> QgsLayoutMapWidget::orderedPresetVisibleLayers( const QString &name ) const
1701{
1702 const QStringList visibleIds = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayerIds( name );
1703
1704 // also make sure to order the layers according to map canvas order
1705 QList<QgsMapLayer *> lst;
1706 const auto constLayerOrder = QgsProject::instance()->layerTreeRoot()->layerOrder();
1707 for ( QgsMapLayer *layer : constLayerOrder )
1708 {
1709 if ( visibleIds.contains( layer->id() ) )
1710 {
1711 lst << layer;
1712 }
1713 }
1714 return lst;
1715}
1716
1717QListWidgetItem *QgsLayoutMapWidget::addOverviewListItem( const QString &id, const QString &name )
1718{
1719 QListWidgetItem *item = new QListWidgetItem( name, nullptr );
1720 item->setData( Qt::UserRole, id );
1721 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
1722 mOverviewListWidget->insertItem( 0, item );
1723 return item;
1724}
1725
1726void QgsLayoutMapWidget::loadOverviewEntries()
1727{
1728 //save selection
1729 QSet<QString> selectedIds;
1730 const QList<QListWidgetItem *> itemSelection = mOverviewListWidget->selectedItems();
1731 QList<QListWidgetItem *>::const_iterator sIt = itemSelection.constBegin();
1732 for ( ; sIt != itemSelection.constEnd(); ++sIt )
1733 {
1734 selectedIds.insert( ( *sIt )->data( Qt::UserRole ).toString() );
1735 }
1736
1737 mOverviewListWidget->clear();
1738 if ( !mMapItem )
1739 {
1740 return;
1741 }
1742
1743 mOverviewFrameMapComboBox->setExceptedItemList( QList<QgsLayoutItem *>() << mMapItem );
1744
1745 //load all composer overviews into list widget
1746 const QList<QgsLayoutItemMapOverview *> overviews = mMapItem->overviews()->asList();
1747 QList<QgsLayoutItemMapOverview *>::const_iterator overviewIt = overviews.constBegin();
1748 for ( ; overviewIt != overviews.constEnd(); ++overviewIt )
1749 {
1750 QListWidgetItem *item = addOverviewListItem( ( *overviewIt )->id(), ( *overviewIt )->name() );
1751 if ( selectedIds.contains( ( *overviewIt )->id() ) )
1752 {
1753 item->setSelected( true );
1754 mOverviewListWidget->setCurrentItem( item );
1755 }
1756 }
1757
1758 if ( mOverviewListWidget->currentItem() )
1759 {
1760 mOverviewListWidget_currentItemChanged( mOverviewListWidget->currentItem(), nullptr );
1761 }
1762 else
1763 {
1764 mOverviewListWidget_currentItemChanged( nullptr, nullptr );
1765 }
1766}
1767
1768void QgsLayoutMapWidget::mOverviewCheckBox_toggled( bool state )
1769{
1770 QgsLayoutItemMapOverview *overview = currentOverview();
1771 if ( !overview )
1772 {
1773 return;
1774 }
1775
1776 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Overview Display Toggled" ) );
1777 overview->setEnabled( state );
1778 mMapItem->invalidateCache();
1779 mMapItem->layout()->undoStack()->endCommand();
1780}
1781
1782void QgsLayoutMapWidget::overviewMapChanged( QgsLayoutItem *item )
1783{
1784 QgsLayoutItemMapOverview *overview = currentOverview();
1785 if ( !overview )
1786 {
1787 return;
1788 }
1789
1790 QgsLayoutItemMap *map = dynamic_cast<QgsLayoutItemMap *>( item );
1791 if ( !map )
1792 return;
1793
1794 mMapItem->beginCommand( tr( "Change Overview Map" ) );
1795 overview->setLinkedMap( map );
1796 mMapItem->invalidateCache();
1797 mMapItem->endCommand();
1798}
1799
1800void QgsLayoutMapWidget::mOverviewBlendModeComboBox_currentIndexChanged( int index )
1801{
1802 Q_UNUSED( index )
1803 QgsLayoutItemMapOverview *overview = currentOverview();
1804 if ( !overview )
1805 {
1806 return;
1807 }
1808
1809 mMapItem->beginCommand( tr( "Change Overview Blend Mode" ) );
1810 overview->setBlendMode( mOverviewBlendModeComboBox->blendMode() );
1811 mMapItem->invalidateCache();
1812 mMapItem->endCommand();
1813}
1814
1815void QgsLayoutMapWidget::mOverviewInvertCheckbox_toggled( bool state )
1816{
1817 QgsLayoutItemMapOverview *overview = currentOverview();
1818 if ( !overview )
1819 {
1820 return;
1821 }
1822
1823 mMapItem->beginCommand( tr( "Toggle Overview Inverted" ) );
1824 overview->setInverted( state );
1825 mMapItem->invalidateCache();
1826 mMapItem->endCommand();
1827}
1828
1829void QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled( bool state )
1830{
1831 QgsLayoutItemMapOverview *overview = currentOverview();
1832 if ( !overview )
1833 {
1834 return;
1835 }
1836
1837 mMapItem->beginCommand( tr( "Toggle Overview Centered" ) );
1838 overview->setCentered( state );
1839 mMapItem->invalidateCache();
1840 mMapItem->endCommand();
1841}
1842
1843void QgsLayoutMapWidget::overviewStackingChanged( int )
1844{
1845 QgsLayoutItemMapOverview *overview = currentOverview();
1846 if ( !overview )
1847 {
1848 return;
1849 }
1850
1851 mMapItem->beginCommand( tr( "Change Overview Position" ) );
1852 overview->setStackingPosition( static_cast<QgsLayoutItemMapItem::StackingPosition>( mOverviewPositionComboBox->currentData().toInt() ) );
1853 mMapItem->invalidateCache();
1854 mMapItem->endCommand();
1855
1856 switch ( overview->stackingPosition() )
1857 {
1861 mOverviewStackingLayerComboBox->setEnabled( false );
1862 break;
1863
1866 mOverviewStackingLayerComboBox->setEnabled( true );
1867 break;
1868 }
1869}
1870
1871void QgsLayoutMapWidget::overviewStackingLayerChanged( QgsMapLayer *layer )
1872{
1873 QgsLayoutItemMapOverview *overview = currentOverview();
1874 if ( !overview )
1875 {
1876 return;
1877 }
1878
1879 mMapItem->beginCommand( tr( "Change Overview Position" ) );
1880 overview->setStackingLayer( layer );
1881 mMapItem->invalidateCache();
1882 mMapItem->endCommand();
1883}
1884
1885//
1886// QgsLayoutMapLabelingWidget
1887//
1888
1890 : QgsLayoutItemBaseWidget( nullptr, map )
1891 , mMapItem( map )
1892{
1893 setupUi( this );
1894 setPanelTitle( tr( "Label Settings" ) );
1895
1896 mLabelBoundarySpinBox->setClearValue( 0 );
1897 mLabelBoundarySpinBox->setShowClearButton( true );
1898
1899 mLabelBoundaryUnitsCombo->linkToWidget( mLabelBoundarySpinBox );
1900 mLabelBoundaryUnitsCombo->setConverter( &mMapItem->layout()->renderContext().measurementConverter() );
1901
1902 connect( mLabelBoundaryUnitsCombo, &QgsLayoutUnitsComboBox::unitChanged, this, &QgsLayoutMapLabelingWidget::labelMarginUnitsChanged );
1903 connect( mLabelBoundarySpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutMapLabelingWidget::labelMarginChanged );
1904 connect( mShowPartialLabelsCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showPartialsToggled );
1905 connect( mShowUnplacedCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showUnplacedToggled );
1906
1908
1909 setNewItem( map );
1910}
1911
1913{
1914 if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
1915 return false;
1916
1917 if ( mMapItem )
1918 {
1919 disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements );
1920 }
1921
1922 mMapItem = qobject_cast<QgsLayoutItemMap *>( item );
1923
1924 if ( mMapItem )
1925 {
1926 connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements );
1927 }
1928
1929 updateGuiElements();
1930
1931 return true;
1932}
1933
1934void QgsLayoutMapLabelingWidget::updateGuiElements()
1935{
1936 whileBlocking( mLabelBoundarySpinBox )->setValue( mMapItem->labelMargin().length() );
1937 whileBlocking( mLabelBoundaryUnitsCombo )->setUnit( mMapItem->labelMargin().units() );
1938 whileBlocking( mShowPartialLabelsCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowPartialLabels );
1939 whileBlocking( mShowUnplacedCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowUnplacedLabels );
1940
1941 if ( mBlockingItemsListView->model() )
1942 {
1943 QAbstractItemModel *oldModel = mBlockingItemsListView->model();
1944 mBlockingItemsListView->setModel( nullptr );
1945 oldModel->deleteLater();
1946 }
1947
1948 QgsLayoutMapItemBlocksLabelsModel *model = new QgsLayoutMapItemBlocksLabelsModel( mMapItem, mMapItem->layout()->itemsModel(), mBlockingItemsListView );
1949 mBlockingItemsListView->setModel( model );
1950
1951 updateDataDefinedButton( mLabelMarginDDBtn );
1952}
1953
1954void QgsLayoutMapLabelingWidget::labelMarginChanged( double val )
1955{
1956 if ( !mMapItem )
1957 return;
1958
1959 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
1960 mMapItem->setLabelMargin( QgsLayoutMeasurement( val, mLabelBoundaryUnitsCombo->unit() ) );
1961 mMapItem->layout()->undoStack()->endCommand();
1962 mMapItem->invalidateCache();
1963}
1964
1965void QgsLayoutMapLabelingWidget::labelMarginUnitsChanged()
1966{
1967 if ( !mMapItem )
1968 return;
1969
1970 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
1971 mMapItem->setLabelMargin( QgsLayoutMeasurement( mLabelBoundarySpinBox->value(), mLabelBoundaryUnitsCombo->unit() ) );
1972 mMapItem->layout()->undoStack()->endCommand();
1973 mMapItem->invalidateCache();
1974}
1975
1976void QgsLayoutMapLabelingWidget::showPartialsToggled( bool checked )
1977{
1978 if ( !mMapItem )
1979 return;
1980
1981 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) );
1982 QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags();
1983 if ( checked )
1985 else
1986 flags &= ~QgsLayoutItemMap::ShowPartialLabels;
1987 mMapItem->setMapFlags( flags );
1988 mMapItem->layout()->undoStack()->endCommand();
1989 mMapItem->invalidateCache();
1990}
1991
1992void QgsLayoutMapLabelingWidget::showUnplacedToggled( bool checked )
1993{
1994 if ( !mMapItem )
1995 return;
1996
1997 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) );
1998 QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags();
1999 if ( checked )
2001 else
2002 flags &= ~QgsLayoutItemMap::ShowUnplacedLabels;
2003 mMapItem->setMapFlags( flags );
2004 mMapItem->layout()->undoStack()->endCommand();
2005 mMapItem->invalidateCache();
2006}
2007
2009 : QSortFilterProxyModel( parent )
2010 , mLayoutModel( layoutModel )
2011 , mMapItem( map )
2012{
2013 setSourceModel( layoutModel );
2014}
2015
2017{
2018 return 1;
2019}
2020
2021QVariant QgsLayoutMapItemBlocksLabelsModel::data( const QModelIndex &i, int role ) const
2022{
2023 if ( !i.isValid() )
2024 return QVariant();
2025
2026 if ( i.column() != 0 )
2027 return QVariant();
2028
2029 const QModelIndex sourceIndex = mapToSource( index( i.row(), QgsLayoutModel::ItemId, i.parent() ) );
2030
2031 QgsLayoutItem *item = mLayoutModel->itemFromIndex( mapToSource( i ) );
2032 if ( !item )
2033 {
2034 return QVariant();
2035 }
2036
2037 switch ( role )
2038 {
2039 case Qt::CheckStateRole:
2040 switch ( i.column() )
2041 {
2042 case 0:
2043 return mMapItem ? ( mMapItem->isLabelBlockingItem( item ) ? Qt::Checked : Qt::Unchecked ) : Qt::Unchecked;
2044 default:
2045 return QVariant();
2046 }
2047
2048 default:
2049 return mLayoutModel->data( sourceIndex, role );
2050 }
2051}
2052
2053bool QgsLayoutMapItemBlocksLabelsModel::setData( const QModelIndex &index, const QVariant &value, int role )
2054{
2055 Q_UNUSED( role )
2056
2057 if ( !index.isValid() )
2058 return false;
2059
2060 QgsLayoutItem *item = mLayoutModel->itemFromIndex( mapToSource( index ) );
2061 if ( !item || !mMapItem )
2062 {
2063 return false;
2064 }
2065
2066 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Blocking Items" ) );
2067
2068 if ( value.toBool() )
2069 {
2070 mMapItem->addLabelBlockingItem( item );
2071 }
2072 else
2073 {
2074 mMapItem->removeLabelBlockingItem( item );
2075 }
2076 emit dataChanged( index, index, QVector<int>() << role );
2077
2078 mMapItem->layout()->undoStack()->endCommand();
2079 mMapItem->invalidateCache();
2080
2081 return true;
2082}
2083
2084Qt::ItemFlags QgsLayoutMapItemBlocksLabelsModel::flags( const QModelIndex &index ) const
2085{
2086 Qt::ItemFlags flags = QSortFilterProxyModel::flags( index );
2087
2088 if ( !index.isValid() )
2089 {
2090 return flags;
2091 }
2092
2093 switch ( index.column() )
2094 {
2095 case 0:
2096 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
2097 default:
2098 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2099 }
2100}
2101
2102bool QgsLayoutMapItemBlocksLabelsModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
2103{
2104 QgsLayoutItem *item = mLayoutModel->itemFromIndex( mLayoutModel->index( source_row, 0, source_parent ) );
2105 if ( !item || item == mMapItem )
2106 {
2107 return false;
2108 }
2109
2110 return true;
2111}
2112
2113
2114//
2115// QgsLayoutMapClippingWidget
2116//
2117
2119 : QgsLayoutItemBaseWidget( nullptr, map )
2120 , mMapItem( map )
2121{
2122 setupUi( this );
2123 setPanelTitle( tr( "Clipping Settings" ) );
2124
2125 mLayerModel = new QgsMapLayerModel( this );
2126 mLayerModel->setItemsCheckable( true );
2127 mLayersTreeView->setModel( mLayerModel );
2128
2129 mAtlasClippingTypeComboBox->addItem( tr( "Clip During Render Only" ), static_cast<int>( QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly ) );
2130 mAtlasClippingTypeComboBox->addItem( tr( "Clip Feature Before Render" ), static_cast<int>( QgsMapClippingRegion::FeatureClippingType::ClipToIntersection ) );
2131 mAtlasClippingTypeComboBox->addItem( tr( "Render Intersecting Features Unchanged" ), static_cast<int>( QgsMapClippingRegion::FeatureClippingType::NoClipping ) );
2132
2133 for ( int i = 0; i < mAtlasClippingTypeComboBox->count(); ++i )
2134 {
2135 mItemClippingTypeComboBox->addItem( mAtlasClippingTypeComboBox->itemText( i ), mAtlasClippingTypeComboBox->itemData( i ) );
2136 }
2137
2138 mClipItemComboBox->setCurrentLayout( map->layout() );
2139 mClipItemComboBox->setItemFlags( QgsLayoutItem::FlagProvidesClipPath );
2140
2141 connect( mRadioClipSelectedLayers, &QRadioButton::toggled, this, &QgsLayoutMapClippingWidget::toggleLayersSelectionGui );
2142 mLayersTreeView->setEnabled( false );
2143 mSelectAllButton->setEnabled( false );
2144 mDeselectAllButton->setEnabled( false );
2145 mInvertSelectionButton->setEnabled( false );
2146 mRadioClipAllLayers->setChecked( true );
2147
2148 connect( mClipToAtlasCheckBox, &QGroupBox::toggled, this, [this]( bool active ) {
2149 if ( !mBlockUpdates )
2150 {
2151 mMapItem->beginCommand( tr( "Toggle Atlas Clipping" ) );
2152 mMapItem->atlasClippingSettings()->setEnabled( active );
2153 mMapItem->endCommand();
2154 }
2155 } );
2156 connect( mForceLabelsInsideCheckBox, &QCheckBox::toggled, this, [this]( bool active ) {
2157 if ( !mBlockUpdates )
2158 {
2159 mMapItem->beginCommand( tr( "Change Atlas Clipping Label Behavior" ) );
2160 mMapItem->atlasClippingSettings()->setForceLabelsInsideFeature( active );
2161 mMapItem->endCommand();
2162 }
2163 } );
2164 connect( mAtlasClippingTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] {
2165 if ( !mBlockUpdates )
2166 {
2167 mMapItem->beginCommand( tr( "Change Atlas Clipping Behavior" ) );
2168 mMapItem->atlasClippingSettings()->setFeatureClippingType( static_cast<QgsMapClippingRegion::FeatureClippingType>( mAtlasClippingTypeComboBox->currentData().toInt() ) );
2169 mMapItem->endCommand();
2170 }
2171 } );
2172
2173 connect( mRadioClipSelectedLayers, &QCheckBox::toggled, this, [this]( bool active ) {
2174 if ( active && !mBlockUpdates )
2175 {
2176 mBlockUpdates = true;
2177 mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
2178 mMapItem->atlasClippingSettings()->setRestrictToLayers( true );
2179 mMapItem->endCommand();
2180 mBlockUpdates = false;
2181 }
2182 } );
2183 // layers selection buttons
2184 connect( mSelectAllButton, &QPushButton::clicked, this, &QgsLayoutMapClippingWidget::selectAll );
2185 connect( mDeselectAllButton, &QPushButton::clicked, this, &QgsLayoutMapClippingWidget::deselectAll );
2186 connect( mInvertSelectionButton, &QPushButton::clicked, this, &QgsLayoutMapClippingWidget::invertSelection );
2187
2188 connect( mRadioClipAllLayers, &QCheckBox::toggled, this, [this]( bool active ) {
2189 if ( active && !mBlockUpdates )
2190 {
2191 mBlockUpdates = true;
2192 mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
2193 mMapItem->atlasClippingSettings()->setRestrictToLayers( false );
2194 mMapItem->endCommand();
2195 mBlockUpdates = false;
2196 }
2197 } );
2198 connect( mLayerModel, &QgsMapLayerModel::dataChanged, this, [this]( const QModelIndex &, const QModelIndex &, const QVector<int> &roles = QVector<int>() ) {
2199 if ( !roles.contains( Qt::CheckStateRole ) )
2200 return;
2201
2202 if ( !mBlockUpdates )
2203 {
2204 mBlockUpdates = true;
2205 mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
2206 mMapItem->atlasClippingSettings()->setLayersToClip( mLayerModel->layersChecked() );
2207 mMapItem->endCommand();
2208 mBlockUpdates = false;
2209 }
2210 } );
2211
2212 // item clipping widgets
2213
2214 connect( mClipToItemCheckBox, &QGroupBox::toggled, this, [this]( bool active ) {
2215 if ( !mBlockUpdates )
2216 {
2217 mMapItem->beginCommand( tr( "Toggle Map Clipping" ) );
2218 mMapItem->itemClippingSettings()->setEnabled( active );
2219 mMapItem->endCommand();
2220 }
2221 } );
2222 connect( mItemClippingTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] {
2223 if ( !mBlockUpdates )
2224 {
2225 mMapItem->beginCommand( tr( "Change Map Clipping Behavior" ) );
2226 mMapItem->itemClippingSettings()->setFeatureClippingType( static_cast<QgsMapClippingRegion::FeatureClippingType>( mItemClippingTypeComboBox->currentData().toInt() ) );
2227 mMapItem->endCommand();
2228 }
2229 } );
2230 connect( mForceLabelsInsideItemCheckBox, &QCheckBox::toggled, this, [this]( bool active ) {
2231 if ( !mBlockUpdates )
2232 {
2233 mMapItem->beginCommand( tr( "Change Map Clipping Label Behavior" ) );
2234 mMapItem->itemClippingSettings()->setForceLabelsInsideClipPath( active );
2235 mMapItem->endCommand();
2236 }
2237 } );
2238 connect( mClipItemComboBox, &QgsLayoutItemComboBox::itemChanged, this, [this]( QgsLayoutItem *item ) {
2239 if ( !mBlockUpdates )
2240 {
2241 mMapItem->beginCommand( tr( "Change Map Clipping Item" ) );
2242 mMapItem->itemClippingSettings()->setSourceItem( item );
2243 mMapItem->endCommand();
2244 }
2245 } );
2246
2247 setNewItem( map );
2248
2249 connect( &map->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutMapClippingWidget::atlasLayerChanged );
2250 if ( QgsLayoutAtlas *atlas = layoutAtlas() )
2251 {
2252 connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutMapClippingWidget::atlasToggled );
2253 atlasToggled( atlas->enabled() );
2254 }
2255}
2256
2258{
2259 mClipToAtlasCheckBox->setTitle( tr( "Clip to %1 feature" ).arg( string ) );
2260 mClipToAtlasLabel->setText( tr( "<b>When enabled, map layers will be automatically clipped to the boundary of the current %1 feature.</b>" ).arg( string ) );
2261 mForceLabelsInsideCheckBox->setText( tr( "Force labels inside %1 feature" ).arg( string ) );
2262}
2263
2265{
2266 if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
2267 return false;
2268
2269 if ( mMapItem )
2270 {
2271 disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapClippingWidget::updateGuiElements );
2272 }
2273
2274 mMapItem = qobject_cast<QgsLayoutItemMap *>( item );
2275
2276 if ( mMapItem )
2277 {
2278 connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapClippingWidget::updateGuiElements );
2279 }
2280
2281 updateGuiElements();
2282
2283 return true;
2284}
2285
2286void QgsLayoutMapClippingWidget::updateGuiElements()
2287{
2288 if ( mBlockUpdates )
2289 return;
2290
2291 mBlockUpdates = true;
2292 mClipToAtlasCheckBox->setChecked( mMapItem->atlasClippingSettings()->enabled() );
2293 mAtlasClippingTypeComboBox->setCurrentIndex( mAtlasClippingTypeComboBox->findData( static_cast<int>( mMapItem->atlasClippingSettings()->featureClippingType() ) ) );
2294 mForceLabelsInsideCheckBox->setChecked( mMapItem->atlasClippingSettings()->forceLabelsInsideFeature() );
2295
2296 mRadioClipAllLayers->setChecked( !mMapItem->atlasClippingSettings()->restrictToLayers() );
2297 mRadioClipSelectedLayers->setChecked( mMapItem->atlasClippingSettings()->restrictToLayers() );
2298 mLayerModel->setLayersChecked( mMapItem->atlasClippingSettings()->layersToClip() );
2299
2300 mClipToItemCheckBox->setChecked( mMapItem->itemClippingSettings()->enabled() );
2301 mItemClippingTypeComboBox->setCurrentIndex( mItemClippingTypeComboBox->findData( static_cast<int>( mMapItem->itemClippingSettings()->featureClippingType() ) ) );
2302 mForceLabelsInsideItemCheckBox->setChecked( mMapItem->itemClippingSettings()->forceLabelsInsideClipPath() );
2303 mClipItemComboBox->setItem( mMapItem->itemClippingSettings()->sourceItem() );
2304
2305 mBlockUpdates = false;
2306}
2307
2308void QgsLayoutMapClippingWidget::atlasLayerChanged( QgsVectorLayer *layer )
2309{
2310 if ( !layer || layer->geometryType() != Qgis::GeometryType::Polygon )
2311 {
2312 //non-polygon layer, disable atlas control
2313 mClipToAtlasCheckBox->setChecked( false );
2314 mClipToAtlasCheckBox->setEnabled( false );
2315 return;
2316 }
2317 else
2318 {
2319 mClipToAtlasCheckBox->setEnabled( true );
2320 }
2321}
2322
2323void QgsLayoutMapClippingWidget::atlasToggled( bool atlasEnabled )
2324{
2325 if ( atlasEnabled && mMapItem && mMapItem->layout() && mMapItem->layout()->reportContext().layer()
2326 && mMapItem->layout()->reportContext().layer()->geometryType() == Qgis::GeometryType::Polygon )
2327 {
2328 mClipToAtlasCheckBox->setEnabled( true );
2329 }
2330 else
2331 {
2332 mClipToAtlasCheckBox->setEnabled( false );
2333 mClipToAtlasCheckBox->setChecked( false );
2334 }
2335}
2336
2337void QgsLayoutMapClippingWidget::invertSelection()
2338{
2339 for ( int i = 0; i < mLayerModel->rowCount( QModelIndex() ); i++ )
2340 {
2341 QModelIndex index = mLayerModel->index( i, 0 );
2342 Qt::CheckState currentState = Qt::CheckState( mLayerModel->data( index, Qt::CheckStateRole ).toInt() );
2343 Qt::CheckState newState = ( currentState == Qt::Checked ) ? Qt::Unchecked : Qt::Checked;
2344 mLayerModel->setData( index, newState, Qt::CheckStateRole );
2345 }
2346}
2347
2348void QgsLayoutMapClippingWidget::selectAll()
2349{
2350 for ( int i = 0; i < mLayerModel->rowCount( QModelIndex() ); i++ )
2351 {
2352 QModelIndex index = mLayerModel->index( i, 0 );
2353 mLayerModel->setData( index, Qt::Checked, Qt::CheckStateRole );
2354 }
2355}
2356
2357void QgsLayoutMapClippingWidget::deselectAll()
2358{
2359 for ( int i = 0; i < mLayerModel->rowCount( QModelIndex() ); i++ )
2360 {
2361 QModelIndex index = mLayerModel->index( i, 0 );
2362 mLayerModel->setData( index, Qt::Unchecked, Qt::CheckStateRole );
2363 }
2364}
2365
2366void QgsLayoutMapClippingWidget::toggleLayersSelectionGui( bool toggled )
2367{
2368 mLayersTreeView->setEnabled( toggled );
2369 mSelectAllButton->setEnabled( toggled );
2370 mDeselectAllButton->setEnabled( toggled );
2371 mInvertSelectionButton->setEnabled( toggled );
2372}
@ SpatialLayer
All spatial layers.
@ Polygon
Polygons.
@ Fill
Fill symbol.
@ NoGeometry
No geometry.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsBookmarkManager * bookmarkManager()
Returns the application's bookmark manager, used for storing installation-wide bookmarks.
@ Extent
Bookmark extent as a QgsReferencedRectangle.
A QSortFilterProxyModel subclass for sorting the entries in a QgsBookmarkManagerModel.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
Custom exception class for Coordinate Reference System related exceptions.
QgsRange which stores a range of double values.
Definition qgsrange.h:233
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
@ MinimumValue
Reset value to minimum()
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
Used to render QgsLayout as an atlas, by iterating over the features from an associated vector layer.
void toggled(bool enabled)
Emitted when atlas is enabled or disabled.
A common interface for layout designer dialogs and widgets.
virtual void activateTool(StandardTool tool)=0
Activates a standard layout designer tool.
@ ToolMoveItemContent
Move item content tool.
A base class for property widgets for layout items.
void updateDataDefinedButton(QgsPropertyOverrideButton *button)
Updates a previously registered data defined button to reflect the item's current properties.
QgsVectorLayer * coverageLayer() const
Returns the current layout context coverage layer (if set).
void registerDataDefinedButton(QgsPropertyOverrideButton *button, QgsLayoutObject::DataDefinedProperty property)
Registers a data defined button, setting up its initial value, connections and description.
virtual void setDesignerInterface(QgsLayoutDesignerInterface *iface)
Sets the the layout designer interface in which the widget is being shown.
QgsLayoutAtlas * layoutAtlas() const
Returns the atlas for the layout (if available)
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
An individual grid which is drawn above the map content in a QgsLayoutItemMap.
void copyProperties(const QgsLayoutItemMapGrid *other)
Copies properties from specified map grid.
QString id() const
Returns the unique id for the map item.
StackingPosition
Item stacking position, specifies where the in the map's stack the item should be rendered.
@ StackBelowMapLabels
Render above all map layers, but below map labels.
@ StackAboveMapLabels
Render above all map layers and labels.
@ StackBelowMapLayer
Render below a specific map layer (see stackingLayer())
@ StackAboveMapLayer
Render above a specific map layer (see stackingLayer())
@ StackBelowMap
Render below all map layers.
void setStackingLayer(QgsMapLayer *layer)
Sets the item's stacking layer, which specifies where the in the map's stack the item should be rende...
virtual void setEnabled(bool enabled)
Controls whether the item will be drawn.
void setStackingPosition(StackingPosition position)
Sets the item's stacking position, which specifies where the in the map's stack the item should be re...
StackingPosition stackingPosition() const
Returns the item's stacking position, which specifies where the in the map's stack the item should be...
QString name() const
Returns the friendly display name for the item.
void setName(const QString &name)
Sets the friendly display name for the item.
bool enabled() const
Returns whether the item will be drawn.
QgsMapLayer * stackingLayer() const
Returns the item's stacking layer, which specifies where the in the map's stack the item should be re...
An individual overview which is drawn above the map content in a QgsLayoutItemMap,...
QgsFillSymbol * frameSymbol()
Returns the fill symbol used for drawing the overview extent.
QPainter::CompositionMode blendMode() const
Retrieves the blending mode used for drawing the overview.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the overview.
bool centered() const
Returns whether the extent of the map is forced to center on the overview.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to show the overview extent of.
void setFrameSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used for drawing the overview extent.
bool inverted() const
Returns whether the overview frame is inverted, ie, whether the shaded area is drawn outside the exte...
void setInverted(bool inverted)
Sets whether the overview frame is inverted, ie, whether the shaded area is drawn outside the extent ...
void setCentered(bool centered)
Sets whether the extent of the map is forced to center on the overview.
QgsLayoutItemMap * linkedMap()
Returns the source map to show the overview extent of.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
@ Predefined
A scale is chosen from the predefined scales.
@ Auto
The extent is adjusted so that each feature is fully visible.
@ Fixed
The current scale of the map is used for each feature of the atlas.
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
QFlags< MapItemFlag > MapItemFlags
@ ShowPartialLabels
Whether to draw labels which are partially outside of the map view.
@ ShowUnplacedLabels
Whether to render unplaced labels in the map view.
A widget for controlling the common properties of layout items (e.g.
void setMasterLayout(QgsMasterLayoutInterface *masterLayout)
Sets the master layout associated with the item.
void setItem(QgsLayoutItem *item)
Sets the layout item.
void updateVariables()
Updates the variables widget, refreshing the values of variables shown.
Base class for graphical items within a QgsLayout.
@ UndoOverviewStyle
Map overview style.
@ UndoMapRotation
Map rotation changed.
@ UndoAtlasMargin
Map atlas margin changed.
@ UndoMapLabelMargin
Margin for labels from edge of map.
int type() const override
Returns a unique graphics item type identifier.
@ FlagProvidesClipPath
Item can act as a clipping path provider (see clipPath())
Allows configuration of layout map clipping settings.
QgsLayoutMapClippingWidget(QgsLayoutItemMap *map)
constructor
void setReportTypeString(const QString &string) override
Sets the string to use to describe the current report type (e.g.
bool setNewItem(QgsLayoutItem *item) final
Attempts to update the widget to show the properties for the specified item.
A widget for configuring layout map grid items.
Model for label blocking items.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
QgsLayoutMapItemBlocksLabelsModel(QgsLayoutItemMap *map, QgsLayoutModel *layoutModel, QObject *parent=nullptr)
constructor
Qt::ItemFlags flags(const QModelIndex &index) const override
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QVariant data(const QModelIndex &index, int role) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Allows configuration of layout map labeling settings.
QgsLayoutMapLabelingWidget(QgsLayoutItemMap *map)
constructor
bool setNewItem(QgsLayoutItem *item) final
Attempts to update the widget to show the properties for the specified item.
void setMasterLayout(QgsMasterLayoutInterface *masterLayout) override
Sets the master layout associated with the item.
bool setNewItem(QgsLayoutItem *item) override
Attempts to update the widget to show the properties for the specified item.
void setReportTypeString(const QString &string) override
Sets the string to use to describe the current report type (e.g.
void populateDataDefinedButtons()
Initializes data defined buttons to current atlas coverage layer.
QgsLayoutMapWidget(QgsLayoutItemMap *item, QgsMapCanvas *mapCanvas)
constructor
void setDesignerInterface(QgsLayoutDesignerInterface *iface) override
Sets the the layout designer interface in which the widget is being shown.
Provides a method of storing measurements for use in QGIS layouts using a variety of different measur...
A model for items attached to a layout.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
QgsLayoutItem * itemFromIndex(const QModelIndex &index) const
Returns the QgsLayoutItem corresponding to a QModelIndex index, if possible.
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
@ MapYMin
Map extent y minimum.
@ MapZRangeUpper
Map frame Z-range lower value.
@ StartDateTime
Temporal range's start DateTime.
@ MapZRangeLower
Map frame Z-range lower value.
@ MapXMax
Map extent x maximum.
@ MapStylePreset
Layer and style map theme.
@ MapYMax
Map extent y maximum.
@ EndDateTime
Temporal range's end DateTime.
@ MapXMin
Map extent x minimum.
@ OriginalValue
Return the original, user set value.
void layerChanged(QgsVectorLayer *layer)
Emitted when the context's layer is changed.
void unitChanged(Qgis::LayoutUnit unit)
Emitted when the unit is changed.
QgsLayoutReportContext & reportContext()
Returns a reference to the layout's report context, which stores information relating to the current ...
Map canvas is a class for displaying all GIS data types on a canvas.
void zoomScale(double scale, bool ignoreScaleLock=false)
Zooms the canvas to a specific scale.
bool setReferencedExtent(const QgsReferencedRectangle &extent)
Sets the canvas to the specified extent.
double scale() const
Returns the last reported scale of the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void refresh()
Repaints the canvas map.
FeatureClippingType
Feature clipping behavior, which controls how features from vector layers will be clipped.
@ NoClipping
Only render features which intersect the clipping region, but do not clip these features to the regio...
@ ClipPainterOnly
Applying clipping on the painter only (i.e. feature boundaries will be unchanged, but may be invisibl...
@ ClipToIntersection
Clip the geometry of these features to the region prior to rendering (i.e. feature boundaries will fo...
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
A model for display of map layers in widgets.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QList< QgsMapLayer * > layersChecked(Qt::CheckState checkState=Qt::Checked)
Returns the list of layers which are checked (or unchecked)
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
void setItemsCheckable(bool checkable)
Defines if layers should be selectable in the widget.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
void setLayersChecked(const QList< QgsMapLayer * > &layers)
Sets which layers are checked in the model.
@ LayerId
Stores the map layer ID.
A proxy model which provides an easy to use model to display the list of layers in widgets.
QgsMapLayerProxyModel * setFilters(Qgis::LayerFilters filters)
Sets filter flags which affect how layers are filtered within the model.
Base class for all map layer types.
Definition qgsmaplayer.h:78
QString id
Definition qgsmaplayer.h:81
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void mapThemesChanged()
Emitted when map themes within the collection are changed.
QStringList mapThemeVisibleLayerIds(const QString &name) const
Returns the list of layer IDs that are visible for the specified map theme.
Interface for master layout type objects, such as print layouts and reports.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsMapThemeCollection * mapThemeCollection
Definition qgsproject.h:115
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
@ CrsNotSet
Not set (hidden by default)
void crsChanged(const QgsCoordinateReferenceSystem &crs)
Emitted when the selected CRS is changed.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
QgsCoordinateReferenceSystem crs() const
Returns the associated coordinate reference system, or an invalid CRS if no reference system is set.
A QgsRectangle with associated coordinate reference system.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryStringList * settingsMapScales
Stores settings for use within QGIS.
Definition qgssettings.h:65
void setLayer(QgsVectorLayer *layer)
Sets a layer to associate with the widget.
void changed()
Emitted when the symbol's settings are changed.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6392
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:6296
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition qgsrange.h:761
const QgsCoordinateReferenceSystem & crs