gui/event-editor: Add time zone selection

Creates a chooser managing a GDateTime. The chooser allows the user
to change the date, the time and the timezone independently.

This replaces the precedent GcalTimeChooserRow widget, because it is
easier to manage the different interactions with the user in a single
widget.

Co-authored-by: Georges Basile Stavracas Neto
<georges.stavracas@gmail.com>
This commit is contained in:
Titouan Real
2025-01-31 19:37:26 -03:00
parent e909422663
commit 42f891ff42
28 changed files with 2007 additions and 1210 deletions
+2
View File
@@ -27,6 +27,8 @@ src/gui/event-editor/gcal-schedule-section.ui
src/gui/event-editor/gcal-summary-section.c
src/gui/event-editor/gcal-summary-section.ui
src/gui/event-editor/gcal-time-selector.ui
src/gui/event-editor/gcal-time-zone-dialog.c
src/gui/event-editor/gcal-time-zone-dialog.ui
src/gui/gcal-application.c
src/gui/gcal-calendar-button.ui
src/gui/gcal-calendar-combo-row.ui
+1 -1
View File
@@ -234,7 +234,7 @@ build_component_from_datetime (GcalEvent *self,
g_autoptr (GTimeZone) zone = NULL;
ICalTimezone *tz;
zone = gcal_util_get_app_timezone_or_local ();
zone = g_date_time_get_timezone (dt);
tz = gcal_timezone_to_icaltimezone (zone);
i_cal_time_set_timezone (itt, tz);
tzid = g_strdup (i_cal_timezone_get_tzid (tz));
@@ -5,6 +5,7 @@
<file compressed="true">gcal-date-chooser.ui</file>
<file compressed="true">gcal-date-chooser-day.ui</file>
<file compressed="true">gcal-date-chooser-row.ui</file>
<file compressed="true">gcal-date-time-chooser.ui</file>
<file compressed="true">gcal-date-selector.ui</file>
<file compressed="true">gcal-event-editor-dialog.ui</file>
<file compressed="true">gcal-multi-choice.ui</file>
@@ -12,7 +13,7 @@
<file compressed="true">gcal-reminders-section.ui</file>
<file compressed="true">gcal-schedule-section.ui</file>
<file compressed="true">gcal-summary-section.ui</file>
<file compressed="true">gcal-time-chooser-row.ui</file>
<file compressed="true">gcal-time-selector.ui</file>
<file compressed="true">gcal-time-zone-dialog.ui</file>
<file compressed="true">gcal-time-zone-dialog-row.ui</file>
</gresource>
</gresources>
+8 -1
View File
@@ -299,9 +299,16 @@ void
gcal_date_chooser_row_set_date (GcalDateChooserRow *self,
GDateTime *date)
{
g_autoptr (GDateTime) date_utc = NULL;
g_return_if_fail (GCAL_IS_DATE_CHOOSER_ROW (self));
gcal_view_set_date (GCAL_VIEW (self->date_chooser), date);
date_utc = g_date_time_new_utc (g_date_time_get_year (date),
g_date_time_get_month (date),
g_date_time_get_day_of_month (date),
0, 0, 0);
gcal_view_set_date (GCAL_VIEW (self->date_chooser), date_utc);
update_entry (self);
@@ -0,0 +1,829 @@
/* gcal-date-time-chooser.c
*
* Copyright (C) 2024 Titouan Real <titouan.real@gmail.com>
*
* gnome-calendar 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.
*
* gnome-calendar 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 "GcalDateTimeChooser"
#include "gcal-date-chooser-row.h"
#include "gcal-date-time-chooser.h"
#include "gcal-date-time-utils.h"
#include "gcal-debug.h"
#include "gcal-time-zone-dialog.h"
#include <glib/gi18n.h>
struct _GcalDateTimeChooser
{
AdwPreferencesGroup parent;
GcalDateChooserRow *date_row;
AdwEntryRow *time_row;
GtkPopover *time_popover;
AdwButtonContent *time_zone_button_content;
AdwToggleGroup *period_toggle_group;
GtkAdjustment *hour_adjustment;
GtkAdjustment *minute_adjustment;
GDateTime *date_time;
GcalTimeFormat time_format;
};
G_DEFINE_TYPE (GcalDateTimeChooser, gcal_date_time_chooser, ADW_TYPE_PREFERENCES_GROUP);
enum
{
PROP_0,
PROP_DATE_TIME,
PROP_DATE_LABEL,
PROP_TIME_LABEL,
N_PROPS
};
static GParamSpec *properties[N_PROPS] = {
NULL,
};
static void on_spin_buttons_value_changed_cb (GcalDateTimeChooser *self);
static void on_period_toggle_group_active_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalDateTimeChooser *self);
/*
* Auxiliary methods
*/
/*
* Use the stored date_time of the GcalDateTimeChooser to format the content of the time entry.
*/
static void
normalize_time_entry (GcalDateTimeChooser *self)
{
g_autoptr (GTimeZone) local_time_zone = g_time_zone_new_local ();
g_autoptr (GString) label = NULL;
const gchar *date_time_identifier;
const gchar *local_identifier;
gchar *date_time_str;
GCAL_ENTRY;
switch (self->time_format)
{
case GCAL_TIME_FORMAT_12H:
date_time_str = g_date_time_format (self->date_time, "%I:%M %p");
break;
case GCAL_TIME_FORMAT_24H:
date_time_str = g_date_time_format (self->date_time, "%R");
break;
default:
g_assert_not_reached ();
}
label = g_string_new_take (date_time_str);
date_time_identifier = g_time_zone_get_identifier (g_date_time_get_timezone (self->date_time));
local_identifier = g_time_zone_get_identifier (local_time_zone);
if (g_strcmp0 (date_time_identifier, local_identifier) != 0)
{
g_autofree gchar *time_zone_str = gcal_date_time_format_utc_offset (self->date_time);
g_string_append (label, " (");
g_string_append (label, time_zone_str);
g_string_append (label, ")");
}
gtk_editable_set_text (GTK_EDITABLE (self->time_row), label->str);
GCAL_EXIT;
}
/*
* Use the timezone of the stored date_time of the GcalDateTimeChooser to format the label of the timezone button.
*/
static void
normalize_timezone_button_label (GcalDateTimeChooser *self)
{
g_autofree gchar *formatted = NULL;
g_autofree gchar *utc_str = NULL;
const gchar *identifier;
identifier = g_time_zone_get_identifier (g_date_time_get_timezone (self->date_time));
if (g_strcmp0 (identifier, "UTC") == 0)
{
adw_button_content_set_label (self->time_zone_button_content, "UTC");
return;
}
utc_str = gcal_date_time_format_utc_offset (self->date_time);
/* Translators: "%1$s" is the timezone identifier (e.g. UTC, America/Sao_Paulo)
* and "%2$s" is the timezone offset (e.g. UTC-3).
*/
formatted = g_strdup_printf (_("%1$s (%2$s)"), identifier, utc_str);
adw_button_content_set_label (self->time_zone_button_content, formatted);
}
/*
* Use the time of the stored date_time of the GcalDateTimeChooser to format the spinners and the am/pm toggle of the
* time popover.
*/
static void
normalize_time_popover (GcalDateTimeChooser *self)
{
gint new_hour, new_minute;
GCAL_ENTRY;
new_hour = g_date_time_get_hour (self->date_time);
new_minute = g_date_time_get_minute (self->date_time);
g_signal_handlers_block_by_func (self->hour_adjustment, on_spin_buttons_value_changed_cb, self);
g_signal_handlers_block_by_func (self->minute_adjustment, on_spin_buttons_value_changed_cb, self);
g_signal_handlers_block_by_func (self->period_toggle_group, on_period_toggle_group_active_changed_cb, self);
adw_toggle_group_set_active_name (self->period_toggle_group, new_hour < 12 ? "am" : "pm");
switch (self->time_format)
{
case GCAL_TIME_FORMAT_12H:
if (new_hour == 0)
gtk_adjustment_set_value (self->hour_adjustment, 12);
else if (new_hour <= 12)
gtk_adjustment_set_value (self->hour_adjustment, new_hour);
else
gtk_adjustment_set_value (self->hour_adjustment, new_hour - 12);
break;
case GCAL_TIME_FORMAT_24H:
gtk_adjustment_set_value (self->hour_adjustment, new_hour);
break;
default:
g_assert_not_reached ();
}
gtk_adjustment_set_value (self->minute_adjustment, new_minute);
g_signal_handlers_unblock_by_func (self->hour_adjustment, on_spin_buttons_value_changed_cb, self);
g_signal_handlers_unblock_by_func (self->minute_adjustment, on_spin_buttons_value_changed_cb, self);
g_signal_handlers_unblock_by_func (self->period_toggle_group, on_period_toggle_group_active_changed_cb, self);
GCAL_EXIT;
}
static GRegex *
get_12h_regex (void)
{
static GRegex *regex = NULL;
if (g_once_init_enter_pointer (&regex))
{
g_autoptr (GError) error = NULL;
g_autofree gchar *str = NULL;
str = g_strdup_printf ("^(?<hour>[0-9]{1,2})([\\-:./_,'a-z](?<minute>[0-9]{1,2}))? ?(?<ampm>%s|%s)",
_ ("AM"), _ ("PM"));
regex = g_regex_new (str, G_REGEX_CASELESS, 0, &error);
if (error)
g_error ("Failed to compile regex: %s. Aborting...", error->message);
}
return regex;
}
static GRegex *
get_24h_regex (void)
{
static GRegex *regex = NULL;
if (g_once_init_enter_pointer (&regex))
{
g_autoptr (GError) error = NULL;
regex = g_regex_new ("^(?<hour>[0-9]{1,2})([\\-:./_,'a-z](?<minute>[0-9]{1,2}))?", G_REGEX_CASELESS, 0, &error);
if (error)
g_error ("Failed to compile regex: %s. Aborting...", error->message);
}
return regex;
}
/*
* Parse the string of the time entry.
* If the string is not recognised as a time, it resets the entry to the formatted form of the last time.
* If it is recognised, the stored date_time is updated and the entry is formatted accordingly.
*/
static void
validate_and_normalize_time_entry (GcalDateTimeChooser *self)
{
const gchar *text;
text = gtk_editable_get_text (GTK_EDITABLE (self->time_row));
switch (self->time_format)
{
case GCAL_TIME_FORMAT_12H:
{
g_autoptr (GMatchInfo) match_info = NULL;
g_autoptr (GDateTime) date_time = NULL;
g_autofree gchar *minute_str = NULL;
g_autofree gchar *hour_str = NULL;
g_autofree gchar *ampm_str = NULL;
gboolean am;
gint64 hour, minute;
if (!g_regex_match (get_12h_regex (), text, 0, &match_info))
{
normalize_time_entry (self);
break;
}
hour_str = g_match_info_fetch_named (match_info, "hour");
minute_str = g_match_info_fetch_named (match_info, "minute");
ampm_str = g_match_info_fetch_named (match_info, "ampm");
hour = g_ascii_strtoll (hour_str, NULL, 10);
minute = g_ascii_strtoll (minute_str, NULL, 10);
am = (strcmp (g_ascii_strup (ampm_str, -1), _ ("AM")) == 0);
if (hour == 0 || hour > 12 || minute > 59)
normalize_time_entry (self);
if (hour == 12)
hour = 0;
if (!am)
hour += 12;
date_time = g_date_time_new (g_date_time_get_timezone (self->date_time),
g_date_time_get_year (self->date_time),
g_date_time_get_month (self->date_time),
g_date_time_get_day_of_month (self->date_time),
hour, minute, 0);
gcal_date_time_chooser_set_date_time (self, date_time);
}
break;
case GCAL_TIME_FORMAT_24H:
{
g_autoptr (GMatchInfo) match_info = NULL;
g_autoptr (GDateTime) date_time = NULL;
g_autofree gchar *minute_str = NULL;
g_autofree gchar *hour_str = NULL;
g_autofree gchar *ampm_str = NULL;
gint64 hour, minute;
if (!g_regex_match (get_24h_regex (), text, 0, &match_info))
{
normalize_time_entry (self);
break;
}
hour_str = g_match_info_fetch_named (match_info, "hour");
minute_str = g_match_info_fetch_named (match_info, "minute");
hour = g_ascii_strtoll (hour_str, NULL, 10);
minute = g_ascii_strtoll (minute_str, NULL, 10);
if (hour > 23 || minute > 59)
{
normalize_time_entry (self);
break;
}
date_time = g_date_time_new (g_date_time_get_timezone (self->date_time),
g_date_time_get_year (self->date_time),
g_date_time_get_month (self->date_time),
g_date_time_get_day_of_month (self->date_time),
hour, minute, 0);
gcal_date_time_chooser_set_date_time (self, date_time);
}
break;
default:
g_assert_not_reached ();
}
}
/*
* Callbacks
*/
static void
on_date_changed_cb (GcalDateTimeChooser *self,
GParamSpec *pspec,
GtkWidget *widget)
{
g_autoptr (GDateTime) new_date_time = NULL;
GDateTime *date_chooser_row_date;
GCAL_ENTRY;
date_chooser_row_date = gcal_date_chooser_row_get_date (self->date_row);
new_date_time = g_date_time_new (g_date_time_get_timezone (self->date_time),
g_date_time_get_year (date_chooser_row_date),
g_date_time_get_month (date_chooser_row_date),
g_date_time_get_day_of_month (date_chooser_row_date),
g_date_time_get_hour (self->date_time),
g_date_time_get_minute (self->date_time),
0);
gcal_date_time_chooser_set_date_time (self, new_date_time);
GCAL_EXIT;
}
static void
on_time_zone_selected_cb (GcalTimeZoneDialog *dialog,
GcalDateTimeChooser *self)
{
GTimeZone *time_zone;
GDateTime *new;
GCAL_ENTRY;
time_zone = gcal_time_zone_dialog_get_time_zone (dialog);
new = g_date_time_new (time_zone,
g_date_time_get_year (self->date_time),
g_date_time_get_month (self->date_time),
g_date_time_get_day_of_month (self->date_time),
g_date_time_get_hour (self->date_time),
g_date_time_get_minute (self->date_time),
0);
gcal_date_time_chooser_set_date_time (self, new);
GCAL_EXIT;
}
static void
on_time_zone_button_clicked_cb (GtkButton *button,
GcalDateTimeChooser *self)
{
AdwDialog *dialog;
validate_and_normalize_time_entry (self);
dialog = ADW_DIALOG (gcal_time_zone_dialog_new (self->date_time));
g_signal_connect (dialog, "timezone-selected", G_CALLBACK (on_time_zone_selected_cb), self);
gtk_popover_popdown (self->time_popover);
adw_dialog_present (dialog, GTK_WIDGET (self));
}
static void
on_time_row_contains_focus_changed_cb (GtkEventControllerFocus *focus_controller,
GParamSpec *pspec,
GcalDateTimeChooser *self)
{
validate_and_normalize_time_entry (self);
}
static void
on_time_popover_shown_cb (GtkPopover *popover,
GcalDateTimeChooser *self)
{
validate_and_normalize_time_entry (self);
}
static void
on_spin_button_output_cb (GtkWidget *widget,
GcalDateTimeChooser *self)
{
g_autofree gchar *text = NULL;
GtkAdjustment *adjustment;
gint hour;
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget));
hour = gtk_adjustment_get_value (adjustment);
text = g_strdup_printf ("%02d", hour);
gtk_editable_set_text (GTK_EDITABLE (widget), text);
}
static void
on_spin_buttons_value_changed_cb (GcalDateTimeChooser *self)
{
g_autoptr (GDateTime) new_date_time = NULL;
gint hour, minute;
GCAL_ENTRY;
hour = gtk_adjustment_get_value (self->hour_adjustment);
minute = gtk_adjustment_get_value (self->minute_adjustment);
switch (self->time_format)
{
case GCAL_TIME_FORMAT_12H:
if (hour == 12)
hour = 0;
if (g_strcmp0 (adw_toggle_group_get_active_name (self->period_toggle_group), "pm") == 0)
hour += 12;
break;
case GCAL_TIME_FORMAT_24H:
break;
default:
g_assert_not_reached ();
}
new_date_time = g_date_time_new (g_date_time_get_timezone (self->date_time),
g_date_time_get_year (self->date_time),
g_date_time_get_month (self->date_time),
g_date_time_get_day_of_month (self->date_time),
hour, minute, 0);
gcal_date_time_chooser_set_date_time (self, new_date_time);
GCAL_EXIT;
}
static void
on_hour_spin_button_wrapped_cb (GtkSpinButton *spin_button,
GcalDateTimeChooser *self)
{
if (g_strcmp0 (adw_toggle_group_get_active_name (self->period_toggle_group), "am") == 0)
adw_toggle_group_set_active_name (self->period_toggle_group, "pm");
else
adw_toggle_group_set_active_name (self->period_toggle_group, "am");
}
static void
on_period_toggle_group_active_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalDateTimeChooser *self)
{
g_autoptr (GDateTime) new_date_time = NULL;
if (g_strcmp0 (adw_toggle_group_get_active_name (self->period_toggle_group), "am") == 0)
new_date_time = g_date_time_add_hours (self->date_time, -12);
else
new_date_time = g_date_time_add_hours (self->date_time, 12);
gcal_date_time_chooser_set_date_time (self, new_date_time);
}
/*
* GObject overrides
*/
static void
gcal_date_time_chooser_dispose (GObject *object)
{
GcalDateTimeChooser *self = GCAL_DATE_TIME_CHOOSER (object);
gcal_clear_date_time (&self->date_time);
G_OBJECT_CLASS (gcal_date_time_chooser_parent_class)->dispose (object);
}
static void
gcal_date_time_chooser_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GcalDateTimeChooser *self = GCAL_DATE_TIME_CHOOSER (object);
switch (prop_id)
{
case PROP_DATE_TIME:
g_value_set_boxed (value, gcal_date_time_chooser_get_date_time (self));
break;
case PROP_DATE_LABEL:
g_value_set_string (value, gcal_date_time_chooser_get_date_label (self));
break;
case PROP_TIME_LABEL:
g_value_set_string (value, gcal_date_time_chooser_get_time_label (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_date_time_chooser_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GcalDateTimeChooser *self = GCAL_DATE_TIME_CHOOSER (object);
switch (prop_id)
{
case PROP_DATE_TIME:
gcal_date_time_chooser_set_date_time (self, g_value_get_boxed (value));
break;
case PROP_DATE_LABEL:
gcal_date_time_chooser_set_date_label (self, g_value_get_string (value));
break;
case PROP_TIME_LABEL:
gcal_date_time_chooser_set_time_label (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_date_time_chooser_class_init (GcalDateTimeChooserClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_ensure (GCAL_TYPE_DATE_CHOOSER_ROW);
object_class->dispose = gcal_date_time_chooser_dispose;
object_class->get_property = gcal_date_time_chooser_get_property;
object_class->set_property = gcal_date_time_chooser_set_property;
/**
* GcalDateTimeChooser::date-time:
*
* The current date-time of the chooser.
*/
properties[PROP_DATE_TIME] = g_param_spec_boxed ("date-time",
"DateTime of the date-time chooser",
"The current date-time of the chooser",
G_TYPE_DATE_TIME,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GcalDateTimeChooser::date-label:
*
* The current date label of the date row.
*/
properties[PROP_DATE_LABEL] = g_param_spec_string ("date-label",
NULL,
NULL,
"",
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GcalDateTimeChooser::time-label:
*
* The current time label of the time row.
*/
properties[PROP_TIME_LABEL] = g_param_spec_string ("time-label",
NULL,
NULL,
"",
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | 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/ui/event-editor/gcal-date-time-chooser.ui");
gtk_widget_class_bind_template_child (widget_class, GcalDateTimeChooser, date_row);
gtk_widget_class_bind_template_child (widget_class, GcalDateTimeChooser, time_row);
gtk_widget_class_bind_template_child (widget_class, GcalDateTimeChooser, time_popover);
gtk_widget_class_bind_template_child (widget_class, GcalDateTimeChooser, time_zone_button_content);
gtk_widget_class_bind_template_child (widget_class, GcalDateTimeChooser, period_toggle_group);
gtk_widget_class_bind_template_child (widget_class, GcalDateTimeChooser, hour_adjustment);
gtk_widget_class_bind_template_child (widget_class, GcalDateTimeChooser, minute_adjustment);
gtk_widget_class_bind_template_callback (widget_class, on_date_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_time_zone_button_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, on_time_row_contains_focus_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_time_popover_shown_cb);
gtk_widget_class_bind_template_callback (widget_class, on_spin_button_output_cb);
gtk_widget_class_bind_template_callback (widget_class, on_spin_buttons_value_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_hour_spin_button_wrapped_cb);
gtk_widget_class_bind_template_callback (widget_class, on_period_toggle_group_active_changed_cb);
}
static void
gcal_date_time_chooser_init (GcalDateTimeChooser *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
self->date_time = g_date_time_new_now_local ();
}
/* Public API */
GtkWidget *
gcal_date_time_chooser_new (void)
{
return g_object_new (GCAL_TYPE_DATE_TIME_CHOOSER, NULL);
}
void
gcal_date_time_chooser_set_time_format (GcalDateTimeChooser *self,
GcalTimeFormat time_format)
{
g_return_if_fail (GCAL_IS_DATE_TIME_CHOOSER (self));
self->time_format = time_format;
gtk_widget_set_visible (GTK_WIDGET (self->period_toggle_group), time_format == GCAL_TIME_FORMAT_12H);
switch (self->time_format)
{
case GCAL_TIME_FORMAT_12H:
gtk_adjustment_set_lower (self->hour_adjustment, 1);
gtk_adjustment_set_upper (self->hour_adjustment, 12);
break;
case GCAL_TIME_FORMAT_24H:
gtk_adjustment_set_lower (self->hour_adjustment, 0);
gtk_adjustment_set_upper (self->hour_adjustment, 23);
break;
default:
g_assert_not_reached ();
}
normalize_time_entry (self);
}
/**
* gcal_date_time_chooser_get_date_time:
* @self: a #GcalDateTimeChooser
*
* Get the value of the date-time shown
*
* Returns: (transfer none): the date-time of the chooser.
*/
GDateTime *
gcal_date_time_chooser_get_date_time (GcalDateTimeChooser *self)
{
g_assert (GCAL_IS_DATE_TIME_CHOOSER (self));
return g_date_time_ref (self->date_time);
}
/**
* gcal_date_time_chooser_set_date_time:
* @self: a #GcalDateTimeChooser
* @date_time: a valid #GDateTime
*
* Set the value of the shown date-time.
*/
void
gcal_date_time_chooser_set_date_time (GcalDateTimeChooser *self,
GDateTime *date_time)
{
GCAL_ENTRY;
gcal_set_date_time (&self->date_time, date_time);
g_signal_handlers_block_by_func (self->date_row, on_date_changed_cb, self);
gcal_date_chooser_row_set_date (self->date_row, date_time);
g_signal_handlers_unblock_by_func (self->date_row, on_date_changed_cb, self);
normalize_time_entry (self);
normalize_timezone_button_label (self);
normalize_time_popover (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DATE_TIME]);
GCAL_EXIT;
}
/**
* gcal_date_time_chooser_get_date:
* @self: a #GcalDateTimeChooser
*
* Get the value of the date shown.
* The timezone, hours, minutes and seconds are discarded and replaced with UTC, 0, 0 and 0 respectively.
*
* Returns: (transfer full): the date of the chooser.
*/
GDateTime *
gcal_date_time_chooser_get_date (GcalDateTimeChooser *self)
{
g_assert (GCAL_IS_DATE_TIME_CHOOSER (self));
return g_date_time_new_utc (g_date_time_get_year (self->date_time),
g_date_time_get_month (self->date_time),
g_date_time_get_day_of_month (self->date_time),
0, 0, 0);
}
/**
* gcal_date_time_chooser_set_date:
* @self: a #GcalDateTimeChooser
* @date_time: a valid #GDateTime
*
* Set the value of the shown date.
* The timezone, hours, minutes and seconds are ignored.
*/
void
gcal_date_time_chooser_set_date (GcalDateTimeChooser *self,
GDateTime *date_time)
{
g_autoptr (GDateTime) new_date_time = NULL;
g_assert (GCAL_IS_DATE_TIME_CHOOSER (self));
GCAL_ENTRY;
new_date_time = g_date_time_new (g_date_time_get_timezone (self->date_time),
g_date_time_get_year (date_time),
g_date_time_get_month (date_time),
g_date_time_get_day_of_month (date_time),
g_date_time_get_hour (self->date_time),
g_date_time_get_minute (self->date_time),
0);
gcal_date_time_chooser_set_date_time (self, new_date_time);
GCAL_EXIT;
}
/**
* gcal_date_time_chooser_get_date_label:
* @self: a #GcalDateTimeChooser
*
* Get the value of the date row label
*
* Returns: (transfer none): the label of the date row.
*/
const gchar*
gcal_date_time_chooser_get_date_label (GcalDateTimeChooser *self)
{
g_assert (GCAL_IS_DATE_TIME_CHOOSER (self));
return adw_preferences_row_get_title (ADW_PREFERENCES_ROW (self->date_row));
}
/**
* gcal_date_time_chooser_set_date_label:
* @self: a #GcalDateTimeChooser
* @date_label: The label
*
* Set the value of the date row label
*/
void
gcal_date_time_chooser_set_date_label (GcalDateTimeChooser *self,
const gchar *date_label)
{
g_assert (GCAL_IS_DATE_TIME_CHOOSER (self));
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->date_row), date_label);
}
/**
* gcal_date_time_chooser_get_time_label:
* @self: a #GcalDateTimeChooser
*
* Get the value of the time row label
*
* Returns: (transfer none): the label of the time row.
*/
const gchar*
gcal_date_time_chooser_get_time_label (GcalDateTimeChooser *self)
{
g_assert (GCAL_IS_DATE_TIME_CHOOSER (self));
return adw_preferences_row_get_title (ADW_PREFERENCES_ROW (self->time_row));
}
/**
* gcal_date_time_chooser_set_time_label:
* @self: a #GcalDateTimeChooser
* @time_label: The label
*
* Set the value of the time row label
*/
void
gcal_date_time_chooser_set_time_label (GcalDateTimeChooser *self,
const gchar *time_label)
{
g_assert (GCAL_IS_DATE_TIME_CHOOSER (self));
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->time_row), time_label);
}
@@ -0,0 +1,56 @@
/* gcal-date-time-chooser.h
*
* Copyright (C) 2024 Titouan Real <titouan.real@gmail.com>
*
* gnome-calendar 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.
*
* gnome-calendar 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/>.
*/
#pragma once
#include <adwaita.h>
#include "gcal-enums.h"
G_BEGIN_DECLS
#define GCAL_TYPE_DATE_TIME_CHOOSER (gcal_date_time_chooser_get_type ())
G_DECLARE_FINAL_TYPE (GcalDateTimeChooser, gcal_date_time_chooser, GCAL, DATE_TIME_CHOOSER, AdwPreferencesGroup)
GtkWidget* gcal_date_time_chooser_new (void);
void gcal_date_time_chooser_set_time_format (GcalDateTimeChooser *chooser,
GcalTimeFormat time_format);
GDateTime* gcal_date_time_chooser_get_date_time (GcalDateTimeChooser *chooser);
void gcal_date_time_chooser_set_date_time (GcalDateTimeChooser *chooser,
GDateTime *date_time);
GDateTime* gcal_date_time_chooser_get_date (GcalDateTimeChooser *chooser);
void gcal_date_time_chooser_set_date (GcalDateTimeChooser *chooser,
GDateTime *date_time);
const gchar* gcal_date_time_chooser_get_date_label (GcalDateTimeChooser *chooser);
void gcal_date_time_chooser_set_date_label (GcalDateTimeChooser *chooser,
const gchar *date_label);
const gchar* gcal_date_time_chooser_get_time_label (GcalDateTimeChooser *chooser);
void gcal_date_time_chooser_set_time_label (GcalDateTimeChooser *chooser,
const gchar *time_label);
G_END_DECLS
@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8" ?>
<interface>
<template class="GcalDateTimeChooser" parent="AdwPreferencesGroup">
<child>
<object class="GcalDateChooserRow" id="date_row">
<property name="title" translatable="yes">Date</property>
<signal name="notify::date" handler="on_date_changed_cb" object="GcalDateTimeChooser" swapped="yes" />
</object>
</child>
<child>
<object class="AdwEntryRow" id="time_row">
<property name="title" translatable="yes">Time</property>
<child>
<object class="GtkEventControllerFocus">
<signal name="notify::contains-focus" handler="on_time_row_contains_focus_changed_cb" object="GcalDateTimeChooser" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuButton">
<property name="icon-name">clock-alt-symbolic</property>
<property name="valign">center</property>
<property name="tooltip-text">Edit Time</property>
<property name="popover">
<object class="GtkPopover" id="time_popover">
<property name="position">bottom</property>
<child>
<object class="GtkBox">
<property name="spacing">15</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkBox">
<property name="spacing">12</property>
<property name="halign">center</property>
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
<child>
<object class="GtkBox">
<property name="spacing">6</property>
<property name="halign">center</property>
<!-- Hour -->
<child>
<object class="GtkSpinButton">
<property name="xalign">0.5</property>
<property name="orientation">vertical</property>
<property name="adjustment">hour_adjustment</property>
<property name="wrap">True</property>
<signal name="output" handler="on_spin_button_output_cb" object="GcalDateTimeChooser" swapped="no"/>
<signal name="wrapped" handler="on_hour_spin_button_wrapped_cb" object="GcalDateTimeChooser" swapped="no"/>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">:</property>
</object>
</child>
<!-- Minute -->
<child>
<object class="GtkSpinButton">
<property name="xalign">0.5</property>
<property name="orientation">vertical</property>
<property name="adjustment">minute_adjustment</property>
<property name="wrap">True</property>
<signal name="output" handler="on_spin_button_output_cb" object="GcalDateTimeChooser" swapped="no"/>
</object>
</child>
</object>
</child>
<!-- AM/PM -->
<child>
<object class="AdwToggleGroup" id="period_toggle_group">
<property name="visible">False</property>
<property name="valign">center</property>
<property name="active-name">am</property>
<child>
<object class="AdwToggle">
<property name="label" translatable="yes">AM</property>
<property name="name">am</property>
</object>
</child>
<child>
<object class="AdwToggle">
<property name="label" translatable="yes">PM</property>
<property name="name">pm</property>
</object>
</child>
<signal name="notify::active" handler="on_period_toggle_group_active_changed_cb" swapped="no"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton">
<property name="valign">center</property>
<property name="tooltip-text" translatable="yes">Change Timezone</property>
<child>
<object class="AdwButtonContent" id="time_zone_button_content">
<property name="icon-name">globe-symbolic</property>
</object>
</child>
<signal name="clicked" handler="on_time_zone_button_clicked_cb" object="GcalDateTimeChooser" swapped="no"/>
<accessibility>
<property name="label" translatable="yes">Change Timezone</property>
</accessibility>
</object>
</child>
</object>
</child>
<signal name="show" handler="on_time_popover_shown_cb" object="GcalDateTimeChooser" swapped="no" />
</object>
</property>
<accessibility>
<property name="label" translatable="yes">Edit time</property>
</accessibility>
<style>
<class name="flat" />
</style>
</object>
</child>
</object>
</child>
</template>
<object class="GtkAdjustment" id="hour_adjustment">
<property name="upper">23</property>
<property name="step_increment">1</property>
<property name="page_increment">5</property>
<signal name="value-changed" handler="on_spin_buttons_value_changed_cb" object="GcalDateTimeChooser" swapped="yes"/>
</object>
<object class="GtkAdjustment" id="minute_adjustment">
<property name="upper">59</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
<signal name="value-changed" handler="on_spin_buttons_value_changed_cb" object="GcalDateTimeChooser" swapped="yes"/>
</object>
</interface>
+315 -291
View File
@@ -23,29 +23,31 @@
#include "gcal-context.h"
#include "gcal-date-selector.h"
#include "gcal-date-chooser-row.h"
#include "gcal-date-time-chooser.h"
#include "gcal-debug.h"
#include "gcal-event.h"
#include "gcal-event-editor-section.h"
#include "gcal-recurrence.h"
#include "gcal-schedule-section.h"
#include "gcal-time-chooser-row.h"
#include "gcal-utils.h"
#include <glib/gi18n.h>
struct _GcalScheduleSection
{
GtkBox parent;
GtkBox parent;
AdwToggleGroup *schedule_type_toggle_group;
GcalDateChooserRow *start_date_row;
GcalTimeChooserRow *start_time_row;
GcalDateChooserRow *end_date_row;
GcalTimeChooserRow *end_time_row;
GtkWidget *number_of_occurrences_spin;
GtkWidget *repeat_combo;
GtkWidget *repeat_duration_combo;
GtkWidget *until_date_selector;
AdwToggleGroup *schedule_type_toggle_group;
AdwPreferencesGroup *start_date_group;
GcalDateChooserRow *start_date_row;
AdwPreferencesGroup *end_date_group;
GcalDateChooserRow *end_date_row;
GcalDateTimeChooser *start_date_time_chooser;
GcalDateTimeChooser *end_date_time_chooser;
GtkWidget *number_of_occurrences_spin;
GtkWidget *repeat_combo;
GtkWidget *repeat_duration_combo;
GtkWidget *until_date_selector;
GcalContext *context;
GcalEvent *event;
@@ -65,6 +67,22 @@ enum
N_PROPS
};
static void on_start_date_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalScheduleSection *self);
static void on_end_date_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalScheduleSection *self);
static void on_start_date_time_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalScheduleSection *self);
static void on_end_date_time_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalScheduleSection *self);
/*
* Auxiliary methods
@@ -77,135 +95,25 @@ all_day_selected (GcalScheduleSection *self)
return g_strcmp0 (active, "all-day") == 0;
}
static void
find_best_timezones_for_event (GcalScheduleSection *self,
GTimeZone **out_tz_start,
GTimeZone **out_tz_end)
block_date_signals (GcalScheduleSection *self)
{
gboolean was_all_day;
gboolean all_day;
gboolean new_event;
g_signal_handlers_block_by_func (self->start_date_row, on_start_date_changed_cb, self);
g_signal_handlers_block_by_func (self->end_date_row, on_end_date_changed_cb, self);
/* Update all day */
all_day = all_day_selected (self);
was_all_day = gcal_event_get_all_day (self->event);
new_event = self->flags & GCAL_EVENT_EDITOR_FLAG_NEW_EVENT;
GCAL_TRACE_MSG ("Finding best timezone with all_day=%d, was_all_day=%d, event_is_new=%d",
all_day,
was_all_day,
new_event);
if (!new_event && was_all_day && !all_day)
{
GCAL_TRACE_MSG ("Using original event timezones");
gcal_event_get_original_timezones (self->event, out_tz_start, out_tz_end);
return;
}
if (all_day)
{
GCAL_TRACE_MSG ("Using UTC timezones");
if (out_tz_start)
*out_tz_start = g_time_zone_new_utc ();
if (out_tz_end)
*out_tz_end = g_time_zone_new_utc ();
}
else
{
g_autoptr (GTimeZone) tz_start = NULL;
g_autoptr (GTimeZone) tz_end = NULL;
if (new_event)
{
GCAL_TRACE_MSG ("Using the local timezone");
tz_start = g_time_zone_new_local ();
tz_end = g_time_zone_new_local ();
}
else
{
GCAL_TRACE_MSG ("Using the current timezones");
tz_start = g_time_zone_ref (g_date_time_get_timezone (gcal_event_get_date_start (self->event)));
tz_end = g_time_zone_ref (g_date_time_get_timezone (gcal_event_get_date_end (self->event)));
}
if (out_tz_start)
*out_tz_start = g_steal_pointer (&tz_start);
if (out_tz_end)
*out_tz_end = g_steal_pointer (&tz_end);
}
g_signal_handlers_block_by_func (self->start_date_time_chooser, on_start_date_time_changed_cb, self);
g_signal_handlers_block_by_func (self->end_date_time_chooser, on_end_date_time_changed_cb, self);
}
static GDateTime*
return_datetime_for_widgets (GcalScheduleSection *self,
GTimeZone *timezone,
GcalDateChooserRow *date_chooser_row,
GcalTimeChooserRow *time_chooser_row)
static void
unblock_date_signals (GcalScheduleSection *self)
{
g_autoptr (GDateTime) date_in_local_tz = NULL;
g_autoptr (GDateTime) date_in_best_tz = NULL;
GDateTime *date;
GDateTime *time;
GDateTime *retval;
gboolean all_day;
g_signal_handlers_unblock_by_func (self->end_date_time_chooser, on_end_date_time_changed_cb, self);
g_signal_handlers_unblock_by_func (self->start_date_time_chooser, on_start_date_time_changed_cb, self);
all_day = all_day_selected (self);
date = gcal_date_chooser_row_get_date (date_chooser_row);
time = gcal_time_chooser_row_get_time (time_chooser_row);
date_in_local_tz = g_date_time_new_local (g_date_time_get_year (date),
g_date_time_get_month (date),
g_date_time_get_day_of_month (date),
all_day ? 0 : g_date_time_get_hour (time),
all_day ? 0 : g_date_time_get_minute (time),
0);
if (all_day)
date_in_best_tz = g_date_time_ref (date_in_local_tz);
else
date_in_best_tz = g_date_time_to_timezone (date_in_local_tz, timezone);
retval = g_date_time_new (timezone,
g_date_time_get_year (date_in_best_tz),
g_date_time_get_month (date_in_best_tz),
g_date_time_get_day_of_month (date_in_best_tz),
all_day ? 0 : g_date_time_get_hour (date_in_best_tz),
all_day ? 0 : g_date_time_get_minute (date_in_best_tz),
0);
return retval;
}
static GDateTime*
get_date_start (GcalScheduleSection *self)
{
g_autoptr (GTimeZone) start_tz = NULL;
find_best_timezones_for_event (self, &start_tz, NULL);
return return_datetime_for_widgets (self,
start_tz,
self->start_date_row,
self->start_time_row);
}
static GDateTime*
get_date_end (GcalScheduleSection *self)
{
g_autoptr (GTimeZone) end_tz = NULL;
find_best_timezones_for_event (self, NULL, &end_tz);
return return_datetime_for_widgets (self,
end_tz,
self->end_date_row,
self->end_time_row);
g_signal_handlers_unblock_by_func (self->end_date_row, on_end_date_changed_cb, self);
g_signal_handlers_unblock_by_func (self->start_date_row, on_start_date_changed_cb, self);
}
static void
@@ -220,68 +128,6 @@ remove_recurrence_properties (GcalEvent *event)
e_cal_component_commit_sequence (comp);
}
static void
sync_datetimes (GcalScheduleSection *self,
GParamSpec *pspec,
GtkWidget *widget)
{
GDateTime *start, *end, *start_local, *end_local, *new_date;
GtkWidget *date_widget, *time_widget;
gboolean is_start, is_all_day;
GCAL_ENTRY;
is_start = (widget == GTK_WIDGET (self->start_time_row) || widget == GTK_WIDGET (self->start_date_row));
is_all_day = all_day_selected (self);
start = get_date_start (self);
end = get_date_end (self);
/* The date is valid, no need to update the fields */
if (g_date_time_compare (end, start) >= 0)
GCAL_GOTO (out);
start_local = g_date_time_to_local (start);
end_local = g_date_time_to_local (end);
/*
* If the user is changing the start date or time, we change the end
* date or time (and vice versa).
*/
if (is_start)
{
new_date = is_all_day ? g_date_time_add_hours (start, 0) : g_date_time_add_hours (start_local, 1);
date_widget = GTK_WIDGET (self->end_date_row);
time_widget = GTK_WIDGET (self->end_time_row);
}
else
{
new_date = is_all_day ? g_date_time_add_hours (end, 0) : g_date_time_add_hours (end_local, -1);
date_widget = GTK_WIDGET (self->start_date_row);
time_widget = GTK_WIDGET (self->start_time_row);
}
g_signal_handlers_block_by_func (date_widget, sync_datetimes, self);
g_signal_handlers_block_by_func (time_widget, sync_datetimes, self);
gcal_date_chooser_row_set_date (GCAL_DATE_CHOOSER_ROW (date_widget), new_date);
gcal_time_chooser_row_set_time (GCAL_TIME_CHOOSER_ROW (time_widget), new_date);
g_signal_handlers_unblock_by_func (date_widget, sync_datetimes, self);
g_signal_handlers_unblock_by_func (time_widget, sync_datetimes, self);
g_clear_pointer (&start_local, g_date_time_unref);
g_clear_pointer (&end_local, g_date_time_unref);
g_clear_pointer (&new_date, g_date_time_unref);
out:
g_clear_pointer (&start, g_date_time_unref);
g_clear_pointer (&end, g_date_time_unref);
GCAL_EXIT;
}
/*
* Callbacks
@@ -294,8 +140,143 @@ on_schedule_type_changed_cb (GtkWidget *widget,
{
gboolean all_day = all_day_selected (self);
gtk_widget_set_visible (GTK_WIDGET (self->start_time_row), !all_day);
gtk_widget_set_visible (GTK_WIDGET (self->end_time_row), !all_day);
gtk_widget_set_visible (GTK_WIDGET (self->start_date_group), all_day);
gtk_widget_set_visible (GTK_WIDGET (self->end_date_group), all_day);
gtk_widget_set_visible (GTK_WIDGET (self->start_date_time_chooser), !all_day);
gtk_widget_set_visible (GTK_WIDGET (self->end_date_time_chooser), !all_day);
block_date_signals (self);
if (all_day)
{
g_autoptr (GDateTime) start_local = NULL;
g_autoptr (GDateTime) end_local = NULL;
GDateTime *start = gcal_date_time_chooser_get_date_time (self->start_date_time_chooser);
GDateTime *end = gcal_date_time_chooser_get_date_time (self->end_date_time_chooser);
start_local = g_date_time_to_local (start);
end_local = g_date_time_to_local (end);
gcal_date_chooser_row_set_date (self->start_date_row, start_local);
gcal_date_chooser_row_set_date (self->end_date_row, end_local);
gcal_date_time_chooser_set_date_time (self->start_date_time_chooser, start_local);
gcal_date_time_chooser_set_date_time (self->end_date_time_chooser, end_local);
}
unblock_date_signals (self);
}
static void
on_start_date_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalScheduleSection *self)
{
GDateTime *start, *end;
GCAL_ENTRY;
block_date_signals (self);
start = gcal_date_chooser_row_get_date (self->start_date_row);
end = gcal_date_chooser_row_get_date (self->end_date_row);
if (g_date_time_compare (start, end) == 1)
{
gcal_date_chooser_row_set_date (self->end_date_row, start);
gcal_date_time_chooser_set_date (self->end_date_time_chooser, start);
}
// Keep the date row and the date-time chooser in sync
gcal_date_time_chooser_set_date (self->start_date_time_chooser, start);
unblock_date_signals (self);
GCAL_EXIT;
}
static void
on_end_date_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalScheduleSection *self)
{
GDateTime *start, *end;
GCAL_ENTRY;
block_date_signals (self);
start = gcal_date_chooser_row_get_date (self->start_date_row);
end = gcal_date_chooser_row_get_date (self->end_date_row);
if (g_date_time_compare (start, end) == 1)
{
gcal_date_chooser_row_set_date (self->start_date_row, end);
gcal_date_time_chooser_set_date (self->start_date_time_chooser, end);
}
// Keep the date row and the date-time chooser in sync
gcal_date_time_chooser_set_date (self->end_date_time_chooser, end);
unblock_date_signals (self);
GCAL_EXIT;
}
static void
on_start_date_time_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalScheduleSection *self)
{
GDateTime *start, *end;
GCAL_ENTRY;
block_date_signals (self);
start = gcal_date_time_chooser_get_date_time (self->start_date_time_chooser);
end = gcal_date_time_chooser_get_date_time (self->end_date_time_chooser);
if (g_date_time_compare (start, end) == 1)
{
GTimeZone *end_tz = g_date_time_get_timezone (end);
g_autoptr (GDateTime) new_end = g_date_time_add_hours (start, 1);
g_autoptr (GDateTime) new_end_in_end_tz = g_date_time_to_timezone (new_end, end_tz);
gcal_date_time_chooser_set_date_time (self->end_date_time_chooser, new_end_in_end_tz);
}
unblock_date_signals (self);
GCAL_EXIT;
}
static void
on_end_date_time_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalScheduleSection *self)
{
GDateTime *start, *end;
GCAL_ENTRY;
block_date_signals (self);
start = gcal_date_time_chooser_get_date_time (self->start_date_time_chooser);
end = gcal_date_time_chooser_get_date_time (self->end_date_time_chooser);
if (g_date_time_compare (start, end) == 1)
{
GTimeZone *start_tz = g_date_time_get_timezone (start);
g_autoptr (GDateTime) new_start = g_date_time_add_hours (end, -1);
g_autoptr (GDateTime) new_start_in_start_tz = g_date_time_to_timezone (new_start, start_tz);
gcal_date_time_chooser_set_date_time (self->start_date_time_chooser, new_start_in_start_tz);
}
unblock_date_signals (self);
GCAL_EXIT;
}
static void
@@ -329,8 +310,8 @@ on_time_format_changed_cb (GcalScheduleSection *self)
{
GcalTimeFormat time_format = gcal_context_get_time_format (self->context);
gcal_time_chooser_row_set_time_format (self->start_time_row, time_format);
gcal_time_chooser_row_set_time_format (self->end_time_row, time_format);
gcal_date_time_chooser_set_time_format (self->start_date_time_chooser, time_format);
gcal_date_time_chooser_set_time_format (self->end_date_time_chooser, time_format);
}
@@ -338,18 +319,33 @@ on_time_format_changed_cb (GcalScheduleSection *self)
* GcalEventEditorSection interface
*/
/*
* Sets the event of the schedule section.
*
* If the event is not a new event, the event date start and date end timezones are kept and displayed to the user.
*
* If the event is a new event, the event date start and date end timezones are forgotten. The results of
* g_date_time_get_year, g_date_time_get_month, g_date_time_get_day_of_month, g_date_time_get_hour and
* g_date_time_get_minute are used for the date row and the date time chooser.
* The date row timezone is set to UTC (as all day events use the UTC timezone).
* The date time chooser timezone is set to local.
*/
static void
gcal_schedule_section_set_event (GcalEventEditorSection *section,
GcalEvent *event,
GcalEventEditorFlags flags)
{
g_autoptr (GDateTime) date_start = NULL;
g_autoptr (GDateTime) date_end = NULL;
g_autoptr (GDateTime) date_time_start_utc = NULL;
g_autoptr (GDateTime) date_time_end_utc = NULL;
g_autoptr (GDateTime) date_time_start_local = NULL;
g_autoptr (GDateTime) date_time_end_local = NULL;
GcalScheduleSection *self;
GDateTime* date_time_start = NULL;
GDateTime* date_time_end = NULL;
GcalRecurrenceLimitType limit_type;
GcalRecurrenceFrequency frequency;
GcalScheduleSection *self;
GcalRecurrence *recur;
gboolean all_day;
gboolean all_day, new_event;
GCAL_ENTRY;
@@ -362,44 +358,69 @@ gcal_schedule_section_set_event (GcalEventEditorSection *section,
GCAL_RETURN ();
all_day = gcal_event_get_all_day (event);
new_event = flags & GCAL_EVENT_EDITOR_FLAG_NEW_EVENT;
/* schedule type */
adw_toggle_group_set_active_name (self->schedule_type_toggle_group,
all_day ? "all-day" : "time-slot");
gtk_widget_set_visible (GTK_WIDGET (self->start_time_row), !all_day);
gtk_widget_set_visible (GTK_WIDGET (self->end_time_row), !all_day);
gtk_widget_set_visible (GTK_WIDGET (self->start_date_group), all_day);
gtk_widget_set_visible (GTK_WIDGET (self->end_date_group), all_day);
gtk_widget_set_visible (GTK_WIDGET (self->start_date_time_chooser), !all_day);
gtk_widget_set_visible (GTK_WIDGET (self->end_date_time_chooser), !all_day);
/* retrieve start and end dates */
date_start = gcal_event_get_date_start (event);
date_start = all_day ? g_date_time_ref (date_start) : g_date_time_to_local (date_start);
date_end = gcal_event_get_date_end (event);
/* retrieve start and end date-times */
date_time_start = gcal_event_get_date_start (event);
date_time_end = gcal_event_get_date_end (event);
/*
* This is subtracting what has been added in action_button_clicked ().
* See bug 769300.
*/
date_end = all_day ? g_date_time_add_days (date_end, -1) : g_date_time_to_local (date_end);
date_time_end = all_day ? g_date_time_add_days (date_time_end, -1) : date_time_end;
date_time_start_utc = g_date_time_new_utc (g_date_time_get_year (date_time_start),
g_date_time_get_month (date_time_start),
g_date_time_get_day_of_month (date_time_start),
0, 0, 0);
date_time_end_utc = g_date_time_new_utc (g_date_time_get_year (date_time_end),
g_date_time_get_month (date_time_end),
g_date_time_get_day_of_month (date_time_end),
0, 0, 0);
date_time_start_local = g_date_time_new_local (g_date_time_get_year (date_time_start),
g_date_time_get_month (date_time_start),
g_date_time_get_day_of_month (date_time_start),
g_date_time_get_hour (date_time_start),
g_date_time_get_minute (date_time_start),
0);
date_time_end_local = g_date_time_new_local (g_date_time_get_year (date_time_end),
g_date_time_get_month (date_time_end),
g_date_time_get_day_of_month (date_time_end),
g_date_time_get_hour (date_time_end),
g_date_time_get_minute (date_time_end),
0);
block_date_signals (self);
/* date */
g_signal_handlers_block_by_func (self->end_date_row, sync_datetimes, self);
g_signal_handlers_block_by_func (self->start_date_row, sync_datetimes, self);
gcal_date_chooser_row_set_date (self->start_date_row, date_time_start_utc);
gcal_date_chooser_row_set_date (self->end_date_row, date_time_end_utc);
gcal_date_chooser_row_set_date (self->start_date_row, date_start);
gcal_date_chooser_row_set_date (self->end_date_row, date_end);
/* date-time */
if (new_event || all_day)
{
gcal_date_time_chooser_set_date_time (self->start_date_time_chooser, date_time_start_local);
gcal_date_time_chooser_set_date_time (self->end_date_time_chooser, date_time_end_local);
}
else
{
gcal_date_time_chooser_set_date_time (self->start_date_time_chooser, date_time_start);
gcal_date_time_chooser_set_date_time (self->end_date_time_chooser, date_time_end);
}
g_signal_handlers_unblock_by_func (self->start_date_row, sync_datetimes, self);
g_signal_handlers_unblock_by_func (self->end_date_row, sync_datetimes, self);
/* time */
g_signal_handlers_block_by_func (self->end_time_row, sync_datetimes, self);
g_signal_handlers_block_by_func (self->start_time_row, sync_datetimes, self);
gcal_time_chooser_row_set_time (self->start_time_row, date_start);
gcal_time_chooser_row_set_time (self->end_time_row, date_end);
g_signal_handlers_unblock_by_func (self->start_time_row, sync_datetimes, self);
g_signal_handlers_unblock_by_func (self->end_time_row, sync_datetimes, self);
unblock_date_signals (self);
/* Recurrences */
recur = gcal_event_get_recurrence (event);
@@ -440,29 +461,28 @@ gcal_schedule_section_set_event (GcalEventEditorSection *section,
static void
gcal_schedule_section_apply (GcalEventEditorSection *section)
{
g_autoptr (GDateTime) start_date = NULL;
g_autoptr (GDateTime) end_date = NULL;
GDateTime *start_date, *end_date;
GcalRecurrenceFrequency freq;
GcalScheduleSection *self;
GcalRecurrence *old_recur;
gboolean was_all_day;
gboolean all_day;
GCAL_ENTRY;
self = GCAL_SCHEDULE_SECTION (section);
all_day = all_day_selected (self);
was_all_day = gcal_event_get_all_day (self->event);
if (!was_all_day && all_day)
gcal_event_save_original_timezones (self->event);
/*
* Update start & end dates. The dates are already translated to the current
* timezone (unless the event used to be all day, but no longer is).
*/
start_date = get_date_start (self);
end_date = get_date_end (self);
if (!all_day)
{
start_date = gcal_date_time_chooser_get_date_time (self->start_date_time_chooser);
end_date = gcal_date_time_chooser_get_date_time (self->end_date_time_chooser);
}
else
{
start_date = gcal_date_chooser_row_get_date (self->start_date_row);
end_date = gcal_date_chooser_row_get_date (self->end_date_row);
}
#ifdef GCAL_ENABLE_TRACE
{
@@ -485,26 +505,8 @@ gcal_schedule_section_apply (GcalEventEditorSection *section)
{
GDateTime *fake_end_date = g_date_time_add_days (end_date, 1);
g_clear_pointer (&end_date, g_date_time_unref);
end_date = fake_end_date;
}
else if (!all_day && was_all_day)
{
/* When an all day event is changed to be not an all day event, we
* need to correct for the fact that the event's timezone was until
* now set to UTC. That means we need to change the timezone to
* localtime now, or else it will be saved incorrectly.
*/
GDateTime *localtime_date;
localtime_date = g_date_time_to_local (start_date);
g_clear_pointer (&start_date, g_date_time_unref);
start_date = localtime_date;
localtime_date = g_date_time_to_local (end_date);
g_clear_pointer (&end_date, g_date_time_unref);
end_date = localtime_date;
}
gcal_event_set_date_start (self->event, start_date);
gcal_event_set_date_end (self->event, end_date);
@@ -548,9 +550,9 @@ gcal_schedule_section_apply (GcalEventEditorSection *section)
static gboolean
gcal_schedule_section_changed (GcalEventEditorSection *section)
{
g_autoptr (GDateTime) start_date = NULL;
g_autoptr (GDateTime) end_date = NULL;
GcalScheduleSection *self;
GDateTime *start_date, *end_date, *prev_start_date, *prev_end_date;
GTimeZone *start_tz, *end_tz, *prev_start_tz, *prev_end_tz;
gboolean was_all_day;
gboolean all_day;
@@ -564,38 +566,41 @@ gcal_schedule_section_changed (GcalEventEditorSection *section)
if (all_day != was_all_day)
GCAL_RETURN (TRUE);
if (!all_day)
{
start_date = gcal_date_time_chooser_get_date_time (self->start_date_time_chooser);
end_date = gcal_date_time_chooser_get_date_time (self->end_date_time_chooser);
}
else
{
start_date = gcal_date_chooser_row_get_date (self->start_date_row);
end_date = gcal_date_chooser_row_get_date (self->end_date_row);
}
prev_start_date = gcal_event_get_date_start (self->event);
prev_end_date = gcal_event_get_date_end (self->event);
start_tz = g_date_time_get_timezone (start_date);
end_tz = g_date_time_get_timezone (end_date);
prev_start_tz = g_date_time_get_timezone (prev_start_date);
prev_end_tz = g_date_time_get_timezone (prev_end_date);
/* Start date */
start_date = get_date_start (self);
if (!g_date_time_equal (start_date, gcal_event_get_date_start (self->event)))
if (!g_date_time_equal (start_date, prev_start_date))
GCAL_RETURN (TRUE);
if (g_strcmp0 (g_time_zone_get_identifier (start_tz), g_time_zone_get_identifier (prev_start_tz)) != 0)
GCAL_RETURN (TRUE);
/* End date */
end_date = get_date_end (self);
if (all_day)
{
g_autoptr (GDateTime) fake_end_date = g_date_time_add_days (end_date, 1);
gcal_set_date_time (&end_date, fake_end_date);
}
else if (!all_day && was_all_day)
{
/* When an all day event is changed to be not an all day event, we
* need to correct for the fact that the event's timezone was until
* now set to UTC. That means we need to change the timezone to
* localtime now, or else it will be saved incorrectly.
*/
GDateTime *localtime_date;
localtime_date = g_date_time_to_local (start_date);
g_clear_pointer (&start_date, g_date_time_unref);
start_date = localtime_date;
localtime_date = g_date_time_to_local (end_date);
g_clear_pointer (&end_date, g_date_time_unref);
end_date = localtime_date;
GDateTime *fake_end_date = g_date_time_add_days (end_date, 1);
end_date = fake_end_date;
}
if (!g_date_time_equal (end_date, gcal_event_get_date_end (self->event)))
GCAL_RETURN (TRUE);
if (g_strcmp0 (g_time_zone_get_identifier (end_tz), g_time_zone_get_identifier (prev_end_tz)) != 0)
GCAL_RETURN (TRUE);
/* Recurrency */
GCAL_RETURN (gcal_schedule_section_recurrence_changed (self));
@@ -676,6 +681,8 @@ gcal_schedule_section_class_init (GcalScheduleSectionClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
g_type_ensure (GCAL_TYPE_DATE_TIME_CHOOSER);
object_class->finalize = gcal_schedule_section_finalize;
object_class->get_property = gcal_schedule_section_get_property;
object_class->set_property = gcal_schedule_section_set_property;
@@ -685,19 +692,23 @@ gcal_schedule_section_class_init (GcalScheduleSectionClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/event-editor/gcal-schedule-section.ui");
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, schedule_type_toggle_group);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, start_date_group);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, start_date_row);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, start_time_row);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, end_date_group);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, end_date_row);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, end_time_row);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, start_date_time_chooser);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, end_date_time_chooser);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, number_of_occurrences_spin);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, repeat_combo);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, repeat_duration_combo);
gtk_widget_class_bind_template_child (widget_class, GcalScheduleSection, until_date_selector);
gtk_widget_class_bind_template_callback (widget_class, on_schedule_type_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_start_date_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_end_date_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_start_date_time_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_end_date_time_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_repeat_duration_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_repeat_type_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, sync_datetimes);
}
static void
@@ -705,7 +716,10 @@ gcal_schedule_section_init (GcalScheduleSection *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
on_time_format_changed_cb (self);
g_signal_connect (self->schedule_type_toggle_group,
"notify::active",
G_CALLBACK (on_schedule_type_changed_cb),
self);
}
gboolean
@@ -734,15 +748,25 @@ gcal_schedule_section_recurrence_changed (GcalScheduleSection *self)
gboolean
gcal_schedule_section_day_changed (GcalScheduleSection *self)
{
g_autoptr (GDateTime) start_date = NULL;
g_autoptr (GDateTime) end_date = NULL;
GDateTime *start_date, *end_date;
gboolean all_day;
g_return_val_if_fail (GCAL_IS_SCHEDULE_SECTION (self), FALSE);
GCAL_ENTRY;
start_date = get_date_start (self);
end_date = get_date_end (self);
all_day = all_day_selected (self);
if (all_day)
{
start_date = gcal_date_time_chooser_get_date_time (self->start_date_time_chooser);
end_date = gcal_date_time_chooser_get_date_time (self->end_date_time_chooser);
}
else
{
start_date = gcal_date_chooser_row_get_date (self->start_date_row);
end_date = gcal_date_chooser_row_get_date (self->end_date_row);
}
GCAL_RETURN (gcal_date_time_compare_date (start_date, gcal_event_get_date_start (self->event)) < 0 ||
gcal_date_time_compare_date (end_date, gcal_event_get_date_end (self->event)) > 0);
+32 -47
View File
@@ -21,61 +21,46 @@
<property name="name">time-slot</property>
</object>
</child>
<signal name="notify::active" handler="on_schedule_type_changed_cb" swapped="no"/>
</object>
</child>
<!-- Start date -->
<child>
<object class="AdwPreferencesGroup" id="start_date_group">
<child>
<object class="GcalDateChooserRow" id="start_date_row">
<property name="title" translatable="yes">Start Date</property>
<signal name="notify::date" handler="on_start_date_changed_cb" object="GcalScheduleSection" swapped="no" />
</object>
</child>
</object>
</child>
<!-- End date -->
<child>
<object class="AdwPreferencesGroup" id="end_date_group">
<child>
<object class="GcalDateChooserRow" id="end_date_row">
<property name="title" translatable="yes">End Date</property>
<signal name="notify::date" handler="on_end_date_changed_cb" object="GcalScheduleSection" swapped="no" />
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="homogeneous">True</property>
<property name="spacing">12</property>
<!-- Start date -->
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="GcalDateChooserRow" id="start_date_row">
<property name="title" translatable="yes">Start Date</property>
<signal name="notify::date" handler="sync_datetimes" object="GcalScheduleSection" swapped="yes" />
</object>
</child>
<child>
<object class="GcalTimeChooserRow" id ="start_time_row">
<property name="title" translatable="yes">Start Time</property>
<signal name="notify::time" handler="sync_datetimes" object="GcalScheduleSection" swapped="yes" />
</object>
</child>
</object>
</child>
<object class="GcalDateTimeChooser" id="start_date_time_chooser">
<property name="date-label" translatable="yes">Start Date</property>
<property name="time-label" translatable="yes">Start Time</property>
<signal name="notify::date-time" handler="on_start_date_time_changed_cb" object="GcalScheduleSection" swapped="no" />
</object>
</child>
<child>
<object class="GtkBox">
<property name="homogeneous">True</property>
<property name="spacing">12</property>
<!-- End date -->
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="GcalDateChooserRow" id="end_date_row">
<property name="title" translatable="yes">End Date</property>
<signal name="notify::date" handler="sync_datetimes" object="GcalScheduleSection" swapped="yes" />
</object>
</child>
<child>
<object class="GcalTimeChooserRow" id ="end_time_row">
<property name="title" translatable="yes">End Time</property>
<signal name="notify::time" handler="sync_datetimes" object="GcalScheduleSection" swapped="yes" />
</object>
</child>
</object>
</child>
<object class="GcalDateTimeChooser" id="end_date_time_chooser">
<property name="date-label" translatable="yes">End Date</property>
<property name="time-label" translatable="yes">End Time</property>
<signal name="notify::date-time" handler="on_end_date_time_changed_cb" object="GcalScheduleSection" swapped="no" />
</object>
</child>
@@ -1,399 +0,0 @@
/* gcal-time-chooser-row.c
*
* Copyright (C) 2024 Titouan Real <titouan.real@gmail.com>
*
* gnome-calendar 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.
*
* gnome-calendar 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 "GcalTimeChooserRow"
#include "gcal-time-chooser-row.h"
#include "gcal-time-selector.h"
#include <locale.h>
#include <langinfo.h>
#include <stdlib.h>
#include <glib/gi18n.h>
struct _GcalTimeChooserRow
{
AdwEntryRow parent;
/* widgets */
GcalTimeSelector *time_selector;
GcalTimeFormat time_format;
};
G_DEFINE_TYPE (GcalTimeChooserRow, gcal_time_chooser_row, ADW_TYPE_ENTRY_ROW);
enum
{
PROP_0,
PROP_TIME_FORMAT,
PROP_TIME,
N_PROPS
};
static GParamSpec* properties[N_PROPS] = { NULL, };
/*
* Auxiliary methods
*/
static void
update_entry (GcalTimeChooserRow *self)
{
g_autofree gchar *label = NULL;
GDateTime *time;
time = gcal_time_selector_get_time (self->time_selector);
switch (self->time_format)
{
case GCAL_TIME_FORMAT_12H:
label = g_date_time_format (time, "%I:%M %p");
break;
case GCAL_TIME_FORMAT_24H:
label = g_date_time_format (time, "%R");
break;
default:
g_assert_not_reached ();
}
gtk_editable_set_text (GTK_EDITABLE (self), label);
}
static GRegex*
get_12h_regex (void)
{
static GRegex *regex = NULL;
if (g_once_init_enter_pointer (&regex))
{
g_autoptr (GError) error = NULL;
g_autofree gchar *str = NULL;
str = g_strdup_printf ("^(?<hour>[0-9]{1,2})([\\-:./_,'a-z](?<minute>[0-9]{1,2}))? ?(?<ampm>%s|%s)",
_("AM"), _("PM"));
regex = g_regex_new (str, G_REGEX_CASELESS, 0, &error);
if (error)
g_error ("Failed to compile regex: %s. Aborting...", error->message);
}
return regex;
}
static GRegex*
get_24h_regex (void)
{
static GRegex *regex = NULL;
if (g_once_init_enter_pointer (&regex))
{
g_autoptr (GError) error = NULL;
regex = g_regex_new ("^(?<hour>[0-9]{1,2})([\\-:./_,'a-z](?<minute>[0-9]{1,2}))?", G_REGEX_CASELESS, 0, &error);
if (error)
g_error ("Failed to compile regex: %s. Aborting...", error->message);
}
return regex;
}
static void
parse_time (GcalTimeChooserRow *self)
{
g_autoptr (GDateTime) now = NULL;
const gchar *text;
text = gtk_editable_get_text (GTK_EDITABLE (self));
now = g_date_time_new_now_local ();
switch (self->time_format)
{
case GCAL_TIME_FORMAT_12H:
{
g_autoptr (GMatchInfo) match_info = NULL;
g_autoptr (GDateTime) time = NULL;
g_autofree gchar *minute_str = NULL;
g_autofree gchar *hour_str = NULL;
g_autofree gchar *ampm_str = NULL;
gboolean am;
gint64 hour, minute;
if (!g_regex_match (get_12h_regex (), text, 0, &match_info))
{
update_entry (self);
break;
}
hour_str = g_match_info_fetch_named (match_info, "hour");
minute_str = g_match_info_fetch_named (match_info, "minute");
ampm_str = g_match_info_fetch_named (match_info, "ampm");
hour = g_ascii_strtoll (hour_str, NULL, 10);
minute = g_ascii_strtoll (minute_str, NULL, 10);
am = (strcmp (g_ascii_strup (ampm_str, -1), _("AM")) == 0);
/* Allow 12AM and 12PM */
if ((hour > 11 || minute > 59) && !(hour == 12 && minute == 0))
{
update_entry (self);
break;
}
if (hour == 12)
{
g_assert (minute == 0);
hour = 0;
}
if (!am)
hour += 12;
now = g_date_time_new_now_local ();
time = g_date_time_new_local (g_date_time_get_year (now),
g_date_time_get_month (now),
g_date_time_get_day_of_month (now),
hour, minute, 0);
gcal_time_chooser_row_set_time (self, time);
}
break;
case GCAL_TIME_FORMAT_24H:
{
g_autoptr (GMatchInfo) match_info = NULL;
g_autoptr (GDateTime) time = NULL;
g_autofree gchar *minute_str = NULL;
g_autofree gchar *hour_str = NULL;
g_autofree gchar *ampm_str = NULL;
gint64 hour, minute;
if (!g_regex_match (get_24h_regex (), text, 0, &match_info))
{
update_entry (self);
break;
}
hour_str = g_match_info_fetch_named (match_info, "hour");
minute_str = g_match_info_fetch_named (match_info, "minute");
hour = g_ascii_strtoll (hour_str, NULL, 10);
minute = g_ascii_strtoll (minute_str, NULL, 10);
if (hour > 23 || minute > 59)
{
update_entry (self);
break;
}
time = g_date_time_new_local (g_date_time_get_year (now),
g_date_time_get_month (now),
g_date_time_get_day_of_month (now),
hour, minute, 0);
gcal_time_chooser_row_set_time (self, time);
}
break;
default:
g_assert_not_reached ();
}
}
/*
* Callbacks
*/
static void
on_contains_focus_changed_cb (GtkEventControllerFocus *focus_controller,
GParamSpec *pspec,
GcalTimeChooserRow *self)
{
parse_time (self);
}
static void
on_time_selected_changed_cb (GcalTimeSelector *selector,
GParamSpec *pspec,
GcalTimeChooserRow *self)
{
update_entry (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TIME]);
}
static void
on_time_popover_shown_cb (GtkPopover *popover,
GcalTimeChooserRow *self)
{
parse_time (self);
}
/*
* GObject overrides
*/
static void
gcal_time_chooser_row_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GcalTimeChooserRow *self = GCAL_TIME_CHOOSER_ROW (object);
switch (prop_id)
{
case PROP_TIME_FORMAT:
g_value_set_enum (value, self->time_format);
break;
case PROP_TIME:
g_value_set_boxed (value, gcal_time_chooser_row_get_time (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_time_chooser_row_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GcalTimeChooserRow *self = GCAL_TIME_CHOOSER_ROW (object);
switch (prop_id)
{
case PROP_TIME_FORMAT:
gcal_time_chooser_row_set_time_format (self, g_value_get_enum (value));
break;
case PROP_TIME:
gcal_time_chooser_row_set_time (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_time_chooser_row_class_init (GcalTimeChooserRowClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gcal_time_chooser_row_get_property;
object_class->set_property = gcal_time_chooser_row_set_property;
properties[PROP_TIME_FORMAT] = g_param_spec_enum ("time-format", NULL, NULL,
GCAL_TYPE_TIME_FORMAT,
GCAL_TIME_FORMAT_24H,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_TIME] = g_param_spec_boxed ("time", NULL, NULL,
G_TYPE_DATE_TIME,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | 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/ui/event-editor/gcal-time-chooser-row.ui");
gtk_widget_class_bind_template_child (widget_class, GcalTimeChooserRow, time_selector);
gtk_widget_class_bind_template_callback (widget_class, on_contains_focus_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_time_selected_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_time_popover_shown_cb);
}
static void
gcal_time_chooser_row_init (GcalTimeChooserRow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
self->time_format = GCAL_TIME_FORMAT_24H;
}
GcalTimeFormat
gcal_time_chooser_row_get_time_format (GcalTimeChooserRow *self)
{
g_return_val_if_fail (GCAL_IS_TIME_CHOOSER_ROW (self), 0);
return self->time_format;
}
void
gcal_time_chooser_row_set_time_format (GcalTimeChooserRow *self,
GcalTimeFormat time_format)
{
g_return_if_fail (GCAL_IS_TIME_CHOOSER_ROW (self));
if (self->time_format == time_format)
return;
self->time_format = time_format;
gcal_time_selector_set_time_format (self->time_selector, time_format);
update_entry (self);
}
/**
* gcal_time_chooser_row_set_time:
* @selector: a #GcalTimeChooserRow
* @date: a valid #GDateTime
*
* Set the value of the shown time.
*/
void
gcal_time_chooser_row_set_time (GcalTimeChooserRow *self,
GDateTime *time)
{
g_return_if_fail (GCAL_IS_TIME_CHOOSER_ROW (self));
gcal_time_selector_set_time (self->time_selector, time);
update_entry (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TIME]);
}
/**
* gcal_time_chooser_row_get_time:
* @selector: a #GcalTimeChooserRow
*
* Get the value of the time shown
*
* Returns: (transfer none): the time of the selector.
*/
GDateTime*
gcal_time_chooser_row_get_time (GcalTimeChooserRow *self)
{
g_return_val_if_fail (GCAL_IS_TIME_CHOOSER_ROW (self), NULL);
return gcal_time_selector_get_time (self->time_selector);
}
-295
View File
@@ -1,295 +0,0 @@
/* gcal-time-selector.c
*
* Copyright (C) 2015 Erick Pérez Castellanos <erickpc@gnome.org>
* 2014 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* gnome-calendar 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.
*
* gnome-calendar 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 "GcalTimeSelector"
#include "gcal-date-time-utils.h"
#include "gcal-time-selector.h"
#include <glib/gi18n.h>
struct _GcalTimeSelector
{
GtkBox parent;
GtkAdjustment *hour_adjustment;
GtkAdjustment *minute_adjustment;
GtkWidget *time_box;
GtkWidget *hour_spin;
GtkWidget *minute_spin;
GtkWidget *period_toggle_group;
GDateTime *time;
GcalTimeFormat time_format;
};
enum
{
PROP_0,
PROP_TIME,
LAST_PROP
};
G_DEFINE_TYPE (GcalTimeSelector, gcal_time_selector, GTK_TYPE_BOX);
static void
update_time (GcalTimeSelector *selector)
{
GDateTime *now, *new_time;
gint hour, minute;
/* Retrieve current time */
hour = (gint) gtk_adjustment_get_value (selector->hour_adjustment);
minute = (gint) gtk_adjustment_get_value (selector->minute_adjustment);
if (selector->time_format == GCAL_TIME_FORMAT_12H)
{
hour = hour % 12;
if (g_strcmp0 (adw_toggle_group_get_active_name (ADW_TOGGLE_GROUP (selector->period_toggle_group)), "pm") == 0)
hour += 12;
}
now = g_date_time_new_now_local ();
new_time = g_date_time_new_local (g_date_time_get_year (now),
g_date_time_get_month (now),
g_date_time_get_day_of_month (now),
hour, minute, 0);
/* Set the new time */
gcal_time_selector_set_time (selector, new_time);
g_clear_pointer (&new_time, g_date_time_unref);
g_clear_pointer (&now, g_date_time_unref);
}
static void
on_period_toggle_changed_cb (GtkWidget *widget,
GParamSpec *pspec,
GcalTimeSelector *selector)
{
update_time (selector);
}
static gboolean
on_output (GtkWidget *widget,
GcalTimeSelector *selector)
{
GtkAdjustment *adjustment;
gchar *text;
gint value;
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget));
value = (gint) gtk_adjustment_get_value (adjustment);
text = g_strdup_printf ("%02d", value);
gtk_editable_set_text (GTK_EDITABLE (widget), text);
g_free (text);
return TRUE;
}
static void
gcal_time_selector_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GcalTimeSelector *self = (GcalTimeSelector*) object;
switch (prop_id)
{
case PROP_TIME:
g_value_set_boxed (value, self->time);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_time_selector_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GcalTimeSelector *self = (GcalTimeSelector*) object;
switch (prop_id)
{
case PROP_TIME:
gcal_time_selector_set_time (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
void
gcal_time_selector_set_time_format (GcalTimeSelector *selector,
GcalTimeFormat time_format)
{
g_return_if_fail (GCAL_IS_TIME_SELECTOR (selector));
selector->time_format = time_format;
gtk_widget_set_visible (selector->period_toggle_group, time_format == GCAL_TIME_FORMAT_12H);
if (time_format == GCAL_TIME_FORMAT_24H)
{
gtk_adjustment_set_lower (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (selector->hour_spin)), 0.0);
gtk_adjustment_set_upper (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (selector->hour_spin)), 23.0);
}
else
{
gtk_adjustment_set_lower (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (selector->hour_spin)), 1.0);
gtk_adjustment_set_upper (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (selector->hour_spin)), 12.0);
}
}
static void
gcal_time_selector_finalize (GObject *object)
{
GcalTimeSelector *self = GCAL_TIME_SELECTOR (object);
g_clear_pointer (&self->time, g_date_time_unref);
G_OBJECT_CLASS (gcal_time_selector_parent_class)->finalize (object);
}
static void
gcal_time_selector_dispose (GObject *object)
{
G_OBJECT_CLASS (gcal_time_selector_parent_class)->dispose (object);
}
static void
gcal_time_selector_class_init (GcalTimeSelectorClass *klass)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gcal_time_selector_dispose;
object_class->finalize = gcal_time_selector_finalize;
object_class->get_property = gcal_time_selector_get_property;
object_class->set_property = gcal_time_selector_set_property;
/**
* GcalTimeSelector::time:
*
* The current time of the selector.
*/
g_object_class_install_property (object_class,
PROP_TIME,
g_param_spec_boxed ("time",
"Time of the selector",
"The current time of the selector",
G_TYPE_DATE_TIME,
G_PARAM_READWRITE));
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/calendar/ui/event-editor/gcal-time-selector.ui");
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GcalTimeSelector, time_box);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GcalTimeSelector, hour_adjustment);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GcalTimeSelector, hour_spin);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GcalTimeSelector, minute_adjustment);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GcalTimeSelector, minute_spin);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GcalTimeSelector, period_toggle_group);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), on_output);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), update_time);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), on_period_toggle_changed_cb);
}
static void
gcal_time_selector_init (GcalTimeSelector *self)
{
self->time = g_date_time_new_now_local ();
gtk_widget_init_template (GTK_WIDGET (self));
if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
gtk_widget_set_direction (self->time_box, GTK_TEXT_DIR_LTR);
}
/* Public API */
GtkWidget*
gcal_time_selector_new (void)
{
return g_object_new (GCAL_TYPE_TIME_SELECTOR, NULL);
}
void
gcal_time_selector_set_time (GcalTimeSelector *selector,
GDateTime *time)
{
g_return_if_fail (GCAL_IS_TIME_SELECTOR (selector));
if (selector->time != time)
{
gint hour, minute;
if (selector->time &&
time &&
g_date_time_get_hour (selector->time) == g_date_time_get_hour (time) &&
g_date_time_get_minute (selector->time) == g_date_time_get_minute (time) &&
g_date_time_get_second (selector->time) == g_date_time_get_second (time))
{
return;
}
gcal_set_date_time (&selector->time, time);
/* Update the spinners */
g_signal_handlers_block_by_func (selector->hour_adjustment, update_time, selector);
g_signal_handlers_block_by_func (selector->minute_adjustment, update_time, selector);
hour = g_date_time_get_hour (time);
minute = g_date_time_get_minute (time);
if (selector->time_format == GCAL_TIME_FORMAT_12H)
{
g_signal_handlers_block_by_func (selector->period_toggle_group, on_period_toggle_changed_cb, selector);
adw_toggle_group_set_active_name (ADW_TOGGLE_GROUP (selector->period_toggle_group), hour >= 12 ? "pm" : "am");
hour = hour % 12;
hour = (hour == 0)? 12 : hour;
g_signal_handlers_unblock_by_func (selector->period_toggle_group, on_period_toggle_changed_cb, selector);
}
gtk_adjustment_set_value (selector->hour_adjustment, hour);
gtk_adjustment_set_value (selector->minute_adjustment, minute);
g_signal_handlers_unblock_by_func (selector->hour_adjustment, update_time, selector);
g_signal_handlers_unblock_by_func (selector->minute_adjustment, update_time, selector);
g_object_notify (G_OBJECT (selector), "time");
}
}
GDateTime*
gcal_time_selector_get_time (GcalTimeSelector *selector)
{
g_return_val_if_fail (GCAL_IS_TIME_SELECTOR (selector), NULL);
return selector->time;
}
-43
View File
@@ -1,43 +0,0 @@
/* gcal-time-selector.h
*
* Copyright (C) 2015 Erick Pérez Castellanos <erickpc@gnome.org>
*
* gnome-calendar 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.
*
* gnome-calendar 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/>.
*/
#ifndef __GCAL_TIME_SELECTOR_H__
#define __GCAL_TIME_SELECTOR_H__
#include "gcal-enums.h"
#include <adwaita.h>
G_BEGIN_DECLS
#define GCAL_TYPE_TIME_SELECTOR (gcal_time_selector_get_type ())
G_DECLARE_FINAL_TYPE (GcalTimeSelector, gcal_time_selector, GCAL, TIME_SELECTOR, GtkBox)
GtkWidget* gcal_time_selector_new (void);
void gcal_time_selector_set_time_format (GcalTimeSelector *selector,
GcalTimeFormat time_format);
GDateTime* gcal_time_selector_get_time (GcalTimeSelector *selector);
void gcal_time_selector_set_time (GcalTimeSelector *selector,
GDateTime *time);
G_END_DECLS
#endif /* __GCAL_TIME_SELECTOR_H__ */
@@ -1,74 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GcalTimeSelector" parent="GtkBox">
<property name="spacing">6</property>
<child>
<object class="GtkBox" id="time_box">
<property name="spacing">6</property>
<!-- Hour -->
<child>
<object class="GtkSpinButton" id="hour_spin">
<property name="xalign">0.5</property>
<property name="orientation">vertical</property>
<property name="adjustment">hour_adjustment</property>
<signal name="output" handler="on_output" object="GcalTimeSelector" swapped="no"/>
</object>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="label" translatable="yes">:</property>
</object>
</child>
<!-- Minute -->
<child>
<object class="GtkSpinButton" id="minute_spin">
<property name="xalign">0.5</property>
<property name="orientation">vertical</property>
<property name="adjustment">minute_adjustment</property>
<signal name="output" handler="on_output" object="GcalTimeSelector" swapped="no"/>
</object>
</child>
</object>
</child>
<!-- AM/PM -->
<child>
<object class="AdwToggleGroup" id="period_toggle_group">
<property name="visible">False</property>
<property name="valign">center</property>
<property name="active-name">am</property>
<property name="orientation">vertical</property>
<child>
<object class="AdwToggle">
<property name="label" translatable="yes">AM</property>
<property name="name">am</property>
</object>
</child>
<child>
<object class="AdwToggle">
<property name="label" translatable="yes">PM</property>
<property name="name">pm</property>
</object>
</child>
<signal name="notify::active" handler="on_period_toggle_changed_cb" swapped="no"/>
</object>
</child>
</template>
<object class="GtkAdjustment" id="hour_adjustment">
<property name="upper">23</property>
<property name="step_increment">1</property>
<property name="page_increment">5</property>
<signal name="value-changed" handler="update_time" object="GcalTimeSelector" swapped="yes"/>
</object>
<object class="GtkAdjustment" id="minute_adjustment">
<property name="upper">59</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
<signal name="value-changed" handler="update_time" object="GcalTimeSelector" swapped="yes"/>
</object>
</interface>
@@ -0,0 +1,89 @@
/* gcal-time-zone-dialog-row.c
*
* Copyright (C) 2024 Titouan Real <titouan.real@gmail.com>
*
* gnome-calendar 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.
*
* gnome-calendar 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/>.
*/
#include "config.h"
#define G_LOG_DOMAIN "GcalTimeZoneDialogRow"
#include "gcal-time-zone-dialog-row.h"
struct _GcalTimeZoneDialogRow
{
AdwActionRow parent;
GWeatherLocation *location;
};
G_DEFINE_TYPE (GcalTimeZoneDialogRow, gcal_time_zone_dialog_row, ADW_TYPE_ACTION_ROW)
/*
* Auxiliary methods
*/
/*
* Callbacks
*/
/*
* Gobject overrides
*/
static void
gcal_time_zone_dialog_row_class_init (GcalTimeZoneDialogRowClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/event-editor/gcal-time-zone-dialog-row.ui");
}
static void
gcal_time_zone_dialog_row_init (GcalTimeZoneDialogRow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
/**
* gcal_time_zone_dialog_row_new:
*
* Creates a new #GcalTimeZoneDialogRow
*
* Returns: (transfer full): a #GcalTimeZoneDialogRow
*/
GtkWidget*
gcal_time_zone_dialog_row_new (GWeatherLocation *location)
{
GtkWidget *widget = g_object_new (GCAL_TYPE_TIME_ZONE_DIALOG_ROW, NULL);
(GCAL_TIME_ZONE_DIALOG_ROW (widget))->location = g_object_ref (location);
return widget;
}
/**
* gcal_time_zone_dialog_row_get_location:
* @self: a #GcalTimeZoneDialogRow
*
* Returns: (transfer none): a #GWeatherLocation
*/
GWeatherLocation*
gcal_time_zone_dialog_row_get_location (GcalTimeZoneDialogRow *self)
{
return self->location;
}
@@ -0,0 +1,34 @@
/* gcal-time-zone-dialog-row.h
*
* Copyright (C) 2024 Titouan Real <titouan.real@gmail.com>
*
* gnome-calendar 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.
*
* gnome-calendar 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/>.
*/
#pragma once
#include <adwaita.h>
#include "libgweather/gweather.h"
G_BEGIN_DECLS
#define GCAL_TYPE_TIME_ZONE_DIALOG_ROW (gcal_time_zone_dialog_row_get_type ())
G_DECLARE_FINAL_TYPE (GcalTimeZoneDialogRow, gcal_time_zone_dialog_row, GCAL, TIME_ZONE_DIALOG_ROW, AdwActionRow);
GtkWidget* gcal_time_zone_dialog_row_new (GWeatherLocation *location);
GWeatherLocation* gcal_time_zone_dialog_row_get_location (GcalTimeZoneDialogRow *self);
G_END_DECLS
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GcalTimeZoneDialogRow" parent="AdwActionRow">
<property name="activatable">true</property>
<style>
<class name="property"/>
</style>
</template>
</interface>
@@ -0,0 +1,313 @@
/* gcal-time-zone-dialog.c
*
* Copyright (C) 2024 Titouan Real <titouan.real@gmail.com>
*
* gnome-calendar 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.
*
* gnome-calendar 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/>.
*/
#include "config.h"
#define G_LOG_DOMAIN "GcalTimeZoneDialog"
#include "gcal-date-time-utils.h"
#include "gcal-debug.h"
#include "gcal-time-zone-dialog.h"
#include "gcal-time-zone-dialog-row.h"
#include <glib/gi18n.h>
#define RESULT_COUNT_LIMIT 12
struct _GcalTimeZoneDialog
{
AdwDialog parent;
GtkStack *stack;
AdwStatusPage *empty_search;
GtkScrolledWindow *search_results;
GtkSearchEntry *location_entry;
GtkListBox *listbox;
GListStore *locations;
GTimeZone *time_zone;
GDateTime *date_time;
};
G_DEFINE_TYPE (GcalTimeZoneDialog, gcal_time_zone_dialog, ADW_TYPE_DIALOG)
enum
{
TIME_ZONE_SELECTED,
N_SIGNALS,
};
static guint signals [N_SIGNALS] = { 0, };
/*
* Auxiliary methods
*/
static GtkWidget*
create_row (gpointer item,
gpointer user_data)
{
GcalTimeZoneDialog *self = (GcalTimeZoneDialog *) user_data;
g_autoptr (GDateTime) date_time = NULL;
g_autofree gchar *country_name = NULL;
g_autofree gchar *utc_str = NULL;
g_autofree gchar *label = NULL;
GWeatherLocation *location;
AdwActionRow *row;
g_assert (GCAL_IS_TIME_ZONE_DIALOG (self));
g_assert (GWEATHER_IS_LOCATION (item));
location = GWEATHER_LOCATION (item);
country_name = gweather_location_get_country_name (location);
if (country_name)
{
/* Translators: "%1$s" is the city / region name, and
* "%2$s" is the country name (already localized).
*/
label = g_strdup_printf (_("%1$s, %2$s"),
gweather_location_get_name (location),
country_name);
}
else
{
label = g_strdup (gweather_location_get_name (location));
}
date_time = g_date_time_new (gweather_location_get_timezone (location),
g_date_time_get_year (self->date_time),
g_date_time_get_month (self->date_time),
g_date_time_get_day_of_month (self->date_time),
g_date_time_get_hour (self->date_time),
g_date_time_get_minute (self->date_time),
0);
utc_str = gcal_date_time_format_utc_offset (date_time);
row = ADW_ACTION_ROW (gcal_time_zone_dialog_row_new (location));
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), utc_str);
adw_action_row_set_subtitle (row, label);
return GTK_WIDGET (row);
}
static void
query_locations (GcalTimeZoneDialog *self,
GWeatherLocation *location,
gchar *search)
{
gboolean contains_name;
GWeatherLocation *loc;
GTimeZone *timezone;
const gchar *name, *country_name;
timezone = gweather_location_get_timezone (location);
if (timezone)
{
name = gweather_location_get_name (location);
country_name = gweather_location_get_country_name (location);
contains_name = name != NULL && g_strrstr (g_utf8_casefold (g_utf8_normalize (name, -1, G_NORMALIZE_ALL), -1), search) != NULL;
contains_name = contains_name | (country_name != NULL && g_strrstr (g_utf8_casefold (g_utf8_normalize (country_name, -1, G_NORMALIZE_ALL), -1), search) != NULL);
if (g_list_model_get_n_items (G_LIST_MODEL (self->locations)) >= RESULT_COUNT_LIMIT)
return;
switch (gweather_location_get_level (location))
{
case GWEATHER_LOCATION_CITY:
if (contains_name)
g_list_store_append (self->locations, location);
return;
case GWEATHER_LOCATION_NAMED_TIMEZONE:
if (contains_name)
g_list_store_append (self->locations, location);
return;
default:
break;
}
}
loc = gweather_location_next_child (location, NULL);
while (loc)
{
query_locations (self, loc, search);
if (g_list_model_get_n_items (G_LIST_MODEL (self->locations)) >= RESULT_COUNT_LIMIT)
return;
loc = gweather_location_next_child (location, loc);
}
}
/*
* Callbacks
*/
static void
on_search_changed (GtkSearchEntry *entry,
GcalTimeZoneDialog *self)
{
gchar *temp, *search;
GWeatherLocation *world_location;
g_list_store_remove_all (self->locations);
if (g_strcmp0 (gtk_editable_get_text (GTK_EDITABLE (self->location_entry)), "") == 0)
{
gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->empty_search));
return;
}
temp = g_utf8_normalize (gtk_editable_get_text (GTK_EDITABLE (self->location_entry)), -1, G_NORMALIZE_ALL);
search = g_utf8_casefold (temp, -1);
g_free(temp);
world_location = gweather_location_get_world ();
if (!world_location)
return;
query_locations (self, world_location, search);
if (g_list_model_get_n_items (G_LIST_MODEL (self->locations)) == 0)
{
gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->empty_search));
return;
}
gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->search_results));
}
static void
on_stop_search_cb (GtkSearchEntry *entry,
GcalTimeZoneDialog *self)
{
adw_dialog_close (ADW_DIALOG (self));
}
static void
on_row_activated_cb (GtkListBox *list_box,
GtkListBoxRow *list_box_row,
GcalTimeZoneDialog *self)
{
GcalTimeZoneDialogRow *row;
GWeatherLocation *location;
GTimeZone *time_zone;
GCAL_ENTRY;
row = GCAL_TIME_ZONE_DIALOG_ROW (list_box_row);
location = gcal_time_zone_dialog_row_get_location (row);
time_zone = gweather_location_get_timezone (location);
// The dialog only shows locations that have a timezone.
g_assert (time_zone != NULL);
self->time_zone = g_time_zone_new_identifier (g_time_zone_get_identifier (time_zone));
g_signal_emit (self, signals[TIME_ZONE_SELECTED], 0);
adw_dialog_close (ADW_DIALOG (self));
GCAL_EXIT;
}
/*
* Gobject overrides
*/
static void
gcal_time_zone_dialog_class_init (GcalTimeZoneDialogClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
signals[TIME_ZONE_SELECTED] = g_signal_new ("timezone-selected",
GCAL_TYPE_TIME_ZONE_DIALOG,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
0);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/event-editor/gcal-time-zone-dialog.ui");
gtk_widget_class_bind_template_child (widget_class, GcalTimeZoneDialog, stack);
gtk_widget_class_bind_template_child (widget_class, GcalTimeZoneDialog, empty_search);
gtk_widget_class_bind_template_child (widget_class, GcalTimeZoneDialog, search_results);
gtk_widget_class_bind_template_child (widget_class, GcalTimeZoneDialog, location_entry);
gtk_widget_class_bind_template_child (widget_class, GcalTimeZoneDialog, listbox);
gtk_widget_class_bind_template_callback (widget_class, on_search_changed);
gtk_widget_class_bind_template_callback (widget_class, on_stop_search_cb);
gtk_widget_class_bind_template_callback (widget_class, on_row_activated_cb);
}
static void
gcal_time_zone_dialog_init (GcalTimeZoneDialog *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
gtk_search_entry_set_key_capture_widget (self->location_entry, GTK_WIDGET (self));
self->locations = g_list_store_new (GWEATHER_TYPE_LOCATION);
gtk_list_box_bind_model (self->listbox,
G_LIST_MODEL (self->locations),
create_row,
self,
NULL);
}
/**
* gcal_time_zone_dialog_new:
* @date_time: a #GDateTime
*
* Creates a new #GcalTimeZoneDialog
*
* Returns: (transfer full): a #GcalTimeZoneDialog
*/
GtkWidget*
gcal_time_zone_dialog_new (GDateTime *date_time)
{
GcalTimeZoneDialog *dialog = g_object_new (GCAL_TYPE_TIME_ZONE_DIALOG, NULL);
dialog->date_time = g_date_time_ref (date_time);
return GTK_WIDGET (dialog);
}
/**
* gcal_time_zone_dialog_get_time_zone:
* @self: a #GcalTimeZoneDialog
*
* Get the value of the selected timezone
*
* Returns: (transfer none): the timezone selected in the dialog.
*/
GTimeZone*
gcal_time_zone_dialog_get_time_zone (GcalTimeZoneDialog *self)
{
return self->time_zone;
}
@@ -0,0 +1,34 @@
/* gcal-time-zone-dialog.h
*
* Copyright (C) 2024 Titouan Real <titouan.real@gmail.com>
*
* gnome-calendar 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.
*
* gnome-calendar 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/>.
*/
#pragma once
#include <adwaita.h>
#include "libgweather/gweather.h"
G_BEGIN_DECLS
#define GCAL_TYPE_TIME_ZONE_DIALOG (gcal_time_zone_dialog_get_type ())
G_DECLARE_FINAL_TYPE (GcalTimeZoneDialog, gcal_time_zone_dialog, GCAL, TIME_ZONE_DIALOG, AdwDialog);
GtkWidget* gcal_time_zone_dialog_new (GDateTime *date_time);
GTimeZone* gcal_time_zone_dialog_get_time_zone (GcalTimeZoneDialog *self);
G_END_DECLS
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<interface>
<template class="GcalTimeZoneDialog" parent="AdwDialog">
<property name="title" translatable="yes">Select Timezone</property>
<property name="content_width">400</property>
<property name="content_height">540</property>
<property name="child">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar" id="dialog-header-bar"/>
</child>
<child type="top">
<object class="AdwBin">
<child>
<object class="GtkSearchEntry" id="location_entry">
<property name="placeholder-text" translatable="yes">Search Locations</property>
<signal name="search-changed" handler="on_search_changed" object="GcalTimeZoneDialog" swapped="no"/>
<signal name="stop-search" handler="on_stop_search_cb" object="GcalTimeZoneDialog" swapped="no"/>
</object>
</child>
<style>
<class name="toolbar"/>
</style>
</object>
</child>
<property name="content">
<object class="GtkStack" id="stack">
<child>
<object class="AdwStatusPage" id="empty_search">
<property name="icon_name">loupe-large-symbolic</property>
<property name="title" translatable="yes">Search for a Timezone</property>
</object>
</child>
<child>
<object class="AdwPreferencesPage" id="search_results">
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="GtkListBox" id="listbox">
<property name="valign">start</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
<signal name="row-activated" handler="on_row_activated_cb" object="GcalTimeZoneDialog" swapped="no"/>
<style>
<class name="boxed-list"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</property>
</template>
</interface>
+3 -2
View File
@@ -11,6 +11,7 @@ sources += files(
'gcal-date-chooser.c',
'gcal-date-chooser-day.c',
'gcal-date-chooser-row.c',
'gcal-date-time-chooser.c',
'gcal-date-selector.c',
'gcal-event-editor-dialog.c',
'gcal-event-editor-section.c',
@@ -19,6 +20,6 @@ sources += files(
'gcal-reminders-section.c',
'gcal-schedule-section.c',
'gcal-summary-section.c',
'gcal-time-chooser-row.c',
'gcal-time-selector.c',
'gcal-time-zone-dialog.c',
'gcal-time-zone-dialog-row.c',
)
+4 -5
View File
@@ -490,11 +490,10 @@ on_window_new_event_cb (GSimpleAction *action,
GcalEvent *event;
self = GCAL_WINDOW (user_data);
start = g_date_time_new (gcal_context_get_timezone (self->context),
g_date_time_get_year (self->active_date),
g_date_time_get_month (self->active_date),
g_date_time_get_day_of_month (self->active_date),
0, 0, 0);
start = g_date_time_new_utc (g_date_time_get_year (self->active_date),
g_date_time_get_month (self->active_date),
g_date_time_get_day_of_month (self->active_date),
0, 0, 0);
end = g_date_time_add_days (start, 1);
manager = gcal_context_get_manager (self->context);
+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 13.753906 4.65625 c 0.175782 -0.199219 0.261719 -0.460938 0.246094 -0.722656 c -0.019531 -0.265625 -0.140625 -0.511719 -0.339844 -0.6875 c -0.199218 -0.175782 -0.460937 -0.265625 -0.726562 -0.246094 c -0.265625 0.015625 -0.511719 0.140625 -0.6875 0.339844 l -6.296875 7.195312 l -2.242188 -2.246094 c -0.390625 -0.390624 -1.023437 -0.390624 -1.414062 0 c -0.1875 0.1875 -0.292969 0.445313 -0.292969 0.710938 s 0.105469 0.519531 0.292969 0.707031 l 3 3 c 0.195312 0.195313 0.464843 0.300781 0.742187 0.292969 c 0.277344 -0.011719 0.535156 -0.132812 0.71875 -0.34375 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 734 B

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 7.515625 -0.00390625 c -4.128906 0 -7.5 3.37109425 -7.5 7.50000025 s 3.371094 7.5 7.5 7.5 s 7.5 -3.371094 7.5 -7.5 s -3.371094 -7.50000025 -7.5 -7.50000025 z m 0 2.00000025 c 0.257813 0 0.503906 0.023437 0.75 0.054687 c 0.191406 0.261719 0.382813 0.59375 0.550781 1.027344 c 0.105469 0.277344 0.203125 0.585937 0.289063 0.917969 h -3.179688 c 0.085938 -0.332032 0.183594 -0.640625 0.289063 -0.917969 c 0.167968 -0.433594 0.359375 -0.765625 0.550781 -1.027344 c 0.246094 -0.03125 0.492187 -0.054687 0.75 -0.054687 z m -2.085937 0.40625 c -0.050782 0.109375 -0.105469 0.203125 -0.148438 0.316406 c -0.148438 0.386719 -0.269531 0.820312 -0.378906 1.277344 h -1.617188 c 0.570313 -0.691406 1.296875 -1.246094 2.144532 -1.59375 z m 4.171874 0 c 0.847657 0.347656 1.574219 0.902344 2.144532 1.59375 h -1.617188 c -0.109375 -0.457032 -0.230468 -0.890625 -0.378906 -1.277344 c -0.042969 -0.113281 -0.097656 -0.207031 -0.148438 -0.316406 z m -6.980468 2.59375 h 2.082031 c -0.097656 0.628906 -0.148437 1.300781 -0.167969 2 h -2.480468 c 0.0625 -0.714844 0.253906 -1.390625 0.566406 -2 z m 3.09375 0 h 3.601562 c 0.101563 0.617187 0.15625 1.292968 0.179688 2 h -3.960938 c 0.023438 -0.707032 0.078125 -1.382813 0.179688 -2 z m 4.613281 0 h 2.082031 c 0.3125 0.609375 0.503906 1.285156 0.566406 2 h -2.480468 c -0.019532 -0.699219 -0.070313 -1.371094 -0.167969 -2 z m -8.273437 3 h 2.480468 c 0.019532 0.699218 0.070313 1.375 0.167969 2 h -2.082031 c -0.3125 -0.609375 -0.503906 -1.285156 -0.566406 -2 z m 3.480468 0 h 3.960938 c -0.023438 0.707031 -0.078125 1.382812 -0.179688 2 h -3.601562 c -0.101563 -0.617188 -0.15625 -1.292969 -0.179688 -2 z m 4.960938 0 h 2.480468 c -0.0625 0.714844 -0.253906 1.390625 -0.566406 2 h -2.082031 c 0.097656 -0.625 0.148437 -1.300782 0.167969 -2 z m -7.210938 3 h 1.617188 c 0.109375 0.457031 0.230468 0.890625 0.378906 1.273437 c 0.042969 0.117188 0.097656 0.210938 0.148438 0.320313 c -0.847657 -0.347656 -1.574219 -0.902344 -2.144532 -1.59375 z m 2.640625 0 h 3.179688 c -0.085938 0.332031 -0.183594 0.640625 -0.289063 0.917968 c -0.167968 0.433594 -0.359375 0.765626 -0.550781 1.027344 c -0.246094 0.03125 -0.496094 0.054688 -0.75 0.054688 s -0.503906 -0.023438 -0.75 -0.054688 c -0.191406 -0.261718 -0.382813 -0.59375 -0.550781 -1.027344 c -0.105469 -0.277343 -0.203125 -0.585937 -0.289063 -0.917968 z m 4.203125 0 h 1.617188 c -0.570313 0.691406 -1.296875 1.246094 -2.144532 1.59375 c 0.050782 -0.109375 0.105469 -0.203125 0.148438 -0.320313 c 0.148438 -0.382812 0.269531 -0.816406 0.378906 -1.273437 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+3
View File
@@ -5,11 +5,14 @@
<file compressed="true">calendar-month-symbolic.svg</file>
<file compressed="true">calendar-week-symbolic.svg</file>
<file compressed="true">calendar-today-symbolic.svg</file>
<file compressed="true">check-plain-symbolic.svg</file>
<file compressed="true">clock-alt-symbolic.svg</file>
<file compressed="true">checkmark-small-symbolic.svg</file>
<file compressed="true">external-link-symbolic.svg</file>
<file compressed="true">edit-symbolic.svg</file>
<file compressed="true">eye-not-looking-symbolic.svg</file>
<file compressed="true">globe-symbolic.svg</file>
<file compressed="true">info-outline-symbolic.svg</file>
<file compressed="true">loupe-large-symbolic.svg</file>
</gresource>
</gresources>
+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 10.875 10.0625 c -0.8125 0.148438 -1.105469 1.160156 -0.5 1.71875 l 3 3 c 0.957031 0.9375 2.363281 -0.5 1.40625 -1.4375 l -3 -3 c -0.234375 -0.238281 -0.574219 -0.347656 -0.90625 -0.28125 z m 0 0"/><path d="m 6.570312 0.0625 c -3.578124 0 -6.4999995 2.921875 -6.4999995 6.5 s 2.9218755 6.5 6.4999995 6.5 c 3.578126 0 6.5 -2.921875 6.5 -6.5 s -2.921874 -6.5 -6.5 -6.5 z m 0 2 c 2.5 0 4.5 2.003906 4.5 4.5 c 0 2.5 -2 4.5 -4.5 4.5 c -2.496093 0 -4.5 -2 -4.5 -4.5 c 0 -2.496094 2.003907 -4.5 4.5 -4.5 z m 0 0"/></g></svg>

After

Width:  |  Height:  |  Size: 673 B

+45
View File
@@ -299,3 +299,48 @@ gcal_date_time_add_floating_minutes (GDateTime *initial,
offset = offset / 1000000;
return g_date_time_add_seconds (result, offset);
}
/**
* gcal_date_time_add_floating_minutes:
* @date_time: #GDateTime for which to calculate the UTC difference
*
* Creates a string "UTC+-n" where n is the offset to UTC of date_time.
*
* Returns: (transfer full): A new string.
*/
gchar*
gcal_date_time_format_utc_offset (GDateTime *date_time)
{
g_autoptr (GTimeZone) local_time_zone = NULL;
g_autoptr (GString) utc_str = NULL;
gint64 utc_offset_seconds;
utc_str = g_string_new ("UTC");
utc_offset_seconds = g_date_time_get_utc_offset (date_time) / (1000 * 1000);
if (utc_offset_seconds != 0)
{
gboolean negative;
gint64 minutes;
gint64 hours;
negative = utc_offset_seconds < 0;
if (negative)
utc_offset_seconds = -utc_offset_seconds;
hours = utc_offset_seconds / 3600;
minutes = (utc_offset_seconds - hours*3600) / 60;
g_string_append (utc_str, negative ? "-" : "+");
g_string_append_printf (utc_str, "%ld", hours);
if (minutes != 0)
{
g_string_append (utc_str, ":");
g_string_append_printf (utc_str, "%02ld", minutes);
}
}
return g_string_free_and_steal (g_steal_pointer (&utc_str));
}
+3
View File
@@ -57,4 +57,7 @@ ICalTimezone* gcal_timezone_to_icaltimezone (GTimeZone
GDateTime* gcal_date_time_add_floating_minutes (GDateTime *initial,
gint minutes);
gchar* gcal_date_time_format_utc_offset (GDateTime *date_time);
G_END_DECLS
-50
View File
@@ -334,55 +334,6 @@ event_date_create_tzid (void)
/*********************************************************************************************************************/
static void
event_date_edit_tzid (void)
{
struct {
const gchar *string;
const gchar *tz;
} events[] = {
{
EVENT_STRING_FOR_DATE (";TZID=Europe/Madrid:20170818T130000", ";TZID=Europe/Madrid:20170818T140000"),
"Europe/Madrid"
},
{
// https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/170
EVENT_STRING_FOR_DATE (";TZID=Europe/London:20170818T130000", ";TZID=Europe/London:20170818T140000"),
"Europe/London"
},
{
EVENT_STRING_FOR_DATE (";TZID=America/Costa_Rica:20170818T130000", ";TZID=America/Costa_Rica:20170818T140000"),
"America/Costa_Rica"
},
};
g_test_bug ("170");
for (size_t i = 0; i < G_N_ELEMENTS (events); i++)
{
g_autoptr (GDateTime) datetime = NULL;
g_autoptr (GTimeZone) timezone = NULL;
g_autoptr (GcalEvent) event = NULL;
g_autoptr (GError) error = NULL;
ECalComponentDateTime *dt_end = NULL;
event = create_event_for_string (events[i].string, &error);
g_assert_no_error (error);
// modify the event and check the tz in the result
timezone = g_time_zone_new_identifier (events[i].tz);
datetime = g_date_time_new (timezone, 2017, 8, 18, 15, 00, 00.);
gcal_event_set_date_end (event, datetime);
dt_end = e_cal_component_get_dtend (gcal_event_get_component (event));
g_assert_cmpstr (e_cal_component_datetime_get_tzid (dt_end), ==, "UTC");
e_cal_component_datetime_free (dt_end);
}
}
/*********************************************************************************************************************/
static void
event_date_check_tz (void)
{
@@ -456,7 +407,6 @@ main (gint argc,
g_test_add_func ("/event/date/end", event_date_end);
g_test_add_func ("/event/date/singleday", event_date_singleday);
g_test_add_func ("/event/date/multiday", event_date_multiday);
g_test_add_func ("/event/date/edit-tzid", event_date_edit_tzid);
g_test_add_func ("/event/date/create-tzid", event_date_create_tzid);
g_test_add_func ("/event/date/check-tz", event_date_check_tz);