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