32 #include <QMessageBox>
36 static QList<QgsExpressionContextScope *> _globalProjectAtlasMapLayerScopes(
QgsMapCanvas *mapCanvas,
const QgsMapLayer *layer )
38 QList<QgsExpressionContextScope *> scopes;
68 mCopyAction =
new QAction( tr(
"Copy" ),
this );
69 mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
70 mPasteAction =
new QAction( tr(
"Paste" ),
this );
71 mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
72 mDeleteAction =
new QAction( tr(
"Remove Rule" ),
this );
73 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
75 viewRules->addAction( mCopyAction );
76 viewRules->addAction( mPasteAction );
77 viewRules->addAction( mDeleteAction );
79 connect( viewRules, &QAbstractItemView::doubleClicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )(
const QModelIndex & )
>( &QgsRuleBasedLabelingWidget::editRule ) );
81 connect( btnAddRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::addRule );
82 connect( btnEditRule, &QAbstractButton::clicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )()
>( &QgsRuleBasedLabelingWidget::editRule ) );
83 connect( btnRemoveRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::removeRule );
84 connect( mCopyAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::copy );
85 connect( mPasteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::paste );
86 connect( mDeleteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::removeRule );
97 std::unique_ptr< QgsPalLayerSettings > newSettings = qgis::make_unique< QgsPalLayerSettings >( mLayer->
labeling()->
settings() );
98 newSettings->drawLabels =
true;
107 viewRules->setModel( mModel );
125 mCopyAction->setShortcut( QKeySequence() );
127 mPasteAction->setShortcut( QKeySequence() );
129 mDeleteAction->setShortcut( QKeySequence() );
134 void QgsRuleBasedLabelingWidget::addRule()
143 QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
144 mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
145 QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
146 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
151 int rows = mModel->rowCount();
152 mModel->insertRule( QModelIndex(), rows, newrule );
153 QModelIndex newindex = mModel->index( rows, 0 );
154 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
159 void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted(
QgsPanelWidget *panel )
161 QgsLabelingRulePropsWidget *widget = qobject_cast<QgsLabelingRulePropsWidget *>( panel );
164 QModelIndex index = viewRules->selectionModel()->currentIndex();
165 mModel->updateRule( index.parent(), index.row() );
168 void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
170 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
174 void QgsRuleBasedLabelingWidget::editRule()
176 editRule( viewRules->selectionModel()->currentIndex() );
179 void QgsRuleBasedLabelingWidget::editRule(
const QModelIndex &index )
181 if ( !index.isValid() )
186 QgsLabelingRulePropsWidget *widget =
new QgsLabelingRulePropsWidget( rule, mLayer,
this, mCanvas );
187 widget->setPanelTitle( tr(
"Edit Rule" ) );
193 void QgsRuleBasedLabelingWidget::removeRule()
195 QItemSelection sel = viewRules->selectionModel()->selection();
196 const auto constSel = sel;
197 for (
const QItemSelectionRange &range : constSel )
199 if ( range.isValid() )
200 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
203 viewRules->selectionModel()->clear();
206 void QgsRuleBasedLabelingWidget::copy()
208 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
210 if ( indexlist.isEmpty() )
213 QMimeData *mime = mModel->mimeData( indexlist );
214 QApplication::clipboard()->setMimeData( mime );
217 void QgsRuleBasedLabelingWidget::paste()
219 const QMimeData *mime = QApplication::clipboard()->mimeData();
220 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
222 if ( indexlist.isEmpty() )
223 index = mModel->index( mModel->rowCount(), 0 );
225 index = indexlist.first();
226 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
231 QItemSelectionModel *sel = viewRules->selectionModel();
232 QModelIndex idx = sel->currentIndex();
233 if ( !idx.isValid() )
235 return mModel->ruleForIndex( idx );
241 : QAbstractItemModel( parent )
242 , mRootRule( rootRule )
248 if ( !
index.isValid() )
249 return Qt::ItemIsDropEnabled;
252 Qt::ItemFlag drop = (
index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
254 Qt::ItemFlag checkable = (
index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
256 return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
257 Qt::ItemIsEditable | checkable |
258 Qt::ItemIsDragEnabled | drop;
263 if ( !
index.isValid() )
268 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
270 switch (
index.column() )
293 else if ( role == Qt::DecorationRole &&
index.column() == 0 && rule->
settings() )
298 else if ( role == Qt::TextAlignmentRole )
300 return (
index.column() == 2 ||
index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
302 else if ( role == Qt::FontRole &&
index.column() == 1 )
307 italicFont.setItalic(
true );
312 else if ( role == Qt::EditRole )
314 switch (
index.column() )
330 else if ( role == Qt::CheckStateRole )
332 if (
index.column() != 0 )
334 return rule->
active() ? Qt::Checked : Qt::Unchecked;
342 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
345 lst << tr(
"Label" ) << tr(
"Rule" ) << tr(
"Min. Scale" ) << tr(
"Max. Scale" ) << tr(
"Text" );
354 if (
parent.column() > 0 )
359 return parentRule->
children().count();
369 if ( hasIndex( row, column,
parent ) )
373 return createIndex( row, column, childRule );
375 return QModelIndex();
380 if ( !
index.isValid() )
381 return QModelIndex();
387 return QModelIndex();
392 return createIndex( row, 0, parentRule );
397 if ( !
index.isValid() )
402 if ( role == Qt::CheckStateRole )
404 rule->
setActive( value.toInt() == Qt::Checked );
409 if ( role != Qt::EditRole )
412 switch (
index.column() )
441 return Qt::MoveAction;
447 types << QStringLiteral(
"application/vnd.text.list" );
455 if ( ruleElem.hasAttribute( QStringLiteral(
"label" ) ) )
456 ruleElem.setAttribute( QStringLiteral(
"description" ), ruleElem.attribute( QStringLiteral(
"label" ) ) );
459 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral(
"rule" ) );
460 while ( !childRuleElem.isNull() )
463 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral(
"rule" ) );
469 QMimeData *
mimeData =
new QMimeData();
470 QByteArray encodedData;
472 QDataStream stream( &encodedData, QIODevice::WriteOnly );
474 const auto constIndexes = indexes;
475 for (
const QModelIndex &
index : constIndexes )
478 if ( !
index.isValid() ||
index.column() != 0 )
486 QDomElement rootElem = doc.createElement( QStringLiteral(
"rule_mime" ) );
487 rootElem.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"labeling" ) );
489 rootElem.appendChild( rulesElem );
490 doc.appendChild( rootElem );
494 stream << doc.toString( -1 );
497 mimeData->setData( QStringLiteral(
"application/vnd.text.list" ), encodedData );
505 if ( action == Qt::IgnoreAction )
508 if ( !
data->hasFormat( QStringLiteral(
"application/vnd.text.list" ) ) )
511 if (
parent.column() > 0 )
514 QByteArray encodedData =
data->data( QStringLiteral(
"application/vnd.text.list" ) );
515 QDataStream stream( &encodedData, QIODevice::ReadOnly );
524 while ( !stream.atEnd() )
530 if ( !doc.setContent( text ) )
532 QDomElement rootElem = doc.documentElement();
533 if ( rootElem.tagName() != QLatin1String(
"rule_mime" ) )
535 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral(
"rule" ) );
536 if ( rootElem.attribute( QStringLiteral(
"type" ) ) == QLatin1String(
"renderer" ) )
551 if ( row < 0 || row >= parentRule->
children().count() )
554 beginRemoveRows(
parent, row, row + count - 1 );
556 for (
int i = 0; i < count; i++ )
558 if ( row < parentRule->children().count() )
564 QgsDebugMsg( QStringLiteral(
"trying to remove invalid index - this should not happen!" ) );
575 if (
index.isValid() )
582 beginInsertRows(
parent, before, before );
602 , mSettings( nullptr )
603 , mMapCanvas( mapCanvas )
607 mElseRadio->setChecked( mRule->
isElse() );
608 mFilterRadio->setChecked( !mRule->
isElse() );
612 editDescription->setToolTip( mRule->
description() );
616 groupScale->setChecked(
true );
621 mScaleRangeWidget->setMapCanvas( mMapCanvas );
625 groupSettings->setChecked(
true );
630 groupSettings->setChecked(
false );
634 mLabelingGui =
new QgsLabelingGui(
nullptr, mMapCanvas, *mSettings,
this );
635 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
636 QVBoxLayout *l =
new QVBoxLayout;
637 l->addWidget( mLabelingGui );
638 groupSettings->setLayout( l );
640 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
641 mLabelingGui->setLayer( mLayer );
643 connect( btnExpressionBuilder, &QAbstractButton::clicked,
this, &QgsLabelingRulePropsWidget::buildExpression );
644 connect( btnTestFilter, &QAbstractButton::clicked,
this, &QgsLabelingRulePropsWidget::testFilter );
651 connect( mFilterRadio, &QRadioButton::toggled,
this, [ = ](
bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
652 connect( mElseRadio, &QRadioButton::toggled,
this, [ = ](
bool toggled ) {
if ( toggled ) editFilter->setText( QStringLiteral(
"ELSE" ) );} );
663 mLabelingGui->setDockMode( dockMode );
666 void QgsLabelingRulePropsWidget::testFilter()
668 if ( !mFilterRadio->isChecked() )
672 if ( filter.hasParserError() )
674 QMessageBox::critical(
this, tr(
"Test Filter" ), tr(
"Filter expression parsing error:\n" ) + filter.parserErrorString() );
680 if ( !filter.prepare( &context ) )
682 QMessageBox::critical(
this, tr(
"Test Filter" ), filter.evalErrorString() );
686 QApplication::setOverrideCursor( Qt::WaitCursor );
694 context.setFeature( f );
696 QVariant value = filter.evaluate( &context );
697 if ( value.toInt() != 0 )
699 if ( filter.hasEvalError() )
703 QApplication::restoreOverrideCursor();
705 QMessageBox::information(
this, tr(
"Test Filter" ), tr(
"Filter returned %n feature(s)",
"number of filtered features", count ) );
709 void QgsLabelingRulePropsWidget::buildExpression()
716 editFilter->setText( dlg.expressionText() );
721 QString filter = mElseRadio->isChecked() ? QStringLiteral(
"ELSE" ) : editFilter->text();
722 mRule->setFilterExpression( filter );
723 mRule->setDescription( editDescription->text() );
724 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
725 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
726 mRule->setSettings( groupSettings->isChecked() ?
new QgsPalLayerSettings( mLabelingGui->layerSettings() ) :
nullptr );