/*************************************************************************** qgslayoutmapwidget.cpp ---------------------- begin : October 2017 copyright : (C) 2017 by Nyall Dawson email : nyall dot dawson at gmail dot com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "qgslayoutmapwidget.h" #include "qgssettingsregistrycore.h" #include "qgslayoutitemmap.h" #include "qgsproject.h" #include "qgsmapthemecollection.h" #include "qgslayout.h" #include "qgslayertree.h" #include "qgsmapcanvas.h" #include "qgslayoutmapgridwidget.h" #include "qgslayoutundostack.h" #include "qgslayoutatlas.h" #include "qgslayoutdesignerinterface.h" #include "qgsguiutils.h" #include "qgsbookmarkmodel.h" #include "qgsreferencedgeometry.h" #include "qgsprojectviewsettings.h" #include "qgsmaplayermodel.h" #include "qgsfillsymbol.h" #include #include #include QgsLayoutMapWidget::QgsLayoutMapWidget( QgsLayoutItemMap *item, QgsMapCanvas *mapCanvas ) : QgsLayoutItemBaseWidget( nullptr, item ) , mMapItem( item ) , mMapCanvas( mapCanvas ) { Q_ASSERT( mMapItem ); setupUi( this ); connect( mScaleLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mScaleLineEdit_editingFinished ); connect( mActionSetToCanvasExtent, &QAction::triggered, this, &QgsLayoutMapWidget::setToMapCanvasExtent ); connect( mActionViewExtentInCanvas, &QAction::triggered, this, &QgsLayoutMapWidget::viewExtentInCanvas ); connect( mActionSetToCanvasScale, &QAction::triggered, this, &QgsLayoutMapWidget::setToMapCanvasScale ); connect( mActionViewScaleInCanvas, &QAction::triggered, this, &QgsLayoutMapWidget::viewScaleInCanvas ); connect( mActionUpdatePreview, &QAction::triggered, this, &QgsLayoutMapWidget::updatePreview ); connect( mFollowVisibilityPresetCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mFollowVisibilityPresetCheckBox_stateChanged ); connect( mKeepLayerListCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mKeepLayerListCheckBox_stateChanged ); connect( mKeepLayerStylesCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mKeepLayerStylesCheckBox_stateChanged ); connect( mDrawCanvasItemsCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mDrawCanvasItemsCheckBox_stateChanged ); connect( mOverviewBlendModeComboBox, static_cast( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::mOverviewBlendModeComboBox_currentIndexChanged ); connect( mOverviewInvertCheckbox, &QCheckBox::toggled, this, &QgsLayoutMapWidget::mOverviewInvertCheckbox_toggled ); connect( mOverviewCenterCheckbox, &QCheckBox::toggled, this, &QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled ); connect( mOverviewPositionComboBox, static_cast( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::overviewStackingChanged ); connect( mOverviewStackingLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsLayoutMapWidget::overviewStackingLayerChanged ); connect( mXMinLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mXMinLineEdit_editingFinished ); connect( mXMaxLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mXMaxLineEdit_editingFinished ); connect( mYMinLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mYMinLineEdit_editingFinished ); connect( mYMaxLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mYMaxLineEdit_editingFinished ); connect( mAtlasMarginRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasMarginRadio_toggled ); connect( mAtlasCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mAtlasCheckBox_toggled ); connect( mAtlasMarginSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsLayoutMapWidget::mAtlasMarginSpinBox_valueChanged ); connect( mAtlasFixedScaleRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasFixedScaleRadio_toggled ); connect( mAtlasPredefinedScaleRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasPredefinedScaleRadio_toggled ); connect( mAddGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mAddGridPushButton_clicked ); connect( mRemoveGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mRemoveGridPushButton_clicked ); connect( mGridUpButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridUpButton_clicked ); connect( mGridDownButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridDownButton_clicked ); connect( mGridListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mGridListWidget_currentItemChanged ); connect( mGridListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mGridListWidget_itemChanged ); connect( mGridPropertiesButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridPropertiesButton_clicked ); connect( mAddOverviewPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mAddOverviewPushButton_clicked ); connect( mRemoveOverviewPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mRemoveOverviewPushButton_clicked ); connect( mOverviewUpButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mOverviewUpButton_clicked ); connect( mOverviewDownButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mOverviewDownButton_clicked ); connect( mOverviewCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mOverviewCheckBox_toggled ); connect( mOverviewListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged ); connect( mOverviewListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_itemChanged ); connect( mActionLabelSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showLabelSettings ); connect( mActionClipSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showClipSettings ); connect( mTemporalCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mTemporalCheckBox_toggled ); connect( mStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsLayoutMapWidget::updateTemporalExtent ); connect( mEndDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsLayoutMapWidget::updateTemporalExtent ); mStartDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() ); mEndDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() ); mStartDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" ); mEndDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" ); connect( mActionMoveContent, &QAction::triggered, this, &QgsLayoutMapWidget::switchToMoveContentTool ); setPanelTitle( tr( "Map Properties" ) ); mMapRotationSpinBox->setClearValue( 0 ); mDockToolbar->setIconSize( QgsGuiUtils::iconSize( true ) ); mBookmarkMenu = new QMenu( this ); QToolButton *btnBookmarks = new QToolButton( this ); btnBookmarks->setAutoRaise( true ); btnBookmarks->setToolTip( tr( "Bookmarks" ) ); btnBookmarks->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowBookmarks.svg" ) ) ); btnBookmarks->setPopupMode( QToolButton::InstantPopup ); btnBookmarks->setMenu( mBookmarkMenu ); mDockToolbar->insertWidget( mActionMoveContent, btnBookmarks ); connect( mBookmarkMenu, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowBookmarkMenu ); //add widget for general composer item properties mItemPropertiesWidget = new QgsLayoutItemPropertiesWidget( this, item ); mainLayout->addWidget( mItemPropertiesWidget ); mScaleLineEdit->setValidator( new QDoubleValidator( mScaleLineEdit ) ); mXMinLineEdit->setValidator( new QDoubleValidator( mXMinLineEdit ) ); mXMaxLineEdit->setValidator( new QDoubleValidator( mXMaxLineEdit ) ); mYMinLineEdit->setValidator( new QDoubleValidator( mYMinLineEdit ) ); mYMaxLineEdit->setValidator( new QDoubleValidator( mYMaxLineEdit ) ); mOverviewPositionComboBox->addItem( tr( "Below Map" ), QgsLayoutItemMapItem::StackBelowMap ); mOverviewPositionComboBox->addItem( tr( "Below Map Layer" ), QgsLayoutItemMapItem::StackBelowMapLayer ); mOverviewPositionComboBox->addItem( tr( "Above Map Layer" ), QgsLayoutItemMapItem::StackAboveMapLayer ); mOverviewPositionComboBox->addItem( tr( "Below Map Labels" ), QgsLayoutItemMapItem::StackBelowMapLabels ); mOverviewPositionComboBox->addItem( tr( "Above Map Labels" ), QgsLayoutItemMapItem::StackAboveMapLabels ); blockAllSignals( true ); mCrsSelector->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true ); mCrsSelector->setNotSetText( tr( "Use Project CRS" ) ); mCrsSelector->setDialogTitle( tr( "Map Item CRS" ) ); mOverviewFrameStyleButton->setSymbolType( Qgis::SymbolType::Fill ); // follow preset combo mFollowVisibilityPresetCombo->setModel( new QStringListModel( mFollowVisibilityPresetCombo ) ); connect( mFollowVisibilityPresetCombo, static_cast( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::followVisibilityPresetSelected ); connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsLayoutMapWidget::onMapThemesChanged ); onMapThemesChanged(); // keep layers from preset button QMenu *menuKeepLayers = new QMenu( this ); mLayerListFromPresetButton->setMenu( menuKeepLayers ); mLayerListFromPresetButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowAllLayers.svg" ) ) ); mLayerListFromPresetButton->setToolTip( tr( "Set layer list from a map theme" ) ); connect( menuKeepLayers, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu ); connect( item, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements ); connect( &item->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutMapWidget::atlasLayerChanged ); if ( QgsLayoutAtlas *atlas = layoutAtlas() ) { connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutMapWidget::compositionAtlasToggled ); compositionAtlasToggled( atlas->enabled() ); } mOverviewFrameMapComboBox->setCurrentLayout( item->layout() ); mOverviewFrameMapComboBox->setItemType( QgsLayoutItemRegistry::LayoutMap ); mOverviewFrameStyleButton->registerExpressionContextGenerator( item ); connect( mOverviewFrameMapComboBox, &QgsLayoutItemComboBox::itemChanged, this, &QgsLayoutMapWidget::overviewMapChanged ); connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsLayoutMapWidget::mapCrsChanged ); connect( mOverviewFrameStyleButton, &QgsSymbolButton::changed, this, &QgsLayoutMapWidget::overviewSymbolChanged ); mOverviewFrameStyleButton->registerExpressionContextGenerator( item ); mOverviewFrameStyleButton->setLayer( coverageLayer() ); if ( item->layout() ) { connect( &item->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, mOverviewFrameStyleButton, &QgsSymbolButton::setLayer ); } registerDataDefinedButton( mScaleDDBtn, QgsLayoutObject::MapScale ); registerDataDefinedButton( mMapRotationDDBtn, QgsLayoutObject::MapRotation ); registerDataDefinedButton( mXMinDDBtn, QgsLayoutObject::MapXMin ); registerDataDefinedButton( mYMinDDBtn, QgsLayoutObject::MapYMin ); registerDataDefinedButton( mXMaxDDBtn, QgsLayoutObject::MapXMax ); registerDataDefinedButton( mYMaxDDBtn, QgsLayoutObject::MapYMax ); registerDataDefinedButton( mAtlasMarginDDBtn, QgsLayoutObject::MapAtlasMargin ); registerDataDefinedButton( mStylePresetsDDBtn, QgsLayoutObject::MapStylePreset ); registerDataDefinedButton( mLayersDDBtn, QgsLayoutObject::MapLayers ); registerDataDefinedButton( mCRSDDBtn, QgsLayoutObject::MapCrs ); registerDataDefinedButton( mStartDateTimeDDBtn, QgsLayoutObject::StartDateTime ); registerDataDefinedButton( mEndDateTimeDDBtn, QgsLayoutObject::EndDateTime ); updateGuiElements(); loadGridEntries(); loadOverviewEntries(); connect( mMapRotationSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutMapWidget::rotationChanged ); connect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables ); connect( mMapItem, &QgsLayoutItemMap::mapRotationChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables ); blockAllSignals( false ); } void QgsLayoutMapWidget::setMasterLayout( QgsMasterLayoutInterface *masterLayout ) { if ( mItemPropertiesWidget ) mItemPropertiesWidget->setMasterLayout( masterLayout ); } void QgsLayoutMapWidget::setReportTypeString( const QString &string ) { mReportTypeString = string; mAtlasCheckBox->setTitle( tr( "Controlled by %1" ).arg( string == tr( "atlas" ) ? tr( "Atlas" ) : tr( "Report" ) ) ); mAtlasPredefinedScaleRadio->setToolTip( tr( "Use one of the predefined scales of the project where the %1 feature best fits." ).arg( string ) ); if ( mClipWidget ) mClipWidget->setReportTypeString( string ); if ( mLabelWidget ) mLabelWidget->setReportTypeString( string ); } void QgsLayoutMapWidget::setDesignerInterface( QgsLayoutDesignerInterface *iface ) { mInterface = iface; QgsLayoutItemBaseWidget::setDesignerInterface( iface ); } bool QgsLayoutMapWidget::setNewItem( QgsLayoutItem *item ) { if ( item->type() != QgsLayoutItemRegistry::LayoutMap ) return false; if ( mMapItem ) { disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements ); disconnect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables ); disconnect( mMapItem, &QgsLayoutItemMap::mapRotationChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables ); } mMapItem = qobject_cast< QgsLayoutItemMap * >( item ); mItemPropertiesWidget->setItem( mMapItem ); if ( mLabelWidget ) mLabelWidget->setItem( mMapItem ); if ( mClipWidget ) mClipWidget->setItem( mMapItem ); if ( mMapItem ) { connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements ); connect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables ); connect( mMapItem, &QgsLayoutItemMap::mapRotationChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables ); mOverviewFrameStyleButton->registerExpressionContextGenerator( mMapItem ); } updateGuiElements(); return true; } void QgsLayoutMapWidget::populateDataDefinedButtons() { updateDataDefinedButton( mScaleDDBtn ); updateDataDefinedButton( mMapRotationDDBtn ); updateDataDefinedButton( mXMinDDBtn ); updateDataDefinedButton( mYMinDDBtn ); updateDataDefinedButton( mXMaxDDBtn ); updateDataDefinedButton( mYMaxDDBtn ); updateDataDefinedButton( mAtlasMarginDDBtn ); updateDataDefinedButton( mStylePresetsDDBtn ); updateDataDefinedButton( mLayersDDBtn ); updateDataDefinedButton( mCRSDDBtn ); updateDataDefinedButton( mStartDateTimeDDBtn ); updateDataDefinedButton( mEndDateTimeDDBtn ); } void QgsLayoutMapWidget::compositionAtlasToggled( bool atlasEnabled ) { if ( atlasEnabled && mMapItem && mMapItem->layout() && mMapItem->layout()->reportContext().layer() && mMapItem->layout()->reportContext().layer()->wkbType() != Qgis::WkbType::NoGeometry ) { mAtlasCheckBox->setEnabled( true ); } else { mAtlasCheckBox->setEnabled( false ); mAtlasCheckBox->setChecked( false ); } } void QgsLayoutMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu() { // this menu is for the case when setting "keep layers" and "keep layer styles" // and the preset configuration is copied. The preset is not followed further. QMenu *menu = qobject_cast( sender() ); if ( !menu ) return; menu->clear(); const auto constMapThemes = QgsProject::instance()->mapThemeCollection()->mapThemes(); for ( const QString &presetName : constMapThemes ) { menu->addAction( presetName, this, &QgsLayoutMapWidget::keepLayersVisibilityPresetSelected ); } if ( menu->actions().isEmpty() ) menu->addAction( tr( "No presets defined" ) )->setEnabled( false ); } void QgsLayoutMapWidget::followVisibilityPresetSelected( int currentIndex ) { if ( !mMapItem ) return; if ( mBlockThemeComboChanges != 0 ) return; if ( currentIndex == -1 ) return; // doing combo box model reset QString presetName; if ( currentIndex != 0 ) { presetName = mFollowVisibilityPresetCombo->currentText(); } if ( presetName == mMapItem->followVisibilityPresetName() ) return; mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) ); mFollowVisibilityPresetCheckBox->setChecked( true ); mMapItem->setFollowVisibilityPresetName( presetName ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); } void QgsLayoutMapWidget::keepLayersVisibilityPresetSelected() { QAction *action = qobject_cast( sender() ); if ( !action ) return; const QString presetName = action->text(); const QList lst = orderedPresetVisibleLayers( presetName ); if ( mMapItem ) { mKeepLayerListCheckBox->setChecked( true ); mMapItem->setLayers( lst ); mKeepLayerStylesCheckBox->setChecked( true ); mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) ); mMapItem->setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( presetName ) ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); } } void QgsLayoutMapWidget::onMapThemesChanged() { if ( QStringListModel *model = qobject_cast( mFollowVisibilityPresetCombo->model() ) ) { mBlockThemeComboChanges++; QStringList lst; lst.append( tr( "(none)" ) ); lst += QgsProject::instance()->mapThemeCollection()->mapThemes(); model->setStringList( lst ); // select the previously selected item again const int presetModelIndex = mFollowVisibilityPresetCombo->findText( mMapItem->followVisibilityPresetName() ); mFollowVisibilityPresetCombo->blockSignals( true ); mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none mFollowVisibilityPresetCombo->blockSignals( false ); mBlockThemeComboChanges--; } } void QgsLayoutMapWidget::mapCrsChanged( const QgsCoordinateReferenceSystem &crs ) { if ( !mMapItem ) { return; } if ( mMapItem->presetCrs() == crs ) return; // try to reproject to maintain extent const QgsCoordinateReferenceSystem oldCrs = mMapItem->crs(); bool updateExtent = false; QgsRectangle newExtent; try { QgsCoordinateTransform xForm( oldCrs, crs.isValid() ? crs : QgsProject::instance()->crs(), QgsProject::instance() ); xForm.setBallparkTransformsAreAppropriate( true ); const QgsRectangle prevExtent = mMapItem->extent(); newExtent = xForm.transformBoundingBox( prevExtent ); updateExtent = true; } catch ( QgsCsException & ) { //transform failed, don't update extent } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map CRS" ) ); mMapItem->setCrs( crs ); if ( updateExtent ) mMapItem->zoomToExtent( newExtent ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); } void QgsLayoutMapWidget::overviewSymbolChanged() { QgsLayoutItemMapOverview *overview = currentOverview(); if ( !overview ) return; mMapItem->beginCommand( tr( "Change Overview Style" ), QgsLayoutItem::UndoOverviewStyle ); overview->setFrameSymbol( mOverviewFrameStyleButton->clonedSymbol() ); mMapItem->endCommand(); mMapItem->invalidateCache(); } void QgsLayoutMapWidget::showLabelSettings() { mLabelWidget = new QgsLayoutMapLabelingWidget( mMapItem ); if ( !mReportTypeString.isEmpty() ) mLabelWidget->setReportTypeString( mReportTypeString ); openPanel( mLabelWidget ); } void QgsLayoutMapWidget::showClipSettings() { mClipWidget = new QgsLayoutMapClippingWidget( mMapItem ); if ( !mReportTypeString.isEmpty() ) mClipWidget->setReportTypeString( mReportTypeString ); openPanel( mClipWidget ); } void QgsLayoutMapWidget::switchToMoveContentTool() { if ( mInterface ) mInterface->activateTool( QgsLayoutDesignerInterface::ToolMoveItemContent ); } void QgsLayoutMapWidget::aboutToShowBookmarkMenu() { mBookmarkMenu->clear(); // query the bookmarks now? or once during widget creation... Hmm. Either way, there's potentially a // delay if there's LOTS of bookmarks. Let's avoid the cost until bookmarks are actually required. if ( !mBookmarkModel ) mBookmarkModel = new QgsBookmarkManagerProxyModel( QgsApplication::bookmarkManager(), QgsProject::instance()->bookmarkManager(), this ); QMap< QString, QMenu * > groupMenus; for ( int i = 0; i < mBookmarkModel->rowCount(); ++i ) { const QString group = mBookmarkModel->data( mBookmarkModel->index( i, 0 ), QgsBookmarkManagerModel::RoleGroup ).toString(); QMenu *destMenu = mBookmarkMenu; if ( !group.isEmpty() ) { destMenu = groupMenus.value( group ); if ( !destMenu ) { destMenu = new QMenu( group, mBookmarkMenu ); groupMenus[ group ] = destMenu; } } QAction *action = new QAction( mBookmarkModel->data( mBookmarkModel->index( i, 0 ), QgsBookmarkManagerModel::RoleName ).toString(), mBookmarkMenu ); const QgsReferencedRectangle extent = mBookmarkModel->data( mBookmarkModel->index( i, 0 ), QgsBookmarkManagerModel::RoleExtent ).value< QgsReferencedRectangle >(); connect( action, &QAction::triggered, this, [ = ] { if ( !mMapItem ) { return; } QgsRectangle newExtent = extent; //transform? if ( extent.crs() != mMapItem->crs() ) { try { QgsCoordinateTransform xForm( extent.crs(), mMapItem->crs(), QgsProject::instance() ); xForm.setBallparkTransformsAreAppropriate( true ); newExtent = xForm.transformBoundingBox( newExtent ); } catch ( QgsCsException & ) { //transform failed, better not proceed return; } } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) ); mMapItem->zoomToExtent( newExtent ); mMapItem->layout()->undoStack()->endCommand(); } ); destMenu->addAction( action ); } QStringList groupKeys = groupMenus.keys(); groupKeys.sort( Qt::CaseInsensitive ); for ( int i = 0; i < groupKeys.count(); ++i ) { if ( mBookmarkMenu->actions().value( i ) ) mBookmarkMenu->insertMenu( mBookmarkMenu->actions().at( i ), groupMenus.value( groupKeys.at( i ) ) ); else mBookmarkMenu->addMenu( groupMenus.value( groupKeys.at( i ) ) ); } } void QgsLayoutMapWidget::mTemporalCheckBox_toggled( bool checked ) { if ( !mMapItem ) { return; } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Temporal Range" ) ); mMapItem->setIsTemporal( checked ); mMapItem->layout()->undoStack()->endCommand(); if ( checked ) { whileBlocking( mStartDateTime )->setDateTime( mMapItem->temporalRange().begin() ); whileBlocking( mEndDateTime )->setDateTime( mMapItem->temporalRange().end() ); } updatePreview(); } void QgsLayoutMapWidget::updateTemporalExtent() { if ( !mMapItem ) { return; } const QDateTime begin = mStartDateTime->dateTime(); const QDateTime end = mEndDateTime->dateTime(); const QgsDateTimeRange range = QgsDateTimeRange( begin, end, true, begin == end ); mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Temporal Range" ) ); mMapItem->setTemporalRange( range ); mMapItem->layout()->undoStack()->endCommand(); updatePreview(); } void QgsLayoutMapWidget::mAtlasCheckBox_toggled( bool checked ) { if ( !mMapItem ) { return; } mAtlasFixedScaleRadio->setEnabled( checked ); mAtlasMarginRadio->setEnabled( checked ); if ( mAtlasMarginRadio->isEnabled() && mAtlasMarginRadio->isChecked() ) { mAtlasMarginSpinBox->setEnabled( true ); } else { mAtlasMarginSpinBox->setEnabled( false ); } mAtlasPredefinedScaleRadio->setEnabled( checked ); if ( checked ) { //check atlas coverage layer type if ( mMapItem->layout() ) { toggleAtlasScalingOptionsByLayerType(); } } // disable predefined scales if none are defined if ( !hasPredefinedScales() ) { mAtlasPredefinedScaleRadio->setEnabled( false ); } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Atlas Driven" ) ); mMapItem->setAtlasDriven( checked ); mMapItem->layout()->undoStack()->endCommand(); updateMapForAtlas(); } void QgsLayoutMapWidget::updateMapForAtlas() { //update map if in atlas preview mode if ( mMapItem->atlasDriven() ) { mMapItem->refresh(); } else { //redraw map mMapItem->invalidateCache(); } } void QgsLayoutMapWidget::mAtlasMarginRadio_toggled( bool checked ) { mAtlasMarginSpinBox->setEnabled( checked ); if ( checked && mMapItem ) { mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) ); mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Auto ); mMapItem->layout()->undoStack()->endCommand(); updateMapForAtlas(); } } void QgsLayoutMapWidget::mAtlasMarginSpinBox_valueChanged( int value ) { if ( !mMapItem ) { return; } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Margin" ), QgsLayoutItem::UndoAtlasMargin ); mMapItem->setAtlasMargin( value / 100. ); mMapItem->layout()->undoStack()->endCommand(); updateMapForAtlas(); } void QgsLayoutMapWidget::mAtlasFixedScaleRadio_toggled( bool checked ) { if ( !mMapItem ) { return; } if ( checked ) { mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) ); mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Fixed ); mMapItem->layout()->undoStack()->endCommand(); updateMapForAtlas(); } } void QgsLayoutMapWidget::mAtlasPredefinedScaleRadio_toggled( bool checked ) { if ( !mMapItem ) { return; } if ( hasPredefinedScales() ) { if ( checked ) { mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Scales" ) ); mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Predefined ); mMapItem->layout()->undoStack()->endCommand(); updateMapForAtlas(); } } else { // restore to fixed scale if no predefined scales exist whileBlocking( mAtlasFixedScaleRadio )->setChecked( true ); mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) ); mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Fixed ); mMapItem->layout()->undoStack()->endCommand(); } } void QgsLayoutMapWidget::mScaleLineEdit_editingFinished() { if ( !mMapItem ) { return; } bool conversionSuccess = false; const double scaleDenominator = QLocale().toDouble( mScaleLineEdit->text(), &conversionSuccess ); if ( !conversionSuccess ) { return; } if ( qgsDoubleNear( scaleDenominator, mMapItem->scale() ) ) return; mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Scale" ) ); mMapItem->setScale( scaleDenominator ); mMapItem->layout()->undoStack()->endCommand(); } void QgsLayoutMapWidget::rotationChanged( double value ) { if ( !mMapItem ) { return; } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Rotation" ), QgsLayoutItem::UndoMapRotation ); mMapItem->setMapRotation( value ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); } void QgsLayoutMapWidget::setToMapCanvasExtent() { if ( !mMapItem ) { return; } QgsRectangle newExtent = mMapCanvas->mapSettings().visibleExtent(); //transform? if ( mMapCanvas->mapSettings().destinationCrs() != mMapItem->crs() ) { try { QgsCoordinateTransform xForm( mMapCanvas->mapSettings().destinationCrs(), mMapItem->crs(), QgsProject::instance() ); xForm.setBallparkTransformsAreAppropriate( true ); newExtent = xForm.transformBoundingBox( newExtent ); } catch ( QgsCsException & ) { //transform failed, better not proceed return; } } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) ); mMapItem->zoomToExtent( newExtent ); mMapItem->layout()->undoStack()->endCommand(); } void QgsLayoutMapWidget::setToMapCanvasScale() { if ( !mMapItem ) { return; } const double newScale = mMapCanvas->scale(); mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Scale" ) ); mMapItem->setScale( newScale ); mMapItem->layout()->undoStack()->endCommand(); } void QgsLayoutMapWidget::viewExtentInCanvas() { if ( !mMapItem ) { return; } const QgsRectangle currentMapExtent = mMapItem->extent(); if ( !currentMapExtent.isEmpty() ) { try { mMapCanvas->setReferencedExtent( QgsReferencedRectangle( currentMapExtent, mMapItem->crs() ) ); } catch ( QgsCsException & ) { //transform failed, better not proceed return; } mMapCanvas->refresh(); } } void QgsLayoutMapWidget::viewScaleInCanvas() { if ( !mMapItem ) { return; } const double currentScale = mMapItem->scale(); mMapCanvas->zoomScale( currentScale, true ); } void QgsLayoutMapWidget::mXMinLineEdit_editingFinished() { updateComposerExtentFromGui(); } void QgsLayoutMapWidget::mXMaxLineEdit_editingFinished() { updateComposerExtentFromGui(); } void QgsLayoutMapWidget::mYMinLineEdit_editingFinished() { updateComposerExtentFromGui(); } void QgsLayoutMapWidget::mYMaxLineEdit_editingFinished() { updateComposerExtentFromGui(); } void QgsLayoutMapWidget::updateGuiElements() { if ( !mMapItem ) { return; } blockAllSignals( true ); mLabel->setText( mMapItem->displayName() ); whileBlocking( mCrsSelector )->setCrs( mMapItem->presetCrs() ); //width, height, scale const double scale = mMapItem->scale(); //round scale to an appropriate number of decimal places if ( scale >= 10000 ) { //round scale to integer if it's greater than 10000 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 0 ) ); } else if ( scale >= 10 ) { mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 3 ) ); } else if ( scale >= 1 ) { //don't round scale if it's less than 10, instead use 4 decimal places mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 4 ) ); } else { //if scale < 1 then use 10 decimal places mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 10 ) ); } //composer map extent const QgsRectangle composerMapExtent = mMapItem->extent(); mXMinLineEdit->setText( QLocale().toString( composerMapExtent.xMinimum(), 'f', 3 ) ); mXMaxLineEdit->setText( QLocale().toString( composerMapExtent.xMaximum(), 'f', 3 ) ); mYMinLineEdit->setText( QLocale().toString( composerMapExtent.yMinimum(), 'f', 3 ) ); mYMaxLineEdit->setText( QLocale().toString( composerMapExtent.yMaximum(), 'f', 3 ) ); mMapRotationSpinBox->setValue( mMapItem->mapRotation( QgsLayoutObject::OriginalValue ) ); // follow preset checkbox mFollowVisibilityPresetCheckBox->setCheckState( mMapItem->followVisibilityPreset() ? Qt::Checked : Qt::Unchecked ); const int presetModelIndex = mFollowVisibilityPresetCombo->findText( mMapItem->followVisibilityPresetName() ); mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none //keep layer list checkbox if ( mMapItem->keepLayerSet() ) { mKeepLayerListCheckBox->setCheckState( Qt::Checked ); } else { mKeepLayerListCheckBox->setCheckState( Qt::Unchecked ); } mKeepLayerStylesCheckBox->setEnabled( mMapItem->keepLayerSet() ); mKeepLayerStylesCheckBox->setCheckState( mMapItem->keepLayerStyles() ? Qt::Checked : Qt::Unchecked ); //draw canvas items if ( mMapItem->drawAnnotations() ) { mDrawCanvasItemsCheckBox->setCheckState( Qt::Checked ); } else { mDrawCanvasItemsCheckBox->setCheckState( Qt::Unchecked ); } //atlas controls mAtlasCheckBox->setChecked( mMapItem->atlasDriven() ); mAtlasMarginSpinBox->setValue( static_cast( mMapItem->atlasMargin( QgsLayoutObject::OriginalValue ) * 100 ) ); mAtlasFixedScaleRadio->setEnabled( mMapItem->atlasDriven() ); mAtlasFixedScaleRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Fixed ); mAtlasMarginSpinBox->setEnabled( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto ); mAtlasMarginRadio->setEnabled( mMapItem->atlasDriven() ); mAtlasMarginRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto ); mAtlasPredefinedScaleRadio->setEnabled( mMapItem->atlasDriven() ); mAtlasPredefinedScaleRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Predefined ); if ( mMapItem->atlasDriven() ) { toggleAtlasScalingOptionsByLayerType(); } // disable predefined scales if none are defined if ( !hasPredefinedScales() ) { mAtlasPredefinedScaleRadio->setEnabled( false ); } mTemporalCheckBox->setChecked( mMapItem->isTemporal() ); mTemporalCheckBox->setCollapsed( !mMapItem->isTemporal() ); mStartDateTime->setEnabled( mMapItem->isTemporal() ); mEndDateTime->setEnabled( mMapItem->isTemporal() ); if ( mMapItem->isTemporal() ) { mStartDateTime->setDateTime( mMapItem->temporalRange().begin() ); mEndDateTime->setDateTime( mMapItem->temporalRange().end() ); } populateDataDefinedButtons(); loadGridEntries(); loadOverviewEntries(); blockAllSignals( false ); } void QgsLayoutMapWidget::toggleAtlasScalingOptionsByLayerType() { if ( !mMapItem ) { return; } //get atlas coverage layer QgsVectorLayer *layer = coverageLayer(); if ( !layer ) { return; } if ( QgsWkbTypes::geometryType( layer->wkbType() ) == Qgis::GeometryType::Point ) { //For point layers buffer setting makes no sense, so set "fixed scale" on and disable margin control if ( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto ) mAtlasFixedScaleRadio->setChecked( true ); mAtlasMarginRadio->setEnabled( false ); } else { //Not a point layer, so enable changes to fixed scale control mAtlasMarginRadio->setEnabled( true ); } } void QgsLayoutMapWidget::updateComposerExtentFromGui() { if ( !mMapItem ) { return; } double xmin, ymin, xmax, ymax; bool conversionSuccess; xmin = QLocale().toDouble( mXMinLineEdit->text(), &conversionSuccess ); if ( !conversionSuccess ) return; xmax = QLocale().toDouble( mXMaxLineEdit->text(), &conversionSuccess ); if ( !conversionSuccess ) return; ymin = QLocale().toDouble( mYMinLineEdit->text(), &conversionSuccess ); if ( !conversionSuccess ) return; ymax = QLocale().toDouble( mYMaxLineEdit->text(), &conversionSuccess ); if ( !conversionSuccess ) return; const QgsRectangle newExtent( xmin, ymin, xmax, ymax ); mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) ); mMapItem->setExtent( newExtent ); mMapItem->layout()->undoStack()->endCommand(); } void QgsLayoutMapWidget::blockAllSignals( bool b ) { mScaleLineEdit->blockSignals( b ); mXMinLineEdit->blockSignals( b ); mXMaxLineEdit->blockSignals( b ); mYMinLineEdit->blockSignals( b ); mYMaxLineEdit->blockSignals( b ); mDrawCanvasItemsCheckBox->blockSignals( b ); mOverviewFrameMapComboBox->blockSignals( b ); mOverviewFrameStyleButton->blockSignals( b ); mOverviewBlendModeComboBox->blockSignals( b ); mOverviewInvertCheckbox->blockSignals( b ); mOverviewCenterCheckbox->blockSignals( b ); mAtlasCheckBox->blockSignals( b ); mAtlasMarginSpinBox->blockSignals( b ); mAtlasFixedScaleRadio->blockSignals( b ); mAtlasMarginRadio->blockSignals( b ); mFollowVisibilityPresetCheckBox->blockSignals( b ); mFollowVisibilityPresetCombo->blockSignals( b ); mKeepLayerListCheckBox->blockSignals( b ); mKeepLayerStylesCheckBox->blockSignals( b ); mActionSetToCanvasExtent->blockSignals( b ); mActionUpdatePreview->blockSignals( b ); mTemporalCheckBox->blockSignals( b ); mStartDateTime->blockSignals( b ); mEndDateTime->blockSignals( b ); blockOverviewItemsSignals( b ); } void QgsLayoutMapWidget::updatePreview() { if ( !mMapItem ) { return; } mMapItem->refresh(); } void QgsLayoutMapWidget::mFollowVisibilityPresetCheckBox_stateChanged( int state ) { if ( !mMapItem ) { return; } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) ); if ( state == Qt::Checked ) { mMapItem->setFollowVisibilityPreset( true ); // mutually exclusive with keeping custom layer list mKeepLayerListCheckBox->setCheckState( Qt::Unchecked ); mKeepLayerStylesCheckBox->setCheckState( Qt::Unchecked ); mMapItem->invalidateCache(); } else { mMapItem->setFollowVisibilityPreset( false ); } mMapItem->layout()->undoStack()->endCommand(); } void QgsLayoutMapWidget::mKeepLayerListCheckBox_stateChanged( int state ) { if ( !mMapItem ) { return; } // update map mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Map Preset Changed" ) ); storeCurrentLayerSet(); mMapItem->setKeepLayerSet( state == Qt::Checked ); if ( state == Qt::Unchecked ) { mMapItem->setLayers( QList< QgsMapLayer * >() ); } mMapItem->layout()->undoStack()->endCommand(); // update gui if ( state == Qt::Checked ) { // mutually exclusive with following a preset mFollowVisibilityPresetCheckBox->setCheckState( Qt::Unchecked ); } else { mKeepLayerStylesCheckBox->setChecked( Qt::Unchecked ); mMapItem->invalidateCache(); } mKeepLayerStylesCheckBox->setEnabled( state == Qt::Checked ); } void QgsLayoutMapWidget::mKeepLayerStylesCheckBox_stateChanged( int state ) { if ( !mMapItem ) { return; } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) ); if ( state == Qt::Checked ) { mMapItem->storeCurrentLayerStyles(); mMapItem->setKeepLayerStyles( true ); } else { mMapItem->setLayerStyleOverrides( QMap() ); mMapItem->setKeepLayerStyles( false ); } mMapItem->layout()->undoStack()->endCommand(); } void QgsLayoutMapWidget::mDrawCanvasItemsCheckBox_stateChanged( int state ) { if ( !mMapItem ) { return; } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Map Item" ) ); mMapItem->setDrawAnnotations( state == Qt::Checked ); mMapItem->invalidateCache(); mMapItem->layout()->undoStack()->endCommand(); } void QgsLayoutMapWidget::atlasLayerChanged( QgsVectorLayer *layer ) { if ( !layer || layer->wkbType() == Qgis::WkbType::NoGeometry ) { //geometryless layer, disable atlas control mAtlasCheckBox->setChecked( false ); mAtlasCheckBox->setEnabled( false ); return; } else { mAtlasCheckBox->setEnabled( true ); } // enable or disable fixed scale control based on layer type if ( mAtlasCheckBox->isChecked() ) toggleAtlasScalingOptionsByLayerType(); } bool QgsLayoutMapWidget::hasPredefinedScales() const { // first look at project's scales const QVector< double > scales( QgsProject::instance()->viewSettings()->mapScales() ); const bool hasProjectScales( QgsProject::instance()->viewSettings()->useProjectScales() ); if ( !hasProjectScales || scales.isEmpty() ) { // default to global map tool scales const QgsSettings settings; const QStringList scales = QgsSettingsRegistryCore::settingsMapScales->value(); return !scales.isEmpty() && !scales[0].isEmpty(); } return true; } void QgsLayoutMapWidget::mAddGridPushButton_clicked() { if ( !mMapItem ) { return; } const QString itemName = tr( "Grid %1" ).arg( mMapItem->grids()->size() + 1 ); QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( itemName, mMapItem ); mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Add Map Grid" ) ); mMapItem->grids()->addGrid( grid ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->updateBoundingRect(); mMapItem->update(); addGridListItem( grid->id(), grid->name() ); mGridListWidget->setCurrentRow( 0 ); mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr ); } void QgsLayoutMapWidget::mRemoveGridPushButton_clicked() { QListWidgetItem *item = mGridListWidget->currentItem(); if ( !item ) { return; } mMapItem->beginCommand( tr( "Remove Grid" ) ); mMapItem->grids()->removeGrid( item->data( Qt::UserRole ).toString() ); QListWidgetItem *delItem = mGridListWidget->takeItem( mGridListWidget->row( item ) ); delete delItem; mMapItem->endCommand(); mMapItem->updateBoundingRect(); mMapItem->update(); } void QgsLayoutMapWidget::mGridUpButton_clicked() { QListWidgetItem *item = mGridListWidget->currentItem(); if ( !item ) { return; } const int row = mGridListWidget->row( item ); if ( row < 1 ) { return; } mGridListWidget->takeItem( row ); mGridListWidget->insertItem( row - 1, item ); mGridListWidget->setCurrentItem( item ); mMapItem->beginCommand( tr( "Move Grid Up" ) ); mMapItem->grids()->moveGridUp( item->data( Qt::UserRole ).toString() ); mMapItem->endCommand(); mMapItem->update(); } void QgsLayoutMapWidget::mGridDownButton_clicked() { QListWidgetItem *item = mGridListWidget->currentItem(); if ( !item ) { return; } const int row = mGridListWidget->row( item ); if ( mGridListWidget->count() <= row ) { return; } mGridListWidget->takeItem( row ); mGridListWidget->insertItem( row + 1, item ); mGridListWidget->setCurrentItem( item ); mMapItem->beginCommand( tr( "Move Grid Down" ) ); mMapItem->grids()->moveGridDown( item->data( Qt::UserRole ).toString() ); mMapItem->endCommand(); mMapItem->update(); } QgsLayoutItemMapGrid *QgsLayoutMapWidget::currentGrid() { if ( !mMapItem ) { return nullptr; } QListWidgetItem *item = mGridListWidget->currentItem(); if ( !item ) { return nullptr; } return mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() ); } void QgsLayoutMapWidget::mGridListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem * ) { mGridPropertiesButton->setEnabled( static_cast< bool >( current ) ); } void QgsLayoutMapWidget::mGridListWidget_itemChanged( QListWidgetItem *item ) { if ( !mMapItem ) { return; } QgsLayoutItemMapGrid *grid = mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() ); if ( !grid ) { return; } mMapItem->beginCommand( tr( "Rename Grid" ) ); grid->setName( item->text() ); mMapItem->endCommand(); } void QgsLayoutMapWidget::mGridPropertiesButton_clicked() { if ( !mMapItem ) { return; } QgsLayoutItemMapGrid *grid = currentGrid(); if ( !grid ) { return; } QgsLayoutMapGridWidget *w = new QgsLayoutMapGridWidget( grid, mMapItem ); w->setDesignerInterface( mInterface ); openPanel( w ); } QListWidgetItem *QgsLayoutMapWidget::addGridListItem( const QString &id, const QString &name ) { QListWidgetItem *item = new QListWidgetItem( name, nullptr ); item->setData( Qt::UserRole, id ); item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable ); mGridListWidget->insertItem( 0, item ); return item; } void QgsLayoutMapWidget::loadGridEntries() { //save selection QSet selectedIds; const QList itemSelection = mGridListWidget->selectedItems(); QList::const_iterator sIt = itemSelection.constBegin(); for ( ; sIt != itemSelection.constEnd(); ++sIt ) { selectedIds.insert( ( *sIt )->data( Qt::UserRole ).toString() ); } mGridListWidget->clear(); if ( !mMapItem ) { return; } //load all composer grids into list widget const QList< QgsLayoutItemMapGrid * > grids = mMapItem->grids()->asList(); QList< QgsLayoutItemMapGrid * >::const_iterator gridIt = grids.constBegin(); for ( ; gridIt != grids.constEnd(); ++gridIt ) { QListWidgetItem *item = addGridListItem( ( *gridIt )->id(), ( *gridIt )->name() ); if ( selectedIds.contains( ( *gridIt )->id() ) ) { item->setSelected( true ); mGridListWidget->setCurrentItem( item ); } } if ( mGridListWidget->currentItem() ) { mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr ); } else { mGridListWidget_currentItemChanged( nullptr, nullptr ); } } void QgsLayoutMapWidget::mAddOverviewPushButton_clicked() { if ( !mMapItem ) { return; } const QString itemName = tr( "Overview %1" ).arg( mMapItem->overviews()->size() + 1 ); QgsLayoutItemMapOverview *overview = new QgsLayoutItemMapOverview( itemName, mMapItem ); mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Add Map Overview" ) ); mMapItem->overviews()->addOverview( overview ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); addOverviewListItem( overview->id(), overview->name() ); mOverviewListWidget->setCurrentRow( 0 ); } void QgsLayoutMapWidget::mRemoveOverviewPushButton_clicked() { QListWidgetItem *item = mOverviewListWidget->currentItem(); if ( !item ) { return; } mMapItem->beginCommand( tr( "Remove Map Overview" ) ); mMapItem->overviews()->removeOverview( item->data( Qt::UserRole ).toString() ); mMapItem->endCommand(); QListWidgetItem *delItem = mOverviewListWidget->takeItem( mOverviewListWidget->row( item ) ); delete delItem; mMapItem->invalidateCache(); } void QgsLayoutMapWidget::mOverviewUpButton_clicked() { QListWidgetItem *item = mOverviewListWidget->currentItem(); if ( !item ) { return; } const int row = mOverviewListWidget->row( item ); if ( row < 1 ) { return; } mOverviewListWidget->takeItem( row ); mOverviewListWidget->insertItem( row - 1, item ); mOverviewListWidget->setCurrentItem( item ); mMapItem->beginCommand( tr( "Move Overview Up" ) ); mMapItem->overviews()->moveOverviewUp( item->data( Qt::UserRole ).toString() ); mMapItem->endCommand(); mMapItem->invalidateCache(); } void QgsLayoutMapWidget::mOverviewDownButton_clicked() { QListWidgetItem *item = mOverviewListWidget->currentItem(); if ( !item ) { return; } const int row = mOverviewListWidget->row( item ); if ( mOverviewListWidget->count() <= row ) { return; } mOverviewListWidget->takeItem( row ); mOverviewListWidget->insertItem( row + 1, item ); mOverviewListWidget->setCurrentItem( item ); mMapItem->beginCommand( tr( "Move Overview Down" ) ); mMapItem->overviews()->moveOverviewDown( item->data( Qt::UserRole ).toString() ); mMapItem->endCommand(); mMapItem->invalidateCache(); } QgsLayoutItemMapOverview *QgsLayoutMapWidget::currentOverview() { if ( !mMapItem ) { return nullptr; } QListWidgetItem *item = mOverviewListWidget->currentItem(); if ( !item ) { return nullptr; } return mMapItem->overviews()->overview( item->data( Qt::UserRole ).toString() ); } void QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous ) { Q_UNUSED( previous ) if ( !current ) { mOverviewCheckBox->setEnabled( false ); return; } mOverviewCheckBox->setEnabled( true ); setOverviewItems( mMapItem->overviews()->overview( current->data( Qt::UserRole ).toString() ) ); } void QgsLayoutMapWidget::mOverviewListWidget_itemChanged( QListWidgetItem *item ) { if ( !mMapItem ) { return; } QgsLayoutItemMapOverview *overview = mMapItem->overviews()->overview( item->data( Qt::UserRole ).toString() ); if ( !overview ) { return; } mMapItem->beginCommand( QStringLiteral( "Rename Overview" ) ); overview->setName( item->text() ); mMapItem->endCommand(); if ( item->isSelected() ) { //update checkbox title if item is current item mOverviewCheckBox->setTitle( tr( "Draw \"%1\" overview" ).arg( overview->name() ) ); } } void QgsLayoutMapWidget::setOverviewItemsEnabled( bool enabled ) { mOverviewFrameMapLabel->setEnabled( enabled ); mOverviewFrameMapComboBox->setEnabled( enabled ); mOverviewFrameStyleLabel->setEnabled( enabled ); mOverviewFrameStyleButton->setEnabled( enabled ); mOverviewBlendModeLabel->setEnabled( enabled ); mOverviewBlendModeComboBox->setEnabled( enabled ); mOverviewInvertCheckbox->setEnabled( enabled ); mOverviewCenterCheckbox->setEnabled( enabled ); mOverviewPositionComboBox->setEnabled( enabled ); const QgsLayoutItemMapItem::StackingPosition currentStackingPos = static_cast< QgsLayoutItemMapItem::StackingPosition >( mOverviewPositionComboBox->currentData().toInt() ); mOverviewStackingLayerComboBox->setEnabled( enabled && ( currentStackingPos == QgsLayoutItemMapItem::StackAboveMapLayer || currentStackingPos == QgsLayoutItemMapItem::StackBelowMapLayer ) ); } void QgsLayoutMapWidget::blockOverviewItemsSignals( const bool block ) { mOverviewFrameMapComboBox->blockSignals( block ); mOverviewFrameStyleButton->blockSignals( block ); mOverviewBlendModeComboBox->blockSignals( block ); mOverviewInvertCheckbox->blockSignals( block ); mOverviewCenterCheckbox->blockSignals( block ); mOverviewPositionComboBox->blockSignals( block ); mOverviewStackingLayerComboBox->blockSignals( block ); } void QgsLayoutMapWidget::setOverviewItems( QgsLayoutItemMapOverview *overview ) { if ( !overview ) { return; } blockOverviewItemsSignals( true ); mOverviewCheckBox->setTitle( tr( "Draw \"%1\" overview" ).arg( overview->name() ) ); mOverviewCheckBox->setChecked( overview->enabled() ); //overview frame mOverviewFrameMapComboBox->setItem( overview->linkedMap() ); //overview frame blending mode mOverviewBlendModeComboBox->setBlendMode( overview->blendMode() ); //overview inverted mOverviewInvertCheckbox->setChecked( overview->inverted() ); //center overview mOverviewCenterCheckbox->setChecked( overview->centered() ); mOverviewPositionComboBox->setCurrentIndex( mOverviewPositionComboBox->findData( overview->stackingPosition() ) ); mOverviewStackingLayerComboBox->setLayer( overview->stackingLayer() ); mOverviewStackingLayerComboBox->setEnabled( mOverviewPositionComboBox->isEnabled() && ( overview->stackingPosition() == QgsLayoutItemMapItem::StackAboveMapLayer || overview->stackingPosition() == QgsLayoutItemMapItem::StackBelowMapLayer ) ); mOverviewFrameStyleButton->setSymbol( overview->frameSymbol()->clone() ); blockOverviewItemsSignals( false ); } void QgsLayoutMapWidget::storeCurrentLayerSet() { if ( !mMapItem ) return; QList layers = mMapCanvas->mapSettings().layers(); mMapItem->setLayers( layers ); if ( mMapItem->keepLayerStyles() ) { // also store styles associated with the layers mMapItem->storeCurrentLayerStyles(); } } QList QgsLayoutMapWidget::orderedPresetVisibleLayers( const QString &name ) const { const QStringList visibleIds = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayerIds( name ); // also make sure to order the layers according to map canvas order QList lst; const auto constLayerOrder = QgsProject::instance()->layerTreeRoot()->layerOrder(); for ( QgsMapLayer *layer : constLayerOrder ) { if ( visibleIds.contains( layer->id() ) ) { lst << layer; } } return lst; } QListWidgetItem *QgsLayoutMapWidget::addOverviewListItem( const QString &id, const QString &name ) { QListWidgetItem *item = new QListWidgetItem( name, nullptr ); item->setData( Qt::UserRole, id ); item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable ); mOverviewListWidget->insertItem( 0, item ); return item; } void QgsLayoutMapWidget::loadOverviewEntries() { //save selection QSet selectedIds; const QList itemSelection = mOverviewListWidget->selectedItems(); QList::const_iterator sIt = itemSelection.constBegin(); for ( ; sIt != itemSelection.constEnd(); ++sIt ) { selectedIds.insert( ( *sIt )->data( Qt::UserRole ).toString() ); } mOverviewListWidget->clear(); if ( !mMapItem ) { return; } mOverviewFrameMapComboBox->setExceptedItemList( QList< QgsLayoutItem * >() << mMapItem ); //load all composer overviews into list widget const QList< QgsLayoutItemMapOverview * > overviews = mMapItem->overviews()->asList(); QList< QgsLayoutItemMapOverview * >::const_iterator overviewIt = overviews.constBegin(); for ( ; overviewIt != overviews.constEnd(); ++overviewIt ) { QListWidgetItem *item = addOverviewListItem( ( *overviewIt )->id(), ( *overviewIt )->name() ); if ( selectedIds.contains( ( *overviewIt )->id() ) ) { item->setSelected( true ); mOverviewListWidget->setCurrentItem( item ); } } if ( mOverviewListWidget->currentItem() ) { mOverviewListWidget_currentItemChanged( mOverviewListWidget->currentItem(), nullptr ); } else { mOverviewListWidget_currentItemChanged( nullptr, nullptr ); } } void QgsLayoutMapWidget::mOverviewCheckBox_toggled( bool state ) { QgsLayoutItemMapOverview *overview = currentOverview(); if ( !overview ) { return; } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Overview Display Toggled" ) ); overview->setEnabled( state ); mMapItem->invalidateCache(); mMapItem->layout()->undoStack()->endCommand(); } void QgsLayoutMapWidget::overviewMapChanged( QgsLayoutItem *item ) { QgsLayoutItemMapOverview *overview = currentOverview(); if ( !overview ) { return; } QgsLayoutItemMap *map = dynamic_cast< QgsLayoutItemMap * >( item ); if ( !map ) return; mMapItem->beginCommand( tr( "Change Overview Map" ) ); overview->setLinkedMap( map ); mMapItem->invalidateCache(); mMapItem->endCommand(); } void QgsLayoutMapWidget::mOverviewBlendModeComboBox_currentIndexChanged( int index ) { Q_UNUSED( index ) QgsLayoutItemMapOverview *overview = currentOverview(); if ( !overview ) { return; } mMapItem->beginCommand( tr( "Change Overview Blend Mode" ) ); overview->setBlendMode( mOverviewBlendModeComboBox->blendMode() ); mMapItem->invalidateCache(); mMapItem->endCommand(); } void QgsLayoutMapWidget::mOverviewInvertCheckbox_toggled( bool state ) { QgsLayoutItemMapOverview *overview = currentOverview(); if ( !overview ) { return; } mMapItem->beginCommand( tr( "Toggle Overview Inverted" ) ); overview->setInverted( state ); mMapItem->invalidateCache(); mMapItem->endCommand(); } void QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled( bool state ) { QgsLayoutItemMapOverview *overview = currentOverview(); if ( !overview ) { return; } mMapItem->beginCommand( tr( "Toggle Overview Centered" ) ); overview->setCentered( state ); mMapItem->invalidateCache(); mMapItem->endCommand(); } void QgsLayoutMapWidget::overviewStackingChanged( int ) { QgsLayoutItemMapOverview *overview = currentOverview(); if ( !overview ) { return; } mMapItem->beginCommand( tr( "Change Overview Position" ) ); overview->setStackingPosition( static_cast< QgsLayoutItemMapItem::StackingPosition >( mOverviewPositionComboBox->currentData().toInt() ) ); mMapItem->invalidateCache(); mMapItem->endCommand(); switch ( overview->stackingPosition() ) { case QgsLayoutItemMapItem::StackAboveMapLabels: case QgsLayoutItemMapItem::StackBelowMap: case QgsLayoutItemMapItem::StackBelowMapLabels: mOverviewStackingLayerComboBox->setEnabled( false ); break; case QgsLayoutItemMapItem::StackAboveMapLayer: case QgsLayoutItemMapItem::StackBelowMapLayer: mOverviewStackingLayerComboBox->setEnabled( true ); break; } } void QgsLayoutMapWidget::overviewStackingLayerChanged( QgsMapLayer *layer ) { QgsLayoutItemMapOverview *overview = currentOverview(); if ( !overview ) { return; } mMapItem->beginCommand( tr( "Change Overview Position" ) ); overview->setStackingLayer( layer ); mMapItem->invalidateCache(); mMapItem->endCommand(); } // // QgsLayoutMapLabelingWidget // QgsLayoutMapLabelingWidget::QgsLayoutMapLabelingWidget( QgsLayoutItemMap *map ) : QgsLayoutItemBaseWidget( nullptr, map ) , mMapItem( map ) { setupUi( this ); setPanelTitle( tr( "Label Settings" ) ); mLabelBoundarySpinBox->setClearValue( 0 ); mLabelBoundarySpinBox->setShowClearButton( true ); mLabelBoundaryUnitsCombo->linkToWidget( mLabelBoundarySpinBox ); mLabelBoundaryUnitsCombo->setConverter( &mMapItem->layout()->renderContext().measurementConverter() ); connect( mLabelBoundaryUnitsCombo, &QgsLayoutUnitsComboBox::unitChanged, this, &QgsLayoutMapLabelingWidget::labelMarginUnitsChanged ); connect( mLabelBoundarySpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutMapLabelingWidget::labelMarginChanged ); connect( mShowPartialLabelsCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showPartialsToggled ); connect( mShowUnplacedCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showUnplacedToggled ); registerDataDefinedButton( mLabelMarginDDBtn, QgsLayoutObject::MapLabelMargin ); setNewItem( map ); } bool QgsLayoutMapLabelingWidget::setNewItem( QgsLayoutItem *item ) { if ( item->type() != QgsLayoutItemRegistry::LayoutMap ) return false; if ( mMapItem ) { disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements ); } mMapItem = qobject_cast< QgsLayoutItemMap * >( item ); if ( mMapItem ) { connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements ); } updateGuiElements(); return true; } void QgsLayoutMapLabelingWidget::updateGuiElements() { whileBlocking( mLabelBoundarySpinBox )->setValue( mMapItem->labelMargin().length() ); whileBlocking( mLabelBoundaryUnitsCombo )->setUnit( mMapItem->labelMargin().units() ); whileBlocking( mShowPartialLabelsCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowPartialLabels ); whileBlocking( mShowUnplacedCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowUnplacedLabels ); if ( mBlockingItemsListView->model() ) { QAbstractItemModel *oldModel = mBlockingItemsListView->model(); mBlockingItemsListView->setModel( nullptr ); oldModel->deleteLater(); } QgsLayoutMapItemBlocksLabelsModel *model = new QgsLayoutMapItemBlocksLabelsModel( mMapItem, mMapItem->layout()->itemsModel(), mBlockingItemsListView ); mBlockingItemsListView->setModel( model ); updateDataDefinedButton( mLabelMarginDDBtn ); } void QgsLayoutMapLabelingWidget::labelMarginChanged( double val ) { if ( !mMapItem ) return; mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin ); mMapItem->setLabelMargin( QgsLayoutMeasurement( val, mLabelBoundaryUnitsCombo->unit() ) ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); } void QgsLayoutMapLabelingWidget::labelMarginUnitsChanged() { if ( !mMapItem ) return; mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin ); mMapItem->setLabelMargin( QgsLayoutMeasurement( mLabelBoundarySpinBox->value(), mLabelBoundaryUnitsCombo->unit() ) ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); } void QgsLayoutMapLabelingWidget::showPartialsToggled( bool checked ) { if ( !mMapItem ) return; mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) ); QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags(); if ( checked ) flags |= QgsLayoutItemMap::ShowPartialLabels; else flags &= ~QgsLayoutItemMap::ShowPartialLabels; mMapItem->setMapFlags( flags ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); } void QgsLayoutMapLabelingWidget::showUnplacedToggled( bool checked ) { if ( !mMapItem ) return; mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) ); QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags(); if ( checked ) flags |= QgsLayoutItemMap::ShowUnplacedLabels; else flags &= ~QgsLayoutItemMap::ShowUnplacedLabels; mMapItem->setMapFlags( flags ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); } QgsLayoutMapItemBlocksLabelsModel::QgsLayoutMapItemBlocksLabelsModel( QgsLayoutItemMap *map, QgsLayoutModel *layoutModel, QObject *parent ) : QSortFilterProxyModel( parent ) , mLayoutModel( layoutModel ) , mMapItem( map ) { setSourceModel( layoutModel ); } int QgsLayoutMapItemBlocksLabelsModel::columnCount( const QModelIndex & ) const { return 1; } QVariant QgsLayoutMapItemBlocksLabelsModel::data( const QModelIndex &i, int role ) const { if ( !i.isValid() ) return QVariant(); if ( i.column() != 0 ) return QVariant(); const QModelIndex sourceIndex = mapToSource( index( i.row(), QgsLayoutModel::ItemId, i.parent() ) ); QgsLayoutItem *item = mLayoutModel->itemFromIndex( mapToSource( i ) ); if ( !item ) { return QVariant(); } switch ( role ) { case Qt::CheckStateRole: switch ( i.column() ) { case 0: return mMapItem ? ( mMapItem->isLabelBlockingItem( item ) ? Qt::Checked : Qt::Unchecked ) : Qt::Unchecked; default: return QVariant(); } default: return mLayoutModel->data( sourceIndex, role ); } } bool QgsLayoutMapItemBlocksLabelsModel::setData( const QModelIndex &index, const QVariant &value, int role ) { Q_UNUSED( role ) if ( !index.isValid() ) return false; QgsLayoutItem *item = mLayoutModel->itemFromIndex( mapToSource( index ) ); if ( !item || !mMapItem ) { return false; } mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Blocking Items" ) ); if ( value.toBool() ) { mMapItem->addLabelBlockingItem( item ); } else { mMapItem->removeLabelBlockingItem( item ); } emit dataChanged( index, index, QVector() << role ); mMapItem->layout()->undoStack()->endCommand(); mMapItem->invalidateCache(); return true; } Qt::ItemFlags QgsLayoutMapItemBlocksLabelsModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = QSortFilterProxyModel::flags( index ); if ( ! index.isValid() ) { return flags ; } switch ( index.column() ) { case 0: return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; default: return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } } bool QgsLayoutMapItemBlocksLabelsModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const { QgsLayoutItem *item = mLayoutModel->itemFromIndex( mLayoutModel->index( source_row, 0, source_parent ) ); if ( !item || item == mMapItem ) { return false; } return true; } // // QgsLayoutMapClippingWidget // QgsLayoutMapClippingWidget::QgsLayoutMapClippingWidget( QgsLayoutItemMap *map ) : QgsLayoutItemBaseWidget( nullptr, map ) , mMapItem( map ) { setupUi( this ); setPanelTitle( tr( "Clipping Settings" ) ); mLayerModel = new QgsMapLayerModel( this ); mLayerModel->setItemsCheckable( true ); mLayersTreeView->setModel( mLayerModel ); mAtlasClippingTypeComboBox->addItem( tr( "Clip During Render Only" ), static_cast< int >( QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly ) ); mAtlasClippingTypeComboBox->addItem( tr( "Clip Feature Before Render" ), static_cast< int >( QgsMapClippingRegion::FeatureClippingType::ClipToIntersection ) ); mAtlasClippingTypeComboBox->addItem( tr( "Render Intersecting Features Unchanged" ), static_cast< int >( QgsMapClippingRegion::FeatureClippingType::NoClipping ) ); for ( int i = 0; i < mAtlasClippingTypeComboBox->count(); ++i ) { mItemClippingTypeComboBox->addItem( mAtlasClippingTypeComboBox->itemText( i ), mAtlasClippingTypeComboBox->itemData( i ) ); } mClipItemComboBox->setCurrentLayout( map->layout() ); mClipItemComboBox->setItemFlags( QgsLayoutItem::FlagProvidesClipPath ); connect( mRadioClipSelectedLayers, &QRadioButton::toggled, mLayersTreeView, &QWidget::setEnabled ); mLayersTreeView->setEnabled( false ); mRadioClipAllLayers->setChecked( true ); connect( mClipToAtlasCheckBox, &QGroupBox::toggled, this, [ = ]( bool active ) { if ( !mBlockUpdates ) { mMapItem->beginCommand( tr( "Toggle Atlas Clipping" ) ); mMapItem->atlasClippingSettings()->setEnabled( active ); mMapItem->endCommand(); } } ); connect( mForceLabelsInsideCheckBox, &QCheckBox::toggled, this, [ = ]( bool active ) { if ( !mBlockUpdates ) { mMapItem->beginCommand( tr( "Change Atlas Clipping Label Behavior" ) ); mMapItem->atlasClippingSettings()->setForceLabelsInsideFeature( active ); mMapItem->endCommand(); } } ); connect( mAtlasClippingTypeComboBox, qOverload( &QComboBox::currentIndexChanged ), this, [ = ] { if ( !mBlockUpdates ) { mMapItem->beginCommand( tr( "Change Atlas Clipping Behavior" ) ); mMapItem->atlasClippingSettings()->setFeatureClippingType( static_cast< QgsMapClippingRegion::FeatureClippingType >( mAtlasClippingTypeComboBox->currentData().toInt() ) ); mMapItem->endCommand(); } } ); connect( mRadioClipSelectedLayers, &QCheckBox::toggled, this, [ = ]( bool active ) { if ( active && !mBlockUpdates ) { mBlockUpdates = true; mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) ); mMapItem->atlasClippingSettings()->setRestrictToLayers( true ); mMapItem->endCommand(); mBlockUpdates = false; } } ); connect( mRadioClipAllLayers, &QCheckBox::toggled, this, [ = ]( bool active ) { if ( active && !mBlockUpdates ) { mBlockUpdates = true; mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) ); mMapItem->atlasClippingSettings()->setRestrictToLayers( false ); mMapItem->endCommand(); mBlockUpdates = false; } } ); connect( mLayerModel, &QgsMapLayerModel::dataChanged, this, [ = ]( const QModelIndex &, const QModelIndex &, const QVector &roles = QVector() ) { if ( !roles.contains( Qt::CheckStateRole ) ) return; if ( !mBlockUpdates ) { mBlockUpdates = true; mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) ); mMapItem->atlasClippingSettings()->setLayersToClip( mLayerModel->layersChecked() ); mMapItem->endCommand(); mBlockUpdates = false; } } ); // item clipping widgets connect( mClipToItemCheckBox, &QGroupBox::toggled, this, [ = ]( bool active ) { if ( !mBlockUpdates ) { mMapItem->beginCommand( tr( "Toggle Map Clipping" ) ); mMapItem->itemClippingSettings()->setEnabled( active ); mMapItem->endCommand(); } } ); connect( mItemClippingTypeComboBox, qOverload( &QComboBox::currentIndexChanged ), this, [ = ] { if ( !mBlockUpdates ) { mMapItem->beginCommand( tr( "Change Map Clipping Behavior" ) ); mMapItem->itemClippingSettings()->setFeatureClippingType( static_cast< QgsMapClippingRegion::FeatureClippingType >( mItemClippingTypeComboBox->currentData().toInt() ) ); mMapItem->endCommand(); } } ); connect( mForceLabelsInsideItemCheckBox, &QCheckBox::toggled, this, [ = ]( bool active ) { if ( !mBlockUpdates ) { mMapItem->beginCommand( tr( "Change Map Clipping Label Behavior" ) ); mMapItem->itemClippingSettings()->setForceLabelsInsideClipPath( active ); mMapItem->endCommand(); } } ); connect( mClipItemComboBox, &QgsLayoutItemComboBox::itemChanged, this, [ = ]( QgsLayoutItem * item ) { if ( !mBlockUpdates ) { mMapItem->beginCommand( tr( "Change Map Clipping Item" ) ); mMapItem->itemClippingSettings()->setSourceItem( item ); mMapItem->endCommand(); } } ); setNewItem( map ); connect( &map->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutMapClippingWidget::atlasLayerChanged ); if ( QgsLayoutAtlas *atlas = layoutAtlas() ) { connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutMapClippingWidget::atlasToggled ); atlasToggled( atlas->enabled() ); } } void QgsLayoutMapClippingWidget::setReportTypeString( const QString &string ) { mClipToAtlasCheckBox->setTitle( tr( "Clip to %1 feature" ).arg( string ) ); mClipToAtlasLabel->setText( tr( "When enabled, map layers will be automatically clipped to the boundary of the current %1 feature." ).arg( string ) ); mForceLabelsInsideCheckBox->setText( tr( "Force labels inside %1 feature" ).arg( string ) ); } bool QgsLayoutMapClippingWidget::setNewItem( QgsLayoutItem *item ) { if ( item->type() != QgsLayoutItemRegistry::LayoutMap ) return false; if ( mMapItem ) { disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapClippingWidget::updateGuiElements ); } mMapItem = qobject_cast< QgsLayoutItemMap * >( item ); if ( mMapItem ) { connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapClippingWidget::updateGuiElements ); } updateGuiElements(); return true; } void QgsLayoutMapClippingWidget::updateGuiElements() { if ( mBlockUpdates ) return; mBlockUpdates = true; mClipToAtlasCheckBox->setChecked( mMapItem->atlasClippingSettings()->enabled() ); mAtlasClippingTypeComboBox->setCurrentIndex( mAtlasClippingTypeComboBox->findData( static_cast< int >( mMapItem->atlasClippingSettings()->featureClippingType() ) ) ); mForceLabelsInsideCheckBox->setChecked( mMapItem->atlasClippingSettings()->forceLabelsInsideFeature() ); mRadioClipAllLayers->setChecked( !mMapItem->atlasClippingSettings()->restrictToLayers() ); mRadioClipSelectedLayers->setChecked( mMapItem->atlasClippingSettings()->restrictToLayers() ); mLayerModel->setLayersChecked( mMapItem->atlasClippingSettings()->layersToClip() ); mClipToItemCheckBox->setChecked( mMapItem->itemClippingSettings()->enabled() ); mItemClippingTypeComboBox->setCurrentIndex( mItemClippingTypeComboBox->findData( static_cast< int >( mMapItem->itemClippingSettings()->featureClippingType() ) ) ); mForceLabelsInsideItemCheckBox->setChecked( mMapItem->itemClippingSettings()->forceLabelsInsideClipPath() ); mClipItemComboBox->setItem( mMapItem->itemClippingSettings()->sourceItem() ); mBlockUpdates = false; } void QgsLayoutMapClippingWidget::atlasLayerChanged( QgsVectorLayer *layer ) { if ( !layer || layer->geometryType() != Qgis::GeometryType::Polygon ) { //non-polygon layer, disable atlas control mClipToAtlasCheckBox->setChecked( false ); mClipToAtlasCheckBox->setEnabled( false ); return; } else { mClipToAtlasCheckBox->setEnabled( true ); } } void QgsLayoutMapClippingWidget::atlasToggled( bool atlasEnabled ) { if ( atlasEnabled && mMapItem && mMapItem->layout() && mMapItem->layout()->reportContext().layer() && mMapItem->layout()->reportContext().layer()->geometryType() == Qgis::GeometryType::Polygon ) { mClipToAtlasCheckBox->setEnabled( true ); } else { mClipToAtlasCheckBox->setEnabled( false ); mClipToAtlasCheckBox->setChecked( false ); } }