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 = std::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()
142 const QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
143 mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
144 const QModelIndex newindex = mModel->index( currentIndex.row() + 1, 0, currentIndex.parent() );
145 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
150 const int rows = mModel->rowCount();
151 mModel->insertRule( QModelIndex(), rows, newrule );
152 const 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 const 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() )
188 QgsLabelingRulePropsWidget *widget =
new QgsLabelingRulePropsWidget( rule, mLayer,
this, mCanvas );
189 widget->setPanelTitle( tr(
"Edit Rule" ) );
199 mModel->updateRule( index.parent(), index.row() );
200 emit widgetChanged();
204 void QgsRuleBasedLabelingWidget::removeRule()
206 const QItemSelection sel = viewRules->selectionModel()->selection();
207 const auto constSel = sel;
208 for (
const QItemSelectionRange &range : constSel )
210 if ( range.isValid() )
211 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
214 viewRules->selectionModel()->clear();
217 void QgsRuleBasedLabelingWidget::copy()
219 const QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
221 if ( indexlist.isEmpty() )
224 QMimeData *mime = mModel->mimeData( indexlist );
225 QApplication::clipboard()->setMimeData( mime );
228 void QgsRuleBasedLabelingWidget::paste()
230 const QMimeData *mime = QApplication::clipboard()->mimeData();
231 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
233 if ( indexlist.isEmpty() )
234 index = mModel->index( mModel->rowCount(), 0 );
236 index = indexlist.first();
237 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
242 QItemSelectionModel *sel = viewRules->selectionModel();
243 const QModelIndex idx = sel->currentIndex();
244 if ( !idx.isValid() )
246 return mModel->ruleForIndex( idx );
256 setWindowModality( Qt::WindowModal );
259 QVBoxLayout *layout =
new QVBoxLayout(
this );
261 scrollArea->setFrameShape( QFrame::NoFrame );
262 layout->addWidget( scrollArea );
264 buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
265 mPropsWidget =
new QgsLabelingRulePropsWidget(
rule, layer,
this, mapCanvas );
267 scrollArea->setWidget( mPropsWidget );
268 layout->addWidget( buttonBox );
269 this->setWindowTitle(
"Edit Rule" );
273 connect( buttonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
274 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsLabelingRulePropsDialog::showHelp );
279 mPropsWidget->testFilter();
284 mPropsWidget->buildExpression();
289 mPropsWidget->apply();
293 void QgsLabelingRulePropsDialog::showHelp()
295 QgsHelp::openHelp( QStringLiteral(
"working_with_vector/vector_properties.html#rule-based-labeling" ) );
301 : QAbstractItemModel( parent )
302 , mRootRule( rootRule )
308 if ( !
index.isValid() )
309 return Qt::ItemIsDropEnabled;
312 const Qt::ItemFlag drop = (
index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
314 const Qt::ItemFlag checkable = (
index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
316 return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
317 Qt::ItemIsEditable | checkable |
318 Qt::ItemIsDragEnabled | drop;
323 if ( !
index.isValid() )
328 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
330 switch (
index.column() )
353 else if ( role == Qt::DecorationRole &&
index.column() == 0 && rule->
settings() )
358 else if ( role == Qt::TextAlignmentRole )
360 return (
index.column() == 2 ||
index.column() == 3 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignRight ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
362 else if ( role == Qt::FontRole &&
index.column() == 1 )
367 italicFont.setItalic(
true );
372 else if ( role == Qt::EditRole )
374 switch (
index.column() )
390 else if ( role == Qt::CheckStateRole )
392 if (
index.column() != 0 )
394 return rule->
active() ? Qt::Checked : Qt::Unchecked;
402 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
405 lst << tr(
"Label" ) << tr(
"Rule" ) << tr(
"Min. Scale" ) << tr(
"Max. Scale" ) << tr(
"Text" );
414 if (
parent.column() > 0 )
419 return parentRule->
children().count();
429 if ( hasIndex( row, column,
parent ) )
433 return createIndex( row, column, childRule );
435 return QModelIndex();
440 if ( !
index.isValid() )
441 return QModelIndex();
447 return QModelIndex();
450 const int row = parentRule->
parent()->
children().indexOf( parentRule );
452 return createIndex( row, 0, parentRule );
457 if ( !
index.isValid() )
462 if ( role == Qt::CheckStateRole )
464 rule->
setActive( value.toInt() == Qt::Checked );
469 if ( role != Qt::EditRole )
472 switch (
index.column() )
501 return Qt::MoveAction;
507 types << QStringLiteral(
"application/vnd.text.list" );
515 if ( ruleElem.hasAttribute( QStringLiteral(
"label" ) ) )
516 ruleElem.setAttribute( QStringLiteral(
"description" ), ruleElem.attribute( QStringLiteral(
"label" ) ) );
519 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral(
"rule" ) );
520 while ( !childRuleElem.isNull() )
523 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral(
"rule" ) );
529 QMimeData *
mimeData =
new QMimeData();
530 QByteArray encodedData;
532 QDataStream stream( &encodedData, QIODevice::WriteOnly );
534 const auto constIndexes = indexes;
535 for (
const QModelIndex &
index : constIndexes )
538 if ( !
index.isValid() ||
index.column() != 0 )
546 QDomElement rootElem = doc.createElement( QStringLiteral(
"rule_mime" ) );
547 rootElem.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"labeling" ) );
549 rootElem.appendChild( rulesElem );
550 doc.appendChild( rootElem );
554 stream << doc.toString( -1 );
557 mimeData->setData( QStringLiteral(
"application/vnd.text.list" ), encodedData );
565 if ( action == Qt::IgnoreAction )
568 if ( !
data->hasFormat( QStringLiteral(
"application/vnd.text.list" ) ) )
571 if (
parent.column() > 0 )
574 QByteArray encodedData =
data->data( QStringLiteral(
"application/vnd.text.list" ) );
575 QDataStream stream( &encodedData, QIODevice::ReadOnly );
584 while ( !stream.atEnd() )
590 if ( !doc.setContent( text ) )
592 const QDomElement rootElem = doc.documentElement();
593 if ( rootElem.tagName() != QLatin1String(
"rule_mime" ) )
595 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral(
"rule" ) );
596 if ( rootElem.attribute( QStringLiteral(
"type" ) ) == QLatin1String(
"renderer" ) )
611 if ( row < 0 || row >= parentRule->
children().count() )
614 beginRemoveRows(
parent, row, row + count - 1 );
616 for (
int i = 0; i < count; i++ )
618 if ( row < parentRule->children().count() )
624 QgsDebugMsg( QStringLiteral(
"trying to remove invalid index - this should not happen!" ) );
635 if (
index.isValid() )
642 beginInsertRows(
parent, before, before );
662 , mSettings( nullptr )
663 , mMapCanvas( mapCanvas )
667 QButtonGroup *radioGroup =
new QButtonGroup(
this );
668 radioGroup->addButton( mFilterRadio );
669 radioGroup->addButton( mElseRadio );
671 mElseRadio->setChecked( mRule->
isElse() );
672 mFilterRadio->setChecked( !mRule->
isElse() );
676 editDescription->setToolTip( mRule->
description() );
680 groupScale->setChecked(
true );
685 mScaleRangeWidget->setMapCanvas( mMapCanvas );
689 groupSettings->setChecked(
true );
694 groupSettings->setChecked(
false );
698 mLabelingGui =
new QgsLabelingGui(
nullptr, mMapCanvas, *mSettings,
this );
699 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
700 QVBoxLayout *l =
new QVBoxLayout;
701 l->addWidget( mLabelingGui );
702 groupSettings->setLayout( l );
704 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
705 mLabelingGui->setLayer( mLayer );
715 connect( mFilterRadio, &QRadioButton::toggled,
this, [ = ](
bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
716 connect( mElseRadio, &QRadioButton::toggled,
this, [ = ](
bool toggled ) {
if ( toggled ) editFilter->setText( QStringLiteral(
"ELSE" ) );} );
727 mLabelingGui->setDockMode( dockMode );
732 if ( !mFilterRadio->isChecked() )
738 QMessageBox::critical(
this, tr(
"Test Filter" ), tr(
"Filter expression parsing error:\n" ) + filter.
parserErrorString() );
744 if ( !filter.
prepare( &context ) )
746 QMessageBox::critical(
this, tr(
"Test Filter" ), filter.
evalErrorString() );
750 QApplication::setOverrideCursor( Qt::WaitCursor );
760 const QVariant value = filter.
evaluate( &context );
761 if ( value.toInt() != 0 )
767 QApplication::restoreOverrideCursor();
769 QMessageBox::information(
this, tr(
"Test Filter" ), tr(
"Filter returned %n feature(s)",
"number of filtered features", count ) );
785 const QString filter = mElseRadio->isChecked() ? QStringLiteral(
"ELSE" ) : editFilter->text().trimmed();
786 mRule->setFilterExpression( filter );
787 mRule->setDescription( editDescription->text() );
788 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
789 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
790 mRule->setSettings( groupSettings->isChecked() ?
new QgsPalLayerSettings( mLabelingGui->layerSettings() ) :
nullptr );