OPTION(SALOME_USE_OCCVIEWER "Enable OCC visualization (Mandatory in classic configurations)" ON)
OPTION(SALOME_USE_GLVIEWER "Enable OpenGL visualization (Mandatory in classic configurations)" ON)
OPTION(SALOME_USE_GRAPHICSVIEW "Enable GraphicsView visualization (Mandatory in classic configurations)" ON)
+OPTION(SALOME_USE_PYVIEWER "Enable Python viewer (Mandatory in classic configurations)" ON)
OPTION(SALOME_USE_PLOT2DVIEWER "Enable Plot2D visualization (Mandatory in classic configurations)" ON)
OPTION(SALOME_USE_PYCONSOLE "Enable Python GUI interface (Mandatory in classic configurations)" ON)
OPTION(SALOME_USE_QXGRAPHVIEWER "Enable QX graph visualization (Mandatory in classic configurations)" ON)
MARK_AS_ADVANCED(SALOME_LIGHT_ONLY SALOME_USE_VTKVIEWER SALOME_USE_GRAPHICSVIEW SALOME_USE_PVVIEWER)
MARK_AS_ADVANCED(SALOME_USE_SALOMEOBJECT SALOME_USE_OCCVIEWER SALOME_USE_GLVIEWER SALOME_USE_PLOT2DVIEWER)
-MARK_AS_ADVANCED(SALOME_USE_PYCONSOLE SALOME_USE_QXGRAPHVIEWER)
+MARK_AS_ADVANCED(SALOME_USE_PYCONSOLE SALOME_USE_QXGRAPHVIEWER SALOME_USE_PYVIEWER)
MARK_AS_ADVANCED(SALOME_USE_SINGLE_DESKTOP)
# Prerequisites
ADD_DEFINITIONS("-DDISABLE_GRAPHICSVIEW")
ENDIF()
+IF (NOT SALOME_USE_PYVIEWER)
+ ADD_DEFINITIONS("-DDISABLE_PYVIEWER")
+ENDIF()
+
IF(SALOME_USE_PYCONSOLE)
# Build with obsolete Python module's methods
ADD_DEFINITIONS(-DCALL_OLD_METHODS)
GraphicsView)
ENDIF(SALOME_USE_GRAPHICSVIEW)
+# PyEditor/Viewer specific targets:
+IF(SALOME_USE_PYVIEWER)
+ LIST(APPEND _${PROJECT_NAME}_exposed_targets
+ PyEditor PyViewer)
+ENDIF(SALOME_USE_PYVIEWER)
+
# ParaView viewer specific targets:
IF(SALOME_USE_PVVIEWER)
LIST(APPEND _${PROJECT_NAME}_exposed_targets PVViewer)
SET(SALOME_USE_GRAPHICSVIEW @SALOME_USE_GRAPHICSVIEW@)
SET(SALOME_USE_QXGRAPHVIEWER @SALOME_USE_QXGRAPHVIEWER@)
SET(SALOME_USE_PVVIEWER @SALOME_USE_PVVIEWER@)
+SET(SALOME_USE_PYVIEWER @SALOME_USE_PYVIEWER@)
SET(SALOME_USE_PYCONSOLE @SALOME_USE_PYCONSOLE@)
SET(SALOME_USE_SALOMEOBJECT @SALOME_USE_SALOMEOBJECT@)
SET(SALOME_USE_SINGLE_DESKTOP @SALOME_USE_SINGLE_DESKTOP@)
IF (NOT SALOME_USE_PVVIEWER)
LIST(APPEND GUI_DEFINITIONS "-DDISABLE_PVVIEWER")
ENDIF()
+IF(NOT SALOME_USE_PYVIEWER)
+ LIST(APPEND GUI_DEFINITIONS "-DDISABLE_PYVIEWER")
+ENDIF()
IF(NOT SALOME_USE_PYCONSOLE)
LIST(APPEND GUI_DEFINITIONS "-DDISABLE_PYCONSOLE")
ENDIF()
SET(GUI_Plot2d Plot2d)
SET(GUI_PyConsole PyConsole)
SET(GUI_PyInterp PyInterp)
+SET(GUI_PyEditor PyEditor)
+SET(GUI_PyViewer PyViewer)
SET(GUI_QDS QDS)
SET(GUI_qtx qtx)
SET(GUI_QxScene QxScene)
SET(SUBDIRS_PVVIEWER PVViewer)
ENDIF()
+##
+# Python Viewer
+##
+IF(SALOME_USE_PYVIEWER)
+ SET(SUBDIRS_PYVIEWER PyEditor PyViewer)
+ENDIF(SALOME_USE_PYVIEWER)
+
##
# Python-based packages
${SUBDIRS_QXGRAPHVIEWER}
${SUBDIRS_PVVIEWER}
${SUBDIRS_GRAPHICSVIEW}
+ ${SUBDIRS_PYVIEWER}
${SUBDIRS_PYCONSOLE}
${SUBDIRS_LIGHT}
${SUBDIRS_CORBA}
IF(SALOME_USE_GRAPHICSVIEW)
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/GraphicsView)
ENDIF()
+IF(SALOME_USE_PYVIEWER)
+ INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/PyEditor)
+ INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/PyViewer)
+ENDIF()
IF(SALOME_USE_OCCVIEWER)
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/OCCViewer)
IF(SALOME_USE_SALOMEOBJECT)
IF(SALOME_USE_GRAPHICSVIEW)
LIST(APPEND _link_LIBRARIES GraphicsView)
ENDIF()
+IF(SALOME_USE_PYVIEWER)
+ LIST(APPEND _link_LIBRARIES PyEditor PyViewer)
+ENDIF()
IF(SALOME_USE_OCCVIEWER)
LIST(APPEND _link_LIBRARIES OCCViewer)
IF(SALOME_USE_SALOMEOBJECT)
#include "PVViewer_ViewModel.h"
#endif
+#ifndef DISABLE_PYVIEWER
+ #include <PyViewer_ViewManager.h>
+ #include <PyViewer_ViewModel.h>
+ #include <PyViewer_ViewWindow.h>
+#endif
+
#define VISIBILITY_COLUMN_WIDTH 25
#ifndef DISABLE_PVVIEWER
createActionForViewer( NewPVViewId, newWinMenu, QString::number( 6 ), Qt::ALT+Qt::Key_A );
#endif
+#ifndef DISABLE_PYVIEWER
+ createActionForViewer( NewPyViewerId, newWinMenu, QString::number( 7 ), Qt::ALT+Qt::Key_Y );
+#endif
createAction( RenameId, tr( "TOT_RENAME" ), QIcon(), tr( "MEN_DESK_RENAME" ), tr( "PRP_RENAME" ),
Qt::ALT+Qt::SHIFT+Qt::Key_R, desk, false, this, SLOT( onRenameWindow() ) );
case NewPVViewId:
type = PVViewer_Viewer::Type();
break;
+#endif
+#ifndef DISABLE_PYVIEWER
+ case NewPyViewerId:
+ type = PyViewer_Viewer::Type();
+ break;
#endif
}
if( a )
a->setEnabled( activeStudy() );
#endif
+
+#ifndef DISABLE_PYVIEWER
+ a = action( NewPyViewerId );
+ if( a )
+ a->setEnabled( activeStudy() );
+#endif
}
/*!
}
}
#endif
+#ifndef DISABLE_PYVIEWER
+ if( vmType == PyViewer_Viewer::Type() )
+ {
+ viewMgr = new PyViewer_ViewManager( activeStudy(), desktop() );
+ }
+#endif
#ifndef DISABLE_OCCVIEWER
if( vmType == OCCViewer_Viewer::Type() )
{
// .. "Plot2d viewer" group <<end>>
+ // .. "PyViewer" preferences tab <<start>>
+ int pyeditTab = pref->addPreference( tr( "PREF_TAB_PYEDITOR" ), salomeCat );
+ // ... "Font settings" group <<start>>
+ int pyFontGroup = pref->addPreference( tr( "PREF_GROUP_PY_FONT" ), pyeditTab );
+ pref->addPreference( tr( "PREF_PY_FONT" ), pyFontGroup,
+ LightApp_Preferences::Font, "PyEditor", "Font" );
+ // ... "Font settings" group <<end>>
+ // ... "Display settings" group <<start>>
+ int pyDispGroup = pref->addPreference( tr( "PREF_GROUP_PY_DISPLAY" ), pyeditTab );
+ pref->setItemProperty( "columns", 2, pyDispGroup );
+ // ... -> current line highlight
+ pref->addPreference( tr( "PREF_PY_CURRLINE_HIGHLIGHT" ), pyDispGroup,
+ LightApp_Preferences::Bool, "PyEditor", "HighlightCurrentLine" );
+ // ... -> text wrapping
+ pref->addPreference( tr( "PREF_PY_TEXT_WRAP" ), pyDispGroup,
+ LightApp_Preferences::Bool, "PyEditor", "TextWrapping" );
+ // ... -> center cursor on scroll
+ pref->addPreference( tr( "PREF_PY_CURSON_ON_SCROLL" ), pyDispGroup,
+ LightApp_Preferences::Bool, "PyEditor", "CenterCursorOnScroll" );
+ // ... -> line numbers area
+ pref->addPreference( tr( "PREF_PY_LINE_NUMBS_AREA" ), pyDispGroup,
+ LightApp_Preferences::Bool, "PyEditor", "LineNumberArea" );
+ // ... "Display settings" group <<end>>
+ // ... "Tab settings" group <<start>>
+ int pyTabGroup = pref->addPreference( tr( "PREF_GROUP_PY_TAB" ), pyeditTab );
+ pref->setItemProperty( "columns", 2, pyTabGroup );
+ // ... -> tab whitespaces
+ pref->addPreference( tr( "PREF_PY_TAB_WHITESPACES" ), pyTabGroup,
+ LightApp_Preferences::Bool, "PyEditor", "TabSpaceVisible" );
+ // ... -> tab size
+ pref->addPreference( tr( "PREF_PY_TAB_SIZE" ), pyTabGroup,
+ LightApp_Preferences::IntSpin, "PyEditor", "TabSize" );
+ // ... "Tab settings" group <<end>>
+ // ... "Vertical edge settings" group <<start>>
+ int pyVertEdgeGroup = pref->addPreference( tr( "PREF_GROUP_VERT_EDGE" ), pyeditTab );
+ pref->setItemProperty( "columns", 2, pyVertEdgeGroup );
+ // ... -> vertical edge
+ pref->addPreference( tr( "PREF_PY_VERT_EDGE" ), pyVertEdgeGroup,
+ LightApp_Preferences::Bool, "PyEditor", "VerticalEdge" );
+ // ... -> number of columns
+ pref->addPreference( tr( "PREF_PY_NUM_COLUMNS" ), pyVertEdgeGroup,
+ LightApp_Preferences::IntSpin, "PyEditor", "NumberColumns" );
+ // ... "Vertical edge settings" group <<end>>
+ // .. "PyEditor" preferences tab <<end>>
+
// .. "Directories" preferences tab <<start>>
int dirTab = pref->addPreference( tr( "PREF_TAB_DIRECTORIES" ), salomeCat );
// ... --> quick directories list
}
}
#endif
+
+#ifndef DISABLE_PYVIEWER
+ if ( sec == QString( "PyViewer" ) && ( param == QString( "HighlightCurrentLine" ) ||
+ param == QString( "LineNumberArea" ) ||
+ param == QString( "TextWrapping" ) ||
+ param == QString( "CenterCursorOnScroll" ) ||
+ param == QString( "TabSpaceVisible" ) ||
+ param == QString( "TabSize" ) ||
+ param == QString( "VerticalEdge" ) ||
+ param == QString( "NumberColumns" ) ||
+ param == QString( "Font" ) ) )
+ {
+ QList<SUIT_ViewManager*> lst;
+ viewManagers( PyViewer_Viewer::Type(), lst );
+ QListIterator<SUIT_ViewManager*> itPy( lst );
+ while ( itPy.hasNext() )
+ {
+ SUIT_ViewManager* viewMgr = itPy.next();
+ SUIT_ViewModel* vm = viewMgr->getViewModel();
+ if ( !vm || !vm->inherits( "PyViewer_Viewer" ) )
+ continue;
+
+ PyViewer_Viewer* pyEditVM = dynamic_cast<PyViewer_Viewer*>( vm );
+
+ viewMgr->setViewModel( vm );
+ PyViewer_ViewWindow* pyView = dynamic_cast<PyViewer_ViewWindow*>( viewMgr->getActiveView() );
+ if( pyView )
+ {
+ pyView->setPreferences();
+ }
+ }
+ }
+#endif
}
/*!
#ifndef DISABLE_PVVIEWER
aTypesList<<PVViewer_Viewer::Type();
#endif
+#ifndef DISABLE_PYVIEWER
+ aTypesList<<PyViewer_Viewer::Type();
+#endif
#ifndef DISABLE_OCCVIEWER
aTypesList<<OCCViewer_Viewer::Type();
#endif
CloseId, CloseAllId, GroupAllId,
PreferencesId, MRUId, ModulesListId,
NewGLViewId, NewPlot2dId, NewOCCViewId, NewVTKViewId,
- NewQxSceneViewId, NewGraphicsViewId, NewPVViewId, StyleId, FullScreenId,
+ NewQxSceneViewId, NewGraphicsViewId, NewPVViewId, NewPyViewerId, StyleId, FullScreenId,
UserID };
protected:
</section>
<section name="resources">
<!-- Resource directories (resource manager)-->
- <parameter name="Qtx" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="Style" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="SUIT" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="STD" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="ViewerTools" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="Plot2d" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="SPlot2d" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="GLViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="GraphicsView" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="OCCViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="VTKViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="PVViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="Qtx" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="Style" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="SUIT" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="STD" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="ViewerTools" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="Plot2d" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="SPlot2d" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="GLViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="GraphicsView" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="OCCViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="VTKViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="PVViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
<parameter name="QxSceneViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="PyConsole" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="SalomeApp" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="OB" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="CAM" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="LightApp" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="SVTK" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="ToolsGUI" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
- <parameter name="LogWindow" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="PyEditor" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="PyViewer" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="PyConsole" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="SalomeApp" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="OB" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="CAM" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="LightApp" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="SVTK" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="ToolsGUI" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
+ <parameter name="LogWindow" value="${GUI_ROOT_DIR}/share/salome/resources/gui"/>
</section>
<section name="desktop" >
<!-- Default GUI desktop state, position, size -->
<parameter name="Title" value="63, 213, 255" />
<parameter name="NodeBody" value="255, 249, 147" />
</section>
+ <section name="PyEditor" >
+ <!-- PyEditor viewer preferences -->
+ <parameter name="Font" value="Courier, 10" />
+ <parameter name="HighlightCurrentLine" value="true" />
+ <parameter name="TextWrapping" value="false" />
+ <parameter name="CenterCursorOnScroll" value="true" />
+ <parameter name="LineNumberArea" value="true" />
+ <parameter name="TabSpaceVisible" value="true" />
+ <parameter name="TabSize" value="4" />
+ <parameter name="VerticalEdge" value="true" />
+ <parameter name="NumberColumns" value="90" />
+ </section>
<section name="GUI" >
<parameter name="documentation" value="gui_help"/>
</section>
<message>
<source>NEW_WINDOW_6</source>
<translation>ParaVie&w view</translation>
+ </message>
+ <message>
+ <source>NEW_WINDOW_7</source>
+ <translation>P&ython view</translation>
</message>
<message>
<source>CREATING_NEW_WINDOW</source>
<source>PREF_GROUP_SHORTCUTS</source>
<translation>Shortcuts settings</translation>
</message>
+ <message>
+ <source>PREF_TAB_PYEDITOR</source>
+ <translation>Python Viewer</translation>
+ </message>
+ <message>
+ <source>PREF_GROUP_PY_FONT</source>
+ <translation>Font settings</translation>
+ </message>
+ <message>
+ <source>PREF_PY_FONT</source>
+ <translation>Font</translation>
+ </message>
+ <message>
+ <source>PREF_GROUP_PY_DISPLAY</source>
+ <translation>Display settings</translation>
+ </message>
+ <message>
+ <source>PREF_PY_CURRLINE_HIGHLIGHT</source>
+ <translation>Enable current line highlight</translation>
+ </message>
+ <message>
+ <source>PREF_PY_TEXT_WRAP</source>
+ <translation>Enable text wrapping</translation>
+ </message>
+ <message>
+ <source>PREF_PY_CURSON_ON_SCROLL</source>
+ <translation>Center cursor on scroll</translation>
+ </message>
+ <message>
+ <source>PREF_PY_LINE_NUMBS_AREA</source>
+ <translation>Display line numbers area</translation>
+ </message>
+ <message>
+ <source>PREF_GROUP_PY_TAB</source>
+ <translation>Tab settings</translation>
+ </message>
+ <message>
+ <source>PREF_PY_TAB_WHITESPACES</source>
+ <translation>Display tab whitespaces</translation>
+ </message>
+ <message>
+ <source>PREF_PY_TAB_SIZE</source>
+ <translation>Tab size:</translation>
+ </message>
+ <message>
+ <source>PREF_GROUP_VERT_EDGE</source>
+ <translation>Vertical edge settings</translation>
+ </message>
+ <message>
+ <source>PREF_PY_VERT_EDGE</source>
+ <translation>Display vertical edge</translation>
+ </message>
+ <message>
+ <source>PREF_PY_NUM_COLUMNS</source>
+ <translation>Number of columns:</translation>
+ </message>
</context>
<context>
<name>LightApp_Module</name>
--- /dev/null
+# Copyright (C) 2015 OPEN CASCADE
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+# Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+#
+
+INCLUDE(UseQt4Ext)
+
+# --- options ---
+
+# additional include directories
+INCLUDE_DIRECTORIES(
+ ${QT_INCLUDES}
+ ${PROJECT_SOURCE_DIR}/src/Qtx
+)
+
+# additional preprocessor / compiler flags
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+
+# libraries to link to
+SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES} qtx)
+
+# --- headers ---
+
+# header files / to be processed by moc
+SET(_moc_HEADERS
+ PyEditor_Editor.h
+ PyEditor_LineNumberArea.h
+ PyEditor_PyHighlighter.h
+ PyEditor_SettingsDlg.h
+)
+
+# header files / no moc processing
+SET(_other_HEADERS
+ PyEditor.h
+ PyEditor_Settings.h
+)
+
+# header files / to install
+SET(PyEditor_HEADERS ${_moc_HEADERS} ${_other_HEADERS})
+
+# --- resources ---
+
+# resource files / to be processed by lrelease
+SET(RESOURCES_PATH resources)
+
+SET(_ts_RESOURCES
+ ${RESOURCES_PATH}/translations/PyEditor_msg_en.ts
+ ${RESOURCES_PATH}/translations/PyEditor_msg_fr.ts
+ ${RESOURCES_PATH}/translations/PyEditor_msg_ja.ts
+)
+
+# sources / moc wrappings
+QT4_WRAP_CPP(_moc_SOURCES ${_moc_HEADERS})
+
+# sources / static
+SET(_other_SOURCES
+ PyEditor_Editor.cxx
+ PyEditor_LineNumberArea.cxx
+ PyEditor_PyHighlighter.cxx
+ PyEditor_Settings.cxx
+ PyEditor_SettingsDlg.cxx
+)
+
+# sources / to compile
+SET(PyEditor_SOURCES ${_other_SOURCES} ${_moc_SOURCES})
+
+# --- rules ---
+ADD_LIBRARY(PyEditor ${PyEditor_SOURCES})
+TARGET_LINK_LIBRARIES(PyEditor ${_link_LIBRARIES})
+INSTALL(TARGETS PyEditor EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS})
+
+INSTALL(FILES ${PyEditor_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS})
+QT4_INSTALL_TS_RESOURCES("${_ts_RESOURCES}" "${SALOME_GUI_INSTALL_RES_DATA}")
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifdef WIN32
+
+#if defined PYEDITOR_EXPORTS || defined PyEditor_EXPORTS
+#define PYEDITOR_EXPORT __declspec(dllexport)
+#else
+#define PYEDITOR_EXPORT __declspec(dllimport)
+#endif
+
+#else
+#define PYEDITOR_EXPORT
+#endif // WIN32
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_Editor.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+//Local includes
+#include "PyEditor_Editor.h"
+#include "PyEditor_LineNumberArea.h"
+#include "PyEditor_PyHighlighter.h"
+#include "PyEditor_Settings.h"
+
+//Qtx includes
+#include <QtxResourceMgr.h>
+
+//Qt includes
+#include <QPainter>
+#include <QTextBlock>
+
+/*!
+ \class PyEditor_Editor
+ \brief Viewer/Editor is used to edit and show advanced plain text.
+*/
+
+/*!
+ \brief Constructor.
+ \param isSingle flag determined single application or reccesed.
+ \param theParent parent widget
+*/
+PyEditor_Editor::PyEditor_Editor( bool isSingle, QtxResourceMgr* theMgr, QWidget* theParent )
+ : QPlainTextEdit( theParent )
+{
+ my_Settings = new PyEditor_Settings( theMgr, isSingle );
+
+ // Create line number area
+ my_LineNumberArea = new PyEditor_LineNumberArea( this );
+ my_LineNumberArea->setMouseTracking( true );
+
+ my_SyntaxHighlighter = new PyEditor_PyHighlighter( this->document() );
+
+ // Signals and slots
+ connect( this, SIGNAL( blockCountChanged( int ) ), this, SLOT( updateLineNumberAreaWidth( int ) ) );
+ connect( this, SIGNAL( updateRequest( QRect, int ) ), this, SLOT( updateLineNumberArea( QRect, int ) ) );
+ connect( this, SIGNAL( cursorPositionChanged() ), this, SLOT( updateHighlightCurrentLine() ) );
+ connect( this, SIGNAL( cursorPositionChanged() ), this, SLOT( matchParentheses() ) );
+
+ updateStatement();
+}
+
+/*!
+ \brief Destructor.
+*/
+PyEditor_Editor::~PyEditor_Editor()
+{
+}
+
+/*!
+ Updates editor.
+ */
+void PyEditor_Editor::updateStatement()
+{
+ //Set font size
+ QFont aFont = font();
+ aFont.setFamily( settings()->p_Font.family() );
+ aFont.setPointSize( settings()->p_Font.pointSize() );
+ setFont( aFont );
+
+ // Set line wrap mode
+ setLineWrapMode( settings()->p_TextWrapping ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap );
+
+ // Center the cursor on screen
+ setCenterOnScroll( settings()->p_CenterCursorOnScroll );
+
+ // Set size white spaces
+ setTabStopWidth( settings()->p_TabSize * 10 );
+
+ // Update current line highlight
+ updateHighlightCurrentLine();
+
+ // Update line numbers area
+ updateLineNumberAreaWidth( 0 );
+
+ my_SyntaxHighlighter->rehighlight();
+ viewport()->update();
+}
+
+/*!
+ SLOT: Delete the current selection's contents
+ */
+void PyEditor_Editor::deleteSelected()
+{
+ QTextCursor aCursor = textCursor();
+ if ( aCursor.hasSelection() )
+ aCursor.removeSelectedText();
+ setTextCursor( aCursor );
+}
+
+/*!
+ \brief Reimplemented calss is to receive key press events for the plain text widget.
+ \param theEvent event
+ */
+void PyEditor_Editor::keyPressEvent( QKeyEvent* theEvent )
+{
+ if ( theEvent->type() == QEvent::KeyPress )
+ {
+ int aKey = theEvent->key();
+ Qt::KeyboardModifiers aCtrl = theEvent->modifiers() & Qt::ControlModifier;
+ Qt::KeyboardModifiers aShift = theEvent->modifiers() & Qt::ShiftModifier;
+
+ if ( aKey == Qt::Key_Tab || ( aKey == Qt::Key_Backtab || ( aKey == Qt::Key_Tab && aShift ) ) )
+ {
+ QTextCursor aCursor = textCursor();
+ aCursor.beginEditBlock();
+ tabIndentation( aKey == Qt::Key_Backtab );
+ aCursor.endEditBlock();
+ theEvent->accept();
+ }
+ else if ( aKey == Qt::Key_Enter || aKey == Qt::Key_Return )
+ {
+ QTextCursor aCursor = textCursor();
+ aCursor.beginEditBlock();
+ if ( lineIndent() == 0 )
+ {
+ QPlainTextEdit::keyPressEvent( theEvent );
+ }
+ aCursor.endEditBlock();
+ theEvent->accept();
+ }
+ else if ( theEvent == QKeySequence::MoveToStartOfLine || theEvent == QKeySequence::SelectStartOfLine )
+ {
+ QTextCursor aCursor = this->textCursor();
+ if ( QTextLayout* aLayout = aCursor.block().layout() )
+ {
+ if ( aLayout->lineForTextPosition( aCursor.position() - aCursor.block().position() ).lineNumber() == 0 )
+ {
+ handleHome( theEvent == QKeySequence::SelectStartOfLine );
+ }
+ }
+ }
+ else if ( ( aKey == Qt::Key_Colon || ( aKey == Qt::Key_Space && !aCtrl && !aShift ) ) &&
+ !textCursor().hasSelection() )
+ {
+ QTextCursor aCursor = textCursor();
+ aCursor.movePosition( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+
+ QString aSelectedText = aCursor.selectedText();
+ int numSpaces = findFirstNonSpace( aSelectedText );
+ int amountChars = aSelectedText.size() - findFirstNonSpace( aSelectedText );
+ QString aLeadingText = aSelectedText.right( amountChars );
+
+ QStringList aReservedWords;
+ aReservedWords.append( "except" );
+ if ( aKey == Qt::Key_Colon )
+ {
+ aReservedWords.append( "else" );
+ aReservedWords.append( "finally" );
+ }
+ else if ( aKey == Qt::Key_Space )
+ {
+ aReservedWords.append( "elif" );
+ }
+
+ if ( aReservedWords.contains( aLeadingText ) )
+ {
+ QString aPreviousText = aCursor.block().previous().text();
+ int numSpacesPrevious = findFirstNonSpace( aPreviousText );
+ if ( numSpaces == numSpacesPrevious )
+ {
+ tabIndentation( true );
+ aCursor.movePosition( QTextCursor::EndOfBlock );
+ setTextCursor( aCursor );
+ }
+ }
+ QPlainTextEdit::keyPressEvent( theEvent );
+ }
+ else
+ {
+ QPlainTextEdit::keyPressEvent( theEvent );
+ }
+ }
+}
+
+/*!
+ \brief Reimplemented calss is to receive plain text widget resize events
+ which are passed in the event parameter.
+ \param theEvent event
+ */
+void PyEditor_Editor::resizeEvent( QResizeEvent* theEvent )
+{
+ QPlainTextEdit::resizeEvent( theEvent );
+
+ // Change size geometry of line number area
+ QRect aContentsRect = contentsRect();
+ my_LineNumberArea->setGeometry(
+ QRect( aContentsRect.left(),
+ aContentsRect.top(),
+ lineNumberAreaWidth(),
+ aContentsRect.height() ) );
+}
+
+/*!
+ \brief Reimplemented calss is to receive paint events passed in theEvent.
+ \param theEvent event
+ */
+void PyEditor_Editor::paintEvent( QPaintEvent* theEvent )
+{
+ QPlainTextEdit::paintEvent( theEvent );
+
+ QTextBlock aBlock( firstVisibleBlock() );
+ QPointF anOffset( contentOffset() );
+ QPainter aPainter( this->viewport() );
+
+ int aTabSpaces = this->tabStopWidth() / 10;
+
+ // Visualization tab spaces
+ if ( settings()->p_TabSpaceVisible )
+ {
+ qreal aTop = blockBoundingGeometry( aBlock ).translated( anOffset ).top();
+ while ( aBlock.isValid() && aTop <= theEvent->rect().bottom() )
+ {
+ if ( aBlock.isVisible() && blockBoundingGeometry( aBlock ).translated( anOffset ).toRect().intersects( theEvent->rect() ) )
+ {
+ QString aText = aBlock.text();
+ if ( aText.contains( QRegExp( "\\w+" ) ) )
+ aText.remove( QRegExp( "(?!\\w+)\\s+$" ) );
+
+ int aColumn = 0;
+ int anIndex = 0;
+ while ( anIndex != -1 )
+ {
+ anIndex = aText.indexOf( QRegExp( QString( "^\\s{%1}" ).arg( aTabSpaces ) ), 0 );
+ if ( anIndex != -1 )
+ {
+ aColumn = aColumn + aTabSpaces;
+ aText = aText.mid( aTabSpaces );
+
+ if ( aText.startsWith( ' ' ) )
+ {
+ QTextCursor aCursor( aBlock );
+ aCursor.setPosition( aBlock.position() + aColumn );
+
+ QRect aRect = cursorRect( aCursor );
+ aPainter.setPen( QPen( Qt::darkGray, 1, Qt::DotLine ) );
+ aPainter.drawLine( aRect.x() + 1, aRect.top(), aRect.x() + 1, aRect.bottom() );
+ }
+ }
+ }
+ }
+ aBlock = aBlock.next();
+ }
+ }
+
+ // Vertical edge line
+ if ( settings()->p_VerticalEdge )
+ {
+ const QRect aRect = theEvent->rect();
+ const QFont aFont = currentCharFormat().font();
+ int aNumberColumn = QFontMetrics( aFont ).averageCharWidth() * settings()->p_NumberColumns + anOffset.x() + document()->documentMargin();
+ aPainter.setPen( QPen( Qt::lightGray, 1, Qt::SolidLine ) );
+ aPainter.drawLine( aNumberColumn, aRect.top(), aNumberColumn, aRect.bottom() );
+ }
+}
+
+/*!
+ \return manager of setting values.
+ */
+PyEditor_Settings* PyEditor_Editor::settings()
+{
+ return my_Settings;
+}
+
+/*!
+ \brief Indenting and tabbing of the text
+ \param isShift flag defines reverse tab
+ */
+void PyEditor_Editor::tabIndentation( bool isShift )
+{
+ QTextCursor aCursor = textCursor();
+ int aTabSpaces = this->tabStopWidth()/10;
+
+ if ( !aCursor.hasSelection() )
+ {
+ if ( !isShift )
+ {
+ int N = aCursor.columnNumber() % aTabSpaces;
+ aCursor.insertText( QString( aTabSpaces - N, QLatin1Char( ' ' ) ) );
+ }
+ else
+ {
+ QTextBlock aCurrentBlock = document()->findBlock( aCursor.position() );
+ int anIndentPos = findFirstNonSpace( aCurrentBlock.text() );
+ aCursor.setPosition( aCurrentBlock.position() + anIndentPos );
+ setTextCursor( aCursor );
+
+ //if ( aCurrCursorColumnPos <= anIndentPos )
+ //{
+ int aColumnPos = aCursor.columnNumber();
+ if ( aColumnPos != 0 )
+ {
+ int N = aCursor.columnNumber() % aTabSpaces;
+ if ( N == 0 ) N = aTabSpaces;
+ aCursor.movePosition( QTextCursor::Left, QTextCursor::KeepAnchor, N );
+ aCursor.removeSelectedText();
+ }
+ setTextCursor( aCursor );
+ //}
+ }
+ }
+ else
+ {
+ indentSelection( isShift );
+ }
+}
+
+/*!
+ \brief Indenting and tabbing of the selected text
+ \param isShift flag defines reverse tab
+ */
+void PyEditor_Editor::indentSelection( bool isShift )
+{
+ QTextCursor aCursor = this->textCursor();
+
+ int aCursorStart = aCursor.selectionStart();
+ int aCursorEnd = aCursor.selectionEnd();
+
+ QTextBlock aStartBlock = document()->findBlock( aCursorStart );
+ QTextBlock anEndBlock = document()->findBlock( aCursorEnd - 1 ).next();
+
+ int aTabSpaces = this->tabStopWidth()/10;
+
+ for ( QTextBlock aBlock = aStartBlock; aBlock.isValid() && aBlock != anEndBlock; aBlock = aBlock.next() )
+ {
+ QString aText = aBlock.text();
+ int anIndentPos = findFirstNonSpace( aText );
+ int N = ( anIndentPos % aTabSpaces );
+
+ aCursor.setPosition( aBlock.position() + anIndentPos );
+ if ( !isShift )
+ {
+ aCursor.insertText( QString( aTabSpaces - N, QLatin1Char( ' ' ) ) );
+ setTextCursor( aCursor );
+ }
+ else
+ {
+ int aColumnPos = aCursor.columnNumber();
+ if ( aColumnPos != 0 )
+ {
+ int blockN = aColumnPos % aTabSpaces;
+ if ( blockN == 0 ) blockN = aTabSpaces;
+ aCursor.movePosition( QTextCursor::Left, QTextCursor::KeepAnchor, blockN );
+ aCursor.removeSelectedText();
+ setTextCursor( aCursor );
+ }
+ }
+ }
+
+ // Reselect the selected lines
+ aCursor.setPosition( aStartBlock.position() );
+ aCursor.setPosition( anEndBlock.previous().position(), QTextCursor::KeepAnchor );
+ aCursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
+ setTextCursor( aCursor );
+}
+
+/*!
+ \brief Finds the first non-white sapce in theText.
+ \param theText string
+ \return index of the first non-white space
+ */
+int PyEditor_Editor::findFirstNonSpace( const QString& theText )
+{
+ int i = 0;
+ while ( i < theText.size() )
+ {
+ if ( !theText.at(i).isSpace() )
+ return i;
+ ++i;
+ }
+ return i;
+}
+
+/*!
+ \brief Auto line indenting
+ \return error code
+ */
+int PyEditor_Editor::lineIndent()
+{
+ int aTabSpaces = this->tabStopWidth() / 10;
+
+ QTextCursor aCursor = textCursor();
+ aCursor.insertBlock();
+ setTextCursor( aCursor );
+
+ QTextBlock aCurrentBlock = aCursor.block();
+ if ( aCurrentBlock == document()->begin() )
+ return 0;
+
+ QTextBlock aPreviousBlock = aCurrentBlock.previous();
+
+ QString aPreviousText;
+ forever
+ {
+ if ( aPreviousBlock == document()->begin() )
+ {
+ aPreviousText = aPreviousBlock.text();
+ if ( aPreviousText.isEmpty() && aPreviousText.trimmed().isEmpty() )
+ return -1;
+ break;
+ }
+
+ // If the text of this block is not empty then break the loop.
+ aPreviousText = aPreviousBlock.text();
+ if ( !aPreviousText.isEmpty() && !aPreviousText.trimmed().isEmpty() )
+ break;
+
+ aPreviousBlock = aPreviousBlock.previous();
+ }
+
+ int aTabIndentation = 0;
+ int anAmountIndentation = -1;
+ int i = 0;
+ while ( i < aPreviousText.size() )
+ {
+ if ( !aPreviousText.at(i).isSpace() )
+ {
+ anAmountIndentation = findFirstNonSpace( aPreviousText );
+ break;
+ }
+ else
+ {
+ ++aTabIndentation;
+ }
+ ++i;
+ }
+
+ if ( anAmountIndentation == -1 )
+ {
+ if ( aTabIndentation > 0 )
+ anAmountIndentation = aTabIndentation;
+ else
+ return 0;
+ }
+
+ const QString aPreviousTrimmed = aPreviousText.trimmed();
+ if ( aPreviousTrimmed.endsWith( ":" ) )
+ {
+ anAmountIndentation += aTabSpaces;
+ }
+ else
+ {
+ if ( aPreviousTrimmed == "continue"
+ || aPreviousTrimmed == "break"
+ || aPreviousTrimmed == "pass"
+ || aPreviousTrimmed == "return"
+ || aPreviousTrimmed == "raise"
+ || aPreviousTrimmed.startsWith( "raise " )
+ || aPreviousTrimmed.startsWith( "return " ) )
+ anAmountIndentation -= aTabSpaces;
+ }
+
+ aCursor.insertText( QString( anAmountIndentation, QLatin1Char(' ') ) );
+ setTextCursor( aCursor );
+
+ return 1;
+}
+
+/*!
+ \brief Set text cursor on home position.
+ */
+void PyEditor_Editor::handleHome( bool isExtendLine )
+{
+ QTextCursor aCursor = textCursor();
+ QTextCursor::MoveMode aMode = QTextCursor::MoveAnchor;
+
+ if ( isExtendLine )
+ aMode = QTextCursor::KeepAnchor;
+
+ int anInitPos = aCursor.position();
+ int aBlockPos = aCursor.block().position();
+
+ QChar aCharacter = document()->characterAt( aBlockPos );
+ while ( aCharacter.category() == QChar::Separator_Space )
+ {
+ ++aBlockPos;
+ if ( aBlockPos == anInitPos )
+ break;
+ aCharacter = document()->characterAt( aBlockPos );
+ }
+
+ if ( aBlockPos == anInitPos )
+ aBlockPos = aCursor.block().position();
+
+ aCursor.setPosition( aBlockPos, aMode );
+ setTextCursor( aCursor );
+}
+
+/*!
+ SLOT: Updates the highlight current line.
+ */
+void PyEditor_Editor::updateHighlightCurrentLine()
+{
+ QList<QTextEdit::ExtraSelection> anExtraSelections;
+ if ( !isReadOnly() && settings()->p_HighlightCurrentLine )
+ {
+ QTextEdit::ExtraSelection selection;
+
+ QColor lineColor = QColor( Qt::gray ).lighter( 155 );
+
+ selection.format.setBackground( lineColor );
+ selection.format.setProperty( QTextFormat::FullWidthSelection, QVariant( true ) );
+ selection.cursor = textCursor();
+ selection.cursor.clearSelection();
+ anExtraSelections.append( selection );
+ }
+ setExtraSelections( anExtraSelections );
+}
+
+/*!
+ \brief Creates line number area.
+ \param theEvent event for paint events.
+ */
+void PyEditor_Editor::lineNumberAreaPaintEvent( QPaintEvent* theEvent )
+{
+ QPainter aPainter( my_LineNumberArea );
+ aPainter.fillRect( theEvent->rect(), QColor( Qt::lightGray ).lighter( 125 ) );
+
+ QTextBlock aBlock = firstVisibleBlock();
+ int aBlockNumber = aBlock.blockNumber();
+ int aTop = (int)blockBoundingGeometry( aBlock ).translated( contentOffset() ).top();
+ int aBottom = aTop + (int)blockBoundingRect( aBlock ).height();
+ int aCurrentLine = document()->findBlock( textCursor().position() ).blockNumber();
+
+ QFont aFont = aPainter.font();
+ aPainter.setPen( this->palette().color( QPalette::Text ) );
+
+ while ( aBlock.isValid() && aTop <= theEvent->rect().bottom() )
+ {
+ if ( aBlock.isVisible() && aBottom >= theEvent->rect().top() )
+ {
+ if ( aBlockNumber == aCurrentLine )
+ {
+ aPainter.setPen( Qt::darkGray );
+ aFont.setBold( true );
+ aPainter.setFont( aFont );
+ }
+ else
+ {
+ aPainter.setPen( Qt::gray ) ;
+ aFont.setBold( false );
+ aPainter.setFont( aFont );
+ }
+ QString aNumber = QString::number( aBlockNumber + 1 );
+ aPainter.drawText( 0, aTop, my_LineNumberArea->width(), fontMetrics().height(), Qt::AlignRight, aNumber );
+ }
+
+ aBlock = aBlock.next();
+ aTop = aBottom;
+ aBottom = aTop + (int)blockBoundingRect( aBlock ).height();
+ ++aBlockNumber;
+ }
+}
+
+/*!
+ \return width of line number area
+ */
+int PyEditor_Editor::lineNumberAreaWidth()
+{
+ int aSpace = 0;
+
+ int aDigits = 1;
+ int aMaximum = qMax( 1, blockCount() );
+ while ( aMaximum >= 10 )
+ {
+ aMaximum /= 10;
+ ++aDigits;
+ }
+
+ if ( settings()->p_LineNumberArea )
+ aSpace += 5 + fontMetrics().width( QLatin1Char( '9' ) ) * aDigits;
+
+ return aSpace;
+}
+
+/*!
+ SLOT: Updates the width of line number area.
+ */
+void PyEditor_Editor::updateLineNumberAreaWidth( int /*theNewBlockCount*/ )
+{
+ setViewportMargins( lineNumberAreaWidth(), 0, 0, 0 );
+}
+
+/*!
+ SLOT: When the editor viewport has been scrolled.
+ */
+void PyEditor_Editor::updateLineNumberArea( const QRect& theRect, int theDY )
+{
+ if ( theDY )
+ my_LineNumberArea->scroll( 0, theDY );
+ else
+ my_LineNumberArea->update( 0, theRect.y(), my_LineNumberArea->width(), theRect.height() );
+
+ if ( theRect.contains( viewport()->rect() ) )
+ updateLineNumberAreaWidth( 0 );
+}
+
+/*!
+ \brief Parenthesis management.
+ SLOT: Walk through and check that we don't exceed 80 chars per line.
+ */
+void PyEditor_Editor::matchParentheses()
+{
+ PyEditor_PyHighlighter::TextBlockData* data =
+ static_cast<PyEditor_PyHighlighter::TextBlockData*>( textCursor().block().userData() );
+
+ if ( data )
+ {
+ QVector<PyEditor_PyHighlighter::ParenthesisInfo*> infoEntries = data->parentheses();
+
+ int aPos = textCursor().block().position();
+ bool ignore = false;
+ for ( int i = 0; i < infoEntries.size(); ++i )
+ {
+ PyEditor_PyHighlighter::ParenthesisInfo* info = infoEntries.at(i);
+
+ int currentColumnPosition = textCursor().columnNumber();
+ if ( info->position == currentColumnPosition - 1 && isLeftBrackets( info->character ) )
+ {
+ if ( matchLeftParenthesis( textCursor().block(), i + 1, 0 ) )
+ createParenthesisSelection( aPos + info->position );
+ }
+ else if ( info->position == currentColumnPosition && isLeftBrackets( info->character ) )
+ {
+ if ( !ignore )
+ {
+ if ( matchLeftParenthesis( textCursor().block(), i + 1, 0 ) )
+ createParenthesisSelection( aPos + info->position );
+ }
+ }
+ else if ( info->position == currentColumnPosition - 1 && isRightBrackets( info->character ) )
+ {
+ if ( matchRightParenthesis( textCursor().block(), i - 1, 0 ) )
+ createParenthesisSelection( aPos + info->position );
+ ignore = true;
+ }
+ else if ( info->position == currentColumnPosition && isRightBrackets( info->character ) )
+ {
+ if ( matchRightParenthesis( textCursor().block(), i - 1, 0 ) )
+ createParenthesisSelection( aPos + info->position );
+ }
+ }
+ }
+}
+
+/*!
+ \brief Matches the left brackets.
+ \param theCurrentBlock text block
+ \param theI index
+ \param theNumLeftParentheses number of left parentheses
+ \return \c true if the left match
+ */
+bool PyEditor_Editor::matchLeftParenthesis(
+ const QTextBlock& theCurrentBlock, int theI, int theNumLeftParentheses )
+{
+ PyEditor_PyHighlighter::TextBlockData* data =
+ static_cast<PyEditor_PyHighlighter::TextBlockData*>( theCurrentBlock.userData() );
+ QVector<PyEditor_PyHighlighter::ParenthesisInfo*> infos = data->parentheses();
+
+ int docPos = theCurrentBlock.position();
+ for ( ; theI < infos.size(); ++theI )
+ {
+ PyEditor_PyHighlighter::ParenthesisInfo* info = infos.at(theI);
+
+ if ( isLeftBrackets( info->character ) )
+ {
+ ++theNumLeftParentheses;
+ continue;
+ }
+
+ if ( isRightBrackets( info->character ) && theNumLeftParentheses == 0 )
+ {
+ createParenthesisSelection( docPos + info->position );
+ return true;
+ }
+ else
+ --theNumLeftParentheses;
+ }
+
+ QTextBlock nextBlock = theCurrentBlock.next();
+ if ( nextBlock.isValid() )
+ return matchLeftParenthesis( nextBlock, 0, theNumLeftParentheses );
+
+ return false;
+}
+
+/*!
+ \brief Matches the right brackets.
+ \param theCurrentBlock text block
+ \param theI index
+ \param theNumRightParentheses number of right parentheses
+ \return \c true if the right match
+ */
+bool PyEditor_Editor::matchRightParenthesis( const QTextBlock& theCurrentBlock, int theI, int theNumRightParentheses )
+{
+ PyEditor_PyHighlighter::TextBlockData* data = static_cast<PyEditor_PyHighlighter::TextBlockData*>( theCurrentBlock.userData() );
+ QVector<PyEditor_PyHighlighter::ParenthesisInfo*> parentheses = data->parentheses();
+
+ int docPos = theCurrentBlock.position();
+ for ( ; theI > -1 && parentheses.size() > 0; --theI )
+ {
+ PyEditor_PyHighlighter::ParenthesisInfo* info = parentheses.at(theI);
+ if ( isRightBrackets( info->character ) )
+ {
+ ++theNumRightParentheses;
+ continue;
+ }
+ if ( isLeftBrackets( info->character ) && theNumRightParentheses == 0 )
+ {
+ createParenthesisSelection( docPos + info->position );
+ return true;
+ }
+ else
+ --theNumRightParentheses;
+ }
+
+ QTextBlock prevBlock = theCurrentBlock.previous();
+ if ( prevBlock.isValid() )
+ {
+ PyEditor_PyHighlighter::TextBlockData* data = static_cast<PyEditor_PyHighlighter::TextBlockData*>( prevBlock.userData() );
+ QVector<PyEditor_PyHighlighter::ParenthesisInfo*> parentheses = data->parentheses();
+ return matchRightParenthesis( prevBlock, parentheses.size() - 1, theNumRightParentheses );
+ }
+
+ return false;
+}
+
+/*!
+ \brief Creates brackets selection.
+ \param thePosition position
+ */
+// Create brackets
+void PyEditor_Editor::createParenthesisSelection( int thePosition )
+{
+ QList<QTextEdit::ExtraSelection> selections = extraSelections();
+
+ QTextEdit::ExtraSelection selection;
+
+ QTextCharFormat format = selection.format;
+ format.setForeground( Qt::red );
+ format.setBackground( Qt::white );
+ selection.format = format;
+
+ QTextCursor cursor = textCursor();
+ cursor.setPosition( thePosition );
+ cursor.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor );
+ selection.cursor = cursor;
+
+ selections.append( selection );
+ setExtraSelections( selections );
+}
+
+/*!
+ return true whether the left bracket
+ */
+bool PyEditor_Editor::isLeftBrackets( QChar theSymbol )
+{
+ return theSymbol == '(' || theSymbol == '{' || theSymbol == '[';
+}
+
+/*!
+ Appends a new paragraph with text to the end of the text edit.
+ */
+void PyEditor_Editor::append( const QString & text ) {
+ QPlainTextEdit::appendPlainText(text);
+}
+
+/*!
+ Sets the text edit's text.
+*/
+void PyEditor_Editor::setText( const QString & text ) {
+ QPlainTextEdit::appendPlainText(text);
+}
+
+/*!
+ return true whether the right bracket
+ */
+bool PyEditor_Editor::isRightBrackets( QChar theSymbol )
+{
+ return theSymbol == ')' || theSymbol == '}' || theSymbol == ']';
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_Editor.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifndef PYEDITOR_EDITOR_H
+#define PYEDITOR_EDITOR_H
+
+#include <QPlainTextEdit>
+
+class PyEditor_PyHighlighter;
+class PyEditor_Settings;
+class QtxResourceMgr;
+
+class PyEditor_Editor : public QPlainTextEdit
+{
+ Q_OBJECT
+
+public:
+ PyEditor_Editor( bool isSingle = false, QtxResourceMgr* = 0, QWidget* = 0 );
+ virtual ~PyEditor_Editor();
+
+ void lineNumberAreaPaintEvent( QPaintEvent* );
+ int lineNumberAreaWidth();
+
+ void updateStatement();
+ PyEditor_Settings* settings();
+
+public Q_SLOTS:
+ void deleteSelected();
+ void append ( const QString & );
+ void setText ( const QString & text );
+protected:
+ virtual void keyPressEvent( QKeyEvent* );
+ virtual void resizeEvent( QResizeEvent* );
+ virtual void paintEvent( QPaintEvent* );
+
+private Q_SLOTS:
+ void updateHighlightCurrentLine();
+ void matchParentheses();
+
+ void updateLineNumberAreaWidth( int );
+ void updateLineNumberArea( const QRect&, int );
+
+private:
+ bool matchLeftParenthesis( const QTextBlock&, int, int );
+ bool matchRightParenthesis( const QTextBlock&, int, int );
+ void createParenthesisSelection( int );
+ bool isLeftBrackets( QChar );
+ bool isRightBrackets( QChar );
+
+ void handleHome( bool );
+ int lineIndent();
+ void tabIndentation( bool );
+ void indentSelection( bool );
+
+ int findFirstNonSpace( const QString& );
+
+ QWidget* my_LineNumberArea;
+ PyEditor_PyHighlighter* my_SyntaxHighlighter;
+ PyEditor_Settings* my_Settings;
+};
+
+#endif // PYEDITOR_EDITOR_H
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_LineNumberArea.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#include "PyEditor_LineNumberArea.h"
+
+#include "PyEditor_Editor.h"
+
+/*!
+ \class PyEditor_LineNumberArea
+ \brief Widget shows line number.
+*/
+
+/*!
+ \brief Constructor.
+ \param theCodeEditor parent widget
+*/
+PyEditor_LineNumberArea::PyEditor_LineNumberArea( PyEditor_Editor* theCodeEditor ) :
+ QWidget( theCodeEditor )
+{
+ my_CodeEditor = theCodeEditor;
+}
+
+QSize PyEditor_LineNumberArea::sizeHint() const
+{
+ return QSize( my_CodeEditor->lineNumberAreaWidth(), 0 );
+}
+
+void PyEditor_LineNumberArea::paintEvent( QPaintEvent* theEvent )
+{
+ my_CodeEditor->lineNumberAreaPaintEvent( theEvent );
+ QWidget::paintEvent( theEvent );
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_LineNumberArea.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifndef PYEDITOR_LINENUMBERAREA_H
+#define PYEDITOR_LINENUMBERAREA_H
+
+#include <QWidget>
+
+class PyEditor_Editor;
+
+class PyEditor_LineNumberArea : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PyEditor_LineNumberArea( PyEditor_Editor* );
+
+ QSize sizeHint() const;
+
+protected:
+ void paintEvent( QPaintEvent* );
+
+private:
+ PyEditor_Editor* my_CodeEditor;
+};
+
+#endif // PYEDITOR_LINENUMBERAREA_H
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_PyHighlighter.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#include "PyEditor_PyHighlighter.h"
+
+#define NORMAL 0
+#define TRIPLESINGLE 1
+#define TRIPLEDOUBLE 2
+
+/*!
+ \class PyEditor_PyHighlighter
+ \brief Python highlighter class defines the syntax highlighting rules.
+*/
+
+PyEditor_PyHighlighter::TextBlockData::TextBlockData()
+{
+}
+
+QVector<PyEditor_PyHighlighter::ParenthesisInfo*> PyEditor_PyHighlighter::TextBlockData::parentheses()
+{
+ return my_Parentheses;
+}
+
+void PyEditor_PyHighlighter::TextBlockData::insert( PyEditor_PyHighlighter::ParenthesisInfo* theInfo )
+{
+ int i = 0;
+ while ( i < my_Parentheses.size() && theInfo->position > my_Parentheses.at(i)->position )
+ ++i;
+
+ my_Parentheses.insert( i, theInfo );
+}
+
+/*!
+ \brief Constructor.
+ \param theDocument container for structured rich text documents.
+*/
+PyEditor_PyHighlighter::PyEditor_PyHighlighter( QTextDocument* theDocument )
+ : QSyntaxHighlighter( theDocument )
+{
+ initialize();
+}
+
+/*!
+ \brief Initialization rules.
+ */
+void PyEditor_PyHighlighter::initialize()
+{
+ HighlightingRule aRule;
+
+ // Keywords
+ keywordFormat.setForeground( Qt::blue );
+ QStringList aKeywords = keywords();
+ foreach ( const QString &keyword, aKeywords )
+ {
+ aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) );
+ aRule.format = keywordFormat;
+ aRule.capture = 0;
+ highlightingRules.append( aRule );
+ }
+
+ // Special keywords
+ specialFromat.setForeground( Qt::magenta );
+ QStringList aSpecialKeywords = specialKeywords();
+ foreach ( const QString &keyword, aSpecialKeywords )
+ {
+ aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) );
+ aRule.format = specialFromat;
+ aRule.capture = 0;
+ highlightingRules.append( aRule );
+ }
+
+ // Reference to the current instance of the class
+ referenceClassFormat.setForeground( QColor( 179, 143, 0 ) );
+ referenceClassFormat.setFontItalic( true );
+ aRule.pattern = QRegExp( "\\bself\\b" );
+ aRule.format = referenceClassFormat;
+ aRule.capture = 0;
+ highlightingRules.append( aRule );
+
+ // Numbers
+ numberFormat.setForeground( Qt::darkMagenta );
+ aRule.pattern = QRegExp( "\\b([-+])?(\\d+(\\.)?\\d*|\\d*(\\.)?\\d+)(([eE]([-+])?)?\\d+)?\\b" );
+ aRule.format = numberFormat;
+ aRule.capture = 0;
+ highlightingRules.append( aRule );
+
+ // String qoutation
+ quotationFormat.setForeground( Qt::darkGreen );
+ aRule.pattern = QRegExp( "(?:'[^']*'|\"[^\"]*\")" );
+ aRule.pattern.setMinimal( true );
+ aRule.format = quotationFormat;
+ aRule.capture = 0;
+ highlightingRules.append( aRule );
+
+ // Function names
+ functionFormat.setFontWeight( QFont::Bold );
+ aRule.pattern = QRegExp( "(?:def\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" );
+ aRule.capture = 1;
+ aRule.format = functionFormat;
+ highlightingRules.append( aRule );
+
+ // Class names
+ classFormat.setForeground( Qt::darkBlue );
+ classFormat.setFontWeight( QFont::Bold );
+ aRule.pattern = QRegExp( "(?:class\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" );
+ aRule.capture = 1;
+ aRule.format = classFormat;
+ highlightingRules.append( aRule );
+
+ // Multi line comments
+ multiLineCommentFormat.setForeground( Qt::darkRed );
+ tripleQuotesExpression = QRegExp( "(:?\"[\"]\".*\"[\"]\"|'''.*''')" );
+ aRule.pattern = tripleQuotesExpression;
+ aRule.pattern.setMinimal( true );
+ aRule.format = multiLineCommentFormat;
+ aRule.capture = 0;
+ highlightingRules.append( aRule );
+
+ tripleSingleExpression = QRegExp( "'''(?!\")" );
+ tripleDoubleExpression = QRegExp( "\"\"\"(?!')" );
+
+ // Single comments
+ singleLineCommentFormat.setForeground( Qt::darkGray );
+ aRule.pattern = QRegExp( "#[^\n]*" );
+ aRule.format = singleLineCommentFormat;
+ aRule.capture = 0;
+ highlightingRules.append( aRule );
+}
+
+/*!
+ \return string list of Python keywords.
+ */
+QStringList PyEditor_PyHighlighter::keywords()
+{
+ QStringList aKeywords;
+ aKeywords << "and"
+ << "as"
+ << "assert"
+ << "break"
+ << "class"
+ << "continue"
+ << "def"
+ << "elif"
+ << "else"
+ << "except"
+ << "exec"
+ << "finally"
+ << "False"
+ << "for"
+ << "from"
+ << "global"
+ << "if"
+ << "import"
+ << "in"
+ << "is"
+ << "lambda"
+ << "None"
+ << "not"
+ << "or"
+ << "pass"
+ << "print"
+ << "raise"
+ << "return"
+ << "True"
+ << "try"
+ << "while"
+ << "with"
+ << "yield";
+ return aKeywords;
+}
+
+/*!
+ \return string list of special Python keywords.
+ */
+QStringList PyEditor_PyHighlighter::specialKeywords()
+{
+ QStringList aSpecialKeywords;
+ aSpecialKeywords << "ArithmeticError"
+ << "AssertionError"
+ << "AttributeError"
+ << "EnvironmentError"
+ << "EOFError"
+ << "Exception"
+ << "FloatingPointError"
+ << "ImportError"
+ << "IndentationError"
+ << "IndexError"
+ << "IOError"
+ << "KeyboardInterrupt"
+ << "KeyError"
+ << "LookupError"
+ << "MemoryError"
+ << "NameError"
+ << "NotImplementedError"
+ << "OSError"
+ << "OverflowError"
+ << "ReferenceError"
+ << "RuntimeError"
+ << "StandardError"
+ << "StopIteration"
+ << "SyntaxError"
+ << "SystemError"
+ << "SystemExit"
+ << "TabError"
+ << "TypeError"
+ << "UnboundLocalError"
+ << "UnicodeDecodeError"
+ << "UnicodeEncodeError"
+ << "UnicodeError"
+ << "UnicodeTranslateError"
+ << "ValueError"
+ << "WindowsError"
+ << "ZeroDivisionError"
+ << "Warning"
+ << "UserWarning"
+ << "DeprecationWarning"
+ << "PendingDeprecationWarning"
+ << "SyntaxWarning"
+ << "OverflowWarning"
+ << "RuntimeWarning"
+ << "FutureWarning";
+ return aSpecialKeywords;
+}
+
+void PyEditor_PyHighlighter::highlightBlock( const QString& theText )
+{
+ TextBlockData* aData = new TextBlockData;
+
+ insertBracketsData( RoundBrackets, aData, theText );
+ insertBracketsData( CurlyBrackets, aData, theText );
+ insertBracketsData( SquareBrackets, aData, theText );
+
+ setCurrentBlockUserData( aData );
+
+ foreach ( const HighlightingRule& rule, highlightingRules )
+ {
+ QRegExp expression( rule.pattern );
+ int anIndex = expression.indexIn( theText );
+ while ( anIndex >= 0 )
+ {
+ anIndex = expression.pos( rule.capture );
+ int aLength = expression.cap( rule.capture ).length();
+ setFormat( anIndex, aLength, rule.format );
+ anIndex = expression.indexIn( theText, anIndex + aLength );
+ }
+ }
+
+ setCurrentBlockState( NORMAL );
+
+ if ( theText.indexOf( tripleQuotesExpression ) != -1 )
+ return;
+
+ QList<int> aTripleSingle;
+ aTripleSingle << theText.indexOf( tripleSingleExpression ) << TRIPLESINGLE;
+
+ QList<int> aTripleDouble;
+ aTripleDouble << theText.indexOf( tripleDoubleExpression ) << TRIPLEDOUBLE;
+
+ QList< QList<int> > aTripleExpressions;
+ aTripleExpressions << aTripleSingle << aTripleDouble;
+
+ for ( int i = 0; i < aTripleExpressions.length(); i++ )
+ {
+ QList<int> aBlock = aTripleExpressions[i];
+ int anIndex = aBlock[0];
+ int aState = aBlock[1];
+ if ( previousBlockState() == aState )
+ {
+ if ( anIndex == -1 )
+ {
+ anIndex = theText.length();
+ setCurrentBlockState( aState );
+ }
+ setFormat( 0, anIndex + 3, multiLineCommentFormat );
+ }
+ else if ( anIndex > -1 )
+ {
+ setCurrentBlockState( aState );
+ setFormat( anIndex, theText.length(), multiLineCommentFormat );
+ }
+ }
+}
+
+void PyEditor_PyHighlighter::insertBracketsData( char theLeftSymbol,
+ char theRightSymbol,
+ TextBlockData* theData,
+ const QString& theText )
+{
+ int leftPosition = theText.indexOf( theLeftSymbol );
+ while( leftPosition != -1 )
+ {
+ ParenthesisInfo* info = new ParenthesisInfo();
+ info->character = theLeftSymbol;
+ info->position = leftPosition;
+
+ theData->insert( info );
+ leftPosition = theText.indexOf( theLeftSymbol, leftPosition + 1 );
+ }
+
+ int rightPosition = theText.indexOf( theRightSymbol );
+ while( rightPosition != -1 )
+ {
+ ParenthesisInfo* info = new ParenthesisInfo();
+ info->character = theRightSymbol;
+ info->position = rightPosition;
+
+ theData->insert( info );
+ rightPosition = theText.indexOf( theRightSymbol, rightPosition + 1 );
+ }
+}
+
+void PyEditor_PyHighlighter::insertBracketsData( Brackets theBrackets,
+ TextBlockData* theData,
+ const QString& theText )
+{
+ char leftChar = '0';
+ char rightChar = '0';
+
+ switch( theBrackets )
+ {
+ case RoundBrackets:
+ leftChar = '(';
+ rightChar = ')';
+ break;
+ case CurlyBrackets:
+ leftChar = '{';
+ rightChar = '}';
+ break;
+ case SquareBrackets:
+ leftChar = '[';
+ rightChar = ']';
+ break;
+ }
+
+ insertBracketsData( leftChar, rightChar, theData, theText );
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_PyHighlighter.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifndef PYEDITOR_PYHIGHLIGHTER_H
+#define PYEDITOR_PYHIGHLIGHTER_H
+
+#include <QSyntaxHighlighter>
+
+class QTextDocument;
+
+class PyEditor_PyHighlighter : public QSyntaxHighlighter
+{
+ Q_OBJECT
+
+public:
+
+ struct ParenthesisInfo
+ {
+ char character;
+ int position;
+ };
+
+ class TextBlockData : public QTextBlockUserData
+ {
+ public:
+ TextBlockData();
+
+ QVector<ParenthesisInfo*> parentheses();
+ void insert( ParenthesisInfo* );
+
+ private:
+ QVector<ParenthesisInfo*> my_Parentheses;
+ };
+
+public:
+ PyEditor_PyHighlighter( QTextDocument* = 0 );
+
+ void initialize();
+ QStringList keywords();
+ QStringList specialKeywords();
+
+protected:
+ struct HighlightingRule
+ {
+ QRegExp pattern;
+ QTextCharFormat format;
+ int capture;
+ };
+ QVector<HighlightingRule> highlightingRules;
+
+ enum Brackets { RoundBrackets, CurlyBrackets, SquareBrackets };
+
+ QRegExp tripleQuotesExpression;
+ QRegExp tripleSingleExpression;
+ QRegExp tripleDoubleExpression;
+
+ QTextCharFormat classFormat;
+ QTextCharFormat referenceClassFormat;
+ QTextCharFormat functionFormat;
+ QTextCharFormat keywordFormat;
+ QTextCharFormat specialFromat;
+ QTextCharFormat numberFormat;
+ QTextCharFormat singleLineCommentFormat;
+ QTextCharFormat multiLineCommentFormat;
+ QTextCharFormat quotationFormat;
+
+ void highlightBlock( const QString& );
+ void insertBracketsData( char, char, TextBlockData*, const QString& );
+ void insertBracketsData( Brackets, TextBlockData*, const QString& );
+};
+
+#endif // PYEDITOR_PYHIGHLIGHTER_H
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_Settings.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#include "PyEditor_Settings.h"
+
+#include <QtxResourceMgr.h>
+
+#include <QDir>
+#include <QFile>
+#include <QSettings>
+
+/*!
+ \class PyEditor_Settings
+ \brief Manager of setting values.
+*/
+
+/*!
+ \brief Constructor.
+ \param isSingle flag determined single application or reccesed.
+*/
+PyEditor_Settings::PyEditor_Settings( QtxResourceMgr* theMgr, bool isSingle )
+ : p_HighlightCurrentLine( true ),
+ p_LineNumberArea( true ),
+ p_TextWrapping( false ),
+ p_CenterCursorOnScroll( true ),
+ p_TabSpaceVisible( true ),
+ p_TabSize( 4 ),
+ p_VerticalEdge( true ),
+ p_NumberColumns( 80 ),
+ p_Font( "Courier", 10 ),
+ m_ResourceMgr(theMgr),
+ m_Single(isSingle)
+
+{
+ if ( m_Single )
+ {
+ m_Settings = new QSettings( "config.ini", QSettings::IniFormat );
+ if ( !QFile::exists( m_Settings->fileName() ) )
+ toSettings( PY_EDITOR );
+ }
+
+ readSettings();
+}
+
+/*!
+ \brief Reads the setting values.
+ */
+void PyEditor_Settings::readSettings()
+{
+ if ( isSingle() )
+ fromSettings( PY_EDITOR );
+ else
+ readPreferences();
+}
+
+/*!
+ \brief Writes the setting values.
+ */
+void PyEditor_Settings::writeSettings()
+{
+ if ( isSingle() )
+ toSettings( PY_EDITOR );
+ else
+ writePreferences();
+}
+
+/*!
+ \return \c true if the application is single
+ */
+bool PyEditor_Settings::isSingle()
+{
+ return m_Single;
+}
+
+/*!
+ \brief Loads the setting values from file settings.
+ */
+void PyEditor_Settings::fromSettings( const QString &theCategory )
+{
+ QString aGroup = theCategory;
+ aGroup += "/";
+
+ p_HighlightCurrentLine = m_Settings->value(aGroup + QLatin1String( HIGHLIGHT_CURRLINE ), p_HighlightCurrentLine).toBool();
+ p_LineNumberArea = m_Settings->value(aGroup + QLatin1String( LINE_NUMBER_AREA ), p_LineNumberArea).toBool();
+ p_TextWrapping = m_Settings->value(aGroup + QLatin1String( TEXT_WRAP ), p_TextWrapping).toBool();
+ p_CenterCursorOnScroll = m_Settings->value(aGroup + QLatin1String( CURSOR_SCROLL ), p_CenterCursorOnScroll).toBool();
+ p_TabSpaceVisible = m_Settings->value(aGroup + QLatin1String( TAB_WHITESPACES ), p_TabSpaceVisible).toBool();
+ p_TabSize = m_Settings->value(aGroup + QLatin1String( TAB_SIZE ), p_TabSize).toInt();
+ p_VerticalEdge = m_Settings->value(aGroup + QLatin1String( VERTICAL_EDGE ), p_VerticalEdge).toBool();
+ p_NumberColumns = m_Settings->value(aGroup + QLatin1String( NUM_COLUMNS ), p_NumberColumns).toInt();
+ p_Font = QFont( m_Settings->value(aGroup + QLatin1String( FONT_FAMILY ), p_Font.family()).toString(),
+ m_Settings->value(aGroup + QLatin1String( FONT_SIZE ), p_Font.pointSize()).toInt() );
+}
+
+/*!
+ \brief Saves the setting values into file settings.
+ */
+void PyEditor_Settings::toSettings( const QString &theCategory ) const
+{
+ m_Settings->beginGroup( theCategory );
+ m_Settings->setValue( QLatin1String( HIGHLIGHT_CURRLINE ), p_HighlightCurrentLine );
+ m_Settings->setValue( QLatin1String( LINE_NUMBER_AREA ), p_LineNumberArea );
+ m_Settings->setValue( QLatin1String( TEXT_WRAP ), p_TextWrapping );
+ m_Settings->setValue( QLatin1String( CURSOR_SCROLL ), p_CenterCursorOnScroll );
+ m_Settings->setValue( QLatin1String( TAB_WHITESPACES ), p_TabSpaceVisible );
+ m_Settings->setValue( QLatin1String( TAB_SIZE ), p_TabSize );
+ m_Settings->setValue( QLatin1String( VERTICAL_EDGE ), p_VerticalEdge );
+ m_Settings->setValue( QLatin1String( NUM_COLUMNS ), p_NumberColumns );
+ m_Settings->setValue( QLatin1String( FONT_FAMILY ), p_Font.family() );
+ m_Settings->setValue( QLatin1String( FONT_SIZE ), p_Font.pointSize() );
+ m_Settings->endGroup();
+}
+
+/*!
+ \brief Loads the setting values from setting resources.
+ */
+void PyEditor_Settings::readPreferences()
+{
+ if(m_ResourceMgr)
+ {
+ p_HighlightCurrentLine = m_ResourceMgr->booleanValue( "PyEditor", "HighlightCurrentLine", p_HighlightCurrentLine );
+ p_LineNumberArea = m_ResourceMgr->booleanValue( "PyEditor", "LineNumberArea", p_LineNumberArea );
+ p_TextWrapping = m_ResourceMgr->booleanValue( "PyEditor", "TextWrapping", p_TextWrapping );
+ p_CenterCursorOnScroll = m_ResourceMgr->booleanValue( "PyEditor", "CenterCursorOnScroll", p_CenterCursorOnScroll );
+ p_TabSpaceVisible = m_ResourceMgr->booleanValue( "PyEditor", "TabSpaceVisible", p_TabSpaceVisible );
+ p_TabSize = m_ResourceMgr->integerValue( "PyEditor", "TabSize", p_TabSize );
+ p_VerticalEdge = m_ResourceMgr->booleanValue( "PyEditor", "VerticalEdge", p_VerticalEdge );
+ p_NumberColumns = m_ResourceMgr->integerValue( "PyEditor", "NumberColumns", p_NumberColumns );
+ p_Font = m_ResourceMgr->fontValue( "PyEditor", "Font", p_Font );
+ }
+}
+
+/*!
+ \brief Saves the setting values into setting resources.
+ */
+void PyEditor_Settings::writePreferences()
+{
+ if(m_ResourceMgr)
+ {
+ m_ResourceMgr->setValue( "PyEditor", "HighlightCurrentLine", p_HighlightCurrentLine );
+ m_ResourceMgr->setValue( "PyEditor", "LineNumberArea", p_LineNumberArea );
+ m_ResourceMgr->setValue( "PyEditor", "TextWrapping", p_TextWrapping );
+ m_ResourceMgr->setValue( "PyEditor", "CenterCursorOnScroll", p_CenterCursorOnScroll );
+ m_ResourceMgr->setValue( "PyEditor", "TabSpaceVisible", p_TabSpaceVisible );
+ m_ResourceMgr->setValue( "PyEditor", "TabSize", p_TabSize );
+ m_ResourceMgr->setValue( "PyEditor", "VerticalEdge", p_VerticalEdge );
+ m_ResourceMgr->setValue( "PyEditor", "NumberColumns", p_NumberColumns );
+ m_ResourceMgr->setValue( "PyEditor", "Font", p_Font );
+ }
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_Settings.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifndef PYEDITOR_SETTINGS_H
+#define PYEDITOR_SETTINGS_H
+
+#include <QFont>
+
+class QSettings;
+class QtxResourceMgr;
+
+const char* const PY_EDITOR = "PythonEditor";
+const char* const HIGHLIGHT_CURRLINE = "HighlightCurrentLine";
+const char* const LINE_NUMBER_AREA = "LineNumberArea";
+const char* const TEXT_WRAP = "TextWrapping";
+const char* const CURSOR_SCROLL = "CenterCursorOnScroll";
+const char* const TAB_WHITESPACES = "TabSpaceVisible";
+const char* const TAB_SIZE = "TabSize";
+const char* const VERTICAL_EDGE = "VerticalEdge";
+const char* const NUM_COLUMNS = "NumberColumns";
+const char* const FONT_FAMILY = "FontFamily";
+const char* const FONT_SIZE = "FontSize";
+
+class PyEditor_Settings
+{
+public:
+ PyEditor_Settings( QtxResourceMgr* = 0 , bool isSingle = true );
+
+ void readSettings();
+ void writeSettings();
+
+ void fromSettings( const QString& );
+ void toSettings( const QString& ) const;
+
+ void readPreferences();
+ void writePreferences();
+
+ bool isSingle();
+
+ // Display settings
+ bool p_HighlightCurrentLine;
+ bool p_TextWrapping;
+ bool p_CenterCursorOnScroll;
+ bool p_LineNumberArea;
+
+ // Vertical edge settings
+ bool p_VerticalEdge;
+ int p_NumberColumns;
+
+ // Tab settings
+ bool p_TabSpaceVisible;
+ int p_TabSize;
+
+ // Font settings
+ QFont p_Font;
+
+private:
+ QSettings* m_Settings;
+ QtxResourceMgr* m_ResourceMgr;
+ bool m_Single;
+};
+
+#endif // PYEDITOR_SETTINGS_H
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_SettingsDlg.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#include "PyEditor_SettingsDlg.h"
+
+#include "PyEditor_Editor.h"
+#include "PyEditor_Settings.h"
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialogButtonBox>
+#include <QFontComboBox>
+#include <QGroupBox>
+#include <QLabel>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+
+/*!
+ \class PyEditor_SettingsDlg
+ \brief Dialog settings for python editor.
+*/
+
+/*!
+ \brief Constructor.
+ \param theEditor widget that is used to edit and display text
+ \param theParent parent widget
+*/
+PyEditor_SettingsDlg::PyEditor_SettingsDlg( PyEditor_Editor* theEditor, QWidget* theParent ) :
+ QDialog( theParent ),
+ my_Editor( theEditor )
+{
+ setWindowTitle( tr("TIT_PY_PREF") );
+ QVBoxLayout* aMainLayout = new QVBoxLayout( this );
+
+ // . Font settings <start>
+ QGroupBox* aFontSetBox = new QGroupBox( tr( "GR_FONT_SET" ) );
+ QHBoxLayout* aFontSetLayout = new QHBoxLayout( aFontSetBox );
+ QLabel* aFontFamilyLabel = new QLabel( tr( "LBL_FONT_FAM" ) );
+ w_FontFamily = new QFontComboBox;
+ w_FontFamily->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred );
+ QLabel* aFontSizeLabel = new QLabel( tr( "LBL_FONT_SIZE" ) );
+ w_FontSize = new QComboBox;
+ w_FontSize->setInsertPolicy( QComboBox::NoInsert );
+ w_FontSize->setValidator( new QIntValidator( 1, 250, w_FontSize ) );
+ w_FontSize->setEditable( true );
+ w_FontSize->setMinimumContentsLength( 3 );
+ aFontSetLayout->addWidget( aFontFamilyLabel );
+ aFontSetLayout->addWidget( w_FontFamily );
+ aFontSetLayout->addWidget( aFontSizeLabel );
+ aFontSetLayout->addWidget( w_FontSize );
+ /*connect( w_FontFamily, SIGNAL( currentFontChanged( const QFont& ) ),
+ this, SLOT( onFontChanged( const QFont& ) ) );*/
+ // . Font settings <end>
+
+ // . Display settings <start>
+ QGroupBox* aDisplaySetBox = new QGroupBox( tr( "GR_DISP_SET" ) );
+ QVBoxLayout* aDisplaySetLayout = new QVBoxLayout;
+ w_HighlightCurrentLine = new QCheckBox( tr( "LBL_CURRLINE_HIGHLIGHT" ) );
+ w_TextWrapping = new QCheckBox( tr( "LBL_TEXT_WRAP" ) );
+ w_CenterCursorOnScroll = new QCheckBox( tr( "LBL_CURSOR_SCROLL" ) );
+ w_LineNumberArea = new QCheckBox( tr( "LBL_LINE_NUMBS_AREA" ) );
+ aDisplaySetLayout->addWidget( w_HighlightCurrentLine );
+ aDisplaySetLayout->addWidget( w_TextWrapping );
+ aDisplaySetLayout->addWidget( w_CenterCursorOnScroll );
+ aDisplaySetLayout->addWidget( w_LineNumberArea );
+ aDisplaySetLayout->addStretch( 1 );
+ aDisplaySetBox->setLayout( aDisplaySetLayout );
+ // . Display settings <end>
+
+ QHBoxLayout* aTabVertEdgeLayout = new QHBoxLayout;
+
+ // . Tab settings <start>
+ QGroupBox* aTabSetBox = new QGroupBox( tr( "GR_TAB_SET" ) );
+ QVBoxLayout* aTabSetLayout = new QVBoxLayout;
+ w_TabSpaceVisible = new QCheckBox( tr( "LBL_TAB_SPACES" ) );
+ QHBoxLayout* aTabSizeLayout = new QHBoxLayout;
+ QLabel* aTabSizeLabel = new QLabel( tr( "LBL_TAB_SIZE" ) );
+ w_TabSize = new QSpinBox;
+ w_TabSize->setMinimum( 0 );
+ w_TabSize->setSingleStep( 1 );
+ aTabSizeLayout->addWidget( aTabSizeLabel );
+ aTabSizeLayout->addWidget( w_TabSize );
+ aTabSizeLayout->addStretch( 1 );
+ aTabSetLayout->addWidget( w_TabSpaceVisible );
+ aTabSetLayout->addLayout( aTabSizeLayout );
+ aTabSetBox->setLayout( aTabSetLayout );
+ // . Tab settings <end>
+
+ // . Vertical edge settings <start>
+ QGroupBox* aVertEdgeSetBox = new QGroupBox( tr( "GR_VERT_EDGE_SET" ) );
+ QVBoxLayout* aVertEdgeLayout = new QVBoxLayout;
+ w_VerticalEdge = new QCheckBox( tr( "LBL_VERT_EDGE" ) );
+ QHBoxLayout* aNumberColLayout = new QHBoxLayout;
+ lbl_NumColumns = new QLabel( tr( "LBL_NUM_COLUMNS" ) );
+ w_NumberColumns = new QSpinBox;
+ w_NumberColumns->setMinimum( 0 );
+ w_NumberColumns->setSingleStep( 1 );
+ aNumberColLayout->addWidget( lbl_NumColumns );
+ aNumberColLayout->addWidget( w_NumberColumns );
+ aNumberColLayout->addStretch( 1 );
+ aVertEdgeLayout->addWidget( w_VerticalEdge );
+ aVertEdgeLayout->addLayout( aNumberColLayout );
+ aVertEdgeSetBox->setLayout( aVertEdgeLayout );
+ connect( w_VerticalEdge, SIGNAL( clicked( bool ) ),
+ this, SLOT( onVerticalEdgeChecked( bool ) ) );
+ // . Vertical edge settings <end>
+
+ aTabVertEdgeLayout->addWidget( aTabSetBox );
+ aTabVertEdgeLayout->addWidget( aVertEdgeSetBox );
+
+ // . "Set as default" check box
+ w_DefaultCheck = new QCheckBox( tr( "WDG_SET_AS_DEFAULT_CHECK" ), this );
+
+ aMainLayout->addWidget( aFontSetBox );
+ aMainLayout->addWidget( aDisplaySetBox );
+ aMainLayout->addLayout( aTabVertEdgeLayout );
+ aMainLayout->addWidget( w_DefaultCheck );
+ aMainLayout->addStretch( 1 );
+
+ QHBoxLayout* aButtonLayout = new QHBoxLayout;
+ w_ButtonBox = new QDialogButtonBox( Qt::Horizontal );
+ w_ButtonBox->setStandardButtons( QDialogButtonBox::Ok
+ | QDialogButtonBox::Cancel
+ | QDialogButtonBox::Apply );
+ aButtonLayout->addWidget( w_ButtonBox, 1, Qt::AlignRight );
+ aMainLayout->addLayout( aButtonLayout );
+
+ connect( w_ButtonBox, SIGNAL( clicked( QAbstractButton* ) ),
+ this, SLOT( onClick( QAbstractButton* ) ) );
+
+ settingsToGui();
+
+ onFontChanged( currentFont() );
+}
+
+/*!
+ \brief Set currently selected font.
+ \param fnt current font
+ \sa currentFont()
+*/
+void PyEditor_SettingsDlg::setCurrentFont( const QFont& fnt )
+{
+ w_FontFamily->blockSignals( true );
+ w_FontSize->blockSignals( true );
+
+ setFontFamily( fnt.family() );
+ setFontSize( fnt.pointSize() );
+
+ w_FontFamily->blockSignals( false );
+ w_FontSize->blockSignals( false );
+}
+
+/*!
+ \brief Get currently selected font.
+ \return current font
+ \sa setCurrentFont()
+*/
+QFont PyEditor_SettingsDlg::currentFont() const
+{
+ return QFont( fontFamily(), fontSize() );
+}
+
+/*!
+ \brief Set font size.
+ \param s size value
+ \sa fontSize()
+*/
+void PyEditor_SettingsDlg::setFontSize( const int s )
+{
+ if ( s <= 0 )
+ return;
+
+ int idx = w_FontSize->findText( QString::number( s ) );
+ if ( idx != -1 )
+ w_FontSize->setCurrentIndex( idx );
+ else if ( w_FontSize->isEditable() )
+ w_FontSize->setEditText( QString::number( s ) );
+}
+
+/*!
+ \brief Get font size.
+ \return size value
+ \sa setFontSize()
+*/
+int PyEditor_SettingsDlg::fontSize() const
+{
+ bool ok;
+ int pSize = w_FontSize->currentText().toInt( &ok );
+ return ok ? pSize : 0;
+}
+
+/*!
+ \brief Set font family name.
+ \param theFamily new font family name
+ \sa fontFamily()
+*/
+void PyEditor_SettingsDlg::setFontFamily( const QString& theFamily )
+{
+ w_FontFamily->setCurrentFont( QFont( theFamily ) );
+ onFontChanged( w_FontFamily->currentFont() );
+}
+
+/*!
+ \brief Get font family name.
+ \return font family name
+ \sa setFontFamily()
+*/
+QString PyEditor_SettingsDlg::fontFamily() const
+{
+ return w_FontFamily->currentFont().family();
+}
+
+/*!
+ \brief Get "Set settings as default" check box value.
+ \return \c true if "Set settings as default" check box is on
+*/
+bool PyEditor_SettingsDlg::isSetAsDefault()
+{
+ return w_DefaultCheck->isChecked();
+}
+
+/*!
+ SLOT: Perform dialog actions
+ \param theButton button
+*/
+void PyEditor_SettingsDlg::onClick( QAbstractButton* theButton )
+{
+ QDialogButtonBox::ButtonRole aButtonRole = w_ButtonBox->buttonRole( theButton );
+ if ( aButtonRole == QDialogButtonBox::AcceptRole )
+ {
+ settingsFromGui();
+ setSettings();
+ accept();
+ }
+ else if ( aButtonRole == QDialogButtonBox::ApplyRole )
+ {
+ settingsFromGui();
+ setSettings();
+ }
+ else if ( aButtonRole == QDialogButtonBox::RejectRole )
+ {
+ reject();
+ }
+}
+
+/*!
+ SLOT: Changes the widget visibility depending on the set theState flag.
+ \param theState flag of visibility
+ */
+void PyEditor_SettingsDlg::onVerticalEdgeChecked( bool theState )
+{
+ lbl_NumColumns->setEnabled( theState );
+ w_NumberColumns->setEnabled( theState );
+}
+
+/*!
+ \brief Called when current font is changed.
+ \param theFont (not used)
+*/
+void PyEditor_SettingsDlg::onFontChanged( const QFont& /*theFont*/ )
+{
+ bool blocked = w_FontSize->signalsBlocked();
+ w_FontSize->blockSignals( true );
+
+ int s = fontSize();
+ w_FontSize->clear();
+
+ QList<int> szList = QFontDatabase().pointSizes( fontFamily() );
+ QStringList sizes;
+ for ( QList<int>::const_iterator it = szList.begin(); it != szList.end(); ++it )
+ sizes.append( QString::number( *it ) );
+ w_FontSize->addItems( sizes );
+
+ setFontSize( s );
+
+ w_FontSize->blockSignals( blocked );
+}
+
+/*!
+ \brief Sets settings from preferences dialog.
+ */
+void PyEditor_SettingsDlg::settingsFromGui()
+{
+ my_Editor->settings()->p_HighlightCurrentLine = w_HighlightCurrentLine->isChecked();
+ my_Editor->settings()->p_TextWrapping = w_TextWrapping->isChecked();
+ my_Editor->settings()->p_CenterCursorOnScroll = w_CenterCursorOnScroll->isChecked();
+ my_Editor->settings()->p_LineNumberArea = w_LineNumberArea->isChecked();
+ my_Editor->settings()->p_TabSpaceVisible = w_TabSpaceVisible->isChecked();
+ my_Editor->settings()->p_TabSize = w_TabSize->value();
+ my_Editor->settings()->p_VerticalEdge = w_VerticalEdge->isChecked();
+ my_Editor->settings()->p_NumberColumns = w_NumberColumns->value();
+ my_Editor->settings()->p_Font = currentFont();
+}
+
+/*!
+ \brief Sets settings into preferences dialog.
+ */
+void PyEditor_SettingsDlg::settingsToGui()
+{
+ w_HighlightCurrentLine->setChecked( my_Editor->settings()->p_HighlightCurrentLine );
+ w_TextWrapping->setChecked( my_Editor->settings()->p_TextWrapping );
+ w_CenterCursorOnScroll->setChecked( my_Editor->settings()->p_CenterCursorOnScroll );
+ w_LineNumberArea->setChecked( my_Editor->settings()->p_LineNumberArea );
+ w_TabSpaceVisible->setChecked( my_Editor->settings()->p_TabSpaceVisible );
+ w_TabSize->setValue( my_Editor->settings()->p_TabSize );
+ w_VerticalEdge->setChecked( my_Editor->settings()->p_VerticalEdge );
+ w_NumberColumns->setValue( my_Editor->settings()->p_NumberColumns );
+ setCurrentFont( my_Editor->settings()->p_Font );
+
+ onVerticalEdgeChecked( my_Editor->settings()->p_VerticalEdge );
+}
+
+/*!
+ \brief Sets settings into the setting resources or file
+ if the flag is set as default is true.
+ */
+void PyEditor_SettingsDlg::setSettings()
+{
+ if ( isSetAsDefault() )
+ my_Editor->settings()->writeSettings();
+
+ my_Editor->updateStatement();
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_SettingsDlg.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifndef PYEDITOR_SETTINGSDLG_H
+#define PYEDITOR_SETTINGSDLG_H
+
+#include <QDialog>
+
+class PyEditor_Editor;
+class QAbstractButton;
+class QCheckBox;
+class QComboBox;
+class QDialogButtonBox;
+class QFontComboBox;
+class QLabel;
+class QSpinBox;
+
+class PyEditor_SettingsDlg : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PyEditor_SettingsDlg( PyEditor_Editor*, QWidget* = 0 );
+
+ void setCurrentFont( const QFont& );
+ QFont currentFont() const;
+
+ void setFontSize( const int );
+ int fontSize() const;
+
+ void setFontFamily( const QString& );
+ QString fontFamily() const;
+
+ bool isSetAsDefault();
+
+public Q_SLOTS:
+ void onClick( QAbstractButton* );
+
+private Q_SLOTS:
+ void onVerticalEdgeChecked( bool );
+ void onFontChanged( const QFont& );
+
+private:
+ void settingsToGui();
+ void settingsFromGui();
+ void setSettings();
+
+ QCheckBox* w_HighlightCurrentLine;
+ QCheckBox* w_TextWrapping;
+ QCheckBox* w_CenterCursorOnScroll;
+ QCheckBox* w_LineNumberArea;
+
+ QCheckBox* w_TabSpaceVisible;
+ QSpinBox* w_TabSize;
+
+ QCheckBox* w_VerticalEdge;
+ QSpinBox* w_NumberColumns;
+ QLabel* lbl_NumColumns;
+
+ QFontComboBox* w_FontFamily;
+ QComboBox* w_FontSize;
+
+ QCheckBox* w_DefaultCheck;
+
+ QDialogButtonBox* w_ButtonBox;
+
+ PyEditor_Editor* my_Editor;
+};
+
+#endif // PYEDITOR_SETTINGSDLG_H
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="en">
+ <context>
+ <name>PyEditor_SettingsDlg</name>
+ <message>
+ <source>TIT_PY_PREF</source>
+ <translation>Preferences</translation>
+ </message>
+ <message>
+ <source>GR_FONT_SET</source>
+ <translation>Font settings</translation>
+ </message>
+ <message>
+ <source>LBL_FONT_FAM</source>
+ <translation>Family:</translation>
+ </message>
+ <message>
+ <source>LBL_FONT_SIZE</source>
+ <translation>Size:</translation>
+ </message>
+ <message>
+ <source>GR_DISP_SET</source>
+ <translation>Display settings</translation>
+ </message>
+ <message>
+ <source>LBL_CURRLINE_HIGHLIGHT</source>
+ <translation>Enable current line highlight</translation>
+ </message>
+ <message>
+ <source>LBL_TEXT_WRAP</source>
+ <translation>Enable text wrapping</translation>
+ </message>
+ <message>
+ <source>LBL_CURSOR_SCROLL</source>
+ <translation>Center cursor on scroll</translation>
+ </message>
+ <message>
+ <source>LBL_LINE_NUMBS_AREA</source>
+ <translation>Display line numbers area</translation>
+ </message>
+ <message>
+ <source>GR_TAB_SET</source>
+ <translation>Tab settings</translation>
+ </message>
+ <message>
+ <source>LBL_TAB_SPACES</source>
+ <translation>Display tab white spaces</translation>
+ </message>
+ <message>
+ <source>LBL_TAB_SIZE</source>
+ <translation>Tab size:</translation>
+ </message>
+ <message>
+ <source>GR_VERT_EDGE_SET</source>
+ <translation>Vertical edge settings</translation>
+ </message>
+ <message>
+ <source>LBL_VERT_EDGE</source>
+ <translation>Display vertical edge</translation>
+ </message>
+ <message>
+ <source>LBL_NUM_COLUMNS</source>
+ <translation>Number of columns:</translation>
+ </message>
+ <message>
+ <source>WDG_SET_AS_DEFAULT_CHECK</source>
+ <translation>Save settings as default</translation>
+ </message>
+ </context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="fr" sourcelanguage="en">
+<context>
+ <name>PyEditor_SettingsDlg</name>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="ja" sourcelanguage="en">
+<context>
+ <name>PyEditor_SettingsDlg</name>
+</context>
+</TS>
--- /dev/null
+# Copyright (C) 2015 OPEN CASCADE
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+# Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+#
+
+INCLUDE(UseQt4Ext)
+
+# additional include directories
+INCLUDE_DIRECTORIES(
+ ${QT_INCLUDES}
+ ${PROJECT_SOURCE_DIR}/src/Qtx
+ ${PROJECT_SOURCE_DIR}/src/SUIT
+ ${PROJECT_SOURCE_DIR}/src/PyEditor
+)
+
+# additional preprocessor / compiler flags
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+
+# libraries to link to
+SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES} qtx suit PyEditor)
+
+# header files / to be processed by moc
+SET(_moc_HEADERS
+ PyViewer_ViewManager.h
+ PyViewer_ViewModel.h
+ PyViewer_ViewWindow.h
+)
+
+# header files / no moc processing
+SET(_other_HEADERS
+ PyViewer.h
+)
+
+# header files / to install
+SET(PyViewer_HEADERS ${_moc_HEADERS} ${_other_HEADERS})
+
+# resource files / to be processed by lrelease
+SET(RESOURCES_PATH resources)
+
+SET(_ts_RESOURCES
+ ${RESOURCES_PATH}/translations/PyViewer_msg_en.ts
+ ${RESOURCES_PATH}/translations/PyViewer_msg_fr.ts
+ ${RESOURCES_PATH}/translations/PyViewer_msg_ja.ts
+)
+
+# resource files / to be processed by rcc
+SET(_rcc_RESOURCES ${RESOURCES_PATH}/PyEditor.qrc)
+
+# sources / moc wrappings
+QT4_WRAP_CPP(_moc_SOURCES ${_moc_HEADERS})
+
+# sources / rcc wrappings
+QT4_ADD_RESOURCES(_rcc_SOURCES ${_rcc_RESOURCES})
+
+# sources / static
+SET(_other_SOURCES
+ PyViewer_ViewManager.cxx
+ PyViewer_ViewModel.cxx
+ PyViewer_ViewWindow.cxx
+)
+
+# sources / to compile
+SET(PyViewer_SOURCES ${_other_SOURCES} ${_moc_SOURCES})
+
+# --- rules ---
+ADD_LIBRARY(PyViewer ${PyViewer_SOURCES} ${_rcc_SOURCES})
+TARGET_LINK_LIBRARIES(PyViewer ${_link_LIBRARIES})
+INSTALL(TARGETS PyViewer EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS})
+
+ADD_EXECUTABLE(DummyPyEditor PyViewer.cxx)
+SET_TARGET_PROPERTIES(DummyPyEditor PROPERTIES OUTPUT_NAME "PyEditor")
+TARGET_LINK_LIBRARIES(DummyPyEditor ${_link_LIBRARIES} PyEditor PyViewer)
+INSTALL(TARGETS DummyPyEditor EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_BINS})
+
+INSTALL(FILES ${PyViewer_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS})
+QT4_INSTALL_TS_RESOURCES("${_ts_RESOURCES}" "${SALOME_GUI_INSTALL_RES_DATA}")
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyViewer.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#include <QApplication>
+#include <QDir>
+#include <QLibraryInfo>
+#include <QLocale>
+
+#include "PyViewer_ViewWindow.h"
+
+#include <QtxTranslator.h>
+
+int main( int argc, char *argv[] )
+{
+ QApplication anApplication( argc, argv );
+
+ // Load translations
+ QtxTranslator aTranslatorEd , aTranslatorVi, aTranslatorQt;
+ QString aLanguage = QLocale::system().name().split('_', QString::SkipEmptyParts)[0];
+ if ( !aLanguage.isEmpty() )
+ {
+ if ( aTranslatorQt.load( QString( "qt_%1" ).arg( aLanguage ), QLibraryInfo::location( QLibraryInfo::TranslationsPath ) ) )
+ anApplication.installTranslator( &aTranslatorQt );
+
+ QDir appDir = QApplication::applicationDirPath();
+ appDir.cdUp(); appDir.cdUp();
+
+ if ( aTranslatorEd.load( QString( "PyEditor_msg_%1" ).arg( aLanguage ), appDir.filePath( "share/salome/resources/gui" ) ) )
+ anApplication.installTranslator( &aTranslatorEd );
+
+ if ( aTranslatorVi.load( QString( "PyViewer_msg_%1" ).arg( aLanguage ), appDir.filePath( "share/salome/resources/gui" ) ) )
+ anApplication.installTranslator( &aTranslatorVi );
+ }
+
+ PyViewer_ViewWindow aViewWin;
+ aViewWin.resize( 650, 700 );
+ aViewWin.show();
+
+ return anApplication.exec();
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyViewer.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifdef WIN32
+
+#if defined PYVIEWER_EXPORTS || defined PyViewer_EXPORTS
+#define PYVIEWER_EXPORT __declspec(dllexport)
+#else
+#define PYVIEWER_EXPORT __declspec(dllimport)
+#endif
+
+#else
+#define PYVIEWER_EXPORT
+#endif // WIN32
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyViewer_ViewManager.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#include "PyViewer_ViewManager.h"
+
+#include "PyViewer_ViewModel.h"
+
+/*!
+ \class PyViewer_ViewManager
+ \brief Python viewer view manager.
+*/
+
+/*!
+ \brief Constructor.
+ \param theStudy study
+ \param theDesktop parent desktop window
+*/
+PyViewer_ViewManager::PyViewer_ViewManager( SUIT_Study* theStudy,
+ SUIT_Desktop* theDesktop )
+: SUIT_ViewManager( theStudy, theDesktop, new PyViewer_Viewer() )
+{
+ setTitle( tr( "PYVIEWER_VIEW_TITLE" ) );
+}
+
+/*!
+ \brief Destructor.
+*/
+PyViewer_ViewManager::~PyViewer_ViewManager()
+{
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyEditor_ViewManager.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifndef PYVIEWER_VIEWMANAGER_H
+#define PYVIEWER_VIEWMANAGER_H
+
+#include "PyViewer.h"
+
+#include <SUIT_ViewManager.h>
+
+class PYVIEWER_EXPORT PyViewer_ViewManager : public SUIT_ViewManager
+{
+ Q_OBJECT
+
+public:
+ PyViewer_ViewManager( SUIT_Study* theStudy,
+ SUIT_Desktop* theDesktop );
+ virtual ~PyViewer_ViewManager();
+};
+
+#endif // PYVIEWER_VIEWMANAGER_H
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyViewer_ViewModel.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#include "PyViewer_ViewModel.h"
+
+#include "PyViewer_ViewWindow.h"
+
+/*!
+ \class PyViewer_Viewer
+ \brief Python view model.
+*/
+
+/*!
+ \brief Constructor.
+*/
+PyViewer_Viewer::PyViewer_Viewer() : SUIT_ViewModel()
+{
+}
+
+/*!
+ \brief Destructor.
+*/
+PyViewer_Viewer::~PyViewer_Viewer()
+{
+}
+
+/*!
+ Create new instance of view window on desktop \a theDesktop.
+ \retval SUIT_ViewWindow* - created view window pointer.
+*/
+SUIT_ViewWindow* PyViewer_Viewer::createView( SUIT_Desktop* theDesktop )
+{
+ PyViewer_ViewWindow* aPyViewer = new PyViewer_ViewWindow( theDesktop, this );
+ initView( aPyViewer );
+ return aPyViewer;
+}
+
+/*!
+ Start initialization of view window
+ \param view - view window to be initialized
+*/
+void PyViewer_Viewer::initView( PyViewer_ViewWindow* theViewModel )
+{
+ if ( theViewModel )
+ theViewModel->initLayout();
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyViewer_ViewModel.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifndef PYVIEWER_VIEWMODEL_H
+#define PYVIEWER_VIEWMODEL_H
+
+#include "PyViewer.h"
+
+#include <SUIT_ViewModel.h>
+
+class PyViewer_ViewWindow;
+class SUIT_ViewWindow;
+class SUIT_Desktop;
+
+class PYVIEWER_EXPORT PyViewer_Viewer : public SUIT_ViewModel
+{
+ Q_OBJECT
+
+public:
+ PyViewer_Viewer();
+ virtual ~PyViewer_Viewer();
+
+ virtual SUIT_ViewWindow* createView( SUIT_Desktop* theDesktop );
+
+ virtual QString getType() const { return Type(); }
+ static QString Type() { return "PyViewer"; }
+
+protected:
+ void initView( PyViewer_ViewWindow* theViewModel );
+};
+
+#endif // PYVIEWER_VIEWMODEL_H
+
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyViewer_ViewWindow.cxx
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#include "PyViewer_ViewWindow.h"
+
+#include "PyEditor_Editor.h"
+#include "PyEditor_Settings.h"
+#include "PyEditor_SettingsDlg.h"
+
+#include <SUIT_Session.h>
+#include <SUIT_ResourceMgr.h>
+
+#include <QtxAction.h>
+#include <QtxActionToolMgr.h>
+#include <QtxMultiAction.h>
+
+#include <QtGui>
+#include <QLocale>
+
+/*!
+ \class PyViewer_ViewWindow
+ \brief Python view window.
+*/
+
+/*!
+ \brief Constructor.
+ \param theParent parent widget
+*/
+PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* theDesktop , PyViewer_Viewer* theModel ) :
+ SUIT_ViewWindow(theDesktop),
+ myModel(theModel)
+{
+ my_IsExternal = (theDesktop == NULL);
+
+ if( isExternal() )
+ initLayout();
+}
+
+void PyViewer_ViewWindow::initLayout()
+{
+ my_TextEditor = new PyEditor_Editor( my_IsExternal ,SUIT_Session::session()->resourceMgr(), this );
+ setCentralWidget( my_TextEditor );
+
+ createActions();
+ createToolBar();
+ setCurrentFile( QString() );
+
+ if ( isExternal() )
+ {
+ connect( my_TextEditor->document(), SIGNAL( modificationChanged( bool ) ),
+ this, SLOT( setWindowModified( bool ) ) );
+
+ statusBar()->showMessage( tr("STS_READY") );
+ }
+}
+
+/*!
+ \brief Destructor.
+ */
+PyViewer_ViewWindow::~PyViewer_ViewWindow()
+{
+ my_CurrentFile.clear();
+ delete my_TextEditor;
+}
+
+/*!
+ \return \c true if the application is external
+ */
+bool PyViewer_ViewWindow::isExternal()
+{
+ return my_IsExternal;
+}
+
+/*!
+ \brief Creates actions of Python view window.
+*/
+void PyViewer_ViewWindow::createActions()
+{
+ QtxActionToolMgr* aMgr = toolMgr();
+ QtxAction* anAction;
+
+ // 1. File operations
+ // 1.1. Create New action
+ anAction = new QtxAction( tr( "MNU_PY_NEW" ), QIcon( ":/images/py_new.png" ),
+ tr( "MNU_PY_NEW" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_NEW" ) );
+ anAction->setShortcuts( QKeySequence::New );
+ connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onNew() ) );
+ aMgr->registerAction( anAction, NewId );
+
+ // 1.2 Create Open action
+ anAction = new QtxAction( tr( "MNU_PY_OPEN" ), QIcon( ":/images/py_open.png" ),
+ tr( "MNU_PY_OPEN" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_OPEN" ) );
+ anAction->setShortcuts( QKeySequence::Open );
+ connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onOpen() ) );
+ aMgr->registerAction( anAction, OpenId );
+
+ // 1.3. Create Save action
+ anAction = new QtxAction( tr( "MNU_PY_SAVE" ), QIcon( ":/images/py_save.png" ),
+ tr( "MNU_PY_SAVE" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_SAVE" ) );
+ anAction->setShortcuts( QKeySequence::Save );
+ connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onSave() ) );
+ aMgr->registerAction( anAction, SaveId );
+ // Set default statement for Save action
+ anAction->setEnabled( my_TextEditor->document()->isModified() );
+ connect( my_TextEditor->document(), SIGNAL( modificationChanged( bool ) ),
+ anAction, SLOT( setEnabled( bool ) ) );
+
+ // 1.4. Create SaveAs action
+ anAction = new QtxAction( tr( "MNU_PY_SAVEAS" ), QIcon( ":/images/py_save_as.png" ),
+ tr( "MNU_PY_SAVEAS" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_SAVEAS" ) );
+ anAction->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_S );
+ connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onSaveAs() ) );
+ aMgr->registerAction( anAction, SaveAsId );
+
+ // 1.5 Create multi-action for file operations
+ /*QtxMultiAction* aFileAction = new QtxMultiAction( this );
+ aFileAction->insertAction( aMgr->action( NewId ) );
+ aFileAction->insertAction( aMgr->action( OpenId ) );
+ aFileAction->insertAction( aMgr->action( SaveId ) );
+ aFileAction->insertAction( aMgr->action( SaveAsId ) );
+ aMgr->registerAction( aFileAction, FileOpId );*/
+
+ // 1.6. Create Close action
+ if (isExternal())
+ {
+ anAction = new QtxAction( tr( "MNU_PY_CLOSE" ), QIcon( ":/images/py_close.png" ),
+ tr( "MNU_PY_CLOSE" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_CLOSE" ) );
+ anAction->setShortcut( Qt::CTRL + Qt::Key_Q );
+ connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( close() ) );
+ aMgr->registerAction( anAction, CloseId );
+ }
+
+ // 2. Edit operations
+ // 2.1. Create Undo action
+ anAction = new QtxAction( tr( "MNU_PY_UNDO" ), QIcon( ":/images/py_undo.png" ),
+ tr( "MNU_PY_UNDO" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_UNDO" ) );
+ anAction->setShortcuts( QKeySequence::Undo );
+ connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( undo() ) );
+ aMgr->registerAction( anAction, UndoId );
+ // Set default statement for Undo action
+ anAction->setEnabled( my_TextEditor->document()->isUndoAvailable() );
+ connect( my_TextEditor->document(), SIGNAL( undoAvailable( bool ) ),
+ anAction, SLOT( setEnabled( bool ) ) );
+
+ // 2.2. Create Redo action
+ anAction = new QtxAction( tr( "MNU_PY_REDO" ), QIcon( ":/images/py_redo.png" ),
+ tr( "MNU_PY_REDO" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_REDO" ) );
+ anAction->setShortcuts( QKeySequence::Redo );
+ connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( redo() ) );
+ aMgr->registerAction( anAction, RedoId );
+ // Set default statement for Redo action
+ anAction->setEnabled( my_TextEditor->document()->isRedoAvailable() );
+ connect( my_TextEditor->document(), SIGNAL( redoAvailable( bool ) ),
+ anAction, SLOT( setEnabled( bool ) ) );
+
+ // 2.3. Create Cut action
+ anAction = new QtxAction( tr( "MNU_PY_CUT" ), QIcon( ":/images/py_cut.png" ),
+ tr( "MNU_PY_CUT" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_CUT" ) );
+ anAction->setShortcuts( QKeySequence::Cut );
+ connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( cut() ) );
+ aMgr->registerAction( anAction, CutId );
+ // Set default statement for Cut action
+ anAction->setEnabled( false );
+ connect( my_TextEditor, SIGNAL( copyAvailable( bool ) ),
+ anAction, SLOT( setEnabled( bool ) ) );
+
+ // 2.4. Create Copy action
+ anAction = new QtxAction( tr( "MNU_PY_COPY" ), QIcon( ":/images/py_copy.png" ),
+ tr( "MNU_PY_COPY" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_COPY" ) );
+ anAction->setShortcuts( QKeySequence::Copy );
+ connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( copy() ) );
+ aMgr->registerAction( anAction, CopyId );
+ // Set default statement for Copy action
+ anAction->setEnabled( false );
+ connect( my_TextEditor, SIGNAL( copyAvailable( bool ) ),
+ anAction, SLOT( setEnabled( bool ) ) );
+
+ // 2.5. Create Paste action
+ anAction = new QtxAction( tr( "MNU_PY_PASTE" ), QIcon( ":/images/py_paste.png" ),
+ tr( "MNU_PY_PASTE" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_PASTE" ) );
+ anAction->setShortcuts( QKeySequence::Paste );
+ connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( paste() ) );
+ aMgr->registerAction( anAction, PasteId );
+
+ // 2.6. Create Delete action
+ anAction = new QtxAction( tr( "MNU_PY_DELETE" ), QIcon( ":/images/py_delete.png" ),
+ tr( "MNU_PY_DELETE" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_DELETE" ) );
+ anAction->setShortcuts( QKeySequence::Delete );
+ connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( deleteSelected() ) );
+ aMgr->registerAction( anAction, DeleteId );
+ // Set default statement for Delete action
+ anAction->setEnabled( false );
+ connect( my_TextEditor, SIGNAL( copyAvailable( bool ) ),
+ anAction, SLOT( setEnabled( bool ) ) );
+
+ // 2.7. Create SelectAll action
+ anAction = new QtxAction( tr( "MNU_PY_SELECTALL" ), QIcon( ":/images/py_select_all.png" ),
+ tr( "MNU_PY_SELECTALL" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_SELECT_ALL" ) );
+ anAction->setShortcuts( QKeySequence::SelectAll );
+ connect( anAction, SIGNAL( triggered( bool ) ), my_TextEditor, SLOT( selectAll() ) );
+ aMgr->registerAction( anAction, SelectAllId );
+
+ // 2.8. Create multi-action for edit operations
+ /*QtxMultiAction* anEditAction = new QtxMultiAction( this );
+ anEditAction->insertAction( aMgr->action( UndoId ) );
+ anEditAction->insertAction( aMgr->action( RedoId ) );
+ anEditAction->insertAction( aMgr->action( CutId ) );
+ anEditAction->insertAction( aMgr->action( CopyId ) );
+ anEditAction->insertAction( aMgr->action( PasteId ) );
+ anEditAction->insertAction( aMgr->action( DeleteId ) );
+ anEditAction->insertAction( aMgr->action( SelectAllId ) );
+ aMgr->registerAction( anEditAction, EditOpId );*/
+
+ // 3. Create Preference action
+ anAction = new QtxAction( tr( "MNU_PY_PREFERENCES" ), QIcon( ":/images/py_preferences.png" ),
+ tr( "MNU_PY_PREFERENCES" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_PREFERENCES" ) );
+ connect( anAction, SIGNAL( triggered( bool ) ), this, SLOT( onPreferences() ) );
+ aMgr->registerAction( anAction, PreferencesId );
+
+ // 4. Help operations
+
+ // 4.1. Create Help action
+ anAction = new QtxAction( tr( "MNU_PY_BROWSER" ), QIcon( ":/images/py_browser.png" ),
+ tr( "MNU_PY_BROWSER" ), 0, this );
+ anAction->setStatusTip( tr( "DSC_PY_BROWSER" ) );
+ connect( anAction, SIGNAL( triggered() ), this, SLOT( onBrowser() ) );
+ aMgr->registerAction( anAction, BrowserId );
+
+ // 4.2. Create multi-action for help operations
+ /*QtxMultiAction* aHelpAction = new QtxMultiAction( this );
+ aHelpAction->insertAction( aMgr->action( BrowserId ) );
+ aMgr->registerAction( aHelpAction, HelpOpId );*/
+}
+
+/*!
+ \brief Create toolbar for the python view window.
+*/
+void PyViewer_ViewWindow::createToolBar()
+{
+ QtxActionToolMgr* aMgr = toolMgr();
+ int idTB = aMgr->createToolBar( tr("LBL_TOOLBAR_LABEL"), // title (language-dependent)
+ QString( "PyEditorOperations" ), // name (language-independent)
+ false ); // disable floatable toolbar
+ aMgr->append( NewId, idTB );
+ aMgr->append( OpenId, idTB );
+ aMgr->append( SaveId, idTB );
+ aMgr->append( SaveAsId, idTB );
+ if ( isExternal() )
+ aMgr->append( CloseId, idTB );
+ aMgr->append( aMgr->separator(), idTB );
+ aMgr->append( UndoId, idTB );
+ aMgr->append( RedoId, idTB );
+ aMgr->append( aMgr->separator(), idTB );
+ aMgr->append( CutId, idTB );
+ aMgr->append( CopyId, idTB );
+ aMgr->append( PasteId, idTB );
+ aMgr->append( DeleteId, idTB );
+ aMgr->append( SelectAllId, idTB );
+ aMgr->append( aMgr->separator(), idTB );
+ aMgr->append( PreferencesId, idTB );
+ aMgr->append( aMgr->separator(), idTB );
+ aMgr->append( BrowserId, idTB );
+
+}
+
+/*!
+ \brief Reimplemented class is to receive a window close request.
+ \param theEvent event
+*/
+void PyViewer_ViewWindow::closeEvent( QCloseEvent* theEvent )
+{
+ if ( whetherSave() )
+ theEvent->accept();
+ else
+ theEvent->ignore();
+}
+
+/*!
+ SLOT: Creates a new document
+ */
+void PyViewer_ViewWindow::onNew()
+{
+ if ( whetherSave() )
+ {
+ my_TextEditor->clear();
+ setCurrentFile( QString() );
+ }
+}
+
+/*!
+ SLOT: Open an existing python file
+ */
+void PyViewer_ViewWindow::onOpen()
+{
+ if ( whetherSave() )
+ {
+ QString aFilePath = QFileDialog::getOpenFileName(
+ this, tr( "TIT_DLG_OPEN" ), QDir::currentPath(), "Python Files (*.py)" );
+
+ if ( !aFilePath.isEmpty() )
+ loadFile( aFilePath );
+ }
+}
+
+/*!
+ SLOT: Save the current file
+ */
+bool PyViewer_ViewWindow::onSave()
+{
+ if ( my_CurrentFile.isEmpty() )
+ return onSaveAs();
+ else
+ return saveFile( my_CurrentFile );
+}
+
+
+/*!
+ SLOT: Save the current file under a new name
+ */
+bool PyViewer_ViewWindow::onSaveAs()
+{
+ QString aFilePath = QFileDialog::getSaveFileName(
+ this, tr( "TIT_DLG_SAVEAS" ), QDir::currentPath(), "Python Files (*.py)" );
+
+ if ( !aFilePath.isEmpty() )
+ return saveFile( aFilePath );
+
+ return false;
+}
+
+/*!
+ SLOT: Open preferences dialog
+ */
+void PyViewer_ViewWindow::onPreferences()
+{
+ PyEditor_SettingsDlg aPage( my_TextEditor, this );
+ aPage.exec();
+}
+
+/*!
+ \brief Set preferece values for view.
+ */
+void PyViewer_ViewWindow::setPreferences()
+{
+ my_TextEditor->settings()->readSettings();
+ my_TextEditor->updateStatement();
+}
+
+/*!
+ \brief Associates the theFilePath with the python view.
+ \param theFilePath file path
+ */
+void PyViewer_ViewWindow::setCurrentFile( const QString &theFilePath )
+{
+ my_CurrentFile = theFilePath;
+ my_TextEditor->document()->setModified( false );
+
+ if ( isExternal() )
+ {
+ setWindowModified( false );
+
+ QString aShownName = my_CurrentFile;
+ if ( my_CurrentFile.isEmpty() )
+ aShownName = "untitled.py";
+ setWindowFilePath( aShownName );
+
+ // Set window title with associated file path
+ QFileInfo anInfo( aShownName );
+ setWindowTitle( "Python Viewer - " + anInfo.fileName() + "[*]" );
+ }
+}
+
+/*!
+ \brief Checks whether the file is modified.
+ If it has the modifications then ask the user to save it.
+ \return true if the document is saved.
+ */
+bool PyViewer_ViewWindow::whetherSave()
+{
+ if ( my_TextEditor->document()->isModified() )
+ {
+ QMessageBox::StandardButton aReturn;
+ aReturn = QMessageBox::warning(
+ this, tr( "TIT_DLG_SAVE" ),tr( "WRN_PY_SAVE_FILE" ),
+ QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
+
+ if ( aReturn == QMessageBox::Save )
+ return onSave();
+ else if ( aReturn == QMessageBox::Cancel )
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \brief Opens file.
+ \param theFilePath file path
+ */
+void PyViewer_ViewWindow::loadFile( const QString &theFilePath )
+{
+ QFile aFile( theFilePath );
+ if ( !aFile.open(QFile::ReadOnly | QFile::Text) )
+ {
+ QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
+ tr( "WRN_PY_READ_FILE" ).arg( theFilePath ).arg( aFile.errorString() ) );
+ return;
+ }
+
+ QTextStream anInput( &aFile );
+ QApplication::setOverrideCursor( Qt::WaitCursor );
+ my_TextEditor->setPlainText( anInput.readAll() );
+ QApplication::restoreOverrideCursor();
+
+ setCurrentFile( theFilePath );
+ aFile.close();
+ if ( isExternal() )
+ statusBar()->showMessage( tr( "STS_F_LOADED" ), 2000 );
+}
+
+/*!
+ \brief Saves file.
+ \param theFilePath file path
+ */
+bool PyViewer_ViewWindow::saveFile( const QString &theFilePath )
+{
+ QFile aFile( theFilePath );
+ if ( !aFile.open( QFile::WriteOnly | QFile::Text ) )
+ {
+ QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
+ tr( "WRN_PY_WRITE_FILE" ).arg( theFilePath ).arg( aFile.errorString() ) );
+ return false;
+ }
+
+ QTextStream anOutput( &aFile );
+ QApplication::setOverrideCursor( Qt::WaitCursor );
+ anOutput << my_TextEditor->toPlainText();
+ QApplication::restoreOverrideCursor();
+
+ setCurrentFile( theFilePath );
+ aFile.close();
+
+ if ( isExternal() )
+ statusBar()->showMessage( tr( "STS_F_SAVED" ), 2000 );
+
+ return true;
+}
+
+/*!
+ \brief Opens help browser with python view help information.
+ */
+void PyViewer_ViewWindow::onBrowser()
+{
+ QDir appDir = QApplication::applicationDirPath();
+ QStringList parameters;
+ parameters << QString( "--file=%1" ).arg( appDir.filePath( "pyeditor.html" ) );
+ QProcess::startDetached( "HelpBrowser", parameters );
+}
--- /dev/null
+// Copyright (C) 2015 OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : PyViewer_ViewWindow.h
+// Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
+//
+
+#ifndef PYVIEWER_VIEWWINDOW_H
+#define PYVIEWER_VIEWWINDOW_H
+
+#include "PyViewer.h"
+
+#include <SUIT_ViewWindow.h>
+
+class PyEditor_Editor;
+class PyViewer_Viewer;
+
+class PYVIEWER_EXPORT PyViewer_ViewWindow : public SUIT_ViewWindow
+{
+ Q_OBJECT
+
+public:
+ enum { NewId, OpenId, SaveId, SaveAsId, CloseId, FileOpId,
+ UndoId, RedoId, CutId, CopyId, PasteId, DeleteId, SelectAllId, EditOpId,
+ PreferencesId, BrowserId, HelpOpId };
+
+ PyViewer_ViewWindow( SUIT_Desktop* theDesktop = 0, PyViewer_Viewer* theModel = 0 );
+ ~PyViewer_ViewWindow();
+
+ virtual void initLayout();
+
+ bool isExternal();
+ void setPreferences();
+
+protected:
+ virtual void closeEvent( QCloseEvent* );
+
+private Q_SLOTS:
+ void onNew();
+ void onOpen();
+ bool onSave();
+ bool onSaveAs();
+ void onPreferences();
+ void onBrowser();
+
+private:
+ void loadFile( const QString& );
+ bool saveFile( const QString& );
+
+ void setCurrentFile( const QString& );
+ bool whetherSave();
+
+private:
+ void createActions();
+ void createToolBar();
+
+private:
+ PyViewer_Viewer* myModel;
+ PyEditor_Editor* my_TextEditor;
+ QString my_CurrentFile;
+ bool my_IsExternal;
+};
+
+#endif // PYVIEWER_VIEWWINDOW_H
--- /dev/null
+<!DOCTYPE RCC><RCC version="1.0">
+ <qresource>
+ <file>images/py_browser.png</file>
+ <file>images/py_close.png</file>
+ <file>images/py_copy.png</file>
+ <file>images/py_cut.png</file>
+ <file>images/py_delete.png</file>
+ <file>images/py_new.png</file>
+ <file>images/py_open.png</file>
+ <file>images/py_paste.png</file>
+ <file>images/py_preferences.png</file>
+ <file>images/py_redo.png</file>
+ <file>images/py_save.png</file>
+ <file>images/py_save_as.png</file>
+ <file>images/py_select_all.png</file>
+ <file>images/py_undo.png</file>
+ </qresource>
+</RCC>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="en">
+ <context>
+ <name>PyViewer_ViewManager</name>
+ <message>
+ <source>PYVIEWER_VIEW_TITLE</source>
+ <translation>Python viewer:%M - viewer:%V</translation>
+ </message>
+ </context>
+ <context>
+ <name>PyViewer_ViewWindow</name>
+ <message>
+ <source>NAME_PYEDITOR</source>
+ <translation>Python Viewer</translation>
+ </message>
+ <message>
+ <source>LBL_TOOLBAR_LABEL</source>
+ <translation>Edit Operations</translation>
+ </message>
+ <message>
+ <source>MNU_PY_NEW</source>
+ <translation>New</translation>
+ </message>
+ <message>
+ <source>DSC_PY_NEW</source>
+ <translation>Create a new python file</translation>
+ </message>
+ <message>
+ <source>MNU_PY_OPEN</source>
+ <translation>Open</translation>
+ </message>
+ <message>
+ <source>DSC_PY_OPEN</source>
+ <translation>Open an existing python file</translation>
+ </message>
+ <message>
+ <source>MNU_PY_SAVE</source>
+ <translation>Save</translation>
+ </message>
+ <message>
+ <source>DSC_PY_SAVE</source>
+ <translation>Save the python document to disk</translation>
+ </message>
+ <message>
+ <source>MNU_PY_SAVEAS</source>
+ <translation>Save As...</translation>
+ </message>
+ <message>
+ <source>DSC_PY_SAVEAS</source>
+ <translation>Save the python document under a new name</translation>
+ </message>
+ <message>
+ <source>MNU_PY_CLOSE</source>
+ <translation>Close</translation>
+ </message>
+ <message>
+ <source>DSC_PY_CLOSE</source>
+ <translation>Close the python document</translation>
+ </message>
+ <message>
+ <source>MNU_PY_UNDO</source>
+ <translation>Undo</translation>
+ </message>
+ <message>
+ <source>DSC_PY_UNDO</source>
+ <translation>Undoes the last operation</translation>
+ </message>
+ <message>
+ <source>MNU_PY_REDO</source>
+ <translation>Redo</translation>
+ </message>
+ <message>
+ <source>DSC_PY_REDO</source>
+ <translation>Redoes the last operation</translation>
+ </message>
+ <message>
+ <source>MNU_PY_CUT</source>
+ <translation>Cut</translation>
+ </message>
+ <message>
+ <source>DSC_PY_CUT</source>
+ <translation>Cut the current selection's contents to the clipboard</translation>
+ </message>
+ <message>
+ <source>MNU_PY_COPY</source>
+ <translation>Copy</translation>
+ </message>
+ <message>
+ <source>DSC_PY_COPY</source>
+ <translation>Copy the current selection's contents to the clipboard</translation>
+ </message>
+ <message>
+ <source>MNU_PY_PASTE</source>
+ <translation>Paste</translation>
+ </message>
+ <message>
+ <source>DSC_PY_PASTE</source>
+ <translation>Paste the clipboard's contents into the current selection</translation>
+ </message>
+ <message>
+ <source>MNU_PY_DELETE</source>
+ <translation>Delete</translation>
+ </message>
+ <message>
+ <source>DSC_PY_DELETE</source>
+ <translation>Delete the current selection's contents</translation>
+ </message>
+ <message>
+ <source>MNU_PY_SELECTALL</source>
+ <translation>Select All</translation>
+ </message>
+ <message>
+ <source>DSC_PY_SELECT_ALL</source>
+ <translation>Select all the contents</translation>
+ </message>
+ <message>
+ <source>MNU_PY_PREFERENCES</source>
+ <translation>Preferences</translation>
+ </message>
+ <message>
+ <source>DSC_PY_PREFERENCES</source>
+ <translation>Show the Preferences box</translation>
+ </message>
+ <message>
+ <source>MNU_PY_BROWSER</source>
+ <translation>Help Browser</translation>
+ </message>
+ <message>
+ <source>DSC_PY_BROWSER</source>
+ <translation>Show the Help browser</translation>
+ </message>
+ <message>
+ <source>WRN_PY_READ_FILE</source>
+ <translation>Cannot read file %1:\n%2.</translation>
+ </message>
+ <message>
+ <source>WRN_PY_WRITE_FILE</source>
+ <translation>Cannot write file %1:\n%2.</translation>
+ </message>
+ <message>
+ <source>WRN_PY_SAVE_FILE</source>
+ <translation>The document has been modified.<br>Do you want to save your changes?</translation>
+ </message>
+ <message>
+ <source>TIT_DLG_SAVE</source>
+ <translation>Save file</translation>
+ </message>
+ <message>
+ <source>TIT_DLG_SAVEAS</source>
+ <translation>Save file as</translation>
+ </message>
+ <message>
+ <source>TIT_DLG_OPEN</source>
+ <translation>Open file</translation>
+ </message>
+ <message>
+ <source>STS_READY</source>
+ <translation>Ready</translation>
+ </message>
+ <message>
+ <source>STS_F_LOADED</source>
+ <translation>File is loaded</translation>
+ </message>
+ <message>
+ <source>STS_F_SAVED</source>
+ <translation>File is saved</translation>
+ </message>
+ </context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="fr" sourcelanguage="en">
+<context>
+ <name>PyViewer_ViewManager</name>
+ <message>
+ <source>PYVIEWER_VIEW_TITLE</source>
+ <translation>Éditeur de python:%M - viseur:%V</translation>
+ </message>
+</context>
+ <context>
+ <name>PyViewer_ViewWindow</name>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="ja" sourcelanguage="en">
+<context>
+ <name>PyViewer_ViewManager</name>
+ <message>
+ <source>PYVIEWER_VIEW_TITLE</source>
+ <translation>Pythonのエディタ:%M - ビューア:%V</translation>
+ </message>
+</context>
+</context>
+<context>
+ <name>PyViewer_ViewWindow</name>
+</context>
+</TS>