1 // Copyright (C) 2007-2008 CEA/DEN, EDF R&D, 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.
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
22 // File: QtxPathDialog.cxx
23 // Author: Sergey TELKOV
25 #include "QtxPathDialog.h"
27 #include "QtxGridBox.h"
28 #include "QtxGroupBox.h"
35 #include <QObjectList>
36 #include <QStringList>
37 #include <QFileDialog>
38 #include <QMessageBox>
39 #include <QPushButton>
41 static const char* open_icon[] = {
68 \brief The QtxPathDialog class provides a simple convenience dialog to
69 enter a path to the file or to the directory.
71 The QtxPathDialog class adds possibility to browse the file system
72 with help of standard Open/Save dialog boxes or enter the file/directory
75 Default implementation provides only one "standard" file entry.
76 Sometimes it is necessary to select several different files/directories
77 from the same dialog box. In this case it is possible to derive from the
78 QtxPathDialog class and use createFileEntry() method to add required
79 number of file entries.
84 \param import if \c true, the dialog box is shown for "open" mode,
85 otherwise, it is shown in the "save" mode
86 \param parent parent widget
87 \param modal if \c true, the dialog box should be modal
88 \param resize if \c true, the dialog box is resizable
89 \param buttons required buttons (QtxDialog::ButtonFlags)
92 QtxPathDialog::QtxPathDialog( const bool import, QWidget* parent, const bool modal,
93 const bool resize, const int buttons, Qt::WindowFlags f )
94 : QtxDialog( parent, modal, resize, buttons, f ),
101 setWindowTitle( tr( import ? "Open file" : "Save file" ) );
103 setDefaultEntry( createFileEntry( tr( "File name" ), import ? OpenFile : SaveFile ) );
104 QLineEdit* le = fileEntry( defaultEntry() );
106 le->setMinimumWidth( 200 );
117 \param parent parent widget
118 \param modal if \c true, the dialog box should be modal
119 \param resize if \c true, the dialog box is resizable
120 \param buttons required buttons (QtxDialog::ButtonFlags)
121 \param f window flags
123 QtxPathDialog::QtxPathDialog( QWidget* parent, const bool modal,
124 const bool resize, const int buttons, Qt::WindowFlags f )
125 : QtxDialog( parent, modal, resize, buttons, f ),
138 QtxPathDialog::~QtxPathDialog()
143 \brief Get selected file name.
146 QString QtxPathDialog::fileName() const
148 return fileName( defaultEntry() );
152 \brief Set the file name.
153 \param txt new file name
154 \param autoExtension if \c true an extension is determined automatically by file
156 void QtxPathDialog::setFileName( const QString& txt, const bool autoExtension )
158 setFileName( defaultEntry(), txt, autoExtension );
162 \brief Get current file filter.
165 QString QtxPathDialog::filter() const
167 return filter( defaultEntry() );
171 \brief Change file filter.
173 Filter is a list of file masks, separated by ';;'. For example,
176 \param fltr new file filter
178 void QtxPathDialog::setFilter( const QString& fltr )
180 setFilter( defaultEntry(), fltr );
184 \brief Show/hide the path dialog box/
185 \param on new visibility state
187 void QtxPathDialog::setVisible( bool on )
192 QtxDialog::setVisible( on );
196 \brief Called when user clicks a "browse" button
197 to open standard file dialog.
199 void QtxPathDialog::onBrowse()
201 const QObject* obj = sender();
205 for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && id == -1; ++it )
207 if ( it.value().btn == obj )
214 FileEntry& entry = myEntries[id];
216 bool isDir = entry.mode != OpenFile && entry.mode != SaveFile;
220 entry.dlg = new QFileDialog( this, windowTitle(), QDir::current().path() );
221 switch ( entry.mode )
227 entry.dlg->setFileMode( QFileDialog::DirectoryOnly );
230 entry.dlg->setFileMode( QFileDialog::AnyFile );
234 entry.dlg->setFileMode( QFileDialog::ExistingFiles );
241 QStringList fList = prepareFilters( entry.filter );
242 if ( !fList.isEmpty() )
243 entry.dlg->setFilters( fList );
245 entry.dlg->selectFile( fileName( id ) );
247 if ( entry.dlg->exec() != Accepted )
250 QStringList fileList = entry.dlg->selectedFiles();
251 QString fName = !fileList.isEmpty() ? fileList.first() : QString();
253 if ( fName.isEmpty() )
256 if ( Qtx::extension( fName ).isEmpty() && !isDir )
257 fName = autoExtension( fName, entry.dlg->selectedFilter() );
259 fName = QDir::convertSeparators( fName );
260 QString prev = QDir::convertSeparators( fileName( id ) );
263 while ( prev.length() && prev.at( prev.length() - 1 ) == QDir::separator() )
264 prev.remove( prev.length() - 1, 1 );
265 while ( fName.length() && fName.at( fName.length() - 1 ) == QDir::separator() )
266 fName.remove( fName.length() - 1, 1 );
272 setFileName( id, fName );
273 fileNameChanged( id, fName );
275 if ( id == defaultEntry() )
276 emit fileNameChanged( fName );
280 \brief Called when user presses \c Return key being in the line edit.
282 void QtxPathDialog::onReturnPressed()
284 const QObject* obj = sender();
287 for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && id == -1; ++it )
289 if ( it.value().edit == obj )
296 fileNameChanged( id, fileName( id ) );
298 if ( id == defaultEntry() )
299 emit fileNameChanged( fileName() );
303 \brief Called when the text in the line edit is changed by the user.
304 \param txt current text (not used)
306 void QtxPathDialog::onTextChanged( const QString& /*txt*/ )
312 \brief Check validity of the entered text and enable/disable standard
313 \c OK, \c Yes buttons.
315 void QtxPathDialog::validate()
317 setButtonEnabled( isValid(), OK | Yes );
321 \brief Check if the entered file/directory name is valid.
322 \return \c true if selected file name is valid
324 bool QtxPathDialog::isValid()
327 for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && ok; ++it )
329 if ( it.value().edit->isEnabled() )
330 ok = !it.value().edit->text().trimmed().isEmpty();
337 \brief Check if the entered data is acceptable.
338 \return \c true if entered data is acceptable
340 bool QtxPathDialog::acceptData() const
344 QWidget* parent = (QWidget*)this;
346 FileEntryMap::ConstIterator it;
347 for ( it = myEntries.begin(); it != myEntries.end() && ok; ++it )
349 const FileEntry& entry = it.value();
350 QFileInfo fileInfo( entry.edit->text() );
351 if ( entry.edit->text().isEmpty() )
353 QMessageBox::critical( parent, windowTitle(), tr( "File name not specified" ),
354 QMessageBox::Ok, QMessageBox::NoButton );
357 else switch ( entry.mode )
360 if ( !fileInfo.exists() )
362 QMessageBox::critical( parent, windowTitle(), tr( "File \"%1\" does not exist" ).arg( fileInfo.filePath() ),
363 QMessageBox::Ok, QMessageBox::NoButton );
368 if ( fileInfo.exists() )
369 ok = QMessageBox::warning( parent, windowTitle(), tr( "File \"%1\" already exist. Do you want to overwrite it?" ).arg( fileInfo.filePath() ),
370 QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
373 if ( !fileInfo.exists() || !fileInfo.isDir() )
375 QMessageBox::critical( parent, windowTitle(), tr( "Directory \"%1\" does not exist" ).arg( fileInfo.filePath() ),
376 QMessageBox::Ok, QMessageBox::NoButton );
381 if ( fileInfo.exists() && !fileInfo.isDir() )
383 QMessageBox::critical( parent, windowTitle(), tr( "Directory \"%1\" can't be created because file with the same name exist" ).arg( fileInfo.filePath() ),
384 QMessageBox::Ok, QMessageBox::NoButton );
389 if ( fileInfo.exists() )
391 if ( !fileInfo.isDir() )
393 QMessageBox::critical( parent, windowTitle(), tr( "Directory \"%1\" can't be created because file with the same name exist" ).arg( fileInfo.filePath() ),
394 QMessageBox::Ok, QMessageBox::NoButton );
397 else if ( QDir( fileInfo.filePath() ).count() > 2 )
398 ok = QMessageBox::warning( parent, windowTitle(), tr( "Directory \"%1\" not empty. Do you want to remove all files in this directory?" ).arg( fileInfo.filePath() ),
399 QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
407 entry.edit->setFocus();
414 \brief Perform custom actions when the file name is changed.
416 This method can be redefined in the successor classes.
417 Default implementation does nothing.
420 \param fileName file name
422 void QtxPathDialog::fileNameChanged( int /*id*/, QString /*fileName*/ )
427 \fn void QtxPathDialog::fileNameChanged( QString fileName );
428 \brief Emitted when the file name is changed.
429 \param fileName file name
433 \brief Get options grame widget.
434 \return options frame widget
436 QFrame* QtxPathDialog::optionsFrame()
438 return myOptionsFrame;
442 \brief Get file name from specified entry.
443 \param id file entry ID
444 \return file name or null string if \a id is invalid
446 QString QtxPathDialog::fileName( const int id ) const
449 if ( myEntries.contains( id ) )
450 res = myEntries[id].edit->text();
455 \brief Change file name by specified file entry.
456 \param id file entry ID
457 \param txt new file name
458 \param autoExt if \c true, assign extension automatically
460 void QtxPathDialog::setFileName( const int id, const QString& txt, const bool autoExt )
463 QLineEdit* le = fileEntry( id, mode );
467 if ( autoExt && ( mode == OpenFile || mode == SaveFile ) )
468 le->setText( autoExtension( txt, filter( id ) ) );
475 \brief Get file filter from the specified file entry.
476 \param id file entry ID
477 \return file filter or null string if \a id is invalid
479 QString QtxPathDialog::filter( const int id ) const
482 if ( myEntries.contains( id ) )
483 res = myEntries[id].filter;
488 \brief Set file filter to the specified file entry.
489 \param id file entry ID
490 \param filter file filter or null string if \a id is invalid
492 void QtxPathDialog::setFilter( const int id, const QString& filter )
494 if ( myEntries.contains( id ) )
495 myEntries[id].filter = filter;
499 \brief Get line edit widget for the specified file entry.
500 \param id file entry ID
501 \return line edit widget or 0 if \a id is invalid
503 QLineEdit* QtxPathDialog::fileEntry( const int id ) const
506 if ( myEntries.contains( id ) )
507 le = myEntries[id].edit;
513 \brief Get line edit widget and file mode for the specified file entry.
514 \param id file entry ID
515 \param theMode to return file entry mode
516 \return line edit widget or 0 if \a id is invalid
518 QLineEdit* QtxPathDialog::fileEntry( const int theId, int& theMode ) const
521 if ( myEntries.contains( theId ) )
523 le = myEntries[theId].edit;
524 theMode = myEntries[theId].mode;
531 \brief Create new file entry.
533 If required file entry is already in use or if specified \a id is < 0,
534 new ID is generated and returned.
536 \param lab file entry title
537 \param mode file entry mode
538 \param id required file entry ID
539 \return created file entry ID
541 int QtxPathDialog::createFileEntry( const QString& lab, const int mode,
542 const QString& filter, const int id )
548 while ( myEntries.contains( num ) )
555 entry.filter = filter;
557 new QLabel( lab, myEntriesFrame );
558 entry.edit = new QLineEdit( myEntriesFrame );
560 entry.btn = new QPushButton( myEntriesFrame );
561 entry.btn->setAutoDefault( false );
562 entry.btn->setIcon( QPixmap( open_icon ) );
564 Qtx::PathType type = Qtx::PT_OpenFile;
568 type = Qtx::PT_OpenFile;
571 type = Qtx::PT_SaveFile;
576 type = Qtx::PT_Directory;
579 entry.edit->setCompleter( Qtx::pathCompleter( type, filter ) );
581 connect( entry.btn, SIGNAL( clicked() ), this, SLOT( onBrowse() ) );
582 connect( entry.edit, SIGNAL( returnPressed() ), this, SLOT( onReturnPressed() ) );
583 connect( entry.edit, SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) );
585 myEntries.insert( num, entry );
591 \brief Get default file entry ID.
592 \return default entry ID
594 int QtxPathDialog::defaultEntry() const
600 \brief Set default entry.
601 \param id new default entry ID
603 void QtxPathDialog::setDefaultEntry( const int id )
609 \brief Initialize dialog layout.
611 void QtxPathDialog::initialize()
613 setWindowTitle( tr( "File dialog" ) );
615 QVBoxLayout* main = new QVBoxLayout( mainFrame() );
616 main->setMargin( 0 );
618 QtxGroupBox* base = new QtxGroupBox( "", mainFrame() );
619 main->addWidget( base );
621 QtxGridBox* mainGroup = new QtxGridBox( 1, Qt::Horizontal, base, 0 );
622 base->setWidget( mainGroup );
624 myEntriesFrame = new QtxGridBox( 3, Qt::Horizontal, mainGroup );
625 myOptionsFrame = new QFrame( mainGroup );
629 \brief Prepare file filters.
630 \param list of file masks, separated by ';;', for example, "*.h;;*.cxx"
631 \return list of processed file filters
633 QStringList QtxPathDialog::prepareFilters( const QString& filter ) const
636 bool allFilter = false;
637 if ( !filter.isEmpty() )
639 res = filter.split( ";;" );
640 for ( QStringList::ConstIterator it = res.begin(); it != res.end() && !allFilter; ++it )
642 QStringList wildCards = filterWildCards( *it );
643 allFilter = wildCards.indexOf( "*.*" ) != -1;
648 res.append( tr( "All files (*.*)" ) );
654 \brief Get wildcards from the specified file filter.
655 \param theFilter file filter being processed
656 \return list of filters with filtered wild cards
658 QStringList QtxPathDialog::filterWildCards( const QString& theFilter ) const
662 int b = theFilter.lastIndexOf( "(" );
663 int e = theFilter.lastIndexOf( ")" );
664 if ( b != -1 && e != -1 )
666 QString content = theFilter.mid( b + 1, e - b - 1 ).trimmed();
667 QStringList lst = content.split( " " );
668 for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it )
670 if ( (*it).indexOf( "." ) != -1 )
671 res.append( (*it).trimmed() );
678 \brief Get file file name with automatically assigned extension.
679 \param theFileName file name being processed
680 \param theFilter list of file filters
681 \return file name with assigned extension
683 QString QtxPathDialog::autoExtension( const QString& theFileName, const QString& theFilter ) const
685 QString fName = theFileName;
687 if ( fName.isEmpty() )
691 QStringList filters = prepareFilters( theFilter );
692 if ( !filters.isEmpty() )
693 filter = filters.first();
695 QStringList wildCards = filterWildCards( filter );
696 if ( !wildCards.isEmpty() )
698 QString ext = wildCards.first();
699 if ( ext.indexOf( "." ) != -1 )
700 ext = ext.mid( ext.indexOf( "." ) + 1 );
702 if ( !ext.isEmpty() && !ext.contains( "*" ) )
703 fName = QDir::convertSeparators( fName ) + QString( "." ) + ext;
710 \brief Check if there are visible child widgets.
711 \param wid parent widget being checked
712 \return \c true if widget \a wid has visible children
714 bool QtxPathDialog::hasVisibleChildren( QWidget* wid ) const
719 const QObjectList& aChildren = wid->children();
720 for ( QObjectList::const_iterator it = aChildren.begin(); it != aChildren.end() && !res; ++it )
722 if ( (*it)->isWidgetType() )
723 res = ((QWidget*)(*it))->isVisibleTo( wid );
730 \brief Upadte dialof box's child widgets visibility state.
732 void QtxPathDialog::updateVisibility()
734 if ( hasVisibleChildren( myEntriesFrame ) )
735 myEntriesFrame->show();
737 myEntriesFrame->hide();
739 if ( hasVisibleChildren( myOptionsFrame ) )
740 myOptionsFrame->show();
742 myOptionsFrame->hide();