home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscGaugePalette / MiscGaugeView.subproj / MiscGaugeCell.m < prev    next >
Encoding:
Text File  |  1995-04-14  |  13.8 KB  |  585 lines

  1. /****************************************************************************
  2.   CLASS:                MiscGaugeCell
  3.   
  4.   For more information, see the interface file.
  5.   
  6.   Since most of the code from this class came from the GaugeView class, here
  7.   is the original comments for your reading pleasure:
  8.    
  9.   GaugeView.m, analog gauge view
  10.   Author: Bruce Blumberg, NeXT Developer Support Group.
  11.   Originally written for 0.6 mid 1988, modified for 1.0 by Ali Ozer.
  12.   Redesigned for 2.0 by Julie Zelenski, NeXT Developer Support
  13.  
  14.   Subclass of view to implement a simple round analog gauge. You can set the 
  15.   minimum, maximum value, start angle, angle range, title, font, and more.  
  16.   It is a pretty generic round gauge view, if you ever have need for one.
  17.  
  18.   You may freely copy, distribute and reuse the code in this example.  
  19.   NeXT disclaims any warranty of any kind, expressed or implied, as to 
  20.   its fitness for any particular use.
  21.  
  22.   This object is included in the MiscKit by permission from the author
  23.   and its use is governed by the MiscKit license, found in the file
  24.   "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  25.   for a list of all applicable permissions and restrictions.
  26.  ****************************************************************************/
  27.  
  28. #import <appkit/appkit.h>
  29. #import "Gauge.h"                // For the pswraps.
  30. #import "MiscGaugeCell.h"
  31.  
  32. // Used for class versioning. If you happen to change the ivars, then 
  33. // bump the version up and update the read:/write: methods.
  34. #define MISCGAUGECELL_VERSION 0
  35. #define MISCGAUGECELL_NAME "MiscGaugeCell"
  36.  
  37.  
  38. @implementation MiscGaugeCell 
  39.  
  40. + initialize
  41. {
  42.     if (self == [MiscGaugeCell class])
  43.         // Set the class's version for archiving purposes.
  44.         [self setVersion: MISCGAUGECELL_VERSION];
  45.     return self;
  46. }
  47.  
  48.  
  49. - init
  50. {
  51.     if ([super init] == nil)
  52.         return nil;
  53.     
  54.     cache = [ [NXImage alloc] init];   
  55.     radius = 0.0;
  56.     center.x = center.y = 0.0;
  57.     startAngle = angleRange = degreesPerUnit = 0.0;
  58.     tickInterval = 0;
  59.     gaugeColor = NXConvertGrayToColor (NX_DKGRAY);
  60.     textColor = NXConvertGrayToColor (NX_BLACK);
  61. //    backgroundColor = NXConvertGrayToColor (NX_LTGRAY);
  62.     minValue = 0.0; 
  63.     maxValue = 1.0;
  64.     value = 0.0;
  65.     strValue = NULL;
  66.     tickRatio = 0.65;
  67.     handRatio = 0.6;
  68.     degreesPerUnit = angleRange/(maxValue-minValue);
  69.     [self setType: NX_TEXTCELL];    // Need this so I can use [self font].
  70.     [self setTitle: "Gauge"];
  71.     [self setTitlePosition: NX_ATTOP];
  72.     titleFont = [self font]; 
  73.  
  74.     return self;
  75. }
  76.  
  77.  
  78. - free
  79. {
  80.     if (strValue != NULL)
  81.         NX_FREE (strValue);
  82.     if (cache != nil)    
  83.         cache = [cache free];
  84.     return [super free];
  85. }
  86.  
  87.  
  88. - (float)maxValue { return maxValue; }
  89. - (float)minValue { return minValue; }
  90. - (float)floatValue    { return value; }
  91. - (double)doubleValue { return (double)value; }
  92. - (int)intValue { return (int)value; }
  93.  
  94.  
  95. - (const char *)stringValue 
  96.     if (strValue != NULL)
  97.         NX_FREE (strValue);
  98.      
  99.     NX_MALLOC (strValue, char, 20);
  100.     sprintf (strValue, "%f", value);
  101.     return strValue;
  102. }
  103.  
  104.  
  105. - setMaxValue: (float)max
  106. {
  107.     if ([self maxValue] == max)
  108.         return self;
  109.         
  110.     maxValue = max;
  111.     if ([self minValue] >= [self maxValue])
  112.         [self setMinValue: max-1];
  113.     if ([self floatValue] > maxValue)
  114.         [self setFloatValue: maxValue];
  115.  
  116.     degreesPerUnit = angleRange/(maxValue-minValue);
  117.     return self;
  118. }
  119.  
  120.  
  121. - setMinValue: (float)min
  122. {
  123.     if ([self minValue] == min)
  124.         return self;
  125.         
  126.     minValue = min;
  127.     if ([self minValue] >= [self maxValue])
  128.         [self setMaxValue: min+1];
  129.     if ([self floatValue] < minValue)
  130.         [self setFloatValue: minValue]; 
  131.  
  132.     degreesPerUnit = angleRange/(maxValue-minValue);
  133.     return self;
  134. }
  135.  
  136.  
  137. - setFloatValue: (float)val
  138. {
  139.     if (val < [self minValue])
  140.         val = minValue;
  141.     if (val > [self maxValue])
  142.         val = maxValue;
  143.     value = val;
  144.     return self;
  145. }
  146.  
  147.  
  148. - setDoubleValue: (double)val
  149. {
  150.     return [self setFloatValue: (float)val];
  151. }
  152.  
  153.  
  154. - setIntValue: (int)val
  155. {
  156.     return [self setFloatValue: (float)val];
  157. }
  158.  
  159.  
  160. - setStringValue: (const char *)val
  161. {
  162.     if (val == NULL)
  163.         [self setFloatValue: 0.0];
  164.     else
  165.         [self setDoubleValue: atof(val)];
  166.     return self;
  167. }
  168.  
  169.  
  170. //- (NXColor)backgroundColor { return backgroundColor; }
  171. - (NXColor)gaugeColor { return gaugeColor; }
  172. - (NXColor)textColor { return textColor; }
  173.  
  174. /*
  175. - setBackgroundColor: (NXColor)color
  176. {
  177.     if (NXEqualColor (color, backgroundColor) == NO)
  178.         backgroundColor = color;
  179.     return self;
  180. }
  181. */
  182.  
  183. - setGaugeColor: (NXColor)color
  184. {
  185.     if (NXEqualColor (color, gaugeColor) == NO)
  186.         gaugeColor = color;
  187.     return self;
  188. }
  189.  
  190.  
  191. - setTextColor: (NXColor)color
  192. {
  193.     if (NXEqualColor (color, textColor) == NO)
  194.         textColor = color;
  195.     return self;
  196. }
  197.  
  198. /*
  199. - (float)backgroundGray    
  200. {
  201.     float gray;
  202.     NXConvertColorToGray (backgroundColor, &gray);
  203.      return gray; 
  204. }
  205. */
  206.  
  207. - (float)gaugeGray 
  208.     float gray;
  209.     NXConvertColorToGray (gaugeColor, &gray);
  210.      return gray; 
  211. }
  212.  
  213.  
  214. - (float)textGray 
  215.     float gray;
  216.     NXConvertColorToGray (textColor, &gray);
  217.      return gray; 
  218. }
  219.  
  220. /*
  221. - setBackgroundGray: (float)gray
  222. {
  223.     [self setBackgroundColor: NXConvertGrayToColor(gray)];
  224.     return self;
  225. }
  226. */
  227.  
  228. - setGaugeGray: (float)gray
  229. {
  230.     [self setGaugeColor: NXConvertGrayToColor(gray)];
  231.     return self;
  232. }
  233.  
  234.  
  235. - setTextGray: (float)gray
  236. {
  237.     [self setTextColor: NXConvertGrayToColor(gray)];
  238.     return self;
  239. }
  240.  
  241.  
  242. - (float)startAngle { return startAngle; }
  243. - (float)angleRange    { return angleRange; }
  244. - (int)tickInterval { return tickInterval; }
  245. - (float)tickRatio { return tickRatio; }
  246. - (float)handRatio { return handRatio; }
  247.  
  248.  
  249. - setStartAngle:(float)newValue
  250. {
  251.     // Sets start angle for gauge, which is the angle of the arm when at the 
  252.     // minimum value. needRedraw is set to indicate that face image must be 
  253.     // redrawn. 0 degrees is straight right, with increasing numbers going
  254.     // counter clockwise.
  255.     if (newValue < 0.0)
  256.         newValue = 0.0;
  257.     if (newValue > 360.0)
  258.         newValue = 360.0;
  259.     startAngle = newValue;
  260.     return self;
  261. }
  262.  
  263.  
  264. - setAngleRange:(float)newValue
  265. {
  266.     // Sets angle range for gauge, which is the sweep of the arm from minimum
  267.     // value to maximum value.  The value cannot exceed 360 degrees (a full
  268.     // revolution).  Recalculates degreesPerUnit which is used to 
  269.     // determine interval of ticks and labels. needRedraw is set to indicate 
  270.     // that face image must be redrawn.
  271.     if (newValue > 360.0) 
  272.         newValue = 360.0;
  273.     if (newValue < 0.0) 
  274.         newValue = 0.0;
  275.     
  276.     angleRange = newValue;
  277.     degreesPerUnit = angleRange/(maxValue-minValue);
  278.     return self;
  279. }
  280.  
  281.  
  282. - setTickInterval:(int)newValue
  283. {
  284.     // Sets tick interval for gauge, which is number of units between tick
  285.     // marks on gauge face.  needRedraw is set to indicate that 
  286.     // face image must be redrawn.
  287.     if (newValue < 1)
  288.         newValue = 1;
  289.     tickInterval = newValue;
  290.     return self;
  291. }
  292.  
  293.  
  294. - setTickRatio: (float)newRatio
  295. {
  296.     // Make sure new tick ratio is between 0.0 (no ticks) and 1.0.
  297.     if (newRatio < 0.0)
  298.         newRatio = 0.0;
  299.     if (newRatio > 1.0)
  300.         newRatio = 1.0;
  301.     tickRatio = newRatio;
  302.     return self;
  303. }
  304.  
  305.  
  306. - setHandRatio: (float)newRatio
  307. {
  308.     // Make sure new hand ratio is between 0.2 and 0.9.
  309.     if (newRatio < 0.2)
  310.         newRatio = 0.2;
  311.     if (newRatio > 0.9)
  312.         newRatio = 0.9;
  313.     handRatio = newRatio;
  314.     return self;
  315. }
  316.  
  317.  
  318. - titleFont { return titleFont; }
  319. - (const char *)title { return [super stringValue]; }
  320. - (int)titlePosition { return titlePosition; }
  321.  
  322. - setTitleFont: newFont
  323. {
  324.     if ([self titleFont] != newFont)
  325.         titleFont = newFont;
  326.     return self;
  327. }
  328.  
  329.  
  330. - setTitle: (const char *)newTitle
  331. {
  332.     // We are making use of the ivar contents inherited from Cell.
  333.     // Since we overrode stringValue and setStringValue:, we have
  334.     // to use Cell's implementation.
  335.     [super setStringValue: newTitle];
  336.     return self;
  337. }    
  338.     
  339.  
  340. - setTitlePosition: (int)newPos
  341. {
  342.     if (newPos == NX_ATTOP || newPos == NX_ATBOTTOM)
  343.         titlePosition = newPos;
  344.     return self;
  345. }
  346.  
  347.  
  348. - calcCellSize: (NXSize *)size inRect:(NXRect *)rect
  349. {
  350.     // Return the minimum size that the cell will fit in.
  351.     size->width = NX_WIDTH(rect);
  352.     size->height = NX_HEIGHT(rect);
  353.  
  354.     if (size->height < size->width) 
  355.         size->width = size->height;
  356.     else 
  357.         size->height = size->width;
  358.  
  359.     return self;
  360. }
  361.  
  362.  
  363. - drawSelf: (const NXRect *)rect inView: controlView
  364. {
  365.     center.x = NX_WIDTH(rect)/2.0;
  366.     center.y = NX_HEIGHT(rect)/2.0;
  367.     if (NX_WIDTH(rect) > NX_HEIGHT(rect))
  368.         radius = (NX_HEIGHT(rect)/2.0) - 8.0;
  369.     else
  370.         radius = (NX_WIDTH(rect)/2.0) - 8.0;
  371.  
  372.     // Redraws the face in the cached image.        
  373.     [self drawFace: rect];
  374.     // Draws the gauge itself. 
  375.     [self drawInside: rect inView: controlView];
  376.     return self;
  377. }
  378.  
  379.  
  380. - drawInside: (const NXRect *)rect inView: controlView
  381. {
  382.     // Composites the "face" of the gauge, then draws the hand.
  383.     // This method will also work inside a flipped view (like a Matrix).
  384.     NXPoint corner = {0.0, 0.0};
  385.  
  386.     // If view is flipped composite from lower left corner.
  387.     if ([controlView isFlipped])
  388.         corner.y = NX_HEIGHT (rect);
  389.  
  390.     // Composite the gauge face into the view.
  391.     [cache composite: NX_SOVER toPoint: &corner];
  392.     // Draw the hand.
  393.     [self drawHand: rect flipped: [controlView isFlipped] ];    
  394.  
  395.     return self;
  396. }
  397.  
  398.  
  399. - drawHand: (const NXRect *)rect flipped: (BOOL)viewFlipped
  400. {
  401.     // If you want the gauge to have a different look you should only
  402.     // have to override drawFace: and drawHand:. The passed rect
  403.     // is the same rect that is passed to drawInside:.
  404.     float valueAngle;    
  405.  
  406.     valueAngle = startAngle - degreesPerUnit*(value-minValue);
  407.     
  408.     if (viewFlipped == YES)
  409.     {
  410.         PSgsave();
  411.         PSWflipme (NX_HEIGHT(rect));
  412.      }
  413.     
  414.     NXSetColor ([self textColor]);
  415.     PSWdrawHand(center.x,center.y,valueAngle, radius*handRatio);
  416.  
  417.     if (viewFlipped == YES)
  418.         PSgrestore();
  419.     return self;
  420. }
  421.  
  422.     
  423. - drawFace: (const NXRect *)rect 
  424. {    
  425.     // This method draws the gauge face image in an NXImage.  It erases
  426.     // the background, draws the circular border, draws the tick marks and 
  427.     // labels them appropriately. Since we are drawing into an NXImage
  428.     // we don't care whether the view we are compositing into is flipped or 
  429.     // not.
  430.     float angle, angleIncrement;
  431.     int number;
  432.     NXSize string;
  433.     NXPoint pt;
  434.     char numString[10];
  435.     NXSize cacheSize;
  436.     Font *cellsFont = [self font];
  437.     Font *flippedFont;
  438.     float titleY;
  439.     
  440.     cacheSize.width = NX_WIDTH (rect);
  441.     cacheSize.height = NX_HEIGHT (rect);
  442.     [cache setSize: &cacheSize];
  443.     
  444.     if ([cache lockFocus] == NO)
  445.         return self;
  446.  
  447.     // Do the entire rect in our background color.
  448. //    NXSetColor ([self backgroundColor]);
  449.     PSsetalpha (0.0);
  450.     NXRectFill(rect);
  451.     PSsetalpha (1.0);
  452.  
  453.     // Draw the gauge face.
  454.     NXSetColor ([self gaugeColor]);
  455.     PSWdrawBorder(center.x, center.y, radius);
  456.     angleIncrement = angleRange/((maxValue-minValue)/(float)tickInterval); 
  457.     if (tickRatio > 0.0)
  458.     {
  459.         NXSetColor ([self textColor]);
  460.         PSWdrawTicks(center.x, center.y, radius*tickRatio, angleIncrement/2.0, 
  461.                         startAngle, startAngle+angleRange); 
  462.      }
  463.  
  464.     if ([self title] != NULL)
  465.     {
  466.         // Makes sure that the title font if using the NX_IDENTITYMATRIX.     
  467.         flippedFont = [Font newFont: [titleFont name] 
  468.                         size: [titleFont pointSize] matrix: NX_IDENTITYMATRIX];
  469.         [flippedFont set];     
  470.     
  471.         // Spit out the title.
  472.         string.height = [flippedFont pointSize];
  473.         string.width = [flippedFont getWidthOf: [self title] ];
  474.         if ([self titlePosition] == NX_ATTOP)
  475.             titleY = center.y+(NX_HEIGHT(rect)/10.0);
  476.         else
  477.             titleY = center.y-(NX_HEIGHT(rect)/12.0)-string.height;
  478.         
  479.         NXSetColor ([self textColor]);    
  480.         PSWdrawString((NX_WIDTH(rect)-string.width)/2, titleY, 
  481.                         [self title]);
  482.      }
  483.      
  484.     // Make sure the cell's font (for the numbers) is also using
  485.     // the NX_IDENTITYMATRIX.     
  486.     flippedFont = [Font newFont: [cellsFont name] 
  487.                     size: [cellsFont pointSize] matrix: NX_IDENTITYMATRIX];
  488.     [flippedFont set];     
  489.     string.height = [flippedFont pointSize];
  490.     
  491.     // Spit out all the numbers.
  492.     number =  minValue;
  493.     NXSetColor ([self textColor]);
  494.     for(angle=startAngle;angle>=startAngle-angleRange;angle -= angleIncrement){
  495.         sprintf(numString,"%d",number);
  496.         string.width = [cellsFont getWidthOf: numString];
  497.         pt.x = cos(angle/57.3)*(radius-1.0-string.width/2)+ center.x; 
  498.         pt.y = sin(angle/57.3)*(radius-1.0-string.height/2) + center.y ;
  499.         PSWdrawString(pt.x-(string.width/2), pt.y-(string.height/2.5),
  500.             numString);
  501.         number += tickInterval;
  502.     }
  503.     
  504.     [cache unlockFocus]; 
  505.     return self;
  506. }    
  507.  
  508.  
  509. - highlight:(const NXRect *)cellFrame inView:aView lit:(BOOL)flag
  510. {
  511.     // We don't want highlighting to occur.
  512.     return self;
  513. }
  514.  
  515.  
  516. - awake
  517. {
  518.     [super awake];
  519.     cache = [ [NXImage alloc] init];
  520.     strValue = NULL;
  521.     degreesPerUnit = angleRange/(maxValue-minValue);
  522.     return self;
  523. }
  524.  
  525.  
  526. - read: (NXTypedStream *)stream
  527. {
  528.     int version;
  529.     
  530.     [super read: stream];
  531.     version = NXTypedStreamClassVersion (stream, MISCGAUGECELL_NAME);
  532.     switch (version)
  533.     {
  534.         case MISCGAUGECELL_VERSION:
  535.             NXReadTypes (stream, "ffiff", &startAngle, &angleRange, 
  536.                             &tickInterval, &tickRatio, &handRatio);
  537.             NXReadTypes (stream, "fffs", &minValue, &maxValue, &value,
  538.                             &titlePosition);
  539. //            backgroundColor = NXReadColor (stream);
  540.             gaugeColor = NXReadColor (stream);
  541.             textColor = NXReadColor (stream);
  542.             titleFont = NXReadObject (stream);
  543.             break;
  544.             
  545.         default:
  546.             NXLogError ("Could not read typed stream for class %s, version %d", MISCGAUGECELL_NAME, version);
  547.             break;
  548.      }
  549.     return self;
  550. }
  551.  
  552.  
  553. - write: (NXTypedStream *)stream
  554. {
  555.     [super write: stream];
  556.     NXWriteTypes (stream, "ffiff", &startAngle, &angleRange, 
  557.                     &tickInterval, &tickRatio, &handRatio);
  558.     NXWriteTypes (stream, "fffs", &minValue, &maxValue, &value,
  559.                     &titlePosition); 
  560. //    NXWriteColor (stream, backgroundColor);
  561.     NXWriteColor (stream, gaugeColor);
  562.     NXWriteColor (stream, textColor);
  563.     NXWriteObject (stream, titleFont);
  564.     return self;
  565. }
  566.  
  567. @end
  568.  
  569.  
  570. /****************************************************************************
  571.   CHANGES:
  572.    Version 0.3, March 19, 1995
  573.   1. Fixed so it would draw with a transparent background correctly.
  574.    Version 0.4, March 21, 1995
  575.   2. Removed the lastRect and needRedraw ivars since as long as 
  576.      drawSelf:inView: is called only when needed the face needs 
  577.      redrawing (from GaugeView) then drawing will be fairly efficient.
  578.   3. Added drawHand:flipped: so you could easily customize the look
  579.      of the gauge face and hand.
  580.      
  581.  ****************************************************************************/
  582.