QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsgraduatedsymbolrendererv2widget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgraduatedsymbolrendererv2widget.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 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  ***************************************************************************/
16 
17 #include "qgssymbolv2.h"
18 #include "qgssymbollayerv2utils.h"
19 #include "qgsvectorcolorrampv2.h"
20 #include "qgsstylev2.h"
21 
22 #include "qgsvectorlayer.h"
23 
26 
27 #include "qgsludialog.h"
28 
29 #include "qgsproject.h"
30 
31 #include <QKeyEvent>
32 #include <QMenu>
33 #include <QMessageBox>
34 #include <QStandardItemModel>
35 #include <QStandardItem>
36 #include <QPen>
37 #include <QPainter>
38 
39 // ------------------------------ Model ------------------------------------
40 
41 QgsGraduatedSymbolRendererV2Model::QgsGraduatedSymbolRendererV2Model( QObject * parent ) : QAbstractItemModel( parent )
42  , mRenderer( 0 )
43  , mMimeFormat( "application/x-qgsgraduatedsymbolrendererv2model" )
44 {
45 }
46 
48 {
49  if ( mRenderer )
50  {
51  beginRemoveRows( QModelIndex(), 0, mRenderer->ranges().size() - 1 );
52  mRenderer = 0;
53  endRemoveRows();
54  }
55  if ( renderer )
56  {
57  beginInsertRows( QModelIndex(), 0, renderer->ranges().size() - 1 );
58  mRenderer = renderer;
59  endInsertRows();
60  }
61 }
62 
64 {
65  if ( !mRenderer ) return;
66  int idx = mRenderer->ranges().size();
67  beginInsertRows( QModelIndex(), idx, idx );
68  mRenderer->addClass( symbol );
69  endInsertRows();
70 }
71 
73 {
74  if ( !mRenderer )
75  {
76  return;
77  }
78  int idx = mRenderer->ranges().size();
79  beginInsertRows( QModelIndex(), idx, idx );
80  mRenderer->addClass( range );
81  endInsertRows();
82 }
83 
85 {
86  if ( !index.isValid() || !mRenderer || mRenderer->ranges().size() <= index.row() )
87  {
88  return QgsRendererRangeV2();
89  }
90 
91  return mRenderer->ranges().value( index.row() );
92 }
93 
94 Qt::ItemFlags QgsGraduatedSymbolRendererV2Model::flags( const QModelIndex & index ) const
95 {
96  if ( !index.isValid() )
97  {
98  return Qt::ItemIsDropEnabled;
99  }
100 
101  Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
102 
103  if ( index.column() == 2 )
104  {
105  flags |= Qt::ItemIsEditable;
106  }
107 
108  return flags;
109 }
110 
112 {
113  return Qt::MoveAction;
114 }
115 
116 QVariant QgsGraduatedSymbolRendererV2Model::data( const QModelIndex &index, int role ) const
117 {
118  if ( !index.isValid() || !mRenderer ) return QVariant();
119 
120  const QgsRendererRangeV2 range = mRenderer->ranges().value( index.row() );
121 
122  if ( role == Qt::CheckStateRole && index.column() == 0 )
123  {
124  return range.renderState() ? Qt::Checked : Qt::Unchecked;
125  }
126  else if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
127  {
128  switch ( index.column() )
129  {
130  case 1:
131  {
132  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
133  if ( decimalPlaces < 0 ) decimalPlaces = 0;
134  return QString::number( range.lowerValue(), 'f', decimalPlaces ) + " - " + QString::number( range.upperValue(), 'f', decimalPlaces );
135  }
136  case 2: return range.label();
137  default: return QVariant();
138  }
139  }
140  else if ( role == Qt::DecorationRole && index.column() == 0 && range.symbol() )
141  {
142  return QgsSymbolLayerV2Utils::symbolPreviewIcon( range.symbol(), QSize( 16, 16 ) );
143  }
144  else if ( role == Qt::TextAlignmentRole )
145  {
146  return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
147  }
148  else if ( role == Qt::EditRole )
149  {
150  switch ( index.column() )
151  {
152  // case 1: return rangeStr;
153  case 2: return range.label();
154  default: return QVariant();
155  }
156  }
157 
158  return QVariant();
159 }
160 
161 bool QgsGraduatedSymbolRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
162 {
163  if ( !index.isValid() )
164  return false;
165 
166  if ( index.column() == 0 && role == Qt::CheckStateRole )
167  {
168  mRenderer->updateRangeRenderState( index.row(), value == Qt::Checked );
169  emit dataChanged( index, index );
170  return true;
171  }
172 
173  if ( role != Qt::EditRole )
174  return false;
175 
176  switch ( index.column() )
177  {
178  case 1: // range
179  return false; // range is edited in popup dialog
180  break;
181  case 2: // label
182  mRenderer->updateRangeLabel( index.row(), value.toString() );
183  break;
184  default:
185  return false;
186  }
187 
188  emit dataChanged( index, index );
189  return true;
190 }
191 
192 QVariant QgsGraduatedSymbolRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
193 {
194  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
195  {
196  QStringList lst; lst << tr( "Symbol" ) << tr( "Values" ) << tr( "Legend" );
197  return lst.value( section );
198  }
199  return QVariant();
200 }
201 
202 int QgsGraduatedSymbolRendererV2Model::rowCount( const QModelIndex &parent ) const
203 {
204  if ( parent.isValid() || !mRenderer )
205  {
206  return 0;
207  }
208  return mRenderer->ranges().size();
209 }
210 
212 {
213  Q_UNUSED( index );
214  return 3;
215 }
216 
217 QModelIndex QgsGraduatedSymbolRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
218 {
219  if ( hasIndex( row, column, parent ) )
220  {
221  return createIndex( row, column );
222  }
223  return QModelIndex();
224 }
225 
226 QModelIndex QgsGraduatedSymbolRendererV2Model::parent( const QModelIndex &index ) const
227 {
228  Q_UNUSED( index );
229  return QModelIndex();
230 }
231 
233 {
234  QStringList types;
235  types << mMimeFormat;
236  return types;
237 }
238 
239 QMimeData *QgsGraduatedSymbolRendererV2Model::mimeData( const QModelIndexList &indexes ) const
240 {
241  QMimeData *mimeData = new QMimeData();
242  QByteArray encodedData;
243 
244  QDataStream stream( &encodedData, QIODevice::WriteOnly );
245 
246  // Create list of rows
247  foreach ( const QModelIndex &index, indexes )
248  {
249  if ( !index.isValid() || index.column() != 0 )
250  continue;
251 
252  stream << index.row();
253  }
254  mimeData->setData( mMimeFormat, encodedData );
255  return mimeData;
256 }
257 
258 bool QgsGraduatedSymbolRendererV2Model::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
259 {
260  Q_UNUSED( row );
261  Q_UNUSED( column );
262  if ( action != Qt::MoveAction ) return true;
263 
264  if ( !data->hasFormat( mMimeFormat ) ) return false;
265 
266  QByteArray encodedData = data->data( mMimeFormat );
267  QDataStream stream( &encodedData, QIODevice::ReadOnly );
268 
269  QVector<int> rows;
270  while ( !stream.atEnd() )
271  {
272  int r;
273  stream >> r;
274  rows.append( r );
275  }
276 
277  int to = parent.row();
278  // to is -1 if dragged outside items, i.e. below any item,
279  // then move to the last position
280  if ( to == -1 ) to = mRenderer->ranges().size(); // out of rang ok, will be decreased
281  for ( int i = rows.size() - 1; i >= 0; i-- )
282  {
283  QgsDebugMsg( QString( "move %1 to %2" ).arg( rows[i] ).arg( to ) );
284  int t = to;
285  // moveCategory first removes and then inserts
286  if ( rows[i] < t ) t--;
287  mRenderer->moveClass( rows[i], t );
288  // current moved under another, shift its index up
289  for ( int j = 0; j < i; j++ )
290  {
291  if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
292  }
293  // removed under 'to' so the target shifted down
294  if ( rows[i] < to ) to--;
295  }
296  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
297  emit rowsMoved();
298  return false;
299 }
300 
302 {
303  for ( int i = rows.size() - 1; i >= 0; i-- )
304  {
305  beginRemoveRows( QModelIndex(), rows[i], rows[i] );
306  mRenderer->deleteClass( rows[i] );
307  endRemoveRows();
308  }
309 }
310 
312 {
313  beginRemoveRows( QModelIndex(), 0, mRenderer->ranges().size() - 1 );
314  mRenderer->deleteAllClasses();
315  endRemoveRows();
316 }
317 
318 void QgsGraduatedSymbolRendererV2Model::sort( int column, Qt::SortOrder order )
319 {
320  QgsDebugMsg( "Entered" );
321  if ( column == 0 )
322  {
323  return;
324  }
325  if ( column == 1 )
326  {
327  mRenderer->sortByValue( order );
328  }
329  else if ( column == 2 )
330  {
331  mRenderer->sortByLabel( order );
332  }
333  emit rowsMoved();
334  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
335  QgsDebugMsg( "Done" );
336 }
337 
339 {
340  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
341 }
342 
344 {
345  emit dataChanged( createIndex( 0, 2 ), createIndex( mRenderer->ranges().size(), 2 ) );
346 }
347 
348 // ------------------------------ View style --------------------------------
350  : QProxyStyle( style )
351 {}
352 
353 void QgsGraduatedSymbolRendererV2ViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget ) const
354 {
355  if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
356  {
357  QStyleOption opt( *option );
358  opt.rect.setLeft( 0 );
359  // draw always as line above, because we move item to that index
360  opt.rect.setHeight( 0 );
361  if ( widget ) opt.rect.setRight( widget->width() );
362  QProxyStyle::drawPrimitive( element, &opt, painter, widget );
363  return;
364  }
365  QProxyStyle::drawPrimitive( element, option, painter, widget );
366 }
367 
368 // ------------------------------ Widget ------------------------------------
369 
371 {
372  return new QgsGraduatedSymbolRendererV2Widget( layer, style, renderer );
373 }
374 
376  : QgsRendererV2Widget( layer, style )
377  , mRenderer( 0 )
378  , mModel( 0 )
379 {
380 
381 
382  // try to recognize the previous renderer
383  // (null renderer means "no previous renderer")
384  if ( renderer )
385  {
387  }
388  if ( !mRenderer )
389  {
391  }
392 
393  // setup user interface
394  setupUi( this );
396 
397  mExpressionWidget->setFilters( QgsFieldProxyModel::Numeric | QgsFieldProxyModel::Date );
398  mExpressionWidget->setLayer( mLayer );
399 
400  cboGraduatedColorRamp->populate( mStyle );
401 
402  spinPrecision->setMinimum( QgsRendererRangeV2LabelFormat::MinPrecision );
403  spinPrecision->setMaximum( QgsRendererRangeV2LabelFormat::MaxPrecision );
404 
405  // set project default color ramp
406  QString defaultColorRamp = QgsProject::instance()->readEntry( "DefaultStyles", "/ColorRamp", "" );
407  if ( defaultColorRamp != "" )
408  {
409  int index = cboGraduatedColorRamp->findText( defaultColorRamp, Qt::MatchCaseSensitive );
410  if ( index >= 0 )
411  cboGraduatedColorRamp->setCurrentIndex( index );
412  }
413 
414 
415  viewGraduated->setStyle( new QgsGraduatedSymbolRendererV2ViewStyle( viewGraduated->style() ) );
416 
418 
419  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( graduatedColumnChanged( QString ) ) );
420  connect( viewGraduated, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( rangesDoubleClicked( const QModelIndex & ) ) );
421  connect( viewGraduated, SIGNAL( clicked( const QModelIndex & ) ), this, SLOT( rangesClicked( const QModelIndex & ) ) );
422  connect( viewGraduated, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
423 
424  connect( btnGraduatedClassify, SIGNAL( clicked() ), this, SLOT( classifyGraduated() ) );
425  connect( btnChangeGraduatedSymbol, SIGNAL( clicked() ), this, SLOT( changeGraduatedSymbol() ) );
426  connect( btnGraduatedDelete, SIGNAL( clicked() ), this, SLOT( deleteClasses() ) );
427  connect( btnDeleteAllClasses, SIGNAL( clicked() ), this, SLOT( deleteAllClasses() ) );
428  connect( btnGraduatedAdd, SIGNAL( clicked() ), this, SLOT( addClass() ) );
429  connect( cbxLinkBoundaries, SIGNAL( toggled( bool ) ), this, SLOT( toggleBoundariesLink( bool ) ) );
430 
432 
433  // initialize from previously set renderer
435 
436  // menus for data-defined rotation/size
437  QMenu* advMenu = new QMenu;
438 
439  advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );
440 
443  connect( mDataDefinedMenus, SIGNAL( rotationFieldChanged( QString ) ), this, SLOT( rotationFieldChanged( QString ) ) );
444  connect( mDataDefinedMenus, SIGNAL( sizeScaleFieldChanged( QString ) ), this, SLOT( sizeScaleFieldChanged( QString ) ) );
446  btnAdvanced->setMenu( advMenu );
447 }
448 
450 {
451  delete mRenderer;
452  delete mModel;
453 }
454 
456 {
457  return mRenderer;
458 }
459 
460 // Connect/disconnect event handlers which trigger updating renderer
461 
463 {
464  connect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
465  connect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
466  connect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
467  connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
468  connect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
469  connect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
470  connect( txtFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
471 
472  connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
473  connect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
474 }
475 
476 // Connect/disconnect event handlers which trigger updating renderer
477 
479 {
480  disconnect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
481  disconnect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
482  disconnect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
483  disconnect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
484  disconnect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
485  disconnect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
486  disconnect( txtFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
487 
488  disconnect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
489  disconnect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
490 }
491 
493 {
495 
497 
498  // update UI from the graduated renderer (update combo boxes, view)
499  if ( mRenderer->mode() < cboGraduatedMode->count() )
500  cboGraduatedMode->setCurrentIndex( mRenderer->mode() );
501 
502  // Only update class count if different - otherwise typing value gets very messy
503  int nclasses = mRenderer->ranges().count();
504  if ( nclasses && updateCount )
505  spinGraduatedClasses->setValue( mRenderer->ranges().count() );
506 
507  // set column
508  QString attrName = mRenderer->classAttribute();
509  mExpressionWidget->setField( attrName );
510 
511  // set source symbol
512  if ( mRenderer->sourceSymbol() )
513  {
514  delete mGraduatedSymbol;
517  }
518 
519  // set source color ramp
520  if ( mRenderer->sourceColorRamp() )
521  {
522  cboGraduatedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
523  cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
524  }
525 
527  txtFormat->setText( labelFormat.format() );
528  spinPrecision->setValue( labelFormat.precision() );
529  cbxTrimTrailingZeroes->setChecked( labelFormat.trimTrailingZeroes() );
530 
532  viewGraduated->setModel( mModel );
533  viewGraduated->resizeColumnToContents( 0 );
534  viewGraduated->resizeColumnToContents( 1 );
535  viewGraduated->resizeColumnToContents( 2 );
536 
538 }
539 
541 {
542  mRenderer->setClassAttribute( field );
543 }
544 
546 {
547  QString attrName = mExpressionWidget->currentField();
548 
549  int nclasses = spinGraduatedClasses->value();
550 
551  QSharedPointer<QgsVectorColorRampV2> ramp( cboGraduatedColorRamp->currentColorRamp() );
552  if ( !ramp )
553  {
554  if ( cboGraduatedColorRamp->count() == 0 )
555  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
556  else
557  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
558  return;
559  }
560 
562  if ( cboGraduatedMode->currentIndex() == 0 )
564  else if ( cboGraduatedMode->currentIndex() == 2 )
566  else if ( cboGraduatedMode->currentIndex() == 3 )
568  else if ( cboGraduatedMode->currentIndex() == 4 )
570  else // default should be quantile for now
572 
573  // Jenks is n^2 complexity, warn for big dataset (more than 50k records)
574  // and give the user the chance to cancel
575  if ( QgsGraduatedSymbolRendererV2::Jenks == mode && mLayer->featureCount() > 50000 )
576  {
577  if ( QMessageBox::Cancel == QMessageBox::question( this, tr( "Warning" ), tr( "Natural break classification (Jenks) is O(n2) complexity, your classification may take a long time.\nPress cancel to abort breaks calculation or OK to continue." ), QMessageBox::Cancel, QMessageBox::Ok ) )
578  return;
579  }
580 
581  // create and set new renderer
582 
583  mRenderer->setClassAttribute( attrName );
584  mRenderer->setMode( mode );
585  mRenderer->setSourceColorRamp( ramp->clone() );
586 
587  QApplication::setOverrideCursor( Qt::WaitCursor );
588  mRenderer->updateClasses( mLayer, mode, nclasses );
590  QApplication::restoreOverrideCursor();
591  // PrettyBreaks and StdDev calculation don't generate exact
592  // number of classes - leave user interface unchanged for these
593  updateUiFromRenderer( false );
594 }
595 
597 {
598  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
599  if ( ramp == NULL )
600  return;
601 
602  mRenderer->updateColorRamp( ramp, cbxInvertedColorRamp->isChecked() );
604 }
605 
607 {
608  // Change the selected symbols alone if anything is selected
609  QItemSelectionModel* m = viewGraduated->selectionModel();
610  QModelIndexList i = m->selectedRows();
611  if ( m && i.size() > 0 )
612  {
614  return;
615  }
616 
617  // Otherwise change the base mGraduatedSymbol
618  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
619 
620  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
621  if ( !dlg.exec() )
622  {
623  delete newSymbol;
624  return;
625  }
626 
627  mGraduatedSymbol = newSymbol;
628 
632 }
633 
635 {
636  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mGraduatedSymbol, btnChangeGraduatedSymbol->iconSize() );
637  btnChangeGraduatedSymbol->setIcon( icon );
638 }
639 
640 #if 0
641 int QgsRendererV2PropertiesDialog::currentRangeRow()
642 {
643  QModelIndex idx = viewGraduated->selectionModel()->currentIndex();
644  if ( !idx.isValid() )
645  return -1;
646  return idx.row();
647 }
648 #endif
649 
651 {
652  QList<int> rows;
653  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
654 
655  foreach ( QModelIndex r, selectedRows )
656  {
657  if ( r.isValid() )
658  {
659  rows.append( r.row() );
660  }
661  }
662  return rows;
663 }
664 
666 {
668  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
669  QModelIndexList::const_iterator sIt = selectedRows.constBegin();
670 
671  for ( ; sIt != selectedRows.constEnd(); ++sIt )
672  {
673  selectedRanges.append( mModel->rendererRange( *sIt ) );
674  }
675  return selectedRanges;
676 }
677 
679 {
680  if ( idx.isValid() && idx.column() == 0 )
681  changeRangeSymbol( idx.row() );
682  if ( idx.isValid() && idx.column() == 1 )
683  changeRange( idx.row() );
684 }
685 
687 {
688  if ( !idx.isValid() )
689  mRowSelected = -1;
690  else
691  mRowSelected = idx.row();
692 }
693 
695 {
696  QItemSelectionModel* m = viewGraduated->selectionModel();
697  QModelIndexList selectedIndexes = m->selectedRows( 1 );
698  if ( m && selectedIndexes.size() > 0 )
699  {
700  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
701  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
702  if ( !dlg.exec() )
703  {
704  delete newSymbol;
705  return;
706  }
707 
708  foreach ( QModelIndex idx, selectedIndexes )
709  {
710  if ( idx.isValid() )
711  {
712  int rangeIdx = idx.row();
713  QgsSymbolV2* newRangeSymbol = newSymbol->clone();
714  newRangeSymbol->setColor( mRenderer->ranges()[rangeIdx].symbol()->color() );
715  mRenderer->updateRangeSymbol( rangeIdx, newRangeSymbol );
716  }
717  }
718  }
720 }
721 
723 {
724  QgsSymbolV2* newSymbol = mRenderer->ranges()[rangeIdx].symbol()->clone();
725 
726  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
727  if ( !dlg.exec() )
728  {
729  delete newSymbol;
730  return;
731  }
732 
733  mRenderer->updateRangeSymbol( rangeIdx, newSymbol );
734 }
735 
737 {
738  QgsLUDialog dialog( this );
739 
740  const QgsRendererRangeV2& range = mRenderer->ranges()[rangeIdx];
741  // Add arbitrary 2 to number of decimal places to retain a bit extra.
742  // Ensures users can see if legend is not completely honest!
743  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
744  if ( decimalPlaces < 0 ) decimalPlaces = 0;
745  dialog.setLowerValue( QString::number( range.lowerValue(), 'f', decimalPlaces ) );
746  dialog.setUpperValue( QString::number( range.upperValue(), 'f', decimalPlaces ) );
747 
748  if ( dialog.exec() == QDialog::Accepted )
749  {
750  double lowerValue = dialog.lowerValue().toDouble();
751  double upperValue = dialog.upperValue().toDouble();
752  mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
753  mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
754 
755  //If the boundaries have to stay linked, we update the ranges above and below, as well as their label if needed
756  if ( cbxLinkBoundaries->isChecked() )
757  {
758  if ( rangeIdx > 0 )
759  {
760  mRenderer->updateRangeUpperValue( rangeIdx - 1, lowerValue );
761  }
762 
763  if ( rangeIdx < mRenderer->ranges().size() - 1 )
764  {
765  mRenderer->updateRangeLowerValue( rangeIdx + 1, upperValue );
766  }
767  }
768  }
769 }
770 
772 {
774 }
775 
777 {
778  QList<int> classIndexes = selectedClasses();
779  mModel->deleteRows( classIndexes );
780 }
781 
783 {
785 }
786 
788 {
789  const QgsRangeList &ranges = mRenderer->ranges();
790  bool ordered = true;
791  for ( int i = 1;i < ranges.size();++i )
792  {
793  if ( ranges[i] < ranges[i-1] )
794  {
795  ordered = false;
796  break;
797  }
798  }
799  return ordered;
800 }
801 
803 {
804  //If the checkbox controlling the link between boundaries was unchecked and we check it, we have to link the boundaries
805  //This is done by updating all lower ranges to the upper value of the range above
806  if ( linked )
807  {
808  if ( ! rowsOrdered() )
809  {
810  int result = QMessageBox::warning(
811  this,
812  tr( "Linked range warning" ),
813  tr( "Rows will be reordered before linking boundaries. Continue?" ),
814  QMessageBox::Ok | QMessageBox::Cancel );
815  if ( result != QMessageBox::Ok )
816  {
817  cbxLinkBoundaries->setChecked( false );
818  return;
819  }
821  }
822 
823  // Ok to proceed
824  for ( int i = 1;i < mRenderer->ranges().size();++i )
825  {
826  mRenderer->updateRangeLowerValue( i, mRenderer->ranges()[i-1].upperValue() );
827  }
829  }
830 }
831 
833 {
834  if ( item->column() == 2 )
835  {
836  QString label = item->text();
837  int idx = item->row();
838  mRenderer->updateRangeLabel( idx, label );
839  }
840 }
841 
843 {
844  mRenderer->setRotationField( fldName );
845 }
846 
848 {
849  mRenderer->setSizeScaleField( fldName );
850 }
851 
853 {
854  mRenderer->setScaleMethod( scaleMethod );
855 }
856 
858 {
860  txtFormat->text(),
861  spinPrecision->value(),
862  cbxTrimTrailingZeroes->isChecked() );
863  mRenderer->setLabelFormat( labelFormat, true );
864  mModel->updateLabels();
865 }
866 
867 
869 {
870  QList<QgsSymbolV2*> selectedSymbols;
871 
872  QItemSelectionModel* m = viewGraduated->selectionModel();
873  QModelIndexList selectedIndexes = m->selectedRows( 1 );
874  if ( m && selectedIndexes.size() > 0 )
875  {
876  const QgsRangeList& ranges = mRenderer->ranges();
877  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
878  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
879  {
880  QStringList list = m->model()->data( *indexIt ).toString().split( " " );
881  if ( list.size() < 3 )
882  {
883  continue;
884  }
885 
886  double lowerBound = list.at( 0 ).toDouble();
887  double upperBound = list.at( 2 ).toDouble();
888  QgsSymbolV2* s = findSymbolForRange( lowerBound, upperBound, ranges );
889  if ( s )
890  {
891  selectedSymbols.append( s );
892  }
893  }
894  }
895  return selectedSymbols;
896 }
897 
898 QgsSymbolV2* QgsGraduatedSymbolRendererV2Widget::findSymbolForRange( double lowerBound, double upperBound, const QgsRangeList& ranges ) const
899 {
900  for ( QgsRangeList::const_iterator it = ranges.begin(); it != ranges.end(); ++it )
901  {
902  //range string has been created with option 'f',4
903  if ( qgsDoubleNear( lowerBound, it->lowerValue(), 0.0001 ) && qgsDoubleNear( upperBound, it->upperValue(), 0.0001 ) )
904  {
905  return it->symbol();
906  }
907  }
908  return 0;
909 }
910 
912 {
913  if ( mModel )
914  {
916  }
917 }
918 
920 {
922 }
923 
925 {
926  viewGraduated->selectionModel()->clear();
927  if ( ! rowsOrdered() )
928  {
929  cbxLinkBoundaries->setChecked( false );
930  }
931 }
932 
934 {
935 }
936 
938 {
939  if ( !event )
940  {
941  return;
942  }
943 
944  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
945  {
946  mCopyBuffer.clear();
948  }
949  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
950  {
951  QgsRangeList::const_iterator rIt = mCopyBuffer.constBegin();
952  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
953  {
954  mModel->addClass( *rIt );
955  }
956  }
957 }