/* GormViewEditor.m * * Copyright (C) 2002 Free Software Foundation, Inc. * * Author: Pierre-Yves Rivaille * Date: 2002 * * This file is part of GNUstep. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include "GormPrivate.h" #include "GormViewEditor.h" #include "GormViewWithSubviewsEditor.h" #include "GormPlacementInfo.h" #include #include @implementation GormPlacementInfo @end @implementation GormPlacementHint - (float) position { return _position; } - (float) start { return _start; } - (float) end { return _end; } - (NSRect) frame { return _frame; } - (GormHintBorder) border { return _border; } - (NSString *) description { switch (_border) { case Left: return [NSString stringWithFormat: @"Left %f (%f-%f)", _position, _start, _end]; case Right: return [NSString stringWithFormat: @"Right %f (%f-%f)", _position, _start, _end]; case Top: return [NSString stringWithFormat: @"Top %f (%f-%f)", _position, _start, _end]; default: return [NSString stringWithFormat: @"Bottom %f (%f-%f)", _position, _start, _end]; } } - (id) initWithBorder: (GormHintBorder) border position: (float) position validityStart: (float) start validityEnd: (float) end frame: (NSRect) frame { _border = border; _start = start; _end = end; _position = position; _frame = frame; return self; } - (NSRect) rectWithHalfDistance: (int) halfHeight { switch (_border) { case Top: case Bottom: return NSMakeRect(_start, _position - halfHeight, _end - _start, 2 * halfHeight); case Left: case Right: return NSMakeRect(_position - halfHeight, _start, 2 * halfHeight, _end - _start); default: return NSZeroRect; } } - (int) distanceToFrame: (NSRect) frame { NSRect rect = [self rectWithHalfDistance: 6]; if (NSIntersectsRect(frame, rect) == NO) return 10; switch (_border) { case Top: return abs (_position - NSMaxY(frame)); case Bottom: return abs (_position - NSMinY(frame)); case Left: return abs (_position - NSMinX(frame)); case Right: return abs (_position - NSMaxX(frame)); default: return 10; } } @end // to allow us to load the image by name, but save it within the archive. // this is a bit of a cheat. @interface NSImage (GormAddition) - (void) setArchiveByName: (BOOL) archiveByName; @end @implementation NSImage (GormAddition) - (void) setArchiveByName: (BOOL) archiveByName { _flags.archiveByName = archiveByName; } @end static BOOL currently_displaying = NO; @implementation GormViewEditor - (void) encodeWithCoder: (NSCoder*)aCoder { [NSException raise: NSInternalInconsistencyException format: @"Argh - encoding view editor"]; } - (id) document { return document; } - (id) editedObject { return _editedObject; } - (BOOL) activate { if (activated == NO) { NSView *superview = [_editedObject superview]; [self setFrame: [_editedObject frame]]; [self setBounds: [self frame]]; [superview replaceSubview: _editedObject with: self]; [self setAutoresizingMask: NSViewMaxXMargin | NSViewMinYMargin]; [self setAutoresizesSubviews: NO]; [self addSubview: _editedObject]; [_editedObject setPostsFrameChangedNotifications: YES]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(editedObjectFrameDidChange:) name: NSViewFrameDidChangeNotification object: _editedObject]; [self setPostsFrameChangedNotifications: YES]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(frameDidChange:) name: NSViewFrameDidChangeNotification object: self]; parent = [document parentEditorForEditor: self]; if ([parent isKindOfClass: [GormViewEditor class]]) [parent setNeedsDisplay: YES]; else [self setNeedsDisplay: YES]; activated = YES; return YES; } return NO; } - (id) parent { return parent; } - (void) _subviewsForView: (NSView *)view withArray: (NSMutableArray *)array { if(view != nil) { NSArray *subviews = [view subviews]; NSEnumerator *en = [subviews objectEnumerator]; NSView *aView = nil; // if it's not me and it's not and editor, include it in the list of // things to be deleted from the document. if(view != self && ![view isKindOfClass: [GormViewEditor class]] && view != _editedObject) { [array addObject: view]; } while((aView = [en nextObject]) != nil) { [self _subviewsForView: aView withArray: array]; } } } - (NSArray *) _allsubviews { NSMutableArray *views = [NSMutableArray array]; [self _subviewsForView: self withArray: views]; return views; } - (void) detachSubviews { NSArray *subviews = [self _allsubviews]; [document detachObjects: subviews]; } - (void) close { if (closed == NO) { [self deactivate]; [document editor: self didCloseForObject: _editedObject]; closed = YES; } else { NSDebugLog(@"%@ close but already closed", self); } } - (void) deactivate { if (activated == YES) { NSView *superview = [self superview]; [self removeSubview: _editedObject]; [superview replaceSubview: self with: _editedObject]; [[NSNotificationCenter defaultCenter] removeObserver: self]; activated = NO; } } - (void) dealloc { if (closed == NO) [self close]; // RELEASE(_editedObject); [super dealloc]; } - (id) initWithObject: (id)anObject inDocument: (id)aDocument { NSMutableArray *draggedTypes; _editedObject = (NSView*)anObject; if ((self = [super initWithFrame: [_editedObject frame]]) == nil) return nil; RETAIN(_editedObject); document = aDocument; draggedTypes = [NSMutableArray arrayWithObject: GormLinkPboardType]; if ([_editedObject respondsToSelector: @selector(setImage:)]) { [draggedTypes addObject: GormImagePboardType]; } if ([_editedObject respondsToSelector: @selector(setSound:)]) { [draggedTypes addObject: GormSoundPboardType]; } [self registerForDraggedTypes: draggedTypes]; activated = NO; closed = NO; return self; } - (void) editedObjectFrameDidChange: (id) sender { [self setFrame: [_editedObject frame]]; [self setBounds: [_editedObject frame]]; } - (void) frameDidChange: (id) sender { [self setBounds: [self frame]]; [_editedObject setFrame: [self frame]]; } - (GormPlacementInfo *) initializeResizingInFrame: (NSView *)view withKnob: (IBKnobPosition) knob { GormPlacementInfo *gip; gip = [[GormPlacementInfo alloc] init]; gip->resizingIn = view; gip->firstPass = YES; gip->hintInitialized = NO; gip->leftHints = nil; gip->rightHints = nil; gip->topHints = nil; gip->bottomHints = nil; gip->knob = knob; return gip; } - (void) _displayFrame: (NSRect) frame withPlacementInfo: (GormPlacementInfo*) gpi { if (gpi->firstPass == NO) [gpi->resizingIn displayRect: gpi->oldRect]; else gpi->firstPass = NO; GormShowFrameWithKnob(frame, gpi->knob); gpi->oldRect = GormExtBoundsForRect(frame); gpi->oldRect.origin.x--; gpi->oldRect.origin.y--; gpi->oldRect.size.width += 2; gpi->oldRect.size.height += 2; } - (void) _initializeHintWithInfo: (GormPlacementInfo*) gpi { int i; NSArray *subviews = [[self superview] subviews]; int count = [subviews count]; NSView *v; // NSLog(@"initializing hints"); gpi->lastLeftRect = NSZeroRect; gpi->lastRightRect = NSZeroRect; gpi->lastTopRect = NSZeroRect; gpi->lastBottomRect = NSZeroRect; gpi->hintInitialized = YES; gpi->leftHints = [[NSMutableArray alloc] initWithCapacity: 2 * count]; gpi->rightHints = [[NSMutableArray alloc] initWithCapacity: 2 * count]; gpi->topHints = [[NSMutableArray alloc] initWithCapacity: 2 * count]; gpi->bottomHints = [[NSMutableArray alloc] initWithCapacity: 2 * count]; [gpi->leftHints addObject: [[GormPlacementHint alloc] initWithBorder: Left position: NSMinX([[self superview] bounds]) validityStart: NSMinY([[self superview] bounds]) validityEnd: NSMaxY([[self superview] bounds]) frame: [[self superview] bounds]]]; [gpi->leftHints addObject: [[GormPlacementHint alloc] initWithBorder: Left position: NSMinX([[self superview] bounds]) + 10 validityStart: NSMinY([[self superview] bounds]) validityEnd: NSMaxY([[self superview] bounds]) frame: [[self superview] bounds]]]; [gpi->rightHints addObject: [[GormPlacementHint alloc] initWithBorder: Right position: NSMaxX([[self superview] bounds]) validityStart: NSMinY([[self superview] bounds]) validityEnd: NSMaxY([[self superview] bounds]) frame: [[self superview] bounds]]]; [gpi->rightHints addObject: [[GormPlacementHint alloc] initWithBorder: Right position: NSMaxX([[self superview] bounds]) - 10 validityStart: NSMinY([[self superview] bounds]) validityEnd: NSMaxY([[self superview] bounds]) frame: [[self superview] bounds]]]; [gpi->topHints addObject: [[GormPlacementHint alloc] initWithBorder: Top position: NSMaxY([[self superview] bounds]) validityStart: NSMinX([[self superview] bounds]) validityEnd: NSMaxX([[self superview] bounds]) frame: [[self superview] bounds]]]; [gpi->topHints addObject: [[GormPlacementHint alloc] initWithBorder: Top position: NSMaxY([[self superview] bounds]) - 10 validityStart: NSMinX([[self superview] bounds]) validityEnd: NSMaxX([[self superview] bounds]) frame: [[self superview] bounds]]]; [gpi->bottomHints addObject: [[GormPlacementHint alloc] initWithBorder: Bottom position: NSMinY([[self superview] bounds]) validityStart: NSMinX([[self superview] bounds]) validityEnd: NSMaxX([[self superview] bounds]) frame: [[self superview] bounds]]]; [gpi->bottomHints addObject: [[GormPlacementHint alloc] initWithBorder: Bottom position: NSMinY([[self superview] bounds]) + 10 validityStart: NSMinX([[self superview] bounds]) validityEnd: NSMaxX([[self superview] bounds]) frame: [[self superview] bounds]]]; for ( i = 0; i < count; i++ ) { v = [subviews objectAtIndex: i]; if (v == self) continue; [gpi->leftHints addObject: [[GormPlacementHint alloc] initWithBorder: Left position: NSMinX([v frame]) validityStart: NSMinY([[self superview] bounds]) validityEnd: NSMaxY([[self superview] bounds]) frame: [v frame]]]; [gpi->leftHints addObject: [[GormPlacementHint alloc] initWithBorder: Left position: NSMaxX([v frame]) validityStart: NSMinY([v frame]) validityEnd: NSMaxY([v frame]) frame: [v frame]]]; [gpi->leftHints addObject: [[GormPlacementHint alloc] initWithBorder: Left position: NSMaxX([v frame]) + 5 validityStart: NSMinY([v frame]) - 10 validityEnd: NSMaxY([v frame]) + 10 frame: [v frame]]]; [gpi->rightHints addObject: [[GormPlacementHint alloc] initWithBorder: Right position: NSMaxX([v frame]) validityStart: NSMinY([[self superview] bounds]) validityEnd: NSMaxY([[self superview] bounds]) frame: [v frame]]]; [gpi->rightHints addObject: [[GormPlacementHint alloc] initWithBorder: Right position: NSMinX([v frame]) validityStart: NSMinY([v frame]) validityEnd: NSMaxY([v frame]) frame: [v frame]]]; [gpi->rightHints addObject: [[GormPlacementHint alloc] initWithBorder: Right position: NSMinX([v frame]) - 5 validityStart: NSMinY([v frame]) - 10 validityEnd: NSMaxY([v frame]) + 10 frame: [v frame]]]; [gpi->topHints addObject: [[GormPlacementHint alloc] initWithBorder: Top position: NSMaxY([v frame]) validityStart: NSMinX([[self superview] bounds]) validityEnd: NSMaxX([[self superview] bounds]) frame: [v frame]]]; [gpi->topHints addObject: [[GormPlacementHint alloc] initWithBorder: Top position: NSMinY([v frame]) validityStart: NSMinX([v frame]) validityEnd: NSMaxX([v frame]) frame: [v frame]]]; [gpi->topHints addObject: [[GormPlacementHint alloc] initWithBorder: Top position: NSMinY([v frame]) - 5 validityStart: NSMinX([v frame]) - 10 validityEnd: NSMaxX([v frame]) + 10 frame: [v frame]]]; [gpi->bottomHints addObject: [[GormPlacementHint alloc] initWithBorder: Bottom position: NSMinY([v frame]) validityStart: NSMinX([[self superview] bounds]) validityEnd: NSMaxX([[self superview] bounds]) frame: [v frame]]]; [gpi->bottomHints addObject: [[GormPlacementHint alloc] initWithBorder: Bottom position: NSMaxY([v frame]) validityStart: NSMinX([v frame]) validityEnd: NSMaxX([v frame]) frame: [v frame]]]; [gpi->bottomHints addObject: [[GormPlacementHint alloc] initWithBorder: Bottom position: NSMaxY([v frame]) + 5 validityStart: NSMinX([v frame]) - 10 validityEnd: NSMaxX([v frame]) + 10 frame: [v frame]]]; } } #undef MIN #undef MAX #define MIN(a,b) (a>b?b:a) #define MAX(a,b) (a>b?a:b) - (void) _displayFrameWithHint: (NSRect) frame withPlacementInfo: (GormPlacementInfo*)gpi { float leftOfFrame = NSMinX(frame); float rightOfFrame = NSMaxX(frame); float topOfFrame = NSMaxY(frame); float bottomOfFrame = NSMinY(frame); int i; int count; int lastDistance; int minimum = 10; BOOL leftEmpty = YES; BOOL rightEmpty = YES; BOOL topEmpty = YES; BOOL bottomEmpty = YES; float bestLeftPosition = 0; float bestRightPosition = 0; float bestTopPosition = 0; float bestBottomPosition = 0; float leftStart = 0; float rightStart = 0; float topStart = 0; float bottomStart = 0; float leftEnd = 0; float rightEnd = 0; float topEnd = 0; float bottomEnd = 0; NSMutableArray *bests; if (gpi->hintInitialized == NO) { [self _initializeHintWithInfo: gpi]; } { if (gpi->firstPass == NO) [gpi->resizingIn displayRect: gpi->oldRect]; else gpi->firstPass = NO; } { [gpi->resizingIn setNeedsDisplayInRect: gpi->lastLeftRect]; [[self window] displayIfNeeded]; gpi->lastLeftRect = NSZeroRect; } { [gpi->resizingIn setNeedsDisplayInRect: gpi->lastRightRect]; [[self window] displayIfNeeded]; gpi->lastRightRect = NSZeroRect; } { [gpi->resizingIn setNeedsDisplayInRect: gpi->lastTopRect]; [[self window] displayIfNeeded]; gpi->lastTopRect = NSZeroRect; } { [gpi->resizingIn setNeedsDisplayInRect: gpi->lastBottomRect]; [[self window] displayIfNeeded]; gpi->lastBottomRect = NSZeroRect; } if (gpi->knob == IBTopLeftKnobPosition || gpi->knob == IBMiddleLeftKnobPosition || gpi->knob == IBBottomLeftKnobPosition) { bests = [NSMutableArray arrayWithCapacity: 4]; minimum = 6; count = [gpi->leftHints count]; for ( i = 0; i < count; i++ ) { lastDistance = [[gpi->leftHints objectAtIndex: i] distanceToFrame: frame]; if (lastDistance < minimum) { bests = [NSMutableArray arrayWithCapacity: 4]; [bests addObject: [gpi->leftHints objectAtIndex: i]]; minimum = lastDistance; bestLeftPosition = [[gpi->leftHints objectAtIndex: i] position]; leftEmpty = NO; } else if ((lastDistance == minimum) && (leftEmpty == NO) && ([[gpi->leftHints objectAtIndex: i] position] == bestLeftPosition)) [bests addObject: [gpi->leftHints objectAtIndex: i]]; } count = [bests count]; if (count >= 1) { leftStart = NSMinY([[bests objectAtIndex: 0] frame]); leftEnd = NSMaxY([[bests objectAtIndex: 0] frame]); for ( i = 1; i < count; i++ ) { leftStart = MIN(NSMinY([[bests objectAtIndex: i] frame]), leftStart); leftEnd = MAX(NSMaxY([[bests objectAtIndex: i] frame]), leftEnd); } leftOfFrame = bestLeftPosition; } } if (gpi->knob == IBTopRightKnobPosition || gpi->knob == IBMiddleRightKnobPosition || gpi->knob == IBBottomRightKnobPosition) { bests = [NSMutableArray arrayWithCapacity: 4]; minimum = 6; count = [gpi->rightHints count]; for ( i = 0; i < count; i++ ) { lastDistance = [[gpi->rightHints objectAtIndex: i] distanceToFrame: frame]; if (lastDistance < minimum) { bests = [NSMutableArray arrayWithCapacity: 4]; [bests addObject: [gpi->rightHints objectAtIndex: i]]; minimum = lastDistance; bestRightPosition = [[gpi->rightHints objectAtIndex: i] position]; rightEmpty = NO; } else if ((lastDistance == minimum) && (rightEmpty == NO) && ([[gpi->rightHints objectAtIndex: i] position] == bestRightPosition)) [bests addObject: [gpi->rightHints objectAtIndex: i]]; } count = [bests count]; if (count >= 1) { rightStart = NSMinY([[bests objectAtIndex: 0] frame]); rightEnd = NSMaxY([[bests objectAtIndex: 0] frame]); for ( i = 1; i < count; i++ ) { rightStart = MIN(NSMinY([[bests objectAtIndex: i] frame]), rightStart); rightEnd = MAX(NSMaxY([[bests objectAtIndex: i] frame]), rightEnd); } rightOfFrame = bestRightPosition; } } if (gpi->knob == IBTopRightKnobPosition || gpi->knob == IBTopLeftKnobPosition || gpi->knob == IBTopMiddleKnobPosition) { bests = [NSMutableArray arrayWithCapacity: 4]; minimum = 6; count = [gpi->topHints count]; for ( i = 0; i < count; i++ ) { lastDistance = [[gpi->topHints objectAtIndex: i] distanceToFrame: frame]; if (lastDistance < minimum) { bests = [NSMutableArray arrayWithCapacity: 4]; [bests addObject: [gpi->topHints objectAtIndex: i]]; minimum = lastDistance; bestTopPosition = [[gpi->topHints objectAtIndex: i] position]; topEmpty = NO; } else if ((lastDistance == minimum) && (topEmpty == NO) && ([[gpi->topHints objectAtIndex: i] position] == bestTopPosition)) [bests addObject: [gpi->topHints objectAtIndex: i]]; } count = [bests count]; if (count >= 1) { topStart = NSMinX([[bests objectAtIndex: 0] frame]); topEnd = NSMaxX([[bests objectAtIndex: 0] frame]); for ( i = 1; i < count; i++ ) { topStart = MIN(NSMinX([[bests objectAtIndex: i] frame]), topStart); topEnd = MAX(NSMaxX([[bests objectAtIndex: i] frame]), topEnd); } topOfFrame = bestTopPosition; } } if (gpi->knob == IBBottomRightKnobPosition || gpi->knob == IBBottomLeftKnobPosition || gpi->knob == IBBottomMiddleKnobPosition) { bests = [NSMutableArray arrayWithCapacity: 4]; minimum = 6; count = [gpi->bottomHints count]; for ( i = 0; i < count; i++ ) { lastDistance = [[gpi->bottomHints objectAtIndex: i] distanceToFrame: frame]; if (lastDistance < minimum) { bests = [NSMutableArray arrayWithCapacity: 4]; [bests addObject: [gpi->bottomHints objectAtIndex: i]]; minimum = lastDistance; bestBottomPosition = [[gpi->bottomHints objectAtIndex: i] position]; bottomEmpty = NO; } else if ((lastDistance == minimum) && (bottomEmpty == NO) && ([[gpi->bottomHints objectAtIndex: i] position] == bestBottomPosition)) [bests addObject: [gpi->bottomHints objectAtIndex: i]]; } count = [bests count]; if (count >= 1) { bottomStart = NSMinX([[bests objectAtIndex: 0] frame]); bottomEnd = NSMaxX([[bests objectAtIndex: 0] frame]); for ( i = 1; i < count; i++ ) { bottomStart = MIN(NSMinX([[bests objectAtIndex: i] frame]), bottomStart); bottomEnd = MAX(NSMaxX([[bests objectAtIndex: i] frame]), bottomEnd); } bottomOfFrame = bestBottomPosition; } } gpi->hintFrame = NSMakeRect (leftOfFrame, bottomOfFrame, rightOfFrame - leftOfFrame, topOfFrame - bottomOfFrame); { [[NSColor redColor] set]; if (!leftEmpty) { leftStart = MIN(NSMinY(gpi->hintFrame), leftStart); leftEnd = MAX(NSMaxY(gpi->hintFrame), leftEnd); gpi->lastLeftRect = NSMakeRect(bestLeftPosition - 1, leftStart, 2, leftEnd - leftStart); NSRectFill(gpi->lastLeftRect); } if (!rightEmpty) { rightStart = MIN(NSMinY(gpi->hintFrame), rightStart); rightEnd = MAX(NSMaxY(gpi->hintFrame), rightEnd); gpi->lastRightRect = NSMakeRect(bestRightPosition - 1, rightStart, 2, rightEnd - rightStart); NSRectFill(gpi->lastRightRect); } if (!topEmpty) { topStart = MIN(NSMinX(gpi->hintFrame), topStart); topEnd = MAX(NSMaxX(gpi->hintFrame), topEnd); gpi->lastTopRect = NSMakeRect(topStart, bestTopPosition - 1, topEnd - topStart, 2); NSRectFill(gpi->lastTopRect); } if (!bottomEmpty) { bottomStart = MIN(NSMinX(gpi->hintFrame), bottomStart); bottomEnd = MAX(NSMaxX(gpi->hintFrame), bottomEnd); gpi->lastBottomRect = NSMakeRect(bottomStart, bestBottomPosition - 1, bottomEnd - bottomStart, 2); NSRectFill(gpi->lastBottomRect); } } GormShowFrameWithKnob(gpi->hintFrame, gpi->knob); gpi->oldRect = GormExtBoundsForRect(gpi->hintFrame); gpi->oldRect.origin.x--; gpi->oldRect.origin.y--; gpi->oldRect.size.width += 2; gpi->oldRect.size.height += 2; } - (void) updateResizingWithFrame: (NSRect) frame andEvent: (NSEvent *)theEvent andPlacementInfo: (GormPlacementInfo*) gpi { if ([theEvent modifierFlags] & NSShiftKeyMask) { [self _displayFrame: frame withPlacementInfo: gpi]; } else [self _displayFrameWithHint: frame withPlacementInfo: gpi]; } - (void) validateFrame: (NSRect) frame withEvent: (NSEvent *) theEvent andPlacementInfo: (GormPlacementInfo*)gpi { if (gpi->leftHints) { RELEASE(gpi->leftHints); RELEASE(gpi->rightHints); [self setFrame: gpi->hintFrame]; } else { [self setFrame: frame]; } } - (NSRect) _displayMovingFrameWithHint: (NSRect) frame andPlacementInfo: (GormPlacementInfo*)gpi { float leftOfFrame = NSMinX(frame); float rightOfFrame = NSMaxX(frame); float topOfFrame = NSMaxY(frame); float bottomOfFrame = NSMinY(frame); float widthOfFrame = frame.size.width; float heightOfFrame = frame.size.height; int i; int count; int lastDistance; int minimum = 10; BOOL leftEmpty = YES; BOOL rightEmpty = YES; BOOL topEmpty = YES; BOOL bottomEmpty = YES; float leftStart = 0; float rightStart = 0; float topStart = 0; float bottomStart = 0; float leftEnd = 0; float rightEnd = 0; float topEnd = 0; float bottomEnd = 0; if (gpi->hintInitialized == NO) { [self _initializeHintWithInfo: gpi]; } { [gpi->resizingIn setNeedsDisplayInRect: gpi->lastLeftRect]; [[self window] displayIfNeeded]; gpi->lastLeftRect = NSZeroRect; } { [gpi->resizingIn setNeedsDisplayInRect: gpi->lastRightRect]; [[self window] displayIfNeeded]; gpi->lastRightRect = NSZeroRect; } { [gpi->resizingIn setNeedsDisplayInRect: gpi->lastTopRect]; [[self window] displayIfNeeded]; gpi->lastTopRect = NSZeroRect; } { [gpi->resizingIn setNeedsDisplayInRect: gpi->lastBottomRect]; [[self window] displayIfNeeded]; gpi->lastBottomRect = NSZeroRect; } { BOOL empty = YES; float bestPosition = 0; NSMutableArray *leftBests; NSMutableArray *rightBests; minimum = 6; count = [gpi->leftHints count]; leftBests = [NSMutableArray arrayWithCapacity: 4]; for ( i = 0; i < count; i++ ) { lastDistance = [[gpi->leftHints objectAtIndex: i] distanceToFrame: frame]; if (lastDistance < minimum) { leftBests = [NSMutableArray arrayWithCapacity: 4]; [leftBests addObject: [gpi->leftHints objectAtIndex: i]]; minimum = lastDistance; bestPosition = [[gpi->leftHints objectAtIndex: i] position]; empty = NO; } else if ((lastDistance == minimum) && (empty == NO) && ([[gpi->leftHints objectAtIndex: i] position] == bestPosition)) [leftBests addObject: [gpi->leftHints objectAtIndex: i]]; } count = [gpi->rightHints count]; rightBests = [NSMutableArray arrayWithCapacity: 4]; for ( i = 0; i < count; i++ ) { lastDistance = [[gpi->rightHints objectAtIndex: i] distanceToFrame: frame]; if (lastDistance < minimum) { rightBests = [NSMutableArray arrayWithCapacity: 4]; leftBests = [NSMutableArray arrayWithCapacity: 4]; [rightBests addObject: [gpi->rightHints objectAtIndex: i]]; minimum = lastDistance; bestPosition = [[gpi->rightHints objectAtIndex: i] position] - widthOfFrame; empty = NO; } else if ((lastDistance == minimum) && (empty == NO) && ([[gpi->rightHints objectAtIndex: i] position] - bestPosition == widthOfFrame)) [rightBests addObject: [gpi->rightHints objectAtIndex: i]]; } count = [leftBests count]; if (count >= 1) { float position; leftEmpty = NO; position = [[leftBests objectAtIndex: 0] position]; leftStart = NSMinY([[leftBests objectAtIndex: 0] frame]); leftEnd = NSMaxY([[leftBests objectAtIndex: 0] frame]); for ( i = 1; i < count; i++ ) { leftStart = MIN(NSMinY([[leftBests objectAtIndex: i] frame]), leftStart); leftEnd = MAX(NSMaxY([[leftBests objectAtIndex: i] frame]), leftEnd); } leftOfFrame = position; rightOfFrame = position + widthOfFrame; } count = [rightBests count]; if (count >= 1) { float position; rightEmpty = NO; position = [[rightBests objectAtIndex: 0] position]; rightStart = NSMinY([[rightBests objectAtIndex: 0] frame]); rightEnd = NSMaxY([[rightBests objectAtIndex: 0] frame]); for ( i = 1; i < count; i++ ) { rightStart = MIN(NSMinY([[rightBests objectAtIndex: i] frame]), rightStart); rightEnd = MAX(NSMaxY([[rightBests objectAtIndex: i] frame]), rightEnd); } rightOfFrame = position; leftOfFrame = position - widthOfFrame; } } { BOOL empty = YES; float bestPosition = 0; NSMutableArray *bottomBests; NSMutableArray *topBests; minimum = 6; count = [gpi->bottomHints count]; bottomBests = [NSMutableArray arrayWithCapacity: 4]; for ( i = 0; i < count; i++ ) { lastDistance = [[gpi->bottomHints objectAtIndex: i] distanceToFrame: frame]; if (lastDistance < minimum) { bottomBests = [NSMutableArray arrayWithCapacity: 4]; [bottomBests addObject: [gpi->bottomHints objectAtIndex: i]]; minimum = lastDistance; bestPosition = [[gpi->bottomHints objectAtIndex: i] position]; empty = NO; } else if ((lastDistance == minimum) && (empty == NO) && ([[gpi->bottomHints objectAtIndex: i] position] == bestPosition)) [bottomBests addObject: [gpi->bottomHints objectAtIndex: i]]; } count = [gpi->topHints count]; topBests = [NSMutableArray arrayWithCapacity: 4]; for ( i = 0; i < count; i++ ) { lastDistance = [[gpi->topHints objectAtIndex: i] distanceToFrame: frame]; if (lastDistance < minimum) { topBests = [NSMutableArray arrayWithCapacity: 4]; bottomBests = [NSMutableArray arrayWithCapacity: 4]; [topBests addObject: [gpi->topHints objectAtIndex: i]]; minimum = lastDistance; bestPosition = [[gpi->topHints objectAtIndex: i] position] - heightOfFrame; empty = NO; } else if (lastDistance == minimum && (empty == NO) && ([[gpi->topHints objectAtIndex: i] position] - bestPosition == heightOfFrame)) [topBests addObject: [gpi->topHints objectAtIndex: i]]; } count = [bottomBests count]; if (count >= 1) { float position; bottomEmpty = NO; position = [[bottomBests objectAtIndex: 0] position]; bottomStart = NSMinX([[bottomBests objectAtIndex: 0] frame]); bottomEnd = NSMaxX([[bottomBests objectAtIndex: 0] frame]); for ( i = 1; i < count; i++ ) { bottomStart = MIN(NSMinX([[bottomBests objectAtIndex: i] frame]), bottomStart); bottomEnd = MAX(NSMaxX([[bottomBests objectAtIndex: i] frame]), bottomEnd); } bottomOfFrame = position; topOfFrame = position + heightOfFrame; } count = [topBests count]; if (count >= 1) { float position; topEmpty = NO; position = [[topBests objectAtIndex: 0] position]; topStart = NSMinX([[topBests objectAtIndex: 0] frame]); topEnd = NSMaxX([[topBests objectAtIndex: 0] frame]); for ( i = 1; i < count; i++ ) { topStart = MIN(NSMinX([[topBests objectAtIndex: i] frame]), topStart); topEnd = MAX(NSMaxX([[topBests objectAtIndex: i] frame]), topEnd); } topOfFrame = position; bottomOfFrame = position - heightOfFrame; } } gpi->hintFrame = NSMakeRect (leftOfFrame, bottomOfFrame, rightOfFrame - leftOfFrame, topOfFrame - bottomOfFrame); { [[NSColor redColor] set]; if (!leftEmpty) { leftStart = MIN(NSMinY(gpi->hintFrame), leftStart); leftEnd = MAX(NSMaxY(gpi->hintFrame), leftEnd); gpi->lastLeftRect = NSMakeRect(leftOfFrame - 1, leftStart, 2, leftEnd - leftStart); NSRectFill(gpi->lastLeftRect); } if (!rightEmpty) { rightStart = MIN(NSMinY(gpi->hintFrame), rightStart); rightEnd = MAX(NSMaxY(gpi->hintFrame), rightEnd); gpi->lastRightRect = NSMakeRect(rightOfFrame - 1, rightStart, 2, rightEnd - rightStart); NSRectFill(gpi->lastRightRect); } if (!topEmpty) { topStart = MIN(NSMinX(gpi->hintFrame), topStart); topEnd = MAX(NSMaxX(gpi->hintFrame), topEnd); gpi->lastTopRect = NSMakeRect(topStart, topOfFrame - 1, topEnd - topStart, 2); NSRectFill(gpi->lastTopRect); } if (!bottomEmpty) { bottomStart = MIN(NSMinX(gpi->hintFrame), bottomStart); bottomEnd = MAX(NSMaxX(gpi->hintFrame), bottomEnd); gpi->lastBottomRect = NSMakeRect(bottomStart, bottomOfFrame - 1, bottomEnd - bottomStart, 2); NSRectFill(gpi->lastBottomRect); } } return gpi->hintFrame; } - (NSView *)hitTest: (NSPoint)loc { id result; result = [super hitTest: loc]; if ((result != nil) && [result isKindOfClass: [GormViewEditor class]]) { return result; } else if (result != nil) { return self; } return nil; } - (NSWindow*) windowAndRect: (NSRect *)rect forObject: (id) anObject { if (anObject != _editedObject) { return nil; } else { *rect = [_editedObject convertRect:[_editedObject visibleRect] toView: nil]; return _window; } } - (void) startConnectingObject: (id) anObject withEvent: (NSEvent *)theEvent { NSPasteboard *pb; NSString *name = [document nameForObject: anObject]; NSPoint dragPoint = [theEvent locationInWindow]; pb = [NSPasteboard pasteboardWithName: NSDragPboard]; [pb declareTypes: [NSArray arrayWithObject: GormLinkPboardType] owner: self]; [pb setString: name forType: GormLinkPboardType]; [NSApp displayConnectionBetween: anObject and: nil]; // isLinkSource = YES; [self dragImage: [NSApp linkImage] at: dragPoint offset: NSZeroSize event: theEvent pasteboard: pb source: self slideBack: YES]; // isLinkSource = NO; return; } - (unsigned) draggingEntered: (id)sender { NSPasteboard *dragPb; NSArray *types; dragPb = [sender draggingPasteboard]; types = [dragPb types]; if ([types containsObject: GormLinkPboardType] == YES) { [NSApp displayConnectionBetween: [NSApp connectSource] and: _editedObject]; return NSDragOperationLink; } else if ([types containsObject: GormImagePboardType] == YES) { return NSDragOperationCopy; } else if ([types containsObject: GormSoundPboardType] == YES) { return NSDragOperationCopy; } else { return NSDragOperationNone; } } - (unsigned) draggingUpdated: (id)sender { return [self draggingEntered: sender]; } - (void) draggingExited: (id)sender { NSPasteboard *dragPb; NSArray *types; dragPb = [sender draggingPasteboard]; types = [dragPb types]; if ([types containsObject: GormLinkPboardType] == YES) { [NSApp displayConnectionBetween: [NSApp connectSource] and: nil]; } } - (void) mouseDown: (NSEvent*)theEvent { if ([theEvent modifierFlags] & NSControlKeyMask) // start a action/outlet connection { // first we need to select ourself // to do so we need to find our first ancestor that can handle a selection NSView *view = [self superview]; while ((view != nil) && ([view respondsToSelector: @selector(selectObjects:)] == NO)) { view = [view superview]; } if (view != nil) [(id)view selectObjects: [NSArray arrayWithObject: self]]; // now we can start the connection process [self startConnectingObject: _editedObject withEvent: theEvent]; } else // just send the event to our parent { if (parent) { [parent mouseDown: theEvent]; } else return [self noResponderFor: @selector(mouseDown:)]; } } - (BOOL) prepareForDragOperation: (id)sender { NSPasteboard *dragPb; NSArray *types; dragPb = [sender draggingPasteboard]; types = [dragPb types]; if ([types containsObject: GormLinkPboardType] == YES) { return YES; } else if ([types containsObject: GormImagePboardType] == YES) { return YES; } else if ([types containsObject: GormSoundPboardType] == YES) { return YES; } else { return NO; } } - (BOOL) performDragOperation: (id)sender { NSPasteboard *dragPb; NSArray *types; dragPb = [sender draggingPasteboard]; types = [dragPb types]; if ([types containsObject: GormLinkPboardType]) { [NSApp displayConnectionBetween: [NSApp connectSource] and: _editedObject]; [NSApp startConnecting]; } else if ([types containsObject: GormImagePboardType] == YES) { NSString *name = [dragPb stringForType: GormImagePboardType]; NSImage *image = [NSImage imageNamed: name]; [image setArchiveByName: NO]; [(id)_editedObject setImage: image]; return YES; } else if ([types containsObject: GormSoundPboardType] == YES) { NSString *name; name = [dragPb stringForType: GormSoundPboardType]; if([(id)_editedObject respondsToSelector: @selector(setSound:)]) { [(id)_editedObject setSound: [NSSound soundNamed: name]]; } return YES; } return NO; } - (unsigned int) draggingSourceOperationMaskForLocal: (BOOL) flag { return NSDragOperationLink; } - (BOOL) wantsSelection { return YES; } - (void) resetObject: (id)anObject { NSLog(@"resetObject should not be called on GormViewEditor !"); } - (void) orderFront { [[self window] orderFront: self]; } - (NSWindow *) window { return [super window]; } /* * Drawing additions */ - (void) postDraw: (NSRect) rect { if ([parent respondsToSelector: @selector(postDrawForView:)]) [parent performSelector: @selector(postDrawForView:) withObject: self]; } - (void) displayIfNeededInRectIgnoringOpacity: (NSRect) rect { if (currently_displaying == NO) { [[self window] disableFlushWindow]; currently_displaying = YES; [super displayIfNeededInRectIgnoringOpacity: rect]; [self lockFocus]; [self postDraw: rect]; [self unlockFocus]; [[self window] enableFlushWindow]; [[self window] flushWindow]; currently_displaying = NO; } else { [super displayIfNeededInRectIgnoringOpacity: rect]; [self lockFocus]; [self postDraw: rect]; [self unlockFocus]; } } - (void) displayRectIgnoringOpacity: (NSRect) rect { if (currently_displaying == NO) { [[self window] disableFlushWindow]; currently_displaying = YES; [super displayIfNeededInRectIgnoringOpacity: rect]; [self lockFocus]; [self postDraw: rect]; [self unlockFocus]; [[self window] enableFlushWindow]; [[self window] flushWindow]; currently_displaying = NO; } else { [super displayIfNeededInRectIgnoringOpacity: rect]; [self lockFocus]; [self postDraw: rect]; [self unlockFocus]; } } - (BOOL) acceptsTypeFromArray: (NSArray*)types { return NO; } - (NSArray*) selection { NSMutableArray *result = [NSMutableArray arrayWithCapacity: 1]; // add self to the result... if ([self respondsToSelector: @selector(editedObject)]) [result addObject: [self editedObject]]; else [result addObject: self]; return result; } - (void) makeSelectionVisible: (BOOL) value { } - (BOOL) canBeOpened { return NO; } - (BOOL) isOpened { return NO; } - (void) setOpened: (BOOL) value { if (value == YES) { [document setSelectionFromEditor: self]; } else { [self setNeedsDisplay: YES]; } } @end @implementation GormViewEditor (ResponderAdditions) - (void) keyDown: (NSEvent*)theEvent { if (_next_responder) return [_next_responder keyDown: theEvent]; else return [self noResponderFor: @selector(keyDown:)]; } - (void) keyUp: (NSEvent*)theEvent { if (_next_responder) return [_next_responder keyUp: theEvent]; else return [self noResponderFor: @selector(keyUp:)]; } - (void) otherMouseDown: (NSEvent*)theEvent { if (_next_responder) return [_next_responder otherMouseDown: theEvent]; else return [self noResponderFor: @selector(otherMouseDown:)]; } - (void) otherMouseDragged: (NSEvent*)theEvent { if (_next_responder) return [_next_responder otherMouseDragged: theEvent]; else return [self noResponderFor: @selector(otherMouseDragged:)]; } - (void) otherMouseUp: (NSEvent*)theEvent { if (_next_responder) return [_next_responder otherMouseUp: theEvent]; else return [self noResponderFor: @selector(otherMouseUp:)]; } - (void) mouseDragged: (NSEvent*)theEvent { if (_next_responder) return [_next_responder mouseDragged: theEvent]; else return [self noResponderFor: @selector(mouseDragged:)]; } - (void) mouseEntered: (NSEvent*)theEvent { if (_next_responder) return [_next_responder mouseEntered: theEvent]; else return [self noResponderFor: @selector(mouseEntered:)]; } - (void) mouseExited: (NSEvent*)theEvent { if (_next_responder) return [_next_responder mouseExited: theEvent]; else return [self noResponderFor: @selector(mouseExited:)]; } - (void) mouseMoved: (NSEvent*)theEvent { if (_next_responder) return [_next_responder mouseMoved: theEvent]; else return [self noResponderFor: @selector(mouseMoved:)]; } - (void) mouseUp: (NSEvent*)theEvent { if (_next_responder) return [_next_responder mouseUp: theEvent]; else return [self noResponderFor: @selector(mouseUp:)]; } - (void) rightMouseDown: (NSEvent*)theEvent { if (_next_responder != nil) return [_next_responder rightMouseDown: theEvent]; else return [self noResponderFor: @selector(rightMouseDown:)]; } - (void) rightMouseDragged: (NSEvent*)theEvent { if (_next_responder) return [_next_responder rightMouseDragged: theEvent]; else return [self noResponderFor: @selector(rightMouseDragged:)]; } - (void) rightMouseUp: (NSEvent*)theEvent { if (_next_responder) return [_next_responder rightMouseUp: theEvent]; else return [self noResponderFor: @selector(rightMouseUp:)]; } - (void) scrollWheel: (NSEvent *)theEvent { if (_next_responder) return [_next_responder scrollWheel: theEvent]; else return [self noResponderFor: @selector(scrollWheel:)]; } - (BOOL) acceptsFirstMouse: (NSEvent*)theEvent { return YES; } - (BOOL) acceptsFirstResponder { return NO; } @end static BOOL done_editing; @implementation GormViewEditor (EditingAdditions) - (void) handleNotification: (NSNotification*)aNotification { NSString *name = [aNotification name]; if ([name isEqual: NSControlTextDidEndEditingNotification] == YES) { done_editing = YES; } } /* Edit a textfield. If it's not already editable, make it so, then edit it */ - (NSEvent *) editTextField: view withEvent: (NSEvent *)theEvent { unsigned eventMask; BOOL wasEditable; BOOL didDrawBackground; NSTextField *editField; NSRect frame; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; NSDate *future = [NSDate distantFuture]; NSEvent *e; editField = view; frame = [editField frame]; wasEditable = [editField isEditable]; [editField setEditable: YES]; didDrawBackground = [editField drawsBackground]; [editField setDrawsBackground: YES]; [nc addObserver: self selector: @selector(handleNotification:) name: NSControlTextDidEndEditingNotification object: nil]; /* Do some modal editing */ [editField selectText: self]; eventMask = NSLeftMouseDownMask | NSLeftMouseUpMask | NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask; done_editing = NO; while (!done_editing) { NSEventType eType; e = [NSApp nextEventMatchingMask: eventMask untilDate: future inMode: NSEventTrackingRunLoopMode dequeue: YES]; eType = [e type]; switch (eType) { case NSLeftMouseDown: { NSPoint dp = [self convertPoint: [e locationInWindow] fromView: nil]; if (NSMouseInRect(dp, frame, NO) == NO) { done_editing = YES; break; } } [[editField currentEditor] mouseDown: e]; break; case NSLeftMouseUp: [[editField currentEditor] mouseUp: e]; break; case NSLeftMouseDragged: [[editField currentEditor] mouseDragged: e]; break; case NSKeyDown: [[editField currentEditor] keyDown: e]; break; case NSKeyUp: [[editField currentEditor] keyUp: e]; break; case NSFlagsChanged: [[editField currentEditor] flagsChanged: e]; break; default: NSLog(@"Internal Error: Unhandled event during editing: %@", e); break; } } [editField setEditable: wasEditable]; [editField setDrawsBackground: didDrawBackground]; [nc removeObserver: self name: NSControlTextDidEndEditingNotification object: nil]; [[editField currentEditor] resignFirstResponder]; [self setNeedsDisplay: YES]; return e; } @end