From e9e403ce7ababa15839bdb9b28046f08c918bc55 Mon Sep 17 00:00:00 2001 From: Florian Brosch Date: Sun, 29 Oct 2017 16:38:00 +0100 Subject: [PATCH] month-view: Show weather reports. This commit triggers two existing issues: 1. window size calculation (this breaks month-cells) 2. wrong reference counts in views (I use :destroy to avoid dangling pointers) --- data/ui/month-cell.ui | 7 +- src/gcal-window.c | 3 +- src/views/gcal-month-cell.c | 35 +++++++ src/views/gcal-month-cell.h | 4 + src/views/gcal-month-view.c | 189 +++++++++++++++++++++++++++++++++++- src/views/gcal-month-view.h | 6 ++ 6 files changed, 240 insertions(+), 4 deletions(-) diff --git a/data/ui/month-cell.ui b/data/ui/month-cell.ui index 5af53880..3ad9f325 100644 --- a/data/ui/month-cell.ui +++ b/data/ui/month-cell.ui @@ -31,7 +31,11 @@ False - gtk-missing-image + True + 16 + @@ -67,7 +71,6 @@ True False end - 0.0 diff --git a/src/gcal-window.c b/src/gcal-window.c index cd13888c..7299af31 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -1670,13 +1670,14 @@ gcal_window_init (GcalWindow *self) * FIXME: this is a hack around the issue that happens when trying to bind * there properties using the GtkBuilder .ui file. */ - g_object_bind_property (self, "weather-service", self->week_view, "weather-service", G_BINDING_DEFAULT); g_object_bind_property (self, "manager", self->edit_dialog, "manager", G_BINDING_DEFAULT); g_object_bind_property (self, "manager", self->source_dialog, "manager", G_BINDING_DEFAULT); g_object_bind_property (self, "manager", self->week_view, "manager", G_BINDING_DEFAULT); g_object_bind_property (self, "manager", self->month_view, "manager", G_BINDING_DEFAULT); g_object_bind_property (self, "manager", self->year_view, "manager", G_BINDING_DEFAULT); g_object_bind_property (self, "manager", self->quick_add_popover, "manager", G_BINDING_DEFAULT); + g_object_bind_property (self, "weather-service", self->week_view, "weather-service", G_BINDING_DEFAULT); + g_object_bind_property (self, "weather-service", self->month_view, "weather-service", G_BINDING_DEFAULT); /* setup accels */ app = g_application_get_default (); diff --git a/src/views/gcal-month-cell.c b/src/views/gcal-month-cell.c index d55216db..8e476152 100644 --- a/src/views/gcal-month-cell.c +++ b/src/views/gcal-month-cell.c @@ -33,6 +33,8 @@ struct _GcalMonthCell GtkLabel *day_label; GtkWidget *header_box; + GtkImage *weather_icon; + GtkWidget *overflow_button; GtkWidget *overflow_label; GtkWidget *overlay; @@ -427,6 +429,7 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass) gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, overflow_button); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, overflow_label); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, overlay); + gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, weather_icon); gtk_widget_class_bind_template_callback (widget_class, enter_notify_event_cb); gtk_widget_class_bind_template_callback (widget_class, leave_notify_event_cb); @@ -485,6 +488,38 @@ gcal_month_cell_set_date (GcalMonthCell *self, update_style_flags (self); } +/** + * gcal_month_cell_set_weather: + * @self: The #GcalMonthCell instance. + * @info: (nullable): The weather information to display. + * + * Sets the weather information to display for this day. + * + * Note that this function does not check dates nor + * manages weather information changes on its own. + */ +void +gcal_month_cell_set_weather (GcalMonthCell *self, + GcalWeatherInfo *info) +{ + g_return_if_fail (GCAL_IS_MONTH_CELL (self)); + g_return_if_fail (info == NULL || GCAL_IS_WEATHER_INFO (info)); + + if (info == NULL) + { + gtk_image_clear (self->weather_icon); + } + else + { + const gchar* icon_name; /* unowned */ + + icon_name = gcal_weather_info_get_icon_name (info); + gtk_image_set_from_icon_name (self->weather_icon, icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); + } + + gtk_widget_show (GTK_WIDGET (self->weather_icon)); +} + gboolean gcal_month_cell_get_different_month (GcalMonthCell *self) { diff --git a/src/views/gcal-month-cell.h b/src/views/gcal-month-cell.h index bdf32fec..89849b38 100644 --- a/src/views/gcal-month-cell.h +++ b/src/views/gcal-month-cell.h @@ -20,6 +20,7 @@ #define GCAL_MONTH_CELL_H #include "gcal-manager.h" +#include "gcal-weather-info.h" #include @@ -36,6 +37,9 @@ GDateTime* gcal_month_cell_get_date (GcalMonthCell void gcal_month_cell_set_date (GcalMonthCell *self, GDateTime *date); +void gcal_month_cell_set_weather (GcalMonthCell *self, + GcalWeatherInfo *info); + gboolean gcal_month_cell_get_different_month (GcalMonthCell *self); void gcal_month_cell_set_different_month (GcalMonthCell *self, diff --git a/src/views/gcal-month-view.c b/src/views/gcal-month-view.c index 31dac066..31ba06d5 100644 --- a/src/views/gcal-month-view.c +++ b/src/views/gcal-month-view.c @@ -70,7 +70,7 @@ struct _GcalMonthView /* Grid widgets */ GtkWidget *grid; - GtkWidget *month_cell[6][7]; + GtkWidget *month_cell[6][7]; /* unowned */ /* * Hash to keep children widgets (all of them, parent widgets and its parts if there's any), @@ -132,6 +132,8 @@ struct _GcalMonthView icaltimetype *date; GcalManager *manager; + GcalWeatherService *weather_service; + gboolean pending_event_allocation; }; @@ -141,6 +143,9 @@ static void gtk_buildable_interface_init (GtkBuildableIf static void e_data_model_subscriber_interface_init (ECalDataModelSubscriberInterface *iface); +static void update_weather (GcalMonthView *self, + gboolean clear_old); + G_DEFINE_TYPE_WITH_CODE (GcalMonthView, gcal_month_view, GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (GCAL_TYPE_VIEW, gcal_view_interface_init) G_IMPLEMENT_INTERFACE (E_TYPE_CAL_DATA_MODEL_SUBSCRIBER, @@ -153,6 +158,7 @@ enum PROP_0, PROP_DATE, PROP_MANAGER, + PROP_WEATHER_SERVICE, N_PROPS }; @@ -162,6 +168,7 @@ enum NUM_SIGNALS }; +static gpointer month_view_parent_class = NULL; static guint signals[NUM_SIGNALS] = { 0, }; @@ -194,6 +201,13 @@ event_widget_visibility_changed_cb (GcalMonthView *self) gtk_widget_queue_resize (GTK_WIDGET (self)); } +static void +weather_changed_cb (GcalWeatherService *weather_service, + GcalMonthView *self) +{ + update_weather (self, TRUE); +} + static void setup_child_widget (GcalMonthView *self, GtkWidget *widget) @@ -896,6 +910,76 @@ setup_month_grid (GcalMonthView *self, } } +static void +update_weather (GcalMonthView *self, + gboolean clear_old) +{ + g_return_if_fail (GCAL_IS_MONTH_VIEW (self)); + + /* Drop old weather information */ + if (clear_old) + { + guint row; + guint col; + + for (row = 0; row < 6; row++) + { + for (col = 0; col < 7; col++) + { + GcalMonthCell *cell; + + cell = GCAL_MONTH_CELL (self->month_cell[row][col]); + gcal_month_cell_set_weather (cell, NULL); + } + } + } + + + /* Set new one */ + if (self->weather_service != NULL) + { + GcalMonthCell *fstcell; + GDateTime *firstdt; + GSList *weather_infos; + GSList *witer; + GDate first; + + fstcell = GCAL_MONTH_CELL (self->month_cell[0][0]); + firstdt = gcal_month_cell_get_date (fstcell); + + g_date_set_dmy (&first, + g_date_time_get_day_of_month (firstdt), + g_date_time_get_month (firstdt), + g_date_time_get_year (firstdt)); + + weather_infos = gcal_weather_service_get_weather_infos (self->weather_service); + for (witer = weather_infos; witer; witer = witer->next) + { + GcalWeatherInfo *info; + GDate weather_date; + gint day_difference; + + info = GCAL_WEATHER_INFO (witer->data); + gcal_weather_info_get_date (info, &weather_date); + day_difference = g_date_days_between (&first, &weather_date); + + if (day_difference >= 0 && day_difference < 6 * 7) + { + GcalMonthCell *wcell; + guint row; + guint column; + + row = day_difference / 7; + column = day_difference % 7; + wcell = GCAL_MONTH_CELL (self->month_cell[row][column]); + + gcal_month_cell_set_weather (wcell, info); + } + } + } + +} + static gboolean update_month_cells (GcalMonthView *self) { @@ -928,6 +1012,7 @@ update_month_cells (GcalMonthView *self) cell_date = g_date_time_add_days (dt, row * 7 + col - self->days_delay); gcal_month_cell_set_date (cell, cell_date); + gcal_month_cell_set_weather (cell, NULL); /* Different month */ different_month = day < self->days_delay || @@ -976,6 +1061,8 @@ update_month_cells (GcalMonthView *self) } } + update_weather (self, FALSE); + self->update_grid_id = 0; return G_SOURCE_REMOVE; @@ -1685,6 +1772,9 @@ gcal_month_view_set_property (GObject *object, g_object_notify (object, "manager"); break; + case PROP_WEATHER_SERVICE: + gcal_month_view_set_weather_service (self, g_value_get_object (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -1709,17 +1799,48 @@ gcal_month_view_get_property (GObject *object, g_value_set_object (value, self->manager); break; + case PROP_WEATHER_SERVICE: + g_value_set_boxed (value, gcal_month_view_get_weather_service (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } +/* + * Called when self is destroyed. + * + * WARNING: This is a workaround. This is one of the views where + * reference counts are off. We overwrite destroy to avoid dangling + * pointers. This issue needs to be addressed and this function removed. + */ +static void +gcal_month_view_destroyed (GtkWidget *widget) +{ + GcalMonthView *self = GCAL_MONTH_VIEW (widget); + + if (self->weather_service) + { + g_signal_handlers_disconnect_by_func (self->weather_service, weather_changed_cb, self); + g_clear_object (&self->weather_service); + } + + GTK_WIDGET_CLASS (month_view_parent_class)->destroy ((GtkWidget*) G_TYPE_CHECK_INSTANCE_CAST (self, GTK_TYPE_GRID, GtkGrid)); +} + static void gcal_month_view_finalize (GObject *object) { GcalMonthView *self = GCAL_MONTH_VIEW (object); + if (self->weather_service) + { + g_signal_handlers_disconnect_by_func (self->weather_service, weather_changed_cb, self); + g_clear_object (&self->weather_service); + } + g_clear_pointer (&self->date, g_free); g_clear_pointer (&self->children, g_hash_table_destroy); g_clear_pointer (&self->single_cell_children, g_hash_table_destroy); @@ -2086,6 +2207,18 @@ gcal_month_view_class_init (GcalMonthViewClass *klass) g_object_class_override_property (object_class, PROP_DATE, "active-date"); g_object_class_override_property (object_class, PROP_MANAGER, "manager"); + /** + * GcalWeekView:weather-service: + * + * Sets the weather service to use. + */ + g_object_class_install_property + (object_class, + PROP_WEATHER_SERVICE, + g_param_spec_object ("weather-service", "weather-service", "weather-service", + GCAL_TYPE_WEATHER_SERVICE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE)); + signals[EVENT_ACTIVATED] = g_signal_new ("event-activated", GCAL_TYPE_MONTH_VIEW, G_SIGNAL_RUN_LAST, @@ -2111,6 +2244,10 @@ gcal_month_view_class_init (GcalMonthViewClass *klass) gtk_widget_class_set_css_name (widget_class, "calendar-view"); g_type_ensure (GCAL_TYPE_MONTH_POPOVER); + + /* FIXME: Hack to deal with broken reference counts */ + month_view_parent_class = g_type_class_peek_parent (klass); + ((GtkWidgetClass *) klass)->destroy = (void (*) (GtkWidget *)) gcal_month_view_destroyed; } static void @@ -2120,6 +2257,7 @@ gcal_month_view_init (GcalMonthView *self) gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); + self->weather_service = NULL; self->children = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_list_free); self->single_cell_children = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_list_free); self->overflow_cells = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_list_free); @@ -2189,3 +2327,52 @@ gcal_month_view_set_use_24h_format (GcalMonthView *self, { self->use_24h_format = use_24h; } + +/** + * gcal_month_view_set_weather_service: + * @self: The #GcalWeatherView instance. + * @service: (nullable): The weather service to query. + * + * Sets the service to query for weather reports. + */ +void +gcal_month_view_set_weather_service (GcalMonthView *self, + GcalWeatherService *service) +{ + g_return_if_fail (GCAL_IS_MONTH_VIEW (self)); + g_return_if_fail (!service || GCAL_IS_WEATHER_SERVICE (service)); + + if (self->weather_service == service) + return; + + if (self->weather_service) + g_signal_handlers_disconnect_by_func (self->weather_service, weather_changed_cb, self); + + g_set_object (&self->weather_service, service); + + if (service) + { + g_signal_connect (service, + "weather-changed", + G_CALLBACK (weather_changed_cb), + self); + } + + update_weather (self, TRUE); + + g_object_notify (G_OBJECT (self), "weather-service"); +} + +/** + * gcal_month_view_get_weather_service: + * @self: The #GcalWeatherView instance. + * + * Returns: (transfer none): The internal weather service object. + */ +GcalWeatherService* +gcal_month_view_get_weather_service (GcalMonthView *self) +{ + g_return_val_if_fail (GCAL_IS_MONTH_VIEW (self), NULL); + + return self->weather_service; +} diff --git a/src/views/gcal-month-view.h b/src/views/gcal-month-view.h index 43e9905c..1670aa70 100644 --- a/src/views/gcal-month-view.h +++ b/src/views/gcal-month-view.h @@ -21,6 +21,7 @@ #define __GCAL_MONTH_VIEW_H__ #include "gcal-manager.h" +#include "gcal-weather-service.h" G_BEGIN_DECLS @@ -34,6 +35,11 @@ void gcal_month_view_set_first_weekday (GcalMonthView void gcal_month_view_set_use_24h_format (GcalMonthView *self, gboolean use_24h); +void gcal_month_view_set_weather_service (GcalMonthView *self, + GcalWeatherService *service); + +GcalWeatherService* gcal_month_view_get_weather_service (GcalMonthView *self); + G_END_DECLS #endif /* __GCAL_MONTH_VIEW_H__ */