Visual C++ Example:
Multiple
CRectTracker Drawing
|
Thierry Maurel
February 5, 2000
Environment:
VC++ 6 SP2, Visual
C++ 2003, Visual C++
2005, NT4 SP3, Win95/98
The class I
present performs the classic dragging
and resizing of multiple
objects, like vector
editors or CAD programs.
It does the same operations that the CRectTracker,
with an array of rectangles instead of a single rectangle.
Those rectangles could be implemented with a CArray<CRect*,
CRect*>, but the code in the project's view would be
complex and not portable. So my solution is the CMRTObject
(for CMultiRectTrackerObject),
a base class for the edited objects, with just a rectangle
information and two functions Get/Set. In a normal
project, those objects are often based on CObject, but
with multiple inheritance, this add-in is easy to be
implemented. Another good news is that the CMultiRectTracker
class gets the possibility to directly change the
position/size of selected objects, so the CView code for
the manipulation is light, as listed below.
class CDemoObject : public CMRTObject, public CObject
{
public:
CDemoObject (COLORREF rgb)
: CMRTObject(), m_color(rgb) {;}
CDemoObject (LPCRECT lpSrcRect, COLORREF rgb)
: CMRTObject(lpSrcRect), m_color(rgb) {;}
~CDemoObject () {;}
public:
void Draw (CDC* pDC) {
pDC->FillSolidRect (GetRect(), m_color);
}
protected:
COLORREF m_color;
};
Listed below is a possible implementation for your
CxxView::OnLButtonDown(). It performs multiple selection
with a local CRectTracker,
and updates the CMultiRectTracker
object contained by the view. If there is no rubber band,
it selects the object clicked, and if the HitTest is
different than -1 (CRectTracker::hitNothing),
it starts the CMultiRectTracker::Track()
function, wich contains the similar internal loop than CRectTracker.
All objects moved or sized are directly updated in the Track()
fct, the only work the CxxView needs to do is a call to
Invalidate(). The CTRL key is scanned, and selected
objects are removed if the user don't hit CTRL.
void CDemoView::OnLButtonDown(UINT nFlags, CPoint point)
{
CDemoDoc* pDoc = GetDocument();
if (multiTrack.HitTest(point) < 0) {
if (!(nFlags & MK_CONTROL))
multiTrack.RemoveAll();
CRect rcObject;
CDemoObject* pObject;
CRectTracker tracker;
if (tracker.TrackRubberBand(this, point, TRUE)) {
CRect rectT;
tracker.m_rect.NormalizeRect();
POSITION pos = pDoc->m_objects.GetHeadPosition ();
while (pos != NULL) {
pObject = (CDemoObject*)(pDoc->m_objects.GetNext(pos));
rcObject = pObject->GetRect();
rectT.IntersectRect(tracker.m_rect, rcObject);
if (rectT == rcObject) {
multiTrack.Add(pObject);
}
}
} else {
POSITION pos = pDoc->m_objects.GetHeadPosition ();
while (pos != NULL) {
pObject = (CDemoObject*)(pDoc->m_objects.GetNext(pos));
rcObject = pObject->GetRect();
if (rcObject.PtInRect(point)) {
multiTrack.Add(pObject);
break;
}
}
}
} else {
if (multiTrack.Track(this, point, FALSE))
pDoc->SetModifiedFlag();
}
Invalidate();
CView::OnLButtonDown(nFlags, point);
}
In order to
be classic, the cursor needs to be updated, function of
its position on the objects, so a call to SetCursor() is
needed before the CView-base class OnSetCursor() call,
like the CRectTracker
usage.
BOOL CDemoView::OnSetCursor(CWnd* pWnd, UINT nHitTest,
UINT message)
{
if (pWnd == this && multiTrack.SetCursor(this, nHitTest))
return TRUE;
return CView::OnSetCursor(pWnd, nHitTest, message);
}
Downloads
Download
demo project - 34 Kb
Download
source - 5 Kb
|