Files
federated-calendar/src/gui/views/gcal-month-cell.c
T

482 lines
13 KiB
C

/* gcal-month-cell.c
*
* Copyright (C) 2017 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}