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