This is ClockView.m in view mode; [Download] [Up]
/*
* (C) 1990 by Adobe Systems Incorporated. All rights reserved.
*
* This file may be freely copied and redistributed as long as:
* 1) This entire notice continues to be included in the file,
* 2) If the file has been modified in any way, a notice of such
* modification is conspicuously indicated.
*
* PostScript, Display PostScript, and Adobe are registered trademarks of
* Adobe Systems Incorporated.
*
* ************************************************************************
* THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
* NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
* INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
* LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
* KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
* AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR PARTICULAR PURPOSES AND NONINFINGEMENT OF THIRD PARTY RIGHTS.
* ************************************************************************
*/
/*
* ClockView.m
*
* This class handles the drawing of the clock and the moving of the alarm.
* The clock face is drawn into a bitmap and then composited into the buffered
* window before drawing the hands. The hands are stored in the server
* as user paths. Each hand also has a graphic state associated with it. Before
* hand is drawn, its graphic state is installed and then rotated to its current
* angle and then the user path is rendered.
*
* An animator object has been borrowed from the stopwatch implementation in
* the NeXTDeveloper directory. This object makes adjustments to the timed
* entry in order to keep the timing up to date.
*/
#import "Animator.h"
#import "ClockView.h"
#import "ClockViewWraps.h"
#import <appkit/Bitmap.h>
#import <appkit/Cell.h>
#import <appkit/Control.h>
#import <appkit/View.h>
#import <appkit/nextstd.h>
#import <dpsclient/dpsclient.h>
#import <dpsclient/wraps.h>
@implementation ClockView
static void drawClockHand(id self, int hand);
/*
* These are the user path operands and operators for the clock hands.
* They are sent and stored in the server.
*/
static float ptsHour[] = { -10, -10, 10, 170, -4.5, 0, 0, 120, 0,120, 4.5, 180, 0, 0, -120,
0, 0, 0, 0, 10, 360, 0};
static char opsHour[] = {dps_setbbox, dps_moveto, dps_rlineto, dps_arcn, dps_rlineto,
dps_closepath, dps_moveto, dps_arcn, dps_closepath};
static float ptsMin[] = { -10, -10, 10, 175, -4.5, 0, 0, 162, 0,162, 4.5, 180, 0, 0, -162,
0, 0, 0, 0, 10, 360, 0};
static char opsMin[] = {dps_setbbox, dps_moveto, dps_rlineto, dps_arcn, dps_rlineto,
dps_closepath, dps_moveto, dps_arcn, dps_closepath};
static float ptsSec[] = { -10, -30, 10, 170, -1.5, 0, 0, 145, 3, 0, 0, -145,
4, 0, 0, -20, 0, -20, 5.5, 360, 180, 0, 20, 4, 0, 0, 0, 0, 0, 10, 360, 0};
static char opsSec[] = {dps_setbbox, dps_moveto, dps_rlineto, dps_rlineto, dps_rlineto,
dps_rlineto, dps_rlineto, dps_arcn, dps_rlineto, dps_rlineto, dps_closepath,
dps_moveto, dps_arcn, dps_closepath};
static float ptsAlarmTop[] = { -5, 70, 5, 120, -1.0, 100, 0, 5, 0, 105, 1.0, 180, 0, 0, -5};
static char opsAlarmTop[] = {dps_setbbox, dps_moveto, dps_rlineto, dps_arcn, dps_rlineto,
dps_closepath};
static float ptsAlarmBot[] = { -5, -2, 5, 120, -1.0, 0, 0, 100, 2.0, 0, 0, -100};
static char opsAlarmBot[] = {dps_setbbox, dps_moveto, dps_rlineto, dps_rlineto,
dps_rlineto, dps_closepath};
/*
* Initialize the instance variables and create an Animator object.
* The animator is used to keep the timed_entry up to date. Without
* the animator adjustments, the clock loses time. An array to hold
* the hit detection user path is allocated. This mouse location
* will be inserted into this user path before the hit detection is
* tested for.
*/
+ new
{
self = [super new];
[self setClipping:NO];
gstatesOn = upathsServer = YES;
totalTime = numIterations = 0;
angleAlarm = 0;
gstateHour = gstateMin = gstateSec = gstateShad = 0;
upathHour = upathMin = upathSec = upathAlarmTop = upathAlarmBot = 0;
NX_MALLOC(hitPoint.pts, float, MAX_PTS_HIT);
NX_MALLOC(hitPoint.ops, char, MAX_OPS_HIT);
[self initializeHitPoint];
animatorId = [Animator newChronon: 1.0
adaptation: 3.0
target: self
action: @selector(tick:)
autoStart: NO
eventMask: NX_ALLEVENTS];
return self;
}
- initializeHitPoint
{
int i;
for (i = 0; i < MAX_PTS_HIT; i++)
{
hitPoint.pts[i] = 0;
}
hitPoint.num_pts = i;
hitPoint.ops[0] = dps_setbbox;
hitPoint.ops[1] = dps_moveto;
hitPoint.ops[2] = dps_rlineto;
hitPoint.ops[3] = dps_rlineto;
hitPoint.ops[4] = dps_rlineto;
hitPoint.ops[5] = dps_closepath;
hitPoint.num_ops = 6;
return self;
}
- free
{
if (hitPoint.pts)
NX_FREE(hitPoint.pts);
if (hitPoint.ops)
NX_FREE(hitPoint.ops);
[animatorId free];
[bitmapId free];
return [super free];
}
- setDisplayTime:anObject
{
displayTime = anObject;
return self;
}
- toggleGstate:sender
{
totalTime = numIterations = 0;
gstatesOn = [sender state];
return self;
}
- toggleUpath:sender
{
totalTime = numIterations = 0;
upathsServer = [sender state];
return self;
}
static void drawUpathLines (pts, ops, clr, wid, x, y, startlen, endlen, deg)
float pts[];
char ops[];
float clr, wid, x, y, startlen, endlen, deg;
{
int i , j;
float angle;
deg = ABS(deg * RADIAN);
i = 4; j = 1;
for (angle = 0; angle < 2 * M_PI; angle += deg)
{
pts[i++] = (floor) (x + (float) cos(angle) * startlen);
pts[i++] = (floor) (y + (float) sin(angle) * startlen);
ops[j++] = dps_moveto;
pts[i++] = (floor) (x + (float) cos(angle) * endlen);
pts[i++] = (floor) (y + (float) sin(angle) * endlen);
ops[j++] = dps_lineto;
}
PSsetgray(clr);
PSsetlinewidth(wid);
DPSDoUserPath(&pts[4], i - 4, dps_float, &ops[1], j -1, pts, dps_ustroke);
}
- drawFace
{
char *ops;
float *pts;
NXSize bmapsize;
NXPoint center;
float maxnums, maxdashes, maxcircle;
if (bitmapId)
{
[bitmapId getSize:&bmapsize];
if (bmapsize.width < frame.size.width || bmapsize.height < frame.size.height)
[bitmapId resize:frame.size.width :frame.size.height];
}
else
{
bitmapId = [[Bitmap newSize:frame.size.width
:frame.size.height type:NX_NOALPHABITMAP] setFlip:NO];
}
center.x = floor(bounds.size.width/2);
center.y = floor(bounds.size.height/2);
maxcircle = MIN(center.y - 10, center.x -10);
maxnums = maxcircle * SIZENUMS;
maxdashes = maxcircle * SIZEDASHES;
NX_MALLOC(pts, float, MAX_PTS);
NX_MALLOC(ops, char, MAX_OPS);
pts[0] = bounds.origin.x;
pts[1] = bounds.origin.y;
pts[2] = bounds.origin.x + bounds.size.width;
pts[3] = bounds.origin.y + bounds.size.height;
ops[0] = dps_setbbox;
[bitmapId lockFocus];
PSscale(frame.size.width/bounds.size.width, frame.size.height/bounds.size.height);
PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
bounds.size.width, bounds.size.height);
PSWMakeCircle(center.x, center.y, maxcircle);
PSWFillPath(CLRCIRC);
PSsetlinecap(1);
drawUpathLines(pts, ops, CLRMIN, WIDMIN, center.x, center.y,
maxdashes * LENMIN, maxdashes, DEGMIN);
drawUpathLines(pts, ops, CLRHOUR, WIDHOUR, center.x, center.y,
maxdashes * LENHOUR, maxdashes, DEGHOUR);
[bitmapId unlockFocus];
if (pts)
NX_FREE(pts);
if (ops)
NX_FREE(ops);
return self;
}
- defineUPaths
{
/* Setup hour hand upath. */
PSWSetUpath(ptsHour, sizeof (ptsHour)/sizeof (float),
opsHour, sizeof (opsHour)/sizeof (char));
upathHour = DPSDefineUserObject(upathHour);
/* Setup minute hand upath. */
PSWSetUpath(ptsMin, sizeof (ptsMin)/sizeof (float),
opsMin, sizeof (opsMin)/sizeof (char));
upathMin = DPSDefineUserObject(upathMin);
/* Setup seconds hand upath. */
PSWSetUpath(ptsSec, sizeof (ptsSec)/sizeof (float),
opsSec, sizeof (opsSec)/sizeof (char));
upathSec = DPSDefineUserObject(upathSec);
/* Setup top of alarm hand upath. */
PSWSetUpath(ptsAlarmTop, sizeof (ptsAlarmTop)/sizeof (float),
opsAlarmTop, sizeof (opsAlarmTop)/sizeof (char));
upathAlarmTop = DPSDefineUserObject(upathAlarmTop);
/* Setup bottom of alarm hand upath. */
PSWSetUpath(ptsAlarmBot, sizeof (ptsAlarmBot)/sizeof (float),
opsAlarmBot, sizeof (opsAlarmBot)/sizeof (char));
upathAlarmBot = DPSDefineUserObject(upathAlarmBot);
return self;
}
/*
* If a user object has not been allocated, then a gstate has also not been
* allocated. As a result, create a gstate before defining the user object.
* If a user object exists, then copy the new gstate into the old
* structure. No need to redefine the user object because
* it still refers to the same structure. The PSpop() pops the result of
* PScurrentgstate() off of the stack.
*/
static int definegstate(gstate, offsetx, offsety, color, linewidth)
int gstate;
float offsetx, offsety, color, linewidth;
{
PSgsave();
PSWSetGstate(offsetx, offsety, color, linewidth);
if (!gstate)
{
PSgstate();
gstate = DPSDefineUserObject(gstate);
}
else
{
PScurrentgstate(gstate);
PSpop();
}
PSgrestore();
return gstate;
}
- defineGStates
{
float angle;
NXPoint center;
struct timeval timeofDay;
struct tm *localTime;
center.x = floor(bounds.size.width/2);
center.y = floor(bounds.size.height/2);
[[animatorId startEntry] resetRealTime];
[self lockFocus];
gettimeofday(&timeofDay, NULL);
localTime = localtime(&timeofDay.tv_sec);
angleHour = ((localTime->tm_hour % 12) + localTime->tm_min/60.0) * DEGHOUR;
gstateHour = definegstate (gstateHour, center.x, center.y,
CLRHANDS - 0.2, LNWIDHANDS);
angleMin = (localTime->tm_min + localTime->tm_sec/60.0) * DEGMIN;
gstateMin = definegstate(gstateMin, center.x + OFFSETHANDSX,
center.y + OFFSETHANDSY, CLRHANDS - 0.2, LNWIDHANDS);
angleSec = localTime->tm_sec * DEGMIN;
gstateSec = definegstate(gstateSec,
center.x + (2 * OFFSETHANDSX),
center.y + (2 * OFFSETHANDSY),
CLRSECOND, LNWIDSECOND);
gstateShad = definegstate(gstateShad,
center.x + (2 * OFFSETHANDSX) + OFFSETSHADX,
center.y + (2 * OFFSETHANDSY) + OFFSETSHADY,
CLRSHADOW, LNWIDSECOND);
[self unlockFocus];
return self;
}
/* Messaged by the Animator object after a timed entry has been received. */
- tick:sender
{
angleSec = angleSec + TICKSEC;
angleMin = angleMin + TICKMIN;
angleHour = angleHour + TICKHOUR;
[self display];
return self;
}
/*
* Scales the view and then redefines the graphic states.
* The graphic state pick up the scale view.
*/
- sizeTo:(NXCoord)width :(NXCoord)height
{
NXRect xframe;
[animatorId stopEntry];
xframe = frame;
[super sizeTo:width :height];
if (window)
{
[self scale:width/xframe.size.width :height/xframe.size.height];
[self drawFace];
[self defineGStates];
}
return self;
}
- setAlarm:(NXEvent *)event
{
int old_mask;
NXPoint p, center;
NXEvent peek;
center.x = floor(bounds.size.width/2);
center.y = floor(bounds.size.height/2);
old_mask = [window addToEventMask:NX_MOUSEUPMASK|
NX_MOUSEDRAGGEDMASK|NX_TIMERMASK];
event = [NXApp getNextEvent:NX_MOUSEUPMASK|
NX_MOUSEDRAGGEDMASK|NX_TIMERMASK];
[self lockFocus];
while (event->type != NX_MOUSEUP)
{
if (event->type == NX_TIMER)
{
angleSec = angleSec + TICKSEC;
angleMin = angleMin + TICKMIN;
angleHour = angleHour + TICKHOUR;
if (![NXApp peekNextEvent:NX_MOUSEDRAGGEDMASK into:&peek])
event = [NXApp getNextEvent:NX_MOUSEDRAGGEDMASK];
}
if (event->type == NX_MOUSEDRAGGED)
{
p = event->location;
[self convertPoint:&p fromView:nil];
angleAlarm = atan((p.y - center.y)/(p.x - center.x))/RADIAN - 90;
if (p.x - center.x < 0)
angleAlarm -= 180;
if ([NXApp peekNextEvent:NX_TIMERMASK into:&peek
waitFor:0 threshold:NX_BASETHRESHOLD])
{
angleSec = angleSec + TICKSEC;
angleMin = angleMin + TICKMIN;
angleHour = angleHour + TICKHOUR;
event = [NXApp getNextEvent:NX_TIMERMASK];
}
}
PSgsave();
[self drawSelf:&bounds :1];
PSgrestore();
[window flushWindow];
NXPing();
event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK|NX_TIMERMASK
waitFor:1000 threshold:NX_BASETHRESHOLD];
}
[window setEventMask:old_mask];
return self;
}
- setHitPoint:(const NXPoint *)p
{
float z;
NXPoint pt;
pt.x = p->x - floor(bounds.size.width/2);
pt.y = p->y - floor(bounds.size.height/2);
z = sqrt (pt.x * pt.x + pt.y * pt.y);
pt.x = pt.x - (z * sin(ABS(angleAlarm) * RADIAN));
pt.y = pt.y + z - z * cos(ABS(angleAlarm) * RADIAN);
/* Bounding Box */
hitPoint.pts[0] = floor(pt.x - HITSETTING/2);
hitPoint.pts[1] = floor(pt.y - HITSETTING/2);
hitPoint.pts[2] = ceil(pt.x + HITSETTING/2);
hitPoint.pts[3] = ceil(pt.y + HITSETTING/2);
/* Moveto */
hitPoint.pts[4] = pt.x - HITSETTING/2;
hitPoint.pts[5] = pt.y - HITSETTING/2;
/* Rlineto's */
hitPoint.pts[7] = HITSETTING;
hitPoint.pts[8] = HITSETTING;
hitPoint.pts[11] = -HITSETTING;
return self;
}
- (BOOL) isHit:(const NXPoint *) p
{
int hit;
[self setHitPoint:p];
PSgsave();
PSWInstallGstate(gstateHour, angleAlarm);
PSWHitPath(upathAlarmTop, upathAlarmBot, hitPoint.pts,
hitPoint.num_pts, hitPoint.ops, hitPoint.num_ops, &hit);
PSgrestore();
return (BOOL) hit;
}
- mouseDown:(NXEvent *)event
/* This method handles a mouse down. */
{
NXPoint p;
p = event->location;
[self convertPoint:&p fromView:nil];
if ([self isHit:&p])
[self setAlarm:event];
return self;
}
- setStateAndDraw
{
NXPoint center;
if (gstatesOn)
{
PSWInstallGstate(gstateHour, angleAlarm);
drawClockHand(self, ALARM);
PSWInstallGstate(gstateHour, angleHour);
drawClockHand(self, HOUR);
PSWInstallGstate(gstateMin, angleMin);
drawClockHand(self, MINUTE);
PSWInstallGstate(gstateShad, angleSec);
drawClockHand(self, SHADOW);
PSWInstallGstate(gstateSec, angleSec);
drawClockHand(self, SECOND);
}
else
{
center.x = floor(bounds.size.width/2);
center.y = floor(bounds.size.height/2);
PSgsave();
PStranslate(center.x, center.y);
PSrotate(angleAlarm);
drawClockHand(self, ALARM);
PSgrestore();
PSgsave();
PSsetgray(CLRHANDS - 0.2);
PSsetlinewidth(LNWIDHANDS);
PStranslate(center.x , center.y);
PSrotate(angleHour);
drawClockHand(self, HOUR);
PSgrestore();
PSgsave();
PSsetgray(CLRHANDS - 0.2);
PSsetlinewidth(LNWIDHANDS);
PStranslate(center.x + OFFSETHANDSX,
center.y + OFFSETHANDSY);
PSrotate(angleMin);
drawClockHand(self, MINUTE);
PSgrestore();
PSgsave();
PSsetgray(CLRSHADOW);
PStranslate(center.x + (2*OFFSETHANDSX) + OFFSETSHADX,
center.y + (2*OFFSETHANDSY) + OFFSETSHADY);
PSrotate(angleSec);
drawClockHand(self, SHADOW);
PSgrestore();
PSgsave();
PSsetgray(CLRSECOND);
PSsetlinewidth(LNWIDSECOND);
PStranslate(center.x + OFFSETHANDSX + OFFSETSHADX,
center.y + OFFSETHANDSY + OFFSETSHADY);
PSrotate(angleSec);
drawClockHand(self, SECOND);
PSgrestore();
}
}
static void drawClockHand(id self, int hand)
{
if (self->upathsServer)
{
switch(hand)
{
case ALARM:
PSsetgray(CLRALARMTOP);
PSWUpathFill(self->upathAlarmTop);
PSsetgray(CLRALARMBOT);
PSWUpathFill(self->upathAlarmBot);
break;
case HOUR:
PSWUpathStrokeFill(self->upathHour);
break;
case MINUTE:
PSWUpathStrokeFill(self->upathMin);
break;
case SHADOW:
PSWUpathFill(self->upathSec);
break;
case SECOND:
PSWUpathFill(self->upathSec);
PSWDrawCircle(CLRSECOND - 0.2);
break;
}
}
else
{
switch(hand)
{
case ALARM:
PSsetgray(CLRALARMTOP);
DPSDoUserPath(&ptsAlarmTop[4], sizeof (ptsAlarmTop)/sizeof (float) - 4,
dps_float, &opsAlarmTop[1], sizeof (opsAlarmTop)/sizeof (char) -1,
ptsAlarmTop, dps_ufill);
PSsetgray(CLRALARMBOT);
DPSDoUserPath(&ptsAlarmBot[4], sizeof (ptsAlarmBot)/sizeof (float) - 4,
dps_float, &opsAlarmBot[1], sizeof (opsAlarmBot)/sizeof (char) - 1,
ptsAlarmBot, dps_ufill);
break;
case HOUR:
DPSDoUserPath(&ptsHour[4], sizeof (ptsHour)/sizeof (float) - 4,
dps_float, &opsHour[1], sizeof (opsHour)/sizeof (char) - 1,
ptsHour, dps_ustroke);
PSsetgray(CLRHANDS);
DPSDoUserPath(&ptsHour[4], sizeof (ptsHour)/sizeof (float) - 4,
dps_float, &opsHour[1], sizeof (opsHour)/sizeof (char) - 1,
ptsHour, dps_ufill);
break;
case MINUTE:
DPSDoUserPath(&ptsMin[4], sizeof (ptsMin)/sizeof (float) - 4,
dps_float, &opsMin[1], sizeof (opsMin)/sizeof (char) - 1,
ptsMin, dps_ustroke);
PSsetgray(CLRHANDS);
DPSDoUserPath(&ptsMin[4], sizeof (ptsMin)/sizeof (float) - 4,
dps_float, &opsMin[1], sizeof (opsMin)/sizeof (char) - 1,
ptsMin, dps_ufill);
break;
case SHADOW:
DPSDoUserPath(&ptsSec[4], sizeof (ptsSec)/sizeof (float) - 4,
dps_float, &opsSec[1], sizeof (opsSec)/sizeof (char) - 1,
ptsSec, dps_ufill);
break;
case SECOND:
DPSDoUserPath(&ptsSec[4], sizeof (ptsSec)/sizeof (float) - 4,
dps_float, &opsSec[1], sizeof (opsSec)/sizeof (char) - 1,
ptsSec, dps_ufill);
PSWDrawCircle(CLRSECOND - 0.2);
break;
}
}
}
- drawSelf:(NXRect *)r :(int) count
{
int ElapsedTime;
NXRect cRect;
[displayTime setStringValue:""];
cRect .origin = bounds.origin;
cRect.size = frame.size;
PSWMarkTime (); NXPing ();
[bitmapId composite:NX_COPY fromRect:&cRect toPoint:&bounds.origin];
[self setStateAndDraw];
PSWReturnTime (&ElapsedTime);
totalTime += ElapsedTime;
++numIterations;
[displayTime setIntValue:(totalTime/numIterations)];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.