QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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  {
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  // Now populate symbols of that type using the symbols list widget:
337  QWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this );
338  setWidget( symbolsList );
339  connect( symbolsList, SIGNAL( changed() ), this, SLOT( symbolChanged() ) );
340  }
342 }
343 
345 {
346  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem*>( model->itemFromIndex( layersTree->currentIndex() ) );
347  if ( currentItem == NULL || currentItem->isLayer() )
348  return;
349  // disconnect to avoid recreating widget
350  disconnect( layersTree->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( layerChanged() ) );
351  if ( currentItem->parent() )
352  {
353  // it is a sub-symbol
354  QgsSymbolV2* symbol = currentItem->symbol();
355  SymbolLayerItem *parent = static_cast<SymbolLayerItem*>( currentItem->parent() );
356  parent->removeRow( 0 );
357  loadSymbol( symbol, parent );
358  layersTree->setCurrentIndex( parent->child( 0 )->index() );
359  parent->updatePreview();
360  }
361  else
362  {
363  //it is the symbol itself
364  loadSymbol();
365  QModelIndex newIndex = layersTree->model()->index( 0, 0 );
366  layersTree->setCurrentIndex( newIndex );
367  }
368  updatePreview();
369  // connect it back once things are set
370  connect( layersTree->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( layerChanged() ) );
371 }
372 
374 {
375  int index = stackedWidget->addWidget( widget );
376  stackedWidget->setCurrentIndex( index );
377  if ( mPresentWidget )
378  {
379  stackedWidget->removeWidget( mPresentWidget );
380  QWidget *dummy = mPresentWidget;
381  mPresentWidget = widget;
382  delete dummy; // auto disconnects all signals
383  }
384 }
385 
387 {
388  QgsSymbolLayerV2* layer = currentLayer();
389  if ( !layer )
390  return;
391  btnLock->setChecked( layer->isLocked() );
392 }
393 
395 {
396  QModelIndex idx = layersTree->currentIndex();
397  if ( !idx.isValid() )
398  return;
399 
400  int insertIdx = -1;
401  SymbolLayerItem *item = static_cast<SymbolLayerItem*>( model->itemFromIndex( idx ) );
402  if ( item->isLayer() )
403  {
404  insertIdx = item->row();
405  item = static_cast<SymbolLayerItem*>( item->parent() );
406  }
407 
408  QgsSymbolV2* parentSymbol = item->symbol();
410  if ( insertIdx == -1 )
411  parentSymbol->appendSymbolLayer( newLayer );
412  else
413  parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
414  SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer );
415  item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
416  item->updatePreview();
417 
418  layersTree->setCurrentIndex( model->indexFromItem( newLayerItem ) );
419  updateUi();
420  updatePreview();
421 }
422 
424 {
426  int row = item->row();
427  SymbolLayerItem *parent = static_cast<SymbolLayerItem*>( item->parent() );
428 
429  int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
430  QgsSymbolV2* parentSymbol = parent->symbol();
431  QgsSymbolLayerV2 *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
432 
433  parent->removeRow( row );
434  parent->updatePreview();
435 
436  QModelIndex newIdx = parent->child( 0 )->index();
437  layersTree->setCurrentIndex( newIdx );
438 
439  updateUi();
440  updatePreview();
441  //finally delete the removed layer pointer
442  delete tmpLayer;
443 }
444 
446 {
447  moveLayerByOffset( + 1 );
448 }
449 
451 {
452  moveLayerByOffset( -1 );
453 }
454 
456 {
458  if ( item == NULL )
459  return;
460  int row = item->row();
461 
462  SymbolLayerItem *parent = static_cast<SymbolLayerItem*>( item->parent() );
463  QgsSymbolV2* parentSymbol = parent->symbol();
464 
465  int layerIdx = parent->rowCount() - row - 1;
466  // switch layers
467  QgsSymbolLayerV2* tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
468  parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
469 
470  QList<QStandardItem*> rowItems = parent->takeRow( row );
471  parent->insertRows( row + offset, rowItems );
472  parent->updatePreview();
473 
474  QModelIndex newIdx = rowItems[ 0 ]->index();
475  layersTree->setCurrentIndex( newIdx );
476 
477  updatePreview();
478  updateUi();
479 }
480 
482 {
483  QgsSymbolLayerV2* layer = currentLayer();
484  if ( !layer )
485  return;
486  layer->setLocked( btnLock->isChecked() );
487 }
488 
490 {
491  bool ok;
492  QString name = QInputDialog::getText( this, tr( "Symbol name" ),
493  tr( "Please enter name for the symbol:" ), QLineEdit::Normal, tr( "New symbol" ), &ok );
494  if ( !ok || name.isEmpty() )
495  return;
496 
497  // check if there is no symbol with same name
498  if ( mStyle->symbolNames().contains( name ) )
499  {
500  int res = QMessageBox::warning( this, tr( "Save symbol" ),
501  tr( "Symbol with name '%1' already exists. Overwrite?" )
502  .arg( name ),
503  QMessageBox::Yes | QMessageBox::No );
504  if ( res != QMessageBox::Yes )
505  {
506  return;
507  }
508  }
509 
510  // add new symbol to style and re-populate the list
511  mStyle->addSymbol( name, mSymbol->clone() );
512 
513  // make sure the symbol is stored
514  mStyle->saveSymbol( name, mSymbol->clone(), 0, QStringList() );
515 }
516 
518 {
520  QgsSymbolLayerV2* layer = item->layer();
521 
522  if ( layer->subSymbol() )
523  {
524  item->removeRow( 0 );
525  }
526  // update symbol layer item
527  item->setLayer( newLayer );
528  // When it is a marker symbol
529  if ( newLayer->subSymbol() )
530  {
531  loadSymbol( newLayer->subSymbol(), item );
532  layersTree->setExpanded( item->index(), true );
533  }
534 
535  // Change the symbol at last to avoid deleting item's layer
536  QgsSymbolV2* symbol = static_cast<SymbolLayerItem*>( item->parent() )->symbol();
537  int layerIdx = item->parent()->rowCount() - item->row() - 1;
538  symbol->changeSymbolLayer( layerIdx, newLayer );
539 
540  item->updatePreview();
541  updatePreview();
542  // Important: This lets the layer to have its own layer properties widget
543  layerChanged();
544 }