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 );
333 item->setIcon( icon );
336 if ( mExpressionGroups.contains( group ) )
339 groupNode->appendRow( item );
345 newgroupNode->setData( group, Qt::UserRole );
348 newgroupNode->appendRow( item );
349 newgroupNode->setBackground( QBrush( QColor( 150, 150, 150, 150 ) ) );
350 mModel->appendRow( newgroupNode );
351 mExpressionGroups.insert( group, newgroupNode );
354 if ( highlightedItem )
358 topLevelItem->setData( label, Qt::UserRole );
360 QFont font = topLevelItem->font();
361 font.setBold(
true );
362 topLevelItem->setFont( font );
363 mModel->appendRow( topLevelItem );
367 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 )
369 const auto constGroups = groups;
370 for (
const QString &group : constGroups )
372 registerItem( group, label, expressionText, helpText, type, highlightedItem, sortOrder, QIcon(), tags );
376 void QgsExpressionTreeView::loadExpressionContext()
379 const auto constVariableNames = variableNames;
380 for (
const QString &variable : constVariableNames )
382 registerItem( QStringLiteral(
"Variables" ), variable,
" @" + variable +
' ',
389 QStringList contextFunctions = mExpressionContext.
functionNames();
390 const auto constContextFunctions = contextFunctions;
391 for (
const QString &functionName : constContextFunctions )
394 QString name = func->
name();
395 if ( name.startsWith(
'_' ) )
397 if ( func->
params() != 0 )
403 void QgsExpressionTreeView::loadLayers()
408 QMap<QString, QgsMapLayer *> layers = mProject->mapLayers();
409 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
410 for ( ; layerIt != layers.constEnd(); ++layerIt )
412 registerItemForAllGroups( QStringList() << tr(
"Map Layers" ), layerIt.value()->name(), QStringLiteral(
"'%1'" ).arg( layerIt.key() ),
formatLayerHelp( layerIt.value() ) );
418 for (
int i = 0; i < fields.
count(); ++i )
427 void QgsExpressionTreeView::loadFieldNames()
430 if ( mExpressionGroups.contains( QStringLiteral(
"Fields and Values" ) ) )
432 QgsExpressionItem *node = mExpressionGroups.value( QStringLiteral(
"Fields and Values" ) );
433 node->removeRows( 0, node->rowCount() );
436 registerItem( QStringLiteral(
"Fields and Values" ), QStringLiteral(
"NULL" ), QStringLiteral(
"NULL" ), QString(),
QgsExpressionItem::ExpressionNode,
false, -1 );
448 void QgsExpressionTreeView::loadRelations()
453 QMap<QString, QgsRelation> relations = mProject->relationManager()->relations();
454 QMap<QString, QgsRelation>::const_iterator relIt = relations.constBegin();
455 for ( ; relIt != relations.constEnd(); ++relIt )
457 registerItemForAllGroups( QStringList() << tr(
"Relations" ), relIt->name(), QStringLiteral(
"'%1'" ).arg( relIt->id() ),
formatRelationHelp( relIt.value() ) );
463 mRecentKey = collection;
464 QString name = tr(
"Recent (%1)" ).arg( collection );
465 if ( mExpressionGroups.contains( name ) )
468 node->removeRows( 0, node->rowCount() );
472 const QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
473 const QStringList expressions = settings.
value( location ).toStringList();
475 for (
const QString &expression : expressions )
486 QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
487 QStringList expressions = settings.
value( location ).toStringList();
488 expressions.removeAll( expressionText );
490 expressions.prepend( expressionText );
492 while ( expressions.count() > 20 )
494 expressions.pop_back();
497 settings.
setValue( location, expressions );
504 const QString location = QStringLiteral(
"user" );
505 settings.
beginGroup( location, QgsSettings::Section::Expressions );
507 settings.
setValue( QStringLiteral(
"expression" ), expression );
508 settings.
setValue( QStringLiteral(
"helpText" ), helpText );
511 const QModelIndexList idxs { mModel->match( mModel->index( 0, 0 ),
512 Qt::DisplayRole, label, 1,
513 Qt::MatchFlag::MatchRecursive ) };
514 if ( ! idxs.isEmpty() )
516 scrollTo( idxs.first() );
523 settings.
remove( QStringLiteral(
"user/%1" ).arg( label ), QgsSettings::Section::Expressions );
531 if ( mExpressionGroups.contains( QStringLiteral(
"UserGroup" ) ) )
533 QgsExpressionItem *node = mExpressionGroups.value( QStringLiteral(
"UserGroup" ) );
534 node->removeRows( 0, node->rowCount() );
538 const QString location = QStringLiteral(
"user" );
539 settings.
beginGroup( location, QgsSettings::Section::Expressions );
545 for (
const auto &label : qgis::as_const( mUserExpressionLabels ) )
548 expression = settings.
value( QStringLiteral(
"expression" ) ).toString();
557 return mUserExpressionLabels;
562 const QString group = QStringLiteral(
"user" );
564 QJsonArray exportList;
565 QJsonObject exportObject
568 {
"exported_at", QDateTime::currentDateTime().toString( Qt::ISODate )},
570 {
"expressions", exportList}
573 settings.
beginGroup( group, QgsSettings::Section::Expressions );
577 for (
const QString &label : qgis::as_const( mUserExpressionLabels ) )
581 const QString expression = settings.
value( QStringLiteral(
"expression" ) ).toString();
582 const QString helpText = settings.
value( QStringLiteral(
"helpText" ) ).toString();
583 const QJsonObject expressionObject
586 {
"type",
"expression"},
587 {
"expression", expression},
589 {
"description", helpText}
591 exportList.push_back( expressionObject );
596 exportObject[
"expressions"] = exportList;
597 QJsonDocument exportJson = QJsonDocument( exportObject );
605 if ( ! expressionsDocument.isObject() )
608 QJsonObject expressionsObject = expressionsDocument.object();
611 if ( ! expressionsObject[
"qgis_version"].isString()
612 || ! expressionsObject[
"exported_at"].isString()
613 || ! expressionsObject[
"author"].isString()
614 || ! expressionsObject[
"expressions"].isArray() )
618 QVersionNumber qgisJsonVersion = QVersionNumber::fromString( expressionsObject[
"qgis_version"].toString() );
619 QVersionNumber qgisVersion = QVersionNumber::fromString(
Qgis::version() );
623 if ( qgisJsonVersion > qgisVersion )
625 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No;
626 switch ( QMessageBox::question(
this,
627 tr(
"QGIS Version Mismatch" ),
628 tr(
"The imported expressions are from newer version of QGIS (%1) "
629 "and some of the expression might not work the current version (%2). "
630 "Are you sure you want to continue?" ).arg( qgisJsonVersion.toString(), qgisVersion.toString() ), buttons ) )
632 case QMessageBox::No:
635 case QMessageBox::Yes:
644 QStringList skippedExpressionLabels;
645 bool isApplyToAll =
false;
646 bool isOkToOverwrite =
false;
649 settings.
beginGroup( QStringLiteral(
"user" ), QgsSettings::Section::Expressions );
652 for (
const QJsonValue &expressionValue : expressionsObject[
"expressions"].toArray() )
655 if ( ! expressionValue.isObject() )
658 skippedExpressionLabels.append( expressionValue.toString() );
662 QJsonObject expressionObj = expressionValue.toObject();
665 if ( ! expressionObj[
"name"].isString()
666 || ! expressionObj[
"type"].isString()
667 || ! expressionObj[
"expression"].isString()
668 || ! expressionObj[
"group"].isString()
669 || ! expressionObj[
"description"].isString() )
672 if ( ! expressionObj[
"name"].toString().isEmpty() )
673 skippedExpressionLabels.append( expressionObj[
"name"].toString() );
675 skippedExpressionLabels.append( expressionObj[
"expression"].toString() );
681 if ( expressionObj[
"type"].toString() != QStringLiteral(
"expression" ) )
683 skippedExpressionLabels.append( expressionObj[
"name"].toString() );
688 if ( expressionObj[
"group"].toString() != QStringLiteral(
"user" ) )
690 skippedExpressionLabels.append( expressionObj[
"name"].toString() );
694 const QString label = expressionObj[
"name"].toString();
695 const QString expression = expressionObj[
"expression"].toString();
696 const QString helpText = expressionObj[
"description"].toString();
699 if ( label.contains(
"\\" ) || label.contains(
'/' ) )
701 skippedExpressionLabels.append( expressionObj[
"name"].toString() );
706 const QString oldExpression = settings.
value( QStringLiteral(
"expression" ) ).toString();
710 if ( mUserExpressionLabels.contains( label ) && expression != oldExpression )
712 if ( ! isApplyToAll )
713 showMessageBoxConfirmExpressionOverwrite( isApplyToAll, isOkToOverwrite, label, oldExpression, expression );
715 if ( isOkToOverwrite )
719 skippedExpressionLabels.append( label );
731 if ( ! skippedExpressionLabels.isEmpty() )
733 QStringList skippedExpressionLabelsQuoted;
734 for (
const QString &skippedExpressionLabel : skippedExpressionLabels )
735 skippedExpressionLabelsQuoted.append( QStringLiteral(
"'%1'" ).arg( skippedExpressionLabel ) );
737 QMessageBox::information(
this,
738 tr(
"Skipped Expression Imports" ),
739 QStringLiteral(
"%1\n%2" ).arg( tr(
"The following expressions have been skipped:" ),
740 skippedExpressionLabelsQuoted.join(
", " ) ) );
746 QList<QgsExpressionItem *> result;
747 const QList<QStandardItem *> found { mModel->findItems( label, Qt::MatchFlag::MatchRecursive ) };
748 for (
const auto &item : qgis::as_const( found ) )
755 void QgsExpressionTreeView::showMessageBoxConfirmExpressionOverwrite(
757 bool &isOkToOverwrite,
758 const QString &label,
759 const QString &oldExpression,
760 const QString &newExpression )
762 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll;
763 switch ( QMessageBox::question(
this,
764 tr(
"Expression Overwrite" ),
765 tr(
"The expression with label '%1' was already defined."
766 "The old expression \"%2\" will be overwritten by \"%3\"."
767 "Are you sure you want to overwrite the expression?" ).arg( label, oldExpression, newExpression ), buttons ) )
769 case QMessageBox::NoToAll:
771 isOkToOverwrite =
false;
774 case QMessageBox::No:
775 isApplyToAll =
false;
776 isOkToOverwrite =
false;
779 case QMessageBox::YesToAll:
781 isOkToOverwrite =
true;
784 case QMessageBox::Yes:
785 isApplyToAll =
false;
786 isOkToOverwrite =
true;
803 setFilterCaseSensitivity( Qt::CaseInsensitive );
808 QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
811 int count = sourceModel()->rowCount( index );
812 bool matchchild =
false;
813 for (
int i = 0; i < count; ++i )
829 if ( QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent ) )
836 for (
const QString &tag : tags )
838 if ( tag.contains( filterRegExp() ) )
849 if ( leftSort != rightSort )
850 return leftSort < rightSort;
852 QString leftString = sourceModel()->data( left, Qt::DisplayRole ).toString();
853 QString rightString = sourceModel()->data( right, Qt::DisplayRole ).toString();
856 if ( leftString.startsWith(
'$' ) )
857 leftString = leftString.mid( 1 );
858 if ( rightString.startsWith(
'$' ) )
859 rightString = rightString.mid( 1 );
861 return QString::localeAwareCompare( leftString, rightString ) < 0;