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