MacOSX/Framework: Support event collapsing, and save the media list thats own the...
[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         //libvlc_media_list_lock(p_mlv->p_mlist);
206         cachedNode = [[NSMutableArray alloc] initWithCapacity:libvlc_media_list_view_count(p_mlv, NULL)];
207         libvlc_media_list_t * p_mlist;
208         p_mlist = libvlc_media_list_view_parent_media_list( p_mlv, NULL );
209         libvlc_media_list_lock( p_mlist );
210         int i, count = libvlc_media_list_view_count(p_mlv, NULL);
211         for( i = 0; i < count; i++ )
212         {
213             libvlc_media_descriptor_t * p_md = libvlc_media_list_view_item_at_index(p_mlv, i, NULL);
214             libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_at_index(p_mlv, i, NULL);
215             VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
216             [node setMedia:[VLCMedia mediaWithLibVLCMediaDescriptor: p_md]];
217             [node setChildren: p_sub_mlv ? [VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv] : nil];
218             [cachedNode addObject:node];
219             libvlc_media_descriptor_release(p_md);
220             if( p_sub_mlv ) libvlc_media_list_view_release(p_sub_mlv);
221         }
222         [self initInternalMediaListView];
223         libvlc_media_list_unlock( p_mlist );
224         libvlc_media_list_release( p_mlist );
225         //libvlc_media_list_unlock(p_mlv->p_mlist);
226     }
227     return self;
228 }
229
230 - (libvlc_media_list_view_t *)libVLCMediaListView
231 {
232     return (libvlc_media_list_view_t *)p_mlv;
233 }
234 @end
235
236 @implementation VLCMediaListAspect (Private)
237 - (void)initInternalMediaListView
238 {
239     libvlc_exception_t e;
240     libvlc_exception_init( &e );
241
242     libvlc_event_manager_t *p_em = libvlc_media_list_event_manager( p_mlv, &e );
243
244     /* Add internal callback */
245     libvlc_event_attach( p_em, libvlc_MediaListViewItemAdded,   HandleMediaListViewItemAdded,   self, &e );
246     libvlc_event_attach( p_em, libvlc_MediaListViewItemDeleted, HandleMediaListViewItemDeleted, self, &e );
247     quit_on_exception( &e );
248 }
249
250 - (void)mediaListViewItemAdded:(NSArray *)arrayOfArgs
251 {
252     NSAssert([NSThread isMainThread], @"We are not on main thread");
253
254     /* We hope to receive index in a nide range, that could change one day */
255     int start = [[[arrayOfArgs objectAtIndex: 0] objectForKey:@"index"] intValue];
256     int end = [[[arrayOfArgs objectAtIndex: [arrayOfArgs count]-1] objectForKey:@"index"] intValue];
257     NSRange range = NSMakeRange(start, end-start);
258
259     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"media"];
260     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"node"];
261     int i = [[[arrayOfArgs objectAtIndex: 0] objectForKey:@"index"] intValue];
262     for( NSDictionary * args in arrayOfArgs )
263     {
264         int index = [[args objectForKey:@"index"] intValue];
265         VLCMedia * media = [args objectForKey:@"media"];
266         VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
267         [node setMedia:media];
268         [node setChildren:[self childrenAtIndex:index]];
269         /* Sanity check */
270         NSAssert( i == index, @"Expects some troubles, inserted items are not in a range" ); i++;
271         if( index && index >= [cachedNode count] )
272             index = [cachedNode count] - 1;
273         [cachedNode insertObject:node atIndex:index];
274     }
275     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"node"];
276     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"media"];
277 }
278
279 - (void)mediaListViewItemRemoved:(NSNumber *)index
280 {
281     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
282     [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"node"];
283     [cachedNode removeObjectAtIndex:[index intValue]];
284     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"node"];
285     [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
286 }
287 @end