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