QGIS API Documentation  2.5.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"
27 
28 #include <QKeyEvent>
29 #include <QMenu>
30 #include <QProgressDialog>
31 #include <QSettings>
32 #include <QTreeWidgetItem>
33 #include <QVBoxLayout>
34 #include <QMessageBox>
35 
36 //#include "modeltest.h"
37 
39 {
40  return new QgsRuleBasedRendererV2Widget( layer, style, renderer );
41 }
42 
44  : QgsRendererV2Widget( layer, style )
45 {
46 
47  // try to recognize the previous renderer
48  // (null renderer means "no previous renderer")
49  if ( !renderer || renderer->type() != "RuleRenderer" )
50  {
51  // we're not going to use it - so let's delete the renderer
52  delete renderer;
53 
54  // some default options
56 
57  mRenderer = new QgsRuleBasedRendererV2( symbol );
58  }
59  else
60  {
61  mRenderer = static_cast<QgsRuleBasedRendererV2*>( renderer );
62  }
63 
64  setupUi( this );
65 
67  //new ModelTest( mModel, this ); // for model validity checking
68  viewRules->setModel( mModel );
69 
70  mDeleteAction = new QAction( tr( "Remove Rule" ), this );
71  mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
72 
73  viewRules->addAction( mDeleteAction );
74  viewRules->addAction( mCopyAction );
75  viewRules->addAction( mPasteAction );
76 
77  mRefineMenu = new QMenu( tr( "Refine current rule" ), btnRefineRule );
78  mRefineMenu->addAction( tr( "Add scales to rule" ), this, SLOT( refineRuleScales() ) );
79  mRefineMenu->addAction( tr( "Add categories to rule" ), this, SLOT( refineRuleCategories() ) );
80  mRefineMenu->addAction( tr( "Add ranges to rule" ), this, SLOT( refineRuleRanges() ) );
81  btnRefineRule->setMenu( mRefineMenu );
82  contextMenu->addMenu( mRefineMenu );
83 
84  btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.png" ) ) );
85  btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.png" ) ) );
86  btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.png" ) ) );
87 
88  connect( viewRules, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( editRule( const QModelIndex & ) ) );
89 
90  // support for context menu (now handled generically)
91  connect( viewRules, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
92 
93  connect( viewRules->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SLOT( currentRuleChanged( QModelIndex, QModelIndex ) ) );
94 
95  connect( btnAddRule, SIGNAL( clicked() ), this, SLOT( addRule() ) );
96  connect( btnEditRule, SIGNAL( clicked() ), this, SLOT( editRule() ) );
97  connect( btnRemoveRule, SIGNAL( clicked() ), this, SLOT( removeRule() ) );
98  connect( mDeleteAction, SIGNAL( triggered() ), this, SLOT( removeRule() ) );
99  connect( btnCountFeatures, SIGNAL( clicked() ), this, SLOT( countFeatures() ) );
100 
101  connect( btnRenderingOrder, SIGNAL( clicked() ), this, SLOT( setRenderingOrder() ) );
102 
104 
105  // store/restore header section widths
106  connect( viewRules->header(), SIGNAL( sectionResized( int, int, int ) ), this, SLOT( saveSectionWidth( int, int, int ) ) );
107 
109 
110 }
111 
113 {
114  qDeleteAll( mCopyBuffer );
115  delete mRenderer;
116 }
117 
119 {
120  return mRenderer;
121 }
122 
124 {
127 
128  QgsRendererRulePropsDialog dlg( newrule, mLayer, mStyle, this );
129  if ( dlg.exec() )
130  {
132  if ( current )
133  {
134  // add after this rule
135  QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
136  mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
137  }
138  else
139  {
140  // append to root rule
141  int rows = mModel->rowCount();
142  mModel->insertRule( QModelIndex(), rows, newrule );
143  }
145  }
146  else
147  {
148  delete newrule;
149  }
150 }
151 
153 {
154  QItemSelectionModel* sel = viewRules->selectionModel();
155  QModelIndex idx = sel->currentIndex();
156  if ( !idx.isValid() )
157  return NULL;
158  return mModel->ruleForIndex( idx );
159 }
160 
162 {
163  editRule( viewRules->selectionModel()->currentIndex() );
164 }
165 
167 {
168  if ( !index.isValid() )
169  return;
171 
172  QgsRendererRulePropsDialog dlg( rule, mLayer, mStyle, this );
173  if ( dlg.exec() )
174  {
175  // model should know about the change and emit dataChanged signal for the view
176  mModel->updateRule( index.parent(), index.row() );
178  }
179 }
180 
182 {
183  QItemSelection sel = viewRules->selectionModel()->selection();
184  QgsDebugMsg( QString( "REMOVE RULES!!! ranges: %1" ).arg( sel.count() ) );
185  foreach ( QItemSelectionRange range, sel )
186  {
187  QgsDebugMsg( QString( "RANGE: r %1 - %2" ).arg( range.top() ).arg( range.bottom() ) );
188  if ( range.isValid() )
189  mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
190  }
191  // make sure that the selection is gone
192  viewRules->selectionModel()->clear();
194 }
195 
196 void QgsRuleBasedRendererV2Widget::currentRuleChanged( const QModelIndex& current, const QModelIndex& previous )
197 {
198  Q_UNUSED( previous );
199  btnRefineRule->setEnabled( current.isValid() );
200 }
201 
202 
208 #include <QDialogButtonBox>
209 #include <QInputDialog>
210 #include <QKeyEvent>
211 #include <QClipboard>
212 
214 {
215  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
216 
217  if ( indexlist.isEmpty() )
218  return;
219 
220 
221  if ( type == 0 ) // categories
222  refineRuleCategoriesGui( indexlist );
223  else if ( type == 1 ) // ranges
224  refineRuleRangesGui( indexlist );
225  else // scales
226  refineRuleScalesGui( indexlist );
227 
228  // TODO: set initial rule's symbol to NULL (?)
229 
230  // show the newly added rules
231  foreach ( QModelIndex index, indexlist )
232  viewRules->expand( index );
233 }
234 
236 {
237  refineRule( 0 );
238 }
239 
241 {
242  refineRule( 1 );
243 }
244 
246 {
247  refineRule( 2 );
248 }
249 
250 void QgsRuleBasedRendererV2Widget::refineRuleCategoriesGui( const QModelIndexList& indexList )
251 {
252  QDialog dlg;
253  dlg.setWindowTitle( tr( "Refine a rule to categories" ) );
254  QVBoxLayout* l = new QVBoxLayout();
256  l->addWidget( w );
257  QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
258  l->addWidget( bb );
259  connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
260  connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
261  dlg.setLayout( l );
262 
263  if ( !dlg.exec() )
264  return;
265 
266  // create new rules
268  foreach ( QModelIndex index, indexList )
269  {
270  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
271  mModel->willAddRules( index, r->categories().count() );
273  }
275 }
276 
277 
278 void QgsRuleBasedRendererV2Widget::refineRuleRangesGui( const QModelIndexList& indexList )
279 {
280 
281 
282  QDialog dlg;
283  dlg.setWindowTitle( tr( "Refine a rule to ranges" ) );
284  QVBoxLayout* l = new QVBoxLayout();
286  l->addWidget( w );
287  QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
288  l->addWidget( bb );
289  connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
290  connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
291  dlg.setLayout( l );
292 
293  if ( !dlg.exec() )
294  return;
295 
296  // create new rules
298  foreach ( QModelIndex index, indexList )
299  {
300  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
301  mModel->willAddRules( index, r->ranges().count() );
303  }
305 }
306 
307 void QgsRuleBasedRendererV2Widget::refineRuleScalesGui( const QModelIndexList& indexList )
308 {
309  foreach ( QModelIndex index, indexList )
310  {
311  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
312 
313  // If any of the rules don't have a symbol let the user know and exit.
314  if ( initialRule->symbol() == NULL )
315  {
316  QMessageBox::warning( this, tr( "Scale refinement" ), tr( "Parent rule %1 must have a symbol for this operation." ).arg( initialRule->label() ) );
317  return;
318  }
319  }
320 
321  QString txt = QInputDialog::getText( this,
322  tr( "Scale refinement" ),
323  tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) );
324  if ( txt.isEmpty() )
325  return;
326 
327  QList<int> scales;
328  bool ok;
329  foreach ( QString item, txt.split( ',' ) )
330  {
331  int scale = item.toInt( &ok );
332  if ( ok )
333  scales.append( scale );
334  else
335  QMessageBox::information( this, tr( "Error" ), QString( tr( "\"%1\" is not valid scale denominator, ignoring it." ) ).arg( item ) );
336  }
337 
338  foreach ( QModelIndex index, indexList )
339  {
340  QgsRuleBasedRendererV2::Rule* initialRule = mModel->ruleForIndex( index );
341  mModel->willAddRules( index, scales.count() + 1 );
342  QgsRuleBasedRendererV2::refineRuleScales( initialRule, scales );
343  }
345 }
346 
348 {
349  QList<QgsSymbolV2*> symbolList;
350 
351  if ( !mRenderer )
352  {
353  return symbolList;
354  }
355 
356  QItemSelection sel = viewRules->selectionModel()->selection();
357  foreach ( QItemSelectionRange range, sel )
358  {
359  QModelIndex parent = range.parent();
360  QgsRuleBasedRendererV2::Rule* parentRule = mModel->ruleForIndex( parent );
361  QgsRuleBasedRendererV2::RuleList& children = parentRule->children();
362  for ( int row = range.top(); row <= range.bottom(); row++ )
363  {
364  symbolList.append( children[row]->symbol() );
365  }
366  }
367 
368  return symbolList;
369 }
370 
372 {
374  QItemSelection sel = viewRules->selectionModel()->selection();
375  foreach ( QItemSelectionRange range, sel )
376  {
377  QModelIndex parent = range.parent();
378  QgsRuleBasedRendererV2::Rule* parentRule = mModel->ruleForIndex( parent );
379  QgsRuleBasedRendererV2::RuleList& children = parentRule->children();
380  for ( int row = range.top(); row <= range.bottom(); row++ )
381  {
382  rl.append( children[row]->clone() );
383  }
384  }
385  return rl;
386 }
387 
389 {
390  // TODO: model/view
391  /*
392  if ( treeRules )
393  {
394  treeRules->populateRules();
395  }
396  */
397 }
398 
400 {
401  if ( !event )
402  {
403  return;
404  }
405 
406  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
407  {
408  qDeleteAll( mCopyBuffer );
409  mCopyBuffer.clear();
411  }
412  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
413  {
414  QgsRuleBasedRendererV2::RuleList::const_iterator rIt = mCopyBuffer.constBegin();
415  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
416  {
417  int rows = mModel->rowCount();
418  mModel->insertRule( QModelIndex(), rows, ( *rIt )->clone() );
419  }
420  }
421 }
422 
423 #include "qgssymbollevelsv2dialog.h"
424 
426 {
428 
429  QgsSymbolLevelsV2Dialog dlg( lst, true, this );
430  dlg.setForceOrderingEnabled( true );
431 
432  dlg.exec();
433 }
434 
435 void QgsRuleBasedRendererV2Widget::saveSectionWidth( int section, int oldSize, int newSize )
436 {
437  Q_UNUSED( oldSize );
438  // skip last section, as it stretches
439  if ( section == 5 )
440  return;
441  QSettings settings;
442  QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
443  settings.setValue( path, newSize );
444 }
445 
447 {
448  QSettings settings;
449  QString path = "/Windows/RuleBasedTree/sectionWidth/";
450  QHeaderView* head = viewRules->header();
451  head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
452  head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
453  head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
454  head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
455  head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
456  head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
457 }
458 
460 {
461  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
462  QgsDebugMsg( QString( "%1" ).arg( indexlist.count() ) );
463 
464  if ( indexlist.isEmpty() )
465  return;
466 
467  QMimeData* mime = mModel->mimeData( indexlist );
468  QApplication::clipboard()->setMimeData( mime );
469 }
470 
472 {
473  const QMimeData* mime = QApplication::clipboard()->mimeData();
474  QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
475  QModelIndex index;
476  if ( indexlist.isEmpty() )
477  index = mModel->index( mModel->rowCount(), 0 );
478  else
479  index = indexlist.first();
480  mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
481 }
482 
483 
485 {
486  if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
487  {
488  return;
489  }
490  QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> countMap;
491 
493  // insert all so that we have counts 0
494  foreach ( QgsRuleBasedRendererV2::Rule* rule, ruleList )
495  {
496  countMap[rule].count = 0;
497  countMap[rule].duplicateCount = 0;
498  }
499 
501 
502  QgsRenderContext renderContext;
503  renderContext.setRendererScale( 0 ); // ignore scale
504  mRenderer->startRender( renderContext, mLayer->pendingFields() );
505 
506  int nFeatures = mLayer->pendingFeatureCount();
507  QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, nFeatures );
508  p.setWindowModality( Qt::WindowModal );
509  int featuresCounted = 0;
510 
511  QgsFeature f;
512  while ( fit.nextFeature( f ) )
513  {
515 
516  foreach ( QgsRuleBasedRendererV2::Rule* rule, featureRuleList )
517  {
518  countMap[rule].count++;
519  if ( featureRuleList.size() > 1 )
520  {
521  countMap[rule].duplicateCount++;
522  }
523  foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, featureRuleList )
524  {
525  if ( duplicateRule == rule ) continue;
526  countMap[rule].duplicateCountMap[duplicateRule] += 1;
527  }
528  }
529  ++featuresCounted;
530  if ( featuresCounted % 50 == 0 )
531  {
532  if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
533  {
534  p.setMaximum( 0 );
535  }
536  p.setValue( featuresCounted );
537  if ( p.wasCanceled() )
538  {
539  return;
540  }
541  }
542  }
543  p.setValue( nFeatures );
544 
545  mRenderer->stopRender( renderContext );
546 
547 #ifdef QGISDEBUG
548  foreach ( QgsRuleBasedRendererV2::Rule *rule, countMap.keys() )
549  {
550  QgsDebugMsg( QString( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ) );
551  }
552 #endif
553 
554  mModel->setFeatureCounts( countMap );
555 }
556 
558 
560  : QDialog( parent ), mRule( rule ), mLayer( layer ), mSymbolSelector( NULL ), mSymbol( NULL )
561 {
562  setupUi( this );
563 #ifdef Q_WS_MAC
564  setWindowModality( Qt::WindowModal );
565 #endif
566 
567  connect( buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
568  connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
569 
570  editFilter->setText( mRule->filterExpression() );
571  editFilter->setToolTip( mRule->filterExpression() );
572  editLabel->setText( mRule->label() );
573  editDescription->setText( mRule->description() );
574  editDescription->setToolTip( mRule->description() );
575 
576  if ( mRule->dependsOnScale() )
577  {
578  groupScale->setChecked( true );
579  // caution: rule uses scale denom, scale widget uses true scales
580  mScaleRangeWidget->setMaximumScale( 1.0 / rule->scaleMinDenom() );
581  mScaleRangeWidget->setMinimumScale( 1.0 / rule->scaleMaxDenom() );
582  }
583 
584  if ( mRule->symbol() )
585  {
586  groupSymbol->setChecked( true );
587  mSymbol = mRule->symbol()->clone(); // use a clone!
588  }
589  else
590  {
591  groupSymbol->setChecked( false );
593  }
594 
595  mSymbolSelector = new QgsSymbolV2SelectorDialog( mSymbol, style, mLayer, this, true );
596  QVBoxLayout* l = new QVBoxLayout;
597  l->addWidget( mSymbolSelector );
598  groupSymbol->setLayout( l );
599 
600  connect( btnExpressionBuilder, SIGNAL( clicked() ), this, SLOT( buildExpression() ) );
601  connect( btnTestFilter, SIGNAL( clicked() ), this, SLOT( testFilter() ) );
602 
603  QSettings settings;
604  restoreGeometry( settings.value( "/Windows/QgsRendererRulePropsDialog/geometry" ).toByteArray() );
605 }
606 
608 {
609  delete mSymbol;
610  QSettings settings;
611  settings.setValue( "/Windows/QgsRendererRulePropsDialog/geometry", saveGeometry() );
612 }
613 
615 {
616  QgsExpressionBuilderDialog dlg( mLayer, editFilter->text(), this );
617 
618  if ( dlg.exec() )
619  editFilter->setText( dlg.expressionText() );
620 }
621 
623 {
624  QgsExpression filter( editFilter->text() );
625  if ( filter.hasParserError() )
626  {
627  QMessageBox::critical( this, tr( "Error" ), tr( "Filter expression parsing error:\n" ) + filter.parserErrorString() );
628  return;
629  }
630 
631  const QgsFields& fields = mLayer->pendingFields();
632 
633  if ( !filter.prepare( fields ) )
634  {
635  QMessageBox::critical( this, tr( "Evaluation error" ), filter.evalErrorString() );
636  return;
637  }
638 
639  QApplication::setOverrideCursor( Qt::WaitCursor );
640 
642 
643  int count = 0;
644  QgsFeature f;
645  while ( fit.nextFeature( f ) )
646  {
647  QVariant value = filter.evaluate( &f );
648  if ( value.toInt() != 0 )
649  count++;
650  if ( filter.hasEvalError() )
651  break;
652  }
653 
654  QApplication::restoreOverrideCursor();
655 
656  QMessageBox::information( this, tr( "Filter" ), tr( "Filter returned %n feature(s)", "number of filtered features", count ) );
657 }
658 
660 {
661  mRule->setFilterExpression( editFilter->text() );
662  mRule->setLabel( editLabel->text() );
663  mRule->setDescription( editDescription->text() );
664  // caution: rule uses scale denom, scale widget uses true scales
665  mRule->setScaleMinDenom( groupScale->isChecked() ? mScaleRangeWidget->minimumScaleDenom() : 0 );
666  mRule->setScaleMaxDenom( groupScale->isChecked() ? mScaleRangeWidget->maximumScaleDenom() : 0 );
667  mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : NULL );
668 
669  QDialog::accept();
670 }
671 
673 
674 /*
675  setDragEnabled(true);
676  viewport()->setAcceptDrops(true);
677  setDropIndicatorShown(true);
678  setDragDropMode(QAbstractItemView::InternalMove);
679 */
680 
681 static QString _formatScale( int denom )
682 {
683  if ( denom != 0 )
684  {
685  QString txt = QString( "1:%L1" ).arg( denom );
686  return txt;
687  }
688  else
689  return QString();
690 }
691 
693 
695  : mR( r )
696 {
697 }
698 
699 Qt::ItemFlags QgsRuleBasedRendererV2Model::flags( const QModelIndex &index ) const
700 {
701  if ( !index.isValid() )
702  return Qt::ItemIsDropEnabled;
703 
704  // allow drop only at first column
705  Qt::ItemFlag drop = ( index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
706 
707  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
708  Qt::ItemIsEditable |
709  Qt::ItemIsDragEnabled | drop;
710 }
711 
712 QVariant QgsRuleBasedRendererV2Model::data( const QModelIndex &index, int role ) const
713 {
714  if ( !index.isValid() )
715  return QVariant();
716 
718 
719  if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
720  {
721  switch ( index.column() )
722  {
723  case 0: return rule->label();
724  case 1:
725  if ( rule->isElse() )
726  {
727  return "ELSE";
728  }
729  else
730  {
731  return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
732  }
733  case 2: return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
734  case 3: return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
735  case 4:
736  if ( mFeatureCountMap.count( rule ) == 1 )
737  {
738  return QVariant( mFeatureCountMap[rule].count );
739  }
740  return QVariant();
741  case 5:
742  if ( mFeatureCountMap.count( rule ) == 1 )
743  {
744  if ( role == Qt::DisplayRole )
745  {
746  return QVariant( mFeatureCountMap[rule].duplicateCount );
747  }
748  else // tooltip - detailed info about duplicates
749  {
750  if ( mFeatureCountMap[rule].duplicateCount > 0 )
751  {
752  QString tip = "<p style='margin:0px;'><ul>";
753  foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, mFeatureCountMap[rule].duplicateCountMap.keys() )
754  {
755  QString label = duplicateRule->label().replace( "&", "&amp;" ).replace( ">", "&gt;" ).replace( "<", "&lt;" );
756  tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( mFeatureCountMap[rule].duplicateCountMap[duplicateRule] ).arg( label );
757  }
758  tip += "</ul>";
759  return tip;
760  }
761  else
762  {
763  return 0;
764  }
765  }
766  }
767  return QVariant();
768  default: return QVariant();
769  }
770  }
771  else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
772  {
773  return QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
774  }
775  else if ( role == Qt::TextAlignmentRole )
776  {
777  return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
778  }
779  else if ( role == Qt::FontRole && index.column() == 1 )
780  {
781  if ( rule->isElse() )
782  {
783  QFont italicFont;
784  italicFont.setItalic( true );
785  return italicFont;
786  }
787  return QVariant();
788  }
789  else if ( role == Qt::EditRole )
790  {
791  switch ( index.column() )
792  {
793  case 0: return rule->label();
794  case 1: return rule->filterExpression();
795  case 2: return rule->scaleMinDenom();
796  case 3: return rule->scaleMaxDenom();
797  default: return QVariant();
798  }
799  }
800  else
801  return QVariant();
802 }
803 
804 QVariant QgsRuleBasedRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
805 {
806  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
807  {
808  QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max. scale" ) << tr( "Count" ) << tr( "Duplicate count" );
809  return lst[section];
810  }
811  else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
812  {
813  if ( section == 4 ) // Count
814  {
815  return tr( "Number of features in this rule." );
816  }
817  else if ( section == 5 ) // Duplicate count
818  {
819  return tr( "Number of features in this rule which are also present in other rule(s)." );
820  }
821  }
822 
823  return QVariant();
824 }
825 
826 int QgsRuleBasedRendererV2Model::rowCount( const QModelIndex &parent ) const
827 {
828  if ( parent.column() > 0 )
829  return 0;
830 
831  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
832 
833  return parentRule->children().count();
834 }
835 
836 int QgsRuleBasedRendererV2Model::columnCount( const QModelIndex & ) const
837 {
838  return 6;
839 }
840 
841 QModelIndex QgsRuleBasedRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
842 {
843  if ( hasIndex( row, column, parent ) )
844  {
845  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
846  QgsRuleBasedRendererV2::Rule* childRule = parentRule->children()[row];
847  return createIndex( row, column, childRule );
848  }
849  return QModelIndex();
850 }
851 
852 QModelIndex QgsRuleBasedRendererV2Model::parent( const QModelIndex &index ) const
853 {
854  if ( !index.isValid() )
855  return QModelIndex();
856 
857  QgsRuleBasedRendererV2::Rule* childRule = ruleForIndex( index );
858  QgsRuleBasedRendererV2::Rule* parentRule = childRule->parent();
859 
860  if ( parentRule == mR->rootRule() )
861  return QModelIndex();
862 
863  // this is right: we need to know row number of our parent (in our grandparent)
864  int row = parentRule->parent()->children().indexOf( parentRule );
865 
866  return createIndex( row, 0, parentRule );
867 }
868 
869 bool QgsRuleBasedRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
870 {
871  if ( !index.isValid() || role != Qt::EditRole )
872  return false;
873 
875 
876  switch ( index.column() )
877  {
878  case 0: // label
879  rule->setLabel( value.toString() );
880  break;
881  case 1: // filter
882  rule->setFilterExpression( value.toString() );
883  break;
884  case 2: // scale min
885  rule->setScaleMinDenom( value.toInt() );
886  break;
887  case 3: // scale max
888  rule->setScaleMaxDenom( value.toInt() );
889  break;
890  default:
891  return false;
892  }
893 
894  emit dataChanged( index, index );
895  return true;
896 }
897 
899 {
900  return Qt::MoveAction; // | Qt::CopyAction
901 }
902 
904 {
905  QStringList types;
906  types << "application/vnd.text.list";
907  return types;
908 }
909 
910 QMimeData *QgsRuleBasedRendererV2Model::mimeData( const QModelIndexList &indexes ) const
911 {
912  QMimeData *mimeData = new QMimeData();
913  QByteArray encodedData;
914 
915  QDataStream stream( &encodedData, QIODevice::WriteOnly );
916 
917  foreach ( const QModelIndex &index, indexes )
918  {
919  // each item consists of several columns - let's add it with just first one
920  if ( !index.isValid() || index.column() != 0 )
921  continue;
922 
924  QDomDocument doc;
925  QgsSymbolV2Map symbols;
926 
927  QDomElement rootElem = doc.createElement( "rule_mime" );
928  QDomElement rulesElem = rule->save( doc, symbols );
929  rootElem.appendChild( rulesElem );
930  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
931  rootElem.appendChild( symbolsElem );
932  doc.appendChild( rootElem );
933 
934  stream << doc.toString( -1 );
935  }
936 
937  mimeData->setData( "application/vnd.text.list", encodedData );
938  return mimeData;
939 }
940 
941 bool QgsRuleBasedRendererV2Model::dropMimeData( const QMimeData *data,
942  Qt::DropAction action, int row, int column, const QModelIndex &parent )
943 {
944  Q_UNUSED( column );
945 
946  if ( action == Qt::IgnoreAction )
947  return true;
948 
949  if ( !data->hasFormat( "application/vnd.text.list" ) )
950  return false;
951 
952  if ( parent.column() > 0 )
953  return false;
954 
955  QByteArray encodedData = data->data( "application/vnd.text.list" );
956  QDataStream stream( &encodedData, QIODevice::ReadOnly );
957  int rows = 0;
958 
959  if ( row == -1 )
960  {
961  // the item was dropped at a parent - we may decide where to put the items - let's append them
962  row = rowCount( parent );
963  }
964 
965  while ( !stream.atEnd() )
966  {
967  QString text;
968  stream >> text;
969 
970  QDomDocument doc;
971  if ( !doc.setContent( text ) )
972  continue;
973  QDomElement rootElem = doc.documentElement();
974  if ( rootElem.tagName() != "rule_mime" )
975  continue;
976  QDomElement symbolsElem = rootElem.firstChildElement( "symbols" );
977  if ( symbolsElem.isNull() )
978  continue;
979  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
980  QDomElement ruleElem = rootElem.firstChildElement( "rule" );
982 
983  insertRule( parent, row + rows, rule );
984 
985  ++rows;
986  }
987  return true;
988 }
989 
991 {
992  if ( index.isValid() )
993  return static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
994  return mR->rootRule();
995 }
996 
997 bool QgsRuleBasedRendererV2Model::removeRows( int row, int count, const QModelIndex & parent )
998 {
999  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
1000 
1001  if ( row < 0 || row >= parentRule->children().count() )
1002  return false;
1003 
1004  QgsDebugMsg( QString( "Called: row %1 count %2 parent ~~%3~~" ).arg( row ).arg( count ).arg( parentRule->dump() ) );
1005 
1006  beginRemoveRows( parent, row, row + count - 1 );
1007 
1008  for ( int i = 0; i < count; i++ )
1009  {
1010  if ( row < parentRule->children().count() )
1011  {
1012  //QgsRuleBasedRendererV2::Rule* r = parentRule->children()[row];
1013  parentRule->removeChildAt( row );
1014  //parentRule->takeChildAt( row );
1015  }
1016  else
1017  {
1018  QgsDebugMsg( "trying to remove invalid index - this should not happen!" );
1019  }
1020  }
1021 
1022  endRemoveRows();
1023 
1024  return true;
1025 }
1026 
1027 
1028 void QgsRuleBasedRendererV2Model::insertRule( const QModelIndex& parent, int before, QgsRuleBasedRendererV2::Rule* newrule )
1029 {
1030  beginInsertRows( parent, before, before );
1031 
1032  QgsDebugMsg( QString( "insert before %1 rule: %2" ).arg( before ).arg( newrule->dump() ) );
1033 
1034  QgsRuleBasedRendererV2::Rule* parentRule = ruleForIndex( parent );
1035  parentRule->insertChild( before, newrule );
1036 
1037  endInsertRows();
1038 }
1039 
1040 void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& parent, int row )
1041 {
1042  emit dataChanged( index( row, 0, parent ),
1043  index( row, columnCount( parent ), parent ) );
1044 }
1045 
1046 void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& idx )
1047 {
1048  emit dataChanged( index( 0, 0, idx ),
1049  index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );
1050 
1051  for ( int i = 0; i < rowCount( idx ); i++ )
1052  {
1053  updateRule( index( i, 0, idx ) );
1054  }
1055 }
1056 
1057 
1059 {
1060  if ( !index.isValid() )
1061  return;
1062 
1063  beginRemoveRows( index.parent(), index.row(), index.row() );
1064 
1065  QgsRuleBasedRendererV2::Rule* rule = ruleForIndex( index );
1066  rule->parent()->removeChild( rule );
1067 
1068  endRemoveRows();
1069 }
1070 
1071 void QgsRuleBasedRendererV2Model::willAddRules( const QModelIndex& parent, int count )
1072 {
1073  int row = rowCount( parent ); // only consider appending
1074  beginInsertRows( parent, row, row + count - 1 );
1075 }
1076 
1078 {
1079  emit endInsertRows();
1080 }
1081 
1082 void QgsRuleBasedRendererV2Model::setFeatureCounts( QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> theCountMap )
1083 {
1084  mFeatureCountMap = theCountMap;
1085  updateRule( QModelIndex() );
1086 }
1087 
1089 {
1090  mFeatureCountMap.clear();
1091  updateRule( QModelIndex() );
1092 }
QgsRuleBasedRendererV2Widget(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
QMap< QString, QgsSymbolV2 * > QgsSymbolV2Map
Definition: qgsrendererv2.h:38
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:89
virtual QgsFeatureRendererV2 * renderer()
return pointer to the renderer (no transfer of ownership)
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
QgsRuleBasedRendererV2Model * mModel
const QgsCategoryList & categories() const
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
provide model index for parent's child item
virtual QgsFeatureRendererV2 * renderer()
return pointer to the renderer (no transfer of ownership)
void contextMenuViewCategories(const QPoint &p)
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
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.
virtual QgsFeatureRendererV2 * renderer()
return pointer to the renderer (no transfer of ownership)
void setRendererScale(double scale)
virtual void stopRender(QgsRenderContext &context)
void refineRuleCategoriesGui(const QModelIndexList &index)
QgsRuleBasedRendererV2Model(QgsRuleBasedRendererV2 *r)
RuleList rulesForFeature(QgsFeature &feat)
tell which rules will be used to render the feature
Container of fields for a vector layer.
Definition: qgsfield.h:161
QgsRuleBasedRendererV2::RuleList mCopyBuffer
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
QgsRuleBasedRendererV2::Rule * currentRule()
virtual QModelIndex parent(const QModelIndex &index) const
provide parent model index
void removeChildAt(int i)
delete child rule
QString type() const
Definition: qgsrendererv2.h:77
void refineRuleRangesGui(const QModelIndexList &index)
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
Qt::DropActions supportedDropActions() const
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 QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, QString rule="")
return a list of item text / symbol
static QString _formatScale(int denom)
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, QString tagName, QDomDocument &doc)
virtual int columnCount(const QModelIndex &=QModelIndex()) const
static Rule * create(QDomElement &ruleElem, QgsSymbolV2Map &symbolMap)
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
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.
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
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())
void setForceOrderingEnabled(bool enabled)
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
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
QgsRuleBasedRendererV2::RuleList selectedRules()
virtual Qt::ItemFlags flags(const QModelIndex &index) 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.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
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)
QList< QgsSymbolV2 * > selectedSymbols()
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
Represents a vector layer which manages a vector based data sets.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
QList< QPair< QString, QgsSymbolV2 * > > QgsLegendSymbolList
Definition: qgsrendererv2.h:41
void setFilterExpression(QString filterExp)
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex())
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
A generic dialog for building expression strings.
QMimeData * mimeData(const QModelIndexList &indexes) const
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)