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 int playlist_NodeDeleteInternal( playlist_t *p_playlist,
58 playlist_item_t *p_root,
59 vlc_bool_t b_delete_items, vlc_bool_t b_force );
62 /**********************************************************************
63 * Exported View management functions
64 **********************************************************************/
69 * \param p_playlist a playlist object
70 * \param i_id the view identifier
71 * \return the new view or NULL on failure
73 playlist_view_t * playlist_ViewCreate( playlist_t *p_playlist, int i_id,
76 playlist_view_t * p_view;
78 msg_Dbg( p_playlist, "creating view %i",i_id );
80 p_view = malloc( sizeof( playlist_view_t ) );
82 memset( p_view, 0, sizeof( playlist_view_t ) );
84 p_view->p_root = playlist_NodeCreate( p_playlist, i_id, NULL, NULL );
86 p_view->psz_name = psz_name ? strdup( psz_name ) : strdup(_("Undefined") );
92 * Creates a new view and add it to the list
94 * This function must be entered without the playlist lock
96 * \param p_playlist a playlist object
97 * \param i_id the view identifier
98 * \return VLC_SUCCESS or an error
100 int playlist_ViewInsert( playlist_t *p_playlist, int i_id, char *psz_name )
102 playlist_view_t *p_view =
103 playlist_ViewCreate( p_playlist, i_id , psz_name );
106 msg_Err( p_playlist, "Creation failed" );
110 vlc_mutex_lock( &p_playlist->object_lock );
112 INSERT_ELEM( p_playlist->pp_views, p_playlist->i_views,
113 p_playlist->i_views, p_view );
115 vlc_mutex_unlock( &p_playlist->object_lock );
123 * This function must be entered wit the playlist lock
125 * \param p_view the view to delete
128 int playlist_ViewDelete( playlist_t *p_playlist,playlist_view_t *p_view )
130 playlist_NodeDeleteInternal( p_playlist, p_view->p_root, VLC_TRUE,
132 REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, 0 );
137 * Dumps the content of a view
139 * \param p_playlist the playlist
140 * \param p_view the view to dump
143 int playlist_ViewDump( playlist_t *p_playlist, playlist_view_t *p_view )
145 msg_Dbg( p_playlist, "dumping view %i",p_view->i_id );
146 playlist_NodeDump( p_playlist,p_view->p_root, 1 );
151 * Counts the items of a view
153 * \param p_playlist the playlist
154 * \param p_view the view to count
155 * \return the number of items
157 int playlist_ViewItemCount( playlist_t *p_playlist,
158 playlist_view_t *p_view )
160 return playlist_NodeChildrenCount( p_playlist, p_view->p_root );
165 * Updates a view. Only make sense for "sorted" and "ALL" views
167 * \param p_playlist the playlist
168 * \param i_view the view to update
171 int playlist_ViewUpdate( playlist_t *p_playlist, int i_view)
173 playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_view );
180 if( i_view == VIEW_ALL )
182 p_view->p_root->i_children = p_playlist->i_size;
183 p_view->p_root->pp_children = p_playlist->pp_items;
186 /* Handle update of sorted views here */
187 if( i_view == VIEW_S_AUTHOR )
189 playlist_ViewEmpty( p_playlist, i_view, VLC_FALSE );
191 playlist_NodeGroup( p_playlist, i_view, p_view->p_root,
192 p_playlist->pp_items,p_playlist->i_size,
193 SORT_AUTHOR, ORDER_NORMAL );
204 * \param p_playlist the playlist
205 * \param i_id the id to find
206 * \return the found view or NULL if not found
208 playlist_view_t *playlist_ViewFind( playlist_t *p_playlist, int i_id )
211 for( i=0 ; i< p_playlist->i_views ; i++ )
213 if( p_playlist->pp_views[i]->i_id == i_id )
215 return p_playlist->pp_views[i];
222 int playlist_ViewEmpty( playlist_t *p_playlist, int i_view,
223 vlc_bool_t b_delete_items )
225 playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_view );
232 return playlist_NodeEmpty( p_playlist, p_view->p_root, b_delete_items );
235 /**********************************************************************
236 * Exported Nodes management functions
237 **********************************************************************/
242 * Create a playlist node
244 * \param p_playlist the playlist
245 * \paam psz_name the name of the node
246 * \param p_parent the parent node to attach to or NULL if no attach
247 * \return the new node
249 playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist, int i_view,
251 playlist_item_t *p_parent )
253 /* Create the item */
254 playlist_item_t *p_item = (playlist_item_t *)malloc(
255 sizeof( playlist_item_t ) );
257 playlist_add_t *p_add = (playlist_add_t*)malloc( sizeof(playlist_add_t));
259 vlc_input_item_Init( VLC_OBJECT(p_playlist), &p_item->input );
265 if( psz_name != NULL )
267 p_item->input.psz_name = strdup( psz_name );
271 p_item->input.psz_name = strdup( _("Undefined") );
274 p_item->input.psz_uri = NULL;
276 p_item->b_enabled = VLC_TRUE;
277 p_item->i_nb_played = 0;
281 p_item->i_children = 0;
282 p_item->pp_children = NULL;
284 p_item->input.i_duration = -1;
285 p_item->input.ppsz_options = NULL;
286 p_item->input.i_options = 0;
287 p_item->input.i_categories = 0;
288 p_item->input.pp_categories = NULL;
289 p_item->input.i_id = ++p_playlist->i_last_id;
291 p_item->input.i_type = ITEM_TYPE_NODE;
293 p_item->pp_parents = NULL;
294 p_item->i_parents = 0;
296 p_item->i_flags |= PLAYLIST_SKIP_FLAG; /* Default behaviour */
298 vlc_mutex_init( p_playlist, &p_item->input.lock );
300 if( p_parent != NULL )
302 playlist_NodeAppend( p_playlist, i_view, p_item, p_parent );
305 p_add->p_node = p_parent;
306 p_add->p_item = p_item;
307 p_add->i_view = i_view;
308 val.p_address = p_add;
309 var_Set( p_playlist, "item-append", val);
317 * Remove all the children of a node
319 * This function must be entered with the playlist lock
321 * \param p_playlist the playlist
322 * \param p_root the node
323 * \param b_delete_items do we have to delete the children items ?
324 * \return VLC_SUCCESS or an error
326 int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
327 vlc_bool_t b_delete_items )
330 if( p_root->i_children == -1 )
335 /* Delete the children */
336 for( i = p_root->i_children-1 ; i >= 0 ;i-- )
338 if( p_root->pp_children[i]->i_children > -1 )
340 playlist_NodeDelete( p_playlist, p_root->pp_children[i],
343 else if( b_delete_items )
345 /* Delete the item here */
346 playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
353 * Remove all the children of a node and removes the node
355 * \param p_playlist the playlist
356 * \param p_root the node
357 * \param b_delete_items do we have to delete the children items ?
358 * \return VLC_SUCCESS or an error
360 int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
361 vlc_bool_t b_delete_items )
363 return playlist_NodeDeleteInternal( p_playlist, p_root,
364 b_delete_items, VLC_FALSE );
367 int playlist_NodeDeleteInternal( playlist_t *p_playlist,
368 playlist_item_t *p_root,
369 vlc_bool_t b_delete_items, vlc_bool_t b_force )
372 if( p_root->i_children == -1 )
377 /* Delete the children */
378 for( i = p_root->i_children - 1 ; i >= 0; i-- )
380 if( p_root->pp_children[i]->i_children > -1 )
382 playlist_NodeDelete( p_playlist, p_root->pp_children[i],
385 else if( b_delete_items )
387 /* Delete the item here */
388 playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
391 /* Delete the node */
392 if( p_root->i_flags & PLAYLIST_RO_FLAG && !b_force )
397 for( i = 0 ; i< p_root->i_parents; i++ )
399 playlist_NodeRemoveItem( p_playlist, p_root,
400 p_root->pp_parents[i]->p_parent );
402 var_SetInteger( p_playlist, "item-deleted", p_root->input.i_id );
403 playlist_ItemDelete( p_root );
411 * Adds an item to the childs of a node
413 * \param p_playlist the playlist
414 * \param i_view the view of the node ( needed for parent search )
415 * \param p_item the item to append
416 * \param p_parent the parent node
417 * \return VLC_SUCCESS or an error
419 int playlist_NodeAppend( playlist_t *p_playlist,
421 playlist_item_t *p_item,
422 playlist_item_t *p_parent )
424 return playlist_NodeInsert( p_playlist, i_view, p_item, p_parent, -1 );
427 int playlist_NodeInsert( playlist_t *p_playlist,
429 playlist_item_t *p_item,
430 playlist_item_t *p_parent,
434 vlc_bool_t b_found = VLC_FALSE;
435 if( !p_parent || p_parent->i_children == -1 )
437 msg_Err( p_playlist, "invalid node" );
441 if( i_position == -1 ) i_position = p_parent->i_children ;
443 INSERT_ELEM( p_parent->pp_children,
444 p_parent->i_children,
448 /* Add the parent to the array */
449 for( i= 0; i< p_item->i_parents ; i++ )
451 if( p_item->pp_parents[i]->i_view == i_view )
457 if( b_found == VLC_FALSE )
459 struct item_parent_t *p_ip = (struct item_parent_t *)
460 malloc(sizeof(struct item_parent_t) );
461 p_ip->i_view = i_view;
462 p_ip->p_parent = p_parent;
464 INSERT_ELEM( p_item->pp_parents,
465 p_item->i_parents, p_item->i_parents,
469 /* Let the interface know this has been updated */
470 p_parent->i_serial++;
475 * Deletes an item from the children of a node
477 * \param p_playlist the playlist
478 * \param p_item the item to remove
479 * \param p_parent the parent node
480 * \return VLC_SUCCESS or an error
482 int playlist_NodeRemoveItem( playlist_t *p_playlist,
483 playlist_item_t *p_item,
484 playlist_item_t *p_parent )
487 if( !p_parent || p_parent->i_children == -1 )
489 msg_Err( p_playlist, "invalid node" );
492 for( i= 0; i< p_parent->i_children ; i++ )
494 if( p_parent->pp_children[i] == p_item )
496 REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
500 /* Let the interface know this has been updated */
501 p_parent->i_serial++;
508 * Count the children of a node
510 * \param p_playlist the playlist
511 * \param p_node the node
512 * \return the number of children
514 int playlist_NodeChildrenCount( playlist_t *p_playlist, playlist_item_t*p_node)
518 if( p_node->i_children == -1 )
523 for( i=0 ; i< p_node->i_children;i++ )
525 if( p_node->pp_children[i]->i_children == -1 )
531 i_nb += playlist_NodeChildrenCount( p_playlist,
532 p_node->pp_children[i] );
539 * Search a child of a node by its name
541 * \param p_node the node
542 * \param psz_search the name of the child to search
543 * \return the child item or NULL if not found or error
545 playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
546 const char *psz_search )
550 if( p_node->i_children < 0 )
554 for( i = 0 ; i< p_node->i_children; i++ )
556 if( !strcmp( p_node->pp_children[i]->input.psz_name, psz_search ) )
558 return p_node->pp_children[i];
565 /**********************************************************************
567 **********************************************************************/
570 * Finds the next item to play
572 * \param p_playlist the playlist
573 * \param i_view the view
574 * \param p_root the root node
575 * \param p_node the node we are playing from
576 * \param p_item the item we were playing (NULL if none )
577 * \return the next item to play, or NULL if none found
579 playlist_item_t *playlist_FindNextFromParent( playlist_t *p_playlist,
580 int i_view, /* FIXME: useless */
581 playlist_item_t *p_root,
582 playlist_item_t *p_node,
583 playlist_item_t *p_item )
585 playlist_item_t *p_search, *p_next;
587 #ifdef PLAYLIST_DEBUG
590 msg_Dbg( p_playlist, "finding next of %s within %s",
591 p_item->input.psz_name, p_node->input.psz_name );
595 msg_Dbg( p_playlist, "finding something to play within %s",
596 p_node->input.psz_name );
601 if( !p_node || p_node->i_children == -1 )
603 msg_Err( p_playlist,"invalid arguments for FindNextFromParent" );
607 /* Find the parent node of the item */
610 p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
611 if( p_search == NULL )
613 msg_Err( p_playlist, "parent node not found" );
622 /* Now, go up the tree until we find a suitable next item */
623 p_next = playlist_RecursiveFindNext( p_playlist,i_view,
624 p_node, p_item, p_search );
626 /* Not found, do we go past p_node ? */
629 if( p_playlist->b_go_next )
631 p_next = playlist_RecursiveFindNext( p_playlist, i_view,
632 p_root, p_item, p_search );
637 /* OK, we could continue, so set our current node to the root */
638 p_playlist->status.p_node = p_root;
649 * Finds the previous item to play
651 * \param p_playlist the playlist
652 * \param i_view the view
653 * \param p_root the root node
654 * \param p_node the node we are playing from
655 * \param p_item the item we were playing (NULL if none )
656 * \return the next item to play, or NULL if none found
658 playlist_item_t *playlist_FindPrevFromParent( playlist_t *p_playlist,
660 playlist_item_t *p_root,
661 playlist_item_t *p_node,
662 playlist_item_t *p_item )
664 playlist_item_t *p_search, *p_next;
666 #ifdef PLAYLIST_DEBUG
669 msg_Dbg( p_playlist, "Finding prev of %s within %s",
670 p_item->input.psz_name, p_node->input.psz_name );
674 msg_Dbg( p_playlist, "Finding prev from %s",p_node->input.psz_name );
678 if( !p_node || p_node->i_children == -1 )
680 msg_Err( p_playlist,"invalid arguments for FindPrevFromParent" );
684 /* Find the parent node of the item */
687 p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
688 if( p_search == NULL )
690 msg_Err( p_playlist, "parent node not found" );
699 /* Now, go up the tree until we find a suitable next item */
700 p_next = playlist_RecursiveFindPrev( p_playlist,i_view,
701 p_node, p_item, p_search );
705 if( p_playlist->b_go_next )
707 p_next = playlist_RecursiveFindPrev( p_playlist, i_view,
708 p_root, p_item, p_search );
713 /* OK, we could continue, so set our current node to the root */
714 p_playlist->status.p_node = p_root;
724 /************************************************************************
725 * Following functions are local
726 ***********************************************************************/
729 /* Recursively search the tree for next item */
730 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
732 playlist_item_t *p_root,
733 playlist_item_t *p_item,
734 playlist_item_t *p_parent )
737 playlist_item_t *p_parent_parent;
739 for( i= 0 ; i < p_parent->i_children ; i++ )
741 if( p_parent->pp_children[i] == p_item || p_item == NULL )
747 #ifdef PLAYLIST_DEBUG
748 msg_Dbg( p_playlist,"Current item found, child %i of %s",
749 i , p_parent->input.psz_name );
751 /* We found our item */
752 if( i+1 >= p_parent->i_children )
755 #ifdef PLAYLIST_DEBUG
756 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
757 p_parent->input.psz_name );
759 if( p_parent == p_root )
761 /* Hmm, seems it's the end for you, guy ! */
765 /* Go up one level */
766 p_parent_parent = playlist_FindDirectParent( p_playlist,
768 if( p_parent_parent == NULL )
770 msg_Warn( p_playlist, "Unable to find parent !");
773 return playlist_RecursiveFindNext( p_playlist, i_view,p_root,
774 p_parent, p_parent_parent );
778 if( p_parent->pp_children[i+1]->i_children == -1 )
780 /* Cool, we have found a real item to play */
781 #ifdef PLAYLIST_DEBUG
782 msg_Dbg( p_playlist, "Playing child %i of %s",
783 i+1 , p_parent->input.psz_name );
785 return p_parent->pp_children[i+1];
787 else if( p_parent->pp_children[i+1]->i_children > 0 )
789 /* Select the first child of this node */
790 #ifdef PLAYLIST_DEBUG
791 msg_Dbg( p_playlist, "%s is a node with children, "
793 p_parent->pp_children[i+1]->input.psz_name);
795 if( p_parent->pp_children[i+1]->pp_children[0]
798 /* first child is a node ! */
799 return playlist_RecursiveFindNext( p_playlist, i_view,
801 p_parent->pp_children[i+1]->pp_children[0]);
803 return p_parent->pp_children[i+1]->pp_children[0];
807 /* This node has no child... We must continue */
808 #ifdef PLAYLIST_DEBUG
809 msg_Dbg( p_playlist, "%s is a node with no children",
810 p_parent->pp_children[i+1]->input.psz_name);
812 p_item = p_parent->pp_children[i+1];
817 /* Just in case :) */
821 /* Recursively search the tree for previous item */
822 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
824 playlist_item_t *p_root,
825 playlist_item_t *p_item,
826 playlist_item_t *p_parent )
829 playlist_item_t *p_parent_parent;
831 for( i= p_parent->i_children - 1 ; i >= 0 ; i-- )
833 if( p_parent->pp_children[i] == p_item || p_item == NULL )
839 #ifdef PLAYLIST_DEBUG
840 msg_Dbg( p_playlist,"Current item found, child %i of %s",
841 i , p_parent->input.psz_name );
843 /* We found our item */
847 #ifdef PLAYLIST_DEBUG
848 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
849 p_parent->input.psz_name );
851 if( p_parent == p_root )
853 /* Hmm, seems it's the end for you, guy ! */
856 /* Go up one level */
857 p_parent_parent = playlist_FindDirectParent( p_playlist,
859 return playlist_RecursiveFindPrev( p_playlist, i_view,p_root,
860 p_parent, p_parent_parent );
864 if( p_parent->pp_children[i-1]->i_children == -1 )
866 /* Cool, we have found a real item to play */
867 #ifdef PLAYLIST_DEBUG
868 msg_Dbg( p_playlist, "Playing child %i of %s",
869 i-1, p_parent->input.psz_name );
871 return p_parent->pp_children[i-1];
873 else if( p_parent->pp_children[i-1]->i_children > 0 )
875 /* Select the last child of this node */
876 #ifdef PLAYLIST_DEBUG
877 msg_Dbg( p_playlist, "%s is a node with children,"
879 p_parent->pp_children[i-1]->input.psz_name);
881 if( p_parent->pp_children[i-1]->pp_children[p_parent->
882 pp_children[i-1]->i_children-1]->i_children >= 0 )
884 /* Last child is a node */
885 return playlist_RecursiveFindPrev( p_playlist, i_view,
887 p_parent->pp_children[i-1]->pp_children[
888 p_parent->pp_children[i-1]->i_children-1]);
890 return p_parent->pp_children[i-1]->pp_children[
891 p_parent->pp_children[i-1]->i_children-1];
895 /* This node has no child... We must continue */
896 #ifdef PLAYLIST_DEBUG
897 msg_Dbg( p_playlist, "%s is a node with no children",
898 p_parent->pp_children[i-1]->input.psz_name);
900 p_item = p_parent->pp_children[i-1];
908 /* This function returns the parent of an item in a view */
909 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
910 playlist_item_t *p_item,
914 for( i= 0; i< p_item->i_parents ; i++ )
916 if( p_item->pp_parents[i]->i_view == i_view )
918 return p_item->pp_parents[i]->p_parent;
925 /* This function dumps a node */
926 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
934 msg_Dbg( p_playlist, "%s (%i)",
935 p_item->input.psz_name, p_item->i_children );
938 if( p_item->i_children == -1 )
943 for( i = 0; i< p_item->i_children; i++ )
945 memset( str, 32, 512 );
946 sprintf( str + 2 * i_level , "%s (%i)",
947 p_item->pp_children[i]->input.psz_name,
948 p_item->pp_children[i]->i_children );
949 msg_Dbg( p_playlist, "%s",str );
950 if( p_item->pp_children[i]->i_children >= 0 )
952 playlist_NodeDump( p_playlist, p_item->pp_children[i],