TRect Bound; // Stores the rectangular boundary of the line. bool Selected; // Stores whether or not the line is selected.
TLine(const TColor& color = TColor(0), int penSize = 1) : TPoints(10,0,10), PenSize(penSize), Color(color), Bound(0,0,0,0), Selected(false)By default, the line has no boundary and is not selected.
TLine::DrawSelection(TDC& dc) { TUIHandle(Bound, TUIHandle::DashFramed).Paint(dc); }For information about the TUIHandle function, search the OWL.HLP file for TUIHandle.
void TLine::UpdateBound() { // Iterates through the points in the line i. TPointsIterator j(*this); if (!j) return; TPoint p = j++; Bound.Set(p.x, p.y, 0, 0); while (j) { p = j++; if ((p.x - PenSize) < Bound.left) Bound.left = (p.x - PenSize); if ((p.x + PenSize) > Bound.right) Bound.right = (p.x + PenSize); if ((p.y - PenSize) < Bound.top) Bound.top = (p.y - PenSize); if ((p.y + PenSize) > Bound.bottom) Bound.bottom = (p.y + PenSize); } Bound.right += 1; Bound.bottom += 1;
}
void TLine::UpdatePosition(TPoint& newPos) { for (TPointsIterator i(*this); i; i++) { TPoint* pt = (TPoint *)&i.Current(); pt->x += newPos.x; pt->y += newPos.y; } Bound.Offset(newPos.x, newPos.y); }
void TLine::Invalidate(TDrawView& view) { TOleClientDC dc(view); TRect rUpdate(GetBound()); rUpdate.Inflate(1, 1); dc.LPtoDP((TPoint *)&rUpdate, 2); TUIHandle handle(rUpdate, TUIHandle::Framed); rUpdate = handle.GetBoundingRect(); view.GetDocument().NotifyViews(vnInvalidate, (long)&rUpdate, 0); }
Here is the CommitSelection function:
bool TDrawDocument::CommitSelection(TOleWindow& oleWin, void* userData) { TOleDocument::CommitSelection(oleWin, userData); TDrawView* drawView = TYPESAFE_DOWNCAST(&oleWin, TDrawView); TOutStream* os = OutStream(ofWrite); if (!os || !drawView) return false; // Make the line usable in a container by adjusting its origin // TLine* line = (TLine*)userData; int i = line? 1 : 0; TPoint newPos(Margin, Margin); if (line) { newPos -= line->GetBound().TopLeft(); line->UpdatePosition(newPos); } // Write the number of lines in the figure *os << i; // Append a description using a resource string *os << ' ' << FileInfo << '\n'; // Copy the current line from the iterator and increment the array. if (line) *os << *line; delete os; // restore line // if (line) line->UpdatePosition(-newPos); // // Commit the storage if it was opened in transacted mode // TOleDocument::CommitTransactedStorage(); return true; }
Here is the OpenSelection function:
bool TDrawDocument::OpenSelection(int mode, const char far* path, TPoint far* where) { char fileinfo[100]; TOleDocument::Open(mode, path); // normally path should be null //if (GetDocPath()) { TInStream* is = (TInStream*)InStream(ofRead); if (!is) return false; unsigned numLines; *is >> numLines; is->getline(fileinfo, sizeof(fileinfo)); while (numLines--) { TLine line; *is >> line; if (where) { TPoint newPos(where->x, where->y); newPos -= line.GetBound().TopLeft(); line.UpdatePosition(newPos); } line.UpdateBound(); Lines->Add(line); } delete is; if (GetDocPath()) { FileInfo = fileinfo; else { FileInfo = string(*::Module,IDS_FILEINFO); } SetDirty(false); UndoState = UndoNone; return true; }
TDrawDocument::Open(int mode, const char far* path) { ? while (numLines--) { TLine line; *is >> line; line.UpdateBound(); ines->Add(line); } ? }
TLine* Selected; enum DRAWTOOL { DrawSelect = 0, DrawPen, }; DRAWTOOL Tool;Then initialize the data members in the view class constructor, as shown in the STEP17DV.CPP file:
TDrawView::TDrawView(TDrawDocument& doc, TWindow* parent) : TOleView(doc, parent), DrawDoc(&doc) { Selected = 0; Tool = DrawPen; ? }
You can specify the name of the Clipboard format in the constructor for the view object, as shown in STEP17DV.CPP:
TDrawView::TDrawView(TDrawDocument& doc, TWindow* parent) : TOleView(doc, parent), DrawDoc(&doc) { ? OcApp->AddUserFormatName("DrawPad Native Data", "Owl DrawPad native data", DrawPadFormat); }
Note: The Cut and Copy commands were not added in the previous steps of the tutorial because OWL handles the commands automatically for linked and embedded objects. However, in order to work with document parts, you must add the commands manually.
In addition, in order to return to normal mode from the selection mode, you need a Pen command. (When an application is in normal mode, you can perform normal actions, but you can't select document parts. When an application is in selection mode, you can select document parts, but you can't perform normal actions. For example, in the drawing application, the Pen and Select commands allow you to switch between drawing and selecting lines.)
To add the Select, Cut, Copy, and Pen commands,
This is the new Select function for the view class, as shown in STEP17DV.CPP:
bool TDrawView::Select(uint modKeys, TPoint& point) { if (Tool != DrawSelect) return false; // Clicked in lines? TLine *line = HitTest(point); SetLineSelection(line); if (Selected) { // there is a selection ? } else // Select OLE object, if any return TOleView::Select(modKeys, point); }The Select function calls the HitTest function to determine whether or not the mouse cursor falls within a line boundary. If so, the SetLineSelection function is called to store the selected line in the Line data member of the view class. Otherwise, the TOleView::Select function is called, which automatically handles the selecting of OLE objects.
To see the implementations of the HitTest and SetLineSelection functions, see the STEP17DV.CPP file.
bool TDrawView::PaintLink(TDC& dc, bool erase, TRect& rect, TString& moniker) { TLine* line = 0; //Draw the whole document if linking to the whole doc // if (strcmp(moniker, OleStr(DocContent)) == 0) { Paint(dc, erase, rect); return true; } // Find the selection with the corresponding moniker // line = DrawDoc->GetLine(moniker); if (!line) return false; TPoint newPos(Margin, Margin); newPos -= rect.TopLeft(); line->UpdatePosition(newPos); line->Draw(dc); line->UpdatePosition(-newPos); return true; }
bool TDrawView::EvOcViewClipData(TOcFormatData far& formatData) { if (strcmp(OleStr(formatData.Format.GetRegName()), OleStr(DrawPadFormat)) != 0) return false; // not our clipboard format bool status = true; if (formatData.Paste) { // Pasting native data DrawDoc->SetHandle(ofReadWrite, formatData.Handle, false); DrawDoc->OpenSelection(ofRead, 0, formatData.Where); Invalidate(); // Restore the original storage // DrawDoc->RestoreStorage(); } else { // Copying native data HANDLE data = GlobalAlloc(GHND|GMEM_SHARE, 0); DrawDoc->SetHandle(ofReadWrite, data, true); // Copy the selection if the target format is "DrawPad" // if (formatData.UserData) { status = DrawDoc->CommitSelection(*this, formatData.UserData); } else { status = DrawDoc->Commit(true); } formatData.Handle = data; // Restore the original storage // DrawDoc->RestoreStorage(); } return status; }
bool TDrawView::EvOcViewGetItemName(TOcItemName& item) { if (item.Selection) { if (!Selected) return false; char name[32]; itoa(DrawDoc->GetLines()->Find(*Selected), name, 10); item.Name = name; } else { item.Name = "content"; // item name representing the whole document } return true; }
bool TDrawView::EvOcViewSetLink(TOcLinkView& view) { // Attach a linked view to this document // new TDrawLinkView(GetDocument(), view); return true; }
bool TDrawView::EvOcViewPartSize(TOcPartSize far& ps) { TClientDC dc(*this); TRect rect(0, 0, 0, 0); TLine* line = 0; if (ps.Selection) { if (ps.Moniker) { if (strcmp(*ps.Moniker, OleStr(DocContent)) == 0) line = 0; // whole document else line = DrawDoc->GetLine(*ps.Moniker); } else{ line = (TLine*) ps.UserData; } } if (line) { *(TPoint*)&rect.left = line->GetBound().TopLeft(); rect.right = rect.left + line->GetBound().Width() + 2 * Margin; rect.bottom = rect.top + line->GetBound().Height() + 2 * Margin; } else { // a 2" x 2" extent for server // rect.right = dc.GetDeviceCaps(LOGPIXELSX) * 2; rect.bottom = dc.GetDeviceCaps(LOGPIXELSY) * 2; } ps.PartRect = rect; return true; }
void TDrawView::EvLButtonDown(uint modKeys, TPoint& point) { TOleView::EvLButtonDown(modKeys, point); if (SelectEmbedded() || !DragDC) return; if (Tool == DrawSelect) { // selection // Select(modKeys, point); } else if (Tool == DrawPen) { SetCapture(); Pen = new TPen(Line->QueryColor(), Line->QueryPenSize()); DragDC->SelectObject(*Pen); DragRect.SetNull(); DragDC->MoveTo(point); Line->Add(point); } }
The actual TOleLinkView objects are created in the EvOcPasteLink event handler.
DEFINE_RESPONSE_TABLE1(TDrawLinkView, TOleLinkView) EV_VN_DRAWDELETE, EV_VN_DRAWMODIFY, END_RESPONSE_TABLE; TDrawLinkView::TDrawLinkView(TDocument& doc, TOcLinkView& view) : TOleLinkView(doc , view) { DrawDoc = TYPESAFE_DOWNCAST(&doc, TDrawDocument); CHECK(DrawDoc); } TDrawLinkView::~TDrawLinkView() { } // // Line was modified // bool TDrawLinkView::VnModify(uint index) { // Get the selection corresponding to the moniker // TLine * line = DrawDoc->GetLine(GetMoniker()); if (!line) return false; // Notify the container // if (index == DrawDoc->GetLines()->Find(*line)) { UpdateLinks();
} return true; } // // Line was deleted // bool TDrawLinkView::VnDelete(uint index) { // Get the selection corresponding to the moniker // TLine * line = DrawDoc->GetLine(GetMoniker()); if (!line) return false; // Notify the container // if (index == DrawDoc->GetLines()->Find(*line)) { UpdateLinks(); } return true; }