[gtk+/wip/matthiasc/calendar: 1/2] wip: date chooser widget



commit 26ab45a3e0f88f814b3f34a43f2dffd48ef3cb40
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Aug 30 21:40:38 2015 -0400

    wip: date chooser widget

 gtk/Makefile.am                |    5 +
 gtk/gtk.h                      |    1 +
 gtk/gtkdatechooserday.c        |  359 ++++++++++++++
 gtk/gtkdatechooserdayprivate.h |   53 +++
 gtk/gtkdatechooserwidget.c     | 1005 ++++++++++++++++++++++++++++++++++++++++
 gtk/gtkdatechooserwidget.h     |   93 ++++
 gtk/ui/gtkdatechooserwidget.ui |  153 ++++++
 7 files changed, 1669 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 4fd509c..799c0b0 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -164,6 +164,7 @@ gtk_public_h_sources =              \
        gtkcontainer.h          \
        gtkcssprovider.h        \
        gtkcsssection.h         \
+       gtkdatechooserwidget.h  \
        gtkdebug.h              \
        gtkdialog.h             \
        gtkdnd.h                \
@@ -424,6 +425,7 @@ gtk_private_h_sources =             \
        gtkcssvalueprivate.h    \
        gtkcsswidgetnodeprivate.h       \
        gtkcustompaperunixdialog.h \
+       gtkdatechooserdayprivate.h \
        gtkdialogprivate.h      \
        gtkdndprivate.h         \
        gtkentryprivate.h       \
@@ -668,6 +670,8 @@ gtk_base_c_sources =                \
        gtkcsstypes.c           \
        gtkcssvalue.c           \
        gtkcsswidgetnode.c      \
+       gtkdatechooserday.c     \
+       gtkdatechooserwidget.c  \
        gtkdialog.c             \
        gtkdrawingarea.c        \
        gtkeditable.c           \
@@ -1080,6 +1084,7 @@ templates =                               \
        ui/gtkcolorchooserdialog.ui     \
        ui/gtkcoloreditor.ui            \
        ui/gtkcombobox.ui               \
+       ui/gtkdatechooserwidget.ui      \
        ui/gtkdialog.ui         \
        ui/gtkfilechooserbutton.ui      \
        ui/gtkfilechooserwidget.ui      \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 3cbd13f..0d85e4a 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -81,6 +81,7 @@
 #include <gtk/gtkcontainer.h>
 #include <gtk/gtkcssprovider.h>
 #include <gtk/gtkcsssection.h>
+#include <gtk/gtkdatechooserwidget.h>
 #include <gtk/gtkdebug.h>
 #include <gtk/gtkdialog.h>
 #include <gtk/gtkdnd.h>
diff --git a/gtk/gtkdatechooserday.c b/gtk/gtkdatechooserday.c
new file mode 100644
index 0000000..5e7c500
--- /dev/null
+++ b/gtk/gtkdatechooserday.c
@@ -0,0 +1,359 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkdatechooserdayprivate.h"
+#include "gtklabel.h"
+#include "gtkgesturemultipress.h"
+#include "gtkrender.h"
+#include "gtkwidgetprivate.h"
+#include "gtkselection.h"
+#include "gtkdnd.h"
+
+#include "gtkintl.h"
+#include "gtkprivate.h"
+
+#include <stdlib.h>
+#include <langinfo.h>
+
+enum {
+  SELECTED,
+  LAST_DAY_SIGNAL
+};
+
+static guint day_signals[LAST_DAY_SIGNAL] = { 0, };
+
+struct _GtkDateChooserDay
+{
+  GtkBin parent;
+  GtkWidget *label;
+  guint day;
+  guint month;
+  guint year;
+  GdkWindow *event_window;
+  GtkGesture *multipress_gesture;
+};
+
+struct _GtkDateChooserDayClass
+{
+  GtkBinClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkDateChooserDay, gtk_date_chooser_day, GTK_TYPE_BIN)
+
+static void
+day_pressed (GtkGestureMultiPress *gesture,
+             gint                  n_press,
+             gdouble               x,
+             gdouble               y,
+             GtkDateChooserDay    *day)
+{
+  gint button;
+
+  button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+
+  if (button == GDK_BUTTON_PRIMARY)
+    {
+      if (n_press == 1)
+        g_signal_emit (day, day_signals[SELECTED], 0);
+    }
+}
+
+static gboolean
+gtk_date_chooser_day_key_press (GtkWidget   *widget,
+                                GdkEventKey *event)
+{
+  GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
+
+  if (event->keyval == GDK_KEY_space ||
+      event->keyval == GDK_KEY_Return ||
+      event->keyval == GDK_KEY_ISO_Enter||
+      event->keyval == GDK_KEY_KP_Enter ||
+      event->keyval == GDK_KEY_KP_Space)
+    {
+      g_signal_emit (day, day_signals[SELECTED], 0);
+      return TRUE;
+    }
+
+  if (GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->key_press_event (widget, event))
+    return TRUE;
+
+ return FALSE;
+}
+
+static void
+gtk_date_chooser_day_init (GtkDateChooserDay *day)
+{
+  GtkWidget *widget = GTK_WIDGET (day);
+
+  gtk_widget_set_can_focus (widget, TRUE);
+  gtk_style_context_add_class (gtk_widget_get_style_context (widget), "day");
+
+  day->label = gtk_label_new ("");
+  gtk_widget_show (day->label);
+  gtk_widget_set_hexpand (day->label, TRUE);
+  gtk_widget_set_vexpand (day->label, TRUE);
+  gtk_label_set_xalign (GTK_LABEL (day->label), 1.0);
+
+  gtk_container_add (GTK_CONTAINER (day), day->label);
+
+  day->multipress_gesture = gtk_gesture_multi_press_new (widget);
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (day->multipress_gesture), 0);
+  g_signal_connect (day->multipress_gesture, "pressed",
+                    G_CALLBACK (day_pressed), day);
+}
+
+static void
+gtk_date_chooser_day_dispose (GObject *object)
+{
+  GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (object);
+
+  g_clear_object (&day->multipress_gesture);
+
+  G_OBJECT_CLASS (gtk_date_chooser_day_parent_class)->dispose (object);
+}
+
+static gboolean
+gtk_date_chooser_day_draw (GtkWidget *widget,
+                           cairo_t   *cr)
+{
+  GtkStyleContext *context;
+  GtkStateFlags state;
+  gint x, y, width, height;
+
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_widget_get_state_flags (widget);
+
+  x = 0;
+  y = 0;
+  width = gtk_widget_get_allocated_width (widget);
+  height = gtk_widget_get_allocated_height (widget);
+
+  gtk_render_background (context, cr, x, y, width, height);
+  gtk_render_frame (context, cr, x, y, width, height);
+
+  GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->draw (widget, cr);
+
+  if (gtk_widget_has_visible_focus (widget))
+    {
+      GtkBorder border;
+
+      gtk_style_context_get_border (context, state, &border);
+      gtk_render_focus (context, cr, border.left, border.top,
+                        gtk_widget_get_allocated_width (widget) - border.left - border.right,
+                        gtk_widget_get_allocated_height (widget) - border.top - border.bottom);
+    }
+
+  return FALSE;
+}
+
+static void
+gtk_date_chooser_day_map (GtkWidget *widget)
+{
+  GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
+
+  GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->map (widget);
+
+  gdk_window_show (day->event_window);
+}
+
+static void
+gtk_date_chooser_day_unmap (GtkWidget *widget)
+{
+  GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
+
+  gdk_window_hide (day->event_window);
+
+  GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->unmap (widget);
+}
+
+static void
+gtk_date_chooser_day_realize (GtkWidget *widget)
+{
+  GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
+  GtkAllocation allocation;
+  GdkWindow *window;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  gtk_widget_get_allocation (widget, &allocation);
+  gtk_widget_set_realized (widget, TRUE);
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = allocation.x;
+  attributes.y = allocation.y;
+  attributes.width = allocation.width;
+  attributes.height = allocation.height;
+  attributes.wclass = GDK_INPUT_ONLY;
+  attributes.event_mask = gtk_widget_get_events (widget);
+  attributes.event_mask |= GDK_BUTTON_PRESS_MASK
+                           | GDK_BUTTON_RELEASE_MASK
+                           | GDK_TOUCH_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+  window = gtk_widget_get_parent_window (widget);
+  gtk_widget_set_window (widget, window);
+  g_object_ref (window);
+
+  day->event_window = gdk_window_new (window, &attributes, attributes_mask);
+  gtk_widget_register_window (widget, day->event_window);
+}
+
+static void
+gtk_date_chooser_day_unrealize (GtkWidget *widget)
+{
+  GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
+
+  if (day->event_window)
+    {
+      gtk_widget_unregister_window (widget, day->event_window);
+      gdk_window_destroy (day->event_window);
+      day->event_window = NULL;
+    }
+
+  GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->unrealize (widget);
+}
+
+static void
+gtk_date_chooser_day_size_allocate (GtkWidget     *widget,
+                                    GtkAllocation *allocation)
+{
+  GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
+
+  GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->size_allocate (widget, allocation);
+
+  if (gtk_widget_get_realized (widget))
+    gdk_window_move_resize (day->event_window,
+                            allocation->x,
+                            allocation->y,
+                            allocation->width,
+                            allocation->height);
+}
+
+static void
+gtk_date_chooser_day_drag_data_get (GtkWidget        *widget,
+                                    GdkDragContext   *context,
+                                    GtkSelectionData *selection_data,
+                                    guint             info,
+                                    guint             time)
+{
+  GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
+  GDateTime *dt;
+  gchar *text;
+
+  dt = g_date_time_new_local (day->year, day->month + 1 , day->day, 1, 1, 1);
+  text = g_date_time_format (dt, "%x");
+  gtk_selection_data_set_text (selection_data, text, -1);
+  g_free (text);
+  g_date_time_unref (dt);
+}
+
+static void
+gtk_date_chooser_day_class_init (GtkDateChooserDayClass *class)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->dispose = gtk_date_chooser_day_dispose;
+
+  widget_class->draw = gtk_date_chooser_day_draw;
+  widget_class->realize = gtk_date_chooser_day_realize;
+  widget_class->unrealize = gtk_date_chooser_day_unrealize;
+  widget_class->map = gtk_date_chooser_day_map;
+  widget_class->unmap = gtk_date_chooser_day_unmap;
+  widget_class->key_press_event = gtk_date_chooser_day_key_press;
+  widget_class->size_allocate = gtk_date_chooser_day_size_allocate;
+  widget_class->drag_data_get = gtk_date_chooser_day_drag_data_get;
+
+  day_signals[SELECTED] = g_signal_new (I_("selected"),
+                                        G_OBJECT_CLASS_TYPE (object_class),
+                                        G_SIGNAL_RUN_FIRST,
+                                        0,
+                                        NULL, NULL,
+                                        NULL,
+                                        G_TYPE_NONE, 0);
+}
+
+GtkWidget *
+gtk_date_chooser_day_new (void)
+{
+  return GTK_WIDGET (g_object_new (GTK_TYPE_DATE_CHOOSER_DAY, NULL));
+}
+
+void
+gtk_date_chooser_day_set_date (GtkDateChooserDay *day,
+                               guint              y,
+                               guint              m,
+                               guint              d)
+{
+  gchar *text;
+
+  day->year = y;
+  day->month = m;
+  day->day = d;
+
+  text = g_strdup_printf ("%d", day->day);
+  gtk_label_set_label (GTK_LABEL (day->label), text);
+  g_free (text);
+}
+
+void
+gtk_date_chooser_day_get_date (GtkDateChooserDay *day,
+                               guint             *y,
+                               guint             *m,
+                               guint             *d)
+{
+  *y = day->year;
+  *m = day->month;
+  *d = day->day;
+}
+
+void
+gtk_date_chooser_day_set_other_month (GtkDateChooserDay *day,
+                                      gboolean           other_month)
+{
+  GtkStyleContext *context;
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (day));
+  if (other_month)
+    {
+      gtk_style_context_add_class (context, "other-month");
+      gtk_drag_source_unset (GTK_WIDGET (day));
+    }
+  else
+    {
+      gtk_style_context_remove_class (context, "other-month");
+      gtk_drag_source_set (GTK_WIDGET (day),
+                           GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+                           NULL, 0,
+                           GDK_ACTION_COPY);
+      gtk_drag_source_add_text_targets (GTK_WIDGET (day));
+    }
+}
+
+void
+gtk_date_chooser_day_set_selected (GtkDateChooserDay *day,
+                                   gboolean           selected)
+{
+  if (selected)
+    gtk_widget_set_state_flags (GTK_WIDGET (day), GTK_STATE_FLAG_SELECTED, FALSE);
+  else
+    gtk_widget_unset_state_flags (GTK_WIDGET (day), GTK_STATE_FLAG_SELECTED);
+}
diff --git a/gtk/gtkdatechooserdayprivate.h b/gtk/gtkdatechooserdayprivate.h
new file mode 100644
index 0000000..768dd74
--- /dev/null
+++ b/gtk/gtkdatechooserdayprivate.h
@@ -0,0 +1,53 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_DATE_CHOOSER_DAY_PRIVATE_H__
+#define __GTK_DATE_CHOOSER_DAY_PRIVATE_H__
+
+#include <gtk/gtkbin.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_DATE_CHOOSER_DAY                  (gtk_date_chooser_day_get_type ())
+#define GTK_DATE_CHOOSER_DAY(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_TYPE_DATE_CHOOSER_DAY, GtkDateChooserDay))
+#define GTK_DATE_CHOOSER_DAY_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), 
GTK_TYPE_DATE_CHOOSER_DAY, GtkDateChooserDayClass))
+#define GTK_IS_DATE_CHOOSER_DAY(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_TYPE_DATE_CHOOSER_DAY))
+#define GTK_IS_DATE_CHOOSER_DAY_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_TYPE_DATE_CHOOSER_DAY))
+#define GTK_DATE_CHOOSER_DAY_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_TYPE_DATE_CHOOSER_DAY, GtkDateChooserDayClass))
+
+
+typedef struct _GtkDateChooserDay        GtkDateChooserDay;
+typedef struct _GtkDateChooserDayClass   GtkDateChooserDayClass;
+
+GType       gtk_date_chooser_day_get_type        (void) G_GNUC_CONST;
+GtkWidget * gtk_date_chooser_day_new             (void);
+void        gtk_date_chooser_day_set_date        (GtkDateChooserDay *widget,
+                                                  guint              year,
+                                                  guint              month,
+                                                  guint              day);
+void        gtk_date_chooser_day_get_date        (GtkDateChooserDay *widget,
+                                                  guint             *year,
+                                                  guint             *month,
+                                                  guint             *day);
+void        gtk_date_chooser_day_set_other_month (GtkDateChooserDay *day,
+                                                  gboolean           other_month);
+void        gtk_date_chooser_day_set_selected    (GtkDateChooserDay *day,
+                                                  gboolean           selected);
+
+G_END_DECLS
+
+#endif /* __GTK_DATE_CHOOSER_DAY_PRIVATE_H__ */
diff --git a/gtk/gtkdatechooserwidget.c b/gtk/gtkdatechooserwidget.c
new file mode 100644
index 0000000..b834c61
--- /dev/null
+++ b/gtk/gtkdatechooserwidget.c
@@ -0,0 +1,1005 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkdatechooserwidget.h"
+#include "gtkdatechooserdayprivate.h"
+#include "gtkstack.h"
+#include "gtklabel.h"
+#include "gtkgrid.h"
+#include "gtkselection.h"
+#include "gtkdnd.h"
+
+#include "gtkintl.h"
+#include "gtkprivate.h"
+
+#include <stdlib.h>
+#include <langinfo.h>
+
+
+enum {
+  MONTH_CHANGED,
+  DAY_SELECTED,
+  LAST_SIGNAL
+};
+
+static guint calendar_signals[LAST_SIGNAL] = { 0, };
+
+enum {
+  PROP_YEAR = 1,
+  PROP_MONTH,
+  PROP_DAY,
+  PROP_SHOW_HEADING,
+  PROP_SHOW_DAY_NAMES,
+  PROP_SHOW_WEEK_NUMBERS,
+  PROP_NO_MONTH_CHANGE,
+  NUM_PROPERTIES
+};
+
+static GParamSpec *calendar_properties[NUM_PROPERTIES] = { NULL, };
+
+struct _GtkDateChooserWidget
+{
+  GtkBin parent;
+
+  GtkWidget *month_down_button;
+  GtkWidget *month_stack;
+  GtkWidget *month_up_button;
+  GtkWidget *year_down_button;
+  GtkWidget *year_stack;
+  GtkWidget *odd_year_label;
+  GtkWidget *even_year_label;
+  GtkWidget *year_up_button;
+  GtkWidget *grid;
+
+  guint      click_id;
+  GtkWidget *active_button;
+
+  guint      year_id;
+  GtkWidget *active_year_button;
+
+  GtkWidget *day_grid;
+  GtkWidget *corner;
+  GtkWidget *cols[7];
+  GtkWidget *rows[6];
+  GtkWidget *days[6][7];
+
+  guint month;
+  guint year;
+  guint day;
+
+  gint week_start;
+
+  gboolean show_heading;
+  gboolean show_day_names;
+  gboolean show_week_numbers;
+  gboolean no_month_change;
+};
+
+struct _GtkDateChooserWidgetClass
+{
+  GtkBinClass parent_class;
+};
+
+
+G_DEFINE_TYPE (GtkDateChooserWidget, gtk_date_chooser_widget, GTK_TYPE_BIN)
+
+static const guint month_length[2][13] =
+{
+  { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+  { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static gboolean
+leap (guint year)
+{
+  return ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
+}
+
+static guint
+day_of_week (guint year, guint mm, guint dd)
+{
+  GDateTime *dt;
+  guint days;
+
+  dt = g_date_time_new_local (year, mm, dd, 1, 1, 1);
+  if (dt == NULL)
+    return 0;
+
+  days = g_date_time_get_day_of_week (dt);
+  g_date_time_unref (dt);
+
+  return days;
+}
+
+static guint
+week_of_year (guint year, guint mm, guint dd)
+{
+  GDateTime *dt;
+  guint week;
+
+  dt = g_date_time_new_local (year, mm, dd, 1, 1, 1);
+  if (dt == NULL)
+    return 1;
+
+  week = g_date_time_get_week_of_year (dt);
+  g_date_time_unref (dt);
+
+  return week;
+}
+
+static void
+calendar_compute_days (GtkDateChooserWidget *calendar)
+{
+  guint year, month, day;
+  gint ndays_in_month;
+  gint ndays_in_prev_month;
+  gint first_day;
+  gint row;
+  gint col;
+  GtkDateChooserDay *d;
+
+  year = calendar->year;
+  month = calendar->month + 1;
+
+  ndays_in_month = month_length[leap (year)][month];
+
+  first_day = day_of_week (year, month, 1);
+  first_day = (first_day + 7 - calendar->week_start) % 7;
+  if (first_day == 0)
+    first_day = 7;
+
+  /* Compute days of previous month */
+  if (month > 1)
+    ndays_in_prev_month = month_length[leap (year)][month - 1];
+  else
+    ndays_in_prev_month = month_length[leap (year - 1)][12];
+  day = ndays_in_prev_month - first_day + 1;
+
+  month = (calendar->month + 11) % 12;
+  year = calendar->year - (month == 11);
+
+  for (col = 0; col < first_day; col++)
+    {
+      d = GTK_DATE_CHOOSER_DAY (calendar->days[0][col]);
+      gtk_date_chooser_day_set_date (d, year, month, day);
+      gtk_date_chooser_day_set_other_month (d, TRUE);
+      day++;
+    }
+
+  /* Compute days of current month */
+  row = first_day / 7;
+  col = first_day % 7;
+
+  month = calendar->month;
+  year = calendar->year;
+
+  for (day = 1; day <= ndays_in_month; day++)
+    {
+      d = GTK_DATE_CHOOSER_DAY (calendar->days[row][col]);
+      gtk_date_chooser_day_set_date (d, year, month, day);
+      gtk_date_chooser_day_set_other_month (d, FALSE);
+
+      col++;
+      if (col == 7)
+        {
+          row++;
+          col = 0;
+        }
+    }
+
+  /* Compute days of next month */
+
+  month = (calendar->month + 1) % 12;
+  year = calendar->year + (month == 0);
+
+  day = 1;
+  for (; row < 6; row++)
+    {
+      for (; col < 7; col++)
+        {
+          d = GTK_DATE_CHOOSER_DAY (calendar->days[row][col]);
+          gtk_date_chooser_day_set_date (d, year, month, day);
+          gtk_date_chooser_day_set_other_month (d, TRUE);
+          day++;
+        }
+      col = 0;
+    }
+
+  /* update week numbers */
+  for (row = 0; row < 6; row++)
+    {
+      gchar *text;
+      guint week;
+
+      d = GTK_DATE_CHOOSER_DAY (calendar->days[row][6]);
+      gtk_date_chooser_day_get_date (d, &year, &month, &day);
+
+      week = week_of_year (year, month + 1, day);
+      text = g_strdup_printf ("%d", week);
+      gtk_label_set_label (GTK_LABEL (calendar->rows[row]), text);
+      g_free (text);
+    }
+}
+
+static const gchar *months[] = {
+  "jan", "feb", "mar", "apr",
+  "may", "jun", "jul", "aug",
+  "sep", "oct", "nov", "dec"
+};
+
+static void
+calendar_update_year_display (GtkDateChooserWidget *calendar)
+{
+  GtkWidget *label;
+  gchar *text;
+  GtkWidget *vis;
+
+  vis = gtk_stack_get_visible_child (GTK_STACK (calendar->year_stack));
+  if (vis == calendar->odd_year_label)
+    label = calendar->even_year_label;
+  else
+    label = calendar->odd_year_label;
+
+  text = g_strdup_printf ("%d", calendar->year);
+  gtk_label_set_label (GTK_LABEL (label), text);
+  g_free (text);
+
+  gtk_stack_set_visible_child (GTK_STACK (calendar->year_stack), label);
+}
+
+static void
+calendar_update_month_display (GtkDateChooserWidget   *calendar,
+                               GtkStackTransitionType  transition)
+{
+  gtk_stack_set_visible_child_full (GTK_STACK (calendar->month_stack),
+                                    months[calendar->month],
+                                    transition);
+}
+
+static gchar *
+calendar_get_weekday_name (gint i)
+{
+  time_t time;
+  gchar buffer[128];
+
+  time = (i + 3) * 86400;
+  strftime (buffer, sizeof (buffer), "%a", gmtime (&time));
+  return g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
+}
+
+static gchar *
+calendar_get_month_name (gint i)
+{
+  time_t time;
+  gchar buffer[128];
+
+  time = i * 2764800;
+  strftime (buffer, sizeof (buffer), "%B", gmtime (&time));
+  return g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
+}
+
+static void
+calendar_init_weekday_display (GtkDateChooserWidget *calendar)
+{
+  gint i;
+  gchar *text;
+
+  for (i = 0; i < 7; i++)
+    {
+      text = calendar_get_weekday_name ((i + calendar->week_start) % 7);
+      gtk_label_set_label (GTK_LABEL (calendar->cols[i]), text);
+      g_free (text);
+    }
+}
+
+static void
+calendar_init_month_display (GtkDateChooserWidget *calendar)
+{
+  gint i;
+  GtkWidget *label;
+  gchar *text;
+
+  for (i = 0; i < 12; i++)
+    {
+      text = calendar_get_month_name (i);
+      label = gtk_label_new (text);
+      gtk_widget_show (label);
+      gtk_stack_add_named (GTK_STACK (calendar->month_stack), label, months[i]);
+      g_free (text);
+    }
+}
+
+static void
+calendar_update_selected_day_display (GtkDateChooserWidget *calendar)
+{
+  gint row, col;
+  GtkDateChooserDay *d;
+  guint year, month, day;
+
+  for (row = 0; row < 6; row++)
+    for (col = 0; col < 7; col++)
+      {
+        d = GTK_DATE_CHOOSER_DAY (calendar->days[row][col]);
+        gtk_date_chooser_day_get_date (d, &year, &month, &day);
+        gtk_date_chooser_day_set_selected (d, day == calendar->day &&
+                                              month == calendar->month &&
+                                              year == calendar->year);
+      }
+}
+
+static void
+calendar_update_selected_day (GtkDateChooserWidget *calendar)
+{
+  gint month_len;
+
+  month_len = month_length[leap (calendar->year)][calendar->month + 1];
+
+  if (month_len < calendar->day)
+    gtk_date_chooser_widget_select_day (calendar, month_len);
+  else
+    calendar_update_selected_day_display (calendar);
+}
+
+static void
+calendar_set_year_prev (GtkDateChooserWidget *calendar)
+{
+  if (calendar->no_month_change)
+    return;
+
+  calendar->year -= 1;
+  calendar_update_year_display (calendar);
+
+  calendar_compute_days (calendar);
+
+  g_signal_emit (calendar, calendar_signals[MONTH_CHANGED], 0);
+
+  calendar_update_selected_day (calendar);
+}
+
+static void
+calendar_set_year_next (GtkDateChooserWidget *calendar)
+{
+  if (calendar->no_month_change)
+    return;
+
+  calendar->year += 1;
+  calendar_update_year_display (calendar);
+
+  calendar_compute_days (calendar);
+
+  g_signal_emit (calendar, calendar_signals[MONTH_CHANGED], 0);
+
+  calendar_update_selected_day (calendar);
+}
+
+static void
+calendar_set_month_prev (GtkDateChooserWidget *calendar)
+{
+  if (calendar->no_month_change)
+    return;
+
+  calendar->month = (calendar->month + 11) % 12;
+  calendar_update_month_display (calendar, GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
+
+  if (calendar->month == 11)
+    {
+      calendar->year -= 1;
+      calendar_update_year_display (calendar);
+    }
+
+  calendar_compute_days (calendar);
+
+  g_signal_emit (calendar, calendar_signals[MONTH_CHANGED], 0);
+
+  calendar_update_selected_day (calendar);
+}
+
+static void
+calendar_set_month_next (GtkDateChooserWidget *calendar)
+{
+  if (calendar->no_month_change)
+    return;
+
+  calendar->month = (calendar->month + 1) % 12;
+  calendar_update_month_display (calendar, GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+
+  if (calendar->month == 0)
+    {
+      calendar->year += 1;
+      calendar_update_year_display (calendar);
+    }
+
+  calendar_compute_days (calendar);
+
+  g_signal_emit (calendar, calendar_signals[MONTH_CHANGED], 0);
+
+  calendar_update_selected_day (calendar);
+}
+
+static gint
+calendar_get_week_start (void)
+{
+  union { unsigned int word; char *string; } langinfo;
+  gint week_1stday = 0;
+  gint first_weekday = 1;
+  guint week_origin;
+
+  langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
+  first_weekday = langinfo.string[0];
+  langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
+  week_origin = langinfo.word;
+  if (week_origin == 19971130) /* Sunday */
+    week_1stday = 0;
+  else if (week_origin == 19971201) /* Monday */
+    week_1stday = 1;
+  else
+    g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
+
+  return (week_1stday + first_weekday - 1) % 7;
+}
+
+static void
+day_selected_cb (GtkDateChooserDay    *d,
+                 GtkDateChooserWidget *calendar)
+{
+  guint year, month, day;
+
+  gtk_date_chooser_day_get_date (d, &year, &month, &day);
+
+  if ((month + 1) % 12 == calendar->month)
+    calendar_set_month_prev (calendar);
+  else if ((calendar->month + 1) % 12 == month)
+    calendar_set_month_next (calendar);
+
+  gtk_date_chooser_widget_select_day (calendar, day);
+}
+
+static void
+gtk_date_chooser_widget_init (GtkDateChooserWidget *calendar)
+{
+  gint row, col;
+  GDateTime *now;
+
+  calendar->show_heading = TRUE;
+  calendar->show_day_names = TRUE;
+  calendar->show_week_numbers = TRUE;
+  calendar->no_month_change = FALSE;
+
+  now = g_date_time_new_now_local ();
+  calendar->year = g_date_time_get_year (now);
+  calendar->month = g_date_time_get_month (now) - 1;
+  calendar->day = g_date_time_get_day_of_month (now);
+  g_date_time_unref (now);
+
+  calendar->week_start = calendar_get_week_start ();
+
+  gtk_widget_init_template (GTK_WIDGET (calendar));
+
+  for (col = 0; col < 7; col++)
+    {
+      calendar->cols[col] = gtk_label_new ("");
+      g_object_bind_property (calendar, "show-day-names",
+                              calendar->cols[col], "visible",
+                              G_BINDING_SYNC_CREATE);
+      gtk_style_context_add_class (gtk_widget_get_style_context (calendar->cols[col]), "weekday");
+      gtk_grid_attach (GTK_GRID (calendar->grid), calendar->cols[col], col, -1, 1, 1);
+    }
+
+  for (row = 0; row < 6; row++)
+    {
+      calendar->rows[row] = gtk_label_new ("");
+      g_object_bind_property (calendar, "show-week-numbers",
+                              calendar->rows[row], "visible",
+                              G_BINDING_SYNC_CREATE);
+      gtk_label_set_xalign (GTK_LABEL (calendar->rows[row]), 1.0);
+      gtk_widget_show (calendar->rows[row]);
+      gtk_style_context_add_class (gtk_widget_get_style_context (calendar->rows[row]), "weeknum");
+      gtk_grid_attach (GTK_GRID (calendar->grid), calendar->rows[row], -1, row, 1, 1);
+    }
+
+  calendar->corner = gtk_label_new ("");
+  gtk_style_context_add_class (gtk_widget_get_style_context (calendar->corner), "weekday");
+  gtk_grid_attach (GTK_GRID (calendar->grid), calendar->corner, -1, -1, 1, 1);
+
+  calendar->day_grid = gtk_grid_new ();
+  gtk_widget_show (calendar->day_grid);
+  gtk_widget_set_halign (calendar->day_grid, GTK_ALIGN_FILL);
+  gtk_widget_set_valign (calendar->day_grid, GTK_ALIGN_FILL);
+  gtk_grid_attach (GTK_GRID (calendar->grid), calendar->day_grid, 0, 0, 7, 6);
+
+  for (row = 0; row < 6; row++)
+    for (col = 0; col < 7; col++)
+      {
+        calendar->days[row][col] = gtk_date_chooser_day_new ();
+        g_signal_connect (calendar->days[row][col], "selected", G_CALLBACK (day_selected_cb), calendar);
+        gtk_widget_show (calendar->days[row][col]);
+        gtk_grid_attach (GTK_GRID (calendar->day_grid), calendar->days[row][col], col, row, 1, 1);
+      }
+
+  calendar_init_month_display (calendar);
+  calendar_init_weekday_display (calendar);
+
+  calendar_compute_days (calendar);
+  calendar_update_month_display (calendar, GTK_STACK_TRANSITION_TYPE_NONE);
+  calendar_update_year_display (calendar);
+  calendar_update_selected_day_display (calendar);
+
+  gtk_drag_dest_set (GTK_WIDGET (calendar), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
+  gtk_drag_dest_add_text_targets (GTK_WIDGET (calendar));
+}
+
+static void
+calendar_set_property (GObject      *obj,
+                       guint         property_id,
+                       const GValue *value,
+                       GParamSpec   *pspec)
+{
+  GtkDateChooserWidget *calendar = GTK_DATE_CHOOSER_WIDGET (obj);
+
+  switch (property_id)
+    {
+    case PROP_YEAR:
+      gtk_date_chooser_widget_set_date (calendar, g_value_get_int (value), calendar->month, calendar->day);
+      break;
+    case PROP_MONTH:
+      gtk_date_chooser_widget_set_date (calendar, calendar->year, g_value_get_int (value), calendar->day);
+      break;
+    case PROP_DAY:
+      gtk_date_chooser_widget_set_date (calendar, calendar->year, calendar->month, g_value_get_int (value));
+      break;
+    case PROP_SHOW_HEADING:
+      gtk_date_chooser_widget_set_show_heading (calendar, g_value_get_boolean (value));
+      break;
+    case PROP_SHOW_DAY_NAMES:
+      gtk_date_chooser_widget_set_show_day_names (calendar, g_value_get_boolean (value));
+      break;
+    case PROP_SHOW_WEEK_NUMBERS:
+      gtk_date_chooser_widget_set_show_week_numbers (calendar, g_value_get_boolean (value));
+      break;
+    case PROP_NO_MONTH_CHANGE:
+      gtk_date_chooser_widget_set_no_month_change (calendar, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+      break;
+    }
+}
+
+static void
+calendar_get_property (GObject    *obj,
+                       guint       property_id,
+                       GValue     *value,
+                       GParamSpec *pspec)
+{
+  GtkDateChooserWidget *calendar = GTK_DATE_CHOOSER_WIDGET (obj);
+
+  switch (property_id)
+    {
+    case PROP_YEAR:
+      g_value_set_int (value, calendar->year);
+      break;
+    case PROP_MONTH:
+      g_value_set_int (value, calendar->month);
+      break;
+    case PROP_DAY:
+      g_value_set_int (value, calendar->day);
+      break;
+    case PROP_SHOW_HEADING:
+      g_value_set_boolean (value, calendar->show_heading);
+      break;
+    case PROP_SHOW_DAY_NAMES:
+      g_value_set_boolean (value, calendar->show_day_names);
+      break;
+    case PROP_SHOW_WEEK_NUMBERS:
+      g_value_set_boolean (value, calendar->show_week_numbers);
+      break;
+    case PROP_NO_MONTH_CHANGE:
+      g_value_set_boolean (value, calendar->no_month_change);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+      break;
+    }
+}
+
+static gboolean
+calendar_button_activate (GtkDateChooserWidget *calendar,
+                          GtkWidget            *button)
+{
+  if (button == calendar->month_down_button)
+    calendar_set_month_prev (calendar);
+  else if (button == calendar->month_up_button)
+    calendar_set_month_next (calendar);
+  else if (button == calendar->year_down_button)
+    calendar_set_year_prev (calendar);
+  else if (button == calendar->year_up_button)
+    calendar_set_year_next (calendar);
+  else
+    g_assert_not_reached ();
+
+  return TRUE;
+}
+
+static gboolean
+calendar_button_timeout (gpointer user_data)
+{
+  GtkDateChooserWidget *calendar = GTK_DATE_CHOOSER_WIDGET (user_data);
+  gboolean res;
+
+  if (calendar->click_id == 0)
+    return G_SOURCE_REMOVE;
+
+  res = calendar_button_activate (calendar, calendar->active_button);
+  if (!res)
+    {
+      g_source_remove (calendar->click_id);
+      calendar->click_id = 0;
+    }
+
+  return res;
+}
+
+static gboolean
+calendar_button_press_cb (GtkWidget            *widget,
+                          GdkEventButton       *button,
+                          GtkDateChooserWidget *calendar)
+{
+  gint double_click_time;
+
+  g_object_get (gtk_widget_get_settings (widget),
+                "gtk-double-click-time", &double_click_time,
+                NULL);
+
+  if (calendar->click_id != 0)
+    g_source_remove (calendar->click_id);
+
+  calendar->active_button = widget;
+
+  calendar->click_id = gdk_threads_add_timeout (double_click_time,
+                                            calendar_button_timeout,
+                                            calendar);
+  g_source_set_name_by_id (calendar->click_id, "[gtk+] calendar_button_timeout");
+  calendar_button_timeout (calendar);
+
+  return TRUE;
+}
+
+static gboolean
+calendar_button_release_cb (GtkWidget      *widget,
+                            GdkEventButton *event,
+                            GtkDateChooserWidget *calendar)
+{
+  if (calendar->click_id != 0)
+    {
+      g_source_remove (calendar->click_id);
+      calendar->click_id = 0;
+    }
+
+  calendar->active_button = NULL;
+
+  return TRUE;
+}
+
+static void
+calendar_clicked_cb (GtkWidget  *widget,
+                     GtkDateChooserWidget *calendar)
+{
+  if (calendar->click_id != 0)
+    return;
+
+  calendar_button_activate (calendar, widget);
+}
+
+static void
+calendar_dispose (GObject *object)
+{
+  GtkDateChooserWidget *calendar = GTK_DATE_CHOOSER_WIDGET (object);
+
+  if (calendar->click_id != 0)
+    {
+      g_source_remove (calendar->click_id);
+      calendar->click_id = 0;
+    }
+
+  G_OBJECT_CLASS (gtk_date_chooser_widget_parent_class)->dispose (object);
+}
+
+static void
+calendar_drag_data_received (GtkWidget        *widget,
+                             GdkDragContext   *context,
+                             gint              x,
+                             gint              y,
+                             GtkSelectionData *selection_data,
+                             guint             info,
+                             guint             time)
+{
+  GtkDateChooserWidget *calendar = GTK_DATE_CHOOSER_WIDGET (widget);
+  gchar *text;
+  GDate *date;
+  guint year, month, day;
+
+  date = g_date_new ();
+  text = (gchar *)gtk_selection_data_get_text (selection_data);
+  if (text)
+    {
+      g_date_set_parse (date, text);
+      g_free (text);
+    }
+  if (!g_date_valid (date))
+    {
+      g_date_free (date);
+      gtk_drag_finish (context, FALSE, FALSE, time);
+      return;
+    }
+
+  year = g_date_get_year (date);
+  month = g_date_get_month (date) - 1;
+  day = g_date_get_day (date);
+
+  g_date_free (date);
+
+  gtk_drag_finish (context, TRUE, FALSE, time);
+
+  if (!calendar->show_heading || calendar->no_month_change)
+    {
+      year = calendar->year;
+      month = calendar->month;
+    }
+  gtk_date_chooser_widget_set_date (calendar, year, month, day);
+}
+
+static void
+gtk_date_chooser_widget_class_init (GtkDateChooserWidgetClass *class)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->dispose = calendar_dispose;
+  object_class->set_property = calendar_set_property;
+  object_class->get_property = calendar_get_property;
+
+  widget_class->drag_data_received = calendar_drag_data_received;
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkdatechooserwidget.ui");
+
+  calendar_properties[PROP_YEAR] =
+      g_param_spec_int ("year", P_("Year"), P_("The selected year"),
+                        0, G_MAXINT >> 9, 0,
+                        G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  calendar_properties[PROP_MONTH] =
+      g_param_spec_int ("month", P_("Month"), P_("The selected month (as a number between 0 and 11)"),
+                        0, 11, 0,
+                        G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  calendar_properties[PROP_DAY] =
+      g_param_spec_int ("day", P_("Day"), P_("The selected day (as a number between 1 and 31, or 0 to 
unselect the currently selected day)"),
+                        0, 31, 0,
+                        G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  calendar_properties[PROP_SHOW_HEADING] =
+      g_param_spec_boolean ("show-heading", P_("Show Heading"), P_("If TRUE, a heading is displayed"),
+                            TRUE, G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  calendar_properties[PROP_SHOW_DAY_NAMES] =
+      g_param_spec_boolean ("show-day-names", P_("Show Day Names"), P_("If TRUE, day names are displayed"),
+                            TRUE, G_PARAM_READWRITE);
+  calendar_properties[PROP_SHOW_WEEK_NUMBERS] =
+      g_param_spec_boolean ("show-week-numbers", P_("Show Week Numbers"), P_("If TRUE, week numbers are 
displayed"),
+                            TRUE, G_PARAM_READWRITE);
+  calendar_properties[PROP_NO_MONTH_CHANGE] =
+      g_param_spec_boolean ("no-month-change", P_("No Month Change"), P_("If TRUE, the selected month cannot 
be changed"),
+                            FALSE, G_PARAM_READWRITE);
+  g_object_class_install_properties (object_class, NUM_PROPERTIES, calendar_properties);
+
+  calendar_signals[MONTH_CHANGED] = g_signal_new (I_("month-changed"),
+                                                  G_OBJECT_CLASS_TYPE (object_class),
+                                                  G_SIGNAL_RUN_FIRST,
+                                                  0,
+                                                  NULL, NULL,
+                                                  NULL,
+                                                  G_TYPE_NONE, 0);
+  calendar_signals[DAY_SELECTED] = g_signal_new (I_("day-selected"),
+                                                  G_OBJECT_CLASS_TYPE (object_class),
+                                                  G_SIGNAL_RUN_FIRST,
+                                                  0,
+                                                  NULL, NULL,
+                                                  NULL,
+                                                  G_TYPE_NONE, 0);
+
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, month_down_button);
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, month_stack);
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, month_up_button);
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, year_down_button);
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, year_stack);
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, odd_year_label);
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, even_year_label);
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, year_up_button);
+  gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, grid);
+
+  gtk_widget_class_bind_template_callback (widget_class, calendar_button_press_cb);
+  gtk_widget_class_bind_template_callback (widget_class, calendar_button_release_cb);
+  gtk_widget_class_bind_template_callback (widget_class, calendar_clicked_cb);
+}
+
+GtkWidget *
+gtk_date_chooser_widget_new (void)
+{
+  return GTK_WIDGET (g_object_new (GTK_TYPE_DATE_CHOOSER_WIDGET, NULL));
+}
+
+void
+gtk_date_chooser_widget_set_show_heading (GtkDateChooserWidget *calendar,
+                                          gboolean              setting)
+{
+  if (calendar->show_heading == setting)
+    return;
+
+  calendar->show_heading = setting;
+
+  g_object_notify_by_pspec (G_OBJECT (calendar),
+                            calendar_properties[PROP_SHOW_HEADING]);
+}
+
+gboolean
+gtk_date_chooser_widget_get_show_heading (GtkDateChooserWidget *calendar)
+{
+  return calendar->show_heading;
+}
+
+void
+gtk_date_chooser_widget_set_show_day_names (GtkDateChooserWidget *calendar,
+                                            gboolean              setting)
+{
+  if (calendar->show_day_names == setting)
+    return;
+
+  calendar->show_day_names = setting;
+
+  gtk_widget_set_visible (calendar->corner, calendar->show_day_names && calendar->show_week_numbers);
+
+  g_object_notify_by_pspec (G_OBJECT (calendar),
+                            calendar_properties[PROP_SHOW_DAY_NAMES]);
+}
+
+gboolean
+gtk_date_chooser_widget_get_show_day_names (GtkDateChooserWidget *calendar)
+{
+  return calendar->show_day_names;
+}
+
+void
+gtk_date_chooser_widget_set_show_week_numbers (GtkDateChooserWidget *calendar,
+                                               gboolean              setting)
+{
+  if (calendar->show_week_numbers == setting)
+    return;
+
+  calendar->show_week_numbers = setting;
+
+  gtk_widget_set_visible (calendar->corner, calendar->show_day_names && calendar->show_week_numbers);
+
+  g_object_notify_by_pspec (G_OBJECT (calendar),
+                            calendar_properties[PROP_SHOW_WEEK_NUMBERS]);
+}
+
+gboolean
+gtk_date_chooser_widget_get_show_week_numbers (GtkDateChooserWidget *calendar)
+{
+  return calendar->show_week_numbers;
+}
+
+void
+gtk_date_chooser_widget_set_no_month_change (GtkDateChooserWidget *calendar,
+                                             gboolean              setting)
+{
+  if (calendar->no_month_change == setting)
+    return;
+
+  calendar->no_month_change = setting;
+
+  g_object_notify_by_pspec (G_OBJECT (calendar),
+                            calendar_properties[PROP_NO_MONTH_CHANGE]);
+}
+
+gboolean
+gtk_date_chooser_widget_get_no_month_change (GtkDateChooserWidget *calendar)
+{
+  return calendar->no_month_change;
+}
+
+void
+gtk_date_chooser_widget_set_date (GtkDateChooserWidget *calendar,
+                                  guint                 year,
+                                  guint                 month,
+                                  guint                 day)
+{
+  gboolean month_changed = FALSE;
+  gboolean day_changed = FALSE;
+  g_object_freeze_notify (G_OBJECT (calendar));
+
+  if (calendar->year != year)
+    {
+      month_changed = TRUE;
+      calendar->year = year;
+      g_object_notify_by_pspec (G_OBJECT (calendar),
+                                calendar_properties[PROP_YEAR]);
+      calendar_update_year_display (calendar);
+    }
+
+  if (calendar->month != month)
+    {
+      month_changed = TRUE;
+      calendar->month = month;
+      g_object_notify_by_pspec (G_OBJECT (calendar),
+                                calendar_properties[PROP_MONTH]);
+      calendar_update_month_display (calendar, GTK_STACK_TRANSITION_TYPE_NONE);
+    }
+
+  if (calendar->day != day)
+    {
+      day_changed = TRUE;
+      calendar->day = day;
+      g_object_notify_by_pspec (G_OBJECT (calendar),
+                                calendar_properties[PROP_DAY]);
+    }
+
+  if (month_changed)
+    calendar_compute_days (calendar);
+
+  if (month_changed || day_changed)
+    {
+      calendar_update_selected_day (calendar);
+      g_signal_emit (calendar, calendar_signals[DAY_SELECTED], 0);
+    }
+
+  g_object_thaw_notify (G_OBJECT (calendar));
+}
+
+void
+gtk_date_chooser_widget_select_month (GtkDateChooserWidget *calendar,
+                                      guint                 year,
+                                      guint                 month)
+{
+  gtk_date_chooser_widget_set_date (calendar, year, month, calendar->day);
+}
+
+void
+gtk_date_chooser_widget_select_day (GtkDateChooserWidget *calendar,
+                                    guint                 day)
+{
+  if (calendar->day == day)
+    return;
+
+  calendar->day = day;
+
+  calendar_update_selected_day_display (calendar);
+
+  g_signal_emit (calendar, calendar_signals[DAY_SELECTED], 0);
+  g_object_notify_by_pspec (G_OBJECT (calendar),
+                            calendar_properties[PROP_DAY]);
+}
+
+void
+gtk_date_chooser_widget_get_date (GtkDateChooserWidget *calendar,
+                                  guint                *year,
+                                  guint                *month,
+                                  guint                *day)
+{
+  *year = calendar->year;
+  *month = calendar->month;
+  *day = calendar->day;
+}
diff --git a/gtk/gtkdatechooserwidget.h b/gtk/gtkdatechooserwidget.h
new file mode 100644
index 0000000..9cd9f8f
--- /dev/null
+++ b/gtk/gtkdatechooserwidget.h
@@ -0,0 +1,93 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_DATE_CHOOSER_WIDGET_H__
+#define __GTK_DATE_CHOOSER_WIDGET_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkwidget.h>
+
+#ifndef GDK_AVAILABLE_IN_3_20
+#define GDK_AVAILABLE_IN_3_20 _GDK_EXTERN
+#endif
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_DATE_CHOOSER_WIDGET              (gtk_date_chooser_widget_get_type ())
+#define GTK_DATE_CHOOSER_WIDGET(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_TYPE_DATE_CHOOSER_WIDGET, GtkDateChooserWidget))
+#define GTK_DATE_CHOOSER_WIDGET_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), 
GTK_TYPE_DATE_CHOOSER_WIDGET, GtkDateChooserWidgetClass))
+#define GTK_IS_DATE_CHOOSER_WIDGET(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_TYPE_DATE_CHOOSER_WIDGET))
+#define GTK_IS_DATE_CHOOSER_WIDGET_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_TYPE_DATE_CHOOSER_WIDGET))
+#define GTK_DATE_CHOOSER_WIDGET_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_TYPE_DATE_CHOOSER_WIDGET, GtkDateChooserWidgetClass))
+
+typedef struct _GtkDateChooserWidget        GtkDateChooserWidget;
+typedef struct _GtkDateChooserWidgetClass   GtkDateChooserWidgetClass;
+
+GDK_AVAILABLE_IN_3_20
+GType       gtk_date_chooser_widget_get_type              (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_20
+GtkWidget * gtk_date_chooser_widget_new                   (void);
+
+
+GDK_AVAILABLE_IN_3_20
+void        gtk_date_chooser_widget_select_month          (GtkDateChooserWidget *chooser,
+                                                           guint                 year,
+                                                           guint                 month);
+
+GDK_AVAILABLE_IN_3_20
+void        gtk_date_chooser_widget_select_day            (GtkDateChooserWidget *chooser,
+                                                           guint                 day);
+
+GDK_AVAILABLE_IN_3_20
+void        gtk_date_chooser_widget_set_date              (GtkDateChooserWidget *chooser,
+                                                           guint                 year,
+                                                           guint                 month,
+                                                           guint                 day);
+GDK_AVAILABLE_IN_3_20
+void        gtk_date_chooser_widget_get_date              (GtkDateChooserWidget *chooser,
+                                                           guint                *year,
+                                                           guint                *month,
+                                                           guint                *day);
+
+
+void        gtk_date_chooser_widget_set_no_month_change   (GtkDateChooserWidget *chooser,
+                                                           gboolean              setting);
+
+GDK_AVAILABLE_IN_3_20
+void        gtk_date_chooser_widget_set_show_heading      (GtkDateChooserWidget *chooser,
+                                                           gboolean              setting);
+GDK_AVAILABLE_IN_3_20
+gboolean    gtk_date_chooser_widget_get_show_heading      (GtkDateChooserWidget *chooser);
+GDK_AVAILABLE_IN_3_20
+void        gtk_date_chooser_widget_set_show_day_names    (GtkDateChooserWidget *chooser,
+                                                           gboolean              setting);
+GDK_AVAILABLE_IN_3_20
+gboolean    gtk_date_chooser_widget_get_show_day_names    (GtkDateChooserWidget *chooser);
+
+GDK_AVAILABLE_IN_3_20
+void        gtk_date_chooser_widget_set_show_week_numbers (GtkDateChooserWidget *chooser,
+                                                           gboolean              setting);
+GDK_AVAILABLE_IN_3_20
+gboolean    gtk_date_chooser_widget_get_show_week_numbers (GtkDateChooserWidget *chooser);
+
+G_END_DECLS
+
+#endif /* __GTK_DATE_CHOOSER_WIDGET_H__ */
diff --git a/gtk/ui/gtkdatechooserwidget.ui b/gtk/ui/gtkdatechooserwidget.ui
new file mode 100644
index 0000000..3dc7e9f
--- /dev/null
+++ b/gtk/ui/gtkdatechooserwidget.ui
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.1 -->
+<interface>
+  <requires lib="gtk+" version="3.16"/>
+  <template class="GtkDateChooserWidget" parent="GtkBin">
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible" bind-source="GtkDateChooserWidget" bind-property="show-heading" 
bind-flags="sync-create"/>
+            <property name="hexpand">True</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="halign">start</property>
+                <child>
+                  <object class="GtkButton" id="month_down_button">
+                    <property name="visible" bind-source="GtkDateChooserWidget" 
bind-property="no-month-change" bind-flags="sync-create|invert-boolean"/>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="relief">none</property>
+                    <signal name="button-press-event" handler="calendar_button_press_cb"/>
+                    <signal name="button-release-event" handler="calendar_button_release_cb"/>
+                    <signal name="clicked" handler="calendar_clicked_cb"/>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="icon_name">pan-start-symbolic</property>
+                      </object>
+                    </child>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkStack" id="month_stack">
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="month_up_button">
+                    <property name="visible" bind-source="GtkDateChooserWidget" 
bind-property="no-month-change" bind-flags="sync-create|invert-boolean"/>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="relief">none</property>
+                    <signal name="button-press-event" handler="calendar_button_press_cb"/>
+                    <signal name="button-release-event" handler="calendar_button_release_cb"/>
+                    <signal name="clicked" handler="calendar_clicked_cb"/>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="icon_name">pan-end-symbolic</property>
+                      </object>
+                    </child>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="halign">end</property>
+                <child>
+                  <object class="GtkButton" id="year_down_button">
+                    <property name="visible" bind-source="GtkDateChooserWidget" 
bind-property="no-month-change" bind-flags="sync-create|invert-boolean"/>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="relief">none</property>
+                    <signal name="button-press-event" handler="calendar_button_press_cb"/>
+                    <signal name="button-release-event" handler="calendar_button_release_cb"/>
+                    <signal name="clicked" handler="calendar_clicked_cb"/>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="icon_name">pan-start-symbolic</property>
+                      </object>
+                    </child>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkStack" id="year_stack">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkLabel" id="odd_year_label">
+                        <property name="visible">True</property>
+                        <property name="label">2015</property>
+                      </object>
+                      <packing>
+                        <property name="name">odd</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="even_year_label">
+                        <property name="visible">True</property>
+                        <property name="label">2016</property>
+                      </object>
+                      <packing>
+                        <property name="name">even</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="year_up_button">
+                    <property name="visible" bind-source="GtkDateChooserWidget" 
bind-property="no-month-change" bind-flags="sync-create|invert-boolean"/>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="relief">none</property>
+                    <signal name="button-press-event" handler="calendar_button_press_cb"/>
+                    <signal name="button-release-event" handler="calendar_button_release_cb"/>
+                    <signal name="clicked" handler="calendar_clicked_cb"/>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="icon_name">pan-end-symbolic</property>
+                      </object>
+                    </child>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkGrid" id="grid">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>


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