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/>
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.
36 #include "e-calendar.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
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
,
81 static void e_calendar_drag_leave (GtkWidget
*widget
,
82 GdkDragContext
*context
,
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
)
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
;
123 e_calendar_init (ECalendar
*cal
)
125 GnomeCanvasGroup
*canvas_group
;
126 PangoFontDescription
*small_font_desc
;
127 GtkWidget
*button
, *pixmap
;
130 /* Create the small font. */
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
,
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 (
152 G_CALLBACK (e_calendar_on_prev_pressed
), cal
);
153 g_signal_connect_swapped (
155 G_CALLBACK (e_calendar_on_prev_released
), cal
);
156 g_signal_connect_swapped (
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 (),
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 (
176 G_CALLBACK (e_calendar_on_next_pressed
), cal
);
177 g_signal_connect_swapped (
179 G_CALLBACK (e_calendar_on_next_released
), cal
);
180 g_signal_connect_swapped (
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 (),
192 a11y
= gtk_widget_get_accessible (button
);
193 atk_object_set_name (a11y
, _("Next"));
205 * @Returns: a new #ECalendar.
207 * Creates a new #ECalendar.
210 e_calendar_new (void)
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"));
223 e_calendar_destroy (GtkObject
*object
)
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
);
237 if (GTK_OBJECT_CLASS (e_calendar_parent_class
)->destroy
)
238 (* GTK_OBJECT_CLASS (e_calendar_parent_class
)->destroy
) (object
);
242 e_calendar_realize (GtkWidget
*widget
)
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
]);
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
,
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
)) {
272 if (GTK_WIDGET_REALIZED (widget
)) {
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
);
285 e_calendar_size_request (GtkWidget
*widget
,
286 GtkRequisition
*requisition
)
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
,
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;
308 e_calendar_size_allocate (GtkWidget
*widget
,
309 GtkAllocation
*allocation
)
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
),
351 /* Position the arrow buttons. */
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
363 : xthickness
* 2 + E_CALENDAR_ARROW_BUTTON_X_PAD
,
365 + E_CALENDAR_ARROW_BUTTON_Y_PAD
,
366 "width", arrow_button_size
,
367 "height", arrow_button_size
,
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
376 + E_CALENDAR_ARROW_BUTTON_Y_PAD
,
377 "width", arrow_button_size
,
378 "height", arrow_button_size
,
381 pango_font_metrics_unref (font_metrics
);
385 e_calendar_set_minimum_size (ECalendar
*cal
,
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
,
399 gtk_widget_queue_resize (GTK_WIDGET (cal
));
403 e_calendar_set_maximum_size (ECalendar
*cal
,
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
,
417 gtk_widget_queue_resize (GTK_WIDGET (cal
));
420 /* Returns the border size on each side of the month displays. */
422 e_calendar_get_border_size (ECalendar
*cal
,
430 g_return_if_fail (E_IS_CALENDAR (cal
));
432 style
= gtk_widget_get_style (GTK_WIDGET (cal
));
435 *top
= style
->ythickness
;
436 *bottom
= style
->ythickness
;
437 *left
= style
->xthickness
;
438 *right
= style
->xthickness
;
440 *top
= *bottom
= *left
= *right
= 0;
445 e_calendar_on_prev_pressed (ECalendar
*cal
)
447 e_calendar_start_auto_move (cal
, FALSE
);
451 e_calendar_on_next_pressed (ECalendar
*cal
)
453 e_calendar_start_auto_move (cal
, TRUE
);
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
,
465 cal
->timeout_delay
= E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY
;
466 cal
->moving_forward
= moving_forward
;
471 e_calendar_auto_move_handler (gpointer data
)
474 ECalendarItem
*calitem
;
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
--;
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 ();
497 e_calendar_on_prev_released (ECalendar
*cal
)
499 e_calendar_stop_auto_move (cal
);
503 e_calendar_on_next_released (ECalendar
*cal
)
505 e_calendar_stop_auto_move (cal
);
509 e_calendar_stop_auto_move (ECalendar
*cal
)
511 if (cal
->timeout_id
!= 0) {
512 g_source_remove (cal
->timeout_id
);
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);
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);
532 e_calendar_drag_motion (GtkWidget
*widget
,
533 GdkDragContext
*context
,
539 g_print ("In e_calendar_drag_motion\n");
546 e_calendar_drag_leave (GtkWidget
*widget
,
547 GdkDragContext
*context
,
551 g_print ("In e_calendar_drag_leave\n");
556 e_calendar_button_has_focus (ECalendar
*cal
)
558 GtkWidget
*prev_widget
, *next_widget
;
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
);
569 ret_val
= GTK_WIDGET_HAS_FOCUS (prev_widget
) ||
570 GTK_WIDGET_HAS_FOCUS (next_widget
);
576 e_calendar_focus (GtkWidget
*widget
, GtkDirectionType direction
)
578 #define E_CALENDAR_FOCUS_CHILDREN_NUM 3
581 GnomeCanvasItem
*children
[E_CALENDAR_FOCUS_CHILDREN_NUM
];
582 gint focused_index
= -1;
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
))
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
))
601 if (GTK_WIDGET_HAS_FOCUS (widget
) || e_calendar_button_has_focus (cal
))
603 for (index
= 0; index
< E_CALENDAR_FOCUS_CHILDREN_NUM
; ++index
) {
604 if (canvas
->focused_item
== NULL
)
607 if (children
[index
] == canvas
->focused_item
) {
608 focused_index
= index
;
613 if (focused_index
== -1)
614 if (direction
== GTK_DIR_TAB_FORWARD
)
617 focused_index
= E_CALENDAR_FOCUS_CHILDREN_NUM
- 1;
619 if (direction
== GTK_DIR_TAB_FORWARD
)
624 if (focused_index
< 0 ||
625 focused_index
>= E_CALENDAR_FOCUS_CHILDREN_NUM
)
626 /* move out of e-calendar */
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
);
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
;
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
);
652 #if GTK_CHECK_VERSION(2,19,7)
653 if (gtk_widget_has_focus (GTK_WIDGET (cal
)) || e_calendar_button_has_focus (cal
)) {
655 if (GTK_WIDGET_HAS_FOCUS (cal
) || e_calendar_button_has_focus (cal
)) {
657 GtkWidget
*toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (cal
));
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
);