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