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;
57 static const float f_min_window_height = 307.;
59 @implementation VLCMainWindow
61 @synthesize nativeFullscreenMode=b_nativeFullscreenMode;
62 @synthesize nonembedded=b_nonembedded;
63 @synthesize fsPanel=o_fspanel;
65 static VLCMainWindow *_o_sharedInstance = nil;
67 + (VLCMainWindow *)sharedInstance
69 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
73 #pragma mark Initialization
77 if (_o_sharedInstance) {
79 return _o_sharedInstance;
81 _o_sharedInstance = [super init];
83 return _o_sharedInstance;
86 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
87 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
89 self = [super initWithContentRect:contentRect styleMask:styleMask
90 backing:backingType defer:flag];
91 _o_sharedInstance = self;
93 [[VLCMain sharedInstance] updateTogglePlaylistState];
98 - (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
103 key = config_GetPsz(VLCIntf, keyString);
104 o_key = [NSString stringWithFormat:@"%s", key];
107 unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
109 NSString * characters = [o_event charactersIgnoringModifiers];
110 if ([characters length] > 0) {
111 return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
112 (i_keyModifiers & NSShiftKeyMask) == ([o_event modifierFlags] & NSShiftKeyMask) &&
113 (i_keyModifiers & NSControlKeyMask) == ([o_event modifierFlags] & NSControlKeyMask) &&
114 (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
115 (i_keyModifiers & NSCommandKeyMask) == ([o_event modifierFlags] & NSCommandKeyMask);
120 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
123 // these are key events which should be handled by vlc core, but are attached to a main menu item
124 if (![self isEvent: o_event forKey: "key-vol-up"] &&
125 ![self isEvent: o_event forKey: "key-vol-down"] &&
126 ![self isEvent: o_event forKey: "key-vol-mute"] &&
127 ![self isEvent: o_event forKey: "key-prev"] &&
128 ![self isEvent: o_event forKey: "key-next"] &&
129 ![self isEvent: o_event forKey: "key-jump+short"] &&
130 ![self isEvent: o_event forKey: "key-jump-short"]) {
131 /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
132 so we perform the menu equivalent now. */
133 if ([[NSApp mainMenu] performKeyEquivalent:o_event])
139 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
140 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
145 if (b_dark_interface)
146 [o_color_backdrop release];
148 [[NSNotificationCenter defaultCenter] removeObserver: self];
149 [o_sidebaritems release];
157 // sets lion fullscreen behaviour
158 [super awakeFromNib];
160 BOOL b_splitviewShouldBeHidden = NO;
162 if (!OSX_SNOW_LEOPARD)
163 [self setRestorable: NO];
164 [self setFrameAutosaveName:@"mainwindow"];
166 /* setup the styled interface */
167 b_nativeFullscreenMode = NO;
168 #ifdef MAC_OS_X_VERSION_10_7
169 if (!OSX_SNOW_LEOPARD)
170 b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
172 [self useOptimizedDrawing: YES];
174 [[o_search_fld cell] setPlaceholderString: _NS("Search")];
175 [[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
177 [o_dropzone_btn setTitle: _NS("Open media...")];
178 [[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];
179 [o_dropzone_lbl setStringValue: _NS("Drop media here")];
180 [o_dropzone_img setImage: imageFromRes(@"dropzone")];
182 [o_podcast_add_btn setTitle: _NS("Subscribe")];
183 [o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
184 [o_podcast_subscribe_title_lbl setStringValue: _NS("Subscribe to a podcast")];
185 [o_podcast_subscribe_subtitle_lbl setStringValue: _NS("Enter URL of the podcast to subscribe to:")];
186 [o_podcast_subscribe_cancel_btn setTitle: _NS("Cancel")];
187 [o_podcast_subscribe_ok_btn setTitle: _NS("Subscribe")];
188 [o_podcast_unsubscribe_title_lbl setStringValue: _NS("Unsubscribe from a podcast")];
189 [o_podcast_unsubscribe_subtitle_lbl setStringValue: _NS("Select the podcast you would like to unsubscribe from:")];
190 [o_podcast_unsubscribe_ok_btn setTitle: _NS("Unsubscribe")];
191 [o_podcast_unsubscribe_cancel_btn setTitle: _NS("Cancel")];
193 /* interface builder action */
194 CGFloat f_threshold_height = f_min_video_height + [o_controls_bar height];
195 if (b_dark_interface)
196 f_threshold_height += [o_titlebar_view frame].size.height;
197 if ([[self contentView] frame].size.height < f_threshold_height)
198 b_splitviewShouldBeHidden = YES;
200 [self setDelegate: self];
201 [self setExcludedFromWindowsMenu: YES];
202 [self setAcceptsMouseMovedEvents: YES];
203 // Set that here as IB seems to be buggy
204 if (b_dark_interface)
205 [self setContentMinSize:NSMakeSize(604., f_min_window_height + [o_titlebar_view frame].size.height)];
207 [self setContentMinSize:NSMakeSize(604., f_min_window_height)];
209 [self setTitle: _NS("VLC media player")];
211 b_dropzone_active = YES;
212 [o_dropzone_view setFrame: [o_playlist_table frame]];
213 [o_left_split_view setFrame: [o_sidebar_view frame]];
215 if (!OSX_SNOW_LEOPARD) {
216 /* the default small size of the search field is slightly different on Lion, let's work-around that */
218 frame = [o_search_fld frame];
219 frame.origin.y = frame.origin.y + 2.0;
220 frame.size.height = frame.size.height - 1.0;
221 [o_search_fld setFrame: frame];
224 /* reload the sidebar */
225 [self reloadSidebar];
227 o_fspanel = [[VLCFSPanel alloc] initWithContentRect:NSMakeRect(110.,267.,549.,87.)
228 styleMask:NSTexturedBackgroundWindowMask
229 backing:NSBackingStoreBuffered
232 /* make sure we display the desired default appearance when VLC launches for the first time */
233 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
234 if (![defaults objectForKey:@"VLCFirstRun"]) {
235 [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
237 NSUInteger i_sidebaritem_count = [o_sidebaritems count];
238 for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
239 [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex:x] expandChildren: YES];
243 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.")];
244 NSInteger returnValue = [albumArtAlert runModal];
245 config_PutInt(VLCIntf, "metadata-network-access", returnValue == NSAlertDefaultReturn);
248 // select playlist item by default
249 [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
251 if (b_dark_interface) {
252 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
253 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
255 [self setBackgroundColor: [NSColor clearColor]];
256 [self setOpaque: NO];
258 [self setHasShadow:NO];
259 [self setHasShadow:YES];
261 NSRect winrect = [self frame];
262 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
264 [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
265 winrect.size.width, f_titleBarHeight)];
266 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
268 if (winrect.size.height > 100) {
269 [self setFrame: winrect display:YES animate:YES];
270 previousSavedFrame = winrect;
273 winrect = [o_split_view frame];
274 winrect.size.height = winrect.size.height - f_titleBarHeight;
275 [o_split_view setFrame: winrect];
276 [o_video_view setFrame: winrect];
278 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
279 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
280 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
282 [o_video_view setFrame: [o_split_view frame]];
283 [o_playlist_table setBorderType: NSNoBorder];
284 [o_sidebar_scrollview setBorderType: NSNoBorder];
287 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
288 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
289 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
290 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(mainSplitViewDidResizeSubviews:) name: NSSplitViewDidResizeSubviewsNotification object:o_split_view];
292 if (b_splitviewShouldBeHidden) {
293 [self hideSplitView: YES];
294 f_lastSplitViewHeight = 300;
297 /* sanity check for the window size */
298 NSRect frame = [self frame];
299 NSSize screenSize = [[self screen] frame].size;
300 if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
301 nativeVideoSize = screenSize;
305 /* update fs button to reflect state for next startup */
306 if (var_InheritBool(pl_Get(VLCIntf), "fullscreen"))
307 [o_controls_bar setFullscreenState:YES];
309 /* restore split view */
310 f_lastLeftSplitViewWidth = 200;
311 /* trick NSSplitView implementation, which pretends to know better than us */
312 if (!config_GetInt(VLCIntf, "macosx-show-sidebar"))
313 [self performSelector:@selector(toggleLeftSubSplitView) withObject:nil afterDelay:0.05];
317 #pragma mark appearance management
319 - (void)reloadSidebar
322 if (o_sidebaritems) {
323 [o_sidebaritems release];
327 o_sidebaritems = [[NSMutableArray alloc] init];
328 SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
329 SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
330 [playlistItem setIcon: imageFromRes(@"sidebar-playlist")];
331 SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
332 [medialibraryItem setIcon: imageFromRes(@"sidebar-playlist")];
333 SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
334 SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
335 SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
336 SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
338 /* SD subnodes, inspired by the Qt4 intf */
339 char **ppsz_longnames = NULL;
340 int *p_categories = NULL;
341 char **ppsz_names = vlc_sd_GetNames(pl_Get(VLCIntf), &ppsz_longnames, &p_categories);
343 msg_Err(VLCIntf, "no sd item found"); //TODO
344 char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
345 int *p_category = p_categories;
346 NSMutableArray *internetItems = [[NSMutableArray alloc] init];
347 NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
348 NSMutableArray *lanItems = [[NSMutableArray alloc] init];
349 NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
350 NSString *o_identifier;
351 for (; ppsz_name && *ppsz_name; ppsz_name++, ppsz_longname++, p_category++) {
352 o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
353 switch (*p_category) {
354 case SD_CAT_INTERNET:
355 [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
356 if (!strncmp(*ppsz_name, "podcast", 7))
357 [[internetItems lastObject] setIcon: imageFromRes(@"sidebar-podcast")];
359 [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
360 [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
361 [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
364 [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
365 [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
366 [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
367 [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
370 [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
371 [[lanItems lastObject] setIcon: imageFromRes(@"sidebar-local")];
372 [[lanItems lastObject] setSdtype: SD_CAT_LAN];
373 [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
375 case SD_CAT_MYCOMPUTER:
376 [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
377 if (!strncmp(*ppsz_name, "video_dir", 9))
378 [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-movie")];
379 else if (!strncmp(*ppsz_name, "audio_dir", 9))
380 [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-music")];
381 else if (!strncmp(*ppsz_name, "picture_dir", 11))
382 [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-pictures")];
384 [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
385 [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
386 [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
389 msg_Warn(VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name);
394 free(*ppsz_longname);
396 [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
397 [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
398 [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
399 [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
400 [mycompItems release];
401 [devicesItems release];
403 [internetItems release];
405 free(ppsz_longnames);
408 [libraryItem setChildren: [NSArray arrayWithObjects:playlistItem, medialibraryItem, nil]];
409 [o_sidebaritems addObject: libraryItem];
410 if ([mycompItem hasChildren])
411 [o_sidebaritems addObject: mycompItem];
412 if ([devicesItem hasChildren])
413 [o_sidebaritems addObject: devicesItem];
414 if ([lanItem hasChildren])
415 [o_sidebaritems addObject: lanItem];
416 if ([internetItem hasChildren])
417 [o_sidebaritems addObject: internetItem];
419 [o_sidebar_view reloadData];
420 [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
421 [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
423 [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
424 [(PXSourceList *)o_sidebar_view setDataSource:self];
425 [o_sidebar_view setDelegate:self];
426 [o_sidebar_view setAutosaveExpandedItems:YES];
428 [o_sidebar_view expandItem: libraryItem expandChildren: YES];
431 NSUInteger i_sidebaritem_count = [o_sidebaritems count];
432 for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
433 [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex:x] expandChildren: YES];
437 - (VLCMainWindowControlsBar *)controlsBar;
439 return (VLCMainWindowControlsBar *)o_controls_bar;
442 - (void)resizePlaylistAfterCollapse
444 // no animation here since we might be in the middle of another resize animation
445 NSRect rightSplitRect = [o_right_split_view frame];
448 plrect.size.height = rightSplitRect.size.height - 20.0; // actual pl top bar height, which differs from its frame
449 plrect.size.width = rightSplitRect.size.width;
450 plrect.origin.x = plrect.origin.y = 0.;
452 NSRect dropzoneboxRect = [o_dropzone_box frame];
453 dropzoneboxRect.origin.x = (plrect.size.width - dropzoneboxRect.size.width) / 2;
454 dropzoneboxRect.origin.y = (plrect.size.height - dropzoneboxRect.size.height) / 2;
456 [o_dropzone_view setFrame: plrect];
457 [o_dropzone_box setFrame: dropzoneboxRect];
459 if (b_podcastView_displayed) {
460 plrect.size.height -= [o_podcast_view frame].size.height;
461 plrect.origin.y = [o_podcast_view frame].size.height;
463 [o_playlist_table setFrame: plrect];
465 [o_dropzone_view setNeedsDisplay: YES];
466 [o_playlist_table setNeedsDisplay: YES];
469 - (void)makeSplitViewVisible
471 if (b_dark_interface)
472 [self setContentMinSize: NSMakeSize(604., f_min_window_height + [o_titlebar_view frame].size.height)];
474 [self setContentMinSize: NSMakeSize(604., f_min_window_height)];
476 NSRect old_frame = [self frame];
477 CGFloat newHeight = [self minSize].height;
478 if (old_frame.size.height < newHeight) {
479 NSRect new_frame = old_frame;
480 new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
481 new_frame.size.height = newHeight;
483 [[self animator] setFrame: new_frame display: YES animate: YES];
486 [o_video_view setHidden: YES];
487 [o_split_view setHidden: NO];
488 if (b_nativeFullscreenMode && [self fullscreen]) {
489 [[o_controls_bar bottomBarView] setHidden: NO];
490 [o_fspanel setNonActive:nil];
493 [self makeFirstResponder: o_playlist_table];
496 - (void)makeSplitViewHidden
498 if (b_dark_interface)
499 [self setContentMinSize: NSMakeSize(604., f_min_video_height + [o_titlebar_view frame].size.height)];
501 [self setContentMinSize: NSMakeSize(604., f_min_video_height)];
503 [o_split_view setHidden: YES];
504 [o_video_view setHidden: NO];
505 if (b_nativeFullscreenMode && [self fullscreen]) {
506 [[o_controls_bar bottomBarView] setHidden: YES];
507 [o_fspanel setActive:nil];
510 if ([[o_video_view subviews] count] > 0)
511 [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
515 - (void)changePlaylistState:(VLCPlaylistStateEvent)event
517 // Beware, this code is really ugly
519 msg_Dbg(VLCIntf, "toggle playlist from state: removed splitview %i, minimized view %i. Event %i", b_splitview_removed, b_minimized_view, event);
520 if (![self isVisible] && event == psUserMenuEvent) {
521 [self makeKeyAndOrderFront: nil];
525 BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
526 BOOL b_restored = NO;
528 // ignore alt if triggered through main menu shortcut
529 BOOL b_have_alt_key = ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0;
530 if (event == psUserMenuEvent)
533 // eUserMenuEvent is now handled same as eUserEvent
534 if(event == psUserMenuEvent)
537 if (b_dropzone_active && b_have_alt_key) {
542 if (!(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ((b_have_alt_key && b_activeVideo)
543 || (b_nonembedded && event == psUserEvent)
544 || (!b_activeVideo && event == psUserEvent)
545 || (b_minimized_view && event == psVideoStartedOrStoppedEvent))) {
546 // for starting playback, window is resized through resized events
547 // for stopping playback, resize through reset to previous frame
548 [self hideSplitView: event != psVideoStartedOrStoppedEvent];
549 b_minimized_view = NO;
551 if (b_splitview_removed) {
552 if (!b_nonembedded || (event == psUserEvent && b_nonembedded))
553 [self showSplitView: event != psVideoStartedOrStoppedEvent];
555 if (event != psUserEvent)
556 b_minimized_view = YES;
558 b_minimized_view = NO;
564 if (!b_nonembedded) {
565 if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && event != psUserEvent))
566 [self makeSplitViewHidden];
568 [self makeSplitViewVisible];
570 [o_split_view setHidden: NO];
571 [o_playlist_table setHidden: NO];
572 [o_video_view setHidden: YES];
576 msg_Dbg(VLCIntf, "toggle playlist to state: removed splitview %i, minimized view %i", b_splitview_removed, b_minimized_view);
579 - (IBAction)dropzoneButtonAction:(id)sender
581 [[[VLCMain sharedInstance] open] openFileGeneric];
585 #pragma mark overwritten default functionality
587 - (void)windowResizedOrMoved:(NSNotification *)notification
589 [self saveFrameUsingName: [self frameAutosaveName]];
592 - (void)applicationWillTerminate:(NSNotification *)notification
594 config_PutInt(VLCIntf, "macosx-show-sidebar", ![o_split_view isSubviewCollapsed:o_left_split_view]);
596 [self saveFrameUsingName: [self frameAutosaveName]];
600 - (void)someWindowWillClose:(NSNotification *)notification
602 id obj = [notification object];
604 // hasActiveVideo is defined for VLCVideoWindowCommon and subclasses
605 if ([obj respondsToSelector:@selector(hasActiveVideo)] && [obj hasActiveVideo]) {
606 if ([[VLCMain sharedInstance] activeVideoPlayback])
607 [[VLCCoreInteraction sharedInstance] stop];
611 - (void)someWindowWillMiniaturize:(NSNotification *)notification
613 if (config_GetInt(VLCIntf, "macosx-pause-minimized")) {
614 id obj = [notification object];
616 if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !b_nonembedded)) {
617 if ([[VLCMain sharedInstance] activeVideoPlayback])
618 [[VLCCoreInteraction sharedInstance] pause];
624 #pragma mark Update interface and respond to foreign events
627 b_dropzone_active = YES;
628 [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
629 [o_dropzone_view setFrame: [o_playlist_table frame]];
630 [o_playlist_table setHidden:YES];
635 b_dropzone_active = NO;
636 [o_dropzone_view removeFromSuperview];
637 [o_playlist_table setHidden: NO];
640 - (void)hideSplitView:(BOOL)b_with_resize
642 // cancel pending pl resizes, in case of fast toggle between both modes
643 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resizePlaylistAfterCollapse) object:nil];
646 NSRect winrect = [self frame];
647 f_lastSplitViewHeight = [o_split_view frame].size.height;
648 winrect.size.height = winrect.size.height - f_lastSplitViewHeight;
649 winrect.origin.y = winrect.origin.y + f_lastSplitViewHeight;
650 [self setFrame: winrect display: YES animate: YES];
653 if (b_dark_interface) {
654 [self setContentMinSize: NSMakeSize(604., [o_controls_bar height] + [o_titlebar_view frame].size.height)];
655 [self setContentMaxSize: NSMakeSize(FLT_MAX, [o_controls_bar height] + [o_titlebar_view frame].size.height)];
657 [self setContentMinSize: NSMakeSize(604., [o_controls_bar height])];
658 [self setContentMaxSize: NSMakeSize(FLT_MAX, [o_controls_bar height])];
661 b_splitview_removed = YES;
664 - (void)showSplitView:(BOOL)b_with_resize
667 if (b_dark_interface)
668 [self setContentMinSize:NSMakeSize(604., f_min_window_height + [o_titlebar_view frame].size.height)];
670 [self setContentMinSize:NSMakeSize(604., f_min_window_height)];
671 [self setContentMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
675 winrect = [self frame];
676 winrect.size.height = winrect.size.height + f_lastSplitViewHeight;
677 winrect.origin.y = winrect.origin.y - f_lastSplitViewHeight;
678 [self setFrame: winrect display: YES animate: YES];
681 // cancel pending pl resizes, in case of fast toggle between both modes
682 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resizePlaylistAfterCollapse) object:nil];
683 [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
685 b_splitview_removed = NO;
688 - (void)updateTimeSlider
690 [o_controls_bar updateTimeSlider];
691 [o_fspanel updatePositionAndTime];
693 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateTimeSlider)];
698 input_thread_t * p_input;
699 p_input = pl_CurrentInput(VLCIntf);
703 if (!config_GetPsz(VLCIntf, "video-title")) {
704 char *format = var_InheritString(VLCIntf, "input-title-format");
705 char *formated = str_format_meta(p_input, format);
707 aString = [NSString stringWithUTF8String:formated];
710 aString = [NSString stringWithUTF8String:config_GetPsz(VLCIntf, "video-title")];
712 char *uri = input_item_GetURI(input_GetItem(p_input));
714 NSURL * o_url = [NSURL URLWithString:[NSString stringWithUTF8String:uri]];
715 if ([o_url isFileURL]) {
716 [self setRepresentedURL: o_url];
717 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
718 [o_window setRepresentedURL:o_url];
721 [self setRepresentedURL: nil];
722 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
723 [o_window setRepresentedURL:nil];
728 if ([aString isEqualToString:@""]) {
729 if ([o_url isFileURL])
730 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
732 aString = [o_url absoluteString];
735 if ([aString length] > 0) {
736 [self setTitle: aString];
737 [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
738 [o_window setTitle:aString];
741 [o_fspanel setStreamTitle: aString];
743 [self setTitle: _NS("VLC media player")];
744 [self setRepresentedURL: nil];
747 vlc_object_release(p_input);
749 [self setTitle: _NS("VLC media player")];
750 [self setRepresentedURL: nil];
756 [o_controls_bar updateControls];
757 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateControls)];
759 bool b_seekable = false;
761 playlist_t * p_playlist = pl_Get(VLCIntf);
762 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
764 /* seekable streams */
765 b_seekable = var_GetBool(p_input, "can-seek");
767 vlc_object_release(p_input);
770 [self updateTimeSlider];
771 if ([o_fspanel respondsToSelector:@selector(setSeekable:)])
772 [o_fspanel setSeekable: b_seekable];
775 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
780 [o_sidebar_view setNeedsDisplay:YES];
782 [self _updatePlaylistTitle];
787 [o_controls_bar setPause];
788 [o_fspanel setPause];
790 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPause)];
795 [o_controls_bar setPlay];
798 [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPlay)];
801 - (void)updateVolumeSlider
803 [[self controlsBar] updateVolumeSlider];
804 [o_fspanel setVolumeLevel: [[VLCCoreInteraction sharedInstance] volume]];
808 #pragma mark Video Output handling
810 - (void)videoplayWillBeStarted
813 frameBeforePlayback = [self frame];
816 - (void)setVideoplayEnabled
818 BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
820 if (!b_videoPlayback) {
821 if (!b_nonembedded && (!b_nativeFullscreenMode || (b_nativeFullscreenMode && !b_fullscreen)) && frameBeforePlayback.size.width > 0 && frameBeforePlayback.size.height > 0) {
823 // only resize back to minimum view of this is still desired final state
824 CGFloat f_threshold_height = f_min_video_height + [o_controls_bar height];
825 if(frameBeforePlayback.size.height > f_threshold_height || b_minimized_view) {
827 if ([[VLCMain sharedInstance] isTerminating])
828 [self setFrame:frameBeforePlayback display:YES];
830 [[self animator] setFrame:frameBeforePlayback display:YES];
835 frameBeforePlayback = NSMakeRect(0, 0, 0, 0);
837 // update fs button to reflect state for next startup
838 if (var_InheritBool(VLCIntf, "fullscreen") || var_GetBool(pl_Get(VLCIntf), "fullscreen")) {
839 [o_controls_bar setFullscreenState:YES];
842 [self makeFirstResponder: o_playlist_table];
843 [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
845 // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
846 [self setAlphaValue:1.0];
849 if (b_nativeFullscreenMode) {
850 if ([self hasActiveVideo] && [self fullscreen]) {
851 [[o_controls_bar bottomBarView] setHidden: b_videoPlayback];
852 [o_fspanel setActive: nil];
854 [[o_controls_bar bottomBarView] setHidden: NO];
855 [o_fspanel setNonActive: nil];
861 #pragma mark Lion native fullscreen handling
862 - (void)windowWillEnterFullScreen:(NSNotification *)notification
864 [super windowWillEnterFullScreen:notification];
866 // update split view frame after removing title bar
867 if (b_dark_interface) {
868 NSRect frame = [[self contentView] frame];
869 frame.origin.y += [o_controls_bar height];
870 frame.size.height -= [o_controls_bar height];
871 [o_split_view setFrame:frame];
875 - (void)windowWillExitFullScreen:(NSNotification *)notification
877 [super windowWillExitFullScreen: notification];
879 // update split view frame after readding title bar
880 if (b_dark_interface) {
881 NSRect frame = [o_split_view frame];
882 frame.size.height -= [o_titlebar_view frame].size.height;
883 [o_split_view setFrame:frame];
887 #pragma mark Fullscreen support
889 - (void)showFullscreenController
891 id currentWindow = [NSApp keyWindow];
892 if ([currentWindow respondsToSelector:@selector(hasActiveVideo)] && [currentWindow hasActiveVideo]) {
893 if ([currentWindow respondsToSelector:@selector(fullscreen)] && [currentWindow fullscreen] && ![[currentWindow videoView] isHidden]) {
895 if ([[VLCMain sharedInstance] activeVideoPlayback])
903 #pragma mark split view delegate
904 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
906 if (dividerIndex == 0)
912 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
914 if (dividerIndex == 0)
920 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
922 return ([subview isEqual:o_left_split_view]);
925 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
927 if ([subview isEqual:o_left_split_view])
932 - (void)mainSplitViewDidResizeSubviews:(id)object
934 f_lastLeftSplitViewWidth = [o_left_split_view frame].size.width;
935 config_PutInt(VLCIntf, "macosx-show-sidebar", ![o_split_view isSubviewCollapsed:o_left_split_view]);
936 [[[VLCMain sharedInstance] mainMenu] updateSidebarMenuItem];
939 - (void)toggleLeftSubSplitView
941 [o_split_view adjustSubviews];
942 if ([o_split_view isSubviewCollapsed:o_left_split_view])
943 [o_split_view setPosition:f_lastLeftSplitViewWidth ofDividerAtIndex:0];
945 [o_split_view setPosition:[o_split_view minPossiblePositionOfDividerAtIndex:0] ofDividerAtIndex:0];
946 [[[VLCMain sharedInstance] mainMenu] updateSidebarMenuItem];
950 #pragma mark private playlist magic
951 - (void)_updatePlaylistTitle
953 playlist_t * p_playlist = pl_Get(VLCIntf);
955 playlist_item_t * currentPlaylistRoot = [[[VLCMain sharedInstance] playlist] currentPlaylistRoot];
957 if (currentPlaylistRoot == p_playlist->p_local_category || currentPlaylistRoot == p_playlist->p_ml_category) {
958 if (currentPlaylistRoot == p_playlist->p_local_category)
959 [o_chosen_category_lbl setStringValue: [_NS("Playlist") stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_local_category]]];
961 [o_chosen_category_lbl setStringValue: [_NS("Media Library") stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_ml_category]]];
965 - (NSString *)_playbackDurationOfNode:(playlist_item_t*)node
970 playlist_t * p_playlist = pl_Get(VLCIntf);
972 mtime_t mt_duration = playlist_GetNodeDuration( node );
978 mt_duration = mt_duration / 1000000;
980 NSDate *date = [NSDate dateWithTimeIntervalSince1970:mt_duration];
981 NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
982 [formatter setDateFormat:@"HH:mm:ss"];
983 [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
985 NSString *playbackDuration = [NSString stringWithFormat:@" — %@",[formatter stringFromDate:date]];
987 return playbackDuration;
991 #pragma mark Side Bar Data handling
992 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
993 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
995 //Works the same way as the NSOutlineView data source: `nil` means a parent item
997 return [o_sidebaritems count];
999 return [[item children] count];
1003 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
1005 //Works the same way as the NSOutlineView data source: `nil` means a parent item
1007 return [o_sidebaritems objectAtIndex:index];
1009 return [[item children] objectAtIndex:index];
1013 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
1015 return [item title];
1018 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
1020 [item setTitle:object];
1023 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
1025 return [item hasChildren];
1029 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
1031 if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
1034 return [item hasBadge];
1038 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
1040 playlist_t * p_playlist = pl_Get(VLCIntf);
1041 NSInteger i_playlist_size = 0;
1043 if ([[item identifier] isEqualToString: @"playlist"]) {
1045 i_playlist_size = p_playlist->p_local_category->i_children;
1048 return i_playlist_size;
1050 if ([[item identifier] isEqualToString: @"medialibrary"]) {
1052 if (p_playlist->p_ml_category)
1053 i_playlist_size = p_playlist->p_ml_category->i_children;
1056 return i_playlist_size;
1059 return [item badgeValue];
1063 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
1065 return [item hasIcon];
1069 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
1074 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
1076 if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
1078 if ([item sdtype] > 0)
1080 NSMenu *m = [[NSMenu alloc] init];
1081 playlist_t * p_playlist = pl_Get(VLCIntf);
1082 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
1084 [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1086 [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1087 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
1089 return [m autorelease];
1097 - (IBAction)sdmenuhandler:(id)sender
1099 NSString * identifier = [sender representedObject];
1100 if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"]) {
1101 playlist_t * p_playlist = pl_Get(VLCIntf);
1102 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [identifier UTF8String]);
1105 playlist_ServicesDiscoveryAdd(p_playlist, [identifier UTF8String]);
1107 playlist_ServicesDiscoveryRemove(p_playlist, [identifier UTF8String]);
1112 #pragma mark Side Bar Delegate Methods
1113 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1114 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
1116 if ([[group identifier] isEqualToString:@"library"])
1122 - (void)sourceListSelectionDidChange:(NSNotification *)notification
1124 playlist_t * p_playlist = pl_Get(VLCIntf);
1126 NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
1127 id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
1129 //Set the label text to represent the new selection
1130 if ([item sdtype] > -1 && [[item identifier] length] > 0) {
1131 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
1133 playlist_ServicesDiscoveryAdd(p_playlist, [[item identifier] UTF8String]);
1136 [o_chosen_category_lbl setStringValue:[item title]];
1138 if ([[item identifier] isEqualToString:@"playlist"]) {
1139 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
1140 [o_chosen_category_lbl setStringValue: [[o_chosen_category_lbl stringValue] stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_local_category]]];
1141 } else if ([[item identifier] isEqualToString:@"medialibrary"]) {
1142 if (p_playlist->p_ml_category) {
1143 [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
1144 [o_chosen_category_lbl setStringValue: [[o_chosen_category_lbl stringValue] stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_ml_category]]];
1147 playlist_item_t * pl_item;
1149 pl_item = playlist_ChildSearchName(p_playlist->p_root, [[item untranslatedTitle] UTF8String]);
1151 [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
1154 // Note the order: first hide the podcast controls, then show the drop zone
1155 if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
1156 [self showPodcastControls];
1158 [self hidePodcastControls];
1161 if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1162 [self hideDropZone];
1164 [self showDropZone];
1167 [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged"
1172 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1174 if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"]) {
1175 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1176 if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
1177 return NSDragOperationGeneric;
1179 return NSDragOperationNone;
1182 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1184 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1186 playlist_t * p_playlist = pl_Get(VLCIntf);
1187 playlist_item_t *p_node;
1189 if ([[item identifier] isEqualToString:@"playlist"])
1190 p_node = p_playlist->p_local_category;
1192 p_node = p_playlist->p_ml_category;
1194 if ([[o_pasteboard types] containsObject: NSFilenamesPboardType]) {
1195 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1196 NSUInteger count = [o_values count];
1197 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
1199 for(NSUInteger i = 0; i < count; i++) {
1200 NSDictionary *o_dic;
1201 char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
1205 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
1209 [o_array addObject: o_dic];
1212 [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
1215 else if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"]) {
1216 NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
1218 NSUInteger count = [array count];
1219 playlist_item_t * p_item = NULL;
1222 for(NSUInteger i = 0; i < count; i++) {
1223 p_item = [[array objectAtIndex:i] pointerValue];
1224 if (!p_item) continue;
1225 playlist_NodeAddCopy(p_playlist, p_item, p_node, PLAYLIST_END);
1234 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
1236 return [item identifier];
1239 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
1241 /* the following code assumes for sakes of simplicity that only the top level
1242 * items are allowed to have children */
1244 NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
1245 NSUInteger count = [array count];
1249 for (NSUInteger x = 0; x < count; x++) {
1250 id item = [array objectAtIndex:x]; // save one objc selector call
1251 if ([[item identifier] isEqualToString:object])
1259 #pragma mark Podcast
1261 - (IBAction)addPodcast:(id)sender
1263 [NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
1266 - (IBAction)addPodcastWindowAction:(id)sender
1268 [o_podcast_subscribe_window orderOut:sender];
1269 [NSApp endSheet: o_podcast_subscribe_window];
1271 if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
1272 NSMutableString * podcastConf = [[NSMutableString alloc] init];
1273 if (config_GetPsz(VLCIntf, "podcast-urls") != NULL)
1274 [podcastConf appendFormat:@"%s|", config_GetPsz(VLCIntf, "podcast-urls")];
1276 [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
1277 config_PutPsz(VLCIntf, "podcast-urls", [podcastConf UTF8String]);
1278 var_SetString(pl_Get(VLCIntf), "podcast-urls", [podcastConf UTF8String]);
1279 [podcastConf release];
1283 - (IBAction)removePodcast:(id)sender
1285 if (config_GetPsz(VLCIntf, "podcast-urls") != NULL) {
1286 [o_podcast_unsubscribe_pop removeAllItems];
1287 [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
1288 [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
1292 - (IBAction)removePodcastWindowAction:(id)sender
1294 [o_podcast_unsubscribe_window orderOut:sender];
1295 [NSApp endSheet: o_podcast_unsubscribe_window];
1297 if (sender == o_podcast_unsubscribe_ok_btn) {
1298 NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
1299 [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
1300 config_PutPsz(VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String]);
1301 var_SetString(pl_Get(VLCIntf), "podcast-urls", config_GetPsz(VLCIntf, "podcast-urls"));
1304 /* reload the podcast module, since it won't update its list when removing podcasts */
1305 playlist_t * p_playlist = pl_Get(VLCIntf);
1306 if (playlist_IsServicesDiscoveryLoaded(p_playlist, "podcast{longname=\"Podcasts\"}")) {
1307 playlist_ServicesDiscoveryRemove(p_playlist, "podcast{longname=\"Podcasts\"}");
1308 playlist_ServicesDiscoveryAdd(p_playlist, "podcast{longname=\"Podcasts\"}");
1309 [[[VLCMain sharedInstance] playlist] playlistUpdated];
1314 - (void)showPodcastControls
1316 NSRect podcastViewDimensions = [o_podcast_view frame];
1317 NSRect rightSplitRect = [o_right_split_view frame];
1318 NSRect playlistTableRect = [o_playlist_table frame];
1320 podcastViewDimensions.size.width = rightSplitRect.size.width;
1321 podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
1322 [o_podcast_view setFrame:podcastViewDimensions];
1324 playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
1325 playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
1326 [o_playlist_table setFrame:playlistTableRect];
1327 [o_playlist_table setNeedsDisplay:YES];
1329 [o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
1330 b_podcastView_displayed = YES;
1333 - (void)hidePodcastControls
1335 if (b_podcastView_displayed) {
1336 NSRect podcastViewDimensions = [o_podcast_view frame];
1337 NSRect playlistTableRect = [o_playlist_table frame];
1339 playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
1340 playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
1342 [o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
1343 [o_playlist_table setFrame: playlistTableRect];
1344 b_podcastView_displayed = NO;
1350 @implementation VLCDetachedVideoWindow
1352 - (void)awakeFromNib
1354 // sets lion fullscreen behaviour
1355 [super awakeFromNib];
1356 [self setAcceptsMouseMovedEvents: YES];
1358 if (b_dark_interface) {
1359 [self setBackgroundColor: [NSColor clearColor]];
1361 [self setOpaque: NO];
1363 [self setHasShadow:NO];
1364 [self setHasShadow:YES];
1366 NSRect winrect = [self frame];
1367 CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1369 [self setTitle: _NS("VLC media player")];
1370 [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight)];
1371 [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: nil];
1374 [self setBackgroundColor: [NSColor blackColor]];
1377 NSRect videoViewRect = [[self contentView] bounds];
1378 if (b_dark_interface)
1379 videoViewRect.size.height -= [o_titlebar_view frame].size.height;
1380 CGFloat f_bottomBarHeight = [[self controlsBar] height];
1381 videoViewRect.size.height -= f_bottomBarHeight;
1382 videoViewRect.origin.y = f_bottomBarHeight;
1383 [o_video_view setFrame: videoViewRect];
1385 if (b_dark_interface) {
1386 o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_video_view frame]];
1387 [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_video_view];
1388 [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
1390 [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[self controlsBar] height] + [o_titlebar_view frame].size.height)];
1392 [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[self controlsBar] height])];
1398 if (b_dark_interface)
1399 [o_color_backdrop release];