class TDrawWindow : public TWindow { public: TDrawWindow(TWindow* parent = 0); protected: // override member function of TWindow bool CanClose(); // message response functions void EvLButtonDown(uint, TPoint&); void EvRButtonDown(uint, TPoint&); DECLARE_RESPONSE_TABLE(TDrawWindow); };The constructor for this class is fairly simple. It takes a single parameter, a TWindow * that indicates the parent window of the object. The constructor definition looks like this:
TDrawWindow::TDrawWindow(TWindow *parent) { Init(parent, 0, 0); }The Init function lets you initialize TDrawWindow's base class. In this case, the call isn't very complicated. The only thing that might be required for your purposes is the window's parent, and, as you'll see, even that's taken care of for you.
The response table definition sets up your class to handle Windows events and to pass each event on to the proper event-handling function. As a general rule, event-handling functions should be protected; this prevents classes and functions outside your own class from calling them. Here is the response table definition for TDrawWindow:
DEFINE_RESPONSE_TABLE1(TDrawWindow, TWindow) EV_WM_LBUTTONDOWN, EV_WM_RBUTTONDOWN, END_RESPONSE_TABLE;You can put the response table anywhere in your source file.
For now, you can keep the response table fairly simple. Here's a description of each part of the table. A response table has four important parts:
These predefined macros pass the message on to functions with predefined names. To determine the function name, substitute Ev for WM_, and convert the name to lowercase with capital letters at word boundaries. For example, the WM_PAINT message is passed to a function called EvPaint, and the WM_LBUTTONDOWN message is passed to a function called EvLButtonDown.
The functions that handle the WM_LBUTTONDOWN or WM_RBUTTONDOWN events are very simple. Each function pops up a message box telling you which button you've pressed. The code for these functions should look something like this:
void TDrawWindow::EvLButtonDown(uint, TPoint&) { MessageBox("You have pressed the left mouse button", "Message Dispatched", MB_OK); } void TDrawWindow::EvRButtonDown(uint, TPoint&) { MessageBox("You have pressed the right mouse button", "Message Dispatched", MB_OK); }This illustrates one of the best features of how ObjectWindows handles standard Windows events. The function that handles each event receives what might seem to be fairly arbitrary parameter types (all the macros and their corresponding functions are presented in Chapter 5 in the ObjectWindows Reference Guide). Actually, these parameter types correspond to the information encoded in the WPARAM and LPARAM variables normally passed along with an event. The event information is automatically "cracked" for you.
The advantages of this approach are two-fold:
From the point of view of the application, this ensures that you don't shut down a window that is currently being used or that contains unstored data. From the window's point of view, this warns you when the application tries to shut down and provides you with an opportunity to make sure that everything has been cleaned up before closing.
Here is the CanClose function from the TDrawWindow class:
bool TDrawWindow::CanClose() { return MessageBox("Do you want to save?", "Drawing has changed", MB_YESNO | MB_ICONQUESTION) == IDNO; }For now, this function merely pops up a message box stating that the drawing has changed and asking if the user wants to save the drawing. Because there's no drawing to save, this message is fairly useless right now. But it'll become useful in Step 7, when you add the ability to save data to a file.
Using TDrawWindow as the main window
The last thing to do is to actually create an instance of this new TDrawWindow class. You might think you can do this by simply substituting TDrawWindow for TFrameWindow in the SetMainWindow call in the InitMainWindow function:
void InitMainWindow() { SetMainWindow(new TDrawWindow); }This won't work, for a number of reasons, but primarily because TDrawWindow isn't based on TFrameWindow. For this code to compile correctly, you'd have to change TDrawWindow so that it's based on TFrameWindow instead of TWindow. Although this is fairly easy to do, it introduces functionality into the TDrawWindow class that isn't necessary. As you'll see in later steps, TDrawWindow has a unique purpose. Adding frame capability to TDrawWindow would reduce its flexibility.
The second approach is to use a TDrawWindow object as a client in a TFrameWindow. This is fairly easy to do: the third parameter of the TFrameWindow constructor that you're already using lets you specify a TWindow or TWindow-derived object as a client to the frame. The code would look something like this:
Where to find more information
Here's a guide to where you can find more information on the topics introduced in this step: