Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An Idea How to Use RichEdit50W for Syntax Highlighting, etc.

0.00/5 (No votes)
16 Sep 2015 2  
Masm code editor with syntax highlighting, etc.

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:

  • Document.cpp
  • Tab.cpp

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;		//1=proc, 2=.if, 3=.while
	DWORD		compressed;	//0=expanded, nonezero=compressed
	DWORD		nestLevel;
	CHARRANGE	cr;
	UINT		compLines;	//number of compressed lines
}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:

  • DrawIndentlines.cpp

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;	//pointer to dictionary key string
	LPSTR	pValue;	//pointer to dictionary value string
}KEY, *LPKEY;
typedef struct tagDICTIONARY{
	LPKEY	pKeys;		//pointer to dictionary keys and values jump table
	LPBYTE	pKeyList;	//pointer to dictionary key list
	LPBYTE	pValuesList;	//pointer to dictionary value list
	UINT	cItems;		//number of dictionary items
}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;		//starting charrange
	CHARRANGE	undoCr;		//charrange before action
	CHARRANGE	redoCr;		//charrange after action
	CHARRANGE	outCr;
	LPSTR		pUndoText;	//pointer to selected text to be replaced
	LPSTR		pRedoText;	//pointer to new text to replace
	struct tagUNDO * pDropUndo;	//pointer to drop undo structure
}UNDO, *LPUNDO;

Files:

  • Undo.cpp

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){
		//delete moving text from the original position
		::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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here