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->pp_parents = NULL;
292 p_item->i_parents = 0;
294 p_item->i_flags |= PLAYLIST_SKIP_FLAG; /* Default behaviour */
296 vlc_mutex_init( p_playlist, &p_item->input.lock );
298 if( p_parent != NULL )
300 playlist_NodeAppend( p_playlist, i_view, p_item, p_parent );
303 p_add->p_node = p_parent;
304 p_add->p_item = p_item;
305 p_add->i_view = i_view;
306 val.p_address = p_add;
307 var_Set( p_playlist, "item-append", val);
315 * Remove all the children of a node
317 * This function must be entered with the playlist lock
319 * \param p_playlist the playlist
320 * \param p_root the node
321 * \param b_delete_items do we have to delete the children items ?
322 * \return VLC_SUCCESS or an error
324 int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
325 vlc_bool_t b_delete_items )
328 if( p_root->i_children == -1 )
333 /* Delete the children */
334 for( i = p_root->i_children-1 ; i >= 0 ;i-- )
336 if( p_root->pp_children[i]->i_children > -1 )
338 playlist_NodeDelete( p_playlist, p_root->pp_children[i],
341 else if( b_delete_items )
343 /* Delete the item here */
344 playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
351 * Remove all the children of a node and removes the node
353 * \param p_playlist the playlist
354 * \param p_root the node
355 * \param b_delete_items do we have to delete the children items ?
356 * \return VLC_SUCCESS or an error
358 int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
359 vlc_bool_t b_delete_items )
361 return playlist_NodeDeleteInternal( p_playlist, p_root,
362 b_delete_items, VLC_FALSE );
365 int playlist_NodeDeleteInternal( playlist_t *p_playlist,
366 playlist_item_t *p_root,
367 vlc_bool_t b_delete_items, vlc_bool_t b_force )
370 if( p_root->i_children == -1 )
375 /* Delete the children */
376 for( i = p_root->i_children - 1 ; i >= 0; i-- )
378 if( p_root->pp_children[i]->i_children > -1 )
380 playlist_NodeDelete( p_playlist, p_root->pp_children[i],
383 else if( b_delete_items )
385 /* Delete the item here */
386 playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
389 /* Delete the node */
390 if( p_root->i_flags & PLAYLIST_RO_FLAG && !b_force )
395 for( i = 0 ; i< p_root->i_parents; i++ )
397 playlist_NodeRemoveItem( p_playlist, p_root,
398 p_root->pp_parents[i]->p_parent );
400 var_SetInteger( p_playlist, "item-deleted", p_root->input.i_id );
401 playlist_ItemDelete( p_root );
409 * Adds an item to the childs of a node
411 * \param p_playlist the playlist
412 * \param i_view the view of the node ( needed for parent search )
413 * \param p_item the item to append
414 * \param p_parent the parent node
415 * \return VLC_SUCCESS or an error
417 int playlist_NodeAppend( playlist_t *p_playlist,
419 playlist_item_t *p_item,
420 playlist_item_t *p_parent )
422 return playlist_NodeInsert( p_playlist, i_view, p_item, p_parent, -1 );
425 int playlist_NodeInsert( playlist_t *p_playlist,
427 playlist_item_t *p_item,
428 playlist_item_t *p_parent,
432 vlc_bool_t b_found = VLC_FALSE;
433 if( !p_parent || p_parent->i_children == -1 )
435 msg_Err( p_playlist, "invalid node" );
439 if( i_position == -1 ) i_position = p_parent->i_children ;
441 INSERT_ELEM( p_parent->pp_children,
442 p_parent->i_children,
446 /* Add the parent to the array */
447 for( i= 0; i< p_item->i_parents ; i++ )
449 if( p_item->pp_parents[i]->i_view == i_view )
455 if( b_found == VLC_FALSE )
457 struct item_parent_t *p_ip = (struct item_parent_t *)
458 malloc(sizeof(struct item_parent_t) );
459 p_ip->i_view = i_view;
460 p_ip->p_parent = p_parent;
462 INSERT_ELEM( p_item->pp_parents,
463 p_item->i_parents, p_item->i_parents,
467 /* Let the interface know this has been updated */
468 p_parent->i_serial++;
473 * Deletes an item from the children of a node
475 * \param p_playlist the playlist
476 * \param p_item the item to remove
477 * \param p_parent the parent node
478 * \return VLC_SUCCESS or an error
480 int playlist_NodeRemoveItem( playlist_t *p_playlist,
481 playlist_item_t *p_item,
482 playlist_item_t *p_parent )
485 if( !p_parent || p_parent->i_children == -1 )
487 msg_Err( p_playlist, "invalid node" );
490 for( i= 0; i< p_parent->i_children ; i++ )
492 if( p_parent->pp_children[i] == p_item )
494 REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
498 /* Let the interface know this has been updated */
499 p_parent->i_serial++;
506 * Count the children of a node
508 * \param p_playlist the playlist
509 * \param p_node the node
510 * \return the number of children
512 int playlist_NodeChildrenCount( playlist_t *p_playlist, playlist_item_t*p_node)
516 if( p_node->i_children == -1 )
521 for( i=0 ; i< p_node->i_children;i++ )
523 if( p_node->pp_children[i]->i_children == -1 )
529 i_nb += playlist_NodeChildrenCount( p_playlist,
530 p_node->pp_children[i] );
537 * Search a child of a node by its name
539 * \param p_node the node
540 * \param psz_search the name of the child to search
541 * \return the child item or NULL if not found or error
543 playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
544 const char *psz_search )
548 if( p_node->i_children < 0 )
552 for( i = 0 ; i< p_node->i_children; i++ )
554 if( !strcmp( p_node->pp_children[i]->input.psz_name, psz_search ) )
556 return p_node->pp_children[i];
563 /**********************************************************************
565 **********************************************************************/
568 * Finds the next item to play
570 * \param p_playlist the playlist
571 * \param i_view the view
572 * \param p_root the root node
573 * \param p_node the node we are playing from
574 * \param p_item the item we were playing (NULL if none )
575 * \return the next item to play, or NULL if none found
577 playlist_item_t *playlist_FindNextFromParent( playlist_t *p_playlist,
578 int i_view, /* FIXME: useless */
579 playlist_item_t *p_root,
580 playlist_item_t *p_node,
581 playlist_item_t *p_item )
583 playlist_item_t *p_search, *p_next;
585 #ifdef PLAYLIST_DEBUG
588 msg_Dbg( p_playlist, "finding next of %s within %s",
589 p_item->input.psz_name, p_node->input.psz_name );
593 msg_Dbg( p_playlist, "finding something to play within %s",
594 p_node->input.psz_name );
599 if( !p_node || p_node->i_children == -1 )
601 msg_Err( p_playlist,"invalid arguments for FindNextFromParent" );
605 /* Find the parent node of the item */
608 p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
609 if( p_search == NULL )
611 msg_Err( p_playlist, "parent node not found" );
620 /* Now, go up the tree until we find a suitable next item */
621 p_next = playlist_RecursiveFindNext( p_playlist,i_view,
622 p_node, p_item, p_search );
624 /* Not found, do we go past p_node ? */
627 if( p_playlist->b_go_next )
629 p_next = playlist_RecursiveFindNext( p_playlist, i_view,
630 p_root, p_item, p_search );
635 /* OK, we could continue, so set our current node to the root */
636 p_playlist->status.p_node = p_root;
647 * Finds the previous item to play
649 * \param p_playlist the playlist
650 * \param i_view the view
651 * \param p_root the root node
652 * \param p_node the node we are playing from
653 * \param p_item the item we were playing (NULL if none )
654 * \return the next item to play, or NULL if none found
656 playlist_item_t *playlist_FindPrevFromParent( playlist_t *p_playlist,
658 playlist_item_t *p_root,
659 playlist_item_t *p_node,
660 playlist_item_t *p_item )
662 playlist_item_t *p_search, *p_next;
664 #ifdef PLAYLIST_DEBUG
667 msg_Dbg( p_playlist, "Finding prev of %s within %s",
668 p_item->input.psz_name, p_node->input.psz_name );
672 msg_Dbg( p_playlist, "Finding prev from %s",p_node->input.psz_name );
676 if( !p_node || p_node->i_children == -1 )
678 msg_Err( p_playlist,"invalid arguments for FindPrevFromParent" );
682 /* Find the parent node of the item */
685 p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
686 if( p_search == NULL )
688 msg_Err( p_playlist, "parent node not found" );
697 /* Now, go up the tree until we find a suitable next item */
698 p_next = playlist_RecursiveFindPrev( p_playlist,i_view,
699 p_node, p_item, p_search );
703 if( p_playlist->b_go_next )
705 p_next = playlist_RecursiveFindPrev( p_playlist, i_view,
706 p_root, p_item, p_search );
711 /* OK, we could continue, so set our current node to the root */
712 p_playlist->status.p_node = p_root;
722 /************************************************************************
723 * Following functions are local
724 ***********************************************************************/
727 /* Recursively search the tree for next item */
728 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
730 playlist_item_t *p_root,
731 playlist_item_t *p_item,
732 playlist_item_t *p_parent )
735 playlist_item_t *p_parent_parent;
737 for( i= 0 ; i < p_parent->i_children ; i++ )
739 if( p_parent->pp_children[i] == p_item || p_item == NULL )
745 #ifdef PLAYLIST_DEBUG
746 msg_Dbg( p_playlist,"Current item found, child %i of %s",
747 i , p_parent->input.psz_name );
749 /* We found our item */
750 if( i+1 >= p_parent->i_children )
753 #ifdef PLAYLIST_DEBUG
754 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
755 p_parent->input.psz_name );
757 if( p_parent == p_root )
759 /* Hmm, seems it's the end for you, guy ! */
763 /* Go up one level */
764 p_parent_parent = playlist_FindDirectParent( p_playlist,
766 if( p_parent_parent == NULL )
768 msg_Warn( p_playlist, "Unable to find parent !");
771 return playlist_RecursiveFindNext( p_playlist, i_view,p_root,
772 p_parent, p_parent_parent );
776 if( p_parent->pp_children[i+1]->i_children == -1 )
778 /* Cool, we have found a real item to play */
779 #ifdef PLAYLIST_DEBUG
780 msg_Dbg( p_playlist, "Playing child %i of %s",
781 i+1 , p_parent->input.psz_name );
783 return p_parent->pp_children[i+1];
785 else if( p_parent->pp_children[i+1]->i_children > 0 )
787 /* Select the first child of this node */
788 #ifdef PLAYLIST_DEBUG
789 msg_Dbg( p_playlist, "%s is a node with children, "
791 p_parent->pp_children[i+1]->input.psz_name);
793 if( p_parent->pp_children[i+1]->pp_children[0]
796 /* first child is a node ! */
797 return playlist_RecursiveFindNext( p_playlist, i_view,
799 p_parent->pp_children[i+1]->pp_children[0]);
801 return p_parent->pp_children[i+1]->pp_children[0];
805 /* This node has no child... We must continue */
806 #ifdef PLAYLIST_DEBUG
807 msg_Dbg( p_playlist, "%s is a node with no children",
808 p_parent->pp_children[i+1]->input.psz_name);
810 p_item = p_parent->pp_children[i+1];
815 /* Just in case :) */
819 /* Recursively search the tree for previous item */
820 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
822 playlist_item_t *p_root,
823 playlist_item_t *p_item,
824 playlist_item_t *p_parent )
827 playlist_item_t *p_parent_parent;
829 for( i= p_parent->i_children - 1 ; i >= 0 ; i-- )
831 if( p_parent->pp_children[i] == p_item || p_item == NULL )
837 #ifdef PLAYLIST_DEBUG
838 msg_Dbg( p_playlist,"Current item found, child %i of %s",
839 i , p_parent->input.psz_name );
841 /* We found our item */
845 #ifdef PLAYLIST_DEBUG
846 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
847 p_parent->input.psz_name );
849 if( p_parent == p_root )
851 /* Hmm, seems it's the end for you, guy ! */
854 /* Go up one level */
855 p_parent_parent = playlist_FindDirectParent( p_playlist,
857 return playlist_RecursiveFindPrev( p_playlist, i_view,p_root,
858 p_parent, p_parent_parent );
862 if( p_parent->pp_children[i-1]->i_children == -1 )
864 /* Cool, we have found a real item to play */
865 #ifdef PLAYLIST_DEBUG
866 msg_Dbg( p_playlist, "Playing child %i of %s",
867 i-1, p_parent->input.psz_name );
869 return p_parent->pp_children[i-1];
871 else if( p_parent->pp_children[i-1]->i_children > 0 )
873 /* Select the last child of this node */
874 #ifdef PLAYLIST_DEBUG
875 msg_Dbg( p_playlist, "%s is a node with children,"
877 p_parent->pp_children[i-1]->input.psz_name);
879 if( p_parent->pp_children[i-1]->pp_children[p_parent->
880 pp_children[i-1]->i_children-1]->i_children >= 0 )
882 /* Last child is a node */
883 return playlist_RecursiveFindPrev( p_playlist, i_view,
885 p_parent->pp_children[i-1]->pp_children[
886 p_parent->pp_children[i-1]->i_children-1]);
888 return p_parent->pp_children[i-1]->pp_children[
889 p_parent->pp_children[i-1]->i_children-1];
893 /* This node has no child... We must continue */
894 #ifdef PLAYLIST_DEBUG
895 msg_Dbg( p_playlist, "%s is a node with no children",
896 p_parent->pp_children[i-1]->input.psz_name);
898 p_item = p_parent->pp_children[i-1];
906 /* This function returns the parent of an item in a view */
907 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
908 playlist_item_t *p_item,
912 for( i= 0; i< p_item->i_parents ; i++ )
914 if( p_item->pp_parents[i]->i_view == i_view )
916 return p_item->pp_parents[i]->p_parent;
923 /* This function dumps a node */
924 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
932 msg_Dbg( p_playlist, "%s (%i)",
933 p_item->input.psz_name, p_item->i_children );
936 if( p_item->i_children == -1 )
941 for( i = 0; i< p_item->i_children; i++ )
943 memset( str, 32, 512 );
944 sprintf( str + 2 * i_level , "%s (%i)",
945 p_item->pp_children[i]->input.psz_name,
946 p_item->pp_children[i]->i_children );
947 msg_Dbg( p_playlist, "%s",str );
948 if( p_item->pp_children[i]->i_children >= 0 )
950 playlist_NodeDump( p_playlist, p_item->pp_children[i],