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 $
7 * Authors: Clément Stenac <zorglub@videolan.org>
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.
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.
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() */
28 #include <vlc/input.h>
30 #include "vlc_playlist.h"
34 /************************************************************************
36 ************************************************************************/
39 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
40 playlist_item_t *, int );
42 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
44 playlist_item_t *p_root,
45 playlist_item_t *p_item,
46 playlist_item_t *p_parent );
48 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
50 playlist_item_t *p_root,
51 playlist_item_t *p_item,
52 playlist_item_t *p_parent );
54 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
57 /**********************************************************************
58 * Exported View management functions
59 **********************************************************************/
64 * \param p_playlist a playlist object
65 * \param i_id the view identifier
66 * \return the new view or NULL on failure
68 playlist_view_t * playlist_ViewCreate( playlist_t *p_playlist, int i_id,
71 playlist_view_t * p_view;
73 msg_Dbg( p_playlist, "creating view %i",i_id );
75 p_view = malloc( sizeof( playlist_view_t ) );
77 memset( p_view, 0, sizeof( playlist_view_t ) );
79 p_view->p_root = playlist_NodeCreate( p_playlist, i_id, NULL, NULL );
81 p_view->psz_name = psz_name ? strdup( psz_name ) : strdup(_("Undefined") );
87 * Creates a new view and add it to the list
89 * \param p_playlist a playlist object
90 * \param i_id the view identifier
91 * \return VLC_SUCCESS or an error
93 int playlist_ViewInsert( playlist_t *p_playlist, int i_id, char *psz_name )
95 playlist_view_t *p_view =
96 playlist_ViewCreate( p_playlist, i_id , psz_name );
99 msg_Err( p_playlist, "Creation failed" );
103 vlc_mutex_lock( &p_playlist->object_lock );
105 INSERT_ELEM( p_playlist->pp_views, p_playlist->i_views,
106 p_playlist->i_views, p_view );
108 vlc_mutex_unlock( &p_playlist->object_lock );
116 * \param p_view the view to delete
119 int playlist_ViewDelete( playlist_t *p_playlist,playlist_view_t *p_view )
121 REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, 0 );
127 * Dumps the content of a view
129 * \param p_playlist the playlist
130 * \param p_view the view to dump
133 int playlist_ViewDump( playlist_t *p_playlist, playlist_view_t *p_view )
135 msg_Dbg( p_playlist, "dumping view %i",p_view->i_id );
136 playlist_NodeDump( p_playlist,p_view->p_root, 1 );
141 * Counts the items of a view
143 * \param p_playlist the playlist
144 * \param p_view the view to count
145 * \return the number of items
147 int playlist_ViewItemCount( playlist_t *p_playlist,
148 playlist_view_t *p_view )
150 return playlist_NodeChildrenCount( p_playlist, p_view->p_root );
155 * Updates a view. Only make sense for "sorted" and "ALL" views
157 * \param p_playlist the playlist
158 * \param i_view the view to update
161 int playlist_ViewUpdate( playlist_t *p_playlist, int i_view)
163 playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_view );
170 if( i_view == VIEW_ALL )
172 p_view->p_root->i_children = p_playlist->i_size;
173 p_view->p_root->pp_children = p_playlist->pp_items;
176 /* Handle update of sorted views here */
177 if( i_view == VIEW_S_AUTHOR )
179 playlist_ViewEmpty( p_playlist, i_view, VLC_FALSE );
181 playlist_NodeGroup( p_playlist, i_view, p_view->p_root,
182 p_playlist->pp_items,p_playlist->i_size,
183 SORT_AUTHOR, ORDER_NORMAL );
193 * \param p_playlist the playlist
194 * \param i_id the id to find
195 * \return the found view or NULL if not found
197 playlist_view_t *playlist_ViewFind( playlist_t *p_playlist, int i_id )
200 for( i=0 ; i< p_playlist->i_views ; i++ )
202 if( p_playlist->pp_views[i]->i_id == i_id )
204 return p_playlist->pp_views[i];
211 int playlist_ViewEmpty( playlist_t *p_playlist, int i_view,
212 vlc_bool_t b_delete_items )
214 playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_view );
221 return playlist_NodeEmpty( p_playlist, p_view->p_root, b_delete_items );
224 /**********************************************************************
225 * Exported Nodes management functions
226 **********************************************************************/
231 * Create a playlist node
233 * \param p_playlist the playlist
234 * \paam psz_name the name of the node
235 * \param p_parent the parent node to attach to or NULL if no attach
236 * \return the new node
238 playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist, int i_view,
240 playlist_item_t *p_parent )
242 /* Create the item */
243 playlist_item_t *p_item = (playlist_item_t *)malloc(
244 sizeof( playlist_item_t ) );
246 playlist_add_t *p_add = (playlist_add_t*)malloc( sizeof(playlist_add_t));
248 vlc_input_item_Init( VLC_OBJECT(p_playlist), &p_item->input );
254 if( psz_name != NULL )
256 p_item->input.psz_name = strdup( psz_name );
260 p_item->input.psz_name = strdup( _("Undefined") );
263 p_item->input.psz_uri = NULL;
265 p_item->b_enabled = VLC_TRUE;
266 p_item->i_nb_played = 0;
270 p_item->i_children = 0;
271 p_item->pp_children = NULL;
273 p_item->input.i_duration = -1;
274 p_item->input.ppsz_options = NULL;
275 p_item->input.i_options = 0;
276 p_item->input.i_categories = 0;
277 p_item->input.pp_categories = NULL;
279 p_item->pp_parents = NULL;
280 p_item->i_parents = 0;
282 p_item->i_flags |= PLAYLIST_SKIP_FLAG; /* Default behaviour */
284 vlc_mutex_init( p_playlist, &p_item->input.lock );
286 if( p_parent != NULL )
288 playlist_NodeAppend( p_playlist, i_view, p_item, p_parent );
291 p_add->p_node = p_parent;
292 p_add->p_item = p_item;
293 p_add->i_view = i_view;
294 val.p_address = p_add;
295 var_Set( p_playlist, "item-append", val);
301 * Remove all the children of a node
303 * \param p_playlist the playlist
304 * \param p_root the node
305 * \param b_delete_items do we have to delete the children items ?
306 * \return VLC_SUCCESS or an error
308 int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
309 vlc_bool_t b_delete_items )
312 if( p_root->i_children == -1 )
317 /* Delete the children */
318 for( i = p_root->i_children-1 ; i >= 0 ;i-- )
320 if( p_root->pp_children[i]->i_children > -1 )
322 playlist_NodeDelete( p_playlist, p_root->pp_children[i],
325 else if( b_delete_items )
327 /* Delete the item here */
328 playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
335 * Remove all the children of a node and removes the node
337 * \param p_playlist the playlist
338 * \param p_root the node
339 * \param b_delete_items do we have to delete the children items ?
340 * \return VLC_SUCCESS or an error
342 int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
343 vlc_bool_t b_delete_items )
346 if( p_root->i_children == -1 )
351 /* Delete the children */
352 for( i = p_root->i_children - 1 ; i >= 0; i-- )
354 if( p_root->pp_children[i]->i_children > -1 )
356 playlist_NodeDelete( p_playlist, p_root->pp_children[i],
359 else if( b_delete_items )
361 /* Delete the item here */
364 /* Delete the node */
365 for( i = 0 ; i< p_root->i_parents; i++ )
367 playlist_NodeRemoveItem( p_playlist, p_root,
368 p_root->pp_parents[i]->p_parent );
370 playlist_ItemDelete( p_root );
377 * Adds an item to the childs of a node
379 * \param p_playlist the playlist
380 * \param i_view the view of the node ( needed for parent search )
381 * \param p_item the item to append
382 * \param p_parent the parent node
383 * \return VLC_SUCCESS or an error
385 int playlist_NodeAppend( playlist_t *p_playlist,
387 playlist_item_t *p_item,
388 playlist_item_t *p_parent )
390 return playlist_NodeInsert( p_playlist, i_view, p_item, p_parent, -1 );
393 int playlist_NodeInsert( playlist_t *p_playlist,
395 playlist_item_t *p_item,
396 playlist_item_t *p_parent,
400 vlc_bool_t b_found = VLC_FALSE;
401 if( !p_parent || p_parent->i_children == -1 )
403 msg_Err( p_playlist, "invalid node" );
407 if( i_position == -1 ) i_position = p_parent->i_children ;
409 INSERT_ELEM( p_parent->pp_children,
410 p_parent->i_children,
414 /* Add the parent to the array */
415 for( i= 0; i< p_item->i_parents ; i++ )
417 if( p_item->pp_parents[i]->i_view == i_view )
423 if( b_found == VLC_FALSE )
425 struct item_parent_t *p_ip = (struct item_parent_t *)
426 malloc(sizeof(struct item_parent_t) );
427 p_ip->i_view = i_view;
428 p_ip->p_parent = p_parent;
430 INSERT_ELEM( p_item->pp_parents,
431 p_item->i_parents, p_item->i_parents,
435 /* Let the interface know this has been updated */
436 p_parent->i_serial++;
441 * Deletes an item from the children of a node
443 * \param p_playlist the playlist
444 * \param p_item the item to remove
445 * \param p_parent the parent node
446 * \return VLC_SUCCESS or an error
448 int playlist_NodeRemoveItem( playlist_t *p_playlist,
449 playlist_item_t *p_item,
450 playlist_item_t *p_parent )
453 if( !p_parent || p_parent->i_children == -1 )
455 msg_Err( p_playlist, "invalid node" );
458 for( i= 0; i< p_parent->i_children ; i++ )
460 if( p_parent->pp_children[i] == p_item )
462 REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
466 /* Let the interface know this has been updated */
467 p_parent->i_serial++;
474 * Count the children of a node
476 * \param p_playlist the playlist
477 * \param p_node the node
478 * \return the number of children
480 int playlist_NodeChildrenCount( playlist_t *p_playlist, playlist_item_t*p_node)
484 if( p_node->i_children == -1 )
489 for( i=0 ; i< p_node->i_children;i++ )
491 if( p_node->pp_children[i]->i_children == -1 )
497 i_nb += playlist_NodeChildrenCount( p_playlist,
498 p_node->pp_children[i] );
505 * Search a child of a node by its name
507 * \param p_node the node
508 * \param psz_search the name of the child to search
509 * \return the child item or NULL if not found or error
511 playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
512 const char *psz_search )
516 if( p_node->i_children < 0 )
520 for( i = 0 ; i< p_node->i_children; i++ )
522 if( !strcmp( p_node->pp_children[i]->input.psz_name, psz_search ) )
524 return p_node->pp_children[i];
531 /**********************************************************************
533 **********************************************************************/
536 * Finds the next item to play
538 * \param p_playlist the playlist
539 * \param i_view the view
540 * \param p_root the root node
541 * \param p_node the node we are playing from
542 * \param p_item the item we were playing (NULL if none )
543 * \return the next item to play, or NULL if none found
545 playlist_item_t *playlist_FindNextFromParent( playlist_t *p_playlist,
546 int i_view, /* FIXME: useless */
547 playlist_item_t *p_root,
548 playlist_item_t *p_node,
549 playlist_item_t *p_item )
551 playlist_item_t *p_search, *p_next;
553 #ifdef PLAYLIST_DEBUG
556 msg_Dbg( p_playlist, "finding next of %s within %s",
557 p_item->input.psz_name, p_node->input.psz_name );
561 msg_Dbg( p_playlist, "finding something to play within %s",
562 p_node->input.psz_name );
567 if( !p_node || p_node->i_children == -1 )
569 msg_Err( p_playlist,"invalid arguments for FindNextFromParent" );
573 /* Find the parent node of the item */
576 p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
577 if( p_search == NULL )
579 msg_Err( p_playlist, "parent node not found" );
588 /* Now, go up the tree until we find a suitable next item */
589 p_next = playlist_RecursiveFindNext( p_playlist,i_view,
590 p_node, p_item, p_search );
592 /* Not found, do we go past p_node ? */
595 if( p_playlist->b_go_next )
597 p_next = playlist_RecursiveFindNext( p_playlist, i_view,
598 p_root, p_item, p_search );
603 /* OK, we could continue, so set our current node to the root */
604 p_playlist->status.p_node = p_root;
615 * Finds the previous item to play
617 * \param p_playlist the playlist
618 * \param i_view the view
619 * \param p_root the root node
620 * \param p_node the node we are playing from
621 * \param p_item the item we were playing (NULL if none )
622 * \return the next item to play, or NULL if none found
624 playlist_item_t *playlist_FindPrevFromParent( playlist_t *p_playlist,
626 playlist_item_t *p_root,
627 playlist_item_t *p_node,
628 playlist_item_t *p_item )
630 playlist_item_t *p_search, *p_next;
632 #ifdef PLAYLIST_DEBUG
635 msg_Dbg( p_playlist, "Finding prev of %s within %s",
636 p_item->input.psz_name, p_node->input.psz_name );
640 msg_Dbg( p_playlist, "Finding prev from %s",p_node->input.psz_name );
644 if( !p_node || p_node->i_children == -1 )
646 msg_Err( p_playlist,"invalid arguments for FindPrevFromParent" );
650 /* Find the parent node of the item */
653 p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
654 if( p_search == NULL )
656 msg_Err( p_playlist, "parent node not found" );
665 /* Now, go up the tree until we find a suitable next item */
666 p_next = playlist_RecursiveFindPrev( p_playlist,i_view,
667 p_node, p_item, p_search );
671 if( p_playlist->b_go_next )
673 p_next = playlist_RecursiveFindPrev( p_playlist, i_view,
674 p_root, p_item, p_search );
679 /* OK, we could continue, so set our current node to the root */
680 p_playlist->status.p_node = p_root;
690 /************************************************************************
691 * Following functions are local
692 ***********************************************************************/
695 /* Recursively search the tree for next item */
696 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
698 playlist_item_t *p_root,
699 playlist_item_t *p_item,
700 playlist_item_t *p_parent )
703 playlist_item_t *p_parent_parent;
705 for( i= 0 ; i < p_parent->i_children ; i++ )
707 if( p_parent->pp_children[i] == p_item || p_item == NULL )
713 #ifdef PLAYLIST_DEBUG
714 msg_Dbg( p_playlist,"Current item found, child %i of %s",
715 i , p_parent->input.psz_name );
717 /* We found our item */
718 if( i+1 >= p_parent->i_children )
721 #ifdef PLAYLIST_DEBUG
722 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
723 p_parent->input.psz_name );
725 if( p_parent == p_root )
727 /* Hmm, seems it's the end for you, guy ! */
731 /* Go up one level */
732 p_parent_parent = playlist_FindDirectParent( p_playlist,
734 if( p_parent_parent == NULL )
736 msg_Warn( p_playlist, "Unable to find parent !");
739 return playlist_RecursiveFindNext( p_playlist, i_view,p_root,
740 p_parent, p_parent_parent );
744 if( p_parent->pp_children[i+1]->i_children == -1 )
746 /* Cool, we have found a real item to play */
747 #ifdef PLAYLIST_DEBUG
748 msg_Dbg( p_playlist, "Playing child %i of %s",
749 i+1 , p_parent->input.psz_name );
751 return p_parent->pp_children[i+1];
753 else if( p_parent->pp_children[i+1]->i_children > 0 )
755 /* Select the first child of this node */
756 #ifdef PLAYLIST_DEBUG
757 msg_Dbg( p_playlist, "%s is a node with children, "
759 p_parent->pp_children[i+1]->input.psz_name);
761 if( p_parent->pp_children[i+1]->pp_children[0]
764 /* first child is a node ! */
765 return playlist_RecursiveFindNext( p_playlist, i_view,
767 p_parent->pp_children[i+1]->pp_children[0]);
769 return p_parent->pp_children[i+1]->pp_children[0];
773 /* This node has no child... We must continue */
774 #ifdef PLAYLIST_DEBUG
775 msg_Dbg( p_playlist, "%s is a node with no children",
776 p_parent->pp_children[i+1]->input.psz_name);
778 p_item = p_parent->pp_children[i+1];
783 /* Just in case :) */
787 /* Recursively search the tree for previous item */
788 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
790 playlist_item_t *p_root,
791 playlist_item_t *p_item,
792 playlist_item_t *p_parent )
795 playlist_item_t *p_parent_parent;
797 for( i= p_parent->i_children - 1 ; i >= 0 ; i-- )
799 if( p_parent->pp_children[i] == p_item || p_item == NULL )
805 #ifdef PLAYLIST_DEBUG
806 msg_Dbg( p_playlist,"Current item found, child %i of %s",
807 i , p_parent->input.psz_name );
809 /* We found our item */
813 #ifdef PLAYLIST_DEBUG
814 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
815 p_parent->input.psz_name );
817 if( p_parent == p_root )
819 /* Hmm, seems it's the end for you, guy ! */
822 /* Go up one level */
823 p_parent_parent = playlist_FindDirectParent( p_playlist,
825 return playlist_RecursiveFindPrev( p_playlist, i_view,p_root,
826 p_parent, p_parent_parent );
830 if( p_parent->pp_children[i-1]->i_children == -1 )
832 /* Cool, we have found a real item to play */
833 #ifdef PLAYLIST_DEBUG
834 msg_Dbg( p_playlist, "Playing child %i of %s",
835 i-1, p_parent->input.psz_name );
837 return p_parent->pp_children[i-1];
839 else if( p_parent->pp_children[i-1]->i_children > 0 )
841 /* Select the last child of this node */
842 #ifdef PLAYLIST_DEBUG
843 msg_Dbg( p_playlist, "%s is a node with children,"
845 p_parent->pp_children[i-1]->input.psz_name);
847 if( p_parent->pp_children[i-1]->pp_children[p_parent->
848 pp_children[i-1]->i_children-1]->i_children >= 0 )
850 /* Last child is a node */
851 return playlist_RecursiveFindPrev( p_playlist, i_view,
853 p_parent->pp_children[i-1]->pp_children[
854 p_parent->pp_children[i-1]->i_children-1]);
856 return p_parent->pp_children[i-1]->pp_children[
857 p_parent->pp_children[i-1]->i_children-1];
861 /* This node has no child... We must continue */
862 #ifdef PLAYLIST_DEBUG
863 msg_Dbg( p_playlist, "%s is a node with no children",
864 p_parent->pp_children[i-1]->input.psz_name);
866 p_item = p_parent->pp_children[i-1];
874 /* This function returns the parent of an item in a view */
875 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
876 playlist_item_t *p_item,
880 for( i= 0; i< p_item->i_parents ; i++ )
882 if( p_item->pp_parents[i]->i_view == i_view )
884 return p_item->pp_parents[i]->p_parent;
891 /* This function dumps a node */
892 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
900 msg_Dbg( p_playlist, "%s (%i)",
901 p_item->input.psz_name, p_item->i_children );
904 if( p_item->i_children == -1 )
909 for( i = 0; i< p_item->i_children; i++ )
911 memset( str, 32, 512 );
912 sprintf( str + 2 * i_level , "%s (%i)",
913 p_item->pp_children[i]->input.psz_name,
914 p_item->pp_children[i]->i_children );
915 msg_Dbg( p_playlist, "%s",str );
916 if( p_item->pp_children[i]->i_children >= 0 )
918 playlist_NodeDump( p_playlist, p_item->pp_children[i],