Remove QgsFileDropEdit widget and integrate functionality into QgsFileWidget

It makes no sense to have two classes covering this use case, with
partial functionality in each. Smash the two together so we can
safely use QgsFileWidget for all use cases in future.
This commit is contained in:
Nyall Dawson 2017-08-06 16:32:10 +10:00
parent 10968aeb25
commit b1a6c790cf
11 changed files with 254 additions and 242 deletions

View File

@ -267,6 +267,7 @@ should now call QgsCoordinateReferenceSystem::invalidateCache() and QgsCoordinat
- QgsDataDefinedSymbolDialog was removed. Code using this dialog should be reworked to use QgsPropertyOverrideButton
- QgsDefaultPluginLayerLegend was removed. Use QgsMapLayer::setLegend() to provide legend nodes for plugin layers.
- QgsFileNameWidgetWrapper was removed. Use QgsExternalResourceWidgetWrapper instead.
- QgsFileDropEdit was removed. Use QgsFileWidget instead.
- QgsFormAnnotationItem. Use QgsFormAnnotation instead.
- QgsHtmlAnnotationItem. Use QgsHtmlAnnotation instead.
- QgsHttpTransaction. This class was outdated and code should be ported to native Qt or Python implementations.

View File

@ -4,7 +4,6 @@
%Include qgscustomdrophandler.sip
%Include qgsdetaileditemdata.sip
%Include qgsexpressionbuilderdialog.sip
%Include qgsfiledropedit.sip
%Include qgsgeometryrubberband.sip
%Include qgsgui.sip
%Include qgshelp.sip

View File

@ -9,8 +9,6 @@
class QgsFileWidget : QWidget
{
%Docstring
@ -149,6 +147,14 @@ returns if the relative path is with respect to the project path or the default
determines if the relative path is with respect to the project path or the default path
%End
QLineEdit *lineEdit();
%Docstring
Returns a pointer to the widget's line edit, which can be used to customise
the appearance and behavior of the line edit portion of the widget.
.. versionadded:: 3.0
:rtype: QLineEdit
%End
signals:
void fileChanged( const QString & );
%Docstring
@ -157,6 +163,10 @@ emitted as soon as the current file or directory is changed
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -243,7 +243,6 @@ SET(QGIS_GUI_SRCS
qgsfieldexpressionwidget.cpp
qgsfieldvalidator.cpp
qgsfieldvalueslineedit.cpp
qgsfiledropedit.cpp
qgsfilewidget.cpp
qgsfilterlineedit.cpp
qgsfloatingwidget.cpp
@ -408,7 +407,6 @@ SET(QGIS_GUI_MOC_HDRS
qgsfieldexpressionwidget.h
qgsfieldvalidator.h
qgsfieldvalueslineedit.h
qgsfiledropedit.h
qgsfilewidget.h
qgsfilterlineedit.h
qgsfloatingwidget.h
@ -691,7 +689,6 @@ SET(QGIS_GUI_HDRS
qgscustomdrophandler.h
qgsdetaileditemdata.h
qgsexpressionbuilderdialog.h
qgsfiledropedit.h
qgsgeometryrubberband.h
qgsgui.h
qgsguiutils.h

View File

@ -1,121 +0,0 @@
/***************************************************************************
qgsfiledropedit.cpp - File Dropable LineEdit
--------------------------------------
Date : 31-Jan-2007
Copyright : (C) 2007 by Tom Elwertowski
Email : telwertowski at users dot sourceforge dot net
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsfiledropedit.h"
#include <QDropEvent>
#include <QFileInfo>
#include <QPainter>
#include <QUrl>
#include <QMimeData>
QgsFileDropEdit::QgsFileDropEdit( QWidget *parent )
: QLineEdit( parent )
{
mDirOnly = false;
mFileOnly = true;
mDragActive = false;
setAcceptDrops( true );
}
void QgsFileDropEdit::setDirOnly( bool isDirOnly )
{
mDirOnly = isDirOnly;
if ( mDirOnly )
{
mFileOnly = false;
}
}
void QgsFileDropEdit::setFileOnly( bool isFileOnly )
{
mFileOnly = isFileOnly;
if ( mFileOnly )
{
mDirOnly = false;
}
}
void QgsFileDropEdit::setSuffixFilter( const QString &suffix )
{
mSuffix = suffix;
}
QString QgsFileDropEdit::acceptableFilePath( QDropEvent *event ) const
{
QString path;
if ( event->mimeData()->hasUrls() )
{
QFileInfo file( event->mimeData()->urls().first().toLocalFile() );
if ( !( ( mFileOnly && !file.isFile() ) ||
( mDirOnly && !file.isDir() ) ||
( !mSuffix.isEmpty() && mSuffix.compare( file.suffix(), Qt::CaseInsensitive ) ) ) )
path = file.filePath();
}
return path;
}
void QgsFileDropEdit::dragEnterEvent( QDragEnterEvent *event )
{
QString filePath = acceptableFilePath( event );
if ( !filePath.isEmpty() )
{
event->acceptProposedAction();
mDragActive = true;
update();
}
else
{
QLineEdit::dragEnterEvent( event );
}
}
void QgsFileDropEdit::dragLeaveEvent( QDragLeaveEvent *event )
{
QLineEdit::dragLeaveEvent( event );
event->accept();
mDragActive = false;
update();
}
void QgsFileDropEdit::dropEvent( QDropEvent *event )
{
QString filePath = acceptableFilePath( event );
if ( !filePath.isEmpty() )
{
setText( filePath );
selectAll();
setFocus( Qt::MouseFocusReason );
event->acceptProposedAction();
mDragActive = false;
update();
}
else
{
QLineEdit::dropEvent( event );
}
}
void QgsFileDropEdit::paintEvent( QPaintEvent *e )
{
QLineEdit::paintEvent( e );
if ( mDragActive )
{
QPainter p( this );
int width = 2; // width of highlight rectangle inside frame
p.setPen( QPen( palette().highlight(), width ) );
QRect r = rect().adjusted( width, width, -width, -width );
p.drawRect( r );
}
}

View File

@ -1,78 +0,0 @@
/***************************************************************************
qgsfiledropedit.h - File Dropable LineEdit
--------------------------------------
Date : 31-Jan-2007
Copyright : (C) 2007 by Tom Elwertowski
Email : telwertowski at users dot sourceforge dot net
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSFILEDROPEDIT_H
#define QGSFILEDROPEDIT_H
#include <QLineEdit>
#include "qgis.h"
#include "qgis_gui.h"
/** \ingroup gui
* A line edit for capturing file names that can have files dropped onto
* it via drag & drop.
*
* Dropping can be limited to files only, files with a specific extension
or directories only. By default, dropping is limited to files only.
*/
class GUI_EXPORT QgsFileDropEdit: public QLineEdit
{
Q_OBJECT
public:
QgsFileDropEdit( QWidget *parent SIP_TRANSFERTHIS = 0 );
bool isDirOnly() const { return mDirOnly; }
/**
Limit drops to directories.
*/
void setDirOnly( bool isDirOnly );
bool isFileOnly() const { return mFileOnly; }
/**
Limit drops to files.
*/
void setFileOnly( bool isFileOnly );
QString suffixFilter() const { return mSuffix; }
/**
Limit drops to files with specified extension.
*/
void setSuffixFilter( const QString &suffix );
protected:
virtual void dragEnterEvent( QDragEnterEvent *event ) override;
virtual void dragLeaveEvent( QDragLeaveEvent *event ) override;
virtual void dropEvent( QDropEvent *event ) override;
virtual void paintEvent( QPaintEvent *e ) override;
private:
/**
Return file name if object meets drop criteria.
*/
QString acceptableFilePath( QDropEvent *event ) const;
QString mSuffix;
bool mDirOnly;
bool mFileOnly;
bool mDragActive;
};
#endif

View File

@ -22,6 +22,7 @@
#include <QFileDialog>
#include <QGridLayout>
#include <QUrl>
#include <QDropEvent>
#include "qgssettings.h"
#include "qgsfilterlineedit.h"
@ -58,8 +59,8 @@ QgsFileWidget::QgsFileWidget( QWidget *parent )
mLinkLabel->setTextFormat( Qt::RichText );
mLinkLabel->hide(); // do not show by default
// otherwise, use the traditional QLineEdit
mLineEdit = new QgsFilterLineEdit( this );
// otherwise, use the traditional QLineEdit subclass
mLineEdit = new QgsFileDropEdit( this );
connect( mLineEdit, &QLineEdit::textChanged, this, &QgsFileWidget::textEdited );
mLayout->addWidget( mLineEdit );
@ -111,6 +112,7 @@ QString QgsFileWidget::filter() const
void QgsFileWidget::setFilter( const QString &filters )
{
mFilter = filters;
mLineEdit->setFilters( filters );
}
bool QgsFileWidget::fileWidgetButtonVisible() const
@ -181,6 +183,7 @@ QgsFileWidget::StorageMode QgsFileWidget::storageMode() const
void QgsFileWidget::setStorageMode( QgsFileWidget::StorageMode storageMode )
{
mStorageMode = storageMode;
mLineEdit->setStorageMode( storageMode );
}
QgsFileWidget::RelativeStorage QgsFileWidget::relativeStorage() const
@ -193,6 +196,11 @@ void QgsFileWidget::setRelativeStorage( QgsFileWidget::RelativeStorage relativeS
mRelativeStorage = relativeStorage;
}
QLineEdit *QgsFileWidget::lineEdit()
{
return mLineEdit;
}
void QgsFileWidget::openFileDialog()
{
QgsSettings settings;
@ -316,3 +324,101 @@ QString QgsFileWidget::toUrl( const QString &path ) const
return rep;
}
///@cond PRIVATE
QgsFileDropEdit::QgsFileDropEdit( QWidget *parent )
: QgsFilterLineEdit( parent )
{
mDragActive = false;
setAcceptDrops( true );
}
void QgsFileDropEdit::setFilters( const QString &filters )
{
mAcceptableExtensions.clear();
if ( filters.contains( QStringLiteral( "*.*" ) ) )
return; // everything is allowed!
QRegularExpression rx( "\\*\\.(\\w+)" );
QRegularExpressionMatchIterator i = rx.globalMatch( filters );
while ( i.hasNext() )
{
QRegularExpressionMatch match = i.next();
if ( match.hasMatch() )
{
mAcceptableExtensions << match.captured( 1 ).toLower();
}
}
}
QString QgsFileDropEdit::acceptableFilePath( QDropEvent *event ) const
{
QString path;
if ( event->mimeData()->hasUrls() )
{
QFileInfo file( event->mimeData()->urls().first().toLocalFile() );
if ( ( mStorageMode == QgsFileWidget::GetFile && file.isFile() &&
( mAcceptableExtensions.isEmpty() || mAcceptableExtensions.contains( file.suffix(), Qt::CaseInsensitive ) ) )
|| ( mStorageMode == QgsFileWidget::GetDirectory && file.isDir() ) )
path = file.filePath();
}
return path;
}
void QgsFileDropEdit::dragEnterEvent( QDragEnterEvent *event )
{
QString filePath = acceptableFilePath( event );
if ( !filePath.isEmpty() )
{
event->acceptProposedAction();
mDragActive = true;
update();
}
else
{
event->ignore();
}
}
void QgsFileDropEdit::dragLeaveEvent( QDragLeaveEvent *event )
{
QgsFilterLineEdit::dragLeaveEvent( event );
event->accept();
mDragActive = false;
update();
}
void QgsFileDropEdit::dropEvent( QDropEvent *event )
{
QString filePath = acceptableFilePath( event );
if ( !filePath.isEmpty() )
{
setText( filePath );
selectAll();
setFocus( Qt::MouseFocusReason );
event->acceptProposedAction();
mDragActive = false;
update();
}
}
void QgsFileDropEdit::paintEvent( QPaintEvent *e )
{
QgsFilterLineEdit::paintEvent( e );
if ( mDragActive )
{
QPainter p( this );
int width = 2; // width of highlight rectangle inside frame
p.setPen( QPen( palette().highlight(), width ) );
QRect r = rect().adjusted( width, width, -width, -width );
p.drawRect( r );
}
}
///@endcond

View File

@ -20,13 +20,13 @@
class QLabel;
class QToolButton;
class QVariant;
class QgsFilterLineEdit;
class QgsFileDropEdit;
class QHBoxLayout;
#include <QWidget>
#include "qgis_gui.h"
#include "qgis.h"
#include "qgsfilterlineedit.h"
/** \ingroup gui
* \brief The QgsFileWidget class creates a widget for selecting a file or a folder.
@ -137,6 +137,13 @@ class GUI_EXPORT QgsFileWidget : public QWidget
//! determines if the relative path is with respect to the project path or the default path
void setRelativeStorage( QgsFileWidget::RelativeStorage relativeStorage );
/**
* Returns a pointer to the widget's line edit, which can be used to customise
* the appearance and behavior of the line edit portion of the widget.
* \since QGIS 3.0
*/
QLineEdit *lineEdit();
signals:
//! emitted as soon as the current file or directory is changed
void fileChanged( const QString & );
@ -157,7 +164,7 @@ class GUI_EXPORT QgsFileWidget : public QWidget
RelativeStorage mRelativeStorage;
QLabel *mLinkLabel = nullptr;
QgsFilterLineEdit *mLineEdit = nullptr;
QgsFileDropEdit *mLineEdit = nullptr;
QToolButton *mFileWidgetButton = nullptr;
QHBoxLayout *mLayout = nullptr;
@ -170,4 +177,52 @@ class GUI_EXPORT QgsFileWidget : public QWidget
friend class TestQgsFileWidget;
};
///@cond PRIVATE
#ifndef SIP_RUN
/** \ingroup gui
* A line edit for capturing file names that can have files dropped onto
* it via drag & drop.
*
* Dropping can be limited to files only, files with a specific extension
* or directories only. By default, dropping is limited to files only.
* \note not available in Python bindings
*/
class GUI_EXPORT QgsFileDropEdit: public QgsFilterLineEdit
{
Q_OBJECT
public:
QgsFileDropEdit( QWidget *parent SIP_TRANSFERTHIS = 0 );
void setStorageMode( QgsFileWidget::StorageMode storageMode ) { mStorageMode = storageMode; }
void setFilters( const QString &filters );
protected:
virtual void dragEnterEvent( QDragEnterEvent *event ) override;
virtual void dragLeaveEvent( QDragLeaveEvent *event ) override;
virtual void dropEvent( QDropEvent *event ) override;
virtual void paintEvent( QPaintEvent *e ) override;
private:
/**
Return file name if object meets drop criteria.
*/
QString acceptableFilePath( QDropEvent *event ) const;
QStringList mAcceptableExtensions;
QgsFileWidget::StorageMode mStorageMode = QgsFileWidget::GetFile;
bool mDragActive;
friend class TestQgsFileWidget;
};
#endif
///@endcond
#endif // QGSFILEWIDGET_H

View File

@ -51,7 +51,7 @@ QgsGPSPluginGui::QgsGPSPluginGui( const BabelMap &importers,
// click it
pbnOK = buttonBox->button( QDialogButtonBox::Ok );
pbnOK->setEnabled( false );
connect( leGPXFile, &QLineEdit::textChanged,
connect( mFileWidget, &QgsFileWidget::fileChanged,
this, &QgsGPSPluginGui::enableRelevantControls );
connect( leIMPInput, &QLineEdit::textChanged,
this, &QgsGPSPluginGui::enableRelevantControls );
@ -75,7 +75,7 @@ QgsGPSPluginGui::QgsGPSPluginGui( const BabelMap &importers,
this, &QgsGPSPluginGui::enableRelevantControls );
// drag and drop filter
leGPXFile->setSuffixFilter( QStringLiteral( "gpx" ) );
mFileWidget->setFilter( tr( "GPX files (*.gpx)" ) );
}
QgsGPSPluginGui::~QgsGPSPluginGui()
@ -92,7 +92,7 @@ void QgsGPSPluginGui::on_buttonBox_accepted()
{
case 0:
// add a GPX layer?
emit loadGPXFile( leGPXFile->text(), cbGPXWaypoints->isChecked(),
emit loadGPXFile( mFileWidget->filePath(), cbGPXWaypoints->isChecked(),
cbGPXRoutes->isChecked(), cbGPXTracks->isChecked() );
break;
@ -180,7 +180,7 @@ void QgsGPSPluginGui::enableRelevantControls()
// load GPX
if ( tabWidget->currentIndex() == 0 )
{
if ( ( leGPXFile->text() == QLatin1String( "" ) ) )
if ( !mFileWidget->filePath().isEmpty() )
{
pbnOK->setEnabled( false );
cbGPXWaypoints->setEnabled( false );
@ -259,7 +259,7 @@ void QgsGPSPluginGui::on_pbnGPXSelectFile_clicked()
tr( "GPS eXchange format" ) + " (*.gpx)" );
if ( !myFileNameQString.isEmpty() )
{
leGPXFile->setText( myFileNameQString );
mFileWidget->setFilePath( myFileNameQString );
settings.setValue( QStringLiteral( "Plugin-GPS/gpxdirectory" ), QFileInfo( myFileNameQString ).absolutePath() );
}
}

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>647</width>
<height>277</height>
<width>1217</width>
<height>504</height>
</rect>
</property>
<property name="windowTitle">
@ -15,8 +15,7 @@
</property>
<property name="windowIcon">
<iconset>
<normaloff/>
</iconset>
<normaloff>.</normaloff>.</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
@ -30,25 +29,15 @@
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<item row="0" column="1">
<widget class="QgsFileWidget" name="mFileWidget" native="true"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblGPXFile">
<property name="text">
<string>File</string>
</property>
<property name="buddy">
<cstring>pbnGPXSelectFile</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsFileDropEdit" name="leGPXFile"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pbnGPXSelectFile">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
@ -655,15 +644,14 @@
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QgsFileDropEdit</class>
<extends>QLineEdit</extends>
<header>qgsfiledropedit.h</header>
<class>QgsFileWidget</class>
<extends>QWidget</extends>
<header>qgsfilewidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>leGPXFile</tabstop>
<tabstop>pbnGPXSelectFile</tabstop>
<tabstop>cbGPXWaypoints</tabstop>
<tabstop>cbGPXRoutes</tabstop>
<tabstop>cbGPXTracks</tabstop>
@ -693,8 +681,6 @@
<tabstop>leCONVLayer</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="qgsgps_plugin.qrc"/>
</resources>
<resources/>
<connections/>
</ui>

View File

@ -17,6 +17,7 @@
#include "qgstest.h"
#include "qgsfilewidget.h"
#include <memory>
class TestQgsFileWidget: public QObject
{
@ -29,6 +30,7 @@ class TestQgsFileWidget: public QObject
void relativePath();
void toUrl();
void testDroppedFiles();
};
@ -81,7 +83,62 @@ void TestQgsFileWidget::toUrl()
QCOMPARE( w->toUrl( "../test2/file6.ext" ), QString( "<a href=\"file:///home/test2/file6.ext\">file6.ext</a>" ) );
}
void TestQgsFileWidget::testDroppedFiles()
{
QgsFileWidget *w = new QgsFileWidget();
w->setStorageMode( QgsFileWidget::GetFile );
// should not accept dropped folders
std::unique_ptr< QMimeData > mime( new QMimeData() );
mime->setUrls( QList<QUrl>() << QUrl::fromLocalFile( TEST_DATA_DIR ) );
std::unique_ptr< QDropEvent > event( new QDropEvent( QPointF( 1, 1 ), Qt::CopyAction, mime.get(), Qt::LeftButton, Qt::NoModifier ) );
qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() );
QVERIFY( w->lineEdit()->text().isEmpty() );
// but dropped files should be fine
mime->setUrls( QList<QUrl>() << QUrl::fromLocalFile( TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) ) );
event.reset( new QDropEvent( QPointF( 1, 1 ), Qt::CopyAction, mime.get(), Qt::LeftButton, Qt::NoModifier ) );
qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() );
QCOMPARE( w->lineEdit()->text(), TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) );
// with file filter
w->setFilter( QStringLiteral( "Data (*.shp)" ) );
w->setFilePath( QString() );
qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() );
QCOMPARE( w->lineEdit()->text(), TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) );
w->setFilePath( QString() );
// should be rejected, not compatible with filter
mime->setUrls( QList<QUrl>() << QUrl::fromLocalFile( TEST_DATA_DIR + QStringLiteral( "/encoded_html.html" ) ) );
event.reset( new QDropEvent( QPointF( 1, 1 ), Qt::CopyAction, mime.get(), Qt::LeftButton, Qt::NoModifier ) );
qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() );
QVERIFY( w->lineEdit()->text().isEmpty() );
// new filter, should be allowed now
w->setFilter( QStringLiteral( "Data (*.shp);;HTML (*.HTML)" ) );
qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() );
QCOMPARE( w->lineEdit()->text(), TEST_DATA_DIR + QStringLiteral( "/encoded_html.html" ) );
//try with wildcard filter
w->setFilter( QStringLiteral( "All files (*.*);;Data (*.shp);;HTML (*.HTML)" ) );
mime->setUrls( QList<QUrl>() << QUrl::fromLocalFile( TEST_DATA_DIR + QStringLiteral( "/bug5598.prj" ) ) );
event.reset( new QDropEvent( QPointF( 1, 1 ), Qt::CopyAction, mime.get(), Qt::LeftButton, Qt::NoModifier ) );
qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() );
QCOMPARE( w->lineEdit()->text(), TEST_DATA_DIR + QStringLiteral( "/bug5598.prj" ) );
// try with folders
w->setStorageMode( QgsFileWidget::GetDirectory );
w->setFilePath( QString() );
// should be rejected
qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() );
QVERIFY( w->lineEdit()->text().isEmpty() );
// but dropping a folder should work
mime->setUrls( QList<QUrl>() << QUrl::fromLocalFile( TEST_DATA_DIR ) );
event.reset( new QDropEvent( QPointF( 1, 1 ), Qt::CopyAction, mime.get(), Qt::LeftButton, Qt::NoModifier ) );
qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() );
QCOMPARE( w->lineEdit()->text(), QString( TEST_DATA_DIR ) );
}
QGSTEST_MAIN( TestQgsFileWidget )
#include "testqgsfilewidget.moc"