]> git.sesse.net Git - vlc/blob - modules/gui/macosx/playlist.m
* modules/gui/macosx/*:
[vlc] / modules / gui / macosx / playlist.m
1 /*****************************************************************************
2  * playlist.m: MacOS X interface plugin
3  *****************************************************************************
4  * Copyright (C) 2002-2003 VideoLAN
5  * $Id: playlist.m,v 1.32 2003/09/20 13:46:00 hartman Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Derk-Jan Hartman <thedj@users.sourceforge.net>
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
33 #include "intf.h"
34 #include "playlist.h"
35 #include "controls.h"
36
37 int MacVersion102 = -1;
38
39 /*****************************************************************************
40  * VLCPlaylistView implementation 
41  *****************************************************************************/
42 @implementation VLCPlaylistView
43
44 - (void)dealloc
45 {
46     if( o_striped_row_color != nil )
47     {
48         [o_striped_row_color release];
49     }
50     [super dealloc];
51 }
52
53 - (NSMenu *)menuForEvent:(NSEvent *)o_event
54 {
55     return( [[self delegate] menuForEvent: o_event] );
56 }
57
58 - (void)keyDown:(NSEvent *)o_event
59 {
60     unichar key = 0;
61     int i, c, i_row;
62     NSMutableArray *o_to_delete;
63     NSNumber *o_number;
64     
65     playlist_t * p_playlist;
66     intf_thread_t * p_intf = [NSApp getIntf];
67
68     if( [[o_event characters] length] )
69     {
70         key = [[o_event characters] characterAtIndex: 0];
71     }
72
73     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
74                                           FIND_ANYWHERE );
75     
76     if ( p_playlist == NULL )
77     {
78         return;
79     }
80     
81     switch( key )
82     {
83         case ' ':
84             [(VLCControls *)[[NSApp delegate] getControls] play: nil];
85             break;
86
87         case NSDeleteCharacter:
88         case NSDeleteFunctionKey:
89         case NSDeleteCharFunctionKey:
90         case NSBackspaceCharacter:
91             o_to_delete = [NSMutableArray arrayWithArray:[[self selectedRowEnumerator] allObjects]];
92             c = [o_to_delete count];
93             
94             for( i = 0; i < c; i++ ) {
95                 o_number = [o_to_delete lastObject];
96                 i_row = [o_number intValue];
97                 
98                 if( p_playlist->i_index == i_row && p_playlist->i_status )
99                 {
100                     playlist_Stop( p_playlist );
101                 }
102                 [o_to_delete removeObject: o_number];
103                 [self deselectRow: i_row];
104                 playlist_Delete( p_playlist, i_row );
105             }
106             [self reloadData];
107             break;
108             
109         default:
110             [super keyDown: o_event];
111             break;
112     }
113
114     if( p_playlist != NULL )
115     {
116         vlc_object_release( p_playlist );
117     }
118 }
119
120 - (void)highlightSelectionInClipRect:(NSRect)o_rect {
121     NSRect o_new_rect;
122     float f_height = [self rowHeight] + [self intercellSpacing].height;
123     float f_origin_y = NSMaxY( o_rect );
124     int i_row = o_rect.origin.y / f_height;
125     
126     if ( i_row % 2 == 0 )
127     {
128         i_row++;
129     }
130     
131     o_new_rect.size.width = o_rect.size.width;
132     o_new_rect.size.height = f_height;
133     o_new_rect.origin.x = o_rect.origin.x;
134     o_new_rect.origin.y = i_row * f_height;
135    
136     if( ( MacVersion102 < 0 ) && ( floor( NSAppKitVersionNumber ) > NSAppKitVersionNumber10_1 ) )
137     {
138         MacVersion102 = 102;
139     }
140  
141     if ( MacVersion102 == 102 && o_striped_row_color == nil )
142     {
143         o_striped_row_color = [[[NSColor alternateSelectedControlColor]
144                                 highlightWithLevel: 0.90] retain];
145         
146     }
147     else if ( o_striped_row_color == nil )
148     {
149         /* OSX 10.1 and before ain't that smart ;) */
150         o_striped_row_color = [[NSColor whiteColor] retain];
151     }
152
153     [o_striped_row_color set];
154     
155     while ( o_new_rect.origin.y < f_origin_y ) {
156         NSRectFill( o_new_rect );
157         o_new_rect.origin.y += f_height * 2.0;
158     }
159     [super highlightSelectionInClipRect:o_rect];
160 }
161
162 @end
163
164 /*****************************************************************************
165  * VLCPlaylist implementation 
166  *****************************************************************************/
167 @implementation VLCPlaylist
168
169 - (id)init
170 {
171     self = [super init];
172     if ( self !=nil )
173     {
174         i_moveRow = -1;
175     }
176     return self;
177 }
178
179 - (void)awakeFromNib
180 {
181     [o_table_view setTarget: self];
182     [o_table_view setDelegate: self];
183     [o_table_view setDataSource: self];
184
185     [o_table_view setDoubleAction: @selector(playItem:)];
186
187     [o_table_view registerForDraggedTypes: 
188         [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
189
190     [o_mi_play setTitle: _NS("Play")];
191     [o_mi_delete setTitle: _NS("Delete")];
192     [o_mi_selectall setTitle: _NS("Select All")];
193     
194     [o_btn_add setToolTip: _NS("Add")];
195     [o_btn_remove setToolTip: _NS("Delete")];
196 }
197
198 - (BOOL)tableView:(NSTableView *)o_tv 
199                   shouldEditTableColumn:(NSTableColumn *)o_tc
200                   row:(int)i_row
201 {
202     return( NO );
203 }
204
205 - (NSMenu *)menuForEvent:(NSEvent *)o_event
206 {
207     NSPoint pt;
208     vlc_bool_t b_rows;
209     vlc_bool_t b_item_sel;
210
211     pt = [o_table_view convertPoint: [o_event locationInWindow] 
212                                                  fromView: nil];
213     b_item_sel = ( [o_table_view rowAtPoint: pt] != -1 &&
214                    [o_table_view selectedRow] != -1 );
215     b_rows = [o_table_view numberOfRows] != 0;
216
217     [o_mi_play setEnabled: b_item_sel];
218     [o_mi_delete setEnabled: b_item_sel];
219     [o_mi_selectall setEnabled: b_rows];
220
221     return( o_ctx_menu );
222 }
223
224 - (IBAction)playItem:(id)sender
225 {
226     intf_thread_t * p_intf = [NSApp getIntf];
227     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
228                                                        FIND_ANYWHERE );
229
230     if( p_playlist != NULL )
231     {
232         playlist_Goto( p_playlist, [o_table_view selectedRow] );
233         vlc_object_release( p_playlist );
234     }
235 }
236
237 - (IBAction)deleteItems:(id)sender
238 {
239     int i, c, i_row;
240     NSMutableArray *o_to_delete;
241     NSNumber *o_number;
242
243     intf_thread_t * p_intf = [NSApp getIntf];
244     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
245                                                        FIND_ANYWHERE );
246
247     if( p_playlist == NULL )
248     {
249         return;
250     }
251     
252     o_to_delete = [NSMutableArray arrayWithArray:[[o_table_view selectedRowEnumerator] allObjects]];
253     c = (int)[o_to_delete count];
254     
255     for( i = 0; i < c; i++ ) {
256         o_number = [o_to_delete lastObject];
257         i_row = [o_number intValue];
258         
259         if( p_playlist->i_index == i_row && p_playlist->i_status )
260         {
261             playlist_Stop( p_playlist );
262         }
263         [o_to_delete removeObject: o_number];
264         [o_table_view deselectRow: i_row];
265         playlist_Delete( p_playlist, i_row );
266     }
267
268     vlc_object_release( p_playlist );
269
270     /* this is actually duplicity, because the intf.m manage also updates the view
271      * when the playlist changes. we do this on purpose, because else there is a 
272      * delay of .5 sec or so when we delete an item */
273     [self playlistUpdated];
274     [self updateRowSelection];
275 }
276
277 - (IBAction)selectAll:(id)sender
278 {
279     [o_table_view selectAll: nil];
280 }
281
282 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
283 {
284     int i_item;
285     intf_thread_t * p_intf = [NSApp getIntf];
286     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
287                                                        FIND_ANYWHERE );
288
289     if( p_playlist == NULL )
290     {
291         return;
292     }
293
294     for ( i_item = 0; i_item < (int)[o_array count]; i_item++ )
295     {
296         /* One item */
297         NSDictionary *o_one_item;
298         NSString *o_url;
299         NSString *o_name;
300         NSArray *o_options;
301         int j, i_total_options = 0;
302         char **ppsz_options = NULL;
303         int i_mode = PLAYLIST_INSERT;
304         
305         /* Get the item */
306         o_one_item = [o_array objectAtIndex: i_item];
307         o_url = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
308         o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
309         o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
310         
311         if( !o_name) o_name = o_url;
312         
313         if (i_item == 0 && !b_enqueue)
314             i_mode |= PLAYLIST_GO;
315
316         if( o_options && [o_options count] > 0 )
317         {
318             /* Count the input options */
319             i_total_options = [o_options count];
320     
321             /* Allocate ppsz_options */
322             for( j = 0; j < i_total_options; j++ )
323             {
324                 if( !ppsz_options )
325                     ppsz_options = (char **)malloc( sizeof(char *) * i_total_options );
326     
327                 ppsz_options[j] = strdup([[o_options objectAtIndex:j] UTF8String]);
328             }
329         }
330         
331         playlist_AddExt( p_playlist, [o_name UTF8String], [o_url fileSystemRepresentation], -1, 
332             (ppsz_options != NULL ) ? (const char **)ppsz_options : 0, i_total_options,
333             i_mode, i_position == -1 ? PLAYLIST_END : i_position + i_item );
334
335         /* clean up */
336         for( j = 0; j < i_total_options; j++ )
337             free( ppsz_options[j] );
338         if( ppsz_options ) free( ppsz_options );
339
340         NSURL *o_true_url = [NSURL fileURLWithPath: o_url];
341         if( o_true_url != nil )
342         { 
343             [[NSDocumentController sharedDocumentController]
344                 noteNewRecentDocumentURL: o_true_url]; 
345         }
346     }
347
348     vlc_object_release( p_playlist );
349 }
350
351 - (void)playlistUpdated
352 {
353     [o_table_view reloadData];
354 }
355
356 - (void)updateRowSelection
357 {
358     int i_row;
359
360     intf_thread_t * p_intf = [NSApp getIntf];
361     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
362                                                        FIND_ANYWHERE );
363
364     if( p_playlist == NULL )
365     {
366         return;
367     }
368
369     vlc_mutex_lock( &p_playlist->object_lock );    
370     i_row = p_playlist->i_index;
371     vlc_mutex_unlock( &p_playlist->object_lock );
372     vlc_object_release( p_playlist );
373
374     [o_table_view selectRow: i_row byExtendingSelection: NO];
375     [o_table_view scrollRowToVisible: i_row];
376 }
377
378 @end
379
380 @implementation VLCPlaylist (NSTableDataSource)
381
382 - (int)numberOfRowsInTableView:(NSTableView *)o_tv
383 {
384     int i_count = 0;
385     intf_thread_t * p_intf = [NSApp getIntf];
386     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
387                                                        FIND_ANYWHERE );
388
389     if( p_playlist != NULL )
390     {
391         vlc_mutex_lock( &p_playlist->object_lock );
392         i_count = p_playlist->i_size;
393         vlc_mutex_unlock( &p_playlist->object_lock );
394         vlc_object_release( p_playlist );
395     }
396
397     return( i_count );
398 }
399
400 - (id)tableView:(NSTableView *)o_tv 
401                 objectValueForTableColumn:(NSTableColumn *)o_tc 
402                 row:(int)i_row
403 {
404     id o_value = nil;
405     intf_thread_t * p_intf = [NSApp getIntf];
406     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
407                                                FIND_ANYWHERE );
408
409     if( p_playlist == NULL )
410     {
411         return( nil );
412     }
413
414     vlc_mutex_lock( &p_playlist->object_lock );
415     o_value = [[NSString stringWithUTF8String: 
416         p_playlist->pp_items[i_row]->psz_name] lastPathComponent]; 
417     vlc_mutex_unlock( &p_playlist->object_lock ); 
418
419     vlc_object_release( p_playlist );
420
421     return( o_value );
422 }
423
424 - (BOOL)tableView:(NSTableView *)o_tv
425                     writeRows:(NSArray*)o_rows
426                     toPasteboard:(NSPasteboard*)o_pasteboard 
427 {
428     int i_rows = [o_rows count];
429     NSArray *o_filenames = [NSArray array];
430     
431     [o_pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:self];
432     [o_pasteboard setPropertyList:o_filenames forType:NSFilenamesPboardType];
433     if ( i_rows == 1 )
434     {
435         i_moveRow = [[o_rows objectAtIndex:0]intValue];
436         return YES;
437     }
438     return NO;
439 }
440
441 - (NSDragOperation)tableView:(NSTableView*)o_tv
442                     validateDrop:(id <NSDraggingInfo>)o_info
443                     proposedRow:(int)i_row
444                     proposedDropOperation:(NSTableViewDropOperation)o_operation 
445 {
446     if ( o_operation == NSTableViewDropAbove )
447     {
448         if ( i_moveRow >= 0 )
449         {
450             if ( i_row != i_moveRow )
451             {
452                 return NSDragOperationMove;
453             }
454             /* what if in the previous run, the row wasn't actually moved? 
455                then we can't drop new files on this location */
456             return NSDragOperationNone;
457         }
458         return NSDragOperationGeneric;
459     }
460     return NSDragOperationNone;
461 }
462
463 - (BOOL)tableView:(NSTableView*)o_tv
464                     acceptDrop:(id <NSDraggingInfo>)o_info
465                     row:(int)i_proposed_row
466                     dropOperation:(NSTableViewDropOperation)o_operation 
467 {
468     if (  i_moveRow >= 0 )
469     {
470         if (i_moveRow != -1 && i_proposed_row != -1)
471         {
472             intf_thread_t * p_intf = [NSApp getIntf];
473             playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
474                                                             FIND_ANYWHERE );
475         
476             if( p_playlist == NULL )
477             {
478                 i_moveRow = -1;
479                 return NO;
480             }
481     
482             playlist_Move( p_playlist, i_moveRow, i_proposed_row ); 
483         
484             vlc_object_release( p_playlist );
485         }
486         [self playlistUpdated];
487         i_moveRow = -1;
488         return YES;
489     }
490     else
491     {
492         NSPasteboard * o_pasteboard;
493         o_pasteboard = [o_info draggingPasteboard];
494         
495         if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
496         {
497             int i;
498             NSArray *o_array = [NSArray array];
499             NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType]
500                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
501
502             for( i = 0; i < (int)[o_values count]; i++)
503             {
504                 NSDictionary *o_dic;
505                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
506                 o_array = [o_array arrayByAddingObject: o_dic];
507             }
508             [self appendArray: o_array atPos: i_proposed_row enqueue:YES];
509
510             return( YES );
511         }
512         
513         return( NO );
514     }
515     [self updateRowSelection];
516 }
517
518 @end
519