/* Copyright 2011 Sven Weidauer * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #import "ArrowWindow.h" #import "ArrowBox.h" @implementation ArrowWindow @synthesize acceptsKey; - (id) initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle backing: (NSBackingStoreType)bufferingType defer: (BOOL)flag { if ((self = [super initWithContentRect: contentRect styleMask: NSBorderlessWindowMask backing: bufferingType defer: flag]) == nil) { return nil; } [self setBackgroundColor: [NSColor clearColor]]; [self setOpaque: NO]; [self setHasShadow: YES]; return self; } - (void) setContentView: (NSView *)aView { if (aView == content) return; [content removeFromSuperview]; content = aView; if (content == nil) return; if (box == nil) { box = [[ArrowBox alloc] initWithFrame: NSZeroRect]; [box setArrowEdge: ArrowTopEdge]; [super setContentView: box]; [box release]; } [box addSubview: content]; NSRect frame = [self contentRectForFrameRect: [self frame]]; frame.origin = [self convertScreenToBase: frame.origin]; frame = [box convertRect: frame fromView: nil]; [content setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; [content setFrame: frame]; } - (void) setContentSize: (NSSize)aSize { NSRect frame = [content frame]; frame.size = aSize; frame = [box convertRect: frame toView: nil]; frame.origin = [self convertBaseToScreen: frame.origin]; frame = [self frameRectForContentRect: frame]; [self setFrame: frame display: YES]; } static const CGFloat padding = 0; - (NSRect) contentRectForFrameRect: (NSRect)frameRect { const CGFloat arrowSize = [box arrowSize]; const CGFloat offset = 2 * (padding + arrowSize ); return NSInsetRect( frameRect, offset, offset ); } - (NSRect) frameRectForContentRect: (NSRect)contentRect { const CGFloat arrowSize = [box arrowSize]; const CGFloat offset = -2 * (padding + arrowSize ); return NSInsetRect( contentRect, offset, offset ); } + (NSRect) frameRectForContentRect: (NSRect)cRect styleMask: (NSUInteger)aStyle { const CGFloat DefaultArrowSize = 15; const CGFloat offset = -2 * (padding + DefaultArrowSize); return NSInsetRect( cRect, offset, offset ); } - (BOOL) canBecomeKeyWindow { return acceptsKey; } - (void) moveToPoint: (NSPoint) screenPoint { switch ([box arrowEdge]) { case ArrowNone: screenPoint.x -= [box arrowSize]; screenPoint.y += [box arrowSize]; break; case ArrowTopEdge: screenPoint.x -= [box arrowPosition]; break; case ArrowBottomEdge: screenPoint.x -= [box arrowPosition]; screenPoint.y += NSHeight( [self frame] ); break; case ArrowLeftEdge: screenPoint.y += NSHeight( [self frame] ) - [box arrowPosition] - [box arrowSize]; break; case ArrowRightEdge: screenPoint.x -= NSWidth( [self frame] ); screenPoint.y += NSHeight( [self frame] ) - [box arrowPosition] - [box arrowSize]; break; } [self setFrameTopLeftPoint: screenPoint]; } static NSRect ScreenRectForView( NSView *view ) { NSRect viewRect = [view bounds]; // in View coordinate system viewRect = [view convertRect: viewRect toView: nil]; // translate to window coordinates viewRect.origin = [[view window] convertBaseToScreen: viewRect.origin]; // translate to screen coordinates return viewRect; } - (void) attachToView: (NSView *) view { if (nil != attachedWindow) [self detach]; NSRect viewRect = ScreenRectForView( view ); NSPoint arrowPoint; switch ([box arrowEdge]) { case ArrowLeftEdge: arrowPoint = NSMakePoint( NSMaxX( viewRect ), NSMidY( viewRect ) ); break; case ArrowBottomEdge: arrowPoint = NSMakePoint( NSMidX( viewRect ), NSMaxY( viewRect ) ); break; case ArrowRightEdge: arrowPoint = NSMakePoint( NSMinX( viewRect ), NSMidY( viewRect ) ); break; case ArrowNone: case ArrowTopEdge: default: arrowPoint = NSMakePoint( NSMidX( viewRect ), NSMinY( viewRect ) ); break; } attachedWindow = [view window]; [self moveToPoint: arrowPoint]; [attachedWindow addChildWindow: self ordered: NSWindowAbove]; } - (void) detach { [attachedWindow removeChildWindow: self]; [self close]; attachedWindow = nil; } //MARK: - //MARK: Properties - (void) setArrowPosition: (CGFloat) newPosition { [box setArrowPosition: newPosition]; } - (CGFloat) arrowPosition { return [box arrowPosition]; } - (void) setArrowSize: (CGFloat)newSize { NSRect contentRect = [self contentRectForFrameRect: [self frame]]; [box setArrowSize: newSize]; [self setFrame: [self frameRectForContentRect: contentRect] display: [self isVisible]]; } - (CGFloat) arrowSize { return [box arrowSize]; } - (void) setArrowEdge: (ArrowEdge) newEdge { [box setArrowEdge: newEdge]; } - (ArrowEdge) arrowEdge { return [box arrowEdge]; } - (void) setCornerRadius: (CGFloat)newRadius { [box setCornerRadius: newRadius]; } - (CGFloat) cornerRadius { return [box cornerRadius]; } @end