Salome HOME
Correction of PyEditor preferences dialog box:
[modules/gui.git] / src / PyViewer / PyViewer_ViewWindow.cxx
1 // Copyright (C) 2015 OPEN CASCADE
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, or (at your option) any later version.
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/ or email : webmaster.salome@opencascade.com
18 //
19 // File   : PyViewer_ViewWindow.cxx
20 // Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
21 //
22
23 #include "PyViewer_ViewWindow.h"
24
25 #include "PyEditor_Editor.h"
26 #include "PyEditor_Settings.h"
27 #include "PyEditor_SettingsDlg.h"
28
29 #include <SUIT_Session.h>
30 #include <SUIT_ResourceMgr.h>
31
32 #include <QtxAction.h>
33 #include <QtxActionToolMgr.h>
34 #include <QtxMultiAction.h>
35
36 #include <QtGui>
37 #include <QLocale>
38
39 /*!
40   \class PyViewer_ViewWindow
41   \brief Python view window.
42 */
43
44 /*!
45   \brief Constructor.
46   \param theParent parent widget
47 */
48 PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* theDesktop , PyViewer_Viewer* theModel ) :
49   SUIT_ViewWindow(theDesktop),
50   myModel(theModel)
51 {
52   my_IsExternal = (theDesktop == NULL);
53
54   if( isExternal() )
55     initLayout();
56 }
57
58 void PyViewer_ViewWindow::initLayout()
59 {
60   my_TextEditor = new PyEditor_Editor( my_IsExternal ,SUIT_Session::session()->resourceMgr(), this );
61   setCentralWidget( my_TextEditor );
62
63   createActions();
64   createToolBar();
65   setCurrentFile( QString() );
66     
67   if ( isExternal() )
68     {
69       connect( my_TextEditor->document(), SIGNAL( modificationChanged( bool ) ),
70                this, SLOT( setWindowModified( bool ) ) );
71       
72       statusBar()->showMessage( tr("STS_READY") );
73     }  
74 }
75
76 /*!
77   \brief Destructor.
78  */
79 PyViewer_ViewWindow::~PyViewer_ViewWindow()
80 {
81   my_CurrentFile.clear();
82   delete my_TextEditor;
83 }
84
85 /*!
86   \return \c true if the application is external
87  */
88 bool PyViewer_ViewWindow::isExternal()
89 {
90   return my_IsExternal;
91 }
92
93 /*!
94   \brief Creates actions of Python view window.
95 */
96 void PyViewer_ViewWindow::createActions()
97 {
98   QtxActionToolMgr* aMgr = toolMgr();
99   QtxAction* anAction;
100
101   // 1. File operations
102   // 1.1. Create New action
103   anAction = new QtxAction( tr( "MNU_PY_NEW" ), QIcon( ":/images/py_new.png" ),
104                                  tr( "MNU_PY_NEW" ), 0, this );
105   anAction->setStatusTip( tr( "DSC_PY_NEW" ) );
106   anAction->setShortcuts( QKeySequence::New );
107   connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onNew() ) );
108   aMgr->registerAction( anAction, NewId );
109
110   // 1.2 Create Open action
111   anAction = new QtxAction( tr( "MNU_PY_OPEN" ), QIcon( ":/images/py_open.png" ),
112                                   tr( "MNU_PY_OPEN" ), 0, this );
113   anAction->setStatusTip( tr( "DSC_PY_OPEN" ) );
114   anAction->setShortcuts( QKeySequence::Open );
115   connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onOpen() ) );
116   aMgr->registerAction( anAction, OpenId );
117
118   // 1.3. Create Save action
119   anAction = new QtxAction( tr( "MNU_PY_SAVE" ), QIcon( ":/images/py_save.png" ),
120                                   tr( "MNU_PY_SAVE" ), 0, this );
121   anAction->setStatusTip( tr( "DSC_PY_SAVE" ) );
122   anAction->setShortcuts( QKeySequence::Save );
123   connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onSave() ) );
124   aMgr->registerAction( anAction, SaveId );
125   // Set default statement for Save action
126   anAction->setEnabled( my_TextEditor->document()->isModified() );
127   connect( my_TextEditor->document(), SIGNAL( modificationChanged( bool ) ),
128     anAction, SLOT( setEnabled( bool ) ) );
129
130   // 1.4. Create SaveAs action
131   anAction = new QtxAction( tr( "MNU_PY_SAVEAS" ), QIcon( ":/images/py_save_as.png" ),
132                                     tr( "MNU_PY_SAVEAS" ), 0, this );
133   anAction->setStatusTip( tr( "DSC_PY_SAVEAS" ) );
134   anAction->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_S );
135   connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onSaveAs() ) );
136   aMgr->registerAction( anAction, SaveAsId );
137
138   // 1.5 Create multi-action for file operations
139   /*QtxMultiAction* aFileAction = new QtxMultiAction( this );
140   aFileAction->insertAction( aMgr->action( NewId ) );
141   aFileAction->insertAction( aMgr->action( OpenId ) );
142   aFileAction->insertAction( aMgr->action( SaveId ) );
143   aFileAction->insertAction( aMgr->action( SaveAsId ) );
144   aMgr->registerAction( aFileAction, FileOpId );*/
145
146   // 1.6. Create Close action
147   if (isExternal())
148   {
149     anAction = new QtxAction( tr( "MNU_PY_CLOSE" ), QIcon( ":/images/py_close.png" ),
150                               tr( "MNU_PY_CLOSE" ), 0, this );
151     anAction->setStatusTip( tr( "DSC_PY_CLOSE" ) );
152     anAction->setShortcut( Qt::CTRL + Qt::Key_Q );
153     connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( close() ) );
154     aMgr->registerAction( anAction, CloseId );
155   }
156
157   // 2. Edit operations
158   // 2.1. Create Undo action
159   anAction = new QtxAction( tr( "MNU_PY_UNDO" ), QIcon( ":/images/py_undo.png" ),
160                             tr( "MNU_PY_UNDO" ), 0, this );
161   anAction->setStatusTip( tr( "DSC_PY_UNDO" ) );
162   anAction->setShortcuts( QKeySequence::Undo );
163   connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( undo() ) );
164   aMgr->registerAction( anAction, UndoId );
165   // Set default statement for Undo action
166   anAction->setEnabled( my_TextEditor->document()->isUndoAvailable() );
167   connect( my_TextEditor->document(), SIGNAL( undoAvailable( bool ) ),
168            anAction, SLOT( setEnabled( bool ) ) );
169
170   // 2.2. Create Redo action
171   anAction = new QtxAction( tr( "MNU_PY_REDO" ), QIcon( ":/images/py_redo.png" ),
172                             tr( "MNU_PY_REDO" ), 0, this );
173   anAction->setStatusTip( tr( "DSC_PY_REDO" ) );
174   anAction->setShortcuts( QKeySequence::Redo );
175   connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( redo() ) );
176   aMgr->registerAction( anAction, RedoId );
177   // Set default statement for Redo action
178   anAction->setEnabled( my_TextEditor->document()->isRedoAvailable() );
179   connect( my_TextEditor->document(), SIGNAL( redoAvailable( bool ) ),
180            anAction, SLOT( setEnabled( bool ) ) );
181
182   // 2.3. Create Cut action
183   anAction = new QtxAction( tr( "MNU_PY_CUT" ), QIcon( ":/images/py_cut.png" ),
184                             tr( "MNU_PY_CUT" ), 0, this );
185   anAction->setStatusTip( tr( "DSC_PY_CUT" ) );
186   anAction->setShortcuts( QKeySequence::Cut );
187   connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( cut() ) );
188   aMgr->registerAction( anAction, CutId );
189   // Set default statement for Cut action
190   anAction->setEnabled( false );
191   connect( my_TextEditor, SIGNAL( copyAvailable( bool ) ),
192            anAction, SLOT( setEnabled( bool ) ) );
193
194   // 2.4. Create Copy action
195   anAction = new QtxAction( tr( "MNU_PY_COPY" ), QIcon( ":/images/py_copy.png" ),
196                             tr( "MNU_PY_COPY" ), 0, this );
197   anAction->setStatusTip( tr( "DSC_PY_COPY" ) );
198   anAction->setShortcuts( QKeySequence::Copy );
199   connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( copy() ) );
200   aMgr->registerAction( anAction, CopyId );
201   // Set default statement for Copy action
202   anAction->setEnabled( false );
203   connect( my_TextEditor, SIGNAL( copyAvailable( bool ) ),
204            anAction, SLOT( setEnabled( bool ) ) );
205
206   // 2.5. Create Paste action
207   anAction = new QtxAction( tr( "MNU_PY_PASTE" ), QIcon( ":/images/py_paste.png" ),
208                             tr( "MNU_PY_PASTE" ), 0, this );
209   anAction->setStatusTip( tr( "DSC_PY_PASTE" ) );
210   anAction->setShortcuts( QKeySequence::Paste );
211   connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( paste() ) );
212   aMgr->registerAction( anAction, PasteId );
213
214   // 2.6. Create Delete action
215   anAction = new QtxAction( tr( "MNU_PY_DELETE" ), QIcon( ":/images/py_delete.png" ),
216                             tr( "MNU_PY_DELETE" ), 0, this );
217   anAction->setStatusTip( tr( "DSC_PY_DELETE" ) );
218   anAction->setShortcuts( QKeySequence::Delete );
219   connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( deleteSelected() ) );
220   aMgr->registerAction( anAction, DeleteId );
221   // Set default statement for Delete action
222   anAction->setEnabled( false );
223   connect( my_TextEditor, SIGNAL( copyAvailable( bool ) ),
224            anAction, SLOT( setEnabled( bool ) ) );
225
226   // 2.7. Create SelectAll action
227   anAction = new QtxAction( tr( "MNU_PY_SELECTALL" ), QIcon( ":/images/py_select_all.png" ),
228                             tr( "MNU_PY_SELECTALL" ), 0, this );
229   anAction->setStatusTip( tr( "DSC_PY_SELECT_ALL" ) );
230   anAction->setShortcuts( QKeySequence::SelectAll );
231   connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( selectAll() ) );
232   aMgr->registerAction( anAction, SelectAllId );
233
234   // 2.8. Create multi-action for edit operations
235   /*QtxMultiAction* anEditAction = new QtxMultiAction( this );
236   anEditAction->insertAction( aMgr->action( UndoId ) );
237   anEditAction->insertAction( aMgr->action( RedoId ) );
238   anEditAction->insertAction( aMgr->action( CutId ) );
239   anEditAction->insertAction( aMgr->action( CopyId ) );
240   anEditAction->insertAction( aMgr->action( PasteId ) );
241   anEditAction->insertAction( aMgr->action( DeleteId ) );
242   anEditAction->insertAction( aMgr->action( SelectAllId ) );
243   aMgr->registerAction( anEditAction, EditOpId );*/
244
245   // 3. Create Preference action
246   anAction = new QtxAction( tr( "MNU_PY_PREFERENCES" ), QIcon( ":/images/py_preferences.png" ),
247                             tr( "MNU_PY_PREFERENCES" ), 0, this );
248   anAction->setStatusTip( tr( "DSC_PY_PREFERENCES" ) );
249   connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onPreferences() ) );
250   aMgr->registerAction( anAction, PreferencesId );
251
252   // 4. Help operations
253
254   // 4.1. Create Help action
255   anAction = new QtxAction( tr( "MNU_PY_BROWSER" ), QIcon( ":/images/py_browser.png" ),
256                             tr( "MNU_PY_BROWSER" ), 0, this );
257   anAction->setStatusTip( tr( "DSC_PY_BROWSER" ) );
258   connect( anAction, SIGNAL( triggered() ), this, SLOT( onBrowser() ) );
259   //aMgr->registerAction( anAction, BrowserId );
260
261   // 4.2. Create multi-action for help operations
262   /*QtxMultiAction* aHelpAction = new QtxMultiAction( this );
263   aHelpAction->insertAction( aMgr->action( BrowserId ) );
264   aMgr->registerAction( aHelpAction, HelpOpId );*/
265 }
266
267 /*!
268   \brief Create toolbar for the python view window.
269 */
270 void PyViewer_ViewWindow::createToolBar()
271 {
272   QtxActionToolMgr* aMgr = toolMgr();
273   int idTB = aMgr->createToolBar( tr("LBL_TOOLBAR_LABEL"),         // title (language-dependent)
274                                   QString( "PyEditorOperations" ), // name (language-independent)
275                                   false );                         // disable floatable toolbar
276   aMgr->append( NewId, idTB );
277   aMgr->append( OpenId, idTB );
278   aMgr->append( SaveId, idTB );
279   aMgr->append( SaveAsId, idTB );
280   if ( isExternal() )
281     aMgr->append( CloseId, idTB );
282   aMgr->append( aMgr->separator(), idTB );
283   aMgr->append( UndoId, idTB );
284   aMgr->append( RedoId, idTB );
285   aMgr->append( aMgr->separator(), idTB );
286   aMgr->append( CutId, idTB );
287   aMgr->append( CopyId, idTB );
288   aMgr->append( PasteId, idTB );
289   aMgr->append( DeleteId, idTB );
290   aMgr->append( SelectAllId, idTB );
291   aMgr->append( aMgr->separator(), idTB );
292   aMgr->append( PreferencesId, idTB );
293   aMgr->append( aMgr->separator(), idTB );
294   aMgr->append( BrowserId, idTB );
295
296 }
297
298 /*!
299   \brief Reimplemented class is to receive a window close request.
300   \param theEvent event
301 */
302 void PyViewer_ViewWindow::closeEvent( QCloseEvent* theEvent )
303 {
304   if ( whetherSave() )
305     theEvent->accept();
306   else
307     theEvent->ignore();
308 }
309
310 /*!
311   SLOT: Creates a new document
312  */
313 void PyViewer_ViewWindow::onNew()
314 {
315   if ( whetherSave() )
316   {
317     my_TextEditor->clear();
318     setCurrentFile( QString() );
319   }
320 }
321
322 /*!
323   SLOT: Open an existing python file
324  */
325 void PyViewer_ViewWindow::onOpen()
326 {
327   if ( whetherSave() )
328   {
329     QString aFilePath = QFileDialog::getOpenFileName( 
330       this, tr( "TIT_DLG_OPEN" ), QDir::currentPath(), "Python Files (*.py)" );
331
332     if ( !aFilePath.isEmpty() )
333       loadFile( aFilePath );
334   }
335 }
336
337 /*!
338   SLOT: Save the current file
339  */
340 bool PyViewer_ViewWindow::onSave()
341 {
342   if ( my_CurrentFile.isEmpty() )
343     return onSaveAs();
344   else
345     return saveFile( my_CurrentFile );
346 }
347
348
349 /*!
350   SLOT: Save the current file under a new name
351  */
352 bool PyViewer_ViewWindow::onSaveAs()
353 {
354   QString aFilePath = QFileDialog::getSaveFileName(
355     this, tr( "TIT_DLG_SAVEAS" ), QDir::currentPath(), "Python Files (*.py)" );
356
357   if ( !aFilePath.isEmpty() )
358     return saveFile( aFilePath );
359
360   return false;
361 }
362
363 /*!
364   SLOT: Open preferences dialog
365  */
366 void PyViewer_ViewWindow::onPreferences()
367 {
368   PyEditor_SettingsDlg* aPage = new PyEditor_SettingsDlg( my_TextEditor, this );
369   connect( aPage, SIGNAL( onHelpClicked() ),
370            this, SLOT( onHelp() ) );
371   aPage->exec();
372   delete aPage;
373 }
374
375 /*!
376   \brief Set preferece values for view.
377  */
378 void PyViewer_ViewWindow::setPreferences()
379 {
380   my_TextEditor->settings()->readSettings();
381   my_TextEditor->updateStatement();
382 }
383
384 /*!
385   \brief Associates the theFilePath with the python view.
386   \param theFilePath file path
387  */
388 void PyViewer_ViewWindow::setCurrentFile( const QString &theFilePath )
389 {
390   my_CurrentFile = theFilePath;
391   my_TextEditor->document()->setModified( false );
392
393   if ( isExternal() )
394   {
395     setWindowModified( false );
396
397     QString aShownName = my_CurrentFile;
398     if ( my_CurrentFile.isEmpty() )
399       aShownName = "untitled.py";
400     setWindowFilePath( aShownName );
401
402     // Set window title with associated file path
403     QFileInfo anInfo( aShownName );
404     setWindowTitle( "Python Viewer - " + anInfo.fileName() + "[*]" );
405   }
406 }
407
408 /*!
409   \brief Checks whether the file is modified.
410   If it has the modifications then ask the user to save it.
411   \return true if the document is saved.
412  */
413 bool PyViewer_ViewWindow::whetherSave()
414 {
415   if ( my_TextEditor->document()->isModified() )
416   {
417     QMessageBox::StandardButton aReturn;
418     aReturn = QMessageBox::warning(
419       this, tr( "TIT_DLG_SAVE" ),tr( "WRN_PY_SAVE_FILE" ),
420       QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
421
422     if ( aReturn == QMessageBox::Save )
423       return onSave();
424     else if ( aReturn == QMessageBox::Cancel )
425       return false;
426   }
427   return true;
428 }
429
430 /*!
431   \brief Opens file.
432   \param theFilePath file path
433  */
434 void PyViewer_ViewWindow::loadFile( const QString &theFilePath )
435 {
436   QFile aFile( theFilePath );
437   if ( !aFile.open(QFile::ReadOnly | QFile::Text) )
438   {
439     QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
440       tr( "WRN_PY_READ_FILE" ).arg( theFilePath ).arg( aFile.errorString() ) );
441     return;
442   }
443
444   QTextStream anInput( &aFile );
445   QApplication::setOverrideCursor( Qt::WaitCursor );
446   my_TextEditor->setPlainText( anInput.readAll() );
447   QApplication::restoreOverrideCursor();
448
449   setCurrentFile( theFilePath );
450   aFile.close();
451   if ( isExternal() )
452     statusBar()->showMessage( tr( "STS_F_LOADED" ), 2000 );
453 }
454
455 /*!
456   \brief Saves file.
457   \param theFilePath file path
458  */
459 bool PyViewer_ViewWindow::saveFile( const QString &theFilePath )
460 {
461   QFile aFile( theFilePath );
462   if ( !aFile.open( QFile::WriteOnly | QFile::Text ) )
463   {
464     QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
465       tr( "WRN_PY_WRITE_FILE" ).arg( theFilePath ).arg( aFile.errorString() ) );
466     return false;
467   }
468
469   QTextStream anOutput( &aFile );
470   QApplication::setOverrideCursor( Qt::WaitCursor );
471   anOutput << my_TextEditor->toPlainText();
472   QApplication::restoreOverrideCursor();
473
474   setCurrentFile( theFilePath );
475   aFile.close();
476
477   if ( isExternal() )
478     statusBar()->showMessage( tr( "STS_F_SAVED" ), 2000 );
479
480   return true;
481 }
482
483 /*!
484   \brief Opens help browser with python view help information.
485  */
486 void PyViewer_ViewWindow::onBrowser()
487 {
488   QDir appDir = QApplication::applicationDirPath();
489   QStringList parameters;
490   parameters << QString( "--file=%1" ).arg( appDir.filePath( "pyeditor.html" ) );
491   QProcess::startDetached( "HelpBrowser", parameters );
492 }
493
494 /*!
495   Slot, called when user clicks "Help" button in "Preferences" dialog box.
496 */
497 void PyViewer_ViewWindow::onHelp()
498 {
499 #ifndef NO_SUIT
500   SUIT_Application* app = SUIT_Session::session()->activeApplication();
501   if ( app )
502     app->onHelpContextModule( "GUI", "python_viewer_page.html", "custom_python_preferences" );
503 #endif
504 }