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