QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgssublayersdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssublayersdialog.cpp - dialog for selecting sublayers
3  ---------------------
4  begin : January 2009
5  copyright : (C) 2009 by Florian El Ahdab
6  email : felahdab 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 
16 #include "qgssublayersdialog.h"
17 #include "qgslogger.h"
18 #include "qgssettings.h"
19 #include "qgsgui.h"
20 #include "qgsproviderregistry.h"
21 
22 #include <QTableWidgetItem>
23 #include <QPushButton>
24 
26 class SubLayerItem : public QTreeWidgetItem
27 {
28  public:
29  SubLayerItem( const QStringList &strings, int type = QTreeWidgetItem::Type )
30  : QTreeWidgetItem( strings, type )
31  {}
32 
33  bool operator <( const QTreeWidgetItem &other ) const override
34  {
35  QgsSublayersDialog *d = qobject_cast<QgsSublayersDialog *>( treeWidget()->parent() );
36  int col = treeWidget()->sortColumn();
37 
38  if ( col == 0 || ( col > 0 && d->countColumn() == col ) )
39  return text( col ).toInt() < other.text( col ).toInt();
40  else
41  return text( col ) < other.text( col );
42  }
43 };
45 
47  const QString &name,
48  QWidget *parent,
49  Qt::WindowFlags fl,
50  const QString &dataSourceUri )
51  : QDialog( parent, fl )
52  , mName( name )
53 {
54  setupUi( this );
56 
57  QString title;
58  switch ( providerType )
59  {
61  title = tr( "Select Vector Layers to Add…" );
62  layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" )
63  << tr( "Number of features" ) << tr( "Geometry type" ) << tr( "Description" ) );
64  mShowCount = true;
65  mShowType = true;
66  mShowDescription = true;
67  break;
69  title = tr( "Select Raster Layers to Add…" );
70  layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" ) );
71  break;
73  title = tr( "Select Mesh Layers to Add…" );
74  layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Mesh name" ) );
75  break;
76  default:
77  title = tr( "Select Layers to Add…" );
78  layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" )
79  << tr( "Type" ) );
80  mShowType = true;
81  }
82 
83  const QVariantMap dataSourceUriParsed = QgsProviderRegistry::instance()->decodeUri( name, dataSourceUri );
84  const QString dataSourceFilePath = dataSourceUriParsed.value( QStringLiteral( "path" ) ).toString();
85  const QString filePath = dataSourceFilePath.isEmpty() ? dataSourceUri : dataSourceFilePath;
86  const QString fileName = QFileInfo( filePath ).fileName();
87 
88  setWindowTitle( fileName.isEmpty() ? title : QStringLiteral( "%1 | %2" ).arg( title, fileName ) );
89  mLblFilePath->setText( QDir::toNativeSeparators( QFileInfo( filePath ).canonicalFilePath() ) );
90  mLblFilePath->setVisible( ! fileName.isEmpty() );
91 
92  // add a "Select All" button - would be nicer with an icon
93  connect( mBtnSelectAll, &QAbstractButton::pressed, layersTable, &QTreeView::selectAll );
94  connect( mBtnDeselectAll, &QAbstractButton::pressed, this, &QgsSublayersDialog::mBtnDeselectAll_pressed );
95  connect( layersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsSublayersDialog::layersTable_selectionChanged );
96 
97  mCbxAddToGroup->setVisible( false );
98 }
99 
101 {
102  QgsSettings settings;
103  settings.setValue( "/Windows/" + mName + "SubLayers/headerColumnCount",
104  layersTable->columnCount() );
105  settings.setValue( "/Windows/" + mName + "SubLayers/headerState",
106  layersTable->header()->saveState() );
107 }
108 
109 static bool _isLayerIdUnique( int layerId, QTreeWidget *layersTable )
110 {
111  int count = 0;
112  for ( int j = 0; j < layersTable->topLevelItemCount(); j++ )
113  {
114  if ( layersTable->topLevelItem( j )->text( 0 ).toInt() == layerId )
115  {
116  count++;
117  }
118  }
119  return count == 1;
120 }
121 
123 {
124  LayerDefinitionList list;
125  for ( int i = 0; i < layersTable->selectedItems().size(); i++ )
126  {
127  QTreeWidgetItem *item = layersTable->selectedItems().at( i );
128 
129  LayerDefinition def;
130  def.layerId = item->text( 0 ).toInt();
131  def.layerName = item->text( 1 );
132  if ( mShowType )
133  {
134  // If there are more sub layers of the same name (virtual for geometry types),
135  // add geometry type
136  if ( !_isLayerIdUnique( def.layerId, layersTable ) )
137  def.type = item->text( mShowCount ? 3 : 2 );
138  }
139 
140  list << def;
141  }
142  return list;
143 }
144 
145 
147 {
148  const auto constList = list;
149  for ( const LayerDefinition &item : constList )
150  {
151  QStringList elements;
152  elements << QString::number( item.layerId ) << item.layerName;
153  if ( mShowCount )
154  elements << ( item.count == -1 ? tr( "Unknown" ) : QString::number( item.count ) );
155  if ( mShowType )
156  elements << item.type;
157  if ( mShowDescription )
158  elements << item.description;
159  layersTable->addTopLevelItem( new SubLayerItem( elements ) );
160  }
161 
162  // resize columns
163  QgsSettings settings;
164  QByteArray ba = settings.value( "/Windows/" + mName + "SubLayers/headerState" ).toByteArray();
165  int savedColumnCount = settings.value( "/Windows/" + mName + "SubLayers/headerColumnCount" ).toInt();
166  if ( ! ba.isNull() && savedColumnCount == layersTable->columnCount() )
167  {
168  layersTable->header()->restoreState( ba );
169  }
170  else
171  {
172  for ( int i = 0; i < layersTable->columnCount(); i++ )
173  layersTable->resizeColumnToContents( i );
174  layersTable->setColumnWidth( 1, layersTable->columnWidth( 1 ) + 10 );
175  }
176 }
177 
178 // override exec() instead of using showEvent()
179 // because in some case we don't want the dialog to appear (depending on user settings)
180 // TODO alert the user when dialog is not opened
182 {
183  QgsSettings settings;
184  PromptMode promptLayers = settings.enumValue( QStringLiteral( "qgis/promptForSublayers" ), PromptAlways );
185 
186  // make sure three are sublayers to choose
187  if ( layersTable->topLevelItemCount() == 0 )
188  return QDialog::Rejected;
189 
190  layersTable->selectAll();
191 
192  // check promptForSublayers settings - perhaps this should be in QgsDataSource instead?
193  if ( promptLayers == PromptNever )
194  return QDialog::Rejected;
195  else if ( promptLayers == PromptLoadAll )
196  return QDialog::Accepted;
197 
198  // if there is only 1 sublayer (probably the main layer), just select that one and return
199  if ( layersTable->topLevelItemCount() == 1 )
200  return QDialog::Accepted;
201 
202  layersTable->sortByColumn( 1, Qt::AscendingOrder );
203  layersTable->setSortingEnabled( true );
204 
205  // if we got here, disable override cursor, open dialog and return result
206  // TODO add override cursor where it is missing (e.g. when opening via "Add Raster")
207  QCursor cursor;
208  bool overrideCursor = nullptr != QApplication::overrideCursor();
209  if ( overrideCursor )
210  {
211  cursor = QCursor( * QApplication::overrideCursor() );
212  QApplication::restoreOverrideCursor();
213  }
214 
215  // Checkbox about adding sublayers to a group
216  if ( mShowAddToGroupCheckbox )
217  {
218  mCbxAddToGroup->setVisible( true );
219  bool addToGroup = settings.value( QStringLiteral( "/qgis/openSublayersInGroup" ), false ).toBool();
220  mCbxAddToGroup->setChecked( addToGroup );
221  }
222 
223  int ret = QDialog::exec();
224  if ( overrideCursor )
225  QApplication::setOverrideCursor( cursor );
226 
227  if ( mShowAddToGroupCheckbox )
228  settings.setValue( QStringLiteral( "/qgis/openSublayersInGroup" ), mCbxAddToGroup->isChecked() );
229  return ret;
230 }
231 
232 void QgsSublayersDialog::layersTable_selectionChanged( const QItemSelection &, const QItemSelection & )
233 {
234  buttonBox->button( QDialogButtonBox::Ok )->setEnabled( layersTable->selectedItems().length() > 0 );
235 }
236 
237 void QgsSublayersDialog::mBtnDeselectAll_pressed()
238 {
239  layersTable->selectionModel()->clear();
240 }
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:156
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
bool mShowType
Whether to show type in the table.
QList< QgsSublayersDialog::LayerDefinition > LayerDefinitionList
List of layer definitions for the purpose of this dialog.
PromptMode
Prompt behavior of the QgsSublayersDialog.
@ PromptNever
never prompt, will not load anything
@ PromptAlways
always ask if there are existing sublayers
@ PromptLoadAll
never prompt, but load all sublayers
QgsSublayersDialog(ProviderType providerType, const QString &name, QWidget *parent=nullptr, Qt::WindowFlags fl=Qt::WindowFlags(), const QString &dataSourceUri=QString())
Constructor for QgsSublayersDialog.
LayerDefinitionList selection()
Returns list of selected layers.
bool mShowCount
Whether to show number of features in the table.
bool mShowDescription
Whether to show description in the table.
void populateLayerTable(const LayerDefinitionList &list)
Populate the table with layers.
int countColumn() const
Returns column with count or -1.
QString mName
Provider type name.
bool operator<(const QVariant &v1, const QVariant &v2)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.h:895
A structure that defines layers for the purpose of this dialog.
QString layerName
Name of the layer (not necessarily unique)
QString type
Extra type depending on the use (e.g. geometry type for vector sublayers)
int layerId
Identifier of the layer (one unique layer id may have multiple types though)