source: trunk/Cocoa/Pester/Source/PSVolumeController.m@ 612

Last change on this file since 612 was 595, checked in by Nicholas Riley, 15 years ago

Keyboard-accessible and better-behaving volume slider.

File size: 4.3 KB
Line 
1//
2// PSVolumeController.m
3// Pester
4//
5// Created by Nicholas Riley on Tue Apr 08 2003.
6// Copyright (c) 2003 Nicholas Riley. All rights reserved.
7//
8
9#import "PSVolumeController.h"
10#import "NJRSoundManager.h"
11#import "NJRNonCenteringWindow.h"
12#include <Carbon/Carbon.h>
13
14@interface NSMenu (SnowLeopardAdditions)
15- (BOOL)popUpMenuPositioningItem:(NSMenuItem *)item atLocation:(NSPoint)location inView:(NSView *)view;
16- (void)setAllowsContextMenuPlugIns:(BOOL)allows;
17- (void)cancelTracking;
18@end
19
20@interface NSMenuItem (SnowLeopardAdditions)
21- (void)setView:(NSView *)view;
22@end
23
24@implementation PSVolumeController
25
26+ (PSVolumeController *)controllerWithVolume:(float)volume delegate:(id)aDelegate;
27{
28 return [[self alloc] initWithVolume: volume delegate: aDelegate];
29}
30
31- (id)initWithVolume:(float)volume delegate:(id)aDelegate;
32{
33 if ( (self = [self initWithWindowNibName: @"Volume"]) != nil) {
34 [self window]; // connect outlets
35
36 if ([NJRSoundManager volumeIsNotMutedOrInvalid: volume])
37 [volumeSlider setFloatValue: volume];
38
39 delegate = [aDelegate retain];
40
41 NSView *view = [aDelegate volumeControllerLaunchingView: self];
42
43 // In 10.6, we can no longer force the modal session to work by "seeding" the slider with a mouse-down event.
44 // Instead, use a menu. (This should mostly work on 10.5 too, but is currently untested.)
45 if ([NSMenu instancesRespondToSelector: @selector(popUpMenuPositioningItem:atLocation:inView:)]) {
46 menu = [[NSMenu alloc] initWithTitle: @""];
47 NSMenuItem *menuItem = [[NSMenuItem alloc] init];
48 [menuItem setView: contentView];
49 [menu addItem: menuItem];
50 [menuItem release];
51 NSPoint point;
52 if (view != nil) {
53 NSSize size = [view bounds].size;
54 point = [view isFlipped] ? NSMakePoint(0, size.height) : NSZeroPoint;
55 } else {
56 point = [NSEvent mouseLocation];
57 }
58 [menu setAllowsContextMenuPlugIns: NO];
59 // replace with http://waffle.wootest.net/2007/08/07/popping-up-a-menu-in-cocoa/
60 [menu popUpMenuPositioningItem: nil atLocation: point inView: view];
61 [menu release];
62 } else {
63 NSWindow *window = [[NJRNonCenteringWindow alloc] initWithContentRect: [contentView bounds] styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO];
64 [window setContentView: contentView];
65 [window setOpaque: NO];
66 [window setBackgroundColor: [NSColor colorWithCalibratedWhite: 0.81f alpha: 0.9f]];
67 [window setHasShadow: YES];
68 [window setOneShot: YES];
69 [window setDelegate: self];
70
71 if (view != nil) {
72 NSRect rect = [view convertRect: [view bounds] toView: nil];
73 NSWindow *parentWindow = [view window];
74 rect.origin = [parentWindow convertBaseToScreen: rect.origin];
75 rect.origin.x -= [window frame].size.width - rect.size.width + 1;
76 [window setFrameTopLeftPoint: rect.origin];
77 NSRect visibleFrame = [[parentWindow screen] visibleFrame];
78 if (!NSContainsRect(visibleFrame, [window frame])) {
79 NSPoint viewTopLeft = { rect.origin.x, rect.origin.y + rect.size.height };
80 [window setFrameOrigin: viewTopLeft];
81 }
82 }
83 // -[NSApplication beginModalSessionForWindow:] shows and centers the window; we use NJRNonCenteringWindow to prevent the repositioning from succeeding
84 NSModalSession session = [NSApp beginModalSessionForWindow: window];
85 [volumeSlider mouseDown: [NSApp currentEvent]];
86 [NSApp runModalSession: session];
87 [NSApp endModalSession: session];
88 [window close];
89 }
90
91 [self autorelease];
92 }
93 return self;
94}
95
96- (void)dealloc;
97{
98 [delegate release];
99 [super dealloc];
100}
101
102- (IBAction)volumeSet:(NSSlider *)sender;
103{
104 // XXX don't delay preview for keyboard adjustment
105 [delegate volumeController: self didSetVolume: [sender floatValue]];
106 NSLog(@"%@", [NSApp currentEvent]);
107 unsigned eventMask = NSEventMaskFromType([[NSApp currentEvent] type]);
108 // The event may simply be a mouse-up: close the menu.
109 if (eventMask & (NSLeftMouseUpMask | NSRightMouseDownMask | NSOtherMouseDownMask))
110 [menu cancelTracking];
111 // On a quick click, the event may be a mouse down but the mouse button is no longer down.
112 if (!(eventMask & (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask)))
113 return;
114 // 10.6+: use [NSEvent pressedMouseButtons] instead
115 if (GetCurrentButtonState() == 0)
116 [menu cancelTracking];
117}
118
119@end
Note: See TracBrowser for help on using the repository browser.