event-editor: Create GcalTimeChooserRow
Create a new GcalTimeChooserRow widget from AdwEntryRow that parses the entry into a time, or lets the user pick a time from a popover. [feaneron] Improved code style, reworked regex management.
This commit is contained in:
+1
-1
@@ -164,7 +164,7 @@ libedataserverui_dep = dependency('libedataserverui4-1.0', version: '>= 3.45.1')
|
||||
libedataserver_dep = dependency('libedataserver-1.2', version: '>= 3.45.1')
|
||||
libecal_dep = dependency('libecal-2.0', version: '>= 3.45.1')
|
||||
libsoup_dep = dependency('libsoup-3.0')
|
||||
libadwaita_dep = dependency('libadwaita-1', version: '>= 1.6.alpha')
|
||||
libadwaita_dep = dependency('libadwaita-1', version: '>= 1.7.alpha')
|
||||
glib_dep = dependency('glib-2.0', version: '>= 2.67.5')
|
||||
gtk_dep = dependency('gtk4', version: '>= 4.15.2')
|
||||
gio_dep = dependency('gio-2.0', version: '>= 2.58.0')
|
||||
|
||||
@@ -11,6 +11,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>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
/* 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 (®ex))
|
||||
{
|
||||
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 (®ex))
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* gcal-time-chooser-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 "gcal-enums.h"
|
||||
|
||||
#include <adwaita.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GCAL_TYPE_TIME_CHOOSER_ROW (gcal_time_chooser_row_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GcalTimeChooserRow, gcal_time_chooser_row, GCAL, TIME_CHOOSER_ROW, AdwEntryRow)
|
||||
|
||||
GcalTimeFormat gcal_time_chooser_row_get_time_format (GcalTimeChooserRow *self);
|
||||
void gcal_time_chooser_row_set_time_format (GcalTimeChooserRow *self,
|
||||
GcalTimeFormat time_format);
|
||||
|
||||
GDateTime* gcal_time_chooser_row_get_time (GcalTimeChooserRow *self);
|
||||
void gcal_time_chooser_row_set_time (GcalTimeChooserRow *self,
|
||||
GDateTime *date);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GcalTimeChooserRow" parent="AdwEntryRow">
|
||||
|
||||
<child>
|
||||
<object class="GtkEventControllerFocus">
|
||||
<signal name="notify::contains-focus" handler="on_contains_focus_changed_cb" object="GcalTimeChooserRow" 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">
|
||||
<property name="position">bottom</property>
|
||||
<property name="margin-top">12</property>
|
||||
<property name="margin-bottom">12</property>
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
|
||||
<child>
|
||||
<object class="GcalTimeSelector" id="time_selector">
|
||||
<signal name="notify::time" handler="on_time_selected_changed_cb" object="GcalTimeChooserRow" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<signal name="show" handler="on_time_popover_shown_cb" object="GcalTimeChooserRow" swapped="no" />
|
||||
</object>
|
||||
</property>
|
||||
|
||||
<accessibility>
|
||||
<property name="label" translatable="yes">Edit time</property>
|
||||
</accessibility>
|
||||
|
||||
<style>
|
||||
<class name="flat" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</template>
|
||||
</interface>
|
||||
@@ -18,5 +18,6 @@ sources += files(
|
||||
'gcal-reminders-section.c',
|
||||
'gcal-schedule-section.c',
|
||||
'gcal-summary-section.c',
|
||||
'gcal-time-chooser-row.c',
|
||||
'gcal-time-selector.c',
|
||||
)
|
||||
|
||||
@@ -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 8 0 c -4.40625 0 -8 3.59375 -8 8 s 3.59375 8 8 8 s 8 -3.59375 8 -8 s -3.59375 -8 -8 -8 z m 0 2 c 3.324219 0 6 2.675781 6 6 s -2.675781 6 -6 6 s -6 -2.675781 -6 -6 s 2.675781 -6 6 -6 z m 0 0"/><path d="m 4.929688 4.953125 c -0.128907 0.003906 -0.257813 0.058594 -0.351563 0.152344 c -0.191406 0.195312 -0.1875 0.511719 0.007813 0.707031 l 3.113281 3.042969 c 0.105469 0.097656 0.246093 0.144531 0.386719 0.128906 h 2.914062 c 0.277344 0 0.5 -0.222656 0.5 -0.5 c 0 -0.273437 -0.222656 -0.5 -0.5 -0.5 h -2.761719 l -2.953125 -2.886719 c -0.09375 -0.09375 -0.222656 -0.144531 -0.355468 -0.144531 z m 0 0"/></g></svg>
|
||||
|
After Width: | Height: | Size: 768 B |
@@ -5,6 +5,7 @@
|
||||
<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">clock-alt-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>
|
||||
|
||||
Reference in New Issue
Block a user