//-------------------------------------------
// DrawBox.cpp - sicherer@sichemsoft.nl
// Complete commented code available on line
// NOTE: only functions whose prototypes are
// shown in Figure 1 are shown here!
//-------------------------------------------
// ... #includes and constants omitted
//-------------------------------------------
__fastcall
TDrawBox::TDrawBox(TComponent* Owner)
: inherited(Owner)
{
Surface = new TDrawBoxSurface(this);
Canvas = Surface->Canvas;
// ... default property assignments omitted
}
//-------------------------------------------
__fastcall
TDrawBox::~TDrawBox() // virtual
{
if (PrintThread)
PrintThread->Terminate();
delete Surface;
}
//-------------------------------------------
void
TDrawBox::SetWindowExtents
(double x1, double y1,
double x2, double y2)
{
assert(x1 != x2 && y1 != y2);
xw1 = x1; yw1 = y1; xw2 = x2; yw2 = y2;
SetTransformation();
// ... code omitted
}
//-------------------------------------------
bool
TDrawBox::SaveToFileAsEMF
(const AnsiString &file,
double x1, double y1,
double x2, double y2)
{
return Save(file, true, x1, y1, x2, y2);
}
//-------------------------------------------
void
TDrawBox::DrawLine(double x1, double y1,
double x2, double y2)
{
int xx1, yy1, xx2, yy2;
WorldtoPC(x1, y1, &xx1, &yy1);
WorldtoPC(x2, y2, &xx2, &yy2);
Canvas->MoveTo(xx1, yy1);
Canvas->LineTo(xx2, yy2);
}
//-------------------------------------------
void __fastcall
TDrawBox::SetCrossHairs(bool on)
{
if (Surface->CrossHairs != on) {
Surface->CrossHairs = on;
if (on) {
oldCursor = Cursor; Cursor = crNone;
} else
Cursor = oldCursor;
Invalidate();
}
}
//-------------------------------------------
bool __fastcall
TDrawBox::GetCrossHairs() const
{
return Surface->CrossHairs;
}
//-------------------------------------------
void
TDrawBox::NotifyMarked(int x1, int y1,
int x2, int y2, bool region)
{
if (region) { // area marked
if (FOnMarked && MarkShape != msNone) {
double xx1, yy1, xx2, yy2;
PCtoWorld(x1, y1, &xx1, &yy1);
PCtoWorld(x2, y2, &xx2, &yy2);
FOnMarked(this, xx1, yy1, xx2, yy2);
}
} else { // point clicked
if (Caret)
Surface->SetCaret(x2, y2);
if (FOnClick) {
double x, y;
PCtoWorld(x2, y2, &x, &y);
FOnClick(this, x, y);
}
}
}
//-------------------------------------------
void
TDrawBox::NotifyMouseUp(TMouseButton button,
TShiftState shift, int x, int y)
{
if (!FOnMouseUp)
return; // no user event handler
double xx, yy;
PCtoWorld(x, y, &xx, &yy);
FOnMouseUp(this, button, shift, xx, yy);
}
//-------------------------------------------
void
TDrawBox::NotifyPaint()
{
if (FOnPaint) // call user's event handler
FOnPaint(this);
}
//-------------------------------------------
bool
TDrawBox::ScrollIfNeeded()
{
assert(Surface->Marking);
// retrieve current mouse position
// and compute window boundaries
POINT mousePosition;
::GetCursorPos(&mousePosition);
POINT surfacePosition = ClientOrigin;
int left = surfacePosition.x;
int top = surfacePosition.y;
int right = left + ClientWidth;
int bottom = top + ClientHeight;
int x = mousePosition.x;
int y = mousePosition.y;
if (x >= left && x < right &&
y >= top && y < bottom)
return false;
// determine horizontal scroll
int dx = 0;
if (x < left)
dx = x - left;
else if (x > right)
dx = x - right;
// determine vertical scroll
int dy = 0;
if (y < top)
dy = y - top;
else if (y > bottom)
dy = y - bottom;
// scroll window
if (dx || dy) {
if (dx) dx = 1 + dx / 2;
if (dy) dy = 1 + dy / 2;
int xpos = HorzScrollBar->Position;
HorzScrollBar->Position = xpos + dx;
int ypos = VertScrollBar->Position;
VertScrollBar->Position = ypos + dy;
if (xpos != HorzScrollBar->Position ||
ypos != VertScrollBar->Position)
return true;
}
// window not scrolled
return false;
}
//-------------------------------------------
TMetafile *
TDrawBox::CreateMetafile(double x1, double y1,
double x2, double y2)
{
// do nothing if there is no
// OnPaint handler!
if (!FOnPaint)
return NULL;
double xx1, yy1, xx2, yy2;
int vw, vh;
bool fullWindow;
if (x1 != x2) { // use specified window
fullWindow = false;
// remember original window and viewport
xx1 = xw1; yy1 = yw1;
xx2 = xw2; yy2 = yw2;
vw = ViewportWidth; vh = ViewportHeight;
// compute proportional viewport size
WorldtoPC(abs(x2 - x1), abs(y2 - y1),
&ViewportWidth, &ViewportHeight);
} else { // use full window extents
fullWindow = true;
ViewportWidth = HorzScrollBar->Range;
ViewportHeight = VertScrollBar->Range;
}
// create metafile with size of viewport
TMetafile *metafile = new TMetafile;
metafile->Width = ViewportWidth;
metafile->Height = ViewportHeight;
// create metafile canvas to draw on
TMetafileCanvas *metafileCanvas =
new TMetafileCanvas(metafile,
Canvas->Handle);
// copy attributes from screen window
// canvas
metafileCanvas->Pen->Assign(Canvas->Pen);
metafileCanvas->Brush->
Assign(Canvas->Brush);
metafileCanvas->Font->Assign(Canvas->Font);
// change Canvas pointer to metafile canvas
Canvas = metafileCanvas;
if (!fullWindow) {
// set new window extents
SetWindowExtents(x1, y1, x2, y2);
}
// set clipping rectangle to viewport
HRGN hrgn =
::CreateRectRgn(0, 0,
ViewportWidth, ViewportHeight);
::SelectClipRgn(Canvas->Handle, hrgn);
::DeleteObject(hrgn);
// call user's OnPaint
FOnPaint(this);
if (!fullWindow) {
// restore previous window and viewport
ViewportWidth = vw; ViewportHeight = vh;
SetWindowExtents(xx1, yy1, xx2, yy2);
}
// change Canvas pointer back to screen
// window
Canvas = Surface->Canvas;
// close metafile canvas and return
// metafile
delete metafileCanvas;
return metafile;
}
//-------------------------------------------
bool
TDrawBox::Save(const AnsiString &filename,
bool emf, double x1, double y1,
double x2, double y2)
{
// create metafile with copy of window
TMetafile *metafile =
CreateMetafile(x1, y1, x2, y2);
if (!metafile)
return false;
bool result = true;
// save/copy as enhanced metafile
if (emf) {
// copy metafile to clipboard
if (filename.IsEmpty()) {
Clipboard()->Open();
Clipboard()->Assign(metafile);
Clipboard()->Close();
// or save metafile on disk
} else {
try {
metafile->SaveToFile(filename);
} catch (...) {
result = false;
}
}
// or save/copy as bitmap
} else {
// create temporary bitmap
Graphics::TBitmap *bitmap =
new Graphics::TBitmap();
// set bitmap to size of metafile
bitmap->Width = metafile->Width;
bitmap->Height = metafile->Height;
// draw metafile on bitmap's canvas
bitmap->Canvas->Draw(0,0,metafile);
// copy bitmap to clipboard
if (filename.IsEmpty()) {
Clipboard()->Open();
Clipboard()->Assign(bitmap);
Clipboard()->Close();
// or save bitmap on disk
} else {
try {
bitmap->SaveToFile(filename);
} catch(...) {
result = false;
}
}
// delete temporary bitmap
delete bitmap;
}
// delete created metafile
delete metafile;
return result;
}
//-------------------------------------------
namespace Drawbox
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] =
{__classid(TDrawBox)};
RegisterComponents("Sichemsoft",
classes, 0);
}
}
//-------------------------------------------
//-------------------------------------------
__fastcall
TDrawBoxSurface::
TDrawBoxSurface(TComponent* owner):
inherited(owner)
{
// ... default property assignments omitted
}
//-------------------------------------------
void __fastcall
TDrawBoxSurface::MouseUp(TMouseButton button,
TShiftState shift, int x, int y)
{
inherited::MouseUp(button,shift,x,y);
DrawBox->NotifyMouseUp(button,
shift, x, y);
if (!(Marking && button == mbLeft))
return;
// undraw rectangle
CurrentX = x; CurrentY = y;
if (MarkShape != msNone)
DrawMarkShape();
// fit and normalize shape
int x1 = OriginX; int x2 = x;
int y1 = OriginY; int y2 = y;
if (MarkShape == msLine) {
x1 = max(0, x1); x1 = min(x1, Width);
y1 = max(0, y1); y1 = min(y1, Height);
x2 = max(0, x2); x2 = min(x2, Width);
y2 = max(0, y2); y2 = min(y2, Height);
} else {
if (x1 > x2) Swap(x1, x2);
if (y1 > y2) Swap(y1, y2);
x1 = max(0, x1); y1 = max(0, y1);
x2 = min(x2, Width);
y2 = min(y2, Height);
}
// is region or point marked?
bool region = abs(x2-x1)>2 || abs(y2-y1)>2;
// redraw rectangle if necessary
if (region && MarkShape == msMark) {
DrawMarkShape();
OriginX = x1; OriginY = y1;
MarkedX = x2; MarkedY = y2;
Marked = true;
}
// send notification to surface object
if (MarkShape != msNone)
delete Timer;
DrawBox->NotifyMarked(x1, y1, x2, y2,
region);
Marking = false;
}
//-------------------------------------------
void __fastcall
TDrawBoxSurface::TimerTick(TObject* Sender)
{
assert(Marking);
if (DrawBox->ScrollIfNeeded())
Invalidate();
}
//-------------------------------------------