+ playlist_t * p_playlist = pl_Yield( p_intf );
+
+ playlist_item_t *p_item;
+ playlist_item_t *p_node = NULL;
+
+ p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
+
+ if( p_item )
+ {
+ if( p_item->i_children == -1 )
+ {
+ p_node = p_item->p_parent;
+
+ }
+ else
+ {
+ p_node = p_item;
+ if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
+ {
+ p_item = p_node->pp_children[0];
+ }
+ else
+ {
+ p_item = NULL;
+ }
+ }
+ playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, true, p_node, p_item );
+ }
+ vlc_object_release( p_playlist );
+}
+
+/* When called retrieves the selected outlineview row and plays that node or item */
+- (IBAction)preparseItem:(id)sender
+{
+ int i_count;
+ NSMutableArray *o_to_preparse;
+ intf_thread_t * p_intf = VLCIntf;
+ playlist_t * p_playlist = pl_Yield( p_intf );
+
+ o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
+ i_count = [o_to_preparse count];
+
+ int i, i_row;
+ NSNumber *o_number;
+ playlist_item_t *p_item = NULL;
+
+ for( i = 0; i < i_count; i++ )
+ {
+ o_number = [o_to_preparse lastObject];
+ i_row = [o_number intValue];
+ p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
+ [o_to_preparse removeObject: o_number];
+ [o_outline_view deselectRow: i_row];
+
+ if( p_item )
+ {
+ if( p_item->i_children == -1 )
+ {
+ playlist_PreparseEnqueue( p_playlist, p_item->p_input );
+ }
+ else
+ {
+ msg_Dbg( p_intf, "preparsing nodes not implemented" );
+ }
+ }
+ }
+ vlc_object_release( p_playlist );
+ [self playlistUpdated];
+}
+
+- (IBAction)servicesChange:(id)sender
+{
+ NSMenuItem *o_mi = (NSMenuItem *)sender;
+ NSString *o_string = [o_mi representedObject];
+ playlist_t * p_playlist = pl_Yield( VLCIntf );
+ if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string UTF8String] ) )
+ playlist_ServicesDiscoveryAdd( p_playlist, [o_string UTF8String] );
+ else
+ playlist_ServicesDiscoveryRemove( p_playlist, [o_string UTF8String] );
+
+ [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
+ [o_string UTF8String] ) ? YES : NO];
+
+ vlc_object_release( p_playlist );
+ [self playlistUpdated];
+ return;
+}
+
+- (IBAction)selectAll:(id)sender
+{
+ [o_outline_view selectAll: nil];
+}
+
+- (IBAction)deleteItem:(id)sender
+{
+ int i_count, i_row;
+ NSMutableArray *o_to_delete;
+ NSNumber *o_number;
+
+ playlist_t * p_playlist;
+ intf_thread_t * p_intf = VLCIntf;
+
+ o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
+ i_count = [o_to_delete count];
+
+ p_playlist = pl_Yield( p_intf );
+
+ PL_LOCK;
+ for( int i = 0; i < i_count; i++ )
+ {
+ o_number = [o_to_delete lastObject];
+ i_row = [o_number intValue];
+ id o_item = [o_outline_view itemAtRow: i_row];
+ playlist_item_t *p_item = [o_item pointerValue];
+#ifndef NDEBUG
+ msg_Dbg( p_intf, "deleting item %i (of %i) with id \"%i\", pointerValue \"%p\" and %i children", i+1, i_count,
+ p_item->p_input->i_id, [o_item pointerValue], p_item->i_children +1 );
+#endif
+ [o_to_delete removeObject: o_number];
+ [o_outline_view deselectRow: i_row];
+
+ if( p_item->i_children != -1 )
+ //is a node and not an item
+ {
+ if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
+ [self isItem: p_playlist->status.p_item inNode:
+ ((playlist_item_t *)[o_item pointerValue])
+ checkItemExistence: NO] == YES )
+ // if current item is in selected node and is playing then stop playlist
+ playlist_Stop( p_playlist );
+
+ playlist_NodeDelete( p_playlist, p_item, true, false );
+ }
+ else
+ playlist_DeleteFromInput( p_playlist, p_item->p_input->i_id, true );
+ }
+ PL_UNLOCK;
+
+ [self playlistUpdated];
+ vlc_object_release( p_playlist );
+}
+
+- (IBAction)sortNodeByName:(id)sender
+{
+ [self sortNode: SORT_TITLE];
+}
+
+- (IBAction)sortNodeByAuthor:(id)sender
+{
+ [self sortNode: SORT_ARTIST];
+}
+
+- (void)sortNode:(int)i_mode
+{
+ playlist_t * p_playlist = pl_Yield( VLCIntf );
+ playlist_item_t * p_item;
+
+ if( [o_outline_view selectedRow] > -1 )
+ {
+ p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]] pointerValue];
+ }
+ else
+ /*If no item is selected, sort the whole playlist*/
+ {
+ p_item = p_playlist->p_root_category;
+ }
+
+ if( p_item->i_children > -1 ) // the item is a node
+ {
+ PL_LOCK;
+ playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
+ PL_UNLOCK;
+ }
+ else
+ {
+ PL_LOCK;
+ playlist_RecursiveNodeSort( p_playlist,
+ p_item->p_parent, i_mode, ORDER_NORMAL );
+ PL_UNLOCK;
+ }
+ vlc_object_release( p_playlist );
+ [self playlistUpdated];
+}
+
+- (input_item_t *)createItem:(NSDictionary *)o_one_item
+{
+ intf_thread_t * p_intf = VLCIntf;
+ playlist_t * p_playlist = pl_Yield( p_intf );
+
+ input_item_t *p_input;
+ int i;
+ BOOL b_rem = FALSE, b_dir = FALSE;
+ NSString *o_uri, *o_name;
+ NSArray *o_options;
+ NSURL *o_true_file;
+
+ /* Get the item */
+ o_uri = (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"];
+
+ /* Find the name for a disc entry (i know, can you believe the trouble?) */
+ if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
+ {
+ int i_count, i_index;
+ struct statfs *mounts = NULL;
+
+ i_count = getmntinfo (&mounts, MNT_NOWAIT);
+ /* getmntinfo returns a pointer to static data. Do not free. */
+ for( i_index = 0 ; i_index < i_count; i_index++ )
+ {
+ NSMutableString *o_temp, *o_temp2;
+ o_temp = [NSMutableString stringWithString: o_uri];
+ o_temp2 = [NSMutableString stringWithUTF8String: mounts[i_index].f_mntfromname];
+ [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:NSLiteralSearch range:NSMakeRange(0, [o_temp length]) ];
+ [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:NSLiteralSearch range:NSMakeRange(0, [o_temp2 length]) ];
+ [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:NSLiteralSearch range:NSMakeRange(0, [o_temp2 length]) ];
+
+ if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
+ {
+ o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithUTF8String:mounts[i_index].f_mntonname]];
+ }
+ }
+ }
+ /* If no name, then make a guess */
+ if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
+
+ if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
+ [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri 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;
+ NSMutableString *o_temp;
+
+ buf = (struct statfs *) malloc (sizeof(struct statfs));
+ statfs( [o_uri fileSystemRepresentation], buf );
+ psz_dev = strdup(buf->f_mntfromname);
+ o_temp = [NSMutableString stringWithUTF8String: psz_dev ];
+ [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:NSLiteralSearch range:NSMakeRange(0, [o_temp length]) ];
+ [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:NSLiteralSearch range:NSMakeRange(0, [o_temp length]) ];
+ [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:NSLiteralSearch range:NSMakeRange(0, [o_temp length]) ];
+ o_uri = o_temp;
+ }
+
+ p_input = input_ItemNew( p_playlist, [o_uri fileSystemRepresentation], [o_name UTF8String] );
+ if( !p_input )
+ return NULL;