/* gcal-month-cell.c * * Copyright (C) 2017 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 . */ #define G_LOG_DOMAIN "GcalMonthCell" #include "config.h" #include "gcal-clock.h" #include "gcal-debug.h" #include "gcal-event-widget.h" #include "gcal-month-cell.h" #include "gcal-utils.h" #include "gcal-weather-info.h" struct _GcalMonthCell { GtkWidget parent; GDateTime *date; guint n_overflow; AdwBreakpoint *breakpoint_narrow; GtkLabel *day_label; GtkWidget *header_box; GtkImage *weather_icon; GtkLabel *temp_label; GtkWidget *breakpoint_bin; GtkWidget *overflow_button; GtkInscription *overflow_inscription; GtkWidget *content_space; gboolean different_month; gboolean selected; GcalWeatherInfo *weather_info; GBinding *icon_tooltip_binding; }; G_DEFINE_TYPE (GcalMonthCell, gcal_month_cell, GTK_TYPE_WIDGET) enum { SHOW_OVERFLOW, ACTIVATE, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0, }; /* * Auxiliary methods */ static inline gint get_parent_bin_height (GtkWidget *widget) { GtkWidget *parent; parent = gtk_widget_get_parent (widget); g_assert (ADW_IS_BIN (parent)); return gtk_widget_get_height (parent); } static void update_style_flags (GcalMonthCell *self) { g_autoptr (GDateTime) today = NULL; gint weekday; /* Today */ today = g_date_time_new_now_local (); if (gcal_date_time_compare_date (self->date, today) == 0) gtk_widget_add_css_class (GTK_WIDGET (self), "today"); else gtk_widget_remove_css_class (GTK_WIDGET (self), "today"); weekday = g_date_time_get_day_of_week (self->date); if (is_workday (weekday)) gtk_widget_add_css_class (GTK_WIDGET (self), "workday"); else gtk_widget_remove_css_class (GTK_WIDGET (self), "workday"); } static void add_month_separators (GcalMonthCell *self) { gint day_of_month; gtk_widget_remove_css_class (GTK_WIDGET (self), "separator-top"); gtk_widget_remove_css_class (GTK_WIDGET (self), "separator-side"); day_of_month = g_date_time_get_day_of_month (self->date); if (day_of_month > 1 && day_of_month <= N_WEEKDAYS) { gtk_widget_add_css_class (GTK_WIDGET (self), "separator-top"); } else if (day_of_month == 1) { gtk_widget_add_css_class (GTK_WIDGET (self), "separator-side"); gtk_widget_add_css_class (GTK_WIDGET (self), "separator-top"); } } static void update_weather (GcalMonthCell *self) { GcalContext *context = gcal_application_get_context (GCAL_DEFAULT_APPLICATION); GcalWeatherService *weather_service; GcalWeatherInfo *weather_info; GDate date; gint day_of_month; day_of_month = g_date_time_get_day_of_month (self->date); g_date_set_dmy (&date, day_of_month, g_date_time_get_month (self->date), g_date_time_get_year (self->date)); weather_service = gcal_context_get_weather_service (context); weather_info = gcal_weather_service_get_weather_info_for_date (weather_service, &date); g_assert (!weather_info || GCAL_IS_WEATHER_INFO (weather_info)); if (self->weather_info == weather_info) return; self->weather_info = weather_info; if (weather_info) { const gchar *icon_name; /* unowned */ const gchar *temp_str; /* unowned */ icon_name = gcal_weather_info_get_icon_name (weather_info); temp_str = gcal_weather_info_get_temperature (weather_info); gtk_image_set_from_icon_name (self->weather_icon, icon_name); gtk_label_set_text (self->temp_label, temp_str); /* Use a short month name label to avoid conflicting with the weather forecast's labels */ if (day_of_month == 1) { g_autofree gchar *month_name = g_date_time_format (self->date, "%b"); gtk_label_set_text (self->day_label, month_name); } } else { gtk_image_clear (self->weather_icon); gtk_label_set_text (self->temp_label, ""); /* No risk of conflicting with the weather forecast labels in their absence, use the full month name label */ if (day_of_month == 1) { g_autofree gchar *month_name = g_date_time_format (self->date, "%OB"); gtk_label_set_text (self->day_label, month_name); } } } /* * Callbacks */ static void day_changed_cb (GcalClock *clock, GcalMonthCell *self) { update_style_flags (self); } static void overflow_button_clicked_cb (GtkWidget *button, GcalMonthCell *self) { g_signal_emit (self, signals[SHOW_OVERFLOW], 0, button); } static void on_weather_service_weather_changed_cb (GcalWeatherService *weather_service, GcalMonthCell *self) { update_weather (self); } static void on_breakpoint_changed_cb (GcalMonthCell *self) { AdwBreakpoint *current_breakpoint = adw_breakpoint_bin_get_current_breakpoint (ADW_BREAKPOINT_BIN (self->breakpoint_bin)); if (current_breakpoint == self->breakpoint_narrow) { self->icon_tooltip_binding = g_object_bind_property (self->temp_label, "label", self->weather_icon, "tooltip-text", G_BINDING_SYNC_CREATE); } else { g_clear_pointer (&self->icon_tooltip_binding, g_binding_unbind); gtk_widget_set_tooltip_text (GTK_WIDGET (self->weather_icon), NULL); } } /* * GtkWidget overrides */ static gboolean gcal_month_cell_focus (GtkWidget *widget, GtkDirectionType direction) { GtkRoot *root = gtk_widget_get_root (widget); if (gtk_root_get_focus (root) == NULL) return FALSE; return GTK_WIDGET_CLASS (gcal_month_cell_parent_class)->focus (widget, direction); } /* * GObject overrides */ static void gcal_month_cell_dispose (GObject *object) { GcalMonthCell *self = (GcalMonthCell *)object; GcalContext *context = gcal_application_get_context (GCAL_DEFAULT_APPLICATION); GcalWeatherService *weather_service = gcal_context_get_weather_service (context); gcal_weather_service_release (weather_service); gcal_clear_date_time (&self->date); g_clear_pointer (&self->breakpoint_bin, gtk_widget_unparent); G_OBJECT_CLASS (gcal_month_cell_parent_class)->dispose (object); } static void gcal_month_cell_class_init (GcalMonthCellClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->dispose = gcal_month_cell_dispose; widget_class->focus = gcal_month_cell_focus; signals[SHOW_OVERFLOW] = g_signal_new ("show-overflow", GCAL_TYPE_MONTH_CELL, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GTK_TYPE_WIDGET); signals[ACTIVATE] = gcal_create_activate_signal_and_shortcuts (widget_class, GCAL_TYPE_MONTH_CELL); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/views/gcal-month-cell.ui"); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, breakpoint_bin); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, breakpoint_narrow); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, day_label); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, header_box); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, overflow_button); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, overflow_inscription); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, content_space); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, temp_label); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, weather_icon); gtk_widget_class_bind_template_callback (widget_class, overflow_button_clicked_cb); gtk_widget_class_set_css_name (widget_class, "monthcell"); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); } static void gcal_month_cell_init (GcalMonthCell *self) { GcalContext *context = gcal_application_get_context (GCAL_DEFAULT_APPLICATION); gtk_widget_init_template (GTK_WIDGET (self)); g_signal_connect_swapped (self->breakpoint_bin, "notify::current-breakpoint", G_CALLBACK (on_breakpoint_changed_cb), self); g_signal_connect_object (gcal_context_get_clock (context), "day-changed", G_CALLBACK (day_changed_cb), self, 0); g_signal_connect_object (gcal_context_get_weather_service (context), "weather-changed", G_CALLBACK (on_weather_service_weather_changed_cb), self, 0); GcalWeatherService *weather_service = gcal_context_get_weather_service (context); gcal_weather_service_hold (weather_service); gtk_widget_set_child_visible (self->overflow_button, FALSE); } GtkWidget* gcal_month_cell_new (void) { return g_object_new (GCAL_TYPE_MONTH_CELL, NULL); } GDateTime* gcal_month_cell_get_date (GcalMonthCell *self) { g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), NULL); return self->date; } void gcal_month_cell_set_date (GcalMonthCell *self, GDateTime *date) { gint day_of_month; g_return_if_fail (GCAL_IS_MONTH_CELL (self)); if (self->date && date && gcal_date_time_compare_date (self->date, date) == 0) return; gcal_clear_date_time (&self->date); self->date = g_date_time_ref (date); day_of_month = g_date_time_get_day_of_month (date); update_style_flags (self); add_month_separators (self); if (day_of_month == 1) { g_autofree gchar *month_name = g_date_time_format (date, "%OB"); gtk_label_set_text (self->day_label, month_name); gtk_widget_add_css_class (GTK_WIDGET (self->day_label), "first-day"); } else { g_autofree gchar *date_number = g_strdup_printf ("%d", day_of_month); gtk_label_set_text (self->day_label, date_number); gtk_widget_remove_css_class (GTK_WIDGET (self->day_label), "first-day"); } update_weather (self); } guint gcal_month_cell_get_overflow (GcalMonthCell *self) { g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), 0); return self->n_overflow; } void gcal_month_cell_set_overflow (GcalMonthCell *self, guint n_overflow) { g_return_if_fail (GCAL_IS_MONTH_CELL (self)); if (self->n_overflow == n_overflow) return; self->n_overflow = n_overflow; gtk_widget_set_child_visible (self->overflow_button, n_overflow > 0); if (n_overflow > 0) { g_autofree gchar *text = g_strdup_printf ("+%d", n_overflow); gtk_inscription_set_text (self->overflow_inscription, text); } else { gtk_inscription_set_text (self->overflow_inscription, ""); } } gint gcal_month_cell_get_content_space (GcalMonthCell *self) { g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), -1); return gtk_widget_get_height (self->content_space); } gint gcal_month_cell_get_header_height (GcalMonthCell *self) { g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), -1); return get_parent_bin_height (self->header_box); } gint gcal_month_cell_get_overflow_height (GcalMonthCell *self) { g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), -1); return get_parent_bin_height (self->overflow_button); } gboolean gcal_month_cell_get_selected (GcalMonthCell *self) { g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), FALSE); return self->selected; } void gcal_month_cell_set_selected (GcalMonthCell *self, gboolean selected) { GtkStateFlags flags; g_return_if_fail (GCAL_IS_MONTH_CELL (self)); if (self->selected == selected) return; self->selected = selected; gtk_accessible_update_state (GTK_ACCESSIBLE (self), GTK_ACCESSIBLE_STATE_SELECTED, selected, -1); /* Update style context state */ flags = gtk_widget_get_state_flags (GTK_WIDGET (self)); if (selected) flags |= GTK_STATE_FLAG_SELECTED; else flags &= ~GTK_STATE_FLAG_SELECTED; gtk_widget_set_state_flags (GTK_WIDGET (self), flags, TRUE); gtk_widget_queue_resize (GTK_WIDGET (self)); } GtkWidget * gcal_month_cell_get_overflow_button (GcalMonthCell *self) { g_assert (GCAL_IS_MONTH_CELL (self)); return self->overflow_button; }