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.
TLine(int penSize = 1);to
TLine(const TColor &color = (TColor) 0, int penSize = 1);The constructor itself changes to set PenSize to the constructor's second parameter and to create a new TColor object and assign it to Color. If no parameters are specified and the first parameter takes on its default value, TColor::Black is used as the pen color.
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; };
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; }
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); }
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); }