]> git.sesse.net Git - vlc/blob - src/playlist/tree.c
Store audio output interface variables into the input manager object
[vlc] / src / playlist / tree.c
1 /*****************************************************************************
2  * tree.c : Playlist tree walking functions
3  *****************************************************************************
4  * Copyright (C) 1999-2007 the VideoLAN team
5  * $Id$
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <vlc_common.h>
28 #include <assert.h>
29 #include "vlc_playlist.h"
30 #include "playlist_internal.h"
31
32 /************************************************************************
33  * Local prototypes
34  ************************************************************************/
35 playlist_item_t *GetNextUncle( playlist_t *p_playlist, playlist_item_t *p_item,
36                                playlist_item_t *p_root );
37 playlist_item_t *GetPrevUncle( playlist_t *p_playlist, playlist_item_t *p_item,
38                                playlist_item_t *p_root );
39
40 playlist_item_t *GetNextItem( playlist_t *p_playlist,
41                               playlist_item_t *p_root,
42                               playlist_item_t *p_item );
43 playlist_item_t *GetPrevItem( playlist_t *p_playlist,
44                               playlist_item_t *p_item,
45                               playlist_item_t *p_root );
46
47 /**
48  * Create a playlist node
49  *
50  * \param p_playlist the playlist
51  * \param psz_name the name of the node
52  * \param p_parent the parent node to attach to or NULL if no attach
53  * \param p_flags miscellaneous flags
54  * \param p_input the input_item to attach to or NULL if it has to be created
55  * \return the new node
56  */
57 playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist,
58                                        const char *psz_name,
59                                        playlist_item_t *p_parent, int i_flags,
60                                        input_item_t *p_input )
61 {
62     input_item_t *p_new_input = NULL;
63     playlist_item_t *p_item;
64
65     PL_ASSERT_LOCKED;
66     if( !psz_name ) psz_name = _("Undefined");
67
68     if( !p_input )
69         p_new_input = input_item_NewWithType( VLC_OBJECT(p_playlist), NULL,
70                                         psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE );
71     p_item = playlist_ItemNewFromInput( p_playlist,
72                                         p_input ? p_input : p_new_input );
73     if( p_new_input )
74         vlc_gc_decref( p_new_input );
75
76     if( p_item == NULL )  return NULL;
77     p_item->i_children = 0;
78
79     ARRAY_APPEND(p_playlist->all_items, p_item);
80
81     if( p_parent != NULL )
82         playlist_NodeAppend( p_playlist, p_item, p_parent );
83     playlist_SendAddNotify( p_playlist, p_item->i_id,
84                             p_parent ? p_parent->i_id : -1,
85                             !( i_flags & PLAYLIST_NO_REBUILD ));
86     return p_item;
87 }
88
89 /**
90  * Remove all the children of a node
91  *
92  * This function must be entered with the playlist lock
93  *
94  * \param p_playlist the playlist
95  * \param p_root the node
96  * \param b_delete_items do we have to delete the children items ?
97  * \return VLC_SUCCESS or an error
98  */
99 int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
100                         bool b_delete_items )
101 {
102     PL_ASSERT_LOCKED;
103     int i;
104     if( p_root->i_children == -1 )
105     {
106         return VLC_EGENERIC;
107     }
108
109     /* Delete the children */
110     for( i =  p_root->i_children-1 ; i >= 0 ;i-- )
111     {
112         if( p_root->pp_children[i]->i_children > -1 )
113         {
114             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
115                                  b_delete_items , false );
116         }
117         else if( b_delete_items )
118         {
119             /* Delete the item here */
120             playlist_DeleteFromItemId( p_playlist,
121                                        p_root->pp_children[i]->i_id );
122         }
123     }
124     return VLC_SUCCESS;
125 }
126
127 /**
128  * Remove all the children of a node and removes the node
129  *
130  * \param p_playlist the playlist
131  * \param p_root the node
132  * \param b_delete_items do we have to delete the children items ?
133  * \return VLC_SUCCESS or an error
134  */
135 int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
136                          bool b_delete_items, bool b_force )
137 {
138     PL_ASSERT_LOCKED;
139     int i;
140
141     if( p_root->i_children == -1 )
142     {
143         return VLC_EGENERIC;
144     }
145
146     /* Delete the children */
147     for( i = p_root->i_children - 1 ; i >= 0; i-- )
148     {
149         if( p_root->pp_children[i]->i_children > -1 )
150         {
151             playlist_NodeDelete( p_playlist, p_root->pp_children[i],
152                                  b_delete_items , b_force );
153         }
154         else if( b_delete_items )
155         {
156             playlist_DeleteItem( p_playlist, p_root->pp_children[i], true );
157         }
158     }
159     /* Delete the node */
160     if( p_root->i_flags & PLAYLIST_RO_FLAG && !b_force )
161     {
162     }
163     else
164     {
165         int i;
166         var_SetInteger( p_playlist, "playlist-item-deleted", p_root->i_id );
167         ARRAY_BSEARCH( p_playlist->all_items, ->i_id, int,
168                        p_root->i_id, i );
169         if( i != -1 )
170             ARRAY_REMOVE( p_playlist->all_items, i );
171
172         /* Remove the item from its parent */
173         if( p_root->p_parent )
174             playlist_NodeRemoveItem( p_playlist, p_root, p_root->p_parent );
175
176         playlist_ItemRelease( p_root );
177     }
178     return VLC_SUCCESS;
179 }
180
181
182 /**
183  * Adds an item to the children of a node
184  *
185  * \param p_playlist the playlist
186  * \param p_item the item to append
187  * \param p_parent the parent node
188  * \return VLC_SUCCESS or an error
189  */
190 int playlist_NodeAppend( playlist_t *p_playlist,
191                          playlist_item_t *p_item,
192                          playlist_item_t *p_parent )
193 {
194     return playlist_NodeInsert( p_playlist, p_item, p_parent, -1 );
195 }
196
197 int playlist_NodeInsert( playlist_t *p_playlist,
198                          playlist_item_t *p_item,
199                          playlist_item_t *p_parent,
200                          int i_position )
201 {
202     PL_ASSERT_LOCKED;
203    (void)p_playlist;
204    assert( p_parent && p_parent->i_children != -1 );
205    if( i_position == -1 ) i_position = p_parent->i_children ;
206
207    INSERT_ELEM( p_parent->pp_children,
208                 p_parent->i_children,
209                 i_position,
210                 p_item );
211    p_item->p_parent = p_parent;
212    return VLC_SUCCESS;
213 }
214
215 /**
216  * Deletes an item from the children of a node
217  *
218  * \param p_playlist the playlist
219  * \param p_item the item to remove
220  * \param p_parent the parent node
221  * \return VLC_SUCCESS or an error
222  */
223 int playlist_NodeRemoveItem( playlist_t *p_playlist,
224                         playlist_item_t *p_item,
225                         playlist_item_t *p_parent )
226 {
227     PL_ASSERT_LOCKED;
228    (void)p_playlist;
229
230    for(int i= 0; i< p_parent->i_children ; i++ )
231    {
232        if( p_parent->pp_children[i] == p_item )
233        {
234            REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
235        }
236    }
237
238    return VLC_SUCCESS;
239 }
240
241 /**
242  * Search a child of a node by its name
243  *
244  * \param p_node the node
245  * \param psz_search the name of the child to search
246  * \return the child item or NULL if not found or error
247  */
248 playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
249                                            const char *psz_search )
250 {
251     playlist_t * p_playlist = p_node->p_playlist; /* For assert_locked */
252     PL_ASSERT_LOCKED;
253     int i;
254
255     if( p_node->i_children < 0 )
256     {
257          return NULL;
258     }
259     for( i = 0 ; i< p_node->i_children; i++ )
260     {
261         if( !strcmp( p_node->pp_children[i]->p_input->psz_name, psz_search ) )
262         {
263             return p_node->pp_children[i];
264         }
265     }
266     return NULL;
267 }
268
269 /**********************************************************************
270  * Tree walking functions
271  **********************************************************************/
272 /**
273  * Finds the next item to play
274  *
275  * \param p_playlist the playlist
276  * \param p_root the root node
277  * \param p_item the previous item  (NULL if none )
278  * \return the next item to play, or NULL if none found
279  */
280 playlist_item_t *playlist_GetNextLeaf( playlist_t *p_playlist,
281                                        playlist_item_t *p_root,
282                                        playlist_item_t *p_item,
283                                        bool b_ena, bool b_unplayed )
284 {
285     PL_ASSERT_LOCKED;
286     playlist_item_t *p_next;
287
288     assert( p_root && p_root->i_children != -1 );
289
290     PL_DEBUG2( "finding next of %s within %s",
291                PLI_NAME( p_item ), PLI_NAME( p_root ) );
292
293     /* Now, walk the tree until we find a suitable next item */
294     p_next = p_item;
295     while( 1 )
296     {
297         bool b_ena_ok = true, b_unplayed_ok = true;
298         p_next = GetNextItem( p_playlist, p_root, p_next );
299         if( !p_next || p_next == p_root )
300             break;
301         if( p_next->i_children == -1 )
302         {
303             if( b_ena && p_next->i_flags & PLAYLIST_DBL_FLAG )
304                 b_ena_ok = false;
305             if( b_unplayed && p_next->p_input->i_nb_played != 0 )
306                 b_unplayed_ok = false;
307             if( b_ena_ok && b_unplayed_ok ) break;
308         }
309     }
310     if( p_next == NULL ) PL_DEBUG2( "at end of node" );
311     return p_next;
312 }
313
314 /**
315  * Finds the previous item to play
316  *
317  * \param p_playlist the playlist
318  * \param p_root the root node
319  * \param p_item the previous item  (NULL if none )
320  * \return the next item to play, or NULL if none found
321  */
322 playlist_item_t *playlist_GetPrevLeaf( playlist_t *p_playlist,
323                                        playlist_item_t *p_root,
324                                        playlist_item_t *p_item,
325                                        bool b_ena, bool b_unplayed )
326 {
327     PL_ASSERT_LOCKED;
328     playlist_item_t *p_prev;
329
330     PL_DEBUG2( "finding previous of %s within %s", PLI_NAME( p_item ),
331                                                    PLI_NAME( p_root ) );
332     assert( p_root && p_root->i_children != -1 );
333
334     /* Now, walk the tree until we find a suitable previous item */
335     p_prev = p_item;
336     while( 1 )
337     {
338         bool b_ena_ok = true, b_unplayed_ok = true;
339         p_prev = GetPrevItem( p_playlist, p_root, p_prev );
340         if( !p_prev || p_prev == p_root )
341             break;
342         if( p_prev->i_children == -1 )
343         {
344             if( b_ena && p_prev->i_flags & PLAYLIST_DBL_FLAG )
345                 b_ena_ok = false;
346             if( b_unplayed && p_prev->p_input->i_nb_played != 0 )
347                 b_unplayed_ok = false;
348             if( b_ena_ok && b_unplayed_ok ) break;
349         }
350     }
351     if( p_prev == NULL ) PL_DEBUG2( "at beginning of node" );
352     return p_prev;
353 }
354
355 /************************************************************************
356  * Following functions are local
357  ***********************************************************************/
358
359 /**
360  * Get the next item in the tree
361  * If p_item is NULL, return the first child of root
362  **/
363 playlist_item_t *GetNextItem( playlist_t *p_playlist,
364                               playlist_item_t *p_root,
365                               playlist_item_t *p_item )
366 {
367     /* If the item is NULL, return the firt child of root */
368     if( p_item == NULL )
369     {
370         if( p_root->i_children > 0 )
371             return p_root->pp_children[0];
372         else
373             return NULL;
374     }
375
376     /* Node with children, get the first one */
377     if( p_item->i_children > 0 )
378         return p_item->pp_children[0];
379
380     playlist_item_t* p_parent = p_item->p_parent;
381     for( int i = 0 ; i < p_parent->i_children ; i++ )
382     {
383         if( p_parent->pp_children[i] == p_item )
384         {
385             // Return the next children
386             if( i + 1 < p_parent->i_children )
387                 return p_parent->pp_children[i+1];
388             // We are the least one, so try to have uncles
389             else
390             {
391                 PL_DEBUG2( "Current item is the last of the node,"
392                            "looking for uncle from %s",
393                             p_parent->p_input->psz_name );
394                 if( p_parent == p_root )
395                 {
396                     PL_DEBUG2( "already at root" );
397                     return NULL;
398                 }
399                 else
400                     return GetNextUncle( p_playlist, p_item, p_root );
401             }
402         }
403     }
404     return NULL;
405 }
406
407 playlist_item_t *GetNextUncle( playlist_t *p_playlist, playlist_item_t *p_item,
408                                playlist_item_t *p_root )
409 {
410     playlist_item_t *p_parent = p_item->p_parent;
411     playlist_item_t *p_grandparent;
412     bool b_found = false;
413
414     (void)p_playlist;
415
416     if( p_parent != NULL )
417     {
418         p_grandparent = p_parent->p_parent;
419         while( p_grandparent )
420         {
421             int i;
422             for( i = 0 ; i< p_grandparent->i_children ; i++ )
423             {
424                 if( p_parent == p_grandparent->pp_children[i] )
425                 {
426                     PL_DEBUG2( "parent %s found as child %i of grandparent %s",
427                                p_parent->p_input->psz_name, i,
428                                p_grandparent->p_input->psz_name );
429                     b_found = true;
430                     break;
431                 }
432             }
433             if( b_found && i + 1 < p_grandparent->i_children )
434             {
435                     return p_grandparent->pp_children[i+1];
436             }
437             /* Not found at root */
438             if( p_grandparent == p_root )
439             {
440                 return NULL;
441             }
442             else
443             {
444                 p_parent = p_grandparent;
445                 p_grandparent = p_parent->p_parent;
446             }
447         }
448     }
449     /* We reached root */
450     return NULL;
451 }
452
453 playlist_item_t *GetPrevUncle( playlist_t *p_playlist, playlist_item_t *p_item,
454                                playlist_item_t *p_root )
455 {
456     playlist_item_t *p_parent = p_item->p_parent;
457     playlist_item_t *p_grandparent;
458     bool b_found = false;
459
460     (void)p_playlist;
461
462     if( p_parent != NULL )
463     {
464         p_grandparent = p_parent->p_parent;
465         while( 1 )
466         {
467             int i;
468             for( i = p_grandparent->i_children -1 ; i >= 0; i-- )
469             {
470                 if( p_parent == p_grandparent->pp_children[i] )
471                 {
472                     b_found = true;
473                     break;
474                 }
475             }
476             if( b_found && i - 1 > 0 )
477             {
478                 return p_grandparent->pp_children[i-1];
479             }
480             /* Not found at root */
481             if( p_grandparent == p_root )
482             {
483                 return NULL;
484             }
485             else
486             {
487                 p_parent = p_grandparent;
488                 p_grandparent = p_parent->p_parent;
489             }
490         }
491     }
492     /* We reached root */
493     return NULL;
494 }
495
496
497 /* Recursively search the tree for previous item */
498 playlist_item_t *GetPrevItem( playlist_t *p_playlist,
499                               playlist_item_t *p_root,
500                               playlist_item_t *p_item )
501 {
502     playlist_item_t *p_parent;
503     int i;
504
505     /* Node with children, get the last one */
506     if( p_item && p_item->i_children > 0 )
507         return p_item->pp_children[p_item->i_children - 1];
508
509     /* Last child of its parent ? */
510     if( p_item != NULL )
511         p_parent = p_item->p_parent;
512     else
513     {
514         msg_Err( p_playlist, "Get the last one" );
515         abort();
516     };
517
518     for( i = p_parent->i_children -1 ; i >= 0 ;  i-- )
519     {
520         if( p_parent->pp_children[i] == p_item )
521         {
522             if( i-1 < 0 )
523             {
524                /* Was already the first sibling. Look for uncles */
525                 PL_DEBUG2( "current item is the first of its node,"
526                            "looking for uncle from %s",
527                            p_parent->p_input->psz_name );
528                 if( p_parent == p_root )
529                 {
530                     PL_DEBUG2( "already at root" );
531                     return NULL;
532                 }
533                 return GetPrevUncle( p_playlist, p_item, p_root );
534             }
535             else
536             {
537                 return p_parent->pp_children[i-1];
538             }
539         }
540     }
541     return NULL;
542 }