]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindowTitle.m
macosx: fixed compilation when using the 10.5 or 10.6 SDK
[vlc] / modules / gui / macosx / MainWindowTitle.m
1 /*****************************************************************************
2  * MainWindowTitle.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2011-2012 Felix Paul Kühne
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #import <vlc_common.h>
25 #import "intf.h"
26 #import "MainWindowTitle.h"
27 #import "CoreInteraction.h"
28 #import "CompatibilityFixes.h"
29
30 /*****************************************************************************
31  * VLCMainWindowTitleView
32  *
33  * this is our title bar, which can do anything a title should do
34  * it relies on the VLCWindowButtonCell to display the correct traffic light
35  * states, since we can't capture the mouse-moved events here correctly
36  *****************************************************************************/
37
38 @implementation VLCMainWindowTitleView
39 - (id)init
40 {
41     o_window_title_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: [NSColor whiteColor], NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:12.0], NSFontAttributeName, nil] retain];
42
43     return [super init];
44 }
45
46 - (void)dealloc
47 {
48     [[NSNotificationCenter defaultCenter] removeObserver: self];
49
50     [o_red_img release];
51     [o_red_over_img release];
52     [o_red_on_img release];
53     [o_yellow_img release];
54     [o_yellow_over_img release];
55     [o_yellow_on_img release];
56     [o_green_img release];
57     [o_green_over_img release];
58     [o_green_on_img release];
59
60     [o_window_title_shadow release];
61     [o_window_title_attributes_dict release];
62
63     [super dealloc];
64 }
65
66 - (void)awakeFromNib
67 {
68     [self setAutoresizesSubviews: YES];
69     [self setImagesLeft:[NSImage imageNamed:@"topbar-dark-left"] middle: [NSImage imageNamed:@"topbar-dark-center-fill"] right:[NSImage imageNamed:@"topbar-dark-right"]];
70
71     [self loadButtonIcons];
72     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(controlTintChanged:) name: NSControlTintDidChangeNotification object: nil];
73
74     [o_red_btn setImage: o_red_img];
75     [o_red_btn setAlternateImage: o_red_on_img];
76     [[o_red_btn cell] setShowsBorderOnlyWhileMouseInside: YES];
77     [[o_red_btn cell] setTag: 0];
78     [o_yellow_btn setImage: o_yellow_img];
79     [o_yellow_btn setAlternateImage: o_yellow_on_img];
80     [[o_yellow_btn cell] setShowsBorderOnlyWhileMouseInside: YES];
81     [[o_yellow_btn cell] setTag: 1];
82     [o_green_btn setImage: o_green_img];
83     [o_green_btn setAlternateImage: o_green_on_img];
84     [[o_green_btn cell] setShowsBorderOnlyWhileMouseInside: YES];
85     [[o_green_btn cell] setTag: 2];
86     [o_fullscreen_btn setImage: [NSImage imageNamed:@"window-fullscreen"]];
87     [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"window-fullscreen-on"]];
88     [[o_fullscreen_btn cell] setShowsBorderOnlyWhileMouseInside: YES];
89     [[o_fullscreen_btn cell] setTag: 3];
90 }
91
92 - (void)controlTintChanged:(NSNotification *)notification
93 {
94     [o_red_img release];
95     [o_red_over_img release];
96     [o_red_on_img release];
97     [o_yellow_img release];
98     [o_yellow_over_img release];
99     [o_yellow_on_img release];
100     [o_green_img release];
101     [o_green_over_img release];
102     [o_green_on_img release];
103
104     [self loadButtonIcons];
105
106     [o_red_btn setNeedsDisplay];
107     [o_yellow_btn setNeedsDisplay];
108     [o_green_btn setNeedsDisplay];
109 }
110
111 - (void)loadButtonIcons
112 {
113     if (OSX_LION)
114     {
115         if( [NSColor currentControlTint] == NSBlueControlTint )
116         {
117             o_red_img = [[NSImage imageNamed:@"lion-window-close"] retain];
118             o_red_over_img = [[NSImage imageNamed:@"lion-window-close-over"] retain];
119             o_red_on_img = [[NSImage imageNamed:@"lion-window-close-on"] retain];
120             o_yellow_img = [[NSImage imageNamed:@"lion-window-minimize"] retain];
121             o_yellow_over_img = [[NSImage imageNamed:@"lion-window-minimize-over"] retain];
122             o_yellow_on_img = [[NSImage imageNamed:@"lion-window-minimize-on"] retain];
123             o_green_img = [[NSImage imageNamed:@"lion-window-zoom"] retain];
124             o_green_over_img = [[NSImage imageNamed:@"lion-window-zoom-over"] retain];
125             o_green_on_img = [[NSImage imageNamed:@"lion-window-zoom-on"] retain];
126         } else {
127             o_red_img = [[NSImage imageNamed:@"lion-window-close-graphite"] retain];
128             o_red_over_img = [[NSImage imageNamed:@"lion-window-close-over-graphite"] retain];
129             o_red_on_img = [[NSImage imageNamed:@"lion-window-close-on-graphite"] retain];
130             o_yellow_img = [[NSImage imageNamed:@"lion-window-minimize-graphite"] retain];
131             o_yellow_over_img = [[NSImage imageNamed:@"lion-window-minimize-over-graphite"] retain];
132             o_yellow_on_img = [[NSImage imageNamed:@"lion-window-minimize-on-graphite"] retain];
133             o_green_img = [[NSImage imageNamed:@"lion-window-zoom-graphite"] retain];
134             o_green_over_img = [[NSImage imageNamed:@"lion-window-zoom-over-graphite"] retain];
135             o_green_on_img = [[NSImage imageNamed:@"lion-window-zoom-on-graphite"] retain];            
136         }
137     } else {
138         if( [NSColor currentControlTint] == NSBlueControlTint )
139         {
140             o_red_img = [[NSImage imageNamed:@"snowleo-window-close"] retain];
141             o_red_over_img = [[NSImage imageNamed:@"snowleo-window-close-over"] retain];
142             o_red_on_img = [[NSImage imageNamed:@"snowleo-window-close-on"] retain];
143             o_yellow_img = [[NSImage imageNamed:@"snowleo-window-minimize"] retain];
144             o_yellow_over_img = [[NSImage imageNamed:@"snowleo-window-minimize-over"] retain];
145             o_yellow_on_img = [[NSImage imageNamed:@"snowleo-window-minimize-on"] retain];
146             o_green_img = [[NSImage imageNamed:@"snowleo-window-zoom"] retain];
147             o_green_over_img = [[NSImage imageNamed:@"snowleo-window-zoom-over"] retain];
148             o_green_on_img = [[NSImage imageNamed:@"snowleo-window-zoom-on"] retain];
149         } else {
150             o_red_img = [[NSImage imageNamed:@"snowleo-window-close-graphite"] retain];
151             o_red_over_img = [[NSImage imageNamed:@"snowleo-window-close-over-graphite"] retain];
152             o_red_on_img = [[NSImage imageNamed:@"snowleo-window-close-on-graphite"] retain];
153             o_yellow_img = [[NSImage imageNamed:@"snowleo-window-minimize-graphite"] retain];
154             o_yellow_over_img = [[NSImage imageNamed:@"snowleo-window-minimize-over-graphite"] retain];
155             o_yellow_on_img = [[NSImage imageNamed:@"snowleo-window-minimize-on-graphite"] retain];
156             o_green_img = [[NSImage imageNamed:@"snowleo-window-zoom-graphite"] retain];
157             o_green_over_img = [[NSImage imageNamed:@"snowleo-window-zoom-over-graphite"] retain];
158             o_green_on_img = [[NSImage imageNamed:@"snowleo-window-zoom-on-graphite"] retain];            
159         }
160     }
161 }
162
163 - (BOOL)mouseDownCanMoveWindow
164 {
165     return YES;
166 }
167
168 - (IBAction)buttonAction:(id)sender
169 {
170     if (sender == o_red_btn)
171         [[self window] performClose: sender];
172     else if (sender == o_yellow_btn)
173         [[self window] miniaturize: sender];
174     else if (sender == o_green_btn)
175         [[self window] performZoom: sender];
176     else if (sender == o_fullscreen_btn)
177         [[VLCCoreInteraction sharedInstance] toggleFullscreen];
178     else
179         msg_Err( VLCIntf, "unknown button action sender" );
180
181     [self setWindowButtonOver: NO];
182     [self setWindowFullscreenButtonOver: NO];
183 }
184
185 - (void)setWindowTitle:(NSString *)title
186 {
187     if (!o_window_title_shadow)
188     {
189         o_window_title_shadow = [[NSShadow alloc] init];
190         [o_window_title_shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
191         [o_window_title_shadow setShadowOffset:NSMakeSize(0.0, -1.5)];
192         [o_window_title_shadow setShadowBlurRadius:0.5];
193         [o_window_title_shadow retain];
194     }
195
196     NSMutableAttributedString *o_attributed_title = [[NSMutableAttributedString alloc] initWithString:title attributes: o_window_title_attributes_dict];
197     NSUInteger i_titleLength = [title length];
198
199     [o_attributed_title addAttribute:NSShadowAttributeName value:o_window_title_shadow range:NSMakeRange(0, i_titleLength)];
200     [o_attributed_title setAlignment: NSCenterTextAlignment range:NSMakeRange(0, i_titleLength)];
201     [o_title_lbl setAttributedStringValue:o_attributed_title];
202     [o_attributed_title release];
203 }
204
205 - (void)setFullscreenButtonHidden:(BOOL)b_value
206 {
207     [o_fullscreen_btn setHidden: b_value];
208 }
209
210 - (void)setWindowButtonOver:(BOOL)b_value
211 {
212     if( b_value )
213     {
214         [o_red_btn setImage: o_red_over_img];
215         [o_yellow_btn setImage: o_yellow_over_img];
216         [o_green_btn setImage: o_green_over_img];
217     }
218     else
219     {
220         [o_red_btn setImage: o_red_img];
221         [o_yellow_btn setImage: o_yellow_img];
222         [o_green_btn setImage: o_green_img];
223     }
224 }
225
226 - (void)setWindowFullscreenButtonOver:(BOOL)b_value
227 {
228     if (b_value)
229         [o_fullscreen_btn setImage: [NSImage imageNamed:@"window-fullscreen-over"]];
230     else
231         [o_fullscreen_btn setImage: [NSImage imageNamed:@"window-fullscreen"]];
232 }
233 @end
234
235 /*****************************************************************************
236  * VLCWindowButtonCell
237  *
238  * since the title bar cannot fetch these mouse events (the more top-level
239  * NSButton is unable fetch them as well), we are using a subclass of the
240  * button cell to do so. It's set in the nib for the respective objects.
241  *****************************************************************************/
242
243 @implementation VLCWindowButtonCell
244
245 - (void)mouseEntered:(NSEvent *)theEvent
246 {
247     if ([self tag] == 3)
248         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowFullscreenButtonOver: YES];
249     else
250         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowButtonOver: YES];
251 }
252
253 - (void)mouseExited:(NSEvent *)theEvent
254 {
255     if ([self tag] == 3)
256         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowFullscreenButtonOver: NO];
257     else
258         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowButtonOver: NO];
259 }
260
261 /* accessibility stuff */
262 - (NSArray*)accessibilityAttributeNames {
263     NSArray *theAttributeNames = [super accessibilityAttributeNames];
264     id theControlView = [self controlView];
265     return ([theControlView respondsToSelector: @selector(extendedAccessibilityAttributeNames:)] ? [theControlView extendedAccessibilityAttributeNames: theAttributeNames] : theAttributeNames);        // ask the cell's control view (i.e., the button) for additional attribute values
266 }
267
268 - (id)accessibilityAttributeValue: (NSString*)theAttributeName {
269     id theControlView = [self controlView];
270     if ([theControlView respondsToSelector: @selector(extendedAccessibilityAttributeValue:)]) {
271         id theValue = [theControlView extendedAccessibilityAttributeValue: theAttributeName];
272         if (theValue) {
273             return theValue;    // if this is an extended attribute value we added, return that -- otherwise, fall back to super's implementation
274         }
275     }
276     return [super accessibilityAttributeValue: theAttributeName];
277 }
278
279 - (BOOL)accessibilityIsAttributeSettable: (NSString*)theAttributeName {
280     id theControlView = [self controlView];
281     if ([theControlView respondsToSelector: @selector(extendedAccessibilityIsAttributeSettable:)]) {
282         NSNumber *theValue = [theControlView extendedAccessibilityIsAttributeSettable: theAttributeName];
283         if (theValue) {
284             return [theValue boolValue];        // same basic strategy we use in -accessibilityAttributeValue:
285         }
286     }
287     return [super accessibilityIsAttributeSettable: theAttributeName];
288 }
289
290 @end
291
292
293 /*****************************************************************************
294  * VLCResizeControl
295  *
296  * For Leopard and Snow Leopard, we need to emulate the resize control on the
297  * bottom right of the window, since it is gone by using the borderless window
298  * mask. A proper fix would be Lion-only.
299  *****************************************************************************/
300
301 @implementation VLCResizeControl
302
303 - (void)mouseDown:(NSEvent *)theEvent {
304     BOOL keepOn = YES;
305
306     while (keepOn) {
307         theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask |
308                     NSLeftMouseDraggedMask];
309
310         switch ([theEvent type]) {
311             case NSLeftMouseDragged:
312             {
313                 NSRect windowFrame = [[self window] frame];
314                 CGFloat deltaX, deltaY, oldOriginY;
315                 deltaX = [theEvent deltaX];
316                 deltaY = [theEvent deltaY];
317                 oldOriginY = windowFrame.origin.y;
318
319                 windowFrame.origin.y = (oldOriginY + windowFrame.size.height) - (windowFrame.size.height + deltaY);
320                 windowFrame.size.width += deltaX;
321                 windowFrame.size.height += deltaY;
322
323                 NSSize winMinSize = [self window].minSize;
324                 if (windowFrame.size.width < winMinSize.width)
325                     windowFrame.size.width = winMinSize.width;
326
327                 if (windowFrame.size.height < winMinSize.height)
328                 {
329                     windowFrame.size.height = winMinSize.height;
330                     windowFrame.origin.y = oldOriginY;
331                 }
332
333                 [[self window] setFrame: windowFrame display: YES animate: NO];
334                 break;
335             }
336                 break;
337             case NSLeftMouseUp:
338                 keepOn = NO;
339                 break;
340             default:
341                 /* Ignore any other kind of event. */
342                 break;
343         }
344
345     };
346
347     return;
348 }
349
350 @end
351
352 /*****************************************************************************
353  * VLCColorView
354  *
355  * since we are using a clear window color when using the black window
356  * style, some filling is needed behind the video and some other elements
357  *****************************************************************************/
358
359 @implementation VLCColorView
360
361 - (void)drawRect:(NSRect)rect {
362     [[NSColor blackColor] setFill];
363     NSRectFill(rect);
364 }
365
366 @end
367
368 /*****************************************************************************
369  * custom window buttons to support the accessibility stuff
370  *****************************************************************************/
371
372 @implementation VLCCustomWindowButtonPrototype
373 + (Class)cellClass {
374     return [VLCWindowButtonCell class];
375 }
376
377 - (NSArray*)extendedAccessibilityAttributeNames: (NSArray*)theAttributeNames {
378     return ([theAttributeNames containsObject: NSAccessibilitySubroleAttribute] ? theAttributeNames : [theAttributeNames arrayByAddingObject: NSAccessibilitySubroleAttribute]);        // run-of-the-mill button cells don't usually have a Subrole attribute, so we add that attribute
379 }
380
381 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
382     return nil;
383 }
384
385 - (NSNumber*)extendedAccessibilityIsAttributeSettable: (NSString*)theAttributeName {
386     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? [NSNumber numberWithBool: NO] : nil); // make the Subrole attribute we added non-settable
387 }
388
389 - (void)accessibilityPerformAction: (NSString*)theActionName {
390     if ([theActionName isEqualToString: NSAccessibilityPressAction]) {
391         if ([self isEnabled]) {
392             [self performClick: nil];
393         }
394     } else {
395         [super accessibilityPerformAction: theActionName];
396     }
397 }
398
399 @end
400
401 @implementation VLCCustomWindowCloseButton
402 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
403     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityCloseButtonAttribute : nil);
404 }
405
406 @end
407
408
409 @implementation VLCCustomWindowMinimizeButton
410 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
411     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityMinimizeButtonAttribute : nil);
412 }
413
414 @end
415
416
417 @implementation VLCCustomWindowZoomButton
418 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
419     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityZoomButtonAttribute : nil);
420 }
421
422 @end
423
424
425 @implementation VLCCustomWindowFullscreenButton
426 #ifdef MAC_OS_X_VERSION_10_7
427 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
428     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityFullScreenButtonAttribute : nil);
429 }
430 #endif
431
432 @end