diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml index 40d081d1..5f8d36c7 100644 --- a/data/calendar.gresource.xml +++ b/data/calendar.gresource.xml @@ -16,6 +16,7 @@ ui/search-view.ui ui/source-dialog.ui ui/time-selector.ui + ui/weather-settings.ui ui/week-header.ui ui/week-view.ui ui/window.ui diff --git a/data/meson.build b/data/meson.build index 4c5e0a7c..3418fd11 100644 --- a/data/meson.build +++ b/data/meson.build @@ -63,6 +63,7 @@ resource_data = files( 'ui/search-view.ui', 'ui/source-dialog.ui', 'ui/time-selector.ui', + 'ui/weather-settings.ui', 'ui/week-header.ui', 'ui/week-view.ui', 'ui/window.ui', diff --git a/data/ui/weather-settings.ui b/data/ui/weather-settings.ui new file mode 100644 index 00000000..086a5f73 --- /dev/null +++ b/data/ui/weather-settings.ui @@ -0,0 +1,77 @@ + + + + + diff --git a/data/ui/window.ui b/data/ui/window.ui index d20cf845..3acfd176 100644 --- a/data/ui/window.ui +++ b/data/ui/window.ui @@ -417,78 +417,9 @@ - + True False - vertical - 12 - 6 - - - _Weather - main-menu - True - True - True - - - - - True - False - 18 - - - True - False - True - 0.0 - Show Weather - - - - - True - True - - - - - - - - True - False - 18 - - - True - False - True - 0.0 - Automatic Location - - - - - True - True - - - - - - - - True - True - edit-find-symbolic - False - False - - - - weather-menu diff --git a/src/gcal-application.c b/src/gcal-application.c index 30d6e2df..bb0c3ccd 100644 --- a/src/gcal-application.c +++ b/src/gcal-application.c @@ -50,11 +50,7 @@ struct _GcalApplication gchar *uuid; icaltimetype *initial_date; - /* Weather service exists as long as #GcalApplication. - * However, it only runs if #GcalApplicatoin->window - * is available. - */ - GcalWeatherService *weather_service; /* owned */ + GcalWeatherService *weather_service; GcalShellSearchProvider *search_provider; }; @@ -431,11 +427,9 @@ gcal_application_init (GcalApplication *self) self->manager = gcal_manager_new (); g_signal_connect_swapped (self->manager, "source-added", G_CALLBACK (process_sources), self); g_signal_connect_swapped (self->manager, "source-changed", G_CALLBACK (process_sources), self); - self->weather_service = gcal_weather_service_new (NULL, /* in prep. for configurable time zones */ - GCAL_WEATHER_FORECAST_MAX_DAYS_DFLT, - GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT, - GCAL_WEATHER_CHECK_INTERVAL_RENEW_DFLT, - GCAL_WEATHER_VALID_TIMESPAN_DFLT); + + self->weather_service = gcal_weather_service_new (); + self->search_provider = gcal_shell_search_provider_new (); gcal_shell_search_provider_connect (self->search_provider, self->manager); } diff --git a/src/gcal-window.c b/src/gcal-window.c index c01c2adc..f1efd5db 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -30,6 +30,7 @@ #include "gcal-search-view.h" #include "gcal-source-dialog.h" #include "gcal-view.h" +#include "gcal-weather-settings.h" #include "gcal-week-view.h" #include "gcal-window.h" #include "gcal-year-view.h" @@ -163,11 +164,8 @@ struct _GcalWindow gint open_edit_dialog_timeout_id; /* weather management */ - GcalWeatherService *weather_service; - GtkSwitch *show_weather_switch; - GtkSwitch *weather_auto_location_switch; - GtkBox *weather_auto_location_box; - GtkWidget *weather_location_entry; + GcalWeatherService *weather_service; + GcalWeatherSettings *weather_settings; /* temp to keep event_creation */ gboolean open_edit_dialog; @@ -216,21 +214,6 @@ static void on_toggle_search_bar_activated (GSimpleAction GVariant *param, gpointer user_data); -static void on_weather_location_searchbox_changed_cb (GWeatherLocationEntry *entry, - GcalWindow *self); - -static void on_show_weather_changed_cb (GtkSwitch *wswitch, - GParamSpec *pspec, - GcalWindow *self); - -static void on_weather_auto_location_changed_cb (GtkSwitch *lswitch, - GParamSpec *pspec, - GcalWindow *self); - -static void update_menu_weather_sensitivity (GcalWindow *self); - -static void load_weather_settings (GcalWindow *self); - G_DEFINE_TYPE (GcalWindow, gcal_window, GTK_TYPE_APPLICATION_WINDOW) static const GActionEntry actions[] = { @@ -265,139 +248,6 @@ hide_search_view_on_click_outside (GcalWindow *window, return GDK_EVENT_PROPAGATE; } -static GWeatherLocation* -get_checked_fixed_location (GcalWindow *self) -{ - g_autoptr (GWeatherLocation) location; - - location = gweather_location_entry_get_location (GWEATHER_LOCATION_ENTRY (self->weather_location_entry)); - - /* - * NOTE: This check feels shabby. However, I couldn't find a better - * one without iterating the model. has-custom-text does not work - * properly. Lets go with it for now. - */ - if (location && gweather_location_get_name (location)) - return g_steal_pointer (&location); - - return NULL; -} - -static void -load_weather_settings (GcalWindow *self) -{ - g_autoptr (GVariant) location = NULL; - g_autoptr (GVariant) value = NULL; - g_autofree gchar *location_name = NULL; - GSettings *settings; - gboolean show_weather; - gboolean auto_location; - - settings = gcal_manager_get_settings (self->manager); - value = g_settings_get_value (settings, "weather-settings"); - - g_variant_get (value, "(bbsmv)", - &show_weather, - &auto_location, - &location_name, - &location); - - g_signal_handlers_block_by_func (self->show_weather_switch, on_show_weather_changed_cb, self); - g_signal_handlers_block_by_func (self->weather_auto_location_switch, on_weather_auto_location_changed_cb, self); - g_signal_handlers_block_by_func (self->weather_location_entry, on_weather_location_searchbox_changed_cb, self); - - gtk_switch_set_active (self->show_weather_switch, show_weather); - gtk_switch_set_active (self->weather_auto_location_switch, auto_location); - - if (!location && !auto_location) - { - GtkStyleContext *context; - - context = gtk_widget_get_style_context (GTK_WIDGET (self->weather_location_entry)); - gtk_entry_set_text (GTK_ENTRY (self->weather_location_entry), location_name); - gtk_style_context_add_class (context, "error"); - } - else - { - g_autoptr (GWeatherLocation) weather_location; - GWeatherLocation *world; - - world = gweather_location_get_world (); - weather_location = gweather_location_deserialize (world, location); - - gweather_location_entry_set_location (GWEATHER_LOCATION_ENTRY (self->weather_location_entry), weather_location); - } - - g_signal_handlers_unblock_by_func (self->show_weather_switch, on_show_weather_changed_cb, self); - g_signal_handlers_unblock_by_func (self->weather_auto_location_switch, on_weather_auto_location_changed_cb, self); - g_signal_handlers_unblock_by_func (self->weather_location_entry, on_weather_location_searchbox_changed_cb, self); -} - -static void -manage_weather_service (GcalWindow *self) -{ - g_autoptr (GWeatherLocation) location = NULL; - - gcal_weather_service_stop (self->weather_service); - - if (!gtk_switch_get_active (self->show_weather_switch)) - return; - - if (gtk_switch_get_active (self->weather_auto_location_switch)) - { - gcal_weather_service_run (self->weather_service, NULL); - return; - } - - location = get_checked_fixed_location (self); - if (!location) - { - /* TODO: this one should get reported to users */ - g_warning ("Unknown location '%s' selected", gtk_entry_get_text (GTK_ENTRY (self->weather_location_entry))); - return; - } - - gcal_weather_service_run (self->weather_service, location); -} - -static void -save_weather_settings (GcalWindow *self) -{ - g_autoptr (GWeatherLocation) location; - GSettings *settings; - GVariant *value; - GVariant *vlocation; - gboolean res; - - location = gweather_location_entry_get_location (GWEATHER_LOCATION_ENTRY (self->weather_location_entry)); - vlocation = location ? gweather_location_serialize (location) : NULL; - - settings = gcal_manager_get_settings (self->manager); - value = g_variant_new ("(bbsmv)", - gtk_switch_get_active (self->show_weather_switch), - gtk_switch_get_active (self->weather_auto_location_switch), - gtk_entry_get_text (GTK_ENTRY (self->weather_location_entry)), - vlocation); - - res = g_settings_set_value (settings, "weather-settings", value); - - if (!res) - g_warning ("Could not persist weather settings"); -} - -static void -update_menu_weather_sensitivity (GcalWindow *self) -{ - gboolean weather_enabled; - gboolean autoloc_enabled; - - weather_enabled = gtk_switch_get_active (self->show_weather_switch); - autoloc_enabled = gtk_switch_get_active (self->weather_auto_location_switch); - - gtk_widget_set_sensitive (GTK_WIDGET (self->weather_auto_location_switch), weather_enabled); - gtk_widget_set_sensitive (GTK_WIDGET (self->weather_location_entry), weather_enabled && !autoloc_enabled); -} - static void update_today_button_sensitive (GcalWindow *window) { @@ -1112,20 +962,6 @@ window_state_changed (GtkWidget *widget, return FALSE; } -static void -on_show_weather_changed_cb (GtkSwitch *wswitch, - GParamSpec *pspec, - GcalWindow *self) -{ - save_weather_settings (self); - - if (!gtk_switch_get_active (wswitch)) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->menu_button), FALSE); - - update_menu_weather_sensitivity (self); - manage_weather_service (self); -} - static void on_popover_menu_visible_cb (GtkWidget *widget, GParamSpec *pspec, @@ -1134,48 +970,6 @@ on_popover_menu_visible_cb (GtkWidget *widget, gtk_stack_set_visible_child_name (GTK_STACK (self->win_menu_stack), "main-menu"); } -static void -on_weather_auto_location_changed_cb (GtkSwitch *lswitch, - GParamSpec *pspec, - GcalWindow *self) -{ - save_weather_settings (self); - - if (gtk_switch_get_active (lswitch)) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->menu_button), FALSE); - - update_menu_weather_sensitivity (self); - manage_weather_service (self); -} - -static void -on_weather_location_searchbox_changed_cb (GWeatherLocationEntry *entry, - GcalWindow *self) -{ - GtkStyleContext *context; - GWeatherLocation *location; - gboolean auto_location; - - save_weather_settings (self); - - context = gtk_widget_get_style_context (self->weather_location_entry); - auto_location = gtk_switch_get_active (self->weather_auto_location_switch); - location = get_checked_fixed_location (self); - - if (!location && !auto_location) - { - gtk_style_context_add_class (context, "error"); - } - else - { - gtk_style_context_remove_class (context, "error"); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->menu_button), FALSE); - manage_weather_service (self); - gweather_location_unref (location); - } -} - static void edit_event (GcalQuickAddPopover *popover, GcalEvent *event, @@ -1482,17 +1276,16 @@ gcal_window_set_property (GObject *object, case PROP_ACTIVE_VIEW: self->active_view = g_value_get_enum (value); gtk_widget_show (self->views[self->active_view]); - gtk_stack_set_visible_child (GTK_STACK (self->views_stack), - self->views[self->active_view]); - return; + gtk_stack_set_visible_child (GTK_STACK (self->views_stack), self->views[self->active_view]); + break; case PROP_ACTIVE_DATE: update_active_date (GCAL_WINDOW (object), g_value_dup_boxed (value)); - return; + break; case PROP_NEW_EVENT_MODE: set_new_event_mode (GCAL_WINDOW (object), g_value_get_boolean (value)); - return; + break; case PROP_MANAGER: if (g_set_object (&self->manager, g_value_get_object (value))) @@ -1524,20 +1317,7 @@ gcal_window_set_property (GObject *object, g_object_notify (object, "manager"); } - return; - - case PROP_WEATHER_SERVICE: - { - GcalWeatherService *service; /* unowned */ - - service = GCAL_WEATHER_SERVICE (g_value_get_object (value)); - g_return_if_fail (service != NULL); - - g_return_if_fail (self->weather_service == NULL); - self->weather_service = (GcalWeatherService*) g_object_ref (service); - g_object_notify (object, "weather-service"); - return ; - } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -1558,23 +1338,23 @@ gcal_window_get_property (GObject *object, { case PROP_ACTIVE_VIEW: g_value_set_enum (value, self->active_view); - return; + break; case PROP_MANAGER: g_value_set_object (value, self->manager); - return; + break; case PROP_WEATHER_SERVICE: g_value_set_object (value, self->weather_service); - return; + break; case PROP_ACTIVE_DATE: g_value_set_boxed (value, self->active_date); - return; + break; case PROP_NEW_EVENT_MODE: g_value_set_boolean (value, self->new_event_mode); - return; + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -1610,8 +1390,9 @@ gcal_window_class_init (GcalWindowClass *klass) g_type_ensure (GCAL_TYPE_QUICK_ADD_POPOVER); g_type_ensure (GCAL_TYPE_SEARCH_VIEW); g_type_ensure (GCAL_TYPE_SOURCE_DIALOG); - g_type_ensure (GCAL_TYPE_WEEK_VIEW); g_type_ensure (GCAL_TYPE_WEATHER_SERVICE); + g_type_ensure (GCAL_TYPE_WEATHER_SETTINGS); + g_type_ensure (GCAL_TYPE_WEEK_VIEW); g_type_ensure (GCAL_TYPE_YEAR_VIEW); object_class = G_OBJECT_CLASS (klass); @@ -1652,7 +1433,7 @@ gcal_window_class_init (GcalWindowClass *klass) "The weather service object", "The weather service object", GCAL_TYPE_WEATHER_SERVICE, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, properties); @@ -1675,15 +1456,12 @@ gcal_window_class_init (GcalWindowClass *klass) gtk_widget_class_bind_template_child (widget_class, GcalWindow, source_dialog); gtk_widget_class_bind_template_child (widget_class, GcalWindow, search_entry); gtk_widget_class_bind_template_child (widget_class, GcalWindow, search_view); - gtk_widget_class_bind_template_child (widget_class, GcalWindow, show_weather_switch); gtk_widget_class_bind_template_child (widget_class, GcalWindow, today_button); gtk_widget_class_bind_template_child (widget_class, GcalWindow, views_overlay); gtk_widget_class_bind_template_child (widget_class, GcalWindow, views_stack); gtk_widget_class_bind_template_child (widget_class, GcalWindow, views_switcher); - gtk_widget_class_bind_template_child (widget_class, GcalWindow, weather_auto_location_box); - gtk_widget_class_bind_template_child (widget_class, GcalWindow, weather_auto_location_switch); - gtk_widget_class_bind_template_child (widget_class, GcalWindow, weather_location_entry); gtk_widget_class_bind_template_child (widget_class, GcalWindow, win_menu_stack); + gtk_widget_class_bind_template_child (widget_class, GcalWindow, weather_settings); gtk_widget_class_bind_template_child (widget_class, GcalWindow, week_view); gtk_widget_class_bind_template_child (widget_class, GcalWindow, year_view); @@ -1701,9 +1479,6 @@ gcal_window_class_init (GcalWindowClass *klass) gtk_widget_class_bind_template_callback (widget_class, date_updated); gtk_widget_class_bind_template_callback (widget_class, on_popover_menu_visible_cb); - gtk_widget_class_bind_template_callback (widget_class, on_show_weather_changed_cb); - gtk_widget_class_bind_template_callback (widget_class, on_weather_auto_location_changed_cb); - gtk_widget_class_bind_template_callback (widget_class, on_weather_location_searchbox_changed_cb); /* Event removal related */ gtk_widget_class_bind_template_callback (widget_class, hide_notification); @@ -1741,6 +1516,9 @@ gcal_window_init (GcalWindow *self) G_N_ELEMENTS (actions), self); + app = g_application_get_default (); + self->weather_service = gcal_application_get_weather_service (GCAL_APPLICATION (app)); + gtk_widget_init_template (GTK_WIDGET (self)); /* source dialog */ @@ -1789,16 +1567,17 @@ gcal_window_init (GcalWindow *self) */ 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->weather_settings, "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->month_view, "weather-service", G_BINDING_DEFAULT); - g_object_bind_property (self, "weather-service", self->year_view, "weather-service", G_BINDING_DEFAULT); + g_object_bind_property (self, "weather-service", self->weather_settings, "weather-service", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + g_object_bind_property (self, "weather-service", self->month_view, "weather-service", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + g_object_bind_property (self, "weather-service", self->week_view, "weather-service", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + g_object_bind_property (self, "weather-service", self->year_view, "weather-service", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); /* setup accels */ - app = g_application_get_default (); - gcal_window_add_accelerator (app, "win.change-view(-1)", "Page_Down"); gcal_window_add_accelerator (app, "win.change-view(-2)", "Page_Up"); gcal_window_add_accelerator (app, "win.change-view(1)", "1") @@ -1820,15 +1599,12 @@ GtkWidget* gcal_window_new_with_date (GcalApplication *app, icaltimetype *date) { - GcalWeatherService *weather_service; GcalManager *manager; GcalWindow *win; - weather_service = gcal_application_get_weather_service (GCAL_APPLICATION (app)); manager = gcal_application_get_manager (GCAL_APPLICATION (app)); win = g_object_new (GCAL_TYPE_WINDOW, "application", GTK_APPLICATION (app), - "weather-service", weather_service, "manager", manager, "active-date", date, NULL); @@ -1836,11 +1612,6 @@ gcal_window_new_with_date (GcalApplication *app, /* loading size */ load_geometry (win); - /* Recover weather settings */ - load_weather_settings (win); - update_menu_weather_sensitivity (win); - manage_weather_service (win); - return GTK_WIDGET (win); } diff --git a/src/meson.build b/src/meson.build index 869e43c8..b3704065 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,6 +33,7 @@ sources = files( 'views/gcal-year-view.c', 'weather/gcal-weather-info.c', 'weather/gcal-weather-service.c', + 'weather/gcal-weather-settings.c', 'gcal-application.c', 'gcal-clock.c', 'gcal-date-chooser-day.c', diff --git a/src/views/gcal-week-header.c b/src/views/gcal-week-header.c index a52ac51d..2e8e922d 100644 --- a/src/views/gcal-week-header.c +++ b/src/views/gcal-week-header.c @@ -1674,7 +1674,7 @@ gcal_week_header_init (GcalWeekHeader *self) self->selection_end = -1; self->dnd_cell = -1; self->weather_service = NULL; - memset (self->weather_infos, 0, sizeof(self->weather_infos)); + gtk_widget_init_template (GTK_WIDGET (self)); /* This is to avoid stray lines when adding and removing events */ @@ -1712,7 +1712,8 @@ gcal_week_header_add_weather_infos (GcalWeekHeader *self, gint consumed = 0; guint i; - g_return_val_if_fail (self != NULL, 0); + if (!self->active_date) + return 0; week_start_dt = get_start_of_week (self->active_date); g_date_set_dmy (&week_start, @@ -2066,7 +2067,7 @@ gcal_week_header_set_date (GcalWeekHeader *self, * Elements are owned by the widget. Do not modify. */ GSList* -gcal_week_header_get_shown_weather_infos (GcalWeekHeader *self) +gcal_week_header_get_shown_weather_infos (GcalWeekHeader *self) { g_return_val_if_fail (GCAL_IS_WEEK_HEADER (self), NULL); GSList* lst = NULL; /* owned[unowned] */ diff --git a/src/views/gcal-week-view.c b/src/views/gcal-week-view.c index 77ae920c..a2bcd152 100644 --- a/src/views/gcal-week-view.c +++ b/src/views/gcal-week-view.c @@ -546,23 +546,9 @@ gcal_week_view_set_property (GObject *object, break; case PROP_WEATHER_SERVICE: - { - GcalWeatherService* weather_service; /* unowned */ - weather_service = g_value_get_object (value); - - g_return_if_fail (weather_service == NULL || GCAL_IS_WEATHER_SERVICE (weather_service)); - - if (self->weather_service != weather_service) - { - g_set_object (&self->weather_service, weather_service); - - gcal_week_header_set_weather_service (GCAL_WEEK_HEADER (self->header), - self->weather_service); - - g_object_notify (object, "weather-service"); - } - break; - } + if (g_set_object (&self->weather_service, g_value_get_object (value))) + g_object_notify (object, "weather-service"); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -639,6 +625,8 @@ gcal_week_view_init (GcalWeekView *self) update_hours_sidebar_size (self); self->weather_service = NULL; + + g_object_bind_property (self, "weather-service", self->header, "weather-service", G_BINDING_DEFAULT); } /* Public API */ diff --git a/src/weather/gcal-weather-service.c b/src/weather/gcal-weather-service.c index 3639ffca..99d63d4a 100644 --- a/src/weather/gcal-weather-service.c +++ b/src/weather/gcal-weather-service.c @@ -30,6 +30,11 @@ #include "gcal-weather-service.h" +#define GCAL_WEATHER_CHECK_INTERVAL_RENEW_DEFAULT (3 * 60 * 60) /* seconds */ +#define GCAL_WEATHER_CHECK_INTERVAL_NEW_DEFAULT (5 * 60) /* seconds */ +#define GCAL_WEATHER_VALID_TIMESPAN_DEFAULT (24 * 60 * 60) /* seconds */ +#define GCAL_WEATHER_FORECAST_MAX_DAYS_DEFAULT 3 + #define DAY_SECONDS (24 * 60 * 60) /** @@ -263,7 +268,7 @@ set_check_interval_new (GcalWeatherService *self, self->check_interval_new = interval; update_timeout_interval (self); - g_object_notify ((GObject*) self, "check-interval-new"); + g_object_notify (G_OBJECT (self), "check-interval-new"); } static void @@ -273,7 +278,7 @@ set_check_interval_renew (GcalWeatherService *self, self->check_interval_renew = interval; update_timeout_interval (self); - g_object_notify ((GObject*) self, "check-interval-renew"); + g_object_notify (G_OBJECT (self), "check-interval-renew"); } static gssize @@ -830,7 +835,7 @@ set_max_days (GcalWeatherService *self, self->max_days = days; - g_object_notify ((GObject*) self, "max-days"); + g_object_notify (G_OBJECT (self), "max-days"); } static void @@ -841,7 +846,7 @@ set_valid_timespan (GcalWeatherService *self, self->valid_timespan = timespan; - g_object_notify ((GObject*) self, "valid-timespan"); + g_object_notify (G_OBJECT (self), "valid-timespan"); } static gboolean @@ -1057,7 +1062,7 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass) "check-interval-new", 0, G_MAXUINT, - GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT, + GCAL_WEATHER_CHECK_INTERVAL_NEW_DEFAULT, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** @@ -1073,7 +1078,7 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass) "check-interval-renew", 0, G_MAXUINT, - GCAL_WEATHER_CHECK_INTERVAL_RENEW_DFLT, + GCAL_WEATHER_CHECK_INTERVAL_RENEW_DEFAULT, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** @@ -1088,7 +1093,7 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass) "valid-timespan", 0, G_MAXINT64, - GCAL_WEATHER_VALID_TIMESPAN_DFLT, + GCAL_WEATHER_VALID_TIMESPAN_DEFAULT, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); @@ -1115,13 +1120,14 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass) static void gcal_weather_service_init (GcalWeatherService *self) { - self->check_interval_new = GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT; - self->check_interval_renew = GCAL_WEATHER_CHECK_INTERVAL_RENEW_DFLT; + self->max_days = GCAL_WEATHER_FORECAST_MAX_DAYS_DEFAULT; + self->check_interval_new = GCAL_WEATHER_CHECK_INTERVAL_NEW_DEFAULT; + self->check_interval_renew = GCAL_WEATHER_CHECK_INTERVAL_RENEW_DEFAULT; self->location_cancellable = g_cancellable_new (); self->weather_infos_upated = -1; - self->valid_timespan = -1; + self->valid_timespan = GCAL_WEATHER_VALID_TIMESPAN_DEFAULT; - self->duration_timer = gcal_timer_new (GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT); + self->duration_timer = gcal_timer_new (GCAL_WEATHER_CHECK_INTERVAL_NEW_DEFAULT); gcal_timer_set_callback (self->duration_timer, (GCalTimerFunc) on_duration_timer_timeout, self, NULL); self->midnight_timer = gcal_timer_new (24 * 60 * 60); @@ -1135,29 +1141,16 @@ gcal_weather_service_init (GcalWeatherService *self) /** * gcal_weather_service_new: - * @max_days: mumber of days to fetch forecasts for. - * @check_interval_new: timeout used when fetching new weather reports. - * @check_interval_renew: timeout used to update valid weather reports. * * Creates a new #GcalWeatherService. This service listens * to location and weather changes and reports them. * * Returns: (transfer full): A newly created #GcalWeatherService. */ -GcalWeatherService * -gcal_weather_service_new (GTimeZone *time_zone, - guint max_days, - guint check_interval_new, - guint check_interval_renew, - gint64 valid_timespan) +GcalWeatherService* +gcal_weather_service_new (void) { - return g_object_new (GCAL_TYPE_WEATHER_SERVICE, - "time-zone", time_zone, - "max-days", max_days, - "check-interval-new", check_interval_new, - "check-interval-renew", check_interval_renew, - "valid-timespan", valid_timespan, - NULL); + return g_object_new (GCAL_TYPE_WEATHER_SERVICE, NULL); } /** @@ -1199,7 +1192,7 @@ gcal_weather_service_set_time_zone (GcalWeatherService *self, /* make sure midnight is timed correctly: */ schedule_midnight (self); - g_object_notify ((GObject*) self, "time-zone"); + g_object_notify (G_OBJECT (self), "time-zone"); } /** diff --git a/src/weather/gcal-weather-service.h b/src/weather/gcal-weather-service.h index cdcd4258..fb3833a6 100644 --- a/src/weather/gcal-weather-service.h +++ b/src/weather/gcal-weather-service.h @@ -27,21 +27,12 @@ G_BEGIN_DECLS -#define GCAL_WEATHER_CHECK_INTERVAL_RENEW_DFLT (3 * 60 * 60) /* seconds */ -#define GCAL_WEATHER_CHECK_INTERVAL_NEW_DFLT (5 * 60) /* seconds */ -#define GCAL_WEATHER_VALID_TIMESPAN_DFLT (24 * 60 * 60) /* seconds */ -#define GCAL_WEATHER_FORECAST_MAX_DAYS_DFLT 3 - #define GCAL_TYPE_WEATHER_SERVICE (gcal_weather_service_get_type()) G_DECLARE_FINAL_TYPE (GcalWeatherService, gcal_weather_service, GCAL, WEATHER_SERVICE, GObject) -GcalWeatherService* gcal_weather_service_new (GTimeZone *time_zone, - guint max_days, - guint check_interval_new, - guint check_interval_renew, - gint64 valid_timespan); +GcalWeatherService* gcal_weather_service_new (void); GTimeZone* gcal_weather_service_get_time_zone (GcalWeatherService *self); diff --git a/src/weather/gcal-weather-settings.c b/src/weather/gcal-weather-settings.c new file mode 100644 index 00000000..0fb21aab --- /dev/null +++ b/src/weather/gcal-weather-settings.c @@ -0,0 +1,391 @@ +/* gcal-weather-settings.c + * + * Copyright © 2018 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gcal-manager.h" +#include "gcal-utils.h" +#include "gcal-weather-service.h" +#include "gcal-weather-settings.h" + +struct _GcalWeatherSettings +{ + GtkBox parent; + + GtkSwitch *show_weather_switch; + GtkSwitch *weather_auto_location_switch; + GtkWidget *weather_location_entry; + + GcalManager *manager; + + GcalWeatherService *weather_service; +}; + + +static void on_weather_location_searchbox_changed_cb (GWeatherLocationEntry *entry, + GcalWeatherSettings *self); + +static void on_show_weather_changed_cb (GtkSwitch *wswitch, + GParamSpec *pspec, + GcalWeatherSettings *self); + +static void on_weather_auto_location_changed_cb (GtkSwitch *lswitch, + GParamSpec *pspec, + GcalWeatherSettings *self); + +G_DEFINE_TYPE (GcalWeatherSettings, gcal_weather_settings, GTK_TYPE_BOX) + +enum +{ + PROP_0, + PROP_MANAGER, + PROP_WEATHER_SERVICE, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS] = { NULL, }; + + +/* + * Auxiliary methods + */ + +static void +load_weather_settings (GcalWeatherSettings *self) +{ + g_autoptr (GVariant) location = NULL; + g_autoptr (GVariant) value = NULL; + g_autofree gchar *location_name = NULL; + GSettings *settings; + gboolean show_weather; + gboolean auto_location; + + if (!self->manager) + return; + + settings = gcal_manager_get_settings (self->manager); + value = g_settings_get_value (settings, "weather-settings"); + + g_variant_get (value, "(bbsmv)", + &show_weather, + &auto_location, + &location_name, + &location); + + g_signal_handlers_block_by_func (self->show_weather_switch, on_show_weather_changed_cb, self); + g_signal_handlers_block_by_func (self->weather_auto_location_switch, on_weather_auto_location_changed_cb, self); + g_signal_handlers_block_by_func (self->weather_location_entry, on_weather_location_searchbox_changed_cb, self); + + gtk_switch_set_active (self->show_weather_switch, show_weather); + gtk_switch_set_active (self->weather_auto_location_switch, auto_location); + + if (!location && !auto_location) + { + GtkStyleContext *context; + + context = gtk_widget_get_style_context (GTK_WIDGET (self->weather_location_entry)); + gtk_entry_set_text (GTK_ENTRY (self->weather_location_entry), location_name); + gtk_style_context_add_class (context, "error"); + } + else + { + g_autoptr (GWeatherLocation) weather_location; + GWeatherLocation *world; + + world = gweather_location_get_world (); + weather_location = gweather_location_deserialize (world, location); + + gweather_location_entry_set_location (GWEATHER_LOCATION_ENTRY (self->weather_location_entry), weather_location); + } + + g_signal_handlers_unblock_by_func (self->show_weather_switch, on_show_weather_changed_cb, self); + g_signal_handlers_unblock_by_func (self->weather_auto_location_switch, on_weather_auto_location_changed_cb, self); + g_signal_handlers_unblock_by_func (self->weather_location_entry, on_weather_location_searchbox_changed_cb, self); +} + +static void +save_weather_settings (GcalWeatherSettings *self) +{ + g_autoptr (GWeatherLocation) location = NULL; + GSettings *settings; + GVariant *value; + GVariant *vlocation; + gboolean res; + + if (!self->manager) + return; + + location = gweather_location_entry_get_location (GWEATHER_LOCATION_ENTRY (self->weather_location_entry)); + vlocation = location ? gweather_location_serialize (location) : NULL; + + settings = gcal_manager_get_settings (self->manager); + value = g_variant_new ("(bbsmv)", + gtk_switch_get_active (self->show_weather_switch), + gtk_switch_get_active (self->weather_auto_location_switch), + gtk_entry_get_text (GTK_ENTRY (self->weather_location_entry)), + vlocation); + + res = g_settings_set_value (settings, "weather-settings", value); + + if (!res) + g_warning ("Could not persist weather settings"); +} + +static void +update_menu_weather_sensitivity (GcalWeatherSettings *self) +{ + gboolean weather_enabled; + gboolean autoloc_enabled; + + weather_enabled = gtk_switch_get_active (self->show_weather_switch); + autoloc_enabled = gtk_switch_get_active (self->weather_auto_location_switch); + + gtk_widget_set_sensitive (GTK_WIDGET (self->weather_auto_location_switch), weather_enabled); + gtk_widget_set_sensitive (GTK_WIDGET (self->weather_location_entry), weather_enabled && !autoloc_enabled); +} + + +static GWeatherLocation* +get_checked_fixed_location (GcalWeatherSettings *self) +{ + g_autoptr (GWeatherLocation) location; + + location = gweather_location_entry_get_location (GWEATHER_LOCATION_ENTRY (self->weather_location_entry)); + + /* + * NOTE: This check feels shabby. However, I couldn't find a better + * one without iterating the model. has-custom-text does not work + * properly. Lets go with it for now. + */ + if (location && gweather_location_get_name (location)) + return g_steal_pointer (&location); + + return NULL; +} + +static void +manage_weather_service (GcalWeatherSettings *self) +{ + g_autoptr (GWeatherLocation) location = NULL; + + gcal_weather_service_stop (self->weather_service); + + if (!gtk_switch_get_active (self->show_weather_switch)) + return; + + if (gtk_switch_get_active (self->weather_auto_location_switch)) + { + gcal_weather_service_run (self->weather_service, NULL); + return; + } + + location = get_checked_fixed_location (self); + if (!location) + { + /* TODO: this one should get reported to users */ + g_warning ("Unknown location '%s' selected", gtk_entry_get_text (GTK_ENTRY (self->weather_location_entry))); + return; + } + + gcal_weather_service_run (self->weather_service, location); +} + + +/* + * Callbacks + */ + +static void +on_show_weather_changed_cb (GtkSwitch *wswitch, + GParamSpec *pspec, + GcalWeatherSettings *self) +{ + save_weather_settings (self); + update_menu_weather_sensitivity (self); + manage_weather_service (self); +} + + +static void +on_weather_auto_location_changed_cb (GtkSwitch *lswitch, + GParamSpec *pspec, + GcalWeatherSettings *self) +{ + save_weather_settings (self); + update_menu_weather_sensitivity (self); + manage_weather_service (self); +} + +static void +on_weather_location_searchbox_changed_cb (GWeatherLocationEntry *entry, + GcalWeatherSettings *self) +{ + GtkStyleContext *context; + GWeatherLocation *location; + gboolean auto_location; + + save_weather_settings (self); + + context = gtk_widget_get_style_context (self->weather_location_entry); + auto_location = gtk_switch_get_active (self->weather_auto_location_switch); + location = get_checked_fixed_location (self); + + if (!location && !auto_location) + { + gtk_style_context_add_class (context, "error"); + } + else + { + gtk_style_context_remove_class (context, "error"); + manage_weather_service (self); + gweather_location_unref (location); + } +} + + + +/* + * GObject overrides + */ + +static void +gcal_weather_settings_finalize (GObject *object) +{ + GcalWeatherSettings *self = (GcalWeatherSettings *)object; + + g_clear_object (&self->weather_service); + + G_OBJECT_CLASS (gcal_weather_settings_parent_class)->finalize (object); +} + +static void +gcal_weather_settings_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcalWeatherSettings *self = GCAL_WEATHER_SETTINGS (object); + + switch (prop_id) + { + case PROP_MANAGER: + g_value_set_object (value, self->manager); + break; + + case PROP_WEATHER_SERVICE: + g_value_set_object (value, self->weather_service); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_weather_settings_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalWeatherSettings *self = GCAL_WEATHER_SETTINGS (object); + + switch (prop_id) + { + case PROP_MANAGER: + if (g_set_object (&self->manager, g_value_get_object (value))) + { + load_weather_settings (self); + update_menu_weather_sensitivity (self); + manage_weather_service (self); + + g_object_notify_by_pspec (object, properties[PROP_MANAGER]); + } + break; + + case PROP_WEATHER_SERVICE: + gcal_weather_settings_set_weather_service (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_weather_settings_class_init (GcalWeatherSettingsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = gcal_weather_settings_finalize; + object_class->get_property = gcal_weather_settings_get_property; + object_class->set_property = gcal_weather_settings_set_property; + + properties[PROP_MANAGER] = g_param_spec_object ("manager", + "Manager", + "Manager", + GCAL_TYPE_MANAGER, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + properties[PROP_WEATHER_SERVICE] = g_param_spec_object ("weather-service", + "The weather service object", + "The weather service object", + GCAL_TYPE_WEATHER_SERVICE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/weather-settings.ui"); + + gtk_widget_class_bind_template_child (widget_class, GcalWeatherSettings, show_weather_switch); + gtk_widget_class_bind_template_child (widget_class, GcalWeatherSettings, weather_auto_location_switch); + gtk_widget_class_bind_template_child (widget_class, GcalWeatherSettings, weather_location_entry); + + gtk_widget_class_bind_template_callback (widget_class, on_show_weather_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_weather_auto_location_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_weather_location_searchbox_changed_cb); +} + +static void +gcal_weather_settings_init (GcalWeatherSettings *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +GcalWeatherService* +gcal_weather_settings_get_weather_service (GcalWeatherSettings *self) +{ + g_return_val_if_fail (GCAL_IS_WEATHER_SETTINGS (self), NULL); + + return self->weather_service; +} + +void +gcal_weather_settings_set_weather_service (GcalWeatherSettings *self, + GcalWeatherService *service) +{ + g_return_if_fail (GCAL_IS_WEATHER_SETTINGS (self)); + + if (!g_set_object (&self->weather_service, service)) + return; + + /* Recover weather settings */ + load_weather_settings (self); + update_menu_weather_sensitivity (self); + manage_weather_service (self); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WEATHER_SERVICE]); +} diff --git a/src/weather/gcal-weather-settings.h b/src/weather/gcal-weather-settings.h new file mode 100644 index 00000000..7c4138d9 --- /dev/null +++ b/src/weather/gcal-weather-settings.h @@ -0,0 +1,36 @@ +/* gcal-weather-settings.h + * + * Copyright © 2018 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "gcal-weather-service.h" + +#include + +G_BEGIN_DECLS + +#define GCAL_TYPE_WEATHER_SETTINGS (gcal_weather_settings_get_type()) + +G_DECLARE_FINAL_TYPE (GcalWeatherSettings, gcal_weather_settings, GCAL, WEATHER_SETTINGS, GtkBox) + +GcalWeatherService* gcal_weather_settings_get_weather_service (GcalWeatherSettings *self); + +void gcal_weather_settings_set_weather_service (GcalWeatherSettings *self, + GcalWeatherService *service); + +G_END_DECLS