20 #include <QToolButton>
22 #include <QGridLayout>
25 #include <QRegularExpression>
27 #include "qgssettings.h"
39 setBackgroundRole( QPalette::Window );
40 setAutoFillBackground(
true );
42 mLayout =
new QHBoxLayout();
43 mLayout->setContentsMargins( 0, 0, 0, 0 );
46 mLinkLabel =
new QLabel(
this );
48 mLinkLabel->setOpenExternalLinks(
true );
51 mLinkLabel->setEnabled(
true );
52 mLinkLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
53 mLinkLabel->setTextFormat( Qt::RichText );
55 mLinkEditButton =
new QToolButton(
this );
57 connect( mLinkEditButton, &QToolButton::clicked,
this, &QgsFileWidget::editLink );
58 mLinkEditButton->hide();
61 mLineEdit =
new QgsFileDropEdit(
this );
62 mLineEdit->setDragEnabled(
true );
63 mLineEdit->setToolTip( tr(
"Full path to the file(s), including name and extension" ) );
64 connect( mLineEdit, &QLineEdit::textChanged,
this, &QgsFileWidget::textEdited );
65 mLayout->addWidget( mLineEdit );
67 mFileWidgetButton =
new QToolButton(
this );
68 mFileWidgetButton->setText( QChar( 0x2026 ) );
69 mFileWidgetButton->setToolTip( tr(
"Browse" ) );
70 connect( mFileWidgetButton, &QAbstractButton::clicked,
this, &QgsFileWidget::openFileDialog );
71 mLayout->addWidget( mFileWidgetButton );
84 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
85 const QStringList pathParts = path.split( QRegExp(
"\"\\s+\"" ), QString::SkipEmptyParts );
87 const QStringList pathParts = path.split( QRegExp(
"\"\\s+\"" ), Qt::SkipEmptyParts );
89 for (
const auto &pathsPart : pathParts )
91 QString cleaned = pathsPart;
92 cleaned.remove( QRegExp(
"(^\\s*\")|(\"\\s*)" ) );
93 paths.append( cleaned );
101 mLineEdit->setValue( path );
106 if ( mReadOnly == readOnly )
109 mReadOnly = readOnly;
121 mDialogTitle = title;
132 mLineEdit->setFilters( filters );
147 return mButtonVisible;
152 mButtonVisible = visible;
153 mFileWidgetButton->setVisible( visible );
156 void QgsFileWidget::textEdited(
const QString &path )
159 mLinkLabel->setText( toUrl( path ) );
161 if ( path.contains( QStringLiteral(
"\" \"" ) ) )
163 mLineEdit->setToolTip( tr(
"Selected files:<br><ul><li>%1</li></ul><br>" ).arg(
splitFilePaths( path ).join( QLatin1String(
"</li><li>" ) ) ) );
167 mLineEdit->setToolTip( QString() );
172 void QgsFileWidget::editLink()
174 if ( !mUseLink || mReadOnly )
177 mIsLinkEdited = !mIsLinkEdited;
228 return mRelativeStorage;
241 void QgsFileWidget::updateLayout()
243 mLayout->removeWidget( mLineEdit );
244 mLayout->removeWidget( mLinkLabel );
245 mLayout->removeWidget( mLinkEditButton );
247 mLinkEditButton->setVisible( mUseLink && !mReadOnly );
249 mFileWidgetButton->setEnabled( !mReadOnly );
250 mLineEdit->setEnabled( !mReadOnly );
252 if ( mUseLink && !mIsLinkEdited )
254 mLayout->insertWidget( 0, mLinkLabel );
255 mLineEdit->setVisible(
false );
256 mLinkLabel->setVisible(
true );
260 mLayout->insertWidget( 1, mLinkEditButton );
266 mLayout->insertWidget( 0, mLineEdit );
267 mLineEdit->setVisible(
true );
268 mLinkLabel->setVisible(
false );
272 mLayout->insertWidget( 1, mLinkEditButton );
278 void QgsFileWidget::openFileDialog()
280 QgsSettings settings;
285 if ( !mFilePath.isEmpty() )
287 oldPath = relativePath( mFilePath,
false );
291 else if ( !mDefaultRoot.isEmpty() )
293 oldPath = QDir::cleanPath( mDefaultRoot );
297 QUrl url = QUrl::fromUserInput( oldPath );
298 if ( !url.isValid() )
300 QString defPath = QDir::cleanPath( QFileInfo(
QgsProject::instance()->absoluteFilePath() ).path() );
301 if ( defPath.isEmpty() )
303 defPath = QDir::homePath();
305 oldPath = settings.value( QStringLiteral(
"UI/lastFileNameWidgetDir" ), defPath ).toString();
310 QStringList fileNames;
315 switch ( mStorageMode )
318 title = !mDialogTitle.isEmpty() ? mDialogTitle : tr(
"Select a file" );
319 fileName = QFileDialog::getOpenFileName(
this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter, &mSelectedFilter, mOptions );
322 title = !mDialogTitle.isEmpty() ? mDialogTitle : tr(
"Select one or more files" );
323 fileNames = QFileDialog::getOpenFileNames(
this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter, &mSelectedFilter, mOptions );
326 title = !mDialogTitle.isEmpty() ? mDialogTitle : tr(
"Select a directory" );
327 fileName = QFileDialog::getExistingDirectory(
this, title, QFileInfo( oldPath ).absoluteFilePath(), mOptions | QFileDialog::ShowDirsOnly );
331 title = !mDialogTitle.isEmpty() ? mDialogTitle : tr(
"Create or select a file" );
334 fileName = QFileDialog::getSaveFileName(
this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter, &mSelectedFilter, mOptions | QFileDialog::DontConfirmOverwrite );
338 fileName = QFileDialog::getSaveFileName(
this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter, &mSelectedFilter, mOptions );
349 if ( fileName.isEmpty() && fileNames.isEmpty( ) )
354 fileName = QDir::toNativeSeparators( QDir::cleanPath( QFileInfo( fileName ).absoluteFilePath() ) );
358 for (
int i = 0; i < fileNames.length(); i++ )
360 fileNames.replace( i, QDir::toNativeSeparators( QDir::cleanPath( QFileInfo( fileNames.at( i ) ).absoluteFilePath() ) ) );
365 switch ( mStorageMode )
369 settings.setValue( QStringLiteral(
"UI/lastFileNameWidgetDir" ), QFileInfo( fileName ).absolutePath() );
372 settings.setValue( QStringLiteral(
"UI/lastFileNameWidgetDir" ), fileName );
375 settings.setValue( QStringLiteral(
"UI/lastFileNameWidgetDir" ), QFileInfo( fileNames.first( ) ).absolutePath() );
382 fileName = relativePath( fileName,
true );
387 for (
int i = 0; i < fileNames.length(); i++ )
389 fileNames.replace( i, relativePath( fileNames.at( i ),
true ) );
391 if ( fileNames.length() > 1 )
393 setFilePath( QStringLiteral(
"\"%1\"" ).arg( fileNames.join( QLatin1String(
"\" \"" ) ) ) );
403 QString QgsFileWidget::relativePath(
const QString &filePath,
bool removeRelative )
const
405 QString RelativePath;
408 RelativePath = QDir::toNativeSeparators( QDir::cleanPath( QFileInfo(
QgsProject::instance()->absoluteFilePath() ).path() ) );
412 RelativePath = QDir::toNativeSeparators( QDir::cleanPath( mDefaultRoot ) );
415 if ( !RelativePath.isEmpty() )
417 if ( removeRelative )
419 return QDir::cleanPath( QDir( RelativePath ).relativeFilePath(
filePath ) );
431 QString QgsFileWidget::toUrl(
const QString &path )
const
434 if ( path.isEmpty() )
439 QString urlStr = relativePath( path,
false );
440 QUrl url = QUrl::fromUserInput( urlStr );
441 if ( !url.isValid() || !url.isLocalFile() )
443 QgsDebugMsgLevel( QStringLiteral(
"URL: %1 is not valid or not a local file!" ).arg( path ), 2 );
447 QString pathStr = url.toString();
450 rep = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( pathStr, path );
454 QString fileName = QFileInfo( urlStr ).fileName();
455 rep = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( pathStr, fileName );
466 QgsFileDropEdit::QgsFileDropEdit( QWidget *parent )
469 setAcceptDrops(
true );
472 void QgsFileDropEdit::setFilters(
const QString &filters )
474 mAcceptableExtensions.clear();
476 if ( filters.contains( QStringLiteral(
"*.*" ) ) )
479 QRegularExpression rx( QStringLiteral(
"\\*\\.(\\w+)" ) );
480 QRegularExpressionMatchIterator i = rx.globalMatch( filters );
481 while ( i.hasNext() )
483 QRegularExpressionMatch match = i.next();
484 if ( match.hasMatch() )
486 mAcceptableExtensions << match.captured( 1 ).toLower();
491 QString QgsFileDropEdit::acceptableFilePath( QDropEvent *event )
const
493 QStringList rawPaths;
495 if ( event->mimeData()->hasUrls() )
497 const QList< QUrl > urls =
event->mimeData()->urls();
498 rawPaths.reserve( urls.count() );
499 for (
const QUrl &url : urls )
501 const QString local = url.toLocalFile();
502 if ( !rawPaths.contains( local ) )
503 rawPaths.append( local );
510 if ( !rawPaths.contains( u.uri ) )
511 rawPaths.append( u.uri );
514 if ( !event->mimeData()->text().isEmpty() && !rawPaths.contains( event->mimeData()->text() ) )
515 rawPaths.append( event->mimeData()->text() );
517 paths.reserve( rawPaths.count() );
518 for (
const QString &path : std::as_const( rawPaths ) )
520 QFileInfo file( path );
521 switch ( mStorageMode )
527 if ( file.isFile() && ( mAcceptableExtensions.isEmpty() || mAcceptableExtensions.contains( file.suffix(), Qt::CaseInsensitive ) ) )
528 paths.append( file.filePath() );
536 paths.append( file.filePath() );
537 else if ( file.isFile() )
540 paths.append( file.absolutePath() );
548 if ( paths.size() > 1 )
550 return QStringLiteral(
"\"%1\"" ).arg( paths.join( QLatin1String(
"\" \"" ) ) );
552 else if ( paths.size() == 1 )
554 return paths.first();
562 void QgsFileDropEdit::dragEnterEvent( QDragEnterEvent *event )
564 QString filePath = acceptableFilePath( event );
565 if ( !filePath.isEmpty() )
567 event->acceptProposedAction();
568 setHighlighted(
true );
576 void QgsFileDropEdit::dragLeaveEvent( QDragLeaveEvent *event )
578 QgsFilterLineEdit::dragLeaveEvent( event );
580 setHighlighted(
false );
583 void QgsFileDropEdit::dropEvent( QDropEvent *event )
585 QString filePath = acceptableFilePath( event );
586 if ( !filePath.isEmpty() )
590 setFocus( Qt::MouseFocusReason );
591 event->acceptProposedAction();
592 setHighlighted(
false );
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString addExtensionFromFilter(const QString &fileName, const QString &filter)
Ensures that a fileName ends with an extension from the specified filter string.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
Trick to keep a widget focused and avoid QT crashes.
A QgsFilterLineEdit subclass with the ability to "highlight" the edges of the widget.
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
static QgsProject * instance()
Returns the QgsProject singleton instance.
#define QgsDebugMsgLevel(str, level)