QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsbrowserwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsbrowserwidget.cpp
3 ---------------------
4 begin : July 2011
5 copyright : (C) 2011 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#include "qgsbrowserwidget.h"
16
18#include "qgsbrowserguimodel.h"
20#include "qgsbrowsertreeview.h"
23#include "qgsdirectoryitem.h"
24#include "qgsgui.h"
25#include "qgslayeritem.h"
26#include "qgslogger.h"
27#include "qgsnative.h"
28#include "qgsnewnamedialog.h"
29#include "qgsproject.h"
30#include "qgsprojectitem.h"
31#include "qgsrasterlayer.h"
32#include "qgssettings.h"
33#include "qgsvectorlayer.h"
35
36#include <QAbstractTextDocumentLayout>
37#include <QActionGroup>
38#include <QFileDialog>
39#include <QHeaderView>
40#include <QMenu>
41#include <QPlainTextDocumentLayout>
42#include <QSortFilterProxyModel>
43#include <QToolButton>
44#include <QTreeView>
45
46#include "moc_qgsbrowserwidget.cpp"
47
48// browser layer properties dialog
49#include "qgsapplication.h"
50#include "qgsmapcanvas.h"
51
52#include <QDragEnterEvent>
53#include <functional>
54
56 : QgsPanelWidget( parent )
57 , mModel( browserModel )
58{
59 setupUi( this );
60
61 layout()->setContentsMargins( 0, 0, 0, 0 );
62 qgis::down_cast<QVBoxLayout *>( layout() )->setSpacing( 0 );
63
64 mBrowserView = new QgsDockBrowserTreeView( this );
65 mLayoutBrowser->addWidget( mBrowserView );
66
67 mWidgetFilter->hide();
68 mLeFilter->setPlaceholderText( tr( "Type here to filter visible items…" ) );
69 // icons from http://www.fatcow.com/free-icons License: CC Attribution 3.0
70
71 QMenu *menu = new QMenu( this );
72 menu->setSeparatorsCollapsible( false );
73 mBtnFilterOptions->setMenu( menu );
74 QAction *action = new QAction( tr( "Case Sensitive" ), menu );
75 action->setData( "case" );
76 action->setCheckable( true );
77 action->setChecked( false );
78 connect( action, &QAction::toggled, this, &QgsBrowserWidget::setCaseSensitive );
79 menu->addAction( action );
80 QActionGroup *group = new QActionGroup( menu );
81 action = new QAction( tr( "Filter Pattern Syntax" ), group );
82 action->setSeparator( true );
83 menu->addAction( action );
84 action = new QAction( tr( "Normal" ), group );
85 action->setData( QgsBrowserProxyModel::Normal );
86 action->setCheckable( true );
87 action->setChecked( true );
88 menu->addAction( action );
89 action = new QAction( tr( "Wildcard(s)" ), group );
90 action->setData( QgsBrowserProxyModel::Wildcards );
91 action->setCheckable( true );
92 menu->addAction( action );
93 action = new QAction( tr( "Regular Expression" ), group );
95 action->setCheckable( true );
96 menu->addAction( action );
97
98 mBrowserView->setExpandsOnDoubleClick( false );
99
100 connect( mActionRefresh, &QAction::triggered, this, &QgsBrowserWidget::refresh );
101 connect( mActionAddLayers, &QAction::triggered, this, &QgsBrowserWidget::addSelectedLayers );
102 connect( mActionCollapse, &QAction::triggered, mBrowserView, &QgsDockBrowserTreeView::collapseAll );
103 connect( mActionShowFilter, &QAction::triggered, this, &QgsBrowserWidget::showFilterWidget );
104 connect( mActionPropertiesWidget, &QAction::triggered, this, &QgsBrowserWidget::propertiesWidgetToggled );
105 connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsBrowserWidget::setFilter );
106 connect( mLeFilter, &QgsFilterLineEdit::cleared, this, &QgsBrowserWidget::setFilter );
107 connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsBrowserWidget::setFilter );
108 connect( group, &QActionGroup::triggered, this, &QgsBrowserWidget::setFilterSyntax );
109 connect( mBrowserView, &QgsDockBrowserTreeView::customContextMenuRequested, this, &QgsBrowserWidget::showContextMenu );
110 connect( mBrowserView, &QgsDockBrowserTreeView::doubleClicked, this, &QgsBrowserWidget::itemDoubleClicked );
111 connect( mSplitter, &QSplitter::splitterMoved, this, &QgsBrowserWidget::splitterMoved );
112
113 connect( QgsGui::instance(), &QgsGui::optionsChanged, this, &QgsBrowserWidget::onOptionsChanged );
114}
115
117
118void QgsBrowserWidget::showEvent( QShowEvent *e )
119{
120 // delayed initialization of the model
121 if ( !mModel->initialized() )
122 {
123 mModel->initialize();
124 }
125 if ( !mProxyModel )
126 {
127 mProxyModel = new QgsBrowserProxyModel( this );
128 mProxyModel->setBrowserModel( mModel );
129 mProxyModel->setHiddenDataItemProviderKeyFilter( mDisabledDataItemsKeys );
130 mBrowserView->setSettingsSection( objectName().toLower() ); // to distinguish 2 or more instances of the browser
131 mBrowserView->setBrowserModel( mModel );
132 mBrowserView->setModel( mProxyModel );
133 mBrowserView->setSortingEnabled( true );
134 mBrowserView->sortByColumn( 0, Qt::AscendingOrder );
135 // provide a horizontal scroll bar instead of using ellipse (...) for longer items
136 mBrowserView->setTextElideMode( Qt::ElideNone );
137 mBrowserView->header()->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
138 mBrowserView->header()->setStretchLastSection( false );
139
140 // selectionModel is created when model is set on tree
141 connect( mBrowserView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsBrowserWidget::selectionChanged );
142
143 // Forward the model changed signals to the widget
145
146 // objectName used by settingsSection() is not yet set in constructor
147 QgsSettings settings;
148 mActionPropertiesWidget->setChecked( settings.value( settingsSection() + "/propertiesWidgetEnabled", false ).toBool() );
149 mPropertiesWidget->setVisible( false ); // false until item is selected
150
151 mSplitter->restoreState( settings.value( QStringLiteral( "%1/splitterState" ).arg( settingsSection() ) ).toByteArray() );
152 }
153
154 QWidget::showEvent( e );
155}
156
157void QgsBrowserWidget::itemDoubleClicked( const QModelIndex &index )
158{
159 QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( index ) );
160 if ( !item )
161 return;
162
163 QgsDataItemGuiContext context = createContext();
164
165 const QList<QgsDataItemGuiProvider *> providers = QgsGui::dataItemGuiProviderRegistry()->providers();
166 for ( QgsDataItemGuiProvider *provider : providers )
167 {
168 if ( provider->handleDoubleClick( item, context ) )
169 return;
170 }
171
172 // if no providers overrode the double-click handling for this item, we give the item itself a chance
173 if ( !item->handleDoubleClick() )
174 {
175 // double-click not handled by browser model, so use as default view expand behavior
176 if ( mBrowserView->isExpanded( index ) )
177 mBrowserView->collapse( index );
178 else
179 mBrowserView->expand( index );
180 }
181}
182
183void QgsBrowserWidget::onOptionsChanged()
184{
185 std::function<void( const QModelIndex &index )> updateItem;
186 updateItem = [this, &updateItem]( const QModelIndex &index ) {
187 if ( QgsDirectoryItem *dirItem = qobject_cast<QgsDirectoryItem *>( mModel->dataItem( index ) ) )
188 {
189 dirItem->reevaluateMonitoring();
190 }
191
192 const int rowCount = mModel->rowCount( index );
193 for ( int i = 0; i < rowCount; ++i )
194 {
195 const QModelIndex child = mModel->index( i, 0, index );
196 updateItem( child );
197 }
198 };
199
200 for ( int i = 0; i < mModel->rowCount(); ++i )
201 {
202 updateItem( mModel->index( i, 0 ) );
203 }
204}
205
206void QgsBrowserWidget::showContextMenu( QPoint pt )
207{
208 QModelIndex index = mProxyModel->mapToSource( mBrowserView->indexAt( pt ) );
209 QgsDataItem *item = mModel->dataItem( index );
210 if ( !item )
211 return;
212
213 const QModelIndexList selection = mBrowserView->selectionModel()->selectedIndexes();
214 QList<QgsDataItem *> selectedItems;
215 selectedItems.reserve( selection.size() );
216 for ( const QModelIndex &selectedIndex : selection )
217 {
218 QgsDataItem *selectedItem = mProxyModel->dataItem( selectedIndex );
219 if ( selectedItem )
220 selectedItems << selectedItem;
221 }
222
223 QMenu *menu = new QMenu( this );
224
225 const QList<QMenu *> menus = item->menus( menu );
226 QList<QAction *> actions = item->actions( menu );
227
228 if ( !menus.isEmpty() )
229 {
230 for ( QMenu *mn : menus )
231 {
232 menu->addMenu( mn );
233 }
234 }
235
236 if ( !actions.isEmpty() )
237 {
238 if ( !menu->actions().isEmpty() )
239 menu->addSeparator();
240 // add action to the menu
241 menu->addActions( actions );
242 }
243
244 QgsDataItemGuiContext context = createContext();
245
246 QList<QgsDataItemGuiProvider *> providers = QgsGui::dataItemGuiProviderRegistry()->providers();
247 std::sort( providers.begin(), providers.end(), []( QgsDataItemGuiProvider *a, QgsDataItemGuiProvider *b ) {
248 return a->precedenceWhenPopulatingMenus() < b->precedenceWhenPopulatingMenus();
249 } );
250 for ( QgsDataItemGuiProvider *provider : std::as_const( providers ) )
251 {
252 provider->populateContextMenu( item, menu, selectedItems, context );
253 }
254
255 if ( menu->actions().isEmpty() )
256 {
257 delete menu;
258 return;
259 }
260
261 menu->popup( mBrowserView->mapToGlobal( pt ) );
262}
263
265{
266 mMessageBar = bar;
267 mModel->setMessageBar( bar );
268}
269
271{
272 return mMessageBar;
273}
274
276{
277 mMapCanvas = canvas;
278 mModel->setMapCanvas( canvas );
279}
280
282{
283 return mMapCanvas;
284}
285
286void QgsBrowserWidget::setDisabledDataItemsKeys( const QStringList &filter )
287{
288 mDisabledDataItemsKeys = filter;
289
290 if ( !mProxyModel )
291 return;
292
293 mProxyModel->setHiddenDataItemProviderKeyFilter( mDisabledDataItemsKeys );
294}
295
297{
298 refreshModel( QModelIndex() );
299}
300
301void QgsBrowserWidget::refreshModel( const QModelIndex &index )
302{
303 if ( mModel && mProxyModel )
304 {
305 QgsDataItem *item = mModel->dataItem( index );
306 if ( item )
307 {
308 QgsDebugMsgLevel( "path = " + item->path(), 4 );
309 }
310 else
311 {
312 QgsDebugMsgLevel( QStringLiteral( "invalid item" ), 4 );
313 }
314
315 if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
316 {
317 mModel->refresh( index );
318 }
319
320 for ( int i = 0; i < mModel->rowCount( index ); i++ )
321 {
322 QModelIndex idx = mModel->index( i, 0, index );
323 QModelIndex proxyIdx = mProxyModel->mapFromSource( idx );
324 QgsDataItem *child = mModel->dataItem( idx );
325
326 // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
327 // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
328 if ( mBrowserView->isExpanded( proxyIdx ) || mBrowserView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
329 {
330 refreshModel( idx );
331 }
332 else
333 {
334 if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
335 {
336 child->depopulate();
337 }
338 }
339 }
340 }
341}
342
343void QgsBrowserWidget::addLayer( QgsLayerItem *layerItem )
344{
345 if ( !layerItem )
346 return;
347
348 emit handleDropUriList( layerItem->mimeUris() );
349}
350
351void QgsBrowserWidget::addSelectedLayers()
352{
353 QApplication::setOverrideCursor( Qt::WaitCursor );
354
355 // get a sorted list of selected indexes
356 QModelIndexList list = mBrowserView->selectionModel()->selectedIndexes();
357 std::sort( list.begin(), list.end() );
358
359 // If any of the layer items are QGIS we just open and exit the loop
360 const auto constList = list;
361 for ( const QModelIndex &index : constList )
362 {
363 QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( index ) );
364 if ( item && item->type() == Qgis::BrowserItemType::Project )
365 {
366 QgsProjectItem *projectItem = qobject_cast<QgsProjectItem *>( item );
367 if ( projectItem )
368 emit openFile( projectItem->path(), QStringLiteral( "project" ) );
369
370 QApplication::restoreOverrideCursor();
371 return;
372 }
373 }
374
375 // add items in reverse order so they are in correct order in the layers dock
376 for ( int i = list.size() - 1; i >= 0; i-- )
377 {
378 QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( list[i] ) );
379 if ( item && item->type() == Qgis::BrowserItemType::Layer )
380 {
381 QgsLayerItem *layerItem = qobject_cast<QgsLayerItem *>( item );
382 if ( layerItem )
383 addLayer( layerItem );
384 }
385 }
386
387 QApplication::restoreOverrideCursor();
388}
389
390void QgsBrowserWidget::hideItem()
391{
392 QModelIndex index = mProxyModel->mapToSource( mBrowserView->currentIndex() );
393 QgsDataItem *item = mModel->dataItem( index );
394 if ( !item )
395 return;
396
397 if ( item->type() == Qgis::BrowserItemType::Directory )
398 {
399 mModel->hidePath( item );
400 }
401}
402
403void QgsBrowserWidget::showProperties()
404{
405 QModelIndex index = mProxyModel->mapToSource( mBrowserView->currentIndex() );
406 QgsDataItem *item = mModel->dataItem( index );
407 if ( !item )
408 return;
409
411 {
412 QgsBrowserPropertiesDialog *dialog = new QgsBrowserPropertiesDialog( settingsSection(), this );
413 dialog->setAttribute( Qt::WA_DeleteOnClose );
414 dialog->setItem( item, createContext() );
415 dialog->show();
416 }
417}
418
419void QgsBrowserWidget::showFilterWidget( bool visible )
420{
421 mWidgetFilter->setVisible( visible );
422 if ( !visible )
423 {
424 mLeFilter->setText( QString() );
425 setFilter();
426 }
427 else
428 {
429 mLeFilter->setFocus();
430 }
431}
432
433void QgsBrowserWidget::setFilter()
434{
435 QString filter = mLeFilter->text();
436 if ( mProxyModel )
437 mProxyModel->setFilterString( filter );
438}
439
441{
442 if ( mModel )
443 mModel->updateProjectHome();
444}
445
446void QgsBrowserWidget::setFilterSyntax( QAction *action )
447{
448 if ( !action || !mProxyModel )
449 return;
450
451 mProxyModel->setFilterSyntax( static_cast<QgsBrowserProxyModel::FilterSyntax>( action->data().toInt() ) );
452}
453
454void QgsBrowserWidget::setCaseSensitive( bool caseSensitive )
455{
456 if ( !mProxyModel )
457 return;
458 mProxyModel->setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
459}
460
461int QgsBrowserWidget::selectedItemsCount()
462{
463 QItemSelectionModel *selectionModel = mBrowserView->selectionModel();
464 if ( selectionModel )
465 {
466 return selectionModel->selectedIndexes().size();
467 }
468 return 0;
469}
470
471QgsDataItemGuiContext QgsBrowserWidget::createContext()
472{
473 QgsDataItemGuiContext context;
474 context.setMessageBar( mMessageBar );
475 context.setMapCanvas( mMapCanvas );
476 context.setView( mBrowserView );
477 return context;
478}
479
480void QgsBrowserWidget::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
481{
482 Q_UNUSED( selected )
483 Q_UNUSED( deselected )
484 if ( mActionPropertiesWidget->isChecked() )
485 {
486 setPropertiesWidget();
487 }
488}
489
490void QgsBrowserWidget::clearPropertiesWidget()
491{
492 while ( mPropertiesLayout->count() > 0 )
493 {
494 delete mPropertiesLayout->itemAt( 0 )->widget();
495 }
496 mPropertiesWidget->setVisible( false );
497}
498
499void QgsBrowserWidget::setPropertiesWidget()
500{
501 clearPropertiesWidget();
502 QItemSelectionModel *selectionModel = mBrowserView->selectionModel();
503 if ( selectionModel )
504 {
505 QModelIndexList indexes = selectionModel->selectedIndexes();
506 if ( indexes.size() == 1 )
507 {
508 QModelIndex index = mProxyModel->mapToSource( indexes.value( 0 ) );
509 QgsDataItem *item = mModel->dataItem( index );
510 QgsDataItemGuiContext context = createContext();
511 QgsBrowserPropertiesWidget *propertiesWidget = QgsBrowserPropertiesWidget::createWidget( item, context, mPropertiesWidget );
512 if ( propertiesWidget )
513 {
514 propertiesWidget->setCondensedMode( true );
515 mPropertiesLayout->addWidget( propertiesWidget );
516 }
517 }
518 }
519 mPropertiesWidget->setVisible( mPropertiesLayout->count() > 0 );
520}
521
522void QgsBrowserWidget::enablePropertiesWidget( bool enable )
523{
524 mActionPropertiesWidget->setChecked( enable );
525 propertiesWidgetToggled( enable );
526}
527
528void QgsBrowserWidget::propertiesWidgetToggled( bool enabled )
529{
530 if ( enabled && selectedItemsCount() == 1 )
531 {
532 setPropertiesWidget();
533 }
534 else
535 {
536 clearPropertiesWidget();
537 }
538
539 QgsSettings settings;
540 settings.setValue( settingsSection() + "/propertiesWidgetEnabled", enabled );
541}
542
543void QgsBrowserWidget::setActiveIndex( const QModelIndex &index )
544{
545 if ( index.isValid() )
546 {
547 QModelIndex proxyIndex = mProxyModel->mapFromSource( index );
548 mBrowserView->expand( proxyIndex );
549 mBrowserView->setCurrentIndex( proxyIndex );
550 }
551}
552
553void QgsBrowserWidget::splitterMoved()
554{
555 QgsSettings settings;
556 settings.setValue( QStringLiteral( "%1/splitterState" ).arg( settingsSection() ), mSplitter->saveState() );
557}
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
Definition qgis.h:954
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
Definition qgis.h:955
@ Layer
Represents a map layer.
Definition qgis.h:922
@ Project
Represents a QGIS project.
Definition qgis.h:925
@ Directory
Represents a file directory.
Definition qgis.h:921
A model for showing available data sources and other items in a structured tree.
void connectionsChanged(const QString &providerKey)
Emitted when connections for the specified providerKey have changed in the browser.
QgsDataItem * dataItem(const QModelIndex &idx) const
Returns the data item at the specified index, or nullptr if no item exists at the index.
A QSortFilterProxyModel subclass for filtering and sorting browser model items.
FilterSyntax
Filter syntax options.
@ RegularExpression
Regular expression filtering.
@ Wildcards
Wildcard filtering.
@ Normal
Standard string filtering.
void setFilterSyntax(FilterSyntax syntax)
Sets the filter syntax.
QgsBrowserWidget(QgsBrowserGuiModel *browserModel, QWidget *parent=nullptr)
Constructor for QgsBrowserWidget.
void updateProjectHome()
Update project home directory.
void setActiveIndex(const QModelIndex &index)
Sets the selection to index and expands it.
QgsMessageBar * messageBar()
Returns the message bar associated with the widget.
void connectionsChanged()
Connections changed in the browser.
void handleDropUriList(const QgsMimeDataUtils::UriList &)
Emitted when drop uri list needs to be handled.
void showEvent(QShowEvent *event) override
QgsMapCanvas * mapCanvas()
Returns the map canvas associated with the widget.
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to use alongside the widget.
~QgsBrowserWidget() override
void setMessageBar(QgsMessageBar *bar)
Sets a message bar to use alongside the widget.
void openFile(const QString &fileName, const QString &fileTypeHint=QString())
Emitted when a file needs to be opened.
void refresh()
Refreshes the browser model and view.
void setDisabledDataItemsKeys(const QStringList &filter)
Sets the customization for data items based on item's data provider key.
Encapsulates the context in which a QgsDataItem is shown within the application GUI.
void setView(QgsBrowserTreeView *view)
Sets the associated view.
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the data item.
void setMessageBar(QgsMessageBar *bar)
Sets the associated message bar.
QList< QgsDataItemGuiProvider * > providers() const
Returns the list of available providers.
Abstract base class for providers which affect how QgsDataItem items behave within the application GU...
Base class for all items in the model.
Definition qgsdataitem.h:47
virtual QList< QMenu * > menus(QWidget *parent)
Returns the list of menus available for this item.
virtual bool handleDoubleClick()
Called when a user double clicks on the item.
Qgis::BrowserItemType type() const
virtual QList< QAction * > actions(QWidget *parent)
Returns the list of actions available for this item.
QString path() const
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
virtual void depopulate()
Remove children recursively and set as not populated. This is used when refreshing collapsed items.
void cleared()
Emitted when the widget is cleared.
void optionsChanged()
This signal is emitted whenever the application options have been changed.
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition qgsgui.cpp:90
static QgsDataItemGuiProviderRegistry * dataItemGuiProviderRegistry()
Returns the global data item GUI provider registry, used for tracking providers which affect the brow...
Definition qgsgui.cpp:191
A browser item that represents a layer that can be opened with one of the providers.
QgsMimeDataUtils::UriList mimeUris() const override
Returns mime URIs for the data item, most data providers will only return a single URI but some data ...
Map canvas is a class for displaying all GIS data types on a canvas.
A bar for displaying non-blocking messages to the user.
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an inline panel.
Stores settings for use within QGIS.
Definition qgssettings.h:65
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61