QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsstylev2managerdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsstylev2managerdialog.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 "qgssymbollayerv2utils.h"
21 #include "qgsvectorcolorrampv2.h"
22 
30 
31 #include <QFile>
32 #include <QFileDialog>
33 #include <QInputDialog>
34 #include <QMessageBox>
35 #include <QSettings>
36 #include <QStandardItemModel>
37 #include <QAction>
38 #include <QMenu>
39 
40 #include "qgsapplication.h"
41 #include "qgslogger.h"
42 
43 
45  : QDialog( parent ), mStyle( style ), mModified( false )
46 {
47  setupUi( this );
48 #ifdef Q_OS_MAC
49  setWindowModality( Qt::WindowModal );
50 #endif
51 
52  QSettings settings;
53  restoreGeometry( settings.value( "/Windows/StyleV2Manager/geometry" ).toByteArray() );
54  mSplitter->setSizes( QList<int>() << 170 << 540 );
55  mSplitter->restoreState( settings.value( "/Windows/StyleV2Manager/splitter" ).toByteArray() );
56 
57  tabItemType->setDocumentMode( true );
58  searchBox->setPlaceholderText( tr( "Type here to filter symbols..." ) );
59 
60  // setup icons
61  btnAddItem->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.png" ) ) );
62  btnEditItem->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.png" ) ) );
63  btnRemoveItem->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.png" ) ) );
64  btnShare->setIcon( QIcon( QgsApplication::iconPath( "user.png" ) ) );
65 
66  connect( this, SIGNAL( finished( int ) ), this, SLOT( onFinished() ) );
67 
68  connect( listItems, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( editItem() ) );
69 
70  connect( btnAddItem, SIGNAL( clicked() ), this, SLOT( addItem() ) );
71  connect( btnEditItem, SIGNAL( clicked() ), this, SLOT( editItem() ) );
72  connect( btnRemoveItem, SIGNAL( clicked() ), this, SLOT( removeItem() ) );
73 
74  QMenu *shareMenu = new QMenu( tr( "Share Menu" ), this );
75  QAction *exportAction = shareMenu->addAction( tr( "Export" ) );
76  QAction *importAction = shareMenu->addAction( tr( "Import" ) );
77  connect( exportAction, SIGNAL( triggered() ), this, SLOT( exportItems() ) );
78  connect( importAction, SIGNAL( triggered() ), this, SLOT( importItems() ) );
79  btnShare->setMenu( shareMenu );
80 
81  // Set editing mode off by default
82  mGrouppingMode = false;
83 
84  QStandardItemModel* model = new QStandardItemModel( listItems );
85  listItems->setModel( model );
86  listItems->setSelectionMode( QAbstractItemView::ExtendedSelection );
87 
88  connect( model, SIGNAL( itemChanged( QStandardItem* ) ), this, SLOT( itemChanged( QStandardItem* ) ) );
89  connect( listItems->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ),
90  this, SLOT( symbolSelected( const QModelIndex& ) ) );
91 
92  populateTypes();
93 
94  QStandardItemModel* groupModel = new QStandardItemModel( groupTree );
95  groupTree->setModel( groupModel );
96  groupTree->setHeaderHidden( true );
98  connect( groupTree->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ),
99  this, SLOT( groupChanged( const QModelIndex& ) ) );
100  connect( groupModel, SIGNAL( itemChanged( QStandardItem* ) ),
101  this, SLOT( groupRenamed( QStandardItem* ) ) );
102 
103  QMenu *groupMenu = new QMenu( tr( "Group Actions" ), this );
104  QAction *groupSymbols = groupMenu->addAction( tr( "Group Symbols" ) );
105  QAction *editSmartgroup = groupMenu->addAction( tr( "Edit Smart Group" ) );
106  btnManageGroups->setMenu( groupMenu );
107  connect( groupSymbols, SIGNAL( triggered() ), this, SLOT( groupSymbolsAction() ) );
108  connect( editSmartgroup, SIGNAL( triggered() ), this, SLOT( editSmartgroupAction() ) );
109 
110  connect( btnAddGroup, SIGNAL( clicked() ), this, SLOT( addGroup() ) );
111  connect( btnRemoveGroup, SIGNAL( clicked() ), this, SLOT( removeGroup() ) );
112 
114 
115  connect( searchBox, SIGNAL( textChanged( QString ) ), this, SLOT( filterSymbols( QString ) ) );
116  tagsLineEdit->installEventFilter( this );
117 
118  // Context menu for groupTree
119  groupTree->setContextMenuPolicy( Qt::CustomContextMenu );
120  connect( groupTree, SIGNAL( customContextMenuRequested( const QPoint& ) ),
121  this, SLOT( grouptreeContextMenu( const QPoint& ) ) );
122 
123  // Context menu for listItems
124  listItems->setContextMenuPolicy( Qt::CustomContextMenu );
125  connect( listItems, SIGNAL( customContextMenuRequested( const QPoint& ) ),
126  this, SLOT( listitemsContextMenu( const QPoint& ) ) );
127 
128 }
129 
131 {
132  if ( mModified )
133  {
134  mStyle->save();
135  }
136 
137  QSettings settings;
138  settings.setValue( "/Windows/StyleV2Manager/geometry", saveGeometry() );
139  settings.setValue( "/Windows/StyleV2Manager/splitter", mSplitter->saveState() );
140 }
141 
143 {
144 #if 0
145  // save current selection index in types combo
146  int current = ( tabItemType->count() > 0 ? tabItemType->currentIndex() : 0 );
147 
148 // no counting of style items
149  int markerCount = 0, lineCount = 0, fillCount = 0;
150 
151  QStringList symbolNames = mStyle->symbolNames();
152  for ( int i = 0; i < symbolNames.count(); ++i )
153  {
154  switch ( mStyle->symbolRef( symbolNames[i] )->type() )
155  {
156  case QgsSymbolV2::Marker:
157  markerCount++;
158  break;
159  case QgsSymbolV2::Line:
160  lineCount++;
161  break;
162  case QgsSymbolV2::Fill:
163  fillCount++;
164  break;
165  default: Q_ASSERT( 0 && "unknown symbol type" );
166  break;
167  }
168  }
169 
170  cboItemType->clear();
171  cboItemType->addItem( tr( "Marker symbol (%1)" ).arg( markerCount ), QVariant( QgsSymbolV2::Marker ) );
172  cboItemType->addItem( tr( "Line symbol (%1)" ).arg( lineCount ), QVariant( QgsSymbolV2::Line ) );
173  cboItemType->addItem( tr( "Fill symbol (%1)" ).arg( fillCount ), QVariant( QgsSymbolV2::Fill ) );
174 
175  cboItemType->addItem( tr( "Color ramp (%1)" ).arg( mStyle->colorRampCount() ), QVariant( 3 ) );
176 
177  // update current index to previous selection
178  cboItemType->setCurrentIndex( current );
179 #endif
180 }
181 
183 {
184  // when in Color Ramp tab, add menu to add item button
185  if ( currentItemType() == 3 )
186  {
187  QStringList rampTypes;
188  rampTypes << tr( "Gradient" ) << tr( "Random" ) << tr( "ColorBrewer" );
189  rampTypes << tr( "cpt-city" ); // todo, only for rasters?
190  QMenu* menu = new QMenu( btnAddItem );
191  foreach ( QString rampType, rampTypes )
192  {
193  menu->addAction( rampType );
194  }
195  btnAddItem->setMenu( menu );
196  connect( menu, SIGNAL( triggered( QAction* ) ),
197  this, SLOT( addColorRamp( QAction* ) ) );
198  }
199  else
200  {
201  if ( btnAddItem->menu() )
202  {
203  disconnect( btnAddItem->menu(), SIGNAL( triggered( QAction* ) ),
204  this, SLOT( addColorRamp( QAction* ) ) );
205  btnAddItem->setMenu( 0 );
206  }
207  }
208 
209  // set icon and grid size, depending on type
210  if ( currentItemType() == 1 || currentItemType() == 3 )
211  {
212  listItems->setIconSize( QSize( 75, 50 ) );
213  listItems->setGridSize( QSize( 100, 80 ) );
214  }
215  else
216  {
217  listItems->setIconSize( QSize( 50, 50 ) );
218  listItems->setGridSize( QSize( 75, 80 ) );
219  }
220 
221  populateList();
222 }
223 
225 {
226  if ( currentItemType() > 3 )
227  {
228  Q_ASSERT( 0 && "not implemented" );
229  return;
230  }
231  groupChanged( groupTree->selectionModel()->currentIndex() );
232 }
233 
234 void QgsStyleV2ManagerDialog::populateSymbols( QStringList symbolNames, bool check )
235 {
236  QStandardItemModel* model = qobject_cast<QStandardItemModel*>( listItems->model() );
237  model->clear();
238 
239  int type = currentItemType();
240 
241  for ( int i = 0; i < symbolNames.count(); ++i )
242  {
243  QString name = symbolNames[i];
244  QgsSymbolV2* symbol = mStyle->symbol( name );
245  if ( symbol && symbol->type() == type )
246  {
247  QStandardItem* item = new QStandardItem( name );
248  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( symbol, listItems->iconSize() );
249  item->setIcon( icon );
250  item->setData( name ); // used to find out original name when user edited the name
251  item->setCheckable( check );
252  item->setToolTip( name );
253  // add to model
254  model->appendRow( item );
255  }
256  delete symbol;
257  }
258 }
259 
260 
261 void QgsStyleV2ManagerDialog::populateColorRamps( QStringList colorRamps, bool check )
262 {
263  QStandardItemModel* model = qobject_cast<QStandardItemModel*>( listItems->model() );
264  model->clear();
265 
266  for ( int i = 0; i < colorRamps.count(); ++i )
267  {
268  QString name = colorRamps[i];
269  QgsVectorColorRampV2* ramp = mStyle->colorRamp( name );
270 
271  QStandardItem* item = new QStandardItem( name );
272  QIcon icon = QgsSymbolLayerV2Utils::colorRampPreviewIcon( ramp, listItems->iconSize() );
273  item->setIcon( icon );
274  item->setData( name ); // used to find out original name when user edited the name
275  item->setCheckable( check );
276  item->setToolTip( name );
277  model->appendRow( item );
278  delete ramp;
279  }
280 }
281 
283 {
284  switch ( tabItemType->currentIndex() )
285  {
286  case 0: return QgsSymbolV2::Marker;
287  case 1: return QgsSymbolV2::Line;
288  case 2: return QgsSymbolV2::Fill;
289  case 3: return 3;
290  default: return 0;
291  }
292 }
293 
295 {
296  QModelIndex index = listItems->selectionModel()->currentIndex();
297  if ( !index.isValid() )
298  return QString();
299  return index.model()->data( index, 0 ).toString();
300 }
301 
303 {
304  bool changed = false;
305  if ( currentItemType() < 3 )
306  {
307  changed = addSymbol();
308  }
309  else if ( currentItemType() == 3 )
310  {
311  changed = addColorRamp();
312  }
313  else
314  {
315  Q_ASSERT( 0 && "not implemented" );
316  }
317 
318  if ( changed )
319  {
320  populateList();
321  populateTypes();
322  }
323 }
324 
326 {
327  // create new symbol with current type
328  QgsSymbolV2* symbol;
329  QString name = tr( "new symbol" );
330  switch ( currentItemType() )
331  {
332  case QgsSymbolV2::Marker:
333  symbol = new QgsMarkerSymbolV2();
334  name = tr( "new marker" );
335  break;
336  case QgsSymbolV2::Line:
337  symbol = new QgsLineSymbolV2();
338  name = tr( "new line" );
339  break;
340  case QgsSymbolV2::Fill:
341  symbol = new QgsFillSymbolV2();
342  name = tr( "new fill symbol" );
343  break;
344  default:
345  Q_ASSERT( 0 && "unknown symbol type" );
346  return false;
347  }
348 
349  // get symbol design
350  // NOTE : Set the parent widget as "this" to notify the Symbol selector
351  // that, it is being called by Style Manager, so recursive calling
352  // of style manager and symbol selector can be arrested
353  // See also: editSymbol()
354  QgsSymbolV2SelectorDialog dlg( symbol, mStyle, NULL, this );
355  if ( dlg.exec() == 0 )
356  {
357  delete symbol;
358  return false;
359  }
360 
361  // get unique name
362  bool nameInvalid = true;
363 
364  while ( nameInvalid )
365  {
366  bool ok;
367  name = QInputDialog::getText( this, tr( "Symbol Name" ),
368  tr( "Please enter a name for new symbol:" ),
369  QLineEdit::Normal, name, &ok );
370  if ( !ok )
371  {
372  delete symbol;
373  return false;
374  }
375  // validate name
376  if ( name.isEmpty() )
377  {
378  QMessageBox::warning( this, tr( "Save symbol" ),
379  tr( "Cannot save symbol without name. Enter a name." ) );
380  }
381  else if ( mStyle->symbolNames().contains( name ) )
382  {
383  int res = QMessageBox::warning( this, tr( "Save symbol" ),
384  tr( "Symbol with name '%1' already exists. Overwrite?" )
385  .arg( name ),
386  QMessageBox::Yes | QMessageBox::No );
387  if ( res == QMessageBox::Yes )
388  {
389  nameInvalid = false;
390  }
391  }
392  else
393  {
394  // valid name
395  nameInvalid = false;
396  }
397  }
398 
399  // add new symbol to style and re-populate the list
400  mStyle->addSymbol( name, symbol, true );
401  // TODO groups and tags
402  mModified = true;
403  return true;
404 }
405 
406 
407 QString QgsStyleV2ManagerDialog::addColorRampStatic( QWidget* parent, QgsStyleV2* style, QString rampType )
408 {
409  // let the user choose the color ramp type if rampType is not given
410  bool ok = true;
411  if ( rampType.isEmpty() )
412  {
413  QStringList rampTypes;
414  rampTypes << tr( "Gradient" ) << tr( "Random" ) << tr( "ColorBrewer" );
415  rampTypes << tr( "cpt-city" ); // todo, only for rasters?
416  rampType = QInputDialog::getItem( parent, tr( "Color ramp type" ),
417  tr( "Please select color ramp type:" ), rampTypes, 0, false, &ok );
418  }
419  if ( !ok || rampType.isEmpty() )
420  return QString();
421 
422  QString name = tr( "new ramp" );
423 
424  QgsVectorColorRampV2 *ramp = NULL;
425  if ( rampType == tr( "Gradient" ) )
426  {
428  QgsVectorGradientColorRampV2Dialog dlg( gradRamp, parent );
429  if ( !dlg.exec() )
430  {
431  delete gradRamp;
432  return QString();
433  }
434  ramp = gradRamp;
435  name = tr( "new gradient ramp" );
436  }
437  else if ( rampType == tr( "Random" ) )
438  {
440  QgsVectorRandomColorRampV2Dialog dlg( randRamp, parent );
441  if ( !dlg.exec() )
442  {
443  delete randRamp;
444  return QString();
445  }
446  ramp = randRamp;
447  name = tr( "new random ramp" );
448  }
449  else if ( rampType == tr( "ColorBrewer" ) )
450  {
452  QgsVectorColorBrewerColorRampV2Dialog dlg( brewerRamp, parent );
453  if ( !dlg.exec() )
454  {
455  delete brewerRamp;
456  return QString();
457  }
458  ramp = brewerRamp;
459  name = brewerRamp->schemeName() + QString::number( brewerRamp->colors() );
460  }
461  else if ( rampType == tr( "cpt-city" ) )
462  {
463  QgsCptCityColorRampV2* cptCityRamp = new QgsCptCityColorRampV2( "", "" );
464  QgsCptCityColorRampV2Dialog dlg( cptCityRamp, parent );
465  if ( !dlg.exec() )
466  {
467  delete cptCityRamp;
468  return QString();
469  }
470  // name = dlg.selectedName();
471  name = QFileInfo( cptCityRamp->schemeName() ).baseName() + cptCityRamp->variantName();
472  if ( dlg.saveAsGradientRamp() )
473  {
474  ramp = cptCityRamp->cloneGradientRamp();
475  delete cptCityRamp;
476  }
477  else
478  {
479  ramp = cptCityRamp;
480  }
481  }
482  else
483  {
484  // Q_ASSERT( 0 && "invalid ramp type" );
485  // bailing out is rather harsh!
486  QgsDebugMsg( "invalid ramp type " + rampType );
487  return QString();
488  }
489 
490  // get unique name
491  bool nameInvalid = true;
492 
493  while ( nameInvalid )
494  {
495  bool ok;
496  name = QInputDialog::getText( parent, tr( "Color Ramp Name" ),
497  tr( "Please enter a name for new color ramp:" ),
498  QLineEdit::Normal, name, &ok );
499  if ( !ok )
500  {
501  delete ramp;
502  return QString();
503  }
504  // validate name
505  if ( name.isEmpty() )
506  {
507  QMessageBox::warning( parent, tr( "Save Color Ramp" ),
508  tr( "Cannot save color ramp without name. Enter a name." ) );
509  }
510  else if ( style->colorRampNames().contains( name ) )
511  {
512  int res = QMessageBox::warning( parent, tr( "Save color ramp" ),
513  tr( "Color ramp with name '%1' already exists. Overwrite?" )
514  .arg( name ),
515  QMessageBox::Yes | QMessageBox::No );
516  if ( res == QMessageBox::Yes )
517  {
518  nameInvalid = false;
519  }
520  }
521  else
522  {
523  // valid name
524  nameInvalid = false;
525  }
526  }
527 
528  // add new symbol to style and re-populate the list
529  style->addColorRamp( name, ramp, true );
530  // TODO groups and tags, using saveColorRamp
531  return name;
532 }
533 
534 
536 {
537  return addColorRamp( 0 );
538 }
539 
541 {
542  // pass the action text, which is the color ramp type
543  QString rampName = addColorRampStatic( this, mStyle,
544  action ? action->text() : QString() );
545  if ( !rampName.isEmpty() )
546  {
547  mModified = true;
548  populateList();
549  return true;
550  }
551 
552  return false;
553 }
554 
556 {
557  bool changed = false;
558  if ( currentItemType() < 3 )
559  {
560  changed = editSymbol();
561  }
562  else if ( currentItemType() == 3 )
563  {
564  changed = editColorRamp();
565  }
566  else
567  {
568  Q_ASSERT( 0 && "not implemented" );
569  }
570 
571  if ( changed )
572  populateList();
573 }
574 
576 {
577  QString symbolName = currentItemName();
578  if ( symbolName.isEmpty() )
579  return false;
580 
581  QgsSymbolV2* symbol = mStyle->symbol( symbolName );
582 
583  // let the user edit the symbol and update list when done
584  QgsSymbolV2SelectorDialog dlg( symbol, mStyle, NULL, this );
585  if ( dlg.exec() == 0 )
586  {
587  delete symbol;
588  return false;
589  }
590 
591  // by adding symbol to style with the same name the old effectively gets overwritten
592  mStyle->addSymbol( symbolName, symbol, true );
593  mModified = true;
594  return true;
595 }
596 
598 {
599  QString name = currentItemName();
600  if ( name.isEmpty() )
601  return false;
602 
603  QgsVectorColorRampV2* ramp = mStyle->colorRamp( name );
604 
605  if ( ramp->type() == "gradient" )
606  {
607  QgsVectorGradientColorRampV2* gradRamp = static_cast<QgsVectorGradientColorRampV2*>( ramp );
608  QgsVectorGradientColorRampV2Dialog dlg( gradRamp, this );
609  if ( !dlg.exec() )
610  {
611  delete ramp;
612  return false;
613  }
614  }
615  else if ( ramp->type() == "random" )
616  {
617  QgsVectorRandomColorRampV2* randRamp = static_cast<QgsVectorRandomColorRampV2*>( ramp );
618  QgsVectorRandomColorRampV2Dialog dlg( randRamp, this );
619  if ( !dlg.exec() )
620  {
621  delete ramp;
622  return false;
623  }
624  }
625  else if ( ramp->type() == "colorbrewer" )
626  {
627  QgsVectorColorBrewerColorRampV2* brewerRamp = static_cast<QgsVectorColorBrewerColorRampV2*>( ramp );
628  QgsVectorColorBrewerColorRampV2Dialog dlg( brewerRamp, this );
629  if ( !dlg.exec() )
630  {
631  delete ramp;
632  return false;
633  }
634  }
635  else if ( ramp->type() == "cpt-city" )
636  {
637  QgsCptCityColorRampV2* cptCityRamp = static_cast<QgsCptCityColorRampV2*>( ramp );
638  QgsCptCityColorRampV2Dialog dlg( cptCityRamp, this );
639  if ( !dlg.exec() )
640  {
641  delete ramp;
642  return false;
643  }
644  if ( dlg.saveAsGradientRamp() )
645  {
646  ramp = cptCityRamp->cloneGradientRamp();
647  delete cptCityRamp;
648  }
649  }
650  else
651  {
652  Q_ASSERT( 0 && "invalid ramp type" );
653  }
654 
655  mStyle->addColorRamp( name, ramp, true );
656  mModified = true;
657  return true;
658 }
659 
660 
662 {
663  bool changed = false;
664  if ( currentItemType() < 3 )
665  {
666  changed = removeSymbol();
667  }
668  else if ( currentItemType() == 3 )
669  {
670  changed = removeColorRamp();
671  }
672  else
673  {
674  Q_ASSERT( 0 && "not implemented" );
675  }
676 
677  if ( changed )
678  {
679  populateList();
680  populateTypes();
681  }
682 }
683 
685 {
686  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
687  foreach ( QModelIndex index, indexes )
688  {
689  QString symbolName = index.data().toString();
690  // delete from style and update list
691  if ( !symbolName.isEmpty() )
692  mStyle->removeSymbol( symbolName );
693  }
694  mModified = true;
695  return true;
696 }
697 
699 {
700  QString rampName = currentItemName();
701  if ( rampName.isEmpty() )
702  return false;
703 
704  mStyle->removeColorRamp( rampName );
705  mModified = true;
706  return true;
707 }
708 
709 void QgsStyleV2ManagerDialog::itemChanged( QStandardItem* item )
710 {
711  // an item has been edited
712  QString oldName = item->data().toString();
713 
714  bool changed = false;
715  if ( currentItemType() < 3 )
716  {
717  changed = mStyle->renameSymbol( oldName, item->text() );
718  }
719  else if ( currentItemType() == 3 )
720  {
721  changed = mStyle->renameColorRamp( oldName, item->text() );
722  }
723 
724  if ( changed )
725  {
726  populateList();
727  mModified = true;
728  }
729  else
730  {
731  QMessageBox::critical( this, tr( "Cannot rename item" ),
732  tr( "Name is already taken by another item. Choose a different name." ) );
733  item->setText( oldName );
734  }
735 }
736 
738 {
740  dlg.exec();
741 }
742 
744 {
746  dlg.exec();
747  populateList();
748  populateGroups();
749 }
750 
751 void QgsStyleV2ManagerDialog::setBold( QStandardItem* item )
752 {
753  QFont font = item->font();
754  font.setBold( true );
755  item->setFont( font );
756 }
757 
759 {
760  QStandardItemModel *model = qobject_cast<QStandardItemModel*>( groupTree->model() );
761  model->clear();
762 
763  QStandardItem *allSymbols = new QStandardItem( tr( "All Symbols" ) );
764  allSymbols->setData( "all" );
765  allSymbols->setEditable( false );
766  setBold( allSymbols );
767  model->appendRow( allSymbols );
768 
769  QStandardItem *group = new QStandardItem( "" ); //require empty name to get first order groups
770  group->setData( "groups" );
771  group->setEditable( false );
772  buildGroupTree( group );
773  group->setText( tr( "Groups" ) );//set title later
774  QStandardItem *ungrouped = new QStandardItem( tr( "Ungrouped" ) );
775  ungrouped->setData( 0 );
776  setBold( ungrouped );
777  setBold( group );
778  group->appendRow( ungrouped );
779  model->appendRow( group );
780 
781  QStandardItem *tag = new QStandardItem( tr( "Smart Groups" ) );
782  tag->setData( "smartgroups" );
783  tag->setEditable( false );
784  setBold( tag );
786  QgsSymbolGroupMap::const_iterator i = sgMap.constBegin();
787  while ( i != sgMap.constEnd() )
788  {
789  QStandardItem *item = new QStandardItem( i.value() );
790  item->setData( i.key() );
791  tag->appendRow( item );
792  ++i;
793  }
794  model->appendRow( tag );
795 
796  // expand things in the grouo tree
797  int rows = model->rowCount( model->indexFromItem( model->invisibleRootItem() ) );
798  for ( int i = 0; i < rows; i++ )
799  {
800  groupTree->setExpanded( model->indexFromItem( model->item( i ) ), true );
801  }
802 }
803 
804 void QgsStyleV2ManagerDialog::buildGroupTree( QStandardItem* &parent )
805 {
806  QgsSymbolGroupMap groups = mStyle->childGroupNames( parent->text() );
807  QgsSymbolGroupMap::const_iterator i = groups.constBegin();
808  while ( i != groups.constEnd() )
809  {
810  QStandardItem *item = new QStandardItem( i.value() );
811  item->setData( i.key() );
812  parent->appendRow( item );
813  buildGroupTree( item );
814  ++i;
815  }
816 }
817 
819 {
820  QStringList symbolNames;
821  QStringList groupSymbols;
822 
824  if ( currentItemType() > 3 )
825  {
826  QgsDebugMsg( "Entity not implemented" );
827  return;
828  }
829 
830  QString category = index.data( Qt::UserRole + 1 ).toString();
831  if ( category == "all" || category == "groups" || category == "smartgroups" )
832  {
833  enableGroupInputs( false );
834  if ( category == "groups" || category == "smartgroups" )
835  {
836  btnAddGroup->setEnabled( true );
837  }
838  symbolNames = currentItemType() < 3 ? mStyle->symbolNames() : mStyle->colorRampNames();
839  }
840  else
841  {
842  //determine groups and tags
843  if ( index.parent().data( Qt::UserRole + 1 ) == "smartgroups" )
844  {
845  btnAddGroup->setEnabled( false );
846  btnRemoveGroup->setEnabled( true );
847  btnManageGroups->setEnabled( true );
848  int groupId = index.data( Qt::UserRole + 1 ).toInt();
849  symbolNames = mStyle->symbolsOfSmartgroup( type, groupId );
850  }
851  else // then it must be a group
852  {
853  if (( !index.data( Qt::UserRole + 1 ).toInt() && ( index.data() == "Ungrouped" ) ) || mGrouppingMode )
854  enableGroupInputs( false );
855  else
856  enableGroupInputs( true );
857  int groupId = index.data( Qt::UserRole + 1 ).toInt();
858  symbolNames = mStyle->symbolsOfGroup( type, groupId );
859  if ( mGrouppingMode && groupId )
860  {
861  groupSymbols = symbolNames;
862  symbolNames += mStyle->symbolsOfGroup( type, 0 );
863  }
864  }
865  }
866 
867  if ( currentItemType() < 3 )
868  {
869  populateSymbols( symbolNames, mGrouppingMode );
870  }
871  else if ( currentItemType() == 3 )
872  {
873  populateColorRamps( symbolNames, mGrouppingMode );
874  }
875  if ( mGrouppingMode )
876  setSymbolsChecked( groupSymbols );
877 }
878 
880 {
881  QStandardItemModel *model = qobject_cast<QStandardItemModel*>( groupTree->model() );
882  QModelIndex parentIndex = groupTree->currentIndex();
883 
884  // Violation 1: Creating sub-groups of system defined groups
885  QString parentData = parentIndex.data( Qt::UserRole + 1 ).toString();
886  if ( parentData == "all" || ( parentIndex.data() == "Ungrouped" && parentData == "0" ) )
887  {
888  int err = QMessageBox::critical( this, tr( "Invalid Selection" ),
889  tr( "The parent group you have selected is not user editable.\n"
890  "Kindly select a user defined group." ) );
891  if ( err )
892  return;
893  }
894 
895  // Violation 2: Creating a nested tag
896  if ( parentIndex.parent().data( Qt::UserRole + 1 ).toString() == "smartgroups" )
897  {
898  int err = QMessageBox::critical( this, tr( "Operation Not Allowed" ),
899  tr( "Creation of nested smart groups are not allowed\n"
900  "Select the 'Smart Group' to create a new group." ) );
901  if ( err )
902  return;
903  }
904 
905  QString itemName;
906  QVariant itemData;
907  bool isGroup = true;
908 
909  // create a smart group if that is selected
910  if ( parentIndex.data( Qt::UserRole + 1 ).toString() == "smartgroups" )
911  {
912  QgsSmartGroupEditorDialog dlg( mStyle, this );
913  if ( dlg.exec() == QDialog::Rejected )
914  return;
915  int id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
916  if ( !id )
917  return;
918  itemData = QVariant( id );
919  itemName = dlg.smartgroupName();
920  isGroup = false;
921  }
922  else
923  {
924  itemName = QString( tr( "New Group" ) );
925  itemData = QVariant( "newgroup" );
926  }
927 
928  // Else create a simple child-group to the selected
929  QStandardItem *parentItem = model->itemFromIndex( parentIndex );
930  QStandardItem *childItem = new QStandardItem( itemName );
931  childItem->setData( itemData );
932  parentItem->appendRow( childItem );
933 
934  groupTree->setCurrentIndex( childItem->index() );
935  if ( isGroup )
936  {
937  groupTree->edit( childItem->index() );
938  }
939 }
940 
942 {
943  QStandardItemModel *model = qobject_cast<QStandardItemModel*>( groupTree->model() );
944  QModelIndex index = groupTree->currentIndex();
945 
946  // Violation: removing system groups
947  QString data = index.data( Qt::UserRole + 1 ).toString();
948  if ( data == "all" || data == "groups" || data == "smartgroups" || index.data() == "Ungrouped" )
949  {
950  int err = QMessageBox::critical( this, tr( "Invalid selection" ),
951  tr( "Cannot delete system defined categories.\n"
952  "Kindly select a group or smart group you might want to delete." ) );
953  if ( err )
954  return;
955  }
956 
957  QStandardItem *parentItem = model->itemFromIndex( index.parent() );
958  if ( parentItem->data( Qt::UserRole + 1 ).toString() == "smartgroups" )
959  {
960  mStyle->remove( QgsStyleV2::SmartgroupEntity, index.data( Qt::UserRole + 1 ).toInt() );
961  }
962  else
963  {
964  mStyle->remove( QgsStyleV2::GroupEntity, index.data( Qt::UserRole + 1 ).toInt() );
965  QStandardItem *item = model->itemFromIndex( index );
966  if ( item->hasChildren() )
967  {
968  QStandardItem *parent = item->parent();
969  for ( int i = 0; i < item->rowCount(); i++ )
970  {
971  parent->appendRow( item->takeChild( i ) );
972  }
973  }
974  }
975  parentItem->removeRow( index.row() );
976 }
977 
978 void QgsStyleV2ManagerDialog::groupRenamed( QStandardItem * item )
979 {
980  QString data = item->data( Qt::UserRole + 1 ).toString();
981  QgsDebugMsg( "Symbol group edited: data=" + data + " text=" + item->text() );
982  if ( data == "newgroup" )
983  {
984  int id;
985  if ( item->parent()->data( Qt::UserRole + 1 ).toString() == "groups" )
986  {
987  id = mStyle->addGroup( item->text() );
988  }
989  else
990  {
991  int parentid = item->parent()->data( Qt::UserRole + 1 ).toInt();
992  id = mStyle->addGroup( item->text(), parentid );
993  }
994  if ( !id )
995  {
996  QMessageBox::critical( this, tr( "Error!" ),
997  tr( "New group could not be created.\n"
998  "There was a problem with your symbol database." ) );
999  item->parent()->removeRow( item->row() );
1000  return;
1001  }
1002  else
1003  {
1004  item->setData( id );
1005  }
1006  }
1007  else
1008  {
1009  int id = item->data( Qt::UserRole + 1 ).toInt();
1010  QString name = item->text();
1011  if ( item->parent()->data( Qt::UserRole + 1 ) == "smartgroups" )
1012  {
1014  }
1015  else
1016  {
1017  mStyle->rename( QgsStyleV2::GroupEntity, id, name );
1018  }
1019  }
1020 }
1021 
1023 {
1024 
1025  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel*>( groupTree->model() );
1026  QStandardItemModel *model = qobject_cast<QStandardItemModel*>( listItems->model() );
1027  QAction *senderAction = qobject_cast<QAction*>( sender() );
1028 
1029  if ( mGrouppingMode )
1030  {
1031  mGrouppingMode = false;
1032  senderAction->setText( tr( "Group Symbols" ) );
1033  // disconnect slot which handles regrouping
1034  disconnect( model, SIGNAL( itemChanged( QStandardItem* ) ),
1035  this, SLOT( regrouped( QStandardItem* ) ) );
1036 
1037  // disabel all items except groups in groupTree
1039  groupChanged( groupTree->currentIndex() );
1040 
1041  // Finally: Reconnect all Symbol editing functionalities
1042  connect( treeModel, SIGNAL( itemChanged( QStandardItem* ) ),
1043  this, SLOT( groupRenamed( QStandardItem* ) ) );
1044  connect( model, SIGNAL( itemChanged( QStandardItem* ) ),
1045  this, SLOT( itemChanged( QStandardItem* ) ) );
1046  // Reset the selection mode
1047  listItems->setSelectionMode( QAbstractItemView::ExtendedSelection );
1048  }
1049  else
1050  {
1051  bool validGroup = false;
1052  // determine whether it is a valid group
1053  QModelIndex present = groupTree->currentIndex();
1054  while ( present.parent().isValid() )
1055  {
1056  if ( present.parent().data() == "Groups" )
1057  {
1058  validGroup = true;
1059  break;
1060  }
1061  else
1062  present = present.parent();
1063  }
1064  if ( !validGroup )
1065  return;
1066 
1067  mGrouppingMode = true;
1068  // Change the text menu
1069  senderAction->setText( tr( "Finish Grouping" ) );
1070  // Remove all Symbol editing functionalities
1071  disconnect( treeModel, SIGNAL( itemChanged( QStandardItem* ) ),
1072  this, SLOT( groupRenamed( QStandardItem* ) ) );
1073  disconnect( model, SIGNAL( itemChanged( QStandardItem* ) ),
1074  this, SLOT( itemChanged( QStandardItem* ) ) );
1075 
1076  // disabel all items except groups in groupTree
1077  enableItemsForGroupingMode( false );
1078  groupChanged( groupTree->currentIndex() );
1079  btnManageGroups->setEnabled( true );
1080 
1081 
1082  // Connect to slot which handles regrouping
1083  connect( model, SIGNAL( itemChanged( QStandardItem* ) ),
1084  this, SLOT( regrouped( QStandardItem* ) ) );
1085 
1086  // No selection should be possible
1087  listItems->setSelectionMode( QAbstractItemView::NoSelection );
1088  }
1089 }
1090 
1091 void QgsStyleV2ManagerDialog::regrouped( QStandardItem *item )
1092 {
1094  if ( currentItemType() > 3 )
1095  {
1096  QgsDebugMsg( "Unknown style entity" );
1097  return;
1098  }
1099  int groupid = groupTree->currentIndex().data( Qt::UserRole + 1 ).toInt();
1100  QString symbolName = item->text();
1101  bool regrouped;
1102  if ( item->checkState() == Qt::Checked )
1103  regrouped = mStyle->group( type, symbolName, groupid );
1104  else
1105  regrouped = mStyle->group( type, symbolName, 0 );
1106  if ( !regrouped )
1107  {
1108  int er = QMessageBox::critical( this, tr( "Database Error" ),
1109  tr( "There was a problem with the Symbols database while regrouping." ) );
1110  // call the slot again to get back to normal
1111  if ( er )
1113  }
1114 }
1115 
1117 {
1118  QStandardItemModel *model = qobject_cast<QStandardItemModel*>( listItems->model() );
1119  foreach ( const QString symbol, symbols )
1120  {
1121  QList<QStandardItem*> items = model->findItems( symbol );
1122  foreach ( QStandardItem* item, items )
1123  item->setCheckState( Qt::Checked );
1124  }
1125 }
1126 
1128 {
1129  QStringList items;
1130  if ( currentItemType() == 3 )
1131  {
1132  items = mStyle->findSymbols( QgsStyleV2::ColorrampEntity, qword );
1133  populateColorRamps( items );
1134  }
1135  else
1136  {
1137  items = mStyle->findSymbols( QgsStyleV2::SymbolEntity, qword );
1138  populateSymbols( items );
1139  }
1140 }
1141 
1143 {
1144  QModelIndexList indexes = listItems->selectionModel()->selection().indexes();
1145  QStringList addtags;
1146  QStringList removetags;
1147 
1148  QStringList oldtags = mTagList;
1149  QStringList newtags = tagsLineEdit->text().split( ",", QString::SkipEmptyParts );
1150 
1152  if ( currentItemType() < 3 )
1153  {
1154  type = QgsStyleV2::SymbolEntity;
1155  }
1156  else if ( currentItemType() == 3 )
1157  {
1159  }
1160  else
1161  {
1162  QgsDebugMsg( "Unknown Style Entity!" );
1163  return;
1164  }
1165  // compare old with new to find removed tags
1166  foreach ( const QString &tag, oldtags )
1167  {
1168  if ( !newtags.contains( tag ) )
1169  removetags.append( tag );
1170  }
1171  if ( removetags.size() > 0 )
1172  {
1173  foreach ( QModelIndex index, indexes )
1174  {
1175  mStyle->detagSymbol( type, index.data().toString(), removetags );
1176  }
1177  }
1178  // compare new with old to find added tags
1179  foreach ( const QString &tag, newtags )
1180  {
1181  if ( !oldtags.contains( tag ) )
1182  addtags.append( tag );
1183  }
1184  if ( addtags.size() > 0 )
1185  {
1186  foreach ( QModelIndex index, indexes )
1187  {
1188  mStyle->tagSymbol( type, index.data().toString(), addtags );
1189  }
1190  }
1191 }
1192 
1194 {
1195  // Populate the tags for the symbol
1196  tagsLineEdit->clear();
1197  QStandardItem *item = static_cast<QStandardItemModel*>( listItems->model() )->itemFromIndex( index );
1199  mTagList = mStyle->tagsOfSymbol( type, item->data().toString() );
1200  tagsLineEdit->setText( mTagList.join( "," ) );
1201 }
1202 
1204 {
1205  groupTree->setEnabled( enable );
1206  btnAddGroup->setEnabled( enable );
1207  btnRemoveGroup->setEnabled( enable );
1208  btnManageGroups->setEnabled( enable );
1209  searchBox->setEnabled( enable );
1210  tagsLineEdit->setEnabled( enable );
1211 }
1212 
1214 {
1215  btnAddGroup->setEnabled( enable );
1216  btnRemoveGroup->setEnabled( enable );
1217  btnManageGroups->setEnabled( enable );
1218 }
1219 
1221 {
1222  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel*>( groupTree->model() );
1223  for ( int i = 0; i < treeModel->rowCount(); i++ )
1224  {
1225  if ( treeModel->item( i )->data() != "groups" )
1226  {
1227  treeModel->item( i )->setEnabled( enable );
1228  }
1229  if ( treeModel->item( i )->data() == "groups" )
1230  {
1231  treeModel->item( i )->setEnabled( enable );
1232  for ( int k = 0; k < treeModel->item( i )->rowCount(); k++ )
1233  {
1234  if ( !treeModel->item( i )->child( k )->data().toInt() )
1235  treeModel->item( i )->child( k )->setEnabled( enable );
1236  }
1237  }
1238  if ( treeModel->item( i )->data() == "smartgroups" )
1239  {
1240  for ( int j = 0; j < treeModel->item( i )->rowCount(); j++ )
1241  {
1242  treeModel->item( i )->child( j )->setEnabled( enable );
1243  }
1244  }
1245  }
1246 
1247  // The buttons
1248  // NOTE: if you ever change the layout name in the .ui file edit here too
1249  for ( int i = 0; i < symbolBtnsLayout->count(); i++ )
1250  {
1251  symbolBtnsLayout->itemAt( i )->widget()->setEnabled( enable );
1252  }
1253 
1254 }
1255 
1257 {
1258  QPoint globalPos = groupTree->viewport()->mapToGlobal( point );
1259 
1260  QModelIndex index = groupTree->indexAt( point );
1261  QgsDebugMsg( "Now you clicked: " + index.data().toString() );
1262 
1263  QMenu groupMenu;
1264 
1265  if ( index.parent().isValid() && ( index.data().toString() != "Ungrouped" ) )
1266  {
1267  if ( index.parent().data( Qt::UserRole + 1 ).toString() == "smartgroups" )
1268  {
1269  groupMenu.addAction( tr( "Edit Group" ) );
1270  }
1271  else
1272  {
1273  groupMenu.addAction( tr( "Add Group" ) );
1274  }
1275  groupMenu.addAction( tr( "Remove Group" ) );
1276  }
1277  else if ( index.data( Qt::UserRole + 1 ) == "groups" || index.data( Qt::UserRole + 1 ) == "smartgroups" )
1278  {
1279  groupMenu.addAction( tr( "Add Group" ) );
1280  }
1281 
1282 
1283  QAction* selectedItem = groupMenu.exec( globalPos );
1284 
1285  if ( selectedItem )
1286  {
1287  if ( selectedItem->text() == tr( "Add Group" ) )
1288  addGroup();
1289  else if ( selectedItem->text() == tr( "Remove Group" ) )
1290  removeGroup();
1291  else if ( selectedItem->text() == tr( "Edit Group" ) )
1293  }
1294 }
1295 
1297 {
1298  QPoint globalPos = listItems->viewport()->mapToGlobal( point );
1299 
1300  QMenu *groupMenu = new QMenu( this );
1301  QMenu *groupList = new QMenu( this );
1302  groupList->setTitle( tr( "Apply Group" ) );
1303 
1304  QStringList groups = mStyle->groupNames();
1305  foreach ( QString group, groups )
1306  {
1307  groupList->addAction( group );
1308  }
1309  groupMenu->addMenu( groupList );
1310  groupMenu->addAction( tr( "Un-group" ) );
1311 
1312  QAction* selectedItem = groupMenu->exec( globalPos );
1313 
1314  if ( selectedItem )
1315  {
1317  if ( currentItemType() > 3 )
1318  {
1319  QgsDebugMsg( "unknow entity type" );
1320  return;
1321  }
1322  int groupId = 0;
1323  if ( selectedItem->text() != tr( "Un-group" ) )
1324  {
1325  groupId = mStyle->groupId( selectedItem->text() );
1326  }
1327  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1328  foreach ( QModelIndex index, indexes )
1329  {
1330  mStyle->group( type, index.data().toString(), groupId );
1331  }
1332  populateList();
1333 
1334  QgsDebugMsg( "Selected Action: " + selectedItem->text() );
1335  }
1336 }
1337 
1339 {
1340  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel*>( groupTree->model() );
1341 
1342  // determine whether it is a valid group
1343  QModelIndex present = groupTree->currentIndex();
1344  if ( present.parent().data( Qt::UserRole + 1 ) != "smartgroups" )
1345  {
1346  QMessageBox::critical( this, tr( "Invalid Selection" ),
1347  tr( "You have not selected a Smart Group. Kindly select a Smart Group to edit." ) );
1348  return;
1349  }
1350  QStandardItem* item = treeModel->itemFromIndex( present );
1351 
1352  QgsSmartGroupEditorDialog dlg( mStyle, this );
1353  QgsSmartConditionMap map = mStyle->smartgroup( present.data( Qt::UserRole + 1 ).toInt() );
1354  dlg.setSmartgroupName( item->text() );
1355  dlg.setOperator( mStyle->smartgroupOperator( item->data().toInt() ) );
1356  dlg.setConditionMap( map );
1357 
1358  if ( dlg.exec() == QDialog::Rejected )
1359  return;
1360 
1361  mStyle->remove( QgsStyleV2::SmartgroupEntity, item->data().toInt() );
1362  int id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
1363  if ( !id )
1364  {
1365  QMessageBox::critical( this, tr( "Database Error!" ),
1366  tr( "There was some error while editing the smart group." ) );
1367  return;
1368  }
1369  item->setText( dlg.smartgroupName() );
1370  item->setData( id );
1371 
1372  groupChanged( present );
1373 }
1374 
1375 bool QgsStyleV2ManagerDialog::eventFilter( QObject *obj, QEvent *event )
1376 {
1377 
1378  if (( obj == tagsLineEdit ) && ( event->type() == QEvent::FocusOut ) )
1379  {
1380  tagsChanged();
1381  return true;
1382  }
1383  return false;
1384 }
1385