]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: avoid index out of bound exceptions in some cases
[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 <vlc_playlist.h>
38 #import <vlc_aout_intf.h>
39 #import <vlc_url.h>
40 #import <vlc_strings.h>
41 #import <vlc_services_discovery.h>
42 #import <vlc_aout_intf.h>
43
44 @implementation VLCMainWindow
45 static const float f_min_video_height = 70.0;
46
47 static VLCMainWindow *_o_sharedInstance = nil;
48
49 + (VLCMainWindow *)sharedInstance
50 {
51     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
52 }
53
54 #pragma mark -
55 #pragma mark Initialization
56
57 - (id)init
58 {
59     if( _o_sharedInstance)
60     {
61         [self dealloc];
62         return _o_sharedInstance;
63     }
64     else
65     {
66         o_fspanel = [[VLCFSPanel alloc] init];
67         _o_sharedInstance = [super init];
68     }
69
70     return _o_sharedInstance;
71 }
72
73 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
74                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
75 {
76     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
77
78     if (b_dark_interface)
79     {
80 #ifdef MAC_OS_X_VERSION_10_7
81         if (OSX_LION)
82             styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
83         else
84             styleMask = NSBorderlessWindowMask;
85 #else
86         styleMask = NSBorderlessWindowMask;
87 #endif
88     }
89
90     self = [super initWithContentRect:contentRect styleMask:styleMask
91                               backing:backingType defer:flag];
92
93     [[VLCMain sharedInstance] updateTogglePlaylistState];
94
95     /* we want to be moveable regardless of our style */
96     [self setMovableByWindowBackground: YES];
97
98     /* we don't want this window to be restored on relaunch */
99     if (OSX_LION)
100         [self setRestorable:NO];
101
102     return self;
103 }
104
105 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
106 {
107     /* We indeed want to prioritize Cocoa key equivalent against libvlc,
108      so we perform the menu equivalent now. */
109     if([[NSApp mainMenu] performKeyEquivalent:o_event])
110         return TRUE;
111
112     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] || [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
113 }
114
115 - (void)dealloc
116 {
117     if (b_dark_interface)
118         [o_color_backdrop release];
119
120     [[NSNotificationCenter defaultCenter] removeObserver: self];
121     [o_sidebaritems release];
122     [super dealloc];
123 }
124
125 - (void)awakeFromNib
126 {
127     BOOL b_splitviewShouldBeHidden = NO;
128     /* setup the styled interface */
129     b_nativeFullscreenMode = NO;
130 #ifdef MAC_OS_X_VERSION_10_7
131     if( OSX_LION )
132         b_nativeFullscreenMode = config_GetInt( VLCIntf, "macosx-nativefullscreenmode" );
133 #endif
134     i_lastShownVolume = -1;
135     t_hide_mouse_timer = nil;
136     [o_detached_video_window setDelegate: self];
137     [self useOptimizedDrawing: YES];
138
139     [o_play_btn setToolTip: _NS("Play/Pause")];
140     [o_detached_play_btn setToolTip: [o_play_btn toolTip]];
141     [o_bwd_btn setToolTip: _NS("Backward")];
142     [o_detached_bwd_btn setToolTip: [o_bwd_btn toolTip]];
143     [o_fwd_btn setToolTip: _NS("Forward")];
144     [o_detached_fwd_btn setToolTip: [o_fwd_btn toolTip]];
145     [o_stop_btn setToolTip: _NS("Stop")];
146     [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
147     [o_repeat_btn setToolTip: _NS("Repeat")];
148     [o_shuffle_btn setToolTip: _NS("Shuffle")];
149     [o_effects_btn setToolTip: _NS("Effects")];
150     [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
151     [o_detached_fullscreen_btn setToolTip: [o_fullscreen_btn toolTip]];
152     [[o_search_fld cell] setPlaceholderString: _NS("Search")];
153     [o_volume_sld setToolTip: _NS("Volume")];
154     [o_volume_down_btn setToolTip: _NS("Mute")];
155     [o_volume_up_btn setToolTip: _NS("Full Volume")];
156     [o_time_sld setToolTip: _NS("Position")];
157     [o_detached_time_sld setToolTip: [o_time_sld toolTip]];
158     [o_dropzone_btn setTitle: _NS("Open media...")];
159     [o_dropzone_lbl setStringValue: _NS("Drop media here")];
160
161     if (!b_dark_interface) {
162         [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
163         [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
164         [o_bwd_btn setImage: [NSImage imageNamed:@"back"]];
165         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
166         [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back"]];
167         [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
168         o_play_img = [[NSImage imageNamed:@"play"] retain];
169         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
170         o_pause_img = [[NSImage imageNamed:@"pause"] retain];
171         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
172         [o_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
173         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
174         [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
175         [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
176         [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
177         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
178         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist"]];
179         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed"]];
180         o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
181         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
182         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all"] retain];
183         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
184         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
185         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
186         o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
187         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
188         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
189         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
190         [o_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle"] right: [NSImage imageNamed:@"progression-track-wrapper-right"]];
191         [o_detached_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle"] right: [NSImage imageNamed:@"progression-track-wrapper-right"]];
192         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
193         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
194         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
195         if (b_nativeFullscreenMode)
196         {
197             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
198             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue"]];
199         }
200         else
201         {
202             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
203             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
204         }
205         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
206         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
207         [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
208         [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
209         [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
210         [o_detached_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
211     }
212     else
213     {
214         [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
215         [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
216         [o_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
217         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
218         [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
219         [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
220         o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
221         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
222         o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
223         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
224         [o_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
225         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
226         [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
227         [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
228         [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
229         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
230         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
231         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
232         o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
233         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
234         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
235         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
236         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
237         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
238         o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
239         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
240         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
241         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
242         [o_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left_dark"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle_dark"] right: [NSImage imageNamed:@"progression-track-wrapper-right_dark"]];
243         [o_detached_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left_dark"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle_dark"] right: [NSImage imageNamed:@"progression-track-wrapper-right_dark"]];
244         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
245         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
246         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
247         if (b_nativeFullscreenMode)
248         {
249             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
250             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue_dark"]];
251         }
252         else
253         {
254             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
255             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
256         }
257         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
258         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
259         [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button_dark"]];
260         [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
261         [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progressbar-fill-left_dark"] middle:[NSImage imageNamed:@"progressbar-fill-middle_dark"] right:[NSImage imageNamed:@"progressbar-fill-right_dark"]];
262         [o_detached_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progressbar-fill-left_dark"] middle:[NSImage imageNamed:@"progressbar-fill-middle_dark"] right:[NSImage imageNamed:@"progressbar-fill-right_dark"]];
263     }
264     [o_repeat_btn setImage: o_repeat_img];
265     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
266     [o_shuffle_btn setImage: o_shuffle_img];
267     [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
268     [o_play_btn setImage: o_play_img];
269     [o_play_btn setAlternateImage: o_play_pressed_img];
270     [o_detached_play_btn setImage: o_play_img];
271     [o_detached_play_btn setAlternateImage: o_play_pressed_img];
272     BOOL b_mute = ![[VLCCoreInteraction sharedInstance] isMuted];
273     [o_volume_sld setEnabled: b_mute];
274     [o_volume_up_btn setEnabled: b_mute];
275
276     /* interface builder action */
277     float f_threshold_height = f_min_video_height + [o_bottombar_view frame].size.height;
278     if( b_dark_interface )
279         f_threshold_height += [o_titlebar_view frame].size.height;
280     if( [[self contentView] frame].size.height < f_threshold_height )
281         b_splitviewShouldBeHidden = YES;
282
283     [self setDelegate: self];
284     [self setExcludedFromWindowsMenu: YES];
285     [self setAcceptsMouseMovedEvents: YES];
286     // Set that here as IB seems to be buggy
287     if (b_dark_interface)
288     {
289         [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
290         [o_detached_video_window setContentMinSize: NSMakeSize( 363., f_min_video_height + [o_detached_bottombar_view frame].size.height + [o_detached_titlebar_view frame].size.height )];
291     }
292     else
293     {
294         [self setContentMinSize:NSMakeSize(604., 288.)];
295         [o_detached_video_window setContentMinSize: NSMakeSize( 363., f_min_video_height + [o_detached_bottombar_view frame].size.height )];
296     }
297
298     [self setTitle: _NS("VLC media player")];
299     [o_time_fld setAlignment: NSCenterTextAlignment];
300     [o_time_fld setNeedsDisplay:YES];
301     b_dropzone_active = YES;
302     o_temp_view = [[NSView alloc] init];
303     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
304     [o_dropzone_view setFrame: [o_playlist_table frame]];
305     [o_left_split_view setFrame: [o_sidebar_view frame]];
306     if (b_nativeFullscreenMode)
307     {
308         NSRect frame;
309         [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
310         float f_width = [o_fullscreen_btn frame].size.width;
311
312         #define moveItem( item ) \
313         frame = [item frame]; \
314         frame.origin.x = f_width + frame.origin.x; \
315         [item setFrame: frame]
316
317         moveItem( o_effects_btn );
318         moveItem( o_volume_up_btn );
319         moveItem( o_volume_sld );
320         moveItem( o_volume_track_view );
321         moveItem( o_volume_down_btn );
322         moveItem( o_time_fld );
323         #undef moveItem
324
325         #define enlargeItem( item ) \
326         frame = [item frame]; \
327         frame.size.width = f_width + frame.size.width; \
328         [item setFrame: frame]
329
330         enlargeItem( o_time_sld );
331         enlargeItem( o_progress_bar );
332         enlargeItem( o_time_sld_background );
333         enlargeItem( o_time_sld_fancygradient_view );
334         #undef enlargeItem
335
336         [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
337     }
338     else
339     {
340         [o_titlebar_view setFullscreenButtonHidden: YES];
341         [o_detached_titlebar_view setFullscreenButtonHidden: YES];
342     }
343
344     if (OSX_LION)
345     {
346         /* the default small size of the search field is slightly different on Lion, let's work-around that */
347         NSRect frame;
348         frame = [o_search_fld frame];
349         frame.origin.y = frame.origin.y + 2.0;
350         frame.size.height = frame.size.height - 1.0;
351         [o_search_fld setFrame: frame];
352     }
353
354     /* create the sidebar */
355     o_sidebaritems = [[NSMutableArray alloc] init];
356     SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
357     SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
358     [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
359     SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
360     [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
361     SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
362     SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
363     SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
364     SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
365
366     /* SD subnodes, inspired by the Qt4 intf */
367     char **ppsz_longnames;
368     int *p_categories;
369     char **ppsz_names = vlc_sd_GetNames( pl_Get( VLCIntf ), &ppsz_longnames, &p_categories );
370     if (!ppsz_names)
371         msg_Err( VLCIntf, "no sd item found" ); //TODO
372     char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
373     int *p_category = p_categories;
374     NSMutableArray *internetItems = [[NSMutableArray alloc] init];
375     NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
376     NSMutableArray *lanItems = [[NSMutableArray alloc] init];
377     NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
378     NSString *o_identifier;
379     for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++)
380     {
381         o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
382         switch (*p_category) {
383             case SD_CAT_INTERNET:
384                 {
385                     [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
386                     if (!strncmp( *ppsz_name, "podcast", 7 ))
387                         [internetItems removeLastObject]; // we don't support podcasts at this point (see #6017)
388 //                        [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
389                     else
390                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
391                     [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
392                     [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
393                 }
394                 break;
395             case SD_CAT_DEVICES:
396                 {
397                     [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
398                     [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
399                     [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
400                     [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
401                 }
402                 break;
403             case SD_CAT_LAN:
404                 {
405                     [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
406                     [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
407                     [[lanItems lastObject] setSdtype: SD_CAT_LAN];
408                     [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
409                 }
410                 break;
411             case SD_CAT_MYCOMPUTER:
412                 {
413                     [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
414                     if (!strncmp( *ppsz_name, "video_dir", 9 ))
415                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
416                     else if (!strncmp( *ppsz_name, "audio_dir", 9 ))
417                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
418                     else if (!strncmp( *ppsz_name, "picture_dir", 11 ))
419                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
420                     else
421                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
422                     [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
423                     [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
424                 }
425                 break;
426             default:
427                 msg_Warn( VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name );
428                 break;
429         }
430
431         free( *ppsz_name );
432         free( *ppsz_longname );
433     }
434     [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
435     [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
436     [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
437     [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
438     [mycompItems release];
439     [devicesItems release];
440     [lanItems release];
441     [internetItems release];
442     free( ppsz_names );
443     free( ppsz_longnames );
444     free( p_categories );
445
446     [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
447     [o_sidebaritems addObject: libraryItem];
448     if ([mycompItem hasChildren])
449         [o_sidebaritems addObject: mycompItem];
450     if ([devicesItem hasChildren])
451         [o_sidebaritems addObject: devicesItem];
452     if ([lanItem hasChildren])
453         [o_sidebaritems addObject: lanItem];
454     if ([internetItem hasChildren])
455         [o_sidebaritems addObject: internetItem];
456
457     [o_sidebar_view reloadData];
458     [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
459     [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
460     [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
461
462     [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
463     [(PXSourceList *)o_sidebar_view setDataSource:self];
464     [o_sidebar_view setDelegate:self];
465     [o_sidebar_view setAutosaveExpandedItems:YES];
466
467     [o_sidebar_view expandItem: libraryItem expandChildren: YES];
468
469     /* make sure we display the desired default appearance when VLC launches for the first time */
470     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
471     if (![defaults objectForKey:@"VLCFirstRun"])
472     {
473         [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
474
475         NSUInteger i_sidebaritem_count = [o_sidebaritems count];
476         for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
477             [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
478     }
479
480     if( b_dark_interface )
481     {
482         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
483         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
484
485         [self setBackgroundColor: [NSColor clearColor]];
486         [self setOpaque: NO];
487         [self display];
488         [self setHasShadow:NO];
489         [self setHasShadow:YES];
490
491         NSRect winrect = [self frame];
492         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
493
494         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
495                                               winrect.size.width, f_titleBarHeight )];
496         [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
497
498         if (winrect.size.height > 100)
499         {
500             [self setFrame: winrect display:YES animate:YES];
501             previousSavedFrame = winrect;
502         }
503
504         winrect = [o_split_view frame];
505         winrect.size.height = winrect.size.height - f_titleBarHeight;
506         [o_split_view setFrame: winrect];
507         [o_video_view setFrame: winrect];
508
509         /* detached video window */
510         winrect = [o_detached_video_window frame];
511         [o_detached_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight )];
512         [[o_detached_video_window contentView] addSubview: o_detached_titlebar_view positioned: NSWindowAbove relativeTo: nil];
513
514         o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
515         [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
516         [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
517     }
518     else
519     {
520         NSRect frame;
521         frame = [o_time_sld_fancygradient_view frame];
522         frame.size.height = frame.size.height - 1;
523         frame.origin.y = frame.origin.y + 1;
524         [o_time_sld_fancygradient_view setFrame: frame];
525
526         frame = [o_detached_time_sld_fancygradient_view frame];
527         frame.size.height = frame.size.height - 1;
528         frame.origin.y = frame.origin.y + 1;
529         [o_detached_time_sld_fancygradient_view setFrame: frame];
530
531         [o_video_view setFrame: [o_split_view frame]];
532         [o_playlist_table setBorderType: NSNoBorder];
533         [o_sidebar_scrollview setBorderType: NSNoBorder];
534     }
535
536     NSRect frame;
537     frame = [o_time_sld_fancygradient_view frame];
538     frame.size.width = 0;
539     [o_time_sld_fancygradient_view setFrame: frame];
540
541     frame = [o_detached_time_sld_fancygradient_view frame];
542     frame.size.width = 0;
543     [o_detached_time_sld_fancygradient_view setFrame: frame];
544
545     if (OSX_LION)
546     {
547         [o_resize_view setImage: NULL];
548         [o_detached_resize_view setImage: NULL];
549     }
550
551     if ([self styleMask] & NSResizableWindowMask)
552     {
553         [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
554         [o_detached_resize_view removeFromSuperviewWithoutNeedingDisplay];
555     }
556
557     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
558     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
559     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
560     [[VLCMain sharedInstance] playbackModeUpdated];
561
562     [o_split_view setAutosaveName:@"10thanniversary-splitview"];
563     if (b_splitviewShouldBeHidden)
564     {
565         [self hideSplitView];
566         i_lastSplitViewHeight = 300;
567     }
568 }
569
570 #pragma mark -
571 #pragma mark Button Actions
572
573 - (IBAction)play:(id)sender
574 {
575     [[VLCCoreInteraction sharedInstance] play];
576 }
577
578 - (void)resetPreviousButton
579 {
580     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
581         // seems like no further event occurred, so let's switch the playback item
582         [[VLCCoreInteraction sharedInstance] previous];
583         just_triggered_previous = NO;
584     }
585 }
586
587 - (void)resetBackwardSkip
588 {
589     // the user stopped skipping, so let's allow him to change the item
590     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
591         just_triggered_previous = NO;
592 }
593
594 - (IBAction)bwd:(id)sender
595 {
596     if(!just_triggered_previous)
597     {
598         just_triggered_previous = YES;
599         [self performSelector:@selector(resetPreviousButton)
600                    withObject: NULL
601                    afterDelay:0.40];
602     }
603     else
604     {
605         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
606         {
607             // we just skipped 4 "continous" events, otherwise we are too fast
608             [[VLCCoreInteraction sharedInstance] backwardExtraShort];
609             last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
610             [self performSelector:@selector(resetBackwardSkip)
611                        withObject: NULL
612                        afterDelay:0.40];
613         }
614     }
615 }
616
617 - (void)resetNextButton
618 {
619     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
620         // seems like no further event occurred, so let's switch the playback item
621         [[VLCCoreInteraction sharedInstance] next];
622         just_triggered_next = NO;
623     }
624 }
625
626 - (void)resetForwardSkip
627 {
628     // the user stopped skipping, so let's allow him to change the item
629     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
630         just_triggered_next = NO;
631 }
632
633 - (IBAction)fwd:(id)sender
634 {
635    if(!just_triggered_next)
636     {
637         just_triggered_next = YES;
638         [self performSelector:@selector(resetNextButton)
639                    withObject: NULL
640                    afterDelay:0.40];
641     }
642     else
643     {
644         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
645         {
646             // we just skipped 4 "continous" events, otherwise we are too fast
647             [[VLCCoreInteraction sharedInstance] forwardExtraShort];
648             last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
649             [self performSelector:@selector(resetForwardSkip)
650                        withObject: NULL
651                        afterDelay:0.40];
652         }
653     }
654 }
655
656 - (IBAction)stop:(id)sender
657 {
658     [[VLCCoreInteraction sharedInstance] stop];
659 }
660
661 - (void)resizePlaylistAfterCollapse
662 {
663     NSRect plrect;
664     plrect = [[o_playlist_table animator] frame];
665     plrect.size.height = i_lastSplitViewHeight - 19.0; // actual pl top bar height, which differs from its frame
666     [[o_playlist_table animator] setFrame: plrect];
667
668     NSRect rightSplitRect;
669     rightSplitRect = [o_right_split_view frame];
670     plrect = [[o_dropzone_box animator] frame];
671     plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
672     plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
673     [[o_dropzone_box animator] setFrame: plrect];
674 }
675
676 - (void)makeSplitViewVisible
677 {
678     if( b_dark_interface )
679         [self setContentMinSize: NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
680     else
681         [self setContentMinSize: NSMakeSize( 604., 288. )];
682
683     NSRect old_frame = [self frame];
684     float newHeight = [self minSize].height;
685     if( old_frame.size.height < newHeight )
686     {
687         NSRect new_frame = old_frame;
688         new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
689         new_frame.size.height = newHeight;
690
691         [[self animator] setFrame: new_frame display: YES animate: YES];
692     }
693
694     [o_video_view setHidden: YES];
695     [o_split_view setHidden: NO];
696     [self makeFirstResponder: nil];
697
698 }
699
700 - (void)makeSplitViewHidden
701 {
702     if( b_dark_interface )
703         [self setContentMinSize: NSMakeSize( 604., f_min_video_height + [o_titlebar_view frame].size.height )];
704     else
705         [self setContentMinSize: NSMakeSize( 604., f_min_video_height )];
706
707     [o_split_view setHidden: YES];
708     [o_video_view setHidden: NO];
709     
710     if( [[o_video_view subviews] count] > 0 )
711         [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
712 }
713
714 - (IBAction)togglePlaylist:(id)sender
715 {
716     if (![self isVisible] && sender != nil)
717     {
718         [self makeKeyAndOrderFront: sender];
719         return;
720     }
721
722     BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
723     BOOL b_restored = NO;
724
725     // TODO: implement toggle playlist in this situation (triggerd via menu item).
726     // but for now we block this case, to avoid displaying only the half
727     if( b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil )
728         return;
729
730     if (b_dropzone_active && !b_activeVideo && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0)
731     {
732         b_dropzone_active = NO;
733         [self hideDropZone];
734         return;
735     }
736
737     if ( !(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ( (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
738                                   || (b_nonembedded && sender != nil)
739                                   || (!b_activeVideo && sender != nil)
740                                   || b_minimized_view ) )
741     {
742         [self hideSplitView];
743     }
744     else
745     {
746         if (b_splitview_removed)
747         {
748             if( !b_nonembedded || ( sender != nil && b_nonembedded))
749                 [self showSplitView];
750
751             if (sender == nil)
752                 b_minimized_view = YES;
753             else
754                 b_minimized_view = NO;
755
756             if (b_activeVideo)
757                 b_restored = YES;
758         }
759
760         if (!b_nonembedded)
761         {
762             if (([o_video_view isHidden] && b_activeVideo) || b_restored )
763                 [self makeSplitViewHidden];
764             else
765                 [self makeSplitViewVisible];
766         }
767         else
768         {
769             [o_split_view setHidden: NO];
770             [o_playlist_table setHidden: NO];
771             [o_video_view setHidden: !b_activeVideo];
772             if( b_activeVideo && [[o_video_view subviews] count] > 0 )
773                 [o_detached_video_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
774         }
775     }
776 }
777
778 - (void)setRepeatOne
779 {
780     [o_repeat_btn setImage: o_repeat_one_img];
781     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];   
782 }
783
784 - (void)setRepeatAll
785 {
786     [o_repeat_btn setImage: o_repeat_all_img];
787     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
788 }
789
790 - (void)setRepeatOff
791 {
792     [o_repeat_btn setImage: o_repeat_img];
793     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
794 }
795
796 - (IBAction)repeat:(id)sender
797 {
798     vlc_value_t looping,repeating;
799     intf_thread_t * p_intf = VLCIntf;
800     playlist_t * p_playlist = pl_Get( p_intf );
801
802     var_Get( p_playlist, "repeat", &repeating );
803     var_Get( p_playlist, "loop", &looping );
804
805     if( !repeating.b_bool && !looping.b_bool )
806     {
807         /* was: no repeating at all, switching to Repeat One */
808         [[VLCCoreInteraction sharedInstance] repeatOne];
809         [self setRepeatOne];
810     }
811     else if( repeating.b_bool && !looping.b_bool )
812     {
813         /* was: Repeat One, switching to Repeat All */
814         [[VLCCoreInteraction sharedInstance] repeatAll];
815         [self setRepeatAll];
816     }
817     else
818     {
819         /* was: Repeat All or bug in VLC, switching to Repeat Off */
820         [[VLCCoreInteraction sharedInstance] repeatOff];
821         [self setRepeatOff];
822     }
823 }
824
825 - (void)setShuffle
826 {
827     bool b_value;
828     playlist_t *p_playlist = pl_Get( VLCIntf );
829     b_value = var_GetBool( p_playlist, "random" );
830
831     if(b_value) {
832         [o_shuffle_btn setImage: o_shuffle_on_img];
833         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
834     }
835     else
836     {
837         [o_shuffle_btn setImage: o_shuffle_img];
838         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
839     }
840 }
841
842 - (IBAction)shuffle:(id)sender
843 {
844     [[VLCCoreInteraction sharedInstance] shuffle];
845     [self setShuffle];
846 }
847
848 - (IBAction)timeSliderAction:(id)sender
849 {
850     float f_updated;
851     input_thread_t * p_input;
852
853     switch( [[NSApp currentEvent] type] )
854     {
855         case NSLeftMouseUp:
856         case NSLeftMouseDown:
857         case NSLeftMouseDragged:
858             f_updated = [sender floatValue];
859             break;
860
861         default:
862             return;
863     }
864     p_input = pl_CurrentInput( VLCIntf );
865     if( p_input != NULL )
866     {
867         vlc_value_t time;
868         vlc_value_t pos;
869         NSString * o_time;
870         char psz_time[MSTRTIME_MAX_SIZE];
871
872         pos.f_float = f_updated / 10000.;
873         var_Set( p_input, "position", pos );
874         [o_time_sld setFloatValue: f_updated];
875
876         var_Get( p_input, "time", &time );
877
878         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
879         if( [o_time_fld timeRemaining] && dur != -1 )
880         {
881             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000) )];
882         }
883         else
884             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
885
886         [o_time_fld setStringValue: o_time];
887         [o_fspanel setStreamPos: f_updated andTime: o_time];
888         vlc_object_release( p_input );
889     }
890 }
891
892 - (IBAction)volumeAction:(id)sender
893 {
894     if (sender == o_volume_sld)
895         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
896     else if (sender == o_volume_down_btn)
897     {
898         [[VLCCoreInteraction sharedInstance] mute];
899     }
900     else
901         [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
902 }
903
904 - (IBAction)effects:(id)sender
905 {
906     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
907 }
908
909 - (IBAction)fullscreen:(id)sender
910 {
911     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
912 }
913
914 - (IBAction)dropzoneButtonAction:(id)sender
915 {
916     [[[VLCMain sharedInstance] open] openFileGeneric];
917 }
918
919 #pragma mark -
920 #pragma mark overwritten default functionality
921 - (BOOL)canBecomeKeyWindow
922 {
923     return YES;
924 }
925
926 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
927 {
928     SEL s_menuAction = [menuItem action];
929
930     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
931             return YES;
932
933     return [super validateMenuItem:menuItem];
934 }
935
936 - (void)setTitle:(NSString *)title
937 {
938     if (b_dark_interface)
939     {
940         [o_titlebar_view setWindowTitle: title];
941         [o_detached_titlebar_view setWindowTitle: title];
942     }
943     if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
944         [o_detached_video_window setTitle: title];
945     [super setTitle: title];
946 }
947
948 - (void)performClose:(id)sender
949 {
950     NSWindow *o_key_window = [NSApp keyWindow];
951
952     if (b_dark_interface)
953     {
954         [o_key_window orderOut: sender];
955         if ( [[VLCMain sharedInstance] activeVideoPlayback] && ( !b_nonembedded || o_key_window != self ))
956             [[VLCCoreInteraction sharedInstance] stop];
957     }
958     else
959     {
960         if( b_nonembedded && o_key_window != self )
961             [o_detached_video_window performClose: sender];
962         else
963             [super performClose: sender];
964     }
965 }
966
967 - (void)performMiniaturize:(id)sender
968 {
969     if (b_dark_interface)
970         [self miniaturize: sender];
971     else
972         [super performMiniaturize: sender];
973 }
974
975 - (void)performZoom:(id)sender
976 {
977     if (b_dark_interface)
978         [self customZoom: sender];
979     else
980         [super performZoom: sender];
981 }
982
983 - (void)zoom:(id)sender
984 {
985     if (b_dark_interface)
986         [self customZoom: sender];
987     else
988         [super zoom: sender];
989 }
990
991 /**
992  * Given a proposed frame rectangle, return a modified version
993  * which will fit inside the screen.
994  *
995  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
996  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
997  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
998  *    Copyright (C) 1996 Free Software Foundation, Inc.
999  */
1000 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
1001 {
1002     NSRect screenRect = [screen visibleFrame];
1003     float difference;
1004
1005     /* Move top edge of the window inside the screen */
1006     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
1007     if (difference > 0)
1008     {
1009         frameRect.origin.y -= difference;
1010     }
1011
1012     /* If the window is resizable, resize it (if needed) so that the
1013      bottom edge is on the screen or can be on the screen when the user moves
1014      the window */
1015     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
1016     if (_styleMask & NSResizableWindowMask)
1017     {
1018         float difference2;
1019
1020         difference2 = screenRect.origin.y - frameRect.origin.y;
1021         difference2 -= difference;
1022         // Take in account the space between the top of window and the top of the 
1023         // screen which can be used to move the bottom of the window on the screen
1024         if (difference2 > 0)
1025         {
1026             frameRect.size.height -= difference2;
1027             frameRect.origin.y += difference2;
1028         }
1029
1030         /* Ensure that resizing doesn't makewindow smaller than minimum */
1031         difference2 = [self minSize].height - frameRect.size.height;
1032         if (difference2 > 0)
1033         {
1034             frameRect.size.height += difference2;
1035             frameRect.origin.y -= difference2;
1036         }
1037     }
1038
1039     return frameRect;
1040 }
1041
1042 #define DIST 3
1043
1044 /**
1045  Zooms the receiver.   This method calls the delegate method
1046  windowShouldZoom:toFrame: to determine if the window should
1047  be allowed to zoom to full screen.
1048  *
1049  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1050  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
1051  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1052  *    Copyright (C) 1996 Free Software Foundation, Inc.
1053  */
1054 - (void)customZoom:(id)sender
1055 {
1056     NSRect maxRect = [[self screen] visibleFrame];
1057     NSRect currentFrame = [self frame];
1058
1059     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
1060     {
1061         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
1062     }
1063
1064     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
1065
1066     // Compare the new frame with the current one
1067     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
1068         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
1069         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
1070         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
1071     {
1072         // Already in zoomed mode, reset user frame, if stored
1073         if ([self frameAutosaveName] != nil)
1074         {
1075             [self setFrame: previousSavedFrame display: YES animate: YES];
1076             [self saveFrameUsingName: [self frameAutosaveName]];
1077         }
1078         return;
1079     }
1080
1081     if ([self frameAutosaveName] != nil)
1082     {
1083         [self saveFrameUsingName: [self frameAutosaveName]];
1084         previousSavedFrame = [self frame];
1085     }
1086
1087     [self setFrame: maxRect display: YES animate: YES];
1088 }
1089
1090 - (void)windowResizedOrMoved:(NSNotification *)notification
1091 {
1092     [self saveFrameUsingName: [self frameAutosaveName]];
1093 }
1094
1095 - (void)applicationWillTerminate:(NSNotification *)notification
1096 {
1097     if( config_GetInt( VLCIntf, "macosx-autosave-volume" ))
1098         config_PutInt( VLCIntf->p_libvlc, "volume", i_lastShownVolume );
1099
1100     [self saveFrameUsingName: [self frameAutosaveName]];
1101 }
1102
1103 - (void)someWindowWillClose:(NSNotification *)notification
1104 {
1105     if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1106     {
1107         if ([[VLCMain sharedInstance] activeVideoPlayback])
1108             [[VLCCoreInteraction sharedInstance] stop];
1109     }
1110 }
1111
1112 - (void)someWindowWillMiniaturize:(NSNotification *)notification
1113 {
1114     if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
1115     {
1116         if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1117         {
1118             if([[VLCMain sharedInstance] activeVideoPlayback])
1119                 [[VLCCoreInteraction sharedInstance] pause];
1120         }
1121     }
1122 }
1123
1124 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
1125 {
1126     id videoWindow = [o_video_view window];
1127     if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
1128         return proposedFrameSize;
1129
1130     // needed when entering lion fullscreen mode
1131     if( b_fullscreen )
1132         return proposedFrameSize;
1133     
1134     if( [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked] )
1135     {
1136         NSRect videoWindowFrame = [videoWindow frame];
1137         NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
1138         NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
1139         float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
1140         float marginx = contentRect.size.width - viewRect.size.width;
1141         if( b_dark_interface )
1142             marginy += [o_titlebar_view frame].size.height;
1143
1144         proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
1145     }
1146
1147     return proposedFrameSize;
1148 }
1149
1150 #pragma mark -
1151 #pragma mark Update interface and respond to foreign events
1152 - (void)showDropZone
1153 {
1154     b_dropzone_active = YES;
1155     [o_right_split_view addSubview: o_dropzone_view];
1156     [o_dropzone_view setFrame: [o_playlist_table frame]];
1157     [[o_playlist_table animator] setHidden:YES];
1158 }
1159
1160 - (void)hideDropZone
1161 {
1162     [o_dropzone_view removeFromSuperview];
1163     [[o_playlist_table animator] setHidden: NO];
1164 }
1165
1166 - (void)hideSplitView
1167 {
1168     NSRect winrect = [self frame];
1169     i_lastSplitViewHeight = [o_split_view frame].size.height;
1170     winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
1171     winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
1172     [self setFrame: winrect display: YES animate: YES];
1173     [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
1174     if (b_dark_interface)
1175     {
1176         [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1177         [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1178     }
1179     else
1180     {
1181         [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height )];
1182         [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height )];
1183     }
1184
1185     b_splitview_removed = YES;
1186 }
1187
1188 - (void)showSplitView
1189 {
1190     [self updateWindow];
1191     if (b_dark_interface)
1192         [self setContentMinSize:NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1193     else
1194         [self setContentMinSize:NSMakeSize( 604., 288. )];
1195     [self setContentMaxSize: NSMakeSize( FLT_MAX, FLT_MAX )];
1196
1197     NSRect winrect;
1198     winrect = [self frame];
1199     winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
1200     winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
1201     [self setFrame: winrect display: YES animate: YES];
1202
1203     [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
1204
1205     b_splitview_removed = NO;
1206 }
1207
1208 - (void)updateTimeSlider
1209 {
1210     input_thread_t * p_input;
1211     p_input = pl_CurrentInput( VLCIntf );
1212     if( p_input )
1213     {
1214         vlc_value_t time;
1215         NSString * o_time;
1216         vlc_value_t pos;
1217         char psz_time[MSTRTIME_MAX_SIZE];
1218         float f_updated;
1219
1220         var_Get( p_input, "position", &pos );
1221         f_updated = 10000. * pos.f_float;
1222         [o_time_sld setFloatValue: f_updated];
1223
1224         var_Get( p_input, "time", &time );
1225
1226         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1227         if( [o_time_fld timeRemaining] && dur > 0 )
1228         {
1229             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000))];
1230         }
1231         else
1232             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
1233
1234         if (dur == -1) {
1235             [o_time_sld setEnabled: NO];
1236             [o_time_sld setHidden: YES];
1237             [o_time_sld_fancygradient_view setHidden: YES];
1238         } else {
1239             [o_time_sld setEnabled: YES];
1240             [o_time_sld setHidden: NO];
1241             [o_time_sld_fancygradient_view setHidden: NO];
1242         }
1243
1244         [o_time_fld setStringValue: o_time];
1245         [o_time_fld setNeedsDisplay:YES];
1246         [o_fspanel setStreamPos: f_updated andTime: o_time];
1247         vlc_object_release( p_input );
1248     }
1249     else
1250     {
1251         [o_time_sld setFloatValue: 0.0];
1252         [o_time_fld setStringValue: @"00:00"];
1253         [o_time_sld setEnabled: NO];
1254         [o_time_sld setHidden: YES];
1255         [o_time_sld_fancygradient_view setHidden: YES];
1256         [o_detached_time_sld_fancygradient_view setHidden: YES];
1257     }
1258
1259     [o_detached_time_sld setFloatValue: [o_time_sld floatValue]];
1260     [o_detached_time_sld setEnabled: [o_time_sld isEnabled]];
1261     [o_detached_time_fld setStringValue: [o_time_fld stringValue]];
1262     [o_detached_time_sld setHidden: [o_time_sld isHidden]];
1263 }
1264
1265 - (void)updateVolumeSlider
1266 {
1267     audio_volume_t i_volume;
1268     playlist_t * p_playlist = pl_Get( VLCIntf );
1269
1270     i_volume = aout_VolumeGet( p_playlist );
1271     BOOL b_muted = [[VLCCoreInteraction sharedInstance] isMuted];
1272
1273     if( !b_muted )
1274     {
1275         i_lastShownVolume = i_volume;
1276         [o_volume_sld setIntValue: i_volume];
1277         [o_fspanel setVolumeLevel: i_volume];
1278     }
1279     else
1280         [o_volume_sld setIntValue: 0];
1281
1282     [o_volume_sld setEnabled: !b_muted];
1283     [o_volume_up_btn setEnabled: !b_muted];
1284 }
1285
1286 - (void)updateName
1287 {
1288     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1289     input_thread_t * p_input;
1290     p_input = pl_CurrentInput( VLCIntf );
1291     if( p_input )
1292     {
1293         NSString *aString;
1294         char *format = var_InheritString( VLCIntf, "input-title-format" );
1295         char *formated = str_format_meta( p_input, format );
1296         free( format );
1297         aString = [NSString stringWithUTF8String:formated];
1298         free( formated );
1299
1300         char *uri = input_item_GetURI( input_GetItem( p_input ) );
1301
1302         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1303         if ([o_url isFileURL])
1304             [self setRepresentedURL: o_url];
1305         else
1306             [self setRepresentedURL: nil];
1307         free( uri );
1308
1309         if ([aString isEqualToString:@""])
1310         {
1311             if ([o_url isFileURL])
1312                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1313             else
1314                 aString = [o_url absoluteString];
1315         }
1316
1317         [self setTitle: aString];
1318         [o_fspanel setStreamTitle: aString];
1319         vlc_object_release( p_input );
1320     }
1321     else
1322     {
1323         [self setTitle: _NS("VLC media player")];
1324         [self setRepresentedURL: nil];
1325     }
1326
1327     [o_pool release];
1328 }
1329
1330 - (void)updateWindow
1331 {
1332     bool b_input = false;
1333     bool b_plmul = false;
1334     bool b_control = false;
1335     bool b_seekable = false;
1336     bool b_chapters = false;
1337
1338     playlist_t * p_playlist = pl_Get( VLCIntf );
1339
1340     PL_LOCK;
1341     b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1342     PL_UNLOCK;
1343
1344     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1345
1346     bool b_buffering = NO;
1347
1348     if( ( b_input = ( p_input != NULL ) ) )
1349     {
1350         /* seekable streams */
1351         cachedInputState = input_GetState( p_input );
1352         if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1353             b_buffering = YES;
1354
1355         /* seekable streams */
1356         b_seekable = var_GetBool( p_input, "can-seek" );
1357
1358         /* check whether slow/fast motion is possible */
1359         b_control = var_GetBool( p_input, "can-rate" );
1360
1361         /* chapters & titles */
1362         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1363
1364         if (( cachedInputState == PLAYING_S || b_buffering == YES ) && [[VLCMain sharedInstance] activeVideoPlayback] )
1365             [[o_video_view window] makeKeyAndOrderFront: nil];
1366
1367         vlc_object_release( p_input );
1368     }
1369
1370     if( b_buffering )
1371     {
1372         [o_progress_bar startAnimation:self];
1373         [o_progress_bar setIndeterminate:YES];
1374         [o_progress_bar setHidden:NO];
1375     } else {
1376         [o_progress_bar stopAnimation:self];
1377         [o_progress_bar setHidden:YES];
1378     }
1379
1380     [o_stop_btn setEnabled: b_input];
1381     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1382     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1383     [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1384     [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1385     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1386
1387     [o_time_sld setEnabled: b_seekable];
1388     [self updateTimeSlider];
1389     [o_fspanel setSeekable: b_seekable];
1390
1391     PL_LOCK;
1392     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1393         [self hideDropZone];
1394     else
1395         [self showDropZone];
1396     PL_UNLOCK;
1397     [o_sidebar_view setNeedsDisplay:YES];
1398 }
1399
1400 - (void)setPause
1401 {
1402     [o_play_btn setImage: o_pause_img];
1403     [o_play_btn setAlternateImage: o_pause_pressed_img];
1404     [o_play_btn setToolTip: _NS("Pause")];
1405     [o_detached_play_btn setImage: o_pause_img];
1406     [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1407     [o_detached_play_btn setToolTip: _NS("Pause")];
1408     [o_fspanel setPause];
1409 }
1410
1411 - (void)setPlay
1412 {
1413     [o_play_btn setImage: o_play_img];
1414     [o_play_btn setAlternateImage: o_play_pressed_img];
1415     [o_play_btn setToolTip: _NS("Play")];
1416     [o_detached_play_btn setImage: o_play_img];
1417     [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1418     [o_detached_play_btn setToolTip: _NS("Play")];
1419     [o_fspanel setPlay];
1420 }
1421
1422 - (void)drawFancyGradientEffectForTimeSlider
1423 {
1424     if (OSX_LEOPARD)
1425         return;
1426
1427     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1428     CGFloat f_value = [o_time_sld knobPosition];
1429     if (f_value > 7.5)
1430     {
1431         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1432         if (f_value != oldFrame.size.width)
1433         {
1434             if ([o_time_sld_fancygradient_view isHidden])
1435                 [o_time_sld_fancygradient_view setHidden: NO];
1436             [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1437         }
1438
1439         if (b_nonembedded)
1440         {
1441             f_value = [o_detached_time_sld knobPosition];
1442             oldFrame = [o_detached_time_sld_fancygradient_view frame];
1443             if (f_value != oldFrame.size.width)
1444             {
1445                 if ([o_detached_time_sld_fancygradient_view isHidden])
1446                     [o_detached_time_sld_fancygradient_view setHidden: NO];
1447                 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1448             }
1449         }
1450     }
1451     else
1452     {
1453         NSRect frame;
1454         frame = [o_time_sld_fancygradient_view frame];
1455         if (frame.size.width > 0)
1456         {
1457             frame.size.width = 0;
1458             [o_time_sld_fancygradient_view setFrame: frame];
1459
1460             frame = [o_detached_time_sld_fancygradient_view frame];
1461             frame.size.width = 0;
1462             [o_detached_time_sld_fancygradient_view setFrame: frame];
1463         }
1464         [o_time_sld_fancygradient_view setHidden: YES];
1465         [o_detached_time_sld_fancygradient_view setHidden: YES];
1466     }
1467     [o_pool release];
1468 }
1469
1470 #pragma mark -
1471 #pragma mark Video Output handling
1472 - (id)videoView
1473 {
1474     return o_video_view;
1475 }
1476
1477 - (id)setupVideoView
1478 {
1479     vout_thread_t *p_vout = getVout();
1480     if (config_GetInt( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode)
1481     {
1482         if ([o_video_view window] != self)
1483         {
1484             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1485             [o_video_view setFrame: [o_split_view frame]];
1486             [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1487         }
1488         b_nonembedded = NO;
1489     }
1490     else
1491     {
1492         if ([o_video_view superview] != NULL)
1493             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1494
1495         NSRect frame = [o_detached_video_window frame];
1496         NSRect videoFrame = [o_video_view frame];
1497         frame.size.width = videoFrame.size.width;
1498         frame.size.height = videoFrame.size.height + [o_detached_bottombar_view frame].size.height + [o_titlebar_view frame].size.height;
1499         [o_detached_video_window setFrame: frame display: NO];
1500         [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1501         [o_detached_video_window setLevel:NSNormalWindowLevel];
1502         [o_detached_video_window useOptimizedDrawing: YES];
1503         [o_detached_video_window center];
1504         b_nonembedded = YES;
1505     }
1506     [[o_video_view window] makeKeyAndOrderFront: self];
1507     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
1508
1509     if (p_vout)
1510     {
1511         if( var_GetBool( p_vout, "video-on-top" ) )
1512             [[o_video_view window] setLevel: NSStatusWindowLevel];
1513         else
1514             [[o_video_view window] setLevel: NSNormalWindowLevel];
1515         vlc_object_release( p_vout );
1516     }
1517     return o_video_view;
1518 }
1519
1520 - (void)setVideoplayEnabled
1521 {
1522     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1523
1524     if( b_videoPlayback )
1525     {
1526         // look for 'start at fullscreen'
1527         [[VLCMain sharedInstance] fullscreenChanged];
1528     }
1529     else
1530     {
1531         [self makeFirstResponder: nil];
1532         [o_detached_video_window orderOut: nil];
1533
1534         // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
1535         [self setAlphaValue:1.0];
1536     }
1537
1538     if( b_nativeFullscreenMode )
1539     {
1540         if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1541             [o_bottombar_view setHidden: b_videoPlayback];
1542         else
1543             [o_bottombar_view setHidden: NO];
1544         if( b_videoPlayback && b_fullscreen )
1545             [o_fspanel setActive: nil];
1546         if( !b_videoPlayback )
1547             [o_fspanel setNonActive: nil];
1548     }
1549
1550     if (!b_videoPlayback && b_fullscreen)
1551     {
1552         if (!b_nativeFullscreenMode)
1553             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1554     }
1555 }
1556
1557 - (void)resizeWindow
1558 {
1559     if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
1560         return;
1561
1562     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1563     NSSize windowMinSize = [o_videoWindow minSize];
1564     NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
1565
1566     NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
1567     NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
1568
1569     unsigned int i_width = nativeVideoSize.width;
1570     unsigned int i_height = nativeVideoSize.height;
1571     if (i_width < windowMinSize.width)
1572         i_width = windowMinSize.width;
1573     if (i_height < f_min_video_height)
1574         i_height = f_min_video_height;
1575
1576     /* Calculate the window's new size */
1577     NSRect new_frame;
1578     new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
1579     new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
1580     new_frame.origin.x = topleftscreen.x;
1581     new_frame.origin.y = topleftscreen.y - new_frame.size.height;
1582
1583     /* make sure the window doesn't exceed the screen size the window is on */
1584     if( new_frame.size.width > screenFrame.size.width )
1585     {
1586         new_frame.size.width = screenFrame.size.width;
1587         new_frame.origin.x = screenFrame.origin.x;
1588     }
1589     if( new_frame.size.height > screenFrame.size.height )
1590     {
1591         new_frame.size.height = screenFrame.size.height;
1592         new_frame.origin.y = screenFrame.origin.y;
1593     }
1594     if( new_frame.origin.y < screenFrame.origin.y )
1595         new_frame.origin.y = screenFrame.origin.y;
1596
1597     [[o_videoWindow animator] setFrame:new_frame display:YES];
1598 }
1599
1600 - (void)setNativeVideoSize:(NSSize)size
1601 {
1602     nativeVideoSize = size;
1603
1604     if( config_GetInt( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen )
1605         [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
1606 }
1607
1608 //  Called automatically if window's acceptsMouseMovedEvents property is true
1609 - (void)mouseMoved:(NSEvent *)theEvent
1610 {
1611     if (b_fullscreen)
1612         [self recreateHideMouseTimer];
1613
1614     [super mouseMoved: theEvent];
1615 }
1616
1617 - (void)recreateHideMouseTimer
1618 {
1619     if (t_hide_mouse_timer != nil) {
1620         [t_hide_mouse_timer invalidate];
1621         [t_hide_mouse_timer release];
1622     }
1623
1624     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
1625                                                           target:self
1626                                                         selector:@selector(hideMouseCursor:)
1627                                                         userInfo:nil
1628                                                          repeats:NO];
1629     [t_hide_mouse_timer retain];
1630 }
1631
1632 //  NSTimer selectors require this function signature as per Apple's docs
1633 - (void)hideMouseCursor:(NSTimer *)timer
1634 {
1635     [NSCursor setHiddenUntilMouseMoves: YES];
1636 }
1637
1638 #pragma mark -
1639 #pragma mark Fullscreen support
1640 - (void)showFullscreenController
1641 {
1642      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
1643         [o_fspanel fadeIn];
1644 }
1645
1646 - (void)updateFullscreen
1647 {
1648     [[VLCMain sharedInstance] fullscreenChanged];
1649 }
1650
1651 - (BOOL)isFullscreen
1652 {
1653     return b_fullscreen;
1654 }
1655
1656 - (void)lockFullscreenAnimation
1657 {
1658     [o_animation_lock lock];
1659 }
1660
1661 - (void)unlockFullscreenAnimation
1662 {
1663     [o_animation_lock unlock];
1664 }
1665
1666 - (void)enterFullscreen
1667 {
1668     NSMutableDictionary *dict1, *dict2;
1669     NSScreen *screen;
1670     NSRect screen_rect;
1671     NSRect rect;
1672     vout_thread_t *p_vout = getVout();
1673     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1674     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1675
1676     if( p_vout )
1677         screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)config_GetInt( VLCIntf, "macosx-vdev" )];
1678
1679     [self lockFullscreenAnimation];
1680
1681     if (!screen)
1682     {
1683         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
1684         screen = [o_videoWindow screen];
1685     }
1686     if (!screen)
1687     {
1688         msg_Dbg( VLCIntf, "Using deepest screen" );
1689         screen = [NSScreen deepestScreen];
1690     }
1691
1692     if( p_vout )
1693         vlc_object_release( p_vout );
1694
1695     screen_rect = [screen frame];
1696
1697     [o_fullscreen_btn setState: YES];
1698     [o_detached_fullscreen_btn setState: YES];
1699
1700     [self recreateHideMouseTimer];
1701
1702     if( blackout_other_displays )
1703         [screen blackoutOtherScreens];
1704
1705     /* Make sure we don't see the window flashes in float-on-top mode */
1706     i_originalLevel = [o_videoWindow level];
1707     [o_videoWindow setLevel:NSNormalWindowLevel];
1708
1709     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
1710     if (!o_fullscreen_window)
1711     {
1712         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
1713
1714         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
1715         rect.origin.x += [o_videoWindow frame].origin.x;
1716         rect.origin.y += [o_videoWindow frame].origin.y;
1717         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
1718         [o_fullscreen_window setFullscreen: YES];
1719         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
1720         [o_fullscreen_window setCanBecomeKeyWindow: YES];
1721
1722         if (![o_videoWindow isVisible] || [o_videoWindow alphaValue] == 0.0)
1723         {
1724             /* We don't animate if we are not visible, instead we
1725              * simply fade the display */
1726             CGDisplayFadeReservationToken token;
1727
1728             if( blackout_other_displays )
1729             {
1730                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1731                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1732             }
1733
1734             if ([screen isMainScreen])
1735             {
1736                 if (OSX_LEOPARD)
1737                     SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1738                 else
1739                     [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1740             }
1741
1742             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1743             [o_temp_view setFrame:[o_video_view frame]];
1744             [o_fullscreen_window setContentView:o_video_view];
1745
1746             [o_fullscreen_window makeKeyAndOrderFront:self];
1747             [o_fullscreen_window orderFront:self animate:YES];
1748
1749             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
1750             [o_fullscreen_window setLevel:NSNormalWindowLevel];
1751
1752             if( blackout_other_displays )
1753             {
1754                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1755                 CGReleaseDisplayFadeReservation( token );
1756             }
1757
1758             /* Will release the lock */
1759             [self hasBecomeFullscreen];
1760
1761             return;
1762         }
1763
1764         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1765         NSDisableScreenUpdates();
1766         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1767         [o_temp_view setFrame:[o_video_view frame]];
1768         [o_fullscreen_window setContentView:o_video_view];
1769         [o_fullscreen_window makeKeyAndOrderFront:self];
1770         NSEnableScreenUpdates();
1771     }
1772
1773     /* We are in fullscreen (and no animation is running) */
1774     if (b_fullscreen)
1775     {
1776         /* Make sure we are hidden */
1777         [o_videoWindow orderOut: self];
1778
1779         [self unlockFullscreenAnimation];
1780         return;
1781     }
1782
1783     if (o_fullscreen_anim1)
1784     {
1785         [o_fullscreen_anim1 stopAnimation];
1786         [o_fullscreen_anim1 release];
1787     }
1788     if (o_fullscreen_anim2)
1789     {
1790         [o_fullscreen_anim2 stopAnimation];
1791         [o_fullscreen_anim2 release];
1792     }
1793
1794     if ([screen isMainScreen])
1795     {
1796         if (OSX_LEOPARD)
1797             SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1798         else
1799             [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1800     }
1801
1802     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
1803     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
1804
1805     [dict1 setObject:o_videoWindow forKey:NSViewAnimationTargetKey];
1806     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
1807
1808     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1809     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1810     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
1811
1812     /* Strategy with NSAnimation allocation:
1813      - Keep at most 2 animation at a time
1814      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
1815      */
1816     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1817     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1818
1819     [dict1 release];
1820     [dict2 release];
1821
1822     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1823     [o_fullscreen_anim1 setDuration: 0.3];
1824     [o_fullscreen_anim1 setFrameRate: 30];
1825     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1826     [o_fullscreen_anim2 setDuration: 0.2];
1827     [o_fullscreen_anim2 setFrameRate: 30];
1828
1829     [o_fullscreen_anim2 setDelegate: self];
1830     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1831
1832     [o_fullscreen_anim1 startAnimation];
1833     /* fullscreenAnimation will be unlocked when animation ends */
1834 }
1835
1836 - (void)hasBecomeFullscreen
1837 {
1838     if( [[o_video_view subviews] count] > 0 )
1839         [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1840
1841     [o_fullscreen_window makeKeyWindow];
1842     [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE];
1843
1844     /* tell the fspanel to move itself to front next time it's triggered */
1845     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
1846     [o_fspanel setActive: nil];
1847
1848     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1849     if( [o_videoWindow isVisible] )
1850         [o_videoWindow orderOut: self];
1851
1852     b_fullscreen = YES;
1853     [self unlockFullscreenAnimation];
1854 }
1855
1856 - (void)leaveFullscreen
1857 {
1858     [self leaveFullscreenAndFadeOut: NO];
1859 }
1860
1861 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1862 {
1863     NSMutableDictionary *dict1, *dict2;
1864     NSRect frame;
1865     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1866
1867     [self lockFullscreenAnimation];
1868
1869     [o_fullscreen_btn setState: NO];
1870     [o_detached_fullscreen_btn setState: NO];
1871
1872     /* We always try to do so */
1873     [NSScreen unblackoutScreens];
1874
1875     vout_thread_t *p_vout = getVout();
1876     if (p_vout)
1877     {
1878         if( var_GetBool( p_vout, "video-on-top" ) )
1879             [[o_video_view window] setLevel: NSStatusWindowLevel];
1880         else
1881             [[o_video_view window] setLevel: NSNormalWindowLevel];
1882         vlc_object_release( p_vout );
1883     }
1884     [[o_video_view window] makeKeyAndOrderFront: nil];
1885
1886     /* Don't do anything if o_fullscreen_window is already closed */
1887     if (!o_fullscreen_window)
1888     {
1889         [self unlockFullscreenAnimation];
1890         return;
1891     }
1892
1893     if (fadeout)
1894     {
1895         /* We don't animate if we are not visible, instead we
1896          * simply fade the display */
1897         CGDisplayFadeReservationToken token;
1898
1899         if( blackout_other_displays )
1900         {
1901             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1902             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1903         }
1904
1905         [o_fspanel setNonActive: nil];
1906         if (OSX_LEOPARD)
1907             SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1908         else
1909             [NSApp setPresentationOptions: NSApplicationPresentationDefault];
1910
1911         /* Will release the lock */
1912         [self hasEndedFullscreen];
1913
1914         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1915          * here */
1916         b_window_is_invisible = YES;
1917
1918         if( blackout_other_displays )
1919         {
1920             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1921             CGReleaseDisplayFadeReservation( token );
1922         }
1923
1924         return;
1925     }
1926
1927     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1928
1929     [o_videoWindow setAlphaValue: 0.0];
1930     [o_videoWindow orderFront: self];
1931     [[o_video_view window] orderFront: self];
1932
1933     [o_fspanel setNonActive: nil];
1934     if (OSX_LEOPARD)
1935         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1936     else
1937         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1938
1939     if (o_fullscreen_anim1)
1940     {
1941         [o_fullscreen_anim1 stopAnimation];
1942         [o_fullscreen_anim1 release];
1943     }
1944     if (o_fullscreen_anim2)
1945     {
1946         [o_fullscreen_anim2 stopAnimation];
1947         [o_fullscreen_anim2 release];
1948     }
1949
1950     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1951     frame.origin.x += [o_videoWindow frame].origin.x;
1952     frame.origin.y += [o_videoWindow frame].origin.y;
1953
1954     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1955     [dict2 setObject:o_videoWindow forKey:NSViewAnimationTargetKey];
1956     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1957
1958     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
1959     [dict2 release];
1960
1961     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1962     [o_fullscreen_anim2 setDuration: 0.3];
1963     [o_fullscreen_anim2 setFrameRate: 30];
1964
1965     [o_fullscreen_anim2 setDelegate: self];
1966
1967     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1968
1969     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1970     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1971     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1972
1973     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
1974     [dict1 release];
1975
1976     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1977     [o_fullscreen_anim1 setDuration: 0.2];
1978     [o_fullscreen_anim1 setFrameRate: 30];
1979     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1980
1981     /* Make sure o_fullscreen_window is the frontmost window */
1982     [o_fullscreen_window orderFront: self];
1983
1984     [o_fullscreen_anim1 startAnimation];
1985     /* fullscreenAnimation will be unlocked when animation ends */
1986 }
1987
1988 - (void)hasEndedFullscreen
1989 {
1990     b_fullscreen = NO;
1991
1992     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1993     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1994     NSDisableScreenUpdates();
1995     [o_video_view retain];
1996     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1997     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1998     [o_video_view release];
1999     [o_video_view setFrame:[o_temp_view frame]];
2000     if( [[o_video_view subviews] count] > 0 )
2001         [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2002     if( [[o_video_view window] isVisible] )
2003     {
2004         if( !b_nonembedded )
2005             [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
2006         else
2007             [[o_video_view window] makeKeyAndOrderFront: self];
2008     }
2009     [o_fullscreen_window orderOut: self];
2010     NSEnableScreenUpdates();
2011
2012     [o_fullscreen_window release];
2013     o_fullscreen_window = nil;
2014     [[o_video_view window] setLevel:i_originalLevel];
2015     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2016
2017     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2018     if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2019         [o_detached_video_window orderOut: self];
2020
2021     [self unlockFullscreenAnimation];
2022 }
2023
2024 - (void)animationDidEnd:(NSAnimation*)animation
2025 {
2026     NSArray *viewAnimations;
2027     if( o_makekey_anim == animation )
2028     {
2029         [o_makekey_anim release];
2030         return;
2031     }
2032     if ([animation currentValue] < 1.0)
2033         return;
2034
2035     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2036     viewAnimations = [o_fullscreen_anim2 viewAnimations];
2037     if ([viewAnimations count] >=1 &&
2038         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2039     {
2040         /* Fullscreen ended */
2041         [self hasEndedFullscreen];
2042     }
2043     else
2044     {
2045         /* Fullscreen started */
2046         [self hasBecomeFullscreen];
2047     }
2048 }
2049
2050 - (void)makeKeyAndOrderFront: (id)sender
2051 {
2052     /* Hack
2053      * when we exit fullscreen and fade out, we may endup in
2054      * having a window that is faded. We can't have it fade in unless we
2055      * animate again. */
2056
2057     if(!b_window_is_invisible)
2058     {
2059         /* Make sure we don't do it too much */
2060         [super makeKeyAndOrderFront: sender];
2061         return;
2062     }
2063
2064     [super setAlphaValue:0.0f];
2065     [super makeKeyAndOrderFront: sender];
2066
2067     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2068     [dict setObject:self forKey:NSViewAnimationTargetKey];
2069     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2070
2071     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2072     [dict release];
2073
2074     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2075     [o_makekey_anim setDuration: 0.1];
2076     [o_makekey_anim setFrameRate: 30];
2077     [o_makekey_anim setDelegate: self];
2078
2079     [o_makekey_anim startAnimation];
2080     b_window_is_invisible = NO;
2081
2082     /* fullscreenAnimation will be unlocked when animation ends */
2083 }
2084
2085 #pragma mark -
2086 #pragma mark Lion native fullscreen handling
2087 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2088 {
2089     // workaround, see #6668
2090     [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2091
2092     var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
2093     
2094     vout_thread_t *p_vout = getVout();
2095     if( p_vout )
2096     {
2097         var_SetBool( p_vout, "fullscreen", true );
2098         vlc_object_release( p_vout );
2099     }
2100     
2101     [o_video_view setFrame: [[self contentView] frame]];
2102     b_fullscreen = YES;
2103
2104     [self recreateHideMouseTimer];
2105     i_originalLevel = [self level];
2106     [self setLevel:NSNormalWindowLevel];
2107
2108     if (b_dark_interface)
2109     {
2110         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2111
2112         NSRect winrect;
2113         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2114         winrect = [self frame];
2115
2116         winrect.size.height = winrect.size.height - f_titleBarHeight;
2117         [self setFrame: winrect display:NO animate:NO];
2118         winrect = [o_split_view frame];
2119         winrect.size.height = winrect.size.height + f_titleBarHeight;
2120         [o_split_view setFrame: winrect];
2121     }
2122
2123     if ([[VLCMain sharedInstance] activeVideoPlayback])
2124         [o_bottombar_view setHidden: YES];
2125     
2126     [self setMovableByWindowBackground: NO];
2127 }
2128
2129 - (void)windowDidEnterFullScreen:(NSNotification *)notification
2130 {
2131     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2132     [o_fspanel setActive: nil];
2133 }
2134
2135 - (void)windowWillExitFullScreen:(NSNotification *)notification
2136 {
2137
2138     var_SetBool( pl_Get( VLCIntf ), "fullscreen", false );
2139     
2140     vout_thread_t *p_vout = getVout();
2141     if( p_vout )
2142     {
2143         var_SetBool( p_vout, "fullscreen", false );
2144         vlc_object_release( p_vout );
2145     }
2146
2147     [o_video_view setFrame: [o_split_view frame]];
2148     [NSCursor setHiddenUntilMouseMoves: NO];
2149     [o_fspanel setNonActive: nil];
2150     [self setLevel:i_originalLevel];
2151     b_fullscreen = NO;
2152
2153     if (b_dark_interface)
2154     {
2155         NSRect winrect;
2156         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2157         winrect = [self frame];
2158
2159         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2160                                               winrect.size.width, f_titleBarHeight )];
2161         [[self contentView] addSubview: o_titlebar_view];
2162
2163         winrect.size.height = winrect.size.height + f_titleBarHeight;
2164         [self setFrame: winrect display:NO animate:NO];
2165         winrect = [o_split_view frame];
2166         winrect.size.height = winrect.size.height - f_titleBarHeight;
2167         [o_split_view setFrame: winrect];
2168         [o_video_view setFrame: winrect];
2169     }
2170
2171     if ([[VLCMain sharedInstance] activeVideoPlayback])
2172         [o_bottombar_view setHidden: NO];
2173     
2174     [self setMovableByWindowBackground: YES];
2175 }
2176
2177 #pragma mark -
2178 #pragma mark split view delegate
2179 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2180 {
2181     if (dividerIndex == 0)
2182         return 300.;
2183     else
2184         return proposedMax;
2185 }
2186
2187 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2188 {
2189     return ([subview isEqual:o_left_split_view]);
2190 }
2191
2192 #pragma mark -
2193 #pragma mark Side Bar Data handling
2194 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2195 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2196 {
2197     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2198     if(item==nil)
2199         return [o_sidebaritems count];
2200     else
2201         return [[item children] count];
2202 }
2203
2204
2205 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2206 {
2207     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2208     if(item==nil)
2209         return [o_sidebaritems objectAtIndex:index];
2210     else
2211         return [[item children] objectAtIndex:index];
2212 }
2213
2214
2215 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2216 {
2217     return [item title];
2218 }
2219
2220 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2221 {
2222     [item setTitle:object];
2223 }
2224
2225 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2226 {
2227     return [item hasChildren];
2228 }
2229
2230
2231 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2232 {
2233     if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2234         return YES;
2235
2236     return [item hasBadge];
2237 }
2238
2239
2240 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2241 {
2242     playlist_t * p_playlist = pl_Get( VLCIntf );
2243     NSInteger i_playlist_size;
2244
2245     if ([[item identifier] isEqualToString: @"playlist"])
2246     {
2247         PL_LOCK;
2248         i_playlist_size = p_playlist->p_local_category->i_children;
2249         PL_UNLOCK;
2250
2251         return i_playlist_size;
2252     }
2253     if ([[item identifier] isEqualToString: @"medialibrary"])
2254     {
2255         PL_LOCK;
2256         i_playlist_size = p_playlist->p_ml_category->i_children;
2257         PL_UNLOCK;
2258
2259         return i_playlist_size;
2260     }
2261
2262     return [item badgeValue];
2263 }
2264
2265
2266 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2267 {
2268     return [item hasIcon];
2269 }
2270
2271
2272 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2273 {
2274     return [item icon];
2275 }
2276
2277 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2278 {
2279     if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2280     {
2281         if (item != nil)
2282         {
2283             NSMenu * m;
2284             if ([item sdtype] > 0)
2285             {
2286                 m = [[NSMenu alloc] init];
2287                 playlist_t * p_playlist = pl_Get( VLCIntf );
2288                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2289                 if (!sd_loaded)
2290                     [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2291                 else
2292                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2293                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2294             }
2295             return [m autorelease];
2296         }
2297     }
2298
2299     return nil;
2300 }
2301
2302 - (IBAction)sdmenuhandler:(id)sender
2303 {
2304     NSString * identifier = [sender representedObject];
2305     if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2306     {
2307         playlist_t * p_playlist = pl_Get( VLCIntf );
2308         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2309
2310         if (!sd_loaded)
2311             playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2312         else
2313             playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2314     }
2315 }
2316
2317 #pragma mark -
2318 #pragma mark Side Bar Delegate Methods
2319 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2320 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2321 {
2322     if ([[group identifier] isEqualToString:@"library"])
2323         return YES;
2324
2325     return NO;
2326 }
2327
2328 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2329 {
2330     playlist_t * p_playlist = pl_Get( VLCIntf );
2331
2332     NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2333     id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2334
2335
2336     //Set the label text to represent the new selection
2337     if ([item sdtype] > -1 && [[item identifier] length] > 0)
2338     {
2339         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2340         if (!sd_loaded)
2341         {
2342             playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2343         }
2344     }
2345
2346     [o_chosen_category_lbl setStringValue:[item title]];
2347
2348     if ([[item identifier] isEqualToString:@"playlist"])
2349     {
2350         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2351     }
2352     else if([[item identifier] isEqualToString:@"medialibrary"])
2353     {
2354         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2355     }
2356     else
2357     {
2358         playlist_item_t * pl_item;
2359         PL_LOCK;
2360         pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2361         PL_UNLOCK;
2362         [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2363     }
2364
2365     PL_LOCK;
2366     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2367         [self hideDropZone];
2368     else
2369         [self showDropZone];
2370     PL_UNLOCK;
2371 }
2372
2373 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2374 {
2375     if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2376     {
2377         NSPasteboard *o_pasteboard = [info draggingPasteboard];
2378         if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2379             return NSDragOperationGeneric;
2380     }
2381     return NSDragOperationNone;
2382 }
2383
2384 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2385 {
2386     NSPasteboard *o_pasteboard = [info draggingPasteboard];
2387
2388     playlist_t * p_playlist = pl_Get( VLCIntf );
2389     playlist_item_t *p_node;
2390
2391     if ([[item identifier] isEqualToString:@"playlist"])
2392         p_node = p_playlist->p_local_category;
2393     else
2394         p_node = p_playlist->p_ml_category;
2395
2396     if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2397     {
2398         NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2399         NSUInteger count = [o_values count];
2400         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2401
2402         for( NSUInteger i = 0; i < count; i++)
2403         {
2404             NSDictionary *o_dic;
2405             char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
2406             if( !psz_uri )
2407                 continue;
2408
2409             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2410
2411             free( psz_uri );
2412
2413             [o_array addObject: o_dic];
2414         }
2415
2416         [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2417         return YES;
2418     }
2419     else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2420     {
2421         NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2422
2423         NSUInteger count = [array count];
2424         playlist_item_t * p_item = NULL;
2425
2426         PL_LOCK;
2427         for( NSUInteger i = 0; i < count; i++ )
2428         {
2429             p_item = [[array objectAtIndex:i] pointerValue];
2430             if( !p_item ) continue;
2431             playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2432         }
2433         PL_UNLOCK;
2434
2435         return YES;
2436     }
2437     return NO;
2438 }
2439
2440 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2441 {
2442     return [item identifier];
2443 }
2444
2445 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2446 {
2447     /* the following code assumes for sakes of simplicity that only the top level
2448      * items are allowed to have children */
2449
2450     NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2451     NSUInteger count = [array count];
2452     if (count < 1)
2453         return nil;
2454
2455     for (NSUInteger x = 0; x < count; x++)
2456     {
2457         id item = [array objectAtIndex: x]; // save one objc selector call
2458         if ([[item identifier] isEqualToString:object])
2459             return item;
2460     }
2461
2462     return nil;
2463 }
2464 @end
2465
2466 @implementation VLCDetachedVideoWindow
2467
2468 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
2469                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
2470 {
2471     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
2472
2473     if (b_dark_interface)
2474     {
2475 #ifdef MAC_OS_X_VERSION_10_7
2476         if (OSX_LION)
2477             styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
2478         else
2479             styleMask = NSBorderlessWindowMask;
2480 #else
2481         styleMask = NSBorderlessWindowMask;
2482 #endif
2483     }
2484
2485     self = [super initWithContentRect:contentRect styleMask:styleMask
2486                               backing:backingType defer:flag];
2487
2488     /* we want to be moveable regardless of our style */
2489     [self setMovableByWindowBackground: YES];
2490
2491     /* we don't want this window to be restored on relaunch */
2492     if (OSX_LION)
2493         [self setRestorable:NO];
2494
2495     return self;
2496 }
2497
2498 - (void)awakeFromNib
2499 {
2500     if (b_dark_interface)
2501     {
2502         [self setBackgroundColor: [NSColor clearColor]];
2503         [self setOpaque: NO];
2504         [self display];
2505         [self setHasShadow:NO];
2506         [self setHasShadow:YES];
2507     }
2508 }
2509
2510 - (IBAction)fullscreen:(id)sender
2511 {
2512     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
2513 }
2514
2515 - (void)updateFullscreen
2516 {
2517     [[VLCMain sharedInstance] fullscreenChanged];
2518 }
2519
2520 - (BOOL)isFullscreen
2521 {
2522     return [[VLCMainWindow sharedInstance] isFullscreen];
2523 }
2524
2525 - (void)performClose:(id)sender
2526 {
2527     if (b_dark_interface)
2528         [[VLCMainWindow sharedInstance] performClose: sender];
2529     else
2530         [super performClose: sender];
2531 }
2532
2533 - (void)performMiniaturize:(id)sender
2534 {
2535     if (b_dark_interface)
2536         [self miniaturize: sender];
2537     else
2538         [super performMiniaturize: sender];
2539 }
2540
2541 - (void)performZoom:(id)sender
2542 {
2543     if (b_dark_interface)
2544         [self customZoom: sender];
2545     else
2546         [super performZoom: sender];
2547 }
2548
2549 - (void)zoom:(id)sender
2550 {
2551     if (b_dark_interface)
2552         [self customZoom: sender];
2553     else
2554         [super zoom: sender];
2555 }
2556
2557 - (BOOL)canBecomeKeyWindow
2558 {
2559     return YES;
2560 }
2561
2562 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
2563 {
2564     SEL s_menuAction = [menuItem action];
2565
2566     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
2567         return YES;
2568
2569     return [super validateMenuItem:menuItem];
2570 }
2571
2572 /**
2573  * Given a proposed frame rectangle, return a modified version
2574  * which will fit inside the screen.
2575  *
2576  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
2577  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
2578  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
2579  *    Copyright (C) 1996 Free Software Foundation, Inc.
2580  */
2581 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
2582 {
2583     NSRect screenRect = [screen visibleFrame];
2584     float difference;
2585
2586     /* Move top edge of the window inside the screen */
2587     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
2588     if (difference > 0)
2589     {
2590         frameRect.origin.y -= difference;
2591     }
2592
2593     /* If the window is resizable, resize it (if needed) so that the
2594      bottom edge is on the screen or can be on the screen when the user moves
2595      the window */
2596     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
2597     if (_styleMask & NSResizableWindowMask)
2598     {
2599         float difference2;
2600
2601         difference2 = screenRect.origin.y - frameRect.origin.y;
2602         difference2 -= difference;
2603         // Take in account the space between the top of window and the top of the 
2604         // screen which can be used to move the bottom of the window on the screen
2605         if (difference2 > 0)
2606         {
2607             frameRect.size.height -= difference2;
2608             frameRect.origin.y += difference2;
2609         }
2610
2611         /* Ensure that resizing doesn't makewindow smaller than minimum */
2612         difference2 = [self minSize].height - frameRect.size.height;
2613         if (difference2 > 0)
2614         {
2615             frameRect.size.height += difference2;
2616             frameRect.origin.y -= difference2;
2617         }
2618     }
2619
2620     return frameRect;
2621 }
2622
2623 #define DIST 3
2624
2625 /**
2626  Zooms the receiver.   This method calls the delegate method
2627  windowShouldZoom:toFrame: to determine if the window should
2628  be allowed to zoom to full screen.
2629  *
2630  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
2631  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
2632  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
2633  *    Copyright (C) 1996 Free Software Foundation, Inc.
2634  */
2635 - (void) customZoom: (id)sender
2636 {
2637     NSRect maxRect = [[self screen] visibleFrame];
2638     NSRect currentFrame = [self frame];
2639
2640     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
2641     {
2642         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
2643     }
2644
2645     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
2646
2647     // Compare the new frame with the current one
2648     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
2649         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
2650         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
2651         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
2652     {
2653         // Already in zoomed mode, reset user frame, if stored
2654         if ([self frameAutosaveName] != nil)
2655         {
2656             [self setFrame: previousSavedFrame display: YES animate: YES];
2657             [self saveFrameUsingName: [self frameAutosaveName]];
2658         }
2659         return;
2660     }
2661
2662     if ([self frameAutosaveName] != nil)
2663     {
2664         [self saveFrameUsingName: [self frameAutosaveName]];
2665         previousSavedFrame = [self frame];
2666     }
2667
2668     [self setFrame: maxRect display: YES animate: YES];
2669 }
2670
2671 @end