From b6a34f7666c2ecee22ddf6340a072af427432bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erick=20P=C3=A9rez=20Castellanos?= Date: Sun, 7 Oct 2012 19:14:15 -0400 Subject: [PATCH] Added GcalTimeEntry widget. GcalTimeEntry widget handle 24-hour format time. --- src/Makefile.am | 2 + src/gcal-time-entry.c | 356 ++++++++++++++++++++++++++++++++++++++++++ src/gcal-time-entry.h | 67 ++++++++ 3 files changed, 425 insertions(+) create mode 100644 src/gcal-time-entry.c create mode 100644 src/gcal-time-entry.h diff --git a/src/Makefile.am b/src/Makefile.am index 691f0f38..459e8360 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,6 +43,8 @@ gnome_calendar_SOURCES = \ gcal-event-overlay.h \ gcal-edit-dialog.c \ gcal-edit-dialog.h \ + gcal-time-entry.c \ + gcal-time-entry.h \ gcal-manager.c \ gcal-manager.h \ gcal-utils.c \ diff --git a/src/gcal-time-entry.c b/src/gcal-time-entry.c new file mode 100644 index 00000000..5bd2e8aa --- /dev/null +++ b/src/gcal-time-entry.c @@ -0,0 +1,356 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * gcal-time-entry.c + * Copyright (C) 2012 Erick Pérez Castellanos + * + * 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 . + */ + +#include "gcal-time-entry.h" + +#include + +struct _GcalTimeEntryPrivate +{ + gboolean internal_delete; + + guint hours; + guint minutes; +}; + +static void gtk_editable_iface_init (GtkEditableInterface *iface); + +static void gcal_time_entry_constructed (GObject *object); + +static gboolean gcal_time_entry_key_press_event (GtkWidget *widget, + GdkEventKey *event); + +static void gcal_time_entry_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position); + +G_DEFINE_TYPE_WITH_CODE (GcalTimeEntry, + gcal_time_entry, + GTK_TYPE_ENTRY, + G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, + gtk_editable_iface_init)); + +static void +gcal_time_entry_class_init (GcalTimeEntryClass *klass) +{ + GtkWidgetClass *widget_class; + GObjectClass *object_class; + + widget_class = GTK_WIDGET_CLASS (klass); + widget_class->key_press_event = gcal_time_entry_key_press_event; + + object_class = G_OBJECT_CLASS (klass); + object_class->constructed = gcal_time_entry_constructed; + + g_type_class_add_private ((gpointer)klass, sizeof(GcalTimeEntryPrivate)); +} + +static void +gcal_time_entry_init (GcalTimeEntry *self) +{ + GcalTimeEntryPrivate *priv; + gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GCAL_TYPE_TIME_ENTRY, + GcalTimeEntryPrivate); + priv = self->priv; + + priv->internal_delete = FALSE; + priv->hours = 0; + priv->minutes = 0; +} + +static void +gtk_editable_iface_init (GtkEditableInterface *iface) +{ + iface->insert_text = gcal_time_entry_insert_text; +} + +static void +gcal_time_entry_constructed (GObject *object) +{ + /* chaining up */ + G_OBJECT_CLASS (gcal_time_entry_parent_class)->constructed (object); + + gtk_entry_set_width_chars (GTK_ENTRY (object), 8); + gtk_entry_set_alignment (GTK_ENTRY (object), 0.5); + + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (object), + GTK_ENTRY_ICON_SECONDARY, + "preferences-system-time-symbolic"); + gtk_entry_set_icon_tooltip_text (GTK_ENTRY (object), + GTK_ENTRY_ICON_SECONDARY, + _("Change the time")); + + /* default text */ + gtk_entry_set_text (GTK_ENTRY (object), "00:00"); +} + +static gboolean +gcal_time_entry_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + GcalTimeEntryPrivate *priv; + gint cursor_position; + gchar *new_time; + + priv = GCAL_TIME_ENTRY (widget)->priv; + + if (event->keyval == GDK_KEY_Up) + { + g_object_get (widget, "cursor-position", &cursor_position, NULL); + if (cursor_position == 0 || + cursor_position == 1) + { + priv->hours = (priv->hours + 1) % 24; + } + else if (cursor_position == 3 || + cursor_position == 4) + { + priv->minutes = (priv->minutes + 1) % 60; + } + + new_time = g_strdup_printf ("%.2d:%.2d", priv->hours, priv->minutes); + gtk_entry_set_text (GTK_ENTRY (widget), new_time); + g_free (new_time); + + g_signal_emit_by_name (widget, "move-cursor", 0, cursor_position, FALSE, NULL); + return TRUE; + } + else if (event->keyval == GDK_KEY_Down) + { + g_object_get (widget, "cursor-position", &cursor_position, NULL); + if (cursor_position == 0 || + cursor_position == 1) + { + priv->hours = priv->hours == 0 ? 23 : priv->hours - 1; + } + else if (cursor_position == 3 || + cursor_position == 4) + { + priv->minutes = priv->minutes == 0 ? 59 : priv->minutes - 1; + } + + new_time = g_strdup_printf ("%.2d:%.2d", priv->hours, priv->minutes); + gtk_entry_set_text (GTK_ENTRY (widget), new_time); + g_free (new_time); + + g_signal_emit_by_name (widget, "move-cursor", 0, cursor_position, FALSE, NULL); + return TRUE; + } + + GTK_WIDGET_CLASS (gcal_time_entry_parent_class)->key_press_event (widget, event); + + return FALSE; +} + +static void +gcal_time_entry_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position) +{ + GcalTimeEntryPrivate *priv; + GtkEditableInterface *parent_editable_iface; + gint i; + gboolean had_colon; + gchar *text_to_insert; + gchar *old_text; + gchar *hours_text; + gint next_pos; + + priv = GCAL_TIME_ENTRY (editable)->priv; + + parent_editable_iface = g_type_interface_peek (gcal_time_entry_parent_class, + GTK_TYPE_EDITABLE); + next_pos = *position + new_text_length; + + if (*position > 4 || + *position == 2) + { + return; + } + + new_text_length = new_text_length + *position > 5 ? 5 - *position : new_text_length; + text_to_insert = g_malloc0 (sizeof(gchar) * new_text_length); + old_text = g_strdup_printf ("%.2d:%.2d", priv->hours, priv->minutes); + + had_colon = FALSE; + for (i = 0; i < new_text_length; i++) + { + if (new_text[i] == 0x3a) + { + had_colon = TRUE; + continue; + } + if (new_text[i] < 0x30 || new_text[i] > 0x39) + text_to_insert[i - (had_colon ? 1 : 0)] = old_text[*position + i]; + else + text_to_insert[i - (had_colon ? 1 : 0)] = new_text[i]; + } + + if (text_to_insert[0] == '\0') + return; + + if (had_colon) + new_text_length--; + + switch (*position) + { + case 0: + switch (new_text_length) + { + case 4: + if (text_to_insert[2] <= '5') + { + old_text[3] = text_to_insert[2]; + } + old_text[4] = text_to_insert[3]; + case 2: + if (text_to_insert[0] == '2') + { + if (text_to_insert[1] <= '3') + old_text[1] = text_to_insert[1]; + else if (old_text[1] > '3') + old_text[1] = '3'; + } + else + { + old_text[1] = text_to_insert[1]; + } + case 1: + if (text_to_insert[0] <= '2') + { + old_text[0] = text_to_insert[0]; + + if (text_to_insert[0] == '2' && + old_text[1] > '3') + { + old_text[1] = '3'; + } + } + break; + default: + return; + } + break; + case 1: + /* this is the only case I will analyze here */ + if (new_text_length == 1) + { + if (old_text[0] == '2') + { + if (text_to_insert[0] < '4') + old_text[1] = text_to_insert[0]; + } + else + { + old_text[1] = text_to_insert[0]; + } + } + break; + case 3: + switch (new_text_length) + { + case 2: + old_text[4] = text_to_insert[1]; + case 1: + if (text_to_insert[0] <= '5') + { + old_text[3] = text_to_insert[0]; + } + break; + default: + return; + } + break; + case 4: + old_text[4] = text_to_insert[0]; + break; + + default: + return; + } + + /* setting hours, minutes */ + hours_text = g_strdup_printf ("%.2s", old_text); + priv->hours = (guint) g_ascii_strtoull (hours_text, NULL, 10); + g_free (hours_text); + priv->minutes = (guint) g_ascii_strtoull (old_text + 3, NULL, 10); + + priv->internal_delete = TRUE; + gtk_editable_delete_text (editable, 0, 5); + priv->internal_delete = FALSE; + *position = 0; + parent_editable_iface->insert_text (editable, + old_text, + 5, + position); + + /* skipping colon */ + if (next_pos == 2) + next_pos = 3; + *position = next_pos; + g_free (old_text); + g_free (text_to_insert); +} + +/* Public API */ +GtkWidget* +gcal_time_entry_new (void) +{ + return g_object_new (GCAL_TYPE_TIME_ENTRY, NULL); +} + +void +gcal_time_entry_set_time (GcalTimeEntry *entry, + guint hours, + guint minutes) +{ + GcalTimeEntryPrivate *priv; + gchar *new_time; + + g_return_if_fail (GCAL_IS_TIME_ENTRY (entry)); + priv = entry->priv; + g_warn_if_fail (hours < 24); + g_warn_if_fail (minutes < 60); + + priv->hours = hours < 24 ? hours : 0; + priv->minutes = minutes < 60 ? minutes : 0; + + new_time = g_strdup_printf ("%.2d:%.2d", priv->hours, priv->minutes); + gtk_entry_set_text (GTK_ENTRY (entry), new_time); + g_free (new_time); +} + +void +gcal_time_entry_get_time (GcalTimeEntry *entry, + guint *hours, + guint *minutes) +{ + GcalTimeEntryPrivate *priv; + + g_return_if_fail (GCAL_IS_TIME_ENTRY (entry)); + priv = entry->priv; + + *hours = priv->hours; + *minutes = priv->minutes; +} diff --git a/src/gcal-time-entry.h b/src/gcal-time-entry.h new file mode 100644 index 00000000..b1967977 --- /dev/null +++ b/src/gcal-time-entry.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * gcal-time-entry.h + * Copyright (C) 2012 Erick Pérez Castellanos + * + * 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 . + */ + +#ifndef __GCAL_TIME_ENTRY_H__ +#define __GCAL_TIME_ENTRY_H__ + +#include + +G_BEGIN_DECLS + +#define GCAL_TYPE_TIME_ENTRY (gcal_time_entry_get_type ()) +#define GCAL_TIME_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCAL_TYPE_TIME_ENTRY, GcalTimeEntry)) +#define GCAL_TIME_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCAL_TYPE_TIME_ENTRY, GcalTimeEntryClass)) +#define GCAL_IS_TIME_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCAL_TYPE_TIME_ENTRY)) +#define GCAL_IS_TIME_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCAL_TYPE_TIME_ENTRY)) +#define GCAL_TIME_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCAL_TYPE_TIME_ENTRY, GcalTimeEntryClass)) + +typedef struct _GcalTime GcalTime; + +typedef struct _GcalTimeEntry GcalTimeEntry; +typedef struct _GcalTimeEntryClass GcalTimeEntryClass; +typedef struct _GcalTimeEntryPrivate GcalTimeEntryPrivate; + +struct _GcalTimeEntry +{ + GtkEntry parent; + /* add your public declarations here */ + + GcalTimeEntryPrivate *priv; +}; + +struct _GcalTimeEntryClass +{ + GtkEntryClass parent_class; +}; + +GType gcal_time_entry_get_type (void); + +GtkWidget* gcal_time_entry_new (void); + +void gcal_time_entry_set_time (GcalTimeEntry *entry, + guint hours, + guint minutes); + +void gcal_time_entry_get_time (GcalTimeEntry *entry, + guint *hours, + guint *minutes); + +G_END_DECLS + +#endif /* __GCAL_TIME_ENTRY_H__ */