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