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