]> git.sesse.net Git - vlc/blob - projects/macosx/framework/Sources/VLCMediaListAspect.m
e58fb5a07dfa08b2dcd3314488990c47cf32bfa5
[vlc] / projects / macosx / framework / Sources / VLCMediaListAspect.m
1 /*****************************************************************************
2  * VLCMediaListAspect.m: VLCKit.framework VLCMediaListAspect 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 "VLCMediaListAspect.h"
26 #import "VLCLibrary.h"
27 #import "VLCEventManager.h"
28 #import "VLCLibVLCBridging.h"
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc/vlc.h>
34 #include <vlc/libvlc.h>
35
36 // TODO: Documentation
37 @interface VLCMediaListAspect (Private)
38 /* Initializers */
39 - (void)initInternalMediaListView;
40
41 - (void)mediaListViewItemAdded:(NSArray *)args;
42 - (void)mediaListViewItemRemoved:(NSNumber *)index;
43 @end
44
45 @implementation VLCMediaListAspectNode
46 - (id)init
47 {
48     if(self = [super init])
49     {
50         media = nil;
51         children = nil;
52     }
53     return self;
54 }
55 - (void)dealloc
56 {
57     [media release];
58     [children release];
59     [super dealloc];
60 }
61
62 @synthesize media;
63 @synthesize children;
64
65 - (BOOL)isLeaf
66 {
67     return self.children == nil;
68 }
69
70 @end
71
72 @implementation VLCMediaListAspect (KeyValueCodingCompliance)
73 /* For the @"media" key */
74 - (NSInteger) countOfMedia
75 {
76     return [cachedNode count];
77 }
78 - (id) objectInMediaAtIndex:(NSInteger)i
79 {
80     return [[cachedNode objectAtIndex:i] media];
81 }
82 /* For the @"node" key */
83 - (NSInteger) countOfNode
84 {
85     return [cachedNode count];
86 }
87 - (id) objectInNodeAtIndex:(NSInteger)i
88 {
89     return [cachedNode objectAtIndex:i];
90 }
91 @end
92
93 /* libvlc event callback */
94 static void HandleMediaListViewItemAdded(const libvlc_event_t * event, void * user_data)
95 {
96     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
97     id self = user_data;
98     [[VLCEventManager sharedManager] callOnMainThreadObject:self
99                                                  withMethod:@selector(mediaListViewItemAdded:)
100                                        withArgumentAsObject:[NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:
101                                                           [VLCMedia mediaWithLibVLCMediaDescriptor:event->u.media_list_item_added.item], @"media",
102                                                           [NSNumber numberWithInt:event->u.media_list_item_added.index], @"index",
103                                                           nil]]];
104     [pool release];
105 }
106
107 static void HandleMediaListViewItemDeleted( const libvlc_event_t * event, void * user_data)
108 {
109     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
110     id self = user_data;
111     [[VLCEventManager sharedManager] callOnMainThreadObject:self
112                                                  withMethod:@selector(mediaListViewItemRemoved:)
113                                        withArgumentAsObject:[NSNumber numberWithInt:event->u.media_list_item_deleted.index]];
114     [pool release];
115 }
116
117 @implementation VLCMediaListAspect
118 - (void)dealloc
119 {
120     // Release allocated memory
121     libvlc_media_list_view_release(p_mlv);
122     [cachedNode release];
123     if( ownHisMediaList )
124         [parentMediaList release];
125     [super dealloc];
126 }
127
128 - (void)release
129 {
130     @synchronized(self)
131     {
132         if([self retainCount] <= 1)
133         {
134             /* We must make sure we won't receive new event after an upcoming dealloc
135              * We also may receive a -retain in some event callback that may occcur
136              * Before libvlc_event_detach. So this can't happen in dealloc */
137             libvlc_event_manager_t * p_em = libvlc_media_list_view_event_manager(p_mlv);
138             libvlc_event_detach(p_em, libvlc_MediaListViewItemDeleted, HandleMediaListViewItemDeleted, self);
139             libvlc_event_detach(p_em, libvlc_MediaListViewItemAdded,   HandleMediaListViewItemAdded,   self);
140         }
141         [super release];
142     }
143 }
144
145 - (NSString *)description
146 {
147     NSMutableString * content = [NSMutableString string];
148     NSUInteger i;
149     for( i = 0; i < [self count]; i++)
150     {
151         [content appendFormat:@"%@\n", [self mediaAtIndex: i]];
152     }
153     return [NSString stringWithFormat:@"<%@ %p> {\n%@}", [self className], self, content];
154 }
155
156 - (VLCMedia *)mediaAtIndex:(NSInteger)index
157 {
158     libvlc_exception_t p_e;
159     libvlc_exception_init( &p_e );
160     libvlc_media_t * p_md = libvlc_media_list_view_item_at_index( p_mlv, index, &p_e );
161     catch_exception( &p_e );
162
163     // Returns local object for media descriptor, searchs for user data first.  If not found it creates a
164     // new cocoa object representation of the media descriptor.
165     return [VLCMedia mediaWithLibVLCMediaDescriptor:p_md];
166 }
167
168 - (VLCMediaListAspect *)childrenAtIndex:(NSInteger)index
169 {
170     libvlc_exception_t p_e;
171     libvlc_exception_init( &p_e );
172     libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_at_index( p_mlv, index, &p_e );
173     catch_exception( &p_e );
174
175     if( !p_sub_mlv )
176         return nil;
177
178     // Returns local object for media descriptor, searchs for user data first.  If not found it creates a
179     // new cocoa object representation of the media descriptor.
180     return [VLCMediaListAspect mediaListAspectWithLibVLCMediaListView:p_sub_mlv];
181 }
182
183 - (VLCMediaListAspectNode *)nodeAtIndex:(NSInteger)index
184 {
185     VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
186     [node setMedia:[self mediaAtIndex: index]];
187     libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_for_item([self libVLCMediaListView], [node.media libVLCMediaDescriptor], NULL);
188     if( p_sub_mlv )
189     {
190         [node setChildren:[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv]];
191         libvlc_media_list_view_release(p_sub_mlv);
192     }
193     return node;
194 }
195
196 - (NSInteger)count
197 {
198     libvlc_exception_t p_e;
199     libvlc_exception_init( &p_e );
200     NSInteger result = libvlc_media_list_view_count( p_mlv, &p_e );
201     catch_exception( &p_e );
202
203     return result;
204 }
205
206 - (VLCMediaList *)parentMediaList
207 {
208     return parentMediaList;
209 }
210 @end
211
212 @implementation VLCMediaListAspect (LibVLCBridging)
213 + (id)mediaListAspectWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv
214 {
215     return [[[VLCMediaListAspect alloc] initWithLibVLCMediaListView:p_new_mlv andMediaList:nil] autorelease];
216 }
217
218 + (id)mediaListAspectWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv andMediaList:(VLCMediaList *)mediaList;
219 {
220     return [[[VLCMediaListAspect alloc] initWithLibVLCMediaListView:p_new_mlv andMediaList:mediaList] autorelease];
221 }
222
223 - (id)initWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv andMediaList:(VLCMediaList *)mediaList;
224 {
225     if( self = [super init] )
226     {
227         p_mlv = p_new_mlv;
228         libvlc_media_list_view_retain(p_mlv);
229
230         /* parentMediaList isn't retained, because we need a mediaList to exists, and not the contrary */
231         parentMediaList = mediaList;
232         ownHisMediaList = NO;
233         if( !parentMediaList )
234         {
235             /* We have to create it then */
236             libvlc_media_list_view_retain(p_mlv);
237             libvlc_media_list_t * p_mlist = libvlc_media_list_view_parent_media_list(p_mlv, NULL);
238             parentMediaList = [[VLCMediaList mediaListWithLibVLCMediaList: p_mlist] retain];
239             libvlc_media_list_release( p_mlist );
240             /* This is an exception, and we owns it here */
241             ownHisMediaList = YES;
242         }
243
244         cachedNode = [[NSMutableArray alloc] initWithCapacity:libvlc_media_list_view_count(p_mlv, NULL)];
245         libvlc_media_list_t * p_mlist;
246         p_mlist = libvlc_media_list_view_parent_media_list( p_mlv, NULL );
247         libvlc_media_list_lock( p_mlist );
248         NSUInteger i, count = libvlc_media_list_view_count(p_mlv, NULL);
249         for( i = 0; i < count; i++ )
250         {
251             libvlc_media_t * p_md = libvlc_media_list_view_item_at_index(p_mlv, i, NULL);
252             libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_at_index(p_mlv, i, NULL);
253             VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
254             [node setMedia:[VLCMedia mediaWithLibVLCMediaDescriptor: p_md]];
255             [node setChildren: p_sub_mlv ? [VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv] : nil];
256             if( p_sub_mlv ) NSAssert(![node isLeaf], @"Not leaf");
257
258             [cachedNode addObject:node];
259             libvlc_media_release(p_md);
260             if( p_sub_mlv ) libvlc_media_list_view_release(p_sub_mlv);
261         }
262         [self initInternalMediaListView];
263         libvlc_media_list_unlock( p_mlist );
264         libvlc_media_list_release( p_mlist );
265     }
266     return self;
267 }
268
269 - (libvlc_media_list_view_t *)libVLCMediaListView
270 {
271     return (libvlc_media_list_view_t *)p_mlv;
272 }
273 @end
274
275 @implementation VLCMediaListAspect (Private)
276 - (void)initInternalMediaListView
277 {
278     libvlc_event_manager_t * p_em = libvlc_media_list_event_manager(p_mlv);
279
280     /* Add internal callback */
281     libvlc_event_attach(p_em, libvlc_MediaListViewItemAdded,   HandleMediaListViewItemAdded,   self);
282     libvlc_event_attach(p_em, libvlc_MediaListViewItemDeleted, HandleMediaListViewItemDeleted, self);
283 }
284
285 - (void)mediaListViewItemAdded:(NSArray *)arrayOfArgs
286 {
287     NSAssert([NSThread isMainThread], @"We are not on main thread");
288
289     /* We hope to receive index in a nide range, that could change one day */
290     NSInteger start = [[[arrayOfArgs objectAtIndex: 0] objectForKey:@"index"] intValue];
291     NSInteger end = [[[arrayOfArgs objectAtIndex: [arrayOfArgs count]-1] objectForKey:@"index"] intValue];
292     NSRange range = NSMakeRange(start, end-start);
293
294     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"media"];
295     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"node"];
296     for( NSDictionary * args in arrayOfArgs )
297     {
298         NSInteger index = [[args objectForKey:@"index"] intValue];
299         VLCMedia * media = [args objectForKey:@"media"];
300         VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
301         [node setMedia:media];
302
303         /* Set the sub media list view we enventually have */
304         libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_for_item([self libVLCMediaListView], [media libVLCMediaDescriptor], NULL);
305
306         if( p_sub_mlv )
307         {
308             [node setChildren:[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv]];
309             libvlc_media_list_view_release(p_sub_mlv);
310             NSAssert(![node isLeaf], @"Not leaf");
311         }
312
313         /* Sanity check */
314         if( index && index > [cachedNode count] )
315             index = [cachedNode count];
316         [cachedNode insertObject:node atIndex:index];
317     }
318     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"node"];
319     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"media"];
320 }
321
322 - (void)mediaListViewItemRemoved:(NSNumber *)index
323 {
324     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
325     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"node"];
326     [cachedNode removeObjectAtIndex:[index intValue]];
327     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"node"];
328     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
329 }
330 @end