]> SALOME platform Git repositories - modules/gui.git/blob - src/SUIT/SUIT_FileDlg.cxx
Salome HOME
87f1994d3460326732c49b906a3343ae98501ecd
[modules/gui.git] / src / SUIT / SUIT_FileDlg.cxx
1 //*********************************************************************************
2 // SUIT_FileDlg class is the extension of the Qt's Open/Save file dialog box.
3 // To get the file/directory name(s) call static methods:
4 //
5 // to invoke "Open file" or "Save file" dialog box
6 //    static QString getFileName(QWidget* parent, const QString& initial, const QStringList& filters, 
7 //                               const QString& caption, const bool open, const bool showQuickDir = true,
8 //                               SUIT_FileValidator* validator = 0);
9 //
10 // to invoke "Open files" dialog box (to get the multiple file selection)
11 //    static QStringList getOpenFileNames(QWidget* parent, const QString& initial, const QStringList& filters, 
12 //                                        const QString& caption, bool showQuickDir = true, 
13 //                                        SUIT_FileValidator* validator = 0);
14 //
15 // to invoke "Select directory" dialog box
16 //    static QString getExistingDirectory(QWidget* parent, const QString& initial,
17 //                                        const QString& caption, const bool showQuickDir = true);
18 //
19 // The parameters:
20 // - parent        parent widget (if 0, the current desktop is used)
21 // - initial       starting directory or file name (if null, last visited directory is used)
22 // - filters       file filters list; patterns inside the filter can be separated by ';','|' or ' ' 
23 //                 symbols
24 // - caption       dialog box's caption: if null, the default one is used
25 // - open          open flag - true for "Open File" and false for "Save File" dialog box
26 // - showQuickDir  this flag enables/disables "Quick directory list" controls
27 // - validator     you can provide custom file validator with this parameter
28 //
29 // Examples:
30 //   ...
31 //   QStringList flist;
32 //   flist.append( "Image files (*.bmp *.gif *.jpg )" );
33 //   flist.append( "All files (*.*)" );
34 //   QMyFileValidator* v = new QMyFileValidator( 0 );
35 //   QString fileName =  SUIT_FileDlg::getFileName( 0, QString::null, flist, "Dump view", false, true, v );
36 //   if ( !fileName.isEmpty() ) {
37 //      ... writing image to the file 
38 //   }
39 //   ...
40 //   QStringList flist;
41 //   flist.append( "*.cpp | *.cxx | *.c++" );
42 //   flist.append( "*.h | *.hpp | *.hxx" );
43 //   QString fileName =  SUIT_FileDlg::getFileName( desktop(), QString::null, flist, QString::null, true, true );
44 //
45 //*********************************************************************************
46
47 #include "SUIT_FileDlg.h"
48
49 #include "SUIT_Tools.h"   
50 #include "SUIT_Session.h"
51 #include "SUIT_Desktop.h"
52 #include "SUIT_MessageBox.h"
53 #include "SUIT_ResourceMgr.h"
54 #include "SUIT_FileValidator.h"
55
56 #include <qdir.h>
57 #include <qlabel.h>
58 #include <qregexp.h>
59 #include <qpalette.h>
60 #include <qobjectlist.h>
61 #include <qcombobox.h>
62 #include <qpushbutton.h>
63 #include <qapplication.h>
64
65 #define MIN_COMBO_SIZE 100
66
67 /*! If the selected file name has extension which does not match the selected filter
68  * this extension is ignored (and new one will be added). See below for details.
69  */
70 const bool IGNORE_NON_MATCHING_EXTENSION = true;
71
72 QString SUIT_FileDlg::myLastVisitedPath;
73
74 /*! Constructor */
75 SUIT_FileDlg::SUIT_FileDlg( QWidget* parent, bool open, bool showQuickDir, bool modal ) :
76 QFileDialog( parent, 0, modal ),
77 myValidator( 0 ),
78 myQuickCombo( 0 ), myQuickButton( 0 ), myQuickLab( 0 ),
79 myOpen( open )//,
80 //myAccepted( false )
81 {    
82   const QObjectList* child = children();
83   QObjectList::const_iterator anIt = child->begin(), aLast = child->end();
84   for( ; anIt!=aLast; anIt++ )
85     if( (*anIt)->inherits( "QPushButton" ) )
86     {
87       QPushButton* bt = ( QPushButton* )( *anIt );
88       bt->setDefault( false );
89       bt->setAutoDefault( false );
90     }
91
92   if ( parent->icon() )
93     setIcon( *parent->icon() );       
94   setSizeGripEnabled( true );
95   
96   if ( showQuickDir ) {
97     // inserting quick dir combo box
98     myQuickLab  = new QLabel(tr("LAB_QUICK_PATH"), this);
99     myQuickCombo = new QComboBox(false, this);
100     myQuickCombo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
101     myQuickCombo->setMinimumSize(MIN_COMBO_SIZE, 0);
102     
103     myQuickButton = new QPushButton(tr("BUT_ADD_PATH"), this);
104
105     connect(myQuickCombo,  SIGNAL(activated(const QString&)), this, SLOT(quickDir(const QString&)));
106     connect(myQuickButton, SIGNAL(clicked()),                 this, SLOT(addQuickDir()));
107     addWidgets(myQuickLab, myQuickCombo, myQuickButton);
108
109     // getting dir list from settings
110     QString dirs;
111     SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
112     if ( resMgr )
113       dirs = resMgr->stringValue( "FileDlg", QString( "QuickDirList" ) );
114
115     QStringList dirList = QStringList::split(';', dirs, false);
116     if (dirList.count() > 0) {
117       for (unsigned i = 0; i < dirList.count(); i++)
118         myQuickCombo->insertItem(dirList[i]);
119     }
120     else {
121       myQuickCombo->insertItem(QDir::homeDirPath());
122     }
123   }
124   setMode( myOpen ? ExistingFile : AnyFile );     
125   setCaption( myOpen ? tr( "INF_DESK_DOC_OPEN" ) : tr( "INF_DESK_DOC_SAVE" ) );
126
127   // If last visited path doesn't exist -> switch to the first preferred path
128   if ( !myLastVisitedPath.isEmpty() ) {
129     if ( !processPath( myLastVisitedPath ) && showQuickDir )
130       processPath( myQuickCombo->text( 0 ) );
131   }
132   else {
133     if ( showQuickDir )
134       processPath(myQuickCombo->text( 0 ) );
135   } 
136
137   // set default file validator
138   myValidator = new SUIT_FileValidator(this);
139 }
140
141 /*! Destructor*/
142 SUIT_FileDlg::~SUIT_FileDlg() 
143 {
144   setValidator( 0 );
145 }
146
147 /*! Redefined from QFileDialog.*/
148 void SUIT_FileDlg::polish()
149 {
150   QFileDialog::polish();
151   if ( myQuickButton && myQuickLab ) {
152     // the following is a workaround for proper layouting of custom widgets
153     QValueList<QPushButton*> buttonList;
154     QValueList<QLabel*> labelList;
155     const QObjectList *list = children();
156     QObjectListIt it(*list);
157     int maxButWidth = myQuickLab->sizeHint().width();
158     int maxLabWidth = myQuickButton->sizeHint().width();
159     
160     for (; it.current() ; ++it) {
161       if ( it.current()->isA( "QLabel" ) ) {
162         int tempW = ((QLabel*)it.current())->minimumWidth();
163         if ( maxLabWidth < tempW ) maxLabWidth = tempW;
164         labelList.append( (QLabel*)it.current() );
165       }
166       else if( it.current()->isA("QPushButton") ) {
167         int tempW = ((QPushButton*)it.current())->minimumWidth();
168         if ( maxButWidth < tempW ) maxButWidth = tempW;
169         buttonList.append( (QPushButton*)it.current() );
170       }
171     }
172     if (maxButWidth > 0) {
173       QValueList<QPushButton*>::Iterator bListIt;
174       for ( bListIt = buttonList.begin(); bListIt != buttonList.end(); ++bListIt )
175         (*bListIt)->setFixedWidth( maxButWidth );
176     }
177     if (maxLabWidth > 0) {
178       QValueList<QLabel*>::Iterator lListIt;
179       for ( lListIt = labelList.begin(); lListIt != labelList.end(); ++lListIt )
180         (*lListIt)->setFixedWidth( maxLabWidth );
181     }
182   }
183 }
184
185 /*! Sets validator for file names to open/save
186  * Deletes previous validator if the dialog owns it.
187  */
188 void SUIT_FileDlg::setValidator( SUIT_FileValidator* v )
189 {
190   if ( myValidator && myValidator->parent() == this )
191     delete myValidator;
192   myValidator = v;
193 }
194
195 /*! Returns the selected file */
196 QString SUIT_FileDlg::selectedFile() const
197 {
198   return mySelectedFile;
199 }
200
201 /*! Returns 'true' if this is 'Open File' dialog 
202  *  and 'false' if 'Save File' dialog
203  */
204 bool SUIT_FileDlg::isOpenDlg() const
205 {
206   return myOpen;
207 }
208
209 /*! Closes this dialog and sets the return code to 'Accepted'
210  * if the selected name is valid ( see 'acceptData()' )
211  */
212 void SUIT_FileDlg::accept()
213 {
214   /* myAccepted 
215    * flag is used to warkaround the Qt 2.2.2 BUG: 
216    * accept() method is called twice if user presses 'Enter' key 
217    * in file name editor while file name is not acceptable by acceptData()
218    * (e.g. permission denied)
219    */
220 //  if ( !myAccepted ) {
221     if ( mode() != ExistingFiles ) {
222       mySelectedFile = QFileDialog::selectedFile();
223       addExtension();
224     }
225
226     if ( acceptData() ) {
227       myLastVisitedPath = dirPath();
228       QFileDialog::accept();        
229 //      myAccepted = true;
230     }
231 //  }
232 //  myAccepted = !myAccepted;
233 }
234
235 /*! Closes this dialog and sets the return code to 'Rejected' */
236 void SUIT_FileDlg::reject()
237 {
238   mySelectedFile = QString::null;
239   QFileDialog::reject();        
240 }
241
242 /*! Returns 'true' if selected file is valid.
243  * The validity is checked by a file validator, 
244  * if there is no validator the file is always
245  * considered as valid    
246  */
247 bool SUIT_FileDlg::acceptData()
248 {    
249   if ( myValidator )
250   {
251     if ( isOpenDlg() )
252     {
253       if ( mode() == ExistingFiles )
254       {
255               QStringList fileNames = selectedFiles();
256               for ( int i = 0; i < (int)fileNames.count(); i++ )
257         {
258                 if ( !myValidator->canOpen( fileNames[i] ) )
259                   return false;
260               }
261               return true;
262       }
263       else
264       {
265               return myValidator->canOpen( selectedFile() );
266       }
267     }
268     else
269       return myValidator->canSave( selectedFile() );
270   }
271   return true;
272 }
273
274 /*! Adds an extension to the selected file name
275  * if the file has not it.
276  * The extension is extracted from the active filter.
277  */
278 void SUIT_FileDlg::addExtension()
279 {
280   // check if file name entered is empty
281   if ( mySelectedFile.stripWhiteSpace().isEmpty() )
282     return;
283
284   // current file extension
285   QString anExt = "." + SUIT_Tools::extension( mySelectedFile.stripWhiteSpace() ).stripWhiteSpace();
286
287   // If the file already has extension and it does not match the filter there are two choices:
288   // - to leave it 'as is'
289   // - to ignore it
290   // The behavior is defined by IGNORE_NON_MATCHING_EXTENSION constant
291   if ( anExt != "." && !IGNORE_NON_MATCHING_EXTENSION )
292     return;
293
294   // get selected file filter
295 #if QT_VERSION < 0x030000
296   QRegExp r( QString::fromLatin1("(?[a-zA-Z0-9.*? +;#|]*)?$") );
297   int len, index = r.match( selectedFilter().stripWhiteSpace(), 0, &len );
298 #else
299   QRegExp r( QString::fromLatin1("\\(?[a-zA-Z0-9.*? +;#|]*\\)?$") );
300   int index = r.search( selectedFilter().stripWhiteSpace() );
301 #endif
302
303   if ( index >= 0 ) {            
304     // Create wildcard regular expression basing on selected filter 
305     // in order to validate a file extension.
306     // Due to transformations from the filter list (*.txt *.*xx *.c++ SUIT*.* ) we 
307     // will have the pattern (\.txt|\..*xx|\.c\+\+|\..*) (as we validate extension only, 
308     // we remove everything except extension mask from the pattern
309 #if QT_VERSION < 0x030000
310     QString wildcard = selectedFilter().mid( index, len ).stripWhiteSpace();
311 #else
312     QString wildcard = selectedFilter().mid( index, r.matchedLength() ).stripWhiteSpace();
313 #endif
314     // replace '|' and ';' separators by space symbol and also brackets if there are some
315     wildcard.replace( QRegExp( "[\\|;|(|)]" )," " ); 
316
317     QString aPattern = wildcard.replace( QRegExp( "(^| )(\\s*)[0-9a-zA-Z*_?]*\\."), " \\." ).stripWhiteSpace().
318                                          replace( QRegExp( "\\s+" ), "|" ).replace( QRegExp( "[?]" ),".?" ).
319                                          replace( QRegExp( "[*]" ),".*" ).replace( QRegExp( "[+]" ),"\\+" );
320
321     // now we get the list of all extension masks and remove all which does not contain wildcard symbols
322     QStringList extList = QStringList::split( "|",aPattern );
323     for( int i = extList.count() - 1; i >= 0; i-- ) {
324       if ( !extList[i].contains( "." ) )
325         extList.remove( extList.at( i ) );
326     }
327     aPattern = extList.join( "|" );
328
329     // finalize pattern
330     QRegExp anExtRExp( "^("+ aPattern + ")$" );
331
332     // Check if the current file extension matches the pattern
333     if ( anExtRExp.match( anExt ) < 0 )
334     {
335       // find first appropriate extension in the selected filter 
336       // (it should be without wildcard symbols)
337       for ( int i = 0; i < (int)extList.count(); i++ )
338       {
339         QString newExt = extList[i].replace( QRegExp( "[\\\\][+]" ),"+" );
340         int res = newExt.findRev( '.' );
341         if ( res >= 0 )
342           newExt = newExt.mid( res + 1 );
343         if ( newExt.find( QRegExp("[*|?]" ) ) < 0 )
344         {
345           mySelectedFile.stripWhiteSpace();
346           mySelectedFile += mySelectedFile.endsWith(".") ? newExt : QString(".") + newExt;
347           break;
348         }
349       }
350     }
351   }
352 }
353
354 /*! Processes selection : tries to set given path or filename as selection */
355 bool SUIT_FileDlg::processPath( const QString& path )
356 {
357   if ( !path.isNull() ) {
358     QFileInfo fi( path );
359     if ( fi.exists() ) {
360       if ( fi.isFile() )
361         setSelection( path );
362       else if ( fi.isDir() )
363         setDir( path );
364       return true;
365     }
366     else {
367       if ( QFileInfo( fi.dirPath() ).exists() ) {
368         setDir( fi.dirPath() );
369         setSelection( path );
370         return true;
371       }
372     }
373   }
374   return false;
375 }
376 /*! Called when user selects item from "Quick Dir" combo box */
377 void SUIT_FileDlg::quickDir(const QString& dirPath)
378 {
379   QString aPath = dirPath;
380   if ( !QDir(aPath).exists() ) {
381     aPath = QDir::homeDirPath();
382     SUIT_MessageBox::error1(this, 
383                    tr("ERR_ERROR"),
384                    tr("ERR_DIR_NOT_EXIST").arg(dirPath), 
385                    tr("BUT_OK"));    
386   }
387   else
388   processPath(aPath);
389 }
390 /*!
391   Called when user presses "Add" button - adds current directory to quick directory
392   list and to the preferences
393 */
394 void SUIT_FileDlg::addQuickDir()
395 {
396   QString dp = dirPath();
397   if ( !dp.isEmpty() ) {
398     QDir dir( dp );
399     // getting dir list from settings
400     QString dirs;
401     SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
402     if ( resMgr )
403       dirs = resMgr->stringValue( "FileDlg", QString( "QuickDirList" ) );
404     QStringList dirList = QStringList::split(';', dirs, false);
405     bool found = false;
406     bool emptyAndHome = false;
407     if ( dirList.count() > 0 ) {
408       for ( unsigned i = 0; i < dirList.count(); i++ ) {
409         QDir aDir( dirList[i] );
410         if ( aDir.canonicalPath().isNull() && dirList[i] == dir.absPath() ||
411             !aDir.canonicalPath().isNull() && aDir.exists() && aDir.canonicalPath() == dir.canonicalPath() ) {
412           found = true;
413           break;
414         }
415       }
416     }
417     else {
418       emptyAndHome = dir.canonicalPath() == QDir(QDir::homeDirPath()).canonicalPath();
419     }
420     if ( !found ) {
421       dirList.append( dp );
422       resMgr->setValue( "FileDlg", QString( "QuickDirList" ), dirList.join(";") );
423       if ( !emptyAndHome )
424         myQuickCombo->insertItem( dp );
425     }
426   }
427 }
428 /*!
429   Returns the file name for Open/Save [ static ]
430 */
431 QString SUIT_FileDlg::getFileName( QWidget*            parent, 
432                                    const QString&      initial, 
433                                    const QStringList&  filters, 
434                                    const QString&      caption,
435                                    bool                open,
436                                    bool                showQuickDir, 
437                                    SUIT_FileValidator* validator )
438 {            
439   SUIT_FileDlg* fd = new SUIT_FileDlg( parent, open, showQuickDir, true );    
440   if ( !caption.isEmpty() )
441     fd->setCaption( caption );
442   if ( !initial.isEmpty() ) { 
443     fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
444   }
445   fd->setFilters( filters );        
446   if ( validator )
447     fd->setValidator( validator );
448   fd->exec();
449   QString filename = fd->selectedFile();
450   delete fd;
451   qApp->processEvents();
452   return filename;
453 }
454
455
456 /*!
457   Returns the list of files to be opened [ static ]
458 */
459 QStringList SUIT_FileDlg::getOpenFileNames( QWidget*            parent, 
460                                             const QString&      initial, 
461                                             const QStringList&  filters, 
462                                             const QString&      caption,
463                                             bool                showQuickDir, 
464                                             SUIT_FileValidator* validator )
465 {            
466   SUIT_FileDlg* fd = new SUIT_FileDlg( parent, true, showQuickDir, true );    
467   fd->setMode( ExistingFiles );     
468   if ( !caption.isEmpty() )
469     fd->setCaption( caption );
470   if ( !initial.isEmpty() ) { 
471     fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
472   }
473   fd->setFilters( filters );        
474   if ( validator )
475     fd->setValidator( validator );
476   fd->exec();
477   QStringList filenames = fd->selectedFiles();
478   delete fd;
479   qApp->processEvents();
480   return filenames;
481 }
482
483 /*!
484   Existing directory selection dialog [ static ]
485 */
486 QString SUIT_FileDlg::getExistingDirectory( QWidget*       parent,
487                                             const QString& initial,
488                                             const QString& caption, 
489                                             bool           showQuickDir )
490 {
491   SUIT_FileDlg* fd = new SUIT_FileDlg( parent, true, showQuickDir, true);
492   if ( !caption.isEmpty() )
493     fd->setCaption( caption );
494   if ( !initial.isEmpty() ) {
495     fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
496   }
497   fd->setMode( DirectoryOnly );
498   fd->setFilters(tr("INF_DIRECTORIES_FILTER"));
499   fd->exec();
500   QString dirname = fd->selectedFile();
501   delete fd;
502   qApp->processEvents();
503   return dirname;
504   
505 }