home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************************
- CLASS: MiscGaugeCell
-
- For more information, see the interface file.
-
- Since most of the code from this class came from the GaugeView class, here
- is the original comments for your reading pleasure:
-
- GaugeView.m, analog gauge view
- Author: Bruce Blumberg, NeXT Developer Support Group.
- Originally written for 0.6 mid 1988, modified for 1.0 by Ali Ozer.
- Redesigned for 2.0 by Julie Zelenski, NeXT Developer Support
-
- Subclass of view to implement a simple round analog gauge. You can set the
- minimum, maximum value, start angle, angle range, title, font, and more.
- It is a pretty generic round gauge view, if you ever have need for one.
-
- You may freely copy, distribute and reuse the code in this example.
- NeXT disclaims any warranty of any kind, expressed or implied, as to
- its fitness for any particular use.
-
- This object is included in the MiscKit by permission from the author
- and its use is governed by the MiscKit license, found in the file
- "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
- for a list of all applicable permissions and restrictions.
- ****************************************************************************/
-
- #import <appkit/appkit.h>
- #import "Gauge.h" // For the pswraps.
- #import "MiscGaugeCell.h"
-
- // Used for class versioning. If you happen to change the ivars, then
- // bump the version up and update the read:/write: methods.
- #define MISCGAUGECELL_VERSION 0
- #define MISCGAUGECELL_NAME "MiscGaugeCell"
-
-
- @implementation MiscGaugeCell
-
- + initialize
- {
- if (self == [MiscGaugeCell class])
- // Set the class's version for archiving purposes.
- [self setVersion: MISCGAUGECELL_VERSION];
- return self;
- }
-
-
- - init
- {
- if ([super init] == nil)
- return nil;
-
- cache = [ [NXImage alloc] init];
- radius = 0.0;
- center.x = center.y = 0.0;
- startAngle = angleRange = degreesPerUnit = 0.0;
- tickInterval = 0;
- gaugeColor = NXConvertGrayToColor (NX_DKGRAY);
- textColor = NXConvertGrayToColor (NX_BLACK);
- // backgroundColor = NXConvertGrayToColor (NX_LTGRAY);
- minValue = 0.0;
- maxValue = 1.0;
- value = 0.0;
- strValue = NULL;
- tickRatio = 0.65;
- handRatio = 0.6;
- degreesPerUnit = angleRange/(maxValue-minValue);
- [self setType: NX_TEXTCELL]; // Need this so I can use [self font].
- [self setTitle: "Gauge"];
- [self setTitlePosition: NX_ATTOP];
- titleFont = [self font];
-
- return self;
- }
-
-
- - free
- {
- if (strValue != NULL)
- NX_FREE (strValue);
- if (cache != nil)
- cache = [cache free];
- return [super free];
- }
-
-
- - (float)maxValue { return maxValue; }
- - (float)minValue { return minValue; }
- - (float)floatValue { return value; }
- - (double)doubleValue { return (double)value; }
- - (int)intValue { return (int)value; }
-
-
- - (const char *)stringValue
- {
- if (strValue != NULL)
- NX_FREE (strValue);
-
- NX_MALLOC (strValue, char, 20);
- sprintf (strValue, "%f", value);
- return strValue;
- }
-
-
- - setMaxValue: (float)max
- {
- if ([self maxValue] == max)
- return self;
-
- maxValue = max;
- if ([self minValue] >= [self maxValue])
- [self setMinValue: max-1];
- if ([self floatValue] > maxValue)
- [self setFloatValue: maxValue];
-
- degreesPerUnit = angleRange/(maxValue-minValue);
- return self;
- }
-
-
- - setMinValue: (float)min
- {
- if ([self minValue] == min)
- return self;
-
- minValue = min;
- if ([self minValue] >= [self maxValue])
- [self setMaxValue: min+1];
- if ([self floatValue] < minValue)
- [self setFloatValue: minValue];
-
- degreesPerUnit = angleRange/(maxValue-minValue);
- return self;
- }
-
-
- - setFloatValue: (float)val
- {
- if (val < [self minValue])
- val = minValue;
- if (val > [self maxValue])
- val = maxValue;
- value = val;
- return self;
- }
-
-
- - setDoubleValue: (double)val
- {
- return [self setFloatValue: (float)val];
- }
-
-
- - setIntValue: (int)val
- {
- return [self setFloatValue: (float)val];
- }
-
-
- - setStringValue: (const char *)val
- {
- if (val == NULL)
- [self setFloatValue: 0.0];
- else
- [self setDoubleValue: atof(val)];
- return self;
- }
-
-
- //- (NXColor)backgroundColor { return backgroundColor; }
- - (NXColor)gaugeColor { return gaugeColor; }
- - (NXColor)textColor { return textColor; }
-
- /*
- - setBackgroundColor: (NXColor)color
- {
- if (NXEqualColor (color, backgroundColor) == NO)
- backgroundColor = color;
- return self;
- }
- */
-
- - setGaugeColor: (NXColor)color
- {
- if (NXEqualColor (color, gaugeColor) == NO)
- gaugeColor = color;
- return self;
- }
-
-
- - setTextColor: (NXColor)color
- {
- if (NXEqualColor (color, textColor) == NO)
- textColor = color;
- return self;
- }
-
- /*
- - (float)backgroundGray
- {
- float gray;
- NXConvertColorToGray (backgroundColor, &gray);
- return gray;
- }
- */
-
- - (float)gaugeGray
- {
- float gray;
- NXConvertColorToGray (gaugeColor, &gray);
- return gray;
- }
-
-
- - (float)textGray
- {
- float gray;
- NXConvertColorToGray (textColor, &gray);
- return gray;
- }
-
- /*
- - setBackgroundGray: (float)gray
- {
- [self setBackgroundColor: NXConvertGrayToColor(gray)];
- return self;
- }
- */
-
- - setGaugeGray: (float)gray
- {
- [self setGaugeColor: NXConvertGrayToColor(gray)];
- return self;
- }
-
-
- - setTextGray: (float)gray
- {
- [self setTextColor: NXConvertGrayToColor(gray)];
- return self;
- }
-
-
- - (float)startAngle { return startAngle; }
- - (float)angleRange { return angleRange; }
- - (int)tickInterval { return tickInterval; }
- - (float)tickRatio { return tickRatio; }
- - (float)handRatio { return handRatio; }
-
-
- - setStartAngle:(float)newValue
- {
- // Sets start angle for gauge, which is the angle of the arm when at the
- // minimum value. needRedraw is set to indicate that face image must be
- // redrawn. 0 degrees is straight right, with increasing numbers going
- // counter clockwise.
- if (newValue < 0.0)
- newValue = 0.0;
- if (newValue > 360.0)
- newValue = 360.0;
- startAngle = newValue;
- return self;
- }
-
-
- - setAngleRange:(float)newValue
- {
- // Sets angle range for gauge, which is the sweep of the arm from minimum
- // value to maximum value. The value cannot exceed 360 degrees (a full
- // revolution). Recalculates degreesPerUnit which is used to
- // determine interval of ticks and labels. needRedraw is set to indicate
- // that face image must be redrawn.
- if (newValue > 360.0)
- newValue = 360.0;
- if (newValue < 0.0)
- newValue = 0.0;
-
- angleRange = newValue;
- degreesPerUnit = angleRange/(maxValue-minValue);
- return self;
- }
-
-
- - setTickInterval:(int)newValue
- {
- // Sets tick interval for gauge, which is number of units between tick
- // marks on gauge face. needRedraw is set to indicate that
- // face image must be redrawn.
- if (newValue < 1)
- newValue = 1;
- tickInterval = newValue;
- return self;
- }
-
-
- - setTickRatio: (float)newRatio
- {
- // Make sure new tick ratio is between 0.0 (no ticks) and 1.0.
- if (newRatio < 0.0)
- newRatio = 0.0;
- if (newRatio > 1.0)
- newRatio = 1.0;
- tickRatio = newRatio;
- return self;
- }
-
-
- - setHandRatio: (float)newRatio
- {
- // Make sure new hand ratio is between 0.2 and 0.9.
- if (newRatio < 0.2)
- newRatio = 0.2;
- if (newRatio > 0.9)
- newRatio = 0.9;
- handRatio = newRatio;
- return self;
- }
-
-
- - titleFont { return titleFont; }
- - (const char *)title { return [super stringValue]; }
- - (int)titlePosition { return titlePosition; }
-
- - setTitleFont: newFont
- {
- if ([self titleFont] != newFont)
- titleFont = newFont;
- return self;
- }
-
-
- - setTitle: (const char *)newTitle
- {
- // We are making use of the ivar contents inherited from Cell.
- // Since we overrode stringValue and setStringValue:, we have
- // to use Cell's implementation.
- [super setStringValue: newTitle];
- return self;
- }
-
-
- - setTitlePosition: (int)newPos
- {
- if (newPos == NX_ATTOP || newPos == NX_ATBOTTOM)
- titlePosition = newPos;
- return self;
- }
-
-
- - calcCellSize: (NXSize *)size inRect:(NXRect *)rect
- {
- // Return the minimum size that the cell will fit in.
- size->width = NX_WIDTH(rect);
- size->height = NX_HEIGHT(rect);
-
- if (size->height < size->width)
- size->width = size->height;
- else
- size->height = size->width;
-
- return self;
- }
-
-
- - drawSelf: (const NXRect *)rect inView: controlView
- {
- center.x = NX_WIDTH(rect)/2.0;
- center.y = NX_HEIGHT(rect)/2.0;
- if (NX_WIDTH(rect) > NX_HEIGHT(rect))
- radius = (NX_HEIGHT(rect)/2.0) - 8.0;
- else
- radius = (NX_WIDTH(rect)/2.0) - 8.0;
-
- // Redraws the face in the cached image.
- [self drawFace: rect];
- // Draws the gauge itself.
- [self drawInside: rect inView: controlView];
- return self;
- }
-
-
- - drawInside: (const NXRect *)rect inView: controlView
- {
- // Composites the "face" of the gauge, then draws the hand.
- // This method will also work inside a flipped view (like a Matrix).
- NXPoint corner = {0.0, 0.0};
-
- // If view is flipped composite from lower left corner.
- if ([controlView isFlipped])
- corner.y = NX_HEIGHT (rect);
-
- // Composite the gauge face into the view.
- [cache composite: NX_SOVER toPoint: &corner];
- // Draw the hand.
- [self drawHand: rect flipped: [controlView isFlipped] ];
-
- return self;
- }
-
-
- - drawHand: (const NXRect *)rect flipped: (BOOL)viewFlipped
- {
- // If you want the gauge to have a different look you should only
- // have to override drawFace: and drawHand:. The passed rect
- // is the same rect that is passed to drawInside:.
- float valueAngle;
-
- valueAngle = startAngle - degreesPerUnit*(value-minValue);
-
- if (viewFlipped == YES)
- {
- PSgsave();
- PSWflipme (NX_HEIGHT(rect));
- }
-
- NXSetColor ([self textColor]);
- PSWdrawHand(center.x,center.y,valueAngle, radius*handRatio);
-
- if (viewFlipped == YES)
- PSgrestore();
- return self;
- }
-
-
- - drawFace: (const NXRect *)rect
- {
- // This method draws the gauge face image in an NXImage. It erases
- // the background, draws the circular border, draws the tick marks and
- // labels them appropriately. Since we are drawing into an NXImage
- // we don't care whether the view we are compositing into is flipped or
- // not.
- float angle, angleIncrement;
- int number;
- NXSize string;
- NXPoint pt;
- char numString[10];
- NXSize cacheSize;
- Font *cellsFont = [self font];
- Font *flippedFont;
- float titleY;
-
- cacheSize.width = NX_WIDTH (rect);
- cacheSize.height = NX_HEIGHT (rect);
- [cache setSize: &cacheSize];
-
- if ([cache lockFocus] == NO)
- return self;
-
- // Do the entire rect in our background color.
- // NXSetColor ([self backgroundColor]);
- PSsetalpha (0.0);
- NXRectFill(rect);
- PSsetalpha (1.0);
-
- // Draw the gauge face.
- NXSetColor ([self gaugeColor]);
- PSWdrawBorder(center.x, center.y, radius);
- angleIncrement = angleRange/((maxValue-minValue)/(float)tickInterval);
- if (tickRatio > 0.0)
- {
- NXSetColor ([self textColor]);
- PSWdrawTicks(center.x, center.y, radius*tickRatio, angleIncrement/2.0,
- startAngle, startAngle+angleRange);
- }
-
- if ([self title] != NULL)
- {
- // Makes sure that the title font if using the NX_IDENTITYMATRIX.
- flippedFont = [Font newFont: [titleFont name]
- size: [titleFont pointSize] matrix: NX_IDENTITYMATRIX];
- [flippedFont set];
-
- // Spit out the title.
- string.height = [flippedFont pointSize];
- string.width = [flippedFont getWidthOf: [self title] ];
- if ([self titlePosition] == NX_ATTOP)
- titleY = center.y+(NX_HEIGHT(rect)/10.0);
- else
- titleY = center.y-(NX_HEIGHT(rect)/12.0)-string.height;
-
- NXSetColor ([self textColor]);
- PSWdrawString((NX_WIDTH(rect)-string.width)/2, titleY,
- [self title]);
- }
-
- // Make sure the cell's font (for the numbers) is also using
- // the NX_IDENTITYMATRIX.
- flippedFont = [Font newFont: [cellsFont name]
- size: [cellsFont pointSize] matrix: NX_IDENTITYMATRIX];
- [flippedFont set];
- string.height = [flippedFont pointSize];
-
- // Spit out all the numbers.
- number = minValue;
- NXSetColor ([self textColor]);
- for(angle=startAngle;angle>=startAngle-angleRange;angle -= angleIncrement){
- sprintf(numString,"%d",number);
- string.width = [cellsFont getWidthOf: numString];
- pt.x = cos(angle/57.3)*(radius-1.0-string.width/2)+ center.x;
- pt.y = sin(angle/57.3)*(radius-1.0-string.height/2) + center.y ;
- PSWdrawString(pt.x-(string.width/2), pt.y-(string.height/2.5),
- numString);
- number += tickInterval;
- }
-
- [cache unlockFocus];
- return self;
- }
-
-
- - highlight:(const NXRect *)cellFrame inView:aView lit:(BOOL)flag
- {
- // We don't want highlighting to occur.
- return self;
- }
-
-
- - awake
- {
- [super awake];
- cache = [ [NXImage alloc] init];
- strValue = NULL;
- degreesPerUnit = angleRange/(maxValue-minValue);
- return self;
- }
-
-
- - read: (NXTypedStream *)stream
- {
- int version;
-
- [super read: stream];
- version = NXTypedStreamClassVersion (stream, MISCGAUGECELL_NAME);
- switch (version)
- {
- case MISCGAUGECELL_VERSION:
- NXReadTypes (stream, "ffiff", &startAngle, &angleRange,
- &tickInterval, &tickRatio, &handRatio);
- NXReadTypes (stream, "fffs", &minValue, &maxValue, &value,
- &titlePosition);
- // backgroundColor = NXReadColor (stream);
- gaugeColor = NXReadColor (stream);
- textColor = NXReadColor (stream);
- titleFont = NXReadObject (stream);
- break;
-
- default:
- NXLogError ("Could not read typed stream for class %s, version %d", MISCGAUGECELL_NAME, version);
- break;
- }
- return self;
- }
-
-
- - write: (NXTypedStream *)stream
- {
- [super write: stream];
- NXWriteTypes (stream, "ffiff", &startAngle, &angleRange,
- &tickInterval, &tickRatio, &handRatio);
- NXWriteTypes (stream, "fffs", &minValue, &maxValue, &value,
- &titlePosition);
- // NXWriteColor (stream, backgroundColor);
- NXWriteColor (stream, gaugeColor);
- NXWriteColor (stream, textColor);
- NXWriteObject (stream, titleFont);
- return self;
- }
-
- @end
-
-
- /****************************************************************************
- CHANGES:
- Version 0.3, March 19, 1995
- 1. Fixed so it would draw with a transparent background correctly.
- Version 0.4, March 21, 1995
- 2. Removed the lastRect and needRedraw ivars since as long as
- drawSelf:inView: is called only when needed the face needs
- redrawing (from GaugeView) then drawing will be fairly efficient.
- 3. Added drawHand:flipped: so you could easily customize the look
- of the gauge face and hand.
-
- ****************************************************************************/
-