]> git.sesse.net Git - vlc/blob - src/playlist/view.c
hopefully fix SAP crash
[vlc] / src / playlist / view.c
1 /*****************************************************************************
2  * view.c : Playlist views functions
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VideoLAN
5  * $Id: item.c 7997 2004-06-18 11:35:45Z sigmunau $
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23 #include <stdlib.h>                                      /* free(), strtol() */
24 #include <stdio.h>                                              /* sprintf() */
25 #include <string.h>                                            /* strerror() */
26
27 #include <vlc/vlc.h>
28 #include <vlc/input.h>
29
30 #include "vlc_playlist.h"
31
32 #undef PLAYLIST_DEBUG
33
34 /************************************************************************
35  * Local prototypes
36  ************************************************************************/
37
38 /* TODO: inline */
39 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
40                                         playlist_item_t *, int );
41
42 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
43                 int i_view,
44                 playlist_item_t *p_root,
45                 playlist_item_t *p_item,
46                 playlist_item_t *p_parent );
47
48 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
49                 int i_view,
50                 playlist_item_t *p_root,
51                 playlist_item_t *p_item,
52                 playlist_item_t *p_parent );
53
54 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
55                         int i_level );
56
57 /**********************************************************************
58  * Exported View management functions
59  **********************************************************************/
60
61 /**
62  * Create a new view
63  *
64  * \param p_playlist a playlist object
65  * \param i_id the view identifier
66  * \return the new view or NULL on failure
67  */
68 playlist_view_t * playlist_ViewCreate( playlist_t *p_playlist, int i_id,
69                                        char *psz_name )
70 {
71     playlist_view_t * p_view;
72
73     msg_Dbg( p_playlist, "creating view %i",i_id );
74
75     p_view = malloc( sizeof( playlist_view_t ) );
76
77     memset( p_view, 0, sizeof( playlist_view_t ) );
78
79     p_view->p_root = playlist_NodeCreate( p_playlist, i_id, NULL, NULL );
80     p_view->i_id = i_id;
81     p_view->psz_name = psz_name ? strdup( psz_name ) : strdup(_("Undefined") );
82
83     return p_view;
84 }
85
86 /**
87  * Creates a new view and add it to the list
88  *
89  * \param p_playlist a playlist object
90  * \param i_id the view identifier
91  * \return VLC_SUCCESS or an error
92  */
93 int playlist_ViewInsert( playlist_t *p_playlist, int i_id, char *psz_name )
94 {
95     playlist_view_t *p_view =
96         playlist_ViewCreate( p_playlist, i_id , psz_name );
97     if( !p_view )
98     {
99         msg_Err( p_playlist, "Creation failed" );
100         return VLC_EGENERIC;
101     }
102
103     vlc_mutex_lock( &p_playlist->object_lock );
104
105     INSERT_ELEM( p_playlist->pp_views, p_playlist->i_views,
106                  p_playlist->i_views, p_view );
107
108     vlc_mutex_unlock( &p_playlist->object_lock );
109     return VLC_SUCCESS;
110 }
111
112
113 /**
114  * Deletes a view
115  *
116  * \param p_view the view to delete
117  * \return nothing
118  */
119 int playlist_ViewDelete( playlist_t *p_playlist,playlist_view_t *p_view )
120 {
121     REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, 0 );
122     return VLC_SUCCESS;
123 }
124
125
126 /**
127  * Dumps the content of a view
128  *
129  * \param p_playlist the playlist
130  * \param p_view the view to dump
131  * \return nothing
132  */
133 int playlist_ViewDump( playlist_t *p_playlist, playlist_view_t *p_view )
134 {
135     msg_Dbg( p_playlist, "dumping view %i",p_view->i_id );
136     playlist_NodeDump( p_playlist,p_view->p_root, 1 );
137     return VLC_SUCCESS;
138 }
139
140 /**
141  * Counts the items of a view
142  *
143  * \param p_playlist the playlist
144  * \param p_view the view to count
145  * \return the number of items
146  */
147 int playlist_ViewItemCount( playlist_t *p_playlist,
148                             playlist_view_t *p_view )
149 {
150     return playlist_NodeChildrenCount( p_playlist, p_view->p_root );
151 }
152
153
154 /**
155  * Updates a view. Only make sense for "sorted" and "ALL" views
156  *
157  * \param p_playlist the playlist
158  * \param i_view the view to update
159  * \return nothing
160  */
161 int playlist_ViewUpdate( playlist_t *p_playlist, int i_view)
162 {
163     playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_view );
164
165     if( p_view == NULL )
166     {
167         return VLC_EGENERIC;
168     }
169
170     if( i_view == VIEW_ALL )
171     {
172         p_view->p_root->i_children = p_playlist->i_size;
173         p_view->p_root->pp_children = p_playlist->pp_items;
174     }
175
176     /* Handle update of sorted views here */
177     if( i_view == VIEW_S_AUTHOR )
178     {
179         playlist_ViewEmpty( p_playlist, i_view, VLC_FALSE );
180
181         playlist_NodeGroup( p_playlist, i_view, p_view->p_root,
182                             p_playlist->pp_items,p_playlist->i_size,
183                             SORT_AUTHOR, ORDER_NORMAL );
184     }
185
186     return VLC_SUCCESS;
187 }
188
189
190 /**
191  * Find a view
192  *
193  * \param p_playlist the playlist
194  * \param i_id the id to find
195  * \return the found view or NULL if not found
196  */
197 playlist_view_t *playlist_ViewFind( playlist_t *p_playlist, int i_id )
198 {
199     int i;
200     for( i=0 ; i< p_playlist->i_views ; i++ )
201     {
202         if( p_playlist->pp_views[i]->i_id == i_id )
203         {
204             return p_playlist->pp_views[i];
205         }
206     }
207     return NULL;
208 }
209
210
211 int playlist_ViewEmpty( playlist_t *p_playlist, int i_view,
212                         vlc_bool_t b_delete_items )
213 {
214     playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_view );
215
216     if( p_view == NULL )
217     {
218         return VLC_EGENERIC;
219     }
220
221     return playlist_NodeEmpty( p_playlist, p_view->p_root, b_delete_items );
222 }
223
224 /**********************************************************************
225  * Exported Nodes management functions
226  **********************************************************************/
227
228
229
230 /**
231  * Create a playlist node
232  *
233  * \param p_playlist the playlist
234  * \paam psz_name the name of the node
235  * \param p_parent the parent node to attach to or NULL if no attach
236  * \return the new node
237  */
238 playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist, int i_view,
239                                        char *psz_name,
240                                        playlist_item_t *p_parent )
241 {
242     /* Create the item */
243     playlist_item_t *p_item = (playlist_item_t *)malloc(
244                                         sizeof( playlist_item_t ) );
245     vlc_value_t val;
246     playlist_add_t *p_add = (playlist_add_t*)malloc( sizeof(playlist_add_t));
247
248     vlc_input_item_Init( VLC_OBJECT(p_playlist), &p_item->input );
249     if( p_item == NULL )
250     {
251         return NULL;
252     }
253
254     if( psz_name != NULL )
255     {
256         p_item->input.psz_name = strdup( psz_name );
257     }
258     else
259     {
260         p_item->input.psz_name = strdup( _("Undefined") );
261     }
262
263     p_item->input.psz_uri = NULL;
264
265     p_item->b_enabled = VLC_TRUE;
266     p_item->i_nb_played = 0;
267
268     p_item->i_flags = 0;
269
270     p_item->i_children = 0;
271     p_item->pp_children = NULL;
272
273     p_item->input.i_duration = -1;
274     p_item->input.ppsz_options = NULL;
275     p_item->input.i_options = 0;
276     p_item->input.i_categories = 0;
277     p_item->input.pp_categories = NULL;
278
279     p_item->pp_parents = NULL;
280     p_item->i_parents = 0;
281
282     p_item->i_flags |= PLAYLIST_SKIP_FLAG; /* Default behaviour */
283
284     vlc_mutex_init( p_playlist, &p_item->input.lock );
285
286     if( p_parent != NULL )
287     {
288         playlist_NodeAppend( p_playlist, i_view, p_item, p_parent );
289     }
290
291     p_add->p_node = p_parent;
292     p_add->p_item = p_item;
293     p_add->i_view = i_view;
294     val.p_address = p_add;
295     var_Set( p_playlist, "item-append", val);
296
297     return p_item;
298 }
299
300 /**
301  * Remove all the children of a node
302  *
303  * \param p_playlist the playlist
304  * \param p_root the node
305  * \param b_delete_items do we have to delete the children items ?
306  * \return VLC_SUCCESS or an error
307  */
308 int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
309                         vlc_bool_t b_delete_items )
310 {
311     int i;
312     if( p_root->i_children == -1 )
313     {
314         return VLC_EGENERIC;
315     }
316
317     /* Delete the children */
318     for( i =  p_root->i_children-1 ; i >= 0 ;i-- )
319     {
320         if( p_root->pp_children[i]->i_children > -1 )
321         {
322             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
323                                 b_delete_items );
324         }
325         else if( b_delete_items )
326         {
327             /* Delete the item here */
328             playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
329         }
330     }
331     return VLC_SUCCESS;
332 }
333
334 /**
335  * Remove all the children of a node and removes the node
336  *
337  * \param p_playlist the playlist
338  * \param p_root the node
339  * \param b_delete_items do we have to delete the children items ?
340  * \return VLC_SUCCESS or an error
341  */
342 int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
343                         vlc_bool_t b_delete_items )
344 {
345     int i;
346     if( p_root->i_children == -1 )
347     {
348         return VLC_EGENERIC;
349     }
350
351     /* Delete the children */
352     for( i =  p_root->i_children - 1 ; i >= 0; i-- )
353     {
354         if( p_root->pp_children[i]->i_children > -1 )
355         {
356             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
357                                 b_delete_items );
358         }
359         else if( b_delete_items )
360         {
361             /* Delete the item here */
362         }
363     }
364     /* Delete the node */
365     for( i = 0 ; i< p_root->i_parents; i++ )
366     {
367         playlist_NodeRemoveItem( p_playlist, p_root,
368                                  p_root->pp_parents[i]->p_parent );
369     }
370     playlist_ItemDelete( p_root );
371    return VLC_SUCCESS;
372 }
373
374
375
376 /**
377  * Adds an item to the childs of a node
378  *
379  * \param p_playlist the playlist
380  * \param i_view the view of the node ( needed for parent search )
381  * \param p_item the item to append
382  * \param p_parent the parent node
383  * \return VLC_SUCCESS or an error
384  */
385 int playlist_NodeAppend( playlist_t *p_playlist,
386                          int i_view,
387                          playlist_item_t *p_item,
388                          playlist_item_t *p_parent )
389 {
390     return playlist_NodeInsert( p_playlist, i_view, p_item, p_parent, -1 );
391 }
392
393 int playlist_NodeInsert( playlist_t *p_playlist,
394                          int i_view,
395                          playlist_item_t *p_item,
396                          playlist_item_t *p_parent,
397                          int i_position )
398 {
399    int i;
400    vlc_bool_t b_found = VLC_FALSE;
401    if( !p_parent || p_parent->i_children == -1 )
402    {
403         msg_Err( p_playlist, "invalid node" );
404         return VLC_EGENERIC;
405    }
406
407    if( i_position == -1 ) i_position = p_parent->i_children ;
408
409    INSERT_ELEM( p_parent->pp_children,
410                 p_parent->i_children,
411                 i_position,
412                 p_item );
413
414    /* Add the parent to the array */
415    for( i= 0; i< p_item->i_parents ; i++ )
416    {
417        if( p_item->pp_parents[i]->i_view == i_view )
418        {
419            b_found = VLC_TRUE;
420            break;
421        }
422    }
423    if( b_found == VLC_FALSE )
424    {
425         struct item_parent_t *p_ip = (struct item_parent_t *)
426                                 malloc(sizeof(struct item_parent_t) );
427         p_ip->i_view = i_view;
428         p_ip->p_parent = p_parent;
429
430         INSERT_ELEM( p_item->pp_parents,
431                      p_item->i_parents, p_item->i_parents,
432                      p_ip );
433    }
434
435    /* Let the interface know this has been updated */
436    p_parent->i_serial++;
437    return VLC_SUCCESS;
438 }
439
440 /**
441  * Deletes an item from the children of a node
442  *
443  * \param p_playlist the playlist
444  * \param p_item the item to remove
445  * \param p_parent the parent node
446  * \return VLC_SUCCESS or an error
447  */
448 int playlist_NodeRemoveItem( playlist_t *p_playlist,
449                         playlist_item_t *p_item,
450                         playlist_item_t *p_parent )
451 {
452    int i;
453    if( !p_parent || p_parent->i_children == -1 )
454    {
455         msg_Err( p_playlist, "invalid node" );
456    }
457
458    for( i= 0; i< p_parent->i_children ; i++ )
459    {
460        if( p_parent->pp_children[i] == p_item )
461        {
462            REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
463        }
464    }
465
466    /* Let the interface know this has been updated */
467    p_parent->i_serial++;
468
469    return VLC_SUCCESS;
470 }
471
472
473 /**
474  * Count the children of a node
475  *
476  * \param p_playlist the playlist
477  * \param p_node the node
478  * \return the number of children
479  */
480 int playlist_NodeChildrenCount( playlist_t *p_playlist, playlist_item_t*p_node)
481 {
482     int i;
483     int i_nb = 0;
484     if( p_node->i_children == -1 )
485     {
486         return 0;
487     }
488
489     for( i=0 ; i< p_node->i_children;i++ )
490     {
491         if( p_node->pp_children[i]->i_children == -1 )
492         {
493             i_nb++;
494         }
495         else
496         {
497             i_nb += playlist_NodeChildrenCount( p_playlist, 
498                                                 p_node->pp_children[i] );
499         }
500     }
501     return i_nb;
502 }
503
504 /**
505  * Search a child of a node by its name
506  *
507  * \param p_node the node
508  * \param psz_search the name of the child to search
509  * \return the child item or NULL if not found or error
510  */
511 playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
512                                            const char *psz_search )
513 {
514     int i;
515
516     if( p_node->i_children < 0 )
517     {
518          return NULL;
519     }
520     for( i = 0 ; i< p_node->i_children; i++ )
521     {
522         if( !strcmp( p_node->pp_children[i]->input.psz_name, psz_search ) )
523         {
524             return p_node->pp_children[i];
525         }
526     }
527     return NULL;
528 }
529
530
531 /**********************************************************************
532  * Tree functions
533  **********************************************************************/
534
535 /**
536  * Finds the next item to play
537  *
538  * \param p_playlist the playlist
539  * \param i_view the view
540  * \param p_root the root node
541  * \param p_node the node we are playing from
542  * \param p_item the item we were playing (NULL if none )
543  * \return the next item to play, or NULL if none found
544  */
545 playlist_item_t *playlist_FindNextFromParent( playlist_t *p_playlist,
546                                         int i_view, /* FIXME: useless */
547                                         playlist_item_t *p_root,
548                                         playlist_item_t *p_node,
549                                         playlist_item_t *p_item )
550 {
551     playlist_item_t *p_search, *p_next;
552
553 #ifdef PLAYLIST_DEBUG
554     if( p_item != NULL )
555     {
556         msg_Dbg( p_playlist, "finding next of %s within %s",
557                         p_item->input.psz_name, p_node->input.psz_name );
558     }
559     else
560     {
561         msg_Dbg( p_playlist, "finding something to play within %s",
562                                  p_node->input.psz_name );
563
564     }
565 #endif
566
567     if( !p_node  || p_node->i_children == -1 )
568     {
569         msg_Err( p_playlist,"invalid arguments for FindNextFromParent" );
570         return NULL;
571     }
572
573     /* Find the parent node of the item */
574     if( p_item != NULL )
575     {
576         p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
577         if( p_search == NULL )
578         {
579             msg_Err( p_playlist, "parent node not found" );
580             return NULL;
581         }
582     }
583     else
584     {
585         p_search = p_node;
586     }
587
588     /* Now, go up the tree until we find a suitable next item */
589     p_next = playlist_RecursiveFindNext( p_playlist,i_view,
590                                          p_node, p_item, p_search );
591
592     /* Not found, do we go past p_node ? */
593     if( p_next == NULL )
594     {
595         if( p_playlist->b_go_next )
596         {
597             p_next = playlist_RecursiveFindNext( p_playlist, i_view,
598                                 p_root, p_item, p_search );
599             if( p_next == NULL )
600             {
601                 return NULL;
602             }
603             /* OK, we could continue, so set our current node to the root */
604             p_playlist->status.p_node = p_root;
605         }
606         else
607         {
608             return NULL;
609         }
610     }
611     return p_next;
612 }
613
614 /**
615  * Finds the previous item to play
616  *
617  * \param p_playlist the playlist
618  * \param i_view the view
619  * \param p_root the root node
620  * \param p_node the node we are playing from
621  * \param p_item the item we were playing (NULL if none )
622  * \return the next item to play, or NULL if none found
623  */
624 playlist_item_t *playlist_FindPrevFromParent( playlist_t *p_playlist,
625                                         int i_view,
626                                         playlist_item_t *p_root,
627                                         playlist_item_t *p_node,
628                                         playlist_item_t *p_item )
629 {
630     playlist_item_t *p_search, *p_next;
631
632 #ifdef PLAYLIST_DEBUG
633     if( p_item != NULL )
634     {
635         msg_Dbg( p_playlist, "Finding prev of %s within %s",
636                         p_item->input.psz_name, p_node->input.psz_name );
637     }
638     else
639     {
640         msg_Dbg( p_playlist, "Finding prev from %s",p_node->input.psz_name );
641     }
642 #endif
643
644     if( !p_node  || p_node->i_children == -1 )
645     {
646         msg_Err( p_playlist,"invalid arguments for FindPrevFromParent" );
647         return NULL;
648     }
649
650     /* Find the parent node of the item */
651     if( p_item != NULL )
652     {
653         p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
654         if( p_search == NULL )
655         {
656             msg_Err( p_playlist, "parent node not found" );
657             return NULL;
658         }
659     }
660     else
661     {
662         p_search = p_node;
663     }
664
665     /* Now, go up the tree until we find a suitable next item */
666     p_next = playlist_RecursiveFindPrev( p_playlist,i_view,
667                                          p_node, p_item, p_search );
668
669     if( p_next == NULL )
670     {
671         if( p_playlist->b_go_next )
672         {
673             p_next = playlist_RecursiveFindPrev( p_playlist, i_view,
674                                 p_root, p_item, p_search );
675             if( p_next == NULL )
676             {
677                 return NULL;
678             }
679             /* OK, we could continue, so set our current node to the root */
680             p_playlist->status.p_node = p_root;
681         }
682         else
683         {
684             return NULL;
685         }
686     }
687     return p_next;
688 }
689
690 /************************************************************************
691  * Following functions are local
692  ***********************************************************************/
693
694
695 /* Recursively search the tree for next item */
696 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
697                 int i_view,
698                 playlist_item_t *p_root,
699                 playlist_item_t *p_item,
700                 playlist_item_t *p_parent )
701 {
702     int i;
703     playlist_item_t *p_parent_parent;
704
705     for( i= 0 ; i < p_parent->i_children ; i++ )
706     {
707         if( p_parent->pp_children[i] == p_item || p_item == NULL )
708         {
709             if( p_item == NULL )
710             {
711                 i = -1;
712             }
713 #ifdef PLAYLIST_DEBUG
714             msg_Dbg( p_playlist,"Current item found, child %i of %s",
715                                 i , p_parent->input.psz_name );
716 #endif
717             /* We found our item */
718             if( i+1 >= p_parent->i_children )
719             {
720                 /* Too far... */
721 #ifdef PLAYLIST_DEBUG
722                 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
723                                 p_parent->input.psz_name );
724 #endif
725                 if( p_parent == p_root )
726                 {
727                     /* Hmm, seems it's the end for you, guy ! */
728                     return NULL;
729                 }
730
731                 /* Go up one level */
732                 p_parent_parent = playlist_FindDirectParent( p_playlist,
733                                                              p_parent, i_view );
734                 if( p_parent_parent == NULL )
735                 {
736                     msg_Warn( p_playlist, "Unable to find parent !");
737                     return NULL;
738                 }
739                 return playlist_RecursiveFindNext( p_playlist, i_view,p_root,
740                                                    p_parent, p_parent_parent );
741             }
742             else
743             {
744                 if( p_parent->pp_children[i+1]->i_children == -1 )
745                 {
746                     /* Cool, we have found a real item to play */
747 #ifdef PLAYLIST_DEBUG
748                     msg_Dbg( p_playlist, "Playing child %i of %s",
749                                      i+1 , p_parent->input.psz_name );
750 #endif
751                     return p_parent->pp_children[i+1];
752                 }
753                 else if( p_parent->pp_children[i+1]->i_children > 0 )
754                 {
755                     /* Select the first child of this node */
756 #ifdef PLAYLIST_DEBUG
757                     msg_Dbg( p_playlist, "%s is a node with children, "
758                                  "playing the first",
759                                   p_parent->pp_children[i+1]->input.psz_name);
760 #endif
761                     if( p_parent->pp_children[i+1]->pp_children[0]
762                                     ->i_children >= 0 )
763                     {
764                         /* first child is a node ! */
765                         return playlist_RecursiveFindNext( p_playlist, i_view,
766                                    p_root, NULL ,
767                                    p_parent->pp_children[i+1]->pp_children[0]);
768                     }
769                     return p_parent->pp_children[i+1]->pp_children[0];
770                 }
771                 else
772                 {
773                     /* This node has no child... We must continue */
774 #ifdef PLAYLIST_DEBUG
775                     msg_Dbg( p_playlist, "%s is a node with no children",
776                                  p_parent->pp_children[i+1]->input.psz_name);
777 #endif
778                     p_item = p_parent->pp_children[i+1];
779                 }
780             }
781         }
782     }
783     /* Just in case :) */
784     return NULL;
785 }
786
787 /* Recursively search the tree for previous item */
788 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
789                 int i_view,
790                 playlist_item_t *p_root,
791                 playlist_item_t *p_item,
792                 playlist_item_t *p_parent )
793 {
794     int i;
795     playlist_item_t *p_parent_parent;
796
797     for( i= p_parent->i_children - 1 ; i >= 0 ; i-- )
798     {
799         if( p_parent->pp_children[i] == p_item || p_item == NULL )
800         {
801             if( p_item == NULL )
802             {
803                 i = -1;
804             }
805 #ifdef PLAYLIST_DEBUG
806             msg_Dbg( p_playlist,"Current item found, child %i of %s",
807                              i , p_parent->input.psz_name );
808 #endif
809             /* We found our item */
810             if( i < 1 )
811             {
812                 /* Too far... */
813 #ifdef PLAYLIST_DEBUG
814                 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
815                                      p_parent->input.psz_name );
816 #endif
817                 if( p_parent == p_root )
818                 {
819                     /* Hmm, seems it's the end for you, guy ! */
820                     return NULL;
821                 }
822                 /* Go up one level */
823                 p_parent_parent = playlist_FindDirectParent( p_playlist,
824                                             p_parent, i_view );
825                 return playlist_RecursiveFindPrev( p_playlist, i_view,p_root,
826                                             p_parent, p_parent_parent );
827             }
828             else
829             {
830                 if( p_parent->pp_children[i-1]->i_children == -1 )
831                 {
832                     /* Cool, we have found a real item to play */
833 #ifdef PLAYLIST_DEBUG
834                     msg_Dbg( p_playlist, "Playing child %i of %s",
835                                      i-1, p_parent->input.psz_name );
836 #endif
837                     return p_parent->pp_children[i-1];
838                 }
839                 else if( p_parent->pp_children[i-1]->i_children > 0 )
840                 {
841                     /* Select the last child of this node */
842 #ifdef PLAYLIST_DEBUG
843                     msg_Dbg( p_playlist, "%s is a node with children,"
844                                    " playing the last",
845                                    p_parent->pp_children[i-1]->input.psz_name);
846 #endif
847                     if( p_parent->pp_children[i-1]->pp_children[p_parent->
848                             pp_children[i-1]->i_children-1]->i_children >= 0 )
849                     {
850                         /* Last child is a node */
851                         return playlist_RecursiveFindPrev( p_playlist, i_view,
852                                     p_root,NULL,
853                                     p_parent->pp_children[i-1]->pp_children[
854                                     p_parent->pp_children[i-1]->i_children-1]);
855                     }
856                     return p_parent->pp_children[i-1]->pp_children[
857                                  p_parent->pp_children[i-1]->i_children-1];
858                 }
859                 else
860                 {
861                     /* This node has no child... We must continue */
862 #ifdef PLAYLIST_DEBUG
863                     msg_Dbg( p_playlist, "%s is a node with no children",
864                                 p_parent->pp_children[i-1]->input.psz_name);
865 #endif
866                     p_item = p_parent->pp_children[i-1];
867                 }
868             }
869         }
870     }
871     return NULL;
872 }
873
874 /* This function returns the parent of an item in a view */
875 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
876                                          playlist_item_t *p_item,
877                                          int i_view )
878 {
879         int i = 0;
880         for( i= 0; i< p_item->i_parents ; i++ )
881         {
882             if( p_item->pp_parents[i]->i_view == i_view )
883             {
884                 return p_item->pp_parents[i]->p_parent;
885             }
886         }
887         return NULL;
888 }
889
890
891 /* This function dumps a node */
892 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
893                         int i_level )
894 {
895     char str[512];
896     int i;
897
898     if( i_level == 1 )
899     {
900         msg_Dbg( p_playlist, "%s (%i)",
901                         p_item->input.psz_name, p_item->i_children );
902     }
903
904     if( p_item->i_children == -1 )
905     {
906         return;
907     }
908
909     for( i = 0; i< p_item->i_children; i++ )
910     {
911         memset( str, 32, 512 );
912         sprintf( str + 2 * i_level , "%s (%i)",
913                                 p_item->pp_children[i]->input.psz_name,
914                                 p_item->pp_children[i]->i_children );
915         msg_Dbg( p_playlist, "%s",str );
916         if( p_item->pp_children[i]->i_children >= 0 )
917         {
918             playlist_NodeDump( p_playlist, p_item->pp_children[i],
919                               i_level + 1 );
920         }
921     }
922     return;
923 }