]> git.sesse.net Git - vlc/blob - modules/gui/macosx/playlist.m
* another thing that wasn't committed yet.
[vlc] / modules / gui / macosx / playlist.m
1 /*****************************************************************************
2  * playlist.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2004 VideoLAN
5  * $Id: playlist.m,v 1.58 2004/02/26 14:40:29 hartman Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Derk-Jan Hartman <hartman at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <sys/param.h>                                    /* for MAXPATHLEN */
30 #include <string.h>
31 #include <math.h>
32 #include <sys/mount.h>
33 #include <vlc_keys.h>
34
35 #include "intf.h"
36 #include "playlist.h"
37 #include "controls.h"
38
39 /*****************************************************************************
40  * VLCPlaylistView implementation 
41  *****************************************************************************/
42 @implementation VLCPlaylistView
43
44 - (NSMenu *)menuForEvent:(NSEvent *)o_event
45 {
46     return( [[self delegate] menuForEvent: o_event] );
47 }
48
49 - (void)keyDown:(NSEvent *)o_event
50 {
51     unichar key = 0;
52     int i, c, i_row;
53     NSMutableArray *o_to_delete;
54     NSNumber *o_number;
55     
56     playlist_t * p_playlist;
57     intf_thread_t * p_intf = [NSApp getIntf];
58
59     if( [[o_event characters] length] )
60     {
61         key = [[o_event characters] characterAtIndex: 0];
62     }
63
64     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
65                                           FIND_ANYWHERE );
66     
67     if ( p_playlist == NULL )
68     {
69         return;
70     }
71     
72     switch( key )
73     {
74         case NSDeleteCharacter:
75         case NSDeleteFunctionKey:
76         case NSDeleteCharFunctionKey:
77         case NSBackspaceCharacter:
78             o_to_delete = [NSMutableArray arrayWithArray:[[self selectedRowEnumerator] allObjects]];
79             c = [o_to_delete count];
80             
81             for( i = 0; i < c; i++ ) {
82                 o_number = [o_to_delete lastObject];
83                 i_row = [o_number intValue];
84                 
85                 if( p_playlist->i_index == i_row && p_playlist->i_status )
86                 {
87                     playlist_Stop( p_playlist );
88                 }
89                 [o_to_delete removeObject: o_number];
90                 [self deselectRow: i_row];
91                 playlist_Delete( p_playlist, i_row );
92             }
93             [self reloadData];
94             break;
95             
96         default:
97             [super keyDown: o_event];
98             break;
99     }
100
101     if( p_playlist != NULL )
102     {
103         vlc_object_release( p_playlist );
104     }
105 }
106
107
108 @end
109
110 /*****************************************************************************
111  * VLCPlaylist implementation 
112  *****************************************************************************/
113 @implementation VLCPlaylist
114
115 - (id)init
116 {
117     self = [super init];
118     if ( self !=nil )
119     {
120         i_moveRow = -1;
121     }
122     return self;
123 }
124
125 - (void)awakeFromNib
126 {
127     [o_table_view setTarget: self];
128     [o_table_view setDelegate: self];
129     [o_table_view setDataSource: self];
130
131     [o_table_view setDoubleAction: @selector(playItem:)];
132
133     [o_table_view registerForDraggedTypes: 
134         [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
135
136     [o_window setExcludedFromWindowsMenu: TRUE];
137
138 /* We need to check whether _defaultTableHeaderSortImage exists, since it 
139 belongs to an Apple hidden private API, and then can "disapear" at any time*/
140
141     if( [[NSTableView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
142     {
143         o_ascendingSortingImage = [[NSTableView class] _defaultTableHeaderSortImage];
144     }
145     else
146     {
147         o_ascendingSortingImage = nil;
148     }
149
150     if( [[NSTableView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
151     {
152         o_descendingSortingImage = [[NSTableView class] _defaultTableHeaderReverseSortImage];
153     }
154     else
155     {
156         o_descendingSortingImage = nil;
157     }
158
159     [self initStrings];
160 }
161
162 - (void)initStrings
163 {
164     [o_window setTitle: _NS("Playlist")];
165     [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
166     [o_mi_play setTitle: _NS("Play")];
167     [o_mi_delete setTitle: _NS("Delete")];
168     [o_mi_selectall setTitle: _NS("Select All")];
169     [[o_tc_name headerCell] setStringValue:_NS("Name")];
170     [[o_tc_author headerCell] setStringValue:_NS("Author")];
171     [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
172     [o_random_ckb setTitle: _NS("Random")];
173     [o_loop_ckb setTitle: _NS("Repeat All")];
174     [o_repeat_ckb setTitle: _NS("Repeat One")];
175     [o_search_button setTitle: _NS("Search")];
176     [o_btn_playlist setToolTip: _NS("Playlist")];
177 }
178
179 - (void) tableView:(NSTableView*)o_tv
180                   didClickTableColumn:(NSTableColumn *)o_tc
181 {
182     intf_thread_t * p_intf = [NSApp getIntf];
183     playlist_t *p_playlist =
184         (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
185                                        FIND_ANYWHERE );
186
187     int max = [[o_table_view tableColumns] count];
188     int i;
189
190     if( p_playlist == NULL )
191     {
192         return;
193     }
194
195     if( o_tc_sortColumn == o_tc )
196     { 
197         b_isSortDescending = !b_isSortDescending;
198     }
199     else if( o_tc == o_tc_name || o_tc == o_tc_author || 
200         o_tc == o_tc_id )
201     {
202         b_isSortDescending = VLC_FALSE;
203         [o_table_view setHighlightedTableColumn:o_tc];
204         o_tc_sortColumn = o_tc;
205         for( i=0 ; i<max ; i++ )
206         {
207             [o_table_view setIndicatorImage:nil inTableColumn:[[o_table_view tableColumns] objectAtIndex:i]];
208         }
209     }
210
211     if( o_tc_id == o_tc && !b_isSortDescending )
212     {    
213         playlist_SortID( p_playlist , ORDER_NORMAL );
214         [o_table_view setIndicatorImage:o_ascendingSortingImage inTableColumn:o_tc];    
215     }
216     else if( o_tc_name == o_tc && !b_isSortDescending )
217     {    
218         playlist_SortTitle( p_playlist , ORDER_NORMAL );
219         [o_table_view setIndicatorImage:o_ascendingSortingImage inTableColumn:o_tc];    
220     }
221     else if( o_tc_author == o_tc && !b_isSortDescending )
222     {
223         playlist_SortAuthor( p_playlist , ORDER_NORMAL );
224         [o_table_view setIndicatorImage:o_ascendingSortingImage inTableColumn:o_tc];
225     }
226     else if( o_tc_id == o_tc && b_isSortDescending )
227     {    
228         playlist_SortID( p_playlist , ORDER_REVERSE );
229         [o_table_view setIndicatorImage:o_ascendingSortingImage inTableColumn:o_tc];    
230     }
231     else if( o_tc_name == o_tc && b_isSortDescending )
232     {    
233         playlist_SortTitle( p_playlist , ORDER_REVERSE );
234         [o_table_view setIndicatorImage:o_descendingSortingImage inTableColumn:o_tc];
235     }
236     else if( o_tc_author == o_tc && b_isSortDescending )
237     {
238         playlist_SortAuthor( p_playlist , ORDER_REVERSE );
239         [o_table_view setIndicatorImage:o_descendingSortingImage inTableColumn:o_tc];
240     } 
241     vlc_object_release( p_playlist );
242     [self playlistUpdated];
243 }
244
245
246 - (BOOL)tableView:(NSTableView *)o_tv 
247                   shouldEditTableColumn:(NSTableColumn *)o_tc
248                   row:(int)i_row
249 {
250     return( NO );
251 }
252
253 - (NSMenu *)menuForEvent:(NSEvent *)o_event
254 {
255     NSPoint pt;
256     vlc_bool_t b_rows;
257     vlc_bool_t b_item_sel;
258
259     pt = [o_table_view convertPoint: [o_event locationInWindow] 
260                                                  fromView: nil];
261     b_item_sel = ( [o_table_view rowAtPoint: pt] != -1 &&
262                    [o_table_view selectedRow] != -1 );
263     b_rows = [o_table_view numberOfRows] != 0;
264
265     [o_mi_play setEnabled: b_item_sel];
266     [o_mi_delete setEnabled: b_item_sel];
267     [o_mi_selectall setEnabled: b_rows];
268
269     return( o_ctx_menu );
270 }
271
272 - (IBAction)toggleWindow:(id)sender
273 {
274     if( [o_window isVisible] )
275     {
276         [o_window orderOut:sender];
277         [o_btn_playlist setState:NSOffState];
278     }
279     else
280     {
281         [o_window makeKeyAndOrderFront:sender];
282         [o_btn_playlist setState:NSOnState];
283     }
284 }
285
286 - (IBAction)savePlaylist:(id)sender
287 {
288     intf_thread_t * p_intf = [NSApp getIntf];
289     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
290                                                        FIND_ANYWHERE );
291     
292     NSSavePanel *o_save_panel = [NSSavePanel savePanel];
293     NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
294     [o_save_panel setTitle: _NS("Save Playlist")];
295     [o_save_panel setPrompt: _NS("Save")];
296
297     if( [o_save_panel runModalForDirectory: nil
298             file: o_name] == NSOKButton )
299     {
300         playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
301     }
302
303 }
304
305 - (IBAction)playItem:(id)sender
306 {
307     intf_thread_t * p_intf = [NSApp getIntf];
308     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
309                                                        FIND_ANYWHERE );
310
311     if( p_playlist != NULL )
312     {
313         playlist_Goto( p_playlist, [o_table_view selectedRow] );
314         vlc_object_release( p_playlist );
315     }
316 }
317
318 - (IBAction)deleteItems:(id)sender
319 {
320     int i, c, i_row;
321     NSMutableArray *o_to_delete;
322     NSNumber *o_number;
323
324     intf_thread_t * p_intf = [NSApp getIntf];
325     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
326                                                        FIND_ANYWHERE );
327
328     if( p_playlist == NULL )
329     {
330         return;
331     }
332     
333     o_to_delete = [NSMutableArray arrayWithArray:[[o_table_view selectedRowEnumerator] allObjects]];
334     c = (int)[o_to_delete count];
335     
336     for( i = 0; i < c; i++ ) {
337         o_number = [o_to_delete lastObject];
338         i_row = [o_number intValue];
339         
340         if( p_playlist->i_index == i_row && p_playlist->i_status )
341         {
342             playlist_Stop( p_playlist );
343         }
344         [o_to_delete removeObject: o_number];
345         [o_table_view deselectRow: i_row];
346         playlist_Delete( p_playlist, i_row );
347     }
348
349     vlc_object_release( p_playlist );
350
351     /* this is actually duplicity, because the intf.m manage also updates the view
352      * when the playlist changes. we do this on purpose, because else there is a 
353      * delay of .5 sec or so when we delete an item */
354     [self playlistUpdated];
355     [self updateRowSelection];
356 }
357
358 - (IBAction)selectAll:(id)sender
359 {
360     [o_table_view selectAll: nil];
361 }
362
363
364 - (IBAction)searchItem:(id)sender
365 {
366     int i_current = -1;
367     NSString *o_current_name;
368     NSString *o_current_author;
369
370     intf_thread_t * p_intf = [NSApp getIntf];
371     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
372                                                FIND_ANYWHERE );
373     
374     if( p_playlist == NULL )
375     {
376         return;
377     }
378     if( [o_table_view numberOfRows] < 1 )
379     {
380         return;
381     }
382
383     if( [o_table_view selectedRow] == [o_table_view numberOfRows]-1 )
384     {
385         i_current = -1;
386     }
387     else
388     {
389         i_current = [o_table_view selectedRow]; 
390     }
391
392     do
393     {
394         i_current++;
395
396         vlc_mutex_lock( &p_playlist->object_lock );
397         o_current_name = [NSString stringWithUTF8String: 
398             p_playlist->pp_items[i_current]->psz_name];
399         o_current_author = [NSString stringWithUTF8String: 
400             playlist_GetInfo(p_playlist, i_current ,_("General"),_("Author") )];
401         vlc_mutex_unlock( &p_playlist->object_lock );
402
403
404         if( [o_current_name rangeOfString:[o_search_keyword stringValue] options:NSCaseInsensitiveSearch ].length ||
405              [o_current_author rangeOfString:[o_search_keyword stringValue] options:NSCaseInsensitiveSearch ].length )
406         {
407              [o_table_view selectRow: i_current byExtendingSelection: NO];
408              [o_table_view scrollRowToVisible: i_current];
409              break;
410         }
411         if( i_current == [o_table_view numberOfRows] - 1 )
412         {
413              i_current = -1;
414         }
415     }
416     while (i_current != [o_table_view selectedRow]);
417     vlc_object_release( p_playlist );
418 }
419
420
421 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
422 {
423     int i_item;
424     intf_thread_t * p_intf = [NSApp getIntf];
425     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
426                                                        FIND_ANYWHERE );
427
428     if( p_playlist == NULL )
429     {
430         return;
431     }
432
433     for ( i_item = 0; i_item < (int)[o_array count]; i_item++ )
434     {
435         /* One item */
436         NSDictionary *o_one_item;
437         int j, i_total_options = 0, i_new_id = -1;
438         int i_mode = PLAYLIST_INSERT;
439         BOOL b_rem = FALSE, b_dir = FALSE;
440         NSString *o_uri, *o_name;
441         NSArray *o_options;
442         NSURL *o_true_file;
443         char **ppsz_options = NULL;
444     
445         /* Get the item */
446         o_one_item = [o_array objectAtIndex: i_item];
447         o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
448         o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
449         o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
450         
451         /* If no name, then make a guess */
452         if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
453     
454         if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
455             [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
456                     isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem   )
457         {
458             /* All of this is to make sure CD's play when you D&D them on VLC */
459             /* Converts mountpoint to a /dev file */
460             struct statfs *buf;
461             char *psz_dev;
462             buf = (struct statfs *) malloc (sizeof(struct statfs));
463             statfs( [o_uri fileSystemRepresentation], buf );
464             psz_dev = strdup(buf->f_mntfromname);
465             o_uri = [NSString stringWithCString: psz_dev ];
466         }
467
468         if( o_options && [o_options count] > 0 )
469         {
470             /* Count the input options */
471             i_total_options = [o_options count];
472     
473             /* Allocate ppsz_options */
474             for( j = 0; j < i_total_options; j++ )
475             {
476                 if( !ppsz_options )
477                     ppsz_options = (char **)malloc( sizeof(char *) * i_total_options );
478     
479                 ppsz_options[j] = strdup([[o_options objectAtIndex:j] UTF8String]);
480             }
481         }
482
483         /* Add the item */
484         i_new_id = playlist_AddExt( p_playlist, [o_uri fileSystemRepresentation], 
485                       [o_name UTF8String], i_mode, 
486                       i_position == -1 ? PLAYLIST_END : i_position + i_item,
487                       0, (ppsz_options != NULL ) ? (const char **)ppsz_options : 0, i_total_options );
488
489         /* clean up 
490         for( j = 0; j < i_total_options; j++ )
491             free( ppsz_options[j] );
492         if( ppsz_options ) free( ppsz_options ); */
493
494         /* Recent documents menu */
495         o_true_file = [NSURL fileURLWithPath: o_uri];
496         if( o_true_file != nil )
497         { 
498             [[NSDocumentController sharedDocumentController]
499                 noteNewRecentDocumentURL: o_true_file]; 
500         }
501         
502         if( i_item == 0 && !b_enqueue )
503         {
504             playlist_Goto( p_playlist, playlist_GetPositionById( p_playlist, i_new_id ) );
505             playlist_Play( p_playlist );
506         }
507     }
508
509     vlc_object_release( p_playlist );
510 }
511
512 - (void)playlistUpdated
513 {
514     vlc_value_t val;
515     intf_thread_t * p_intf = [NSApp getIntf];
516     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
517                                                        FIND_ANYWHERE );
518     if( p_playlist != NULL )
519     {
520         var_Get( p_playlist, "random", &val );
521         [o_random_ckb setState: val.b_bool];
522
523         var_Get( p_playlist, "loop", &val );
524         [o_loop_ckb setState: val.b_bool];
525
526         var_Get( p_playlist, "repeat", &val );
527         [o_repeat_ckb setState: val.b_bool];
528
529         vlc_object_release( p_playlist );
530     }
531     [o_table_view reloadData];
532 }
533
534 - (void)updateRowSelection
535 {
536     int i_row;
537
538     intf_thread_t * p_intf = [NSApp getIntf];
539     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
540                                                        FIND_ANYWHERE );
541
542     if( p_playlist == NULL )
543     {
544         return;
545     }
546
547     i_row = p_playlist->i_index;
548     vlc_object_release( p_playlist );
549
550     [o_table_view selectRow: i_row byExtendingSelection: NO];
551     [o_table_view scrollRowToVisible: i_row];
552 }
553
554
555 @end
556
557 @implementation VLCPlaylist (NSTableDataSource)
558
559 - (int)numberOfRowsInTableView:(NSTableView *)o_tv
560 {
561     int i_count = 0;
562     intf_thread_t * p_intf = [NSApp getIntf];
563     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
564                                                        FIND_ANYWHERE );
565
566     if( p_playlist != NULL )
567     {
568         vlc_mutex_lock( &p_playlist->object_lock );
569         i_count = p_playlist->i_size;
570         vlc_mutex_unlock( &p_playlist->object_lock );
571         vlc_object_release( p_playlist );
572     }
573     [o_status_field setStringValue: [NSString stringWithFormat:_NS("%i items in playlist"), i_count]];
574     return( i_count );
575 }
576
577 - (id)tableView:(NSTableView *)o_tv 
578                 objectValueForTableColumn:(NSTableColumn *)o_tc 
579                 row:(int)i_row
580 {
581     id o_value = nil;
582     intf_thread_t * p_intf = [NSApp getIntf];
583     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
584                                                FIND_ANYWHERE );
585
586     if( p_playlist == NULL )
587     {
588         return( nil );
589     }
590
591     if( [[o_tc identifier] isEqualToString:@"0"] )
592     {
593         o_value = [NSString stringWithFormat:@"%i", i_row + 1];
594     }
595     else if( [[o_tc identifier] isEqualToString:@"1"] )
596     {
597         vlc_mutex_lock( &p_playlist->object_lock );
598         o_value = [NSString stringWithUTF8String: 
599             p_playlist->pp_items[i_row]->psz_name];
600         vlc_mutex_unlock( &p_playlist->object_lock );
601     }
602     else if( [[o_tc identifier] isEqualToString:@"2"] )
603     {
604         vlc_mutex_lock( &p_playlist->object_lock );
605         o_value = [NSString stringWithUTF8String: 
606             playlist_GetInfo(p_playlist, i_row ,_("General"),_("Author") )];
607         vlc_mutex_unlock( &p_playlist->object_lock );
608     }
609     else if( [[o_tc identifier] isEqualToString:@"3"] )
610     {
611         char psz_duration[MSTRTIME_MAX_SIZE];
612         mtime_t dur = p_playlist->pp_items[i_row]->i_duration;
613         if( dur != -1 )
614         {
615             secstotimestr( psz_duration, dur/1000000 );
616             o_value = [NSString stringWithUTF8String: psz_duration];
617         }
618         else
619         {
620             o_value = @"-:--:--";
621         }
622     }
623
624     vlc_object_release( p_playlist );
625
626     return( o_value );
627 }
628
629 - (BOOL)tableView:(NSTableView *)o_tv
630                     writeRows:(NSArray*)o_rows
631                     toPasteboard:(NSPasteboard*)o_pasteboard 
632 {
633     int i_rows = [o_rows count];
634     NSArray *o_filenames = [NSArray array];
635     
636     [o_pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:self];
637     [o_pasteboard setPropertyList:o_filenames forType:NSFilenamesPboardType];
638     if ( i_rows == 1 )
639     {
640         i_moveRow = [[o_rows objectAtIndex:0]intValue];
641         return YES;
642     }
643     return NO;
644 }
645
646 - (NSDragOperation)tableView:(NSTableView*)o_tv
647                     validateDrop:(id <NSDraggingInfo>)o_info
648                     proposedRow:(int)i_row
649                     proposedDropOperation:(NSTableViewDropOperation)o_operation 
650 {
651     if ( o_operation == NSTableViewDropAbove )
652     {
653         if ( i_moveRow >= 0 )
654         {
655             if ( i_row != i_moveRow )
656             {
657                 return NSDragOperationMove;
658             }
659             /* what if in the previous run, the row wasn't actually moved? 
660                then we can't drop new files on this location */
661             return NSDragOperationNone;
662         }
663         return NSDragOperationGeneric;
664     }
665     return NSDragOperationNone;
666 }
667
668 - (BOOL)tableView:(NSTableView*)o_tv
669                     acceptDrop:(id <NSDraggingInfo>)o_info
670                     row:(int)i_proposed_row
671                     dropOperation:(NSTableViewDropOperation)o_operation 
672 {
673     if (  i_moveRow >= 0 )
674     {
675         if (i_moveRow != -1 && i_proposed_row != -1)
676         {
677             intf_thread_t * p_intf = [NSApp getIntf];
678             playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
679                                                             FIND_ANYWHERE );
680         
681             if( p_playlist == NULL )
682             {
683                 i_moveRow = -1;
684                 return NO;
685             }
686     
687             playlist_Move( p_playlist, i_moveRow, i_proposed_row ); 
688         
689             vlc_object_release( p_playlist );
690         }
691         [self playlistUpdated];
692         i_moveRow = -1;
693         return YES;
694     }
695     else
696     {
697         NSPasteboard * o_pasteboard;
698         o_pasteboard = [o_info draggingPasteboard];
699         
700         if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
701         {
702             int i;
703             NSArray *o_array = [NSArray array];
704             NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType]
705                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
706
707             for( i = 0; i < (int)[o_values count]; i++)
708             {
709                 NSDictionary *o_dic;
710                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
711                 o_array = [o_array arrayByAddingObject: o_dic];
712             }
713             [self appendArray: o_array atPos: i_proposed_row enqueue:YES];
714             return YES;
715         }
716         return NO;
717     }
718     [self updateRowSelection];
719 }
720
721 /* Delegate method of NSWindow */
722 - (void)windowWillClose:(NSNotification *)aNotification
723 {
724     [o_btn_playlist setState: NSOffState];
725 }
726
727 @end
728