SET(${PROJECT_NAME_UC}_PATCH_VERSION 0)
SET(${PROJECT_NAME_UC}_VERSION
${${PROJECT_NAME_UC}_MAJOR_VERSION}.${${PROJECT_NAME_UC}_MINOR_VERSION}.${${PROJECT_NAME_UC}_PATCH_VERSION})
-SET(${PROJECT_NAME_UC}_VERSION_DEV 0)
+SET(${PROJECT_NAME_UC}_VERSION_DEV 1)
# Common CMake macros
# ===================
if ( toolMgr() )
toolMgr()->registerAction( a );
- if ( application() && application()->desktop() )
+ if ( application() && application()->desktop() &&
+ a->shortcutContext() != Qt::WidgetShortcut &&
+ a->shortcutContext() != Qt::WidgetWithChildrenShortcut )
application()->desktop()->addAction( a );
return ident;
// Preferences
createAction( PreferencesId, tr( "TOT_DESK_PREFERENCES" ), QIcon(),
tr( "MEN_DESK_PREFERENCES" ), tr( "PRP_DESK_PREFERENCES" ),
- Qt::CTRL+Qt::Key_R, desk, false, this, SLOT( onPreferences() ) );
+ Qt::CTRL+Qt::Key_P, desk, false, this, SLOT( onPreferences() ) );
// Help menu:
pref->addPreference( tr( "PREF_PY_LINE_NUMBS_AREA" ), pyDispGroup,
LightApp_Preferences::Bool, "PyEditor", "LineNumberArea" );
// ... "Display settings" group <<end>>
+
+ // ... "Editor settings" group <<start>>
+ int pyEditGroup = pref->addPreference( tr( "PREF_GROUP_PY_EDITOR" ), pyeditTab );
+ // ... -> navigation mode
+ int pyCompletion = pref->addPreference( tr( "PREF_PY_COMPLETION_MODE" ), pyEditGroup,
+ LightApp_Preferences::Selector, "PyEditor", "CompletionPolicy" );
+ aValuesList.clear();
+ anIndicesList.clear();
+ aValuesList << tr("PREF_PY_NONE") << tr("PREF_PY_AUTO") << tr("PREF_PY_MANUAL") << tr("PREF_PY_ALWAYS");
+ anIndicesList << 0 << 1 << 2 << 3 ;
+ pref->setItemProperty( "strings", aValuesList, pyCompletion );
+ pref->setItemProperty( "indexes", anIndicesList, pyCompletion );
+ // ... "Editor settings" group <<end>>
+
// ... "Tab settings" group <<start>>
int pyTabGroup = pref->addPreference( tr( "PREF_GROUP_PY_TAB" ), pyeditTab );
pref->setItemProperty( "columns", 2, pyTabGroup );
<parameter name="TabSize" value="4" />
<parameter name="VerticalEdge" value="true" />
<parameter name="NumberColumns" value="90" />
+ <parameter name="CompletionPolicy" value="3" />
</section>
<section name="GUI" >
<parameter name="documentation" value="gui_help"/>
<source>PREF_PY_LINE_NUMBS_AREA</source>
<translation>Display line numbers area</translation>
</message>
+ <message>
+ <source>PREF_GROUP_PY_EDITOR</source>
+ <translation>Editor settings</translation>
+ </message>
+ <message>
+ <source>PREF_PY_COMPLETION_MODE</source>
+ <translation>Completion mode</translation>
+ </message>
+ <message>
+ <source>PREF_PY_NONE</source>
+ <translation>None</translation>
+ </message>
+ <message>
+ <source>PREF_PY_AUTO</source>
+ <translation>Auto</translation>
+ </message>
+ <message>
+ <source>PREF_PY_MANUAL</source>
+ <translation>Manual</translation>
+ </message>
+ <message>
+ <source>PREF_PY_ALWAYS</source>
+ <translation>Always</translation>
+ </message>
<message>
<source>PREF_GROUP_PY_TAB</source>
<translation>Tab settings</translation>
<source>PREF_PY_LINE_NUMBS_AREA</source>
<translation>Affiche les numéros de ligne</translation>
</message>
+ <message>
+ <source>PREF_GROUP_PY_EDITOR</source>
+ <translation type="unfinished">Editor settings</translation>
+ </message>
+ <message>
+ <source>PREF_PY_COMPLETION_MODE</source>
+ <translation type="unfinished">Completion mode</translation>
+ </message>
+ <message>
+ <source>PREF_PY_NONE</source>
+ <translation type="unfinished">None</translation>
+ </message>
+ <message>
+ <source>PREF_PY_AUTO</source>
+ <translation type="unfinished">Auto</translation>
+ </message>
+ <message>
+ <source>PREF_PY_MANUAL</source>
+ <translation type="unfinished">Manual</translation>
+ </message>
+ <message>
+ <source>PREF_PY_ALWAYS</source>
+ <translation type="unfinished">Always</translation>
+ </message>
<message>
<source>PREF_GROUP_PY_TAB</source>
<translation>Indentation</translation>
<source>PREF_PY_LINE_NUMBS_AREA</source>
<translation>ライン数エリアの表示</translation>
</message>
+ <message>
+ <source>PREF_GROUP_PY_EDITOR</source>
+ <translation type="unfinished">Editor settings</translation>
+ </message>
+ <message>
+ <source>PREF_PY_COMPLETION_MODE</source>
+ <translation type="unfinished">Completion mode</translation>
+ </message>
+ <message>
+ <source>PREF_PY_NONE</source>
+ <translation type="unfinished">None</translation>
+ </message>
+ <message>
+ <source>PREF_PY_AUTO</source>
+ <translation type="unfinished">Auto</translation>
+ </message>
+ <message>
+ <source>PREF_PY_MANUAL</source>
+ <translation type="unfinished">Manual</translation>
+ </message>
+ <message>
+ <source>PREF_PY_ALWAYS</source>
+ <translation type="unfinished">Always</translation>
+ </message>
<message>
<source>PREF_GROUP_PY_TAB</source>
<translation>設定タブ</translation>
myIsRelative(true),
myTopLayerId( 0 ),
myTrihedronSize(100),
- myClippingDlg (NULL)
+ myClippingDlg (NULL),
+ myIsUseLocalSelection(false)
{
// init CasCade viewers
myV3dViewer = OCCViewer_VService::CreateViewer( TCollection_ExtendedString("Viewer3d").ToExtString() );
break;
case Qt::Key_N:
if ( isPreselectionEnabled() ) {
- if ( getAISContext()->HasOpenedContext() )
+ if ( useLocalSelection() )
getAISContext()->HilightNextDetected( aView->getViewPort()->getView() );
}
break;
return new OCCViewer_ViewWindow(0, this);
}
+/*!
+ Sets using local selection state
+ \param theIsUseLocalSelection - state
+*/
+void OCCViewer_Viewer::setUseLocalSelection(bool theIsUseLocalSelection)
+{
+ myIsUseLocalSelection = theIsUseLocalSelection;
+}
+
+/*
+ * Returns true if local context is opened or view model local state is set
+ */
+bool OCCViewer_Viewer::useLocalSelection() const
+{
+ if (myIsUseLocalSelection)
+ return true;
+
+ Handle(AIS_InteractiveContext) ic = getAISContext();
+ return !ic.IsNull() && ic->HasOpenedContext();
+}
+
// obsolete
QColor OCCViewer_Viewer::backgroundColor( int theViewId ) const
{
virtual OCCViewer_ViewWindow* createSubWindow();
+ void setUseLocalSelection(bool theIsUseLocalSelection);
+ bool useLocalSelection() const;
+
public:
Handle(V3d_Viewer) getViewer3d() const { return myV3dViewer;}
Handle(AIS_InteractiveContext) getAISContext() const { return myAISContext; }
QString myClippingTexture;
bool myTextureModulated;
double myClippingTextureScale;
-
+ bool myIsUseLocalSelection;
};
#ifdef WIN32
if ( aEvent->modifiers().testFlag(Qt::ControlModifier) ) {
Handle(AIS_InteractiveContext) ic = myModel->getAISContext();
- if ( isPreselectionEnabled() && ic->HasOpenedContext() ) {
+ if ( isPreselectionEnabled() && myModel->useLocalSelection() ) {
if ( aEvent->delta() > 0 ) {
ic->HilightNextDetected( myViewPort->getView() );
} else {
#include <pqCollaborationBehavior.h>
#include <pqVerifyRequiredPluginBehavior.h>
#include <pqPluginSettingsBehavior.h>
-#include <pqFixPathsInStateFilesBehavior.h>
#include <pqApplyBehavior.h>
#include <pqPropertiesPanel.h>
new pqAlwaysConnectedBehavior(this); // client always connected to a server
new pqVerifyRequiredPluginBehavior(this);
new pqPluginSettingsBehavior(this);
- new pqFixPathsInStateFilesBehavior(this);
new pqCrashRecoveryBehavior(this);
new pqCommandLineOptionsBehavior(this);
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_copy.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_cut.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_delete.png
+ ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_find.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_help.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_new.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_open.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_paste.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_preferences.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_redo.png
+ ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_replace.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_save.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_save_as.png
${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_select_all.png
setTabSpaceVisible( myResMgr->booleanValue( group, option( snTabSpaceVisible ), tabSpaceVisible() ) );
setTabSize( myResMgr->integerValue( group, option( snTabSize ), tabSize() ) );
setFont( myResMgr->fontValue( group, option( snFont ), font() ) );
+ setCompletionPolicy( myResMgr->integerValue( group, option( snCompletionPolicy ),
+ completionPolicy() ) );
}
void PyViewer_Settings::save()
myResMgr->setValue( group, option( snTabSpaceVisible ), tabSpaceVisible() );
myResMgr->setValue( group, option( snTabSize ), tabSize() );
myResMgr->setValue( group, option( snFont ), font() );
+ myResMgr->setValue( group, option( snCompletionPolicy ), completionPolicy() );
}
#include "PyViewer_ViewWindow.h"
-#include "PyEditor_Editor.h"
+#include "PyEditor_Widget.h"
#include "PyEditor_SettingsDlg.h"
#include "SUIT_Session.h"
#include "QtxActionToolMgr.h"
#include <QApplication>
+#include <QCloseEvent>
#include <QFileDialog>
#include <QMessageBox>
#include <QTextStream>
+#include <QVBoxLayout>
/*!
\class PyViewer_ViewWindow
PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
SUIT_ViewWindow( desktop )
{
- // Create editor and set it as a central widget.
- myTextEditor = new PyEditor_Editor( this );
- setCentralWidget( myTextEditor );
+ // Create central widget.
+ myEditor = new PyEditor_Widget( this );
+ setCentralWidget( myEditor );
// Create actions.
SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
action->setShortcut( QKeySequence::Save );
connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onSave() ) );
action->setEnabled( false );
- connect( myTextEditor->document(), SIGNAL( modificationChanged( bool ) ),
+ connect( myEditor, SIGNAL( modificationChanged( bool ) ),
action, SLOT( setEnabled( bool ) ) );
toolMgr()->registerAction( action, SaveId );
tr( "ACT_UNDO" ), 0, this );
action->setStatusTip( tr( "DSC_UNDO" ) );
action->setShortcut( QKeySequence::Undo );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( undo() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( undo() ) );
action->setEnabled( false );
- connect( myTextEditor->document(), SIGNAL( undoAvailable( bool ) ),
+ connect( myEditor, SIGNAL( undoAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
toolMgr()->registerAction( action, UndoId );
tr( "ACT_REDO" ), 0, this );
action->setStatusTip( tr( "DSC_REDO" ) );
action->setShortcut( QKeySequence::Redo );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( redo() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( redo() ) );
action->setEnabled( false );
- connect( myTextEditor->document(), SIGNAL( redoAvailable( bool ) ),
+ connect( myEditor, SIGNAL( redoAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
toolMgr()->registerAction( action, RedoId );
tr( "ACT_CUT" ), 0, this );
action->setStatusTip( tr( "DSC_CUT" ) );
action->setShortcut( QKeySequence::Cut );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( cut() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( cut() ) );
action->setEnabled( false );
- connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+ connect( myEditor, SIGNAL( copyAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
toolMgr()->registerAction( action, CutId );
tr( "ACT_COPY" ), 0, this );
action->setStatusTip( tr( "DSC_COPY" ) );
action->setShortcut( QKeySequence::Copy );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( copy() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
action->setEnabled( false );
- connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+ connect( myEditor, SIGNAL( copyAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
toolMgr()->registerAction( action, CopyId );
tr( "ACT_PASTE" ), 0, this );
action->setStatusTip( tr( "DSC_PASTE" ) );
action->setShortcut( QKeySequence::Paste );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( paste() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
toolMgr()->registerAction( action, PasteId );
// . Delete
tr( "ACT_DELETE" ), 0, this );
action->setStatusTip( tr( "DSC_DELETE" ) );
action->setShortcut( QKeySequence::Delete );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( deleteSelected() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( deleteSelected() ) );
action->setEnabled( false );
- connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+ connect( myEditor, SIGNAL( copyAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
toolMgr()->registerAction( action, DeleteId );
tr( "ACT_SELECT_ALL" ), 0, this );
action->setStatusTip( tr( "DSC_SELECT_ALL" ) );
action->setShortcut( QKeySequence::SelectAll );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( selectAll() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
toolMgr()->registerAction( action, SelectAllId );
+ // . Find
+ action = new QtxAction( tr( "TTP_FIND" ),
+ resMgr->loadPixmap( "PyViewer", tr( "ICON_FIND" ) ),
+ tr( "ACT_FIND" ), 0, this );
+ action->setStatusTip( tr( "DSC_FIND" ) );
+ action->setShortcut( QKeySequence::Find );
+ action->setShortcutContext( Qt::WidgetShortcut );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( find() ) );
+ toolMgr()->registerAction( action, FindId );
+
+ // . Replace
+ action = new QtxAction( tr( "TTP_REPLACE" ),
+ resMgr->loadPixmap( "PyViewer", tr( "ICON_REPLACE" ) ),
+ tr( "ACT_REPLACE" ), 0, this );
+ action->setStatusTip( tr( "DSC_REPLACE" ) );
+ action->setShortcuts( QList<QKeySequence>() << QKeySequence( "Ctrl+H" ) << QKeySequence( QKeySequence::Replace ) );
+ action->setShortcutContext( Qt::WidgetShortcut );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( replace() ) );
+ toolMgr()->registerAction( action, ReplaceId );
+
// . Preferences
action = new QtxAction( tr( "TTP_PREFERENCES" ),
resMgr->loadPixmap( "PyViewer", tr( "ICON_PREFERENCES" ) ),
toolMgr()->append( DeleteId, idTB );
toolMgr()->append( SelectAllId, idTB );
toolMgr()->append( toolMgr()->separator(), idTB );
+ toolMgr()->append( FindId, idTB );
+ toolMgr()->append( ReplaceId, idTB );
+ toolMgr()->append( toolMgr()->separator(), idTB );
toolMgr()->append( PreferencesId, idTB );
toolMgr()->append( toolMgr()->separator(), idTB );
toolMgr()->append( HelpId, idTB );
{
if ( whetherSave() )
{
- myTextEditor->clear();
+ myEditor->clear();
setCurrentFile( QString() );
}
}
*/
void PyViewer_ViewWindow::onPreferences()
{
- PyEditor_SettingsDlg dlg( myTextEditor, true, this );
+ PyEditor_SettingsDlg dlg( myEditor->editor(), true, this );
connect( &dlg, SIGNAL( help() ), this, SLOT( onHelp() ) );
dlg.exec();
}
void PyViewer_ViewWindow::setCurrentFile( const QString& filePath )
{
myURL = filePath;
- myTextEditor->document()->setModified( false );
+ myEditor->setModified( false );
}
/*!
*/
bool PyViewer_ViewWindow::whetherSave()
{
- if ( myTextEditor->document()->isModified() )
+ if ( myEditor->isModified() )
{
QMessageBox::StandardButton answer = QMessageBox::warning( this,
tr( "NAME_PYEDITOR" ),
QTextStream anInput( &aFile );
QApplication::setOverrideCursor( Qt::WaitCursor );
- myTextEditor->setPlainText( anInput.readAll() );
+ myEditor->setText( anInput.readAll() );
QApplication::restoreOverrideCursor();
setCurrentFile( filePath );
QTextStream anOutput( &aFile );
QApplication::setOverrideCursor( Qt::WaitCursor );
- anOutput << myTextEditor->toPlainText();
+ anOutput << myEditor->text();
QApplication::restoreOverrideCursor();
setCurrentFile( filePath );
#include <SUIT_ViewWindow.h>
-class PyEditor_Editor;
+class PyEditor_Widget;
class PYVIEWER_EXPORT PyViewer_ViewWindow : public SUIT_ViewWindow
{
public:
enum { NewId, OpenId, SaveId, SaveAsId,
UndoId, RedoId, CutId, CopyId, PasteId, DeleteId, SelectAllId,
+ FindId, ReplaceId,
PreferencesId, HelpId };
PyViewer_ViewWindow( SUIT_Desktop* = 0 );
QString defaultName() const;
private:
- PyEditor_Editor* myTextEditor;
+ PyEditor_Widget* myEditor;
QString myURL;
};
<source>ICON_SELECT_ALL</source>
<translation>py_select_all.png</translation>
</message>
+ <message>
+ <source>ICON_FIND</source>
+ <translation>py_find.png</translation>
+ </message>
+ <message>
+ <source>ICON_REPLACE</source>
+ <translation>py_replace.png</translation>
+ </message>
<message>
<source>ICON_PREFERENCES</source>
<translation>py_preferences.png</translation>
<source>DSC_SELECT_ALL</source>
<translation>Select all the contents</translation>
</message>
+ <message>
+ <source>ACT_FIND</source>
+ <translation>Find</translation>
+ </message>
+ <message>
+ <source>TTP_FIND</source>
+ <translation>Find</translation>
+ </message>
+ <message>
+ <source>DSC_FIND</source>
+ <translation>Find text</translation>
+ </message>
+ <message>
+ <source>ACT_REPLACE</source>
+ <translation>Replace</translation>
+ </message>
+ <message>
+ <source>TTP_REPLACE</source>
+ <translation>Find & Replace</translation>
+ </message>
+ <message>
+ <source>DSC_REPLACE</source>
+ <translation>Find and replace text</translation>
+ </message>
<message>
<source>ACT_PREFERENCES</source>
<translation>Pre&ferences</translation>
<source>DSC_SELECT_ALL</source>
<translation>Sélectionne tout le contenu</translation>
</message>
+ <message>
+ <source>ACT_FIND</source>
+ <translation type="unfinished">Find</translation>
+ </message>
+ <message>
+ <source>TTP_FIND</source>
+ <translation type="unfinished">Find</translation>
+ </message>
+ <message>
+ <source>DSC_FIND</source>
+ <translation type="unfinished">Find text</translation>
+ </message>
+ <message>
+ <source>ACT_REPLACE</source>
+ <translation type="unfinished">Replace</translation>
+ </message>
+ <message>
+ <source>TTP_REPLACE</source>
+ <translation type="unfinished">Find & Replace</translation>
+ </message>
+ <message>
+ <source>DSC_REPLACE</source>
+ <translation type="unfinished">Find and replace text</translation>
+ </message>
<message>
<source>ACT_PREFERENCES</source>
<translation>Préférences</translation>
<source>DSC_SELECT_ALL</source>
<translation>全選択</translation>
</message>
+ <message>
+ <source>ACT_FIND</source>
+ <translation type="unfinished">Find</translation>
+ </message>
+ <message>
+ <source>TTP_FIND</source>
+ <translation type="unfinished">Find</translation>
+ </message>
+ <message>
+ <source>DSC_FIND</source>
+ <translation type="unfinished">Find text</translation>
+ </message>
+ <message>
+ <source>ACT_REPLACE</source>
+ <translation type="unfinished">Replace</translation>
+ </message>
+ <message>
+ <source>TTP_REPLACE</source>
+ <translation type="unfinished">Find & Replace</translation>
+ </message>
+ <message>
+ <source>DSC_REPLACE</source>
+ <translation type="unfinished">Find and replace text</translation>
+ </message>
<message>
<source>ACT_PREFERENCES</source>
<translation>環境設定 (&f)</translation>
#include <QApplication>
#include <QPaintEvent>
#include <QCoreApplication>
+#include <QVBoxLayout>
#include <utilities.h>
+
namespace
{
/*!
ProcessVoidEvent( new TEvent( mySelMgr ) );
}
+/*!
+ \class UserDefinedContent
+ \brief The class represents base class for user defined widget that
+ can be inserted to the Preferences dialog.
+*/
+
+/*!
+ \brief Constructor
+*/
+UserDefinedContent::UserDefinedContent()
+ : QWidget()
+{
+}
+
+/*!
+ \brief Called from Preferences dialog to store settings to the resource file.
+*/
+void UserDefinedContent::store()
+{
+}
+
+/*!
+ \brief Called from Preferences dialog to restore settings from the resource file.
+*/
+void UserDefinedContent::retrieve()
+{
+}
+
+/*!
+ \class SgPyQtUserDefinedContent
+ \brief A Wrapper for UserDefinedContent class.
+ \internal
+*/
+class SgPyQtUserDefinedContent: public QtxUserDefinedContent
+{
+public:
+ SgPyQtUserDefinedContent(UserDefinedContent*);
+ virtual ~SgPyQtUserDefinedContent();
+
+ void store( QtxResourceMgr*, QtxPreferenceMgr* );
+ void retrieve( QtxResourceMgr*, QtxPreferenceMgr* );
+
+private:
+ UserDefinedContent* myContent;
+};
+
+/*!
+ \brief Create custom item for Preferences dialog wrapping widget passed from Python.
+ \internal
+*/
+SgPyQtUserDefinedContent::SgPyQtUserDefinedContent(UserDefinedContent* content)
+ : QtxUserDefinedContent( 0 ), myContent( content )
+{
+ QVBoxLayout* l = new QVBoxLayout( this );
+ l->setContentsMargins( 0, 0, 0, 0 );
+ l->addWidget( myContent );
+}
+
+/*!
+ \brief Destructor.
+ \internal
+*/
+SgPyQtUserDefinedContent::~SgPyQtUserDefinedContent()
+{
+}
+
+/*!
+ \brief Called from Preferences dialog to store settings to the resource file.
+ \internal
+*/
+void SgPyQtUserDefinedContent::store( QtxResourceMgr*, QtxPreferenceMgr* )
+{
+ myContent->store();
+}
+
+/*!
+ \brief Called from Preferences dialog to restore settings from the resource file.
+ \internal
+*/
+void SgPyQtUserDefinedContent::retrieve( QtxResourceMgr*, QtxPreferenceMgr* )
+{
+ myContent->retrieve();
+}
+
/*!
\class SalomePyQt
\brief The class provides utility functions which can be used in the Python
ProcessVoidEvent( new TEvent( section, name, value ) );
}
+/*!
+ \brief Add font setting to the application preferences.
+ \param section resources file section name
+ \param name setting name
+ \param value new setting value
+*/
+void SalomePyQt::addSetting( const QString& section, const QString& name, const QFont& value )
+{
+ class TEvent: public SALOME_Event
+ {
+ QString mySection;
+ QString myName;
+ QFont myValue;
+ public:
+ TEvent( const QString& section, const QString& name, const QFont& value )
+ : mySection( section ), myName( name ), myValue( value ) {}
+ virtual void Execute()
+ {
+ if ( SUIT_Session::session() ) {
+ SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+ if ( !mySection.isEmpty() && !myName.isEmpty() )
+ resMgr->setValue( mySection, myName, myValue );
+ }
+ }
+ };
+ ProcessVoidEvent( new TEvent( section, name, value ) );
+}
+
/*!
\fn int SalomePyQt::integerSetting( const QString& section,
const QString& name,
/*!
\fn QByteArray SalomePyQt::byteArraySetting( const QString& section,
const QString& name,
- const QByteArray def );
+ const QByteArray& def );
\brief Get byte array setting from the application preferences.
\param section resources file section name
\param name setting name
return ProcessEvent( new TGetByteArraySettingEvent( section, name, def ) );
}
+/*!
+ \fn QByteArray SalomePyQt::fontSetting( const QString& section,
+ const QString& name,
+ const QFont& def );
+ \brief Get font setting from the application preferences.
+ \param section resources file section name
+ \param name setting name
+ \param def default value which is returned if the setting is not found
+ \return setting value
+*/
+
+class TGetFontSettingEvent: public SALOME_Event
+{
+public:
+ typedef QFont TResult;
+ TResult myResult;
+ QString mySection;
+ QString myName;
+ TResult myDefault;
+ TGetFontSettingEvent( const QString& section, const QString& name, const QFont& def )
+ : mySection( section ), myName( name ), myDefault( def ) {}
+ virtual void Execute()
+ {
+ if ( SUIT_Session::session() ) {
+ SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+ myResult = ( !mySection.isEmpty() && !myName.isEmpty() ) ? resMgr->fontValue( mySection, myName, myDefault ) : myDefault;
+ }
+ }
+};
+QFont SalomePyQt::fontSetting ( const QString& section, const QString& name, const QFont& def )
+{
+ return ProcessEvent( new TGetFontSettingEvent( section, name, def ) );
+}
+
/*!
\brief Remove setting from the application preferences.
\param section resources file section name
}
}
};
- ProcessVoidEvent( new TEvent( id, prop, var) );
+ ProcessVoidEvent( new TEvent( id, prop, var ) );
+}
+
+/*!
+ \brief Set specific widget as a custom preferences item.
+ \param id preferences identifier
+ \param prop preferences property name
+ \param widget custom widget
+*/
+void SalomePyQt::setPreferencePropertyWg( const int id,
+ const QString& prop,
+ UserDefinedContent* widget )
+{
+ class TEvent: public SALOME_Event
+ {
+ int myId;
+ QString myProp;
+ UserDefinedContent* myWidget;
+ public:
+ TEvent( const int id, const QString& prop, UserDefinedContent* widget )
+ : myId( id ), myProp( prop ), myWidget( widget ) {}
+ virtual void Execute()
+ {
+ LightApp_Module* module = getActiveModule();
+ if ( module ) {
+ LightApp_Preferences* pref = module->getApp()->preferences();
+ if ( pref ) {
+ pref->setItemProperty( myProp, (qint64) new SgPyQtUserDefinedContent( myWidget ), myId );
+ }
+ }
+ }
+ };
+ ProcessVoidEvent( new TEvent( id, prop, widget ) );
}
/*!
PT_Font = LightApp_Preferences::Font,
PT_DirList = LightApp_Preferences::DirList,
PT_File = LightApp_Preferences::File,
+ PT_Slider = LightApp_Preferences::Slider,
+ PT_Shortcut = LightApp_Preferences::Shortcut,
+ PT_ShortcutTree = LightApp_Preferences::ShortcutTree,
+ PT_BiColor = LightApp_Preferences::BiColor,
+ PT_Background = LightApp_Preferences::Background,
+ PT_UserDefined = LightApp_Preferences::UserDefined,
+};
+
+class UserDefinedContent: public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit UserDefinedContent();
+
+ virtual void store();
+ virtual void retrieve();
};
//! Orientation
static void addSetting ( const QString&, const QString&, const QString& );
static void addSetting ( const QString&, const QString&, const QColor& );
static void addSetting ( const QString&, const QString&, const QByteArray& );
+ static void addSetting ( const QString&, const QString&, const QFont& );
static int integerSetting( const QString&, const QString&, const int = 0 );
static double doubleSetting ( const QString&, const QString&, const double = 0 );
static bool boolSetting ( const QString&, const QString&, const bool = 0 );
static QString stringSetting ( const QString&, const QString&, const QString& = QString(""), const bool = true );
static QColor colorSetting ( const QString&, const QString&, const QColor& = QColor() );
static QByteArray byteArraySetting( const QString&, const QString&, const QByteArray& = QByteArray() );
+ static QFont fontSetting( const QString&, const QString&, const QFont& = QFont() );
static void removeSetting ( const QString&, const QString& );
static bool hasSetting ( const QString&, const QString& );
static QStringList parameters ( const QString& );
const QString& = QString() );
static QVariant preferenceProperty( const int, const QString& );
static void setPreferenceProperty( const int, const QString&, const QVariant& );
+ static void setPreferencePropertyWg( const int, const QString&, UserDefinedContent* );
static void addPreferenceProperty( const int, const QString&, const int, const QVariant& );
static void message( const QString&, bool = true );
PT_Font,
PT_DirList,
PT_File,
+ PT_Slider,
+ PT_Shortcut,
+ PT_ShortcutTree,
+ PT_BiColor,
+ PT_Background,
+ PT_UserDefined
};
enum Orientation {
QtxTreeView( const QtxTreeView& );
};
+class UserDefinedContent : public QWidget
+{
+%TypeHeaderCode
+#include <SalomePyQt.h>
+%End
+
+%ConvertToSubClassCode
+ if ( qobject_cast<UserDefinedContent*>( sipCpp ) )
+ sipClass = sipClass_UserDefinedContent;
+ else
+ sipClass = NULL;
+%End
+
+public:
+ explicit UserDefinedContent();
+
+ virtual void store();
+ virtual void retrieve();
+};
+
enum VisibilityState
{
ShownState,
static void addSetting ( const QString&, const QString&, const QString& ) /ReleaseGIL/ ;
static void addSetting ( const QString&, const QString&, const QColor& ) /ReleaseGIL/ ;
static void addSetting ( const QString&, const QString&, const QByteArray& ) /ReleaseGIL/ ;
+ static void addSetting ( const QString&, const QString&, const QFont& ) /ReleaseGIL/ ;
static int integerSetting( const QString&, const QString&, const int = 0 ) /ReleaseGIL/ ;
static double doubleSetting ( const QString&, const QString&, const double = 0 ) /ReleaseGIL/ ;
static bool boolSetting ( const QString&, const QString&, const bool = false ) /ReleaseGIL/ ;
static QString stringSetting ( const QString&, const QString&, const QString& = QString(""), const bool = true ) /ReleaseGIL/ ;
static QColor colorSetting ( const QString&, const QString&, const QColor& = QColor() ) /ReleaseGIL/ ;
static QByteArray byteArraySetting( const QString&, const QString&, const QByteArray& = QByteArray() ) /ReleaseGIL/ ;
+ static QFont fontSetting( const QString&, const QString&, const QFont& = QFont() ) /ReleaseGIL/ ;
static void removeSetting ( const QString&, const QString& ) /ReleaseGIL/ ;
static bool hasSetting ( const QString&, const QString& ) /ReleaseGIL/ ;
static QStringList parameters ( const QString& ) /ReleaseGIL/ ;
static void setPreferenceProperty( const int,
const QString&,
const QVariant& ) /ReleaseGIL/ ;
+ static void setPreferencePropertyWg( const int,
+ const QString&,
+ UserDefinedContent* ) /ReleaseGIL/ ;
static void addPreferenceProperty( const int,
const QString&,
const int,
if ( desktop() && desktop()->toolMgr() )
desktop()->toolMgr()->registerAction( a );
- if ( desktop() )
+ if ( desktop() && a->shortcutContext() != Qt::WidgetShortcut &&
+ a->shortcutContext() != Qt::WidgetWithChildrenShortcut )
desktop()->addAction( a );
return ident;
//! Properties
createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(),
tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ),
- Qt::CTRL+Qt::Key_P, desk, false, this, SLOT( onProperties() ) );
+ 0, desk, false, this, SLOT( onProperties() ) );
//! Catalog Generator
createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(),
# header files / to be processed by moc
SET(_moc_HEADERS
PyEditor_Editor.h
+ PyEditor_FindTool.h
PyEditor_LineNumberArea.h
+ PyEditor_Keywords.h
+ PyEditor_Completer.h
PyEditor_PyHighlighter.h
PyEditor_SettingsDlg.h
+ PyEditor_Widget.h
PyEditor_Window.h
)
# sources / static
SET(_other_SOURCES
PyEditor_Editor.cxx
+ PyEditor_FindTool.cxx
PyEditor_LineNumberArea.cxx
+ PyEditor_Keywords.cxx
+ PyEditor_Completer.cxx
PyEditor_PyHighlighter.cxx
PyEditor_Settings.cxx
PyEditor_SettingsDlg.cxx
PyEditor_StdSettings.cxx
+ PyEditor_Widget.cxx
PyEditor_Window.cxx
)
#include "PyEditor_StdSettings.h"
#include <QApplication>
+#include <QCommandLineParser>
#include <QDir>
#include <QLibraryInfo>
#include <QLocale>
app.setOrganizationDomain( "www.salome-platform.org" );
app.setApplicationName( "pyeditor" );
+ QLocale locale;
+
PyEditor_StdSettings* settings = new PyEditor_StdSettings();
PyEditor_Settings::setSettings( settings );
- QString language = settings->language();
-
// Load Qt translations.
QString qtDirTrSet = QLibraryInfo::location( QLibraryInfo::TranslationsPath );
QString qtDirTrEnv = qtTrDir();
foreach( QString qtTrFile, qtTrFiles ) {
foreach ( QString qtTrDir, qtTrDirs ) {
QTranslator* translator = new QTranslator;
- if ( translator->load( QString("%1_%2").arg( qtTrFile ).arg( language ), qtTrDir ) ) {
+ if ( translator->load( locale, QString("%1").arg( qtTrFile ), "_", qtTrDir ) ) {
+ app.installTranslator( translator );
+ break;
+ }
+ else if ( translator->load( QString("%1_en").arg( qtTrFile ), qtTrDir ) ) {
app.installTranslator( translator );
break;
}
// Load application's translations.
QTranslator translator;
- if ( translator.load( QString( "PyEditor_msg_%1" ).arg( language ), resourceDir() ) )
+ if ( translator.load( locale, QString( "PyEditor_msg" ), "_", resourceDir() ) )
app.installTranslator( &translator );
-
+ else if ( translator.load( QString( "PyEditor_msg_en" ), resourceDir() ) )
+ app.installTranslator( &translator );
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription( QApplication::translate( "PyEditor", "PROGRAM_DESCRIPTION" ) );
+ parser.addHelpOption();
+ parser.addPositionalArgument( QApplication::translate( "PyEditor", "FILE_PARAM_NAME" ),
+ QApplication::translate( "PyEditor", "FILE_PARAM_DESCRIPTION" ) );
+
+ parser.process( app );
+ const QStringList args = parser.positionalArguments();
+
PyEditor_Window window;
window.setWindowIcon( QIcon( ":/images/py_editor.png" ) );
window.resize( 650, 700 );
window.show();
+
+ if ( args.count() > 0 )
+ window.loadFile( args[0], false );
return app.exec();
}
--- /dev/null
+// Copyright (C) 2015-2016 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_Completer.cxx
+// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
+//
+
+#include "PyEditor_Completer.h"
+
+#include "PyEditor_Editor.h"
+#include "PyEditor_Keywords.h"
+
+#include <QSet>
+#include <QTimer>
+#include <QTextBlock>
+#include <QTextCursor>
+#include <QApplication>
+#include <QAbstractItemView>
+#include <QStandardItemModel>
+
+/*!
+ \brief Constructor.
+*/
+PyEditor_Completer::PyEditor_Completer( PyEditor_Editor* editor,
+ PyEditor_Keywords* std, PyEditor_Keywords* user )
+ : QCompleter( editor ),
+ myEditor( editor ),
+ myTimer( 0 ),
+ myStdKeywords( std ),
+ myUserKeywords( user )
+{
+ setWidget( editor );
+ setCompletionMode(QCompleter::PopupCompletion);
+
+ connect(this, SIGNAL( activated( const QString& ) ),
+ this, SLOT( onActivated( const QString& ) ) );
+ connect(editor, SIGNAL( textChanged() ), this, SLOT( onTextChanged() ) );
+ connect(myStdKeywords, SIGNAL( keywordsChanged() ),
+ this, SLOT( onKeywordsChanged() ) );
+ connect(myUserKeywords, SIGNAL( keywordsChanged() ),
+ this, SLOT( onKeywordsChanged() ) );
+
+ updateKeywords();
+}
+
+/*!
+ \brief Destructor.
+*/
+PyEditor_Completer::~PyEditor_Completer()
+{
+}
+
+/*!
+ \brief Perform the completion if it possible.
+*/
+void PyEditor_Completer::perform()
+{
+ QString prefix = completionText();
+ setCompletionPrefix( prefix );
+
+ if ( !completionPrefix().isEmpty() &&
+ ( completionCount() > 1 || ( completionCount() == 1 &&
+ currentCompletion() != completionPrefix() ) ) )
+ complete(completionRect());
+ else
+ uncomplete();
+}
+
+/*!
+ \brief Hide the completer's popup menu.
+*/
+void PyEditor_Completer::uncomplete()
+{
+ if ( popup() )
+ popup()->hide();
+}
+
+/*!
+ \brief Handling 'Enter' key.
+*/
+bool PyEditor_Completer::eventFilter(QObject* o, QEvent* e)
+{
+ bool res = false;
+ if ( e->type() == QEvent::KeyPress && popup()->isVisible() ) {
+ QKeyEvent* ke = (QKeyEvent*)e;
+ if ( ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) {
+ res = true;
+ setCurrentRow(popup()->currentIndex().row());
+ onActivated(currentCompletion());
+ }
+ }
+
+ if ( !res )
+ res = QCompleter::eventFilter(o, e);
+
+ return res;
+}
+
+/*!
+ \brief Perform delayed completion.
+*/
+void PyEditor_Completer::onTimeout()
+{
+ perform();
+}
+
+/*!
+ \brief Invoked when text changed in editor.
+*/
+void PyEditor_Completer::onTextChanged()
+{
+ uncomplete();
+ if ( myEditor->completionPolicy() == PyEditor_Editor::Auto ||
+ myEditor->completionPolicy() == PyEditor_Editor::Always )
+ triggerComplete();
+}
+
+/*!
+ \brief Invoked when keywords changed in editor.
+*/
+void PyEditor_Completer::onKeywordsChanged()
+{
+ updateKeywords();
+}
+
+/*!
+ \brief Insert selected completion into editor.
+*/
+void PyEditor_Completer::onActivated( const QString& text)
+{
+ QPoint rng = completionRange();
+ QTextCursor cursor = myEditor->textCursor();
+ cursor.setPosition(cursor.position() - rng.y() + rng.x() - 1,
+ QTextCursor::KeepAnchor);
+ cursor.insertText(text);
+ uncomplete();
+}
+
+/*!
+ \brief Get the rectangle for completion popup.
+ \return completion popup rect
+*/
+QRect PyEditor_Completer::completionRect() const
+{
+ QRect res = myEditor->cursorRect(myEditor->textCursor());
+ res.setWidth(popup()->sizeHint().width());
+ res.translate(myEditor->document()->documentMargin(),
+ myEditor->document()->documentMargin());
+ return res;
+}
+
+/*!
+ \brief Get the current completion prefix from editor.
+ \return completion prefix string
+*/
+QString PyEditor_Completer::completionText() const
+{
+ QString prefix;
+ if ( myEditor ) {
+ QString txt = myEditor->textCursor().block().text();
+ if ( !txt.isEmpty() ) {
+ QPoint range = completionRange();
+ prefix = txt.mid( range.x(), range.y() - range.x() + 1 );
+ }
+ }
+ return prefix;
+}
+
+/*!
+ \brief Get position of completion prefix in editor.
+ \return begin and end of completion prefix
+*/
+QPoint PyEditor_Completer::completionRange() const
+{
+ QPoint range;
+
+ if ( myEditor ) {
+ QTextCursor cursor = myEditor->textCursor();
+ QString txt = cursor.block().text();
+ int beg = 0;
+ int end = cursor.positionInBlock() - 1;
+
+ QRegExp rx("[A-Za-z]{1}\\w*$");
+ int pos = rx.indexIn(txt.mid(beg, end - beg + 1));
+
+ if ( pos >= 0 )
+ beg = pos;
+
+ range = QPoint(beg, end);
+ }
+
+ return range;
+}
+
+/*!
+ \brief Schedule the delayed completion.
+*/
+void PyEditor_Completer::triggerComplete()
+{
+ if ( !myTimer ) {
+ myTimer = new QTimer( this );
+ myTimer->setSingleShot( true );
+ myTimer->setInterval( 200 );
+
+ connect( myTimer, SIGNAL( timeout() ), this, SLOT( onTimeout() ) );
+ }
+
+ if ( myTimer->isActive() )
+ myTimer->stop();
+ myTimer->start();
+}
+
+/*!
+ \brief Updates the keywords list in completer.
+*/
+void PyEditor_Completer::updateKeywords()
+{
+ QStandardItemModel* model = new QStandardItemModel( this );
+ KeywordMap kwMap = keywords();
+ for ( KeywordMap::const_iterator it = kwMap.begin(); it != kwMap.end(); ++it ) {
+ QStandardItem* item = new QStandardItem( it.key() );
+ if ( it.value().isValid() )
+ item->setForeground( it.value() );
+ model->appendRow( item );
+ }
+ setModel( model );
+}
+
+/*!
+ \brief Gets the keywords list.
+ \return keyword string list
+*/
+PyEditor_Completer::KeywordMap PyEditor_Completer::keywords() const
+{
+ KeywordMap res;
+ QList<PyEditor_Keywords*> kwDicts;
+ kwDicts << myStdKeywords << myUserKeywords;
+
+ for ( QList<PyEditor_Keywords*>::iterator itr = kwDicts.begin(); itr != kwDicts.end(); ++itr ) {
+ PyEditor_Keywords* dict = *itr;
+ QStringList kwList = dict->keywords();
+ for ( QStringList::const_iterator it = kwList.begin(); it != kwList.end(); ++it ) {
+ if ( !res.contains( *it ) ) {
+ res.insert( *it, dict->color( *it ) );
+ }
+ }
+ }
+ return res;
+}
--- /dev/null
+// Copyright (C) 2015-2016 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_Completer.h
+// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
+//
+
+#ifndef PYEDITOR_COMPLETER_H
+#define PYEDITOR_COMPLETER_H
+
+#include <QCompleter>
+
+class QTimer;
+class PyEditor_Editor;
+class PyEditor_Keywords;
+
+class PyEditor_Completer : public QCompleter
+{
+ Q_OBJECT
+
+public:
+ PyEditor_Completer( PyEditor_Editor*,
+ PyEditor_Keywords*, PyEditor_Keywords* );
+ virtual ~PyEditor_Completer();
+
+ void perform();
+
+ void uncomplete();
+
+ virtual bool eventFilter(QObject*, QEvent*);
+
+private Q_SLOTS:
+ void onTimeout();
+ void onTextChanged();
+ void onKeywordsChanged();
+ void onActivated( const QString& );
+
+protected:
+ QRect completionRect() const;
+ QString completionText() const;
+ QPoint completionRange() const;
+
+private:
+ typedef QMap<QString, QColor> KeywordMap;
+
+private:
+ KeywordMap keywords() const;
+ void updateKeywords();
+ void triggerComplete();
+
+private:
+ PyEditor_Editor* myEditor;
+ QTimer* myTimer;
+ PyEditor_Keywords* myStdKeywords;
+ PyEditor_Keywords* myUserKeywords;
+};
+
+#endif
#include "PyEditor_Editor.h"
#include "PyEditor_LineNumberArea.h"
#include "PyEditor_PyHighlighter.h"
+#include "PyEditor_Completer.h"
#include "PyEditor_Settings.h"
+#include "PyEditor_Keywords.h"
+#include <QMenu>
#include <QPainter>
#include <QTextBlock>
+#include <iostream>
+
/*!
\class PyEditor_Editor
\brief Widget to show / edit Python scripts.
\param parent parent widget
*/
PyEditor_Editor::PyEditor_Editor( QWidget* parent )
- : QPlainTextEdit( parent )
+ : QPlainTextEdit( parent ),
+ myCompletionPolicy( Always )
{
+ myStdKeywords = new PyEditor_StandardKeywords( this );
+ myUserKeywords = new PyEditor_Keywords( this );
+ myUserKeywords->append( "print", 0, Qt::red );
+
// Set up line number area
myLineNumberArea = new PyEditor_LineNumberArea( this );
myLineNumberArea->setMouseTracking( true );
// Set up syntax highighter
- mySyntaxHighlighter = new PyEditor_PyHighlighter( this->document() );
+ mySyntaxHighlighter = new PyEditor_PyHighlighter( this->document(),
+ myStdKeywords, myUserKeywords );
// Set-up settings
PyEditor_Settings* settings = PyEditor_Settings::settings();
if ( settings )
setSettings( *settings );
+ myCompleter = new PyEditor_Completer( this, myStdKeywords, myUserKeywords );
+
// Connect signals
connect( this, SIGNAL( blockCountChanged( int ) ), this, SLOT( updateLineNumberAreaWidth( int ) ) );
connect( this, SIGNAL( updateRequest( QRect, int ) ), this, SLOT( updateLineNumberArea( QRect, int ) ) );
// Set size white spaces
setTabStopWidth( mySettings.tabSize() * 10 );
+ // Set completion policy
+ setCompletionPolicy( (CompletionPolicy)mySettings.completionPolicy() );
+
// Update current line highlight
updateHighlightCurrentLine();
viewport()->update();
}
+/*!
+ \brief Gets the current completion policy
+ \return completion policy
+*/
+PyEditor_Editor::CompletionPolicy PyEditor_Editor::completionPolicy() const
+{
+ return myCompletionPolicy;
+}
+
+/*!
+ \brief Sets the current completion policy
+ \param policy completion policy
+*/
+void PyEditor_Editor::setCompletionPolicy( const CompletionPolicy& policy )
+{
+ myCompletionPolicy = policy;
+}
+
+/*!
+ \brief Gets the all user keywords.
+ \param event key press event
+ \return keyword string list
+*/
+QStringList PyEditor_Editor::keywords() const
+{
+ return myUserKeywords->keywords();
+}
+
+/*!
+ \brief Add the user keywords.
+ \param kws keywords string list
+ \param type keywords type
+ \param color keywords color
+*/
+void PyEditor_Editor::appendKeywords( const QStringList& kws, int type, const QColor& color )
+{
+ myUserKeywords->append( kws, type, color );
+}
+
+/*!
+ \brief Remove the user keywords.
+ \param kws keywords string list
+*/
+void PyEditor_Editor::removeKeywords( const QStringList& kws )
+{
+ myUserKeywords->remove( kws );
+}
+
/*!
Delete current selection contents.
*/
aCursor.endEditBlock();
event->accept();
}
+ else if ( aKey == Qt::Key_Space && aCtrl && !aShift &&
+ ( completionPolicy() == Manual || completionPolicy() == Always ) )
+ {
+ myCompleter->perform();
+ event->accept();
+ }
else if ( event == QKeySequence::MoveToStartOfLine || event == QKeySequence::SelectStartOfLine )
{
QTextCursor aCursor = this->textCursor();
}
}
+void PyEditor_Editor::contextMenuEvent( QContextMenuEvent* event )
+{
+ QMenu* menu = createStandardContextMenu();
+ emit customizeMenu( menu );
+ menu->exec( event->globalPos() );
+ delete menu;
+}
+
/*!
\brief Indent and tab text.
\param isShift flag defines reverse tab direction
{
return toPlainText();
}
+
+/*!
+ \brief Get user keywords dictionary.
+ \return keywords dictionary
+*/
+PyEditor_Keywords* PyEditor_Editor::userKeywords() const
+{
+ return myUserKeywords;
+}
+
+/*!
+ \brief Get standard keywords dictionary.
+ \return keywords dictionary
+*/
+PyEditor_Keywords* PyEditor_Editor::standardKeywords() const
+{
+ return myStdKeywords;
+}
#include <QPlainTextEdit>
+class PyEditor_Keywords;
+class PyEditor_Completer;
class PyEditor_PyHighlighter;
+class QMenu;
class PYEDITOR_EXPORT PyEditor_Editor : public QPlainTextEdit
{
Q_OBJECT
+public:
+ typedef enum { None, Auto, Manual, Always } CompletionPolicy;
+
public:
PyEditor_Editor( QWidget* = 0 );
virtual ~PyEditor_Editor();
- void setSettings( const PyEditor_Settings& );
+ void setSettings( const PyEditor_Settings& );
const PyEditor_Settings& settings() const;
QString text() const;
+ QStringList keywords() const;
+ void appendKeywords( const QStringList&, int, const QColor& = QColor() );
+ void removeKeywords( const QStringList& );
+
+ CompletionPolicy completionPolicy() const;
+ void setCompletionPolicy( const CompletionPolicy& );
+
public Q_SLOTS:
void deleteSelected();
void append( const QString& );
virtual void keyPressEvent( QKeyEvent* );
virtual void resizeEvent( QResizeEvent* );
virtual void paintEvent( QPaintEvent* );
-
+ virtual void contextMenuEvent( QContextMenuEvent* );
+
+ PyEditor_Keywords* userKeywords() const;
+ PyEditor_Keywords* standardKeywords() const;
+
private Q_SLOTS:
void updateHighlightCurrentLine();
void matchParentheses();
void updateLineNumberAreaWidth( int );
void updateLineNumberArea( const QRect&, int );
+Q_SIGNALS:
+ void customizeMenu( QMenu* );
+
private:
bool matchLeftParenthesis( const QTextBlock&, int, int );
bool matchRightParenthesis( const QTextBlock&, int, int );
int lineIndent();
void tabIndentation( bool );
void indentSelection( bool );
-
+
int findFirstNonSpace( const QString& );
-
+
QWidget* myLineNumberArea;
PyEditor_PyHighlighter* mySyntaxHighlighter;
+ PyEditor_Completer* myCompleter;
PyEditor_Settings mySettings;
+ PyEditor_Keywords* myStdKeywords;
+ PyEditor_Keywords* myUserKeywords;
+
+ CompletionPolicy myCompletionPolicy;
+
friend class PyEditor_LineNumberArea;
};
--- /dev/null
+// Copyright (C) 2015-2016 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_FindTool.cxx
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
+//
+
+#include "PyEditor_FindTool.h"
+#include "PyEditor_Editor.h"
+
+#include <QAction>
+#include <QCompleter>
+#include <QEvent>
+#include <QGridLayout>
+#include <QIcon>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMenu>
+#include <QSignalMapper>
+#include <QToolButton>
+
+/*!
+ \class PyEditor_FindTool
+ \brief Find / Replace widget for PyEditor
+*/
+
+/*!
+ \brief Constructor.
+ \param editor Python editor widget.
+ \param parent Parent widget.
+*/
+PyEditor_FindTool::PyEditor_FindTool( PyEditor_Editor* editor, QWidget* parent )
+ : QWidget( parent ), myEditor( editor )
+{
+ QLabel* findLabel = new QLabel( tr( "FIND_LABEL" ), this );
+ myFindEdit = new QLineEdit( this );
+ myFindEdit->setClearButtonEnabled( true );
+ myFindEdit->installEventFilter( this );
+ connect( myFindEdit, SIGNAL( textChanged( const QString& ) ), this, SLOT( find( const QString& ) ) );
+ connect( myFindEdit, SIGNAL( returnPressed() ), this, SLOT( findNext() ) );
+ myFindEdit->setCompleter( new QCompleter( myFindEdit ) );
+ myFindEdit->completer()->setModel( &myFindCompletion );
+
+ QLabel* replaceLabel = new QLabel( tr( "REPLACE_LABEL" ), this );
+ myReplaceEdit = new QLineEdit( this );
+ myReplaceEdit->setClearButtonEnabled( true );
+ myReplaceEdit->installEventFilter( this );
+ myReplaceEdit->setCompleter( new QCompleter( myReplaceEdit ) );
+ myReplaceEdit->completer()->setModel( &myReplaceCompletion );
+
+ myInfoLabel = new QLabel( this );
+ myInfoLabel->setAlignment( Qt::AlignVCenter | Qt::AlignRight );
+
+ QToolButton* prevBtn = new QToolButton( this );
+ prevBtn->setIcon( QIcon( ":images/py_find_previous.png" ) );
+ prevBtn->setAutoRaise( true );
+ connect( prevBtn, SIGNAL( clicked() ), this, SLOT( findPrevious() ) );
+
+ QToolButton* nextBtn = new QToolButton( this );
+ nextBtn->setIcon( QIcon( ":images/py_find_next.png" ) );
+ nextBtn->setAutoRaise( true );
+ connect( nextBtn, SIGNAL( clicked() ), this, SLOT( findNext() ) );
+
+ QToolButton* replaceBtn = new QToolButton();
+ replaceBtn->setText( tr( "REPLACE_BTN" ) );
+ replaceBtn->setAutoRaise( true );
+ connect( replaceBtn, SIGNAL( clicked() ), this, SLOT( replace() ) );
+
+ QToolButton* replaceAllBtn = new QToolButton();
+ replaceAllBtn->setText( tr( "REPLACE_ALL_BTN" ) );
+ replaceAllBtn->setAutoRaise( true );
+ connect( replaceAllBtn, SIGNAL( clicked() ), this, SLOT( replaceAll() ) );
+
+ QHBoxLayout* hl = new QHBoxLayout;
+ hl->setContentsMargins( 0, 0, 0, 0 );
+ hl->setSpacing( 0 );
+ hl->addWidget( prevBtn );
+ hl->addWidget( nextBtn );
+
+ QGridLayout* l = new QGridLayout( this );
+ l->setContentsMargins( 6, 2, 6, 2 );
+ l->setSpacing( 2 );
+ l->addWidget( findLabel, 0, 0 );
+ l->addWidget( myFindEdit, 0, 1 );
+ l->addLayout( hl, 0, 2 );
+ l->addWidget( myInfoLabel, 0, 3 );
+ l->addWidget( replaceLabel, 1, 0 );
+ l->addWidget( myReplaceEdit, 1, 1 );
+ l->addWidget( replaceBtn, 1, 2 );
+ l->addWidget( replaceAllBtn, 1, 3 );
+
+ QAction* menuAction = myFindEdit->addAction( QIcon(":images/py_search.png"), QLineEdit::LeadingPosition );
+ connect( menuAction, SIGNAL( triggered( bool ) ), this, SLOT( showMenu() ) );
+
+ addAction( new QAction( tr( "CASE_SENSITIVE_CHECK" ), this ) );
+ addAction( new QAction( tr( "WHOLE_WORDS_CHECK" ), this ) );
+ addAction( new QAction( tr( "REGEX_CHECK" ), this ) );
+ addAction( new QAction( QIcon( ":/images/py_find.png" ), tr( "Find" ), this ) );
+ addAction( new QAction( tr( "FindPrevious" ), this ) );
+ addAction( new QAction( tr( "FindNext" ), this ) );
+ addAction( new QAction( QIcon( ":/images/py_replace.png" ), tr( "Replace" ), this ) );
+
+ foreach ( QAction* action, actions().mid( CaseSensitive, RegExp+1 ) )
+ {
+ action->setCheckable( true );
+ connect( action, SIGNAL( toggled( bool ) ), this, SLOT( update() ) );
+ }
+
+ QSignalMapper* mapper = new QSignalMapper( this );
+ connect( mapper, SIGNAL( mapped( int ) ), this, SLOT( activate( int ) ) );
+
+ for ( int i = Find; i < actions().count(); i++ )
+ {
+ QAction* action = actions()[i];
+ action->setShortcuts( shortcuts( i ) );
+ action->setShortcutContext( Qt::WidgetWithChildrenShortcut );
+ connect( action, SIGNAL( triggered( bool ) ), mapper, SLOT( map() ) );
+ mapper->setMapping( action, i );
+ myEditor->addAction( action );
+ }
+
+ myEditor->installEventFilter( this );
+ connect( myEditor, SIGNAL( customizeMenu( QMenu* ) ), this, SLOT( customizeMenu( QMenu* ) ) );
+
+ hide();
+}
+
+/*!
+ \brief Process events for this widget,
+ \param e Event being processed.
+ \return true if event's processing should be stopped; false otherwise.
+*/
+bool PyEditor_FindTool::event( QEvent* e )
+{
+ if ( e->type() == QEvent::EnabledChange )
+ {
+ updateShortcuts();
+ }
+ else if ( e->type() == QEvent::KeyPress )
+ {
+ QKeyEvent* ke = (QKeyEvent*)e;
+ switch ( ke->key() )
+ {
+ case Qt::Key_Escape:
+ hide();
+ return true;
+ default:
+ break;
+ }
+ }
+ else if ( e->type() == QEvent::Hide )
+ {
+ addCompletion( myFindEdit->text(), false );
+ addCompletion( myReplaceEdit->text(), true );
+ myEditor->setFocus();
+ }
+ return QWidget::event( e );
+}
+
+/*!
+ \brief Filter events from watched objects.
+ \param o Object being watched.
+ \param e Event being processed.
+ \return true if event should be filtered out; false otherwise.
+*/
+bool PyEditor_FindTool::eventFilter( QObject* o, QEvent* e )
+{
+ if ( o == myFindEdit )
+ {
+ if ( e->type() == QEvent::KeyPress )
+ {
+ QKeyEvent* keyEvent = (QKeyEvent*)e;
+ if ( keyEvent->key() == Qt::Key_Escape && !myFindEdit->text().isEmpty() )
+ {
+ addCompletion( myFindEdit->text(), false );
+ myFindEdit->clear();
+ return true;
+ }
+ }
+ }
+ else if ( o == myReplaceEdit )
+ {
+ if ( e->type() == QEvent::KeyPress )
+ {
+ QKeyEvent* keyEvent = (QKeyEvent*)e;
+ if ( keyEvent->key() == Qt::Key_Escape && !myReplaceEdit->text().isEmpty() )
+ {
+ myReplaceEdit->clear();
+ return true;
+ }
+ }
+ }
+ else if ( o == myEditor )
+ {
+ if ( e->type() == QEvent::EnabledChange )
+ {
+ setEnabled( myEditor->isEnabled() );
+ }
+ else if ( e->type() == QEvent::Hide )
+ {
+ hide();
+ }
+ else if ( e->type() == QEvent::KeyPress )
+ {
+ QKeyEvent* ke = (QKeyEvent*)e;
+ switch ( ke->key() )
+ {
+ case Qt::Key_Escape:
+ if ( isVisible() )
+ {
+ hide();
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return QWidget::eventFilter( o, e );
+}
+
+/*!
+ \brief Slot: activate 'Find' dialog.
+*/
+void PyEditor_FindTool::activateFind()
+{
+ activate( Find );
+}
+
+/*!
+ \brief Customize menu for editor.
+*/
+void PyEditor_FindTool::customizeMenu( QMenu* menu )
+{
+ menu->addSeparator();
+ menu->addAction( actions()[Find] );
+ menu->addAction( actions()[Replace] );
+}
+
+/*!
+ \brief Slot: activate 'Replace' dialog.
+*/
+void PyEditor_FindTool::activateReplace()
+{
+ activate( Replace );
+}
+
+/*!
+ \brief Slot: show context menu with search options.
+ \internal
+*/
+void PyEditor_FindTool::showMenu()
+{
+ QMenu::exec( actions().mid( CaseSensitive, RegExp+1 ), QCursor::pos() );
+}
+
+/*!
+ \brief Slot: find text being typed in the 'Find' control.
+ \param text Text entered by the user.
+ \internal
+*/
+void PyEditor_FindTool::find( const QString& text )
+{
+ find( text, 0 );
+}
+
+/*!
+ \brief Slot: find text entered in the 'Find' control.
+ \internal
+ \overload
+*/
+void PyEditor_FindTool::find()
+{
+ find( myFindEdit->text(), 0 );
+}
+
+/*!
+ \brief Slot: find previous matched item; called when user presses 'Previous' button.
+ \internal
+*/
+void PyEditor_FindTool::findPrevious()
+{
+ find( myFindEdit->text(), -1 );
+}
+
+/*!
+ \brief Slot: find next matched item; called when user presses 'Next' button.
+ \internal
+*/
+void PyEditor_FindTool::findNext()
+{
+ find( myFindEdit->text(), 1 );
+}
+
+/*!
+ \brief Slot: replace currently selected match; called when user presses 'Replace' button.
+ \internal
+*/
+void PyEditor_FindTool::replace()
+{
+ QString text = myFindEdit->text();
+ QString replacement = myReplaceEdit->text();
+
+ QTextCursor editor = myEditor->textCursor();
+ if ( editor.hasSelection() && editor.selectedText() == text )
+ {
+ editor.beginEditBlock();
+ editor.removeSelectedText();
+ editor.insertText( replacement );
+ editor.endEditBlock();
+ find();
+ }
+}
+
+/*!
+ \brief Slot: replace all matches; called when user presses 'Replace All' button.
+ \internal
+*/
+void PyEditor_FindTool::replaceAll()
+{
+ QString text = myFindEdit->text();
+ QString replacement = myReplaceEdit->text();
+ QList<QTextCursor> results = matches( text );
+ if ( !results.isEmpty() )
+ {
+ QTextCursor editor( myEditor->document() );
+ editor.beginEditBlock();
+ foreach ( QTextCursor cursor, results )
+ {
+ editor.setPosition( cursor.anchor() );
+ editor.setPosition( cursor.position(), QTextCursor::KeepAnchor );
+ editor.removeSelectedText();
+ editor.insertText( replacement );
+ }
+ editor.endEditBlock();
+ find();
+ }
+}
+
+/*!
+ \brief Slot: restart search; called when search options are changed.
+ \internal
+*/
+void PyEditor_FindTool::update()
+{
+ find();
+}
+
+/*!
+ \brief Slot: activate action; called when user types corresponding shortcut.
+ \param action Action being activated.
+ \internal
+*/
+void PyEditor_FindTool::activate( int action )
+{
+ QTextCursor cursor = myEditor->textCursor();
+ cursor.movePosition( QTextCursor::StartOfWord );
+ cursor.movePosition( QTextCursor::EndOfWord, QTextCursor::KeepAnchor );
+ QString word = cursor.selectedText();
+
+ switch ( action )
+ {
+ case Find:
+ case Replace:
+ showReplaceControls( action == Replace );
+ show();
+ if ( !word.isEmpty() ) {
+ myFindEdit->setText( word );
+ myEditor->setTextCursor( cursor );
+ }
+ myFindEdit->setFocus();
+ myFindEdit->selectAll();
+ find( myFindEdit->text() );
+ break;
+ case FindPrevious:
+ findPrevious();
+ break;
+ case FindNext:
+ findNext();
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ \brief Get shortcuts for given action.
+ \param action Editor's action.
+ \return List of shortcuts.
+ \internal
+*/
+QList<QKeySequence> PyEditor_FindTool::shortcuts( int action ) const
+{
+ QList<QKeySequence> bindings;
+ switch ( action )
+ {
+ case Find:
+ bindings << QKeySequence( QKeySequence::Find );
+ break;
+ case FindPrevious:
+ bindings << QKeySequence( QKeySequence::FindPrevious );
+ break;
+ case FindNext:
+ bindings << QKeySequence( QKeySequence::FindNext );
+ break;
+ case Replace:
+ bindings << QKeySequence( "Ctrl+H" );
+ bindings << QKeySequence( QKeySequence::Replace );
+ break;
+ default:
+ break;
+ }
+ return bindings;
+}
+
+/*!
+ \brief Update shortcuts when widget is enabled / disabled.
+ \internal
+*/
+void PyEditor_FindTool::updateShortcuts()
+{
+ foreach ( QAction* action, actions().mid( Find ) )
+ {
+ action->setEnabled( isEnabled() && myEditor->isEnabled() );
+ }
+}
+
+/*!
+ \brief Show / hide 'Replace' controls.
+ \param on Visibility flag.
+ \internal
+*/
+void PyEditor_FindTool::showReplaceControls( bool on )
+{
+ QGridLayout* l = qobject_cast<QGridLayout*>( layout() );
+ for ( int j = 0; j < l->columnCount(); j++ )
+ {
+ if ( l->itemAtPosition( 1, j )->widget() )
+ l->itemAtPosition( 1, j )->widget()->setVisible( on );
+ }
+}
+
+/*!
+ \brief Set palette for 'Find' tool depending on results of search.
+ \param found Search result: true in case of success; false otherwise.
+ \internal
+*/
+void PyEditor_FindTool::setSearchResult( bool found )
+{
+ QPalette pal = myFindEdit->palette();
+ QPalette ref = myReplaceEdit->palette();
+ pal.setColor( QPalette::Active, QPalette::Text,
+ found ? ref.color( QPalette::Active, QPalette::Text ) : QColor( 255, 0, 0 ) );
+ myFindEdit->setPalette( pal );
+}
+
+/*!
+ \brief Get 'Use regular expression' search option.
+ \return true if option is switched on; false otherwise.
+ \internal
+*/
+bool PyEditor_FindTool::isRegExp() const
+{
+ return actions()[RegExp]->isChecked();
+}
+
+/*!
+ \brief Get 'Case sensitive search' search option.
+ \return true if option is switched on; false otherwise.
+ \internal
+*/
+bool PyEditor_FindTool::isCaseSensitive() const
+{
+ return actions()[CaseSensitive]->isChecked();
+}
+
+/*!
+ \brief Get 'Whole words only' search option.
+ \return true if option is switched on; false otherwise.
+ \internal
+*/
+bool PyEditor_FindTool::isWholeWord() const
+{
+ return actions()[WholeWord]->isChecked();
+}
+
+/*!
+ \brief Get search options.
+ \param back Search direction: backward if false; forward otherwise.
+ \return List of options
+ \internal
+*/
+QTextDocument::FindFlags PyEditor_FindTool::searchFlags( bool back ) const
+{
+ QTextDocument::FindFlags flags = 0;
+ if ( isCaseSensitive() )
+ flags |= QTextDocument::FindCaseSensitively;
+ if ( isWholeWord() )
+ flags |= QTextDocument::FindWholeWords;
+ if ( back )
+ flags |= QTextDocument::FindBackward;
+ return flags;
+}
+
+/*!
+ \brief Get all matches from Python editor.
+ \param text Text being searched.
+ \return List of all matches.
+ \internal
+*/
+QList<QTextCursor> PyEditor_FindTool::matches( const QString& text ) const
+{
+ QList<QTextCursor> results;
+
+ QTextDocument* document = myEditor->document();
+
+ QTextCursor cursor( document );
+ while ( !cursor.isNull() )
+ {
+ cursor = isRegExp() ?
+ document->find( QRegExp( text, isCaseSensitive() ?
+ Qt::CaseSensitive : Qt::CaseInsensitive ),
+ cursor, searchFlags() ) :
+ document->find( text, cursor, searchFlags() );
+ if ( !cursor.isNull() )
+ results.append( cursor );
+ }
+ return results;
+}
+
+/*!
+ \brief Find specified text.
+ \param text Text being searched.
+ \param delta Search direction.
+ \internal
+*/
+void PyEditor_FindTool::find( const QString& text, int delta )
+{
+ QTextCursor cursor = myEditor->textCursor();
+ int position = qMin( cursor.position(), cursor.anchor() ) + delta;
+ cursor.setPosition( position );
+ myEditor->setTextCursor( cursor );
+
+ QList<QTextCursor> results = matches( text );
+
+ int index = -1;
+ if ( !results.isEmpty() )
+ {
+ if ( delta >= 0 )
+ {
+ // search forward
+ if ( position > results.last().anchor() )
+ position = 0;
+ for ( int i = 0; i < results.count() && index == -1; i++ )
+ {
+ QTextCursor result = results[i];
+ if ( result.hasSelection() && position <= result.anchor() )
+ {
+ index = i;
+ }
+ }
+ }
+ else
+ {
+ // search backward
+ if ( position < results.first().position() )
+ position = results.last().position();
+
+ for ( int i = results.count()-1; i >= 0 && index == -1; i-- )
+ {
+ QTextCursor result = results[i];
+ if ( result.hasSelection() && position >= result.position() )
+ {
+ index = i;
+ }
+ }
+ }
+ }
+ if ( index != -1 )
+ {
+ myInfoLabel->setText( tr( "NB_MATCHED_LABEL" ).arg( index+1 ).arg( results.count() ) );
+ myEditor->setTextCursor( results[index] );
+ }
+ else
+ {
+ myInfoLabel->clear();
+ cursor.clearSelection();
+ myEditor->setTextCursor( cursor );
+ }
+
+ setSearchResult( text.isEmpty() || !results.isEmpty() );
+}
+
+/*!
+ \brief Add completion.
+ \param text Completeion being added.
+ \param replace true to add 'Replace' completion; false to add 'Find' completion.
+ \internal
+*/
+void PyEditor_FindTool::addCompletion( const QString& text, bool replace )
+{
+ QStringListModel& model = replace ? myReplaceCompletion : myFindCompletion;
+
+ QStringList completions = model.stringList();
+ if ( !text.isEmpty() and !completions.contains( text ) )
+ {
+ completions.prepend( text );
+ model.setStringList( completions );
+ }
+}
--- /dev/null
+// Copyright (C) 2015-2016 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_FindTool.h
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
+//
+
+#ifndef PYEDITOR_FINDTOOL_H
+#define PYEDITOR_FINDTOOL_H
+
+#include "PyEditor.h"
+
+#include <QTextDocument>
+#include <QStringListModel>
+#include <QWidget>
+
+class PyEditor_Editor;
+class QAction;
+class QLabel;
+class QLineEdit;
+class QMenu;
+
+class PYEDITOR_EXPORT PyEditor_FindTool : public QWidget
+{
+Q_OBJECT
+
+ enum { CaseSensitive, WholeWord, RegExp, Find, FindPrevious, FindNext, Replace };
+
+public:
+ PyEditor_FindTool( PyEditor_Editor*, QWidget* = 0 );
+
+ bool event( QEvent* );
+ bool eventFilter( QObject*, QEvent* );
+
+public slots:
+ void activateFind();
+ void activateReplace();
+
+private slots:
+ void showMenu();
+ void find( const QString& );
+ void find();
+ void findPrevious();
+ void findNext();
+ void replace();
+ void replaceAll();
+ void update();
+ void activate( int );
+ void customizeMenu( QMenu* );
+
+private:
+ QList<QKeySequence> shortcuts( int ) const;
+ void updateShortcuts();
+
+ void showReplaceControls( bool );
+ void setSearchResult( bool );
+
+ bool isRegExp() const;
+ bool isCaseSensitive() const;
+ bool isWholeWord() const;
+ QTextDocument::FindFlags searchFlags( bool = false ) const;
+
+ QList<QTextCursor> matches( const QString& ) const;
+ void find( const QString&, int );
+
+ void addCompletion( const QString&, bool );
+
+private:
+ PyEditor_Editor* myEditor;
+ QLineEdit* myFindEdit;
+ QLineEdit* myReplaceEdit;
+ QLabel* myInfoLabel;
+ QStringListModel myFindCompletion, myReplaceCompletion;
+};
+
+#endif // PYEDITOR_FINDTOOL_H
--- /dev/null
+// Copyright (C) 2015-2016 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_Keywords.cxx
+// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
+//
+
+#include "PyEditor_Keywords.h"
+
+#include <QSet>
+
+/*!
+ \brief PyEditor_Keywords
+*/
+
+/*!
+ \brief Constructor.
+*/
+PyEditor_Keywords::PyEditor_Keywords( QObject* parent )
+ : QObject( parent )
+{
+}
+
+/*!
+ \brief Destructor.
+*/
+PyEditor_Keywords::~PyEditor_Keywords()
+{
+}
+
+
+QList<int> PyEditor_Keywords::types() const
+{
+ QMap<int, bool> map;
+ for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) {
+ map.insert( it.value().type, false );
+ }
+ return map.keys();
+}
+
+/*!
+ \brief Gets the colors list.
+ \return color list
+*/
+QList<QColor> PyEditor_Keywords::colors() const
+{
+ QList<QColor> list;
+ QSet<QRgb> set;
+ for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) {
+ const QColor& color = it.value().color;
+ if ( !set.contains( color.rgba() ) ) {
+ list.append( color );
+ set.insert( color.rgba() );
+ }
+ }
+ return list;
+}
+
+/*!
+ \brief Gets the keyword type.
+ \return type number
+*/
+int PyEditor_Keywords::type( const QString& keyword ) const
+{
+ return myKeywords.contains(keyword) ? myKeywords[keyword].type : -1;
+}
+
+/*!
+ \brief Gets the keyword color.
+ \return color
+*/
+QColor PyEditor_Keywords::color( const QString& keyword ) const
+{
+ return myKeywords.contains(keyword) ? myKeywords[keyword].color : QColor();
+}
+
+/*!
+ \brief Gets all keywords.
+ \return keywords string list
+*/
+QStringList PyEditor_Keywords::keywords() const
+{
+ return myKeywords.keys();
+}
+
+/*!
+ \brief Gets all keywords of specified type.
+ \return keywords string list
+*/
+QStringList PyEditor_Keywords::keywords( int type ) const
+{
+ QStringList keywords;
+ for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) {
+ if ( it.value().type == type )
+ keywords.append( it.key() );
+ }
+ return keywords;
+}
+
+/*!
+ \brief Gets all keywords with specified color.
+ \return keywords string list
+*/
+QStringList PyEditor_Keywords::keywords( const QColor& color ) const
+{
+ QStringList keywords;
+ for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) {
+ if ( it.value().color == color )
+ keywords.append( it.key() );
+ }
+ return keywords;
+}
+
+/*!
+ \brief Append keyword with type and color.
+*/
+void PyEditor_Keywords::append( const QString& keyword,
+ int type, const QColor& color )
+{
+ append( QStringList() << keyword, type, color );
+}
+
+/*!
+ \brief Append keyword list with type and color.
+*/
+void PyEditor_Keywords::append( const QStringList& keywords,
+ int type, const QColor& color )
+{
+ bool modif = false;
+ for ( QStringList::const_iterator it = keywords.begin(); it != keywords.end(); ++it ) {
+ const QString& kw = *it;
+ bool changed = false;
+ if ( !myKeywords.contains( kw ) ) {
+ myKeywords.insert( kw, KeywordInfo() );
+ changed = true;
+ }
+ KeywordInfo& info = myKeywords[kw];
+ changed = changed || info.type != type || info.color != color;
+ info.type = type;
+ info.color = color;
+
+ modif = modif || changed;
+ }
+
+ if ( modif )
+ Q_EMIT keywordsChanged();
+}
+
+/*!
+ \brief Remove all keywords with specified type.
+*/
+void PyEditor_Keywords::remove( int type )
+{
+ remove( keywords( type ) );
+}
+
+/*!
+ \brief Remove keyword.
+*/
+void PyEditor_Keywords::remove( const QString& keyword )
+{
+ remove( QStringList() << keyword );
+}
+
+/*!
+ \brief Remove keywords.
+*/
+void PyEditor_Keywords::remove( const QStringList& keywords )
+{
+ bool changed = false;
+ for ( QStringList::const_iterator it = keywords.begin(); it != keywords.end(); ++it ) {
+ if ( myKeywords.contains( *it ) ) {
+ myKeywords.remove( *it );
+ changed = true;
+ }
+ }
+ if ( changed )
+ Q_EMIT keywordsChanged();
+}
+
+/*!
+ \brief Remove all keywords.
+*/
+void PyEditor_Keywords::clear()
+{
+ if ( !myKeywords.isEmpty() ) {
+ myKeywords.clear();
+ Q_EMIT keywordsChanged();
+ }
+}
+
+/*!
+ \brief PyEditor_StandardKeywords
+*/
+
+PyEditor_StandardKeywords::PyEditor_StandardKeywords( QObject* parent )
+ : PyEditor_Keywords( parent )
+{
+ QStringList aBase;
+ aBase << "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";
+ append( aBase, Base, Qt::blue );
+
+ QStringList anExcept;
+ anExcept << "ArithmeticError" << "AssertionError" << "AttributeError"
+ << "EnvironmentError" << "EOFError" << "Exception"
+ << "FloatingPointError" << "ImportError" << "IndentationError"
+ << "IndexError" << "IOError" << "KeyboardInterrupt" << "KeyError"
+ << "LookupError" << "MemoryError" << "NameError" << "OSError"
+ << "NotImplementedError" << "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";
+ append( anExcept, Exceptions, Qt::magenta );
+}
+
+PyEditor_StandardKeywords::~PyEditor_StandardKeywords()
+{
+}
--- /dev/null
+// Copyright (C) 2015-2016 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_Keywords.h
+// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
+//
+
+#ifndef PYEDITOR_KEYWORDS_H
+#define PYEDITOR_KEYWORDS_H
+
+#include <QObject>
+#include <QColor>
+#include <QList>
+#include <QMap>
+
+class PyEditor_Keywords : public QObject
+{
+ Q_OBJECT
+
+public:
+ PyEditor_Keywords( QObject* = 0 );
+ virtual ~PyEditor_Keywords();
+
+ QList<int> types() const;
+ QList<QColor> colors() const;
+
+ int type( const QString& ) const;
+ QColor color( const QString& ) const;
+
+ QStringList keywords() const;
+ QStringList keywords( int ) const;
+ QStringList keywords( const QColor& ) const;
+
+ void append( const QString&, int, const QColor& = QColor() );
+ void append( const QStringList&, int, const QColor& = QColor() );
+
+ void remove( int );
+ void remove( const QString& );
+ void remove( const QStringList& );
+
+ void clear();
+
+Q_SIGNALS:
+ void keywordsChanged();
+
+private:
+ typedef struct { int type; QColor color; } KeywordInfo;
+ typedef QMap<QString, KeywordInfo> KeywordMap;
+
+private:
+ KeywordMap myKeywords;
+};
+
+class PyEditor_StandardKeywords : public PyEditor_Keywords
+{
+ Q_OBJECT
+
+public:
+ typedef enum { Base, Exceptions } KeywordType;
+
+public:
+ PyEditor_StandardKeywords( QObject* = 0 );
+ virtual ~PyEditor_StandardKeywords();
+};
+
+#endif
#include "PyEditor_PyHighlighter.h"
+#include "PyEditor_Keywords.h"
+
+#include <QSet>
+
#define NORMAL 0
#define TRIPLESINGLE 1
#define TRIPLEDOUBLE 2
\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()
+PyEditor_PyHighlighter::PyEditor_PyHighlighter( QTextDocument* theDocument,
+ PyEditor_Keywords* std,
+ PyEditor_Keywords* user )
+ : QSyntaxHighlighter( theDocument ),
+ myStdKeywords( std ),
+ myUserKeywords( user )
{
- 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;
-}
+ connect(myStdKeywords, SIGNAL( keywordsChanged() ),
+ this, SLOT( onKeywordsChanged() ) );
+ connect(myUserKeywords, SIGNAL( keywordsChanged() ),
+ this, SLOT( onKeywordsChanged() ) );
-/*!
- \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;
+ updateHighlight();
}
void PyEditor_PyHighlighter::highlightBlock( const QString& theText )
insertBracketsData( leftChar, rightChar, theData, theText );
}
+
+void PyEditor_PyHighlighter::onKeywordsChanged()
+{
+ updateHighlight();
+ rehighlight();
+}
+
+void PyEditor_PyHighlighter::updateHighlight()
+{
+ highlightingRules.clear();
+
+ HighlightingRule aRule;
+
+ QList<PyEditor_Keywords*> dictList;
+ dictList << myStdKeywords << myUserKeywords;
+
+ // Keywords
+ QSet<QString> existing;
+ for ( QList<PyEditor_Keywords*>::const_iterator it = dictList.begin();
+ it != dictList.end(); ++it ) {
+ PyEditor_Keywords* kwDict = *it;
+ QList<QColor> colors = kwDict->colors();
+ for ( QList<QColor>::const_iterator itr = colors.begin(); itr != colors.end(); ++itr ) {
+ QColor color = *itr;
+ QTextCharFormat format;
+ format.setForeground( color );
+ QStringList keywords = kwDict->keywords( color );
+ foreach ( const QString& keyword, keywords ) {
+ if ( existing.contains( keyword ) )
+ continue;
+
+ aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) );
+ aRule.format = format;
+ aRule.capture = 0;
+ highlightingRules.append( aRule );
+ existing.insert( keyword );
+ }
+ }
+ }
+
+ // 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 );
+}
#include <QSyntaxHighlighter>
class QTextDocument;
+class PyEditor_Keywords;
class PyEditor_PyHighlighter : public QSyntaxHighlighter
{
};
public:
- PyEditor_PyHighlighter( QTextDocument* = 0 );
+ PyEditor_PyHighlighter( QTextDocument*,
+ PyEditor_Keywords*, PyEditor_Keywords* );
- void initialize();
- QStringList keywords();
- QStringList specialKeywords();
+private Q_SLOTS:
+ void onKeywordsChanged();
+
+protected:
+ void updateHighlight();
protected:
struct HighlightingRule
QTextCharFormat classFormat;
QTextCharFormat referenceClassFormat;
QTextCharFormat functionFormat;
- QTextCharFormat keywordFormat;
- QTextCharFormat specialFromat;
QTextCharFormat numberFormat;
QTextCharFormat singleLineCommentFormat;
QTextCharFormat multiLineCommentFormat;
void highlightBlock( const QString& );
void insertBracketsData( char, char, TextBlockData*, const QString& );
void insertBracketsData( Brackets, TextBlockData*, const QString& );
+
+private:
+ PyEditor_Keywords* myStdKeywords;
+ PyEditor_Keywords* myUserKeywords;
};
#endif // PYEDITOR_PYHIGHLIGHTER_H
#include "PyEditor_Settings.h"
+#include "PyEditor_Editor.h"
+
/*!
\class PyEditor_Settings
\brief Manager of setting values.
myNumberColumns( 80 ),
myTabSpaceVisible( true ),
myTabSize( 4 ),
- myFont( "Courier", 10 )
+ myFont( "Courier", 10 ),
+ myCompletionPolicy( PyEditor_Editor::Always )
{
}
return myFont;
}
+/*!
+ \brief Set "completionPolicy" option.
+ \param completion policy option value
+*/
+void PyEditor_Settings::setCompletionPolicy( int value )
+{
+ myCompletionPolicy = value;
+}
+
+/*!
+ \brief Get "completionPolicy" option.
+ \return option value
+*/
+int PyEditor_Settings::completionPolicy() const
+{
+ return myCompletionPolicy;
+}
+
/*!
\brief Read settings from the persistence storage.
Base implementation does nothing; it should be reimplemented in successors.
setVerticalEdge( other.verticalEdge() );
setNumberColumns( other.numberColumns() );
setFont( other.font() );
+ setCompletionPolicy( other.completionPolicy() );
+
save();
}
"TabSpaceVisible",
"TabSize",
"Font",
+ "CompletionPolicy"
};
- return option >= 0 && option <= snFont ? options[option] : "Unknown";
+ return option >= 0 && option <= snCompletionPolicy ? options[option] : "Unknown";
}
snNumberColumns,
snTabSpaceVisible,
snTabSize,
- snFont };
+ snFont,
+ snCompletionPolicy };
public:
static PyEditor_Settings* settings();
void setFont( const QFont& );
QFont font() const;
+ void setCompletionPolicy( int );
+ int completionPolicy() const;
+
virtual void load();
virtual void save();
// Font settings
QFont myFont;
+ // Completion settings
+ int myCompletionPolicy;
+
static PyEditor_Settings* myGlobalSettings;
};
aMainLayout->addWidget( aDisplaySetBox );
// . Display settings <end>
+ // . Editor settings <start>
+ QGroupBox* aEditorSetBox = new QGroupBox( tr( "GR_EDIT_SET" ), this );
+ QGridLayout* aEditorSetLayout = new QGridLayout( aEditorSetBox );
+ aEditorSetLayout->addWidget( new QLabel( tr( "LBL_COMPLETION_MODE" ), aEditorSetBox ), 0, 0 );
+ aEditorSetLayout->addWidget( myCompletionPolicy = new QComboBox( aEditorSetBox ), 0, 1 );
+ myCompletionPolicy->addItems( QStringList() << tr( "LBL_NONE" ) << tr( "LBL_AUTO" )
+ << tr( "LBL_MANUAL" ) << tr( "LBL_ALWAYS" ) );
+ aMainLayout->addWidget( aEditorSetBox );
+ // . Editor settings <end>
+
// . Tab settings <start>
QGroupBox* aTabSetBox = new QGroupBox( tr( "GR_TAB_SET" ), this );
QVBoxLayout* aTabSetLayout = new QVBoxLayout( aTabSetBox );
settings.setVerticalEdge( myVerticalEdge->isChecked() );
settings.setNumberColumns( myNumberColumns->value() );
settings.setFont( font );
+ settings.setCompletionPolicy( myCompletionPolicy->currentIndex() );
myEditor->setSettings(settings); // updateContent()
PyEditor_Settings* globals = PyEditor_Settings::settings();
myNumberColumns->setValue( settings.numberColumns() );
myFontFamily->setCurrentFont( settings.font() );
setFontSize( QString::number( settings.font().pointSize() ) );
+ myCompletionPolicy->setCurrentIndex( settings.completionPolicy() );
onVerticalEdgeChecked();
onFontChanged();
public:
PyEditor_SettingsDlg( PyEditor_Editor*, bool = false, QWidget* = 0 );
- ~PyEditor_SettingsDlg();
+ virtual ~PyEditor_SettingsDlg();
private Q_SLOTS:
void onVerticalEdgeChecked();
QFontComboBox* myFontFamily;
QComboBox* myFontSize;
+ QComboBox* myCompletionPolicy;
+
QCheckBox* myDefaultCheck;
PyEditor_Editor* myEditor;
{
mySettings.beginGroup( myGroup.isEmpty() ? option( snEditor ) : myGroup );
- setHighlightCurrentLine( mySettings.value( option( snHighlightCurrentLine ), highlightCurrentLine() ).toBool() );
- setTextWrapping( mySettings.value( option( snTextWrapping ), textWrapping() ).toBool() );
- setCenterCursorOnScroll( mySettings.value( option( snCenterCursorOnScroll ), centerCursorOnScroll() ).toBool() );
- setLineNumberArea( mySettings.value( option( snLineNumberArea ), lineNumberArea() ).toBool() );
- setVerticalEdge( mySettings.value( option( snVerticalEdge ), verticalEdge() ).toBool() );
- setNumberColumns( mySettings.value( option( snNumberColumns ), numberColumns() ).toInt() );
- setTabSpaceVisible( mySettings.value( option( snTabSpaceVisible ), tabSpaceVisible() ).toBool() );
+ setHighlightCurrentLine( mySettings.value( option( snHighlightCurrentLine ),
+ highlightCurrentLine() ).toBool() );
+ setTextWrapping( mySettings.value( option( snTextWrapping ),
+ textWrapping() ).toBool() );
+ setCenterCursorOnScroll( mySettings.value( option( snCenterCursorOnScroll ),
+ centerCursorOnScroll() ).toBool() );
+ setLineNumberArea( mySettings.value( option( snLineNumberArea ),
+ lineNumberArea() ).toBool() );
+ setVerticalEdge( mySettings.value( option( snVerticalEdge ),
+ verticalEdge() ).toBool() );
+ setNumberColumns( mySettings.value( option( snNumberColumns ),
+ numberColumns() ).toInt() );
+ setTabSpaceVisible( mySettings.value( option( snTabSpaceVisible ),
+ tabSpaceVisible() ).toBool() );
setTabSize( mySettings.value( option( snTabSize ), tabSize() ).toInt() );
setFont( mySettings.value( option( snFont ), font() ).value<QFont>() );
+ setCompletionPolicy( mySettings.value( option( snCompletionPolicy ),
+ completionPolicy() ).toInt() );
setLanguage( mySettings.value( "language", language() ).toString() );
mySettings.endGroup();
mySettings.setValue( option( snTabSpaceVisible ), tabSpaceVisible() );
mySettings.setValue( option( snTabSize ), tabSize() );
mySettings.setValue( option( snFont ), font() );
+ mySettings.setValue( option( snCompletionPolicy ), completionPolicy() );
mySettings.setValue( "language", language() );
mySettings.endGroup();
--- /dev/null
+// Copyright (C) 2015-2016 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_Widget.cxx
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
+//
+
+#include "PyEditor_Editor.h"
+#include "PyEditor_FindTool.h"
+#include "PyEditor_Widget.h"
+
+#include <QVBoxLayout>
+
+/*!
+ \class PyEditor_Widget
+ \brief Wraps Python editor with the find/replace functionality to a single widget.
+*/
+
+/*!
+ \brief Constructor.
+ \param parent Parent widget.
+*/
+PyEditor_Widget::PyEditor_Widget( QWidget* parent )
+{
+ // Create editor.
+ myEditor = new PyEditor_Editor( this );
+
+ // Create find tool.
+ myFindTool = new PyEditor_FindTool( myEditor, this );
+
+ // Set-up layout
+ QVBoxLayout* layout = new QVBoxLayout( this );
+ layout->setContentsMargins( 0, 0, 0, 0 );
+ layout->setSpacing( 3 );
+ layout->addWidget( myEditor );
+ layout->addWidget( myFindTool );
+
+ connect( myEditor, SIGNAL( modificationChanged( bool ) ),
+ this, SIGNAL( modificationChanged( bool ) ) );
+ connect( myEditor, SIGNAL( undoAvailable( bool ) ),
+ this, SIGNAL( undoAvailable( bool ) ) );
+ connect( myEditor, SIGNAL( redoAvailable( bool ) ),
+ this, SIGNAL( redoAvailable( bool ) ) );
+ connect( myEditor, SIGNAL( copyAvailable( bool ) ),
+ this, SIGNAL( copyAvailable( bool ) ) );
+
+ connect( myEditor, SIGNAL( selectionChanged() ),
+ this, SIGNAL( selectionChanged() ) );
+ connect( myEditor, SIGNAL( textChanged() ),
+ this, SIGNAL( textChanged() ) );
+ connect( myEditor, SIGNAL( cursorPositionChanged() ),
+ this, SIGNAL( cursorPositionChanged() ) );
+
+ setFocusProxy( myEditor );
+}
+
+/*!
+ \brief Get editor.
+ \return Pointer to editor.
+*/
+PyEditor_Editor* PyEditor_Widget::editor()
+{
+ return myEditor;
+}
+
+/*!
+ \brief Get find tool.
+ \return Pointer to find tool.
+*/
+PyEditor_FindTool* PyEditor_Widget::findTool()
+{
+ return myFindTool;
+}
+
+/*!
+ \brief Get all custom keywords from editor.
+ \return List of keywords.
+*/
+QStringList PyEditor_Widget::keywords() const
+{
+ return myEditor->keywords();
+}
+
+/*!
+ \brief Set custom keywords to editor.
+ \param keywords List of keywords.
+ \param type Type of keywords (group id).
+ \param color Color of keywords.
+*/
+void PyEditor_Widget::appendKeywords( const QStringList& keywords, int type, const QColor& color )
+{
+ myEditor->appendKeywords( keywords, type, color );
+}
+
+/*!
+ \brief Remove given custom keywords from editor.
+ \param keywords List of keywords to remove.
+*/
+void PyEditor_Widget::removeKeywords( const QStringList& keywords )
+{
+ myEditor->removeKeywords( keywords );
+}
+
+/*!
+ \brief Get current editor's completion policy.
+ \return Completion policy (see PyEditor_Editor::CompletionPolicy).
+*/
+int PyEditor_Widget::completionPolicy() const
+{
+ return (int) myEditor->completionPolicy();
+}
+
+/*!
+ \brief Set editor's completion policy.
+ \param policy Completion policy (see PyEditor_Editor::CompletionPolicy).
+*/
+void PyEditor_Widget::setCompletionPolicy( int policy )
+{
+ myEditor->setCompletionPolicy( (PyEditor_Editor::CompletionPolicy) policy );
+}
+
+/*!
+ \brief Activate Find dialog.
+*/
+void PyEditor_Widget::find()
+{
+ myFindTool->activateFind();
+}
+
+/*!
+ \brief Activate Replace dialog.
+*/
+void PyEditor_Widget::replace()
+{
+ myFindTool->activateReplace();
+}
+
+/*!
+ \brief Undo last editor's operation.
+*/
+void PyEditor_Widget::undo()
+{
+ myEditor->undo();
+}
+
+/*!
+ \brief Redo last undone editor's operation.
+*/
+void PyEditor_Widget::redo()
+{
+ myEditor->redo();
+}
+
+/*!
+ \brief Cut text selected in editor and put it into clipboard.
+*/
+void PyEditor_Widget::cut()
+{
+ myEditor->cut();
+}
+
+/*!
+ \brief Copy text selected in editor into clipboard.
+*/
+void PyEditor_Widget::copy()
+{
+ myEditor->copy();
+}
+
+/*!
+ \brief Paste text from clipboard into editor.
+*/
+void PyEditor_Widget::paste()
+{
+ myEditor->paste();
+}
+
+/*!
+ \brief Delete text selected in editor.
+*/
+void PyEditor_Widget::deleteSelected()
+{
+ myEditor->deleteSelected();
+}
+
+/*!
+ \brief Select all text in editor.
+*/
+void PyEditor_Widget::selectAll()
+{
+ myEditor->selectAll();
+}
+
+/*!
+ \brief Clear content of editor.
+*/
+void PyEditor_Widget::clear()
+{
+ myEditor->clear();
+}
+
+/*!
+ \brief Set/clear modified flag of editor.
+ \param on 'Modified' flag's value.
+*/
+void PyEditor_Widget::setModified( bool on )
+{
+ myEditor->document()->setModified( on );
+}
+
+/*!
+ \brief Get modified flag of editor.
+ \return 'Modified' flag's value.
+*/
+bool PyEditor_Widget::isModified()
+{
+ return myEditor->document()->isModified();
+}
+
+/*!
+ \brief Set text to editor.
+ \param text Text to be put into editor.
+*/
+void PyEditor_Widget::setText( const QString& text )
+{
+ myEditor->setPlainText( text );
+}
+
+/*!
+ \brief Get text from editor.
+ \return Current editor contents.
+*/
+QString PyEditor_Widget::text() const
+{
+ return myEditor->toPlainText();
+}
+
+/*!
+ \brief Set editor's settings.
+ \param settings Settings object.
+*/
+void PyEditor_Widget::setSettings( const PyEditor_Settings& settings )
+{
+ myEditor->setSettings( settings );
+}
+
+/*!
+ \brief Get editor's settings.
+ \return Settings object.
+*/
+const PyEditor_Settings& PyEditor_Widget::settings() const
+{
+ return myEditor->settings();
+}
--- /dev/null
+// Copyright (C) 2015-2016 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_Widget.h
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
+//
+
+#ifndef PYEDITOR_WIDGET_H
+#define PYEDITOR_WIDGET_H
+
+#include "PyEditor.h"
+#include "PyEditor_Settings.h"
+
+#include <QWidget>
+
+class PyEditor_Editor;
+class PyEditor_FindTool;
+
+class PYEDITOR_EXPORT PyEditor_Widget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ PyEditor_Widget( QWidget* = 0 );
+
+ PyEditor_Editor* editor();
+ PyEditor_FindTool* findTool();
+
+ void setSettings( const PyEditor_Settings& );
+ const PyEditor_Settings& settings() const;
+
+ bool isModified();
+
+ QString text() const;
+
+ QStringList keywords() const;
+ void appendKeywords( const QStringList&, int, const QColor& = QColor() );
+ void removeKeywords( const QStringList& );
+
+ int completionPolicy() const;
+ void setCompletionPolicy( int );
+
+public slots:
+ void find();
+ void replace();
+
+ void undo();
+ void redo();
+ void cut();
+ void copy();
+ void paste();
+ void deleteSelected();
+ void selectAll();
+ void clear();
+
+ void setModified( bool );
+
+ void setText( const QString& );
+
+signals:
+ void modificationChanged( bool );
+ void undoAvailable( bool );
+ void redoAvailable( bool );
+ void copyAvailable( bool );
+ void selectionChanged();
+ void textChanged();
+ void cursorPositionChanged();
+
+private:
+ PyEditor_Editor* myEditor;
+ PyEditor_FindTool* myFindTool;
+};
+
+#endif // PYEDITOR_WIDGET_H
//
#include "PyEditor_Window.h"
-#include "PyEditor_Editor.h"
+#include "PyEditor_Widget.h"
#include "PyEditor_Settings.h"
#include "PyEditor_SettingsDlg.h"
#include <QAction>
#include <QApplication>
+#include <QCloseEvent>
#include <QFileDialog>
#include <QMenuBar>
#include <QMessageBox>
{
Q_INIT_RESOURCE( PyEditor );
- // Create editor and set it as a central widget.
- myTextEditor = new PyEditor_Editor( this );
- setCentralWidget( myTextEditor );
+ // Create central widget.
+ myEditor = new PyEditor_Widget( this );
+ setCentralWidget( myEditor );
// Create actions.
QAction* action;
action->setShortcut( QKeySequence::Save );
connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onSave() ) );
action->setEnabled( false );
- connect( myTextEditor->document(), SIGNAL( modificationChanged( bool ) ),
+ connect( myEditor, SIGNAL( modificationChanged( bool ) ),
action, SLOT( setEnabled( bool ) ) );
myActions[ SaveId ] = action;
action->setToolTip( tr( "TTP_UNDO" ) );
action->setStatusTip( tr( "DSC_UNDO" ) );
action->setShortcut( QKeySequence::Undo );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( undo() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( undo() ) );
action->setEnabled( false );
- connect( myTextEditor->document(), SIGNAL( undoAvailable( bool ) ),
+ connect( myEditor, SIGNAL( undoAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
myActions[ UndoId ] = action;
action->setToolTip( tr( "TTP_REDO" ) );
action->setStatusTip( tr( "DSC_REDO" ) );
action->setShortcut( QKeySequence::Redo );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( redo() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( redo() ) );
action->setEnabled( false );
- connect( myTextEditor->document(), SIGNAL( redoAvailable( bool ) ),
+ connect( myEditor, SIGNAL( redoAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
myActions[ RedoId ] = action;
action->setToolTip( tr( "TTP_CUT" ) );
action->setStatusTip( tr( "DSC_CUT" ) );
action->setShortcut( QKeySequence::Cut );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( cut() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( cut() ) );
action->setEnabled( false );
- connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+ connect( myEditor, SIGNAL( copyAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
myActions[ CutId ] = action;
action->setToolTip( tr( "TTP_COPY" ) );
action->setStatusTip( tr( "DSC_COPY" ) );
action->setShortcut( QKeySequence::Copy );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( copy() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
action->setEnabled( false );
- connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+ connect( myEditor, SIGNAL( copyAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
myActions[ CopyId ] = action;
action->setToolTip( tr( "TTP_PASTE" ) );
action->setStatusTip( tr( "DSC_PASTE" ) );
action->setShortcut( QKeySequence::Paste );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( paste() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
myActions[ PasteId ] = action;
// . Delete
action->setToolTip( tr( "TTP_DELETE" ) );
action->setStatusTip( tr( "DSC_DELETE" ) );
action->setShortcut( QKeySequence::Delete );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( deleteSelected() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( deleteSelected() ) );
action->setEnabled( false );
- connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+ connect( myEditor, SIGNAL( copyAvailable( bool ) ),
action, SLOT( setEnabled( bool ) ) );
myActions[ DeleteId ] = action;
action->setToolTip( tr( "TTP_SELECT_ALL" ) );
action->setStatusTip( tr( "DSC_SELECT_ALL" ) );
action->setShortcut( QKeySequence::SelectAll );
- connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( selectAll() ) );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
myActions[ SelectAllId ] = action;
+ // . Find
+ action = new QAction( QIcon( ":/images/py_find.png" ),
+ tr( "ACT_FIND" ), this );
+ action->setToolTip( tr( "TTP_FIND" ) );
+ action->setStatusTip( tr( "DSC_FIND" ) );
+ action->setShortcut( QKeySequence::Find );
+ action->setShortcutContext( Qt::WidgetShortcut );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( find() ) );
+ myActions[ FindId ] = action;
+
+ // . Replace
+ action = new QAction( QIcon( ":/images/py_replace.png" ),
+ tr( "ACT_REPLACE" ), this );
+ action->setToolTip( tr( "TTP_REPLACE" ) );
+ action->setStatusTip( tr( "DSC_REPLACE" ) );
+ action->setShortcuts( QList<QKeySequence>() << QKeySequence( "Ctrl+H" ) << QKeySequence( QKeySequence::Replace ) );
+ action->setShortcutContext( Qt::WidgetShortcut );
+ connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( replace() ) );
+ myActions[ ReplaceId ] = action;
+
// . Preferences
action = new QAction( QIcon( ":/images/py_preferences.png" ),
tr( "ACT_PREFERENCES" ), this );
menu->addSeparator();
menu->addAction( myActions[ SelectAllId ] );
menu->addSeparator();
+ menu->addAction( myActions[ FindId ] );
+ menu->addAction( myActions[ ReplaceId ] );
+ menu->addSeparator();
menu->addAction( myActions[ PreferencesId ] );
menu = menuBar()->addMenu( tr( "MNU_HELP" ) );
toolbar->addAction( myActions[ DeleteId ] );
toolbar->addAction( myActions[ SelectAllId ] );
toolbar->addSeparator();
+ toolbar->addAction( myActions[ FindId ] );
+ toolbar->addAction( myActions[ ReplaceId ] );
+ toolbar->addSeparator();
toolbar->addAction( myActions[ PreferencesId ] );
toolbar->addSeparator();
toolbar->addAction( myActions[ HelpId ] );
setCurrentFile( QString() );
// Additional set-up for main window.
- connect( myTextEditor->document(), SIGNAL( modificationChanged( bool ) ),
+ connect( myEditor, SIGNAL( modificationChanged( bool ) ),
this, SLOT( setWindowModified( bool ) ) );
// Initialize status bar.
{
if ( whetherSave() )
{
- myTextEditor->clear();
+ myEditor->clear();
setCurrentFile( QString() );
}
}
*/
void PyEditor_Window::onPreferences()
{
- PyEditor_SettingsDlg dlg( myTextEditor, true, this );
+ PyEditor_SettingsDlg dlg( myEditor->editor(), true, this );
connect( &dlg, SIGNAL( help() ), this, SLOT( onHelp() ) );
dlg.exec();
}
void PyEditor_Window::setCurrentFile( const QString& filePath )
{
myURL = filePath;
- myTextEditor->document()->setModified( false );
+ myEditor->setModified( false );
setWindowModified( false );
*/
bool PyEditor_Window::whetherSave()
{
- if ( myTextEditor->document()->isModified() )
+ if ( myEditor->isModified() )
{
QMessageBox::StandardButton answer = QMessageBox::warning( this,
tr( "NAME_PYEDITOR" ),
\brief Open file.
\param filePath file path
*/
-void PyEditor_Window::loadFile( const QString& filePath )
+void PyEditor_Window::loadFile( const QString& filePath, bool verbose )
{
QFile aFile( filePath );
if ( !aFile.open(QFile::ReadOnly | QFile::Text) )
{
- QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
- tr( "WRN_READ_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
+ if ( verbose )
+ QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
+ tr( "WRN_READ_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
return;
}
QTextStream anInput( &aFile );
QApplication::setOverrideCursor( Qt::WaitCursor );
- myTextEditor->setPlainText( anInput.readAll() );
+ myEditor->setText( anInput.readAll() );
QApplication::restoreOverrideCursor();
setCurrentFile( filePath );
\brief Save file.
\param filePath file path
*/
-bool PyEditor_Window::saveFile( const QString& filePath )
+bool PyEditor_Window::saveFile( const QString& filePath, bool verbose )
{
QFile aFile( filePath );
if ( !aFile.open( QFile::WriteOnly | QFile::Text ) )
{
- QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
- tr( "WRN_WRITE_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
+ if ( verbose )
+ QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
+ tr( "WRN_WRITE_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
return false;
}
QTextStream anOutput( &aFile );
QApplication::setOverrideCursor( Qt::WaitCursor );
- anOutput << myTextEditor->toPlainText();
+ anOutput << myEditor->text();
QApplication::restoreOverrideCursor();
setCurrentFile( filePath );
#include <QMap>
class QAction;
-class PyEditor_Editor;
+class PyEditor_Widget;
class PYEDITOR_EXPORT PyEditor_Window : public QMainWindow
{
public:
enum { NewId, OpenId, SaveId, SaveAsId, ExitId,
UndoId, RedoId, CutId, CopyId, PasteId, DeleteId, SelectAllId,
+ FindId, ReplaceId,
PreferencesId, HelpId };
PyEditor_Window( QWidget* = 0 );
~PyEditor_Window();
+ void loadFile( const QString&, bool = true );
+ bool saveFile( const QString&, bool = true );
+
protected:
virtual void closeEvent( QCloseEvent* );
void onPreferences();
void onHelp();
-private:
- void loadFile( const QString& );
- bool saveFile( const QString& );
-
void setCurrentFile( const QString& );
bool whetherSave();
QString defaultName() const;
private:
- PyEditor_Editor* myTextEditor;
+ PyEditor_Widget* myEditor;
QString myURL;
QMap<int, QAction*> myActions;
};
void setFont( const QFont& );
QFont font() const;
+
+ void setCompletionPolicy( int );
+ int completionPolicy() const;
};
class PyEditor_Editor : QPlainTextEdit
const PyEditor_Settings& settings() const;
QString text() const;
+ QStringList keywords() const;
+ void appendKeywords( const QStringList&, int, const QColor& = QColor() );
+ void removeKeywords( const QStringList& );
+
public slots:
void deleteSelected();
void append( const QString& );
PyEditor_Editor( const PyEditor_Editor& );
PyEditor_Editor& operator=( const PyEditor_Editor& );
};
+
+class PyEditor_FindTool : public QWidget
+{
+%TypeHeaderCode
+#include <PyEditor_FindTool.h>
+%End
+
+public:
+ explicit PyEditor_FindTool( PyEditor_Editor* /TransferThis/, QWidget* /TransferThis/ = 0 );
+
+public slots:
+ void activateFind();
+ void activateReplace();
+
+private:
+ PyEditor_FindTool( const PyEditor_FindTool& );
+ PyEditor_FindTool& operator=( const PyEditor_FindTool& );
+};
+
+class PyEditor_Widget : public QWidget
+{
+%TypeHeaderCode
+#include <PyEditor_Widget.h>
+%End
+
+public:
+ explicit PyEditor_Widget( QWidget* /TransferThis/ = 0 );
+
+ PyEditor_Editor* editor();
+ PyEditor_FindTool* findTool();
+
+ void setSettings( const PyEditor_Settings& );
+ const PyEditor_Settings& settings() const;
+
+ bool isModified();
+
+ QString text() const;
+
+ QStringList keywords() const;
+ void appendKeywords( const QStringList&, int, const QColor& = QColor() );
+ void removeKeywords( const QStringList& );
+
+ int completionPolicy() const;
+ void setCompletionPolicy( int );
+
+public slots:
+ void find();
+ void replace();
+
+ void undo();
+ void redo();
+ void cut();
+ void copy();
+ void paste();
+ void deleteSelected();
+ void selectAll();
+ void clear();
+
+ void setModified( bool );
+
+ void setText( const QString& );
+
+signals:
+ void modificationChanged( bool );
+ void undoAvailable( bool );
+ void redoAvailable( bool );
+ void copyAvailable( bool );
+ void selectionChanged();
+ void textChanged();
+ void cursorPositionChanged();
+
+private:
+ PyEditor_Widget( const PyEditor_Widget& );
+ PyEditor_Widget& operator=( const PyEditor_Widget& );
+};
<file>images/py_delete.png</file>
<file>images/py_editor.png</file>
<file>images/py_exit.png</file>
+ <file>images/py_find.png</file>
+ <file>images/py_find_next.png</file>
+ <file>images/py_find_previous.png</file>
<file>images/py_help.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_replace.png</file>
<file>images/py_save.png</file>
<file>images/py_save_as.png</file>
+ <file>images/py_search.png</file>
<file>images/py_select_all.png</file>
<file>images/py_undo.png</file>
<file>about.txt</file>
<b>Python Editor</b>
<hr>
+<p>
Python Editor is a simple program for writing Python scripts.
+</p>
+
+<p>
Program provides standard editing operations like copy/cut/paste, undo/redo, select all, delete, etc.
-Also it supports syntax highlighting and auto-indentation of Python code.
+It supports syntax highlighting and auto-indentation of Python code.
+</p>
+<p>
Most often used editing operations are available via the toolbar:
<ul>
<li><b>New</b>: creates new document.</li>
<li><b>Preferences</b>: opens Preferences dialog that allows specifying advanced parameters for the program.</li>
<li><b>Help</b>: shows this help information.</li>
</ul>
+</p>
+
+<p>
+Also, editor supports standard <b>Find</b> and <b>Replace</b> operations.
+</p>
+<p>
The behavior of the editor can be customized via the <b>Preferences</b> dialog. The following options can be customized:
<ul>
<li><b>Font settings</b>: choose the font.</li>
<li><b>Enable text wrapping</b>: allows wrapping text at the border of the editor's window.</li>
<li><b>Center cursor on scroll</b>: allows scrolling the script vertically to make the cursor visible at the center of the viewer.</li>
<li><b>Display line numbers area</b>: shows line numbers at the left border of the editor.</li>
+<li><b>Completion mode</b>: select the completion mode for inputted keywords.</li>
<li><b>Vertical edge settings</b>: draws vertical line at the specified column of the viewer.</li>
<li><b>Display tab delimiters</b>: displays tab marks at a given number of white spaces.</li>
<li><b>Save settings as default</b>: saves chosen options as a default ones. These settings will be restored after application restart.</li>
</ul>
+</p>
<source>LBL_LINE_NUMBS_AREA</source>
<translation>Display line numbers area</translation>
</message>
+ <message>
+ <source>GR_EDIT_SET</source>
+ <translation>Editor settings</translation>
+ </message>
+ <message>
+ <source>LBL_COMPLETION_MODE</source>
+ <translation>Completion mode</translation>
+ </message>
+ <message>
+ <source>LBL_NONE</source>
+ <translation>None</translation>
+ </message>
+ <message>
+ <source>LBL_AUTO</source>
+ <translation>Auto</translation>
+ </message>
+ <message>
+ <source>LBL_MANUAL</source>
+ <translation>Manual</translation>
+ </message>
+ <message>
+ <source>LBL_ALWAYS</source>
+ <translation>Always</translation>
+ </message>
<message>
<source>GR_TAB_SET</source>
<translation>Tab settings</translation>
<source>DSC_SELECT_ALL</source>
<translation>Select all the contents</translation>
</message>
+ <message>
+ <source>ACT_FIND</source>
+ <translation>Find</translation>
+ </message>
+ <message>
+ <source>TTP_FIND</source>
+ <translation>Find</translation>
+ </message>
+ <message>
+ <source>DSC_FIND</source>
+ <translation>Find text</translation>
+ </message>
+ <message>
+ <source>ACT_REPLACE</source>
+ <translation>Replace</translation>
+ </message>
+ <message>
+ <source>TTP_REPLACE</source>
+ <translation>Find & Replace</translation>
+ </message>
+ <message>
+ <source>DSC_REPLACE</source>
+ <translation>Find and replace text</translation>
+ </message>
<message>
<source>ACT_PREFERENCES</source>
<translation>Pre&ferences</translation>
</message>
<message>
<source>WRN_READ_FILE</source>
- <translation>Cannot read file %1:\n%2.</translation>
+ <translation>Cannot read file %1:
+%2.</translation>
</message>
<message>
<source>WRN_WRITE_FILE</source>
- <translation>Cannot write file %1:\n%2.</translation>
+ <translation>Cannot write file %1:
+%2.</translation>
</message>
<message>
<source>STS_READY</source>
<translation>Noname.py</translation>
</message>
</context>
+ <context>
+ <name>PyEditor</name>
+ <message>
+ <source>PROGRAM_DESCRIPTION</source>
+ <translation>Simple Python editor</translation>
+ </message>
+ <message>
+ <source>FILE_PARAM_NAME</source>
+ <translation>file</translation>
+ </message>
+ <message>
+ <source>FILE_PARAM_DESCRIPTION</source>
+ <translation>File to edit.</translation>
+ </message>
+ </context>
+ <context>
+ <name>PyEditor_FindTool</name>
+ <message>
+ <source>FIND_LABEL</source>
+ <translation>Find:</translation>
+ </message>
+ <message>
+ <source>REPLACE_LABEL</source>
+ <translation>Replace with:</translation>
+ </message>
+ <message>
+ <source>REPLACE_BTN</source>
+ <translation>Replace</translation>
+ </message>
+ <message>
+ <source>REPLACE_ALL_BTN</source>
+ <translation>Replace All</translation>
+ </message>
+ <message>
+ <source>CASE_SENSITIVE_CHECK</source>
+ <translation>Case Sensitive</translation>
+ </message>
+ <message>
+ <source>WHOLE_WORDS_CHECK</source>
+ <translation>Whole Words Only</translation>
+ </message>
+ <message>
+ <source>REGEX_CHECK</source>
+ <translation>Use Regular Expressions</translation>
+ </message>
+ <message>
+ <source>NB_MATCHED_LABEL</source>
+ <translation>%1 of %2 matches</translation>
+ </message>
+ </context>
</TS>
<source>LBL_LINE_NUMBS_AREA</source>
<translation>Affiche les numéros de ligne</translation>
</message>
+ <message>
+ <source>GR_EDIT_SET</source>
+ <translation type="unfinished">Editor settings</translation>
+ </message>
+ <message>
+ <source>LBL_COMPLETION_MODE</source>
+ <translation type="unfinished">Completion mode</translation>
+ </message>
+ <message>
+ <source>LBL_NONE</source>
+ <translation type="unfinished">None</translation>
+ </message>
+ <message>
+ <source>LBL_AUTO</source>
+ <translation type="unfinished">Auto</translation>
+ </message>
+ <message>
+ <source>LBL_MANUAL</source>
+ <translation type="unfinished">Manual</translation>
+ </message>
+ <message>
+ <source>LBL_ALWAYS</source>
+ <translation type="unfinished">Always</translation>
+ </message>
<message>
<source>GR_TAB_SET</source>
<translation>Indentation</translation>
<source>DSC_SELECT_ALL</source>
<translation>Sélectionne tout le contenu</translation>
</message>
+ <message>
+ <source>ACT_FIND</source>
+ <translation type="unfinished">Find</translation>
+ </message>
+ <message>
+ <source>TTP_FIND</source>
+ <translation type="unfinished">Find</translation>
+ </message>
+ <message>
+ <source>DSC_FIND</source>
+ <translation type="unfinished">Find text</translation>
+ </message>
+ <message>
+ <source>ACT_REPLACE</source>
+ <translation type="unfinished">Replace</translation>
+ </message>
+ <message>
+ <source>TTP_REPLACE</source>
+ <translation type="unfinished">Find & Replace</translation>
+ </message>
+ <message>
+ <source>DSC_REPLACE</source>
+ <translation type="unfinished">Find and replace text</translation>
+ </message>
<message>
<source>ACT_PREFERENCES</source>
<translation>Préférences</translation>
</message>
<message>
<source>WRN_READ_FILE</source>
- <translation>Impossible de lire le fichier %1:\n%2.</translation>
+ <translation>Impossible de lire le fichier %1:
+%2.</translation>
</message>
<message>
<source>WRN_WRITE_FILE</source>
- <translation>Impossible d'écrire le fichier %1:\n%2.</translation>
+ <translation>Impossible d'écrire le fichier %1:
+%2.</translation>
</message>
<message>
<source>STS_READY</source>
<translation>Noname.py</translation>
</message>
</context>
+ <context>
+ <name>PyEditor</name>
+ <message>
+ <source>PROGRAM_DESCRIPTION</source>
+ <translation>Editeur python</translation>
+ </message>
+ <message>
+ <source>FILE_PARAM_NAME</source>
+ <translation type="unfinished">file</translation>
+ </message>
+ <message>
+ <source>FILE_PARAM_DESCRIPTION</source>
+ <translation type="unfinished">File to edit.</translation>
+ </message>
+ </context>
+ <context>
+ <name>PyEditor_FindTool</name>
+ <message>
+ <source>FIND_LABEL</source>
+ <translation type="unfinished">Find:</translation>
+ </message>
+ <message>
+ <source>REPLACE_LABEL</source>
+ <translation type="unfinished">Replace with:</translation>
+ </message>
+ <message>
+ <source>REPLACE_BTN</source>
+ <translation type="unfinished">Replace</translation>
+ </message>
+ <message>
+ <source>REPLACE_ALL_BTN</source>
+ <translation type="unfinished">Replace All</translation>
+ </message>
+ <message>
+ <source>CASE_SENSITIVE_CHECK</source>
+ <translation type="unfinished">Case Sensitive</translation>
+ </message>
+ <message>
+ <source>WHOLE_WORDS_CHECK</source>
+ <translation type="unfinished">Whole Words Only</translation>
+ </message>
+ <message>
+ <source>REGEX_CHECK</source>
+ <translation type="unfinished">Use Regular Expressions</translation>
+ </message>
+ <message>
+ <source>NB_MATCHED_LABEL</source>
+ <translation type="unfinished">%1 of %2 matches</translation>
+ </message>
+ </context>
</TS>
<source>LBL_LINE_NUMBS_AREA</source>
<translation>ライン番号エリアの表示</translation>
</message>
+ <message>
+ <source>GR_EDIT_SET</source>
+ <translation type="unfinished">Editor settings</translation>
+ </message>
+ <message>
+ <source>LBL_COMPLETION_MODE</source>
+ <translation type="unfinished">Completion mode</translation>
+ </message>
+ <message>
+ <source>LBL_NONE</source>
+ <translation type="unfinished">None</translation>
+ </message>
+ <message>
+ <source>LBL_AUTO</source>
+ <translation type="unfinished">Auto</translation>
+ </message>
+ <message>
+ <source>LBL_MANUAL</source>
+ <translation type="unfinished">Manual</translation>
+ </message>
+ <message>
+ <source>LBL_ALWAYS</source>
+ <translation type="unfinished">Always</translation>
+ </message>
<message>
<source>GR_TAB_SET</source>
<translation>タブ設定</translation>
<source>DSC_SELECT_ALL</source>
<translation>全選択</translation>
</message>
+ <message>
+ <source>ACT_FIND</source>
+ <translation type="unfinished">Find</translation>
+ </message>
+ <message>
+ <source>TTP_FIND</source>
+ <translation type="unfinished">Find</translation>
+ </message>
+ <message>
+ <source>DSC_FIND</source>
+ <translation type="unfinished">Find text</translation>
+ </message>
+ <message>
+ <source>ACT_REPLACE</source>
+ <translation type="unfinished">Replace</translation>
+ </message>
+ <message>
+ <source>TTP_REPLACE</source>
+ <translation type="unfinished">Find & Replace</translation>
+ </message>
+ <message>
+ <source>DSC_REPLACE</source>
+ <translation type="unfinished">Find and replace text</translation>
+ </message>
<message>
<source>ACT_PREFERENCES</source>
<translation>環境設定 (&f)</translation>
</message>
<message>
<source>WRN_READ_FILE</source>
- <translation>ファイルが読めません %1:\n%2.</translation>
+ <translation>ファイルが読めません %1:
+%2.</translation>
</message>
<message>
<source>WRN_WRITE_FILE</source>
- <translation>ファイルが書き込めません %1:\n%2.</translation>
+ <translation>ファイルが書き込めません %1:
+%2.</translation>
</message>
<message>
<source>STS_READY</source>
<translation>Noname. py</translation>
</message>
</context>
+ <context>
+ <name>PyEditor</name>
+ <message>
+ <source>PROGRAM_DESCRIPTION</source>
+ <translation>Pythonのエディタ</translation>
+ </message>
+ <message>
+ <source>FILE_PARAM_NAME</source>
+ <translation type="unfinished">file</translation>
+ </message>
+ <message>
+ <source>FILE_PARAM_DESCRIPTION</source>
+ <translation type="unfinished">File to edit.</translation>
+ </message>
+ </context>
+ <context>
+ <name>PyEditor_FindTool</name>
+ <message>
+ <source>FIND_LABEL</source>
+ <translation type="unfinished">Find:</translation>
+ </message>
+ <message>
+ <source>REPLACE_LABEL</source>
+ <translation type="unfinished">Replace with:</translation>
+ </message>
+ <message>
+ <source>REPLACE_BTN</source>
+ <translation type="unfinished">Replace</translation>
+ </message>
+ <message>
+ <source>REPLACE_ALL_BTN</source>
+ <translation type="unfinished">Replace All</translation>
+ </message>
+ <message>
+ <source>CASE_SENSITIVE_CHECK</source>
+ <translation type="unfinished">Case Sensitive</translation>
+ </message>
+ <message>
+ <source>WHOLE_WORDS_CHECK</source>
+ <translation type="unfinished">Whole Words Only</translation>
+ </message>
+ <message>
+ <source>REGEX_CHECK</source>
+ <translation type="unfinished">Use Regular Expressions</translation>
+ </message>
+ <message>
+ <source>NB_MATCHED_LABEL</source>
+ <translation type="unfinished">%1 of %2 matches</translation>
+ </message>
+ </context>
</TS>