aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-calendar.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-calendar.c')
-rw-r--r--e-util/e-calendar.c848
1 files changed, 848 insertions, 0 deletions
diff --git a/e-util/e-calendar.c b/e-util/e-calendar.c
new file mode 100644
index 0000000000..38336cb618
--- /dev/null
+++ b/e-util/e-calendar.c
@@ -0,0 +1,848 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Damon Chaplin <damon@ximian.com>
+ * Bolian Yin <bolian.yin@sun.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/*
+ * ECalendar - displays a table of monthly calendars, allowing highlighting
+ * and selection of one or more days. Like GtkCalendar with more features.
+ * Most of the functionality is in the ECalendarItem canvas item, though
+ * we also add GnomeCanvasWidget buttons to go to the previous/next month and
+ * to got to the current day.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-calendar.h"
+
+#include <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas-widget.h>
+#include <glib/gi18n.h>
+
+#define E_CALENDAR_SMALL_FONT_PTSIZE 6
+
+#define E_CALENDAR_SMALL_FONT \
+ "-adobe-utopia-regular-r-normal-*-*-100-*-*-p-*-iso8859-*"
+#define E_CALENDAR_SMALL_FONT_FALLBACK \
+ "-adobe-helvetica-medium-r-normal-*-*-80-*-*-p-*-iso8859-*"
+
+/* The space between the arrow buttons and the edge of the widget. */
+#define E_CALENDAR_ARROW_BUTTON_X_PAD 2
+#define E_CALENDAR_ARROW_BUTTON_Y_PAD 0
+
+/* Vertical padding. The padding above the button includes the space for the
+ * horizontal line. */
+#define E_CALENDAR_YPAD_ABOVE_LOWER_BUTTONS 4
+#define E_CALENDAR_YPAD_BELOW_LOWER_BUTTONS 3
+
+/* Horizontal padding inside & between buttons. */
+#define E_CALENDAR_IXPAD_BUTTONS 4
+#define E_CALENDAR_XPAD_BUTTONS 8
+
+/* The time between steps when the prev/next buttons is pressed, in 1/1000ths
+ * of a second, and the number of timeouts we skip before we start
+ * automatically moving back/forward. */
+#define E_CALENDAR_AUTO_MOVE_TIMEOUT 150
+#define E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY 2
+
+static void e_calendar_dispose (GObject *object);
+static void e_calendar_realize (GtkWidget *widget);
+static void e_calendar_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static void e_calendar_get_preferred_width (GtkWidget *widget,
+ gint *minimal_width,
+ gint *natural_width);
+static void e_calendar_get_preferred_height (GtkWidget *widget,
+ gint *minimal_height,
+ gint *natural_height);
+static void e_calendar_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint e_calendar_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time);
+static void e_calendar_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time);
+static gboolean e_calendar_button_has_focus (ECalendar *cal);
+static gboolean e_calendar_focus (GtkWidget *widget,
+ GtkDirectionType direction);
+
+static void e_calendar_on_prev_pressed (ECalendar *cal);
+static void e_calendar_on_prev_released (ECalendar *cal);
+static void e_calendar_on_prev_clicked (ECalendar *cal);
+static void e_calendar_on_next_pressed (ECalendar *cal);
+static void e_calendar_on_next_released (ECalendar *cal);
+static void e_calendar_on_next_clicked (ECalendar *cal);
+static void e_calendar_on_prev_year_pressed (ECalendar *cal);
+static void e_calendar_on_prev_year_released (ECalendar *cal);
+static void e_calendar_on_prev_year_clicked (ECalendar *cal);
+static void e_calendar_on_next_year_pressed (ECalendar *cal);
+static void e_calendar_on_next_year_released (ECalendar *cal);
+static void e_calendar_on_next_year_clicked (ECalendar *cal);
+
+static void e_calendar_start_auto_move (ECalendar *cal,
+ gboolean moving_forward);
+static gboolean e_calendar_auto_move_handler (gpointer data);
+static void e_calendar_start_auto_move_year (ECalendar *cal,
+ gboolean moving_forward);
+static gboolean e_calendar_auto_move_year_handler (gpointer data);
+static void e_calendar_stop_auto_move (ECalendar *cal);
+
+G_DEFINE_TYPE (
+ ECalendar,
+ e_calendar,
+ E_TYPE_CANVAS)
+
+static void
+e_calendar_class_init (ECalendarClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+
+ object_class->dispose = e_calendar_dispose;
+
+ widget_class->realize = e_calendar_realize;
+ widget_class->style_set = e_calendar_style_set;
+ widget_class->get_preferred_width = e_calendar_get_preferred_width;
+ widget_class->get_preferred_height = e_calendar_get_preferred_height;
+ widget_class->size_allocate = e_calendar_size_allocate;
+ widget_class->drag_motion = e_calendar_drag_motion;
+ widget_class->drag_leave = e_calendar_drag_leave;
+ widget_class->focus = e_calendar_focus;
+}
+
+static void
+e_calendar_init (ECalendar *cal)
+{
+ GnomeCanvasGroup *canvas_group;
+ PangoFontDescription *small_font_desc;
+ GtkWidget *button, *pixmap;
+ AtkObject *a11y;
+
+ /* Create the small font. */
+
+ small_font_desc = pango_font_description_copy (
+ gtk_widget_get_style (GTK_WIDGET (cal))->font_desc);
+ pango_font_description_set_size (
+ small_font_desc,
+ E_CALENDAR_SMALL_FONT_PTSIZE * PANGO_SCALE);
+
+ canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (cal)->root);
+
+ cal->calitem = E_CALENDAR_ITEM (
+ gnome_canvas_item_new (
+ canvas_group,
+ e_calendar_item_get_type (),
+ "week_number_font_desc", small_font_desc,
+ NULL));
+
+ pango_font_description_free (small_font_desc);
+
+ /* Create the arrow buttons to move to the previous/next month. */
+ button = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_show (button);
+ g_signal_connect_swapped (
+ button, "pressed",
+ G_CALLBACK (e_calendar_on_prev_pressed), cal);
+ g_signal_connect_swapped (
+ button, "released",
+ G_CALLBACK (e_calendar_on_prev_released), cal);
+ g_signal_connect_swapped (
+ button, "clicked",
+ G_CALLBACK (e_calendar_on_prev_clicked), cal);
+
+ pixmap = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
+ gtk_widget_show (pixmap);
+ gtk_container_add (GTK_CONTAINER (button), pixmap);
+
+ cal->prev_item = gnome_canvas_item_new (
+ canvas_group,
+ gnome_canvas_widget_get_type (),
+ "widget", button,
+ NULL);
+ a11y = gtk_widget_get_accessible (button);
+ atk_object_set_name (a11y, _("Previous month"));
+
+ button = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_show (button);
+ g_signal_connect_swapped (
+ button, "pressed",
+ G_CALLBACK (e_calendar_on_next_pressed), cal);
+ g_signal_connect_swapped (
+ button, "released",
+ G_CALLBACK (e_calendar_on_next_released), cal);
+ g_signal_connect_swapped (
+ button, "clicked",
+ G_CALLBACK (e_calendar_on_next_clicked), cal);
+
+ pixmap = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+ gtk_widget_show (pixmap);
+ gtk_container_add (GTK_CONTAINER (button), pixmap);
+
+ cal->next_item = gnome_canvas_item_new (
+ canvas_group,
+ gnome_canvas_widget_get_type (),
+ "widget", button,
+ NULL);
+ a11y = gtk_widget_get_accessible (button);
+ atk_object_set_name (a11y, _("Next month"));
+
+ /* Create the arrow buttons to move to the previous/next year. */
+ button = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_show (button);
+ g_signal_connect_swapped (
+ button, "pressed",
+ G_CALLBACK (e_calendar_on_prev_year_pressed), cal);
+ g_signal_connect_swapped (
+ button, "released",
+ G_CALLBACK (e_calendar_on_prev_year_released), cal);
+ g_signal_connect_swapped (
+ button, "clicked",
+ G_CALLBACK (e_calendar_on_prev_year_clicked), cal);
+
+ pixmap = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
+ gtk_widget_show (pixmap);
+ gtk_container_add (GTK_CONTAINER (button), pixmap);
+
+ cal->prev_item_year = gnome_canvas_item_new (
+ canvas_group,
+ gnome_canvas_widget_get_type (),
+ "widget", button,
+ NULL);
+ a11y = gtk_widget_get_accessible (button);
+ atk_object_set_name (a11y, _("Previous year"));
+
+ button = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_show (button);
+ g_signal_connect_swapped (
+ button, "pressed",
+ G_CALLBACK (e_calendar_on_next_year_pressed), cal);
+ g_signal_connect_swapped (
+ button, "released",
+ G_CALLBACK (e_calendar_on_next_year_released), cal);
+ g_signal_connect_swapped (
+ button, "clicked",
+ G_CALLBACK (e_calendar_on_next_year_clicked), cal);
+
+ pixmap = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+ gtk_widget_show (pixmap);
+ gtk_container_add (GTK_CONTAINER (button), pixmap);
+
+ cal->next_item_year = gnome_canvas_item_new (
+ canvas_group,
+ gnome_canvas_widget_get_type (),
+ "widget", button,
+ NULL);
+ a11y = gtk_widget_get_accessible (button);
+ atk_object_set_name (a11y, _("Next year"));
+
+ cal->min_rows = 1;
+ cal->min_cols = 1;
+ cal->max_rows = -1;
+ cal->max_cols = -1;
+
+ cal->timeout_id = 0;
+}
+
+/**
+ * e_calendar_new:
+ * @Returns: a new #ECalendar.
+ *
+ * Creates a new #ECalendar.
+ **/
+GtkWidget *
+e_calendar_new (void)
+{
+ GtkWidget *cal;
+ AtkObject *a11y;
+
+ cal = g_object_new (e_calendar_get_type (), NULL);
+ a11y = gtk_widget_get_accessible (cal);
+ atk_object_set_name (a11y, _("Month Calendar"));
+
+ return cal;
+}
+
+static void
+e_calendar_dispose (GObject *object)
+{
+ ECalendar *cal;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (E_IS_CALENDAR (object));
+
+ cal = E_CALENDAR (object);
+
+ if (cal->timeout_id != 0) {
+ g_source_remove (cal->timeout_id);
+ cal->timeout_id = 0;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_calendar_parent_class)->dispose (object);
+}
+
+static void
+e_calendar_realize (GtkWidget *widget)
+{
+ GtkStyle *style;
+ GdkWindow *window;
+
+ (*GTK_WIDGET_CLASS (e_calendar_parent_class)->realize) (widget);
+
+ /* Set the background of the canvas window to the normal color,
+ * or the arrow buttons are not displayed properly. */
+ style = gtk_widget_get_style (widget);
+ window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
+ gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
+}
+
+static void
+e_calendar_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ ECalendar *e_calendar;
+
+ e_calendar = E_CALENDAR (widget);
+ if (GTK_WIDGET_CLASS (e_calendar_parent_class)->style_set)
+ (*GTK_WIDGET_CLASS (e_calendar_parent_class)->style_set) (widget,
+ previous_style);
+
+ /* Set the background of the canvas window to the normal color,
+ * or the arrow buttons are not displayed properly. */
+ if (gtk_widget_get_realized (widget)) {
+ GtkStyle *style;
+ GdkWindow *window;
+
+ style = gtk_widget_get_style (widget);
+ window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
+ gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
+ }
+ e_calendar_item_style_set (widget, e_calendar->calitem);
+}
+
+static void
+e_calendar_get_preferred_width (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ ECalendar *cal;
+ GtkStyle *style;
+ gint col_width;
+
+ cal = E_CALENDAR (widget);
+ style = gtk_widget_get_style (GTK_WIDGET (cal));
+
+ g_object_get ((cal->calitem), "column_width", &col_width, NULL);
+
+ *minimum = *natural = col_width * cal->min_cols + style->xthickness * 2;
+}
+
+static void
+e_calendar_get_preferred_height (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ ECalendar *cal;
+ GtkStyle *style;
+ gint row_height;
+
+ cal = E_CALENDAR (widget);
+ style = gtk_widget_get_style (GTK_WIDGET (cal));
+
+ g_object_get ((cal->calitem), "row_height", &row_height, NULL);
+
+ *minimum = *natural = row_height * cal->min_rows + style->ythickness * 2;
+}
+
+static void
+e_calendar_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ ECalendar *cal;
+ GtkStyle *style;
+ GtkAllocation old_allocation;
+ PangoFontDescription *font_desc;
+ PangoContext *pango_context;
+ PangoFontMetrics *font_metrics;
+ gdouble old_x2, old_y2, new_x2, new_y2;
+ gdouble xthickness, ythickness, arrow_button_size, current_x, month_width;
+ gboolean is_rtl;
+
+ cal = E_CALENDAR (widget);
+ style = gtk_widget_get_style (widget);
+ xthickness = style->xthickness;
+ ythickness = style->ythickness;
+
+ (*GTK_WIDGET_CLASS (e_calendar_parent_class)->size_allocate) (widget, allocation);
+
+ /* Set up Pango prerequisites */
+ font_desc = gtk_widget_get_style (widget)->font_desc;
+ pango_context = gtk_widget_get_pango_context (widget);
+ font_metrics = pango_context_get_metrics (
+ pango_context, font_desc,
+ pango_context_get_language (pango_context));
+
+ /* Set the scroll region to its allocated size, if changed. */
+ gnome_canvas_get_scroll_region (
+ GNOME_CANVAS (cal),
+ NULL, NULL, &old_x2, &old_y2);
+ gtk_widget_get_allocation (widget, &old_allocation);
+ new_x2 = old_allocation.width - 1;
+ new_y2 = old_allocation.height - 1;
+ if (old_x2 != new_x2 || old_y2 != new_y2)
+ gnome_canvas_set_scroll_region (
+ GNOME_CANVAS (cal),
+ 0, 0, new_x2, new_y2);
+
+ /* Take off space for line & buttons if shown. */
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (cal->calitem),
+ "x1", 0.0,
+ "y1", 0.0,
+ "x2", new_x2,
+ "y2", new_y2,
+ NULL);
+
+ if (cal->calitem->month_width > 0)
+ month_width = cal->calitem->month_width;
+ else
+ month_width = new_x2;
+ month_width -= E_CALENDAR_ITEM_MIN_CELL_XPAD + E_CALENDAR_ARROW_BUTTON_X_PAD;
+
+ /* Position the arrow buttons. */
+ arrow_button_size =
+ PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
+ + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
+ + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
+ + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
+ - E_CALENDAR_ARROW_BUTTON_Y_PAD * 2 - 2;
+
+ is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+ current_x = is_rtl ?
+ (month_width - 2 * xthickness - E_CALENDAR_ARROW_BUTTON_X_PAD - arrow_button_size) :
+ (xthickness);
+
+ gnome_canvas_item_set (
+ cal->prev_item,
+ "x", current_x,
+ "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD,
+ "width", arrow_button_size,
+ "height", arrow_button_size,
+ NULL);
+
+ current_x += (is_rtl ? -1.0 : +1.0) * (cal->calitem->max_month_name_width - xthickness + 2 * arrow_button_size);
+
+ gnome_canvas_item_set (
+ cal->next_item,
+ "x", current_x,
+ "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD,
+ "width", arrow_button_size,
+ "height", arrow_button_size,
+ NULL);
+
+ current_x = is_rtl ?
+ (xthickness) :
+ (month_width - 2 * xthickness - E_CALENDAR_ARROW_BUTTON_X_PAD - arrow_button_size);
+
+ gnome_canvas_item_set (
+ cal->next_item_year,
+ "x", current_x,
+ "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD,
+ "width", arrow_button_size,
+ "height", arrow_button_size,
+ NULL);
+
+ current_x += (is_rtl ? +1.0 : -1.0) * (cal->calitem->max_digit_width * 5 - xthickness + 2 * arrow_button_size);
+
+ gnome_canvas_item_set (
+ cal->prev_item_year,
+ "x", current_x,
+ "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD,
+ "width", arrow_button_size,
+ "height", arrow_button_size,
+ NULL);
+
+ pango_font_metrics_unref (font_metrics);
+}
+
+void
+e_calendar_set_minimum_size (ECalendar *cal,
+ gint rows,
+ gint cols)
+{
+ g_return_if_fail (E_IS_CALENDAR (cal));
+
+ cal->min_rows = rows;
+ cal->min_cols = cols;
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (cal->calitem),
+ "minimum_rows", rows,
+ "minimum_columns", cols,
+ NULL);
+
+ gtk_widget_queue_resize (GTK_WIDGET (cal));
+}
+
+void
+e_calendar_set_maximum_size (ECalendar *cal,
+ gint rows,
+ gint cols)
+{
+ g_return_if_fail (E_IS_CALENDAR (cal));
+
+ cal->max_rows = rows;
+ cal->max_cols = cols;
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (cal->calitem),
+ "maximum_rows", rows,
+ "maximum_columns", cols,
+ NULL);
+
+ gtk_widget_queue_resize (GTK_WIDGET (cal));
+}
+
+/* Returns the border size on each side of the month displays. */
+void
+e_calendar_get_border_size (ECalendar *cal,
+ gint *top,
+ gint *bottom,
+ gint *left,
+ gint *right)
+{
+ GtkStyle *style;
+
+ g_return_if_fail (E_IS_CALENDAR (cal));
+
+ style = gtk_widget_get_style (GTK_WIDGET (cal));
+
+ if (style) {
+ *top = style->ythickness;
+ *bottom = style->ythickness;
+ *left = style->xthickness;
+ *right = style->xthickness;
+ } else {
+ *top = *bottom = *left = *right = 0;
+ }
+}
+
+static void
+e_calendar_on_prev_pressed (ECalendar *cal)
+{
+ e_calendar_start_auto_move (cal, FALSE);
+}
+
+static void
+e_calendar_on_next_pressed (ECalendar *cal)
+{
+ e_calendar_start_auto_move (cal, TRUE);
+}
+
+static void
+e_calendar_on_prev_year_pressed (ECalendar *cal)
+{
+ e_calendar_start_auto_move_year (cal, FALSE);
+}
+
+static void
+e_calendar_on_next_year_pressed (ECalendar *cal)
+{
+ e_calendar_start_auto_move_year (cal, TRUE);
+}
+
+static void
+e_calendar_start_auto_move (ECalendar *cal,
+ gboolean moving_forward)
+{
+ if (cal->timeout_id == 0) {
+ cal->timeout_id = g_timeout_add (
+ E_CALENDAR_AUTO_MOVE_TIMEOUT,
+ e_calendar_auto_move_handler,
+ cal);
+ }
+ cal->timeout_delay = E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY;
+ cal->moving_forward = moving_forward;
+
+}
+
+static void
+e_calendar_start_auto_move_year (ECalendar *cal,
+ gboolean moving_forward)
+{
+ if (cal->timeout_id == 0) {
+ cal->timeout_id = g_timeout_add (
+ E_CALENDAR_AUTO_MOVE_TIMEOUT,
+ e_calendar_auto_move_year_handler,
+ cal);
+ }
+ cal->timeout_delay = E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY;
+ cal->moving_forward = moving_forward;
+}
+
+static gboolean
+e_calendar_auto_move_year_handler (gpointer data)
+{
+ ECalendar *cal;
+ ECalendarItem *calitem;
+ gint offset;
+
+ g_return_val_if_fail (E_IS_CALENDAR (data), FALSE);
+
+ cal = E_CALENDAR (data);
+ calitem = cal->calitem;
+
+ if (cal->timeout_delay > 0) {
+ cal->timeout_delay--;
+ } else {
+ offset = cal->moving_forward ? 12 : -12;
+ e_calendar_item_set_first_month (
+ calitem, calitem->year,
+ calitem->month + offset);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+e_calendar_auto_move_handler (gpointer data)
+{
+ ECalendar *cal;
+ ECalendarItem *calitem;
+ gint offset;
+
+ g_return_val_if_fail (E_IS_CALENDAR (data), FALSE);
+
+ cal = E_CALENDAR (data);
+ calitem = cal->calitem;
+
+ if (cal->timeout_delay > 0) {
+ cal->timeout_delay--;
+ } else {
+ offset = cal->moving_forward ? 1 : -1;
+ e_calendar_item_set_first_month (
+ calitem, calitem->year,
+ calitem->month + offset);
+ }
+
+ return TRUE;
+}
+
+static void
+e_calendar_on_prev_released (ECalendar *cal)
+{
+ e_calendar_stop_auto_move (cal);
+}
+
+static void
+e_calendar_on_next_released (ECalendar *cal)
+{
+ e_calendar_stop_auto_move (cal);
+}
+
+static void
+e_calendar_on_prev_year_released (ECalendar *cal)
+{
+ e_calendar_stop_auto_move (cal);
+}
+
+static void
+e_calendar_on_next_year_released (ECalendar *cal)
+{
+ e_calendar_stop_auto_move (cal);
+}
+
+static void
+e_calendar_stop_auto_move (ECalendar *cal)
+{
+ if (cal->timeout_id != 0) {
+ g_source_remove (cal->timeout_id);
+ cal->timeout_id = 0;
+ }
+}
+
+static void
+e_calendar_on_prev_clicked (ECalendar *cal)
+{
+ e_calendar_item_set_first_month (
+ cal->calitem, cal->calitem->year,
+ cal->calitem->month - 1);
+}
+
+static void
+e_calendar_on_next_clicked (ECalendar *cal)
+{
+ e_calendar_item_set_first_month (
+ cal->calitem, cal->calitem->year,
+ cal->calitem->month + 1);
+}
+
+static void
+e_calendar_on_prev_year_clicked (ECalendar *cal)
+{
+ e_calendar_item_set_first_month (
+ cal->calitem, cal->calitem->year,
+ cal->calitem->month - 12);
+}
+
+static void
+e_calendar_on_next_year_clicked (ECalendar *cal)
+{
+ e_calendar_item_set_first_month (
+ cal->calitem, cal->calitem->year,
+ cal->calitem->month + 12);
+}
+
+static gint
+e_calendar_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ return FALSE;
+}
+
+static void
+e_calendar_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+}
+
+static gboolean
+e_calendar_button_has_focus (ECalendar *cal)
+{
+ GtkWidget *prev_widget, *next_widget;
+ gboolean ret_val;
+
+ g_return_val_if_fail (E_IS_CALENDAR (cal), FALSE);
+
+ prev_widget = GNOME_CANVAS_WIDGET (cal->prev_item)->widget;
+ next_widget = GNOME_CANVAS_WIDGET (cal->next_item)->widget;
+ ret_val = gtk_widget_has_focus (prev_widget) ||
+ gtk_widget_has_focus (next_widget);
+ return ret_val;
+}
+
+static gboolean
+e_calendar_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+#define E_CALENDAR_FOCUS_CHILDREN_NUM 5
+ ECalendar *cal;
+ GnomeCanvas *canvas;
+ GnomeCanvasItem *children[E_CALENDAR_FOCUS_CHILDREN_NUM];
+ gint focused_index = -1;
+ gint index;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_CALENDAR (widget), FALSE);
+ cal = E_CALENDAR (widget);
+ canvas = GNOME_CANVAS (widget);
+
+ if (!gtk_widget_get_can_focus (widget))
+ return FALSE;
+
+ children[0] = GNOME_CANVAS_ITEM (cal->calitem);
+ children[1] = cal->prev_item;
+ children[2] = cal->next_item;
+ children[3] = cal->prev_item_year;
+ children[4] = cal->next_item_year;
+
+ /* get current focused item, if e-calendar has had focus */
+ if (gtk_widget_has_focus (widget) || e_calendar_button_has_focus (cal))
+ for (index = 0; index < E_CALENDAR_FOCUS_CHILDREN_NUM; ++index) {
+ if (canvas->focused_item == NULL)
+ break;
+
+ if (children[index] == canvas->focused_item) {
+ focused_index = index;
+ break;
+ }
+ }
+
+ if (focused_index == -1)
+ if (direction == GTK_DIR_TAB_FORWARD)
+ focused_index = 0;
+ else
+ focused_index = E_CALENDAR_FOCUS_CHILDREN_NUM - 1;
+ else
+ if (direction == GTK_DIR_TAB_FORWARD)
+ ++focused_index;
+ else
+ --focused_index;
+
+ if (focused_index < 0 ||
+ focused_index >= E_CALENDAR_FOCUS_CHILDREN_NUM)
+ /* move out of e-calendar */
+ return FALSE;
+ gnome_canvas_item_grab_focus (children[focused_index]);
+ if (GNOME_IS_CANVAS_WIDGET (children[focused_index])) {
+ widget = GNOME_CANVAS_WIDGET (children[focused_index])->widget;
+ gtk_widget_grab_focus (widget);
+ }
+ return TRUE;
+}
+
+void
+e_calendar_set_focusable (ECalendar *cal,
+ gboolean focusable)
+{
+ GtkWidget *widget;
+ GtkWidget *prev_widget, *next_widget;
+ GtkWidget *toplevel;
+
+ g_return_if_fail (E_IS_CALENDAR (cal));
+
+ widget = GTK_WIDGET (cal);
+ prev_widget = GNOME_CANVAS_WIDGET (cal->prev_item)->widget;
+ next_widget = GNOME_CANVAS_WIDGET (cal->next_item)->widget;
+
+ if (focusable) {
+ gtk_widget_set_can_focus (widget, TRUE);
+ gtk_widget_set_can_focus (prev_widget, TRUE);
+ gtk_widget_set_can_focus (next_widget, TRUE);
+ }
+ else {
+ if (gtk_widget_has_focus (GTK_WIDGET (cal)) ||
+ e_calendar_button_has_focus (cal)) {
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (toplevel)
+ gtk_widget_grab_focus (toplevel);
+ }
+ gtk_widget_set_can_focus (widget, FALSE);
+ gtk_widget_set_can_focus (prev_widget, FALSE);
+ gtk_widget_set_can_focus (next_widget, FALSE);
+ }
+}