1 /*****************************************************************************
2 * MainWindow.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2013 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>
11 * David Fuhrmann <david dot fuhrmann at googlemail dot com>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
29 #import "CompatibilityFixes.h"
30 #import "MainWindow.h"
31 #import "CoreInteraction.h"
32 #import "AudioEffects.h"
35 #import "controls.h" // TODO: remove me
37 #import "SideBarItem.h"
39 #import <vlc_playlist.h>
41 #import <vlc_strings.h>
42 #import <vlc_services_discovery.h>
44 #import "ControlsBar.h"
46 #import "VLCVoutWindowController.h"
49 @interface VLCMainWindow (Internal)
50 - (void)resizePlaylistAfterCollapse;
51 - (void)makeSplitViewVisible;
52 - (void)makeSplitViewHidden;
53 - (void)showPodcastControls;
54 - (void)hidePodcastControls;
58 @implementation VLCMainWindow
60 @synthesize nativeFullscreenMode=b_nativeFullscreenMode;
61 @synthesize nonembedded=b_nonembedded;
62 @synthesize fsPanel=o_fspanel;
64 static VLCMainWindow *_o_sharedInstance = nil;
66 + (VLCMainWindow *)sharedInstance
68 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
72 #pragma mark Initialization
76 if (_o_sharedInstance) {
78 return _o_sharedInstance;
80 _o_sharedInstance = [super init];
82 return _o_sharedInstance;
85 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
86 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
88 self = [super initWithContentRect:contentRect styleMask:styleMask
89 backing:backingType defer:flag];
90 _o_sharedInstance = self;
92 [[VLCMain sharedInstance] updateTogglePlaylistState];
97 - (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
102 key = config_GetPsz(VLCIntf, keyString);
103 o_key = [NSString stringWithFormat:@"%s", key];
106 unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
108 NSString * characters = [o_event charactersIgnoringModifiers];
109 if ([characters length] > 0) {
110 return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
111 (i_keyModifiers & NSShiftKeyMask) == ([o_event modifierFlags] & NSShiftKeyMask) &&
112 (i_keyModifiers & NSControlKeyMask) == ([o_event modifierFlags] & NSControlKeyMask) &&
113 (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
114 (i_keyModifiers & NSCommandKeyMask) == ([o_event modifierFlags] & NSCommandKeyMask);
119 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
122 // these are key events which should be handled by vlc core, but are attached to a main menu item
123 if (![self isEvent: o_event forKey: "key-vol-up"] &&
124 ![self isEvent: o_event forKey: "key-vol-down"] &&
125 ![self isEvent: o_event forKey: "key-vol-mute"] &&
126 ![self isEvent: o_event forKey: "key-prev"] &&
127 ![self isEvent: o_event forKey: "key-next"] &&
128 ![self isEvent: o_event forKey: "key-jump+short"] &&
129 ![self isEvent: o_event forKey: "key-jump-short"]) {
130 /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
131 so we perform the menu equivalent now. */
132 if ([[NSApp mainMenu] performKeyEquivalent:o_event])
138 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
139 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
144 if (b_dark_interface)
145 [o_color_backdrop release];
147 [[NSNotificationCenter defaultCenter] removeObserver: self];
148 [o_sidebaritems release];
156 // sets lion fullscreen behaviour
157 [super awakeFromNib];
159 BOOL b_splitviewShouldBeHidden = NO;
161 if (!OSX_SNOW_LEOPARD)
162 [self setRestorable: NO];
163 [self setFrameAutosaveName:@"mainwindow"];
165 /* setup the styled interface */
166 b_nativeFullscreenMode = NO;
167 #ifdef MAC_OS_X_VERSION_10_7
168 if (!OSX_SNOW_LEOPARD)
169 b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
171 [self useOptimizedDrawing: YES];
173 [[o_search_fld cell] setPlaceholderString: _NS("Search")];
174 [[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
176 [o_dropzone_btn setTitle: _NS("Open media...")];
177 [[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];
178 [o_dropzone_lbl setStringValue: _NS("Drop media here")];
180 [o_podcast_add_btn setTitle: _NS("Subscribe")];
181 [o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
182 [o_podcast_subscribe_title_lbl setStringValue: _NS("Subscribe to a podcast")];
183 [o_podcast_subscribe_subtitle_lbl setStringValue: _NS("Enter URL of the podcast to subscribe to:")];
184 [o_podcast_subscribe_cancel_btn setTitle: _NS("Cancel")];
185 [o_podcast_subscribe_ok_btn setTitle: _NS("Subscribe")];
186 [o_podcast_unsubscribe_title_lbl setStringValue: _NS("Unsubscribe from a podcast")];
187 [o_podcast_unsubscribe_subtitle_lbl setStringValue: _NS("Select the podcast you would like to unsubscribe from:")];
188 [o_podcast_unsubscribe_ok_btn setTitle: _NS("Unsubscribe")];
189 [o_podcast_unsubscribe_cancel_btn setTitle: _NS("Cancel")];
191 /* interface builder action */
192 CGFloat f_threshold_height = f_min_video_height + [o_controls_bar height];
193 if (b_dark_interface)
194 f_threshold_height += [o_titlebar_view frame].size.height;
195 if ([[self contentView] frame].size.height < f_threshold_height)
196 b_splitviewShouldBeHidden = YES;
198 [self setDelegate: self];
199 [self setExcludedFromWindowsMenu: YES];
200 [self setAcceptsMouseMovedEvents: YES];
201 // Set that here as IB seems to be buggy
202 if (b_dark_interface)
203 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
205 [self setContentMinSize:NSMakeSize(604., 288.)];
207 [self setTitle: _NS("VLC media player")];
209 b_dropzone_active = YES;
210 [o_dropzone_view setFrame: [o_playlist_table frame]];
211 [o_left_split_view setFrame: [o_sidebar_view frame]];
213 if (!OSX_SNOW_LEOPARD) {
214 /* the default small size of the search field is slightly different on Lion, let's work-around that */
216 frame = [o_search_fld frame];
217 frame.origin.y = frame.origin.y + 2.0;
218 frame.size.height = frame.size.height - 1.0;
219 [o_search_fld setFrame: frame];
222 /* reload the sidebar */
223 [self reloadSidebar];
225 o_fspanel = [[VLCFSPanel alloc] initWithContentRect:NSMakeRect(110.,267.,549.,87.)
226 styleMask:NSTexturedBackgroundWindowMask
227 backing:NSBackingStoreBuffered
230 /* make sure we display the desired default appearance when VLC launches for the first time */
231 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
232 if (![defaults objectForKey:@"VLCFirstRun"]) {
233 [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
235 NSUInteger i_sidebaritem_count = [o_sidebaritems count];
236 for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
237 [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex:x] expandChildren: YES];
241 NSAlert *albumArtAlert = [NSAlert alertWithMessageText:_NS("Check for album art and metadata?") defaultButton:_NS("Enable Metadata Retrieval") alternateButton:_NS("No, Thanks") otherButton:nil informativeTextWithFormat:@"%@",_NS("VLC can check online for album art and metadata to enrich your playback experience, e.g. by providing track information when playing Audio CDs. To provide this functionality, VLC will send information about your contents to trusted services in an anonymized form.")];
242 NSInteger returnValue = [albumArtAlert runModal];
243 config_PutInt(VLCIntf, "metadata-network-access", returnValue == NSAlertDefaultReturn);
246 // select playlist item by default
247 [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
249 if (b_dark_interface) {
250 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
251 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
253 [self setBackgroundColor: [NSColor clearColor]];
254 [self setOpaque: NO];
256 [self setHasShadow:NO];
257 [self setHasShadow:YES];
259 NSRect winrect = [self frame];
260 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
262 [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
263 winrect.size.width, f_titleBarHeight)];
264 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
266 if (winrect.size.height > 100) {
267 [self setFrame: winrect display:YES animate:YES];
268 previousSavedFrame = winrect;
271 winrect = [o_split_view frame];
272 winrect.size.height = winrect.size.height - f_titleBarHeight;
273 [o_split_view setFrame: winrect];
274 [o_video_view setFrame: winrect];
276 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
277 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
278 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
280 [o_video_view setFrame: [o_split_view frame]];
281 [o_playlist_table setBorderType: NSNoBorder];
282 [o_sidebar_scrollview setBorderType: NSNoBorder];
285 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
286 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
287 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
288 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(mainSplitViewDidResizeSubviews:) name: NSSplitViewDidResizeSubviewsNotification object:o_split_view];
290 if (b_splitviewShouldBeHidden) {
291 [self hideSplitView: YES];
292 f_lastSplitViewHeight = 300;
295 /* sanity check for the window size */
296 NSRect frame = [self frame];
297 NSSize screenSize = [[self screen] frame].size;
298 if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
299 nativeVideoSize = screenSize;
303 /* update fs button to reflect state for next startup */
304 if (var_InheritBool(pl_Get(VLCIntf), "fullscreen"))
305 [o_controls_bar setFullscreenState:YES];
307 /* restore split view */
308 f_lastLeftSplitViewWidth = 200;
309 /* trick NSSplitView implementation, which pretends to know better than us */
310 if (!config_GetInt(VLCIntf, "macosx-show-sidebar"))
311 [self performSelector:@selector(toggleLeftSubSplitView) withObject:nil afterDelay:0.05];
315 #pragma mark appearance management
317 - (void)reloadSidebar
320 if (o_sidebaritems) {
321 [o_sidebaritems release];
325 o_sidebaritems = [[NSMutableArray alloc] init];
326 SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
327 SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
328 [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
329 SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
330 [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
331 SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
332 SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
333 SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
334 SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
336 /* SD subnodes, inspired by the Qt4 intf */
337 char **ppsz_longnames = NULL;
338 int *p_categories = NULL;
339 char **ppsz_names = vlc_sd_GetNames(pl_Get(VLCIntf), &ppsz_longnames, &p_categories);
341 msg_Err(VLCIntf, "no sd item found"); //TODO
342 char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
343 int *p_category = p_categories;
344 NSMutableArray *internetItems = [[NSMutableArray alloc] init];
345 NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
346 NSMutableArray *lanItems = [[NSMutableArray alloc] init];
347 NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
348 NSString *o_identifier;
349 for (; ppsz_name && *ppsz_name; ppsz_name++, ppsz_longname++, p_category++) {
350 o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
351 switch (*p_category) {
352 case SD_CAT_INTERNET:
353 [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
354 if (!strncmp(*ppsz_name, "podcast", 7))
355 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
357 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
358 [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
359 [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
362 [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
363 [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
364 [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
365 [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
368 [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
369 [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
370 [[lanItems lastObject] setSdtype: SD_CAT_LAN];
371 [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
373 case SD_CAT_MYCOMPUTER:
374 [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
375 if (!strncmp(*ppsz_name, "video_dir", 9))
376 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
377 else if (!strncmp(*ppsz_name, "audio_dir", 9))
378 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
379 else if (!strncmp(*ppsz_name, "picture_dir", 11))
380 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
382 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
383 [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
384 [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
387 msg_Warn(VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name);
392 free(*ppsz_longname);
394 [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
395 [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
396 [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
397 [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
398 [mycompItems release];
399 [devicesItems release];
401 [internetItems release];
403 free(ppsz_longnames);
406 [libraryItem setChildren: [NSArray arrayWithObjects:playlistItem, medialibraryItem, nil]];
407 [o_sidebaritems addObject: libraryItem];
408 if ([mycompItem hasChildren])
409 [o_sidebaritems addObject: mycompItem];
410 if ([devicesItem hasChildren])
411 [o_sidebaritems addObject: devicesItem];
412 if ([lanItem hasChildren])
413 [o_sidebaritems addObject: lanItem];
414 if ([internetItem hasChildren])
415 [o_sidebaritems addObject: internetItem];
417 [o_sidebar_view reloadData];
418 [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
419 [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
421 [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
422 [(PXSourceList *)o_sidebar_view setDataSource:self];
423 [o_sidebar_view setDelegate:self];
424 [o_sidebar_view setAutosaveExpandedItems:YES];
426 [o_sidebar_view expandItem: libraryItem expandChildren: YES];
429 NSUInteger i_sidebaritem_count = [o_sidebaritems count];
430 for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
431 [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex:x] expandChildren: YES];
435 - (VLCMainWindowControlsBar *)controlsBar;
437 return (VLCMainWindowControlsBar *)o_controls_bar;
440 - (void)resizePlaylistAfterCollapse
442 // no animation here since we might be in the middle of another resize animation
444 plrect = [o_playlist_table frame];
445 plrect.size.height = [o_split_view frame].size.height - 20.0; // actual pl top bar height, which differs from its frame
446 [o_playlist_table setFrame: plrect];
447 [o_playlist_table setNeedsDisplay: YES];
449 NSRect rightSplitRect;
450 rightSplitRect = [o_right_split_view frame];
451 plrect = [o_dropzone_box frame];
452 plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
453 plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
454 [o_dropzone_view setFrame: [o_playlist_table frame]];
455 [o_dropzone_box setFrame: plrect];
456 [o_dropzone_view setNeedsDisplay: YES];
459 - (void)makeSplitViewVisible
461 if (b_dark_interface)
462 [self setContentMinSize: NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
464 [self setContentMinSize: NSMakeSize(604., 288.)];
466 NSRect old_frame = [self frame];
467 CGFloat newHeight = [self minSize].height;
468 if (old_frame.size.height < newHeight) {
469 NSRect new_frame = old_frame;
470 new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
471 new_frame.size.height = newHeight;
473 [[self animator] setFrame: new_frame display: YES animate: YES];
476 [o_video_view setHidden: YES];
477 [o_split_view setHidden: NO];
478 if (b_nativeFullscreenMode && [self fullscreen]) {
479 [[o_controls_bar bottomBarView] setHidden: NO];
480 [o_fspanel setNonActive:nil];
483 [self makeFirstResponder: o_playlist_table];
486 - (void)makeSplitViewHidden
488 if (b_dark_interface)
489 [self setContentMinSize: NSMakeSize(604., f_min_video_height + [o_titlebar_view frame].size.height)];
491 [self setContentMinSize: NSMakeSize(604., f_min_video_height)];
493 [o_split_view setHidden: YES];
494 [o_video_view setHidden: NO];
495 if (b_nativeFullscreenMode && [self fullscreen]) {
496 [[o_controls_bar bottomBarView] setHidden: YES];
497 [o_fspanel setActive:nil];
500 if ([[o_video_view subviews] count] > 0)
501 [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
505 - (void)changePlaylistState:(VLCPlaylistStateEvent)event
507 // Beware, this code is really ugly
509 msg_Dbg(VLCIntf, "toggle playlist from state: removed splitview %i, minimized view %i. Event %i", b_splitview_removed, b_minimized_view, event);
510 if (![self isVisible] && event == psUserMenuEvent) {
511 [self makeKeyAndOrderFront: nil];
515 BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
516 BOOL b_restored = NO;
518 // ignore alt if triggered through main menu shortcut
519 BOOL b_have_alt_key = ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0;
520 if (event == psUserMenuEvent)
523 // eUserMenuEvent is now handled same as eUserEvent
524 if(event == psUserMenuEvent)
527 if (b_dropzone_active && b_have_alt_key) {
532 if (!(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ((b_have_alt_key && b_activeVideo)
533 || (b_nonembedded && event == psUserEvent)
534 || (!b_activeVideo && event == psUserEvent)
535 || (b_minimized_view && event == psVideoStartedOrStoppedEvent))) {
536 // for starting playback, window is resized through resized events
537 // for stopping playback, resize through reset to previous frame
538 [self hideSplitView: event != psVideoStartedOrStoppedEvent];
539 b_minimized_view = NO;
541 if (b_splitview_removed) {
542 if (!b_nonembedded || (event == psUserEvent && b_nonembedded))
543 [self showSplitView: event != psVideoStartedOrStoppedEvent];
545 if (event != psUserEvent)
546 b_minimized_view = YES;
548 b_minimized_view = NO;
554 if (!b_nonembedded) {
555 if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && event != psUserEvent))
556 [self makeSplitViewHidden];
558 [self makeSplitViewVisible];
560 [o_split_view setHidden: NO];
561 [o_playlist_table setHidden: NO];
562 [o_video_view setHidden: YES];
566 msg_Dbg(VLCIntf, "toggle playlist to state: removed splitview %i, minimized view %i", b_splitview_removed, b_minimized_view);
569 - (IBAction)dropzoneButtonAction:(id)sender
571 [[[VLCMain sharedInstance] open] openFileGeneric];
575 #pragma mark overwritten default functionality
577 - (void)windowResizedOrMoved:(NSNotification *)notification
579 [self saveFrameUsingName: [self frameAutosaveName]];
582 - (void)applicationWillTerminate:(NSNotification *)notification
584 config_PutInt(VLCIntf, "macosx-show-sidebar", ![o_split_view isSubviewCollapsed:o_left_split_view]);
586 [self saveFrameUsingName: [self frameAutosaveName]];
590 - (void)someWindowWillClose:(NSNotification *)notification
592 id obj = [notification object];
594 // hasActiveVideo is defined for VLCVideoWindowCommon and subclasses
595 if ([obj respondsToSelector:@selector(hasActiveVideo)] && [obj hasActiveVideo]) {
596 if ([[VLCMain sharedInstance] activeVideoPlayback])
597 [[VLCCoreInteraction sharedInstance] stop];
601 - (void)someWindowWillMiniaturize:(NSNotification *)notification
603 if (config_GetInt(VLCIntf, "macosx-pause-minimized")) {
604 id obj = [notification object];
606 if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !b_nonembedded)) {
607 if ([[VLCMain sharedInstance] activeVideoPlayback])
608 [[VLCCoreInteraction sharedInstance] pause];
614 #pragma mark Update interface and respond to foreign events
617 b_dropzone_active = YES;
618 [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
619 [o_dropzone_view setFrame: [o_playlist_table frame]];
620 [o_playlist_table setHidden:YES];
625 b_dropzone_active = NO;
626 [o_dropzone_view removeFromSuperview];
627 [o_playlist_table setHidden: NO];
630 - (void)hideSplitView:(BOOL)b_with_resize
633 NSRect winrect = [self frame];
634 f_lastSplitViewHeight = [o_split_view frame].size.height;
635 winrect.size.height = winrect.size.height - f_lastSplitViewHeight;
636 winrect.origin.y = winrect.origin.y + f_lastSplitViewHeight;
637 [self setFrame: winrect display: YES animate: YES];
640 [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
641 if (b_dark_interface) {
642 [self setContentMinSize: NSMakeSize(604., [o_controls_bar height] + [o_titlebar_view frame].size.height)];
643 [self setContentMaxSize: NSMakeSize(FLT_MAX, [o_controls_bar height] + [o_titlebar_view frame].size.height)];
645 [self setContentMinSize: NSMakeSize(604., [o_controls_bar height])];
646 [self setContentMaxSize: NSMakeSize(FLT_MAX, [o_controls_bar height])];
649 b_splitview_removed = YES;
652 - (void)showSplitView:(BOOL)b_with_resize
655 if (b_dark_interface)
656 [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
658 [self setContentMinSize:NSMakeSize(604., 288.)];
659 [self setContentMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
663 winrect = [self frame];
664 winrect.size.height = winrect.size.height + f_lastSplitViewHeight;
665 winrect.origin.y = winrect.origin.y - f_lastSplitViewHeight;
666 [self setFrame: winrect display: YES animate: YES];
669 [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
671 b_splitview_removed = NO;
674 - (void)updateTimeSlider
676 [o_controls_bar updateTimeSlider];
677 [o_fspanel updatePositionAndTime];
679 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateTimeSlider)];
684 input_thread_t * p_input;
685 p_input = pl_CurrentInput(VLCIntf);
689 if (!config_GetPsz(VLCIntf, "video-title")) {
690 char *format = var_InheritString(VLCIntf, "input-title-format");
691 char *formated = str_format_meta(p_input, format);
693 aString = [NSString stringWithUTF8String:formated];
696 aString = [NSString stringWithUTF8String:config_GetPsz(VLCIntf, "video-title")];
698 char *uri = input_item_GetURI(input_GetItem(p_input));
700 NSURL * o_url = [NSURL URLWithString:[NSString stringWithUTF8String:uri]];
701 if ([o_url isFileURL]) {
702 [self setRepresentedURL: o_url];
703 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
704 [o_window setRepresentedURL:o_url];
707 [self setRepresentedURL: nil];
708 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
709 [o_window setRepresentedURL:nil];
714 if ([aString isEqualToString:@""]) {
715 if ([o_url isFileURL])
716 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
718 aString = [o_url absoluteString];
721 if ([aString length] > 0) {
722 [self setTitle: aString];
723 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
724 [o_window setTitle:aString];
727 [o_fspanel setStreamTitle: aString];
729 [self setTitle: _NS("VLC media player")];
730 [self setRepresentedURL: nil];
733 vlc_object_release(p_input);
735 [self setTitle: _NS("VLC media player")];
736 [self setRepresentedURL: nil];
742 [o_controls_bar updateControls];
743 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateControls)];
745 bool b_seekable = false;
747 playlist_t * p_playlist = pl_Get(VLCIntf);
748 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
750 /* seekable streams */
751 b_seekable = var_GetBool(p_input, "can-seek");
753 vlc_object_release(p_input);
756 [self updateTimeSlider];
757 if ([o_fspanel respondsToSelector:@selector(setSeekable:)])
758 [o_fspanel setSeekable: b_seekable];
761 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
766 [o_sidebar_view setNeedsDisplay:YES];
768 [self _updatePlaylistTitle];
773 [o_controls_bar setPause];
774 [o_fspanel setPause];
776 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPause)];
781 [o_controls_bar setPlay];
784 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPlay)];
787 - (void)updateVolumeSlider
789 [[self controlsBar] updateVolumeSlider];
790 [o_fspanel setVolumeLevel: [[VLCCoreInteraction sharedInstance] volume]];
794 #pragma mark Video Output handling
796 - (void)videoplayWillBeStarted
799 frameBeforePlayback = [self frame];
802 - (void)setVideoplayEnabled
804 BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
806 if (!b_videoPlayback) {
807 if (!b_nonembedded && (!b_nativeFullscreenMode || (b_nativeFullscreenMode && !b_fullscreen)) && frameBeforePlayback.size.width > 0 && frameBeforePlayback.size.height > 0) {
809 // only resize back to minimum view of this is still desired final state
810 CGFloat f_threshold_height = f_min_video_height + [o_controls_bar height];
811 if(frameBeforePlayback.size.height > f_threshold_height || b_minimized_view) {
813 if ([[VLCMain sharedInstance] isTerminating])
814 [self setFrame:frameBeforePlayback display:YES];
816 [[self animator] setFrame:frameBeforePlayback display:YES];
821 frameBeforePlayback = NSMakeRect(0, 0, 0, 0);
823 // update fs button to reflect state for next startup
824 if (var_InheritBool(VLCIntf, "fullscreen") || var_GetBool(pl_Get(VLCIntf), "fullscreen")) {
825 [o_controls_bar setFullscreenState:YES];
828 [self makeFirstResponder: o_playlist_table];
829 [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
831 // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
832 [self setAlphaValue:1.0];
835 if (b_nativeFullscreenMode) {
836 if ([self hasActiveVideo] && [self fullscreen]) {
837 [[o_controls_bar bottomBarView] setHidden: b_videoPlayback];
838 [o_fspanel setActive: nil];
840 [[o_controls_bar bottomBarView] setHidden: NO];
841 [o_fspanel setNonActive: nil];
847 #pragma mark Lion native fullscreen handling
848 - (void)windowWillEnterFullScreen:(NSNotification *)notification
850 [super windowWillEnterFullScreen:notification];
852 // update split view frame after removing title bar
853 if (b_dark_interface) {
854 NSRect frame = [[self contentView] frame];
855 frame.origin.y += [o_controls_bar height];
856 frame.size.height -= [o_controls_bar height];
857 [o_split_view setFrame:frame];
861 - (void)windowWillExitFullScreen:(NSNotification *)notification
863 [super windowWillExitFullScreen: notification];
865 // update split view frame after readding title bar
866 if (b_dark_interface) {
867 NSRect frame = [o_split_view frame];
868 frame.size.height -= [o_titlebar_view frame].size.height;
869 [o_split_view setFrame:frame];
873 #pragma mark Fullscreen support
875 - (void)showFullscreenController
877 id currentWindow = [NSApp keyWindow];
878 if ([currentWindow respondsToSelector:@selector(hasActiveVideo)] && [currentWindow hasActiveVideo]) {
879 if ([currentWindow respondsToSelector:@selector(fullscreen)] && [currentWindow fullscreen] && ![[currentWindow videoView] isHidden]) {
881 if ([[VLCMain sharedInstance] activeVideoPlayback])
889 #pragma mark split view delegate
890 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
892 if (dividerIndex == 0)
898 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
900 if (dividerIndex == 0)
906 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
908 return ([subview isEqual:o_left_split_view]);
911 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
913 if ([subview isEqual:o_left_split_view])
918 - (void)mainSplitViewDidResizeSubviews:(id)object
920 f_lastLeftSplitViewWidth = [o_left_split_view frame].size.width;
921 config_PutInt(VLCIntf, "macosx-show-sidebar", ![o_split_view isSubviewCollapsed:o_left_split_view]);
922 [[[VLCMain sharedInstance] mainMenu] updateSidebarMenuItem];
925 - (void)toggleLeftSubSplitView
927 [o_split_view adjustSubviews];
928 if ([o_split_view isSubviewCollapsed:o_left_split_view])
929 [o_split_view setPosition:f_lastLeftSplitViewWidth ofDividerAtIndex:0];
931 [o_split_view setPosition:[o_split_view minPossiblePositionOfDividerAtIndex:0] ofDividerAtIndex:0];
932 [[[VLCMain sharedInstance] mainMenu] updateSidebarMenuItem];
936 #pragma mark private playlist magic
937 - (void)_updatePlaylistTitle
939 playlist_t * p_playlist = pl_Get(VLCIntf);
941 playlist_item_t * currentPlaylistRoot = [[[VLCMain sharedInstance] playlist] currentPlaylistRoot];
943 if (currentPlaylistRoot == p_playlist->p_local_category || currentPlaylistRoot == p_playlist->p_ml_category) {
944 if (currentPlaylistRoot == p_playlist->p_local_category)
945 [o_chosen_category_lbl setStringValue: [_NS("Playlist") stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_local_category]]];
947 [o_chosen_category_lbl setStringValue: [_NS("Media Library") stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_ml_category]]];
951 - (NSString *)_playbackDurationOfNode:(playlist_item_t*)node
956 playlist_t * p_playlist = pl_Get(VLCIntf);
958 mtime_t mt_duration = playlist_GetNodeDuration( node );
964 mt_duration = mt_duration / 1000000;
966 NSDate *date = [NSDate dateWithTimeIntervalSince1970:mt_duration];
967 NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
968 [formatter setDateFormat:@"HH:mm:ss"];
969 [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
971 NSString *playbackDuration = [NSString stringWithFormat:@" — %@",[formatter stringFromDate:date]];
973 return playbackDuration;
977 #pragma mark Side Bar Data handling
978 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
979 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
981 //Works the same way as the NSOutlineView data source: `nil` means a parent item
983 return [o_sidebaritems count];
985 return [[item children] count];
989 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
991 //Works the same way as the NSOutlineView data source: `nil` means a parent item
993 return [o_sidebaritems objectAtIndex:index];
995 return [[item children] objectAtIndex:index];
999 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
1001 return [item title];
1004 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
1006 [item setTitle:object];
1009 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
1011 return [item hasChildren];
1015 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
1017 if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
1020 return [item hasBadge];
1024 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
1026 playlist_t * p_playlist = pl_Get(VLCIntf);
1027 NSInteger i_playlist_size = 0;
1029 if ([[item identifier] isEqualToString: @"playlist"]) {
1031 i_playlist_size = p_playlist->p_local_category->i_children;
1034 return i_playlist_size;
1036 if ([[item identifier] isEqualToString: @"medialibrary"]) {
1038 if (p_playlist->p_ml_category)
1039 i_playlist_size = p_playlist->p_ml_category->i_children;
1042 return i_playlist_size;
1045 return [item badgeValue];
1049 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
1051 return [item hasIcon];
1055 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
1060 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
1062 if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
1064 if ([item sdtype] > 0)
1066 NSMenu *m = [[NSMenu alloc] init];
1067 playlist_t * p_playlist = pl_Get(VLCIntf);
1068 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
1070 [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1072 [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1073 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
1075 return [m autorelease];
1083 - (IBAction)sdmenuhandler:(id)sender
1085 NSString * identifier = [sender representedObject];
1086 if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"]) {
1087 playlist_t * p_playlist = pl_Get(VLCIntf);
1088 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [identifier UTF8String]);
1091 playlist_ServicesDiscoveryAdd(p_playlist, [identifier UTF8String]);
1093 playlist_ServicesDiscoveryRemove(p_playlist, [identifier UTF8String]);
1098 #pragma mark Side Bar Delegate Methods
1099 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1100 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
1102 if ([[group identifier] isEqualToString:@"library"])
1108 - (void)sourceListSelectionDidChange:(NSNotification *)notification
1110 playlist_t * p_playlist = pl_Get(VLCIntf);
1112 NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
1113 id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
1115 //Set the label text to represent the new selection
1116 if ([item sdtype] > -1 && [[item identifier] length] > 0) {
1117 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
1119 playlist_ServicesDiscoveryAdd(p_playlist, [[item identifier] UTF8String]);
1122 [o_chosen_category_lbl setStringValue:[item title]];
1124 if ([[item identifier] isEqualToString:@"playlist"]) {
1125 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
1126 [o_chosen_category_lbl setStringValue: [[o_chosen_category_lbl stringValue] stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_local_category]]];
1127 } else if ([[item identifier] isEqualToString:@"medialibrary"]) {
1128 if (p_playlist->p_ml_category) {
1129 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
1130 [o_chosen_category_lbl setStringValue: [[o_chosen_category_lbl stringValue] stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_ml_category]]];
1133 playlist_item_t * pl_item;
1135 pl_item = playlist_ChildSearchName(p_playlist->p_root, [[item untranslatedTitle] UTF8String]);
1137 [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
1141 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1142 [self hideDropZone];
1144 [self showDropZone];
1147 if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
1148 [self showPodcastControls];
1150 [self hidePodcastControls];
1152 [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged"
1157 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1159 if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"]) {
1160 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1161 if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
1162 return NSDragOperationGeneric;
1164 return NSDragOperationNone;
1167 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1169 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1171 playlist_t * p_playlist = pl_Get(VLCIntf);
1172 playlist_item_t *p_node;
1174 if ([[item identifier] isEqualToString:@"playlist"])
1175 p_node = p_playlist->p_local_category;
1177 p_node = p_playlist->p_ml_category;
1179 if ([[o_pasteboard types] containsObject: NSFilenamesPboardType]) {
1180 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1181 NSUInteger count = [o_values count];
1182 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
1184 for(NSUInteger i = 0; i < count; i++) {
1185 NSDictionary *o_dic;
1186 char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
1190 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
1194 [o_array addObject: o_dic];
1197 [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
1200 else if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"]) {
1201 NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
1203 NSUInteger count = [array count];
1204 playlist_item_t * p_item = NULL;
1207 for(NSUInteger i = 0; i < count; i++) {
1208 p_item = [[array objectAtIndex:i] pointerValue];
1209 if (!p_item) continue;
1210 playlist_NodeAddCopy(p_playlist, p_item, p_node, PLAYLIST_END);
1219 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
1221 return [item identifier];
1224 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
1226 /* the following code assumes for sakes of simplicity that only the top level
1227 * items are allowed to have children */
1229 NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
1230 NSUInteger count = [array count];
1234 for (NSUInteger x = 0; x < count; x++) {
1235 id item = [array objectAtIndex:x]; // save one objc selector call
1236 if ([[item identifier] isEqualToString:object])
1244 #pragma mark Podcast
1246 - (IBAction)addPodcast:(id)sender
1248 [NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
1251 - (IBAction)addPodcastWindowAction:(id)sender
1253 [o_podcast_subscribe_window orderOut:sender];
1254 [NSApp endSheet: o_podcast_subscribe_window];
1256 if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
1257 NSMutableString * podcastConf = [[NSMutableString alloc] init];
1258 if (config_GetPsz(VLCIntf, "podcast-urls") != NULL)
1259 [podcastConf appendFormat:@"%s|", config_GetPsz(VLCIntf, "podcast-urls")];
1261 [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
1262 config_PutPsz(VLCIntf, "podcast-urls", [podcastConf UTF8String]);
1263 var_SetString(pl_Get(VLCIntf), "podcast-urls", [podcastConf UTF8String]);
1264 [podcastConf release];
1268 - (IBAction)removePodcast:(id)sender
1270 if (config_GetPsz(VLCIntf, "podcast-urls") != NULL) {
1271 [o_podcast_unsubscribe_pop removeAllItems];
1272 [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
1273 [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
1277 - (IBAction)removePodcastWindowAction:(id)sender
1279 [o_podcast_unsubscribe_window orderOut:sender];
1280 [NSApp endSheet: o_podcast_unsubscribe_window];
1282 if (sender == o_podcast_unsubscribe_ok_btn) {
1283 NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
1284 [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
1285 config_PutPsz(VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String]);
1286 var_SetString(pl_Get(VLCIntf), "podcast-urls", config_GetPsz(VLCIntf, "podcast-urls"));
1289 /* reload the podcast module, since it won't update its list when removing podcasts */
1290 playlist_t * p_playlist = pl_Get(VLCIntf);
1291 if (playlist_IsServicesDiscoveryLoaded(p_playlist, "podcast{longname=\"Podcasts\"}")) {
1292 playlist_ServicesDiscoveryRemove(p_playlist, "podcast{longname=\"Podcasts\"}");
1293 playlist_ServicesDiscoveryAdd(p_playlist, "podcast{longname=\"Podcasts\"}");
1294 [[[VLCMain sharedInstance] playlist] playlistUpdated];
1299 - (void)showPodcastControls
1301 NSRect podcastViewDimensions = [o_podcast_view frame];
1302 NSRect rightSplitRect = [o_right_split_view frame];
1303 NSRect playlistTableRect = [o_playlist_table frame];
1305 podcastViewDimensions.size.width = rightSplitRect.size.width;
1306 podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
1307 [o_podcast_view setFrame:podcastViewDimensions];
1309 playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
1310 playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
1311 [o_playlist_table setFrame:playlistTableRect];
1312 [o_playlist_table setNeedsDisplay:YES];
1314 [o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
1315 b_podcastView_displayed = YES;
1318 - (void)hidePodcastControls
1320 if (b_podcastView_displayed) {
1321 NSRect podcastViewDimensions = [o_podcast_view frame];
1322 NSRect playlistTableRect = [o_playlist_table frame];
1324 playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
1325 playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
1327 [o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
1328 [o_playlist_table setFrame: playlistTableRect];
1329 b_podcastView_displayed = NO;
1335 @implementation VLCDetachedVideoWindow
1337 - (void)awakeFromNib
1339 // sets lion fullscreen behaviour
1340 [super awakeFromNib];
1341 [self setAcceptsMouseMovedEvents: YES];
1343 if (b_dark_interface) {
1344 [self setBackgroundColor: [NSColor clearColor]];
1346 [self setOpaque: NO];
1348 [self setHasShadow:NO];
1349 [self setHasShadow:YES];
1351 NSRect winrect = [self frame];
1352 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1354 [self setTitle: _NS("VLC media player")];
1355 [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight)];
1356 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: nil];
1359 [self setBackgroundColor: [NSColor blackColor]];
1362 NSRect videoViewRect = [[self contentView] bounds];
1363 if (b_dark_interface)
1364 videoViewRect.size.height -= [o_titlebar_view frame].size.height;
1365 CGFloat f_bottomBarHeight = [[self controlsBar] height];
1366 videoViewRect.size.height -= f_bottomBarHeight;
1367 videoViewRect.origin.y = f_bottomBarHeight;
1368 [o_video_view setFrame: videoViewRect];
1370 if (b_dark_interface) {
1371 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_video_view frame]];
1372 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_video_view];
1373 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
1375 [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[self controlsBar] height] + [o_titlebar_view frame].size.height)];
1377 [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[self controlsBar] height])];
1383 if (b_dark_interface)
1384 [o_color_backdrop release];