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)