32 #include <QMessageBox>
35 static QList<QgsExpressionContextScope *> _globalProjectAtlasMapLayerScopes(
QgsMapCanvas *mapCanvas,
const QgsMapLayer *layer )
37 QList<QgsExpressionContextScope *> scopes;
67 mCopyAction =
new QAction( tr(
"Copy" ),
this );
68 mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
69 mPasteAction =
new QAction( tr(
"Paste" ),
this );
70 mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
71 mDeleteAction =
new QAction( tr(
"Remove Rule" ),
this );
72 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
74 viewRules->addAction( mCopyAction );
75 viewRules->addAction( mPasteAction );
76 viewRules->addAction( mDeleteAction );
78 connect( viewRules, &QAbstractItemView::doubleClicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )(
const QModelIndex & )
>( &QgsRuleBasedLabelingWidget::editRule ) );
80 connect( btnAddRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::addRule );
81 connect( btnEditRule, &QAbstractButton::clicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )()
>( &QgsRuleBasedLabelingWidget::editRule ) );
82 connect( btnRemoveRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::removeRule );
83 connect( mCopyAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::copy );
84 connect( mPasteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::paste );
85 connect( mDeleteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::removeRule );
96 std::unique_ptr< QgsPalLayerSettings > newSettings = qgis::make_unique< QgsPalLayerSettings >( mLayer->
labeling()->
settings() );
97 newSettings->drawLabels =
true;
106 viewRules->setModel( mModel );
124 mCopyAction->setShortcut( QKeySequence() );
126 mPasteAction->setShortcut( QKeySequence() );
128 mDeleteAction->setShortcut( QKeySequence() );
133 void QgsRuleBasedLabelingWidget::addRule()
142 QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
143 mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
144 QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
145 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
150 int rows = mModel->rowCount();
151 mModel->insertRule( QModelIndex(), rows, newrule );
152 QModelIndex newindex = mModel->index( rows, 0 );
153 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
158 void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted(
QgsPanelWidget *panel )
160 QgsLabelingRulePropsWidget *widget = qobject_cast<QgsLabelingRulePropsWidget *>( panel );
163 QModelIndex index = viewRules->selectionModel()->currentIndex();
164 mModel->updateRule( index.parent(), index.row() );
167 void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
169 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
173 void QgsRuleBasedLabelingWidget::editRule()
175 editRule( viewRules->selectionModel()->currentIndex() );
178 void QgsRuleBasedLabelingWidget::editRule(
const QModelIndex &index )
180 if ( !index.isValid() )
185 QgsLabelingRulePropsWidget *widget =
new QgsLabelingRulePropsWidget( rule, mLayer,
this, mCanvas );
186 widget->setPanelTitle( tr(
"Edit Rule" ) );
192 void QgsRuleBasedLabelingWidget::removeRule()
194 QItemSelection sel = viewRules->selectionModel()->selection();
195 const auto constSel = sel;
196 for (
const QItemSelectionRange &range : constSel )
198 if ( range.isValid() )
199 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
202 viewRules->selectionModel()->clear();
205 void QgsRuleBasedLabelingWidget::copy()
207 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
209 if ( indexlist.isEmpty() )
212 QMimeData *mime = mModel->mimeData( indexlist );
213 QApplication::clipboard()->setMimeData( mime );
216 void QgsRuleBasedLabelingWidget::paste()
218 const QMimeData *mime = QApplication::clipboard()->mimeData();
219 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
221 if ( indexlist.isEmpty() )
222 index = mModel->index( mModel->rowCount(), 0 );
224 index = indexlist.first();
225 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
230 QItemSelectionModel *sel = viewRules->selectionModel();
231 QModelIndex idx = sel->currentIndex();
232 if ( !idx.isValid() )
234 return mModel->ruleForIndex( idx );
240 : QAbstractItemModel( parent )
241 , mRootRule( rootRule )
247 if ( !
index.isValid() )
248 return Qt::ItemIsDropEnabled;
251 Qt::ItemFlag drop = (
index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
253 Qt::ItemFlag checkable = (
index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
255 return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
256 Qt::ItemIsEditable | checkable |
257 Qt::ItemIsDragEnabled | drop;
262 if ( !
index.isValid() )
267 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
269 switch (
index.column() )
292 else if ( role == Qt::DecorationRole &&
index.column() == 0 && rule->
settings() )
297 else if ( role == Qt::TextAlignmentRole )
299 return (
index.column() == 2 ||
index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
301 else if ( role == Qt::FontRole &&
index.column() == 1 )
306 italicFont.setItalic(
true );
311 else if ( role == Qt::EditRole )
313 switch (
index.column() )
329 else if ( role == Qt::CheckStateRole )
331 if (
index.column() != 0 )
333 return rule->
active() ? Qt::Checked : Qt::Unchecked;
341 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
344 lst << tr(
"Label" ) << tr(
"Rule" ) << tr(
"Min. Scale" ) << tr(
"Max. Scale" ) << tr(
"Text" );
353 if (
parent.column() > 0 )
358 return parentRule->
children().count();
368 if ( hasIndex( row, column,
parent ) )
372 return createIndex( row, column, childRule );
374 return QModelIndex();
379 if ( !
index.isValid() )
380 return QModelIndex();
386 return QModelIndex();
391 return createIndex( row, 0, parentRule );
396 if ( !
index.isValid() )
401 if ( role == Qt::CheckStateRole )
403 rule->
setActive( value.toInt() == Qt::Checked );
408 if ( role != Qt::EditRole )
411 switch (
index.column() )
440 return Qt::MoveAction;
446 types << QStringLiteral(
"application/vnd.text.list" );
454 if ( ruleElem.hasAttribute( QStringLiteral(
"label" ) ) )
455 ruleElem.setAttribute( QStringLiteral(
"description" ), ruleElem.attribute( QStringLiteral(
"label" ) ) );
458 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral(
"rule" ) );
459 while ( !childRuleElem.isNull() )
462 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral(
"rule" ) );
468 QMimeData *
mimeData =
new QMimeData();
469 QByteArray encodedData;
471 QDataStream stream( &encodedData, QIODevice::WriteOnly );
473 const auto constIndexes = indexes;
474 for (
const QModelIndex &
index : constIndexes )
477 if ( !
index.isValid() ||
index.column() != 0 )
485 QDomElement rootElem = doc.createElement( QStringLiteral(
"rule_mime" ) );
486 rootElem.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"labeling" ) );
488 rootElem.appendChild( rulesElem );
489 doc.appendChild( rootElem );
493 stream << doc.toString( -1 );
496 mimeData->setData( QStringLiteral(
"application/vnd.text.list" ), encodedData );
504 if ( action == Qt::IgnoreAction )
507 if ( !
data->hasFormat( QStringLiteral(
"application/vnd.text.list" ) ) )
510 if (
parent.column() > 0 )
513 QByteArray encodedData =
data->data( QStringLiteral(
"application/vnd.text.list" ) );
514 QDataStream stream( &encodedData, QIODevice::ReadOnly );
523 while ( !stream.atEnd() )
529 if ( !doc.setContent( text ) )
531 QDomElement rootElem = doc.documentElement();
532 if ( rootElem.tagName() != QLatin1String(
"rule_mime" ) )
534 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral(
"rule" ) );
535 if ( rootElem.attribute( QStringLiteral(
"type" ) ) == QLatin1String(
"renderer" ) )
550 if ( row < 0 || row >= parentRule->
children().count() )
553 beginRemoveRows(
parent, row, row + count - 1 );
555 for (
int i = 0; i < count; i++ )
557 if ( row < parentRule->children().count() )
563 QgsDebugMsg( QStringLiteral(
"trying to remove invalid index - this should not happen!" ) );
574 if (
index.isValid() )
581 beginInsertRows(
parent, before, before );
601 , mSettings( nullptr )
602 , mMapCanvas( mapCanvas )
606 mElseRadio->setChecked( mRule->
isElse() );
607 mFilterRadio->setChecked( !mRule->
isElse() );
611 editDescription->setToolTip( mRule->
description() );
615 groupScale->setChecked(
true );
620 mScaleRangeWidget->setMapCanvas( mMapCanvas );
624 groupSettings->setChecked(
true );
629 groupSettings->setChecked(
false );
633 mLabelingGui =
new QgsLabelingGui(
nullptr, mMapCanvas, *mSettings,
this );
634 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
635 QVBoxLayout *l =
new QVBoxLayout;
636 l->addWidget( mLabelingGui );
637 groupSettings->setLayout( l );
639 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
640 mLabelingGui->setLayer( mLayer );
642 connect( btnExpressionBuilder, &QAbstractButton::clicked,
this, &QgsLabelingRulePropsWidget::buildExpression );
643 connect( btnTestFilter, &QAbstractButton::clicked,
this, &QgsLabelingRulePropsWidget::testFilter );
650 connect( mFilterRadio, &QRadioButton::toggled,
this, [ = ](
bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
651 connect( mElseRadio, &QRadioButton::toggled,
this, [ = ](
bool toggled ) {
if ( toggled ) editFilter->setText( QStringLiteral(
"ELSE" ) );} );
662 mLabelingGui->setDockMode( dockMode );
665 void QgsLabelingRulePropsWidget::testFilter()
667 if ( !mFilterRadio->isChecked() )
671 if ( filter.hasParserError() )
673 QMessageBox::critical(
this, tr(
"Test Filter" ), tr(
"Filter expression parsing error:\n" ) + filter.parserErrorString() );
679 if ( !filter.prepare( &context ) )
681 QMessageBox::critical(
this, tr(
"Test Filter" ), filter.evalErrorString() );
685 QApplication::setOverrideCursor( Qt::WaitCursor );
693 context.setFeature( f );
695 QVariant value = filter.evaluate( &context );
696 if ( value.toInt() != 0 )
698 if ( filter.hasEvalError() )
702 QApplication::restoreOverrideCursor();
704 QMessageBox::information(
this, tr(
"Test Filter" ), tr(
"Filter returned %n feature(s)",
"number of filtered features", count ) );
708 void QgsLabelingRulePropsWidget::buildExpression()
715 editFilter->setText( dlg.expressionText() );
720 QString filter = mElseRadio->isChecked() ? QStringLiteral(
"ELSE" ) : editFilter->text();
721 mRule->setFilterExpression( filter );
722 mRule->setDescription( editDescription->text() );
723 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
724 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
725 mRule->setSettings( groupSettings->isChecked() ?
new QgsPalLayerSettings( mLabelingGui->layerSettings() ) :
nullptr );