1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
5 * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Groenlund
8 * Copyright (c) 1995, 1996, 1997, 1998 by Steffen Beyer
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free
22 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
27 * file for a list of people on the GTK+ Team. See the ChangeLog
28 * files for a list of changes. These files are distributed with
29 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
34 * @Short_description: Displays a calendar and allows the user to select a date
37 * #GtkCalendar is a widget that displays a calendar, one month at a time. It
38 * can be created with gtk_calendar_new().
40 * The month and year currently displayed can be altered with
41 * gtk_calendar_select_month(). The exact day can be selected from the displayed
42 * month using gtk_calendar_select_day().
44 * To place a visual marker on a particular day, use gtk_calendar_mark_day() and
45 * to remove the marker, gtk_calendar_unmark_day(). Alternative, all marks can
46 * be cleared with gtk_calendar_clear_marks().
48 * The way in which the calendar itself is displayed can be altered using
49 * gtk_calendar_set_display_options().
51 * The selected date can be retrieved from a #GtkCalendar using
52 * gtk_calendar_get_date().
57 #ifdef HAVE_SYS_TIME_H
60 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
73 #include "gtkcalendar.h"
77 #include "gtkmarshalers.h"
78 #include "gtktooltip.h"
79 #include "gtkprivate.h"
80 #include "gdk/gdkkeysyms.h"
82 /***************************************************************************/
83 /* The following date routines are taken from the lib_date package.
84 * They have been minimally edited to avoid conflict with types defined
88 static const guint month_length[2][13] =
90 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
91 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
94 static const guint days_in_months[2][14] =
96 { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
97 { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
100 static glong calc_days(guint year, guint mm, guint dd);
101 static guint day_of_week(guint year, guint mm, guint dd);
102 static glong dates_difference(guint year1, guint mm1, guint dd1,
103 guint year2, guint mm2, guint dd2);
104 static guint weeks_in_year(guint year);
109 return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
113 day_of_week (guint year, guint mm, guint dd)
117 days = calc_days(year, mm, dd);
124 return( (guint) days );
127 static guint weeks_in_year(guint year)
129 return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
133 check_date(guint year, guint mm, guint dd)
135 if (year < 1) return FALSE;
136 if ((mm < 1) || (mm > 12)) return FALSE;
137 if ((dd < 1) || (dd > month_length[leap(year)][mm])) return FALSE;
142 week_number(guint year, guint mm, guint dd)
146 first = day_of_week(year,1,1) - 1;
147 return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
152 year_to_days(guint year)
154 return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
159 calc_days(guint year, guint mm, guint dd)
163 if (year < 1) return(0L);
164 if ((mm < 1) || (mm > 12)) return(0L);
165 if ((dd < 1) || (dd > month_length[(lp = leap(year))][mm])) return(0L);
166 return( year_to_days(--year) + days_in_months[lp][mm] + dd );
170 week_of_year(guint *week, guint *year, guint mm, guint dd)
172 if (check_date(*year,mm,dd))
174 *week = week_number(*year,mm,dd);
176 *week = weeks_in_year(--(*year));
177 else if (*week > weeks_in_year(*year))
188 dates_difference(guint year1, guint mm1, guint dd1,
189 guint year2, guint mm2, guint dd2)
191 return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
194 /*** END OF lib_date routines ********************************************/
196 /* Spacing around day/week headers and main area, inside those windows */
197 #define CALENDAR_MARGIN 0
199 #define DAY_XSEP 0 /* not really good for small calendar */
200 #define DAY_YSEP 0 /* not really good for small calendar */
202 #define SCROLL_DELAY_FACTOR 5
205 #define HEADER_FG_COLOR(widget) (& (widget)->style->fg[gtk_widget_get_state (widget)])
206 #define HEADER_BG_COLOR(widget) (& (widget)->style->bg[gtk_widget_get_state (widget)])
207 #define SELECTED_BG_COLOR(widget) (& (widget)->style->base[gtk_widget_has_focus (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
208 #define SELECTED_FG_COLOR(widget) (& (widget)->style->text[gtk_widget_has_focus (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
209 #define NORMAL_DAY_COLOR(widget) (& (widget)->style->text[gtk_widget_get_state (widget)])
210 #define PREV_MONTH_COLOR(widget) (& (widget)->style->mid[gtk_widget_get_state (widget)])
211 #define NEXT_MONTH_COLOR(widget) (& (widget)->style->mid[gtk_widget_get_state (widget)])
212 #define MARKED_COLOR(widget) (& (widget)->style->text[gtk_widget_get_state (widget)])
213 #define BACKGROUND_COLOR(widget) (& (widget)->style->base[gtk_widget_get_state (widget)])
214 #define HIGHLIGHT_BACK_COLOR(widget) (& (widget)->style->mid[gtk_widget_get_state (widget)])
230 MONTH_CHANGED_SIGNAL,
232 DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
248 PROP_NO_MONTH_CHANGE,
249 PROP_SHOW_WEEK_NUMBERS,
251 PROP_DETAIL_WIDTH_CHARS,
252 PROP_DETAIL_HEIGHT_ROWS
255 static guint gtk_calendar_signals[LAST_SIGNAL] = { 0 };
257 struct _GtkCalendarPrivate
259 GtkCalendarDisplayOptions display_flags;
260 GtkStyle *header_style;
261 GtkStyle *label_style;
263 GdkColor marked_date_color[31];
264 GdkWindow *header_win;
265 GdkWindow *day_name_win;
268 GdkWindow *arrow_win[4];
270 gchar grow_space [32];
276 gint day_month[6][7];
279 gint num_marked_dates;
280 gint marked_date[31];
292 guint arrow_state[4];
294 guint max_month_width;
295 guint max_year_width;
301 guint max_day_char_width;
302 guint max_day_char_ascent;
303 guint max_day_char_descent;
304 guint max_label_char_ascent;
305 guint max_label_char_descent;
306 guint max_week_char_width;
309 guint year_before : 1;
311 guint need_timer : 1;
314 guint drag_highlight : 1;
324 /* Optional callback, used to display extra information for each day. */
325 GtkCalendarDetailFunc detail_func;
326 gpointer detail_func_user_data;
327 GDestroyNotify detail_func_destroy;
329 /* Size requistion for details provided by the hook. */
330 gint detail_height_rows;
331 gint detail_width_chars;
332 gint detail_overflow[6];
335 #define GTK_CALENDAR_GET_PRIVATE(widget) (GTK_CALENDAR (widget)->priv)
337 static void gtk_calendar_finalize (GObject *calendar);
338 static void gtk_calendar_destroy (GtkObject *calendar);
339 static void gtk_calendar_set_property (GObject *object,
343 static void gtk_calendar_get_property (GObject *object,
348 static void gtk_calendar_realize (GtkWidget *widget);
349 static void gtk_calendar_unrealize (GtkWidget *widget);
350 static void gtk_calendar_size_request (GtkWidget *widget,
351 GtkRequisition *requisition);
352 static void gtk_calendar_size_allocate (GtkWidget *widget,
353 GtkAllocation *allocation);
354 static gboolean gtk_calendar_expose (GtkWidget *widget,
355 GdkEventExpose *event);
356 static gboolean gtk_calendar_button_press (GtkWidget *widget,
357 GdkEventButton *event);
358 static gboolean gtk_calendar_button_release (GtkWidget *widget,
359 GdkEventButton *event);
360 static gboolean gtk_calendar_motion_notify (GtkWidget *widget,
361 GdkEventMotion *event);
362 static gboolean gtk_calendar_enter_notify (GtkWidget *widget,
363 GdkEventCrossing *event);
364 static gboolean gtk_calendar_leave_notify (GtkWidget *widget,
365 GdkEventCrossing *event);
366 static gboolean gtk_calendar_scroll (GtkWidget *widget,
367 GdkEventScroll *event);
368 static gboolean gtk_calendar_key_press (GtkWidget *widget,
370 static gboolean gtk_calendar_focus_out (GtkWidget *widget,
371 GdkEventFocus *event);
372 static void gtk_calendar_grab_notify (GtkWidget *widget,
373 gboolean was_grabbed);
374 static void gtk_calendar_state_changed (GtkWidget *widget,
375 GtkStateType previous_state);
376 static void gtk_calendar_style_set (GtkWidget *widget,
377 GtkStyle *previous_style);
378 static gboolean gtk_calendar_query_tooltip (GtkWidget *widget,
381 gboolean keyboard_mode,
382 GtkTooltip *tooltip);
384 static void gtk_calendar_drag_data_get (GtkWidget *widget,
385 GdkDragContext *context,
386 GtkSelectionData *selection_data,
389 static void gtk_calendar_drag_data_received (GtkWidget *widget,
390 GdkDragContext *context,
393 GtkSelectionData *selection_data,
396 static gboolean gtk_calendar_drag_motion (GtkWidget *widget,
397 GdkDragContext *context,
401 static void gtk_calendar_drag_leave (GtkWidget *widget,
402 GdkDragContext *context,
404 static gboolean gtk_calendar_drag_drop (GtkWidget *widget,
405 GdkDragContext *context,
410 static void calendar_start_spinning (GtkCalendar *calendar,
412 static void calendar_stop_spinning (GtkCalendar *calendar);
414 static void calendar_invalidate_day (GtkCalendar *widget,
417 static void calendar_invalidate_day_num (GtkCalendar *widget,
419 static void calendar_invalidate_arrow (GtkCalendar *widget,
422 static void calendar_compute_days (GtkCalendar *calendar);
423 static gint calendar_get_xsep (GtkCalendar *calendar);
424 static gint calendar_get_ysep (GtkCalendar *calendar);
426 static char *default_abbreviated_dayname[7];
427 static char *default_monthname[12];
429 G_DEFINE_TYPE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET)
432 gtk_calendar_class_init (GtkCalendarClass *class)
434 GObjectClass *gobject_class;
435 GtkObjectClass *object_class;
436 GtkWidgetClass *widget_class;
438 gobject_class = (GObjectClass*) class;
439 object_class = (GtkObjectClass*) class;
440 widget_class = (GtkWidgetClass*) class;
442 gobject_class->set_property = gtk_calendar_set_property;
443 gobject_class->get_property = gtk_calendar_get_property;
444 gobject_class->finalize = gtk_calendar_finalize;
446 object_class->destroy = gtk_calendar_destroy;
448 widget_class->realize = gtk_calendar_realize;
449 widget_class->unrealize = gtk_calendar_unrealize;
450 widget_class->expose_event = gtk_calendar_expose;
451 widget_class->size_request = gtk_calendar_size_request;
452 widget_class->size_allocate = gtk_calendar_size_allocate;
453 widget_class->button_press_event = gtk_calendar_button_press;
454 widget_class->button_release_event = gtk_calendar_button_release;
455 widget_class->motion_notify_event = gtk_calendar_motion_notify;
456 widget_class->enter_notify_event = gtk_calendar_enter_notify;
457 widget_class->leave_notify_event = gtk_calendar_leave_notify;
458 widget_class->key_press_event = gtk_calendar_key_press;
459 widget_class->scroll_event = gtk_calendar_scroll;
460 widget_class->style_set = gtk_calendar_style_set;
461 widget_class->state_changed = gtk_calendar_state_changed;
462 widget_class->grab_notify = gtk_calendar_grab_notify;
463 widget_class->focus_out_event = gtk_calendar_focus_out;
464 widget_class->query_tooltip = gtk_calendar_query_tooltip;
466 widget_class->drag_data_get = gtk_calendar_drag_data_get;
467 widget_class->drag_motion = gtk_calendar_drag_motion;
468 widget_class->drag_leave = gtk_calendar_drag_leave;
469 widget_class->drag_drop = gtk_calendar_drag_drop;
470 widget_class->drag_data_received = gtk_calendar_drag_data_received;
476 * This property gets initially set to the current year.
478 g_object_class_install_property (gobject_class,
480 g_param_spec_int ("year",
482 P_("The selected year"),
484 GTK_PARAM_READWRITE));
489 * The selected month (as a number between 0 and 11).
490 * This property gets initially set to the current month.
492 g_object_class_install_property (gobject_class,
494 g_param_spec_int ("month",
496 P_("The selected month (as a number between 0 and 11)"),
498 GTK_PARAM_READWRITE));
503 * The selected day (as a number between 1 and 31, or 0
504 * to unselect the currently selected day).
505 * This property gets initially set to the current day.
507 g_object_class_install_property (gobject_class,
509 g_param_spec_int ("day",
511 P_("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
513 GTK_PARAM_READWRITE));
516 * GtkCalendar:show-heading:
518 * Determines whether a heading is displayed.
522 g_object_class_install_property (gobject_class,
524 g_param_spec_boolean ("show-heading",
526 P_("If TRUE, a heading is displayed"),
528 GTK_PARAM_READWRITE));
531 * GtkCalendar:show-day-names:
533 * Determines whether day names are displayed.
537 g_object_class_install_property (gobject_class,
539 g_param_spec_boolean ("show-day-names",
540 P_("Show Day Names"),
541 P_("If TRUE, day names are displayed"),
543 GTK_PARAM_READWRITE));
545 * GtkCalendar:no-month-change:
547 * Determines whether the selected month can be changed.
551 g_object_class_install_property (gobject_class,
552 PROP_NO_MONTH_CHANGE,
553 g_param_spec_boolean ("no-month-change",
554 P_("No Month Change"),
555 P_("If TRUE, the selected month cannot be changed"),
557 GTK_PARAM_READWRITE));
560 * GtkCalendar:show-week-numbers:
562 * Determines whether week numbers are displayed.
566 g_object_class_install_property (gobject_class,
567 PROP_SHOW_WEEK_NUMBERS,
568 g_param_spec_boolean ("show-week-numbers",
569 P_("Show Week Numbers"),
570 P_("If TRUE, week numbers are displayed"),
572 GTK_PARAM_READWRITE));
575 * GtkCalendar:detail-width-chars:
577 * Width of a detail cell, in characters.
578 * A value of 0 allows any width. See gtk_calendar_set_detail_func().
582 g_object_class_install_property (gobject_class,
583 PROP_DETAIL_WIDTH_CHARS,
584 g_param_spec_int ("detail-width-chars",
586 P_("Details width in characters"),
588 GTK_PARAM_READWRITE));
591 * GtkCalendar:detail-height-rows:
593 * Height of a detail cell, in rows.
594 * A value of 0 allows any width. See gtk_calendar_set_detail_func().
598 g_object_class_install_property (gobject_class,
599 PROP_DETAIL_HEIGHT_ROWS,
600 g_param_spec_int ("detail-height-rows",
601 P_("Details Height"),
602 P_("Details height in rows"),
604 GTK_PARAM_READWRITE));
607 * GtkCalendar:show-details:
609 * Determines whether details are shown directly in the widget, or if they are
610 * available only as tooltip. When this property is set days with details are
615 g_object_class_install_property (gobject_class,
617 g_param_spec_boolean ("show-details",
619 P_("If TRUE, details are shown"),
621 GTK_PARAM_READWRITE));
625 * GtkCalendar:inner-border
627 * The spacing around the day/week headers and main area.
629 gtk_widget_class_install_style_property (widget_class,
630 g_param_spec_int ("inner-border",
632 P_("Inner border space"),
634 GTK_PARAM_READABLE));
637 * GtkCalndar:vertical-separation
639 * Separation between day headers and main area.
641 gtk_widget_class_install_style_property (widget_class,
642 g_param_spec_int ("vertical-separation",
643 P_("Vertical separation"),
644 P_("Space between day headers and main area"),
646 GTK_PARAM_READABLE));
649 * GtkCalendar:horizontal-separation
651 * Separation between week headers and main area.
653 gtk_widget_class_install_style_property (widget_class,
654 g_param_spec_int ("horizontal-separation",
655 P_("Horizontal separation"),
656 P_("Space between week headers and main area"),
658 GTK_PARAM_READABLE));
661 * GtkCalendar::month-changed:
662 * @calendar: the object which received the signal.
664 * Emitted when the user clicks a button to change the selected month on a
667 gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
668 g_signal_new (I_("month-changed"),
669 G_OBJECT_CLASS_TYPE (gobject_class),
671 G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
673 _gtk_marshal_VOID__VOID,
677 * GtkCalendar::day-selected:
678 * @calendar: the object which received the signal.
680 * Emitted when the user selects a day.
682 gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
683 g_signal_new (I_("day-selected"),
684 G_OBJECT_CLASS_TYPE (gobject_class),
686 G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
688 _gtk_marshal_VOID__VOID,
692 * GtkCalendar::day-selected-double-click:
693 * @calendar: the object which received the signal.
695 * Emitted when the user double-clicks a day.
697 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
698 g_signal_new (I_("day-selected-double-click"),
699 G_OBJECT_CLASS_TYPE (gobject_class),
701 G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
703 _gtk_marshal_VOID__VOID,
707 * GtkCalendar::prev-month:
708 * @calendar: the object which received the signal.
710 * Emitted when the user switched to the previous month.
712 gtk_calendar_signals[PREV_MONTH_SIGNAL] =
713 g_signal_new (I_("prev-month"),
714 G_OBJECT_CLASS_TYPE (gobject_class),
716 G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
718 _gtk_marshal_VOID__VOID,
722 * GtkCalendar::next-month:
723 * @calendar: the object which received the signal.
725 * Emitted when the user switched to the next month.
727 gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
728 g_signal_new (I_("next-month"),
729 G_OBJECT_CLASS_TYPE (gobject_class),
731 G_STRUCT_OFFSET (GtkCalendarClass, next_month),
733 _gtk_marshal_VOID__VOID,
737 * GtkCalendar::prev-year:
738 * @calendar: the object which received the signal.
740 * Emitted when user switched to the previous year.
742 gtk_calendar_signals[PREV_YEAR_SIGNAL] =
743 g_signal_new (I_("prev-year"),
744 G_OBJECT_CLASS_TYPE (gobject_class),
746 G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
748 _gtk_marshal_VOID__VOID,
752 * GtkCalendar::next-year:
753 * @calendar: the object which received the signal.
755 * Emitted when user switched to the next year.
757 gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
758 g_signal_new (I_("next-year"),
759 G_OBJECT_CLASS_TYPE (gobject_class),
761 G_STRUCT_OFFSET (GtkCalendarClass, next_year),
763 _gtk_marshal_VOID__VOID,
766 g_type_class_add_private (gobject_class, sizeof (GtkCalendarPrivate));
770 gtk_calendar_init (GtkCalendar *calendar)
772 GtkWidget *widget = GTK_WIDGET (calendar);
777 wchar_t wbuffer[100];
782 GtkCalendarPrivate *priv;
784 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
785 union { unsigned int word; char *string; } langinfo;
786 gint week_1stday = 0;
787 gint first_weekday = 1;
793 priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
797 gtk_widget_set_can_focus (widget, TRUE);
799 if (!default_abbreviated_dayname[0])
803 tmp_time= (i+3)*86400;
804 strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
805 default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
807 if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
808 wbuffer, G_N_ELEMENTS (wbuffer)))
809 default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
811 default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
815 if (!default_monthname[0])
820 strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
821 default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
823 if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
824 wbuffer, G_N_ELEMENTS (wbuffer)))
825 default_monthname[i] = g_strdup_printf ("(%d)", i);
827 default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
833 tm = localtime (&secs);
834 priv->month = tm->tm_mon;
835 priv->year = 1900 + tm->tm_year;
838 priv->marked_date[i] = FALSE;
839 priv->num_marked_dates = 0;
840 priv->selected_day = tm->tm_mday;
842 priv->display_flags = (GTK_CALENDAR_SHOW_HEADING |
843 GTK_CALENDAR_SHOW_DAY_NAMES |
844 GTK_CALENDAR_SHOW_DETAILS);
846 priv->highlight_row = -1;
847 priv->highlight_col = -1;
849 priv->focus_row = -1;
850 priv->focus_col = -1;
852 priv->max_year_width = 0;
853 priv->max_month_width = 0;
854 priv->max_day_char_width = 0;
855 priv->max_week_char_width = 0;
857 priv->max_day_char_ascent = 0;
858 priv->max_day_char_descent = 0;
859 priv->max_label_char_ascent = 0;
860 priv->max_label_char_descent = 0;
862 priv->arrow_width = 10;
864 priv->need_timer = 0;
866 priv->click_child = -1;
869 priv->drag_highlight = 0;
871 gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
872 gtk_drag_dest_add_text_targets (widget);
874 priv->year_before = 0;
876 /* Translate to calendar:YM if you want years to be displayed
877 * before months; otherwise translate to calendar:MY.
878 * Do *not* translate it to anything else, if it
879 * it isn't calendar:YM or calendar:MY it will not work.
881 * Note that the ordering described here is logical order, which is
882 * further influenced by BIDI ordering. Thus, if you have a default
883 * text direction of RTL and specify "calendar:YM", then the year
884 * will appear to the right of the month.
886 year_before = _("calendar:MY");
887 if (strcmp (year_before, "calendar:YM") == 0)
888 priv->year_before = 1;
889 else if (strcmp (year_before, "calendar:MY") != 0)
890 g_warning ("Whoever translated calendar:MY did so wrongly.\n");
893 priv->week_start = 0;
896 if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
897 wbuffer, G_N_ELEMENTS (wbuffer)))
898 week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
900 if (week_start != NULL)
902 priv->week_start = (week_start[0] - '0' + 1) % 7;
906 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
907 langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
908 first_weekday = langinfo.string[0];
909 langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
910 week_origin = langinfo.word;
911 if (week_origin == 19971130) /* Sunday */
913 else if (week_origin == 19971201) /* Monday */
916 g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
918 priv->week_start = (week_1stday + first_weekday - 1) % 7;
920 /* Translate to calendar:week_start:0 if you want Sunday to be the
921 * first day of the week to calendar:week_start:1 if you want Monday
922 * to be the first day of the week, and so on.
924 week_start = _("calendar:week_start:0");
926 if (strncmp (week_start, "calendar:week_start:", 20) == 0)
927 priv->week_start = *(week_start + 20) - '0';
929 priv->week_start = -1;
931 if (priv->week_start < 0 || priv->week_start > 6)
933 g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
934 priv->week_start = 0;
939 calendar_compute_days (calendar);
943 /****************************************
944 * Utility Functions *
945 ****************************************/
948 calendar_queue_refresh (GtkCalendar *calendar)
950 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
952 if (!(priv->detail_func) ||
953 !(priv->display_flags & GTK_CALENDAR_SHOW_DETAILS) ||
954 (priv->detail_width_chars && priv->detail_height_rows))
955 gtk_widget_queue_draw (GTK_WIDGET (calendar));
957 gtk_widget_queue_resize (GTK_WIDGET (calendar));
961 calendar_set_month_next (GtkCalendar *calendar)
964 GtkCalendarPrivate *priv = calendar->priv;
966 if (priv->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
969 if (priv->month == 11)
977 calendar_compute_days (calendar);
978 g_signal_emit (calendar,
979 gtk_calendar_signals[NEXT_MONTH_SIGNAL],
981 g_signal_emit (calendar,
982 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
985 month_len = month_length[leap (priv->year)][priv->month + 1];
987 if (month_len < priv->selected_day)
989 priv->selected_day = 0;
990 gtk_calendar_select_day (calendar, month_len);
993 gtk_calendar_select_day (calendar, priv->selected_day);
995 calendar_queue_refresh (calendar);
999 calendar_set_year_prev (GtkCalendar *calendar)
1001 GtkCalendarPrivate *priv = calendar->priv;
1005 calendar_compute_days (calendar);
1006 g_signal_emit (calendar,
1007 gtk_calendar_signals[PREV_YEAR_SIGNAL],
1009 g_signal_emit (calendar,
1010 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1013 month_len = month_length[leap (priv->year)][priv->month + 1];
1015 if (month_len < priv->selected_day)
1017 priv->selected_day = 0;
1018 gtk_calendar_select_day (calendar, month_len);
1021 gtk_calendar_select_day (calendar, priv->selected_day);
1023 calendar_queue_refresh (calendar);
1027 calendar_set_year_next (GtkCalendar *calendar)
1029 GtkCalendarPrivate *priv = calendar->priv;
1033 calendar_compute_days (calendar);
1034 g_signal_emit (calendar,
1035 gtk_calendar_signals[NEXT_YEAR_SIGNAL],
1037 g_signal_emit (calendar,
1038 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1041 month_len = month_length[leap (priv->year)][priv->month + 1];
1043 if (month_len < priv->selected_day)
1045 priv->selected_day = 0;
1046 gtk_calendar_select_day (calendar, month_len);
1049 gtk_calendar_select_day (calendar, priv->selected_day);
1051 calendar_queue_refresh (calendar);
1055 calendar_compute_days (GtkCalendar *calendar)
1057 GtkCalendarPrivate *priv = calendar->priv;
1060 gint ndays_in_month;
1061 gint ndays_in_prev_month;
1068 month = priv->month + 1;
1070 ndays_in_month = month_length[leap (year)][month];
1072 first_day = day_of_week (year, month, 1);
1073 first_day = (first_day + 7 - priv->week_start) % 7;
1075 /* Compute days of previous month */
1077 ndays_in_prev_month = month_length[leap (year)][month-1];
1079 ndays_in_prev_month = month_length[leap (year)][12];
1080 day = ndays_in_prev_month - first_day + 1;
1085 for (col = 0; col < first_day; col++)
1087 priv->day[row][col] = day;
1088 priv->day_month[row][col] = MONTH_PREV;
1093 /* Compute days of current month */
1095 for (day = 1; day <= ndays_in_month; day++)
1097 priv->day[row][col] = day;
1098 priv->day_month[row][col] = MONTH_CURRENT;
1108 /* Compute days of next month */
1110 for (; row <= 5; row++)
1112 for (; col <= 6; col++)
1114 priv->day[row][col] = day;
1115 priv->day_month[row][col] = MONTH_NEXT;
1123 calendar_select_and_focus_day (GtkCalendar *calendar,
1126 GtkCalendarPrivate *priv = calendar->priv;
1127 gint old_focus_row = priv->focus_row;
1128 gint old_focus_col = priv->focus_col;
1132 for (row = 0; row < 6; row ++)
1133 for (col = 0; col < 7; col++)
1135 if (priv->day_month[row][col] == MONTH_CURRENT
1136 && priv->day[row][col] == day)
1138 priv->focus_row = row;
1139 priv->focus_col = col;
1143 if (old_focus_row != -1 && old_focus_col != -1)
1144 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
1146 gtk_calendar_select_day (calendar, day);
1150 /****************************************
1151 * Layout computation utilities *
1152 ****************************************/
1155 calendar_row_height (GtkCalendar *calendar)
1157 GtkCalendarPrivate *priv = calendar->priv;
1159 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
1160 - ((priv->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1161 ? calendar_get_ysep (calendar) : CALENDAR_MARGIN)) / 6;
1165 /* calendar_left_x_for_column: returns the x coordinate
1166 * for the left of the column */
1168 calendar_left_x_for_column (GtkCalendar *calendar,
1171 GtkCalendarPrivate *priv = calendar->priv;
1174 gint calendar_xsep = calendar_get_xsep (calendar);
1176 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1177 column = 6 - column;
1179 width = GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1180 if (priv->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1181 x_left = calendar_xsep + (width + DAY_XSEP) * column;
1183 x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
1188 /* column_from_x: returns the column 0-6 that the
1189 * x pixel of the xwindow is in */
1191 calendar_column_from_x (GtkCalendar *calendar,
1195 gint x_left, x_right;
1199 for (c = 0; c < 7; c++)
1201 x_left = calendar_left_x_for_column (calendar, c);
1202 x_right = x_left + GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1204 if (event_x >= x_left && event_x < x_right)
1214 /* calendar_top_y_for_row: returns the y coordinate
1215 * for the top of the row */
1217 calendar_top_y_for_row (GtkCalendar *calendar,
1221 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h
1222 - (CALENDAR_MARGIN + (6 - row)
1223 * calendar_row_height (calendar)));
1226 /* row_from_y: returns the row 0-5 that the
1227 * y pixel of the xwindow is in */
1229 calendar_row_from_y (GtkCalendar *calendar,
1234 gint y_top, y_bottom;
1236 height = calendar_row_height (calendar);
1239 for (r = 0; r < 6; r++)
1241 y_top = calendar_top_y_for_row (calendar, r);
1242 y_bottom = y_top + height;
1244 if (event_y >= y_top && event_y < y_bottom)
1255 calendar_arrow_rectangle (GtkCalendar *calendar,
1259 GtkWidget *widget = GTK_WIDGET (calendar);
1260 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1263 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1264 year_left = priv->year_before;
1266 year_left = !priv->year_before;
1269 rect->width = priv->arrow_width;
1270 rect->height = priv->header_h - 7;
1274 case ARROW_MONTH_LEFT:
1276 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1277 - (3 + 2*priv->arrow_width + priv->max_month_width));
1281 case ARROW_MONTH_RIGHT:
1283 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1284 - 3 - priv->arrow_width);
1286 rect->x = (priv->arrow_width + priv->max_month_width);
1288 case ARROW_YEAR_LEFT:
1292 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1293 - (3 + 2*priv->arrow_width + priv->max_year_width));
1295 case ARROW_YEAR_RIGHT:
1297 rect->x = (priv->arrow_width + priv->max_year_width);
1299 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1300 - 3 - priv->arrow_width);
1306 calendar_day_rectangle (GtkCalendar *calendar,
1311 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1313 rect->x = calendar_left_x_for_column (calendar, col);
1314 rect->y = calendar_top_y_for_row (calendar, row);
1315 rect->height = calendar_row_height (calendar);
1316 rect->width = priv->day_width;
1320 calendar_set_month_prev (GtkCalendar *calendar)
1322 GtkCalendarPrivate *priv = calendar->priv;
1325 if (priv->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1328 if (priv->month == 0)
1336 month_len = month_length[leap (priv->year)][priv->month + 1];
1338 calendar_compute_days (calendar);
1340 g_signal_emit (calendar,
1341 gtk_calendar_signals[PREV_MONTH_SIGNAL],
1343 g_signal_emit (calendar,
1344 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1347 if (month_len < priv->selected_day)
1349 priv->selected_day = 0;
1350 gtk_calendar_select_day (calendar, month_len);
1354 if (priv->selected_day < 0)
1355 priv->selected_day = priv->selected_day + 1 + month_length[leap (priv->year)][priv->month + 1];
1356 gtk_calendar_select_day (calendar, priv->selected_day);
1359 calendar_queue_refresh (calendar);
1363 /****************************************
1364 * Basic object methods *
1365 ****************************************/
1368 gtk_calendar_finalize (GObject *object)
1370 G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize (object);
1374 gtk_calendar_destroy (GtkObject *object)
1376 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1378 calendar_stop_spinning (GTK_CALENDAR (object));
1380 /* Call the destroy function for the extra display callback: */
1381 if (priv->detail_func_destroy && priv->detail_func_user_data)
1383 priv->detail_func_destroy (priv->detail_func_user_data);
1384 priv->detail_func_user_data = NULL;
1385 priv->detail_func_destroy = NULL;
1388 GTK_OBJECT_CLASS (gtk_calendar_parent_class)->destroy (object);
1393 calendar_set_display_option (GtkCalendar *calendar,
1394 GtkCalendarDisplayOptions flag,
1397 GtkCalendarPrivate *priv = calendar->priv;
1398 GtkCalendarDisplayOptions flags;
1401 flags = priv->display_flags | flag;
1403 flags = priv->display_flags & ~flag;
1404 gtk_calendar_set_display_options (calendar, flags);
1408 calendar_get_display_option (GtkCalendar *calendar,
1409 GtkCalendarDisplayOptions flag)
1411 GtkCalendarPrivate *priv = calendar->priv;
1413 return (priv->display_flags & flag) != 0;
1417 gtk_calendar_set_property (GObject *object,
1419 const GValue *value,
1422 GtkCalendar *calendar = GTK_CALENDAR (object);
1423 GtkCalendarPrivate *priv = calendar->priv;
1428 gtk_calendar_select_month (calendar,
1430 g_value_get_int (value));
1433 gtk_calendar_select_month (calendar,
1434 g_value_get_int (value),
1438 gtk_calendar_select_day (calendar,
1439 g_value_get_int (value));
1441 case PROP_SHOW_HEADING:
1442 calendar_set_display_option (calendar,
1443 GTK_CALENDAR_SHOW_HEADING,
1444 g_value_get_boolean (value));
1446 case PROP_SHOW_DAY_NAMES:
1447 calendar_set_display_option (calendar,
1448 GTK_CALENDAR_SHOW_DAY_NAMES,
1449 g_value_get_boolean (value));
1451 case PROP_NO_MONTH_CHANGE:
1452 calendar_set_display_option (calendar,
1453 GTK_CALENDAR_NO_MONTH_CHANGE,
1454 g_value_get_boolean (value));
1456 case PROP_SHOW_WEEK_NUMBERS:
1457 calendar_set_display_option (calendar,
1458 GTK_CALENDAR_SHOW_WEEK_NUMBERS,
1459 g_value_get_boolean (value));
1461 case PROP_SHOW_DETAILS:
1462 calendar_set_display_option (calendar,
1463 GTK_CALENDAR_SHOW_DETAILS,
1464 g_value_get_boolean (value));
1466 case PROP_DETAIL_WIDTH_CHARS:
1467 gtk_calendar_set_detail_width_chars (calendar,
1468 g_value_get_int (value));
1470 case PROP_DETAIL_HEIGHT_ROWS:
1471 gtk_calendar_set_detail_height_rows (calendar,
1472 g_value_get_int (value));
1475 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1481 gtk_calendar_get_property (GObject *object,
1486 GtkCalendar *calendar = GTK_CALENDAR (object);
1487 GtkCalendarPrivate *priv = calendar->priv;
1492 g_value_set_int (value, priv->year);
1495 g_value_set_int (value, priv->month);
1498 g_value_set_int (value, priv->selected_day);
1500 case PROP_SHOW_HEADING:
1501 g_value_set_boolean (value, calendar_get_display_option (calendar,
1502 GTK_CALENDAR_SHOW_HEADING));
1504 case PROP_SHOW_DAY_NAMES:
1505 g_value_set_boolean (value, calendar_get_display_option (calendar,
1506 GTK_CALENDAR_SHOW_DAY_NAMES));
1508 case PROP_NO_MONTH_CHANGE:
1509 g_value_set_boolean (value, calendar_get_display_option (calendar,
1510 GTK_CALENDAR_NO_MONTH_CHANGE));
1512 case PROP_SHOW_WEEK_NUMBERS:
1513 g_value_set_boolean (value, calendar_get_display_option (calendar,
1514 GTK_CALENDAR_SHOW_WEEK_NUMBERS));
1516 case PROP_SHOW_DETAILS:
1517 g_value_set_boolean (value, calendar_get_display_option (calendar,
1518 GTK_CALENDAR_SHOW_DETAILS));
1520 case PROP_DETAIL_WIDTH_CHARS:
1521 g_value_set_int (value, priv->detail_width_chars);
1523 case PROP_DETAIL_HEIGHT_ROWS:
1524 g_value_set_int (value, priv->detail_height_rows);
1527 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1533 /****************************************
1535 ****************************************/
1538 calendar_realize_arrows (GtkCalendar *calendar)
1540 GtkWidget *widget = GTK_WIDGET (calendar);
1541 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1542 GdkWindowAttr attributes;
1543 gint attributes_mask;
1546 /* Arrow windows ------------------------------------- */
1547 if (! (priv->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1548 && (priv->display_flags & GTK_CALENDAR_SHOW_HEADING))
1550 attributes.wclass = GDK_INPUT_OUTPUT;
1551 attributes.window_type = GDK_WINDOW_CHILD;
1552 attributes.visual = gtk_widget_get_visual (widget);
1553 attributes.colormap = gtk_widget_get_colormap (widget);
1554 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1555 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1556 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1557 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1558 for (i = 0; i < 4; i++)
1561 calendar_arrow_rectangle (calendar, i, &rect);
1563 attributes.x = rect.x;
1564 attributes.y = rect.y;
1565 attributes.width = rect.width;
1566 attributes.height = rect.height;
1567 priv->arrow_win[i] = gdk_window_new (priv->header_win,
1570 if (gtk_widget_is_sensitive (widget))
1571 priv->arrow_state[i] = GTK_STATE_NORMAL;
1573 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
1574 gdk_window_set_background (priv->arrow_win[i],
1575 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1576 gdk_window_show (priv->arrow_win[i]);
1577 gdk_window_set_user_data (priv->arrow_win[i], widget);
1582 for (i = 0; i < 4; i++)
1583 priv->arrow_win[i] = NULL;
1588 calendar_realize_header (GtkCalendar *calendar)
1590 GtkWidget *widget = GTK_WIDGET (calendar);
1591 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1592 GdkWindowAttr attributes;
1593 gint attributes_mask;
1595 /* Header window ------------------------------------- */
1596 if (priv->display_flags & GTK_CALENDAR_SHOW_HEADING)
1598 attributes.wclass = GDK_INPUT_OUTPUT;
1599 attributes.window_type = GDK_WINDOW_CHILD;
1600 attributes.visual = gtk_widget_get_visual (widget);
1601 attributes.colormap = gtk_widget_get_colormap (widget);
1602 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1603 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1604 attributes.x = widget->style->xthickness;
1605 attributes.y = widget->style->ythickness;
1606 attributes.width = widget->allocation.width - 2 * attributes.x;
1607 attributes.height = priv->header_h;
1608 priv->header_win = gdk_window_new (widget->window,
1609 &attributes, attributes_mask);
1611 gdk_window_set_background (priv->header_win,
1612 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1613 gdk_window_show (priv->header_win);
1614 gdk_window_set_user_data (priv->header_win, widget);
1619 priv->header_win = NULL;
1621 calendar_realize_arrows (calendar);
1625 calendar_get_inner_border (GtkCalendar *calendar)
1629 gtk_widget_style_get (GTK_WIDGET (calendar),
1630 "inner-border", &inner_border,
1633 return inner_border;
1637 calendar_get_xsep (GtkCalendar *calendar)
1641 gtk_widget_style_get (GTK_WIDGET (calendar),
1642 "horizontal-separation", &xsep,
1649 calendar_get_ysep (GtkCalendar *calendar)
1653 gtk_widget_style_get (GTK_WIDGET (calendar),
1654 "vertical-separation", &ysep,
1661 calendar_realize_day_names (GtkCalendar *calendar)
1663 GtkWidget *widget = GTK_WIDGET (calendar);
1664 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1665 GdkWindowAttr attributes;
1666 gint attributes_mask;
1667 gint inner_border = calendar_get_inner_border (calendar);
1669 /* Day names window --------------------------------- */
1670 if ( priv->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1672 attributes.wclass = GDK_INPUT_OUTPUT;
1673 attributes.window_type = GDK_WINDOW_CHILD;
1674 attributes.visual = gtk_widget_get_visual (widget);
1675 attributes.colormap = gtk_widget_get_colormap (widget);
1676 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1677 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1678 attributes.x = (widget->style->xthickness + inner_border);
1679 attributes.y = priv->header_h + (widget->style->ythickness
1681 attributes.width = (widget->allocation.width
1682 - (widget->style->xthickness + inner_border)
1684 attributes.height = priv->day_name_h;
1685 priv->day_name_win = gdk_window_new (widget->window,
1688 gdk_window_set_background (priv->day_name_win,
1689 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1690 gdk_window_show (priv->day_name_win);
1691 gdk_window_set_user_data (priv->day_name_win, widget);
1695 priv->day_name_win = NULL;
1700 calendar_realize_week_numbers (GtkCalendar *calendar)
1702 GtkWidget *widget = GTK_WIDGET (calendar);
1703 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1704 GdkWindowAttr attributes;
1705 gint attributes_mask;
1706 gint inner_border = calendar_get_inner_border (calendar);
1708 /* Week number window -------------------------------- */
1709 if (priv->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1711 attributes.wclass = GDK_INPUT_OUTPUT;
1712 attributes.window_type = GDK_WINDOW_CHILD;
1713 attributes.visual = gtk_widget_get_visual (widget);
1714 attributes.colormap = gtk_widget_get_colormap (widget);
1715 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1717 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1718 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1719 attributes.x = widget->style->xthickness + inner_border;
1721 attributes.x = widget->allocation.width - priv->week_width - (widget->style->xthickness + inner_border);
1722 attributes.y = (priv->header_h + priv->day_name_h
1723 + (widget->style->ythickness + inner_border));
1724 attributes.width = priv->week_width;
1725 attributes.height = priv->main_h;
1726 priv->week_win = gdk_window_new (widget->window,
1727 &attributes, attributes_mask);
1728 gdk_window_set_background (priv->week_win,
1729 BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1730 gdk_window_show (priv->week_win);
1731 gdk_window_set_user_data (priv->week_win, widget);
1735 priv->week_win = NULL;
1740 gtk_calendar_realize (GtkWidget *widget)
1742 GtkCalendar *calendar = GTK_CALENDAR (widget);
1743 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1744 GdkWindowAttr attributes;
1745 gint attributes_mask;
1746 gint inner_border = calendar_get_inner_border (calendar);
1748 gtk_widget_set_realized (widget, TRUE);
1750 attributes.x = widget->allocation.x;
1751 attributes.y = widget->allocation.y;
1752 attributes.width = widget->allocation.width;
1753 attributes.height = widget->allocation.height;
1754 attributes.wclass = GDK_INPUT_OUTPUT;
1755 attributes.window_type = GDK_WINDOW_CHILD;
1756 attributes.event_mask = (gtk_widget_get_events (widget)
1757 | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1758 attributes.visual = gtk_widget_get_visual (widget);
1759 attributes.colormap = gtk_widget_get_colormap (widget);
1761 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1762 widget->window = gdk_window_new (widget->parent->window,
1763 &attributes, attributes_mask);
1765 widget->style = gtk_style_attach (widget->style, widget->window);
1767 /* Header window ------------------------------------- */
1768 calendar_realize_header (calendar);
1769 /* Day names window --------------------------------- */
1770 calendar_realize_day_names (calendar);
1771 /* Week number window -------------------------------- */
1772 calendar_realize_week_numbers (calendar);
1773 /* Main Window -------------------------------------- */
1774 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1775 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1776 | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1778 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1779 attributes.x = priv->week_width + (widget->style->ythickness + inner_border);
1781 attributes.x = widget->style->ythickness + inner_border;
1783 attributes.y = (priv->header_h + priv->day_name_h
1784 + (widget->style->ythickness + inner_border));
1785 attributes.width = (widget->allocation.width - attributes.x
1786 - (widget->style->xthickness + inner_border));
1787 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1788 attributes.width -= priv->week_width;
1790 attributes.height = priv->main_h;
1791 priv->main_win = gdk_window_new (widget->window,
1792 &attributes, attributes_mask);
1793 gdk_window_set_background (priv->main_win,
1794 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1795 gdk_window_show (priv->main_win);
1796 gdk_window_set_user_data (priv->main_win, widget);
1797 gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1798 gdk_window_show (widget->window);
1799 gdk_window_set_user_data (widget->window, widget);
1803 gtk_calendar_unrealize (GtkWidget *widget)
1805 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1808 if (priv->header_win)
1810 for (i = 0; i < 4; i++)
1812 if (priv->arrow_win[i])
1814 gdk_window_set_user_data (priv->arrow_win[i], NULL);
1815 gdk_window_destroy (priv->arrow_win[i]);
1816 priv->arrow_win[i] = NULL;
1819 gdk_window_set_user_data (priv->header_win, NULL);
1820 gdk_window_destroy (priv->header_win);
1821 priv->header_win = NULL;
1826 gdk_window_set_user_data (priv->week_win, NULL);
1827 gdk_window_destroy (priv->week_win);
1828 priv->week_win = NULL;
1833 gdk_window_set_user_data (priv->main_win, NULL);
1834 gdk_window_destroy (priv->main_win);
1835 priv->main_win = NULL;
1837 if (priv->day_name_win)
1839 gdk_window_set_user_data (priv->day_name_win, NULL);
1840 gdk_window_destroy (priv->day_name_win);
1841 priv->day_name_win = NULL;
1844 GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize (widget);
1848 gtk_calendar_get_detail (GtkCalendar *calendar,
1852 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1855 if (priv->detail_func == NULL)
1859 month = priv->month + priv->day_month[row][column] - MONTH_CURRENT;
1866 else if (month > 11)
1872 return priv->detail_func (calendar,
1874 priv->day[row][column],
1875 priv->detail_func_user_data);
1879 gtk_calendar_query_tooltip (GtkWidget *widget,
1882 gboolean keyboard_mode,
1883 GtkTooltip *tooltip)
1885 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1886 GtkCalendar *calendar = GTK_CALENDAR (widget);
1887 gchar *detail = NULL;
1888 GdkRectangle day_rect;
1892 gint x0, y0, row, col;
1894 gdk_window_get_position (priv->main_win, &x0, &y0);
1895 col = calendar_column_from_x (calendar, x - x0);
1896 row = calendar_row_from_y (calendar, y - y0);
1898 if (col != -1 && row != -1 &&
1899 (0 != (priv->detail_overflow[row] & (1 << col)) ||
1900 0 == (priv->display_flags & GTK_CALENDAR_SHOW_DETAILS)))
1902 detail = gtk_calendar_get_detail (calendar, row, col);
1903 calendar_day_rectangle (calendar, row, col, &day_rect);
1912 gtk_tooltip_set_tip_area (tooltip, &day_rect);
1913 gtk_tooltip_set_markup (tooltip, detail);
1920 if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip)
1921 return GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
1927 /****************************************
1928 * Size Request and Allocate *
1929 ****************************************/
1932 gtk_calendar_size_request (GtkWidget *widget,
1933 GtkRequisition *requisition)
1935 GtkCalendar *calendar = GTK_CALENDAR (widget);
1936 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1937 PangoLayout *layout;
1938 PangoRectangle logical_rect;
1942 gint calendar_margin = CALENDAR_MARGIN;
1943 gint header_width, main_width;
1944 gint max_header_height = 0;
1947 gint max_detail_height;
1948 gint inner_border = calendar_get_inner_border (calendar);
1949 gint calendar_ysep = calendar_get_ysep (calendar);
1950 gint calendar_xsep = calendar_get_xsep (calendar);
1952 gtk_widget_style_get (GTK_WIDGET (widget),
1953 "focus-line-width", &focus_width,
1954 "focus-padding", &focus_padding,
1957 layout = gtk_widget_create_pango_layout (widget, NULL);
1960 * Calculate the requisition width for the widget.
1965 if (priv->display_flags & GTK_CALENDAR_SHOW_HEADING)
1967 priv->max_month_width = 0;
1968 for (i = 0; i < 12; i++)
1970 pango_layout_set_text (layout, default_monthname[i], -1);
1971 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1972 priv->max_month_width = MAX (priv->max_month_width,
1973 logical_rect.width + 8);
1974 max_header_height = MAX (max_header_height, logical_rect.height);
1977 priv->max_year_width = 0;
1978 /* Translators: This is a text measurement template.
1979 * Translate it to the widest year text
1981 * If you don't understand this, leave it as "2000"
1983 pango_layout_set_text (layout, C_("year measurement template", "2000"), -1);
1984 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1985 priv->max_year_width = MAX (priv->max_year_width,
1986 logical_rect.width + 8);
1987 max_header_height = MAX (max_header_height, logical_rect.height);
1991 priv->max_month_width = 0;
1992 priv->max_year_width = 0;
1995 if (priv->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1996 header_width = (priv->max_month_width
1997 + priv->max_year_width
2000 header_width = (priv->max_month_width
2001 + priv->max_year_width
2002 + 4 * priv->arrow_width + 3 * 3);
2004 /* Mainwindow labels width */
2006 priv->max_day_char_width = 0;
2007 priv->max_day_char_ascent = 0;
2008 priv->max_day_char_descent = 0;
2009 priv->min_day_width = 0;
2011 for (i = 0; i < 9; i++)
2014 g_snprintf (buffer, sizeof (buffer), C_("calendar:day:digits", "%d"), i * 11);
2015 pango_layout_set_text (layout, buffer, -1);
2016 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2017 priv->min_day_width = MAX (priv->min_day_width,
2018 logical_rect.width);
2020 priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
2021 PANGO_ASCENT (logical_rect));
2022 priv->max_day_char_descent = MAX (priv->max_day_char_descent,
2023 PANGO_DESCENT (logical_rect));
2026 priv->max_label_char_ascent = 0;
2027 priv->max_label_char_descent = 0;
2028 if (priv->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
2029 for (i = 0; i < 7; i++)
2031 pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
2032 pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
2034 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
2035 priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
2036 PANGO_ASCENT (logical_rect));
2037 priv->max_label_char_descent = MAX (priv->max_label_char_descent,
2038 PANGO_DESCENT (logical_rect));
2041 priv->max_week_char_width = 0;
2042 if (priv->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2043 for (i = 0; i < 9; i++)
2046 g_snprintf (buffer, sizeof (buffer), C_("calendar:week:digits", "%d"), i * 11);
2047 pango_layout_set_text (layout, buffer, -1);
2048 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2049 priv->max_week_char_width = MAX (priv->max_week_char_width,
2050 logical_rect.width / 2);
2053 /* Calculate detail extents. Do this as late as possible since
2054 * pango_layout_set_markup is called which alters font settings. */
2055 max_detail_height = 0;
2057 if (priv->detail_func && (priv->display_flags & GTK_CALENDAR_SHOW_DETAILS))
2059 gchar *markup, *tail;
2061 if (priv->detail_width_chars || priv->detail_height_rows)
2063 gint rows = MAX (1, priv->detail_height_rows) - 1;
2064 gsize len = priv->detail_width_chars + rows + 16;
2066 markup = tail = g_alloca (len);
2068 memcpy (tail, "<small>", 7);
2071 memset (tail, 'm', priv->detail_width_chars);
2072 tail += priv->detail_width_chars;
2074 memset (tail, '\n', rows);
2077 memcpy (tail, "</small>", 9);
2080 g_assert (len == (tail - markup));
2082 pango_layout_set_markup (layout, markup, -1);
2083 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2085 if (priv->detail_width_chars)
2086 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
2087 if (priv->detail_height_rows)
2088 max_detail_height = MAX (max_detail_height, logical_rect.height);
2091 if (!priv->detail_width_chars || !priv->detail_height_rows)
2092 for (r = 0; r < 6; r++)
2093 for (c = 0; c < 7; c++)
2095 gchar *detail = gtk_calendar_get_detail (calendar, r, c);
2099 markup = g_strconcat ("<small>", detail, "</small>", NULL);
2100 pango_layout_set_markup (layout, markup, -1);
2102 if (priv->detail_width_chars)
2104 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2105 pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
2108 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2110 if (!priv->detail_width_chars)
2111 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
2112 if (!priv->detail_height_rows)
2113 max_detail_height = MAX (max_detail_height, logical_rect.height);
2121 /* We add one to max_day_char_width to be able to make the marked day "bold" */
2122 priv->max_day_char_width = priv->min_day_width / 2 + 1;
2124 main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
2125 + (priv->max_week_char_width
2126 ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + calendar_xsep * 2
2130 requisition->width = MAX (header_width, main_width + inner_border * 2) + widget->style->xthickness * 2;
2133 * Calculate the requisition height for the widget.
2136 if (priv->display_flags & GTK_CALENDAR_SHOW_HEADING)
2138 priv->header_h = (max_header_height + calendar_ysep * 2);
2145 if (priv->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
2147 priv->day_name_h = (priv->max_label_char_ascent
2148 + priv->max_label_char_descent
2149 + 2 * (focus_padding + focus_width) + calendar_margin);
2150 calendar_margin = calendar_ysep;
2154 priv->day_name_h = 0;
2157 priv->main_h = (CALENDAR_MARGIN + calendar_margin
2158 + 6 * (priv->max_day_char_ascent
2159 + priv->max_day_char_descent
2161 + 2 * (focus_padding + focus_width))
2164 height = (priv->header_h + priv->day_name_h
2167 requisition->height = height + (widget->style->ythickness + inner_border) * 2;
2169 g_object_unref (layout);
2173 gtk_calendar_size_allocate (GtkWidget *widget,
2174 GtkAllocation *allocation)
2176 GtkCalendar *calendar = GTK_CALENDAR (widget);
2177 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2178 gint xthickness = widget->style->xthickness;
2179 gint ythickness = widget->style->xthickness;
2181 gint inner_border = calendar_get_inner_border (calendar);
2182 gint calendar_xsep = calendar_get_xsep (calendar);
2184 widget->allocation = *allocation;
2186 if (priv->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2188 priv->day_width = (priv->min_day_width
2189 * ((allocation->width - (xthickness + inner_border) * 2
2190 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - calendar_xsep * 2))
2191 / (7 * priv->min_day_width + priv->max_week_char_width * 2));
2192 priv->week_width = ((allocation->width - (xthickness + inner_border) * 2
2193 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - calendar_xsep * 2 )
2194 - priv->day_width * 7 + CALENDAR_MARGIN + calendar_xsep);
2198 priv->day_width = (allocation->width
2199 - (xthickness + inner_border) * 2
2200 - (CALENDAR_MARGIN * 2)
2201 - (DAY_XSEP * 6))/7;
2202 priv->week_width = 0;
2205 if (gtk_widget_get_realized (widget))
2207 gdk_window_move_resize (widget->window,
2208 allocation->x, allocation->y,
2209 allocation->width, allocation->height);
2210 if (priv->header_win)
2211 gdk_window_move_resize (priv->header_win,
2212 xthickness, ythickness,
2213 allocation->width - 2 * xthickness, priv->header_h);
2215 for (i = 0 ; i < 4 ; i++)
2217 if (priv->arrow_win[i])
2220 calendar_arrow_rectangle (calendar, i, &rect);
2222 gdk_window_move_resize (priv->arrow_win[i],
2223 rect.x, rect.y, rect.width, rect.height);
2227 if (priv->day_name_win)
2228 gdk_window_move_resize (priv->day_name_win,
2229 xthickness + inner_border,
2230 priv->header_h + (widget->style->ythickness + inner_border),
2231 allocation->width - (xthickness + inner_border) * 2,
2233 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2236 gdk_window_move_resize (priv->week_win,
2237 (xthickness + inner_border),
2238 priv->header_h + priv->day_name_h
2239 + (widget->style->ythickness + inner_border),
2242 gdk_window_move_resize (priv->main_win,
2243 priv->week_width + (xthickness + inner_border),
2244 priv->header_h + priv->day_name_h
2245 + (widget->style->ythickness + inner_border),
2248 - (xthickness + inner_border) * 2,
2253 gdk_window_move_resize (priv->main_win,
2254 (xthickness + inner_border),
2255 priv->header_h + priv->day_name_h
2256 + (widget->style->ythickness + inner_border),
2259 - (xthickness + inner_border) * 2,
2262 gdk_window_move_resize (priv->week_win,
2265 - (xthickness + inner_border),
2266 priv->header_h + priv->day_name_h
2267 + (widget->style->ythickness + inner_border),
2275 /****************************************
2277 ****************************************/
2280 calendar_paint_header (GtkCalendar *calendar)
2282 GtkWidget *widget = GTK_WIDGET (calendar);
2283 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2288 gint max_month_width;
2289 gint max_year_width;
2290 PangoLayout *layout;
2291 PangoRectangle logical_rect;
2297 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2298 year_left = priv->year_before;
2300 year_left = !priv->year_before;
2302 cr = gdk_cairo_create (priv->header_win);
2304 header_width = widget->allocation.width - 2 * widget->style->xthickness;
2306 max_month_width = priv->max_month_width;
2307 max_year_width = priv->max_year_width;
2309 gtk_paint_shadow (widget->style, priv->header_win,
2310 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2311 NULL, widget, "calendar",
2312 0, 0, header_width, priv->header_h);
2314 tmp_time = 1; /* Jan 1 1970, 00:00:01 UTC */
2315 tm = gmtime (&tmp_time);
2316 tm->tm_year = priv->year - 1900;
2318 /* Translators: This dictates how the year is displayed in
2319 * gtkcalendar widget. See strftime() manual for the format.
2320 * Use only ASCII in the translation.
2322 * Also look for the msgid "2000".
2323 * Translate that entry to a year with the widest output of this
2326 * "%Y" is appropriate for most locales.
2328 strftime (buffer, sizeof (buffer), C_("calendar year format", "%Y"), tm);
2329 str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
2330 layout = gtk_widget_create_pango_layout (widget, str);
2333 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2336 y = (priv->header_h - logical_rect.height) / 2;
2338 /* Draw year and its arrows */
2340 if (priv->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2342 x = 3 + (max_year_width - logical_rect.width)/2;
2344 x = header_width - (3 + max_year_width
2345 - (max_year_width - logical_rect.width)/2);
2348 x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
2350 x = header_width - (3 + priv->arrow_width + max_year_width
2351 - (max_year_width - logical_rect.width)/2);
2354 gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2355 cairo_move_to (cr, x, y);
2356 pango_cairo_show_layout (cr, layout);
2359 g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[priv->month]);
2360 pango_layout_set_text (layout, buffer, -1);
2361 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2363 if (priv->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2365 x = header_width - (3 + max_month_width
2366 - (max_month_width - logical_rect.width)/2);
2368 x = 3 + (max_month_width - logical_rect.width) / 2;
2371 x = header_width - (3 + priv->arrow_width + max_month_width
2372 - (max_month_width - logical_rect.width)/2);
2374 x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
2376 cairo_move_to (cr, x, y);
2377 pango_cairo_show_layout (cr, layout);
2379 g_object_unref (layout);
2384 calendar_paint_day_names (GtkCalendar *calendar)
2386 GtkWidget *widget = GTK_WIDGET (calendar);
2387 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2391 int day_width, cal_width;
2393 PangoLayout *layout;
2394 PangoRectangle logical_rect;
2397 gint calendar_ysep = calendar_get_ysep (calendar);
2398 gint calendar_xsep = calendar_get_xsep (calendar);
2400 cr = gdk_cairo_create (priv->day_name_win);
2402 gtk_widget_style_get (GTK_WIDGET (widget),
2403 "focus-line-width", &focus_width,
2404 "focus-padding", &focus_padding,
2407 day_width = priv->day_width;
2408 cal_width = widget->allocation.width;
2409 day_wid_sep = day_width + DAY_XSEP;
2412 * Draw rectangles as inverted background for the labels.
2415 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2416 cairo_rectangle (cr,
2417 CALENDAR_MARGIN, CALENDAR_MARGIN,
2418 cal_width-CALENDAR_MARGIN * 2,
2419 priv->day_name_h - CALENDAR_MARGIN);
2422 if (priv->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2424 cairo_rectangle (cr,
2426 priv->day_name_h - calendar_ysep,
2427 priv->week_width - calendar_ysep - CALENDAR_MARGIN,
2436 layout = gtk_widget_create_pango_layout (widget, NULL);
2438 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2439 for (i = 0; i < 7; i++)
2441 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2445 day = (day + priv->week_start) % 7;
2446 g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
2448 pango_layout_set_text (layout, buffer, -1);
2449 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2453 + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2454 (priv->week_width + (priv->week_width ? calendar_xsep : 0))
2457 + (day_width - logical_rect.width)/2),
2458 CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2459 pango_cairo_show_layout (cr, layout);
2462 g_object_unref (layout);
2467 calendar_paint_week_numbers (GtkCalendar *calendar)
2469 GtkWidget *widget = GTK_WIDGET (calendar);
2470 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2473 guint week = 0, year;
2474 gint row, x_loc, y_loc;
2477 PangoLayout *layout;
2478 PangoRectangle logical_rect;
2481 gint calendar_xsep = calendar_get_xsep (calendar);
2483 cr = gdk_cairo_create (priv->week_win);
2485 gtk_widget_style_get (GTK_WIDGET (widget),
2486 "focus-line-width", &focus_width,
2487 "focus-padding", &focus_padding,
2491 * Draw a rectangle as inverted background for the labels.
2494 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2495 if (priv->day_name_win)
2496 cairo_rectangle (cr,
2499 priv->week_width - CALENDAR_MARGIN,
2500 priv->main_h - CALENDAR_MARGIN);
2502 cairo_rectangle (cr,
2505 priv->week_width - CALENDAR_MARGIN,
2506 priv->main_h - 2 * CALENDAR_MARGIN);
2513 layout = gtk_widget_create_pango_layout (widget, NULL);
2515 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2516 day_height = calendar_row_height (calendar);
2517 for (row = 0; row < 6; row++)
2522 if (priv->day[row][6] < 15 && row > 3 && priv->month == 11)
2525 result = week_of_year (&week, &year,
2526 ((priv->day[row][6] < 15 && row > 3 ? 1 : 0)
2527 + priv->month) % 12 + 1, priv->day[row][6]);
2528 g_return_if_fail (result);
2530 /* Translators: this defines whether the week numbers should use
2531 * localized digits or the ones used in English (0123...).
2533 * Translate to "%Id" if you want to use localized digits, or
2534 * translate to "%d" otherwise.
2536 * Note that translating this doesn't guarantee that you get localized
2537 * digits. That needs support from your system and locale definition
2540 g_snprintf (buffer, sizeof (buffer), C_("calendar:week:digits", "%d"), week);
2541 pango_layout_set_text (layout, buffer, -1);
2542 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2544 y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2546 x_loc = (priv->week_width
2547 - logical_rect.width
2548 - calendar_xsep - focus_padding - focus_width);
2550 cairo_move_to (cr, x_loc, y_loc);
2551 pango_cairo_show_layout (cr, layout);
2554 g_object_unref (layout);
2559 calendar_invalidate_day_num (GtkCalendar *calendar,
2562 GtkCalendarPrivate *priv = calendar->priv;
2563 gint r, c, row, col;
2567 for (r = 0; r < 6; r++)
2568 for (c = 0; c < 7; c++)
2569 if (priv->day_month[r][c] == MONTH_CURRENT &&
2570 priv->day[r][c] == day)
2576 g_return_if_fail (row != -1);
2577 g_return_if_fail (col != -1);
2579 calendar_invalidate_day (calendar, row, col);
2583 calendar_invalidate_day (GtkCalendar *calendar,
2587 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2591 GdkRectangle day_rect;
2593 calendar_day_rectangle (calendar, row, col, &day_rect);
2594 gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2599 is_color_attribute (PangoAttribute *attribute,
2602 return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
2603 attribute->klass->type == PANGO_ATTR_BACKGROUND);
2607 calendar_paint_day (GtkCalendar *calendar,
2611 GtkWidget *widget = GTK_WIDGET (calendar);
2612 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2614 GdkColor *text_color;
2619 GdkRectangle day_rect;
2621 PangoLayout *layout;
2622 PangoRectangle logical_rect;
2623 gboolean overflow = FALSE;
2624 gboolean show_details;
2626 g_return_if_fail (row < 6);
2627 g_return_if_fail (col < 7);
2629 cr = gdk_cairo_create (priv->main_win);
2631 day = priv->day[row][col];
2632 show_details = (priv->display_flags & GTK_CALENDAR_SHOW_DETAILS);
2634 calendar_day_rectangle (calendar, row, col, &day_rect);
2636 if (priv->day_month[row][col] == MONTH_PREV)
2638 text_color = PREV_MONTH_COLOR (widget);
2640 else if (priv->day_month[row][col] == MONTH_NEXT)
2642 text_color = NEXT_MONTH_COLOR (widget);
2647 if (priv->highlight_row == row && priv->highlight_col == col)
2649 cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2650 gdk_cairo_rectangle (cr, &day_rect);
2654 if (priv->selected_day == day)
2656 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2657 gdk_cairo_rectangle (cr, &day_rect);
2660 if (priv->selected_day == day)
2661 text_color = SELECTED_FG_COLOR (widget);
2662 else if (priv->marked_date[day-1])
2663 text_color = MARKED_COLOR (widget);
2665 text_color = NORMAL_DAY_COLOR (widget);
2668 /* Translators: this defines whether the day numbers should use
2669 * localized digits or the ones used in English (0123...).
2671 * Translate to "%Id" if you want to use localized digits, or
2672 * translate to "%d" otherwise.
2674 * Note that translating this doesn't guarantee that you get localized
2675 * digits. That needs support from your system and locale definition
2678 g_snprintf (buffer, sizeof (buffer), C_("calendar:day:digits", "%d"), day);
2680 /* Get extra information to show, if any: */
2682 detail = gtk_calendar_get_detail (calendar, row, col);
2684 layout = gtk_widget_create_pango_layout (widget, buffer);
2685 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2686 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2688 x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2691 gdk_cairo_set_source_color (cr, text_color);
2692 cairo_move_to (cr, x_loc, y_loc);
2693 pango_cairo_show_layout (cr, layout);
2695 if (priv->day_month[row][col] == MONTH_CURRENT &&
2696 (priv->marked_date[day-1] || (detail && !show_details)))
2698 cairo_move_to (cr, x_loc - 1, y_loc);
2699 pango_cairo_show_layout (cr, layout);
2702 y_loc += priv->max_day_char_descent;
2704 if (priv->detail_func && show_details)
2708 if (priv->selected_day == day)
2709 gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
2710 else if (priv->day_month[row][col] == MONTH_CURRENT)
2711 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
2713 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
2715 cairo_set_line_width (cr, 1);
2716 cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
2717 cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
2725 if (detail && show_details)
2727 gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
2728 pango_layout_set_markup (layout, markup, -1);
2731 if (day == priv->selected_day)
2733 /* Stripping colors as they conflict with selection marking. */
2735 PangoAttrList *attrs = pango_layout_get_attributes (layout);
2736 PangoAttrList *colors = NULL;
2739 colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
2741 pango_attr_list_unref (colors);
2744 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2745 pango_layout_set_width (layout, PANGO_SCALE * day_rect.width);
2747 if (priv->detail_height_rows)
2749 gint dy = day_rect.height - (y_loc - day_rect.y);
2750 pango_layout_set_height (layout, PANGO_SCALE * dy);
2751 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
2754 cairo_move_to (cr, day_rect.x, y_loc);
2755 pango_cairo_show_layout (cr, layout);
2758 if (gtk_widget_has_focus (widget)
2759 && priv->focus_row == row && priv->focus_col == col)
2763 if (priv->selected_day == day)
2764 state = gtk_widget_has_focus (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2766 state = GTK_STATE_NORMAL;
2768 gtk_paint_focus (widget->style,
2771 NULL, widget, "calendar-day",
2772 day_rect.x, day_rect.y,
2773 day_rect.width, day_rect.height);
2777 priv->detail_overflow[row] |= (1 << col);
2779 priv->detail_overflow[row] &= ~(1 << col);
2781 g_object_unref (layout);
2787 calendar_paint_main (GtkCalendar *calendar)
2791 for (col = 0; col < 7; col++)
2792 for (row = 0; row < 6; row++)
2793 calendar_paint_day (calendar, row, col);
2797 calendar_invalidate_arrow (GtkCalendar *calendar,
2800 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2803 window = priv->arrow_win[arrow];
2805 gdk_window_invalidate_rect (window, NULL, FALSE);
2809 calendar_paint_arrow (GtkCalendar *calendar,
2812 GtkWidget *widget = GTK_WIDGET (calendar);
2813 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2816 window = priv->arrow_win[arrow];
2819 cairo_t *cr = gdk_cairo_create (window);
2823 state = priv->arrow_state[arrow];
2825 gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2829 gdk_drawable_get_size (window, &width, &height);
2830 if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2831 gtk_paint_arrow (widget->style, window, state,
2832 GTK_SHADOW_OUT, NULL, widget, "calendar",
2833 GTK_ARROW_LEFT, TRUE,
2834 width/2 - 3, height/2 - 4, 8, 8);
2836 gtk_paint_arrow (widget->style, window, state,
2837 GTK_SHADOW_OUT, NULL, widget, "calendar",
2838 GTK_ARROW_RIGHT, TRUE,
2839 width/2 - 4, height/2 - 4, 8, 8);
2844 gtk_calendar_expose (GtkWidget *widget,
2845 GdkEventExpose *event)
2847 GtkCalendar *calendar = GTK_CALENDAR (widget);
2848 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2851 if (gtk_widget_is_drawable (widget))
2853 if (event->window == priv->main_win)
2854 calendar_paint_main (calendar);
2856 if (event->window == priv->header_win)
2857 calendar_paint_header (calendar);
2859 for (i = 0; i < 4; i++)
2860 if (event->window == priv->arrow_win[i])
2861 calendar_paint_arrow (calendar, i);
2863 if (event->window == priv->day_name_win)
2864 calendar_paint_day_names (calendar);
2866 if (event->window == priv->week_win)
2867 calendar_paint_week_numbers (calendar);
2868 if (event->window == widget->window)
2870 gtk_paint_shadow (widget->style, widget->window, gtk_widget_get_state (widget),
2871 GTK_SHADOW_IN, NULL, widget, "calendar",
2872 0, 0, widget->allocation.width, widget->allocation.height);
2880 /****************************************
2882 ****************************************/
2885 calendar_arrow_action (GtkCalendar *calendar,
2890 case ARROW_YEAR_LEFT:
2891 calendar_set_year_prev (calendar);
2893 case ARROW_YEAR_RIGHT:
2894 calendar_set_year_next (calendar);
2896 case ARROW_MONTH_LEFT:
2897 calendar_set_month_prev (calendar);
2899 case ARROW_MONTH_RIGHT:
2900 calendar_set_month_next (calendar);
2908 calendar_timer (gpointer data)
2910 GtkCalendar *calendar = data;
2911 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2912 gboolean retval = FALSE;
2916 calendar_arrow_action (calendar, priv->click_child);
2918 if (priv->need_timer)
2920 GtkSettings *settings;
2923 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2924 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2926 priv->need_timer = FALSE;
2927 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2928 timeout * SCROLL_DELAY_FACTOR,
2929 (GSourceFunc) calendar_timer,
2930 (gpointer) calendar, NULL);
2940 calendar_start_spinning (GtkCalendar *calendar,
2943 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2945 priv->click_child = click_child;
2949 GtkSettings *settings;
2952 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2953 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2955 priv->need_timer = TRUE;
2956 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2958 (GSourceFunc) calendar_timer,
2959 (gpointer) calendar, NULL);
2964 calendar_stop_spinning (GtkCalendar *calendar)
2966 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2970 g_source_remove (priv->timer);
2972 priv->need_timer = FALSE;
2977 calendar_main_button_press (GtkCalendar *calendar,
2978 GdkEventButton *event)
2980 GtkWidget *widget = GTK_WIDGET (calendar);
2981 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2987 x = (gint) (event->x);
2988 y = (gint) (event->y);
2990 row = calendar_row_from_y (calendar, y);
2991 col = calendar_column_from_x (calendar, x);
2993 /* If row or column isn't found, just return. */
2994 if (row == -1 || col == -1)
2997 day_month = priv->day_month[row][col];
2999 if (event->type == GDK_BUTTON_PRESS)
3001 day = priv->day[row][col];
3003 if (day_month == MONTH_PREV)
3004 calendar_set_month_prev (calendar);
3005 else if (day_month == MONTH_NEXT)
3006 calendar_set_month_next (calendar);
3008 if (!gtk_widget_has_focus (widget))
3009 gtk_widget_grab_focus (widget);
3011 if (event->button == 1)
3014 priv->drag_start_x = x;
3015 priv->drag_start_y = y;
3018 calendar_select_and_focus_day (calendar, day);
3020 else if (event->type == GDK_2BUTTON_PRESS)
3023 if (day_month == MONTH_CURRENT)
3024 g_signal_emit (calendar,
3025 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
3031 gtk_calendar_button_press (GtkWidget *widget,
3032 GdkEventButton *event)
3034 GtkCalendar *calendar = GTK_CALENDAR (widget);
3035 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3038 if (event->window == priv->main_win)
3039 calendar_main_button_press (calendar, event);
3041 if (!gtk_widget_has_focus (widget))
3042 gtk_widget_grab_focus (widget);
3044 for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
3046 if (event->window == priv->arrow_win[arrow])
3049 /* only call the action on single click, not double */
3050 if (event->type == GDK_BUTTON_PRESS)
3052 if (event->button == 1)
3053 calendar_start_spinning (calendar, arrow);
3055 calendar_arrow_action (calendar, arrow);
3066 gtk_calendar_button_release (GtkWidget *widget,
3067 GdkEventButton *event)
3069 GtkCalendar *calendar = GTK_CALENDAR (widget);
3070 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3072 if (event->button == 1)
3074 calendar_stop_spinning (calendar);
3084 gtk_calendar_motion_notify (GtkWidget *widget,
3085 GdkEventMotion *event)
3087 GtkCalendar *calendar = GTK_CALENDAR (widget);
3088 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3089 gint event_x, event_y;
3091 gint old_row, old_col;
3093 event_x = (gint) (event->x);
3094 event_y = (gint) (event->y);
3096 if (event->window == priv->main_win)
3101 if (gtk_drag_check_threshold (widget,
3102 priv->drag_start_x, priv->drag_start_y,
3103 event->x, event->y))
3105 GdkDragContext *context;
3106 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
3107 gtk_target_list_add_text_targets (target_list, 0);
3108 context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
3109 1, (GdkEvent *)event);
3114 gtk_target_list_unref (target_list);
3115 gtk_drag_set_icon_default (context);
3120 row = calendar_row_from_y (calendar, event_y);
3121 col = calendar_column_from_x (calendar, event_x);
3123 if (row != priv->highlight_row || priv->highlight_col != col)
3125 old_row = priv->highlight_row;
3126 old_col = priv->highlight_col;
3127 if (old_row > -1 && old_col > -1)
3129 priv->highlight_row = -1;
3130 priv->highlight_col = -1;
3131 calendar_invalidate_day (calendar, old_row, old_col);
3134 priv->highlight_row = row;
3135 priv->highlight_col = col;
3137 if (row > -1 && col > -1)
3138 calendar_invalidate_day (calendar, row, col);
3146 gtk_calendar_enter_notify (GtkWidget *widget,
3147 GdkEventCrossing *event)
3149 GtkCalendar *calendar = GTK_CALENDAR (widget);
3150 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3152 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3154 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
3155 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3158 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3160 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
3161 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3164 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3166 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
3167 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3170 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3172 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
3173 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3180 gtk_calendar_leave_notify (GtkWidget *widget,
3181 GdkEventCrossing *event)
3183 GtkCalendar *calendar = GTK_CALENDAR (widget);
3184 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3188 if (event->window == priv->main_win)
3190 row = priv->highlight_row;
3191 col = priv->highlight_col;
3192 priv->highlight_row = -1;
3193 priv->highlight_col = -1;
3194 if (row > -1 && col > -1)
3195 calendar_invalidate_day (calendar, row, col);
3198 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3200 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3201 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3204 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3206 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3207 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3210 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3212 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3213 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3216 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3218 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3219 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3226 gtk_calendar_scroll (GtkWidget *widget,
3227 GdkEventScroll *event)
3229 GtkCalendar *calendar = GTK_CALENDAR (widget);
3231 if (event->direction == GDK_SCROLL_UP)
3233 if (!gtk_widget_has_focus (widget))
3234 gtk_widget_grab_focus (widget);
3235 calendar_set_month_prev (calendar);
3237 else if (event->direction == GDK_SCROLL_DOWN)
3239 if (!gtk_widget_has_focus (widget))
3240 gtk_widget_grab_focus (widget);
3241 calendar_set_month_next (calendar);
3250 /****************************************
3252 ****************************************/
3255 move_focus (GtkCalendar *calendar,
3258 GtkCalendarPrivate *priv = calendar->priv;
3259 GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3261 if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3262 (text_dir == GTK_TEXT_DIR_RTL && direction == 1))
3264 if (priv->focus_col > 0)
3266 else if (priv->focus_row > 0)
3268 priv->focus_col = 6;
3272 if (priv->focus_col < 0)
3273 priv->focus_col = 6;
3274 if (priv->focus_row < 0)
3275 priv->focus_row = 5;
3279 if (priv->focus_col < 6)
3281 else if (priv->focus_row < 5)
3283 priv->focus_col = 0;
3287 if (priv->focus_col < 0)
3288 priv->focus_col = 0;
3289 if (priv->focus_row < 0)
3290 priv->focus_row = 0;
3295 gtk_calendar_key_press (GtkWidget *widget,
3298 GtkCalendar *calendar = GTK_CALENDAR (widget);
3299 GtkCalendarPrivate *priv = calendar->priv;
3307 old_focus_row = priv->focus_row;
3308 old_focus_col = priv->focus_col;
3310 switch (event->keyval)
3315 if (event->state & GDK_CONTROL_MASK)
3316 calendar_set_month_prev (calendar);
3319 move_focus (calendar, -1);
3320 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3321 calendar_invalidate_day (calendar, priv->focus_row,
3328 if (event->state & GDK_CONTROL_MASK)
3329 calendar_set_month_next (calendar);
3332 move_focus (calendar, 1);
3333 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3334 calendar_invalidate_day (calendar, priv->focus_row,
3341 if (event->state & GDK_CONTROL_MASK)
3342 calendar_set_year_prev (calendar);
3345 if (priv->focus_row > 0)
3347 if (priv->focus_row < 0)
3348 priv->focus_row = 5;
3349 if (priv->focus_col < 0)
3350 priv->focus_col = 6;
3351 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3352 calendar_invalidate_day (calendar, priv->focus_row,
3359 if (event->state & GDK_CONTROL_MASK)
3360 calendar_set_year_next (calendar);
3363 if (priv->focus_row < 5)
3365 if (priv->focus_col < 0)
3366 priv->focus_col = 0;
3367 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3368 calendar_invalidate_day (calendar, priv->focus_row,
3374 row = priv->focus_row;
3375 col = priv->focus_col;
3377 if (row > -1 && col > -1)
3381 day = priv->day[row][col];
3382 if (priv->day_month[row][col] == MONTH_PREV)
3383 calendar_set_month_prev (calendar);
3384 else if (priv->day_month[row][col] == MONTH_NEXT)
3385 calendar_set_month_next (calendar);
3387 calendar_select_and_focus_day (calendar, day);
3395 /****************************************
3396 * Misc widget methods *
3397 ****************************************/
3400 calendar_set_background (GtkWidget *widget)
3402 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3405 if (gtk_widget_get_realized (widget))
3407 for (i = 0; i < 4; i++)
3409 if (priv->arrow_win[i])
3410 gdk_window_set_background (priv->arrow_win[i],
3411 HEADER_BG_COLOR (widget));
3413 if (priv->header_win)
3414 gdk_window_set_background (priv->header_win,
3415 HEADER_BG_COLOR (widget));
3416 if (priv->day_name_win)
3417 gdk_window_set_background (priv->day_name_win,
3418 BACKGROUND_COLOR (widget));
3420 gdk_window_set_background (priv->week_win,
3421 BACKGROUND_COLOR (widget));
3423 gdk_window_set_background (priv->main_win,
3424 BACKGROUND_COLOR (widget));
3426 gdk_window_set_background (widget->window,
3427 BACKGROUND_COLOR (widget));
3432 gtk_calendar_style_set (GtkWidget *widget,
3433 GtkStyle *previous_style)
3435 if (previous_style && gtk_widget_get_realized (widget))
3436 calendar_set_background (widget);
3440 gtk_calendar_state_changed (GtkWidget *widget,
3441 GtkStateType previous_state)
3443 GtkCalendar *calendar = GTK_CALENDAR (widget);
3444 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3447 if (!gtk_widget_is_sensitive (widget))
3450 calendar_stop_spinning (calendar);
3453 for (i = 0; i < 4; i++)
3454 if (gtk_widget_is_sensitive (widget))
3455 priv->arrow_state[i] = GTK_STATE_NORMAL;
3457 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
3459 calendar_set_background (widget);
3463 gtk_calendar_grab_notify (GtkWidget *widget,
3464 gboolean was_grabbed)
3467 calendar_stop_spinning (GTK_CALENDAR (widget));
3471 gtk_calendar_focus_out (GtkWidget *widget,
3472 GdkEventFocus *event)
3474 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3475 GtkCalendar *calendar = GTK_CALENDAR (widget);
3477 calendar_queue_refresh (calendar);
3478 calendar_stop_spinning (calendar);
3486 /****************************************
3488 ****************************************/
3491 gtk_calendar_drag_data_get (GtkWidget *widget,
3492 GdkDragContext *context,
3493 GtkSelectionData *selection_data,
3497 GtkCalendar *calendar = GTK_CALENDAR (widget);
3498 GtkCalendarPrivate *priv = calendar->priv;
3503 date = g_date_new_dmy (priv->selected_day, priv->month + 1, priv->year);
3504 len = g_date_strftime (str, 127, "%x", date);
3505 gtk_selection_data_set_text (selection_data, str, len);
3510 /* Get/set whether drag_motion requested the drag data and
3511 * drag_data_received should thus not actually insert the data,
3512 * since the data doesn't result from a drop.
3515 set_status_pending (GdkDragContext *context,
3516 GdkDragAction suggested_action)
3518 g_object_set_data (G_OBJECT (context),
3519 I_("gtk-calendar-status-pending"),
3520 GINT_TO_POINTER (suggested_action));
3523 static GdkDragAction
3524 get_status_pending (GdkDragContext *context)
3526 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
3527 "gtk-calendar-status-pending"));
3531 gtk_calendar_drag_leave (GtkWidget *widget,
3532 GdkDragContext *context,
3535 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3537 priv->drag_highlight = 0;
3538 gtk_drag_unhighlight (widget);
3543 gtk_calendar_drag_motion (GtkWidget *widget,
3544 GdkDragContext *context,
3549 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3552 if (!priv->drag_highlight)
3554 priv->drag_highlight = 1;
3555 gtk_drag_highlight (widget);
3558 target = gtk_drag_dest_find_target (widget, context, NULL);
3559 if (target == GDK_NONE || context->suggested_action == 0)
3560 gdk_drag_status (context, 0, time);
3563 set_status_pending (context, context->suggested_action);
3564 gtk_drag_get_data (widget, context, target, time);
3571 gtk_calendar_drag_drop (GtkWidget *widget,
3572 GdkDragContext *context,
3579 target = gtk_drag_dest_find_target (widget, context, NULL);
3580 if (target != GDK_NONE)
3582 gtk_drag_get_data (widget, context,
3592 gtk_calendar_drag_data_received (GtkWidget *widget,
3593 GdkDragContext *context,
3596 GtkSelectionData *selection_data,
3600 GtkCalendar *calendar = GTK_CALENDAR (widget);
3601 GtkCalendarPrivate *priv = calendar->priv;
3602 guint day, month, year;
3605 GdkDragAction suggested_action;
3607 suggested_action = get_status_pending (context);
3609 if (suggested_action)
3611 set_status_pending (context, 0);
3613 /* We are getting this data due to a request in drag_motion,
3614 * rather than due to a request in drag_drop, so we are just
3615 * supposed to call drag_status, not actually paste in the
3618 str = (gchar*) gtk_selection_data_get_text (selection_data);
3622 date = g_date_new ();
3623 g_date_set_parse (date, str);
3624 if (!g_date_valid (date))
3625 suggested_action = 0;
3630 suggested_action = 0;
3632 gdk_drag_status (context, suggested_action, time);
3637 date = g_date_new ();
3638 str = (gchar*) gtk_selection_data_get_text (selection_data);
3641 g_date_set_parse (date, str);
3645 if (!g_date_valid (date))
3647 g_warning ("Received invalid date data\n");
3649 gtk_drag_finish (context, FALSE, FALSE, time);
3653 day = g_date_get_day (date);
3654 month = g_date_get_month (date);
3655 year = g_date_get_year (date);
3658 gtk_drag_finish (context, TRUE, FALSE, time);
3661 g_object_freeze_notify (G_OBJECT (calendar));
3662 if (!(priv->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3663 && (priv->display_flags & GTK_CALENDAR_SHOW_HEADING))
3664 gtk_calendar_select_month (calendar, month - 1, year);
3665 gtk_calendar_select_day (calendar, day);
3666 g_object_thaw_notify (G_OBJECT (calendar));
3670 /****************************************
3672 ****************************************/
3677 * Creates a new calendar, with the current date being selected.
3679 * Return value: a newly #GtkCalendar widget
3682 gtk_calendar_new (void)
3684 return g_object_new (GTK_TYPE_CALENDAR, NULL);
3688 * gtk_calendar_get_display_options:
3689 * @calendar: a #GtkCalendar
3691 * Returns the current display options of @calendar.
3693 * Return value: the display options.
3697 GtkCalendarDisplayOptions
3698 gtk_calendar_get_display_options (GtkCalendar *calendar)
3700 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3702 return calendar->priv->display_flags;
3706 * gtk_calendar_set_display_options:
3707 * @calendar: a #GtkCalendar
3708 * @flags: the display options to set
3710 * Sets display options (whether to display the heading and the month
3716 gtk_calendar_set_display_options (GtkCalendar *calendar,
3717 GtkCalendarDisplayOptions flags)
3719 GtkWidget *widget = GTK_WIDGET (calendar);
3720 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3723 GtkCalendarDisplayOptions old_flags;
3725 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3727 old_flags = priv->display_flags;
3729 if (gtk_widget_get_realized (widget))
3731 if ((flags ^ priv->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3734 if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3735 && (priv->header_win))
3737 priv->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3738 calendar_realize_arrows (calendar);
3742 for (i = 0; i < 4; i++)
3744 if (priv->arrow_win[i])
3746 gdk_window_set_user_data (priv->arrow_win[i],
3748 gdk_window_destroy (priv->arrow_win[i]);
3749 priv->arrow_win[i] = NULL;
3755 if ((flags ^ priv->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3759 if (flags & GTK_CALENDAR_SHOW_HEADING)
3761 priv->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3762 calendar_realize_header (calendar);
3766 for (i = 0; i < 4; i++)
3768 if (priv->arrow_win[i])
3770 gdk_window_set_user_data (priv->arrow_win[i],
3772 gdk_window_destroy (priv->arrow_win[i]);
3773 priv->arrow_win[i] = NULL;
3776 gdk_window_set_user_data (priv->header_win, NULL);
3777 gdk_window_destroy (priv->header_win);
3778 priv->header_win = NULL;
3783 if ((flags ^ priv->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3787 if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3789 priv->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3790 calendar_realize_day_names (calendar);
3794 gdk_window_set_user_data (priv->day_name_win, NULL);
3795 gdk_window_destroy (priv->day_name_win);
3796 priv->day_name_win = NULL;
3800 if ((flags ^ priv->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3804 if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3806 priv->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3807 calendar_realize_week_numbers (calendar);
3811 gdk_window_set_user_data (priv->week_win, NULL);
3812 gdk_window_destroy (priv->week_win);
3813 priv->week_win = NULL;
3817 if ((flags ^ priv->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3818 g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3820 if ((flags ^ priv->display_flags) & GTK_CALENDAR_SHOW_DETAILS)
3823 priv->display_flags = flags;
3825 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3829 priv->display_flags = flags;
3831 g_object_freeze_notify (G_OBJECT (calendar));
3832 if ((old_flags ^ priv->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3833 g_object_notify (G_OBJECT (calendar), "show-heading");
3834 if ((old_flags ^ priv->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3835 g_object_notify (G_OBJECT (calendar), "show-day-names");
3836 if ((old_flags ^ priv->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3837 g_object_notify (G_OBJECT (calendar), "no-month-change");
3838 if ((old_flags ^ priv->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3839 g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3840 g_object_thaw_notify (G_OBJECT (calendar));
3844 * gtk_calendar_select_month:
3845 * @calendar: a #GtkCalendar
3846 * @month: a month number between 0 and 11.
3847 * @year: the year the month is in.
3849 * Shifts the calendar to a different month.
3852 gtk_calendar_select_month (GtkCalendar *calendar,
3856 GtkCalendarPrivate *priv;
3858 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3859 g_return_if_fail (month <= 11);
3861 priv = calendar->priv;
3863 priv->month = month;
3866 calendar_compute_days (calendar);
3867 calendar_queue_refresh (calendar);
3869 g_object_freeze_notify (G_OBJECT (calendar));
3870 g_object_notify (G_OBJECT (calendar), "month");
3871 g_object_notify (G_OBJECT (calendar), "year");
3872 g_object_thaw_notify (G_OBJECT (calendar));
3874 g_signal_emit (calendar,
3875 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3880 * gtk_calendar_select_day:
3881 * @calendar: a #GtkCalendar.
3882 * @day: the day number between 1 and 31, or 0 to unselect
3883 * the currently selected day.
3885 * Selects a day from the current month.
3888 gtk_calendar_select_day (GtkCalendar *calendar,
3891 GtkCalendarPrivate *priv;
3893 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3894 g_return_if_fail (day <= 31);
3896 priv = calendar->priv;
3898 /* Deselect the old day */
3899 if (priv->selected_day > 0)
3903 selected_day = priv->selected_day;
3904 priv->selected_day = 0;
3905 if (gtk_widget_is_drawable (GTK_WIDGET (calendar)))
3906 calendar_invalidate_day_num (calendar, selected_day);
3909 priv->selected_day = day;
3911 /* Select the new day */
3914 if (gtk_widget_is_drawable (GTK_WIDGET (calendar)))
3915 calendar_invalidate_day_num (calendar, day);
3918 g_object_notify (G_OBJECT (calendar), "day");
3920 g_signal_emit (calendar,
3921 gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3926 * gtk_calendar_clear_marks:
3927 * @calendar: a #GtkCalendar
3929 * Remove all visual markers.
3932 gtk_calendar_clear_marks (GtkCalendar *calendar)
3934 GtkCalendarPrivate *priv;
3937 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3939 priv = calendar->priv;
3941 for (day = 0; day < 31; day++)
3943 priv->marked_date[day] = FALSE;
3946 priv->num_marked_dates = 0;
3947 calendar_queue_refresh (calendar);
3951 * gtk_calendar_mark_day:
3952 * @calendar: a #GtkCalendar
3953 * @day: the day number to mark between 1 and 31.
3955 * Places a visual marker on a particular day.
3958 gtk_calendar_mark_day (GtkCalendar *calendar,
3961 GtkCalendarPrivate *priv;
3963 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3965 priv = calendar->priv;
3967 if (day >= 1 && day <= 31 && !priv->marked_date[day-1])
3969 priv->marked_date[day - 1] = TRUE;
3970 priv->num_marked_dates++;
3971 calendar_invalidate_day_num (calendar, day);
3976 * gtk_calendar_unmark_day:
3977 * @calendar: a #GtkCalendar.
3978 * @day: the day number to unmark between 1 and 31.
3980 * Removes the visual marker from a particular day.
3983 gtk_calendar_unmark_day (GtkCalendar *calendar,
3986 GtkCalendarPrivate *priv;
3988 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3990 priv = calendar->priv;
3992 if (day >= 1 && day <= 31 && priv->marked_date[day-1])
3994 priv->marked_date[day - 1] = FALSE;
3995 priv->num_marked_dates--;
3996 calendar_invalidate_day_num (calendar, day);
4001 * gtk_calendar_get_date:
4002 * @calendar: a #GtkCalendar
4003 * @year: (allow-none): location to store the year number, or %NULL
4004 * @month: (allow-none): location to store the month number (between 0 and 11), or %NULL
4005 * @day: (allow-none): location to store the day number (between 1 and 31), or %NULL
4007 * Obtains the selected date from a #GtkCalendar.
4010 gtk_calendar_get_date (GtkCalendar *calendar,
4015 GtkCalendarPrivate *priv;
4017 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4019 priv = calendar->priv;
4025 *month = priv->month;
4028 *day = priv->selected_day;
4032 * gtk_calendar_set_detail_func:
4033 * @calendar: a #GtkCalendar.
4034 * @func: a function providing details for each day.
4035 * @data: data to pass to @func invokations.
4036 * @destroy: a function for releasing @data.
4038 * Installs a function which provides Pango markup with detail information
4039 * for each day. Examples for such details are holidays or appointments. That
4040 * information is shown below each day when #GtkCalendar:show-details is set.
4041 * A tooltip containing with full detail information is provided, if the entire
4042 * text should not fit into the details area, or if #GtkCalendar:show-details
4045 * The size of the details area can be restricted by setting the
4046 * #GtkCalendar:detail-width-chars and #GtkCalendar:detail-height-rows
4052 gtk_calendar_set_detail_func (GtkCalendar *calendar,
4053 GtkCalendarDetailFunc func,
4055 GDestroyNotify destroy)
4057 GtkCalendarPrivate *priv;
4059 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4061 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
4063 if (priv->detail_func_destroy)
4064 priv->detail_func_destroy (priv->detail_func_user_data);
4066 priv->detail_func = func;
4067 priv->detail_func_user_data = data;
4068 priv->detail_func_destroy = destroy;
4070 gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
4071 NULL != priv->detail_func);
4072 gtk_widget_queue_resize (GTK_WIDGET (calendar));
4076 * gtk_calendar_set_detail_width_chars:
4077 * @calendar: a #GtkCalendar.
4078 * @chars: detail width in characters.
4080 * Updates the width of detail cells.
4081 * See #GtkCalendar:detail-width-chars.
4086 gtk_calendar_set_detail_width_chars (GtkCalendar *calendar,
4089 GtkCalendarPrivate *priv;
4091 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4093 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
4095 if (chars != priv->detail_width_chars)
4097 priv->detail_width_chars = chars;
4098 g_object_notify (G_OBJECT (calendar), "detail-width-chars");
4099 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
4104 * gtk_calendar_set_detail_height_rows:
4105 * @calendar: a #GtkCalendar.
4106 * @rows: detail height in rows.
4108 * Updates the height of detail cells.
4109 * See #GtkCalendar:detail-height-rows.
4114 gtk_calendar_set_detail_height_rows (GtkCalendar *calendar,
4117 GtkCalendarPrivate *priv;
4119 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4121 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
4123 if (rows != priv->detail_height_rows)
4125 priv->detail_height_rows = rows;
4126 g_object_notify (G_OBJECT (calendar), "detail-height-rows");
4127 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
4132 * gtk_calendar_get_detail_width_chars:
4133 * @calendar: a #GtkCalendar.
4135 * Queries the width of detail cells, in characters.
4136 * See #GtkCalendar:detail-width-chars.
4140 * Return value: The width of detail cells, in characters.
4143 gtk_calendar_get_detail_width_chars (GtkCalendar *calendar)
4145 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
4146 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
4150 * gtk_calendar_get_detail_height_rows:
4151 * @calendar: a #GtkCalendar.
4153 * Queries the height of detail cells, in rows.
4154 * See #GtkCalendar:detail-width-chars.
4158 * Return value: The height of detail cells, in rows.
4161 gtk_calendar_get_detail_height_rows (GtkCalendar *calendar)
4163 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
4164 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;