QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsfieldexpressionwidget.cpp
Go to the documentation of this file.
1 
2 /***************************************************************************
3  qgsfieldexpressionwidget.cpp
4  --------------------------------------
5  Date : 01.04.2014
6  Copyright : (C) 2014 Denis Rouzaud
7  Email : denis.rouzaud@gmail.com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16 
17 #include <QHBoxLayout>
18 
19 #include "qgsapplication.h"
22 #include "qgsfieldproxymodel.h"
23 #include "qgsdistancearea.h"
24 
26  : QWidget( parent )
27  , mExpressionDialogTitle( tr( "Expression dialog" ) )
28  , mDa( 0 )
29 {
30  QHBoxLayout* layout = new QHBoxLayout( this );
31  layout->setContentsMargins( 0, 0, 0, 0 );
32  mCombo = new QComboBox( this );
33  mCombo->setEditable( true );
34  mCombo->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Minimum );
35  int width = mCombo->minimumSizeHint().width();
36  mCombo->setMinimumWidth( width );
37  mFieldProxyModel = new QgsFieldProxyModel( mCombo );
38  mFieldProxyModel->sourceFieldModel()->setAllowExpression( true );
39  mCombo->setModel( mFieldProxyModel );
40 
41  mButton = new QToolButton( this );
42  mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
43  mButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpression.svg" ) );
44 
45  layout->addWidget( mCombo );
46  layout->addWidget( mButton );
47 
48  // give focus to the combo
49  // hence if the widget is used as a delegate
50  // it will allow pressing on the expression dialog button
51  setFocusProxy( mCombo );
52 
53  connect( mCombo->lineEdit(), SIGNAL( textEdited( QString ) ), this, SLOT( expressionEdited( QString ) ) );
54  connect( mCombo->lineEdit(), SIGNAL( editingFinished() ), this, SLOT( expressionEditingFinished() ) );
55  connect( mCombo, SIGNAL( activated( int ) ), this, SLOT( currentFieldChanged() ) );
56  connect( mButton, SIGNAL( clicked() ), this, SLOT( editExpression() ) );
57  // NW TODO - Fix in 2.6
58 // connect( mCombo->lineEdit(), SIGNAL( returnPressed() ), this, SIGNAL( returnPressed() ) );
59 }
60 
62 {
63  mExpressionDialogTitle = title;
64 }
65 
66 void QgsFieldExpressionWidget::setFilters( QgsFieldProxyModel::Filters filters )
67 {
68  mFieldProxyModel->setFilters( filters );
69 }
70 
72 {
73  QHBoxLayout* layout = dynamic_cast<QHBoxLayout*>( this->layout() );
74  if ( !layout )
75  return;
76 
77  if ( isLeft )
78  {
79  QLayoutItem* item = layout->takeAt( 1 );
80  layout->insertWidget( 0, item->widget() );
81  }
82  else
83  layout->addWidget( mCombo );
84 }
85 
87 {
88  mDa = QSharedPointer<const QgsDistanceArea>( new QgsDistanceArea( da ) );
89 }
90 
92 {
93  return mCombo->currentText();
94 }
95 
96 bool QgsFieldExpressionWidget::isValidExpression( QString *expressionError ) const
97 {
98  QString temp;
99  QgsVectorLayer* vl = layer();
100  return QgsExpression::isValid( currentText(), vl ? vl->pendingFields() : QgsFields(), expressionError ? *expressionError : temp );
101 }
102 
104 {
105  return !mFieldProxyModel->sourceFieldModel()->isField( currentText() );
106 }
107 
108 QString QgsFieldExpressionWidget::currentField( bool *isExpression, bool *isValid ) const
109 {
110  QString text = currentText();
111  if ( isValid )
112  {
113  *isValid = isValidExpression();
114  }
115  if ( isExpression )
116  {
117  *isExpression = this->isExpression();
118  }
119  return text;
120 }
121 
123 {
124  return mFieldProxyModel->sourceFieldModel()->layer();
125 }
126 
128 {
129  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
130  if ( vl )
131  {
132  setLayer( vl );
133  }
134 }
135 
137 {
138  mFieldProxyModel->sourceFieldModel()->setLayer( layer );
139 }
140 
141 void QgsFieldExpressionWidget::setField( const QString &fieldName )
142 {
143  if ( fieldName.isEmpty() )
144  return;
145 
146  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
147  if ( !idx.isValid() )
148  {
149  // try to remove quotes and white spaces
150  QString simpleFieldName = fieldName.trimmed();
151  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
152  {
153  simpleFieldName.remove( 0, 1 ).chop( 1 );
154  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
155  }
156 
157  if ( !idx.isValid() )
158  {
159  // new expression
160  mFieldProxyModel->sourceFieldModel()->setExpression( fieldName );
161  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
162  }
163  }
164  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
165  mCombo->setCurrentIndex( proxyIndex.row() );
167 }
168 
170 {
171  QString currentExpression = currentText();
172  QgsVectorLayer* vl = layer();
173 
174  QgsExpressionBuilderDialog dlg( vl, currentExpression );
175  if ( !mDa.isNull() )
176  {
177  dlg.setGeomCalculator( *mDa );
178  }
179  dlg.setWindowTitle( mExpressionDialogTitle );
180 
181  if ( dlg.exec() )
182  {
183  QString newExpression = dlg.expressionText();
184  setField( newExpression );
185  }
186 }
187 
188 void QgsFieldExpressionWidget::expressionEdited( const QString expression )
189 {
190  updateLineEditStyle( expression );
191  emit fieldChanged( expression, isValidExpression() );
192 }
193 
195 {
196  QgsDebugMsg( "Editing finished" );
197  const QString expression = mCombo->lineEdit()->text();
198  mFieldProxyModel->sourceFieldModel()->setExpression( expression );
199  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
200  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
201  mCombo->setCurrentIndex( proxyIndex.row() );
203 }
204 
206 {
207  if ( event->type() == QEvent::EnabledChange )
208  {
210  }
211 }
212 
214 {
216 
217  bool isExpression, isValid;
218  QString fieldName = currentField( &isExpression, &isValid );
219 
220  // display tooltip if widget is shorter than expression
221  QFontMetrics metrics( mCombo->lineEdit()->font() );
222  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
223  {
224  mCombo->setToolTip( fieldName );
225  }
226  else
227  {
228  mCombo->setToolTip( "" );
229  }
230 
231  emit fieldChanged( fieldName );
232  emit fieldChanged( fieldName, isValid );
233 }
234 
235 void QgsFieldExpressionWidget::updateLineEditStyle( const QString expression )
236 {
237  QPalette palette;
238  if ( !isEnabled() )
239  {
240  palette.setColor( QPalette::Text, Qt::gray );
241  }
242  else
243  {
244  bool isExpression, isValid;
245  if ( !expression.isEmpty() )
246  {
247  isExpression = true;
248  isValid = isExpressionValid( expression );
249  }
250  else
251  {
252  currentField( &isExpression, &isValid );
253  }
254  QFont font = mCombo->lineEdit()->font();
255  font.setItalic( isExpression );
256  mCombo->lineEdit()->setFont( font );
257 
258  if ( isExpression && !isValid )
259  {
260  palette.setColor( QPalette::Text, Qt::red );
261  }
262  else
263  {
264  palette.setColor( QPalette::Text, Qt::black );
265  }
266  }
267  mCombo->lineEdit()->setPalette( palette );
268 }
269 
270 bool QgsFieldExpressionWidget::isExpressionValid( const QString expressionStr )
271 {
272  QgsVectorLayer* vl = layer();
273 
274  QgsExpression expression( expressionStr );
275  expression.prepare( vl ? vl->pendingFields() : QgsFields() );
276  return !expression.hasParserError();
277 }