Step 5: Changing line thickness
You can find the source for Step 5 in the files STEP05.CPP and STEP05.RC
in the directory EXAMPLES\OWL\TUTORIAL. In this step, you'll make the drawing capability
in the application a little more robust. This step adds the ability to change the
thickness of the line. To support this, you can add to the TDrawWindow class a TPen *
drawing object and an int to hold the pen width.
Adding a pen
Add the pen to the window class by adding two protected
members, Pen (a TPen *) and PenSize (an int). The most important changes
that result from adding a pen to the window class are implemented in the EvLButtonDown and
EvRButtonDown functions.
Initializing the pen
The Pen object and PenSize must be created and initialized before
the user has an opportunity to draw with the pen. The best place to do this is in the
constructor:
TDrawWindow::TDrawWindow(TWindow *parent)
{
Init(parent, 0, 0);
DragDC = 0;
PenSize = 1;
Pen = new TPen(TColor::Black, PenSize);
}
The TColor::Black object in the TPen
constructor is an enum defined in the owl\color.h header file. This makes the pen
black. You'll learn more about this parameter of the TPen constructor later on in Step 9.
Selecting the pen into DragDC
To use the new pen object to draw a line, the pen has to be selected
into the device context. The device-context classes have a function called SelectObject.
This function is similar to the API function SelectObject, except that the ObjectWindows
version doesn't require a handle to the device context.
You can use SelectObject to select a variety of objects into a device
context, including brushes, fonts, palettes, and pens. You need to call SelectObject
before you begin to draw. Add the call in the EvLButtonDown function immediately after you
create the device context:
void
TDrawWindow::EvLButtonDown(uint, TPoint& point)
{
Invalidate();
if (!DragDC) {
SetCapture();
DragDC = new TClientDC(*this);
DragDC->SelectObject(*Pen);
DragDC->MoveTo(point);
}
}
Notice that Pen is dereferenced in the
SelectObject call. This is because the SelectObject function takes a TPen & for
its parameter, and Pen is a TPen *. Dereferencing the pointer makes Pen comply with
SelectObject's type requirements.
Changing the pen size
Having the ability to change the pen size in the application is of
little use unless the user has access to that ability. To provide that access, you can
change the meaning of pressing the right mouse button. Instead of clearing the screen, it
now indicates that the user wants to change the width of the drawing pen. Therefore the
process of changing the pen size goes into the EvRButtonDown function.
Once the user has indicated that he or she wants to change the pen
width by pressing the right mouse button, you need to find some way to let the user enter
the new pen width. For this, you can pop up a TInputDialog, in which the user can input
the pen size.
Constructing an input dialog box
The TInputDialog constructor looks like this:
TInputDialog(TWindow* parent,
const char far* title,
const char far* prompt,
char far* buffer,
int bufferSize,
TModule* module = 0);
where:
To use TInputDialog, you
must make sure its resources and resource identifiers are included in your source files
and resource script files. These are contained in the file INCLUDE\OWL\INPUTDIA.RC. You
should include INPUTDIA.RC in your resource script files and your C++ source files.
Executing an input dialog box
Once you've constructed a TInputDialog object, you can either call
the TDialog::Execute function to execute the dialog box modally or the TDialog::Create
function to execute the dialog box modelessly. Because there's no need to execute the
dialog box modelessly, you can use the Execute function.
The Execute function for TInputDialog can return two important
values, IDOK and IDCANCEL. The value that is returned depends on which button the user
presses. If the user presses the OK button, Execute returns IDOK. If the user presses the
Cancel button, Execute returns IDCANCEL. So when you execute the input dialog box, you
need to make sure that the return value is IDOK before changing the pen size. If it's not,
then leave the pen size the same as it is.
If the call to Execute does return IDOK, the new value for PenSize is
in the string passed in for the dialog's buffer. Before this can be used as a pen size, it
must be converted to an int. Then you should make sure that the value you get from
the buffer is a valid pen width. Finally, once you're sure that the input from the user is
acceptable, you can change the pen size. TDrawWindow now has a function called SetPenSize
that you can use to change the pen size. The reason for doing it this way, instead of
directly modifying the pen, is explained in the next section.
The EvRButtonDown function should now look something like this:
void
TDrawWindow::EvRButtonDown(uint, TPoint&)
{
char inputText[6];
wsprintf(inputText, "%d", PenSize);
if ((TInputDialog(this, "Line Thickness",
"Input a new thickness:",
inputText,
sizeof(inputText))).Execute() == IDOK) {
int newPenSize = atoi(inputText);
if (newPenSize < 0)
newPenSize = 1;
SetPenSize(newPenSize);
}
}
Calling SetPenSize
To change the pen size, use the SetPenSize function. Although the
EvRButtonDown function is a member of TDrawWindow, and as such has full access to the protected
data members Pen and PenSize, it is better to establish a public access function to make
the actual changes to the data. This becomes more important later, when the pen is
modified more often.
For TDrawWindow, you have the public SetPenSize function. The
SetPenSize function takes one parameter, an int that contains the new width for the
pen. After opening the input dialog box, processing the input, and checking the validity
of the result, all you need to do is call SetPenSize.
SetPenSize is a fairly simple function. To resize the pen, you must
first delete the existing pen object. Then set PenSize to the new size. Finally construct
a new pen object with the new pen size. The function should look something like this:
void
TDrawWindow::SetPenSize(int newSize)
{
delete Pen;
PenSize = newSize;
Pen = new TPen(TColor(0,0,0), PenSize);
}
Cleaning up after Pen
Because Pen is a pointer to a TPen object, and not an actual TPen
object, it isn't automatically destroyed when the TDrawWindow object is destroyed. You
need to explicitly destroy Pen in the TDrawWindow destructor to properly clean up. The
only thing required is to call delete on Pen. TDrawWindow should now look something
like this:
class TDrawWindow : public TWindow
{
public:
TDrawWindow(TWindow *parent = 0);
~TDrawWindow() {delete DragDC; delete Pen;}
void SetPenSize(int newSize);
protected:
TDC *DragDC;
int PenSize;
TPen *Pen;
// Override member function of TWindow
bool CanClose();
// Message response functions
void EvLButtonDown(uint, TPoint&);
void EvRButtonDown(uint, TPoint&);
void EvMouseMove(uint, TPoint&);
void EvLButtonUp(uint, TPoint&);
DECLARE_RESPONSE_TABLE(TDrawWindow);
};
Where to find more information
Here's a guide to where you can find more information on the topics
introduced in this step:
|