4e15e065cb9cc83bce6f3e4b41d3461299b35006
[vlc.git] / extras / MacOSX / Framework / Sources / VLCMediaList.m
1 /*****************************************************************************
2  * VLCMediaList.m: VLC.framework VLCMediaList implementation
3  *****************************************************************************
4  * Copyright (C) 2007 Pierre d'Herbemont
5  * Copyright (C) 2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Pierre d'Herbemont <pdherbemont # videolan.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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "VLCMediaList.h"
26 #import "VLCLibrary.h"
27 #import "VLCEventManager.h"
28 #import "VLCLibVLCBridging.h"
29 #include <vlc/vlc.h>
30 #include <vlc/libvlc.h>
31
32 /* Notification Messages */
33 NSString *VLCMediaListItemAdded        = @"VLCMediaListItemAdded";
34 NSString *VLCMediaListItemDeleted    = @"VLCMediaListItemDeleted";
35
36 // TODO: Documentation
37 @interface VLCMediaList (Private)
38 /* Initializers */
39 - (void)initInternalMediaList;
40
41 /* Libvlc event bridges */
42 - (void)mediaListItemAdded:(NSDictionary *)args;
43 - (void)mediaListItemRemoved:(NSNumber *)index;
44 @end
45
46 /* libvlc event callback */
47 static void HandleMediaListItemAdded(const libvlc_event_t *event, void *user_data)
48 {
49     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
50     id self = user_data;
51     [[VLCEventManager sharedManager] callOnMainThreadObject:self 
52                                                  withMethod:@selector(mediaListItemAdded:) 
53                                        withArgumentAsObject:[NSDictionary dictionaryWithObjectsAndKeys:
54                                                           [VLCMedia mediaWithLibVLCMediaDescriptor:event->u.media_list_item_added.item], @"media",
55                                                           [NSNumber numberWithInt:event->u.media_list_item_added.index], @"index",
56                                                           nil]];
57     [pool release];
58 }
59
60 static void HandleMediaListItemDeleted( const libvlc_event_t * event, void * user_data)
61 {
62     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
63     id self = user_data;
64     [[VLCEventManager sharedManager] callOnMainThreadObject:self 
65                                                  withMethod:@selector(mediaListItemRemoved:) 
66                                        withArgumentAsObject:[NSNumber numberWithInt:event->u.media_list_item_deleted.index]];
67     [pool release];
68 }
69
70 @implementation VLCMediaList (KeyValueCodingCompliance)
71 /* For the @"media" key */
72 - (int) countOfMedia
73 {
74     return [self count];
75 }
76
77 - (id) objectInMediaAtIndex:(int)i
78 {
79     return [self mediaAtIndex:i];
80 }
81 @end
82
83 @implementation VLCMediaList
84 - (id)init
85 {
86     if (self = [super init])
87     {
88         // Create a new libvlc media list instance
89         libvlc_exception_t p_e;
90         libvlc_exception_init(&p_e);
91         p_mlist = libvlc_media_list_new([VLCLibrary sharedInstance], &p_e);
92         quit_on_exception(&p_e);
93         
94         // Initialize internals to defaults
95         cachedMedia = [[NSMutableArray alloc] init];
96         delegate = flatAspect = hierarchicalAspect = nil;
97         [self initInternalMediaList];
98     }
99     return self;
100 }
101
102 - (void)release
103 {
104     @synchronized(self)
105     {
106         if([self retainCount] <= 1)
107         {
108             /* We must make sure we won't receive new event after an upcoming dealloc
109              * We also may receive a -retain in some event callback that may occcur
110              * Before libvlc_event_detach. So this can't happen in dealloc */
111             libvlc_event_manager_t * p_em = libvlc_media_list_event_manager(p_mlist, NULL);
112             libvlc_event_detach(p_em, libvlc_MediaListItemDeleted, HandleMediaListItemDeleted, self, NULL);
113             libvlc_event_detach(p_em, libvlc_MediaListItemAdded,   HandleMediaListItemAdded,   self, NULL);
114         }
115         [super release];
116     }
117 }
118
119 - (void)dealloc
120 {
121     // Release allocated memory
122     libvlc_media_list_release(p_mlist);
123     [cachedMedia release];
124     [flatAspect release];
125     [hierarchicalAspect release];
126     [super dealloc];
127 }
128
129 - (NSString *)description
130 {
131     NSMutableString *content = [NSMutableString string];
132     int i;
133     [self lock];
134     for( i = 0; i < [self count]; i++)
135     {
136         [content appendFormat:@"%@\n", [self mediaAtIndex: i]];
137     }
138     [self unlock];
139     return [NSString stringWithFormat:@"<%@ %p> {\n%@}", [self className], self, content];
140 }
141
142 - (void)setDelegate:(id)value
143 {
144     delegate = value;
145 }
146
147 - (id)delegate
148 {
149     return delegate;
150 }
151
152 - (void)lock
153 {
154     libvlc_media_list_lock( p_mlist );
155 }
156
157 - (void)unlock
158 {
159     libvlc_media_list_unlock( p_mlist );
160 }
161
162 - (int)addMedia:(VLCMedia *)media
163 {
164     int index = [self count];
165     [self insertMedia:media atIndex:index];
166     return index;
167 }
168
169 - (void)insertMedia:(VLCMedia *)media atIndex: (int)index
170 {
171     [media retain];
172
173     // Add it to the libvlc's medialist
174     libvlc_exception_t p_e;
175     libvlc_exception_init( &p_e );
176     libvlc_media_list_insert_media_descriptor( p_mlist, [media libVLCMediaDescriptor], index, &p_e );
177     quit_on_exception( &p_e );
178 }
179
180 - (void)removeMediaAtIndex:(int)index
181 {
182     [[self mediaAtIndex:index] release];
183
184     // Remove it from the libvlc's medialist
185     libvlc_exception_t p_e;
186     libvlc_exception_init( &p_e );
187     libvlc_media_list_remove_index( p_mlist, index, &p_e );
188     quit_on_exception( &p_e );
189 }
190
191 - (VLCMedia *)mediaAtIndex:(int)index
192 {
193     return [cachedMedia objectAtIndex:index];
194 }
195
196 - (int)count
197 {
198     return [cachedMedia count];
199 }
200
201 - (int)indexOfMedia:(VLCMedia *)media
202 {
203     libvlc_exception_t p_e;
204     libvlc_exception_init( &p_e );
205     int result = libvlc_media_list_index_of_item( p_mlist, [media libVLCMediaDescriptor], &p_e );
206     quit_on_exception( &p_e );
207     
208     return result;
209 }
210
211 - (BOOL)isReadOnly
212 {
213     libvlc_exception_t p_e;
214     libvlc_exception_init( &p_e );
215     BOOL isReadOnly = libvlc_media_list_is_readonly( p_mlist );
216     quit_on_exception( &p_e );
217     
218     return isReadOnly;
219 }
220
221 /* Media list aspect */
222 - (VLCMediaListAspect *)hierarchicalAspect
223 {
224     if( hierarchicalAspect )
225         return hierarchicalAspect;
226     libvlc_media_list_view_t * p_mlv = libvlc_media_list_hierarchical_view( p_mlist, NULL );
227     hierarchicalAspect = [[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_mlv] retain];
228     libvlc_media_list_view_release( p_mlv );
229     return hierarchicalAspect;
230 }
231
232 - (VLCMediaListAspect *)hierarchicalNodeAspect
233 {
234     if( hierarchicalAspect )
235         return hierarchicalAspect;
236     libvlc_media_list_view_t * p_mlv = libvlc_media_list_hierarchical_node_view( p_mlist, NULL );
237     hierarchicalAspect = [[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_mlv] retain];
238     libvlc_media_list_view_release( p_mlv );
239     return hierarchicalAspect;
240 }
241
242 - (VLCMediaListAspect *)flatAspect
243 {
244     if( flatAspect )
245         return flatAspect;
246     libvlc_media_list_view_t * p_mlv = libvlc_media_list_flat_view( p_mlist, NULL );
247     flatAspect = [[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_mlv] retain];
248     libvlc_media_list_view_release( p_mlv );
249     return flatAspect;
250 }
251
252 @end
253
254 @implementation VLCMediaList (LibVLCBridging)
255 + (id)mediaListWithLibVLCMediaList:(void *)p_new_mlist;
256 {
257     return [[[VLCMediaList alloc] initWithLibVLCMediaList:p_new_mlist] autorelease];
258 }
259
260 - (id)initWithLibVLCMediaList:(void *)p_new_mlist;
261 {
262     if( self = [super init] )
263     {
264         p_mlist = p_new_mlist;
265         libvlc_media_list_retain(p_mlist);
266         libvlc_media_list_lock(p_mlist);
267         cachedMedia = [[NSMutableArray alloc] initWithCapacity:libvlc_media_list_count(p_mlist, NULL)];
268         int i, count = libvlc_media_list_count(p_mlist, NULL);
269         for( i = 0; i < count; i++ )
270         {
271             libvlc_media_descriptor_t * p_md = libvlc_media_list_item_at_index(p_mlist, i, NULL);
272             [cachedMedia addObject:[VLCMedia mediaWithLibVLCMediaDescriptor: p_md]];
273             libvlc_media_descriptor_release(p_md);
274         }
275         [self initInternalMediaList];
276         libvlc_media_list_unlock(p_mlist);
277     }
278     return self;
279 }
280
281 - (void *)libVLCMediaList
282 {
283     return p_mlist;
284 }
285 @end
286
287 @implementation VLCMediaList (Private)
288 - (void)initInternalMediaList
289 {
290     // Add event callbacks
291     libvlc_exception_t p_e;
292     libvlc_exception_init(&p_e);
293
294     libvlc_event_manager_t *p_em = libvlc_media_list_event_manager( p_mlist, &p_e );
295     libvlc_event_attach( p_em, libvlc_MediaListItemAdded,   HandleMediaListItemAdded,   self, &p_e );
296     libvlc_event_attach( p_em, libvlc_MediaListItemDeleted, HandleMediaListItemDeleted, self, &p_e );
297     
298     quit_on_exception( &p_e );
299 }
300
301 - (void)mediaListItemAdded:(NSDictionary *)args
302 {
303     int index = [[args objectForKey:@"index"] intValue];
304     VLCMedia * media = [args objectForKey:@"media"];
305
306     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"media"];
307     [cachedMedia insertObject:media atIndex:index];
308     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"media"];
309
310     // Post the notification
311     [[NSNotificationCenter defaultCenter] postNotificationName:VLCMediaListItemAdded
312                                                         object:self
313                                                       userInfo:args];
314
315     // Let the delegate know that the item was added
316     if (delegate && [delegate respondsToSelector:@selector(mediaList:mediaAdded:atIndex:)])
317         [delegate mediaList:self mediaAdded:media atIndex:index];
318 }
319
320 - (void)mediaListItemRemoved:(NSNumber *)index
321 {
322     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
323     [cachedMedia removeObjectAtIndex:[index intValue]];
324     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
325
326     // Post the notification
327     [[NSNotificationCenter defaultCenter] postNotificationName:VLCMediaListItemDeleted 
328                                                         object:self
329                                                       userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
330                                                           index, @"index",
331                                                           nil]];
332
333     // Let the delegate know that the item is being removed
334     if (delegate && [delegate respondsToSelector:@selector(mediaList:mediaRemovedAtIndex:)])
335         [delegate mediaList:self mediaRemovedAtIndex:[index intValue]];
336 }
337 @end