|
There are a few flaws with the application from Step 5. The biggest problem is that the drawing window doesn't know how to paint itself. To see this for yourself, try drawing a line in the window, minimizing the application, then restoring it. The line you drew is gone.You can find the source for Step 6 in the files STEP06.CPP and STEP06.RC in the directory EXAMPLES\OWL\TUTORIAL. Another problem is that the only way the user can access the application is with the mouse. The user can either press the left button to draw a line or the right button to change the pen size. In Step 6, you'll make it possible for the application to remember the contexts of the window and redraw it. You'll also add some menus to increase the number of ways the user can access the application. Repainting the windowThere are two problems that must be dealt with when you're trying to paint the window:
Storing the drawingIn the earlier steps of the tutorial application, the line in the window was drawn as the user moved the mouse while holding the left mouse button. This approach is fine for drawing the line, but doesn't store the points in the line for later use. Because the line is composed of a number of points in the window, you can store each point in the ObjectWindows TPoint class. And because each line is composed of multiple points, you need an array of TPoint objects to store a line. Instead of attempting to allocate, manage, and update an array of TPoint objects from scratch, the tutorial application uses the Borland container class TArray to define a data type called TPoints. It also uses the Borland container class TArrayIterator to define an iterator called TPointsIterator. The definitions of these two types look like this:
The TDrawWindow class adds a TPoints object in which it can store the points in the line. It actually uses a TPoints *, a protected member called Line, which is set to point to a TPoints array created in the constructor. The constructor now looks something like this:
TPointsThe Borland C++ container class library and the TArray and TArrayIterator classes are explained in detail in Chapter 1 of the Class Libraries Guide. For now, here's a simple explanation of how the TPoints and TPointsIterator container classes are used in the tutorial application. To use the TArray and TArrayIterator classes, you must include the header file classlib\arrays.h. The TArray constructor takes three parameters, all ints:
Here's the statement that allocates the initial array of points in the TDrawWindow constructor:
The array of points is created with room for ten members, beginning at 0. Once ten objects are stored in the array, attempting to add another object adds room for ten new members to the array. This lets you start with a small conservative array size, but also alleviates one of the main problems normally associated with static arrays, which is running out of room and having to reallocate and expand the array. Once you've created an array, you need to be able to manipulate it. The TArray class (and, by extension, the TPoints class) provides a number of functions to add members, delete members, clear the array, and the like. The tutorial application uses only a small number of the functions provided. Here's a short description of each function:
TPointsIteratorIterators-in this case the TPointsIterator type-let you move through the array, accessing a single member of the array at a time. An iterator constructor takes a single parameter, a reference to a TArray of objects (the type of objects in the array is set up by the definition of the iterator). Here's what an iterator looks like when it's set up using the Line member of the TDrawWindow class:
Note that Line is dereferenced because the iterator constructor takes a TPoints & for its parameter, and Line is a TPoints *. Dereferencing the pointer makes Line comply with the iterator constructor type requirements. Once you've created an iterator, you can use it to access each object in the array, one at a time, starting with the first member. In the tutorial application, the iterator isn't used very much and you won't learn much about the possibilities of an iterator from it. But the tutorial does use two properties of iterators that require a note of explanation:
Using the array classesOnce the Line array is created in the TDrawWindow constructor, it is accessed in four main places:
Paint functionIn standard C Windows programs, if you need to repaint a window manually, you catch the WM_PAINT messages and do whatever you need to do to repaint the screen. This might lead you to think that the proper way to repaint the window in the TDrawWindow class is to add the EV_WM_PAINT macro to the class' response table and set up a function called EvPaint. You can do this if you want. However, a better way is to override the TWindow function Paint. TDrawWindow's base class TWindow actually does quite a bit of work in its EvPaint function. It sets up the BeginPaint and EndPaint calls, creates a device context for the window, and so on. Paint is a virtual member of the TWindow class. TWindow's EvPaint calls it in the middle of its processing. The default Paint function doesn't do anything. You can use it to provide the special processing required to draw a line from a TPoints array. Here is the signature of the Paint function. This is added to the TDrawWindow class:
where:
In the current case, you always want to clear the window. You can also assume that the entire area of the drawing needs to be repainted. The Paint function implements this basic algorithm:
Menu commandsThere are a number of steps you need to perform to add a menu choice and its corresponding event handler to your application:
Adding event identifiersYou need to add identifiers for each of these menu choices. Here's the definition of the event identifiers:
These identifiers are contained in the file STEP06.RC. The ObjectWindows style places the definitions of identifiers in the resource script file, instead of a header file. This cuts down on the number of source files required for a project, and also makes it easier to maintain the consistency of identifier values between the resources and the application source code. The actual resource definitions in the resource file are contained in a block contained in an #ifndef/#endif block, like so:
RC_INVOKED is defined by all resource compilers, but not by C++ compilers. The resource information is never seen during C++ compilation. Identifier definitions should be placed outside this #ifndef/#endif block, usually at the beginning of the file. Adding menu resourcesFor now, you want to add five menu choices to the application:
Each of these menu choices needs to associated with the correct event identifier; that is, the File Open menu choice should send the CM_FILEOPEN event. The menu resource is attached to the application in the InitMainWindow function. You need to call the main window's AssignMenu function. To get the main window, you can call the GetMainWindow function. The InitMainWindow function should look like this:
Adding response table entriesEach event identifier needs to be associated with its corresponding handler. To do this, add the following lines to the response table:
Adding event handlersNow you need to add a function to handle each of the events you've just added to the response table. Because these functions will eventually grow rather large, you should declare them in the class declaration and define them outside the class declaration. The declarations of these function should look something like this:
Implementing the event handlersThe last step in implementing the event handlers is defining the functions. For now, leave the implementation of these functions to a bare minimum. Most of them can just pop up a message box saying that the function has not yet been implemented. The functions that are set up this way are CmFileOpen, CmFileSave, CmFileSaveAs, and CmAbout. Here's how these functions look:
The only function that's implemented in this step is the CmFileNew function. That's because it's very easy to set up. All that needs to be done is to clear the array of points and erase the window. The CmFileNew function looks like this:
Where to find more informationHere's a guide to where you can find more information on the topics introduced in this step:
|
|||||||||||||
Last updated: NA |