home *** CD-ROM | disk | FTP | other *** search
- /*
- * Doug Felt, 9/5/88 creation.
- *
- * the cdev routines
- *
- * varcodes:
- *
- * if the low bit of the varcode is 1, do not show the number box in the middle of
- * the dial.
- *
- * bugs:
- *
- * there may be memory problems involving allocation of the offscreen bitmaps during
- * drawing. this is just a quick hack so I don't know.
- *
- * possible modifications:
- *
- * -- more varcodes to number the ticks, and to toggle whether the name shows.
- *
- * -- changing to 'inactive' state is slow since I redraw the whole image. it would
- * probably be better to keep a region of the dial background and redraw it in
- * white.
- *
- * -- what really slows this down on the plus is the floating point calculations,
- * so if this were to run on the plus it might be better to use fixed point.
- *
- * -- support for color, real grays, pixmaps instead of bitmaps, etc.
- */
-
- #include "cdef.h"
-
- #include "sincos.h"
- #include "rectutils.h"
- #include "bitmap.h"
-
- static void cDraw(ControlHandle theControl, long param);
- static long cTest(ControlHandle theControl, long param);
- static void cCalcRgns(ControlHandle theControl, long param);
- static void cInit(int varCode, ControlHandle theControl);
- static void cDispose(ControlHandle theControl);
- static void cPos(ControlHandle theControl, long param);
- static void cThumb(ControlHandle theControl, long param);
- static long cDrag(ControlHandle theControl, long param);
- static long cAutoTrack(ControlHandle theControl, long param);
-
- static void redrawPict(ControlHandle ch);
- static void calcValue(ControlHandle ch);
- static void drawValue(ControlHandle ch);
- static int pointToValue(ControlHandle ch, Point pt);
- static void xdrawValue(ControlHandle ch);
- static void drawRectImage(ControlHandle ch, Rect *r);
- static void setNewValue(ControlHandle ch, int v);
- static Boolean checkAndUpdate(ControlHandle ch);
-
- /*
- * try to accomodate the system we are running on a little bit, in case it has
- * a wierd selection of fonts.
- */
- #define kNumFontName "\pGeneva"
- #define kNameFontName "\pNew York"
- #define kNameFontMax 16
- #define kNameFontMin 12
- #define kNumFontMax 10
- #define kNumFontMin 8
-
- /*
- * needle image ranges between -kNeedleMax and kNeedleMax degrees.
- * ticks are in increments of kTickIncrement degrees, should evenly divide
- * 2 * kNeedleMax
- */
- #define kNeedleMax 80
- #define kTickIncrement 10
-
- typedef struct {
- Rect limitRect;
- Rect slopRect;
- int axis;
- } CThumbRec, *PCThumbRec;
-
- typedef struct {
- Rect lastR;
- Rect trackR;
- Rect titleR;
- Rect numR;
- Boolean inactive;
- Boolean doNum;
- int numFont;
- int numSize;
- int numBase;
- Point cp,ep;
- PBMap buffer;
- PBMap image;
- } IData, *PIData, **HIData;
-
- long
- MyControl(varCode, theControl, message, param)
- int varCode;
- ControlHandle theControl;
- int message;
- long int param;
- {
- long int res = 0L;
- GrafPtr port;
-
- GetPort(&port); /* I'm chicken */
- SetPort((**theControl).contrlOwner);
- switch (message) {
- case drawCntl: cDraw(theControl,param); break;
- case testCntl: res = cTest(theControl,param); break;
- case calcCRgns: cCalcRgns(theControl,param); break;
- case initCntl: cInit(varCode,theControl); break;
- case dispCntl: cDispose(theControl); break;
- case dragCntl: res = cDrag(theControl,param); break;
- case posCntl: cPos(theControl,param); break;
- case thumbCntl: cThumb(theControl,param); break;
- case autoTrack: res = cAutoTrack(theControl,param); break;
- default: break;
- }
- SetPort(port);
- return res;
- }
-
- static void
- cDraw(ch,param)
- ControlHandle ch;
- long param;
- {
- HIData idh;
- Rect r;
- Boolean newpict;
-
- if ((**ch).contrlVis) {
- param &= 0xffff; /* apparently, garbage in high word!!! */
- idh = (HIData)(**ch).contrlData;
- r = (**ch).contrlRect;
- if (idh) {
- newpict = checkAndUpdate(ch);
- if ((param != 129) || newpict) {
- if (newpict)
- calcValue(ch);
- drawRectImage(ch,&r);
- } else {
- setNewValue(ch,(**ch).contrlValue); /* only track indicator */
- }
- } else {
- PenNormal(); /* couldn't allocate offscreen buffers, etc */
- FrameRect(&r);
- SysBeep(3);
- }
- }
- }
-
- /*
- * return the number of the control part
- */
- static long
- cTest(ch,param)
- ControlHandle ch;
- long param;
- {
- CPartNumber res = CInNothing;
- HIData idh;
- int bot;
- Point pt;
-
- if ((**ch).contrlHilite != 255) {
- pt = *(Point *)¶m;
- if (PtInRect(pt,&(**ch).contrlRect)) {
- idh = (HIData)(**ch).contrlData;
- if (idh) {
- bot = (**idh).titleR.top;
- if (pt.v > bot)
- res = CInTitle;
- else {
- if ((**idh).doNum && PtInRect(pt,&(**idh).numR)) {
- res = CInNumBox;
- } else {
- res = CInDial;
- }
- }
- }
- }
- }
- return (long)res;
- }
-
- /*
- * This Control Manager hack will break when the mac moves to 32-bit memory
- * management. To be a bit safer, I only mess with param when iOnly is set.
- * Apple has only themselves to blame.
- */
- static void
- cCalcRgns(ch,param)
- ControlHandle ch;
- long param;
- {
- Boolean iOnly;
- Rect r;
-
- iOnly = (param & 0x80000000) != 0;
- if (iOnly) {
- /*
- * essentially ignore iOnly since the Control Manager should never call it. But
- * then you never know what kinds of ugly madness lurk within the toolbox.
- */
- SysBeep(3);
- param &= 0x00ffffff;
- SetEmptyRgn((RgnHandle)param);
- } else {
- /*
- * gets called when I free, move the control. Make sure this is a solid
- * region since the CM apparently uses InvalRgn to clear us!!!
- */
- OpenRgn();
- r = (**ch).contrlRect;
- FrameRect(&r);
- CloseRgn((RgnHandle)param);
- }
- }
-
- static void
- cInit(vc,ch)
- int vc;
- ControlHandle ch;
- {
- HIData idh = 0;
- PIData idp;
-
- idh = (HIData)NewHandle(sizeof(IData));
- if (idh) {
- HLock(idh);
- idp = *idh;
- SetRect(&idp->lastR,0,0,0,0);
- idp->trackR = idp->lastR;
- idp->titleR = idp->lastR;
- idp->numR = idp->lastR;
- idp->inactive = false;
- idp->doNum = (vc & 0x01) == 0; /* default is to show number box */
- idp->numFont = 0;
- idp->numSize = 0;
- idp->numBase = 0;
- SetPt(&idp->cp,0,0);
- SetPt(&idp->ep,0,0);
- idp->buffer = BMNew();
- idp->image = BMNew();
-
- if (!(idp->buffer && idp->image)) {
- if (idp->buffer)
- BMDispose(idp->buffer);
- if (idp->image)
- BMDispose(idp->image);
- DisposHandle(idh);
- idh = 0L;
- } else {
- HUnlock(idh);
- }
- }
- (**ch).contrlData = (Handle)idh;
- (**ch).contrlAction = 0L;
- }
-
- static void
- cDispose(ch)
- ControlHandle ch;
- {
- HIData idh;
-
- idh = (HIData)(**ch).contrlData;
- if (idh) {
- BMDispose((**idh).image);
- BMDispose((**idh).buffer);
- DisposHandle(idh);
- (**ch).contrlData = 0L;
- }
- }
-
- static long
- cDrag(ch,param)
- ControlHandle ch;
- long param;
- {
- long res;
- Point pt;
- Rect oldr,newr;
-
- if (!(**ch).contrlData) {
- res = -1; /* better safe than sorry */
- } else {
- param &= 0xffff; /* garbage in high word again */
- if (param != 0) {
- int v;
- while (StillDown()) {
- GetMouse(&pt);
- v = pointToValue(ch,pt);
- if (v != (**ch).contrlValue) {
- setNewValue(ch,v);
- }
- }
- /*
- * pass -1 to keep cPos from being called. just passing 1 doesn't work,
- * obviously it expects some other byte to be non zero, so I use -1
- * to get all four
- */
- res = -1;
- } else {
- res = 0; /* let control manager drag us */
- }
- }
- return res;
- }
-
- /*
- * since I drag the indicator when I move it, and never restore the
- * previous position, this need never be called. In case it does get
- * called, here it is, but it does nothing.
- */
- static void
- cPos(ch,param)
- ControlHandle ch;
- long param;
- {
- }
-
- /*
- * This is clearly a hack for scroll bars to call DragGrayRgn. Why even make
- * this part of the CDEF interface? This will get called before I drag the
- * indicator, but I don't use it, nor do I see how to usefully do so.
- */
- static void
- cThumb(ch,param)
- ControlHandle ch;
- long param;
- {
- PCThumbRec p;
-
- p = (PCThumbRec)param;
- p->limitRect = (**ch).contrlRect;
- p->slopRect = (**ch).contrlRect;
- p->axis = noConstraint; /* from window manager */
- }
-
- /*
- * again, this should never be called.
- */
- static long
- cAutoTrack(ch,param)
- ControlHandle ch;
- long param;
- {
- long res = 0L;
-
- SysBeep(3);
- return res;
- }
-
- /*
- *************************************************************************
- */
-
- static void
- redrawPict(ch)
- ControlHandle ch;
- {
- GrafPtr port;
- HIData idh;
- Rect b,r,r2,r3;
- int oldFont,oldFace,oldSize;
- int fnum,i,space,base,ht,wid,xwid;
- int minang,maxang;
- Point c,p;
- FontInfo finfo;
- char *str;
- double pi80,fh,fv;
- Boolean active;
- RgnHandle oclip,ovis;
- Pattern backpat;
-
- GetPort(&port);
-
- oclip = NewRgn();
- ovis = NewRgn();
- if (oclip && ovis) {
- GetClip(oclip);
- ClipRect(&(**ch).contrlRect); /* in case we're off the window when drawing */
- CopyRgn(port->visRgn,ovis);
- RectRgn(port->visRgn,&(**ch).contrlRect);
- } else {
- if (ovis) DisposeRgn(ovis);
- if (oclip) DisposeRgn(oclip);
- ovis = oclip = 0L;
- }
- pi80 = PI / 180.0;
-
- b = (**ch).contrlRect;
- idh = (HIData)(**ch).contrlData;
- r = b;
- r.right -= 2;
- r.bottom -= 2;
-
- str = (char *)&(**ch).contrlTitle;
- active = (**ch).contrlHilite != 255;
-
- PenNormal();
- FrameRect(&r);
- PenSize(2,2);
- MoveTo(r.right,r.top+2);
- LineTo(r.right,r.bottom);
- LineTo(r.left+2,r.bottom);
-
- oldFont = port->txFont;
- oldFace = port->txFace;
- oldSize = port->txSize;
-
- GetFNum(kNameFontName,&fnum);
- TextFont(fnum);
- TextFace((Style)bold);
- for (i=kNameFontMax; i>=kNameFontMin; --i)
- if (RealFont(fnum,i)) break;
- TextSize(i);
-
- GetFontInfo(&finfo);
- space = 2;
- base = finfo.descent + finfo.leading + space;
- ht = finfo.ascent + base + space;
- wid = StringWidth(str);
-
- r2.left = r.left + 2;
- r2.right = r.right - 2;
- r2.bottom = r.bottom - 2;
- r2.top = r2.bottom - ht;
-
- xwid = r2.right-r2.left-2;
- if (wid > xwid) {
- if (oclip) {
- r3 = r2;
- InsetRect(&r3,1,0);
- ClipRect(&r3);
- MoveTo(r3.left,r.bottom-base);
- DrawString(str);
- ClipRect(&(**ch).contrlRect);
- }
- } else {
- MoveTo((r.right+r.left-wid) >> 1,r.bottom-base);
- DrawString(str);
- }
-
- if (active) {
- InvertRect(&r2);
- }
- (**idh).titleR = r2;
-
- PenSize(1,1);
- MoveTo(r.left,r2.top - 2);
- LineTo(r.right,r2.top - 2);
-
- r3 = r;
- r3.bottom = r2.top;
- InsetRect(&r3,2,2);
- if (active) {
- StuffHex(&backpat,"\pAA55AA55AA55AA55");
- FillRect(&r3,&backpat);
- }
-
- c.h = (r.right + r.left) >> 1;
- c.v = r2.top - 10;
-
- wid = c.h - (r.left+7);
- ht = c.v - (r.top+7);
- SetRect(&r2,c.h-wid,c.v-ht,c.h+wid,c.v+ht);
- PenSize(3,1);
- minang = -kNeedleMax-5;
- maxang = kNeedleMax+5;
- EraseArc(&r2,minang,170);
- FrameArc(&r2,minang,170);
- fh = wid * sin(minang * pi80);
- fv = ht * -cos(minang * pi80);
- p.h = c.h + fh;
- p.v = c.v + fv;
- MoveTo(p.h,p.v);
- LineTo(c.h,c.v);
- p.h = c.h - fh; /* dial is symmetric around vertical axis */
- LineTo(p.h,p.v);
- PenNormal();
-
- InsetRect(&r2,7,7);
- (**idh).trackR = r2;
- SetPt(&(**idh).cp,c.h,c.v);
-
- for (i=-kNeedleMax; i<=kNeedleMax; i+=kTickIncrement) {
- fh = sin(i * pi80);
- fv = -cos(i * pi80);
- p.h = c.h + wid * fh;
- p.v = c.v + ht * fv;
- MoveTo(p.h,p.v);
- p.h = c.h + (wid-4) * fh;
- p.v = c.v + (ht-4) * fv;
- LineTo(p.h,p.v);
- }
-
- GetFNum(kNumFontName,&fnum);
- TextFont(fnum);
- TextFace((Style)bold);
- for (i=kNumFontMax; i>=kNumFontMin; --i)
- if (RealFont(fnum,i)) break;
- TextSize(i);
-
- GetFontInfo(&finfo);
- base = finfo.descent + finfo.leading;
- ht = finfo.ascent + base;
- wid = finfo.widMax * 3;
- r3.left = c.h - (wid >> 1);
- r3.right = r3.left + wid;
- r3.bottom = c.v - 7;
- r3.top = r3.bottom - ht;
- (**idh).numR = r3;
- (**idh).numFont = fnum;
- (**idh).numSize = i;
- (**idh).numBase = base;
-
- TextFont(oldFont);
- TextFace(oldFace);
- TextSize(oldSize);
-
- if (oclip) {
- SetClip(oclip);
- DisposeRgn(oclip);
- }
- if (ovis) {
- CopyRgn(ovis,port->visRgn);
- DisposeRgn(ovis);
- }
- }
-
- /*
- * calculate the needle position
- */
- static void
- calcValue(ch)
- ControlHandle ch;
- {
- double pi80,fv,nrange,range;
- int v,max,min;
- HIData id;
-
- pi80 = PI / 180.0;
- v = (**ch).contrlValue;
- max = (**ch).contrlMax;
- min = (**ch).contrlMin;
- range = max-min;
- nrange = kNeedleMax * 2;
- id = (HIData)(**ch).contrlData;
- if (v < min)
- v = min;
- else if (v > max)
- v = max;
- fv = pi80 * (-kNeedleMax + ((v-min) * nrange / range));
- (**id).ep.h = ((**id).cp.h - (**id).trackR.left) * sin(fv);
- (**id).ep.v = ((**id).cp.v - (**id).trackR.top) * -cos(fv);
- }
-
- static void
- drawValue(ch)
- ControlHandle ch;
- {
- Rect r;
- Str255 str;
- int wid,v;
- HIData idh;
- PIData idp;
- SignedByte state;
-
- PenNormal();
- idh = (HIData)(**ch).contrlData;
- state = HGetState(idh);
- HLock(idh);
- idp = *idh;
-
- MoveTo(idp->cp.h,idp->cp.v);
- Line(idp->ep.h,idp->ep.v);
- SetRect(&r,idp->cp.h-3,idp->cp.v-3,idp->cp.h+3,idp->cp.v+3);
- PaintOval(&r);
- if (idp->doNum) {
- r = idp->numR;
- FrameRect(&r);
- InsetRect(&r,1,1);
- EraseRect(&r);
- TextFont(idp->numFont);
- TextSize(idp->numSize);
- TextFace((Style)0);
- v = (**ch).contrlValue;
- NumToString(v,&str);
- wid = StringWidth(&str);
- MoveTo((idp->numR.right + idp->numR.left - wid) >> 1,idp->numR.bottom - idp->numBase);
- DrawString(&str);
- }
- HSetState(idh,state);
- }
-
- static int
- pointToValue(ch,pt)
- ControlHandle ch;
- Point pt;
- {
- int v,min,max;
- Rect r;
- HIData idh;
- double val,range,nrange;
-
- r = (**ch).contrlRect;
- idh = (HIData)(**ch).contrlData;
- if (PtInRect(pt,&r)) {
- r = (**idh).trackR;
- PtToAngle(&r,pt,&v);
- if (v > 180)
- v -= 360;
- if (v > kNeedleMax)
- v = kNeedleMax;
- else if (v < -kNeedleMax)
- v = -kNeedleMax;
- min = (**ch).contrlMin;
- max = (**ch).contrlMax;
- nrange = kNeedleMax * 2;
- range = max-min;
- val = v + kNeedleMax;
- v = min + (val * range / nrange);
- return v;
- } else {
- return (**ch).contrlValue;
- }
- }
-
- static void
- xdrawvalue(ch)
- ControlHandle ch;
- {
- HIData idh;
-
- idh = (HIData)(**ch).contrlData;
- BMDraw((**idh).image);
- drawValue(ch);
- }
-
- /*
- * might be memory problems here if I can't size the buffer. I haven't bothered
- * to check.
- */
- static void
- drawRectImage(ch,r)
- ControlHandle ch;
- Rect *r;
- {
- HIData idh;
-
- idh = (HIData)(**ch).contrlData;
- BMSize((**idh).buffer,r);
- BMInBitMapDo((**idh).buffer,xdrawvalue,ch);
- BMDraw((**idh).buffer);
- }
-
- /*
- * calculates the new value of the needle and updates immediately
- */
- static void
- setNewValue(ch,v)
- ControlHandle ch;
- int v;
- {
- Rect r,r2;
- Point pt;
- HIData idh;
- PIData idp;
- SignedByte state;
-
- idh = (HIData)(**ch).contrlData;
- state = HGetState(idh);
- HLock(idh);
- idp = *idh;
-
- RUMakeDeltaRect(idp->cp,idp->ep,&r);
- InsetRect(&r,-1,-1);
- (**ch).contrlValue = v;
- calcValue(ch);
- RUMakeDeltaRect(idp->cp,idp->ep,&r2);
- InsetRect(&r2,-1,-1);
- UnionRect(&r2,&r,&r);
- if (idp->doNum)
- UnionRect(&idp->numR,&r,&r);
- drawRectImage(ch,&r);
-
- HSetState(idh,state);
- }
-
- /*
- * see whether I need to update the offscreen image, either because
- * it changed size, or because it changed highlight state, and
- * redraw the image if so.
- * return true if the image was redrawn.
- */
- static Boolean
- checkAndUpdate(ch)
- ControlHandle ch;
- {
- Boolean inactive,newactive,newsize,newpict;
- Point owh,wh,d;
- Rect r;
- SignedByte state;
- HIData idh;
- PIData idp;
-
- r = (**ch).contrlRect;
- idh = (HIData)(**ch).contrlData;
- state = HGetState(idh);
- HLock(idh);
- idp = *idh;
-
- inactive = (**ch).contrlHilite == 255;
- newactive = idp->inactive != inactive;
- owh.h = r.right-r.left;
- owh.v = r.bottom-r.top;
- wh.h = idp->lastR.right - idp->lastR.left;
- wh.v = idp->lastR.bottom - idp->lastR.top;
- newsize = (owh.h != wh.h) || (owh.v != wh.v);
- newpict = newsize || newactive;
- if (newpict) {
- idp->inactive = inactive;
- idp->lastR = r;
- BMSize(idp->image,&r);
- BMErase(idp->image);
- BMInBitMapDo(idp->image,redrawPict,ch);
- } else if (!EqualRect(&r,&idp->lastR)) {
- d.h = r.left - idp->lastR.left;
- d.v = r.top - idp->lastR.top;
- BMSize(idp->image,&r);
- idp->lastR = r;
- OffsetRect(&idp->titleR,d.h,d.v);
- OffsetRect(&idp->numR,d.h,d.v);
- OffsetRect(&idp->trackR,d.h,d.v);
- AddPt(d,&idp->cp);
- }
- HSetState(idh,state);
-
- return newpict;
- }
-