]> SALOME platform Git repositories - modules/gui.git/blob - src/STD/STD_Application.cxx
Salome HOME
no message
[modules/gui.git] / src / STD / STD_Application.cxx
1 #include "STD_Application.h"
2
3 #include "STD_MDIDesktop.h"
4
5 #include <SUIT_Tools.h>
6 #include <SUIT_Desktop.h>
7 #include <SUIT_Session.h>
8 #include <SUIT_ViewModel.h>
9 #include <SUIT_Operation.h>
10 #include <SUIT_MessageBox.h>
11 #include <SUIT_ResourceMgr.h>
12
13 #include <QtxDockAction.h>
14 #include <QtxActionMenuMgr.h>
15 #include <QtxActionToolMgr.h>
16 #include <QtxPopupMenu.h>
17
18 #include <qmenubar.h>
19 #include <qtoolbar.h>
20 #include <qpopupmenu.h>
21 #include <qstatusbar.h>
22 #include <qfiledialog.h>
23 #include <qapplication.h>
24
25 extern "C" STD_EXPORT SUIT_Application* createApplication()
26 {
27   return new STD_Application();
28 }
29
30 STD_Application::STD_Application()
31 : SUIT_Application(),
32 myEditEnabled( true ),
33 myActiveViewMgr( 0 )
34 {
35   STD_MDIDesktop* desk = new STD_MDIDesktop();
36
37   connect( desk, SIGNAL( closing( SUIT_Desktop*, QCloseEvent* ) ),
38            this, SLOT( onDesktopClosing( SUIT_Desktop*, QCloseEvent* ) ) );
39
40   setDesktop( desk );
41 }
42
43 STD_Application::~STD_Application()
44 {
45 }
46
47 QString STD_Application::applicationName() const
48 {
49   return QString( "StdApplication" );
50 }
51
52 void STD_Application::start()
53 {
54   createActions();
55
56   updateDesktopTitle();
57   updateCommandsStatus();
58   setEditEnabled( myEditEnabled );
59
60   SUIT_Application::start();
61 }
62
63 void STD_Application::onDesktopClosing( SUIT_Desktop*, QCloseEvent* e )
64 {
65   if ( !isPossibleToClose() )
66   {
67     e->ignore();
68     return;
69   }
70
71   SUIT_Study* study = activeStudy();
72
73   if ( study )
74     study->closeDocument();
75
76   setActiveStudy( 0 );
77   delete study;
78
79   setDesktop( 0 );
80
81   closeApplication();
82 }
83
84 void STD_Application::createActions()
85 {
86   SUIT_Desktop* desk = desktop();
87   SUIT_ResourceMgr* resMgr = resourceMgr();
88   if ( !desk || !resMgr )
89     return;
90
91   // Create actions
92
93   createAction( FileNewId, tr( "TOT_DESK_FILE_NEW" ),
94                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_NEW" ) ),
95                 tr( "MEN_DESK_FILE_NEW" ), tr( "PRP_DESK_FILE_NEW" ),
96                 CTRL+Key_N, desk, false, this, SLOT( onNewDoc() ) );
97
98   createAction( FileOpenId, tr( "TOT_DESK_FILE_OPEN" ),
99                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_OPEN" ) ),
100                 tr( "MEN_DESK_FILE_OPEN" ), tr( "PRP_DESK_FILE_OPEN" ),
101                 CTRL+Key_O, desk, false, this, SLOT( onOpenDoc() ) );
102
103   createAction( FileCloseId, tr( "TOT_DESK_FILE_CLOSE" ),
104                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_CLOSE" ) ),
105                 tr( "MEN_DESK_FILE_CLOSE" ), tr( "PRP_DESK_FILE_CLOSE" ),
106                 CTRL+Key_W, desk, false, this, SLOT( onCloseDoc() ) );
107
108   createAction( FileExitId, tr( "TOT_DESK_FILE_EXIT" ), QIconSet(),
109                 tr( "MEN_DESK_FILE_EXIT" ), tr( "PRP_DESK_FILE_EXIT" ),
110                 CTRL+Key_Q, desk, false, this, SLOT( onExit() ) );
111
112   createAction( FileSaveId, tr( "TOT_DESK_FILE_SAVE" ),
113                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_SAVE" ) ),
114                 tr( "MEN_DESK_FILE_SAVE" ), tr( "PRP_DESK_FILE_SAVE" ),
115                 CTRL+Key_S, desk, false, this, SLOT( onSaveDoc() ) );
116
117   createAction( FileSaveAsId, tr( "TOT_DESK_FILE_SAVEAS" ), QIconSet(),
118                 tr( "MEN_DESK_FILE_SAVEAS" ), tr( "PRP_DESK_FILE_SAVEAS" ),
119                 0, desk, false, this, SLOT( onSaveAsDoc() ) );
120
121   createAction( EditCopyId, tr( "TOT_DESK_EDIT_COPY" ),
122                 resMgr->loadPixmap( "STD", tr( "ICON_EDIT_COPY" ) ),
123                 tr( "MEN_DESK_EDIT_COPY" ), tr( "PRP_DESK_EDIT_COPY" ),
124                 CTRL+Key_C, desk, false, this, SLOT( onCopy() ) );
125
126   createAction( EditPasteId, tr( "TOT_DESK_EDIT_PASTE" ),
127                 resMgr->loadPixmap( "STD", tr( "ICON_EDIT_PASTE" ) ),
128                 tr( "MEN_DESK_EDIT_PASTE" ), tr( "PRP_DESK_EDIT_PASTE" ),
129                 CTRL+Key_V, desk, false, this, SLOT( onPaste() ) );
130
131   QAction* a = createAction( ViewStatusBarId, tr( "TOT_DESK_VIEW_STATUSBAR" ),
132                              QIconSet(), tr( "MEN_DESK_VIEW_STATUSBAR" ),
133                              tr( "PRP_DESK_VIEW_STATUSBAR" ), 0, desk, true );
134   a->setOn( desk->statusBar()->isVisibleTo( desk ) );
135   connect( a, SIGNAL( toggled( bool ) ), this, SLOT( onViewStatusBar( bool ) ) );
136
137   createAction( NewWindowId, tr( "TOT_DESK_NEWWINDOW" ), QIconSet(),
138                 tr( "MEN_DESK_NEWWINDOW" ), tr( "PRP_DESK_NEWWINDOW" ), 0, desk  );
139
140   createAction( HelpAboutId, tr( "TOT_DESK_HELP_ABOUT" ), QIconSet(),
141                 tr( "MEN_DESK_HELP_ABOUT" ), tr( "PRP_DESK_HELP_ABOUT" ),
142                 0, desk, false, this, SLOT( onHelpAbout() ) );
143
144   QtxDockAction* da = new QtxDockAction( tr( "TOT_DOCK_WINDOWS" ), tr( "MEN_DOCK_WINDOWS" ), desk );
145   registerAction( ViewWindowsId, da );
146   da->setAutoPlace( false );
147
148   // Create menus
149
150   int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1, -1, 0 );
151   int editMenu = createMenu( tr( "MEN_DESK_EDIT" ), -1, -1, 10 );
152   int viewMenu = createMenu( tr( "MEN_DESK_VIEW" ), -1, -1, 10 );
153   int helpMenu = createMenu( tr( "MEN_DESK_HELP" ), -1, -1, 1000 );
154
155   // Create menu items
156
157   createMenu( FileNewId, fileMenu, 0 );
158   createMenu( FileOpenId, fileMenu, 0 );
159   createMenu( FileCloseId, fileMenu, 0 );
160   createMenu( separator(), fileMenu, -1, 0 );
161   createMenu( FileSaveId, fileMenu, 0 );
162   createMenu( FileSaveAsId, fileMenu, 0 );
163   createMenu( separator(), fileMenu, -1, 0 );
164
165   createMenu( separator(), fileMenu );
166   createMenu( FileExitId, fileMenu );
167
168   createMenu( EditCopyId, editMenu );
169   createMenu( EditPasteId, editMenu );
170   createMenu( separator(), editMenu );
171
172   createMenu( ViewWindowsId, viewMenu );
173   createMenu( ViewStatusBarId, viewMenu );
174   createMenu( separator(), viewMenu );
175
176   createMenu( HelpAboutId, helpMenu );
177   createMenu( separator(), helpMenu );
178
179   // Create tool bars
180
181   int stdTBar = createTool( tr( "INF_DESK_TOOLBAR_STANDARD" ) );
182
183   // Create tool items
184
185   createTool( FileNewId, stdTBar );
186   createTool( FileOpenId, stdTBar );
187   createTool( FileSaveId, stdTBar );
188   createTool( FileCloseId, stdTBar );
189   createTool( separator(), stdTBar );
190   createTool( EditCopyId, stdTBar );
191   createTool( EditPasteId, stdTBar );
192 }
193
194 /*!
195   Opens new application
196 */
197 void STD_Application::onNewDoc() 
198 {
199   if ( !activeStudy() )
200   {
201     createEmptyStudy();
202     activeStudy()->createDocument();
203     updateDesktopTitle();
204     updateCommandsStatus();
205   }
206   else
207   {
208     SUIT_Application* aApp = startApplication( 0, 0 );
209     if ( aApp->inherits( "STD_Application" ) )
210       ((STD_Application*)aApp)->onNewDoc();
211     else {
212       aApp->createEmptyStudy();
213       aApp->activeStudy()->createDocument();
214     }
215   }
216 }
217
218 void STD_Application::onOpenDoc()
219 {
220   // It is preferrable to use OS-specific file dialog box here !!!
221   QString aName = getFileName( true, QString::null, getFileFilter(), QString::null, 0 );
222   if ( aName.isNull() )
223     return;
224
225   onOpenDoc( aName );
226 }
227
228 bool STD_Application::onOpenDoc( const QString& aName )
229 {
230   bool res = true;
231   if ( !activeStudy() )
232   {
233     // if no study - open in current desktop
234     // jfa 21.06.2005:createEmptyStudy();
235     // jfa 21.06.2005:res = activeStudy()->openDocument( aName );
236     // jfa 21.06.2005:updateDesktopTitle();
237     // jfa 21.06.2005:updateCommandsStatus();
238     res = useFile( aName ); // jfa 21.06.2005
239   }
240   else
241   {
242     // if study exists - open in new desktop. Check: is the same file is opened?
243     SUIT_Session* aSession = SUIT_Session::session();
244     QPtrList<SUIT_Application> aAppList = aSession->applications();
245     bool isAlreadyOpen = false;
246     SUIT_Application* aApp = 0;
247     for ( QPtrListIterator<SUIT_Application> it( aAppList ); it.current() && !isAlreadyOpen; ++it )
248     {
249       aApp = it.current();
250       if ( aApp->activeStudy()->studyName() == aName )
251         isAlreadyOpen = true;
252     }
253     if ( !isAlreadyOpen )
254     {
255       aApp = startApplication( 0, 0 );
256       if ( aApp )
257         res = aApp->useFile( aName );
258     }
259     else
260       aApp->desktop()->setActiveWindow();
261   }
262   return res;
263 }
264
265 bool STD_Application::onLoadDoc( const QString& aName )
266 {
267   bool res = true;
268   if ( !activeStudy() )
269   {
270     // if no study - load in current desktop
271     res = useStudy( aName );
272   }
273   else
274   {
275     // if study exists - load in new desktop. Check: is the same file is loaded?
276     SUIT_Session* aSession = SUIT_Session::session();
277     QPtrList<SUIT_Application> aAppList = aSession->applications();
278     bool isAlreadyOpen = false;
279     SUIT_Application* aApp = 0;
280     for ( QPtrListIterator<SUIT_Application> it( aAppList ); it.current() && !isAlreadyOpen; ++it )
281     {
282       aApp = it.current();
283       if ( aApp->activeStudy()->studyName() == aName )
284         isAlreadyOpen = true;
285     }
286     if ( !isAlreadyOpen )
287     {
288       // temporary commented because of "pure virtual method called" execution error.
289       // current state of code is not right, but works somehow.
290       // SALOMEDS::Study of the first found application is replaced by the new one,
291       // while normally the new study must be used by a new application
292       //jfa tmp:aApp = startApplication( 0, 0 );
293       if ( aApp )
294         res = aApp->useStudy( aName );
295     }
296     else
297       aApp->desktop()->setActiveWindow();
298   }
299   return res;
300 }
301
302 void STD_Application::beforeCloseDoc( SUIT_Study* )
303 {
304 }
305
306 void STD_Application::afterCloseDoc()
307 {
308 }
309
310 void STD_Application::onCloseDoc()
311 {
312   if ( !isPossibleToClose() )
313     return;
314
315   SUIT_Study* study = activeStudy();
316
317   beforeCloseDoc( study );
318
319   if ( study )
320     study->closeDocument();
321
322   clearViewManagers();
323
324   setActiveStudy( 0 );
325   delete study;
326
327   int aNbStudies = 0;
328   QPtrList<SUIT_Application> apps = SUIT_Session::session()->applications();
329   for ( unsigned i = 0; i < apps.count(); i++ )
330     aNbStudies += apps.at( i )->getNbStudies();
331
332   // STV: aNbStudies - number of currently existing studies (exclude currently closed)
333   // STV: aNbStudies should be compared with 0.
334   if ( aNbStudies )
335     setDesktop( 0 );
336   else
337   {
338     updateDesktopTitle();
339     updateCommandsStatus();
340   }
341
342   afterCloseDoc();
343
344   if ( !desktop() )
345     closeApplication();
346 }
347
348 bool STD_Application::isPossibleToClose()
349 {
350   if ( activeStudy() )
351   {
352     activeStudy()->abortAllOperations();
353     if ( activeStudy()->isModified() )
354     {
355       QString sName = activeStudy()->studyName().stripWhiteSpace();
356       QString msg = sName.isEmpty() ? tr( "INF_DOC_MODIFIED" ) : tr ( "INF_DOCUMENT_MODIFIED" ).arg( sName );
357       int aAnswer = SUIT_MessageBox::warn3( desktop(), tr( "TOT_DESK_FILE_CLOSE" ), msg,
358                                             tr( "BUT_YES" ), tr( "BUT_NO" ), tr( "BUT_CANCEL" ), 1, 2, 3, 1 );
359       switch ( aAnswer )
360       {
361       case 1:
362         if ( activeStudy()->isSaved() )
363           onSaveDoc();
364         else if ( !onSaveAsDoc() )
365           return false;
366         break;
367       case 2:
368         break;
369       case 3:
370       default:
371         return false;
372       }
373     }
374   }
375   return true;
376 }
377
378 void STD_Application::onSaveDoc()
379 {
380   if ( !activeStudy() )
381     return;
382
383   bool isOk = false;
384   if ( activeStudy()->isSaved() )
385   {
386     isOk = activeStudy()->saveDocument();
387     if ( !isOk )
388       SUIT_MessageBox::error1( desktop(), tr( "TIT_FILE_SAVEAS" ),
389                                tr( "MSG_CANT_SAVE" ).arg( activeStudy()->studyName() ), tr( "BUT_OK" ) );
390   }
391
392   if ( isOk )
393     updateCommandsStatus();
394   else
395     onSaveAsDoc();
396 }
397
398 bool STD_Application::onSaveAsDoc()
399 {
400   SUIT_Study* study = activeStudy();
401   if ( !study )
402     return false;
403
404   QString aName = getFileName( false, study->studyName(), getFileFilter(), QString::null, 0 );
405
406   if ( aName.isNull() ) 
407     return false;
408   bool isOk = study->saveDocumentAs( aName );
409
410   updateDesktopTitle();
411   updateCommandsStatus();
412
413   return isOk;
414 }
415
416 void STD_Application::onExit()
417 {
418   SUIT_Session::session()->closeSession();
419 }
420
421 void STD_Application::onCopy()
422 {
423 }
424
425 void STD_Application::onPaste()
426 {
427 }
428
429 void STD_Application::setEditEnabled( bool theEnable )
430 {
431   myEditEnabled = theEnable;
432
433   QtxActionMenuMgr* mMgr = desktop()->menuMgr();
434   QtxActionToolMgr* tMgr = desktop()->toolMgr();
435
436   for ( int i = EditCopyId; i <= EditPasteId; i++ )
437   {
438     mMgr->setShown( i, myEditEnabled );
439     tMgr->setShown( i, myEditEnabled );
440   }
441 }
442
443 bool STD_Application::useFile(const QString& theFileName)
444 {
445   bool res = SUIT_Application::useFile(theFileName);
446   updateDesktopTitle();
447   updateCommandsStatus();
448   return res;
449 }
450
451 void STD_Application::updateDesktopTitle()
452 {
453   QString aTitle = applicationName();
454   QString aVer = applicationVersion();
455   if ( !aVer.isEmpty() )
456     aTitle += QString( " " ) + aVer;
457
458   if ( activeStudy() )
459   {
460     QString sName = SUIT_Tools::file( activeStudy()->studyName().stripWhiteSpace(), false );
461     if ( !sName.isEmpty() )
462       aTitle += QString( " - [%1]" ).arg( sName );
463   }
464
465   desktop()->setCaption( aTitle );
466 }
467
468 void STD_Application::updateCommandsStatus()
469 {
470   bool aHasStudy = activeStudy() != 0;
471   bool aIsNeedToSave = false;
472   if ( aHasStudy ) 
473     aIsNeedToSave = !activeStudy()->isSaved() || activeStudy()->isModified();
474
475   if ( action( FileSaveId ) )
476     action( FileSaveId )->setEnabled( aIsNeedToSave );
477   if ( action( FileSaveAsId ) )
478     action( FileSaveAsId )->setEnabled( aHasStudy );
479   if ( action( FileCloseId ) )
480     action( FileCloseId )->setEnabled( aHasStudy );
481   if ( action( NewWindowId ) )
482     action( NewWindowId )->setEnabled( aHasStudy );
483 }
484
485 SUIT_ViewManager* STD_Application::viewManager( const QString& vmType ) const
486 {
487   SUIT_ViewManager* vm = 0;
488   for ( QPtrListIterator<SUIT_ViewManager> it( myViewMgrs ); it.current() && !vm; ++it )
489   {
490     if ( it.current()->getType() == vmType )
491       vm = it.current();
492   }
493   return vm;
494 }
495
496 void STD_Application::viewManagers( const QString& vmType, ViewManagerList& lst ) const
497 {
498   for ( QPtrListIterator<SUIT_ViewManager> it( myViewMgrs ); it.current(); ++it )
499     if ( it.current()->getType() == vmType )
500       lst.append( it.current() );
501 }
502
503 void STD_Application::viewManagers( ViewManagerList& lst ) const
504 {
505   for ( QPtrListIterator<SUIT_ViewManager> it( myViewMgrs ); it.current(); ++it )
506     lst.append( it.current() );
507 }
508
509 ViewManagerList STD_Application::viewManagers() const
510 {
511   ViewManagerList lst;
512   viewManagers( lst );
513   return lst;
514 }
515
516 SUIT_ViewManager* STD_Application::activeViewManager() const
517 {
518   return myActiveViewMgr;
519 }
520
521 void STD_Application::addViewManager( SUIT_ViewManager* vm )
522 {
523   if ( !vm )
524     return;
525
526   if ( !containsViewManager( vm ) )
527   {
528     myViewMgrs.append( vm );
529     connect( vm, SIGNAL( activated( SUIT_ViewManager* ) ),
530              this, SLOT( onViewManagerActivated( SUIT_ViewManager* ) ) );
531     vm->connectPopupRequest( this, SLOT( onConnectPopupRequest( SUIT_PopupClient*, QContextMenuEvent* ) ) );
532
533     emit viewManagerAdded( vm );
534   }
535 /*
536   if ( !activeViewManager() && myViewMgrs.count() == 1 )
537     setActiveViewManager( vm );
538 */
539 }
540
541 void STD_Application::removeViewManager( SUIT_ViewManager* vm )
542 {
543   if ( !vm )
544     return;
545
546   vm->closeAllViews();
547
548   emit viewManagerRemoved( vm );
549
550   vm->disconnectPopupRequest( this, SLOT( onConnectPopupRequest( SUIT_PopupClient*, QContextMenuEvent* ) ) );
551   vm->disconnect();
552   myViewMgrs.removeRef( vm );
553
554   if ( myActiveViewMgr == vm )
555     myActiveViewMgr = 0;
556 }
557
558 void STD_Application::clearViewManagers()
559 {
560   ViewManagerList lst;
561   viewManagers( lst );
562
563   for ( QPtrListIterator<SUIT_ViewManager> it( lst ); it.current(); ++it )
564     removeViewManager( it.current() );
565 }
566
567 bool STD_Application::containsViewManager( SUIT_ViewManager* vm ) const
568 {
569   return myViewMgrs.contains( vm ) > 0;
570 }
571
572 void STD_Application::onViewManagerActivated( SUIT_ViewManager* vm )
573 {
574   setActiveViewManager( vm );
575 }
576
577 void STD_Application::onViewStatusBar( bool on )
578 {
579   if ( on )
580     desktop()->statusBar()->show();
581   else
582     desktop()->statusBar()->hide();
583 }
584
585 void STD_Application::onHelpAbout()
586 {
587   SUIT_MessageBox::info1( desktop(), tr( "About" ), tr( "ABOUT_INFO" ), "&OK" );
588 }
589
590 void STD_Application::createEmptyStudy()
591 {
592   SUIT_Application::createEmptyStudy();
593
594   SUIT_ViewManager* vm = new SUIT_ViewManager( activeStudy(), desktop(), new SUIT_ViewModel() );
595
596   addViewManager( vm );
597 }
598
599 void STD_Application::setActiveViewManager( SUIT_ViewManager* vm )
600 {
601   if ( !containsViewManager( vm ) )
602     return;
603
604   myActiveViewMgr = vm;
605   emit viewManagerActivated( vm );
606 }
607
608 void STD_Application::onConnectPopupRequest( SUIT_PopupClient* client, QContextMenuEvent* e )
609 {
610   QtxPopupMenu* popup = new QtxPopupMenu();
611   // fill popup by own items
612   QString title;
613   contextMenuPopup( client->popupClientType(), popup, title );
614   popup->setTitleText( title );
615
616   popup->insertSeparator();
617   // add items from popup client
618   client->contextMenuPopup( popup );
619   
620   SUIT_Tools::simplifySeparators( popup );
621
622   if ( popup->count() )
623     popup->exec( e->globalPos() );
624   delete popup;
625 }
626
627 QString STD_Application::getFileName( bool open, const QString& initial, const QString& filters, 
628                                       const QString& caption, QWidget* parent )
629 {
630   if ( !parent )
631     parent = desktop();
632   if ( open ) 
633   {
634     return QFileDialog::getOpenFileName( initial, filters, parent, 0, caption );
635   }
636   else
637   {
638     QString aName;
639     QString aUsedFilter;
640     QString anOldPath = initial;
641
642     bool isOk = false;
643     while ( !isOk )
644     {
645       // It is preferrable to use OS-specific file dialog box here !!!
646       aName = QFileDialog::getSaveFileName( anOldPath, filters, parent,
647                                             0, caption, &aUsedFilter);
648
649       if ( aName.isNull() )
650         isOk = true;
651       else
652       {
653         if ( !getFileFilter().isNull() ) // check extension and add if it is necessary
654         {
655           int aStart = aUsedFilter.find( '*' );
656           int aEnd = aUsedFilter.find( ')', aStart + 1 );
657           QString aExt = aUsedFilter.mid( aStart + 1, aEnd - aStart - 1 );
658           if ( aExt.contains( '*' ) == 0 ) // if it is not *.*
659           {
660             // Check that there is an extension at the end of the name
661             QString aNameTail = aName.right( aExt.length() );
662             if ( aNameTail != aExt )
663               aName += aExt;
664           }
665         }
666         if ( QFileInfo( aName ).exists() )
667         {
668           int aAnswer = SUIT_MessageBox::warn3( desktop(), tr( "TIT_FILE_SAVEAS" ),
669                                                 tr( "MSG_FILE_EXISTS" ).arg( aName ),
670                                                 tr( "BUT_YES" ), tr( "BUT_NO" ), tr( "BUT_CANCEL" ), 1, 2, 3, 1 );
671           if ( aAnswer == 3 ) {     // cancelled
672             aName = QString::null;
673             isOk = true;
674           }
675           else if ( aAnswer == 2 ) // not save to this file
676             anOldPath = aName;             // not to return to the same initial dir at each "while" step
677           else                     // overwrite the existing file
678             isOk = true;
679         }
680         else
681           isOk = true;
682       }
683     }
684     return aName;
685   }
686 }
687
688 QString STD_Application::getDirectory( const QString& initial, const QString& caption, QWidget* parent )
689 {
690   if ( !parent )
691     parent = desktop();
692   return QFileDialog::getExistingDirectory( initial, parent, 0, caption, true );
693 }