]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindowTitle.m
macosx: fixed white spacing errors
[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     {
178         // set fs directly to true, as the vars can be already true in some configs
179         var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
180
181         vout_thread_t *p_vout = getVout();
182         if( p_vout )
183         {
184             var_SetBool( p_vout, "fullscreen", true );
185             vlc_object_release( p_vout );
186         }
187     }
188     else
189         msg_Err( VLCIntf, "unknown button action sender" );
190
191     [self setWindowButtonOver: NO];
192     [self setWindowFullscreenButtonOver: NO];
193 }
194
195 - (void)setWindowTitle:(NSString *)title
196 {
197     if (!o_window_title_shadow)
198     {
199         o_window_title_shadow = [[NSShadow alloc] init];
200         [o_window_title_shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
201         [o_window_title_shadow setShadowOffset:NSMakeSize(0.0, -1.5)];
202         [o_window_title_shadow setShadowBlurRadius:0.5];
203         [o_window_title_shadow retain];
204     }
205
206     NSMutableAttributedString *o_attributed_title = [[NSMutableAttributedString alloc] initWithString:title attributes: o_window_title_attributes_dict];
207     NSUInteger i_titleLength = [title length];
208
209     [o_attributed_title addAttribute:NSShadowAttributeName value:o_window_title_shadow range:NSMakeRange(0, i_titleLength)];
210     [o_attributed_title setAlignment: NSCenterTextAlignment range:NSMakeRange(0, i_titleLength)];
211     [o_title_lbl setAttributedStringValue:o_attributed_title];
212     [o_attributed_title release];
213 }
214
215 - (void)setFullscreenButtonHidden:(BOOL)b_value
216 {
217     [o_fullscreen_btn setHidden: b_value];
218 }
219
220 - (void)setWindowButtonOver:(BOOL)b_value
221 {
222     if( b_value )
223     {
224         [o_red_btn setImage: o_red_over_img];
225         [o_yellow_btn setImage: o_yellow_over_img];
226         [o_green_btn setImage: o_green_over_img];
227     }
228     else
229     {
230         [o_red_btn setImage: o_red_img];
231         [o_yellow_btn setImage: o_yellow_img];
232         [o_green_btn setImage: o_green_img];
233     }
234 }
235
236 - (void)setWindowFullscreenButtonOver:(BOOL)b_value
237 {
238     if (b_value)
239         [o_fullscreen_btn setImage: [NSImage imageNamed:@"window-fullscreen-over"]];
240     else
241         [o_fullscreen_btn setImage: [NSImage imageNamed:@"window-fullscreen"]];
242 }
243 @end
244
245 /*****************************************************************************
246  * VLCWindowButtonCell
247  *
248  * since the title bar cannot fetch these mouse events (the more top-level
249  * NSButton is unable fetch them as well), we are using a subclass of the
250  * button cell to do so. It's set in the nib for the respective objects.
251  *****************************************************************************/
252
253 @implementation VLCWindowButtonCell
254
255 - (void)mouseEntered:(NSEvent *)theEvent
256 {
257     if ([self tag] == 3)
258         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowFullscreenButtonOver: YES];
259     else
260         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowButtonOver: YES];
261 }
262
263 - (void)mouseExited:(NSEvent *)theEvent
264 {
265     if ([self tag] == 3)
266         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowFullscreenButtonOver: NO];
267     else
268         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowButtonOver: NO];
269 }
270
271 /* accessibility stuff */
272 - (NSArray*)accessibilityAttributeNames {
273     NSArray *theAttributeNames = [super accessibilityAttributeNames];
274     id theControlView = [self controlView];
275     return ([theControlView respondsToSelector: @selector(extendedAccessibilityAttributeNames:)] ? [theControlView extendedAccessibilityAttributeNames: theAttributeNames] : theAttributeNames); // ask the cell's control view (i.e., the button) for additional attribute values
276 }
277
278 - (id)accessibilityAttributeValue: (NSString*)theAttributeName {
279     id theControlView = [self controlView];
280     if ([theControlView respondsToSelector: @selector(extendedAccessibilityAttributeValue:)]) {
281         id theValue = [theControlView extendedAccessibilityAttributeValue: theAttributeName];
282         if (theValue) {
283             return theValue; // if this is an extended attribute value we added, return that -- otherwise, fall back to super's implementation
284         }
285     }
286     return [super accessibilityAttributeValue: theAttributeName];
287 }
288
289 - (BOOL)accessibilityIsAttributeSettable: (NSString*)theAttributeName {
290     id theControlView = [self controlView];
291     if ([theControlView respondsToSelector: @selector(extendedAccessibilityIsAttributeSettable:)]) {
292         NSNumber *theValue = [theControlView extendedAccessibilityIsAttributeSettable: theAttributeName];
293         if (theValue) {
294             return [theValue boolValue]; // same basic strategy we use in -accessibilityAttributeValue:
295         }
296     }
297     return [super accessibilityIsAttributeSettable: theAttributeName];
298 }
299
300 @end
301
302
303 /*****************************************************************************
304  * VLCResizeControl
305  *
306  * For Leopard and Snow Leopard, we need to emulate the resize control on the
307  * bottom right of the window, since it is gone by using the borderless window
308  * mask. A proper fix would be Lion-only.
309  *****************************************************************************/
310
311 @implementation VLCResizeControl
312
313 - (void)mouseDown:(NSEvent *)theEvent {
314     BOOL keepOn = YES;
315
316     while (keepOn) {
317         theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask |
318                     NSLeftMouseDraggedMask];
319
320         switch ([theEvent type]) {
321             case NSLeftMouseDragged:
322             {
323                 NSRect windowFrame = [[self window] frame];
324                 CGFloat deltaX, deltaY, oldOriginY;
325                 deltaX = [theEvent deltaX];
326                 deltaY = [theEvent deltaY];
327                 oldOriginY = windowFrame.origin.y;
328
329                 windowFrame.origin.y = (oldOriginY + windowFrame.size.height) - (windowFrame.size.height + deltaY);
330                 windowFrame.size.width += deltaX;
331                 windowFrame.size.height += deltaY;
332
333                 NSSize winMinSize = [self window].minSize;
334                 if (windowFrame.size.width < winMinSize.width)
335                     windowFrame.size.width = winMinSize.width;
336
337                 if (windowFrame.size.height < winMinSize.height)
338                 {
339                     windowFrame.size.height = winMinSize.height;
340                     windowFrame.origin.y = oldOriginY;
341                 }
342
343                 [[self window] setFrame: windowFrame display: YES animate: NO];
344                 break;
345             }
346                 break;
347             case NSLeftMouseUp:
348                 keepOn = NO;
349                 break;
350             default:
351                 /* Ignore any other kind of event. */
352                 break;
353         }
354
355     };
356
357     return;
358 }
359
360 @end
361
362 /*****************************************************************************
363  * VLCColorView
364  *
365  * since we are using a clear window color when using the black window
366  * style, some filling is needed behind the video and some other elements
367  *****************************************************************************/
368
369 @implementation VLCColorView
370
371 - (void)drawRect:(NSRect)rect {
372     [[NSColor blackColor] setFill];
373     NSRectFill(rect);
374 }
375
376 @end
377
378 /*****************************************************************************
379  * custom window buttons to support the accessibility stuff
380  *****************************************************************************/
381
382 @implementation VLCCustomWindowButtonPrototype
383 + (Class)cellClass {
384     return [VLCWindowButtonCell class];
385 }
386
387 - (NSArray*)extendedAccessibilityAttributeNames: (NSArray*)theAttributeNames {
388     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
389 }
390
391 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
392     return nil;
393 }
394
395 - (NSNumber*)extendedAccessibilityIsAttributeSettable: (NSString*)theAttributeName {
396     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? [NSNumber numberWithBool: NO] : nil); // make the Subrole attribute we added non-settable
397 }
398
399 - (void)accessibilityPerformAction: (NSString*)theActionName {
400     if ([theActionName isEqualToString: NSAccessibilityPressAction]) {
401         if ([self isEnabled]) {
402             [self performClick: nil];
403         }
404     } else {
405         [super accessibilityPerformAction: theActionName];
406     }
407 }
408
409 @end
410
411 @implementation VLCCustomWindowCloseButton
412 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
413     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityCloseButtonAttribute : nil);
414 }
415
416 @end
417
418
419 @implementation VLCCustomWindowMinimizeButton
420 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
421     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityMinimizeButtonAttribute : nil);
422 }
423
424 @end
425
426
427 @implementation VLCCustomWindowZoomButton
428 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
429     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityZoomButtonAttribute : nil);
430 }
431
432 @end
433
434
435 @implementation VLCCustomWindowFullscreenButton
436 #ifdef MAC_OS_X_VERSION_10_7
437 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
438     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityFullScreenButtonAttribute : nil);
439 }
440 #endif
441
442 @end