Co-authored by: Ryan Oliveira ryan.oliveiraf@usp.br Part-of: <https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/806>
482 lines
13 KiB
C
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;
|
|
}
|
|
|