Skip to content

Commit 02ecb56

Browse files
committedNov 7, 2018
QgsDataSourceSelectDialog: filter, refresh and scroll to last selected item
plus: - expand children of last selected item - save/restore status
1 parent 4cdde31 commit 02ecb56

File tree

4 files changed

+380
-5
lines changed

4 files changed

+380
-5
lines changed
 

‎python/gui/auto_generated/qgsdatasourceselectdialog.sip.in

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,28 @@ Sets layer type filter to ``layerType`` and activates the filtering
5555
QgsMimeDataUtils::Uri uri() const;
5656
%Docstring
5757
Returns the (possibly invalid) uri of the selected data source
58+
%End
59+
60+
void showFilterWidget( bool visible );
61+
%Docstring
62+
Show/hide filter widget
63+
%End
64+
void setFilterSyntax( QAction * );
65+
%Docstring
66+
Sets filter syntax
67+
%End
68+
void setCaseSensitive( bool caseSensitive );
69+
%Docstring
70+
Sets filter case sensitivity
71+
%End
72+
void setFilter();
73+
%Docstring
74+
Apply filter to the model
75+
%End
76+
virtual void showEvent( QShowEvent *e );
77+
78+
%Docstring
79+
Scroll to last selected index and expand it's children
5880
%End
5981

6082
};

‎src/gui/qgsdatasourceselectdialog.cpp

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
#include "qgssettings.h"
2020
#include "qgsgui.h"
2121
#include "qgis.h"
22+
#include "qgsbrowsermodel.h"
2223

2324
#include <QPushButton>
25+
#include <QMenu>
2426

2527
QgsDataSourceSelectDialog::QgsDataSourceSelectDialog(
2628
QgsBrowserModel *browserModel,
@@ -32,6 +34,7 @@ QgsDataSourceSelectDialog::QgsDataSourceSelectDialog(
3234
if ( ! browserModel )
3335
{
3436
mBrowserModel = qgis::make_unique<QgsBrowserModel>();
37+
mBrowserModel->initialize();
3538
mOwnModel = true;
3639
}
3740
else
@@ -44,20 +47,68 @@ QgsDataSourceSelectDialog::QgsDataSourceSelectDialog(
4447
setWindowTitle( tr( "Select a Data Source" ) );
4548
QgsGui::enableAutoGeometryRestore( this );
4649

47-
mBrowserModel->initialize();
4850
mBrowserProxyModel.setBrowserModel( mBrowserModel.get() );
4951
mBrowserTreeView->setHeaderHidden( true );
5052

5153
if ( setFilterByLayerType )
5254
{
55+
// This will also set the (proxy) model
5356
setLayerTypeFilter( layerType );
5457
}
5558
else
5659
{
5760
mBrowserTreeView->setModel( &mBrowserProxyModel );
5861
buttonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( false );
5962
}
63+
64+
mBrowserTreeView->setBrowserModel( mBrowserModel.get() );
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, &QgsDataSourceSelectDialog::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 );
93+
action->setData( QgsBrowserProxyModel::RegularExpression );
94+
action->setCheckable( true );
95+
menu->addAction( action );
96+
97+
mBrowserTreeView->setExpandsOnDoubleClick( false );
98+
99+
connect( mActionRefresh, &QAction::triggered, [ = ] { refreshModel( QModelIndex() ); } );
60100
connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, &QgsDataSourceSelectDialog::onLayerSelected );
101+
connect( mActionCollapse, &QAction::triggered, mBrowserTreeView, &QgsBrowserTreeView::collapseAll );
102+
connect( mActionShowFilter, &QAction::triggered, this, &QgsDataSourceSelectDialog::showFilterWidget );
103+
connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsDataSourceSelectDialog::setFilter );
104+
connect( mLeFilter, &QgsFilterLineEdit::cleared, this, &QgsDataSourceSelectDialog::setFilter );
105+
connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsDataSourceSelectDialog::setFilter );
106+
connect( group, &QActionGroup::triggered, this, &QgsDataSourceSelectDialog::setFilterSyntax );
107+
108+
if ( QgsSettings().value( QStringLiteral( "datasourceSelectFilterVisible" ), false, QgsSettings::Section::Gui ).toBool() )
109+
{
110+
mActionShowFilter->trigger();
111+
}
61112
}
62113

63114
QgsDataSourceSelectDialog::~QgsDataSourceSelectDialog()
@@ -66,6 +117,107 @@ QgsDataSourceSelectDialog::~QgsDataSourceSelectDialog()
66117
mBrowserModel.release();
67118
}
68119

120+
121+
void QgsDataSourceSelectDialog::showEvent( QShowEvent *e )
122+
{
123+
QDialog::showEvent( e );
124+
QString lastSelectedPath( QgsSettings().value( QStringLiteral( "datasourceSelectLastSelectedItem" ),
125+
QString(), QgsSettings::Section::Gui ).toString() );
126+
if ( ! lastSelectedPath.isEmpty() )
127+
{
128+
QModelIndexList items = mBrowserProxyModel.match(
129+
mBrowserProxyModel.index( 0, 0 ),
130+
QgsBrowserModel::PathRole,
131+
QVariant::fromValue( lastSelectedPath ),
132+
1,
133+
Qt::MatchRecursive );
134+
if ( items.count( ) > 0 )
135+
{
136+
QModelIndex expandIndex = items.at( 0 );
137+
if ( expandIndex.isValid() )
138+
{
139+
mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::QgsBrowserTreeView::ScrollHint::PositionAtTop );
140+
mBrowserTreeView->expand( expandIndex );
141+
}
142+
}
143+
}
144+
}
145+
146+
void QgsDataSourceSelectDialog::showFilterWidget( bool visible )
147+
{
148+
QgsSettings().setValue( QStringLiteral( "datasourceSelectFilterVisible" ), visible, QgsSettings::Section::Gui );
149+
mWidgetFilter->setVisible( visible );
150+
if ( ! visible )
151+
{
152+
mLeFilter->setText( QString() );
153+
setFilter();
154+
}
155+
else
156+
{
157+
mLeFilter->setFocus();
158+
}
159+
}
160+
161+
void QgsDataSourceSelectDialog::setFilter()
162+
{
163+
QString filter = mLeFilter->text();
164+
mBrowserProxyModel.setFilterString( filter );
165+
}
166+
167+
168+
void QgsDataSourceSelectDialog::refreshModel( const QModelIndex &index )
169+
{
170+
171+
QgsDataItem *item = mBrowserModel->dataItem( index );
172+
if ( item )
173+
{
174+
QgsDebugMsg( "path = " + item->path() );
175+
}
176+
else
177+
{
178+
QgsDebugMsg( QStringLiteral( "invalid item" ) );
179+
}
180+
181+
if ( item && ( item->capabilities2() & QgsDataItem::Fertile ) )
182+
{
183+
mBrowserModel->refresh( index );
184+
}
185+
186+
for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
187+
{
188+
QModelIndex idx = mBrowserModel->index( i, 0, index );
189+
QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
190+
QgsDataItem *child = mBrowserModel->dataItem( idx );
191+
192+
// Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
193+
// Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
194+
if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & QgsDataItem::Fast ) )
195+
{
196+
refreshModel( idx );
197+
}
198+
else
199+
{
200+
if ( child && ( child->capabilities2() & QgsDataItem::Fertile ) )
201+
{
202+
child->depopulate();
203+
}
204+
}
205+
}
206+
}
207+
208+
209+
void QgsDataSourceSelectDialog::setFilterSyntax( QAction *action )
210+
{
211+
if ( !action )
212+
return;
213+
mBrowserProxyModel.setFilterSyntax( static_cast< QgsBrowserProxyModel::FilterSyntax >( action->data().toInt() ) );
214+
}
215+
216+
void QgsDataSourceSelectDialog::setCaseSensitive( bool caseSensitive )
217+
{
218+
mBrowserProxyModel.setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
219+
}
220+
69221
void QgsDataSourceSelectDialog::setLayerTypeFilter( QgsMapLayer::LayerType layerType )
70222
{
71223
mBrowserProxyModel.setFilterByLayerType( true );
@@ -95,6 +247,8 @@ void QgsDataSourceSelectDialog::onLayerSelected( const QModelIndex &index )
95247
{
96248
isLayerCompatible = true;
97249
mUri = layerItem->mimeUri();
250+
// Store last viewed item
251+
QgsSettings().setValue( QStringLiteral( "datasourceSelectLastSelectedItem" ), mBrowserProxyModel.data( index, QgsBrowserModel::PathRole ).toString(), QgsSettings::Section::Gui );
98252
}
99253
}
100254
}

‎src/gui/qgsdatasourceselectdialog.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,27 @@ class GUI_EXPORT QgsDataSourceSelectDialog: public QDialog, private Ui::QgsDataS
7373
*/
7474
QgsMimeDataUtils::Uri uri() const;
7575

76+
//! Show/hide filter widget
77+
void showFilterWidget( bool visible );
78+
//! Sets filter syntax
79+
void setFilterSyntax( QAction * );
80+
//! Sets filter case sensitivity
81+
void setCaseSensitive( bool caseSensitive );
82+
//! Apply filter to the model
83+
void setFilter();
84+
//! Scroll to last selected index and expand it's children
85+
void showEvent( QShowEvent *e ) override;
86+
7687
private slots:
7788

7889
//! Triggered when a layer is selected in the browser
7990
void onLayerSelected( const QModelIndex &index );
8091

8192
private:
8293

94+
//! Refresh the model
95+
void refreshModel( const QModelIndex &index );
96+
8397
QgsBrowserProxyModel mBrowserProxyModel;
8498
std::unique_ptr<QgsBrowserModel> mBrowserModel;
8599
bool mOwnModel = true;

‎src/ui/qgsdatasourceselectdialog.ui

Lines changed: 189 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,152 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>400</width>
10-
<height>300</height>
9+
<width>700</width>
10+
<height>629</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
1414
<string>Dialog</string>
1515
</property>
1616
<layout class="QVBoxLayout" name="verticalLayout">
1717
<item>
18-
<widget class="QgsBrowserTreeView" name="mBrowserTreeView"/>
18+
<widget class="QWidget" name="mContents">
19+
<layout class="QVBoxLayout" name="verticalLayout_2">
20+
<property name="spacing">
21+
<number>6</number>
22+
</property>
23+
<property name="leftMargin">
24+
<number>0</number>
25+
</property>
26+
<property name="topMargin">
27+
<number>0</number>
28+
</property>
29+
<property name="rightMargin">
30+
<number>0</number>
31+
</property>
32+
<property name="bottomMargin">
33+
<number>0</number>
34+
</property>
35+
<item>
36+
<widget class="QToolBar" name="mBrowserToolbar">
37+
<property name="iconSize">
38+
<size>
39+
<width>24</width>
40+
<height>24</height>
41+
</size>
42+
</property>
43+
<property name="floatable">
44+
<bool>false</bool>
45+
</property>
46+
<addaction name="mActionRefresh"/>
47+
<addaction name="mActionShowFilter"/>
48+
<addaction name="mActionCollapse"/>
49+
</widget>
50+
</item>
51+
<item>
52+
<widget class="QWidget" name="mWidgetFilter" native="true">
53+
<layout class="QHBoxLayout" name="horizontalLayout">
54+
<property name="spacing">
55+
<number>0</number>
56+
</property>
57+
<property name="leftMargin">
58+
<number>0</number>
59+
</property>
60+
<property name="topMargin">
61+
<number>0</number>
62+
</property>
63+
<property name="rightMargin">
64+
<number>0</number>
65+
</property>
66+
<property name="bottomMargin">
67+
<number>0</number>
68+
</property>
69+
<item>
70+
<widget class="QFrame" name="frame">
71+
<property name="sizePolicy">
72+
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
73+
<horstretch>0</horstretch>
74+
<verstretch>0</verstretch>
75+
</sizepolicy>
76+
</property>
77+
<property name="frameShape">
78+
<enum>QFrame::NoFrame</enum>
79+
</property>
80+
<property name="frameShadow">
81+
<enum>QFrame::Raised</enum>
82+
</property>
83+
<layout class="QHBoxLayout" name="horizontalLayout_3">
84+
<property name="spacing">
85+
<number>2</number>
86+
</property>
87+
<property name="leftMargin">
88+
<number>2</number>
89+
</property>
90+
<property name="topMargin">
91+
<number>0</number>
92+
</property>
93+
<property name="rightMargin">
94+
<number>2</number>
95+
</property>
96+
<property name="bottomMargin">
97+
<number>0</number>
98+
</property>
99+
<item>
100+
<widget class="QToolButton" name="mBtnFilterOptions">
101+
<property name="toolTip">
102+
<string>Options</string>
103+
</property>
104+
<property name="text">
105+
<string/>
106+
</property>
107+
<property name="icon">
108+
<iconset resource="../../images/images.qrc">
109+
<normaloff>:/images/themes/default/mActionOptions.svg</normaloff>:/images/themes/default/mActionOptions.svg</iconset>
110+
</property>
111+
<property name="popupMode">
112+
<enum>QToolButton::InstantPopup</enum>
113+
</property>
114+
<property name="toolButtonStyle">
115+
<enum>Qt::ToolButtonIconOnly</enum>
116+
</property>
117+
<property name="autoRaise">
118+
<bool>true</bool>
119+
</property>
120+
</widget>
121+
</item>
122+
<item>
123+
<widget class="QgsFilterLineEdit" name="mLeFilter">
124+
<property name="sizePolicy">
125+
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
126+
<horstretch>0</horstretch>
127+
<verstretch>0</verstretch>
128+
</sizepolicy>
129+
</property>
130+
<property name="minimumSize">
131+
<size>
132+
<width>0</width>
133+
<height>0</height>
134+
</size>
135+
</property>
136+
<property name="baseSize">
137+
<size>
138+
<width>0</width>
139+
<height>0</height>
140+
</size>
141+
</property>
142+
</widget>
143+
</item>
144+
</layout>
145+
</widget>
146+
</item>
147+
</layout>
148+
</widget>
149+
</item>
150+
<item>
151+
<widget class="QgsBrowserTreeView" name="mBrowserTreeView"/>
152+
</item>
153+
</layout>
154+
</widget>
19155
</item>
20156
<item>
21157
<widget class="QDialogButtonBox" name="buttonBox">
@@ -28,15 +164,64 @@
28164
</widget>
29165
</item>
30166
</layout>
167+
168+
<action name="mActionRefresh">
169+
<property name="icon">
170+
<iconset resource="../../images/images.qrc">
171+
<normaloff>:/images/themes/default/mActionRefresh.svg</normaloff>:/images/themes/default/mActionRefresh.svg</iconset>
172+
</property>
173+
<property name="text">
174+
<string>Refresh</string>
175+
</property>
176+
</action>
177+
<action name="mActionShowFilter">
178+
<property name="checkable">
179+
<bool>true</bool>
180+
</property>
181+
<property name="icon">
182+
<iconset resource="../../images/images.qrc">
183+
<normaloff>:/images/themes/default/mActionFilter2.svg</normaloff>:/images/themes/default/mActionFilter2.svg</iconset>
184+
</property>
185+
<property name="text">
186+
<string>Filter Browser</string>
187+
</property>
188+
<property name="iconText">
189+
<string>Filter Browser</string>
190+
</property>
191+
<property name="toolTip">
192+
<string>Filter Browser</string>
193+
</property>
194+
</action>
195+
<action name="mActionCollapse">
196+
<property name="icon">
197+
<iconset resource="../../images/images.qrc">
198+
<normaloff>:/images/themes/default/mActionCollapseTree.svg</normaloff>:/images/themes/default/mActionCollapseTree.svg</iconset>
199+
</property>
200+
<property name="text">
201+
<string>Collapse All</string>
202+
</property>
203+
<property name="toolTip">
204+
<string>Collapse All</string>
205+
</property>
206+
</action>
207+
31208
</widget>
32209
<customwidgets>
210+
<customwidget>
211+
<class>QgsFilterLineEdit</class>
212+
<extends>QLineEdit</extends>
213+
<header>qgsfilterlineedit.h</header>
214+
</customwidget>
33215
<customwidget>
34216
<class>QgsBrowserTreeView</class>
35217
<extends>QTreeView</extends>
36218
<header>qgsbrowsertreeview.h</header>
37219
</customwidget>
38220
</customwidgets>
39-
<resources/>
221+
<resources>
222+
<include location="../../images/images.qrc"/>
223+
224+
</resources>
40225
<connections>
41226
<connection>
42227
<sender>buttonBox</sender>

0 commit comments

Comments
 (0)
Please sign in to comment.