WxStyledTextCtrl

From WxWiki

Jump to: navigation, search

Contents

[edit] Adding a Lexer to wxStyledTextCtrl

This is my 2 days work to create a new lexer for wxStyledTextCtrl (wxSTC). The first thing you should know is that wxSTC is a wrapper around a widely known text control named scintilla [1]. So if you want to add a lexer you really are adding it to the scintilla distribution that wxSTC comes with.

Here is this web page [2] where you can find how to add a lexer to scintilla and its text editing application called SciTE. The first step is basically the same.

You should have a $(wxDIR)/contrib/src/stc dir where you have all the things that go with the control; I not a expert in this just I saw what I needed to add the lexer, in the documentation you will find more information. For the moment you should go to that directory and follow these steps. We will suppose we're adding some language, call it foo.

1. So go to the scintilla/include dir and open the SciLexer.h file, there you will add a ID value:

  1. define SCLEX_FOO=79, (supposed the last ID was 78)

And any lexical class IDs:

  1. define SCE_FOO_DEFAULT=0
  2. define SCE_FOO_COMMENT=1

2. Now you should add the ID value to stc.h located in $(wxDIR)/contrib/include/wx/stc.

  1. define wxSTC_LEX_FOO 79 and
  2. define wxSTC_FOO_DEFAULT 0
  3. define wxSTC_FOO_COMMENT 1

I think you should be able to recognize an order in the file so you can do it that way.

3. In the scintilla/src/LexOthers.cxx write a ColouriseFooDoc function similar to one of the other functions such as ColouriseLatexDoc. For a more detailed guide open the file LexCpp.cxx and see the ColouriseCppDoc. What I did was to copy and paste that function and fit it to my needs.

LexerModule lmfoo(SCLEX_FOO, ColouriseFooDoc, "conf");

4. If this is a complex lexer then it may be better off in its own file, in which case clone one of the current files and then add the file to all of the make files where LexOthers is currently referenced

5. You should be able to compile wxSTC.

Those were the steps I followed to get the lexer working. You should see this page to understand what the Colourise function does: [3]

To get the lexer working we would modify the sample that comes with wxSTC, you should find it in $(wxDIR)/contrib/samples/stc, here what we need to do is to modify the prefs.cpp file, this contains all the preferences for the lexers:

1. If you have some keywords you want to add, just follow the way C++ and Python do it.

2. To the g_LanguagePrefs const add _T("FOO") with your definitions.

That's all, you should be able to compile the sample and select the Foo language from the View->Highlight Language->Foo

I hope that serves you.

[edit] Deriving from ScintillaWX

From: dhoward ~at- mountbromo.com (Daniel Howard)
Subject: Custom ScintillaWX for wxStyledTextCtrl
Date: 27 Jun 2003 18:05:28 -0700
To: wx-users@lists.wxwidgets.org

I just wanted to record for posterity how to make a ScintillaWX
derived class under wxWidgets when using the Scintilla Edit Control
from the contrib/stc directory.  The Scintilla Edit Control is a full
service text editing control (with built-in auto-completion and source
code coloring).  It is included with the wxWidgets distribution with
documentation located at http://www.scintilla.org/ .  It took me the
better part of a day to figure out how hook in my own ScintillaWX
derived class without crashing.

ScintillaWX is only meant for internal use by the wxStyledTextCtrl
control.  However, once you've started using wxStyledTextCtrl, you
often want to make customizations or fix bugs in Scintilla itself. 
Since wxStyledTextCtrl is a wrapper around ScintillaWX and ScintillaWX
contains the bulk of the Scintilla code, it often becomes necessary to
subclass ScintillaWX to get control of Scintilla's behavior.

To get at ScintillaWX and other wxStyledTextCtrl internals, you've got
to add the following include directories to your project:

C:\wxWidgets_2.4.0\contrib\include
C:\wxWidgets_2.4.0\contrib\src\stc\scintilla\include
C:\wxWidgets_2.4.0\contrib\src\stc\scintilla\src

Also, you've got to build the Scintilla control and add the Scintilla
(debug) library to your project:

C:\wxWidgets_2.4.0\lib\stcd.lib

The code to demonstrate deriving from ScintillaWX is below.  There are
a few gotchas.  The first trick is do "delete m_swx" and "m_swx = new
CoolScintillaWX(this)" in order to replace wxStyledTextCtrl's generic
ScintillaWX.  There is no easy way to intercept the creation of the
generic ScintillaWX by Scintilla so, instead, we replace it.  The
second trick is to define __WX__ and LINK_LEXERS before including
ScintillaWX.h (preferably everywhere, though especially for the file
that implements the first trick).  If you do not, your program will
compile and link without warnings but will crash after typing a few
characters into the control.  This happens because you accidentally
use two different memory managers: wxWidgets memory manager and Visual
C++'s default memory manager.  If this happens, the program gets all
confused, allocating memory from one manager and then trying to
deallocate it from the other and vice-versa.

Here's the code (based on wxWidgets 2.4.0):

//CoolScintillaWX.h
#ifndef CoolScintillaWX_h
#define CoolScintillaWX_h

#define __WX__
#define SCI_LEXER
#define LINK_LEXERS
#include <../src/stc/ScintillaWX.h>

class CoolScintillaWX : public ScintillaWX
{
public:
  CoolScintillaWX(wxStyledTextCtrl* win);
};

#endif //CoolScintillaWX_h

//CoolScintillaWX.cpp
//#include "CoolScintillaWX.h"

CoolScintillaWX::CoolScintillaWX( wxStyledTextCtrl * win ) :
  ScintillaWX(win)
{
}

//CoolStyledTextCtrl.h
#ifndef CoolStyledTextCtrl_h
#define CoolStyledTextCtrl_h

#include <wx/stc/stc.h>

class CoolStyledTextCtrl : public wxStyledTextCtrl
{
public:
  CoolStyledTextCtrl(wxWindow * parent);
};

#endif //CoolStyledTextCtrl_h

//CoolStyledTextCtrl.cpp
//#include "CoolScintillaWX.h"
//#include "CoolStyledTextCtrl.h"

CoolStyledTextCtrl::CoolStyledTextCtrl( wxWindow * parent ) :
  wxStyledTextCtrl(parent, wxID_ANY)
{
  delete m_swx;
  m_swx = new CoolScintillaWX(this);
}

//test.h
void test();

//test.cpp
//#include "CoolStyledTextCtrl.h"

void test()
{
  wxFrame * frame = new wxFrame(NULL, wxID_ANY, "Scintilla Test");
  CoolStyledTextCtrl * ed = new CoolStyledTextCtrl(frame);
  frame->Show(TRUE);
}

[edit] Scintilla Internals

Scintilla is complex and, at least in my experience, is hard to use without looking at the source code. Ideally, Scintilla.org would have its own Wiki. Since it does not, this section describes Scintilla internals which is essentially the same as wxStyledTextControl internals with special attention to how those internals relate to wxWidgets.

Scintilla is written in C++ but many of its data structures are reminiscent of C.

Often, Scintilla objects will allocate an regular array of C++ objects or C++ object pointers and use a data member to track how much of the array is used. Then, when the array grows to large, a new array is manually allocated, contents are moved from the old array to the new one and data members are fixed up. In contrast, wxWidgets itself has dedicated, general purpose containers that do allocations, copying and deallocations internally so the object that uses these containers need concern itself with low-level array manipulation.

Also, Scintilla sometimes allocates a pointer inside of one object and passes the pointer to another object to hold and use. Scintilla uses various ways to track these pointers that are shared between multiple objects and takes care to delete them only once. In contrast, wxWidgets objects usually allocate and permanently store pointers to child objects in a single owning object. In this way, wxWidgets needs very little code to track pointers and avoiding deleting a pointer more than once is usually trivial.

[edit] CellBuffer.cxx

CellBuffer.cxx contains the Action class and the UndoHistory class among several others.

The '''UndoHistory''' class stores an array of actions. There are three types of actions: Start, Insert and Remove. The Start action indicates the start of a sequence of actions that represent a single user action; for example, if the user swaps two lines using Ctrl-T, the deletion of the lower line and its re-insertion above the upper line are two actions that represent a single user action. The Insert action indicates a string of text that is inserted as a single position. The Remove action indicates an uninterrupted range of text that was removed from the document.

'''UndoHistory::actions''' contains an array of actions. '''UndoHistory::lenActions''' and '''UndoHistory::maxAction''' indicate the total number of action objects and the index of the largest object being used.

'''UndoHistory::currentAction''' indicates where the user is in the history. Normally, the user would be at the end but, if the user has done an Undo, this variable would point further towards the front of the actions array. The actions after the position indicated by this variable are actions that are taken if the user does a Redo.

'''UndoHistory::undoSequenceDepth''' tracks the number of BeginUndoAction() calls that do not have a matching EndUndoAction() call. These calls allow Scintilla to mark a group of actions as a single user action. Normally, Scintilla tries to group actions together that appear to be the result of uninterrupted typing or single deletes at the same position (i.e. "coalesce" in Scintilla terminology). Similiarly, Scintilla tries to add Start actions to separate actions that appear to be unrelated. The BeginUndoAction() and EndUndoAction() override this separation and force all actions between these calls to be part of the same user action; UndoHistory::undoSequenceDepth tracks this state.

'''UndoHistory::EnsureUndoRoom()''' helps to manage the allocation, copying and deallocation of the actions array when it becomes too large.

'''UndoHistory::AppendAction()''' adds an action to the actions array and checks the state to decide whether this action can be lumped in with a previous user action and should be preceded by a Start action to indicate a new user action.

[edit] Tips, Tricks, and Pitfalls

  • Look out for SetReadOnly(). Once set future calls like ClearAll(), SetText(), and even LoadFile() will look to successfully complete, but will not change the content of the control.

[edit] Resources

Personal tools