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