// // PSVolumeController.m // Pester // // Created by Nicholas Riley on Tue Apr 08 2003. // Copyright (c) 2003 Nicholas Riley. All rights reserved. // #import "PSVolumeController.h" #import "NJRSoundManager.h" #import "NJRNonCenteringWindow.h" @interface NSMenu (SnowLeopardAdditions) - (BOOL)popUpMenuPositioningItem:(NSMenuItem *)item atLocation:(NSPoint)location inView:(NSView *)view; - (void)setAllowsContextMenuPlugIns:(BOOL)allows; - (void)cancelTracking; @end @interface NSMenuItem (SnowLeopardAdditions) - (void)setView:(NSView *)view; @end @implementation PSVolumeController + (PSVolumeController *)controllerWithVolume:(float)volume delegate:(id)aDelegate; { return [[self alloc] initWithVolume: volume delegate: aDelegate]; } - (id)initWithVolume:(float)volume delegate:(id)aDelegate; { if ( (self = [self initWithWindowNibName: @"Volume"]) != nil) { [self window]; // connect outlets if ([NJRSoundManager volumeIsNotMutedOrInvalid: volume]) [volumeSlider setFloatValue: volume]; delegate = [aDelegate retain]; NSView *view = [aDelegate volumeControllerLaunchingView: self]; // In 10.6, we can no longer force the modal session to work by "seeding" the slider with a mouse-down event. // Instead, use a menu. (This should mostly work on 10.5 too, but is currently untested.) if ([NSMenu instancesRespondToSelector: @selector(popUpMenuPositioningItem:atLocation:inView:)]) { menu = [[NSMenu alloc] initWithTitle: @""]; NSMenuItem *menuItem = [[NSMenuItem alloc] init]; [menuItem setView: contentView]; [menu addItem: menuItem]; [menuItem release]; NSPoint point; if (view != nil) { NSSize size = [view bounds].size; point = [view isFlipped] ? NSMakePoint(0, size.height) : NSZeroPoint; } else { point = [NSEvent mouseLocation]; } [menu setAllowsContextMenuPlugIns: NO]; [menu popUpMenuPositioningItem: nil atLocation: point inView: view]; [menu release]; } else { NSWindow *window = [[NJRNonCenteringWindow alloc] initWithContentRect: [contentView bounds] styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO]; [window setContentView: contentView]; [window setOpaque: NO]; [window setBackgroundColor: [NSColor colorWithCalibratedWhite: 0.81f alpha: 0.9f]]; [window setHasShadow: YES]; [window setOneShot: YES]; [window setDelegate: self]; if (view != nil) { NSRect rect = [view convertRect: [view bounds] toView: nil]; NSWindow *parentWindow = [view window]; rect.origin = [parentWindow convertBaseToScreen: rect.origin]; rect.origin.x -= [window frame].size.width - rect.size.width + 1; [window setFrameTopLeftPoint: rect.origin]; NSRect visibleFrame = [[parentWindow screen] visibleFrame]; if (!NSContainsRect(visibleFrame, [window frame])) { NSPoint viewTopLeft = { rect.origin.x, rect.origin.y + rect.size.height }; [window setFrameOrigin: viewTopLeft]; } } // -[NSApplication beginModalSessionForWindow:] shows and centers the window; we use NJRNonCenteringWindow to prevent the repositioning from succeeding NSModalSession session = [NSApp beginModalSessionForWindow: window]; [volumeSlider mouseDown: [NSApp currentEvent]]; [NSApp runModalSession: session]; [NSApp endModalSession: session]; [window close]; } [self autorelease]; } return self; } - (void)dealloc; { [delegate release]; [super dealloc]; } - (IBAction)volumeSet:(NSSlider *)sender; { [delegate volumeController: self didSetVolume: [sender floatValue]]; if (NSEventMaskFromType([[NSApp currentEvent] type]) & (NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask)) [menu cancelTracking]; } @end