1 /***************************************************************************
2 qgsexpressionlineedit.cpp
3 ------------------------
4 Date : 18.08.2016
5 Copyright : (C) 2016 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgsexpressionlineedit.h"
17 #include "qgsfilterlineedit.h"
18 #include "qgsexpressioncontext.h"
19 #include "qgsapplication.h"
20 #include "qgsexpressionbuilderdialog.h"
21 #include "qgsexpressioncontextgenerator.h"
22 #include "qgscodeeditorsql.h"
23 #include "qgsproject.h"
24 #include "qgsvectorlayer.h"
25 #include "qgsexpressioncontextutils.h"
26
27 #include <QHBoxLayout>
28 #include <QVBoxLayout>
29 #include <QToolButton>
30
31
QgsExpressionLineEdit(QWidget * parent)32 QgsExpressionLineEdit::QgsExpressionLineEdit( QWidget *parent )
33 : QWidget( parent )
34 , mExpressionDialogTitle( tr( "Expression Dialog" ) )
35 {
36 mButton = new QToolButton();
37 mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
38 mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
39 connect( mButton, &QAbstractButton::clicked, this, &QgsExpressionLineEdit::editExpression );
40
41 //sets up layout
42 setMultiLine( false );
43
44 mExpressionContext = QgsExpressionContext();
45 mExpressionContext << QgsExpressionContextUtils::globalScope()
46 << QgsExpressionContextUtils::projectScope( QgsProject::instance() );
47 }
48
49 QgsExpressionLineEdit::~QgsExpressionLineEdit() = default;
50
setExpressionDialogTitle(const QString & title)51 void QgsExpressionLineEdit::setExpressionDialogTitle( const QString &title )
52 {
53 mExpressionDialogTitle = title;
54 }
55
setMultiLine(bool multiLine)56 void QgsExpressionLineEdit::setMultiLine( bool multiLine )
57 {
58 const QString exp = expression();
59
60 if ( multiLine && !mCodeEditor )
61 {
62 mCodeEditor = new QgsCodeEditorExpression();
63 mCodeEditor->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
64 delete mLineEdit;
65 mLineEdit = nullptr;
66
67 QHBoxLayout *newLayout = new QHBoxLayout();
68 newLayout->setContentsMargins( 0, 0, 0, 0 );
69 newLayout->addWidget( mCodeEditor );
70
71 QVBoxLayout *vLayout = new QVBoxLayout();
72 vLayout->addWidget( mButton );
73 vLayout->addStretch();
74 newLayout->addLayout( vLayout );
75
76 delete layout();
77 setLayout( newLayout );
78
79 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
80
81 setFocusProxy( mCodeEditor );
82 connect( mCodeEditor, &QsciScintilla::textChanged, this, static_cast < void ( QgsExpressionLineEdit::* )() > ( &QgsExpressionLineEdit::expressionEdited ) );
83
84 setExpression( exp );
85 }
86 else if ( !multiLine && !mLineEdit )
87 {
88 delete mCodeEditor;
89 mCodeEditor = nullptr;
90 mLineEdit = new QgsFilterLineEdit();
91 mLineEdit->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
92
93 QHBoxLayout *newLayout = new QHBoxLayout();
94 newLayout->setContentsMargins( 0, 0, 0, 0 );
95 newLayout->addWidget( mLineEdit );
96 newLayout->addWidget( mButton );
97
98 delete layout();
99 setLayout( newLayout );
100
101 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
102
103 setFocusProxy( mLineEdit );
104 connect( mLineEdit, &QLineEdit::textChanged, this, static_cast < void ( QgsExpressionLineEdit::* )( const QString & ) > ( &QgsExpressionLineEdit::expressionEdited ) );
105
106 setExpression( exp );
107 }
108 }
109
expectedOutputFormat() const110 QString QgsExpressionLineEdit::expectedOutputFormat() const
111 {
112 return mExpectedOutputFormat;
113 }
114
setExpectedOutputFormat(const QString & expected)115 void QgsExpressionLineEdit::setExpectedOutputFormat( const QString &expected )
116 {
117 mExpectedOutputFormat = expected;
118 }
119
setGeomCalculator(const QgsDistanceArea & da)120 void QgsExpressionLineEdit::setGeomCalculator( const QgsDistanceArea &da )
121 {
122 mDa.reset( new QgsDistanceArea( da ) );
123 }
124
setLayer(QgsVectorLayer * layer)125 void QgsExpressionLineEdit::setLayer( QgsVectorLayer *layer )
126 {
127 if ( !mExpressionContextGenerator || mExpressionContextGenerator == mLayer )
128 mExpressionContextGenerator = layer;
129 mLayer = layer;
130 }
131
expression() const132 QString QgsExpressionLineEdit::expression() const
133 {
134 if ( mLineEdit )
135 return mLineEdit->text();
136 else if ( mCodeEditor )
137 return mCodeEditor->text();
138
139 return QString();
140 }
141
isValidExpression(QString * expressionError) const142 bool QgsExpressionLineEdit::isValidExpression( QString *expressionError ) const
143 {
144 QString temp;
145 const QgsExpressionContext context = mExpressionContextGenerator ? mExpressionContextGenerator->createExpressionContext() : mExpressionContext;
146 return QgsExpression::checkExpression( expression(), &context, expressionError ? *expressionError : temp );
147 }
148
registerExpressionContextGenerator(const QgsExpressionContextGenerator * generator)149 void QgsExpressionLineEdit::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
150 {
151 mExpressionContextGenerator = generator;
152 }
153
setExpression(const QString & newExpression)154 void QgsExpressionLineEdit::setExpression( const QString &newExpression )
155 {
156 if ( mLineEdit )
157 mLineEdit->setText( newExpression );
158 else if ( mCodeEditor )
159 mCodeEditor->setText( newExpression );
160 }
161
editExpression()162 void QgsExpressionLineEdit::editExpression()
163 {
164 const QString currentExpression = expression();
165
166 const QgsExpressionContext context = mExpressionContextGenerator ? mExpressionContextGenerator->createExpressionContext() : mExpressionContext;
167
168 QgsExpressionBuilderDialog dlg( mLayer, currentExpression, this, QStringLiteral( "generic" ), context );
169 dlg.setExpectedOutputFormat( mExpectedOutputFormat );
170 if ( mDa )
171 {
172 dlg.setGeomCalculator( *mDa );
173 }
174 dlg.setWindowTitle( mExpressionDialogTitle );
175
176 if ( dlg.exec() )
177 {
178 const QString newExpression = dlg.expressionText();
179 setExpression( newExpression );
180 }
181 }
182
expressionEdited()183 void QgsExpressionLineEdit::expressionEdited()
184 {
185 emit expressionChanged( expression() );
186 }
187
expressionEdited(const QString & expression)188 void QgsExpressionLineEdit::expressionEdited( const QString &expression )
189 {
190 updateLineEditStyle( expression );
191 emit expressionChanged( expression );
192 }
193
changeEvent(QEvent * event)194 void QgsExpressionLineEdit::changeEvent( QEvent *event )
195 {
196 if ( event->type() == QEvent::EnabledChange )
197 {
198 updateLineEditStyle( expression() );
199 }
200 }
201
updateLineEditStyle(const QString & expression)202 void QgsExpressionLineEdit::updateLineEditStyle( const QString &expression )
203 {
204 if ( !mLineEdit )
205 return;
206
207 QPalette palette = mLineEdit->palette();
208 if ( !isEnabled() )
209 {
210 palette.setColor( QPalette::Text, Qt::gray );
211 }
212 else
213 {
214 bool isValid = true;
215 if ( !expression.isEmpty() )
216 {
217 isValid = isExpressionValid( expression );
218 }
219 if ( !isValid )
220 {
221 palette.setColor( QPalette::Text, Qt::red );
222 }
223 else
224 {
225 palette.setColor( QPalette::Text, Qt::black );
226 }
227 }
228 mLineEdit->setPalette( palette );
229 }
230
isExpressionValid(const QString & expressionStr)231 bool QgsExpressionLineEdit::isExpressionValid( const QString &expressionStr )
232 {
233 QgsExpression expression( expressionStr );
234
235 const QgsExpressionContext context = mExpressionContextGenerator ? mExpressionContextGenerator->createExpressionContext() : mExpressionContext;
236 expression.prepare( &mExpressionContext );
237 return !expression.hasParserError();
238 }
239