QGIS API Documentation  2.12.0-Lyon
qgsstylev2exportimportdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsstylev2exportimportdialog.cpp
3  ---------------------
4  begin : Jan 2011
5  copyright : (C) 2011 by Alexander Bruy
6  email : alexander dot bruy at gmail dot com
7 
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
17 
18 #include "qgsstylev2.h"
19 #include "qgssymbolv2.h"
20 #include "qgssymbollayerv2utils.h"
21 #include "qgsvectorcolorrampv2.h"
22 #include "qgslogger.h"
23 
24 #include <QInputDialog>
25 #include <QCloseEvent>
26 #include <QFileDialog>
27 #include <QMessageBox>
28 #include <QPushButton>
29 #include <QStandardItemModel>
30 
32  : QDialog( parent )
33  , mDialogMode( mode )
34  , mQgisStyle( style )
35 {
36  setupUi( this );
37 
38  // additional buttons
39  QPushButton *pb;
40  pb = new QPushButton( tr( "Select all" ) );
41  buttonBox->addButton( pb, QDialogButtonBox::ActionRole );
42  connect( pb, SIGNAL( clicked() ), this, SLOT( selectAll() ) );
43 
44  pb = new QPushButton( tr( "Clear selection" ) );
45  buttonBox->addButton( pb, QDialogButtonBox::ActionRole );
46  connect( pb, SIGNAL( clicked() ), this, SLOT( clearSelection() ) );
47 
48  QStandardItemModel* model = new QStandardItemModel( listItems );
49  listItems->setModel( model );
50 
51  mTempStyle = new QgsStyleV2();
52  // TODO validate
53  mFileName = "";
54  mProgressDlg = NULL;
55  mTempFile = NULL;
56  mNetManager = new QNetworkAccessManager( this );
57  mNetReply = NULL;
58 
59  if ( mDialogMode == Import )
60  {
61  setWindowTitle( tr( "Import style(s)" ) );
62  // populate the import types
63  importTypeCombo->addItem( tr( "file specified below" ), QVariant( "file" ) );
64  // importTypeCombo->addItem( "official QGIS repo online", QVariant( "official" ) );
65  importTypeCombo->addItem( tr( "URL specified below" ), QVariant( "url" ) );
66  connect( importTypeCombo, SIGNAL( currentIndexChanged( int ) ), this, SLOT( importTypeChanged( int ) ) );
67 
68  QStringList groups = mQgisStyle->groupNames();
69  groupCombo->addItem( "imported", QVariant( "new" ) );
70  Q_FOREACH ( const QString& gName, groups )
71  {
72  groupCombo->addItem( gName );
73  }
74 
75  btnBrowse->setText( "Browse" );
76  connect( btnBrowse, SIGNAL( clicked() ), this, SLOT( browse() ) );
77 
78  label->setText( tr( "Select symbols to import" ) );
79  buttonBox->button( QDialogButtonBox::Ok )->setText( tr( "Import" ) );
80  }
81  else
82  {
83  setWindowTitle( tr( "Export style(s)" ) );
84  // hide import specific controls when exporting
85  btnBrowse->setHidden( true );
86  fromLabel->setHidden( true );
87  importTypeCombo->setHidden( true );
88  locationLabel->setHidden( true );
89  locationLineEdit->setHidden( true );
90  groupLabel->setHidden( true );
91  groupCombo->setHidden( true );
92 
93  buttonBox->button( QDialogButtonBox::Ok )->setText( tr( "Export" ) );
94  if ( !populateStyles( mQgisStyle ) )
95  {
96  QApplication::postEvent( this, new QCloseEvent() );
97  }
98  }
99 
100  // use Ok button for starting import and export operations
101  disconnect( buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
102  connect( buttonBox, SIGNAL( accepted() ), this, SLOT( doExportImport() ) );
103 }
104 
106 {
107  QModelIndexList selection = listItems->selectionModel()->selectedIndexes();
108  if ( selection.isEmpty() )
109  {
110  QMessageBox::warning( this, tr( "Export/import error" ),
111  tr( "You should select at least one symbol/color ramp." ) );
112  return;
113  }
114 
115  if ( mDialogMode == Export )
116  {
117  QString fileName = QFileDialog::getSaveFileName( this, tr( "Save styles" ), ".",
118  tr( "XML files (*.xml *.XML)" ) );
119  if ( fileName.isEmpty() )
120  {
121  return;
122  }
123 
124  // ensure the user never ommited the extension from the file name
125  if ( !fileName.toLower().endsWith( ".xml" ) )
126  {
127  fileName += ".xml";
128  }
129 
130  mFileName = fileName;
131 
132  moveStyles( &selection, mQgisStyle, mTempStyle );
133  if ( !mTempStyle->exportXML( mFileName ) )
134  {
135  QMessageBox::warning( this, tr( "Export/import error" ),
136  tr( "Error when saving selected symbols to file:\n%1" )
137  .arg( mTempStyle->errorString() ) );
138  return;
139  }
140  }
141  else // import
142  {
143  moveStyles( &selection, mTempStyle, mQgisStyle );
144 
145  // clear model
146  QStandardItemModel* model = qobject_cast<QStandardItemModel*>( listItems->model() );
147  model->clear();
148  accept();
149  }
150 
151  mFileName = "";
152  mTempStyle->clear();
153 }
154 
155 bool QgsStyleV2ExportImportDialog::populateStyles( QgsStyleV2* style )
156 {
157  // load symbols and color ramps from file
158  if ( mDialogMode == Import )
159  {
160  // NOTE mTempStyle is style here
161  if ( !style->importXML( mFileName ) )
162  {
163  QMessageBox::warning( this, tr( "Import error" ),
164  tr( "An error occured during import:\n%1" ).arg( style->errorString() ) );
165  return false;
166  }
167  }
168 
169  QStandardItemModel* model = qobject_cast<QStandardItemModel*>( listItems->model() );
170  model->clear();
171 
172  // populate symbols
173  QStringList styleNames = style->symbolNames();
174  QString name;
175 
176  for ( int i = 0; i < styleNames.count(); ++i )
177  {
178  name = styleNames[i];
179  QgsSymbolV2* symbol = style->symbol( name );
180  QStandardItem* item = new QStandardItem( name );
181  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( symbol, listItems->iconSize() );
182  item->setIcon( icon );
183  model->appendRow( item );
184  delete symbol;
185  }
186 
187  // and color ramps
188  styleNames = style->colorRampNames();
189 
190  for ( int i = 0; i < styleNames.count(); ++i )
191  {
192  name = styleNames[i];
193  QgsVectorColorRampV2* ramp = style->colorRamp( name );
194 
195  QStandardItem* item = new QStandardItem( name );
196  QIcon icon = QgsSymbolLayerV2Utils::colorRampPreviewIcon( ramp, listItems->iconSize() );
197  item->setIcon( icon );
198  model->appendRow( item );
199  delete ramp;
200  }
201  return true;
202 }
203 
204 void QgsStyleV2ExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyleV2* src, QgsStyleV2* dst )
205 {
206  QString symbolName;
207  QgsSymbolV2* symbol;
208  QgsVectorColorRampV2 *ramp = 0;
210  bool isSymbol = true;
211  bool prompt = true;
212  bool overwrite = true;
213  int groupid = 0;
214 
215  // get the groupid when going for import
216  if ( mDialogMode == Import )
217  {
218  int index = groupCombo->currentIndex();
219  QString name = groupCombo->itemText( index );
220  if ( name.isEmpty() )
221  {
222  // get name of the group
223  bool nameInvalid = true;
224  while ( nameInvalid )
225  {
226  bool ok;
227  name = QInputDialog::getText( this, tr( "Group Name" ),
228  tr( "Please enter a name for new group:" ),
229  QLineEdit::Normal,
230  tr( "imported" ),
231  &ok );
232  if ( !ok )
233  {
234  QMessageBox::warning( this, tr( "New Group" ),
235  tr( "New group cannot be created without a name. Kindly enter a name." ) );
236  continue;
237  }
238  // validate name
239  if ( name.isEmpty() )
240  {
241  QMessageBox::warning( this, tr( "New group" ),
242  tr( "Cannot create a group without name. Enter a name." ) );
243  }
244  else
245  {
246  // valid name
247  nameInvalid = false;
248  }
249  }
250  groupid = dst->addGroup( name );
251  }
252  else if ( dst->groupNames().contains( name ) )
253  {
254  groupid = dst->groupId( name );
255  }
256  else
257  {
258  groupid = dst->addGroup( name );
259  }
260  }
261 
262  for ( int i = 0; i < selection->size(); ++i )
263  {
264  index = selection->at( i );
265  symbolName = index.model()->data( index, 0 ).toString();
266  symbol = src->symbol( symbolName );
267  if ( symbol == NULL )
268  {
269  isSymbol = false;
270  ramp = src->colorRamp( symbolName );
271  }
272 
273  if ( isSymbol )
274  {
275  if ( dst->symbolNames().contains( symbolName ) && prompt )
276  {
277  int res = QMessageBox::warning( this, tr( "Duplicate names" ),
278  tr( "Symbol with name '%1' already exists.\nOverwrite?" )
279  .arg( symbolName ),
280  QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
281  switch ( res )
282  {
283  case QMessageBox::Cancel:
284  return;
285  case QMessageBox::No:
286  continue;
287  case QMessageBox::Yes:
288  dst->addSymbol( symbolName, symbol );
289  if ( mDialogMode == Import )
290  dst->saveSymbol( symbolName, symbol, groupid, QStringList() );
291  continue;
292  case QMessageBox::YesToAll:
293  prompt = false;
294  overwrite = true;
295  break;
296  case QMessageBox::NoToAll:
297  prompt = false;
298  overwrite = false;
299  break;
300  }
301  }
302 
303  if ( dst->symbolNames().contains( symbolName ) && overwrite )
304  {
305  dst->addSymbol( symbolName, symbol );
306  if ( mDialogMode == Import )
307  dst->saveSymbol( symbolName, symbol, groupid, QStringList() );
308  }
309  else if ( dst->symbolNames().contains( symbolName ) && !overwrite )
310  {
311  continue;
312  }
313  else
314  {
315  dst->addSymbol( symbolName, symbol );
316  if ( mDialogMode == Import )
317  dst->saveSymbol( symbolName, symbol, groupid, QStringList() );
318  }
319  }
320  else
321  {
322  if ( dst->colorRampNames().contains( symbolName ) && prompt )
323  {
324  int res = QMessageBox::warning( this, tr( "Duplicate names" ),
325  tr( "Color ramp with name '%1' already exists.\nOverwrite?" )
326  .arg( symbolName ),
327  QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
328  switch ( res )
329  {
330  case QMessageBox::Cancel:
331  return;
332  case QMessageBox::No:
333  continue;
334  case QMessageBox::Yes:
335  dst->addColorRamp( symbolName, ramp );
336  if ( mDialogMode == Import )
337  dst->saveColorRamp( symbolName, ramp, groupid, QStringList() );
338  continue;
339  case QMessageBox::YesToAll:
340  prompt = false;
341  overwrite = true;
342  break;
343  case QMessageBox::NoToAll: prompt = false;
344  overwrite = false;
345  break;
346  }
347  }
348 
349  if ( dst->colorRampNames().contains( symbolName ) && overwrite )
350  {
351  dst->addColorRamp( symbolName, ramp );
352  if ( mDialogMode == Import )
353  dst->saveColorRamp( symbolName, ramp, groupid, QStringList() );
354  }
355  else if ( dst->colorRampNames().contains( symbolName ) && !overwrite )
356  {
357  continue;
358  }
359  else
360  {
361  dst->addColorRamp( symbolName, ramp );
362  if ( mDialogMode == Import )
363  dst->saveColorRamp( symbolName, ramp, groupid, QStringList() );
364  }
365  }
366  }
367 }
368 
370 {
371  delete mTempFile;
372  delete mTempStyle;
373 }
374 
376 {
377  listItems->selectAll();
378 }
379 
381 {
382  listItems->clearSelection();
383 }
384 
386 {
387  QString type = importTypeCombo->itemData( index ).toString();
388 
389  locationLineEdit->setText( "" );
390 
391  if ( type == "file" )
392  {
393  locationLineEdit->setEnabled( true );
394  btnBrowse->setText( "Browse" );
395  }
396  else if ( type == "official" )
397  {
398  btnBrowse->setText( "Fetch Symbols" );
399  locationLineEdit->setEnabled( false );
400  }
401  else
402  {
403  btnBrowse->setText( "Fetch Symbols" );
404  locationLineEdit->setEnabled( true );
405  }
406 }
407 
409 {
410  QString type = importTypeCombo->itemData( importTypeCombo->currentIndex() ).toString();
411 
412  if ( type == "file" )
413  {
414  mFileName = QFileDialog::getOpenFileName( this, tr( "Load styles" ), ".",
415  tr( "XML files (*.xml *XML)" ) );
416  if ( mFileName.isEmpty() )
417  {
418  return;
419  }
420  QFileInfo pathInfo( mFileName );
421  QString groupName = pathInfo.fileName().replace( ".xml", "" );
422  groupCombo->setItemText( 0, groupName );
423  locationLineEdit->setText( mFileName );
424  populateStyles( mTempStyle );
425  }
426  else if ( type == "official" )
427  {
428  // TODO set URL
429  // downloadStyleXML( QUrl( "http://...." ) );
430  }
431  else
432  {
433  downloadStyleXML( QUrl( locationLineEdit->text() ) );
434  }
435 }
436 
437 void QgsStyleV2ExportImportDialog::downloadStyleXML( const QUrl& url )
438 {
439  // XXX Try to move this code to some core Network interface,
440  // HTTP downloading is a generic functionality that might be used elsewhere
441 
442  mTempFile = new QTemporaryFile();
443  if ( mTempFile->open() )
444  {
445  mFileName = mTempFile->fileName();
446 
447  if ( mProgressDlg )
448  {
449  QProgressDialog *dummy = mProgressDlg;
450  mProgressDlg = NULL;
451  delete dummy;
452  }
453  mProgressDlg = new QProgressDialog();
454  mProgressDlg->setLabelText( tr( "Downloading style ... " ) );
455  mProgressDlg->setAutoClose( true );
456 
457  connect( mProgressDlg, SIGNAL( canceled() ), this, SLOT( downloadCanceled() ) );
458 
459  // open the network connection and connect the respective slots
460  if ( mNetReply )
461  {
462  QNetworkReply *dummyReply = mNetReply;
463  mNetReply = NULL;
464  delete dummyReply;
465  }
466  mNetReply = mNetManager->get( QNetworkRequest( url ) );
467 
468  connect( mNetReply, SIGNAL( finished() ), this, SLOT( httpFinished() ) );
469  connect( mNetReply, SIGNAL( readyRead() ), this, SLOT( fileReadyRead() ) );
470  connect( mNetReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( updateProgress( qint64, qint64 ) ) );
471  }
472 }
473 
474 void QgsStyleV2ExportImportDialog::httpFinished()
475 {
476  if ( mNetReply->error() )
477  {
478  mTempFile->remove();
479  mFileName = "";
480  mProgressDlg->hide();
481  QMessageBox::information( this, tr( "HTTP Error!" ),
482  tr( "Download failed: %1." ).arg( mNetReply->errorString() ) );
483  return;
484  }
485  else
486  {
487  mTempFile->flush();
488  mTempFile->close();
489  populateStyles( mTempStyle );
490  }
491 }
492 
493 void QgsStyleV2ExportImportDialog::fileReadyRead()
494 {
495  mTempFile->write( mNetReply->readAll() );
496 }
497 
498 void QgsStyleV2ExportImportDialog::updateProgress( qint64 bytesRead, qint64 bytesTotal )
499 {
500  mProgressDlg->setMaximum( bytesTotal );
501  mProgressDlg->setValue( bytesRead );
502 }
503 
504 void QgsStyleV2ExportImportDialog::downloadCanceled()
505 {
506  mNetReply->abort();
507  mTempFile->remove();
508  mFileName = "";
509 }
QgsStyleV2ExportImportDialog(QgsStyleV2 *style, QWidget *parent=NULL, Mode mode=Export)
bool flush()
static unsigned index
bool saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, int groupid, const QStringList &tags)
add the colorramp to the DB
Definition: qgsstylev2.cpp:212
QgsVectorColorRampV2 * colorRamp(const QString &name)
return a NEW copy of color ramp
Definition: qgsstylev2.cpp:260
void setupUi(QWidget *widget)
void setIcon(const QIcon &icon)
static QIcon colorRampPreviewIcon(QgsVectorColorRampV2 *ramp, QSize size)
bool importXML(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file...
void setMaximum(int maximum)
bool addSymbol(const QString &name, QgsSymbolV2 *symbol, bool update=false)
add symbol to style. takes symbol's ownership
Definition: qgsstylev2.cpp:83
bool remove()
QString errorString() const
int groupId(const QString &group)
return the DB id for the given group name
Definition: qgsstylev2.cpp:962
void setLabelText(const QString &text)
bool contains(const QString &str, Qt::CaseSensitivity cs) const
bool exportXML(const QString &filename)
Exports the style as a XML file.
void accepted()
bool addColorRamp(const QString &name, QgsVectorColorRampV2 *colorRamp, bool update=false)
add color ramp to style. takes ramp's ownership
Definition: qgsstylev2.cpp:188
const QPixmap * icon() const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QStringList colorRampNames()
return a list of names of color ramps
Definition: qgsstylev2.cpp:276
QString tr(const char *sourceText, const char *disambiguation, int n)
StandardButton information(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
void finished(int result)
const char * name() const
void setValue(int progress)
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
int count(const T &value) const
bool saveSymbol(const QString &name, QgsSymbolV2 *symbol, int groupid, const QStringList &tags)
add the symbol to the DB with the tags
Definition: qgsstylev2.cpp:107
QString fileName() const
bool isEmpty() const
QByteArray readAll()
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
void setAutoClose(bool close)
void hide()
QStringList symbolNames()
return a list of names of symbols
Definition: qgsstylev2.cpp:182
virtual void accept()
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, QFlags< Qt::WindowType > flags, QFlags< Qt::InputMethodHint > inputMethodHints)
QString toLower() const
virtual void abort()=0
QgsSymbolV2 * symbol(const QString &name)
return a NEW copy of symbol
Definition: qgsstylev2.cpp:166
virtual void close()
QString fileName() const
QString & replace(int position, int n, QChar after)
void clear()
remove all contents of the style
Definition: qgsstylev2.cpp:70
void setWindowTitle(const QString &)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFlags< QFileDialog::Option > options)
qint64 write(const char *data, qint64 maxSize)
NetworkError error() const
StandardButton warning(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
QNetworkReply * get(const QNetworkRequest &request)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFlags< QFileDialog::Option > options)
void postEvent(QObject *receiver, QEvent *event)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QChar * data()
QString errorString()
return last error from load/save operation
Definition: qgsstylev2.h:274
void appendRow(const QList< QStandardItem * > &items)
QStringList groupNames()
return the all the groups in the style
Definition: qgsstylev2.cpp:466
int addGroup(const QString &groupName, int parent=0)
adds a new group and returns the group's id
Definition: qgsstylev2.cpp:613