]> git.sesse.net Git - vlc/blob - src/playlist/view.c
9d85dc756c198330af759099e7aebf7d96f6d42d
[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->pp_parents = NULL;
292     p_item->i_parents = 0;
293
294     p_item->i_flags |= PLAYLIST_SKIP_FLAG; /* Default behaviour */
295
296     vlc_mutex_init( p_playlist, &p_item->input.lock );
297
298     if( p_parent != NULL )
299     {
300         playlist_NodeAppend( p_playlist, i_view, p_item, p_parent );
301     }
302
303     p_add->p_node = p_parent;
304     p_add->p_item = p_item;
305     p_add->i_view = i_view;
306     val.p_address = p_add;
307     var_Set( p_playlist, "item-append", val);
308
309     free( p_add );
310
311     return p_item;
312 }
313
314 /**
315  * Remove all the children of a node
316  *
317  * This function must be entered with the playlist lock
318  *
319  * \param p_playlist the playlist
320  * \param p_root the node
321  * \param b_delete_items do we have to delete the children items ?
322  * \return VLC_SUCCESS or an error
323  */
324 int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
325                         vlc_bool_t b_delete_items )
326 {
327     int i;
328     if( p_root->i_children == -1 )
329     {
330         return VLC_EGENERIC;
331     }
332
333     /* Delete the children */
334     for( i =  p_root->i_children-1 ; i >= 0 ;i-- )
335     {
336         if( p_root->pp_children[i]->i_children > -1 )
337         {
338             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
339                                 b_delete_items );
340         }
341         else if( b_delete_items )
342         {
343             /* Delete the item here */
344             playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
345         }
346     }
347     return VLC_SUCCESS;
348 }
349
350 /**
351  * Remove all the children of a node and removes the node
352  *
353  * \param p_playlist the playlist
354  * \param p_root the node
355  * \param b_delete_items do we have to delete the children items ?
356  * \return VLC_SUCCESS or an error
357  */
358 int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
359                          vlc_bool_t b_delete_items )
360 {
361     return playlist_NodeDeleteInternal( p_playlist, p_root,
362                                         b_delete_items, VLC_FALSE );
363 }
364
365 int playlist_NodeDeleteInternal( playlist_t *p_playlist,
366                                  playlist_item_t *p_root,
367                                  vlc_bool_t b_delete_items, vlc_bool_t b_force )
368 {
369     int i;
370     if( p_root->i_children == -1 )
371     {
372         return VLC_EGENERIC;
373     }
374
375     /* Delete the children */
376     for( i =  p_root->i_children - 1 ; i >= 0; i-- )
377     {
378         if( p_root->pp_children[i]->i_children > -1 )
379         {
380             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
381                                 b_delete_items );
382         }
383         else if( b_delete_items )
384         {
385             /* Delete the item here */
386             playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
387         }
388     }
389     /* Delete the node */
390     if( p_root->i_flags & PLAYLIST_RO_FLAG && !b_force )
391     {
392     }
393     else
394     {
395         for( i = 0 ; i< p_root->i_parents; i++ )
396         {
397             playlist_NodeRemoveItem( p_playlist, p_root,
398                                      p_root->pp_parents[i]->p_parent );
399         }
400         var_SetInteger( p_playlist, "item-deleted", p_root->input.i_id );
401         playlist_ItemDelete( p_root );
402     }
403     return VLC_SUCCESS;
404 }
405
406
407
408 /**
409  * Adds an item to the childs of a node
410  *
411  * \param p_playlist the playlist
412  * \param i_view the view of the node ( needed for parent search )
413  * \param p_item the item to append
414  * \param p_parent the parent node
415  * \return VLC_SUCCESS or an error
416  */
417 int playlist_NodeAppend( playlist_t *p_playlist,
418                          int i_view,
419                          playlist_item_t *p_item,
420                          playlist_item_t *p_parent )
421 {
422     return playlist_NodeInsert( p_playlist, i_view, p_item, p_parent, -1 );
423 }
424
425 int playlist_NodeInsert( playlist_t *p_playlist,
426                          int i_view,
427                          playlist_item_t *p_item,
428                          playlist_item_t *p_parent,
429                          int i_position )
430 {
431    int i;
432    vlc_bool_t b_found = VLC_FALSE;
433    if( !p_parent || p_parent->i_children == -1 )
434    {
435         msg_Err( p_playlist, "invalid node" );
436         return VLC_EGENERIC;
437    }
438
439    if( i_position == -1 ) i_position = p_parent->i_children ;
440
441    INSERT_ELEM( p_parent->pp_children,
442                 p_parent->i_children,
443                 i_position,
444                 p_item );
445
446    /* Add the parent to the array */
447    for( i= 0; i< p_item->i_parents ; i++ )
448    {
449        if( p_item->pp_parents[i]->i_view == i_view )
450        {
451            b_found = VLC_TRUE;
452            break;
453        }
454    }
455    if( b_found == VLC_FALSE )
456    {
457         struct item_parent_t *p_ip = (struct item_parent_t *)
458                                      malloc(sizeof(struct item_parent_t) );
459         p_ip->i_view = i_view;
460         p_ip->p_parent = p_parent;
461
462         INSERT_ELEM( p_item->pp_parents,
463                      p_item->i_parents, p_item->i_parents,
464                      p_ip );
465    }
466
467    /* Let the interface know this has been updated */
468    p_parent->i_serial++;
469    return VLC_SUCCESS;
470 }
471
472 /**
473  * Deletes an item from the children of a node
474  *
475  * \param p_playlist the playlist
476  * \param p_item the item to remove
477  * \param p_parent the parent node
478  * \return VLC_SUCCESS or an error
479  */
480 int playlist_NodeRemoveItem( playlist_t *p_playlist,
481                         playlist_item_t *p_item,
482                         playlist_item_t *p_parent )
483 {
484    int i;
485    if( !p_parent || p_parent->i_children == -1 )
486    {
487         msg_Err( p_playlist, "invalid node" );
488    }
489
490    for( i= 0; i< p_parent->i_children ; i++ )
491    {
492        if( p_parent->pp_children[i] == p_item )
493        {
494            REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
495        }
496    }
497
498    /* Let the interface know this has been updated */
499    p_parent->i_serial++;
500
501    return VLC_SUCCESS;
502 }
503
504
505 /**
506  * Count the children of a node
507  *
508  * \param p_playlist the playlist
509  * \param p_node the node
510  * \return the number of children
511  */
512 int playlist_NodeChildrenCount( playlist_t *p_playlist, playlist_item_t*p_node)
513 {
514     int i;
515     int i_nb = 0;
516     if( p_node->i_children == -1 )
517     {
518         return 0;
519     }
520
521     for( i=0 ; i< p_node->i_children;i++ )
522     {
523         if( p_node->pp_children[i]->i_children == -1 )
524         {
525             i_nb++;
526         }
527         else
528         {
529             i_nb += playlist_NodeChildrenCount( p_playlist, 
530                                                 p_node->pp_children[i] );
531         }
532     }
533     return i_nb;
534 }
535
536 /**
537  * Search a child of a node by its name
538  *
539  * \param p_node the node
540  * \param psz_search the name of the child to search
541  * \return the child item or NULL if not found or error
542  */
543 playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
544                                            const char *psz_search )
545 {
546     int i;
547
548     if( p_node->i_children < 0 )
549     {
550          return NULL;
551     }
552     for( i = 0 ; i< p_node->i_children; i++ )
553     {
554         if( !strcmp( p_node->pp_children[i]->input.psz_name, psz_search ) )
555         {
556             return p_node->pp_children[i];
557         }
558     }
559     return NULL;
560 }
561
562
563 /**********************************************************************
564  * Tree functions
565  **********************************************************************/
566
567 /**
568  * Finds the next item to play
569  *
570  * \param p_playlist the playlist
571  * \param i_view the view
572  * \param p_root the root node
573  * \param p_node the node we are playing from
574  * \param p_item the item we were playing (NULL if none )
575  * \return the next item to play, or NULL if none found
576  */
577 playlist_item_t *playlist_FindNextFromParent( playlist_t *p_playlist,
578                                         int i_view, /* FIXME: useless */
579                                         playlist_item_t *p_root,
580                                         playlist_item_t *p_node,
581                                         playlist_item_t *p_item )
582 {
583     playlist_item_t *p_search, *p_next;
584
585 #ifdef PLAYLIST_DEBUG
586     if( p_item != NULL )
587     {
588         msg_Dbg( p_playlist, "finding next of %s within %s",
589                         p_item->input.psz_name, p_node->input.psz_name );
590     }
591     else
592     {
593         msg_Dbg( p_playlist, "finding something to play within %s",
594                                  p_node->input.psz_name );
595
596     }
597 #endif
598
599     if( !p_node  || p_node->i_children == -1 )
600     {
601         msg_Err( p_playlist,"invalid arguments for FindNextFromParent" );
602         return NULL;
603     }
604
605     /* Find the parent node of the item */
606     if( p_item != NULL )
607     {
608         p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
609         if( p_search == NULL )
610         {
611             msg_Err( p_playlist, "parent node not found" );
612             return NULL;
613         }
614     }
615     else
616     {
617         p_search = p_node;
618     }
619
620     /* Now, go up the tree until we find a suitable next item */
621     p_next = playlist_RecursiveFindNext( p_playlist,i_view,
622                                          p_node, p_item, p_search );
623
624     /* Not found, do we go past p_node ? */
625     if( p_next == NULL )
626     {
627         if( p_playlist->b_go_next )
628         {
629             p_next = playlist_RecursiveFindNext( p_playlist, i_view,
630                                 p_root, p_item, p_search );
631             if( p_next == NULL )
632             {
633                 return NULL;
634             }
635             /* OK, we could continue, so set our current node to the root */
636             p_playlist->status.p_node = p_root;
637         }
638         else
639         {
640             return NULL;
641         }
642     }
643     return p_next;
644 }
645
646 /**
647  * Finds the previous item to play
648  *
649  * \param p_playlist the playlist
650  * \param i_view the view
651  * \param p_root the root node
652  * \param p_node the node we are playing from
653  * \param p_item the item we were playing (NULL if none )
654  * \return the next item to play, or NULL if none found
655  */
656 playlist_item_t *playlist_FindPrevFromParent( playlist_t *p_playlist,
657                                         int i_view,
658                                         playlist_item_t *p_root,
659                                         playlist_item_t *p_node,
660                                         playlist_item_t *p_item )
661 {
662     playlist_item_t *p_search, *p_next;
663
664 #ifdef PLAYLIST_DEBUG
665     if( p_item != NULL )
666     {
667         msg_Dbg( p_playlist, "Finding prev of %s within %s",
668                         p_item->input.psz_name, p_node->input.psz_name );
669     }
670     else
671     {
672         msg_Dbg( p_playlist, "Finding prev from %s",p_node->input.psz_name );
673     }
674 #endif
675
676     if( !p_node  || p_node->i_children == -1 )
677     {
678         msg_Err( p_playlist,"invalid arguments for FindPrevFromParent" );
679         return NULL;
680     }
681
682     /* Find the parent node of the item */
683     if( p_item != NULL )
684     {
685         p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
686         if( p_search == NULL )
687         {
688             msg_Err( p_playlist, "parent node not found" );
689             return NULL;
690         }
691     }
692     else
693     {
694         p_search = p_node;
695     }
696
697     /* Now, go up the tree until we find a suitable next item */
698     p_next = playlist_RecursiveFindPrev( p_playlist,i_view,
699                                          p_node, p_item, p_search );
700
701     if( p_next == NULL )
702     {
703         if( p_playlist->b_go_next )
704         {
705             p_next = playlist_RecursiveFindPrev( p_playlist, i_view,
706                                 p_root, p_item, p_search );
707             if( p_next == NULL )
708             {
709                 return NULL;
710             }
711             /* OK, we could continue, so set our current node to the root */
712             p_playlist->status.p_node = p_root;
713         }
714         else
715         {
716             return NULL;
717         }
718     }
719     return p_next;
720 }
721
722 /************************************************************************
723  * Following functions are local
724  ***********************************************************************/
725
726
727 /* Recursively search the tree for next item */
728 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
729                 int i_view,
730                 playlist_item_t *p_root,
731                 playlist_item_t *p_item,
732                 playlist_item_t *p_parent )
733 {
734     int i;
735     playlist_item_t *p_parent_parent;
736
737     for( i= 0 ; i < p_parent->i_children ; i++ )
738     {
739         if( p_parent->pp_children[i] == p_item || p_item == NULL )
740         {
741             if( p_item == NULL )
742             {
743                 i = -1;
744             }
745 #ifdef PLAYLIST_DEBUG
746             msg_Dbg( p_playlist,"Current item found, child %i of %s",
747                                 i , p_parent->input.psz_name );
748 #endif
749             /* We found our item */
750             if( i+1 >= p_parent->i_children )
751             {
752                 /* Too far... */
753 #ifdef PLAYLIST_DEBUG
754                 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
755                                 p_parent->input.psz_name );
756 #endif
757                 if( p_parent == p_root )
758                 {
759                     /* Hmm, seems it's the end for you, guy ! */
760                     return NULL;
761                 }
762
763                 /* Go up one level */
764                 p_parent_parent = playlist_FindDirectParent( p_playlist,
765                                                              p_parent, i_view );
766                 if( p_parent_parent == NULL )
767                 {
768                     msg_Warn( p_playlist, "Unable to find parent !");
769                     return NULL;
770                 }
771                 return playlist_RecursiveFindNext( p_playlist, i_view,p_root,
772                                                    p_parent, p_parent_parent );
773             }
774             else
775             {
776                 if( p_parent->pp_children[i+1]->i_children == -1 )
777                 {
778                     /* Cool, we have found a real item to play */
779 #ifdef PLAYLIST_DEBUG
780                     msg_Dbg( p_playlist, "Playing child %i of %s",
781                                      i+1 , p_parent->input.psz_name );
782 #endif
783                     return p_parent->pp_children[i+1];
784                 }
785                 else if( p_parent->pp_children[i+1]->i_children > 0 )
786                 {
787                     /* Select the first child of this node */
788 #ifdef PLAYLIST_DEBUG
789                     msg_Dbg( p_playlist, "%s is a node with children, "
790                                  "playing the first",
791                                   p_parent->pp_children[i+1]->input.psz_name);
792 #endif
793                     if( p_parent->pp_children[i+1]->pp_children[0]
794                                     ->i_children >= 0 )
795                     {
796                         /* first child is a node ! */
797                         return playlist_RecursiveFindNext( p_playlist, i_view,
798                                    p_root, NULL ,
799                                    p_parent->pp_children[i+1]->pp_children[0]);
800                     }
801                     return p_parent->pp_children[i+1]->pp_children[0];
802                 }
803                 else
804                 {
805                     /* This node has no child... We must continue */
806 #ifdef PLAYLIST_DEBUG
807                     msg_Dbg( p_playlist, "%s is a node with no children",
808                                  p_parent->pp_children[i+1]->input.psz_name);
809 #endif
810                     p_item = p_parent->pp_children[i+1];
811                 }
812             }
813         }
814     }
815     /* Just in case :) */
816     return NULL;
817 }
818
819 /* Recursively search the tree for previous item */
820 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
821                 int i_view,
822                 playlist_item_t *p_root,
823                 playlist_item_t *p_item,
824                 playlist_item_t *p_parent )
825 {
826     int i;
827     playlist_item_t *p_parent_parent;
828
829     for( i= p_parent->i_children - 1 ; i >= 0 ; i-- )
830     {
831         if( p_parent->pp_children[i] == p_item || p_item == NULL )
832         {
833             if( p_item == NULL )
834             {
835                 i = -1;
836             }
837 #ifdef PLAYLIST_DEBUG
838             msg_Dbg( p_playlist,"Current item found, child %i of %s",
839                              i , p_parent->input.psz_name );
840 #endif
841             /* We found our item */
842             if( i < 1 )
843             {
844                 /* Too far... */
845 #ifdef PLAYLIST_DEBUG
846                 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
847                                      p_parent->input.psz_name );
848 #endif
849                 if( p_parent == p_root )
850                 {
851                     /* Hmm, seems it's the end for you, guy ! */
852                     return NULL;
853                 }
854                 /* Go up one level */
855                 p_parent_parent = playlist_FindDirectParent( p_playlist,
856                                             p_parent, i_view );
857                 return playlist_RecursiveFindPrev( p_playlist, i_view,p_root,
858                                             p_parent, p_parent_parent );
859             }
860             else
861             {
862                 if( p_parent->pp_children[i-1]->i_children == -1 )
863                 {
864                     /* Cool, we have found a real item to play */
865 #ifdef PLAYLIST_DEBUG
866                     msg_Dbg( p_playlist, "Playing child %i of %s",
867                                      i-1, p_parent->input.psz_name );
868 #endif
869                     return p_parent->pp_children[i-1];
870                 }
871                 else if( p_parent->pp_children[i-1]->i_children > 0 )
872                 {
873                     /* Select the last child of this node */
874 #ifdef PLAYLIST_DEBUG
875                     msg_Dbg( p_playlist, "%s is a node with children,"
876                                    " playing the last",
877                                    p_parent->pp_children[i-1]->input.psz_name);
878 #endif
879                     if( p_parent->pp_children[i-1]->pp_children[p_parent->
880                             pp_children[i-1]->i_children-1]->i_children >= 0 )
881                     {
882                         /* Last child is a node */
883                         return playlist_RecursiveFindPrev( p_playlist, i_view,
884                                     p_root,NULL,
885                                     p_parent->pp_children[i-1]->pp_children[
886                                     p_parent->pp_children[i-1]->i_children-1]);
887                     }
888                     return p_parent->pp_children[i-1]->pp_children[
889                                  p_parent->pp_children[i-1]->i_children-1];
890                 }
891                 else
892                 {
893                     /* This node has no child... We must continue */
894 #ifdef PLAYLIST_DEBUG
895                     msg_Dbg( p_playlist, "%s is a node with no children",
896                                 p_parent->pp_children[i-1]->input.psz_name);
897 #endif
898                     p_item = p_parent->pp_children[i-1];
899                 }
900             }
901         }
902     }
903     return NULL;
904 }
905
906 /* This function returns the parent of an item in a view */
907 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
908                                          playlist_item_t *p_item,
909                                          int i_view )
910 {
911         int i = 0;
912         for( i= 0; i< p_item->i_parents ; i++ )
913         {
914             if( p_item->pp_parents[i]->i_view == i_view )
915             {
916                 return p_item->pp_parents[i]->p_parent;
917             }
918         }
919         return NULL;
920 }
921
922
923 /* This function dumps a node */
924 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
925                         int i_level )
926 {
927     char str[512];
928     int i;
929
930     if( i_level == 1 )
931     {
932         msg_Dbg( p_playlist, "%s (%i)",
933                         p_item->input.psz_name, p_item->i_children );
934     }
935
936     if( p_item->i_children == -1 )
937     {
938         return;
939     }
940
941     for( i = 0; i< p_item->i_children; i++ )
942     {
943         memset( str, 32, 512 );
944         sprintf( str + 2 * i_level , "%s (%i)",
945                                 p_item->pp_children[i]->input.psz_name,
946                                 p_item->pp_children[i]->i_children );
947         msg_Dbg( p_playlist, "%s",str );
948         if( p_item->pp_children[i]->i_children >= 0 )
949         {
950             playlist_NodeDump( p_playlist, p_item->pp_children[i],
951                               i_level + 1 );
952         }
953     }
954     return;
955 }