QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules 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 );
86  contextMenu->addMenu( mRefineMenu );
87 
88  btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.png" ) ) );
89  btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.png" ) ) );
90  btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.png" ) ) );
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 
200 void QgsRuleBasedRendererV2Widget::currentRuleChanged( const QModelIndex& current, const QModelIndex& previous )
201 {
202  Q_UNUSED( previous );
203  btnRefineRule->setEnabled( current.isValid() );
204 }
205 
206 
212 #include <QDialogButtonBox>
213 #include <QInputDialog>
214 #include <QKeyEvent>
215 #include <QClipboard>
216 
218 {
219  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
220 
221  if ( indexlist.isEmpty() )
222  return;
223 
224 
225  if ( type == 0 ) // categories
226  refineRuleCategoriesGui( indexlist );
227  else if ( type == 1 ) // ranges
228  refineRuleRangesGui( indexlist );
229  else // scales
230  refineRuleScalesGui( indexlist );
231 
232  // TODO: set initial rule's symbol to NULL (?)
233 
234  // show the newly added rules
235  foreach ( QModelIndex index, indexlist )
236  viewRules->expand( index );
237 }
238 
240 {
241  refineRule( 0 );
242 }
243 
245 {
246  refineRule( 1 );
247 }
248 
250 {
251  refineRule( 2 );
252 }
253 
254 void QgsRuleBasedRendererV2Widget::refineRuleCategoriesGui( const QModelIndexList& indexList )
255 {
256  QDialog dlg;
257  dlg.setWindowTitle( tr( "Refine a rule to categories" ) );
258  QVBoxLayout* l = new QVBoxLayout();
260  l->addWidget( w );
261  QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
262  l->addWidget( bb );
263  connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
264  connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
265  dlg.setLayout( l );
266 
267  if ( !dlg.exec() )
268  return;
269 
270  // create new rules
272  foreach ( QModelIndex index, indexList )
273  {
274  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
275  mModel->willAddRules( index, r->categories().count() );
277  }
279 }
280 
281 
282 void QgsRuleBasedRendererV2Widget::refineRuleRangesGui( const QModelIndexList& indexList )
283 {
284 
285 
286  QDialog dlg;
287  dlg.setWindowTitle( tr( "Refine a rule to ranges" ) );
288  QVBoxLayout* l = new QVBoxLayout();
290  l->addWidget( w );
291  QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
292  l->addWidget( bb );
293  connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
294  connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
295  dlg.setLayout( l );
296 
297  if ( !dlg.exec() )
298  return;
299 
300  // create new rules
302  foreach ( QModelIndex index, indexList )
303  {
304  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
305  mModel->willAddRules( index, r->ranges().count() );
307  }
309 }
310 
311 void QgsRuleBasedRendererV2Widget::refineRuleScalesGui( const QModelIndexList& indexList )
312 {
313  foreach ( QModelIndex index, indexList )
314  {
315  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
316 
317  // If any of the rules don't have a symbol let the user know and exit.
318  if ( initialRule->symbol() == NULL )
319  {
320  QMessageBox::warning( this, tr( "Scale refinement" ), tr( "Parent rule %1 must have a symbol for this operation." ).arg( initialRule->label() ) );
321  return;
322  }
323  }
324 
325  QString txt = QInputDialog::getText( this,
326  tr( "Scale refinement" ),
327  tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) );
328  if ( txt.isEmpty() )
329  return;
330 
331  QList<int> scales;
332  bool ok;
333  foreach ( QString item, txt.split( ',' ) )
334  {
335  int scale = item.toInt( &ok );
336  if ( ok )
337  scales.append( scale );
338  else
339  QMessageBox::information( this, tr( "Error" ), QString( tr( "\"%1\" is not valid scale denominator, ignoring it." ) ).arg( item ) );
340  }
341 
342  foreach ( QModelIndex index, indexList )
343  {
344  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
345  mModel->willAddRules( index, scales.count() + 1 );
346  QgsRuleBasedRendererV2::refineRuleScales( initialRule, scales );
347  }
349 }
350 
352 {
353  QList<QgsSymbolV2*> symbolList;
354 
355  if ( !mRenderer )
356  {
357  return symbolList;
358  }
359 
360  QItemSelection sel = viewRules->selectionModel()->selection();
361  foreach ( QItemSelectionRange range, sel )
362  {
363  QModelIndex parent = range.parent();
364  QgsRuleBasedRendererV2::Rule* parentRule = mModel->ruleForIndex( parent );
365  QgsRuleBasedRendererV2::RuleList& children = parentRule->children();
366  for ( int row = range.top(); row <= range.bottom(); row++ )
367  {
368  symbolList.append( children[row]->symbol() );
369  }
370  }
371 
372  return symbolList;
373 }
374 
376 {
378  QItemSelection sel = viewRules->selectionModel()->selection();
379  foreach ( QItemSelectionRange range, sel )
380  {
381  QModelIndex parent = range.parent();
382  QgsRuleBasedRendererV2::Rule* parentRule = mModel->ruleForIndex( parent );
383  QgsRuleBasedRendererV2::RuleList& children = parentRule->children();
384  for ( int row = range.top(); row <= range.bottom(); row++ )
385  {
386  rl.append( children[row]->clone() );
387  }
388  }
389  return rl;
390 }
391 
393 {
394  // TODO: model/view
395  /*
396  if ( treeRules )
397  {
398  treeRules->populateRules();
399  }
400  */
401 }
402 
404 {
405  if ( !event )
406  {
407  return;
408  }
409 
410  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
411  {
412  qDeleteAll( mCopyBuffer );
413  mCopyBuffer.clear();
415  }
416  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
417  {
418  QgsRuleBasedRendererV2::RuleList::const_iterator rIt = mCopyBuffer.constBegin();
419  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
420  {
421  int rows = mModel->rowCount();
422  mModel->insertRule( QModelIndex(), rows, ( *rIt )->clone() );
423  }
424  }
425 }
426 
427 #include "qgssymbollevelsv2dialog.h"
428 
430 {
432 
433  QgsSymbolLevelsV2Dialog dlg( lst, true, this );
434  dlg.setForceOrderingEnabled( true );
435 
436  dlg.exec();
437 }
438 
439 void QgsRuleBasedRendererV2Widget::saveSectionWidth( int section, int oldSize, int newSize )
440 {
441  Q_UNUSED( oldSize );
442  // skip last section, as it stretches
443  if ( section == 5 )
444  return;
445  QSettings settings;
446  QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
447  settings.setValue( path, newSize );
448 }
449 
451 {
452  QSettings settings;
453  QString path = "/Windows/RuleBasedTree/sectionWidth/";
454  QHeaderView* head = viewRules->header();
455  head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
456  head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
457  head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
458  head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
459  head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
460  head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
461 }
462 
464 {
465  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
466  QgsDebugMsg( QString( "%1" ).arg( indexlist.count() ) );
467 
468  if ( indexlist.isEmpty() )
469  return;
470 
471  QMimeData* mime = mModel->mimeData( indexlist );
472  QApplication::clipboard()->setMimeData( mime );
473 }
474 
476 {
477  const QMimeData* mime = QApplication::clipboard()->mimeData();
478  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
479  QModelIndex index;
480  if ( indexlist.isEmpty() )
481  index = mModel->index( mModel->rowCount(), 0 );
482  else
483  index = indexlist.first();
484  mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
485 }
486 
487 
489 {
490  if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
491  {
492  return;
493  }
494  QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> countMap;
495 
497  // insert all so that we have counts 0
498  foreach ( QgsRuleBasedRendererV2::Rule* rule, ruleList )
499  {
500  countMap[rule].count = 0;
501  countMap[rule].duplicateCount = 0;
502  }
503 
505 
506  QgsRenderContext renderContext;
507  renderContext.setRendererScale( 0 ); // ignore scale
508  mRenderer->startRender( renderContext, mLayer->pendingFields() );
509 
510  int nFeatures = mLayer->pendingFeatureCount();
511  QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, nFeatures );
512  p.setWindowModality( Qt::WindowModal );
513  int featuresCounted = 0;
514 
515  QgsFeature f;
516  while ( fit.nextFeature( f ) )
517  {
519 
520  foreach ( QgsRuleBasedRendererV2::Rule* rule, featureRuleList )
521  {
522  countMap[rule].count++;
523  if ( featureRuleList.size() > 1 )
524  {
525  countMap[rule].duplicateCount++;
526  }
527  foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, featureRuleList )
528  {
529  if ( duplicateRule == rule ) continue;
530  countMap[rule].duplicateCountMap[duplicateRule] += 1;
531  }
532  }
533  ++featuresCounted;
534  if ( featuresCounted % 50 == 0 )
535  {
536  if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
537  {
538  p.setMaximum( 0 );
539  }
540  p.setValue( featuresCounted );
541  if ( p.wasCanceled() )
542  {
543  return;
544  }
545  }
546  }
547  p.setValue( nFeatures );
548 
549  mRenderer->stopRender( renderContext );
550 
551 #ifdef QGISDEBUG
552  foreach ( QgsRuleBasedRendererV2::Rule *rule, countMap.keys() )
553  {
554  QgsDebugMsg( QString( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ) );
555  }
556 #endif
557 
558  mModel->setFeatureCounts( countMap );
559 }
560 
562 
564  : QDialog( parent ), mRule( rule ), mLayer( layer ), mSymbolSelector( NULL ), mSymbol( NULL )
565 {
566  setupUi( this );
567 #ifdef Q_OS_MAC
568  setWindowModality( Qt::WindowModal );
569 #endif
570 
571  connect( buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
572  connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
573 
574  editFilter->setText( mRule->filterExpression() );
575  editFilter->setToolTip( mRule->filterExpression() );
576  editLabel->setText( mRule->label() );
577  editDescription->setText( mRule->description() );
578  editDescription->setToolTip( mRule->description() );
579 
580  if ( mRule->dependsOnScale() )
581  {
582  groupScale->setChecked( true );
583  // caution: rule uses scale denom, scale widget uses true scales
584  mScaleRangeWidget->setMaximumScale( 1.0 / rule->scaleMinDenom() );
585  mScaleRangeWidget->setMinimumScale( 1.0 / rule->scaleMaxDenom() );
586  }
587 
588  if ( mRule->symbol() )
589  {
590  groupSymbol->setChecked( true );
591  mSymbol = mRule->symbol()->clone(); // use a clone!
592  }
593  else
594  {
595  groupSymbol->setChecked( false );
597  }
598 
599  mSymbolSelector = new QgsSymbolV2SelectorDialog( mSymbol, style, mLayer, this, true );
600  QVBoxLayout* l = new QVBoxLayout;
601  l->addWidget( mSymbolSelector );
602  groupSymbol->setLayout( l );
603 
604  connect( btnExpressionBuilder, SIGNAL( clicked() ), this, SLOT( buildExpression() ) );
605  connect( btnTestFilter, SIGNAL( clicked() ), this, SLOT( testFilter() ) );
606 
607  QSettings settings;
608  restoreGeometry( settings.value( "/Windows/QgsRendererRulePropsDialog/geometry" ).toByteArray() );
609 }
610 
612 {
613  delete mSymbol;
614  QSettings settings;
615  settings.setValue( "/Windows/QgsRendererRulePropsDialog/geometry", saveGeometry() );
616 }
617 
619 {
620  QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this );
621 
622  if ( dlg.exec() )
623  editFilter->setText( dlg.expressionText() );
624 }
625 
627 {
628  QgsExpression filter( editFilter->text() );
629  if ( filter.hasParserError() )
630  {
631  QMessageBox::critical( this, tr( "Error" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
632  return;
633  }
634 
635  const QgsFields& fields = mLayer->pendingFields();
636 
637  if ( !filter.prepare( fields ) )
638  {
639  QMessageBox::critical( this, tr( "Evaluation error" ), filter.evalErrorString() );
640  return;
641  }
642 
643  QApplication::setOverrideCursor( Qt::WaitCursor );
644 
646 
647  int count = 0;
648  QgsFeature f;
649  while ( fit.nextFeature( f ) )
650  {
651  QVariant value = filter.evaluate( &f );
652  if ( value.toInt() != 0 )
653  count++;
654  if ( filter.hasEvalError() )
655  break;
656  }
657 
658  QApplication::restoreOverrideCursor();
659 
660  QMessageBox::information( this, tr( "Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
661 }
662 
664 {
665  mRule->setFilterExpression( editFilter->text() );
666  mRule->setLabel( editLabel->text() );
667  mRule->setDescription( editDescription->text() );
668  // caution: rule uses scale denom, scale widget uses true scales
669  mRule->setScaleMinDenom( groupScale->isChecked() ? mScaleRangeWidget->minimumScaleDenom() : 0 );
670  mRule->setScaleMaxDenom( groupScale->isChecked() ? mScaleRangeWidget->maximumScaleDenom() : 0 );
671  mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : NULL );
672 
673  QDialog::accept();
674 }
675 
677 
678 /*
679  setDragEnabled(true);
680  viewport()->setAcceptDrops(true);
681  setDropIndicatorShown(true);
682  setDragDropMode(QAbstractItemView::InternalMove);
683 */
684 
685 static QString _formatScale( int denom )
686 {
687  if ( denom != 0 )
688  {
689  QString txt = QString( "1:%L1" ).arg( denom );
690  return txt;
691  }
692  else
693  return QString();
694 }
695 
697 
699  : mR( r )
700 {
701 }
702 
703 Qt::ItemFlags QgsRuleBasedRendererV2Model::flags( const QModelIndex &index ) const
704 {
705  if ( !index.isValid() )
706  return Qt::ItemIsDropEnabled;
707 
708  // allow drop only at first column
709  Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
710 
711  Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
712 
713  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
714  Qt::ItemIsEditable | checkable |
715  Qt::ItemIsDragEnabled | drop;
716 }
717 
718 QVariant QgsRuleBasedRendererV2Model::data( const QModelIndex &index, int role ) const
719 {
720  if ( !index.isValid() )
721  return QVariant();
722 
724 
725  if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
726  {
727  switch ( index.column() )
728  {
729  case 0: return rule->label();
730  case 1:
731  if ( rule->isElse() )
732  {
733  return "ELSE";
734  }
735  else
736  {
737  return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
738  }
739  case 2: return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
740  case 3: return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
741  case 4:
742  if ( mFeatureCountMap.count( rule ) == 1 )
743  {
744  return QVariant( mFeatureCountMap[rule].count );
745  }
746  return QVariant();
747  case 5:
748  if ( mFeatureCountMap.count( rule ) == 1 )
749  {
750  if ( role == Qt::DisplayRole )
751  {
752  return QVariant( mFeatureCountMap[rule].duplicateCount );
753  }
754  else // tooltip - detailed info about duplicates
755  {
756  if ( mFeatureCountMap[rule].duplicateCount > 0 )
757  {
758  QString tip = "<p style='margin:0px;'><ul>";
759  foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, mFeatureCountMap[rule].duplicateCountMap.keys() )
760  {
761  QString label = duplicateRule->label().replace( "&", "&amp;" ).replace( ">", "&gt;" ).replace( "<", "&lt;" );
762  tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( mFeatureCountMap[rule].duplicateCountMap[duplicateRule] ).arg( label );
763  }
764  tip += "</ul>";
765  return tip;
766  }
767  else
768  {
769  return 0;
770  }
771  }
772  }
773  return QVariant();
774  default: return QVariant();
775  }
776  }
777  else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
778  {
779  return QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
780  }
781  else if ( role == Qt::TextAlignmentRole )
782  {
783  return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
784  }
785  else if ( role == Qt::FontRole && index.column() == 1 )
786  {
787  if ( rule->isElse() )
788  {
789  QFont italicFont;
790  italicFont.setItalic( true );
791  return italicFont;
792  }
793  return QVariant();
794  }
795  else if ( role == Qt::EditRole )
796  {
797  switch ( index.column() )
798  {
799  case 0: return rule->label();
800  case 1: return rule->filterExpression();
801  case 2: return rule->scaleMinDenom();
802  case 3: return rule->scaleMaxDenom();
803  default: return QVariant();
804  }
805  }
806  else if ( role == Qt::CheckStateRole )
807  {
808  if ( index.column() != 0 )
809  return QVariant();
810  return rule->checkState() ? Qt::Checked : Qt::Unchecked;
811  }
812  else
813  return QVariant();
814 }
815 
816 QVariant QgsRuleBasedRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
817 {
818  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
819  {
820  QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max. scale" ) << tr( "Count" ) << tr( "Duplicate count" );
821  return lst[section];
822  }
823  else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
824  {
825  if ( section == 4 ) // Count
826  {
827  return tr( "Number of features in this rule." );
828  }
829  else if ( section == 5 ) // Duplicate count
830  {
831  return tr( "Number of features in this rule which are also present in other rule(s)." );
832  }
833  }
834 
835  return QVariant();
836 }
837 
838 int QgsRuleBasedRendererV2Model::rowCount( const QModelIndex &parent ) const
839 {
840  if ( parent.column() > 0 )
841  return 0;
842 
843  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
844 
845  return parentRule->children().count();
846 }
847 
848 int QgsRuleBasedRendererV2Model::columnCount( const QModelIndex & ) const
849 {
850  return 6;
851 }
852 
853 QModelIndex QgsRuleBasedRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
854 {
855  if ( hasIndex( row, column, parent ) )
856  {
857  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
858  QgsRuleBasedRendererV2::Rule* childRule = parentRule->children()[row];
859  return createIndex( row, column, childRule );
860  }
861  return QModelIndex();
862 }
863 
864 QModelIndex QgsRuleBasedRendererV2Model::parent( const QModelIndex &index ) const
865 {
866  if ( !index.isValid() )
867  return QModelIndex();
868 
869  QgsRuleBasedRendererV2::Rule* childRule = ruleForIndex( index );
870  QgsRuleBasedRendererV2::Rule* parentRule = childRule->parent();
871 
872  if ( parentRule == mR->rootRule() )
873  return QModelIndex();
874 
875  // this is right: we need to know row number of our parent (in our grandparent)
876  int row = parentRule->parent()->children().indexOf( parentRule );
877 
878  return createIndex( row, 0, parentRule );
879 }
880 
881 bool QgsRuleBasedRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
882 {
883  if ( !index.isValid() )
884  return false;
885 
887 
888  if ( role == Qt::CheckStateRole )
889  {
890  rule->setCheckState( value.toInt() == Qt::Checked );
891  emit dataChanged( index, index );
892  return true;
893  }
894 
895  if ( role != Qt::EditRole )
896  return false;
897 
898  switch ( index.column() )
899  {
900  case 0: // label
901  rule->setLabel( value.toString() );
902  break;
903  case 1: // filter
904  rule->setFilterExpression( value.toString() );
905  break;
906  case 2: // scale min
907  rule->setScaleMinDenom( value.toInt() );
908  break;
909  case 3: // scale max
910  rule->setScaleMaxDenom( value.toInt() );
911  break;
912  default:
913  return false;
914  }
915 
916  emit dataChanged( index, index );
917  return true;
918 }
919 
921 {
922  return Qt::MoveAction; // | Qt::CopyAction
923 }
924 
926 {
927  QStringList types;
928  types << "application/vnd.text.list";
929  return types;
930 }
931 
932 QMimeData *QgsRuleBasedRendererV2Model::mimeData( const QModelIndexList &indexes ) const
933 {
934  QMimeData *mimeData = new QMimeData();
935  QByteArray encodedData;
936 
937  QDataStream stream( &encodedData, QIODevice::WriteOnly );
938 
939  foreach ( const QModelIndex &index, indexes )
940  {
941  // each item consists of several columns - let's add it with just first one
942  if ( !index.isValid() || index.column() != 0 )
943  continue;
944 
945  // we use a clone of the existing rule because it has a new unique rule key
946  // non-unique rule keys would confuse other components using them (e.g. legend)
947  QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index )->clone();
948  QDomDocument doc;
949  QgsSymbolV2Map symbols;
950 
951  QDomElement rootElem = doc.createElement( "rule_mime" );
952  QDomElement rulesElem = rule->save( doc, symbols );
953  rootElem.appendChild( rulesElem );
954  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
955  rootElem.appendChild( symbolsElem );
956  doc.appendChild( rootElem );
957 
958  delete rule;
959 
960  stream << doc.toString( -1 );
961  }
962 
963  mimeData->setData( "application/vnd.text.list", encodedData );
964  return mimeData;
965 }
966 
967 bool QgsRuleBasedRendererV2Model::dropMimeData( const QMimeData *data,
968  Qt::DropAction action, int row, int column, const QModelIndex &parent )
969 {
970  Q_UNUSED( column );
971 
972  if ( action == Qt::IgnoreAction )
973  return true;
974 
975  if ( !data->hasFormat( "application/vnd.text.list" ) )
976  return false;
977 
978  if ( parent.column() > 0 )
979  return false;
980 
981  QByteArray encodedData = data->data( "application/vnd.text.list" );
982  QDataStream stream( &encodedData, QIODevice::ReadOnly );
983  int rows = 0;
984 
985  if ( row == -1 )
986  {
987  // the item was dropped at a parent - we may decide where to put the items - let's append them
988  row = rowCount( parent );
989  }
990 
991  while ( !stream.atEnd() )
992  {
993  QString text;
994  stream >> text;
995 
996  QDomDocument doc;
997  if ( !doc.setContent( text ) )
998  continue;
999  QDomElement rootElem = doc.documentElement();
1000  if ( rootElem.tagName() != "rule_mime" )
1001  continue;
1002  QDomElement symbolsElem = rootElem.firstChildElement( "symbols" );
1003  if ( symbolsElem.isNull() )
1004  continue;
1005  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1006  QDomElement ruleElem = rootElem.firstChildElement( "rule" );
1008 
1009  insertRule( parent, row + rows, rule );
1010 
1011  ++rows;
1012  }
1013  return true;
1014 }
1015 
1017 {
1018  if ( index.isValid() )
1019  return static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
1020  return mR->rootRule();
1021 }
1022 
1023 bool QgsRuleBasedRendererV2Model::removeRows( int row, int count, const QModelIndex & parent )
1024 {
1025  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
1026 
1027  if ( row < 0 || row >= parentRule->children().count() )
1028  return false;
1029 
1030  QgsDebugMsg( QString( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ) );
1031 
1032  beginRemoveRows( parent, row, row + count - 1 );
1033 
1034  for ( int i = 0; i < count; i++ )
1035  {
1036  if ( row < parentRule->children().count() )
1037  {
1038  //QgsRuleBasedRendererV2::Rule* r = parentRule->children()[row];
1039  parentRule->removeChildAt( row );
1040  //parentRule->takeChildAt( row );
1041  }
1042  else
1043  {
1044  QgsDebugMsg( "trying to remove invalid index - this should not happen!" );
1045  }
1046  }
1047 
1048  endRemoveRows();
1049 
1050  return true;
1051 }
1052 
1053 
1054 void QgsRuleBasedRendererV2Model::insertRule( const QModelIndex& parent, int before, QgsRuleBasedRendererV2::Rule* newrule )
1055 {
1056  beginInsertRows( parent, before, before );
1057 
1058  QgsDebugMsg( QString( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ) );
1059 
1060  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
1061  parentRule->insertChild( before, newrule );
1062 
1063  endInsertRows();
1064 }
1065 
1066 void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& parent, int row )
1067 {
1068  emit dataChanged( index( row, 0, parent ),
1069  index( row, columnCount( parent ), parent ) );
1070 }
1071 
1072 void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& idx )
1073 {
1074  emit dataChanged( index( 0, 0, idx ),
1075  index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1076 
1077  for ( int i = 0; i < rowCount( idx ); i++ )
1078  {
1079  updateRule( index( i, 0, idx ) );
1080  }
1081 }
1082 
1083 
1085 {
1086  if ( !index.isValid() )
1087  return;
1088 
1089  beginRemoveRows( index.parent(), index.row(), index.row() );
1090 
1091  QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index );
1092  rule->parent()->removeChild( rule );
1093 
1094  endRemoveRows();
1095 }
1096 
1097 void QgsRuleBasedRendererV2Model::willAddRules( const QModelIndex& parent, int count )
1098 {
1099  int row = rowCount( parent ); // only consider appending
1100  beginInsertRows( parent, row, row + count - 1 );
1101 }
1102 
1104 {
1105  emit endInsertRows();
1106 }
1107 
1108 void QgsRuleBasedRendererV2Model::setFeatureCounts( QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> theCountMap )
1109 {
1110  mFeatureCountMap = theCountMap;
1111  updateRule( QModelIndex() );
1112 }
1113 
1115 {
1116  mFeatureCountMap.clear();
1117  updateRule( QModelIndex() );
1118 }
QgsRuleBasedRendererV2Widget(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
QMap< QString, QgsSymbolV2 * > QgsSymbolV2Map
Definition: qgsrendererv2.h:39
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:87
Wrapper for iterator of features from vector data provider or vector layer.
static QgsSymbolV2Map loadSymbols(QDomElement &element)
static unsigned index
void refineRuleScalesGui(const QModelIndexList &index)
void setFeatureCounts(QMap< QgsRuleBasedRendererV2::Rule *, QgsRuleBasedRendererV2Count > theCountMap)
QgsSymbolV2SelectorDialog * mSymbolSelector
void keyPressEvent(QKeyEvent *event) override
QgsRuleBasedRendererV2Model * mModel
const QgsCategoryList & categories() const
void contextMenuViewCategories(const QPoint &p)
virtual QModelIndex parent(const QModelIndex &index) const override
provide parent model index
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
static QString iconPath(QString iconFile)
Returns path to the desired icon file.
void setScaleMaxDenom(int scaleMaxDenom)
virtual QgsSymbolV2 * clone() const =0
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
void setRendererScale(double scale)
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
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
RuleList rulesForFeature(QgsFeature &feat)
tell which rules will be used to render the feature
Container of fields for a vector layer.
Definition: qgsfield.h:172
QgsRuleBasedRendererV2::RuleList mCopyBuffer
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
void updateRule(const QModelIndex &parent, int row)
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:113
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
QgsRuleBasedRendererV2::Rule * currentRule()
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, QString rule="") override
return a list of item text / symbol
void removeChildAt(int i)
delete child rule
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const override
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
void refineRuleRangesGui(const QModelIndexList &index)
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
QgsVectorLayer * mLayer
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 ...
virtual void stopRender(QgsRenderContext &context) override
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
static QString _formatScale(int denom)
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, QString tagName, QDomDocument &doc)
static Rule * create(QDomElement &ruleElem, QgsSymbolV2Map &symbolMap)
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QDomElement save(QDomDocument &doc, QgsSymbolV2Map &symbolMap)
int pendingFeatureCount()
returns feature count after commit
This class keeps data about a rules for rule-based renderer.
QGis::GeometryType geometryType() const
Returns point, line or polygon.
QMap< QgsRuleBasedRendererV2::Rule *, QgsRuleBasedRendererV2Count > mFeatureCountMap
void removeRule(const QModelIndex &index)
static QgsRendererV2Widget * create(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
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
QgsRendererRulePropsDialog(QgsRuleBasedRendererV2::Rule *rule, QgsVectorLayer *layer, QgsStyleV2 *style, QWidget *parent=0)
void currentRuleChanged(const QModelIndex &current=QModelIndex(), const QModelIndex &previous=QModelIndex())
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QMimeData * mimeData(const QModelIndexList &indexes) const override
void setForceOrderingEnabled(bool enabled)
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
const QgsRangeList & ranges() const
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
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
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.
static QgsRuleBasedRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsRuleBasedRendererV2 from an existing renderer.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
void insertChild(int i, Rule *rule)
add child rule, take ownership, sets this as parent
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
void setScaleMinDenom(int scaleMinDenom)
Represents a vector layer which manages a vector based data sets.
QList< QPair< QString, QgsSymbolV2 * > > QgsLegendSymbolList
Definition: qgsrendererv2.h:42
void setFilterExpression(QString filterExp)
A generic dialog for building expression strings.
void willAddRules(const QModelIndex &parent, int count)
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...
#define tr(sourceText)