diff options
Diffstat (limited to 'e-util/e-calendar.c')
-rw-r--r-- | e-util/e-calendar.c | 848 |
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); + } +} |