32#include <QRegularExpression>
36#include "moc_qgsfilewidget.cpp"
42 mLayout->setContentsMargins( 0, 0, 0, 0 );
51 mLinkLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
59 mLineEdit->setToolTip( tr(
"Full path to the file(s), including name and extension" ) );
60 connect(
mLineEdit, &QLineEdit::textChanged,
this, &QgsFileWidget::textEdited );
61 connect(
mLineEdit, &QgsFileDropEdit::fileDropped,
this, &QgsFileWidget::fileDropped );
67 connect(
mLinkEditButton, &QToolButton::clicked,
this, &QgsFileWidget::editLink );
73 connect(
mFileWidgetButton, &QAbstractButton::clicked,
this, &QgsFileWidget::openFileDialog );
86 QStringList pathParts;
91 const thread_local QRegularExpression partSeparatorsRegex = QRegularExpression( QStringLiteral(
"(?:\")(\\s+)(?:\")" ) );
92 QRegularExpressionMatchIterator partSeparatorMatches = partSeparatorsRegex.globalMatch( path );
93 int substringStart = 0;
94 while ( partSeparatorMatches.hasNext() )
96 QRegularExpressionMatch match = partSeparatorMatches.next();
97 int substringEnd = match.capturedStart() + 1;
98 int substringLength = substringEnd - substringStart;
99 pathParts.append( path.mid( substringStart, substringLength ) );
100 substringStart = match.capturedEnd() - 1;
101 if ( !partSeparatorMatches.hasNext() )
103 pathParts.append( path.mid( substringStart ) );
106 if ( pathParts.length() == 0 )
108 pathParts.append( path );
112 const thread_local QRegularExpression doubleQuoteWrappedRegex( QStringLiteral(
"(?:^\\s*\")(.+)(?:\"\\s*$)" ) );
113 for (
const QString &pathsPart : pathParts )
115 QRegularExpressionMatch match = doubleQuoteWrappedRegex.match( pathsPart );
117 if ( match.hasMatch() )
119 finalPath = match.captured( 1 );
123 finalPath = pathsPart;
125 paths.append( finalPath );
190 return path.contains( QStringLiteral(
"\" \"" ) );
193void QgsFileWidget::textEdited(
const QString &path )
200 mLineEdit->setToolTip( tr(
"Selected files:<br><ul><li>%1</li></ul><br>" ).arg(
splitFilePaths( path ).join( QLatin1String(
"</li><li>" ) ) ) );
209void QgsFileWidget::editLink()
218void QgsFileWidget::fileDropped(
const QString &filePath )
222 mLineEdit->setFocus( Qt::MouseFocusReason );
299void QgsFileWidget::openFileDialog()
318 QUrl url = QUrl::fromUserInput( oldPath );
319 if ( !url.isValid() )
321 QString defPath = QDir::cleanPath( QFileInfo(
QgsProject::instance()->absoluteFilePath() ).path() );
322 if ( defPath.isEmpty() )
324 defPath = QDir::homePath();
326 oldPath = settings.
value( QStringLiteral(
"UI/lastFileNameWidgetDir" ), defPath ).toString();
331 QStringList fileNames;
335 QgsFocusKeeper focusKeeper;
348 fileName = QFileDialog::getExistingDirectory(
this, title, QFileInfo( oldPath ).absoluteFilePath(),
mOptions );
355 fileName = QFileDialog::getSaveFileName(
this, title, QFileInfo( oldPath ).absoluteFilePath(),
mFilter, &
mSelectedFilter,
mOptions | QFileDialog::DontConfirmOverwrite );
371 if (
mFilter.contains( QLatin1String(
"(*.gdb *.GDB gdb)" ) ) && ( fileName.endsWith( QLatin1String(
"/gdb.gdb" ) ) || fileName.endsWith( QLatin1String(
"\\gdb.gdb" ) ) ) )
373 fileName.chop(
static_cast<int>( strlen(
"/gdb.gdb" ) ) );
384 if ( fileName.isEmpty() && fileNames.isEmpty() )
388 fileNames << fileName;
390 for (
int i = 0; i < fileNames.length(); i++ )
392 fileNames.replace( i, QDir::toNativeSeparators( QDir::cleanPath( QFileInfo( fileNames.at( i ) ).absoluteFilePath() ) ) );
401 settings.
setValue( QStringLiteral(
"UI/lastFileNameWidgetDir" ), QFileInfo( fileNames.first() ).absolutePath() );
404 settings.
setValue( QStringLiteral(
"UI/lastFileNameWidgetDir" ), fileNames.first() );
413 Q_ASSERT( fileNames.count() );
416 for (
int i = 0; i < fileNames.length(); i++ )
418 fileNames.replace( i,
relativePath( fileNames.at( i ),
true ) );
432 if ( filePaths.length() > 1 )
434 setFilePath( QStringLiteral(
"\"%1\"" ).arg( filePaths.join( QLatin1String(
"\" \"" ) ) ) );
445 QString RelativePath;
448 RelativePath = QDir::toNativeSeparators( QDir::cleanPath( QFileInfo(
QgsProject::instance()->absoluteFilePath() ).path() ) );
452 RelativePath = QDir::toNativeSeparators( QDir::cleanPath(
mDefaultRoot ) );
455 if ( !RelativePath.isEmpty() )
457 if ( removeRelative )
459 return QDir::cleanPath( QDir( RelativePath ).relativeFilePath(
filePath ) );
472 QSize size {
mLineEdit->minimumSizeHint() };
474 size.setWidth( size.width() + btnSize.width() );
475 size.setHeight( std::max( size.height(), btnSize.height() ) );
490 return QStringLiteral(
"<a>%1</a>" ).arg( path );
494 QUrl url = QUrl::fromUserInput( urlStr );
495 if ( !url.isValid() || !url.isLocalFile() )
497 QgsDebugMsgLevel( QStringLiteral(
"URL: %1 is not valid or not a local file!" ).arg( path ), 2 );
501 QString pathStr = url.toString();
504 rep = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( pathStr, path );
508 QString fileName = QFileInfo( urlStr ).fileName();
509 rep = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( pathStr, fileName );
519QgsFileDropEdit::QgsFileDropEdit( QWidget *parent )
522 setAcceptDrops(
true );
525void QgsFileDropEdit::setFilters(
const QString &filters )
527 mAcceptableExtensions.clear();
529 if ( filters.contains( QStringLiteral(
"*.*" ) ) )
532 const thread_local QRegularExpression rx( QStringLiteral(
"\\*\\.(\\w+)" ) );
533 QRegularExpressionMatchIterator i = rx.globalMatch( filters );
534 while ( i.hasNext() )
536 QRegularExpressionMatch match = i.next();
537 if ( match.hasMatch() )
539 mAcceptableExtensions << match.captured( 1 ).toLower();
544QStringList QgsFileDropEdit::acceptableFilePaths( QDropEvent *event )
const
546 QStringList rawPaths;
548 if ( event->mimeData()->hasUrls() )
550 const QList<QUrl> urls =
event->mimeData()->urls();
551 rawPaths.reserve( urls.count() );
552 for (
const QUrl &url : urls )
554 const QString local = url.toLocalFile();
555 if ( !rawPaths.contains( local ) )
556 rawPaths.append( local );
563 if ( !rawPaths.contains( u.uri ) )
564 rawPaths.append( u.uri );
567 if ( !event->mimeData()->text().isEmpty() && !rawPaths.contains( event->mimeData()->text() ) )
568 rawPaths.append( event->mimeData()->text() );
570 paths.reserve( rawPaths.count() );
571 for (
const QString &path : std::as_const( rawPaths ) )
573 QFileInfo file( path );
574 switch ( mStorageMode )
580 if ( file.isFile() && ( mAcceptableExtensions.isEmpty() || mAcceptableExtensions.contains( file.suffix(), Qt::CaseInsensitive ) ) )
581 paths.append( file.filePath() );
589 paths.append( file.filePath() );
590 else if ( file.isFile() )
593 paths.append( file.absolutePath() );
604QString QgsFileDropEdit::acceptableFilePath( QDropEvent *event )
const
606 const QStringList paths = acceptableFilePaths( event );
607 if ( paths.size() > 1 )
609 return QStringLiteral(
"\"%1\"" ).arg( paths.join( QLatin1String(
"\" \"" ) ) );
611 else if ( paths.size() == 1 )
613 return paths.first();
621void QgsFileDropEdit::dragEnterEvent( QDragEnterEvent *event )
623 QString filePath = acceptableFilePath( event );
624 if ( !filePath.isEmpty() )
626 event->acceptProposedAction();
627 setHighlighted(
true );
635void QgsFileDropEdit::dragLeaveEvent( QDragLeaveEvent *event )
637 QgsFilterLineEdit::dragLeaveEvent( event );
639 setHighlighted(
false );
642void QgsFileDropEdit::dropEvent( QDropEvent *event )
644 QString filePath = acceptableFilePath( event );
645 if ( !filePath.isEmpty() )
647 event->acceptProposedAction();
648 emit fileDropped( filePath );
651 setHighlighted(
false );
static QString nullRepresentation()
Returns the string 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...
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.
Stores settings for use within QGIS.
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)