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