]> SALOME platform Git repositories - modules/gui.git/blob - src/PyViewer/PyViewer_ViewWindow.cxx
Salome HOME
Integrates the python editor as new SALOME viewer. Writes comments. Designs icons...
[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( my_TextEditor, this );
369   aPage.exec();
370 }
371
372 /*!
373   \brief Set preferece values for view.
374  */
375 void PyViewer_ViewWindow::setPreferences()
376 {
377   my_TextEditor->settings()->readSettings();
378   my_TextEditor->updateStatement();
379 }
380
381 /*!
382   \brief Associates the theFilePath with the python view.
383   \param theFilePath file path
384  */
385 void PyViewer_ViewWindow::setCurrentFile( const QString &theFilePath )
386 {
387   my_CurrentFile = theFilePath;
388   my_TextEditor->document()->setModified( false );
389
390   if ( isExternal() )
391   {
392     setWindowModified( false );
393
394     QString aShownName = my_CurrentFile;
395     if ( my_CurrentFile.isEmpty() )
396       aShownName = "untitled.py";
397     setWindowFilePath( aShownName );
398
399     // Set window title with associated file path
400     QFileInfo anInfo( aShownName );
401     setWindowTitle( "Python Viewer - " + anInfo.fileName() + "[*]" );
402   }
403 }
404
405 /*!
406   \brief Checks whether the file is modified.
407   If it has the modifications then ask the user to save it.
408   \return true if the document is saved.
409  */
410 bool PyViewer_ViewWindow::whetherSave()
411 {
412   if ( my_TextEditor->document()->isModified() )
413   {
414     QMessageBox::StandardButton aReturn;
415     aReturn = QMessageBox::warning(
416       this, tr( "TIT_DLG_SAVE" ),tr( "WRN_PY_SAVE_FILE" ),
417       QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
418
419     if ( aReturn == QMessageBox::Save )
420       return onSave();
421     else if ( aReturn == QMessageBox::Cancel )
422       return false;
423   }
424   return true;
425 }
426
427 /*!
428   \brief Opens file.
429   \param theFilePath file path
430  */
431 void PyViewer_ViewWindow::loadFile( const QString &theFilePath )
432 {
433   QFile aFile( theFilePath );
434   if ( !aFile.open(QFile::ReadOnly | QFile::Text) )
435   {
436     QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
437       tr( "WRN_PY_READ_FILE" ).arg( theFilePath ).arg( aFile.errorString() ) );
438     return;
439   }
440
441   QTextStream anInput( &aFile );
442   QApplication::setOverrideCursor( Qt::WaitCursor );
443   my_TextEditor->setPlainText( anInput.readAll() );
444   QApplication::restoreOverrideCursor();
445
446   setCurrentFile( theFilePath );
447   aFile.close();
448   if ( isExternal() )
449     statusBar()->showMessage( tr( "STS_F_LOADED" ), 2000 );
450 }
451
452 /*!
453   \brief Saves file.
454   \param theFilePath file path
455  */
456 bool PyViewer_ViewWindow::saveFile( const QString &theFilePath )
457 {
458   QFile aFile( theFilePath );
459   if ( !aFile.open( QFile::WriteOnly | QFile::Text ) )
460   {
461     QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
462       tr( "WRN_PY_WRITE_FILE" ).arg( theFilePath ).arg( aFile.errorString() ) );
463     return false;
464   }
465
466   QTextStream anOutput( &aFile );
467   QApplication::setOverrideCursor( Qt::WaitCursor );
468   anOutput << my_TextEditor->toPlainText();
469   QApplication::restoreOverrideCursor();
470
471   setCurrentFile( theFilePath );
472   aFile.close();
473
474   if ( isExternal() )
475     statusBar()->showMessage( tr( "STS_F_SAVED" ), 2000 );
476
477   return true;
478 }
479
480 /*!
481   \brief Opens help browser with python view help information.
482  */
483 void PyViewer_ViewWindow::onBrowser()
484 {
485   QDir appDir = QApplication::applicationDirPath();
486   QStringList parameters;
487   parameters << QString( "--file=%1" ).arg( appDir.filePath( "pyeditor.html" ) );
488   QProcess::startDetached( "HelpBrowser", parameters );
489 }