1 // Copyright (C) 2015-2024 OPEN CASCADE
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 // File : PyEditor_Window.cxx
20 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
23 #include "PyEditor_Window.h"
24 #include "PyEditor_Widget.h"
25 #include "PyEditor_Settings.h"
26 #include "PyEditor_SettingsDlg.h"
29 #include <QApplication>
30 #include <QCloseEvent>
31 #include <QFileDialog>
33 #include <QMessageBox>
35 #include <QTextStream>
39 \class PyEditor_Window
40 \brief Python view window.
45 \param parent parent widget
47 PyEditor_Window::PyEditor_Window( QWidget* parent ) :
50 Q_INIT_RESOURCE( PyEditor );
52 // Create central widget.
53 myEditor = new PyEditor_Widget( this );
54 setCentralWidget( myEditor );
60 action = new QAction( QIcon( ":/images/py_new.png" ),
61 tr( "ACT_NEW" ), this );
62 action->setToolTip( tr( "TTP_NEW" ) );
63 action->setStatusTip( tr( "DSC_NEW" ) );
64 action->setShortcut( QKeySequence::New );
65 connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onNew() ) );
66 myActions[ NewId ] = action;
69 action = new QAction( QIcon( ":/images/py_open.png" ),
70 tr( "ACT_OPEN" ), this );
71 action->setToolTip( tr( "TTP_OPEN" ) );
72 action->setStatusTip( tr( "DSC_OPEN" ) );
73 action->setShortcut( QKeySequence::Open );
74 connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onOpen() ) );
75 myActions[ OpenId ] = action;
78 action = new QAction( QIcon( ":/images/py_save.png" ),
79 tr( "ACT_SAVE" ), this );
80 action->setToolTip( tr( "TTP_SAVE" ) );
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( myEditor, SIGNAL( modificationChanged( bool ) ),
86 action, SLOT( setEnabled( bool ) ) );
87 myActions[ SaveId ] = action;
90 action = new QAction( QIcon( ":/images/py_save_as.png" ),
91 tr( "ACT_SAVEAS" ), this );
92 action->setToolTip( tr( "TTP_SAVEAS" ) );
93 action->setStatusTip( tr( "DSC_SAVEAS" ) );
94 action->setShortcut( QKeySequence::SaveAs );
95 connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onSaveAs() ) );
96 myActions[ SaveAsId ] = action;
99 action = new QAction( QIcon( ":/images/py_exit.png" ),
100 tr( "ACT_EXIT" ), this );
101 action->setToolTip( tr( "TTP_EXIT" ) );
102 action->setStatusTip( tr( "DSC_EXIT" ) );
103 action->setShortcut( QKeySequence::Quit );
104 connect( action, SIGNAL( triggered( bool ) ), this, SLOT( close() ) );
105 myActions[ ExitId ] = action;
108 action = new QAction( QIcon( ":/images/py_undo.png" ),
109 tr( "ACT_UNDO" ), this );
110 action->setToolTip( tr( "TTP_UNDO" ) );
111 action->setStatusTip( tr( "DSC_UNDO" ) );
112 action->setShortcut( QKeySequence::Undo );
113 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( undo() ) );
114 action->setEnabled( false );
115 connect( myEditor, SIGNAL( undoAvailable( bool ) ),
116 action, SLOT( setEnabled( bool ) ) );
117 myActions[ UndoId ] = action;
120 action = new QAction( QIcon( ":/images/py_redo.png" ),
121 tr( "ACT_REDO" ), this );
122 action->setToolTip( tr( "TTP_REDO" ) );
123 action->setStatusTip( tr( "DSC_REDO" ) );
124 action->setShortcut( QKeySequence::Redo );
125 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( redo() ) );
126 action->setEnabled( false );
127 connect( myEditor, SIGNAL( redoAvailable( bool ) ),
128 action, SLOT( setEnabled( bool ) ) );
129 myActions[ RedoId ] = action;
132 action = new QAction( QIcon( ":/images/py_cut.png" ),
133 tr( "ACT_CUT" ), this );
134 action->setToolTip( tr( "TTP_CUT" ) );
135 action->setStatusTip( tr( "DSC_CUT" ) );
136 action->setShortcut( QKeySequence::Cut );
137 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( cut() ) );
138 action->setEnabled( false );
139 connect( myEditor, SIGNAL( copyAvailable( bool ) ),
140 action, SLOT( setEnabled( bool ) ) );
141 myActions[ CutId ] = action;
144 action = new QAction( QIcon( ":/images/py_copy.png" ),
145 tr( "ACT_COPY" ), this );
146 action->setToolTip( tr( "TTP_COPY" ) );
147 action->setStatusTip( tr( "DSC_COPY" ) );
148 action->setShortcut( QKeySequence::Copy );
149 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
150 action->setEnabled( false );
151 connect( myEditor, SIGNAL( copyAvailable( bool ) ),
152 action, SLOT( setEnabled( bool ) ) );
153 myActions[ CopyId ] = action;
156 action = new QAction( QIcon( ":/images/py_paste.png" ),
157 tr( "ACT_PASTE" ), this );
158 action->setToolTip( tr( "TTP_PASTE" ) );
159 action->setStatusTip( tr( "DSC_PASTE" ) );
160 action->setShortcut( QKeySequence::Paste );
161 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
162 myActions[ PasteId ] = action;
165 action = new QAction( QIcon( ":/images/py_delete.png" ),
166 tr( "ACT_DELETE" ), this );
167 action->setToolTip( tr( "TTP_DELETE" ) );
168 action->setStatusTip( tr( "DSC_DELETE" ) );
169 action->setShortcut( QKeySequence::Delete );
170 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( deleteSelected() ) );
171 action->setEnabled( false );
172 connect( myEditor, SIGNAL( copyAvailable( bool ) ),
173 action, SLOT( setEnabled( bool ) ) );
174 myActions[ DeleteId ] = action;
177 action = new QAction( QIcon( ":/images/py_select_all.png" ),
178 tr( "ACT_SELECT_ALL" ), this );
179 action->setToolTip( tr( "TTP_SELECT_ALL" ) );
180 action->setStatusTip( tr( "DSC_SELECT_ALL" ) );
181 action->setShortcut( QKeySequence::SelectAll );
182 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
183 myActions[ SelectAllId ] = action;
186 action = new QAction( QIcon( ":/images/py_find.png" ),
187 tr( "ACT_FIND" ), this );
188 action->setToolTip( tr( "TTP_FIND" ) );
189 action->setStatusTip( tr( "DSC_FIND" ) );
190 action->setShortcut( QKeySequence::Find );
191 action->setShortcutContext( Qt::WidgetShortcut );
192 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( find() ) );
193 myActions[ FindId ] = action;
196 action = new QAction( QIcon( ":/images/py_replace.png" ),
197 tr( "ACT_REPLACE" ), this );
198 action->setToolTip( tr( "TTP_REPLACE" ) );
199 action->setStatusTip( tr( "DSC_REPLACE" ) );
200 action->setShortcuts( QList<QKeySequence>() << QKeySequence( "Ctrl+H" ) << QKeySequence( QKeySequence::Replace ) );
201 action->setShortcutContext( Qt::WidgetShortcut );
202 connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( replace() ) );
203 myActions[ ReplaceId ] = action;
206 action = new QAction( QIcon( ":/images/py_preferences.png" ),
207 tr( "ACT_PREFERENCES" ), this );
208 action->setToolTip( tr( "TTP_PREFERENCES" ) );
209 action->setStatusTip( tr( "DSC_PREFERENCES" ) );
210 connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onPreferences() ) );
211 myActions[ PreferencesId ] = action;
214 action = new QAction( QIcon( ":/images/py_help.png" ),
215 tr( "ACT_HELP" ), this );
216 action->setToolTip( tr( "TTP_HELP" ) );
217 action->setStatusTip( tr( "DSC_HELP" ) );
218 connect( action, SIGNAL( triggered() ), this, SLOT( onHelp() ) );
219 myActions[ HelpId ] = action;
222 QMenu* menu = menuBar()->addMenu( tr( "MNU_FILE" ) );
223 menu->addAction( myActions[ NewId ] );
224 menu->addAction( myActions[ OpenId ] );
225 menu->addSeparator();
226 menu->addAction( myActions[ SaveId ] );
227 menu->addAction( myActions[ SaveAsId ] );
228 menu->addSeparator();
229 menu->addAction( myActions[ ExitId ] );
231 menu = menuBar()->addMenu( tr( "MNU_EDIT" ) );
232 menu->addAction( myActions[ UndoId ] );
233 menu->addAction( myActions[ RedoId ] );
234 menu->addSeparator();
235 menu->addAction( myActions[ CutId ] );
236 menu->addAction( myActions[ CopyId ] );
237 menu->addAction( myActions[ PasteId ] );
238 menu->addAction( myActions[ DeleteId ] );
239 menu->addSeparator();
240 menu->addAction( myActions[ SelectAllId ] );
241 menu->addSeparator();
242 menu->addAction( myActions[ FindId ] );
243 menu->addAction( myActions[ ReplaceId ] );
244 menu->addSeparator();
245 menu->addAction( myActions[ PreferencesId ] );
247 menu = menuBar()->addMenu( tr( "MNU_HELP" ) );
248 menu->addAction( myActions[ HelpId ] );
251 QToolBar* toolbar = addToolBar( tr( "TOOLBAR_LABEL" ) );
252 toolbar->setObjectName("PythonEditor");
253 toolbar->addAction( myActions[ NewId ] );
254 toolbar->addAction( myActions[ OpenId ] );
255 toolbar->addAction( myActions[ SaveId ] );
256 toolbar->addAction( myActions[ SaveAsId ] );
257 toolbar->addSeparator();
258 toolbar->addAction( myActions[ ExitId ] );
259 toolbar->addSeparator();
260 toolbar->addAction( myActions[ UndoId ] );
261 toolbar->addAction( myActions[ RedoId ] );
262 toolbar->addSeparator();
263 toolbar->addAction( myActions[ CutId ] );
264 toolbar->addAction( myActions[ CopyId ] );
265 toolbar->addAction( myActions[ PasteId ] );
266 toolbar->addAction( myActions[ DeleteId ] );
267 toolbar->addAction( myActions[ SelectAllId ] );
268 toolbar->addSeparator();
269 toolbar->addAction( myActions[ FindId ] );
270 toolbar->addAction( myActions[ ReplaceId ] );
271 toolbar->addSeparator();
272 toolbar->addAction( myActions[ PreferencesId ] );
273 toolbar->addSeparator();
274 toolbar->addAction( myActions[ HelpId ] );
277 setCurrentFile( QString() );
279 // Additional set-up for main window.
280 connect( myEditor, SIGNAL( modificationChanged( bool ) ),
281 this, SLOT( setWindowModified( bool ) ) );
283 // Initialize status bar.
284 statusBar()->showMessage( tr( "STS_READY" ) );
290 PyEditor_Window::~PyEditor_Window()
295 \brief Manage window close request.
296 \param event close event
298 void PyEditor_Window::closeEvent( QCloseEvent* event )
307 SLOT: Create new document
309 void PyEditor_Window::onNew()
314 setCurrentFile( QString() );
319 SLOT: Open existing Python file
321 void PyEditor_Window::onOpen()
325 QString filter = tr( "TIT_PY_FILES" );
327 QString aFilePath = QFileDialog::getOpenFileName( this,
328 tr( "TIT_DLG_OPEN" ),
332 if ( !aFilePath.isEmpty() )
333 loadFile( aFilePath );
338 SLOT: Save current document
340 bool PyEditor_Window::onSave()
342 if ( myURL.isEmpty() )
345 return saveFile( myURL );
350 SLOT: Save current document under a new name
352 bool PyEditor_Window::onSaveAs()
354 QString filter = tr( "TIT_PY_FILES" );
356 QString url = myURL.isEmpty() ? defaultName() : myURL;
357 QString aFilePath = QFileDialog::getSaveFileName( this,
358 tr( "TIT_DLG_SAVE" ),
362 if ( !aFilePath.isEmpty() )
363 return saveFile( aFilePath );
369 SLOT: Open preferences dialog
371 void PyEditor_Window::onPreferences()
373 PyEditor_SettingsDlg dlg( myEditor->editor(), true, this );
374 connect( &dlg, SIGNAL( help() ), this, SLOT( onHelp() ) );
379 \brief Associate \a filePath with the current document
380 \param filePath document's file path
382 void PyEditor_Window::setCurrentFile( const QString& filePath )
385 myEditor->setModified( false );
387 setWindowModified( false );
389 setWindowFilePath( myURL.isEmpty() ? defaultName() : myURL );
393 \brief Check whether the file is modified.
394 If it has the modifications then ask the user to save it.
395 \return true if the document is saved.
397 bool PyEditor_Window::whetherSave()
399 if ( myEditor->isModified() )
401 QMessageBox::StandardButton answer = QMessageBox::warning( this,
402 tr( "NAME_PYEDITOR" ),
403 tr( "WRN_SAVE_FILE" ),
405 QMessageBox::Discard |
406 QMessageBox::Cancel );
409 case QMessageBox::Save:
411 case QMessageBox::Cancel:
422 \param filePath file path
424 void PyEditor_Window::loadFile( const QString& filePath, bool verbose )
426 QFile aFile( filePath );
427 if ( !aFile.open(QFile::ReadOnly | QFile::Text) )
430 QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
431 tr( "WRN_READ_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
435 QTextStream anInput( &aFile );
436 QApplication::setOverrideCursor( Qt::WaitCursor );
437 myEditor->setText( anInput.readAll() );
438 QApplication::restoreOverrideCursor();
440 setCurrentFile( filePath );
443 statusBar()->showMessage( tr( "STS_F_LOADED" ), 2000 );
448 \param filePath file path
450 bool PyEditor_Window::saveFile( const QString& filePath, bool verbose )
452 QFile aFile( filePath );
453 if ( !aFile.open( QFile::WriteOnly | QFile::Text ) )
456 QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
457 tr( "WRN_WRITE_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
461 QTextStream anOutput( &aFile );
462 QApplication::setOverrideCursor( Qt::WaitCursor );
463 anOutput << myEditor->text();
464 QApplication::restoreOverrideCursor();
466 setCurrentFile( filePath );
469 statusBar()->showMessage( tr( "STS_F_SAVED" ), 2000 );
477 \return Editor widget.
479 PyEditor_Widget* PyEditor_Window::editor()
485 Slot, called when user clicks "Help" button in "Preferences" dialog box.
487 void PyEditor_Window::onHelp()
489 QWidget* w = qobject_cast<QWidget*>( sender() );
491 QFile file(":/about.txt");
492 file.open(QFile::ReadOnly | QFile::Text);
493 QTextStream stream( &file );
494 QString about = stream.readAll();
496 QMessageBox::about( w, tr( "NAME_PYEDITOR" ), about );
500 Get default name for Python file
503 QString PyEditor_Window::defaultName() const
505 return tr( "NONAME" );