wxStyledTextCtrl

From WxWiki
Jump to: navigation, search
Official Classes SmallBlocks.png Archive Containers Controls Data Structures Database Date & Time Debug Device Contexts Dialogs Document & Views Drag & Drop Events Filesystem Frames Graphics Grid Cell Help HTML Logging Miscellaneous Networking Printing Sizers Streams Threading Windows

A wxWidgets implementation of the Scintilla source code editing component.

As well as features found in standard text editing components, Scintilla includes features especially useful when editing and debugging source code. These include support for syntax styling, error indicators, code completion and call tips.

The selection margin can contain markers like those used in debuggers to indicate breakpoints and the current line. Styling choices are more open than with many editors, allowing the use of proportional fonts, bold and italics, multiple foreground and background colours and multiple fonts.

wxStyledTextCtrl is a 1 to 1 mapping of "raw" scintilla interface, whose documentation can be found in the Scintilla website (http://www.scintilla.org/).

Margins

Margins are generally used for things like break points and line numbering as well as code folding. Usually you just set the margin width and use styling and markers to achieve what you are looking for.

Styling

Styling is used to define your keyword lists and the coloring and fonts or just general style of delimiters, characters, keywords, strings, numbers, etc.

Lexers

Lexers are what determine what needs highlighted in your scintilla based control. Several default lexers come with the wxStyledTextCtrl such as HTML, XML, C++, and C# (constants wxSTC_LEX_*) but custom lexers can be used to achieve custom syntax styling for other languages or even for regular languages that you may want to expand the keyword lists and highlighting of. wxStyledTextCtrl being a wrapper to Scintilla means if you want to add a lexer you really are adding it to the scintilla distribution that wxSTC comes with.

This web page is 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. 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, (suppose 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

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: [1]

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

Markers

Markers are used for indicating parsing errors in the code, as well as special symbol margins such as those you would use to insert break points with bitmap images.

Automatic Completion

NOTE: In early releases of 2.9 there was a bug cutting off the Autocomp images, it has been fixed in the latest SVN

Auto completion windows can be shown with bitmap graphics as well by first registering the image to an integer type then adding each keyword to the auto complete list followed by a question mark and then then the integer type for that keyword followed by a space. For instance, "test_case?1 test_parse?2", would have test_case use the integer type one and its image and have test_parse use integer type two and its image.

Example

Find below a minimal sample showing how to highlight HTML code :

/** Not really needed here since this example uses a single margin */
enum
{
    MARGIN_LINE_NUMBERS
};
 
 
class SourceViewDialog : public wxDialog
{
public:
 
    void onClose(wxCloseEvent& evt)
    {
        EndModal( GetReturnCode() );
    }
 
    SourceViewDialog(wxWindow* parent, wxString source) :
        wxDialog(parent, wxID_ANY, "Source Code",
                 wxDefaultPosition, wxSize(700,500),
                 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
    {
        wxStyledTextCtrl* text = new wxStyledTextCtrl(this, wxID_ANY);
 
        text->SetMarginWidth (MARGIN_LINE_NUMBERS, 50);
        text->StyleSetForeground (wxSTC_STYLE_LINENUMBER, wxColour (75, 75, 75) );
        text->StyleSetBackground (wxSTC_STYLE_LINENUMBER, wxColour (220, 220, 220));
        text->SetMarginType (MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER);
 
        text->SetWrapMode (wxSTC_WRAP_WORD);
 
        text->SetText(source);
 
        text->StyleClearAll();
        text->SetLexer(wxSTC_LEX_HTML);
        text->StyleSetForeground (wxSTC_H_DOUBLESTRING,     wxColour(255,0,0));
        text->StyleSetForeground (wxSTC_H_SINGLESTRING,     wxColour(255,0,0));
        text->StyleSetForeground (wxSTC_H_ENTITY,           wxColour(255,0,0));
        text->StyleSetForeground (wxSTC_H_TAG,              wxColour(0,150,0));
        text->StyleSetForeground (wxSTC_H_TAGUNKNOWN,       wxColour(0,150,0));
        text->StyleSetForeground (wxSTC_H_ATTRIBUTE,        wxColour(0,0,150));
        text->StyleSetForeground (wxSTC_H_ATTRIBUTEUNKNOWN, wxColour(0,0,150));
        text->StyleSetForeground (wxSTC_H_COMMENT,          wxColour(150,150,150));
 
        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
        sizer->Add(text, 1, wxEXPAND);
        SetSizer(sizer);
 
        Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(SourceViewDialog::onClose), NULL, this);
    }
 
};

And here is a somewhat more elaborate example to highlight C++ :

#include <wx/wx.h>
#include <wx/stc/stc.h>
 
enum
{
    MARGIN_LINE_NUMBERS,
    MARGIN_FOLD
};
 
 
class SourceViewDialog : public wxDialog
{
    wxStyledTextCtrl* text;
 
public:
 
    void onClose(wxCloseEvent& evt)
    {
        EndModal( GetReturnCode() );
    }
 
    SourceViewDialog(wxWindow* parent, wxString source) :
    wxDialog(parent, wxID_ANY, _("Source Code"),
             wxDefaultPosition, wxSize(700,500),
             wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
    {
        text = new wxStyledTextCtrl(this, wxID_ANY);
 
        text->StyleClearAll();
        text->SetLexer(wxSTC_LEX_CPP);
 
        text->SetMarginWidth (MARGIN_LINE_NUMBERS, 50);
        text->StyleSetForeground (wxSTC_STYLE_LINENUMBER, wxColour (75, 75, 75) );
        text->StyleSetBackground (wxSTC_STYLE_LINENUMBER, wxColour (220, 220, 220));
        text->SetMarginType (MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER);
 
 
        // ---- Enable code folding
        text->SetMarginType (MARGIN_FOLD, wxSTC_MARGIN_SYMBOL);
        text->SetMarginWidth(MARGIN_FOLD, 15);
        text->SetMarginMask (MARGIN_FOLD, wxSTC_MASK_FOLDERS);
        text->StyleSetBackground(MARGIN_FOLD, wxColor(200, 200, 200) );
        text->SetMarginSensitive(MARGIN_FOLD, true);
 
        // Properties found from http://www.scintilla.org/SciTEDoc.html
        text->SetProperty (wxT("fold"),         wxT("1") );
        text->SetProperty (wxT("fold.comment"), wxT("1") );
        text->SetProperty (wxT("fold.compact"), wxT("1") );
 
        wxColor grey( 100, 100, 100 );
        text->MarkerDefine (wxSTC_MARKNUM_FOLDER, wxSTC_MARK_ARROW );
        text->MarkerSetForeground (wxSTC_MARKNUM_FOLDER, grey);
        text->MarkerSetBackground (wxSTC_MARKNUM_FOLDER, grey);
 
        text->MarkerDefine (wxSTC_MARKNUM_FOLDEROPEN,    wxSTC_MARK_ARROWDOWN);
        text->MarkerSetForeground (wxSTC_MARKNUM_FOLDEROPEN, grey);
        text->MarkerSetBackground (wxSTC_MARKNUM_FOLDEROPEN, grey);
 
        text->MarkerDefine (wxSTC_MARKNUM_FOLDERSUB,     wxSTC_MARK_EMPTY);
        text->MarkerSetForeground (wxSTC_MARKNUM_FOLDERSUB, grey);
        text->MarkerSetBackground (wxSTC_MARKNUM_FOLDERSUB, grey);
 
        text->MarkerDefine (wxSTC_MARKNUM_FOLDEREND,     wxSTC_MARK_ARROW);
        text->MarkerSetForeground (wxSTC_MARKNUM_FOLDEREND, grey);
        text->MarkerSetBackground (wxSTC_MARKNUM_FOLDEREND, _T("WHITE"));
 
        text->MarkerDefine (wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_ARROWDOWN);
        text->MarkerSetForeground (wxSTC_MARKNUM_FOLDEROPENMID, grey);
        text->MarkerSetBackground (wxSTC_MARKNUM_FOLDEROPENMID, _T("WHITE"));
 
        text->MarkerDefine (wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_EMPTY);
        text->MarkerSetForeground (wxSTC_MARKNUM_FOLDERMIDTAIL, grey);
        text->MarkerSetBackground (wxSTC_MARKNUM_FOLDERMIDTAIL, grey);
 
        text->MarkerDefine (wxSTC_MARKNUM_FOLDERTAIL,    wxSTC_MARK_EMPTY);
        text->MarkerSetForeground (wxSTC_MARKNUM_FOLDERTAIL, grey);
        text->MarkerSetBackground (wxSTC_MARKNUM_FOLDERTAIL, grey);
        // ---- End of code folding part
 
        text->SetWrapMode (wxSTC_WRAP_WORD); // other choice is wxSCI_WRAP_NONE
 
        text->SetText(source);
 
        text->StyleSetForeground (wxSTC_C_STRING,            wxColour(150,0,0));
        text->StyleSetForeground (wxSTC_C_PREPROCESSOR,      wxColour(165,105,0));
        text->StyleSetForeground (wxSTC_C_IDENTIFIER,        wxColour(40,0,60));
        text->StyleSetForeground (wxSTC_C_NUMBER,            wxColour(0,150,0));
        text->StyleSetForeground (wxSTC_C_CHARACTER,         wxColour(150,0,0));
        text->StyleSetForeground (wxSTC_C_WORD,              wxColour(0,0,150));
        text->StyleSetForeground (wxSTC_C_WORD2,             wxColour(0,150,0));
        text->StyleSetForeground (wxSTC_C_COMMENT,           wxColour(150,150,150));
        text->StyleSetForeground (wxSTC_C_COMMENTLINE,       wxColour(150,150,150));
        text->StyleSetForeground (wxSTC_C_COMMENTDOC,        wxColour(150,150,150));
        text->StyleSetForeground (wxSTC_C_COMMENTDOCKEYWORD, wxColour(0,0,200));
        text->StyleSetForeground (wxSTC_C_COMMENTDOCKEYWORDERROR, wxColour(0,0,200));
        text->StyleSetBold(wxSTC_C_WORD, true);
        text->StyleSetBold(wxSTC_C_WORD2, true);
        text->StyleSetBold(wxSTC_C_COMMENTDOCKEYWORD, true);
 
        // a sample list of keywords, I haven't included them all to keep it short...
        text->SetKeyWords(0, wxT("return for while break continue"));
        text->SetKeyWords(1, wxT("const int float void char double"));
 
        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
        sizer->Add(text, 1, wxEXPAND);
        SetSizer(sizer);
 
        Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(SourceViewDialog::onClose), NULL, this);
        text->Connect(wxEVT_STC_MARGINCLICK, wxStyledTextEventHandler(SourceViewDialog::OnMarginClick), NULL, this);
    }
 
    /** Event callback when a margin is clicked, used here for code folding */
    void OnMarginClick(wxStyledTextEvent &event)
    {
        if (event.GetMargin() == MARGIN_FOLD)
        {
            int lineClick = text->LineFromPosition(event.GetPosition());
            int levelClick = text->GetFoldLevel(lineClick);
 
            if ((levelClick & wxSTC_FOLDLEVELHEADERFLAG) > 0)
            {
                text->ToggleFold(lineClick);
            }
        }
    }
};
 
const char* code = "// Some example\n"
                   "#define MAX 0\n\n"
                   "/** @brief The entry point */\n"
                   "int main(int argc, char *argv[])\n"
                   "{\n"
                   "    for (int n=0; n<MAX; n++)\n"
                   "    {\n"
                   "        printf(\"Hello World %i\\n\", n);\n"
                   "    }\n"
                   "    return 0;\n"
                   "}\n";
 
class MyApp : public wxApp
{
public:
    bool OnInit()
    {
        SourceViewDialog* dlg = new SourceViewDialog(NULL, wxString::FromUTF8(code));
        dlg->Show();
    }
};
 
IMPLEMENT_APP(MyApp)

For lengthier examples, make sure to also check the sample that comes with wxWidgets.

Synchronizing the scrolling of two STCs

This is a common need, for instance to create a diff viewer

#include <wx/wx.h>
#include <wx/stc/stc.h>
 
/** Not really needed here since this example uses a single margin */
enum
{
    MARGIN_LINE_NUMBERS
};
 
 
class SourceViewDialog : public wxDialog
{
    wxStyledTextCtrl* m_text1;
    wxStyledTextCtrl* m_text2;
    bool m_changing_values;
 
 
public:
 
    void onClose(wxCloseEvent& evt)
    {
        EndModal( GetReturnCode() );
    }
 
    wxStyledTextCtrl* createSTC()
    {
        wxStyledTextCtrl* text = new wxStyledTextCtrl(this, wxID_ANY);
 
        text->SetMarginWidth (MARGIN_LINE_NUMBERS, 50);
        text->StyleSetForeground (wxSTC_STYLE_LINENUMBER, wxColour (75, 75, 75) );
        text->StyleSetBackground (wxSTC_STYLE_LINENUMBER, wxColour (220, 220, 220));
        text->SetMarginType (MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER);
 
        text->SetWrapMode (wxSTC_WRAP_WORD);
 
        text->StyleClearAll();
        text->SetLexer(wxSTC_LEX_HTML);
        text->StyleSetForeground (wxSTC_H_DOUBLESTRING,     wxColour(255,0,0));
        text->StyleSetForeground (wxSTC_H_SINGLESTRING,     wxColour(255,0,0));
        text->StyleSetForeground (wxSTC_H_ENTITY,           wxColour(255,0,0));
        text->StyleSetForeground (wxSTC_H_TAG,              wxColour(0,150,0));
        text->StyleSetForeground (wxSTC_H_TAGUNKNOWN,       wxColour(0,150,0));
        text->StyleSetForeground (wxSTC_H_ATTRIBUTE,        wxColour(0,0,150));
        text->StyleSetForeground (wxSTC_H_ATTRIBUTEUNKNOWN, wxColour(0,0,150));
        text->StyleSetForeground (wxSTC_H_COMMENT,          wxColour(150,150,150));
 
        return text;
    }
 
    SourceViewDialog(wxWindow* parent, wxString source) :
    wxDialog(parent, wxID_ANY, "Source Code",
             wxDefaultPosition, wxSize(700,500),
             wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
    {
        m_changing_values = false;
        m_text1 = createSTC();
        m_text1->SetText(source + source + source + source + source + source + source + source);
 
        m_text2 = createSTC();
        m_text2->SetText(source + source + source + source + source + source + source + source);
 
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
        sizer->Add(m_text1, 1, wxEXPAND);
        sizer->Add(m_text2, 1, wxEXPAND);
        SetSizer(sizer);
 
        Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(SourceViewDialog::onClose), NULL, this);
        m_text1->Connect(wxEVT_STC_PAINTED, wxStyledTextEventHandler(SourceViewDialog::onScrollLeft), NULL, this);
        m_text2->Connect(wxEVT_STC_PAINTED, wxStyledTextEventHandler(SourceViewDialog::onScrollRight), NULL, this);
    }
 
 
    void onScrollLeft(wxStyledTextEvent& evt)
    {
        if (m_changing_values) return;
        m_changing_values = true;
 
        int fvl = m_text1->GetFirstVisibleLine();
        if (m_text2->GetFirstVisibleLine() != fvl)
        {
            m_text2->ScrollToLine(fvl);
            // ShowLines
        }
 
        m_changing_values = false;
    }
 
    void onScrollRight(wxStyledTextEvent& evt)
    {
        if (m_changing_values) return;
        m_changing_values = true;
 
        int fvl = m_text2->GetFirstVisibleLine();
        if (m_text1->GetFirstVisibleLine() != fvl)
        {
            m_text1->ScrollToLine(fvl);
            // ShowLines
        }
 
        m_changing_values = false;
    }
};
 
const char* code = "<a>\n"
"<b foo=\"y\">\n"
"<c>\n"
"<d bar=\"x\">\n"
"<e>\n"
"<f>\n"
"<g foobar=\"z\">\n"
"<h>\n"
"<i>\n"
"<j foo=\"w\">\n";
 
class MyApp : public wxApp
{
public:
    bool OnInit()
    {
        SourceViewDialog* dlg = new SourceViewDialog(NULL, wxString::FromUTF8(code));
        dlg->Show();
    }
};
 
IMPLEMENT_APP(MyApp)

Deriving from ScintillaWX

For posterity, there may be a need to make a ScintillaWX derived class under wxWidgets when using the Scintilla Edit Control.

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);
}

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.

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.

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.

Resources

See Also