QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsrulebasedrendererv2widget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrulebasedrendererv2widget.cpp - Settings widget for rule-based renderer
3  ---------------------
4  begin : May 2010
5  copyright : (C) 2010 by Martin Dobias
6  email : wonder dot sk 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 
17 
18 #include "qgsrulebasedrendererv2.h"
19 #include "qgssymbollayerv2utils.h"
20 #include "qgssymbolv2.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsapplication.h"
23 #include "qgsexpression.h"
25 #include "qgslogger.h"
26 #include "qstring.h"
28 
29 #include <QKeyEvent>
30 #include <QMenu>
31 #include <QProgressDialog>
32 #include <QSettings>
33 #include <QTreeWidgetItem>
34 #include <QVBoxLayout>
35 #include <QMessageBox>
36 
37 #ifdef ENABLE_MODELTEST
38 #include "modeltest.h"
39 #endif
40 
42 {
43  return new QgsRuleBasedRendererV2Widget( layer, style, renderer );
44 }
45 
47  : QgsRendererV2Widget( layer, style )
48 {
49  mRenderer = 0;
50  // try to recognize the previous renderer
51  // (null renderer means "no previous renderer")
52 
53 
54  if ( renderer )
55  {
57  }
58  if ( !mRenderer )
59  {
60  // some default options
62 
63  mRenderer = new QgsRuleBasedRendererV2( symbol );
64  }
65 
66  setupUi( this );
67 
69 #ifdef ENABLE_MODELTEST
70  new ModelTest( mModel, this ); // for model validity checking
71 #endif
72  viewRules->setModel( mModel );
73 
74  mDeleteAction = new QAction( tr( "Remove Rule" ), this );
75  mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
76 
77  viewRules->addAction( mDeleteAction );
78  viewRules->addAction( mCopyAction );
79  viewRules->addAction( mPasteAction );
80 
81  mRefineMenu = new QMenu( tr( "Refine current rule" ), btnRefineRule );
82  mRefineMenu->addAction( tr( "Add scales to rule" ), this, SLOT( refineRuleScales() ) );
83  mRefineMenu->addAction( tr( "Add categories to rule" ), this, SLOT( refineRuleCategories() ) );
84  mRefineMenu->addAction( tr( "Add ranges to rule" ), this, SLOT( refineRuleRanges() ) );
85  btnRefineRule->setMenu( mRefineMenu );
87 
88  btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
89  btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.png" ) ) );
90  btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
91 
92  connect( viewRules, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( editRule( const QModelIndex & ) ) );
93 
94  // support for context menu (now handled generically)
95  connect( viewRules, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
96 
97  connect( viewRules->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SLOT( currentRuleChanged( QModelIndex, QModelIndex ) ) );
98 
99  connect( btnAddRule, SIGNAL( clicked() ), this, SLOT( addRule() ) );
100  connect( btnEditRule, SIGNAL( clicked() ), this, SLOT( editRule() ) );
101  connect( btnRemoveRule, SIGNAL( clicked() ), this, SLOT( removeRule() ) );
102  connect( mDeleteAction, SIGNAL( triggered() ), this, SLOT( removeRule() ) );
103  connect( btnCountFeatures, SIGNAL( clicked() ), this, SLOT( countFeatures() ) );
104 
105  connect( btnRenderingOrder, SIGNAL( clicked() ), this, SLOT( setRenderingOrder() ) );
106 
108 
109  // store/restore header section widths
110  connect( viewRules->header(), SIGNAL( sectionResized( int, int, int ) ), this, SLOT( saveSectionWidth( int, int, int ) ) );
111 
113 
114 }
115 
117 {
118  qDeleteAll( mCopyBuffer );
119  delete mRenderer;
120 }
121 
123 {
124  return mRenderer;
125 }
126 
128 {
131 
132  QgsRendererRulePropsDialog dlg( newrule, mLayer, mStyle, this );
133  if ( dlg.exec() )
134  {
136  if ( current )
137  {
138  // add after this rule
139  QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
140  mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
141  }
142  else
143  {
144  // append to root rule
145  int rows = mModel->rowCount();
146  mModel->insertRule( QModelIndex(), rows, newrule );
147  }
149  }
150  else
151  {
152  delete newrule;
153  }
154 }
155 
157 {
158  QItemSelectionModel* sel = viewRules->selectionModel();
159  QModelIndex idx = sel->currentIndex();
160  if ( !idx.isValid() )
161  return NULL;
162  return mModel->ruleForIndex( idx );
163 }
164 
166 {
167  editRule( viewRules->selectionModel()->currentIndex() );
168 }
169 
171 {
172  if ( !index.isValid() )
173  return;
175 
176  QgsRendererRulePropsDialog dlg( rule, mLayer, mStyle, this );
177  if ( dlg.exec() )
178  {
179  // model should know about the change and emit dataChanged signal for the view
180  mModel->updateRule( index.parent(), index.row() );
182  }
183 }
184 
186 {
187  QItemSelection sel = viewRules->selectionModel()->selection();
188  QgsDebugMsg( QString( "REMOVE RULES!!! ranges: %1" ).arg( sel.count() ) );
189  foreach ( QItemSelectionRange range, sel )
190  {
191  QgsDebugMsg( QString( "RANGE: r %1 - %2" ).arg( range.top() ).arg( range.bottom() ) );
192  if ( range.isValid() )
193  mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
194  }
195  // make sure that the selection is gone
196  viewRules->selectionModel()->clear();
198 }
199 
201 {
202  Q_UNUSED( previous );
203  btnRefineRule->setEnabled( current.isValid() );
204 }
205 
206 
212 #include <QDialogButtonBox>
213 #include <QInputDialog>
214 #include <QClipboard>
215 
217 {
218  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
219 
220  if ( indexlist.isEmpty() )
221  return;
222 
223 
224  if ( type == 0 ) // categories
225  refineRuleCategoriesGui( indexlist );
226  else if ( type == 1 ) // ranges
227  refineRuleRangesGui( indexlist );
228  else // scales
229  refineRuleScalesGui( indexlist );
230 
231  // TODO: set initial rule's symbol to NULL (?)
232 
233  // show the newly added rules
234  foreach ( QModelIndex index, indexlist )
235  viewRules->expand( index );
236 }
237 
239 {
240  refineRule( 0 );
241 }
242 
244 {
245  refineRule( 1 );
246 }
247 
249 {
250  refineRule( 2 );
251 }
252 
253 void QgsRuleBasedRendererV2Widget::refineRuleCategoriesGui( const QModelIndexList& indexList )
254 {
255  QDialog dlg;
256  dlg.setWindowTitle( tr( "Refine a rule to categories" ) );
257  QVBoxLayout* l = new QVBoxLayout();
259  l->addWidget( w );
260  QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
261  l->addWidget( bb );
262  connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
263  connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
264  dlg.setLayout( l );
265 
266  if ( !dlg.exec() )
267  return;
268 
269  // create new rules
271  foreach ( QModelIndex index, indexList )
272  {
273  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
274  mModel->willAddRules( index, r->categories().count() );
276  }
278 }
279 
280 
281 void QgsRuleBasedRendererV2Widget::refineRuleRangesGui( const QModelIndexList& indexList )
282 {
283 
284 
285  QDialog dlg;
286  dlg.setWindowTitle( tr( "Refine a rule to ranges" ) );
287  QVBoxLayout* l = new QVBoxLayout();
289  l->addWidget( w );
290  QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
291  l->addWidget( bb );
292  connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
293  connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
294  dlg.setLayout( l );
295 
296  if ( !dlg.exec() )
297  return;
298 
299  // create new rules
301  foreach ( QModelIndex index, indexList )
302  {
303  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
304  mModel->willAddRules( index, r->ranges().count() );
306  }
308 }
309 
310 void QgsRuleBasedRendererV2Widget::refineRuleScalesGui( const QModelIndexList& indexList )
311 {
312  foreach ( QModelIndex index, indexList )
313  {
314  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
315 
316  // If any of the rules don't have a symbol let the user know and exit.
317  if ( initialRule->symbol() == NULL )
318  {
319  QMessageBox::warning( this, tr( "Scale refinement" ), tr( "Parent rule %1 must have a symbol for this operation." ).arg( initialRule->label() ) );
320  return;
321  }
322  }
323 
324  QString txt = QInputDialog::getText( this,
325  tr( "Scale refinement" ),
326  tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) );
327  if ( txt.isEmpty() )
328  return;
329 
330  QList<int> scales;
331  bool ok;
332  foreach ( QString item, txt.split( ',' ) )
333  {
334  int scale = item.toInt( &ok );
335  if ( ok )
336  scales.append( scale );
337  else
338  QMessageBox::information( this, tr( "Error" ), QString( tr( "\"%1\" is not valid scale denominator, ignoring it." ) ).arg( item ) );
339  }
340 
341  foreach ( QModelIndex index, indexList )
342  {
343  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
344  mModel->willAddRules( index, scales.count() + 1 );
345  QgsRuleBasedRendererV2::refineRuleScales( initialRule, scales );
346  }
348 }
349 
351 {
352  QList<QgsSymbolV2*> symbolList;
353 
354  if ( !mRenderer )
355  {
356  return symbolList;
357  }
358 
359  QItemSelection sel = viewRules->selectionModel()->selection();
360  foreach ( QItemSelectionRange range, sel )
361  {
362  QModelIndex parent = range.parent();
363  QgsRuleBasedRendererV2::Rule* parentRule = mModel->ruleForIndex( parent );
365  for ( int row = range.top(); row <= range.bottom(); row++ )
366  {
367  symbolList.append( children[row]->symbol() );
368  }
369  }
370 
371  return symbolList;
372 }
373 
375 {
377  QItemSelection sel = viewRules->selectionModel()->selection();
378  foreach ( QItemSelectionRange range, sel )
379  {
380  QModelIndex parent = range.parent();
381  QgsRuleBasedRendererV2::Rule* parentRule = mModel->ruleForIndex( parent );
383  for ( int row = range.top(); row <= range.bottom(); row++ )
384  {
385  rl.append( children[row]->clone() );
386  }
387  }
388  return rl;
389 }
390 
392 {
393  // TODO: model/view
394  /*
395  if ( treeRules )
396  {
397  treeRules->populateRules();
398  }
399  */
400 }
401 
403 {
404  if ( !event )
405  {
406  return;
407  }
408 
409  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
410  {
411  qDeleteAll( mCopyBuffer );
412  mCopyBuffer.clear();
414  }
415  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
416  {
418  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
419  {
420  int rows = mModel->rowCount();
421  mModel->insertRule( QModelIndex(), rows, ( *rIt )->clone() );
422  }
423  }
424 }
425 
426 #include "qgssymbollevelsv2dialog.h"
427 
429 {
431 
432  QgsSymbolLevelsV2Dialog dlg( lst, true, this );
433  dlg.setForceOrderingEnabled( true );
434 
435  dlg.exec();
436 }
437 
438 void QgsRuleBasedRendererV2Widget::saveSectionWidth( int section, int oldSize, int newSize )
439 {
440  Q_UNUSED( oldSize );
441  // skip last section, as it stretches
442  if ( section == 5 )
443  return;
444  QSettings settings;
445  QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
446  settings.setValue( path, newSize );
447 }
448 
450 {
451  QSettings settings;
452  QString path = "/Windows/RuleBasedTree/sectionWidth/";
453  QHeaderView* head = viewRules->header();
454  head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
455  head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
456  head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
457  head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
458  head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
459  head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
460 }
461 
463 {
464  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
465  QgsDebugMsg( QString( "%1" ).arg( indexlist.count() ) );
466 
467  if ( indexlist.isEmpty() )
468  return;
469 
470  QMimeData* mime = mModel->mimeData( indexlist );
472 }
473 
475 {
476  const QMimeData* mime = QApplication::clipboard()->mimeData();
477  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
479  if ( indexlist.isEmpty() )
480  index = mModel->index( mModel->rowCount(), 0 );
481  else
482  index = indexlist.first();
483  mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
484 }
485 
486 
488 {
489  if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
490  {
491  return;
492  }
494 
496  // insert all so that we have counts 0
497  foreach ( QgsRuleBasedRendererV2::Rule* rule, ruleList )
498  {
499  countMap[rule].count = 0;
500  countMap[rule].duplicateCount = 0;
501  }
502 
504 
505  QgsRenderContext renderContext;
506  renderContext.setRendererScale( 0 ); // ignore scale
507 
508  QgsExpressionContext context;
512 
513  renderContext.setExpressionContext( context );
514 
515  mRenderer->startRender( renderContext, mLayer->fields() );
516 
517  int nFeatures = mLayer->featureCount();
518  QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, nFeatures );
519  p.setWindowModality( Qt::WindowModal );
520  int featuresCounted = 0;
521 
522  QgsFeature f;
523  while ( fit.nextFeature( f ) )
524  {
525  renderContext.expressionContext().setFeature( f );
526  QgsRuleBasedRendererV2::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f, &renderContext );
527 
528  foreach ( QgsRuleBasedRendererV2::Rule* rule, featureRuleList )
529  {
530  countMap[rule].count++;
531  if ( featureRuleList.size() > 1 )
532  {
533  countMap[rule].duplicateCount++;
534  }
535  foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, featureRuleList )
536  {
537  if ( duplicateRule == rule ) continue;
538  countMap[rule].duplicateCountMap[duplicateRule] += 1;
539  }
540  }
541  ++featuresCounted;
542  if ( featuresCounted % 50 == 0 )
543  {
544  if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
545  {
546  p.setMaximum( 0 );
547  }
548  p.setValue( featuresCounted );
549  if ( p.wasCanceled() )
550  {
551  return;
552  }
553  }
554  }
555  p.setValue( nFeatures );
556 
557  mRenderer->stopRender( renderContext );
558 
559 #ifdef QGISDEBUG
560  foreach ( QgsRuleBasedRendererV2::Rule *rule, countMap.keys() )
561  {
562  QgsDebugMsg( QString( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ) );
563  }
564 #endif
565 
566  mModel->setFeatureCounts( countMap );
567 }
568 
570 
572  : QDialog( parent ), mRule( rule ), mLayer( layer ), mSymbolSelector( NULL ), mSymbol( NULL )
573 {
574  setupUi( this );
575 #ifdef Q_OS_MAC
576  setWindowModality( Qt::WindowModal );
577 #endif
578 
579  connect( buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
580  connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
581 
582  editFilter->setText( mRule->filterExpression() );
583  editFilter->setToolTip( mRule->filterExpression() );
584  editLabel->setText( mRule->label() );
585  editDescription->setText( mRule->description() );
586  editDescription->setToolTip( mRule->description() );
587 
588  if ( mRule->dependsOnScale() )
589  {
590  groupScale->setChecked( true );
591  // caution: rule uses scale denom, scale widget uses true scales
592  if ( rule->scaleMinDenom() > 0 )
593  mScaleRangeWidget->setMaximumScale( 1.0 / rule->scaleMinDenom() );
594  if ( rule->scaleMaxDenom() > 0 )
595  mScaleRangeWidget->setMinimumScale( 1.0 / rule->scaleMaxDenom() );
596  }
597 
598  if ( mRule->symbol() )
599  {
600  groupSymbol->setChecked( true );
601  mSymbol = mRule->symbol()->clone(); // use a clone!
602  }
603  else
604  {
605  groupSymbol->setChecked( false );
607  }
608 
609  mSymbolSelector = new QgsSymbolV2SelectorDialog( mSymbol, style, mLayer, this, true );
610  QVBoxLayout* l = new QVBoxLayout;
612  groupSymbol->setLayout( l );
613 
614  connect( btnExpressionBuilder, SIGNAL( clicked() ), this, SLOT( buildExpression() ) );
615  connect( btnTestFilter, SIGNAL( clicked() ), this, SLOT( testFilter() ) );
616 
617  QSettings settings;
618  restoreGeometry( settings.value( "/Windows/QgsRendererRulePropsDialog/geometry" ).toByteArray() );
619 }
620 
622 {
623  delete mSymbol;
624  QSettings settings;
625  settings.setValue( "/Windows/QgsRendererRulePropsDialog/geometry", saveGeometry() );
626 }
627 
629 {
630  QgsExpressionContext context;
634 
635  QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this, "generic", context );
636 
637  if ( dlg.exec() )
638  editFilter->setText( dlg.expressionText() );
639 }
640 
642 {
643  QgsExpression filter( editFilter->text() );
644  if ( filter.hasParserError() )
645  {
646  QMessageBox::critical( this, tr( "Error" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
647  return;
648  }
649 
650  QgsExpressionContext context;
654 
655  if ( !filter.prepare( &context ) )
656  {
657  QMessageBox::critical( this, tr( "Evaluation error" ), filter.evalErrorString() );
658  return;
659  }
660 
661  QApplication::setOverrideCursor( Qt::WaitCursor );
662 
664 
665  int count = 0;
666  QgsFeature f;
667  while ( fit.nextFeature( f ) )
668  {
669  context.setFeature( f );
670 
671  QVariant value = filter.evaluate( &context );
672  if ( value.toInt() != 0 )
673  count++;
674  if ( filter.hasEvalError() )
675  break;
676  }
677 
679 
680  QMessageBox::information( this, tr( "Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
681 }
682 
684 {
685  mRule->setFilterExpression( editFilter->text() );
686  mRule->setLabel( editLabel->text() );
687  mRule->setDescription( editDescription->text() );
688  // caution: rule uses scale denom, scale widget uses true scales
689  mRule->setScaleMinDenom( groupScale->isChecked() ? mScaleRangeWidget->minimumScaleDenom() : 0 );
690  mRule->setScaleMaxDenom( groupScale->isChecked() ? mScaleRangeWidget->maximumScaleDenom() : 0 );
691  mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : NULL );
692 
693  QDialog::accept();
694 }
695 
697 
698 /*
699  setDragEnabled(true);
700  viewport()->setAcceptDrops(true);
701  setDropIndicatorShown(true);
702  setDragDropMode(QAbstractItemView::InternalMove);
703 */
704 
705 static QString _formatScale( int denom )
706 {
707  if ( denom != 0 )
708  {
709  QString txt = QString( "1:%L1" ).arg( denom );
710  return txt;
711  }
712  else
713  return QString();
714 }
715 
717 
719  : mR( r )
720 {
721 }
722 
724 {
725  if ( !index.isValid() )
726  return Qt::ItemIsDropEnabled;
727 
728  // allow drop only at first column
729  Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
730 
731  Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
732 
733  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
734  Qt::ItemIsEditable | checkable |
735  Qt::ItemIsDragEnabled | drop;
736 }
737 
739 {
740  if ( !index.isValid() )
741  return QVariant();
742 
744 
745  if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
746  {
747  switch ( index.column() )
748  {
749  case 0: return rule->label();
750  case 1:
751  if ( rule->isElse() )
752  {
753  return "ELSE";
754  }
755  else
756  {
757  return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
758  }
759  case 2: return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
760  case 3: return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
761  case 4:
762  if ( mFeatureCountMap.count( rule ) == 1 )
763  {
764  return QVariant( mFeatureCountMap[rule].count );
765  }
766  return QVariant();
767  case 5:
768  if ( mFeatureCountMap.count( rule ) == 1 )
769  {
770  if ( role == Qt::DisplayRole )
771  {
772  return QVariant( mFeatureCountMap[rule].duplicateCount );
773  }
774  else // tooltip - detailed info about duplicates
775  {
776  if ( mFeatureCountMap[rule].duplicateCount > 0 )
777  {
778  QString tip = "<p style='margin:0px;'><ul>";
779  foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, mFeatureCountMap[rule].duplicateCountMap.keys() )
780  {
781  QString label = duplicateRule->label().replace( "&", "&amp;" ).replace( ">", "&gt;" ).replace( "<", "&lt;" );
782  tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( mFeatureCountMap[rule].duplicateCountMap[duplicateRule] ).arg( label );
783  }
784  tip += "</ul>";
785  return tip;
786  }
787  else
788  {
789  return 0;
790  }
791  }
792  }
793  return QVariant();
794  default: return QVariant();
795  }
796  }
797  else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
798  {
799  return QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
800  }
801  else if ( role == Qt::TextAlignmentRole )
802  {
803  return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
804  }
805  else if ( role == Qt::FontRole && index.column() == 1 )
806  {
807  if ( rule->isElse() )
808  {
809  QFont italicFont;
810  italicFont.setItalic( true );
811  return italicFont;
812  }
813  return QVariant();
814  }
815  else if ( role == Qt::EditRole )
816  {
817  switch ( index.column() )
818  {
819  case 0: return rule->label();
820  case 1: return rule->filterExpression();
821  case 2: return rule->scaleMaxDenom();
822  case 3: return rule->scaleMinDenom();
823  default: return QVariant();
824  }
825  }
826  else if ( role == Qt::CheckStateRole )
827  {
828  if ( index.column() != 0 )
829  return QVariant();
830  return rule->checkState() ? Qt::Checked : Qt::Unchecked;
831  }
832  else
833  return QVariant();
834 }
835 
836 QVariant QgsRuleBasedRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
837 {
838  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
839  {
840  QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max. scale" ) << tr( "Count" ) << tr( "Duplicate count" );
841  return lst[section];
842  }
843  else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
844  {
845  if ( section == 4 ) // Count
846  {
847  return tr( "Number of features in this rule." );
848  }
849  else if ( section == 5 ) // Duplicate count
850  {
851  return tr( "Number of features in this rule which are also present in other rule(s)." );
852  }
853  }
854 
855  return QVariant();
856 }
857 
859 {
860  if ( parent.column() > 0 )
861  return 0;
862 
863  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
864 
865  return parentRule->children().count();
866 }
867 
869 {
870  return 6;
871 }
872 
873 QModelIndex QgsRuleBasedRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
874 {
875  if ( hasIndex( row, column, parent ) )
876  {
877  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
878  QgsRuleBasedRendererV2::Rule* childRule = parentRule->children()[row];
879  return createIndex( row, column, childRule );
880  }
881  return QModelIndex();
882 }
883 
885 {
886  if ( !index.isValid() )
887  return QModelIndex();
888 
889  QgsRuleBasedRendererV2::Rule* childRule = ruleForIndex( index );
890  QgsRuleBasedRendererV2::Rule* parentRule = childRule->parent();
891 
892  if ( parentRule == mR->rootRule() )
893  return QModelIndex();
894 
895  // this is right: we need to know row number of our parent (in our grandparent)
896  int row = parentRule->parent()->children().indexOf( parentRule );
897 
898  return createIndex( row, 0, parentRule );
899 }
900 
901 bool QgsRuleBasedRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
902 {
903  if ( !index.isValid() )
904  return false;
905 
907 
908  if ( role == Qt::CheckStateRole )
909  {
910  rule->setCheckState( value.toInt() == Qt::Checked );
911  emit dataChanged( index, index );
912  return true;
913  }
914 
915  if ( role != Qt::EditRole )
916  return false;
917 
918  switch ( index.column() )
919  {
920  case 0: // label
921  rule->setLabel( value.toString() );
922  break;
923  case 1: // filter
924  rule->setFilterExpression( value.toString() );
925  break;
926  case 2: // scale min
927  rule->setScaleMaxDenom( value.toInt() );
928  break;
929  case 3: // scale max
930  rule->setScaleMinDenom( value.toInt() );
931  break;
932  default:
933  return false;
934  }
935 
936  emit dataChanged( index, index );
937  return true;
938 }
939 
941 {
942  return Qt::MoveAction; // | Qt::CopyAction
943 }
944 
946 {
947  QStringList types;
948  types << "application/vnd.text.list";
949  return types;
950 }
951 
952 QMimeData *QgsRuleBasedRendererV2Model::mimeData( const QModelIndexList &indexes ) const
953 {
954  QMimeData *mimeData = new QMimeData();
955  QByteArray encodedData;
956 
957  QDataStream stream( &encodedData, QIODevice::WriteOnly );
958 
959  foreach ( const QModelIndex &index, indexes )
960  {
961  // each item consists of several columns - let's add it with just first one
962  if ( !index.isValid() || index.column() != 0 )
963  continue;
964 
965  // we use a clone of the existing rule because it has a new unique rule key
966  // non-unique rule keys would confuse other components using them (e.g. legend)
967  QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index )->clone();
968  QDomDocument doc;
969  QgsSymbolV2Map symbols;
970 
971  QDomElement rootElem = doc.createElement( "rule_mime" );
972  QDomElement rulesElem = rule->save( doc, symbols );
973  rootElem.appendChild( rulesElem );
974  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
975  rootElem.appendChild( symbolsElem );
976  doc.appendChild( rootElem );
977 
978  delete rule;
979 
980  stream << doc.toString( -1 );
981  }
982 
983  mimeData->setData( "application/vnd.text.list", encodedData );
984  return mimeData;
985 }
986 
988  Qt::DropAction action, int row, int column, const QModelIndex &parent )
989 {
990  Q_UNUSED( column );
991 
992  if ( action == Qt::IgnoreAction )
993  return true;
994 
995  if ( !data->hasFormat( "application/vnd.text.list" ) )
996  return false;
997 
998  if ( parent.column() > 0 )
999  return false;
1000 
1001  QByteArray encodedData = data->data( "application/vnd.text.list" );
1002  QDataStream stream( &encodedData, QIODevice::ReadOnly );
1003  int rows = 0;
1004 
1005  if ( row == -1 )
1006  {
1007  // the item was dropped at a parent - we may decide where to put the items - let's append them
1008  row = rowCount( parent );
1009  }
1010 
1011  while ( !stream.atEnd() )
1012  {
1013  QString text;
1014  stream >> text;
1015 
1016  QDomDocument doc;
1017  if ( !doc.setContent( text ) )
1018  continue;
1019  QDomElement rootElem = doc.documentElement();
1020  if ( rootElem.tagName() != "rule_mime" )
1021  continue;
1022  QDomElement symbolsElem = rootElem.firstChildElement( "symbols" );
1023  if ( symbolsElem.isNull() )
1024  continue;
1025  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1026  QDomElement ruleElem = rootElem.firstChildElement( "rule" );
1028 
1029  insertRule( parent, row + rows, rule );
1030 
1031  ++rows;
1032  }
1033  return true;
1034 }
1035 
1037 {
1038  if ( index.isValid() )
1039  return static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
1040  return mR->rootRule();
1041 }
1042 
1043 bool QgsRuleBasedRendererV2Model::removeRows( int row, int count, const QModelIndex & parent )
1044 {
1045  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
1046 
1047  if ( row < 0 || row >= parentRule->children().count() )
1048  return false;
1049 
1050  QgsDebugMsg( QString( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ) );
1051 
1052  beginRemoveRows( parent, row, row + count - 1 );
1053 
1054  for ( int i = 0; i < count; i++ )
1055  {
1056  if ( row < parentRule->children().count() )
1057  {
1058  //QgsRuleBasedRendererV2::Rule* r = parentRule->children()[row];
1059  parentRule->removeChildAt( row );
1060  //parentRule->takeChildAt( row );
1061  }
1062  else
1063  {
1064  QgsDebugMsg( "trying to remove invalid index - this should not happen!" );
1065  }
1066  }
1067 
1068  endRemoveRows();
1069 
1070  return true;
1071 }
1072 
1073 
1075 {
1076  beginInsertRows( parent, before, before );
1077 
1078  QgsDebugMsg( QString( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ) );
1079 
1080  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
1081  parentRule->insertChild( before, newrule );
1082 
1083  endInsertRows();
1084 }
1085 
1087 {
1088  emit dataChanged( index( row, 0, parent ),
1089  index( row, columnCount( parent ), parent ) );
1090 }
1091 
1093 {
1094  emit dataChanged( index( 0, 0, idx ),
1095  index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1096 
1097  for ( int i = 0; i < rowCount( idx ); i++ )
1098  {
1099  updateRule( index( i, 0, idx ) );
1100  }
1101 }
1102 
1103 
1105 {
1106  if ( !index.isValid() )
1107  return;
1108 
1109  beginRemoveRows( index.parent(), index.row(), index.row() );
1110 
1111  QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index );
1112  rule->parent()->removeChild( rule );
1113 
1114  endRemoveRows();
1115 }
1116 
1118 {
1119  int row = rowCount( parent ); // only consider appending
1120  beginInsertRows( parent, row, row + count - 1 );
1121 }
1122 
1124 {
1125  emit endInsertRows();
1126 }
1127 
1129 {
1130  mFeatureCountMap = theCountMap;
1131  updateRule( QModelIndex() );
1132 }
1133 
1135 {
1137  updateRule( QModelIndex() );
1138 }
bool hasIndex(int row, int column, const QModelIndex &parent) const
void customContextMenuRequested(const QPoint &pos)
QgsRuleBasedRendererV2Widget(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:88
void clear()
Wrapper for iterator of features from vector data provider or vector layer.
static QgsSymbolV2Map loadSymbols(QDomElement &element)
QByteArray toByteArray() const
static unsigned index
void refineRuleScalesGui(const QModelIndexList &index)
void setFeatureCounts(QMap< QgsRuleBasedRendererV2::Rule *, QgsRuleBasedRendererV2Count > theCountMap)
QByteArray data(const QString &mimeType) const
void setupUi(QWidget *widget)
QgsSymbolV2SelectorDialog * mSymbolSelector
int bottom() const
virtual void reject()
void keyPressEvent(QKeyEvent *event) override
QgsRuleBasedRendererV2Model * mModel
const QgsCategoryList & categories() const
void contextMenuViewCategories(const QPoint &p)
QModelIndex currentIndex() const
const QMimeData * mimeData(Mode mode) const
QDomNode appendChild(const QDomNode &newChild)
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
void setMaximum(int maximum)
void setWindowModality(Qt::WindowModality windowModality)
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QgsFields fields() const
Returns the list of fields of this layer.
virtual bool hasFormat(const QString &mimeType) const
static QString iconPath(QString iconFile)
Returns path to the desired icon file.
QString toString(int indent) const
void setScaleMaxDenom(int scaleMaxDenom)
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
virtual QgsSymbolV2 * clone() const =0
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
void rejected()
void setRendererScale(double scale)
void setIcon(const QIcon &icon)
const QObjectList & children() const
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
void addAction(QAction *action)
QList< QgsSymbolV2 * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
void refineRuleCategoriesGui(const QModelIndexList &index)
QgsRuleBasedRendererV2Model(QgsRuleBasedRendererV2 *r)
Qt::DropActions supportedDropActions() const override
Rule * clone() const
clone this rule, return new instance
void accepted()
int exec()
QgsRuleBasedRendererV2::RuleList mCopyBuffer
QDomElement documentElement() const
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
void updateRule(const QModelIndex &parent, int row)
RuleList rulesForFeature(QgsFeature &feat, QgsRenderContext *context=0)
tell which rules will be used to render the feature
void saveSectionWidth(int section, int oldSize, int newSize)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:162
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
void clear()
QgsRuleBasedRendererV2::Rule * currentRule()
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, QString rule="") override
return a list of item text / symbol
QString tr(const char *sourceText, const char *disambiguation, int n)
StandardButton information(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
void removeChildAt(int i)
delete child rule
int size() const
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const override
int indexOf(const T &value, int from) const
void resizeSection(int logicalIndex, int size)
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
long featureCount(QgsSymbolV2 *symbol)
Number of features rendered with specified symbol.
QList< Key > keys() const
void setValue(const QString &key, const QVariant &value)
void setValue(int progress)
bool isValid() const
void refineRuleRangesGui(const QModelIndexList &index)
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
QString number(int n, int base)
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
int count(const T &value) const
QgsVectorLayer * mLayer
void append(const T &value)
static void refineRuleCategories(Rule *initialRule, QgsCategorizedSymbolRendererV2 *r)
take a rule and create a list of new rules based on the categories from categorized symbol renderer ...
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
void setLayout(QLayout *layout)
int toInt(bool *ok) const
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
virtual void stopRender(QgsRenderContext &context) override
QClipboard * clipboard()
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
bool restoreGeometry(const QByteArray &geometry)
static QString _formatScale(int denom)
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
int toInt(bool *ok, int base) const
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, QString tagName, QDomDocument &doc)
static Rule * create(QDomElement &ruleElem, QgsSymbolV2Map &symbolMap)
bool isEmpty() const
void beginRemoveRows(const QModelIndex &parent, int first, int last)
int row() const
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void setOverrideCursor(const QCursor &cursor)
QDomElement save(QDomDocument &doc, QgsSymbolV2Map &symbolMap)
This class keeps data about a rules for rule-based renderer.
void restoreOverrideCursor()
void * internalPointer() const
QModelIndex parent() const
QGis::GeometryType geometryType() const
Returns point, line or polygon.
QMap< QgsRuleBasedRendererV2::Rule *, QgsRuleBasedRendererV2Count > mFeatureCountMap
void setMimeData(QMimeData *src, Mode mode)
QModelIndex parent() const
void removeRule(const QModelIndex &index)
static QgsRendererV2Widget * create(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
bool atEnd() const
static void refineRuleRanges(Rule *initialRule, QgsGraduatedSymbolRendererV2 *r)
take a rule and create a list of new rules based on the ranges from graduated symbol renderer ...
QString dump(int offset=0) const
virtual void accept()
QgsRendererRulePropsDialog(QgsRuleBasedRendererV2::Rule *rule, QgsVectorLayer *layer, QgsStyleV2 *style, QWidget *parent=0)
void currentRuleChanged(const QModelIndex &current=QModelIndex(), const QModelIndex &previous=QModelIndex())
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, QFlags< Qt::WindowType > flags, QFlags< Qt::InputMethodHint > inputMethodHints)
QModelIndex createIndex(int row, int column, void *ptr) const
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
int key() const
void setShortcut(const QKeySequence &shortcut)
QgsExpressionContext & expressionContext()
Gets the expression context.
QMimeData * mimeData(const QModelIndexList &indexes) const override
void setItalic(bool enable)
void beginInsertRows(const QModelIndex &parent, int first, int last)
bool isNull() const
QString & replace(int position, int n, QChar after)
void setForceOrderingEnabled(bool enabled)
QVariant value(const QString &key, const QVariant &defaultValue) const
QStringList mimeTypes() const override
Contains information about the context of a rendering operation.
void insertRule(const QModelIndex &parent, int before, QgsRuleBasedRendererV2::Rule *newrule)
static QgsSymbolV2 * defaultSymbol(QGis::GeometryType geomType)
return new default symbol for specified geometry type
QByteArray saveGeometry() const
typedef DropActions
const QgsRangeList & ranges() const
QAction * addMenu(QMenu *menu)
When drawing a vector layer with rule-based renderer, it goes through the rules and draws features wi...
QgsRuleBasedRendererV2::Rule * ruleForIndex(const QModelIndex &index) const
void setWindowTitle(const QString &)
QDomElement firstChildElement(const QString &tagName) const
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
provide model index for parent's child item
virtual int columnCount(const QModelIndex &=QModelIndex()) const override
QgsRuleBasedRendererV2::RuleList selectedRules()
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
StandardButton critical(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
int column() const
Base class for renderer settings widgets.
QgsRuleBasedRendererV2::Rule * mRule
void setDescription(QString description)
void setSymbol(QgsSymbolV2 *sym)
set a new symbol (or NULL). Deletes old symbol.
StandardButton warning(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
static QgsRuleBasedRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsRuleBasedRendererV2 from an existing renderer.
QString tagName() const
void setData(const QString &mimeType, const QByteArray &data)
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
const_iterator constEnd() const
void insertChild(int i, Rule *rule)
add child rule, take ownership, sets this as parent
QDomElement createElement(const QString &tagName)
bool nextFeature(QgsFeature &f)
const_iterator constBegin() const
bool isValid() const
Geometry is not required. It may still be returned if e.g. required for a filter condition.
void setScaleMinDenom(int scaleMinDenom)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
Represents a vector layer which manages a vector based data sets.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
void setFilterExpression(QString filterExp)
int count(const Key &key) const
A generic dialog for building expression strings.
void willAddRules(const QModelIndex &parent, int count)
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
static void refineRuleScales(Rule *initialRule, QList< int > scales)
take a rule and create a list of new rules with intervals of scales given by the passed scale denomin...
typedef ItemFlags