]> git.sesse.net Git - vlc/blob - src/control/media_list_view.c
async event handling: Push is now O(1) instead of O(n).
[vlc] / 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 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc/libvlc.h>
30 #include <vlc/libvlc_media.h>
31 #include <vlc/libvlc_media_list.h>
32 #include <vlc/libvlc_media_list_view.h>
33 #include <vlc/libvlc_events.h>
34
35 #include "libvlc_internal.h"  // Abuse, could and should be removed
36
37 #include "media_internal.h"  // Abuse, could and should be removed
38 #include "media_list_internal.h"  // Abuse, could and should be removed
39 #include "media_list_view_internal.h"
40
41 //#define DEBUG_FLAT_LIST
42
43 #ifdef DEBUG_FLAT_LIST
44 # define trace( fmt, ... ) printf( "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__ )
45 #else
46 # define trace( ... )
47 #endif
48
49 /*
50  * Private functions
51  */
52 static void
53 media_list_item_added( const libvlc_event_t * p_event, void * p_user_data );
54 static void
55 media_list_item_removed( const libvlc_event_t * p_event, void * p_user_data );
56 static void
57 media_list_subitem_added( const libvlc_event_t * p_event, void * p_user_data );
58
59 static void
60 install_md_listener( libvlc_media_list_view_t * p_mlv,
61                      libvlc_media_t * p_md)
62 {
63     libvlc_media_list_t * p_mlist;
64     if((p_mlist = libvlc_media_subitems( p_md )))
65     {
66         libvlc_media_list_lock( p_mlist );
67         int i, count = libvlc_media_list_count( p_mlist );
68         for( i = 0; i < count; i++)
69         {
70             libvlc_event_t added_event;
71             libvlc_media_t * p_submd;
72             p_submd = libvlc_media_list_item_at_index( p_mlist, i, NULL );
73
74             /* Install our listeners */
75             install_md_listener( p_mlv, p_submd );
76
77             /* For each item, send a notification to the mlv subclasses */
78             added_event.u.media_list_item_added.item = p_submd;
79             added_event.u.media_list_item_added.index = 0;
80             if( p_mlv->pf_ml_item_added ) p_mlv->pf_ml_item_added( &added_event, p_mlv );
81             libvlc_media_release( p_submd );
82         }
83         libvlc_event_attach( p_mlist->p_event_manager,
84                              libvlc_MediaListItemAdded,
85                              media_list_item_added, p_mlv );
86         libvlc_event_attach( p_mlist->p_event_manager,
87                              libvlc_MediaListItemDeleted,
88                              media_list_item_removed, p_mlv );
89         libvlc_media_list_unlock( p_mlist );
90         libvlc_media_list_release( p_mlist );
91     }
92     else
93     {
94         /* No mlist, wait for a subitem added event */
95         libvlc_event_attach( p_md->p_event_manager,
96                             libvlc_MediaSubItemAdded,
97                             media_list_subitem_added, p_mlv );
98     }
99 }
100
101 static void
102 uninstall_md_listener( libvlc_media_list_view_t * p_mlv,
103                        libvlc_media_t * p_md)
104 {
105     libvlc_media_list_t * p_mlist;
106     libvlc_event_detach( p_md->p_event_manager,
107                          libvlc_MediaSubItemAdded,
108                          media_list_subitem_added, p_mlv );
109     if((p_mlist = libvlc_media_subitems( p_md )))
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 );
115         libvlc_event_detach( p_mlist->p_event_manager,
116                              libvlc_MediaListItemDeleted,
117                              media_list_item_removed, p_mlv );
118
119         int i, count = libvlc_media_list_count( p_mlist );
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 )))
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 );
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 );
171         libvlc_event_attach( p_mlist->p_event_manager,
172                              libvlc_MediaListItemDeleted,
173                              media_list_item_removed, p_mlv );
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 );
203     libvlc_event_attach( p_mlv->p_mlist->p_event_manager,
204                          libvlc_MediaListItemDeleted,
205                          media_list_item_removed, p_mlv );
206     int i, count = libvlc_media_list_count( p_mlv->p_mlist );
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 {
308     libvlc_media_list_view_t * p_mlv;
309     p_mlv = calloc( 1, sizeof(libvlc_media_list_view_t) );
310     if( unlikely(p_mlv == NULL) )
311     {
312         libvlc_printerr( "Not enough memory" );
313         return NULL;
314     }
315
316     p_mlv->p_libvlc_instance = p_mlist->p_libvlc_instance;
317     p_mlv->p_event_manager = libvlc_event_manager_new( p_mlist,
318                                     p_mlv->p_libvlc_instance );
319     if( unlikely(p_mlv->p_event_manager == NULL) )
320     {
321         free(p_mlv);
322         return NULL;
323     }
324
325     libvlc_event_manager_register_event_type( p_mlv->p_event_manager,
326             libvlc_MediaListViewItemAdded );
327     libvlc_event_manager_register_event_type( p_mlv->p_event_manager,
328             libvlc_MediaListViewWillAddItem );
329     libvlc_event_manager_register_event_type( p_mlv->p_event_manager,
330             libvlc_MediaListViewItemDeleted );
331     libvlc_event_manager_register_event_type( p_mlv->p_event_manager,
332             libvlc_MediaListViewWillDeleteItem );
333
334     libvlc_media_list_retain( p_mlist );
335     p_mlv->p_mlist = p_mlist;
336
337     p_mlv->pf_count             = pf_count;
338     p_mlv->pf_item_at_index     = pf_item_at_index;
339     p_mlv->pf_children_at_index = pf_children_at_index;
340     p_mlv->pf_constructor       = pf_constructor;
341     p_mlv->pf_release           = pf_release;
342
343     p_mlv->p_this_view_data = this_view_data;
344
345     vlc_mutex_init( &p_mlv->object_lock );
346     p_mlv->i_refcount = 1;
347
348     return p_mlv;
349 }
350
351
352 /*
353  * Public libvlc functions
354  */
355
356 /**************************************************************************
357  *       libvlc_media_list_view_retain (Public)
358  **************************************************************************/
359 void
360 libvlc_media_list_view_retain( libvlc_media_list_view_t * p_mlv )
361 {
362     vlc_mutex_lock( &p_mlv->object_lock );
363     p_mlv->i_refcount++;
364     vlc_mutex_unlock( &p_mlv->object_lock );
365 }
366
367 /**************************************************************************
368  *       libvlc_media_list_view_release (Public)
369  **************************************************************************/
370 void
371 libvlc_media_list_view_release( libvlc_media_list_view_t * p_mlv )
372 {
373     vlc_mutex_lock( &p_mlv->object_lock );
374     p_mlv->i_refcount--;
375     if( p_mlv->i_refcount > 0 )
376     {
377         vlc_mutex_unlock( &p_mlv->object_lock );
378         return;
379     }
380     vlc_mutex_unlock( &p_mlv->object_lock );
381
382     /* Refcount null, time to free */
383     libvlc_media_list_lock( p_mlv->p_mlist );
384
385     if( p_mlv->pf_ml_item_added )
386     {
387         libvlc_event_detach( p_mlv->p_mlist->p_event_manager,
388                             libvlc_MediaListItemAdded,
389                             media_list_item_added, p_mlv );
390     }
391     if( p_mlv->pf_ml_item_removed )
392     {
393         libvlc_event_detach( p_mlv->p_mlist->p_event_manager,
394                             libvlc_MediaListItemDeleted,
395                             media_list_item_removed, p_mlv );
396     }
397     int i, count = libvlc_media_list_count( p_mlv->p_mlist );
398     for( i = 0; i < count; i++)
399     {
400         libvlc_media_t * p_md;
401         p_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, i, NULL );
402         uninstall_md_listener( p_mlv, p_md );
403         libvlc_media_release( p_md );
404     }
405     libvlc_media_list_unlock( p_mlv->p_mlist );
406
407     libvlc_event_manager_release( p_mlv->p_event_manager );
408
409     if( p_mlv->pf_release ) p_mlv->pf_release( p_mlv );
410     libvlc_media_list_release( p_mlv->p_mlist );
411     vlc_mutex_destroy( &p_mlv->object_lock );
412 }
413
414 /**************************************************************************
415  *       libvlc_media_list_view_event_manager (Public)
416  **************************************************************************/
417 libvlc_event_manager_t *
418 libvlc_media_list_view_event_manager( libvlc_media_list_view_t * p_mlv )
419 {
420     libvlc_event_manager_t * p_em;
421     vlc_mutex_lock( &p_mlv->object_lock );
422     p_em = p_mlv->p_event_manager;
423     vlc_mutex_unlock( &p_mlv->object_lock );
424     return p_em;
425 }
426
427 /**************************************************************************
428  *       libvlc_media_list_view_parent_media_list (Public)
429  **************************************************************************/
430 libvlc_media_list_t *
431 libvlc_media_list_view_parent_media_list( libvlc_media_list_view_t * p_mlv,
432                                          libvlc_exception_t * p_e)
433 {
434     (void)p_e;
435     libvlc_media_list_t * p_mlist;
436     vlc_mutex_lock( &p_mlv->object_lock );
437     p_mlist = p_mlv->p_mlist;
438     libvlc_media_list_retain( p_mlv->p_mlist );
439     vlc_mutex_unlock( &p_mlv->object_lock );
440     return p_mlist;
441 }
442
443 /**************************************************************************
444  *       libvlc_media_list_view_children_for_item (Public)
445  **************************************************************************/
446 libvlc_media_list_view_t *
447 libvlc_media_list_view_children_for_item( libvlc_media_list_view_t * p_mlv,
448                                           libvlc_media_t * p_md,
449                                           libvlc_exception_t * p_e)
450 {
451     (void)p_e;
452     libvlc_media_list_t * p_mlist;
453     libvlc_media_list_view_t * ret;
454
455     p_mlist = libvlc_media_subitems(p_md);
456     if(!p_mlist) return NULL;
457
458     ret = p_mlv->pf_constructor( p_mlist, p_e );
459     libvlc_media_list_release( p_mlist );
460
461     return ret;
462 }
463
464 /* Limited to four args, because it should be enough */
465
466 #define AN_SELECT( collapser, dec1, dec2, dec3, dec4, p, ...) p
467 #define ARGS(...) AN_SELECT( collapser, ##__VA_ARGS__, \
468                                               (p_mlv, arg1, arg2, arg3, arg4, p_e), \
469                                               (p_mlv, arg1, arg2, arg3, p_e), \
470                                               (p_mlv, arg1, arg2, p_e), \
471                                               (p_mlv, arg1, p_e), (p_mlv, p_e) )
472
473 #define MEDIA_LIST_VIEW_FUNCTION( name, ret_type, default_ret_value, /* Params */ ... ) \
474     ret_type \
475     libvlc_media_list_view_##name( libvlc_media_list_view_t * p_mlv, \
476                                   ##__VA_ARGS__, \
477                                   libvlc_exception_t * p_e ) \
478     { \
479         if( p_mlv->pf_##name ) \
480             return p_mlv->pf_##name ARGS(__VA_ARGS__) ; \
481         libvlc_exception_raise( p_e ); \
482         libvlc_printerr( "No '" #name "' method in this media_list_view" ); \
483         return default_ret_value;\
484     }
485
486 #define MEDIA_LIST_VIEW_FUNCTION_VOID_RET( name, /* Params */ ... ) \
487     void \
488     libvlc_media_list_view_##name( libvlc_media_list_view_t * p_mlv, \
489                                   ##__VA_ARGS__, \
490                                   libvlc_exception_t * p_e ) \
491     { \
492         if( p_mlv->pf_##name ) \
493         { \
494             p_mlv->pf_##name ARGS(__VA_ARGS__) ; \
495             return; \
496         } \
497         libvlc_exception_raise( p_e ); \
498         libvlc_printerr( "No '" #name "' method in this media_list_view" ); \
499     }
500
501
502 MEDIA_LIST_VIEW_FUNCTION( count, int, 0 )
503 MEDIA_LIST_VIEW_FUNCTION( item_at_index, libvlc_media_t *, NULL, int arg1 )
504 MEDIA_LIST_VIEW_FUNCTION( children_at_index, libvlc_media_list_view_t *, NULL, int arg1 )
505