1 /*****************************************************************************
2 * MainWindow.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2012 VLC authors and VideoLAN
7 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8 * Jon Lech Johansen <jon-vl@nanocrew.net>
9 * Christophe Massiot <massiot@via.ecp.fr>
10 * Derk-Jan Hartman <hartman at videolan.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 #import "CompatibilityFixes.h"
28 #import "MainWindow.h"
30 #import "CoreInteraction.h"
31 #import "AudioEffects.h"
34 #import "controls.h" // TODO: remove me
36 #import "SideBarItem.h"
38 #import <vlc_playlist.h>
39 #import <vlc_aout_intf.h>
41 #import <vlc_strings.h>
42 #import <vlc_services_discovery.h>
43 #import <vlc_aout_intf.h>
45 @interface VLCMainWindow ()
46 - (void)addJumpButtons:(BOOL)b_fast;
47 - (void)removeJumpButtons:(BOOL)b_fast;
48 - (void)addPlaymodeButtons:(BOOL)b_fast;
49 - (void)removePlaymodeButtons:(BOOL)b_fast;
51 - (void)resetPreviousButton;
52 - (void)resetBackwardSkip;
53 - (void)resetNextButton;
54 - (void)resetForwardSkip;
55 - (void)resizePlaylistAfterCollapse;
56 - (void)makeSplitViewVisible;
57 - (void)makeSplitViewHidden;
59 - (NSRect)customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen;
62 @implementation VLCMainWindow
63 static const float f_min_video_height = 70.0;
65 static VLCMainWindow *_o_sharedInstance = nil;
67 + (VLCMainWindow *)sharedInstance
69 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
73 #pragma mark Initialization
77 if( _o_sharedInstance )
80 return _o_sharedInstance;
84 _o_sharedInstance = [super init];
87 return _o_sharedInstance;
90 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
91 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
93 b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
97 #ifdef MAC_OS_X_VERSION_10_7
99 styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
101 styleMask = NSBorderlessWindowMask;
103 styleMask = NSBorderlessWindowMask;
107 self = [super initWithContentRect:contentRect styleMask:styleMask
108 backing:backingType defer:flag];
109 _o_sharedInstance = self;
111 [[VLCMain sharedInstance] updateTogglePlaylistState];
113 /* we want to be moveable regardless of our style */
114 [self setMovableByWindowBackground: YES];
116 /* we don't want this window to be restored on relaunch */
118 [self setRestorable:NO];
123 - (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
128 key = config_GetPsz( VLCIntf, keyString );
129 o_key = [NSString stringWithFormat:@"%s", key];
132 unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
134 NSString * characters = [o_event charactersIgnoringModifiers];
135 if ([characters length] > 0) {
136 return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
137 (i_keyModifiers & NSShiftKeyMask) == ([o_event modifierFlags] & NSShiftKeyMask) &&
138 (i_keyModifiers & NSControlKeyMask) == ([o_event modifierFlags] & NSControlKeyMask) &&
139 (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
140 (i_keyModifiers & NSCommandKeyMask) == ([o_event modifierFlags] & NSCommandKeyMask);
145 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
148 // these are key events which should be handled by vlc core, but are attached to a main menu item
149 if( ![self isEvent: o_event forKey: "key-vol-up"] &&
150 ![self isEvent: o_event forKey: "key-vol-down"] &&
151 ![self isEvent: o_event forKey: "key-vol-mute"] )
153 /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
154 so we perform the menu equivalent now. */
155 if([[NSApp mainMenu] performKeyEquivalent:o_event])
161 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
162 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
167 if (b_dark_interface)
168 [o_color_backdrop release];
170 [[NSNotificationCenter defaultCenter] removeObserver: self];
171 [o_sidebaritems release];
173 if( o_extra_video_window )
175 [o_extra_video_window release];
176 o_extra_video_window = nil;
184 BOOL b_splitviewShouldBeHidden = NO;
186 /* setup the styled interface */
187 b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
188 b_nativeFullscreenMode = NO;
189 #ifdef MAC_OS_X_VERSION_10_7
190 if( OSX_LION && b_video_deco )
191 b_nativeFullscreenMode = var_InheritBool( VLCIntf, "macosx-nativefullscreenmode" );
193 t_hide_mouse_timer = nil;
194 [o_detached_video_window setDelegate: self];
195 [self useOptimizedDrawing: YES];
197 [o_play_btn setToolTip: _NS("Play/Pause")];
198 [[o_play_btn cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
199 [o_detached_play_btn setToolTip: [o_play_btn toolTip]];
200 [[o_detached_play_btn cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
201 [o_bwd_btn setToolTip: _NS("Backward")];
202 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold pressed to skip backwards through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
203 [o_detached_bwd_btn setToolTip: [o_bwd_btn toolTip]];
204 [[o_detached_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold pressed to skip backwards through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
205 [o_fwd_btn setToolTip: _NS("Forward")];
206 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold pressed to skip forwards through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
207 [o_detached_fwd_btn setToolTip: [o_fwd_btn toolTip]];
208 [[o_detached_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold pressed to skip forwards through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
209 [o_stop_btn setToolTip: _NS("Stop")];
210 [[o_stop_btn cell] accessibilitySetOverrideValue:_NS("Click to stop playback.") forAttribute:NSAccessibilityDescriptionAttribute];
211 [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
212 [[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];
213 [o_repeat_btn setToolTip: _NS("Repeat")];
214 [[o_repeat_btn cell] accessibilitySetOverrideValue:_NS("Click to change repeat mode. There are 3 states: repeat one, repeat all and off.") forAttribute:NSAccessibilityDescriptionAttribute];
215 [o_shuffle_btn setToolTip: _NS("Shuffle")];
216 [[o_shuffle_btn cell] accessibilitySetOverrideValue:_NS("Click to enable or disable random playback.") forAttribute:NSAccessibilityDescriptionAttribute];
217 [o_effects_btn setToolTip: _NS("Effects")];
218 [[o_effects_btn cell] accessibilitySetOverrideValue:_NS("Click to show an Audio Effects panel featuring an equalizer and further filters.") forAttribute:NSAccessibilityDescriptionAttribute];
219 [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
220 [[o_fullscreen_btn cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
221 [o_detached_fullscreen_btn setToolTip: [o_fullscreen_btn toolTip]];
222 [[o_detached_fullscreen_btn cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
223 [[o_search_fld cell] setPlaceholderString: _NS("Search")];
224 [[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
225 [o_volume_sld setToolTip: _NS("Volume")];
226 [[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];
227 [o_volume_down_btn setToolTip: _NS("Mute")];
228 [[o_volume_down_btn cell] accessibilitySetOverrideValue:_NS("Click to mute or unmute the audio.") forAttribute:NSAccessibilityDescriptionAttribute];
229 [o_volume_up_btn setToolTip: _NS("Full Volume")];
230 [[o_volume_up_btn cell] accessibilitySetOverrideValue:_NS("Click to play the audio at maximum volume.") forAttribute:NSAccessibilityDescriptionAttribute];
231 [o_time_sld setToolTip: _NS("Position")];
232 [[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];
233 [o_detached_time_sld setToolTip: [o_time_sld toolTip]];
234 [[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];
235 [o_dropzone_btn setTitle: _NS("Open media...")];
236 [[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];
237 [o_dropzone_lbl setStringValue: _NS("Drop media here")];
239 if (!b_dark_interface) {
240 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
241 [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
242 [o_bwd_btn setImage: [NSImage imageNamed:@"back"]];
243 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
244 [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back"]];
245 [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
246 o_play_img = [[NSImage imageNamed:@"play"] retain];
247 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
248 o_pause_img = [[NSImage imageNamed:@"pause"] retain];
249 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
250 [o_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
251 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
252 [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
253 [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
254 [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
255 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
256 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
257 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
258 o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
259 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
260 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all"] retain];
261 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
262 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
263 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
264 o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
265 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
266 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
267 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
268 [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"]];
269 [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"]];
270 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
271 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
272 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
273 if (b_nativeFullscreenMode)
275 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
276 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue"]];
280 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
281 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
283 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
284 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
285 [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
286 [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
287 [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
288 [o_detached_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
292 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
293 [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
294 [o_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
295 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
296 [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
297 [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
298 o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
299 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
300 o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
301 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
302 [o_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
303 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
304 [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
305 [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
306 [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
307 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
308 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
309 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
310 o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
311 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
312 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
313 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
314 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
315 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
316 o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
317 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
318 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
319 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
320 [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"]];
321 [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"]];
322 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
323 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
324 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
325 if (b_nativeFullscreenMode)
327 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
328 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue_dark"]];
332 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
333 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
335 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
336 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
337 [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button_dark"]];
338 [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
339 [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"]];
340 [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"]];
342 [o_repeat_btn setImage: o_repeat_img];
343 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
344 [o_shuffle_btn setImage: o_shuffle_img];
345 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
346 [o_play_btn setImage: o_play_img];
347 [o_play_btn setAlternateImage: o_play_pressed_img];
348 [o_detached_play_btn setImage: o_play_img];
349 [o_detached_play_btn setAlternateImage: o_play_pressed_img];
350 BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
351 [o_volume_sld setEnabled: b_mute];
352 [o_volume_up_btn setEnabled: b_mute];
354 b_show_jump_buttons = config_GetInt( VLCIntf, "macosx-show-playback-buttons" );
355 if (b_show_jump_buttons)
356 [self addJumpButtons:YES];
358 b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
359 if (!b_show_playmode_buttons)
360 [self removePlaymodeButtons:YES];
362 /* interface builder action */
363 float f_threshold_height = f_min_video_height + [o_bottombar_view frame].size.height;
364 if( b_dark_interface )
365 f_threshold_height += [o_titlebar_view frame].size.height;
366 if( [[self contentView] frame].size.height < f_threshold_height )
367 b_splitviewShouldBeHidden = YES;
369 [self setDelegate: self];
370 [self setExcludedFromWindowsMenu: YES];
371 [self setAcceptsMouseMovedEvents: YES];
372 // Set that here as IB seems to be buggy
373 if (b_dark_interface && b_video_deco)
375 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
376 [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 )];
378 else if( b_video_deco )
380 [self setContentMinSize:NSMakeSize(604., 288.)];
381 [o_detached_video_window setContentMinSize: NSMakeSize( 363., f_min_video_height + [o_detached_bottombar_view frame].size.height )];
385 if (b_dark_interface)
386 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
388 [self setContentMinSize:NSMakeSize(604., 288.)];
390 [o_detached_bottombar_view setHidden:YES];
391 [o_detached_video_window setContentMinSize: NSMakeSize( f_min_video_height, f_min_video_height )];
394 [self setTitle: _NS("VLC media player")];
395 [o_time_fld setAlignment: NSCenterTextAlignment];
396 [o_time_fld setNeedsDisplay:YES];
397 b_dropzone_active = YES;
398 o_temp_view = [[NSView alloc] init];
399 [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
400 [o_dropzone_view setFrame: [o_playlist_table frame]];
401 [o_left_split_view setFrame: [o_sidebar_view frame]];
402 if (b_nativeFullscreenMode)
405 [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
406 float f_width = [o_fullscreen_btn frame].size.width;
408 #define moveItem( item ) \
409 frame = [item frame]; \
410 frame.origin.x = f_width + frame.origin.x; \
411 [item setFrame: frame]
413 moveItem( o_effects_btn );
414 moveItem( o_volume_up_btn );
415 moveItem( o_volume_sld );
416 moveItem( o_volume_track_view );
417 moveItem( o_volume_down_btn );
418 moveItem( o_time_fld );
421 #define enlargeItem( item ) \
422 frame = [item frame]; \
423 frame.size.width = f_width + frame.size.width; \
424 [item setFrame: frame]
426 enlargeItem( o_time_sld );
427 enlargeItem( o_progress_bar );
428 enlargeItem( o_time_sld_background );
429 enlargeItem( o_time_sld_fancygradient_view );
432 [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
436 [o_titlebar_view setFullscreenButtonHidden: YES];
438 [o_detached_titlebar_view setFullscreenButtonHidden: YES];
443 /* the default small size of the search field is slightly different on Lion, let's work-around that */
445 frame = [o_search_fld frame];
446 frame.origin.y = frame.origin.y + 2.0;
447 frame.size.height = frame.size.height - 1.0;
448 [o_search_fld setFrame: frame];
451 /* create the sidebar */
452 o_sidebaritems = [[NSMutableArray alloc] init];
453 SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
454 SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
455 [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
456 SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
457 [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
458 SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
459 SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
460 SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
461 SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
463 /* SD subnodes, inspired by the Qt4 intf */
464 char **ppsz_longnames;
466 char **ppsz_names = vlc_sd_GetNames( pl_Get( VLCIntf ), &ppsz_longnames, &p_categories );
468 msg_Err( VLCIntf, "no sd item found" ); //TODO
469 char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
470 int *p_category = p_categories;
471 NSMutableArray *internetItems = [[NSMutableArray alloc] init];
472 NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
473 NSMutableArray *lanItems = [[NSMutableArray alloc] init];
474 NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
475 NSString *o_identifier;
476 for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++)
478 o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
479 switch (*p_category) {
480 case SD_CAT_INTERNET:
482 [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
483 if (!strncmp( *ppsz_name, "podcast", 7 ))
484 [internetItems removeLastObject]; // we don't support podcasts at this point (see #6017)
485 // [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
487 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
488 [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
489 [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
494 [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
495 [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
496 [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
497 [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
502 [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
503 [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
504 [[lanItems lastObject] setSdtype: SD_CAT_LAN];
505 [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
508 case SD_CAT_MYCOMPUTER:
510 [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
511 if (!strncmp( *ppsz_name, "video_dir", 9 ))
512 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
513 else if (!strncmp( *ppsz_name, "audio_dir", 9 ))
514 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
515 else if (!strncmp( *ppsz_name, "picture_dir", 11 ))
516 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
518 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
519 [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
520 [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
524 msg_Warn( VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name );
529 free( *ppsz_longname );
531 [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
532 [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
533 [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
534 [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
535 [mycompItems release];
536 [devicesItems release];
538 [internetItems release];
540 free( ppsz_longnames );
541 free( p_categories );
543 [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
544 [o_sidebaritems addObject: libraryItem];
545 if ([mycompItem hasChildren])
546 [o_sidebaritems addObject: mycompItem];
547 if ([devicesItem hasChildren])
548 [o_sidebaritems addObject: devicesItem];
549 if ([lanItem hasChildren])
550 [o_sidebaritems addObject: lanItem];
551 if ([internetItem hasChildren])
552 [o_sidebaritems addObject: internetItem];
554 [o_sidebar_view reloadData];
555 [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
556 [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
557 [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
559 [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
560 [(PXSourceList *)o_sidebar_view setDataSource:self];
561 [o_sidebar_view setDelegate:self];
562 [o_sidebar_view setAutosaveExpandedItems:YES];
564 [o_sidebar_view expandItem: libraryItem expandChildren: YES];
566 /* make sure we display the desired default appearance when VLC launches for the first time */
567 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
568 if (![defaults objectForKey:@"VLCFirstRun"])
570 [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
572 NSUInteger i_sidebaritem_count = [o_sidebaritems count];
573 for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
574 [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
577 if( b_dark_interface )
579 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
580 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
582 [self setBackgroundColor: [NSColor clearColor]];
583 [self setOpaque: NO];
585 [self setHasShadow:NO];
586 [self setHasShadow:YES];
588 NSRect winrect = [self frame];
589 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
591 [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
592 winrect.size.width, f_titleBarHeight )];
593 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
595 if (winrect.size.height > 100)
597 [self setFrame: winrect display:YES animate:YES];
598 previousSavedFrame = winrect;
601 winrect = [o_split_view frame];
602 winrect.size.height = winrect.size.height - f_titleBarHeight;
603 [o_split_view setFrame: winrect];
604 [o_video_view setFrame: winrect];
606 /* detached video window */
607 winrect = [o_detached_video_window frame];
610 [o_detached_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight )];
611 [[o_detached_video_window contentView] addSubview: o_detached_titlebar_view positioned: NSWindowAbove relativeTo: nil];
614 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
615 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
616 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
621 frame = [o_time_sld_fancygradient_view frame];
622 frame.size.height = frame.size.height - 1;
623 frame.origin.y = frame.origin.y + 1;
624 [o_time_sld_fancygradient_view setFrame: frame];
626 frame = [o_detached_time_sld_fancygradient_view frame];
627 frame.size.height = frame.size.height - 1;
628 frame.origin.y = frame.origin.y + 1;
629 [o_detached_time_sld_fancygradient_view setFrame: frame];
631 [o_video_view setFrame: [o_split_view frame]];
632 [o_playlist_table setBorderType: NSNoBorder];
633 [o_sidebar_scrollview setBorderType: NSNoBorder];
637 frame = [o_time_sld_fancygradient_view frame];
638 frame.size.width = 0;
639 [o_time_sld_fancygradient_view setFrame: frame];
641 frame = [o_detached_time_sld_fancygradient_view frame];
642 frame.size.width = 0;
643 [o_detached_time_sld_fancygradient_view setFrame: frame];
647 [o_resize_view setImage: NULL];
648 [o_detached_resize_view setImage: NULL];
651 if ([self styleMask] & NSResizableWindowMask)
653 [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
654 [o_detached_resize_view removeFromSuperviewWithoutNeedingDisplay];
657 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
658 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
659 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
660 [[VLCMain sharedInstance] playbackModeUpdated];
662 [o_split_view setAutosaveName:@"10thanniversary-splitview"];
663 if (b_splitviewShouldBeHidden)
665 [self hideSplitView];
666 i_lastSplitViewHeight = 300;
669 /* sanity check for the window size */
670 frame = [self frame];
671 NSSize screenSize = [[self screen] frame].size;
672 if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
673 nativeVideoSize = screenSize;
679 #pragma mark interface customization
680 - (void)toggleJumpButtons
682 b_show_jump_buttons = config_GetInt( VLCIntf, "macosx-show-playback-buttons" );
684 if (b_show_jump_buttons)
685 [self addJumpButtons:NO];
687 [self removeJumpButtons:NO];
690 - (void)addJumpButtons:(BOOL)b_fast
692 NSRect preliminaryFrame = [o_bwd_btn frame];
693 BOOL b_enabled = [o_bwd_btn isEnabled];
694 preliminaryFrame.size.width = 26.;
695 o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
696 [o_prev_btn setButtonType: NSMomentaryChangeButton];
697 [o_prev_btn setImage: [NSImage imageNamed:@"back-single"]];
698 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"back-pressed-single"]];
699 [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
700 [o_prev_btn setBordered:NO];
701 [o_prev_btn setTarget:self];
702 [o_prev_btn setAction:@selector(prev:)];
703 [o_prev_btn setToolTip: _NS("Previous")];
704 [o_prev_btn setEnabled: b_enabled];
706 o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
707 [o_next_btn setButtonType: NSMomentaryChangeButton];
708 [o_next_btn setImage: [NSImage imageNamed:@"forward-single"]];
709 [o_next_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed-single"]];
710 [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
711 [o_next_btn setBordered:NO];
712 [o_next_btn setTarget:self];
713 [o_next_btn setAction:@selector(next:)];
714 [o_next_btn setToolTip: _NS("Next")];
715 [o_next_btn setEnabled: b_enabled];
719 #define moveItem( item ) \
720 frame = [item frame]; \
721 frame.origin.x = frame.origin.x + f_space; \
723 [item setFrame: frame]; \
725 [[item animator] setFrame: frame]
727 moveItem( o_bwd_btn );
728 moveItem( o_play_btn );
729 moveItem( o_fwd_btn );
731 moveItem( o_stop_btn );
732 moveItem( o_playlist_btn );
733 moveItem( o_repeat_btn );
734 moveItem( o_shuffle_btn );
737 #define resizeItem( item ) \
738 frame = [item frame]; \
739 frame.size.width = frame.size.width - f_space; \
740 frame.origin.x = frame.origin.x + f_space; \
742 [item setFrame: frame]; \
744 [[item animator] setFrame: frame]
746 resizeItem( o_time_sld );
747 resizeItem( o_progress_bar );
748 resizeItem( o_time_sld_background );
749 resizeItem( o_time_sld_fancygradient_view );
752 preliminaryFrame.origin.x = [o_next_btn frame].origin.x + 80. + [o_fwd_btn frame].size.width;
753 [o_next_btn setFrame: preliminaryFrame];
755 // wait until the animation is done, if displayed
757 [[self contentView] addSubview:o_prev_btn];
758 [[self contentView] addSubview:o_next_btn];
760 [[self contentView] performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
761 [[self contentView] performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
764 [o_fwd_btn setAction:@selector(forward:)];
765 [o_bwd_btn setAction:@selector(backward:)];
768 - (void)removeJumpButtons:(BOOL)b_fast
770 if (!o_prev_btn || !o_next_btn )
774 [o_prev_btn setHidden: YES];
775 [o_next_btn setHidden: YES];
777 [[o_prev_btn animator] setHidden: YES];
778 [[o_next_btn animator] setHidden: YES];
780 [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
781 [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
782 [o_prev_btn release];
783 [o_next_btn release];
787 #define moveItem( item ) \
788 frame = [item frame]; \
789 frame.origin.x = frame.origin.x - f_space; \
791 [item setFrame: frame]; \
793 [[item animator] setFrame: frame]
795 moveItem( o_bwd_btn );
796 moveItem( o_play_btn );
797 moveItem( o_fwd_btn );
799 moveItem( o_stop_btn );
800 moveItem( o_playlist_btn );
801 moveItem( o_repeat_btn );
802 moveItem( o_shuffle_btn );
805 #define resizeItem( item ) \
806 frame = [item frame]; \
807 frame.size.width = frame.size.width + f_space; \
808 frame.origin.x = frame.origin.x - f_space; \
810 [item setFrame: frame]; \
812 [[item animator] setFrame: frame]
814 resizeItem( o_time_sld );
815 resizeItem( o_progress_bar );
816 resizeItem( o_time_sld_background );
817 resizeItem( o_time_sld_fancygradient_view );
820 [o_bottombar_view setNeedsDisplay:YES];
822 [o_fwd_btn setAction:@selector(fwd:)];
823 [o_bwd_btn setAction:@selector(bwd:)];
826 - (void)togglePlaymodeButtons
828 b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
830 if (b_show_playmode_buttons)
831 [self addPlaymodeButtons:NO];
833 [self removePlaymodeButtons:NO];
836 - (void)addPlaymodeButtons:(BOOL)b_fast
839 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
841 // FIXME: switch o_playlist_btn artwork
843 #define resizeItem( item ) \
844 frame = [item frame]; \
845 frame.size.width = frame.size.width - f_space; \
846 frame.origin.x = frame.origin.x + f_space; \
848 [item setFrame: frame]; \
850 [[item animator] setFrame: frame]
852 resizeItem( o_time_sld );
853 resizeItem( o_progress_bar );
854 resizeItem( o_time_sld_background );
855 resizeItem( o_time_sld_fancygradient_view );
859 [o_repeat_btn setHidden: NO];
860 [o_shuffle_btn setHidden: NO];
862 [[o_repeat_btn animator] setHidden: NO];
863 [[o_shuffle_btn animator] setHidden: NO];
867 - (void)removePlaymodeButtons:(BOOL)b_fast
870 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
871 [o_repeat_btn setHidden: YES];
872 [o_shuffle_btn setHidden: YES];
874 // FIXME: switch o_playlist_btn artwork
876 #define resizeItem( item ) \
877 frame = [item frame]; \
878 frame.size.width = frame.size.width + f_space; \
879 frame.origin.x = frame.origin.x - f_space; \
881 [item setFrame: frame]; \
883 [[item animator] setFrame: frame]
885 resizeItem( o_time_sld );
886 resizeItem( o_progress_bar );
887 resizeItem( o_time_sld_background );
888 resizeItem( o_time_sld_fancygradient_view );
893 #pragma mark Button Actions
895 - (IBAction)play:(id)sender
897 [[VLCCoreInteraction sharedInstance] play];
900 - (void)resetPreviousButton
902 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
903 // seems like no further event occurred, so let's switch the playback item
904 [[VLCCoreInteraction sharedInstance] previous];
905 just_triggered_previous = NO;
909 - (void)resetBackwardSkip
911 // the user stopped skipping, so let's allow him to change the item
912 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
913 just_triggered_previous = NO;
916 - (IBAction)prev:(id)sender
918 [[VLCCoreInteraction sharedInstance] previous];
921 - (IBAction)bwd:(id)sender
923 if(!just_triggered_previous)
925 just_triggered_previous = YES;
926 [self performSelector:@selector(resetPreviousButton)
932 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
934 // we just skipped 4 "continous" events, otherwise we are too fast
935 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
936 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
937 [self performSelector:@selector(resetBackwardSkip)
944 - (IBAction)backward:(id)sender
946 [[VLCCoreInteraction sharedInstance] backwardShort];
949 - (void)resetNextButton
951 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
952 // seems like no further event occurred, so let's switch the playback item
953 [[VLCCoreInteraction sharedInstance] next];
954 just_triggered_next = NO;
958 - (void)resetForwardSkip
960 // the user stopped skipping, so let's allow him to change the item
961 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
962 just_triggered_next = NO;
965 - (IBAction)next:(id)sender
967 [[VLCCoreInteraction sharedInstance] next];
970 - (IBAction)forward:(id)sender
972 [[VLCCoreInteraction sharedInstance] forwardShort];
975 - (IBAction)fwd:(id)sender
977 if(!just_triggered_next)
979 just_triggered_next = YES;
980 [self performSelector:@selector(resetNextButton)
986 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
988 // we just skipped 4 "continous" events, otherwise we are too fast
989 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
990 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
991 [self performSelector:@selector(resetForwardSkip)
998 - (IBAction)stop:(id)sender
1000 [[VLCCoreInteraction sharedInstance] stop];
1003 - (void)resizePlaylistAfterCollapse
1006 plrect = [o_playlist_table frame];
1007 plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
1008 [[o_playlist_table animator] setFrame: plrect];
1010 NSRect rightSplitRect;
1011 rightSplitRect = [o_right_split_view frame];
1012 plrect = [o_dropzone_box frame];
1013 plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
1014 plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
1015 [[o_dropzone_box animator] setFrame: plrect];
1018 - (void)makeSplitViewVisible
1020 if( b_dark_interface )
1021 [self setContentMinSize: NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1023 [self setContentMinSize: NSMakeSize( 604., 288. )];
1025 NSRect old_frame = [self frame];
1026 float newHeight = [self minSize].height;
1027 if( old_frame.size.height < newHeight )
1029 NSRect new_frame = old_frame;
1030 new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
1031 new_frame.size.height = newHeight;
1033 [[self animator] setFrame: new_frame display: YES animate: YES];
1036 [o_video_view setHidden: YES];
1037 [o_split_view setHidden: NO];
1038 [self makeFirstResponder: nil];
1042 - (void)makeSplitViewHidden
1044 if( b_dark_interface )
1045 [self setContentMinSize: NSMakeSize( 604., f_min_video_height + [o_titlebar_view frame].size.height )];
1047 [self setContentMinSize: NSMakeSize( 604., f_min_video_height )];
1049 [o_split_view setHidden: YES];
1050 [o_video_view setHidden: NO];
1052 if( [[o_video_view subviews] count] > 0 )
1053 [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1056 - (IBAction)togglePlaylist:(id)sender
1058 if (![self isVisible] && sender != nil)
1060 [self makeKeyAndOrderFront: sender];
1064 BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
1065 BOOL b_restored = NO;
1067 // TODO: implement toggle playlist in this situation (triggerd via menu item).
1068 // but for now we block this case, to avoid displaying only the half
1069 if( b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil )
1072 if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0)
1074 [self hideDropZone];
1078 if ( !(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ( (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
1079 || (b_nonembedded && sender != nil)
1080 || (!b_activeVideo && sender != nil)
1081 || b_minimized_view ) )
1083 [self hideSplitView];
1087 if (b_splitview_removed)
1089 if( !b_nonembedded || ( sender != nil && b_nonembedded))
1090 [self showSplitView];
1093 b_minimized_view = YES;
1095 b_minimized_view = NO;
1103 if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil) )
1104 [self makeSplitViewHidden];
1106 [self makeSplitViewVisible];
1110 [o_split_view setHidden: NO];
1111 [o_playlist_table setHidden: NO];
1112 [o_video_view setHidden: !b_activeVideo];
1113 if( b_activeVideo && [[o_video_view subviews] count] > 0 )
1114 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1119 - (void)setRepeatOne
1121 [o_repeat_btn setImage: o_repeat_one_img];
1122 [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1125 - (void)setRepeatAll
1127 [o_repeat_btn setImage: o_repeat_all_img];
1128 [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1131 - (void)setRepeatOff
1133 [o_repeat_btn setImage: o_repeat_img];
1134 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1137 - (IBAction)repeat:(id)sender
1139 vlc_value_t looping,repeating;
1140 intf_thread_t * p_intf = VLCIntf;
1141 playlist_t * p_playlist = pl_Get( p_intf );
1143 var_Get( p_playlist, "repeat", &repeating );
1144 var_Get( p_playlist, "loop", &looping );
1146 if( !repeating.b_bool && !looping.b_bool )
1148 /* was: no repeating at all, switching to Repeat One */
1149 [[VLCCoreInteraction sharedInstance] repeatOne];
1150 [self setRepeatOne];
1152 else if( repeating.b_bool && !looping.b_bool )
1154 /* was: Repeat One, switching to Repeat All */
1155 [[VLCCoreInteraction sharedInstance] repeatAll];
1156 [self setRepeatAll];
1160 /* was: Repeat All or bug in VLC, switching to Repeat Off */
1161 [[VLCCoreInteraction sharedInstance] repeatOff];
1162 [self setRepeatOff];
1169 playlist_t *p_playlist = pl_Get( VLCIntf );
1170 b_value = var_GetBool( p_playlist, "random" );
1173 [o_shuffle_btn setImage: o_shuffle_on_img];
1174 [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1178 [o_shuffle_btn setImage: o_shuffle_img];
1179 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1183 - (IBAction)shuffle:(id)sender
1185 [[VLCCoreInteraction sharedInstance] shuffle];
1189 - (IBAction)timeSliderAction:(id)sender
1192 input_thread_t * p_input;
1194 switch( [[NSApp currentEvent] type] )
1197 case NSLeftMouseDown:
1198 case NSLeftMouseDragged:
1199 f_updated = [sender floatValue];
1205 p_input = pl_CurrentInput( VLCIntf );
1206 if( p_input != NULL )
1211 pos.f_float = f_updated / 10000.;
1212 var_Set( p_input, "position", pos );
1213 [o_time_sld setFloatValue: f_updated];
1215 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1216 [o_time_fld setStringValue: o_time];
1217 [o_fspanel setStreamPos: f_updated andTime: o_time];
1218 vlc_object_release( p_input );
1222 - (IBAction)volumeAction:(id)sender
1224 if (sender == o_volume_sld)
1225 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1226 else if (sender == o_volume_down_btn)
1227 [[VLCCoreInteraction sharedInstance] toggleMute];
1229 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1232 - (IBAction)effects:(id)sender
1234 [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1237 - (IBAction)fullscreen:(id)sender
1239 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1242 - (IBAction)dropzoneButtonAction:(id)sender
1244 [[[VLCMain sharedInstance] open] openFileGeneric];
1248 #pragma mark overwritten default functionality
1249 - (BOOL)canBecomeKeyWindow
1254 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
1256 SEL s_menuAction = [menuItem action];
1258 if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
1261 return [super validateMenuItem:menuItem];
1264 - (void)setTitle:(NSString *)title
1266 if (b_dark_interface)
1268 [o_titlebar_view setWindowTitle: title];
1270 [o_detached_titlebar_view setWindowTitle: title];
1272 if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
1273 [o_detached_video_window setTitle: title];
1274 [super setTitle: title];
1277 - (void)performClose:(id)sender
1279 NSWindow *o_key_window = [NSApp keyWindow];
1281 if (b_dark_interface || !b_video_deco)
1283 [o_key_window orderOut: sender];
1284 if ( [[VLCMain sharedInstance] activeVideoPlayback] && ( !b_nonembedded || o_key_window != self ))
1285 [[VLCCoreInteraction sharedInstance] stop];
1289 if( b_nonembedded && o_key_window != self )
1290 [o_detached_video_window performClose: sender];
1292 [super performClose: sender];
1296 - (void)performMiniaturize:(id)sender
1298 if (b_dark_interface)
1299 [self miniaturize: sender];
1301 [super performMiniaturize: sender];
1304 - (void)performZoom:(id)sender
1306 if (b_dark_interface)
1307 [self customZoom: sender];
1309 [super performZoom: sender];
1312 - (void)zoom:(id)sender
1314 if (b_dark_interface)
1315 [self customZoom: sender];
1317 [super zoom: sender];
1321 * Given a proposed frame rectangle, return a modified version
1322 * which will fit inside the screen.
1324 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1325 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1326 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1327 * Copyright (C) 1996 Free Software Foundation, Inc.
1329 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
1331 NSRect screenRect = [screen visibleFrame];
1334 /* Move top edge of the window inside the screen */
1335 difference = NSMaxY (frameRect) - NSMaxY (screenRect);
1338 frameRect.origin.y -= difference;
1341 /* If the window is resizable, resize it (if needed) so that the
1342 bottom edge is on the screen or can be on the screen when the user moves
1344 difference = NSMaxY (screenRect) - NSMaxY (frameRect);
1345 if (_styleMask & NSResizableWindowMask)
1349 difference2 = screenRect.origin.y - frameRect.origin.y;
1350 difference2 -= difference;
1351 // Take in account the space between the top of window and the top of the
1352 // screen which can be used to move the bottom of the window on the screen
1353 if (difference2 > 0)
1355 frameRect.size.height -= difference2;
1356 frameRect.origin.y += difference2;
1359 /* Ensure that resizing doesn't makewindow smaller than minimum */
1360 difference2 = [self minSize].height - frameRect.size.height;
1361 if (difference2 > 0)
1363 frameRect.size.height += difference2;
1364 frameRect.origin.y -= difference2;
1374 Zooms the receiver. This method calls the delegate method
1375 windowShouldZoom:toFrame: to determine if the window should
1376 be allowed to zoom to full screen.
1378 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1379 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1380 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1381 * Copyright (C) 1996 Free Software Foundation, Inc.
1383 - (void)customZoom:(id)sender
1385 NSRect maxRect = [[self screen] visibleFrame];
1386 NSRect currentFrame = [self frame];
1388 if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
1390 maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
1393 maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
1395 // Compare the new frame with the current one
1396 if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
1397 && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
1398 && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
1399 && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
1401 // Already in zoomed mode, reset user frame, if stored
1402 if ([self frameAutosaveName] != nil)
1404 [self setFrame: previousSavedFrame display: YES animate: YES];
1405 [self saveFrameUsingName: [self frameAutosaveName]];
1410 if ([self frameAutosaveName] != nil)
1412 [self saveFrameUsingName: [self frameAutosaveName]];
1413 previousSavedFrame = [self frame];
1416 [self setFrame: maxRect display: YES animate: YES];
1419 - (void)windowResizedOrMoved:(NSNotification *)notification
1421 [self saveFrameUsingName: [self frameAutosaveName]];
1424 - (void)applicationWillTerminate:(NSNotification *)notification
1426 [self saveFrameUsingName: [self frameAutosaveName]];
1429 - (void)someWindowWillClose:(NSNotification *)notification
1431 if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1433 if ([[VLCMain sharedInstance] activeVideoPlayback])
1434 [[VLCCoreInteraction sharedInstance] stop];
1438 - (void)someWindowWillMiniaturize:(NSNotification *)notification
1440 if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
1442 if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1444 if([[VLCMain sharedInstance] activeVideoPlayback])
1445 [[VLCCoreInteraction sharedInstance] pause];
1450 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
1452 id videoWindow = [o_video_view window];
1453 if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
1454 return proposedFrameSize;
1456 // needed when entering lion fullscreen mode
1458 return proposedFrameSize;
1460 if( [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked] )
1462 NSRect videoWindowFrame = [videoWindow frame];
1463 NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
1464 NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
1465 float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
1466 float marginx = contentRect.size.width - viewRect.size.width;
1467 if( b_dark_interface && b_video_deco )
1468 marginy += [o_titlebar_view frame].size.height;
1470 proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
1473 return proposedFrameSize;
1477 #pragma mark Update interface and respond to foreign events
1478 - (void)showDropZone
1480 b_dropzone_active = YES;
1481 [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
1482 [o_dropzone_view setFrame: [o_playlist_table frame]];
1483 [[o_playlist_table animator] setHidden:YES];
1486 - (void)hideDropZone
1488 b_dropzone_active = NO;
1489 [o_dropzone_view removeFromSuperview];
1490 [[o_playlist_table animator] setHidden: NO];
1493 - (void)hideSplitView
1495 NSRect winrect = [self frame];
1496 i_lastSplitViewHeight = [o_split_view frame].size.height;
1497 winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
1498 winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
1499 [self setFrame: winrect display: YES animate: YES];
1500 [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
1501 if (b_dark_interface)
1503 [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1504 [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1508 [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height )];
1509 [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height )];
1512 b_splitview_removed = YES;
1515 - (void)showSplitView
1517 [self updateWindow];
1518 if (b_dark_interface)
1519 [self setContentMinSize:NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1521 [self setContentMinSize:NSMakeSize( 604., 288. )];
1522 [self setContentMaxSize: NSMakeSize( FLT_MAX, FLT_MAX )];
1525 winrect = [self frame];
1526 winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
1527 winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
1528 [self setFrame: winrect display: YES animate: YES];
1530 [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
1532 b_splitview_removed = NO;
1535 - (void)updateTimeSlider
1537 input_thread_t * p_input;
1538 p_input = pl_CurrentInput( VLCIntf );
1545 var_Get( p_input, "position", &pos );
1546 f_updated = 10000. * pos.f_float;
1547 [o_time_sld setFloatValue: f_updated];
1549 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1551 mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1553 [o_time_sld setEnabled: NO];
1554 [o_time_sld setHidden: YES];
1555 [o_time_sld_fancygradient_view setHidden: YES];
1557 [o_time_sld setEnabled: YES];
1558 [o_time_sld setHidden: NO];
1559 [o_time_sld_fancygradient_view setHidden: NO];
1562 [o_time_fld setStringValue: o_time];
1563 [o_time_fld setNeedsDisplay:YES];
1564 [o_fspanel setStreamPos: f_updated andTime: o_time];
1565 vlc_object_release( p_input );
1569 [o_time_sld setFloatValue: 0.0];
1570 [o_time_fld setStringValue: @"00:00"];
1571 [o_time_sld setEnabled: NO];
1572 [o_time_sld setHidden: YES];
1573 [o_time_sld_fancygradient_view setHidden: YES];
1575 [o_detached_time_sld_fancygradient_view setHidden: YES];
1580 [o_detached_time_sld setFloatValue: [o_time_sld floatValue]];
1581 [o_detached_time_sld setEnabled: [o_time_sld isEnabled]];
1582 [o_detached_time_fld setStringValue: [o_time_fld stringValue]];
1583 [o_detached_time_sld setHidden: [o_time_sld isHidden]];
1587 - (void)updateVolumeSlider
1589 int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1590 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1594 [o_volume_sld setIntValue: i_volume];
1595 [o_fspanel setVolumeLevel: i_volume];
1598 [o_volume_sld setIntValue: 0];
1600 [o_volume_sld setEnabled: !b_muted];
1601 [o_volume_up_btn setEnabled: !b_muted];
1606 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1607 input_thread_t * p_input;
1608 p_input = pl_CurrentInput( VLCIntf );
1612 char *format = var_InheritString( VLCIntf, "input-title-format" );
1613 char *formated = str_format_meta( pl_Get( VLCIntf ), format );
1615 aString = [NSString stringWithUTF8String:formated];
1618 char *uri = input_item_GetURI( input_GetItem( p_input ) );
1620 NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1621 if ([o_url isFileURL])
1623 [self setRepresentedURL: o_url];
1624 [o_detached_video_window setRepresentedURL: o_url];
1626 [self setRepresentedURL: nil];
1627 [o_detached_video_window setRepresentedURL: nil];
1631 if ([aString isEqualToString:@""])
1633 if ([o_url isFileURL])
1634 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1636 aString = [o_url absoluteString];
1639 [self setTitle: aString];
1640 [o_fspanel setStreamTitle: aString];
1641 vlc_object_release( p_input );
1645 [self setTitle: _NS("VLC media player")];
1646 [self setRepresentedURL: nil];
1652 - (void)updateWindow
1654 bool b_input = false;
1655 bool b_plmul = false;
1656 bool b_control = false;
1657 bool b_seekable = false;
1658 bool b_chapters = false;
1660 playlist_t * p_playlist = pl_Get( VLCIntf );
1663 b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1666 input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1668 bool b_buffering = NO;
1670 if( ( b_input = ( p_input != NULL ) ) )
1672 /* seekable streams */
1673 cachedInputState = input_GetState( p_input );
1674 if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1677 /* seekable streams */
1678 b_seekable = var_GetBool( p_input, "can-seek" );
1680 /* check whether slow/fast motion is possible */
1681 b_control = var_GetBool( p_input, "can-rate" );
1683 /* chapters & titles */
1684 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1686 vlc_object_release( p_input );
1691 [o_progress_bar startAnimation:self];
1692 [o_progress_bar setIndeterminate:YES];
1693 [o_progress_bar setHidden:NO];
1695 [o_progress_bar stopAnimation:self];
1696 [o_progress_bar setHidden:YES];
1699 [o_stop_btn setEnabled: b_input];
1700 [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1701 [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1702 if (b_show_jump_buttons)
1704 [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1705 [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1709 [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1710 [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1712 [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1714 [o_time_sld setEnabled: b_seekable];
1715 [self updateTimeSlider];
1716 [o_fspanel setSeekable: b_seekable];
1719 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1720 [self hideDropZone];
1722 [self showDropZone];
1724 [o_sidebar_view setNeedsDisplay:YES];
1729 [o_play_btn setImage: o_pause_img];
1730 [o_play_btn setAlternateImage: o_pause_pressed_img];
1731 [o_play_btn setToolTip: _NS("Pause")];
1734 [o_detached_play_btn setImage: o_pause_img];
1735 [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1736 [o_detached_play_btn setToolTip: _NS("Pause")];
1738 [o_fspanel setPause];
1743 [o_play_btn setImage: o_play_img];
1744 [o_play_btn setAlternateImage: o_play_pressed_img];
1745 [o_play_btn setToolTip: _NS("Play")];
1748 [o_detached_play_btn setImage: o_play_img];
1749 [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1750 [o_detached_play_btn setToolTip: _NS("Play")];
1752 [o_fspanel setPlay];
1755 - (void)drawFancyGradientEffectForTimeSlider
1757 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1758 CGFloat f_value = [o_time_sld knobPosition];
1761 NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1762 if (f_value != oldFrame.size.width)
1764 if ([o_time_sld_fancygradient_view isHidden])
1765 [o_time_sld_fancygradient_view setHidden: NO];
1766 [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1769 if (b_nonembedded && b_video_deco)
1771 f_value = [o_detached_time_sld knobPosition];
1772 oldFrame = [o_detached_time_sld_fancygradient_view frame];
1773 if (f_value != oldFrame.size.width)
1775 if ([o_detached_time_sld_fancygradient_view isHidden])
1776 [o_detached_time_sld_fancygradient_view setHidden: NO];
1777 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1784 frame = [o_time_sld_fancygradient_view frame];
1785 if (frame.size.width > 0)
1787 frame.size.width = 0;
1788 [o_time_sld_fancygradient_view setFrame: frame];
1792 frame = [o_detached_time_sld_fancygradient_view frame];
1793 frame.size.width = 0;
1794 [o_detached_time_sld_fancygradient_view setFrame: frame];
1797 [o_time_sld_fancygradient_view setHidden: YES];
1799 [o_detached_time_sld_fancygradient_view setHidden: YES];
1805 #pragma mark Video Output handling
1808 return o_video_view;
1811 - (void)setupVideoView
1813 // TODO: make lion fullscreen compatible with macosx-background and !embedded-video
1814 if( var_InheritBool( VLCIntf, "macosx-background" ) && !b_nativeFullscreenMode )
1816 msg_Dbg( VLCIntf, "Creating background window" );
1817 NSScreen *screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
1819 screen = [self screen];
1820 NSRect screen_rect = [screen frame];
1822 if( o_extra_video_window )
1823 [o_extra_video_window release];
1825 o_extra_video_window = [[VLCWindow alloc] initWithContentRect:screen_rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
1826 [o_extra_video_window setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey) + 1];
1827 [o_extra_video_window setBackgroundColor: [NSColor blackColor]];
1828 [o_extra_video_window setCanBecomeKeyWindow: NO];
1829 [o_extra_video_window setCanBecomeMainWindow: NO];
1830 [o_extra_video_window useOptimizedDrawing: YES];
1831 [o_extra_video_window setMovableByWindowBackground: NO];
1833 [o_video_view retain];
1834 if ([o_video_view superview] != NULL)
1835 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1836 screen_rect.origin.x = screen_rect.origin.y = 0;
1837 [o_video_view setFrame: screen_rect];
1838 [[o_extra_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1839 [o_video_view release];
1841 [o_extra_video_window orderBack:nil];
1843 b_nonembedded = YES;
1847 if ((var_InheritBool( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode) && b_video_deco)
1849 if ([o_video_view window] != self)
1851 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1852 [o_video_view setFrame: [o_split_view frame]];
1853 [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1859 if ([o_video_view superview] != NULL)
1860 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1863 videoFrame.size = [[o_detached_video_window contentView] frame].size;
1866 videoFrame.size.height -= [o_detached_bottombar_view frame].size.height;
1867 if( b_dark_interface )
1868 videoFrame.size.height -= [o_detached_titlebar_view frame].size.height;
1870 videoFrame.origin.x = .0;
1871 videoFrame.origin.y = [o_detached_bottombar_view frame].size.height;
1875 videoFrame.origin.y = .0;
1876 videoFrame.origin.x = .0;
1879 [o_video_view setFrame: videoFrame];
1880 [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1881 [o_detached_video_window setLevel:NSNormalWindowLevel];
1882 [o_detached_video_window useOptimizedDrawing: YES];
1883 b_nonembedded = YES;
1886 [[o_video_view window] makeKeyAndOrderFront: self];
1888 vout_thread_t *p_vout = getVout();
1891 if( var_GetBool( p_vout, "video-on-top" ) )
1892 [[o_video_view window] setLevel: NSStatusWindowLevel];
1894 [[o_video_view window] setLevel: NSNormalWindowLevel];
1895 vlc_object_release( p_vout );
1899 [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
1902 - (void)setVideoplayEnabled
1904 BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1906 if( b_videoPlayback )
1908 // look for 'start at fullscreen'
1909 [[VLCMain sharedInstance] fullscreenChanged];
1913 [self makeFirstResponder: nil];
1914 [o_detached_video_window orderOut: nil];
1915 if( o_extra_video_window )
1916 [o_extra_video_window orderOut: nil];
1918 if( [self level] != NSNormalWindowLevel )
1919 [self setLevel: NSNormalWindowLevel];
1920 if( [o_detached_video_window level] != NSNormalWindowLevel )
1921 [o_detached_video_window setLevel: NSNormalWindowLevel];
1923 // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
1924 [self setAlphaValue:1.0];
1927 if( b_nativeFullscreenMode )
1929 if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1930 [o_bottombar_view setHidden: b_videoPlayback];
1932 [o_bottombar_view setHidden: NO];
1933 if( b_videoPlayback && b_fullscreen )
1934 [o_fspanel setActive: nil];
1935 if( !b_videoPlayback )
1936 [o_fspanel setNonActive: nil];
1939 if (!b_videoPlayback && b_fullscreen)
1941 if (!b_nativeFullscreenMode)
1942 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1946 - (void)resizeWindow
1948 if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
1951 id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1952 NSSize windowMinSize = [o_videoWindow minSize];
1953 NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
1955 NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
1956 NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
1958 unsigned int i_width = nativeVideoSize.width;
1959 unsigned int i_height = nativeVideoSize.height;
1960 if (i_width < windowMinSize.width)
1961 i_width = windowMinSize.width;
1962 if (i_height < f_min_video_height)
1963 i_height = f_min_video_height;
1965 /* Calculate the window's new size */
1967 new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
1968 new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
1969 new_frame.origin.x = topleftscreen.x;
1970 new_frame.origin.y = topleftscreen.y - new_frame.size.height;
1972 /* make sure the window doesn't exceed the screen size the window is on */
1973 if( new_frame.size.width > screenFrame.size.width )
1975 new_frame.size.width = screenFrame.size.width;
1976 new_frame.origin.x = screenFrame.origin.x;
1978 if( new_frame.size.height > screenFrame.size.height )
1980 new_frame.size.height = screenFrame.size.height;
1981 new_frame.origin.y = screenFrame.origin.y;
1983 if( new_frame.origin.y < screenFrame.origin.y )
1984 new_frame.origin.y = screenFrame.origin.y;
1986 CGFloat right_screen_point = screenFrame.origin.x + screenFrame.size.width;
1987 CGFloat right_window_point = new_frame.origin.x + new_frame.size.width;
1988 if( right_window_point > right_screen_point )
1989 new_frame.origin.x -= ( right_window_point - right_screen_point );
1991 [[o_videoWindow animator] setFrame:new_frame display:YES];
1994 - (void)setNativeVideoSize:(NSSize)size
1996 nativeVideoSize = size;
1998 if( var_InheritBool( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen && !var_InheritBool( VLCIntf, "macosx-background" ) )
1999 [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
2002 // Called automatically if window's acceptsMouseMovedEvents property is true
2003 - (void)mouseMoved:(NSEvent *)theEvent
2006 [self recreateHideMouseTimer];
2008 [super mouseMoved: theEvent];
2011 - (void)recreateHideMouseTimer
2013 if (t_hide_mouse_timer != nil) {
2014 [t_hide_mouse_timer invalidate];
2015 [t_hide_mouse_timer release];
2018 t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
2020 selector:@selector(hideMouseCursor:)
2023 [t_hide_mouse_timer retain];
2026 // NSTimer selectors require this function signature as per Apple's docs
2027 - (void)hideMouseCursor:(NSTimer *)timer
2029 [NSCursor setHiddenUntilMouseMoves: YES];
2033 #pragma mark Fullscreen support
2034 - (void)showFullscreenController
2036 if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
2042 return b_fullscreen;
2045 - (void)lockFullscreenAnimation
2047 [o_animation_lock lock];
2050 - (void)unlockFullscreenAnimation
2052 [o_animation_lock unlock];
2055 - (void)enterFullscreen
2057 NSMutableDictionary *dict1, *dict2;
2061 BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2062 o_current_video_window = [o_video_view window];
2064 screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
2065 [self lockFullscreenAnimation];
2069 msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
2070 screen = [o_current_video_window screen];
2074 msg_Dbg( VLCIntf, "Using deepest screen" );
2075 screen = [NSScreen deepestScreen];
2078 screen_rect = [screen frame];
2080 [o_fullscreen_btn setState: YES];
2082 [o_detached_fullscreen_btn setState: YES];
2084 [self recreateHideMouseTimer];
2086 if( blackout_other_displays )
2087 [screen blackoutOtherScreens];
2089 /* Make sure we don't see the window flashes in float-on-top mode */
2090 i_originalLevel = [o_current_video_window level];
2091 [o_current_video_window setLevel:NSNormalWindowLevel];
2093 /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
2094 if (!o_fullscreen_window)
2096 /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
2098 rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
2099 rect.origin.x += [o_current_video_window frame].origin.x;
2100 rect.origin.y += [o_current_video_window frame].origin.y;
2101 o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
2102 [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
2103 [o_fullscreen_window setCanBecomeKeyWindow: YES];
2104 [o_fullscreen_window setCanBecomeMainWindow: YES];
2106 if (![o_current_video_window isVisible] || [o_current_video_window alphaValue] == 0.0)
2108 /* We don't animate if we are not visible, instead we
2109 * simply fade the display */
2110 CGDisplayFadeReservationToken token;
2112 if( blackout_other_displays )
2114 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2115 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2118 if ([screen mainScreen])
2119 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2121 [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2122 [o_temp_view setFrame:[o_video_view frame]];
2123 [o_fullscreen_window setContentView:o_video_view];
2125 [o_fullscreen_window makeKeyAndOrderFront:self];
2126 [o_fullscreen_window orderFront:self animate:YES];
2128 [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
2129 [o_fullscreen_window setLevel:NSNormalWindowLevel];
2131 if( blackout_other_displays )
2133 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2134 CGReleaseDisplayFadeReservation( token );
2137 /* Will release the lock */
2138 [self hasBecomeFullscreen];
2143 /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2144 NSDisableScreenUpdates();
2145 [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2146 [o_temp_view setFrame:[o_video_view frame]];
2147 [o_fullscreen_window setContentView:o_video_view];
2148 [o_fullscreen_window makeKeyAndOrderFront:self];
2149 NSEnableScreenUpdates();
2152 /* We are in fullscreen (and no animation is running) */
2155 /* Make sure we are hidden */
2156 [o_current_video_window orderOut: self];
2158 [self unlockFullscreenAnimation];
2162 if (o_fullscreen_anim1)
2164 [o_fullscreen_anim1 stopAnimation];
2165 [o_fullscreen_anim1 release];
2167 if (o_fullscreen_anim2)
2169 [o_fullscreen_anim2 stopAnimation];
2170 [o_fullscreen_anim2 release];
2173 if ([screen mainScreen])
2174 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2176 dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
2177 dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
2179 [dict1 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2180 [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
2182 [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2183 [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2184 [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
2186 /* Strategy with NSAnimation allocation:
2187 - Keep at most 2 animation at a time
2188 - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
2190 o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
2191 o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
2196 [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2197 [o_fullscreen_anim1 setDuration: 0.3];
2198 [o_fullscreen_anim1 setFrameRate: 30];
2199 [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2200 [o_fullscreen_anim2 setDuration: 0.2];
2201 [o_fullscreen_anim2 setFrameRate: 30];
2203 [o_fullscreen_anim2 setDelegate: self];
2204 [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2206 [o_fullscreen_anim1 startAnimation];
2207 /* fullscreenAnimation will be unlocked when animation ends */
2210 - (void)hasBecomeFullscreen
2212 if( [[o_video_view subviews] count] > 0 )
2213 [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2215 [o_fullscreen_window makeKeyWindow];
2216 [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
2218 /* tell the fspanel to move itself to front next time it's triggered */
2219 [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
2220 [o_fspanel setActive: nil];
2222 if( [o_current_video_window isVisible] )
2223 [o_current_video_window orderOut: self];
2226 [self unlockFullscreenAnimation];
2229 - (void)leaveFullscreen
2231 [self leaveFullscreenAndFadeOut: NO];
2234 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
2236 NSMutableDictionary *dict1, *dict2;
2238 BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2240 if( !o_current_video_window )
2243 [self lockFullscreenAnimation];
2245 [o_fullscreen_btn setState: NO];
2247 [o_detached_fullscreen_btn setState: NO];
2249 /* We always try to do so */
2250 [NSScreen unblackoutScreens];
2252 vout_thread_t *p_vout = getVout();
2255 if( var_GetBool( p_vout, "video-on-top" ) )
2256 [[o_video_view window] setLevel: NSStatusWindowLevel];
2258 [[o_video_view window] setLevel: NSNormalWindowLevel];
2259 vlc_object_release( p_vout );
2261 [[o_video_view window] makeKeyAndOrderFront: nil];
2263 /* Don't do anything if o_fullscreen_window is already closed */
2264 if (!o_fullscreen_window)
2266 [self unlockFullscreenAnimation];
2272 /* We don't animate if we are not visible, instead we
2273 * simply fade the display */
2274 CGDisplayFadeReservationToken token;
2276 if( blackout_other_displays )
2278 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2279 CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2282 [o_fspanel setNonActive: nil];
2283 [NSApp setPresentationOptions: NSApplicationPresentationDefault];
2285 /* Will release the lock */
2286 [self hasEndedFullscreen];
2288 /* Our window is hidden, and might be faded. We need to workaround that, so note it
2290 b_window_is_invisible = YES;
2292 if( blackout_other_displays )
2294 CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2295 CGReleaseDisplayFadeReservation( token );
2301 [o_current_video_window setAlphaValue: 0.0];
2302 [o_current_video_window orderFront: self];
2303 [[o_video_view window] orderFront: self];
2305 [o_fspanel setNonActive: nil];
2306 [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
2308 if (o_fullscreen_anim1)
2310 [o_fullscreen_anim1 stopAnimation];
2311 [o_fullscreen_anim1 release];
2313 if (o_fullscreen_anim2)
2315 [o_fullscreen_anim2 stopAnimation];
2316 [o_fullscreen_anim2 release];
2319 frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
2320 frame.origin.x += [o_current_video_window frame].origin.x;
2321 frame.origin.y += [o_current_video_window frame].origin.y;
2323 dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
2324 [dict2 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2325 [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2327 o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
2330 [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2331 [o_fullscreen_anim2 setDuration: 0.3];
2332 [o_fullscreen_anim2 setFrameRate: 30];
2334 [o_fullscreen_anim2 setDelegate: self];
2336 dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
2338 [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2339 [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2340 [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
2342 o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
2345 [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2346 [o_fullscreen_anim1 setDuration: 0.2];
2347 [o_fullscreen_anim1 setFrameRate: 30];
2348 [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2350 /* Make sure o_fullscreen_window is the frontmost window */
2351 [o_fullscreen_window orderFront: self];
2353 [o_fullscreen_anim1 startAnimation];
2354 /* fullscreenAnimation will be unlocked when animation ends */
2357 - (void)hasEndedFullscreen
2361 /* This function is private and should be only triggered at the end of the fullscreen change animation */
2362 /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2363 NSDisableScreenUpdates();
2364 [o_video_view retain];
2365 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
2366 [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
2367 [o_video_view release];
2368 [o_video_view setFrame:[o_temp_view frame]];
2369 if( [[o_video_view subviews] count] > 0 )
2370 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2371 if( !b_nonembedded )
2372 [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
2374 [[o_video_view window] makeKeyAndOrderFront: self];
2375 [o_fullscreen_window orderOut: self];
2376 NSEnableScreenUpdates();
2378 [o_fullscreen_window release];
2379 o_fullscreen_window = nil;
2380 [[o_video_view window] setLevel:i_originalLevel];
2381 [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2383 // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2384 if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2385 [o_current_video_window orderOut: self];
2387 o_current_video_window = nil;
2388 [self unlockFullscreenAnimation];
2391 - (void)animationDidEnd:(NSAnimation*)animation
2393 NSArray *viewAnimations;
2394 if( o_makekey_anim == animation )
2396 [o_makekey_anim release];
2399 if ([animation currentValue] < 1.0)
2402 /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2403 viewAnimations = [o_fullscreen_anim2 viewAnimations];
2404 if ([viewAnimations count] >=1 &&
2405 [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2407 /* Fullscreen ended */
2408 [self hasEndedFullscreen];
2412 /* Fullscreen started */
2413 [self hasBecomeFullscreen];
2417 - (void)makeKeyAndOrderFront: (id)sender
2420 * when we exit fullscreen and fade out, we may endup in
2421 * having a window that is faded. We can't have it fade in unless we
2424 if(!b_window_is_invisible)
2426 /* Make sure we don't do it too much */
2427 [super makeKeyAndOrderFront: sender];
2431 [super setAlphaValue:0.0f];
2432 [super makeKeyAndOrderFront: sender];
2434 NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2435 [dict setObject:self forKey:NSViewAnimationTargetKey];
2436 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2438 o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2441 [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2442 [o_makekey_anim setDuration: 0.1];
2443 [o_makekey_anim setFrameRate: 30];
2444 [o_makekey_anim setDelegate: self];
2446 [o_makekey_anim startAnimation];
2447 b_window_is_invisible = NO;
2449 /* fullscreenAnimation will be unlocked when animation ends */
2453 #pragma mark Lion native fullscreen handling
2454 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2456 // workaround, see #6668
2457 [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2459 var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
2461 vout_thread_t *p_vout = getVout();
2464 var_SetBool( p_vout, "fullscreen", true );
2465 vlc_object_release( p_vout );
2468 [o_video_view setFrame: [[self contentView] frame]];
2471 [self recreateHideMouseTimer];
2472 i_originalLevel = [self level];
2473 [self setLevel:NSNormalWindowLevel];
2475 if (b_dark_interface)
2477 [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2480 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2481 winrect = [self frame];
2483 winrect.size.height = winrect.size.height - f_titleBarHeight;
2484 [self setFrame: winrect display:NO animate:NO];
2485 winrect = [o_split_view frame];
2486 winrect.size.height = winrect.size.height + f_titleBarHeight;
2487 [o_split_view setFrame: winrect];
2490 if ([[VLCMain sharedInstance] activeVideoPlayback])
2491 [o_bottombar_view setHidden: YES];
2493 [self setMovableByWindowBackground: NO];
2496 - (void)windowDidEnterFullScreen:(NSNotification *)notification
2498 // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
2499 // But this creates some problems when leaving fs over remote intfs, so activate app here.
2500 [NSApp activateIgnoringOtherApps:YES];
2502 [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2503 [o_fspanel setActive: nil];
2506 - (void)windowWillExitFullScreen:(NSNotification *)notification
2509 var_SetBool( pl_Get( VLCIntf ), "fullscreen", false );
2511 vout_thread_t *p_vout = getVout();
2514 var_SetBool( p_vout, "fullscreen", false );
2515 vlc_object_release( p_vout );
2518 [o_video_view setFrame: [o_split_view frame]];
2519 [NSCursor setHiddenUntilMouseMoves: NO];
2520 [o_fspanel setNonActive: nil];
2521 [self setLevel:i_originalLevel];
2524 if (b_dark_interface)
2527 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2528 winrect = [self frame];
2530 [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2531 winrect.size.width, f_titleBarHeight )];
2532 [[self contentView] addSubview: o_titlebar_view];
2534 winrect.size.height = winrect.size.height + f_titleBarHeight;
2535 [self setFrame: winrect display:NO animate:NO];
2536 winrect = [o_split_view frame];
2537 winrect.size.height = winrect.size.height - f_titleBarHeight;
2538 [o_split_view setFrame: winrect];
2539 [o_video_view setFrame: winrect];
2542 if ([[VLCMain sharedInstance] activeVideoPlayback])
2543 [o_bottombar_view setHidden: NO];
2545 [self setMovableByWindowBackground: YES];
2549 #pragma mark split view delegate
2550 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2552 if (dividerIndex == 0)
2558 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
2560 if (dividerIndex == 0)
2566 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2568 return ([subview isEqual:o_left_split_view]);
2571 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
2573 if ([subview isEqual:o_left_split_view])
2579 #pragma mark Side Bar Data handling
2580 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2581 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2583 //Works the same way as the NSOutlineView data source: `nil` means a parent item
2585 return [o_sidebaritems count];
2587 return [[item children] count];
2591 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2593 //Works the same way as the NSOutlineView data source: `nil` means a parent item
2595 return [o_sidebaritems objectAtIndex:index];
2597 return [[item children] objectAtIndex:index];
2601 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2603 return [item title];
2606 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2608 [item setTitle:object];
2611 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2613 return [item hasChildren];
2617 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2619 if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2622 return [item hasBadge];
2626 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2628 playlist_t * p_playlist = pl_Get( VLCIntf );
2629 NSInteger i_playlist_size;
2631 if ([[item identifier] isEqualToString: @"playlist"])
2634 i_playlist_size = p_playlist->p_local_category->i_children;
2637 return i_playlist_size;
2639 if ([[item identifier] isEqualToString: @"medialibrary"])
2642 i_playlist_size = p_playlist->p_ml_category->i_children;
2645 return i_playlist_size;
2648 return [item badgeValue];
2652 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2654 return [item hasIcon];
2658 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2663 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2665 if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2670 if ([item sdtype] > 0)
2672 m = [[NSMenu alloc] init];
2673 playlist_t * p_playlist = pl_Get( VLCIntf );
2674 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2676 [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2678 [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2679 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2681 return [m autorelease];
2688 - (IBAction)sdmenuhandler:(id)sender
2690 NSString * identifier = [sender representedObject];
2691 if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2693 playlist_t * p_playlist = pl_Get( VLCIntf );
2694 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2697 playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2699 playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2704 #pragma mark Side Bar Delegate Methods
2705 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2706 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2708 if ([[group identifier] isEqualToString:@"library"])
2714 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2716 playlist_t * p_playlist = pl_Get( VLCIntf );
2718 NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2719 id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2722 //Set the label text to represent the new selection
2723 if ([item sdtype] > -1 && [[item identifier] length] > 0)
2725 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2728 playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2732 [o_chosen_category_lbl setStringValue:[item title]];
2734 if ([[item identifier] isEqualToString:@"playlist"])
2736 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2738 else if([[item identifier] isEqualToString:@"medialibrary"])
2740 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2744 playlist_item_t * pl_item;
2746 pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2748 [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2752 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2753 [self hideDropZone];
2755 [self showDropZone];
2759 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2761 if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2763 NSPasteboard *o_pasteboard = [info draggingPasteboard];
2764 if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2765 return NSDragOperationGeneric;
2767 return NSDragOperationNone;
2770 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2772 NSPasteboard *o_pasteboard = [info draggingPasteboard];
2774 playlist_t * p_playlist = pl_Get( VLCIntf );
2775 playlist_item_t *p_node;
2777 if ([[item identifier] isEqualToString:@"playlist"])
2778 p_node = p_playlist->p_local_category;
2780 p_node = p_playlist->p_ml_category;
2782 if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2784 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2785 NSUInteger count = [o_values count];
2786 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2788 for( NSUInteger i = 0; i < count; i++)
2790 NSDictionary *o_dic;
2791 char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
2795 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2799 [o_array addObject: o_dic];
2802 [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2805 else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2807 NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2809 NSUInteger count = [array count];
2810 playlist_item_t * p_item = NULL;
2813 for( NSUInteger i = 0; i < count; i++ )
2815 p_item = [[array objectAtIndex:i] pointerValue];
2816 if( !p_item ) continue;
2817 playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2826 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2828 return [item identifier];
2831 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2833 /* the following code assumes for sakes of simplicity that only the top level
2834 * items are allowed to have children */
2836 NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2837 NSUInteger count = [array count];
2841 for (NSUInteger x = 0; x < count; x++)
2843 id item = [array objectAtIndex: x]; // save one objc selector call
2844 if ([[item identifier] isEqualToString:object])
2852 #pragma mark Accessibility stuff
2854 - (NSArray *)accessibilityAttributeNames
2856 if( !b_dark_interface )
2857 return [super accessibilityAttributeNames];
2859 static NSMutableArray *attributes = nil;
2860 if ( attributes == nil ) {
2861 attributes = [[super accessibilityAttributeNames] mutableCopy];
2862 NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
2863 NSAccessibilityCloseButtonAttribute,
2864 NSAccessibilityMinimizeButtonAttribute,
2865 NSAccessibilityZoomButtonAttribute,
2868 for( NSString *attribute in appendAttributes )
2870 if( ![attributes containsObject:attribute] )
2871 [attributes addObject:attribute];
2877 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
2879 if( b_dark_interface )
2881 VLCMainWindowTitleView *o_tbv = o_titlebar_view;
2883 if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
2884 return NSAccessibilityStandardWindowSubrole;
2886 if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
2887 return [[o_tbv closeButton] cell];
2889 if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
2890 return [[o_tbv minimizeButton] cell];
2892 if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
2893 return [[o_tbv zoomButton] cell];
2896 return [super accessibilityAttributeValue: o_attribute_name];
2899 - (id)detachedTitlebarView
2901 return o_detached_titlebar_view;
2905 @implementation VLCDetachedVideoWindow
2907 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
2908 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
2910 b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
2911 b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
2913 if (b_dark_interface || !b_video_deco)
2915 #ifdef MAC_OS_X_VERSION_10_7
2917 styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
2919 styleMask = NSBorderlessWindowMask;
2921 styleMask = NSBorderlessWindowMask;
2925 self = [super initWithContentRect:contentRect styleMask:styleMask
2926 backing:backingType defer:flag];
2928 /* we want to be moveable regardless of our style */
2929 [self setMovableByWindowBackground: YES];
2931 /* we don't want this window to be restored on relaunch */
2933 [self setRestorable:NO];
2938 - (void)awakeFromNib
2940 [self setAcceptsMouseMovedEvents: YES];
2942 if (b_dark_interface)
2944 [self setBackgroundColor: [NSColor clearColor]];
2945 [self setOpaque: NO];
2947 [self setHasShadow:NO];
2948 [self setHasShadow:YES];
2952 - (IBAction)fullscreen:(id)sender
2954 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
2957 - (void)performClose:(id)sender
2959 if (b_dark_interface || !b_video_deco)
2960 [[VLCMainWindow sharedInstance] performClose: sender];
2962 [super performClose: sender];
2965 - (void)performMiniaturize:(id)sender
2967 if (b_dark_interface || !b_video_deco)
2968 [self miniaturize: sender];
2970 [super performMiniaturize: sender];
2973 - (void)performZoom:(id)sender
2975 if (b_dark_interface || !b_video_deco)
2976 [self customZoom: sender];
2978 [super performZoom: sender];
2981 - (void)zoom:(id)sender
2983 if (b_dark_interface || !b_video_deco)
2984 [self customZoom: sender];
2986 [super zoom: sender];
2989 - (BOOL)canBecomeKeyWindow
2994 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
2996 SEL s_menuAction = [menuItem action];
2998 if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
3001 return [super validateMenuItem:menuItem];
3005 * Given a proposed frame rectangle, return a modified version
3006 * which will fit inside the screen.
3008 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3009 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3010 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3011 * Copyright (C) 1996 Free Software Foundation, Inc.
3013 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
3015 NSRect screenRect = [screen visibleFrame];
3018 /* Move top edge of the window inside the screen */
3019 difference = NSMaxY (frameRect) - NSMaxY (screenRect);
3022 frameRect.origin.y -= difference;
3025 /* If the window is resizable, resize it (if needed) so that the
3026 bottom edge is on the screen or can be on the screen when the user moves
3028 difference = NSMaxY (screenRect) - NSMaxY (frameRect);
3029 if (_styleMask & NSResizableWindowMask)
3033 difference2 = screenRect.origin.y - frameRect.origin.y;
3034 difference2 -= difference;
3035 // Take in account the space between the top of window and the top of the
3036 // screen which can be used to move the bottom of the window on the screen
3037 if (difference2 > 0)
3039 frameRect.size.height -= difference2;
3040 frameRect.origin.y += difference2;
3043 /* Ensure that resizing doesn't makewindow smaller than minimum */
3044 difference2 = [self minSize].height - frameRect.size.height;
3045 if (difference2 > 0)
3047 frameRect.size.height += difference2;
3048 frameRect.origin.y -= difference2;
3058 Zooms the receiver. This method calls the delegate method
3059 windowShouldZoom:toFrame: to determine if the window should
3060 be allowed to zoom to full screen.
3062 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3063 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3064 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3065 * Copyright (C) 1996 Free Software Foundation, Inc.
3067 - (void) customZoom: (id)sender
3069 NSRect maxRect = [[self screen] visibleFrame];
3070 NSRect currentFrame = [self frame];
3072 if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
3074 maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
3077 maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
3079 // Compare the new frame with the current one
3080 if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
3081 && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
3082 && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
3083 && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
3085 // Already in zoomed mode, reset user frame, if stored
3086 if ([self frameAutosaveName] != nil)
3088 [self setFrame: previousSavedFrame display: YES animate: YES];
3089 [self saveFrameUsingName: [self frameAutosaveName]];
3094 if ([self frameAutosaveName] != nil)
3096 [self saveFrameUsingName: [self frameAutosaveName]];
3097 previousSavedFrame = [self frame];
3100 [self setFrame: maxRect display: YES animate: YES];
3103 - (NSArray *)accessibilityAttributeNames
3105 if( !b_dark_interface )
3106 return [super accessibilityAttributeNames];
3108 static NSMutableArray *attributes = nil;
3109 if ( attributes == nil ) {
3110 attributes = [[super accessibilityAttributeNames] mutableCopy];
3111 NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3112 NSAccessibilityCloseButtonAttribute,
3113 NSAccessibilityMinimizeButtonAttribute,
3114 NSAccessibilityZoomButtonAttribute,
3117 for( NSString *attribute in appendAttributes )
3119 if( ![attributes containsObject:attribute] )
3120 [attributes addObject:attribute];
3126 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3128 if( b_dark_interface )
3130 VLCMainWindowTitleView *o_tbv = [[VLCMainWindow sharedInstance] detachedTitlebarView];
3132 if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3133 return NSAccessibilityStandardWindowSubrole;
3135 if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3136 return [[o_tbv closeButton] cell];
3138 if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3139 return [[o_tbv minimizeButton] cell];
3141 if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3142 return [[o_tbv zoomButton] cell];
3145 return [super accessibilityAttributeValue: o_attribute_name];