1 /*****************************************************************************
2 * VLCMediaListAspect.m: VLCKit.framework VLCMediaListAspect implementation
3 *****************************************************************************
4 * Copyright (C) 2007 Pierre d'Herbemont
5 * Copyright (C) 2007 the VideoLAN team
8 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
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.
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.
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 *****************************************************************************/
25 #import "VLCMediaListAspect.h"
26 #import "VLCLibrary.h"
27 #import "VLCEventManager.h"
28 #import "VLCLibVLCBridging.h"
34 #include <vlc/libvlc.h>
36 // TODO: Documentation
37 @interface VLCMediaListAspect (Private)
39 - (void)initInternalMediaListView;
41 - (void)mediaListViewItemAdded:(NSArray *)args;
42 - (void)mediaListViewItemRemoved:(NSNumber *)index;
45 @implementation VLCMediaListAspectNode
48 if(self = [super init])
67 return self.children == nil;
72 @implementation VLCMediaListAspect (KeyValueCodingCompliance)
73 /* For the @"media" key */
74 - (NSInteger) countOfMedia
76 return [cachedNode count];
78 - (id) objectInMediaAtIndex:(NSInteger)i
80 return [[cachedNode objectAtIndex:i] media];
82 /* For the @"node" key */
83 - (NSInteger) countOfNode
85 return [cachedNode count];
87 - (id) objectInNodeAtIndex:(NSInteger)i
89 return [cachedNode objectAtIndex:i];
93 /* libvlc event callback */
94 static void HandleMediaListViewItemAdded(const libvlc_event_t * event, void * user_data)
96 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
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",
107 static void HandleMediaListViewItemDeleted( const libvlc_event_t * event, void * user_data)
109 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
111 [[VLCEventManager sharedManager] callOnMainThreadObject:self
112 withMethod:@selector(mediaListViewItemRemoved:)
113 withArgumentAsObject:[NSNumber numberWithInt:event->u.media_list_item_deleted.index]];
117 @implementation VLCMediaListAspect
120 // Release allocated memory
121 libvlc_media_list_view_release(p_mlv);
122 [cachedNode release];
123 if( ownHisMediaList )
124 [parentMediaList release];
132 if([self retainCount] <= 1)
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);
145 - (NSString *)description
147 NSMutableString * content = [NSMutableString string];
149 for( i = 0; i < [self count]; i++)
151 [content appendFormat:@"%@\n", [self mediaAtIndex: i]];
153 return [NSString stringWithFormat:@"<%@ %p> {\n%@}", [self className], self, content];
156 - (VLCMedia *)mediaAtIndex:(NSInteger)index
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 );
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];
168 - (VLCMediaListAspect *)childrenAtIndex:(NSInteger)index
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 );
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];
183 - (VLCMediaListAspectNode *)nodeAtIndex:(NSInteger)index
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);
190 [node setChildren:[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv]];
191 libvlc_media_list_view_release(p_sub_mlv);
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 );
206 - (VLCMediaList *)parentMediaList
208 return parentMediaList;
212 @implementation VLCMediaListAspect (LibVLCBridging)
213 + (id)mediaListAspectWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv
215 return [[[VLCMediaListAspect alloc] initWithLibVLCMediaListView:p_new_mlv andMediaList:nil] autorelease];
218 + (id)mediaListAspectWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv andMediaList:(VLCMediaList *)mediaList;
220 return [[[VLCMediaListAspect alloc] initWithLibVLCMediaListView:p_new_mlv andMediaList:mediaList] autorelease];
223 - (id)initWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv andMediaList:(VLCMediaList *)mediaList;
225 if( self = [super init] )
228 libvlc_media_list_view_retain(p_mlv);
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 )
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;
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++ )
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");
258 [cachedNode addObject:node];
259 libvlc_media_release(p_md);
260 if( p_sub_mlv ) libvlc_media_list_view_release(p_sub_mlv);
262 [self initInternalMediaListView];
263 libvlc_media_list_unlock( p_mlist );
264 libvlc_media_list_release( p_mlist );
269 - (libvlc_media_list_view_t *)libVLCMediaListView
271 return (libvlc_media_list_view_t *)p_mlv;
275 @implementation VLCMediaListAspect (Private)
276 - (void)initInternalMediaListView
278 libvlc_exception_t e;
279 libvlc_exception_init(&e);
281 libvlc_event_manager_t * p_em = libvlc_media_list_event_manager(p_mlv);
283 /* Add internal callback */
284 libvlc_event_attach(p_em, libvlc_MediaListViewItemAdded, HandleMediaListViewItemAdded, self, &e);
285 libvlc_event_attach(p_em, libvlc_MediaListViewItemDeleted, HandleMediaListViewItemDeleted, self, &e);
289 - (void)mediaListViewItemAdded:(NSArray *)arrayOfArgs
291 NSAssert([NSThread isMainThread], @"We are not on main thread");
293 /* We hope to receive index in a nide range, that could change one day */
294 NSInteger start = [[[arrayOfArgs objectAtIndex: 0] objectForKey:@"index"] intValue];
295 NSInteger end = [[[arrayOfArgs objectAtIndex: [arrayOfArgs count]-1] objectForKey:@"index"] intValue];
296 NSRange range = NSMakeRange(start, end-start);
298 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"media"];
299 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"node"];
300 for( NSDictionary * args in arrayOfArgs )
302 NSInteger index = [[args objectForKey:@"index"] intValue];
303 VLCMedia * media = [args objectForKey:@"media"];
304 VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
305 [node setMedia:media];
307 /* Set the sub media list view we enventually have */
308 libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_for_item([self libVLCMediaListView], [media libVLCMediaDescriptor], NULL);
312 [node setChildren:[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv]];
313 libvlc_media_list_view_release(p_sub_mlv);
314 NSAssert(![node isLeaf], @"Not leaf");
318 if( index && index > [cachedNode count] )
319 index = [cachedNode count];
320 [cachedNode insertObject:node atIndex:index];
322 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"node"];
323 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"media"];
326 - (void)mediaListViewItemRemoved:(NSNumber *)index
328 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
329 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"node"];
330 [cachedNode removeObjectAtIndex:[index intValue]];
331 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"node"];
332 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];