#import "JVInspectorController.h" #import "MVApplicationController.h" static JVInspectorController *sharedInstance = nil; static NSPoint inspectorLastPoint = { 100., 800. }; static NSMutableSet *inspectors = nil; @interface JVInspectorController (JVInspectionControllerPrivate) - (void) _loadInspector; - (void) _resizeWindowForContentSize:(NSSize) size; - (void) _inspectWindow:(NSWindow *) window; @end #pragma mark - @implementation JVInspectorController + (JVInspectorController *) sharedInspector { extern JVInspectorController *sharedInstance; if( ! sharedInstance && [MVApplicationController isTerminating] ) return nil; return ( sharedInstance ? sharedInstance : ( sharedInstance = [[self alloc] initWithObject:nil lockedOn:NO] ) ); } + (IBAction) showInspector:(id) sender { [[self sharedInspector] show:sender]; } + (JVInspectorController *) inspectorOfObject:(id ) object { extern NSMutableSet *inspectors; NSEnumerator *e = [inspectors objectEnumerator]; JVInspectorController *inspector = nil; while( ( inspector = [e nextObject] ) ) if( [inspector inspectedObject] == object ) return inspector; return [[self alloc] initWithObject:object lockedOn:YES]; } #pragma mark - - (id) initWithObject:(id ) object lockedOn:(BOOL) locked { NSRect panelRect = NSZeroRect; panelRect.origin.x = 200; panelRect.origin.y = NSMaxY( [[NSScreen mainScreen] visibleFrame] ) + 400; panelRect.size.width = 175; panelRect.size.height = 200; NSWindow *panel = [[[NSPanel alloc] initWithContentRect:panelRect styleMask:( ( ! locked ? NSUtilityWindowMask : 0 ) | NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask ) backing:NSBackingStoreBuffered defer:YES] autorelease]; if( locked ) { [(NSPanel *)panel setFloatingPanel:YES]; [(NSPanel *)panel setHidesOnDeactivate:YES]; } else [panel setFrameAutosaveName:@"inspector"]; [panel setDelegate:self]; if( ( self = [self initWithWindow:panel] ) ) { _locked = locked; _object = [object retain]; _inspector = [[_object inspector] retain]; if( _locked ) { extern NSMutableSet *inspectors; if( ! inspectors ) inspectors = [[NSMutableSet set] retain]; [inspectors addObject:self]; } else [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( keyWindowChanged: ) name:NSWindowDidBecomeKeyNotification object:nil]; if( _object == nil ) [self _inspectWindow:[[NSApplication sharedApplication] keyWindow]]; } return self; } - (void) dealloc { extern JVInspectorController *sharedInstance; [[self window] close]; if( [_inspector respondsToSelector:@selector( didUnload )] ) [(NSObject *)_inspector didUnload]; [[NSNotificationCenter defaultCenter] removeObserver:self]; if( self == sharedInstance ) sharedInstance = nil; [_object release]; [_inspector release]; _object = nil; _inspector = nil; [super dealloc]; } #pragma mark - - (IBAction) show:(id) sender { extern NSPoint inspectorLastPoint; [self _loadInspector]; if( _locked && ! [[self window] isVisible] ) inspectorLastPoint = [[self window] cascadeTopLeftFromPoint:inspectorLastPoint]; [[self window] makeKeyAndOrderFront:nil]; } - (BOOL) locked { return _locked; } #pragma mark - - (void) inspectObject:(id ) object { if( object == _object ) return; if( [_inspector respondsToSelector:@selector( shouldUnload )] ) if( ! [(NSObject *)_inspector shouldUnload] ) return; [_object autorelease]; _object = [object retain]; id oldInspector = _inspector; [_inspector autorelease]; _inspector = [[_object inspector] retain]; if( [[self window] isVisible] ) { if( [oldInspector respondsToSelector:@selector( didUnload )] ) [(NSObject *)oldInspector didUnload]; [self _loadInspector]; } } #pragma mark - - (id ) inspectedObject { return [[_object retain] autorelease]; } - (id ) inspector { return [[_inspector retain] autorelease]; } #pragma mark - - (BOOL) windowShouldClose:(id) sender { BOOL should = YES; if( [_inspector respondsToSelector:@selector( shouldUnload )] ) should = [(NSObject *)_inspector shouldUnload]; if( should ) { extern NSMutableSet *inspectors; [inspectors removeObject:self]; if( ! [inspectors count] ) { [inspectors release]; inspectors = nil; } [self autorelease]; } return should; } - (void) keyWindowChanged:(NSNotification *) notification { if( ! _locked ) [self _inspectWindow:[notification object]]; } @end #pragma mark - @implementation JVInspectorController (JVInspectionControllerPrivate) - (void) _loadInspector { NSView *view = [_inspector view]; if( [_object respondsToSelector:@selector( willBeInspected )] ) [(NSObject *)_object willBeInspected]; if( [_inspector respondsToSelector:@selector( willLoad )] ) [(NSObject *)_inspector willLoad]; if( view && _inspector ) { NSRect windowFrame = [[[self window] contentView] frame]; NSSize minSize = [_inspector minSize]; if( NSWidth( windowFrame ) < minSize.width || NSHeight( windowFrame ) < minSize.height ) { NSSize newSize = windowFrame.size; if( NSWidth( windowFrame ) < minSize.width ) newSize.width = minSize.width; if( NSHeight( windowFrame ) < minSize.height ) newSize.height = minSize.height; [self _resizeWindowForContentSize:newSize]; } [[self window] setTitle:[_inspector title]]; [[self window] setMinSize:[NSWindow frameRectForContentRect:NSMakeRect( 0., 0., minSize.width, minSize.height) styleMask:[[self window] styleMask]].size]; [[self window] setContentView:view]; if( [_inspector respondsToSelector:@selector( didLoad )] ) [(NSObject *)_inspector didLoad]; } else { [[self window] setTitle:NSLocalizedString( @"No Info", "no info inspector title" )]; [[self window] setContentView:[[[NSView alloc] initWithFrame:[[[self window] contentView] frame]] autorelease]]; } } - (void) _resizeWindowForContentSize:(NSSize) size { NSRect windowFrame = [NSWindow contentRectForFrameRect:[[self window] frame] styleMask:[[self window] styleMask]]; NSRect newWindowFrame = [NSWindow frameRectForContentRect:NSMakeRect( NSMinX( windowFrame ), NSMaxY( windowFrame ) - size.height, size.width, size.height ) styleMask:[[self window] styleMask]]; [[self window] setFrame:newWindowFrame display:YES animate:[[self window] isVisible]]; } - (void) _inspectWindow:(NSWindow *) window { if( [[window delegate] conformsToProtocol:@protocol( JVInspectionDelegator )] ) { id obj = [[window delegate] objectToInspect]; if( obj != _object ) [self inspectObject:obj]; } } @end