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 #ifdef HAVE_SYS_TIME_H
37 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
50 #include "gtkcalendar.h"
54 #include "gtkmarshalers.h"
55 #include "gtktooltip.h"
56 #include "gtkprivate.h"
57 #include "gdk/gdkkeysyms.h"
60 /***************************************************************************/
61 /* The following date routines are taken from the lib_date package.
62 * They have been minimally edited to avoid conflict with types defined
66 static const guint month_length[2][13] =
68 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
69 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
72 static const guint days_in_months[2][14] =
74 { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
75 { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
78 static glong calc_days(guint year, guint mm, guint dd);
79 static guint day_of_week(guint year, guint mm, guint dd);
80 static glong dates_difference(guint year1, guint mm1, guint dd1,
81 guint year2, guint mm2, guint dd2);
82 static guint weeks_in_year(guint year);
87 return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
91 day_of_week (guint year, guint mm, guint dd)
95 days = calc_days(year, mm, dd);
102 return( (guint) days );
105 static guint weeks_in_year(guint year)
107 return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
111 check_date(guint year, guint mm, guint dd)
113 if (year < 1) return FALSE;
114 if ((mm < 1) || (mm > 12)) return FALSE;
115 if ((dd < 1) || (dd > month_length[leap(year)][mm])) return FALSE;
120 week_number(guint year, guint mm, guint dd)
124 first = day_of_week(year,1,1) - 1;
125 return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
130 year_to_days(guint year)
132 return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
137 calc_days(guint year, guint mm, guint dd)
141 if (year < 1) return(0L);
142 if ((mm < 1) || (mm > 12)) return(0L);
143 if ((dd < 1) || (dd > month_length[(lp = leap(year))][mm])) return(0L);
144 return( year_to_days(--year) + days_in_months[lp][mm] + dd );
148 week_of_year(guint *week, guint *year, guint mm, guint dd)
150 if (check_date(*year,mm,dd))
152 *week = week_number(*year,mm,dd);
154 *week = weeks_in_year(--(*year));
155 else if (*week > weeks_in_year(*year))
166 dates_difference(guint year1, guint mm1, guint dd1,
167 guint year2, guint mm2, guint dd2)
169 return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
172 /*** END OF lib_date routines ********************************************/
174 /* Spacing around day/week headers and main area, inside those windows */
175 #define CALENDAR_MARGIN 0
176 /* Spacing around day/week headers and main area, outside those windows */
177 #define INNER_BORDER 4
178 /* Separation between day headers and main area */
179 #define CALENDAR_YSEP 4
180 /* Separation between week headers and main area */
181 #define CALENDAR_XSEP 4
183 #define DAY_XSEP 0 /* not really good for small calendar */
184 #define DAY_YSEP 0 /* not really good for small calendar */
186 #define SCROLL_DELAY_FACTOR 5
189 #define HEADER_FG_COLOR(widget) (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
190 #define HEADER_BG_COLOR(widget) (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
191 #define SELECTED_BG_COLOR(widget) (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
192 #define SELECTED_FG_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
193 #define NORMAL_DAY_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
194 #define PREV_MONTH_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
195 #define NEXT_MONTH_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
196 #define MARKED_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
197 #define BACKGROUND_COLOR(widget) (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
198 #define HIGHLIGHT_BACK_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
214 MONTH_CHANGED_SIGNAL,
216 DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
232 PROP_NO_MONTH_CHANGE,
233 PROP_SHOW_WEEK_NUMBERS,
235 PROP_DETAIL_WIDTH_CHARS,
236 PROP_DETAIL_HEIGHT_ROWS
239 static guint gtk_calendar_signals[LAST_SIGNAL] = { 0 };
241 struct _GtkCalendarPrivate
243 GdkWindow *header_win;
244 GdkWindow *day_name_win;
247 GdkWindow *arrow_win[4];
253 guint arrow_state[4];
255 guint max_month_width;
256 guint max_year_width;
262 guint max_day_char_width;
263 guint max_day_char_ascent;
264 guint max_day_char_descent;
265 guint max_label_char_ascent;
266 guint max_label_char_descent;
267 guint max_week_char_width;
270 guint year_before : 1;
272 guint need_timer : 1;
275 guint drag_highlight : 1;
285 /* Optional callback, used to display extra information for each day. */
286 GtkCalendarDetailFunc detail_func;
287 gpointer detail_func_user_data;
288 GDestroyNotify detail_func_destroy;
290 /* Size requistion for details provided by the hook. */
291 gint detail_height_rows;
292 gint detail_width_chars;
293 gint detail_overflow[6];
296 #define GTK_CALENDAR_GET_PRIVATE(widget) (GTK_CALENDAR (widget)->priv)
298 static void gtk_calendar_finalize (GObject *calendar);
299 static void gtk_calendar_destroy (GtkObject *calendar);
300 static void gtk_calendar_set_property (GObject *object,
304 static void gtk_calendar_get_property (GObject *object,
309 static void gtk_calendar_realize (GtkWidget *widget);
310 static void gtk_calendar_unrealize (GtkWidget *widget);
311 static void gtk_calendar_size_request (GtkWidget *widget,
312 GtkRequisition *requisition);
313 static void gtk_calendar_size_allocate (GtkWidget *widget,
314 GtkAllocation *allocation);
315 static gboolean gtk_calendar_expose (GtkWidget *widget,
316 GdkEventExpose *event);
317 static gboolean gtk_calendar_button_press (GtkWidget *widget,
318 GdkEventButton *event);
319 static gboolean gtk_calendar_button_release (GtkWidget *widget,
320 GdkEventButton *event);
321 static gboolean gtk_calendar_motion_notify (GtkWidget *widget,
322 GdkEventMotion *event);
323 static gboolean gtk_calendar_enter_notify (GtkWidget *widget,
324 GdkEventCrossing *event);
325 static gboolean gtk_calendar_leave_notify (GtkWidget *widget,
326 GdkEventCrossing *event);
327 static gboolean gtk_calendar_scroll (GtkWidget *widget,
328 GdkEventScroll *event);
329 static gboolean gtk_calendar_key_press (GtkWidget *widget,
331 static gboolean gtk_calendar_focus_out (GtkWidget *widget,
332 GdkEventFocus *event);
333 static void gtk_calendar_grab_notify (GtkWidget *widget,
334 gboolean was_grabbed);
335 static void gtk_calendar_state_changed (GtkWidget *widget,
336 GtkStateType previous_state);
337 static void gtk_calendar_style_set (GtkWidget *widget,
338 GtkStyle *previous_style);
339 static gboolean gtk_calendar_query_tooltip (GtkWidget *widget,
342 gboolean keyboard_mode,
343 GtkTooltip *tooltip);
345 static void gtk_calendar_drag_data_get (GtkWidget *widget,
346 GdkDragContext *context,
347 GtkSelectionData *selection_data,
350 static void gtk_calendar_drag_data_received (GtkWidget *widget,
351 GdkDragContext *context,
354 GtkSelectionData *selection_data,
357 static gboolean gtk_calendar_drag_motion (GtkWidget *widget,
358 GdkDragContext *context,
362 static void gtk_calendar_drag_leave (GtkWidget *widget,
363 GdkDragContext *context,
365 static gboolean gtk_calendar_drag_drop (GtkWidget *widget,
366 GdkDragContext *context,
371 static void calendar_start_spinning (GtkCalendar *calendar,
373 static void calendar_stop_spinning (GtkCalendar *calendar);
375 static void calendar_invalidate_day (GtkCalendar *widget,
378 static void calendar_invalidate_day_num (GtkCalendar *widget,
380 static void calendar_invalidate_arrow (GtkCalendar *widget,
383 static void calendar_compute_days (GtkCalendar *calendar);
385 static char *default_abbreviated_dayname[7];
386 static char *default_monthname[12];
388 G_DEFINE_TYPE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET)
391 gtk_calendar_class_init (GtkCalendarClass *class)
393 GObjectClass *gobject_class;
394 GtkObjectClass *object_class;
395 GtkWidgetClass *widget_class;
397 gobject_class = (GObjectClass*) class;
398 object_class = (GtkObjectClass*) class;
399 widget_class = (GtkWidgetClass*) class;
401 gobject_class->set_property = gtk_calendar_set_property;
402 gobject_class->get_property = gtk_calendar_get_property;
403 gobject_class->finalize = gtk_calendar_finalize;
405 object_class->destroy = gtk_calendar_destroy;
407 widget_class->realize = gtk_calendar_realize;
408 widget_class->unrealize = gtk_calendar_unrealize;
409 widget_class->expose_event = gtk_calendar_expose;
410 widget_class->size_request = gtk_calendar_size_request;
411 widget_class->size_allocate = gtk_calendar_size_allocate;
412 widget_class->button_press_event = gtk_calendar_button_press;
413 widget_class->button_release_event = gtk_calendar_button_release;
414 widget_class->motion_notify_event = gtk_calendar_motion_notify;
415 widget_class->enter_notify_event = gtk_calendar_enter_notify;
416 widget_class->leave_notify_event = gtk_calendar_leave_notify;
417 widget_class->key_press_event = gtk_calendar_key_press;
418 widget_class->scroll_event = gtk_calendar_scroll;
419 widget_class->style_set = gtk_calendar_style_set;
420 widget_class->state_changed = gtk_calendar_state_changed;
421 widget_class->grab_notify = gtk_calendar_grab_notify;
422 widget_class->focus_out_event = gtk_calendar_focus_out;
423 widget_class->query_tooltip = gtk_calendar_query_tooltip;
425 widget_class->drag_data_get = gtk_calendar_drag_data_get;
426 widget_class->drag_motion = gtk_calendar_drag_motion;
427 widget_class->drag_leave = gtk_calendar_drag_leave;
428 widget_class->drag_drop = gtk_calendar_drag_drop;
429 widget_class->drag_data_received = gtk_calendar_drag_data_received;
435 * This property gets initially set to the current year.
437 g_object_class_install_property (gobject_class,
439 g_param_spec_int ("year",
441 P_("The selected year"),
443 GTK_PARAM_READWRITE));
448 * The selected month (as a number between 0 and 11).
449 * This property gets initially set to the current month.
451 g_object_class_install_property (gobject_class,
453 g_param_spec_int ("month",
455 P_("The selected month (as a number between 0 and 11)"),
457 GTK_PARAM_READWRITE));
462 * The selected day (as a number between 1 and 31, or 0
463 * to unselect the currently selected day).
464 * This property gets initially set to the current day.
466 g_object_class_install_property (gobject_class,
468 g_param_spec_int ("day",
470 P_("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
472 GTK_PARAM_READWRITE));
475 * GtkCalendar:show-heading:
477 * Determines whether a heading is displayed.
481 g_object_class_install_property (gobject_class,
483 g_param_spec_boolean ("show-heading",
485 P_("If TRUE, a heading is displayed"),
487 GTK_PARAM_READWRITE));
490 * GtkCalendar:show-day-names:
492 * Determines whether day names are displayed.
496 g_object_class_install_property (gobject_class,
498 g_param_spec_boolean ("show-day-names",
499 P_("Show Day Names"),
500 P_("If TRUE, day names are displayed"),
502 GTK_PARAM_READWRITE));
504 * GtkCalendar:no-month-change:
506 * Determines whether the selected month can be changed.
510 g_object_class_install_property (gobject_class,
511 PROP_NO_MONTH_CHANGE,
512 g_param_spec_boolean ("no-month-change",
513 P_("No Month Change"),
514 P_("If TRUE, the selected month cannot be changed"),
516 GTK_PARAM_READWRITE));
519 * GtkCalendar:show-week-numbers:
521 * Determines whether week numbers are displayed.
525 g_object_class_install_property (gobject_class,
526 PROP_SHOW_WEEK_NUMBERS,
527 g_param_spec_boolean ("show-week-numbers",
528 P_("Show Week Numbers"),
529 P_("If TRUE, week numbers are displayed"),
531 GTK_PARAM_READWRITE));
534 * GtkCalendar:detail-width-chars:
536 * Width of a detail cell, in characters.
537 * A value of 0 allows any width. See gtk_calendar_set_detail_func().
541 g_object_class_install_property (gobject_class,
542 PROP_DETAIL_WIDTH_CHARS,
543 g_param_spec_int ("detail-width-chars",
545 P_("Details width in characters"),
547 GTK_PARAM_READWRITE));
550 * GtkCalendar:detail-height-rows:
552 * Height of a detail cell, in rows.
553 * A value of 0 allows any width. See gtk_calendar_set_detail_func().
557 g_object_class_install_property (gobject_class,
558 PROP_DETAIL_HEIGHT_ROWS,
559 g_param_spec_int ("detail-height-rows",
560 P_("Details Height"),
561 P_("Details height in rows"),
563 GTK_PARAM_READWRITE));
566 * GtkCalendar:show-details:
568 * Determines whether details are shown directly in the widget, or if they are
569 * available only as tooltip. When this property is set days with details are
574 g_object_class_install_property (gobject_class,
576 g_param_spec_boolean ("show-details",
578 P_("If TRUE, details are shown"),
580 GTK_PARAM_READWRITE));
582 gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
583 g_signal_new (I_("month-changed"),
584 G_OBJECT_CLASS_TYPE (gobject_class),
586 G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
588 _gtk_marshal_VOID__VOID,
590 gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
591 g_signal_new (I_("day-selected"),
592 G_OBJECT_CLASS_TYPE (gobject_class),
594 G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
596 _gtk_marshal_VOID__VOID,
598 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
599 g_signal_new (I_("day-selected-double-click"),
600 G_OBJECT_CLASS_TYPE (gobject_class),
602 G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
604 _gtk_marshal_VOID__VOID,
606 gtk_calendar_signals[PREV_MONTH_SIGNAL] =
607 g_signal_new (I_("prev-month"),
608 G_OBJECT_CLASS_TYPE (gobject_class),
610 G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
612 _gtk_marshal_VOID__VOID,
614 gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
615 g_signal_new (I_("next-month"),
616 G_OBJECT_CLASS_TYPE (gobject_class),
618 G_STRUCT_OFFSET (GtkCalendarClass, next_month),
620 _gtk_marshal_VOID__VOID,
622 gtk_calendar_signals[PREV_YEAR_SIGNAL] =
623 g_signal_new (I_("prev-year"),
624 G_OBJECT_CLASS_TYPE (gobject_class),
626 G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
628 _gtk_marshal_VOID__VOID,
630 gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
631 g_signal_new (I_("next-year"),
632 G_OBJECT_CLASS_TYPE (gobject_class),
634 G_STRUCT_OFFSET (GtkCalendarClass, next_year),
636 _gtk_marshal_VOID__VOID,
639 g_type_class_add_private (gobject_class, sizeof (GtkCalendarPrivate));
643 gtk_calendar_init (GtkCalendar *calendar)
645 GtkWidget *widget = GTK_WIDGET (calendar);
650 wchar_t wbuffer[100];
655 GtkCalendarPrivate *priv;
657 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
658 union { unsigned int word; char *string; } langinfo;
659 gint week_1stday = 0;
660 gint first_weekday = 1;
666 priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
670 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
672 if (!default_abbreviated_dayname[0])
676 tmp_time= (i+3)*86400;
677 strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
678 default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
680 if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
681 wbuffer, G_N_ELEMENTS (wbuffer)))
682 default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
684 default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
688 if (!default_monthname[0])
693 strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
694 default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
696 if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
697 wbuffer, G_N_ELEMENTS (wbuffer)))
698 default_monthname[i] = g_strdup_printf ("(%d)", i);
700 default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
706 tm = localtime (&secs);
707 calendar->month = tm->tm_mon;
708 calendar->year = 1900 + tm->tm_year;
711 calendar->marked_date[i] = FALSE;
712 calendar->num_marked_dates = 0;
713 calendar->selected_day = tm->tm_mday;
715 calendar->display_flags = (GTK_CALENDAR_SHOW_HEADING |
716 GTK_CALENDAR_SHOW_DAY_NAMES |
717 GTK_CALENDAR_SHOW_DETAILS);
719 calendar->highlight_row = -1;
720 calendar->highlight_col = -1;
722 calendar->focus_row = -1;
723 calendar->focus_col = -1;
725 priv->max_year_width = 0;
726 priv->max_month_width = 0;
727 priv->max_day_char_width = 0;
728 priv->max_week_char_width = 0;
730 priv->max_day_char_ascent = 0;
731 priv->max_day_char_descent = 0;
732 priv->max_label_char_ascent = 0;
733 priv->max_label_char_descent = 0;
735 priv->arrow_width = 10;
737 priv->need_timer = 0;
739 priv->click_child = -1;
742 priv->drag_highlight = 0;
744 gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
745 gtk_drag_dest_add_text_targets (widget);
747 priv->year_before = 0;
749 /* Translate to calendar:YM if you want years to be displayed
750 * before months; otherwise translate to calendar:MY.
751 * Do *not* translate it to anything else, if it
752 * it isn't calendar:YM or calendar:MY it will not work.
754 * Note that this flipping is in top of the text direction flipping,
755 * so if you have a default text direction of RTL and YM, then
756 * the year will appear on the right.
758 year_before = _("calendar:MY");
759 if (strcmp (year_before, "calendar:YM") == 0)
760 priv->year_before = 1;
761 else if (strcmp (year_before, "calendar:MY") != 0)
762 g_warning ("Whoever translated calendar:MY did so wrongly.\n");
765 priv->week_start = 0;
768 if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
769 wbuffer, G_N_ELEMENTS (wbuffer)))
770 week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
772 if (week_start != NULL)
774 priv->week_start = (week_start[0] - '0' + 1) % 7;
778 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
779 langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
780 first_weekday = langinfo.string[0];
781 langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
782 week_origin = langinfo.word;
783 if (week_origin == 19971130) /* Sunday */
785 else if (week_origin == 19971201) /* Monday */
788 g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
790 priv->week_start = (week_1stday + first_weekday - 1) % 7;
792 /* Translate to calendar:week_start:0 if you want Sunday to be the
793 * first day of the week to calendar:week_start:1 if you want Monday
794 * to be the first day of the week, and so on.
796 week_start = _("calendar:week_start:0");
798 if (strncmp (week_start, "calendar:week_start:", 20) == 0)
799 priv->week_start = *(week_start + 20) - '0';
801 priv->week_start = -1;
803 if (priv->week_start < 0 || priv->week_start > 6)
805 g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
806 priv->week_start = 0;
811 calendar_compute_days (calendar);
815 /****************************************
816 * Utility Functions *
817 ****************************************/
820 calendar_queue_refresh (GtkCalendar *calendar)
822 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
824 if (!(priv->detail_func) ||
825 !(calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS) ||
826 (priv->detail_width_chars && priv->detail_height_rows))
827 gtk_widget_queue_draw (GTK_WIDGET (calendar));
829 gtk_widget_queue_resize (GTK_WIDGET (calendar));
833 calendar_set_month_next (GtkCalendar *calendar)
837 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
841 if (calendar->month == 11)
849 calendar_compute_days (calendar);
850 g_signal_emit (calendar,
851 gtk_calendar_signals[NEXT_MONTH_SIGNAL],
853 g_signal_emit (calendar,
854 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
857 month_len = month_length[leap (calendar->year)][calendar->month + 1];
859 if (month_len < calendar->selected_day)
861 calendar->selected_day = 0;
862 gtk_calendar_select_day (calendar, month_len);
865 gtk_calendar_select_day (calendar, calendar->selected_day);
867 calendar_queue_refresh (calendar);
871 calendar_set_year_prev (GtkCalendar *calendar)
876 calendar_compute_days (calendar);
877 g_signal_emit (calendar,
878 gtk_calendar_signals[PREV_YEAR_SIGNAL],
880 g_signal_emit (calendar,
881 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
884 month_len = month_length[leap (calendar->year)][calendar->month + 1];
886 if (month_len < calendar->selected_day)
888 calendar->selected_day = 0;
889 gtk_calendar_select_day (calendar, month_len);
892 gtk_calendar_select_day (calendar, calendar->selected_day);
894 calendar_queue_refresh (calendar);
898 calendar_set_year_next (GtkCalendar *calendar)
903 calendar_compute_days (calendar);
904 g_signal_emit (calendar,
905 gtk_calendar_signals[NEXT_YEAR_SIGNAL],
907 g_signal_emit (calendar,
908 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
911 month_len = month_length[leap (calendar->year)][calendar->month + 1];
913 if (month_len < calendar->selected_day)
915 calendar->selected_day = 0;
916 gtk_calendar_select_day (calendar, month_len);
919 gtk_calendar_select_day (calendar, calendar->selected_day);
921 calendar_queue_refresh (calendar);
925 calendar_compute_days (GtkCalendar *calendar)
927 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
931 gint ndays_in_prev_month;
937 year = calendar->year;
938 month = calendar->month + 1;
940 ndays_in_month = month_length[leap (year)][month];
942 first_day = day_of_week (year, month, 1);
943 first_day = (first_day + 7 - priv->week_start) % 7;
945 /* Compute days of previous month */
947 ndays_in_prev_month = month_length[leap (year)][month-1];
949 ndays_in_prev_month = month_length[leap (year)][12];
950 day = ndays_in_prev_month - first_day + 1;
955 for (col = 0; col < first_day; col++)
957 calendar->day[row][col] = day;
958 calendar->day_month[row][col] = MONTH_PREV;
963 /* Compute days of current month */
965 for (day = 1; day <= ndays_in_month; day++)
967 calendar->day[row][col] = day;
968 calendar->day_month[row][col] = MONTH_CURRENT;
978 /* Compute days of next month */
980 for (; row <= 5; row++)
982 for (; col <= 6; col++)
984 calendar->day[row][col] = day;
985 calendar->day_month[row][col] = MONTH_NEXT;
993 calendar_select_and_focus_day (GtkCalendar *calendar,
996 gint old_focus_row = calendar->focus_row;
997 gint old_focus_col = calendar->focus_col;
1001 for (row = 0; row < 6; row ++)
1002 for (col = 0; col < 7; col++)
1004 if (calendar->day_month[row][col] == MONTH_CURRENT
1005 && calendar->day[row][col] == day)
1007 calendar->focus_row = row;
1008 calendar->focus_col = col;
1012 if (old_focus_row != -1 && old_focus_col != -1)
1013 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
1015 gtk_calendar_select_day (calendar, day);
1019 /****************************************
1020 * Layout computation utilities *
1021 ****************************************/
1024 calendar_row_height (GtkCalendar *calendar)
1026 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
1027 - ((calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1028 ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
1032 /* calendar_left_x_for_column: returns the x coordinate
1033 * for the left of the column */
1035 calendar_left_x_for_column (GtkCalendar *calendar,
1041 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1042 column = 6 - column;
1044 width = GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1045 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1046 x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
1048 x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
1053 /* column_from_x: returns the column 0-6 that the
1054 * x pixel of the xwindow is in */
1056 calendar_column_from_x (GtkCalendar *calendar,
1060 gint x_left, x_right;
1064 for (c = 0; c < 7; c++)
1066 x_left = calendar_left_x_for_column (calendar, c);
1067 x_right = x_left + GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1069 if (event_x >= x_left && event_x < x_right)
1079 /* calendar_top_y_for_row: returns the y coordinate
1080 * for the top of the row */
1082 calendar_top_y_for_row (GtkCalendar *calendar,
1086 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h
1087 - (CALENDAR_MARGIN + (6 - row)
1088 * calendar_row_height (calendar)));
1091 /* row_from_y: returns the row 0-5 that the
1092 * y pixel of the xwindow is in */
1094 calendar_row_from_y (GtkCalendar *calendar,
1099 gint y_top, y_bottom;
1101 height = calendar_row_height (calendar);
1104 for (r = 0; r < 6; r++)
1106 y_top = calendar_top_y_for_row (calendar, r);
1107 y_bottom = y_top + height;
1109 if (event_y >= y_top && event_y < y_bottom)
1120 calendar_arrow_rectangle (GtkCalendar *calendar,
1124 GtkWidget *widget = GTK_WIDGET (calendar);
1125 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1128 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1129 year_left = priv->year_before;
1131 year_left = !priv->year_before;
1134 rect->width = priv->arrow_width;
1135 rect->height = priv->header_h - 7;
1139 case ARROW_MONTH_LEFT:
1141 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1142 - (3 + 2*priv->arrow_width
1143 + priv->max_month_width));
1147 case ARROW_MONTH_RIGHT:
1149 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1150 - 3 - priv->arrow_width);
1152 rect->x = (priv->arrow_width
1153 + priv->max_month_width);
1155 case ARROW_YEAR_LEFT:
1159 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1160 - (3 + 2*priv->arrow_width
1161 + priv->max_year_width));
1163 case ARROW_YEAR_RIGHT:
1165 rect->x = (priv->arrow_width
1166 + priv->max_year_width);
1168 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1169 - 3 - priv->arrow_width);
1175 calendar_day_rectangle (GtkCalendar *calendar,
1180 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1182 rect->x = calendar_left_x_for_column (calendar, col);
1183 rect->y = calendar_top_y_for_row (calendar, row);
1184 rect->height = calendar_row_height (calendar);
1185 rect->width = priv->day_width;
1189 calendar_set_month_prev (GtkCalendar *calendar)
1193 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1196 if (calendar->month == 0)
1198 calendar->month = 11;
1204 month_len = month_length[leap (calendar->year)][calendar->month + 1];
1206 calendar_compute_days (calendar);
1208 g_signal_emit (calendar,
1209 gtk_calendar_signals[PREV_MONTH_SIGNAL],
1211 g_signal_emit (calendar,
1212 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1215 if (month_len < calendar->selected_day)
1217 calendar->selected_day = 0;
1218 gtk_calendar_select_day (calendar, month_len);
1222 if (calendar->selected_day < 0)
1223 calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
1224 gtk_calendar_select_day (calendar, calendar->selected_day);
1227 calendar_queue_refresh (calendar);
1231 /****************************************
1232 * Basic object methods *
1233 ****************************************/
1236 gtk_calendar_finalize (GObject *object)
1238 G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize (object);
1242 gtk_calendar_destroy (GtkObject *object)
1244 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1246 calendar_stop_spinning (GTK_CALENDAR (object));
1248 /* Call the destroy function for the extra display callback: */
1249 if (priv->detail_func_destroy && priv->detail_func_user_data)
1251 priv->detail_func_destroy (priv->detail_func_user_data);
1252 priv->detail_func_user_data = NULL;
1253 priv->detail_func_destroy = NULL;
1256 GTK_OBJECT_CLASS (gtk_calendar_parent_class)->destroy (object);
1261 calendar_set_display_option (GtkCalendar *calendar,
1262 GtkCalendarDisplayOptions flag,
1265 GtkCalendarDisplayOptions flags;
1267 flags = calendar->display_flags | flag;
1269 flags = calendar->display_flags & ~flag;
1270 gtk_calendar_set_display_options (calendar, flags);
1274 calendar_get_display_option (GtkCalendar *calendar,
1275 GtkCalendarDisplayOptions flag)
1277 return (calendar->display_flags & flag) != 0;
1281 gtk_calendar_set_property (GObject *object,
1283 const GValue *value,
1286 GtkCalendar *calendar;
1288 calendar = GTK_CALENDAR (object);
1293 gtk_calendar_select_month (calendar,
1295 g_value_get_int (value));
1298 gtk_calendar_select_month (calendar,
1299 g_value_get_int (value),
1303 gtk_calendar_select_day (calendar,
1304 g_value_get_int (value));
1306 case PROP_SHOW_HEADING:
1307 calendar_set_display_option (calendar,
1308 GTK_CALENDAR_SHOW_HEADING,
1309 g_value_get_boolean (value));
1311 case PROP_SHOW_DAY_NAMES:
1312 calendar_set_display_option (calendar,
1313 GTK_CALENDAR_SHOW_DAY_NAMES,
1314 g_value_get_boolean (value));
1316 case PROP_NO_MONTH_CHANGE:
1317 calendar_set_display_option (calendar,
1318 GTK_CALENDAR_NO_MONTH_CHANGE,
1319 g_value_get_boolean (value));
1321 case PROP_SHOW_WEEK_NUMBERS:
1322 calendar_set_display_option (calendar,
1323 GTK_CALENDAR_SHOW_WEEK_NUMBERS,
1324 g_value_get_boolean (value));
1326 case PROP_SHOW_DETAILS:
1327 calendar_set_display_option (calendar,
1328 GTK_CALENDAR_SHOW_DETAILS,
1329 g_value_get_boolean (value));
1331 case PROP_DETAIL_WIDTH_CHARS:
1332 gtk_calendar_set_detail_width_chars (calendar,
1333 g_value_get_int (value));
1335 case PROP_DETAIL_HEIGHT_ROWS:
1336 gtk_calendar_set_detail_height_rows (calendar,
1337 g_value_get_int (value));
1340 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1346 gtk_calendar_get_property (GObject *object,
1351 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1352 GtkCalendar *calendar = GTK_CALENDAR (object);
1357 g_value_set_int (value, calendar->year);
1360 g_value_set_int (value, calendar->month);
1363 g_value_set_int (value, calendar->selected_day);
1365 case PROP_SHOW_HEADING:
1366 g_value_set_boolean (value, calendar_get_display_option (calendar,
1367 GTK_CALENDAR_SHOW_HEADING));
1369 case PROP_SHOW_DAY_NAMES:
1370 g_value_set_boolean (value, calendar_get_display_option (calendar,
1371 GTK_CALENDAR_SHOW_DAY_NAMES));
1373 case PROP_NO_MONTH_CHANGE:
1374 g_value_set_boolean (value, calendar_get_display_option (calendar,
1375 GTK_CALENDAR_NO_MONTH_CHANGE));
1377 case PROP_SHOW_WEEK_NUMBERS:
1378 g_value_set_boolean (value, calendar_get_display_option (calendar,
1379 GTK_CALENDAR_SHOW_WEEK_NUMBERS));
1381 case PROP_SHOW_DETAILS:
1382 g_value_set_boolean (value, calendar_get_display_option (calendar,
1383 GTK_CALENDAR_SHOW_DETAILS));
1385 case PROP_DETAIL_WIDTH_CHARS:
1386 g_value_set_int (value, priv->detail_width_chars);
1388 case PROP_DETAIL_HEIGHT_ROWS:
1389 g_value_set_int (value, priv->detail_height_rows);
1392 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1398 /****************************************
1400 ****************************************/
1403 calendar_realize_arrows (GtkCalendar *calendar)
1405 GtkWidget *widget = GTK_WIDGET (calendar);
1406 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1407 GdkWindowAttr attributes;
1408 gint attributes_mask;
1411 /* Arrow windows ------------------------------------- */
1412 if (! (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1413 && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
1415 attributes.wclass = GDK_INPUT_OUTPUT;
1416 attributes.window_type = GDK_WINDOW_CHILD;
1417 attributes.visual = gtk_widget_get_visual (widget);
1418 attributes.colormap = gtk_widget_get_colormap (widget);
1419 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1420 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1421 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1422 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1423 for (i = 0; i < 4; i++)
1426 calendar_arrow_rectangle (calendar, i, &rect);
1428 attributes.x = rect.x;
1429 attributes.y = rect.y;
1430 attributes.width = rect.width;
1431 attributes.height = rect.height;
1432 priv->arrow_win[i] = gdk_window_new (priv->header_win,
1435 if (GTK_WIDGET_IS_SENSITIVE (widget))
1436 priv->arrow_state[i] = GTK_STATE_NORMAL;
1438 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
1439 gdk_window_set_background (priv->arrow_win[i],
1440 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1441 gdk_window_show (priv->arrow_win[i]);
1442 gdk_window_set_user_data (priv->arrow_win[i], widget);
1447 for (i = 0; i < 4; i++)
1448 priv->arrow_win[i] = NULL;
1453 calendar_realize_header (GtkCalendar *calendar)
1455 GtkWidget *widget = GTK_WIDGET (calendar);
1456 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1457 GdkWindowAttr attributes;
1458 gint attributes_mask;
1460 /* Header window ------------------------------------- */
1461 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1463 attributes.wclass = GDK_INPUT_OUTPUT;
1464 attributes.window_type = GDK_WINDOW_CHILD;
1465 attributes.visual = gtk_widget_get_visual (widget);
1466 attributes.colormap = gtk_widget_get_colormap (widget);
1467 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1468 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1469 attributes.x = widget->style->xthickness;
1470 attributes.y = widget->style->ythickness;
1471 attributes.width = widget->allocation.width - 2 * attributes.x;
1472 attributes.height = priv->header_h;
1473 priv->header_win = gdk_window_new (widget->window,
1474 &attributes, attributes_mask);
1476 gdk_window_set_background (priv->header_win,
1477 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1478 gdk_window_show (priv->header_win);
1479 gdk_window_set_user_data (priv->header_win, widget);
1484 priv->header_win = NULL;
1486 calendar_realize_arrows (calendar);
1490 calendar_realize_day_names (GtkCalendar *calendar)
1492 GtkWidget *widget = GTK_WIDGET (calendar);
1493 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1494 GdkWindowAttr attributes;
1495 gint attributes_mask;
1497 /* Day names window --------------------------------- */
1498 if ( calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1500 attributes.wclass = GDK_INPUT_OUTPUT;
1501 attributes.window_type = GDK_WINDOW_CHILD;
1502 attributes.visual = gtk_widget_get_visual (widget);
1503 attributes.colormap = gtk_widget_get_colormap (widget);
1504 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1505 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1506 attributes.x = (widget->style->xthickness + INNER_BORDER);
1507 attributes.y = priv->header_h + (widget->style->ythickness
1509 attributes.width = (widget->allocation.width
1510 - (widget->style->xthickness + INNER_BORDER)
1512 attributes.height = priv->day_name_h;
1513 priv->day_name_win = gdk_window_new (widget->window,
1516 gdk_window_set_background (priv->day_name_win,
1517 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1518 gdk_window_show (priv->day_name_win);
1519 gdk_window_set_user_data (priv->day_name_win, widget);
1523 priv->day_name_win = NULL;
1528 calendar_realize_week_numbers (GtkCalendar *calendar)
1530 GtkWidget *widget = GTK_WIDGET (calendar);
1531 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1532 GdkWindowAttr attributes;
1533 gint attributes_mask;
1535 /* Week number window -------------------------------- */
1536 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1538 attributes.wclass = GDK_INPUT_OUTPUT;
1539 attributes.window_type = GDK_WINDOW_CHILD;
1540 attributes.visual = gtk_widget_get_visual (widget);
1541 attributes.colormap = gtk_widget_get_colormap (widget);
1542 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1544 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1545 attributes.x = widget->style->xthickness + INNER_BORDER;
1546 attributes.y = (priv->header_h + priv->day_name_h
1547 + (widget->style->ythickness + INNER_BORDER));
1548 attributes.width = priv->week_width;
1549 attributes.height = priv->main_h;
1550 priv->week_win = gdk_window_new (widget->window,
1551 &attributes, attributes_mask);
1552 gdk_window_set_background (priv->week_win,
1553 BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1554 gdk_window_show (priv->week_win);
1555 gdk_window_set_user_data (priv->week_win, widget);
1559 priv->week_win = NULL;
1564 gtk_calendar_realize (GtkWidget *widget)
1566 GtkCalendar *calendar = GTK_CALENDAR (widget);
1567 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1568 GdkWindowAttr attributes;
1569 gint attributes_mask;
1571 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1573 attributes.x = widget->allocation.x;
1574 attributes.y = widget->allocation.y;
1575 attributes.width = widget->allocation.width;
1576 attributes.height = widget->allocation.height;
1577 attributes.wclass = GDK_INPUT_OUTPUT;
1578 attributes.window_type = GDK_WINDOW_CHILD;
1579 attributes.event_mask = (gtk_widget_get_events (widget)
1580 | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1581 attributes.visual = gtk_widget_get_visual (widget);
1582 attributes.colormap = gtk_widget_get_colormap (widget);
1584 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1585 widget->window = gdk_window_new (widget->parent->window,
1586 &attributes, attributes_mask);
1588 widget->style = gtk_style_attach (widget->style, widget->window);
1590 /* Header window ------------------------------------- */
1591 calendar_realize_header (calendar);
1592 /* Day names window --------------------------------- */
1593 calendar_realize_day_names (calendar);
1594 /* Week number window -------------------------------- */
1595 calendar_realize_week_numbers (calendar);
1596 /* Main Window -------------------------------------- */
1597 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1598 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1599 | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1601 attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
1602 attributes.y = (priv->header_h + priv->day_name_h
1603 + (widget->style->ythickness + INNER_BORDER));
1604 attributes.width = (widget->allocation.width - attributes.x
1605 - (widget->style->xthickness + INNER_BORDER));
1606 attributes.height = priv->main_h;
1607 priv->main_win = gdk_window_new (widget->window,
1608 &attributes, attributes_mask);
1609 gdk_window_set_background (priv->main_win,
1610 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1611 gdk_window_show (priv->main_win);
1612 gdk_window_set_user_data (priv->main_win, widget);
1613 gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1614 gdk_window_show (widget->window);
1615 gdk_window_set_user_data (widget->window, widget);
1619 gtk_calendar_unrealize (GtkWidget *widget)
1621 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1624 if (priv->header_win)
1626 for (i = 0; i < 4; i++)
1628 if (priv->arrow_win[i])
1630 gdk_window_set_user_data (priv->arrow_win[i], NULL);
1631 gdk_window_destroy (priv->arrow_win[i]);
1632 priv->arrow_win[i] = NULL;
1635 gdk_window_set_user_data (priv->header_win, NULL);
1636 gdk_window_destroy (priv->header_win);
1637 priv->header_win = NULL;
1642 gdk_window_set_user_data (priv->week_win, NULL);
1643 gdk_window_destroy (priv->week_win);
1644 priv->week_win = NULL;
1649 gdk_window_set_user_data (priv->main_win, NULL);
1650 gdk_window_destroy (priv->main_win);
1651 priv->main_win = NULL;
1653 if (priv->day_name_win)
1655 gdk_window_set_user_data (priv->day_name_win, NULL);
1656 gdk_window_destroy (priv->day_name_win);
1657 priv->day_name_win = NULL;
1660 GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize (widget);
1664 gtk_calendar_get_detail (GtkCalendar *calendar,
1668 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1671 year = calendar->year;
1672 month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
1679 else if (month > 11)
1685 return priv->detail_func (calendar,
1687 calendar->day[row][column],
1688 priv->detail_func_user_data);
1692 gtk_calendar_query_tooltip (GtkWidget *widget,
1695 gboolean keyboard_mode,
1696 GtkTooltip *tooltip)
1698 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1699 GtkCalendar *calendar = GTK_CALENDAR (widget);
1700 gchar *detail = NULL;
1701 GdkRectangle day_rect;
1705 gint x0, y0, row, col;
1707 gdk_window_get_position (priv->main_win, &x0, &y0);
1708 col = calendar_column_from_x (calendar, x - x0);
1709 row = calendar_row_from_y (calendar, y - y0);
1711 if (col != -1 && row != -1 &&
1712 (0 != (priv->detail_overflow[row] & (1 << col)) ||
1713 0 == (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS)))
1715 detail = gtk_calendar_get_detail (calendar, row, col);
1716 calendar_day_rectangle (calendar, row, col, &day_rect);
1725 gtk_tooltip_set_tip_area (tooltip, &day_rect);
1726 gtk_tooltip_set_markup (tooltip, detail);
1733 if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip)
1734 return GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
1740 /****************************************
1741 * Size Request and Allocate *
1742 ****************************************/
1745 gtk_calendar_size_request (GtkWidget *widget,
1746 GtkRequisition *requisition)
1748 GtkCalendar *calendar = GTK_CALENDAR (widget);
1749 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1750 PangoLayout *layout;
1751 PangoRectangle logical_rect;
1755 gint calendar_margin = CALENDAR_MARGIN;
1756 gint header_width, main_width;
1757 gint max_header_height = 0;
1760 gint max_detail_height;
1762 gtk_widget_style_get (GTK_WIDGET (widget),
1763 "focus-line-width", &focus_width,
1764 "focus-padding", &focus_padding,
1767 layout = gtk_widget_create_pango_layout (widget, NULL);
1770 * Calculate the requisition width for the widget.
1775 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1777 priv->max_month_width = 0;
1778 for (i = 0; i < 12; i++)
1780 pango_layout_set_text (layout, default_monthname[i], -1);
1781 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1782 priv->max_month_width = MAX (priv->max_month_width,
1783 logical_rect.width + 8);
1784 max_header_height = MAX (max_header_height, logical_rect.height);
1787 priv->max_year_width = 0;
1788 /* Translators: This is a text measurement template.
1789 * Translate it to the widest year text.
1791 * Don't include the prefix "year measurement template|"
1792 * in the translation.
1794 * If you don't understand this, leave it as "2000"
1796 pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);
1797 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1798 priv->max_year_width = MAX (priv->max_year_width,
1799 logical_rect.width + 8);
1800 max_header_height = MAX (max_header_height, logical_rect.height);
1804 priv->max_month_width = 0;
1805 priv->max_year_width = 0;
1808 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1809 header_width = (priv->max_month_width
1810 + priv->max_year_width
1813 header_width = (priv->max_month_width
1814 + priv->max_year_width
1815 + 4 * priv->arrow_width + 3 * 3);
1817 /* Mainwindow labels width */
1819 priv->max_day_char_width = 0;
1820 priv->max_day_char_ascent = 0;
1821 priv->max_day_char_descent = 0;
1822 priv->min_day_width = 0;
1824 for (i = 0; i < 9; i++)
1827 g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), i * 11);
1828 pango_layout_set_text (layout, buffer, -1);
1829 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1830 priv->min_day_width = MAX (priv->min_day_width,
1831 logical_rect.width);
1833 priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1834 PANGO_ASCENT (logical_rect));
1835 priv->max_day_char_descent = MAX (priv->max_day_char_descent,
1836 PANGO_DESCENT (logical_rect));
1839 priv->max_label_char_ascent = 0;
1840 priv->max_label_char_descent = 0;
1841 if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1842 for (i = 0; i < 7; i++)
1844 pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1845 pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
1847 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1848 priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1849 PANGO_ASCENT (logical_rect));
1850 priv->max_label_char_descent = MAX (priv->max_label_char_descent,
1851 PANGO_DESCENT (logical_rect));
1854 priv->max_week_char_width = 0;
1855 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1856 for (i = 0; i < 9; i++)
1859 g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), i * 11);
1860 pango_layout_set_text (layout, buffer, -1);
1861 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1862 priv->max_week_char_width = MAX (priv->max_week_char_width,
1863 logical_rect.width / 2);
1866 /* Calculate detail extents. Do this as late as possible since
1867 * pango_layout_set_markup is called which alters font settings. */
1868 max_detail_height = 0;
1870 if (priv->detail_func && (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS))
1872 gchar *markup, *tail;
1874 if (priv->detail_width_chars || priv->detail_height_rows)
1876 gint rows = MAX (1, priv->detail_height_rows) - 1;
1877 gsize len = priv->detail_width_chars + rows + 16;
1879 markup = tail = g_alloca (len);
1881 memcpy (tail, "<small>", 7);
1884 memset (tail, 'm', priv->detail_width_chars);
1885 tail += priv->detail_width_chars;
1887 memset (tail, '\n', rows);
1890 memcpy (tail, "</small>", 9);
1893 g_assert (len == (tail - markup));
1895 pango_layout_set_markup (layout, markup, -1);
1896 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1898 if (priv->detail_width_chars)
1899 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1900 if (priv->detail_height_rows)
1901 max_detail_height = MAX (max_detail_height, logical_rect.height);
1904 if (!priv->detail_width_chars || !priv->detail_height_rows)
1905 for (r = 0; r < 6; r++)
1906 for (c = 0; c < 7; c++)
1908 gchar *detail = gtk_calendar_get_detail (calendar, r, c);
1912 markup = g_strconcat ("<small>", detail, "</small>", NULL);
1913 pango_layout_set_markup (layout, markup, -1);
1915 if (priv->detail_width_chars)
1917 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1918 pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
1921 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1923 if (!priv->detail_width_chars)
1924 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1925 if (!priv->detail_height_rows)
1926 max_detail_height = MAX (max_detail_height, logical_rect.height);
1934 /* We add one to max_day_char_width to be able to make the marked day "bold" */
1935 priv->max_day_char_width = priv->min_day_width / 2 + 1;
1937 main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1938 + (priv->max_week_char_width
1939 ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1943 requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1946 * Calculate the requisition height for the widget.
1949 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1951 priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1958 if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1960 priv->day_name_h = (priv->max_label_char_ascent
1961 + priv->max_label_char_descent
1962 + 2 * (focus_padding + focus_width) + calendar_margin);
1963 calendar_margin = CALENDAR_YSEP;
1967 priv->day_name_h = 0;
1970 priv->main_h = (CALENDAR_MARGIN + calendar_margin
1971 + 6 * (priv->max_day_char_ascent
1972 + priv->max_day_char_descent
1974 + 2 * (focus_padding + focus_width))
1977 height = (priv->header_h + priv->day_name_h
1980 requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1982 g_object_unref (layout);
1986 gtk_calendar_size_allocate (GtkWidget *widget,
1987 GtkAllocation *allocation)
1989 GtkCalendar *calendar = GTK_CALENDAR (widget);
1990 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1991 gint xthickness = widget->style->xthickness;
1992 gint ythickness = widget->style->xthickness;
1995 widget->allocation = *allocation;
1997 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1999 priv->day_width = (priv->min_day_width
2000 * ((allocation->width - (xthickness + INNER_BORDER) * 2
2001 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
2002 / (7 * priv->min_day_width + priv->max_week_char_width * 2));
2003 priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
2004 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
2005 - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
2009 priv->day_width = (allocation->width
2010 - (xthickness + INNER_BORDER) * 2
2011 - (CALENDAR_MARGIN * 2)
2012 - (DAY_XSEP * 6))/7;
2013 priv->week_width = 0;
2016 if (GTK_WIDGET_REALIZED (widget))
2018 gdk_window_move_resize (widget->window,
2019 allocation->x, allocation->y,
2020 allocation->width, allocation->height);
2021 if (priv->header_win)
2022 gdk_window_move_resize (priv->header_win,
2023 xthickness, ythickness,
2024 allocation->width - 2 * xthickness, priv->header_h);
2026 for (i = 0 ; i < 4 ; i++)
2028 if (priv->arrow_win[i])
2031 calendar_arrow_rectangle (calendar, i, &rect);
2033 gdk_window_move_resize (priv->arrow_win[i],
2034 rect.x, rect.y, rect.width, rect.height);
2038 if (priv->day_name_win)
2039 gdk_window_move_resize (priv->day_name_win,
2040 xthickness + INNER_BORDER,
2041 priv->header_h + (widget->style->ythickness + INNER_BORDER),
2042 allocation->width - (xthickness + INNER_BORDER) * 2,
2044 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2047 gdk_window_move_resize (priv->week_win,
2048 (xthickness + INNER_BORDER),
2049 priv->header_h + priv->day_name_h
2050 + (widget->style->ythickness + INNER_BORDER),
2053 gdk_window_move_resize (priv->main_win,
2054 priv->week_width + (xthickness + INNER_BORDER),
2055 priv->header_h + priv->day_name_h
2056 + (widget->style->ythickness + INNER_BORDER),
2059 - (xthickness + INNER_BORDER) * 2,
2064 gdk_window_move_resize (priv->main_win,
2065 (xthickness + INNER_BORDER),
2066 priv->header_h + priv->day_name_h
2067 + (widget->style->ythickness + INNER_BORDER),
2070 - (xthickness + INNER_BORDER) * 2,
2073 gdk_window_move_resize (priv->week_win,
2076 - (xthickness + INNER_BORDER),
2077 priv->header_h + priv->day_name_h
2078 + (widget->style->ythickness + INNER_BORDER),
2086 /****************************************
2088 ****************************************/
2091 calendar_paint_header (GtkCalendar *calendar)
2093 GtkWidget *widget = GTK_WIDGET (calendar);
2094 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2099 gint max_month_width;
2100 gint max_year_width;
2101 PangoLayout *layout;
2102 PangoRectangle logical_rect;
2108 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2109 year_left = priv->year_before;
2111 year_left = !priv->year_before;
2113 cr = gdk_cairo_create (priv->header_win);
2115 header_width = widget->allocation.width - 2 * widget->style->xthickness;
2117 max_month_width = priv->max_month_width;
2118 max_year_width = priv->max_year_width;
2120 gtk_paint_shadow (widget->style, priv->header_win,
2121 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2122 NULL, widget, "calendar",
2123 0, 0, header_width, priv->header_h);
2125 tmp_time = 1; /* Jan 1 1970, 00:00:01 UTC */
2126 tm = gmtime (&tmp_time);
2127 tm->tm_year = calendar->year - 1900;
2129 /* Translators: This dictates how the year is displayed in
2130 * gtkcalendar widget. See strftime() manual for the format.
2131 * Use only ASCII in the translation.
2133 * Also look for the msgid "year measurement template|2000".
2134 * Translate that entry to a year with the widest output of this
2137 * Don't include the prefix "calendar year format|" in the
2138 * translation. "%Y" is appropriate for most locales.
2140 strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
2141 str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
2142 layout = gtk_widget_create_pango_layout (widget, str);
2145 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2148 y = (priv->header_h - logical_rect.height) / 2;
2150 /* Draw year and its arrows */
2152 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2154 x = 3 + (max_year_width - logical_rect.width)/2;
2156 x = header_width - (3 + max_year_width
2157 - (max_year_width - logical_rect.width)/2);
2160 x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
2162 x = header_width - (3 + priv->arrow_width + max_year_width
2163 - (max_year_width - logical_rect.width)/2);
2166 gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2167 cairo_move_to (cr, x, y);
2168 pango_cairo_show_layout (cr, layout);
2171 g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
2172 pango_layout_set_text (layout, buffer, -1);
2173 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2175 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2177 x = header_width - (3 + max_month_width
2178 - (max_month_width - logical_rect.width)/2);
2180 x = 3 + (max_month_width - logical_rect.width) / 2;
2183 x = header_width - (3 + priv->arrow_width + max_month_width
2184 - (max_month_width - logical_rect.width)/2);
2186 x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
2188 cairo_move_to (cr, x, y);
2189 pango_cairo_show_layout (cr, layout);
2191 g_object_unref (layout);
2196 calendar_paint_day_names (GtkCalendar *calendar)
2198 GtkWidget *widget = GTK_WIDGET (calendar);
2199 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2203 int day_width, cal_width;
2205 PangoLayout *layout;
2206 PangoRectangle logical_rect;
2210 cr = gdk_cairo_create (priv->day_name_win);
2212 gtk_widget_style_get (GTK_WIDGET (widget),
2213 "focus-line-width", &focus_width,
2214 "focus-padding", &focus_padding,
2217 day_width = priv->day_width;
2218 cal_width = widget->allocation.width;
2219 day_wid_sep = day_width + DAY_XSEP;
2222 * Draw rectangles as inverted background for the labels.
2225 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2226 cairo_rectangle (cr,
2227 CALENDAR_MARGIN, CALENDAR_MARGIN,
2228 cal_width-CALENDAR_MARGIN * 2,
2229 priv->day_name_h - CALENDAR_MARGIN);
2232 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2234 cairo_rectangle (cr,
2236 priv->day_name_h - CALENDAR_YSEP,
2237 priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
2246 layout = gtk_widget_create_pango_layout (widget, NULL);
2248 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2249 for (i = 0; i < 7; i++)
2251 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2255 day = (day + priv->week_start) % 7;
2256 g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
2258 pango_layout_set_text (layout, buffer, -1);
2259 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2263 + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2264 (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2267 + (day_width - logical_rect.width)/2),
2268 CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2269 pango_cairo_show_layout (cr, layout);
2272 g_object_unref (layout);
2277 calendar_paint_week_numbers (GtkCalendar *calendar)
2279 GtkWidget *widget = GTK_WIDGET (calendar);
2280 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2283 guint week = 0, year;
2284 gint row, x_loc, y_loc;
2287 PangoLayout *layout;
2288 PangoRectangle logical_rect;
2292 cr = gdk_cairo_create (priv->week_win);
2294 gtk_widget_style_get (GTK_WIDGET (widget),
2295 "focus-line-width", &focus_width,
2296 "focus-padding", &focus_padding,
2300 * Draw a rectangle as inverted background for the labels.
2303 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2304 if (priv->day_name_win)
2305 cairo_rectangle (cr,
2308 priv->week_width - CALENDAR_MARGIN,
2309 priv->main_h - CALENDAR_MARGIN);
2311 cairo_rectangle (cr,
2314 priv->week_width - CALENDAR_MARGIN,
2315 priv->main_h - 2 * CALENDAR_MARGIN);
2322 layout = gtk_widget_create_pango_layout (widget, NULL);
2324 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2325 day_height = calendar_row_height (calendar);
2326 for (row = 0; row < 6; row++)
2330 year = calendar->year;
2331 if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2334 result = week_of_year (&week, &year,
2335 ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2336 + calendar->month) % 12 + 1, calendar->day[row][6]);
2337 g_return_if_fail (result);
2339 /* Translators: this defines whether the week numbers should use
2340 * localized digits or the ones used in English (0123...).
2342 * Translate to "%Id" if you want to use localized digits, or
2343 * translate to "%d" otherwise. Don't include the
2344 * "calendar:week:digits|" part in the translation.
2346 * Note that translating this doesn't guarantee that you get localized
2347 * digits. That needs support from your system and locale definition
2350 g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), week);
2351 pango_layout_set_text (layout, buffer, -1);
2352 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2354 y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2356 x_loc = (priv->week_width
2357 - logical_rect.width
2358 - CALENDAR_XSEP - focus_padding - focus_width);
2360 cairo_move_to (cr, x_loc, y_loc);
2361 pango_cairo_show_layout (cr, layout);
2364 g_object_unref (layout);
2369 calendar_invalidate_day_num (GtkCalendar *calendar,
2372 gint r, c, row, col;
2376 for (r = 0; r < 6; r++)
2377 for (c = 0; c < 7; c++)
2378 if (calendar->day_month[r][c] == MONTH_CURRENT &&
2379 calendar->day[r][c] == day)
2385 g_return_if_fail (row != -1);
2386 g_return_if_fail (col != -1);
2388 calendar_invalidate_day (calendar, row, col);
2392 calendar_invalidate_day (GtkCalendar *calendar,
2396 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2400 GdkRectangle day_rect;
2402 calendar_day_rectangle (calendar, row, col, &day_rect);
2403 gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2408 is_color_attribute (PangoAttribute *attribute,
2411 return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
2412 attribute->klass->type == PANGO_ATTR_BACKGROUND);
2416 calendar_paint_day (GtkCalendar *calendar,
2420 GtkWidget *widget = GTK_WIDGET (calendar);
2421 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2423 GdkColor *text_color;
2428 GdkRectangle day_rect;
2430 PangoLayout *layout;
2431 PangoRectangle logical_rect;
2432 gboolean overflow = FALSE;
2433 gboolean show_details;
2435 g_return_if_fail (row < 6);
2436 g_return_if_fail (col < 7);
2438 cr = gdk_cairo_create (priv->main_win);
2440 day = calendar->day[row][col];
2441 show_details = (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS);
2443 calendar_day_rectangle (calendar, row, col, &day_rect);
2445 if (calendar->day_month[row][col] == MONTH_PREV)
2447 text_color = PREV_MONTH_COLOR (widget);
2449 else if (calendar->day_month[row][col] == MONTH_NEXT)
2451 text_color = NEXT_MONTH_COLOR (widget);
2456 if (calendar->highlight_row == row && calendar->highlight_col == col)
2458 cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2459 gdk_cairo_rectangle (cr, &day_rect);
2463 if (calendar->selected_day == day)
2465 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2466 gdk_cairo_rectangle (cr, &day_rect);
2469 if (calendar->selected_day == day)
2470 text_color = SELECTED_FG_COLOR (widget);
2471 else if (calendar->marked_date[day-1])
2472 text_color = MARKED_COLOR (widget);
2474 text_color = NORMAL_DAY_COLOR (widget);
2477 /* Translators: this defines whether the day numbers should use
2478 * localized digits or the ones used in English (0123...).
2480 * Translate to "%Id" if you want to use localized digits, or
2481 * translate to "%d" otherwise. Don't include the "calendar:day:digits|"
2482 * part in the translation.
2484 * Note that translating this doesn't guarantee that you get localized
2485 * digits. That needs support from your system and locale definition
2488 g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), day);
2490 /* Get extra information to show, if any: */
2492 if (priv->detail_func)
2493 detail = gtk_calendar_get_detail (calendar, row, col);
2497 layout = gtk_widget_create_pango_layout (widget, buffer);
2498 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2499 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2501 x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2504 gdk_cairo_set_source_color (cr, text_color);
2505 cairo_move_to (cr, x_loc, y_loc);
2506 pango_cairo_show_layout (cr, layout);
2508 if (calendar->day_month[row][col] == MONTH_CURRENT &&
2509 (calendar->marked_date[day-1] || (detail && !show_details)))
2511 cairo_move_to (cr, x_loc - 1, y_loc);
2512 pango_cairo_show_layout (cr, layout);
2515 y_loc += priv->max_day_char_descent;
2517 if (priv->detail_func && show_details)
2521 if (calendar->selected_day == day)
2522 gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
2523 else if (calendar->day_month[row][col] == MONTH_CURRENT)
2524 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
2526 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
2528 cairo_set_line_width (cr, 1);
2529 cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
2530 cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
2538 if (detail && show_details)
2540 gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
2541 pango_layout_set_markup (layout, markup, -1);
2544 if (day == calendar->selected_day)
2546 /* Stripping colors as they conflict with selection marking. */
2548 PangoAttrList *attrs = pango_layout_get_attributes (layout);
2549 PangoAttrList *colors = NULL;
2552 colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
2554 pango_attr_list_unref (colors);
2557 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2558 pango_layout_set_width (layout, PANGO_SCALE * day_rect.width);
2560 if (priv->detail_height_rows)
2562 gint dy = day_rect.height - (y_loc - day_rect.y);
2563 pango_layout_set_height (layout, PANGO_SCALE * dy);
2564 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
2567 cairo_move_to (cr, day_rect.x, y_loc);
2568 pango_cairo_show_layout (cr, layout);
2571 if (GTK_WIDGET_HAS_FOCUS (calendar)
2572 && calendar->focus_row == row && calendar->focus_col == col)
2576 if (calendar->selected_day == day)
2577 state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2579 state = GTK_STATE_NORMAL;
2581 gtk_paint_focus (widget->style,
2584 NULL, widget, "calendar-day",
2585 day_rect.x, day_rect.y,
2586 day_rect.width, day_rect.height);
2590 priv->detail_overflow[row] |= (1 << col);
2592 priv->detail_overflow[row] &= ~(1 << col);
2594 g_object_unref (layout);
2600 calendar_paint_main (GtkCalendar *calendar)
2604 for (col = 0; col < 7; col++)
2605 for (row = 0; row < 6; row++)
2606 calendar_paint_day (calendar, row, col);
2610 calendar_invalidate_arrow (GtkCalendar *calendar,
2613 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2616 window = priv->arrow_win[arrow];
2618 gdk_window_invalidate_rect (window, NULL, FALSE);
2622 calendar_paint_arrow (GtkCalendar *calendar,
2625 GtkWidget *widget = GTK_WIDGET (calendar);
2626 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2629 window = priv->arrow_win[arrow];
2632 cairo_t *cr = gdk_cairo_create (window);
2636 state = priv->arrow_state[arrow];
2638 gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2642 gdk_drawable_get_size (window, &width, &height);
2643 if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2644 gtk_paint_arrow (widget->style, window, state,
2645 GTK_SHADOW_OUT, NULL, widget, "calendar",
2646 GTK_ARROW_LEFT, TRUE,
2647 width/2 - 3, height/2 - 4, 8, 8);
2649 gtk_paint_arrow (widget->style, window, state,
2650 GTK_SHADOW_OUT, NULL, widget, "calendar",
2651 GTK_ARROW_RIGHT, TRUE,
2652 width/2 - 4, height/2 - 4, 8, 8);
2657 gtk_calendar_expose (GtkWidget *widget,
2658 GdkEventExpose *event)
2660 GtkCalendar *calendar = GTK_CALENDAR (widget);
2661 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2664 if (GTK_WIDGET_DRAWABLE (widget))
2666 if (event->window == priv->main_win)
2667 calendar_paint_main (calendar);
2669 if (event->window == priv->header_win)
2670 calendar_paint_header (calendar);
2672 for (i = 0; i < 4; i++)
2673 if (event->window == priv->arrow_win[i])
2674 calendar_paint_arrow (calendar, i);
2676 if (event->window == priv->day_name_win)
2677 calendar_paint_day_names (calendar);
2679 if (event->window == priv->week_win)
2680 calendar_paint_week_numbers (calendar);
2681 if (event->window == widget->window)
2683 gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2684 GTK_SHADOW_IN, NULL, widget, "calendar",
2685 0, 0, widget->allocation.width, widget->allocation.height);
2693 /****************************************
2695 ****************************************/
2698 calendar_arrow_action (GtkCalendar *calendar,
2703 case ARROW_YEAR_LEFT:
2704 calendar_set_year_prev (calendar);
2706 case ARROW_YEAR_RIGHT:
2707 calendar_set_year_next (calendar);
2709 case ARROW_MONTH_LEFT:
2710 calendar_set_month_prev (calendar);
2712 case ARROW_MONTH_RIGHT:
2713 calendar_set_month_next (calendar);
2721 calendar_timer (gpointer data)
2723 GtkCalendar *calendar = data;
2724 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2725 gboolean retval = FALSE;
2729 calendar_arrow_action (calendar, priv->click_child);
2731 if (priv->need_timer)
2733 GtkSettings *settings;
2736 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2737 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2739 priv->need_timer = FALSE;
2740 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2741 timeout * SCROLL_DELAY_FACTOR,
2742 (GSourceFunc) calendar_timer,
2743 (gpointer) calendar, NULL);
2753 calendar_start_spinning (GtkCalendar *calendar,
2756 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2758 priv->click_child = click_child;
2762 GtkSettings *settings;
2765 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2766 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2768 priv->need_timer = TRUE;
2769 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2771 (GSourceFunc) calendar_timer,
2772 (gpointer) calendar, NULL);
2777 calendar_stop_spinning (GtkCalendar *calendar)
2779 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2783 g_source_remove (priv->timer);
2785 priv->need_timer = FALSE;
2790 calendar_main_button_press (GtkCalendar *calendar,
2791 GdkEventButton *event)
2793 GtkWidget *widget = GTK_WIDGET (calendar);
2794 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2800 x = (gint) (event->x);
2801 y = (gint) (event->y);
2803 row = calendar_row_from_y (calendar, y);
2804 col = calendar_column_from_x (calendar, x);
2806 /* If row or column isn't found, just return. */
2807 if (row == -1 || col == -1)
2810 day_month = calendar->day_month[row][col];
2812 if (event->type == GDK_BUTTON_PRESS)
2814 day = calendar->day[row][col];
2816 if (day_month == MONTH_PREV)
2817 calendar_set_month_prev (calendar);
2818 else if (day_month == MONTH_NEXT)
2819 calendar_set_month_next (calendar);
2821 if (!GTK_WIDGET_HAS_FOCUS (widget))
2822 gtk_widget_grab_focus (widget);
2824 if (event->button == 1)
2827 priv->drag_start_x = x;
2828 priv->drag_start_y = y;
2831 calendar_select_and_focus_day (calendar, day);
2833 else if (event->type == GDK_2BUTTON_PRESS)
2836 if (day_month == MONTH_CURRENT)
2837 g_signal_emit (calendar,
2838 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2844 gtk_calendar_button_press (GtkWidget *widget,
2845 GdkEventButton *event)
2847 GtkCalendar *calendar = GTK_CALENDAR (widget);
2848 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2851 if (event->window == priv->main_win)
2852 calendar_main_button_press (calendar, event);
2854 if (!GTK_WIDGET_HAS_FOCUS (widget))
2855 gtk_widget_grab_focus (widget);
2857 for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2859 if (event->window == priv->arrow_win[arrow])
2862 /* only call the action on single click, not double */
2863 if (event->type == GDK_BUTTON_PRESS)
2865 if (event->button == 1)
2866 calendar_start_spinning (calendar, arrow);
2868 calendar_arrow_action (calendar, arrow);
2879 gtk_calendar_button_release (GtkWidget *widget,
2880 GdkEventButton *event)
2882 GtkCalendar *calendar = GTK_CALENDAR (widget);
2883 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2885 if (event->button == 1)
2887 calendar_stop_spinning (calendar);
2897 gtk_calendar_motion_notify (GtkWidget *widget,
2898 GdkEventMotion *event)
2900 GtkCalendar *calendar = GTK_CALENDAR (widget);
2901 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2902 gint event_x, event_y;
2904 gint old_row, old_col;
2906 event_x = (gint) (event->x);
2907 event_y = (gint) (event->y);
2909 if (event->window == priv->main_win)
2914 if (gtk_drag_check_threshold (widget,
2915 priv->drag_start_x, priv->drag_start_y,
2916 event->x, event->y))
2918 GdkDragContext *context;
2919 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2920 gtk_target_list_add_text_targets (target_list, 0);
2921 context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
2922 1, (GdkEvent *)event);
2927 gtk_target_list_unref (target_list);
2928 gtk_drag_set_icon_default (context);
2933 row = calendar_row_from_y (calendar, event_y);
2934 col = calendar_column_from_x (calendar, event_x);
2936 if (row != calendar->highlight_row || calendar->highlight_col != col)
2938 old_row = calendar->highlight_row;
2939 old_col = calendar->highlight_col;
2940 if (old_row > -1 && old_col > -1)
2942 calendar->highlight_row = -1;
2943 calendar->highlight_col = -1;
2944 calendar_invalidate_day (calendar, old_row, old_col);
2947 calendar->highlight_row = row;
2948 calendar->highlight_col = col;
2950 if (row > -1 && col > -1)
2951 calendar_invalidate_day (calendar, row, col);
2959 gtk_calendar_enter_notify (GtkWidget *widget,
2960 GdkEventCrossing *event)
2962 GtkCalendar *calendar = GTK_CALENDAR (widget);
2963 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2965 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2967 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2968 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2971 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2973 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2974 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2977 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2979 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2980 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2983 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2985 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2986 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2993 gtk_calendar_leave_notify (GtkWidget *widget,
2994 GdkEventCrossing *event)
2996 GtkCalendar *calendar = GTK_CALENDAR (widget);
2997 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3001 if (event->window == priv->main_win)
3003 row = calendar->highlight_row;
3004 col = calendar->highlight_col;
3005 calendar->highlight_row = -1;
3006 calendar->highlight_col = -1;
3007 if (row > -1 && col > -1)
3008 calendar_invalidate_day (calendar, row, col);
3011 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3013 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3014 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3017 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3019 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3020 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3023 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3025 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3026 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3029 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3031 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3032 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3039 gtk_calendar_scroll (GtkWidget *widget,
3040 GdkEventScroll *event)
3042 GtkCalendar *calendar = GTK_CALENDAR (widget);
3044 if (event->direction == GDK_SCROLL_UP)
3046 if (!GTK_WIDGET_HAS_FOCUS (widget))
3047 gtk_widget_grab_focus (widget);
3048 calendar_set_month_prev (calendar);
3050 else if (event->direction == GDK_SCROLL_DOWN)
3052 if (!GTK_WIDGET_HAS_FOCUS (widget))
3053 gtk_widget_grab_focus (widget);
3054 calendar_set_month_next (calendar);
3063 /****************************************
3065 ****************************************/
3068 move_focus (GtkCalendar *calendar,
3071 GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3073 if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3074 (text_dir == GTK_TEXT_DIR_RTL && direction == 1))
3076 if (calendar->focus_col > 0)
3077 calendar->focus_col--;
3078 else if (calendar->focus_row > 0)
3080 calendar->focus_col = 6;
3081 calendar->focus_row--;
3084 if (calendar->focus_col < 0)
3085 calendar->focus_col = 6;
3086 if (calendar->focus_row < 0)
3087 calendar->focus_row = 5;
3091 if (calendar->focus_col < 6)
3092 calendar->focus_col++;
3093 else if (calendar->focus_row < 5)
3095 calendar->focus_col = 0;
3096 calendar->focus_row++;
3099 if (calendar->focus_col < 0)
3100 calendar->focus_col = 0;
3101 if (calendar->focus_row < 0)
3102 calendar->focus_row = 0;
3107 gtk_calendar_key_press (GtkWidget *widget,
3110 GtkCalendar *calendar;
3116 calendar = GTK_CALENDAR (widget);
3119 old_focus_row = calendar->focus_row;
3120 old_focus_col = calendar->focus_col;
3122 switch (event->keyval)
3127 if (event->state & GDK_CONTROL_MASK)
3128 calendar_set_month_prev (calendar);
3131 move_focus (calendar, -1);
3132 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3133 calendar_invalidate_day (calendar, calendar->focus_row,
3134 calendar->focus_col);
3140 if (event->state & GDK_CONTROL_MASK)
3141 calendar_set_month_next (calendar);
3144 move_focus (calendar, 1);
3145 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3146 calendar_invalidate_day (calendar, calendar->focus_row,
3147 calendar->focus_col);
3153 if (event->state & GDK_CONTROL_MASK)
3154 calendar_set_year_prev (calendar);
3157 if (calendar->focus_row > 0)
3158 calendar->focus_row--;
3159 if (calendar->focus_row < 0)
3160 calendar->focus_row = 5;
3161 if (calendar->focus_col < 0)
3162 calendar->focus_col = 6;
3163 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3164 calendar_invalidate_day (calendar, calendar->focus_row,
3165 calendar->focus_col);
3171 if (event->state & GDK_CONTROL_MASK)
3172 calendar_set_year_next (calendar);
3175 if (calendar->focus_row < 5)
3176 calendar->focus_row++;
3177 if (calendar->focus_col < 0)
3178 calendar->focus_col = 0;
3179 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3180 calendar_invalidate_day (calendar, calendar->focus_row,
3181 calendar->focus_col);
3186 row = calendar->focus_row;
3187 col = calendar->focus_col;
3189 if (row > -1 && col > -1)
3193 day = calendar->day[row][col];
3194 if (calendar->day_month[row][col] == MONTH_PREV)
3195 calendar_set_month_prev (calendar);
3196 else if (calendar->day_month[row][col] == MONTH_NEXT)
3197 calendar_set_month_next (calendar);
3199 calendar_select_and_focus_day (calendar, day);
3207 /****************************************
3208 * Misc widget methods *
3209 ****************************************/
3212 calendar_set_background (GtkWidget *widget)
3214 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3217 if (GTK_WIDGET_REALIZED (widget))
3219 for (i = 0; i < 4; i++)
3221 if (priv->arrow_win[i])
3222 gdk_window_set_background (priv->arrow_win[i],
3223 HEADER_BG_COLOR (widget));
3225 if (priv->header_win)
3226 gdk_window_set_background (priv->header_win,
3227 HEADER_BG_COLOR (widget));
3228 if (priv->day_name_win)
3229 gdk_window_set_background (priv->day_name_win,
3230 BACKGROUND_COLOR (widget));
3232 gdk_window_set_background (priv->week_win,
3233 BACKGROUND_COLOR (widget));
3235 gdk_window_set_background (priv->main_win,
3236 BACKGROUND_COLOR (widget));
3238 gdk_window_set_background (widget->window,
3239 BACKGROUND_COLOR (widget));
3244 gtk_calendar_style_set (GtkWidget *widget,
3245 GtkStyle *previous_style)
3247 if (previous_style && GTK_WIDGET_REALIZED (widget))
3248 calendar_set_background (widget);
3252 gtk_calendar_state_changed (GtkWidget *widget,
3253 GtkStateType previous_state)
3255 GtkCalendar *calendar = GTK_CALENDAR (widget);
3256 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3259 if (!GTK_WIDGET_IS_SENSITIVE (widget))
3262 calendar_stop_spinning (calendar);
3265 for (i = 0; i < 4; i++)
3266 if (GTK_WIDGET_IS_SENSITIVE (widget))
3267 priv->arrow_state[i] = GTK_STATE_NORMAL;
3269 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
3271 calendar_set_background (widget);
3275 gtk_calendar_grab_notify (GtkWidget *widget,
3276 gboolean was_grabbed)
3279 calendar_stop_spinning (GTK_CALENDAR (widget));
3283 gtk_calendar_focus_out (GtkWidget *widget,
3284 GdkEventFocus *event)
3286 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3287 GtkCalendar *calendar = GTK_CALENDAR (widget);
3289 calendar_queue_refresh (calendar);
3290 calendar_stop_spinning (calendar);
3298 /****************************************
3300 ****************************************/
3303 gtk_calendar_drag_data_get (GtkWidget *widget,
3304 GdkDragContext *context,
3305 GtkSelectionData *selection_data,
3309 GtkCalendar *calendar = GTK_CALENDAR (widget);
3314 date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
3315 len = g_date_strftime (str, 127, "%x", date);
3316 gtk_selection_data_set_text (selection_data, str, len);
3321 /* Get/set whether drag_motion requested the drag data and
3322 * drag_data_received should thus not actually insert the data,
3323 * since the data doesn't result from a drop.
3326 set_status_pending (GdkDragContext *context,
3327 GdkDragAction suggested_action)
3329 g_object_set_data (G_OBJECT (context),
3330 I_("gtk-calendar-status-pending"),
3331 GINT_TO_POINTER (suggested_action));
3334 static GdkDragAction
3335 get_status_pending (GdkDragContext *context)
3337 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
3338 "gtk-calendar-status-pending"));
3342 gtk_calendar_drag_leave (GtkWidget *widget,
3343 GdkDragContext *context,
3346 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3348 priv->drag_highlight = 0;
3349 gtk_drag_unhighlight (widget);
3354 gtk_calendar_drag_motion (GtkWidget *widget,
3355 GdkDragContext *context,
3360 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3363 if (!priv->drag_highlight)
3365 priv->drag_highlight = 1;
3366 gtk_drag_highlight (widget);
3369 target = gtk_drag_dest_find_target (widget, context, NULL);
3370 if (target == GDK_NONE || context->suggested_action == 0)
3371 gdk_drag_status (context, 0, time);
3374 set_status_pending (context, context->suggested_action);
3375 gtk_drag_get_data (widget, context, target, time);
3382 gtk_calendar_drag_drop (GtkWidget *widget,
3383 GdkDragContext *context,
3390 target = gtk_drag_dest_find_target (widget, context, NULL);
3391 if (target != GDK_NONE)
3393 gtk_drag_get_data (widget, context,
3403 gtk_calendar_drag_data_received (GtkWidget *widget,
3404 GdkDragContext *context,
3407 GtkSelectionData *selection_data,
3411 GtkCalendar *calendar = GTK_CALENDAR (widget);
3412 guint day, month, year;
3415 GdkDragAction suggested_action;
3417 suggested_action = get_status_pending (context);
3419 if (suggested_action)
3421 set_status_pending (context, 0);
3423 /* We are getting this data due to a request in drag_motion,
3424 * rather than due to a request in drag_drop, so we are just
3425 * supposed to call drag_status, not actually paste in the
3428 str = (gchar*) gtk_selection_data_get_text (selection_data);
3432 date = g_date_new ();
3433 g_date_set_parse (date, str);
3434 if (!g_date_valid (date))
3435 suggested_action = 0;
3440 suggested_action = 0;
3442 gdk_drag_status (context, suggested_action, time);
3447 date = g_date_new ();
3448 str = (gchar*) gtk_selection_data_get_text (selection_data);
3451 g_date_set_parse (date, str);
3455 if (!g_date_valid (date))
3457 g_warning ("Received invalid date data\n");
3459 gtk_drag_finish (context, FALSE, FALSE, time);
3463 day = g_date_get_day (date);
3464 month = g_date_get_month (date);
3465 year = g_date_get_year (date);
3468 gtk_drag_finish (context, TRUE, FALSE, time);
3471 g_object_freeze_notify (G_OBJECT (calendar));
3472 if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3473 && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
3474 gtk_calendar_select_month (calendar, month - 1, year);
3475 gtk_calendar_select_day (calendar, day);
3476 g_object_thaw_notify (G_OBJECT (calendar));
3480 /****************************************
3482 ****************************************/
3487 * Creates a new calendar, with the current date being selected.
3489 * Return value: a newly #GtkCalendar widget
3492 gtk_calendar_new (void)
3494 return g_object_new (GTK_TYPE_CALENDAR, NULL);
3498 * gtk_calendar_display_options:
3499 * @calendar: a #GtkCalendar.
3500 * @flags: the display options to set.
3502 * Sets display options (whether to display the heading and the month headings).
3504 * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
3507 gtk_calendar_display_options (GtkCalendar *calendar,
3508 GtkCalendarDisplayOptions flags)
3510 gtk_calendar_set_display_options (calendar, flags);
3514 * gtk_calendar_get_display_options:
3515 * @calendar: a #GtkCalendar
3517 * Returns the current display options of @calendar.
3519 * Return value: the display options.
3523 GtkCalendarDisplayOptions
3524 gtk_calendar_get_display_options (GtkCalendar *calendar)
3526 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3528 return calendar->display_flags;
3532 * gtk_calendar_set_display_options:
3533 * @calendar: a #GtkCalendar
3534 * @flags: the display options to set
3536 * Sets display options (whether to display the heading and the month
3542 gtk_calendar_set_display_options (GtkCalendar *calendar,
3543 GtkCalendarDisplayOptions flags)
3545 GtkWidget *widget = GTK_WIDGET (calendar);
3546 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3549 GtkCalendarDisplayOptions old_flags;
3551 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3553 old_flags = calendar->display_flags;
3555 if (GTK_WIDGET_REALIZED (widget))
3557 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3560 if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3561 && (priv->header_win))
3563 calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3564 calendar_realize_arrows (calendar);
3568 for (i = 0; i < 4; i++)
3570 if (priv->arrow_win[i])
3572 gdk_window_set_user_data (priv->arrow_win[i],
3574 gdk_window_destroy (priv->arrow_win[i]);
3575 priv->arrow_win[i] = NULL;
3581 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3585 if (flags & GTK_CALENDAR_SHOW_HEADING)
3587 calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3588 calendar_realize_header (calendar);
3592 for (i = 0; i < 4; i++)
3594 if (priv->arrow_win[i])
3596 gdk_window_set_user_data (priv->arrow_win[i],
3598 gdk_window_destroy (priv->arrow_win[i]);
3599 priv->arrow_win[i] = NULL;
3602 gdk_window_set_user_data (priv->header_win, NULL);
3603 gdk_window_destroy (priv->header_win);
3604 priv->header_win = NULL;
3609 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3613 if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3615 calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3616 calendar_realize_day_names (calendar);
3620 gdk_window_set_user_data (priv->day_name_win, NULL);
3621 gdk_window_destroy (priv->day_name_win);
3622 priv->day_name_win = NULL;
3626 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3630 if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3632 calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3633 calendar_realize_week_numbers (calendar);
3637 gdk_window_set_user_data (priv->week_win, NULL);
3638 gdk_window_destroy (priv->week_win);
3639 priv->week_win = NULL;
3643 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3644 g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3646 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DETAILS)
3649 calendar->display_flags = flags;
3651 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3655 calendar->display_flags = flags;
3657 g_object_freeze_notify (G_OBJECT (calendar));
3658 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3659 g_object_notify (G_OBJECT (calendar), "show-heading");
3660 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3661 g_object_notify (G_OBJECT (calendar), "show-day-names");
3662 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3663 g_object_notify (G_OBJECT (calendar), "no-month-change");
3664 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3665 g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3666 g_object_thaw_notify (G_OBJECT (calendar));
3670 * gtk_calendar_select_month:
3671 * @calendar: a #GtkCalendar
3672 * @month: a month number between 0 and 11.
3673 * @year: the year the month is in.
3675 * Shifts the calendar to a different month.
3677 * Return value: %TRUE, always
3680 gtk_calendar_select_month (GtkCalendar *calendar,
3684 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3685 g_return_val_if_fail (month <= 11, FALSE);
3687 calendar->month = month;
3688 calendar->year = year;
3690 calendar_compute_days (calendar);
3691 calendar_queue_refresh (calendar);
3693 g_object_freeze_notify (G_OBJECT (calendar));
3694 g_object_notify (G_OBJECT (calendar), "month");
3695 g_object_notify (G_OBJECT (calendar), "year");
3696 g_object_thaw_notify (G_OBJECT (calendar));
3698 g_signal_emit (calendar,
3699 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3705 * gtk_calendar_select_day:
3706 * @calendar: a #GtkCalendar.
3707 * @day: the day number between 1 and 31, or 0 to unselect
3708 * the currently selected day.
3710 * Selects a day from the current month.
3713 gtk_calendar_select_day (GtkCalendar *calendar,
3716 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3717 g_return_if_fail (day <= 31);
3719 /* Deselect the old day */
3720 if (calendar->selected_day > 0)
3724 selected_day = calendar->selected_day;
3725 calendar->selected_day = 0;
3726 if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3727 calendar_invalidate_day_num (calendar, selected_day);
3730 calendar->selected_day = day;
3732 /* Select the new day */
3735 if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3736 calendar_invalidate_day_num (calendar, day);
3739 g_object_notify (G_OBJECT (calendar), "day");
3741 g_signal_emit (calendar,
3742 gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3747 * gtk_calendar_clear_marks:
3748 * @calendar: a #GtkCalendar
3750 * Remove all visual markers.
3753 gtk_calendar_clear_marks (GtkCalendar *calendar)
3757 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3759 for (day = 0; day < 31; day++)
3761 calendar->marked_date[day] = FALSE;
3764 calendar->num_marked_dates = 0;
3765 calendar_queue_refresh (calendar);
3769 * gtk_calendar_mark_day:
3770 * @calendar: a #GtkCalendar
3771 * @day: the day number to mark between 1 and 31.
3773 * Places a visual marker on a particular day.
3775 * Return value: %TRUE, always
3778 gtk_calendar_mark_day (GtkCalendar *calendar,
3781 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3783 if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3785 calendar->marked_date[day - 1] = TRUE;
3786 calendar->num_marked_dates++;
3787 calendar_invalidate_day_num (calendar, day);
3794 * gtk_calendar_unmark_day:
3795 * @calendar: a #GtkCalendar.
3796 * @day: the day number to unmark between 1 and 31.
3798 * Removes the visual marker from a particular day.
3800 * Return value: %TRUE, always
3803 gtk_calendar_unmark_day (GtkCalendar *calendar,
3806 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3808 if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3810 calendar->marked_date[day - 1] = FALSE;
3811 calendar->num_marked_dates--;
3812 calendar_invalidate_day_num (calendar, day);
3819 * gtk_calendar_get_date:
3820 * @calendar: a #GtkCalendar
3821 * @year: location to store the year number, or %NULL
3822 * @month: location to store the month number (between 0 and 11), or %NULL
3823 * @day: location to store the day number (between 1 and 31), or %NULL
3825 * Obtains the selected date from a #GtkCalendar.
3828 gtk_calendar_get_date (GtkCalendar *calendar,
3833 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3836 *year = calendar->year;
3839 *month = calendar->month;
3842 *day = calendar->selected_day;
3846 * gtk_calendar_set_detail_func:
3847 * @calendar: a #GtkCalendar.
3848 * @func: a function providing details for each day.
3849 * @data: data to pass to @func invokations.
3850 * @destroy: a function for releasing @data.
3852 * Installs a function which provides Pango markup with detail information
3853 * for each day. Examples for such details are holidays or appointments. That
3854 * information is shown below each day when #GtkCalendar:show-details is set.
3855 * A tooltip containing with full detail information is provided, if the entire
3856 * text should not fit into the details area, or if #GtkCalendar:show-details
3859 * The size of the details area can be restricted by setting the
3860 * #GtkCalendar:detail-width-chars and #GtkCalendar:detail-height-rows
3866 gtk_calendar_set_detail_func (GtkCalendar *calendar,
3867 GtkCalendarDetailFunc func,
3869 GDestroyNotify destroy)
3871 GtkCalendarPrivate *priv;
3873 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3875 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3877 if (priv->detail_func_destroy)
3878 priv->detail_func_destroy (priv->detail_func_user_data);
3880 priv->detail_func = func;
3881 priv->detail_func_user_data = data;
3882 priv->detail_func_destroy = destroy;
3884 gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
3885 NULL != priv->detail_func);
3886 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3890 * gtk_calendar_set_detail_width_chars:
3891 * @calendar: a #GtkCalendar.
3892 * @chars: detail width in characters.
3894 * Updates the width of detail cells.
3895 * See #GtkCalendar:detail-width-chars.
3900 gtk_calendar_set_detail_width_chars (GtkCalendar *calendar,
3903 GtkCalendarPrivate *priv;
3905 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3907 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3909 if (chars != priv->detail_width_chars)
3911 priv->detail_width_chars = chars;
3912 g_object_notify (G_OBJECT (calendar), "detail-width-chars");
3913 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3918 * gtk_calendar_set_detail_height_rows:
3919 * @calendar: a #GtkCalendar.
3920 * @rows: detail height in rows.
3922 * Updates the height of detail cells.
3923 * See #GtkCalendar:detail-height-rows.
3928 gtk_calendar_set_detail_height_rows (GtkCalendar *calendar,
3931 GtkCalendarPrivate *priv;
3933 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3935 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3937 if (rows != priv->detail_height_rows)
3939 priv->detail_height_rows = rows;
3940 g_object_notify (G_OBJECT (calendar), "detail-height-rows");
3941 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3946 * gtk_calendar_get_detail_width_chars:
3947 * @calendar: a #GtkCalendar.
3949 * Queries the width of detail cells, in characters.
3950 * See #GtkCalendar:detail-width-chars.
3954 * Return value: The width of detail cells, in characters.
3957 gtk_calendar_get_detail_width_chars (GtkCalendar *calendar)
3959 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3960 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
3964 * gtk_calendar_get_detail_height_rows:
3965 * @calendar: a #GtkCalendar.
3967 * Queries the height of detail cells, in rows.
3968 * See #GtkCalendar:detail-width-chars.
3972 * Return value: The height of detail cells, in rows.
3975 gtk_calendar_get_detail_height_rows (GtkCalendar *calendar)
3977 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3978 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
3982 * gtk_calendar_freeze:
3983 * @calendar: a #GtkCalendar
3985 * Does nothing. Previously locked the display of the calendar until
3986 * it was thawed with gtk_calendar_thaw().
3991 gtk_calendar_freeze (GtkCalendar *calendar)
3993 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3997 * gtk_calendar_thaw:
3998 * @calendar: a #GtkCalendar
4000 * Does nothing. Previously defrosted a calendar; all the changes made
4001 * since the last gtk_calendar_freeze() were displayed.
4006 gtk_calendar_thaw (GtkCalendar *calendar)
4008 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4011 #define __GTK_CALENDAR_C__
4012 #include "gtkaliasdef.c"