[svn r217] Added signals to program changed messages coming from backend (MythTV).
1 #include <gtk/gtksignal.h>
2 #include <gdk/gdkevents.h>
3 #include <gdk/gdkkeysyms.h>
5 #include "mmyth_uicommon.h"
6 #include "mmyth_epg_grid_widget.h"
8 #include "gmyth_util.h"
11 #define PIXELS_HOUR 105
12 #define PROGRAM_SEPARATION 2
15 SELECTION_UPDATED_SIGNAL,
19 struct _MMythEpgGridWidgetPrivate {
20 /* private widget components */
21 GtkWidget *epg_channels_vbox;
22 GtkWidget *epg_programs_vbox;
24 GHashTable *service_model_hash;
26 /* guidegrid attributes */
27 gboolean show_favorites;
28 gint current_start_channel_id;
30 time_t current_start_time;
31 time_t current_end_time;
33 guint selected_channel_index;
35 /* GList of ProgramInfo for each Channel */
36 GList * program_list[MAX_DISPLAY_CHANS];
41 GMythBackendInfo *backend_info;
46 static void mmyth_epg_grid_widget_class_init (MMythEpgGridWidgetClass *klass);
47 static void mmyth_epg_grid_widget_init (MMythEpgGridWidget *object);
48 static void mmyth_epg_grid_widget_private_init (MMythEpgGridWidgetPrivate *private);
49 static void mmyth_epg_grid_widget_mount_services (MMythEpgGridWidget *object,
50 int start_time, int end_time);
51 static void mmyth_epg_grid_widget_mount_header (MMythEpgGridWidget *object);
52 static void mmyth_epg_grid_widget_clicked (GtkWidget* widget,
53 GdkEventExpose *event,
55 static GtkWidget *create_event_box_lbl (gchar *str, int width,
56 const GdkColor *bg_color,
57 const GdkColor *fg_color);
59 static void mmyth_epg_grid_widget_fill_programinfos(MMythEpgGridWidgetPrivate *private);
60 static void mmyth_epg_grid_widget_fill_program_row_infos(
61 MMythEpgGridWidgetPrivate *private,
62 unsigned int chanNum, unsigned int row);
64 static gint mmyth_epg_grid_widget_signals[LAST_SIGNAL] = { 0 };
66 G_DEFINE_TYPE(MMythEpgGridWidget, mmyth_epg_grid_widget, GTK_TYPE_EVENT_BOX)
69 mmyth_epg_grid_widget_class_init (MMythEpgGridWidgetClass *klass)
71 g_type_class_add_private (klass, sizeof (MMythEpgGridWidgetPrivate));
73 mmyth_epg_grid_widget_signals[SELECTION_UPDATED_SIGNAL] = g_signal_new (
75 G_TYPE_FROM_CLASS(klass),
80 g_cclosure_marshal_VOID__POINTER,
87 mmyth_epg_grid_widget_private_init (MMythEpgGridWidgetPrivate *private)
91 g_return_if_fail(private != NULL);
93 private->epg_channels_vbox = NULL;
94 private->epg_programs_vbox = NULL;
95 private->service_model_hash = NULL;
97 private->show_favorites = FALSE;
98 private->current_start_channel_id = -1;
100 /* Selected the first diplayable channel initially */
101 private->selected_channel_index = 0;
103 /* TODO fix the current start/end time */
104 private->current_start_time = time(&cur_time);
105 private->current_end_time = time(&cur_time) + 10800;
107 private->DISPLAY_CHANS = MAX_DISPLAY_CHANS;
109 private->backend_info = gmyth_backend_info_new_full ( "192.168.1.109", "mythtv",
110 "mythtv", "mythconverg", 6543 );
112 // TODO: Close the epg and unref it in dispose call
113 private->mmyth_epg = gmyth_epg_new ();
114 if (!gmyth_epg_connect (private->mmyth_epg, private->backend_info)) {
115 g_warning ("[%s] Could not connect mysql handler to db", __FUNCTION__);
116 g_object_unref (private->mmyth_epg);
117 private->mmyth_epg = NULL;
122 mmyth_epg_grid_widget_init (MMythEpgGridWidget *mmyth_epg_grid_widget)
124 MMythEpgGridWidgetPrivate *private =
125 MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
127 /* init private fields */
128 mmyth_epg_grid_widget_private_init(private);
130 mmyth_epg_grid_widget->epg_view_model = NULL;
131 mmyth_epg_grid_widget->selected_grid_item = NULL;
133 GtkWidget *epg_event_box = GTK_WIDGET(mmyth_epg_grid_widget);
134 gtk_widget_modify_bg(epg_event_box, GTK_STATE_NORMAL, &main_bg_color);
135 gtk_widget_set_size_request (epg_event_box, 0, 125);
137 GtkWidget *epg_main_hbox = gtk_hbox_new (FALSE, 10);
138 gtk_container_set_border_width(GTK_CONTAINER (epg_main_hbox), 10);
140 gtk_container_add (GTK_CONTAINER (epg_event_box),
144 GtkWidget *epg_channels_vbox = gtk_vbox_new (FALSE, 3);
145 private->epg_channels_vbox = epg_channels_vbox;
148 GtkWidget *epg_programs_vbox = gtk_vbox_new (FALSE, 3);
149 private->epg_programs_vbox = epg_programs_vbox;
152 gtk_box_pack_start (GTK_BOX (epg_main_hbox),
153 epg_channels_vbox, FALSE, FALSE, 0);
154 gtk_box_pack_start (GTK_BOX (epg_main_hbox),
155 epg_programs_vbox, FALSE, FALSE, 0);
157 /* table header (first line) */
158 mmyth_epg_grid_widget_mount_header(mmyth_epg_grid_widget);
160 /* service programs */
161 /* mount service programs with current time */
162 mmyth_epg_grid_widget_mount_services(mmyth_epg_grid_widget,
163 private->current_start_time,
164 private->current_end_time);
168 mmyth_epg_grid_widget_new ()
170 return GTK_WIDGET ( gtk_type_new (mmyth_epg_grid_widget_get_type ()));
174 mmyth_epg_grid_widget_mount_services(MMythEpgGridWidget *mmyth_epg_grid_widget,
175 int start_time, int end_time)
178 GList *channel_list = NULL;
179 GMythChannelInfo *channel_info;
182 MMythEpgGridWidgetPrivate *private =
183 MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
186 /* FIXME shallow free or recursive? */
187 if(mmyth_epg_grid_widget->epg_view_model != NULL) {
188 g_list_free(mmyth_epg_grid_widget->epg_view_model);
189 mmyth_epg_grid_widget->epg_view_model = NULL;
192 if(private->service_model_hash != NULL) {
193 g_hash_table_destroy(private->service_model_hash);
196 private->service_model_hash = g_hash_table_new(NULL, NULL);
198 /* fill program infos from db */
199 mmyth_epg_grid_widget_fill_programinfos(private);
201 channel_list = private->channel_list;
203 /* for each channel get_programs() */
204 for (chanid=0; channel_list &&
205 chanid < private->DISPLAY_CHANS; chanid++) {
206 proglist = (GList *) private->program_list[chanid];
208 channel_info = (GMythChannelInfo *) channel_list->data;
209 channel_list = g_list_next(channel_list);
212 GString *name = NULL;
213 if (channel_info->channel_name)
214 name = g_string_new (channel_info->channel_name->str);
216 GdkColor title_bg_color;
217 title_bg_color.red = 5000;
218 title_bg_color.green = 9000;
219 title_bg_color.blue = 40000;
221 GdkColor title_fg_color;
222 title_fg_color.red = 60000;
223 title_fg_color.green = 60000;
224 title_fg_color.blue = 60000;
226 GtkWidget *event_box_channel = create_event_box_lbl(
231 gtk_box_pack_start (GTK_BOX (private->epg_channels_vbox),
232 event_box_channel, FALSE, FALSE, 0);
234 GtkWidget *epg_line_hbox = gtk_hbox_new (FALSE, 0);
238 bg_color.green = 30000;
239 bg_color.blue = 60000;
242 fg_color.red = 60000;
243 fg_color.green = 60000;
244 fg_color.blue = 60000;
246 /* Content parsing */
247 GList *epg_grid_list = NULL;
249 GMythProgramInfo *proginfo;
251 for (; proglist; proglist = proglist->next) {
252 proginfo = (GMythProgramInfo *) proglist->data;
254 GString *content_name = proginfo->title;
256 int initial_time, last_time, duration;
258 int schedule_start_time = proginfo->startts;
259 int schedule_end_time = proginfo->endts;
262 (schedule_start_time < start_time) ? start_time : schedule_start_time;
263 last_time = (schedule_end_time > end_time) ? end_time : schedule_end_time;
264 duration = last_time - initial_time;
266 // Verify program time
268 g_debug ("ServiceID: %d, ScheduleID: %d\n", service->id, schedule->id);
269 fprintf (stderr, "program time\nfrom = %d, to = %d\n",
270 schedule->validFrom, schedule->validTo);
274 /* Convert it to local time representation. */
275 if (localtime_r((time_t *)&schedule->validFrom, &loctime) == NULL) {
276 g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
279 fprintf (stderr, asctime (&loctime));
281 if (localtime_r((time_t *)&schedule->validTo, &loctime) == NULL) {
282 g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
285 fprintf (stderr, asctime (&loctime));
288 /* fprintf(stderr, "duration = %d\n", duration); */
289 double duration_hour = duration / (double) 3600.0;
290 /* fprintf(stderr, "duration_hour = %lf\n", duration_hour); */
292 int size = PIXELS_HOUR * duration_hour;
295 /* FIXME: UGLY WRONG HACK TO ALIGN PROGRAM TIME!!!*/
296 if(last_time%3600 != 0) {
297 size -= PROGRAM_SEPARATION;
299 if(initial_time%3600 != 0) {
300 size -= PROGRAM_SEPARATION;
303 pixel_count += size + PROGRAM_SEPARATION;
304 GtkWidget *event_box = create_event_box_lbl(content_name->str,
307 gtk_widget_add_events(event_box,
308 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
310 /* create EpgGridItem */
311 EpgGridItem *epg_grid_item = g_new(EpgGridItem, 1);
312 epg_grid_item->proginfo = proginfo;
313 epg_grid_item->event_box = event_box;
314 epg_grid_item->object = mmyth_epg_grid_widget;
316 epg_grid_list = g_list_prepend(epg_grid_list, (gpointer) epg_grid_item);
318 gtk_box_pack_start (GTK_BOX (epg_line_hbox),
319 event_box, FALSE, FALSE, PROGRAM_SEPARATION);
321 g_signal_connect (G_OBJECT (event_box), "button-press-event",
322 G_CALLBACK (mmyth_epg_grid_widget_clicked),
323 (gpointer*) epg_grid_list);
326 printf("chaind = %d!!!!" chanid);fflush(stdout);
330 /* No programs for current channel */
331 /* FIXME: size HARDCODED */
332 GtkWidget *event_box = create_event_box_lbl("No program list available",
333 PIXELS_HOUR * 3, &bg_color,
335 gtk_widget_add_events(event_box,
336 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
338 /* create EpgGridItem */
339 EpgGridItem *epg_grid_item = g_new(EpgGridItem, 1);
340 epg_grid_item->proginfo = NULL;
341 epg_grid_item->event_box = event_box;
342 epg_grid_item->object = mmyth_epg_grid_widget;
344 epg_grid_list = g_list_prepend(epg_grid_list, (gpointer) epg_grid_item);
346 gtk_box_pack_start (GTK_BOX (epg_line_hbox),
347 event_box, FALSE, FALSE, PROGRAM_SEPARATION);
349 g_signal_connect (G_OBJECT (event_box), "button-press-event",
350 G_CALLBACK (mmyth_epg_grid_widget_clicked),
351 (gpointer*) epg_grid_list);
354 epg_grid_list = g_list_reverse(epg_grid_list);
355 mmyth_epg_grid_widget->epg_view_model =
356 g_list_append(mmyth_epg_grid_widget->epg_view_model, epg_grid_list);
358 gtk_box_pack_start (GTK_BOX (private->epg_programs_vbox),
359 epg_line_hbox, FALSE, FALSE, 0);
364 mmyth_epg_grid_widget_mount_header(MMythEpgGridWidget *mmyth_epg_grid_widget)
366 MMythEpgGridWidgetPrivate *private =
367 MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
370 const gchar name_title[] = "Today";
371 GtkWidget * lbl_title = gtk_label_new(name_title);
373 gtk_misc_set_alignment (GTK_MISC(lbl_title), 0.0, 0.5);
375 gtk_box_pack_start (GTK_BOX (private->epg_channels_vbox),
376 lbl_title, FALSE, FALSE, 0);
378 /* hours title line */
379 GtkWidget *epg_programs_hours_hbox = gtk_hbox_new (TRUE, 0);
381 if (localtime_r((time_t *)&private->current_start_time, &hour_tm) == NULL) {
382 g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
386 if (hour_tm.tm_min>30) {
388 } else if (hour_tm.tm_min>0) {
393 strftime(hour1_str, 8, "%H:%M", &hour_tm);
394 GtkWidget * lbl_hour1 = gtk_label_new(hour1_str);
395 gtk_misc_set_alignment (GTK_MISC(lbl_hour1), 0.0, 0.5);
399 strftime(hour2_str, 8, "%H:%M", &hour_tm);
400 GtkWidget * lbl_hour2 = gtk_label_new(hour2_str);
401 gtk_misc_set_alignment (GTK_MISC(lbl_hour2), 0.0, 0.5);
405 strftime(hour3_str, 8, "%H:%M", &hour_tm);
406 GtkWidget * lbl_hour3 = gtk_label_new(hour3_str);
407 gtk_misc_set_alignment (GTK_MISC(lbl_hour3), 0.0, 0.5);
409 gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
410 lbl_hour1, TRUE, TRUE, 0);
411 gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
412 lbl_hour2, TRUE, TRUE, 0);
413 gtk_box_pack_start (GTK_BOX (epg_programs_hours_hbox),
414 lbl_hour3, TRUE, TRUE, 0);
416 gtk_box_pack_start (GTK_BOX (private->epg_programs_vbox),
417 epg_programs_hours_hbox, FALSE, FALSE, 0);
420 /******************************************************************************
421 * INTERNAL CALLBACKS FOR STATE CHANGE *
422 *****************************************************************************/
424 mmyth_epg_grid_widget_deselect_service(MMythEpgGridWidget *mmyth_epg_grid_widget)
426 EpgGridItem *epg_grid_item;
429 if(mmyth_epg_grid_widget->selected_grid_item != NULL) {
431 (EpgGridItem*) mmyth_epg_grid_widget->selected_grid_item->data;
432 gtk_widget_set_state(GTK_WIDGET(epg_grid_item->event_box), GTK_STATE_NORMAL);
437 mmyth_epg_grid_widget_clicked (GtkWidget* widget,
438 GdkEventExpose *event, gpointer data)
440 g_return_if_fail(data != NULL);
442 GList *epg_grid_item_list = (GList *) data;
443 EpgGridItem *epg_grid_item = (EpgGridItem *) epg_grid_item_list->data;
445 /* update the selected service */
446 mmyth_epg_grid_widget_update_service( epg_grid_item->object, (GList*) data );
450 mmyth_epg_grid_widget_update_service(MMythEpgGridWidget * object,
451 GList *selected_grid_list)
453 g_return_if_fail(object != NULL);
454 g_return_if_fail(selected_grid_list != NULL);
456 EpgGridItem *epg_grid_item = (EpgGridItem *) selected_grid_list->data;
458 mmyth_epg_grid_widget_deselect_service(epg_grid_item->object);
460 /* updating current selected schedule_item and schedule_list*/
461 object->selected_grid_item = selected_grid_list;
463 /* set state of the event box */
464 gtk_widget_set_state(GTK_WIDGET(epg_grid_item->event_box), GTK_STATE_SELECTED);
465 /* emit update signal for listeners */
466 g_signal_emit(object,
467 mmyth_epg_grid_widget_signals[SELECTION_UPDATED_SIGNAL],
469 (gpointer) epg_grid_item);
473 create_event_box_lbl(gchar *str, int width, const GdkColor *bg_color,
474 const GdkColor *fg_color)
476 GtkWidget *event_box = gtk_event_box_new();
477 GtkWidget *lbl = gtk_label_new(str);
478 gtk_label_set_ellipsize(GTK_LABEL(lbl), PANGO_ELLIPSIZE_END);
480 gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, bg_color);
481 gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, fg_color);
483 /* selected colors are const*/
484 GdkColor selected_bg_color;
485 selected_bg_color.red = 100;
486 selected_bg_color.green = 40000;
487 selected_bg_color.blue = 100;
489 GdkColor selected_fg_color;
490 selected_fg_color.red = 100;
491 selected_fg_color.green = 100;
492 selected_fg_color.blue = 100;
494 gtk_widget_modify_bg(event_box, GTK_STATE_SELECTED, &selected_bg_color);
495 gtk_widget_modify_fg(lbl, GTK_STATE_SELECTED, &selected_fg_color);
497 gtk_misc_set_alignment (GTK_MISC(lbl), 0.0, 0.5);
498 gtk_container_add (GTK_CONTAINER (event_box),
500 gtk_widget_set_size_request(event_box, width, -1);
505 /******************************************************************************
507 *****************************************************************************/
509 /* Callback for hardware keys */
511 mmyth_epg_grid_widget_key_press (MMythEpgGridWidget * object,
512 GtkWidget * widget, GdkEventKey * event)
514 MMythEpgGridWidgetPrivate *private =
515 MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(object);
517 EpgGridItem *epg_grid_item;
520 /* List of selected_grid_item */
521 GList *selected_view_model;
525 if(object->selected_grid_item == NULL) {
526 g_warning ("No program selected");
530 epg_grid_item = (EpgGridItem*) object->selected_grid_item->data;
532 channel_index = private->selected_channel_index;
534 switch (event->keyval) {
536 selected_view_model = g_list_nth( object->epg_view_model, channel_index - 1 );
537 if(selected_view_model != NULL) {
538 private->selected_channel_index = channel_index - 1;
539 tmp = (GList *) selected_view_model->data;
540 /* TODO: select a better centralized item
541 currently is picking the 1st or last item */
542 if(g_list_next(object->selected_grid_item) == NULL &&
543 g_list_previous(object->selected_grid_item) != NULL) {
544 /* in this case the new selected will be the last */
545 tmp = g_list_last(tmp);
548 /* update the selected service */
549 mmyth_epg_grid_widget_update_service( object, tmp );
553 selected_view_model = g_list_nth( object->epg_view_model, channel_index + 1 );
554 if(selected_view_model != NULL) {
555 private->selected_channel_index = channel_index + 1;
556 tmp = (GList *) selected_view_model->data;
557 /* TODO: select a better centralized item
558 currently is picking the 1st or last item */
559 if(g_list_next(object->selected_grid_item) == NULL &&
560 g_list_previous(object->selected_grid_item) != NULL) {
561 /* in this case the new selected will be the last */
562 tmp = g_list_last(tmp);
565 /* update the selected service */
566 mmyth_epg_grid_widget_update_service( object, tmp );
570 tmp = g_list_previous( object->selected_grid_item );
572 /* update the selected service */
573 mmyth_epg_grid_widget_update_service( object, tmp );
577 tmp = g_list_next( object->selected_grid_item );
579 /* update the selected service */
580 mmyth_epg_grid_widget_update_service( object, tmp );
591 mmyth_epg_grid_widget_fill_programinfos (MMythEpgGridWidgetPrivate *private)
593 GList *channels_list = NULL;
596 if ((private->mmyth_epg != NULL) &&
597 (gmyth_epg_get_channel_list (private->mmyth_epg, &channels_list) < 0 )) {
598 private->channel_list = NULL;
602 private->channel_list = channels_list;
604 for (y = 0; y < private->DISPLAY_CHANS && channels_list; y++) {
605 GMythChannelInfo *channel_info = (GMythChannelInfo *) channels_list->data;
607 mmyth_epg_grid_widget_fill_program_row_infos(
608 private, channel_info->channel_ID, y);
610 channels_list = g_list_next (channels_list);
615 mmyth_epg_grid_widget_fill_program_row_infos(MMythEpgGridWidgetPrivate *private,
616 guint chanNum, guint row)
618 gint res = gmyth_epg_get_program_list (private->mmyth_epg,
619 &(private->program_list[row]),
620 chanNum, private->current_start_time,
621 private->current_end_time);
624 g_warning ("[%s] Error while retrieving epg programs", __FUNCTION__);