]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: fix menu support in detached video window
[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         vlc_object_release( p_input );
1365     }
1366
1367     if( b_buffering )
1368     {
1369         [o_progress_bar startAnimation:self];
1370         [o_progress_bar setIndeterminate:YES];
1371         [o_progress_bar setHidden:NO];
1372     } else {
1373         [o_progress_bar stopAnimation:self];
1374         [o_progress_bar setHidden:YES];
1375     }
1376
1377     [o_stop_btn setEnabled: b_input];
1378     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1379     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1380     [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1381     [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1382     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1383
1384     [o_time_sld setEnabled: b_seekable];
1385     [self updateTimeSlider];
1386     [o_fspanel setSeekable: b_seekable];
1387
1388     PL_LOCK;
1389     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1390         [self hideDropZone];
1391     else
1392         [self showDropZone];
1393     PL_UNLOCK;
1394     [o_sidebar_view setNeedsDisplay:YES];
1395 }
1396
1397 - (void)setPause
1398 {
1399     [o_play_btn setImage: o_pause_img];
1400     [o_play_btn setAlternateImage: o_pause_pressed_img];
1401     [o_play_btn setToolTip: _NS("Pause")];
1402     [o_detached_play_btn setImage: o_pause_img];
1403     [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1404     [o_detached_play_btn setToolTip: _NS("Pause")];
1405     [o_fspanel setPause];
1406 }
1407
1408 - (void)setPlay
1409 {
1410     [o_play_btn setImage: o_play_img];
1411     [o_play_btn setAlternateImage: o_play_pressed_img];
1412     [o_play_btn setToolTip: _NS("Play")];
1413     [o_detached_play_btn setImage: o_play_img];
1414     [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1415     [o_detached_play_btn setToolTip: _NS("Play")];
1416     [o_fspanel setPlay];
1417 }
1418
1419 - (void)drawFancyGradientEffectForTimeSlider
1420 {
1421     if (OSX_LEOPARD)
1422         return;
1423
1424     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1425     CGFloat f_value = [o_time_sld knobPosition];
1426     if (f_value > 7.5)
1427     {
1428         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1429         if (f_value != oldFrame.size.width)
1430         {
1431             if ([o_time_sld_fancygradient_view isHidden])
1432                 [o_time_sld_fancygradient_view setHidden: NO];
1433             [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1434         }
1435
1436         if (b_nonembedded)
1437         {
1438             f_value = [o_detached_time_sld knobPosition];
1439             oldFrame = [o_detached_time_sld_fancygradient_view frame];
1440             if (f_value != oldFrame.size.width)
1441             {
1442                 if ([o_detached_time_sld_fancygradient_view isHidden])
1443                     [o_detached_time_sld_fancygradient_view setHidden: NO];
1444                 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1445             }
1446         }
1447     }
1448     else
1449     {
1450         NSRect frame;
1451         frame = [o_time_sld_fancygradient_view frame];
1452         if (frame.size.width > 0)
1453         {
1454             frame.size.width = 0;
1455             [o_time_sld_fancygradient_view setFrame: frame];
1456
1457             frame = [o_detached_time_sld_fancygradient_view frame];
1458             frame.size.width = 0;
1459             [o_detached_time_sld_fancygradient_view setFrame: frame];
1460         }
1461         [o_time_sld_fancygradient_view setHidden: YES];
1462         [o_detached_time_sld_fancygradient_view setHidden: YES];
1463     }
1464     [o_pool release];
1465 }
1466
1467 #pragma mark -
1468 #pragma mark Video Output handling
1469 - (id)videoView
1470 {
1471     return o_video_view;
1472 }
1473
1474 - (id)setupVideoView
1475 {
1476     vout_thread_t *p_vout = getVout();
1477     if (config_GetInt( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode)
1478     {
1479         if ([o_video_view window] != self)
1480         {
1481             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1482             [o_video_view setFrame: [o_split_view frame]];
1483             [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1484         }
1485         b_nonembedded = NO;
1486     }
1487     else
1488     {
1489         if ([o_video_view superview] != NULL)
1490             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1491
1492         NSRect frame = [o_detached_video_window frame];
1493         NSRect videoFrame = [o_video_view frame];
1494         frame.size.width = videoFrame.size.width;
1495         frame.size.height = videoFrame.size.height + [o_detached_bottombar_view frame].size.height + [o_titlebar_view frame].size.height;
1496         [o_detached_video_window setFrame: frame display: NO];
1497         [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1498         [o_detached_video_window setLevel:NSNormalWindowLevel];
1499         [o_detached_video_window useOptimizedDrawing: YES];
1500         [o_detached_video_window center];
1501         b_nonembedded = YES;
1502     }
1503     [[o_video_view window] makeKeyAndOrderFront: self];
1504     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
1505
1506     if (p_vout)
1507     {
1508         if( var_GetBool( p_vout, "video-on-top" ) )
1509             [[o_video_view window] setLevel: NSStatusWindowLevel];
1510         else
1511             [[o_video_view window] setLevel: NSNormalWindowLevel];
1512         vlc_object_release( p_vout );
1513     }
1514     return o_video_view;
1515 }
1516
1517 - (void)setVideoplayEnabled
1518 {
1519     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1520
1521     if( b_videoPlayback )
1522     {
1523         // look for 'start at fullscreen'
1524         [[VLCMain sharedInstance] fullscreenChanged];
1525     }
1526     else
1527     {
1528         [self makeFirstResponder: nil];
1529         [o_detached_video_window orderOut: nil];
1530
1531         // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
1532         [self setAlphaValue:1.0];
1533     }
1534
1535     if( b_nativeFullscreenMode )
1536     {
1537         if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1538             [o_bottombar_view setHidden: b_videoPlayback];
1539         else
1540             [o_bottombar_view setHidden: NO];
1541         if( b_videoPlayback && b_fullscreen )
1542             [o_fspanel setActive: nil];
1543         if( !b_videoPlayback )
1544             [o_fspanel setNonActive: nil];
1545     }
1546
1547     if (!b_videoPlayback && b_fullscreen)
1548     {
1549         if (!b_nativeFullscreenMode)
1550             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1551     }
1552 }
1553
1554 - (void)resizeWindow
1555 {
1556     if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
1557         return;
1558
1559     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1560     NSSize windowMinSize = [o_videoWindow minSize];
1561     NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
1562
1563     NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
1564     NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
1565
1566     unsigned int i_width = nativeVideoSize.width;
1567     unsigned int i_height = nativeVideoSize.height;
1568     if (i_width < windowMinSize.width)
1569         i_width = windowMinSize.width;
1570     if (i_height < f_min_video_height)
1571         i_height = f_min_video_height;
1572
1573     /* Calculate the window's new size */
1574     NSRect new_frame;
1575     new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
1576     new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
1577     new_frame.origin.x = topleftscreen.x;
1578     new_frame.origin.y = topleftscreen.y - new_frame.size.height;
1579
1580     /* make sure the window doesn't exceed the screen size the window is on */
1581     if( new_frame.size.width > screenFrame.size.width )
1582     {
1583         new_frame.size.width = screenFrame.size.width;
1584         new_frame.origin.x = screenFrame.origin.x;
1585     }
1586     if( new_frame.size.height > screenFrame.size.height )
1587     {
1588         new_frame.size.height = screenFrame.size.height;
1589         new_frame.origin.y = screenFrame.origin.y;
1590     }
1591     if( new_frame.origin.y < screenFrame.origin.y )
1592         new_frame.origin.y = screenFrame.origin.y;
1593
1594     [[o_videoWindow animator] setFrame:new_frame display:YES];
1595 }
1596
1597 - (void)setNativeVideoSize:(NSSize)size
1598 {
1599     nativeVideoSize = size;
1600
1601     if( config_GetInt( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen )
1602         [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
1603 }
1604
1605 //  Called automatically if window's acceptsMouseMovedEvents property is true
1606 - (void)mouseMoved:(NSEvent *)theEvent
1607 {
1608     if (b_fullscreen)
1609         [self recreateHideMouseTimer];
1610
1611     [super mouseMoved: theEvent];
1612 }
1613
1614 - (void)recreateHideMouseTimer
1615 {
1616     if (t_hide_mouse_timer != nil) {
1617         [t_hide_mouse_timer invalidate];
1618         [t_hide_mouse_timer release];
1619     }
1620
1621     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
1622                                                           target:self
1623                                                         selector:@selector(hideMouseCursor:)
1624                                                         userInfo:nil
1625                                                          repeats:NO];
1626     [t_hide_mouse_timer retain];
1627 }
1628
1629 //  NSTimer selectors require this function signature as per Apple's docs
1630 - (void)hideMouseCursor:(NSTimer *)timer
1631 {
1632     [NSCursor setHiddenUntilMouseMoves: YES];
1633 }
1634
1635 #pragma mark -
1636 #pragma mark Fullscreen support
1637 - (void)showFullscreenController
1638 {
1639      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
1640         [o_fspanel fadeIn];
1641 }
1642
1643 - (void)updateFullscreen
1644 {
1645     [[VLCMain sharedInstance] fullscreenChanged];
1646 }
1647
1648 - (BOOL)isFullscreen
1649 {
1650     return b_fullscreen;
1651 }
1652
1653 - (void)lockFullscreenAnimation
1654 {
1655     [o_animation_lock lock];
1656 }
1657
1658 - (void)unlockFullscreenAnimation
1659 {
1660     [o_animation_lock unlock];
1661 }
1662
1663 - (void)enterFullscreen
1664 {
1665     NSMutableDictionary *dict1, *dict2;
1666     NSScreen *screen;
1667     NSRect screen_rect;
1668     NSRect rect;
1669     vout_thread_t *p_vout = getVout();
1670     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1671     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1672
1673     if( p_vout )
1674         screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)config_GetInt( VLCIntf, "macosx-vdev" )];
1675
1676     [self lockFullscreenAnimation];
1677
1678     if (!screen)
1679     {
1680         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
1681         screen = [o_videoWindow screen];
1682     }
1683     if (!screen)
1684     {
1685         msg_Dbg( VLCIntf, "Using deepest screen" );
1686         screen = [NSScreen deepestScreen];
1687     }
1688
1689     if( p_vout )
1690         vlc_object_release( p_vout );
1691
1692     screen_rect = [screen frame];
1693
1694     [o_fullscreen_btn setState: YES];
1695     [o_detached_fullscreen_btn setState: YES];
1696
1697     [self recreateHideMouseTimer];
1698
1699     if( blackout_other_displays )
1700         [screen blackoutOtherScreens];
1701
1702     /* Make sure we don't see the window flashes in float-on-top mode */
1703     i_originalLevel = [o_videoWindow level];
1704     [o_videoWindow setLevel:NSNormalWindowLevel];
1705
1706     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
1707     if (!o_fullscreen_window)
1708     {
1709         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
1710
1711         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
1712         rect.origin.x += [o_videoWindow frame].origin.x;
1713         rect.origin.y += [o_videoWindow frame].origin.y;
1714         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
1715         [o_fullscreen_window setFullscreen: YES];
1716         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
1717         [o_fullscreen_window setCanBecomeKeyWindow: YES];
1718
1719         if (![o_videoWindow isVisible] || [o_videoWindow alphaValue] == 0.0)
1720         {
1721             /* We don't animate if we are not visible, instead we
1722              * simply fade the display */
1723             CGDisplayFadeReservationToken token;
1724
1725             if( blackout_other_displays )
1726             {
1727                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1728                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1729             }
1730
1731             if ([screen isMainScreen])
1732             {
1733                 if (OSX_LEOPARD)
1734                     SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1735                 else
1736                     [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1737             }
1738
1739             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1740             [o_temp_view setFrame:[o_video_view frame]];
1741             [o_fullscreen_window setContentView:o_video_view];
1742
1743             [o_fullscreen_window makeKeyAndOrderFront:self];
1744             [o_fullscreen_window orderFront:self animate:YES];
1745
1746             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
1747             [o_fullscreen_window setLevel:NSNormalWindowLevel];
1748
1749             if( blackout_other_displays )
1750             {
1751                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1752                 CGReleaseDisplayFadeReservation( token );
1753             }
1754
1755             /* Will release the lock */
1756             [self hasBecomeFullscreen];
1757
1758             return;
1759         }
1760
1761         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1762         NSDisableScreenUpdates();
1763         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1764         [o_temp_view setFrame:[o_video_view frame]];
1765         [o_fullscreen_window setContentView:o_video_view];
1766         [o_fullscreen_window makeKeyAndOrderFront:self];
1767         NSEnableScreenUpdates();
1768     }
1769
1770     /* We are in fullscreen (and no animation is running) */
1771     if (b_fullscreen)
1772     {
1773         /* Make sure we are hidden */
1774         [o_videoWindow orderOut: self];
1775
1776         [self unlockFullscreenAnimation];
1777         return;
1778     }
1779
1780     if (o_fullscreen_anim1)
1781     {
1782         [o_fullscreen_anim1 stopAnimation];
1783         [o_fullscreen_anim1 release];
1784     }
1785     if (o_fullscreen_anim2)
1786     {
1787         [o_fullscreen_anim2 stopAnimation];
1788         [o_fullscreen_anim2 release];
1789     }
1790
1791     if ([screen isMainScreen])
1792     {
1793         if (OSX_LEOPARD)
1794             SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1795         else
1796             [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1797     }
1798
1799     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
1800     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
1801
1802     [dict1 setObject:o_videoWindow forKey:NSViewAnimationTargetKey];
1803     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
1804
1805     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1806     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1807     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
1808
1809     /* Strategy with NSAnimation allocation:
1810      - Keep at most 2 animation at a time
1811      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
1812      */
1813     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1814     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1815
1816     [dict1 release];
1817     [dict2 release];
1818
1819     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1820     [o_fullscreen_anim1 setDuration: 0.3];
1821     [o_fullscreen_anim1 setFrameRate: 30];
1822     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1823     [o_fullscreen_anim2 setDuration: 0.2];
1824     [o_fullscreen_anim2 setFrameRate: 30];
1825
1826     [o_fullscreen_anim2 setDelegate: self];
1827     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1828
1829     [o_fullscreen_anim1 startAnimation];
1830     /* fullscreenAnimation will be unlocked when animation ends */
1831 }
1832
1833 - (void)hasBecomeFullscreen
1834 {
1835     if( [[o_video_view subviews] count] > 0 )
1836         [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1837
1838     [o_fullscreen_window makeKeyWindow];
1839     [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
1840
1841     /* tell the fspanel to move itself to front next time it's triggered */
1842     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
1843     [o_fspanel setActive: nil];
1844
1845     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1846     if( [o_videoWindow isVisible] )
1847         [o_videoWindow orderOut: self];
1848
1849     b_fullscreen = YES;
1850     [self unlockFullscreenAnimation];
1851 }
1852
1853 - (void)leaveFullscreen
1854 {
1855     [self leaveFullscreenAndFadeOut: NO];
1856 }
1857
1858 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1859 {
1860     NSMutableDictionary *dict1, *dict2;
1861     NSRect frame;
1862     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1863
1864     [self lockFullscreenAnimation];
1865
1866     [o_fullscreen_btn setState: NO];
1867     [o_detached_fullscreen_btn setState: NO];
1868
1869     /* We always try to do so */
1870     [NSScreen unblackoutScreens];
1871
1872     vout_thread_t *p_vout = getVout();
1873     if (p_vout)
1874     {
1875         if( var_GetBool( p_vout, "video-on-top" ) )
1876             [[o_video_view window] setLevel: NSStatusWindowLevel];
1877         else
1878             [[o_video_view window] setLevel: NSNormalWindowLevel];
1879         vlc_object_release( p_vout );
1880     }
1881     [[o_video_view window] makeKeyAndOrderFront: nil];
1882
1883     /* Don't do anything if o_fullscreen_window is already closed */
1884     if (!o_fullscreen_window)
1885     {
1886         [self unlockFullscreenAnimation];
1887         return;
1888     }
1889
1890     if (fadeout)
1891     {
1892         /* We don't animate if we are not visible, instead we
1893          * simply fade the display */
1894         CGDisplayFadeReservationToken token;
1895
1896         if( blackout_other_displays )
1897         {
1898             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1899             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1900         }
1901
1902         [o_fspanel setNonActive: nil];
1903         if (OSX_LEOPARD)
1904             SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1905         else
1906             [NSApp setPresentationOptions: NSApplicationPresentationDefault];
1907
1908         /* Will release the lock */
1909         [self hasEndedFullscreen];
1910
1911         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1912          * here */
1913         b_window_is_invisible = YES;
1914
1915         if( blackout_other_displays )
1916         {
1917             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1918             CGReleaseDisplayFadeReservation( token );
1919         }
1920
1921         return;
1922     }
1923
1924     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1925
1926     [o_videoWindow setAlphaValue: 0.0];
1927     [o_videoWindow orderFront: self];
1928     [[o_video_view window] orderFront: self];
1929
1930     [o_fspanel setNonActive: nil];
1931     if (OSX_LEOPARD)
1932         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1933     else
1934         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1935
1936     if (o_fullscreen_anim1)
1937     {
1938         [o_fullscreen_anim1 stopAnimation];
1939         [o_fullscreen_anim1 release];
1940     }
1941     if (o_fullscreen_anim2)
1942     {
1943         [o_fullscreen_anim2 stopAnimation];
1944         [o_fullscreen_anim2 release];
1945     }
1946
1947     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1948     frame.origin.x += [o_videoWindow frame].origin.x;
1949     frame.origin.y += [o_videoWindow frame].origin.y;
1950
1951     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1952     [dict2 setObject:o_videoWindow forKey:NSViewAnimationTargetKey];
1953     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1954
1955     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
1956     [dict2 release];
1957
1958     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1959     [o_fullscreen_anim2 setDuration: 0.3];
1960     [o_fullscreen_anim2 setFrameRate: 30];
1961
1962     [o_fullscreen_anim2 setDelegate: self];
1963
1964     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1965
1966     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1967     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1968     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1969
1970     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
1971     [dict1 release];
1972
1973     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1974     [o_fullscreen_anim1 setDuration: 0.2];
1975     [o_fullscreen_anim1 setFrameRate: 30];
1976     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1977
1978     /* Make sure o_fullscreen_window is the frontmost window */
1979     [o_fullscreen_window orderFront: self];
1980
1981     [o_fullscreen_anim1 startAnimation];
1982     /* fullscreenAnimation will be unlocked when animation ends */
1983 }
1984
1985 - (void)hasEndedFullscreen
1986 {
1987     b_fullscreen = NO;
1988
1989     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1990     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1991     NSDisableScreenUpdates();
1992     [o_video_view retain];
1993     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1994     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1995     [o_video_view release];
1996     [o_video_view setFrame:[o_temp_view frame]];
1997     if( [[o_video_view subviews] count] > 0 )
1998         [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1999     if( [[o_video_view window] isVisible] )
2000     {
2001         if( !b_nonembedded )
2002             [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
2003         else
2004             [[o_video_view window] makeKeyAndOrderFront: self];
2005     }
2006     [o_fullscreen_window orderOut: self];
2007     NSEnableScreenUpdates();
2008
2009     [o_fullscreen_window release];
2010     o_fullscreen_window = nil;
2011     [[o_video_view window] setLevel:i_originalLevel];
2012     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2013
2014     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2015     if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2016         [o_detached_video_window orderOut: self];
2017
2018     [self unlockFullscreenAnimation];
2019 }
2020
2021 - (void)animationDidEnd:(NSAnimation*)animation
2022 {
2023     NSArray *viewAnimations;
2024     if( o_makekey_anim == animation )
2025     {
2026         [o_makekey_anim release];
2027         return;
2028     }
2029     if ([animation currentValue] < 1.0)
2030         return;
2031
2032     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2033     viewAnimations = [o_fullscreen_anim2 viewAnimations];
2034     if ([viewAnimations count] >=1 &&
2035         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2036     {
2037         /* Fullscreen ended */
2038         [self hasEndedFullscreen];
2039     }
2040     else
2041     {
2042         /* Fullscreen started */
2043         [self hasBecomeFullscreen];
2044     }
2045 }
2046
2047 - (void)makeKeyAndOrderFront: (id)sender
2048 {
2049     /* Hack
2050      * when we exit fullscreen and fade out, we may endup in
2051      * having a window that is faded. We can't have it fade in unless we
2052      * animate again. */
2053
2054     if(!b_window_is_invisible)
2055     {
2056         /* Make sure we don't do it too much */
2057         [super makeKeyAndOrderFront: sender];
2058         return;
2059     }
2060
2061     [super setAlphaValue:0.0f];
2062     [super makeKeyAndOrderFront: sender];
2063
2064     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2065     [dict setObject:self forKey:NSViewAnimationTargetKey];
2066     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2067
2068     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2069     [dict release];
2070
2071     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2072     [o_makekey_anim setDuration: 0.1];
2073     [o_makekey_anim setFrameRate: 30];
2074     [o_makekey_anim setDelegate: self];
2075
2076     [o_makekey_anim startAnimation];
2077     b_window_is_invisible = NO;
2078
2079     /* fullscreenAnimation will be unlocked when animation ends */
2080 }
2081
2082 #pragma mark -
2083 #pragma mark Lion native fullscreen handling
2084 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2085 {
2086     // workaround, see #6668
2087     [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2088
2089     var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
2090     
2091     vout_thread_t *p_vout = getVout();
2092     if( p_vout )
2093     {
2094         var_SetBool( p_vout, "fullscreen", true );
2095         vlc_object_release( p_vout );
2096     }
2097     
2098     [o_video_view setFrame: [[self contentView] frame]];
2099     b_fullscreen = YES;
2100
2101     [self recreateHideMouseTimer];
2102     i_originalLevel = [self level];
2103     [self setLevel:NSNormalWindowLevel];
2104
2105     if (b_dark_interface)
2106     {
2107         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2108
2109         NSRect winrect;
2110         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2111         winrect = [self frame];
2112
2113         winrect.size.height = winrect.size.height - f_titleBarHeight;
2114         [self setFrame: winrect display:NO animate:NO];
2115         winrect = [o_split_view frame];
2116         winrect.size.height = winrect.size.height + f_titleBarHeight;
2117         [o_split_view setFrame: winrect];
2118     }
2119
2120     if ([[VLCMain sharedInstance] activeVideoPlayback])
2121         [o_bottombar_view setHidden: YES];
2122     
2123     [self setMovableByWindowBackground: NO];
2124 }
2125
2126 - (void)windowDidEnterFullScreen:(NSNotification *)notification
2127 {
2128     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2129     [o_fspanel setActive: nil];
2130 }
2131
2132 - (void)windowWillExitFullScreen:(NSNotification *)notification
2133 {
2134
2135     var_SetBool( pl_Get( VLCIntf ), "fullscreen", false );
2136     
2137     vout_thread_t *p_vout = getVout();
2138     if( p_vout )
2139     {
2140         var_SetBool( p_vout, "fullscreen", false );
2141         vlc_object_release( p_vout );
2142     }
2143
2144     [o_video_view setFrame: [o_split_view frame]];
2145     [NSCursor setHiddenUntilMouseMoves: NO];
2146     [o_fspanel setNonActive: nil];
2147     [self setLevel:i_originalLevel];
2148     b_fullscreen = NO;
2149
2150     if (b_dark_interface)
2151     {
2152         NSRect winrect;
2153         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2154         winrect = [self frame];
2155
2156         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2157                                               winrect.size.width, f_titleBarHeight )];
2158         [[self contentView] addSubview: o_titlebar_view];
2159
2160         winrect.size.height = winrect.size.height + f_titleBarHeight;
2161         [self setFrame: winrect display:NO animate:NO];
2162         winrect = [o_split_view frame];
2163         winrect.size.height = winrect.size.height - f_titleBarHeight;
2164         [o_split_view setFrame: winrect];
2165         [o_video_view setFrame: winrect];
2166     }
2167
2168     if ([[VLCMain sharedInstance] activeVideoPlayback])
2169         [o_bottombar_view setHidden: NO];
2170     
2171     [self setMovableByWindowBackground: YES];
2172 }
2173
2174 #pragma mark -
2175 #pragma mark split view delegate
2176 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2177 {
2178     if (dividerIndex == 0)
2179         return 300.;
2180     else
2181         return proposedMax;
2182 }
2183
2184 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2185 {
2186     return ([subview isEqual:o_left_split_view]);
2187 }
2188
2189 #pragma mark -
2190 #pragma mark Side Bar Data handling
2191 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2192 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2193 {
2194     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2195     if(item==nil)
2196         return [o_sidebaritems count];
2197     else
2198         return [[item children] count];
2199 }
2200
2201
2202 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2203 {
2204     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2205     if(item==nil)
2206         return [o_sidebaritems objectAtIndex:index];
2207     else
2208         return [[item children] objectAtIndex:index];
2209 }
2210
2211
2212 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2213 {
2214     return [item title];
2215 }
2216
2217 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2218 {
2219     [item setTitle:object];
2220 }
2221
2222 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2223 {
2224     return [item hasChildren];
2225 }
2226
2227
2228 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2229 {
2230     if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2231         return YES;
2232
2233     return [item hasBadge];
2234 }
2235
2236
2237 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2238 {
2239     playlist_t * p_playlist = pl_Get( VLCIntf );
2240     NSInteger i_playlist_size;
2241
2242     if ([[item identifier] isEqualToString: @"playlist"])
2243     {
2244         PL_LOCK;
2245         i_playlist_size = p_playlist->p_local_category->i_children;
2246         PL_UNLOCK;
2247
2248         return i_playlist_size;
2249     }
2250     if ([[item identifier] isEqualToString: @"medialibrary"])
2251     {
2252         PL_LOCK;
2253         i_playlist_size = p_playlist->p_ml_category->i_children;
2254         PL_UNLOCK;
2255
2256         return i_playlist_size;
2257     }
2258
2259     return [item badgeValue];
2260 }
2261
2262
2263 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2264 {
2265     return [item hasIcon];
2266 }
2267
2268
2269 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2270 {
2271     return [item icon];
2272 }
2273
2274 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2275 {
2276     if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2277     {
2278         if (item != nil)
2279         {
2280             NSMenu * m;
2281             if ([item sdtype] > 0)
2282             {
2283                 m = [[NSMenu alloc] init];
2284                 playlist_t * p_playlist = pl_Get( VLCIntf );
2285                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2286                 if (!sd_loaded)
2287                     [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2288                 else
2289                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2290                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2291             }
2292             return [m autorelease];
2293         }
2294     }
2295
2296     return nil;
2297 }
2298
2299 - (IBAction)sdmenuhandler:(id)sender
2300 {
2301     NSString * identifier = [sender representedObject];
2302     if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2303     {
2304         playlist_t * p_playlist = pl_Get( VLCIntf );
2305         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2306
2307         if (!sd_loaded)
2308             playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2309         else
2310             playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2311     }
2312 }
2313
2314 #pragma mark -
2315 #pragma mark Side Bar Delegate Methods
2316 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2317 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2318 {
2319     if ([[group identifier] isEqualToString:@"library"])
2320         return YES;
2321
2322     return NO;
2323 }
2324
2325 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2326 {
2327     playlist_t * p_playlist = pl_Get( VLCIntf );
2328
2329     NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2330     id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2331
2332
2333     //Set the label text to represent the new selection
2334     if ([item sdtype] > -1 && [[item identifier] length] > 0)
2335     {
2336         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2337         if (!sd_loaded)
2338         {
2339             playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2340         }
2341     }
2342
2343     [o_chosen_category_lbl setStringValue:[item title]];
2344
2345     if ([[item identifier] isEqualToString:@"playlist"])
2346     {
2347         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2348     }
2349     else if([[item identifier] isEqualToString:@"medialibrary"])
2350     {
2351         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2352     }
2353     else
2354     {
2355         playlist_item_t * pl_item;
2356         PL_LOCK;
2357         pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2358         PL_UNLOCK;
2359         [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2360     }
2361
2362     PL_LOCK;
2363     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2364         [self hideDropZone];
2365     else
2366         [self showDropZone];
2367     PL_UNLOCK;
2368 }
2369
2370 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2371 {
2372     if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2373     {
2374         NSPasteboard *o_pasteboard = [info draggingPasteboard];
2375         if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2376             return NSDragOperationGeneric;
2377     }
2378     return NSDragOperationNone;
2379 }
2380
2381 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2382 {
2383     NSPasteboard *o_pasteboard = [info draggingPasteboard];
2384
2385     playlist_t * p_playlist = pl_Get( VLCIntf );
2386     playlist_item_t *p_node;
2387
2388     if ([[item identifier] isEqualToString:@"playlist"])
2389         p_node = p_playlist->p_local_category;
2390     else
2391         p_node = p_playlist->p_ml_category;
2392
2393     if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2394     {
2395         NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2396         NSUInteger count = [o_values count];
2397         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2398
2399         for( NSUInteger i = 0; i < count; i++)
2400         {
2401             NSDictionary *o_dic;
2402             char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
2403             if( !psz_uri )
2404                 continue;
2405
2406             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2407
2408             free( psz_uri );
2409
2410             [o_array addObject: o_dic];
2411         }
2412
2413         [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2414         return YES;
2415     }
2416     else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2417     {
2418         NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2419
2420         NSUInteger count = [array count];
2421         playlist_item_t * p_item = NULL;
2422
2423         PL_LOCK;
2424         for( NSUInteger i = 0; i < count; i++ )
2425         {
2426             p_item = [[array objectAtIndex:i] pointerValue];
2427             if( !p_item ) continue;
2428             playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2429         }
2430         PL_UNLOCK;
2431
2432         return YES;
2433     }
2434     return NO;
2435 }
2436
2437 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2438 {
2439     return [item identifier];
2440 }
2441
2442 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2443 {
2444     /* the following code assumes for sakes of simplicity that only the top level
2445      * items are allowed to have children */
2446
2447     NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2448     NSUInteger count = [array count];
2449     if (count < 1)
2450         return nil;
2451
2452     for (NSUInteger x = 0; x < count; x++)
2453     {
2454         id item = [array objectAtIndex: x]; // save one objc selector call
2455         if ([[item identifier] isEqualToString:object])
2456             return item;
2457     }
2458
2459     return nil;
2460 }
2461 @end
2462
2463 @implementation VLCDetachedVideoWindow
2464
2465 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
2466                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
2467 {
2468     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
2469
2470     if (b_dark_interface)
2471     {
2472 #ifdef MAC_OS_X_VERSION_10_7
2473         if (OSX_LION)
2474             styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
2475         else
2476             styleMask = NSBorderlessWindowMask;
2477 #else
2478         styleMask = NSBorderlessWindowMask;
2479 #endif
2480     }
2481
2482     self = [super initWithContentRect:contentRect styleMask:styleMask
2483                               backing:backingType defer:flag];
2484
2485     /* we want to be moveable regardless of our style */
2486     [self setMovableByWindowBackground: YES];
2487
2488     /* we don't want this window to be restored on relaunch */
2489     if (OSX_LION)
2490         [self setRestorable:NO];
2491
2492     return self;
2493 }
2494
2495 - (void)awakeFromNib
2496 {
2497     [self setAcceptsMouseMovedEvents: YES];
2498
2499     if (b_dark_interface)
2500     {
2501         [self setBackgroundColor: [NSColor clearColor]];
2502         [self setOpaque: NO];
2503         [self display];
2504         [self setHasShadow:NO];
2505         [self setHasShadow:YES];
2506     }
2507 }
2508
2509 - (IBAction)fullscreen:(id)sender
2510 {
2511     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
2512 }
2513
2514 - (void)updateFullscreen
2515 {
2516     [[VLCMain sharedInstance] fullscreenChanged];
2517 }
2518
2519 - (BOOL)isFullscreen
2520 {
2521     return [[VLCMainWindow sharedInstance] isFullscreen];
2522 }
2523
2524 - (void)performClose:(id)sender
2525 {
2526     if (b_dark_interface)
2527         [[VLCMainWindow sharedInstance] performClose: sender];
2528     else
2529         [super performClose: sender];
2530 }
2531
2532 - (void)performMiniaturize:(id)sender
2533 {
2534     if (b_dark_interface)
2535         [self miniaturize: sender];
2536     else
2537         [super performMiniaturize: sender];
2538 }
2539
2540 - (void)performZoom:(id)sender
2541 {
2542     if (b_dark_interface)
2543         [self customZoom: sender];
2544     else
2545         [super performZoom: sender];
2546 }
2547
2548 - (void)zoom:(id)sender
2549 {
2550     if (b_dark_interface)
2551         [self customZoom: sender];
2552     else
2553         [super zoom: sender];
2554 }
2555
2556 - (BOOL)canBecomeKeyWindow
2557 {
2558     return YES;
2559 }
2560
2561 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
2562 {
2563     SEL s_menuAction = [menuItem action];
2564
2565     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
2566         return YES;
2567
2568     return [super validateMenuItem:menuItem];
2569 }
2570
2571 /**
2572  * Given a proposed frame rectangle, return a modified version
2573  * which will fit inside the screen.
2574  *
2575  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
2576  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
2577  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
2578  *    Copyright (C) 1996 Free Software Foundation, Inc.
2579  */
2580 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
2581 {
2582     NSRect screenRect = [screen visibleFrame];
2583     float difference;
2584
2585     /* Move top edge of the window inside the screen */
2586     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
2587     if (difference > 0)
2588     {
2589         frameRect.origin.y -= difference;
2590     }
2591
2592     /* If the window is resizable, resize it (if needed) so that the
2593      bottom edge is on the screen or can be on the screen when the user moves
2594      the window */
2595     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
2596     if (_styleMask & NSResizableWindowMask)
2597     {
2598         float difference2;
2599
2600         difference2 = screenRect.origin.y - frameRect.origin.y;
2601         difference2 -= difference;
2602         // Take in account the space between the top of window and the top of the 
2603         // screen which can be used to move the bottom of the window on the screen
2604         if (difference2 > 0)
2605         {
2606             frameRect.size.height -= difference2;
2607             frameRect.origin.y += difference2;
2608         }
2609
2610         /* Ensure that resizing doesn't makewindow smaller than minimum */
2611         difference2 = [self minSize].height - frameRect.size.height;
2612         if (difference2 > 0)
2613         {
2614             frameRect.size.height += difference2;
2615             frameRect.origin.y -= difference2;
2616         }
2617     }
2618
2619     return frameRect;
2620 }
2621
2622 #define DIST 3
2623
2624 /**
2625  Zooms the receiver.   This method calls the delegate method
2626  windowShouldZoom:toFrame: to determine if the window should
2627  be allowed to zoom to full screen.
2628  *
2629  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
2630  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
2631  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
2632  *    Copyright (C) 1996 Free Software Foundation, Inc.
2633  */
2634 - (void) customZoom: (id)sender
2635 {
2636     NSRect maxRect = [[self screen] visibleFrame];
2637     NSRect currentFrame = [self frame];
2638
2639     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
2640     {
2641         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
2642     }
2643
2644     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
2645
2646     // Compare the new frame with the current one
2647     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
2648         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
2649         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
2650         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
2651     {
2652         // Already in zoomed mode, reset user frame, if stored
2653         if ([self frameAutosaveName] != nil)
2654         {
2655             [self setFrame: previousSavedFrame display: YES animate: YES];
2656             [self saveFrameUsingName: [self frameAutosaveName]];
2657         }
2658         return;
2659     }
2660
2661     if ([self frameAutosaveName] != nil)
2662     {
2663         [self saveFrameUsingName: [self frameAutosaveName]];
2664         previousSavedFrame = [self frame];
2665     }
2666
2667     [self setFrame: maxRect display: YES animate: YES];
2668 }
2669
2670 @end