No issues found
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.
6 *
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.
11 *
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/>
14 *
15 *
16 * Authors:
17 * Damon Chaplin <damon@ximian.com>
18 * Bolian Yin <bolian.yin@sun.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 /*
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.
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35
36 #include "e-calendar.h"
37
38 #include <gtk/gtk.h>
39 #include <libgnomecanvas/gnome-canvas-widget.h>
40 #include <glib/gi18n.h>
41
42 #define E_CALENDAR_SMALL_FONT_PTSIZE 6
43
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-*"
48
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
52
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
57
58 /* Horizontal padding inside & between buttons. */
59 #define E_CALENDAR_IXPAD_BUTTONS 4
60 #define E_CALENDAR_XPAD_BUTTONS 8
61
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
67
68 static void e_calendar_dispose (GObject *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_get_preferred_width (GtkWidget *widget,
73 gint *minimal_width,
74 gint *natural_width);
75 static void e_calendar_get_preferred_height (GtkWidget *widget,
76 gint *minimal_height,
77 gint *natural_height);
78 static void e_calendar_size_allocate (GtkWidget *widget,
79 GtkAllocation *allocation);
80 static gint e_calendar_drag_motion (GtkWidget *widget,
81 GdkDragContext *context,
82 gint x,
83 gint y,
84 guint time);
85 static void e_calendar_drag_leave (GtkWidget *widget,
86 GdkDragContext *context,
87 guint time);
88 static gboolean e_calendar_button_has_focus (ECalendar *cal);
89 static gboolean e_calendar_focus (GtkWidget *widget,
90 GtkDirectionType direction);
91
92 static void e_calendar_on_prev_pressed (ECalendar *cal);
93 static void e_calendar_on_prev_released (ECalendar *cal);
94 static void e_calendar_on_prev_clicked (ECalendar *cal);
95 static void e_calendar_on_next_pressed (ECalendar *cal);
96 static void e_calendar_on_next_released (ECalendar *cal);
97 static void e_calendar_on_next_clicked (ECalendar *cal);
98 static void e_calendar_on_prev_year_pressed (ECalendar *cal);
99 static void e_calendar_on_prev_year_released (ECalendar *cal);
100 static void e_calendar_on_prev_year_clicked (ECalendar *cal);
101 static void e_calendar_on_next_year_pressed (ECalendar *cal);
102 static void e_calendar_on_next_year_released (ECalendar *cal);
103 static void e_calendar_on_next_year_clicked (ECalendar *cal);
104
105 static void e_calendar_start_auto_move (ECalendar *cal,
106 gboolean moving_forward);
107 static gboolean e_calendar_auto_move_handler (gpointer data);
108 static void e_calendar_start_auto_move_year (ECalendar *cal,
109 gboolean moving_forward);
110 static gboolean e_calendar_auto_move_year_handler (gpointer data);
111 static void e_calendar_stop_auto_move (ECalendar *cal);
112
113 G_DEFINE_TYPE (
114 ECalendar,
115 e_calendar,
116 E_TYPE_CANVAS)
117
118 static void
119 e_calendar_class_init (ECalendarClass *class)
120 {
121 GObjectClass *object_class;
122 GtkWidgetClass *widget_class;
123
124 object_class = (GObjectClass *) class;
125 widget_class = (GtkWidgetClass *) class;
126
127 object_class->dispose = e_calendar_dispose;
128
129 widget_class->realize = e_calendar_realize;
130 widget_class->style_set = e_calendar_style_set;
131 widget_class->get_preferred_width = e_calendar_get_preferred_width;
132 widget_class->get_preferred_height = e_calendar_get_preferred_height;
133 widget_class->size_allocate = e_calendar_size_allocate;
134 widget_class->drag_motion = e_calendar_drag_motion;
135 widget_class->drag_leave = e_calendar_drag_leave;
136 widget_class->focus = e_calendar_focus;
137 }
138
139 static void
140 e_calendar_init (ECalendar *cal)
141 {
142 GnomeCanvasGroup *canvas_group;
143 PangoFontDescription *small_font_desc;
144 GtkWidget *button, *pixmap;
145 AtkObject *a11y;
146
147 /* Create the small font. */
148
149 small_font_desc = pango_font_description_copy (
150 gtk_widget_get_style (GTK_WIDGET (cal))->font_desc);
151 pango_font_description_set_size (
152 small_font_desc,
153 E_CALENDAR_SMALL_FONT_PTSIZE * PANGO_SCALE);
154
155 canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (cal)->root);
156
157 cal->calitem = E_CALENDAR_ITEM (
158 gnome_canvas_item_new (
159 canvas_group,
160 e_calendar_item_get_type (),
161 "week_number_font_desc", small_font_desc,
162 NULL));
163
164 pango_font_description_free (small_font_desc);
165
166 /* Create the arrow buttons to move to the previous/next month. */
167 button = gtk_button_new ();
168 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
169 gtk_widget_show (button);
170 g_signal_connect_swapped (
171 button, "pressed",
172 G_CALLBACK (e_calendar_on_prev_pressed), cal);
173 g_signal_connect_swapped (
174 button, "released",
175 G_CALLBACK (e_calendar_on_prev_released), cal);
176 g_signal_connect_swapped (
177 button, "clicked",
178 G_CALLBACK (e_calendar_on_prev_clicked), cal);
179
180 pixmap = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
181 gtk_widget_show (pixmap);
182 gtk_container_add (GTK_CONTAINER (button), pixmap);
183
184 cal->prev_item = gnome_canvas_item_new (
185 canvas_group,
186 gnome_canvas_widget_get_type (),
187 "widget", button,
188 NULL);
189 a11y = gtk_widget_get_accessible (button);
190 atk_object_set_name (a11y, _("Previous month"));
191
192 button = gtk_button_new ();
193 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
194 gtk_widget_show (button);
195 g_signal_connect_swapped (
196 button, "pressed",
197 G_CALLBACK (e_calendar_on_next_pressed), cal);
198 g_signal_connect_swapped (
199 button, "released",
200 G_CALLBACK (e_calendar_on_next_released), cal);
201 g_signal_connect_swapped (
202 button, "clicked",
203 G_CALLBACK (e_calendar_on_next_clicked), cal);
204
205 pixmap = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
206 gtk_widget_show (pixmap);
207 gtk_container_add (GTK_CONTAINER (button), pixmap);
208
209 cal->next_item = gnome_canvas_item_new (
210 canvas_group,
211 gnome_canvas_widget_get_type (),
212 "widget", button,
213 NULL);
214 a11y = gtk_widget_get_accessible (button);
215 atk_object_set_name (a11y, _("Next month"));
216
217 /* Create the arrow buttons to move to the previous/next year. */
218 button = gtk_button_new ();
219 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
220 gtk_widget_show (button);
221 g_signal_connect_swapped (
222 button, "pressed",
223 G_CALLBACK (e_calendar_on_prev_year_pressed), cal);
224 g_signal_connect_swapped (
225 button, "released",
226 G_CALLBACK (e_calendar_on_prev_year_released), cal);
227 g_signal_connect_swapped (
228 button, "clicked",
229 G_CALLBACK (e_calendar_on_prev_year_clicked), cal);
230
231 pixmap = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
232 gtk_widget_show (pixmap);
233 gtk_container_add (GTK_CONTAINER (button), pixmap);
234
235 cal->prev_item_year = gnome_canvas_item_new (
236 canvas_group,
237 gnome_canvas_widget_get_type (),
238 "widget", button,
239 NULL);
240 a11y = gtk_widget_get_accessible (button);
241 atk_object_set_name (a11y, _("Previous year"));
242
243 button = gtk_button_new ();
244 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
245 gtk_widget_show (button);
246 g_signal_connect_swapped (
247 button, "pressed",
248 G_CALLBACK (e_calendar_on_next_year_pressed), cal);
249 g_signal_connect_swapped (
250 button, "released",
251 G_CALLBACK (e_calendar_on_next_year_released), cal);
252 g_signal_connect_swapped (
253 button, "clicked",
254 G_CALLBACK (e_calendar_on_next_year_clicked), cal);
255
256 pixmap = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
257 gtk_widget_show (pixmap);
258 gtk_container_add (GTK_CONTAINER (button), pixmap);
259
260 cal->next_item_year = gnome_canvas_item_new (
261 canvas_group,
262 gnome_canvas_widget_get_type (),
263 "widget", button,
264 NULL);
265 a11y = gtk_widget_get_accessible (button);
266 atk_object_set_name (a11y, _("Next year"));
267
268 cal->min_rows = 1;
269 cal->min_cols = 1;
270 cal->max_rows = -1;
271 cal->max_cols = -1;
272
273 cal->timeout_id = 0;
274 }
275
276 /**
277 * e_calendar_new:
278 * @Returns: a new #ECalendar.
279 *
280 * Creates a new #ECalendar.
281 **/
282 GtkWidget *
283 e_calendar_new (void)
284 {
285 GtkWidget *cal;
286 AtkObject *a11y;
287
288 cal = g_object_new (e_calendar_get_type (), NULL);
289 a11y = gtk_widget_get_accessible (cal);
290 atk_object_set_name (a11y, _("Month Calendar"));
291
292 return cal;
293 }
294
295 static void
296 e_calendar_dispose (GObject *object)
297 {
298 ECalendar *cal;
299
300 g_return_if_fail (object != NULL);
301 g_return_if_fail (E_IS_CALENDAR (object));
302
303 cal = E_CALENDAR (object);
304
305 if (cal->timeout_id != 0) {
306 g_source_remove (cal->timeout_id);
307 cal->timeout_id = 0;
308 }
309
310 /* Chain up to parent's dispose() method. */
311 G_OBJECT_CLASS (e_calendar_parent_class)->dispose (object);
312 }
313
314 static void
315 e_calendar_realize (GtkWidget *widget)
316 {
317 GtkStyle *style;
318 GdkWindow *window;
319
320 (*GTK_WIDGET_CLASS (e_calendar_parent_class)->realize) (widget);
321
322 /* Set the background of the canvas window to the normal color,
323 * or the arrow buttons are not displayed properly. */
324 style = gtk_widget_get_style (widget);
325 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
326 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
327 }
328
329 static void
330 e_calendar_style_set (GtkWidget *widget,
331 GtkStyle *previous_style)
332 {
333 ECalendar *e_calendar;
334
335 e_calendar = E_CALENDAR (widget);
336 if (GTK_WIDGET_CLASS (e_calendar_parent_class)->style_set)
337 (*GTK_WIDGET_CLASS (e_calendar_parent_class)->style_set) (widget,
338 previous_style);
339
340 /* Set the background of the canvas window to the normal color,
341 * or the arrow buttons are not displayed properly. */
342 if (gtk_widget_get_realized (widget)) {
343 GtkStyle *style;
344 GdkWindow *window;
345
346 style = gtk_widget_get_style (widget);
347 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
348 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
349 }
350 e_calendar_item_style_set (widget, e_calendar->calitem);
351 }
352
353 static void
354 e_calendar_get_preferred_width (GtkWidget *widget,
355 gint *minimum,
356 gint *natural)
357 {
358 ECalendar *cal;
359 GtkStyle *style;
360 gint col_width;
361
362 cal = E_CALENDAR (widget);
363 style = gtk_widget_get_style (GTK_WIDGET (cal));
364
365 g_object_get ((cal->calitem), "column_width", &col_width, NULL);
366
367 *minimum = *natural = col_width * cal->min_cols + style->xthickness * 2;
368 }
369
370 static void
371 e_calendar_get_preferred_height (GtkWidget *widget,
372 gint *minimum,
373 gint *natural)
374 {
375 ECalendar *cal;
376 GtkStyle *style;
377 gint row_height;
378
379 cal = E_CALENDAR (widget);
380 style = gtk_widget_get_style (GTK_WIDGET (cal));
381
382 g_object_get ((cal->calitem), "row_height", &row_height, NULL);
383
384 *minimum = *natural = row_height * cal->min_rows + style->ythickness * 2;
385 }
386
387 static void
388 e_calendar_size_allocate (GtkWidget *widget,
389 GtkAllocation *allocation)
390 {
391 ECalendar *cal;
392 GtkStyle *style;
393 GtkAllocation old_allocation;
394 PangoFontDescription *font_desc;
395 PangoContext *pango_context;
396 PangoFontMetrics *font_metrics;
397 gdouble old_x2, old_y2, new_x2, new_y2;
398 gdouble xthickness, ythickness, arrow_button_size, current_x, month_width;
399 gboolean is_rtl;
400
401 cal = E_CALENDAR (widget);
402 style = gtk_widget_get_style (widget);
403 xthickness = style->xthickness;
404 ythickness = style->ythickness;
405
406 (*GTK_WIDGET_CLASS (e_calendar_parent_class)->size_allocate) (widget, allocation);
407
408 /* Set up Pango prerequisites */
409 font_desc = gtk_widget_get_style (widget)->font_desc;
410 pango_context = gtk_widget_get_pango_context (widget);
411 font_metrics = pango_context_get_metrics (
412 pango_context, font_desc,
413 pango_context_get_language (pango_context));
414
415 /* Set the scroll region to its allocated size, if changed. */
416 gnome_canvas_get_scroll_region (
417 GNOME_CANVAS (cal),
418 NULL, NULL, &old_x2, &old_y2);
419 gtk_widget_get_allocation (widget, &old_allocation);
420 new_x2 = old_allocation.width - 1;
421 new_y2 = old_allocation.height - 1;
422 if (old_x2 != new_x2 || old_y2 != new_y2)
423 gnome_canvas_set_scroll_region (
424 GNOME_CANVAS (cal),
425 0, 0, new_x2, new_y2);
426
427 /* Take off space for line & buttons if shown. */
428 gnome_canvas_item_set (
429 GNOME_CANVAS_ITEM (cal->calitem),
430 "x1", 0.0,
431 "y1", 0.0,
432 "x2", new_x2,
433 "y2", new_y2,
434 NULL);
435
436 if (cal->calitem->month_width > 0)
437 month_width = cal->calitem->month_width;
438 else
439 month_width = new_x2;
440 month_width -= E_CALENDAR_ITEM_MIN_CELL_XPAD + E_CALENDAR_ARROW_BUTTON_X_PAD;
441
442 /* Position the arrow buttons. */
443 arrow_button_size =
444 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
445 + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
446 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
447 + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
448 - E_CALENDAR_ARROW_BUTTON_Y_PAD * 2 - 2;
449
450 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
451 current_x = is_rtl ?
452 (month_width - 2 * xthickness - E_CALENDAR_ARROW_BUTTON_X_PAD - arrow_button_size) :
453 (xthickness);
454
455 gnome_canvas_item_set (
456 cal->prev_item,
457 "x", current_x,
458 "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD,
459 "width", arrow_button_size,
460 "height", arrow_button_size,
461 NULL);
462
463 current_x += (is_rtl ? -1.0 : +1.0) * (cal->calitem->max_month_name_width - xthickness + 2 * arrow_button_size);
464
465 gnome_canvas_item_set (
466 cal->next_item,
467 "x", current_x,
468 "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD,
469 "width", arrow_button_size,
470 "height", arrow_button_size,
471 NULL);
472
473 current_x = is_rtl ?
474 (xthickness) :
475 (month_width - 2 * xthickness - E_CALENDAR_ARROW_BUTTON_X_PAD - arrow_button_size);
476
477 gnome_canvas_item_set (
478 cal->next_item_year,
479 "x", current_x,
480 "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD,
481 "width", arrow_button_size,
482 "height", arrow_button_size,
483 NULL);
484
485 current_x += (is_rtl ? +1.0 : -1.0) * (cal->calitem->max_digit_width * 5 - xthickness + 2 * arrow_button_size);
486
487 gnome_canvas_item_set (
488 cal->prev_item_year,
489 "x", current_x,
490 "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD,
491 "width", arrow_button_size,
492 "height", arrow_button_size,
493 NULL);
494
495 pango_font_metrics_unref (font_metrics);
496 }
497
498 void
499 e_calendar_set_minimum_size (ECalendar *cal,
500 gint rows,
501 gint cols)
502 {
503 g_return_if_fail (E_IS_CALENDAR (cal));
504
505 cal->min_rows = rows;
506 cal->min_cols = cols;
507
508 gnome_canvas_item_set (
509 GNOME_CANVAS_ITEM (cal->calitem),
510 "minimum_rows", rows,
511 "minimum_columns", cols,
512 NULL);
513
514 gtk_widget_queue_resize (GTK_WIDGET (cal));
515 }
516
517 void
518 e_calendar_set_maximum_size (ECalendar *cal,
519 gint rows,
520 gint cols)
521 {
522 g_return_if_fail (E_IS_CALENDAR (cal));
523
524 cal->max_rows = rows;
525 cal->max_cols = cols;
526
527 gnome_canvas_item_set (
528 GNOME_CANVAS_ITEM (cal->calitem),
529 "maximum_rows", rows,
530 "maximum_columns", cols,
531 NULL);
532
533 gtk_widget_queue_resize (GTK_WIDGET (cal));
534 }
535
536 /* Returns the border size on each side of the month displays. */
537 void
538 e_calendar_get_border_size (ECalendar *cal,
539 gint *top,
540 gint *bottom,
541 gint *left,
542 gint *right)
543 {
544 GtkStyle *style;
545
546 g_return_if_fail (E_IS_CALENDAR (cal));
547
548 style = gtk_widget_get_style (GTK_WIDGET (cal));
549
550 if (style) {
551 *top = style->ythickness;
552 *bottom = style->ythickness;
553 *left = style->xthickness;
554 *right = style->xthickness;
555 } else {
556 *top = *bottom = *left = *right = 0;
557 }
558 }
559
560 static void
561 e_calendar_on_prev_pressed (ECalendar *cal)
562 {
563 e_calendar_start_auto_move (cal, FALSE);
564 }
565
566 static void
567 e_calendar_on_next_pressed (ECalendar *cal)
568 {
569 e_calendar_start_auto_move (cal, TRUE);
570 }
571
572 static void
573 e_calendar_on_prev_year_pressed (ECalendar *cal)
574 {
575 e_calendar_start_auto_move_year (cal, FALSE);
576 }
577
578 static void
579 e_calendar_on_next_year_pressed (ECalendar *cal)
580 {
581 e_calendar_start_auto_move_year (cal, TRUE);
582 }
583
584 static void
585 e_calendar_start_auto_move (ECalendar *cal,
586 gboolean moving_forward)
587 {
588 if (cal->timeout_id == 0) {
589 cal->timeout_id = g_timeout_add (
590 E_CALENDAR_AUTO_MOVE_TIMEOUT,
591 e_calendar_auto_move_handler,
592 cal);
593 }
594 cal->timeout_delay = E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY;
595 cal->moving_forward = moving_forward;
596
597 }
598
599 static void
600 e_calendar_start_auto_move_year (ECalendar *cal,
601 gboolean moving_forward)
602 {
603 if (cal->timeout_id == 0) {
604 cal->timeout_id = g_timeout_add (
605 E_CALENDAR_AUTO_MOVE_TIMEOUT,
606 e_calendar_auto_move_year_handler,
607 cal);
608 }
609 cal->timeout_delay = E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY;
610 cal->moving_forward = moving_forward;
611 }
612
613 static gboolean
614 e_calendar_auto_move_year_handler (gpointer data)
615 {
616 ECalendar *cal;
617 ECalendarItem *calitem;
618 gint offset;
619
620 g_return_val_if_fail (E_IS_CALENDAR (data), FALSE);
621
622 cal = E_CALENDAR (data);
623 calitem = cal->calitem;
624
625 if (cal->timeout_delay > 0) {
626 cal->timeout_delay--;
627 } else {
628 offset = cal->moving_forward ? 12 : -12;
629 e_calendar_item_set_first_month (
630 calitem, calitem->year,
631 calitem->month + offset);
632 }
633
634 return TRUE;
635 }
636
637 static gboolean
638 e_calendar_auto_move_handler (gpointer data)
639 {
640 ECalendar *cal;
641 ECalendarItem *calitem;
642 gint offset;
643
644 g_return_val_if_fail (E_IS_CALENDAR (data), FALSE);
645
646 cal = E_CALENDAR (data);
647 calitem = cal->calitem;
648
649 if (cal->timeout_delay > 0) {
650 cal->timeout_delay--;
651 } else {
652 offset = cal->moving_forward ? 1 : -1;
653 e_calendar_item_set_first_month (
654 calitem, calitem->year,
655 calitem->month + offset);
656 }
657
658 return TRUE;
659 }
660
661 static void
662 e_calendar_on_prev_released (ECalendar *cal)
663 {
664 e_calendar_stop_auto_move (cal);
665 }
666
667 static void
668 e_calendar_on_next_released (ECalendar *cal)
669 {
670 e_calendar_stop_auto_move (cal);
671 }
672
673 static void
674 e_calendar_on_prev_year_released (ECalendar *cal)
675 {
676 e_calendar_stop_auto_move (cal);
677 }
678
679 static void
680 e_calendar_on_next_year_released (ECalendar *cal)
681 {
682 e_calendar_stop_auto_move (cal);
683 }
684
685 static void
686 e_calendar_stop_auto_move (ECalendar *cal)
687 {
688 if (cal->timeout_id != 0) {
689 g_source_remove (cal->timeout_id);
690 cal->timeout_id = 0;
691 }
692 }
693
694 static void
695 e_calendar_on_prev_clicked (ECalendar *cal)
696 {
697 e_calendar_item_set_first_month (
698 cal->calitem, cal->calitem->year,
699 cal->calitem->month - 1);
700 }
701
702 static void
703 e_calendar_on_next_clicked (ECalendar *cal)
704 {
705 e_calendar_item_set_first_month (
706 cal->calitem, cal->calitem->year,
707 cal->calitem->month + 1);
708 }
709
710 static void
711 e_calendar_on_prev_year_clicked (ECalendar *cal)
712 {
713 e_calendar_item_set_first_month (
714 cal->calitem, cal->calitem->year,
715 cal->calitem->month - 12);
716 }
717
718 static void
719 e_calendar_on_next_year_clicked (ECalendar *cal)
720 {
721 e_calendar_item_set_first_month (
722 cal->calitem, cal->calitem->year,
723 cal->calitem->month + 12);
724 }
725
726 static gint
727 e_calendar_drag_motion (GtkWidget *widget,
728 GdkDragContext *context,
729 gint x,
730 gint y,
731 guint time)
732 {
733 return FALSE;
734 }
735
736 static void
737 e_calendar_drag_leave (GtkWidget *widget,
738 GdkDragContext *context,
739 guint time)
740 {
741 }
742
743 static gboolean
744 e_calendar_button_has_focus (ECalendar *cal)
745 {
746 GtkWidget *prev_widget, *next_widget;
747 gboolean ret_val;
748
749 g_return_val_if_fail (E_IS_CALENDAR (cal), FALSE);
750
751 prev_widget = GNOME_CANVAS_WIDGET (cal->prev_item)->widget;
752 next_widget = GNOME_CANVAS_WIDGET (cal->next_item)->widget;
753 ret_val = gtk_widget_has_focus (prev_widget) ||
754 gtk_widget_has_focus (next_widget);
755 return ret_val;
756 }
757
758 static gboolean
759 e_calendar_focus (GtkWidget *widget,
760 GtkDirectionType direction)
761 {
762 #define E_CALENDAR_FOCUS_CHILDREN_NUM 5
763 ECalendar *cal;
764 GnomeCanvas *canvas;
765 GnomeCanvasItem *children[E_CALENDAR_FOCUS_CHILDREN_NUM];
766 gint focused_index = -1;
767 gint index;
768
769 g_return_val_if_fail (widget != NULL, FALSE);
770 g_return_val_if_fail (E_IS_CALENDAR (widget), FALSE);
771 cal = E_CALENDAR (widget);
772 canvas = GNOME_CANVAS (widget);
773
774 if (!gtk_widget_get_can_focus (widget))
775 return FALSE;
776
777 children[0] = GNOME_CANVAS_ITEM (cal->calitem);
778 children[1] = cal->prev_item;
779 children[2] = cal->next_item;
780 children[3] = cal->prev_item_year;
781 children[4] = cal->next_item_year;
782
783 /* get current focused item, if e-calendar has had focus */
784 if (gtk_widget_has_focus (widget) || e_calendar_button_has_focus (cal))
785 for (index = 0; index < E_CALENDAR_FOCUS_CHILDREN_NUM; ++index) {
786 if (canvas->focused_item == NULL)
787 break;
788
789 if (children[index] == canvas->focused_item) {
790 focused_index = index;
791 break;
792 }
793 }
794
795 if (focused_index == -1)
796 if (direction == GTK_DIR_TAB_FORWARD)
797 focused_index = 0;
798 else
799 focused_index = E_CALENDAR_FOCUS_CHILDREN_NUM - 1;
800 else
801 if (direction == GTK_DIR_TAB_FORWARD)
802 ++focused_index;
803 else
804 --focused_index;
805
806 if (focused_index < 0 ||
807 focused_index >= E_CALENDAR_FOCUS_CHILDREN_NUM)
808 /* move out of e-calendar */
809 return FALSE;
810 gnome_canvas_item_grab_focus (children[focused_index]);
811 if (GNOME_IS_CANVAS_WIDGET (children[focused_index])) {
812 widget = GNOME_CANVAS_WIDGET (children[focused_index])->widget;
813 gtk_widget_grab_focus (widget);
814 }
815 return TRUE;
816 }
817
818 void
819 e_calendar_set_focusable (ECalendar *cal,
820 gboolean focusable)
821 {
822 GtkWidget *widget;
823 GtkWidget *prev_widget, *next_widget;
824 GtkWidget *toplevel;
825
826 g_return_if_fail (E_IS_CALENDAR (cal));
827
828 widget = GTK_WIDGET (cal);
829 prev_widget = GNOME_CANVAS_WIDGET (cal->prev_item)->widget;
830 next_widget = GNOME_CANVAS_WIDGET (cal->next_item)->widget;
831
832 if (focusable) {
833 gtk_widget_set_can_focus (widget, TRUE);
834 gtk_widget_set_can_focus (prev_widget, TRUE);
835 gtk_widget_set_can_focus (next_widget, TRUE);
836 }
837 else {
838 if (gtk_widget_has_focus (GTK_WIDGET (cal)) ||
839 e_calendar_button_has_focus (cal)) {
840 toplevel = gtk_widget_get_toplevel (widget);
841 if (toplevel)
842 gtk_widget_grab_focus (toplevel);
843 }
844 gtk_widget_set_can_focus (widget, FALSE);
845 gtk_widget_set_can_focus (prev_widget, FALSE);
846 gtk_widget_set_can_focus (next_widget, FALSE);
847 }
848 }