]> git.sesse.net Git - vlc/blob - src/playlist/item.c
playlist: Listen to duration changed events, and remove a vout->playlist dependency.
[vlc] / src / playlist / item.c
1 /*****************************************************************************
2  * item.c : Playlist item creation/deletion/add/removal functions
3  *****************************************************************************
4  * Copyright (C) 1999-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          ClĂ©ment Stenac <zorglub@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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc_common.h>
29 #include <assert.h>
30 #include <vlc_playlist.h>
31 #include "playlist_internal.h"
32
33 static void AddItem( playlist_t *p_playlist, playlist_item_t *p_item,
34                      playlist_item_t *p_node, int i_mode, int i_pos );
35 static void GoAndPreparse( playlist_t *p_playlist, int i_mode,
36                            playlist_item_t *, playlist_item_t * );
37 static void ChangeToNode( playlist_t *p_playlist, playlist_item_t *p_item );
38 static int DeleteInner( playlist_t * p_playlist, playlist_item_t *p_item,
39                         bool b_stop );
40
41 /*****************************************************************************
42  * An input item has gained a subitem (Event Callback)
43  *****************************************************************************/
44 static void input_item_subitem_added( const vlc_event_t * p_event,
45                                       void * user_data )
46 {
47     playlist_item_t *p_parent_playlist_item = user_data;
48     playlist_t * p_playlist = p_parent_playlist_item->p_playlist;
49     input_item_t * p_parent, * p_child;
50     playlist_item_t * p_child_in_category;
51     playlist_item_t * p_item_in_category;
52     bool b_play;
53
54     p_parent = p_event->p_obj;
55     p_child = p_event->u.input_item_subitem_added.p_new_child;
56
57     PL_LOCK;
58     b_play = var_CreateGetBool( p_playlist, "playlist-autostart" );
59
60     /* This part is really hakish, but this playlist system isn't simple */
61     /* First check if we haven't already added the item as we are
62      * listening using the onelevel and the category representent
63      * (Because of the playlist design) */
64     p_child_in_category = playlist_ItemFindFromInputAndRoot(
65                             p_playlist, p_child->i_id,
66                             p_playlist->p_root_category,
67                             false /* Only non-node */ );
68
69     if( !p_child_in_category )
70     {
71         /* Then, transform to a node if needed */
72         p_item_in_category = playlist_ItemFindFromInputAndRoot(
73                                 p_playlist, p_parent->i_id,
74                                 p_playlist->p_root_category,
75                                 false /* Only non-node */ );
76         if( !p_item_in_category )
77         {
78             /* Item may have been removed */
79             PL_UNLOCK;
80             return;
81         }
82
83         b_play = b_play && p_item_in_category == p_playlist->status.p_item;
84
85         /* If this item is already a node don't transform it */
86         if( p_item_in_category->i_children == -1 )
87         {
88             p_item_in_category = playlist_ItemToNode( p_playlist,
89                     p_item_in_category, true );
90             p_item_in_category->p_input->i_type = ITEM_TYPE_PLAYLIST;
91         }
92
93         int i_ret = playlist_BothAddInput( p_playlist, p_child,
94                 p_item_in_category,
95                 PLAYLIST_APPEND | PLAYLIST_SPREPARSE , PLAYLIST_END,
96                 NULL, NULL,  true );
97
98         if( i_ret == VLC_SUCCESS && b_play )
99         {
100             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
101                           true, p_item_in_category, NULL );
102         }
103     }
104
105     PL_UNLOCK;
106
107 }
108
109 /*****************************************************************************
110  * An input item's meta or duration has changed (Event Callback)
111  *****************************************************************************/
112 static void input_item_changed( const vlc_event_t * p_event,
113                                 void * user_data )
114 {
115     playlist_item_t * p_item = user_data;
116     var_SetInteger( p_item->p_playlist,
117                     "item-change", p_item->i_id );
118 }
119
120 /*****************************************************************************
121  * Listen to vlc_InputItemAddSubItem event
122  *****************************************************************************/
123 static void install_input_item_observer( playlist_item_t * p_item )
124 {
125     vlc_event_attach( &p_item->p_input->event_manager,
126                       vlc_InputItemSubItemAdded,
127                       input_item_subitem_added,
128                       p_item );
129     vlc_event_attach( &p_item->p_input->event_manager,
130                       vlc_InputItemDurationChanged,
131                       input_item_meta_changed,
132                       p_item );
133     vlc_event_attach( &p_item->p_input->event_manager,
134                       vlc_InputItemMetaChanged,
135                       input_item_meta_changed,
136                       p_item );
137 }
138
139 static void uninstall_input_item_observer( playlist_item_t * p_item )
140 {
141     vlc_event_detach( &p_item->p_input->event_manager,
142                       vlc_InputItemMetaChanged,
143                       input_item_meta_changed,
144                       p_item );
145     vlc_event_detach( &p_item->p_input->event_manager,
146                       vlc_InputItemDurationChanged,
147                       input_item_meta_changed,
148                       p_item );
149     vlc_event_detach( &p_item->p_input->event_manager,
150                       vlc_InputItemSubItemAdded,
151                       input_item_subitem_added,
152                       p_item );
153 }
154
155 /*****************************************************************************
156  * Playlist item creation
157  *****************************************************************************/
158 playlist_item_t * playlist_ItemNewWithType( vlc_object_t *p_obj,
159                                             const char *psz_uri,
160                                             const char *psz_name,
161                                             int i_options,
162                                             const char *const *ppsz_options,
163                                             int i_duration, int i_type )
164 {
165     input_item_t *p_input;
166     if( psz_uri == NULL ) return NULL;
167     p_input = input_ItemNewWithType( p_obj, psz_uri,
168                                      psz_name, i_options, ppsz_options,
169                                      i_duration, i_type );
170     return playlist_ItemNewFromInput( p_obj, p_input );
171 }
172
173 playlist_item_t *__playlist_ItemNewFromInput( vlc_object_t *p_obj,
174                                               input_item_t *p_input )
175 {
176     DECMALLOC_NULL( p_item, playlist_item_t );
177     playlist_t *p_playlist = pl_Yield( p_obj );
178
179     p_item->p_input = p_input;
180     vlc_gc_incref( p_item->p_input );
181
182     p_item->i_id = ++p_playlist->i_last_playlist_id;
183
184     p_item->p_parent = NULL;
185     p_item->i_children = -1;
186     p_item->pp_children = NULL;
187     p_item->i_flags = 0;
188     p_item->p_playlist = p_playlist;
189
190     install_input_item_observer( p_item );
191
192     pl_Release( p_item->p_playlist );
193
194     return p_item;
195 }
196
197 /***************************************************************************
198  * Playlist item destruction
199  ***************************************************************************/
200
201 /**
202  * Delete item
203  *
204  * Delete a playlist item and detach its input item
205  * \param p_item item to delete
206  * \return VLC_SUCCESS
207 */
208 int playlist_ItemDelete( playlist_item_t *p_item )
209 {
210     uninstall_input_item_observer( p_item );
211
212     vlc_gc_decref( p_item->p_input );
213     free( p_item );
214     return VLC_SUCCESS;
215 }
216
217 /**
218  * Delete input item
219  *
220  * Remove an input item when it appears from a root playlist item
221  * \param p_playlist playlist object
222  * \param i_input_id id of the input to delete
223  * \param p_root root playlist item
224  * \param b_do_stop must stop or not the playlist
225  * \return VLC_SUCCESS or VLC_EGENERIC
226 */
227 static int DeleteFromInput( playlist_t *p_playlist, int i_input_id,
228                             playlist_item_t *p_root, bool b_do_stop )
229 {
230     int i;
231     for( i = 0 ; i< p_root->i_children ; i++ )
232     {
233         if( p_root->pp_children[i]->i_children == -1 &&
234             p_root->pp_children[i]->p_input->i_id == i_input_id )
235         {
236             DeleteInner( p_playlist, p_root->pp_children[i], b_do_stop );
237             return VLC_SUCCESS;
238         }
239         else if( p_root->pp_children[i]->i_children >= 0 )
240         {
241             int i_ret = DeleteFromInput( p_playlist, i_input_id,
242                                          p_root->pp_children[i], b_do_stop );
243             if( i_ret == VLC_SUCCESS ) return VLC_SUCCESS;
244         }
245     }
246     return VLC_EGENERIC;
247 }
248
249 /**
250  * Delete input item
251  *
252  * Remove an input item when it appears from a root playlist item
253  * \param p_playlist playlist object
254  * \param i_input_id id of the input to delete
255  * \param p_root root playlist item
256  * \param b_locked TRUE if the playlist is locked
257  * \return VLC_SUCCESS or VLC_EGENERIC
258  */
259 int playlist_DeleteInputInParent( playlist_t *p_playlist, int i_input_id,
260                                   playlist_item_t *p_root, bool b_locked )
261 {
262     int i_ret;
263     if( !b_locked ) PL_LOCK;
264     i_ret = DeleteFromInput( p_playlist, i_input_id,
265                              p_root, true );
266     if( !b_locked ) PL_UNLOCK;
267     return i_ret;
268 }
269
270 /**
271  * Delete from input
272  *
273  * Remove an input item from ONELEVEL and CATEGORY
274  * \param p_playlist playlist object
275  * \param i_input_id id of the input to delete
276  * \param b_locked TRUE if the playlist is locked
277  * \return VLC_SUCCESS or VLC_ENOITEM
278  */
279 int playlist_DeleteFromInput( playlist_t *p_playlist, int i_input_id,
280                               bool b_locked )
281 {
282     int i_ret1, i_ret2;
283     if( !b_locked ) PL_LOCK;
284     i_ret1 = DeleteFromInput( p_playlist, i_input_id,
285                              p_playlist->p_root_category, true );
286     i_ret2 = DeleteFromInput( p_playlist, i_input_id,
287                      p_playlist->p_root_onelevel, true );
288     if( !b_locked ) PL_UNLOCK;
289     return ( i_ret1 == VLC_SUCCESS || i_ret2 == VLC_SUCCESS ) ?
290                             VLC_SUCCESS : VLC_ENOITEM;
291 }
292
293 /**
294  * Clear the playlist
295  *
296  * \param p_playlist playlist object
297  * \param b_locked TRUE if the playlist is locked
298  * \return nothing
299  */
300 void playlist_Clear( playlist_t * p_playlist, bool b_locked )
301 {
302     if( !b_locked ) PL_LOCK;
303     playlist_NodeEmpty( p_playlist, p_playlist->p_local_category, true );
304     playlist_NodeEmpty( p_playlist, p_playlist->p_local_onelevel, true );
305     if( !b_locked ) PL_UNLOCK;
306 }
307
308 /**
309  * Delete playlist item
310  *
311  * Remove a playlist item from the playlist, given its id
312  * This function is to be used only by the playlist
313  * \param p_playlist playlist object
314  * \param i_id id of the item do delete
315  * \return VLC_SUCCESS or an error
316  */
317 int playlist_DeleteFromItemId( playlist_t *p_playlist, int i_id )
318 {
319     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id,
320                                                     true );
321     if( !p_item ) return VLC_EGENERIC;
322     return DeleteInner( p_playlist, p_item, true );
323 }
324
325 /***************************************************************************
326  * Playlist item addition
327  ***************************************************************************/
328 /**
329  * Playlist add
330  *
331  * Add an item to the playlist or the media library
332  * \param p_playlist the playlist to add into
333  * \param psz_uri the mrl to add to the playlist
334  * \param psz_name a text giving a name or description of this item
335  * \param i_mode the mode used when adding
336  * \param i_pos the position in the playlist where to add. If this is
337  *        PLAYLIST_END the item will be added at the end of the playlist
338  *        regardless of its size
339  * \param b_playlist TRUE for playlist, FALSE for media library
340  * \param b_locked TRUE if the playlist is locked
341  * \return The id of the playlist item
342  */
343 int playlist_Add( playlist_t *p_playlist, const char *psz_uri,
344                   const char *psz_name, int i_mode, int i_pos,
345                   bool b_playlist, bool b_locked )
346 {
347     return playlist_AddExt( p_playlist, psz_uri, psz_name,
348                             i_mode, i_pos, -1, NULL, 0, b_playlist, b_locked );
349 }
350
351 /**
352  * Add a MRL into the playlist or the media library, duration and options given
353  *
354  * \param p_playlist the playlist to add into
355  * \param psz_uri the mrl to add to the playlist
356  * \param psz_name a text giving a name or description of this item
357  * \param i_mode the mode used when adding
358  * \param i_pos the position in the playlist where to add. If this is
359  *        PLAYLIST_END the item will be added at the end of the playlist
360  *        regardless of its size
361  * \param i_duration length of the item in milliseconds.
362  * \param ppsz_options an array of options
363  * \param i_options the number of options
364  * \param b_playlist TRUE for playlist, FALSE for media library
365  * \param b_locked TRUE if the playlist is locked
366  * \return The id of the playlist item
367 */
368 int playlist_AddExt( playlist_t *p_playlist, const char * psz_uri,
369                      const char *psz_name, int i_mode, int i_pos,
370                      mtime_t i_duration, const char *const *ppsz_options,
371                      int i_options, bool b_playlist, bool b_locked )
372 {
373     int i_ret;
374     input_item_t *p_input = input_ItemNewExt( p_playlist, psz_uri, psz_name,
375                                               i_options, ppsz_options,
376                                               i_duration );
377
378     i_ret = playlist_AddInput( p_playlist, p_input, i_mode, i_pos, b_playlist,
379                                b_locked );
380     int i_id = i_ret == VLC_SUCCESS ? p_input->i_id : -1;
381
382     vlc_gc_decref( p_input );
383
384     return i_id;
385 }
386
387 /**
388  * Add an input item to the playlist node
389  *
390  * \param p_playlist the playlist to add into
391  * \param p_input the input item to add
392  * \param i_mode the mode used when adding
393  * \param i_pos the position in the playlist where to add. If this is
394  *        PLAYLIST_END the item will be added at the end of the playlist
395  *        regardless of its size
396  * \param b_playlist TRUE for playlist, FALSE for media library
397  * \param b_locked TRUE if the playlist is locked
398  * \return VLC_SUCCESS or VLC_ENOMEM or VLC_EGENERIC
399 */
400 int playlist_AddInput( playlist_t* p_playlist, input_item_t *p_input,
401                        int i_mode, int i_pos, bool b_playlist,
402                        bool b_locked )
403 {
404     playlist_item_t *p_item_cat, *p_item_one;
405     if( p_playlist->b_die ) return VLC_EGENERIC;
406     if( !p_playlist->b_doing_ml )
407         PL_DEBUG( "adding item `%s' ( %s )", p_input->psz_name,
408                                              p_input->psz_uri );
409
410     if( !b_locked ) PL_LOCK;
411
412     /* Add to ONELEVEL */
413     p_item_one = playlist_ItemNewFromInput( p_playlist, p_input );
414     if( p_item_one == NULL ) return VLC_ENOMEM;
415     AddItem( p_playlist, p_item_one,
416              b_playlist ? p_playlist->p_local_onelevel :
417                           p_playlist->p_ml_onelevel , i_mode, i_pos );
418
419     /* Add to CATEGORY */
420     p_item_cat = playlist_ItemNewFromInput( p_playlist, p_input );
421     if( p_item_cat == NULL ) return VLC_ENOMEM;
422     AddItem( p_playlist, p_item_cat,
423              b_playlist ? p_playlist->p_local_category :
424                           p_playlist->p_ml_category , i_mode, i_pos );
425
426     GoAndPreparse( p_playlist, i_mode, p_item_cat, p_item_one );
427
428     if( !b_locked ) PL_UNLOCK;
429     return VLC_SUCCESS;
430 }
431
432 /**
433  * Add input
434  *
435  * Add an input item to p_direct_parent in the category tree, and to the
436  * matching top category in onelevel
437  * \param p_playlist the playlist to add into
438  * \param p_input the input item to add
439  * \param p_direct_parent the parent item to add into
440  * \param i_mode the mode used when adding
441  * \param i_pos the position in the playlist where to add. If this is
442  *        PLAYLIST_END the item will be added at the end of the playlist
443  *        regardless of its size
444  * \param i_cat id of the items category
445  * \param i_one id of the item onelevel category
446  * \param b_locked TRUE if the playlist is locked
447  * \return VLC_SUCCESS if success, VLC_EGENERIC if fail, VLC_ENOMEM if OOM
448  */
449 int playlist_BothAddInput( playlist_t *p_playlist,
450                            input_item_t *p_input,
451                            playlist_item_t *p_direct_parent,
452                            int i_mode, int i_pos,
453                            int *i_cat, int *i_one, bool b_locked )
454 {
455     playlist_item_t *p_item_cat, *p_item_one, *p_up;
456     int i_top;
457     assert( p_input );
458     if( p_playlist->b_die ) return VLC_EGENERIC;
459     if( !b_locked ) PL_LOCK;
460
461     /* Add to category */
462     p_item_cat = playlist_ItemNewFromInput( p_playlist, p_input );
463     if( p_item_cat == NULL ) return VLC_ENOMEM;
464     AddItem( p_playlist, p_item_cat, p_direct_parent, i_mode, i_pos );
465
466     /* Add to onelevel */
467     /** \todo make a faster case for ml import */
468     p_item_one = playlist_ItemNewFromInput( p_playlist, p_input );
469     if( p_item_one == NULL ) return VLC_ENOMEM;
470
471     p_up = p_direct_parent;
472     while( p_up->p_parent != p_playlist->p_root_category )
473     {
474         p_up = p_up->p_parent;
475     }
476     for( i_top = 0 ; i_top < p_playlist->p_root_onelevel->i_children; i_top++ )
477     {
478         if( p_playlist->p_root_onelevel->pp_children[i_top]->p_input->i_id ==
479                              p_up->p_input->i_id )
480         {
481             AddItem( p_playlist, p_item_one,
482                      p_playlist->p_root_onelevel->pp_children[i_top],
483                      i_mode, i_pos );
484             break;
485         }
486     }
487     GoAndPreparse( p_playlist, i_mode, p_item_cat, p_item_one );
488
489     if( i_cat ) *i_cat = p_item_cat->i_id;
490     if( i_one ) *i_one = p_item_one->i_id;
491
492     if( !b_locked ) PL_UNLOCK;
493     return VLC_SUCCESS;
494 }
495
496 /**
497  * Add an input item to a given node
498  *
499  * \param p_playlist the playlist to add into
500  * \param p_input the input item to add
501  * \param p_parent the parent item to add into
502  * \param i_mode the mode used when addin
503  * \param i_pos the position in the playlist where to add. If this is
504  *        PLAYLIST_END the item will be added at the end of the playlist
505  *        regardless of its size
506  * \param b_locked TRUE if the playlist is locked
507  * \return the new playlist item
508  */
509 playlist_item_t * playlist_NodeAddInput( playlist_t *p_playlist,
510                                          input_item_t *p_input,
511                                          playlist_item_t *p_parent,
512                                          int i_mode, int i_pos,
513                                          bool b_locked )
514 {
515     playlist_item_t *p_item;
516     assert( p_input );
517     assert( p_parent && p_parent->i_children != -1 );
518
519     if( p_playlist->b_die )
520         return NULL;
521     if( !b_locked ) PL_LOCK;
522
523     p_item = playlist_ItemNewFromInput( p_playlist, p_input );
524     if( p_item == NULL ) return NULL;
525     AddItem( p_playlist, p_item, p_parent, i_mode, i_pos );
526
527     if( !b_locked ) PL_UNLOCK;
528
529     return p_item;
530 }
531
532 /*****************************************************************************
533  * Playlist item misc operations
534  *****************************************************************************/
535
536 /**
537  * Item to node
538  *
539  * Transform an item to a node. Return the node in the category tree, or NULL
540  * if not found there
541  * This function must be entered without the playlist lock
542  * \param p_playlist the playlist object
543  * \param p_item the item to transform
544  * \param b_locked TRUE if the playlist is locked
545  * \return the item transform in a node
546  */
547 playlist_item_t *playlist_ItemToNode( playlist_t *p_playlist,
548                                       playlist_item_t *p_item,
549                                       bool b_locked )
550 {
551
552     playlist_item_t *p_item_in_category;
553     /* What we do
554      * Find the input in CATEGORY.
555      *  - If we find it
556      *    - change it to node
557      *    - we'll return it at the end
558      *    - If we are a direct child of onelevel root, change to node, else
559      *      delete the input from ONELEVEL
560      *  - If we don't find it, just change to node (we are probably in VLM)
561      *    and return NULL
562      *
563      * If we were in ONELEVEL, we thus retrieve the node in CATEGORY (will be
564      * useful for later BothAddInput )
565      */
566
567     if( !b_locked ) PL_LOCK;
568
569     /* Fast track the media library, no time to loose */
570     if( p_item == p_playlist->p_ml_category ) {
571         if( !b_locked ) PL_UNLOCK;
572         return p_item;
573     }
574
575     /** \todo First look if we don't already have it */
576     p_item_in_category = playlist_ItemFindFromInputAndRoot(
577                                             p_playlist, p_item->p_input->i_id,
578                                             p_playlist->p_root_category,
579                                             true );
580
581     if( p_item_in_category )
582     {
583         playlist_item_t *p_item_in_one = playlist_ItemFindFromInputAndRoot(
584                                             p_playlist, p_item->p_input->i_id,
585                                             p_playlist->p_root_onelevel,
586                                             true );
587         assert( p_item_in_one );
588
589         /* We already have it, and there is nothing more to do */
590         ChangeToNode( p_playlist, p_item_in_category );
591
592         /* Item in one is a root, change it to node */
593         if( p_item_in_one->p_parent == p_playlist->p_root_onelevel )
594             ChangeToNode( p_playlist, p_item_in_one );
595         else
596         {
597             DeleteFromInput( p_playlist, p_item_in_one->p_input->i_id,
598                              p_playlist->p_root_onelevel, false );
599         }
600         p_playlist->b_reset_currently_playing = true;
601         vlc_cond_signal( &p_playlist->object_wait );
602         var_SetInteger( p_playlist, "item-change", p_item_in_category->
603                                                         p_input->i_id );
604         if( !b_locked ) PL_UNLOCK;
605         return p_item_in_category;
606     }
607     else
608     {
609         ChangeToNode( p_playlist, p_item );
610         if( !b_locked ) PL_UNLOCK;
611         return NULL;
612     }
613 }
614
615 /**
616  * Find an item within a root, given its input id.
617  *
618  * \param p_playlist the playlist object
619  * \param i_input_id id of the input
620  * \param p_root root playlist item
621  * \param b_items_only TRUE if we want the item himself
622  * \return the first found item, or NULL if not found
623  */
624 playlist_item_t *playlist_ItemFindFromInputAndRoot( playlist_t *p_playlist,
625                                                     int i_input_id,
626                                                     playlist_item_t *p_root,
627                                                     bool b_items_only )
628 {
629     int i;
630     for( i = 0 ; i< p_root->i_children ; i++ )
631     {
632         if( ( b_items_only ? p_root->pp_children[i]->i_children == -1 : 1 ) &&
633             p_root->pp_children[i]->p_input->i_id == i_input_id )
634         {
635             return p_root->pp_children[i];
636         }
637         else if( p_root->pp_children[i]->i_children >= 0 )
638         {
639             playlist_item_t *p_search =
640                  playlist_ItemFindFromInputAndRoot( p_playlist, i_input_id,
641                                                     p_root->pp_children[i],
642                                                     b_items_only );
643             if( p_search ) return p_search;
644         }
645     }
646     return NULL;
647 }
648
649
650 static int TreeMove( playlist_t *p_playlist, playlist_item_t *p_item,
651                      playlist_item_t *p_node, int i_newpos )
652 {
653     int j;
654     playlist_item_t *p_detach = p_item->p_parent;
655     (void)p_playlist;
656
657     if( p_node->i_children == -1 ) return VLC_EGENERIC;
658
659     for( j = 0; j < p_detach->i_children; j++ )
660     {
661          if( p_detach->pp_children[j] == p_item ) break;
662     }
663     REMOVE_ELEM( p_detach->pp_children, p_detach->i_children, j );
664
665     /* Attach to new parent */
666     INSERT_ELEM( p_node->pp_children, p_node->i_children, i_newpos, p_item );
667     p_item->p_parent = p_node;
668
669     return VLC_SUCCESS;
670 }
671
672 /**
673  * Moves an item
674  *
675  * This function must be entered with the playlist lock
676  *
677  * \param p_playlist the playlist
678  * \param p_item the item to move
679  * \param p_node the new parent of the item
680  * \param i_newpos the new position under this new parent
681  * \return VLC_SUCCESS or an error
682  */
683 int playlist_TreeMove( playlist_t * p_playlist, playlist_item_t *p_item,
684                        playlist_item_t *p_node, int i_newpos )
685 {
686     int i_ret;
687     /* Drop on a top level node. Move in the two trees */
688     if( p_node->p_parent == p_playlist->p_root_category ||
689         p_node->p_parent == p_playlist->p_root_onelevel )
690     {
691         /* Fixme: avoid useless lookups but we need some clean helpers */
692         {
693             /* Fixme: if we try to move a node on a top-level node, it will
694              * fail because the node doesn't exist in onelevel and we will
695              * do some shit in onelevel. We should recursively move all items
696              * within the node */
697             playlist_item_t *p_node_onelevel;
698             playlist_item_t *p_item_onelevel;
699             p_node_onelevel = playlist_ItemFindFromInputAndRoot( p_playlist,
700                                                 p_node->p_input->i_id,
701                                                 p_playlist->p_root_onelevel,
702                                                 false );
703             p_item_onelevel = playlist_ItemFindFromInputAndRoot( p_playlist,
704                                                 p_item->p_input->i_id,
705                                                 p_playlist->p_root_onelevel,
706                                                 false );
707             if( p_node_onelevel && p_item_onelevel )
708                 TreeMove( p_playlist, p_item_onelevel, p_node_onelevel, 0 );
709         }
710         {
711             playlist_item_t *p_node_category;
712             playlist_item_t *p_item_category;
713             p_node_category = playlist_ItemFindFromInputAndRoot( p_playlist,
714                                                 p_node->p_input->i_id,
715                                                 p_playlist->p_root_category,
716                                                 false );
717             p_item_category = playlist_ItemFindFromInputAndRoot( p_playlist,
718                                                 p_item->p_input->i_id,
719                                                 p_playlist->p_root_category,
720                                                 false );
721             if( p_node_category && p_item_category )
722                 TreeMove( p_playlist, p_item_category, p_node_category, 0 );
723         }
724         i_ret = VLC_SUCCESS;
725     }
726     else
727         i_ret = TreeMove( p_playlist, p_item, p_node, i_newpos );
728     p_playlist->b_reset_currently_playing = true;
729     vlc_cond_signal( &p_playlist->object_wait );
730     return i_ret;
731 }
732
733 /**
734  * Send a notification that an item has been added to a node
735  *
736  * \param p_playlist the playlist object
737  * \param i_item_id id of the item added
738  * \param i_node_id id of the node in wich the item was added
739  * \param b_signal TRUE if the function must send a signal
740  * \return nothing
741  */
742 void playlist_SendAddNotify( playlist_t *p_playlist, int i_item_id,
743                              int i_node_id, bool b_signal )
744 {
745     vlc_value_t val;
746     playlist_add_t *p_add = (playlist_add_t *)malloc( sizeof( playlist_add_t) );
747     if( !p_add )
748         return;
749
750     p_add->i_item = i_item_id;
751     p_add->i_node = i_node_id;
752     val.p_address = p_add;
753     p_playlist->b_reset_currently_playing = true;
754     if( b_signal )
755         vlc_cond_signal( &p_playlist->object_wait );
756     var_Set( p_playlist, "item-append", val );
757     free( p_add );
758 }
759
760 /*****************************************************************************
761  * Playlist item accessors
762  *****************************************************************************/
763
764 /**
765  * Set the name of a playlist item
766  *
767  * \param p_item the item
768  * \param psz_name the name
769  * \return VLC_SUCCESS or VLC_EGENERIC
770  */
771 int playlist_ItemSetName( playlist_item_t *p_item, const char *psz_name )
772 {
773     if( psz_name && p_item )
774     {
775         input_item_SetName( p_item->p_input, psz_name );
776         return VLC_SUCCESS;
777     }
778     return VLC_EGENERIC;
779 }
780
781 /***************************************************************************
782  * The following functions are local
783  ***************************************************************************/
784
785 /* Enqueue an item for preparsing, and play it, if needed */
786 static void GoAndPreparse( playlist_t *p_playlist, int i_mode,
787                            playlist_item_t *p_item_cat,
788                            playlist_item_t *p_item_one )
789 {
790     if( (i_mode & PLAYLIST_GO ) )
791     {
792         playlist_item_t *p_parent = p_item_one;
793         playlist_item_t *p_toplay = NULL;
794         while( p_parent )
795         {
796             if( p_parent == p_playlist->p_root_category )
797             {
798                 p_toplay = p_item_cat; break;
799             }
800             else if( p_parent == p_playlist->p_root_onelevel )
801             {
802                 p_toplay = p_item_one; break;
803             }
804             p_parent = p_parent->p_parent;
805         }
806         assert( p_toplay );
807         p_playlist->request.b_request = true;
808         p_playlist->request.i_skip = 0;
809         p_playlist->request.p_item = p_toplay;
810         if( p_playlist->p_input )
811             input_StopThread( p_playlist->p_input );
812         p_playlist->request.i_status = PLAYLIST_RUNNING;
813         vlc_cond_signal( &p_playlist->object_wait );
814     }
815     /* Preparse if PREPARSE or SPREPARSE & not enough meta */
816     char *psz_artist = input_item_GetArtist( p_item_cat->p_input );
817     char *psz_album = input_item_GetAlbum( p_item_cat->p_input );
818     if( p_playlist->b_auto_preparse &&
819           (i_mode & PLAYLIST_PREPARSE ||
820           ( i_mode & PLAYLIST_SPREPARSE &&
821             ( EMPTY_STR( psz_artist ) || ( EMPTY_STR( psz_album ) ) )
822           ) ) )
823         playlist_PreparseEnqueue( p_playlist, p_item_cat->p_input );
824     /* If we already have it, signal it */
825     else if( !EMPTY_STR( psz_artist ) && !EMPTY_STR( psz_album ) )
826         input_item_SetPreparsed( p_item_cat->p_input, true );
827     free( psz_artist );
828     free( psz_album );
829 }
830
831 /* Add the playlist item to the requested node and fire a notification */
832 static void AddItem( playlist_t *p_playlist, playlist_item_t *p_item,
833                      playlist_item_t *p_node, int i_mode, int i_pos )
834 {
835     ARRAY_APPEND(p_playlist->items, p_item);
836     ARRAY_APPEND(p_playlist->all_items, p_item);
837
838     if( i_pos == PLAYLIST_END )
839         playlist_NodeAppend( p_playlist, p_item, p_node );
840     else
841         playlist_NodeInsert( p_playlist, p_item, p_node, i_pos );
842
843     if( !p_playlist->b_doing_ml )
844         playlist_SendAddNotify( p_playlist, p_item->i_id, p_node->i_id,
845                                  !( i_mode & PLAYLIST_NO_REBUILD ) );
846 }
847
848 /* Actually convert an item to a node */
849 static void ChangeToNode( playlist_t *p_playlist, playlist_item_t *p_item )
850 {
851     int i;
852     if( p_item->i_children == -1 )
853         p_item->i_children = 0;
854
855     /* Remove it from the array of available items */
856     ARRAY_BSEARCH( p_playlist->items,->i_id, int, p_item->i_id, i );
857     if( i != -1 )
858         ARRAY_REMOVE( p_playlist->items, i );
859 }
860
861 /* Do the actual removal */
862 static int DeleteInner( playlist_t * p_playlist, playlist_item_t *p_item,
863                         bool b_stop )
864 {
865     int i;
866     int i_id = p_item->i_id;
867     bool b_delay_deletion = false;
868
869     if( p_item->i_children > -1 )
870     {
871         return playlist_NodeDelete( p_playlist, p_item, true, false );
872     }
873     p_playlist->b_reset_currently_playing = true;
874     var_SetInteger( p_playlist, "item-deleted", i_id );
875
876     /* Remove the item from the bank */
877     ARRAY_BSEARCH( p_playlist->all_items,->i_id, int, i_id, i );
878     if( i != -1 )
879         ARRAY_REMOVE( p_playlist->all_items, i );
880
881     ARRAY_BSEARCH( p_playlist->items,->i_id, int, i_id, i );
882     if( i != -1 )
883         ARRAY_REMOVE( p_playlist->items, i );
884
885     /* Check if it is the current item */
886     if( p_playlist->status.p_item == p_item )
887     {
888         /* Hack we don't call playlist_Control for lock reasons */
889         if( b_stop )
890         {
891             p_playlist->request.i_status = PLAYLIST_STOPPED;
892             p_playlist->request.b_request = true;
893             p_playlist->request.p_item = NULL;
894             msg_Info( p_playlist, "stopping playback" );
895             vlc_cond_signal( &p_playlist->object_wait );
896         }
897         b_delay_deletion = true;
898     }
899
900     PL_DEBUG( "deleting item `%s'", p_item->p_input->psz_name );
901
902     /* Remove the item from its parent */
903     playlist_NodeRemoveItem( p_playlist, p_item, p_item->p_parent );
904
905     if( !b_delay_deletion )
906         playlist_ItemDelete( p_item );
907     else
908     {
909         PL_DEBUG( "marking %s for further deletion", PLI_NAME( p_item ) );
910         p_item->i_flags |= PLAYLIST_REMOVE_FLAG;
911     }
912
913     return VLC_SUCCESS;
914 }