]> git.sesse.net Git - vlc/blob - src/playlist/tree.c
Fix bogus warnings
[vlc] / src / playlist / tree.c
1 /*****************************************************************************
2  * tree.c : Playlist tree walking functions
3  *****************************************************************************
4  * Copyright (C) 1999-2004 the VideoLAN team
5  * $Id$
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 #include <vlc/vlc.h>
24 #include <assert.h>
25 #include <vlc/input.h>
26 #include "vlc_playlist.h"
27 #include "playlist_internal.h"
28
29 /************************************************************************
30  * Local prototypes
31  ************************************************************************/
32 playlist_item_t *GetNextUncle( playlist_t *p_playlist, playlist_item_t *p_item,
33                                playlist_item_t *p_root );
34 playlist_item_t *GetPrevUncle( playlist_t *p_playlist, playlist_item_t *p_item,
35                                playlist_item_t *p_root );
36
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 /**
45  * Create a playlist node
46  *
47  * \param p_playlist the playlist
48  * \paam psz_name the name of the node
49  * \param p_parent the parent node to attach to or NULL if no attach
50  * \return the new node
51  */
52 playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist, char *psz_name,
53                                        playlist_item_t *p_parent )
54 {
55     input_item_t *p_input;
56     playlist_item_t *p_item;
57
58     if( !psz_name ) psz_name = _("Undefined");
59     p_input = input_ItemNewWithType( VLC_OBJECT(p_playlist), NULL, psz_name,
60                                      0, NULL, -1, ITEM_TYPE_NODE );
61     p_input->i_id = ++p_playlist->i_last_input_id;
62     p_item = playlist_ItemNewFromInput( VLC_OBJECT(p_playlist), p_input );
63     p_item->i_children = 0;
64
65     if( p_item == NULL )
66     {
67         return NULL;
68     }
69     INSERT_ELEM( p_playlist->pp_all_items,
70                  p_playlist->i_all_size,
71                  p_playlist->i_all_size,
72                  p_item );
73
74     if( p_parent != NULL )
75     {
76         playlist_NodeAppend( p_playlist, p_item, p_parent );
77     }
78
79     playlist_SendAddNotify( p_playlist, p_item->i_id,
80                             p_parent ? p_parent->i_id : -1 );
81     return p_item;
82 }
83
84 /**
85  * Remove all the children of a node
86  *
87  * This function must be entered with the playlist lock
88  *
89  * \param p_playlist the playlist
90  * \param p_root the node
91  * \param b_delete_items do we have to delete the children items ?
92  * \return VLC_SUCCESS or an error
93  */
94 int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
95                         vlc_bool_t b_delete_items )
96 {
97     int i;
98     if( p_root->i_children == -1 )
99     {
100         return VLC_EGENERIC;
101     }
102
103     /* Delete the children */
104     for( i =  p_root->i_children-1 ; i >= 0 ;i-- )
105     {
106         if( p_root->pp_children[i]->i_children > -1 )
107         {
108             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
109                                  b_delete_items , VLC_FALSE );
110         }
111         else if( b_delete_items )
112         {
113             /* Delete the item here */
114             playlist_DeleteFromItemId( p_playlist,
115                                        p_root->pp_children[i]->i_id );
116         }
117     }
118     return VLC_SUCCESS;
119 }
120
121 /**
122  * Remove all the children of a node and removes the node
123  *
124  * \param p_playlist the playlist
125  * \param p_root the node
126  * \param b_delete_items do we have to delete the children items ?
127  * \return VLC_SUCCESS or an error
128  */
129 int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
130                          vlc_bool_t b_delete_items, vlc_bool_t b_force )
131 {
132     int i;
133
134     if( p_root->i_children == -1 )
135     {
136         return VLC_EGENERIC;
137     }
138
139     /* Delete the children */
140     for( i =  p_root->i_children - 1 ; i >= 0; i-- )
141     {
142         if( p_root->pp_children[i]->i_children > -1 )
143         {
144             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
145                                  b_delete_items , b_force );
146         }
147         else if( b_delete_items )
148         {
149             playlist_DeleteFromItemId( p_playlist,
150                                        p_root->pp_children[i]->i_id );
151         }
152     }
153     /* Delete the node */
154     if( p_root->i_flags & PLAYLIST_RO_FLAG && !b_force )
155     {
156     }
157     else
158     {
159         var_SetInteger( p_playlist, "item-deleted", p_root->p_input->i_id );
160         for( i = 0 ; i< p_playlist->i_all_size; i ++ )
161         {
162             if( p_playlist->pp_all_items[i]->p_input->i_id ==
163                                p_root->p_input->i_id )
164             {
165                 REMOVE_ELEM( p_playlist->pp_all_items, p_playlist->i_all_size,
166                              i );
167                 break;
168             }
169         }
170
171         /* Remove the item from its parent */
172         if( p_root->p_parent )
173         {
174             playlist_NodeRemoveItem( p_playlist, p_root, p_root->p_parent );
175         }
176
177         playlist_ItemDelete( p_root );
178     }
179     return VLC_SUCCESS;
180 }
181
182
183 /**
184  * Adds an item to the children of a node
185  *
186  * \param p_playlist the playlist
187  * \param p_item the item to append
188  * \param p_parent the parent node
189  * \return VLC_SUCCESS or an error
190  */
191 int playlist_NodeAppend( playlist_t *p_playlist,
192                          playlist_item_t *p_item,
193                          playlist_item_t *p_parent )
194 {
195     return playlist_NodeInsert( p_playlist, p_item, p_parent, -1 );
196 }
197
198 int playlist_NodeInsert( playlist_t *p_playlist,
199                          playlist_item_t *p_item,
200                          playlist_item_t *p_parent,
201                          int i_position )
202 {
203    assert( p_parent && p_parent->i_children != -1 );
204    if( i_position == -1 ) i_position = p_parent->i_children ;
205
206    INSERT_ELEM( p_parent->pp_children,
207                 p_parent->i_children,
208                 i_position,
209                 p_item );
210    p_item->p_parent = p_parent;
211    return VLC_SUCCESS;
212 }
213
214 /**
215  * Deletes an item from the children of a node
216  *
217  * \param p_playlist the playlist
218  * \param p_item the item to remove
219  * \param p_parent the parent node
220  * \return VLC_SUCCESS or an error
221  */
222 int playlist_NodeRemoveItem( playlist_t *p_playlist,
223                         playlist_item_t *p_item,
224                         playlist_item_t *p_parent )
225 {
226    int i;
227    for( i= 0; i< p_parent->i_children ; i++ )
228    {
229        if( p_parent->pp_children[i] == p_item )
230        {
231            REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
232        }
233    }
234
235    return VLC_SUCCESS;
236 }
237
238
239 /**
240  * Count the children of a node
241  *
242  * \param p_playlist the playlist
243  * \param p_node the node
244  * \return the number of children
245  */
246 int playlist_NodeChildrenCount( playlist_t *p_playlist, playlist_item_t*p_node)
247 {
248     int i;
249     int i_nb = 0;
250
251     if( p_node->i_children == -1 )
252         return 0;
253
254     i_nb = p_node->i_children;
255     for( i=0 ; i< p_node->i_children;i++ )
256     {
257         if( p_node->pp_children[i]->i_children == -1 )
258             break;
259         else
260             i_nb += playlist_NodeChildrenCount( p_playlist,
261                                                 p_node->pp_children[i] );
262     }
263     return i_nb;
264 }
265
266 /**
267  * Search a child of a node by its name
268  *
269  * \param p_node the node
270  * \param psz_search the name of the child to search
271  * \return the child item or NULL if not found or error
272  */
273 playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
274                                            const char *psz_search )
275 {
276     int i;
277
278     if( p_node->i_children < 0 )
279     {
280          return NULL;
281     }
282     for( i = 0 ; i< p_node->i_children; i++ )
283     {
284         if( !strcmp( p_node->pp_children[i]->p_input->psz_name, psz_search ) )
285         {
286             return p_node->pp_children[i];
287         }
288     }
289     return NULL;
290 }
291
292 /**
293  * Create a pair of nodes in the category and onelevel trees.
294  * They share the same input ID.
295  * \todo really share the input item
296  * \param p_playlist the playlist
297  * \param psz_name the name of the nodes
298  * \param pp_node_cat pointer to return the node in category tree
299  * \param pp_node_one pointer to return the node in onelevel tree
300  * \param b_for_sd For Services Discovery ? (make node read-only and unskipping)
301  */
302 void playlist_NodesPairCreate( playlist_t *p_playlist, char *psz_name,
303                                playlist_item_t **pp_node_cat,
304                                playlist_item_t **pp_node_one,
305                                vlc_bool_t b_for_sd )
306 {
307     *pp_node_cat = playlist_NodeCreate( p_playlist, psz_name,
308                                         p_playlist->p_root_category );
309     *pp_node_one = playlist_NodeCreate( p_playlist, psz_name,
310                                         p_playlist->p_root_onelevel );
311     (*pp_node_one)->p_input->i_id = (*pp_node_cat)->p_input->i_id;
312     if( b_for_sd )
313     {
314         (*pp_node_cat)->i_flags |= PLAYLIST_RO_FLAG;
315         (*pp_node_cat)->i_flags |= PLAYLIST_SKIP_FLAG;
316         (*pp_node_one)->i_flags |= PLAYLIST_RO_FLAG;
317         (*pp_node_one)->i_flags |= PLAYLIST_SKIP_FLAG;
318     }
319 }
320
321 /**
322  * Get the node in the preferred tree from a node in one of category
323  * or onelevel tree. 
324  * For example, for the SAP node, it will return the node in the category
325  * tree if --playlist-tree is not set to never, because the SAP node prefers
326  * category
327  */
328 playlist_item_t * playlist_GetPreferredNode( playlist_t *p_playlist,
329                                              playlist_item_t *p_node )
330 {
331     int i;
332     if( p_node->p_parent == p_playlist->p_root_category )
333     {
334         if( p_playlist->b_always_tree ||
335             p_node->p_input->b_prefers_tree ) return p_node;
336         for( i = 0 ; i< p_playlist->p_root_onelevel->i_children; i++ )
337         {
338             if( p_playlist->p_root_onelevel->pp_children[i]->p_input->i_id ==
339                     p_node->p_input->i_id )
340                 return p_playlist->p_root_onelevel->pp_children[i];
341         }
342     }
343     else if( p_node->p_parent == p_playlist->p_root_onelevel )
344     {
345         if( p_playlist->b_never_tree || !p_node->p_input->b_prefers_tree )
346             return p_node;
347         for( i = 0 ; i< p_playlist->p_root_category->i_children; i++ )
348         {
349             if( p_playlist->p_root_category->pp_children[i]->p_input->i_id ==
350                     p_node->p_input->i_id )
351                 return p_playlist->p_root_category->pp_children[i];
352         }
353     }
354     return NULL;
355 }
356
357 /**********************************************************************
358  * Tree walking functions
359  **********************************************************************/
360
361 playlist_item_t *playlist_GetLastLeaf(playlist_t *p_playlist,
362                                       playlist_item_t *p_root )
363 {
364     int i;
365     playlist_item_t *p_item;
366     for ( i = p_root->i_children - 1; i >= 0; i-- )
367     {
368         if( p_root->pp_children[i]->i_children == -1 )
369             return p_root->pp_children[i];
370         else if( p_root->pp_children[i]->i_children > 0)
371         {
372              p_item = playlist_GetLastLeaf( p_playlist,
373                                             p_root->pp_children[i] );
374             if ( p_item != NULL )
375                 return p_item;
376         }
377         else if( i == 0 )
378             return NULL;
379     }
380     return NULL;
381 }
382
383 int playlist_GetAllEnabledChildren( playlist_t *p_playlist,
384                                     playlist_item_t *p_node,
385                                     playlist_item_t ***ppp_items )
386 {
387     int i_count = 0;
388     playlist_item_t *p_next = NULL;
389     while( 1 )
390     {
391         p_next = playlist_GetNextLeaf( p_playlist, p_node,
392                                        p_next, VLC_TRUE, VLC_FALSE );
393         if( p_next )
394             INSERT_ELEM( *ppp_items, i_count, i_count, p_next );
395         else
396             break;
397     }
398     return i_count;
399 }
400
401 /**
402  * Finds the next item to play
403  *
404  * \param p_playlist the playlist
405  * \param p_root the root node
406  * \param p_item the previous item  (NULL if none )
407  * \return the next item to play, or NULL if none found
408  */
409 playlist_item_t *playlist_GetNextLeaf( playlist_t *p_playlist,
410                                        playlist_item_t *p_root,
411                                        playlist_item_t *p_item,
412                                        vlc_bool_t b_ena, vlc_bool_t b_unplayed )
413 {
414     playlist_item_t *p_next;
415
416     assert( p_root && p_root->i_children != -1 );
417
418 #ifdef PLAYLIST_DEBUG
419     if( p_item != NULL )
420         msg_Dbg( p_playlist, "finding next of %s within %s",
421                         p_item->p_input->psz_name,  p_root->p_input->psz_name );
422     else
423         msg_Dbg( p_playlist, "finding something to play within %s",
424                          p_root->p_input->psz_name );
425 #endif
426
427     /* Now, walk the tree until we find a suitable next item */
428     p_next = p_item;
429     while( 1 )
430     {
431         vlc_bool_t b_ena_ok = VLC_TRUE, b_unplayed_ok = VLC_TRUE;
432         p_next = GetNextItem( p_playlist, p_root, p_next );
433         PL_DEBUG( "Got next item %s, testing suitability", PLI_NAME(p_next) );
434         if( !p_next || p_next == p_root )
435             break;
436         if( p_next->i_children == -1 )
437         {
438             if( b_ena && p_next->i_flags & PLAYLIST_DBL_FLAG )
439                 b_ena_ok = VLC_FALSE;
440             if( b_unplayed && p_next->p_input->i_nb_played != 0 )
441                 b_unplayed_ok = VLC_FALSE;
442             if( b_ena_ok && b_unplayed_ok ) break;
443         }
444     }
445 #ifdef PLAYLIST_DEBUG
446     if( p_next == NULL )
447         msg_Dbg( p_playlist, "At end of node" );
448 #endif
449     return p_next;
450 }
451
452 /**
453  * Finds the previous item to play
454  *
455  * \param p_playlist the playlist
456  * \param p_root the root node
457  * \param p_item the previous item  (NULL if none )
458  * \return the next item to play, or NULL if none found
459  */
460 playlist_item_t *playlist_GetPrevLeaf( playlist_t *p_playlist,
461                                        playlist_item_t *p_root,
462                                        playlist_item_t *p_item,
463                                        vlc_bool_t b_ena, vlc_bool_t b_unplayed )
464 {
465     playlist_item_t *p_prev;
466
467 #ifdef PLAYLIST_DEBUG
468     if( p_item != NULL )
469         msg_Dbg( p_playlist, "finding previous of %s within %s",
470                         p_item->p_input->psz_name,  p_root->p_input->psz_name );
471     else
472         msg_Dbg( p_playlist, "finding previous to play within %s",
473                          p_root->p_input->psz_name );
474 #endif
475     assert( p_root && p_root->i_children != -1 );
476
477     /* Now, walk the tree until we find a suitable previous item */
478     p_prev = p_item;
479     while( 1 )
480     {
481         vlc_bool_t b_ena_ok = VLC_TRUE, b_unplayed_ok = VLC_TRUE;
482         p_prev = GetPrevItem( p_playlist, p_root, p_prev );
483         if( !p_prev || p_prev == p_root )
484             break;
485         if( p_prev->i_children == -1 )
486         {
487             if( b_ena && p_prev->i_flags & PLAYLIST_DBL_FLAG )
488                 b_ena_ok = VLC_FALSE;
489             if( b_unplayed && p_prev->p_input->i_nb_played != 0 )
490                 b_unplayed_ok = VLC_FALSE;
491             if( b_ena_ok && b_unplayed_ok ) break;
492         }
493     }
494 #ifdef PLAYLIST_DEBUG
495     if( p_prev == NULL )
496         msg_Dbg( p_playlist, "At beginning of node" );
497 #endif
498     return p_prev;
499 }
500
501 /************************************************************************
502  * Following functions are local
503  ***********************************************************************/
504
505 /**
506  * Get the next item in the tree
507  * If p_item is NULL, return the first child of root
508  **/
509 playlist_item_t *GetNextItem( playlist_t *p_playlist,
510                               playlist_item_t *p_root,
511                               playlist_item_t *p_item )
512 {
513     playlist_item_t *p_parent;
514     int i;
515
516     /* Node with children, get the first one */
517     if( p_item && p_item->i_children > 0 )
518         return p_item->pp_children[0];
519
520     if( p_item != NULL )
521         p_parent = p_item->p_parent;
522     else
523         p_parent = p_root;
524     PL_DEBUG( "Parent %s has %i children", PLI_NAME(p_parent),
525                                            p_parent->i_children );
526     for( i= 0 ; i < p_parent->i_children ; i++ )
527     {
528         if( p_item == NULL || p_parent->pp_children[i] == p_item )
529         {
530             if( p_item == NULL )
531                 i = -1;
532
533             if( i+1 >= p_parent->i_children )
534             {
535                 /* Was already the last sibling. Look for uncles */
536                 PL_DEBUG( "Current item is the last of the node,"
537                           "looking for uncle from %s",
538                            p_parent->p_input->psz_name );
539
540                 if( p_parent == p_root )
541                 {
542                     PL_DEBUG( "already at root" );
543                     return NULL;
544                 }
545                 return GetNextUncle( p_playlist, p_item, p_root );
546             }
547             else
548             {
549                 return  p_parent->pp_children[i+1];
550             }
551         }
552     }
553     return NULL;
554 }
555
556 playlist_item_t *GetNextUncle( playlist_t *p_playlist, playlist_item_t *p_item,
557                                playlist_item_t *p_root )
558 {
559     playlist_item_t *p_parent = p_item->p_parent;
560     playlist_item_t *p_grandparent;
561     vlc_bool_t b_found = VLC_FALSE;
562
563     if( p_parent != NULL )
564     {
565         p_grandparent = p_parent->p_parent;
566         while( 1 )
567         {
568             int i;
569             for( i = 0 ; i< p_grandparent->i_children ; i++ )
570             {
571                 if( p_parent == p_grandparent->pp_children[i] )
572                 {
573                     PL_DEBUG( "parent %s found as child %i of grandparent %s",
574                               p_parent->p_input->psz_name, i,
575                               p_grandparent->p_input->psz_name );
576                     b_found = VLC_TRUE;
577                     break;
578                 }
579             }
580             if( b_found && i + 1 < p_grandparent->i_children )
581             {
582                     return p_grandparent->pp_children[i+1];
583             }
584             /* Not found at root */
585             if( p_grandparent == p_root )
586             {
587                 return NULL;
588             }
589             else
590             {
591                 p_parent = p_grandparent;
592                 p_grandparent = p_parent->p_parent;
593             }
594         }
595     }
596     /* We reached root */
597     return NULL;
598 }
599
600 playlist_item_t *GetPrevUncle( playlist_t *p_playlist, playlist_item_t *p_item,
601                                playlist_item_t *p_root )
602 {
603     playlist_item_t *p_parent = p_item->p_parent;
604     playlist_item_t *p_grandparent;
605     vlc_bool_t b_found = VLC_FALSE;
606
607     if( p_parent != NULL )
608     {
609         p_grandparent = p_parent->p_parent;
610         while( 1 )
611         {
612             int i;
613             for( i = p_grandparent->i_children -1 ; i >= 0; i-- )
614             {
615                 if( p_parent == p_grandparent->pp_children[i] )
616                 {
617                     b_found = VLC_TRUE;
618                     break;
619                 }
620             }
621             if( b_found && i - 1 > 0 )
622             {
623                 return p_grandparent->pp_children[i-1];
624             }
625             /* Not found at root */
626             if( p_grandparent == p_root )
627             {
628                 return NULL;
629             }
630             else
631             {
632                 p_parent = p_grandparent;
633                 p_grandparent = p_parent->p_parent;
634             }
635         }
636     }
637     /* We reached root */
638     return NULL;
639 }
640
641
642 /* Recursively search the tree for previous item */
643 playlist_item_t *GetPrevItem( playlist_t *p_playlist,
644                               playlist_item_t *p_root,
645                               playlist_item_t *p_item )
646 {
647     playlist_item_t *p_parent;
648     int i;
649
650     /* Node with children, get the last one */
651     if( p_item && p_item->i_children > 0 )
652         return p_item->pp_children[p_item->i_children - 1];
653
654     /* Last child of its parent ? */
655     if( p_item != NULL )
656         p_parent = p_item->p_parent;
657     else
658     {
659         msg_Err( p_playlist, "Get the last one" );
660         abort();
661     };
662
663     for( i = p_parent->i_children -1 ; i >= 0 ;  i-- )
664     {
665         if( p_parent->pp_children[i] == p_item )
666         {
667             if( i-1 < 0 )
668             {
669                /* Was already the first sibling. Look for uncles */
670                 PL_DEBUG( "current item is the first of its node,"
671                           "looking for uncle from %s",
672                           p_parent->p_input->psz_name );
673                 if( p_parent == p_root )
674                 {
675                     PL_DEBUG( "already at root" );
676                     return NULL;
677                 }
678                 return GetPrevUncle( p_playlist, p_item, p_root );
679             }
680             else
681             {
682                 return p_parent->pp_children[i-1];
683             }
684         }
685     }
686     return NULL;
687 }
688
689 /* Dump the contents of a node */
690 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
691                         int i_level )
692 {
693     char str[512];
694     int i;
695
696     if( i_level == 1 )
697     {
698         msg_Dbg( p_playlist, "%s (%i)",
699                         p_item->p_input->psz_name, p_item->i_children );
700     }
701
702     if( p_item->i_children == -1 )
703     {
704         return;
705     }
706
707     for( i = 0; i< p_item->i_children; i++ )
708     {
709         memset( str, 32, 512 );
710         sprintf( str + 2 * i_level , "%s (%i)",
711                                 p_item->pp_children[i]->p_input->psz_name,
712                                 p_item->pp_children[i]->i_children );
713         msg_Dbg( p_playlist, "%s",str );
714         if( p_item->pp_children[i]->i_children >= 0 )
715         {
716             playlist_NodeDump( p_playlist, p_item->pp_children[i],
717                               i_level + 1 );
718         }
719     }
720     return;
721 }