QGIS API Documentation  2.9.0-Master
qgssymbolv2selectordialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssymbolv2selectordialog.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  ***************************************************************************/
15 
17 
18 #include "qgsstylev2.h"
19 #include "qgssymbolv2.h"
20 #include "qgssymbollayerv2.h"
21 #include "qgssymbollayerv2utils.h"
23 
24 // the widgets
25 #include "qgssymbolslistwidget.h"
27 #include "qgssymbollayerv2widget.h"
30 
31 #include "qgslogger.h"
32 #include "qgsapplication.h"
33 
34 #include <QColorDialog>
35 #include <QPainter>
36 #include <QStandardItemModel>
37 #include <QInputDialog>
38 #include <QMessageBox>
39 #include <QKeyEvent>
40 #include <QMenu>
41 
42 #include <QWidget>
43 #include <QFile>
44 #include <QStandardItem>
45 
46 static const int SymbolLayerItemType = QStandardItem::UserType + 1;
47 
48 // Hybrid item which may represent a symbol or a layer
49 // Check using item->isLayer()
50 class SymbolLayerItem : public QStandardItem
51 {
52  public:
54  {
55  setLayer( layer );
56  }
57 
59  {
60  setSymbol( symbol );
61  }
62 
64  {
65  mLayer = layer;
66  mIsLayer = true;
67  mSymbol = NULL;
68  updatePreview();
69  }
70 
72  {
73  mSymbol = symbol;
74  mIsLayer = false;
75  mLayer = NULL;
76  updatePreview();
77  }
78 
80  {
81  QIcon icon;
82  if ( mIsLayer )
83  icon = QgsSymbolLayerV2Utils::symbolLayerPreviewIcon( mLayer, QgsSymbolV2::MM, QSize( 16, 16 ) ); //todo: make unit a parameter
84  else
85  icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mSymbol, QSize( 16, 16 ) );
86  setIcon( icon );
87 
88  if ( parent() )
89  static_cast<SymbolLayerItem*>( parent() )->updatePreview();
90  }
91 
92  int type() const override { return SymbolLayerItemType; }
93  bool isLayer() { return mIsLayer; }
94 
95  // returns the symbol pointer; helpful in determining a layer's parent symbol
97  {
98  if ( mIsLayer )
99  return NULL;
100  return mSymbol;
101  }
102 
104  {
105  if ( mIsLayer )
106  return mLayer;
107  return NULL;
108  }
109 
110  QVariant data( int role ) const override
111  {
112  if ( role == Qt::DisplayRole || role == Qt::EditRole )
113  {
114  if ( mIsLayer )
116  else
117  {
118  switch ( mSymbol->type() )
119  {
120  case QgsSymbolV2::Marker : return "Marker";
121  case QgsSymbolV2::Fill : return "Fill";
122  case QgsSymbolV2::Line : return "Line";
123  default: return "Symbol";
124  }
125  }
126  }
127  if ( role == Qt::SizeHintRole )
128  return QVariant( QSize( 32, 32 ) );
129  if ( role == Qt::CheckStateRole )
130  return QVariant(); // could be true/false
131  return QStandardItem::data( role );
132  }
133 
134  protected:
137  bool mIsLayer;
138 };
139 
141 
142 QgsSymbolV2SelectorDialog::QgsSymbolV2SelectorDialog( QgsSymbolV2* symbol, QgsStyleV2* style, const QgsVectorLayer* vl, QWidget* parent, bool embedded )
143  : QDialog( parent ), mAdvancedMenu( NULL ), mVectorLayer( vl )
144 {
145 #ifdef Q_OS_MAC
146  setWindowModality( Qt::WindowModal );
147 #endif
148  mStyle = style;
149  mSymbol = symbol;
150  mPresentWidget = NULL;
151 
152  setupUi( this );
153  // can be embedded in renderer properties dialog
154  if ( embedded )
155  {
156  buttonBox->hide();
157  layout()->setContentsMargins( 0, 0, 0, 0 );
158  }
159  // setup icons
160  btnAddLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.png" ) ) );
161  btnRemoveLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.png" ) ) );
162  QIcon iconLock;
163  iconLock.addFile( QgsApplication::iconPath( "locked.svg" ), QSize(), QIcon::Normal, QIcon::On );
164  iconLock.addFile( QgsApplication::iconPath( "unlocked.svg" ), QSize(), QIcon::Normal, QIcon::Off );
165  btnLock->setIcon( iconLock );
166  btnUp->setIcon( QIcon( QgsApplication::iconPath( "symbologyUp.png" ) ) );
167  btnDown->setIcon( QIcon( QgsApplication::iconPath( "symbologyDown.png" ) ) );
168 
169  model = new QStandardItemModel();
170  // Set the symbol
171  layersTree->setModel( model );
172  layersTree->setHeaderHidden( true );
173 
174  QItemSelectionModel* selModel = layersTree->selectionModel();
175  connect( selModel, SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( layerChanged() ) );
176 
177  loadSymbol( symbol, static_cast<SymbolLayerItem*>( model->invisibleRootItem() ) );
178  updatePreview();
179 
180  connect( btnUp, SIGNAL( clicked() ), this, SLOT( moveLayerUp() ) );
181  connect( btnDown, SIGNAL( clicked() ), this, SLOT( moveLayerDown() ) );
182  connect( btnAddLayer, SIGNAL( clicked() ), this, SLOT( addLayer() ) );
183  connect( btnRemoveLayer, SIGNAL( clicked() ), this, SLOT( removeLayer() ) );
184  connect( btnLock, SIGNAL( clicked() ), this, SLOT( lockLayer() ) );
185  connect( btnSaveSymbol, SIGNAL( clicked() ), this, SLOT( saveSymbol() ) );
186 
187  updateUi();
188 
189  // set symbol as active item in the tree
190  QModelIndex newIndex = layersTree->model()->index( 0, 0 );
191  layersTree->setCurrentIndex( newIndex );
192 }
193 
195 {
196  // Ignore the ESC key to avoid close the dialog without the properties window
197  if ( !isWindow() && e->key() == Qt::Key_Escape )
198  {
199  e->ignore();
200  }
201  else
202  {
203  QDialog::keyPressEvent( e );
204  }
205 }
206 
208 {
209  if ( mAdvancedMenu == NULL )
210  {
211  mAdvancedMenu = new QMenu;
212  // Brute force method to activate the Advanced menu
213  layerChanged();
214  }
215  return mAdvancedMenu;
216 }
217 
219 {
220  SymbolLayerItem* symbolItem = new SymbolLayerItem( symbol );
221  QFont boldFont = symbolItem->font();
222  boldFont.setBold( true );
223  symbolItem->setFont( boldFont );
224  parent->appendRow( symbolItem );
225 
226  int count = symbol->symbolLayerCount();
227  for ( int i = count - 1; i >= 0; i-- )
228  {
229  SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ) );
230  layerItem->setEditable( false );
231  symbolItem->appendRow( layerItem );
232  if ( symbol->symbolLayer( i )->subSymbol() )
233  {
234  loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
235  }
236  }
237  layersTree->setExpanded( symbolItem->index(), true );
238 }
239 
240 
242 {
243  model->clear();
244  loadSymbol( mSymbol, static_cast<SymbolLayerItem*>( model->invisibleRootItem() ) );
245 }
246 
248 {
249  QModelIndex currentIdx = layersTree->currentIndex();
250  if ( !currentIdx.isValid() )
251  return;
252 
253  SymbolLayerItem *item = static_cast<SymbolLayerItem*>( model->itemFromIndex( currentIdx ) );
254  if ( !item->isLayer() )
255  {
256  btnUp->setEnabled( false );
257  btnDown->setEnabled( false );
258  btnRemoveLayer->setEnabled( false );
259  btnLock->setEnabled( false );
260  return;
261  }
262 
263  int rowCount = item->parent()->rowCount();
264  int currentRow = item->row();
265 
266  btnUp->setEnabled( currentRow > 0 );
267  btnDown->setEnabled( currentRow < rowCount - 1 );
268  btnRemoveLayer->setEnabled( rowCount > 1 );
269  btnLock->setEnabled( true );
270 }
271 
273 {
274  QImage preview = mSymbol->bigSymbolPreviewImage();
275  lblPreview->setPixmap( QPixmap::fromImage( preview ) );
276  // Hope this is a appropriate place
277  emit symbolModified();
278 }
279 
281 {
282  // get current layer item and update its icon
284  if ( item )
285  item->updatePreview();
286  // update also preview of the whole symbol
287  updatePreview();
288 }
289 
291 {
292  QModelIndex idx = layersTree->currentIndex();
293  if ( !idx.isValid() )
294  return NULL;
295 
296  SymbolLayerItem *item = static_cast<SymbolLayerItem*>( model->itemFromIndex( idx ) );
297  if ( !item->isLayer() )
298  return NULL;
299 
300  return item;
301 }
302 
304 {
305  QModelIndex idx = layersTree->currentIndex();
306  if ( !idx.isValid() )
307  return NULL;
308 
309  SymbolLayerItem *item = static_cast<SymbolLayerItem*>( model->itemFromIndex( idx ) );
310  if ( item->isLayer() )
311  return item->layer();
312 
313  return NULL;
314 }
315 
317 {
318  updateUi();
319 
320  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem*>( model->itemFromIndex( layersTree->currentIndex() ) );
321  if ( currentItem == NULL )
322  return;
323 
324  if ( currentItem->isLayer() )
325  {
326  SymbolLayerItem *parent = static_cast<SymbolLayerItem*>( currentItem->parent() );
327  QWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
328  setWidget( layerProp );
329  connect( layerProp, SIGNAL( changed() ), this, SLOT( updateLayerPreview() ) );
330  // This connection when layer type is changed
331  connect( layerProp, SIGNAL( changeLayer( QgsSymbolLayerV2* ) ), this, SLOT( changeLayer( QgsSymbolLayerV2* ) ) );
332  }
333  else
334  {
335  // then it must be a symbol
336  currentItem->symbol()->setLayer( mVectorLayer );
337  // Now populate symbols of that type using the symbols list widget:
338  QWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
339 
340  setWidget( symbolsList );
341  connect( symbolsList, SIGNAL( changed() ), this, SLOT( symbolChanged() ) );
342  }
344 }
345 
347 {
348  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem*>( model->itemFromIndex( layersTree->currentIndex() ) );
349  if ( currentItem == NULL || currentItem->isLayer() )
350  return;
351  // disconnect to avoid recreating widget
352  disconnect( layersTree->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( layerChanged() ) );
353  if ( currentItem->parent() )
354  {
355  // it is a sub-symbol
356  QgsSymbolV2* symbol = currentItem->symbol();
357  SymbolLayerItem *parent = static_cast<SymbolLayerItem*>( currentItem->parent() );
358  parent->removeRow( 0 );
359  loadSymbol( symbol, parent );
360  layersTree->setCurrentIndex( parent->child( 0 )->index() );
361  parent->updatePreview();
362  }
363  else
364  {
365  //it is the symbol itself
366  loadSymbol();
367  QModelIndex newIndex = layersTree->model()->index( 0, 0 );
368  layersTree->setCurrentIndex( newIndex );
369  }
370  updatePreview();
371  // connect it back once things are set
372  connect( layersTree->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( layerChanged() ) );
373 }
374 
376 {
377  int index = stackedWidget->addWidget( widget );
378  stackedWidget->setCurrentIndex( index );
379  if ( mPresentWidget )
380  {
381  stackedWidget->removeWidget( mPresentWidget );
382  QWidget *dummy = mPresentWidget;
383  mPresentWidget = widget;
384  delete dummy; // auto disconnects all signals
385  }
386 }
387 
389 {
390  QgsSymbolLayerV2* layer = currentLayer();
391  if ( !layer )
392  return;
393  btnLock->setChecked( layer->isLocked() );
394 }
395 
397 {
398  QModelIndex idx = layersTree->currentIndex();
399  if ( !idx.isValid() )
400  return;
401 
402  int insertIdx = -1;
403  SymbolLayerItem *item = static_cast<SymbolLayerItem*>( model->itemFromIndex( idx ) );
404  if ( item->isLayer() )
405  {
406  insertIdx = item->row();
407  item = static_cast<SymbolLayerItem*>( item->parent() );
408  }
409 
410  QgsSymbolV2* parentSymbol = item->symbol();
412  if ( insertIdx == -1 )
413  parentSymbol->appendSymbolLayer( newLayer );
414  else
415  parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
416  SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer );
417  item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
418  item->updatePreview();
419 
420  layersTree->setCurrentIndex( model->indexFromItem( newLayerItem ) );
421  updateUi();
422  updatePreview();
423 }
424 
426 {
428  int row = item->row();
429  SymbolLayerItem *parent = static_cast<SymbolLayerItem*>( item->parent() );
430 
431  int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
432  QgsSymbolV2* parentSymbol = parent->symbol();
433  QgsSymbolLayerV2 *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
434 
435  parent->removeRow( row );
436  parent->updatePreview();
437 
438  QModelIndex newIdx = parent->child( 0 )->index();
439  layersTree->setCurrentIndex( newIdx );
440 
441  updateUi();
442  updatePreview();
443  //finally delete the removed layer pointer
444  delete tmpLayer;
445 }
446 
448 {
449  moveLayerByOffset( + 1 );
450 }
451 
453 {
454  moveLayerByOffset( -1 );
455 }
456 
458 {
460  if ( item == NULL )
461  return;
462  int row = item->row();
463 
464  SymbolLayerItem *parent = static_cast<SymbolLayerItem*>( item->parent() );
465  QgsSymbolV2* parentSymbol = parent->symbol();
466 
467  int layerIdx = parent->rowCount() - row - 1;
468  // switch layers
469  QgsSymbolLayerV2* tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
470  parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
471 
472  QList<QStandardItem*> rowItems = parent->takeRow( row );
473  parent->insertRows( row + offset, rowItems );
474  parent->updatePreview();
475 
476  QModelIndex newIdx = rowItems[ 0 ]->index();
477  layersTree->setCurrentIndex( newIdx );
478 
479  updatePreview();
480  updateUi();
481 }
482 
484 {
485  QgsSymbolLayerV2* layer = currentLayer();
486  if ( !layer )
487  return;
488  layer->setLocked( btnLock->isChecked() );
489 }
490 
492 {
493  bool ok;
494  QString name = QInputDialog::getText( this, tr( "Symbol name" ),
495  tr( "Please enter name for the symbol:" ), QLineEdit::Normal, tr( "New symbol" ), &ok );
496  if ( !ok || name.isEmpty() )
497  return;
498 
499  // check if there is no symbol with same name
500  if ( mStyle->symbolNames().contains( name ) )
501  {
502  int res = QMessageBox::warning( this, tr( "Save symbol" ),
503  tr( "Symbol with name '%1' already exists. Overwrite?" )
504  .arg( name ),
505  QMessageBox::Yes | QMessageBox::No );
506  if ( res != QMessageBox::Yes )
507  {
508  return;
509  }
510  }
511 
512  // add new symbol to style and re-populate the list
513  mStyle->addSymbol( name, mSymbol->clone() );
514 
515  // make sure the symbol is stored
516  mStyle->saveSymbol( name, mSymbol->clone(), 0, QStringList() );
517 }
518 
520 {
522  QgsSymbolLayerV2* layer = item->layer();
523 
524  if ( layer->subSymbol() )
525  {
526  item->removeRow( 0 );
527  }
528  // update symbol layer item
529  item->setLayer( newLayer );
530  // When it is a marker symbol
531  if ( newLayer->subSymbol() )
532  {
533  loadSymbol( newLayer->subSymbol(), item );
534  layersTree->setExpanded( item->index(), true );
535  }
536 
537  // Change the symbol at last to avoid deleting item's layer
538  QgsSymbolV2* symbol = static_cast<SymbolLayerItem*>( item->parent() )->symbol();
539  int layerIdx = item->parent()->rowCount() - item->row() - 1;
540  symbol->changeSymbolLayer( layerIdx, newLayer );
541 
542  item->updatePreview();
543  updatePreview();
544  // Important: This lets the layer to have its own layer properties widget
545  layerChanged();
546 }
void setLocked(bool locked)
int type() const override
QgsSymbolLayerV2 * layer()
static unsigned index
static QgsSymbolLayerV2 * defaultSymbolLayer(QgsSymbolV2::SymbolType type)
create a new instance of symbol layer for specified symbol type with default settings ...
bool saveSymbol(QString name, QgsSymbolV2 *symbol, int groupid, QStringList tags)
add the symbol to the DB with the tags
Definition: qgsstylev2.cpp:107
static QIcon symbolLayerPreviewIcon(QgsSymbolLayerV2 *layer, QgsSymbolV2::OutputUnit u, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale())
QVariant data(int role) const override
SymbolType type() const
Definition: qgssymbolv2.h:86
static QString iconPath(QString iconFile)
Returns path to the desired icon file.
virtual QgsSymbolV2 * clone() const =0
QgsSymbolLayerV2AbstractMetadata * symbolLayerMetadata(QString name) const
return metadata for specified symbol layer. Returns NULL if not found
SymbolLayerItem(QgsSymbolV2 *symbol)
void symbolChanged()
Slot to update tree when a new symbol from style.
bool changeSymbolLayer(int index, QgsSymbolLayerV2 *layer)
delete layer at specified index and set a new one
static QgsSymbolLayerV2Registry * instance()
return the single instance of this class (instantiate it if not exists)
SymbolLayerItem(QgsSymbolLayerV2 *layer)
const QgsVectorLayer * mVectorLayer
bool addSymbol(QString name, QgsSymbolV2 *symbol, bool update=false)
add symbol to style. takes symbol's ownership
Definition: qgsstylev2.cpp:83
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
bool appendSymbolLayer(QgsSymbolLayerV2 *layer)
append symbol layer at the end of the list
void keyPressEvent(QKeyEvent *event) override
Reimplements dialog keyPress event so we can ignore it.
QMenu * advancedMenu()
return menu for "advanced" button - create it if doesn't exist and show the advanced button ...
void setLayer(const QgsVectorLayer *layer)
Definition: qgssymbolv2.h:188
static const int SymbolLayerItemType
int symbolLayerCount()
Returns total number of symbol layers contained in the symbol.
Definition: qgssymbolv2.h:113
QStringList symbolNames()
return a list of names of symbols
Definition: qgsstylev2.cpp:182
QgsSymbolV2SelectorDialog(QgsSymbolV2 *symbol, QgsStyleV2 *style, const QgsVectorLayer *vl, QWidget *parent=0, bool embedded=false)
virtual QString layerType() const =0
virtual QgsSymbolV2 * subSymbol()
void setLayer(QgsSymbolLayerV2 *layer)
bool insertSymbolLayer(int index, QgsSymbolLayerV2 *layer)
insert symbol layer to specified index
bool isLocked() const
void changeLayer(QgsSymbolLayerV2 *layer)
alters tree and sets proper widget when Layer Type is changed
QgsSymbolLayerV2 * takeSymbolLayer(int index)
remove symbol layer from the list and return pointer to it
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
void setSymbol(QgsSymbolV2 *symbol)
Represents a vector layer which manages a vector based data sets.
QImage bigSymbolPreviewImage()
#define tr(sourceText)