[gnome-calendar/wip/gbsneto/date-chooser: 1/7] date-chooser: add date chooser widget
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-calendar/wip/gbsneto/date-chooser: 1/7] date-chooser: add date chooser widget
- Date: Sat, 23 Jul 2016 20:46:56 +0000 (UTC)
commit c2dca8a3dcb598e2b6f1c9dfe219fe4870bf4781
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Thu Jul 21 17:00:22 2016 -0300
date-chooser: add date chooser widget
This is mainly a copy-paste from an old branch authored
by Matthias Clasen. The code style was updated to match
Calendar's one, and the theming was improved a little bit.
data/Makefile.am | 2 +
data/calendar.gresource.xml | 2 +
data/theme/gtk-styles.css | 31 ++
data/ui/date-chooser.ui | 54 +++
data/ui/multi-choice.ui | 71 ++++
src/Makefile.am | 6 +
src/gcal-date-chooser-day.c | 443 +++++++++++++++++++++++
src/gcal-date-chooser-day.h | 50 +++
src/gcal-date-chooser.c | 817 +++++++++++++++++++++++++++++++++++++++++++
src/gcal-date-chooser.h | 77 ++++
src/gcal-multi-choice.c | 481 +++++++++++++++++++++++++
src/gcal-multi-choice.h | 51 +++
12 files changed, 2085 insertions(+), 0 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 54b4c98..046d240 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -51,10 +51,12 @@ EXTRA_DIST= \
shell-search-provider-dbus-interfaces.xml \
ui/alarm-row.ui \
ui/calendar-row.ui \
+ ui/date-chooser.ui \
ui/date-selector.ui \
ui/edit-dialog.ui \
ui/help-overlay.ui \
ui/menus.ui \
+ ui/multi-choice.ui \
ui/quick-add-popover.ui \
ui/search-view.ui \
ui/source-dialog.ui \
diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml
index f083f3f..7c64779 100644
--- a/data/calendar.gresource.xml
+++ b/data/calendar.gresource.xml
@@ -3,8 +3,10 @@
<gresource prefix="/org/gnome/calendar">
<file alias="alarm-row.ui" compressed="true" preprocess="xml-stripblanks">ui/alarm-row.ui</file>
<file alias="calendar-row.ui" compressed="true" preprocess="xml-stripblanks">ui/calendar-row.ui</file>
+ <file alias="date-chooser.ui" compressed="true" preprocess="xml-stripblanks">ui/date-chooser.ui</file>
<file alias="date-selector.ui" compressed="true" preprocess="xml-stripblanks">ui/date-selector.ui</file>
<file alias="edit-dialog.ui" compressed="true" preprocess="xml-stripblanks">ui/edit-dialog.ui</file>
+ <file alias="multi-choice.ui" compressed="true" preprocess="xml-stripblanks">ui/multi-choice.ui</file>
<file alias="online-account-row.ui" compressed="true"
preprocess="xml-stripblanks">ui/online-account-row.ui</file>
<file alias="quick-add-popover.ui" compressed="true"
preprocess="xml-stripblanks">ui/quick-add-popover.ui</file>
<file alias="search-view.ui" compressed="true" preprocess="xml-stripblanks">ui/search-view.ui</file>
diff --git a/data/theme/gtk-styles.css b/data/theme/gtk-styles.css
index f0d81c7..f2a8d44 100644
--- a/data/theme/gtk-styles.css
+++ b/data/theme/gtk-styles.css
@@ -290,3 +290,34 @@ button:checked:not(:backdrop) .calendar-color-image,
.sources-button:not(:backdrop):not(:disabled) .calendar-color-image {
-gtk-icon-shadow: 0 1px alpha(black, 0.1);
}
+
+/* Date chooser */
+datechooser .weeknum,
+datechooser .weekday {
+ color: alpha(@theme_fg_color, 0.55);;
+ font-size: 80%;
+}
+
+navigator button {
+ padding: 0;
+}
+
+navigator label {
+ font-weight: bold;
+}
+
+datechooser day {
+ color: @theme_fg_color;
+ min-width: 32px;
+ min-height: 32px;
+}
+
+datechooser day.other-month {
+ color: @theme_insensitive_fg_color;
+}
+
+datechooser day:selected {
+ color: @theme_selected_fg_color;
+ background-color: @theme_selected_bg_color;
+ border-radius: 50%;
+}
diff --git a/data/ui/date-chooser.ui b/data/ui/date-chooser.ui
new file mode 100644
index 0000000..db20343
--- /dev/null
+++ b/data/ui/date-chooser.ui
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.16"/>
+ <template class="GcalDateChooser" parent="GtkBin">
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GcalMultiChoice" id="month_choice">
+ <property name="visible" bind-source="GcalDateChooser" bind-property="show-heading"
bind-flags="sync-create"/>
+ <property name="halign">fill</property>
+ <property name="min-value">0</property>
+ <property name="max-value">11</property>
+ <property name="animate">False</property>
+ <property name="value">0</property>
+ <signal name="notify::value" handler="multi_choice_changed" swapped="yes"/>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GcalMultiChoice" id="year_choice">
+ <property name="visible" bind-source="GcalDateChooser" bind-property="show-heading"
bind-flags="sync-create"/>
+ <property name="halign">fill</property>
+ <property name="min-value">0</property>
+ <property name="max-value">120000</property>
+ <property name="animate">False</property>
+ <property name="value">0</property>
+ <signal name="notify::value" handler="multi_choice_changed" swapped="yes"/>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid">
+ <property name="visible">True</property>
+ <property name="row-homogeneous">True</property>
+ <property name="column-homogeneous">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/multi-choice.ui b/data/ui/multi-choice.ui
new file mode 100644
index 0000000..8376567
--- /dev/null
+++ b/data/ui/multi-choice.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk30">
+ <requires lib="gtk+" version="3.16"/>
+ <template class="GcalMultiChoice" parent="GtkBox">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="down_button">
+ <property name="visible">True</property>
+ <property name="relief">none</property>
+ <signal name="button-press-event" handler="button_press_cb"/>
+ <signal name="button-release-event" handler="button_release_cb"/>
+ <signal name="clicked" handler="button_clicked_cb"/>
+ <style>
+ <class name="image-button"/>
+ <class name="back-button"/>
+ <class name="circular"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">pan-start-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="name">label1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="name">label2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="up_button">
+ <property name="visible">True</property>
+ <property name="relief">none</property>
+ <signal name="button-press-event" handler="button_press_cb"/>
+ <signal name="button-release-event" handler="button_release_cb"/>
+ <signal name="clicked" handler="button_clicked_cb"/>
+ <style>
+ <class name="image-button"/>
+ <class name="forward-button"/>
+ <class name="circular"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">pan-end-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/Makefile.am b/src/Makefile.am
index 3b5f79f..e4f52fd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,6 +28,10 @@ gnome_calendar_SOURCES = \
css-code.h \
gcal-application.h \
gcal-application.c \
+ gcal-date-chooser-day.c \
+ gcal-date-chooser-day.h \
+ gcal-date-chooser.c \
+ gcal-date-chooser.h \
gcal-date-selector.c \
gcal-date-selector.h \
gcal-edit-dialog.c \
@@ -40,6 +44,8 @@ gnome_calendar_SOURCES = \
gcal-manager.h \
gcal-month-view.c \
gcal-month-view.h \
+ gcal-multi-choice.c \
+ gcal-multi-choice.h \
gcal-quick-add-popover.c \
gcal-quick-add-popover.h \
gcal-search-view.h \
diff --git a/src/gcal-date-chooser-day.c b/src/gcal-date-chooser-day.c
new file mode 100644
index 0000000..1f85d14
--- /dev/null
+++ b/src/gcal-date-chooser-day.c
@@ -0,0 +1,443 @@
+/* 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 "gcal-date-chooser-day.h"
+
+#include <stdlib.h>
+#include <langinfo.h>
+
+enum {
+ SELECTED,
+ LAST_DAY_SIGNAL
+};
+
+static guint signals[LAST_DAY_SIGNAL] = { 0, };
+
+struct _GcalDateChooserDay
+{
+ GtkBin parent;
+
+ GtkWidget *label;
+ GDateTime *date;
+ GdkWindow *event_window;
+ GtkGesture *multipress_gesture;
+};
+
+G_DEFINE_TYPE (GcalDateChooserDay, gcal_date_chooser_day, GTK_TYPE_BIN)
+
+static void
+day_pressed (GtkGestureMultiPress *gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GcalDateChooserDay *self)
+{
+ 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 (self, signals[SELECTED], 0);
+ }
+}
+
+static gboolean
+gcal_date_chooser_day_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ GcalDateChooserDay *self = GCAL_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 (self, signals[SELECTED], 0);
+ return TRUE;
+ }
+
+ if (GTK_WIDGET_CLASS (gcal_date_chooser_day_parent_class)->key_press_event (widget, event))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+gcal_date_chooser_day_dispose (GObject *object)
+{
+ GcalDateChooserDay *self = GCAL_DATE_CHOOSER_DAY (object);
+
+ g_clear_object (&self->multipress_gesture);
+
+ G_OBJECT_CLASS (gcal_date_chooser_day_parent_class)->dispose (object);
+}
+
+static gboolean
+gcal_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_style_context_get_state (context);
+
+ 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 (gcal_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
+gcal_date_chooser_day_map (GtkWidget *widget)
+{
+ GcalDateChooserDay *self = GCAL_DATE_CHOOSER_DAY (widget);
+
+ GTK_WIDGET_CLASS (gcal_date_chooser_day_parent_class)->map (widget);
+
+ gdk_window_show (self->event_window);
+}
+
+static void
+gcal_date_chooser_day_unmap (GtkWidget *widget)
+{
+ GcalDateChooserDay *self = GCAL_DATE_CHOOSER_DAY (widget);
+
+ gdk_window_hide (self->event_window);
+
+ GTK_WIDGET_CLASS (gcal_date_chooser_day_parent_class)->unmap (widget);
+}
+
+static void
+gcal_date_chooser_day_realize (GtkWidget *widget)
+{
+ GcalDateChooserDay *self = GCAL_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);
+
+ self->event_window = gdk_window_new (window, &attributes, attributes_mask);
+ gtk_widget_register_window (widget, self->event_window);
+}
+
+static void
+gcal_date_chooser_day_unrealize (GtkWidget *widget)
+{
+ GcalDateChooserDay *self = GCAL_DATE_CHOOSER_DAY (widget);
+
+ if (self->event_window)
+ {
+ gtk_widget_unregister_window (widget, self->event_window);
+ gdk_window_destroy (self->event_window);
+ self->event_window = NULL;
+ }
+
+ GTK_WIDGET_CLASS (gcal_date_chooser_day_parent_class)->unrealize (widget);
+}
+
+static void
+gcal_date_chooser_day_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GcalDateChooserDay *self = GCAL_DATE_CHOOSER_DAY (widget);
+
+ GTK_WIDGET_CLASS (gcal_date_chooser_day_parent_class)->size_allocate (widget, allocation);
+
+ if (gtk_widget_get_realized (widget))
+ {
+ gdk_window_move_resize (self->event_window,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+ }
+}
+
+static void
+gcal_date_chooser_day_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ GcalDateChooserDay *self = GCAL_DATE_CHOOSER_DAY (widget);
+ gchar *text;
+
+ text = g_date_time_format (self->date, "%x");
+ gtk_selection_data_set_text (selection_data, text, -1);
+ g_free (text);
+}
+
+static void
+gcal_date_chooser_day_get_preferred_width (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GcalDateChooserDay *self;
+ GtkStyleContext *context;
+ GtkStateFlags flags;
+ GtkBorder border, margin, padding;
+ gint label_min, label_nat;
+ gint min_width, min, nat;
+
+ self = GCAL_DATE_CHOOSER_DAY (widget);
+ context = gtk_widget_get_style_context (widget);
+ flags = gtk_style_context_get_state (context);
+
+ gtk_style_context_get_border (context, flags, &border);
+ gtk_style_context_get_margin (context, flags, &margin);
+ gtk_style_context_get_padding (context, flags, &padding);
+
+ gtk_style_context_get (context,
+ flags,
+ "min-width", &min_width,
+ NULL);
+
+ gtk_widget_get_preferred_width (self->label, &label_min, &label_nat);
+
+ min = label_min + margin.left + border.left + padding.left + margin.right + border.right + padding.right;
+ nat = label_nat + margin.left + border.left + padding.left + margin.right + border.right + padding.right;
+
+ if (minimum)
+ *minimum = MAX (min, min_width);
+
+ if (natural)
+ *natural = MAX (nat, min_width);
+}
+
+static void
+gcal_date_chooser_day_get_preferred_height (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GcalDateChooserDay *self;
+ GtkStyleContext *context;
+ GtkStateFlags flags;
+ GtkBorder border, margin, padding;
+ gint label_min, label_nat;
+ gint min_height, min, nat;
+
+ self = GCAL_DATE_CHOOSER_DAY (widget);
+ context = gtk_widget_get_style_context (widget);
+ flags = gtk_style_context_get_state (context);
+
+ gtk_style_context_get_border (context, flags, &border);
+ gtk_style_context_get_margin (context, flags, &margin);
+ gtk_style_context_get_padding (context, flags, &padding);
+
+ gtk_style_context_get (context,
+ flags,
+ "min-height", &min_height,
+ NULL);
+
+ gtk_widget_get_preferred_height (self->label, &label_min, &label_nat);
+
+ min = label_min + margin.top + border.top + padding.top + margin.bottom + border.bottom + padding.bottom;
+ nat = label_nat + margin.top + border.top + padding.top + margin.bottom + border.bottom + padding.bottom;
+
+ if (minimum)
+ *minimum = MAX (min, min_height);
+
+ if (natural)
+ *natural = MAX (nat, min_height);
+}
+
+static void
+gcal_date_chooser_day_class_init (GcalDateChooserDayClass *class)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->dispose = gcal_date_chooser_day_dispose;
+
+ widget_class->draw = gcal_date_chooser_day_draw;
+ widget_class->realize = gcal_date_chooser_day_realize;
+ widget_class->unrealize = gcal_date_chooser_day_unrealize;
+ widget_class->map = gcal_date_chooser_day_map;
+ widget_class->unmap = gcal_date_chooser_day_unmap;
+ widget_class->key_press_event = gcal_date_chooser_day_key_press;
+ widget_class->size_allocate = gcal_date_chooser_day_size_allocate;
+ widget_class->drag_data_get = gcal_date_chooser_day_drag_data_get;
+ widget_class->get_preferred_width = gcal_date_chooser_day_get_preferred_width;
+ widget_class->get_preferred_height = gcal_date_chooser_day_get_preferred_height;
+
+ signals[SELECTED] = g_signal_new ("selected",
+ GCAL_TYPE_DATE_CHOOSER_DAY,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ gtk_widget_class_set_css_name (widget_class, "day");
+}
+
+static void
+gcal_date_chooser_day_init (GcalDateChooserDay *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+
+ gtk_widget_set_can_focus (widget, TRUE);
+ gtk_style_context_add_class (gtk_widget_get_style_context (widget), "day");
+
+ self->label = gtk_label_new ("");
+ gtk_widget_show (self->label);
+ gtk_widget_set_halign (self->label, GTK_ALIGN_CENTER);
+ gtk_widget_set_valign (self->label, GTK_ALIGN_CENTER);
+ gtk_widget_set_hexpand (self->label, TRUE);
+ gtk_widget_set_vexpand (self->label, TRUE);
+
+ gtk_container_add (GTK_CONTAINER (self), self->label);
+
+ self->multipress_gesture = gtk_gesture_multi_press_new (widget);
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->multipress_gesture), 0);
+
+ g_signal_connect (self->multipress_gesture,
+ "pressed",
+ G_CALLBACK (day_pressed),
+ self);
+}
+
+GtkWidget*
+gcal_date_chooser_day_new (void)
+{
+ return g_object_new (GCAL_TYPE_DATE_CHOOSER_DAY, NULL);
+}
+
+void
+gcal_date_chooser_day_set_date (GcalDateChooserDay *self,
+ GDateTime *date)
+{
+ gchar *text;
+
+ g_clear_pointer (&self->date, g_date_time_unref);
+ self->date = g_date_time_ref (date);
+
+ text = g_strdup_printf ("%d", g_date_time_get_day_of_month (date));
+ gtk_label_set_label (GTK_LABEL (self->label), text);
+ g_free (text);
+}
+
+GDateTime*
+gcal_date_chooser_day_get_date (GcalDateChooserDay *self)
+{
+ return self->date;
+}
+
+void
+gcal_date_chooser_day_set_other_month (GcalDateChooserDay *self,
+ gboolean other_month)
+{
+ GtkStyleContext *context;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+
+ if (other_month)
+ {
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+ gtk_drag_source_unset (GTK_WIDGET (self));
+ }
+ else
+ {
+ gtk_style_context_remove_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+ gtk_drag_source_set (GTK_WIDGET (self),
+ GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+ NULL, 0,
+ GDK_ACTION_COPY);
+ gtk_drag_source_add_text_targets (GTK_WIDGET (self));
+ }
+}
+
+void
+gcal_date_chooser_day_set_selected (GcalDateChooserDay *self,
+ gboolean selected)
+{
+ if (selected)
+ gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
+ else
+ gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
+}
+
+void
+gcal_date_chooser_day_set_options (GcalDateChooserDay *self,
+ GcalDateChooserDayOptions options)
+{
+ GtkStyleContext *context;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+
+ if (options & GCAL_DATE_CHOOSER_DAY_WEEKEND)
+ gtk_style_context_add_class (context, "weekend");
+ else
+ gtk_style_context_remove_class (context, "weekend");
+
+ if (options & GCAL_DATE_CHOOSER_DAY_HOLIDAY)
+ gtk_style_context_add_class (context, "holiday");
+ else
+ gtk_style_context_remove_class (context, "holiday");
+
+ if (options & GCAL_DATE_CHOOSER_DAY_MARKED)
+ gtk_style_context_add_class (context, "marked");
+ else
+ gtk_style_context_remove_class (context, "marked");
+}
diff --git a/src/gcal-date-chooser-day.h b/src/gcal-date-chooser-day.h
new file mode 100644
index 0000000..afb18c2
--- /dev/null
+++ b/src/gcal-date-chooser-day.h
@@ -0,0 +1,50 @@
+/* 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 __GCAL_DATE_CHOOSER_DAY_PRIVATE_H__
+#define __GCAL_DATE_CHOOSER_DAY_PRIVATE_H__
+
+#include "gcal-date-chooser.h"
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GCAL_TYPE_DATE_CHOOSER_DAY (gcal_date_chooser_day_get_type())
+
+G_DECLARE_FINAL_TYPE (GcalDateChooserDay, gcal_date_chooser_day, GCAL, DATE_CHOOSER_DAY, GtkBin)
+
+GtkWidget* gcal_date_chooser_day_new (void);
+
+GDateTime* gcal_date_chooser_day_get_date (GcalDateChooserDay *day);
+
+void gcal_date_chooser_day_set_date (GcalDateChooserDay *day,
+ GDateTime *date);
+
+void gcal_date_chooser_day_set_other_month (GcalDateChooserDay *day,
+ gboolean other_month);
+
+void gcal_date_chooser_day_set_selected (GcalDateChooserDay *day,
+ gboolean selected);
+
+void gcal_date_chooser_day_set_options (GcalDateChooserDay *day,
+ GcalDateChooserDayOptions options);
+
+G_END_DECLS
+
+#endif /* __GCAL_DATE_CHOOSER_DAY_PRIVATE_H__ */
diff --git a/src/gcal-date-chooser.c b/src/gcal-date-chooser.c
new file mode 100644
index 0000000..656f9b6
--- /dev/null
+++ b/src/gcal-date-chooser.c
@@ -0,0 +1,817 @@
+/* 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 "gcal-utils.h"
+#include "gcal-date-chooser.h"
+#include "gcal-date-chooser-day.h"
+#include "gcal-multi-choice.h"
+
+#include <stdlib.h>
+#include <langinfo.h>
+
+struct _GcalDateChooser
+{
+ GtkBin parent;
+
+ GtkWidget *month_choice;
+ GtkWidget *year_choice;
+ GtkWidget *grid;
+
+ GtkWidget *day_grid;
+ GtkWidget *corner;
+ GtkWidget *cols[7];
+ GtkWidget *rows[6];
+ GtkWidget *days[6][7];
+
+ GDateTime *date;
+
+ gint this_year;
+ gint week_start;
+
+ gboolean show_heading;
+ gboolean show_day_names;
+ gboolean show_week_numbers;
+ gboolean no_month_change;
+
+ GcalDateChooserDayOptionsCallback day_options_cb;
+ gpointer day_options_data;
+ GDestroyNotify day_options_destroy;
+};
+
+G_DEFINE_TYPE (GcalDateChooser, gcal_date_chooser, GTK_TYPE_BIN)
+
+enum
+{
+ MONTH_CHANGED,
+ DAY_SELECTED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_DATE,
+ PROP_SHOW_HEADING,
+ PROP_SHOW_DAY_NAMES,
+ PROP_SHOW_WEEK_NUMBERS,
+ PROP_NO_MONTH_CHANGE,
+ NUM_PROPERTIES
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+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 void
+calendar_compute_days (GcalDateChooser *self)
+{
+ GcalDateChooserDay *d;
+ GDateTime *date;
+ gint year, month, day;
+ gint other_year, other_month;
+ gint ndays_in_month;
+ gint ndays_in_prev_month;
+ gint first_day;
+ gint row;
+ gint col;
+
+ g_date_time_get_ymd (self->date, &year, &month, NULL);
+
+ ndays_in_month = month_length[leap (year)][month];
+
+ date = g_date_time_new_local (year, month, 1, 1, 1, 1);
+ first_day = g_date_time_get_day_of_week (date);
+ g_date_time_unref (date);
+
+ first_day = (first_day + 7 - self->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;
+
+ other_year = year;
+ other_month = month - 1;
+ if (other_month == 0)
+ {
+ other_month = 12;
+ other_year -= 1;
+ }
+
+ for (col = 0; col < first_day; col++)
+ {
+ d = GCAL_DATE_CHOOSER_DAY (self->days[0][col]);
+ date = g_date_time_new_local (other_year, other_month, day, 1, 1, 1);
+ gcal_date_chooser_day_set_date (d, date);
+ gcal_date_chooser_day_set_other_month (d, TRUE);
+ g_date_time_unref (date);
+ day++;
+ }
+
+ /* Compute days of current month */
+ row = first_day / 7;
+ col = first_day % 7;
+
+ for (day = 1; day <= ndays_in_month; day++)
+ {
+ d = GCAL_DATE_CHOOSER_DAY (self->days[row][col]);
+ date = g_date_time_new_local (year, month, day, 1, 1, 1);
+ gcal_date_chooser_day_set_date (d, date);
+ gcal_date_chooser_day_set_other_month (d, FALSE);
+ g_date_time_unref (date);
+
+ col++;
+ if (col == 7)
+ {
+ row++;
+ col = 0;
+ }
+ }
+
+ /* Compute days of next month */
+
+ other_year = year;
+ other_month = month + 1;
+ if (other_month == 13)
+ {
+ other_month = 1;
+ other_year += 1;
+ }
+
+ day = 1;
+ for (; row < 6; row++)
+ {
+ for (; col < 7; col++)
+ {
+ d = GCAL_DATE_CHOOSER_DAY (self->days[row][col]);
+ date = g_date_time_new_local (other_year, other_month, day, 1, 1, 1);
+ gcal_date_chooser_day_set_date (d, date);
+ gcal_date_chooser_day_set_other_month (d, TRUE);
+ g_date_time_unref (date);
+ day++;
+ }
+ col = 0;
+ }
+
+ /* update week numbers */
+ for (row = 0; row < 6; row++)
+ {
+ gchar *text;
+
+ d = GCAL_DATE_CHOOSER_DAY (self->days[row][6]);
+ date = gcal_date_chooser_day_get_date (d);
+ text = g_strdup_printf ("%d", g_date_time_get_week_of_year (date));
+ gtk_label_set_label (GTK_LABEL (self->rows[row]), text);
+ g_free (text);
+ }
+
+ gcal_date_chooser_invalidate_day_options (self);
+}
+
+/* 0 == sunday */
+static gchar *
+calendar_get_weekday_name (gint i)
+{
+ GDateTime *date;
+ gchar *text;
+
+ date = g_date_time_new_local (2015, 1, 4 + i, 1, 1, 1);
+ text = g_date_time_format (date, "%a");
+ g_date_time_unref (date);
+
+ return text;
+}
+
+/* 0 = january */
+static gchar *
+calendar_get_month_name (gint i)
+{
+ GDateTime *date;
+ gchar *text;
+
+ date = g_date_time_new_local (2015, i + 1, 1, 1, 1, 1);
+ text = g_date_time_format (date, "%B");
+ g_date_time_unref (date);
+
+ return text;
+}
+
+static void
+calendar_init_weekday_display (GcalDateChooser *self)
+{
+ gint i;
+ gchar *text;
+
+ for (i = 0; i < 7; i++)
+ {
+ text = calendar_get_weekday_name ((i + self->week_start) % 7);
+ gtk_label_set_label (GTK_LABEL (self->cols[i]), text);
+ g_free (text);
+ }
+}
+
+static gchar *
+format_month (GcalMultiChoice *choice,
+ gint value,
+ gpointer data)
+{
+ return calendar_get_month_name (value);
+}
+
+static void
+calendar_init_month_display (GcalDateChooser *self)
+{
+ gchar *months[13];
+ gchar *month;
+ gint i;
+
+ for (i = 0; i < 12; i++)
+ {
+ month = calendar_get_month_name (i);
+ months[i] = g_strdup_printf ("%s", month);
+ g_free (month);
+ }
+
+ months[12] = NULL;
+
+ gcal_multi_choice_set_choices (GCAL_MULTI_CHOICE (self->month_choice),
+ (const gchar**) months);
+
+ for (i = 0; i < 12; i++)
+ g_free (months[i]);
+
+ gcal_multi_choice_set_format_callback (GCAL_MULTI_CHOICE (self->month_choice),
+ format_month,
+ self,
+ NULL);
+}
+
+static void
+calendar_update_selected_day_display (GcalDateChooser *self)
+{
+ GcalDateChooserDay *d;
+ GDateTime *date;
+ gint row, col;
+
+ for (row = 0; row < 6; row++)
+ {
+ for (col = 0; col < 7; col++)
+ {
+ d = GCAL_DATE_CHOOSER_DAY (self->days[row][col]);
+ date = gcal_date_chooser_day_get_date (d);
+ gcal_date_chooser_day_set_selected (d, datetime_compare_date (date, self->date) == 0);
+ }
+ }
+}
+
+static void
+calendar_update_selected_day (GcalDateChooser *self)
+{
+ GDateTime *date;
+ gint month_len;
+ gint year, month, day;
+
+ g_date_time_get_ymd (self->date, &year, &month, &day);
+
+ month_len = month_length[leap (year)][month];
+
+ if (month_len < day)
+ {
+ date = g_date_time_new_local (year, month, month_len, 1, 1, 1);
+ gcal_date_chooser_set_date (self, date);
+ g_date_time_unref (date);
+ }
+ else
+ {
+ calendar_update_selected_day_display (self);
+ }
+}
+
+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.");
+
+ return (week_1stday + first_weekday - 1) % 7;
+}
+
+static void
+day_selected_cb (GcalDateChooserDay *d,
+ GcalDateChooser *self)
+{
+ gcal_date_chooser_set_date (self, gcal_date_chooser_day_get_date (d));
+}
+
+static void
+calendar_set_property (GObject *obj,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GcalDateChooser *self = GCAL_DATE_CHOOSER (obj);
+
+ switch (property_id)
+ {
+ case PROP_DATE:
+ gcal_date_chooser_set_date (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_SHOW_HEADING:
+ gcal_date_chooser_set_show_heading (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_DAY_NAMES:
+ gcal_date_chooser_set_show_day_names (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_WEEK_NUMBERS:
+ gcal_date_chooser_set_show_week_numbers (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_NO_MONTH_CHANGE:
+ gcal_date_chooser_set_no_month_change (self, 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)
+{
+ GcalDateChooser *self = GCAL_DATE_CHOOSER (obj);
+
+ switch (property_id)
+ {
+ case PROP_DATE:
+ g_value_set_boxed (value, self->date);
+ break;
+
+ case PROP_SHOW_HEADING:
+ g_value_set_boolean (value, self->show_heading);
+ break;
+
+ case PROP_SHOW_DAY_NAMES:
+ g_value_set_boolean (value, self->show_day_names);
+ break;
+
+ case PROP_SHOW_WEEK_NUMBERS:
+ g_value_set_boolean (value, self->show_week_numbers);
+ break;
+
+ case PROP_NO_MONTH_CHANGE:
+ g_value_set_boolean (value, self->no_month_change);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+ break;
+ }
+}
+
+static void
+multi_choice_changed (GcalDateChooser *self)
+{
+ GDateTime *date;
+ gint year, month, day;
+
+ year = gcal_multi_choice_get_value (GCAL_MULTI_CHOICE (self->year_choice));
+ month = gcal_multi_choice_get_value (GCAL_MULTI_CHOICE (self->month_choice)) + 1;
+ g_date_time_get_ymd (self->date, NULL, NULL, &day);
+
+ date = g_date_time_new_local (year, month, day, 1, 1, 1);
+ gcal_date_chooser_set_date (self, date);
+ g_date_time_unref (date);
+}
+
+static void
+calendar_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ GcalDateChooser *self = GCAL_DATE_CHOOSER (widget);
+ gchar *text;
+ GDate *gdate;
+ gint year, month, day;
+ GDateTime *date;
+
+ gdate = g_date_new ();
+ text = (gchar *)gtk_selection_data_get_text (selection_data);
+ if (text)
+ {
+ g_date_set_parse (gdate, text);
+ g_free (text);
+ }
+ if (!g_date_valid (gdate))
+ {
+ g_date_free (gdate);
+ gtk_drag_finish (context, FALSE, FALSE, time);
+ return;
+ }
+
+ year = g_date_get_year (gdate);
+ month = g_date_get_month (gdate);
+ day = g_date_get_day (gdate);
+
+ g_date_free (gdate);
+
+ gtk_drag_finish (context, TRUE, FALSE, time);
+
+ if (!self->show_heading || self->no_month_change)
+ g_date_time_get_ymd (self->date, &year, &month, NULL);
+
+ date = g_date_time_new_local (year, month, day, 1, 1, 1);
+ gcal_date_chooser_set_date (self, date);
+ g_date_time_unref (date);
+}
+
+static void
+gcal_date_chooser_class_init (GcalDateChooserClass *class)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ g_type_ensure (GCAL_TYPE_MULTI_CHOICE);
+
+ 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/gnome/calendar/date-chooser.ui");
+
+ properties[PROP_DATE] = g_param_spec_boxed ("date",
+ "Date",
+ "The selected date",
+ G_TYPE_DATE_TIME,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ properties[PROP_SHOW_HEADING] = g_param_spec_boolean ("show-heading",
+ "Show Heading",
+ "If TRUE, a heading is displayed",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ properties[PROP_SHOW_DAY_NAMES] = g_param_spec_boolean ("show-day-names",
+ "Show Day Names",
+ "If TRUE, day names are displayed",
+ TRUE,
+ G_PARAM_READWRITE);
+
+ properties[PROP_SHOW_WEEK_NUMBERS] = g_param_spec_boolean ("show-week-numbers",
+ "Show Week Numbers",
+ "If TRUE, week numbers are displayed",
+ TRUE,
+ G_PARAM_READWRITE);
+
+ properties[PROP_NO_MONTH_CHANGE] = g_param_spec_boolean ("no-month-change",
+ "No Month Change",
+ "If TRUE, the selected month cannot be changed",
+ FALSE,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+ signals[MONTH_CHANGED] = g_signal_new ("month-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ signals[DAY_SELECTED] = g_signal_new ("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, GcalDateChooser, month_choice);
+ gtk_widget_class_bind_template_child (widget_class, GcalDateChooser, year_choice);
+ gtk_widget_class_bind_template_child (widget_class, GcalDateChooser, grid);
+
+ gtk_widget_class_bind_template_callback (widget_class, multi_choice_changed);
+
+ gtk_widget_class_set_css_name (widget_class, "datechooser");
+}
+
+
+static void
+gcal_date_chooser_init (GcalDateChooser *self)
+{
+ GtkWidget *label;
+ gint row, col;
+ gint year, month;
+
+ self->show_heading = TRUE;
+ self->show_day_names = TRUE;
+ self->show_week_numbers = TRUE;
+ self->no_month_change = FALSE;
+
+ self->date = g_date_time_new_now_local ();
+ g_date_time_get_ymd (self->date, &self->this_year, NULL, NULL);
+
+ self->week_start = calendar_get_week_start ();
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ for (col = 0; col < 7; col++)
+ {
+ self->cols[col] = gtk_label_new ("");
+
+ g_object_bind_property (self,
+ "show-day-names",
+ self->cols[col],
+ "visible",
+ G_BINDING_SYNC_CREATE);
+
+ gtk_style_context_add_class (gtk_widget_get_style_context (self->cols[col]), "weekday");
+
+ gtk_grid_attach (GTK_GRID (self->grid), self->cols[col], col, -1, 1, 1);
+ }
+
+ for (row = 0; row < 6; row++)
+ {
+ self->rows[row] = gtk_label_new ("");
+
+ g_object_bind_property (self,
+ "show-week-numbers",
+ self->rows[row],
+ "visible",
+ G_BINDING_SYNC_CREATE);
+
+ gtk_widget_show (self->rows[row]);
+ gtk_style_context_add_class (gtk_widget_get_style_context (self->rows[row]), "weeknum");
+ gtk_grid_attach (GTK_GRID (self->grid), self->rows[row], -1, row, 1, 1);
+ }
+
+ /* We are using a stack here to keep the week number column from shrinking
+ * when all the weeks are single-digit
+ */
+ self->corner = gtk_stack_new ();
+ gtk_grid_attach (GTK_GRID (self->grid), self->corner, -1, -1, 1, 1);
+ label = gtk_label_new ("");
+ gtk_widget_show (label);
+ gtk_style_context_add_class (gtk_widget_get_style_context (label), "weekday");
+ gtk_container_add (GTK_CONTAINER (self->corner), label);
+
+ label = gtk_label_new ("99");
+ gtk_style_context_add_class (gtk_widget_get_style_context (label), "weeknum");
+ gtk_container_add (GTK_CONTAINER (self->corner), label);
+
+ self->day_grid = g_object_new (GTK_TYPE_GRID,
+ "valign", GTK_ALIGN_FILL,
+ "halign", GTK_ALIGN_FILL,
+ "row-spacing", 2,
+ "column-spacing", 2,
+ "visible", TRUE,
+ NULL);
+ gtk_grid_attach (GTK_GRID (self->grid), self->day_grid, 0, 0, 7, 6);
+
+ for (row = 0; row < 6; row++)
+ {
+ for (col = 0; col < 7; col++)
+ {
+ self->days[row][col] = gcal_date_chooser_day_new ();
+
+ g_signal_connect (self->days[row][col],
+ "selected",
+ G_CALLBACK (day_selected_cb),
+ self);
+
+ gtk_widget_show (self->days[row][col]);
+ gtk_grid_attach (GTK_GRID (self->day_grid), self->days[row][col], col, row, 1, 1);
+ }
+ }
+
+ calendar_init_month_display (self);
+ calendar_init_weekday_display (self);
+
+ calendar_compute_days (self);
+ g_date_time_get_ymd (self->date, &year, &month, NULL);
+ gcal_multi_choice_set_value (GCAL_MULTI_CHOICE (self->year_choice), year);
+ gcal_multi_choice_set_value (GCAL_MULTI_CHOICE (self->month_choice), month - 1);
+ calendar_update_selected_day_display (self);
+
+ gtk_drag_dest_set (GTK_WIDGET (self), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
+ gtk_drag_dest_add_text_targets (GTK_WIDGET (self));
+}
+
+GtkWidget*
+gcal_date_chooser_new (void)
+{
+ return g_object_new (GCAL_TYPE_DATE_CHOOSER, NULL);
+}
+
+void
+gcal_date_chooser_set_show_heading (GcalDateChooser *self,
+ gboolean setting)
+{
+ if (self->show_heading == setting)
+ return;
+
+ self->show_heading = setting;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_HEADING]);
+}
+
+gboolean
+gcal_date_chooser_get_show_heading (GcalDateChooser *self)
+{
+ return self->show_heading;
+}
+
+void
+gcal_date_chooser_set_show_day_names (GcalDateChooser *self,
+ gboolean setting)
+{
+ if (self->show_day_names == setting)
+ return;
+
+ self->show_day_names = setting;
+
+ gtk_widget_set_visible (self->corner, self->show_day_names && self->show_week_numbers);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_DAY_NAMES]);
+}
+
+gboolean
+gcal_date_chooser_get_show_day_names (GcalDateChooser *self)
+{
+ return self->show_day_names;
+}
+
+void
+gcal_date_chooser_set_show_week_numbers (GcalDateChooser *self,
+ gboolean setting)
+{
+ if (self->show_week_numbers == setting)
+ return;
+
+ self->show_week_numbers = setting;
+
+ gtk_widget_set_visible (self->corner, self->show_day_names && self->show_week_numbers);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_WEEK_NUMBERS]);
+}
+
+gboolean
+gcal_date_chooser_get_show_week_numbers (GcalDateChooser *self)
+{
+ return self->show_week_numbers;
+}
+
+void
+gcal_date_chooser_set_no_month_change (GcalDateChooser *self,
+ gboolean setting)
+{
+ if (self->no_month_change == setting)
+ return;
+
+ self->no_month_change = setting;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NO_MONTH_CHANGE]);
+}
+
+gboolean
+gcal_date_chooser_get_no_month_change (GcalDateChooser *self)
+{
+ return self->no_month_change;
+}
+
+void
+gcal_date_chooser_set_date (GcalDateChooser *self,
+ GDateTime *date)
+{
+ gint y1, m1, d1, y2, m2, d2;
+
+ g_object_freeze_notify (G_OBJECT (self));
+
+ g_date_time_get_ymd (self->date, &y1, &m1, &d1);
+ g_date_time_get_ymd (date, &y2, &m2, &d2);
+
+ g_date_time_unref (self->date);
+ self->date = g_date_time_ref (date);
+
+ if (y1 != y2 || m1 != m2)
+ {
+ gcal_multi_choice_set_value (GCAL_MULTI_CHOICE (self->year_choice), y2);
+ gcal_multi_choice_set_value (GCAL_MULTI_CHOICE (self->month_choice), m2 - 1);
+ calendar_compute_days (self);
+ }
+
+ if (y1 != y2 || m1 != m2 || d1 != d2)
+ {
+ calendar_update_selected_day (self);
+ g_signal_emit (self, signals[DAY_SELECTED], 0);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DATE]);
+ }
+
+ g_object_thaw_notify (G_OBJECT (self));
+}
+
+GDateTime *
+gcal_date_chooser_get_date (GcalDateChooser *self)
+{
+ return self->date;
+}
+
+void
+gcal_date_chooser_set_day_options_callback (GcalDateChooser *self,
+ GcalDateChooserDayOptionsCallback callback,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ if (self->day_options_destroy)
+ self->day_options_destroy (self->day_options_data);
+
+ self->day_options_cb = callback;
+ self->day_options_data = data;
+ self->day_options_destroy = destroy;
+
+ gcal_date_chooser_invalidate_day_options (self);
+}
+
+
+void
+gcal_date_chooser_invalidate_day_options (GcalDateChooser *self)
+{
+ GcalDateChooserDayOptions options;
+ GcalDateChooserDay *d;
+ GDateTime *date;
+ gint row, col;
+
+ for (row = 0; row < 6; row++)
+ {
+ for (col = 0; col < 7; col++)
+ {
+ d = GCAL_DATE_CHOOSER_DAY (self->days[row][col]);
+ date = gcal_date_chooser_day_get_date (d);
+
+ if (self->day_options_cb)
+ options = self->day_options_cb (self, date, self->day_options_data);
+ else
+ options = GCAL_DATE_CHOOSER_DAY_NONE;
+
+ gcal_date_chooser_day_set_options (d, options);
+ }
+ }
+}
diff --git a/src/gcal-date-chooser.h b/src/gcal-date-chooser.h
new file mode 100644
index 0000000..cbbdf1d
--- /dev/null
+++ b/src/gcal-date-chooser.h
@@ -0,0 +1,77 @@
+/* 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 __GCAL_DATE_CHOOSER_H__
+#define __GCAL_DATE_CHOOSER_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ GCAL_DATE_CHOOSER_DAY_NONE = 1 << 0,
+ GCAL_DATE_CHOOSER_DAY_WEEKEND = 1 << 1,
+ GCAL_DATE_CHOOSER_DAY_HOLIDAY = 1 << 2,
+ GCAL_DATE_CHOOSER_DAY_MARKED = 1 << 3
+} GcalDateChooserDayOptions;
+
+#define GCAL_TYPE_DATE_CHOOSER (gcal_date_chooser_get_type ())
+
+G_DECLARE_FINAL_TYPE (GcalDateChooser, gcal_date_chooser, GCAL, DATE_CHOOSER, GtkBin)
+
+typedef GcalDateChooserDayOptions (*GcalDateChooserDayOptionsCallback) (GcalDateChooser *self,
+ GDateTime *date,
+ gpointer user_data);
+
+GtkWidget* gcal_date_chooser_new (void);
+
+GDateTime* gcal_date_chooser_get_date (GcalDateChooser *self);
+
+void gcal_date_chooser_set_date (GcalDateChooser *self,
+ GDateTime *date);
+
+void gcal_date_chooser_set_day_options_callback (GcalDateChooser *self,
+ GcalDateChooserDayOptionsCallback callback,
+ gpointer data,
+ GDestroyNotify destroy);
+
+void gcal_date_chooser_invalidate_day_options (GcalDateChooser *self);
+
+gboolean gcal_date_chooser_get_no_month_change (GcalDateChooser *self);
+
+void gcal_date_chooser_set_no_month_change (GcalDateChooser *self,
+ gboolean setting);
+
+gboolean gcal_date_chooser_get_show_heading (GcalDateChooser *self);
+
+void gcal_date_chooser_set_show_heading (GcalDateChooser *self,
+ gboolean setting);
+
+gboolean gcal_date_chooser_get_show_day_names (GcalDateChooser *self);
+
+void gcal_date_chooser_set_show_day_names (GcalDateChooser *self,
+ gboolean setting);
+
+gboolean gcal_date_chooser_get_show_week_numbers (GcalDateChooser *self);
+
+void gcal_date_chooser_set_show_week_numbers (GcalDateChooser *self,
+ gboolean setting);
+
+G_END_DECLS
+
+#endif /* __GCAL_DATE_CHOOSER_H__ */
diff --git a/src/gcal-multi-choice.c b/src/gcal-multi-choice.c
new file mode 100644
index 0000000..bfcb19e
--- /dev/null
+++ b/src/gcal-multi-choice.c
@@ -0,0 +1,481 @@
+/* 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 "gcal-multi-choice.h"
+
+struct _GcalMultiChoice
+{
+ GtkBox parent;
+ GtkWidget *down_button;
+ GtkWidget *stack;
+ GtkWidget *up_button;
+ gint value;
+ gint min_value;
+ gint max_value;
+ gboolean wrap;
+ gboolean animate;
+ GtkWidget **choices;
+ gint n_choices;
+ guint click_id;
+ GtkWidget *active;
+ GtkWidget *label1;
+ GtkWidget *label2;
+ GcalMultiChoiceFormatCallback format_cb;
+ gpointer format_data;
+ GDestroyNotify format_destroy;
+};
+
+enum
+{
+ PROP_VALUE = 1,
+ PROP_MIN_VALUE,
+ PROP_MAX_VALUE,
+ PROP_WRAP,
+ PROP_ANIMATE,
+ PROP_CHOICES,
+ NUM_PROPERTIES
+};
+
+enum
+{
+ WRAPPED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE (GcalMultiChoice, gcal_multi_choice, GTK_TYPE_BOX)
+
+static gchar *
+get_value_string (GcalMultiChoice *self,
+ gint value)
+{
+ if (self->format_cb)
+ return self->format_cb (self, value, self->format_data);
+ else if (0 <= value && value < self->n_choices)
+ return g_strdup (gtk_label_get_label (GTK_LABEL (self->choices[value])));
+ else
+ return g_strdup_printf ("%d", value);
+}
+
+static void
+set_value (GcalMultiChoice *self,
+ gint value,
+ GtkStackTransitionType transition)
+{
+ GtkWidget *label;
+ const gchar *name;
+ gchar *text;
+
+ value = CLAMP (value, self->min_value, self->max_value);
+
+ if (self->value == value)
+ return;
+
+ self->value = value;
+
+ if (gtk_stack_get_visible_child (GTK_STACK (self->stack)) == self->label1)
+ {
+ name = "label2";
+ label = self->label2;
+ }
+ else
+ {
+ name = "label1";
+ label = self->label1;
+ }
+
+ text = get_value_string (self, value);
+ gtk_label_set_text (GTK_LABEL (label), text);
+ g_free (text);
+
+ if (!self->animate)
+ transition = GTK_STACK_TRANSITION_TYPE_NONE;
+
+ gtk_stack_set_visible_child_full (GTK_STACK (self->stack), name, transition);
+
+ gtk_widget_set_sensitive (self->down_button,
+ self->wrap || self->value > self->min_value);
+ gtk_widget_set_sensitive (self->up_button,
+ self->wrap || self->value < self->max_value);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]);
+}
+
+static void
+go_up (GcalMultiChoice *self)
+{
+ gboolean wrapped = FALSE;
+ gint value;
+
+ value = self->value + 1;
+ if (value > self->max_value)
+ {
+ if (!self->wrap)
+ return;
+
+ value = self->min_value;
+ wrapped = TRUE;
+ }
+
+ set_value (self, value, GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+
+ if (wrapped)
+ g_signal_emit (self, signals[WRAPPED], 0);
+}
+
+static void
+go_down (GcalMultiChoice *self)
+{
+ gint value;
+ gboolean wrapped = FALSE;
+
+ value = self->value - 1;
+ if (value < self->min_value)
+ {
+ if (!self->wrap)
+ return;
+
+ value = self->max_value;
+ wrapped = TRUE;
+ }
+
+ set_value (self, value, GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
+
+ if (wrapped)
+ g_signal_emit (self, signals[WRAPPED], 0);
+}
+
+static gboolean
+button_activate (GcalMultiChoice *self,
+ GtkWidget *button)
+{
+ if (button == self->down_button)
+ go_down (self);
+ else if (button == self->up_button)
+ go_up (self);
+ else
+ g_assert_not_reached ();
+
+ return TRUE;
+}
+
+static gboolean
+button_timeout (gpointer user_data)
+{
+ GcalMultiChoice *self = GCAL_MULTI_CHOICE (user_data);
+ gboolean res;
+
+ if (self->click_id == 0)
+ return G_SOURCE_REMOVE;
+
+ res = button_activate (self, self->active);
+ if (!res)
+ {
+ g_source_remove (self->click_id);
+ self->click_id = 0;
+ }
+
+ return res;
+}
+
+static gboolean
+button_press_cb (GtkWidget *widget,
+ GdkEventButton *button,
+ GcalMultiChoice *self)
+{
+ gint double_click_time;
+
+ if (button->type != GDK_BUTTON_PRESS)
+ return TRUE;
+
+ g_object_get (gtk_widget_get_settings (widget),
+ "gtk-double-click-time", &double_click_time,
+ NULL);
+
+ if (self->click_id != 0)
+ g_source_remove (self->click_id);
+
+ self->active = widget;
+
+ self->click_id = gdk_threads_add_timeout (double_click_time,
+ button_timeout,
+ self);
+ g_source_set_name_by_id (self->click_id, "[gtk+] button_timeout");
+ button_timeout (self);
+
+ return TRUE;
+}
+
+static gboolean
+button_release_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ GcalMultiChoice *self)
+{
+ if (self->click_id != 0)
+ {
+ g_source_remove (self->click_id);
+ self->click_id = 0;
+ }
+
+ self->active = NULL;
+
+ return TRUE;
+}
+
+static void
+button_clicked_cb (GtkWidget *button,
+ GcalMultiChoice *self)
+{
+ if (self->click_id != 0)
+ return;
+
+ button_activate (self, button);
+}
+
+static void
+gcal_multi_choice_init (GcalMultiChoice *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+static void
+gcal_multi_choice_dispose (GObject *object)
+{
+ GcalMultiChoice *self = GCAL_MULTI_CHOICE (object);
+
+ if (self->click_id != 0)
+ {
+ g_source_remove (self->click_id);
+ self->click_id = 0;
+ }
+
+ g_free (self->choices);
+ self->choices = NULL;
+
+ if (self->format_destroy)
+ {
+ self->format_destroy (self->format_data);
+ self->format_destroy = NULL;
+ }
+
+ G_OBJECT_CLASS (gcal_multi_choice_parent_class)->dispose (object);
+}
+
+static void
+gcal_multi_choice_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GcalMultiChoice *self = GCAL_MULTI_CHOICE (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ g_value_set_int (value, self->value);
+ break;
+
+ case PROP_MIN_VALUE:
+ g_value_set_int (value, self->min_value);
+ break;
+
+ case PROP_MAX_VALUE:
+ g_value_set_int (value, self->max_value);
+ break;
+
+ case PROP_WRAP:
+ g_value_set_boolean (value, self->wrap);
+ break;
+
+ case PROP_ANIMATE:
+ g_value_set_boolean (value, self->animate);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gcal_multi_choice_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GcalMultiChoice *self = GCAL_MULTI_CHOICE (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ gcal_multi_choice_set_value (self, g_value_get_int (value));
+ break;
+
+ case PROP_MIN_VALUE:
+ self->min_value = g_value_get_int (value);
+ g_object_notify_by_pspec (object, properties[PROP_MIN_VALUE]);
+ gcal_multi_choice_set_value (self, self->value);
+ break;
+
+ case PROP_MAX_VALUE:
+ self->max_value = g_value_get_int (value);
+ g_object_notify_by_pspec (object, properties[PROP_MAX_VALUE]);
+ gcal_multi_choice_set_value (self, self->value);
+ break;
+
+ case PROP_WRAP:
+ self->wrap = g_value_get_boolean (value);
+ g_object_notify_by_pspec (object, properties[PROP_WRAP]);
+ break;
+
+ case PROP_ANIMATE:
+ self->animate = g_value_get_boolean (value);
+ g_object_notify_by_pspec (object, properties[PROP_ANIMATE]);
+ break;
+
+ case PROP_CHOICES:
+ gcal_multi_choice_set_choices (self, (const gchar **)g_value_get_boxed (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gcal_multi_choice_class_init (GcalMultiChoiceClass *class)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->dispose = gcal_multi_choice_dispose;
+ object_class->set_property = gcal_multi_choice_set_property;
+ object_class->get_property = gcal_multi_choice_get_property;
+
+ properties[PROP_VALUE] =
+ g_param_spec_int ("value", "Value", "Value",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ properties[PROP_MIN_VALUE] =
+ g_param_spec_int ("min-value", "Minimum Value", "Minimum Value",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ properties[PROP_MAX_VALUE] =
+ g_param_spec_int ("max-value", "Maximum Value", "Maximum Value",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ properties[PROP_WRAP] =
+ g_param_spec_boolean ("wrap", "Wrap", "Wrap",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ properties[PROP_ANIMATE] =
+ g_param_spec_boolean ("animate", "Animate", "Animate",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ properties[PROP_CHOICES] =
+ g_param_spec_boxed ("choices", "Choices", "Choices",
+ G_TYPE_STRV,
+ G_PARAM_WRITABLE|G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+ signals[WRAPPED] =
+ g_signal_new ("wrapped",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/multi-choice.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, down_button);
+ gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, up_button);
+ gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, stack);
+ gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, label1);
+ gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, label2);
+
+ gtk_widget_class_bind_template_callback (widget_class, button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, button_press_cb);
+ gtk_widget_class_bind_template_callback (widget_class, button_release_cb);
+
+ gtk_widget_class_set_css_name (widget_class, "navigator");
+}
+
+GtkWidget *
+gcal_multi_choice_new (void)
+{
+ return GTK_WIDGET (g_object_new (GCAL_TYPE_MULTI_CHOICE, NULL));
+}
+
+void
+gcal_multi_choice_set_value (GcalMultiChoice *self,
+ gint value)
+{
+ set_value (self, value, GTK_STACK_TRANSITION_TYPE_NONE);
+}
+
+gint
+gcal_multi_choice_get_value (GcalMultiChoice *self)
+{
+ return self->value;
+}
+
+void
+gcal_multi_choice_set_choices (GcalMultiChoice *self,
+ const gchar **choices)
+{
+ gint i;
+
+ for (i = 0; i < self->n_choices; i++)
+ gtk_container_remove (GTK_CONTAINER (self->stack), self->choices[i]);
+ g_free (self->choices);
+
+ self->n_choices = g_strv_length ((gchar **)choices);
+ self->choices = g_new (GtkWidget *, self->n_choices);
+ for (i = 0; i < self->n_choices; i++)
+ {
+ self->choices[i] = gtk_label_new (choices[i]);
+ gtk_widget_show (self->choices[i]);
+ gtk_stack_add_named (GTK_STACK (self->stack),
+ self->choices[i],
+ choices[i]);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHOICES]);
+}
+
+void
+gcal_multi_choice_set_format_callback (GcalMultiChoice *self,
+ GcalMultiChoiceFormatCallback callback,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ if (self->format_destroy)
+ self->format_destroy (self->format_data);
+
+ self->format_cb = callback;
+ self->format_data = user_data;
+ self->format_destroy = destroy;
+}
diff --git a/src/gcal-multi-choice.h b/src/gcal-multi-choice.h
new file mode 100644
index 0000000..9b3377e
--- /dev/null
+++ b/src/gcal-multi-choice.h
@@ -0,0 +1,51 @@
+/* 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 GCAL_MULTI_CHOICE_H
+#define GCAL_MULTI_CHOICE_H
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCAL_TYPE_MULTI_CHOICE (gcal_multi_choice_get_type())
+
+G_DECLARE_FINAL_TYPE (GcalMultiChoice, gcal_multi_choice, GCAL, MULTI_CHOICE, GtkBox)
+
+GtkWidget* gcal_multi_choice_new (void);
+
+gint gcal_multi_choice_get_value (GcalMultiChoice *self);
+
+void gcal_multi_choice_set_value (GcalMultiChoice *self,
+ gint value);
+
+void gcal_multi_choice_set_choices (GcalMultiChoice *self,
+ const gchar **selfs);
+
+typedef gchar* (*GcalMultiChoiceFormatCallback) (GcalMultiChoice *self,
+ gint value,
+ gpointer user_data);
+
+void gcal_multi_choice_set_format_callback (GcalMultiChoice *self,
+ GcalMultiChoiceFormatCallback callback,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+G_END_DECLS
+
+#endif /* GCAL_MULTI_CHOICE_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]