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