Salome HOME
PyEditor refactoring
[modules/gui.git] / src / PyViewer / PyViewer_ViewWindow.cxx
1 // Copyright (C) 2015-2016  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_SettingsDlg.h"
27
28 #include "SUIT_Session.h"
29 #include "SUIT_ResourceMgr.h"
30
31 #include "QtxAction.h"
32 #include "QtxActionToolMgr.h"
33
34 #include <QApplication>
35 #include <QFileDialog>
36 #include <QMessageBox>
37 #include <QTextStream>
38
39 /*!
40   \class PyViewer_ViewWindow
41   \brief Python view window.
42 */
43
44 /*!
45   \brief Constructor.
46   \param desktop SALOME desktop window
47 */
48 PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
49   SUIT_ViewWindow( desktop )
50 {
51   // Create editor and set it as a central widget.
52   myTextEditor = new PyEditor_Editor( this );
53   setCentralWidget( myTextEditor );
54
55   // Create actions.
56   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
57   QtxAction* action;
58
59   // . New
60   action = new QtxAction( tr( "TTP_NEW" ),
61                           resMgr->loadPixmap( "PyViewer", tr( "ICON_NEW" ) ),
62                           tr( "ACT_NEW" ), 0, this );
63   action->setStatusTip( tr( "DSC_NEW" ) );
64   action->setShortcut( QKeySequence::New );
65   connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onNew() ) );
66   toolMgr()->registerAction( action, NewId );
67   
68   // . Open
69   action = new QtxAction( tr( "TTP_OPEN" ),
70                           resMgr->loadPixmap( "PyViewer", tr( "ICON_OPEN" ) ),
71                           tr( "ACT_OPEN" ), 0, this );
72   action->setStatusTip( tr( "DSC_OPEN" ) );
73   action->setShortcut( QKeySequence::Open );
74   connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onOpen() ) );
75   toolMgr()->registerAction( action, OpenId );
76   
77   // . Save
78   action = new QtxAction( tr( "TTP_SAVE" ),
79                           resMgr->loadPixmap( "PyViewer", tr( "ICON_SAVE" ) ),
80                           tr( "ACT_SAVE" ), 0, this );
81   action->setStatusTip( tr( "DSC_SAVE" ) );
82   action->setShortcut( QKeySequence::Save );
83   connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onSave() ) );
84   action->setEnabled( false );
85   connect( myTextEditor->document(), SIGNAL( modificationChanged( bool ) ),
86            action, SLOT( setEnabled( bool ) ) );
87   toolMgr()->registerAction( action, SaveId );
88   
89   // . SaveAs
90   action = new QtxAction( tr( "TTP_SAVEAS" ),
91                           resMgr->loadPixmap( "PyViewer", tr( "ICON_SAVEAS" ) ),
92                           tr( "ACT_SAVEAS" ), 0, this );
93   action->setStatusTip( tr( "DSC_SAVEAS" ) );
94   action->setShortcut( QKeySequence::SaveAs );
95   connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onSaveAs() ) );
96   toolMgr()->registerAction( action, SaveAsId );
97
98   // . Undo
99   action = new QtxAction( tr( "TTP_UNDO" ),
100                           resMgr->loadPixmap( "PyViewer", tr( "ICON_UNDO" ) ),
101                           tr( "ACT_UNDO" ), 0, this );
102   action->setStatusTip( tr( "DSC_UNDO" ) );
103   action->setShortcut( QKeySequence::Undo );
104   connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( undo() ) );
105   action->setEnabled( false );
106   connect( myTextEditor->document(), SIGNAL( undoAvailable( bool ) ),
107            action, SLOT( setEnabled( bool ) ) );
108   toolMgr()->registerAction( action, UndoId );
109
110   // . Redo
111   action = new QtxAction( tr( "TTP_REDO" ), 
112                           resMgr->loadPixmap( "PyViewer", tr( "ICON_REDO" ) ),
113                           tr( "ACT_REDO" ), 0, this );
114   action->setStatusTip( tr( "DSC_REDO" ) );
115   action->setShortcut( QKeySequence::Redo );
116   connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( redo() ) );
117   action->setEnabled( false );
118   connect( myTextEditor->document(), SIGNAL( redoAvailable( bool ) ),
119            action, SLOT( setEnabled( bool ) ) );
120   toolMgr()->registerAction( action, RedoId );
121
122   // . Cut
123   action = new QtxAction( tr( "TTP_CUT" ),
124                           resMgr->loadPixmap( "PyViewer", tr( "ICON_CUT" ) ),
125                           tr( "ACT_CUT" ), 0, this );
126   action->setStatusTip( tr( "DSC_CUT" ) );
127   action->setShortcut( QKeySequence::Cut );
128   connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( cut() ) );
129   action->setEnabled( false );
130   connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
131            action, SLOT( setEnabled( bool ) ) );
132   toolMgr()->registerAction( action, CutId );
133
134   // . Copy
135   action = new QtxAction( tr( "TTP_COPY" ), 
136                           resMgr->loadPixmap( "PyViewer", tr( "ICON_COPY" ) ),
137                           tr( "ACT_COPY" ), 0, this );
138   action->setStatusTip( tr( "DSC_COPY" ) );
139   action->setShortcut( QKeySequence::Copy );
140   connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( copy() ) );
141   action->setEnabled( false );
142   connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
143            action, SLOT( setEnabled( bool ) ) );
144   toolMgr()->registerAction( action, CopyId );
145
146   // . Paste
147   action = new QtxAction( tr( "TTP_PASTE" ),
148                           resMgr->loadPixmap( "PyViewer", tr( "ICON_PASTE" ) ),
149                           tr( "ACT_PASTE" ), 0, this );
150   action->setStatusTip( tr( "DSC_PASTE" ) );
151   action->setShortcut( QKeySequence::Paste );
152   connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( paste() ) );
153   toolMgr()->registerAction( action, PasteId );
154
155   // . Delete
156   action = new QtxAction( tr( "TTP_DELETE" ),
157                           resMgr->loadPixmap( "PyViewer", tr( "ICON_DELETE" ) ),
158                           tr( "ACT_DELETE" ), 0, this );
159   action->setStatusTip( tr( "DSC_DELETE" ) );
160   action->setShortcut( QKeySequence::Delete );
161   connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( deleteSelected() ) );
162   action->setEnabled( false );
163   connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
164            action, SLOT( setEnabled( bool ) ) );
165   toolMgr()->registerAction( action, DeleteId );
166
167   // . SelectAll
168   action = new QtxAction( tr( "TTP_SELECT_ALL" ),
169                           resMgr->loadPixmap( "PyViewer", tr( "ICON_SELECT_ALL" ) ),
170                           tr( "ACT_SELECT_ALL" ), 0, this );
171   action->setStatusTip( tr( "DSC_SELECT_ALL" ) );
172   action->setShortcut( QKeySequence::SelectAll );
173   connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( selectAll() ) );
174   toolMgr()->registerAction( action, SelectAllId );
175
176   // . Preferences
177   action = new QtxAction( tr( "TTP_PREFERENCES" ),
178                           resMgr->loadPixmap( "PyViewer", tr( "ICON_PREFERENCES" ) ),
179                           tr( "ACT_PREFERENCES" ), 0, this );
180   action->setStatusTip( tr( "DSC_PREFERENCES" ) );
181   connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onPreferences() ) );
182   toolMgr()->registerAction( action, PreferencesId );
183
184   // . Help
185   action = new QtxAction( tr( "TTP_HELP" ),
186                           resMgr->loadPixmap( "PyViewer", tr( "ICON_HELP" ) ),
187                           tr( "ACT_HELP" ), 0, this );
188   action->setStatusTip( tr( "DSC_HELP" ) );
189   connect( action, SIGNAL( triggered() ), this, SLOT( onHelp() ) );
190   toolMgr()->registerAction( action, HelpId );
191
192   // Create toolbar.
193   int idTB = toolMgr()->createToolBar( tr("TOOLBAR_LABEL"), QString( "PythonEditor" ), false );
194   toolMgr()->append( NewId, idTB );
195   toolMgr()->append( OpenId, idTB );
196   toolMgr()->append( SaveId, idTB );
197   toolMgr()->append( SaveAsId, idTB );
198   toolMgr()->append( toolMgr()->separator(), idTB );
199   toolMgr()->append( UndoId, idTB );
200   toolMgr()->append( RedoId, idTB );
201   toolMgr()->append( toolMgr()->separator(), idTB );
202   toolMgr()->append( CutId, idTB );
203   toolMgr()->append( CopyId, idTB );
204   toolMgr()->append( PasteId, idTB );
205   toolMgr()->append( DeleteId, idTB );
206   toolMgr()->append( SelectAllId, idTB );
207   toolMgr()->append( toolMgr()->separator(), idTB );
208   toolMgr()->append( PreferencesId, idTB );
209   toolMgr()->append( toolMgr()->separator(), idTB );
210   toolMgr()->append( HelpId, idTB );
211
212   // Set current file.
213   setCurrentFile( QString() );
214 }
215
216 /*!
217   \brief Destructor.
218 */
219 PyViewer_ViewWindow::~PyViewer_ViewWindow()
220 {
221 }
222
223 /*!
224   \brief Manage window close request.
225   \param event close event
226 */
227 void PyViewer_ViewWindow::closeEvent( QCloseEvent* event )
228 {
229   if ( whetherSave() )
230     event->accept();
231   else
232     event->ignore();
233 }
234
235 /*!
236   SLOT: Create new document
237 */
238 void PyViewer_ViewWindow::onNew()
239 {
240   if ( whetherSave() )
241   {
242     myTextEditor->clear();
243     setCurrentFile( QString() );
244   }
245 }
246
247 /*!
248   SLOT: Open existing Python file
249 */
250 void PyViewer_ViewWindow::onOpen()
251 {
252   if ( whetherSave() )
253   {
254     QString filter = tr( "TIT_PY_FILES" );
255     filter += " (*.py)";
256     QString aFilePath = QFileDialog::getOpenFileName( this,
257                                                       tr( "TIT_DLG_OPEN" ),
258                                                       QDir::currentPath(),
259                                                       filter );
260
261     if ( !aFilePath.isEmpty() )
262       loadFile( aFilePath );
263   }
264 }
265
266 /*!
267   SLOT: Save current document
268 */
269 bool PyViewer_ViewWindow::onSave()
270 {
271   if ( myURL.isEmpty() )
272     return onSaveAs();
273   else
274     return saveFile( myURL );
275 }
276
277
278 /*!
279   SLOT: Save current document under a new name
280 */
281 bool PyViewer_ViewWindow::onSaveAs()
282 {
283   QString filter = tr( "TIT_PY_FILES" );
284   filter += " (*.py)";
285   QString url = myURL.isEmpty() ? defaultName() : myURL;
286   QString aFilePath = QFileDialog::getSaveFileName( this,
287                                                     tr( "TIT_DLG_SAVE" ),
288                                                     url,
289                                                     filter );
290
291   if ( !aFilePath.isEmpty() )
292     return saveFile( aFilePath );
293
294   return false;
295 }
296
297 /*!
298   SLOT: Open preferences dialog
299 */
300 void PyViewer_ViewWindow::onPreferences()
301 {
302   PyEditor_SettingsDlg dlg( myTextEditor, true, this );
303   connect( &dlg, SIGNAL( help() ), this, SLOT( onHelp() ) );
304   dlg.exec();
305 }
306
307 /*!
308   \brief Associate \a filePath with the current document
309   \param filePath document's file path
310 */
311 void PyViewer_ViewWindow::setCurrentFile( const QString& filePath )
312 {
313   myURL = filePath;
314   myTextEditor->document()->setModified( false );
315 }
316
317 /*!
318   \brief Check whether the file is modified.
319   If it has the modifications then ask the user to save it.
320   \return true if the document is saved.
321 */
322 bool PyViewer_ViewWindow::whetherSave()
323 {
324   if ( myTextEditor->document()->isModified() )
325   {
326     QMessageBox::StandardButton answer =  QMessageBox::warning( this,
327                                                                 tr( "NAME_PYEDITOR" ),
328                                                                 tr( "WRN_SAVE_FILE" ),
329                                                                 QMessageBox::Save |
330                                                                 QMessageBox::Discard |
331                                                                 QMessageBox::Cancel );
332     switch( answer )
333     {
334     case QMessageBox::Save:
335       return onSave();
336     case QMessageBox::Cancel:
337       return false;
338     default:
339       break;
340     }
341   }
342   return true;
343 }
344
345 /*!
346   \brief Open file.
347   \param filePath file path
348 */
349 void PyViewer_ViewWindow::loadFile( const QString& filePath )
350 {
351   QFile aFile( filePath );
352   if ( !aFile.open(QFile::ReadOnly | QFile::Text) )
353   {
354     QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
355                           tr( "WRN_READ_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
356     return;
357   }
358
359   QTextStream anInput( &aFile );
360   QApplication::setOverrideCursor( Qt::WaitCursor );
361   myTextEditor->setPlainText( anInput.readAll() );
362   QApplication::restoreOverrideCursor();
363
364   setCurrentFile( filePath );
365   aFile.close();
366
367 }
368
369 /*!
370   \brief Saves file.
371   \param theFilePath file path
372 */
373 bool PyViewer_ViewWindow::saveFile( const QString& filePath )
374 {
375   QFile aFile( filePath );
376   if ( !aFile.open( QFile::WriteOnly | QFile::Text ) )
377   {
378     QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
379                           tr( "WRN_WRITE_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
380     return false;
381   }
382
383   QTextStream anOutput( &aFile );
384   QApplication::setOverrideCursor( Qt::WaitCursor );
385   anOutput << myTextEditor->toPlainText();
386   QApplication::restoreOverrideCursor();
387
388   setCurrentFile( filePath );
389   aFile.close();
390
391   return true;
392 }
393
394 /*!
395   Slot, called when user clicks "Help" button in "Preferences" dialog box.
396 */
397 void PyViewer_ViewWindow::onHelp()
398 {
399   SUIT_Application* app = SUIT_Session::session()->activeApplication();
400   if ( app ) {
401     QString page = "python_viewer_page.html";
402     if ( qobject_cast<QWidget*>( sender() ) )
403       page += "#custom_python_preferences";
404     app->onHelpContextModule( "GUI", page );
405   }
406 }
407
408 /*!
409   Get default name for Python file 
410   \return default name
411 */
412 QString PyViewer_ViewWindow::defaultName() const
413 {
414   return tr( "NONAME" );
415 }