[svn r200] Put a GThreadPool, for a better memory usage.
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];
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,
53 static GtkWidget *create_event_box_lbl (gchar *str, int width,
54 const GdkColor *bg_color,
55 const GdkColor *fg_color);
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);
62 static gint mmyth_epg_grid_widget_signals[LAST_SIGNAL] = { 0 };
64 G_DEFINE_TYPE(MMythEpgGridWidget, mmyth_epg_grid_widget, GTK_TYPE_EVENT_BOX)
67 mmyth_epg_grid_widget_class_init (MMythEpgGridWidgetClass *klass)
69 g_type_class_add_private (klass, sizeof (MMythEpgGridWidgetPrivate));
71 mmyth_epg_grid_widget_signals[SELECTION_UPDATED_SIGNAL] = g_signal_new (
73 G_TYPE_FROM_CLASS(klass),
78 g_cclosure_marshal_VOID__POINTER,
85 mmyth_epg_grid_widget_private_init (MMythEpgGridWidgetPrivate *private)
89 g_return_if_fail(private != NULL);
91 private->epg_channels_vbox = NULL;
92 private->epg_programs_vbox = NULL;
93 private->service_model_hash = NULL;
95 private->show_favorites = FALSE;
96 private->current_start_channel_id = -1;
98 /* Selected the first diplayable channel initially */
99 private->selected_channel_index = 0;
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;
105 private->DISPLAY_CHANS = MAX_DISPLAY_CHANS;
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;
117 mmyth_epg_grid_widget_init (MMythEpgGridWidget *mmyth_epg_grid_widget)
119 MMythEpgGridWidgetPrivate *private =
120 MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
122 /* init private fields */
123 mmyth_epg_grid_widget_private_init(private);
125 mmyth_epg_grid_widget->epg_view_model = NULL;
126 mmyth_epg_grid_widget->selected_grid_item = NULL;
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);
132 GtkWidget *epg_main_hbox = gtk_hbox_new (FALSE, 10);
133 gtk_container_set_border_width(GTK_CONTAINER (epg_main_hbox), 10);
135 gtk_container_add (GTK_CONTAINER (epg_event_box),
139 GtkWidget *epg_channels_vbox = gtk_vbox_new (FALSE, 3);
140 private->epg_channels_vbox = epg_channels_vbox;
143 GtkWidget *epg_programs_vbox = gtk_vbox_new (FALSE, 3);
144 private->epg_programs_vbox = epg_programs_vbox;
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);
152 /* table header (first line) */
153 mmyth_epg_grid_widget_mount_header(mmyth_epg_grid_widget);
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);
163 mmyth_epg_grid_widget_new ()
165 return GTK_WIDGET ( gtk_type_new (mmyth_epg_grid_widget_get_type ()));
169 mmyth_epg_grid_widget_mount_services(MMythEpgGridWidget *mmyth_epg_grid_widget,
170 int start_time, int end_time)
173 GList *channel_list = NULL;
174 GMythChannelInfo *channel_info;
177 MMythEpgGridWidgetPrivate *private =
178 MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
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;
187 if(private->service_model_hash != NULL) {
188 g_hash_table_destroy(private->service_model_hash);
191 private->service_model_hash = g_hash_table_new(NULL, NULL);
193 /* fill program infos from db */
194 mmyth_epg_grid_widget_fill_programinfos(private);
196 channel_list = private->channel_list;
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];
203 channel_info = (GMythChannelInfo *) channel_list->data;
204 channel_list = g_list_next(channel_list);
207 GString *name = NULL;
208 if (channel_info->channel_name)
209 name = g_string_new (channel_info->channel_name->str);
211 GdkColor title_bg_color;
212 title_bg_color.red = 5000;
213 title_bg_color.green = 9000;
214 title_bg_color.blue = 40000;
216 GdkColor title_fg_color;
217 title_fg_color.red = 60000;
218 title_fg_color.green = 60000;
219 title_fg_color.blue = 60000;
221 GtkWidget *event_box_channel = create_event_box_lbl(
226 gtk_box_pack_start (GTK_BOX (private->epg_channels_vbox),
227 event_box_channel, FALSE, FALSE, 0);
229 GtkWidget *epg_line_hbox = gtk_hbox_new (FALSE, 0);
233 bg_color.green = 30000;
234 bg_color.blue = 60000;
237 fg_color.red = 60000;
238 fg_color.green = 60000;
239 fg_color.blue = 60000;
241 /* Content parsing */
242 GList *epg_grid_list = NULL;
244 GMythProgramInfo *proginfo;
246 for (; proglist; proglist = proglist->next) {
247 proginfo = (GMythProgramInfo *) proglist->data;
249 GString *content_name = proginfo->title;
251 int initial_time, last_time, duration;
253 int schedule_start_time = proginfo->startts;
254 int schedule_end_time = proginfo->endts;
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;
261 // Verify program time
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);
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");
274 fprintf (stderr, asctime (&loctime));
276 if (localtime_r((time_t *)&schedule->validTo, &loctime) == NULL) {
277 g_warning ("localtime_r error in mmyth_epg_grid_widget!\n");
280 fprintf (stderr, asctime (&loctime));
283 /* fprintf(stderr, "duration = %d\n", duration); */
284 double duration_hour = duration / (double) 3600.0;
285 /* fprintf(stderr, "duration_hour = %lf\n", duration_hour); */
287 int size = PIXELS_HOUR * duration_hour;
290 /* FIXME: UGLY WRONG HACK TO ALIGN PROGRAM TIME!!!*/
291 if(last_time%3600 != 0) {
292 size -= PROGRAM_SEPARATION;
294 if(initial_time%3600 != 0) {
295 size -= PROGRAM_SEPARATION;
298 pixel_count += size + PROGRAM_SEPARATION;
299 GtkWidget *event_box = create_event_box_lbl(content_name->str,
302 gtk_widget_add_events(event_box,
303 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
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;
311 epg_grid_list = g_list_prepend(epg_grid_list, (gpointer) epg_grid_item);
313 gtk_box_pack_start (GTK_BOX (epg_line_hbox),
314 event_box, FALSE, FALSE, PROGRAM_SEPARATION);
316 g_signal_connect (G_OBJECT (event_box), "button-press-event",
317 G_CALLBACK (mmyth_epg_grid_widget_clicked),
318 (gpointer*) epg_grid_list);
321 printf("chaind = %d!!!!" chanid);fflush(stdout);
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,
330 gtk_widget_add_events(event_box,
331 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
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;
339 epg_grid_list = g_list_prepend(epg_grid_list, (gpointer) epg_grid_item);
341 gtk_box_pack_start (GTK_BOX (epg_line_hbox),
342 event_box, FALSE, FALSE, PROGRAM_SEPARATION);
344 g_signal_connect (G_OBJECT (event_box), "button-press-event",
345 G_CALLBACK (mmyth_epg_grid_widget_clicked),
346 (gpointer*) epg_grid_list);
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);
353 gtk_box_pack_start (GTK_BOX (private->epg_programs_vbox),
354 epg_line_hbox, FALSE, FALSE, 0);
359 mmyth_epg_grid_widget_mount_header(MMythEpgGridWidget *mmyth_epg_grid_widget)
361 MMythEpgGridWidgetPrivate *private =
362 MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(mmyth_epg_grid_widget);
365 const gchar name_title[] = "Today";
366 GtkWidget * lbl_title = gtk_label_new(name_title);
368 gtk_misc_set_alignment (GTK_MISC(lbl_title), 0.0, 0.5);
370 gtk_box_pack_start (GTK_BOX (private->epg_channels_vbox),
371 lbl_title, FALSE, FALSE, 0);
373 /* hours title line */
374 GtkWidget *epg_programs_hours_hbox = gtk_hbox_new (TRUE, 0);
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");
381 if (hour_tm.tm_min>30) {
383 } else if (hour_tm.tm_min>0) {
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);
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);
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);
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);
411 gtk_box_pack_start (GTK_BOX (private->epg_programs_vbox),
412 epg_programs_hours_hbox, FALSE, FALSE, 0);
415 /******************************************************************************
416 * INTERNAL CALLBACKS FOR STATE CHANGE *
417 *****************************************************************************/
419 mmyth_epg_grid_widget_deselect_service(MMythEpgGridWidget *mmyth_epg_grid_widget)
421 EpgGridItem *epg_grid_item;
424 if(mmyth_epg_grid_widget->selected_grid_item != NULL) {
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);
432 mmyth_epg_grid_widget_clicked (GtkWidget* widget,
433 GdkEventExpose *event, gpointer data)
435 g_return_if_fail(data != NULL);
437 GList *epg_grid_item_list = (GList *) data;
438 EpgGridItem *epg_grid_item = (EpgGridItem *) epg_grid_item_list->data;
440 /* update the selected service */
441 mmyth_epg_grid_widget_update_service( epg_grid_item->object, (GList*) data );
445 mmyth_epg_grid_widget_update_service(MMythEpgGridWidget * object,
446 GList *selected_grid_list)
448 g_return_if_fail(object != NULL);
449 g_return_if_fail(selected_grid_list != NULL);
451 EpgGridItem *epg_grid_item = (EpgGridItem *) selected_grid_list->data;
453 mmyth_epg_grid_widget_deselect_service(epg_grid_item->object);
455 /* updating current selected schedule_item and schedule_list*/
456 object->selected_grid_item = selected_grid_list;
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],
464 (gpointer) epg_grid_item);
468 create_event_box_lbl(gchar *str, int width, const GdkColor *bg_color,
469 const GdkColor *fg_color)
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);
475 gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, bg_color);
476 gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, fg_color);
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;
484 GdkColor selected_fg_color;
485 selected_fg_color.red = 100;
486 selected_fg_color.green = 100;
487 selected_fg_color.blue = 100;
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);
492 gtk_misc_set_alignment (GTK_MISC(lbl), 0.0, 0.5);
493 gtk_container_add (GTK_CONTAINER (event_box),
495 gtk_widget_set_size_request(event_box, width, -1);
500 /******************************************************************************
502 *****************************************************************************/
504 /* Callback for hardware keys */
506 mmyth_epg_grid_widget_key_press (MMythEpgGridWidget * object,
507 GtkWidget * widget, GdkEventKey * event)
509 MMythEpgGridWidgetPrivate *private =
510 MMYTH_EPG_GRID_WIDGET_GET_PRIVATE(object);
512 EpgGridItem *epg_grid_item;
515 /* List of selected_grid_item */
516 GList *selected_view_model;
520 if(object->selected_grid_item == NULL) {
521 g_warning ("No program selected");
525 epg_grid_item = (EpgGridItem*) object->selected_grid_item->data;
527 channel_index = private->selected_channel_index;
529 switch (event->keyval) {
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);
543 /* update the selected service */
544 mmyth_epg_grid_widget_update_service( object, tmp );
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);
560 /* update the selected service */
561 mmyth_epg_grid_widget_update_service( object, tmp );
565 tmp = g_list_previous( object->selected_grid_item );
567 /* update the selected service */
568 mmyth_epg_grid_widget_update_service( object, tmp );
572 tmp = g_list_next( object->selected_grid_item );
574 /* update the selected service */
575 mmyth_epg_grid_widget_update_service( object, tmp );
586 mmyth_epg_grid_widget_fill_programinfos (MMythEpgGridWidgetPrivate *private)
588 GList *channels_list = NULL;
591 if ((private->mmyth_epg != NULL) &&
592 (gmyth_epg_get_channel_list (private->mmyth_epg, &channels_list) < 0 )) {
593 private->channel_list = NULL;
597 private->channel_list = channels_list;
599 for (y = 0; y < private->DISPLAY_CHANS && channels_list; y++) {
600 GMythChannelInfo *channel_info = (GMythChannelInfo *) channels_list->data;
602 mmyth_epg_grid_widget_fill_program_row_infos(
603 private, channel_info->channel_ID, y);
605 channels_list = g_list_next (channels_list);
610 mmyth_epg_grid_widget_fill_program_row_infos(MMythEpgGridWidgetPrivate *private,
611 guint chanNum, guint row)
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);
619 g_warning ("[%s] Error while retrieving epg programs", __FUNCTION__);