Updated Galician translation
[evolution.git] / widgets / misc / e-calendar.c
blob27ac031b8db3cf6f6344ad6d76e4d3f146eccb4d
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 * Authors:
17 * Damon Chaplin <damon@ximian.com>
18 * Bolian Yin <bolian.yin@sun.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
25 * ECalendar - displays a table of monthly calendars, allowing highlighting
26 * and selection of one or more days. Like GtkCalendar with more features.
27 * Most of the functionality is in the ECalendarItem canvas item, though
28 * we also add GnomeCanvasWidget buttons to go to the previous/next month and
29 * to got to the current day.
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #include "e-calendar.h"
38 #include <gtk/gtk.h>
39 #include <libgnomecanvas/gnome-canvas-widget.h>
40 #include <glib/gi18n.h>
42 #define E_CALENDAR_SMALL_FONT_PTSIZE 6
44 #define E_CALENDAR_SMALL_FONT \
45 "-adobe-utopia-regular-r-normal-*-*-100-*-*-p-*-iso8859-*"
46 #define E_CALENDAR_SMALL_FONT_FALLBACK \
47 "-adobe-helvetica-medium-r-normal-*-*-80-*-*-p-*-iso8859-*"
49 /* The space between the arrow buttons and the edge of the widget. */
50 #define E_CALENDAR_ARROW_BUTTON_X_PAD 2
51 #define E_CALENDAR_ARROW_BUTTON_Y_PAD 0
53 /* Vertical padding. The padding above the button includes the space for the
54 horizontal line. */
55 #define E_CALENDAR_YPAD_ABOVE_LOWER_BUTTONS 4
56 #define E_CALENDAR_YPAD_BELOW_LOWER_BUTTONS 3
58 /* Horizontal padding inside & between buttons. */
59 #define E_CALENDAR_IXPAD_BUTTONS 4
60 #define E_CALENDAR_XPAD_BUTTONS 8
62 /* The time between steps when the prev/next buttons is pressed, in 1/1000ths
63 of a second, and the number of timeouts we skip before we start
64 automatically moving back/forward. */
65 #define E_CALENDAR_AUTO_MOVE_TIMEOUT 150
66 #define E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY 2
68 static void e_calendar_destroy (GtkObject *object);
69 static void e_calendar_realize (GtkWidget *widget);
70 static void e_calendar_style_set (GtkWidget *widget,
71 GtkStyle *previous_style);
72 static void e_calendar_size_request (GtkWidget *widget,
73 GtkRequisition *requisition);
74 static void e_calendar_size_allocate (GtkWidget *widget,
75 GtkAllocation *allocation);
76 static gint e_calendar_drag_motion (GtkWidget *widget,
77 GdkDragContext *context,
78 gint x,
79 gint y,
80 guint time);
81 static void e_calendar_drag_leave (GtkWidget *widget,
82 GdkDragContext *context,
83 guint time);
84 static gboolean e_calendar_button_has_focus (ECalendar *cal);
85 static gboolean e_calendar_focus (GtkWidget *widget,
86 GtkDirectionType direction);
88 static void e_calendar_on_prev_pressed (ECalendar *cal);
89 static void e_calendar_on_prev_released (ECalendar *cal);
90 static void e_calendar_on_prev_clicked (ECalendar *cal);
91 static void e_calendar_on_next_pressed (ECalendar *cal);
92 static void e_calendar_on_next_released (ECalendar *cal);
93 static void e_calendar_on_next_clicked (ECalendar *cal);
95 static void e_calendar_start_auto_move (ECalendar *cal,
96 gboolean moving_forward);
97 static gboolean e_calendar_auto_move_handler (gpointer data);
98 static void e_calendar_stop_auto_move (ECalendar *cal);
100 G_DEFINE_TYPE (ECalendar, e_calendar, E_CANVAS_TYPE)
102 static void
103 e_calendar_class_init (ECalendarClass *class)
105 GtkObjectClass *object_class;
106 GtkWidgetClass *widget_class;
108 object_class = (GtkObjectClass *) class;
109 widget_class = (GtkWidgetClass *) class;
111 object_class->destroy = e_calendar_destroy;
113 widget_class->realize = e_calendar_realize;
114 widget_class->style_set = e_calendar_style_set;
115 widget_class->size_request = e_calendar_size_request;
116 widget_class->size_allocate = e_calendar_size_allocate;
117 widget_class->drag_motion = e_calendar_drag_motion;
118 widget_class->drag_leave = e_calendar_drag_leave;
119 widget_class->focus = e_calendar_focus;
122 static void
123 e_calendar_init (ECalendar *cal)
125 GnomeCanvasGroup *canvas_group;
126 PangoFontDescription *small_font_desc;
127 GtkWidget *button, *pixmap;
128 AtkObject *a11y;
130 /* Create the small font. */
132 small_font_desc =
133 pango_font_description_copy (gtk_widget_get_style (GTK_WIDGET (cal))->font_desc);
134 pango_font_description_set_size (small_font_desc,
135 E_CALENDAR_SMALL_FONT_PTSIZE * PANGO_SCALE);
137 canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (cal)->root);
139 cal->calitem = E_CALENDAR_ITEM (gnome_canvas_item_new (canvas_group,
140 e_calendar_item_get_type (),
141 "week_number_font_desc", small_font_desc,
142 NULL));
144 pango_font_description_free (small_font_desc);
146 /* Create the arrow buttons to move to the previous/next month. */
147 button = gtk_button_new ();
148 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
149 gtk_widget_show (button);
150 g_signal_connect_swapped (
151 button, "pressed",
152 G_CALLBACK (e_calendar_on_prev_pressed), cal);
153 g_signal_connect_swapped (
154 button, "released",
155 G_CALLBACK (e_calendar_on_prev_released), cal);
156 g_signal_connect_swapped (
157 button, "clicked",
158 G_CALLBACK (e_calendar_on_prev_clicked), cal);
160 pixmap = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
161 gtk_widget_show (pixmap);
162 gtk_container_add (GTK_CONTAINER (button), pixmap);
164 cal->prev_item = gnome_canvas_item_new (canvas_group,
165 gnome_canvas_widget_get_type (),
166 "widget", button,
167 NULL);
168 a11y = gtk_widget_get_accessible (button);
169 atk_object_set_name (a11y, _("Previous"));
171 button = gtk_button_new ();
172 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
173 gtk_widget_show (button);
174 g_signal_connect_swapped (
175 button, "pressed",
176 G_CALLBACK (e_calendar_on_next_pressed), cal);
177 g_signal_connect_swapped (
178 button, "released",
179 G_CALLBACK (e_calendar_on_next_released), cal);
180 g_signal_connect_swapped (
181 button, "clicked",
182 G_CALLBACK (e_calendar_on_next_clicked), cal);
184 pixmap = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
185 gtk_widget_show (pixmap);
186 gtk_container_add (GTK_CONTAINER (button), pixmap);
188 cal->next_item = gnome_canvas_item_new (canvas_group,
189 gnome_canvas_widget_get_type (),
190 "widget", button,
191 NULL);
192 a11y = gtk_widget_get_accessible (button);
193 atk_object_set_name (a11y, _("Next"));
195 cal->min_rows = 1;
196 cal->min_cols = 1;
197 cal->max_rows = -1;
198 cal->max_cols = -1;
200 cal->timeout_id = 0;
204 * e_calendar_new:
205 * @Returns: a new #ECalendar.
207 * Creates a new #ECalendar.
209 GtkWidget *
210 e_calendar_new (void)
212 GtkWidget *cal;
213 AtkObject *a11y;
215 cal = g_object_new (e_calendar_get_type (), NULL);
216 a11y = gtk_widget_get_accessible (cal);
217 atk_object_set_name (a11y, _("Month Calendar"));
219 return cal;
222 static void
223 e_calendar_destroy (GtkObject *object)
225 ECalendar *cal;
227 g_return_if_fail (object != NULL);
228 g_return_if_fail (E_IS_CALENDAR (object));
230 cal = E_CALENDAR (object);
232 if (cal->timeout_id != 0) {
233 g_source_remove (cal->timeout_id);
234 cal->timeout_id = 0;
237 if (GTK_OBJECT_CLASS (e_calendar_parent_class)->destroy)
238 (* GTK_OBJECT_CLASS (e_calendar_parent_class)->destroy) (object);
241 static void
242 e_calendar_realize (GtkWidget *widget)
244 GtkStyle *style;
245 GdkWindow *window;
247 (*GTK_WIDGET_CLASS (e_calendar_parent_class)->realize) (widget);
249 /* Set the background of the canvas window to the normal color,
250 or the arrow buttons are not displayed properly. */
251 style = gtk_widget_get_style (widget);
252 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
253 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
256 static void
257 e_calendar_style_set (GtkWidget *widget,
258 GtkStyle *previous_style)
260 ECalendar *e_calendar;
262 e_calendar = E_CALENDAR(widget);
263 if (GTK_WIDGET_CLASS (e_calendar_parent_class)->style_set)
264 (*GTK_WIDGET_CLASS (e_calendar_parent_class)->style_set) (widget,
265 previous_style);
267 /* Set the background of the canvas window to the normal color,
268 or the arrow buttons are not displayed properly. */
269 #if GTK_CHECK_VERSION(2,19,7)
270 if (gtk_widget_get_realized (widget)) {
271 #else
272 if (GTK_WIDGET_REALIZED (widget)) {
273 #endif
274 GtkStyle *style;
275 GdkWindow *window;
277 style = gtk_widget_get_style (widget);
278 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
279 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
281 e_calendar_item_style_set (widget, e_calendar->calitem);
284 static void
285 e_calendar_size_request (GtkWidget *widget,
286 GtkRequisition *requisition)
288 ECalendar *cal;
289 GtkStyle *style;
290 gint col_width, row_height, width, height;
292 cal = E_CALENDAR (widget);
293 style = gtk_widget_get_style (GTK_WIDGET (cal));
295 g_object_get((cal->calitem),
296 "row_height", &row_height,
297 "column_width", &col_width,
298 NULL);
300 height = row_height * cal->min_rows;
301 width = col_width * cal->min_cols;
303 requisition->width = width + style->xthickness * 2;
304 requisition->height = height + style->ythickness * 2;
307 static void
308 e_calendar_size_allocate (GtkWidget *widget,
309 GtkAllocation *allocation)
311 ECalendar *cal;
312 GtkStyle *style;
313 GtkAllocation old_allocation;
314 PangoFontDescription *font_desc;
315 PangoContext *pango_context;
316 PangoFontMetrics *font_metrics;
317 gdouble old_x2, old_y2, new_x2, new_y2;
318 gdouble xthickness, ythickness, arrow_button_size;
320 cal = E_CALENDAR (widget);
321 style = gtk_widget_get_style (widget);
322 xthickness = style->xthickness;
323 ythickness = style->ythickness;
325 (*GTK_WIDGET_CLASS (e_calendar_parent_class)->size_allocate) (widget, allocation);
327 /* Set up Pango prerequisites */
328 font_desc = gtk_widget_get_style (widget)->font_desc;
329 pango_context = gtk_widget_get_pango_context (widget);
330 font_metrics = pango_context_get_metrics (pango_context, font_desc,
331 pango_context_get_language (pango_context));
333 /* Set the scroll region to its allocated size, if changed. */
334 gnome_canvas_get_scroll_region (GNOME_CANVAS (cal),
335 NULL, NULL, &old_x2, &old_y2);
336 gtk_widget_get_allocation (widget, &old_allocation);
337 new_x2 = old_allocation.width - 1;
338 new_y2 = old_allocation.height - 1;
339 if (old_x2 != new_x2 || old_y2 != new_y2)
340 gnome_canvas_set_scroll_region (GNOME_CANVAS (cal),
341 0, 0, new_x2, new_y2);
343 /* Take off space for line & buttons if shown. */
344 gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
345 "x1", 0.0,
346 "y1", 0.0,
347 "x2", new_x2,
348 "y2", new_y2,
349 NULL);
351 /* Position the arrow buttons. */
352 arrow_button_size =
353 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
354 + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
355 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
356 + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
357 - E_CALENDAR_ARROW_BUTTON_Y_PAD * 2;
359 gnome_canvas_item_set (cal->prev_item,
360 "x", (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
361 ? new_x2 + 1 - xthickness * 2 - E_CALENDAR_ARROW_BUTTON_X_PAD
362 - arrow_button_size
363 : xthickness * 2 + E_CALENDAR_ARROW_BUTTON_X_PAD,
364 "y", ythickness * 2
365 + E_CALENDAR_ARROW_BUTTON_Y_PAD,
366 "width", arrow_button_size,
367 "height", arrow_button_size,
368 NULL);
370 gnome_canvas_item_set (cal->next_item,
371 "x", (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
372 ? xthickness * 2 + E_CALENDAR_ARROW_BUTTON_X_PAD
373 : new_x2 + 1 - xthickness * 2 - E_CALENDAR_ARROW_BUTTON_X_PAD
374 - arrow_button_size,
375 "y", ythickness * 2
376 + E_CALENDAR_ARROW_BUTTON_Y_PAD,
377 "width", arrow_button_size,
378 "height", arrow_button_size,
379 NULL);
381 pango_font_metrics_unref (font_metrics);
384 void
385 e_calendar_set_minimum_size (ECalendar *cal,
386 gint rows,
387 gint cols)
389 g_return_if_fail (E_IS_CALENDAR (cal));
391 cal->min_rows = rows;
392 cal->min_cols = cols;
394 gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
395 "minimum_rows", rows,
396 "minimum_columns", cols,
397 NULL);
399 gtk_widget_queue_resize (GTK_WIDGET (cal));
402 void
403 e_calendar_set_maximum_size (ECalendar *cal,
404 gint rows,
405 gint cols)
407 g_return_if_fail (E_IS_CALENDAR (cal));
409 cal->max_rows = rows;
410 cal->max_cols = cols;
412 gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
413 "maximum_rows", rows,
414 "maximum_columns", cols,
415 NULL);
417 gtk_widget_queue_resize (GTK_WIDGET (cal));
420 /* Returns the border size on each side of the month displays. */
421 void
422 e_calendar_get_border_size (ECalendar *cal,
423 gint *top,
424 gint *bottom,
425 gint *left,
426 gint *right)
428 GtkStyle *style;
430 g_return_if_fail (E_IS_CALENDAR (cal));
432 style = gtk_widget_get_style (GTK_WIDGET (cal));
434 if (style) {
435 *top = style->ythickness;
436 *bottom = style->ythickness;
437 *left = style->xthickness;
438 *right = style->xthickness;
439 } else {
440 *top = *bottom = *left = *right = 0;
444 static void
445 e_calendar_on_prev_pressed (ECalendar *cal)
447 e_calendar_start_auto_move (cal, FALSE);
450 static void
451 e_calendar_on_next_pressed (ECalendar *cal)
453 e_calendar_start_auto_move (cal, TRUE);
456 static void
457 e_calendar_start_auto_move (ECalendar *cal,
458 gboolean moving_forward)
460 if (cal->timeout_id == 0) {
461 cal->timeout_id = g_timeout_add (E_CALENDAR_AUTO_MOVE_TIMEOUT,
462 e_calendar_auto_move_handler,
463 cal);
465 cal->timeout_delay = E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY;
466 cal->moving_forward = moving_forward;
470 static gboolean
471 e_calendar_auto_move_handler (gpointer data)
473 ECalendar *cal;
474 ECalendarItem *calitem;
475 gint offset;
477 g_return_val_if_fail (E_IS_CALENDAR (data), FALSE);
479 cal = E_CALENDAR (data);
480 calitem = cal->calitem;
482 GDK_THREADS_ENTER ();
484 if (cal->timeout_delay > 0) {
485 cal->timeout_delay--;
486 } else {
487 offset = cal->moving_forward ? 1 : -1;
488 e_calendar_item_set_first_month (calitem, calitem->year,
489 calitem->month + offset);
492 GDK_THREADS_LEAVE ();
493 return TRUE;
496 static void
497 e_calendar_on_prev_released (ECalendar *cal)
499 e_calendar_stop_auto_move (cal);
502 static void
503 e_calendar_on_next_released (ECalendar *cal)
505 e_calendar_stop_auto_move (cal);
508 static void
509 e_calendar_stop_auto_move (ECalendar *cal)
511 if (cal->timeout_id != 0) {
512 g_source_remove (cal->timeout_id);
513 cal->timeout_id = 0;
517 static void
518 e_calendar_on_prev_clicked (ECalendar *cal)
520 e_calendar_item_set_first_month (cal->calitem, cal->calitem->year,
521 cal->calitem->month - 1);
524 static void
525 e_calendar_on_next_clicked (ECalendar *cal)
527 e_calendar_item_set_first_month (cal->calitem, cal->calitem->year,
528 cal->calitem->month + 1);
531 static gint
532 e_calendar_drag_motion (GtkWidget *widget,
533 GdkDragContext *context,
534 gint x,
535 gint y,
536 guint time)
538 #if 0
539 g_print ("In e_calendar_drag_motion\n");
540 #endif
542 return FALSE;
545 static void
546 e_calendar_drag_leave (GtkWidget *widget,
547 GdkDragContext *context,
548 guint time)
550 #if 0
551 g_print ("In e_calendar_drag_leave\n");
552 #endif
555 static gboolean
556 e_calendar_button_has_focus (ECalendar *cal)
558 GtkWidget *prev_widget, *next_widget;
559 gboolean ret_val;
561 g_return_val_if_fail (E_IS_CALENDAR (cal), FALSE);
563 prev_widget = GNOME_CANVAS_WIDGET(cal->prev_item)->widget;
564 next_widget = GNOME_CANVAS_WIDGET(cal->next_item)->widget;
565 #if GTK_CHECK_VERSION(2,19,7)
566 ret_val = gtk_widget_has_focus (prev_widget) ||
567 gtk_widget_has_focus (next_widget);
568 #else
569 ret_val = GTK_WIDGET_HAS_FOCUS (prev_widget) ||
570 GTK_WIDGET_HAS_FOCUS (next_widget);
571 #endif
572 return ret_val;
575 static gboolean
576 e_calendar_focus (GtkWidget *widget, GtkDirectionType direction)
578 #define E_CALENDAR_FOCUS_CHILDREN_NUM 3
579 ECalendar *cal;
580 GnomeCanvas *canvas;
581 GnomeCanvasItem *children[E_CALENDAR_FOCUS_CHILDREN_NUM];
582 gint focused_index = -1;
583 gint index;
585 g_return_val_if_fail (widget != NULL, FALSE);
586 g_return_val_if_fail (E_IS_CALENDAR (widget), FALSE);
587 cal = E_CALENDAR (widget);
588 canvas = GNOME_CANVAS (widget);
590 if (!gtk_widget_get_can_focus (widget))
591 return FALSE;
593 children[0] = GNOME_CANVAS_ITEM (cal->calitem);
594 children[1] = cal->prev_item;
595 children[2] = cal->next_item;
597 /* get current focused item, if e-calendar has had focus */
598 #if GTK_CHECK_VERSION(2,19,7)
599 if (gtk_widget_has_focus (widget) || e_calendar_button_has_focus (cal))
600 #else
601 if (GTK_WIDGET_HAS_FOCUS (widget) || e_calendar_button_has_focus (cal))
602 #endif
603 for (index = 0; index < E_CALENDAR_FOCUS_CHILDREN_NUM; ++index) {
604 if (canvas->focused_item == NULL)
605 break;
607 if (children[index] == canvas->focused_item) {
608 focused_index = index;
609 break;
613 if (focused_index == -1)
614 if (direction == GTK_DIR_TAB_FORWARD)
615 focused_index = 0;
616 else
617 focused_index = E_CALENDAR_FOCUS_CHILDREN_NUM - 1;
618 else
619 if (direction == GTK_DIR_TAB_FORWARD)
620 ++focused_index;
621 else
622 --focused_index;
624 if (focused_index < 0 ||
625 focused_index >= E_CALENDAR_FOCUS_CHILDREN_NUM)
626 /* move out of e-calendar */
627 return FALSE;
628 gnome_canvas_item_grab_focus (children[focused_index]);
629 if (GNOME_IS_CANVAS_WIDGET (children[focused_index])) {
630 widget = GNOME_CANVAS_WIDGET (children[focused_index])->widget;
631 gtk_widget_grab_focus (widget);
633 return TRUE;
636 void
637 e_calendar_set_focusable (ECalendar *cal, gboolean focusable)
639 GtkWidget *prev_widget, *next_widget;
641 g_return_if_fail (E_IS_CALENDAR (cal));
643 prev_widget = GNOME_CANVAS_WIDGET(cal->prev_item)->widget;
644 next_widget = GNOME_CANVAS_WIDGET(cal->next_item)->widget;
646 if (focusable) {
647 GTK_WIDGET_SET_FLAGS (cal, GTK_CAN_FOCUS);
648 GTK_WIDGET_SET_FLAGS (prev_widget, GTK_CAN_FOCUS);
649 GTK_WIDGET_SET_FLAGS (next_widget, GTK_CAN_FOCUS);
651 else {
652 #if GTK_CHECK_VERSION(2,19,7)
653 if (gtk_widget_has_focus (GTK_WIDGET (cal)) || e_calendar_button_has_focus (cal)) {
654 #else
655 if (GTK_WIDGET_HAS_FOCUS (cal) || e_calendar_button_has_focus (cal)) {
656 #endif
657 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (cal));
658 if (toplevel)
659 gtk_widget_grab_focus (toplevel);
661 GTK_WIDGET_UNSET_FLAGS (cal, GTK_CAN_FOCUS);
662 GTK_WIDGET_UNSET_FLAGS (prev_widget, GTK_CAN_FOCUS);
663 GTK_WIDGET_UNSET_FLAGS (next_widget, GTK_CAN_FOCUS);