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.