1 /*****************************************************************************
2 * gtk_playlist.c : Interface for the playlist dialog
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: gtk_playlist.c,v 1.32 2002/06/01 12:31:59 sam Exp $
7 * Authors: Pierre Baillet <oct@zoy.org>
8 * Stéphane Borel <stef@via.ecp.fr>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
34 #include <sys/types.h> /* for readdir and stat stuff */
36 #if (!defined( WIN32 ) || defined(__MINGW32__))
37 /* Mingw has its own version of dirent */
44 #ifdef MODULE_NAME_IS_gnome
50 #include "gtk_callbacks.h"
51 #include "gtk_interface.h"
52 #include "gtk_support.h"
53 #include "gtk_playlist.h"
54 #include "gtk_common.h"
56 /****************************************************************************
58 ****************************************************************************/
59 static void UrlDecode( char *encoded_path );
61 /****************************************************************************
62 * Playlist window management
63 ****************************************************************************/
64 gboolean GtkPlaylistShow( GtkWidget *widget,
65 GdkEventButton *event,
68 #if 0 /* PLAYLIST TARASS */
69 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(widget), (char*)user_data );
71 if( GTK_WIDGET_VISIBLE( p_intf->p_sys->p_playlist ) )
73 gtk_widget_hide( p_intf->p_sys->p_playlist );
79 p_clist = GTK_CLIST( gtk_object_get_data(
80 GTK_OBJECT( p_intf->p_sys->p_playlist ), "playlist_clist" ) );
81 GtkRebuildCList( p_clist , p_intf->p_vlc->p_playlist );
82 gtk_widget_show( p_intf->p_sys->p_playlist );
83 gdk_window_raise( p_intf->p_sys->p_playlist->window );
91 void GtkPlaylistOk( GtkButton * button, gpointer user_data )
93 gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
97 void GtkPlaylistCancel( GtkButton * button, gpointer user_data )
99 gtk_widget_hide( gtk_widget_get_toplevel( GTK_WIDGET (button) ) );
104 gboolean GtkPlaylistPrev( GtkWidget *widget,
105 GdkEventButton *event,
108 #if 0 /* PLAYLIST TARASS */
109 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(widget), (char*)user_data );
111 if( p_intf->p_sys->p_input != NULL )
113 /* FIXME: temporary hack */
114 intf_PlaylistPrev( p_intf->p_vlc->p_playlist );
115 intf_PlaylistPrev( p_intf->p_vlc->p_playlist );
116 p_intf->p_sys->p_input->b_eof = 1;
124 gboolean GtkPlaylistNext( GtkWidget *widget,
125 GdkEventButton *event,
128 #if 0 /* PLAYLIST TARASS */
129 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(widget), (char*)user_data );
131 if( p_intf->p_sys->p_input != NULL )
133 /* FIXME: temporary hack */
134 p_intf->p_sys->p_input->b_eof = 1;
141 /****************************************************************************
142 * Menu callbacks for playlist functions
143 ****************************************************************************/
144 void GtkPlaylistActivate( GtkMenuItem * menuitem, gpointer user_data )
146 GtkPlaylistShow( GTK_WIDGET( menuitem ), NULL, user_data );
150 void GtkNextActivate( GtkMenuItem * menuitem, gpointer user_data )
152 GtkPlaylistNext( GTK_WIDGET( menuitem ), NULL, user_data );
156 void GtkPrevActivate( GtkMenuItem * menuitem, gpointer user_data )
158 GtkPlaylistPrev( GTK_WIDGET( menuitem ), NULL, user_data );
162 /****************************************************************************
163 * Playlist core functions
164 ****************************************************************************/
165 void GtkPlaylistAddUrl( GtkMenuItem * menuitem, gpointer user_data )
171 void GtkPlaylistDeleteAll( GtkMenuItem * menuitem, gpointer user_data )
177 void GtkPlaylistDeleteSelected( GtkMenuItem * menuitem, gpointer user_data )
179 #if 0 /* PLAYLIST TARASS */
180 /* user wants to delete a file in the queue */
183 playlist_t *p_playlist;
185 /* catch the thread back */
186 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(menuitem), /*(char*)user_data*/"intf_playlist" );
188 p_playlist = p_intf->p_vlc->p_playlist;
190 /* lock the struct */
191 vlc_mutex_lock( &p_intf->change_lock );
193 p_clist = GTK_CLIST( gtk_object_get_data( GTK_OBJECT(
194 p_intf->p_sys->p_playlist ), "playlist_clist" ) );
196 /* I use UNDOCUMENTED features to retrieve the selection... */
197 p_selection = p_clist->selection;
199 if( g_list_length( p_selection ) > 0 )
201 /* reverse-sort so that we can delete from the furthest
202 * to the closest item to delete...
204 p_selection = g_list_sort( p_selection, GtkCompareItems );
205 g_list_foreach( p_selection, GtkDeleteGListItem, p_intf );
206 /* rebuild the CList */
207 GtkRebuildCList( p_clist, p_playlist );
210 vlc_mutex_unlock( &p_intf->change_lock );
214 void GtkPlaylistCrop( GtkMenuItem * menuitem, gpointer user_data )
216 /* Ok, this is a really small thing, but, hey, it works and
217 might be useful, who knows ? */
218 GtkPlaylistInvert( menuitem, user_data );
219 GtkPlaylistDeleteSelected( menuitem, user_data );
222 void GtkPlaylistInvert( GtkMenuItem * menuitem, gpointer user_data )
224 #if 0 /* PLAYLIST TARASS */
225 playlist_t *p_playlist;
231 /* catch the thread back */
232 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(menuitem), (char*)user_data );
234 p_playlist = p_intf->p_vlc->p_playlist;
236 /* lock the struct */
237 vlc_mutex_lock( &p_intf->change_lock );
239 p_clist = GTK_CLIST( gtk_object_get_data( GTK_OBJECT(
240 p_intf->p_sys->p_playlist ), "playlist_clist" ) );
242 /* have to copy the selection to an int *
243 I wasn't able to copy the g_list to another g_list
244 glib only does pointer copies, not real copies :( */
246 pi_selected = malloc( sizeof(int) *g_list_length( p_clist->selection ) );
247 i_sel_l = g_list_length( p_clist->selection );
249 for( i_dummy = 0 ; i_dummy < i_sel_l ; i_dummy++)
251 pi_selected[i_dummy] = (long)g_list_nth_data( p_clist->selection,
255 gtk_clist_freeze( p_clist );
256 gtk_clist_select_all( p_clist );
258 for( i_dummy = 0; i_dummy < i_sel_l; i_dummy++)
260 gtk_clist_unselect_row( p_clist, pi_selected[i_dummy], 0 );
261 gtk_clist_unselect_row( p_clist, pi_selected[i_dummy], 1 );
265 gtk_clist_thaw( p_clist );
267 vlc_mutex_unlock( &p_intf->change_lock );
271 void GtkPlaylistSelect( GtkMenuItem * menuitem, gpointer user_data)
276 gboolean GtkPlaylistEvent( GtkWidget * widget,
280 #if 0 /* PLAYLIST TARASS */
281 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(widget), (char*)user_data );
283 if( ( event->button ).type == GDK_2BUTTON_PRESS )
289 p_clist = GTK_CLIST( gtk_object_get_data( GTK_OBJECT(
290 p_intf->p_sys->p_playlist ), "playlist_clist" ) );
292 if( gtk_clist_get_selection_info( p_clist, (event->button).x,
293 (event->button).y, &i_row, &i_col ) == 1 )
295 /* clicked is in range. */
296 if( p_intf->p_sys->p_input != NULL )
298 /* FIXME: temporary hack */
299 p_intf->p_sys->p_input->b_eof = 1;
302 intf_PlaylistJumpto( p_intf->p_vlc->p_playlist, i_row - 1 );
311 void GtkPlaylistDragData( GtkWidget *widget,
312 GdkDragContext *drag_context,
315 GtkSelectionData *data,
320 #if 0 /* PLAYLIST TARASS */
321 intf_thread_t * p_intf = GetIntf( GTK_WIDGET(widget), (char*)user_data );
325 int i_end = p_intf->p_vlc->p_playlist->i_size;
327 p_clist = GTK_CLIST( gtk_object_get_data( GTK_OBJECT(
328 p_intf->p_sys->p_playlist ), "playlist_clist" ) );
330 if( gtk_clist_get_selection_info( p_clist, x, y, &i_row, &i_col ) == 1 )
332 /* we are dropping somewhere into the clist items */
333 GtkDropDataReceived( p_intf, data, info, i_row );
337 /* else, put that at the end of the playlist */
338 GtkDropDataReceived( p_intf, data, info, PLAYLIST_END );
341 intf_PlaylistJumpto( p_intf->p_vlc->p_playlist, i_end - 1 );
346 gboolean GtkPlaylistDragMotion( GtkWidget *widget,
347 GdkDragContext *drag_context,
353 #if 0 /* PLAYLIST TARASS */
354 intf_thread_t *p_intf;
361 p_intf = GetIntf( GTK_WIDGET(widget), (char*)user_data );
363 p_clist = GTK_CLIST( gtk_object_get_data( GTK_OBJECT(
364 p_intf->p_sys->p_playlist ), "playlist_clist" ) );
366 if( !GTK_WIDGET_TOPLEVEL(widget) )
368 gdk_window_raise( p_intf->p_sys->p_playlist->window );
373 color.green = 0xffff;
375 gtk_clist_freeze( p_clist );
377 for( i_dummy = 0; i_dummy < p_clist->rows; i_dummy++)
379 gtk_clist_set_background ( p_clist, i_dummy , &color);
385 i_row = p_intf->p_vlc->p_playlist->i_index;
386 gtk_clist_set_background( p_clist, i_row, &color );
388 if( gtk_clist_get_selection_info( p_clist, x, y, &i_row, &i_col ) == 1)
392 color.green = 0x9000;
393 gtk_clist_set_background ( p_clist, i_row - 1, &color);
394 gtk_clist_set_background ( p_clist, i_row, &color);
397 gtk_clist_thaw( p_clist );
403 void GtkDropDataReceived( intf_thread_t * p_intf,
404 GtkSelectionData * p_data, guint i_info, int i_position)
406 #if 0 /* PLAYLIST TARASS */
407 /* first we'll have to split against all the '\n' we have */
411 gchar * p_string = p_data->data ;
412 GList * p_files = NULL;
416 /* catch the playlist back */
417 playlist_t * p_playlist = p_intf->p_vlc->p_playlist;
420 /* if this has been URLencoded, decode it
422 * Is it a good thing to do it in place ?
425 if( i_info == DROP_ACCEPT_TEXT_URI_LIST )
427 UrlDecode( p_string );
430 /* this cuts string into single file drops */
431 /* this code was borrowed from xmms, thx guys :) */
434 p_next = strchr( p_string, '\n' );
437 if( *( p_next - 1 ) == '\r' )
439 *( p_next - 1) = '\0';
444 /* do we have a protocol or something ? */
445 p_temp = strstr( p_string, ":" );
446 if( p_temp != NULL && p_temp[0] != '\0' )
452 p_protocol = strdup( p_string );
456 /* Allowed things are proto: or proto:// */
457 if( p_temp[0] == '/' && p_temp[1] == '/')
462 msg_Dbg( p_intf, "playlist protocol '%s', target '%s'",
463 p_protocol, p_temp );
467 p_protocol = strdup( "" );
470 /* if it uses the file protocol we can do something, else, sorry :(
471 * I think this is a good choice for now, as we don't have any
472 * ability to read http:// or ftp:// files
473 * what about adding dvd:// to the list of authorized proto ? */
475 if( strcmp( p_protocol, "file:" ) == 0 )
477 p_files = g_list_concat( p_files, GtkReadFiles( p_string ) );
481 p_files = g_list_concat( p_files,
482 g_list_append( NULL, g_strdup( p_string ) ) );
485 /* free the malloc and go on... */
492 p_string = p_next + 1;
495 /* At this point, we have a nice big list maybe NULL */
496 if( p_files != NULL )
498 /* lock the interface */
499 vlc_mutex_lock( &p_intf->change_lock );
501 msg_Dbg( p_intf, "list has %d elements", g_list_length( p_files ) );
502 GtkAppendList( p_playlist, i_position, p_files );
504 /* get the CList and rebuild it. */
505 p_clist = GTK_CLIST( lookup_widget( p_intf->p_sys->p_playlist,
506 "playlist_clist" ) );
507 GtkRebuildCList( p_clist , p_playlist );
509 /* unlock the interface */
510 vlc_mutex_unlock( &p_intf->change_lock );
516 void GtkDeleteGListItem( gpointer data, gpointer param )
518 #if 0 /* PLAYLIST TARASS */
519 int i_cur_row = (long)data;
520 intf_thread_t * p_intf = param;
522 intf_PlaylistDelete( p_intf->p_vlc->p_playlist, i_cur_row );
524 /* are we deleting the current played stream */
525 if( p_intf->p_sys->i_playing == i_cur_row )
528 p_intf->p_sys->p_input->b_eof = 1;
529 /* this has to set the slider to 0 */
532 p_intf->p_sys->i_playing-- ;
534 vlc_mutex_lock( &p_intf->p_vlc->p_playlist->change_lock );
535 p_intf->p_vlc->p_playlist->i_index-- ;
536 vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
542 gint GtkCompareItems( gconstpointer a, gconstpointer b )
548 /* check a file (string) against supposed valid extension */
549 int GtkHasValidExtension( gchar * psz_filename )
551 char * ppsz_ext[6] = { "mpg", "mpeg", "vob", "mp2", "ts", "ps" };
555 gchar * psz_ext = strrchr( psz_filename, '.' ) + sizeof( char );
557 for( i_dummy = 0 ; i_dummy < i_ext ; i_dummy++ )
559 if( strcmp( psz_ext, ppsz_ext[i_dummy] ) == 0 )
568 /* recursive function: descend into folders and build a list of
570 GList * GtkReadFiles( gchar * psz_fsname )
573 GList * p_current = NULL;
575 /* get the attributes of this file */
576 stat( psz_fsname, &statbuf );
578 /* is it a regular file ? */
579 if( S_ISREG( statbuf.st_mode ) )
581 if( GtkHasValidExtension( psz_fsname ) )
583 //X msg_Warn( "%s is a valid file. Stacking on the playlist",
585 return g_list_append( NULL, g_strdup( psz_fsname ) );
592 /* is it a directory (should we check for symlinks ?) */
593 else if( S_ISDIR( statbuf.st_mode ) )
595 /* have to cd into this dir */
596 DIR * p_current_dir = opendir( psz_fsname );
597 struct dirent * p_dir_content;
599 //X msg_Warn( "%s is a folder.", psz_fsname );
601 if( p_current_dir == NULL )
603 /* something went bad, get out of here ! */
606 p_dir_content = readdir( p_current_dir );
608 /* while we still have entries in the directory */
609 while( p_dir_content != NULL )
611 /* if it is "." or "..", forget it */
612 if( ( strcmp( p_dir_content->d_name, "." ) != 0 ) &&
613 ( strcmp( p_dir_content->d_name, ".." ) != 0 ) )
615 /* else build the new directory by adding
616 fsname "/" and the current entry name
619 char * psz_newfs = malloc ( 2 + strlen( psz_fsname ) +
620 strlen( p_dir_content->d_name ) * sizeof(char) );
621 strcpy( psz_newfs, psz_fsname );
622 strcpy( psz_newfs + strlen( psz_fsname ) + 1,
623 p_dir_content->d_name );
624 psz_newfs[strlen( psz_fsname )] = '/';
626 p_current = g_list_concat( p_current,
627 GtkReadFiles( psz_newfs ) );
631 p_dir_content = readdir( p_current_dir );
638 /* add items in a playlist
639 * when i_pos==-1 add to the end of the list...
641 int GtkAppendList( playlist_t * p_playlist, int i_pos, GList * p_list )
643 #if 0 /* PLAYLIST TARASS */
647 i_length = g_list_length( p_list );
649 for( i_dummy = 0; i_dummy < i_length ; i_dummy++ )
651 intf_PlaylistAdd( p_playlist,
652 /* ok; this is a really nasty trick to insert
653 the item where they are suppose to go but, hey
654 this works :P (btw, you are really nasty too) */
655 i_pos==PLAYLIST_END?PLAYLIST_END:( i_pos + i_dummy ),
656 g_list_nth_data( p_list, i_dummy ) );
662 /* statis timeouted function */
663 void GtkPlayListManage( intf_thread_t * p_intf )
665 #if 0 /* PLAYLIST TARASS */
666 /* this thing really sucks for now :( */
668 /* TODO speak more with interface/intf_playlist.c */
670 playlist_t * p_playlist = p_intf->p_vlc->p_playlist ;
673 if( GTK_IS_WIDGET( p_intf->p_sys->p_playlist ) )
675 p_clist = GTK_CLIST( gtk_object_get_data( GTK_OBJECT(
676 p_intf->p_sys->p_playlist ), "playlist_clist" ) );
678 vlc_mutex_lock( &p_playlist->change_lock );
680 if( p_intf->p_sys->i_playing != p_playlist->i_index )
688 gtk_clist_set_background( p_clist, p_playlist->i_index, &color );
690 if( p_intf->p_sys->i_playing != -1 )
694 color.green = 0xffff;
695 gtk_clist_set_background( p_clist, p_intf->p_sys->i_playing,
698 p_intf->p_sys->i_playing = p_playlist->i_index;
701 vlc_mutex_unlock( &p_playlist->change_lock );
706 void GtkRebuildCList( GtkCList * p_clist, playlist_t * p_playlist )
708 #if 0 /* PLAYLIST TARASS */
710 gchar * ppsz_text[2];
716 gtk_clist_freeze( p_clist );
717 gtk_clist_clear( p_clist );
719 vlc_mutex_lock( &p_playlist->change_lock );
720 for( i_dummy = p_playlist->i_size ; i_dummy-- ; )
722 ppsz_text[0] = p_playlist->p_item[i_dummy].psz_name;
723 ppsz_text[1] = "no info";
724 gtk_clist_insert( p_clist, 0, ppsz_text );
726 vlc_mutex_unlock( &p_playlist->change_lock );
728 gtk_clist_set_background( p_clist, p_playlist->i_index, &red);
729 gtk_clist_thaw( p_clist );
733 /* URL-decode a file: URL path, return NULL if it's not what we expect */
734 static void UrlDecode( char *encoded_path )
736 char *tmp = NULL, *cur = NULL, *ext = NULL;
739 if( !encoded_path || *encoded_path == '\0' )
746 tmp = calloc(strlen(encoded_path) + 1, sizeof(char) );
748 while ( ( ext = strchr(cur, '%') ) != NULL)
750 strncat(tmp, cur, (ext - cur) / sizeof(char));
753 if (!sscanf(ext, "%2x", &realchar))
759 tmp[strlen(tmp)] = (char)realchar;
765 strcpy(encoded_path,tmp);