Introduction
In the last 15 years, I have programmed in assembler language as a hobby. The latest project I've been working on is an editor based on Microsoft RichEdit control. Although I have been in contact with the C / C ++ language, I have never learned it. Hence I planned to try to also write the editor code in C / C ++. Which I hereby report in this article.
Microsoft Macro Assembler (MASM)
The assembler project is written in MASM language. For those interested, you can read the ASM code on my website (minor28.divdev.se). The two project's files, functions and variables are on the whole consistent so it would be quite easy to follow.
Editor
I started using RichEdit20
. It did not work satisfactorily for large files so I chose RichEdit50W
instead. The application can have multiple documents open but is not an MDI project. Only one control that is used for all documents.
Some application data is stored in the registry (HKEY_CURRENT_USER\Software\MINOR28\RichEditor).
There is also a dialog for setting of colors, fonts and keywords. Max undos is set to 100 but is not programmed to be changed.
Files:
- RichEditor.cpp
- ApplicationData.cpp
- SubRichEditProc.cpp
- OptionDlgProc.cpp
- OptionTabs.cpp
Document
A document is the text content of an open file. Showing and hiding documents are performed with respective document's button. A hidden document is temporarily stored according to the following structure:
typedef struct tagARCHIVE{
UINT DocId;
LPSTR pRTF;
INT firstvisibleline;
CHARRANGE cr;
UINT margin;
CHAR FileName[MAX_PATH];
LPDOCUNDOS pDocUndos;
}ARCHIVE, *LPARCHIVE;
Files:
Line Numbering
Line numbers and outlinings are painted on RichEdit
process command WM_PAINT
. A number margin and an outlining margin are reserved on the left side of the RichEdit
client area. A bitmap is created for each margin to paint line numbers and outlining markers.
Files:
- DrawLineNumbersAndOutlinings.cpp
Outlining
Hiding and showing selected text is a build in feature of the RichEdit
control. Outlining is how the application handles this feature. In this case, there are three types of keywords that can trigger the hide/show operation, namely "Proc - endp
", ".if - .endif
" and ".while - .endw
".
Each document has a buffer to hold data for the outlinings.
typedef struct tagOUTLINING{
DWORD Type; DWORD compressed; DWORD nestLevel;
CHARRANGE cr;
UINT compLines; }OUTLINING, *LPOUTLINING;
typedef struct tagDOCOUTLININGS{
UINT cOutlinings;
LPOUTLINING pOutlinings;
INT docID;
UINT maxNum;
}DOCOUTLININGS, *LPDOCOUTLININGS;
Files:
- DrawLineNumbersAndOutlinings.cpp
- OutliningMarkers.cpp
Indent Markers
Indent markers are drawn on a transparent bitmap above the text area. This bitmap is created and drawn with the GDI+ API. A PostMessage
to RichEdit
command WM_PRINTCLIENT
after each paint operation will draw these markers.
Files:
Syntax Highlighting
I use the control's own rich text format to color the keywords. I retrieve the RTF header from a blank document and complete the header with a color table that can be chosen for the various keywords. All text are inserted in RTF format.
The keywords are stored in four separate text files where the keywords are separated by a space. All keywords are loaded into a dictionary when starting the application.
typedef struct tagKEY{
LPSTR pKey; LPSTR pValue; }KEY, *LPKEY;
typedef struct tagDICTIONARY{
LPKEY pKeys; LPBYTE pKeyList; LPBYTE pValuesList; UINT cItems; }DICTIONARY, *LPDICTIONARY;
The text is converted to RTF by a scanner and a lexer which locates each keyword and a token provider marks the tokens with the right color.
typedef struct tagTOKEN{
DWORD _Type;
COLORREF TxtCol;
DWORD Style;
LPSTR pText;
CHARRANGE cr;
DWORD Len;
}TOKEN, *LPTOKEN;
Files:
- CreateDictionary.cpp
- FillDictionary.cpp
- TokenProvider.cpp
- Lexer.cpp
- RTFHeader.cpp
- Scanners.cpp
Undo / Redo
This is a built in feature. In this application, however, it is disabled due to each action involving several steps. Therefore, each document will have a custom buffer for the undo / redo actions.
Each document has a structure like this:
typedef struct tagDOCUNDOS{
UINT docID;
UINT isDirty;
UINT cUndo;
LPUNDO pUndo;
UINT cRedo;
LPUNDO pRedo;
}DOCUNDOS, *LPDOCUNDOS;
and each undo/redo action has a structure like this:
typedef struct tagUNDO{
DWORD action;
CHARRANGE inCr; CHARRANGE undoCr; CHARRANGE redoCr; CHARRANGE outCr;
LPSTR pUndoText; LPSTR pRedoText; struct tagUNDO * pDropUndo; }UNDO, *LPUNDO;
Files:
Drop Files
This is also a build in feature. Calling the function DragAcceptFiles
with the fAccept
parameter set to TRUE
makes it able to process the WM_DROPFILES
message from File Manager.
Drag and Drop
This is a build in feature. With the same reason as undo/redo, this feature is also disabled and a custom dragdrop is created. Dragdrop is based on com technology. If the control is already registred for OLE dragdrop operations, the registration is revoked and then registred with a customized IDropTarget
interface.
::OleInitialize(0);
if (::RegisterDragDrop(hRED1,(LPDROPTARGET)&pDropTarget)
==DRAGDROP_E_ALREADYREGISTERED){
::RevokeDragDrop(hRED1);
::RegisterDragDrop(hRED1,(LPDROPTARGET)&pDropTarget);
}
The control window messages WM_LBUTTONDOWN
, WM_MOUSEMOVE
and WM_LBUTTONUP
will carry out an OLE dragdrop
operation.
if (::DoDragDrop((LPDATAOBJECT)&pDataObject,(LPDROPSOURCE)&pDropSource,
DROPEFFECT_COPY | DROPEFFECT_MOVE,&pdwEffect) == DRAGDROP_S_DROP){
if (pdwEffect == DROPEFFECT_MOVE && External==1){
::SendMessage(hRED1,WM_CHAR,VK_DELETE,0);
}
}
The dragdrop
operations are handled in the customized methods of dragdrop
interfaces.
Files:
- IDataObject.cpp
- IDataSource.cpp
- IDropTarget.cpp
- IEnumFORMATETC.cpp
- IUndknow.cpp
- DrawDragDropMarker.cpp
Drag Document
This feature has nothing to do with dragdrop
operations. If you drag the document button, three different cursors can appear:
The drag operation is within the editor window. No effect.
The drag operation is within another opened editor window. The document is transfered to the new editor.
The drag operation is outside any editor window. A new editor is opened and the document is transferred to that editor.
typedef struct tagDOCUMENTEXPORT{
INT WinX1;
INT WinY1;
INT firstvisibleline;
CHARRANGE cr;
UINT margin;
CHAR FileName[MAX_PATH];
}DOCUMENTEXPORT, *LPDOCUMENTEXPORT;
Assembly vs C/C++
I think that the two languages are quite similar to write. Calculations are much easier to do in C than in assembly. However, I think it is complicated with all the data types that you have to keep right on in C and all the header files with variables declarations and function prototypes scattered in many places. Here is the assembly far more orderly.
Concluding Remarks
You may take it as it is. The application is probably not free from bugs. I believe I have structured the files and code so that it will be quite easy to adapt the project to any language.