]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: allow a smaller min-height when video is playing and currently visible in...
[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     [self makeFirstResponder: o_video_view];
710 }
711
712 - (IBAction)togglePlaylist:(id)sender
713 {
714     if (![self isVisible] && sender != nil)
715     {
716         [self makeKeyAndOrderFront: sender];
717         return;
718     }
719
720     BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
721     BOOL b_restored = NO;
722
723     if (b_dropzone_active && !b_activeVideo && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0)
724     {
725         b_dropzone_active = NO;
726         [self hideDropZone];
727         return;
728     }
729
730     if ( !b_splitview_removed && ( (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
731                                   || (b_nonembedded && sender != nil)
732                                   || (!b_activeVideo && sender != nil)
733                                   || b_minimized_view ) )
734     {
735         [self hideSplitView];
736     }
737     else
738     {
739         if (b_splitview_removed)
740         {
741             if( !b_nonembedded || ( sender != nil && b_nonembedded))
742                 [self showSplitView];
743
744             if (sender == nil)
745                 b_minimized_view = YES;
746             else
747                 b_minimized_view = NO;
748
749             if (b_activeVideo)
750                 b_restored = YES;
751         }
752
753         if (!b_nonembedded)
754         {
755             if (([o_video_view isHidden] && b_activeVideo) || b_restored )
756                 [self makeSplitViewHidden];
757             else
758                 [self makeSplitViewVisible];
759         }
760         else
761         {
762             [o_split_view setHidden: NO];
763             [o_playlist_table setHidden: NO];
764             [o_video_view setHidden: !b_activeVideo];
765             if (b_activeVideo)
766                 [o_detached_video_window makeFirstResponder: o_video_view];
767         }
768     }
769 }
770
771 - (void)setRepeatOne
772 {
773     [o_repeat_btn setImage: o_repeat_one_img];
774     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];   
775 }
776
777 - (void)setRepeatAll
778 {
779     [o_repeat_btn setImage: o_repeat_all_img];
780     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
781 }
782
783 - (void)setRepeatOff
784 {
785     [o_repeat_btn setImage: o_repeat_img];
786     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
787 }
788
789 - (IBAction)repeat:(id)sender
790 {
791     vlc_value_t looping,repeating;
792     intf_thread_t * p_intf = VLCIntf;
793     playlist_t * p_playlist = pl_Get( p_intf );
794
795     var_Get( p_playlist, "repeat", &repeating );
796     var_Get( p_playlist, "loop", &looping );
797
798     if( !repeating.b_bool && !looping.b_bool )
799     {
800         /* was: no repeating at all, switching to Repeat One */
801         [[VLCCoreInteraction sharedInstance] repeatOne];
802         [self setRepeatOne];
803     }
804     else if( repeating.b_bool && !looping.b_bool )
805     {
806         /* was: Repeat One, switching to Repeat All */
807         [[VLCCoreInteraction sharedInstance] repeatAll];
808         [self setRepeatAll];
809     }
810     else
811     {
812         /* was: Repeat All or bug in VLC, switching to Repeat Off */
813         [[VLCCoreInteraction sharedInstance] repeatOff];
814         [self setRepeatOff];
815     }
816 }
817
818 - (void)setShuffle
819 {
820     bool b_value;
821     playlist_t *p_playlist = pl_Get( VLCIntf );
822     b_value = var_GetBool( p_playlist, "random" );
823
824     if(b_value) {
825         [o_shuffle_btn setImage: o_shuffle_on_img];
826         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
827     }
828     else
829     {
830         [o_shuffle_btn setImage: o_shuffle_img];
831         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
832     }
833 }
834
835 - (IBAction)shuffle:(id)sender
836 {
837     [[VLCCoreInteraction sharedInstance] shuffle];
838     [self setShuffle];
839 }
840
841 - (IBAction)timeSliderAction:(id)sender
842 {
843     float f_updated;
844     input_thread_t * p_input;
845
846     switch( [[NSApp currentEvent] type] )
847     {
848         case NSLeftMouseUp:
849         case NSLeftMouseDown:
850         case NSLeftMouseDragged:
851             f_updated = [sender floatValue];
852             break;
853
854         default:
855             return;
856     }
857     p_input = pl_CurrentInput( VLCIntf );
858     if( p_input != NULL )
859     {
860         vlc_value_t time;
861         vlc_value_t pos;
862         NSString * o_time;
863         char psz_time[MSTRTIME_MAX_SIZE];
864
865         pos.f_float = f_updated / 10000.;
866         var_Set( p_input, "position", pos );
867         [o_time_sld setFloatValue: f_updated];
868
869         var_Get( p_input, "time", &time );
870
871         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
872         if( [o_time_fld timeRemaining] && dur != -1 )
873         {
874             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000) )];
875         }
876         else
877             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
878
879         [o_time_fld setStringValue: o_time];
880         [o_fspanel setStreamPos: f_updated andTime: o_time];
881         vlc_object_release( p_input );
882     }
883 }
884
885 - (IBAction)volumeAction:(id)sender
886 {
887     if (sender == o_volume_sld)
888         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
889     else if (sender == o_volume_down_btn)
890     {
891         [[VLCCoreInteraction sharedInstance] mute];
892     }
893     else
894         [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
895 }
896
897 - (IBAction)effects:(id)sender
898 {
899     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
900 }
901
902 - (IBAction)fullscreen:(id)sender
903 {
904     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
905 }
906
907 - (IBAction)dropzoneButtonAction:(id)sender
908 {
909     [[[VLCMain sharedInstance] open] openFileGeneric];
910 }
911
912 #pragma mark -
913 #pragma mark overwritten default functionality
914 - (BOOL)canBecomeKeyWindow
915 {
916     return YES;
917 }
918
919 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
920 {
921     SEL s_menuAction = [menuItem action];
922
923     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
924             return YES;
925
926     return [super validateMenuItem:menuItem];
927 }
928
929 - (void)setTitle:(NSString *)title
930 {
931     if (b_dark_interface)
932     {
933         [o_titlebar_view setWindowTitle: title];
934         [o_detached_titlebar_view setWindowTitle: title];
935     }
936     if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
937         [o_detached_video_window setTitle: title];
938     [super setTitle: title];
939 }
940
941 - (void)performClose:(id)sender
942 {
943     NSWindow *o_key_window = [NSApp keyWindow];
944
945     if (b_dark_interface)
946     {
947         [o_key_window orderOut: sender];
948         if ( [[VLCMain sharedInstance] activeVideoPlayback] && ( !b_nonembedded || o_key_window != self ))
949             [[VLCCoreInteraction sharedInstance] stop];
950     }
951     else
952     {
953         if( b_nonembedded && o_key_window != self )
954             [o_detached_video_window performClose: sender];
955         else
956             [super performClose: sender];
957     }
958 }
959
960 - (void)performMiniaturize:(id)sender
961 {
962     if (b_dark_interface)
963         [self miniaturize: sender];
964     else
965         [super performMiniaturize: sender];
966 }
967
968 - (void)performZoom:(id)sender
969 {
970     if (b_dark_interface)
971         [self customZoom: sender];
972     else
973         [super performZoom: sender];
974 }
975
976 - (void)zoom:(id)sender
977 {
978     if (b_dark_interface)
979         [self customZoom: sender];
980     else
981         [super zoom: sender];
982 }
983
984 /**
985  * Given a proposed frame rectangle, return a modified version
986  * which will fit inside the screen.
987  *
988  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
989  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
990  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
991  *    Copyright (C) 1996 Free Software Foundation, Inc.
992  */
993 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
994 {
995     NSRect screenRect = [screen visibleFrame];
996     float difference;
997
998     /* Move top edge of the window inside the screen */
999     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
1000     if (difference > 0)
1001     {
1002         frameRect.origin.y -= difference;
1003     }
1004
1005     /* If the window is resizable, resize it (if needed) so that the
1006      bottom edge is on the screen or can be on the screen when the user moves
1007      the window */
1008     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
1009     if (_styleMask & NSResizableWindowMask)
1010     {
1011         float difference2;
1012
1013         difference2 = screenRect.origin.y - frameRect.origin.y;
1014         difference2 -= difference;
1015         // Take in account the space between the top of window and the top of the 
1016         // screen which can be used to move the bottom of the window on the screen
1017         if (difference2 > 0)
1018         {
1019             frameRect.size.height -= difference2;
1020             frameRect.origin.y += difference2;
1021         }
1022
1023         /* Ensure that resizing doesn't makewindow smaller than minimum */
1024         difference2 = [self minSize].height - frameRect.size.height;
1025         if (difference2 > 0)
1026         {
1027             frameRect.size.height += difference2;
1028             frameRect.origin.y -= difference2;
1029         }
1030     }
1031
1032     return frameRect;
1033 }
1034
1035 #define DIST 3
1036
1037 /**
1038  Zooms the receiver.   This method calls the delegate method
1039  windowShouldZoom:toFrame: to determine if the window should
1040  be allowed to zoom to full screen.
1041  *
1042  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1043  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
1044  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1045  *    Copyright (C) 1996 Free Software Foundation, Inc.
1046  */
1047 - (void)customZoom:(id)sender
1048 {
1049     NSRect maxRect = [[self screen] visibleFrame];
1050     NSRect currentFrame = [self frame];
1051
1052     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
1053     {
1054         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
1055     }
1056
1057     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
1058
1059     // Compare the new frame with the current one
1060     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
1061         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
1062         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
1063         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
1064     {
1065         // Already in zoomed mode, reset user frame, if stored
1066         if ([self frameAutosaveName] != nil)
1067         {
1068             [self setFrame: previousSavedFrame display: YES animate: YES];
1069             [self saveFrameUsingName: [self frameAutosaveName]];
1070         }
1071         return;
1072     }
1073
1074     if ([self frameAutosaveName] != nil)
1075     {
1076         [self saveFrameUsingName: [self frameAutosaveName]];
1077         previousSavedFrame = [self frame];
1078     }
1079
1080     [self setFrame: maxRect display: YES animate: YES];
1081 }
1082
1083 - (void)windowResizedOrMoved:(NSNotification *)notification
1084 {
1085     [self saveFrameUsingName: [self frameAutosaveName]];
1086 }
1087
1088 - (void)applicationWillTerminate:(NSNotification *)notification
1089 {
1090     if( config_GetInt( VLCIntf, "macosx-autosave-volume" ))
1091         config_PutInt( VLCIntf->p_libvlc, "volume", i_lastShownVolume );
1092
1093     [self saveFrameUsingName: [self frameAutosaveName]];
1094 }
1095
1096 - (void)someWindowWillClose:(NSNotification *)notification
1097 {
1098     if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1099     {
1100         if ([[VLCMain sharedInstance] activeVideoPlayback])
1101             [[VLCCoreInteraction sharedInstance] stop];
1102     }
1103 }
1104
1105 - (void)someWindowWillMiniaturize:(NSNotification *)notification
1106 {
1107     if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
1108     {
1109         if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1110         {
1111             if([[VLCMain sharedInstance] activeVideoPlayback])
1112                 [[VLCCoreInteraction sharedInstance] pause];
1113         }
1114     }
1115 }
1116
1117 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
1118 {
1119     id videoWindow = [o_video_view window];
1120     if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
1121         return proposedFrameSize;
1122
1123     if( [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked] )
1124     {
1125         NSRect videoWindowFrame = [videoWindow frame];
1126         NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
1127         NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
1128         float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
1129         float marginx = contentRect.size.width - viewRect.size.width;
1130         if( b_dark_interface )
1131             marginy += [o_titlebar_view frame].size.height;
1132
1133         proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
1134     }
1135
1136     return proposedFrameSize;
1137 }
1138
1139 #pragma mark -
1140 #pragma mark Update interface and respond to foreign events
1141 - (void)showDropZone
1142 {
1143     b_dropzone_active = YES;
1144     [o_right_split_view addSubview: o_dropzone_view];
1145     [o_dropzone_view setFrame: [o_playlist_table frame]];
1146     [[o_playlist_table animator] setHidden:YES];
1147 }
1148
1149 - (void)hideDropZone
1150 {
1151     [o_dropzone_view removeFromSuperview];
1152     [[o_playlist_table animator] setHidden: NO];
1153 }
1154
1155 - (void)hideSplitView
1156 {
1157     NSRect winrect = [self frame];
1158     i_lastSplitViewHeight = [o_split_view frame].size.height;
1159     winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
1160     winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
1161     [self setFrame: winrect display: YES animate: YES];
1162     [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
1163     if (b_dark_interface)
1164     {
1165         [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1166         [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1167     }
1168     else
1169     {
1170         [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height )];
1171         [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height )];
1172     }
1173
1174     b_splitview_removed = YES;
1175 }
1176
1177 - (void)showSplitView
1178 {
1179     [self updateWindow];
1180     if (b_dark_interface)
1181         [self setContentMinSize:NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1182     else
1183         [self setContentMinSize:NSMakeSize( 604., 288. )];
1184     [self setContentMaxSize: NSMakeSize( FLT_MAX, FLT_MAX )];
1185
1186     NSRect winrect;
1187     winrect = [self frame];
1188     winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
1189     winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
1190     [self setFrame: winrect display: YES animate: YES];
1191
1192     [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
1193
1194     b_splitview_removed = NO;
1195 }
1196
1197 - (void)updateTimeSlider
1198 {
1199     input_thread_t * p_input;
1200     p_input = pl_CurrentInput( VLCIntf );
1201     if( p_input )
1202     {
1203         vlc_value_t time;
1204         NSString * o_time;
1205         vlc_value_t pos;
1206         char psz_time[MSTRTIME_MAX_SIZE];
1207         float f_updated;
1208
1209         var_Get( p_input, "position", &pos );
1210         f_updated = 10000. * pos.f_float;
1211         [o_time_sld setFloatValue: f_updated];
1212
1213         var_Get( p_input, "time", &time );
1214
1215         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1216         if( [o_time_fld timeRemaining] && dur != -1 )
1217         {
1218             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000))];
1219         }
1220         else
1221             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
1222
1223         if (dur == -1) {
1224             [o_time_sld setEnabled: NO];
1225             [o_time_sld setHidden: YES];
1226             [o_time_sld_fancygradient_view setHidden: YES];
1227         } else {
1228             [o_time_sld setEnabled: YES];
1229             [o_time_sld setHidden: NO];
1230             [o_time_sld_fancygradient_view setHidden: NO];
1231         }
1232
1233         [o_time_fld setStringValue: o_time];
1234         [o_time_fld setNeedsDisplay:YES];
1235         [o_fspanel setStreamPos: f_updated andTime: o_time];
1236         vlc_object_release( p_input );
1237     }
1238     else
1239     {
1240         [o_time_sld setFloatValue: 0.0];
1241         [o_time_fld setStringValue: @"00:00"];
1242         [o_time_sld setEnabled: NO];
1243         [o_time_sld setHidden: YES];
1244         [o_time_sld_fancygradient_view setHidden: YES];
1245         [o_detached_time_sld_fancygradient_view setHidden: YES];
1246     }
1247
1248     [o_detached_time_sld setFloatValue: [o_time_sld floatValue]];
1249     [o_detached_time_sld setEnabled: [o_time_sld isEnabled]];
1250     [o_detached_time_fld setStringValue: [o_time_fld stringValue]];
1251     [o_detached_time_sld setHidden: [o_time_sld isHidden]];
1252 }
1253
1254 - (void)updateVolumeSlider
1255 {
1256     audio_volume_t i_volume;
1257     playlist_t * p_playlist = pl_Get( VLCIntf );
1258
1259     i_volume = aout_VolumeGet( p_playlist );
1260     BOOL b_muted = [[VLCCoreInteraction sharedInstance] isMuted];
1261
1262     if( !b_muted )
1263     {
1264         i_lastShownVolume = i_volume;
1265         [o_volume_sld setIntValue: i_volume];
1266         [o_fspanel setVolumeLevel: i_volume];
1267     }
1268     else
1269         [o_volume_sld setIntValue: 0];
1270
1271     [o_volume_sld setEnabled: !b_muted];
1272     [o_volume_up_btn setEnabled: !b_muted];
1273 }
1274
1275 - (void)updateName
1276 {
1277     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1278     input_thread_t * p_input;
1279     p_input = pl_CurrentInput( VLCIntf );
1280     if( p_input )
1281     {
1282         NSString *aString;
1283         char *format = var_InheritString( VLCIntf, "input-title-format" );
1284         char *formated = str_format_meta( p_input, format );
1285         free( format );
1286         aString = [NSString stringWithUTF8String:formated];
1287         free( formated );
1288
1289         char *uri = input_item_GetURI( input_GetItem( p_input ) );
1290
1291         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1292         if ([o_url isFileURL])
1293             [self setRepresentedURL: o_url];
1294         else
1295             [self setRepresentedURL: nil];
1296         free( uri );
1297
1298         if ([aString isEqualToString:@""])
1299         {
1300             if ([o_url isFileURL])
1301                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1302             else
1303                 aString = [o_url absoluteString];
1304         }
1305
1306         [self setTitle: aString];
1307         [o_fspanel setStreamTitle: aString];
1308         vlc_object_release( p_input );
1309     }
1310     else
1311     {
1312         [self setTitle: _NS("VLC media player")];
1313         [self setRepresentedURL: nil];
1314     }
1315
1316     [o_pool release];
1317 }
1318
1319 - (void)updateWindow
1320 {
1321     bool b_input = false;
1322     bool b_plmul = false;
1323     bool b_control = false;
1324     bool b_seekable = false;
1325     bool b_chapters = false;
1326
1327     playlist_t * p_playlist = pl_Get( VLCIntf );
1328
1329     PL_LOCK;
1330     b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1331     PL_UNLOCK;
1332
1333     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1334
1335     bool b_buffering = NO;
1336
1337     if( ( b_input = ( p_input != NULL ) ) )
1338     {
1339         /* seekable streams */
1340         cachedInputState = input_GetState( p_input );
1341         if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1342             b_buffering = YES;
1343
1344         /* seekable streams */
1345         b_seekable = var_GetBool( p_input, "can-seek" );
1346
1347         /* check whether slow/fast motion is possible */
1348         b_control = var_GetBool( p_input, "can-rate" );
1349
1350         /* chapters & titles */
1351         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1352
1353         if (( cachedInputState == PLAYING_S || b_buffering == YES ) && [[VLCMain sharedInstance] activeVideoPlayback] )
1354             [[o_video_view window] makeKeyAndOrderFront: nil];
1355
1356         vlc_object_release( p_input );
1357     }
1358
1359     if( b_buffering )
1360     {
1361         [o_progress_bar startAnimation:self];
1362         [o_progress_bar setIndeterminate:YES];
1363         [o_progress_bar setHidden:NO];
1364     } else {
1365         [o_progress_bar stopAnimation:self];
1366         [o_progress_bar setHidden:YES];
1367     }
1368
1369     [o_stop_btn setEnabled: b_input];
1370     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1371     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1372     [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1373     [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1374     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1375
1376     [o_time_sld setEnabled: b_seekable];
1377     [self updateTimeSlider];
1378     [o_fspanel setSeekable: b_seekable];
1379
1380     PL_LOCK;
1381     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1382         [self hideDropZone];
1383     else
1384         [self showDropZone];
1385     PL_UNLOCK;
1386     [o_sidebar_view setNeedsDisplay:YES];
1387 }
1388
1389 - (void)setPause
1390 {
1391     [o_play_btn setImage: o_pause_img];
1392     [o_play_btn setAlternateImage: o_pause_pressed_img];
1393     [o_play_btn setToolTip: _NS("Pause")];
1394     [o_detached_play_btn setImage: o_pause_img];
1395     [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1396     [o_detached_play_btn setToolTip: _NS("Pause")];
1397     [o_fspanel setPause];
1398 }
1399
1400 - (void)setPlay
1401 {
1402     [o_play_btn setImage: o_play_img];
1403     [o_play_btn setAlternateImage: o_play_pressed_img];
1404     [o_play_btn setToolTip: _NS("Play")];
1405     [o_detached_play_btn setImage: o_play_img];
1406     [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1407     [o_detached_play_btn setToolTip: _NS("Play")];
1408     [o_fspanel setPlay];
1409 }
1410
1411 - (void)drawFancyGradientEffectForTimeSlider
1412 {
1413     if (OSX_LEOPARD)
1414         return;
1415
1416     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1417     CGFloat f_value = [o_time_sld knobPosition];
1418     if (f_value > 7.5)
1419     {
1420         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1421         if (f_value != oldFrame.size.width)
1422         {
1423             if ([o_time_sld_fancygradient_view isHidden])
1424                 [o_time_sld_fancygradient_view setHidden: NO];
1425             [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1426         }
1427
1428         if (b_nonembedded)
1429         {
1430             f_value = [o_detached_time_sld knobPosition];
1431             oldFrame = [o_detached_time_sld_fancygradient_view frame];
1432             if (f_value != oldFrame.size.width)
1433             {
1434                 if ([o_detached_time_sld_fancygradient_view isHidden])
1435                     [o_detached_time_sld_fancygradient_view setHidden: NO];
1436                 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1437             }
1438         }
1439     }
1440     else
1441     {
1442         NSRect frame;
1443         frame = [o_time_sld_fancygradient_view frame];
1444         if (frame.size.width > 0)
1445         {
1446             frame.size.width = 0;
1447             [o_time_sld_fancygradient_view setFrame: frame];
1448
1449             frame = [o_detached_time_sld_fancygradient_view frame];
1450             frame.size.width = 0;
1451             [o_detached_time_sld_fancygradient_view setFrame: frame];
1452         }
1453         [o_time_sld_fancygradient_view setHidden: YES];
1454         [o_detached_time_sld_fancygradient_view setHidden: YES];
1455     }
1456     [o_pool release];
1457 }
1458
1459 #pragma mark -
1460 #pragma mark Video Output handling
1461 - (id)videoView
1462 {
1463     return o_video_view;
1464 }
1465
1466 - (id)setupVideoView
1467 {
1468     vout_thread_t *p_vout = getVout();
1469     if (config_GetInt( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode)
1470     {
1471         if ([o_video_view window] != self)
1472         {
1473             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1474             [o_video_view setFrame: [o_split_view frame]];
1475             [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1476         }
1477         b_nonembedded = NO;
1478     }
1479     else
1480     {
1481         if ([o_video_view superview] != NULL)
1482             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1483
1484         NSRect frame = [o_detached_video_window frame];
1485         NSRect videoFrame = [o_video_view frame];
1486         frame.size.width = videoFrame.size.width;
1487         frame.size.height = videoFrame.size.height + [o_detached_bottombar_view frame].size.height + [o_titlebar_view frame].size.height;
1488         [o_detached_video_window setFrame: frame display: NO];
1489         [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1490         [o_detached_video_window setLevel:NSNormalWindowLevel];
1491         [o_detached_video_window useOptimizedDrawing: YES];
1492         [o_detached_video_window center];
1493         b_nonembedded = YES;
1494     }
1495     [[o_video_view window] makeKeyAndOrderFront: self];
1496     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
1497
1498     if (p_vout)
1499     {
1500         if( var_GetBool( p_vout, "video-on-top" ) )
1501             [[o_video_view window] setLevel: NSStatusWindowLevel];
1502         else
1503             [[o_video_view window] setLevel: NSNormalWindowLevel];
1504         vlc_object_release( p_vout );
1505     }
1506     return o_video_view;
1507 }
1508
1509 - (void)setVideoplayEnabled
1510 {
1511     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1512
1513     if (!b_videoPlayback)
1514     {
1515         [o_detached_video_window orderOut: nil];
1516
1517         // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
1518         [self setAlphaValue:1.0];
1519     }
1520
1521     if( b_nativeFullscreenMode )
1522     {
1523         if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1524             [o_bottombar_view setHidden: b_videoPlayback];
1525         else
1526             [o_bottombar_view setHidden: NO];
1527         if (!b_videoPlayback)
1528             [o_fspanel setNonActive: nil];
1529     }
1530     if (b_videoPlayback)
1531         [self makeFirstResponder: o_video_view];
1532     else
1533         [self makeFirstResponder: nil];
1534
1535     if (!b_videoPlayback && b_fullscreen)
1536     {
1537         if (!b_nativeFullscreenMode)
1538             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1539     }
1540 }
1541
1542 - (void)resizeWindow
1543 {
1544     if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
1545         return;
1546
1547     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1548     NSSize windowMinSize = [o_videoWindow minSize];
1549     NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
1550
1551     NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
1552     NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
1553
1554     unsigned int i_width = nativeVideoSize.width;
1555     unsigned int i_height = nativeVideoSize.height;
1556     if (i_width < windowMinSize.width)
1557         i_width = windowMinSize.width;
1558     if (i_height < f_min_video_height)
1559         i_height = f_min_video_height;
1560
1561     /* Calculate the window's new size */
1562     NSRect new_frame;
1563     new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
1564     new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
1565     new_frame.origin.x = topleftscreen.x;
1566     new_frame.origin.y = topleftscreen.y - new_frame.size.height;
1567
1568     /* make sure the window doesn't exceed the screen size the window is on */
1569     if( new_frame.size.width > screenFrame.size.width )
1570     {
1571         new_frame.size.width = screenFrame.size.width;
1572         new_frame.origin.x = screenFrame.origin.x;
1573     }
1574     if( new_frame.size.height > screenFrame.size.height )
1575     {
1576         new_frame.size.height = screenFrame.size.height;
1577         new_frame.origin.y = screenFrame.origin.y;
1578     }
1579     if( new_frame.origin.y < screenFrame.origin.y )
1580         new_frame.origin.y = screenFrame.origin.y;
1581
1582     [[o_videoWindow animator] setFrame:new_frame display:YES];
1583 }
1584
1585 - (void)setNativeVideoSize:(NSSize)size
1586 {
1587     nativeVideoSize = size;
1588
1589     if( config_GetInt( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen )
1590         [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
1591 }
1592
1593 //  Called automatically if window's acceptsMouseMovedEvents property is true
1594 - (void)mouseMoved:(NSEvent *)theEvent
1595 {
1596     if (b_fullscreen)
1597         [self recreateHideMouseTimer];
1598
1599     [super mouseMoved: theEvent];
1600 }
1601
1602 - (void)recreateHideMouseTimer
1603 {
1604     if (t_hide_mouse_timer != nil) {
1605         [t_hide_mouse_timer invalidate];
1606         [t_hide_mouse_timer release];
1607     }
1608
1609     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
1610                                                           target:self
1611                                                         selector:@selector(hideMouseCursor:)
1612                                                         userInfo:nil
1613                                                          repeats:NO];
1614     [t_hide_mouse_timer retain];
1615 }
1616
1617 //  NSTimer selectors require this function signature as per Apple's docs
1618 - (void)hideMouseCursor:(NSTimer *)timer
1619 {
1620     [NSCursor setHiddenUntilMouseMoves: YES];
1621 }
1622
1623 #pragma mark -
1624 #pragma mark Fullscreen support
1625 - (void)showFullscreenController
1626 {
1627      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
1628         [o_fspanel fadeIn];
1629 }
1630
1631 - (void)updateFullscreen
1632 {
1633     [[VLCMain sharedInstance] fullscreenChanged];
1634 }
1635
1636 - (BOOL)isFullscreen
1637 {
1638     return b_fullscreen;
1639 }
1640
1641 - (void)lockFullscreenAnimation
1642 {
1643     [o_animation_lock lock];
1644 }
1645
1646 - (void)unlockFullscreenAnimation
1647 {
1648     [o_animation_lock unlock];
1649 }
1650
1651 - (void)enterFullscreen
1652 {
1653     NSMutableDictionary *dict1, *dict2;
1654     NSScreen *screen;
1655     NSRect screen_rect;
1656     NSRect rect;
1657     vout_thread_t *p_vout = getVout();
1658     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1659     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1660
1661     if( p_vout )
1662         screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)config_GetInt( VLCIntf, "macosx-vdev" )];
1663
1664     [self lockFullscreenAnimation];
1665
1666     if (!screen)
1667     {
1668         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
1669         screen = [o_videoWindow screen];
1670     }
1671     if (!screen)
1672     {
1673         msg_Dbg( VLCIntf, "Using deepest screen" );
1674         screen = [NSScreen deepestScreen];
1675     }
1676
1677     if( p_vout )
1678         vlc_object_release( p_vout );
1679
1680     screen_rect = [screen frame];
1681
1682     [o_fullscreen_btn setState: YES];
1683     [o_detached_fullscreen_btn setState: YES];
1684
1685     [self recreateHideMouseTimer];
1686
1687     if( blackout_other_displays )
1688         [screen blackoutOtherScreens];
1689
1690     /* Make sure we don't see the window flashes in float-on-top mode */
1691     i_originalLevel = [o_videoWindow level];
1692     [o_videoWindow setLevel:NSNormalWindowLevel];
1693
1694     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
1695     if (!o_fullscreen_window)
1696     {
1697         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
1698
1699         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
1700         rect.origin.x += [o_videoWindow frame].origin.x;
1701         rect.origin.y += [o_videoWindow frame].origin.y;
1702         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
1703         [o_fullscreen_window setFullscreen: YES];
1704         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
1705         [o_fullscreen_window setCanBecomeKeyWindow: YES];
1706
1707         if (![o_videoWindow isVisible] || [o_videoWindow alphaValue] == 0.0)
1708         {
1709             /* We don't animate if we are not visible, instead we
1710              * simply fade the display */
1711             CGDisplayFadeReservationToken token;
1712
1713             if( blackout_other_displays )
1714             {
1715                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1716                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1717             }
1718
1719             if ([screen isMainScreen])
1720             {
1721                 if (OSX_LEOPARD)
1722                     SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1723                 else
1724                     [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1725             }
1726
1727             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1728             [o_temp_view setFrame:[o_video_view frame]];
1729             [o_fullscreen_window setContentView:o_video_view];
1730
1731             [o_fullscreen_window makeKeyAndOrderFront:self];
1732             [o_fullscreen_window orderFront:self animate:YES];
1733
1734             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
1735             [o_fullscreen_window setLevel:NSNormalWindowLevel];
1736
1737             if( blackout_other_displays )
1738             {
1739                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1740                 CGReleaseDisplayFadeReservation( token );
1741             }
1742
1743             /* Will release the lock */
1744             [self hasBecomeFullscreen];
1745
1746             return;
1747         }
1748
1749         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1750         NSDisableScreenUpdates();
1751         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1752         [o_temp_view setFrame:[o_video_view frame]];
1753         [o_fullscreen_window setContentView:o_video_view];
1754         [o_fullscreen_window makeKeyAndOrderFront:self];
1755         NSEnableScreenUpdates();
1756     }
1757
1758     /* We are in fullscreen (and no animation is running) */
1759     if (b_fullscreen)
1760     {
1761         /* Make sure we are hidden */
1762         [o_videoWindow orderOut: self];
1763
1764         [self unlockFullscreenAnimation];
1765         return;
1766     }
1767
1768     if (o_fullscreen_anim1)
1769     {
1770         [o_fullscreen_anim1 stopAnimation];
1771         [o_fullscreen_anim1 release];
1772     }
1773     if (o_fullscreen_anim2)
1774     {
1775         [o_fullscreen_anim2 stopAnimation];
1776         [o_fullscreen_anim2 release];
1777     }
1778
1779     if ([screen isMainScreen])
1780     {
1781         if (OSX_LEOPARD)
1782             SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1783         else
1784             [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1785     }
1786
1787     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
1788     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
1789
1790     [dict1 setObject:o_videoWindow forKey:NSViewAnimationTargetKey];
1791     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
1792
1793     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1794     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1795     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
1796
1797     /* Strategy with NSAnimation allocation:
1798      - Keep at most 2 animation at a time
1799      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
1800      */
1801     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1802     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1803
1804     [dict1 release];
1805     [dict2 release];
1806
1807     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1808     [o_fullscreen_anim1 setDuration: 0.3];
1809     [o_fullscreen_anim1 setFrameRate: 30];
1810     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1811     [o_fullscreen_anim2 setDuration: 0.2];
1812     [o_fullscreen_anim2 setFrameRate: 30];
1813
1814     [o_fullscreen_anim2 setDelegate: self];
1815     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1816
1817     [o_fullscreen_anim1 startAnimation];
1818     /* fullscreenAnimation will be unlocked when animation ends */
1819 }
1820
1821 - (void)hasBecomeFullscreen
1822 {
1823     [o_fullscreen_window makeFirstResponder: o_video_view];
1824
1825     [o_fullscreen_window makeKeyWindow];
1826     [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE];
1827
1828     /* tell the fspanel to move itself to front next time it's triggered */
1829     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
1830     [o_fspanel setActive: nil];
1831
1832     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1833     if( [o_videoWindow isVisible] )
1834         [o_videoWindow orderOut: self];
1835
1836     [o_fspanel setActive: nil];
1837
1838     b_fullscreen = YES;
1839     [self unlockFullscreenAnimation];
1840 }
1841
1842 - (void)leaveFullscreen
1843 {
1844     [self leaveFullscreenAndFadeOut: NO];
1845 }
1846
1847 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1848 {
1849     NSMutableDictionary *dict1, *dict2;
1850     NSRect frame;
1851     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1852
1853     [self lockFullscreenAnimation];
1854
1855     [o_fullscreen_btn setState: NO];
1856     [o_detached_fullscreen_btn setState: NO];
1857
1858     /* We always try to do so */
1859     [NSScreen unblackoutScreens];
1860
1861     vout_thread_t *p_vout = getVout();
1862     if (p_vout)
1863     {
1864         if( var_GetBool( p_vout, "video-on-top" ) )
1865             [[o_video_view window] setLevel: NSStatusWindowLevel];
1866         else
1867             [[o_video_view window] setLevel: NSNormalWindowLevel];
1868         vlc_object_release( p_vout );
1869     }
1870     [[o_video_view window] makeKeyAndOrderFront: nil];
1871
1872     /* Don't do anything if o_fullscreen_window is already closed */
1873     if (!o_fullscreen_window)
1874     {
1875         [self unlockFullscreenAnimation];
1876         return;
1877     }
1878
1879     if (fadeout)
1880     {
1881         /* We don't animate if we are not visible, instead we
1882          * simply fade the display */
1883         CGDisplayFadeReservationToken token;
1884
1885         if( blackout_other_displays )
1886         {
1887             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1888             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1889         }
1890
1891         [o_fspanel setNonActive: nil];
1892         if (OSX_LEOPARD)
1893             SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1894         else
1895             [NSApp setPresentationOptions: NSApplicationPresentationDefault];
1896
1897         /* Will release the lock */
1898         [self hasEndedFullscreen];
1899
1900         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1901          * here */
1902         b_window_is_invisible = YES;
1903
1904         if( blackout_other_displays )
1905         {
1906             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1907             CGReleaseDisplayFadeReservation( token );
1908         }
1909
1910         return;
1911     }
1912
1913     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1914
1915     [o_videoWindow setAlphaValue: 0.0];
1916     [o_videoWindow orderFront: self];
1917     [[o_video_view window] orderFront: self];
1918
1919     [o_fspanel setNonActive: nil];
1920     if (OSX_LEOPARD)
1921         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1922     else
1923         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1924
1925     if (o_fullscreen_anim1)
1926     {
1927         [o_fullscreen_anim1 stopAnimation];
1928         [o_fullscreen_anim1 release];
1929     }
1930     if (o_fullscreen_anim2)
1931     {
1932         [o_fullscreen_anim2 stopAnimation];
1933         [o_fullscreen_anim2 release];
1934     }
1935
1936     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1937     frame.origin.x += [o_videoWindow frame].origin.x;
1938     frame.origin.y += [o_videoWindow frame].origin.y;
1939
1940     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1941     [dict2 setObject:o_videoWindow forKey:NSViewAnimationTargetKey];
1942     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1943
1944     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
1945     [dict2 release];
1946
1947     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1948     [o_fullscreen_anim2 setDuration: 0.3];
1949     [o_fullscreen_anim2 setFrameRate: 30];
1950
1951     [o_fullscreen_anim2 setDelegate: self];
1952
1953     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1954
1955     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1956     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1957     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1958
1959     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
1960     [dict1 release];
1961
1962     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1963     [o_fullscreen_anim1 setDuration: 0.2];
1964     [o_fullscreen_anim1 setFrameRate: 30];
1965     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1966
1967     /* Make sure o_fullscreen_window is the frontmost window */
1968     [o_fullscreen_window orderFront: self];
1969
1970     [o_fullscreen_anim1 startAnimation];
1971     /* fullscreenAnimation will be unlocked when animation ends */
1972 }
1973
1974 - (void)hasEndedFullscreen
1975 {
1976     b_fullscreen = NO;
1977
1978     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1979     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1980     NSDisableScreenUpdates();
1981     [o_video_view retain];
1982     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1983     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1984     [o_video_view release];
1985     [o_video_view setFrame:[o_temp_view frame]];
1986     [[o_video_view window] makeFirstResponder: o_video_view];
1987     if( [[o_video_view window] isVisible] )
1988     {
1989         if( !b_nonembedded )
1990             [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
1991         else
1992             [[o_video_view window] makeKeyAndOrderFront: self];
1993     }
1994     [o_fullscreen_window orderOut: self];
1995     NSEnableScreenUpdates();
1996
1997     [o_fullscreen_window release];
1998     o_fullscreen_window = nil;
1999     [[o_video_view window] setLevel:i_originalLevel];
2000     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2001
2002     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2003     if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2004         [o_detached_video_window orderOut: self];
2005
2006     [self unlockFullscreenAnimation];
2007 }
2008
2009 - (void)animationDidEnd:(NSAnimation*)animation
2010 {
2011     NSArray *viewAnimations;
2012     if( o_makekey_anim == animation )
2013     {
2014         [o_makekey_anim release];
2015         return;
2016     }
2017     if ([animation currentValue] < 1.0)
2018         return;
2019
2020     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2021     viewAnimations = [o_fullscreen_anim2 viewAnimations];
2022     if ([viewAnimations count] >=1 &&
2023         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2024     {
2025         /* Fullscreen ended */
2026         [self hasEndedFullscreen];
2027     }
2028     else
2029     {
2030         /* Fullscreen started */
2031         [self hasBecomeFullscreen];
2032     }
2033 }
2034
2035 - (void)makeKeyAndOrderFront: (id)sender
2036 {
2037     /* Hack
2038      * when we exit fullscreen and fade out, we may endup in
2039      * having a window that is faded. We can't have it fade in unless we
2040      * animate again. */
2041
2042     if(!b_window_is_invisible)
2043     {
2044         /* Make sure we don't do it too much */
2045         [super makeKeyAndOrderFront: sender];
2046         return;
2047     }
2048
2049     [super setAlphaValue:0.0f];
2050     [super makeKeyAndOrderFront: sender];
2051
2052     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2053     [dict setObject:self forKey:NSViewAnimationTargetKey];
2054     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2055
2056     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2057     [dict release];
2058
2059     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2060     [o_makekey_anim setDuration: 0.1];
2061     [o_makekey_anim setFrameRate: 30];
2062     [o_makekey_anim setDelegate: self];
2063
2064     [o_makekey_anim startAnimation];
2065     b_window_is_invisible = NO;
2066
2067     /* fullscreenAnimation will be unlocked when animation ends */
2068 }
2069
2070 #pragma mark -
2071 #pragma mark Lion native fullscreen handling
2072 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2073 {
2074     [o_video_view setFrame: [[self contentView] frame]];
2075     b_fullscreen = YES;
2076     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2077     [o_fspanel setActive: nil];
2078
2079     [self recreateHideMouseTimer];
2080     i_originalLevel = [self level];
2081     [self setLevel:NSNormalWindowLevel];
2082
2083     if (b_dark_interface)
2084     {
2085         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2086
2087         NSRect winrect;
2088         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2089         winrect = [self frame];
2090
2091         winrect.size.height = winrect.size.height - f_titleBarHeight;
2092         [self setFrame: winrect display:NO animate:NO];
2093         winrect = [o_split_view frame];
2094         winrect.size.height = winrect.size.height + f_titleBarHeight;
2095         [o_split_view setFrame: winrect];
2096     }
2097
2098     if ([[VLCMain sharedInstance] activeVideoPlayback])
2099         [o_bottombar_view setHidden: YES];
2100 }
2101
2102 - (void)windowWillExitFullScreen:(NSNotification *)notification
2103 {
2104     [o_video_view setFrame: [o_split_view frame]];
2105     [NSCursor setHiddenUntilMouseMoves: NO];
2106     [o_fspanel setNonActive: nil];
2107     [self setLevel:i_originalLevel];
2108     b_fullscreen = NO;
2109
2110     if (b_dark_interface)
2111     {
2112         NSRect winrect;
2113         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2114         winrect = [self frame];
2115
2116         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2117                                               winrect.size.width, f_titleBarHeight )];
2118         [[self contentView] addSubview: o_titlebar_view];
2119
2120         winrect.size.height = winrect.size.height + f_titleBarHeight;
2121         [self setFrame: winrect display:NO animate:NO];
2122         winrect = [o_split_view frame];
2123         winrect.size.height = winrect.size.height - f_titleBarHeight;
2124         [o_split_view setFrame: winrect];
2125         [o_video_view setFrame: winrect];
2126     }
2127
2128     if ([[VLCMain sharedInstance] activeVideoPlayback])
2129         [o_bottombar_view setHidden: NO];
2130 }
2131
2132 #pragma mark -
2133 #pragma mark split view delegate
2134 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2135 {
2136     if (dividerIndex == 0)
2137         return 300.;
2138     else
2139         return proposedMax;
2140 }
2141
2142 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2143 {
2144     return ([subview isEqual:o_left_split_view]);
2145 }
2146
2147 #pragma mark -
2148 #pragma mark Side Bar Data handling
2149 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2150 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2151 {
2152     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2153     if(item==nil)
2154         return [o_sidebaritems count];
2155     else
2156         return [[item children] count];
2157 }
2158
2159
2160 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2161 {
2162     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2163     if(item==nil)
2164         return [o_sidebaritems objectAtIndex:index];
2165     else
2166         return [[item children] objectAtIndex:index];
2167 }
2168
2169
2170 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2171 {
2172     return [item title];
2173 }
2174
2175 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2176 {
2177     [item setTitle:object];
2178 }
2179
2180 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2181 {
2182     return [item hasChildren];
2183 }
2184
2185
2186 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2187 {
2188     if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2189         return YES;
2190
2191     return [item hasBadge];
2192 }
2193
2194
2195 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2196 {
2197     playlist_t * p_playlist = pl_Get( VLCIntf );
2198     NSInteger i_playlist_size;
2199
2200     if ([[item identifier] isEqualToString: @"playlist"])
2201     {
2202         PL_LOCK;
2203         i_playlist_size = p_playlist->p_local_category->i_children;
2204         PL_UNLOCK;
2205
2206         return i_playlist_size;
2207     }
2208     if ([[item identifier] isEqualToString: @"medialibrary"])
2209     {
2210         PL_LOCK;
2211         i_playlist_size = p_playlist->p_ml_category->i_children;
2212         PL_UNLOCK;
2213
2214         return i_playlist_size;
2215     }
2216
2217     return [item badgeValue];
2218 }
2219
2220
2221 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2222 {
2223     return [item hasIcon];
2224 }
2225
2226
2227 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2228 {
2229     return [item icon];
2230 }
2231
2232 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2233 {
2234     if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2235     {
2236         if (item != nil)
2237         {
2238             NSMenu * m;
2239             if ([item sdtype] > 0)
2240             {
2241                 m = [[NSMenu alloc] init];
2242                 playlist_t * p_playlist = pl_Get( VLCIntf );
2243                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2244                 if (!sd_loaded)
2245                     [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2246                 else
2247                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2248                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2249             }
2250             return [m autorelease];
2251         }
2252     }
2253
2254     return nil;
2255 }
2256
2257 - (IBAction)sdmenuhandler:(id)sender
2258 {
2259     NSString * identifier = [sender representedObject];
2260     if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2261     {
2262         playlist_t * p_playlist = pl_Get( VLCIntf );
2263         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2264
2265         if (!sd_loaded)
2266             playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2267         else
2268             playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2269     }
2270 }
2271
2272 #pragma mark -
2273 #pragma mark Side Bar Delegate Methods
2274 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2275 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2276 {
2277     if ([[group identifier] isEqualToString:@"library"])
2278         return YES;
2279
2280     return NO;
2281 }
2282
2283 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2284 {
2285     playlist_t * p_playlist = pl_Get( VLCIntf );
2286
2287     NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2288     id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2289
2290
2291     //Set the label text to represent the new selection
2292     if ([item sdtype] > -1 && [[item identifier] length] > 0)
2293     {
2294         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2295         if (!sd_loaded)
2296         {
2297             playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2298         }
2299     }
2300
2301     [o_chosen_category_lbl setStringValue:[item title]];
2302
2303     if ([[item identifier] isEqualToString:@"playlist"])
2304     {
2305         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2306     }
2307     else if([[item identifier] isEqualToString:@"medialibrary"])
2308     {
2309         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2310     }
2311     else
2312     {
2313         playlist_item_t * pl_item;
2314         PL_LOCK;
2315         pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2316         PL_UNLOCK;
2317         [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2318     }
2319
2320     PL_LOCK;
2321     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2322         [self hideDropZone];
2323     else
2324         [self showDropZone];
2325     PL_UNLOCK;
2326 }
2327
2328 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2329 {
2330     if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2331     {
2332         NSPasteboard *o_pasteboard = [info draggingPasteboard];
2333         if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2334             return NSDragOperationGeneric;
2335     }
2336     return NSDragOperationNone;
2337 }
2338
2339 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2340 {
2341     NSPasteboard *o_pasteboard = [info draggingPasteboard];
2342
2343     playlist_t * p_playlist = pl_Get( VLCIntf );
2344     playlist_item_t *p_node;
2345
2346     if ([[item identifier] isEqualToString:@"playlist"])
2347         p_node = p_playlist->p_local_category;
2348     else
2349         p_node = p_playlist->p_ml_category;
2350
2351     if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2352     {
2353         NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2354         NSUInteger count = [o_values count];
2355         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2356
2357         for( NSUInteger i = 0; i < count; i++)
2358         {
2359             NSDictionary *o_dic;
2360             char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
2361             if( !psz_uri )
2362                 continue;
2363
2364             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2365
2366             free( psz_uri );
2367
2368             [o_array addObject: o_dic];
2369         }
2370
2371         [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2372         return YES;
2373     }
2374     else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2375     {
2376         NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2377
2378         NSUInteger count = [array count];
2379         playlist_item_t * p_item = NULL;
2380
2381         PL_LOCK;
2382         for( NSUInteger i = 0; i < count; i++ )
2383         {
2384             p_item = [[array objectAtIndex:i] pointerValue];
2385             if( !p_item ) continue;
2386             playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2387         }
2388         PL_UNLOCK;
2389
2390         return YES;
2391     }
2392     return NO;
2393 }
2394
2395 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2396 {
2397     return [item identifier];
2398 }
2399
2400 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2401 {
2402     /* the following code assumes for sakes of simplicity that only the top level
2403      * items are allowed to have children */
2404
2405     NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2406     NSUInteger count = [array count];
2407     if (count < 1)
2408         return nil;
2409
2410     for (NSUInteger x = 0; x < count; x++)
2411     {
2412         id item = [array objectAtIndex: x]; // save one objc selector call
2413         if ([[item identifier] isEqualToString:object])
2414             return item;
2415     }
2416
2417     return nil;
2418 }
2419 @end
2420
2421 @implementation VLCDetachedVideoWindow
2422
2423 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
2424                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
2425 {
2426     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
2427
2428     if (b_dark_interface)
2429     {
2430 #ifdef MAC_OS_X_VERSION_10_7
2431         if (OSX_LION)
2432             styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
2433         else
2434             styleMask = NSBorderlessWindowMask;
2435 #else
2436         styleMask = NSBorderlessWindowMask;
2437 #endif
2438     }
2439
2440     self = [super initWithContentRect:contentRect styleMask:styleMask
2441                               backing:backingType defer:flag];
2442
2443     /* we want to be moveable regardless of our style */
2444     [self setMovableByWindowBackground: YES];
2445
2446     /* we don't want this window to be restored on relaunch */
2447     if (OSX_LION)
2448         [self setRestorable:NO];
2449
2450     return self;
2451 }
2452
2453 - (void)awakeFromNib
2454 {
2455     if (b_dark_interface)
2456     {
2457         [self setBackgroundColor: [NSColor clearColor]];
2458         [self setOpaque: NO];
2459         [self display];
2460         [self setHasShadow:NO];
2461         [self setHasShadow:YES];
2462     }
2463 }
2464
2465 - (IBAction)fullscreen:(id)sender
2466 {
2467     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
2468 }
2469
2470 - (void)updateFullscreen
2471 {
2472     [[VLCMain sharedInstance] fullscreenChanged];
2473 }
2474
2475 - (BOOL)isFullscreen
2476 {
2477     return [[VLCMainWindow sharedInstance] isFullscreen];
2478 }
2479
2480 - (void)performClose:(id)sender
2481 {
2482     if (b_dark_interface)
2483         [[VLCMainWindow sharedInstance] performClose: sender];
2484     else
2485         [super performClose: sender];
2486 }
2487
2488 - (void)performMiniaturize:(id)sender
2489 {
2490     if (b_dark_interface)
2491         [self miniaturize: sender];
2492     else
2493         [super performMiniaturize: sender];
2494 }
2495
2496 - (void)performZoom:(id)sender
2497 {
2498     if (b_dark_interface)
2499         [self customZoom: sender];
2500     else
2501         [super performZoom: sender];
2502 }
2503
2504 - (void)zoom:(id)sender
2505 {
2506     if (b_dark_interface)
2507         [self customZoom: sender];
2508     else
2509         [super zoom: sender];
2510 }
2511
2512 - (BOOL)canBecomeKeyWindow
2513 {
2514     return YES;
2515 }
2516
2517 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
2518 {
2519     SEL s_menuAction = [menuItem action];
2520
2521     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
2522         return YES;
2523
2524     return [super validateMenuItem:menuItem];
2525 }
2526
2527 /**
2528  * Given a proposed frame rectangle, return a modified version
2529  * which will fit inside the screen.
2530  *
2531  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
2532  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
2533  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
2534  *    Copyright (C) 1996 Free Software Foundation, Inc.
2535  */
2536 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
2537 {
2538     NSRect screenRect = [screen visibleFrame];
2539     float difference;
2540
2541     /* Move top edge of the window inside the screen */
2542     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
2543     if (difference > 0)
2544     {
2545         frameRect.origin.y -= difference;
2546     }
2547
2548     /* If the window is resizable, resize it (if needed) so that the
2549      bottom edge is on the screen or can be on the screen when the user moves
2550      the window */
2551     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
2552     if (_styleMask & NSResizableWindowMask)
2553     {
2554         float difference2;
2555
2556         difference2 = screenRect.origin.y - frameRect.origin.y;
2557         difference2 -= difference;
2558         // Take in account the space between the top of window and the top of the 
2559         // screen which can be used to move the bottom of the window on the screen
2560         if (difference2 > 0)
2561         {
2562             frameRect.size.height -= difference2;
2563             frameRect.origin.y += difference2;
2564         }
2565
2566         /* Ensure that resizing doesn't makewindow smaller than minimum */
2567         difference2 = [self minSize].height - frameRect.size.height;
2568         if (difference2 > 0)
2569         {
2570             frameRect.size.height += difference2;
2571             frameRect.origin.y -= difference2;
2572         }
2573     }
2574
2575     return frameRect;
2576 }
2577
2578 #define DIST 3
2579
2580 /**
2581  Zooms the receiver.   This method calls the delegate method
2582  windowShouldZoom:toFrame: to determine if the window should
2583  be allowed to zoom to full screen.
2584  *
2585  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
2586  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
2587  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
2588  *    Copyright (C) 1996 Free Software Foundation, Inc.
2589  */
2590 - (void) customZoom: (id)sender
2591 {
2592     NSRect maxRect = [[self screen] visibleFrame];
2593     NSRect currentFrame = [self frame];
2594
2595     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
2596     {
2597         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
2598     }
2599
2600     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
2601
2602     // Compare the new frame with the current one
2603     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
2604         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
2605         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
2606         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
2607     {
2608         // Already in zoomed mode, reset user frame, if stored
2609         if ([self frameAutosaveName] != nil)
2610         {
2611             [self setFrame: previousSavedFrame display: YES animate: YES];
2612             [self saveFrameUsingName: [self frameAutosaveName]];
2613         }
2614         return;
2615     }
2616
2617     if ([self frameAutosaveName] != nil)
2618     {
2619         [self saveFrameUsingName: [self frameAutosaveName]];
2620         previousSavedFrame = [self frame];
2621     }
2622
2623     [self setFrame: maxRect display: YES animate: YES];
2624 }
2625
2626 @end