[Contents] [Prev] [Next]

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:

TDrawWindow::EvLButtonDown(uint,  TPoint&  point)

    if  (!DragDC)  {
        DragDC  =  new  TClientDC(*this);
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);

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:

TDrawWindow::EvRButtonDown(uint,  TPoint&)
    char  inputText[6];

    wsprintf(inputText,  "%d",  PenSize);
    if  ((TInputDialog(this,  "Line  Thickness",
                        "Input  a  new  thickness:",
                        sizeof(inputText))).Execute()  ==  IDOK)  {
        int    newPenSize  =  atoi(inputText);

        if  (newPenSize  <  0)
            newPenSize  =  1;


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:

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
        TDrawWindow(TWindow  *parent  =  0);
      ~TDrawWindow()  {delete  DragDC;  delete  Pen;}

        void  SetPenSize(int  newSize);

        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&);


Where to find more information

Here's a guide to where you can find more information on the topics introduced in this step:

[Contents] [Prev] [Next]