1 // Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File: QtxPathDialog.cxx
24 // Author: Sergey TELKOV
26 #include "QtxPathDialog.h"
28 #include "QtxGridBox.h"
29 #include "QtxGroupBox.h"
36 #include <QObjectList>
37 #include <QStringList>
38 #include <QFileDialog>
39 #include <QMessageBox>
40 #include <QPushButton>
42 static const char* open_icon[] = {
69 \brief The QtxPathDialog class provides a simple convenience dialog to
70 enter a path to the file or to the directory.
72 The QtxPathDialog class adds possibility to browse the file system
73 with help of standard Open/Save dialog boxes or enter the file/directory
76 Default implementation provides only one "standard" file entry.
77 Sometimes it is necessary to select several different files/directories
78 from the same dialog box. In this case it is possible to derive from the
79 QtxPathDialog class and use createFileEntry() method to add required
80 number of file entries.
85 \param import if \c true, the dialog box is shown for "open" mode,
86 otherwise, it is shown in the "save" mode
87 \param parent parent widget
88 \param modal if \c true, the dialog box should be modal
89 \param resize if \c true, the dialog box is resizable
90 \param buttons required buttons (QtxDialog::ButtonFlags)
93 QtxPathDialog::QtxPathDialog( const bool import, QWidget* parent, const bool modal,
94 const bool resize, const int buttons, Qt::WindowFlags f )
95 : QtxDialog( parent, modal, resize, buttons, f ),
102 setWindowTitle( tr( import ? "Open file" : "Save file" ) );
104 setDefaultEntry( createFileEntry( tr( "File name" ), import ? OpenFile : SaveFile ) );
105 QLineEdit* le = fileEntry( defaultEntry() );
107 le->setMinimumWidth( 200 );
118 \param parent parent widget
119 \param modal if \c true, the dialog box should be modal
120 \param resize if \c true, the dialog box is resizable
121 \param buttons required buttons (QtxDialog::ButtonFlags)
122 \param f window flags
124 QtxPathDialog::QtxPathDialog( QWidget* parent, const bool modal,
125 const bool resize, const int buttons, Qt::WindowFlags f )
126 : QtxDialog( parent, modal, resize, buttons, f ),
139 QtxPathDialog::~QtxPathDialog()
144 \brief Get selected file name.
147 QString QtxPathDialog::fileName() const
149 return fileName( defaultEntry() );
153 \brief Set the file name.
154 \param txt new file name
155 \param autoExtension if \c true an extension is determined automatically by file
157 void QtxPathDialog::setFileName( const QString& txt, const bool autoExtension )
159 setFileName( defaultEntry(), txt, autoExtension );
163 \brief Get current file filter.
166 QString QtxPathDialog::filter() const
168 return filter( defaultEntry() );
172 \brief Change file filter.
174 Filter is a list of file masks, separated by ';;'. For example,
177 \param fltr new file filter
179 void QtxPathDialog::setFilter( const QString& fltr )
181 setFilter( defaultEntry(), fltr );
185 \brief Show/hide the path dialog box/
186 \param on new visibility state
188 void QtxPathDialog::setVisible( bool on )
193 QtxDialog::setVisible( on );
197 \brief Called when user clicks a "browse" button
198 to open standard file dialog.
200 void QtxPathDialog::onBrowse()
202 const QObject* obj = sender();
206 for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && id == -1; ++it )
208 if ( it.value().btn == obj )
215 FileEntry& entry = myEntries[id];
217 bool isDir = entry.mode != OpenFile && entry.mode != SaveFile;
221 entry.dlg = new QFileDialog( this, windowTitle(), QDir::current().path() );
222 switch ( entry.mode )
228 entry.dlg->setFileMode( QFileDialog::DirectoryOnly );
231 entry.dlg->setFileMode( QFileDialog::AnyFile );
235 entry.dlg->setFileMode( QFileDialog::ExistingFiles );
242 QStringList fList = prepareFilters( entry.filter );
243 if ( !fList.isEmpty() )
244 entry.dlg->setNameFilters( fList );
246 entry.dlg->selectFile( fileName( id ) );
248 if ( entry.dlg->exec() != Accepted )
251 QStringList fileList = entry.dlg->selectedFiles();
252 QString fName = !fileList.isEmpty() ? fileList.first() : QString();
254 if ( fName.isEmpty() )
257 if ( Qtx::extension( fName ).isEmpty() && !isDir )
258 fName = autoExtension( fName, entry.dlg->selectedNameFilter() );
260 fName = QDir::toNativeSeparators( fName );
261 QString prev = QDir::toNativeSeparators( fileName( id ) );
264 while ( prev.length() && prev.at( prev.length() - 1 ) == QDir::separator() )
265 prev.remove( prev.length() - 1, 1 );
266 while ( fName.length() && fName.at( fName.length() - 1 ) == QDir::separator() )
267 fName.remove( fName.length() - 1, 1 );
273 setFileName( id, fName );
274 fileNameChanged( id, fName );
276 if ( id == defaultEntry() )
277 emit fileNameChanged( fName );
281 \brief Called when user presses \c Return key being in the line edit.
283 void QtxPathDialog::onReturnPressed()
285 const QObject* obj = sender();
288 for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && id == -1; ++it )
290 if ( it.value().edit == obj )
297 fileNameChanged( id, fileName( id ) );
299 if ( id == defaultEntry() )
300 emit fileNameChanged( fileName() );
304 \brief Called when the text in the line edit is changed by the user.
305 \param txt current text (not used)
307 void QtxPathDialog::onTextChanged( const QString& /*txt*/ )
313 \brief Check validity of the entered text and enable/disable standard
314 \c OK, \c Yes buttons.
316 void QtxPathDialog::validate()
318 setButtonEnabled( isValid(), OK | Yes );
322 \brief Check if the entered file/directory name is valid.
323 \return \c true if selected file name is valid
325 bool QtxPathDialog::isValid()
328 for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && ok; ++it )
330 if ( it.value().edit->isEnabled() )
331 ok = !it.value().edit->text().trimmed().isEmpty();
338 \brief Check if the entered data is acceptable.
339 \return \c true if entered data is acceptable
341 bool QtxPathDialog::acceptData() const
345 QWidget* parent = (QWidget*)this;
347 FileEntryMap::ConstIterator it;
348 for ( it = myEntries.begin(); it != myEntries.end() && ok; ++it )
350 const FileEntry& entry = it.value();
351 QFileInfo fileInfo( entry.edit->text() );
352 if ( entry.edit->text().isEmpty() )
354 QMessageBox::critical( parent, windowTitle(), tr( "File name not specified" ),
355 QMessageBox::Ok, QMessageBox::NoButton );
358 else switch ( entry.mode )
361 if ( !fileInfo.exists() )
363 QMessageBox::critical( parent, windowTitle(), tr( "File \"%1\" does not exist" ).arg( fileInfo.filePath() ),
364 QMessageBox::Ok, QMessageBox::NoButton );
369 if ( fileInfo.exists() )
370 ok = QMessageBox::warning( parent, windowTitle(), tr( "File \"%1\" already exist. Do you want to overwrite it?" ).arg( fileInfo.filePath() ),
371 QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
374 if ( !fileInfo.exists() || !fileInfo.isDir() )
376 QMessageBox::critical( parent, windowTitle(), tr( "Directory \"%1\" does not exist" ).arg( fileInfo.filePath() ),
377 QMessageBox::Ok, QMessageBox::NoButton );
382 if ( fileInfo.exists() && !fileInfo.isDir() )
384 QMessageBox::critical( parent, windowTitle(), tr( "Directory \"%1\" can't be created because file with the same name exist" ).arg( fileInfo.filePath() ),
385 QMessageBox::Ok, QMessageBox::NoButton );
390 if ( fileInfo.exists() )
392 if ( !fileInfo.isDir() )
394 QMessageBox::critical( parent, windowTitle(), tr( "Directory \"%1\" can't be created because file with the same name exist" ).arg( fileInfo.filePath() ),
395 QMessageBox::Ok, QMessageBox::NoButton );
398 else if ( QDir( fileInfo.filePath() ).count() > 2 )
399 ok = QMessageBox::warning( parent, windowTitle(), tr( "Directory \"%1\" not empty. Do you want to remove all files in this directory?" ).arg( fileInfo.filePath() ),
400 QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
408 entry.edit->setFocus();
415 \brief Perform custom actions when the file name is changed.
417 This method can be redefined in the successor classes.
418 Default implementation does nothing.
421 \param fileName file name
423 void QtxPathDialog::fileNameChanged( int /*id*/, QString /*fileName*/ )
428 \fn void QtxPathDialog::fileNameChanged( QString fileName );
429 \brief Emitted when the file name is changed.
430 \param fileName file name
434 \brief Get options grame widget.
435 \return options frame widget
437 QFrame* QtxPathDialog::optionsFrame()
439 return myOptionsFrame;
443 \brief Get file name from specified entry.
444 \param id file entry ID
445 \return file name or null string if \a id is invalid
447 QString QtxPathDialog::fileName( const int id ) const
450 if ( myEntries.contains( id ) )
451 res = myEntries[id].edit->text();
456 \brief Change file name by specified file entry.
457 \param id file entry ID
458 \param txt new file name
459 \param autoExt if \c true, assign extension automatically
461 void QtxPathDialog::setFileName( const int id, const QString& txt, const bool autoExt )
464 QLineEdit* le = fileEntry( id, mode );
468 if ( autoExt && ( mode == OpenFile || mode == SaveFile ) )
469 le->setText( autoExtension( txt, filter( id ) ) );
476 \brief Get file filter from the specified file entry.
477 \param id file entry ID
478 \return file filter or null string if \a id is invalid
480 QString QtxPathDialog::filter( const int id ) const
483 if ( myEntries.contains( id ) )
484 res = myEntries[id].filter;
489 \brief Set file filter to the specified file entry.
490 \param id file entry ID
491 \param filter file filter or null string if \a id is invalid
493 void QtxPathDialog::setFilter( const int id, const QString& filter )
495 if ( myEntries.contains( id ) )
496 myEntries[id].filter = filter;
500 \brief Get line edit widget for the specified file entry.
501 \param id file entry ID
502 \return line edit widget or 0 if \a id is invalid
504 QLineEdit* QtxPathDialog::fileEntry( const int id ) const
507 if ( myEntries.contains( id ) )
508 le = myEntries[id].edit;
514 \brief Get line edit widget and file mode for the specified file entry.
515 \param id file entry ID
516 \param theMode to return file entry mode
517 \return line edit widget or 0 if \a id is invalid
519 QLineEdit* QtxPathDialog::fileEntry( const int theId, int& theMode ) const
522 if ( myEntries.contains( theId ) )
524 le = myEntries[theId].edit;
525 theMode = myEntries[theId].mode;
532 \brief Create new file entry.
534 If required file entry is already in use or if specified \a id is < 0,
535 new ID is generated and returned.
537 \param lab file entry title
538 \param mode file entry mode
539 \param id required file entry ID
540 \return created file entry ID
542 int QtxPathDialog::createFileEntry( const QString& lab, const int mode,
543 const QString& filter, const int id )
549 while ( myEntries.contains( num ) )
556 entry.filter = filter;
558 new QLabel( lab, myEntriesFrame );
559 entry.edit = new QLineEdit( myEntriesFrame );
561 entry.btn = new QPushButton( myEntriesFrame );
562 entry.btn->setAutoDefault( false );
563 entry.btn->setIcon( QPixmap( open_icon ) );
565 Qtx::PathType type = Qtx::PT_OpenFile;
569 type = Qtx::PT_OpenFile;
572 type = Qtx::PT_SaveFile;
577 type = Qtx::PT_Directory;
580 entry.edit->setCompleter( Qtx::pathCompleter( type, filter ) );
582 connect( entry.btn, SIGNAL( clicked() ), this, SLOT( onBrowse() ) );
583 connect( entry.edit, SIGNAL( returnPressed() ), this, SLOT( onReturnPressed() ) );
584 connect( entry.edit, SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) );
586 myEntries.insert( num, entry );
592 \brief Get default file entry ID.
593 \return default entry ID
595 int QtxPathDialog::defaultEntry() const
601 \brief Set default entry.
602 \param id new default entry ID
604 void QtxPathDialog::setDefaultEntry( const int id )
610 \brief Initialize dialog layout.
612 void QtxPathDialog::initialize()
614 setWindowTitle( tr( "File dialog" ) );
616 QVBoxLayout* main = new QVBoxLayout( mainFrame() );
617 main->setMargin( 0 );
619 QtxGroupBox* base = new QtxGroupBox( "", mainFrame() );
620 main->addWidget( base );
622 QtxGridBox* mainGroup = new QtxGridBox( 1, Qt::Horizontal, base, 0 );
623 base->setWidget( mainGroup );
625 myEntriesFrame = new QtxGridBox( 3, Qt::Horizontal, mainGroup );
626 myOptionsFrame = new QFrame( mainGroup );
630 \brief Prepare file filters.
631 \param list of file masks, separated by ';;', for example, "*.h;;*.cxx"
632 \return list of processed file filters
634 QStringList QtxPathDialog::prepareFilters( const QString& filter ) const
637 bool allFilter = false;
638 if ( !filter.isEmpty() )
640 res = filter.split( ";;" );
641 for ( QStringList::ConstIterator it = res.begin(); it != res.end() && !allFilter; ++it )
643 QStringList wildCards = filterWildCards( *it );
644 allFilter = wildCards.indexOf( "*.*" ) != -1;
649 res.append( tr( "All files (*.*)" ) );
655 \brief Get wildcards from the specified file filter.
656 \param theFilter file filter being processed
657 \return list of filters with filtered wild cards
659 QStringList QtxPathDialog::filterWildCards( const QString& theFilter ) const
663 int b = theFilter.lastIndexOf( "(" );
664 int e = theFilter.lastIndexOf( ")" );
665 if ( b != -1 && e != -1 )
667 QString content = theFilter.mid( b + 1, e - b - 1 ).trimmed();
668 QStringList lst = content.split( " " );
669 for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it )
671 if ( (*it).indexOf( "." ) != -1 )
672 res.append( (*it).trimmed() );
679 \brief Get file file name with automatically assigned extension.
680 \param theFileName file name being processed
681 \param theFilter list of file filters
682 \return file name with assigned extension
684 QString QtxPathDialog::autoExtension( const QString& theFileName, const QString& theFilter ) const
686 QString fName = theFileName;
688 if ( fName.isEmpty() )
692 QStringList filters = prepareFilters( theFilter );
693 if ( !filters.isEmpty() )
694 filter = filters.first();
696 QStringList wildCards = filterWildCards( filter );
697 if ( !wildCards.isEmpty() )
699 QString ext = wildCards.first();
700 if ( ext.indexOf( "." ) != -1 )
701 ext = ext.mid( ext.indexOf( "." ) + 1 );
703 if ( !ext.isEmpty() && !ext.contains( "*" ) )
704 fName = QDir::toNativeSeparators( fName ) + QString( "." ) + ext;
711 \brief Check if there are visible child widgets.
712 \param wid parent widget being checked
713 \return \c true if widget \a wid has visible children
715 bool QtxPathDialog::hasVisibleChildren( QWidget* wid ) const
720 const QObjectList& aChildren = wid->children();
721 for ( QObjectList::const_iterator it = aChildren.begin(); it != aChildren.end() && !res; ++it )
723 if ( (*it)->isWidgetType() )
724 res = ((QWidget*)(*it))->isVisibleTo( wid );
731 \brief Upadte dialof box's child widgets visibility state.
733 void QtxPathDialog::updateVisibility()
735 if ( hasVisibleChildren( myEntriesFrame ) )
736 myEntriesFrame->show();
738 myEntriesFrame->hide();
740 if ( hasVisibleChildren( myOptionsFrame ) )
741 myOptionsFrame->show();
743 myOptionsFrame->hide();