]> git.sesse.net Git - vlc/blob - src/playlist/item.c
playlist: refactor and fix #3932
[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 <vlc_rand.h>
32 #include "playlist_internal.h"
33
34 static void AddItem( playlist_t *p_playlist, playlist_item_t *p_item,
35                      playlist_item_t *p_node, int i_mode, int i_pos );
36 static void GoAndPreparse( playlist_t *p_playlist, int i_mode,
37                            playlist_item_t * );
38 static void ChangeToNode( playlist_t *p_playlist, playlist_item_t *p_item );
39
40 static int RecursiveAddIntoParent (
41                 playlist_t *p_playlist, playlist_item_t *p_parent,
42                 input_item_node_t *p_node, int i_pos, bool b_flat,
43                 playlist_item_t **pp_first_leaf );
44 static int RecursiveInsertCopy (
45                 playlist_t *p_playlist, playlist_item_t *p_item,
46                 playlist_item_t *p_parent, int i_pos, bool b_flat );
47
48 /*****************************************************************************
49  * An input item has gained subitems (Event Callback)
50  *****************************************************************************/
51
52 static void input_item_add_subitem_tree ( const vlc_event_t * p_event,
53                                           void * user_data )
54 {
55     input_item_t *p_input = p_event->p_obj;
56     playlist_t *p_playlist = (( playlist_item_t* ) user_data)->p_playlist;
57     input_item_node_t *p_new_root = p_event->u.input_item_subitem_tree_added.p_root;
58
59     PL_LOCK;
60
61     playlist_item_t *p_item =
62         playlist_ItemGetByInput( p_playlist, p_input );
63
64     assert( p_item != NULL );
65
66     bool b_current = get_current_status_item( p_playlist ) == p_item;
67     bool b_autostart = var_CreateGetBool( p_playlist, "playlist-autostart" );
68     bool b_stop = p_item->i_flags & PLAYLIST_SUBITEM_STOP_FLAG;
69     bool b_flat = false;
70
71     p_item->i_flags &= ~PLAYLIST_SUBITEM_STOP_FLAG;
72
73     /* We will have to flatten the tree out if we are in "the playlist" node and
74     the user setting demands flat playlist */
75
76     if( !pl_priv(p_playlist)->b_tree ) {
77         playlist_item_t *p_up = p_item;
78         while( p_up->p_parent )
79         {
80             if( p_up->p_parent == p_playlist->p_playing )
81             {
82                 b_flat = true;
83                 break;
84             }
85             p_up = p_up->p_parent;
86         }
87     }
88
89     int pos = 0;
90
91     /* If we have to flatten out, then take the item's position in the parent as
92     insertion point and delete the item */
93
94     if( b_flat )
95     {
96         playlist_item_t *p_parent = p_item->p_parent;
97         assert( p_parent != NULL );
98
99         int i;
100         for( i = 0; i < p_parent->i_children; i++ )
101         {
102             if( p_parent->pp_children[i] == p_item )
103             {
104                 pos = i;
105                 break;
106             }
107         }
108         assert( i < p_parent->i_children );
109
110         playlist_DeleteItem( p_playlist, p_item, true );
111
112         p_item = p_parent;
113     }
114     else
115     {
116         pos = p_item->i_children >= 0 ? p_item->i_children : 0;
117     }
118
119     /* At this point:
120     "p_item" is the node where sub-items should be inserted,
121     "pos" is the insertion position in that node */
122
123     int last_pos = playlist_InsertInputItemTree( p_playlist,
124                                                  p_item,
125                                                  p_new_root,
126                                                  pos,
127                                                  b_flat );
128
129     if( !b_flat ) var_SetAddress( p_playlist, "leaf-to-parent", p_input );
130
131     //control playback only if it was the current playing item that got subitems
132     if( b_current )
133     {
134         if( last_pos == pos || ( b_stop && !b_flat ) || !b_autostart )
135         {
136             /* We stop, either because no sub-item was actually created, or some
137             flags/settings want us to do so at this point */
138             PL_UNLOCK;
139             playlist_Stop( p_playlist );
140             return;
141         }
142         else
143         {
144             /* Continue to play, either random or the first new item */
145             playlist_item_t *p_play_item;
146
147             if( var_GetBool( p_playlist, "random" ) )
148             {
149                 unsigned rand_pos =
150                     ((unsigned)vlc_mrand48()) % (last_pos - pos);
151                 rand_pos += pos;
152                 p_play_item = p_item->pp_children[rand_pos];
153             }
154             else
155             {
156                 p_play_item = playlist_GetNextLeaf( p_playlist,
157                                                     p_item,
158                                                     NULL,
159                                                     true,
160                                                     false );
161                 char *psz_name = input_item_GetName(p_play_item->p_input);
162                 free(psz_name);
163             }
164
165             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
166                                   pl_Locked,
167                                   get_current_status_node( p_playlist ),
168                                   p_play_item );
169         }
170     }
171
172     PL_UNLOCK;
173 }
174 /*****************************************************************************
175  * An input item's meta or duration has changed (Event Callback)
176  *****************************************************************************/
177 static void input_item_changed( const vlc_event_t * p_event,
178                                 void * user_data )
179 {
180     playlist_item_t *p_item = user_data;
181     VLC_UNUSED( p_event );
182     var_SetAddress( p_item->p_playlist, "item-change", p_item->p_input );
183 }
184
185 /*****************************************************************************
186  * Listen to vlc_InputItemAddSubItem event
187  *****************************************************************************/
188 static void install_input_item_observer( playlist_item_t * p_item )
189 {
190     vlc_event_manager_t * p_em = &p_item->p_input->event_manager;
191     vlc_event_attach( p_em, vlc_InputItemSubItemTreeAdded,
192                       input_item_add_subitem_tree, p_item );
193     vlc_event_attach( p_em, vlc_InputItemDurationChanged,
194                       input_item_changed, p_item );
195     vlc_event_attach( p_em, vlc_InputItemMetaChanged,
196                       input_item_changed, p_item );
197     vlc_event_attach( p_em, vlc_InputItemNameChanged,
198                       input_item_changed, p_item );
199     vlc_event_attach( p_em, vlc_InputItemInfoChanged,
200                       input_item_changed, p_item );
201     vlc_event_attach( p_em, vlc_InputItemErrorWhenReadingChanged,
202                       input_item_changed, p_item );
203 }
204
205 static void uninstall_input_item_observer( playlist_item_t * p_item )
206 {
207     vlc_event_manager_t * p_em = &p_item->p_input->event_manager;
208     vlc_event_detach( p_em, vlc_InputItemSubItemTreeAdded,
209                       input_item_add_subitem_tree, p_item );
210     vlc_event_detach( p_em, vlc_InputItemMetaChanged,
211                       input_item_changed, p_item );
212     vlc_event_detach( p_em, vlc_InputItemDurationChanged,
213                       input_item_changed, p_item );
214     vlc_event_detach( p_em, vlc_InputItemNameChanged,
215                       input_item_changed, p_item );
216     vlc_event_detach( p_em, vlc_InputItemInfoChanged,
217                       input_item_changed, p_item );
218     vlc_event_detach( p_em, vlc_InputItemErrorWhenReadingChanged,
219                       input_item_changed, p_item );
220 }
221
222 /*****************************************************************************
223  * Playlist item creation
224  *****************************************************************************/
225 playlist_item_t *playlist_ItemNewFromInput( playlist_t *p_playlist,
226                                               input_item_t *p_input )
227 {
228     playlist_item_t* p_item = malloc( sizeof( playlist_item_t ) );
229     if( !p_item )
230         return NULL;
231
232     assert( p_input );
233
234     p_item->p_input = p_input;
235     vlc_gc_incref( p_item->p_input );
236
237     p_item->i_id = ++pl_priv(p_playlist)->i_last_playlist_id;
238
239     p_item->p_parent = NULL;
240     p_item->i_children = -1;
241     p_item->pp_children = NULL;
242     p_item->i_flags = 0;
243     p_item->p_playlist = p_playlist;
244
245     install_input_item_observer( p_item );
246
247     return p_item;
248 }
249
250 /***************************************************************************
251  * Playlist item destruction
252  ***************************************************************************/
253
254 /**
255  * Release an item
256  *
257  * \param p_item item to delete
258  * \return VLC_SUCCESS
259 */
260 int playlist_ItemRelease( playlist_item_t *p_item )
261 {
262     /* For the assert */
263     playlist_t *p_playlist = p_item->p_playlist;
264     PL_ASSERT_LOCKED;
265
266     /* Surprise, we can't actually do more because we
267      * don't do refcounting, or eauivalent.
268      * Because item are not only accessed by their id
269      * using playlist_item outside the PL_LOCK isn't safe.
270      * Most of the modules does that.
271      *
272      * Who wants to add proper memory management? */
273     uninstall_input_item_observer( p_item );
274     ARRAY_APPEND( pl_priv(p_playlist)->items_to_delete, p_item);
275     return VLC_SUCCESS;
276 }
277
278 /**
279  * Delete input item
280  *
281  * Remove an input item when it appears from a root playlist item
282  * \param p_playlist playlist object
283  * \param p_input the input to delete
284  * \param p_root root playlist item
285  * \param b_do_stop must stop or not the playlist
286  * \return VLC_SUCCESS or VLC_EGENERIC
287 */
288 static int DeleteFromInput( playlist_t *p_playlist, input_item_t *p_input,
289                             playlist_item_t *p_root, bool b_do_stop )
290 {
291     PL_ASSERT_LOCKED;
292     playlist_item_t *p_item = playlist_ItemFindFromInputAndRoot(
293         p_playlist, p_input, p_root, false );
294     if( !p_item ) return VLC_EGENERIC;
295     return playlist_DeleteItem( p_playlist, p_item, b_do_stop );
296 }
297
298 /**
299  * Delete input item
300  *
301  * Remove an input item when it appears from a root playlist item
302  * \param p_playlist playlist object
303  * \param p_input the input to delete
304  * \param p_root root playlist item
305  * \param b_locked TRUE if the playlist is locked
306  * \return VLC_SUCCESS or VLC_EGENERIC
307  */
308 int playlist_DeleteFromInputInParent( playlist_t *p_playlist,
309                                       input_item_t *p_item,
310                                       playlist_item_t *p_root, bool b_locked )
311 {
312     int i_ret;
313     PL_LOCK_IF( !b_locked );
314     i_ret = DeleteFromInput( p_playlist, p_item, p_root, true );
315     PL_UNLOCK_IF( !b_locked );
316     return i_ret;
317 }
318
319 /**
320  * Delete from input
321  *
322  * Search anywhere in playlist for an an input item and delete it
323  * \param p_playlist playlist object
324  * \param p_input the input to delete
325  * \param b_locked TRUE if the playlist is locked
326  * \return VLC_SUCCESS or VLC_ENOITEM
327  */
328 int playlist_DeleteFromInput( playlist_t *p_playlist, input_item_t *p_input,
329                               bool b_locked )
330 {
331     int i_ret;
332     PL_LOCK_IF( !b_locked );
333     i_ret = DeleteFromInput( p_playlist, p_input,
334                              p_playlist->p_root, true );
335     PL_UNLOCK_IF( !b_locked );
336     return ( i_ret == VLC_SUCCESS ? VLC_SUCCESS : VLC_ENOITEM );
337 }
338
339 /**
340  * Clear the playlist
341  *
342  * \param p_playlist playlist object
343  * \param b_locked TRUE if the playlist is locked
344  * \return nothing
345  */
346 void playlist_Clear( playlist_t * p_playlist, bool b_locked )
347 {
348     PL_LOCK_IF( !b_locked );
349     playlist_NodeEmpty( p_playlist, p_playlist->p_playing, true );
350     PL_UNLOCK_IF( !b_locked );
351 }
352
353 /**
354  * Delete playlist item
355  *
356  * Remove a playlist item from the playlist, given its id
357  * This function is to be used only by the playlist
358  * \param p_playlist playlist object
359  * \param i_id id of the item do delete
360  * \return VLC_SUCCESS or an error
361  */
362 int playlist_DeleteFromItemId( playlist_t *p_playlist, int i_id )
363 {
364     PL_ASSERT_LOCKED;
365     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id );
366     if( !p_item ) return VLC_EGENERIC;
367     return playlist_DeleteItem( p_playlist, p_item, true );
368 }
369
370 /***************************************************************************
371  * Playlist item addition
372  ***************************************************************************/
373 /**
374  * Playlist add
375  *
376  * Add an item to the playlist or the media library
377  * \param p_playlist the playlist to add into
378  * \param psz_uri the mrl to add to the playlist
379  * \param psz_name a text giving a name or description of this item
380  * \param i_mode the mode used when adding
381  * \param i_pos the position in the playlist where to add. If this is
382  *        PLAYLIST_END the item will be added at the end of the playlist
383  *        regardless of its size
384  * \param b_playlist TRUE for playlist, FALSE for media library
385  * \param b_locked TRUE if the playlist is locked
386  * \return VLC_SUCCESS or a VLC error code
387  */
388 int playlist_Add( playlist_t *p_playlist, const char *psz_uri,
389                   const char *psz_name, int i_mode, int i_pos,
390                   bool b_playlist, bool b_locked )
391 {
392     return playlist_AddExt( p_playlist, psz_uri, psz_name,
393                             i_mode, i_pos, -1, 0, NULL, 0, b_playlist, b_locked );
394 }
395
396 /**
397  * Add a MRL into the playlist or the media library, duration and options given
398  *
399  * \param p_playlist the playlist to add into
400  * \param psz_uri the mrl to add to the playlist
401  * \param psz_name a text giving a name or description of this item
402  * \param i_mode the mode used when adding
403  * \param i_pos the position in the playlist where to add. If this is
404  *        PLAYLIST_END the item will be added at the end of the playlist
405  *        regardless of its size
406  * \param i_duration length of the item in milliseconds.
407  * \param i_options the number of options
408  * \param ppsz_options an array of options
409  * \param i_option_flags options flags
410  * \param b_playlist TRUE for playlist, FALSE for media library
411  * \param b_locked TRUE if the playlist is locked
412  * \return VLC_SUCCESS or a VLC error code
413 */
414 int playlist_AddExt( playlist_t *p_playlist, const char * psz_uri,
415                      const char *psz_name, int i_mode, int i_pos,
416                      mtime_t i_duration,
417                      int i_options, const char *const *ppsz_options,
418                      unsigned i_option_flags,
419                      bool b_playlist, bool b_locked )
420 {
421     int i_ret;
422     input_item_t *p_input;
423
424     p_input = input_item_NewExt( p_playlist, psz_uri, psz_name,
425                                  i_options, ppsz_options, i_option_flags,
426                                  i_duration );
427     if( p_input == NULL )
428         return VLC_ENOMEM;
429     i_ret = playlist_AddInput( p_playlist, p_input, i_mode, i_pos, b_playlist,
430                                b_locked );
431     vlc_gc_decref( p_input );
432     return i_ret;
433 }
434
435 /**
436  * Add an input item to the playlist node
437  *
438  * \param p_playlist the playlist to add into
439  * \param p_input the input item to add
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 b_playlist TRUE for playlist, FALSE for media library
445  * \param b_locked TRUE if the playlist is locked
446  * \return VLC_SUCCESS or VLC_ENOMEM or VLC_EGENERIC
447 */
448 int playlist_AddInput( playlist_t* p_playlist, input_item_t *p_input,
449                        int i_mode, int i_pos, bool b_playlist,
450                        bool b_locked )
451 {
452     playlist_item_t *p_item;
453     if( p_playlist->b_die ) return VLC_EGENERIC;
454     if( !pl_priv(p_playlist)->b_doing_ml )
455         PL_DEBUG( "adding item `%s' ( %s )", p_input->psz_name,
456                                              p_input->psz_uri );
457
458     PL_LOCK_IF( !b_locked );
459
460     p_item = playlist_ItemNewFromInput( p_playlist, p_input );
461     if( p_item == NULL ) return VLC_ENOMEM;
462     AddItem( p_playlist, p_item,
463              b_playlist ? p_playlist->p_playing :
464                           p_playlist->p_media_library , i_mode, i_pos );
465
466     GoAndPreparse( p_playlist, i_mode, p_item );
467
468     PL_UNLOCK_IF( !b_locked );
469     return VLC_SUCCESS;
470 }
471
472 /**
473  * Add an input item to a given node
474  *
475  * \param p_playlist the playlist to add into
476  * \param p_input the input item to add
477  * \param p_parent the parent item to add into
478  * \param i_mode the mode used when addin
479  * \param i_pos the position in the playlist where to add. If this is
480  *        PLAYLIST_END the item will be added at the end of the playlist
481  *        regardless of its size
482  * \param b_locked TRUE if the playlist is locked
483  * \return the new playlist item
484  */
485 playlist_item_t * playlist_NodeAddInput( playlist_t *p_playlist,
486                                          input_item_t *p_input,
487                                          playlist_item_t *p_parent,
488                                          int i_mode, int i_pos,
489                                          bool b_locked )
490 {
491     playlist_item_t *p_item;
492     assert( p_input );
493     assert( p_parent && p_parent->i_children != -1 );
494
495     if( p_playlist->b_die )
496         return NULL;
497     PL_LOCK_IF( !b_locked );
498
499     p_item = playlist_ItemNewFromInput( p_playlist, p_input );
500     if( p_item == NULL ) return NULL;
501     AddItem( p_playlist, p_item, p_parent, i_mode, i_pos );
502
503     GoAndPreparse( p_playlist, i_mode, p_item );
504
505     PL_UNLOCK_IF( !b_locked );
506
507     return p_item;
508 }
509
510 /**
511  * Copy an item (and all its children, if any) into another node
512  *
513  * \param p_playlist the playlist to operate on
514  * \param p_item the playlist item to copy
515  * \param p_parent the parent item to copy into
516  * \param i_pos the position in the parent item for the new copy;
517  *              if this is PLAYLIST_END, the copy is appended after all
518  *              parent's children
519  * \return the position in parent item just behind the last new item inserted
520  */
521 int playlist_NodeAddCopy (
522     playlist_t *p_playlist, playlist_item_t *p_item,
523     playlist_item_t *p_parent, int i_pos )
524 {
525     PL_ASSERT_LOCKED;
526     assert( p_parent != NULL && p_item != NULL );
527     assert( p_parent->i_children > -1 );
528
529     if( i_pos == PLAYLIST_END ) i_pos = p_parent->i_children;
530
531     bool b_flat = false;
532
533     playlist_item_t *p_up = p_parent;
534     while( p_up )
535     {
536         if( p_up == p_playlist->p_playing )
537             if( !pl_priv(p_playlist)->b_tree ) b_flat = true;
538         if( p_up == p_item )
539             /* TODO: We don't support copying a node into itself (yet),
540             because we insert items as we copy. Instead, we should copy
541             all items first and then insert. */
542             return i_pos;
543         p_up = p_up->p_parent;
544     }
545
546     return RecursiveInsertCopy( p_playlist, p_item, p_parent, i_pos, b_flat );
547 }
548
549 /**
550  * Insert a tree of input items into a given playlist node
551  *
552  * \param p_playlist the playlist to insert into
553  * \param p_parent the receiving playlist node (can be an item)
554  * \param p_node the root of input item tree,
555           only it's contents will be inserted
556  * \param i_pos the position in the playlist where to insert. If this is
557  *        PLAYLIST_END the items will be added at the end of the playlist
558  *        regardless of its size
559  * \param b_flat TRUE if the new tree contents should be flattened into a list
560  * \return the first new leaf inserted (in playing order)
561  */
562 int playlist_InsertInputItemTree (
563     playlist_t *p_playlist, playlist_item_t *p_parent,
564     input_item_node_t *p_node, int i_pos, bool b_flat )
565 {
566   playlist_item_t *p_first_leaf = NULL;
567   int i = RecursiveAddIntoParent ( p_playlist, p_parent, p_node, i_pos, b_flat, &p_first_leaf );
568   return i;
569 }
570
571
572 /*****************************************************************************
573  * Playlist item misc operations
574  *****************************************************************************/
575
576 /**
577  * Find an item within a root, given its input id.
578  *
579  * \param p_playlist the playlist object
580  * \param p_item the input item
581  * \param p_root root playlist item
582  * \param b_items_only TRUE if we want the item himself
583  * \return the first found item, or NULL if not found
584  */
585 playlist_item_t *playlist_ItemFindFromInputAndRoot( playlist_t *p_playlist,
586                                                     input_item_t *p_item,
587                                                     playlist_item_t *p_root,
588                                                     bool b_items_only )
589 {
590     int i;
591     for( i = 0 ; i< p_root->i_children ; i++ )
592     {
593         if( ( b_items_only ? p_root->pp_children[i]->i_children == -1 : 1 ) &&
594             p_root->pp_children[i]->p_input == p_item )
595         {
596             return p_root->pp_children[i];
597         }
598         else if( p_root->pp_children[i]->i_children >= 0 )
599         {
600             playlist_item_t *p_search =
601                  playlist_ItemFindFromInputAndRoot( p_playlist, p_item,
602                                                     p_root->pp_children[i],
603                                                     b_items_only );
604             if( p_search ) return p_search;
605         }
606     }
607     return NULL;
608 }
609
610
611 static int ItemIndex ( playlist_item_t *p_item )
612 {
613     for( int i = 0; i < p_item->p_parent->i_children; i++ )
614         if( p_item->p_parent->pp_children[i] == p_item ) return i;
615     return -1;
616 }
617
618 /**
619  * Moves an item
620  *
621  * This function must be entered with the playlist lock
622  *
623  * \param p_playlist the playlist
624  * \param p_item the item to move
625  * \param p_node the new parent of the item
626  * \param i_newpos the new position under this new parent
627  * \return VLC_SUCCESS or an error
628  */
629 int playlist_TreeMove( playlist_t * p_playlist, playlist_item_t *p_item,
630                        playlist_item_t *p_node, int i_newpos )
631 {
632     PL_ASSERT_LOCKED;
633
634     if( p_node->i_children == -1 ) return VLC_EGENERIC;
635
636     playlist_item_t *p_detach = p_item->p_parent;
637     int i_index = ItemIndex( p_item );
638
639     REMOVE_ELEM( p_detach->pp_children, p_detach->i_children, i_index );
640
641     if( p_detach == p_node && i_index < i_newpos )
642         i_newpos--;
643
644     INSERT_ELEM( p_node->pp_children, p_node->i_children, i_newpos, p_item );
645     p_item->p_parent = p_node;
646
647     pl_priv( p_playlist )->b_reset_currently_playing = true;
648     vlc_cond_signal( &pl_priv( p_playlist )->signal );
649     return VLC_SUCCESS;
650 }
651
652 /**
653  * Moves an array of items
654  *
655  * This function must be entered with the playlist lock
656  *
657  * \param p_playlist the playlist
658  * \param i_items the number of indexes to move
659  * \param pp_items the array of indexes to move
660  * \param p_node the target node
661  * \param i_newpos the target position under this node
662  * \return VLC_SUCCESS or an error
663  */
664 int playlist_TreeMoveMany( playlist_t *p_playlist,
665                             int i_items, playlist_item_t **pp_items,
666                             playlist_item_t *p_node, int i_newpos )
667 {
668     PL_ASSERT_LOCKED;
669
670     if ( p_node->i_children == -1 ) return VLC_EGENERIC;
671
672     int i;
673     for( i = 0; i < i_items; i++ )
674     {
675         playlist_item_t *p_item = pp_items[i];
676         int i_index = ItemIndex( p_item );
677         playlist_item_t *p_parent = p_item->p_parent;
678         REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i_index );
679         if ( p_parent == p_node && i_index < i_newpos ) i_newpos--;
680     }
681     for( i = i_items - 1; i >= 0; i-- )
682     {
683         playlist_item_t *p_item = pp_items[i];
684         INSERT_ELEM( p_node->pp_children, p_node->i_children, i_newpos, p_item );
685         p_item->p_parent = p_node;
686     }
687
688     pl_priv( p_playlist )->b_reset_currently_playing = true;
689     vlc_cond_signal( &pl_priv( p_playlist )->signal );
690     return VLC_SUCCESS;
691 }
692
693 /**
694  * Send a notification that an item has been added to a node
695  *
696  * \param p_playlist the playlist object
697  * \param i_item_id id of the item added
698  * \param i_node_id id of the node in wich the item was added
699  * \param b_signal TRUE if the function must send a signal
700  * \return nothing
701  */
702 void playlist_SendAddNotify( playlist_t *p_playlist, int i_item_id,
703                              int i_node_id, bool b_signal )
704 {
705     playlist_private_t *p_sys = pl_priv(p_playlist);
706     PL_ASSERT_LOCKED;
707
708     p_sys->b_reset_currently_playing = true;
709     if( b_signal )
710         vlc_cond_signal( &p_sys->signal );
711
712     playlist_add_t add;
713     add.i_item = i_item_id;
714     add.i_node = i_node_id;
715
716     vlc_value_t val;
717     val.p_address = &add;
718
719     var_Set( p_playlist, "playlist-item-append", val );
720 }
721
722 /***************************************************************************
723  * The following functions are local
724  ***************************************************************************/
725
726 /* Enqueue an item for preparsing, and play it, if needed */
727 static void GoAndPreparse( playlist_t *p_playlist, int i_mode,
728                            playlist_item_t *p_item )
729 {
730     PL_ASSERT_LOCKED;
731     if( (i_mode & PLAYLIST_GO ) )
732     {
733         pl_priv(p_playlist)->request.b_request = true;
734         pl_priv(p_playlist)->request.i_skip = 0;
735         pl_priv(p_playlist)->request.p_item = p_item;
736         if( pl_priv(p_playlist)->p_input )
737             input_Stop( pl_priv(p_playlist)->p_input, true );
738         pl_priv(p_playlist)->request.i_status = PLAYLIST_RUNNING;
739         vlc_cond_signal( &pl_priv(p_playlist)->signal );
740     }
741     /* Preparse if no artist/album info, and hasn't been preparsed allready
742        and if user has some preparsing option (auto-preparse variable)
743        enabled*/
744     char *psz_artist = input_item_GetArtist( p_item->p_input );
745     char *psz_album = input_item_GetAlbum( p_item->p_input );
746     if( pl_priv(p_playlist)->b_auto_preparse &&
747         input_item_IsPreparsed( p_item->p_input ) == false &&
748             ( EMPTY_STR( psz_artist ) || ( EMPTY_STR( psz_album ) ) )
749           )
750         playlist_PreparseEnqueue( p_playlist, p_item->p_input );
751     free( psz_artist );
752     free( psz_album );
753 }
754
755 /* Add the playlist item to the requested node and fire a notification */
756 static void AddItem( playlist_t *p_playlist, playlist_item_t *p_item,
757                      playlist_item_t *p_node, int i_mode, int i_pos )
758 {
759     PL_ASSERT_LOCKED;
760     ARRAY_APPEND(p_playlist->items, p_item);
761     ARRAY_APPEND(p_playlist->all_items, p_item);
762
763     if( i_pos == PLAYLIST_END )
764         playlist_NodeAppend( p_playlist, p_item, p_node );
765     else
766         playlist_NodeInsert( p_playlist, p_item, p_node, i_pos );
767
768     if( !pl_priv(p_playlist)->b_doing_ml )
769         playlist_SendAddNotify( p_playlist, p_item->i_id, p_node->i_id,
770                                  !( i_mode & PLAYLIST_NO_REBUILD ) );
771 }
772
773 /* Actually convert an item to a node */
774 static void ChangeToNode( playlist_t *p_playlist, playlist_item_t *p_item )
775 {
776     int i;
777     if( p_item->i_children != -1 ) return;
778
779     p_item->i_children = 0;
780
781     input_item_t *p_input = p_item->p_input;
782     vlc_mutex_lock( &p_input->lock );
783     p_input->i_type = ITEM_TYPE_NODE;
784     vlc_mutex_unlock( &p_input->lock );
785
786     var_SetAddress( p_playlist, "item-change", p_item->p_input );
787
788     /* Remove it from the array of available items */
789     ARRAY_BSEARCH( p_playlist->items,->i_id, int, p_item->i_id, i );
790     if( i != -1 )
791         ARRAY_REMOVE( p_playlist->items, i );
792 }
793
794 /* Do the actual removal */
795 int playlist_DeleteItem( playlist_t * p_playlist, playlist_item_t *p_item,
796                         bool b_stop )
797 {
798     assert( b_stop );
799     return playlist_NodeDelete( p_playlist, p_item, true, false );
800 #if 0
801     int i;
802     int i_id = p_item->i_id;
803     PL_ASSERT_LOCKED;
804
805     if( p_item->i_children > -1 )
806     {
807         return playlist_NodeDelete( p_playlist, p_item, true, false );
808     }
809
810     pl_priv(p_playlist)->b_reset_currently_playing = true;
811     var_SetInteger( p_playlist, "playlist-item-deleted", i_id );
812
813     /* Remove the item from the bank */
814     ARRAY_BSEARCH( p_playlist->all_items,->i_id, int, i_id, i );
815     if( i != -1 )
816         ARRAY_REMOVE( p_playlist->all_items, i );
817
818     ARRAY_BSEARCH( p_playlist->items,->i_id, int, i_id, i );
819     if( i != -1 )
820         ARRAY_REMOVE( p_playlist->items, i );
821
822     /* Check if it is the current item */
823     if( get_current_status_item( p_playlist ) == p_item )
824     {
825         /* Stop it if we have to */
826         if( b_stop )
827         {
828             playlist_Control( p_playlist, PLAYLIST_STOP, pl_Locked );
829             msg_Info( p_playlist, "stopping playback" );
830         }
831         /* In any case, this item can't be the next one to be played ! */
832         set_current_status_item( p_playlist, NULL );
833     }
834
835     ARRAY_BSEARCH( p_playlist->current,->i_id, int, i_id, i );
836     if( i != -1 )
837         ARRAY_REMOVE( p_playlist->current, i );
838
839     PL_DEBUG( "deleting item `%s'", p_item->p_input->psz_name );
840
841     /* Remove the item from its parent */
842     playlist_NodeRemoveItem( p_playlist, p_item, p_item->p_parent );
843
844     playlist_ItemRelease( p_item );
845
846     return VLC_SUCCESS;
847 #endif
848 }
849
850 static int RecursiveAddIntoParent (
851     playlist_t *p_playlist, playlist_item_t *p_parent,
852     input_item_node_t *p_node, int i_pos, bool b_flat,
853     playlist_item_t **pp_first_leaf )
854 {
855   *pp_first_leaf = NULL;
856
857   if( p_parent->i_children == -1 ) ChangeToNode( p_playlist, p_parent );
858
859   if( i_pos == PLAYLIST_END ) i_pos = p_parent->i_children;
860
861   for( int i = 0; i < p_node->i_children; i++ )
862   {
863       input_item_node_t *p_child_node = p_node->pp_children[i];
864
865       playlist_item_t *p_new_item = NULL;
866       bool b_children = p_child_node->i_children > 0;
867
868       //Create the playlist item represented by input node, if allowed.
869       if( !(b_flat && b_children) )
870       {
871           p_new_item = playlist_NodeAddInput( p_playlist,
872                                               p_child_node->p_item,
873                                               p_parent,
874                                               PLAYLIST_INSERT, i_pos,
875                                               pl_Locked );
876           if( !p_new_item ) return i_pos;
877
878           i_pos++;
879       }
880       //Recurse if any children
881       if( b_children )
882       {
883           //Substitute p_new_item for first child leaf
884           //(If flat, continue counting from current position)
885           int i_last_pos = RecursiveAddIntoParent(
886                                       p_playlist,
887                                       p_new_item ? p_new_item : p_parent,
888                                       p_child_node,
889                                       ( b_flat ? i_pos : 0 ),
890                                       b_flat,
891                                       &p_new_item );
892           //If flat, take position after recursion as current position
893           if( b_flat ) i_pos = i_last_pos;
894       }
895
896       assert( p_new_item != NULL );
897       if( i == 0 ) *pp_first_leaf = p_new_item;
898   }
899   return i_pos;
900 }
901
902 static int RecursiveInsertCopy (
903     playlist_t *p_playlist, playlist_item_t *p_item,
904     playlist_item_t *p_parent, int i_pos, bool b_flat )
905 {
906     PL_ASSERT_LOCKED;
907     assert( p_parent != NULL && p_item != NULL );
908
909     if( p_item == p_parent ) return i_pos;
910
911     input_item_t *p_input = p_item->p_input;
912
913     if( !(p_item->i_children != -1 && b_flat) )
914     {
915         input_item_t *p_new_input = input_item_Copy( VLC_OBJECT(p_playlist),
916                                                      p_input );
917         if( !p_new_input ) return i_pos;
918
919         playlist_item_t *p_new_item = NULL;
920         if( p_item->i_children == -1 )
921             p_new_item = playlist_NodeAddInput( p_playlist, p_new_input,
922                                    p_parent, PLAYLIST_INSERT, i_pos,
923                                    pl_Locked );
924         else
925             p_new_item = playlist_NodeCreate( p_playlist, NULL,
926                                  p_parent, i_pos, 0, p_new_input );
927         vlc_gc_decref( p_new_input );
928         if( !p_new_item ) return i_pos;
929
930         i_pos++;
931
932         if( p_new_item->i_children != -1 )
933             p_parent = p_new_item;
934     }
935
936     for( int i = 0; i < p_item->i_children; i++ )
937     {
938         if( b_flat )
939             i_pos = RecursiveInsertCopy( p_playlist, p_item->pp_children[i],
940                                          p_parent, i_pos, true );
941         else
942             RecursiveInsertCopy( p_playlist, p_item->pp_children[i],
943                                  p_parent, p_parent->i_children, false );
944     }
945
946     return i_pos;
947 }