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