Step 8: Adding multiple lines
You can find the source for Step 8 in the files STEP08.CPP and STEP08.RC
in the directory EXAMPLES\OWL\TUTORIAL. Step 8 makes a great leap in terms of usefulness.
In this step, you'll add a new class, TLine, that is derived from the TPoints array you've
been using to contain the points in a line. You'll then define another array class,
TLines, that contains an array of TLine objects, enabling us to have multiple lines in the
window. You'll add streaming operators to make it a little easier to save drawings.
Lastly, you'll develop the Paint function further to handle drawings with multiple lines.
TLine class
The TLine class is derived from the public base class TPoints. This
gives TLine all the functionality that you've been using with the Line member of the
TDrawWindow class. This includes the Add, Flush, and GetItemsInContainer functions that
you've been using. In addition, you can continue to use TPointsIterator with the TLine
class in the same way you used it with TPoints.
But because you're creating your own class now, you can also add any
additional functionality you need. For example, you should add a data member to contain
the size of the pen for each line. Then, to hide the data, add accessor functions to
manipulate the data.
In TLine, the pen size is contained in a protected int called
PenSize. PenSize is accessed by one of two functions, both called QueryPen. Both versions
of QueryPen return an int, which contains the value of PenSize. Here's the
difference between the two functions:
TLine also contains a
definition for the == operator. This operator checks to see if the two objects are
actually the same object. If so, the operator returns true. Defining an array using the
TArray class (which you'll do later when defining TLines) requires that the object used in
TArray have the == operator defined.
Lastly you should declare two operators, << and >>,
to be friends of the TLine class. When these operators are implemented later in
this section, they'll provide easy access to stream operations for the SaveFile and
OpenFile functions.
Here is the declaration of the TLine class:
class TLine : public TPoints
{
public:
TLine(int penSize = 1) : TPoints(10, 0, 10) { PenSize = penSize; }
int QueryPen() const { return PenSize; }
int QueryPen(int penSize);
// 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;
};
TLines array
Once you've defined the TLine class, you can define the TLines array
and the TLinesIterator array. These containers work the same way as the TPoints and
TPointsIterator container classes that you defined earlier. The only difference is that,
instead of containing an array of TPoint objects like TPoints, TLines contains an array of
TLine objects.
Here are the definitions of TLines and TLinesIterator:
typedef TArray<TLine> TLines;
typedef TArrayIterator<TLine> TLinesIterator;
Insertion and extraction of TLine objects
Most objects that need to be saved to and retrieved from files on a
regular basis are set up to use the insertion and extraction operators << and
>>. By declaring these operators as friends of TLine, you need to define the
operators to handle the particular type of data encapsulated in TLine.
Having these operators defined gives you the ability to place an
entire TLine object into a file with a single line of code. You'll see how this is used
when you make the changes to the OpenFile and SaveFile functions.
Insertion operator <<
In essence, the insertion operator takes on the functionality of the
SaveFile function used in Step 7. It doesn't have to open a file (that's handled by
whatever function uses the operator) and it has an extra piece of data to insert
(PenSize). Other than that, it's not much different. Compare the definition of this
function with the SaveFile function from Step 7. Notice the use of TPointsIterator with
the TLine object:
ostream& operator <<(ostream& os, const TLine& line)
{
// Write the number of points in the line
os << line.GetItemsInContainer() << '
// Write the pen size
os << ' ' << line.PenSize;
// Get an iterator for the array of points
TPointsIterator j(line);
// While the iterator is valid (i.e. it hasn't run out of points)
while(j)
// Write the point from the iterator and increment the array.
os << j++;
os << '
// return the stream object
return os;
}
Extraction operator >>
Much like the insertion operator, the extraction operator takes on
the functionality of the OpenFile function in Step 7. It doesn't have to open a file
itself and it has an extra piece of data to extract. Other than that, it's implemented
similarly to the OpenFile function:
istream& operator >>(istream& is, TLine& line)
{
unsigned numPoints;
is >> numPoints;
is >> line.PenSize;
while (numPoints--) {
TPoint point;
is >> point;
line.Add(point);
}
// return the stream object
return is;
}
Extending TDrawWindow
There are a number of changes required in TDrawWindow to accommodate
the new TLine class. First, there are a number of changes in data members:
The following functions are
modified or added:
In addition, the Paint
function is changed quite a bit, as described in the following section.
Paint function
The Paint function must now perform two iterations instead one.
Instead of iterating through a single array of points, Paint must now iterate through an
array of lines. For each line, it must set the pen width and then iterate through the
points that compose the line.
Paint does this by first creating an iterator from Lines. This
iterator goes through the array of lines. For each line, Paint queries the pen size of the
current line. It sets the window's Pen to this size and selects this pen into the device
context. It then creates an iterator for the current line and increments the line array
iterator.
The next part of Paint looks like the Paint function from Step 7.
That's because it does basically the same thing as that function-it takes the array of
points and draws the line in the window.
Here is the code for the new Paint function:
void
TDrawWindow::Paint(TDC& dc, bool, TRect&)
{
// Iterates through the array of line objects.
TLinesIterator i(*Lines);
while (i) {
// Set pen for the dc to current line's pen.
TPen pen(TColor::Black, i.Current().QueryPen());
dc.SelectObject(pen);
// Iterates through the points in the line i.
TPointsIterator j(i++);
bool first = true;
while (j) {
TPoint p = j++;
if (!first)
dc.LineTo(p);
else {
dc.MoveTo(p);
first = false;
}
}
}
}
Where to find more information
Here's a guide to where you can find more information on the topics
introduced in this step:
|