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