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