Salome HOME
updated copyright message
[modules/gui.git] / src / STD / STD_Application.cxx
1 // Copyright (C) 2007-2023  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
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, or (at your option) any later version.
10 //
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.
15 //
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
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #include "STD_Application.h"
24
25 #include "STD_MDIDesktop.h"
26
27 #include <SUIT_Tools.h>
28 #include <SUIT_Study.h>
29 #include <SUIT_Desktop.h>
30 #include <SUIT_Session.h>
31 #include <SUIT_MessageBox.h>
32 #include <SUIT_ViewManager.h>
33 #include <SUIT_ResourceMgr.h>
34
35 #include <QtxDockAction.h>
36 #include <QtxMenu.h>
37 #include <QtxActionMenuMgr.h>
38 #include <QtxActionToolMgr.h>
39
40 #include <QMenu>
41 #include <QStatusBar>
42 #include <QCloseEvent>
43 #include <QFileDialog>
44 #include <QApplication>
45
46 /*!Create and return new instance of STD_Application*/
47 extern "C" STD_EXPORT SUIT_Application* createApplication()
48 {
49   return new STD_Application();
50 }
51
52 /*!Constructor.*/
53 STD_Application::STD_Application()
54 : SUIT_Application(),
55   myActiveViewMgr( 0 ),
56   myNotify( 0 ),
57   myExitConfirm( true ),
58   myEditEnabled( true )
59 {
60   setDesktop( new STD_MDIDesktop() );
61 }
62
63 /*!Destructor.*/
64 STD_Application::~STD_Application()
65 {
66   clearViewManagers();
67 }
68
69 /*! \retval requirement of exit confirmation*/
70 bool STD_Application::exitConfirmation() const
71 {
72   return myExitConfirm;
73 }
74
75 /*! Set the requirement of exit confirmation*/
76 void STD_Application::setExitConfirmation( const bool on )
77 {
78   myExitConfirm = on;
79 }
80
81 /*! \retval QString "StdApplication"*/
82 QString STD_Application::applicationName() const
83 {
84   return QString( "StdApplication" );
85 }
86
87 /*!Start STD_Application*/
88 void STD_Application::start()
89 {
90   createActions();
91   customize();
92
93   updateDesktopTitle();
94   updateCommandsStatus();
95   setEditEnabled( myEditEnabled );
96
97   loadPreferences();
98
99   SUIT_Application::start();
100 }
101
102 /*!
103   Close the Application
104 */
105 void STD_Application::closeApplication()
106 {
107   if ( desktop() )
108     savePreferences();
109   SUIT_Study* study = activeStudy();
110
111   if ( study )
112   {
113     beforeCloseDoc( study );
114
115     study->closeDocument();
116     emit appClosed();
117     setActiveStudy( 0 );
118     //delete study;
119
120     afterCloseDoc();
121   }
122
123   setDesktop( 0 );
124
125   SUIT_Application::closeApplication();
126 }
127
128 /*!Event on closing desktop*/
129 void STD_Application::onDesktopClosing( SUIT_Desktop*, QCloseEvent* e )
130 {
131   if ( SUIT_Session::session()->applications().count() < 2 )
132   {
133     onExit();
134     return;
135   }
136
137   bool closePermanently;
138   if ( !isPossibleToClose( closePermanently ) )
139   {
140     e->ignore();
141     return;
142   }
143
144   closeApplication();
145 }
146
147 /*!Create actions, menus and tools*/
148 void STD_Application::createActions()
149 {
150   SUIT_Desktop* desk = desktop();
151   SUIT_ResourceMgr* resMgr = resourceMgr();
152   if ( !desk || !resMgr )
153     return;
154
155   // Create actions
156
157   createAction( FileNewId, tr( "TOT_DESK_FILE_NEW" ),
158                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_NEW" ) ),
159                 tr( "MEN_DESK_FILE_NEW" ), tr( "PRP_DESK_FILE_NEW" ),
160                 Qt::CTRL+Qt::Key_N, desk, false, this, SLOT( onNewDoc() ) );
161
162   createAction( FileOpenId, tr( "TOT_DESK_FILE_OPEN" ),
163                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_OPEN" ) ),
164                 tr( "MEN_DESK_FILE_OPEN" ), tr( "PRP_DESK_FILE_OPEN" ),
165                 Qt::CTRL+Qt::Key_O, desk, false, this, SLOT( onOpenDoc() ) );
166
167   createAction( FileReopenId, tr( "TOT_DESK_FILE_REOPEN" ), QIcon(),
168                 tr( "MEN_DESK_FILE_REOPEN" ), tr( "PRP_DESK_FILE_REOPEN" ),
169                 0, desk, false, this, SLOT( onReopenDoc() ) );
170
171   createAction( FileCloseId, tr( "TOT_DESK_FILE_CLOSE" ),
172                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_CLOSE" ) ),
173                 tr( "MEN_DESK_FILE_CLOSE" ), tr( "PRP_DESK_FILE_CLOSE" ),
174                 Qt::CTRL+Qt::Key_W, desk, false, this, SLOT( onCloseDoc() ) );
175   //no need in this action for mono-study application as it is same as NewDoc
176   action( FileCloseId )->setVisible( false );
177
178   createAction( FileExitId, tr( "TOT_DESK_FILE_EXIT" ), QIcon(),
179                 tr( "MEN_DESK_FILE_EXIT" ), tr( "PRP_DESK_FILE_EXIT" ),
180                 Qt::CTRL+Qt::Key_Q, desk, false, this, SLOT( onExit() ) );
181
182   createAction( FileSaveId, tr( "TOT_DESK_FILE_SAVE" ),
183                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_SAVE" ) ),
184                 tr( "MEN_DESK_FILE_SAVE" ), tr( "PRP_DESK_FILE_SAVE" ),
185                 Qt::CTRL+Qt::Key_S, desk, false, this, SLOT( onSaveDoc() ) );
186
187   createAction( FileSaveAsId, tr( "TOT_DESK_FILE_SAVEAS" ), QIcon(),
188                 tr( "MEN_DESK_FILE_SAVEAS" ), tr( "PRP_DESK_FILE_SAVEAS" ),
189                 Qt::CTRL+Qt::SHIFT+Qt::Key_S, desk, false, this, SLOT( onSaveAsDoc() ) );
190
191   createAction( EditCopyId, tr( "TOT_DESK_EDIT_COPY" ),
192                 resMgr->loadPixmap( "STD", tr( "ICON_EDIT_COPY" ) ),
193                 tr( "MEN_DESK_EDIT_COPY" ), tr( "PRP_DESK_EDIT_COPY" ),
194                 Qt::CTRL+Qt::Key_C, desk, false, this, SLOT( onCopy() ) );
195
196   createAction( EditPasteId, tr( "TOT_DESK_EDIT_PASTE" ),
197                 resMgr->loadPixmap( "STD", tr( "ICON_EDIT_PASTE" ) ),
198                 tr( "MEN_DESK_EDIT_PASTE" ), tr( "PRP_DESK_EDIT_PASTE" ),
199                 Qt::CTRL+Qt::Key_V, desk, false, this, SLOT( onPaste() ) );
200
201   QAction* a = createAction( ViewStatusBarId, tr( "TOT_DESK_VIEW_STATUSBAR" ),
202                              QIcon(), tr( "MEN_DESK_VIEW_STATUSBAR" ),
203                              tr( "PRP_DESK_VIEW_STATUSBAR" ), Qt::ALT+Qt::SHIFT+Qt::Key_S, desk, true );
204   a->setChecked( desk->statusBar()->isVisibleTo( desk ) );
205   connect( a, SIGNAL( toggled( bool ) ), this, SLOT( onViewStatusBar( bool ) ) );
206
207   createAction( NewWindowId, tr( "TOT_DESK_NEWWINDOW" ), QIcon(),
208                 tr( "MEN_DESK_NEWWINDOW" ), tr( "PRP_DESK_NEWWINDOW" ), 0, desk  );
209
210   createAction( HelpAboutId, tr( "TOT_DESK_HELP_ABOUT" ),
211                 resMgr->loadPixmap( "STD", tr( "ICON_DESK_ABOUT" ) ),
212                 tr( "MEN_DESK_HELP_ABOUT" ), tr( "PRP_DESK_HELP_ABOUT" ),
213                 Qt::ALT+Qt::SHIFT+Qt::Key_A, desk, false, this, SLOT( onHelpAbout() ) );
214
215
216   QtxDockAction* dwa = new QtxDockAction( tr( "TOT_DOCKWINDOWS" ), tr( "MEN_DESK_VIEW_DOCKWINDOWS" ), desk );
217   dwa->setDockType( QtxDockAction::DockWidget );
218   registerAction( ViewWindowsId, dwa );
219
220   QtxDockAction* tba = new QtxDockAction( tr( "TOT_TOOLBARS" ), tr( "MEN_DESK_VIEW_TOOLBARS" ), desk );
221   tba->setDockType( QtxDockAction::ToolBar );
222   registerAction( ViewToolBarsId, tba );
223
224   // Create menus
225
226   int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1, MenuFileId, 0 );
227   // Let the application developers insert some menus between Edit and View
228   int editMenu = createMenu( tr( "MEN_DESK_EDIT" ), -1, MenuEditId, 5 );
229   int viewMenu = createMenu( tr( "MEN_DESK_VIEW" ), -1, MenuViewId, 10 );
230   int helpMenu = createMenu( tr( "MEN_DESK_HELP" ), -1, MenuHelpId, 1000 );
231
232   // Create menu items
233
234   createMenu( FileNewId,    fileMenu, 0 );
235   createMenu( FileOpenId,   fileMenu, 0 );
236   createMenu( FileReopenId, fileMenu, 0 ); 
237   createMenu( FileSaveId,   fileMenu, 5 );
238   createMenu( FileSaveAsId, fileMenu, 5 );
239   createMenu( FileCloseId,  fileMenu, 5 );
240   createMenu( separator(),  fileMenu, -1, 5 );
241
242   createMenu( separator(),  fileMenu );
243   createMenu( FileExitId,   fileMenu );
244
245   createMenu( EditCopyId,  editMenu );
246   createMenu( EditPasteId, editMenu );
247   createMenu( separator(), editMenu );
248
249   createMenu( ViewToolBarsId,  viewMenu, 0 );
250   createMenu( ViewWindowsId,   viewMenu, 0 );
251   createMenu( separator(),     viewMenu, -1, 10 );
252   createMenu( ViewStatusBarId, viewMenu, 10 );
253   createMenu( separator(),     viewMenu );
254
255   createMenu( HelpAboutId, helpMenu );
256   createMenu( separator(), helpMenu );
257
258   // Create tool bars
259
260   int stdTBar = createTool( tr( "INF_DESK_TOOLBAR_STANDARD" ),  // title (language-dependant)
261                             QString( "SalomeStandard" ) );      // name (language-independant)
262
263   // Create tool items
264
265   createTool( FileNewId, stdTBar );
266   createTool( FileOpenId, stdTBar );
267   createTool( FileSaveId, stdTBar );
268   createTool( FileCloseId, stdTBar );
269   createTool( separator(), stdTBar );
270   createTool( EditCopyId, stdTBar );
271   createTool( EditPasteId, stdTBar );
272 }
273
274 /*!
275   Customize actions.
276 */
277 void STD_Application::customize()
278 {
279 }
280
281 /*!Opens new application*/
282 void STD_Application::onNewDoc()
283 {
284   onNewDoc( QString() );
285 }
286
287 /*!Opens new application*/
288 bool STD_Application::onNewDoc( const QString& name )
289 {
290   QApplication::setOverrideCursor( Qt::WaitCursor );
291
292   bool res = true;
293   if ( !activeStudy() )
294   {
295     createEmptyStudy();
296     res = activeStudy()->createDocument( name );
297     if ( res )
298       studyCreated( activeStudy() );
299     else
300     {
301       SUIT_Study* st = activeStudy();
302       setActiveStudy( 0 );
303       delete st;
304     }
305   }
306   else
307   {
308     SUIT_Application* aApp = startApplication( 0, 0 );
309     if ( aApp->inherits( "STD_Application" ) )
310       res = ((STD_Application*)aApp)->onNewDoc( name );
311     else
312     {
313       aApp->createEmptyStudy();
314       res = aApp->activeStudy()->createDocument( name );
315     }
316     if ( !res )
317       aApp->closeApplication();
318   }
319
320   QApplication::restoreOverrideCursor();
321
322   return res;
323 }
324
325 /*!Put file name from file dialog to onOpenDoc(const QString&) function*/
326 void STD_Application::onOpenDoc()
327 {
328   // It is preferrable to use OS-specific file dialog box here !!!
329   QString aName = getFileName( true, QString(), getFileFilter( true ), QString(), 0 );
330   if ( aName.isNull() )
331     return;
332
333   onOpenDoc( aName );
334 }
335
336 /*! \retval \c true, if document was opened successful, else \c false.*/
337 bool STD_Application::onOpenDoc( const QString& aName )
338 {
339   if ( !abortAllOperations() )
340     return false;
341
342   QApplication::setOverrideCursor( Qt::WaitCursor );
343
344   bool res = openAction( openChoice( aName ), aName );
345   
346   QApplication::restoreOverrideCursor();
347
348   return res;
349 }
350
351 /*! Reload document from the file.*/
352 bool STD_Application::onReopenDoc()
353 {
354   bool res = false;
355
356   SUIT_Study* study = activeStudy();
357   if ( study && study->isSaved() ) {
358     // ask user for the confirmation
359     if ( SUIT_MessageBox::question( desktop(), tr( "REOPEN_STUDY" ), tr( "REOPEN_QUESTION" ),
360                                     SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No
361                                     ) == SUIT_MessageBox::No )
362       return false;
363
364     // remember study name
365     QString studyName = study->studyName();
366
367     // close study
368     beforeCloseDoc( study );
369     study->closeDocument( true );
370
371     // update views / windows / status bar / title
372     clearViewManagers();
373     setActiveStudy( 0 );
374
375     // delete study
376     delete study;
377     study = 0;
378     
379     // post closing actions
380     afterCloseDoc();
381
382     // reload study from the file
383     res = useFile( studyName ) && activeStudy();
384
385     // if reloading is failed, close the desktop
386     if ( activeStudy() && !res )
387       closeApplication();
388     else
389     {
390       updateDesktopTitle();
391       updateCommandsStatus();
392     }
393   }
394   return res;
395 }
396
397 /*!Virtual function. Not implemented here.*/
398 void STD_Application::beforeCloseDoc( SUIT_Study* )
399 {
400 }
401
402 /*!Virtual function. Not implemented here.*/
403 void STD_Application::afterCloseDoc()
404 {
405 }
406
407 /*!Close document, if it's possible.*/
408 void STD_Application::onCloseDoc( bool ask )
409 {
410   closeDoc( ask );
411 }
412
413 /*!Close document, if it's possible.*/
414 bool STD_Application::closeDoc( bool ask )
415 {
416   bool closePermanently = true;
417
418   if ( ask && !isPossibleToClose( closePermanently ) )
419     return false;
420
421   return closeActiveDoc( closePermanently );
422 }
423
424 /*!Close document.*/
425 bool STD_Application::closeActiveDoc( bool permanently )
426 {
427   SUIT_Study* study = activeStudy();
428   if ( !study ) // no active study
429     return true;
430
431   beforeCloseDoc( study );
432
433   if ( study )
434     study->closeDocument( permanently );
435
436   clearViewManagers();
437
438   setActiveStudy( 0 );
439
440   int aNbStudies = 0;
441   QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
442   for ( int i = 0; i < apps.count(); i++ )
443     aNbStudies += apps.at( i )->getNbStudies();
444
445   if ( aNbStudies )
446   {
447     savePreferences();
448     setDesktop( 0 );
449   }
450   else
451   {
452     updateDesktopTitle();
453     updateCommandsStatus();
454   }
455
456   // IPAL19532: deleting study should be performed after calling setDesktop(0)
457   delete study;
458
459   afterCloseDoc();
460
461   if ( !desktop() )
462     closeApplication();
463   return true;
464 }
465
466 /*!Check the application on closing.
467  * \retval \c true if possible, else \c false
468  */
469 bool STD_Application::isPossibleToClose( bool& closePermanently )
470 {
471   if ( activeStudy() )
472   {
473     activeStudy()->abortAllOperations();
474     if ( activeStudy()->isModified() )
475     {
476       QString sName = activeStudy()->studyName().trimmed();
477       return closeAction( closeChoice( sName ), closePermanently );
478     }
479   }
480   return true;
481 }
482
483 int STD_Application::closeChoice( const QString& /*docName*/ )
484 {
485   int answer = SUIT_MessageBox::question( desktop(), tr( "CLOSE_STUDY" ), tr( "CLOSE_QUESTION" ),
486                                           SUIT_MessageBox::Save | SUIT_MessageBox::Discard | SUIT_MessageBox::Cancel,
487                                           SUIT_MessageBox::Save );
488
489   int res = CloseCancel;
490   if ( answer == SUIT_MessageBox::Save )
491     res = CloseSave;
492   else if ( answer == SUIT_MessageBox::Discard )
493     res = CloseDiscard;
494
495   return res;
496 }
497
498 bool STD_Application::closeAction( const int choice, bool& /*closePermanently*/ )
499 {
500   bool res = true;
501   switch( choice )
502   {
503   case CloseSave:
504     if ( activeStudy()->isSaved() )
505       onSaveDoc();
506     else if ( !onSaveAsDoc() )
507       res = false;
508     break;
509   case CloseDiscard:
510     break;
511   case CloseCancel:
512   default:
513     res = false;
514   }
515
516   return res;
517 }
518
519 int STD_Application::openChoice( const QString& aName )
520 {
521   SUIT_Session* aSession = SUIT_Session::session();
522
523   bool isAlreadyOpen = false;
524   QList<SUIT_Application*> aAppList = aSession->applications();
525   for ( QList<SUIT_Application*>::iterator it = aAppList.begin(); it != aAppList.end() && !isAlreadyOpen; ++it )
526     isAlreadyOpen = (*it)->activeStudy() && (*it)->activeStudy()->studyName() == aName;
527   return isAlreadyOpen ? OpenExist : OpenNew;
528 }
529
530 bool STD_Application::openAction( const int choice, const QString& aName )
531 {
532   bool res = true;
533   switch ( choice )
534   {
535   case OpenExist:
536     {
537       SUIT_Application* aApp = 0;
538       SUIT_Session* aSession = SUIT_Session::session();
539       QList<SUIT_Application*> aAppList = aSession->applications();
540       for ( QList<SUIT_Application*>::iterator it = aAppList.begin(); it != aAppList.end() && !aApp; ++it )
541       {
542         if ( (*it)->activeStudy() && (*it)->activeStudy()->studyName() == aName )
543           aApp = *it;
544       }
545       if ( aApp )
546         aApp->desktop()->activateWindow();
547       else
548         res = false;
549     }
550     break;
551   case OpenNew:
552     if ( !activeStudy() )
553       res = useFile( aName );
554     else
555     {
556       SUIT_Application* aApp = startApplication( 0, 0 );
557       if ( aApp )
558         res = aApp->useFile( aName );
559       if ( !res )
560         aApp->closeApplication();
561     }
562     break;
563   case OpenCancel:
564   default:
565     res = false;
566   }
567
568   return res;
569 }
570
571 /*!Save document if all ok, else error message.*/
572 bool STD_Application::onSaveDoc()
573 {
574   if ( !activeStudy() )
575     return false;
576
577   if ( !abortAllOperations() )
578     return false;
579
580   bool isOk = false;
581   if ( activeStudy()->isSaved() )
582   {
583     putInfo( tr( "INF_DOC_SAVING" ) + activeStudy()->studyName() );
584
585     QApplication::setOverrideCursor( Qt::WaitCursor );
586
587     isOk = activeStudy()->saveDocument();
588
589     QApplication::restoreOverrideCursor();
590
591     if ( !isOk )
592     {
593       putInfo( "" );
594       // displaying a message box as SUIT_Validator in case file can't be written (the most frequent case)
595       SUIT_MessageBox::critical( desktop(), tr( "ERR_ERROR" ),
596                                  tr( "INF_DOC_SAVING_FAILS" ).arg( activeStudy()->studyName() ) );
597     }
598     else
599       putInfo( tr( "INF_DOC_SAVED" ).arg( "" ) );
600   }
601
602   if ( isOk )
603     studySaved( activeStudy() );
604   else
605     isOk = onSaveAsDoc();
606   return isOk;
607 }
608
609 /*! \retval \c true, if document saved successfully, else \c false.*/
610 bool STD_Application::onSaveAsDoc()
611 {
612   SUIT_Study* study = activeStudy();
613   if ( !study )
614     return false;
615
616   if ( !abortAllOperations() )
617     return false;
618
619   bool isOk = false;
620   while ( !isOk )
621   {
622     QString aName = getFileName( false, study->studyName(), getFileFilter( false ), QString(), 0 );
623     if ( aName.isNull() )
624       return false;
625
626     QApplication::setOverrideCursor( Qt::WaitCursor );
627
628     putInfo( tr( "INF_DOC_SAVING" ) + aName );
629     isOk = study->saveDocumentAs( aName );
630
631     putInfo( isOk ? tr( "INF_DOC_SAVED" ).arg( aName ) : "" );
632
633     QApplication::restoreOverrideCursor();
634
635     if ( !isOk )
636       SUIT_MessageBox::critical( desktop(), tr( "ERROR" ), tr( "INF_DOC_SAVING_FAILS" ).arg( aName ) );
637   }
638
639   studySaved( activeStudy() );
640
641   return isOk;
642 }
643
644 /*!Closing session.*/
645 void STD_Application::onExit()
646 {
647   int aAnswer = SUIT_MessageBox::Ok;
648   if ( exitConfirmation() )
649     aAnswer = SUIT_MessageBox::question( desktop(), tr( "INF_DESK_EXIT" ), tr( "QUE_DESK_EXIT" ),
650                                          SUIT_MessageBox::Ok | SUIT_MessageBox::Cancel, SUIT_MessageBox::Cancel );
651   if ( aAnswer == SUIT_MessageBox::Ok )
652     SUIT_Session::session()->closeSession();
653 }
654
655 /*!Virtual slot. Not implemented here.*/
656 void STD_Application::onCopy()
657 {
658 }
659
660 /*!Virtual slot. Not implemented here.*/
661 void STD_Application::onPaste()
662 {
663 }
664
665 /*!Sets \a theEnable for menu manager and for tool manager.*/
666 void STD_Application::setEditEnabled( bool theEnable )
667 {
668   myEditEnabled = theEnable;
669
670   QtxActionMenuMgr* mMgr = desktop()->menuMgr();
671   QtxActionToolMgr* tMgr = desktop()->toolMgr();
672
673   for ( int i = EditCopyId; i <= EditPasteId; i++ )
674   {
675     mMgr->setShown( mMgr->actionId(action(i)), myEditEnabled );
676     tMgr->setShown( tMgr->actionId(action(i)), myEditEnabled );
677   }
678 }
679
680 /*!\retval \c true, if document opened successfully, else \c false.*/
681 bool STD_Application::useFile(const QString& theFileName)
682 {
683   bool res = SUIT_Application::useFile( theFileName );
684
685   if ( res )
686     studyOpened( activeStudy() );
687
688   return res;
689 }
690
691 /*!Update desktop title.*/
692 void STD_Application::updateDesktopTitle()
693 {
694   QString aTitle = applicationName();
695   QString aVer = applicationVersion();
696   if ( !aVer.isEmpty() )
697     aTitle += QString( " " ) + aVer;
698
699   if ( activeStudy() )
700   {
701     QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false );
702     if ( !sName.isEmpty() )
703       aTitle += QString( " - [%1]" ).arg( sName );
704   }
705
706   desktop()->setWindowTitle( aTitle );
707 }
708
709 /*!Update commands status.*/
710 void STD_Application::updateCommandsStatus()
711 {
712   SUIT_Application::updateCommandsStatus();
713
714   bool aHasStudy     = activeStudy() != 0;
715   bool aSaved        = aHasStudy && activeStudy()->isSaved();
716   bool aModified     = aHasStudy && activeStudy()->isModified();
717   bool aIsNeedToSave = aHasStudy && ( !aSaved || aModified );
718
719  if ( action( FileReopenId ) )
720     action( FileReopenId )->setEnabled( aSaved );
721  if ( action( FileSaveId ) )
722     action( FileSaveId )->setEnabled( aIsNeedToSave );
723   if ( action( FileSaveAsId ) )
724     action( FileSaveAsId )->setEnabled( aHasStudy );
725   if ( action( FileCloseId ) )
726     action( FileCloseId )->setEnabled( aHasStudy );
727   if ( action( NewWindowId ) )
728     action( NewWindowId )->setEnabled( aHasStudy );
729 }
730
731 /*!
732   \brief Show notification with specified text and title.
733   
734   Notification will be automatically hidden after specified \a timeout
735   (given in milliseconds). If \a timeout is zero, the notification
736   is not automatically hidden; it can be only closed by the user manually.
737   
738   \param text - Notification text
739   \param title - Notification title
740   \param timeout - Timeout in milliseconds
741   \return Notification's unique identifier
742 */
743 int STD_Application::showNotification(const QString& message, const QString& title, int timeout)
744 {
745   int uid = -1;
746   QtxNotify* ntfMgr = notifyMgr();
747   if (ntfMgr)
748   {
749     int delay = timeout;
750     if (delay < 0)
751     {
752       SUIT_ResourceMgr* aResMgr = resourceMgr();
753       if (aResMgr)
754         delay = aResMgr->integerValue("notification", "timeout", 0) * 1000;
755     }
756     uid = ntfMgr->showNotification(message, title, qMax(delay, 0));
757   }
758   return uid;
759 }
760
761 /*!
762   \brief Close notifications with specified text.
763   \param text - Notification text
764 */
765 void STD_Application::hideNotification(const QString& message)
766 {
767   QtxNotify* ntfMgr = notifyMgr();
768   if (ntfMgr)
769     ntfMgr->hideNotification(message);
770 }
771
772 /*!
773   \brief Closes the notifications with specified identifier.
774   \param id - Notification identifier
775 */
776 void STD_Application::hideNotification(int id)
777 {
778   QtxNotify* ntfMgr = notifyMgr();
779   if (ntfMgr)
780     ntfMgr->hideNotification(id);
781 }
782
783 /*!\retval SUIT_ViewManager by viewer manager type name.*/
784 SUIT_ViewManager* STD_Application::viewManager( const QString& vmType ) const
785 {
786   SUIT_ViewManager* vm = 0;
787   for ( QList<SUIT_ViewManager*>::const_iterator it = myViewMgrs.begin(); it != myViewMgrs.end() && !vm; ++it )
788   {
789     bool keepDetached = property("keep_detached").toBool();
790     if ( (*it)->getType() == vmType && (!(*it)->getDetached() || keepDetached))
791       vm = *it;
792   }
793   return vm;
794 }
795
796 /*! \param vmType - input view manager type name
797  * \param lst - output list of view managers with types \a vmType.
798  */
799 void STD_Application::viewManagers( const QString& vmType, ViewManagerList& lst ) const
800 {
801   for ( QList<SUIT_ViewManager*>::const_iterator it = myViewMgrs.begin(); it != myViewMgrs.end(); ++it )
802   {
803     if ( (*it)->getType() == vmType )
804       lst.append( *it );
805   }
806 }
807
808 /*!\param lst - output list of all view managers.*/
809 void STD_Application::viewManagers( ViewManagerList& lst ) const
810 {
811   for ( QList<SUIT_ViewManager*>::const_iterator it = myViewMgrs.begin(); it != myViewMgrs.end(); ++it )
812     lst.append( *it );
813 }
814
815 /*!\retval ViewManagerList - const list of all view managers.*/
816 ViewManagerList STD_Application::viewManagers() const
817 {
818   ViewManagerList lst;
819   viewManagers( lst );
820   return lst;
821 }
822
823 /*!\retval SUIT_ViewManager - return pointer to active view manager.*/
824 SUIT_ViewManager* STD_Application::activeViewManager() const
825 {
826   return myActiveViewMgr;
827 }
828
829 /*!Add view manager to view managers list, if it not already there.*/
830 void STD_Application::addViewManager( SUIT_ViewManager* vm )
831 {
832   if ( !vm )
833     return;
834
835   if ( !containsViewManager( vm ) )
836   {
837     myViewMgrs.append( vm );
838     connect( vm, SIGNAL( activated( SUIT_ViewManager* ) ),
839              this, SLOT( onViewManagerActivated( SUIT_ViewManager* ) ) );
840     vm->connectPopupRequest( this, SLOT( onConnectPopupRequest( SUIT_PopupClient*, QContextMenuEvent* ) ) );
841
842     emit viewManagerAdded( vm );
843   }
844 /*
845   if ( !activeViewManager() && myViewMgrs.count() == 1 )
846     setActiveViewManager( vm );
847 */
848 }
849
850 /*!Remove view manager from view managers list.*/
851 void STD_Application::removeViewManager( SUIT_ViewManager* vm )
852 {
853   if ( !vm )
854     return;
855
856   vm->closeAllViews();
857
858   emit viewManagerRemoved( vm );
859
860   vm->disconnectPopupRequest( this, SLOT( onConnectPopupRequest( SUIT_PopupClient*, QContextMenuEvent* ) ) );
861   disconnect( vm, SIGNAL( activated( SUIT_ViewManager* ) ),
862              this, SLOT( onViewManagerActivated( SUIT_ViewManager* ) ) );
863   myViewMgrs.removeAll( vm );
864
865   if ( myActiveViewMgr == vm )
866     myActiveViewMgr = 0;
867 }
868
869 /*!Remove all view managers from view managers list.*/
870 void STD_Application::clearViewManagers()
871 {
872   ViewManagerList lst;
873   viewManagers( lst );
874
875   for ( QList<SUIT_ViewManager*>::iterator it = lst.begin(); it != lst.end(); ++it )
876   {
877     QPointer<SUIT_ViewManager> vm = *it;
878     removeViewManager( vm );
879     delete vm;
880   }
881 }
882
883 /*!\retval \c true, if view manager \a vm, already in view manager list (\a myViewMgrs).*/
884 bool STD_Application::containsViewManager( SUIT_ViewManager* vm ) const
885 {
886   return myViewMgrs.contains( vm );
887 }
888
889 /*!Private slot, sets active manager to \vm, if \vm in view managers list.*/
890 void STD_Application::onViewManagerActivated( SUIT_ViewManager* vm )
891 {
892   setActiveViewManager( vm );
893 }
894
895 /*!Shows status bar, if on is \c true, else hides status bar.*/
896 void STD_Application::onViewStatusBar( bool on )
897 {
898   if ( on )
899     desktop()->statusBar()->show();
900   else
901     desktop()->statusBar()->hide();
902 }
903
904 /*!Call SUIT_MessageBox::info1(...) with about information.*/
905 void STD_Application::onHelpAbout()
906 {
907   SUIT_MessageBox::information( desktop(), tr( "About" ), tr( "ABOUT_INFO" ) );
908 }
909
910 /*!Create empty study. \n
911  * Create new view manager and adding it to view managers list.
912  */
913 void STD_Application::createEmptyStudy()
914 {
915   SUIT_Application::createEmptyStudy();
916 }
917
918 /*!Sets active manager to \vm, if \vm in view managers list.*/
919 void STD_Application::setActiveViewManager( SUIT_ViewManager* vm )
920 {
921   if ( !containsViewManager( vm ) )
922     return;
923
924   myActiveViewMgr = vm;
925   emit viewManagerActivated( vm );
926 }
927
928 /*!Public slot. */
929 void STD_Application::onConnectPopupRequest( SUIT_PopupClient* client, QContextMenuEvent* e )
930 {
931   QtxMenu* popup = new QtxMenu();
932   // fill popup by own items
933   QString title;
934   contextMenuPopup( client->popupClientType(), popup, title );
935   popup->setTitleText( title );
936
937   popup->addSeparator();
938   // add items from popup client
939   client->contextMenuPopup( popup );
940
941   SUIT_Tools::simplifySeparators( popup );
942
943   if ( !popup->actions().isEmpty() )
944     popup->exec( e->globalPos() );
945   delete popup;
946 }
947
948 /*!\retval QString - return file extension(s).*/
949 QString STD_Application::getFileFilter( bool /*open*/ ) const
950 {
951   return QString();
952 }
953
954 /*!\retval QString - return file name from dialog.*/
955 QString STD_Application::getFileName( bool open, const QString& initial, const QString& filters,
956                                       const QString& caption, QWidget* parent )
957 {
958   if ( !parent )
959     parent = desktop();
960   if ( open )
961     return QFileDialog::getOpenFileName( parent, caption, initial, filters );
962   else
963   {
964     QString aName;
965     QString aUsedFilter;
966     QString anOldPath = initial;
967
968     bool isOk = false;
969     while ( !isOk )
970     {
971       // It is preferrable to use OS-specific file dialog box here !!!
972       aName = QFileDialog::getSaveFileName( parent, caption, anOldPath, filters, &aUsedFilter );
973
974       if ( aName.isNull() )
975         isOk = true;
976       else
977       {
978         int aEnd = aUsedFilter.lastIndexOf( ')' );
979         int aStart = aUsedFilter.lastIndexOf( '(', aEnd );
980         QString wcStr = aUsedFilter.mid( aStart + 1, aEnd - aStart - 1 );
981
982         int idx = 0;
983         QStringList extList;
984         QRegExp rx( "[\b\\*]*\\.([\\w]+)" );
985         while ( ( idx = rx.indexIn( wcStr, idx ) ) != -1 )
986         {
987           extList.append( rx.cap( 1 ) );
988           idx += rx.matchedLength();
989         }
990
991         if ( !extList.isEmpty() && !extList.contains( SUIT_Tools::extension( aName ) ) )
992           aName += QString( ".%1" ).arg( extList.first() );
993
994         if ( QFileInfo( aName ).exists() )
995         {
996           int aAnswer = SUIT_MessageBox::question( desktop(), tr( "TIT_FILE_SAVEAS" ),
997                                                    tr( "MSG_FILE_EXISTS" ).arg( aName ),
998                                                    SUIT_MessageBox::Yes | SUIT_MessageBox::No | SUIT_MessageBox::Cancel, SUIT_MessageBox::Yes );
999           if ( aAnswer == SUIT_MessageBox::Cancel )
1000           {     // cancelled
1001             aName = QString();
1002             isOk = true;
1003           }
1004           else if ( aAnswer == SUIT_MessageBox::No ) // not save to this file
1005             anOldPath = aName;             // not to return to the same initial dir at each "while" step
1006           else                     // overwrite the existing file
1007             isOk = true;
1008         }
1009         else
1010           isOk = true;
1011       }
1012     }
1013     return aName;
1014   }
1015 }
1016
1017 /*!\retval QString - return directory name from dialog.*/
1018 QString STD_Application::getDirectory( const QString& initial, const QString& caption, QWidget* parent )
1019 {
1020   if ( !parent )
1021     parent = desktop();
1022
1023   return QFileDialog::getExistingDirectory( parent, caption, initial );
1024 }
1025
1026 /*!
1027   Changes desktop
1028   \param desk - new desktop
1029 */
1030 void STD_Application::setDesktop( SUIT_Desktop* desk )
1031 {
1032   SUIT_Application::setDesktop( desk );
1033
1034   if ( desk ) {
1035     connect( desk, SIGNAL( closing( SUIT_Desktop*, QCloseEvent* ) ),
1036              this, SLOT( onDesktopClosing( SUIT_Desktop*, QCloseEvent* ) ), Qt::UniqueConnection );
1037   }
1038 }
1039
1040 /*!
1041   Allow to load preferences before the desktop will be shown.
1042 */
1043 void STD_Application::loadPreferences()
1044 {
1045 }
1046
1047 /*!
1048   Allow to save preferences before the application will be closed.
1049 */
1050 void STD_Application::savePreferences()
1051 {
1052 }
1053
1054 /*!
1055   Custom activity after study is created
1056   Updates desktop and actions
1057 */
1058 void STD_Application::studyCreated( SUIT_Study* )
1059 {
1060   updateDesktopTitle();
1061   updateCommandsStatus();
1062 }
1063
1064 /*!
1065   Custom activity after study is opened
1066   Updates desktop and actions
1067 */
1068 void STD_Application::studyOpened( SUIT_Study* )
1069 {
1070   updateDesktopTitle();
1071   updateCommandsStatus();
1072 }
1073
1074 /*!
1075   Custom activity after study is opened
1076   Updates desktop and actions
1077 */
1078 void STD_Application::studySaved( SUIT_Study* )
1079 {
1080   updateDesktopTitle();
1081   updateCommandsStatus();
1082 }
1083
1084 /*!
1085   Return index of the view ma
1086 */
1087 int STD_Application::viewManagerId( const SUIT_ViewManager* theManager) const
1088 {
1089   return myViewMgrs.indexOf(const_cast<SUIT_ViewManager*>(theManager));
1090 }
1091
1092 /*!
1093   \brief Abort active operations if there are any
1094   \return \c false if some operation cannot be aborted
1095 */
1096 bool STD_Application::abortAllOperations()
1097 {
1098   return true;
1099 }
1100
1101 /*!
1102   \brief Gets the notification manager. Creates it if not exists.
1103   \return \c notification manager instance
1104 */
1105 QtxNotify* STD_Application::notifyMgr()
1106 {
1107   if ( !myNotify )
1108   {
1109     myNotify = new QtxNotify(desktop());
1110     myNotify->setWindow(desktop());
1111
1112     SUIT_ResourceMgr* aResMgr = resourceMgr();
1113     if (aResMgr)
1114     {
1115       int anim = aResMgr->integerValue("notification", "animation", 0);
1116       myNotify->setAnimationTime(anim);
1117
1118       double size = aResMgr->integerValue("notification", "size", 250);
1119       myNotify->setNotificationSize(size);
1120     }
1121   }
1122   return myNotify;
1123 }