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