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