1 /*****************************************************************************
2 * tree.c : Playlist tree walking functions
3 *****************************************************************************
4 * Copyright (C) 1999-2007 VLC authors and VideoLAN
7 * Authors: Clément Stenac <zorglub@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
27 #include <vlc_common.h>
29 #include "vlc_playlist.h"
30 #include "playlist_internal.h"
32 /************************************************************************
34 ************************************************************************/
35 playlist_item_t
*GetNextUncle( playlist_t
*p_playlist
, playlist_item_t
*p_item
,
36 playlist_item_t
*p_root
);
37 playlist_item_t
*GetPrevUncle( playlist_t
*p_playlist
, playlist_item_t
*p_item
,
38 playlist_item_t
*p_root
);
40 playlist_item_t
*GetNextItem( playlist_t
*p_playlist
,
41 playlist_item_t
*p_root
,
42 playlist_item_t
*p_item
);
43 playlist_item_t
*GetPrevItem( playlist_t
*p_playlist
,
44 playlist_item_t
*p_item
,
45 playlist_item_t
*p_root
);
48 * Create a playlist node
50 * \param p_playlist the playlist
51 * \param psz_name the name of the node
52 * \param p_parent the parent node to attach to or NULL if no attach
53 * \param i_pos position of the node in the parent, PLAYLIST_END to append to end.
54 * \param p_flags miscellaneous flags
55 * \param p_input the input_item to attach to or NULL if it has to be created
56 * \return the new node
58 playlist_item_t
* playlist_NodeCreate( playlist_t
*p_playlist
,
60 playlist_item_t
*p_parent
, int i_pos
,
61 int i_flags
, input_item_t
*p_input
)
63 input_item_t
*p_new_input
= NULL
;
64 playlist_item_t
*p_item
;
67 if( !psz_name
) psz_name
= _("Undefined");
70 p_new_input
= input_item_NewWithType( NULL
, psz_name
, 0, NULL
, 0, -1,
72 p_item
= playlist_ItemNewFromInput( p_playlist
,
73 p_input
? p_input
: p_new_input
);
75 vlc_gc_decref( p_new_input
);
77 if( p_item
== NULL
) return NULL
;
78 p_item
->i_children
= 0;
80 ARRAY_APPEND(p_playlist
->all_items
, p_item
);
82 if( p_parent
!= NULL
)
83 playlist_NodeInsert( p_playlist
, p_item
, p_parent
,
84 i_pos
== PLAYLIST_END
? -1 : i_pos
);
85 playlist_SendAddNotify( p_playlist
, p_item
->i_id
,
86 p_parent
? p_parent
->i_id
: -1,
87 !( i_flags
& PLAYLIST_NO_REBUILD
));
89 p_item
->i_flags
|= i_flags
;
95 * Remove all the children of a node
97 * This function must be entered with the playlist lock
99 * \param p_playlist the playlist
100 * \param p_root the node
101 * \param b_delete_items do we have to delete the children items ?
102 * \return VLC_SUCCESS or an error
104 int playlist_NodeEmpty( playlist_t
*p_playlist
, playlist_item_t
*p_root
,
105 bool b_delete_items
)
109 if( p_root
->i_children
== -1 )
114 /* Delete the children */
115 for( i
= p_root
->i_children
-1 ; i
>= 0 ;i
-- )
117 if( p_root
->pp_children
[i
]->i_children
> -1 )
119 playlist_NodeDelete( p_playlist
, p_root
->pp_children
[i
],
120 b_delete_items
, false );
122 else if( b_delete_items
)
124 /* Delete the item here */
125 playlist_DeleteFromItemId( p_playlist
,
126 p_root
->pp_children
[i
]->i_id
);
133 * Remove all the children of a node and removes the node
135 * \param p_playlist the playlist
136 * \param p_root the node
137 * \param b_delete_items do we have to delete the children items ?
138 * \return VLC_SUCCESS or an error
140 int playlist_NodeDelete( playlist_t
*p_playlist
, playlist_item_t
*p_root
,
141 bool b_delete_items
, bool b_force
)
145 /* Delete the children */
146 for( int i
= p_root
->i_children
- 1 ; i
>= 0; i
-- )
147 if( b_delete_items
|| p_root
->pp_children
[i
]->i_children
>= 0 )
148 playlist_NodeDelete( p_playlist
, p_root
->pp_children
[i
],
149 b_delete_items
, b_force
);
151 /* Delete the node */
152 if( p_root
->i_flags
& PLAYLIST_RO_FLAG
&& !b_force
)
155 pl_priv(p_playlist
)->b_reset_currently_playing
= true;
158 var_SetInteger( p_playlist
, "playlist-item-deleted", p_root
->i_id
);
159 ARRAY_BSEARCH( p_playlist
->all_items
, ->i_id
, int, p_root
->i_id
, i
);
161 ARRAY_REMOVE( p_playlist
->all_items
, i
);
163 if( p_root
->i_children
== -1 ) {
164 ARRAY_BSEARCH( p_playlist
->items
,->i_id
, int, p_root
->i_id
, i
);
166 ARRAY_REMOVE( p_playlist
->items
, i
);
169 /* Check if it is the current item */
170 if( get_current_status_item( p_playlist
) == p_root
)
173 playlist_Control( p_playlist
, PLAYLIST_STOP
, pl_Locked
);
174 msg_Info( p_playlist
, "stopping playback" );
175 /* This item can't be the next one to be played ! */
176 set_current_status_item( p_playlist
, NULL
);
179 ARRAY_BSEARCH( p_playlist
->current
,->i_id
, int, p_root
->i_id
, i
);
181 ARRAY_REMOVE( p_playlist
->current
, i
);
183 PL_DEBUG( "deleting item `%s'", p_root
->p_input
->psz_name
);
185 /* Remove the item from its parent */
186 if( p_root
->p_parent
)
187 playlist_NodeRemoveItem( p_playlist
, p_root
, p_root
->p_parent
);
189 playlist_ItemRelease( p_root
);
195 * Adds an item to the children of a node
197 * \param p_playlist the playlist
198 * \param p_item the item to append
199 * \param p_parent the parent node
200 * \return VLC_SUCCESS or an error
202 int playlist_NodeAppend( playlist_t
*p_playlist
,
203 playlist_item_t
*p_item
,
204 playlist_item_t
*p_parent
)
206 return playlist_NodeInsert( p_playlist
, p_item
, p_parent
, -1 );
209 int playlist_NodeInsert( playlist_t
*p_playlist
,
210 playlist_item_t
*p_item
,
211 playlist_item_t
*p_parent
,
216 assert( p_parent
&& p_parent
->i_children
!= -1 );
217 if( i_position
== -1 ) i_position
= p_parent
->i_children
;
218 assert( i_position
<= p_parent
->i_children
);
220 INSERT_ELEM( p_parent
->pp_children
,
221 p_parent
->i_children
,
224 p_item
->p_parent
= p_parent
;
229 * Deletes an item from the children of a node
231 * \param p_playlist the playlist
232 * \param p_item the item to remove
233 * \param p_parent the parent node
234 * \return VLC_SUCCESS or an error
236 int playlist_NodeRemoveItem( playlist_t
*p_playlist
,
237 playlist_item_t
*p_item
,
238 playlist_item_t
*p_parent
)
243 int ret
= VLC_EGENERIC
;
245 for(int i
= 0; i
< p_parent
->i_children
; i
++ )
247 if( p_parent
->pp_children
[i
] == p_item
)
249 REMOVE_ELEM( p_parent
->pp_children
, p_parent
->i_children
, i
);
254 if( ret
== VLC_SUCCESS
) {
255 assert( p_item
->p_parent
== p_parent
);
256 p_item
->p_parent
= NULL
;
263 * Search a child of a node by its name
265 * \param p_node the node
266 * \param psz_search the name of the child to search
267 * \return the child item or NULL if not found or error
269 playlist_item_t
*playlist_ChildSearchName( playlist_item_t
*p_node
,
270 const char *psz_search
)
272 playlist_t
* p_playlist
= p_node
->p_playlist
; /* For assert_locked */
276 if( p_node
->i_children
< 0 )
280 for( i
= 0 ; i
< p_node
->i_children
; i
++ )
282 if( !strcmp( p_node
->pp_children
[i
]->p_input
->psz_name
, psz_search
) )
284 return p_node
->pp_children
[i
];
290 /**********************************************************************
291 * Tree walking functions
292 **********************************************************************/
294 * Finds the next item to play
296 * \param p_playlist the playlist
297 * \param p_root the root node
298 * \param p_item the previous item (NULL if none )
299 * \return the next item to play, or NULL if none found
301 playlist_item_t
*playlist_GetNextLeaf( playlist_t
*p_playlist
,
302 playlist_item_t
*p_root
,
303 playlist_item_t
*p_item
,
304 bool b_ena
, bool b_unplayed
)
307 playlist_item_t
*p_next
;
309 assert( p_root
&& p_root
->i_children
!= -1 );
311 PL_DEBUG2( "finding next of %s within %s",
312 PLI_NAME( p_item
), PLI_NAME( p_root
) );
314 /* Now, walk the tree until we find a suitable next item */
318 bool b_ena_ok
= true, b_unplayed_ok
= true;
319 p_next
= GetNextItem( p_playlist
, p_root
, p_next
);
320 if( !p_next
|| p_next
== p_root
)
322 if( p_next
->i_children
== -1 )
324 if( b_ena
&& p_next
->i_flags
& PLAYLIST_DBL_FLAG
)
326 if( b_unplayed
&& p_next
->p_input
->i_nb_played
!= 0 )
327 b_unplayed_ok
= false;
328 if( b_ena_ok
&& b_unplayed_ok
) break;
331 if( p_next
== NULL
) PL_DEBUG2( "at end of node" );
336 * Finds the previous item to play
338 * \param p_playlist the playlist
339 * \param p_root the root node
340 * \param p_item the previous item (NULL if none )
341 * \return the next item to play, or NULL if none found
343 playlist_item_t
*playlist_GetPrevLeaf( playlist_t
*p_playlist
,
344 playlist_item_t
*p_root
,
345 playlist_item_t
*p_item
,
346 bool b_ena
, bool b_unplayed
)
349 playlist_item_t
*p_prev
;
351 PL_DEBUG2( "finding previous of %s within %s", PLI_NAME( p_item
),
352 PLI_NAME( p_root
) );
353 assert( p_root
&& p_root
->i_children
!= -1 );
355 /* Now, walk the tree until we find a suitable previous item */
359 bool b_ena_ok
= true, b_unplayed_ok
= true;
360 p_prev
= GetPrevItem( p_playlist
, p_root
, p_prev
);
361 if( !p_prev
|| p_prev
== p_root
)
363 if( p_prev
->i_children
== -1 )
365 if( b_ena
&& p_prev
->i_flags
& PLAYLIST_DBL_FLAG
)
367 if( b_unplayed
&& p_prev
->p_input
->i_nb_played
!= 0 )
368 b_unplayed_ok
= false;
369 if( b_ena_ok
&& b_unplayed_ok
) break;
372 if( p_prev
== NULL
) PL_DEBUG2( "at beginning of node" );
376 /************************************************************************
377 * Following functions are local
378 ***********************************************************************/
381 * Get the next item in the tree
382 * If p_item is NULL, return the first child of root
384 playlist_item_t
*GetNextItem( playlist_t
*p_playlist
,
385 playlist_item_t
*p_root
,
386 playlist_item_t
*p_item
)
388 /* If the item is NULL, return the firt child of root */
391 if( p_root
->i_children
> 0 )
392 return p_root
->pp_children
[0];
397 /* Node with children, get the first one */
398 if( p_item
->i_children
> 0 )
399 return p_item
->pp_children
[0];
401 playlist_item_t
* p_parent
= p_item
->p_parent
;
402 for( int i
= 0 ; i
< p_parent
->i_children
; i
++ )
404 if( p_parent
->pp_children
[i
] == p_item
)
406 // Return the next children
407 if( i
+ 1 < p_parent
->i_children
)
408 return p_parent
->pp_children
[i
+1];
409 // We are the least one, so try to have uncles
412 PL_DEBUG2( "Current item is the last of the node,"
413 "looking for uncle from %s",
414 p_parent
->p_input
->psz_name
);
415 if( p_parent
== p_root
)
417 PL_DEBUG2( "already at root" );
421 return GetNextUncle( p_playlist
, p_item
, p_root
);
428 playlist_item_t
*GetNextUncle( playlist_t
*p_playlist
, playlist_item_t
*p_item
,
429 playlist_item_t
*p_root
)
431 playlist_item_t
*p_parent
= p_item
->p_parent
;
432 playlist_item_t
*p_grandparent
;
433 bool b_found
= false;
437 if( p_parent
!= NULL
)
439 p_grandparent
= p_parent
->p_parent
;
440 while( p_grandparent
)
443 for( i
= 0 ; i
< p_grandparent
->i_children
; i
++ )
445 if( p_parent
== p_grandparent
->pp_children
[i
] )
447 PL_DEBUG2( "parent %s found as child %i of grandparent %s",
448 p_parent
->p_input
->psz_name
, i
,
449 p_grandparent
->p_input
->psz_name
);
454 if( b_found
&& i
+ 1 < p_grandparent
->i_children
)
456 return p_grandparent
->pp_children
[i
+1];
458 /* Not found at root */
459 if( p_grandparent
== p_root
)
465 p_parent
= p_grandparent
;
466 p_grandparent
= p_parent
->p_parent
;
470 /* We reached root */
474 playlist_item_t
*GetPrevUncle( playlist_t
*p_playlist
, playlist_item_t
*p_item
,
475 playlist_item_t
*p_root
)
477 playlist_item_t
*p_parent
= p_item
->p_parent
;
478 playlist_item_t
*p_grandparent
;
479 bool b_found
= false;
483 if( p_parent
!= NULL
)
485 p_grandparent
= p_parent
->p_parent
;
489 for( i
= p_grandparent
->i_children
-1 ; i
>= 0; i
-- )
491 if( p_parent
== p_grandparent
->pp_children
[i
] )
497 if( b_found
&& i
- 1 > 0 )
499 return p_grandparent
->pp_children
[i
-1];
501 /* Not found at root */
502 if( p_grandparent
== p_root
)
508 p_parent
= p_grandparent
;
509 p_grandparent
= p_parent
->p_parent
;
513 /* We reached root */
518 /* Recursively search the tree for previous item */
519 playlist_item_t
*GetPrevItem( playlist_t
*p_playlist
,
520 playlist_item_t
*p_root
,
521 playlist_item_t
*p_item
)
523 playlist_item_t
*p_parent
;
526 /* Node with children, get the last one */
527 if( p_item
&& p_item
->i_children
> 0 )
528 return p_item
->pp_children
[p_item
->i_children
- 1];
530 /* Last child of its parent ? */
532 p_parent
= p_item
->p_parent
;
535 msg_Err( p_playlist
, "Get the last one" );
539 for( i
= p_parent
->i_children
-1 ; i
>= 0 ; i
-- )
541 if( p_parent
->pp_children
[i
] == p_item
)
545 /* Was already the first sibling. Look for uncles */
546 PL_DEBUG2( "current item is the first of its node,"
547 "looking for uncle from %s",
548 p_parent
->p_input
->psz_name
);
549 if( p_parent
== p_root
)
551 PL_DEBUG2( "already at root" );
554 return GetPrevUncle( p_playlist
, p_item
, p_root
);
558 return p_parent
->pp_children
[i
-1];