1 /***************************************************************************
2     qgsdefaultsearchwidgettwrapper.cpp
3      --------------------------------------
4     Date                 : 31.5.2015
5     Copyright            : (C) 2015 Karolina Alexiou (carolinux)
6     Email                : carolinegr 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 "qgsdefaultsearchwidgetwrapper.h"
17 
18 #include "qgsfields.h"
19 #include "qgsfieldvalidator.h"
20 #include "qgsexpression.h"
21 #include "qgsfieldvalueslineedit.h"
22 #include "qgssettings.h"
23 #include "qgsapplication.h"
24 
25 #include <QHBoxLayout>
26 
QgsDefaultSearchWidgetWrapper(QgsVectorLayer * vl,int fieldIdx,QWidget * parent)27 QgsDefaultSearchWidgetWrapper::QgsDefaultSearchWidgetWrapper( QgsVectorLayer *vl, int fieldIdx, QWidget *parent )
28   : QgsSearchWidgetWrapper( vl, fieldIdx, parent )
29   , mCaseString( QStringLiteral( "LIKE" ) )
30 {
31 }
32 
expression() const33 QString QgsDefaultSearchWidgetWrapper::expression() const
34 {
35   return mExpression;
36 }
37 
setCaseString(int caseSensitiveCheckState)38 void QgsDefaultSearchWidgetWrapper::setCaseString( int caseSensitiveCheckState )
39 {
40   if ( caseSensitiveCheckState == Qt::Checked )
41   {
42     mCaseString = QStringLiteral( "LIKE" );
43   }
44   else
45   {
46     mCaseString = QStringLiteral( "ILIKE" );
47   }
48   // need to update also the line edit
49   setExpression( mLineEdit->text() );
50 
51   if ( applyDirectly() )
52     emit expressionChanged( mExpression );
53 }
54 
setExpression(const QString & expression)55 void QgsDefaultSearchWidgetWrapper::setExpression( const QString &expression )
56 {
57   QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
58   bool numeric = ( fldType == QVariant::Int || fldType == QVariant::Double || fldType == QVariant::LongLong );
59 
60   QString exp = expression;
61   QString nullValue = QgsApplication::nullRepresentation();
62   QString fieldName = layer()->fields().at( mFieldIdx ).name();
63   QString str;
64   if ( exp == nullValue )
65   {
66     str = QStringLiteral( "%1 IS NULL" ).arg( QgsExpression::quotedColumnRef( fieldName ) );
67   }
68   else
69   {
70     str = QStringLiteral( "%1 %2 '%3'" )
71           .arg( QgsExpression::quotedColumnRef( fieldName ),
72                 numeric ? QStringLiteral( "=" ) : mCaseString,
73                 numeric ?
74                 exp.replace( '\'', QLatin1String( "''" ) )
75                 :
76                 '%' + exp.replace( '\'', QLatin1String( "''" ) ) + '%' ); // escape quotes
77   }
78   mExpression = str;
79 }
80 
createWidget(QWidget * parent)81 QWidget *QgsDefaultSearchWidgetWrapper::createWidget( QWidget *parent )
82 {
83   return new QWidget( parent );
84 }
85 
applyDirectly()86 bool QgsDefaultSearchWidgetWrapper::applyDirectly()
87 {
88   return false;
89 }
90 
supportedFlags() const91 QgsSearchWidgetWrapper::FilterFlags QgsDefaultSearchWidgetWrapper::supportedFlags() const
92 {
93   FilterFlags flags = EqualTo | NotEqualTo | IsNull | IsNotNull;
94 
95   QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
96   switch ( fldType )
97   {
98     case QVariant::Int:
99     case QVariant::UInt:
100     case QVariant::Double:
101     case QVariant::LongLong:
102     case QVariant::ULongLong:
103     case QVariant::Date:
104     case QVariant::DateTime:
105     case QVariant::Time:
106       flags |= GreaterThan | LessThan | GreaterThanOrEqualTo | LessThanOrEqualTo | Between | IsNotBetween;
107       break;
108 
109     case QVariant::String:
110       flags |= Contains | DoesNotContain | StartsWith | EndsWith;
111       break;
112 
113     default:
114       break;
115   }
116   return flags;
117 }
118 
defaultFlags() const119 QgsSearchWidgetWrapper::FilterFlags QgsDefaultSearchWidgetWrapper::defaultFlags() const
120 {
121   QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
122   switch ( fldType )
123   {
124     case QVariant::Int:
125     case QVariant::UInt:
126     case QVariant::Double:
127     case QVariant::LongLong:
128     case QVariant::ULongLong:
129       //numeric
130       return EqualTo;
131 
132     case QVariant::Date:
133     case QVariant::DateTime:
134     case QVariant::Time:
135       return EqualTo;
136 
137     case QVariant::String:
138       return Contains;
139 
140     default:
141       break;
142   }
143   return EqualTo;
144 }
145 
createExpression(QgsSearchWidgetWrapper::FilterFlags flags) const146 QString QgsDefaultSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper::FilterFlags flags ) const
147 {
148   //clear any unsupported flags
149   flags &= supportedFlags();
150 
151   QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
152   QString fieldName = createFieldIdentifier();
153 
154   if ( flags & IsNull )
155     return fieldName + " IS NULL";
156   if ( flags & IsNotNull )
157     return fieldName + " IS NOT NULL";
158 
159   switch ( fldType )
160   {
161     case QVariant::Int:
162     case QVariant::UInt:
163     case QVariant::Double:
164     case QVariant::LongLong:
165     case QVariant::ULongLong:
166     {
167       if ( flags & EqualTo )
168         return fieldName + '=' + mLineEdit->text();
169       else if ( flags & NotEqualTo )
170         return fieldName + "<>" + mLineEdit->text();
171       else if ( flags & GreaterThan )
172         return fieldName + '>' + mLineEdit->text();
173       else if ( flags & LessThan )
174         return fieldName + '<' + mLineEdit->text();
175       else if ( flags & GreaterThanOrEqualTo )
176         return fieldName + ">=" + mLineEdit->text();
177       else if ( flags & LessThanOrEqualTo )
178         return fieldName + "<=" + mLineEdit->text();
179       break;
180     }
181 
182     case QVariant::Date:
183     case QVariant::DateTime:
184     case QVariant::Time:
185     {
186       if ( flags & EqualTo )
187         return fieldName + "='" + mLineEdit->text() + '\'';
188       else if ( flags & NotEqualTo )
189         return fieldName + "<>'" + mLineEdit->text() + '\'';
190       else if ( flags & GreaterThan )
191         return fieldName + ">'" + mLineEdit->text() + '\'';
192       else if ( flags & LessThan )
193         return fieldName + "<'" + mLineEdit->text() + '\'';
194       else if ( flags & GreaterThanOrEqualTo )
195         return fieldName + ">='" + mLineEdit->text() + '\'';
196       else if ( flags & LessThanOrEqualTo )
197         return fieldName + "<='" + mLineEdit->text() + '\'';
198       break;
199     }
200 
201     case QVariant::String:
202     {
203       // case insensitive!
204       if ( flags & EqualTo || flags & NotEqualTo )
205       {
206         if ( mCheckbox && mCheckbox->isChecked() )
207           return fieldName + ( ( flags & EqualTo ) ? "=" : "<>" )
208                  + QgsExpression::quotedString( mLineEdit->text() );
209         else
210           return QStringLiteral( "lower(%1)" ).arg( fieldName )
211                  + ( ( flags & EqualTo ) ? "=" : "<>" ) +
212                  QStringLiteral( "lower(%1)" ).arg( QgsExpression::quotedString( mLineEdit->text() ) );
213       }
214       else if ( flags & Contains || flags & DoesNotContain || flags & StartsWith || flags & EndsWith )
215       {
216         QString exp = fieldName + ( mCheckbox && mCheckbox->isChecked() ? " LIKE " : " ILIKE " );
217         QString value = QgsExpression::quotedString( mLineEdit->text() );
218         value.chop( 1 );
219         value = value.remove( 0, 1 );
220         exp += '\'';
221         if ( !flags.testFlag( StartsWith ) )
222           exp += '%';
223         exp += value;
224         if ( !flags.testFlag( EndsWith ) )
225           exp += '%';
226         exp += '\'';
227         if ( flags & DoesNotContain )
228           exp.prepend( "NOT (" ).append( ')' );
229         return exp;
230       }
231 
232       break;
233     }
234 
235     default:
236       break;
237   }
238 
239   return QString();
240 }
241 
clearWidget()242 void QgsDefaultSearchWidgetWrapper::clearWidget()
243 {
244   mLineEdit->setText( QString() );
245 }
246 
setEnabled(bool enabled)247 void QgsDefaultSearchWidgetWrapper::setEnabled( bool enabled )
248 {
249   mLineEdit->setEnabled( enabled );
250   if ( mCheckbox )
251     mCheckbox->setEnabled( enabled );
252 }
253 
initWidget(QWidget * widget)254 void QgsDefaultSearchWidgetWrapper::initWidget( QWidget *widget )
255 {
256   mContainer = widget;
257   mContainer->setLayout( new QHBoxLayout() );
258   mContainer->layout()->setContentsMargins( 0, 0, 0, 0 );
259   QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
260 
261   if ( fldType == QVariant::String )
262   {
263     mLineEdit = new QgsFieldValuesLineEdit();
264     static_cast< QgsFieldValuesLineEdit * >( mLineEdit )->setLayer( layer() );
265     static_cast< QgsFieldValuesLineEdit * >( mLineEdit )->setAttributeIndex( mFieldIdx );
266   }
267   else
268   {
269     mLineEdit = new QgsFilterLineEdit();
270   }
271   mContainer->layout()->addWidget( mLineEdit );
272   mContainer->setFocusProxy( mLineEdit );
273 
274   if ( fldType == QVariant::String )
275   {
276     mCheckbox = new QCheckBox( QStringLiteral( "Case sensitive" ) );
277     mContainer->layout()->addWidget( mCheckbox );
278     connect( mCheckbox, &QCheckBox::stateChanged, this, &QgsDefaultSearchWidgetWrapper::setCaseString );
279     mCheckbox->setChecked( Qt::Unchecked );
280   }
281 
282   connect( mLineEdit, &QLineEdit::textChanged, this, &QgsDefaultSearchWidgetWrapper::textChanged );
283   connect( mLineEdit, &QLineEdit::returnPressed, this, &QgsDefaultSearchWidgetWrapper::filterChanged );
284   connect( mLineEdit, &QLineEdit::textEdited, this, &QgsSearchWidgetWrapper::valueChanged );
285 
286   mCaseString = QStringLiteral( "ILIKE" );
287 }
288 
valid() const289 bool QgsDefaultSearchWidgetWrapper::valid() const
290 {
291   return true;
292 }
293 
lineEdit()294 QgsFilterLineEdit *QgsDefaultSearchWidgetWrapper::lineEdit()
295 {
296   return mLineEdit;
297 }
298 
caseSensitiveCheckBox()299 QCheckBox *QgsDefaultSearchWidgetWrapper::caseSensitiveCheckBox()
300 {
301   return mCheckbox;
302 }
303 
filterChanged()304 void QgsDefaultSearchWidgetWrapper::filterChanged()
305 {
306   emit expressionChanged( mExpression );
307 }
308 
textChanged(const QString & text)309 void QgsDefaultSearchWidgetWrapper::textChanged( const QString &text )
310 {
311   if ( text.isEmpty() )
312     emit valueCleared();
313 
314   setExpression( text );
315 }
316