QGIS API Documentation 4.1.0-Master (60fea48833c)
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 ) { return a->precedenceWhenPopulatingMenus() < b->precedenceWhenPopulatingMenus(); } );
251 for ( QgsDataItemGuiProvider *provider : std::as_const( providers ) )
252 {
253 provider->populateContextMenu( item, menu, selectedItems, context );
254 }
255
256 if ( menu->actions().isEmpty() )
257 {
258 delete menu;
259 return;
260 }
261
262 menu->popup( mBrowserView->mapToGlobal( pt ) );
263}
264
266{
267 mMessageBar = bar;
268 mModel->setMessageBar( bar );
269}
270
272{
273 return mMessageBar;
274}
275
277{
278 mMapCanvas = canvas;
279 mModel->setMapCanvas( canvas );
280}
281
283{
284 return mMapCanvas;
285}
286
287void QgsBrowserWidget::setDisabledDataItemsKeys( const QStringList &filter )
288{
289 mDisabledDataItemsKeys = filter;
290
291 if ( !mProxyModel )
292 return;
293
294 mProxyModel->setHiddenDataItemProviderKeyFilter( mDisabledDataItemsKeys );
295}
296
298{
299 refreshModel( QModelIndex() );
300}
301
302void QgsBrowserWidget::refreshModel( const QModelIndex &index )
303{
304 if ( mModel && mProxyModel )
305 {
306 QgsDataItem *item = mModel->dataItem( index );
307 if ( item )
308 {
309 QgsDebugMsgLevel( "path = " + item->path(), 4 );
310 }
311 else
312 {
313 QgsDebugMsgLevel( u"invalid item"_s, 4 );
314 }
315
316 if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
317 {
318 mModel->refresh( index );
319 }
320
321 for ( int i = 0; i < mModel->rowCount( index ); i++ )
322 {
323 QModelIndex idx = mModel->index( i, 0, index );
324 QModelIndex proxyIdx = mProxyModel->mapFromSource( idx );
325 QgsDataItem *child = mModel->dataItem( idx );
326
327 // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
328 // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
329 if ( mBrowserView->isExpanded( proxyIdx ) || mBrowserView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
330 {
331 refreshModel( idx );
332 }
333 else
334 {
335 if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
336 {
337 child->depopulate();
338 }
339 }
340 }
341 }
342}
343
344void QgsBrowserWidget::addLayer( QgsLayerItem *layerItem )
345{
346 if ( !layerItem )
347 return;
348
349 emit handleDropUriList( layerItem->mimeUris() );
350}
351
352void QgsBrowserWidget::addSelectedLayers()
353{
354 QApplication::setOverrideCursor( Qt::WaitCursor );
355
356 // get a sorted list of selected indexes
357 QModelIndexList list = mBrowserView->selectionModel()->selectedIndexes();
358 std::sort( list.begin(), list.end() );
359
360 // If any of the layer items are QGIS we just open and exit the loop
361 const auto constList = list;
362 for ( const QModelIndex &index : constList )
363 {
364 QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( index ) );
365 if ( item && item->type() == Qgis::BrowserItemType::Project )
366 {
367 QgsProjectItem *projectItem = qobject_cast<QgsProjectItem *>( item );
368 if ( projectItem )
369 emit openFile( projectItem->path(), u"project"_s );
370
371 QApplication::restoreOverrideCursor();
372 return;
373 }
374 }
375
376 // add items in reverse order so they are in correct order in the layers dock
377 for ( int i = list.size() - 1; i >= 0; i-- )
378 {
379 QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( list[i] ) );
380 if ( item && item->type() == Qgis::BrowserItemType::Layer )
381 {
382 QgsLayerItem *layerItem = qobject_cast<QgsLayerItem *>( item );
383 if ( layerItem )
384 addLayer( layerItem );
385 }
386 }
387
388 QApplication::restoreOverrideCursor();
389}
390
391void QgsBrowserWidget::hideItem()
392{
393 QModelIndex index = mProxyModel->mapToSource( mBrowserView->currentIndex() );
394 QgsDataItem *item = mModel->dataItem( index );
395 if ( !item )
396 return;
397
398 if ( item->type() == Qgis::BrowserItemType::Directory )
399 {
400 mModel->hidePath( item );
401 }
402}
403
404void QgsBrowserWidget::showProperties()
405{
406 QModelIndex index = mProxyModel->mapToSource( mBrowserView->currentIndex() );
407 QgsDataItem *item = mModel->dataItem( index );
408 if ( !item )
409 return;
410
412 {
413 QgsBrowserPropertiesDialog *dialog = new QgsBrowserPropertiesDialog( settingsSection(), this );
414 dialog->setAttribute( Qt::WA_DeleteOnClose );
415 dialog->setItem( item, createContext() );
416 dialog->show();
417 }
418}
419
420void QgsBrowserWidget::showFilterWidget( bool visible )
421{
422 mWidgetFilter->setVisible( visible );
423 if ( !visible )
424 {
425 mLeFilter->setText( QString() );
426 setFilter();
427 }
428 else
429 {
430 mLeFilter->setFocus();
431 }
432}
433
434void QgsBrowserWidget::setFilter()
435{
436 QString filter = mLeFilter->text();
437 if ( mProxyModel )
438 mProxyModel->setFilterString( filter );
439}
440
442{
443 if ( mModel )
444 mModel->updateProjectHome();
445}
446
447void QgsBrowserWidget::setFilterSyntax( QAction *action )
448{
449 if ( !action || !mProxyModel )
450 return;
451
452 mProxyModel->setFilterSyntax( static_cast<QgsBrowserProxyModel::FilterSyntax>( action->data().toInt() ) );
453}
454
455void QgsBrowserWidget::setCaseSensitive( bool caseSensitive )
456{
457 if ( !mProxyModel )
458 return;
459 mProxyModel->setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
460}
461
462int QgsBrowserWidget::selectedItemsCount()
463{
464 QItemSelectionModel *selectionModel = mBrowserView->selectionModel();
465 if ( selectionModel )
466 {
467 return selectionModel->selectedIndexes().size();
468 }
469 return 0;
470}
471
472QgsDataItemGuiContext QgsBrowserWidget::createContext()
473{
474 QgsDataItemGuiContext context;
475 context.setMessageBar( mMessageBar );
476 context.setMapCanvas( mMapCanvas );
477 context.setView( mBrowserView );
478 return context;
479}
480
481void QgsBrowserWidget::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
482{
483 Q_UNUSED( selected )
484 Q_UNUSED( deselected )
485 if ( mActionPropertiesWidget->isChecked() )
486 {
487 setPropertiesWidget();
488 }
489}
490
491void QgsBrowserWidget::clearPropertiesWidget()
492{
493 while ( mPropertiesLayout->count() > 0 )
494 {
495 delete mPropertiesLayout->itemAt( 0 )->widget();
496 }
497 mPropertiesWidget->setVisible( false );
498}
499
500void QgsBrowserWidget::setPropertiesWidget()
501{
502 clearPropertiesWidget();
503 QItemSelectionModel *selectionModel = mBrowserView->selectionModel();
504 if ( selectionModel )
505 {
506 QModelIndexList indexes = selectionModel->selectedIndexes();
507 if ( indexes.size() == 1 )
508 {
509 QModelIndex index = mProxyModel->mapToSource( indexes.value( 0 ) );
510 QgsDataItem *item = mModel->dataItem( index );
511 QgsDataItemGuiContext context = createContext();
512 QgsBrowserPropertiesWidget *propertiesWidget = QgsBrowserPropertiesWidget::createWidget( item, context, mPropertiesWidget );
513 if ( propertiesWidget )
514 {
515 propertiesWidget->setCondensedMode( true );
516 mPropertiesLayout->addWidget( propertiesWidget );
517 }
518 }
519 }
520 mPropertiesWidget->setVisible( mPropertiesLayout->count() > 0 );
521}
522
523void QgsBrowserWidget::enablePropertiesWidget( bool enable )
524{
525 mActionPropertiesWidget->setChecked( enable );
526 propertiesWidgetToggled( enable );
527}
528
529void QgsBrowserWidget::propertiesWidgetToggled( bool enabled )
530{
531 if ( enabled && selectedItemsCount() == 1 )
532 {
533 setPropertiesWidget();
534 }
535 else
536 {
537 clearPropertiesWidget();
538 }
539
540 QgsSettings settings;
541 settings.setValue( settingsSection() + "/propertiesWidgetEnabled", enabled );
542}
543
544void QgsBrowserWidget::setActiveIndex( const QModelIndex &index )
545{
546 if ( index.isValid() )
547 {
548 QModelIndex proxyIndex = mProxyModel->mapFromSource( index );
549 mBrowserView->expand( proxyIndex );
550 mBrowserView->setCurrentIndex( proxyIndex );
551 }
552}
553
554void QgsBrowserWidget::splitterMoved()
555{
556 QgsSettings settings;
557 settings.setValue( u"%1/splitterState"_s.arg( settingsSection() ), mSplitter->saveState() );
558}
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
Definition qgis.h:980
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
Definition qgis.h:981
@ Layer
Represents a map layer.
Definition qgis.h:948
@ Project
Represents a QGIS project.
Definition qgis.h:951
@ Directory
Represents a file directory.
Definition qgis.h:947
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