1// 2// PXCanvasResizeView.m 3// Pixen-XCode 4// 5// Created by Ian Henderson on Wed Jun 09 2004. 6// Copyright (c) 2004 Open Sword Group. All rights reserved. 7// 8 9#import "PXCanvasResizeView.h" 10#import <math.h> 11 12@implementation PXCanvasResizeView 13 14- (id)initWithFrame:(NSRect)frame 15{ 16 self = [super initWithFrame:frame]; 17 if (self) { 18 cachedImage = [[NSImage imageNamed:@"greybox"] retain]; 19 scaleTransform = [[NSAffineTransform alloc] init]; 20 backgroundColor = [[NSColor colorWithCalibratedRed:1 green:1 blue:1 alpha:0] retain]; 21 } 22 return self; 23} 24 25- (void)dealloc 26{ 27 [guideDisappearTimer invalidate]; 28 [guideDisappearTimer release]; 29 [backgroundColor release]; 30 [scaleTransform release]; 31 [cachedImage release]; 32 [super dealloc]; 33} 34 35 36- (NSRect)applyTransformation:(NSAffineTransform *)transform toRect:(NSRect)rect 37{ 38 NSRect newRect; 39 newRect.size = [transform transformSize:rect.size]; 40 newRect.origin = [transform transformPoint:rect.origin]; 41 return newRect; 42} 43 44 45- (void)drawLineAndNumberLengthFromPoint:(NSPoint)from toPoint:(NSPoint)to scale:(float)scale inSize:(NSSize)frameSize; 46{ 47 float temp; 48 if (from.x > to.x) { 49 temp = from.x; 50 from.x = to.x; 51 to.x = temp; 52 } 53 if (from.y > to.y) { 54 temp = from.y; 55 from.y = to.y; 56 to.y = temp; 57 } 58 float distance = sqrt(pow(from.x - to.x, 2) + pow(from.y - to.y, 2)) / scale; 59 [[NSColor grayColor] set]; 60 [NSBezierPath strokeLineFromPoint:from toPoint:to]; 61 62 NSAttributedString *distanceString = [[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d", (int)distance] attributes:[NSDictionary dictionaryWithObjectsAndKeys: 63 [NSColor blackColor], NSForegroundColorAttributeName, 64 [NSFont fontWithName:@"Helvetica" size:10], NSFontAttributeName, 65 nil]] autorelease]; 66 NSRect stringRect = NSZeroRect; 67 stringRect.size = [distanceString size]; 68 stringRect.origin = NSMakePoint((from.x + to.x - stringRect.size.width)/2, (from.y + to.y - stringRect.size.height)/2); // center the string on the center of the line 69 if (from.x < to.x + .1 && from.x > to.x - .1) { 70 [NSBezierPath strokeLineFromPoint:from toPoint:NSMakePoint(from.x + 3, from.y + 3)]; 71 [NSBezierPath strokeLineFromPoint:from toPoint:NSMakePoint(from.x - 3, from.y + 3)]; 72 [NSBezierPath strokeLineFromPoint:to toPoint:NSMakePoint(to.x + 3, to.y - 3)]; 73 [NSBezierPath strokeLineFromPoint:to toPoint:NSMakePoint(to.x - 3, to.y - 3)]; 74 stringRect.origin.x += stringRect.size.width / 2.0; 75 } else { 76 [NSBezierPath strokeLineFromPoint:from toPoint:NSMakePoint(from.x + 3, from.y + 3)]; 77 [NSBezierPath strokeLineFromPoint:from toPoint:NSMakePoint(from.x + 3, from.y - 3)]; 78 [NSBezierPath strokeLineFromPoint:to toPoint:NSMakePoint(to.x - 3, to.y + 3)]; 79 [NSBezierPath strokeLineFromPoint:to toPoint:NSMakePoint(to.x - 3, to.y - 3)]; 80 stringRect.origin.y += stringRect.size.height / 2.0; 81 } 82 83 if (stringRect.origin.x < 0) { // make sure the string is going to be visible 84 stringRect.origin.x = 0; 85 } 86 if (stringRect.origin.y < 0) { 87 stringRect.origin.y = 0; 88 } 89 if (stringRect.origin.x + stringRect.size.width > frameSize.width) { 90 stringRect.origin.x = frameSize.width - stringRect.size.width; 91 } 92 if (stringRect.origin.y + stringRect.size.height > frameSize.height) { 93 stringRect.origin.y = frameSize.height - stringRect.size.height; 94 } 95 96 //float radius = sqrt(pow(stringRect.size.width, 2) + pow(stringRect.size.height, 2)) / 2.0; // find the radius of the circle using a^2 + b^2 = c^2 97 98 //NSBezierPath *path = [NSBezierPath bezierPath]; 99 //[path appendBezierPathWithArcWithCenter:NSMakePoint(stringRect.origin.x + stringRect.size.width/2.0, stringRect.origin.y + stringRect.size.height/2.0) radius:radius startAngle:0 endAngle:360]; // make the circle 100 101 NSRect stringBackgroundRect = stringRect; 102 stringRect.origin.x += 2; 103 stringBackgroundRect.size.width += 4; 104 [[NSColor colorWithCalibratedRed:1 green:1 blue:1 alpha:.9] set]; 105 NSRectFillUsingOperation(stringBackgroundRect, NSCompositeSourceAtop); 106 //[path fill]; // fill the circle 107 [distanceString drawAtPoint:stringRect.origin]; 108} 109 110- (void)drawRect:(NSRect)rect 111{ 112 NSRect newRect = NSMakeRect(0, 0, newSize.width, newSize.height); // Find the new size of the canvas 113 NSRect oldRect = NSMakeRect(position.x, position.y, oldSize.width, oldSize.height); // Find the old size of the canvas 114 NSSize maxSize = NSMakeSize(MAX(newSize.width, oldSize.width), MAX(newSize.height, oldSize.height)); // Find the size we need to display in the view 115 NSSize frameSize = [self frame].size; 116 117 float scale = 1.0f / MAX(maxSize.height / frameSize.height, maxSize.width / frameSize.width); // Find the scaling factor by looking at the rect that contains both the new size and old size, then scaling it to fit our frame 118 119 oldRect.origin.x = round(oldRect.origin.x); 120 oldRect.origin.y = round(oldRect.origin.y); 121 122 [scaleTransform release]; 123 scaleTransform = [[NSAffineTransform transform] retain]; // transform the image-pixel scale to screen-pixel scale 124 [scaleTransform scaleBy:scale]; 125 126 newRect = [self applyTransformation:scaleTransform toRect:newRect]; // transform our rects 127 oldRect = [self applyTransformation:scaleTransform toRect:oldRect]; 128 129 NSAffineTransform *translateTransform = [NSAffineTransform transform]; 130 [translateTransform translateXBy:(frameSize.width - newRect.size.width) / 2 yBy:(frameSize.height - newRect.size.height) / 2]; // center the view on the new frame 131 132 newRect = [self applyTransformation:translateTransform toRect:newRect]; // transform the rects again 133 oldRect = [self applyTransformation:translateTransform toRect:oldRect]; 134 135 [[backgroundColor colorWithAlphaComponent:1] set]; 136 NSRectFill(newRect); // draw background for new frame 137 138 [cachedImage drawInRect:oldRect fromRect:NSMakeRect(0, 0, [cachedImage size].width, [cachedImage size].height) operation:NSCompositeSourceAtop fraction:1.0f]; // draw the image in the old frame 139 [[NSColor blackColor] set]; 140 [NSBezierPath strokeRect:oldRect]; // draw an outline around the image 141 142 NSBezierPath *canvasOutline = [NSBezierPath bezierPathWithRect:newRect]; 143 float dashed[2] = {3, 3}; 144 [canvasOutline setLineDash:dashed count:2 phase:0]; 145 [canvasOutline stroke]; // draw an outline around the canvas 146 [canvasOutline setLineDash:dashed count:2 phase:3]; 147 [[NSColor whiteColor] set]; 148 [canvasOutline stroke]; // dash white and black 149 150 if (drawingArrows) { // if we're dragging the image 151 152 float horizontalOffset = (oldRect.size.width / 2) + oldRect.origin.x; 153 float verticalOffset = (oldRect.size.height / 2) + oldRect.origin.y; 154 BOOL drawLines = YES; 155 156 // a whole bunch of checks to position the lines correctly follow... 157 158 if (horizontalOffset > newRect.size.width + newRect.origin.x) { 159 if (newRect.size.width + newRect.origin.x < oldRect.origin.x) { 160 drawLines = NO; // we don't care about it, it's off the new image entirely 161 } else { 162 horizontalOffset = newRect.size.width + newRect.origin.x; // move line so it doesn't go off the edge 163 } 164 } 165 166 if (horizontalOffset < newRect.origin.x) { // pretty much the same as above 167 if (newRect.origin.x > oldRect.size.width + oldRect.origin.x) { 168 drawLines = NO; 169 } else { 170 horizontalOffset = newRect.origin.x; 171 } 172 } 173 174 if (verticalOffset > newRect.size.height + newRect.origin.y) { 175 if (newRect.size.height + newRect.origin.y < oldRect.origin.y) { 176 drawLines = NO; 177 } else { 178 verticalOffset = newRect.size.height + newRect.origin.y; // move line so it doesn't go off the edge 179 } 180 } 181 182 if (verticalOffset < newRect.origin.y) { // pretty much the same as above 183 if (newRect.origin.y > oldRect.size.height + oldRect.origin.y) { 184 drawLines = NO; 185 } else { 186 verticalOffset = newRect.origin.y; 187 } 188 } 189 190 if (drawLines) { 191 // up 192 [self drawLineAndNumberLengthFromPoint:NSMakePoint(horizontalOffset, oldRect.size.height + oldRect.origin.y) 193 toPoint:NSMakePoint(horizontalOffset, newRect.size.height + newRect.origin.y) 194 scale:scale 195 inSize:frameSize]; 196 197 // down 198 [self drawLineAndNumberLengthFromPoint:NSMakePoint(horizontalOffset, oldRect.origin.y) 199 toPoint:NSMakePoint(horizontalOffset, newRect.origin.y) 200 scale:scale 201 inSize:frameSize]; 202 203 // right 204 [self drawLineAndNumberLengthFromPoint:NSMakePoint(oldRect.size.width + oldRect.origin.x, verticalOffset) 205 toPoint:NSMakePoint(newRect.size.width + newRect.origin.x, verticalOffset) 206 scale:scale 207 inSize:frameSize]; 208 209 // left 210 [self drawLineAndNumberLengthFromPoint:NSMakePoint(oldRect.origin.x, verticalOffset) 211 toPoint:NSMakePoint(newRect.origin.x, verticalOffset) 212 scale:scale 213 inSize:frameSize]; 214 } 215 } 216} 217 218- (NSSize)newSize 219{ 220 return newSize; 221} 222 223- (NSPoint)position 224{ 225 NSPoint roundedPosition = position; 226 roundedPosition.x = round(roundedPosition.x); 227 roundedPosition.y = round(roundedPosition.y); 228 return roundedPosition; 229} 230 231- (void)setNewImageSize:(NSSize)size 232{ 233 newSize = size; 234 [self setNeedsDisplay:YES]; 235} 236 237- (void)setOldImageSize:(NSSize)size 238{ 239 oldSize = size; 240 position = NSMakePoint(0,0); 241 [self setNeedsDisplay:YES]; 242} 243 244- (void)setCachedImage:(NSImage *)image 245{ 246 [cachedImage release]; 247 cachedImage = [[NSImage alloc] initWithSize:[image size]]; 248 249 [cachedImage lockFocus]; 250 [[NSColor colorWithCalibratedRed:1 green:1 blue:1 alpha:1] set]; 251 NSRectFill(NSMakeRect(0,0,[image size].width,[image size].height)); 252 [image compositeToPoint:NSMakePoint(0, 0) operation:NSCompositeSourceAtop]; 253 [cachedImage unlockFocus]; 254 255 [self setNeedsDisplay:YES]; 256} 257 258 259- (void)setBackgroundColor:(NSColor *)color 260{ 261 [color retain]; 262 [backgroundColor release]; 263 backgroundColor = color; 264 [self setNeedsDisplay:YES]; 265} 266 267- (void)mouseDown:(NSEvent *)event 268{ 269 drawingArrows = YES; 270} 271 272- (void)mouseUp:(NSEvent *)event 273{ 274 drawingArrows = NO; 275 [self setNeedsDisplay:YES]; 276} 277 278- (void)mouseDragged:(NSEvent *)event 279{ 280 NSAffineTransform *muffineTransform = [scaleTransform copy]; 281 [muffineTransform invert]; 282 NSPoint deltaVector = [muffineTransform transformPoint:NSMakePoint([event deltaX], [event deltaY])]; 283 284 position.x += deltaVector.x; 285 position.y -= deltaVector.y; 286 [self setNeedsDisplay:YES]; 287} 288 289- (void)hideArrows:(NSTimer *)timer; 290{ 291 drawingArrows = NO; 292 [self setNeedsDisplay:YES]; 293 [guideDisappearTimer release]; 294 guideDisappearTimer = nil; 295} 296 297- (void)keyDown:(NSEvent *)event 298{ 299 NSString *characters = [event charactersIgnoringModifiers]; 300 NSPoint deltaVector = NSMakePoint(0, 0); 301 if ([characters characterAtIndex:0] == NSUpArrowFunctionKey) { 302 deltaVector.y = 1; 303 } else if ([characters characterAtIndex:0] == NSDownArrowFunctionKey) { 304 deltaVector.y = -1; 305 } else if ([characters characterAtIndex:0] == NSRightArrowFunctionKey) { 306 deltaVector.x = 1; 307 } else if ([characters characterAtIndex:0] == NSLeftArrowFunctionKey) { 308 deltaVector.x = -1; 309 } else { 310 [super keyDown:event]; 311 return; 312 } 313 314 if ([event modifierFlags] & NSShiftKeyMask) { 315 deltaVector.x *= 10; 316 deltaVector.y *= 10; 317 } 318 319 position.x += deltaVector.x; 320 position.y += deltaVector.y; 321 322 drawingArrows = YES; 323 [guideDisappearTimer invalidate]; 324 [guideDisappearTimer release]; 325 guideDisappearTimer = [[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(hideArrows:) userInfo:nil repeats:NO] retain]; 326 [self setNeedsDisplay:YES]; 327} 328 329- (BOOL)acceptsFirstResponder 330{ 331 return YES; 332} 333 334@end 335