This is RulerView.m in view mode; [Download] [Up]
// // Time-stamp: <95/12/08 22:21:48 stephan> // // RulerView.m // Project: Rulers // // Stephan Wacker // 93-02-01 #import "RulerView.h" #import "RulerControl.h" #import "RulerPreferences.h" // We only need the constants, not the methods #import "Rubber.h" // psw stuff #define RUBBERBAND_WINDOW_WIDTH 1 #define CONTINUOUS_UPDATE 1 // YES/NO doesn't work in #ifdef // Tick mark systems. TickMark TMpt[] = { // 1 point is one pixel on the screen 100.0, 1.0, 100, // 100 pt 50.0, 0.7, 0, // 50 pt 10.0, 0.3, 0, // 10 pt 5.0, 0.1, 0, // 5 pt 0.0, 0.0, 0 }; TickMark TMpc[] = { // 1 pica = 12pt 48.0, 1.0, 4, // 4 pc 24.0, 0.6, 0, // 2 pc 12.0, 0.3, 0, // 1 pc 4.0, 0.1, 0, // 4 pt 0.0, 0.0, 0 }; TickMark TMin[] = { // 1 inch = 72pt 72.0, 1.0, 1, // 1 in 72.0/2.0, 0.6, -2, // 1/2 in 72.0/4.0, 0.3, 0, // 1/4 in 72.0/8.0, 0.1, 0, // 1/8 in 0.0, 0.0, 0 }; TickMark TMin_true[] = { // xx dpi 1.0, 1.0, 1, // 1 in 1.0/2.0, 0.6, -2, // 1/2 in 1.0/4.0, 0.3, 0, // 1/4 in 1.0/8.0, 0.1, 0, // 1/8 in 0.0, 0.0, 0 }; TickMark TMin_10[] = { // 1/10 inch marks 72.0, 1.0, 1, // 1 in 72.0*0.5, 0.7, 0, // 0.5 in 72.0*0.1, 0.3, 0, // 0.1 in 0.0, 0.0, 0 }; TickMark TMin_true_10[] = { // xx dpi 75.0, 1.0, 1, // 1 in 75.0*0.5, 0.7, 0, // 0.5 in 75.0*0.1, 0.3, 0, // 0.1 in 0.0, 0.0, 0 }; #define ONE_CM (72.0 / 2.54) TickMark TMcm[] = { // 2.54 centimeter = 1 inch ONE_CM, 1.0, 1, // 1 cm ONE_CM*0.5, 0.7, 0, // 0.5 cm ONE_CM*0.1, 0.3, 0, // 0.1 cm 0.0, 0.0, 0 }; TickMark TMcm_true[] = { // xx dpi 1.0, 1.0, 1, // 1 cm 1.0*0.5, 0.7, 0, // 0.5 cm 1.0*0.1, 0.3, 0, // 0.1 cm 0.0, 0.0, 0 }; @implementation RulerView - initFrame: (const NXRect *) frameRect // Initialize yourself after instance has been created. { const char *pbTypes[] = { NXColorPboardType }; [super initFrame: frameRect]; origin.x = 0.0, origin.y = 0.0; // left bottom corner [self synchScreenOrigin]; markFont = [Font newFont: "Helvetica-Bold" size: 14 matrix: NX_IDENTITYMATRIX ]; [self registerForDraggedTypes: pbTypes count: 1]; cachedImage = nil; // will be created by first -verifyValidRect: imageOrigin.x = imageOrigin.y = 0; validRect.origin = imageOrigin; validRect.size.width = validRect.size.height = 0; return self; } - drawMarks: (const NXRect *) aRect { return [self subclassResponsibility: _cmd]; } - drawBackground: (const NXRect *) aRect { NXRect rect = *aRect; float h, s, b; float db; NXSetColor( [control color] ); NXRectFill( aRect ); NXConvertColorToHSB( [control color], &h, &s, &b ); db = MIN( b/3, (1-b)/3 ); if( Q_IsHorizontalRulerView(self) ) { rect.origin.y += 0.8 * rect.size.height; rect.size.height -= 0.8 * rect.size.height; } else { rect.size.width -= 0.8 * rect.size.width; } NXSetColor( NXConvertHSBToColor( h, s, b+db ) ); NXRectFill( &rect ); if( Q_IsHorizontalRulerView(self) ) { rect.origin.y = aRect->origin.y; } else { rect.origin.x += 4 * rect.size.width; } NXSetColor( NXConvertHSBToColor( h, s, b-db ) ); NXRectFill( &rect ); return self; } - verifyValidRect: (const NXRect *) aRect // Make sure that aRect is correctly rendered in the cachedImage. // aRect is given in Ruler coordinates. { NXRect rect = *aRect; NXSize size = { 0, 0 }; NXSize newSize; NXSize offset = { 0, 0 }; NXImage *newImage; NXPoint point; // Get the image size. [cachedImage getSize: &size]; newSize = size; // Convert aRect to image coordinates. rect.origin.x += imageOrigin.x; rect.origin.y += imageOrigin.y; // Find the required size for an image containing aRect. if( NX_MAXX(&rect) > newSize.width ) { newSize.width += (NX_MAXX(&rect) - newSize.width); } if( NX_X(&rect) < 0 ) { newSize.width += offset.width = -NX_X(&rect); } if( NX_MAXY(&rect) > newSize.height ) { newSize.height += (NX_MAXY(&rect) - newSize.height); } if( NX_Y(&rect) < 0 ) { newSize.height += offset.height = -NX_Y(&rect); } // Check if we have to resize the image. if( newSize.width > size.width || newSize.height > size.height ) { // Add some spare room. if( Q_IsHorizontalRulerView(self) ) { newSize.width += 100; if( offset.width > 0 ) { offset.width += 100; rect.origin.x += offset.width; imageOrigin.x += offset.width; } } else { newSize.height += 100; if( offset.height > 0 ) { offset.height += 100; rect.origin.y += offset.height; imageOrigin.y += offset.height; } } // Create a new image. newImage = [[NXImage alloc] initSize: &newSize]; [newImage useCacheWithDepth: NX_TwelveBitRGBDepth]; size = newSize; // Paste the currently valid region into the new image. [newImage lockFocus]; point.x = NX_X(&validRect) + offset.width; point.y = NX_Y(&validRect) + offset.height; [cachedImage composite: NX_COPY fromRect: &validRect toPoint: &point]; validRect.origin = point; [newImage unlockFocus]; // Replace the cachedImage. [cachedImage free]; cachedImage = newImage; } // Find the parts of aRect that are not contained in validRect. if( NX_X(&rect) < NX_X(&validRect) ) { rect.size.width = NX_X(&validRect) - NX_X(&rect); if( NX_X(&rect) - 0 > 20 ) { rect.size.width += 20; rect.origin.x -= 20; } else { rect.size.width += (NX_X(&rect) - 0); rect.origin.x = 0; } validRect.size.width += NX_WIDTH(&rect); validRect.origin.x = NX_X(&rect); } else if( NX_MAXX(&rect) > NX_MAXX(&validRect) ) { rect.size.width = NX_MAXX(&rect) - NX_MAXX(&validRect); rect.origin.x = NX_MAXX(&validRect); if( size.width - NX_MAXX(&rect) > 20 ) { rect.size.width += 20; } else { rect.size.width += (size.width - NX_MAXX(&rect)); } validRect.size.width += NX_WIDTH(&rect); } else if( NX_Y(&rect) < NX_Y(&validRect) ) { rect.size.height = NX_Y(&validRect) - NX_Y(&rect); if( NX_Y(&rect) - 0 > 20 ) { rect.size.height += 20; rect.origin.y -= 20; } else { rect.size.height += (NX_Y(&rect) - 0); rect.origin.y = 0; } validRect.size.height += NX_HEIGHT(&rect); validRect.origin.y = NX_Y(&rect); } else if( NX_MAXY(&rect) > NX_MAXY(&validRect) ) { rect.size.height = NX_MAXY(&rect) - NX_MAXY(&validRect); rect.origin.y = NX_MAXY(&validRect); if( size.height - NX_MAXY(&rect) > 20 ) { rect.size.height += 20; } else { rect.size.height += (size.height - NX_MAXY(&rect)); } validRect.size.height += NX_HEIGHT(&rect); } else { return self; } // Draw inside rect. [cachedImage lockFocus]; [self drawBackground: &rect]; [self drawMarks: &rect]; [cachedImage unlockFocus]; return self; } #if 0 # - drawSelf: (const NXRect *) rects: (int) rectCount // Draw the ruler image's background. // Should be refined by subclasses. { NXSetColor( [control color] ); switch( rectCount ) { case 1: NXRectFill( rects ); // one exposed region break; case 3: NXRectFill( rects+1 ); // two exposed regions NXRectFill( rects+2 ); break; default: NXRectFill( &bounds ); // complete view break; } return self; } #else - drawSelf: (const NXRect *) rects: (int) rectCount // Draw the ruler. { NXRect rect = rects[0]; // Convert to Ruler coordinates. rect.origin.x -= origin.x; rect.origin.y -= origin.y; // Render the region in cachedImage. [self verifyValidRect: &rect]; // Convert to Image coordinates. rect.origin.x += imageOrigin.x; rect.origin.y += imageOrigin.y; // Copy the region from cachedImage to the View. [cachedImage composite: NX_COPY fromRect: &rect toPoint: &rects[0].origin]; return self; } #endif - (BOOL) acceptsFirstMouse // Accept mouse even if not key window. { return YES; } - mouseDown: (NXEvent *) event // Drag the local origin with the mouse cursor. // With Cmd-Key: Set Origin to mouse location first. { if( event->flags & NX_COMMANDMASK ) { NXPoint loc = event->location; // window coordinates [self convertPoint: &loc fromView: nil]; // view coordinates [self setOrigin: &loc]; } [self trackMouseFor: self action: @selector( moveOrigin: )]; [window display]; return self; } #if 0 - rightMouseDown: (NXEvent *) event // Do nothing and wait for rightMouseUp. { #if CONTINUOUS_UPDATE NXPoint loc = event->location; // window coordinates [self convertPoint: &loc fromView: nil]; // view coordinates [self setOrigin: &loc]; [self trackMouseFor: self action: @selector( moveOrigin: )]; [window display]; #endif return self; } #endif #if 0 - rightMouseUp: (NXEvent *) event // Set the local origin to the mouse cursor. { NXPoint loc = event->location; // window coordinates [self convertPoint: &loc fromView: nil]; // view coordinates [self setOrigin: &loc]; [window display]; return self; } #endif - getOrigin: (NXPoint *) point // Get coordinate system's origin (relative to the view). { *point = origin; return self; return self; } - setOrigin: (const NXPoint *) point // Set local coordinate system's origin. // Caller should send a display message to the view or its window. { origin = *point; if( Q_IsHorizontalRulerView(self) ) { origin.y = 0; } else { origin.x = 0; } [self synchScreenOrigin]; #if CONTINUOUS_UPDATE [self update]; #else [self setNeedsDisplay: YES]; #endif return self; } - moveOrigin: (const NXSize *) delta // Move local coordinate system's origin. // Caller should send a display message to the view or its window. { if( Q_IsHorizontalRulerView( self ) ) { origin.x += delta->width; } else { origin.y +=delta->height; } [self synchScreenOrigin]; #if CONTINUOUS_UPDATE [self update]; #else [self setNeedsDisplay: YES]; #endif return self; } - synchOrigin // Take origin from screenOrigin. { origin = screenOrigin; [window convertScreenToBase: &origin]; if( Q_IsHorizontalRulerView(self) ) { origin.y = 0; } else { origin.x = 0; } [self setNeedsDisplay: YES]; return self; } - synchScreenOrigin // Take screenOrigin from origin. { screenOrigin = origin; [window convertBaseToScreen: &screenOrigin]; return self; } - setCacheInvalid // Force the cachedImage to be redrawn. { // Put the validRect origin at the bottom left corner of // the currently visible view. validRect.origin.x = imageOrigin.x - origin.x; validRect.origin.y = imageOrigin.y - origin.y; if( Q_IsHorizontalRulerView(self) ) { validRect.size.width = 0; } else { validRect.size.height = 0; } [self setNeedsDisplay: YES]; return self; } - (const TickMark *) tickMarkSystem // Answer current tick mark system, as selected by the RulerControl. { TickMark *result; switch( [control unitsMode] ) { case UNIT_PT: return TMpt; break; case UNIT_PC: default: return TMpc; break; case UNIT_IN: switch( [control inchMode] ) { case INCH_DIV_8: default: return TMin; case INCH_DIV_10: return TMin_10; } break; case UNIT_CM: return TMcm; break; case UNIT_TRUE_IN: switch( [control inchMode] ) { case INCH_DIV_8: default: result = TMin_true; result[0].distance = [control screenDpi]; result[1].distance = result[0].distance / 2.0; result[2].distance = result[0].distance / 4.0; result[3].distance = result[0].distance / 8.0; return result; case INCH_DIV_10: result = TMin_true_10; result[0].distance = [control screenDpi]; result[1].distance = result[0].distance * 0.5; result[2].distance = result[0].distance * 0.1; return result; } break; case UNIT_TRUE_CM: result = TMcm_true; result[0].distance = [control screenDpi] / 2.54; result[1].distance = result[0].distance * 0.5; result[2].distance = result[0].distance * 0.1; return result; break; } return NULL; } - trackMouseFor: target action: (SEL) action // // Track the mouse while it drags some control object. // Initiated by a mouseDown: event; tracks mouse until mouseUp:. // // The action is repeatedly sent to the target with // the partial moving distance as its argument. // The method definition should be // action: (NXSize *) delta // { NXEvent *event = [NXApp currentEvent]; // MouseDown NXPoint oldLoc = event->location; // window coordinates NXPoint newLoc; NXSize delta; int oldMask = [window eventMask]; int trackMask; int stopEvent; // Reset local variable for resizing correction. off = 0; switch( event->type ) { default: case NX_LMOUSEDOWN: trackMask = NX_LMOUSEUPMASK | NX_LMOUSEDRAGGEDMASK; stopEvent = NX_LMOUSEUP; break; #if 0 case NX_RMOUSEDOWN: trackMask = NX_RMOUSEUPMASK | NX_RMOUSEDRAGGEDMASK; stopEvent = NX_RMOUSEUP; break; #endif } [window setEventMask: (oldMask | trackMask)]; [window convertBaseToScreen: &oldLoc]; // screen coordinates while( 1 ) { newLoc = event->location; // window coordinates [window convertBaseToScreen: &newLoc]; // screen coordinates #if 0 This was an ugly mistake: If the cursor was moved in both x and y direction, the first test signaled that delta.width was not zero and the second test -- including the assignment to delta.height -- was skipped. if( (delta.width = newLoc.x - oldLoc.x) != 0.0 || (delta.height = newLoc.y - oldLoc.y) != 0.0 ) { [target perform: action with: (id) &delta]; } #else delta.width = newLoc.x - oldLoc.x; delta.height = newLoc.y - oldLoc.y; if( delta.width || delta.height ) { [target perform: action with: (id) &delta]; } #endif if( event->type == stopEvent ) break; // from while() oldLoc = newLoc; event = [NXApp getNextEvent: trackMask]; } [window setEventMask: oldMask]; // restore old settings return self; } - showRubberband { PSWInitRubberband(); [window getFrame: &rubberRect]; [self drawRubberband]; PSWMakeVisible(TRUE); return self; } - hideRubberband { PSWMakeVisible(FALSE); [window placeWindowAndDisplay: &rubberRect]; return self; } // The code for this method is copied from // /BGCD/BGCDDeveloper/Sources/Classes/Rubberview/Rubberband.m // by Slugg Jello, // Mouthing Flowers // 152 20th Ave. #1 // Seattle, WA. 98112 // slugg@mouthers.nwnexus.wa.com // - drawRubberband { NXRect tRect, screenRect = rubberRect; struct { NXRect top, left, bottom, right; } points; /**** set size of each rubberband side ****/ /* left */ tRect = screenRect; tRect.size.width = RUBBERBAND_WINDOW_WIDTH; tRect.size.height = MAX(RUBBERBAND_WINDOW_WIDTH,screenRect.size.height); points.left = tRect; /* right */ tRect.origin.x += screenRect.size.width - 1.0; points.right = tRect; /* bottom */ tRect = screenRect; tRect.origin.x += 1.0; tRect.size.width = MAX(RUBBERBAND_WINDOW_WIDTH,screenRect.size.width-1.0); tRect.size.height = RUBBERBAND_WINDOW_WIDTH; points.bottom = tRect; /* top */ tRect.origin.y += screenRect.size.height - 1.0; points.top = tRect; /* this'll erase old one and draw new one in new position */ PSWPositionRect( (float *)&points ); return self; } @end // RulerView @implementation RulerView ( DraggingDestination ) - (NXDragOperation) draggingEntered: sender // Invoked when the dragged image enters the destination. // Answer what we will do when the object is released. { return ([sender draggingSourceOperationMask] & NX_DragOperationGeneric); } - (BOOL) prepareForDragOperation: sender // Invoked when the image is released. // Answer whether we will perform the drag operation. { return YES; } - (BOOL) performDragOperation: sender // Invoked after the released image has been removed from the screen. // Answer whether the data has been accepted. { Pasteboard *pb = [Pasteboard newName: NXDragPboard]; [control setColor: NXReadColorFromPasteboard(pb)]; return YES; } @end //RulerView ( DraggingDestination )
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.