maemo-ui/src/mmyth_epg_grid_widget.c
author rosfran
Mon Oct 23 22:28:02 2006 +0100 (2006-10-23)
branchtrunk
changeset 44 7a654d6de88f
child 208 c3c073032757
permissions -rw-r--r--
[svn r45] Moved Myth FileTransfer, LiveTV and Myth URI to the gmyth library.
     1 #include <gtk/gtksignal.h>
     2 #include <gdk/gdkevents.h>
     3 #include <gdk/gdkkeysyms.h>
     4 
     5 #include "mmyth_uicommon.h"
     6 #include "mmyth_epg_grid_widget.h"
     7 
     8 #include "gmyth_util.h" 
     9 #include "gmyth_epg.h"
    10 
    11 #define PIXELS_HOUR 105
    12 #define PROGRAM_SEPARATION 2
    13 
    14 enum {
    15   SELECTION_UPDATED_SIGNAL,
    16   LAST_SIGNAL
    17 };
    18 
    19 struct _MMythEpgGridWidgetPrivate {
    20     /* private widget components */
    21     GtkWidget *epg_channels_vbox;
    22     GtkWidget *epg_programs_vbox;
    23  
    24     GHashTable *service_model_hash;
    25     
    26     /* guidegrid attributes */
    27     gboolean show_favorites;
    28     gint current_start_channel_id;
    29     
    30     time_t current_start_time;
    31     time_t current_end_time;
    32 
    33     guint selected_channel_index;
    34     
    35     /* GList of ProgramInfo for each Channel */
    36     GList * program_list[MAX_DISPLAY_CHANS];
    37     GList * channel_list;
    38     
    39     GMythEPG *mmyth_epg;
    40     
    41     gint DISPLAY_CHANS;
    42 };
    43 
    44 static void mmyth_epg_grid_widget_class_init          (MMythEpgGridWidgetClass *klass);
    45 static void mmyth_epg_grid_widget_init                (MMythEpgGridWidget *object);
    46 static void mmyth_epg_grid_widget_private_init        (MMythEpgGridWidgetPrivate *private);
    47 static void mmyth_epg_grid_widget_mount_services      (MMythEpgGridWidget *object, 
    48                                                        int start_time, int end_time);
    49 static void mmyth_epg_grid_widget_mount_header        (MMythEpgGridWidget *object);
    50 static void mmyth_epg_grid_widget_clicked             (GtkWidget* widget, 
    51                                                        GdkEventExpose *event, 
    52                                                        gpointer data);
    53 static GtkWidget *create_event_box_lbl                (gchar *str, int width, 
    54                                                        const GdkColor *bg_color, 
    55                                                        const GdkColor *fg_color);
    56 
    57 static void mmyth_epg_grid_widget_fill_programinfos(MMythEpgGridWidgetPrivate *private);
    58 static void mmyth_epg_grid_widget_fill_program_row_infos(
    59                     MMythEpgGridWidgetPrivate *private, 
    60                     unsigned int chanNum, unsigned int row);
    61 
    62 static gint mmyth_epg_grid_widget_signals[LAST_SIGNAL] = { 0 };
    63 
    64 G_DEFINE_TYPE(MMythEpgGridWidget, mmyth_epg_grid_widget, GTK_TYPE_EVENT_BOX)
    65     
    66 static void
    67 mmyth_epg_grid_widget_class_init (MMythEpgGridWidgetClass *klass)
    68 {
    69   g_type_class_add_private (klass, sizeof (MMythEpgGridWidgetPrivate));
    70   
    71   mmyth_epg_grid_widget_signals[SELECTION_UPDATED_SIGNAL] = g_signal_new (
    72                      "selection_updated",
    73 					 G_TYPE_FROM_CLASS(klass), 
    74 					 G_SIGNAL_RUN_FIRST,
    75 					 0,
    76                      NULL,
    77                      NULL,
    78 					 g_cclosure_marshal_VOID__POINTER, 
    79                      G_TYPE_NONE, 
    80                      1,
    81                      G_TYPE_POINTER);
    82 }
    83 
    84 static void 
    85 mmyth_epg_grid_widget_private_init (MMythEpgGridWidgetPrivate *private)
    86 {
    87     time_t cur_time;
    88     
    89     g_return_if_fail(private != NULL);     
    90 
    91     private->epg_channels_vbox   = NULL;
    92     private->epg_programs_vbox   = NULL;
    93     private->service_model_hash  = NULL;
    94 
    95     private->show_favorites = FALSE;
    96     private->current_start_channel_id = -1;
    97 
    98     /* Selected the first diplayable channel initially */
    99     private->selected_channel_index = 0;
   100         
   101     /* TODO fix the current start/end time */
   102     private->current_start_time = time(&cur_time);
   103     private->current_end_time = time(&cur_time) + 10800;   
   104     
   105     private->DISPLAY_CHANS = MAX_DISPLAY_CHANS;
   106     
   107     // TODO: Close the epg and unref it in dispose call
   108     private->mmyth_epg = gmyth_epg_new ();
   109     if (!gmyth_epg_connect (private->mmyth_epg)) {
   110     	g_warning ("[%s] Could not connect mysql handler to db", __FUNCTION__);
   111     	g_object_unref (private->mmyth_epg);
   112     	private->mmyth_epg = NULL;
   113     }
   114 }
   115 
   116 static void
   117 mmyth_epg_grid_widget_init (MMythEpgGridWidget *mmyth_epg_grid_widget)
   118 {
   119     MMythEpgGridWidgetPrivate *private = 
   120         MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
   121     
   122     /* init private fields */
   123     mmyth_epg_grid_widget_private_init(private);
   124 
   125     mmyth_epg_grid_widget->epg_view_model      = NULL;
   126     mmyth_epg_grid_widget->selected_grid_item  = NULL;
   127 
   128     GtkWidget *epg_event_box = GTK_WIDGET(mmyth_epg_grid_widget);
   129     gtk_widget_modify_bg(epg_event_box, GTK_STATE_NORMAL, &main_bg_color);
   130     gtk_widget_set_size_request (epg_event_box, 0, 125);
   131     
   132     GtkWidget *epg_main_hbox = gtk_hbox_new (FALSE, 10);
   133     gtk_container_set_border_width(GTK_CONTAINER (epg_main_hbox), 10);
   134     
   135     gtk_container_add (GTK_CONTAINER (epg_event_box),
   136                        epg_main_hbox);
   137 		        
   138     /* channels vbox */
   139     GtkWidget *epg_channels_vbox = gtk_vbox_new (FALSE, 3); 
   140     private->epg_channels_vbox = epg_channels_vbox; 
   141 
   142     /* programs vbox */
   143     GtkWidget *epg_programs_vbox = gtk_vbox_new (FALSE, 3);
   144     private->epg_programs_vbox = epg_programs_vbox;
   145     
   146     /* packing start */
   147     gtk_box_pack_start (GTK_BOX (epg_main_hbox),
   148 	                epg_channels_vbox, FALSE, FALSE, 0);
   149     gtk_box_pack_start (GTK_BOX (epg_main_hbox),
   150                         epg_programs_vbox, FALSE, FALSE, 0);
   151 
   152     /* table header (first line) */  
   153     mmyth_epg_grid_widget_mount_header(mmyth_epg_grid_widget);                 
   154     
   155     /* service programs */ 
   156     /* mount service programs with current time */
   157     mmyth_epg_grid_widget_mount_services(mmyth_epg_grid_widget, 
   158                                          private->current_start_time,
   159                                          private->current_end_time);                                           
   160 }
   161 
   162 GtkWidget*
   163 mmyth_epg_grid_widget_new ()
   164 {
   165   return GTK_WIDGET ( gtk_type_new (mmyth_epg_grid_widget_get_type ()));
   166 }
   167 
   168 static void
   169 mmyth_epg_grid_widget_mount_services(MMythEpgGridWidget *mmyth_epg_grid_widget, 
   170                                      int start_time, int end_time)
   171 {
   172     GList *proglist;
   173     GList *channel_list = NULL;
   174     GMythChannelInfo *channel_info;
   175 
   176     int chanid;
   177     MMythEpgGridWidgetPrivate *private = 
   178         MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
   179 	
   180 	// update view_model
   181 	/* FIXME shallow free or recursive? */
   182 	if(mmyth_epg_grid_widget->epg_view_model != NULL) {
   183 		g_list_free(mmyth_epg_grid_widget->epg_view_model);
   184 		mmyth_epg_grid_widget->epg_view_model = NULL;
   185 	}
   186 	
   187 	if(private->service_model_hash != NULL) {
   188 		g_hash_table_destroy(private->service_model_hash);
   189 	}
   190 
   191 	private->service_model_hash = g_hash_table_new(NULL, NULL);
   192 
   193     /* fill program infos from db */
   194     mmyth_epg_grid_widget_fill_programinfos(private);
   195     
   196     channel_list = private->channel_list;
   197  
   198     /* for each channel get_programs() */
   199     for (chanid=0; channel_list &&
   200                    chanid < private->DISPLAY_CHANS; chanid++) {
   201         proglist = (GList *) private->program_list[chanid];        
   202     
   203         channel_info = (GMythChannelInfo *) channel_list->data;
   204         channel_list = g_list_next(channel_list);        
   205 
   206         /* Service Title*/
   207         GString *name = NULL;
   208         if (channel_info->channel_name)
   209         	name = g_string_new (channel_info->channel_name->str);
   210 		
   211 		GdkColor title_bg_color;
   212 		title_bg_color.red = 5000;
   213 		title_bg_color.green = 9000;
   214 		title_bg_color.blue = 40000;        
   215 		
   216 		GdkColor title_fg_color;
   217 		title_fg_color.red = 60000;
   218 		title_fg_color.green = 60000;
   219 		title_fg_color.blue = 60000;        
   220 		
   221 		GtkWidget *event_box_channel = create_event_box_lbl(
   222                                                         name->str, 90, 
   223 									                    &title_bg_color, 
   224 									                    &title_fg_color);
   225 
   226         gtk_box_pack_start (GTK_BOX (private->epg_channels_vbox),
   227 		                    event_box_channel, FALSE, FALSE, 0);
   228 
   229   		GtkWidget *epg_line_hbox = gtk_hbox_new (FALSE, 0);      
   230 
   231 	    GdkColor bg_color;
   232 	    bg_color.red = 5000;
   233 	    bg_color.green = 30000;
   234 	    bg_color.blue = 60000;        
   235 	
   236 	    GdkColor fg_color;
   237 	    fg_color.red = 60000;
   238 	    fg_color.green = 60000;
   239 	    fg_color.blue = 60000;        
   240 		
   241 		/* Content parsing */
   242         GList *epg_grid_list = NULL;
   243 
   244         GMythProgramInfo *proginfo;
   245         int pixel_count = 0;
   246         for (; proglist; proglist = proglist->next) {
   247             proginfo = (GMythProgramInfo *) proglist->data;
   248             
   249             GString *content_name = proginfo->title;
   250                     
   251        	    int initial_time, last_time, duration;
   252 
   253             int schedule_start_time = proginfo->startts;
   254             int schedule_end_time   = proginfo->endts;
   255 
   256 	        initial_time = 
   257                 (schedule_start_time < start_time) ? start_time : schedule_start_time;
   258             last_time = (schedule_end_time > end_time) ? end_time : schedule_end_time;
   259 	        duration = last_time - initial_time;
   260 		    
   261             // Verify program time 
   262             #if 0
   263 			    g_debug ("ServiceID: %d, ScheduleID: %d\n", service->id, schedule->id);
   264                 fprintf (stderr, "program time\nfrom = %d, to = %d\n", 
   265                          schedule->validFrom, schedule->validTo); 
   266                 
   267                 struct tm loctime;
   268     
   269                 /* Convert it to local time representation. */
   270                 if (localtime_r((time_t *)&schedule->validFrom, &loctime) == NULL) {
   271                     g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
   272                     return NULL;
   273                 }            
   274                 fprintf (stderr, asctime (&loctime)); 
   275                 
   276                 if (localtime_r((time_t *)&schedule->validTo, &loctime) == NULL) {
   277                     g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
   278                     return NULL;
   279                 } 
   280                 fprintf (stderr, asctime (&loctime)); 
   281             #endif
   282     		
   283     	    /* fprintf(stderr, "duration = %d\n", duration); */
   284     	    double duration_hour = duration / (double) 3600.0;
   285     	    /* fprintf(stderr, "duration_hour = %lf\n", duration_hour); */
   286     		
   287             int size = PIXELS_HOUR * duration_hour;
   288                 
   289             /* complete hour */
   290             /* FIXME: UGLY WRONG HACK TO ALIGN PROGRAM TIME!!!*/
   291             if(last_time%3600 != 0) {
   292                 size -= PROGRAM_SEPARATION;                
   293             }
   294             if(initial_time%3600 != 0) {
   295                 size -= PROGRAM_SEPARATION;                
   296             }
   297 
   298             pixel_count += size + PROGRAM_SEPARATION;
   299 	        GtkWidget *event_box = create_event_box_lbl(content_name->str, 
   300 							  size, &bg_color, 
   301 							  &fg_color);
   302 							  gtk_widget_add_events(event_box, 
   303 							  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
   304             
   305             /* create EpgGridItem */
   306             EpgGridItem *epg_grid_item = g_new(EpgGridItem, 1);
   307             epg_grid_item->proginfo  = proginfo;
   308             epg_grid_item->event_box = event_box;
   309             epg_grid_item->object    = mmyth_epg_grid_widget;
   310             
   311             epg_grid_list = g_list_prepend(epg_grid_list, (gpointer) epg_grid_item);                                   		                        
   312 
   313      	    gtk_box_pack_start (GTK_BOX (epg_line_hbox),
   314 	                            event_box, FALSE, FALSE, PROGRAM_SEPARATION);
   315 	                                 
   316    	        g_signal_connect (G_OBJECT (event_box), "button-press-event",
   317 		                      G_CALLBACK (mmyth_epg_grid_widget_clicked), 
   318                               (gpointer*) epg_grid_list);
   319         }
   320 #if 0
   321         printf("chaind = %d!!!!" chanid);fflush(stdout);
   322 #endif        
   323 
   324         if(!epg_grid_list) {
   325             /* No programs for current channel */
   326             /* FIXME: size HARDCODED */
   327             GtkWidget *event_box = create_event_box_lbl("No program list available",
   328                               PIXELS_HOUR * 3, &bg_color,
   329                               &fg_color);
   330                               gtk_widget_add_events(event_box,
   331                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
   332 
   333             /* create EpgGridItem */
   334             EpgGridItem *epg_grid_item = g_new(EpgGridItem, 1);
   335             epg_grid_item->proginfo  = NULL;
   336             epg_grid_item->event_box = event_box;
   337             epg_grid_item->object    = mmyth_epg_grid_widget;
   338 
   339             epg_grid_list = g_list_prepend(epg_grid_list, (gpointer) epg_grid_item);
   340             
   341             gtk_box_pack_start (GTK_BOX (epg_line_hbox),
   342 	                            event_box, FALSE, FALSE, PROGRAM_SEPARATION);            	
   343 	                                 
   344    	        g_signal_connect (G_OBJECT (event_box), "button-press-event",
   345 		                      G_CALLBACK (mmyth_epg_grid_widget_clicked),
   346                               (gpointer*) epg_grid_list);
   347         }
   348 
   349         epg_grid_list = g_list_reverse(epg_grid_list);
   350         mmyth_epg_grid_widget->epg_view_model = 
   351               g_list_append(mmyth_epg_grid_widget->epg_view_model, epg_grid_list);
   352         
   353 	    gtk_box_pack_start (GTK_BOX (private->epg_programs_vbox),
   354 	                        epg_line_hbox, FALSE, FALSE,  0);                    		    		
   355     }
   356 }
   357 
   358 static void
   359 mmyth_epg_grid_widget_mount_header(MMythEpgGridWidget *mmyth_epg_grid_widget)
   360 {	          	    
   361     MMythEpgGridWidgetPrivate *private = 
   362         MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
   363     
   364     struct tm hour_tm;
   365     const gchar name_title[] = "Today";    
   366     GtkWidget * lbl_title = gtk_label_new(name_title);
   367 
   368     gtk_misc_set_alignment (GTK_MISC(lbl_title), 0.0, 0.5);
   369     
   370     gtk_box_pack_start (GTK_BOX (private->epg_channels_vbox),
   371                         lbl_title, FALSE, FALSE, 0);
   372 
   373 	/* hours title line */
   374     GtkWidget *epg_programs_hours_hbox = gtk_hbox_new (TRUE, 0); 
   375 
   376     if (localtime_r((time_t *)&private->current_start_time, &hour_tm) == NULL) {
   377         g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
   378         return NULL;
   379     }    
   380 
   381     if (hour_tm.tm_min>30) {
   382         hour_tm.tm_min = 30;
   383     } else if (hour_tm.tm_min>0) {
   384         hour_tm.tm_min = 0;
   385     }
   386         
   387     gchar hour1_str[10];
   388     strftime(hour1_str, 8, "%H:%M", &hour_tm);
   389     GtkWidget * lbl_hour1 = gtk_label_new(hour1_str);
   390     gtk_misc_set_alignment (GTK_MISC(lbl_hour1), 0.0, 0.5);    
   391 	
   392     hour_tm.tm_hour++;
   393     gchar hour2_str[10];    
   394     strftime(hour2_str, 8, "%H:%M", &hour_tm);
   395     GtkWidget * lbl_hour2 = gtk_label_new(hour2_str);
   396     gtk_misc_set_alignment (GTK_MISC(lbl_hour2), 0.0, 0.5);        
   397 
   398     hour_tm.tm_hour++;
   399 	gchar hour3_str[10];    
   400     strftime(hour3_str, 8, "%H:%M", &hour_tm);
   401     GtkWidget * lbl_hour3 = gtk_label_new(hour3_str);
   402     gtk_misc_set_alignment (GTK_MISC(lbl_hour3), 0.0, 0.5);        
   403     
   404     gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
   405                         lbl_hour1, TRUE, TRUE, 0);                    
   406     gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
   407                         lbl_hour2, TRUE, TRUE, 0);                    
   408     gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
   409                         lbl_hour3, TRUE, TRUE, 0); 
   410  
   411     gtk_box_pack_start (GTK_BOX (private->epg_programs_vbox),
   412                         epg_programs_hours_hbox, FALSE, FALSE, 0);                        
   413 }
   414 
   415 /******************************************************************************
   416  *              INTERNAL CALLBACKS FOR STATE CHANGE                           *
   417  *****************************************************************************/
   418 static void 
   419 mmyth_epg_grid_widget_deselect_service(MMythEpgGridWidget *mmyth_epg_grid_widget)
   420 {
   421 	EpgGridItem *epg_grid_item;
   422 	
   423   	/* deselect*/
   424     if(mmyth_epg_grid_widget->selected_grid_item != NULL) {
   425     	epg_grid_item = 
   426             (EpgGridItem*) mmyth_epg_grid_widget->selected_grid_item->data;
   427     	gtk_widget_set_state(GTK_WIDGET(epg_grid_item->event_box), GTK_STATE_NORMAL);
   428     }            
   429 }
   430 
   431 static void 
   432 mmyth_epg_grid_widget_clicked (GtkWidget* widget, 
   433                                GdkEventExpose *event, gpointer data)
   434 {           
   435     g_return_if_fail(data != NULL);
   436 
   437     GList *epg_grid_item_list = (GList *) data;
   438     EpgGridItem *epg_grid_item = (EpgGridItem *) epg_grid_item_list->data;
   439 
   440     /* update the selected service */
   441     mmyth_epg_grid_widget_update_service( epg_grid_item->object, (GList*) data );       
   442 }
   443 
   444 void
   445 mmyth_epg_grid_widget_update_service(MMythEpgGridWidget * object,
   446                                      GList *selected_grid_list)
   447 {
   448     g_return_if_fail(object != NULL);
   449     g_return_if_fail(selected_grid_list != NULL);
   450 		
   451 	EpgGridItem *epg_grid_item = (EpgGridItem *) selected_grid_list->data;
   452 
   453     mmyth_epg_grid_widget_deselect_service(epg_grid_item->object);
   454 
   455     /* updating current selected schedule_item and schedule_list*/
   456     object->selected_grid_item = selected_grid_list;
   457     
   458     /* set state of the event box */
   459     gtk_widget_set_state(GTK_WIDGET(epg_grid_item->event_box), GTK_STATE_SELECTED);
   460     /* emit update signal for listeners */
   461     g_signal_emit(object, 
   462                   mmyth_epg_grid_widget_signals[SELECTION_UPDATED_SIGNAL],
   463                   0,
   464                   (gpointer) epg_grid_item);
   465 }
   466 
   467 static GtkWidget * 
   468 create_event_box_lbl(gchar *str, int width, const GdkColor *bg_color, 
   469                      const GdkColor *fg_color) 
   470 {
   471     GtkWidget *event_box = gtk_event_box_new();
   472 	GtkWidget *lbl = gtk_label_new(str);
   473     gtk_label_set_ellipsize(GTK_LABEL(lbl), PANGO_ELLIPSIZE_END);
   474 
   475     gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, bg_color);    
   476     gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, fg_color);    
   477     
   478     /* selected colors are const*/
   479     GdkColor selected_bg_color;
   480     selected_bg_color.red = 100;
   481     selected_bg_color.green = 40000;
   482     selected_bg_color.blue = 100;        
   483 
   484     GdkColor selected_fg_color;
   485     selected_fg_color.red = 100;
   486     selected_fg_color.green = 100;
   487     selected_fg_color.blue = 100;        
   488             
   489     gtk_widget_modify_bg(event_box, GTK_STATE_SELECTED, &selected_bg_color);    
   490     gtk_widget_modify_fg(lbl, GTK_STATE_SELECTED, &selected_fg_color);    
   491     
   492     gtk_misc_set_alignment (GTK_MISC(lbl), 0.0, 0.5);    
   493 	gtk_container_add (GTK_CONTAINER (event_box),
   494                        lbl);
   495 	gtk_widget_set_size_request(event_box, width, -1);
   496 	                       
   497     return event_box;
   498 }
   499 
   500 /******************************************************************************
   501  *                            METHODS                                         *
   502  *****************************************************************************/
   503 
   504 /* Callback for hardware keys */
   505 gboolean
   506 mmyth_epg_grid_widget_key_press (MMythEpgGridWidget * object, 
   507                                  GtkWidget * widget, GdkEventKey * event)
   508 {
   509     MMythEpgGridWidgetPrivate *private = 
   510         MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(object);
   511   
   512     EpgGridItem *epg_grid_item;
   513     GList *tmp;
   514     
   515     /* List of selected_grid_item */
   516     GList *selected_view_model;
   517     
   518     gint channel_index;
   519     
   520 	if(object->selected_grid_item == NULL) {
   521         g_warning ("No program selected");
   522 	    return FALSE;
   523 	} 
   524     
   525     epg_grid_item = (EpgGridItem*) object->selected_grid_item->data;    
   526     
   527     channel_index = private->selected_channel_index;
   528     
   529     switch (event->keyval) {
   530         case GDK_Up:        
   531             selected_view_model = g_list_nth( object->epg_view_model, channel_index - 1 );
   532             if(selected_view_model != NULL) {
   533                 private->selected_channel_index = channel_index - 1;
   534    	            tmp = (GList *) selected_view_model->data;               
   535                 /* TODO: select a better centralized item 
   536                    currently is picking the 1st or last item */
   537                 if(g_list_next(object->selected_grid_item) == NULL &&
   538                    g_list_previous(object->selected_grid_item) != NULL) {
   539                     /* in this case the new selected will be the last */ 
   540                     tmp = g_list_last(tmp);
   541                 }
   542 
   543 	            /* update the selected service */
   544                 mmyth_epg_grid_widget_update_service( object, tmp );
   545             }    
   546         return TRUE;         
   547         case GDK_Down:
   548             selected_view_model = g_list_nth( object->epg_view_model, channel_index + 1 );            
   549             if(selected_view_model != NULL) {   	            
   550                 private->selected_channel_index = channel_index + 1;
   551    	            tmp = (GList *) selected_view_model->data;
   552                 /* TODO: select a better centralized item 
   553                    currently is picking the 1st or last item */
   554                 if(g_list_next(object->selected_grid_item) == NULL &&
   555                    g_list_previous(object->selected_grid_item) != NULL) {
   556                     /* in this case the new selected will be the last */ 
   557                     tmp = g_list_last(tmp);
   558                 }
   559 
   560 	            /* update the selected service */
   561                 mmyth_epg_grid_widget_update_service( object, tmp );
   562             }
   563         return TRUE;        
   564         case GDK_Left:    
   565             tmp = g_list_previous( object->selected_grid_item );
   566             if(tmp != NULL) {
   567 	            /* update the selected service */
   568                 mmyth_epg_grid_widget_update_service( object, tmp );
   569             }
   570         return TRUE;        
   571         case GDK_Right:
   572             tmp = g_list_next( object->selected_grid_item );
   573             if(tmp != NULL) {
   574 	            /* update the selected service */
   575                 mmyth_epg_grid_widget_update_service( object, tmp );
   576             }
   577         return TRUE;        
   578 	    default:
   579 	    return TRUE;
   580     }
   581     
   582     return FALSE;
   583 }
   584 
   585 static void 
   586 mmyth_epg_grid_widget_fill_programinfos (MMythEpgGridWidgetPrivate *private)
   587 {
   588     GList *channels_list = NULL;
   589     int y;
   590 
   591     if ((private->mmyth_epg != NULL) &&
   592 	   	(gmyth_epg_get_channel_list (private->mmyth_epg, &channels_list) < 0 )) {
   593 			private->channel_list = NULL;
   594 			return;
   595     }
   596     
   597     private->channel_list = channels_list;
   598     
   599     for (y = 0; y < private->DISPLAY_CHANS && channels_list; y++) {
   600         GMythChannelInfo *channel_info = (GMythChannelInfo *) channels_list->data;
   601 
   602         mmyth_epg_grid_widget_fill_program_row_infos(
   603                             private, channel_info->channel_ID, y);
   604 
   605         channels_list = g_list_next (channels_list);
   606     }
   607 }
   608 
   609 static void 
   610 mmyth_epg_grid_widget_fill_program_row_infos(MMythEpgGridWidgetPrivate *private,
   611                                              guint chanNum, guint row)
   612 {    
   613     gint res =  gmyth_epg_get_program_list (private->mmyth_epg,
   614     					&(private->program_list[row]),
   615                         chanNum, private->current_start_time, 
   616                         private->current_end_time);
   617                         
   618     if (res < 0) {
   619     	g_warning ("[%s] Error while retrieving epg programs", __FUNCTION__);
   620     }
   621 }
   622