Skip to content

Commit 0034108

Browse files
Médéric RIBREUX3nids
authored andcommitted
Improve FileName editor widget
* Use an hyperlink for file name: when checked the widget displays an hyperlink (in a QLabel) instead of a QLineEdit. The link is always clickable, even in non edit mode. In this mode, user can only change the content of the field by selecting a file with the file selector (which is opened with the "..." button). If you want to revert to editable QLineEdit file name widget, just unchek the option. * Display the full path: when checked, the hyperlink will display the absolute path of the file. When unchecked, the hyperlink will display only the name of the file (useful if the file path is always long). Default Path: this is the path that will be used by the file selector when opening. The file selector will start at this path (if the option is not empty). * Store Relative paths to the default path: when checked, file names are stored relatively to the default path described above. It is useful to store long paths into text shapefiles attributes limited to 254 characters. * Store relative paths to the project path: when checked, file names are stored relatively to the project (.qgs file) path. This option will override the "save paths" setting of the project because it is sometimes useful to store code/svg/layer path relatively but not for the file name attributes. * File storage options are exclusive options to make the file selector select files (default mode) or only directories. File Name editor widget: * add a isFieldSupported method to the factory to allow File Name widget only on fields that are QStrings. * add a new QSetting to save the last path used in the widget. Before the PR, users were always defaulted to QGIS home directory when using the file selector button. This QSetting could also be used for the photo widget. * when you want to change the value of an already set attribute, file selector will point to this path. * when the value of the attribute is null or non valid (not a file), the file selector will use the default path (if set), or the last used path or the home directory.
1 parent b7170b8 commit 0034108

8 files changed

+680
-41
lines changed

src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ SET(QGIS_GUI_SRCS
103103
editorwidgets/qgsdummyconfigdlg.cpp
104104
editorwidgets/qgsenumerationwidgetwrapper.cpp
105105
editorwidgets/qgsenumerationwidgetfactory.cpp
106+
editorwidgets/qgsfilenameconfigdlg.cpp
106107
editorwidgets/qgsfilenamewidgetwrapper.cpp
107108
editorwidgets/qgsfilenamewidgetfactory.cpp
108109
editorwidgets/qgshiddenwidgetwrapper.cpp
@@ -495,6 +496,7 @@ SET(QGIS_GUI_MOC_HDRS
495496
editorwidgets/qgsdoublespinbox.h
496497
editorwidgets/qgsdummyconfigdlg.h
497498
editorwidgets/qgsenumerationwidgetwrapper.h
499+
editorwidgets/qgsfilenameconfigdlg.h
498500
editorwidgets/qgsfilenamewidgetwrapper.h
499501
editorwidgets/qgshiddenwidgetwrapper.h
500502
editorwidgets/qgsphotoconfigdlg.h
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/***************************************************************************
2+
qgsfilenameconfigdlg.cpp
3+
--------------------------------------
4+
Date : 2015-11-26
5+
Copyright : (C) 2015 Médéric Ribreux
6+
Email : mederic.ribreux at medspx dot fr
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+
16+
#include "qgsfilenameconfigdlg.h"
17+
#include "qgsproject.h"
18+
19+
#include <QFileDialog>
20+
#include <QSettings>
21+
22+
class QgsFileNameWidgetWrapper;
23+
24+
QgsFileNameConfigDlg::QgsFileNameConfigDlg( QgsVectorLayer* vl, int fieldIdx, QWidget* parent )
25+
: QgsEditorConfigWidget( vl, fieldIdx, parent )
26+
{
27+
setupUi( this );
28+
29+
// By default, uncheck some options
30+
mUseLink->setChecked( false );
31+
mFullUrl->setChecked( false );
32+
mRootPath->setPlaceholderText( QSettings().value( "/UI/lastFileNameWidgetDir", QDir::toNativeSeparators( QDir::cleanPath( QgsProject::instance()->fileInfo().absolutePath() ) ) ).toString() );
33+
34+
// Add connection to button for choosing default path
35+
connect( mRootPathButton, SIGNAL( clicked() ), this, SLOT( chooseDefaultPath() ) );
36+
37+
// Activate Relative Default Path option only if Default Path is set
38+
connect( mRootPath, SIGNAL( textChanged( const QString & ) ), this, SLOT( enableRelativeDefault() ) );
39+
40+
// Dynamic GroupBox for relative paths option
41+
connect( mRelativeGroupBox, SIGNAL( toggled( bool ) ), this, SLOT( enableRelative( bool ) ) );
42+
43+
// set ids for StorageTypeButtons
44+
mStorageButtonGroup->setId( mStoreFilesButton, 0 );
45+
mStorageButtonGroup->setId( mStoreDirsButton, 1 );
46+
47+
// First button is toggled by default
48+
mStorageButtonGroup->button( 0 )->toggle();
49+
50+
// set ids for RelativeButtons
51+
mRelativeButtonGroup->setId( mRelativeProj, 0 );
52+
mRelativeButtonGroup->setId( mRelativeDefault, 1 );
53+
54+
mRelativeButtonGroup->button( 0 )->toggle();
55+
}
56+
57+
// Choose a base directory for rootPath
58+
void QgsFileNameConfigDlg::chooseDefaultPath()
59+
{
60+
QString dir;
61+
if ( !mRootPath->text().isEmpty() )
62+
dir = mRootPath->text();
63+
else
64+
dir = QSettings().value( "/UI/lastFileNameWidgetDir", QDir::toNativeSeparators( QDir::cleanPath( QgsProject::instance()->fileInfo().absolutePath() ) ) ).toString();
65+
66+
QString rootName = QFileDialog::getExistingDirectory( this, tr( "Select a directory" ), dir, QFileDialog::ShowDirsOnly );
67+
68+
if ( rootName.isNull() )
69+
return;
70+
71+
mRootPath->setText( rootName );
72+
}
73+
74+
// Modify RelativeDefault according to mRootPath content
75+
void QgsFileNameConfigDlg::enableRelativeDefault()
76+
{
77+
// Activate (or not) the RelativeDefault button if default path
78+
if ( mRelativeGroupBox->isChecked() )
79+
mRelativeDefault->setEnabled( !mRootPath->text().isEmpty() );
80+
81+
// If no default path, RelativeProj button enabled by default
82+
if ( mRootPath->text().isEmpty() )
83+
mRelativeProj->toggle();
84+
}
85+
86+
// Dynamic activation of RelativeGroupBox
87+
void QgsFileNameConfigDlg::enableRelative( bool state )
88+
{
89+
if ( state )
90+
{
91+
mRelativeProj->setEnabled( true );
92+
if ( mRootPath->text().isEmpty() )
93+
mRelativeDefault->setEnabled( false );
94+
else
95+
mRelativeDefault->setEnabled( true );
96+
}
97+
else
98+
{
99+
mRelativeProj->setEnabled( false );
100+
mRelativeDefault->setEnabled( false );
101+
}
102+
}
103+
104+
// All non mandatory options are not stored to reduce
105+
// the file size of the project file !
106+
QgsEditorWidgetConfig QgsFileNameConfigDlg::config()
107+
{
108+
QgsEditorWidgetConfig cfg;
109+
110+
if ( mUseLink->isChecked() )
111+
{
112+
cfg.insert( "UseLink", mUseLink->isChecked() );
113+
if ( mFullUrl->isChecked() )
114+
cfg.insert( "FullUrl", mFullUrl->isChecked() );
115+
}
116+
117+
if ( !mRootPath->text().isEmpty() )
118+
cfg.insert( "DefaultRoot", mRootPath->text() );
119+
120+
// Save Storage Mode
121+
int but = mStorageButtonGroup->checkedId();
122+
if ( but != -1 )
123+
{
124+
if ( but == 0 )
125+
cfg.insert( "StorageMode", "Files" );
126+
else if ( but == 1 )
127+
cfg.insert( "StorageMode", "Dirs" );
128+
}
129+
130+
// Save Relative Paths option
131+
if ( mRelativeGroupBox->isChecked() )
132+
{
133+
but = mRelativeButtonGroup->checkedId();
134+
if ( but != -1 )
135+
{
136+
if ( but == 0 )
137+
cfg.insert( "RelativeStorage", "Project" );
138+
else if ( but == 1 )
139+
cfg.insert( "RelativeStorage", "Default" );
140+
}
141+
}
142+
143+
return cfg;
144+
}
145+
146+
// Set the configuration on the widget
147+
void QgsFileNameConfigDlg::setConfig( const QgsEditorWidgetConfig& config )
148+
{
149+
if ( config.contains( "UseLink" ) )
150+
{
151+
mUseLink->setChecked( true );
152+
if ( config.contains( "FullUrl" ) )
153+
mFullUrl->setChecked( true );
154+
}
155+
156+
if ( config.contains( "DefaultRoot" ) )
157+
mRootPath->setText( config.value( "DefaultRoot" ).toString() );
158+
159+
if ( config.contains( "RelativeStorage" ) )
160+
{
161+
mRelativeGroupBox->setChecked( true );
162+
if ( config.value( "RelativeStorage" ) == "Default" )
163+
mRelativeDefault->toggle();
164+
else if ( config.value( "RelativeStorage" ) == "Project" )
165+
mRelativeProj->toggle();
166+
}
167+
168+
// set storage mode
169+
if ( config.contains( "StorageMode" ) )
170+
{
171+
if ( config.value( "StorageMode" ) == "Files" )
172+
mStorageButtonGroup->button( 0 )->toggle();
173+
else if ( config.value( "StorageMode" ) == "Dirs" )
174+
mStorageButtonGroup->button( 1 )->toggle();
175+
}
176+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/***************************************************************************
2+
qgsfilenameconfigdlg.h
3+
--------------------------------------
4+
Date : 2015-11-26
5+
Copyright : (C) 2015 Médéric Ribreux
6+
Email : mederic dot ribreux at medspx dot fr
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+
16+
#ifndef QGSFILENAMECONFIGDLG_H
17+
#define QGSFILENAMECONFIGDLG_H
18+
19+
#include "ui_qgsfilenameconfigdlg.h"
20+
21+
#include "qgseditorconfigwidget.h"
22+
//#include "qgsfilenamewidgetwrapper.h"
23+
24+
/** \class QgsFileNameConfigDlg
25+
* \note not available in Python bindings
26+
*/
27+
28+
class GUI_EXPORT QgsFileNameConfigDlg : public QgsEditorConfigWidget, private Ui::QgsFileNameConfigDlg
29+
{
30+
Q_OBJECT
31+
32+
public:
33+
explicit QgsFileNameConfigDlg( QgsVectorLayer* vl, int fieldIdx, QWidget *parent = 0 );
34+
35+
// QgsEditorConfigWidget interface
36+
public:
37+
QgsEditorWidgetConfig config() override;
38+
void setConfig( const QgsEditorWidgetConfig& config ) override;
39+
40+
private slots:
41+
void chooseDefaultPath();
42+
void enableRelativeDefault();
43+
void enableRelative( bool state );
44+
};
45+
46+
#endif // QGSFILENAMECONFIGDLG_H

src/gui/editorwidgets/qgsfilenamewidgetfactory.cpp

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,8 @@
1515

1616
#include "qgsfilenamewidgetfactory.h"
1717

18-
#include "qgsfilenamewidgetwrapper.h"
19-
#include "qgsdummyconfigdlg.h"
20-
21-
QgsFileNameWidgetFactory::QgsFileNameWidgetFactory( const QString& name ) :
22-
QgsEditorWidgetFactory( name )
18+
QgsFileNameWidgetFactory::QgsFileNameWidgetFactory( const QString& name )
19+
: QgsEditorWidgetFactory( name )
2320
{
2421
}
2522

@@ -30,5 +27,63 @@ QgsEditorWidgetWrapper* QgsFileNameWidgetFactory::create( QgsVectorLayer* vl, in
3027

3128
QgsEditorConfigWidget* QgsFileNameWidgetFactory::configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const
3229
{
33-
return new QgsDummyConfigDlg( vl, fieldIdx, parent, QObject::tr( "Simplifies file selection by adding a file chooser dialog." ) );
30+
return new QgsFileNameConfigDlg( vl, fieldIdx, parent );
31+
}
32+
33+
void QgsFileNameWidgetFactory::writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx )
34+
{
35+
Q_UNUSED( doc )
36+
Q_UNUSED( layer )
37+
Q_UNUSED( fieldIdx )
38+
39+
// Non mandatory options are not saved into project file (to save some space).
40+
if ( config.contains( "UseLink" ) )
41+
configElement.setAttribute( "UseLink", config.value( "UseLink" ).toBool() );
42+
43+
if ( config.contains( "FullUrl" ) )
44+
configElement.setAttribute( "FullUrl", config.value( "FullUrl" ).toBool() );
45+
46+
if ( config.contains( "DefaultRoot" ) )
47+
configElement.setAttribute( "DefaultRoot", config.value( "DefaultRoot" ).toString() );
48+
49+
if ( config.contains( "RelativeStorage" ) )
50+
configElement.setAttribute( "RelativeStorage" , config.value( "RelativeStorage" ).toString() );
51+
52+
configElement.setAttribute( "StorageMode", config.value( "StorageMode" ).toString() );
53+
}
54+
55+
QgsEditorWidgetConfig QgsFileNameWidgetFactory::readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx )
56+
{
57+
Q_UNUSED( layer )
58+
Q_UNUSED( fieldIdx )
59+
60+
QgsEditorWidgetConfig cfg;
61+
62+
if ( configElement.hasAttribute( "UseLink" ) )
63+
cfg.insert( "UseLink", configElement.attribute( "UseLink" ) == "1" );
64+
65+
if ( configElement.hasAttribute( "FullUrl" ) )
66+
cfg.insert( "FullUrl", configElement.attribute( "FullUrl" ) == "1" );
67+
68+
if ( configElement.hasAttribute( "DefaultRoot" ) )
69+
cfg.insert( "DefaultRoot", configElement.attribute( "DefaultRoot" ) );
70+
71+
if ( configElement.hasAttribute( "RelativeStorage" ) )
72+
{
73+
if ( ( configElement.attribute( "RelativeStorage" ) == "Default" && configElement.hasAttribute( "DefaultRoot" ) ) ||
74+
configElement.attribute( "RelativeStorage" ) == "Project" )
75+
cfg.insert( "RelativeStorage" , configElement.attribute( "RelativeStorage" ) );
76+
}
77+
78+
cfg.insert( "StorageMode", configElement.attribute( "StorageMode", "Files" ) );
79+
80+
return cfg;
81+
}
82+
83+
bool QgsFileNameWidgetFactory::isFieldSupported( QgsVectorLayer* vl, int fieldIdx )
84+
{
85+
if ( vl->fields().at( fieldIdx ).type() == QVariant::String )
86+
return true;
87+
88+
return false;
3489
}

src/gui/editorwidgets/qgsfilenamewidgetfactory.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#define QGSFILENAMEWIDGETFACTORY_H
1818

1919
#include "qgseditorwidgetfactory.h"
20+
#include "qgsfilenameconfigdlg.h"
21+
#include "qgsfilenamewidgetwrapper.h"
2022

2123
/** \class QgsFileNameWidgetFactory
2224
* \note not available in Python bindings
@@ -25,12 +27,20 @@
2527
class GUI_EXPORT QgsFileNameWidgetFactory : public QgsEditorWidgetFactory
2628
{
2729
public:
28-
explicit QgsFileNameWidgetFactory( const QString& name );
30+
QgsFileNameWidgetFactory( const QString& name );
2931

3032
// QgsEditorWidgetFactory interface
3133
public:
3234
QgsEditorWidgetWrapper* create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent ) const override;
3335
QgsEditorConfigWidget* configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const override;
36+
37+
// QgsEditorWidgetFactory interface
38+
public:
39+
void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;
40+
41+
private:
42+
QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx ) override;
43+
bool isFieldSupported( QgsVectorLayer* vl, int fieldIdx ) override;
3444
};
3545

3646
#endif // QGSFILENAMEWIDGETFACTORY_H

0 commit comments

Comments
 (0)