QGIS API Documentation  master-3f58142
src/gui/symbology-ng/qgsrulebasedrendererv2widget.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsrulebasedrendererv2widget.cpp - Settings widget for rule-based renderer
00003     ---------------------
00004     begin                : May 2010
00005     copyright            : (C) 2010 by Martin Dobias
00006     email                : wonder dot sk at gmail dot com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include "qgsrulebasedrendererv2widget.h"
00017 
00018 #include "qgsrulebasedrendererv2.h"
00019 #include "qgssymbollayerv2utils.h"
00020 #include "qgssymbolv2.h"
00021 #include "qgsvectorlayer.h"
00022 #include "qgsapplication.h"
00023 #include "qgsexpression.h"
00024 #include "qgssymbolv2selectordialog.h"
00025 #include "qgslogger.h"
00026 #include "qstring.h"
00027 
00028 #include <QMenu>
00029 #include <QProgressDialog>
00030 #include <QSettings>
00031 #include <QTreeWidgetItem>
00032 #include <QVBoxLayout>
00033 #include <QMessageBox>
00034 
00035 //#include "modeltest.h"
00036 
00037 QgsRendererV2Widget* QgsRuleBasedRendererV2Widget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
00038 {
00039   return new QgsRuleBasedRendererV2Widget( layer, style, renderer );
00040 }
00041 
00042 QgsRuleBasedRendererV2Widget::QgsRuleBasedRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
00043     : QgsRendererV2Widget( layer, style )
00044 {
00045 
00046   // try to recognize the previous renderer
00047   // (null renderer means "no previous renderer")
00048   if ( !renderer || renderer->type() != "RuleRenderer" )
00049   {
00050     // we're not going to use it - so let's delete the renderer
00051     delete renderer;
00052 
00053     // some default options
00054     QgsSymbolV2* symbol = QgsSymbolV2::defaultSymbol( mLayer->geometryType() );
00055 
00056     mRenderer = new QgsRuleBasedRendererV2( symbol );
00057   }
00058   else
00059   {
00060     mRenderer = static_cast<QgsRuleBasedRendererV2*>( renderer );
00061   }
00062 
00063   setupUi( this );
00064 
00065   mModel = new QgsRuleBasedRendererV2Model( mRenderer );
00066   //new ModelTest( mModel, this ); // for model validity checking
00067   viewRules->setModel( mModel );
00068 
00069   mRefineMenu = new QMenu( "Refine current rule", btnRefineRule );
00070   mRefineMenu->addAction( tr( "Add scales to rule" ), this, SLOT( refineRuleScales() ) );
00071   mRefineMenu->addAction( tr( "Add categories to rule" ), this, SLOT( refineRuleCategories() ) );
00072   mRefineMenu->addAction( tr( "Add ranges to rule" ), this, SLOT( refineRuleRanges() ) );
00073   btnRefineRule->setMenu( mRefineMenu );
00074   contextMenu->addMenu( mRefineMenu );
00075 
00076   btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.png" ) ) );
00077   btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.png" ) ) );
00078   btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.png" ) ) );
00079 
00080   connect( viewRules, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( editRule( const QModelIndex & ) ) );
00081 
00082   // support for context menu (now handled generically)
00083   connect( viewRules, SIGNAL( customContextMenuRequested( const QPoint& ) ),  this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
00084 
00085   connect( viewRules->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SLOT( currentRuleChanged( QModelIndex, QModelIndex ) ) );
00086 
00087   connect( btnAddRule, SIGNAL( clicked() ), this, SLOT( addRule() ) );
00088   connect( btnEditRule, SIGNAL( clicked() ), this, SLOT( editRule() ) );
00089   connect( btnRemoveRule, SIGNAL( clicked() ), this, SLOT( removeRule() ) );
00090   connect( btnCountFeatures, SIGNAL( clicked() ), this, SLOT( countFeatures() ) );
00091 
00092   connect( btnRenderingOrder, SIGNAL( clicked() ), this, SLOT( setRenderingOrder() ) );
00093 
00094   currentRuleChanged();
00095 
00096   // store/restore header section widths
00097   connect( viewRules->header(), SIGNAL( sectionResized( int, int, int ) ), this, SLOT( saveSectionWidth( int, int, int ) ) );
00098 
00099   restoreSectionWidths();
00100 
00101 }
00102 
00103 QgsRuleBasedRendererV2Widget::~QgsRuleBasedRendererV2Widget()
00104 {
00105   delete mRenderer;
00106 }
00107 
00108 QgsFeatureRendererV2* QgsRuleBasedRendererV2Widget::renderer()
00109 {
00110   return mRenderer;
00111 }
00112 
00113 void QgsRuleBasedRendererV2Widget::addRule()
00114 {
00115   QgsSymbolV2* s = QgsSymbolV2::defaultSymbol( mLayer->geometryType() );
00116   QgsRuleBasedRendererV2::Rule* newrule = new QgsRuleBasedRendererV2::Rule( s );
00117 
00118   QgsRendererRulePropsDialog dlg( newrule, mLayer, mStyle, this );
00119   if ( dlg.exec() )
00120   {
00121     QgsRuleBasedRendererV2::Rule* current = currentRule();
00122     if ( current )
00123     {
00124       // add after this rule
00125       QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
00126       mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
00127     }
00128     else
00129     {
00130       // append to root rule
00131       int rows = mModel->rowCount();
00132       mModel->insertRule( QModelIndex(), rows, newrule );
00133     }
00134     mModel->clearFeatureCounts();
00135   }
00136   else
00137   {
00138     delete newrule;
00139   }
00140 }
00141 
00142 QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2Widget::currentRule()
00143 {
00144   QItemSelectionModel* sel = viewRules->selectionModel();
00145   QModelIndex idx = sel->currentIndex();
00146   if ( !idx.isValid() )
00147     return NULL;
00148   return mModel->ruleForIndex( idx );
00149 }
00150 
00151 void QgsRuleBasedRendererV2Widget::editRule()
00152 {
00153   editRule( viewRules->selectionModel()->currentIndex() );
00154 }
00155 
00156 void QgsRuleBasedRendererV2Widget::editRule( const QModelIndex& index )
00157 {
00158   if ( !index.isValid() )
00159     return;
00160   QgsRuleBasedRendererV2::Rule* rule = mModel->ruleForIndex( index );
00161 
00162   QgsRendererRulePropsDialog dlg( rule, mLayer, mStyle, this );
00163   if ( dlg.exec() )
00164   {
00165     // model should know about the change and emit dataChanged signal for the view
00166     mModel->updateRule( index.parent(), index.row() );
00167     mModel->clearFeatureCounts();
00168   }
00169 }
00170 
00171 void QgsRuleBasedRendererV2Widget::removeRule()
00172 {
00173   QItemSelection sel = viewRules->selectionModel()->selection();
00174   QgsDebugMsg( QString( "REMOVE RULES!!! ranges: %1" ).arg( sel.count() ) );
00175   foreach ( QItemSelectionRange range, sel )
00176   {
00177     QgsDebugMsg( QString( "RANGE: r %1 - %2" ).arg( range.top() ).arg( range.bottom() ) );
00178     if ( range.isValid() )
00179       mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
00180   }
00181   // make sure that the selection is gone
00182   viewRules->selectionModel()->clear();
00183   mModel->clearFeatureCounts();
00184 }
00185 
00186 void QgsRuleBasedRendererV2Widget::currentRuleChanged( const QModelIndex& current, const QModelIndex& previous )
00187 {
00188   Q_UNUSED( previous );
00189   btnRefineRule->setEnabled( current.isValid() );
00190 }
00191 
00192 
00193 #include "qgscategorizedsymbolrendererv2.h"
00194 #include "qgscategorizedsymbolrendererv2widget.h"
00195 #include "qgsgraduatedsymbolrendererv2.h"
00196 #include "qgsgraduatedsymbolrendererv2widget.h"
00197 #include "qgsexpressionbuilderdialog.h"
00198 #include <QDialogButtonBox>
00199 #include <QInputDialog>
00200 
00201 void QgsRuleBasedRendererV2Widget::refineRule( int type )
00202 {
00203   QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
00204 
00205   if ( indexlist.isEmpty() )
00206     return;
00207 
00208 
00209   if ( type == 0 ) // categories
00210     refineRuleCategoriesGui( indexlist );
00211   else if ( type == 1 ) // ranges
00212     refineRuleRangesGui( indexlist );
00213   else // scales
00214     refineRuleScalesGui( indexlist );
00215 
00216   // TODO: set initial rule's symbol to NULL (?)
00217 
00218   // show the newly added rules
00219   foreach ( QModelIndex index, indexlist )
00220     viewRules->expand( index );
00221 }
00222 
00223 void QgsRuleBasedRendererV2Widget::refineRuleCategories()
00224 {
00225   refineRule( 0 );
00226 }
00227 
00228 void QgsRuleBasedRendererV2Widget::refineRuleRanges()
00229 {
00230   refineRule( 1 );
00231 }
00232 
00233 void QgsRuleBasedRendererV2Widget::refineRuleScales()
00234 {
00235   refineRule( 2 );
00236 }
00237 
00238 void QgsRuleBasedRendererV2Widget::refineRuleCategoriesGui( const QModelIndexList& indexList )
00239 {
00240   QDialog dlg;
00241   dlg.setWindowTitle( tr( "Refine a rule to categories" ) );
00242   QVBoxLayout* l = new QVBoxLayout();
00243   QgsCategorizedSymbolRendererV2Widget* w = new QgsCategorizedSymbolRendererV2Widget( mLayer, mStyle, NULL );
00244   l->addWidget( w );
00245   QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
00246   l->addWidget( bb );
00247   connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
00248   connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
00249   dlg.setLayout( l );
00250 
00251   if ( !dlg.exec() )
00252     return;
00253 
00254   // create new rules
00255   QgsCategorizedSymbolRendererV2* r = static_cast<QgsCategorizedSymbolRendererV2*>( w->renderer() );
00256   foreach ( QModelIndex index, indexList )
00257   {
00258     QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
00259     mModel->willAddRules( index, r->categories().count() );
00260     QgsRuleBasedRendererV2::refineRuleCategories( initialRule, r );
00261   }
00262   mModel->finishedAddingRules();
00263 }
00264 
00265 
00266 void QgsRuleBasedRendererV2Widget::refineRuleRangesGui( const QModelIndexList& indexList )
00267 {
00268 
00269 
00270   QDialog dlg;
00271   dlg.setWindowTitle( tr( "Refine a rule to ranges" ) );
00272   QVBoxLayout* l = new QVBoxLayout();
00273   QgsGraduatedSymbolRendererV2Widget* w = new QgsGraduatedSymbolRendererV2Widget( mLayer, mStyle, NULL );
00274   l->addWidget( w );
00275   QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
00276   l->addWidget( bb );
00277   connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
00278   connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
00279   dlg.setLayout( l );
00280 
00281   if ( !dlg.exec() )
00282     return;
00283 
00284   // create new rules
00285   QgsGraduatedSymbolRendererV2* r = static_cast<QgsGraduatedSymbolRendererV2*>( w->renderer() );
00286   foreach ( QModelIndex index, indexList )
00287   {
00288     QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
00289     mModel->willAddRules( index, r->ranges().count() );
00290     QgsRuleBasedRendererV2::refineRuleRanges( initialRule, r );
00291   }
00292   mModel->finishedAddingRules();
00293 }
00294 
00295 void QgsRuleBasedRendererV2Widget::refineRuleScalesGui( const QModelIndexList& indexList )
00296 {
00297   foreach ( QModelIndex index, indexList )
00298   {
00299     QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
00300 
00301     // If any of the rules don't have a symbol let the user know and exit.
00302     if ( initialRule->symbol() == NULL )
00303     {
00304       QMessageBox::warning( this, tr( "Scale refinement" ), tr( "Parent rule %1 must have a symbol for this operation." ).arg( initialRule->label() ) );
00305       return;
00306     }
00307   }
00308 
00309   QString txt = QInputDialog::getText( this,
00310                                        tr( "Scale refinement" ),
00311                                        tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) );
00312   if ( txt.isEmpty() )
00313     return;
00314 
00315   QList<int> scales;
00316   bool ok;
00317   foreach ( QString item, txt.split( ',' ) )
00318   {
00319     int scale = item.toInt( &ok );
00320     if ( ok )
00321       scales.append( scale );
00322     else
00323       QMessageBox::information( this, tr( "Error" ), QString( tr( "\"%1\" is not valid scale denominator, ignoring it." ) ).arg( item ) );
00324   }
00325 
00326   foreach ( QModelIndex index, indexList )
00327   {
00328     QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
00329     mModel->willAddRules( index, scales.count() + 1 );
00330     QgsRuleBasedRendererV2::refineRuleScales( initialRule, scales );
00331   }
00332   mModel->finishedAddingRules();
00333 }
00334 
00335 QList<QgsSymbolV2*> QgsRuleBasedRendererV2Widget::selectedSymbols()
00336 {
00337   QList<QgsSymbolV2*> symbolList;
00338 
00339   if ( !mRenderer )
00340   {
00341     return symbolList;
00342   }
00343 
00344   QItemSelection sel = viewRules->selectionModel()->selection();
00345   foreach ( QItemSelectionRange range, sel )
00346   {
00347     QModelIndex parent = range.parent();
00348     QgsRuleBasedRendererV2::Rule* parentRule = mModel->ruleForIndex( parent );
00349     QgsRuleBasedRendererV2::RuleList& children = parentRule->children();
00350     for ( int row = range.top(); row <= range.bottom(); row++ )
00351     {
00352       symbolList.append( children[row]->symbol() );
00353     }
00354   }
00355 
00356   return symbolList;
00357 }
00358 
00359 void QgsRuleBasedRendererV2Widget::refreshSymbolView()
00360 {
00361   // TODO: model/view
00362   /*
00363   if ( treeRules )
00364   {
00365     treeRules->populateRules();
00366   }
00367   */
00368 }
00369 
00370 #include "qgssymbollevelsv2dialog.h"
00371 
00372 void QgsRuleBasedRendererV2Widget::setRenderingOrder()
00373 {
00374   QgsLegendSymbolList lst = mRenderer->legendSymbolItems();
00375 
00376   QgsSymbolLevelsV2Dialog dlg( lst, true, this );
00377   dlg.setForceOrderingEnabled( true );
00378 
00379   dlg.exec();
00380 }
00381 
00382 void QgsRuleBasedRendererV2Widget::saveSectionWidth( int section, int oldSize, int newSize )
00383 {
00384   Q_UNUSED( oldSize );
00385   // skip last section, as it stretches
00386   if ( section == 5 )
00387     return;
00388   QSettings settings;
00389   QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
00390   settings.setValue( path, newSize );
00391 }
00392 
00393 void QgsRuleBasedRendererV2Widget::restoreSectionWidths()
00394 {
00395   QSettings settings;
00396   QString path = "/Windows/RuleBasedTree/sectionWidth/";
00397   QHeaderView* head = viewRules->header();
00398   head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
00399   head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
00400   head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
00401   head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
00402   head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
00403   head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
00404 }
00405 
00406 void QgsRuleBasedRendererV2Widget::countFeatures()
00407 {
00408   if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
00409   {
00410     return;
00411   }
00412   QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> countMap;
00413 
00414   QgsRuleBasedRendererV2::RuleList ruleList = mRenderer->rootRule()->descendants();
00415   // insert all so that we have counts 0
00416   foreach ( QgsRuleBasedRendererV2::Rule* rule, ruleList )
00417   {
00418     countMap[rule].count = 0;
00419     countMap[rule].duplicateCount = 0;
00420   }
00421 
00422   QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) );
00423 
00424   QgsRenderContext renderContext;
00425   renderContext.setRendererScale( 0 ); // ignore scale
00426   mRenderer->startRender( renderContext, mLayer );
00427 
00428   int nFeatures = mLayer->pendingFeatureCount();
00429   QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, nFeatures );
00430   p.setWindowModality( Qt::WindowModal );
00431   int featuresCounted = 0;
00432 
00433   QgsFeature f;
00434   while ( fit.nextFeature( f ) )
00435   {
00436     QgsRuleBasedRendererV2::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f );
00437 
00438     foreach ( QgsRuleBasedRendererV2::Rule* rule, featureRuleList )
00439     {
00440       countMap[rule].count++;
00441       if ( featureRuleList.size() > 1 )
00442       {
00443         countMap[rule].duplicateCount++;
00444       }
00445       foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, featureRuleList )
00446       {
00447         if ( duplicateRule == rule ) continue;
00448         countMap[rule].duplicateCountMap[duplicateRule] += 1;
00449       }
00450     }
00451     ++featuresCounted;
00452     if ( featuresCounted % 50 == 0 )
00453     {
00454       if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
00455       {
00456         p.setMaximum( 0 );
00457       }
00458       p.setValue( featuresCounted );
00459       if ( p.wasCanceled() )
00460       {
00461         return;
00462       }
00463     }
00464   }
00465   p.setValue( nFeatures );
00466 
00467   mRenderer->stopRender( renderContext );
00468 
00469 #ifdef QGISDEBUG
00470   foreach ( QgsRuleBasedRendererV2::Rule *rule, countMap.keys() )
00471   {
00472     QgsDebugMsg( QString( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ) );
00473   }
00474 #endif
00475 
00476   mModel->setFeatureCounts( countMap );
00477 }
00478 
00480 
00481 QgsRendererRulePropsDialog::QgsRendererRulePropsDialog( QgsRuleBasedRendererV2::Rule* rule, QgsVectorLayer* layer, QgsStyleV2* style, QWidget* parent )
00482     : QDialog( parent ), mRule( rule ), mLayer( layer ), mSymbolSelector( NULL ), mSymbol( NULL )
00483 {
00484   setupUi( this );
00485 #ifdef Q_WS_MAC
00486   setWindowModality( Qt::WindowModal );
00487 #endif
00488 
00489   connect( buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
00490   connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
00491 
00492   editFilter->setText( mRule->filterExpression() );
00493   editFilter->setToolTip( mRule->filterExpression() );
00494   editLabel->setText( mRule->label() );
00495   editDescription->setText( mRule->description() );
00496   editDescription->setToolTip( mRule->description() );
00497 
00498   if ( mRule->dependsOnScale() )
00499   {
00500     groupScale->setChecked( true );
00501     spinMinScale->setValue( rule->scaleMinDenom() );
00502     spinMaxScale->setValue( rule->scaleMaxDenom() );
00503   }
00504 
00505   if ( mRule->symbol() )
00506   {
00507     groupSymbol->setChecked( true );
00508     mSymbol = mRule->symbol()->clone(); // use a clone!
00509   }
00510   else
00511   {
00512     groupSymbol->setChecked( false );
00513     mSymbol = QgsSymbolV2::defaultSymbol( mLayer->geometryType() );
00514   }
00515 
00516   mSymbolSelector = new QgsSymbolV2SelectorDialog( mSymbol, style, mLayer, this, true );
00517   QVBoxLayout* l = new QVBoxLayout;
00518   l->addWidget( mSymbolSelector );
00519   groupSymbol->setLayout( l );
00520 
00521   connect( btnExpressionBuilder, SIGNAL( clicked() ), this, SLOT( buildExpression() ) );
00522   connect( btnTestFilter, SIGNAL( clicked() ), this, SLOT( testFilter() ) );
00523 }
00524 
00525 QgsRendererRulePropsDialog::~QgsRendererRulePropsDialog()
00526 {
00527   delete mSymbol;
00528 }
00529 
00530 void QgsRendererRulePropsDialog::buildExpression()
00531 {
00532   QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this );
00533 
00534   if ( dlg.exec() )
00535     editFilter->setText( dlg.expressionText() );
00536 }
00537 
00538 void QgsRendererRulePropsDialog::testFilter()
00539 {
00540   QgsExpression filter( editFilter->text() );
00541   if ( filter.hasParserError() )
00542   {
00543     QMessageBox::critical( this, tr( "Error" ),  tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
00544     return;
00545   }
00546 
00547   const QgsFields& fields = mLayer->pendingFields();
00548 
00549   if ( !filter.prepare( fields ) )
00550   {
00551     QMessageBox::critical( this, tr( "Evaluation error" ), filter.evalErrorString() );
00552     return;
00553   }
00554 
00555   QApplication::setOverrideCursor( Qt::WaitCursor );
00556 
00557   QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) );
00558 
00559   int count = 0;
00560   QgsFeature f;
00561   while ( fit.nextFeature( f ) )
00562   {
00563     QVariant value = filter.evaluate( &f );
00564     if ( value.toInt() != 0 )
00565       count++;
00566     if ( filter.hasEvalError() )
00567       break;
00568   }
00569 
00570   QApplication::restoreOverrideCursor();
00571 
00572   QMessageBox::information( this, tr( "Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
00573 }
00574 
00575 void QgsRendererRulePropsDialog::accept()
00576 {
00577   mRule->setFilterExpression( editFilter->text() );
00578   mRule->setLabel( editLabel->text() );
00579   mRule->setDescription( editDescription->text() );
00580   mRule->setScaleMinDenom( groupScale->isChecked() ? spinMinScale->value() : 0 );
00581   mRule->setScaleMaxDenom( groupScale->isChecked() ? spinMaxScale->value() : 0 );
00582   mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : NULL );
00583 
00584   QDialog::accept();
00585 }
00586 
00588 
00589 /*
00590   setDragEnabled(true);
00591   viewport()->setAcceptDrops(true);
00592   setDropIndicatorShown(true);
00593   setDragDropMode(QAbstractItemView::InternalMove);
00594 */
00595 
00596 static QString _formatScale( int denom )
00597 {
00598   if ( denom != 0 )
00599   {
00600     QString txt = QString( "1:%L1" ).arg( denom );
00601     return txt;
00602   }
00603   else
00604     return QString();
00605 }
00606 
00608 
00609 QgsRuleBasedRendererV2Model::QgsRuleBasedRendererV2Model( QgsRuleBasedRendererV2* r )
00610     : mR( r )
00611 {
00612 }
00613 
00614 Qt::ItemFlags QgsRuleBasedRendererV2Model::flags( const QModelIndex &index ) const
00615 {
00616   if ( !index.isValid() )
00617     return Qt::ItemIsDropEnabled;
00618 
00619   // allow drop only at first column
00620   Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
00621 
00622   return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
00623          Qt::ItemIsEditable |
00624          Qt::ItemIsDragEnabled | drop;
00625 }
00626 
00627 QVariant QgsRuleBasedRendererV2Model::data( const QModelIndex &index, int role ) const
00628 {
00629   if ( !index.isValid() )
00630     return QVariant();
00631 
00632   QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index );
00633 
00634   if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
00635   {
00636     switch ( index.column() )
00637     {
00638       case 0: return rule->label();
00639       case 1: return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
00640       case 2: return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
00641       case 3: return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
00642       case 4:
00643         if ( mFeatureCountMap.count( rule ) == 1 )
00644         {
00645           return QVariant( mFeatureCountMap[rule].count );
00646         }
00647         return QVariant();
00648       case 5:
00649         if ( mFeatureCountMap.count( rule ) == 1 )
00650         {
00651           if ( role == Qt::DisplayRole )
00652           {
00653             return QVariant( mFeatureCountMap[rule].duplicateCount );
00654           }
00655           else // tooltip - detailed info about duplicates
00656           {
00657             if ( mFeatureCountMap[rule].duplicateCount > 0 )
00658             {
00659               QString tip = "<p style='margin:0px;'><ul>";
00660               foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, mFeatureCountMap[rule].duplicateCountMap.keys() )
00661               {
00662                 QString label = duplicateRule->label().replace( "&", "&amp;" ).replace( ">", "&gt;" ).replace( "<", "&lt;" );
00663                 tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( mFeatureCountMap[rule].duplicateCountMap[duplicateRule] ).arg( label );
00664               }
00665               tip += "</ul>";
00666               return tip;
00667             }
00668             else
00669             {
00670               return 0;
00671             }
00672           }
00673         }
00674         return QVariant();
00675       default: return QVariant();
00676     }
00677   }
00678   else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
00679   {
00680     return QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
00681   }
00682   else if ( role == Qt::TextAlignmentRole )
00683   {
00684     return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
00685   }
00686   else if ( role == Qt::EditRole )
00687   {
00688     switch ( index.column() )
00689     {
00690       case 0: return rule->label();
00691       case 1: return rule->filterExpression();
00692       case 2: return rule->scaleMinDenom();
00693       case 3: return rule->scaleMaxDenom();
00694       default: return QVariant();
00695     }
00696   }
00697   else
00698     return QVariant();
00699 }
00700 
00701 QVariant QgsRuleBasedRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
00702 {
00703   if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
00704   {
00705     QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max.scale" ) << tr( "Count" ) << tr( "Duplicate count" );
00706     return lst[section];
00707   }
00708   else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
00709   {
00710     if ( section == 4 ) // Count
00711     {
00712       return tr( "Number of features in this rule." );
00713     }
00714     else if ( section == 5 )  // Duplicate count
00715     {
00716       return tr( "Number of features in this rule which are also present in other rule(s)." );
00717     }
00718   }
00719 
00720   return QVariant();
00721 }
00722 
00723 int QgsRuleBasedRendererV2Model::rowCount( const QModelIndex &parent ) const
00724 {
00725   if ( parent.column() > 0 )
00726     return 0;
00727 
00728   QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
00729 
00730   return parentRule->children().count();
00731 }
00732 
00733 int QgsRuleBasedRendererV2Model::columnCount( const QModelIndex & ) const
00734 {
00735   return 6;
00736 }
00737 
00738 QModelIndex QgsRuleBasedRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
00739 {
00740   if ( hasIndex( row, column, parent ) )
00741   {
00742     QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
00743     QgsRuleBasedRendererV2::Rule* childRule = parentRule->children()[row];
00744     return createIndex( row, column, childRule );
00745   }
00746   return QModelIndex();
00747 }
00748 
00749 QModelIndex QgsRuleBasedRendererV2Model::parent( const QModelIndex &index ) const
00750 {
00751   if ( !index.isValid() )
00752     return QModelIndex();
00753 
00754   QgsRuleBasedRendererV2::Rule* childRule = ruleForIndex( index );
00755   QgsRuleBasedRendererV2::Rule* parentRule = childRule->parent();
00756 
00757   if ( parentRule == mR->rootRule() )
00758     return QModelIndex();
00759 
00760   // this is right: we need to know row number of our parent (in our grandparent)
00761   int row = parentRule->parent()->children().indexOf( parentRule );
00762 
00763   return createIndex( row, 0, parentRule );
00764 }
00765 
00766 bool QgsRuleBasedRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
00767 {
00768   if ( !index.isValid() || role != Qt::EditRole )
00769     return false;
00770 
00771   QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index );
00772 
00773   switch ( index.column() )
00774   {
00775     case 0: // label
00776       rule->setLabel( value.toString() );
00777       break;
00778     case 1: // filter
00779       rule->setFilterExpression( value.toString() );
00780       break;
00781     case 2: // scale min
00782       rule->setScaleMinDenom( value.toInt() );
00783       break;
00784     case 3: // scale max
00785       rule->setScaleMaxDenom( value.toInt() );
00786       break;
00787     default:
00788       return false;
00789   }
00790 
00791   emit dataChanged( index, index );
00792   return true;
00793 }
00794 
00795 Qt::DropActions QgsRuleBasedRendererV2Model::supportedDropActions() const
00796 {
00797   return Qt::MoveAction; // | Qt::CopyAction
00798 }
00799 
00800 QStringList QgsRuleBasedRendererV2Model::mimeTypes() const
00801 {
00802   QStringList types;
00803   types << "application/vnd.text.list";
00804   return types;
00805 }
00806 
00807 QMimeData *QgsRuleBasedRendererV2Model::mimeData( const QModelIndexList &indexes ) const
00808 {
00809   QMimeData *mimeData = new QMimeData();
00810   QByteArray encodedData;
00811 
00812   QDataStream stream( &encodedData, QIODevice::WriteOnly );
00813 
00814   foreach ( const QModelIndex &index, indexes )
00815   {
00816     // each item consists of several columns - let's add it with just first one
00817     if ( !index.isValid() || index.column() != 0 )
00818       continue;
00819 
00820     QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index );
00821     QDomDocument doc;
00822     QgsSymbolV2Map symbols;
00823 
00824     QDomElement rootElem = doc.createElement( "rule_mime" );
00825     QDomElement rulesElem = rule->save( doc, symbols );
00826     rootElem.appendChild( rulesElem );
00827     QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
00828     rootElem.appendChild( symbolsElem );
00829     doc.appendChild( rootElem );
00830 
00831     stream << doc.toString( -1 );
00832   }
00833 
00834   mimeData->setData( "application/vnd.text.list", encodedData );
00835   return mimeData;
00836 }
00837 
00838 bool QgsRuleBasedRendererV2Model::dropMimeData( const QMimeData *data,
00839     Qt::DropAction action, int row, int column, const QModelIndex &parent )
00840 {
00841   Q_UNUSED( column );
00842 
00843   if ( action == Qt::IgnoreAction )
00844     return true;
00845 
00846   if ( !data->hasFormat( "application/vnd.text.list" ) )
00847     return false;
00848 
00849   if ( parent.column() > 0 )
00850     return false;
00851 
00852   QByteArray encodedData = data->data( "application/vnd.text.list" );
00853   QDataStream stream( &encodedData, QIODevice::ReadOnly );
00854   int rows = 0;
00855 
00856   if ( row == -1 )
00857   {
00858     // the item was dropped at a parent - we may decide where to put the items - let's append them
00859     row = rowCount( parent );
00860   }
00861 
00862   while ( !stream.atEnd() )
00863   {
00864     QString text;
00865     stream >> text;
00866 
00867     QDomDocument doc;
00868     if ( !doc.setContent( text ) )
00869       continue;
00870     QDomElement rootElem = doc.documentElement();
00871     if ( rootElem.tagName() != "rule_mime" )
00872       continue;
00873     QDomElement symbolsElem = rootElem.firstChildElement( "symbols" );
00874     if ( symbolsElem.isNull() )
00875       continue;
00876     QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
00877     QDomElement ruleElem = rootElem.firstChildElement( "rule" );
00878     QgsRuleBasedRendererV2::Rule* rule = QgsRuleBasedRendererV2::Rule::create( ruleElem, symbolMap );
00879 
00880     insertRule( parent, row + rows, rule );
00881 
00882     ++rows;
00883   }
00884   return true;
00885 }
00886 
00887 QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2Model::ruleForIndex( const QModelIndex& index ) const
00888 {
00889   if ( index.isValid() )
00890     return static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
00891   return mR->rootRule();
00892 }
00893 
00894 bool QgsRuleBasedRendererV2Model::removeRows( int row, int count, const QModelIndex & parent )
00895 {
00896   QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
00897 
00898   if ( row < 0 || row >= parentRule->children().count() )
00899     return false;
00900 
00901   QgsDebugMsg( QString( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ) );
00902 
00903   beginRemoveRows( parent, row, row + count - 1 );
00904 
00905   for ( int i = 0; i < count; i++ )
00906   {
00907     if ( row < parentRule->children().count() )
00908     {
00909       //QgsRuleBasedRendererV2::Rule* r = parentRule->children()[row];
00910       parentRule->removeChildAt( row );
00911       //parentRule->takeChildAt( row );
00912     }
00913     else
00914     {
00915       QgsDebugMsg( "trying to remove invalid index - this should not happen!" );
00916     }
00917   }
00918 
00919   endRemoveRows();
00920 
00921   return true;
00922 }
00923 
00924 
00925 void QgsRuleBasedRendererV2Model::insertRule( const QModelIndex& parent, int before, QgsRuleBasedRendererV2::Rule* newrule )
00926 {
00927   beginInsertRows( parent, before, before );
00928 
00929   QgsDebugMsg( QString( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ) );
00930 
00931   QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
00932   parentRule->insertChild( before, newrule );
00933 
00934   endInsertRows();
00935 }
00936 
00937 void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& parent, int row )
00938 {
00939   emit dataChanged( index( row, 0, parent ),
00940                     index( row, columnCount( parent ), parent ) );
00941 }
00942 
00943 void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& idx )
00944 {
00945   emit dataChanged( index( 0, 0, idx ),
00946                     index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
00947 
00948   for ( int i = 0; i < rowCount( idx ); i++ )
00949   {
00950     updateRule( index( i, 0, idx ) );
00951   }
00952 }
00953 
00954 
00955 void QgsRuleBasedRendererV2Model::removeRule( const QModelIndex& index )
00956 {
00957   if ( !index.isValid() )
00958     return;
00959 
00960   beginRemoveRows( index.parent(), index.row(), index.row() );
00961 
00962   QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index );
00963   rule->parent()->removeChild( rule );
00964 
00965   endRemoveRows();
00966 }
00967 
00968 void QgsRuleBasedRendererV2Model::willAddRules( const QModelIndex& parent, int count )
00969 {
00970   int row = rowCount( parent ); // only consider appending
00971   beginInsertRows( parent, row, row + count - 1 );
00972 }
00973 
00974 void QgsRuleBasedRendererV2Model::finishedAddingRules()
00975 {
00976   emit endInsertRows();
00977 }
00978 
00979 void QgsRuleBasedRendererV2Model::setFeatureCounts( QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> theCountMap )
00980 {
00981   mFeatureCountMap = theCountMap;
00982   updateRule( QModelIndex() );
00983 }
00984 
00985 void QgsRuleBasedRendererV2Model::clearFeatureCounts()
00986 {
00987   mFeatureCountMap.clear();
00988   updateRule( QModelIndex() );
00989 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines