16 #include <QDomDocument> 
   17 #include <QDomElement> 
   18 #include <QFileDialog> 
   20 #include <QInputDialog> 
   22 #include <QMessageBox> 
   23 #include <QStandardItem> 
   24 #include <QTextStream> 
   39     QWidget *parent, Qt::WindowFlags fl )
 
   40   : QDialog( parent, fl )
 
   44   connect( btnEqual, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnEqual_clicked );
 
   45   connect( btnLessThan, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnLessThan_clicked );
 
   46   connect( btnGreaterThan, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnGreaterThan_clicked );
 
   47   connect( btnLike, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnLike_clicked );
 
   48   connect( btnILike, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnILike_clicked );
 
   49   connect( btnPct, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnPct_clicked );
 
   50   connect( btnIn, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnIn_clicked );
 
   51   connect( btnNotIn, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnNotIn_clicked );
 
   52   connect( lstFields, &QListView::doubleClicked, 
this, &QgsSearchQueryBuilder::lstFields_doubleClicked );
 
   53   connect( lstValues, &QListView::doubleClicked, 
this, &QgsSearchQueryBuilder::lstValues_doubleClicked );
 
   54   connect( btnLessEqual, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnLessEqual_clicked );
 
   55   connect( btnGreaterEqual, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnGreaterEqual_clicked );
 
   56   connect( btnNotEqual, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnNotEqual_clicked );
 
   57   connect( btnAnd, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnAnd_clicked );
 
   58   connect( btnNot, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnNot_clicked );
 
   59   connect( btnOr, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnOr_clicked );
 
   60   connect( btnGetAllValues, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnGetAllValues_clicked );
 
   61   connect( btnSampleValues, &QPushButton::clicked, 
this, &QgsSearchQueryBuilder::btnSampleValues_clicked );
 
   63   connect( buttonBox, &QDialogButtonBox::helpRequested, 
this, &QgsSearchQueryBuilder::showHelp );
 
   65   setWindowTitle( tr( 
"Search Query Builder" ) );
 
   67   QPushButton *pbn = 
new QPushButton( tr( 
"&Test" ) );
 
   68   buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
 
   69   connect( pbn, &QAbstractButton::clicked, 
this, &QgsSearchQueryBuilder::btnTest_clicked );
 
   71   pbn = 
new QPushButton( tr( 
"&Clear" ) );
 
   72   buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
 
   73   connect( pbn, &QAbstractButton::clicked, 
this, &QgsSearchQueryBuilder::btnClear_clicked );
 
   75   pbn = 
new QPushButton( tr( 
"&Save…" ) );
 
   76   buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
 
   77   pbn->setToolTip( tr( 
"Save query to an xml file" ) );
 
   80   pbn = 
new QPushButton( tr( 
"&Load…" ) );
 
   81   buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
 
   82   pbn->setToolTip( tr( 
"Load query from xml file" ) );
 
   86     lblDataUri->setText( layer->
name() );
 
   90 void QgsSearchQueryBuilder::populateFields()
 
   96   for ( 
int idx = 0; idx < fields.
count(); ++idx )
 
   98     QString fieldName = fields.
at( idx ).
name();
 
   99     mFieldMap[fieldName] = idx;
 
  100     QStandardItem *myItem = 
new QStandardItem( fieldName );
 
  101     myItem->setEditable( 
false );
 
  102     mModelFields->insertRow( mModelFields->rowCount(), myItem );
 
  106 void QgsSearchQueryBuilder::setupListViews()
 
  109   mModelFields = 
new QStandardItemModel();
 
  110   mModelValues = 
new QStandardItemModel();
 
  111   lstFields->setModel( mModelFields );
 
  112   lstValues->setModel( mModelValues );
 
  114   lstFields->setViewMode( QListView::ListMode );
 
  115   lstValues->setViewMode( QListView::ListMode );
 
  116   lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
 
  117   lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
 
  119   lstFields->setUniformItemSizes( 
true );
 
  120   lstValues->setUniformItemSizes( 
true );
 
  123 void QgsSearchQueryBuilder::getFieldValues( 
int limit )
 
  130   mModelValues->clear();
 
  133   QString fieldName = mModelFields->data( lstFields->currentIndex() ).toString();
 
  134   int fieldIndex = mFieldMap[fieldName];
 
  136   bool numeric = ( field.
type() == QVariant::Int || field.
type() == QVariant::Double );
 
  142   attrs.append( fieldIndex );
 
  146   lstValues->setCursor( Qt::WaitCursor );
 
  148   mModelValues->blockSignals( 
true );
 
  149   lstValues->setUpdatesEnabled( 
false );
 
  152   QSet<QString> insertedValues;
 
  155           ( limit == 0 || mModelValues->rowCount() != limit ) )
 
  157     value = feat.
attribute( fieldIndex ).toString();
 
  162       value = 
'\'' + value.replace( 
'\'', QLatin1String( 
"''" ) ) + 
'\'';
 
  166     if ( !insertedValues.contains( value ) )
 
  168       QStandardItem *myItem = 
new QStandardItem( value );
 
  169       myItem->setEditable( 
false );
 
  170       mModelValues->insertRow( mModelValues->rowCount(), myItem );
 
  171       insertedValues.insert( value );
 
  175   mModelValues->blockSignals( 
false );
 
  176   lstValues->setUpdatesEnabled( 
true );
 
  178   mModelValues->sort( 0 );
 
  179   lstValues->setCursor( Qt::ArrowCursor );
 
  182 void QgsSearchQueryBuilder::btnSampleValues_clicked()
 
  184   getFieldValues( 25 );
 
  187 void QgsSearchQueryBuilder::btnGetAllValues_clicked()
 
  192 void QgsSearchQueryBuilder::btnTest_clicked()
 
  194   long count = countRecords( txtSQL->text() );
 
  200   QMessageBox::information( 
this, tr( 
"Test Query" ), tr( 
"Found %n matching feature(s).", 
"test result", count ) );
 
  204 long QgsSearchQueryBuilder::countRecords( 
const QString &searchString )
 
  207   if ( search.hasParserError() )
 
  209     QMessageBox::critical( 
this, tr( 
"Query Result" ), search.parserErrorString() );
 
  216   bool fetchGeom = search.needsGeometry();
 
  223   if ( !search.prepare( &context ) )
 
  225     QMessageBox::critical( 
this, tr( 
"Query Result" ), search.evalErrorString() );
 
  229   QApplication::setOverrideCursor( Qt::WaitCursor );
 
  235     context.setFeature( feat );
 
  236     QVariant value = search.evaluate( &context );
 
  237     if ( value.toInt() != 0 )
 
  243     if ( search.hasEvalError() )
 
  247   QApplication::restoreOverrideCursor();
 
  249   if ( search.hasEvalError() )
 
  251     QMessageBox::critical( 
this, tr( 
"Query Result" ), search.evalErrorString() );
 
  259 void QgsSearchQueryBuilder::btnOk_clicked()
 
  262   if ( txtSQL->text().trimmed().length() > 0 )
 
  269   long numRecs = countRecords( txtSQL->text() );
 
  274   else if ( numRecs == 0 )
 
  276     QMessageBox::warning( 
this, tr( 
"Query Result" ), tr( 
"The query you specified results in zero records being returned." ) );
 
  285 void QgsSearchQueryBuilder::btnEqual_clicked()
 
  287   txtSQL->insertText( QStringLiteral( 
" = " ) );
 
  290 void QgsSearchQueryBuilder::btnLessThan_clicked()
 
  292   txtSQL->insertText( QStringLiteral( 
" < " ) );
 
  295 void QgsSearchQueryBuilder::btnGreaterThan_clicked()
 
  297   txtSQL->insertText( QStringLiteral( 
" > " ) );
 
  300 void QgsSearchQueryBuilder::btnPct_clicked()
 
  302   txtSQL->insertText( QStringLiteral( 
"%" ) );
 
  305 void QgsSearchQueryBuilder::btnIn_clicked()
 
  307   txtSQL->insertText( QStringLiteral( 
" IN " ) );
 
  310 void QgsSearchQueryBuilder::btnNotIn_clicked()
 
  312   txtSQL->insertText( QStringLiteral( 
" NOT IN " ) );
 
  315 void QgsSearchQueryBuilder::btnLike_clicked()
 
  317   txtSQL->insertText( QStringLiteral( 
" LIKE " ) );
 
  322   return txtSQL->text();
 
  330 void QgsSearchQueryBuilder::lstFields_doubleClicked( 
const QModelIndex &index )
 
  335 void QgsSearchQueryBuilder::lstValues_doubleClicked( 
const QModelIndex &index )
 
  337   txtSQL->insertText( mModelValues->data( index ).toString() );
 
  340 void QgsSearchQueryBuilder::btnLessEqual_clicked()
 
  342   txtSQL->insertText( QStringLiteral( 
" <= " ) );
 
  345 void QgsSearchQueryBuilder::btnGreaterEqual_clicked()
 
  347   txtSQL->insertText( QStringLiteral( 
" >= " ) );
 
  350 void QgsSearchQueryBuilder::btnNotEqual_clicked()
 
  352   txtSQL->insertText( QStringLiteral( 
" != " ) );
 
  355 void QgsSearchQueryBuilder::btnAnd_clicked()
 
  357   txtSQL->insertText( QStringLiteral( 
" AND " ) );
 
  360 void QgsSearchQueryBuilder::btnNot_clicked()
 
  362   txtSQL->insertText( QStringLiteral( 
" NOT " ) );
 
  365 void QgsSearchQueryBuilder::btnOr_clicked()
 
  367   txtSQL->insertText( QStringLiteral( 
" OR " ) );
 
  370 void QgsSearchQueryBuilder::btnClear_clicked()
 
  375 void QgsSearchQueryBuilder::btnILike_clicked()
 
  377   txtSQL->insertText( QStringLiteral( 
" ILIKE " ) );
 
  383   QString lastQueryFileDir = s.
value( QStringLiteral( 
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
 
  385   QString saveFileName = QFileDialog::getSaveFileName( 
nullptr, tr( 
"Save Query to File" ), lastQueryFileDir, QStringLiteral( 
"*.qqf" ) );
 
  386   if ( saveFileName.isNull() )
 
  391   if ( !saveFileName.endsWith( QLatin1String( 
".qqf" ), Qt::CaseInsensitive ) )
 
  393     saveFileName += QLatin1String( 
".qqf" );
 
  396   QFile saveFile( saveFileName );
 
  397   if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
 
  399     QMessageBox::critical( 
nullptr, tr( 
"Save Query to File" ), tr( 
"Could not open file for writing." ) );
 
  404   QDomElement queryElem = xmlDoc.createElement( QStringLiteral( 
"Query" ) );
 
  405   QDomText queryTextNode = xmlDoc.createTextNode( txtSQL->text() );
 
  406   queryElem.appendChild( queryTextNode );
 
  407   xmlDoc.appendChild( queryElem );
 
  409   QTextStream fileStream( &saveFile );
 
  410   xmlDoc.save( fileStream, 2 );
 
  412   QFileInfo fi( saveFile );
 
  413   s.
setValue( QStringLiteral( 
"/UI/lastQueryFileDir" ), fi.absolutePath() );
 
  419   QString lastQueryFileDir = s.
value( QStringLiteral( 
"/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
 
  421   QString queryFileName = QFileDialog::getOpenFileName( 
nullptr, tr( 
"Load Query from File" ), lastQueryFileDir, tr( 
"Query files" ) + 
" (*.qqf);;" + tr( 
"All files" ) + 
" (*)" );
 
  422   if ( queryFileName.isNull() )
 
  427   QFile queryFile( queryFileName );
 
  428   if ( !queryFile.open( QIODevice::ReadOnly ) )
 
  430     QMessageBox::critical( 
nullptr, tr( 
"Load Query from File" ), tr( 
"Could not open file for reading." ) );
 
  433   QDomDocument queryDoc;
 
  434   if ( !queryDoc.setContent( &queryFile ) )
 
  436     QMessageBox::critical( 
nullptr, tr( 
"Load Query from File" ), tr( 
"File is not a valid xml document." ) );
 
  440   QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral( 
"Query" ) );
 
  441   if ( queryElem.isNull() )
 
  443     QMessageBox::critical( 
nullptr, tr( 
"Load Query from File" ), tr( 
"File is not a valid query document." ) );
 
  447   QString query = queryElem.text();
 
  457   QString newQueryText = query;
 
  462   QStringList attributes = searchTree->referencedColumns();
 
  463   QMap< QString, QString> attributesToReplace;
 
  464   QStringList existingAttributes;
 
  467   QMap<QString, int>::const_iterator fieldIt = mFieldMap.constBegin();
 
  468   for ( ; fieldIt != mFieldMap.constEnd(); ++fieldIt )
 
  470     existingAttributes.push_back( fieldIt.key() );
 
  474   QStringList::const_iterator attIt = attributes.constBegin();
 
  475   for ( ; attIt != attributes.constEnd(); ++attIt )
 
  478     if ( !mFieldMap.contains( attIt ) )
 
  481       QString replaceAttribute = QInputDialog::getItem( 0, tr( 
"Select Attribute" ), tr( 
"There is no attribute '%1' in the current vector layer. Please select an existing attribute." ).arg( *attIt ),
 
  482                                  existingAttributes, 0, 
false, &ok );
 
  483       if ( !ok || replaceAttribute.isEmpty() )
 
  487       attributesToReplace.insert( *attIt, replaceAttribute );
 
  492   QList<QgsSearchTreeNode *> columnRefList = searchTree->columnRefNodes();
 
  493   QList<QgsSearchTreeNode *>::iterator columnIt = columnRefList.begin();
 
  494   for ( ; columnIt != columnRefList.end(); ++columnIt )
 
  496     QMap< QString, QString>::const_iterator replaceIt = attributesToReplace.find( ( *columnIt )->columnRef() );
 
  497     if ( replaceIt != attributesToReplace.constEnd() )
 
  499       ( *columnIt )->setColumnRef( replaceIt.value() );
 
  503   if ( attributesToReplace.size() > 0 )
 
  505     newQueryText = query;
 
  510   txtSQL->insertText( newQueryText );
 
  513 void QgsSearchQueryBuilder::showHelp()
 
  515   QgsHelp::openHelp( QStringLiteral( 
"working_with_vector/vector_properties.html#query-builder" ) );