[seahorse/wip/nielsdg/datetime: 1/2] Create SeahorseDatePicker




commit cfd583a665a3f3f2c7ec7c041649ca1f21e5fa6e
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Sat Feb 27 21:30:57 2021 +0100

    Create SeahorseDatePicker
    
    Use it to get rid of `EggDateTime`, and the sometimes troublesome parts
    of its implementation. `SeahorseDatePicker` is implemented in such a way
    that it can be embedded in GtkBuilder .xml files, so we can define it in
    a declarative manner, and also don't have to deal with placeholder
    widgets.
    
    Fixes https://gitlab.gnome.org/GNOME/seahorse/-/issues/282

 common/datepicker.vala                | 107 ++++++++++++++++++++++++++++++++++
 common/meson.build                    |   1 +
 pgp/seahorse-gpgme-add-subkey.c       |  26 ++++-----
 pgp/seahorse-gpgme-add-subkey.ui      |   7 +--
 pgp/seahorse-gpgme-generate-dialog.c  |  32 ++++------
 pgp/seahorse-gpgme-generate-dialog.ui |   8 ++-
 po/POTFILES.in                        |   1 +
 po/POTFILES.skip                      |   1 +
 8 files changed, 141 insertions(+), 42 deletions(-)
---
diff --git a/common/datepicker.vala b/common/datepicker.vala
new file mode 100644
index 00000000..0725bd43
--- /dev/null
+++ b/common/datepicker.vala
@@ -0,0 +1,107 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2021 Niels De Graef
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Shows a configurable date. A user can configure it by either manually
+ * changing a {@link Gtk.Entry} or by opening up a popover with a
+ * {@link Gtk.Calendar} and finding the right date there.
+ */
+public class Seahorse.DatePicker : Gtk.Box {
+
+    private Gtk.Entry date_entry;
+    // private Gtk.MenuButton calendar_button;
+    private Gtk.Popover calendar_popover;
+    private Gtk.Calendar calendar;
+
+    private GLib.DateTime _datetime = new GLib.DateTime.now();
+    public GLib.DateTime datetime {
+        get { return this._datetime; }
+        set {
+          if (this._datetime.equal(value))
+            return;
+
+          this._datetime = value;
+          this.date_entry.text = value.format("%F");
+          // Note: GtkCalendar's months are [0,11] while GDateTime uses [1,12]
+          this.calendar.select_month(value.get_month() - 1, value.get_year());
+          this.calendar.select_day(value.get_day_of_month());
+       }
+    }
+
+    construct {
+        this.orientation = Gtk.Orientation.HORIZONTAL;
+        get_style_context().add_class("linked");
+
+        // The entry for manual editing
+        this.date_entry = new Gtk.Entry();
+        this.date_entry.visible = true;
+        this.date_entry.max_length = 10;
+        this.date_entry.max_width_chars = 10;
+        this.date_entry.tooltip_text = _("Enter the date directly");
+        this.date_entry.activate.connect(on_date_entry_activated);
+        add(this.date_entry);
+
+        // The button with a popover
+        var calendar_button = new Gtk.MenuButton();
+        calendar_button.visible = true;
+        calendar_button.tooltip_text = _("Select the date from a calendar");
+        add(calendar_button);
+
+        // The popover that contains the calendar
+        this.calendar_popover = new Gtk.Popover(calendar_button);
+        calendar_button.popover = this.calendar_popover;
+
+        // The calendar
+        this.calendar = new Gtk.Calendar();
+        this.calendar.visible = true;
+        this.calendar.show_day_names = true;
+        this.calendar.show_heading = true;
+        this.calendar.day_selected.connect(on_calendar_day_selected);
+        this.calendar_popover.add(this.calendar);
+    }
+
+    [CCode (type="GtkWidget*")]
+    public DatePicker() {
+    }
+
+    private void on_date_entry_activated(Gtk.Entry date_entry) {
+        int y, m, d;
+
+        // FIXME show something on an invalid date
+        if (date_entry.get_text().scanf("%d-%d-%d", out y, out m, out d) != 3)
+            return;
+
+        var parsed_date = new DateTime.utc(y, m, d, 0, 0, 0);
+        warning("activated, %p", parsed_date);
+        // FIXME warn on invalid date, or date before today
+        if (parsed_date == null)
+            return;
+
+        this.datetime = parsed_date;
+    }
+
+    private void on_calendar_day_selected(Gtk.Calendar calendar) {
+        uint y, m, d;
+
+        calendar.get_date(out y, out m, out d);
+        // Note: GtkCalendar's months are [0,11] while GDateTime uses [1,12]
+        this.datetime = new DateTime.utc((int) y, (int) m + 1, (int) d, 0, 0, 0);
+        this.calendar_popover.popdown();
+    }
+}
diff --git a/common/meson.build b/common/meson.build
index 52e57c8a..755fe17b 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -4,6 +4,7 @@ common_sources = files(
   'app-settings.vala',
   'backend.vala',
   'catalog.vala',
+  'datepicker.vala',
   'deletable.vala',
   'delete-dialog.vala',
   'deleter.vala',
diff --git a/pgp/seahorse-gpgme-add-subkey.c b/pgp/seahorse-gpgme-add-subkey.c
index 2ddd88ed..9a706323 100644
--- a/pgp/seahorse-gpgme-add-subkey.c
+++ b/pgp/seahorse-gpgme-add-subkey.c
@@ -20,8 +20,8 @@
 #include <glib/gi18n.h>
 
 #include "config.h"
+#include "seahorse-common.h"
 #include "libseahorse/seahorse-util.h"
-#include "libegg/egg-datetime.h"
 #include "seahorse-gpgme-add-subkey.h"
 #include "seahorse-gpgme-key-op.h"
 
@@ -42,8 +42,7 @@ struct _SeahorseGpgmeAddSubkey {
 
     GtkWidget *length_spinner;
 
-    GtkWidget *datetime_placeholder;
-    GtkWidget *expires_datetime;
+    GtkWidget *expires_datepicker;
     GtkWidget *never_expires_check;
 };
 
@@ -103,7 +102,7 @@ on_gpgme_add_subkey_never_expires_toggled (GtkToggleButton *togglebutton,
 {
     SeahorseGpgmeAddSubkey *self = SEAHORSE_GPGME_ADD_SUBKEY (user_data);
 
-    gtk_widget_set_sensitive (self->expires_datetime,
+    gtk_widget_set_sensitive (self->expires_datepicker,
                               !gtk_toggle_button_get_active (togglebutton));
 }
 
@@ -135,16 +134,12 @@ seahorse_gpgme_add_subkey_get_active_type (SeahorseGpgmeAddSubkey *self)
 GDateTime *
 seahorse_gpgme_add_subkey_get_expires (SeahorseGpgmeAddSubkey *self)
 {
-    time_t expires;
-
     g_return_val_if_fail (SEAHORSE_GPGME_IS_ADD_SUBKEY (self), 0);
 
     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->never_expires_check)))
         return 0;
 
-    egg_datetime_get_as_time_t (EGG_DATETIME (self->expires_datetime),
-                                &expires);
-    return g_date_time_new_from_unix_utc (expires);
+    return seahorse_date_picker_get_datetime (SEAHORSE_DATE_PICKER(self->expires_datepicker));
 }
 
 guint
@@ -220,6 +215,8 @@ seahorse_gpgme_add_subkey_init (SeahorseGpgmeAddSubkey *self)
 {
     GtkTreeIter iter;
     GtkCellRenderer *renderer;
+    g_autoptr (GDateTime) now = NULL;
+    g_autoptr (GDateTime) next_year = NULL;
 
     gtk_widget_init_template (GTK_WIDGET (self));
 
@@ -260,11 +257,10 @@ seahorse_gpgme_add_subkey_init (SeahorseGpgmeAddSubkey *self)
                         COMBO_INT, 3,
                         -1);
 
-    self->expires_datetime = egg_datetime_new ();
-    gtk_container_add (GTK_CONTAINER (self->datetime_placeholder),
-                       self->expires_datetime);
-    gtk_widget_show (self->expires_datetime);
-    gtk_widget_set_sensitive (self->expires_datetime, FALSE);
+    now = g_date_time_new_now_utc ();
+    next_year = g_date_time_add_years (now, 1);
+    seahorse_date_picker_set_datetime (SEAHORSE_DATE_PICKER (self->expires_datepicker),
+                                       next_year);
 }
 
 static void
@@ -287,7 +283,7 @@ seahorse_gpgme_add_subkey_class_init (SeahorseGpgmeAddSubkeyClass *klass)
     gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Seahorse/seahorse-gpgme-add-subkey.ui");
     gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeAddSubkey, type_combo);
     gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeAddSubkey, length_spinner);
-    gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeAddSubkey, datetime_placeholder);
+    gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeAddSubkey, expires_datepicker);
     gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeAddSubkey, never_expires_check);
     gtk_widget_class_bind_template_callback (widget_class, handler_gpgme_add_subkey_type_changed);
     gtk_widget_class_bind_template_callback (widget_class, on_gpgme_add_subkey_never_expires_toggled);
diff --git a/pgp/seahorse-gpgme-add-subkey.ui b/pgp/seahorse-gpgme-add-subkey.ui
index 7b687e82..c8efa24e 100644
--- a/pgp/seahorse-gpgme-add-subkey.ui
+++ b/pgp/seahorse-gpgme-add-subkey.ui
@@ -84,6 +84,7 @@
                 <property name="visible">True</property>
                 <property name="xalign">1</property>
                 <property name="label" translatable="yes">Expiration Date</property>
+                <property name="mnemonic-widget">expires_datepicker</property>
                 <style>
                   <class name="dim-label"/>
                 </style>
@@ -94,11 +95,9 @@
               </packing>
             </child>
             <child>
-              <object class="GtkBox" id="datetime_placeholder">
+              <object class="SeahorseDatePicker" id="expires_datepicker">
                 <property name="visible">True</property>
-                <child>
-                  <placeholder/>
-                </child>
+                <property name="sensitive">False</property>
               </object>
               <packing>
                 <property name="top_attach">2</property>
diff --git a/pgp/seahorse-gpgme-generate-dialog.c b/pgp/seahorse-gpgme-generate-dialog.c
index f5e6c5e4..2a0c122a 100644
--- a/pgp/seahorse-gpgme-generate-dialog.c
+++ b/pgp/seahorse-gpgme-generate-dialog.c
@@ -30,8 +30,6 @@
 
 #include "seahorse-common.h"
 
-#include "libegg/egg-datetime.h"
-
 #include "libseahorse/seahorse-progress.h"
 #include "libseahorse/seahorse-util.h"
 
@@ -59,8 +57,7 @@ struct _SeahorseGpgmeGenerateDialog {
     GtkWidget *bits_entry;
 
     GtkWidget *expires_check;
-    GtkWidget *expiry_date_container;
-    GtkWidget *expiry_date;
+    GtkWidget *expires_datepicker;
 };
 
 enum {
@@ -195,7 +192,7 @@ on_gpgme_generate_expires_toggled (GtkToggleButton *button,
 {
     SeahorseGpgmeGenerateDialog *self = SEAHORSE_GPGME_GENERATE_DIALOG (user_data);
 
-    gtk_widget_set_sensitive (self->expiry_date,
+    gtk_widget_set_sensitive (self->expires_datepicker,
                               !gtk_toggle_button_get_active (button));
 }
 
@@ -257,12 +254,8 @@ seahorse_gpgme_generate_dialog_response (GtkDialog *dialog, int response)
     }
 
     /* The expiry */
-    if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->expires_check))) {
-        time_t time_expires;
-
-        egg_datetime_get_as_time_t (EGG_DATETIME (self->expiry_date), &time_expires);
-        expires = g_date_time_new_from_unix_utc (time_expires);
-    }
+    if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->expires_check)))
+        expires = seahorse_date_picker_get_datetime (SEAHORSE_DATE_PICKER (self->expires_datepicker));
 
     /* Less confusing with less on the screen */
     gtk_widget_hide (GTK_WIDGET (self));
@@ -322,7 +315,8 @@ seahorse_gpgme_generate_dialog_finalize (GObject *obj)
 static void
 seahorse_gpgme_generate_dialog_init (SeahorseGpgmeGenerateDialog *self)
 {
-    time_t expires;
+    g_autoptr (GDateTime) now = NULL;
+    g_autoptr (GDateTime) next_year = NULL;
     guint i;
 
     gtk_widget_init_template (GTK_WIDGET (self));
@@ -337,15 +331,11 @@ seahorse_gpgme_generate_dialog_init (SeahorseGpgmeGenerateDialog *self)
     on_gpgme_generate_algorithm_changed (GTK_COMBO_BOX (self->algorithm_choice),
                                          self);
 
-    expires = time (NULL);
-    expires += (60 * 60 * 24 * 365); /* Seconds in a year */
-
     /* Default expiry date */
-    self->expiry_date = egg_datetime_new_from_time_t (expires);
-    gtk_box_pack_start (GTK_BOX (self->expiry_date_container),
-                        self->expiry_date, TRUE, TRUE, 0);
-    gtk_widget_set_sensitive (self->expiry_date, FALSE);
-    gtk_widget_show (self->expiry_date);
+    now = g_date_time_new_now_utc ();
+    next_year = g_date_time_add_years (now, 1);
+    seahorse_date_picker_set_datetime (SEAHORSE_DATE_PICKER (self->expires_datepicker),
+                                       next_year);
 
     on_gpgme_generate_entry_changed (NULL, self);
 }
@@ -377,7 +367,7 @@ seahorse_gpgme_generate_dialog_class_init (SeahorseGpgmeGenerateDialogClass *kla
     gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeGenerateDialog, comment_entry);
     gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeGenerateDialog, algorithm_choice);
     gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeGenerateDialog, bits_entry);
-    gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeGenerateDialog, expiry_date_container);
+    gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeGenerateDialog, expires_datepicker);
     gtk_widget_class_bind_template_child (widget_class, SeahorseGpgmeGenerateDialog, expires_check);
     gtk_widget_class_bind_template_callback (widget_class, on_gpgme_generate_entry_changed);
     gtk_widget_class_bind_template_callback (widget_class, on_gpgme_generate_expires_toggled);
diff --git a/pgp/seahorse-gpgme-generate-dialog.ui b/pgp/seahorse-gpgme-generate-dialog.ui
index d821830b..35380ce3 100644
--- a/pgp/seahorse-gpgme-generate-dialog.ui
+++ b/pgp/seahorse-gpgme-generate-dialog.ui
@@ -228,6 +228,7 @@
                     <property name="halign">end</property>
                     <property name="label" translatable="yes">E_xpiration Date</property>
                     <property name="use_underline">True</property>
+                    <property name="mnemonic-widget">expires_datepicker</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
@@ -238,13 +239,16 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkBox" id="expiry_date_container">
+                  <object class="GtkBox">
                     <property name="visible">True</property>
                     <property name="orientation">horizontal</property>
                     <property name="can_focus">False</property>
                     <property name="spacing">12</property>
                     <child>
-                      <placeholder/>
+                      <object class="SeahorseDatePicker" id="expires_datepicker">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                      </object>
                     </child>
                     <child>
                       <object class="GtkCheckButton" id="expires_check">
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 00226b3b..5bca1890 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,6 +2,7 @@
 # Please keep this file sorted alphabetically.
 common/add-keyserver-dialog.vala
 common/catalog.vala
+common/datepicker.vala
 common/delete-dialog.vala
 common/exportable.vala
 common/object.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 4310f4d3..964e65ba 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -1,6 +1,7 @@
 # List of source files that should *not* be translated.
 # Please keep this file sorted alphabetically.
 common/catalog.c
+common/datepicker.c
 common/delete-dialog.c
 common/exportable.c
 common/interaction.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]