Design & Implementation of a Win32 Text Editor
Welcome to the brand new tutorial series - "Design and Implementation of a Win32 Text Editor!" The purpose of these tutorials is to follow the development of a win32 text editor - codenamed Neatpad. Each tutorial will take you step-by-step over the major components and design decisions that lay ahead.
Now, whilst the title says "text-editor", this is not the real goal of this tutorial series. Rather, we will aim to write a complete edit control which will be able to provide the core editing front-end for a text editor. Of course, we will have to build a small test application (Neatpad) to test the code with, but we will not go any further than the standard Windows Notepad goes.
Alternative Win32 Solutions
Before we begin you should also check out the following three win32 open-source editor components:
Scintilla in particular looks very nice (it has loads of good features) however all three editors suffer from a large, unwieldy code-base, and all have limitations in their memory-management schemes meaning that large files are not handled particularly smartly.
Text Editor Resources
The following set of resources discuss text editor design rather than individual applications, so you may find it a useful exercise to look through the links and read what's there.
Data Structures For Text Sequences
Compares and describes many different text editor data structures. The author (Charles Crowley) has done a very good job, although no sample code is available. The one that stands out is the "Piece Table" method which is where the original idea for my HexEdit design originated. This will be the method we will use for our own text editor. The link above is quite old and is no longer accessible from the main page. Also the images are missing. However the complete PostScript version may be downloaded here. I also have a PDF version which can be downloaded directly.
The Craft of Text Editing
Discusses issues related to the design of the EMACS text editor. Although a very well-known editor (primarily for it's user-interface) it appears to use a "buffer gap" method so presumably EMACS is not too good for larger files.
Design and Implementation of an Advanced Text Editor
Whilst the text editor described is not advanced at all (calling it "basic" would be an overstatement), the document does contain some interesting ideas about text editors and user interfaces.
Contains a nice selection of links to other text-editing documents and websites.
The very first thing we must do is to make sure that there aren't any text controls already available which will do the job perfectly well. I imagine that anyone who is reading this has come to the same conclusions that I have - the standard Windows edit and rich-edit controls are not up to scratch - they are slow, don't handle large files and have bad flicking problems for a start, and boy do I hate apps that flicker :-)
The primary reason I want to start this project is as a learning exercise - for me and for anyone else who reads this series. So now that we have decided to make our own edit control, the next step is to decide what features the edit control will have. This is important, because these initial design ideas can have big impacts on the final implementation. So, in no particular order, here are the features that I want an edit control to offer:
- Unlimited file size and line length editing with little (or no) degradation in performance for larger files.
- Fast, smooth graphic display. This includes text selection and smooth scrolling with no artifacts or glitches.
- Syntax colouring. Come on, we're programmer's after all, so what good is an edit control that can't syntax-colour our source-code :-)
- Full Clipboard and Drag-and-Drop support.
- Single-font only. We will start by limiting this even further to fixed-width fonts, but with a view to handling variable width fonts if it doesn't look too difficult :-)
- Complete Undo and Redo support.
- ASCII, Unicode and UTF-8 compatible.
I'm sure that I'll dream up extra features as we move through this article series, but for now I think that'll be enough :-)
Choice of language
This is a tough one. Realistically speaking we have the choice between C and C++. C is good because everyone can compile it, whereas C++ has an advantage that is is easier to write this type of control using it. For this tutorial we are actually going to use a mixture of C and C++. The text editor control will be written using C++, but we will provide a "C" interface to the control, because our main application (Neatpad) will be written in "pure win32" C.
Don't expect this control to be available in .NET, or Visual Basic, or any other high-level language. Maybe towards the end of this article series we will look at putting the editor control in an external DLL so that other languages can use it, but for now this is a pure C/C++ solution with no 3rd party libraries.
Design of a Text Editor
A text editor is conceptually very simple. Just take a look at Notepad - all it has is a main window and an "edit" control inside this main window. This is the design we will follow when we are implementing our text editor.
The diagram below illustrates the key components to a text editor. First of all we have a top-level main window. This is the window that contains the title, menu and status bars. The main window doesn't know how to edit text files, it's only purpose is to provide an interface to the user. This component will be written in pure win32 C.
The most important component is the "TextView". This is a separate window that is a child of the main (parent) window. The TextView is the visual component of a text editor. This control's primary purpose is to display and edit text, but it needs to do other things besides this, such as display scrollbars, process mouse and keyboard input, support drag and drop etc.
The "TextDocument" component is not a visible one, but is used to store and manipulate a text file once it has been loaded into memory. The text object has no concept of windows, mice, drawing or painting. All it knows how to do is manipulate text, and supply text to the edit control when it wants to draw something.
The last component is not really part of a text editor, but plays an important role none-the-less. It is the disk-file that stores the text we want to edit. The TextDocument will interface directly with this text file, reading and writing to it when it is time to load or save a new document.
It is important to separate the TextView from the data it stores (the TextDocument). This has two major benefits. One is that we can attach multiple edit controls to one text object - this will enable us to add "split-views" sometime in the future. The biggest advantage is that we can change the way we store and represent a text file at any time without this impacting upon the design of the visual component.
One thing to note on the component design are the "interfaces" between the various components. The TextView is a line-oriented, graphical entity. Most of the work performed by the edit-window will be updating the display. This will entail retreiving data from the TextDocument on a line-by-line basis.
On the other end of the scale is the interface between the TextDocument and the disk-file. Rather than using a line-by-line strategy, the TextDocument will either load the entire file into memory in one go, or access the file in manageable chunks. No matter how this happens, we've got some interesting design decisions ahead :-)
Neatpad - a Win32 Text Editor
At the top of this tutorial there is a link to a zip-file, which contains the sourcecode to a skeleton text editor. I have called this project "Neatpad" in passing reference to the standard Windows Notepad.
When you unzip the download, there will be a single Visual C++ project workspace (neatpad.dsw) and two subdirectories (Neatpad and TextView). The Neatpad directory contains several files, the important ones listed below:
neatpad.c- the main Neatpad application source file.
resource.rc/h- Neatpad's resources.
neatpad.dsp- the Visual Studio project.
It is intended that you work using the main neatpad.dsw workspace in the top-level directory. From this workspace you can build the complete project (Neatpad and TextView). Although the workspace and projects are in Visual Studio 6.0 format, it shouldn't be difficult to convert the projects to other IDEs.
TextView - a Win32 Custom Control
The TextView directory contains the source-code to a skeleton win32 custom control, implemented using C++. The control does nothing apart from display "Hello World" in it's window - in fact most of the code is simply what is required to register and create the empty TextView window.
There are four files in the TextView directory - TextView.dsp, TextView.c, TextView.h and TextViewInternal.h:
TextView.h- the "public interface" to the TextView control.
TextView.c- the main implementation of the TextView window.
TextViewInternal.h- the private header file used by TextView.c, and contains the TextView C++ class definition.
TextView.dsp- the Visual Studio project.
Most of the future tutorials will concentrate on adding functionality to the TextView project.
Public Interface to the TextView
The basic method of controlling the TextView will be Windows Messages, using the SendMessage API, just like you would use with a standard Edit control. To achieve this we need to define a range of message values that we can use. These message values will be defined in the "public" TextView.h, so whenever you want to use the TextView control in your projects, simply #include TextView.h:
#define TEXTVIEW_CLASS "TextView32" #define TXM_BASE (WM_USER) #define TXM_OPENFILE (TXM_BASE + 0)#define TextView_OpenFile(hwndTV, szFile) \ SendMessage((hwndTV), TXM_OPENFILE, 0, (LPARAM)(szFile))
As you can see only one message has been defined so far - TXM_OPENFILE. As we progress through this tutorial and add more functionality to the TextView, so we will add more messages as well. i imagine that our TextView will also support the standard EM_xxx edit control messages as well, so our TextView could be a simple drop-in replacement for that control.
The macro defined above (TextView_OpenFile) provides an alternative interface to the control. It is basically a wrapper around the SendMessage call and makes it easier to use. An example of opening a file is shown below:
Coming up in Part 2
This first part in the tutorial series was really just an introduction to what we are trying to achieve. I have assumed that the reader (you) as at least some knowledge of C and Win32 because you will need a reasonable level of programming experience if you intend to benefit from these tutorials. Please take some time to study the skeleton project download - the code is very simple but it is important that you understand how the various components are all going to plug together.
Future tutorials will begin to flesh out the functionality of the text-view control. As I've been experimenting with text editors I have drawn up a list of steps that I believe will be necessary to cover. The list below will hopefully give you an idea of the sequence of this tutorial series.
- Drawing lines of text
- Scrolling and scrollbars
- Selection and highlighting using the mouse
- Syntax colouring
- Ascii, Unicode and UTF-8
- Keyboard and Mouse input
- Highlighting and text-selection.
- Selection margin and borders.
- Loading large files
- Editing large files
- Undo and Redo
- Clipboard and Drag+Drop
- Scrolling with the mouse
- Searching for data
- Folding editors
I will try and stick to the above topics as closely as possible, but bear in mind that I don't have a fixed project plan, so we will see this text editor evolve incrementally over the next few weeks.