17 #include <QMessageBox>
32 QString text = QStringLiteral(
"<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
33 .arg( QCoreApplication::translate(
"relation_help",
"relation %1" ).arg( relation.
name() ),
34 QObject::tr(
"Inserts the relation ID for the relation named '%1'." ).arg( relation.
name() ) );
36 text += QStringLiteral(
"<h4>%1</h4><div class=\"description\"><pre>%2</pre></div>" )
37 .arg( QObject::tr(
"Current value" ), relation.
id() );
46 QString text = QStringLiteral(
"<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
47 .arg( QCoreApplication::translate(
"layer_help",
"map layer %1" ).arg( layer->
name() ),
48 QObject::tr(
"Inserts the layer ID for the layer named '%1'." ).arg( layer->
name() ) );
50 text += QStringLiteral(
"<h4>%1</h4><div class=\"description\"><pre>%2</pre></div>" )
51 .arg( QObject::tr(
"Current value" ), layer->
id() );
59 QString text = QStringLiteral(
"<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
60 .arg( QCoreApplication::translate(
"recent_expression_help",
"expression %1" ).arg( label ),
61 QCoreApplication::translate(
"recent_expression_help",
"Recently used expression." ) );
63 text += QStringLiteral(
"<h4>%1</h4><div class=\"description\"><pre>%2</pre></div>" )
64 .arg( QObject::tr(
"Expression" ), expression );
72 QString text = QStringLiteral(
"<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
73 .arg( QCoreApplication::translate(
"user_expression_help",
"expression %1" ).arg( label ), description );
75 text += QStringLiteral(
"<h4>%1</h4><div class=\"description\"><pre>%2</pre></div>" )
76 .arg( QObject::tr(
"Expression" ), expression );
82 QString
formatVariableHelp(
const QString &variable,
const QString &description,
bool showValue,
const QVariant &value )
84 QString text = QStringLiteral(
"<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
85 .arg( QCoreApplication::translate(
"variable_help",
"variable %1" ).arg( variable ), description );
89 QString valueString = !value.isValid()
90 ? QCoreApplication::translate(
"variable_help",
"not set" )
93 text += QStringLiteral(
"<h4>%1</h4><div class=\"description\"><p>%2</p></div>" )
94 .arg( QObject::tr(
"Current value" ), valueString );
108 : QTreeView( parent )
111 connect(
this, &QTreeView::doubleClicked,
this, &QgsExpressionTreeView::onDoubleClicked );
113 mModel = qgis::make_unique<QStandardItemModel>();
114 mProxyModel = qgis::make_unique<QgsExpressionItemSearchProxy>();
115 mProxyModel->setDynamicSortFilter(
true );
116 mProxyModel->setSourceModel( mModel.get() );
117 setModel( mProxyModel.get() );
118 setSortingEnabled(
true );
119 sortByColumn( 0, Qt::AscendingOrder );
121 setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
123 setContextMenuPolicy( Qt::CustomContextMenu );
124 connect(
this, &QWidget::customContextMenuRequested,
this, &QgsExpressionTreeView::showContextMenu );
125 connect( selectionModel(), &QItemSelectionModel::currentChanged,
this, &QgsExpressionTreeView::currentItemChanged );
127 updateFunctionTree();
132 QModelIndex firstItem = mProxyModel->index( 0, 0, QModelIndex() );
133 setCurrentIndex( firstItem );
150 mExpressionContext = context;
151 updateFunctionTree();
159 mMenuProvider = provider;
164 updateFunctionTree();
172 QModelIndex idx = mProxyModel->mapToSource( currentIndex() );
190 updateFunctionTree();
196 mProxyModel->setFilterWildcard( text );
197 if ( text.isEmpty() )
204 QModelIndex index = mProxyModel->index( 0, 0 );
205 if ( mProxyModel->hasChildren( index ) )
207 QModelIndex child = mProxyModel->index( 0, 0, index );
208 selectionModel()->setCurrentIndex( child, QItemSelectionModel::ClearAndSelect );
213 void QgsExpressionTreeView::onDoubleClicked(
const QModelIndex &index )
215 QModelIndex idx = mProxyModel->mapToSource( index );
227 void QgsExpressionTreeView::showContextMenu( QPoint pt )
229 QModelIndex idx = indexAt( pt );
230 idx = mProxyModel->mapToSource( idx );
235 if ( !mMenuProvider )
241 menu->popup( mapToGlobal( pt ) );
244 void QgsExpressionTreeView::currentItemChanged(
const QModelIndex &index,
const QModelIndex & )
247 QModelIndex idx = mProxyModel->mapToSource( index );
255 void QgsExpressionTreeView::updateFunctionTree()
258 mExpressionGroups.clear();
261 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"+" ), QStringLiteral(
" + " ) );
262 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"-" ), QStringLiteral(
" - " ) );
263 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"*" ), QStringLiteral(
" * " ) );
264 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"/" ), QStringLiteral(
" / " ) );
265 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"%" ), QStringLiteral(
" % " ) );
266 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"^" ), QStringLiteral(
" ^ " ) );
267 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"=" ), QStringLiteral(
" = " ) );
268 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"~" ), QStringLiteral(
" ~ " ) );
269 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
">" ), QStringLiteral(
" > " ) );
270 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<" ), QStringLiteral(
" < " ) );
271 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<>" ), QStringLiteral(
" <> " ) );
272 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<=" ), QStringLiteral(
" <= " ) );
273 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
">=" ), QStringLiteral(
" >= " ) );
274 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"[]" ), QStringLiteral(
"[ ]" ) );
275 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"||" ), QStringLiteral(
" || " ) );
276 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"IN" ), QStringLiteral(
" IN " ) );
277 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"LIKE" ), QStringLiteral(
" LIKE " ) );
278 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"ILIKE" ), QStringLiteral(
" ILIKE " ) );
279 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"IS" ), QStringLiteral(
" IS " ) );
280 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"OR" ), QStringLiteral(
" OR " ) );
281 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"AND" ), QStringLiteral(
" AND " ) );
282 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"NOT" ), QStringLiteral(
" NOT " ) );
284 QString casestring = QStringLiteral(
"CASE WHEN condition THEN result END" );
285 registerItem( QStringLiteral(
"Conditionals" ), QStringLiteral(
"CASE" ), casestring );
288 registerItem( QStringLiteral(
"Fields and Values" ), QStringLiteral(
"NULL" ), QStringLiteral(
"NULL" ), QString(),
QgsExpressionItem::ExpressionNode,
false, -1 );
292 for (
int i = 0; i < count; i++ )
295 QString name = func->
name();
296 if ( name.startsWith(
'_' ) )
306 if ( func->
params() != 0 )
308 else if ( !name.startsWith(
'$' ) )
309 name += QLatin1String(
"()" );
320 loadExpressionContext();
323 void QgsExpressionTreeView::registerItem(
const QString &group,
324 const QString &label,
325 const QString &expressionText,
326 const QString &helpText,
330 item->setData( label, Qt::UserRole );
334 item->setIcon( icon );
337 if ( mExpressionGroups.contains( group ) )
340 groupNode->appendRow( item );
346 newgroupNode->setData( group, Qt::UserRole );
349 newgroupNode->appendRow( item );
350 newgroupNode->setBackground( QBrush( QColor( 150, 150, 150, 150 ) ) );
351 mModel->appendRow( newgroupNode );
352 mExpressionGroups.insert( group, newgroupNode );
355 if ( highlightedItem )
359 topLevelItem->setData( label, Qt::UserRole );
361 QFont font = topLevelItem->font();
362 font.setBold(
true );
363 topLevelItem->setFont( font );
364 mModel->appendRow( topLevelItem );
368 void QgsExpressionTreeView::registerItemForAllGroups(
const QStringList &groups,
const QString &label,
const QString &expressionText,
const QString &helpText,
QgsExpressionItem::ItemType type,
bool highlightedItem,
int sortOrder,
const QStringList &tags )
370 const auto constGroups = groups;
371 for (
const QString &group : constGroups )
373 registerItem( group, label, expressionText, helpText, type, highlightedItem, sortOrder, QIcon(), tags );
377 void QgsExpressionTreeView::loadExpressionContext()
380 const auto constVariableNames = variableNames;
381 for (
const QString &variable : constVariableNames )
383 registerItem( QStringLiteral(
"Variables" ), variable,
" @" + variable +
' ',
390 QStringList contextFunctions = mExpressionContext.
functionNames();
391 const auto constContextFunctions = contextFunctions;
392 for (
const QString &functionName : constContextFunctions )
395 QString name = func->
name();
396 if ( name.startsWith(
'_' ) )
398 if ( func->
params() != 0 )
404 void QgsExpressionTreeView::loadLayers()
409 QMap<QString, QgsMapLayer *> layers = mProject->mapLayers();
410 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
411 for ( ; layerIt != layers.constEnd(); ++layerIt )
413 registerItemForAllGroups( QStringList() << tr(
"Map Layers" ), layerIt.value()->name(), QStringLiteral(
"'%1'" ).arg( layerIt.key() ),
formatLayerHelp( layerIt.value() ) );
419 for (
int i = 0; i < fields.
count(); ++i )
428 void QgsExpressionTreeView::loadFieldNames()
431 if ( mExpressionGroups.contains( QStringLiteral(
"Fields and Values" ) ) )
433 QgsExpressionItem *node = mExpressionGroups.value( QStringLiteral(
"Fields and Values" ) );
434 node->removeRows( 0, node->rowCount() );
437 registerItem( QStringLiteral(
"Fields and Values" ), QStringLiteral(
"NULL" ), QStringLiteral(
"NULL" ), QString(),
QgsExpressionItem::ExpressionNode,
false, -1 );
449 void QgsExpressionTreeView::loadRelations()
454 QMap<QString, QgsRelation> relations = mProject->relationManager()->relations();
455 QMap<QString, QgsRelation>::const_iterator relIt = relations.constBegin();
456 for ( ; relIt != relations.constEnd(); ++relIt )
458 registerItemForAllGroups( QStringList() << tr(
"Relations" ), relIt->name(), QStringLiteral(
"'%1'" ).arg( relIt->id() ),
formatRelationHelp( relIt.value() ) );
464 mRecentKey = collection;
465 QString name = tr(
"Recent (%1)" ).arg( collection );
466 if ( mExpressionGroups.contains( name ) )
469 node->removeRows( 0, node->rowCount() );
473 const QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
474 const QStringList expressions = settings.
value( location ).toStringList();
476 for (
const QString &expression : expressions )
487 QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
488 QStringList expressions = settings.
value( location ).toStringList();
489 expressions.removeAll( expressionText );
491 expressions.prepend( expressionText );
493 while ( expressions.count() > 20 )
495 expressions.pop_back();
498 settings.
setValue( location, expressions );
505 const QString location = QStringLiteral(
"user" );
506 settings.
beginGroup( location, QgsSettings::Section::Expressions );
508 settings.
setValue( QStringLiteral(
"expression" ), expression );
509 settings.
setValue( QStringLiteral(
"helpText" ), helpText );
512 const QModelIndexList idxs { mModel->match( mModel->index( 0, 0 ),
513 Qt::DisplayRole, label, 1,
514 Qt::MatchFlag::MatchRecursive ) };
515 if ( ! idxs.isEmpty() )
517 scrollTo( idxs.first() );
524 settings.
remove( QStringLiteral(
"user/%1" ).arg( label ), QgsSettings::Section::Expressions );
532 if ( mExpressionGroups.contains( QStringLiteral(
"UserGroup" ) ) )
534 QgsExpressionItem *node = mExpressionGroups.value( QStringLiteral(
"UserGroup" ) );
535 node->removeRows( 0, node->rowCount() );
539 const QString location = QStringLiteral(
"user" );
540 settings.
beginGroup( location, QgsSettings::Section::Expressions );
546 for (
const auto &label : qgis::as_const( mUserExpressionLabels ) )
549 expression = settings.
value( QStringLiteral(
"expression" ) ).toString();
558 return mUserExpressionLabels;
563 const QString group = QStringLiteral(
"user" );
565 QJsonArray exportList;
566 QJsonObject exportObject
569 {
"exported_at", QDateTime::currentDateTime().toString( Qt::ISODate )},
571 {
"expressions", exportList}
574 settings.
beginGroup( group, QgsSettings::Section::Expressions );
578 for (
const QString &label : qgis::as_const( mUserExpressionLabels ) )
582 const QString expression = settings.
value( QStringLiteral(
"expression" ) ).toString();
583 const QString helpText = settings.
value( QStringLiteral(
"helpText" ) ).toString();
584 const QJsonObject expressionObject
587 {
"type",
"expression"},
588 {
"expression", expression},
590 {
"description", helpText}
592 exportList.push_back( expressionObject );
597 exportObject[
"expressions"] = exportList;
598 QJsonDocument exportJson = QJsonDocument( exportObject );
606 if ( ! expressionsDocument.isObject() )
609 QJsonObject expressionsObject = expressionsDocument.object();
612 if ( ! expressionsObject[
"qgis_version"].isString()
613 || ! expressionsObject[
"exported_at"].isString()
614 || ! expressionsObject[
"author"].isString()
615 || ! expressionsObject[
"expressions"].isArray() )
619 QVersionNumber qgisJsonVersion = QVersionNumber::fromString( expressionsObject[
"qgis_version"].toString() );
620 QVersionNumber qgisVersion = QVersionNumber::fromString(
Qgis::version() );
624 if ( qgisJsonVersion > qgisVersion )
626 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No;
627 switch ( QMessageBox::question(
this,
628 tr(
"QGIS Version Mismatch" ),
629 tr(
"The imported expressions are from newer version of QGIS (%1) "
630 "and some of the expression might not work the current version (%2). "
631 "Are you sure you want to continue?" ).arg( qgisJsonVersion.toString(), qgisVersion.toString() ), buttons ) )
633 case QMessageBox::No:
636 case QMessageBox::Yes:
645 QStringList skippedExpressionLabels;
646 bool isApplyToAll =
false;
647 bool isOkToOverwrite =
false;
650 settings.
beginGroup( QStringLiteral(
"user" ), QgsSettings::Section::Expressions );
653 for (
const QJsonValue &expressionValue : expressionsObject[
"expressions"].toArray() )
656 if ( ! expressionValue.isObject() )
659 skippedExpressionLabels.append( expressionValue.toString() );
663 QJsonObject expressionObj = expressionValue.toObject();
666 if ( ! expressionObj[
"name"].isString()
667 || ! expressionObj[
"type"].isString()
668 || ! expressionObj[
"expression"].isString()
669 || ! expressionObj[
"group"].isString()
670 || ! expressionObj[
"description"].isString() )
673 if ( ! expressionObj[
"name"].toString().isEmpty() )
674 skippedExpressionLabels.append( expressionObj[
"name"].toString() );
676 skippedExpressionLabels.append( expressionObj[
"expression"].toString() );
682 if ( expressionObj[
"type"].toString() != QLatin1String(
"expression" ) )
684 skippedExpressionLabels.append( expressionObj[
"name"].toString() );
689 if ( expressionObj[
"group"].toString() != QLatin1String(
"user" ) )
691 skippedExpressionLabels.append( expressionObj[
"name"].toString() );
695 const QString label = expressionObj[
"name"].toString();
696 const QString expression = expressionObj[
"expression"].toString();
697 const QString helpText = expressionObj[
"description"].toString();
700 if ( label.contains(
"\\" ) || label.contains(
'/' ) )
702 skippedExpressionLabels.append( expressionObj[
"name"].toString() );
707 const QString oldExpression = settings.
value( QStringLiteral(
"expression" ) ).toString();
711 if ( mUserExpressionLabels.contains( label ) && expression != oldExpression )
713 if ( ! isApplyToAll )
714 showMessageBoxConfirmExpressionOverwrite( isApplyToAll, isOkToOverwrite, label, oldExpression, expression );
716 if ( isOkToOverwrite )
720 skippedExpressionLabels.append( label );
732 if ( ! skippedExpressionLabels.isEmpty() )
734 QStringList skippedExpressionLabelsQuoted;
735 for (
const QString &skippedExpressionLabel : skippedExpressionLabels )
736 skippedExpressionLabelsQuoted.append( QStringLiteral(
"'%1'" ).arg( skippedExpressionLabel ) );
738 QMessageBox::information(
this,
739 tr(
"Skipped Expression Imports" ),
740 QStringLiteral(
"%1\n%2" ).arg( tr(
"The following expressions have been skipped:" ),
741 skippedExpressionLabelsQuoted.join(
", " ) ) );
747 QList<QgsExpressionItem *> result;
748 const QList<QStandardItem *> found { mModel->findItems( label, Qt::MatchFlag::MatchRecursive ) };
749 for (
const auto &item : qgis::as_const( found ) )
756 void QgsExpressionTreeView::showMessageBoxConfirmExpressionOverwrite(
758 bool &isOkToOverwrite,
759 const QString &label,
760 const QString &oldExpression,
761 const QString &newExpression )
763 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll;
764 switch ( QMessageBox::question(
this,
765 tr(
"Expression Overwrite" ),
766 tr(
"The expression with label '%1' was already defined."
767 "The old expression \"%2\" will be overwritten by \"%3\"."
768 "Are you sure you want to overwrite the expression?" ).arg( label, oldExpression, newExpression ), buttons ) )
770 case QMessageBox::NoToAll:
772 isOkToOverwrite =
false;
775 case QMessageBox::No:
776 isApplyToAll =
false;
777 isOkToOverwrite =
false;
780 case QMessageBox::YesToAll:
782 isOkToOverwrite =
true;
785 case QMessageBox::Yes:
786 isApplyToAll =
false;
787 isOkToOverwrite =
true;
804 setFilterCaseSensitivity( Qt::CaseInsensitive );
809 QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
812 int count = sourceModel()->rowCount( index );
813 bool matchchild =
false;
814 for (
int i = 0; i < count; ++i )
830 if ( QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent ) )
837 for (
const QString &tag : tags )
839 if ( tag.contains( filterRegExp() ) )
850 if ( leftSort != rightSort )
851 return leftSort < rightSort;
853 QString leftString = sourceModel()->data( left, Qt::DisplayRole ).toString();
854 QString rightString = sourceModel()->data( right, Qt::DisplayRole ).toString();
857 if ( leftString.startsWith(
'$' ) )
858 leftString = leftString.mid( 1 );
859 if ( rightString.startsWith(
'$' ) )
860 rightString = rightString.mid( 1 );
862 return QString::localeAwareCompare( leftString, rightString ) < 0;