]> git.sesse.net Git - vlc/blobdiff - modules/gui/macosx/playlist.m
macosx: cancel resume dialog after 6 seconds
[vlc] / modules / gui / macosx / playlist.m
index 7c92bb6502d21dac231b67e8fab72fb699bdba20..203381e75b998a10efcba90b2cf13e3577a2626d 100644 (file)
@@ -50,6 +50,7 @@
 #import "misc.h"
 #import "open.h"
 #import "MainMenu.h"
+#import "CoreInteraction.h"
 
 #include <vlc_keys.h>
 #import <vlc_interface.h>
     [o_outline_view setAllowsEmptySelection: NO];
     [o_outline_view expandItem: [o_outline_view itemAtRow:0]];
 
-    [o_outline_view_other setTarget: self];
-    [o_outline_view_other setDelegate: self];
-    [o_outline_view_other setDataSource: self];
-    [o_outline_view_other setAllowsEmptySelection: NO];
-
-    [[o_tc_name_other headerCell] setStringValue:_NS("Name")];
-    [[o_tc_author_other headerCell] setStringValue:_NS("Author")];
-    [[o_tc_duration_other headerCell] setStringValue:_NS("Duration")];
+    [self reloadStyles];
 }
 
 - (void)setPlaylistRoot: (playlist_item_t *)root_item
 {
     p_current_root_item = root_item;
     [o_outline_view reloadData];
-    [o_outline_view_other reloadData];
 }
 
 - (playlist_item_t *)currentPlaylistRoot
                                                                 pointerValue];
 }
 
+- (void)reloadStyles
+{
+    NSFont *fontToUse;
+    CGFloat rowHeight;
+    if (config_GetInt(VLCIntf, "macosx-large-text")) {
+        fontToUse = [NSFont systemFontOfSize:13.];
+        rowHeight = 21.;
+    } else {
+        fontToUse = [NSFont systemFontOfSize:11.];
+        rowHeight = 16.;
+    }
+
+    NSArray *columns = [o_outline_view tableColumns];
+    NSUInteger count = columns.count;
+    for (NSUInteger x = 0; x < count; x++)
+        [[[columns objectAtIndex:x] dataCell] setFont:fontToUse];
+    [o_outline_view setRowHeight:rowHeight];
+}
+
 - (void)dealloc {
     [o_outline_dict release];
     [super dealloc];
     BOOL b_selected_item_met;
     BOOL b_isSortDescending;
     id o_tc_sortColumn;
-    NSInteger retainedRowSelection;
+    NSUInteger retainedRowSelection;
 
     BOOL b_playlistmenu_nib_loaded;
     BOOL b_view_setup;
 
 @implementation VLCPlaylist
 
-+ (void)initialize{
++ (void)initialize
+{
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    NSMutableArray * o_columnArray = [[NSMutableArray alloc] init];
+    NSMutableArray *o_columnArray = [[NSMutableArray alloc] init];
     [o_columnArray addObject: [NSArray arrayWithObjects:TITLE_COLUMN, [NSNumber numberWithFloat:190.], nil]];
     [o_columnArray addObject: [NSArray arrayWithObjects:ARTIST_COLUMN, [NSNumber numberWithFloat:95.], nil]];
     [o_columnArray addObject: [NSArray arrayWithObjects:DURATION_COLUMN, [NSNumber numberWithFloat:95.], nil]];
-    NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:[NSArray arrayWithArray:o_columnArray] forKey: @"PlaylistColumnSelection"];
+
+    NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
+                                 [NSArray arrayWithArray:o_columnArray], @"PlaylistColumnSelection",
+                                 [NSArray array], @"recentlyPlayedMediaList",
+                                 [NSDictionary dictionary], @"recentlyPlayedMedia", nil];
 
     [defaults registerDefaults:appDefaults];
     [o_columnArray release];
     [self initStrings];
 
     [o_outline_view setDoubleAction: @selector(playItem:)];
-    [o_outline_view_other setDoubleAction: @selector(playItem:)];
 
     [o_outline_view registerForDraggedTypes: [NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
     [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
 
-    [o_outline_view_other registerForDraggedTypes: [NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
-    [o_outline_view_other setIntercellSpacing: NSMakeSize (0.0, 1.0)];
-
     /* This uses a private API, but works fine on all current OSX releases.
      * Radar ID 11739459 request a public API for this. However, it is probably
      * easier and faster to recreate similar looking bitmaps ourselves. */
         if ([o_column isEqualToString:@"status"])
             continue;
 
-        [o_menu setPlaylistColumnTableState: NSOnState forColumn: o_column];
+        if(![o_menu setPlaylistColumnTableState: NSOnState forColumn: o_column])
+            continue;
+
         [[o_outline_view tableColumnWithIdentifier: o_column] setWidth: [[[o_columnArray objectAtIndex:i] objectAtIndex:1] floatValue]];
     }
 
     [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
 
     [o_search_field setToolTip: _NS("Search in Playlist")];
-    [o_search_field_other setToolTip: _NS("Search in Playlist")];
 }
 
 - (void)playlistUpdated
     //[o_outline_dict removeAllObjects];
     [o_outline_view reloadData];
     [[[[VLCMain sharedInstance] wizard] playlistWizard] reloadOutlineView];
-    [[[[VLCMain sharedInstance] bookmarks] dataTable] reloadData];
 
     [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:retainedRowSelection] byExtendingSelection:NO];
 
             else
                 p_item = NULL;
         }
+
         playlist_Control(p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_node, p_item);
     }
     PL_UNLOCK;
 
         if (p_item) {
             if (p_item->i_children == -1)
-                libvlc_MetaRequest(p_intf->p_libvlc, p_item->p_input);
+                libvlc_MetaRequest(p_intf->p_libvlc, p_item->p_input, META_REQUEST_OPTION_NONE);
             else
                 msg_Dbg(p_intf, "preparsing nodes not implemented");
         }
         p_item = [[o_outline_view itemAtRow: indexes[i]] pointerValue];
 
         if (p_item && p_item->i_children == -1)
-            libvlc_ArtRequest(p_intf->p_libvlc, p_item->p_input);
+            libvlc_ArtRequest(p_intf->p_libvlc, p_item->p_input, META_REQUEST_OPTION_NONE);
     }
     [self playlistUpdated];
 }
 {
     int i_count;
     NSIndexSet *o_selected_indexes;
-    playlist_t * p_playlist;
     intf_thread_t * p_intf = VLCIntf;
+    playlist_t * p_playlist = pl_Get(p_intf);
+
+    // check if deletion is allowed
+    if ([self currentPlaylistRoot] != p_playlist->p_local_category && [self currentPlaylistRoot] != p_playlist->p_ml_category)
+        return;
 
     o_selected_indexes = [o_outline_view selectedRowIndexes];
     i_count = [o_selected_indexes count];
     retainedRowSelection = [o_selected_indexes firstIndex];
+    if (retainedRowSelection == NSNotFound)
+        retainedRowSelection = 0;
 
-    p_playlist = pl_Get(p_intf);
 
     NSUInteger indexes[i_count];
     if (i_count == [o_outline_view numberOfRows]) {
     if ([[NSFileManager defaultManager] fileExistsAtPath:o_path isDirectory:&b_dir] && b_dir &&
         [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath:o_path isRemovable: &b_rem
                                                      isWritable:&b_writable isUnmountable:NULL description:NULL type:NULL] && b_rem && !b_writable && [o_nsurl isFileURL]) {
-        id o_vlc_open = [[VLCMain sharedInstance] open];
 
-        NSString *diskType = [o_vlc_open getVolumeTypeFromMountPath: o_path];
+        NSString *diskType = [VLCOpen getVolumeTypeFromMountPath: o_path];
         msg_Dbg(p_intf, "detected optical media of type %s in the file input", [diskType UTF8String]);
 
         if ([diskType isEqualToString: kVLCMediaDVD])
-            o_uri = [NSString stringWithFormat: @"dvdnav://%@", [o_vlc_open getBSDNodeFromMountPath: o_path]];
+            o_uri = [NSString stringWithFormat: @"dvdnav://%@", [VLCOpen getBSDNodeFromMountPath: o_path]];
         else if ([diskType isEqualToString: kVLCMediaVideoTSFolder])
             o_uri = [NSString stringWithFormat: @"dvdnav://%@", o_path];
         else if ([diskType isEqualToString: kVLCMediaAudioCD])
-            o_uri = [NSString stringWithFormat: @"cdda://%@", [o_vlc_open getBSDNodeFromMountPath: o_path]];
+            o_uri = [NSString stringWithFormat: @"cdda://%@", [VLCOpen getBSDNodeFromMountPath: o_path]];
         else if ([diskType isEqualToString: kVLCMediaVCD])
-            o_uri = [NSString stringWithFormat: @"vcd://%@#0:0", [o_vlc_open getBSDNodeFromMountPath: o_path]];
+            o_uri = [NSString stringWithFormat: @"vcd://%@#0:0", [VLCOpen getBSDNodeFromMountPath: o_path]];
         else if ([diskType isEqualToString: kVLCMediaSVCD])
-            o_uri = [NSString stringWithFormat: @"vcd://%@@0:0", [o_vlc_open getBSDNodeFromMountPath: o_path]];
+            o_uri = [NSString stringWithFormat: @"vcd://%@@0:0", [VLCOpen getBSDNodeFromMountPath: o_path]];
         else if ([diskType isEqualToString: kVLCMediaBD] || [diskType isEqualToString: kVLCMediaBDMVFolder])
             o_uri = [NSString stringWithFormat: @"bluray://%@", o_path];
         else
         NSDictionary *o_one_item;
 
         /* Get the item */
+        PL_LOCK;
         o_one_item = [o_array objectAtIndex:i_item];
         p_input = [self createItem: o_one_item];
 
             continue;
 
         /* Add the item */
-        PL_LOCK;
         playlist_NodeAddInput(p_playlist, p_input, p_node,
                                       PLAYLIST_INSERT,
                                       i_position == -1 ?
     b_item_sel = (row != -1 && [o_outline_view selectedRow] != -1);
     b_rows = [o_outline_view numberOfRows] != 0;
 
+    playlist_t *p_playlist = pl_Get(VLCIntf);
+    bool b_del_allowed = [self currentPlaylistRoot] == p_playlist->p_local_category || [self currentPlaylistRoot] == p_playlist->p_ml_category;
+
     [o_mi_play setEnabled: b_item_sel];
-    [o_mi_delete setEnabled: b_item_sel];
+    [o_mi_delete setEnabled: b_item_sel && b_del_allowed];
     [o_mi_selectall setEnabled: b_rows];
     [o_mi_info setEnabled: b_item_sel];
     [o_mi_preparse setEnabled: b_item_sel];
     o_playing_item = [o_outline_dict objectForKey: [NSString stringWithFormat:@"%p",  playlist_CurrentPlayingItem(p_playlist)]];
     PL_UNLOCK;
 
+    NSFont *fontToUse;
+    if (config_GetInt(VLCIntf, "macosx-large-text"))
+        fontToUse = [NSFont systemFontOfSize:13.];
+    else
+        fontToUse = [NSFont systemFontOfSize:11.];
+
     if ([self isItem: [o_playing_item pointerValue] inNode: [item pointerValue] checkItemExistence:YES locked:NO]
                         || [o_playing_item isEqual: item])
-        [cell setFont: [[NSFontManager sharedFontManager] convertFont:[cell font] toHaveTrait:NSBoldFontMask]];
+        [cell setFont: [[NSFontManager sharedFontManager] convertFont:fontToUse toHaveTrait:NSBoldFontMask]];
     else
-        [cell setFont: [[NSFontManager sharedFontManager] convertFont:[cell font] toNotHaveTrait:NSBoldFontMask]];
+        [cell setFont: [[NSFontManager sharedFontManager] convertFont:fontToUse toNotHaveTrait:NSBoldFontMask]];
 }
 
 - (id)playingItem
     NSTableColumn * o_work_tc;
 
     if (i_state == NSOnState) {
+        NSString *o_title = [o_dict objectForKey:o_column];
+        if (!o_title)
+            return;
+
         o_work_tc = [[NSTableColumn alloc] initWithIdentifier: o_column];
         [o_work_tc setEditable: NO];
         [[o_work_tc dataCell] setFont: [NSFont controlContentFontOfSize:11.]];
     [o_arrayToSave release];
 }
 
+- (BOOL)isValidResumeItem:(input_item_t *)p_item
+{
+    char *psz_url = input_item_GetURI(p_item);
+    NSString *o_url_string = toNSStr(psz_url);
+    free(psz_url);
+
+    if ([o_url_string isEqualToString:@""])
+        return NO;
+
+    NSURL *o_url = [NSURL URLWithString:o_url_string];
+
+    if (![o_url isFileURL])
+        return NO;
+
+    BOOL isDir = false;
+    if (![[NSFileManager defaultManager] fileExistsAtPath:[o_url path] isDirectory:&isDir])
+        return NO;
+
+    if (isDir)
+        return NO;
+
+    return YES;
+}
+
+- (void)updateAlertWindow:(NSTimer *)timer
+{
+    NSAlert *alert = [timer userInfo];
+
+    --currentResumeTimeout;
+    if (currentResumeTimeout <= 0) {
+        [[alert window] close];
+        [NSApp abortModal];
+    }
+
+    NSString *buttonLabel = _NS("Restart playback");
+    buttonLabel = [buttonLabel stringByAppendingFormat:@" (%d)", currentResumeTimeout];
+
+    [[[alert buttons] objectAtIndex:2] setTitle:buttonLabel];
+}
+
+- (void)continuePlaybackWhereYouLeftOff:(input_thread_t *)p_input_thread
+{
+    NSDictionary *recentlyPlayedFiles = [[NSUserDefaults standardUserDefaults] objectForKey:@"recentlyPlayedMedia"];
+    if (!recentlyPlayedFiles)
+        return;
+
+    input_item_t *p_item = input_GetItem(p_input_thread);
+    if (!p_item)
+        return;
+
+    /* allow the user to over-write the start/stop/run-time */
+    if (var_GetFloat(p_input_thread, "run-time") > 0 ||
+        var_GetFloat(p_input_thread, "start-time") > 0 ||
+        var_GetFloat(p_input_thread, "stop-time") > 0) {
+        return;
+    }
+
+    /* check for file existance before resuming */
+    if (![self isValidResumeItem:p_item])
+        return;
+
+    char *psz_url = decode_URI(input_item_GetURI(p_item));
+    if (!psz_url)
+        return;
+    NSString *url = toNSStr(psz_url);
+    free(psz_url);
+
+    NSNumber *lastPosition = [recentlyPlayedFiles objectForKey:url];
+    if (!lastPosition || lastPosition.intValue <= 0)
+        return;
+
+    int settingValue = config_GetInt(VLCIntf, "macosx-continue-playback");
+    if (settingValue == 2) // never resume
+        return;
+
+    NSInteger returnValue = NSAlertErrorReturn;
+    if (settingValue == 0) { // ask
+        NSAlert *theAlert = [NSAlert alertWithMessageText:_NS("Continue playback?") defaultButton:_NS("Continue") alternateButton:_NS("Restart playback") otherButton:_NS("Always continue") informativeTextWithFormat:_NS("Playback of \"%@\" will continue at %@"), [NSString stringWithUTF8String:input_item_GetTitleFbName(p_item)], [[VLCStringUtility sharedInstance] stringForTime:lastPosition.intValue]];
+
+        currentResumeTimeout = 6;
+        NSTimer *timer = [NSTimer timerWithTimeInterval:1
+                                                 target:self
+                                               selector:@selector(updateAlertWindow:)
+                                               userInfo:theAlert
+                                                repeats:YES];
+
+        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode];
+
+        [[VLCCoreInteraction sharedInstance] pause];
+        returnValue = [theAlert runModal];
+        [timer invalidate];
+        [[VLCCoreInteraction sharedInstance] playOrPause];
+
+        // restart button was pressed or timeout happened
+        if (returnValue == NSAlertAlternateReturn ||
+            returnValue == NSRunAbortedResponse)
+            return;
+    }
+
+    mtime_t lastPos = (mtime_t)lastPosition.intValue * 1000000;
+    msg_Dbg(VLCIntf, "continuing playback at %lld", lastPos);
+    var_SetTime(p_input_thread, "time", lastPos);
+
+    if (returnValue == NSAlertOtherReturn)
+        config_PutInt(VLCIntf, "macosx-continue-playback", 1);
+}
+
+- (void)storePlaybackPositionForItem:(input_thread_t *)p_input_thread
+{
+    if (!var_InheritBool(VLCIntf, "macosx-recentitems"))
+        return;
+
+    input_item_t *p_item = input_GetItem(p_input_thread);
+    if (!p_item)
+        return;
+
+    if (![self isValidResumeItem:p_item])
+        return;
+
+    char *psz_url = decode_URI(input_item_GetURI(p_item));
+    if (!psz_url)
+        return;
+    NSString *url = toNSStr(psz_url);
+    free(psz_url);
+
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:@"recentlyPlayedMedia"]];
+
+    float relativePos = var_GetFloat(p_input_thread, "position");
+    mtime_t pos = var_GetTime(p_input_thread, "time") / 1000000;
+    mtime_t dur = input_item_GetDuration(p_item) / 1000000;
+
+    NSMutableArray *mediaList = [[defaults objectForKey:@"recentlyPlayedMediaList"] mutableCopy];
+
+    if (relativePos > .05 && relativePos < .95 && dur > 180) {
+        [mutDict setObject:[NSNumber numberWithInt:pos] forKey:url];
+
+        [mediaList removeObject:url];
+        [mediaList addObject:url];
+        NSUInteger mediaListCount = mediaList.count;
+        if (mediaListCount > 30) {
+            for (NSUInteger x = 0; x < mediaListCount - 30; x++) {
+                [mutDict removeObjectForKey:[mediaList objectAtIndex:0]];
+                [mediaList removeObjectAtIndex:0];
+            }
+        }
+    } else {
+        [mutDict removeObjectForKey:url];
+        [mediaList removeObject:url];
+    }
+    [defaults setObject:mutDict forKey:@"recentlyPlayedMedia"];
+    [defaults setObject:mediaList forKey:@"recentlyPlayedMediaList"];
+    [defaults synchronize];
+
+    [mutDict release];
+    [mediaList release];
+}
+
 @end
 
 @implementation VLCPlaylist (NSOutlineViewDataSource)