renatofilho@20: #include <gtk/gtksignal.h>
renatofilho@20: #include <gdk/gdkevents.h>
renatofilho@20: #include <gdk/gdkkeysyms.h>
renatofilho@20: 
renatofilho@20: #include "mmyth_uicommon.h"
renatofilho@20: #include "mmyth_epg_grid_widget.h"
renatofilho@20: 
rosfran@245: #include <gmyth/gmyth_util.h>
rosfran@245: #include <gmyth/gmyth_epg.h>
renatofilho@20: 
renatofilho@20: #define PIXELS_HOUR 105
renatofilho@20: #define PROGRAM_SEPARATION 2
renatofilho@20: 
renatofilho@20: enum {
renatofilho@20:   SELECTION_UPDATED_SIGNAL,
renatofilho@20:   LAST_SIGNAL
renatofilho@20: };
renatofilho@20: 
renatofilho@20: struct _MMythEpgGridWidgetPrivate {
renatofilho@20:     /* private widget components */
renatofilho@20:     GtkWidget *epg_channels_vbox;
renatofilho@20:     GtkWidget *epg_programs_vbox;
renatofilho@20:  
renatofilho@20:     GHashTable *service_model_hash;
renatofilho@20:     
renatofilho@20:     /* guidegrid attributes */
renatofilho@20:     gboolean show_favorites;
renatofilho@20:     gint current_start_channel_id;
renatofilho@20:     
rosfran@244:     GTimeVal* current_start_time;
rosfran@244:     GTimeVal* current_end_time;
renatofilho@20: 
renatofilho@20:     guint selected_channel_index;
renatofilho@20:     
renatofilho@20:     /* GList of ProgramInfo for each Channel */
renatofilho@20:     GList * program_list[MAX_DISPLAY_CHANS];
renatofilho@20:     GList * channel_list;
renatofilho@20:     
renatofilho@20:     GMythEPG *mmyth_epg;
rosfran@208: 
rosfran@208:     GMythBackendInfo *backend_info;
renatofilho@20:     
renatofilho@20:     gint DISPLAY_CHANS;
renatofilho@20: };
renatofilho@20: 
renatofilho@20: static void mmyth_epg_grid_widget_class_init          (MMythEpgGridWidgetClass *klass);
renatofilho@20: static void mmyth_epg_grid_widget_init                (MMythEpgGridWidget *object);
renatofilho@20: static void mmyth_epg_grid_widget_private_init        (MMythEpgGridWidgetPrivate *private);
renatofilho@20: static void mmyth_epg_grid_widget_mount_services      (MMythEpgGridWidget *object, 
rosfran@244:                                                        GTimeVal* start_time, GTimeVal* end_time);
renatofilho@20: static void mmyth_epg_grid_widget_mount_header        (MMythEpgGridWidget *object);
renatofilho@20: static void mmyth_epg_grid_widget_clicked             (GtkWidget* widget, 
renatofilho@20:                                                        GdkEventExpose *event, 
renatofilho@20:                                                        gpointer data);
renatofilho@20: static GtkWidget *create_event_box_lbl                (gchar *str, int width, 
renatofilho@20:                                                        const GdkColor *bg_color, 
renatofilho@20:                                                        const GdkColor *fg_color);
renatofilho@20: 
renatofilho@20: static void mmyth_epg_grid_widget_fill_programinfos(MMythEpgGridWidgetPrivate *private);
renatofilho@20: static void mmyth_epg_grid_widget_fill_program_row_infos(
renatofilho@20:                     MMythEpgGridWidgetPrivate *private, 
renatofilho@20:                     unsigned int chanNum, unsigned int row);
renatofilho@20: 
renatofilho@20: static gint mmyth_epg_grid_widget_signals[LAST_SIGNAL] = { 0 };
renatofilho@20: 
renatofilho@20: G_DEFINE_TYPE(MMythEpgGridWidget, mmyth_epg_grid_widget, GTK_TYPE_EVENT_BOX)
renatofilho@20:     
renatofilho@20: static void
renatofilho@20: mmyth_epg_grid_widget_class_init (MMythEpgGridWidgetClass *klass)
renatofilho@20: {
renatofilho@20:   g_type_class_add_private (klass, sizeof (MMythEpgGridWidgetPrivate));
renatofilho@20:   
renatofilho@20:   mmyth_epg_grid_widget_signals[SELECTION_UPDATED_SIGNAL] = g_signal_new (
renatofilho@20:                      "selection_updated",
renatofilho@20: 					 G_TYPE_FROM_CLASS(klass), 
renatofilho@20: 					 G_SIGNAL_RUN_FIRST,
renatofilho@20: 					 0,
renatofilho@20:                      NULL,
renatofilho@20:                      NULL,
renatofilho@20: 					 g_cclosure_marshal_VOID__POINTER, 
renatofilho@20:                      G_TYPE_NONE, 
renatofilho@20:                      1,
renatofilho@20:                      G_TYPE_POINTER);
renatofilho@20: }
renatofilho@20: 
renatofilho@20: static void 
renatofilho@20: mmyth_epg_grid_widget_private_init (MMythEpgGridWidgetPrivate *private)
renatofilho@20: {
renatofilho@20:     time_t cur_time;
renatofilho@20:     
renatofilho@20:     g_return_if_fail(private != NULL);     
renatofilho@20: 
renatofilho@20:     private->epg_channels_vbox   = NULL;
renatofilho@20:     private->epg_programs_vbox   = NULL;
renatofilho@20:     private->service_model_hash  = NULL;
renatofilho@20: 
renatofilho@20:     private->show_favorites = FALSE;
renatofilho@20:     private->current_start_channel_id = -1;
renatofilho@20: 
renatofilho@20:     /* Selected the first diplayable channel initially */
renatofilho@20:     private->selected_channel_index = 0;
renatofilho@20:         
renatofilho@20:     /* TODO fix the current start/end time */
rosfran@244:     private->current_start_time = g_new0( GTimeVal, 1 );
rosfran@244:     private->current_start_time->tv_sec = time(&cur_time);
rosfran@244: 
rosfran@244:     private->current_end_time = g_new0( GTimeVal, 1 );
rosfran@244:     private->current_end_time->tv_sec = time(&cur_time) + 10800;
renatofilho@20:     
renatofilho@20:     private->DISPLAY_CHANS = MAX_DISPLAY_CHANS;
rosfran@208: 
morphbr@268:     private->backend_info = gmyth_backend_info_new_full ( "localhost", "mythtv",
rosfran@208:     	"mythtv", "mythconverg", 6543 );
rosfran@208: 
renatofilho@20:     // TODO: Close the epg and unref it in dispose call
renatofilho@20:     private->mmyth_epg = gmyth_epg_new ();
rosfran@208:     if (!gmyth_epg_connect (private->mmyth_epg, private->backend_info)) {
renatofilho@20:     	g_warning ("[%s] Could not connect mysql handler to db", __FUNCTION__);
renatofilho@20:     	g_object_unref (private->mmyth_epg);
renatofilho@20:     	private->mmyth_epg = NULL;
renatofilho@20:     }
renatofilho@20: }
renatofilho@20: 
renatofilho@20: static void
renatofilho@20: mmyth_epg_grid_widget_init (MMythEpgGridWidget *mmyth_epg_grid_widget)
renatofilho@20: {
renatofilho@20:     MMythEpgGridWidgetPrivate *private = 
renatofilho@20:         MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
renatofilho@20:     
renatofilho@20:     /* init private fields */
renatofilho@20:     mmyth_epg_grid_widget_private_init(private);
renatofilho@20: 
renatofilho@20:     mmyth_epg_grid_widget->epg_view_model      = NULL;
renatofilho@20:     mmyth_epg_grid_widget->selected_grid_item  = NULL;
renatofilho@20: 
renatofilho@20:     GtkWidget *epg_event_box = GTK_WIDGET(mmyth_epg_grid_widget);
renatofilho@20:     gtk_widget_modify_bg(epg_event_box, GTK_STATE_NORMAL, &main_bg_color);
renatofilho@20:     gtk_widget_set_size_request (epg_event_box, 0, 125);
renatofilho@20:     
renatofilho@20:     GtkWidget *epg_main_hbox = gtk_hbox_new (FALSE, 10);
renatofilho@20:     gtk_container_set_border_width(GTK_CONTAINER (epg_main_hbox), 10);
renatofilho@20:     
renatofilho@20:     gtk_container_add (GTK_CONTAINER (epg_event_box),
renatofilho@20:                        epg_main_hbox);
renatofilho@20: 		        
renatofilho@20:     /* channels vbox */
renatofilho@20:     GtkWidget *epg_channels_vbox = gtk_vbox_new (FALSE, 3); 
renatofilho@20:     private->epg_channels_vbox = epg_channels_vbox; 
renatofilho@20: 
renatofilho@20:     /* programs vbox */
renatofilho@20:     GtkWidget *epg_programs_vbox = gtk_vbox_new (FALSE, 3);
renatofilho@20:     private->epg_programs_vbox = epg_programs_vbox;
renatofilho@20:     
renatofilho@20:     /* packing start */
renatofilho@20:     gtk_box_pack_start (GTK_BOX (epg_main_hbox),
renatofilho@20: 	                epg_channels_vbox, FALSE, FALSE, 0);
renatofilho@20:     gtk_box_pack_start (GTK_BOX (epg_main_hbox),
renatofilho@20:                         epg_programs_vbox, FALSE, FALSE, 0);
renatofilho@20: 
renatofilho@20:     /* table header (first line) */  
renatofilho@20:     mmyth_epg_grid_widget_mount_header(mmyth_epg_grid_widget);                 
renatofilho@20:     
renatofilho@20:     /* service programs */ 
renatofilho@20:     /* mount service programs with current time */
renatofilho@20:     mmyth_epg_grid_widget_mount_services(mmyth_epg_grid_widget, 
renatofilho@20:                                          private->current_start_time,
renatofilho@20:                                          private->current_end_time);                                           
renatofilho@20: }
renatofilho@20: 
renatofilho@20: GtkWidget*
renatofilho@20: mmyth_epg_grid_widget_new ()
renatofilho@20: {
renatofilho@20:   return GTK_WIDGET ( gtk_type_new (mmyth_epg_grid_widget_get_type ()));
renatofilho@20: }
renatofilho@20: 
renatofilho@20: static void
rosfran@244: mmyth_epg_grid_widget_mount_services( MMythEpgGridWidget *mmyth_epg_grid_widget, 
rosfran@244:                                      GTimeVal* start_time, GTimeVal* end_time )
renatofilho@20: {
renatofilho@20:     GList *proglist;
renatofilho@20:     GList *channel_list = NULL;
renatofilho@20:     GMythChannelInfo *channel_info;
renatofilho@20: 
renatofilho@20:     int chanid;
renatofilho@20:     MMythEpgGridWidgetPrivate *private = 
renatofilho@20:         MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
renatofilho@20: 	
renatofilho@20: 	// update view_model
renatofilho@20: 	/* FIXME shallow free or recursive? */
renatofilho@20: 	if(mmyth_epg_grid_widget->epg_view_model != NULL) {
renatofilho@20: 		g_list_free(mmyth_epg_grid_widget->epg_view_model);
renatofilho@20: 		mmyth_epg_grid_widget->epg_view_model = NULL;
renatofilho@20: 	}
renatofilho@20: 	
renatofilho@20: 	if(private->service_model_hash != NULL) {
renatofilho@20: 		g_hash_table_destroy(private->service_model_hash);
renatofilho@20: 	}
renatofilho@20: 
renatofilho@20: 	private->service_model_hash = g_hash_table_new(NULL, NULL);
renatofilho@20: 
renatofilho@20:     /* fill program infos from db */
renatofilho@20:     mmyth_epg_grid_widget_fill_programinfos(private);
renatofilho@20:     
renatofilho@20:     channel_list = private->channel_list;
renatofilho@20:  
renatofilho@20:     /* for each channel get_programs() */
renatofilho@20:     for (chanid=0; channel_list &&
renatofilho@20:                    chanid < private->DISPLAY_CHANS; chanid++) {
renatofilho@20:         proglist = (GList *) private->program_list[chanid];        
renatofilho@20:     
renatofilho@20:         channel_info = (GMythChannelInfo *) channel_list->data;
renatofilho@20:         channel_list = g_list_next(channel_list);        
renatofilho@20: 
renatofilho@20:         /* Service Title*/
renatofilho@20:         GString *name = NULL;
renatofilho@20:         if (channel_info->channel_name)
renatofilho@20:         	name = g_string_new (channel_info->channel_name->str);
renatofilho@20: 		
renatofilho@20: 		GdkColor title_bg_color;
renatofilho@20: 		title_bg_color.red = 5000;
renatofilho@20: 		title_bg_color.green = 9000;
renatofilho@20: 		title_bg_color.blue = 40000;        
renatofilho@20: 		
renatofilho@20: 		GdkColor title_fg_color;
renatofilho@20: 		title_fg_color.red = 60000;
renatofilho@20: 		title_fg_color.green = 60000;
renatofilho@20: 		title_fg_color.blue = 60000;        
renatofilho@20: 		
renatofilho@20: 		GtkWidget *event_box_channel = create_event_box_lbl(
renatofilho@20:                                                         name->str, 90, 
renatofilho@20: 									                    &title_bg_color, 
renatofilho@20: 									                    &title_fg_color);
renatofilho@20: 
renatofilho@20:         gtk_box_pack_start (GTK_BOX (private->epg_channels_vbox),
renatofilho@20: 		                    event_box_channel, FALSE, FALSE, 0);
renatofilho@20: 
renatofilho@20:   		GtkWidget *epg_line_hbox = gtk_hbox_new (FALSE, 0);      
renatofilho@20: 
renatofilho@20: 	    GdkColor bg_color;
renatofilho@20: 	    bg_color.red = 5000;
renatofilho@20: 	    bg_color.green = 30000;
renatofilho@20: 	    bg_color.blue = 60000;        
renatofilho@20: 	
renatofilho@20: 	    GdkColor fg_color;
renatofilho@20: 	    fg_color.red = 60000;
renatofilho@20: 	    fg_color.green = 60000;
renatofilho@20: 	    fg_color.blue = 60000;        
renatofilho@20: 		
renatofilho@20: 		/* Content parsing */
renatofilho@20:         GList *epg_grid_list = NULL;
renatofilho@20: 
renatofilho@20:         GMythProgramInfo *proginfo;
renatofilho@20:         int pixel_count = 0;
renatofilho@20:         for (; proglist; proglist = proglist->next) {
renatofilho@20:             proginfo = (GMythProgramInfo *) proglist->data;
renatofilho@20:             
renatofilho@20:             GString *content_name = proginfo->title;
renatofilho@20:                     
rosfran@244:        	    GTimeVal* initial_time = g_new0( GTimeVal, 1 );
rosfran@244: 	    GTimeVal* last_time = g_new0( GTimeVal, 1 ); 
rosfran@244: 	    GTimeVal* duration = g_new0( GTimeVal, 1 );
renatofilho@20: 
rosfran@244:             GTimeVal* schedule_start_time = proginfo->startts;
rosfran@244:             GTimeVal* schedule_end_time   = proginfo->endts;
renatofilho@20: 
rosfran@244: 	        initial_time->tv_sec = 
rosfran@244:                 (schedule_start_time->tv_sec < start_time->tv_sec) ? start_time->tv_sec : schedule_start_time->tv_sec;
rosfran@244:             last_time->tv_sec = (schedule_end_time->tv_sec > end_time->tv_sec) ? end_time->tv_sec : schedule_end_time->tv_sec;
rosfran@244: 	        duration->tv_sec = last_time->tv_sec - initial_time->tv_sec;
renatofilho@20: 		    
renatofilho@20:             // Verify program time 
renatofilho@20:             #if 0
renatofilho@20: 			    g_debug ("ServiceID: %d, ScheduleID: %d\n", service->id, schedule->id);
renatofilho@20:                 fprintf (stderr, "program time\nfrom = %d, to = %d\n", 
renatofilho@20:                          schedule->validFrom, schedule->validTo); 
renatofilho@20:                 
renatofilho@20:                 struct tm loctime;
renatofilho@20:     
renatofilho@20:                 /* Convert it to local time representation. */
renatofilho@20:                 if (localtime_r((time_t *)&schedule->validFrom, &loctime) == NULL) {
renatofilho@20:                     g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
renatofilho@20:                     return NULL;
renatofilho@20:                 }            
renatofilho@20:                 fprintf (stderr, asctime (&loctime)); 
renatofilho@20:                 
renatofilho@20:                 if (localtime_r((time_t *)&schedule->validTo, &loctime) == NULL) {
renatofilho@20:                     g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
renatofilho@20:                     return NULL;
renatofilho@20:                 } 
renatofilho@20:                 fprintf (stderr, asctime (&loctime)); 
renatofilho@20:             #endif
renatofilho@20:     		
renatofilho@20:     	    /* fprintf(stderr, "duration = %d\n", duration); */
rosfran@244:     	    double duration_hour = duration->tv_sec / (double) 3600.0;
renatofilho@20:     	    /* fprintf(stderr, "duration_hour = %lf\n", duration_hour); */
renatofilho@20:     		
renatofilho@20:             int size = PIXELS_HOUR * duration_hour;
renatofilho@20:                 
renatofilho@20:             /* complete hour */
renatofilho@20:             /* FIXME: UGLY WRONG HACK TO ALIGN PROGRAM TIME!!!*/
rosfran@244:             if( last_time->tv_sec % 3600 != 0 ) {
renatofilho@20:                 size -= PROGRAM_SEPARATION;                
renatofilho@20:             }
rosfran@244:             if ( initial_time->tv_sec % 3600 != 0 ) {
renatofilho@20:                 size -= PROGRAM_SEPARATION;                
renatofilho@20:             }
renatofilho@20: 
renatofilho@20:             pixel_count += size + PROGRAM_SEPARATION;
renatofilho@20: 	        GtkWidget *event_box = create_event_box_lbl(content_name->str, 
renatofilho@20: 							  size, &bg_color, 
renatofilho@20: 							  &fg_color);
renatofilho@20: 							  gtk_widget_add_events(event_box, 
renatofilho@20: 							  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
renatofilho@20:             
renatofilho@20:             /* create EpgGridItem */
rosfran@244:             EpgGridItem *epg_grid_item = g_new0(EpgGridItem, 1);
renatofilho@20:             epg_grid_item->proginfo  = proginfo;
renatofilho@20:             epg_grid_item->event_box = event_box;
renatofilho@20:             epg_grid_item->object    = mmyth_epg_grid_widget;
renatofilho@20:             
renatofilho@20:             epg_grid_list = g_list_prepend(epg_grid_list, (gpointer) epg_grid_item);                                   		                        
renatofilho@20: 
renatofilho@20:      	    gtk_box_pack_start (GTK_BOX (epg_line_hbox),
renatofilho@20: 	                            event_box, FALSE, FALSE, PROGRAM_SEPARATION);
renatofilho@20: 	                                 
renatofilho@20:    	        g_signal_connect (G_OBJECT (event_box), "button-press-event",
renatofilho@20: 		                      G_CALLBACK (mmyth_epg_grid_widget_clicked), 
renatofilho@20:                               (gpointer*) epg_grid_list);
renatofilho@20:         }
renatofilho@20: #if 0
renatofilho@20:         printf("chaind = %d!!!!" chanid);fflush(stdout);
renatofilho@20: #endif        
renatofilho@20: 
renatofilho@20:         if(!epg_grid_list) {
renatofilho@20:             /* No programs for current channel */
renatofilho@20:             /* FIXME: size HARDCODED */
renatofilho@20:             GtkWidget *event_box = create_event_box_lbl("No program list available",
renatofilho@20:                               PIXELS_HOUR * 3, &bg_color,
renatofilho@20:                               &fg_color);
renatofilho@20:                               gtk_widget_add_events(event_box,
renatofilho@20:                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
renatofilho@20: 
renatofilho@20:             /* create EpgGridItem */
rosfran@244:             EpgGridItem *epg_grid_item = g_new0(EpgGridItem, 1);
renatofilho@20:             epg_grid_item->proginfo  = NULL;
renatofilho@20:             epg_grid_item->event_box = event_box;
renatofilho@20:             epg_grid_item->object    = mmyth_epg_grid_widget;
renatofilho@20: 
renatofilho@20:             epg_grid_list = g_list_prepend(epg_grid_list, (gpointer) epg_grid_item);
renatofilho@20:             
renatofilho@20:             gtk_box_pack_start (GTK_BOX (epg_line_hbox),
renatofilho@20: 	                            event_box, FALSE, FALSE, PROGRAM_SEPARATION);            	
renatofilho@20: 	                                 
renatofilho@20:    	        g_signal_connect (G_OBJECT (event_box), "button-press-event",
renatofilho@20: 		                      G_CALLBACK (mmyth_epg_grid_widget_clicked),
renatofilho@20:                               (gpointer*) epg_grid_list);
renatofilho@20:         }
renatofilho@20: 
renatofilho@20:         epg_grid_list = g_list_reverse(epg_grid_list);
renatofilho@20:         mmyth_epg_grid_widget->epg_view_model = 
renatofilho@20:               g_list_append(mmyth_epg_grid_widget->epg_view_model, epg_grid_list);
renatofilho@20:         
renatofilho@20: 	    gtk_box_pack_start (GTK_BOX (private->epg_programs_vbox),
renatofilho@20: 	                        epg_line_hbox, FALSE, FALSE,  0);                    		    		
renatofilho@20:     }
renatofilho@20: }
renatofilho@20: 
renatofilho@20: static void
renatofilho@20: mmyth_epg_grid_widget_mount_header(MMythEpgGridWidget *mmyth_epg_grid_widget)
renatofilho@20: {	          	    
renatofilho@20:     MMythEpgGridWidgetPrivate *private = 
renatofilho@20:         MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
renatofilho@20:     
renatofilho@20:     struct tm hour_tm;
renatofilho@20:     const gchar name_title[] = "Today";    
renatofilho@20:     GtkWidget * lbl_title = gtk_label_new(name_title);
renatofilho@20: 
renatofilho@20:     gtk_misc_set_alignment (GTK_MISC(lbl_title), 0.0, 0.5);
renatofilho@20:     
renatofilho@20:     gtk_box_pack_start (GTK_BOX (private->epg_channels_vbox),
renatofilho@20:                         lbl_title, FALSE, FALSE, 0);
renatofilho@20: 
renatofilho@20: 	/* hours title line */
renatofilho@20:     GtkWidget *epg_programs_hours_hbox = gtk_hbox_new (TRUE, 0); 
renatofilho@20: 
rosfran@244:     if (localtime_r((time_t *)&private->current_start_time->tv_sec, &hour_tm) == NULL) {
renatofilho@20:         g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
rosfran@244:         return;
renatofilho@20:     }    
renatofilho@20: 
renatofilho@20:     if (hour_tm.tm_min>30) {
renatofilho@20:         hour_tm.tm_min = 30;
renatofilho@20:     } else if (hour_tm.tm_min>0) {
renatofilho@20:         hour_tm.tm_min = 0;
renatofilho@20:     }
renatofilho@20:         
renatofilho@20:     gchar hour1_str[10];
renatofilho@20:     strftime(hour1_str, 8, "%H:%M", &hour_tm);
renatofilho@20:     GtkWidget * lbl_hour1 = gtk_label_new(hour1_str);
renatofilho@20:     gtk_misc_set_alignment (GTK_MISC(lbl_hour1), 0.0, 0.5);    
renatofilho@20: 	
renatofilho@20:     hour_tm.tm_hour++;
renatofilho@20:     gchar hour2_str[10];    
renatofilho@20:     strftime(hour2_str, 8, "%H:%M", &hour_tm);
renatofilho@20:     GtkWidget * lbl_hour2 = gtk_label_new(hour2_str);
renatofilho@20:     gtk_misc_set_alignment (GTK_MISC(lbl_hour2), 0.0, 0.5);        
renatofilho@20: 
renatofilho@20:     hour_tm.tm_hour++;
renatofilho@20: 	gchar hour3_str[10];    
renatofilho@20:     strftime(hour3_str, 8, "%H:%M", &hour_tm);
renatofilho@20:     GtkWidget * lbl_hour3 = gtk_label_new(hour3_str);
renatofilho@20:     gtk_misc_set_alignment (GTK_MISC(lbl_hour3), 0.0, 0.5);        
renatofilho@20:     
renatofilho@20:     gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
renatofilho@20:                         lbl_hour1, TRUE, TRUE, 0);                    
renatofilho@20:     gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
renatofilho@20:                         lbl_hour2, TRUE, TRUE, 0);                    
renatofilho@20:     gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
renatofilho@20:                         lbl_hour3, TRUE, TRUE, 0); 
renatofilho@20:  
renatofilho@20:     gtk_box_pack_start (GTK_BOX (private->epg_programs_vbox),
renatofilho@20:                         epg_programs_hours_hbox, FALSE, FALSE, 0);                        
renatofilho@20: }
renatofilho@20: 
renatofilho@20: /******************************************************************************
renatofilho@20:  *              INTERNAL CALLBACKS FOR STATE CHANGE                           *
renatofilho@20:  *****************************************************************************/
renatofilho@20: static void 
renatofilho@20: mmyth_epg_grid_widget_deselect_service(MMythEpgGridWidget *mmyth_epg_grid_widget)
renatofilho@20: {
renatofilho@20: 	EpgGridItem *epg_grid_item;
renatofilho@20: 	
renatofilho@20:   	/* deselect*/
renatofilho@20:     if(mmyth_epg_grid_widget->selected_grid_item != NULL) {
renatofilho@20:     	epg_grid_item = 
renatofilho@20:             (EpgGridItem*) mmyth_epg_grid_widget->selected_grid_item->data;
renatofilho@20:     	gtk_widget_set_state(GTK_WIDGET(epg_grid_item->event_box), GTK_STATE_NORMAL);
renatofilho@20:     }            
renatofilho@20: }
renatofilho@20: 
renatofilho@20: static void 
renatofilho@20: mmyth_epg_grid_widget_clicked (GtkWidget* widget, 
renatofilho@20:                                GdkEventExpose *event, gpointer data)
renatofilho@20: {           
renatofilho@20:     g_return_if_fail(data != NULL);
renatofilho@20: 
renatofilho@20:     GList *epg_grid_item_list = (GList *) data;
renatofilho@20:     EpgGridItem *epg_grid_item = (EpgGridItem *) epg_grid_item_list->data;
renatofilho@20: 
renatofilho@20:     /* update the selected service */
renatofilho@20:     mmyth_epg_grid_widget_update_service( epg_grid_item->object, (GList*) data );       
renatofilho@20: }
renatofilho@20: 
renatofilho@20: void
renatofilho@20: mmyth_epg_grid_widget_update_service(MMythEpgGridWidget * object,
renatofilho@20:                                      GList *selected_grid_list)
renatofilho@20: {
renatofilho@20:     g_return_if_fail(object != NULL);
renatofilho@20:     g_return_if_fail(selected_grid_list != NULL);
renatofilho@20: 		
renatofilho@20: 	EpgGridItem *epg_grid_item = (EpgGridItem *) selected_grid_list->data;
renatofilho@20: 
renatofilho@20:     mmyth_epg_grid_widget_deselect_service(epg_grid_item->object);
renatofilho@20: 
renatofilho@20:     /* updating current selected schedule_item and schedule_list*/
renatofilho@20:     object->selected_grid_item = selected_grid_list;
renatofilho@20:     
renatofilho@20:     /* set state of the event box */
renatofilho@20:     gtk_widget_set_state(GTK_WIDGET(epg_grid_item->event_box), GTK_STATE_SELECTED);
renatofilho@20:     /* emit update signal for listeners */
renatofilho@20:     g_signal_emit(object, 
renatofilho@20:                   mmyth_epg_grid_widget_signals[SELECTION_UPDATED_SIGNAL],
renatofilho@20:                   0,
renatofilho@20:                   (gpointer) epg_grid_item);
renatofilho@20: }
renatofilho@20: 
renatofilho@20: static GtkWidget * 
renatofilho@20: create_event_box_lbl(gchar *str, int width, const GdkColor *bg_color, 
renatofilho@20:                      const GdkColor *fg_color) 
renatofilho@20: {
renatofilho@20:     GtkWidget *event_box = gtk_event_box_new();
renatofilho@20: 	GtkWidget *lbl = gtk_label_new(str);
renatofilho@20:     gtk_label_set_ellipsize(GTK_LABEL(lbl), PANGO_ELLIPSIZE_END);
renatofilho@20: 
renatofilho@20:     gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, bg_color);    
renatofilho@20:     gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, fg_color);    
renatofilho@20:     
renatofilho@20:     /* selected colors are const*/
renatofilho@20:     GdkColor selected_bg_color;
renatofilho@20:     selected_bg_color.red = 100;
renatofilho@20:     selected_bg_color.green = 40000;
renatofilho@20:     selected_bg_color.blue = 100;        
renatofilho@20: 
renatofilho@20:     GdkColor selected_fg_color;
renatofilho@20:     selected_fg_color.red = 100;
renatofilho@20:     selected_fg_color.green = 100;
renatofilho@20:     selected_fg_color.blue = 100;        
renatofilho@20:             
renatofilho@20:     gtk_widget_modify_bg(event_box, GTK_STATE_SELECTED, &selected_bg_color);    
renatofilho@20:     gtk_widget_modify_fg(lbl, GTK_STATE_SELECTED, &selected_fg_color);    
renatofilho@20:     
renatofilho@20:     gtk_misc_set_alignment (GTK_MISC(lbl), 0.0, 0.5);    
renatofilho@20: 	gtk_container_add (GTK_CONTAINER (event_box),
renatofilho@20:                        lbl);
renatofilho@20: 	gtk_widget_set_size_request(event_box, width, -1);
renatofilho@20: 	                       
renatofilho@20:     return event_box;
renatofilho@20: }
renatofilho@20: 
renatofilho@20: /******************************************************************************
renatofilho@20:  *                            METHODS                                         *
renatofilho@20:  *****************************************************************************/
renatofilho@20: 
renatofilho@20: /* Callback for hardware keys */
renatofilho@20: gboolean
renatofilho@20: mmyth_epg_grid_widget_key_press (MMythEpgGridWidget * object, 
renatofilho@20:                                  GtkWidget * widget, GdkEventKey * event)
renatofilho@20: {
renatofilho@20:     MMythEpgGridWidgetPrivate *private = 
renatofilho@20:         MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(object);
renatofilho@20:   
renatofilho@20:     EpgGridItem *epg_grid_item;
renatofilho@20:     GList *tmp;
renatofilho@20:     
renatofilho@20:     /* List of selected_grid_item */
renatofilho@20:     GList *selected_view_model;
renatofilho@20:     
renatofilho@20:     gint channel_index;
renatofilho@20:     
renatofilho@20: 	if(object->selected_grid_item == NULL) {
renatofilho@20:         g_warning ("No program selected");
renatofilho@20: 	    return FALSE;
renatofilho@20: 	} 
renatofilho@20:     
renatofilho@20:     epg_grid_item = (EpgGridItem*) object->selected_grid_item->data;    
renatofilho@20:     
renatofilho@20:     channel_index = private->selected_channel_index;
renatofilho@20:     
renatofilho@20:     switch (event->keyval) {
renatofilho@20:         case GDK_Up:        
renatofilho@20:             selected_view_model = g_list_nth( object->epg_view_model, channel_index - 1 );
renatofilho@20:             if(selected_view_model != NULL) {
renatofilho@20:                 private->selected_channel_index = channel_index - 1;
renatofilho@20:    	            tmp = (GList *) selected_view_model->data;               
renatofilho@20:                 /* TODO: select a better centralized item 
renatofilho@20:                    currently is picking the 1st or last item */
renatofilho@20:                 if(g_list_next(object->selected_grid_item) == NULL &&
renatofilho@20:                    g_list_previous(object->selected_grid_item) != NULL) {
renatofilho@20:                     /* in this case the new selected will be the last */ 
renatofilho@20:                     tmp = g_list_last(tmp);
renatofilho@20:                 }
renatofilho@20: 
renatofilho@20: 	            /* update the selected service */
renatofilho@20:                 mmyth_epg_grid_widget_update_service( object, tmp );
renatofilho@20:             }    
renatofilho@20:         return TRUE;         
renatofilho@20:         case GDK_Down:
renatofilho@20:             selected_view_model = g_list_nth( object->epg_view_model, channel_index + 1 );            
renatofilho@20:             if(selected_view_model != NULL) {   	            
renatofilho@20:                 private->selected_channel_index = channel_index + 1;
renatofilho@20:    	            tmp = (GList *) selected_view_model->data;
renatofilho@20:                 /* TODO: select a better centralized item 
renatofilho@20:                    currently is picking the 1st or last item */
renatofilho@20:                 if(g_list_next(object->selected_grid_item) == NULL &&
renatofilho@20:                    g_list_previous(object->selected_grid_item) != NULL) {
renatofilho@20:                     /* in this case the new selected will be the last */ 
renatofilho@20:                     tmp = g_list_last(tmp);
renatofilho@20:                 }
renatofilho@20: 
renatofilho@20: 	            /* update the selected service */
renatofilho@20:                 mmyth_epg_grid_widget_update_service( object, tmp );
renatofilho@20:             }
renatofilho@20:         return TRUE;        
renatofilho@20:         case GDK_Left:    
renatofilho@20:             tmp = g_list_previous( object->selected_grid_item );
renatofilho@20:             if(tmp != NULL) {
renatofilho@20: 	            /* update the selected service */
renatofilho@20:                 mmyth_epg_grid_widget_update_service( object, tmp );
renatofilho@20:             }
renatofilho@20:         return TRUE;        
renatofilho@20:         case GDK_Right:
renatofilho@20:             tmp = g_list_next( object->selected_grid_item );
renatofilho@20:             if(tmp != NULL) {
renatofilho@20: 	            /* update the selected service */
renatofilho@20:                 mmyth_epg_grid_widget_update_service( object, tmp );
renatofilho@20:             }
renatofilho@20:         return TRUE;        
renatofilho@20: 	    default:
renatofilho@20: 	    return TRUE;
renatofilho@20:     }
renatofilho@20:     
renatofilho@20:     return FALSE;
renatofilho@20: }
renatofilho@20: 
renatofilho@20: static void 
renatofilho@20: mmyth_epg_grid_widget_fill_programinfos (MMythEpgGridWidgetPrivate *private)
renatofilho@20: {
renatofilho@20:     GList *channels_list = NULL;
renatofilho@20:     int y;
renatofilho@20: 
renatofilho@20:     if ((private->mmyth_epg != NULL) &&
renatofilho@20: 	   	(gmyth_epg_get_channel_list (private->mmyth_epg, &channels_list) < 0 )) {
renatofilho@20: 			private->channel_list = NULL;
renatofilho@20: 			return;
renatofilho@20:     }
renatofilho@20:     
renatofilho@20:     private->channel_list = channels_list;
renatofilho@20:     
renatofilho@20:     for (y = 0; y < private->DISPLAY_CHANS && channels_list; y++) {
renatofilho@20:         GMythChannelInfo *channel_info = (GMythChannelInfo *) channels_list->data;
renatofilho@20: 
renatofilho@20:         mmyth_epg_grid_widget_fill_program_row_infos(
renatofilho@20:                             private, channel_info->channel_ID, y);
renatofilho@20: 
renatofilho@20:         channels_list = g_list_next (channels_list);
renatofilho@20:     }
renatofilho@20: }
renatofilho@20: 
renatofilho@20: static void 
renatofilho@20: mmyth_epg_grid_widget_fill_program_row_infos(MMythEpgGridWidgetPrivate *private,
renatofilho@20:                                              guint chanNum, guint row)
renatofilho@20: {    
renatofilho@20:     gint res =  gmyth_epg_get_program_list (private->mmyth_epg,
renatofilho@20:     					&(private->program_list[row]),
renatofilho@20:                         chanNum, private->current_start_time, 
renatofilho@20:                         private->current_end_time);
renatofilho@20:                         
renatofilho@20:     if (res < 0) {
renatofilho@20:     	g_warning ("[%s] Error while retrieving epg programs", __FUNCTION__);
renatofilho@20:     }
renatofilho@20: }
renatofilho@20: