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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * tree.c : Playlist tree walking functions
  3.  *****************************************************************************
  4.  * Copyright (C) 1999-2007 the VideoLAN team
  5.  * $Id: 1eab637c0ab5aa5467e2a02f5f29b8ea7d7d5101 $
  6.  *
  7.  * Authors: Clément Stenac <zorglub@videolan.org>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. #ifdef HAVE_CONFIG_H
  24. # include "config.h"
  25. #endif
  26. #include <vlc_common.h>
  27. #include <assert.h>
  28. #include "vlc_playlist.h"
  29. #include "playlist_internal.h"
  30. /************************************************************************
  31.  * Local prototypes
  32.  ************************************************************************/
  33. playlist_item_t *GetNextUncle( playlist_t *p_playlist, playlist_item_t *p_item,
  34.                                playlist_item_t *p_root );
  35. playlist_item_t *GetPrevUncle( playlist_t *p_playlist, playlist_item_t *p_item,
  36.                                playlist_item_t *p_root );
  37. playlist_item_t *GetNextItem( playlist_t *p_playlist,
  38.                               playlist_item_t *p_root,
  39.                               playlist_item_t *p_item );
  40. playlist_item_t *GetPrevItem( playlist_t *p_playlist,
  41.                               playlist_item_t *p_item,
  42.                               playlist_item_t *p_root );
  43. /**
  44.  * Create a playlist node
  45.  *
  46.  * param p_playlist the playlist
  47.  * param psz_name the name of the node
  48.  * param p_parent the parent node to attach to or NULL if no attach
  49.  * param p_flags miscellaneous flags
  50.  * param p_input the input_item to attach to or NULL if it has to be created
  51.  * return the new node
  52.  */
  53. playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist,
  54.                                        const char *psz_name,
  55.                                        playlist_item_t *p_parent, int i_flags,
  56.                                        input_item_t *p_input )
  57. {
  58.     input_item_t *p_new_input = NULL;
  59.     playlist_item_t *p_item;
  60.     PL_ASSERT_LOCKED;
  61.     if( !psz_name ) psz_name = _("Undefined");
  62.     if( !p_input )
  63.         p_new_input = input_item_NewWithType( VLC_OBJECT(p_playlist), NULL,
  64.                                         psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE );
  65.     p_item = playlist_ItemNewFromInput( p_playlist,
  66.                                         p_input ? p_input : p_new_input );
  67.     if( p_new_input )
  68.         vlc_gc_decref( p_new_input );
  69.     if( p_item == NULL )  return NULL;
  70.     p_item->i_children = 0;
  71.     ARRAY_APPEND(p_playlist->all_items, p_item);
  72.     if( p_parent != NULL )
  73.         playlist_NodeAppend( p_playlist, p_item, p_parent );
  74.     playlist_SendAddNotify( p_playlist, p_item->i_id,
  75.                             p_parent ? p_parent->i_id : -1,
  76.                             !( i_flags & PLAYLIST_NO_REBUILD ));
  77.     return p_item;
  78. }
  79. /**
  80.  * Remove all the children of a node
  81.  *
  82.  * This function must be entered with the playlist lock
  83.  *
  84.  * param p_playlist the playlist
  85.  * param p_root the node
  86.  * param b_delete_items do we have to delete the children items ?
  87.  * return VLC_SUCCESS or an error
  88.  */
  89. int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
  90.                         bool b_delete_items )
  91. {
  92.     PL_ASSERT_LOCKED;
  93.     int i;
  94.     if( p_root->i_children == -1 )
  95.     {
  96.         return VLC_EGENERIC;
  97.     }
  98.     /* Delete the children */
  99.     for( i =  p_root->i_children-1 ; i >= 0 ;i-- )
  100.     {
  101.         if( p_root->pp_children[i]->i_children > -1 )
  102.         {
  103.             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
  104.                                  b_delete_items , false );
  105.         }
  106.         else if( b_delete_items )
  107.         {
  108.             /* Delete the item here */
  109.             playlist_DeleteFromItemId( p_playlist,
  110.                                        p_root->pp_children[i]->i_id );
  111.         }
  112.     }
  113.     return VLC_SUCCESS;
  114. }
  115. /**
  116.  * Remove all the children of a node and removes the node
  117.  *
  118.  * param p_playlist the playlist
  119.  * param p_root the node
  120.  * param b_delete_items do we have to delete the children items ?
  121.  * return VLC_SUCCESS or an error
  122.  */
  123. int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
  124.                          bool b_delete_items, bool b_force )
  125. {
  126.     PL_ASSERT_LOCKED;
  127.     int i;
  128.     if( p_root->i_children == -1 )
  129.     {
  130.         return VLC_EGENERIC;
  131.     }
  132.     /* Delete the children */
  133.     for( i =  p_root->i_children - 1 ; i >= 0; i-- )
  134.     {
  135.         if( p_root->pp_children[i]->i_children > -1 )
  136.         {
  137.             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
  138.                                  b_delete_items , b_force );
  139.         }
  140.         else if( b_delete_items )
  141.         {
  142.             playlist_DeleteFromItemId( p_playlist,
  143.                                        p_root->pp_children[i]->i_id );
  144.         }
  145.     }
  146.     /* Delete the node */
  147.     if( p_root->i_flags & PLAYLIST_RO_FLAG && !b_force )
  148.     {
  149.     }
  150.     else
  151.     {
  152.         int i;
  153.         var_SetInteger( p_playlist, "playlist-item-deleted", p_root->i_id );
  154.         ARRAY_BSEARCH( p_playlist->all_items, ->i_id, int,
  155.                        p_root->i_id, i );
  156.         if( i != -1 )
  157.             ARRAY_REMOVE( p_playlist->all_items, i );
  158.         /* Remove the item from its parent */
  159.         if( p_root->p_parent )
  160.             playlist_NodeRemoveItem( p_playlist, p_root, p_root->p_parent );
  161.         playlist_ItemRelease( p_root );
  162.     }
  163.     return VLC_SUCCESS;
  164. }
  165. /**
  166.  * Adds an item to the children of a node
  167.  *
  168.  * param p_playlist the playlist
  169.  * param p_item the item to append
  170.  * param p_parent the parent node
  171.  * return VLC_SUCCESS or an error
  172.  */
  173. int playlist_NodeAppend( playlist_t *p_playlist,
  174.                          playlist_item_t *p_item,
  175.                          playlist_item_t *p_parent )
  176. {
  177.     return playlist_NodeInsert( p_playlist, p_item, p_parent, -1 );
  178. }
  179. int playlist_NodeInsert( playlist_t *p_playlist,
  180.                          playlist_item_t *p_item,
  181.                          playlist_item_t *p_parent,
  182.                          int i_position )
  183. {
  184.     PL_ASSERT_LOCKED;
  185.    (void)p_playlist;
  186.    assert( p_parent && p_parent->i_children != -1 );
  187.    if( i_position == -1 ) i_position = p_parent->i_children ;
  188.    INSERT_ELEM( p_parent->pp_children,
  189.                 p_parent->i_children,
  190.                 i_position,
  191.                 p_item );
  192.    p_item->p_parent = p_parent;
  193.    return VLC_SUCCESS;
  194. }
  195. /**
  196.  * Deletes an item from the children of a node
  197.  *
  198.  * param p_playlist the playlist
  199.  * param p_item the item to remove
  200.  * param p_parent the parent node
  201.  * return VLC_SUCCESS or an error
  202.  */
  203. int playlist_NodeRemoveItem( playlist_t *p_playlist,
  204.                         playlist_item_t *p_item,
  205.                         playlist_item_t *p_parent )
  206. {
  207.     PL_ASSERT_LOCKED;
  208.    (void)p_playlist;
  209.    for(int i= 0; i< p_parent->i_children ; i++ )
  210.    {
  211.        if( p_parent->pp_children[i] == p_item )
  212.        {
  213.            REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
  214.        }
  215.    }
  216.    return VLC_SUCCESS;
  217. }
  218. /**
  219.  * Search a child of a node by its name
  220.  *
  221.  * param p_node the node
  222.  * param psz_search the name of the child to search
  223.  * return the child item or NULL if not found or error
  224.  */
  225. playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
  226.                                            const char *psz_search )
  227. {
  228.     playlist_t * p_playlist = p_node->p_playlist; /* For assert_locked */
  229.     PL_ASSERT_LOCKED;
  230.     int i;
  231.     if( p_node->i_children < 0 )
  232.     {
  233.          return NULL;
  234.     }
  235.     for( i = 0 ; i< p_node->i_children; i++ )
  236.     {
  237.         if( !strcmp( p_node->pp_children[i]->p_input->psz_name, psz_search ) )
  238.         {
  239.             return p_node->pp_children[i];
  240.         }
  241.     }
  242.     return NULL;
  243. }
  244. /**
  245.  * Create a pair of nodes in the category and onelevel trees.
  246.  * They share the same input item.
  247.  * param p_playlist the playlist
  248.  * param psz_name the name of the nodes
  249.  * param pp_node_cat pointer to return the node in category tree
  250.  * param pp_node_one pointer to return the node in onelevel tree
  251.  * param b_for_sd For Services Discovery ? (make node read-only and unskipping)
  252.  */
  253. void playlist_NodesPairCreate( playlist_t *p_playlist, const char *psz_name,
  254.                                playlist_item_t **pp_node_cat,
  255.                                playlist_item_t **pp_node_one,
  256.                                bool b_for_sd )
  257. {
  258.     PL_ASSERT_LOCKED;
  259.     *pp_node_cat = playlist_NodeCreate( p_playlist, psz_name,
  260.                                         p_playlist->p_root_category, 0, NULL );
  261.     *pp_node_one = playlist_NodeCreate( p_playlist, psz_name,
  262.                                         p_playlist->p_root_onelevel, 0,
  263.                                         (*pp_node_cat)->p_input );
  264.     if( b_for_sd )
  265.     {
  266.         (*pp_node_cat)->i_flags |= PLAYLIST_RO_FLAG;
  267.         (*pp_node_cat)->i_flags |= PLAYLIST_SKIP_FLAG;
  268.         (*pp_node_one)->i_flags |= PLAYLIST_RO_FLAG;
  269.         (*pp_node_one)->i_flags |= PLAYLIST_SKIP_FLAG;
  270.     }
  271. }
  272. /**
  273.  * Get the node in the preferred tree from a node in one of category
  274.  * or onelevel tree.
  275.  */
  276. playlist_item_t * playlist_GetPreferredNode( playlist_t *p_playlist,
  277.                                              playlist_item_t *p_node )
  278. {
  279.     PL_ASSERT_LOCKED;
  280.     int i;
  281.     if( p_node->p_parent == p_playlist->p_root_category )
  282.     {
  283.         if( pl_priv(p_playlist)->b_tree || p_node->p_input->b_prefers_tree )
  284.             return p_node;
  285.         for( i = 0 ; i< p_playlist->p_root_onelevel->i_children; i++ )
  286.         {
  287.             if( p_playlist->p_root_onelevel->pp_children[i]->p_input->i_id ==
  288.                     p_node->p_input->i_id )
  289.                 return p_playlist->p_root_onelevel->pp_children[i];
  290.         }
  291.     }
  292.     else if( p_node->p_parent == p_playlist->p_root_onelevel )
  293.     {
  294.         if( !pl_priv(p_playlist)->b_tree || !p_node->p_input->b_prefers_tree )
  295.             return p_node;
  296.         for( i = 0 ; i< p_playlist->p_root_category->i_children; i++ )
  297.         {
  298.             if( p_playlist->p_root_category->pp_children[i]->p_input->i_id ==
  299.                     p_node->p_input->i_id )
  300.                 return p_playlist->p_root_category->pp_children[i];
  301.         }
  302.     }
  303.     return NULL;
  304. }
  305. /**********************************************************************
  306.  * Tree walking functions
  307.  **********************************************************************/
  308. playlist_item_t *playlist_GetLastLeaf(playlist_t *p_playlist,
  309.                                       playlist_item_t *p_root )
  310. {
  311.     PL_ASSERT_LOCKED;
  312.     int i;
  313.     playlist_item_t *p_item;
  314.     for ( i = p_root->i_children - 1; i >= 0; i-- )
  315.     {
  316.         if( p_root->pp_children[i]->i_children == -1 )
  317.             return p_root->pp_children[i];
  318.         else if( p_root->pp_children[i]->i_children > 0)
  319.         {
  320.              p_item = playlist_GetLastLeaf( p_playlist,
  321.                                             p_root->pp_children[i] );
  322.             if ( p_item != NULL )
  323.                 return p_item;
  324.         }
  325.         else if( i == 0 )
  326.             return NULL;
  327.     }
  328.     return NULL;
  329. }
  330. /**
  331.  * Finds the next item to play
  332.  *
  333.  * param p_playlist the playlist
  334.  * param p_root the root node
  335.  * param p_item the previous item  (NULL if none )
  336.  * return the next item to play, or NULL if none found
  337.  */
  338. playlist_item_t *playlist_GetNextLeaf( playlist_t *p_playlist,
  339.                                        playlist_item_t *p_root,
  340.                                        playlist_item_t *p_item,
  341.                                        bool b_ena, bool b_unplayed )
  342. {
  343.     PL_ASSERT_LOCKED;
  344.     playlist_item_t *p_next;
  345.     assert( p_root && p_root->i_children != -1 );
  346.     PL_DEBUG2( "finding next of %s within %s",
  347.                PLI_NAME( p_item ), PLI_NAME( p_root ) );
  348.     /* Now, walk the tree until we find a suitable next item */
  349.     p_next = p_item;
  350.     while( 1 )
  351.     {
  352.         bool b_ena_ok = true, b_unplayed_ok = true;
  353.         p_next = GetNextItem( p_playlist, p_root, p_next );
  354.         if( !p_next || p_next == p_root )
  355.             break;
  356.         if( p_next->i_children == -1 )
  357.         {
  358.             if( b_ena && p_next->i_flags & PLAYLIST_DBL_FLAG )
  359.                 b_ena_ok = false;
  360.             if( b_unplayed && p_next->p_input->i_nb_played != 0 )
  361.                 b_unplayed_ok = false;
  362.             if( b_ena_ok && b_unplayed_ok ) break;
  363.         }
  364.     }
  365.     if( p_next == NULL ) PL_DEBUG2( "at end of node" );
  366.     return p_next;
  367. }
  368. /**
  369.  * Finds the previous item to play
  370.  *
  371.  * param p_playlist the playlist
  372.  * param p_root the root node
  373.  * param p_item the previous item  (NULL if none )
  374.  * return the next item to play, or NULL if none found
  375.  */
  376. playlist_item_t *playlist_GetPrevLeaf( playlist_t *p_playlist,
  377.                                        playlist_item_t *p_root,
  378.                                        playlist_item_t *p_item,
  379.                                        bool b_ena, bool b_unplayed )
  380. {
  381.     PL_ASSERT_LOCKED;
  382.     playlist_item_t *p_prev;
  383.     PL_DEBUG2( "finding previous os %s within %s", PLI_NAME( p_item ),
  384.                                                    PLI_NAME( p_root ) );
  385.     assert( p_root && p_root->i_children != -1 );
  386.     /* Now, walk the tree until we find a suitable previous item */
  387.     p_prev = p_item;
  388.     while( 1 )
  389.     {
  390.         bool b_ena_ok = true, b_unplayed_ok = true;
  391.         p_prev = GetPrevItem( p_playlist, p_root, p_prev );
  392.         if( !p_prev || p_prev == p_root )
  393.             break;
  394.         if( p_prev->i_children == -1 )
  395.         {
  396.             if( b_ena && p_prev->i_flags & PLAYLIST_DBL_FLAG )
  397.                 b_ena_ok = false;
  398.             if( b_unplayed && p_prev->p_input->i_nb_played != 0 )
  399.                 b_unplayed_ok = false;
  400.             if( b_ena_ok && b_unplayed_ok ) break;
  401.         }
  402.     }
  403.     if( p_prev == NULL ) PL_DEBUG2( "at beginning of node" );
  404.     return p_prev;
  405. }
  406. /************************************************************************
  407.  * Following functions are local
  408.  ***********************************************************************/
  409. /**
  410.  * Get the next item in the tree
  411.  * If p_item is NULL, return the first child of root
  412.  **/
  413. playlist_item_t *GetNextItem( playlist_t *p_playlist,
  414.                               playlist_item_t *p_root,
  415.                               playlist_item_t *p_item )
  416. {
  417.     /* If the item is NULL, return the firt child of root */
  418.     if( p_item == NULL )
  419.     {
  420.         if( p_root->i_children > 0 )
  421.             return p_root->pp_children[0];
  422.         else
  423.             return NULL;
  424.     }
  425.     /* Node with children, get the first one */
  426.     if( p_item->i_children > 0 )
  427.         return p_item->pp_children[0];
  428.     playlist_item_t* p_parent = p_item->p_parent;
  429.     for( int i = 0 ; i < p_parent->i_children ; i++ )
  430.     {
  431.         if( p_parent->pp_children[i] == p_item )
  432.         {
  433.             // Return the next children
  434.             if( i + 1 < p_parent->i_children )
  435.                 return p_parent->pp_children[i+1];
  436.             // We are the least one, so try to have uncles
  437.             else
  438.             {
  439.                 PL_DEBUG2( "Current item is the last of the node,"
  440.                            "looking for uncle from %s",
  441.                             p_parent->p_input->psz_name );
  442.                 if( p_parent == p_root )
  443.                 {
  444.                     PL_DEBUG2( "already at root" );
  445.                     return NULL;
  446.                 }
  447.                 else
  448.                     return GetNextUncle( p_playlist, p_item, p_root );
  449.             }
  450.         }
  451.     }
  452.     return NULL;
  453. }
  454. playlist_item_t *GetNextUncle( playlist_t *p_playlist, playlist_item_t *p_item,
  455.                                playlist_item_t *p_root )
  456. {
  457.     playlist_item_t *p_parent = p_item->p_parent;
  458.     playlist_item_t *p_grandparent;
  459.     bool b_found = false;
  460.     (void)p_playlist;
  461.     if( p_parent != NULL )
  462.     {
  463.         p_grandparent = p_parent->p_parent;
  464.         while( p_grandparent )
  465.         {
  466.             int i;
  467.             for( i = 0 ; i< p_grandparent->i_children ; i++ )
  468.             {
  469.                 if( p_parent == p_grandparent->pp_children[i] )
  470.                 {
  471.                     PL_DEBUG2( "parent %s found as child %i of grandparent %s",
  472.                                p_parent->p_input->psz_name, i,
  473.                                p_grandparent->p_input->psz_name );
  474.                     b_found = true;
  475.                     break;
  476.                 }
  477.             }
  478.             if( b_found && i + 1 < p_grandparent->i_children )
  479.             {
  480.                     return p_grandparent->pp_children[i+1];
  481.             }
  482.             /* Not found at root */
  483.             if( p_grandparent == p_root )
  484.             {
  485.                 return NULL;
  486.             }
  487.             else
  488.             {
  489.                 p_parent = p_grandparent;
  490.                 p_grandparent = p_parent->p_parent;
  491.             }
  492.         }
  493.     }
  494.     /* We reached root */
  495.     return NULL;
  496. }
  497. playlist_item_t *GetPrevUncle( playlist_t *p_playlist, playlist_item_t *p_item,
  498.                                playlist_item_t *p_root )
  499. {
  500.     playlist_item_t *p_parent = p_item->p_parent;
  501.     playlist_item_t *p_grandparent;
  502.     bool b_found = false;
  503.     (void)p_playlist;
  504.     if( p_parent != NULL )
  505.     {
  506.         p_grandparent = p_parent->p_parent;
  507.         while( 1 )
  508.         {
  509.             int i;
  510.             for( i = p_grandparent->i_children -1 ; i >= 0; i-- )
  511.             {
  512.                 if( p_parent == p_grandparent->pp_children[i] )
  513.                 {
  514.                     b_found = true;
  515.                     break;
  516.                 }
  517.             }
  518.             if( b_found && i - 1 > 0 )
  519.             {
  520.                 return p_grandparent->pp_children[i-1];
  521.             }
  522.             /* Not found at root */
  523.             if( p_grandparent == p_root )
  524.             {
  525.                 return NULL;
  526.             }
  527.             else
  528.             {
  529.                 p_parent = p_grandparent;
  530.                 p_grandparent = p_parent->p_parent;
  531.             }
  532.         }
  533.     }
  534.     /* We reached root */
  535.     return NULL;
  536. }
  537. /* Recursively search the tree for previous item */
  538. playlist_item_t *GetPrevItem( playlist_t *p_playlist,
  539.                               playlist_item_t *p_root,
  540.                               playlist_item_t *p_item )
  541. {
  542.     playlist_item_t *p_parent;
  543.     int i;
  544.     /* Node with children, get the last one */
  545.     if( p_item && p_item->i_children > 0 )
  546.         return p_item->pp_children[p_item->i_children - 1];
  547.     /* Last child of its parent ? */
  548.     if( p_item != NULL )
  549.         p_parent = p_item->p_parent;
  550.     else
  551.     {
  552.         msg_Err( p_playlist, "Get the last one" );
  553.         abort();
  554.     };
  555.     for( i = p_parent->i_children -1 ; i >= 0 ;  i-- )
  556.     {
  557.         if( p_parent->pp_children[i] == p_item )
  558.         {
  559.             if( i-1 < 0 )
  560.             {
  561.                /* Was already the first sibling. Look for uncles */
  562.                 PL_DEBUG2( "current item is the first of its node,"
  563.                            "looking for uncle from %s",
  564.                            p_parent->p_input->psz_name );
  565.                 if( p_parent == p_root )
  566.                 {
  567.                     PL_DEBUG2( "already at root" );
  568.                     return NULL;
  569.                 }
  570.                 return GetPrevUncle( p_playlist, p_item, p_root );
  571.             }
  572.             else
  573.             {
  574.                 return p_parent->pp_children[i-1];
  575.             }
  576.         }
  577.     }
  578.     return NULL;
  579. }