1 /*****************************************************************************
2 * playlist_interface.c : Interface for the playlist dialog
3 *****************************************************************************
4 * Copyright (C) 2000, 2001 VideoLAN
6 * Authors: .Pierre Baillet <oct@zoy.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 *****************************************************************************/
23 #define MODULE_NAME gtk
24 #include "modules_inner.h"
26 /*****************************************************************************
28 *****************************************************************************/
37 #include <sys/types.h> /* for readdir and stat stuff */
47 #include "stream_control.h"
48 #include "input_ext-intf.h"
50 #include "interface.h"
51 #include "intf_plst.h"
53 #include "intf_urldecode.h"
56 #include "gtk_callbacks.h"
57 #include "gtk_interface.h"
58 #include "gtk_support.h"
62 /* Playlist specific functions */
63 void rebuildCList(GtkCList * clist, playlist_t * playlist_p);
64 gint compareItems(gconstpointer a, gconstpointer b);
65 int hasValidExtension(gchar * filename);
66 GList * intf_readFiles(gchar * fsname );
67 int intf_AppendList( playlist_t * p_playlist, int i_pos, GList * list );
68 void GtkPlayListManage( gpointer p_data );
69 void on_generic_drop_data_received( intf_thread_t * p_intf,
70 GtkSelectionData *data, guint info, int position);
73 static __inline__ intf_thread_t * GetIntf( GtkWidget *item, char * psz_parent )
75 return( gtk_object_get_data( GTK_OBJECT( lookup_widget(item, psz_parent) ),
82 on_menubar_playlist_activate (GtkMenuItem *menuitem,
85 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(menuitem), "intf_window" );
86 playlist_t * p_playlist ;
89 if( !GTK_IS_WIDGET( p_intf->p_sys->p_playlist ) )
91 p_intf->p_sys->p_playlist = create_intf_playlist();
92 gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_playlist ),
97 vlc_mutex_lock( &p_main->p_playlist->change_lock );
98 if(p_main->p_playlist->i_size > 0 )
100 p_playlist = p_main->p_playlist;
102 list = GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist, "clist1" )) ;
103 rebuildCList( list, p_playlist );
107 vlc_mutex_unlock( &p_main->p_playlist->change_lock );
109 gtk_widget_show( p_intf->p_sys->p_playlist );
110 gdk_window_raise( p_intf->p_sys->p_playlist->window );
115 on_toolbar_playlist_clicked (GtkButton *button,
118 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(button), "intf_window" );
120 if( !GTK_IS_WIDGET( p_intf->p_sys->p_playlist ) )
122 p_intf->p_sys->p_playlist = create_intf_playlist();
123 gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_playlist ),
126 if( GTK_WIDGET_VISIBLE(p_intf->p_sys->p_playlist) ) {
127 gtk_widget_hide( p_intf->p_sys->p_playlist);
130 gtk_widget_show( p_intf->p_sys->p_playlist );
131 clist = GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist,"clist1" ));
132 gdk_window_raise( p_intf->p_sys->p_playlist->window );
133 rebuildCList( clist , p_main->p_playlist );
138 on_playlist_ok_clicked (GtkButton *button,
141 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(button), "intf_playlist" );
142 gtk_widget_hide( p_intf->p_sys->p_playlist );
145 void deleteGListItem(gpointer data, gpointer param)
147 int curRow = ( int )data;
148 intf_thread_t * p_intf = param;
150 intf_PlstDelete( p_main->p_playlist, curRow );
152 /* are we deleting the current played stream */
153 if( p_intf->p_sys->i_playing == curRow )
156 p_intf->p_input->b_eof = 1;
157 /* this has to set the slider to 0 */
160 p_intf->p_sys->i_playing-- ;
161 p_main->p_playlist->i_index-- ;
164 gint compareItems(gconstpointer a, gconstpointer b)
170 rebuildCList(GtkCList * clist, playlist_t * playlist_p)
180 gtk_clist_freeze( clist );
181 gtk_clist_clear( clist );
183 for( dummy=0; dummy < playlist_p->i_size; dummy++ )
185 text[0] = g_strdup( rindex( (char *)(playlist_p->p_item[playlist_p->i_size -1 - dummy].psz_name ), '/' ) + 1 );
186 text[1] = g_strdup( "no info");
188 gtk_clist_insert( clist, 0, text );
193 gtk_clist_set_background (
197 gtk_clist_thaw( clist );
201 on_invertselection_clicked (GtkMenuItem *item, gpointer user_data)
203 int * selected, sel_l;
205 playlist_t * playlist_p;
208 /* catch the thread back */
209 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(item), "intf_playlist" );
210 playlist_p = p_main->p_playlist;
212 /* lock the struct */
213 vlc_mutex_lock( &p_intf->p_sys->change_lock );
214 clist = GTK_CLIST( lookup_widget(p_intf->p_sys->p_playlist,"clist1") );
215 selected = malloc(sizeof(int)* g_list_length(clist->selection));
217 sel_l = g_list_length(clist->selection);
219 for(dummy=0; dummy < sel_l; dummy++)
221 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->p_sys->change_lock );
236 on_crop_activate (GtkMenuItem *menuitem,
239 on_invertselection_clicked (menuitem, user_data);
240 on_delete_clicked(menuitem, user_data);
245 on_delete_clicked (GtkMenuItem *item,
248 /* user wants to delete a file in the queue */
251 playlist_t * playlist_p;
253 /* catch the thread back */
254 intf_thread_t *p_intf = GetIntf( GTK_WIDGET(item), "intf_playlist" );
255 playlist_p = p_main->p_playlist;
257 /* lock the struct */
258 vlc_mutex_lock( &p_intf->p_sys->change_lock );
259 clist = GTK_CLIST( lookup_widget(p_intf->p_sys->p_playlist,"clist1") );
261 /* I use UNDOCUMENTED features to retrieve the selection... */
262 selection = clist->selection;
264 if( g_list_length(selection)>0 )
266 selection = g_list_sort( selection, compareItems );
267 g_list_foreach( selection,
271 rebuildCList( clist, playlist_p );
274 vlc_mutex_unlock( &p_intf->p_sys->change_lock );
278 on_intf_playlist_destroy_event (GtkWidget *widget,
282 gtk_widget_hide(widget);
288 on_intf_playlist_drag_data_received (GtkWidget *widget,
289 GdkDragContext *drag_context,
292 GtkSelectionData *data,
297 /* catch the interface back */
298 intf_thread_t * p_intf = GetIntf( GTK_WIDGET(widget), "intf_playlist" );
302 clist = GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist,"clist1" ));
304 if( gtk_clist_get_selection_info( clist,
310 on_generic_drop_data_received( p_intf, data, info, row);
312 on_generic_drop_data_received( p_intf, data, info, 0);
316 void on_generic_drop_data_received( intf_thread_t * p_intf,
317 GtkSelectionData *data, guint info, int position)
319 /* first we'll have to split against all the '\n' we have */
322 gchar * string = data->data ;
323 GList * files = NULL;
327 /* catch the playlist back */
328 playlist_t * p_playlist = p_main->p_playlist ;
331 /* if this has been URLencoded, decode it
333 * Is it a good thing to do it in place ?
336 if(info == DROP_ACCEPT_TEXT_URI_LIST)
338 urldecode_path( string );
341 /* this cuts string into single file drops */
344 temp = strchr(string, '\n');
347 if (*(temp - 1) == '\r')
353 /* do we have a protocol or something ? */
354 protocol = strstr( string, ":/" );
355 if( protocol != NULL )
357 protocol = calloc( protocol - string + 2 ,
359 protocol = strncpy( protocol, string, strstr( string, ":/") + 1 - string );
361 intf_WarnMsg(1,"Protocol dropped is %s",protocol);
362 string += strlen(protocol) ;
364 /* Allowed things are proto: or proto:// */
365 if(string[0]=='/' && string[1]=='/')
370 intf_WarnMsg(1,"Dropped %s",string);
373 protocol = strdup("");
376 /* if it uses the file protocol we can do something, else, sorry :(
377 * I think this is a good choice for now, as we don't have any
378 * ability to read http:// or ftp:// files
379 * what about adding dvd:// to the list of authorized proto ? */
381 if( strcmp(protocol,"file:")==0 )
383 files = g_list_concat( files, intf_readFiles( string ) );
386 /* free the malloc and go on... */
393 /* At this point, we have a nice big list maybe NULL */
396 /* lock the interface */
397 vlc_mutex_lock( &p_intf->p_sys->change_lock );
398 intf_WarnMsg(1, "List has %d elements",g_list_length(files));
399 intf_AppendList( p_playlist, position, files );
400 /* get the CList and rebuild it. */
401 clist = GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist,"clist1" ));
402 rebuildCList( clist , p_playlist );
404 /* unlock the interface */
405 vlc_mutex_unlock( &p_intf->p_sys->change_lock );
409 /* check a file (string) against supposed valid extension */
411 hasValidExtension(gchar * filename)
413 char * ext[6] = {"mpg","mpeg","vob","mp2","ts","ps"};
416 gchar * p_filename = strrchr( filename, '.') + sizeof( char );
417 for(dummy=0; dummy<i_ext;dummy++)
419 if(strcmp(p_filename,ext[dummy])==0)
425 /* recursive function: descend into folders and build a list of valid filenames */
427 intf_readFiles(gchar * fsname )
430 GList * current = NULL;
432 stat(fsname, &statbuf);
434 /* is it a regular file ? */
435 if( S_ISREG( statbuf.st_mode ) )
437 if( hasValidExtension(fsname) )
439 intf_WarnMsg( 3, "%s is a valid file. Stacking on the playlist", fsname );
440 return g_list_append( NULL, g_strdup(fsname) );
444 /* is it a directory (should we check for symlinks ?) */
445 else if( S_ISDIR( statbuf.st_mode ) )
447 /* have to cd into this dir */
448 DIR * currentDir = opendir( fsname );
449 struct dirent * dirContent;
451 intf_WarnMsg( 3, "%s is a folder.", fsname );
453 if( currentDir == NULL )
455 /* something went bad, get out of here ! */
458 dirContent = readdir( currentDir );
460 /* while we still have entries in the directory */
461 while( dirContent != NULL )
463 /* if it is "." or "..", forget it */
464 if(strcmp(dirContent->d_name,".") != 0
465 && strcmp(dirContent->d_name,"..") != 0)
467 /* else build the new directory by adding
468 fsname "/" and the current entry name
471 char * newfs = malloc ( 2 +
473 strlen( dirContent->d_name ) * sizeof( char ) );
474 strcpy( newfs, fsname );
475 strcpy( newfs + strlen( fsname )+1, dirContent->d_name);
476 newfs[strlen(fsname)] = '/';
478 current = g_list_concat( current, intf_readFiles( newfs ) );
482 dirContent = readdir( currentDir );
489 /* add items in a playlist */
490 int intf_AppendList( playlist_t * p_playlist, int i_pos, GList * list )
493 length = g_list_length( list );
494 for(dummy=0; dummy<length; dummy++)
496 intf_WarnMsg(1,"Adding: %s@%d",g_list_nth_data(list, dummy), i_pos + dummy);
497 intf_PlstAdd( p_playlist, i_pos + dummy, g_list_nth_data(list, dummy));
502 on_clist1_event (GtkWidget *widget,
506 intf_thread_t * p_intf = GetIntf( GTK_WIDGET(widget), "intf_playlist" );
508 if( (event->button).type == GDK_2BUTTON_PRESS )
513 clist = GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist,"clist1" ));
514 if( gtk_clist_get_selection_info( clist,
521 /* clicked is in range. */
522 if( p_intf->p_input != NULL )
524 /* FIXME: temporary hack */
525 p_intf->p_input->b_eof = 1;
527 intf_PlstJumpto( p_main->p_playlist, row-1 );
534 /* statis timeouted function */
535 void GtkPlayListManage( gpointer p_data )
538 /* this thing really sucks for now :( */
540 /* TODO speak more with interface/intf_plst.c */
542 intf_thread_t *p_intf = (void *)p_data;
543 playlist_t * p_playlist = p_main->p_playlist ;
545 vlc_mutex_lock( &p_intf->p_sys->change_lock );
547 if(p_intf->p_sys->i_playing != p_playlist->i_index)
555 gtk_clist_set_background (
556 GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist, "clist1" ) ),
559 if( p_intf->p_sys->i_playing != -1 )
564 gtk_clist_set_background (
565 GTK_CLIST(lookup_widget( p_intf->p_sys->p_playlist, "clist1" ) ),
566 p_intf->p_sys->i_playing,
569 p_intf->p_sys->i_playing = p_playlist->i_index;
571 vlc_mutex_unlock( &p_intf->p_sys->change_lock );