item.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:34k
源码类别:

midi

开发平台:

Unix_Linux

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