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;
61 - (NSString *)getCurrentTimeAsString:(input_thread_t *)p_input;
64 @implementation VLCMainWindow
65 static const float f_min_video_height = 70.0;
67 static VLCMainWindow *_o_sharedInstance = nil;
69 + (VLCMainWindow *)sharedInstance
71 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
75 #pragma mark Initialization
79 if( _o_sharedInstance )
82 return _o_sharedInstance;
86 _o_sharedInstance = [super init];
89 return _o_sharedInstance;
92 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
93 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
95 b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
99 #ifdef MAC_OS_X_VERSION_10_7
101 styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
103 styleMask = NSBorderlessWindowMask;
105 styleMask = NSBorderlessWindowMask;
109 self = [super initWithContentRect:contentRect styleMask:styleMask
110 backing:backingType defer:flag];
111 _o_sharedInstance = self;
113 [[VLCMain sharedInstance] updateTogglePlaylistState];
115 /* we want to be moveable regardless of our style */
116 [self setMovableByWindowBackground: YES];
118 /* we don't want this window to be restored on relaunch */
120 [self setRestorable:NO];
125 - (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
130 key = config_GetPsz( VLCIntf, keyString );
131 o_key = [NSString stringWithFormat:@"%s", key];
134 unsigned int i_keyModifiers = [[VLCMain sharedInstance] VLCModifiersToCocoa:o_key];
136 NSString * characters = [o_event charactersIgnoringModifiers];
137 if ([characters length] > 0) {
138 return [[characters lowercaseString] isEqualToString: [[VLCMain sharedInstance] VLCKeyToString: o_key]] &&
139 (i_keyModifiers & NSShiftKeyMask) == ([o_event modifierFlags] & NSShiftKeyMask) &&
140 (i_keyModifiers & NSControlKeyMask) == ([o_event modifierFlags] & NSControlKeyMask) &&
141 (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
142 (i_keyModifiers & NSCommandKeyMask) == ([o_event modifierFlags] & NSCommandKeyMask);
147 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
150 // these are key events which should be handled by vlc core, but are attached to a main menu item
151 if( ![self isEvent: o_event forKey: "key-vol-up"] &&
152 ![self isEvent: o_event forKey: "key-vol-down"] &&
153 ![self isEvent: o_event forKey: "key-vol-mute"] )
155 /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
156 so we perform the menu equivalent now. */
157 if([[NSApp mainMenu] performKeyEquivalent:o_event])
163 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
164 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
169 if (b_dark_interface)
170 [o_color_backdrop release];
172 [[NSNotificationCenter defaultCenter] removeObserver: self];
173 [o_sidebaritems release];
175 if( o_extra_video_window )
177 [o_extra_video_window release];
178 o_extra_video_window = nil;
186 BOOL b_splitviewShouldBeHidden = NO;
188 /* setup the styled interface */
189 b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
190 b_nativeFullscreenMode = NO;
191 #ifdef MAC_OS_X_VERSION_10_7
192 if( OSX_LION && b_video_deco )
193 b_nativeFullscreenMode = var_InheritBool( VLCIntf, "macosx-nativefullscreenmode" );
195 t_hide_mouse_timer = nil;
196 [o_detached_video_window setDelegate: self];
197 [self useOptimizedDrawing: YES];
199 [o_play_btn setToolTip: _NS("Play/Pause")];
200 [o_detached_play_btn setToolTip: [o_play_btn toolTip]];
201 [o_bwd_btn setToolTip: _NS("Backward")];
202 [o_detached_bwd_btn setToolTip: [o_bwd_btn toolTip]];
203 [o_fwd_btn setToolTip: _NS("Forward")];
204 [o_detached_fwd_btn setToolTip: [o_fwd_btn toolTip]];
205 [o_stop_btn setToolTip: _NS("Stop")];
206 [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
207 [o_repeat_btn setToolTip: _NS("Repeat")];
208 [o_shuffle_btn setToolTip: _NS("Shuffle")];
209 [o_effects_btn setToolTip: _NS("Effects")];
210 [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
211 [o_detached_fullscreen_btn setToolTip: [o_fullscreen_btn toolTip]];
212 [[o_search_fld cell] setPlaceholderString: _NS("Search")];
213 [o_volume_sld setToolTip: _NS("Volume")];
214 [o_volume_down_btn setToolTip: _NS("Mute")];
215 [o_volume_up_btn setToolTip: _NS("Full Volume")];
216 [o_time_sld setToolTip: _NS("Position")];
217 [o_detached_time_sld setToolTip: [o_time_sld toolTip]];
218 [o_dropzone_btn setTitle: _NS("Open media...")];
219 [o_dropzone_lbl setStringValue: _NS("Drop media here")];
221 if (!b_dark_interface) {
222 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
223 [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
224 [o_bwd_btn setImage: [NSImage imageNamed:@"back"]];
225 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
226 [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back"]];
227 [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
228 o_play_img = [[NSImage imageNamed:@"play"] retain];
229 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
230 o_pause_img = [[NSImage imageNamed:@"pause"] retain];
231 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
232 [o_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
233 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
234 [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
235 [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
236 [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
237 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
238 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
239 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
240 o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
241 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
242 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all"] retain];
243 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
244 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
245 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
246 o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
247 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
248 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
249 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
250 [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"]];
251 [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"]];
252 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
253 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
254 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
255 if (b_nativeFullscreenMode)
257 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
258 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue"]];
262 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
263 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
265 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
266 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
267 [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
268 [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
269 [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
270 [o_detached_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
274 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
275 [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
276 [o_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
277 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
278 [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
279 [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
280 o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
281 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
282 o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
283 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
284 [o_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
285 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
286 [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
287 [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
288 [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
289 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
290 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
291 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
292 o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
293 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
294 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
295 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
296 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
297 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
298 o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
299 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
300 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
301 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
302 [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"]];
303 [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"]];
304 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
305 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
306 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
307 if (b_nativeFullscreenMode)
309 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
310 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue_dark"]];
314 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
315 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
317 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
318 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
319 [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button_dark"]];
320 [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
321 [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"]];
322 [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"]];
324 [o_repeat_btn setImage: o_repeat_img];
325 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
326 [o_shuffle_btn setImage: o_shuffle_img];
327 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
328 [o_play_btn setImage: o_play_img];
329 [o_play_btn setAlternateImage: o_play_pressed_img];
330 [o_detached_play_btn setImage: o_play_img];
331 [o_detached_play_btn setAlternateImage: o_play_pressed_img];
332 BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
333 [o_volume_sld setEnabled: b_mute];
334 [o_volume_up_btn setEnabled: b_mute];
336 b_show_jump_buttons = config_GetInt( VLCIntf, "macosx-show-playback-buttons" );
337 if (b_show_jump_buttons)
338 [self addJumpButtons:YES];
340 b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
341 if (!b_show_playmode_buttons)
342 [self removePlaymodeButtons:YES];
344 /* interface builder action */
345 float f_threshold_height = f_min_video_height + [o_bottombar_view frame].size.height;
346 if( b_dark_interface )
347 f_threshold_height += [o_titlebar_view frame].size.height;
348 if( [[self contentView] frame].size.height < f_threshold_height )
349 b_splitviewShouldBeHidden = YES;
351 [self setDelegate: self];
352 [self setExcludedFromWindowsMenu: YES];
353 [self setAcceptsMouseMovedEvents: YES];
354 // Set that here as IB seems to be buggy
355 if (b_dark_interface && b_video_deco)
357 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
358 [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 )];
360 else if( b_video_deco )
362 [self setContentMinSize:NSMakeSize(604., 288.)];
363 [o_detached_video_window setContentMinSize: NSMakeSize( 363., f_min_video_height + [o_detached_bottombar_view frame].size.height )];
367 if (b_dark_interface)
368 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
370 [self setContentMinSize:NSMakeSize(604., 288.)];
372 [o_detached_bottombar_view setHidden:YES];
373 [o_detached_video_window setContentMinSize: NSMakeSize( f_min_video_height, f_min_video_height )];
376 [self setTitle: _NS("VLC media player")];
377 [o_time_fld setAlignment: NSCenterTextAlignment];
378 [o_time_fld setNeedsDisplay:YES];
379 b_dropzone_active = YES;
380 o_temp_view = [[NSView alloc] init];
381 [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
382 [o_dropzone_view setFrame: [o_playlist_table frame]];
383 [o_left_split_view setFrame: [o_sidebar_view frame]];
384 if (b_nativeFullscreenMode)
387 [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
388 float f_width = [o_fullscreen_btn frame].size.width;
390 #define moveItem( item ) \
391 frame = [item frame]; \
392 frame.origin.x = f_width + frame.origin.x; \
393 [item setFrame: frame]
395 moveItem( o_effects_btn );
396 moveItem( o_volume_up_btn );
397 moveItem( o_volume_sld );
398 moveItem( o_volume_track_view );
399 moveItem( o_volume_down_btn );
400 moveItem( o_time_fld );
403 #define enlargeItem( item ) \
404 frame = [item frame]; \
405 frame.size.width = f_width + frame.size.width; \
406 [item setFrame: frame]
408 enlargeItem( o_time_sld );
409 enlargeItem( o_progress_bar );
410 enlargeItem( o_time_sld_background );
411 enlargeItem( o_time_sld_fancygradient_view );
414 [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
418 [o_titlebar_view setFullscreenButtonHidden: YES];
420 [o_detached_titlebar_view setFullscreenButtonHidden: YES];
425 /* the default small size of the search field is slightly different on Lion, let's work-around that */
427 frame = [o_search_fld frame];
428 frame.origin.y = frame.origin.y + 2.0;
429 frame.size.height = frame.size.height - 1.0;
430 [o_search_fld setFrame: frame];
433 /* create the sidebar */
434 o_sidebaritems = [[NSMutableArray alloc] init];
435 SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
436 SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
437 [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
438 SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
439 [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
440 SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
441 SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
442 SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
443 SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
445 /* SD subnodes, inspired by the Qt4 intf */
446 char **ppsz_longnames;
448 char **ppsz_names = vlc_sd_GetNames( pl_Get( VLCIntf ), &ppsz_longnames, &p_categories );
450 msg_Err( VLCIntf, "no sd item found" ); //TODO
451 char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
452 int *p_category = p_categories;
453 NSMutableArray *internetItems = [[NSMutableArray alloc] init];
454 NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
455 NSMutableArray *lanItems = [[NSMutableArray alloc] init];
456 NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
457 NSString *o_identifier;
458 for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++)
460 o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
461 switch (*p_category) {
462 case SD_CAT_INTERNET:
464 [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
465 if (!strncmp( *ppsz_name, "podcast", 7 ))
466 [internetItems removeLastObject]; // we don't support podcasts at this point (see #6017)
467 // [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
469 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
470 [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
471 [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
476 [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
477 [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
478 [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
479 [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
484 [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
485 [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
486 [[lanItems lastObject] setSdtype: SD_CAT_LAN];
487 [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
490 case SD_CAT_MYCOMPUTER:
492 [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
493 if (!strncmp( *ppsz_name, "video_dir", 9 ))
494 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
495 else if (!strncmp( *ppsz_name, "audio_dir", 9 ))
496 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
497 else if (!strncmp( *ppsz_name, "picture_dir", 11 ))
498 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
500 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
501 [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
502 [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
506 msg_Warn( VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name );
511 free( *ppsz_longname );
513 [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
514 [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
515 [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
516 [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
517 [mycompItems release];
518 [devicesItems release];
520 [internetItems release];
522 free( ppsz_longnames );
523 free( p_categories );
525 [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
526 [o_sidebaritems addObject: libraryItem];
527 if ([mycompItem hasChildren])
528 [o_sidebaritems addObject: mycompItem];
529 if ([devicesItem hasChildren])
530 [o_sidebaritems addObject: devicesItem];
531 if ([lanItem hasChildren])
532 [o_sidebaritems addObject: lanItem];
533 if ([internetItem hasChildren])
534 [o_sidebaritems addObject: internetItem];
536 [o_sidebar_view reloadData];
537 [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
538 [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
539 [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
541 [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
542 [(PXSourceList *)o_sidebar_view setDataSource:self];
543 [o_sidebar_view setDelegate:self];
544 [o_sidebar_view setAutosaveExpandedItems:YES];
546 [o_sidebar_view expandItem: libraryItem expandChildren: YES];
548 /* make sure we display the desired default appearance when VLC launches for the first time */
549 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
550 if (![defaults objectForKey:@"VLCFirstRun"])
552 [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
554 NSUInteger i_sidebaritem_count = [o_sidebaritems count];
555 for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
556 [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
559 if( b_dark_interface )
561 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
562 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
564 [self setBackgroundColor: [NSColor clearColor]];
565 [self setOpaque: NO];
567 [self setHasShadow:NO];
568 [self setHasShadow:YES];
570 NSRect winrect = [self frame];
571 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
573 [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
574 winrect.size.width, f_titleBarHeight )];
575 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
577 if (winrect.size.height > 100)
579 [self setFrame: winrect display:YES animate:YES];
580 previousSavedFrame = winrect;
583 winrect = [o_split_view frame];
584 winrect.size.height = winrect.size.height - f_titleBarHeight;
585 [o_split_view setFrame: winrect];
586 [o_video_view setFrame: winrect];
588 /* detached video window */
589 winrect = [o_detached_video_window frame];
592 [o_detached_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight )];
593 [[o_detached_video_window contentView] addSubview: o_detached_titlebar_view positioned: NSWindowAbove relativeTo: nil];
596 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
597 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
598 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
603 frame = [o_time_sld_fancygradient_view frame];
604 frame.size.height = frame.size.height - 1;
605 frame.origin.y = frame.origin.y + 1;
606 [o_time_sld_fancygradient_view setFrame: frame];
608 frame = [o_detached_time_sld_fancygradient_view frame];
609 frame.size.height = frame.size.height - 1;
610 frame.origin.y = frame.origin.y + 1;
611 [o_detached_time_sld_fancygradient_view setFrame: frame];
613 [o_video_view setFrame: [o_split_view frame]];
614 [o_playlist_table setBorderType: NSNoBorder];
615 [o_sidebar_scrollview setBorderType: NSNoBorder];
619 frame = [o_time_sld_fancygradient_view frame];
620 frame.size.width = 0;
621 [o_time_sld_fancygradient_view setFrame: frame];
623 frame = [o_detached_time_sld_fancygradient_view frame];
624 frame.size.width = 0;
625 [o_detached_time_sld_fancygradient_view setFrame: frame];
629 [o_resize_view setImage: NULL];
630 [o_detached_resize_view setImage: NULL];
633 if ([self styleMask] & NSResizableWindowMask)
635 [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
636 [o_detached_resize_view removeFromSuperviewWithoutNeedingDisplay];
639 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
640 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
641 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
642 [[VLCMain sharedInstance] playbackModeUpdated];
644 [o_split_view setAutosaveName:@"10thanniversary-splitview"];
645 if (b_splitviewShouldBeHidden)
647 [self hideSplitView];
648 i_lastSplitViewHeight = 300;
651 /* sanity check for the window size */
652 frame = [self frame];
653 NSSize screenSize = [[self screen] frame].size;
654 if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
655 nativeVideoSize = screenSize;
661 #pragma mark interface customization
662 - (void)toggleJumpButtons
664 b_show_jump_buttons = config_GetInt( VLCIntf, "macosx-show-playback-buttons" );
666 if (b_show_jump_buttons)
667 [self addJumpButtons:NO];
669 [self removeJumpButtons:NO];
672 - (void)addJumpButtons:(BOOL)b_fast
674 NSRect preliminaryFrame = [o_bwd_btn frame];
675 BOOL b_enabled = [o_bwd_btn isEnabled];
676 preliminaryFrame.size.width = 26.;
677 o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
678 [o_prev_btn setButtonType: NSMomentaryChangeButton];
679 [o_prev_btn setImage: [NSImage imageNamed:@"back-single"]];
680 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"back-pressed-single"]];
681 [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
682 [o_prev_btn setBordered:NO];
683 [o_prev_btn setTarget:self];
684 [o_prev_btn setAction:@selector(prev:)];
685 [o_prev_btn setToolTip: _NS("Previous")];
686 [o_prev_btn setEnabled: b_enabled];
688 o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
689 [o_next_btn setButtonType: NSMomentaryChangeButton];
690 [o_next_btn setImage: [NSImage imageNamed:@"forward-single"]];
691 [o_next_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed-single"]];
692 [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
693 [o_next_btn setBordered:NO];
694 [o_next_btn setTarget:self];
695 [o_next_btn setAction:@selector(next:)];
696 [o_next_btn setToolTip: _NS("Next")];
697 [o_next_btn setEnabled: b_enabled];
701 #define moveItem( item ) \
702 frame = [item frame]; \
703 frame.origin.x = frame.origin.x + f_space; \
705 [item setFrame: frame]; \
707 [[item animator] setFrame: frame]
709 moveItem( o_bwd_btn );
710 moveItem( o_play_btn );
711 moveItem( o_fwd_btn );
713 moveItem( o_stop_btn );
714 moveItem( o_playlist_btn );
715 moveItem( o_repeat_btn );
716 moveItem( o_shuffle_btn );
719 #define resizeItem( item ) \
720 frame = [item frame]; \
721 frame.size.width = frame.size.width - f_space; \
722 frame.origin.x = frame.origin.x + f_space; \
724 [item setFrame: frame]; \
726 [[item animator] setFrame: frame]
728 resizeItem( o_time_sld );
729 resizeItem( o_progress_bar );
730 resizeItem( o_time_sld_background );
731 resizeItem( o_time_sld_fancygradient_view );
734 preliminaryFrame.origin.x = [o_next_btn frame].origin.x + 80. + [o_fwd_btn frame].size.width;
735 [o_next_btn setFrame: preliminaryFrame];
737 // wait until the animation is done, if displayed
739 [[self contentView] addSubview:o_prev_btn];
740 [[self contentView] addSubview:o_next_btn];
742 [[self contentView] performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
743 [[self contentView] performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
746 [o_fwd_btn setAction:@selector(forward:)];
747 [o_bwd_btn setAction:@selector(backward:)];
750 - (void)removeJumpButtons:(BOOL)b_fast
752 if (!o_prev_btn || !o_next_btn )
756 [o_prev_btn setHidden: YES];
757 [o_next_btn setHidden: YES];
759 [[o_prev_btn animator] setHidden: YES];
760 [[o_next_btn animator] setHidden: YES];
762 [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
763 [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
764 [o_prev_btn release];
765 [o_next_btn release];
769 #define moveItem( item ) \
770 frame = [item frame]; \
771 frame.origin.x = frame.origin.x - f_space; \
773 [item setFrame: frame]; \
775 [[item animator] setFrame: frame]
777 moveItem( o_bwd_btn );
778 moveItem( o_play_btn );
779 moveItem( o_fwd_btn );
781 moveItem( o_stop_btn );
782 moveItem( o_playlist_btn );
783 moveItem( o_repeat_btn );
784 moveItem( o_shuffle_btn );
787 #define resizeItem( item ) \
788 frame = [item frame]; \
789 frame.size.width = frame.size.width + f_space; \
790 frame.origin.x = frame.origin.x - f_space; \
792 [item setFrame: frame]; \
794 [[item animator] setFrame: frame]
796 resizeItem( o_time_sld );
797 resizeItem( o_progress_bar );
798 resizeItem( o_time_sld_background );
799 resizeItem( o_time_sld_fancygradient_view );
802 [o_bottombar_view setNeedsDisplay:YES];
804 [o_fwd_btn setAction:@selector(fwd:)];
805 [o_bwd_btn setAction:@selector(bwd:)];
808 - (void)togglePlaymodeButtons
810 b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
812 if (b_show_playmode_buttons)
813 [self addPlaymodeButtons:NO];
815 [self removePlaymodeButtons:NO];
818 - (void)addPlaymodeButtons:(BOOL)b_fast
821 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
823 // FIXME: switch o_playlist_btn artwork
825 #define resizeItem( item ) \
826 frame = [item frame]; \
827 frame.size.width = frame.size.width - f_space; \
828 frame.origin.x = frame.origin.x + f_space; \
830 [item setFrame: frame]; \
832 [[item animator] setFrame: frame]
834 resizeItem( o_time_sld );
835 resizeItem( o_progress_bar );
836 resizeItem( o_time_sld_background );
837 resizeItem( o_time_sld_fancygradient_view );
841 [o_repeat_btn setHidden: NO];
842 [o_shuffle_btn setHidden: NO];
844 [[o_repeat_btn animator] setHidden: NO];
845 [[o_shuffle_btn animator] setHidden: NO];
849 - (void)removePlaymodeButtons:(BOOL)b_fast
852 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
853 [o_repeat_btn setHidden: YES];
854 [o_shuffle_btn setHidden: YES];
856 // FIXME: switch o_playlist_btn artwork
858 #define resizeItem( item ) \
859 frame = [item frame]; \
860 frame.size.width = frame.size.width + f_space; \
861 frame.origin.x = frame.origin.x - f_space; \
863 [item setFrame: frame]; \
865 [[item animator] setFrame: frame]
867 resizeItem( o_time_sld );
868 resizeItem( o_progress_bar );
869 resizeItem( o_time_sld_background );
870 resizeItem( o_time_sld_fancygradient_view );
875 #pragma mark Button Actions
877 - (IBAction)play:(id)sender
879 [[VLCCoreInteraction sharedInstance] play];
882 - (void)resetPreviousButton
884 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
885 // seems like no further event occurred, so let's switch the playback item
886 [[VLCCoreInteraction sharedInstance] previous];
887 just_triggered_previous = NO;
891 - (void)resetBackwardSkip
893 // the user stopped skipping, so let's allow him to change the item
894 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
895 just_triggered_previous = NO;
898 - (IBAction)prev:(id)sender
900 [[VLCCoreInteraction sharedInstance] previous];
903 - (IBAction)bwd:(id)sender
905 if(!just_triggered_previous)
907 just_triggered_previous = YES;
908 [self performSelector:@selector(resetPreviousButton)
914 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
916 // we just skipped 4 "continous" events, otherwise we are too fast
917 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
918 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
919 [self performSelector:@selector(resetBackwardSkip)
926 - (IBAction)backward:(id)sender
928 [[VLCCoreInteraction sharedInstance] backwardShort];
931 - (void)resetNextButton
933 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
934 // seems like no further event occurred, so let's switch the playback item
935 [[VLCCoreInteraction sharedInstance] next];
936 just_triggered_next = NO;
940 - (void)resetForwardSkip
942 // the user stopped skipping, so let's allow him to change the item
943 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
944 just_triggered_next = NO;
947 - (IBAction)next:(id)sender
949 [[VLCCoreInteraction sharedInstance] next];
952 - (IBAction)forward:(id)sender
954 [[VLCCoreInteraction sharedInstance] forwardShort];
957 - (IBAction)fwd:(id)sender
959 if(!just_triggered_next)
961 just_triggered_next = YES;
962 [self performSelector:@selector(resetNextButton)
968 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
970 // we just skipped 4 "continous" events, otherwise we are too fast
971 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
972 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
973 [self performSelector:@selector(resetForwardSkip)
980 - (IBAction)stop:(id)sender
982 [[VLCCoreInteraction sharedInstance] stop];
985 - (void)resizePlaylistAfterCollapse
988 plrect = [o_playlist_table frame];
989 plrect.size.height = i_lastSplitViewHeight - 19.0; // actual pl top bar height, which differs from its frame
990 [[o_playlist_table animator] setFrame: plrect];
992 NSRect rightSplitRect;
993 rightSplitRect = [o_right_split_view frame];
994 plrect = [o_dropzone_box frame];
995 plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
996 plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
997 [[o_playlist_table animator] setFrame: plrect];
1000 - (void)makeSplitViewVisible
1002 if( b_dark_interface )
1003 [self setContentMinSize: NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1005 [self setContentMinSize: NSMakeSize( 604., 288. )];
1007 NSRect old_frame = [self frame];
1008 float newHeight = [self minSize].height;
1009 if( old_frame.size.height < newHeight )
1011 NSRect new_frame = old_frame;
1012 new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
1013 new_frame.size.height = newHeight;
1015 [[self animator] setFrame: new_frame display: YES animate: YES];
1018 [o_video_view setHidden: YES];
1019 [o_split_view setHidden: NO];
1020 [self makeFirstResponder: nil];
1024 - (void)makeSplitViewHidden
1026 if( b_dark_interface )
1027 [self setContentMinSize: NSMakeSize( 604., f_min_video_height + [o_titlebar_view frame].size.height )];
1029 [self setContentMinSize: NSMakeSize( 604., f_min_video_height )];
1031 [o_split_view setHidden: YES];
1032 [o_video_view setHidden: NO];
1034 if( [[o_video_view subviews] count] > 0 )
1035 [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1038 - (IBAction)togglePlaylist:(id)sender
1040 if (![self isVisible] && sender != nil)
1042 [self makeKeyAndOrderFront: sender];
1046 BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
1047 BOOL b_restored = NO;
1049 // TODO: implement toggle playlist in this situation (triggerd via menu item).
1050 // but for now we block this case, to avoid displaying only the half
1051 if( b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil )
1054 if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0)
1056 [self hideDropZone];
1060 if ( !(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ( (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
1061 || (b_nonembedded && sender != nil)
1062 || (!b_activeVideo && sender != nil)
1063 || b_minimized_view ) )
1065 [self hideSplitView];
1069 if (b_splitview_removed)
1071 if( !b_nonembedded || ( sender != nil && b_nonembedded))
1072 [self showSplitView];
1075 b_minimized_view = YES;
1077 b_minimized_view = NO;
1085 if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil) )
1086 [self makeSplitViewHidden];
1088 [self makeSplitViewVisible];
1092 [o_split_view setHidden: NO];
1093 [o_playlist_table setHidden: NO];
1094 [o_video_view setHidden: !b_activeVideo];
1095 if( b_activeVideo && [[o_video_view subviews] count] > 0 )
1096 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1101 - (void)setRepeatOne
1103 [o_repeat_btn setImage: o_repeat_one_img];
1104 [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1107 - (void)setRepeatAll
1109 [o_repeat_btn setImage: o_repeat_all_img];
1110 [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1113 - (void)setRepeatOff
1115 [o_repeat_btn setImage: o_repeat_img];
1116 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1119 - (IBAction)repeat:(id)sender
1121 vlc_value_t looping,repeating;
1122 intf_thread_t * p_intf = VLCIntf;
1123 playlist_t * p_playlist = pl_Get( p_intf );
1125 var_Get( p_playlist, "repeat", &repeating );
1126 var_Get( p_playlist, "loop", &looping );
1128 if( !repeating.b_bool && !looping.b_bool )
1130 /* was: no repeating at all, switching to Repeat One */
1131 [[VLCCoreInteraction sharedInstance] repeatOne];
1132 [self setRepeatOne];
1134 else if( repeating.b_bool && !looping.b_bool )
1136 /* was: Repeat One, switching to Repeat All */
1137 [[VLCCoreInteraction sharedInstance] repeatAll];
1138 [self setRepeatAll];
1142 /* was: Repeat All or bug in VLC, switching to Repeat Off */
1143 [[VLCCoreInteraction sharedInstance] repeatOff];
1144 [self setRepeatOff];
1151 playlist_t *p_playlist = pl_Get( VLCIntf );
1152 b_value = var_GetBool( p_playlist, "random" );
1155 [o_shuffle_btn setImage: o_shuffle_on_img];
1156 [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1160 [o_shuffle_btn setImage: o_shuffle_img];
1161 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1165 - (IBAction)shuffle:(id)sender
1167 [[VLCCoreInteraction sharedInstance] shuffle];
1171 - (IBAction)timeSliderAction:(id)sender
1174 input_thread_t * p_input;
1176 switch( [[NSApp currentEvent] type] )
1179 case NSLeftMouseDown:
1180 case NSLeftMouseDragged:
1181 f_updated = [sender floatValue];
1187 p_input = pl_CurrentInput( VLCIntf );
1188 if( p_input != NULL )
1193 pos.f_float = f_updated / 10000.;
1194 var_Set( p_input, "position", pos );
1195 [o_time_sld setFloatValue: f_updated];
1197 o_time = [self getCurrentTimeAsString: p_input];
1198 [o_time_fld setStringValue: o_time];
1199 [o_fspanel setStreamPos: f_updated andTime: o_time];
1200 vlc_object_release( p_input );
1204 - (IBAction)volumeAction:(id)sender
1206 if (sender == o_volume_sld)
1207 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1208 else if (sender == o_volume_down_btn)
1210 [[VLCCoreInteraction sharedInstance] setMute: YES];
1213 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1216 - (IBAction)effects:(id)sender
1218 [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1221 - (IBAction)fullscreen:(id)sender
1223 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1226 - (IBAction)dropzoneButtonAction:(id)sender
1228 [[[VLCMain sharedInstance] open] openFileGeneric];
1232 #pragma mark overwritten default functionality
1233 - (BOOL)canBecomeKeyWindow
1238 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
1240 SEL s_menuAction = [menuItem action];
1242 if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
1245 return [super validateMenuItem:menuItem];
1248 - (void)setTitle:(NSString *)title
1250 if (b_dark_interface)
1252 [o_titlebar_view setWindowTitle: title];
1254 [o_detached_titlebar_view setWindowTitle: title];
1256 if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
1257 [o_detached_video_window setTitle: title];
1258 [super setTitle: title];
1261 - (void)performClose:(id)sender
1263 NSWindow *o_key_window = [NSApp keyWindow];
1265 if (b_dark_interface || !b_video_deco)
1267 [o_key_window orderOut: sender];
1268 if ( [[VLCMain sharedInstance] activeVideoPlayback] && ( !b_nonembedded || o_key_window != self ))
1269 [[VLCCoreInteraction sharedInstance] stop];
1273 if( b_nonembedded && o_key_window != self )
1274 [o_detached_video_window performClose: sender];
1276 [super performClose: sender];
1280 - (void)performMiniaturize:(id)sender
1282 if (b_dark_interface)
1283 [self miniaturize: sender];
1285 [super performMiniaturize: sender];
1288 - (void)performZoom:(id)sender
1290 if (b_dark_interface)
1291 [self customZoom: sender];
1293 [super performZoom: sender];
1296 - (void)zoom:(id)sender
1298 if (b_dark_interface)
1299 [self customZoom: sender];
1301 [super zoom: sender];
1305 * Given a proposed frame rectangle, return a modified version
1306 * which will fit inside the screen.
1308 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1309 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1310 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1311 * Copyright (C) 1996 Free Software Foundation, Inc.
1313 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
1315 NSRect screenRect = [screen visibleFrame];
1318 /* Move top edge of the window inside the screen */
1319 difference = NSMaxY (frameRect) - NSMaxY (screenRect);
1322 frameRect.origin.y -= difference;
1325 /* If the window is resizable, resize it (if needed) so that the
1326 bottom edge is on the screen or can be on the screen when the user moves
1328 difference = NSMaxY (screenRect) - NSMaxY (frameRect);
1329 if (_styleMask & NSResizableWindowMask)
1333 difference2 = screenRect.origin.y - frameRect.origin.y;
1334 difference2 -= difference;
1335 // Take in account the space between the top of window and the top of the
1336 // screen which can be used to move the bottom of the window on the screen
1337 if (difference2 > 0)
1339 frameRect.size.height -= difference2;
1340 frameRect.origin.y += difference2;
1343 /* Ensure that resizing doesn't makewindow smaller than minimum */
1344 difference2 = [self minSize].height - frameRect.size.height;
1345 if (difference2 > 0)
1347 frameRect.size.height += difference2;
1348 frameRect.origin.y -= difference2;
1358 Zooms the receiver. This method calls the delegate method
1359 windowShouldZoom:toFrame: to determine if the window should
1360 be allowed to zoom to full screen.
1362 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1363 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1364 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1365 * Copyright (C) 1996 Free Software Foundation, Inc.
1367 - (void)customZoom:(id)sender
1369 NSRect maxRect = [[self screen] visibleFrame];
1370 NSRect currentFrame = [self frame];
1372 if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
1374 maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
1377 maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
1379 // Compare the new frame with the current one
1380 if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
1381 && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
1382 && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
1383 && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
1385 // Already in zoomed mode, reset user frame, if stored
1386 if ([self frameAutosaveName] != nil)
1388 [self setFrame: previousSavedFrame display: YES animate: YES];
1389 [self saveFrameUsingName: [self frameAutosaveName]];
1394 if ([self frameAutosaveName] != nil)
1396 [self saveFrameUsingName: [self frameAutosaveName]];
1397 previousSavedFrame = [self frame];
1400 [self setFrame: maxRect display: YES animate: YES];
1403 - (void)windowResizedOrMoved:(NSNotification *)notification
1405 [self saveFrameUsingName: [self frameAutosaveName]];
1408 - (void)applicationWillTerminate:(NSNotification *)notification
1410 [self saveFrameUsingName: [self frameAutosaveName]];
1413 - (void)someWindowWillClose:(NSNotification *)notification
1415 if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1417 if ([[VLCMain sharedInstance] activeVideoPlayback])
1418 [[VLCCoreInteraction sharedInstance] stop];
1422 - (void)someWindowWillMiniaturize:(NSNotification *)notification
1424 if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
1426 if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1428 if([[VLCMain sharedInstance] activeVideoPlayback])
1429 [[VLCCoreInteraction sharedInstance] pause];
1434 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
1436 id videoWindow = [o_video_view window];
1437 if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
1438 return proposedFrameSize;
1440 // needed when entering lion fullscreen mode
1442 return proposedFrameSize;
1444 if( [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked] )
1446 NSRect videoWindowFrame = [videoWindow frame];
1447 NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
1448 NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
1449 float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
1450 float marginx = contentRect.size.width - viewRect.size.width;
1451 if( b_dark_interface && b_video_deco )
1452 marginy += [o_titlebar_view frame].size.height;
1454 proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
1457 return proposedFrameSize;
1461 #pragma mark Update interface and respond to foreign events
1462 - (void)showDropZone
1464 b_dropzone_active = YES;
1465 [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
1466 [o_dropzone_view setFrame: [o_playlist_table frame]];
1467 [[o_playlist_table animator] setHidden:YES];
1470 - (void)hideDropZone
1472 b_dropzone_active = NO;
1473 [o_dropzone_view removeFromSuperview];
1474 [[o_playlist_table animator] setHidden: NO];
1477 - (void)hideSplitView
1479 NSRect winrect = [self frame];
1480 i_lastSplitViewHeight = [o_split_view frame].size.height;
1481 winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
1482 winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
1483 [self setFrame: winrect display: YES animate: YES];
1484 [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
1485 if (b_dark_interface)
1487 [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1488 [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1492 [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height )];
1493 [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height )];
1496 b_splitview_removed = YES;
1499 - (void)showSplitView
1501 [self updateWindow];
1502 if (b_dark_interface)
1503 [self setContentMinSize:NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1505 [self setContentMinSize:NSMakeSize( 604., 288. )];
1506 [self setContentMaxSize: NSMakeSize( FLT_MAX, FLT_MAX )];
1509 winrect = [self frame];
1510 winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
1511 winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
1512 [self setFrame: winrect display: YES animate: YES];
1514 [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
1516 b_splitview_removed = NO;
1519 - (NSString *)getCurrentTimeAsString:(input_thread_t *)p_input
1521 assert( p_input != nil );
1524 char psz_time[MSTRTIME_MAX_SIZE];
1526 var_Get( p_input, "time", &time );
1528 mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1529 if( [o_time_fld timeRemaining] && dur > 0 )
1531 mtime_t remaining = 0;
1532 if( dur > time.i_time )
1533 remaining = dur - time.i_time;
1534 return [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ( remaining / 1000000 ) )];
1537 return [NSString stringWithUTF8String: secstotimestr( psz_time, ( time.i_time / 1000000 ) )];
1540 - (void)updateTimeSlider
1542 input_thread_t * p_input;
1543 p_input = pl_CurrentInput( VLCIntf );
1550 var_Get( p_input, "position", &pos );
1551 f_updated = 10000. * pos.f_float;
1552 [o_time_sld setFloatValue: f_updated];
1554 o_time = [self getCurrentTimeAsString: p_input];
1556 mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1558 [o_time_sld setEnabled: NO];
1559 [o_time_sld setHidden: YES];
1560 [o_time_sld_fancygradient_view setHidden: YES];
1562 [o_time_sld setEnabled: YES];
1563 [o_time_sld setHidden: NO];
1564 [o_time_sld_fancygradient_view setHidden: NO];
1567 [o_time_fld setStringValue: o_time];
1568 [o_time_fld setNeedsDisplay:YES];
1569 [o_fspanel setStreamPos: f_updated andTime: o_time];
1570 vlc_object_release( p_input );
1574 [o_time_sld setFloatValue: 0.0];
1575 [o_time_fld setStringValue: @"00:00"];
1576 [o_time_sld setEnabled: NO];
1577 [o_time_sld setHidden: YES];
1578 [o_time_sld_fancygradient_view setHidden: YES];
1580 [o_detached_time_sld_fancygradient_view setHidden: YES];
1585 [o_detached_time_sld setFloatValue: [o_time_sld floatValue]];
1586 [o_detached_time_sld setEnabled: [o_time_sld isEnabled]];
1587 [o_detached_time_fld setStringValue: [o_time_fld stringValue]];
1588 [o_detached_time_sld setHidden: [o_time_sld isHidden]];
1592 - (void)updateVolumeSlider
1594 playlist_t * p_playlist = pl_Get( VLCIntf );
1595 int i_volume = lroundf(aout_VolumeGet( p_playlist ) * AOUT_VOLUME_DEFAULT);
1596 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1600 [o_volume_sld setIntValue: i_volume];
1601 [o_fspanel setVolumeLevel: i_volume];
1604 [o_volume_sld setIntValue: 0];
1606 [o_volume_sld setEnabled: !b_muted];
1607 [o_volume_up_btn setEnabled: !b_muted];
1612 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1613 input_thread_t * p_input;
1614 p_input = pl_CurrentInput( VLCIntf );
1618 char *format = var_InheritString( VLCIntf, "input-title-format" );
1619 char *formated = str_format_meta( pl_Get( VLCIntf ), format );
1621 aString = [NSString stringWithUTF8String:formated];
1624 char *uri = input_item_GetURI( input_GetItem( p_input ) );
1626 NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1627 if ([o_url isFileURL])
1629 [self setRepresentedURL: o_url];
1630 [o_detached_video_window setRepresentedURL: o_url];
1632 [self setRepresentedURL: nil];
1633 [o_detached_video_window setRepresentedURL: nil];
1637 if ([aString isEqualToString:@""])
1639 if ([o_url isFileURL])
1640 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1642 aString = [o_url absoluteString];
1645 [self setTitle: aString];
1646 [o_fspanel setStreamTitle: aString];
1647 vlc_object_release( p_input );
1651 [self setTitle: _NS("VLC media player")];
1652 [self setRepresentedURL: nil];
1658 - (void)updateWindow
1660 bool b_input = false;
1661 bool b_plmul = false;
1662 bool b_control = false;
1663 bool b_seekable = false;
1664 bool b_chapters = false;
1666 playlist_t * p_playlist = pl_Get( VLCIntf );
1669 b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1672 input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1674 bool b_buffering = NO;
1676 if( ( b_input = ( p_input != NULL ) ) )
1678 /* seekable streams */
1679 cachedInputState = input_GetState( p_input );
1680 if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1683 /* seekable streams */
1684 b_seekable = var_GetBool( p_input, "can-seek" );
1686 /* check whether slow/fast motion is possible */
1687 b_control = var_GetBool( p_input, "can-rate" );
1689 /* chapters & titles */
1690 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1692 vlc_object_release( p_input );
1697 [o_progress_bar startAnimation:self];
1698 [o_progress_bar setIndeterminate:YES];
1699 [o_progress_bar setHidden:NO];
1701 [o_progress_bar stopAnimation:self];
1702 [o_progress_bar setHidden:YES];
1705 [o_stop_btn setEnabled: b_input];
1706 [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1707 [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1708 if (b_show_jump_buttons)
1710 [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1711 [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1715 [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1716 [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1718 [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1720 [o_time_sld setEnabled: b_seekable];
1721 [self updateTimeSlider];
1722 [o_fspanel setSeekable: b_seekable];
1725 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1726 [self hideDropZone];
1728 [self showDropZone];
1730 [o_sidebar_view setNeedsDisplay:YES];
1735 [o_play_btn setImage: o_pause_img];
1736 [o_play_btn setAlternateImage: o_pause_pressed_img];
1737 [o_play_btn setToolTip: _NS("Pause")];
1740 [o_detached_play_btn setImage: o_pause_img];
1741 [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1742 [o_detached_play_btn setToolTip: _NS("Pause")];
1744 [o_fspanel setPause];
1749 [o_play_btn setImage: o_play_img];
1750 [o_play_btn setAlternateImage: o_play_pressed_img];
1751 [o_play_btn setToolTip: _NS("Play")];
1754 [o_detached_play_btn setImage: o_play_img];
1755 [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1756 [o_detached_play_btn setToolTip: _NS("Play")];
1758 [o_fspanel setPlay];
1761 - (void)drawFancyGradientEffectForTimeSlider
1763 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1764 CGFloat f_value = [o_time_sld knobPosition];
1767 NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1768 if (f_value != oldFrame.size.width)
1770 if ([o_time_sld_fancygradient_view isHidden])
1771 [o_time_sld_fancygradient_view setHidden: NO];
1772 [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1775 if (b_nonembedded && b_video_deco)
1777 f_value = [o_detached_time_sld knobPosition];
1778 oldFrame = [o_detached_time_sld_fancygradient_view frame];
1779 if (f_value != oldFrame.size.width)
1781 if ([o_detached_time_sld_fancygradient_view isHidden])
1782 [o_detached_time_sld_fancygradient_view setHidden: NO];
1783 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1790 frame = [o_time_sld_fancygradient_view frame];
1791 if (frame.size.width > 0)
1793 frame.size.width = 0;
1794 [o_time_sld_fancygradient_view setFrame: frame];
1798 frame = [o_detached_time_sld_fancygradient_view frame];
1799 frame.size.width = 0;
1800 [o_detached_time_sld_fancygradient_view setFrame: frame];
1803 [o_time_sld_fancygradient_view setHidden: YES];
1805 [o_detached_time_sld_fancygradient_view setHidden: YES];
1811 #pragma mark Video Output handling
1814 return o_video_view;
1817 - (void)setupVideoView
1819 // TODO: make lion fullscreen compatible with macosx-background and !embedded-video
1820 if( var_InheritBool( VLCIntf, "macosx-background" ) && !b_nativeFullscreenMode )
1822 msg_Dbg( VLCIntf, "Creating background window" );
1823 NSScreen *screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
1825 screen = [self screen];
1826 NSRect screen_rect = [screen frame];
1828 if( o_extra_video_window )
1829 [o_extra_video_window release];
1831 o_extra_video_window = [[VLCWindow alloc] initWithContentRect:screen_rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
1832 [o_extra_video_window setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey) + 1];
1833 [o_extra_video_window setBackgroundColor: [NSColor blackColor]];
1834 [o_extra_video_window setCanBecomeKeyWindow: NO];
1835 [o_extra_video_window setCanBecomeMainWindow: NO];
1836 [o_extra_video_window useOptimizedDrawing: YES];
1837 [o_extra_video_window setMovableByWindowBackground: NO];
1839 [o_video_view retain];
1840 if ([o_video_view superview] != NULL)
1841 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1842 screen_rect.origin.x = screen_rect.origin.y = 0;
1843 [o_video_view setFrame: screen_rect];
1844 [[o_extra_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1845 [o_video_view release];
1847 [o_extra_video_window orderBack:nil];
1849 b_nonembedded = YES;
1853 if ((var_InheritBool( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode) && b_video_deco)
1855 if ([o_video_view window] != self)
1857 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1858 [o_video_view setFrame: [o_split_view frame]];
1859 [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1865 if ([o_video_view superview] != NULL)
1866 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1869 videoFrame.size = [[o_detached_video_window contentView] frame].size;
1872 videoFrame.size.height -= [o_detached_bottombar_view frame].size.height;
1873 if( b_dark_interface )
1874 videoFrame.size.height -= [o_detached_titlebar_view frame].size.height;
1876 videoFrame.origin.x = .0;
1877 videoFrame.origin.y = [o_detached_bottombar_view frame].size.height;
1881 videoFrame.origin.y = .0;
1882 videoFrame.origin.x = .0;
1885 [o_video_view setFrame: videoFrame];
1886 [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1887 [o_detached_video_window setLevel:NSNormalWindowLevel];
1888 [o_detached_video_window useOptimizedDrawing: YES];
1889 b_nonembedded = YES;
1892 [[o_video_view window] makeKeyAndOrderFront: self];
1894 vout_thread_t *p_vout = getVout();
1897 if( var_GetBool( p_vout, "video-on-top" ) )
1898 [[o_video_view window] setLevel: NSStatusWindowLevel];
1900 [[o_video_view window] setLevel: NSNormalWindowLevel];
1901 vlc_object_release( p_vout );
1905 [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
1908 - (void)setVideoplayEnabled
1910 BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1912 if( b_videoPlayback )
1914 // look for 'start at fullscreen'
1915 [[VLCMain sharedInstance] fullscreenChanged];
1919 [self makeFirstResponder: nil];
1920 [o_detached_video_window orderOut: nil];
1921 if( o_extra_video_window )
1922 [o_extra_video_window orderOut: nil];
1924 if( [self level] != NSNormalWindowLevel )
1925 [self setLevel: NSNormalWindowLevel];
1926 if( [o_detached_video_window level] != NSNormalWindowLevel )
1927 [o_detached_video_window setLevel: NSNormalWindowLevel];
1929 // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
1930 [self setAlphaValue:1.0];
1933 if( b_nativeFullscreenMode )
1935 if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1936 [o_bottombar_view setHidden: b_videoPlayback];
1938 [o_bottombar_view setHidden: NO];
1939 if( b_videoPlayback && b_fullscreen )
1940 [o_fspanel setActive: nil];
1941 if( !b_videoPlayback )
1942 [o_fspanel setNonActive: nil];
1945 if (!b_videoPlayback && b_fullscreen)
1947 if (!b_nativeFullscreenMode)
1948 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1952 - (void)resizeWindow
1954 if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
1957 id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1958 NSSize windowMinSize = [o_videoWindow minSize];
1959 NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
1961 NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
1962 NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
1964 unsigned int i_width = nativeVideoSize.width;
1965 unsigned int i_height = nativeVideoSize.height;
1966 if (i_width < windowMinSize.width)
1967 i_width = windowMinSize.width;
1968 if (i_height < f_min_video_height)
1969 i_height = f_min_video_height;
1971 /* Calculate the window's new size */
1973 new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
1974 new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
1975 new_frame.origin.x = topleftscreen.x;
1976 new_frame.origin.y = topleftscreen.y - new_frame.size.height;
1978 /* make sure the window doesn't exceed the screen size the window is on */
1979 if( new_frame.size.width > screenFrame.size.width )
1981 new_frame.size.width = screenFrame.size.width;
1982 new_frame.origin.x = screenFrame.origin.x;
1984 if( new_frame.size.height > screenFrame.size.height )
1986 new_frame.size.height = screenFrame.size.height;
1987 new_frame.origin.y = screenFrame.origin.y;
1989 if( new_frame.origin.y < screenFrame.origin.y )
1990 new_frame.origin.y = screenFrame.origin.y;
1992 CGFloat right_screen_point = screenFrame.origin.x + screenFrame.size.width;
1993 CGFloat right_window_point = new_frame.origin.x + new_frame.size.width;
1994 if( right_window_point > right_screen_point )
1995 new_frame.origin.x -= ( right_window_point - right_screen_point );
1997 [[o_videoWindow animator] setFrame:new_frame display:YES];
2000 - (void)setNativeVideoSize:(NSSize)size
2002 nativeVideoSize = size;
2004 if( var_InheritBool( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen && !var_InheritBool( VLCIntf, "macosx-background" ) )
2005 [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
2008 // Called automatically if window's acceptsMouseMovedEvents property is true
2009 - (void)mouseMoved:(NSEvent *)theEvent
2012 [self recreateHideMouseTimer];
2014 [super mouseMoved: theEvent];
2017 - (void)recreateHideMouseTimer
2019 if (t_hide_mouse_timer != nil) {
2020 [t_hide_mouse_timer invalidate];
2021 [t_hide_mouse_timer release];
2024 t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
2026 selector:@selector(hideMouseCursor:)
2029 [t_hide_mouse_timer retain];
2032 // NSTimer selectors require this function signature as per Apple's docs
2033 - (void)hideMouseCursor:(NSTimer *)timer
2035 [NSCursor setHiddenUntilMouseMoves: YES];
2039 #pragma mark Fullscreen support
2040 - (void)showFullscreenController
2042 if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
2046 - (BOOL)isFullscreen
2048 return b_fullscreen;
2051 - (void)lockFullscreenAnimation
2053 [o_animation_lock lock];
2056 - (void)unlockFullscreenAnimation
2058 [o_animation_lock unlock];
2061 - (void)enterFullscreen
2063 NSMutableDictionary *dict1, *dict2;
2067 BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2068 o_current_video_window = [o_video_view window];
2070 screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
2071 [self lockFullscreenAnimation];
2075 msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
2076 screen = [o_current_video_window screen];
2080 msg_Dbg( VLCIntf, "Using deepest screen" );
2081 screen = [NSScreen deepestScreen];
2084 screen_rect = [screen frame];
2086 [o_fullscreen_btn setState: YES];
2088 [o_detached_fullscreen_btn setState: YES];
2090 [self recreateHideMouseTimer];
2092 if( blackout_other_displays )
2093 [screen blackoutOtherScreens];
2095 /* Make sure we don't see the window flashes in float-on-top mode */
2096 i_originalLevel = [o_current_video_window level];
2097 [o_current_video_window setLevel:NSNormalWindowLevel];
2099 /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
2100 if (!o_fullscreen_window)
2102 /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
2104 rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
2105 rect.origin.x += [o_current_video_window frame].origin.x;
2106 rect.origin.y += [o_current_video_window frame].origin.y;
2107 o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
2108 [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
2109 [o_fullscreen_window setCanBecomeKeyWindow: YES];
2110 [o_fullscreen_window setCanBecomeMainWindow: YES];
2112 if (![o_current_video_window isVisible] || [o_current_video_window alphaValue] == 0.0)
2114 /* We don't animate if we are not visible, instead we
2115 * simply fade the display */
2116 CGDisplayFadeReservationToken token;
2118 if( blackout_other_displays )
2120 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2121 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2124 if ([screen isMainScreen])
2125 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2127 [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2128 [o_temp_view setFrame:[o_video_view frame]];
2129 [o_fullscreen_window setContentView:o_video_view];
2131 [o_fullscreen_window makeKeyAndOrderFront:self];
2132 [o_fullscreen_window orderFront:self animate:YES];
2134 [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
2135 [o_fullscreen_window setLevel:NSNormalWindowLevel];
2137 if( blackout_other_displays )
2139 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2140 CGReleaseDisplayFadeReservation( token );
2143 /* Will release the lock */
2144 [self hasBecomeFullscreen];
2149 /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2150 NSDisableScreenUpdates();
2151 [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2152 [o_temp_view setFrame:[o_video_view frame]];
2153 [o_fullscreen_window setContentView:o_video_view];
2154 [o_fullscreen_window makeKeyAndOrderFront:self];
2155 NSEnableScreenUpdates();
2158 /* We are in fullscreen (and no animation is running) */
2161 /* Make sure we are hidden */
2162 [o_current_video_window orderOut: self];
2164 [self unlockFullscreenAnimation];
2168 if (o_fullscreen_anim1)
2170 [o_fullscreen_anim1 stopAnimation];
2171 [o_fullscreen_anim1 release];
2173 if (o_fullscreen_anim2)
2175 [o_fullscreen_anim2 stopAnimation];
2176 [o_fullscreen_anim2 release];
2179 if ([screen isMainScreen])
2180 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2182 dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
2183 dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
2185 [dict1 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2186 [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
2188 [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2189 [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2190 [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
2192 /* Strategy with NSAnimation allocation:
2193 - Keep at most 2 animation at a time
2194 - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
2196 o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
2197 o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
2202 [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2203 [o_fullscreen_anim1 setDuration: 0.3];
2204 [o_fullscreen_anim1 setFrameRate: 30];
2205 [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2206 [o_fullscreen_anim2 setDuration: 0.2];
2207 [o_fullscreen_anim2 setFrameRate: 30];
2209 [o_fullscreen_anim2 setDelegate: self];
2210 [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2212 [o_fullscreen_anim1 startAnimation];
2213 /* fullscreenAnimation will be unlocked when animation ends */
2216 - (void)hasBecomeFullscreen
2218 if( [[o_video_view subviews] count] > 0 )
2219 [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2221 [o_fullscreen_window makeKeyWindow];
2222 [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
2224 /* tell the fspanel to move itself to front next time it's triggered */
2225 [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
2226 [o_fspanel setActive: nil];
2228 if( [o_current_video_window isVisible] )
2229 [o_current_video_window orderOut: self];
2232 [self unlockFullscreenAnimation];
2235 - (void)leaveFullscreen
2237 [self leaveFullscreenAndFadeOut: NO];
2240 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
2242 NSMutableDictionary *dict1, *dict2;
2244 BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2246 if( !o_current_video_window )
2249 [self lockFullscreenAnimation];
2251 [o_fullscreen_btn setState: NO];
2253 [o_detached_fullscreen_btn setState: NO];
2255 /* We always try to do so */
2256 [NSScreen unblackoutScreens];
2258 vout_thread_t *p_vout = getVout();
2261 if( var_GetBool( p_vout, "video-on-top" ) )
2262 [[o_video_view window] setLevel: NSStatusWindowLevel];
2264 [[o_video_view window] setLevel: NSNormalWindowLevel];
2265 vlc_object_release( p_vout );
2267 [[o_video_view window] makeKeyAndOrderFront: nil];
2269 /* Don't do anything if o_fullscreen_window is already closed */
2270 if (!o_fullscreen_window)
2272 [self unlockFullscreenAnimation];
2278 /* We don't animate if we are not visible, instead we
2279 * simply fade the display */
2280 CGDisplayFadeReservationToken token;
2282 if( blackout_other_displays )
2284 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2285 CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2288 [o_fspanel setNonActive: nil];
2289 [NSApp setPresentationOptions: NSApplicationPresentationDefault];
2291 /* Will release the lock */
2292 [self hasEndedFullscreen];
2294 /* Our window is hidden, and might be faded. We need to workaround that, so note it
2296 b_window_is_invisible = YES;
2298 if( blackout_other_displays )
2300 CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2301 CGReleaseDisplayFadeReservation( token );
2307 [o_current_video_window setAlphaValue: 0.0];
2308 [o_current_video_window orderFront: self];
2309 [[o_video_view window] orderFront: self];
2311 [o_fspanel setNonActive: nil];
2312 [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
2314 if (o_fullscreen_anim1)
2316 [o_fullscreen_anim1 stopAnimation];
2317 [o_fullscreen_anim1 release];
2319 if (o_fullscreen_anim2)
2321 [o_fullscreen_anim2 stopAnimation];
2322 [o_fullscreen_anim2 release];
2325 frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
2326 frame.origin.x += [o_current_video_window frame].origin.x;
2327 frame.origin.y += [o_current_video_window frame].origin.y;
2329 dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
2330 [dict2 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2331 [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2333 o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
2336 [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2337 [o_fullscreen_anim2 setDuration: 0.3];
2338 [o_fullscreen_anim2 setFrameRate: 30];
2340 [o_fullscreen_anim2 setDelegate: self];
2342 dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
2344 [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2345 [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2346 [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
2348 o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
2351 [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2352 [o_fullscreen_anim1 setDuration: 0.2];
2353 [o_fullscreen_anim1 setFrameRate: 30];
2354 [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2356 /* Make sure o_fullscreen_window is the frontmost window */
2357 [o_fullscreen_window orderFront: self];
2359 [o_fullscreen_anim1 startAnimation];
2360 /* fullscreenAnimation will be unlocked when animation ends */
2363 - (void)hasEndedFullscreen
2367 /* This function is private and should be only triggered at the end of the fullscreen change animation */
2368 /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2369 NSDisableScreenUpdates();
2370 [o_video_view retain];
2371 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
2372 [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
2373 [o_video_view release];
2374 [o_video_view setFrame:[o_temp_view frame]];
2375 if( [[o_video_view subviews] count] > 0 )
2376 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2377 if( !b_nonembedded )
2378 [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
2380 [[o_video_view window] makeKeyAndOrderFront: self];
2381 [o_fullscreen_window orderOut: self];
2382 NSEnableScreenUpdates();
2384 [o_fullscreen_window release];
2385 o_fullscreen_window = nil;
2386 [[o_video_view window] setLevel:i_originalLevel];
2387 [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2389 // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2390 if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2391 [o_current_video_window orderOut: self];
2393 o_current_video_window = nil;
2394 [self unlockFullscreenAnimation];
2397 - (void)animationDidEnd:(NSAnimation*)animation
2399 NSArray *viewAnimations;
2400 if( o_makekey_anim == animation )
2402 [o_makekey_anim release];
2405 if ([animation currentValue] < 1.0)
2408 /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2409 viewAnimations = [o_fullscreen_anim2 viewAnimations];
2410 if ([viewAnimations count] >=1 &&
2411 [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2413 /* Fullscreen ended */
2414 [self hasEndedFullscreen];
2418 /* Fullscreen started */
2419 [self hasBecomeFullscreen];
2423 - (void)makeKeyAndOrderFront: (id)sender
2426 * when we exit fullscreen and fade out, we may endup in
2427 * having a window that is faded. We can't have it fade in unless we
2430 if(!b_window_is_invisible)
2432 /* Make sure we don't do it too much */
2433 [super makeKeyAndOrderFront: sender];
2437 [super setAlphaValue:0.0f];
2438 [super makeKeyAndOrderFront: sender];
2440 NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2441 [dict setObject:self forKey:NSViewAnimationTargetKey];
2442 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2444 o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2447 [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2448 [o_makekey_anim setDuration: 0.1];
2449 [o_makekey_anim setFrameRate: 30];
2450 [o_makekey_anim setDelegate: self];
2452 [o_makekey_anim startAnimation];
2453 b_window_is_invisible = NO;
2455 /* fullscreenAnimation will be unlocked when animation ends */
2459 #pragma mark Lion native fullscreen handling
2460 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2462 // workaround, see #6668
2463 [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2465 var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
2467 vout_thread_t *p_vout = getVout();
2470 var_SetBool( p_vout, "fullscreen", true );
2471 vlc_object_release( p_vout );
2474 [o_video_view setFrame: [[self contentView] frame]];
2477 [self recreateHideMouseTimer];
2478 i_originalLevel = [self level];
2479 [self setLevel:NSNormalWindowLevel];
2481 if (b_dark_interface)
2483 [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2486 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2487 winrect = [self frame];
2489 winrect.size.height = winrect.size.height - f_titleBarHeight;
2490 [self setFrame: winrect display:NO animate:NO];
2491 winrect = [o_split_view frame];
2492 winrect.size.height = winrect.size.height + f_titleBarHeight;
2493 [o_split_view setFrame: winrect];
2496 if ([[VLCMain sharedInstance] activeVideoPlayback])
2497 [o_bottombar_view setHidden: YES];
2499 [self setMovableByWindowBackground: NO];
2502 - (void)windowDidEnterFullScreen:(NSNotification *)notification
2504 // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
2505 // But this creates some problems when leaving fs over remote intfs, so activate app here.
2506 [NSApp activateIgnoringOtherApps:YES];
2508 [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2509 [o_fspanel setActive: nil];
2512 - (void)windowWillExitFullScreen:(NSNotification *)notification
2515 var_SetBool( pl_Get( VLCIntf ), "fullscreen", false );
2517 vout_thread_t *p_vout = getVout();
2520 var_SetBool( p_vout, "fullscreen", false );
2521 vlc_object_release( p_vout );
2524 [o_video_view setFrame: [o_split_view frame]];
2525 [NSCursor setHiddenUntilMouseMoves: NO];
2526 [o_fspanel setNonActive: nil];
2527 [self setLevel:i_originalLevel];
2530 if (b_dark_interface)
2533 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2534 winrect = [self frame];
2536 [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2537 winrect.size.width, f_titleBarHeight )];
2538 [[self contentView] addSubview: o_titlebar_view];
2540 winrect.size.height = winrect.size.height + f_titleBarHeight;
2541 [self setFrame: winrect display:NO animate:NO];
2542 winrect = [o_split_view frame];
2543 winrect.size.height = winrect.size.height - f_titleBarHeight;
2544 [o_split_view setFrame: winrect];
2545 [o_video_view setFrame: winrect];
2548 if ([[VLCMain sharedInstance] activeVideoPlayback])
2549 [o_bottombar_view setHidden: NO];
2551 [self setMovableByWindowBackground: YES];
2555 #pragma mark split view delegate
2556 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2558 if (dividerIndex == 0)
2564 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
2566 if (dividerIndex == 0)
2572 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2574 return ([subview isEqual:o_left_split_view]);
2577 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
2579 if ([subview isEqual:o_left_split_view])
2585 #pragma mark Side Bar Data handling
2586 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2587 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2589 //Works the same way as the NSOutlineView data source: `nil` means a parent item
2591 return [o_sidebaritems count];
2593 return [[item children] count];
2597 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2599 //Works the same way as the NSOutlineView data source: `nil` means a parent item
2601 return [o_sidebaritems objectAtIndex:index];
2603 return [[item children] objectAtIndex:index];
2607 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2609 return [item title];
2612 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2614 [item setTitle:object];
2617 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2619 return [item hasChildren];
2623 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2625 if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2628 return [item hasBadge];
2632 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2634 playlist_t * p_playlist = pl_Get( VLCIntf );
2635 NSInteger i_playlist_size;
2637 if ([[item identifier] isEqualToString: @"playlist"])
2640 i_playlist_size = p_playlist->p_local_category->i_children;
2643 return i_playlist_size;
2645 if ([[item identifier] isEqualToString: @"medialibrary"])
2648 i_playlist_size = p_playlist->p_ml_category->i_children;
2651 return i_playlist_size;
2654 return [item badgeValue];
2658 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2660 return [item hasIcon];
2664 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2669 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2671 if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2676 if ([item sdtype] > 0)
2678 m = [[NSMenu alloc] init];
2679 playlist_t * p_playlist = pl_Get( VLCIntf );
2680 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2682 [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2684 [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2685 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2687 return [m autorelease];
2694 - (IBAction)sdmenuhandler:(id)sender
2696 NSString * identifier = [sender representedObject];
2697 if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2699 playlist_t * p_playlist = pl_Get( VLCIntf );
2700 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2703 playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2705 playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2710 #pragma mark Side Bar Delegate Methods
2711 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2712 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2714 if ([[group identifier] isEqualToString:@"library"])
2720 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2722 playlist_t * p_playlist = pl_Get( VLCIntf );
2724 NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2725 id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2728 //Set the label text to represent the new selection
2729 if ([item sdtype] > -1 && [[item identifier] length] > 0)
2731 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2734 playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2738 [o_chosen_category_lbl setStringValue:[item title]];
2740 if ([[item identifier] isEqualToString:@"playlist"])
2742 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2744 else if([[item identifier] isEqualToString:@"medialibrary"])
2746 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2750 playlist_item_t * pl_item;
2752 pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2754 [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2758 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2759 [self hideDropZone];
2761 [self showDropZone];
2765 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2767 if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2769 NSPasteboard *o_pasteboard = [info draggingPasteboard];
2770 if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2771 return NSDragOperationGeneric;
2773 return NSDragOperationNone;
2776 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2778 NSPasteboard *o_pasteboard = [info draggingPasteboard];
2780 playlist_t * p_playlist = pl_Get( VLCIntf );
2781 playlist_item_t *p_node;
2783 if ([[item identifier] isEqualToString:@"playlist"])
2784 p_node = p_playlist->p_local_category;
2786 p_node = p_playlist->p_ml_category;
2788 if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2790 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2791 NSUInteger count = [o_values count];
2792 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2794 for( NSUInteger i = 0; i < count; i++)
2796 NSDictionary *o_dic;
2797 char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
2801 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2805 [o_array addObject: o_dic];
2808 [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2811 else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2813 NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2815 NSUInteger count = [array count];
2816 playlist_item_t * p_item = NULL;
2819 for( NSUInteger i = 0; i < count; i++ )
2821 p_item = [[array objectAtIndex:i] pointerValue];
2822 if( !p_item ) continue;
2823 playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2832 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2834 return [item identifier];
2837 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2839 /* the following code assumes for sakes of simplicity that only the top level
2840 * items are allowed to have children */
2842 NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2843 NSUInteger count = [array count];
2847 for (NSUInteger x = 0; x < count; x++)
2849 id item = [array objectAtIndex: x]; // save one objc selector call
2850 if ([[item identifier] isEqualToString:object])
2858 #pragma mark Accessibility stuff
2860 - (NSArray *)accessibilityAttributeNames
2862 if( !b_dark_interface )
2863 return [super accessibilityAttributeNames];
2865 static NSMutableArray *attributes = nil;
2866 if ( attributes == nil ) {
2867 attributes = [[super accessibilityAttributeNames] mutableCopy];
2868 NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
2869 NSAccessibilityCloseButtonAttribute,
2870 NSAccessibilityMinimizeButtonAttribute,
2871 NSAccessibilityZoomButtonAttribute,
2874 for( NSString *attribute in appendAttributes )
2876 if( ![attributes containsObject:attribute] )
2877 [attributes addObject:attribute];
2883 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
2885 if( b_dark_interface )
2887 VLCMainWindowTitleView *o_tbv = o_titlebar_view;
2889 if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
2890 return NSAccessibilityStandardWindowSubrole;
2892 if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
2893 return [[o_tbv closeButton] cell];
2895 if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
2896 return [[o_tbv minimizeButton] cell];
2898 if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
2899 return [[o_tbv zoomButton] cell];
2902 return [super accessibilityAttributeValue: o_attribute_name];
2905 - (id)detachedTitlebarView
2907 return o_detached_titlebar_view;
2911 @implementation VLCDetachedVideoWindow
2913 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
2914 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
2916 b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
2917 b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
2919 if (b_dark_interface || !b_video_deco)
2921 #ifdef MAC_OS_X_VERSION_10_7
2923 styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
2925 styleMask = NSBorderlessWindowMask;
2927 styleMask = NSBorderlessWindowMask;
2931 self = [super initWithContentRect:contentRect styleMask:styleMask
2932 backing:backingType defer:flag];
2934 /* we want to be moveable regardless of our style */
2935 [self setMovableByWindowBackground: YES];
2937 /* we don't want this window to be restored on relaunch */
2939 [self setRestorable:NO];
2944 - (void)awakeFromNib
2946 [self setAcceptsMouseMovedEvents: YES];
2948 if (b_dark_interface)
2950 [self setBackgroundColor: [NSColor clearColor]];
2951 [self setOpaque: NO];
2953 [self setHasShadow:NO];
2954 [self setHasShadow:YES];
2958 - (IBAction)fullscreen:(id)sender
2960 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
2963 - (void)performClose:(id)sender
2965 if (b_dark_interface || !b_video_deco)
2966 [[VLCMainWindow sharedInstance] performClose: sender];
2968 [super performClose: sender];
2971 - (void)performMiniaturize:(id)sender
2973 if (b_dark_interface || !b_video_deco)
2974 [self miniaturize: sender];
2976 [super performMiniaturize: sender];
2979 - (void)performZoom:(id)sender
2981 if (b_dark_interface || !b_video_deco)
2982 [self customZoom: sender];
2984 [super performZoom: sender];
2987 - (void)zoom:(id)sender
2989 if (b_dark_interface || !b_video_deco)
2990 [self customZoom: sender];
2992 [super zoom: sender];
2995 - (BOOL)canBecomeKeyWindow
3000 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
3002 SEL s_menuAction = [menuItem action];
3004 if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
3007 return [super validateMenuItem:menuItem];
3011 * Given a proposed frame rectangle, return a modified version
3012 * which will fit inside the screen.
3014 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3015 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3016 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3017 * Copyright (C) 1996 Free Software Foundation, Inc.
3019 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
3021 NSRect screenRect = [screen visibleFrame];
3024 /* Move top edge of the window inside the screen */
3025 difference = NSMaxY (frameRect) - NSMaxY (screenRect);
3028 frameRect.origin.y -= difference;
3031 /* If the window is resizable, resize it (if needed) so that the
3032 bottom edge is on the screen or can be on the screen when the user moves
3034 difference = NSMaxY (screenRect) - NSMaxY (frameRect);
3035 if (_styleMask & NSResizableWindowMask)
3039 difference2 = screenRect.origin.y - frameRect.origin.y;
3040 difference2 -= difference;
3041 // Take in account the space between the top of window and the top of the
3042 // screen which can be used to move the bottom of the window on the screen
3043 if (difference2 > 0)
3045 frameRect.size.height -= difference2;
3046 frameRect.origin.y += difference2;
3049 /* Ensure that resizing doesn't makewindow smaller than minimum */
3050 difference2 = [self minSize].height - frameRect.size.height;
3051 if (difference2 > 0)
3053 frameRect.size.height += difference2;
3054 frameRect.origin.y -= difference2;
3064 Zooms the receiver. This method calls the delegate method
3065 windowShouldZoom:toFrame: to determine if the window should
3066 be allowed to zoom to full screen.
3068 * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3069 * Authors: Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3070 * Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3071 * Copyright (C) 1996 Free Software Foundation, Inc.
3073 - (void) customZoom: (id)sender
3075 NSRect maxRect = [[self screen] visibleFrame];
3076 NSRect currentFrame = [self frame];
3078 if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
3080 maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
3083 maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
3085 // Compare the new frame with the current one
3086 if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
3087 && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
3088 && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
3089 && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
3091 // Already in zoomed mode, reset user frame, if stored
3092 if ([self frameAutosaveName] != nil)
3094 [self setFrame: previousSavedFrame display: YES animate: YES];
3095 [self saveFrameUsingName: [self frameAutosaveName]];
3100 if ([self frameAutosaveName] != nil)
3102 [self saveFrameUsingName: [self frameAutosaveName]];
3103 previousSavedFrame = [self frame];
3106 [self setFrame: maxRect display: YES animate: YES];
3109 - (NSArray *)accessibilityAttributeNames
3111 if( !b_dark_interface )
3112 return [super accessibilityAttributeNames];
3114 static NSMutableArray *attributes = nil;
3115 if ( attributes == nil ) {
3116 attributes = [[super accessibilityAttributeNames] mutableCopy];
3117 NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3118 NSAccessibilityCloseButtonAttribute,
3119 NSAccessibilityMinimizeButtonAttribute,
3120 NSAccessibilityZoomButtonAttribute,
3123 for( NSString *attribute in appendAttributes )
3125 if( ![attributes containsObject:attribute] )
3126 [attributes addObject:attribute];
3132 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3134 if( b_dark_interface )
3136 VLCMainWindowTitleView *o_tbv = [[VLCMainWindow sharedInstance] detachedTitlebarView];
3138 if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3139 return NSAccessibilityStandardWindowSubrole;
3141 if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3142 return [[o_tbv closeButton] cell];
3144 if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3145 return [[o_tbv minimizeButton] cell];
3147 if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3148 return [[o_tbv zoomButton] cell];
3151 return [super accessibilityAttributeValue: o_attribute_name];