Step 9: Changing pens
You can find the source for Step 9 in the files STEP09.CPP and STEP09.RC
in the directory EXAMPLES\OWL\TUTORIAL. In Step 9, you'll add a TColor member to the TLine
class, letting the user draw with lines of different widths and different colors. To
change the color of the line, you'll add the CmPenColor function. This function handles
the CM_PENCOLOR menu command. CmPenColor uses the TChooseColorDialog class to let the user
change colors. It also adds some helper functions to deal with changes to the width and
color and give external classes access to information about the line.
Along with adding color to the pen, Step 9 adds functionality to the
streaming operators to deal with the new attributes of the TLine class. It also adds a
Draw function to the TLine class to make the class more self-sufficient and to make the
Paint function simpler.
Changes to the TLine class
A number of changes to the TLine class declaration are required to
accommodate the new functionality:
Here's how the new TLine
class declaration should look:
class TLine : public TPoints {
public:
// Constructor to allow construction from a color and a pen size.
// Also serves as default constructor.
TLine(const TColor &color = TColor(0), int penSize = 1)
: TPoints(10, 0, 10), PenSize(penSize), Color(color) {}
// Functions to modify and query pen attributes.
int QueryPenSize() { return PenSize; }
TColor& QueryColor() { return Color; }
void SetPen(TColor &newColor, int penSize = 0);
void SetPen(int penSize);
// TLine draws itself. Returns true if everything went OK.
virtual bool Draw(TDC &) const;
// The == operator must be defined for the container class,
// even if unused
bool operator ==(const TLine& other) const
{ return &other == this; }
friend ostream& operator <<(ostream& os, const TLine& line);
friend istream& operator >>(istream& is, TLine& line);
protected:
int PenSize;
TColor Color;
};
Pen access functions
In Step 8, the QueryPen function could be used both to access the
current size of the pen and to set the size of the pen. The new TLine query
functions-QueryPenSize and QueryColor-can't be used to modify the pen attributes. These
functions only return pen attributes.
To set pen attributes, there are two new functions called SetPen. The
first SetPen sets just the pen size. The other SetPen can be used to set the color, size,
and style of the pen. But by letting the second and third parameters take on their default
values, you can use the second constructor to set just the color. Here's the code for
these functions:
void
TLine::SetPen(int penSize)
{
if (penSize < 1)
PenSize = 1;
else
PenSize = penSize;
}
void
TLine::SetPen(TColor &newColor, int penSize)
{
// If penSize isn't the default (0), set PenSize to the new size.
if (penSize)
PenSize = penSize;
Color = newColor;
}
Draw function
The Draw function draws the line in the window, taking that
functionality from the window's Paint function. This functionality is moved because the
TLine object can now dictate how it gets painted onscreen. Take a look at the code for the
Draw function below and compare this to the Paint function from Step 8. From a certain
point, the two bits of code are nearly identical:
bool
TLine::Draw(TDC &dc) const
{
// Set pen for the dc to the values for this line
TPen pen(Color, PenSize);
dc.SelectObject(pen);
// Iterates through the points in the line i.
TPointsIterator j(*this);
bool first = true;
while (j) {
TPoint p = j++;
if (!first)
dc.LineTo(p);
else {
dc.MoveTo(p);
first = false;
}
}
dc.RestorePen();
return true;
}
After putting all this code into the TLine
class, the TDrawWindow::Paint function is greatly simplified:
void
TDrawWindow::Paint(TDC& dc, bool, TRect&)
{
// Iterates through the array of line objects.
TLinesIterator i(*Lines);
while (i)
i++.Draw(dc);
}
Insertion and extraction operators
There also some changes to the insertion and extraction operators
that are necessary to handle the revised TLine class.
Changes to the TDrawWindow class
There are a few fairly minor changes to the TDrawWindow class to
accommodate the revised TLine class:
CmPenColor function
The CmPenColor function opens a TChooseColorDialog for the user to
select a color from. Like TFileOpenDialog and TFileSaveDialog, TChooseColorDialog is an
encapsulation of one of the Windows common dialog boxes.
Also like TFileOpenDialog and TFileSaveDialog, the TChooseColorDialog
constructor can take up to five parameters, but in this case you need only two. The last
three all have default values. The two parameters you need to provide are a pointer to the
parent window and a reference to a TChooseColorDialog::TData object. In this case, the
pointer to the parent window is simply the this pointer. The
TChooseColorDialog::TData object is provided by colors.
Setting the Color member of colors to a particular color makes that
color (or its closest equivalent displayed in the dialog box) the default color in the
dialog box. By setting Color to the color of the current pen, you ensure that the Color
dialog box reflects the current state of the application.
Setting the CustColors member of the colors object to some array of
TColor objects sets those colors in the Custom Colors section of the Color dialog box. You
can use whatever colors you want for the CustColors array. The values that are used in the
tutorial produce a range of monochrome colors that goes from black to white.
Creating and executing a TChooseColorDialog works exactly the same as
for a TFileOpenDialog or TFileSaveDialog. Although the Color dialog box has an extra
button (the Define Custom Colors button), that button is handled by the Windows part of
the common dialog box. Therefore there are only two possible results for the Execute
function, IDOK and IDCANCEL. If the user selects Cancel, you ignore any changes from the
dialog box.
On the other hand, if the user selects OK, you need to change the pen
color to the new color chosen by the user. The TChooseColorDialog places the color chosen
by the user into the Color member of the colors object. Color is a TColor, which fits
nicely into the SetPen function of a TLine object.
Here's the code for the CmPenColor function:
void
TDrawWindow::CmPenColor()
{
TChooseColorDialog::TData colors;
static TColor custColors[16] =
{
0x010101L, 0x101010L, 0x202020L, 0x303030L,
0x404040L, 0x505050L, 0x606060L, 0x707070L,
0x808080L, 0x909090L, 0xA0A0A0L, 0xB0B0B0L,
0xC0C0C0L, 0xD0D0D0L, 0xE0E0E0L, 0xF0F0F0L
};
colors.Flags = CC_RGBINIT;
colors.Color = TColor(Line->QueryColor());
colors.CustColors = custColors;
if (TChooseColorDialog(this, colors).Execute() == IDOK)
Line->SetPen(colors.Color);
}
Where to find more information
Here's a guide to where you can find more information on the topics
introduced in this step:
|