1 /*****************************************************************************
2 * MainWindow.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2012 VLC authors and VideoLAN
7 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8 * Jon Lech Johansen <jon-vl@nanocrew.net>
9 * Christophe Massiot <massiot@via.ecp.fr>
10 * Derk-Jan Hartman <hartman at videolan.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 #import "CompatibilityFixes.h"
28 #import "MainWindow.h"
30 #import "CoreInteraction.h"
31 #import "AudioEffects.h"
34 #import "controls.h" // TODO: remove me
36 #import "SideBarItem.h"
38 #import <vlc_playlist.h>
39 #import <vlc_aout_intf.h>
41 #import <vlc_strings.h>
42 #import <vlc_services_discovery.h>
43 #import <vlc_aout_intf.h>
45 @interface VLCMainWindow ()
46 - (void)addJumpButtons:(BOOL)b_fast;
47 - (void)removeJumpButtons:(BOOL)b_fast;
48 - (void)addPlaymodeButtons:(BOOL)b_fast;
49 - (void)removePlaymodeButtons:(BOOL)b_fast;
51 - (void)resetPreviousButton;
52 - (void)resetBackwardSkip;
53 - (void)resetNextButton;
54 - (void)resetForwardSkip;
55 - (void)resizePlaylistAfterCollapse;
56 - (void)makeSplitViewVisible;
57 - (void)makeSplitViewHidden;
59 - (NSRect)customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen;
62 @implementation VLCMainWindow
63 static const float f_min_video_height = 70.0;
65 static VLCMainWindow *_o_sharedInstance = nil;
67 + (VLCMainWindow *)sharedInstance
69 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
73 #pragma mark Initialization
77 if( _o_sharedInstance )
80 return _o_sharedInstance;
84 _o_sharedInstance = [super init];
87 return _o_sharedInstance;
90 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
91 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
93 b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
97 #ifdef MAC_OS_X_VERSION_10_7
99 styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
101 styleMask = NSBorderlessWindowMask;
103 styleMask = NSBorderlessWindowMask;
107 self = [super initWithContentRect:contentRect styleMask:styleMask
108 backing:backingType defer:flag];
109 _o_sharedInstance = self;
111 [[VLCMain sharedInstance] updateTogglePlaylistState];
113 /* we want to be moveable regardless of our style */
114 [self setMovableByWindowBackground: YES];
116 /* we don't want this window to be restored on relaunch */
118 [self setRestorable:NO];
123 - (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
128 key = config_GetPsz( VLCIntf, keyString );
129 o_key = [NSString stringWithFormat:@"%s", key];
132 unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
134 NSString * characters = [o_event charactersIgnoringModifiers];
135 if ([characters length] > 0) {
136 return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
137 (i_keyModifiers & NSShiftKeyMask) == ([o_event modifierFlags] & NSShiftKeyMask) &&
138 (i_keyModifiers & NSControlKeyMask) == ([o_event modifierFlags] & NSControlKeyMask) &&
139 (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
140 (i_keyModifiers & NSCommandKeyMask) == ([o_event modifierFlags] & NSCommandKeyMask);
145 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
148 // these are key events which should be handled by vlc core, but are attached to a main menu item
149 if( ![self isEvent: o_event forKey: "key-vol-up"] &&
150 ![self isEvent: o_event forKey: "key-vol-down"] &&
151 ![self isEvent: o_event forKey: "key-vol-mute"] )
153 /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
154 so we perform the menu equivalent now. */
155 if([[NSApp mainMenu] performKeyEquivalent:o_event])
161 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
162 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
167 if (b_dark_interface)
168 [o_color_backdrop release];
170 [[NSNotificationCenter defaultCenter] removeObserver: self];
171 [o_sidebaritems release];
173 if( o_extra_video_window )
175 [o_extra_video_window release];
176 o_extra_video_window = nil;
184 BOOL b_splitviewShouldBeHidden = NO;
186 /* setup the styled interface */
187 b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
188 b_nativeFullscreenMode = NO;
189 #ifdef MAC_OS_X_VERSION_10_7
190 if( OSX_LION && b_video_deco )
191 b_nativeFullscreenMode = var_InheritBool( VLCIntf, "macosx-nativefullscreenmode" );
193 t_hide_mouse_timer = nil;
194 [o_detached_video_window setDelegate: self];
195 [self useOptimizedDrawing: YES];
197 [o_play_btn setToolTip: _NS("Play/Pause")];
198 [[o_play_btn cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
199 [o_detached_play_btn setToolTip: [o_play_btn toolTip]];
200 [[o_play_btn cell] accessibilitySetOverrideValue:[o_play_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
201 [[o_detached_play_btn cell] accessibilitySetOverrideValue:[o_play_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
202 [[o_detached_play_btn cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
203 [o_bwd_btn setToolTip: _NS("Backward")];
204 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
205 [o_detached_bwd_btn setToolTip: [o_bwd_btn toolTip]];
206 [[o_bwd_btn cell] accessibilitySetOverrideValue:[o_bwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
207 [[o_detached_bwd_btn cell] accessibilitySetOverrideValue:[o_bwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
208 [[o_detached_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
209 [o_fwd_btn setToolTip: _NS("Forward")];
210 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
211 [o_detached_fwd_btn setToolTip: [o_fwd_btn toolTip]];
212 [[o_detached_fwd_btn cell] accessibilitySetOverrideValue:[o_fwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
213 [[o_detached_fwd_btn cell] accessibilitySetOverrideValue:[o_fwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
214 [[o_detached_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
215 [o_stop_btn setToolTip: _NS("Stop")];
216 [[o_stop_btn cell] accessibilitySetOverrideValue:_NS("Click to stop playback.") forAttribute:NSAccessibilityDescriptionAttribute];
217 [[o_stop_btn cell] accessibilitySetOverrideValue:[o_stop_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
218 [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
219 [[o_playlist_btn cell] accessibilitySetOverrideValue:_NS("Click to switch between video output and playlist. If no video is shown in the main window, this allows you to hide the playlist.") forAttribute:NSAccessibilityDescriptionAttribute];
220 [[o_playlist_btn cell] accessibilitySetOverrideValue:[o_playlist_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
221 [o_repeat_btn setToolTip: _NS("Repeat")];
222 [[o_repeat_btn cell] accessibilitySetOverrideValue:_NS("Click to change repeat mode. There are 3 states: repeat one, repeat all and off.") forAttribute:NSAccessibilityDescriptionAttribute];
223 [[o_repeat_btn cell] accessibilitySetOverrideValue:[o_repeat_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
224 [o_shuffle_btn setToolTip: _NS("Shuffle")];
225 [[o_shuffle_btn cell] accessibilitySetOverrideValue:[o_shuffle_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
226 [[o_shuffle_btn cell] accessibilitySetOverrideValue:_NS("Click to enable or disable random playback.") forAttribute:NSAccessibilityDescriptionAttribute];
227 [o_effects_btn setToolTip: _NS("Effects")];
228 [[o_effects_btn cell] accessibilitySetOverrideValue:_NS("Click to show an Audio Effects panel featuring an equalizer and further filters.") forAttribute:NSAccessibilityDescriptionAttribute];
229 [[o_effects_btn cell] accessibilitySetOverrideValue:[o_effects_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
230 [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
231 [[o_fullscreen_btn cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
232 [o_detached_fullscreen_btn setToolTip: [o_fullscreen_btn toolTip]];
233 [[o_fullscreen_btn cell] accessibilitySetOverrideValue:[o_fullscreen_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
234 [[o_detached_fullscreen_btn cell] accessibilitySetOverrideValue:[o_fullscreen_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
235 [[o_detached_fullscreen_btn cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
236 [[o_search_fld cell] setPlaceholderString: _NS("Search")];
237 [[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
238 [o_volume_sld setToolTip: _NS("Volume")];
239 [[o_volume_sld cell] accessibilitySetOverrideValue:_NS("Click and move the mouse while keeping the button pressed to use this slider to change the volume.") forAttribute:NSAccessibilityDescriptionAttribute];
240 [[o_volume_sld cell] accessibilitySetOverrideValue:[o_volume_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
241 [o_volume_down_btn setToolTip: _NS("Mute")];
242 [[o_volume_down_btn cell] accessibilitySetOverrideValue:_NS("Click to mute or unmute the audio.") forAttribute:NSAccessibilityDescriptionAttribute];
243 [[o_volume_down_btn cell] accessibilitySetOverrideValue:[o_volume_down_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
244 [o_volume_up_btn setToolTip: _NS("Full Volume")];
245 [[o_volume_up_btn cell] accessibilitySetOverrideValue:_NS("Click to play the audio at maximum volume.") forAttribute:NSAccessibilityDescriptionAttribute];
246 [[o_volume_up_btn cell] accessibilitySetOverrideValue:[o_volume_up_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
247 [o_time_sld setToolTip: _NS("Position")];
248 [[o_time_sld cell] accessibilitySetOverrideValue:_NS("Click and move the mouse while keeping the button pressed to use this slider to change current playback position.") forAttribute:NSAccessibilityDescriptionAttribute];
249 [[o_time_sld cell] accessibilitySetOverrideValue:[o_time_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
250 [o_detached_time_sld setToolTip: [o_time_sld toolTip]];
251 [[o_detached_time_sld cell] accessibilitySetOverrideValue:_NS("Click and move the mouse while keeping the button pressed to use this slider to change current playback position.") forAttribute:NSAccessibilityDescriptionAttribute];
252 [[o_detached_time_sld cell] accessibilitySetOverrideValue:[o_time_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
253 [o_dropzone_btn setTitle: _NS("Open media...")];
254 [[o_dropzone_btn cell] accessibilitySetOverrideValue:_NS("Click to open an advanced dialog to select the media to play. You can also drop files here to play.") forAttribute:NSAccessibilityDescriptionAttribute];
255 [o_dropzone_lbl setStringValue: _NS("Drop media here")];
257 [o_podcast_add_btn setTitle: _NS("Subscribe")];
258 [o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
259 [o_podcast_subscribe_title_lbl setStringValue: _NS("Subscribe to a podcast")];
260 [o_podcast_subscribe_subtitle_lbl setStringValue: _NS("Enter URL of the podcast to subscribe to:")];
261 [o_podcast_subscribe_cancel_btn setTitle: _NS("Cancel")];
262 [o_podcast_subscribe_ok_btn setTitle: _NS("Subscribe")];
263 [o_podcast_unsubscribe_title_lbl setStringValue: _NS("Unsubscribe from a podcast")];
264 [o_podcast_unsubscribe_subtitle_lbl setStringValue: _NS("Select the podcast you would like to unsubscribe from:")];
265 [o_podcast_unsubscribe_ok_btn setTitle: _NS("Unsubscribe")];
266 [o_podcast_unsubscribe_cancel_btn setTitle: _NS("Cancel")];
268 if (!b_dark_interface) {
269 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
270 [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
271 [o_bwd_btn setImage: [NSImage imageNamed:@"back"]];
272 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
273 [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back"]];
274 [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
275 o_play_img = [[NSImage imageNamed:@"play"] retain];
276 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
277 o_pause_img = [[NSImage imageNamed:@"pause"] retain];
278 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
279 [o_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
280 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
281 [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
282 [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
283 [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
284 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
285 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
286 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
287 o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
288 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
289 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all"] retain];
290 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
291 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
292 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
293 o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
294 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
295 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
296 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
297 [o_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle"] right: [NSImage imageNamed:@"progression-track-wrapper-right"]];
298 [o_detached_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle"] right: [NSImage imageNamed:@"progression-track-wrapper-right"]];
299 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
300 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
301 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
302 if (b_nativeFullscreenMode)
304 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
305 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue"]];
309 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
310 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
312 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
313 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
314 [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
315 [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
316 [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
317 [o_detached_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
321 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
322 [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
323 [o_bwd_btn setImage: [NSImage imageNamed:@"back-3btns-dark"]];
324 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-3btns-dark-pressed"]];
325 [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back-3btns-dark"]];
326 [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-3btns-dark-pressed"]];
327 o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
328 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
329 o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
330 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
331 [o_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns-dark"]];
332 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-dark-pressed"]];
333 [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns-dark"]];
334 [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-dark-pressed"]];
335 [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
336 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
337 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
338 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
339 o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
340 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
341 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
342 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
343 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
344 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
345 o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
346 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
347 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
348 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
349 [o_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left_dark"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle_dark"] right: [NSImage imageNamed:@"progression-track-wrapper-right_dark"]];
350 [o_detached_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left_dark"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle_dark"] right: [NSImage imageNamed:@"progression-track-wrapper-right_dark"]];
351 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
352 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
353 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
354 if (b_nativeFullscreenMode)
356 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
357 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue_dark"]];
361 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
362 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
364 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
365 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
366 [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button_dark"]];
367 [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
368 [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progressbar-fill-left_dark"] middle:[NSImage imageNamed:@"progressbar-fill-middle_dark"] right:[NSImage imageNamed:@"progressbar-fill-right_dark"]];
369 [o_detached_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progressbar-fill-left_dark"] middle:[NSImage imageNamed:@"progressbar-fill-middle_dark"] right:[NSImage imageNamed:@"progressbar-fill-right_dark"]];
371 [o_repeat_btn setImage: o_repeat_img];
372 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
373 [o_shuffle_btn setImage: o_shuffle_img];
374 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
375 [o_play_btn setImage: o_play_img];
376 [o_play_btn setAlternateImage: o_play_pressed_img];
377 [o_detached_play_btn setImage: o_play_img];
378 [o_detached_play_btn setAlternateImage: o_play_pressed_img];
379 BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
380 [o_volume_sld setEnabled: b_mute];
381 [o_volume_up_btn setEnabled: b_mute];
383 b_show_jump_buttons = config_GetInt( VLCIntf, "macosx-show-playback-buttons" );
384 if (b_show_jump_buttons)
385 [self addJumpButtons:YES];
387 b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
388 if (!b_show_playmode_buttons)
389 [self removePlaymodeButtons:YES];
391 /* interface builder action */
392 float f_threshold_height = f_min_video_height + [o_bottombar_view frame].size.height;
393 if( b_dark_interface )
394 f_threshold_height += [o_titlebar_view frame].size.height;
395 if( [[self contentView] frame].size.height < f_threshold_height )
396 b_splitviewShouldBeHidden = YES;
398 [self setDelegate: self];
399 [self setExcludedFromWindowsMenu: YES];
400 [self setAcceptsMouseMovedEvents: YES];
401 // Set that here as IB seems to be buggy
402 if (b_dark_interface && b_video_deco)
404 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
405 [o_detached_video_window setContentMinSize: NSMakeSize( 363., f_min_video_height + [o_detached_bottombar_view frame].size.height + [o_detached_titlebar_view frame].size.height )];
407 else if( b_video_deco )
409 [self setContentMinSize:NSMakeSize(604., 288.)];
410 [o_detached_video_window setContentMinSize: NSMakeSize( 363., f_min_video_height + [o_detached_bottombar_view frame].size.height )];
414 if (b_dark_interface)
415 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
417 [self setContentMinSize:NSMakeSize(604., 288.)];
419 [o_detached_bottombar_view setHidden:YES];
420 [o_detached_video_window setContentMinSize: NSMakeSize( f_min_video_height, f_min_video_height )];
423 [self setTitle: _NS("VLC media player")];
424 [o_time_fld setAlignment: NSCenterTextAlignment];
425 [o_time_fld setNeedsDisplay:YES];
426 b_dropzone_active = YES;
427 o_temp_view = [[NSView alloc] init];
428 [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
429 [o_dropzone_view setFrame: [o_playlist_table frame]];
430 [o_left_split_view setFrame: [o_sidebar_view frame]];
431 if (b_nativeFullscreenMode)
434 [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
435 float f_width = [o_fullscreen_btn frame].size.width;
437 #define moveItem( item ) \
438 frame = [item frame]; \
439 frame.origin.x = f_width + frame.origin.x; \
440 [item setFrame: frame]
442 moveItem( o_effects_btn );
443 moveItem( o_volume_up_btn );
444 moveItem( o_volume_sld );
445 moveItem( o_volume_track_view );
446 moveItem( o_volume_down_btn );
447 moveItem( o_time_fld );
450 #define enlargeItem( item ) \
451 frame = [item frame]; \
452 frame.size.width = f_width + frame.size.width; \
453 [item setFrame: frame]
455 enlargeItem( o_time_sld );
456 enlargeItem( o_progress_bar );
457 enlargeItem( o_time_sld_background );
458 enlargeItem( o_time_sld_fancygradient_view );
461 [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
465 [o_titlebar_view setFullscreenButtonHidden: YES];
467 [o_detached_titlebar_view setFullscreenButtonHidden: YES];
472 /* the default small size of the search field is slightly different on Lion, let's work-around that */
474 frame = [o_search_fld frame];
475 frame.origin.y = frame.origin.y + 2.0;
476 frame.size.height = frame.size.height - 1.0;
477 [o_search_fld setFrame: frame];
480 /* create the sidebar */
481 o_sidebaritems = [[NSMutableArray alloc] init];
482 SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
483 SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
484 [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
485 SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
486 [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
487 SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
488 SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
489 SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
490 SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
492 /* SD subnodes, inspired by the Qt4 intf */
493 char **ppsz_longnames;
495 char **ppsz_names = vlc_sd_GetNames( pl_Get( VLCIntf ), &ppsz_longnames, &p_categories );
497 msg_Err( VLCIntf, "no sd item found" ); //TODO
498 char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
499 int *p_category = p_categories;
500 NSMutableArray *internetItems = [[NSMutableArray alloc] init];
501 NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
502 NSMutableArray *lanItems = [[NSMutableArray alloc] init];
503 NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
504 NSString *o_identifier;
505 for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++)
507 o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
508 switch (*p_category) {
509 case SD_CAT_INTERNET:
511 [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
512 if (!strncmp( *ppsz_name, "podcast", 7 ))
513 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
515 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
516 [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
517 [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
522 [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
523 [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
524 [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
525 [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
530 [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
531 [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
532 [[lanItems lastObject] setSdtype: SD_CAT_LAN];
533 [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
536 case SD_CAT_MYCOMPUTER:
538 [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
539 if (!strncmp( *ppsz_name, "video_dir", 9 ))
540 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
541 else if (!strncmp( *ppsz_name, "audio_dir", 9 ))
542 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
543 else if (!strncmp( *ppsz_name, "picture_dir", 11 ))
544 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
546 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
547 [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
548 [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
552 msg_Warn( VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name );
557 free( *ppsz_longname );
559 [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
560 [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
561 [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
562 [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
563 [mycompItems release];
564 [devicesItems release];
566 [internetItems release];
568 free( ppsz_longnames );
569 free( p_categories );
571 [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
572 [o_sidebaritems addObject: libraryItem];
573 if ([mycompItem hasChildren])
574 [o_sidebaritems addObject: mycompItem];
575 if ([devicesItem hasChildren])
576 [o_sidebaritems addObject: devicesItem];
577 if ([lanItem hasChildren])
578 [o_sidebaritems addObject: lanItem];
579 if ([internetItem hasChildren])
580 [o_sidebaritems addObject: internetItem];
582 [o_sidebar_view reloadData];
583 [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
584 [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
585 [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
587 [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
588 [(PXSourceList *)o_sidebar_view setDataSource:self];
589 [o_sidebar_view setDelegate:self];
590 [o_sidebar_view setAutosaveExpandedItems:YES];
592 [o_sidebar_view expandItem: libraryItem expandChildren: YES];
594 /* make sure we display the desired default appearance when VLC launches for the first time */
595 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
596 if (![defaults objectForKey:@"VLCFirstRun"])
598 [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
600 NSUInteger i_sidebaritem_count = [o_sidebaritems count];
601 for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
602 [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
605 if( b_dark_interface )
607 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
608 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
610 [self setBackgroundColor: [NSColor clearColor]];
611 [self setOpaque: NO];
613 [self setHasShadow:NO];
614 [self setHasShadow:YES];
616 NSRect winrect = [self frame];
617 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
619 [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
620 winrect.size.width, f_titleBarHeight )];
621 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
623 if (winrect.size.height > 100)
625 [self setFrame: winrect display:YES animate:YES];
626 previousSavedFrame = winrect;
629 winrect = [o_split_view frame];
630 winrect.size.height = winrect.size.height - f_titleBarHeight;
631 [o_split_view setFrame: winrect];
632 [o_video_view setFrame: winrect];
634 /* detached video window */
635 winrect = [o_detached_video_window frame];
638 [o_detached_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight )];
639 [[o_detached_video_window contentView] addSubview: o_detached_titlebar_view positioned: NSWindowAbove relativeTo: nil];
642 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
643 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
644 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
649 frame = [o_time_sld_fancygradient_view frame];
650 frame.size.height = frame.size.height - 1;
651 frame.origin.y = frame.origin.y + 1;
652 [o_time_sld_fancygradient_view setFrame: frame];
654 frame = [o_detached_time_sld_fancygradient_view frame];
655 frame.size.height = frame.size.height - 1;
656 frame.origin.y = frame.origin.y + 1;
657 [o_detached_time_sld_fancygradient_view setFrame: frame];
659 [o_video_view setFrame: [o_split_view frame]];
660 [o_playlist_table setBorderType: NSNoBorder];
661 [o_sidebar_scrollview setBorderType: NSNoBorder];
665 frame = [o_time_sld_fancygradient_view frame];
666 frame.size.width = 0;
667 [o_time_sld_fancygradient_view setFrame: frame];
669 frame = [o_detached_time_sld_fancygradient_view frame];
670 frame.size.width = 0;
671 [o_detached_time_sld_fancygradient_view setFrame: frame];
675 [o_resize_view setImage: NULL];
676 [o_detached_resize_view setImage: NULL];
679 if ([self styleMask] & NSResizableWindowMask)
681 [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
682 [o_detached_resize_view removeFromSuperviewWithoutNeedingDisplay];
685 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
686 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
687 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
688 [[VLCMain sharedInstance] playbackModeUpdated];
690 [o_split_view setAutosaveName:@"10thanniversary-splitview"];
691 if (b_splitviewShouldBeHidden)
693 [self hideSplitView];
694 i_lastSplitViewHeight = 300;
697 /* sanity check for the window size */
698 frame = [self frame];
699 NSSize screenSize = [[self screen] frame].size;
700 if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
701 nativeVideoSize = screenSize;
707 #pragma mark interface customization
708 - (void)toggleJumpButtons
710 b_show_jump_buttons = config_GetInt( VLCIntf, "macosx-show-playback-buttons" );
712 if (b_show_jump_buttons)
713 [self addJumpButtons:NO];
715 [self removeJumpButtons:NO];
718 - (void)addJumpButtons:(BOOL)b_fast
720 NSRect preliminaryFrame = [o_bwd_btn frame];
721 BOOL b_enabled = [o_bwd_btn isEnabled];
722 preliminaryFrame.size.width = 26.;
723 o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
724 [o_prev_btn setButtonType: NSMomentaryChangeButton];
725 [o_prev_btn setImage: [NSImage imageNamed:@"back-single"]];
726 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"back-pressed-single"]];
727 [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
728 [o_prev_btn setBordered:NO];
729 [o_prev_btn setTarget:self];
730 [o_prev_btn setAction:@selector(prev:)];
731 [o_prev_btn setToolTip: _NS("Previous")];
732 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Previous") forAttribute:NSAccessibilityTitleAttribute];
733 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
734 [o_prev_btn setEnabled: b_enabled];
736 o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
737 [o_next_btn setButtonType: NSMomentaryChangeButton];
738 [o_next_btn setImage: [NSImage imageNamed:@"forward-single"]];
739 [o_next_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed-single"]];
740 [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
741 [o_next_btn setBordered:NO];
742 [o_next_btn setTarget:self];
743 [o_next_btn setAction:@selector(next:)];
744 [o_next_btn setToolTip: _NS("Next")];
745 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Next") forAttribute:NSAccessibilityTitleAttribute];
746 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
747 [o_next_btn setEnabled: b_enabled];
749 /* change the accessibility help for the backward/forward buttons accordingly */
750 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
751 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
755 #define moveItem( item ) \
756 frame = [item frame]; \
757 frame.origin.x = frame.origin.x + f_space; \
759 [item setFrame: frame]; \
761 [[item animator] setFrame: frame]
763 moveItem( o_bwd_btn );
764 moveItem( o_play_btn );
765 moveItem( o_fwd_btn );
767 moveItem( o_stop_btn );
768 moveItem( o_playlist_btn );
769 moveItem( o_repeat_btn );
770 moveItem( o_shuffle_btn );
773 #define resizeItem( item ) \
774 frame = [item frame]; \
775 frame.size.width = frame.size.width - f_space; \
776 frame.origin.x = frame.origin.x + f_space; \
778 [item setFrame: frame]; \
780 [[item animator] setFrame: frame]
782 resizeItem( o_time_sld );
783 resizeItem( o_progress_bar );
784 resizeItem( o_time_sld_background );
785 resizeItem( o_time_sld_fancygradient_view );
788 preliminaryFrame.origin.x = [o_next_btn frame].origin.x + 80. + [o_fwd_btn frame].size.width;
789 [o_next_btn setFrame: preliminaryFrame];
791 // wait until the animation is done, if displayed
793 [[self contentView] addSubview:o_prev_btn];
794 [[self contentView] addSubview:o_next_btn];
796 [[self contentView] performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
797 [[self contentView] performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
800 [o_fwd_btn setAction:@selector(forward:)];
801 [o_bwd_btn setAction:@selector(backward:)];
804 - (void)removeJumpButtons:(BOOL)b_fast
806 if (!o_prev_btn || !o_next_btn )
810 [o_prev_btn setHidden: YES];
811 [o_next_btn setHidden: YES];
813 [[o_prev_btn animator] setHidden: YES];
814 [[o_next_btn animator] setHidden: YES];
816 [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
817 [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
818 [o_prev_btn release];
819 [o_next_btn release];
821 /* change the accessibility help for the backward/forward buttons accordingly */
822 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
823 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
827 #define moveItem( item ) \
828 frame = [item frame]; \
829 frame.origin.x = frame.origin.x - f_space; \
831 [item setFrame: frame]; \
833 [[item animator] setFrame: frame]
835 moveItem( o_bwd_btn );
836 moveItem( o_play_btn );
837 moveItem( o_fwd_btn );
839 moveItem( o_stop_btn );
840 moveItem( o_playlist_btn );
841 moveItem( o_repeat_btn );
842 moveItem( o_shuffle_btn );
845 #define resizeItem( item ) \
846 frame = [item frame]; \
847 frame.size.width = frame.size.width + f_space; \
848 frame.origin.x = frame.origin.x - f_space; \
850 [item setFrame: frame]; \
852 [[item animator] setFrame: frame]
854 resizeItem( o_time_sld );
855 resizeItem( o_progress_bar );
856 resizeItem( o_time_sld_background );
857 resizeItem( o_time_sld_fancygradient_view );
860 [o_bottombar_view setNeedsDisplay:YES];
862 [o_fwd_btn setAction:@selector(fwd:)];
863 [o_bwd_btn setAction:@selector(bwd:)];
866 - (void)togglePlaymodeButtons
868 b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
870 if (b_show_playmode_buttons)
871 [self addPlaymodeButtons:NO];
873 [self removePlaymodeButtons:NO];
876 - (void)addPlaymodeButtons:(BOOL)b_fast
879 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
881 // FIXME: switch o_playlist_btn artwork
883 #define resizeItem( item ) \
884 frame = [item frame]; \
885 frame.size.width = frame.size.width - f_space; \
886 frame.origin.x = frame.origin.x + f_space; \
888 [item setFrame: frame]; \
890 [[item animator] setFrame: frame]
892 resizeItem( o_time_sld );
893 resizeItem( o_progress_bar );
894 resizeItem( o_time_sld_background );
895 resizeItem( o_time_sld_fancygradient_view );
899 [o_repeat_btn setHidden: NO];
900 [o_shuffle_btn setHidden: NO];
902 [[o_repeat_btn animator] setHidden: NO];
903 [[o_shuffle_btn animator] setHidden: NO];
907 - (void)removePlaymodeButtons:(BOOL)b_fast
910 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
911 [o_repeat_btn setHidden: YES];
912 [o_shuffle_btn setHidden: YES];
914 // FIXME: switch o_playlist_btn artwork
916 #define resizeItem( item ) \
917 frame = [item frame]; \
918 frame.size.width = frame.size.width + f_space; \
919 frame.origin.x = frame.origin.x - f_space; \
921 [item setFrame: frame]; \
923 [[item animator] setFrame: frame]
925 resizeItem( o_time_sld );
926 resizeItem( o_progress_bar );
927 resizeItem( o_time_sld_background );
928 resizeItem( o_time_sld_fancygradient_view );
933 #pragma mark Button Actions
935 - (IBAction)play:(id)sender
937 [[VLCCoreInteraction sharedInstance] play];
940 - (void)resetPreviousButton
942 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
943 // seems like no further event occurred, so let's switch the playback item
944 [[VLCCoreInteraction sharedInstance] previous];
945 just_triggered_previous = NO;
949 - (void)resetBackwardSkip
951 // the user stopped skipping, so let's allow him to change the item
952 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
953 just_triggered_previous = NO;
956 - (IBAction)prev:(id)sender
958 [[VLCCoreInteraction sharedInstance] previous];
961 - (IBAction)bwd:(id)sender
963 if(!just_triggered_previous)
965 just_triggered_previous = YES;
966 [self performSelector:@selector(resetPreviousButton)
972 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
974 // we just skipped 4 "continous" events, otherwise we are too fast
975 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
976 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
977 [self performSelector:@selector(resetBackwardSkip)
984 - (IBAction)backward:(id)sender
986 [[VLCCoreInteraction sharedInstance] backwardShort];
989 - (void)resetNextButton
991 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
992 // seems like no further event occurred, so let's switch the playback item
993 [[VLCCoreInteraction sharedInstance] next];
994 just_triggered_next = NO;
998 - (void)resetForwardSkip
1000 // the user stopped skipping, so let's allow him to change the item
1001 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
1002 just_triggered_next = NO;
1005 - (IBAction)next:(id)sender
1007 [[VLCCoreInteraction sharedInstance] next];
1010 - (IBAction)forward:(id)sender
1012 [[VLCCoreInteraction sharedInstance] forwardShort];
1015 - (IBAction)fwd:(id)sender
1017 if(!just_triggered_next)
1019 just_triggered_next = YES;
1020 [self performSelector:@selector(resetNextButton)
1026 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
1028 // we just skipped 4 "continous" events, otherwise we are too fast
1029 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
1030 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
1031 [self performSelector:@selector(resetForwardSkip)
1038 - (IBAction)stop:(id)sender
1040 [[VLCCoreInteraction sharedInstance] stop];
1043 - (void)resizePlaylistAfterCollapse
1046 plrect = [o_playlist_table frame];
1047 plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
1048 [[o_playlist_table animator] setFrame: plrect];
1050 NSRect rightSplitRect;
1051 rightSplitRect = [o_right_split_view frame];
1052 plrect = [o_dropzone_box frame];
1053 plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
1054 plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
1055 [[o_dropzone_box animator] setFrame: plrect];
1058 - (void)makeSplitViewVisible
1060 if( b_dark_interface )
1061 [self setContentMinSize: NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1063 [self setContentMinSize: NSMakeSize( 604., 288. )];
1065 NSRect old_frame = [self frame];
1066 float newHeight = [self minSize].height;
1067 if( old_frame.size.height < newHeight )
1069 NSRect new_frame = old_frame;
1070 new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
1071 new_frame.size.height = newHeight;
1073 [[self animator] setFrame: new_frame display: YES animate: YES];
1076 [o_video_view setHidden: YES];
1077 [o_split_view setHidden: NO];
1078 [self makeFirstResponder: nil];
1082 - (void)makeSplitViewHidden
1084 if( b_dark_interface )
1085 [self setContentMinSize: NSMakeSize( 604., f_min_video_height + [o_titlebar_view frame].size.height )];
1087 [self setContentMinSize: NSMakeSize( 604., f_min_video_height )];
1089 [o_split_view setHidden: YES];
1090 [o_video_view setHidden: NO];
1092 if( [[o_video_view subviews] count] > 0 )
1093 [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1096 - (IBAction)togglePlaylist:(id)sender
1098 if (![self isVisible] && sender != nil)
1100 [self makeKeyAndOrderFront: sender];
1104 BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
1105 BOOL b_restored = NO;
1107 // TODO: implement toggle playlist in this situation (triggerd via menu item).
1108 // but for now we block this case, to avoid displaying only the half
1109 if( b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil )
1112 if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0)
1114 [self hideDropZone];
1118 if ( !(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ( (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
1119 || (b_nonembedded && sender != nil)
1120 || (!b_activeVideo && sender != nil)
1121 || b_minimized_view ) )
1123 [self hideSplitView];
1127 if (b_splitview_removed)
1129 if( !b_nonembedded || ( sender != nil && b_nonembedded))
1130 [self showSplitView];
1133 b_minimized_view = YES;
1135 b_minimized_view = NO;
1143 if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil) )
1144 [self makeSplitViewHidden];
1146 [self makeSplitViewVisible];
1150 [o_split_view setHidden: NO];
1151 [o_playlist_table setHidden: NO];
1152 [o_video_view setHidden: !b_activeVideo];
1153 if( b_activeVideo && [[o_video_view subviews] count] > 0 )
1154 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1159 - (void)setRepeatOne
1161 [o_repeat_btn setImage: o_repeat_one_img];
1162 [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1165 - (void)setRepeatAll
1167 [o_repeat_btn setImage: o_repeat_all_img];
1168 [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1171 - (void)setRepeatOff
1173 [o_repeat_btn setImage: o_repeat_img];
1174 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1177 - (IBAction)repeat:(id)sender
1179 vlc_value_t looping,repeating;
1180 intf_thread_t * p_intf = VLCIntf;
1181 playlist_t * p_playlist = pl_Get( p_intf );
1183 var_Get( p_playlist, "repeat", &repeating );
1184 var_Get( p_playlist, "loop", &looping );
1186 if( !repeating.b_bool && !looping.b_bool )
1188 /* was: no repeating at all, switching to Repeat One */
1189 [[VLCCoreInteraction sharedInstance] repeatOne];
1190 [self setRepeatOne];
1192 else if( repeating.b_bool && !looping.b_bool )
1194 /* was: Repeat One, switching to Repeat All */
1195 [[VLCCoreInteraction sharedInstance] repeatAll];
1196 [self setRepeatAll];
1200 /* was: Repeat All or bug in VLC, switching to Repeat Off */
1201 [[VLCCoreInteraction sharedInstance] repeatOff];
1202 [self setRepeatOff];
1209 playlist_t *p_playlist = pl_Get( VLCIntf );
1210 b_value = var_GetBool( p_playlist, "random" );
1213 [o_shuffle_btn setImage: o_shuffle_on_img];
1214 [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1218 [o_shuffle_btn setImage: o_shuffle_img];
1219 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1223 - (IBAction)shuffle:(id)sender
1225 [[VLCCoreInteraction sharedInstance] shuffle];
1229 - (IBAction)timeSliderAction:(id)sender
1232 input_thread_t * p_input;
1234 switch( [[NSApp currentEvent] type] )
1237 case NSLeftMouseDown:
1238 case NSLeftMouseDragged:
1239 f_updated = [sender floatValue];
1245 p_input = pl_CurrentInput( VLCIntf );
1246 if( p_input != NULL )
1251 pos.f_float = f_updated / 10000.;
1252 var_Set( p_input, "position", pos );
1253 [o_time_sld setFloatValue: f_updated];
1255 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1256 [o_time_fld setStringValue: o_time];
1257 [o_fspanel setStreamPos: f_updated andTime: o_time];
1258 vlc_object_release( p_input );
1262 - (IBAction)volumeAction:(id)sender
1264 if (sender == o_volume_sld)
1265 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1266 else if (sender == o_volume_down_btn)
1267 [[VLCCoreInteraction sharedInstance] toggleMute];
1269 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1272 - (IBAction)effects:(id)sender
1274 [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1277 - (IBAction)fullscreen:(id)sender
1279 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1282 - (IBAction)dropzoneButtonAction:(id)sender
1284 [[[VLCMain sharedInstance] open] openFileGeneric];
1288 #pragma mark overwritten default functionality
1289 - (BOOL)canBecomeKeyWindow
1294 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
1296 SEL s_menuAction = [menuItem action];
1298 if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
1301 return [super validateMenuItem:menuItem];
1304 - (void)setTitle:(NSString *)title
1306 if (b_dark_interface)
1308 [o_titlebar_view setWindowTitle: title];
1310 [o_detached_titlebar_view setWindowTitle: title];
1312 if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
1313 [o_detached_video_window setTitle: title];
1314 [super setTitle: title];
1317 - (void)performClose:(id)sender
1319 NSWindow *o_key_window = [NSApp keyWindow];
1321 if (b_dark_interface || !b_video_deco)
1323 [o_key_window orderOut: sender];
1324 if ( [[VLCMain sharedInstance] activeVideoPlayback] && ( !b_nonembedded || o_key_window != self ))
1325 [[VLCCoreInteraction sharedInstance] stop];
1329 if( b_nonembedded && o_key_window != self )
1330 [o_detached_video_window performClose: sender];
1332 [super performClose: sender];
1336 - (void)performMiniaturize:(id)sender
1338 if (b_dark_interface)
1339 [self miniaturize: sender];
1341 [super performMiniaturize: sender];
1344 - (void)performZoom:(id)sender
1346 if (b_dark_interface)
1347 [self customZoom: sender];
1349 [super performZoom: sender];
1352 - (void)zoom:(id)sender
1354 if (b_dark_interface)
1355 [self customZoom: sender];
1357 [super zoom: sender];
1361 * Given a proposed frame rectangle, return a modified version
1362 * which will fit inside the screen.
1364 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1365 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1366 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1367 * Copyright (C) 1996 Free Software Foundation, Inc.
1369 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
1371 NSRect screenRect = [screen visibleFrame];
1374 /* Move top edge of the window inside the screen */
1375 difference = NSMaxY (frameRect) - NSMaxY (screenRect);
1378 frameRect.origin.y -= difference;
1381 /* If the window is resizable, resize it (if needed) so that the
1382 bottom edge is on the screen or can be on the screen when the user moves
1384 difference = NSMaxY (screenRect) - NSMaxY (frameRect);
1385 if (_styleMask & NSResizableWindowMask)
1389 difference2 = screenRect.origin.y - frameRect.origin.y;
1390 difference2 -= difference;
1391 // Take in account the space between the top of window and the top of the
1392 // screen which can be used to move the bottom of the window on the screen
1393 if (difference2 > 0)
1395 frameRect.size.height -= difference2;
1396 frameRect.origin.y += difference2;
1399 /* Ensure that resizing doesn't makewindow smaller than minimum */
1400 difference2 = [self minSize].height - frameRect.size.height;
1401 if (difference2 > 0)
1403 frameRect.size.height += difference2;
1404 frameRect.origin.y -= difference2;
1414 Zooms the receiver. This method calls the delegate method
1415 windowShouldZoom:toFrame: to determine if the window should
1416 be allowed to zoom to full screen.
1418 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1419 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1420 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1421 * Copyright (C) 1996 Free Software Foundation, Inc.
1423 - (void)customZoom:(id)sender
1425 NSRect maxRect = [[self screen] visibleFrame];
1426 NSRect currentFrame = [self frame];
1428 if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
1430 maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
1433 maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
1435 // Compare the new frame with the current one
1436 if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
1437 && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
1438 && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
1439 && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
1441 // Already in zoomed mode, reset user frame, if stored
1442 if ([self frameAutosaveName] != nil)
1444 [self setFrame: previousSavedFrame display: YES animate: YES];
1445 [self saveFrameUsingName: [self frameAutosaveName]];
1450 if ([self frameAutosaveName] != nil)
1452 [self saveFrameUsingName: [self frameAutosaveName]];
1453 previousSavedFrame = [self frame];
1456 [self setFrame: maxRect display: YES animate: YES];
1459 - (void)windowResizedOrMoved:(NSNotification *)notification
1461 [self saveFrameUsingName: [self frameAutosaveName]];
1464 - (void)applicationWillTerminate:(NSNotification *)notification
1466 [self saveFrameUsingName: [self frameAutosaveName]];
1469 - (void)someWindowWillClose:(NSNotification *)notification
1471 if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1473 if ([[VLCMain sharedInstance] activeVideoPlayback])
1474 [[VLCCoreInteraction sharedInstance] stop];
1478 - (void)someWindowWillMiniaturize:(NSNotification *)notification
1480 if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
1482 if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1484 if([[VLCMain sharedInstance] activeVideoPlayback])
1485 [[VLCCoreInteraction sharedInstance] pause];
1490 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
1492 id videoWindow = [o_video_view window];
1493 if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
1494 return proposedFrameSize;
1496 // needed when entering lion fullscreen mode
1498 return proposedFrameSize;
1500 if( [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked] )
1502 NSRect videoWindowFrame = [videoWindow frame];
1503 NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
1504 NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
1505 float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
1506 float marginx = contentRect.size.width - viewRect.size.width;
1507 if( b_dark_interface && b_video_deco )
1508 marginy += [o_titlebar_view frame].size.height;
1510 proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
1513 return proposedFrameSize;
1517 #pragma mark Update interface and respond to foreign events
1518 - (void)showDropZone
1520 b_dropzone_active = YES;
1521 [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
1522 [o_dropzone_view setFrame: [o_playlist_table frame]];
1523 [[o_playlist_table animator] setHidden:YES];
1526 - (void)hideDropZone
1528 b_dropzone_active = NO;
1529 [o_dropzone_view removeFromSuperview];
1530 [[o_playlist_table animator] setHidden: NO];
1533 - (void)hideSplitView
1535 NSRect winrect = [self frame];
1536 i_lastSplitViewHeight = [o_split_view frame].size.height;
1537 winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
1538 winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
1539 [self setFrame: winrect display: YES animate: YES];
1540 [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
1541 if (b_dark_interface)
1543 [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1544 [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1548 [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height )];
1549 [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height )];
1552 b_splitview_removed = YES;
1555 - (void)showSplitView
1557 [self updateWindow];
1558 if (b_dark_interface)
1559 [self setContentMinSize:NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1561 [self setContentMinSize:NSMakeSize( 604., 288. )];
1562 [self setContentMaxSize: NSMakeSize( FLT_MAX, FLT_MAX )];
1565 winrect = [self frame];
1566 winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
1567 winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
1568 [self setFrame: winrect display: YES animate: YES];
1570 [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
1572 b_splitview_removed = NO;
1575 - (void)updateTimeSlider
1577 input_thread_t * p_input;
1578 p_input = pl_CurrentInput( VLCIntf );
1585 var_Get( p_input, "position", &pos );
1586 f_updated = 10000. * pos.f_float;
1587 [o_time_sld setFloatValue: f_updated];
1589 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1591 mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1593 [o_time_sld setEnabled: NO];
1594 [o_time_sld setHidden: YES];
1595 [o_time_sld_fancygradient_view setHidden: YES];
1597 [o_time_sld setEnabled: YES];
1598 [o_time_sld setHidden: NO];
1599 [o_time_sld_fancygradient_view setHidden: NO];
1602 [o_time_fld setStringValue: o_time];
1603 [o_time_fld setNeedsDisplay:YES];
1604 [o_fspanel setStreamPos: f_updated andTime: o_time];
1605 vlc_object_release( p_input );
1609 [o_time_sld setFloatValue: 0.0];
1610 [o_time_fld setStringValue: @"00:00"];
1611 [o_time_sld setEnabled: NO];
1612 [o_time_sld setHidden: YES];
1613 [o_time_sld_fancygradient_view setHidden: YES];
1615 [o_detached_time_sld_fancygradient_view setHidden: YES];
1620 [o_detached_time_sld setFloatValue: [o_time_sld floatValue]];
1621 [o_detached_time_sld setEnabled: [o_time_sld isEnabled]];
1622 [o_detached_time_fld setStringValue: [o_time_fld stringValue]];
1623 [o_detached_time_sld setHidden: [o_time_sld isHidden]];
1627 - (void)updateVolumeSlider
1629 int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1630 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1634 [o_volume_sld setIntValue: i_volume];
1635 [o_fspanel setVolumeLevel: i_volume];
1638 [o_volume_sld setIntValue: 0];
1640 [o_volume_sld setEnabled: !b_muted];
1641 [o_volume_up_btn setEnabled: !b_muted];
1646 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1647 input_thread_t * p_input;
1648 p_input = pl_CurrentInput( VLCIntf );
1652 char *format = var_InheritString( VLCIntf, "input-title-format" );
1653 char *formated = str_format_meta( pl_Get( VLCIntf ), format );
1655 aString = [NSString stringWithUTF8String:formated];
1658 char *uri = input_item_GetURI( input_GetItem( p_input ) );
1660 NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1661 if ([o_url isFileURL])
1663 [self setRepresentedURL: o_url];
1664 [o_detached_video_window setRepresentedURL: o_url];
1666 [self setRepresentedURL: nil];
1667 [o_detached_video_window setRepresentedURL: nil];
1671 if ([aString isEqualToString:@""])
1673 if ([o_url isFileURL])
1674 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1676 aString = [o_url absoluteString];
1679 [self setTitle: aString];
1680 [o_fspanel setStreamTitle: aString];
1681 vlc_object_release( p_input );
1685 [self setTitle: _NS("VLC media player")];
1686 [self setRepresentedURL: nil];
1692 - (void)updateWindow
1694 bool b_input = false;
1695 bool b_plmul = false;
1696 bool b_control = false;
1697 bool b_seekable = false;
1698 bool b_chapters = false;
1700 playlist_t * p_playlist = pl_Get( VLCIntf );
1703 b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1706 input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1708 bool b_buffering = NO;
1710 if( ( b_input = ( p_input != NULL ) ) )
1712 /* seekable streams */
1713 cachedInputState = input_GetState( p_input );
1714 if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1717 /* seekable streams */
1718 b_seekable = var_GetBool( p_input, "can-seek" );
1720 /* check whether slow/fast motion is possible */
1721 b_control = var_GetBool( p_input, "can-rate" );
1723 /* chapters & titles */
1724 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1726 vlc_object_release( p_input );
1731 [o_progress_bar startAnimation:self];
1732 [o_progress_bar setIndeterminate:YES];
1733 [o_progress_bar setHidden:NO];
1735 [o_progress_bar stopAnimation:self];
1736 [o_progress_bar setHidden:YES];
1739 [o_stop_btn setEnabled: b_input];
1740 [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1741 [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1742 if (b_show_jump_buttons)
1744 [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1745 [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1749 [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1750 [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1752 [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1754 [o_time_sld setEnabled: b_seekable];
1755 [self updateTimeSlider];
1756 [o_fspanel setSeekable: b_seekable];
1759 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1760 [self hideDropZone];
1762 [self showDropZone];
1764 [o_sidebar_view setNeedsDisplay:YES];
1769 [o_play_btn setImage: o_pause_img];
1770 [o_play_btn setAlternateImage: o_pause_pressed_img];
1771 [o_play_btn setToolTip: _NS("Pause")];
1774 [o_detached_play_btn setImage: o_pause_img];
1775 [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1776 [o_detached_play_btn setToolTip: _NS("Pause")];
1778 [o_fspanel setPause];
1783 [o_play_btn setImage: o_play_img];
1784 [o_play_btn setAlternateImage: o_play_pressed_img];
1785 [o_play_btn setToolTip: _NS("Play")];
1788 [o_detached_play_btn setImage: o_play_img];
1789 [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1790 [o_detached_play_btn setToolTip: _NS("Play")];
1792 [o_fspanel setPlay];
1795 - (void)drawFancyGradientEffectForTimeSlider
1797 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1798 CGFloat f_value = [o_time_sld knobPosition];
1801 NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1802 if (f_value != oldFrame.size.width)
1804 if ([o_time_sld_fancygradient_view isHidden])
1805 [o_time_sld_fancygradient_view setHidden: NO];
1806 [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1809 if (b_nonembedded && b_video_deco)
1811 f_value = [o_detached_time_sld knobPosition];
1812 oldFrame = [o_detached_time_sld_fancygradient_view frame];
1813 if (f_value != oldFrame.size.width)
1815 if ([o_detached_time_sld_fancygradient_view isHidden])
1816 [o_detached_time_sld_fancygradient_view setHidden: NO];
1817 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1824 frame = [o_time_sld_fancygradient_view frame];
1825 if (frame.size.width > 0)
1827 frame.size.width = 0;
1828 [o_time_sld_fancygradient_view setFrame: frame];
1832 frame = [o_detached_time_sld_fancygradient_view frame];
1833 frame.size.width = 0;
1834 [o_detached_time_sld_fancygradient_view setFrame: frame];
1837 [o_time_sld_fancygradient_view setHidden: YES];
1839 [o_detached_time_sld_fancygradient_view setHidden: YES];
1845 #pragma mark Video Output handling
1848 return o_video_view;
1851 - (void)setupVideoView
1853 // TODO: make lion fullscreen compatible with macosx-background and !embedded-video
1854 if( var_InheritBool( VLCIntf, "macosx-background" ) && !b_nativeFullscreenMode )
1856 msg_Dbg( VLCIntf, "Creating background window" );
1857 NSScreen *screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
1859 screen = [self screen];
1860 NSRect screen_rect = [screen frame];
1862 if( o_extra_video_window )
1863 [o_extra_video_window release];
1865 o_extra_video_window = [[VLCWindow alloc] initWithContentRect:screen_rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
1866 [o_extra_video_window setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey) + 1];
1867 [o_extra_video_window setBackgroundColor: [NSColor blackColor]];
1868 [o_extra_video_window setCanBecomeKeyWindow: NO];
1869 [o_extra_video_window setCanBecomeMainWindow: NO];
1870 [o_extra_video_window useOptimizedDrawing: YES];
1871 [o_extra_video_window setMovableByWindowBackground: NO];
1873 [o_video_view retain];
1874 if ([o_video_view superview] != NULL)
1875 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1876 screen_rect.origin.x = screen_rect.origin.y = 0;
1877 [o_video_view setFrame: screen_rect];
1878 [[o_extra_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1879 [o_video_view release];
1881 [o_extra_video_window orderBack:nil];
1883 b_nonembedded = YES;
1887 if ((var_InheritBool( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode) && b_video_deco)
1889 if ([o_video_view window] != self)
1891 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1892 [o_video_view setFrame: [o_split_view frame]];
1893 [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1899 if ([o_video_view superview] != NULL)
1900 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1903 videoFrame.size = [[o_detached_video_window contentView] frame].size;
1906 videoFrame.size.height -= [o_detached_bottombar_view frame].size.height;
1907 if( b_dark_interface )
1908 videoFrame.size.height -= [o_detached_titlebar_view frame].size.height;
1910 videoFrame.origin.x = .0;
1911 videoFrame.origin.y = [o_detached_bottombar_view frame].size.height;
1915 videoFrame.origin.y = .0;
1916 videoFrame.origin.x = .0;
1919 [o_video_view setFrame: videoFrame];
1920 [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1921 [o_detached_video_window setLevel:NSNormalWindowLevel];
1922 [o_detached_video_window useOptimizedDrawing: YES];
1923 b_nonembedded = YES;
1926 [[o_video_view window] makeKeyAndOrderFront: self];
1928 vout_thread_t *p_vout = getVout();
1931 if( var_GetBool( p_vout, "video-on-top" ) )
1932 [[o_video_view window] setLevel: NSStatusWindowLevel];
1934 [[o_video_view window] setLevel: NSNormalWindowLevel];
1935 vlc_object_release( p_vout );
1939 [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
1942 - (void)setVideoplayEnabled
1944 BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1946 if( b_videoPlayback )
1948 // look for 'start at fullscreen'
1949 [[VLCMain sharedInstance] fullscreenChanged];
1953 [self makeFirstResponder: nil];
1954 [o_detached_video_window orderOut: nil];
1955 if( o_extra_video_window )
1956 [o_extra_video_window orderOut: nil];
1958 if( [self level] != NSNormalWindowLevel )
1959 [self setLevel: NSNormalWindowLevel];
1960 if( [o_detached_video_window level] != NSNormalWindowLevel )
1961 [o_detached_video_window setLevel: NSNormalWindowLevel];
1963 // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
1964 [self setAlphaValue:1.0];
1967 if( b_nativeFullscreenMode )
1969 if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1970 [o_bottombar_view setHidden: b_videoPlayback];
1972 [o_bottombar_view setHidden: NO];
1973 if( b_videoPlayback && b_fullscreen )
1974 [o_fspanel setActive: nil];
1975 if( !b_videoPlayback )
1976 [o_fspanel setNonActive: nil];
1979 if (!b_videoPlayback && b_fullscreen)
1981 if (!b_nativeFullscreenMode)
1982 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1986 - (void)resizeWindow
1988 if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
1991 id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1992 NSSize windowMinSize = [o_videoWindow minSize];
1993 NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
1995 NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
1996 NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
1998 unsigned int i_width = nativeVideoSize.width;
1999 unsigned int i_height = nativeVideoSize.height;
2000 if (i_width < windowMinSize.width)
2001 i_width = windowMinSize.width;
2002 if (i_height < f_min_video_height)
2003 i_height = f_min_video_height;
2005 /* Calculate the window's new size */
2007 new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
2008 new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
2009 new_frame.origin.x = topleftscreen.x;
2010 new_frame.origin.y = topleftscreen.y - new_frame.size.height;
2012 /* make sure the window doesn't exceed the screen size the window is on */
2013 if( new_frame.size.width > screenFrame.size.width )
2015 new_frame.size.width = screenFrame.size.width;
2016 new_frame.origin.x = screenFrame.origin.x;
2018 if( new_frame.size.height > screenFrame.size.height )
2020 new_frame.size.height = screenFrame.size.height;
2021 new_frame.origin.y = screenFrame.origin.y;
2023 if( new_frame.origin.y < screenFrame.origin.y )
2024 new_frame.origin.y = screenFrame.origin.y;
2026 CGFloat right_screen_point = screenFrame.origin.x + screenFrame.size.width;
2027 CGFloat right_window_point = new_frame.origin.x + new_frame.size.width;
2028 if( right_window_point > right_screen_point )
2029 new_frame.origin.x -= ( right_window_point - right_screen_point );
2031 [[o_videoWindow animator] setFrame:new_frame display:YES];
2034 - (void)setNativeVideoSize:(NSSize)size
2036 nativeVideoSize = size;
2038 if( var_InheritBool( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen && !var_InheritBool( VLCIntf, "macosx-background" ) )
2039 [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
2042 // Called automatically if window's acceptsMouseMovedEvents property is true
2043 - (void)mouseMoved:(NSEvent *)theEvent
2046 [self recreateHideMouseTimer];
2048 [super mouseMoved: theEvent];
2051 - (void)recreateHideMouseTimer
2053 if (t_hide_mouse_timer != nil) {
2054 [t_hide_mouse_timer invalidate];
2055 [t_hide_mouse_timer release];
2058 t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
2060 selector:@selector(hideMouseCursor:)
2063 [t_hide_mouse_timer retain];
2066 // NSTimer selectors require this function signature as per Apple's docs
2067 - (void)hideMouseCursor:(NSTimer *)timer
2069 [NSCursor setHiddenUntilMouseMoves: YES];
2073 #pragma mark Fullscreen support
2074 - (void)showFullscreenController
2076 if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
2082 return b_fullscreen;
2085 - (void)lockFullscreenAnimation
2087 [o_animation_lock lock];
2090 - (void)unlockFullscreenAnimation
2092 [o_animation_lock unlock];
2095 - (void)enterFullscreen
2097 NSMutableDictionary *dict1, *dict2;
2101 BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2102 o_current_video_window = [o_video_view window];
2104 screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
2105 [self lockFullscreenAnimation];
2109 msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
2110 screen = [o_current_video_window screen];
2114 msg_Dbg( VLCIntf, "Using deepest screen" );
2115 screen = [NSScreen deepestScreen];
2118 screen_rect = [screen frame];
2120 [o_fullscreen_btn setState: YES];
2122 [o_detached_fullscreen_btn setState: YES];
2124 [self recreateHideMouseTimer];
2126 if( blackout_other_displays )
2127 [screen blackoutOtherScreens];
2129 /* Make sure we don't see the window flashes in float-on-top mode */
2130 i_originalLevel = [o_current_video_window level];
2131 [o_current_video_window setLevel:NSNormalWindowLevel];
2133 /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
2134 if (!o_fullscreen_window)
2136 /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
2138 rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
2139 rect.origin.x += [o_current_video_window frame].origin.x;
2140 rect.origin.y += [o_current_video_window frame].origin.y;
2141 o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
2142 [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
2143 [o_fullscreen_window setCanBecomeKeyWindow: YES];
2144 [o_fullscreen_window setCanBecomeMainWindow: YES];
2146 if (![o_current_video_window isVisible] || [o_current_video_window alphaValue] == 0.0)
2148 /* We don't animate if we are not visible, instead we
2149 * simply fade the display */
2150 CGDisplayFadeReservationToken token;
2152 if( blackout_other_displays )
2154 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2155 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2158 if ([screen mainScreen])
2159 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2161 [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2162 [o_temp_view setFrame:[o_video_view frame]];
2163 [o_fullscreen_window setContentView:o_video_view];
2165 [o_fullscreen_window makeKeyAndOrderFront:self];
2166 [o_fullscreen_window orderFront:self animate:YES];
2168 [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
2169 [o_fullscreen_window setLevel:NSNormalWindowLevel];
2171 if( blackout_other_displays )
2173 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2174 CGReleaseDisplayFadeReservation( token );
2177 /* Will release the lock */
2178 [self hasBecomeFullscreen];
2183 /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2184 NSDisableScreenUpdates();
2185 [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2186 [o_temp_view setFrame:[o_video_view frame]];
2187 [o_fullscreen_window setContentView:o_video_view];
2188 [o_fullscreen_window makeKeyAndOrderFront:self];
2189 NSEnableScreenUpdates();
2192 /* We are in fullscreen (and no animation is running) */
2195 /* Make sure we are hidden */
2196 [o_current_video_window orderOut: self];
2198 [self unlockFullscreenAnimation];
2202 if (o_fullscreen_anim1)
2204 [o_fullscreen_anim1 stopAnimation];
2205 [o_fullscreen_anim1 release];
2207 if (o_fullscreen_anim2)
2209 [o_fullscreen_anim2 stopAnimation];
2210 [o_fullscreen_anim2 release];
2213 if ([screen mainScreen])
2214 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2216 dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
2217 dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
2219 [dict1 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2220 [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
2222 [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2223 [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2224 [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
2226 /* Strategy with NSAnimation allocation:
2227 - Keep at most 2 animation at a time
2228 - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
2230 o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
2231 o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
2236 [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2237 [o_fullscreen_anim1 setDuration: 0.3];
2238 [o_fullscreen_anim1 setFrameRate: 30];
2239 [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2240 [o_fullscreen_anim2 setDuration: 0.2];
2241 [o_fullscreen_anim2 setFrameRate: 30];
2243 [o_fullscreen_anim2 setDelegate: self];
2244 [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2246 [o_fullscreen_anim1 startAnimation];
2247 /* fullscreenAnimation will be unlocked when animation ends */
2250 - (void)hasBecomeFullscreen
2252 if( [[o_video_view subviews] count] > 0 )
2253 [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2255 [o_fullscreen_window makeKeyWindow];
2256 [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
2258 /* tell the fspanel to move itself to front next time it's triggered */
2259 [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
2260 [o_fspanel setActive: nil];
2262 if( [o_current_video_window isVisible] )
2263 [o_current_video_window orderOut: self];
2266 [self unlockFullscreenAnimation];
2269 - (void)leaveFullscreen
2271 [self leaveFullscreenAndFadeOut: NO];
2274 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
2276 NSMutableDictionary *dict1, *dict2;
2278 BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2280 if( !o_current_video_window )
2283 [self lockFullscreenAnimation];
2285 [o_fullscreen_btn setState: NO];
2287 [o_detached_fullscreen_btn setState: NO];
2289 /* We always try to do so */
2290 [NSScreen unblackoutScreens];
2292 vout_thread_t *p_vout = getVout();
2295 if( var_GetBool( p_vout, "video-on-top" ) )
2296 [[o_video_view window] setLevel: NSStatusWindowLevel];
2298 [[o_video_view window] setLevel: NSNormalWindowLevel];
2299 vlc_object_release( p_vout );
2301 [[o_video_view window] makeKeyAndOrderFront: nil];
2303 /* Don't do anything if o_fullscreen_window is already closed */
2304 if (!o_fullscreen_window)
2306 [self unlockFullscreenAnimation];
2312 /* We don't animate if we are not visible, instead we
2313 * simply fade the display */
2314 CGDisplayFadeReservationToken token;
2316 if( blackout_other_displays )
2318 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2319 CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2322 [o_fspanel setNonActive: nil];
2323 [NSApp setPresentationOptions: NSApplicationPresentationDefault];
2325 /* Will release the lock */
2326 [self hasEndedFullscreen];
2328 /* Our window is hidden, and might be faded. We need to workaround that, so note it
2330 b_window_is_invisible = YES;
2332 if( blackout_other_displays )
2334 CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2335 CGReleaseDisplayFadeReservation( token );
2341 [o_current_video_window setAlphaValue: 0.0];
2342 [o_current_video_window orderFront: self];
2343 [[o_video_view window] orderFront: self];
2345 [o_fspanel setNonActive: nil];
2346 [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
2348 if (o_fullscreen_anim1)
2350 [o_fullscreen_anim1 stopAnimation];
2351 [o_fullscreen_anim1 release];
2353 if (o_fullscreen_anim2)
2355 [o_fullscreen_anim2 stopAnimation];
2356 [o_fullscreen_anim2 release];
2359 frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
2360 frame.origin.x += [o_current_video_window frame].origin.x;
2361 frame.origin.y += [o_current_video_window frame].origin.y;
2363 dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
2364 [dict2 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2365 [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2367 o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
2370 [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2371 [o_fullscreen_anim2 setDuration: 0.3];
2372 [o_fullscreen_anim2 setFrameRate: 30];
2374 [o_fullscreen_anim2 setDelegate: self];
2376 dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
2378 [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2379 [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2380 [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
2382 o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
2385 [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2386 [o_fullscreen_anim1 setDuration: 0.2];
2387 [o_fullscreen_anim1 setFrameRate: 30];
2388 [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2390 /* Make sure o_fullscreen_window is the frontmost window */
2391 [o_fullscreen_window orderFront: self];
2393 [o_fullscreen_anim1 startAnimation];
2394 /* fullscreenAnimation will be unlocked when animation ends */
2397 - (void)hasEndedFullscreen
2401 /* This function is private and should be only triggered at the end of the fullscreen change animation */
2402 /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2403 NSDisableScreenUpdates();
2404 [o_video_view retain];
2405 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
2406 [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
2407 [o_video_view release];
2408 [o_video_view setFrame:[o_temp_view frame]];
2409 if( [[o_video_view subviews] count] > 0 )
2410 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2411 if( !b_nonembedded )
2412 [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
2414 [[o_video_view window] makeKeyAndOrderFront: self];
2415 [o_fullscreen_window orderOut: self];
2416 NSEnableScreenUpdates();
2418 [o_fullscreen_window release];
2419 o_fullscreen_window = nil;
2420 [[o_video_view window] setLevel:i_originalLevel];
2421 [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2423 // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2424 if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2425 [o_current_video_window orderOut: self];
2427 o_current_video_window = nil;
2428 [self unlockFullscreenAnimation];
2431 - (void)animationDidEnd:(NSAnimation*)animation
2433 NSArray *viewAnimations;
2434 if( o_makekey_anim == animation )
2436 [o_makekey_anim release];
2439 if ([animation currentValue] < 1.0)
2442 /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2443 viewAnimations = [o_fullscreen_anim2 viewAnimations];
2444 if ([viewAnimations count] >=1 &&
2445 [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2447 /* Fullscreen ended */
2448 [self hasEndedFullscreen];
2452 /* Fullscreen started */
2453 [self hasBecomeFullscreen];
2457 - (void)makeKeyAndOrderFront: (id)sender
2460 * when we exit fullscreen and fade out, we may endup in
2461 * having a window that is faded. We can't have it fade in unless we
2464 if(!b_window_is_invisible)
2466 /* Make sure we don't do it too much */
2467 [super makeKeyAndOrderFront: sender];
2471 [super setAlphaValue:0.0f];
2472 [super makeKeyAndOrderFront: sender];
2474 NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2475 [dict setObject:self forKey:NSViewAnimationTargetKey];
2476 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2478 o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2481 [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2482 [o_makekey_anim setDuration: 0.1];
2483 [o_makekey_anim setFrameRate: 30];
2484 [o_makekey_anim setDelegate: self];
2486 [o_makekey_anim startAnimation];
2487 b_window_is_invisible = NO;
2489 /* fullscreenAnimation will be unlocked when animation ends */
2493 #pragma mark Lion native fullscreen handling
2494 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2496 // workaround, see #6668
2497 [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2499 var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
2501 vout_thread_t *p_vout = getVout();
2504 var_SetBool( p_vout, "fullscreen", true );
2505 vlc_object_release( p_vout );
2508 [o_video_view setFrame: [[self contentView] frame]];
2511 [self recreateHideMouseTimer];
2512 i_originalLevel = [self level];
2513 [self setLevel:NSNormalWindowLevel];
2515 if (b_dark_interface)
2517 [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2520 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2521 winrect = [self frame];
2523 winrect.size.height = winrect.size.height - f_titleBarHeight;
2524 [self setFrame: winrect display:NO animate:NO];
2525 winrect = [o_split_view frame];
2526 winrect.size.height = winrect.size.height + f_titleBarHeight;
2527 [o_split_view setFrame: winrect];
2530 if ([[VLCMain sharedInstance] activeVideoPlayback])
2531 [o_bottombar_view setHidden: YES];
2533 [self setMovableByWindowBackground: NO];
2536 - (void)windowDidEnterFullScreen:(NSNotification *)notification
2538 // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
2539 // But this creates some problems when leaving fs over remote intfs, so activate app here.
2540 [NSApp activateIgnoringOtherApps:YES];
2542 [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2543 [o_fspanel setActive: nil];
2546 - (void)windowWillExitFullScreen:(NSNotification *)notification
2549 var_SetBool( pl_Get( VLCIntf ), "fullscreen", false );
2551 vout_thread_t *p_vout = getVout();
2554 var_SetBool( p_vout, "fullscreen", false );
2555 vlc_object_release( p_vout );
2558 [o_video_view setFrame: [o_split_view frame]];
2559 [NSCursor setHiddenUntilMouseMoves: NO];
2560 [o_fspanel setNonActive: nil];
2561 [self setLevel:i_originalLevel];
2564 if (b_dark_interface)
2567 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2568 winrect = [self frame];
2570 [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2571 winrect.size.width, f_titleBarHeight )];
2572 [[self contentView] addSubview: o_titlebar_view];
2574 winrect.size.height = winrect.size.height + f_titleBarHeight;
2575 [self setFrame: winrect display:NO animate:NO];
2576 winrect = [o_split_view frame];
2577 winrect.size.height = winrect.size.height - f_titleBarHeight;
2578 [o_split_view setFrame: winrect];
2579 [o_video_view setFrame: winrect];
2582 if ([[VLCMain sharedInstance] activeVideoPlayback])
2583 [o_bottombar_view setHidden: NO];
2585 [self setMovableByWindowBackground: YES];
2589 #pragma mark split view delegate
2590 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2592 if (dividerIndex == 0)
2598 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
2600 if (dividerIndex == 0)
2606 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2608 return ([subview isEqual:o_left_split_view]);
2611 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
2613 if ([subview isEqual:o_left_split_view])
2619 #pragma mark Side Bar Data handling
2620 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2621 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2623 //Works the same way as the NSOutlineView data source: `nil` means a parent item
2625 return [o_sidebaritems count];
2627 return [[item children] count];
2631 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2633 //Works the same way as the NSOutlineView data source: `nil` means a parent item
2635 return [o_sidebaritems objectAtIndex:index];
2637 return [[item children] objectAtIndex:index];
2641 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2643 return [item title];
2646 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2648 [item setTitle:object];
2651 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2653 return [item hasChildren];
2657 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2659 if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2662 return [item hasBadge];
2666 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2668 playlist_t * p_playlist = pl_Get( VLCIntf );
2669 NSInteger i_playlist_size;
2671 if ([[item identifier] isEqualToString: @"playlist"])
2674 i_playlist_size = p_playlist->p_local_category->i_children;
2677 return i_playlist_size;
2679 if ([[item identifier] isEqualToString: @"medialibrary"])
2682 i_playlist_size = p_playlist->p_ml_category->i_children;
2685 return i_playlist_size;
2688 return [item badgeValue];
2692 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2694 return [item hasIcon];
2698 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2703 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2705 if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2710 if ([item sdtype] > 0)
2712 m = [[NSMenu alloc] init];
2713 playlist_t * p_playlist = pl_Get( VLCIntf );
2714 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2716 [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2718 [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2719 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2721 return [m autorelease];
2728 - (IBAction)sdmenuhandler:(id)sender
2730 NSString * identifier = [sender representedObject];
2731 if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2733 playlist_t * p_playlist = pl_Get( VLCIntf );
2734 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2737 playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2739 playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2744 #pragma mark Side Bar Delegate Methods
2745 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2746 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2748 if ([[group identifier] isEqualToString:@"library"])
2754 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2756 playlist_t * p_playlist = pl_Get( VLCIntf );
2758 NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2759 id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2762 //Set the label text to represent the new selection
2763 if ([item sdtype] > -1 && [[item identifier] length] > 0)
2765 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2768 playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2772 [o_chosen_category_lbl setStringValue:[item title]];
2774 if ([[item identifier] isEqualToString:@"playlist"])
2776 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2778 else if([[item identifier] isEqualToString:@"medialibrary"])
2780 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2784 playlist_item_t * pl_item;
2786 pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2788 [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2792 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2793 [self hideDropZone];
2795 [self showDropZone];
2798 if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
2799 [self showPodcastControls];
2801 [self hidePodcastControls];
2804 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2806 if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2808 NSPasteboard *o_pasteboard = [info draggingPasteboard];
2809 if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2810 return NSDragOperationGeneric;
2812 return NSDragOperationNone;
2815 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2817 NSPasteboard *o_pasteboard = [info draggingPasteboard];
2819 playlist_t * p_playlist = pl_Get( VLCIntf );
2820 playlist_item_t *p_node;
2822 if ([[item identifier] isEqualToString:@"playlist"])
2823 p_node = p_playlist->p_local_category;
2825 p_node = p_playlist->p_ml_category;
2827 if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2829 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2830 NSUInteger count = [o_values count];
2831 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2833 for( NSUInteger i = 0; i < count; i++)
2835 NSDictionary *o_dic;
2836 char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
2840 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2844 [o_array addObject: o_dic];
2847 [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2850 else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2852 NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2854 NSUInteger count = [array count];
2855 playlist_item_t * p_item = NULL;
2858 for( NSUInteger i = 0; i < count; i++ )
2860 p_item = [[array objectAtIndex:i] pointerValue];
2861 if( !p_item ) continue;
2862 playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2871 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2873 return [item identifier];
2876 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2878 /* the following code assumes for sakes of simplicity that only the top level
2879 * items are allowed to have children */
2881 NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2882 NSUInteger count = [array count];
2886 for (NSUInteger x = 0; x < count; x++)
2888 id item = [array objectAtIndex: x]; // save one objc selector call
2889 if ([[item identifier] isEqualToString:object])
2897 #pragma mark Podcast
2899 - (IBAction)addPodcast:(id)sender
2901 [NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
2904 - (IBAction)addPodcastWindowAction:(id)sender
2906 [o_podcast_subscribe_window orderOut:sender];
2907 [NSApp endSheet: o_podcast_subscribe_window];
2909 if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
2910 NSMutableString * podcastConf = [[NSMutableString alloc] init];
2911 if (config_GetPsz( VLCIntf, "podcast-urls" ) != NULL)
2912 [podcastConf appendFormat:@"%s|", config_GetPsz( VLCIntf, "podcast-urls" )];
2914 [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
2915 config_PutPsz( VLCIntf, "podcast-urls", [podcastConf UTF8String] );
2917 vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( VLCIntf->p_libvlc, "podcast" );
2919 var_SetString( p_obj, "podcast-urls", [podcastConf UTF8String] );
2920 vlc_object_release( p_obj );
2922 [podcastConf release];
2926 - (IBAction)removePodcast:(id)sender
2928 if (config_GetPsz( VLCIntf, "podcast-urls" ) != NULL) {
2929 [o_podcast_unsubscribe_pop removeAllItems];
2930 [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz( VLCIntf, "podcast-urls" )] componentsSeparatedByString:@"|"]];
2931 [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
2935 - (IBAction)removePodcastWindowAction:(id)sender
2937 [o_podcast_unsubscribe_window orderOut:sender];
2938 [NSApp endSheet: o_podcast_unsubscribe_window];
2940 if (sender == o_podcast_unsubscribe_ok_btn) {
2941 NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz( VLCIntf, "podcast-urls" )] componentsSeparatedByString:@"|"]];
2942 [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
2943 config_PutPsz( VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String] );
2946 vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( VLCIntf->p_libvlc, "podcast" );
2948 var_SetString( p_obj, "podcast-urls", config_GetPsz( VLCIntf, "podcast-urls" ) );
2949 vlc_object_release( p_obj );
2952 /* reload the podcast module, since it won't update its list when removing podcasts */
2953 playlist_t * p_playlist = pl_Get( VLCIntf );
2954 if( playlist_IsServicesDiscoveryLoaded( p_playlist, "podcast{longname=\"Podcasts\"}" ) ) {
2955 playlist_ServicesDiscoveryRemove( p_playlist, "podcast{longname=\"Podcasts\"}" );
2956 playlist_ServicesDiscoveryAdd( p_playlist, "podcast{longname=\"Podcasts\"}" );
2957 [o_playlist_table reloadData];
2963 - (void)showPodcastControls
2965 NSRect podcastViewDimensions = [o_podcast_view frame];
2966 NSRect rightSplitRect = [o_right_split_view frame];
2967 NSRect playlistTableRect = [o_playlist_table frame];
2969 podcastViewDimensions.size.width = rightSplitRect.size.width;
2970 podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
2971 [o_podcast_view setFrame:podcastViewDimensions];
2973 playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
2974 playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
2975 [o_playlist_table setFrame:playlistTableRect];
2976 [o_playlist_table setNeedsDisplay:YES];
2978 [o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
2979 b_podcastView_displayed = YES;
2982 - (void)hidePodcastControls
2984 if (b_podcastView_displayed) {
2985 NSRect podcastViewDimensions = [o_podcast_view frame];
2986 NSRect playlistTableRect = [o_playlist_table frame];
2988 playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
2989 playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
2991 [o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
2992 [o_playlist_table setFrame: playlistTableRect];
2993 b_podcastView_displayed = NO;
2998 #pragma mark Accessibility stuff
3000 - (NSArray *)accessibilityAttributeNames
3002 if( !b_dark_interface )
3003 return [super accessibilityAttributeNames];
3005 static NSMutableArray *attributes = nil;
3006 if ( attributes == nil ) {
3007 attributes = [[super accessibilityAttributeNames] mutableCopy];
3008 NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3009 NSAccessibilityCloseButtonAttribute,
3010 NSAccessibilityMinimizeButtonAttribute,
3011 NSAccessibilityZoomButtonAttribute,
3014 for( NSString *attribute in appendAttributes )
3016 if( ![attributes containsObject:attribute] )
3017 [attributes addObject:attribute];
3023 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3025 if( b_dark_interface )
3027 VLCMainWindowTitleView *o_tbv = o_titlebar_view;
3029 if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3030 return NSAccessibilityStandardWindowSubrole;
3032 if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3033 return [[o_tbv closeButton] cell];
3035 if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3036 return [[o_tbv minimizeButton] cell];
3038 if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3039 return [[o_tbv zoomButton] cell];
3042 return [super accessibilityAttributeValue: o_attribute_name];
3045 - (id)detachedTitlebarView
3047 return o_detached_titlebar_view;
3051 @implementation VLCDetachedVideoWindow
3053 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
3054 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
3056 b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
3057 b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
3059 if (b_dark_interface || !b_video_deco)
3061 #ifdef MAC_OS_X_VERSION_10_7
3063 styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
3065 styleMask = NSBorderlessWindowMask;
3067 styleMask = NSBorderlessWindowMask;
3071 self = [super initWithContentRect:contentRect styleMask:styleMask
3072 backing:backingType defer:flag];
3074 /* we want to be moveable regardless of our style */
3075 [self setMovableByWindowBackground: YES];
3077 /* we don't want this window to be restored on relaunch */
3079 [self setRestorable:NO];
3084 - (void)awakeFromNib
3086 [self setAcceptsMouseMovedEvents: YES];
3088 if (b_dark_interface)
3090 [self setBackgroundColor: [NSColor clearColor]];
3091 [self setOpaque: NO];
3093 [self setHasShadow:NO];
3094 [self setHasShadow:YES];
3098 - (IBAction)fullscreen:(id)sender
3100 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
3103 - (void)performClose:(id)sender
3105 if (b_dark_interface || !b_video_deco)
3106 [[VLCMainWindow sharedInstance] performClose: sender];
3108 [super performClose: sender];
3111 - (void)performMiniaturize:(id)sender
3113 if (b_dark_interface || !b_video_deco)
3114 [self miniaturize: sender];
3116 [super performMiniaturize: sender];
3119 - (void)performZoom:(id)sender
3121 if (b_dark_interface || !b_video_deco)
3122 [self customZoom: sender];
3124 [super performZoom: sender];
3127 - (void)zoom:(id)sender
3129 if (b_dark_interface || !b_video_deco)
3130 [self customZoom: sender];
3132 [super zoom: sender];
3135 - (BOOL)canBecomeKeyWindow
3140 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
3142 SEL s_menuAction = [menuItem action];
3144 if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
3147 return [super validateMenuItem:menuItem];
3151 * Given a proposed frame rectangle, return a modified version
3152 * which will fit inside the screen.
3154 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3155 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3156 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3157 * Copyright (C) 1996 Free Software Foundation, Inc.
3159 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
3161 NSRect screenRect = [screen visibleFrame];
3164 /* Move top edge of the window inside the screen */
3165 difference = NSMaxY (frameRect) - NSMaxY (screenRect);
3168 frameRect.origin.y -= difference;
3171 /* If the window is resizable, resize it (if needed) so that the
3172 bottom edge is on the screen or can be on the screen when the user moves
3174 difference = NSMaxY (screenRect) - NSMaxY (frameRect);
3175 if (_styleMask & NSResizableWindowMask)
3179 difference2 = screenRect.origin.y - frameRect.origin.y;
3180 difference2 -= difference;
3181 // Take in account the space between the top of window and the top of the
3182 // screen which can be used to move the bottom of the window on the screen
3183 if (difference2 > 0)
3185 frameRect.size.height -= difference2;
3186 frameRect.origin.y += difference2;
3189 /* Ensure that resizing doesn't makewindow smaller than minimum */
3190 difference2 = [self minSize].height - frameRect.size.height;
3191 if (difference2 > 0)
3193 frameRect.size.height += difference2;
3194 frameRect.origin.y -= difference2;
3204 Zooms the receiver. This method calls the delegate method
3205 windowShouldZoom:toFrame: to determine if the window should
3206 be allowed to zoom to full screen.
3208 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3209 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3210 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3211 * Copyright (C) 1996 Free Software Foundation, Inc.
3213 - (void) customZoom: (id)sender
3215 NSRect maxRect = [[self screen] visibleFrame];
3216 NSRect currentFrame = [self frame];
3218 if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
3220 maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
3223 maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
3225 // Compare the new frame with the current one
3226 if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
3227 && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
3228 && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
3229 && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
3231 // Already in zoomed mode, reset user frame, if stored
3232 if ([self frameAutosaveName] != nil)
3234 [self setFrame: previousSavedFrame display: YES animate: YES];
3235 [self saveFrameUsingName: [self frameAutosaveName]];
3240 if ([self frameAutosaveName] != nil)
3242 [self saveFrameUsingName: [self frameAutosaveName]];
3243 previousSavedFrame = [self frame];
3246 [self setFrame: maxRect display: YES animate: YES];
3249 - (NSArray *)accessibilityAttributeNames
3251 if( !b_dark_interface )
3252 return [super accessibilityAttributeNames];
3254 static NSMutableArray *attributes = nil;
3255 if ( attributes == nil ) {
3256 attributes = [[super accessibilityAttributeNames] mutableCopy];
3257 NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3258 NSAccessibilityCloseButtonAttribute,
3259 NSAccessibilityMinimizeButtonAttribute,
3260 NSAccessibilityZoomButtonAttribute,
3263 for( NSString *attribute in appendAttributes )
3265 if( ![attributes containsObject:attribute] )
3266 [attributes addObject:attribute];
3272 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3274 if( b_dark_interface )
3276 VLCMainWindowTitleView *o_tbv = [[VLCMainWindow sharedInstance] detachedTitlebarView];
3278 if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3279 return NSAccessibilityStandardWindowSubrole;
3281 if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3282 return [[o_tbv closeButton] cell];
3284 if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3285 return [[o_tbv minimizeButton] cell];
3287 if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3288 return [[o_tbv zoomButton] cell];
3291 return [super accessibilityAttributeValue: o_attribute_name];