1 /*****************************************************************************
2 * gtk_playlist.c : Interface for the playlist dialog
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: gtk_playlist.c,v 1.8 2001/03/21 13:42:34 sam Exp $
7 * Authors: Pierre Baillet <oct@zoy.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 *****************************************************************************/
24 #define MODULE_NAME gtk
25 #include "modules_inner.h"
27 /*****************************************************************************
29 *****************************************************************************/
38 #include <sys/types.h> /* for readdir and stat stuff */
48 #include "stream_control.h"
49 #include "input_ext-intf.h"
51 #include "interface.h"
52 #include "intf_playlist.h"
54 #include "intf_urldecode.h"
56 #include "gtk_callbacks.h"
57 #include "gtk_interface.h"
58 #include "gtk_support.h"
63 /* Playlist specific functions */
64 void rebuildCList(GtkCList * clist, playlist_t * playlist_p);
65 gint compareItems(gconstpointer a, gconstpointer b);
66 int hasValidExtension(gchar * filename);
67 GList * intf_readFiles(gchar * fsname );
68 int intf_AppendList( playlist_t * p_playlist, int i_pos, GList * list );
69 void GtkPlayListManage( gpointer p_data );
70 void on_generic_drop_data_received( intf_thread_t * p_intf,
71 GtkSelectionData *data, guint info, int position);
74 on_menubar_playlist_activate (GtkMenuItem *menuitem,
78 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(menuitem), "intf_window" );
79 playlist_t * p_playlist ;
82 if( !GTK_IS_WIDGET( p_intf->p_sys->p_playlist ) )
84 /* this shoud never happen */
85 intf_ErrMsgImm("intf_playlist is not a widget !");
86 p_intf->p_sys->p_playlist = create_intf_playlist();
87 gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_playlist ),
91 vlc_mutex_lock( &p_main->p_playlist->change_lock );
92 if(p_main->p_playlist->i_size > 0 )
94 p_playlist = p_main->p_playlist;
95 list = GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist, "playlist_clist" )) ;
96 rebuildCList( list, p_playlist );
98 vlc_mutex_unlock( &p_main->p_playlist->change_lock );
100 gtk_widget_show( p_intf->p_sys->p_playlist );
101 gdk_window_raise( p_intf->p_sys->p_playlist->window );
106 on_toolbar_playlist_clicked (GtkButton *button,
109 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(button), "intf_window" );
111 if( !GTK_IS_WIDGET( p_intf->p_sys->p_playlist ) )
113 /* this shoud never happen */
114 intf_ErrMsgImm("intf_playlist is not a widget !");
116 p_intf->p_sys->p_playlist = create_intf_playlist();
117 gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_playlist ),
120 if( GTK_WIDGET_VISIBLE(p_intf->p_sys->p_playlist) ) {
121 gtk_widget_hide( p_intf->p_sys->p_playlist);
126 gtk_widget_show( p_intf->p_sys->p_playlist );
127 clist = GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist,"playlist_clist" ));
128 gdk_window_raise( p_intf->p_sys->p_playlist->window );
129 rebuildCList( clist , p_main->p_playlist );
134 on_playlist_ok_clicked (GtkButton *button,
137 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(button), "intf_playlist" );
138 gtk_widget_hide( p_intf->p_sys->p_playlist );
141 void deleteGListItem(gpointer data, gpointer param)
143 int curRow = ( int )data;
144 intf_thread_t * p_intf = param;
146 intf_PlaylistDelete( p_main->p_playlist, curRow );
148 /* are we deleting the current played stream */
149 if( p_intf->p_sys->i_playing == curRow )
152 p_intf->p_input->b_eof = 1;
153 /* this has to set the slider to 0 */
156 p_intf->p_sys->i_playing-- ;
157 p_main->p_playlist->i_index-- ;
160 gint compareItems(gconstpointer a, gconstpointer b)
166 rebuildCList(GtkCList * clist, playlist_t * playlist_p)
176 gtk_clist_freeze( clist );
177 gtk_clist_clear( clist );
179 for( dummy=0; dummy < playlist_p->i_size; dummy++ )
181 text[0] = g_strdup( rindex( (char *)(playlist_p->p_item[playlist_p->i_size -1 - dummy].psz_name ), '/' ) + 1 );
182 text[1] = g_strdup( "no info");
184 gtk_clist_insert( clist, 0, text );
189 gtk_clist_set_background (
193 gtk_clist_thaw( clist );
197 on_invertselection_clicked (GtkMenuItem *item, gpointer user_data)
199 int * selected, sel_l;
201 playlist_t * playlist_p;
204 /* catch the thread back */
205 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(item), "intf_playlist" );
206 playlist_p = p_main->p_playlist;
208 /* lock the struct */
209 vlc_mutex_lock( &p_intf->change_lock );
210 clist = GTK_CLIST( lookup_widget(p_intf->p_sys->p_playlist,"playlist_clist") );
212 /* have to copy the selection to an int *
213 I wasn't able to copy the g_list to another g_list
214 glib only does pointer copies, not real copies :( */
216 selected = malloc(sizeof(int)* g_list_length(clist->selection));
217 sel_l = g_list_length(clist->selection);
218 for(dummy=0; dummy < sel_l; dummy++)
220 selected[dummy] = (int)g_list_nth_data(clist->selection,dummy);
223 gtk_clist_freeze( clist );
224 gtk_clist_select_all( clist );
225 for(dummy=0; dummy < sel_l; dummy++)
227 gtk_clist_unselect_row( clist, selected[dummy],0);
228 gtk_clist_unselect_row( clist, selected[dummy],1);
231 gtk_clist_thaw( clist );
232 vlc_mutex_unlock( &p_intf->change_lock );
236 on_crop_activate (GtkMenuItem *menuitem,
239 /* Ok, this is a really small thing, but, hey, it works and
240 might be useful, who knows ? */
242 on_invertselection_clicked (menuitem, user_data);
243 on_delete_clicked(menuitem, user_data);
248 on_delete_clicked (GtkMenuItem *item,
251 /* user wants to delete a file in the queue */
254 playlist_t * playlist_p;
256 /* catch the thread back */
257 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(item), "intf_playlist" );
258 playlist_p = p_main->p_playlist;
260 /* lock the struct */
261 vlc_mutex_lock( &p_intf->change_lock );
262 clist = GTK_CLIST( lookup_widget(p_intf->p_sys->p_playlist,"playlist_clist") );
264 /* I use UNDOCUMENTED features to retrieve the selection... */
265 selection = clist->selection;
267 if( g_list_length(selection)>0 )
269 /* reverse-sort so that we can delete from the furthest to the
270 closest item to delete...
272 selection = g_list_sort( selection, compareItems );
273 g_list_foreach( selection,
277 rebuildCList( clist, playlist_p );
280 vlc_mutex_unlock( &p_intf->change_lock );
284 on_intf_playlist_destroy_event (GtkWidget *widget,
289 gtk_widget_hide(widget);
294 on_intf_playlist_drag_data_received (GtkWidget *widget,
295 GdkDragContext *drag_context,
298 GtkSelectionData *data,
303 /* catch the interface back */
304 intf_thread_t * p_intf = GetIntf( GTK_WIDGET(widget), "intf_playlist" );
308 clist = GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist,"playlist_clist" ));
310 /* are we dropping somewhere into the clist items ? */
311 if( gtk_clist_get_selection_info( clist,
317 on_generic_drop_data_received( p_intf, data, info, row );
319 /* else, put that at the end of the playlist */
322 on_generic_drop_data_received( p_intf, data, info, PLAYLIST_END);
326 void on_generic_drop_data_received( intf_thread_t * p_intf,
327 GtkSelectionData *data, guint info, int position)
329 /* first we'll have to split against all the '\n' we have */
332 gchar * string = data->data ;
333 GList * files = NULL;
337 /* catch the playlist back */
338 playlist_t * p_playlist = p_main->p_playlist ;
341 /* if this has been URLencoded, decode it
343 * Is it a good thing to do it in place ?
346 if(info == DROP_ACCEPT_TEXT_URI_LIST)
348 urldecode_path( string );
351 /* this cuts string into single file drops */
352 /* this code was borrowed from xmms, thx guys :) */
355 temp = strchr(string, '\n');
358 if (*(temp - 1) == '\r')
364 /* do we have a protocol or something ? */
365 protocol = strstr( string, ":/" );
366 if( protocol != NULL )
368 protocol = calloc( protocol - string + 2 ,
370 protocol = strncpy( protocol, string, strstr( string, ":/") + 1 - string );
372 intf_WarnMsg(1,"Protocol dropped is %s",protocol);
373 string += strlen(protocol) ;
375 /* Allowed things are proto: or proto:// */
376 if(string[0]=='/' && string[1]=='/')
381 intf_WarnMsg(1,"Dropped %s",string);
386 protocol = strdup("");
389 /* if it uses the file protocol we can do something, else, sorry :(
390 * I think this is a good choice for now, as we don't have any
391 * ability to read http:// or ftp:// files
392 * what about adding dvd:// to the list of authorized proto ? */
394 if( strcmp(protocol,"file:")==0 )
396 files = g_list_concat( files, intf_readFiles( string ) );
399 /* free the malloc and go on... */
406 /* At this point, we have a nice big list maybe NULL */
409 /* lock the interface */
410 vlc_mutex_lock( &p_intf->change_lock );
411 intf_WarnMsg( 1, "List has %d elements",g_list_length( files ) );
412 intf_AppendList( p_playlist, position, files );
414 /* get the CList and rebuild it. */
416 lookup_widget( p_intf->p_sys->p_playlist,
417 "playlist_clist" ) );
418 rebuildCList( clist , p_playlist );
420 /* unlock the interface */
421 vlc_mutex_unlock( &p_intf->change_lock );
425 /* check a file (string) against supposed valid extension */
427 hasValidExtension( gchar * filename )
429 char * ext[6] = {"mpg","mpeg","vob","mp2","ts","ps"};
432 gchar * p_filename = strrchr( filename, '.' ) + sizeof( char );
433 for( dummy=0; dummy<i_ext;dummy++ )
435 if( strcmp( p_filename,ext[dummy] )==0 )
441 /* recursive function: descend into folders and build a list of valid filenames */
443 intf_readFiles( gchar * fsname )
446 GList * current = NULL;
448 /* get the attributes of this file */
449 stat(fsname, &statbuf);
451 /* is it a regular file ? */
452 if( S_ISREG( statbuf.st_mode ) )
454 if( hasValidExtension(fsname) )
456 intf_WarnMsg( 3, "%s is a valid file. Stacking on the playlist", fsname );
457 return g_list_append( NULL, g_strdup(fsname) );
464 /* is it a directory (should we check for symlinks ?) */
465 else if( S_ISDIR( statbuf.st_mode ) )
467 /* have to cd into this dir */
468 DIR * currentDir = opendir( fsname );
469 struct dirent * dirContent;
471 intf_WarnMsg( 3, "%s is a folder.", fsname );
473 if( currentDir == NULL )
475 /* something went bad, get out of here ! */
478 dirContent = readdir( currentDir );
480 /* while we still have entries in the directory */
481 while( dirContent != NULL )
483 /* if it is "." or "..", forget it */
484 if(strcmp(dirContent->d_name,".") != 0
485 && strcmp(dirContent->d_name,"..") != 0)
487 /* else build the new directory by adding
488 fsname "/" and the current entry name
491 char * newfs = malloc ( 2 +
493 strlen( dirContent->d_name ) * sizeof( char ) );
494 strcpy( newfs, fsname );
495 strcpy( newfs + strlen( fsname )+1, dirContent->d_name);
496 newfs[strlen( fsname )] = '/';
498 current = g_list_concat( current, intf_readFiles( newfs ) );
502 dirContent = readdir( currentDir );
509 /* add items in a playlist
510 when i_pos==-1 add to the end of the list...
512 int intf_AppendList( playlist_t * p_playlist, int i_pos, GList * list )
515 length = g_list_length( list );
516 for( dummy=0; dummy<length; dummy++ )
518 intf_PlaylistAdd( p_playlist,
519 /* ok; this is a really nasty trick to insert
520 the item where they are suppose to go but, hey
521 this works :P (btw, you are really nasty too) */
522 i_pos==PLAYLIST_END?PLAYLIST_END:( i_pos + dummy ),
523 g_list_nth_data(list, dummy));
528 on_playlist_clist_event (GtkWidget *widget,
532 intf_thread_t * p_intf = GetIntf( GTK_WIDGET( widget ), "intf_playlist" );
534 if( ( event->button ).type == GDK_2BUTTON_PRESS )
541 p_intf->p_sys->p_playlist,
542 "playlist_clist" ) );
544 if( gtk_clist_get_selection_info( clist,
551 /* clicked is in range. */
552 if( p_intf->p_input != NULL )
554 /* FIXME: temporary hack */
555 p_intf->p_input->b_eof = 1;
557 intf_PlaylistJumpto( p_main->p_playlist, row-1 );
564 /* statis timeouted function */
565 void GtkPlayListManage( gpointer p_data )
567 /* this thing really sucks for now :( */
569 /* TODO speak more with interface/intf_playlist.c */
571 intf_thread_t *p_intf = (void *)p_data;
572 playlist_t * p_playlist = p_main->p_playlist ;
574 vlc_mutex_lock( &p_intf->change_lock );
576 if( p_intf->p_sys->i_playing != p_playlist->i_index )
584 gtk_clist_set_background ( GTK_CLIST(
585 lookup_widget( p_intf->p_sys->p_playlist,
586 "playlist_clist" ) ),
590 if( p_intf->p_sys->i_playing != -1 )
593 color.green = 0xffff;
595 gtk_clist_set_background (
596 GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist, "playlist_clist" ) ),
597 p_intf->p_sys->i_playing,
600 p_intf->p_sys->i_playing = p_playlist->i_index;
602 vlc_mutex_unlock( &p_intf->change_lock );