Salome HOME
Join modifications from branch OCC_debug_for_3_2_0b1
[modules/gui.git] / src / Qtx / QtxPathDialog.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
2 // 
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either 
6 // version 2.1 of the License.
7 // 
8 // This library is distributed in the hope that it will be useful 
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public  
14 // License along with this library; if not, write to the Free Software 
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/
18 //
19 // File:      QtxPathDialog.cxx
20 // Author:    Sergey TELKOV
21
22 #include "QtxPathDialog.h"
23
24 #include "QtxGroupBox.h"
25
26 #include <qdir.h>
27 #include <qlabel.h>
28 #include <qpixmap.h>
29 #include <qlayout.h>
30 #include <qlineedit.h>
31 #include <qfileinfo.h>
32 #include <qobjectlist.h>
33 #include <qstringlist.h>
34 #include <qfiledialog.h>
35 #include <qmessagebox.h>
36 #include <qpushbutton.h>
37
38 static const char* open_icon[] = {
39 "16 16 5 1",
40 "  c none",
41 ". c #ffff00",
42 "# c #848200",
43 "a c #ffffff",
44 "b c #000000",
45 "                ",
46 "          bbb   ",
47 "         b   b b",
48 "              bb",
49 "  bbb        bbb",
50 " ba.abbbbbbb    ",
51 " b.a.a.a.a.b    ",
52 " ba.a.a.a.ab    ",
53 " b.a.abbbbbbbbbb",
54 " ba.ab#########b",
55 " b.ab#########b ",
56 " bab#########b  ",
57 " bb#########b   ",
58 " bbbbbbbbbbb    ",
59 "                ",
60 "                "
61 };
62
63 /*!
64   Constructor.
65 */
66 QtxPathDialog::QtxPathDialog( const bool import, QWidget* parent, const bool modal, const bool resize, const int buttons, WFlags f )
67 : QtxDialog( parent, 0, modal, resize, buttons, f ),
68 myDefault( -1 ),
69 myEntriesFrame( 0 ),
70 myOptionsFrame( 0 )
71 {
72         initialize();
73
74         setCaption( tr( import ? "Open file" : "Save file" ) );
75
76         setDefaultEntry( createFileEntry( tr( "File name" ), import ? OpenFile : SaveFile ) );
77         QLineEdit* le = fileEntry( defaultEntry() );
78         if ( le )
79                 le->setMinimumWidth( 200 );
80
81         validate();
82
83         setFocusProxy( le );
84 }
85
86 /*!
87   Constructor.
88 */
89 QtxPathDialog::QtxPathDialog( QWidget* parent, const bool modal, const bool resize, const int buttons, WFlags f )
90 : QtxDialog( parent, 0, modal, resize, buttons, f ),
91 myDefault( -1 ),
92 myEntriesFrame( 0 ),
93 myOptionsFrame( 0 )
94 {
95         initialize();
96 }
97
98 /*!
99   Destructor.
100 */
101 QtxPathDialog::~QtxPathDialog()
102 {
103 }
104
105 /*!
106   \return file name
107 */
108 QString QtxPathDialog::fileName() const
109 {
110         return fileName( defaultEntry() );
111 }
112
113 /*!
114   Sets file name
115   \param txt - new file name
116   \param autoExtension - auto extension determination by file
117 */
118 void QtxPathDialog::setFileName( const QString& txt, const bool autoExtension )
119 {
120         setFileName( defaultEntry(), txt, autoExtension );
121 }
122
123 /*!
124   \return filter
125 */
126 QString QtxPathDialog::filter() const
127 {
128         return myFilter;
129 }
130
131 /*!
132   Changes filter (filter is a list of masks, separated by ';;')
133   \param fltr - new filter
134 */
135 void QtxPathDialog::setFilter( const QString& fltr )
136 {
137         myFilter = fltr;
138 }
139
140 /*!
141   Shows path dialog
142 */
143 void QtxPathDialog::show()
144 {
145         if ( hasVisibleChildren( myEntriesFrame ) )
146                 myEntriesFrame->show();
147         else
148                 myEntriesFrame->hide();
149
150         if ( hasVisibleChildren( myOptionsFrame ) )
151                 myOptionsFrame->show();
152         else
153                 myOptionsFrame->hide();
154
155         QtxDialog::show();
156 }
157
158 /*!
159   SLOT: called if user click button to show standard file dialog
160 */
161 void QtxPathDialog::onBrowse()
162 {
163         const QObject* obj = sender();
164
165         int id = -1;
166
167         for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && id == -1; ++it )
168                 if ( it.data().btn == obj )
169                         id = it.key();
170
171         if ( id == -1 )
172                 return;
173
174         FileEntry& entry = myEntries[id];
175
176         bool isDir = entry.mode != OpenFile && entry.mode != SaveFile;
177
178         if ( !entry.dlg )
179         {
180                 entry.dlg = new QFileDialog( QDir::current().path(), QString::null, this, 0, true );
181                 entry.dlg->setCaption( caption() );
182                 switch ( entry.mode )
183                 {
184                 case NewDir:
185                 case OpenDir:
186                 case SaveDir:
187                         isDir = true;
188                         entry.dlg->setMode( QFileDialog::DirectoryOnly );
189                         break;
190                 case SaveFile:
191                         entry.dlg->setMode( QFileDialog::AnyFile );
192                         break;
193                 case OpenFile:
194                 default:
195                         entry.dlg->setMode( QFileDialog::ExistingFile );
196                         break;
197                 }
198         }
199
200         if ( !isDir )
201                 entry.dlg->setFilters( prepareFilters() );
202         entry.dlg->setSelection( fileName( id ) );
203
204         if ( entry.dlg->exec() != Accepted )
205                 return;
206
207         QString fName = entry.dlg->selectedFile();
208
209         if ( fName.isEmpty() )
210                 return;
211
212         if ( QFileInfo( fName ).extension().isEmpty() && !isDir )
213                 fName = autoExtension( fName, entry.dlg->selectedFilter() );
214
215         fName = QDir::convertSeparators( fName );
216         QString prev = QDir::convertSeparators( fileName( id ) );
217         if ( isDir )
218         {
219                 while ( prev.length() && prev.at( prev.length() - 1 ) == QDir::separator() )
220                         prev.remove( prev.length() - 1, 1 );
221                 while ( fName.length() && fName.at( fName.length() - 1 ) == QDir::separator() )
222                         fName.remove( fName.length() - 1, 1 );
223         }
224
225         if ( prev == fName )
226                 return;
227
228         setFileName( id, fName );
229         fileNameChanged( id, fName );
230
231         if ( id == defaultEntry() )
232                 emit fileNameChanged( fName );
233 }
234
235 /*!
236   SLOT: called if user presses RETURN in line edit
237 */
238 void QtxPathDialog::onReturnPressed()
239 {
240         const QObject* obj = sender();
241
242         int id = -1;
243         for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && id == -1; ++it )
244                 if ( it.data().edit == obj )
245                         id = it.key();
246
247         if ( id == -1 )
248                 return;
249
250         fileNameChanged( id, fileName( id ) );
251
252         if ( id == defaultEntry() )
253                 emit fileNameChanged( fileName() );
254 }
255
256 /*!
257   SLOT: called if text in line edit is changed
258 */
259 void QtxPathDialog::onTextChanged( const QString& )
260 {
261         validate();
262 }
263
264 /*!
265   Checks validity of text and according to it enables/disables OK, Yes buttons
266 */
267 void QtxPathDialog::validate()
268 {
269         setButtonEnabled( isValid(), OK | Yes );
270 }
271
272 /*!
273   \return true if selected file is valid
274 */
275 bool QtxPathDialog::isValid()
276 {
277         bool ok = true;
278         for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && ok; ++it )
279                 if ( it.data().edit->isEnabled() )
280                         ok = !it.data().edit->text().stripWhiteSpace().isEmpty();
281
282         return ok;
283 }
284
285 /*!
286   \return true entered data is accepted
287 */
288 bool QtxPathDialog::acceptData() const
289 {
290         bool ok = true;
291
292         QWidget* parent = (QWidget*)this;
293
294         FileEntryMap::ConstIterator it;
295         for ( it = myEntries.begin(); it != myEntries.end() && ok; ++it )
296         {
297                 const FileEntry& entry = it.data();
298                 QFileInfo fileInfo( entry.edit->text() );
299                 if ( entry.edit->text().isEmpty() )
300                 {
301                         QMessageBox::critical( parent, caption(), tr( "File name not specified" ),
302                                                                    QMessageBox::Ok, QMessageBox::NoButton );
303                         ok = false;
304                 }
305                 else switch ( entry.mode )
306                 {
307                 case OpenFile:
308                         if ( !fileInfo.exists() )
309                         {
310                                 QMessageBox::critical( parent, caption(), tr( "File \"%1\" does not exist" ).arg( fileInfo.filePath() ),
311                                                                            QMessageBox::Ok, QMessageBox::NoButton );
312                                 ok = false;
313                         }
314                         break;
315                 case SaveFile:
316                         if ( fileInfo.exists() )
317                                 ok = QMessageBox::warning( parent, caption(), tr( "File \"%1\" already exist. Do you want to overwrite it?" ).arg( fileInfo.filePath() ),
318                                                                                    QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
319                         break;
320                 case OpenDir:
321                         if ( !fileInfo.exists() || !fileInfo.isDir() )
322                         {
323                                 QMessageBox::critical( parent, caption(), tr( "Directory \"%1\" does not exist" ).arg( fileInfo.filePath() ),
324                                                                            QMessageBox::Ok, QMessageBox::NoButton );
325                                 ok = false;
326                         }
327                         break;
328                 case SaveDir:
329                         if ( fileInfo.exists() && !fileInfo.isDir() )
330                         {
331                                 QMessageBox::critical( parent, caption(), tr( "Directory \"%1\" can't be created because file with the same name exist" ).arg( fileInfo.filePath() ),
332                                                                            QMessageBox::Ok, QMessageBox::NoButton );
333                                 ok = false;
334                         }
335                         break;
336                 case NewDir:
337                         if ( fileInfo.exists() )
338                         {
339                                 if ( !fileInfo.isDir() )
340                                 {
341                                         QMessageBox::critical( parent, caption(), tr( "Directory \"%1\" can't be created because file with the same name exist" ).arg( fileInfo.filePath() ),
342                                                                                    QMessageBox::Ok, QMessageBox::NoButton );
343                                         ok = false;
344                                 }
345                                 else if ( QDir( fileInfo.filePath() ).count() > 2 )
346                                         ok = QMessageBox::warning( parent, caption(), tr( "Directory \"%1\" not empty. Do you want to remove all files in this directory?" ).arg( fileInfo.filePath() ),
347                                                                                            QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
348                         }
349                         break;
350                 default:
351                         break;
352                 }
353
354                 if ( !ok )
355                         entry.edit->setFocus();
356         }
357
358         return ok;
359 }
360
361 /*!
362   Some custom activity on file name changing (must be redefined, default implementation is empty
363 */
364 void QtxPathDialog::fileNameChanged( int, QString )
365 {
366 }
367
368 /*!
369   \return frame with options
370 */
371 QFrame* QtxPathDialog::optionsFrame()
372 {
373         return myOptionsFrame;
374 }
375
376 /*!
377   \return file name
378   \param id - id of file entry
379 */
380 QString QtxPathDialog::fileName( const int id ) const
381 {
382         QString res;
383         if ( myEntries.contains( id ) )
384                 res = myEntries[id].edit->text();
385
386         return res;
387 }
388
389 /*!
390   Change file name of file entry
391   \param id - id of file entry
392   \param txt - new file name
393   \param autoExt - assign extension automatically
394 */
395 void QtxPathDialog::setFileName( const int id, const QString& txt, const bool autoExt )
396 {
397         int mode;
398         QLineEdit* le = fileEntry( id, mode );
399
400         if ( le )
401         {
402                 if ( autoExt && ( mode == OpenFile || mode == SaveFile ) )
403                         le->setText( autoExtension( txt ) );
404                 else
405                         le->setText( txt );
406         }
407 }
408
409 /*!
410   \return line edit of file entry
411   \param id - id of file entry
412 */
413 QLineEdit* QtxPathDialog::fileEntry( const int id ) const
414 {
415         QLineEdit* le = 0;
416         if ( myEntries.contains( id ) )
417                 le = myEntries[id].edit;
418
419         return le;
420 }
421
422 /*!
423   \return line edit and mode of file entry
424   \param id - id of file entry
425   \param theMode - for return mode of file entry
426 */
427 QLineEdit* QtxPathDialog::fileEntry( const int theId, int& theMode ) const
428 {
429         QLineEdit* le = 0;
430         if ( myEntries.contains( theId ) )
431         {
432                 le = myEntries[theId].edit;
433                 theMode = myEntries[theId].mode;
434         }
435
436         return le;
437 }
438
439 /*!
440   Creates file entry
441   \return id of just created file entry
442   \param lab - title of entry
443   \param mode - mode of entry
444   \param id - proposed id (if it is -1, then id will be chosen automatically)
445 */
446 int QtxPathDialog::createFileEntry( const QString& lab, const int mode, const int id )
447 {
448         int num = id;
449         if ( num == -1 )
450         {
451                 num--;
452                 while ( myEntries.contains( num ) )
453                         num--;
454         }
455
456         FileEntry entry;
457         entry.dlg = 0;
458         entry.mode = mode;
459
460         new QLabel( lab, myEntriesFrame );
461         entry.edit = new QLineEdit( myEntriesFrame );
462         entry.btn = new QPushButton( myEntriesFrame );
463         entry.btn->setAutoDefault( false );
464         entry.btn->setPixmap( QPixmap( open_icon ) );
465
466         connect( entry.btn, SIGNAL( clicked() ), this, SLOT( onBrowse() ) );
467         connect( entry.edit, SIGNAL( returnPressed() ), this, SLOT( onReturnPressed() ) );
468         connect( entry.edit, SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) );
469
470         myEntries.insert( num, entry );
471
472         return num;
473 }
474
475 /*!
476   \return id of default entry
477 */
478 int QtxPathDialog::defaultEntry() const
479 {
480         return myDefault;
481 }
482
483 /*!
484   Change default entry id
485   \param id - new default entry id
486 */
487 void QtxPathDialog::setDefaultEntry( const int id )
488 {
489         myDefault = id;
490 }
491
492 /*!
493   Initialize dialog layout
494 */
495 void QtxPathDialog::initialize()
496 {
497         setCaption( tr( "File dialog" ) );
498
499         QVBoxLayout* main = new QVBoxLayout( mainFrame() );
500         QtxGroupBox* mainGroup = new QtxGroupBox( 1, Qt::Horizontal, "", mainFrame() );
501         mainGroup->setFrameStyle( QFrame::NoFrame );
502   mainGroup->setInsideMargin( 0 );
503         main->addWidget( mainGroup );
504
505         myEntriesFrame = new QGroupBox( 3, Qt::Horizontal, "", mainGroup );
506         myOptionsFrame = new QFrame( mainGroup );
507 }
508
509 /*!
510   \return list of filters
511 */
512 QStringList QtxPathDialog::prepareFilters() const
513 {
514         QStringList res;
515         if ( !myFilter.isEmpty() )
516         {
517                 res = QStringList::split( ";;", myFilter );
518                 bool allFilter = false;
519                 for ( QStringList::ConstIterator it = res.begin(); it != res.end() && !allFilter; ++it )
520                 {
521                         QStringList wildCards = filterWildCards( *it );
522                         allFilter = wildCards.findIndex( "*.*" ) != -1;
523                 }
524
525                 if ( !allFilter )
526                         res.append( tr( "All files (*.*)" ) );
527         }
528
529         return res;
530 }
531
532 /*!
533   \return list of filters with filtered wild cards
534 */
535 QStringList QtxPathDialog::filterWildCards( const QString& theFilter ) const
536 {
537         QStringList res;
538
539         int b = theFilter.findRev( "(" );
540         int e = theFilter.findRev( ")" );
541         if ( b != -1 && e != -1 )
542         {
543                 QString content = theFilter.mid( b + 1, e - b - 1 ).stripWhiteSpace();
544                 QStringList lst = QStringList::split( " ", content );
545                 for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it )
546                         if ( (*it).find( "." ) != -1 )
547                                 res.append( (*it).stripWhiteSpace() );
548         }
549         return res;
550 }
551
552 /*!
553   \return file name with assigned extension
554   \param theFileName - source file name
555   \param theFilter - list of filters
556 */
557 QString QtxPathDialog::autoExtension( const QString& theFileName, const QString& theFilter ) const
558 {
559         QString fName = theFileName;
560
561         if ( fName.isEmpty() )
562                 return fName;
563
564         QString filter = theFilter;
565         if ( filter.isEmpty() )
566         {
567                 QStringList filters = prepareFilters();
568                 if ( !filters.isEmpty() )
569                         filter = filters.first();
570         }
571
572         QStringList wildCards = filterWildCards( filter );
573         if ( !wildCards.isEmpty() )
574         {
575                 QString ext = wildCards.first();
576                 if ( ext.find( "." ) != -1 )
577                         ext = ext.mid( ext.find( "." ) + 1 );
578
579                 if ( !ext.isEmpty() && !ext.contains( "*" ) )
580                         fName = QDir::convertSeparators( fName ) + QString( "." ) + ext;
581         }
582
583         return fName;
584 }
585
586 /*!
587   \return true if widget has visible children
588   \param wid - widget
589 */
590 bool QtxPathDialog::hasVisibleChildren( QWidget* wid ) const
591 {
592         bool res = false;
593         if ( wid )
594         {
595                 const QObjectList* aChildren = wid->children();
596                 if ( aChildren )
597                 {
598                         for ( QObjectListIt it( *aChildren ); it.current() && !res; ++it )
599                         {
600                                 if ( it.current()->isWidgetType() )
601                                         res = ((QWidget*)it.current())->isVisibleTo( wid );
602                         }
603                 }
604         }
605         return res;
606 }