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