]> git.sesse.net Git - vlc/blobdiff - modules/gui/macosx/MainWindow.m
macosx: fix memleaks in podcast handling, remove unnecessary module restart
[vlc] / modules / gui / macosx / MainWindow.m
index 49c284487af74aca5930dbcf04d2b99bf4188c3a..1adfe4e0bbfcf4599101d9ed7125ba59f5542222 100644 (file)
@@ -1,13 +1,14 @@
 /*****************************************************************************
  * MainWindow.m: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2002-2012 VLC authors and VideoLAN
+ * Copyright (C) 2002-2013 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
  *          Jon Lech Johansen <jon-vl@nanocrew.net>
  *          Christophe Massiot <massiot@via.ecp.fr>
  *          Derk-Jan Hartman <hartman at videolan.org>
+ *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,9 +25,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
+#import "intf.h"
 #import "CompatibilityFixes.h"
 #import "MainWindow.h"
-#import "intf.h"
 #import "CoreInteraction.h"
 #import "AudioEffects.h"
 #import "MainMenu.h"
 - (void)hidePodcastControls;
 @end
 
+static const float f_min_window_height = 307.;
 
 @implementation VLCMainWindow
 
-@synthesize fullscreen=b_fullscreen;
 @synthesize nativeFullscreenMode=b_nativeFullscreenMode;
 @synthesize nonembedded=b_nonembedded;
 @synthesize fsPanel=o_fspanel;
@@ -122,7 +123,11 @@ static VLCMainWindow *_o_sharedInstance = nil;
     // these are key events which should be handled by vlc core, but are attached to a main menu item
     if (![self isEvent: o_event forKey: "key-vol-up"] &&
         ![self isEvent: o_event forKey: "key-vol-down"] &&
-        ![self isEvent: o_event forKey: "key-vol-mute"]) {
+        ![self isEvent: o_event forKey: "key-vol-mute"] &&
+        ![self isEvent: o_event forKey: "key-prev"] &&
+        ![self isEvent: o_event forKey: "key-next"] &&
+        ![self isEvent: o_event forKey: "key-jump+short"] &&
+        ![self isEvent: o_event forKey: "key-jump-short"]) {
         /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
          so we perform the menu equivalent now. */
         if ([[NSApp mainMenu] performKeyEquivalent:o_event])
@@ -142,6 +147,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
     [[NSNotificationCenter defaultCenter] removeObserver: self];
     [o_sidebaritems release];
+    [o_fspanel release];
 
     [super dealloc];
 }
@@ -153,13 +159,16 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
     BOOL b_splitviewShouldBeHidden = NO;
 
+    if (!OSX_SNOW_LEOPARD)
+        [self setRestorable: NO];
+    [self setFrameAutosaveName:@"mainwindow"];
+
     /* setup the styled interface */
     b_nativeFullscreenMode = NO;
 #ifdef MAC_OS_X_VERSION_10_7
     if (!OSX_SNOW_LEOPARD)
         b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
 #endif
-    t_hide_mouse_timer = nil;
     [self useOptimizedDrawing: YES];
 
     [[o_search_fld cell] setPlaceholderString: _NS("Search")];
@@ -168,6 +177,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
     [o_dropzone_btn setTitle: _NS("Open media...")];
     [[o_dropzone_btn cell] accessibilitySetOverrideValue:_NS("Click to open an advanced dialog to select the media to play. You can also drop files here to play.") forAttribute:NSAccessibilityDescriptionAttribute];
     [o_dropzone_lbl setStringValue: _NS("Drop media here")];
+    [o_dropzone_img setImage: imageFromRes(@"dropzone")];
 
     [o_podcast_add_btn setTitle: _NS("Subscribe")];
     [o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
@@ -181,7 +191,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
     [o_podcast_unsubscribe_cancel_btn setTitle: _NS("Cancel")];
 
     /* interface builder action */
-    float f_threshold_height = f_min_video_height + [[o_controls_bar bottomBarView] frame].size.height;
+    CGFloat f_threshold_height = f_min_video_height + [o_controls_bar height];
     if (b_dark_interface)
         f_threshold_height += [o_titlebar_view frame].size.height;
     if ([[self contentView] frame].size.height < f_threshold_height)
@@ -191,11 +201,10 @@ static VLCMainWindow *_o_sharedInstance = nil;
     [self setExcludedFromWindowsMenu: YES];
     [self setAcceptsMouseMovedEvents: YES];
     // Set that here as IB seems to be buggy
-    if (b_dark_interface) {
-        [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
-    } else {
-        [self setContentMinSize:NSMakeSize(604., 288.)];
-    }
+    if (b_dark_interface)
+        [self setContentMinSize:NSMakeSize(604., f_min_window_height + [o_titlebar_view frame].size.height)];
+    else
+        [self setContentMinSize:NSMakeSize(604., f_min_window_height)];
 
     [self setTitle: _NS("VLC media player")];
 
@@ -212,110 +221,13 @@ static VLCMainWindow *_o_sharedInstance = nil;
         [o_search_fld setFrame: frame];
     }
 
-    /* create the sidebar */
-    o_sidebaritems = [[NSMutableArray alloc] init];
-    SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
-    SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
-    [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
-    SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
-    [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
-    SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
-    SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
-    SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
-    SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
-
-    /* SD subnodes, inspired by the Qt4 intf */
-    char **ppsz_longnames;
-    int *p_categories;
-    char **ppsz_names = vlc_sd_GetNames(pl_Get(VLCIntf), &ppsz_longnames, &p_categories);
-    if (!ppsz_names)
-        msg_Err(VLCIntf, "no sd item found"); //TODO
-    char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
-    int *p_category = p_categories;
-    NSMutableArray *internetItems = [[NSMutableArray alloc] init];
-    NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
-    NSMutableArray *lanItems = [[NSMutableArray alloc] init];
-    NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
-    NSString *o_identifier;
-    for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++) {
-        o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
-        switch (*p_category) {
-            case SD_CAT_INTERNET:
-                    [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
-                    if (!strncmp(*ppsz_name, "podcast", 7))
-                        [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
-                    else
-                        [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
-                    [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
-                    [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
-                break;
-            case SD_CAT_DEVICES:
-                    [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
-                    [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
-                    [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
-                    [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
-                break;
-            case SD_CAT_LAN:
-                    [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
-                    [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
-                    [[lanItems lastObject] setSdtype: SD_CAT_LAN];
-                    [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
-                break;
-            case SD_CAT_MYCOMPUTER:
-                    [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
-                    if (!strncmp(*ppsz_name, "video_dir", 9))
-                        [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
-                    else if (!strncmp(*ppsz_name, "audio_dir", 9))
-                        [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
-                    else if (!strncmp(*ppsz_name, "picture_dir", 11))
-                        [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
-                    else
-                        [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
-                    [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
-                    [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
-                break;
-            default:
-                msg_Warn(VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name);
-                break;
-        }
-
-        free(*ppsz_name);
-        free(*ppsz_longname);
-    }
-    [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
-    [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
-    [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
-    [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
-    [mycompItems release];
-    [devicesItems release];
-    [lanItems release];
-    [internetItems release];
-    free(ppsz_names);
-    free(ppsz_longnames);
-    free(p_categories);
-
-    [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
-    [o_sidebaritems addObject: libraryItem];
-    if ([mycompItem hasChildren])
-        [o_sidebaritems addObject: mycompItem];
-    if ([devicesItem hasChildren])
-        [o_sidebaritems addObject: devicesItem];
-    if ([lanItem hasChildren])
-        [o_sidebaritems addObject: lanItem];
-    if ([internetItem hasChildren])
-        [o_sidebaritems addObject: internetItem];
-
-    [o_sidebar_view reloadData];
-    [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
-    [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
-    [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
+    /* reload the sidebar */
+    [self reloadSidebar];
 
-    [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
-    [(PXSourceList *)o_sidebar_view setDataSource:self];
-    [o_sidebar_view setDelegate:self];
-    [o_sidebar_view setAutosaveExpandedItems:YES];
-
-    [o_sidebar_view expandItem: libraryItem expandChildren: YES];
+    o_fspanel = [[VLCFSPanel alloc] initWithContentRect:NSMakeRect(110.,267.,549.,87.)
+                                              styleMask:NSTexturedBackgroundWindowMask
+                                                backing:NSBackingStoreBuffered
+                                                  defer:YES];
 
     /* make sure we display the desired default appearance when VLC launches for the first time */
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
@@ -324,9 +236,18 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
         NSUInteger i_sidebaritem_count = [o_sidebaritems count];
         for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
-            [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
+            [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex:x] expandChildren: YES];
+
+        [o_fspanel center];
+
+        NSAlert *albumArtAlert = [NSAlert alertWithMessageText:_NS("Check for album art and metadata?") defaultButton:_NS("Enable Metadata Retrieval") alternateButton:_NS("No, Thanks") otherButton:nil informativeTextWithFormat:@"%@",_NS("VLC can check online for album art and metadata to enrich your playback experience, e.g. by providing track information when playing Audio CDs. To provide this functionality, VLC will send information about your contents to trusted services in an anonymized form.")];
+        NSInteger returnValue = [albumArtAlert runModal];
+        config_PutInt(VLCIntf, "metadata-network-access", returnValue == NSAlertDefaultReturn);
     }
 
+    // select playlist item by default
+    [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
+
     if (b_dark_interface) {
         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
@@ -357,7 +278,6 @@ static VLCMainWindow *_o_sharedInstance = nil;
         o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
         [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
         [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
-
     } else {
         [o_video_view setFrame: [o_split_view frame]];
         [o_playlist_table setBorderType: NSNoBorder];
@@ -367,11 +287,11 @@ static VLCMainWindow *_o_sharedInstance = nil;
     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mainSplitViewWillResizeSubviews:) name:NSSplitViewWillResizeSubviewsNotification object:o_split_view];
+    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(mainSplitViewDidResizeSubviews:) name: NSSplitViewDidResizeSubviewsNotification object:o_split_view];
 
     if (b_splitviewShouldBeHidden) {
-        [self hideSplitView];
-        i_lastSplitViewHeight = 300;
+        [self hideSplitView: YES];
+        f_lastSplitViewHeight = 300;
     }
 
     /* sanity check for the window size */
@@ -387,7 +307,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
         [o_controls_bar setFullscreenState:YES];
 
     /* restore split view */
-    i_lastLeftSplitViewWidth = 200;
+    f_lastLeftSplitViewWidth = 200;
     /* trick NSSplitView implementation, which pretends to know better than us */
     if (!config_GetInt(VLCIntf, "macosx-show-sidebar"))
         [self performSelector:@selector(toggleLeftSubSplitView) withObject:nil afterDelay:0.05];
@@ -396,6 +316,121 @@ static VLCMainWindow *_o_sharedInstance = nil;
 #pragma mark -
 #pragma mark appearance management
 
+- (void)reloadSidebar
+{
+    BOOL isAReload = NO;
+    if (o_sidebaritems) {
+        [o_sidebaritems release];
+        isAReload = YES;
+    }
+
+    o_sidebaritems = [[NSMutableArray alloc] init];
+    SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
+    SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
+    [playlistItem setIcon: imageFromRes(@"sidebar-playlist")];
+    SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
+    [medialibraryItem setIcon: imageFromRes(@"sidebar-playlist")];
+    SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
+    SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
+    SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
+    SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
+
+    /* SD subnodes, inspired by the Qt4 intf */
+    char **ppsz_longnames = NULL;
+    int *p_categories = NULL;
+    char **ppsz_names = vlc_sd_GetNames(pl_Get(VLCIntf), &ppsz_longnames, &p_categories);
+    if (!ppsz_names)
+        msg_Err(VLCIntf, "no sd item found"); //TODO
+    char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
+    int *p_category = p_categories;
+    NSMutableArray *internetItems = [[NSMutableArray alloc] init];
+    NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
+    NSMutableArray *lanItems = [[NSMutableArray alloc] init];
+    NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
+    NSString *o_identifier;
+    for (; ppsz_name && *ppsz_name; ppsz_name++, ppsz_longname++, p_category++) {
+        o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
+        switch (*p_category) {
+            case SD_CAT_INTERNET:
+                [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
+                [[internetItems lastObject] setIcon: imageFromRes(@"sidebar-podcast")];
+                [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
+                [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
+                break;
+            case SD_CAT_DEVICES:
+                [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
+                [[devicesItems lastObject] setIcon: imageFromRes(@"sidebar-local")];
+                [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
+                [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
+                break;
+            case SD_CAT_LAN:
+                [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
+                [[lanItems lastObject] setIcon: imageFromRes(@"sidebar-local")];
+                [[lanItems lastObject] setSdtype: SD_CAT_LAN];
+                [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
+                break;
+            case SD_CAT_MYCOMPUTER:
+                [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
+                if (!strncmp(*ppsz_name, "video_dir", 9))
+                    [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-movie")];
+                else if (!strncmp(*ppsz_name, "audio_dir", 9))
+                    [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-music")];
+                else if (!strncmp(*ppsz_name, "picture_dir", 11))
+                    [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-pictures")];
+                else
+                    [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
+                [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String:*ppsz_longname]];
+                [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
+                break;
+            default:
+                msg_Warn(VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name);
+                break;
+        }
+
+        free(*ppsz_name);
+        free(*ppsz_longname);
+    }
+    [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
+    [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
+    [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
+    [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
+    [mycompItems release];
+    [devicesItems release];
+    [lanItems release];
+    [internetItems release];
+    free(ppsz_names);
+    free(ppsz_longnames);
+    free(p_categories);
+
+    [libraryItem setChildren: [NSArray arrayWithObjects:playlistItem, medialibraryItem, nil]];
+    [o_sidebaritems addObject: libraryItem];
+    if ([mycompItem hasChildren])
+        [o_sidebaritems addObject: mycompItem];
+    if ([devicesItem hasChildren])
+        [o_sidebaritems addObject: devicesItem];
+    if ([lanItem hasChildren])
+        [o_sidebaritems addObject: lanItem];
+    if ([internetItem hasChildren])
+        [o_sidebaritems addObject: internetItem];
+
+    [o_sidebar_view reloadData];
+    [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
+    [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
+
+    [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
+    [(PXSourceList *)o_sidebar_view setDataSource:self];
+    [o_sidebar_view setDelegate:self];
+    [o_sidebar_view setAutosaveExpandedItems:YES];
+
+    [o_sidebar_view expandItem: libraryItem expandChildren: YES];
+
+    if (isAReload) {
+        NSUInteger i_sidebaritem_count = [o_sidebaritems count];
+        for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
+            [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex:x] expandChildren: YES];
+    }
+}
+
 - (VLCMainWindowControlsBar *)controlsBar;
 {
     return (VLCMainWindowControlsBar *)o_controls_bar;
@@ -403,28 +438,40 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
 - (void)resizePlaylistAfterCollapse
 {
+    // no animation here since we might be in the middle of another resize animation
+    NSRect rightSplitRect = [o_right_split_view frame];
+
     NSRect plrect;
-    plrect = [o_playlist_table frame];
-    plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
-    [[o_playlist_table animator] setFrame: plrect];
+    plrect.size.height = rightSplitRect.size.height - 20.0; // actual pl top bar height, which differs from its frame
+    plrect.size.width = rightSplitRect.size.width;
+    plrect.origin.x = plrect.origin.y = 0.;
 
-    NSRect rightSplitRect;
-    rightSplitRect = [o_right_split_view frame];
-    plrect = [o_dropzone_box frame];
-    plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
-    plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
-    [[o_dropzone_box animator] setFrame: plrect];
+    NSRect dropzoneboxRect = [o_dropzone_box frame];
+    dropzoneboxRect.origin.x = (plrect.size.width - dropzoneboxRect.size.width) / 2;
+    dropzoneboxRect.origin.y = (plrect.size.height - dropzoneboxRect.size.height) / 2;
+
+    [o_dropzone_view setFrame: plrect];
+    [o_dropzone_box setFrame: dropzoneboxRect];
+
+    if (b_podcastView_displayed) {
+        plrect.size.height -= [o_podcast_view frame].size.height;
+        plrect.origin.y = [o_podcast_view frame].size.height;
+    }
+    [o_playlist_table setFrame: plrect];
+
+    [o_dropzone_view setNeedsDisplay: YES];
+    [o_playlist_table setNeedsDisplay: YES];
 }
 
 - (void)makeSplitViewVisible
 {
     if (b_dark_interface)
-        [self setContentMinSize: NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
+        [self setContentMinSize: NSMakeSize(604., f_min_window_height + [o_titlebar_view frame].size.height)];
     else
-        [self setContentMinSize: NSMakeSize(604., 288.)];
+        [self setContentMinSize: NSMakeSize(604., f_min_window_height)];
 
     NSRect old_frame = [self frame];
-    float newHeight = [self minSize].height;
+    CGFloat newHeight = [self minSize].height;
     if (old_frame.size.height < newHeight) {
         NSRect new_frame = old_frame;
         new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
@@ -435,8 +482,12 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
     [o_video_view setHidden: YES];
     [o_split_view setHidden: NO];
-    [self makeFirstResponder: nil];
+    if (b_nativeFullscreenMode && [self fullscreen]) {
+        [[o_controls_bar bottomBarView] setHidden: NO];
+        [o_fspanel setNonActive:nil];
+    }
 
+    [self makeFirstResponder: o_playlist_table];
 }
 
 - (void)makeSplitViewHidden
@@ -448,43 +499,57 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
     [o_split_view setHidden: YES];
     [o_video_view setHidden: NO];
+    if (b_nativeFullscreenMode && [self fullscreen]) {
+        [[o_controls_bar bottomBarView] setHidden: YES];
+        [o_fspanel setActive:nil];
+    }
 
     if ([[o_video_view subviews] count] > 0)
         [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
 }
 
-// only exception for an controls bar button action
-- (IBAction)togglePlaylist:(id)sender
+
+- (void)changePlaylistState:(VLCPlaylistStateEvent)event
 {
-    if (![self isVisible] && sender != nil) {
-        [self makeKeyAndOrderFront: sender];
+    // Beware, this code is really ugly
+
+    msg_Dbg(VLCIntf, "toggle playlist from state: removed splitview %i, minimized view %i. Event %i", b_splitview_removed, b_minimized_view, event);
+    if (![self isVisible] && event == psUserMenuEvent) {
+        [self makeKeyAndOrderFront: nil];
         return;
     }
 
     BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
     BOOL b_restored = NO;
 
-    // TODO: implement toggle playlist in this situation (triggerd via menu item).
-    // but for now we block this case, to avoid displaying only the half
-    if (b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil)
-        return;
+    // ignore alt if triggered through main menu shortcut
+    BOOL b_have_alt_key = ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0;
+    if (event == psUserMenuEvent)
+        b_have_alt_key = NO;
+
+    // eUserMenuEvent is now handled same as eUserEvent
+    if(event == psUserMenuEvent)
+        event = psUserEvent;
 
-    if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0) {
+    if (b_dropzone_active && b_have_alt_key) {
         [self hideDropZone];
         return;
     }
 
-    if (!(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ((([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
-                                                                              || (b_nonembedded && sender != nil)
-                                                                              || (!b_activeVideo && sender != nil)
-                                                                              || b_minimized_view))
-        [self hideSplitView];
-    else {
+    if (!(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ((b_have_alt_key && b_activeVideo)
+                                                                              || (b_nonembedded && event == psUserEvent)
+                                                                              || (!b_activeVideo && event == psUserEvent)
+                                                                              || (b_minimized_view && event == psVideoStartedOrStoppedEvent))) {
+        // for starting playback, window is resized through resized events
+        // for stopping playback, resize through reset to previous frame
+        [self hideSplitView: event != psVideoStartedOrStoppedEvent];
+        b_minimized_view = NO;
+    } else {
         if (b_splitview_removed) {
-            if (!b_nonembedded || (sender != nil && b_nonembedded))
-                [self showSplitView];
+            if (!b_nonembedded || (event == psUserEvent && b_nonembedded))
+                [self showSplitView: event != psVideoStartedOrStoppedEvent];
 
-            if (sender == nil)
+            if (event != psUserEvent)
                 b_minimized_view = YES;
             else
                 b_minimized_view = NO;
@@ -494,7 +559,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
         }
 
         if (!b_nonembedded) {
-            if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil))
+            if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && event != psUserEvent))
                 [self makeSplitViewHidden];
             else
                 [self makeSplitViewVisible];
@@ -504,6 +569,8 @@ static VLCMainWindow *_o_sharedInstance = nil;
             [o_video_view setHidden: YES];
         }
     }
+
+    msg_Dbg(VLCIntf, "toggle playlist to state: removed splitview %i, minimized view %i", b_splitview_removed, b_minimized_view);
 }
 
 - (IBAction)dropzoneButtonAction:(id)sender
@@ -531,7 +598,8 @@ static VLCMainWindow *_o_sharedInstance = nil;
 {
     id obj = [notification object];
 
-    if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !b_nonembedded)) {
+    // hasActiveVideo is defined for VLCVideoWindowCommon and subclasses
+    if ([obj respondsToSelector:@selector(hasActiveVideo)] && [obj hasActiveVideo]) {
         if ([[VLCMain sharedInstance] activeVideoPlayback])
             [[VLCCoreInteraction sharedInstance] stop];
     }
@@ -556,50 +624,59 @@ static VLCMainWindow *_o_sharedInstance = nil;
     b_dropzone_active = YES;
     [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
     [o_dropzone_view setFrame: [o_playlist_table frame]];
-    [[o_playlist_table animator] setHidden:YES];
+    [o_playlist_table setHidden:YES];
 }
 
 - (void)hideDropZone
 {
     b_dropzone_active = NO;
     [o_dropzone_view removeFromSuperview];
-    [[o_playlist_table animator] setHidden: NO];
+    [o_playlist_table setHidden: NO];
 }
 
-- (void)hideSplitView
+- (void)hideSplitView:(BOOL)b_with_resize
 {
-    NSRect winrect = [self frame];
-    i_lastSplitViewHeight = [o_split_view frame].size.height;
-    winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
-    winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
-    [self setFrame: winrect display: YES animate: YES];
-    [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
+    // cancel pending pl resizes, in case of fast toggle between both modes
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resizePlaylistAfterCollapse) object:nil];
+
+    if (b_with_resize) {
+        NSRect winrect = [self frame];
+        f_lastSplitViewHeight = [o_split_view frame].size.height;
+        winrect.size.height = winrect.size.height - f_lastSplitViewHeight;
+        winrect.origin.y = winrect.origin.y + f_lastSplitViewHeight;
+        [self setFrame: winrect display: YES animate: YES];
+    }
+
     if (b_dark_interface) {
-        [self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
-        [self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
+        [self setContentMinSize: NSMakeSize(604., [o_controls_bar height] + [o_titlebar_view frame].size.height)];
+        [self setContentMaxSize: NSMakeSize(FLT_MAX, [o_controls_bar height] + [o_titlebar_view frame].size.height)];
     } else {
-        [self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height)];
-        [self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height)];
+        [self setContentMinSize: NSMakeSize(604., [o_controls_bar height])];
+        [self setContentMaxSize: NSMakeSize(FLT_MAX, [o_controls_bar height])];
     }
 
     b_splitview_removed = YES;
 }
 
-- (void)showSplitView
+- (void)showSplitView:(BOOL)b_with_resize
 {
     [self updateWindow];
     if (b_dark_interface)
-        [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
+        [self setContentMinSize:NSMakeSize(604., f_min_window_height + [o_titlebar_view frame].size.height)];
     else
-        [self setContentMinSize:NSMakeSize(604., 288.)];
+        [self setContentMinSize:NSMakeSize(604., f_min_window_height)];
     [self setContentMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
 
-    NSRect winrect;
-    winrect = [self frame];
-    winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
-    winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
-    [self setFrame: winrect display: YES animate: YES];
+    if (b_with_resize) {
+        NSRect winrect;
+        winrect = [self frame];
+        winrect.size.height = winrect.size.height + f_lastSplitViewHeight;
+        winrect.origin.y = winrect.origin.y - f_lastSplitViewHeight;
+        [self setFrame: winrect display: YES animate: YES];
+    }
 
+    // cancel pending pl resizes, in case of fast toggle between both modes
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resizePlaylistAfterCollapse) object:nil];
     [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
 
     b_splitview_removed = NO;
@@ -608,7 +685,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
 - (void)updateTimeSlider
 {
     [o_controls_bar updateTimeSlider];
-    [[self controlsBar] updatePosAndTimeInFSPanel:o_fspanel];
+    [o_fspanel updatePositionAndTime];
 
     [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateTimeSlider)];
 }
@@ -619,15 +696,19 @@ static VLCMainWindow *_o_sharedInstance = nil;
     p_input = pl_CurrentInput(VLCIntf);
     if (p_input) {
         NSString *aString;
-        char *format = var_InheritString(VLCIntf, "input-title-format");
-        char *formated = str_format_meta(pl_Get(VLCIntf), format);
-        free(format);
-        aString = [NSString stringWithUTF8String:formated];
-        free(formated);
+
+        if (!config_GetPsz(VLCIntf, "video-title")) {
+            char *format = var_InheritString(VLCIntf, "input-title-format");
+            char *formated = str_format_meta(p_input, format);
+            free(format);
+            aString = [NSString stringWithUTF8String:formated];
+            free(formated);
+        } else
+            aString = [NSString stringWithUTF8String:config_GetPsz(VLCIntf, "video-title")];
 
         char *uri = input_item_GetURI(input_GetItem(p_input));
 
-        NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
+        NSURL * o_url = [NSURL URLWithString:[NSString stringWithUTF8String:uri]];
         if ([o_url isFileURL]) {
             [self setRepresentedURL: o_url];
             [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
@@ -656,7 +737,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
             [o_fspanel setStreamTitle: aString];
         } else {
-           [self setTitle: _NS("VLC media player")];
+            [self setTitle: _NS("VLC media player")];
             [self setRepresentedURL: nil];
         }
 
@@ -694,6 +775,8 @@ static VLCMainWindow *_o_sharedInstance = nil;
         [self showDropZone];
     PL_UNLOCK;
     [o_sidebar_view setNeedsDisplay:YES];
+
+    [self _updatePlaylistTitle];
 }
 
 - (void)setPause
@@ -710,7 +793,6 @@ static VLCMainWindow *_o_sharedInstance = nil;
     [o_fspanel setPlay];
 
     [[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPlay)];
-
 }
 
 - (void)updateVolumeSlider
@@ -722,23 +804,39 @@ static VLCMainWindow *_o_sharedInstance = nil;
 #pragma mark -
 #pragma mark Video Output handling
 
+- (void)videoplayWillBeStarted
+{
+    if (!b_fullscreen)
+        frameBeforePlayback = [self frame];
+}
+
 - (void)setVideoplayEnabled
 {
     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
+        
+    if (!b_videoPlayback) {
+        if (!b_nonembedded && (!b_nativeFullscreenMode || (b_nativeFullscreenMode && !b_fullscreen)) && frameBeforePlayback.size.width > 0 && frameBeforePlayback.size.height > 0) {
 
-    if (b_videoPlayback) {
-        if (!b_fullscreen)
-            frameBeforePlayback = [self frame];
-    } else {
-        if (!b_nonembedded && !b_fullscreen && frameBeforePlayback.size.width > 0 && frameBeforePlayback.size.height > 0)
-            [[self animator] setFrame:frameBeforePlayback display:YES];
+            // only resize back to minimum view of this is still desired final state
+            CGFloat f_threshold_height = f_min_video_height + [o_controls_bar height];
+            if(frameBeforePlayback.size.height > f_threshold_height || b_minimized_view) {
+
+                if ([[VLCMain sharedInstance] isTerminating])
+                    [self setFrame:frameBeforePlayback display:YES];
+                else
+                    [[self animator] setFrame:frameBeforePlayback display:YES];
+
+            }
+        }
+
+        frameBeforePlayback = NSMakeRect(0, 0, 0, 0);
 
         // update fs button to reflect state for next startup
-        if (var_InheritBool(pl_Get(VLCIntf), "fullscreen")) {
+        if (var_InheritBool(VLCIntf, "fullscreen") || var_GetBool(pl_Get(VLCIntf), "fullscreen")) {
             [o_controls_bar setFullscreenState:YES];
         }
 
-        [self makeFirstResponder: nil];
+        [self makeFirstResponder: o_playlist_table];
         [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
 
         // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
@@ -746,47 +844,16 @@ static VLCMainWindow *_o_sharedInstance = nil;
     }
 
     if (b_nativeFullscreenMode) {
-        if ([NSApp presentationOptions] & NSApplicationPresentationFullScreen)
+        if ([self hasActiveVideo] && [self fullscreen]) {
             [[o_controls_bar bottomBarView] setHidden: b_videoPlayback];
-        else
-            [[o_controls_bar bottomBarView] setHidden: NO];
-        if (b_videoPlayback && b_fullscreen)
             [o_fspanel setActive: nil];
-        if (!b_videoPlayback)
+        } else {
+            [[o_controls_bar bottomBarView] setHidden: NO];
             [o_fspanel setNonActive: nil];
+        }
     }
 }
 
-//  Called automatically if window's acceptsMouseMovedEvents property is true
-- (void)mouseMoved:(NSEvent *)theEvent
-{
-    if (b_fullscreen)
-        [self recreateHideMouseTimer];
-
-    [super mouseMoved: theEvent];
-}
-
-- (void)recreateHideMouseTimer
-{
-    if (t_hide_mouse_timer != nil) {
-        [t_hide_mouse_timer invalidate];
-        [t_hide_mouse_timer release];
-    }
-
-    t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
-                                                          target:self
-                                                        selector:@selector(hideMouseCursor:)
-                                                        userInfo:nil
-                                                         repeats:NO];
-    [t_hide_mouse_timer retain];
-}
-
-//  NSTimer selectors require this function signature as per Apple's docs
-- (void)hideMouseCursor:(NSTimer *)timer
-{
-    [NSCursor setHiddenUntilMouseMoves: YES];
-}
-
 #pragma mark -
 #pragma mark Lion native fullscreen handling
 - (void)windowWillEnterFullScreen:(NSNotification *)notification
@@ -794,7 +861,12 @@ static VLCMainWindow *_o_sharedInstance = nil;
     [super windowWillEnterFullScreen:notification];
 
     // update split view frame after removing title bar
-    [o_split_view setFrame: [o_video_view frame]];
+    if (b_dark_interface) {
+        NSRect frame = [[self contentView] frame];
+        frame.origin.y += [o_controls_bar height];
+        frame.size.height -= [o_controls_bar height];
+        [o_split_view setFrame:frame];
+    }
 }
 
 - (void)windowWillExitFullScreen:(NSNotification *)notification
@@ -802,49 +874,26 @@ static VLCMainWindow *_o_sharedInstance = nil;
     [super windowWillExitFullScreen: notification];
 
     // update split view frame after readding title bar
-    [o_split_view setFrame: [o_video_view frame]];
+    if (b_dark_interface) {
+        NSRect frame = [o_split_view frame];
+        frame.size.height -= [o_titlebar_view frame].size.height;
+        [o_split_view setFrame:frame];
+    }
 }
 #pragma mark -
 #pragma mark Fullscreen support
 
 - (void)showFullscreenController
 {
-     if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback])
-        [o_fspanel fadeIn];
-}
+    id currentWindow = [NSApp keyWindow];
+    if ([currentWindow respondsToSelector:@selector(hasActiveVideo)] && [currentWindow hasActiveVideo]) {
+        if ([currentWindow respondsToSelector:@selector(fullscreen)] && [currentWindow fullscreen] && ![[currentWindow videoView] isHidden]) {
 
-- (void)makeKeyAndOrderFront: (id)sender
-{
-    /* Hack
-     * when we exit fullscreen and fade out, we may endup in
-     * having a window that is faded. We can't have it fade in unless we
-     * animate again. */
-
-    if (!b_window_is_invisible) {
-        /* Make sure we don't do it too much */
-        [super makeKeyAndOrderFront: sender];
-        return;
+            if ([[VLCMain sharedInstance] activeVideoPlayback])
+                [o_fspanel fadeIn];
+        }
     }
 
-    [super setAlphaValue:0.0f];
-    [super makeKeyAndOrderFront: sender];
-
-    NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
-    [dict setObject:self forKey:NSViewAnimationTargetKey];
-    [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
-
-    o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
-    [dict release];
-
-    [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
-    [o_makekey_anim setDuration: 0.1];
-    [o_makekey_anim setFrameRate: 30];
-    [o_makekey_anim setDelegate: self];
-
-    [o_makekey_anim startAnimation];
-    b_window_is_invisible = NO;
-
-    /* fullscreenAnimation will be unlocked when animation ends */
 }
 
 #pragma mark -
@@ -877,9 +926,9 @@ static VLCMainWindow *_o_sharedInstance = nil;
     return YES;
 }
 
-- (void)mainSplitViewWillResizeSubviews:(id)object
+- (void)mainSplitViewDidResizeSubviews:(id)object
 {
-    i_lastLeftSplitViewWidth = [o_left_split_view frame].size.width;
+    f_lastLeftSplitViewWidth = [o_left_split_view frame].size.width;
     config_PutInt(VLCIntf, "macosx-show-sidebar", ![o_split_view isSubviewCollapsed:o_left_split_view]);
     [[[VLCMain sharedInstance] mainMenu] updateSidebarMenuItem];
 }
@@ -888,9 +937,51 @@ static VLCMainWindow *_o_sharedInstance = nil;
 {
     [o_split_view adjustSubviews];
     if ([o_split_view isSubviewCollapsed:o_left_split_view])
-        [o_split_view setPosition:i_lastLeftSplitViewWidth ofDividerAtIndex:0];
+        [o_split_view setPosition:f_lastLeftSplitViewWidth ofDividerAtIndex:0];
     else
         [o_split_view setPosition:[o_split_view minPossiblePositionOfDividerAtIndex:0] ofDividerAtIndex:0];
+    [[[VLCMain sharedInstance] mainMenu] updateSidebarMenuItem];
+}
+
+#pragma mark -
+#pragma mark private playlist magic
+- (void)_updatePlaylistTitle
+{
+    playlist_t * p_playlist = pl_Get(VLCIntf);
+    PL_LOCK;
+    playlist_item_t * currentPlaylistRoot = [[[VLCMain sharedInstance] playlist] currentPlaylistRoot];
+    PL_UNLOCK;
+    if (currentPlaylistRoot == p_playlist->p_local_category || currentPlaylistRoot == p_playlist->p_ml_category) {
+        if (currentPlaylistRoot == p_playlist->p_local_category)
+            [o_chosen_category_lbl setStringValue: [_NS("Playlist") stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_local_category]]];
+        else
+            [o_chosen_category_lbl setStringValue: [_NS("Media Library") stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_ml_category]]];
+    }
+}
+
+- (NSString *)_playbackDurationOfNode:(playlist_item_t*)node
+{
+    if (!node)
+        return @"";
+
+    playlist_t * p_playlist = pl_Get(VLCIntf);
+    PL_LOCK;
+    mtime_t mt_duration = playlist_GetNodeDuration( node );
+    PL_UNLOCK;
+
+    if (mt_duration < 1)
+        return @"";
+
+    mt_duration = mt_duration / 1000000;
+
+    NSDate *date = [NSDate dateWithTimeIntervalSince1970:mt_duration];
+    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+    [formatter setDateFormat:@"HH:mm:ss"];
+    [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+
+    NSString *playbackDuration = [NSString stringWithFormat:@" — %@",[formatter stringFromDate:date]];
+    [formatter release];
+    return playbackDuration;
 }
 
 #pragma mark -
@@ -944,7 +1035,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
 {
     playlist_t * p_playlist = pl_Get(VLCIntf);
-    NSInteger i_playlist_size;
+    NSInteger i_playlist_size = 0;
 
     if ([[item identifier] isEqualToString: @"playlist"]) {
         PL_LOCK;
@@ -955,7 +1046,8 @@ static VLCMainWindow *_o_sharedInstance = nil;
     }
     if ([[item identifier] isEqualToString: @"medialibrary"]) {
         PL_LOCK;
-        i_playlist_size = p_playlist->p_ml_category->i_children;
+        if (p_playlist->p_ml_category)
+            i_playlist_size = p_playlist->p_ml_category->i_children;
         PL_UNLOCK;
 
         return i_playlist_size;
@@ -980,10 +1072,9 @@ static VLCMainWindow *_o_sharedInstance = nil;
 {
     if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
         if (item != nil) {
-            NSMenu * m;
             if ([item sdtype] > 0)
             {
-                m = [[NSMenu alloc] init];
+                NSMenu *m = [[NSMenu alloc] init];
                 playlist_t * p_playlist = pl_Get(VLCIntf);
                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
                 if (!sd_loaded)
@@ -991,8 +1082,9 @@ static VLCMainWindow *_o_sharedInstance = nil;
                 else
                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
+
+                return [m autorelease];
             }
-            return [m autorelease];
         }
     }
 
@@ -1031,7 +1123,6 @@ static VLCMainWindow *_o_sharedInstance = nil;
     NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
     id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
 
-
     //Set the label text to represent the new selection
     if ([item sdtype] > -1 && [[item identifier] length] > 0) {
         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
@@ -1043,8 +1134,12 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
     if ([[item identifier] isEqualToString:@"playlist"]) {
         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
+        [o_chosen_category_lbl setStringValue: [[o_chosen_category_lbl stringValue] stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_local_category]]];
     } else if ([[item identifier] isEqualToString:@"medialibrary"]) {
-        [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
+        if (p_playlist->p_ml_category) {
+            [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
+            [o_chosen_category_lbl setStringValue: [[o_chosen_category_lbl stringValue] stringByAppendingString:[self _playbackDurationOfNode:p_playlist->p_ml_category]]];
+        }
     } else {
         playlist_item_t * pl_item;
         PL_LOCK;
@@ -1053,6 +1148,12 @@ static VLCMainWindow *_o_sharedInstance = nil;
         [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
     }
 
+    // Note the order: first hide the podcast controls, then show the drop zone
+    if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
+        [self showPodcastControls];
+    else
+        [self hidePodcastControls];
+
     PL_LOCK;
     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
         [self hideDropZone];
@@ -1060,10 +1161,9 @@ static VLCMainWindow *_o_sharedInstance = nil;
         [self showDropZone];
     PL_UNLOCK;
 
-    if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
-        [self showPodcastControls];
-    else
-        [self hidePodcastControls];
+    [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged"
+                                                        object: nil
+                                                      userInfo: nil];
 }
 
 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
@@ -1144,7 +1244,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
         return nil;
 
     for (NSUInteger x = 0; x < count; x++) {
-        id item = [array objectAtIndex: x]; // save one objc selector call
+        id item = [array objectAtIndex:x]; // save one objc selector call
         if ([[item identifier] isEqualToString:object])
             return item;
     }
@@ -1172,23 +1272,20 @@ static VLCMainWindow *_o_sharedInstance = nil;
 
         [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
         config_PutPsz(VLCIntf, "podcast-urls", [podcastConf UTF8String]);
-
-        vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name(VLCIntf->p_libvlc, "podcast");
-        if (p_obj) {
-            var_SetString(p_obj, "podcast-urls", [podcastConf UTF8String]);
-            vlc_object_release(p_obj);
-        }
+        var_SetString(pl_Get(VLCIntf), "podcast-urls", [podcastConf UTF8String]);
         [podcastConf release];
     }
 }
 
 - (IBAction)removePodcast:(id)sender
 {
-    if (config_GetPsz(VLCIntf, "podcast-urls") != NULL) {
+    char *psz_urls = var_InheritString(pl_Get(VLCIntf), "podcast-urls");
+    if (psz_urls != NULL) {
         [o_podcast_unsubscribe_pop removeAllItems];
-        [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
+        [o_podcast_unsubscribe_pop addItemsWithTitles:[toNSStr(psz_urls) componentsSeparatedByString:@"|"]];
         [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
     }
+    free(psz_urls);
 }
 
 - (IBAction)removePodcastWindowAction:(id)sender
@@ -1197,25 +1294,22 @@ static VLCMainWindow *_o_sharedInstance = nil;
     [NSApp endSheet: o_podcast_unsubscribe_window];
 
     if (sender == o_podcast_unsubscribe_ok_btn) {
+        playlist_t * p_playlist = pl_Get(VLCIntf);
+        char *psz_urls = var_InheritString(p_playlist, "podcast-urls");
+
         NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
         [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
-        config_PutPsz(VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String]);
+        const char *psz_new_urls = [[urls componentsJoinedByString:@"|"] UTF8String];
+        var_SetString(pl_Get(VLCIntf), "podcast-urls", psz_new_urls);
+        config_PutPsz(VLCIntf, "podcast-urls", psz_new_urls);
         [urls release];
 
-        vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name(VLCIntf->p_libvlc, "podcast");
-        if (p_obj) {
-            var_SetString(p_obj, "podcast-urls", config_GetPsz(VLCIntf, "podcast-urls"));
-            vlc_object_release(p_obj);
-        }
+        free(psz_urls);
 
-        /* reload the podcast module, since it won't update its list when removing podcasts */
-        playlist_t * p_playlist = pl_Get(VLCIntf);
+        /* update playlist table */
         if (playlist_IsServicesDiscoveryLoaded(p_playlist, "podcast{longname=\"Podcasts\"}")) {
-            playlist_ServicesDiscoveryRemove(p_playlist, "podcast{longname=\"Podcasts\"}");
-            playlist_ServicesDiscoveryAdd(p_playlist, "podcast{longname=\"Podcasts\"}");
-            [o_playlist_table reloadData];
+            [[[VLCMain sharedInstance] playlist] playlistUpdated];
         }
-
     }
 }
 
@@ -1285,7 +1379,7 @@ static VLCMainWindow *_o_sharedInstance = nil;
     NSRect videoViewRect = [[self contentView] bounds];
     if (b_dark_interface)
         videoViewRect.size.height -= [o_titlebar_view frame].size.height;
-    CGFloat f_bottomBarHeight = [[[self controlsBar] bottomBarView] frame].size.height;
+    CGFloat f_bottomBarHeight = [[self controlsBar] height];
     videoViewRect.size.height -= f_bottomBarHeight;
     videoViewRect.origin.y = f_bottomBarHeight;
     [o_video_view setFrame: videoViewRect];
@@ -1295,9 +1389,9 @@ static VLCMainWindow *_o_sharedInstance = nil;
         [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_video_view];
         [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
 
-        [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[[self controlsBar] bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
+        [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[self controlsBar] height] + [o_titlebar_view frame].size.height)];
     } else {
-        [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[[self controlsBar] bottomBarView] frame].size.height)];
+        [self setContentMinSize: NSMakeSize(363., f_min_video_height + [[self controlsBar] height])];
     }
 }