]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: add black view to detached video view in order to avoid flashes
[vlc] / modules / gui / macosx / MainWindow.m
1 /*****************************************************************************
2  * MainWindow.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2012 VLC authors and VideoLAN
5  * $Id$
6  *
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  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #import "CompatibilityFixes.h"
28 #import "MainWindow.h"
29 #import "intf.h"
30 #import "CoreInteraction.h"
31 #import "AudioEffects.h"
32 #import "MainMenu.h"
33 #import "open.h"
34 #import "controls.h" // TODO: remove me
35 #import "playlist.h"
36 #import "SideBarItem.h"
37 #import <math.h>
38 #import <vlc_playlist.h>
39 #import <vlc_aout_intf.h>
40 #import <vlc_url.h>
41 #import <vlc_strings.h>
42 #import <vlc_services_discovery.h>
43 #import <vlc_aout_intf.h>
44
45 #import "ControlsBar.h"
46 #import "VideoView.h"
47 #import "VLCVoutWindowController.h"
48
49
50 @interface VLCMainWindow ()
51 - (void)resizePlaylistAfterCollapse;
52 - (void)makeSplitViewVisible;
53 - (void)makeSplitViewHidden;
54
55 @end
56
57
58 @implementation VLCMainWindow
59
60 static VLCMainWindow *_o_sharedInstance = nil;
61
62 + (VLCMainWindow *)sharedInstance
63 {
64     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
65 }
66
67 #pragma mark -
68 #pragma mark Initialization
69
70 - (id)init
71 {
72     if (_o_sharedInstance) {
73         [self dealloc];
74         return _o_sharedInstance;
75     } else
76         _o_sharedInstance = [super init];
77
78     return _o_sharedInstance;
79 }
80
81 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
82                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
83 {
84     self = [super initWithContentRect:contentRect styleMask:styleMask
85                               backing:backingType defer:flag];
86     _o_sharedInstance = self;
87
88     [[VLCMain sharedInstance] updateTogglePlaylistState];
89
90     return self;
91 }
92
93 - (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
94 {
95     char *key;
96     NSString *o_key;
97
98     key = config_GetPsz(VLCIntf, keyString);
99     o_key = [NSString stringWithFormat:@"%s", key];
100     FREENULL(key);
101
102     unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
103
104     NSString * characters = [o_event charactersIgnoringModifiers];
105     if ([characters length] > 0) {
106         return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
107                 (i_keyModifiers & NSShiftKeyMask)     == ([o_event modifierFlags] & NSShiftKeyMask) &&
108                 (i_keyModifiers & NSControlKeyMask)   == ([o_event modifierFlags] & NSControlKeyMask) &&
109                 (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
110                 (i_keyModifiers & NSCommandKeyMask)   == ([o_event modifierFlags] & NSCommandKeyMask);
111     }
112     return NO;
113 }
114
115 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
116 {
117     BOOL b_force = NO;
118     // these are key events which should be handled by vlc core, but are attached to a main menu item
119     if (![self isEvent: o_event forKey: "key-vol-up"] &&
120         ![self isEvent: o_event forKey: "key-vol-down"] &&
121         ![self isEvent: o_event forKey: "key-vol-mute"]) {
122         /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
123          so we perform the menu equivalent now. */
124         if ([[NSApp mainMenu] performKeyEquivalent:o_event])
125             return TRUE;
126     }
127     else
128         b_force = YES;
129
130     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
131            [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
132 }
133
134 - (void)dealloc
135 {
136     if (b_dark_interface)
137         [o_color_backdrop release];
138
139     [[NSNotificationCenter defaultCenter] removeObserver: self];
140     [o_sidebaritems release];
141
142     [super dealloc];
143 }
144
145 - (void)awakeFromNib
146 {
147     BOOL b_splitviewShouldBeHidden = NO;
148
149     /* setup the styled interface */
150     b_nativeFullscreenMode = NO;
151 #ifdef MAC_OS_X_VERSION_10_7
152     if (!OSX_SNOW_LEOPARD)
153         b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
154 #endif
155     t_hide_mouse_timer = nil;
156     [self useOptimizedDrawing: YES];
157     
158     [[o_search_fld cell] setPlaceholderString: _NS("Search")];
159     [[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
160
161     [o_dropzone_btn setTitle: _NS("Open media...")];
162     [[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];
163     [o_dropzone_lbl setStringValue: _NS("Drop media here")];
164
165     [o_podcast_add_btn setTitle: _NS("Subscribe")];
166     [o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
167     [o_podcast_subscribe_title_lbl setStringValue: _NS("Subscribe to a podcast")];
168     [o_podcast_subscribe_subtitle_lbl setStringValue: _NS("Enter URL of the podcast to subscribe to:")];
169     [o_podcast_subscribe_cancel_btn setTitle: _NS("Cancel")];
170     [o_podcast_subscribe_ok_btn setTitle: _NS("Subscribe")];
171     [o_podcast_unsubscribe_title_lbl setStringValue: _NS("Unsubscribe from a podcast")];
172     [o_podcast_unsubscribe_subtitle_lbl setStringValue: _NS("Select the podcast you would like to unsubscribe from:")];
173     [o_podcast_unsubscribe_ok_btn setTitle: _NS("Unsubscribe")];
174     [o_podcast_unsubscribe_cancel_btn setTitle: _NS("Cancel")];
175
176     /* interface builder action */
177     float f_threshold_height = f_min_video_height + [[o_controls_bar bottomBarView] frame].size.height;
178     if (b_dark_interface)
179         f_threshold_height += [o_titlebar_view frame].size.height;
180     if ([[self contentView] frame].size.height < f_threshold_height)
181         b_splitviewShouldBeHidden = YES;
182
183     [self setDelegate: self];
184     [self setExcludedFromWindowsMenu: YES];
185     [self setAcceptsMouseMovedEvents: YES];
186     // Set that here as IB seems to be buggy
187     if (b_dark_interface) {
188         [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
189     } else {
190         [self setContentMinSize:NSMakeSize(604., 288.)];
191     }
192
193     [self setTitle: _NS("VLC media player")];
194
195     b_dropzone_active = YES;
196     o_temp_view = [[NSView alloc] init];
197     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
198     [o_dropzone_view setFrame: [o_playlist_table frame]];
199     [o_left_split_view setFrame: [o_sidebar_view frame]];
200     
201     if (b_nativeFullscreenMode) {
202         [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
203     } else {
204         [o_titlebar_view setFullscreenButtonHidden: YES];
205     }
206
207     if (!OSX_SNOW_LEOPARD) {
208         /* the default small size of the search field is slightly different on Lion, let's work-around that */
209         NSRect frame;
210         frame = [o_search_fld frame];
211         frame.origin.y = frame.origin.y + 2.0;
212         frame.size.height = frame.size.height - 1.0;
213         [o_search_fld setFrame: frame];
214     }
215
216     /* create the sidebar */
217     o_sidebaritems = [[NSMutableArray alloc] init];
218     SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
219     SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
220     [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
221     SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
222     [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
223     SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
224     SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
225     SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
226     SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
227
228     /* SD subnodes, inspired by the Qt4 intf */
229     char **ppsz_longnames;
230     int *p_categories;
231     char **ppsz_names = vlc_sd_GetNames(pl_Get(VLCIntf), &ppsz_longnames, &p_categories);
232     if (!ppsz_names)
233         msg_Err(VLCIntf, "no sd item found"); //TODO
234     char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
235     int *p_category = p_categories;
236     NSMutableArray *internetItems = [[NSMutableArray alloc] init];
237     NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
238     NSMutableArray *lanItems = [[NSMutableArray alloc] init];
239     NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
240     NSString *o_identifier;
241     for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++) {
242         o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
243         switch (*p_category) {
244             case SD_CAT_INTERNET:
245                     [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
246                     if (!strncmp(*ppsz_name, "podcast", 7))
247                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
248                     else
249                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
250                     [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
251                     [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
252                 break;
253             case SD_CAT_DEVICES:
254                     [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
255                     [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
256                     [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
257                     [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
258                 break;
259             case SD_CAT_LAN:
260                     [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
261                     [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
262                     [[lanItems lastObject] setSdtype: SD_CAT_LAN];
263                     [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
264                 break;
265             case SD_CAT_MYCOMPUTER:
266                     [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
267                     if (!strncmp(*ppsz_name, "video_dir", 9))
268                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
269                     else if (!strncmp(*ppsz_name, "audio_dir", 9))
270                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
271                     else if (!strncmp(*ppsz_name, "picture_dir", 11))
272                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
273                     else
274                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
275                     [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
276                     [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
277                 break;
278             default:
279                 msg_Warn(VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name);
280                 break;
281         }
282
283         free(*ppsz_name);
284         free(*ppsz_longname);
285     }
286     [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
287     [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
288     [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
289     [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
290     [mycompItems release];
291     [devicesItems release];
292     [lanItems release];
293     [internetItems release];
294     free(ppsz_names);
295     free(ppsz_longnames);
296     free(p_categories);
297
298     [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
299     [o_sidebaritems addObject: libraryItem];
300     if ([mycompItem hasChildren])
301         [o_sidebaritems addObject: mycompItem];
302     if ([devicesItem hasChildren])
303         [o_sidebaritems addObject: devicesItem];
304     if ([lanItem hasChildren])
305         [o_sidebaritems addObject: lanItem];
306     if ([internetItem hasChildren])
307         [o_sidebaritems addObject: internetItem];
308
309     [o_sidebar_view reloadData];
310     [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
311     [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
312     [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
313
314     [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
315     [(PXSourceList *)o_sidebar_view setDataSource:self];
316     [o_sidebar_view setDelegate:self];
317     [o_sidebar_view setAutosaveExpandedItems:YES];
318
319     [o_sidebar_view expandItem: libraryItem expandChildren: YES];
320
321     /* make sure we display the desired default appearance when VLC launches for the first time */
322     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
323     if (![defaults objectForKey:@"VLCFirstRun"]) {
324         [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
325
326         NSUInteger i_sidebaritem_count = [o_sidebaritems count];
327         for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
328             [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
329     }
330
331     if (b_dark_interface) {
332         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
333         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
334
335         [self setBackgroundColor: [NSColor clearColor]];
336         [self setOpaque: NO];
337         [self display];
338         [self setHasShadow:NO];
339         [self setHasShadow:YES];
340
341         NSRect winrect = [self frame];
342         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
343
344         [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
345                                               winrect.size.width, f_titleBarHeight)];
346         [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
347
348         if (winrect.size.height > 100) {
349             [self setFrame: winrect display:YES animate:YES];
350             previousSavedFrame = winrect;
351         }
352
353         winrect = [o_split_view frame];
354         winrect.size.height = winrect.size.height - f_titleBarHeight;
355         [o_split_view setFrame: winrect];
356         [o_video_view setFrame: winrect];
357
358         o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
359         [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
360         [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
361         
362     } else {
363         [o_video_view setFrame: [o_split_view frame]];
364         [o_playlist_table setBorderType: NSNoBorder];
365         [o_sidebar_scrollview setBorderType: NSNoBorder];
366     }
367
368     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
369     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
370     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
371
372     [o_split_view setAutosaveName:@"10thanniversary-splitview"];
373     if (b_splitviewShouldBeHidden) {
374         [self hideSplitView];
375         i_lastSplitViewHeight = 300;
376     }
377
378     /* sanity check for the window size */
379     NSRect frame = [self frame];
380     NSSize screenSize = [[self screen] frame].size;
381     if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
382         nativeVideoSize = screenSize;
383         [self resizeWindow];
384     }
385 }
386
387 #pragma mark -
388
389 - (VLCMainWindowControlsBar *)controlsBar;
390 {
391     return (VLCMainWindowControlsBar *)o_controls_bar;
392 }
393
394 - (void)resizePlaylistAfterCollapse
395 {
396     NSRect plrect;
397     plrect = [o_playlist_table frame];
398     plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
399     [[o_playlist_table animator] setFrame: plrect];
400
401     NSRect rightSplitRect;
402     rightSplitRect = [o_right_split_view frame];
403     plrect = [o_dropzone_box frame];
404     plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
405     plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
406     [[o_dropzone_box animator] setFrame: plrect];
407 }
408
409 - (void)makeSplitViewVisible
410 {
411     if (b_dark_interface)
412         [self setContentMinSize: NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
413     else
414         [self setContentMinSize: NSMakeSize(604., 288.)];
415
416     NSRect old_frame = [self frame];
417     float newHeight = [self minSize].height;
418     if (old_frame.size.height < newHeight) {
419         NSRect new_frame = old_frame;
420         new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
421         new_frame.size.height = newHeight;
422
423         [[self animator] setFrame: new_frame display: YES animate: YES];
424     }
425
426     [o_video_view setHidden: YES];
427     [o_split_view setHidden: NO];
428     [self makeFirstResponder: nil];
429
430 }
431
432 - (void)makeSplitViewHidden
433 {
434     if (b_dark_interface)
435         [self setContentMinSize: NSMakeSize(604., f_min_video_height + [o_titlebar_view frame].size.height)];
436     else
437         [self setContentMinSize: NSMakeSize(604., f_min_video_height)];
438
439     [o_split_view setHidden: YES];
440     [o_video_view setHidden: NO];
441
442     if ([[o_video_view subviews] count] > 0)
443         [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
444 }
445
446 // only exception for an controls bar button action
447 - (IBAction)togglePlaylist:(id)sender
448 {
449     if (![self isVisible] && sender != nil) {
450         [self makeKeyAndOrderFront: sender];
451         return;
452     }
453
454     BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
455     BOOL b_restored = NO;
456
457     // TODO: implement toggle playlist in this situation (triggerd via menu item).
458     // but for now we block this case, to avoid displaying only the half
459     if (b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil)
460         return;
461
462     if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0) {
463         [self hideDropZone];
464         return;
465     }
466
467     if (!(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ((([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
468                                                                               || (b_nonembedded && sender != nil)
469                                                                               || (!b_activeVideo && sender != nil)
470                                                                               || b_minimized_view))
471         [self hideSplitView];
472     else {
473         if (b_splitview_removed) {
474             if (!b_nonembedded || (sender != nil && b_nonembedded))
475                 [self showSplitView];
476
477             if (sender == nil)
478                 b_minimized_view = YES;
479             else
480                 b_minimized_view = NO;
481
482             if (b_activeVideo)
483                 b_restored = YES;
484         }
485
486         if (!b_nonembedded) {
487             if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil))
488                 [self makeSplitViewHidden];
489             else
490                 [self makeSplitViewVisible];
491         } else {
492             [o_split_view setHidden: NO];
493             [o_playlist_table setHidden: NO];
494             [o_video_view setHidden: !b_activeVideo];
495             if (b_activeVideo && [[o_video_view subviews] count] > 0)
496                 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
497         }
498     }
499 }
500
501 - (IBAction)dropzoneButtonAction:(id)sender
502 {
503     [[[VLCMain sharedInstance] open] openFileGeneric];
504 }
505
506 #pragma mark -
507 #pragma mark overwritten default functionality
508
509 - (void)windowResizedOrMoved:(NSNotification *)notification
510 {
511     [self saveFrameUsingName: [self frameAutosaveName]];
512 }
513
514 - (void)applicationWillTerminate:(NSNotification *)notification
515 {
516     [self saveFrameUsingName: [self frameAutosaveName]];
517 }
518
519
520 - (void)someWindowWillClose:(NSNotification *)notification
521 {
522     id obj = [notification object];
523     
524     if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !b_nonembedded)) {
525         if ([[VLCMain sharedInstance] activeVideoPlayback])
526             [[VLCCoreInteraction sharedInstance] stop];
527     }
528 }
529
530 - (void)someWindowWillMiniaturize:(NSNotification *)notification
531 {
532     if (config_GetInt(VLCIntf, "macosx-pause-minimized")) {
533         id obj = [notification object];
534
535         if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !b_nonembedded)) {
536             if ([[VLCMain sharedInstance] activeVideoPlayback])
537                 [[VLCCoreInteraction sharedInstance] pause];
538         }
539     }
540 }
541
542 #pragma mark -
543 #pragma mark Update interface and respond to foreign events
544 - (void)showDropZone
545 {
546     b_dropzone_active = YES;
547     [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
548     [o_dropzone_view setFrame: [o_playlist_table frame]];
549     [[o_playlist_table animator] setHidden:YES];
550 }
551
552 - (void)hideDropZone
553 {
554     b_dropzone_active = NO;
555     [o_dropzone_view removeFromSuperview];
556     [[o_playlist_table animator] setHidden: NO];
557 }
558
559 - (void)hideSplitView
560 {
561     NSRect winrect = [self frame];
562     i_lastSplitViewHeight = [o_split_view frame].size.height;
563     winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
564     winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
565     [self setFrame: winrect display: YES animate: YES];
566     [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
567     if (b_dark_interface) {
568         [self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
569         [self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
570     } else {
571         [self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height)];
572         [self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height)];
573     }
574
575     b_splitview_removed = YES;
576 }
577
578 - (void)showSplitView
579 {
580     [self updateWindow];
581     if (b_dark_interface)
582         [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
583     else
584         [self setContentMinSize:NSMakeSize(604., 288.)];
585     [self setContentMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
586
587     NSRect winrect;
588     winrect = [self frame];
589     winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
590     winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
591     [self setFrame: winrect display: YES animate: YES];
592
593     [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
594
595     b_splitview_removed = NO;
596 }
597
598 - (void)updateTimeSlider
599 {
600     [o_controls_bar updateTimeSlider];
601     [[self controlsBar] updatePosAndTimeInFSPanel:o_fspanel];
602
603     [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateTimeSlider)];
604 }
605
606 - (void)updateName
607 {
608     input_thread_t * p_input;
609     p_input = pl_CurrentInput(VLCIntf);
610     if (p_input) {
611         NSString *aString;
612         char *format = var_InheritString(VLCIntf, "input-title-format");
613         char *formated = str_format_meta(pl_Get(VLCIntf), format);
614         free(format);
615         aString = [NSString stringWithUTF8String:formated];
616         free(formated);
617
618         char *uri = input_item_GetURI(input_GetItem(p_input));
619
620         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
621         if ([o_url isFileURL]) {
622             [self setRepresentedURL: o_url];
623             [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
624                 [o_window setRepresentedURL:o_url];
625             }];
626         } else {
627             [self setRepresentedURL: nil];
628             [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
629                 [o_window setRepresentedURL:nil];
630             }];
631         }
632         free(uri);
633
634         if ([aString isEqualToString:@""]) {
635             if ([o_url isFileURL])
636                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
637             else
638                 aString = [o_url absoluteString];
639         }
640
641         [self setTitle: aString];
642         [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
643             [o_window setTitle:aString];
644         }];
645
646         [o_fspanel setStreamTitle: aString];
647         vlc_object_release(p_input);
648     } else {
649         [self setTitle: _NS("VLC media player")];
650         [self setRepresentedURL: nil];
651     }
652 }
653
654 - (void)updateWindow
655 {
656     [o_controls_bar updateControls];
657     [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateControls)];
658
659     bool b_seekable = false;
660
661     playlist_t * p_playlist = pl_Get(VLCIntf);
662     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
663     if (p_input) {
664         /* seekable streams */
665         b_seekable = var_GetBool(p_input, "can-seek");
666
667         vlc_object_release(p_input);
668     }
669
670     [self updateTimeSlider];
671     if ([o_fspanel respondsToSelector:@selector(setSeekable:)])
672         [o_fspanel setSeekable: b_seekable];
673
674     PL_LOCK;
675     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
676         [self hideDropZone];
677     else
678         [self showDropZone];
679     PL_UNLOCK;
680     [o_sidebar_view setNeedsDisplay:YES];
681 }
682
683 - (void)setPause
684 {
685     [o_controls_bar setPause];
686     [o_fspanel setPause];
687
688     [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPause)];
689 }
690
691 - (void)setPlay
692 {
693     [o_controls_bar setPlay];
694     [o_fspanel setPlay];
695
696     [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPlay)];
697
698 }
699
700 - (void)updateVolumeSlider
701 {
702     [[self controlsBar] updateVolumeSlider];
703     [o_fspanel setVolumeLevel: [[VLCCoreInteraction sharedInstance] volume]];
704 }
705
706 #pragma mark -
707 #pragma mark Video Output handling
708
709 - (VLCVoutView *)setupVout:(vout_window_t *)p_wnd
710 {
711     BOOL b_video_deco = var_InheritBool(VLCIntf, "video-deco");
712     BOOL b_video_wallpaper = var_InheritBool(VLCIntf, "video-wallpaper");
713     VLCVoutView *o_vout_view;
714     VLCVideoWindowCommon *o_new_video_window;
715
716     // TODO: make lion fullscreen compatible with video-wallpaper and !embedded-video
717     if ((b_video_wallpaper || !b_video_deco) && !b_nativeFullscreenMode) {
718         // b_video_wallpaper is priorized over !b_video_deco
719
720         msg_Dbg(VLCIntf, "Creating background / blank window");
721         NSScreen *screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger(VLCIntf, "macosx-vdev")];
722         if (!screen)
723             screen = [self screen];
724
725         NSRect window_rect;
726         if (b_video_wallpaper)
727             window_rect = [screen frame];
728         else
729             window_rect = [self frame];
730
731         NSUInteger mask = NSBorderlessWindowMask;
732         if (!OSX_SNOW_LEOPARD && !b_video_deco)
733             mask |= NSResizableWindowMask;
734
735         BOOL b_no_video_deco_only = !b_video_wallpaper;
736         o_new_video_window = [[VLCVideoWindowCommon alloc] initWithContentRect:window_rect styleMask:mask backing:NSBackingStoreBuffered defer:YES];
737         [o_new_video_window setDelegate:o_new_video_window];
738
739         if (b_video_wallpaper)
740             [o_new_video_window setLevel:CGWindowLevelForKey(kCGDesktopWindowLevelKey) + 1];
741
742         [o_new_video_window setBackgroundColor: [NSColor blackColor]];
743         [o_new_video_window setCanBecomeKeyWindow: !b_video_wallpaper];
744         [o_new_video_window setCanBecomeMainWindow: !b_video_wallpaper];
745         [o_new_video_window setAcceptsMouseMovedEvents: !b_video_wallpaper];
746         [o_new_video_window setMovableByWindowBackground: !b_video_wallpaper];
747         [o_new_video_window useOptimizedDrawing: YES];
748
749         o_vout_view = [[VLCVoutView alloc] initWithFrame:[[o_new_video_window contentView] bounds]];
750         [o_vout_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
751         [[o_new_video_window contentView] addSubview:o_vout_view positioned:NSWindowAbove relativeTo:nil];
752         [o_new_video_window setVideoView:o_vout_view];
753
754
755         if (b_video_wallpaper)
756             [o_new_video_window orderBack:nil];
757         else {
758             [o_new_video_window center];
759             [o_new_video_window setFrameAutosaveName:@"extra-videowindow"];
760             [o_new_video_window setContentMinSize: NSMakeSize(f_min_video_height, f_min_video_height)];
761         }
762
763         b_nonembedded = YES;
764     } else {
765         if (var_InheritBool(VLCIntf, "embedded-video") || b_nativeFullscreenMode) {
766             o_vout_view = [o_video_view retain];
767             o_new_video_window = self;
768             b_nonembedded = NO;
769         } else {
770             NSWindowController *o_controller = [[NSWindowController alloc] initWithWindowNibName:@"DetachedVideoWindow"];
771             [o_controller loadWindow];
772             o_new_video_window = [(VLCDetachedVideoWindow *)[o_controller window] retain];
773             [o_controller release];
774
775             [o_new_video_window setDelegate: o_new_video_window];
776             [o_new_video_window setLevel:NSNormalWindowLevel];
777             [o_new_video_window useOptimizedDrawing: YES];
778             o_vout_view = [[o_new_video_window videoView] retain];
779             b_nonembedded = YES;
780         }
781     }
782
783     if (!b_video_wallpaper) {
784         [o_new_video_window makeKeyAndOrderFront: self];
785
786         vout_thread_t *p_vout = getVout();
787         if (p_vout) {
788             if (var_GetBool(p_vout, "video-on-top"))
789                 [o_new_video_window setLevel: NSStatusWindowLevel];
790             else
791                 [o_new_video_window setLevel: NSNormalWindowLevel];
792             vlc_object_release(p_vout);
793         }
794     }
795
796     [o_new_video_window setAlphaValue: config_GetFloat(VLCIntf, "macosx-opaqueness")];
797     [[[VLCMain sharedInstance] voutController] addVout:o_new_video_window forDisplay:p_wnd];
798
799     if(b_nonembedded) {
800         // event occurs before window is created, so call again
801         [[VLCMain sharedInstance] playbackStatusUpdated];
802     }
803
804     return [o_vout_view autorelease];
805 }
806
807 - (void)setVideoplayEnabled
808 {
809     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
810
811     if (b_videoPlayback) {
812         frameBeforePlayback = [self frame];
813
814         // look for 'start at fullscreen'
815         [[VLCMain sharedInstance] fullscreenChanged];
816     } else {
817         if (!b_nonembedded)
818             [[self animator] setFrame:frameBeforePlayback display:YES];
819
820         [self makeFirstResponder: nil];
821
822         if ([self level] != NSNormalWindowLevel)
823             [self setLevel: NSNormalWindowLevel];
824
825         // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
826         [self setAlphaValue:1.0];
827     }
828
829     if (b_nativeFullscreenMode) {
830         if ([NSApp presentationOptions] & NSApplicationPresentationFullScreen)
831             [[o_controls_bar bottomBarView] setHidden: b_videoPlayback];
832         else
833             [[o_controls_bar bottomBarView] setHidden: NO];
834         if (b_videoPlayback && b_fullscreen)
835             [o_fspanel setActive: nil];
836         if (!b_videoPlayback)
837             [o_fspanel setNonActive: nil];
838     }
839
840     if (!b_videoPlayback && b_fullscreen) {
841         if (!b_nativeFullscreenMode)
842             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
843     }
844 }
845
846
847 //  Called automatically if window's acceptsMouseMovedEvents property is true
848 - (void)mouseMoved:(NSEvent *)theEvent
849 {
850     if (b_fullscreen)
851         [self recreateHideMouseTimer];
852
853     [super mouseMoved: theEvent];
854 }
855
856 - (void)recreateHideMouseTimer
857 {
858     if (t_hide_mouse_timer != nil) {
859         [t_hide_mouse_timer invalidate];
860         [t_hide_mouse_timer release];
861     }
862
863     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
864                                                           target:self
865                                                         selector:@selector(hideMouseCursor:)
866                                                         userInfo:nil
867                                                          repeats:NO];
868     [t_hide_mouse_timer retain];
869 }
870
871 //  NSTimer selectors require this function signature as per Apple's docs
872 - (void)hideMouseCursor:(NSTimer *)timer
873 {
874     [NSCursor setHiddenUntilMouseMoves: YES];
875 }
876
877 #pragma mark -
878 #pragma mark Fullscreen support
879 - (void)showFullscreenController
880 {
881      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback])
882         [o_fspanel fadeIn];
883 }
884
885 - (BOOL)fullscreen
886 {
887     return b_fullscreen;
888 }
889
890 - (void)lockFullscreenAnimation
891 {
892     [o_animation_lock lock];
893 }
894
895 - (void)unlockFullscreenAnimation
896 {
897     [o_animation_lock unlock];
898 }
899
900 - (void)enterFullscreen
901 {
902     NSMutableDictionary *dict1, *dict2;
903     NSScreen *screen;
904     NSRect screen_rect;
905     NSRect rect;
906     BOOL blackout_other_displays = var_InheritBool(VLCIntf, "macosx-black");
907     o_current_video_window = [o_video_view window];
908
909     screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger(VLCIntf, "macosx-vdev")];
910     [self lockFullscreenAnimation];
911
912     if (!screen) {
913         msg_Dbg(VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode");
914         screen = [o_current_video_window screen];
915     }
916     if (!screen) {
917         msg_Dbg(VLCIntf, "Using deepest screen");
918         screen = [NSScreen deepestScreen];
919     }
920
921     screen_rect = [screen frame];
922
923     [o_controls_bar setFullscreenState:YES];
924     //if (o_detached_video_window)
925     //    [[o_detached_video_window controlsBar] setFullscreenState:YES];
926
927     [self recreateHideMouseTimer];
928
929     if (blackout_other_displays)
930         [screen blackoutOtherScreens];
931
932     /* Make sure we don't see the window flashes in float-on-top mode */
933     i_originalLevel = [o_current_video_window level];
934     [o_current_video_window setLevel:NSNormalWindowLevel];
935
936     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
937     if (!o_fullscreen_window) {
938         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
939
940         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
941         rect.origin.x += [o_current_video_window frame].origin.x;
942         rect.origin.y += [o_current_video_window frame].origin.y;
943         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
944         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
945         [o_fullscreen_window setCanBecomeKeyWindow: YES];
946         [o_fullscreen_window setCanBecomeMainWindow: YES];
947
948         if (![o_current_video_window isVisible] || [o_current_video_window alphaValue] == 0.0) {
949             /* We don't animate if we are not visible, instead we
950              * simply fade the display */
951             CGDisplayFadeReservationToken token;
952
953             if (blackout_other_displays) {
954                 CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
955                 CGDisplayFade(token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
956             }
957
958             if ([screen mainScreen])
959                 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
960
961             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
962             [o_temp_view setFrame:[o_video_view frame]];
963             [o_fullscreen_window setContentView:o_video_view];
964
965             [o_fullscreen_window makeKeyAndOrderFront:self];
966             [o_fullscreen_window orderFront:self animate:YES];
967
968             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
969             [o_fullscreen_window setLevel:NSNormalWindowLevel];
970
971             if (blackout_other_displays) {
972                 CGDisplayFade(token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO);
973                 CGReleaseDisplayFadeReservation(token);
974             }
975
976             /* Will release the lock */
977             [self hasBecomeFullscreen];
978
979             return;
980         }
981
982         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
983         NSDisableScreenUpdates();
984         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
985         [o_temp_view setFrame:[o_video_view frame]];
986         [o_fullscreen_window setContentView:o_video_view];
987         [o_fullscreen_window makeKeyAndOrderFront:self];
988         NSEnableScreenUpdates();
989     }
990
991     /* We are in fullscreen (and no animation is running) */
992     if (b_fullscreen) {
993         /* Make sure we are hidden */
994         [o_current_video_window orderOut: self];
995
996         [self unlockFullscreenAnimation];
997         return;
998     }
999
1000     if (o_fullscreen_anim1) {
1001         [o_fullscreen_anim1 stopAnimation];
1002         [o_fullscreen_anim1 release];
1003     }
1004     if (o_fullscreen_anim2) {
1005         [o_fullscreen_anim2 stopAnimation];
1006         [o_fullscreen_anim2 release];
1007     }
1008
1009     if ([screen mainScreen])
1010         [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1011
1012     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
1013     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
1014
1015     [dict1 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
1016     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
1017
1018     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1019     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1020     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
1021
1022     /* Strategy with NSAnimation allocation:
1023      - Keep at most 2 animation at a time
1024      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
1025      */
1026     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1027     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1028
1029     [dict1 release];
1030     [dict2 release];
1031
1032     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1033     [o_fullscreen_anim1 setDuration: 0.3];
1034     [o_fullscreen_anim1 setFrameRate: 30];
1035     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1036     [o_fullscreen_anim2 setDuration: 0.2];
1037     [o_fullscreen_anim2 setFrameRate: 30];
1038
1039     [o_fullscreen_anim2 setDelegate: self];
1040     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1041
1042     [o_fullscreen_anim1 startAnimation];
1043     /* fullscreenAnimation will be unlocked when animation ends */
1044 }
1045
1046 - (void)hasBecomeFullscreen
1047 {
1048     if ([[o_video_view subviews] count] > 0)
1049         [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1050
1051     [o_fullscreen_window makeKeyWindow];
1052     [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
1053
1054     /* tell the fspanel to move itself to front next time it's triggered */
1055     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
1056     [o_fspanel setActive: nil];
1057
1058     if ([o_current_video_window isVisible])
1059         [o_current_video_window orderOut: self];
1060
1061     b_fullscreen = YES;
1062     [self unlockFullscreenAnimation];
1063 }
1064
1065 - (void)leaveFullscreen
1066 {
1067     [self leaveFullscreenAndFadeOut: NO];
1068 }
1069
1070 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1071 {
1072     NSMutableDictionary *dict1, *dict2;
1073     NSRect frame;
1074     BOOL blackout_other_displays = var_InheritBool(VLCIntf, "macosx-black");
1075
1076     if (!o_current_video_window)
1077         return;
1078
1079     [self lockFullscreenAnimation];
1080
1081     [o_controls_bar setFullscreenState:NO];
1082     //if (o_detached_video_window)
1083     //    [[o_detached_video_window controlsBar] setFullscreenState:NO];
1084
1085     /* We always try to do so */
1086     [NSScreen unblackoutScreens];
1087
1088     vout_thread_t *p_vout = getVout();
1089     if (p_vout) {
1090         if (var_GetBool(p_vout, "video-on-top"))
1091             [[o_video_view window] setLevel: NSStatusWindowLevel];
1092         else
1093             [[o_video_view window] setLevel: NSNormalWindowLevel];
1094         vlc_object_release(p_vout);
1095     }
1096     [[o_video_view window] makeKeyAndOrderFront: nil];
1097
1098     /* Don't do anything if o_fullscreen_window is already closed */
1099     if (!o_fullscreen_window) {
1100         [self unlockFullscreenAnimation];
1101         return;
1102     }
1103
1104     if (fadeout) {
1105         /* We don't animate if we are not visible, instead we
1106          * simply fade the display */
1107         CGDisplayFadeReservationToken token;
1108
1109         if (blackout_other_displays) {
1110             CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
1111             CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
1112         }
1113
1114         [o_fspanel setNonActive: nil];
1115         [NSApp setPresentationOptions: NSApplicationPresentationDefault];
1116
1117         /* Will release the lock */
1118         [self hasEndedFullscreen];
1119
1120         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1121          * here */
1122         b_window_is_invisible = YES;
1123
1124         if (blackout_other_displays) {
1125             CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO);
1126             CGReleaseDisplayFadeReservation(token);
1127         }
1128
1129         return;
1130     }
1131
1132     [o_current_video_window setAlphaValue: 0.0];
1133     [o_current_video_window orderFront: self];
1134     [[o_video_view window] orderFront: self];
1135
1136     [o_fspanel setNonActive: nil];
1137     [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1138
1139     if (o_fullscreen_anim1) {
1140         [o_fullscreen_anim1 stopAnimation];
1141         [o_fullscreen_anim1 release];
1142     }
1143     if (o_fullscreen_anim2) {
1144         [o_fullscreen_anim2 stopAnimation];
1145         [o_fullscreen_anim2 release];
1146     }
1147
1148     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1149     frame.origin.x += [o_current_video_window frame].origin.x;
1150     frame.origin.y += [o_current_video_window frame].origin.y;
1151
1152     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1153     [dict2 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
1154     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1155
1156     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
1157     [dict2 release];
1158
1159     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1160     [o_fullscreen_anim2 setDuration: 0.3];
1161     [o_fullscreen_anim2 setFrameRate: 30];
1162
1163     [o_fullscreen_anim2 setDelegate: self];
1164
1165     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1166
1167     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1168     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1169     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1170
1171     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
1172     [dict1 release];
1173
1174     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1175     [o_fullscreen_anim1 setDuration: 0.2];
1176     [o_fullscreen_anim1 setFrameRate: 30];
1177     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1178
1179     /* Make sure o_fullscreen_window is the frontmost window */
1180     [o_fullscreen_window orderFront: self];
1181
1182     [o_fullscreen_anim1 startAnimation];
1183     /* fullscreenAnimation will be unlocked when animation ends */
1184 }
1185
1186 - (void)hasEndedFullscreen
1187 {
1188     b_fullscreen = NO;
1189
1190     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1191     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1192     NSDisableScreenUpdates();
1193     [o_video_view retain];
1194     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1195     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1196     [o_video_view release];
1197     [o_video_view setFrame:[o_temp_view frame]];
1198     if ([[o_video_view subviews] count] > 0)
1199         [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1200     if (!b_nonembedded)
1201             [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
1202     else
1203         [[o_video_view window] makeKeyAndOrderFront: self];
1204     [o_fullscreen_window orderOut: self];
1205     NSEnableScreenUpdates();
1206
1207     [o_fullscreen_window release];
1208     o_fullscreen_window = nil;
1209     [[o_video_view window] setLevel:i_originalLevel];
1210     [[o_video_view window] setAlphaValue: config_GetFloat(VLCIntf, "macosx-opaqueness")];
1211
1212     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
1213     if (![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded)
1214         [o_current_video_window orderOut: self];
1215
1216     o_current_video_window = nil;
1217     [self unlockFullscreenAnimation];
1218 }
1219
1220 - (void)animationDidEnd:(NSAnimation*)animation
1221 {
1222     NSArray *viewAnimations;
1223     if (o_makekey_anim == animation) {
1224         [o_makekey_anim release];
1225         return;
1226     }
1227     if ([animation currentValue] < 1.0)
1228         return;
1229
1230     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
1231     viewAnimations = [o_fullscreen_anim2 viewAnimations];
1232     if ([viewAnimations count] >=1 &&
1233         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect]) {
1234         /* Fullscreen ended */
1235         [self hasEndedFullscreen];
1236     } else
1237         /* Fullscreen started */
1238         [self hasBecomeFullscreen];
1239 }
1240
1241 - (void)makeKeyAndOrderFront: (id)sender
1242 {
1243     /* Hack
1244      * when we exit fullscreen and fade out, we may endup in
1245      * having a window that is faded. We can't have it fade in unless we
1246      * animate again. */
1247
1248     if (!b_window_is_invisible) {
1249         /* Make sure we don't do it too much */
1250         [super makeKeyAndOrderFront: sender];
1251         return;
1252     }
1253
1254     [super setAlphaValue:0.0f];
1255     [super makeKeyAndOrderFront: sender];
1256
1257     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
1258     [dict setObject:self forKey:NSViewAnimationTargetKey];
1259     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1260
1261     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1262     [dict release];
1263
1264     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
1265     [o_makekey_anim setDuration: 0.1];
1266     [o_makekey_anim setFrameRate: 30];
1267     [o_makekey_anim setDelegate: self];
1268
1269     [o_makekey_anim startAnimation];
1270     b_window_is_invisible = NO;
1271
1272     /* fullscreenAnimation will be unlocked when animation ends */
1273 }
1274
1275 #pragma mark -
1276 #pragma mark Lion native fullscreen handling
1277 - (void)windowWillEnterFullScreen:(NSNotification *)notification
1278 {
1279     // workaround, see #6668
1280     [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1281
1282     var_SetBool(pl_Get(VLCIntf), "fullscreen", true);
1283
1284     vout_thread_t *p_vout = getVout();
1285     if (p_vout) {
1286         var_SetBool(p_vout, "fullscreen", true);
1287         vlc_object_release(p_vout);
1288     }
1289
1290     [o_video_view setFrame: [[self contentView] frame]];
1291     b_fullscreen = YES;
1292
1293     [self recreateHideMouseTimer];
1294     i_originalLevel = [self level];
1295     [self setLevel:NSNormalWindowLevel];
1296
1297     if (b_dark_interface) {
1298         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
1299
1300         NSRect winrect;
1301         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1302         winrect = [self frame];
1303
1304         winrect.size.height = winrect.size.height - f_titleBarHeight;
1305         [self setFrame: winrect display:NO animate:NO];
1306         winrect = [o_split_view frame];
1307         winrect.size.height = winrect.size.height + f_titleBarHeight;
1308         [o_split_view setFrame: winrect];
1309     }
1310
1311     if ([[VLCMain sharedInstance] activeVideoPlayback])
1312         [[o_controls_bar bottomBarView] setHidden: YES];
1313
1314     [self setMovableByWindowBackground: NO];
1315 }
1316
1317 - (void)windowDidEnterFullScreen:(NSNotification *)notification
1318 {
1319     // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
1320     // But this creates some problems when leaving fs over remote intfs, so activate app here.
1321     [NSApp activateIgnoringOtherApps:YES];
1322
1323     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
1324     [o_fspanel setActive: nil];
1325 }
1326
1327 - (void)windowWillExitFullScreen:(NSNotification *)notification
1328 {
1329
1330     var_SetBool(pl_Get(VLCIntf), "fullscreen", false);
1331
1332     vout_thread_t *p_vout = getVout();
1333     if (p_vout) {
1334         var_SetBool(p_vout, "fullscreen", false);
1335         vlc_object_release(p_vout);
1336     }
1337
1338     [o_video_view setFrame: [o_split_view frame]];
1339     [NSCursor setHiddenUntilMouseMoves: NO];
1340     [o_fspanel setNonActive: nil];
1341     [self setLevel:i_originalLevel];
1342     b_fullscreen = NO;
1343
1344     if (b_dark_interface) {
1345         NSRect winrect;
1346         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1347         winrect = [self frame];
1348
1349         [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
1350                                               winrect.size.width, f_titleBarHeight)];
1351         [[self contentView] addSubview: o_titlebar_view];
1352
1353         winrect.size.height = winrect.size.height + f_titleBarHeight;
1354         [self setFrame: winrect display:NO animate:NO];
1355         winrect = [o_split_view frame];
1356         winrect.size.height = winrect.size.height - f_titleBarHeight;
1357         [o_split_view setFrame: winrect];
1358         [o_video_view setFrame: winrect];
1359     }
1360
1361     if ([[VLCMain sharedInstance] activeVideoPlayback])
1362         [[o_controls_bar bottomBarView] setHidden: NO];
1363
1364     [self setMovableByWindowBackground: YES];
1365 }
1366
1367 #pragma mark -
1368 #pragma mark split view delegate
1369 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
1370 {
1371     if (dividerIndex == 0)
1372         return 300.;
1373     else
1374         return proposedMax;
1375 }
1376
1377 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
1378 {
1379     if (dividerIndex == 0)
1380         return 100.;
1381     else
1382         return proposedMin;
1383 }
1384
1385 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
1386 {
1387     return ([subview isEqual:o_left_split_view]);
1388 }
1389
1390 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
1391 {
1392     if ([subview isEqual:o_left_split_view])
1393         return NO;
1394     return YES;
1395 }
1396
1397 #pragma mark -
1398 #pragma mark Side Bar Data handling
1399 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1400 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
1401 {
1402     //Works the same way as the NSOutlineView data source: `nil` means a parent item
1403     if (item==nil)
1404         return [o_sidebaritems count];
1405     else
1406         return [[item children] count];
1407 }
1408
1409
1410 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
1411 {
1412     //Works the same way as the NSOutlineView data source: `nil` means a parent item
1413     if (item==nil)
1414         return [o_sidebaritems objectAtIndex:index];
1415     else
1416         return [[item children] objectAtIndex:index];
1417 }
1418
1419
1420 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
1421 {
1422     return [item title];
1423 }
1424
1425 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
1426 {
1427     [item setTitle:object];
1428 }
1429
1430 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
1431 {
1432     return [item hasChildren];
1433 }
1434
1435
1436 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
1437 {
1438     if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
1439         return YES;
1440
1441     return [item hasBadge];
1442 }
1443
1444
1445 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
1446 {
1447     playlist_t * p_playlist = pl_Get(VLCIntf);
1448     NSInteger i_playlist_size;
1449
1450     if ([[item identifier] isEqualToString: @"playlist"]) {
1451         PL_LOCK;
1452         i_playlist_size = p_playlist->p_local_category->i_children;
1453         PL_UNLOCK;
1454
1455         return i_playlist_size;
1456     }
1457     if ([[item identifier] isEqualToString: @"medialibrary"]) {
1458         PL_LOCK;
1459         i_playlist_size = p_playlist->p_ml_category->i_children;
1460         PL_UNLOCK;
1461
1462         return i_playlist_size;
1463     }
1464
1465     return [item badgeValue];
1466 }
1467
1468
1469 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
1470 {
1471     return [item hasIcon];
1472 }
1473
1474
1475 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
1476 {
1477     return [item icon];
1478 }
1479
1480 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
1481 {
1482     if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
1483         if (item != nil) {
1484             NSMenu * m;
1485             if ([item sdtype] > 0)
1486             {
1487                 m = [[NSMenu alloc] init];
1488                 playlist_t * p_playlist = pl_Get(VLCIntf);
1489                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
1490                 if (!sd_loaded)
1491                     [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1492                 else
1493                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1494                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
1495             }
1496             return [m autorelease];
1497         }
1498     }
1499
1500     return nil;
1501 }
1502
1503 - (IBAction)sdmenuhandler:(id)sender
1504 {
1505     NSString * identifier = [sender representedObject];
1506     if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"]) {
1507         playlist_t * p_playlist = pl_Get(VLCIntf);
1508         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [identifier UTF8String]);
1509
1510         if (!sd_loaded)
1511             playlist_ServicesDiscoveryAdd(p_playlist, [identifier UTF8String]);
1512         else
1513             playlist_ServicesDiscoveryRemove(p_playlist, [identifier UTF8String]);
1514     }
1515 }
1516
1517 #pragma mark -
1518 #pragma mark Side Bar Delegate Methods
1519 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1520 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
1521 {
1522     if ([[group identifier] isEqualToString:@"library"])
1523         return YES;
1524
1525     return NO;
1526 }
1527
1528 - (void)sourceListSelectionDidChange:(NSNotification *)notification
1529 {
1530     playlist_t * p_playlist = pl_Get(VLCIntf);
1531
1532     NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
1533     id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
1534
1535
1536     //Set the label text to represent the new selection
1537     if ([item sdtype] > -1 && [[item identifier] length] > 0) {
1538         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
1539         if (!sd_loaded)
1540             playlist_ServicesDiscoveryAdd(p_playlist, [[item identifier] UTF8String]);
1541     }
1542
1543     [o_chosen_category_lbl setStringValue:[item title]];
1544
1545     if ([[item identifier] isEqualToString:@"playlist"]) {
1546         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
1547     } else if ([[item identifier] isEqualToString:@"medialibrary"]) {
1548         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
1549     } else {
1550         playlist_item_t * pl_item;
1551         PL_LOCK;
1552         pl_item = playlist_ChildSearchName(p_playlist->p_root, [[item untranslatedTitle] UTF8String]);
1553         PL_UNLOCK;
1554         [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
1555     }
1556
1557     PL_LOCK;
1558     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1559         [self hideDropZone];
1560     else
1561         [self showDropZone];
1562     PL_UNLOCK;
1563
1564     if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
1565         [self showPodcastControls];
1566     else
1567         [self hidePodcastControls];
1568 }
1569
1570 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1571 {
1572     if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"]) {
1573         NSPasteboard *o_pasteboard = [info draggingPasteboard];
1574         if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
1575             return NSDragOperationGeneric;
1576     }
1577     return NSDragOperationNone;
1578 }
1579
1580 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1581 {
1582     NSPasteboard *o_pasteboard = [info draggingPasteboard];
1583
1584     playlist_t * p_playlist = pl_Get(VLCIntf);
1585     playlist_item_t *p_node;
1586
1587     if ([[item identifier] isEqualToString:@"playlist"])
1588         p_node = p_playlist->p_local_category;
1589     else
1590         p_node = p_playlist->p_ml_category;
1591
1592     if ([[o_pasteboard types] containsObject: NSFilenamesPboardType]) {
1593         NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1594         NSUInteger count = [o_values count];
1595         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
1596
1597         for(NSUInteger i = 0; i < count; i++) {
1598             NSDictionary *o_dic;
1599             char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
1600             if (!psz_uri)
1601                 continue;
1602
1603             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
1604
1605             free(psz_uri);
1606
1607             [o_array addObject: o_dic];
1608         }
1609
1610         [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
1611         return YES;
1612     }
1613     else if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"]) {
1614         NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
1615
1616         NSUInteger count = [array count];
1617         playlist_item_t * p_item = NULL;
1618
1619         PL_LOCK;
1620         for(NSUInteger i = 0; i < count; i++) {
1621             p_item = [[array objectAtIndex:i] pointerValue];
1622             if (!p_item) continue;
1623             playlist_NodeAddCopy(p_playlist, p_item, p_node, PLAYLIST_END);
1624         }
1625         PL_UNLOCK;
1626
1627         return YES;
1628     }
1629     return NO;
1630 }
1631
1632 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
1633 {
1634     return [item identifier];
1635 }
1636
1637 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
1638 {
1639     /* the following code assumes for sakes of simplicity that only the top level
1640      * items are allowed to have children */
1641
1642     NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
1643     NSUInteger count = [array count];
1644     if (count < 1)
1645         return nil;
1646
1647     for (NSUInteger x = 0; x < count; x++) {
1648         id item = [array objectAtIndex: x]; // save one objc selector call
1649         if ([[item identifier] isEqualToString:object])
1650             return item;
1651     }
1652
1653     return nil;
1654 }
1655
1656 #pragma mark -
1657 #pragma mark Podcast
1658
1659 - (IBAction)addPodcast:(id)sender
1660 {
1661     [NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
1662 }
1663
1664 - (IBAction)addPodcastWindowAction:(id)sender
1665 {
1666     [o_podcast_subscribe_window orderOut:sender];
1667     [NSApp endSheet: o_podcast_subscribe_window];
1668
1669     if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
1670         NSMutableString * podcastConf = [[NSMutableString alloc] init];
1671         if (config_GetPsz(VLCIntf, "podcast-urls") != NULL)
1672             [podcastConf appendFormat:@"%s|", config_GetPsz(VLCIntf, "podcast-urls")];
1673
1674         [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
1675         config_PutPsz(VLCIntf, "podcast-urls", [podcastConf UTF8String]);
1676
1677         vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name(VLCIntf->p_libvlc, "podcast");
1678         if (p_obj) {
1679             var_SetString(p_obj, "podcast-urls", [podcastConf UTF8String]);
1680             vlc_object_release(p_obj);
1681         }
1682         [podcastConf release];
1683     }
1684 }
1685
1686 - (IBAction)removePodcast:(id)sender
1687 {
1688     if (config_GetPsz(VLCIntf, "podcast-urls") != NULL) {
1689         [o_podcast_unsubscribe_pop removeAllItems];
1690         [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
1691         [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
1692     }
1693 }
1694
1695 - (IBAction)removePodcastWindowAction:(id)sender
1696 {
1697     [o_podcast_unsubscribe_window orderOut:sender];
1698     [NSApp endSheet: o_podcast_unsubscribe_window];
1699
1700     if (sender == o_podcast_unsubscribe_ok_btn) {
1701         NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
1702         [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
1703         config_PutPsz(VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String]);
1704         [urls release];
1705
1706         vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name(VLCIntf->p_libvlc, "podcast");
1707         if (p_obj) {
1708             var_SetString(p_obj, "podcast-urls", config_GetPsz(VLCIntf, "podcast-urls"));
1709             vlc_object_release(p_obj);
1710         }
1711
1712         /* reload the podcast module, since it won't update its list when removing podcasts */
1713         playlist_t * p_playlist = pl_Get(VLCIntf);
1714         if (playlist_IsServicesDiscoveryLoaded(p_playlist, "podcast{longname=\"Podcasts\"}")) {
1715             playlist_ServicesDiscoveryRemove(p_playlist, "podcast{longname=\"Podcasts\"}");
1716             playlist_ServicesDiscoveryAdd(p_playlist, "podcast{longname=\"Podcasts\"}");
1717             [o_playlist_table reloadData];
1718         }
1719
1720     }
1721 }
1722
1723 - (void)showPodcastControls
1724 {
1725     NSRect podcastViewDimensions = [o_podcast_view frame];
1726     NSRect rightSplitRect = [o_right_split_view frame];
1727     NSRect playlistTableRect = [o_playlist_table frame];
1728
1729     podcastViewDimensions.size.width = rightSplitRect.size.width;
1730     podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
1731     [o_podcast_view setFrame:podcastViewDimensions];
1732
1733     playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
1734     playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
1735     [o_playlist_table setFrame:playlistTableRect];
1736     [o_playlist_table setNeedsDisplay:YES];
1737
1738     [o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
1739     b_podcastView_displayed = YES;
1740 }
1741
1742 - (void)hidePodcastControls
1743 {
1744     if (b_podcastView_displayed) {
1745         NSRect podcastViewDimensions = [o_podcast_view frame];
1746         NSRect playlistTableRect = [o_playlist_table frame];
1747
1748         playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
1749         playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
1750
1751         [o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
1752         [o_playlist_table setFrame: playlistTableRect];
1753         b_podcastView_displayed = NO;
1754     }
1755 }
1756
1757 @end
1758
1759 @implementation VLCDetachedVideoWindow
1760
1761 - (void)awakeFromNib
1762 {
1763     [self setAcceptsMouseMovedEvents: YES];
1764
1765     if (b_dark_interface) {
1766         [self setBackgroundColor: [NSColor clearColor]];
1767
1768         [self setOpaque: NO];
1769         [self display];
1770         [self setHasShadow:NO];
1771         [self setHasShadow:YES];
1772
1773         NSRect winrect = [self frame];
1774         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1775
1776         [self setTitle: _NS("VLC media player")];
1777         [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight)];
1778         [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: nil];
1779
1780         // native fs not supported with detached view yet
1781         [o_titlebar_view setFullscreenButtonHidden: YES];
1782     } else {
1783         [self setBackgroundColor: [NSColor blackColor]];
1784     }
1785
1786     NSRect videoViewRect = [[self contentView] bounds];
1787     if (b_dark_interface)
1788         videoViewRect.size.height -= [o_titlebar_view frame].size.height;
1789     CGFloat f_bottomBarHeight = [[[self controlsBar] bottomBarView] frame].size.height;
1790     videoViewRect.size.height -= f_bottomBarHeight;
1791     videoViewRect.origin.y = f_bottomBarHeight;
1792     [o_video_view setFrame: videoViewRect];
1793
1794     if (b_dark_interface) {
1795         o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_video_view frame]];
1796         [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_video_view];
1797         [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
1798
1799         [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[[self controlsBar] bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
1800     } else {
1801         [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[[self controlsBar] bottomBarView] frame].size.height)];
1802     }
1803 }
1804
1805 - (void)dealloc
1806 {
1807     if (b_dark_interface)
1808         [o_color_backdrop release];
1809
1810     [super dealloc];
1811 }
1812
1813 @end