/*****************************************************************************
* playlist.m: MacOS X interface plugin
*****************************************************************************
- * Copyright (C) 2002 VideoLAN
- * $Id: playlist.m,v 1.7 2003/01/31 02:53:52 jlj Exp $
+ * Copyright (C) 2002-2003 VideoLAN
+ * $Id: playlist.m,v 1.40 2003/11/17 13:05:17 bigben Exp $
*
* Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
+ * Derk-Jan Hartman <thedj@users.sourceforge.net>
*
* 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
#include <stdlib.h> /* malloc(), free() */
#include <sys/param.h> /* for MAXPATHLEN */
#include <string.h>
+#include <math.h>
+#include <sys/mount.h>
+#include <vlc_keys.h>
#include "intf.h"
#include "playlist.h"
+#include "controls.h"
/*****************************************************************************
* VLCPlaylistView implementation
- (void)keyDown:(NSEvent *)o_event
{
unichar key = 0;
+ int i, c, i_row;
+ NSMutableArray *o_to_delete;
+ NSNumber *o_number;
+
playlist_t * p_playlist;
intf_thread_t * p_intf = [NSApp getIntf];
p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
- if( p_playlist != NULL )
+
+ if ( p_playlist == NULL )
{
- vlc_mutex_lock( &p_playlist->object_lock );
+ return;
}
-
+
switch( key )
{
- case ' ':
- if( p_playlist != NULL && p_playlist->p_input != NULL )
- {
- input_SetStatus( p_playlist->p_input, INPUT_STATUS_PAUSE );
+ case NSDeleteCharacter:
+ case NSDeleteFunctionKey:
+ case NSDeleteCharFunctionKey:
+ case NSBackspaceCharacter:
+ o_to_delete = [NSMutableArray arrayWithArray:[[self selectedRowEnumerator] allObjects]];
+ c = [o_to_delete count];
+
+ for( i = 0; i < c; i++ ) {
+ o_number = [o_to_delete lastObject];
+ i_row = [o_number intValue];
+
+ if( p_playlist->i_index == i_row && p_playlist->i_status )
+ {
+ playlist_Stop( p_playlist );
+ }
+ [o_to_delete removeObject: o_number];
+ [self deselectRow: i_row];
+ playlist_Delete( p_playlist, i_row );
}
+ [self reloadData];
break;
-
+
default:
[super keyDown: o_event];
break;
if( p_playlist != NULL )
{
- vlc_mutex_unlock( &p_playlist->object_lock );
vlc_object_release( p_playlist );
}
}
*****************************************************************************/
@implementation VLCPlaylist
+- (id)init
+{
+ self = [super init];
+ if ( self !=nil )
+ {
+ i_moveRow = -1;
+ }
+ return self;
+}
+
- (void)awakeFromNib
{
[o_table_view setTarget: self];
[o_table_view registerForDraggedTypes:
[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
+ [o_window setExcludedFromWindowsMenu: TRUE];
+
+ [o_window setTitle: _NS("Playlist")];
+ [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
[o_mi_play setTitle: _NS("Play")];
[o_mi_delete setTitle: _NS("Delete")];
[o_mi_selectall setTitle: _NS("Select All")];
+ [[o_tc_name headerCell] setStringValue:_NS("Name")];
+ [[o_tc_author headerCell] setStringValue:_NS("Author")];
+ [o_random_ckb setTitle: _NS("Random")];
+ [o_loop_ckb setTitle: _NS("Repeat All")];
+ [o_repeat_ckb setTitle: _NS("Repeat One")];
+
}
- (BOOL)tableView:(NSTableView *)o_tv
return( NO );
}
-- (NSDragOperation)tableView:(NSTableView*)o_tv
- validateDrop:(id <NSDraggingInfo>)info
- proposedRow:(int)i_row
- proposedDropOperation:(NSTableViewDropOperation)operation
-{
- return( NSDragOperationPrivate );
-}
-
-- (BOOL)tableView:(NSTableView*)o_tv
- acceptDrop:(id <NSDraggingInfo>)info
- row:(int)i_row
- dropOperation:(NSTableViewDropOperation)operation
-{
- NSArray * o_values;
- NSPasteboard * o_pasteboard;
-
- o_pasteboard = [info draggingPasteboard];
-
- if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
- {
- o_values = [o_pasteboard propertyListForType: NSFilenamesPboardType];
-
- [self appendArray: o_values atPos: i_row enqueue:YES];
-
- return( YES );
- }
-
- return( NO );
-}
-
-- (void)tableView:(NSTableView *)o_tv willDisplayCell:(id)o_cell
- forTableColumn:(NSTableColumn *)o_tc row:(int)i_row
-{
- NSColor * o_color;
-
- [o_cell setDrawsBackground: YES];
-
- if( i_row % 2 )
- {
- o_color = [[NSColor alternateSelectedControlColor]
- highlightWithLevel: 0.90];
- }
- else
- {
- o_color = [o_tv backgroundColor];
- }
-
- [o_cell setBackgroundColor: o_color];
-}
-
- (NSMenu *)menuForEvent:(NSEvent *)o_event
{
NSPoint pt;
return( o_ctx_menu );
}
-- (IBAction)playItem:(id)sender
+- (IBAction)toggleWindow:(id)sender
+{
+ if( [o_window isVisible] )
+ {
+ [o_window orderOut:sender];
+ }
+ else
+ {
+ [o_window makeKeyAndOrderFront:sender];
+ }
+}
+
+- (IBAction)savePlaylist:(id)sender
{
intf_thread_t * p_intf = [NSApp getIntf];
playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
-
- if( p_playlist == NULL )
+
+ NSSavePanel *o_save_panel = [NSSavePanel savePanel];
+ NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
+ [o_save_panel setTitle: _NS("Save Playlist")];
+ [o_save_panel setPrompt: _NS("Save")];
+
+ if( [o_save_panel runModalForDirectory: nil
+ file: o_name] == NSOKButton )
{
- return;
+ playlist_SaveFile( p_playlist, [[o_save_panel filename] fileSystemRepresentation] );
}
- playlist_Goto( p_playlist, [o_table_view selectedRow] );
+}
- vlc_object_release( p_playlist );
+- (IBAction)playItem:(id)sender
+{
+ intf_thread_t * p_intf = [NSApp getIntf];
+ playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+ FIND_ANYWHERE );
+
+ if( p_playlist != NULL )
+ {
+ playlist_Goto( p_playlist, [o_table_view selectedRow] );
+ vlc_object_release( p_playlist );
+ }
}
- (IBAction)deleteItems:(id)sender
{
- int i_row;
+ int i, c, i_row;
+ NSMutableArray *o_to_delete;
+ NSNumber *o_number;
intf_thread_t * p_intf = [NSApp getIntf];
playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
{
return;
}
-
- while( ( i_row = [o_table_view selectedRow] ) != -1 )
- {
+
+ o_to_delete = [NSMutableArray arrayWithArray:[[o_table_view selectedRowEnumerator] allObjects]];
+ c = (int)[o_to_delete count];
+
+ for( i = 0; i < c; i++ ) {
+ o_number = [o_to_delete lastObject];
+ i_row = [o_number intValue];
+
if( p_playlist->i_index == i_row && p_playlist->i_status )
{
playlist_Stop( p_playlist );
}
-
- playlist_Delete( p_playlist, i_row );
-
+ [o_to_delete removeObject: o_number];
[o_table_view deselectRow: i_row];
+ playlist_Delete( p_playlist, i_row );
}
vlc_object_release( p_playlist );
+ /* this is actually duplicity, because the intf.m manage also updates the view
+ * when the playlist changes. we do this on purpose, because else there is a
+ * delay of .5 sec or so when we delete an item */
[self playlistUpdated];
+ [self updateRowSelection];
}
- (IBAction)selectAll:(id)sender
[o_table_view selectAll: nil];
}
-- (void)appendArray:(NSArray*)o_array atPos:(int)i_pos enqueue:(BOOL)b_enqueue
+
+- (IBAction)searchItem:(id)sender
{
- int i_items;
- NSString * o_value;
- NSEnumerator * o_enum;
+ int i_start;
+ int i_current;
+ id o_current_name;
+
+ intf_thread_t * p_intf = [NSApp getIntf];
+ playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+ FIND_ANYWHERE );
+ if ( [o_table_view selectedRow] == [o_table_view numberOfRows]-1 )
+ {
+ i_start = -1;
+ }
+ else
+ {
+ i_start = [o_table_view selectedRow];
+ }
+
+ for ( i_current = i_start + 1 ; i_current < [o_table_view numberOfRows] ; i_current++ )
+ {
+
+ if( p_playlist == NULL )
+ {
+ o_current_name = nil;
+ }
+
+
+ else
+ {
+ vlc_mutex_lock( &p_playlist->object_lock );
+ o_current_name = [[NSString stringWithUTF8String:
+ p_playlist->pp_items[i_current]->psz_name] lastPathComponent];
+ vlc_mutex_unlock( &p_playlist->object_lock );
+ }
+
+ if( [o_current_name rangeOfString:[o_search_keyword stringValue] options:NSCaseInsensitiveSearch ].length )
+ {
+ [o_table_view selectRow: i_current byExtendingSelection: NO];
+ [o_table_view scrollRowToVisible: i_current];
+ break;
+ }
+ [o_table_view selectRow: i_current byExtendingSelection: NO];
+ [o_table_view scrollRowToVisible: i_current];
+ }
+ vlc_object_release( p_playlist );
+}
+
+- (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
+{
+ int i_item;
intf_thread_t * p_intf = [NSApp getIntf];
playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
return;
}
- i_items = 0;
- o_enum = [o_array objectEnumerator];
- while( ( o_value = [o_enum nextObject] ) )
+ for ( i_item = 0; i_item < (int)[o_array count]; i_item++ )
{
- NSURL * o_url;
-
+ /* One item */
+ NSDictionary *o_one_item;
+ int j;
+ int i_total_options = 0;
int i_mode = PLAYLIST_INSERT;
+ BOOL b_rem = FALSE, b_dir = FALSE;
+ NSString *o_url, *o_name;
+ NSArray *o_options;
+ char **ppsz_options = NULL;
+
+ /* Get the item */
+ o_one_item = [o_array objectAtIndex: i_item];
+ o_url = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
+ o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
+ o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
- if (i_items == 0 && !b_enqueue)
+ /* If no name, then make a guess */
+ if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_url];
+
+ if( [[NSFileManager defaultManager] fileExistsAtPath:o_url isDirectory:&b_dir] && b_dir &&
+ [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_url isRemovable: &b_rem
+ isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
+ {
+ /* All of this is to make sure CD's play when you D&D them on VLC */
+ /* Converts mountpoint to a /dev file */
+ struct statfs *buf;
+ char *psz_dev, *temp;
+ buf = (struct statfs *) malloc (sizeof(struct statfs));
+ statfs( [o_url fileSystemRepresentation], buf );
+ psz_dev = strdup(buf->f_mntfromname);
+ free( buf );
+ temp = strrchr( psz_dev , 's' );
+ psz_dev[temp - psz_dev] = '\0';
+ o_url = [NSString stringWithCString: psz_dev ];
+ }
+
+ if (i_item == 0 && !b_enqueue)
i_mode |= PLAYLIST_GO;
-
- playlist_Add( p_playlist, [o_value fileSystemRepresentation],
- i_mode, i_pos == -1 ? PLAYLIST_END : i_pos + i_items );
-
- o_url = [NSURL fileURLWithPath: o_value];
- if( o_url != nil )
+
+ if( o_options && [o_options count] > 0 )
+ {
+ /* Count the input options */
+ i_total_options = [o_options count];
+
+ /* Allocate ppsz_options */
+ for( j = 0; j < i_total_options; j++ )
+ {
+ if( !ppsz_options )
+ ppsz_options = (char **)malloc( sizeof(char *) * i_total_options );
+
+ ppsz_options[j] = strdup([[o_options objectAtIndex:j] UTF8String]);
+ }
+ }
+
+ playlist_AddExt( p_playlist, [o_url fileSystemRepresentation], [o_name UTF8String], -1,
+ (ppsz_options != NULL ) ? (const char **)ppsz_options : 0, i_total_options,
+ i_mode, i_position == -1 ? PLAYLIST_END : i_position + i_item);
+
+ /* clean up */
+ for( j = 0; j < i_total_options; j++ )
+ free( ppsz_options[j] );
+ if( ppsz_options ) free( ppsz_options );
+
+ /* Recent documents menu */
+ NSURL *o_true_url = [NSURL fileURLWithPath: o_url];
+ if( o_true_url != nil )
{
[[NSDocumentController sharedDocumentController]
- noteNewRecentDocumentURL: o_url];
+ noteNewRecentDocumentURL: o_true_url];
}
-
- i_items++;
}
vlc_object_release( p_playlist );
-
- [self playlistUpdated];
}
- (void)playlistUpdated
[o_table_view reloadData];
}
+- (void)updateRowSelection
+{
+ int i_row;
+
+ intf_thread_t * p_intf = [NSApp getIntf];
+ playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+ FIND_ANYWHERE );
+
+ if( p_playlist == NULL )
+ {
+ return;
+ }
+
+ i_row = p_playlist->i_index;
+ vlc_object_release( p_playlist );
+
+ [o_table_view selectRow: i_row byExtendingSelection: NO];
+ [o_table_view scrollRowToVisible: i_row];
+}
+
+
@end
@implementation VLCPlaylist (NSTableDataSource)
int i_count = 0;
intf_thread_t * p_intf = [NSApp getIntf];
playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
- FIND_ANYWHERE );
+ FIND_ANYWHERE );
if( p_playlist != NULL )
{
+ vlc_mutex_lock( &p_playlist->object_lock );
i_count = p_playlist->i_size;
+ vlc_mutex_unlock( &p_playlist->object_lock );
vlc_object_release( p_playlist );
}
-
+ [o_status_field setStringValue: [NSString stringWithFormat:_NS("%i items in playlist"), i_count]];
return( i_count );
}
return( nil );
}
- vlc_mutex_lock( &p_playlist->object_lock );
- o_value = [NSString stringWithUTF8String:
- p_playlist->pp_items[i_row]->psz_name];
- vlc_mutex_unlock( &p_playlist->object_lock );
+ if( [[o_tc identifier] isEqualToString:@"0"] )
+ {
+ o_value = [NSString stringWithFormat:@"%i", i_row + 1];
+ }
+ else if( [[o_tc identifier] isEqualToString:@"1"] )
+ {
+ vlc_mutex_lock( &p_playlist->object_lock );
+ o_value = [[NSString stringWithUTF8String:
+ p_playlist->pp_items[i_row]->psz_name] lastPathComponent];
+ vlc_mutex_unlock( &p_playlist->object_lock );
+ }
+ else if( [[o_tc identifier] isEqualToString:@"2"] )
+ {
+ vlc_mutex_lock( &p_playlist->object_lock );
+ o_value = [NSString stringWithUTF8String:
+ p_playlist->pp_items[i_row]->psz_author];
+ vlc_mutex_unlock( &p_playlist->object_lock );
+ }
vlc_object_release( p_playlist );
return( o_value );
}
+- (BOOL)tableView:(NSTableView *)o_tv
+ writeRows:(NSArray*)o_rows
+ toPasteboard:(NSPasteboard*)o_pasteboard
+{
+ int i_rows = [o_rows count];
+ NSArray *o_filenames = [NSArray array];
+
+ [o_pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:self];
+ [o_pasteboard setPropertyList:o_filenames forType:NSFilenamesPboardType];
+ if ( i_rows == 1 )
+ {
+ i_moveRow = [[o_rows objectAtIndex:0]intValue];
+ return YES;
+ }
+ return NO;
+}
+
+- (NSDragOperation)tableView:(NSTableView*)o_tv
+ validateDrop:(id <NSDraggingInfo>)o_info
+ proposedRow:(int)i_row
+ proposedDropOperation:(NSTableViewDropOperation)o_operation
+{
+ if ( o_operation == NSTableViewDropAbove )
+ {
+ if ( i_moveRow >= 0 )
+ {
+ if ( i_row != i_moveRow )
+ {
+ return NSDragOperationMove;
+ }
+ /* what if in the previous run, the row wasn't actually moved?
+ then we can't drop new files on this location */
+ return NSDragOperationNone;
+ }
+ return NSDragOperationGeneric;
+ }
+ return NSDragOperationNone;
+}
+
+- (BOOL)tableView:(NSTableView*)o_tv
+ acceptDrop:(id <NSDraggingInfo>)o_info
+ row:(int)i_proposed_row
+ dropOperation:(NSTableViewDropOperation)o_operation
+{
+ if ( i_moveRow >= 0 )
+ {
+ if (i_moveRow != -1 && i_proposed_row != -1)
+ {
+ intf_thread_t * p_intf = [NSApp getIntf];
+ playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+ FIND_ANYWHERE );
+
+ if( p_playlist == NULL )
+ {
+ i_moveRow = -1;
+ return NO;
+ }
+
+ playlist_Move( p_playlist, i_moveRow, i_proposed_row );
+
+ vlc_object_release( p_playlist );
+ }
+ [self playlistUpdated];
+ i_moveRow = -1;
+ return YES;
+ }
+ else
+ {
+ NSPasteboard * o_pasteboard;
+ o_pasteboard = [o_info draggingPasteboard];
+
+ if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
+ {
+ int i;
+ NSArray *o_array = [NSArray array];
+ NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType]
+ sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
+
+ for( i = 0; i < (int)[o_values count]; i++)
+ {
+ NSDictionary *o_dic;
+ o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
+ o_array = [o_array arrayByAddingObject: o_dic];
+ }
+ [self appendArray: o_array atPos: i_proposed_row enqueue:YES];
+ return YES;
+ }
+ return NO;
+ }
+ [self updateRowSelection];
+}
+
@end