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>
40 #import <vlc_strings.h>
41 #import <vlc_services_discovery.h>
43 #import "ControlsBar.h"
45 #import "VLCVoutWindowController.h"
48 @interface VLCMainWindow ()
49 - (void)resizePlaylistAfterCollapse;
50 - (void)makeSplitViewVisible;
51 - (void)makeSplitViewHidden;
56 @implementation VLCMainWindow
58 @synthesize fullscreen=b_fullscreen;
59 @synthesize nativeFullscreenMode=b_nativeFullscreenMode;
60 @synthesize nonembedded=b_nonembedded;
61 @synthesize fsPanel=o_fspanel;
63 static VLCMainWindow *_o_sharedInstance = nil;
65 + (VLCMainWindow *)sharedInstance
67 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
71 #pragma mark Initialization
75 if (_o_sharedInstance) {
77 return _o_sharedInstance;
79 _o_sharedInstance = [super init];
81 return _o_sharedInstance;
84 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
85 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
87 self = [super initWithContentRect:contentRect styleMask:styleMask
88 backing:backingType defer:flag];
89 _o_sharedInstance = self;
91 [[VLCMain sharedInstance] updateTogglePlaylistState];
96 - (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
101 key = config_GetPsz(VLCIntf, keyString);
102 o_key = [NSString stringWithFormat:@"%s", key];
105 unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
107 NSString * characters = [o_event charactersIgnoringModifiers];
108 if ([characters length] > 0) {
109 return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
110 (i_keyModifiers & NSShiftKeyMask) == ([o_event modifierFlags] & NSShiftKeyMask) &&
111 (i_keyModifiers & NSControlKeyMask) == ([o_event modifierFlags] & NSControlKeyMask) &&
112 (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
113 (i_keyModifiers & NSCommandKeyMask) == ([o_event modifierFlags] & NSCommandKeyMask);
118 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
121 // these are key events which should be handled by vlc core, but are attached to a main menu item
122 if (![self isEvent: o_event forKey: "key-vol-up"] &&
123 ![self isEvent: o_event forKey: "key-vol-down"] &&
124 ![self isEvent: o_event forKey: "key-vol-mute"]) {
125 /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
126 so we perform the menu equivalent now. */
127 if ([[NSApp mainMenu] performKeyEquivalent:o_event])
133 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
134 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
139 if (b_dark_interface)
140 [o_color_backdrop release];
142 [[NSNotificationCenter defaultCenter] removeObserver: self];
143 [o_sidebaritems release];
150 BOOL b_splitviewShouldBeHidden = NO;
152 /* setup the styled interface */
153 b_nativeFullscreenMode = NO;
154 #ifdef MAC_OS_X_VERSION_10_7
155 if (!OSX_SNOW_LEOPARD)
156 b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
158 t_hide_mouse_timer = nil;
159 [self useOptimizedDrawing: YES];
161 [[o_search_fld cell] setPlaceholderString: _NS("Search")];
162 [[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
164 [o_dropzone_btn setTitle: _NS("Open media...")];
165 [[o_dropzone_btn cell] accessibilitySetOverrideValue:_NS("Click to open an advanced dialog to select the media to play. You can also drop files here to play.") forAttribute:NSAccessibilityDescriptionAttribute];
166 [o_dropzone_lbl setStringValue: _NS("Drop media here")];
168 [o_podcast_add_btn setTitle: _NS("Subscribe")];
169 [o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
170 [o_podcast_subscribe_title_lbl setStringValue: _NS("Subscribe to a podcast")];
171 [o_podcast_subscribe_subtitle_lbl setStringValue: _NS("Enter URL of the podcast to subscribe to:")];
172 [o_podcast_subscribe_cancel_btn setTitle: _NS("Cancel")];
173 [o_podcast_subscribe_ok_btn setTitle: _NS("Subscribe")];
174 [o_podcast_unsubscribe_title_lbl setStringValue: _NS("Unsubscribe from a podcast")];
175 [o_podcast_unsubscribe_subtitle_lbl setStringValue: _NS("Select the podcast you would like to unsubscribe from:")];
176 [o_podcast_unsubscribe_ok_btn setTitle: _NS("Unsubscribe")];
177 [o_podcast_unsubscribe_cancel_btn setTitle: _NS("Cancel")];
179 /* interface builder action */
180 float f_threshold_height = f_min_video_height + [[o_controls_bar bottomBarView] frame].size.height;
181 if (b_dark_interface)
182 f_threshold_height += [o_titlebar_view frame].size.height;
183 if ([[self contentView] frame].size.height < f_threshold_height)
184 b_splitviewShouldBeHidden = YES;
186 [self setDelegate: self];
187 [self setExcludedFromWindowsMenu: YES];
188 [self setAcceptsMouseMovedEvents: YES];
189 // Set that here as IB seems to be buggy
190 if (b_dark_interface) {
191 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
193 [self setContentMinSize:NSMakeSize(604., 288.)];
196 [self setTitle: _NS("VLC media player")];
198 b_dropzone_active = YES;
199 [o_dropzone_view setFrame: [o_playlist_table frame]];
200 [o_left_split_view setFrame: [o_sidebar_view frame]];
202 if (b_nativeFullscreenMode) {
203 [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
205 [o_titlebar_view setFullscreenButtonHidden: YES];
208 if (!OSX_SNOW_LEOPARD) {
209 /* the default small size of the search field is slightly different on Lion, let's work-around that */
211 frame = [o_search_fld frame];
212 frame.origin.y = frame.origin.y + 2.0;
213 frame.size.height = frame.size.height - 1.0;
214 [o_search_fld setFrame: frame];
217 /* create the sidebar */
218 o_sidebaritems = [[NSMutableArray alloc] init];
219 SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
220 SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
221 [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
222 SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
223 [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
224 SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
225 SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
226 SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
227 SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
229 /* SD subnodes, inspired by the Qt4 intf */
230 char **ppsz_longnames;
232 char **ppsz_names = vlc_sd_GetNames(pl_Get(VLCIntf), &ppsz_longnames, &p_categories);
234 msg_Err(VLCIntf, "no sd item found"); //TODO
235 char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
236 int *p_category = p_categories;
237 NSMutableArray *internetItems = [[NSMutableArray alloc] init];
238 NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
239 NSMutableArray *lanItems = [[NSMutableArray alloc] init];
240 NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
241 NSString *o_identifier;
242 for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++) {
243 o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
244 switch (*p_category) {
245 case SD_CAT_INTERNET:
246 [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
247 if (!strncmp(*ppsz_name, "podcast", 7))
248 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
250 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
251 [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
252 [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
255 [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
256 [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
257 [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
258 [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
261 [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
262 [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
263 [[lanItems lastObject] setSdtype: SD_CAT_LAN];
264 [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
266 case SD_CAT_MYCOMPUTER:
267 [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
268 if (!strncmp(*ppsz_name, "video_dir", 9))
269 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
270 else if (!strncmp(*ppsz_name, "audio_dir", 9))
271 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
272 else if (!strncmp(*ppsz_name, "picture_dir", 11))
273 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
275 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
276 [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
277 [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
280 msg_Warn(VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name);
285 free(*ppsz_longname);
287 [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
288 [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
289 [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
290 [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
291 [mycompItems release];
292 [devicesItems release];
294 [internetItems release];
296 free(ppsz_longnames);
299 [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
300 [o_sidebaritems addObject: libraryItem];
301 if ([mycompItem hasChildren])
302 [o_sidebaritems addObject: mycompItem];
303 if ([devicesItem hasChildren])
304 [o_sidebaritems addObject: devicesItem];
305 if ([lanItem hasChildren])
306 [o_sidebaritems addObject: lanItem];
307 if ([internetItem hasChildren])
308 [o_sidebaritems addObject: internetItem];
310 [o_sidebar_view reloadData];
311 [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
312 [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
313 [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
315 [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
316 [(PXSourceList *)o_sidebar_view setDataSource:self];
317 [o_sidebar_view setDelegate:self];
318 [o_sidebar_view setAutosaveExpandedItems:YES];
320 [o_sidebar_view expandItem: libraryItem expandChildren: YES];
322 /* make sure we display the desired default appearance when VLC launches for the first time */
323 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
324 if (![defaults objectForKey:@"VLCFirstRun"]) {
325 [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
327 NSUInteger i_sidebaritem_count = [o_sidebaritems count];
328 for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
329 [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
332 if (b_dark_interface) {
333 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
334 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
336 [self setBackgroundColor: [NSColor clearColor]];
337 [self setOpaque: NO];
339 [self setHasShadow:NO];
340 [self setHasShadow:YES];
342 NSRect winrect = [self frame];
343 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
345 [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
346 winrect.size.width, f_titleBarHeight)];
347 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
349 if (winrect.size.height > 100) {
350 [self setFrame: winrect display:YES animate:YES];
351 previousSavedFrame = winrect;
354 winrect = [o_split_view frame];
355 winrect.size.height = winrect.size.height - f_titleBarHeight;
356 [o_split_view setFrame: winrect];
357 [o_video_view setFrame: winrect];
359 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
360 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
361 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
364 [o_video_view setFrame: [o_split_view frame]];
365 [o_playlist_table setBorderType: NSNoBorder];
366 [o_sidebar_scrollview setBorderType: NSNoBorder];
369 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
370 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
371 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
373 [o_split_view setAutosaveName:@"10thanniversary-splitview"];
374 if (b_splitviewShouldBeHidden) {
375 [self hideSplitView];
376 i_lastSplitViewHeight = 300;
379 /* sanity check for the window size */
380 NSRect frame = [self frame];
381 NSSize screenSize = [[self screen] frame].size;
382 if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
383 nativeVideoSize = screenSize;
387 // update fs button to reflect state for next startup
388 if (var_InheritBool(pl_Get(VLCIntf), "fullscreen")) {
389 [o_controls_bar setFullscreenState:YES];
395 - (VLCMainWindowControlsBar *)controlsBar;
397 return (VLCMainWindowControlsBar *)o_controls_bar;
400 - (void)resizePlaylistAfterCollapse
403 plrect = [o_playlist_table frame];
404 plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
405 [[o_playlist_table animator] setFrame: plrect];
407 NSRect rightSplitRect;
408 rightSplitRect = [o_right_split_view frame];
409 plrect = [o_dropzone_box frame];
410 plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
411 plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
412 [[o_dropzone_box animator] setFrame: plrect];
415 - (void)makeSplitViewVisible
417 if (b_dark_interface)
418 [self setContentMinSize: NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
420 [self setContentMinSize: NSMakeSize(604., 288.)];
422 NSRect old_frame = [self frame];
423 float newHeight = [self minSize].height;
424 if (old_frame.size.height < newHeight) {
425 NSRect new_frame = old_frame;
426 new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
427 new_frame.size.height = newHeight;
429 [[self animator] setFrame: new_frame display: YES animate: YES];
432 [o_video_view setHidden: YES];
433 [o_split_view setHidden: NO];
434 [self makeFirstResponder: nil];
438 - (void)makeSplitViewHidden
440 if (b_dark_interface)
441 [self setContentMinSize: NSMakeSize(604., f_min_video_height + [o_titlebar_view frame].size.height)];
443 [self setContentMinSize: NSMakeSize(604., f_min_video_height)];
445 [o_split_view setHidden: YES];
446 [o_video_view setHidden: NO];
448 if ([[o_video_view subviews] count] > 0)
449 [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
452 // only exception for an controls bar button action
453 - (IBAction)togglePlaylist:(id)sender
455 if (![self isVisible] && sender != nil) {
456 [self makeKeyAndOrderFront: sender];
460 BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
461 BOOL b_restored = NO;
463 // TODO: implement toggle playlist in this situation (triggerd via menu item).
464 // but for now we block this case, to avoid displaying only the half
465 if (b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil)
468 if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0) {
473 if (!(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ((([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
474 || (b_nonembedded && sender != nil)
475 || (!b_activeVideo && sender != nil)
476 || b_minimized_view))
477 [self hideSplitView];
479 if (b_splitview_removed) {
480 if (!b_nonembedded || (sender != nil && b_nonembedded))
481 [self showSplitView];
484 b_minimized_view = YES;
486 b_minimized_view = NO;
492 if (!b_nonembedded) {
493 if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil))
494 [self makeSplitViewHidden];
496 [self makeSplitViewVisible];
498 [o_split_view setHidden: NO];
499 [o_playlist_table setHidden: NO];
500 [o_video_view setHidden: !b_activeVideo];
501 if (b_activeVideo && [[o_video_view subviews] count] > 0)
502 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
507 - (IBAction)dropzoneButtonAction:(id)sender
509 [[[VLCMain sharedInstance] open] openFileGeneric];
513 #pragma mark overwritten default functionality
515 - (void)windowResizedOrMoved:(NSNotification *)notification
517 [self saveFrameUsingName: [self frameAutosaveName]];
520 - (void)applicationWillTerminate:(NSNotification *)notification
522 [self saveFrameUsingName: [self frameAutosaveName]];
526 - (void)someWindowWillClose:(NSNotification *)notification
528 id obj = [notification object];
530 if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !b_nonembedded)) {
531 if ([[VLCMain sharedInstance] activeVideoPlayback])
532 [[VLCCoreInteraction sharedInstance] stop];
536 - (void)someWindowWillMiniaturize:(NSNotification *)notification
538 if (config_GetInt(VLCIntf, "macosx-pause-minimized")) {
539 id obj = [notification object];
541 if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !b_nonembedded)) {
542 if ([[VLCMain sharedInstance] activeVideoPlayback])
543 [[VLCCoreInteraction sharedInstance] pause];
549 #pragma mark Update interface and respond to foreign events
552 b_dropzone_active = YES;
553 [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
554 [o_dropzone_view setFrame: [o_playlist_table frame]];
555 [[o_playlist_table animator] setHidden:YES];
560 b_dropzone_active = NO;
561 [o_dropzone_view removeFromSuperview];
562 [[o_playlist_table animator] setHidden: NO];
565 - (void)hideSplitView
567 NSRect winrect = [self frame];
568 i_lastSplitViewHeight = [o_split_view frame].size.height;
569 winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
570 winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
571 [self setFrame: winrect display: YES animate: YES];
572 [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
573 if (b_dark_interface) {
574 [self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
575 [self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
577 [self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height)];
578 [self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height)];
581 b_splitview_removed = YES;
584 - (void)showSplitView
587 if (b_dark_interface)
588 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
590 [self setContentMinSize:NSMakeSize(604., 288.)];
591 [self setContentMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
594 winrect = [self frame];
595 winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
596 winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
597 [self setFrame: winrect display: YES animate: YES];
599 [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
601 b_splitview_removed = NO;
604 - (void)updateTimeSlider
606 [o_controls_bar updateTimeSlider];
607 [[self controlsBar] updatePosAndTimeInFSPanel:o_fspanel];
609 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateTimeSlider)];
614 input_thread_t * p_input;
615 p_input = pl_CurrentInput(VLCIntf);
618 char *format = var_InheritString(VLCIntf, "input-title-format");
619 char *formated = str_format_meta(pl_Get(VLCIntf), format);
621 aString = [NSString stringWithUTF8String:formated];
624 char *uri = input_item_GetURI(input_GetItem(p_input));
626 NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
627 if ([o_url isFileURL]) {
628 [self setRepresentedURL: o_url];
629 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
630 [o_window setRepresentedURL:o_url];
633 [self setRepresentedURL: nil];
634 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
635 [o_window setRepresentedURL:nil];
640 if ([aString isEqualToString:@""]) {
641 if ([o_url isFileURL])
642 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
644 aString = [o_url absoluteString];
647 [self setTitle: aString];
648 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
649 [o_window setTitle:aString];
652 [o_fspanel setStreamTitle: aString];
653 vlc_object_release(p_input);
655 [self setTitle: _NS("VLC media player")];
656 [self setRepresentedURL: nil];
662 [o_controls_bar updateControls];
663 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateControls)];
665 bool b_seekable = false;
667 playlist_t * p_playlist = pl_Get(VLCIntf);
668 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
670 /* seekable streams */
671 b_seekable = var_GetBool(p_input, "can-seek");
673 vlc_object_release(p_input);
676 [self updateTimeSlider];
677 if ([o_fspanel respondsToSelector:@selector(setSeekable:)])
678 [o_fspanel setSeekable: b_seekable];
681 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
686 [o_sidebar_view setNeedsDisplay:YES];
691 [o_controls_bar setPause];
692 [o_fspanel setPause];
694 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPause)];
699 [o_controls_bar setPlay];
702 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPlay)];
706 - (void)updateVolumeSlider
708 [[self controlsBar] updateVolumeSlider];
709 [o_fspanel setVolumeLevel: [[VLCCoreInteraction sharedInstance] volume]];
713 #pragma mark Video Output handling
715 - (void)setVideoplayEnabled
717 BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
719 if (b_videoPlayback) {
721 frameBeforePlayback = [self frame];
723 if (!b_nonembedded && !b_fullscreen && frameBeforePlayback.size.width > 0 && frameBeforePlayback.size.height > 0)
724 [[self animator] setFrame:frameBeforePlayback display:YES];
726 // update fs button to reflect state for next startup
727 if (var_InheritBool(pl_Get(VLCIntf), "fullscreen")) {
728 [o_controls_bar setFullscreenState:YES];
731 [self makeFirstResponder: nil];
733 if ([self level] != NSNormalWindowLevel)
734 [self setLevel: NSNormalWindowLevel];
736 // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
737 [self setAlphaValue:1.0];
740 if (b_nativeFullscreenMode) {
741 if ([NSApp presentationOptions] & NSApplicationPresentationFullScreen)
742 [[o_controls_bar bottomBarView] setHidden: b_videoPlayback];
744 [[o_controls_bar bottomBarView] setHidden: NO];
745 if (b_videoPlayback && b_fullscreen)
746 [o_fspanel setActive: nil];
747 if (!b_videoPlayback)
748 [o_fspanel setNonActive: nil];
752 // Called automatically if window's acceptsMouseMovedEvents property is true
753 - (void)mouseMoved:(NSEvent *)theEvent
756 [self recreateHideMouseTimer];
758 [super mouseMoved: theEvent];
761 - (void)recreateHideMouseTimer
763 if (t_hide_mouse_timer != nil) {
764 [t_hide_mouse_timer invalidate];
765 [t_hide_mouse_timer release];
768 t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
770 selector:@selector(hideMouseCursor:)
773 [t_hide_mouse_timer retain];
776 // NSTimer selectors require this function signature as per Apple's docs
777 - (void)hideMouseCursor:(NSTimer *)timer
779 [NSCursor setHiddenUntilMouseMoves: YES];
784 #pragma mark Fullscreen support
786 - (void)showFullscreenController
788 if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback])
792 - (void)makeKeyAndOrderFront: (id)sender
795 * when we exit fullscreen and fade out, we may endup in
796 * having a window that is faded. We can't have it fade in unless we
799 if (!b_window_is_invisible) {
800 /* Make sure we don't do it too much */
801 [super makeKeyAndOrderFront: sender];
805 [super setAlphaValue:0.0f];
806 [super makeKeyAndOrderFront: sender];
808 NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
809 [dict setObject:self forKey:NSViewAnimationTargetKey];
810 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
812 o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
815 [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
816 [o_makekey_anim setDuration: 0.1];
817 [o_makekey_anim setFrameRate: 30];
818 [o_makekey_anim setDelegate: self];
820 [o_makekey_anim startAnimation];
821 b_window_is_invisible = NO;
823 /* fullscreenAnimation will be unlocked when animation ends */
827 #pragma mark Lion native fullscreen handling
828 - (void)windowWillEnterFullScreen:(NSNotification *)notification
830 // workaround, see #6668
831 [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
833 var_SetBool(pl_Get(VLCIntf), "fullscreen", true);
835 vout_thread_t *p_vout = getVout();
837 var_SetBool(p_vout, "fullscreen", true);
838 vlc_object_release(p_vout);
841 [o_video_view setFrame: [[self contentView] frame]];
844 [self recreateHideMouseTimer];
845 i_originalLevel = [self level];
846 [self setLevel:NSNormalWindowLevel];
848 if (b_dark_interface) {
849 [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
852 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
853 winrect = [self frame];
855 winrect.size.height = winrect.size.height - f_titleBarHeight;
856 [self setFrame: winrect display:NO animate:NO];
857 winrect = [o_split_view frame];
858 winrect.size.height = winrect.size.height + f_titleBarHeight;
859 [o_split_view setFrame: winrect];
862 if ([[VLCMain sharedInstance] activeVideoPlayback])
863 [[o_controls_bar bottomBarView] setHidden: YES];
865 [self setMovableByWindowBackground: NO];
868 - (void)windowDidEnterFullScreen:(NSNotification *)notification
870 // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
871 // But this creates some problems when leaving fs over remote intfs, so activate app here.
872 [NSApp activateIgnoringOtherApps:YES];
874 [o_fspanel setVoutWasUpdated: self];
875 [o_fspanel setActive: nil];
878 - (void)windowWillExitFullScreen:(NSNotification *)notification
881 var_SetBool(pl_Get(VLCIntf), "fullscreen", false);
883 vout_thread_t *p_vout = getVout();
885 var_SetBool(p_vout, "fullscreen", false);
886 vlc_object_release(p_vout);
889 [o_video_view setFrame: [o_split_view frame]];
890 [NSCursor setHiddenUntilMouseMoves: NO];
891 [o_fspanel setNonActive: nil];
892 [self setLevel:i_originalLevel];
895 if (b_dark_interface) {
897 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
898 winrect = [self frame];
900 [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
901 winrect.size.width, f_titleBarHeight)];
902 [[self contentView] addSubview: o_titlebar_view];
904 winrect.size.height = winrect.size.height + f_titleBarHeight;
905 [self setFrame: winrect display:NO animate:NO];
906 winrect = [o_split_view frame];
907 winrect.size.height = winrect.size.height - f_titleBarHeight;
908 [o_split_view setFrame: winrect];
909 [o_video_view setFrame: winrect];
912 if ([[VLCMain sharedInstance] activeVideoPlayback])
913 [[o_controls_bar bottomBarView] setHidden: NO];
915 [self setMovableByWindowBackground: YES];
919 #pragma mark split view delegate
920 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
922 if (dividerIndex == 0)
928 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
930 if (dividerIndex == 0)
936 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
938 return ([subview isEqual:o_left_split_view]);
941 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
943 if ([subview isEqual:o_left_split_view])
949 #pragma mark Side Bar Data handling
950 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
951 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
953 //Works the same way as the NSOutlineView data source: `nil` means a parent item
955 return [o_sidebaritems count];
957 return [[item children] count];
961 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
963 //Works the same way as the NSOutlineView data source: `nil` means a parent item
965 return [o_sidebaritems objectAtIndex:index];
967 return [[item children] objectAtIndex:index];
971 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
976 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
978 [item setTitle:object];
981 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
983 return [item hasChildren];
987 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
989 if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
992 return [item hasBadge];
996 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
998 playlist_t * p_playlist = pl_Get(VLCIntf);
999 NSInteger i_playlist_size;
1001 if ([[item identifier] isEqualToString: @"playlist"]) {
1003 i_playlist_size = p_playlist->p_local_category->i_children;
1006 return i_playlist_size;
1008 if ([[item identifier] isEqualToString: @"medialibrary"]) {
1010 i_playlist_size = p_playlist->p_ml_category->i_children;
1013 return i_playlist_size;
1016 return [item badgeValue];
1020 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
1022 return [item hasIcon];
1026 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
1031 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
1033 if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
1036 if ([item sdtype] > 0)
1038 m = [[NSMenu alloc] init];
1039 playlist_t * p_playlist = pl_Get(VLCIntf);
1040 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
1042 [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1044 [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1045 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
1047 return [m autorelease];
1054 - (IBAction)sdmenuhandler:(id)sender
1056 NSString * identifier = [sender representedObject];
1057 if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"]) {
1058 playlist_t * p_playlist = pl_Get(VLCIntf);
1059 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [identifier UTF8String]);
1062 playlist_ServicesDiscoveryAdd(p_playlist, [identifier UTF8String]);
1064 playlist_ServicesDiscoveryRemove(p_playlist, [identifier UTF8String]);
1069 #pragma mark Side Bar Delegate Methods
1070 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1071 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
1073 if ([[group identifier] isEqualToString:@"library"])
1079 - (void)sourceListSelectionDidChange:(NSNotification *)notification
1081 playlist_t * p_playlist = pl_Get(VLCIntf);
1083 NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
1084 id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
1087 //Set the label text to represent the new selection
1088 if ([item sdtype] > -1 && [[item identifier] length] > 0) {
1089 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
1091 playlist_ServicesDiscoveryAdd(p_playlist, [[item identifier] UTF8String]);
1094 [o_chosen_category_lbl setStringValue:[item title]];
1096 if ([[item identifier] isEqualToString:@"playlist"]) {
1097 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
1098 } else if ([[item identifier] isEqualToString:@"medialibrary"]) {
1099 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
1101 playlist_item_t * pl_item;
1103 pl_item = playlist_ChildSearchName(p_playlist->p_root, [[item untranslatedTitle] UTF8String]);
1105 [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
1109 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1110 [self hideDropZone];
1112 [self showDropZone];
1115 if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
1116 [self showPodcastControls];
1118 [self hidePodcastControls];
1121 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1123 if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"]) {
1124 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1125 if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
1126 return NSDragOperationGeneric;
1128 return NSDragOperationNone;
1131 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1133 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1135 playlist_t * p_playlist = pl_Get(VLCIntf);
1136 playlist_item_t *p_node;
1138 if ([[item identifier] isEqualToString:@"playlist"])
1139 p_node = p_playlist->p_local_category;
1141 p_node = p_playlist->p_ml_category;
1143 if ([[o_pasteboard types] containsObject: NSFilenamesPboardType]) {
1144 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1145 NSUInteger count = [o_values count];
1146 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
1148 for(NSUInteger i = 0; i < count; i++) {
1149 NSDictionary *o_dic;
1150 char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
1154 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
1158 [o_array addObject: o_dic];
1161 [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
1164 else if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"]) {
1165 NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
1167 NSUInteger count = [array count];
1168 playlist_item_t * p_item = NULL;
1171 for(NSUInteger i = 0; i < count; i++) {
1172 p_item = [[array objectAtIndex:i] pointerValue];
1173 if (!p_item) continue;
1174 playlist_NodeAddCopy(p_playlist, p_item, p_node, PLAYLIST_END);
1183 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
1185 return [item identifier];
1188 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
1190 /* the following code assumes for sakes of simplicity that only the top level
1191 * items are allowed to have children */
1193 NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
1194 NSUInteger count = [array count];
1198 for (NSUInteger x = 0; x < count; x++) {
1199 id item = [array objectAtIndex: x]; // save one objc selector call
1200 if ([[item identifier] isEqualToString:object])
1208 #pragma mark Podcast
1210 - (IBAction)addPodcast:(id)sender
1212 [NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
1215 - (IBAction)addPodcastWindowAction:(id)sender
1217 [o_podcast_subscribe_window orderOut:sender];
1218 [NSApp endSheet: o_podcast_subscribe_window];
1220 if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
1221 NSMutableString * podcastConf = [[NSMutableString alloc] init];
1222 if (config_GetPsz(VLCIntf, "podcast-urls") != NULL)
1223 [podcastConf appendFormat:@"%s|", config_GetPsz(VLCIntf, "podcast-urls")];
1225 [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
1226 config_PutPsz(VLCIntf, "podcast-urls", [podcastConf UTF8String]);
1228 vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name(VLCIntf->p_libvlc, "podcast");
1230 var_SetString(p_obj, "podcast-urls", [podcastConf UTF8String]);
1231 vlc_object_release(p_obj);
1233 [podcastConf release];
1237 - (IBAction)removePodcast:(id)sender
1239 if (config_GetPsz(VLCIntf, "podcast-urls") != NULL) {
1240 [o_podcast_unsubscribe_pop removeAllItems];
1241 [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
1242 [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
1246 - (IBAction)removePodcastWindowAction:(id)sender
1248 [o_podcast_unsubscribe_window orderOut:sender];
1249 [NSApp endSheet: o_podcast_unsubscribe_window];
1251 if (sender == o_podcast_unsubscribe_ok_btn) {
1252 NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
1253 [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
1254 config_PutPsz(VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String]);
1257 vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name(VLCIntf->p_libvlc, "podcast");
1259 var_SetString(p_obj, "podcast-urls", config_GetPsz(VLCIntf, "podcast-urls"));
1260 vlc_object_release(p_obj);
1263 /* reload the podcast module, since it won't update its list when removing podcasts */
1264 playlist_t * p_playlist = pl_Get(VLCIntf);
1265 if (playlist_IsServicesDiscoveryLoaded(p_playlist, "podcast{longname=\"Podcasts\"}")) {
1266 playlist_ServicesDiscoveryRemove(p_playlist, "podcast{longname=\"Podcasts\"}");
1267 playlist_ServicesDiscoveryAdd(p_playlist, "podcast{longname=\"Podcasts\"}");
1268 [o_playlist_table reloadData];
1274 - (void)showPodcastControls
1276 NSRect podcastViewDimensions = [o_podcast_view frame];
1277 NSRect rightSplitRect = [o_right_split_view frame];
1278 NSRect playlistTableRect = [o_playlist_table frame];
1280 podcastViewDimensions.size.width = rightSplitRect.size.width;
1281 podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
1282 [o_podcast_view setFrame:podcastViewDimensions];
1284 playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
1285 playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
1286 [o_playlist_table setFrame:playlistTableRect];
1287 [o_playlist_table setNeedsDisplay:YES];
1289 [o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
1290 b_podcastView_displayed = YES;
1293 - (void)hidePodcastControls
1295 if (b_podcastView_displayed) {
1296 NSRect podcastViewDimensions = [o_podcast_view frame];
1297 NSRect playlistTableRect = [o_playlist_table frame];
1299 playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
1300 playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
1302 [o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
1303 [o_playlist_table setFrame: playlistTableRect];
1304 b_podcastView_displayed = NO;
1310 @implementation VLCDetachedVideoWindow
1312 - (void)awakeFromNib
1314 [self setAcceptsMouseMovedEvents: YES];
1316 if (b_dark_interface) {
1317 [self setBackgroundColor: [NSColor clearColor]];
1319 [self setOpaque: NO];
1321 [self setHasShadow:NO];
1322 [self setHasShadow:YES];
1324 NSRect winrect = [self frame];
1325 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1327 [self setTitle: _NS("VLC media player")];
1328 [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight)];
1329 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: nil];
1331 // native fs not supported with detached view yet
1332 [o_titlebar_view setFullscreenButtonHidden: YES];
1334 [self setBackgroundColor: [NSColor blackColor]];
1337 NSRect videoViewRect = [[self contentView] bounds];
1338 if (b_dark_interface)
1339 videoViewRect.size.height -= [o_titlebar_view frame].size.height;
1340 CGFloat f_bottomBarHeight = [[[self controlsBar] bottomBarView] frame].size.height;
1341 videoViewRect.size.height -= f_bottomBarHeight;
1342 videoViewRect.origin.y = f_bottomBarHeight;
1343 [o_video_view setFrame: videoViewRect];
1345 if (b_dark_interface) {
1346 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_video_view frame]];
1347 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_video_view];
1348 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
1350 [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[[self controlsBar] bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
1352 [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[[self controlsBar] bottomBarView] frame].size.height)];
1358 if (b_dark_interface)
1359 [o_color_backdrop release];