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_can_focus (widget, TRUE);
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 the ordering described here is logical order, which is
755 * further influenced by BIDI ordering. Thus, if you have a default
756 * text direction of RTL and specify "calendar:YM", then the year
757 * will appear to the right of the month.
759 year_before = _("calendar:MY");
760 if (strcmp (year_before, "calendar:YM") == 0)
761 priv->year_before = 1;
762 else if (strcmp (year_before, "calendar:MY") != 0)
763 g_warning ("Whoever translated calendar:MY did so wrongly.\n");
766 priv->week_start = 0;
769 if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
770 wbuffer, G_N_ELEMENTS (wbuffer)))
771 week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
773 if (week_start != NULL)
775 priv->week_start = (week_start[0] - '0' + 1) % 7;
779 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
780 langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
781 first_weekday = langinfo.string[0];
782 langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
783 week_origin = langinfo.word;
784 if (week_origin == 19971130) /* Sunday */
786 else if (week_origin == 19971201) /* Monday */
789 g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
791 priv->week_start = (week_1stday + first_weekday - 1) % 7;
793 /* Translate to calendar:week_start:0 if you want Sunday to be the
794 * first day of the week to calendar:week_start:1 if you want Monday
795 * to be the first day of the week, and so on.
797 week_start = _("calendar:week_start:0");
799 if (strncmp (week_start, "calendar:week_start:", 20) == 0)
800 priv->week_start = *(week_start + 20) - '0';
802 priv->week_start = -1;
804 if (priv->week_start < 0 || priv->week_start > 6)
806 g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
807 priv->week_start = 0;
812 calendar_compute_days (calendar);
816 /****************************************
817 * Utility Functions *
818 ****************************************/
821 calendar_queue_refresh (GtkCalendar *calendar)
823 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
825 if (!(priv->detail_func) ||
826 !(calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS) ||
827 (priv->detail_width_chars && priv->detail_height_rows))
828 gtk_widget_queue_draw (GTK_WIDGET (calendar));
830 gtk_widget_queue_resize (GTK_WIDGET (calendar));
834 calendar_set_month_next (GtkCalendar *calendar)
838 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
842 if (calendar->month == 11)
850 calendar_compute_days (calendar);
851 g_signal_emit (calendar,
852 gtk_calendar_signals[NEXT_MONTH_SIGNAL],
854 g_signal_emit (calendar,
855 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
858 month_len = month_length[leap (calendar->year)][calendar->month + 1];
860 if (month_len < calendar->selected_day)
862 calendar->selected_day = 0;
863 gtk_calendar_select_day (calendar, month_len);
866 gtk_calendar_select_day (calendar, calendar->selected_day);
868 calendar_queue_refresh (calendar);
872 calendar_set_year_prev (GtkCalendar *calendar)
877 calendar_compute_days (calendar);
878 g_signal_emit (calendar,
879 gtk_calendar_signals[PREV_YEAR_SIGNAL],
881 g_signal_emit (calendar,
882 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
885 month_len = month_length[leap (calendar->year)][calendar->month + 1];
887 if (month_len < calendar->selected_day)
889 calendar->selected_day = 0;
890 gtk_calendar_select_day (calendar, month_len);
893 gtk_calendar_select_day (calendar, calendar->selected_day);
895 calendar_queue_refresh (calendar);
899 calendar_set_year_next (GtkCalendar *calendar)
904 calendar_compute_days (calendar);
905 g_signal_emit (calendar,
906 gtk_calendar_signals[NEXT_YEAR_SIGNAL],
908 g_signal_emit (calendar,
909 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
912 month_len = month_length[leap (calendar->year)][calendar->month + 1];
914 if (month_len < calendar->selected_day)
916 calendar->selected_day = 0;
917 gtk_calendar_select_day (calendar, month_len);
920 gtk_calendar_select_day (calendar, calendar->selected_day);
922 calendar_queue_refresh (calendar);
926 calendar_compute_days (GtkCalendar *calendar)
928 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
932 gint ndays_in_prev_month;
938 year = calendar->year;
939 month = calendar->month + 1;
941 ndays_in_month = month_length[leap (year)][month];
943 first_day = day_of_week (year, month, 1);
944 first_day = (first_day + 7 - priv->week_start) % 7;
946 /* Compute days of previous month */
948 ndays_in_prev_month = month_length[leap (year)][month-1];
950 ndays_in_prev_month = month_length[leap (year)][12];
951 day = ndays_in_prev_month - first_day + 1;
956 for (col = 0; col < first_day; col++)
958 calendar->day[row][col] = day;
959 calendar->day_month[row][col] = MONTH_PREV;
964 /* Compute days of current month */
966 for (day = 1; day <= ndays_in_month; day++)
968 calendar->day[row][col] = day;
969 calendar->day_month[row][col] = MONTH_CURRENT;
979 /* Compute days of next month */
981 for (; row <= 5; row++)
983 for (; col <= 6; col++)
985 calendar->day[row][col] = day;
986 calendar->day_month[row][col] = MONTH_NEXT;
994 calendar_select_and_focus_day (GtkCalendar *calendar,
997 gint old_focus_row = calendar->focus_row;
998 gint old_focus_col = calendar->focus_col;
1002 for (row = 0; row < 6; row ++)
1003 for (col = 0; col < 7; col++)
1005 if (calendar->day_month[row][col] == MONTH_CURRENT
1006 && calendar->day[row][col] == day)
1008 calendar->focus_row = row;
1009 calendar->focus_col = col;
1013 if (old_focus_row != -1 && old_focus_col != -1)
1014 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
1016 gtk_calendar_select_day (calendar, day);
1020 /****************************************
1021 * Layout computation utilities *
1022 ****************************************/
1025 calendar_row_height (GtkCalendar *calendar)
1027 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
1028 - ((calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1029 ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
1033 /* calendar_left_x_for_column: returns the x coordinate
1034 * for the left of the column */
1036 calendar_left_x_for_column (GtkCalendar *calendar,
1042 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1043 column = 6 - column;
1045 width = GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1046 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1047 x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
1049 x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
1054 /* column_from_x: returns the column 0-6 that the
1055 * x pixel of the xwindow is in */
1057 calendar_column_from_x (GtkCalendar *calendar,
1061 gint x_left, x_right;
1065 for (c = 0; c < 7; c++)
1067 x_left = calendar_left_x_for_column (calendar, c);
1068 x_right = x_left + GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1070 if (event_x >= x_left && event_x < x_right)
1080 /* calendar_top_y_for_row: returns the y coordinate
1081 * for the top of the row */
1083 calendar_top_y_for_row (GtkCalendar *calendar,
1087 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h
1088 - (CALENDAR_MARGIN + (6 - row)
1089 * calendar_row_height (calendar)));
1092 /* row_from_y: returns the row 0-5 that the
1093 * y pixel of the xwindow is in */
1095 calendar_row_from_y (GtkCalendar *calendar,
1100 gint y_top, y_bottom;
1102 height = calendar_row_height (calendar);
1105 for (r = 0; r < 6; r++)
1107 y_top = calendar_top_y_for_row (calendar, r);
1108 y_bottom = y_top + height;
1110 if (event_y >= y_top && event_y < y_bottom)
1121 calendar_arrow_rectangle (GtkCalendar *calendar,
1125 GtkWidget *widget = GTK_WIDGET (calendar);
1126 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1129 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1130 year_left = priv->year_before;
1132 year_left = !priv->year_before;
1135 rect->width = priv->arrow_width;
1136 rect->height = priv->header_h - 7;
1140 case ARROW_MONTH_LEFT:
1142 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1143 - (3 + 2*priv->arrow_width
1144 + priv->max_month_width));
1148 case ARROW_MONTH_RIGHT:
1150 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1151 - 3 - priv->arrow_width);
1153 rect->x = (priv->arrow_width
1154 + priv->max_month_width);
1156 case ARROW_YEAR_LEFT:
1160 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1161 - (3 + 2*priv->arrow_width
1162 + priv->max_year_width));
1164 case ARROW_YEAR_RIGHT:
1166 rect->x = (priv->arrow_width
1167 + priv->max_year_width);
1169 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1170 - 3 - priv->arrow_width);
1176 calendar_day_rectangle (GtkCalendar *calendar,
1181 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1183 rect->x = calendar_left_x_for_column (calendar, col);
1184 rect->y = calendar_top_y_for_row (calendar, row);
1185 rect->height = calendar_row_height (calendar);
1186 rect->width = priv->day_width;
1190 calendar_set_month_prev (GtkCalendar *calendar)
1194 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1197 if (calendar->month == 0)
1199 calendar->month = 11;
1205 month_len = month_length[leap (calendar->year)][calendar->month + 1];
1207 calendar_compute_days (calendar);
1209 g_signal_emit (calendar,
1210 gtk_calendar_signals[PREV_MONTH_SIGNAL],
1212 g_signal_emit (calendar,
1213 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1216 if (month_len < calendar->selected_day)
1218 calendar->selected_day = 0;
1219 gtk_calendar_select_day (calendar, month_len);
1223 if (calendar->selected_day < 0)
1224 calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
1225 gtk_calendar_select_day (calendar, calendar->selected_day);
1228 calendar_queue_refresh (calendar);
1232 /****************************************
1233 * Basic object methods *
1234 ****************************************/
1237 gtk_calendar_finalize (GObject *object)
1239 G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize (object);
1243 gtk_calendar_destroy (GtkObject *object)
1245 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1247 calendar_stop_spinning (GTK_CALENDAR (object));
1249 /* Call the destroy function for the extra display callback: */
1250 if (priv->detail_func_destroy && priv->detail_func_user_data)
1252 priv->detail_func_destroy (priv->detail_func_user_data);
1253 priv->detail_func_user_data = NULL;
1254 priv->detail_func_destroy = NULL;
1257 GTK_OBJECT_CLASS (gtk_calendar_parent_class)->destroy (object);
1262 calendar_set_display_option (GtkCalendar *calendar,
1263 GtkCalendarDisplayOptions flag,
1266 GtkCalendarDisplayOptions flags;
1268 flags = calendar->display_flags | flag;
1270 flags = calendar->display_flags & ~flag;
1271 gtk_calendar_set_display_options (calendar, flags);
1275 calendar_get_display_option (GtkCalendar *calendar,
1276 GtkCalendarDisplayOptions flag)
1278 return (calendar->display_flags & flag) != 0;
1282 gtk_calendar_set_property (GObject *object,
1284 const GValue *value,
1287 GtkCalendar *calendar;
1289 calendar = GTK_CALENDAR (object);
1294 gtk_calendar_select_month (calendar,
1296 g_value_get_int (value));
1299 gtk_calendar_select_month (calendar,
1300 g_value_get_int (value),
1304 gtk_calendar_select_day (calendar,
1305 g_value_get_int (value));
1307 case PROP_SHOW_HEADING:
1308 calendar_set_display_option (calendar,
1309 GTK_CALENDAR_SHOW_HEADING,
1310 g_value_get_boolean (value));
1312 case PROP_SHOW_DAY_NAMES:
1313 calendar_set_display_option (calendar,
1314 GTK_CALENDAR_SHOW_DAY_NAMES,
1315 g_value_get_boolean (value));
1317 case PROP_NO_MONTH_CHANGE:
1318 calendar_set_display_option (calendar,
1319 GTK_CALENDAR_NO_MONTH_CHANGE,
1320 g_value_get_boolean (value));
1322 case PROP_SHOW_WEEK_NUMBERS:
1323 calendar_set_display_option (calendar,
1324 GTK_CALENDAR_SHOW_WEEK_NUMBERS,
1325 g_value_get_boolean (value));
1327 case PROP_SHOW_DETAILS:
1328 calendar_set_display_option (calendar,
1329 GTK_CALENDAR_SHOW_DETAILS,
1330 g_value_get_boolean (value));
1332 case PROP_DETAIL_WIDTH_CHARS:
1333 gtk_calendar_set_detail_width_chars (calendar,
1334 g_value_get_int (value));
1336 case PROP_DETAIL_HEIGHT_ROWS:
1337 gtk_calendar_set_detail_height_rows (calendar,
1338 g_value_get_int (value));
1341 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1347 gtk_calendar_get_property (GObject *object,
1352 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1353 GtkCalendar *calendar = GTK_CALENDAR (object);
1358 g_value_set_int (value, calendar->year);
1361 g_value_set_int (value, calendar->month);
1364 g_value_set_int (value, calendar->selected_day);
1366 case PROP_SHOW_HEADING:
1367 g_value_set_boolean (value, calendar_get_display_option (calendar,
1368 GTK_CALENDAR_SHOW_HEADING));
1370 case PROP_SHOW_DAY_NAMES:
1371 g_value_set_boolean (value, calendar_get_display_option (calendar,
1372 GTK_CALENDAR_SHOW_DAY_NAMES));
1374 case PROP_NO_MONTH_CHANGE:
1375 g_value_set_boolean (value, calendar_get_display_option (calendar,
1376 GTK_CALENDAR_NO_MONTH_CHANGE));
1378 case PROP_SHOW_WEEK_NUMBERS:
1379 g_value_set_boolean (value, calendar_get_display_option (calendar,
1380 GTK_CALENDAR_SHOW_WEEK_NUMBERS));
1382 case PROP_SHOW_DETAILS:
1383 g_value_set_boolean (value, calendar_get_display_option (calendar,
1384 GTK_CALENDAR_SHOW_DETAILS));
1386 case PROP_DETAIL_WIDTH_CHARS:
1387 g_value_set_int (value, priv->detail_width_chars);
1389 case PROP_DETAIL_HEIGHT_ROWS:
1390 g_value_set_int (value, priv->detail_height_rows);
1393 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1399 /****************************************
1401 ****************************************/
1404 calendar_realize_arrows (GtkCalendar *calendar)
1406 GtkWidget *widget = GTK_WIDGET (calendar);
1407 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1408 GdkWindowAttr attributes;
1409 gint attributes_mask;
1412 /* Arrow windows ------------------------------------- */
1413 if (! (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1414 && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
1416 attributes.wclass = GDK_INPUT_OUTPUT;
1417 attributes.window_type = GDK_WINDOW_CHILD;
1418 attributes.visual = gtk_widget_get_visual (widget);
1419 attributes.colormap = gtk_widget_get_colormap (widget);
1420 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1421 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1422 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1423 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1424 for (i = 0; i < 4; i++)
1427 calendar_arrow_rectangle (calendar, i, &rect);
1429 attributes.x = rect.x;
1430 attributes.y = rect.y;
1431 attributes.width = rect.width;
1432 attributes.height = rect.height;
1433 priv->arrow_win[i] = gdk_window_new (priv->header_win,
1436 if (gtk_widget_is_sensitive (widget))
1437 priv->arrow_state[i] = GTK_STATE_NORMAL;
1439 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
1440 gdk_window_set_background (priv->arrow_win[i],
1441 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1442 gdk_window_show (priv->arrow_win[i]);
1443 gdk_window_set_user_data (priv->arrow_win[i], widget);
1448 for (i = 0; i < 4; i++)
1449 priv->arrow_win[i] = NULL;
1454 calendar_realize_header (GtkCalendar *calendar)
1456 GtkWidget *widget = GTK_WIDGET (calendar);
1457 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1458 GdkWindowAttr attributes;
1459 gint attributes_mask;
1461 /* Header window ------------------------------------- */
1462 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1464 attributes.wclass = GDK_INPUT_OUTPUT;
1465 attributes.window_type = GDK_WINDOW_CHILD;
1466 attributes.visual = gtk_widget_get_visual (widget);
1467 attributes.colormap = gtk_widget_get_colormap (widget);
1468 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1469 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1470 attributes.x = widget->style->xthickness;
1471 attributes.y = widget->style->ythickness;
1472 attributes.width = widget->allocation.width - 2 * attributes.x;
1473 attributes.height = priv->header_h;
1474 priv->header_win = gdk_window_new (widget->window,
1475 &attributes, attributes_mask);
1477 gdk_window_set_background (priv->header_win,
1478 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1479 gdk_window_show (priv->header_win);
1480 gdk_window_set_user_data (priv->header_win, widget);
1485 priv->header_win = NULL;
1487 calendar_realize_arrows (calendar);
1491 calendar_realize_day_names (GtkCalendar *calendar)
1493 GtkWidget *widget = GTK_WIDGET (calendar);
1494 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1495 GdkWindowAttr attributes;
1496 gint attributes_mask;
1498 /* Day names window --------------------------------- */
1499 if ( calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1501 attributes.wclass = GDK_INPUT_OUTPUT;
1502 attributes.window_type = GDK_WINDOW_CHILD;
1503 attributes.visual = gtk_widget_get_visual (widget);
1504 attributes.colormap = gtk_widget_get_colormap (widget);
1505 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1506 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1507 attributes.x = (widget->style->xthickness + INNER_BORDER);
1508 attributes.y = priv->header_h + (widget->style->ythickness
1510 attributes.width = (widget->allocation.width
1511 - (widget->style->xthickness + INNER_BORDER)
1513 attributes.height = priv->day_name_h;
1514 priv->day_name_win = gdk_window_new (widget->window,
1517 gdk_window_set_background (priv->day_name_win,
1518 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1519 gdk_window_show (priv->day_name_win);
1520 gdk_window_set_user_data (priv->day_name_win, widget);
1524 priv->day_name_win = NULL;
1529 calendar_realize_week_numbers (GtkCalendar *calendar)
1531 GtkWidget *widget = GTK_WIDGET (calendar);
1532 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1533 GdkWindowAttr attributes;
1534 gint attributes_mask;
1536 /* Week number window -------------------------------- */
1537 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1539 attributes.wclass = GDK_INPUT_OUTPUT;
1540 attributes.window_type = GDK_WINDOW_CHILD;
1541 attributes.visual = gtk_widget_get_visual (widget);
1542 attributes.colormap = gtk_widget_get_colormap (widget);
1543 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1545 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1546 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1547 attributes.x = widget->style->xthickness + INNER_BORDER;
1549 attributes.x = widget->allocation.width - priv->week_width - (widget->style->xthickness + INNER_BORDER);
1550 attributes.y = (priv->header_h + priv->day_name_h
1551 + (widget->style->ythickness + INNER_BORDER));
1552 attributes.width = priv->week_width;
1553 attributes.height = priv->main_h;
1554 priv->week_win = gdk_window_new (widget->window,
1555 &attributes, attributes_mask);
1556 gdk_window_set_background (priv->week_win,
1557 BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1558 gdk_window_show (priv->week_win);
1559 gdk_window_set_user_data (priv->week_win, widget);
1563 priv->week_win = NULL;
1568 gtk_calendar_realize (GtkWidget *widget)
1570 GtkCalendar *calendar = GTK_CALENDAR (widget);
1571 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1572 GdkWindowAttr attributes;
1573 gint attributes_mask;
1575 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1577 attributes.x = widget->allocation.x;
1578 attributes.y = widget->allocation.y;
1579 attributes.width = widget->allocation.width;
1580 attributes.height = widget->allocation.height;
1581 attributes.wclass = GDK_INPUT_OUTPUT;
1582 attributes.window_type = GDK_WINDOW_CHILD;
1583 attributes.event_mask = (gtk_widget_get_events (widget)
1584 | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1585 attributes.visual = gtk_widget_get_visual (widget);
1586 attributes.colormap = gtk_widget_get_colormap (widget);
1588 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1589 widget->window = gdk_window_new (widget->parent->window,
1590 &attributes, attributes_mask);
1592 widget->style = gtk_style_attach (widget->style, widget->window);
1594 /* Header window ------------------------------------- */
1595 calendar_realize_header (calendar);
1596 /* Day names window --------------------------------- */
1597 calendar_realize_day_names (calendar);
1598 /* Week number window -------------------------------- */
1599 calendar_realize_week_numbers (calendar);
1600 /* Main Window -------------------------------------- */
1601 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1602 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1603 | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1605 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1606 attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
1608 attributes.x = widget->style->ythickness + INNER_BORDER;
1610 attributes.y = (priv->header_h + priv->day_name_h
1611 + (widget->style->ythickness + INNER_BORDER));
1612 attributes.width = (widget->allocation.width - attributes.x
1613 - (widget->style->xthickness + INNER_BORDER));
1614 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1615 attributes.width -= priv->week_width;
1617 attributes.height = priv->main_h;
1618 priv->main_win = gdk_window_new (widget->window,
1619 &attributes, attributes_mask);
1620 gdk_window_set_background (priv->main_win,
1621 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1622 gdk_window_show (priv->main_win);
1623 gdk_window_set_user_data (priv->main_win, widget);
1624 gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1625 gdk_window_show (widget->window);
1626 gdk_window_set_user_data (widget->window, widget);
1630 gtk_calendar_unrealize (GtkWidget *widget)
1632 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1635 if (priv->header_win)
1637 for (i = 0; i < 4; i++)
1639 if (priv->arrow_win[i])
1641 gdk_window_set_user_data (priv->arrow_win[i], NULL);
1642 gdk_window_destroy (priv->arrow_win[i]);
1643 priv->arrow_win[i] = NULL;
1646 gdk_window_set_user_data (priv->header_win, NULL);
1647 gdk_window_destroy (priv->header_win);
1648 priv->header_win = NULL;
1653 gdk_window_set_user_data (priv->week_win, NULL);
1654 gdk_window_destroy (priv->week_win);
1655 priv->week_win = NULL;
1660 gdk_window_set_user_data (priv->main_win, NULL);
1661 gdk_window_destroy (priv->main_win);
1662 priv->main_win = NULL;
1664 if (priv->day_name_win)
1666 gdk_window_set_user_data (priv->day_name_win, NULL);
1667 gdk_window_destroy (priv->day_name_win);
1668 priv->day_name_win = NULL;
1671 GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize (widget);
1675 gtk_calendar_get_detail (GtkCalendar *calendar,
1679 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1682 if (priv->detail_func == NULL)
1685 year = calendar->year;
1686 month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
1693 else if (month > 11)
1699 return priv->detail_func (calendar,
1701 calendar->day[row][column],
1702 priv->detail_func_user_data);
1706 gtk_calendar_query_tooltip (GtkWidget *widget,
1709 gboolean keyboard_mode,
1710 GtkTooltip *tooltip)
1712 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1713 GtkCalendar *calendar = GTK_CALENDAR (widget);
1714 gchar *detail = NULL;
1715 GdkRectangle day_rect;
1719 gint x0, y0, row, col;
1721 gdk_window_get_position (priv->main_win, &x0, &y0);
1722 col = calendar_column_from_x (calendar, x - x0);
1723 row = calendar_row_from_y (calendar, y - y0);
1725 if (col != -1 && row != -1 &&
1726 (0 != (priv->detail_overflow[row] & (1 << col)) ||
1727 0 == (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS)))
1729 detail = gtk_calendar_get_detail (calendar, row, col);
1730 calendar_day_rectangle (calendar, row, col, &day_rect);
1739 gtk_tooltip_set_tip_area (tooltip, &day_rect);
1740 gtk_tooltip_set_markup (tooltip, detail);
1747 if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip)
1748 return GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
1754 /****************************************
1755 * Size Request and Allocate *
1756 ****************************************/
1759 gtk_calendar_size_request (GtkWidget *widget,
1760 GtkRequisition *requisition)
1762 GtkCalendar *calendar = GTK_CALENDAR (widget);
1763 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1764 PangoLayout *layout;
1765 PangoRectangle logical_rect;
1769 gint calendar_margin = CALENDAR_MARGIN;
1770 gint header_width, main_width;
1771 gint max_header_height = 0;
1774 gint max_detail_height;
1776 gtk_widget_style_get (GTK_WIDGET (widget),
1777 "focus-line-width", &focus_width,
1778 "focus-padding", &focus_padding,
1781 layout = gtk_widget_create_pango_layout (widget, NULL);
1784 * Calculate the requisition width for the widget.
1789 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1791 priv->max_month_width = 0;
1792 for (i = 0; i < 12; i++)
1794 pango_layout_set_text (layout, default_monthname[i], -1);
1795 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1796 priv->max_month_width = MAX (priv->max_month_width,
1797 logical_rect.width + 8);
1798 max_header_height = MAX (max_header_height, logical_rect.height);
1801 priv->max_year_width = 0;
1802 /* Translators: This is a text measurement template.
1803 * Translate it to the widest year text
1805 * If you don't understand this, leave it as "2000"
1807 pango_layout_set_text (layout, C_("year measurement template", "2000"), -1);
1808 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1809 priv->max_year_width = MAX (priv->max_year_width,
1810 logical_rect.width + 8);
1811 max_header_height = MAX (max_header_height, logical_rect.height);
1815 priv->max_month_width = 0;
1816 priv->max_year_width = 0;
1819 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1820 header_width = (priv->max_month_width
1821 + priv->max_year_width
1824 header_width = (priv->max_month_width
1825 + priv->max_year_width
1826 + 4 * priv->arrow_width + 3 * 3);
1828 /* Mainwindow labels width */
1830 priv->max_day_char_width = 0;
1831 priv->max_day_char_ascent = 0;
1832 priv->max_day_char_descent = 0;
1833 priv->min_day_width = 0;
1835 for (i = 0; i < 9; i++)
1838 g_snprintf (buffer, sizeof (buffer), C_("calendar:day:digits", "%d"), i * 11);
1839 pango_layout_set_text (layout, buffer, -1);
1840 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1841 priv->min_day_width = MAX (priv->min_day_width,
1842 logical_rect.width);
1844 priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1845 PANGO_ASCENT (logical_rect));
1846 priv->max_day_char_descent = MAX (priv->max_day_char_descent,
1847 PANGO_DESCENT (logical_rect));
1850 priv->max_label_char_ascent = 0;
1851 priv->max_label_char_descent = 0;
1852 if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1853 for (i = 0; i < 7; i++)
1855 pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1856 pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
1858 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1859 priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1860 PANGO_ASCENT (logical_rect));
1861 priv->max_label_char_descent = MAX (priv->max_label_char_descent,
1862 PANGO_DESCENT (logical_rect));
1865 priv->max_week_char_width = 0;
1866 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1867 for (i = 0; i < 9; i++)
1870 g_snprintf (buffer, sizeof (buffer), C_("calendar:week:digits", "%d"), i * 11);
1871 pango_layout_set_text (layout, buffer, -1);
1872 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1873 priv->max_week_char_width = MAX (priv->max_week_char_width,
1874 logical_rect.width / 2);
1877 /* Calculate detail extents. Do this as late as possible since
1878 * pango_layout_set_markup is called which alters font settings. */
1879 max_detail_height = 0;
1881 if (priv->detail_func && (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS))
1883 gchar *markup, *tail;
1885 if (priv->detail_width_chars || priv->detail_height_rows)
1887 gint rows = MAX (1, priv->detail_height_rows) - 1;
1888 gsize len = priv->detail_width_chars + rows + 16;
1890 markup = tail = g_alloca (len);
1892 memcpy (tail, "<small>", 7);
1895 memset (tail, 'm', priv->detail_width_chars);
1896 tail += priv->detail_width_chars;
1898 memset (tail, '\n', rows);
1901 memcpy (tail, "</small>", 9);
1904 g_assert (len == (tail - markup));
1906 pango_layout_set_markup (layout, markup, -1);
1907 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1909 if (priv->detail_width_chars)
1910 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1911 if (priv->detail_height_rows)
1912 max_detail_height = MAX (max_detail_height, logical_rect.height);
1915 if (!priv->detail_width_chars || !priv->detail_height_rows)
1916 for (r = 0; r < 6; r++)
1917 for (c = 0; c < 7; c++)
1919 gchar *detail = gtk_calendar_get_detail (calendar, r, c);
1923 markup = g_strconcat ("<small>", detail, "</small>", NULL);
1924 pango_layout_set_markup (layout, markup, -1);
1926 if (priv->detail_width_chars)
1928 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1929 pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
1932 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1934 if (!priv->detail_width_chars)
1935 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1936 if (!priv->detail_height_rows)
1937 max_detail_height = MAX (max_detail_height, logical_rect.height);
1945 /* We add one to max_day_char_width to be able to make the marked day "bold" */
1946 priv->max_day_char_width = priv->min_day_width / 2 + 1;
1948 main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1949 + (priv->max_week_char_width
1950 ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1954 requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1957 * Calculate the requisition height for the widget.
1960 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1962 priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1969 if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1971 priv->day_name_h = (priv->max_label_char_ascent
1972 + priv->max_label_char_descent
1973 + 2 * (focus_padding + focus_width) + calendar_margin);
1974 calendar_margin = CALENDAR_YSEP;
1978 priv->day_name_h = 0;
1981 priv->main_h = (CALENDAR_MARGIN + calendar_margin
1982 + 6 * (priv->max_day_char_ascent
1983 + priv->max_day_char_descent
1985 + 2 * (focus_padding + focus_width))
1988 height = (priv->header_h + priv->day_name_h
1991 requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1993 g_object_unref (layout);
1997 gtk_calendar_size_allocate (GtkWidget *widget,
1998 GtkAllocation *allocation)
2000 GtkCalendar *calendar = GTK_CALENDAR (widget);
2001 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2002 gint xthickness = widget->style->xthickness;
2003 gint ythickness = widget->style->xthickness;
2006 widget->allocation = *allocation;
2008 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2010 priv->day_width = (priv->min_day_width
2011 * ((allocation->width - (xthickness + INNER_BORDER) * 2
2012 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
2013 / (7 * priv->min_day_width + priv->max_week_char_width * 2));
2014 priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
2015 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
2016 - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
2020 priv->day_width = (allocation->width
2021 - (xthickness + INNER_BORDER) * 2
2022 - (CALENDAR_MARGIN * 2)
2023 - (DAY_XSEP * 6))/7;
2024 priv->week_width = 0;
2027 if (GTK_WIDGET_REALIZED (widget))
2029 gdk_window_move_resize (widget->window,
2030 allocation->x, allocation->y,
2031 allocation->width, allocation->height);
2032 if (priv->header_win)
2033 gdk_window_move_resize (priv->header_win,
2034 xthickness, ythickness,
2035 allocation->width - 2 * xthickness, priv->header_h);
2037 for (i = 0 ; i < 4 ; i++)
2039 if (priv->arrow_win[i])
2042 calendar_arrow_rectangle (calendar, i, &rect);
2044 gdk_window_move_resize (priv->arrow_win[i],
2045 rect.x, rect.y, rect.width, rect.height);
2049 if (priv->day_name_win)
2050 gdk_window_move_resize (priv->day_name_win,
2051 xthickness + INNER_BORDER,
2052 priv->header_h + (widget->style->ythickness + INNER_BORDER),
2053 allocation->width - (xthickness + INNER_BORDER) * 2,
2055 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2058 gdk_window_move_resize (priv->week_win,
2059 (xthickness + INNER_BORDER),
2060 priv->header_h + priv->day_name_h
2061 + (widget->style->ythickness + INNER_BORDER),
2064 gdk_window_move_resize (priv->main_win,
2065 priv->week_width + (xthickness + INNER_BORDER),
2066 priv->header_h + priv->day_name_h
2067 + (widget->style->ythickness + INNER_BORDER),
2070 - (xthickness + INNER_BORDER) * 2,
2075 gdk_window_move_resize (priv->main_win,
2076 (xthickness + INNER_BORDER),
2077 priv->header_h + priv->day_name_h
2078 + (widget->style->ythickness + INNER_BORDER),
2081 - (xthickness + INNER_BORDER) * 2,
2084 gdk_window_move_resize (priv->week_win,
2087 - (xthickness + INNER_BORDER),
2088 priv->header_h + priv->day_name_h
2089 + (widget->style->ythickness + INNER_BORDER),
2097 /****************************************
2099 ****************************************/
2102 calendar_paint_header (GtkCalendar *calendar)
2104 GtkWidget *widget = GTK_WIDGET (calendar);
2105 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2110 gint max_month_width;
2111 gint max_year_width;
2112 PangoLayout *layout;
2113 PangoRectangle logical_rect;
2119 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2120 year_left = priv->year_before;
2122 year_left = !priv->year_before;
2124 cr = gdk_cairo_create (priv->header_win);
2126 header_width = widget->allocation.width - 2 * widget->style->xthickness;
2128 max_month_width = priv->max_month_width;
2129 max_year_width = priv->max_year_width;
2131 gtk_paint_shadow (widget->style, priv->header_win,
2132 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2133 NULL, widget, "calendar",
2134 0, 0, header_width, priv->header_h);
2136 tmp_time = 1; /* Jan 1 1970, 00:00:01 UTC */
2137 tm = gmtime (&tmp_time);
2138 tm->tm_year = calendar->year - 1900;
2140 /* Translators: This dictates how the year is displayed in
2141 * gtkcalendar widget. See strftime() manual for the format.
2142 * Use only ASCII in the translation.
2144 * Also look for the msgid "2000".
2145 * Translate that entry to a year with the widest output of this
2148 * "%Y" is appropriate for most locales.
2150 strftime (buffer, sizeof (buffer), C_("calendar year format", "%Y"), tm);
2151 str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
2152 layout = gtk_widget_create_pango_layout (widget, str);
2155 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2158 y = (priv->header_h - logical_rect.height) / 2;
2160 /* Draw year and its arrows */
2162 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2164 x = 3 + (max_year_width - logical_rect.width)/2;
2166 x = header_width - (3 + max_year_width
2167 - (max_year_width - logical_rect.width)/2);
2170 x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
2172 x = header_width - (3 + priv->arrow_width + max_year_width
2173 - (max_year_width - logical_rect.width)/2);
2176 gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2177 cairo_move_to (cr, x, y);
2178 pango_cairo_show_layout (cr, layout);
2181 g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
2182 pango_layout_set_text (layout, buffer, -1);
2183 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2185 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2187 x = header_width - (3 + max_month_width
2188 - (max_month_width - logical_rect.width)/2);
2190 x = 3 + (max_month_width - logical_rect.width) / 2;
2193 x = header_width - (3 + priv->arrow_width + max_month_width
2194 - (max_month_width - logical_rect.width)/2);
2196 x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
2198 cairo_move_to (cr, x, y);
2199 pango_cairo_show_layout (cr, layout);
2201 g_object_unref (layout);
2206 calendar_paint_day_names (GtkCalendar *calendar)
2208 GtkWidget *widget = GTK_WIDGET (calendar);
2209 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2213 int day_width, cal_width;
2215 PangoLayout *layout;
2216 PangoRectangle logical_rect;
2220 cr = gdk_cairo_create (priv->day_name_win);
2222 gtk_widget_style_get (GTK_WIDGET (widget),
2223 "focus-line-width", &focus_width,
2224 "focus-padding", &focus_padding,
2227 day_width = priv->day_width;
2228 cal_width = widget->allocation.width;
2229 day_wid_sep = day_width + DAY_XSEP;
2232 * Draw rectangles as inverted background for the labels.
2235 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2236 cairo_rectangle (cr,
2237 CALENDAR_MARGIN, CALENDAR_MARGIN,
2238 cal_width-CALENDAR_MARGIN * 2,
2239 priv->day_name_h - CALENDAR_MARGIN);
2242 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2244 cairo_rectangle (cr,
2246 priv->day_name_h - CALENDAR_YSEP,
2247 priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
2256 layout = gtk_widget_create_pango_layout (widget, NULL);
2258 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2259 for (i = 0; i < 7; i++)
2261 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2265 day = (day + priv->week_start) % 7;
2266 g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
2268 pango_layout_set_text (layout, buffer, -1);
2269 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2273 + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2274 (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2277 + (day_width - logical_rect.width)/2),
2278 CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2279 pango_cairo_show_layout (cr, layout);
2282 g_object_unref (layout);
2287 calendar_paint_week_numbers (GtkCalendar *calendar)
2289 GtkWidget *widget = GTK_WIDGET (calendar);
2290 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2293 guint week = 0, year;
2294 gint row, x_loc, y_loc;
2297 PangoLayout *layout;
2298 PangoRectangle logical_rect;
2302 cr = gdk_cairo_create (priv->week_win);
2304 gtk_widget_style_get (GTK_WIDGET (widget),
2305 "focus-line-width", &focus_width,
2306 "focus-padding", &focus_padding,
2310 * Draw a rectangle as inverted background for the labels.
2313 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2314 if (priv->day_name_win)
2315 cairo_rectangle (cr,
2318 priv->week_width - CALENDAR_MARGIN,
2319 priv->main_h - CALENDAR_MARGIN);
2321 cairo_rectangle (cr,
2324 priv->week_width - CALENDAR_MARGIN,
2325 priv->main_h - 2 * CALENDAR_MARGIN);
2332 layout = gtk_widget_create_pango_layout (widget, NULL);
2334 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2335 day_height = calendar_row_height (calendar);
2336 for (row = 0; row < 6; row++)
2340 year = calendar->year;
2341 if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2344 result = week_of_year (&week, &year,
2345 ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2346 + calendar->month) % 12 + 1, calendar->day[row][6]);
2347 g_return_if_fail (result);
2349 /* Translators: this defines whether the week numbers should use
2350 * localized digits or the ones used in English (0123...).
2352 * Translate to "%Id" if you want to use localized digits, or
2353 * translate to "%d" otherwise.
2355 * Note that translating this doesn't guarantee that you get localized
2356 * digits. That needs support from your system and locale definition
2359 g_snprintf (buffer, sizeof (buffer), C_("calendar:week:digits", "%d"), week);
2360 pango_layout_set_text (layout, buffer, -1);
2361 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2363 y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2365 x_loc = (priv->week_width
2366 - logical_rect.width
2367 - CALENDAR_XSEP - focus_padding - focus_width);
2369 cairo_move_to (cr, x_loc, y_loc);
2370 pango_cairo_show_layout (cr, layout);
2373 g_object_unref (layout);
2378 calendar_invalidate_day_num (GtkCalendar *calendar,
2381 gint r, c, row, col;
2385 for (r = 0; r < 6; r++)
2386 for (c = 0; c < 7; c++)
2387 if (calendar->day_month[r][c] == MONTH_CURRENT &&
2388 calendar->day[r][c] == day)
2394 g_return_if_fail (row != -1);
2395 g_return_if_fail (col != -1);
2397 calendar_invalidate_day (calendar, row, col);
2401 calendar_invalidate_day (GtkCalendar *calendar,
2405 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2409 GdkRectangle day_rect;
2411 calendar_day_rectangle (calendar, row, col, &day_rect);
2412 gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2417 is_color_attribute (PangoAttribute *attribute,
2420 return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
2421 attribute->klass->type == PANGO_ATTR_BACKGROUND);
2425 calendar_paint_day (GtkCalendar *calendar,
2429 GtkWidget *widget = GTK_WIDGET (calendar);
2430 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2432 GdkColor *text_color;
2437 GdkRectangle day_rect;
2439 PangoLayout *layout;
2440 PangoRectangle logical_rect;
2441 gboolean overflow = FALSE;
2442 gboolean show_details;
2444 g_return_if_fail (row < 6);
2445 g_return_if_fail (col < 7);
2447 cr = gdk_cairo_create (priv->main_win);
2449 day = calendar->day[row][col];
2450 show_details = (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS);
2452 calendar_day_rectangle (calendar, row, col, &day_rect);
2454 if (calendar->day_month[row][col] == MONTH_PREV)
2456 text_color = PREV_MONTH_COLOR (widget);
2458 else if (calendar->day_month[row][col] == MONTH_NEXT)
2460 text_color = NEXT_MONTH_COLOR (widget);
2465 if (calendar->highlight_row == row && calendar->highlight_col == col)
2467 cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2468 gdk_cairo_rectangle (cr, &day_rect);
2472 if (calendar->selected_day == day)
2474 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2475 gdk_cairo_rectangle (cr, &day_rect);
2478 if (calendar->selected_day == day)
2479 text_color = SELECTED_FG_COLOR (widget);
2480 else if (calendar->marked_date[day-1])
2481 text_color = MARKED_COLOR (widget);
2483 text_color = NORMAL_DAY_COLOR (widget);
2486 /* Translators: this defines whether the day numbers should use
2487 * localized digits or the ones used in English (0123...).
2489 * Translate to "%Id" if you want to use localized digits, or
2490 * translate to "%d" otherwise.
2492 * Note that translating this doesn't guarantee that you get localized
2493 * digits. That needs support from your system and locale definition
2496 g_snprintf (buffer, sizeof (buffer), C_("calendar:day:digits", "%d"), day);
2498 /* Get extra information to show, if any: */
2500 detail = gtk_calendar_get_detail (calendar, row, col);
2502 layout = gtk_widget_create_pango_layout (widget, buffer);
2503 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2504 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2506 x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2509 gdk_cairo_set_source_color (cr, text_color);
2510 cairo_move_to (cr, x_loc, y_loc);
2511 pango_cairo_show_layout (cr, layout);
2513 if (calendar->day_month[row][col] == MONTH_CURRENT &&
2514 (calendar->marked_date[day-1] || (detail && !show_details)))
2516 cairo_move_to (cr, x_loc - 1, y_loc);
2517 pango_cairo_show_layout (cr, layout);
2520 y_loc += priv->max_day_char_descent;
2522 if (priv->detail_func && show_details)
2526 if (calendar->selected_day == day)
2527 gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
2528 else if (calendar->day_month[row][col] == MONTH_CURRENT)
2529 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
2531 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
2533 cairo_set_line_width (cr, 1);
2534 cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
2535 cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
2543 if (detail && show_details)
2545 gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
2546 pango_layout_set_markup (layout, markup, -1);
2549 if (day == calendar->selected_day)
2551 /* Stripping colors as they conflict with selection marking. */
2553 PangoAttrList *attrs = pango_layout_get_attributes (layout);
2554 PangoAttrList *colors = NULL;
2557 colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
2559 pango_attr_list_unref (colors);
2562 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2563 pango_layout_set_width (layout, PANGO_SCALE * day_rect.width);
2565 if (priv->detail_height_rows)
2567 gint dy = day_rect.height - (y_loc - day_rect.y);
2568 pango_layout_set_height (layout, PANGO_SCALE * dy);
2569 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
2572 cairo_move_to (cr, day_rect.x, y_loc);
2573 pango_cairo_show_layout (cr, layout);
2576 if (gtk_widget_has_focus (widget)
2577 && calendar->focus_row == row && calendar->focus_col == col)
2581 if (calendar->selected_day == day)
2582 state = gtk_widget_has_focus (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2584 state = GTK_STATE_NORMAL;
2586 gtk_paint_focus (widget->style,
2589 NULL, widget, "calendar-day",
2590 day_rect.x, day_rect.y,
2591 day_rect.width, day_rect.height);
2595 priv->detail_overflow[row] |= (1 << col);
2597 priv->detail_overflow[row] &= ~(1 << col);
2599 g_object_unref (layout);
2605 calendar_paint_main (GtkCalendar *calendar)
2609 for (col = 0; col < 7; col++)
2610 for (row = 0; row < 6; row++)
2611 calendar_paint_day (calendar, row, col);
2615 calendar_invalidate_arrow (GtkCalendar *calendar,
2618 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2621 window = priv->arrow_win[arrow];
2623 gdk_window_invalidate_rect (window, NULL, FALSE);
2627 calendar_paint_arrow (GtkCalendar *calendar,
2630 GtkWidget *widget = GTK_WIDGET (calendar);
2631 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2634 window = priv->arrow_win[arrow];
2637 cairo_t *cr = gdk_cairo_create (window);
2641 state = priv->arrow_state[arrow];
2643 gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2647 gdk_drawable_get_size (window, &width, &height);
2648 if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2649 gtk_paint_arrow (widget->style, window, state,
2650 GTK_SHADOW_OUT, NULL, widget, "calendar",
2651 GTK_ARROW_LEFT, TRUE,
2652 width/2 - 3, height/2 - 4, 8, 8);
2654 gtk_paint_arrow (widget->style, window, state,
2655 GTK_SHADOW_OUT, NULL, widget, "calendar",
2656 GTK_ARROW_RIGHT, TRUE,
2657 width/2 - 4, height/2 - 4, 8, 8);
2662 gtk_calendar_expose (GtkWidget *widget,
2663 GdkEventExpose *event)
2665 GtkCalendar *calendar = GTK_CALENDAR (widget);
2666 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2669 if (gtk_widget_is_drawable (widget))
2671 if (event->window == priv->main_win)
2672 calendar_paint_main (calendar);
2674 if (event->window == priv->header_win)
2675 calendar_paint_header (calendar);
2677 for (i = 0; i < 4; i++)
2678 if (event->window == priv->arrow_win[i])
2679 calendar_paint_arrow (calendar, i);
2681 if (event->window == priv->day_name_win)
2682 calendar_paint_day_names (calendar);
2684 if (event->window == priv->week_win)
2685 calendar_paint_week_numbers (calendar);
2686 if (event->window == widget->window)
2688 gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2689 GTK_SHADOW_IN, NULL, widget, "calendar",
2690 0, 0, widget->allocation.width, widget->allocation.height);
2698 /****************************************
2700 ****************************************/
2703 calendar_arrow_action (GtkCalendar *calendar,
2708 case ARROW_YEAR_LEFT:
2709 calendar_set_year_prev (calendar);
2711 case ARROW_YEAR_RIGHT:
2712 calendar_set_year_next (calendar);
2714 case ARROW_MONTH_LEFT:
2715 calendar_set_month_prev (calendar);
2717 case ARROW_MONTH_RIGHT:
2718 calendar_set_month_next (calendar);
2726 calendar_timer (gpointer data)
2728 GtkCalendar *calendar = data;
2729 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2730 gboolean retval = FALSE;
2734 calendar_arrow_action (calendar, priv->click_child);
2736 if (priv->need_timer)
2738 GtkSettings *settings;
2741 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2742 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2744 priv->need_timer = FALSE;
2745 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2746 timeout * SCROLL_DELAY_FACTOR,
2747 (GSourceFunc) calendar_timer,
2748 (gpointer) calendar, NULL);
2758 calendar_start_spinning (GtkCalendar *calendar,
2761 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2763 priv->click_child = click_child;
2767 GtkSettings *settings;
2770 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2771 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2773 priv->need_timer = TRUE;
2774 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2776 (GSourceFunc) calendar_timer,
2777 (gpointer) calendar, NULL);
2782 calendar_stop_spinning (GtkCalendar *calendar)
2784 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2788 g_source_remove (priv->timer);
2790 priv->need_timer = FALSE;
2795 calendar_main_button_press (GtkCalendar *calendar,
2796 GdkEventButton *event)
2798 GtkWidget *widget = GTK_WIDGET (calendar);
2799 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2805 x = (gint) (event->x);
2806 y = (gint) (event->y);
2808 row = calendar_row_from_y (calendar, y);
2809 col = calendar_column_from_x (calendar, x);
2811 /* If row or column isn't found, just return. */
2812 if (row == -1 || col == -1)
2815 day_month = calendar->day_month[row][col];
2817 if (event->type == GDK_BUTTON_PRESS)
2819 day = calendar->day[row][col];
2821 if (day_month == MONTH_PREV)
2822 calendar_set_month_prev (calendar);
2823 else if (day_month == MONTH_NEXT)
2824 calendar_set_month_next (calendar);
2826 if (!gtk_widget_has_focus (widget))
2827 gtk_widget_grab_focus (widget);
2829 if (event->button == 1)
2832 priv->drag_start_x = x;
2833 priv->drag_start_y = y;
2836 calendar_select_and_focus_day (calendar, day);
2838 else if (event->type == GDK_2BUTTON_PRESS)
2841 if (day_month == MONTH_CURRENT)
2842 g_signal_emit (calendar,
2843 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2849 gtk_calendar_button_press (GtkWidget *widget,
2850 GdkEventButton *event)
2852 GtkCalendar *calendar = GTK_CALENDAR (widget);
2853 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2856 if (event->window == priv->main_win)
2857 calendar_main_button_press (calendar, event);
2859 if (!gtk_widget_has_focus (widget))
2860 gtk_widget_grab_focus (widget);
2862 for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2864 if (event->window == priv->arrow_win[arrow])
2867 /* only call the action on single click, not double */
2868 if (event->type == GDK_BUTTON_PRESS)
2870 if (event->button == 1)
2871 calendar_start_spinning (calendar, arrow);
2873 calendar_arrow_action (calendar, arrow);
2884 gtk_calendar_button_release (GtkWidget *widget,
2885 GdkEventButton *event)
2887 GtkCalendar *calendar = GTK_CALENDAR (widget);
2888 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2890 if (event->button == 1)
2892 calendar_stop_spinning (calendar);
2902 gtk_calendar_motion_notify (GtkWidget *widget,
2903 GdkEventMotion *event)
2905 GtkCalendar *calendar = GTK_CALENDAR (widget);
2906 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2907 gint event_x, event_y;
2909 gint old_row, old_col;
2911 event_x = (gint) (event->x);
2912 event_y = (gint) (event->y);
2914 if (event->window == priv->main_win)
2919 if (gtk_drag_check_threshold (widget,
2920 priv->drag_start_x, priv->drag_start_y,
2921 event->x, event->y))
2923 GdkDragContext *context;
2924 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2925 gtk_target_list_add_text_targets (target_list, 0);
2926 context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
2927 1, (GdkEvent *)event);
2932 gtk_target_list_unref (target_list);
2933 gtk_drag_set_icon_default (context);
2938 row = calendar_row_from_y (calendar, event_y);
2939 col = calendar_column_from_x (calendar, event_x);
2941 if (row != calendar->highlight_row || calendar->highlight_col != col)
2943 old_row = calendar->highlight_row;
2944 old_col = calendar->highlight_col;
2945 if (old_row > -1 && old_col > -1)
2947 calendar->highlight_row = -1;
2948 calendar->highlight_col = -1;
2949 calendar_invalidate_day (calendar, old_row, old_col);
2952 calendar->highlight_row = row;
2953 calendar->highlight_col = col;
2955 if (row > -1 && col > -1)
2956 calendar_invalidate_day (calendar, row, col);
2964 gtk_calendar_enter_notify (GtkWidget *widget,
2965 GdkEventCrossing *event)
2967 GtkCalendar *calendar = GTK_CALENDAR (widget);
2968 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2970 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2972 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2973 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2976 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2978 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2979 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2982 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2984 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2985 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2988 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2990 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2991 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2998 gtk_calendar_leave_notify (GtkWidget *widget,
2999 GdkEventCrossing *event)
3001 GtkCalendar *calendar = GTK_CALENDAR (widget);
3002 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3006 if (event->window == priv->main_win)
3008 row = calendar->highlight_row;
3009 col = calendar->highlight_col;
3010 calendar->highlight_row = -1;
3011 calendar->highlight_col = -1;
3012 if (row > -1 && col > -1)
3013 calendar_invalidate_day (calendar, row, col);
3016 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3018 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3019 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3022 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3024 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3025 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3028 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3030 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3031 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3034 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3036 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3037 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3044 gtk_calendar_scroll (GtkWidget *widget,
3045 GdkEventScroll *event)
3047 GtkCalendar *calendar = GTK_CALENDAR (widget);
3049 if (event->direction == GDK_SCROLL_UP)
3051 if (!gtk_widget_has_focus (widget))
3052 gtk_widget_grab_focus (widget);
3053 calendar_set_month_prev (calendar);
3055 else if (event->direction == GDK_SCROLL_DOWN)
3057 if (!gtk_widget_has_focus (widget))
3058 gtk_widget_grab_focus (widget);
3059 calendar_set_month_next (calendar);
3068 /****************************************
3070 ****************************************/
3073 move_focus (GtkCalendar *calendar,
3076 GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3078 if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3079 (text_dir == GTK_TEXT_DIR_RTL && direction == 1))
3081 if (calendar->focus_col > 0)
3082 calendar->focus_col--;
3083 else if (calendar->focus_row > 0)
3085 calendar->focus_col = 6;
3086 calendar->focus_row--;
3089 if (calendar->focus_col < 0)
3090 calendar->focus_col = 6;
3091 if (calendar->focus_row < 0)
3092 calendar->focus_row = 5;
3096 if (calendar->focus_col < 6)
3097 calendar->focus_col++;
3098 else if (calendar->focus_row < 5)
3100 calendar->focus_col = 0;
3101 calendar->focus_row++;
3104 if (calendar->focus_col < 0)
3105 calendar->focus_col = 0;
3106 if (calendar->focus_row < 0)
3107 calendar->focus_row = 0;
3112 gtk_calendar_key_press (GtkWidget *widget,
3115 GtkCalendar *calendar;
3121 calendar = GTK_CALENDAR (widget);
3124 old_focus_row = calendar->focus_row;
3125 old_focus_col = calendar->focus_col;
3127 switch (event->keyval)
3132 if (event->state & GDK_CONTROL_MASK)
3133 calendar_set_month_prev (calendar);
3136 move_focus (calendar, -1);
3137 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3138 calendar_invalidate_day (calendar, calendar->focus_row,
3139 calendar->focus_col);
3145 if (event->state & GDK_CONTROL_MASK)
3146 calendar_set_month_next (calendar);
3149 move_focus (calendar, 1);
3150 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3151 calendar_invalidate_day (calendar, calendar->focus_row,
3152 calendar->focus_col);
3158 if (event->state & GDK_CONTROL_MASK)
3159 calendar_set_year_prev (calendar);
3162 if (calendar->focus_row > 0)
3163 calendar->focus_row--;
3164 if (calendar->focus_row < 0)
3165 calendar->focus_row = 5;
3166 if (calendar->focus_col < 0)
3167 calendar->focus_col = 6;
3168 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3169 calendar_invalidate_day (calendar, calendar->focus_row,
3170 calendar->focus_col);
3176 if (event->state & GDK_CONTROL_MASK)
3177 calendar_set_year_next (calendar);
3180 if (calendar->focus_row < 5)
3181 calendar->focus_row++;
3182 if (calendar->focus_col < 0)
3183 calendar->focus_col = 0;
3184 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3185 calendar_invalidate_day (calendar, calendar->focus_row,
3186 calendar->focus_col);
3191 row = calendar->focus_row;
3192 col = calendar->focus_col;
3194 if (row > -1 && col > -1)
3198 day = calendar->day[row][col];
3199 if (calendar->day_month[row][col] == MONTH_PREV)
3200 calendar_set_month_prev (calendar);
3201 else if (calendar->day_month[row][col] == MONTH_NEXT)
3202 calendar_set_month_next (calendar);
3204 calendar_select_and_focus_day (calendar, day);
3212 /****************************************
3213 * Misc widget methods *
3214 ****************************************/
3217 calendar_set_background (GtkWidget *widget)
3219 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3222 if (GTK_WIDGET_REALIZED (widget))
3224 for (i = 0; i < 4; i++)
3226 if (priv->arrow_win[i])
3227 gdk_window_set_background (priv->arrow_win[i],
3228 HEADER_BG_COLOR (widget));
3230 if (priv->header_win)
3231 gdk_window_set_background (priv->header_win,
3232 HEADER_BG_COLOR (widget));
3233 if (priv->day_name_win)
3234 gdk_window_set_background (priv->day_name_win,
3235 BACKGROUND_COLOR (widget));
3237 gdk_window_set_background (priv->week_win,
3238 BACKGROUND_COLOR (widget));
3240 gdk_window_set_background (priv->main_win,
3241 BACKGROUND_COLOR (widget));
3243 gdk_window_set_background (widget->window,
3244 BACKGROUND_COLOR (widget));
3249 gtk_calendar_style_set (GtkWidget *widget,
3250 GtkStyle *previous_style)
3252 if (previous_style && GTK_WIDGET_REALIZED (widget))
3253 calendar_set_background (widget);
3257 gtk_calendar_state_changed (GtkWidget *widget,
3258 GtkStateType previous_state)
3260 GtkCalendar *calendar = GTK_CALENDAR (widget);
3261 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3264 if (!gtk_widget_is_sensitive (widget))
3267 calendar_stop_spinning (calendar);
3270 for (i = 0; i < 4; i++)
3271 if (gtk_widget_is_sensitive (widget))
3272 priv->arrow_state[i] = GTK_STATE_NORMAL;
3274 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
3276 calendar_set_background (widget);
3280 gtk_calendar_grab_notify (GtkWidget *widget,
3281 gboolean was_grabbed)
3284 calendar_stop_spinning (GTK_CALENDAR (widget));
3288 gtk_calendar_focus_out (GtkWidget *widget,
3289 GdkEventFocus *event)
3291 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3292 GtkCalendar *calendar = GTK_CALENDAR (widget);
3294 calendar_queue_refresh (calendar);
3295 calendar_stop_spinning (calendar);
3303 /****************************************
3305 ****************************************/
3308 gtk_calendar_drag_data_get (GtkWidget *widget,
3309 GdkDragContext *context,
3310 GtkSelectionData *selection_data,
3314 GtkCalendar *calendar = GTK_CALENDAR (widget);
3319 date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
3320 len = g_date_strftime (str, 127, "%x", date);
3321 gtk_selection_data_set_text (selection_data, str, len);
3326 /* Get/set whether drag_motion requested the drag data and
3327 * drag_data_received should thus not actually insert the data,
3328 * since the data doesn't result from a drop.
3331 set_status_pending (GdkDragContext *context,
3332 GdkDragAction suggested_action)
3334 g_object_set_data (G_OBJECT (context),
3335 I_("gtk-calendar-status-pending"),
3336 GINT_TO_POINTER (suggested_action));
3339 static GdkDragAction
3340 get_status_pending (GdkDragContext *context)
3342 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
3343 "gtk-calendar-status-pending"));
3347 gtk_calendar_drag_leave (GtkWidget *widget,
3348 GdkDragContext *context,
3351 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3353 priv->drag_highlight = 0;
3354 gtk_drag_unhighlight (widget);
3359 gtk_calendar_drag_motion (GtkWidget *widget,
3360 GdkDragContext *context,
3365 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3368 if (!priv->drag_highlight)
3370 priv->drag_highlight = 1;
3371 gtk_drag_highlight (widget);
3374 target = gtk_drag_dest_find_target (widget, context, NULL);
3375 if (target == GDK_NONE || context->suggested_action == 0)
3376 gdk_drag_status (context, 0, time);
3379 set_status_pending (context, context->suggested_action);
3380 gtk_drag_get_data (widget, context, target, time);
3387 gtk_calendar_drag_drop (GtkWidget *widget,
3388 GdkDragContext *context,
3395 target = gtk_drag_dest_find_target (widget, context, NULL);
3396 if (target != GDK_NONE)
3398 gtk_drag_get_data (widget, context,
3408 gtk_calendar_drag_data_received (GtkWidget *widget,
3409 GdkDragContext *context,
3412 GtkSelectionData *selection_data,
3416 GtkCalendar *calendar = GTK_CALENDAR (widget);
3417 guint day, month, year;
3420 GdkDragAction suggested_action;
3422 suggested_action = get_status_pending (context);
3424 if (suggested_action)
3426 set_status_pending (context, 0);
3428 /* We are getting this data due to a request in drag_motion,
3429 * rather than due to a request in drag_drop, so we are just
3430 * supposed to call drag_status, not actually paste in the
3433 str = (gchar*) gtk_selection_data_get_text (selection_data);
3437 date = g_date_new ();
3438 g_date_set_parse (date, str);
3439 if (!g_date_valid (date))
3440 suggested_action = 0;
3445 suggested_action = 0;
3447 gdk_drag_status (context, suggested_action, time);
3452 date = g_date_new ();
3453 str = (gchar*) gtk_selection_data_get_text (selection_data);
3456 g_date_set_parse (date, str);
3460 if (!g_date_valid (date))
3462 g_warning ("Received invalid date data\n");
3464 gtk_drag_finish (context, FALSE, FALSE, time);
3468 day = g_date_get_day (date);
3469 month = g_date_get_month (date);
3470 year = g_date_get_year (date);
3473 gtk_drag_finish (context, TRUE, FALSE, time);
3476 g_object_freeze_notify (G_OBJECT (calendar));
3477 if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3478 && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
3479 gtk_calendar_select_month (calendar, month - 1, year);
3480 gtk_calendar_select_day (calendar, day);
3481 g_object_thaw_notify (G_OBJECT (calendar));
3485 /****************************************
3487 ****************************************/
3492 * Creates a new calendar, with the current date being selected.
3494 * Return value: a newly #GtkCalendar widget
3497 gtk_calendar_new (void)
3499 return g_object_new (GTK_TYPE_CALENDAR, NULL);
3503 * gtk_calendar_display_options:
3504 * @calendar: a #GtkCalendar.
3505 * @flags: the display options to set.
3507 * Sets display options (whether to display the heading and the month headings).
3509 * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
3512 gtk_calendar_display_options (GtkCalendar *calendar,
3513 GtkCalendarDisplayOptions flags)
3515 gtk_calendar_set_display_options (calendar, flags);
3519 * gtk_calendar_get_display_options:
3520 * @calendar: a #GtkCalendar
3522 * Returns the current display options of @calendar.
3524 * Return value: the display options.
3528 GtkCalendarDisplayOptions
3529 gtk_calendar_get_display_options (GtkCalendar *calendar)
3531 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3533 return calendar->display_flags;
3537 * gtk_calendar_set_display_options:
3538 * @calendar: a #GtkCalendar
3539 * @flags: the display options to set
3541 * Sets display options (whether to display the heading and the month
3547 gtk_calendar_set_display_options (GtkCalendar *calendar,
3548 GtkCalendarDisplayOptions flags)
3550 GtkWidget *widget = GTK_WIDGET (calendar);
3551 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3554 GtkCalendarDisplayOptions old_flags;
3556 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3558 old_flags = calendar->display_flags;
3560 if (GTK_WIDGET_REALIZED (widget))
3562 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3565 if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3566 && (priv->header_win))
3568 calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3569 calendar_realize_arrows (calendar);
3573 for (i = 0; i < 4; i++)
3575 if (priv->arrow_win[i])
3577 gdk_window_set_user_data (priv->arrow_win[i],
3579 gdk_window_destroy (priv->arrow_win[i]);
3580 priv->arrow_win[i] = NULL;
3586 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3590 if (flags & GTK_CALENDAR_SHOW_HEADING)
3592 calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3593 calendar_realize_header (calendar);
3597 for (i = 0; i < 4; i++)
3599 if (priv->arrow_win[i])
3601 gdk_window_set_user_data (priv->arrow_win[i],
3603 gdk_window_destroy (priv->arrow_win[i]);
3604 priv->arrow_win[i] = NULL;
3607 gdk_window_set_user_data (priv->header_win, NULL);
3608 gdk_window_destroy (priv->header_win);
3609 priv->header_win = NULL;
3614 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3618 if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3620 calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3621 calendar_realize_day_names (calendar);
3625 gdk_window_set_user_data (priv->day_name_win, NULL);
3626 gdk_window_destroy (priv->day_name_win);
3627 priv->day_name_win = NULL;
3631 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3635 if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3637 calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3638 calendar_realize_week_numbers (calendar);
3642 gdk_window_set_user_data (priv->week_win, NULL);
3643 gdk_window_destroy (priv->week_win);
3644 priv->week_win = NULL;
3648 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3649 g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3651 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DETAILS)
3654 calendar->display_flags = flags;
3656 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3660 calendar->display_flags = flags;
3662 g_object_freeze_notify (G_OBJECT (calendar));
3663 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3664 g_object_notify (G_OBJECT (calendar), "show-heading");
3665 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3666 g_object_notify (G_OBJECT (calendar), "show-day-names");
3667 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3668 g_object_notify (G_OBJECT (calendar), "no-month-change");
3669 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3670 g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3671 g_object_thaw_notify (G_OBJECT (calendar));
3675 * gtk_calendar_select_month:
3676 * @calendar: a #GtkCalendar
3677 * @month: a month number between 0 and 11.
3678 * @year: the year the month is in.
3680 * Shifts the calendar to a different month.
3682 * Return value: %TRUE, always
3685 gtk_calendar_select_month (GtkCalendar *calendar,
3689 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3690 g_return_val_if_fail (month <= 11, FALSE);
3692 calendar->month = month;
3693 calendar->year = year;
3695 calendar_compute_days (calendar);
3696 calendar_queue_refresh (calendar);
3698 g_object_freeze_notify (G_OBJECT (calendar));
3699 g_object_notify (G_OBJECT (calendar), "month");
3700 g_object_notify (G_OBJECT (calendar), "year");
3701 g_object_thaw_notify (G_OBJECT (calendar));
3703 g_signal_emit (calendar,
3704 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3710 * gtk_calendar_select_day:
3711 * @calendar: a #GtkCalendar.
3712 * @day: the day number between 1 and 31, or 0 to unselect
3713 * the currently selected day.
3715 * Selects a day from the current month.
3718 gtk_calendar_select_day (GtkCalendar *calendar,
3721 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3722 g_return_if_fail (day <= 31);
3724 /* Deselect the old day */
3725 if (calendar->selected_day > 0)
3729 selected_day = calendar->selected_day;
3730 calendar->selected_day = 0;
3731 if (gtk_widget_is_drawable (GTK_WIDGET (calendar)))
3732 calendar_invalidate_day_num (calendar, selected_day);
3735 calendar->selected_day = day;
3737 /* Select the new day */
3740 if (gtk_widget_is_drawable (GTK_WIDGET (calendar)))
3741 calendar_invalidate_day_num (calendar, day);
3744 g_object_notify (G_OBJECT (calendar), "day");
3746 g_signal_emit (calendar,
3747 gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3752 * gtk_calendar_clear_marks:
3753 * @calendar: a #GtkCalendar
3755 * Remove all visual markers.
3758 gtk_calendar_clear_marks (GtkCalendar *calendar)
3762 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3764 for (day = 0; day < 31; day++)
3766 calendar->marked_date[day] = FALSE;
3769 calendar->num_marked_dates = 0;
3770 calendar_queue_refresh (calendar);
3774 * gtk_calendar_mark_day:
3775 * @calendar: a #GtkCalendar
3776 * @day: the day number to mark between 1 and 31.
3778 * Places a visual marker on a particular day.
3780 * Return value: %TRUE, always
3783 gtk_calendar_mark_day (GtkCalendar *calendar,
3786 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3788 if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3790 calendar->marked_date[day - 1] = TRUE;
3791 calendar->num_marked_dates++;
3792 calendar_invalidate_day_num (calendar, day);
3799 * gtk_calendar_unmark_day:
3800 * @calendar: a #GtkCalendar.
3801 * @day: the day number to unmark between 1 and 31.
3803 * Removes the visual marker from a particular day.
3805 * Return value: %TRUE, always
3808 gtk_calendar_unmark_day (GtkCalendar *calendar,
3811 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3813 if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3815 calendar->marked_date[day - 1] = FALSE;
3816 calendar->num_marked_dates--;
3817 calendar_invalidate_day_num (calendar, day);
3824 * gtk_calendar_get_date:
3825 * @calendar: a #GtkCalendar
3826 * @year: (allow-none): location to store the year number, or %NULL
3827 * @month: (allow-none): location to store the month number (between 0 and 11), or %NULL
3828 * @day: (allow-none): location to store the day number (between 1 and 31), or %NULL
3830 * Obtains the selected date from a #GtkCalendar.
3833 gtk_calendar_get_date (GtkCalendar *calendar,
3838 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3841 *year = calendar->year;
3844 *month = calendar->month;
3847 *day = calendar->selected_day;
3851 * gtk_calendar_set_detail_func:
3852 * @calendar: a #GtkCalendar.
3853 * @func: a function providing details for each day.
3854 * @data: data to pass to @func invokations.
3855 * @destroy: a function for releasing @data.
3857 * Installs a function which provides Pango markup with detail information
3858 * for each day. Examples for such details are holidays or appointments. That
3859 * information is shown below each day when #GtkCalendar:show-details is set.
3860 * A tooltip containing with full detail information is provided, if the entire
3861 * text should not fit into the details area, or if #GtkCalendar:show-details
3864 * The size of the details area can be restricted by setting the
3865 * #GtkCalendar:detail-width-chars and #GtkCalendar:detail-height-rows
3871 gtk_calendar_set_detail_func (GtkCalendar *calendar,
3872 GtkCalendarDetailFunc func,
3874 GDestroyNotify destroy)
3876 GtkCalendarPrivate *priv;
3878 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3880 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3882 if (priv->detail_func_destroy)
3883 priv->detail_func_destroy (priv->detail_func_user_data);
3885 priv->detail_func = func;
3886 priv->detail_func_user_data = data;
3887 priv->detail_func_destroy = destroy;
3889 gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
3890 NULL != priv->detail_func);
3891 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3895 * gtk_calendar_set_detail_width_chars:
3896 * @calendar: a #GtkCalendar.
3897 * @chars: detail width in characters.
3899 * Updates the width of detail cells.
3900 * See #GtkCalendar:detail-width-chars.
3905 gtk_calendar_set_detail_width_chars (GtkCalendar *calendar,
3908 GtkCalendarPrivate *priv;
3910 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3912 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3914 if (chars != priv->detail_width_chars)
3916 priv->detail_width_chars = chars;
3917 g_object_notify (G_OBJECT (calendar), "detail-width-chars");
3918 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3923 * gtk_calendar_set_detail_height_rows:
3924 * @calendar: a #GtkCalendar.
3925 * @rows: detail height in rows.
3927 * Updates the height of detail cells.
3928 * See #GtkCalendar:detail-height-rows.
3933 gtk_calendar_set_detail_height_rows (GtkCalendar *calendar,
3936 GtkCalendarPrivate *priv;
3938 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3940 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3942 if (rows != priv->detail_height_rows)
3944 priv->detail_height_rows = rows;
3945 g_object_notify (G_OBJECT (calendar), "detail-height-rows");
3946 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3951 * gtk_calendar_get_detail_width_chars:
3952 * @calendar: a #GtkCalendar.
3954 * Queries the width of detail cells, in characters.
3955 * See #GtkCalendar:detail-width-chars.
3959 * Return value: The width of detail cells, in characters.
3962 gtk_calendar_get_detail_width_chars (GtkCalendar *calendar)
3964 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3965 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
3969 * gtk_calendar_get_detail_height_rows:
3970 * @calendar: a #GtkCalendar.
3972 * Queries the height of detail cells, in rows.
3973 * See #GtkCalendar:detail-width-chars.
3977 * Return value: The height of detail cells, in rows.
3980 gtk_calendar_get_detail_height_rows (GtkCalendar *calendar)
3982 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3983 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
3987 * gtk_calendar_freeze:
3988 * @calendar: a #GtkCalendar
3990 * Does nothing. Previously locked the display of the calendar until
3991 * it was thawed with gtk_calendar_thaw().
3996 gtk_calendar_freeze (GtkCalendar *calendar)
3998 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4002 * gtk_calendar_thaw:
4003 * @calendar: a #GtkCalendar
4005 * Does nothing. Previously defrosted a calendar; all the changes made
4006 * since the last gtk_calendar_freeze() were displayed.
4011 gtk_calendar_thaw (GtkCalendar *calendar)
4013 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4016 #define __GTK_CALENDAR_C__
4017 #include "gtkaliasdef.c"