home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
src
/
mactech
/
volume18_2002
/
18.05.sit
/
18.05
/
PC
/
CGraphWindow.cp
< prev
next >
Wrap
Text File
|
2002-01-30
|
11KB
|
419 lines
//////////////////////////////////////////////////////////////////////
//
// Graph Window
// Written by Allen Stenger, January 2002
//
// This draws the graph in a window.
//
// This reads in the graph descriptions from file and creates a
// document window with a drawing of the graph inside. It also takes
// care of scrolling and redrawing the document window.
//
//////////////////////////////////////////////////////////////////////
#include "CGraphWindow.h"
#include "SxChartApp.h"
#include <fstream>
#include <sstream>
static const Rect kPicClipRect = {0, 0, 32767, 32767};
// Picture's clipping rect
static const int kMaxZoom = 1 << 8; // 2^(max number of zoom outs)
//////////////////////////////////////////////////////////////////////
// CGraphWindow implementation
//////////////////////////////////////////////////////////////////////
// class static variables
std::vector<CGraphWindow *> CGraphWindow::fgGraphWindows;
CGraphWindow::CGraphWindow(LStream *pStream) :
LWindow(pStream),
fpGraphView(0)
{
// add ourselves to list
fgGraphWindows.push_back(this);
// add a placeholder title to the Window menu
// (we don't know our title yet)
LMenu *pWindowMenu =
LMenuBar::GetCurrentMenuBar()->FetchMenu(kWindowMENU);
pWindowMenu->InsertCommand( "\p ", cmd_UseMenuItem, 16000 );
LCommander::SetUpdateCommandStatus(true);
}
CGraphWindow::~CGraphWindow()
{
// remove ourselves from Window menu
LMenu *pWindowMenu =
LMenuBar::GetCurrentMenuBar()->FetchMenu(kWindowMENU);
std::vector<CGraphWindow *>::iterator iter =
std::lower_bound(fgGraphWindows.begin(),
fgGraphWindows.end(), this);
int itemNumber = iter - fgGraphWindows.begin() + 1;
pWindowMenu->RemoveItem(itemNumber);
LCommander::SetUpdateCommandStatus(true);
// remove ourselves from list -
// use erase-remove idiom (see Meyers, "Effective STL" item 32)
fgGraphWindows.erase(
std::remove(fgGraphWindows.begin(), fgGraphWindows.end(), this),
fgGraphWindows.end());
}
void CGraphWindow::FindCommandStatus(
CommandT inCommand,
Boolean& outEnabled,
Boolean& outUsesMark,
UInt16& outMark,
Str255 outName)
{
ResIDT theMenuID;
SInt16 theMenuItem;
if (IsSyntheticCommand(inCommand, theMenuID, theMenuItem) &&
theMenuID == kWindowMENU && theMenuItem > 0)
{
// window items are always enabled;
// place checkmark next to frontmost window;
// supply the window title because we didn't know
// it at window creation time
LWindow *pWindow = fgGraphWindows[theMenuItem -1];
pWindow->GetDescriptor(outName);
outEnabled = true;
outUsesMark = true;
outMark = noMark;
if (pWindow == UDesktop::FetchTopRegular())
outMark = checkMark;
}
else
{
switch (inCommand)
{
case cmd_ZoomOut:
{
int zoom1 = fpGraphView->GetZoomFactor();
if (zoom1 < kMaxZoom)
outEnabled = true;
}
break;
case cmd_ZoomIn:
{
int zoom2 = fpGraphView->GetZoomFactor();
if (zoom2 > 1)
outEnabled = true;
}
break;
default:
{
LWindow::FindCommandStatus(inCommand, outEnabled,
outUsesMark, outMark, outName);
}
break;
}
}
}
Boolean CGraphWindow::ObeyCommand(
CommandT inCommand,
void* ioParam)
{
ResIDT theMenuID;
SInt16 theMenuItem;
Boolean cmdHandled = true;
if (IsSyntheticCommand(inCommand, theMenuID, theMenuItem) &&
theMenuID == kWindowMENU && theMenuItem > 0)
{
// bring desired window to front
CGraphWindow *pWindow = fgGraphWindows[theMenuItem - 1];
UDesktop::SelectDeskWindow(pWindow);
}
else
{
switch (inCommand)
{
case cmd_ZoomOut:
{
int zoom1 = fpGraphView->GetZoomFactor();
fpGraphView->SetZoomFactor(2 * zoom1);
}
break;
case cmd_ZoomIn:
{
int zoom2 = fpGraphView->GetZoomFactor();
fpGraphView->SetZoomFactor(zoom2 / 2);
}
break;
default:
{
cmdHandled = LWindow::ObeyCommand(inCommand, ioParam);
}
break;
}
}
return cmdHandled;
}
void CGraphWindow::FinishCreateSelf()
{
fpGraphView = static_cast<CGraphView *>(FindPaneByID(kGraphView));
// add attachment to handling page up/down etc. keys
AddAttachment(new LKeyScrollAttachment(fpGraphView));
LWindow::FinishCreateSelf();
}
//////////////////////////////////////////////////////////////////////
// CGraphWindow implementation
//////////////////////////////////////////////////////////////////////
CGraphView::CGraphView(LStream *pStream) :
LView(pStream),
fhPicture(0),
fZoomFactor(1)
{
fPicFrame.top = fPicFrame.left = 0;
fPicFrame.bottom = fPicFrame.right = 0;
}
CGraphView::~CGraphView()
{
if (fhPicture)
::DisposeHandle(reinterpret_cast<Handle>(fhPicture));
fhPicture = 0;
}
void CGraphView::LoadGraph(int testNumber)
{
fFileNumberString = SxChartApp::TestNumberToString(testNumber);
// How we will handle the unknown picture size:
// we don't know yet the actual extent of the graph, so
// we'll use a big source and clipping rectangle, but
// we'll also keep track of the actual size needed.
// Then we'll set the view's image size to the actual size.
// However we will draw into the large sized area to avoid
// scaling the picture.
// We assume the graph will alway start somewhere near (0,0),
// so that will be our view's initial top left display,
// even if there's no data around there.
OpenCPicParams myOpenCPicParams;
myOpenCPicParams.srcRect = kPicClipRect;
myOpenCPicParams.hRes = 0x00480000;
myOpenCPicParams.vRes = 0x00480000;
myOpenCPicParams.version = 2;
myOpenCPicParams.reserved1 = 0;
myOpenCPicParams.reserved2 = 0;
fhPicture = ::OpenCPicture(&myOpenCPicParams);
// set up Picture the way we want it
::ClipRect(&kPicClipRect);
::PenNormal();
TextFont(1); // application font
TextFace(0); // plain
TextMode(srcCopy); // plain
TextSize(9); // 9 point
DrawEdges();
DrawNames();
::ClosePicture();
// make image the size of the useful parts of the picture
ResizeImageTo(
(fPicFrame.right - fPicFrame.left) / fZoomFactor,
(fPicFrame.bottom - fPicFrame.top) / fZoomFactor,
false);
// force redraw now that we have something to draw
Refresh();
}
void CGraphView::DrawSelf()
{
// code swiped from LPicture::DrawSelf; we don't use
// LPicture because it requires the picture to be in a resource
if (fhPicture != nil)
{
Rect destRect = kPicClipRect;
destRect.top /= fZoomFactor;
destRect.left /= fZoomFactor;
destRect.right /= fZoomFactor;
destRect.bottom /= fZoomFactor;
::DrawPicture(fhPicture, &destRect);
}
else
{
Rect frame;
CalcLocalFrameRect(frame);
::PenNormal();
Pattern ltGrayPat;
::MacFillRect(&frame, UQDGlobals::GetLightGrayPat(<GrayPat));
::MacFrameRect(&frame);
}
}
void CGraphView::SetZoomFactor(int factor)
{
// attempt to scroll to the same center after zooming
SDimension16 frameSize;
GetFrameSize(frameSize);
SPoint32 imageLocation;
GetImageLocation(imageLocation);
SPoint32 viewCenter;
viewCenter.h = -imageLocation.h + (frameSize.width / 2);
viewCenter.v = -imageLocation.v + (frameSize.height / 2);
viewCenter.h *= fZoomFactor;
viewCenter.v *= fZoomFactor;
// now do the zoom
fZoomFactor = factor;
ResizeImageTo(
(fPicFrame.right - fPicFrame.left) / fZoomFactor,
(fPicFrame.bottom - fPicFrame.top) / fZoomFactor,
false);
// try to re-center
viewCenter.h /= fZoomFactor;
viewCenter.v /= fZoomFactor;
imageLocation.h = (frameSize.width / 2) - viewCenter.h;
imageLocation.v = (frameSize.height / 2) - viewCenter.v;
ScrollPinnedImageTo(-imageLocation.h, -imageLocation.v, false);
Refresh(); // redraw ourself
}
int CGraphView::GetZoomFactor()
{
return fZoomFactor;
}
void CGraphView::DrawEdges()
{
// open correct segments file
std::ostringstream fileNameStream;
fileNameStream << "segments" << fFileNumberString << ".out";
std::string fileName(fileNameStream.str());
std::ifstream segmentsStream(fileName.c_str());
if (!segmentsStream.is_open())
{
SxChartApp::SayFileError(fileName);
return; // give up
}
// file format is: several blocks of form
// number of points
// point1horiz,point1vert
// ...
// read all blocks
while (!segmentsStream.eof())
{
// read number of points in block
std::string numPtsString;
std::getline(segmentsStream, numPtsString);
int numPts = std::atoi(numPtsString.c_str());
// read all points in block
for (int i = 0; i < numPts; i++)
{
std::string pointLine;
std::getline(segmentsStream, pointLine);
std::string::size_type firstComma = pointLine.find(',');
std::string horizString = pointLine.substr(0, firstComma);
std::string vertString = pointLine.substr(firstComma + 1);
Point aVertex;
aVertex.h = std::atoi(horizString.c_str());
aVertex.v = std::atoi(vertString.c_str());
// draw segment
if (i == 0)
::MoveTo(aVertex.h, aVertex.v);
else
::LineTo(aVertex.h, aVertex.v);
}
}
}
void CGraphView::DrawNames()
{
// we also keep a running set of bounds for the vertices
int maxH = 0;
int maxV = 0;
// open correct locations file
std::ostringstream fileNameStream;
fileNameStream << "locations" << fFileNumberString << ".out";
std::string fileName(fileNameStream.str());
std::ifstream locationsStream(fileName.c_str());
if (!locationsStream.is_open())
{
SxChartApp::SayFileError(fileName);
return; // give up
}
// file format is: several blocks of form
// pointhoriz,pointvert,personname
// read all lines
while (!locationsStream.eof())
{
std::string pointLine;
std::getline(locationsStream, pointLine);
std::string::size_type firstComma = pointLine.find(',');
std::string::size_type secondComma =
pointLine.find(',', firstComma + 1);
std::string horizString = pointLine.substr(0, firstComma);
std::string vertString =
pointLine.substr(firstComma + 1, secondComma - firstComma - 1);
std::string personName = pointLine.substr(secondComma + 1);
Point aVertex;
aVertex.h = std::atoi(horizString.c_str());
aVertex.v = std::atoi(vertString.c_str());
// draw name at vertex, attempt to center on the vertex;
// do not draw left of or above 0.
const int kCharHalfHeight = 5; // assuming 9 point type
const char *pPersonString = personName.c_str();
size_t nameCharLen = personName.length();
int nameWidth = ::TextWidth(pPersonString, 0, nameCharLen);
// width in pixels
aVertex.h -= (nameWidth / 2);
aVertex.v += kCharHalfHeight;
if (aVertex.h < 0)
aVertex.h = 0;
if (aVertex.v < 2 * kCharHalfHeight)
aVertex.v = 2 * kCharHalfHeight;
::MoveTo(aVertex.h, aVertex.v);
::MacDrawText(personName.c_str(), 0, personName.length());
// update bounds
if (aVertex.h > maxH)
maxH = aVertex.h;
if (aVertex.v > maxV)
maxV = aVertex.v;
}
// update bounding rect - we somewhat arbitrarily pad it
// to avoid having the labels clipped off
const int kPadding = 100; // units: pixels
maxV += kPadding;
maxH += kPadding;
Rect tempRect = {0, 0, maxV, maxH};
fPicFrame = tempRect;
}