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.
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:
typedef TArray<TPoint> TPoints;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:
typedef TArrayIterator<TPoint> TPointsIterator;
TDrawWindow::TDrawWindow(TWindow *parent) { Init(parent, 0, 0); DragDC = 0; PenSize = 1; Pen = new TPen(TColor::Black, PenSize); Line = new TPoints(10, 0, 10); }
The TArray constructor takes three parameters, all ints:
Line = new TPoints(10, 0, 10);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:
// Construct a TPoints array (an array of TPoint objects) TPoints Points(10, 0, 10); // Construct a TPoint object TPoint p(3,4); // Add the TPoint object p to the array Points.Add(p);
// Clear all members in the Points array Points.Flush();
TPointsIterator i(*Line);
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:
TPointsIterator i(*Line); while(i) i++;
TPointsIterator i(*Line); TPoint tmp(5, 6); if (i.Current() == tmp) return true; else return false;
void TDrawWindow::EvLButtonDown(uint, TPoint& point) { Line->Flush(); Invalidate(); if (!DragDC) { SetCapture(); DragDC = new TClientDC(*this); DragDC->SelectObject(*Pen); DragDC->MoveTo(point); Line->Add(point); } }
void TDrawWindow::EvMouseMove(uint, TPoint& point) { if (DragDC) { DragDC->LineTo(point); Line->Add(point); } }
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:
void Paint(TDC&, bool, TRect&);where:
void TDrawWindow::Paint(TDC& dc, bool, TRect&) { bool first = true; TPointsIterator i(*Line); dc.SelectObject(*Pen); while (i) { TPoint p = i++; if (!first) dc.LineTo(p); else { dc.MoveTo(p); first = false; } } }
EV_COMMAND(CM_FILEOPEN, CmFileOpen),
void CmFileOpen();
#define CM_FILENEW 201 #define CM_FILEOPEN 202 #define CM_FILESAVE 203 #define CM_FILESAVEAS 204 #define CM_ABOUT 205These 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.
#ifdef RC_INVOKED // Resource definitions here. #endifRC_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 resources
For now, you want to add five menu choices to the application:
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:
void InitMainWindow() { SetMainWindow(new TFrameWindow(0, "Drawing Pad", new TDrawWindow)); GetMainWindow()->AssignMenu("COMMANDS"); }
EV_COMMAND(CM_FILENEW, CmFileNew), EV_COMMAND(CM_FILEOPEN, CmFileOpen), EV_COMMAND(CM_FILESAVE, CmFileSave), EV_COMMAND(CM_FILESAVEAS, CmFileSaveAs), EV_COMMAND(CM_ABOUT, CmAbout),
The declarations of these function should look something like this:
void CmFileNew(); void CmFileOpen(); void CmFileSave(); void CmFileSaveAs(); void CmAbout();
void TDrawWindow::CmFileOpen() { MessageBox("Feature not implemented", "File Open", MB_OK); }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:
void TDrawWindow::CmFileNew() { Line->Flush(); Invalidate(); }