QGIS/src/gui/layout/qgslayoutmapwidget.cpp
Alessandro Pasotti 4f531bb35f Fix map layout item lock group layers
Fix #53379 plus unreported lock layers lost when reloading project
2023-06-10 14:36:59 +02:00

2187 lines
71 KiB
C++

/***************************************************************************
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 <QMenu>
#include <QMessageBox>
#include <QStringListModel>
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<void ( QComboBox::* )( int )>( &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<void ( QComboBox::* )( int )>( &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<void ( QComboBox::* )( int )>( &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<QMenu *>( 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<QAction *>( sender() );
if ( !action )
return;
const QString presetName = action->text();
const QList<QgsMapLayer *> 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<QStringListModel *>( 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<QgsFillSymbol>() );
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<int>( 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<QString, QString>() );
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<QString> selectedIds;
const QList<QListWidgetItem *> itemSelection = mGridListWidget->selectedItems();
QList<QListWidgetItem *>::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<QgsMapLayer *> layers = mMapCanvas->mapSettings().layers();
mMapItem->setLayers( layers );
if ( mMapItem->keepLayerStyles() )
{
// also store styles associated with the layers
mMapItem->storeCurrentLayerStyles();
}
}
QList<QgsMapLayer *> 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<QgsMapLayer *> 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<QString> selectedIds;
const QList<QListWidgetItem *> itemSelection = mOverviewListWidget->selectedItems();
QList<QListWidgetItem *>::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<int>() << 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<int>( &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<int> &roles = QVector<int>() )
{
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<int>( &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( "<b>When enabled, map layers will be automatically clipped to the boundary of the current %1 feature.</b>" ).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 );
}
}