QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 "qgslayoutitemmap.h"
21 #include "qgsproject.h"
22 #include "qgsmapthemecollection.h"
23 #include "qgslayout.h"
24 #include "qgslayertree.h"
25 #include "qgsmapcanvas.h"
27 #include "qgssymbollayerutils.h"
28 #include "qgslayoutmapgridwidget.h"
29 #include "qgsstyle.h"
30 #include "qgslayoutundostack.h"
31 #include "qgslayoutatlas.h"
33 #include "qgsguiutils.h"
34 #include "qgsbookmarkmodel.h"
35 #include "qgsreferencedgeometry.h"
36 #include "qgsprojectviewsettings.h"
37 
38 #include <QMenu>
39 #include <QMessageBox>
40 
42  : QgsLayoutItemBaseWidget( nullptr, item )
43  , mMapItem( item )
44  , mMapCanvas( mapCanvas )
45 {
46  Q_ASSERT( mMapItem );
47 
48  setupUi( this );
49  connect( mScaleLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mScaleLineEdit_editingFinished );
50  connect( mActionSetToCanvasExtent, &QAction::triggered, this, &QgsLayoutMapWidget::setToMapCanvasExtent );
51  connect( mActionViewExtentInCanvas, &QAction::triggered, this, &QgsLayoutMapWidget::viewExtentInCanvas );
52  connect( mActionSetToCanvasScale, &QAction::triggered, this, &QgsLayoutMapWidget::setToMapCanvasScale );
53  connect( mActionViewScaleInCanvas, &QAction::triggered, this, &QgsLayoutMapWidget::viewScaleInCanvas );
54  connect( mActionUpdatePreview, &QAction::triggered, this, &QgsLayoutMapWidget::updatePreview );
55  connect( mFollowVisibilityPresetCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mFollowVisibilityPresetCheckBox_stateChanged );
56  connect( mKeepLayerListCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mKeepLayerListCheckBox_stateChanged );
57  connect( mKeepLayerStylesCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mKeepLayerStylesCheckBox_stateChanged );
58  connect( mDrawCanvasItemsCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mDrawCanvasItemsCheckBox_stateChanged );
59  connect( mOverviewBlendModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::mOverviewBlendModeComboBox_currentIndexChanged );
60  connect( mOverviewInvertCheckbox, &QCheckBox::toggled, this, &QgsLayoutMapWidget::mOverviewInvertCheckbox_toggled );
61  connect( mOverviewCenterCheckbox, &QCheckBox::toggled, this, &QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled );
62  connect( mOverviewPositionComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::overviewStackingChanged );
63  connect( mOverviewStackingLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsLayoutMapWidget::overviewStackingLayerChanged );
64  connect( mXMinLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mXMinLineEdit_editingFinished );
65  connect( mXMaxLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mXMaxLineEdit_editingFinished );
66  connect( mYMinLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mYMinLineEdit_editingFinished );
67  connect( mYMaxLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mYMaxLineEdit_editingFinished );
68  connect( mAtlasMarginRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasMarginRadio_toggled );
69  connect( mAtlasCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mAtlasCheckBox_toggled );
70  connect( mAtlasMarginSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsLayoutMapWidget::mAtlasMarginSpinBox_valueChanged );
71  connect( mAtlasFixedScaleRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasFixedScaleRadio_toggled );
72  connect( mAtlasPredefinedScaleRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasPredefinedScaleRadio_toggled );
73  connect( mAddGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mAddGridPushButton_clicked );
74  connect( mRemoveGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mRemoveGridPushButton_clicked );
75  connect( mGridUpButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridUpButton_clicked );
76  connect( mGridDownButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridDownButton_clicked );
77  connect( mGridListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mGridListWidget_currentItemChanged );
78  connect( mGridListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mGridListWidget_itemChanged );
79  connect( mGridPropertiesButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridPropertiesButton_clicked );
80  connect( mAddOverviewPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mAddOverviewPushButton_clicked );
81  connect( mRemoveOverviewPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mRemoveOverviewPushButton_clicked );
82  connect( mOverviewUpButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mOverviewUpButton_clicked );
83  connect( mOverviewDownButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mOverviewDownButton_clicked );
84  connect( mOverviewCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mOverviewCheckBox_toggled );
85  connect( mOverviewListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged );
86  connect( mOverviewListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_itemChanged );
87  connect( mActionLabelSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showLabelSettings );
88 
89  connect( mActionMoveContent, &QAction::triggered, this, &QgsLayoutMapWidget::switchToMoveContentTool );
90  setPanelTitle( tr( "Map Properties" ) );
91  mMapRotationSpinBox->setClearValue( 0 );
92 
93  mDockToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
94 
95  mBookmarkMenu = new QMenu( this );
96  QToolButton *btnBookmarks = new QToolButton( this );
97  btnBookmarks->setAutoRaise( true );
98  btnBookmarks->setToolTip( tr( "Bookmarks" ) );
99  btnBookmarks->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowBookmarks.svg" ) ) );
100  btnBookmarks->setPopupMode( QToolButton::InstantPopup );
101  btnBookmarks->setMenu( mBookmarkMenu );
102 
103  mDockToolbar->insertWidget( mActionMoveContent, btnBookmarks );
104  connect( mBookmarkMenu, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowBookmarkMenu );
105 
106  //add widget for general composer item properties
107  mItemPropertiesWidget = new QgsLayoutItemPropertiesWidget( this, item );
108  mainLayout->addWidget( mItemPropertiesWidget );
109 
110  mScaleLineEdit->setValidator( new QDoubleValidator( mScaleLineEdit ) );
111 
112  mXMinLineEdit->setValidator( new QDoubleValidator( mXMinLineEdit ) );
113  mXMaxLineEdit->setValidator( new QDoubleValidator( mXMaxLineEdit ) );
114  mYMinLineEdit->setValidator( new QDoubleValidator( mYMinLineEdit ) );
115  mYMaxLineEdit->setValidator( new QDoubleValidator( mYMaxLineEdit ) );
116 
117  mOverviewPositionComboBox->addItem( tr( "Below Map" ), QgsLayoutItemMapItem::StackBelowMap );
118  mOverviewPositionComboBox->addItem( tr( "Below Map Layer" ), QgsLayoutItemMapItem::StackBelowMapLayer );
119  mOverviewPositionComboBox->addItem( tr( "Above Map Layer" ), QgsLayoutItemMapItem::StackAboveMapLayer );
120  mOverviewPositionComboBox->addItem( tr( "Below Map Labels" ), QgsLayoutItemMapItem::StackBelowMapLabels );
121  mOverviewPositionComboBox->addItem( tr( "Above Map Labels" ), QgsLayoutItemMapItem::StackAboveMapLabels );
122 
123  blockAllSignals( true );
124 
125  mCrsSelector->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
126  mCrsSelector->setNotSetText( tr( "Use Project CRS" ) );
127 
128  mOverviewFrameStyleButton->setSymbolType( QgsSymbol::Fill );
129 
130  // follow preset combo
131  mFollowVisibilityPresetCombo->setModel( new QStringListModel( mFollowVisibilityPresetCombo ) );
132  connect( mFollowVisibilityPresetCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::followVisibilityPresetSelected );
133  connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged,
134  this, &QgsLayoutMapWidget::onMapThemesChanged );
135  onMapThemesChanged();
136 
137  // keep layers from preset button
138  QMenu *menuKeepLayers = new QMenu( this );
139  mLayerListFromPresetButton->setMenu( menuKeepLayers );
140  mLayerListFromPresetButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowAllLayers.svg" ) ) );
141  mLayerListFromPresetButton->setToolTip( tr( "Set layer list from a map theme" ) );
142  connect( menuKeepLayers, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu );
143 
144  connect( item, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
145 
147  this, &QgsLayoutMapWidget::atlasLayerChanged );
148  if ( QgsLayoutAtlas *atlas = layoutAtlas() )
149  {
150  connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutMapWidget::compositionAtlasToggled );
151  compositionAtlasToggled( atlas->enabled() );
152  }
153 
154  mOverviewFrameMapComboBox->setCurrentLayout( item->layout() );
155  mOverviewFrameMapComboBox->setItemType( QgsLayoutItemRegistry::LayoutMap );
156  mOverviewFrameStyleButton->registerExpressionContextGenerator( item );
157 
158  connect( mOverviewFrameMapComboBox, &QgsLayoutItemComboBox::itemChanged, this, &QgsLayoutMapWidget::overviewMapChanged );
159  connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsLayoutMapWidget::mapCrsChanged );
160  connect( mOverviewFrameStyleButton, &QgsSymbolButton::changed, this, &QgsLayoutMapWidget::overviewSymbolChanged );
161 
162  mOverviewFrameStyleButton->registerExpressionContextGenerator( item );
163  mOverviewFrameStyleButton->setLayer( coverageLayer() );
164  if ( item->layout() )
165  {
166  connect( &item->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, mOverviewFrameStyleButton, &QgsSymbolButton::setLayer );
167  }
168 
169 
179 
180  updateGuiElements();
181  loadGridEntries();
182  loadOverviewEntries();
183 
184  connect( mMapRotationSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutMapWidget::rotationChanged );
185  connect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
186  connect( mMapItem, &QgsLayoutItemMap::mapRotationChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
187 
188  blockAllSignals( false );
189 }
190 
192 {
193  if ( mItemPropertiesWidget )
194  mItemPropertiesWidget->setMasterLayout( masterLayout );
195 }
196 
197 void QgsLayoutMapWidget::setReportTypeString( const QString &string )
198 {
199  mAtlasCheckBox->setTitle( tr( "Controlled by %1" ).arg( string == tr( "atlas" ) ? tr( "Atlas" ) : tr( "Report" ) ) );
200  mAtlasPredefinedScaleRadio->setToolTip( tr( "Use one of the predefined scales of the project where the %1 feature best fits." ).arg( string ) );
201 }
202 
204 {
205  mInterface = iface;
207 }
208 
210 {
211  if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
212  return false;
213 
214  if ( mMapItem )
215  {
216  disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
217  disconnect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
218  disconnect( mMapItem, &QgsLayoutItemMap::mapRotationChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
219  }
220 
221  mMapItem = qobject_cast< QgsLayoutItemMap * >( item );
222  mItemPropertiesWidget->setItem( mMapItem );
223  if ( mLabelWidget )
224  mLabelWidget->setItem( mMapItem );
225 
226  if ( mMapItem )
227  {
228  connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
229  connect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
230  connect( mMapItem, &QgsLayoutItemMap::mapRotationChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
231  mOverviewFrameStyleButton->registerExpressionContextGenerator( mMapItem );
232  }
233 
234  updateGuiElements();
235 
236  return true;
237 }
238 
240 {
241  updateDataDefinedButton( mScaleDDBtn );
242  updateDataDefinedButton( mMapRotationDDBtn );
243  updateDataDefinedButton( mXMinDDBtn );
244  updateDataDefinedButton( mYMinDDBtn );
245  updateDataDefinedButton( mXMaxDDBtn );
246  updateDataDefinedButton( mYMaxDDBtn );
247  updateDataDefinedButton( mAtlasMarginDDBtn );
248  updateDataDefinedButton( mStylePresetsDDBtn );
249  updateDataDefinedButton( mLayersDDBtn );
250 }
251 
252 void QgsLayoutMapWidget::compositionAtlasToggled( bool atlasEnabled )
253 {
254  if ( atlasEnabled &&
255  mMapItem && mMapItem->layout() && mMapItem->layout()->reportContext().layer()
256  && mMapItem->layout()->reportContext().layer()->wkbType() != QgsWkbTypes::NoGeometry )
257  {
258  mAtlasCheckBox->setEnabled( true );
259  }
260  else
261  {
262  mAtlasCheckBox->setEnabled( false );
263  mAtlasCheckBox->setChecked( false );
264  }
265 }
266 
267 void QgsLayoutMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu()
268 {
269  // this menu is for the case when setting "keep layers" and "keep layer styles"
270  // and the preset configuration is copied. The preset is not followed further.
271 
272  QMenu *menu = qobject_cast<QMenu *>( sender() );
273  if ( !menu )
274  return;
275 
276  menu->clear();
277  const auto constMapThemes = QgsProject::instance()->mapThemeCollection()->mapThemes();
278  for ( const QString &presetName : constMapThemes )
279  {
280  menu->addAction( presetName, this, &QgsLayoutMapWidget::keepLayersVisibilityPresetSelected );
281  }
282 
283  if ( menu->actions().isEmpty() )
284  menu->addAction( tr( "No presets defined" ) )->setEnabled( false );
285 }
286 
287 void QgsLayoutMapWidget::followVisibilityPresetSelected( int currentIndex )
288 {
289  if ( !mMapItem )
290  return;
291 
292  if ( currentIndex == -1 )
293  return; // doing combo box model reset
294 
295  QString presetName;
296  if ( currentIndex != 0 )
297  {
298  presetName = mFollowVisibilityPresetCombo->currentText();
299  }
300 
301  if ( presetName == mMapItem->followVisibilityPresetName() )
302  return;
303 
304  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
305  mFollowVisibilityPresetCheckBox->setChecked( true );
306  mMapItem->setFollowVisibilityPresetName( presetName );
307  mMapItem->layout()->undoStack()->endCommand();
308 
309  mMapItem->invalidateCache();
310 }
311 
312 void QgsLayoutMapWidget::keepLayersVisibilityPresetSelected()
313 {
314  QAction *action = qobject_cast<QAction *>( sender() );
315  if ( !action )
316  return;
317 
318  QString presetName = action->text();
319  QList<QgsMapLayer *> lst = orderedPresetVisibleLayers( presetName );
320  if ( mMapItem )
321  {
322  mKeepLayerListCheckBox->setChecked( true );
323  mMapItem->setLayers( lst );
324 
325  mKeepLayerStylesCheckBox->setChecked( true );
326 
327  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
328  mMapItem->setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( presetName ) );
329  mMapItem->layout()->undoStack()->endCommand();
330 
331  mMapItem->invalidateCache();
332  }
333 }
334 
335 void QgsLayoutMapWidget::onMapThemesChanged()
336 {
337  if ( QStringListModel *model = qobject_cast<QStringListModel *>( mFollowVisibilityPresetCombo->model() ) )
338  {
339  QStringList lst;
340  lst.append( tr( "(none)" ) );
342  model->setStringList( lst );
343 
344  // select the previously selected item again
345  int presetModelIndex = mFollowVisibilityPresetCombo->findText( mMapItem->followVisibilityPresetName() );
346  mFollowVisibilityPresetCombo->blockSignals( true );
347  mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none
348  mFollowVisibilityPresetCombo->blockSignals( false );
349  }
350 }
351 
352 void QgsLayoutMapWidget::mapCrsChanged( const QgsCoordinateReferenceSystem &crs )
353 {
354  if ( !mMapItem )
355  {
356  return;
357  }
358 
359  if ( mMapItem->presetCrs() == crs )
360  return;
361 
362  // try to reproject to maintain extent
363  QgsCoordinateReferenceSystem oldCrs = mMapItem->crs();
364 
365  bool updateExtent = false;
366  QgsRectangle newExtent;
367  try
368  {
370  QgsRectangle prevExtent = mMapItem->extent();
371  newExtent = xForm.transformBoundingBox( prevExtent );
372  updateExtent = true;
373  }
374  catch ( QgsCsException & )
375  {
376  //transform failed, don't update extent
377  }
378 
379  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map CRS" ) );
380  mMapItem->setCrs( crs );
381  if ( updateExtent )
382  mMapItem->zoomToExtent( newExtent );
383  mMapItem->layout()->undoStack()->endCommand();
384  mMapItem->invalidateCache();
385 }
386 
387 void QgsLayoutMapWidget::overviewSymbolChanged()
388 {
389  QgsLayoutItemMapOverview *overview = currentOverview();
390  if ( !overview )
391  return;
392 
393  mMapItem->beginCommand( tr( "Change Overview Style" ), QgsLayoutItem::UndoOverviewStyle );
394  overview->setFrameSymbol( mOverviewFrameStyleButton->clonedSymbol<QgsFillSymbol>() );
395  mMapItem->endCommand();
396  mMapItem->invalidateCache();
397 }
398 
399 void QgsLayoutMapWidget::showLabelSettings()
400 {
401  mLabelWidget = new QgsLayoutMapLabelingWidget( mMapItem );
402  openPanel( mLabelWidget );
403 }
404 
405 void QgsLayoutMapWidget::switchToMoveContentTool()
406 {
407  if ( mInterface )
409 }
410 
411 void QgsLayoutMapWidget::aboutToShowBookmarkMenu()
412 {
413  mBookmarkMenu->clear();
414 
415  // query the bookmarks now? or once during widget creation... Hmm. Either way, there's potentially a
416  // delay if there's LOTS of bookmarks. Let's avoid the cost until bookmarks are actually required.
417  if ( !mBookmarkModel )
418  mBookmarkModel = new QgsBookmarkManagerProxyModel( QgsApplication::bookmarkManager(), QgsProject::instance()->bookmarkManager(), this );
419 
420  QMap< QString, QMenu * > groupMenus;
421  for ( int i = 0; i < mBookmarkModel->rowCount(); ++i )
422  {
423  const QString group = mBookmarkModel->data( mBookmarkModel->index( i, 0 ), QgsBookmarkManagerModel::RoleGroup ).toString();
424  QMenu *destMenu = mBookmarkMenu;
425  if ( !group.isEmpty() )
426  {
427  destMenu = groupMenus.value( group );
428  if ( !destMenu )
429  {
430  destMenu = new QMenu( group, mBookmarkMenu );
431  groupMenus[ group ] = destMenu;
432  }
433  }
434  QAction *action = new QAction( mBookmarkModel->data( mBookmarkModel->index( i, 0 ), QgsBookmarkManagerModel::RoleName ).toString(), mBookmarkMenu );
435  const QgsReferencedRectangle extent = mBookmarkModel->data( mBookmarkModel->index( i, 0 ), QgsBookmarkManagerModel::RoleExtent ).value< QgsReferencedRectangle >();
436  connect( action, &QAction::triggered, this, [ = ]
437  {
438  if ( !mMapItem )
439  {
440  return;
441  }
442 
443  QgsRectangle newExtent = extent;
444 
445  //transform?
446  if ( extent.crs() != mMapItem->crs() )
447  {
448  try
449  {
450  QgsCoordinateTransform xForm( extent.crs(), mMapItem->crs(), QgsProject::instance() );
451  newExtent = xForm.transformBoundingBox( newExtent );
452  }
453  catch ( QgsCsException & )
454  {
455  //transform failed, better not proceed
456  return;
457  }
458  }
459 
460  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
461  mMapItem->zoomToExtent( newExtent );
462  mMapItem->layout()->undoStack()->endCommand();
463  } );
464  destMenu->addAction( action );
465  }
466 
467  QStringList groupKeys = groupMenus.keys();
468  groupKeys.sort( Qt::CaseInsensitive );
469  for ( int i = 0; i < groupKeys.count(); ++i )
470  {
471  if ( mBookmarkMenu->actions().value( i ) )
472  mBookmarkMenu->insertMenu( mBookmarkMenu->actions().at( i ), groupMenus.value( groupKeys.at( i ) ) );
473  else
474  mBookmarkMenu->addMenu( groupMenus.value( groupKeys.at( i ) ) );
475  }
476 }
477 
478 void QgsLayoutMapWidget::mAtlasCheckBox_toggled( bool checked )
479 {
480  if ( !mMapItem )
481  {
482  return;
483  }
484 
485  mAtlasFixedScaleRadio->setEnabled( checked );
486  mAtlasMarginRadio->setEnabled( checked );
487 
488  if ( mAtlasMarginRadio->isEnabled() && mAtlasMarginRadio->isChecked() )
489  {
490  mAtlasMarginSpinBox->setEnabled( true );
491  }
492  else
493  {
494  mAtlasMarginSpinBox->setEnabled( false );
495  }
496 
497  mAtlasPredefinedScaleRadio->setEnabled( checked );
498 
499  if ( checked )
500  {
501  //check atlas coverage layer type
502  if ( mMapItem->layout() )
503  {
504  toggleAtlasScalingOptionsByLayerType();
505  }
506  }
507 
508  // disable predefined scales if none are defined
509  if ( !hasPredefinedScales() )
510  {
511  mAtlasPredefinedScaleRadio->setEnabled( false );
512  }
513 
514  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Atlas Driven" ) );
515  mMapItem->setAtlasDriven( checked );
516  mMapItem->layout()->undoStack()->endCommand();
517  updateMapForAtlas();
518 }
519 
520 void QgsLayoutMapWidget::updateMapForAtlas()
521 {
522  //update map if in atlas preview mode
523  if ( mMapItem->atlasDriven() )
524  {
525  mMapItem->refresh();
526  }
527  else
528  {
529  //redraw map
530  mMapItem->invalidateCache();
531  }
532 }
533 
534 void QgsLayoutMapWidget::mAtlasMarginRadio_toggled( bool checked )
535 {
536  mAtlasMarginSpinBox->setEnabled( checked );
537 
538  if ( checked && mMapItem )
539  {
540  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
541  mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Auto );
542  mMapItem->layout()->undoStack()->endCommand();
543  updateMapForAtlas();
544  }
545 }
546 
547 void QgsLayoutMapWidget::mAtlasMarginSpinBox_valueChanged( int value )
548 {
549  if ( !mMapItem )
550  {
551  return;
552  }
553 
554  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Margin" ), QgsLayoutItem::UndoAtlasMargin );
555  mMapItem->setAtlasMargin( value / 100. );
556  mMapItem->layout()->undoStack()->endCommand();
557  updateMapForAtlas();
558 }
559 
560 void QgsLayoutMapWidget::mAtlasFixedScaleRadio_toggled( bool checked )
561 {
562  if ( !mMapItem )
563  {
564  return;
565  }
566 
567  if ( checked )
568  {
569  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
570  mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Fixed );
571  mMapItem->layout()->undoStack()->endCommand();
572  updateMapForAtlas();
573  }
574 }
575 
576 void QgsLayoutMapWidget::mAtlasPredefinedScaleRadio_toggled( bool checked )
577 {
578  if ( !mMapItem )
579  {
580  return;
581  }
582 
583  if ( hasPredefinedScales() )
584  {
585  if ( checked )
586  {
587  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Scales" ) );
588  mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Predefined );
589  mMapItem->layout()->undoStack()->endCommand();
590  updateMapForAtlas();
591  }
592  }
593  else
594  {
595  // restore to fixed scale if no predefined scales exist
596  whileBlocking( mAtlasFixedScaleRadio )->setChecked( true );
597  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
598  mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Fixed );
599  mMapItem->layout()->undoStack()->endCommand();
600  }
601 }
602 
603 void QgsLayoutMapWidget::mScaleLineEdit_editingFinished()
604 {
605  if ( !mMapItem )
606  {
607  return;
608  }
609 
610  bool conversionSuccess = false;
611  double scaleDenominator = QLocale().toDouble( mScaleLineEdit->text(), &conversionSuccess );
612  if ( !conversionSuccess )
613  {
614  return;
615  }
616 
617  if ( qgsDoubleNear( scaleDenominator, mMapItem->scale() ) )
618  return;
619 
620  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Scale" ) );
621  mMapItem->setScale( scaleDenominator );
622  mMapItem->layout()->undoStack()->endCommand();
623 }
624 
625 void QgsLayoutMapWidget::rotationChanged( double value )
626 {
627  if ( !mMapItem )
628  {
629  return;
630  }
631 
632  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Rotation" ), QgsLayoutItem::UndoMapRotation );
633  mMapItem->setMapRotation( value );
634  mMapItem->layout()->undoStack()->endCommand();
635  mMapItem->invalidateCache();
636 }
637 
638 void QgsLayoutMapWidget::setToMapCanvasExtent()
639 {
640  if ( !mMapItem )
641  {
642  return;
643  }
644 
645  QgsRectangle newExtent = mMapCanvas->mapSettings().visibleExtent();
646 
647  //transform?
648  if ( mMapCanvas->mapSettings().destinationCrs()
649  != mMapItem->crs() )
650  {
651  try
652  {
653  QgsCoordinateTransform xForm( mMapCanvas->mapSettings().destinationCrs(),
654  mMapItem->crs(), QgsProject::instance() );
655  newExtent = xForm.transformBoundingBox( newExtent );
656  }
657  catch ( QgsCsException & )
658  {
659  //transform failed, better not proceed
660  return;
661  }
662  }
663 
664  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
665  mMapItem->zoomToExtent( newExtent );
666  mMapItem->layout()->undoStack()->endCommand();
667 }
668 
669 void QgsLayoutMapWidget::setToMapCanvasScale()
670 {
671  if ( !mMapItem )
672  {
673  return;
674  }
675 
676  const double newScale = mMapCanvas->scale();
677 
678  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Scale" ) );
679  mMapItem->setScale( newScale );
680  mMapItem->layout()->undoStack()->endCommand();
681 }
682 
683 void QgsLayoutMapWidget::viewExtentInCanvas()
684 {
685  if ( !mMapItem )
686  {
687  return;
688  }
689 
690  QgsRectangle currentMapExtent = mMapItem->extent();
691 
692  if ( !currentMapExtent.isEmpty() )
693  {
694  try
695  {
696  mMapCanvas->setReferencedExtent( QgsReferencedRectangle( currentMapExtent, mMapItem->crs() ) );
697  }
698  catch ( QgsCsException & )
699  {
700  //transform failed, better not proceed
701  return;
702  }
703  mMapCanvas->refresh();
704  }
705 }
706 
707 void QgsLayoutMapWidget::viewScaleInCanvas()
708 {
709  if ( !mMapItem )
710  {
711  return;
712  }
713 
714  const double currentScale = mMapItem->scale();
715  mMapCanvas->zoomScale( currentScale );
716 }
717 
718 void QgsLayoutMapWidget::mXMinLineEdit_editingFinished()
719 {
720  updateComposerExtentFromGui();
721 }
722 
723 void QgsLayoutMapWidget::mXMaxLineEdit_editingFinished()
724 {
725  updateComposerExtentFromGui();
726 }
727 
728 void QgsLayoutMapWidget::mYMinLineEdit_editingFinished()
729 {
730  updateComposerExtentFromGui();
731 }
732 
733 void QgsLayoutMapWidget::mYMaxLineEdit_editingFinished()
734 {
735  updateComposerExtentFromGui();
736 }
737 
738 void QgsLayoutMapWidget::updateGuiElements()
739 {
740  if ( !mMapItem )
741  {
742  return;
743  }
744 
745  blockAllSignals( true );
746  mLabel->setText( mMapItem->displayName() );
747 
748  whileBlocking( mCrsSelector )->setCrs( mMapItem->presetCrs() );
749 
750  //width, height, scale
751  double scale = mMapItem->scale();
752 
753  //round scale to an appropriate number of decimal places
754  if ( scale >= 10000 )
755  {
756  //round scale to integer if it's greater than 10000
757  mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 0 ) );
758  }
759  else if ( scale >= 10 )
760  {
761  mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 3 ) );
762  }
763  else if ( scale >= 1 )
764  {
765  //don't round scale if it's less than 10, instead use 4 decimal places
766  mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 4 ) );
767  }
768  else
769  {
770  //if scale < 1 then use 10 decimal places
771  mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 10 ) );
772  }
773 
774  //composer map extent
775  QgsRectangle composerMapExtent = mMapItem->extent();
776  mXMinLineEdit->setText( QLocale().toString( composerMapExtent.xMinimum(), 'f', 3 ) );
777  mXMaxLineEdit->setText( QLocale().toString( composerMapExtent.xMaximum(), 'f', 3 ) );
778  mYMinLineEdit->setText( QLocale().toString( composerMapExtent.yMinimum(), 'f', 3 ) );
779  mYMaxLineEdit->setText( QLocale().toString( composerMapExtent.yMaximum(), 'f', 3 ) );
780 
781  mMapRotationSpinBox->setValue( mMapItem->mapRotation( QgsLayoutObject::OriginalValue ) );
782 
783  // follow preset checkbox
784  mFollowVisibilityPresetCheckBox->setCheckState(
785  mMapItem->followVisibilityPreset() ? Qt::Checked : Qt::Unchecked );
786  int presetModelIndex = mFollowVisibilityPresetCombo->findText( mMapItem->followVisibilityPresetName() );
787  mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none
788 
789  //keep layer list checkbox
790  if ( mMapItem->keepLayerSet() )
791  {
792  mKeepLayerListCheckBox->setCheckState( Qt::Checked );
793  }
794  else
795  {
796  mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
797  }
798 
799  mKeepLayerStylesCheckBox->setEnabled( mMapItem->keepLayerSet() );
800  mKeepLayerStylesCheckBox->setCheckState( mMapItem->keepLayerStyles() ? Qt::Checked : Qt::Unchecked );
801 
802  //draw canvas items
803  if ( mMapItem->drawAnnotations() )
804  {
805  mDrawCanvasItemsCheckBox->setCheckState( Qt::Checked );
806  }
807  else
808  {
809  mDrawCanvasItemsCheckBox->setCheckState( Qt::Unchecked );
810  }
811 
812  //atlas controls
813  mAtlasCheckBox->setChecked( mMapItem->atlasDriven() );
814  mAtlasMarginSpinBox->setValue( static_cast<int>( mMapItem->atlasMargin( QgsLayoutObject::OriginalValue ) * 100 ) );
815 
816  mAtlasFixedScaleRadio->setEnabled( mMapItem->atlasDriven() );
817  mAtlasFixedScaleRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Fixed );
818  mAtlasMarginSpinBox->setEnabled( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto );
819  mAtlasMarginRadio->setEnabled( mMapItem->atlasDriven() );
820  mAtlasMarginRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto );
821  mAtlasPredefinedScaleRadio->setEnabled( mMapItem->atlasDriven() );
822  mAtlasPredefinedScaleRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Predefined );
823 
824  if ( mMapItem->atlasDriven() )
825  {
826  toggleAtlasScalingOptionsByLayerType();
827  }
828  // disable predefined scales if none are defined
829  if ( !hasPredefinedScales() )
830  {
831  mAtlasPredefinedScaleRadio->setEnabled( false );
832  }
833 
835  loadGridEntries();
836  loadOverviewEntries();
837 
838  blockAllSignals( false );
839 }
840 
841 void QgsLayoutMapWidget::toggleAtlasScalingOptionsByLayerType()
842 {
843  if ( !mMapItem )
844  {
845  return;
846  }
847 
848  //get atlas coverage layer
849  QgsVectorLayer *layer = coverageLayer();
850  if ( !layer )
851  {
852  return;
853  }
854 
856  {
857  //For point layers buffer setting makes no sense, so set "fixed scale" on and disable margin control
858  mAtlasFixedScaleRadio->setChecked( true );
859  mAtlasMarginRadio->setEnabled( false );
860  mAtlasPredefinedScaleRadio->setEnabled( false );
861  }
862  else
863  {
864  //Not a point layer, so enable changes to fixed scale control
865  mAtlasMarginRadio->setEnabled( true );
866  mAtlasPredefinedScaleRadio->setEnabled( true );
867  }
868 }
869 
870 void QgsLayoutMapWidget::updateComposerExtentFromGui()
871 {
872  if ( !mMapItem )
873  {
874  return;
875  }
876 
877  double xmin, ymin, xmax, ymax;
878  bool conversionSuccess;
879 
880  xmin = QLocale().toDouble( mXMinLineEdit->text(), &conversionSuccess );
881  if ( !conversionSuccess )
882  return;
883  xmax = QLocale().toDouble( mXMaxLineEdit->text(), &conversionSuccess );
884  if ( !conversionSuccess )
885  return;
886  ymin = QLocale().toDouble( mYMinLineEdit->text(), &conversionSuccess );
887  if ( !conversionSuccess )
888  return;
889  ymax = QLocale().toDouble( mYMaxLineEdit->text(), &conversionSuccess );
890  if ( !conversionSuccess )
891  return;
892 
893  QgsRectangle newExtent( xmin, ymin, xmax, ymax );
894 
895  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
896  mMapItem->setExtent( newExtent );
897  mMapItem->layout()->undoStack()->endCommand();
898 }
899 
900 void QgsLayoutMapWidget::blockAllSignals( bool b )
901 {
902  mScaleLineEdit->blockSignals( b );
903  mXMinLineEdit->blockSignals( b );
904  mXMaxLineEdit->blockSignals( b );
905  mYMinLineEdit->blockSignals( b );
906  mYMaxLineEdit->blockSignals( b );
907  mDrawCanvasItemsCheckBox->blockSignals( b );
908  mOverviewFrameMapComboBox->blockSignals( b );
909  mOverviewFrameStyleButton->blockSignals( b );
910  mOverviewBlendModeComboBox->blockSignals( b );
911  mOverviewInvertCheckbox->blockSignals( b );
912  mOverviewCenterCheckbox->blockSignals( b );
913  mAtlasCheckBox->blockSignals( b );
914  mAtlasMarginSpinBox->blockSignals( b );
915  mAtlasFixedScaleRadio->blockSignals( b );
916  mAtlasMarginRadio->blockSignals( b );
917  mFollowVisibilityPresetCheckBox->blockSignals( b );
918  mFollowVisibilityPresetCombo->blockSignals( b );
919  mKeepLayerListCheckBox->blockSignals( b );
920  mKeepLayerStylesCheckBox->blockSignals( b );
921  mActionSetToCanvasExtent->blockSignals( b );
922  mActionUpdatePreview->blockSignals( b );
923 
924  blockOverviewItemsSignals( b );
925 }
926 
927 void QgsLayoutMapWidget::updatePreview()
928 {
929  if ( !mMapItem )
930  {
931  return;
932  }
933  mMapItem->refresh();
934 }
935 
936 void QgsLayoutMapWidget::mFollowVisibilityPresetCheckBox_stateChanged( int state )
937 {
938  if ( !mMapItem )
939  {
940  return;
941  }
942 
943  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
944  if ( state == Qt::Checked )
945  {
946  mMapItem->setFollowVisibilityPreset( true );
947 
948  // mutually exclusive with keeping custom layer list
949  mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
950  mKeepLayerStylesCheckBox->setCheckState( Qt::Unchecked );
951 
952  mMapItem->invalidateCache();
953  }
954  else
955  {
956  mMapItem->setFollowVisibilityPreset( false );
957  }
958  mMapItem->layout()->undoStack()->endCommand();
959 }
960 
961 void QgsLayoutMapWidget::mKeepLayerListCheckBox_stateChanged( int state )
962 {
963  if ( !mMapItem )
964  {
965  return;
966  }
967 
968  // update map
969  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Map Preset Changed" ) );
970  storeCurrentLayerSet();
971  mMapItem->setKeepLayerSet( state == Qt::Checked );
972  if ( state == Qt::Unchecked )
973  {
974  mMapItem->setLayers( QList< QgsMapLayer * >() );
975  }
976  mMapItem->layout()->undoStack()->endCommand();
977 
978  // update gui
979  if ( state == Qt::Checked )
980  {
981  // mutually exclusive with following a preset
982  mFollowVisibilityPresetCheckBox->setCheckState( Qt::Unchecked );
983  }
984  else
985  {
986  mKeepLayerStylesCheckBox->setChecked( Qt::Unchecked );
987  mMapItem->invalidateCache();
988  }
989 
990  mKeepLayerStylesCheckBox->setEnabled( state == Qt::Checked );
991 }
992 
993 void QgsLayoutMapWidget::mKeepLayerStylesCheckBox_stateChanged( int state )
994 {
995  if ( !mMapItem )
996  {
997  return;
998  }
999 
1000  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
1001  if ( state == Qt::Checked )
1002  {
1003  mMapItem->storeCurrentLayerStyles();
1004  mMapItem->setKeepLayerStyles( true );
1005  }
1006  else
1007  {
1008  mMapItem->setLayerStyleOverrides( QMap<QString, QString>() );
1009  mMapItem->setKeepLayerStyles( false );
1010  }
1011  mMapItem->layout()->undoStack()->endCommand();
1012 }
1013 
1014 void QgsLayoutMapWidget::mDrawCanvasItemsCheckBox_stateChanged( int state )
1015 {
1016  if ( !mMapItem )
1017  {
1018  return;
1019  }
1020 
1021  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Map Item" ) );
1022  mMapItem->setDrawAnnotations( state == Qt::Checked );
1023  mMapItem->invalidateCache();
1024  mMapItem->layout()->undoStack()->endCommand();
1025 }
1026 
1027 void QgsLayoutMapWidget::atlasLayerChanged( QgsVectorLayer *layer )
1028 {
1029  if ( !layer || layer->wkbType() == QgsWkbTypes::NoGeometry )
1030  {
1031  //geometryless layer, disable atlas control
1032  mAtlasCheckBox->setChecked( false );
1033  mAtlasCheckBox->setEnabled( false );
1034  return;
1035  }
1036  else
1037  {
1038  mAtlasCheckBox->setEnabled( true );
1039  }
1040 
1041  // enable or disable fixed scale control based on layer type
1042  if ( mAtlasCheckBox->isChecked() )
1043  toggleAtlasScalingOptionsByLayerType();
1044 }
1045 
1046 bool QgsLayoutMapWidget::hasPredefinedScales() const
1047 {
1048  // first look at project's scales
1049  const QVector< double > scales( QgsProject::instance()->viewSettings()->mapScales() );
1050  bool hasProjectScales( QgsProject::instance()->viewSettings()->useProjectScales() );
1051  if ( !hasProjectScales || scales.isEmpty() )
1052  {
1053  // default to global map tool scales
1054  QgsSettings settings;
1055  QString scalesStr( settings.value( QStringLiteral( "Map/scales" ), Qgis::defaultProjectScales() ).toString() );
1056  QStringList myScalesList = scalesStr.split( ',' );
1057  return !myScalesList.isEmpty() && !myScalesList[0].isEmpty();
1058  }
1059  return true;
1060 }
1061 
1062 void QgsLayoutMapWidget::mAddGridPushButton_clicked()
1063 {
1064  if ( !mMapItem )
1065  {
1066  return;
1067  }
1068 
1069  QString itemName = tr( "Grid %1" ).arg( mMapItem->grids()->size() + 1 );
1070  QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( itemName, mMapItem );
1071  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Add Map Grid" ) );
1072  mMapItem->grids()->addGrid( grid );
1073  mMapItem->layout()->undoStack()->endCommand();
1074  mMapItem->updateBoundingRect();
1075  mMapItem->update();
1076 
1077  addGridListItem( grid->id(), grid->name() );
1078  mGridListWidget->setCurrentRow( 0 );
1079  mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr );
1080 }
1081 
1082 void QgsLayoutMapWidget::mRemoveGridPushButton_clicked()
1083 {
1084  QListWidgetItem *item = mGridListWidget->currentItem();
1085  if ( !item )
1086  {
1087  return;
1088  }
1089 
1090  mMapItem->beginCommand( tr( "Remove Grid" ) );
1091  mMapItem->grids()->removeGrid( item->data( Qt::UserRole ).toString() );
1092  QListWidgetItem *delItem = mGridListWidget->takeItem( mGridListWidget->row( item ) );
1093  delete delItem;
1094  mMapItem->endCommand();
1095  mMapItem->updateBoundingRect();
1096  mMapItem->update();
1097 }
1098 
1099 void QgsLayoutMapWidget::mGridUpButton_clicked()
1100 {
1101  QListWidgetItem *item = mGridListWidget->currentItem();
1102  if ( !item )
1103  {
1104  return;
1105  }
1106 
1107  int row = mGridListWidget->row( item );
1108  if ( row < 1 )
1109  {
1110  return;
1111  }
1112  mGridListWidget->takeItem( row );
1113  mGridListWidget->insertItem( row - 1, item );
1114  mGridListWidget->setCurrentItem( item );
1115  mMapItem->beginCommand( tr( "Move Grid Up" ) );
1116  mMapItem->grids()->moveGridUp( item->data( Qt::UserRole ).toString() );
1117  mMapItem->endCommand();
1118  mMapItem->update();
1119 }
1120 
1121 void QgsLayoutMapWidget::mGridDownButton_clicked()
1122 {
1123  QListWidgetItem *item = mGridListWidget->currentItem();
1124  if ( !item )
1125  {
1126  return;
1127  }
1128 
1129  int row = mGridListWidget->row( item );
1130  if ( mGridListWidget->count() <= row )
1131  {
1132  return;
1133  }
1134  mGridListWidget->takeItem( row );
1135  mGridListWidget->insertItem( row + 1, item );
1136  mGridListWidget->setCurrentItem( item );
1137  mMapItem->beginCommand( tr( "Move Grid Down" ) );
1138  mMapItem->grids()->moveGridDown( item->data( Qt::UserRole ).toString() );
1139  mMapItem->endCommand();
1140  mMapItem->update();
1141 }
1142 
1143 QgsLayoutItemMapGrid *QgsLayoutMapWidget::currentGrid()
1144 {
1145  if ( !mMapItem )
1146  {
1147  return nullptr;
1148  }
1149 
1150  QListWidgetItem *item = mGridListWidget->currentItem();
1151  if ( !item )
1152  {
1153  return nullptr;
1154  }
1155  return mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() );
1156 }
1157 
1158 void QgsLayoutMapWidget::mGridListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem * )
1159 {
1160  mGridPropertiesButton->setEnabled( static_cast< bool >( current ) );
1161 }
1162 
1163 void QgsLayoutMapWidget::mGridListWidget_itemChanged( QListWidgetItem *item )
1164 {
1165  if ( !mMapItem )
1166  {
1167  return;
1168  }
1169 
1170  QgsLayoutItemMapGrid *grid = mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() );
1171  if ( !grid )
1172  {
1173  return;
1174  }
1175 
1176  mMapItem->beginCommand( tr( "Rename Grid" ) );
1177  grid->setName( item->text() );
1178  mMapItem->endCommand();
1179 }
1180 
1181 void QgsLayoutMapWidget::mGridPropertiesButton_clicked()
1182 {
1183  if ( !mMapItem )
1184  {
1185  return;
1186  }
1187  QgsLayoutItemMapGrid *grid = currentGrid();
1188  if ( !grid )
1189  {
1190  return;
1191  }
1192 
1193  QgsLayoutMapGridWidget *w = new QgsLayoutMapGridWidget( grid, mMapItem );
1194  w->setDesignerInterface( mInterface );
1195  openPanel( w );
1196 }
1197 
1198 QListWidgetItem *QgsLayoutMapWidget::addGridListItem( const QString &id, const QString &name )
1199 {
1200  QListWidgetItem *item = new QListWidgetItem( name, nullptr );
1201  item->setData( Qt::UserRole, id );
1202  item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
1203  mGridListWidget->insertItem( 0, item );
1204  return item;
1205 }
1206 
1207 void QgsLayoutMapWidget::loadGridEntries()
1208 {
1209  //save selection
1210  QSet<QString> selectedIds;
1211  QList<QListWidgetItem *> itemSelection = mGridListWidget->selectedItems();
1212  QList<QListWidgetItem *>::const_iterator sIt = itemSelection.constBegin();
1213  for ( ; sIt != itemSelection.constEnd(); ++sIt )
1214  {
1215  selectedIds.insert( ( *sIt )->data( Qt::UserRole ).toString() );
1216  }
1217 
1218  mGridListWidget->clear();
1219  if ( !mMapItem )
1220  {
1221  return;
1222  }
1223  //load all composer grids into list widget
1224  QList< QgsLayoutItemMapGrid * > grids = mMapItem->grids()->asList();
1225  QList< QgsLayoutItemMapGrid * >::const_iterator gridIt = grids.constBegin();
1226  for ( ; gridIt != grids.constEnd(); ++gridIt )
1227  {
1228  QListWidgetItem *item = addGridListItem( ( *gridIt )->id(), ( *gridIt )->name() );
1229  if ( selectedIds.contains( ( *gridIt )->id() ) )
1230  {
1231  item->setSelected( true );
1232  mGridListWidget->setCurrentItem( item );
1233  }
1234  }
1235 
1236  if ( mGridListWidget->currentItem() )
1237  {
1238  mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr );
1239  }
1240  else
1241  {
1242  mGridListWidget_currentItemChanged( nullptr, nullptr );
1243  }
1244 }
1245 
1246 void QgsLayoutMapWidget::mAddOverviewPushButton_clicked()
1247 {
1248  if ( !mMapItem )
1249  {
1250  return;
1251  }
1252 
1253  QString itemName = tr( "Overview %1" ).arg( mMapItem->overviews()->size() + 1 );
1254  QgsLayoutItemMapOverview *overview = new QgsLayoutItemMapOverview( itemName, mMapItem );
1255  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Add Map Overview" ) );
1256  mMapItem->overviews()->addOverview( overview );
1257  mMapItem->layout()->undoStack()->endCommand();
1258  mMapItem->invalidateCache();
1259 
1260  addOverviewListItem( overview->id(), overview->name() );
1261 
1262  mOverviewListWidget->setCurrentRow( 0 );
1263 }
1264 
1265 void QgsLayoutMapWidget::mRemoveOverviewPushButton_clicked()
1266 {
1267  QListWidgetItem *item = mOverviewListWidget->currentItem();
1268  if ( !item )
1269  {
1270  return;
1271  }
1272  mMapItem->beginCommand( tr( "Remove Map Overview" ) );
1273  mMapItem->overviews()->removeOverview( item->data( Qt::UserRole ).toString() );
1274  mMapItem->endCommand();
1275  QListWidgetItem *delItem = mOverviewListWidget->takeItem( mOverviewListWidget->row( item ) );
1276  delete delItem;
1277  mMapItem->invalidateCache();
1278 }
1279 
1280 void QgsLayoutMapWidget::mOverviewUpButton_clicked()
1281 {
1282  QListWidgetItem *item = mOverviewListWidget->currentItem();
1283  if ( !item )
1284  {
1285  return;
1286  }
1287 
1288  int row = mOverviewListWidget->row( item );
1289  if ( row < 1 )
1290  {
1291  return;
1292  }
1293  mOverviewListWidget->takeItem( row );
1294  mOverviewListWidget->insertItem( row - 1, item );
1295  mOverviewListWidget->setCurrentItem( item );
1296  mMapItem->beginCommand( tr( "Move Overview Up" ) );
1297  mMapItem->overviews()->moveOverviewUp( item->data( Qt::UserRole ).toString() );
1298  mMapItem->endCommand();
1299  mMapItem->invalidateCache();
1300 }
1301 
1302 void QgsLayoutMapWidget::mOverviewDownButton_clicked()
1303 {
1304  QListWidgetItem *item = mOverviewListWidget->currentItem();
1305  if ( !item )
1306  {
1307  return;
1308  }
1309 
1310  int row = mOverviewListWidget->row( item );
1311  if ( mOverviewListWidget->count() <= row )
1312  {
1313  return;
1314  }
1315  mOverviewListWidget->takeItem( row );
1316  mOverviewListWidget->insertItem( row + 1, item );
1317  mOverviewListWidget->setCurrentItem( item );
1318  mMapItem->beginCommand( tr( "Move Overview Down" ) );
1319  mMapItem->overviews()->moveOverviewDown( item->data( Qt::UserRole ).toString() );
1320  mMapItem->endCommand();
1321  mMapItem->invalidateCache();
1322 }
1323 
1324 QgsLayoutItemMapOverview *QgsLayoutMapWidget::currentOverview()
1325 {
1326  if ( !mMapItem )
1327  {
1328  return nullptr;
1329  }
1330 
1331  QListWidgetItem *item = mOverviewListWidget->currentItem();
1332  if ( !item )
1333  {
1334  return nullptr;
1335  }
1336 
1337  return mMapItem->overviews()->overview( item->data( Qt::UserRole ).toString() );
1338 }
1339 
1340 void QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous )
1341 {
1342  Q_UNUSED( previous )
1343  if ( !current )
1344  {
1345  mOverviewCheckBox->setEnabled( false );
1346  return;
1347  }
1348 
1349  mOverviewCheckBox->setEnabled( true );
1350  setOverviewItems( mMapItem->overviews()->overview( current->data( Qt::UserRole ).toString() ) );
1351 }
1352 
1353 void QgsLayoutMapWidget::mOverviewListWidget_itemChanged( QListWidgetItem *item )
1354 {
1355  if ( !mMapItem )
1356  {
1357  return;
1358  }
1359 
1360  QgsLayoutItemMapOverview *overview = mMapItem->overviews()->overview( item->data( Qt::UserRole ).toString() );
1361  if ( !overview )
1362  {
1363  return;
1364  }
1365 
1366  mMapItem->beginCommand( QStringLiteral( "Rename Overview" ) );
1367  overview->setName( item->text() );
1368  mMapItem->endCommand();
1369  if ( item->isSelected() )
1370  {
1371  //update checkbox title if item is current item
1372  mOverviewCheckBox->setTitle( QString( tr( "Draw \"%1\" overview" ) ).arg( overview->name() ) );
1373  }
1374 }
1375 
1376 void QgsLayoutMapWidget::setOverviewItemsEnabled( bool enabled )
1377 {
1378  mOverviewFrameMapLabel->setEnabled( enabled );
1379  mOverviewFrameMapComboBox->setEnabled( enabled );
1380  mOverviewFrameStyleLabel->setEnabled( enabled );
1381  mOverviewFrameStyleButton->setEnabled( enabled );
1382  mOverviewBlendModeLabel->setEnabled( enabled );
1383  mOverviewBlendModeComboBox->setEnabled( enabled );
1384  mOverviewInvertCheckbox->setEnabled( enabled );
1385  mOverviewCenterCheckbox->setEnabled( enabled );
1386  mOverviewPositionComboBox->setEnabled( enabled );
1387 
1388  QgsLayoutItemMapItem::StackingPosition currentStackingPos = static_cast< QgsLayoutItemMapItem::StackingPosition >( mOverviewPositionComboBox->currentData().toInt() );
1389  mOverviewStackingLayerComboBox->setEnabled( enabled && ( currentStackingPos == QgsLayoutItemMapItem::StackAboveMapLayer || currentStackingPos == QgsLayoutItemMapItem::StackBelowMapLayer ) );
1390 }
1391 
1392 void QgsLayoutMapWidget::blockOverviewItemsSignals( const bool block )
1393 {
1394  mOverviewFrameMapComboBox->blockSignals( block );
1395  mOverviewFrameStyleButton->blockSignals( block );
1396  mOverviewBlendModeComboBox->blockSignals( block );
1397  mOverviewInvertCheckbox->blockSignals( block );
1398  mOverviewCenterCheckbox->blockSignals( block );
1399  mOverviewPositionComboBox->blockSignals( block );
1400  mOverviewStackingLayerComboBox->blockSignals( block );
1401 }
1402 
1403 void QgsLayoutMapWidget::setOverviewItems( QgsLayoutItemMapOverview *overview )
1404 {
1405  if ( !overview )
1406  {
1407  return;
1408  }
1409 
1410  blockOverviewItemsSignals( true );
1411 
1412  mOverviewCheckBox->setTitle( QString( tr( "Draw \"%1\" overview" ) ).arg( overview->name() ) );
1413  mOverviewCheckBox->setChecked( overview->enabled() );
1414 
1415  //overview frame
1416  mOverviewFrameMapComboBox->setItem( overview->linkedMap() );
1417 
1418  //overview frame blending mode
1419  mOverviewBlendModeComboBox->setBlendMode( overview->blendMode() );
1420  //overview inverted
1421  mOverviewInvertCheckbox->setChecked( overview->inverted() );
1422  //center overview
1423  mOverviewCenterCheckbox->setChecked( overview->centered() );
1424 
1425  mOverviewPositionComboBox->setCurrentIndex( mOverviewPositionComboBox->findData( overview->stackingPosition() ) );
1426  mOverviewStackingLayerComboBox->setLayer( overview->stackingLayer() );
1427  mOverviewStackingLayerComboBox->setEnabled( mOverviewPositionComboBox->isEnabled() && ( overview->stackingPosition() == QgsLayoutItemMapItem::StackAboveMapLayer
1429 
1430  mOverviewFrameStyleButton->setSymbol( overview->frameSymbol()->clone() );
1431 
1432  blockOverviewItemsSignals( false );
1433 }
1434 
1435 void QgsLayoutMapWidget::storeCurrentLayerSet()
1436 {
1437  if ( !mMapItem )
1438  return;
1439 
1440  QList<QgsMapLayer *> layers = mMapCanvas->mapSettings().layers();
1441  mMapItem->setLayers( layers );
1442 
1443  if ( mMapItem->keepLayerStyles() )
1444  {
1445  // also store styles associated with the layers
1446  mMapItem->storeCurrentLayerStyles();
1447  }
1448 }
1449 
1450 QList<QgsMapLayer *> QgsLayoutMapWidget::orderedPresetVisibleLayers( const QString &name ) const
1451 {
1452  QStringList visibleIds = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayerIds( name );
1453 
1454  // also make sure to order the layers according to map canvas order
1455  QList<QgsMapLayer *> lst;
1456  const auto constLayerOrder = QgsProject::instance()->layerTreeRoot()->layerOrder();
1457  for ( QgsMapLayer *layer : constLayerOrder )
1458  {
1459  if ( visibleIds.contains( layer->id() ) )
1460  {
1461  lst << layer;
1462  }
1463  }
1464  return lst;
1465 }
1466 
1467 QListWidgetItem *QgsLayoutMapWidget::addOverviewListItem( const QString &id, const QString &name )
1468 {
1469  QListWidgetItem *item = new QListWidgetItem( name, nullptr );
1470  item->setData( Qt::UserRole, id );
1471  item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
1472  mOverviewListWidget->insertItem( 0, item );
1473  return item;
1474 }
1475 
1476 void QgsLayoutMapWidget::loadOverviewEntries()
1477 {
1478  //save selection
1479  QSet<QString> selectedIds;
1480  QList<QListWidgetItem *> itemSelection = mOverviewListWidget->selectedItems();
1481  QList<QListWidgetItem *>::const_iterator sIt = itemSelection.constBegin();
1482  for ( ; sIt != itemSelection.constEnd(); ++sIt )
1483  {
1484  selectedIds.insert( ( *sIt )->data( Qt::UserRole ).toString() );
1485  }
1486 
1487  mOverviewListWidget->clear();
1488  if ( !mMapItem )
1489  {
1490  return;
1491  }
1492 
1493  mOverviewFrameMapComboBox->setExceptedItemList( QList< QgsLayoutItem * >() << mMapItem );
1494 
1495  //load all composer overviews into list widget
1496  QList< QgsLayoutItemMapOverview * > overviews = mMapItem->overviews()->asList();
1497  QList< QgsLayoutItemMapOverview * >::const_iterator overviewIt = overviews.constBegin();
1498  for ( ; overviewIt != overviews.constEnd(); ++overviewIt )
1499  {
1500  QListWidgetItem *item = addOverviewListItem( ( *overviewIt )->id(), ( *overviewIt )->name() );
1501  if ( selectedIds.contains( ( *overviewIt )->id() ) )
1502  {
1503  item->setSelected( true );
1504  mOverviewListWidget->setCurrentItem( item );
1505  }
1506  }
1507 
1508  if ( mOverviewListWidget->currentItem() )
1509  {
1510  mOverviewListWidget_currentItemChanged( mOverviewListWidget->currentItem(), nullptr );
1511  }
1512  else
1513  {
1514  mOverviewListWidget_currentItemChanged( nullptr, nullptr );
1515  }
1516 }
1517 
1518 void QgsLayoutMapWidget::mOverviewCheckBox_toggled( bool state )
1519 {
1520  QgsLayoutItemMapOverview *overview = currentOverview();
1521  if ( !overview )
1522  {
1523  return;
1524  }
1525 
1526  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Overview Display Toggled" ) );
1527  overview->setEnabled( state );
1528  mMapItem->invalidateCache();
1529  mMapItem->layout()->undoStack()->endCommand();
1530 }
1531 
1532 void QgsLayoutMapWidget::overviewMapChanged( QgsLayoutItem *item )
1533 {
1534  QgsLayoutItemMapOverview *overview = currentOverview();
1535  if ( !overview )
1536  {
1537  return;
1538  }
1539 
1540  QgsLayoutItemMap *map = dynamic_cast< QgsLayoutItemMap * >( item );
1541  if ( !map )
1542  return;
1543 
1544  mMapItem->beginCommand( tr( "Change Overview Map" ) );
1545  overview->setLinkedMap( map );
1546  mMapItem->invalidateCache();
1547  mMapItem->endCommand();
1548 }
1549 
1550 void QgsLayoutMapWidget::mOverviewBlendModeComboBox_currentIndexChanged( int index )
1551 {
1552  Q_UNUSED( index )
1553  QgsLayoutItemMapOverview *overview = currentOverview();
1554  if ( !overview )
1555  {
1556  return;
1557  }
1558 
1559  mMapItem->beginCommand( tr( "Change Overview Blend Mode" ) );
1560  overview->setBlendMode( mOverviewBlendModeComboBox->blendMode() );
1561  mMapItem->invalidateCache();
1562  mMapItem->endCommand();
1563 }
1564 
1565 void QgsLayoutMapWidget::mOverviewInvertCheckbox_toggled( bool state )
1566 {
1567  QgsLayoutItemMapOverview *overview = currentOverview();
1568  if ( !overview )
1569  {
1570  return;
1571  }
1572 
1573  mMapItem->beginCommand( tr( "Toggle Overview Inverted" ) );
1574  overview->setInverted( state );
1575  mMapItem->invalidateCache();
1576  mMapItem->endCommand();
1577 }
1578 
1579 void QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled( bool state )
1580 {
1581  QgsLayoutItemMapOverview *overview = currentOverview();
1582  if ( !overview )
1583  {
1584  return;
1585  }
1586 
1587  mMapItem->beginCommand( tr( "Toggle Overview Centered" ) );
1588  overview->setCentered( state );
1589  mMapItem->invalidateCache();
1590  mMapItem->endCommand();
1591 }
1592 
1593 void QgsLayoutMapWidget::overviewStackingChanged( int )
1594 {
1595  QgsLayoutItemMapOverview *overview = currentOverview();
1596  if ( !overview )
1597  {
1598  return;
1599  }
1600 
1601  mMapItem->beginCommand( tr( "Change Overview Position" ) );
1602  overview->setStackingPosition( static_cast< QgsLayoutItemMapItem::StackingPosition >( mOverviewPositionComboBox->currentData().toInt() ) );
1603  mMapItem->invalidateCache();
1604  mMapItem->endCommand();
1605 
1606  switch ( overview->stackingPosition() )
1607  {
1611  mOverviewStackingLayerComboBox->setEnabled( false );
1612  break;
1613 
1616  mOverviewStackingLayerComboBox->setEnabled( true );
1617  break;
1618  }
1619 }
1620 
1621 void QgsLayoutMapWidget::overviewStackingLayerChanged( QgsMapLayer *layer )
1622 {
1623  QgsLayoutItemMapOverview *overview = currentOverview();
1624  if ( !overview )
1625  {
1626  return;
1627  }
1628 
1629  mMapItem->beginCommand( tr( "Change Overview Position" ) );
1630  overview->setStackingLayer( layer );
1631  mMapItem->invalidateCache();
1632  mMapItem->endCommand();
1633 }
1634 
1635 //
1636 // QgsLayoutMapLabelingWidget
1637 //
1638 
1640  : QgsLayoutItemBaseWidget( nullptr, map )
1641  , mMapItem( map )
1642 {
1643  setupUi( this );
1644  setPanelTitle( tr( "Label Settings" ) );
1645 
1646  mLabelBoundarySpinBox->setClearValue( 0 );
1647  mLabelBoundarySpinBox->setShowClearButton( true );
1648 
1649  mLabelBoundaryUnitsCombo->linkToWidget( mLabelBoundarySpinBox );
1650  mLabelBoundaryUnitsCombo->setConverter( &mMapItem->layout()->renderContext().measurementConverter() );
1651 
1652  connect( mLabelBoundaryUnitsCombo, &QgsLayoutUnitsComboBox::changed, this, &QgsLayoutMapLabelingWidget::labelMarginUnitsChanged );
1653  connect( mLabelBoundarySpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutMapLabelingWidget::labelMarginChanged );
1654  connect( mShowPartialLabelsCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showPartialsToggled );
1655  connect( mShowUnplacedCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showUnplacedToggled );
1656 
1658 
1659  setNewItem( map );
1660 }
1661 
1663 {
1664  if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
1665  return false;
1666 
1667  if ( mMapItem )
1668  {
1669  disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements );
1670  }
1671 
1672  mMapItem = qobject_cast< QgsLayoutItemMap * >( item );
1673 
1674  if ( mMapItem )
1675  {
1676  connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements );
1677  }
1678 
1679  updateGuiElements();
1680 
1681  return true;
1682 }
1683 
1684 void QgsLayoutMapLabelingWidget::updateGuiElements()
1685 {
1686  whileBlocking( mLabelBoundarySpinBox )->setValue( mMapItem->labelMargin().length() );
1687  whileBlocking( mLabelBoundaryUnitsCombo )->setUnit( mMapItem->labelMargin().units() );
1688  whileBlocking( mShowPartialLabelsCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowPartialLabels );
1689  whileBlocking( mShowUnplacedCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowUnplacedLabels );
1690 
1691  if ( mBlockingItemsListView->model() )
1692  {
1693  QAbstractItemModel *oldModel = mBlockingItemsListView->model();
1694  mBlockingItemsListView->setModel( nullptr );
1695  oldModel->deleteLater();
1696  }
1697 
1698  QgsLayoutMapItemBlocksLabelsModel *model = new QgsLayoutMapItemBlocksLabelsModel( mMapItem, mMapItem->layout()->itemsModel(), mBlockingItemsListView );
1699  mBlockingItemsListView->setModel( model );
1700 
1701  updateDataDefinedButton( mLabelMarginDDBtn );
1702 }
1703 
1704 void QgsLayoutMapLabelingWidget::labelMarginChanged( double val )
1705 {
1706  if ( !mMapItem )
1707  return;
1708 
1709  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
1710  mMapItem->setLabelMargin( QgsLayoutMeasurement( val, mLabelBoundaryUnitsCombo->unit() ) );
1711  mMapItem->layout()->undoStack()->endCommand();
1712  mMapItem->invalidateCache();
1713 }
1714 
1715 void QgsLayoutMapLabelingWidget::labelMarginUnitsChanged()
1716 {
1717  if ( !mMapItem )
1718  return;
1719 
1720  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
1721  mMapItem->setLabelMargin( QgsLayoutMeasurement( mLabelBoundarySpinBox->value(), mLabelBoundaryUnitsCombo->unit() ) );
1722  mMapItem->layout()->undoStack()->endCommand();
1723  mMapItem->invalidateCache();
1724 }
1725 
1726 void QgsLayoutMapLabelingWidget::showPartialsToggled( bool checked )
1727 {
1728  if ( !mMapItem )
1729  return;
1730 
1731  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) );
1732  QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags();
1733  if ( checked )
1735  else
1736  flags &= ~QgsLayoutItemMap::ShowPartialLabels;
1737  mMapItem->setMapFlags( flags );
1738  mMapItem->layout()->undoStack()->endCommand();
1739  mMapItem->invalidateCache();
1740 }
1741 
1742 void QgsLayoutMapLabelingWidget::showUnplacedToggled( bool checked )
1743 {
1744  if ( !mMapItem )
1745  return;
1746 
1747  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) );
1748  QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags();
1749  if ( checked )
1751  else
1752  flags &= ~QgsLayoutItemMap::ShowUnplacedLabels;
1753  mMapItem->setMapFlags( flags );
1754  mMapItem->layout()->undoStack()->endCommand();
1755  mMapItem->invalidateCache();
1756 }
1757 
1759  : QSortFilterProxyModel( parent )
1760  , mLayoutModel( layoutModel )
1761  , mMapItem( map )
1762 {
1763  setSourceModel( layoutModel );
1764 }
1765 
1766 int QgsLayoutMapItemBlocksLabelsModel::columnCount( const QModelIndex & ) const
1767 {
1768  return 1;
1769 }
1770 
1771 QVariant QgsLayoutMapItemBlocksLabelsModel::data( const QModelIndex &i, int role ) const
1772 {
1773  if ( !i.isValid() )
1774  return QVariant();
1775 
1776  if ( i.column() != 0 )
1777  return QVariant();
1778 
1779  QModelIndex sourceIndex = mapToSource( index( i.row(), QgsLayoutModel::ItemId, i.parent() ) );
1780 
1781  QgsLayoutItem *item = mLayoutModel->itemFromIndex( mapToSource( i ) );
1782  if ( !item )
1783  {
1784  return QVariant();
1785  }
1786 
1787  switch ( role )
1788  {
1789  case Qt::CheckStateRole:
1790  switch ( i.column() )
1791  {
1792  case 0:
1793  return mMapItem ? ( mMapItem->isLabelBlockingItem( item ) ? Qt::Checked : Qt::Unchecked ) : Qt::Unchecked;
1794  default:
1795  return QVariant();
1796  }
1797 
1798  default:
1799  return mLayoutModel->data( sourceIndex, role );
1800  }
1801 }
1802 
1803 bool QgsLayoutMapItemBlocksLabelsModel::setData( const QModelIndex &index, const QVariant &value, int role )
1804 {
1805  Q_UNUSED( role )
1806 
1807  if ( !index.isValid() )
1808  return false;
1809 
1810  QgsLayoutItem *item = mLayoutModel->itemFromIndex( mapToSource( index ) );
1811  if ( !item || !mMapItem )
1812  {
1813  return false;
1814  }
1815 
1816  mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Blocking Items" ) );
1817 
1818  if ( value.toBool() )
1819  {
1820  mMapItem->addLabelBlockingItem( item );
1821  }
1822  else
1823  {
1824  mMapItem->removeLabelBlockingItem( item );
1825  }
1826  emit dataChanged( index, index, QVector<int>() << role );
1827 
1828  mMapItem->layout()->undoStack()->endCommand();
1829  mMapItem->invalidateCache();
1830 
1831  return true;
1832 }
1833 
1834 Qt::ItemFlags QgsLayoutMapItemBlocksLabelsModel::flags( const QModelIndex &index ) const
1835 {
1836  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
1837 
1838  if ( ! index.isValid() )
1839  {
1840  return flags ;
1841  }
1842 
1843  switch ( index.column() )
1844  {
1845  case 0:
1846  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
1847  default:
1848  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
1849  }
1850 }
1851 
1852 bool QgsLayoutMapItemBlocksLabelsModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
1853 {
1854  QgsLayoutItem *item = mLayoutModel->itemFromIndex( mLayoutModel->index( source_row, 0, source_parent ) );
1855  if ( !item || item == mMapItem )
1856  {
1857  return false;
1858  }
1859 
1860  return true;
1861 }
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value...
Map rotation changed.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
bool setReferencedExtent(const QgsReferencedRectangle &extent) SIP_THROW(QgsCsException)
Sets the canvas to the specified extent.
void setMasterLayout(QgsMasterLayoutInterface *masterLayout) override
Sets the master layout associated with the item.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to show the overview extent of.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:79
QgsLayoutMapLabelingWidget(QgsLayoutItemMap *map)
constructor
void setDesignerInterface(QgsLayoutDesignerInterface *iface) override
Sets the the layout designer interface in which the widget is being shown.
void setFrameSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used for drawing the overview extent.
virtual void setDesignerInterface(QgsLayoutDesignerInterface *iface)
Sets the the layout designer interface in which the widget is being shown.
QgsLayoutItem * itemFromIndex(const QModelIndex &index) const
Returns the QgsLayoutItem corresponding to a QModelIndex index, if possible.
Base class for graphical items within a QgsLayout.
An individual overview which is drawn above the map content in a QgsLayoutItemMap, and shows the extent of another QgsLayoutItemMap.
int type() const override
Returns a unique graphics item type identifier.
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QVariant data(const QModelIndex &index, int role) const override
StackingPosition
Item stacking position, specifies where the in the map&#39;s stack the item should be rendered...
void setInverted(bool inverted)
Sets whether the overview frame is inverted, ie, whether the shaded area is drawn outside the extent ...
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
bool setNewItem(QgsLayoutItem *item) override
Attempts to update the widget to show the properties for the specified item.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Layer and style map theme.
void extentChanged()
Emitted when the map&#39;s extent changes.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
Render above a specific map layer (see stackingLayer())
A widget for controlling the common properties of layout items (e.g.
void changed()
Emitted when the symbol&#39;s settings are changed.
QVariant data(const QModelIndex &index, int role) const override
void refresh()
Repaints the canvas map.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
A common interface for layout designer dialogs and widgets.
void toggled(bool)
Emitted when atlas is enabled or disabled.
QgsFillSymbol * frameSymbol()
Returns the fill symbol used for drawing the overview extent.
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
void updateDataDefinedButton(QgsPropertyOverrideButton *button)
Updates a previously registered data defined button to reflect the item&#39;s current properties...
const QgsCoordinateReferenceSystem & crs
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
void mapThemesChanged()
Emitted when map themes within the collection are changed.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the overview.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:75
Render below a specific map layer (see stackingLayer())
Map extent x minimum.
Render above all map layers, but below map labels.
Margin for labels from edge of map.
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
QStringList mapThemeVisibleLayerIds(const QString &name) const
Returns the list of layer IDs that are visible for the specified map theme.
Qt::ItemFlags flags(const QModelIndex &index) const override
void registerDataDefinedButton(QgsPropertyOverrideButton *button, QgsLayoutObject::DataDefinedProperty property)
Registers a data defined button, setting up its initial value, connections and description.
bool setNewItem(QgsLayoutItem *item) override
Attempts to update the widget to show the properties for the specified item.
bool inverted() const
Returns whether the overview frame is inverted, ie, whether the shaded area is drawn outside the exte...
virtual void activateTool(StandardTool tool)=0
Activates a standard layout designer tool.
Layout graphical items for displaying a map.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
const QgsLayout * layout() const
Returns the layout the object is attached to.
Map extent x maximum.
QgsMapThemeCollection mapThemeCollection
Definition: qgsproject.h:100
void setReportTypeString(const QString &string) override
Sets the string to use to describe the current report type (e.g.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
void setName(const QString &name)
Sets the friendly display name for the item.
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2134
Allows configuration of layout map labeling settings.
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:97
QPainter::CompositionMode blendMode() const
Retrieves the blending mode used for drawing the overview.
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector ...
StackingPosition stackingPosition() const
Returns the item&#39;s stacking position, which specifies where the in the map&#39;s stack the item should be...
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:812
bool enabled() const
Returns whether the item will be drawn.
Whether to draw labels which are partially outside of the map view.
QgsMapLayer * stackingLayer() const
Returns the item&#39;s stacking layer, which specifies where the in the map&#39;s stack the item should be re...
void layerChanged(QgsVectorLayer *layer)
Emitted when the context&#39;s layer is changed.
A QgsRectangle with associated coordinate reference system.
void changed(QgsUnitTypes::LayoutUnit unit)
Emitted when the unit is changed.
QgsLayoutAtlas * layoutAtlas() const
Returns the atlas for the layout (if available)
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
void setStackingLayer(QgsMapLayer *layer)
Sets the item&#39;s stacking layer, which specifies where the in the map&#39;s stack the item should be rende...
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
void setCentered(bool centered)
Sets whether the extent of the map is forced to center on the overview.
The extent is adjusted so that each feature is fully visible.
An individual grid which is drawn above the map content in a QgsLayoutItemMap.
static QString defaultProjectScales()
A string with default project scales.
Definition: qgis.cpp:270
void mapRotationChanged(double newRotation)
Emitted when the map&#39;s rotation changes.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
Map extent y maximum.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project&#39;s layer tree.
double scale() const
Returns the last reported scale of the canvas.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsLayoutReportContext & reportContext()
Returns a reference to the layout&#39;s report context, which stores information relating to the current ...
Definition: qgslayout.cpp:368
QgsVectorLayer * coverageLayer() const
Returns the current layout context coverage layer (if set).
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
Fill symbol.
Definition: qgssymbol.h:88
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:262
void setLayer(QgsVectorLayer *layer)
Sets a layer to associate with the widget.
A widget for configuring layout map grid items.
A base class for property widgets for layout items.
void populateDataDefinedButtons()
Initializes data defined buttons to current atlas coverage layer.
void crsChanged(const QgsCoordinateReferenceSystem &)
Emitted when the selected CRS is changed.
Map extent y minimum.
QgsLayoutMapWidget(QgsLayoutItemMap *item, QgsMapCanvas *mapCanvas)
constructor
void setStackingPosition(StackingPosition position)
Sets the item&#39;s stacking position, which specifies where the in the map&#39;s stack the item should be re...
The current scale of the map is used for each feature of the atlas.
A QSortFilterProxyModel subclass for sorting the entries in a QgsBookmarkManagerModel.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:450
This class represents a coordinate reference system (CRS).
QgsLayoutMapItemBlocksLabelsModel(QgsLayoutItemMap *map, QgsLayoutModel *layoutModel, QObject *parent=nullptr)
constructor
int columnCount(const QModelIndex &parent=QModelIndex()) const override
void zoomScale(double scale)
Zooms the canvas to a specific scale.
Class for doing transforms between two map coordinate systems.
bool centered() const
Returns whether the extent of the map is forced to center on the overview.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
static QgsBookmarkManager * bookmarkManager()
Returns the application&#39;s bookmark manager, used for storing installation-wide bookmarks.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
QString id() const
Returns the unique id for the map item.
void setItem(QgsLayoutItem *item)
Sets the layout item.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgssymbol.h:1190
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
A model for items attached to a layout.
Bookmark extent as a QgsReferencedRectangle.
Model for label blocking items.
Interface for master layout type objects, such as print layouts and reports.
virtual void setEnabled(bool enabled)
Controls whether the item will be drawn.
void changed()
Emitted when the object&#39;s properties change.
void updateVariables()
Updates the variables widget, refreshing the values of variables shown.
Represents a vector layer which manages a vector based data sets.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Whether to render unplaced labels in the map view.
QString name() const
Returns the friendly display name for the item.
Render above all map layers and labels.
Return the original, user set value.
A scale is chosen from the predefined scales.
void setMasterLayout(QgsMasterLayoutInterface *masterLayout)
Sets the master layout associated with the item.
Map atlas margin changed.
QgsLayoutItemMap * linkedMap()
Returns the source map to show the overview extent of.
Render below all map layers.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.