This is Ruler.m in view mode; [Download] [Up]
// Ruler.m // A general ruler class designed to be used with the RuledScrollView class #import <dpsclient/psops.h> #import <libc.h> // for atoi and strrchr #import <math.h> #import <appkit/Font.h> #import <appkit/ScrollView.h> #import <appkit/Application.h> // for NXApp method #import "RuledScrollView.h" // for retrieving rulers #import "Ruler.h" #define DEFAULTSCALESPACING (72*0.39) @implementation Ruler /* instance methods */ /* Initializing and freeing an instance */ - initFrame:(NXRect *)frameRect { [super initFrame:frameRect]; scaleSpacing = DEFAULTSCALESPACING; orientation = HORIZONTAL; inverted = NO; mirrored = NO; scale = 1.0; scaleOffset = 0; border = 0.0; scaleSpacing = DEFAULTSCALESPACING; alignment = NX_CENTERED; leftOrBottomMargin = 0; rightOrTopMargin = 0; backgroundColor = NXConvertRGBToColor(1.0,1.0,1.0); backgroundGray = NX_LTGRAY; draggableRuler = NO; units = malloc(2*sizeof(char)); strcpy(units,"\0"); font = [Font newFont:"Helvetica" size:12 style:0 matrix:NX_IDENTITYMATRIX]; NXTextFontInfo(font, &ascender, &descender, &lineHeight); return self; } - free { if (units) free(units); return [super free]; } /* Drawing the view */ - drawSelf:(NXRect *)rects :(int)rectCount { char buf[25]; float r,g,b; int j,start,end; float x0,y0,x1,y1,measure, halfWidth; float vtextOffset,htextOffset; x0 = rects[0].origin.x; y0 = rects[0].origin.y; x1 = x0 + rects[0].size.width; y1 = y0 + rects[0].size.height; NXConvertColorToRGB(backgroundColor, &r, &g, &b); if ((r!=1.0) || (g!=1.0) || (b!=1.0)) { PSsetrgbcolor(r,g,b); } else PSsetgray(backgroundGray); NXRectFill(&(rects[0])); [font set]; // draw a line along the edge of the ruler next to the 'rulered view' PSsetgray(NX_BLACK); PSsetlinewidth(0.0); if (orientation == HORIZONTAL) { if (inverted) { PSmoveto(x0,1); PSlineto(x1,1); } else { PSmoveto(x0,bounds.size.height); PSlineto(x1,bounds.size.height); } } else { if (inverted) { PSmoveto(0,y0); PSlineto(0,y1); } else { PSmoveto(bounds.size.width-1,y0); PSlineto(bounds.size.width-1,y1); } } PSstroke(); // draw measures if (orientation == HORIZONTAL) { start = (int)floor((double)(x0/scaleSpacing))-2; end = (int)ceil((double)(x1/scaleSpacing))+1; } else { start = (int)floor((double)(y0/scaleSpacing))-2; end = (int)ceil((double)(y1/scaleSpacing))+1; } for(j=start; j<=end; j++) { measure = j*scale+scaleOffset; sprintf(buf,"%.4g",measure); strcatn(buf,units,23-strlen(buf)); halfWidth = [font getWidthOf:buf]/2; switch (alignment) { case NX_LEFTALIGNED: htextOffset = 2; break; case NX_RIGHTALIGNED: if (orientation == HORIZONTAL) htextOffset = -2-(halfWidth*2); else htextOffset = bounds.size.width-2-(halfWidth*2); break; default: // default is centered if (orientation == HORIZONTAL) htextOffset = -halfWidth; else htextOffset = bounds.size.width/2-halfWidth; } if (orientation == HORIZONTAL) { if (inverted) vtextOffset = TEXTBASELINE; else vtextOffset = bounds.size.height - lineHeight; if (vtextOffset < TEXTBASELINE) vtextOffset = TEXTBASELINE; PSmoveto(j*scaleSpacing+leftOrBottomMargin+htextOffset+border, vtextOffset); } else PSmoveto(htextOffset, j*scaleSpacing+leftOrBottomMargin-(lineHeight/2)+border); if (j>=0) PSshow(buf); } return self; } /* Event handling */ - (BOOL) acceptsFirstMouse { return YES; } - mouseDown:(NXEvent *)theEvent { id rulerWindow; int oldMask, looping = YES; NXRect windowRect,tempRect,visibleRect; NXPoint offset, mouseLocation; BOOL shiftHeld = NO; if (!draggableRuler) return self; if (theEvent->flags & NX_SHIFTMASK) shiftHeld = YES; // if(theEvent->data.mouse.click == 2) /* action for a double-click event */ /* convert the visible rect to screen-based coordinates */ [self getVisibleRect:(NXRect *)&windowRect]; [self convertRect:&windowRect toView:nil]; [window convertBaseToScreen:&windowRect.origin]; /* adjust for width of outline */ windowRect.size.width += 2; windowRect.size.height += 2; windowRect.origin.x -= 1; windowRect.origin.y -= 1; tempRect.size = windowRect.size; tempRect.origin = bounds.origin; rulerWindow = [[Window alloc] initContent:(const NXRect *)&windowRect style:(int)NX_PLAINSTYLE // no title bar and frame backing:(int)NX_BUFFERED buttonMask:(int)0 // no controls in title bar and frame defer:(BOOL)NO]; /* so we can draw the currently visible region */ [self getVisibleRect:&visibleRect]; /* draw the ruler into the newly created window's content view */ [[rulerWindow contentView] lockFocus]; // account for frame and translate to currently visible region PStranslate(1-visibleRect.origin.x,1-visibleRect.origin.y); [self drawSelf:&bounds:0]; PStranslate(-1+visibleRect.origin.x,-1+visibleRect.origin.y); NXFrameRect((const NXRect *)&tempRect); [[rulerWindow contentView] unlockFocus]; [rulerWindow orderFront:NULL]; /* compute the offset from the image's origin to the mouse location */ offset.x = theEvent->location.x - NX_X(&windowRect)+2; offset.y = theEvent->location.y - NX_Y(&windowRect)-2; /* convert the mousedown location to screen coordinates */ mouseLocation = theEvent->location; [window convertBaseToScreen:&mouseLocation]; /* go into the dragging loop */ oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK]; while(looping) { theEvent = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]; if(theEvent->type == NX_MOUSEDRAGGED){ if (shiftHeld == YES) { // constrain drag [rulerWindow getFrame:&tempRect]; if (orientation == HORIZONTAL) [rulerWindow moveTo:tempRect.origin.x :theEvent->location.y-offset.y]; else [rulerWindow moveTo:theEvent->location.x-offset.x :tempRect.origin.y]; } else [rulerWindow moveTo:theEvent->location.x-offset.x :theEvent->location.y-offset.y]; } else { /* NX_MOUSEUP */ [rulerWindow free]; looping = NO; } } return self; } /* Sizing the rulers */ - setSize // Sets the size of the view. Invoked by the setSize method of // RuledScrollView, which must be invoked whenever the size of // the mainView is changed. // In addition to sizing the ruler, this method also sets the instance // variables leftOrBottomMargin and rightOrTopMargin, which determine // the location of the origin of the ruler. If the ruler extends over // the end of an adjacent ruler, the corresponding margin is set to // the width or height of that ruler. (In order to extend over the // end of another ruler, the ruler must be a 'primary' ruler and // there must be no corresponding stubView.) The intention of the // margin variables is to locate the origin of the ruler at the // edge of the view that it 'rules', automatically accounting // for whatever stub views exist and which rulers are primary. // // For example, sizing a Right Ruler is done as follows: // 1) if Vertical Rulers are primary, and there is a bottom ruler // and there isn't a bottom right stubView, set // leftOrBottomMargin to the height of the bottom ruler // 2) if Vertical Rulers are primary, and there is a top ruler // and there isn't a top right stubView, set // rightOrTopMargin to the height of the top ruler // 4) the height is set to the height of the main View + // leftOrBottomMargin + rightOrTopMargin. The width is // unchanged. { NXRect theRect, mainViewFrame, rulerFrame; id leftOrBottomRuler, rightOrTopRuler, ruledScrollView, stub; /* Ruler's super is a clipview; it's super is RuledScrollView, * it's docView is the main View */ ruledScrollView = [superview superview]; if (![ruledScrollView isKindOf:[RuledScrollView class]]) // this ruler is not currently in a RuledScrollView // (probably because ruler is hidden) return self; [[ruledScrollView docView] getFrame:&mainViewFrame]; [self getFrame:(NXRect *)&theRect]; leftOrBottomMargin = 0.0; rightOrTopMargin = 0.0; if (orientation == HORIZONTAL) { leftOrBottomRuler = [ruledScrollView leftRuler]; if ([ruledScrollView bottomRuler] == self) stub = [ruledScrollView bottomLeftStub]; else stub = [ruledScrollView topLeftStub]; if (leftOrBottomRuler && (!stub) && ([ruledScrollView primaryRulers] == TOPBOTTOM)) { [leftOrBottomRuler getFrame:&rulerFrame]; leftOrBottomMargin = rulerFrame.size.width; } rightOrTopRuler = [ruledScrollView rightRuler]; if ([ruledScrollView bottomRuler] == self) stub = [ruledScrollView bottomRightStub]; else stub = [ruledScrollView topRightStub]; if (rightOrTopRuler && (!stub) && ([ruledScrollView primaryRulers] == TOPBOTTOM)) { [rightOrTopRuler getFrame:&rulerFrame]; rightOrTopMargin = rulerFrame.size.width; } theRect.size.width = mainViewFrame.size.width + leftOrBottomMargin + rightOrTopMargin; } else { leftOrBottomRuler = [ruledScrollView bottomRuler]; if ([ruledScrollView leftRuler] == self) stub = [ruledScrollView bottomLeftStub]; else stub = [ruledScrollView bottomRightStub]; if ((leftOrBottomRuler) && (!stub) && ([ruledScrollView primaryRulers] == LEFTRIGHT)) { [leftOrBottomRuler getFrame:&rulerFrame]; leftOrBottomMargin = rulerFrame.size.height; } rightOrTopRuler = [ruledScrollView topRuler]; if ([ruledScrollView leftRuler] == self) stub = [ruledScrollView topLeftStub]; else stub = [ruledScrollView topRightStub]; if ((rightOrTopRuler) && (!stub) && ([ruledScrollView primaryRulers] == LEFTRIGHT)) { [rightOrTopRuler getFrame:&rulerFrame]; rightOrTopMargin = rulerFrame.size.height; } theRect.size.height = mainViewFrame.size.height + leftOrBottomMargin + rightOrTopMargin; } [self setFrame:(NXRect *)&theRect]; return self; } /* Setting/returning Ruler attributes */ - setScaleSpacing:(NXCoord)points { scaleSpacing = points; return self; } - (NXCoord)scaleSpacing { return scaleSpacing; } - setScale:(float)scaleIncrement // the increment of the scale reading between major marks { scale = scaleIncrement; return self; } - (float)scale { return scale; } - setScaleOffset:(float)origin { scaleOffset = origin; return self; } - (float)scaleOffset { return scaleOffset; } - setBorder:(NXCoord)theBorder { border = theBorder; return self; } - (NXCoord)border { return border; } - setOrientation:(float)angle // set to the manifest HORIZONTAL or VERTICAL // must be set before sending setSize: { orientation = angle; return self; } - (float)orientation { return orientation; } - setInverted:(BOOL)invertFlag // a flag that may be checked in drawself: to draw a // ruler reflected vertically top or right side { inverted = invertFlag; return self; } - (BOOL)inverted { return inverted; } - setMirrored:(BOOL)mirrorFlag // a flag that may be checked in drawself: to draw a // the ruler reflected horizontally { mirrored = mirrorFlag; return self; } - (BOOL)mirrored { return mirrored; } - setAlignment:(int)alignType // set to one of NX_LEFTALIGNED, NX_CENTERED, NX_RIGHTALIGNED // manifests defined in appkit/Text.h { alignment = alignType; return self; } - (int)alignment { return alignment; } - setUnits:(char *)name { if (name == NULL) return self; if (strlen(units) < strlen(name)) { free(units); units = malloc(strlen(name)*sizeof(char)); } strcpy(units,name); return self; } - (const char *)units {return (const char *)units;} - setFont:aFont { font = aFont; NXTextFontInfo(font, &ascender, &descender, &lineHeight); if (descender < 0.0) descender = -1.0 * descender; return self; } - (Font *)font { return font; } - setBackgroundColor:(NXColor)bColor { backgroundColor = bColor; return self; } - (NXColor)backgroundColor {return backgroundColor;} - setBackgroundGray:(float)value; { backgroundGray = value; return self; } - (float)backgroundGray{return backgroundGray;} - setImage:anImage { /* an image object for rulers that tile an image to the background */ image = anImage; return self; } - image { return image; } - setDraggableRuler:(BOOL)yesOrNo; { draggableRuler = yesOrNo; return self; } - (BOOL)draggableRuler {return draggableRuler; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.