62818f8274c89b37848e43f52cdff28d70a282bf
[vlc.git] / src / control / media_list_view.c
1 /*****************************************************************************
2  * flat_media_list.c: libvlc flat media list functions. (extension to
3  * media_list.c).
4  *****************************************************************************
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 #include <vlc/libvlc.h>
26 #include <vlc/libvlc_media.h>
27 #include <vlc/libvlc_media_list.h>
28 #include <vlc/libvlc_media_list_view.h>
29 #include <vlc/libvlc_events.h>
30
31 #include "libvlc_internal.h"  // Abuse, could and should be removed
32
33 #include "media_internal.h"  // Abuse, could and should be removed
34 #include "media_list_internal.h"  // Abuse, could and should be removed
35 #include "media_list_view_internal.h"
36
37 //#define DEBUG_FLAT_LIST
38
39 #ifdef DEBUG_FLAT_LIST
40 # define trace( fmt, ... ) printf( "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__ )
41 #else
42 # define trace( ... )
43 #endif
44
45 /*
46  * Private functions
47  */
48 static void
49 media_list_item_added( const libvlc_event_t * p_event, void * p_user_data );
50 static void
51 media_list_item_removed( const libvlc_event_t * p_event, void * p_user_data );
52 static void
53 media_list_subitem_added( const libvlc_event_t * p_event, void * p_user_data );
54
55 static void
56 install_md_listener( libvlc_media_list_view_t * p_mlv,
57                      libvlc_media_t * p_md)
58 {
59     libvlc_media_list_t * p_mlist;
60     if((p_mlist = libvlc_media_subitems( p_md, NULL )))
61     {
62         libvlc_media_list_lock( p_mlist );
63         int i, count = libvlc_media_list_count( p_mlist, NULL );
64         for( i = 0; i < count; i++)
65         {
66             libvlc_event_t added_event;
67             libvlc_media_t * p_submd;
68             p_submd = libvlc_media_list_item_at_index( p_mlist, i, NULL );
69
70             /* Install our listeners */
71             install_md_listener( p_mlv, p_submd );
72
73             /* For each item, send a notification to the mlv subclasses */
74             added_event.u.media_list_item_added.item = p_submd;
75             added_event.u.media_list_item_added.index = 0;
76             if( p_mlv->pf_ml_item_added ) p_mlv->pf_ml_item_added( &added_event, p_mlv );
77             libvlc_media_release( p_submd );
78         }
79         libvlc_event_attach( p_mlist->p_event_manager,
80                              libvlc_MediaListItemAdded,
81                              media_list_item_added, p_mlv, NULL );
82         libvlc_event_attach( p_mlist->p_event_manager,
83                              libvlc_MediaListItemDeleted,
84                              media_list_item_removed, p_mlv, NULL );
85         libvlc_media_list_unlock( p_mlist );
86         libvlc_media_list_release( p_mlist );
87     }
88     else
89     {
90         /* No mlist, wait for a subitem added event */
91         libvlc_event_attach( p_md->p_event_manager,
92                             libvlc_MediaSubItemAdded,
93                             media_list_subitem_added, p_mlv, NULL );
94     }
95 }
96
97 static void
98 uninstall_md_listener( libvlc_media_list_view_t * p_mlv,
99                        libvlc_media_t * p_md)
100 {
101     libvlc_media_list_t * p_mlist;
102     libvlc_exception_t ignored_exception;
103     libvlc_exception_init( &ignored_exception );
104     libvlc_event_detach( p_md->p_event_manager,
105                          libvlc_MediaSubItemAdded,
106                          media_list_subitem_added, p_mlv, &ignored_exception );
107     if( libvlc_exception_raised( &ignored_exception ) )
108         libvlc_exception_clear( &ignored_exception ); /* We don't care if we encounter an exception */
109     if((p_mlist = libvlc_media_subitems( p_md, NULL )))
110     {
111         libvlc_media_list_lock( p_mlist );
112         libvlc_event_detach( p_mlist->p_event_manager,
113                              libvlc_MediaListItemAdded,
114                              media_list_item_added, p_mlv, NULL );
115         libvlc_event_detach( p_mlist->p_event_manager,
116                              libvlc_MediaListItemDeleted,
117                              media_list_item_removed, p_mlv, NULL );
118
119         int i, count = libvlc_media_list_count( p_mlist, NULL );
120         for( i = 0; i < count; i++)
121         {
122             libvlc_media_t * p_submd;
123             p_submd = libvlc_media_list_item_at_index( p_mlist,i, NULL );
124             uninstall_md_listener( p_mlv, p_submd );
125             libvlc_media_release( p_submd );
126         }
127         libvlc_media_list_unlock( p_mlist );
128         libvlc_media_list_release( p_mlist );
129     }
130 }
131
132 static void
133 media_list_item_added( const libvlc_event_t * p_event, void * p_user_data )
134 {
135     libvlc_media_list_view_t * p_mlv = p_user_data;
136     libvlc_media_t * p_md = p_event->u.media_list_item_added.item;
137     install_md_listener( p_mlv, p_md );
138     if( p_mlv->pf_ml_item_added ) p_mlv->pf_ml_item_added( p_event, p_mlv );
139 }
140
141 static void
142 media_list_item_removed( const libvlc_event_t * p_event, void * p_user_data )
143 {
144     libvlc_media_list_view_t * p_mlv = p_user_data;
145     libvlc_media_t * p_md = p_event->u.media_list_item_added.item;
146     uninstall_md_listener( p_mlv, p_md );
147     if( p_mlv->pf_ml_item_removed ) p_mlv->pf_ml_item_removed( p_event, p_mlv );
148 }
149
150 static void
151 media_list_subitem_added( const libvlc_event_t * p_event, void * p_user_data )
152 {
153     libvlc_media_list_t * p_mlist;
154     libvlc_event_t added_event;
155     libvlc_media_list_view_t * p_mlv = p_user_data;
156     libvlc_media_t * p_submd = p_event->u.media_subitem_added.new_child;
157     libvlc_media_t * p_md = p_event->p_obj;
158
159     if((p_mlist = libvlc_media_subitems( p_md, NULL )))
160     {
161         /* We have a mlist to which we're going to listen to events
162          * thus, no need to wait for SubItemAdded events */
163         libvlc_event_detach( p_md->p_event_manager,
164                              libvlc_MediaSubItemAdded,
165                              media_list_subitem_added, p_mlv, NULL );
166         libvlc_media_list_lock( p_mlist );
167
168         libvlc_event_attach( p_mlist->p_event_manager,
169                              libvlc_MediaListItemAdded,
170                              media_list_item_added, p_mlv, NULL );
171         libvlc_event_attach( p_mlist->p_event_manager,
172                              libvlc_MediaListItemDeleted,
173                              media_list_item_removed, p_mlv, NULL );
174         libvlc_media_list_unlock( p_mlist );
175         libvlc_media_list_release( p_mlist );
176     }
177
178     install_md_listener( p_mlv, p_submd );
179
180     added_event.u.media_list_item_added.item = p_submd;
181     added_event.u.media_list_item_added.index = 0;
182     if( p_mlv->pf_ml_item_added ) p_mlv->pf_ml_item_added( &added_event, p_mlv );
183 }
184
185 /*
186  * LibVLC Internal functions
187  */
188 /**************************************************************************
189  *       libvlc_media_list_view_set_ml_notification_callback (Internal)
190  * The mlist lock should be held when entered
191  **************************************************************************/
192 void
193 libvlc_media_list_view_set_ml_notification_callback(
194                 libvlc_media_list_view_t * p_mlv,
195                 void (*item_added)(const libvlc_event_t *, libvlc_media_list_view_t *),
196                 void (*item_removed)(const libvlc_event_t *, libvlc_media_list_view_t *) )
197 {
198     p_mlv->pf_ml_item_added = item_added;
199     p_mlv->pf_ml_item_removed = item_removed;
200     libvlc_event_attach( p_mlv->p_mlist->p_event_manager,
201                          libvlc_MediaListItemAdded,
202                          media_list_item_added, p_mlv, NULL );
203     libvlc_event_attach( p_mlv->p_mlist->p_event_manager,
204                          libvlc_MediaListItemDeleted,
205                          media_list_item_removed, p_mlv, NULL );
206     int i, count = libvlc_media_list_count( p_mlv->p_mlist, NULL );
207     for( i = 0; i < count; i++)
208     {
209         libvlc_media_t * p_md;
210         p_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, i, NULL );
211         install_md_listener( p_mlv, p_md );
212         libvlc_media_release( p_md );
213     }
214 }
215
216 /**************************************************************************
217  *       libvlc_media_list_view_notify_deletion (Internal)
218  **************************************************************************/
219 void
220 libvlc_media_list_view_will_delete_item(
221                 libvlc_media_list_view_t * p_mlv,
222                 libvlc_media_t * p_item,
223                 int index )
224 {
225     libvlc_event_t event;
226
227     /* Construct the event */
228     event.type = libvlc_MediaListViewWillDeleteItem;
229     event.u.media_list_view_will_delete_item.item = p_item;
230     event.u.media_list_view_will_delete_item.index = index;
231
232     /* Send the event */
233     libvlc_event_send( p_mlv->p_event_manager, &event );
234 }
235
236 /**************************************************************************
237  *       libvlc_media_list_view_item_deleted (Internal)
238  **************************************************************************/
239 void
240 libvlc_media_list_view_item_deleted(
241                 libvlc_media_list_view_t * p_mlv,
242                 libvlc_media_t * p_item,
243                 int index )
244 {
245     libvlc_event_t event;
246
247     /* Construct the event */
248     event.type = libvlc_MediaListViewItemDeleted;
249     event.u.media_list_view_item_deleted.item = p_item;
250     event.u.media_list_view_item_deleted.index = index;
251
252     /* Send the event */
253     libvlc_event_send( p_mlv->p_event_manager, &event );
254 }
255
256 /**************************************************************************
257  *       libvlc_media_list_view_will_add_item (Internal)
258  **************************************************************************/
259 void
260 libvlc_media_list_view_will_add_item(
261                 libvlc_media_list_view_t * p_mlv,
262                 libvlc_media_t * p_item,
263                 int index )
264 {
265     libvlc_event_t event;
266
267     /* Construct the event */
268     event.type = libvlc_MediaListViewWillAddItem;
269     event.u.media_list_view_will_add_item.item = p_item;
270     event.u.media_list_view_will_add_item.index = index;
271
272     /* Send the event */
273     libvlc_event_send( p_mlv->p_event_manager, &event );
274 }
275
276 /**************************************************************************
277  *       libvlc_media_list_view_item_added (Internal)
278  **************************************************************************/
279 void
280 libvlc_media_list_view_item_added(
281                 libvlc_media_list_view_t * p_mlv,
282                 libvlc_media_t * p_item,
283                 int index )
284 {
285     libvlc_event_t event;
286
287     /* Construct the event */
288     event.type = libvlc_MediaListViewItemAdded;
289     event.u.media_list_view_item_added.item = p_item;
290     event.u.media_list_view_item_added.index = index;
291
292     /* Send the event */
293     libvlc_event_send( p_mlv->p_event_manager, &event );
294 }
295
296 /**************************************************************************
297  *       libvlc_media_list_view_new (Internal)
298  **************************************************************************/
299 libvlc_media_list_view_t *
300 libvlc_media_list_view_new( libvlc_media_list_t * p_mlist,
301                             libvlc_media_list_view_count_func_t pf_count,
302                             libvlc_media_list_view_item_at_index_func_t pf_item_at_index,
303                             libvlc_media_list_view_children_at_index_func_t pf_children_at_index,
304                             libvlc_media_list_view_constructor_func_t pf_constructor,
305                             libvlc_media_list_view_release_func_t pf_release,
306                             void * this_view_data,
307                             libvlc_exception_t * p_e )
308 {
309     libvlc_media_list_view_t * p_mlv;
310     p_mlv = calloc( 1, sizeof(libvlc_media_list_view_t) );
311     if( !p_mlv )
312         return NULL;
313
314     p_mlv->p_libvlc_instance = p_mlist->p_libvlc_instance;
315     p_mlv->p_event_manager = libvlc_event_manager_new( p_mlist,
316                                     p_mlv->p_libvlc_instance, p_e );
317
318     libvlc_event_manager_register_event_type( p_mlv->p_event_manager,
319             libvlc_MediaListViewItemAdded, p_e );
320     libvlc_event_manager_register_event_type( p_mlv->p_event_manager,
321             libvlc_MediaListViewWillAddItem, p_e );
322     libvlc_event_manager_register_event_type( p_mlv->p_event_manager,
323             libvlc_MediaListViewItemDeleted, p_e );
324     libvlc_event_manager_register_event_type( p_mlv->p_event_manager,
325             libvlc_MediaListViewWillDeleteItem, p_e );
326
327     libvlc_media_list_retain( p_mlist );
328     p_mlv->p_mlist = p_mlist;
329
330     p_mlv->pf_count             = pf_count;
331     p_mlv->pf_item_at_index     = pf_item_at_index;
332     p_mlv->pf_children_at_index = pf_children_at_index;
333     p_mlv->pf_constructor       = pf_constructor;
334     p_mlv->pf_release           = pf_release;
335
336     p_mlv->p_this_view_data = this_view_data;
337
338     vlc_mutex_init( &p_mlv->object_lock );
339     p_mlv->i_refcount = 1;
340
341     return p_mlv;
342 }
343
344
345 /*
346  * Public libvlc functions
347  */
348
349 /**************************************************************************
350  *       libvlc_media_list_view_retain (Public)
351  **************************************************************************/
352 void
353 libvlc_media_list_view_retain( libvlc_media_list_view_t * p_mlv )
354 {
355     vlc_mutex_lock( &p_mlv->object_lock );
356     p_mlv->i_refcount++;
357     vlc_mutex_unlock( &p_mlv->object_lock );
358 }
359
360 /**************************************************************************
361  *       libvlc_media_list_view_release (Public)
362  **************************************************************************/
363 void
364 libvlc_media_list_view_release( libvlc_media_list_view_t * p_mlv )
365 {
366     vlc_mutex_lock( &p_mlv->object_lock );
367     p_mlv->i_refcount--;
368     if( p_mlv->i_refcount > 0 )
369     {
370         vlc_mutex_unlock( &p_mlv->object_lock );
371         return;
372     }
373     vlc_mutex_unlock( &p_mlv->object_lock );
374
375     /* Refcount null, time to free */
376     libvlc_media_list_lock( p_mlv->p_mlist );
377
378     if( p_mlv->pf_ml_item_added )
379     {
380         libvlc_event_detach( p_mlv->p_mlist->p_event_manager,
381                             libvlc_MediaListItemAdded,
382                             media_list_item_added, p_mlv, NULL );
383     }
384     if( p_mlv->pf_ml_item_removed )
385     {
386         libvlc_event_detach( p_mlv->p_mlist->p_event_manager,
387                             libvlc_MediaListItemDeleted,
388                             media_list_item_removed, p_mlv, NULL );
389     }
390     int i, count = libvlc_media_list_count( p_mlv->p_mlist, NULL );
391     for( i = 0; i < count; i++)
392     {
393         libvlc_media_t * p_md;
394         p_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, i, NULL );
395         uninstall_md_listener( p_mlv, p_md );
396         libvlc_media_release( p_md );
397     }
398     libvlc_media_list_unlock( p_mlv->p_mlist );
399
400     libvlc_event_manager_release( p_mlv->p_event_manager );
401
402     if( p_mlv->pf_release ) p_mlv->pf_release( p_mlv );
403     libvlc_media_list_release( p_mlv->p_mlist );
404     vlc_mutex_destroy( &p_mlv->object_lock );
405 }
406
407 /**************************************************************************
408  *       libvlc_media_list_view_event_manager (Public)
409  **************************************************************************/
410 libvlc_event_manager_t *
411 libvlc_media_list_view_event_manager( libvlc_media_list_view_t * p_mlv )
412 {
413     libvlc_event_manager_t * p_em;
414     vlc_mutex_lock( &p_mlv->object_lock );
415     p_em = p_mlv->p_event_manager;
416     vlc_mutex_unlock( &p_mlv->object_lock );
417     return p_em;
418 }
419
420 /**************************************************************************
421  *       libvlc_media_list_view_parent_media_list (Public)
422  **************************************************************************/
423 libvlc_media_list_t *
424 libvlc_media_list_view_parent_media_list( libvlc_media_list_view_t * p_mlv,
425                                          libvlc_exception_t * p_e)
426 {
427     (void)p_e;
428     libvlc_media_list_t * p_mlist;
429     vlc_mutex_lock( &p_mlv->object_lock );
430     p_mlist = p_mlv->p_mlist;
431     libvlc_media_list_retain( p_mlv->p_mlist );
432     vlc_mutex_unlock( &p_mlv->object_lock );
433     return p_mlist;
434 }
435
436 /**************************************************************************
437  *       libvlc_media_list_view_children_for_item (Public)
438  **************************************************************************/
439 libvlc_media_list_view_t *
440 libvlc_media_list_view_children_for_item( libvlc_media_list_view_t * p_mlv,
441                                           libvlc_media_t * p_md,
442                                           libvlc_exception_t * p_e)
443 {
444     (void)p_e;
445     libvlc_media_list_t * p_mlist;
446     libvlc_media_list_view_t * ret;
447
448     p_mlist = libvlc_media_subitems(p_md, p_e);
449     if(!p_mlist) return NULL;
450
451     ret = p_mlv->pf_constructor( p_mlist, p_e );
452     libvlc_media_list_release( p_mlist );
453
454     return ret;
455 }
456
457 /* Limited to four args, because it should be enough */
458
459 #define AN_SELECT( collapser, dec1, dec2, dec3, dec4, p, ...) p
460 #define ARGS(...) AN_SELECT( collapser, ##__VA_ARGS__, \
461                                               (p_mlv, arg1, arg2, arg3, arg4, p_e), \
462                                               (p_mlv, arg1, arg2, arg3, p_e), \
463                                               (p_mlv, arg1, arg2, p_e), \
464                                               (p_mlv, arg1, p_e), (p_mlv, p_e) )
465
466 #define MEDIA_LIST_VIEW_FUNCTION( name, ret_type, default_ret_value, /* Params */ ... ) \
467     ret_type \
468     libvlc_media_list_view_##name( libvlc_media_list_view_t * p_mlv, \
469                                   ##__VA_ARGS__, \
470                                   libvlc_exception_t * p_e ) \
471     { \
472         if( p_mlv->pf_##name ) \
473             return p_mlv->pf_##name ARGS(__VA_ARGS__) ; \
474         libvlc_exception_raise( p_e, "No '" #name "' method in this media_list_view" ); \
475         return default_ret_value;\
476     }
477
478 #define MEDIA_LIST_VIEW_FUNCTION_VOID_RET( name, /* Params */ ... ) \
479     void \
480     libvlc_media_list_view_##name( libvlc_media_list_view_t * p_mlv, \
481                                   ##__VA_ARGS__, \
482                                   libvlc_exception_t * p_e ) \
483     { \
484         if( p_mlv->pf_##name ) \
485         { \
486             p_mlv->pf_##name ARGS(__VA_ARGS__) ; \
487             return; \
488         } \
489         libvlc_exception_raise( p_e, "No '" #name "' method in this media_list_view" ); \
490     }
491
492
493 MEDIA_LIST_VIEW_FUNCTION( count, int, 0 )
494 MEDIA_LIST_VIEW_FUNCTION( item_at_index, libvlc_media_t *, NULL, int arg1 )
495 MEDIA_LIST_VIEW_FUNCTION( children_at_index, libvlc_media_list_view_t *, NULL, int arg1 )
496