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