home *** CD-ROM | disk | FTP | other *** search
- /*
- * BarChart, A simple multi source loadable class.
- *
- * Written by: Joe Freeman 7/92
- *
- * HSB color sweep stolen from some of Randy Nelson's code
- * should use NXEqualColor() for duplicates
- *
- */
-
-
- #import "ChartOfMatrix.h"
- #import <dpsclient/psops.h>
- #import <c.h>
- #import <stdio.h>
-
- #define NUM_BOGUS 8 /* number of bars to draw when no data */
- #define COM_VERSION 3.3 /* version 3 ported to 3.0 */
-
- @implementation ChartOfMatrix
-
-
- /*============================================================
- *factory
- *============================================================*/
-
-
- + initialize
- {
- [super initialize];
- [ChartOfMatrix setVersion:2];
- return self;
- }
-
- - (const char *)getInspectorClassName
- {
- return "ChartOfMatrixInspector"; }
-
- - initFrame:(NXRect *)r
- {
- self = [super initFrame:r];
- minSheetSet = 0.0;
- maxSheetSet = 1.0;
- COM_Flags.autoScale = YES;
- COM_Flags.drawType = DRAW_V_BAR;
- backgroundColor = NX_COLORWHITE;
- highlightColor = NX_COLORBLACK;
- highlightIndex = MAXINT; /* don't show on default */
- COM_Flags.drawFrame = YES;
- hMargin = vMargin = 15.0;
- hMargin = vMargin = 5.0;
- numPrototypes = 5;
- COM_Flags.randomBarColors = YES;
- return self;
- }
-
- - awake
- {
- [super awake];
- highlightIndex = MAXINT;
- [self registerForDraggedTypes:&NXColorPboardType count:1];
- return self;
- }
-
- /*============================================================
- * color dragging support
- *============================================================*/
-
- - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
- {
- if ([sender draggingSourceOperationMask] & NX_DragOperationGeneric) {
- return NX_DragOperationGeneric;
- } else {
- return NX_DragOperationNone;
- }
- }
-
- - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
- {
- NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]);
- [self setBackgroundColor: c];
- return YES;
- }
- /*============================================================
- * instance set / query methods
- *============================================================*/
-
- - setDataSrc:anObject
- {
- dataSrc = anObject;
- return self;
- }
-
- - setGraphType:(int)drawCode
- { COM_Flags.drawType = drawCode; [self update]; return self; }
- - (int)graphType
- { return COM_Flags.drawType; }
-
- - takeRandomColorStateFrom:sender
- { [self setRandomBarColorEnabled:[sender state]]; return self; }
- - setRandomBarColorEnabled:(BOOL)flag
- { COM_Flags.randomBarColors = flag; [self update]; return self; }
- - (BOOL)isRandomBarColorEnabled
- { return COM_Flags.randomBarColors; }
-
- - takeBackgroundColorFrom:sender
- { [self setBackgroundColor: [sender color]]; return self; }
- - setBackgroundColor:(NXColor)aColor
- { backgroundColor = aColor; [self update]; return self; }
- - (NXColor)backgroundColor
- { return backgroundColor; }
-
- - takeHighlightColorFrom:sender
- { [self setHighlightColor: [sender color]]; return self; }
- - setHighlightColor:(NXColor)aColor
- { highlightColor = aColor; [self update]; return self; }
- - (NXColor)highlightColor
- { return highlightColor; }
-
-
- /* set and query the size for the margins (in points) */
- - takeHMarginFrom:sender
- { hMargin = [sender floatValue]; [self update]; return self; }
- - takeVMarginFrom:sender
- { vMargin = [sender floatValue]; [self update]; return self; }
- - (float)hMargin
- { return hMargin; }
- - (float)vMargin
- { return vMargin; }
-
- - takeAutoScaleStateFrom:sender
- { COM_Flags.autoScale = [sender floatValue]; return self; }
- - setAutoScale:(BOOL)flag
- { COM_Flags.autoScale = flag; [self update]; return self; }
- - (BOOL)autoScale
- { return COM_Flags.autoScale; }
-
- /* fancy controls */
- - takeFrameStateFrom:sender
- { COM_Flags.drawFrame = [sender state]; [self update]; return self; }
- - (BOOL)frameState
- { return COM_Flags.drawFrame; }
-
- - takeNumProtosFrom:sender
- { numPrototypes= [sender intValue]; [self update]; return self; }
- - (int)numProtos
- { return numPrototypes; }
-
- - takeBorderTypeFrom:sender
- { borderType = [sender tag]; [self update]; return self; }
- - (int)borderType
- { return borderType; }
-
- /* when autoscale is off, set the min and max for the sheet */
- - takeMinValueFrom:sender
- { minSheetSet = [sender floatValue]; return self; }
- - takeMaxValueFrom:sender
- { maxSheetSet = [sender floatValue]; return self; }
- - (double)minValue
- { return minSheetSet; }
- - (double)maxValue
- { return maxSheetSet; }
-
-
- /*============================================================
- * target/action
- *============================================================*/
-
- - (BOOL)acceptsFirstResponder { return YES; }
-
- - copy:sender
- {
- id pb = [Pasteboard new]; /* global pasteboard object */
- NXStream *st; /* stream to collect data in */
- char *data; /* actual data buffer */
- int length; /* length of data */
- int maxLength; /* (not used here) */
-
- /* declare that we will supply a single type of data, eps */
- [pb declareTypes:&NXPostScriptPboard num:1 owner:self];
-
- /* get a stream which writes to memory */
- st = NXOpenMemory (NULL, 0, NX_WRITEONLY);
-
- /* find bounding box and then write it to the stream */
- [self copyPSCodeInside:&bounds to:st];
-
- /* get actuall data buffer form stream */
- NXGetMemoryBuffer (st, &data, &length, &maxLength);
-
- /* write data to pasteboard */
- [pb writeType:NXPostScriptPboard data:data length:length ];
-
- /* dealocate stream including it's buffer */
- NXCloseMemory (st, NX_FREEBUFFER );
-
-
- return self;
- }
-
- /*============================================================
- * do real work
- *============================================================*/
-
- - (int)numLocations
- {
- int numRows,numCols;
-
- if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){
- return [dataSrc count];
- } else if ([dataSrc respondsTo:@selector(selectedCell)]){
- [dataSrc getNumRows:&numRows numCols:&numCols];
- return MAX(numRows,numCols);
- } else if (!dataSrc) {
- /* put up some dummy graph on palette */
- return NUM_BOGUS;
- }
- return 0;
- }
-
- - (float)valueOfLocation:(int)n
- {
- static float data[NUM_BOGUS] = {.1, .3, .2, .7, .4, .8, .5, .98};
- int numRows,numCols;
- float theValue;
- id mrValue = [[DBValue alloc] init];
-
- if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){
- [dataSrc getValue:mrValue forProperty:mrExpression at:n];
- theValue = [mrValue floatValue];
- [mrValue free];
- return theValue;
- } else if ([dataSrc respondsTo:@selector(selectedCell)]){
- [dataSrc getNumRows:&numRows numCols:&numCols];
- if (numCols == 1) {
- return [[dataSrc cellAt:n :0] floatValue];
- } else { /* numRows == 1 */
- return [[dataSrc cellAt:0 :n] floatValue];
- }
- } else if (!dataSrc) {
- return data[n];
- }
-
- return 0.0;
- }
-
- /*============================================================
- * target action loading
- *============================================================*/
-
- - plotFromMatrix:sender
- {
- if (!dataSrc && [sender respondsTo:@selector(selectedCell)]){
- [self setDataSrc:sender];
- }
- [self update];
- return self;
- }
-
- /*============================================================
- * dbKit support
- *============================================================*/
-
- - associationContentsDidChange:association
- {
- mrFetchGroup = [association fetchGroup];
- dataSrc = [mrFetchGroup recordList];
- highlightIndex = [mrFetchGroup currentRecord];
- mrExpression = [association expression];
- [self update];
- return self;
- }
-
- /*
- ** adh 7/27/92
- ** Do this so we redraw when values are updated in the UI
- */
- - association:association setValue:(DBValue *)value
- {
- return [self update];
- }
-
- - associationSelectionDidChange:association
- {
- /* assume fetchgroup doesn't change so don't update mrFetchGroup */
- highlightIndex = [[association fetchGroup] currentRecord];
- return [self update];
- }
-
- - associationCurrentRecordDidDelete:association
- {
- /* assumes record list is the same */
- return [self update];
- return self;
- }
-
- /*============================================================
- * do a selection with the mouse
- *============================================================*/
-
- - mouseDown:(NXEvent *)theEvent
- {
- NXEvent lastEvent;
- NXRect rectOfBar;
- NXRect drawRect;
- int longSize = [self numLocations];
- int index;
-
-
- if ((COM_Flags.drawType != DRAW_H_BAR &&
- COM_Flags.drawType != DRAW_V_BAR ) ||
- ![dataSrc respondsTo:@selector(getValue:forProperty:at:)]) {
- NXBeep();
- return nil;
- }
-
- lastEvent = *theEvent;
- [self convertPoint: &lastEvent.location fromView:nil];
- /* modify the mouse position to be in psuedo drawing area coords */
- lastEvent.location.x -= hMargin;
- lastEvent.location.y -= vMargin;
-
- /* allow for the margins when calc'ing the bar moused down on */
- drawRect = bounds;
- drawRect.origin.x += hMargin;
- drawRect.origin.y += vMargin;
- drawRect.size.width -= (2*hMargin);
- drawRect.size.height -= (2*vMargin);
-
- for ( index = 0 ; index < longSize; index++){
- if (COM_Flags.drawType == DRAW_V_BAR) {
- [self calcRect:&rectOfBar ofBar:index
- insideRect:&drawRect
- usingMin:NX_Y(&bounds) ];
- rectOfBar.origin.y = bounds.origin.y;
- rectOfBar.size.height = bounds.size.height;
- } else if (COM_Flags.drawType == DRAW_H_BAR) {
- [self calcRect:&rectOfBar ofBar:index
- insideRect:&drawRect
- usingMin:NX_X(&bounds) ];
- rectOfBar.origin.x = bounds.origin.x;
- rectOfBar.size.width = bounds.size.width;
- } else {
- return nil;
- }
- if ([self mouse:&lastEvent.location inRect:&rectOfBar]){
- [mrFetchGroup setCurrentRecord:index];
- break;
- }
- }
- return self;
- }
-
- /*============================================================
- * target/action
- *============================================================*/
-
- - read:(NXTypedStream *)stream
- {
- int tmpScale, tmpDrawFrame, tmpDrawType;
- [super read:stream];
- dataSrc = NXReadObject(stream);
- minField = NXReadObject(stream);
- maxField = NXReadObject(stream);
- meanField = NXReadObject(stream);
- /* the first demo palette went out as version 1 */
- if (NXTypedStreamClassVersion(stream, "ChartOfMatrix")<2) {
- NXReadTypes(stream,"ffffiii",
- &minSheetSet,&maxSheetSet,
- &vMargin,&hMargin,
- &tmpScale,
- &tmpDrawType,
- &tmpDrawFrame);
- COM_Flags.autoScale = tmpScale;
- COM_Flags.drawType = tmpDrawType;
- COM_Flags.drawFrame = tmpDrawFrame;
- COM_Flags.randomBarColors = YES;
- } else {
- NXReadTypes(stream,"ffffi",
- &minSheetSet,&maxSheetSet,
- &vMargin,&hMargin,
- &COM_Flags);
- }
-
- backgroundColor = NXReadColor(stream);
- highlightColor = NXReadColor(stream);
- NXReadTypes(stream,"ii",
- &numPrototypes,
- &borderType);
- return self;
- }
-
- - write:(NXTypedStream *)stream
- {
- [super write:stream];
- NXWriteObjectReference(stream, dataSrc);
- NXWriteObjectReference(stream, minField);
- NXWriteObjectReference(stream, maxField);
- NXWriteObjectReference(stream, meanField);
- NXWriteTypes(stream,"ffffi",
- &minSheetSet,&maxSheetSet,
- &vMargin,&hMargin,
- &COM_Flags);
- NXWriteColor(stream, backgroundColor);
- NXWriteColor(stream, highlightColor);
- NXWriteTypes(stream,"ii",
- &numPrototypes,
- &borderType);
- return self;
- }
-
- /*============================================================
- *display
- *============================================================*/
-
- - calcMin:(float *)rMin andMax:(float *)rMax andMean:(float *)rMean;
- {
- int index;
- int longSize;
- float thisVal;
- float sumAll = 0;
-
- *rMin = *rMax = [self valueOfLocation:0];
- longSize = [self numLocations];
-
- /* first figure out what the maxs and mins are */
- for ( index = 0 ; index < longSize; index++){
- thisVal = [self valueOfLocation:index];
- if (*rMin > thisVal) *rMin = thisVal;
- if (*rMax < thisVal) *rMax = thisVal;
- sumAll += thisVal;
- }
- *rMean = sumAll / longSize;
-
- [minField setFloatValue:*rMin];
- [maxField setFloatValue:*rMax];
- [meanField setFloatValue:*rMean];
- return self;
- }
-
- - renderVLines:(NXRect *)r min:(float )minSheetVal max:(float )maxSheetVal
- {
- int longSize; /* numRows or numCols, whichever is the long side */
- int index;
- float thisVal;
- float cellWidth; /* the width of a unit (N) to plot */
-
- /* scale the plot */
- PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) );
- PStranslate(0.0, -minSheetVal);
-
- longSize = [self numLocations];
- cellWidth = r->size.width / (longSize);
-
- /* now plot each square */
- PSsetgray(NX_BLACK);
- thisVal = [self valueOfLocation:0];
- PSmoveto(cellWidth/2.0, thisVal);
- for ( index = 0 ; index < longSize; index++){
- thisVal = [self valueOfLocation:index];
- PSlineto(cellWidth*index+ cellWidth/2.0, thisVal);
- }
- PSstroke();
- return self;
- }
-
- /* we can position the bars anywhere inside the view by changing the
- * insideRect parameter
- */
- - calcRect:(NXRect *)r
- ofBar:(int)n
- insideRect:(NXRect *)boundingRect
- usingMin:(float)minSheetVal
- {
- float thisVal;
- float cellWidth; /* width of a unit (N) to plot */
-
- thisVal = [self valueOfLocation:n];
- if (COM_Flags.drawType == DRAW_V_BAR){
- cellWidth = boundingRect->size.width / (3 * [self numLocations] + 1);
- r->origin.x = n * 3 * cellWidth + cellWidth;
- if (thisVal < 0.0){
- r->origin.y = thisVal;
- r->size.height = -thisVal;
- }else{
- r->origin.y = MAX(minSheetVal, 0.0);
- r->size.height= thisVal-r->origin.y;
- }
- r->size.width = 2 * cellWidth;
- } else /* assume h bar */ {
- cellWidth = boundingRect->size.height / (3 * [self numLocations] + 1);
- r->origin.y = n * 3 * cellWidth + cellWidth;
- if (thisVal < 0.0){
- r->origin.x = thisVal;
- r->size.width = -thisVal;
- }else{
- r->origin.x = MAX(minSheetVal, 0.0);
- r->size.width= thisVal - r->origin.x;
- }
- r->size.height = 2 * cellWidth;
- }
-
- return self;
- }
-
- /* this should probably be empty but because Kris asked for a bar/lines switch
- * in the inspectorwe have the abillity to draw bars (vertical). Of course
- * this makes the vertical bar drawing object almost codeless
- */
- - renderBars:(NXRect *)r min:(float )minSheetVal max:(float )maxSheetVal
- {
- int longSize; /* numRows/numCols, whichever is long side */
- int index;
- NXRect barRect; /* bounding rectangle for bar in the chart */
- NXColor HSBColor; /* in case randomBarColors */
-
- longSize = [self numLocations];
- if (COM_Flags.drawType == DRAW_V_BAR){
- /* scale the plot */
- PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) );
- PStranslate(0.0, -minSheetVal);
- } else /* assume h bar */ {
- /* scale the plot */
- PSscale(r->size.width / ( maxSheetVal - minSheetVal), 1.0 );
- PStranslate( -minSheetVal, 0.0);
- }
-
- /* now plot each square */
- for ( index = 0 ; index < longSize; index++){
- if (index == highlightIndex)
- NXSetColor(highlightColor);
- else if (!COM_Flags.randomBarColors) {
- PSsetgray ((1.0 / (longSize+2.0)) * (index +1));
- } else {
- HSBColor = NXConvertHSBToColor(
- ((float)index / (float)longSize),
- 1.0, 1.0);
- NXSetColor(HSBColor);
- }
- [self calcRect:&barRect ofBar:index
- insideRect:(NXRect *)r
- usingMin:minSheetVal];
- NXRectFill(&barRect);
- }
- return self;
- }
-
-
- /* spacing: Each plot is a width of 2N and each gap is a width of 1N
- * total width is m*(2N+N) + N = 3mN+N = where m = number of bars
- * Thus N = width / (3m+1)
- *
- * scaling: The scaling can make some stuff look pretty funny.
- */
- - drawSelf:(NXRect *)r :(int)c
- {
- float minCellVal, maxCellVal; /* min & max of values to be plotted */
- float meanCellVal; /* mean of the plotted values */
- float minSheetVal,maxSheetVal;/* RangeOfNumbers will plot in graph */
- NXRect rectOfPlot; /* bounds of rect that will hold plot */
- NXSetColor(backgroundColor);
- NXRectFill(&bounds);
- PSsetgray(NX_BLACK);
- switch (borderType) {
- case NX_LINE:
- NXFrameRect(&bounds);
- break;
- case NX_BEZEL:
- NXDrawWhiteBezel(&bounds,&bounds);
- break;
- case NX_GROOVE:
- NXDrawGroove(&bounds, &bounds);
- break;
- default: break;
- }
- rectOfPlot= bounds;
- rectOfPlot.origin.x += hMargin;
- rectOfPlot.origin.y += vMargin;
- rectOfPlot.size.width -=2*hMargin;
- rectOfPlot.size.height -=2*vMargin;
- if ( NX_WIDTH(&bounds) < (2*hMargin) ||
- NX_HEIGHT(&bounds) < (2*vMargin) )
- return self;
-
-
- [window disableFlushWindow];
- PSgsave();
- PSsetlinewidth(0.0);
- PStranslate (hMargin, vMargin);
-
- if (YES){
- /* great we only have a single axis to work on */
- [self calcMin:&minCellVal andMax:&maxCellVal andMean:&meanCellVal];
-
- if (COM_Flags.autoScale){
- /* give us a plot if they are all the same (but not 0.0) */
- if (minCellVal == maxCellVal){
- if (minCellVal > 0.0) minCellVal = 0.0;
- else if (maxCellVal < 0.0) maxCellVal = 0.0;
- }
-
- /* figure out what min and the max should be on the sheet */
- if (minCellVal == 0.0) minSheetVal= 0.0;
- else minSheetVal= minCellVal - ((maxCellVal - minCellVal)*0.2);
- /* we shouldn't have pushed this across the origin */
- if (minSheetVal < 0.0 && minCellVal > 0.0) minSheetVal = 0.0;
-
- if (maxCellVal == 0.0) maxSheetVal = 0.0;
- else maxSheetVal= maxCellVal +((maxCellVal - minCellVal)* 0.2);
- /* make sure we didn't go across the origin */
- if (maxSheetVal > 0.0 && maxCellVal < 0.0) maxSheetVal = 0.0;
- } else {
- minSheetVal = minSheetSet;
- maxSheetVal = maxSheetSet;
- }
-
- switch(COM_Flags.drawType){
- case DRAW_H_BAR:
- case DRAW_V_BAR:
- [self renderBars:&rectOfPlot min:minSheetVal max:maxSheetVal];
- break;
- case DRAW_H_LINE:
- break;
- case DRAW_V_LINE:
- [self renderVLines:&rectOfPlot min:minSheetVal max:maxSheetVal ];
- break;
- default:
- break;
- }
-
- PSgrestore();
- if (COM_Flags.drawFrame){
- PSsetgray(NX_BLACK);
- NXFrameRect(&rectOfPlot);
- }
- }
- [window reenableFlushWindow];
- return self;
- }
-
- @end
-