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
98 if (!OSX_SNOW_LEOPARD)
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 */
117 if (!OSX_SNOW_LEOPARD)
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_SNOW_LEOPARD && 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:@"backward-3btns"]];
272 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-pressed"]];
273 [o_detached_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns"]];
274 [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-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-3btns"]];
280 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-pressed"]];
281 [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns"]];
282 [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-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:@"backward-3btns-dark"]];
324 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-dark-pressed"]];
325 [o_detached_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns-dark"]];
326 [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-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];
470 if (!OSX_SNOW_LEOPARD)
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];
673 if (!OSX_SNOW_LEOPARD)
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 = 29.;
723 o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
724 [o_prev_btn setButtonType: NSMomentaryChangeButton];
725 [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
726 [o_prev_btn setBordered:NO];
727 [o_prev_btn setTarget:self];
728 [o_prev_btn setAction:@selector(prev:)];
729 [o_prev_btn setToolTip: _NS("Previous")];
730 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Previous") forAttribute:NSAccessibilityTitleAttribute];
731 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
732 [o_prev_btn setEnabled: b_enabled];
734 o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
735 [o_next_btn setButtonType: NSMomentaryChangeButton];
736 [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
737 [o_next_btn setBordered:NO];
738 [o_next_btn setTarget:self];
739 [o_next_btn setAction:@selector(next:)];
740 [o_next_btn setToolTip: _NS("Next")];
741 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Next") forAttribute:NSAccessibilityTitleAttribute];
742 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
743 [o_next_btn setEnabled: b_enabled];
745 if (b_dark_interface) {
746 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns-dark"]];
747 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-dark-pressed"]];
748 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns-dark"]];
749 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-dark-pressed"]];
751 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns"]];
752 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-pressed"]];
753 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns"]];
754 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-pressed"]];
757 /* change the accessibility help for the backward/forward buttons accordingly */
758 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
759 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
762 frame = [o_bwd_btn frame];
764 [o_bwd_btn setFrame:frame];
765 frame = [o_fwd_btn frame];
767 [o_fwd_btn setFrame:frame];
770 #define moveItem( item ) \
771 frame = [item frame]; \
772 frame.origin.x = frame.origin.x + f_space; \
774 [item setFrame: frame]; \
776 [[item animator] setFrame: frame]
778 moveItem( o_bwd_btn );
779 moveItem( o_play_btn );
781 moveItem( o_fwd_btn );
783 moveItem( o_stop_btn );
784 moveItem( o_playlist_btn );
785 moveItem( o_repeat_btn );
786 moveItem( o_shuffle_btn );
789 #define resizeItem( item ) \
790 frame = [item frame]; \
791 frame.size.width = frame.size.width - f_space; \
792 frame.origin.x = frame.origin.x + f_space; \
794 [item setFrame: frame]; \
796 [[item animator] setFrame: frame]
798 resizeItem( o_time_sld );
799 resizeItem( o_progress_bar );
800 resizeItem( o_time_sld_background );
801 resizeItem( o_time_sld_fancygradient_view );
804 if (b_dark_interface) {
805 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns-dark"]];
806 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-dark-pressed"]];
807 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns-dark"]];
808 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-dark-pressed"]];
810 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns"]];
811 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-pressed"]];
812 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns"]];
813 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-pressed"]];
816 preliminaryFrame.origin.x = [o_next_btn frame].origin.x + 82. + [o_fwd_btn frame].size.width;
817 [o_next_btn setFrame: preliminaryFrame];
819 // wait until the animation is done, if displayed
821 [[self contentView] addSubview:o_prev_btn];
822 [[self contentView] addSubview:o_next_btn];
824 [[self contentView] performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
825 [[self contentView] performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
828 [o_fwd_btn setAction:@selector(forward:)];
829 [o_bwd_btn setAction:@selector(backward:)];
832 - (void)removeJumpButtons:(BOOL)b_fast
834 if (!o_prev_btn || !o_next_btn )
838 [o_prev_btn setHidden: YES];
839 [o_next_btn setHidden: YES];
841 [[o_prev_btn animator] setHidden: YES];
842 [[o_next_btn animator] setHidden: YES];
844 [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
845 [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
846 [o_prev_btn release];
847 [o_next_btn release];
849 /* change the accessibility help for the backward/forward buttons accordingly */
850 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
851 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
854 frame = [o_bwd_btn frame];
856 [o_bwd_btn setFrame:frame];
857 frame = [o_fwd_btn frame];
859 [o_fwd_btn setFrame:frame];
862 #define moveItem( item ) \
863 frame = [item frame]; \
864 frame.origin.x = frame.origin.x - f_space; \
866 [item setFrame: frame]; \
868 [[item animator] setFrame: frame]
870 moveItem( o_bwd_btn );
871 moveItem( o_play_btn );
873 moveItem( o_fwd_btn );
875 moveItem( o_stop_btn );
876 moveItem( o_playlist_btn );
877 moveItem( o_repeat_btn );
878 moveItem( o_shuffle_btn );
881 #define resizeItem( item ) \
882 frame = [item frame]; \
883 frame.size.width = frame.size.width + f_space; \
884 frame.origin.x = frame.origin.x - f_space; \
886 [item setFrame: frame]; \
888 [[item animator] setFrame: frame]
890 resizeItem( o_time_sld );
891 resizeItem( o_progress_bar );
892 resizeItem( o_time_sld_background );
893 resizeItem( o_time_sld_fancygradient_view );
896 if (b_dark_interface) {
897 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns-dark"]];
898 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-dark-pressed"]];
899 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns-dark"]];
900 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-dark-pressed"]];
902 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns"]];
903 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-pressed"]];
904 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns"]];
905 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-pressed"]];
908 [o_bottombar_view setNeedsDisplay:YES];
910 [o_fwd_btn setAction:@selector(fwd:)];
911 [o_bwd_btn setAction:@selector(bwd:)];
914 - (void)togglePlaymodeButtons
916 b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
918 if (b_show_playmode_buttons)
919 [self addPlaymodeButtons:NO];
921 [self removePlaymodeButtons:NO];
924 - (void)addPlaymodeButtons:(BOOL)b_fast
927 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
929 if (b_dark_interface) {
930 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist_dark"]];
931 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-pressed_dark"]];
933 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
934 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
936 frame = [o_playlist_btn frame];
938 [o_playlist_btn setFrame:frame];
941 [o_repeat_btn setHidden: NO];
942 [o_shuffle_btn setHidden: NO];
944 [[o_repeat_btn animator] setHidden: NO];
945 [[o_shuffle_btn animator] setHidden: NO];
948 #define resizeItem( item ) \
949 frame = [item frame]; \
950 frame.size.width = frame.size.width - f_space; \
951 frame.origin.x = frame.origin.x + f_space; \
953 [item setFrame: frame]; \
955 [[item animator] setFrame: frame]
957 resizeItem( o_time_sld );
958 resizeItem( o_progress_bar );
959 resizeItem( o_time_sld_background );
960 resizeItem( o_time_sld_fancygradient_view );
964 - (void)removePlaymodeButtons:(BOOL)b_fast
967 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
968 [o_repeat_btn setHidden: YES];
969 [o_shuffle_btn setHidden: YES];
971 if (b_dark_interface) {
972 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn-dark"]];
973 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-dark-pressed"]];
975 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
976 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
978 frame = [o_playlist_btn frame];
980 [o_playlist_btn setFrame:frame];
982 #define resizeItem( item ) \
983 frame = [item frame]; \
984 frame.size.width = frame.size.width + f_space; \
985 frame.origin.x = frame.origin.x - f_space; \
987 [item setFrame: frame]; \
989 [[item animator] setFrame: frame]
991 resizeItem( o_time_sld );
992 resizeItem( o_progress_bar );
993 resizeItem( o_time_sld_background );
994 resizeItem( o_time_sld_fancygradient_view );
999 #pragma mark Button Actions
1001 - (IBAction)play:(id)sender
1003 [[VLCCoreInteraction sharedInstance] play];
1006 - (void)resetPreviousButton
1008 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
1009 // seems like no further event occurred, so let's switch the playback item
1010 [[VLCCoreInteraction sharedInstance] previous];
1011 just_triggered_previous = NO;
1015 - (void)resetBackwardSkip
1017 // the user stopped skipping, so let's allow him to change the item
1018 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
1019 just_triggered_previous = NO;
1022 - (IBAction)prev:(id)sender
1024 [[VLCCoreInteraction sharedInstance] previous];
1027 - (IBAction)bwd:(id)sender
1029 if(!just_triggered_previous)
1031 just_triggered_previous = YES;
1032 [self performSelector:@selector(resetPreviousButton)
1038 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
1040 // we just skipped 4 "continous" events, otherwise we are too fast
1041 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
1042 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
1043 [self performSelector:@selector(resetBackwardSkip)
1050 - (IBAction)backward:(id)sender
1052 [[VLCCoreInteraction sharedInstance] backwardShort];
1055 - (void)resetNextButton
1057 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
1058 // seems like no further event occurred, so let's switch the playback item
1059 [[VLCCoreInteraction sharedInstance] next];
1060 just_triggered_next = NO;
1064 - (void)resetForwardSkip
1066 // the user stopped skipping, so let's allow him to change the item
1067 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
1068 just_triggered_next = NO;
1071 - (IBAction)next:(id)sender
1073 [[VLCCoreInteraction sharedInstance] next];
1076 - (IBAction)forward:(id)sender
1078 [[VLCCoreInteraction sharedInstance] forwardShort];
1081 - (IBAction)fwd:(id)sender
1083 if(!just_triggered_next)
1085 just_triggered_next = YES;
1086 [self performSelector:@selector(resetNextButton)
1092 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
1094 // we just skipped 4 "continous" events, otherwise we are too fast
1095 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
1096 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
1097 [self performSelector:@selector(resetForwardSkip)
1104 - (IBAction)stop:(id)sender
1106 [[VLCCoreInteraction sharedInstance] stop];
1109 - (void)resizePlaylistAfterCollapse
1112 plrect = [o_playlist_table frame];
1113 plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
1114 [[o_playlist_table animator] setFrame: plrect];
1116 NSRect rightSplitRect;
1117 rightSplitRect = [o_right_split_view frame];
1118 plrect = [o_dropzone_box frame];
1119 plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
1120 plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
1121 [[o_dropzone_box animator] setFrame: plrect];
1124 - (void)makeSplitViewVisible
1126 if( b_dark_interface )
1127 [self setContentMinSize: NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1129 [self setContentMinSize: NSMakeSize( 604., 288. )];
1131 NSRect old_frame = [self frame];
1132 float newHeight = [self minSize].height;
1133 if( old_frame.size.height < newHeight )
1135 NSRect new_frame = old_frame;
1136 new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
1137 new_frame.size.height = newHeight;
1139 [[self animator] setFrame: new_frame display: YES animate: YES];
1142 [o_video_view setHidden: YES];
1143 [o_split_view setHidden: NO];
1144 [self makeFirstResponder: nil];
1148 - (void)makeSplitViewHidden
1150 if( b_dark_interface )
1151 [self setContentMinSize: NSMakeSize( 604., f_min_video_height + [o_titlebar_view frame].size.height )];
1153 [self setContentMinSize: NSMakeSize( 604., f_min_video_height )];
1155 [o_split_view setHidden: YES];
1156 [o_video_view setHidden: NO];
1158 if( [[o_video_view subviews] count] > 0 )
1159 [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1162 - (IBAction)togglePlaylist:(id)sender
1164 if (![self isVisible] && sender != nil)
1166 [self makeKeyAndOrderFront: sender];
1170 BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
1171 BOOL b_restored = NO;
1173 // TODO: implement toggle playlist in this situation (triggerd via menu item).
1174 // but for now we block this case, to avoid displaying only the half
1175 if( b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil )
1178 if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0)
1180 [self hideDropZone];
1184 if ( !(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ( (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
1185 || (b_nonembedded && sender != nil)
1186 || (!b_activeVideo && sender != nil)
1187 || b_minimized_view ) )
1189 [self hideSplitView];
1193 if (b_splitview_removed)
1195 if( !b_nonembedded || ( sender != nil && b_nonembedded))
1196 [self showSplitView];
1199 b_minimized_view = YES;
1201 b_minimized_view = NO;
1209 if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil) )
1210 [self makeSplitViewHidden];
1212 [self makeSplitViewVisible];
1216 [o_split_view setHidden: NO];
1217 [o_playlist_table setHidden: NO];
1218 [o_video_view setHidden: !b_activeVideo];
1219 if( b_activeVideo && [[o_video_view subviews] count] > 0 )
1220 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1225 - (void)setRepeatOne
1227 [o_repeat_btn setImage: o_repeat_one_img];
1228 [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1231 - (void)setRepeatAll
1233 [o_repeat_btn setImage: o_repeat_all_img];
1234 [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1237 - (void)setRepeatOff
1239 [o_repeat_btn setImage: o_repeat_img];
1240 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1243 - (IBAction)repeat:(id)sender
1245 vlc_value_t looping,repeating;
1246 intf_thread_t * p_intf = VLCIntf;
1247 playlist_t * p_playlist = pl_Get( p_intf );
1249 var_Get( p_playlist, "repeat", &repeating );
1250 var_Get( p_playlist, "loop", &looping );
1252 if( !repeating.b_bool && !looping.b_bool )
1254 /* was: no repeating at all, switching to Repeat One */
1255 [[VLCCoreInteraction sharedInstance] repeatOne];
1256 [self setRepeatOne];
1258 else if( repeating.b_bool && !looping.b_bool )
1260 /* was: Repeat One, switching to Repeat All */
1261 [[VLCCoreInteraction sharedInstance] repeatAll];
1262 [self setRepeatAll];
1266 /* was: Repeat All or bug in VLC, switching to Repeat Off */
1267 [[VLCCoreInteraction sharedInstance] repeatOff];
1268 [self setRepeatOff];
1275 playlist_t *p_playlist = pl_Get( VLCIntf );
1276 b_value = var_GetBool( p_playlist, "random" );
1279 [o_shuffle_btn setImage: o_shuffle_on_img];
1280 [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1284 [o_shuffle_btn setImage: o_shuffle_img];
1285 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1289 - (IBAction)shuffle:(id)sender
1291 [[VLCCoreInteraction sharedInstance] shuffle];
1295 - (IBAction)timeSliderAction:(id)sender
1298 input_thread_t * p_input;
1300 switch( [[NSApp currentEvent] type] )
1303 case NSLeftMouseDown:
1304 case NSLeftMouseDragged:
1305 f_updated = [sender floatValue];
1311 p_input = pl_CurrentInput( VLCIntf );
1312 if( p_input != NULL )
1317 pos.f_float = f_updated / 10000.;
1318 var_Set( p_input, "position", pos );
1319 [o_time_sld setFloatValue: f_updated];
1321 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1322 [o_time_fld setStringValue: o_time];
1323 [o_fspanel setStreamPos: f_updated andTime: o_time];
1324 vlc_object_release( p_input );
1328 - (IBAction)volumeAction:(id)sender
1330 if (sender == o_volume_sld)
1331 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1332 else if (sender == o_volume_down_btn)
1333 [[VLCCoreInteraction sharedInstance] toggleMute];
1335 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1338 - (IBAction)effects:(id)sender
1340 [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1343 - (IBAction)fullscreen:(id)sender
1345 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1348 - (IBAction)dropzoneButtonAction:(id)sender
1350 [[[VLCMain sharedInstance] open] openFileGeneric];
1354 #pragma mark overwritten default functionality
1355 - (BOOL)canBecomeKeyWindow
1360 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
1362 SEL s_menuAction = [menuItem action];
1364 if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
1367 return [super validateMenuItem:menuItem];
1370 - (void)setTitle:(NSString *)title
1372 if (b_dark_interface)
1374 [o_titlebar_view setWindowTitle: title];
1376 [o_detached_titlebar_view setWindowTitle: title];
1378 if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
1379 [o_detached_video_window setTitle: title];
1380 [super setTitle: title];
1383 - (void)performClose:(id)sender
1385 NSWindow *o_key_window = [NSApp keyWindow];
1387 if (b_dark_interface || !b_video_deco)
1389 [o_key_window orderOut: sender];
1390 if ( [[VLCMain sharedInstance] activeVideoPlayback] && ( !b_nonembedded || o_key_window != self ))
1391 [[VLCCoreInteraction sharedInstance] stop];
1395 if( b_nonembedded && o_key_window != self )
1396 [o_detached_video_window performClose: sender];
1398 [super performClose: sender];
1402 - (void)performMiniaturize:(id)sender
1404 if (b_dark_interface)
1405 [self miniaturize: sender];
1407 [super performMiniaturize: sender];
1410 - (void)performZoom:(id)sender
1412 if (b_dark_interface)
1413 [self customZoom: sender];
1415 [super performZoom: sender];
1418 - (void)zoom:(id)sender
1420 if (b_dark_interface)
1421 [self customZoom: sender];
1423 [super zoom: sender];
1427 * Given a proposed frame rectangle, return a modified version
1428 * which will fit inside the screen.
1430 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1431 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1432 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1433 * Copyright (C) 1996 Free Software Foundation, Inc.
1435 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
1437 NSRect screenRect = [screen visibleFrame];
1440 /* Move top edge of the window inside the screen */
1441 difference = NSMaxY (frameRect) - NSMaxY (screenRect);
1444 frameRect.origin.y -= difference;
1447 /* If the window is resizable, resize it (if needed) so that the
1448 bottom edge is on the screen or can be on the screen when the user moves
1450 difference = NSMaxY (screenRect) - NSMaxY (frameRect);
1451 if (_styleMask & NSResizableWindowMask)
1455 difference2 = screenRect.origin.y - frameRect.origin.y;
1456 difference2 -= difference;
1457 // Take in account the space between the top of window and the top of the
1458 // screen which can be used to move the bottom of the window on the screen
1459 if (difference2 > 0)
1461 frameRect.size.height -= difference2;
1462 frameRect.origin.y += difference2;
1465 /* Ensure that resizing doesn't makewindow smaller than minimum */
1466 difference2 = [self minSize].height - frameRect.size.height;
1467 if (difference2 > 0)
1469 frameRect.size.height += difference2;
1470 frameRect.origin.y -= difference2;
1480 Zooms the receiver. This method calls the delegate method
1481 windowShouldZoom:toFrame: to determine if the window should
1482 be allowed to zoom to full screen.
1484 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1485 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1486 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1487 * Copyright (C) 1996 Free Software Foundation, Inc.
1489 - (void)customZoom:(id)sender
1491 NSRect maxRect = [[self screen] visibleFrame];
1492 NSRect currentFrame = [self frame];
1494 if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
1496 maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
1499 maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
1501 // Compare the new frame with the current one
1502 if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
1503 && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
1504 && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
1505 && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
1507 // Already in zoomed mode, reset user frame, if stored
1508 if ([self frameAutosaveName] != nil)
1510 [self setFrame: previousSavedFrame display: YES animate: YES];
1511 [self saveFrameUsingName: [self frameAutosaveName]];
1516 if ([self frameAutosaveName] != nil)
1518 [self saveFrameUsingName: [self frameAutosaveName]];
1519 previousSavedFrame = [self frame];
1522 [self setFrame: maxRect display: YES animate: YES];
1525 - (void)windowResizedOrMoved:(NSNotification *)notification
1527 [self saveFrameUsingName: [self frameAutosaveName]];
1530 - (void)applicationWillTerminate:(NSNotification *)notification
1532 [self saveFrameUsingName: [self frameAutosaveName]];
1535 - (void)someWindowWillClose:(NSNotification *)notification
1537 if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1539 if ([[VLCMain sharedInstance] activeVideoPlayback])
1540 [[VLCCoreInteraction sharedInstance] stop];
1544 - (void)someWindowWillMiniaturize:(NSNotification *)notification
1546 if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
1548 if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1550 if([[VLCMain sharedInstance] activeVideoPlayback])
1551 [[VLCCoreInteraction sharedInstance] pause];
1556 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
1558 id videoWindow = [o_video_view window];
1559 if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
1560 return proposedFrameSize;
1562 // needed when entering lion fullscreen mode
1564 return proposedFrameSize;
1566 if( [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked] )
1568 NSRect videoWindowFrame = [videoWindow frame];
1569 NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
1570 NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
1571 float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
1572 float marginx = contentRect.size.width - viewRect.size.width;
1573 if( b_dark_interface && b_video_deco )
1574 marginy += [o_titlebar_view frame].size.height;
1576 proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
1579 return proposedFrameSize;
1583 #pragma mark Update interface and respond to foreign events
1584 - (void)showDropZone
1586 b_dropzone_active = YES;
1587 [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
1588 [o_dropzone_view setFrame: [o_playlist_table frame]];
1589 [[o_playlist_table animator] setHidden:YES];
1592 - (void)hideDropZone
1594 b_dropzone_active = NO;
1595 [o_dropzone_view removeFromSuperview];
1596 [[o_playlist_table animator] setHidden: NO];
1599 - (void)hideSplitView
1601 NSRect winrect = [self frame];
1602 i_lastSplitViewHeight = [o_split_view frame].size.height;
1603 winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
1604 winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
1605 [self setFrame: winrect display: YES animate: YES];
1606 [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
1607 if (b_dark_interface)
1609 [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1610 [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1614 [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height )];
1615 [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height )];
1618 b_splitview_removed = YES;
1621 - (void)showSplitView
1623 [self updateWindow];
1624 if (b_dark_interface)
1625 [self setContentMinSize:NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1627 [self setContentMinSize:NSMakeSize( 604., 288. )];
1628 [self setContentMaxSize: NSMakeSize( FLT_MAX, FLT_MAX )];
1631 winrect = [self frame];
1632 winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
1633 winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
1634 [self setFrame: winrect display: YES animate: YES];
1636 [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
1638 b_splitview_removed = NO;
1641 - (void)updateTimeSlider
1643 input_thread_t * p_input;
1644 p_input = pl_CurrentInput( VLCIntf );
1651 var_Get( p_input, "position", &pos );
1652 f_updated = 10000. * pos.f_float;
1653 [o_time_sld setFloatValue: f_updated];
1655 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1657 mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1659 [o_time_sld setEnabled: NO];
1660 [o_time_sld setHidden: YES];
1661 [o_time_sld_fancygradient_view setHidden: YES];
1663 [o_time_sld setEnabled: YES];
1664 [o_time_sld setHidden: NO];
1665 [o_time_sld_fancygradient_view setHidden: NO];
1668 [o_time_fld setStringValue: o_time];
1669 [o_time_fld setNeedsDisplay:YES];
1670 [o_fspanel setStreamPos: f_updated andTime: o_time];
1671 vlc_object_release( p_input );
1675 [o_time_sld setFloatValue: 0.0];
1676 [o_time_fld setStringValue: @"00:00"];
1677 [o_time_sld setEnabled: NO];
1678 [o_time_sld setHidden: YES];
1679 [o_time_sld_fancygradient_view setHidden: YES];
1681 [o_detached_time_sld_fancygradient_view setHidden: YES];
1686 [o_detached_time_sld setFloatValue: [o_time_sld floatValue]];
1687 [o_detached_time_sld setEnabled: [o_time_sld isEnabled]];
1688 [o_detached_time_fld setStringValue: [o_time_fld stringValue]];
1689 [o_detached_time_sld setHidden: [o_time_sld isHidden]];
1693 - (void)updateVolumeSlider
1695 int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1696 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1700 [o_volume_sld setIntValue: i_volume];
1701 [o_fspanel setVolumeLevel: i_volume];
1704 [o_volume_sld setIntValue: 0];
1706 [o_volume_sld setEnabled: !b_muted];
1707 [o_volume_up_btn setEnabled: !b_muted];
1712 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1713 input_thread_t * p_input;
1714 p_input = pl_CurrentInput( VLCIntf );
1718 char *format = var_InheritString( VLCIntf, "input-title-format" );
1719 char *formated = str_format_meta( pl_Get( VLCIntf ), format );
1721 aString = [NSString stringWithUTF8String:formated];
1724 char *uri = input_item_GetURI( input_GetItem( p_input ) );
1726 NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1727 if ([o_url isFileURL])
1729 [self setRepresentedURL: o_url];
1730 [o_detached_video_window setRepresentedURL: o_url];
1732 [self setRepresentedURL: nil];
1733 [o_detached_video_window setRepresentedURL: nil];
1737 if ([aString isEqualToString:@""])
1739 if ([o_url isFileURL])
1740 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1742 aString = [o_url absoluteString];
1745 [self setTitle: aString];
1746 [o_fspanel setStreamTitle: aString];
1747 vlc_object_release( p_input );
1751 [self setTitle: _NS("VLC media player")];
1752 [self setRepresentedURL: nil];
1758 - (void)updateWindow
1760 bool b_input = false;
1761 bool b_plmul = false;
1762 bool b_control = false;
1763 bool b_seekable = false;
1764 bool b_chapters = false;
1766 playlist_t * p_playlist = pl_Get( VLCIntf );
1769 b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1772 input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1774 bool b_buffering = NO;
1776 if( ( b_input = ( p_input != NULL ) ) )
1778 /* seekable streams */
1779 cachedInputState = input_GetState( p_input );
1780 if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1783 /* seekable streams */
1784 b_seekable = var_GetBool( p_input, "can-seek" );
1786 /* check whether slow/fast motion is possible */
1787 b_control = var_GetBool( p_input, "can-rate" );
1789 /* chapters & titles */
1790 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1792 vlc_object_release( p_input );
1797 [o_progress_bar startAnimation:self];
1798 [o_progress_bar setIndeterminate:YES];
1799 [o_progress_bar setHidden:NO];
1801 [o_progress_bar stopAnimation:self];
1802 [o_progress_bar setHidden:YES];
1805 [o_stop_btn setEnabled: b_input];
1806 [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1807 [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1808 if (b_show_jump_buttons)
1810 [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1811 [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1815 [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1816 [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1818 [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1820 [o_time_sld setEnabled: b_seekable];
1821 [self updateTimeSlider];
1822 [o_fspanel setSeekable: b_seekable];
1825 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1826 [self hideDropZone];
1828 [self showDropZone];
1830 [o_sidebar_view setNeedsDisplay:YES];
1835 [o_play_btn setImage: o_pause_img];
1836 [o_play_btn setAlternateImage: o_pause_pressed_img];
1837 [o_play_btn setToolTip: _NS("Pause")];
1840 [o_detached_play_btn setImage: o_pause_img];
1841 [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1842 [o_detached_play_btn setToolTip: _NS("Pause")];
1844 [o_fspanel setPause];
1849 [o_play_btn setImage: o_play_img];
1850 [o_play_btn setAlternateImage: o_play_pressed_img];
1851 [o_play_btn setToolTip: _NS("Play")];
1854 [o_detached_play_btn setImage: o_play_img];
1855 [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1856 [o_detached_play_btn setToolTip: _NS("Play")];
1858 [o_fspanel setPlay];
1861 - (void)drawFancyGradientEffectForTimeSlider
1863 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1864 CGFloat f_value = [o_time_sld knobPosition];
1867 NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1868 if (f_value != oldFrame.size.width)
1870 if ([o_time_sld_fancygradient_view isHidden])
1871 [o_time_sld_fancygradient_view setHidden: NO];
1872 [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1875 if (b_nonembedded && b_video_deco)
1877 f_value = [o_detached_time_sld knobPosition];
1878 oldFrame = [o_detached_time_sld_fancygradient_view frame];
1879 if (f_value != oldFrame.size.width)
1881 if ([o_detached_time_sld_fancygradient_view isHidden])
1882 [o_detached_time_sld_fancygradient_view setHidden: NO];
1883 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1890 frame = [o_time_sld_fancygradient_view frame];
1891 if (frame.size.width > 0)
1893 frame.size.width = 0;
1894 [o_time_sld_fancygradient_view setFrame: frame];
1898 frame = [o_detached_time_sld_fancygradient_view frame];
1899 frame.size.width = 0;
1900 [o_detached_time_sld_fancygradient_view setFrame: frame];
1903 [o_time_sld_fancygradient_view setHidden: YES];
1905 [o_detached_time_sld_fancygradient_view setHidden: YES];
1911 #pragma mark Video Output handling
1914 return o_video_view;
1917 - (void)setupVideoView
1919 // TODO: make lion fullscreen compatible with macosx-background and !embedded-video
1920 if( var_InheritBool( VLCIntf, "macosx-background" ) && !b_nativeFullscreenMode )
1922 msg_Dbg( VLCIntf, "Creating background window" );
1923 NSScreen *screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
1925 screen = [self screen];
1926 NSRect screen_rect = [screen frame];
1928 if( o_extra_video_window )
1929 [o_extra_video_window release];
1931 o_extra_video_window = [[VLCWindow alloc] initWithContentRect:screen_rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
1932 [o_extra_video_window setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey) + 1];
1933 [o_extra_video_window setBackgroundColor: [NSColor blackColor]];
1934 [o_extra_video_window setCanBecomeKeyWindow: NO];
1935 [o_extra_video_window setCanBecomeMainWindow: NO];
1936 [o_extra_video_window useOptimizedDrawing: YES];
1937 [o_extra_video_window setMovableByWindowBackground: NO];
1939 [o_video_view retain];
1940 if ([o_video_view superview] != NULL)
1941 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1942 screen_rect.origin.x = screen_rect.origin.y = 0;
1943 [o_video_view setFrame: screen_rect];
1944 [[o_extra_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1945 [o_video_view release];
1947 [o_extra_video_window orderBack:nil];
1949 b_nonembedded = YES;
1953 if ((var_InheritBool( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode) && b_video_deco)
1955 if ([o_video_view window] != self)
1957 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1958 [o_video_view setFrame: [o_split_view frame]];
1959 [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1965 if ([o_video_view superview] != NULL)
1966 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1969 videoFrame.size = [[o_detached_video_window contentView] frame].size;
1972 videoFrame.size.height -= [o_detached_bottombar_view frame].size.height;
1973 if( b_dark_interface )
1974 videoFrame.size.height -= [o_detached_titlebar_view frame].size.height;
1976 videoFrame.origin.x = .0;
1977 videoFrame.origin.y = [o_detached_bottombar_view frame].size.height;
1981 videoFrame.origin.y = .0;
1982 videoFrame.origin.x = .0;
1985 [o_video_view setFrame: videoFrame];
1986 [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1987 [o_detached_video_window setLevel:NSNormalWindowLevel];
1988 [o_detached_video_window useOptimizedDrawing: YES];
1989 b_nonembedded = YES;
1992 [[o_video_view window] makeKeyAndOrderFront: self];
1994 vout_thread_t *p_vout = getVout();
1997 if( var_GetBool( p_vout, "video-on-top" ) )
1998 [[o_video_view window] setLevel: NSStatusWindowLevel];
2000 [[o_video_view window] setLevel: NSNormalWindowLevel];
2001 vlc_object_release( p_vout );
2005 [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2008 - (void)setVideoplayEnabled
2010 BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
2012 if( b_videoPlayback )
2014 // look for 'start at fullscreen'
2015 [[VLCMain sharedInstance] fullscreenChanged];
2019 [self makeFirstResponder: nil];
2020 [o_detached_video_window orderOut: nil];
2021 if( o_extra_video_window )
2022 [o_extra_video_window orderOut: nil];
2024 if( [self level] != NSNormalWindowLevel )
2025 [self setLevel: NSNormalWindowLevel];
2026 if( [o_detached_video_window level] != NSNormalWindowLevel )
2027 [o_detached_video_window setLevel: NSNormalWindowLevel];
2029 // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
2030 [self setAlphaValue:1.0];
2033 if( b_nativeFullscreenMode )
2035 if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
2036 [o_bottombar_view setHidden: b_videoPlayback];
2038 [o_bottombar_view setHidden: NO];
2039 if( b_videoPlayback && b_fullscreen )
2040 [o_fspanel setActive: nil];
2041 if( !b_videoPlayback )
2042 [o_fspanel setNonActive: nil];
2045 if (!b_videoPlayback && b_fullscreen)
2047 if (!b_nativeFullscreenMode)
2048 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
2052 - (void)resizeWindow
2054 if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
2057 id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
2058 NSSize windowMinSize = [o_videoWindow minSize];
2059 NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
2061 NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
2062 NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
2064 unsigned int i_width = nativeVideoSize.width;
2065 unsigned int i_height = nativeVideoSize.height;
2066 if (i_width < windowMinSize.width)
2067 i_width = windowMinSize.width;
2068 if (i_height < f_min_video_height)
2069 i_height = f_min_video_height;
2071 /* Calculate the window's new size */
2073 new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
2074 new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
2075 new_frame.origin.x = topleftscreen.x;
2076 new_frame.origin.y = topleftscreen.y - new_frame.size.height;
2078 /* make sure the window doesn't exceed the screen size the window is on */
2079 if( new_frame.size.width > screenFrame.size.width )
2081 new_frame.size.width = screenFrame.size.width;
2082 new_frame.origin.x = screenFrame.origin.x;
2084 if( new_frame.size.height > screenFrame.size.height )
2086 new_frame.size.height = screenFrame.size.height;
2087 new_frame.origin.y = screenFrame.origin.y;
2089 if( new_frame.origin.y < screenFrame.origin.y )
2090 new_frame.origin.y = screenFrame.origin.y;
2092 CGFloat right_screen_point = screenFrame.origin.x + screenFrame.size.width;
2093 CGFloat right_window_point = new_frame.origin.x + new_frame.size.width;
2094 if( right_window_point > right_screen_point )
2095 new_frame.origin.x -= ( right_window_point - right_screen_point );
2097 [[o_videoWindow animator] setFrame:new_frame display:YES];
2100 - (void)setNativeVideoSize:(NSSize)size
2102 nativeVideoSize = size;
2104 if( var_InheritBool( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen && !var_InheritBool( VLCIntf, "macosx-background" ) )
2105 [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
2108 // Called automatically if window's acceptsMouseMovedEvents property is true
2109 - (void)mouseMoved:(NSEvent *)theEvent
2112 [self recreateHideMouseTimer];
2114 [super mouseMoved: theEvent];
2117 - (void)recreateHideMouseTimer
2119 if (t_hide_mouse_timer != nil) {
2120 [t_hide_mouse_timer invalidate];
2121 [t_hide_mouse_timer release];
2124 t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
2126 selector:@selector(hideMouseCursor:)
2129 [t_hide_mouse_timer retain];
2132 // NSTimer selectors require this function signature as per Apple's docs
2133 - (void)hideMouseCursor:(NSTimer *)timer
2135 [NSCursor setHiddenUntilMouseMoves: YES];
2139 #pragma mark Fullscreen support
2140 - (void)showFullscreenController
2142 if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
2148 return b_fullscreen;
2151 - (void)lockFullscreenAnimation
2153 [o_animation_lock lock];
2156 - (void)unlockFullscreenAnimation
2158 [o_animation_lock unlock];
2161 - (void)enterFullscreen
2163 NSMutableDictionary *dict1, *dict2;
2167 BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2168 o_current_video_window = [o_video_view window];
2170 screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
2171 [self lockFullscreenAnimation];
2175 msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
2176 screen = [o_current_video_window screen];
2180 msg_Dbg( VLCIntf, "Using deepest screen" );
2181 screen = [NSScreen deepestScreen];
2184 screen_rect = [screen frame];
2186 [o_fullscreen_btn setState: YES];
2188 [o_detached_fullscreen_btn setState: YES];
2190 [self recreateHideMouseTimer];
2192 if( blackout_other_displays )
2193 [screen blackoutOtherScreens];
2195 /* Make sure we don't see the window flashes in float-on-top mode */
2196 i_originalLevel = [o_current_video_window level];
2197 [o_current_video_window setLevel:NSNormalWindowLevel];
2199 /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
2200 if (!o_fullscreen_window)
2202 /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
2204 rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
2205 rect.origin.x += [o_current_video_window frame].origin.x;
2206 rect.origin.y += [o_current_video_window frame].origin.y;
2207 o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
2208 [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
2209 [o_fullscreen_window setCanBecomeKeyWindow: YES];
2210 [o_fullscreen_window setCanBecomeMainWindow: YES];
2212 if (![o_current_video_window isVisible] || [o_current_video_window alphaValue] == 0.0)
2214 /* We don't animate if we are not visible, instead we
2215 * simply fade the display */
2216 CGDisplayFadeReservationToken token;
2218 if( blackout_other_displays )
2220 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2221 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2224 if ([screen mainScreen])
2225 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2227 [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2228 [o_temp_view setFrame:[o_video_view frame]];
2229 [o_fullscreen_window setContentView:o_video_view];
2231 [o_fullscreen_window makeKeyAndOrderFront:self];
2232 [o_fullscreen_window orderFront:self animate:YES];
2234 [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
2235 [o_fullscreen_window setLevel:NSNormalWindowLevel];
2237 if( blackout_other_displays )
2239 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2240 CGReleaseDisplayFadeReservation( token );
2243 /* Will release the lock */
2244 [self hasBecomeFullscreen];
2249 /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2250 NSDisableScreenUpdates();
2251 [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2252 [o_temp_view setFrame:[o_video_view frame]];
2253 [o_fullscreen_window setContentView:o_video_view];
2254 [o_fullscreen_window makeKeyAndOrderFront:self];
2255 NSEnableScreenUpdates();
2258 /* We are in fullscreen (and no animation is running) */
2261 /* Make sure we are hidden */
2262 [o_current_video_window orderOut: self];
2264 [self unlockFullscreenAnimation];
2268 if (o_fullscreen_anim1)
2270 [o_fullscreen_anim1 stopAnimation];
2271 [o_fullscreen_anim1 release];
2273 if (o_fullscreen_anim2)
2275 [o_fullscreen_anim2 stopAnimation];
2276 [o_fullscreen_anim2 release];
2279 if ([screen mainScreen])
2280 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2282 dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
2283 dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
2285 [dict1 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2286 [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
2288 [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2289 [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2290 [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
2292 /* Strategy with NSAnimation allocation:
2293 - Keep at most 2 animation at a time
2294 - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
2296 o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
2297 o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
2302 [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2303 [o_fullscreen_anim1 setDuration: 0.3];
2304 [o_fullscreen_anim1 setFrameRate: 30];
2305 [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2306 [o_fullscreen_anim2 setDuration: 0.2];
2307 [o_fullscreen_anim2 setFrameRate: 30];
2309 [o_fullscreen_anim2 setDelegate: self];
2310 [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2312 [o_fullscreen_anim1 startAnimation];
2313 /* fullscreenAnimation will be unlocked when animation ends */
2316 - (void)hasBecomeFullscreen
2318 if( [[o_video_view subviews] count] > 0 )
2319 [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2321 [o_fullscreen_window makeKeyWindow];
2322 [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
2324 /* tell the fspanel to move itself to front next time it's triggered */
2325 [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
2326 [o_fspanel setActive: nil];
2328 if( [o_current_video_window isVisible] )
2329 [o_current_video_window orderOut: self];
2332 [self unlockFullscreenAnimation];
2335 - (void)leaveFullscreen
2337 [self leaveFullscreenAndFadeOut: NO];
2340 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
2342 NSMutableDictionary *dict1, *dict2;
2344 BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2346 if( !o_current_video_window )
2349 [self lockFullscreenAnimation];
2351 [o_fullscreen_btn setState: NO];
2353 [o_detached_fullscreen_btn setState: NO];
2355 /* We always try to do so */
2356 [NSScreen unblackoutScreens];
2358 vout_thread_t *p_vout = getVout();
2361 if( var_GetBool( p_vout, "video-on-top" ) )
2362 [[o_video_view window] setLevel: NSStatusWindowLevel];
2364 [[o_video_view window] setLevel: NSNormalWindowLevel];
2365 vlc_object_release( p_vout );
2367 [[o_video_view window] makeKeyAndOrderFront: nil];
2369 /* Don't do anything if o_fullscreen_window is already closed */
2370 if (!o_fullscreen_window)
2372 [self unlockFullscreenAnimation];
2378 /* We don't animate if we are not visible, instead we
2379 * simply fade the display */
2380 CGDisplayFadeReservationToken token;
2382 if( blackout_other_displays )
2384 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2385 CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2388 [o_fspanel setNonActive: nil];
2389 [NSApp setPresentationOptions: NSApplicationPresentationDefault];
2391 /* Will release the lock */
2392 [self hasEndedFullscreen];
2394 /* Our window is hidden, and might be faded. We need to workaround that, so note it
2396 b_window_is_invisible = YES;
2398 if( blackout_other_displays )
2400 CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2401 CGReleaseDisplayFadeReservation( token );
2407 [o_current_video_window setAlphaValue: 0.0];
2408 [o_current_video_window orderFront: self];
2409 [[o_video_view window] orderFront: self];
2411 [o_fspanel setNonActive: nil];
2412 [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
2414 if (o_fullscreen_anim1)
2416 [o_fullscreen_anim1 stopAnimation];
2417 [o_fullscreen_anim1 release];
2419 if (o_fullscreen_anim2)
2421 [o_fullscreen_anim2 stopAnimation];
2422 [o_fullscreen_anim2 release];
2425 frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
2426 frame.origin.x += [o_current_video_window frame].origin.x;
2427 frame.origin.y += [o_current_video_window frame].origin.y;
2429 dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
2430 [dict2 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2431 [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2433 o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
2436 [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2437 [o_fullscreen_anim2 setDuration: 0.3];
2438 [o_fullscreen_anim2 setFrameRate: 30];
2440 [o_fullscreen_anim2 setDelegate: self];
2442 dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
2444 [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2445 [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2446 [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
2448 o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
2451 [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2452 [o_fullscreen_anim1 setDuration: 0.2];
2453 [o_fullscreen_anim1 setFrameRate: 30];
2454 [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2456 /* Make sure o_fullscreen_window is the frontmost window */
2457 [o_fullscreen_window orderFront: self];
2459 [o_fullscreen_anim1 startAnimation];
2460 /* fullscreenAnimation will be unlocked when animation ends */
2463 - (void)hasEndedFullscreen
2467 /* This function is private and should be only triggered at the end of the fullscreen change animation */
2468 /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2469 NSDisableScreenUpdates();
2470 [o_video_view retain];
2471 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
2472 [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
2473 [o_video_view release];
2474 [o_video_view setFrame:[o_temp_view frame]];
2475 if( [[o_video_view subviews] count] > 0 )
2476 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2477 if( !b_nonembedded )
2478 [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
2480 [[o_video_view window] makeKeyAndOrderFront: self];
2481 [o_fullscreen_window orderOut: self];
2482 NSEnableScreenUpdates();
2484 [o_fullscreen_window release];
2485 o_fullscreen_window = nil;
2486 [[o_video_view window] setLevel:i_originalLevel];
2487 [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2489 // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2490 if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2491 [o_current_video_window orderOut: self];
2493 o_current_video_window = nil;
2494 [self unlockFullscreenAnimation];
2497 - (void)animationDidEnd:(NSAnimation*)animation
2499 NSArray *viewAnimations;
2500 if( o_makekey_anim == animation )
2502 [o_makekey_anim release];
2505 if ([animation currentValue] < 1.0)
2508 /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2509 viewAnimations = [o_fullscreen_anim2 viewAnimations];
2510 if ([viewAnimations count] >=1 &&
2511 [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2513 /* Fullscreen ended */
2514 [self hasEndedFullscreen];
2518 /* Fullscreen started */
2519 [self hasBecomeFullscreen];
2523 - (void)makeKeyAndOrderFront: (id)sender
2526 * when we exit fullscreen and fade out, we may endup in
2527 * having a window that is faded. We can't have it fade in unless we
2530 if(!b_window_is_invisible)
2532 /* Make sure we don't do it too much */
2533 [super makeKeyAndOrderFront: sender];
2537 [super setAlphaValue:0.0f];
2538 [super makeKeyAndOrderFront: sender];
2540 NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2541 [dict setObject:self forKey:NSViewAnimationTargetKey];
2542 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2544 o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2547 [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2548 [o_makekey_anim setDuration: 0.1];
2549 [o_makekey_anim setFrameRate: 30];
2550 [o_makekey_anim setDelegate: self];
2552 [o_makekey_anim startAnimation];
2553 b_window_is_invisible = NO;
2555 /* fullscreenAnimation will be unlocked when animation ends */
2559 #pragma mark Lion native fullscreen handling
2560 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2562 // workaround, see #6668
2563 [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2565 var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
2567 vout_thread_t *p_vout = getVout();
2570 var_SetBool( p_vout, "fullscreen", true );
2571 vlc_object_release( p_vout );
2574 [o_video_view setFrame: [[self contentView] frame]];
2577 [self recreateHideMouseTimer];
2578 i_originalLevel = [self level];
2579 [self setLevel:NSNormalWindowLevel];
2581 if (b_dark_interface)
2583 [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2586 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2587 winrect = [self frame];
2589 winrect.size.height = winrect.size.height - f_titleBarHeight;
2590 [self setFrame: winrect display:NO animate:NO];
2591 winrect = [o_split_view frame];
2592 winrect.size.height = winrect.size.height + f_titleBarHeight;
2593 [o_split_view setFrame: winrect];
2596 if ([[VLCMain sharedInstance] activeVideoPlayback])
2597 [o_bottombar_view setHidden: YES];
2599 [self setMovableByWindowBackground: NO];
2602 - (void)windowDidEnterFullScreen:(NSNotification *)notification
2604 // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
2605 // But this creates some problems when leaving fs over remote intfs, so activate app here.
2606 [NSApp activateIgnoringOtherApps:YES];
2608 [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2609 [o_fspanel setActive: nil];
2612 - (void)windowWillExitFullScreen:(NSNotification *)notification
2615 var_SetBool( pl_Get( VLCIntf ), "fullscreen", false );
2617 vout_thread_t *p_vout = getVout();
2620 var_SetBool( p_vout, "fullscreen", false );
2621 vlc_object_release( p_vout );
2624 [o_video_view setFrame: [o_split_view frame]];
2625 [NSCursor setHiddenUntilMouseMoves: NO];
2626 [o_fspanel setNonActive: nil];
2627 [self setLevel:i_originalLevel];
2630 if (b_dark_interface)
2633 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2634 winrect = [self frame];
2636 [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2637 winrect.size.width, f_titleBarHeight )];
2638 [[self contentView] addSubview: o_titlebar_view];
2640 winrect.size.height = winrect.size.height + f_titleBarHeight;
2641 [self setFrame: winrect display:NO animate:NO];
2642 winrect = [o_split_view frame];
2643 winrect.size.height = winrect.size.height - f_titleBarHeight;
2644 [o_split_view setFrame: winrect];
2645 [o_video_view setFrame: winrect];
2648 if ([[VLCMain sharedInstance] activeVideoPlayback])
2649 [o_bottombar_view setHidden: NO];
2651 [self setMovableByWindowBackground: YES];
2655 #pragma mark split view delegate
2656 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2658 if (dividerIndex == 0)
2664 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
2666 if (dividerIndex == 0)
2672 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2674 return ([subview isEqual:o_left_split_view]);
2677 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
2679 if ([subview isEqual:o_left_split_view])
2685 #pragma mark Side Bar Data handling
2686 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2687 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2689 //Works the same way as the NSOutlineView data source: `nil` means a parent item
2691 return [o_sidebaritems count];
2693 return [[item children] count];
2697 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2699 //Works the same way as the NSOutlineView data source: `nil` means a parent item
2701 return [o_sidebaritems objectAtIndex:index];
2703 return [[item children] objectAtIndex:index];
2707 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2709 return [item title];
2712 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2714 [item setTitle:object];
2717 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2719 return [item hasChildren];
2723 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2725 if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2728 return [item hasBadge];
2732 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2734 playlist_t * p_playlist = pl_Get( VLCIntf );
2735 NSInteger i_playlist_size;
2737 if ([[item identifier] isEqualToString: @"playlist"])
2740 i_playlist_size = p_playlist->p_local_category->i_children;
2743 return i_playlist_size;
2745 if ([[item identifier] isEqualToString: @"medialibrary"])
2748 i_playlist_size = p_playlist->p_ml_category->i_children;
2751 return i_playlist_size;
2754 return [item badgeValue];
2758 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2760 return [item hasIcon];
2764 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2769 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2771 if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2776 if ([item sdtype] > 0)
2778 m = [[NSMenu alloc] init];
2779 playlist_t * p_playlist = pl_Get( VLCIntf );
2780 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2782 [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2784 [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2785 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2787 return [m autorelease];
2794 - (IBAction)sdmenuhandler:(id)sender
2796 NSString * identifier = [sender representedObject];
2797 if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2799 playlist_t * p_playlist = pl_Get( VLCIntf );
2800 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2803 playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2805 playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2810 #pragma mark Side Bar Delegate Methods
2811 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2812 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2814 if ([[group identifier] isEqualToString:@"library"])
2820 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2822 playlist_t * p_playlist = pl_Get( VLCIntf );
2824 NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2825 id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2828 //Set the label text to represent the new selection
2829 if ([item sdtype] > -1 && [[item identifier] length] > 0)
2831 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2834 playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2838 [o_chosen_category_lbl setStringValue:[item title]];
2840 if ([[item identifier] isEqualToString:@"playlist"])
2842 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2844 else if([[item identifier] isEqualToString:@"medialibrary"])
2846 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2850 playlist_item_t * pl_item;
2852 pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2854 [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2858 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2859 [self hideDropZone];
2861 [self showDropZone];
2864 if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
2865 [self showPodcastControls];
2867 [self hidePodcastControls];
2870 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2872 if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2874 NSPasteboard *o_pasteboard = [info draggingPasteboard];
2875 if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2876 return NSDragOperationGeneric;
2878 return NSDragOperationNone;
2881 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2883 NSPasteboard *o_pasteboard = [info draggingPasteboard];
2885 playlist_t * p_playlist = pl_Get( VLCIntf );
2886 playlist_item_t *p_node;
2888 if ([[item identifier] isEqualToString:@"playlist"])
2889 p_node = p_playlist->p_local_category;
2891 p_node = p_playlist->p_ml_category;
2893 if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2895 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2896 NSUInteger count = [o_values count];
2897 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2899 for( NSUInteger i = 0; i < count; i++)
2901 NSDictionary *o_dic;
2902 char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
2906 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2910 [o_array addObject: o_dic];
2913 [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2916 else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2918 NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2920 NSUInteger count = [array count];
2921 playlist_item_t * p_item = NULL;
2924 for( NSUInteger i = 0; i < count; i++ )
2926 p_item = [[array objectAtIndex:i] pointerValue];
2927 if( !p_item ) continue;
2928 playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2937 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2939 return [item identifier];
2942 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2944 /* the following code assumes for sakes of simplicity that only the top level
2945 * items are allowed to have children */
2947 NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2948 NSUInteger count = [array count];
2952 for (NSUInteger x = 0; x < count; x++)
2954 id item = [array objectAtIndex: x]; // save one objc selector call
2955 if ([[item identifier] isEqualToString:object])
2963 #pragma mark Podcast
2965 - (IBAction)addPodcast:(id)sender
2967 [NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
2970 - (IBAction)addPodcastWindowAction:(id)sender
2972 [o_podcast_subscribe_window orderOut:sender];
2973 [NSApp endSheet: o_podcast_subscribe_window];
2975 if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
2976 NSMutableString * podcastConf = [[NSMutableString alloc] init];
2977 if (config_GetPsz( VLCIntf, "podcast-urls" ) != NULL)
2978 [podcastConf appendFormat:@"%s|", config_GetPsz( VLCIntf, "podcast-urls" )];
2980 [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
2981 config_PutPsz( VLCIntf, "podcast-urls", [podcastConf UTF8String] );
2983 vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( VLCIntf->p_libvlc, "podcast" );
2985 var_SetString( p_obj, "podcast-urls", [podcastConf UTF8String] );
2986 vlc_object_release( p_obj );
2988 [podcastConf release];
2992 - (IBAction)removePodcast:(id)sender
2994 if (config_GetPsz( VLCIntf, "podcast-urls" ) != NULL) {
2995 [o_podcast_unsubscribe_pop removeAllItems];
2996 [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz( VLCIntf, "podcast-urls" )] componentsSeparatedByString:@"|"]];
2997 [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
3001 - (IBAction)removePodcastWindowAction:(id)sender
3003 [o_podcast_unsubscribe_window orderOut:sender];
3004 [NSApp endSheet: o_podcast_unsubscribe_window];
3006 if (sender == o_podcast_unsubscribe_ok_btn) {
3007 NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz( VLCIntf, "podcast-urls" )] componentsSeparatedByString:@"|"]];
3008 [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
3009 config_PutPsz( VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String] );
3012 vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( VLCIntf->p_libvlc, "podcast" );
3014 var_SetString( p_obj, "podcast-urls", config_GetPsz( VLCIntf, "podcast-urls" ) );
3015 vlc_object_release( p_obj );
3018 /* reload the podcast module, since it won't update its list when removing podcasts */
3019 playlist_t * p_playlist = pl_Get( VLCIntf );
3020 if( playlist_IsServicesDiscoveryLoaded( p_playlist, "podcast{longname=\"Podcasts\"}" ) ) {
3021 playlist_ServicesDiscoveryRemove( p_playlist, "podcast{longname=\"Podcasts\"}" );
3022 playlist_ServicesDiscoveryAdd( p_playlist, "podcast{longname=\"Podcasts\"}" );
3023 [o_playlist_table reloadData];
3029 - (void)showPodcastControls
3031 NSRect podcastViewDimensions = [o_podcast_view frame];
3032 NSRect rightSplitRect = [o_right_split_view frame];
3033 NSRect playlistTableRect = [o_playlist_table frame];
3035 podcastViewDimensions.size.width = rightSplitRect.size.width;
3036 podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
3037 [o_podcast_view setFrame:podcastViewDimensions];
3039 playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
3040 playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
3041 [o_playlist_table setFrame:playlistTableRect];
3042 [o_playlist_table setNeedsDisplay:YES];
3044 [o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
3045 b_podcastView_displayed = YES;
3048 - (void)hidePodcastControls
3050 if (b_podcastView_displayed) {
3051 NSRect podcastViewDimensions = [o_podcast_view frame];
3052 NSRect playlistTableRect = [o_playlist_table frame];
3054 playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
3055 playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
3057 [o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
3058 [o_playlist_table setFrame: playlistTableRect];
3059 b_podcastView_displayed = NO;
3064 #pragma mark Accessibility stuff
3066 - (NSArray *)accessibilityAttributeNames
3068 if( !b_dark_interface )
3069 return [super accessibilityAttributeNames];
3071 static NSMutableArray *attributes = nil;
3072 if ( attributes == nil ) {
3073 attributes = [[super accessibilityAttributeNames] mutableCopy];
3074 NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3075 NSAccessibilityCloseButtonAttribute,
3076 NSAccessibilityMinimizeButtonAttribute,
3077 NSAccessibilityZoomButtonAttribute,
3080 for( NSString *attribute in appendAttributes )
3082 if( ![attributes containsObject:attribute] )
3083 [attributes addObject:attribute];
3089 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3091 if( b_dark_interface )
3093 VLCMainWindowTitleView *o_tbv = o_titlebar_view;
3095 if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3096 return NSAccessibilityStandardWindowSubrole;
3098 if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3099 return [[o_tbv closeButton] cell];
3101 if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3102 return [[o_tbv minimizeButton] cell];
3104 if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3105 return [[o_tbv zoomButton] cell];
3108 return [super accessibilityAttributeValue: o_attribute_name];
3111 - (id)detachedTitlebarView
3113 return o_detached_titlebar_view;
3117 @implementation VLCDetachedVideoWindow
3119 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
3120 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
3122 b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
3123 b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
3125 if (b_dark_interface || !b_video_deco)
3127 #ifdef MAC_OS_X_VERSION_10_7
3128 if (!OSX_SNOW_LEOPARD)
3129 styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
3131 styleMask = NSBorderlessWindowMask;
3133 styleMask = NSBorderlessWindowMask;
3137 self = [super initWithContentRect:contentRect styleMask:styleMask
3138 backing:backingType defer:flag];
3140 /* we want to be moveable regardless of our style */
3141 [self setMovableByWindowBackground: YES];
3143 /* we don't want this window to be restored on relaunch */
3144 if (!OSX_SNOW_LEOPARD)
3145 [self setRestorable:NO];
3150 - (void)awakeFromNib
3152 [self setAcceptsMouseMovedEvents: YES];
3154 if (b_dark_interface)
3156 [self setBackgroundColor: [NSColor clearColor]];
3157 [self setOpaque: NO];
3159 [self setHasShadow:NO];
3160 [self setHasShadow:YES];
3164 - (IBAction)fullscreen:(id)sender
3166 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
3169 - (void)performClose:(id)sender
3171 if (b_dark_interface || !b_video_deco)
3172 [[VLCMainWindow sharedInstance] performClose: sender];
3174 [super performClose: sender];
3177 - (void)performMiniaturize:(id)sender
3179 if (b_dark_interface || !b_video_deco)
3180 [self miniaturize: sender];
3182 [super performMiniaturize: sender];
3185 - (void)performZoom:(id)sender
3187 if (b_dark_interface || !b_video_deco)
3188 [self customZoom: sender];
3190 [super performZoom: sender];
3193 - (void)zoom:(id)sender
3195 if (b_dark_interface || !b_video_deco)
3196 [self customZoom: sender];
3198 [super zoom: sender];
3201 - (BOOL)canBecomeKeyWindow
3206 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
3208 SEL s_menuAction = [menuItem action];
3210 if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
3213 return [super validateMenuItem:menuItem];
3217 * Given a proposed frame rectangle, return a modified version
3218 * which will fit inside the screen.
3220 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3221 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3222 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3223 * Copyright (C) 1996 Free Software Foundation, Inc.
3225 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
3227 NSRect screenRect = [screen visibleFrame];
3230 /* Move top edge of the window inside the screen */
3231 difference = NSMaxY (frameRect) - NSMaxY (screenRect);
3234 frameRect.origin.y -= difference;
3237 /* If the window is resizable, resize it (if needed) so that the
3238 bottom edge is on the screen or can be on the screen when the user moves
3240 difference = NSMaxY (screenRect) - NSMaxY (frameRect);
3241 if (_styleMask & NSResizableWindowMask)
3245 difference2 = screenRect.origin.y - frameRect.origin.y;
3246 difference2 -= difference;
3247 // Take in account the space between the top of window and the top of the
3248 // screen which can be used to move the bottom of the window on the screen
3249 if (difference2 > 0)
3251 frameRect.size.height -= difference2;
3252 frameRect.origin.y += difference2;
3255 /* Ensure that resizing doesn't makewindow smaller than minimum */
3256 difference2 = [self minSize].height - frameRect.size.height;
3257 if (difference2 > 0)
3259 frameRect.size.height += difference2;
3260 frameRect.origin.y -= difference2;
3270 Zooms the receiver. This method calls the delegate method
3271 windowShouldZoom:toFrame: to determine if the window should
3272 be allowed to zoom to full screen.
3274 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3275 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3276 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3277 * Copyright (C) 1996 Free Software Foundation, Inc.
3279 - (void) customZoom: (id)sender
3281 NSRect maxRect = [[self screen] visibleFrame];
3282 NSRect currentFrame = [self frame];
3284 if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
3286 maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
3289 maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
3291 // Compare the new frame with the current one
3292 if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
3293 && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
3294 && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
3295 && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
3297 // Already in zoomed mode, reset user frame, if stored
3298 if ([self frameAutosaveName] != nil)
3300 [self setFrame: previousSavedFrame display: YES animate: YES];
3301 [self saveFrameUsingName: [self frameAutosaveName]];
3306 if ([self frameAutosaveName] != nil)
3308 [self saveFrameUsingName: [self frameAutosaveName]];
3309 previousSavedFrame = [self frame];
3312 [self setFrame: maxRect display: YES animate: YES];
3315 - (NSArray *)accessibilityAttributeNames
3317 if( !b_dark_interface )
3318 return [super accessibilityAttributeNames];
3320 static NSMutableArray *attributes = nil;
3321 if ( attributes == nil ) {
3322 attributes = [[super accessibilityAttributeNames] mutableCopy];
3323 NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3324 NSAccessibilityCloseButtonAttribute,
3325 NSAccessibilityMinimizeButtonAttribute,
3326 NSAccessibilityZoomButtonAttribute,
3329 for( NSString *attribute in appendAttributes )
3331 if( ![attributes containsObject:attribute] )
3332 [attributes addObject:attribute];
3338 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3340 if( b_dark_interface )
3342 VLCMainWindowTitleView *o_tbv = [[VLCMainWindow sharedInstance] detachedTitlebarView];
3344 if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3345 return NSAccessibilityStandardWindowSubrole;
3347 if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3348 return [[o_tbv closeButton] cell];
3350 if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3351 return [[o_tbv minimizeButton] cell];
3353 if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3354 return [[o_tbv zoomButton] cell];
3357 return [super accessibilityAttributeValue: o_attribute_name];