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

WTL wrappers for WinMain's CodeMax syntax highlighting edit control

0.00/5 (No votes)
28 May 2001 1  
An article on the CodeMax editing control.

Sample Image

Introduction

When I first started writing this article, I had planned only to illustrate how to use the CodeMax syntax editing control wrappers I had created. I quickly found though, that I would also need to explain what it was I was doing in the demo application itself. So, instead of simply releasing the set of wrappers and a demo program, I decided to write a small article on the CodeMax wrappers, meanwhile expanding on some of the things I discovered while writing an MDI application using the Windows Template Library (WTL).

Let me start by telling you a little about how the sample code is laid out. If you've ever used the WTL wizard, you are familiar with the fact that it shoves all of the application's code into a set of header files. While I like to put my components into a single header file, to simplify integration, I hate to work with application code that is set-up in this manner. So, the first thing I did was split the code up into its respective source and header files. This gives a much cleaner implementation, and in my opinion, it is easier to read and edit.

Unfortunately, by doing this, I destroyed any chance I might have had of using the 'Add Windows Message Handler' wizard that Visual C++ was nice enough to provide. But, I also opted to use the 'extended' message map (BEGIN_MSG_MAP_EX, etc.) that WTL implements, which is also incompatible with the above-mentioned wizard. So, in the end, it would have been useless anyway.

The CodeMax Wrappers

The wrappers that I included encapsulate almost everything CodeMax has to offer. I say, almost, because I have yet to implement a central way of updating the CodeMax control's settings (when you run the sample, you will see what I mean).

The classes are all defined in the cmaxwtl.h file, and all of the classes are declared within the cmax namespace.

  • CodeMaxLibrary - Wraps all the basic CodeMax library calls, including registration and unregistration.
  • CodeMaxControl - Wraps the CodeMax control itself.
  • CodeMaxControlNotifications - Base class that allows you to handle notification messages from the CodeMax control (reflected or not).
  • CodeMaxControlCommands - Base class that handles the standard CodeMax commands (cut, copy, paste, etc.).
  • UndoBlock - Simple undo helper, allows you to group a series of undoable commands as one.

How to use the wrappers

First, you will need to declare an instance of the CodeMaxLibrary object and initialize it.

// ...

CodeMaxLibrary cmaxlib;

if ( !cmaxlib.Initialize () ) {

    ATLTRACE ( _T ( "CodeMax initialization failed!\n" ) );
    
    return 0; // bail...

}     

// ...

While it does not really matter where you do this, it's best to do it early off in the game so as to avoid any hassles later on. I recommend you do it in your WinMain function or the Run function that the wizard declares for you.

Next, you will need to modify your 'view' or client window, and derive it from the CodeMaxControl class.

Note that you need to use DECLARE_WND_SUPERCLASS() in the class declaration. This allows you to superclass the window, and thereby create a window based on the CodeMax control with a new window procedure that you will later define.

class CCodeMaxWTLSampleView : 
      public CWindowImpl < CCodeMaxWTLSampleView, CodeMaxControl >
{  
   // ...
   
public:

   // Superclass the CodeMax control   

   DECLARE_WND_SUPERCLASS ( NULL, CodeMaxControl::GetWndClassName () )
   
   // ...

}

I have also included the CodeMaxControlNotifications class to allow you to handle the CodeMax control's notification messages. This class can be used as a base for either the view or the main frame. In the sample, I use it in the view, but there may be some good reasons to use it in the application's main frame. If you do decide to use it in the main frame of the application, be sure to forward all the child window's messages to the main frame. In the case of an MDI application, placing a call to FORWARD_NOTIFICATIONS() in your MDI child's message map will do this.

Our new view, with notification handling, would look something like this:

class CCodeMaxWTLSampleView : public CWindowImpl < CCodeMaxWTLSampleView, CodeMaxControl >,
                              public CodeMaxControlNotifications < CCodeMaxWTLSampleView >
{
   // ...
   
public:

   // Superclass the CodeMax control   

   DECLARE_WND_SUPERCLASS ( NULL, CodeMaxControl::GetWndClassName () )
   
  // ...

};

Of course, you still have to reflect the notification messages back to the view from its parent window and chain the notification class' message map in your view's message map.

Reflection

In MFC, we did not have to really worry about reflecting messages. All one needed to do was subclass a window or control, and all of its notification messages would be reflected back for you. In WTL, this is not the case, you actually have to reflect the messages back from the parent window to its child. Luckily, this is not all that difficult, since ATL provides us with a quick and simple method of doing this. All you have to do is remember to add REFLECT_NOTIFICATIONS() to your parent window's message map. The beauty of this macro is that it allows you to handle everything about a window within the class that defines it.

The following piece of code demonstrates how to reflect messages back to the child that sent them:

class CChildFrame : public CMDIChildWindowImpl < CChildFrame >
{ 
  
   // ...
   
   BEGIN_MSG_MAP_EX ( CChildFrame )
   
      // ...
      //
      // Notification handlers
      //      

      // Reflect all the WM_NOTIFY messages to the client window

      REFLECT_NOTIFICATIONS ()
      
   END_MSG_MAP ()
   
   // ...
  
};

Message Changing

One of the most powerful concepts offered for window centric programming, by ATL and WTL alike, is message changing. This allows you to divide the already flexible message-handling scheme into smaller, more manageable parts. What this enabled me to do was provide CodeMaxControlCommands, a class that handles all of Windows' standard command messages. Basic handling is provided for commands such as Cut & Paste and Undo/Redo.

To reap the benefit of this class, you can either derive your view from it, or as with the notification handler class, the main frame window. As you may have gathered, this is not all that you have to do. You must also make sure that the command messages are passed down from the main frame to the view. Remember that if you are using an MDI interface, the messages must first pass through the child frame before they arrive at the view.

The following macros will make this possible (both of which are defined in atlframe.h):

  • CHAIN_MDI_CHILD_COMMANDS()
  • CHAIN_CLIENT_COMMANDS()

The first one goes in the main frame's message map, where it simply passes control to the active MDI child window. The second picks up the control from the main frame and passes it to the child's client control (namely the view).

The main frame window:

class CMainFrame : public CMDIFrameWindowImpl < CMainFrame >, 
                   public CUpdateUI < CMainFrame >,
                   public CMessageFilter, 
                   public CIdleHandler
{ 
   
   // ...
   
   BEGIN_MSG_MAP_EX ( CMainFrame )
   
      // ...
      //
      // Chained Message maps
      //        

      // Pass all unhandled WM_COMMAND messages to the active child window
      CHAIN_MDI_CHILD_COMMANDS ()
         // other chains...
     
   END_MSG_MAP ()
   
   // ...
  
};

The child frame window:

class CChildFrame : public CMDIChildWindowImpl < CChildFrame >
{ 
   // ...
  
   BEGIN_MSG_MAP_EX ( CChildFrame )
   
      // ...
      //
      // Chained Message maps
      //
      
      // Pass all unhandled WM_COMMAND messages to the client window or 'view'

      CHAIN_CLIENT_COMMANDS ()
         // other chains...
      
      // ...
   
   END_MSG_MAP ()
   
   // ...
   
};

We are now free to handle any or all of the command messages in the view.

The final rendition of our view would look something like this:

class CCodeMaxWTLSampleView : 
      public CWindowImpl < CCodeMaxWTLSampleView, CodeMaxControl >,
      public CodeMaxControlNotifications < CCodeMaxWTLSampleView >,
      public CodeMaxControlCommands < CCodeMaxWTLSampleView > 
{  
   // ...

public:

   // Superclass the CodeMax control   
   DECLARE_WND_SUPERCLASS ( NULL, CodeMaxControl::GetWndClassName () )
   
   // View's message map
   BEGIN_MSG_MAP_EX ( CCodeMaxWTLSampleView )    
   
      // ...
      //
      // Chained Message maps
      //

      // Make sure that the notification
      // and default message handlers get a crack at the messages

      CHAIN_MSG_MAP_ALT ( CodeMaxControlNotifications < CCodeMaxWTLSampleView >, 
           CMAX_REFLECTED_NOTIFY_CODE_HANDLERS )
      CHAIN_MSG_MAP_ALT ( CodeMaxControlCommands < CCodeMaxWTLSampleView >, 
           CMAX_BASIC_COMMAND_ID_HANDLERS )
      
   END_MSG_MAP ()    
        
   // ...
}

The End

Well, that about wraps it up (pardon the pun); if I've forgotten something, please let me know. For those of you doing the MFC thing, I have full MFC versions of the wrappers as well... if you'd like them, just drop me a line; if the demand is high enough I'll post them.

Wanted

  1. Loosely tied mechanism to update the CodeMax control from outside the wrapper classes.
  2. A way to load and modify custom languages from disk. (I have included the beginnings of my attempt to do this, but be warned, it breaks so many rules, it's unreal. I am positive there is a more elegant way of doing it.)

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