1 /*****************************************************************************
2 * tree.c : Playlist tree walking functions
3 *****************************************************************************
4 * Copyright (C) 1999-2007 the VideoLAN team
7 * Authors: Clément Stenac <zorglub@videolan.org>
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.
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.
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 *****************************************************************************/
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( VLC_OBJECT(p_playlist
), NULL
,
71 psz_name
, 0, NULL
, 0, -1, ITEM_TYPE_NODE
);
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
)
146 if( p_root
->i_children
== -1 )
151 /* Delete the children */
152 for( i
= p_root
->i_children
- 1 ; i
>= 0; i
-- )
154 if( p_root
->pp_children
[i
]->i_children
> -1 )
156 playlist_NodeDelete( p_playlist
, p_root
->pp_children
[i
],
157 b_delete_items
, b_force
);
159 else if( b_delete_items
)
161 playlist_DeleteItem( p_playlist
, p_root
->pp_children
[i
], true );
164 /* Delete the node */
165 if( p_root
->i_flags
& PLAYLIST_RO_FLAG
&& !b_force
)
171 var_SetInteger( p_playlist
, "playlist-item-deleted", p_root
->i_id
);
172 ARRAY_BSEARCH( p_playlist
->all_items
, ->i_id
, int,
175 ARRAY_REMOVE( p_playlist
->all_items
, i
);
177 /* Remove the item from its parent */
178 if( p_root
->p_parent
)
179 playlist_NodeRemoveItem( p_playlist
, p_root
, p_root
->p_parent
);
181 playlist_ItemRelease( p_root
);
188 * Adds an item to the children of a node
190 * \param p_playlist the playlist
191 * \param p_item the item to append
192 * \param p_parent the parent node
193 * \return VLC_SUCCESS or an error
195 int playlist_NodeAppend( playlist_t
*p_playlist
,
196 playlist_item_t
*p_item
,
197 playlist_item_t
*p_parent
)
199 return playlist_NodeInsert( p_playlist
, p_item
, p_parent
, -1 );
202 int playlist_NodeInsert( playlist_t
*p_playlist
,
203 playlist_item_t
*p_item
,
204 playlist_item_t
*p_parent
,
209 assert( p_parent
&& p_parent
->i_children
!= -1 );
210 if( i_position
== -1 ) i_position
= p_parent
->i_children
;
212 INSERT_ELEM( p_parent
->pp_children
,
213 p_parent
->i_children
,
216 p_item
->p_parent
= p_parent
;
221 * Deletes an item from the children of a node
223 * \param p_playlist the playlist
224 * \param p_item the item to remove
225 * \param p_parent the parent node
226 * \return VLC_SUCCESS or an error
228 int playlist_NodeRemoveItem( playlist_t
*p_playlist
,
229 playlist_item_t
*p_item
,
230 playlist_item_t
*p_parent
)
235 for(int i
= 0; i
< p_parent
->i_children
; i
++ )
237 if( p_parent
->pp_children
[i
] == p_item
)
239 REMOVE_ELEM( p_parent
->pp_children
, p_parent
->i_children
, i
);
247 * Search a child of a node by its name
249 * \param p_node the node
250 * \param psz_search the name of the child to search
251 * \return the child item or NULL if not found or error
253 playlist_item_t
*playlist_ChildSearchName( playlist_item_t
*p_node
,
254 const char *psz_search
)
256 playlist_t
* p_playlist
= p_node
->p_playlist
; /* For assert_locked */
260 if( p_node
->i_children
< 0 )
264 for( i
= 0 ; i
< p_node
->i_children
; i
++ )
266 if( !strcmp( p_node
->pp_children
[i
]->p_input
->psz_name
, psz_search
) )
268 return p_node
->pp_children
[i
];
274 /**********************************************************************
275 * Tree walking functions
276 **********************************************************************/
278 * Finds the next item to play
280 * \param p_playlist the playlist
281 * \param p_root the root node
282 * \param p_item the previous item (NULL if none )
283 * \return the next item to play, or NULL if none found
285 playlist_item_t
*playlist_GetNextLeaf( playlist_t
*p_playlist
,
286 playlist_item_t
*p_root
,
287 playlist_item_t
*p_item
,
288 bool b_ena
, bool b_unplayed
)
291 playlist_item_t
*p_next
;
293 assert( p_root
&& p_root
->i_children
!= -1 );
295 PL_DEBUG2( "finding next of %s within %s",
296 PLI_NAME( p_item
), PLI_NAME( p_root
) );
298 /* Now, walk the tree until we find a suitable next item */
302 bool b_ena_ok
= true, b_unplayed_ok
= true;
303 p_next
= GetNextItem( p_playlist
, p_root
, p_next
);
304 if( !p_next
|| p_next
== p_root
)
306 if( p_next
->i_children
== -1 )
308 if( b_ena
&& p_next
->i_flags
& PLAYLIST_DBL_FLAG
)
310 if( b_unplayed
&& p_next
->p_input
->i_nb_played
!= 0 )
311 b_unplayed_ok
= false;
312 if( b_ena_ok
&& b_unplayed_ok
) break;
315 if( p_next
== NULL
) PL_DEBUG2( "at end of node" );
320 * Finds the previous item to play
322 * \param p_playlist the playlist
323 * \param p_root the root node
324 * \param p_item the previous item (NULL if none )
325 * \return the next item to play, or NULL if none found
327 playlist_item_t
*playlist_GetPrevLeaf( playlist_t
*p_playlist
,
328 playlist_item_t
*p_root
,
329 playlist_item_t
*p_item
,
330 bool b_ena
, bool b_unplayed
)
333 playlist_item_t
*p_prev
;
335 PL_DEBUG2( "finding previous of %s within %s", PLI_NAME( p_item
),
336 PLI_NAME( p_root
) );
337 assert( p_root
&& p_root
->i_children
!= -1 );
339 /* Now, walk the tree until we find a suitable previous item */
343 bool b_ena_ok
= true, b_unplayed_ok
= true;
344 p_prev
= GetPrevItem( p_playlist
, p_root
, p_prev
);
345 if( !p_prev
|| p_prev
== p_root
)
347 if( p_prev
->i_children
== -1 )
349 if( b_ena
&& p_prev
->i_flags
& PLAYLIST_DBL_FLAG
)
351 if( b_unplayed
&& p_prev
->p_input
->i_nb_played
!= 0 )
352 b_unplayed_ok
= false;
353 if( b_ena_ok
&& b_unplayed_ok
) break;
356 if( p_prev
== NULL
) PL_DEBUG2( "at beginning of node" );
360 /************************************************************************
361 * Following functions are local
362 ***********************************************************************/
365 * Get the next item in the tree
366 * If p_item is NULL, return the first child of root
368 playlist_item_t
*GetNextItem( playlist_t
*p_playlist
,
369 playlist_item_t
*p_root
,
370 playlist_item_t
*p_item
)
372 /* If the item is NULL, return the firt child of root */
375 if( p_root
->i_children
> 0 )
376 return p_root
->pp_children
[0];
381 /* Node with children, get the first one */
382 if( p_item
->i_children
> 0 )
383 return p_item
->pp_children
[0];
385 playlist_item_t
* p_parent
= p_item
->p_parent
;
386 for( int i
= 0 ; i
< p_parent
->i_children
; i
++ )
388 if( p_parent
->pp_children
[i
] == p_item
)
390 // Return the next children
391 if( i
+ 1 < p_parent
->i_children
)
392 return p_parent
->pp_children
[i
+1];
393 // We are the least one, so try to have uncles
396 PL_DEBUG2( "Current item is the last of the node,"
397 "looking for uncle from %s",
398 p_parent
->p_input
->psz_name
);
399 if( p_parent
== p_root
)
401 PL_DEBUG2( "already at root" );
405 return GetNextUncle( p_playlist
, p_item
, p_root
);
412 playlist_item_t
*GetNextUncle( playlist_t
*p_playlist
, playlist_item_t
*p_item
,
413 playlist_item_t
*p_root
)
415 playlist_item_t
*p_parent
= p_item
->p_parent
;
416 playlist_item_t
*p_grandparent
;
417 bool b_found
= false;
421 if( p_parent
!= NULL
)
423 p_grandparent
= p_parent
->p_parent
;
424 while( p_grandparent
)
427 for( i
= 0 ; i
< p_grandparent
->i_children
; i
++ )
429 if( p_parent
== p_grandparent
->pp_children
[i
] )
431 PL_DEBUG2( "parent %s found as child %i of grandparent %s",
432 p_parent
->p_input
->psz_name
, i
,
433 p_grandparent
->p_input
->psz_name
);
438 if( b_found
&& i
+ 1 < p_grandparent
->i_children
)
440 return p_grandparent
->pp_children
[i
+1];
442 /* Not found at root */
443 if( p_grandparent
== p_root
)
449 p_parent
= p_grandparent
;
450 p_grandparent
= p_parent
->p_parent
;
454 /* We reached root */
458 playlist_item_t
*GetPrevUncle( playlist_t
*p_playlist
, playlist_item_t
*p_item
,
459 playlist_item_t
*p_root
)
461 playlist_item_t
*p_parent
= p_item
->p_parent
;
462 playlist_item_t
*p_grandparent
;
463 bool b_found
= false;
467 if( p_parent
!= NULL
)
469 p_grandparent
= p_parent
->p_parent
;
473 for( i
= p_grandparent
->i_children
-1 ; i
>= 0; i
-- )
475 if( p_parent
== p_grandparent
->pp_children
[i
] )
481 if( b_found
&& i
- 1 > 0 )
483 return p_grandparent
->pp_children
[i
-1];
485 /* Not found at root */
486 if( p_grandparent
== p_root
)
492 p_parent
= p_grandparent
;
493 p_grandparent
= p_parent
->p_parent
;
497 /* We reached root */
502 /* Recursively search the tree for previous item */
503 playlist_item_t
*GetPrevItem( playlist_t
*p_playlist
,
504 playlist_item_t
*p_root
,
505 playlist_item_t
*p_item
)
507 playlist_item_t
*p_parent
;
510 /* Node with children, get the last one */
511 if( p_item
&& p_item
->i_children
> 0 )
512 return p_item
->pp_children
[p_item
->i_children
- 1];
514 /* Last child of its parent ? */
516 p_parent
= p_item
->p_parent
;
519 msg_Err( p_playlist
, "Get the last one" );
523 for( i
= p_parent
->i_children
-1 ; i
>= 0 ; i
-- )
525 if( p_parent
->pp_children
[i
] == p_item
)
529 /* Was already the first sibling. Look for uncles */
530 PL_DEBUG2( "current item is the first of its node,"
531 "looking for uncle from %s",
532 p_parent
->p_input
->psz_name
);
533 if( p_parent
== p_root
)
535 PL_DEBUG2( "already at root" );
538 return GetPrevUncle( p_playlist
, p_item
, p_root
);
542 return p_parent
->pp_children
[i
-1];