Salome HOME
Copyrights update
[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 // Function : QtxPathDialog
65 // Purpose  : Constructor.
66 //================================================================
67
68 QtxPathDialog::QtxPathDialog( const bool import, QWidget* parent, const bool modal, const bool resize, const int buttons, WFlags f )
69 : QtxDialog( parent, 0, modal, resize, buttons, f ),
70 myDefault( -1 ),
71 myEntriesFrame( 0 ),
72 myOptionsFrame( 0 )
73 {
74         initialize();
75
76         setCaption( tr( import ? "Open file" : "Save file" ) );
77
78         setDefaultEntry( createFileEntry( tr( "File name" ), import ? OpenFile : SaveFile ) );
79         QLineEdit* le = fileEntry( defaultEntry() );
80         if ( le )
81                 le->setMinimumWidth( 200 );
82
83         validate();
84
85         setFocusProxy( le );
86 }
87
88 //================================================================
89 // Function : QtxPathDialog
90 // Purpose  : Constructor.
91 //================================================================
92
93 QtxPathDialog::QtxPathDialog( QWidget* parent, const bool modal, const bool resize, const int buttons, WFlags f )
94 : QtxDialog( parent, 0, modal, resize, buttons, f ),
95 myDefault( -1 ),
96 myEntriesFrame( 0 ),
97 myOptionsFrame( 0 )
98 {
99         initialize();
100 }
101
102 //================================================================
103 // Function : ~QtxPathDialog
104 // Purpose  : Destructor.
105 //================================================================
106
107 QtxPathDialog::~QtxPathDialog()
108 {
109 }
110
111 //================================================================
112 // Function : fileName
113 // Purpose  : 
114 //================================================================
115
116 QString QtxPathDialog::fileName() const
117 {
118         return fileName( defaultEntry() );
119 }
120
121 //================================================================
122 // Function : setFileName
123 // Purpose  : 
124 //================================================================
125
126 void QtxPathDialog::setFileName( const QString& txt, const bool autoExtension )
127 {
128         setFileName( defaultEntry(), txt, autoExtension );
129 }
130
131 //================================================================
132 // Function : filter
133 // Purpose  : 
134 //================================================================
135
136 QString QtxPathDialog::filter() const
137 {
138         return myFilter;
139 }
140
141 //================================================================
142 // Function : setFilter
143 // Purpose  : 
144 //================================================================
145
146 void QtxPathDialog::setFilter( const QString& fltr )
147 {
148         myFilter = fltr;
149 }
150
151 //================================================================
152 // Function : show
153 // Purpose  : 
154 //================================================================
155
156 void QtxPathDialog::show()
157 {
158         if ( hasVisibleChildren( myEntriesFrame ) )
159                 myEntriesFrame->show();
160         else
161                 myEntriesFrame->hide();
162
163         if ( hasVisibleChildren( myOptionsFrame ) )
164                 myOptionsFrame->show();
165         else
166                 myOptionsFrame->hide();
167
168         QtxDialog::show();
169 }
170
171 //================================================================
172 // Function : onBrowse
173 // Purpose  : 
174 //================================================================
175
176 void QtxPathDialog::onBrowse()
177 {
178         const QObject* obj = sender();
179
180         int id = -1;
181
182         for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && id == -1; ++it )
183                 if ( it.data().btn == obj )
184                         id = it.key();
185
186         if ( id == -1 )
187                 return;
188
189         FileEntry& entry = myEntries[id];
190
191         bool isDir = entry.mode != OpenFile && entry.mode != SaveFile;
192
193         if ( !entry.dlg )
194         {
195                 entry.dlg = new QFileDialog( QDir::current().path(), QString::null, this, 0, true );
196                 entry.dlg->setCaption( caption() );
197                 switch ( entry.mode )
198                 {
199                 case NewDir:
200                 case OpenDir:
201                 case SaveDir:
202                         isDir = true;
203                         entry.dlg->setMode( QFileDialog::DirectoryOnly );
204                         break;
205                 case SaveFile:
206                         entry.dlg->setMode( QFileDialog::AnyFile );
207                         break;
208                 case OpenFile:
209                 default:
210                         entry.dlg->setMode( QFileDialog::ExistingFile );
211                         break;
212                 }
213         }
214
215         if ( !isDir )
216                 entry.dlg->setFilters( prepareFilters() );
217         entry.dlg->setSelection( fileName( id ) );
218
219         if ( entry.dlg->exec() != Accepted )
220                 return;
221
222         QString fName = entry.dlg->selectedFile();
223
224         if ( fName.isEmpty() )
225                 return;
226
227         if ( QFileInfo( fName ).extension().isEmpty() && !isDir )
228                 fName = autoExtension( fName, entry.dlg->selectedFilter() );
229
230         fName = QDir::convertSeparators( fName );
231         QString prev = QDir::convertSeparators( fileName( id ) );
232         if ( isDir )
233         {
234                 while ( prev.length() && prev.at( prev.length() - 1 ) == QDir::separator() )
235                         prev.remove( prev.length() - 1, 1 );
236                 while ( fName.length() && fName.at( fName.length() - 1 ) == QDir::separator() )
237                         fName.remove( fName.length() - 1, 1 );
238         }
239
240         if ( prev == fName )
241                 return;
242
243         setFileName( id, fName );
244         fileNameChanged( id, fName );
245
246         if ( id == defaultEntry() )
247                 emit fileNameChanged( fName );
248 }
249
250 //================================================================
251 // Function : onReturnPressed
252 // Purpose  : 
253 //================================================================
254
255 void QtxPathDialog::onReturnPressed()
256 {
257         const QObject* obj = sender();
258
259         int id = -1;
260         for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && id == -1; ++it )
261                 if ( it.data().edit == obj )
262                         id = it.key();
263
264         if ( id == -1 )
265                 return;
266
267         fileNameChanged( id, fileName( id ) );
268
269         if ( id == defaultEntry() )
270                 emit fileNameChanged( fileName() );
271 }
272
273 //================================================================
274 // Function : onTextChanged
275 // Purpose  : 
276 //================================================================
277
278 void QtxPathDialog::onTextChanged( const QString& )
279 {
280         validate();
281 }
282
283 //================================================================
284 // Function : validate
285 // Purpose  : 
286 //================================================================
287
288 void QtxPathDialog::validate()
289 {
290         setButtonEnabled( isValid(), OK | Yes );
291 }
292
293 //================================================================
294 // Function : isValid
295 // Purpose  : 
296 //================================================================
297
298 bool QtxPathDialog::isValid()
299 {
300         bool ok = true;
301         for ( FileEntryMap::Iterator it = myEntries.begin(); it != myEntries.end() && ok; ++it )
302                 if ( it.data().edit->isEnabled() )
303                         ok = !it.data().edit->text().stripWhiteSpace().isEmpty();
304
305         return ok;
306 }
307
308 //================================================================
309 // Function : acceptData
310 // Purpose  : 
311 //================================================================
312
313 bool QtxPathDialog::acceptData() const
314 {
315         bool ok = true;
316
317         QWidget* parent = (QWidget*)this;
318
319         FileEntryMap::ConstIterator it;
320         for ( it = myEntries.begin(); it != myEntries.end() && ok; ++it )
321         {
322                 const FileEntry& entry = it.data();
323                 QFileInfo fileInfo( entry.edit->text() );
324                 if ( entry.edit->text().isEmpty() )
325                 {
326                         QMessageBox::critical( parent, caption(), tr( "File name not specified" ),
327                                                                    QMessageBox::Ok, QMessageBox::NoButton );
328                         ok = false;
329                 }
330                 else switch ( entry.mode )
331                 {
332                 case OpenFile:
333                         if ( !fileInfo.exists() )
334                         {
335                                 QMessageBox::critical( parent, caption(), tr( "File \"%1\" does not exist" ).arg( fileInfo.filePath() ),
336                                                                            QMessageBox::Ok, QMessageBox::NoButton );
337                                 ok = false;
338                         }
339                         break;
340                 case SaveFile:
341                         if ( fileInfo.exists() )
342                                 ok = QMessageBox::warning( parent, caption(), tr( "File \"%1\" already exist. Do you want to overwrite it?" ).arg( fileInfo.filePath() ),
343                                                                                    QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
344                         break;
345                 case OpenDir:
346                         if ( !fileInfo.exists() || !fileInfo.isDir() )
347                         {
348                                 QMessageBox::critical( parent, caption(), tr( "Directory \"%1\" does not exist" ).arg( fileInfo.filePath() ),
349                                                                            QMessageBox::Ok, QMessageBox::NoButton );
350                                 ok = false;
351                         }
352                         break;
353                 case SaveDir:
354                         if ( fileInfo.exists() && !fileInfo.isDir() )
355                         {
356                                 QMessageBox::critical( parent, caption(), tr( "Directory \"%1\" can't be created because file with the same name exist" ).arg( fileInfo.filePath() ),
357                                                                            QMessageBox::Ok, QMessageBox::NoButton );
358                                 ok = false;
359                         }
360                         break;
361                 case NewDir:
362                         if ( fileInfo.exists() )
363                         {
364                                 if ( !fileInfo.isDir() )
365                                 {
366                                         QMessageBox::critical( parent, caption(), tr( "Directory \"%1\" can't be created because file with the same name exist" ).arg( fileInfo.filePath() ),
367                                                                                    QMessageBox::Ok, QMessageBox::NoButton );
368                                         ok = false;
369                                 }
370                                 else if ( QDir( fileInfo.filePath() ).count() > 2 )
371                                         ok = QMessageBox::warning( parent, caption(), tr( "Directory \"%1\" not empty. Do you want to remove all files in this directory?" ).arg( fileInfo.filePath() ),
372                                                                                            QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
373                         }
374                         break;
375                 default:
376                         break;
377                 }
378
379                 if ( !ok )
380                         entry.edit->setFocus();
381         }
382
383         return ok;
384 }
385
386 //================================================================
387 // Function : fileNameChanged
388 // Purpose  : 
389 //================================================================
390
391 void QtxPathDialog::fileNameChanged( int, QString )
392 {
393 }
394
395 //================================================================
396 // Function : optionsFrame
397 // Purpose  : 
398 //================================================================
399
400 QFrame* QtxPathDialog::optionsFrame()
401 {
402         return myOptionsFrame;
403 }
404
405 //================================================================
406 // Function : getFileName
407 // Purpose  : 
408 //================================================================
409
410 QString QtxPathDialog::fileName( const int id ) const
411 {
412         QString res;
413         if ( myEntries.contains( id ) )
414                 res = myEntries[id].edit->text();
415
416         return res;
417 }
418
419 //================================================================
420 // Function : setFileName
421 // Purpose  : 
422 //================================================================
423
424 void QtxPathDialog::setFileName( const int id, const QString& txt, const bool autoExt )
425 {
426         int mode;
427         QLineEdit* le = fileEntry( id, mode );
428
429         if ( le )
430         {
431                 if ( autoExt && ( mode == OpenFile || mode == SaveFile ) )
432                         le->setText( autoExtension( txt ) );
433                 else
434                         le->setText( txt );
435         }
436 }
437
438 //================================================================
439 // Function : fileEntry
440 // Purpose  : 
441 //================================================================
442
443 QLineEdit* QtxPathDialog::fileEntry( const int id ) const
444 {
445         QLineEdit* le = 0;
446         if ( myEntries.contains( id ) )
447                 le = myEntries[id].edit;
448
449         return le;
450 }
451
452 //================================================================
453 // Function : fileEntry
454 // Purpose  : 
455 //================================================================
456
457 QLineEdit* QtxPathDialog::fileEntry( const int theId, int& theMode ) const
458 {
459         QLineEdit* le = 0;
460         if ( myEntries.contains( theId ) )
461         {
462                 le = myEntries[theId].edit;
463                 theMode = myEntries[theId].mode;
464         }
465
466         return le;
467 }
468
469 //================================================================
470 // Function : createFileEntry
471 // Purpose  : 
472 //================================================================
473
474 int QtxPathDialog::createFileEntry( const QString& lab, const int mode, const int id )
475 {
476         int num = id;
477         if ( num == -1 )
478         {
479                 num--;
480                 while ( myEntries.contains( num ) )
481                         num--;
482         }
483
484         FileEntry entry;
485         entry.dlg = 0;
486         entry.mode = mode;
487
488         new QLabel( lab, myEntriesFrame );
489         entry.edit = new QLineEdit( myEntriesFrame );
490         entry.btn = new QPushButton( myEntriesFrame );
491         entry.btn->setAutoDefault( false );
492         entry.btn->setPixmap( QPixmap( open_icon ) );
493
494         connect( entry.btn, SIGNAL( clicked() ), this, SLOT( onBrowse() ) );
495         connect( entry.edit, SIGNAL( returnPressed() ), this, SLOT( onReturnPressed() ) );
496         connect( entry.edit, SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) );
497
498         myEntries.insert( num, entry );
499
500         return num;
501 }
502
503 //================================================================
504 // Function : defaultEntry
505 // Purpose  : 
506 //================================================================
507
508 int QtxPathDialog::defaultEntry() const
509 {
510         return myDefault;
511 }
512
513 //================================================================
514 // Function : setDefaultEntry
515 // Purpose  : 
516 //================================================================
517
518 void QtxPathDialog::setDefaultEntry( const int id )
519 {
520         myDefault = id;
521 }
522
523 //================================================================
524 // Function : initialize
525 // Purpose  : 
526 //================================================================
527
528 void QtxPathDialog::initialize()
529 {
530         setCaption( tr( "File dialog" ) );
531
532         QVBoxLayout* main = new QVBoxLayout( mainFrame() );
533         QtxGroupBox* mainGroup = new QtxGroupBox( 1, Qt::Horizontal, "", mainFrame() );
534         mainGroup->setFrameStyle( QFrame::NoFrame );
535   mainGroup->setInsideMargin( 0 );
536         main->addWidget( mainGroup );
537
538         myEntriesFrame = new QGroupBox( 3, Qt::Horizontal, "", mainGroup );
539         myOptionsFrame = new QFrame( mainGroup );
540 }
541
542 //================================================================
543 // Function : prepareFilters
544 // Purpose  : 
545 //================================================================
546
547 QStringList QtxPathDialog::prepareFilters() const
548 {
549         QStringList res;
550         if ( !myFilter.isEmpty() )
551         {
552                 res = QStringList::split( ";;", myFilter );
553                 bool allFilter = false;
554                 for ( QStringList::ConstIterator it = res.begin(); it != res.end() && !allFilter; ++it )
555                 {
556                         QStringList wildCards = filterWildCards( *it );
557                         allFilter = wildCards.findIndex( "*.*" ) != -1;
558                 }
559
560                 if ( !allFilter )
561                         res.append( tr( "All files (*.*)" ) );
562         }
563
564         return res;
565 }
566
567 //================================================================
568 // Function : filterWildCards
569 // Purpose  : 
570 //================================================================
571
572 QStringList QtxPathDialog::filterWildCards( const QString& theFilter ) const
573 {
574         QStringList res;
575
576         int b = theFilter.findRev( "(" );
577         int e = theFilter.findRev( ")" );
578         if ( b != -1 && e != -1 )
579         {
580                 QString content = theFilter.mid( b + 1, e - b - 1 ).stripWhiteSpace();
581                 QStringList lst = QStringList::split( " ", content );
582                 for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it )
583                         if ( (*it).find( "." ) != -1 )
584                                 res.append( (*it).stripWhiteSpace() );
585         }
586         return res;
587 }
588
589 //================================================================
590 // Function : autoExtension
591 // Purpose  : 
592 //================================================================
593
594 QString QtxPathDialog::autoExtension( const QString& theFileName, const QString& theFilter ) const
595 {
596         QString fName = theFileName;
597
598         if ( fName.isEmpty() )
599                 return fName;
600
601         QString filter = theFilter;
602         if ( filter.isEmpty() )
603         {
604                 QStringList filters = prepareFilters();
605                 if ( !filters.isEmpty() )
606                         filter = filters.first();
607         }
608
609         QStringList wildCards = filterWildCards( filter );
610         if ( !wildCards.isEmpty() )
611         {
612                 QString ext = wildCards.first();
613                 if ( ext.find( "." ) != -1 )
614                         ext = ext.mid( ext.find( "." ) + 1 );
615
616                 if ( !ext.isEmpty() && !ext.contains( "*" ) )
617                         fName = QDir::convertSeparators( fName ) + QString( "." ) + ext;
618         }
619
620         return fName;
621 }
622
623 //================================================================
624 // Function : hasVisibleChildren
625 // Purpose  : 
626 //================================================================
627
628 bool QtxPathDialog::hasVisibleChildren( QWidget* wid ) const
629 {
630         bool res = false;
631         if ( wid )
632         {
633                 const QObjectList* aChildren = wid->children();
634                 if ( aChildren )
635                 {
636                         for ( QObjectListIt it( *aChildren ); it.current() && !res; ++it )
637                         {
638                                 if ( it.current()->isWidgetType() )
639                                         res = ((QWidget*)it.current())->isVisibleTo( wid );
640                         }
641                 }
642         }
643         return res;
644 }