QGIS API Documentation  2.8.2-Wien
 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 );
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  if ( rule->scaleMinDenom() > 0 )
585  mScaleRangeWidget->setMaximumScale( 1.0 / rule->scaleMinDenom() );
586  if ( rule->scaleMaxDenom() > 0 )
587  mScaleRangeWidget->setMinimumScale( 1.0 / rule->scaleMaxDenom() );
588  }
589 
590  if ( mRule->symbol() )
591  {
592  groupSymbol->setChecked( true );
593  mSymbol = mRule->symbol()->clone(); // use a clone!
594  }
595  else
596  {
597  groupSymbol->setChecked( false );
599  }
600 
601  mSymbolSelector = new QgsSymbolV2SelectorDialog( mSymbol, style, mLayer, this, true );
602  QVBoxLayout* l = new QVBoxLayout;
603  l->addWidget( mSymbolSelector );
604  groupSymbol->setLayout( l );
605 
606  connect( btnExpressionBuilder, SIGNAL( clicked() ), this, SLOT( buildExpression() ) );
607  connect( btnTestFilter, SIGNAL( clicked() ), this, SLOT( testFilter() ) );
608 
609  QSettings settings;
610  restoreGeometry( settings.value( "/Windows/QgsRendererRulePropsDialog/geometry" ).toByteArray() );
611 }
612 
614 {
615  delete mSymbol;
616  QSettings settings;
617  settings.setValue( "/Windows/QgsRendererRulePropsDialog/geometry", saveGeometry() );
618 }
619 
621 {
622  QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this );
623 
624  if ( dlg.exec() )
625  editFilter->setText( dlg.expressionText() );
626 }
627 
629 {
630  QgsExpression filter( editFilter->text() );
631  if ( filter.hasParserError() )
632  {
633  QMessageBox::critical( this, tr( "Error" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
634  return;
635  }
636 
637  const QgsFields& fields = mLayer->pendingFields();
638 
639  if ( !filter.prepare( fields ) )
640  {
641  QMessageBox::critical( this, tr( "Evaluation error" ), filter.evalErrorString() );
642  return;
643  }
644 
645  QApplication::setOverrideCursor( Qt::WaitCursor );
646 
648 
649  int count = 0;
650  QgsFeature f;
651  while ( fit.nextFeature( f ) )
652  {
653  QVariant value = filter.evaluate( &f );
654  if ( value.toInt() != 0 )
655  count++;
656  if ( filter.hasEvalError() )
657  break;
658  }
659 
660  QApplication::restoreOverrideCursor();
661 
662  QMessageBox::information( this, tr( "Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
663 }
664 
666 {
667  mRule->setFilterExpression( editFilter->text() );
668  mRule->setLabel( editLabel->text() );
669  mRule->setDescription( editDescription->text() );
670  // caution: rule uses scale denom, scale widget uses true scales
671  mRule->setScaleMinDenom( groupScale->isChecked() ? mScaleRangeWidget->minimumScaleDenom() : 0 );
672  mRule->setScaleMaxDenom( groupScale->isChecked() ? mScaleRangeWidget->maximumScaleDenom() : 0 );
673  mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : NULL );
674 
675  QDialog::accept();
676 }
677 
679 
680 /*
681  setDragEnabled(true);
682  viewport()->setAcceptDrops(true);
683  setDropIndicatorShown(true);
684  setDragDropMode(QAbstractItemView::InternalMove);
685 */
686 
687 static QString _formatScale( int denom )
688 {
689  if ( denom != 0 )
690  {
691  QString txt = QString( "1:%L1" ).arg( denom );
692  return txt;
693  }
694  else
695  return QString();
696 }
697 
699 
701  : mR( r )
702 {
703 }
704 
705 Qt::ItemFlags QgsRuleBasedRendererV2Model::flags( const QModelIndex &index ) const
706 {
707  if ( !index.isValid() )
708  return Qt::ItemIsDropEnabled;
709 
710  // allow drop only at first column
711  Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
712 
713  Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
714 
715  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
716  Qt::ItemIsEditable | checkable |
717  Qt::ItemIsDragEnabled | drop;
718 }
719 
720 QVariant QgsRuleBasedRendererV2Model::data( const QModelIndex &index, int role ) const
721 {
722  if ( !index.isValid() )
723  return QVariant();
724 
726 
727  if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
728  {
729  switch ( index.column() )
730  {
731  case 0: return rule->label();
732  case 1:
733  if ( rule->isElse() )
734  {
735  return "ELSE";
736  }
737  else
738  {
739  return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
740  }
741  case 2: return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
742  case 3: return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
743  case 4:
744  if ( mFeatureCountMap.count( rule ) == 1 )
745  {
746  return QVariant( mFeatureCountMap[rule].count );
747  }
748  return QVariant();
749  case 5:
750  if ( mFeatureCountMap.count( rule ) == 1 )
751  {
752  if ( role == Qt::DisplayRole )
753  {
754  return QVariant( mFeatureCountMap[rule].duplicateCount );
755  }
756  else // tooltip - detailed info about duplicates
757  {
758  if ( mFeatureCountMap[rule].duplicateCount > 0 )
759  {
760  QString tip = "<p style='margin:0px;'><ul>";
761  foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, mFeatureCountMap[rule].duplicateCountMap.keys() )
762  {
763  QString label = duplicateRule->label().replace( "&", "&amp;" ).replace( ">", "&gt;" ).replace( "<", "&lt;" );
764  tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( mFeatureCountMap[rule].duplicateCountMap[duplicateRule] ).arg( label );
765  }
766  tip += "</ul>";
767  return tip;
768  }
769  else
770  {
771  return 0;
772  }
773  }
774  }
775  return QVariant();
776  default: return QVariant();
777  }
778  }
779  else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
780  {
781  return QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
782  }
783  else if ( role == Qt::TextAlignmentRole )
784  {
785  return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
786  }
787  else if ( role == Qt::FontRole && index.column() == 1 )
788  {
789  if ( rule->isElse() )
790  {
791  QFont italicFont;
792  italicFont.setItalic( true );
793  return italicFont;
794  }
795  return QVariant();
796  }
797  else if ( role == Qt::EditRole )
798  {
799  switch ( index.column() )
800  {
801  case 0: return rule->label();
802  case 1: return rule->filterExpression();
803  case 2: return rule->scaleMaxDenom();
804  case 3: return rule->scaleMinDenom();
805  default: return QVariant();
806  }
807  }
808  else if ( role == Qt::CheckStateRole )
809  {
810  if ( index.column() != 0 )
811  return QVariant();
812  return rule->checkState() ? Qt::Checked : Qt::Unchecked;
813  }
814  else
815  return QVariant();
816 }
817 
818 QVariant QgsRuleBasedRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
819 {
820  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
821  {
822  QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max. scale" ) << tr( "Count" ) << tr( "Duplicate count" );
823  return lst[section];
824  }
825  else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
826  {
827  if ( section == 4 ) // Count
828  {
829  return tr( "Number of features in this rule." );
830  }
831  else if ( section == 5 ) // Duplicate count
832  {
833  return tr( "Number of features in this rule which are also present in other rule(s)." );
834  }
835  }
836 
837  return QVariant();
838 }
839 
840 int QgsRuleBasedRendererV2Model::rowCount( const QModelIndex &parent ) const
841 {
842  if ( parent.column() > 0 )
843  return 0;
844 
845  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
846 
847  return parentRule->children().count();
848 }
849 
850 int QgsRuleBasedRendererV2Model::columnCount( const QModelIndex & ) const
851 {
852  return 6;
853 }
854 
855 QModelIndex QgsRuleBasedRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
856 {
857  if ( hasIndex( row, column, parent ) )
858  {
859  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
860  QgsRuleBasedRendererV2::Rule* childRule = parentRule->children()[row];
861  return createIndex( row, column, childRule );
862  }
863  return QModelIndex();
864 }
865 
866 QModelIndex QgsRuleBasedRendererV2Model::parent( const QModelIndex &index ) const
867 {
868  if ( !index.isValid() )
869  return QModelIndex();
870 
871  QgsRuleBasedRendererV2::Rule* childRule = ruleForIndex( index );
872  QgsRuleBasedRendererV2::Rule* parentRule = childRule->parent();
873 
874  if ( parentRule == mR->rootRule() )
875  return QModelIndex();
876 
877  // this is right: we need to know row number of our parent (in our grandparent)
878  int row = parentRule->parent()->children().indexOf( parentRule );
879 
880  return createIndex( row, 0, parentRule );
881 }
882 
883 bool QgsRuleBasedRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
884 {
885  if ( !index.isValid() )
886  return false;
887 
889 
890  if ( role == Qt::CheckStateRole )
891  {
892  rule->setCheckState( value.toInt() == Qt::Checked );
893  emit dataChanged( index, index );
894  return true;
895  }
896 
897  if ( role != Qt::EditRole )
898  return false;
899 
900  switch ( index.column() )
901  {
902  case 0: // label
903  rule->setLabel( value.toString() );
904  break;
905  case 1: // filter
906  rule->setFilterExpression( value.toString() );
907  break;
908  case 2: // scale min
909  rule->setScaleMaxDenom( value.toInt() );
910  break;
911  case 3: // scale max
912  rule->setScaleMinDenom( value.toInt() );
913  break;
914  default:
915  return false;
916  }
917 
918  emit dataChanged( index, index );
919  return true;
920 }
921 
923 {
924  return Qt::MoveAction; // | Qt::CopyAction
925 }
926 
928 {
929  QStringList types;
930  types << "application/vnd.text.list";
931  return types;
932 }
933 
934 QMimeData *QgsRuleBasedRendererV2Model::mimeData( const QModelIndexList &indexes ) const
935 {
936  QMimeData *mimeData = new QMimeData();
937  QByteArray encodedData;
938 
939  QDataStream stream( &encodedData, QIODevice::WriteOnly );
940 
941  foreach ( const QModelIndex &index, indexes )
942  {
943  // each item consists of several columns - let's add it with just first one
944  if ( !index.isValid() || index.column() != 0 )
945  continue;
946 
947  // we use a clone of the existing rule because it has a new unique rule key
948  // non-unique rule keys would confuse other components using them (e.g. legend)
949  QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index )->clone();
950  QDomDocument doc;
951  QgsSymbolV2Map symbols;
952 
953  QDomElement rootElem = doc.createElement( "rule_mime" );
954  QDomElement rulesElem = rule->save( doc, symbols );
955  rootElem.appendChild( rulesElem );
956  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
957  rootElem.appendChild( symbolsElem );
958  doc.appendChild( rootElem );
959 
960  delete rule;
961 
962  stream << doc.toString( -1 );
963  }
964 
965  mimeData->setData( "application/vnd.text.list", encodedData );
966  return mimeData;
967 }
968 
969 bool QgsRuleBasedRendererV2Model::dropMimeData( const QMimeData *data,
970  Qt::DropAction action, int row, int column, const QModelIndex &parent )
971 {
972  Q_UNUSED( column );
973 
974  if ( action == Qt::IgnoreAction )
975  return true;
976 
977  if ( !data->hasFormat( "application/vnd.text.list" ) )
978  return false;
979 
980  if ( parent.column() > 0 )
981  return false;
982 
983  QByteArray encodedData = data->data( "application/vnd.text.list" );
984  QDataStream stream( &encodedData, QIODevice::ReadOnly );
985  int rows = 0;
986 
987  if ( row == -1 )
988  {
989  // the item was dropped at a parent - we may decide where to put the items - let's append them
990  row = rowCount( parent );
991  }
992 
993  while ( !stream.atEnd() )
994  {
995  QString text;
996  stream >> text;
997 
998  QDomDocument doc;
999  if ( !doc.setContent( text ) )
1000  continue;
1001  QDomElement rootElem = doc.documentElement();
1002  if ( rootElem.tagName() != "rule_mime" )
1003  continue;
1004  QDomElement symbolsElem = rootElem.firstChildElement( "symbols" );
1005  if ( symbolsElem.isNull() )
1006  continue;
1007  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1008  QDomElement ruleElem = rootElem.firstChildElement( "rule" );
1010 
1011  insertRule( parent, row + rows, rule );
1012 
1013  ++rows;
1014  }
1015  return true;
1016 }
1017 
1019 {
1020  if ( index.isValid() )
1021  return static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
1022  return mR->rootRule();
1023 }
1024 
1025 bool QgsRuleBasedRendererV2Model::removeRows( int row, int count, const QModelIndex & parent )
1026 {
1027  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
1028 
1029  if ( row < 0 || row >= parentRule->children().count() )
1030  return false;
1031 
1032  QgsDebugMsg( QString( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ) );
1033 
1034  beginRemoveRows( parent, row, row + count - 1 );
1035 
1036  for ( int i = 0; i < count; i++ )
1037  {
1038  if ( row < parentRule->children().count() )
1039  {
1040  //QgsRuleBasedRendererV2::Rule* r = parentRule->children()[row];
1041  parentRule->removeChildAt( row );
1042  //parentRule->takeChildAt( row );
1043  }
1044  else
1045  {
1046  QgsDebugMsg( "trying to remove invalid index - this should not happen!" );
1047  }
1048  }
1049 
1050  endRemoveRows();
1051 
1052  return true;
1053 }
1054 
1055 
1056 void QgsRuleBasedRendererV2Model::insertRule( const QModelIndex& parent, int before, QgsRuleBasedRendererV2::Rule* newrule )
1057 {
1058  beginInsertRows( parent, before, before );
1059 
1060  QgsDebugMsg( QString( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ) );
1061 
1062  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
1063  parentRule->insertChild( before, newrule );
1064 
1065  endInsertRows();
1066 }
1067 
1068 void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& parent, int row )
1069 {
1070  emit dataChanged( index( row, 0, parent ),
1071  index( row, columnCount( parent ), parent ) );
1072 }
1073 
1074 void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& idx )
1075 {
1076  emit dataChanged( index( 0, 0, idx ),
1077  index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1078 
1079  for ( int i = 0; i < rowCount( idx ); i++ )
1080  {
1081  updateRule( index( i, 0, idx ) );
1082  }
1083 }
1084 
1085 
1087 {
1088  if ( !index.isValid() )
1089  return;
1090 
1091  beginRemoveRows( index.parent(), index.row(), index.row() );
1092 
1093  QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index );
1094  rule->parent()->removeChild( rule );
1095 
1096  endRemoveRows();
1097 }
1098 
1099 void QgsRuleBasedRendererV2Model::willAddRules( const QModelIndex& parent, int count )
1100 {
1101  int row = rowCount( parent ); // only consider appending
1102  beginInsertRows( parent, row, row + count - 1 );
1103 }
1104 
1106 {
1107  emit endInsertRows();
1108 }
1109 
1110 void QgsRuleBasedRendererV2Model::setFeatureCounts( QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> theCountMap )
1111 {
1112  mFeatureCountMap = theCountMap;
1113  updateRule( QModelIndex() );
1114 }
1115 
1117 {
1118  mFeatureCountMap.clear();
1119  updateRule( QModelIndex() );
1120 }