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