1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
5 * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Groenlund
8 * Copyright (c) 1995, 1996, 1997, 1998 by Steffen Beyer
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free
22 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
27 * file for a list of people on the GTK+ Team. See the ChangeLog
28 * files for a list of changes. These files are distributed with
29 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
34 #ifdef HAVE_SYS_TIME_H
37 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
50 #include "gtkcalendar.h"
54 #include "gtkmarshalers.h"
55 #include "gtktooltip.h"
56 #include "gtkprivate.h"
57 #include "gdk/gdkkeysyms.h"
60 /***************************************************************************/
61 /* The following date routines are taken from the lib_date package.
62 * They have been minimally edited to avoid conflict with types defined
66 static const guint month_length[2][13] =
68 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
69 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
72 static const guint days_in_months[2][14] =
74 { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
75 { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
78 static glong calc_days(guint year, guint mm, guint dd);
79 static guint day_of_week(guint year, guint mm, guint dd);
80 static glong dates_difference(guint year1, guint mm1, guint dd1,
81 guint year2, guint mm2, guint dd2);
82 static guint weeks_in_year(guint year);
87 return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
91 day_of_week (guint year, guint mm, guint dd)
95 days = calc_days(year, mm, dd);
102 return( (guint) days );
105 static guint weeks_in_year(guint year)
107 return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
111 check_date(guint year, guint mm, guint dd)
113 if (year < 1) return FALSE;
114 if ((mm < 1) || (mm > 12)) return FALSE;
115 if ((dd < 1) || (dd > month_length[leap(year)][mm])) return FALSE;
120 week_number(guint year, guint mm, guint dd)
124 first = day_of_week(year,1,1) - 1;
125 return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
130 year_to_days(guint year)
132 return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
137 calc_days(guint year, guint mm, guint dd)
141 if (year < 1) return(0L);
142 if ((mm < 1) || (mm > 12)) return(0L);
143 if ((dd < 1) || (dd > month_length[(lp = leap(year))][mm])) return(0L);
144 return( year_to_days(--year) + days_in_months[lp][mm] + dd );
148 week_of_year(guint *week, guint *year, guint mm, guint dd)
150 if (check_date(*year,mm,dd))
152 *week = week_number(*year,mm,dd);
154 *week = weeks_in_year(--(*year));
155 else if (*week > weeks_in_year(*year))
166 dates_difference(guint year1, guint mm1, guint dd1,
167 guint year2, guint mm2, guint dd2)
169 return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
172 /*** END OF lib_date routines ********************************************/
174 /* Spacing around day/week headers and main area, inside those windows */
175 #define CALENDAR_MARGIN 0
176 /* Spacing around day/week headers and main area, outside those windows */
177 #define INNER_BORDER 4
178 /* Separation between day headers and main area */
179 #define CALENDAR_YSEP 4
180 /* Separation between week headers and main area */
181 #define CALENDAR_XSEP 4
183 #define DAY_XSEP 0 /* not really good for small calendar */
184 #define DAY_YSEP 0 /* not really good for small calendar */
186 #define SCROLL_DELAY_FACTOR 5
189 #define HEADER_FG_COLOR(widget) (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
190 #define HEADER_BG_COLOR(widget) (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
191 #define SELECTED_BG_COLOR(widget) (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
192 #define SELECTED_FG_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
193 #define NORMAL_DAY_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
194 #define PREV_MONTH_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
195 #define NEXT_MONTH_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
196 #define MARKED_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
197 #define BACKGROUND_COLOR(widget) (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
198 #define HIGHLIGHT_BACK_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
214 MONTH_CHANGED_SIGNAL,
216 DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
232 PROP_NO_MONTH_CHANGE,
233 PROP_SHOW_WEEK_NUMBERS,
235 PROP_DETAIL_WIDTH_CHARS,
236 PROP_DETAIL_HEIGHT_ROWS
239 static guint gtk_calendar_signals[LAST_SIGNAL] = { 0 };
241 struct _GtkCalendarPrivate
243 GdkWindow *header_win;
244 GdkWindow *day_name_win;
247 GdkWindow *arrow_win[4];
253 guint arrow_state[4];
255 guint max_month_width;
256 guint max_year_width;
262 guint max_day_char_width;
263 guint max_day_char_ascent;
264 guint max_day_char_descent;
265 guint max_label_char_ascent;
266 guint max_label_char_descent;
267 guint max_week_char_width;
270 guint year_before : 1;
272 guint need_timer : 1;
275 guint drag_highlight : 1;
285 /* Optional callback, used to display extra information for each day. */
286 GtkCalendarDetailFunc detail_func;
287 gpointer detail_func_user_data;
288 GDestroyNotify detail_func_destroy;
290 /* Size requistion for details provided by the hook. */
291 gint detail_height_rows;
292 gint detail_width_chars;
293 gint detail_overflow[6];
296 #define GTK_CALENDAR_GET_PRIVATE(widget) (GTK_CALENDAR (widget)->priv)
298 static void gtk_calendar_finalize (GObject *calendar);
299 static void gtk_calendar_destroy (GtkObject *calendar);
300 static void gtk_calendar_set_property (GObject *object,
304 static void gtk_calendar_get_property (GObject *object,
309 static void gtk_calendar_realize (GtkWidget *widget);
310 static void gtk_calendar_unrealize (GtkWidget *widget);
311 static void gtk_calendar_size_request (GtkWidget *widget,
312 GtkRequisition *requisition);
313 static void gtk_calendar_size_allocate (GtkWidget *widget,
314 GtkAllocation *allocation);
315 static gboolean gtk_calendar_expose (GtkWidget *widget,
316 GdkEventExpose *event);
317 static gboolean gtk_calendar_button_press (GtkWidget *widget,
318 GdkEventButton *event);
319 static gboolean gtk_calendar_button_release (GtkWidget *widget,
320 GdkEventButton *event);
321 static gboolean gtk_calendar_motion_notify (GtkWidget *widget,
322 GdkEventMotion *event);
323 static gboolean gtk_calendar_enter_notify (GtkWidget *widget,
324 GdkEventCrossing *event);
325 static gboolean gtk_calendar_leave_notify (GtkWidget *widget,
326 GdkEventCrossing *event);
327 static gboolean gtk_calendar_scroll (GtkWidget *widget,
328 GdkEventScroll *event);
329 static gboolean gtk_calendar_key_press (GtkWidget *widget,
331 static gboolean gtk_calendar_focus_out (GtkWidget *widget,
332 GdkEventFocus *event);
333 static void gtk_calendar_grab_notify (GtkWidget *widget,
334 gboolean was_grabbed);
335 static void gtk_calendar_state_changed (GtkWidget *widget,
336 GtkStateType previous_state);
337 static void gtk_calendar_style_set (GtkWidget *widget,
338 GtkStyle *previous_style);
339 static gboolean gtk_calendar_query_tooltip (GtkWidget *widget,
342 gboolean keyboard_mode,
343 GtkTooltip *tooltip);
345 static void gtk_calendar_drag_data_get (GtkWidget *widget,
346 GdkDragContext *context,
347 GtkSelectionData *selection_data,
350 static void gtk_calendar_drag_data_received (GtkWidget *widget,
351 GdkDragContext *context,
354 GtkSelectionData *selection_data,
357 static gboolean gtk_calendar_drag_motion (GtkWidget *widget,
358 GdkDragContext *context,
362 static void gtk_calendar_drag_leave (GtkWidget *widget,
363 GdkDragContext *context,
365 static gboolean gtk_calendar_drag_drop (GtkWidget *widget,
366 GdkDragContext *context,
371 static void calendar_start_spinning (GtkCalendar *calendar,
373 static void calendar_stop_spinning (GtkCalendar *calendar);
375 static void calendar_invalidate_day (GtkCalendar *widget,
378 static void calendar_invalidate_day_num (GtkCalendar *widget,
380 static void calendar_invalidate_arrow (GtkCalendar *widget,
383 static void calendar_compute_days (GtkCalendar *calendar);
385 static char *default_abbreviated_dayname[7];
386 static char *default_monthname[12];
388 G_DEFINE_TYPE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET)
391 gtk_calendar_class_init (GtkCalendarClass *class)
393 GObjectClass *gobject_class;
394 GtkObjectClass *object_class;
395 GtkWidgetClass *widget_class;
397 gobject_class = (GObjectClass*) class;
398 object_class = (GtkObjectClass*) class;
399 widget_class = (GtkWidgetClass*) class;
401 gobject_class->set_property = gtk_calendar_set_property;
402 gobject_class->get_property = gtk_calendar_get_property;
403 gobject_class->finalize = gtk_calendar_finalize;
405 object_class->destroy = gtk_calendar_destroy;
407 widget_class->realize = gtk_calendar_realize;
408 widget_class->unrealize = gtk_calendar_unrealize;
409 widget_class->expose_event = gtk_calendar_expose;
410 widget_class->size_request = gtk_calendar_size_request;
411 widget_class->size_allocate = gtk_calendar_size_allocate;
412 widget_class->button_press_event = gtk_calendar_button_press;
413 widget_class->button_release_event = gtk_calendar_button_release;
414 widget_class->motion_notify_event = gtk_calendar_motion_notify;
415 widget_class->enter_notify_event = gtk_calendar_enter_notify;
416 widget_class->leave_notify_event = gtk_calendar_leave_notify;
417 widget_class->key_press_event = gtk_calendar_key_press;
418 widget_class->scroll_event = gtk_calendar_scroll;
419 widget_class->style_set = gtk_calendar_style_set;
420 widget_class->state_changed = gtk_calendar_state_changed;
421 widget_class->grab_notify = gtk_calendar_grab_notify;
422 widget_class->focus_out_event = gtk_calendar_focus_out;
423 widget_class->query_tooltip = gtk_calendar_query_tooltip;
425 widget_class->drag_data_get = gtk_calendar_drag_data_get;
426 widget_class->drag_motion = gtk_calendar_drag_motion;
427 widget_class->drag_leave = gtk_calendar_drag_leave;
428 widget_class->drag_drop = gtk_calendar_drag_drop;
429 widget_class->drag_data_received = gtk_calendar_drag_data_received;
435 * This property gets initially set to the current year.
437 g_object_class_install_property (gobject_class,
439 g_param_spec_int ("year",
441 P_("The selected year"),
443 GTK_PARAM_READWRITE));
448 * The selected month (as a number between 0 and 11).
449 * This property gets initially set to the current month.
451 g_object_class_install_property (gobject_class,
453 g_param_spec_int ("month",
455 P_("The selected month (as a number between 0 and 11)"),
457 GTK_PARAM_READWRITE));
462 * The selected day (as a number between 1 and 31, or 0
463 * to unselect the currently selected day).
464 * This property gets initially set to the current day.
466 g_object_class_install_property (gobject_class,
468 g_param_spec_int ("day",
470 P_("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
472 GTK_PARAM_READWRITE));
475 * GtkCalendar:show-heading:
477 * Determines whether a heading is displayed.
481 g_object_class_install_property (gobject_class,
483 g_param_spec_boolean ("show-heading",
485 P_("If TRUE, a heading is displayed"),
487 GTK_PARAM_READWRITE));
490 * GtkCalendar:show-day-names:
492 * Determines whether day names are displayed.
496 g_object_class_install_property (gobject_class,
498 g_param_spec_boolean ("show-day-names",
499 P_("Show Day Names"),
500 P_("If TRUE, day names are displayed"),
502 GTK_PARAM_READWRITE));
504 * GtkCalendar:no-month-change:
506 * Determines whether the selected month can be changed.
510 g_object_class_install_property (gobject_class,
511 PROP_NO_MONTH_CHANGE,
512 g_param_spec_boolean ("no-month-change",
513 P_("No Month Change"),
514 P_("If TRUE, the selected month cannot be changed"),
516 GTK_PARAM_READWRITE));
519 * GtkCalendar:show-week-numbers:
521 * Determines whether week numbers are displayed.
525 g_object_class_install_property (gobject_class,
526 PROP_SHOW_WEEK_NUMBERS,
527 g_param_spec_boolean ("show-week-numbers",
528 P_("Show Week Numbers"),
529 P_("If TRUE, week numbers are displayed"),
531 GTK_PARAM_READWRITE));
534 * GtkCalendar:detail-width-chars:
536 * Width of a detail cell, in characters.
537 * A value of 0 allows any width. See gtk_calendar_set_detail_func().
541 g_object_class_install_property (gobject_class,
542 PROP_DETAIL_WIDTH_CHARS,
543 g_param_spec_int ("detail-width-chars",
545 P_("Details width in characters"),
547 GTK_PARAM_READWRITE));
550 * GtkCalendar:detail-height-rows:
552 * Height of a detail cell, in rows.
553 * A value of 0 allows any width. See gtk_calendar_set_detail_func().
557 g_object_class_install_property (gobject_class,
558 PROP_DETAIL_HEIGHT_ROWS,
559 g_param_spec_int ("detail-height-rows",
560 P_("Details Height"),
561 P_("Details height in rows"),
563 GTK_PARAM_READWRITE));
566 * GtkCalendar:show-details:
568 * Determines whether details are shown directly in the widget, or if they are
569 * available only as tooltip. When this property is set days with details are
574 g_object_class_install_property (gobject_class,
576 g_param_spec_boolean ("show-details",
578 P_("If TRUE, details are shown"),
580 GTK_PARAM_READWRITE));
582 gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
583 g_signal_new (I_("month-changed"),
584 G_OBJECT_CLASS_TYPE (gobject_class),
586 G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
588 _gtk_marshal_VOID__VOID,
590 gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
591 g_signal_new (I_("day-selected"),
592 G_OBJECT_CLASS_TYPE (gobject_class),
594 G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
596 _gtk_marshal_VOID__VOID,
598 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
599 g_signal_new (I_("day-selected-double-click"),
600 G_OBJECT_CLASS_TYPE (gobject_class),
602 G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
604 _gtk_marshal_VOID__VOID,
606 gtk_calendar_signals[PREV_MONTH_SIGNAL] =
607 g_signal_new (I_("prev-month"),
608 G_OBJECT_CLASS_TYPE (gobject_class),
610 G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
612 _gtk_marshal_VOID__VOID,
614 gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
615 g_signal_new (I_("next-month"),
616 G_OBJECT_CLASS_TYPE (gobject_class),
618 G_STRUCT_OFFSET (GtkCalendarClass, next_month),
620 _gtk_marshal_VOID__VOID,
622 gtk_calendar_signals[PREV_YEAR_SIGNAL] =
623 g_signal_new (I_("prev-year"),
624 G_OBJECT_CLASS_TYPE (gobject_class),
626 G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
628 _gtk_marshal_VOID__VOID,
630 gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
631 g_signal_new (I_("next-year"),
632 G_OBJECT_CLASS_TYPE (gobject_class),
634 G_STRUCT_OFFSET (GtkCalendarClass, next_year),
636 _gtk_marshal_VOID__VOID,
639 g_type_class_add_private (gobject_class, sizeof (GtkCalendarPrivate));
643 gtk_calendar_init (GtkCalendar *calendar)
645 GtkWidget *widget = GTK_WIDGET (calendar);
650 wchar_t wbuffer[100];
655 GtkCalendarPrivate *priv;
657 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
658 union { unsigned int word; char *string; } langinfo;
659 gint week_1stday = 0;
660 gint first_weekday = 1;
666 priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
670 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
672 if (!default_abbreviated_dayname[0])
676 tmp_time= (i+3)*86400;
677 strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
678 default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
680 if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
681 wbuffer, G_N_ELEMENTS (wbuffer)))
682 default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
684 default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
688 if (!default_monthname[0])
693 strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
694 default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
696 if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
697 wbuffer, G_N_ELEMENTS (wbuffer)))
698 default_monthname[i] = g_strdup_printf ("(%d)", i);
700 default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
706 tm = localtime (&secs);
707 calendar->month = tm->tm_mon;
708 calendar->year = 1900 + tm->tm_year;
711 calendar->marked_date[i] = FALSE;
712 calendar->num_marked_dates = 0;
713 calendar->selected_day = tm->tm_mday;
715 calendar->display_flags = (GTK_CALENDAR_SHOW_HEADING |
716 GTK_CALENDAR_SHOW_DAY_NAMES |
717 GTK_CALENDAR_SHOW_DETAILS);
719 calendar->highlight_row = -1;
720 calendar->highlight_col = -1;
722 calendar->focus_row = -1;
723 calendar->focus_col = -1;
725 priv->max_year_width = 0;
726 priv->max_month_width = 0;
727 priv->max_day_char_width = 0;
728 priv->max_week_char_width = 0;
730 priv->max_day_char_ascent = 0;
731 priv->max_day_char_descent = 0;
732 priv->max_label_char_ascent = 0;
733 priv->max_label_char_descent = 0;
735 priv->arrow_width = 10;
737 priv->need_timer = 0;
739 priv->click_child = -1;
742 priv->drag_highlight = 0;
744 gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
745 gtk_drag_dest_add_text_targets (widget);
747 priv->year_before = 0;
749 /* Translate to calendar:YM if you want years to be displayed
750 * before months; otherwise translate to calendar:MY.
751 * Do *not* translate it to anything else, if it
752 * it isn't calendar:YM or calendar:MY it will not work.
754 * Note that this flipping is in top of the text direction flipping,
755 * so if you have a default text direction of RTL and YM, then
756 * the year will appear on the right.
758 year_before = _("calendar:MY");
759 if (strcmp (year_before, "calendar:YM") == 0)
760 priv->year_before = 1;
761 else if (strcmp (year_before, "calendar:MY") != 0)
762 g_warning ("Whoever translated calendar:MY did so wrongly.\n");
765 priv->week_start = 0;
768 if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
769 wbuffer, G_N_ELEMENTS (wbuffer)))
770 week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
772 if (week_start != NULL)
774 priv->week_start = (week_start[0] - '0' + 1) % 7;
778 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
779 langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
780 first_weekday = langinfo.string[0];
781 langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
782 week_origin = langinfo.word;
783 if (week_origin == 19971130) /* Sunday */
785 else if (week_origin == 19971201) /* Monday */
788 g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
790 priv->week_start = (week_1stday + first_weekday - 1) % 7;
792 /* Translate to calendar:week_start:0 if you want Sunday to be the
793 * first day of the week to calendar:week_start:1 if you want Monday
794 * to be the first day of the week, and so on.
796 week_start = _("calendar:week_start:0");
798 if (strncmp (week_start, "calendar:week_start:", 20) == 0)
799 priv->week_start = *(week_start + 20) - '0';
801 priv->week_start = -1;
803 if (priv->week_start < 0 || priv->week_start > 6)
805 g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
806 priv->week_start = 0;
811 calendar_compute_days (calendar);
815 /****************************************
816 * Utility Functions *
817 ****************************************/
820 calendar_queue_refresh (GtkCalendar *calendar)
822 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
824 if (!(priv->detail_func) ||
825 !(calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS) ||
826 (priv->detail_width_chars && priv->detail_height_rows))
827 gtk_widget_queue_draw (GTK_WIDGET (calendar));
829 gtk_widget_queue_resize (GTK_WIDGET (calendar));
833 calendar_set_month_next (GtkCalendar *calendar)
837 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
841 if (calendar->month == 11)
849 calendar_compute_days (calendar);
850 g_signal_emit (calendar,
851 gtk_calendar_signals[NEXT_MONTH_SIGNAL],
853 g_signal_emit (calendar,
854 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
857 month_len = month_length[leap (calendar->year)][calendar->month + 1];
859 if (month_len < calendar->selected_day)
861 calendar->selected_day = 0;
862 gtk_calendar_select_day (calendar, month_len);
865 gtk_calendar_select_day (calendar, calendar->selected_day);
867 calendar_queue_refresh (calendar);
871 calendar_set_year_prev (GtkCalendar *calendar)
876 calendar_compute_days (calendar);
877 g_signal_emit (calendar,
878 gtk_calendar_signals[PREV_YEAR_SIGNAL],
880 g_signal_emit (calendar,
881 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
884 month_len = month_length[leap (calendar->year)][calendar->month + 1];
886 if (month_len < calendar->selected_day)
888 calendar->selected_day = 0;
889 gtk_calendar_select_day (calendar, month_len);
892 gtk_calendar_select_day (calendar, calendar->selected_day);
894 calendar_queue_refresh (calendar);
898 calendar_set_year_next (GtkCalendar *calendar)
903 calendar_compute_days (calendar);
904 g_signal_emit (calendar,
905 gtk_calendar_signals[NEXT_YEAR_SIGNAL],
907 g_signal_emit (calendar,
908 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
911 month_len = month_length[leap (calendar->year)][calendar->month + 1];
913 if (month_len < calendar->selected_day)
915 calendar->selected_day = 0;
916 gtk_calendar_select_day (calendar, month_len);
919 gtk_calendar_select_day (calendar, calendar->selected_day);
921 calendar_queue_refresh (calendar);
925 calendar_compute_days (GtkCalendar *calendar)
927 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
931 gint ndays_in_prev_month;
937 year = calendar->year;
938 month = calendar->month + 1;
940 ndays_in_month = month_length[leap (year)][month];
942 first_day = day_of_week (year, month, 1);
943 first_day = (first_day + 7 - priv->week_start) % 7;
945 /* Compute days of previous month */
947 ndays_in_prev_month = month_length[leap (year)][month-1];
949 ndays_in_prev_month = month_length[leap (year)][12];
950 day = ndays_in_prev_month - first_day + 1;
955 for (col = 0; col < first_day; col++)
957 calendar->day[row][col] = day;
958 calendar->day_month[row][col] = MONTH_PREV;
963 /* Compute days of current month */
965 for (day = 1; day <= ndays_in_month; day++)
967 calendar->day[row][col] = day;
968 calendar->day_month[row][col] = MONTH_CURRENT;
978 /* Compute days of next month */
980 for (; row <= 5; row++)
982 for (; col <= 6; col++)
984 calendar->day[row][col] = day;
985 calendar->day_month[row][col] = MONTH_NEXT;
993 calendar_select_and_focus_day (GtkCalendar *calendar,
996 gint old_focus_row = calendar->focus_row;
997 gint old_focus_col = calendar->focus_col;
1001 for (row = 0; row < 6; row ++)
1002 for (col = 0; col < 7; col++)
1004 if (calendar->day_month[row][col] == MONTH_CURRENT
1005 && calendar->day[row][col] == day)
1007 calendar->focus_row = row;
1008 calendar->focus_col = col;
1012 if (old_focus_row != -1 && old_focus_col != -1)
1013 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
1015 gtk_calendar_select_day (calendar, day);
1019 /****************************************
1020 * Layout computation utilities *
1021 ****************************************/
1024 calendar_row_height (GtkCalendar *calendar)
1026 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
1027 - ((calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1028 ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
1032 /* calendar_left_x_for_column: returns the x coordinate
1033 * for the left of the column */
1035 calendar_left_x_for_column (GtkCalendar *calendar,
1041 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1042 column = 6 - column;
1044 width = GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1045 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1046 x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
1048 x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
1053 /* column_from_x: returns the column 0-6 that the
1054 * x pixel of the xwindow is in */
1056 calendar_column_from_x (GtkCalendar *calendar,
1060 gint x_left, x_right;
1064 for (c = 0; c < 7; c++)
1066 x_left = calendar_left_x_for_column (calendar, c);
1067 x_right = x_left + GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1069 if (event_x >= x_left && event_x < x_right)
1079 /* calendar_top_y_for_row: returns the y coordinate
1080 * for the top of the row */
1082 calendar_top_y_for_row (GtkCalendar *calendar,
1086 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h
1087 - (CALENDAR_MARGIN + (6 - row)
1088 * calendar_row_height (calendar)));
1091 /* row_from_y: returns the row 0-5 that the
1092 * y pixel of the xwindow is in */
1094 calendar_row_from_y (GtkCalendar *calendar,
1099 gint y_top, y_bottom;
1101 height = calendar_row_height (calendar);
1104 for (r = 0; r < 6; r++)
1106 y_top = calendar_top_y_for_row (calendar, r);
1107 y_bottom = y_top + height;
1109 if (event_y >= y_top && event_y < y_bottom)
1120 calendar_arrow_rectangle (GtkCalendar *calendar,
1124 GtkWidget *widget = GTK_WIDGET (calendar);
1125 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1128 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1129 year_left = priv->year_before;
1131 year_left = !priv->year_before;
1134 rect->width = priv->arrow_width;
1135 rect->height = priv->header_h - 7;
1139 case ARROW_MONTH_LEFT:
1141 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1142 - (3 + 2*priv->arrow_width
1143 + priv->max_month_width));
1147 case ARROW_MONTH_RIGHT:
1149 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1150 - 3 - priv->arrow_width);
1152 rect->x = (priv->arrow_width
1153 + priv->max_month_width);
1155 case ARROW_YEAR_LEFT:
1159 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1160 - (3 + 2*priv->arrow_width
1161 + priv->max_year_width));
1163 case ARROW_YEAR_RIGHT:
1165 rect->x = (priv->arrow_width
1166 + priv->max_year_width);
1168 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1169 - 3 - priv->arrow_width);
1175 calendar_day_rectangle (GtkCalendar *calendar,
1180 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1182 rect->x = calendar_left_x_for_column (calendar, col);
1183 rect->y = calendar_top_y_for_row (calendar, row);
1184 rect->height = calendar_row_height (calendar);
1185 rect->width = priv->day_width;
1189 calendar_set_month_prev (GtkCalendar *calendar)
1193 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1196 if (calendar->month == 0)
1198 calendar->month = 11;
1204 month_len = month_length[leap (calendar->year)][calendar->month + 1];
1206 calendar_compute_days (calendar);
1208 g_signal_emit (calendar,
1209 gtk_calendar_signals[PREV_MONTH_SIGNAL],
1211 g_signal_emit (calendar,
1212 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1215 if (month_len < calendar->selected_day)
1217 calendar->selected_day = 0;
1218 gtk_calendar_select_day (calendar, month_len);
1222 if (calendar->selected_day < 0)
1223 calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
1224 gtk_calendar_select_day (calendar, calendar->selected_day);
1227 calendar_queue_refresh (calendar);
1231 /****************************************
1232 * Basic object methods *
1233 ****************************************/
1236 gtk_calendar_finalize (GObject *object)
1238 G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize (object);
1242 gtk_calendar_destroy (GtkObject *object)
1244 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1246 calendar_stop_spinning (GTK_CALENDAR (object));
1248 /* Call the destroy function for the extra display callback: */
1249 if (priv->detail_func_destroy && priv->detail_func_user_data)
1251 priv->detail_func_destroy (priv->detail_func_user_data);
1252 priv->detail_func_user_data = NULL;
1253 priv->detail_func_destroy = NULL;
1256 GTK_OBJECT_CLASS (gtk_calendar_parent_class)->destroy (object);
1261 calendar_set_display_option (GtkCalendar *calendar,
1262 GtkCalendarDisplayOptions flag,
1265 GtkCalendarDisplayOptions flags;
1267 flags = calendar->display_flags | flag;
1269 flags = calendar->display_flags & ~flag;
1270 gtk_calendar_set_display_options (calendar, flags);
1274 calendar_get_display_option (GtkCalendar *calendar,
1275 GtkCalendarDisplayOptions flag)
1277 return (calendar->display_flags & flag) != 0;
1281 gtk_calendar_set_property (GObject *object,
1283 const GValue *value,
1286 GtkCalendar *calendar;
1288 calendar = GTK_CALENDAR (object);
1293 gtk_calendar_select_month (calendar,
1295 g_value_get_int (value));
1298 gtk_calendar_select_month (calendar,
1299 g_value_get_int (value),
1303 gtk_calendar_select_day (calendar,
1304 g_value_get_int (value));
1306 case PROP_SHOW_HEADING:
1307 calendar_set_display_option (calendar,
1308 GTK_CALENDAR_SHOW_HEADING,
1309 g_value_get_boolean (value));
1311 case PROP_SHOW_DAY_NAMES:
1312 calendar_set_display_option (calendar,
1313 GTK_CALENDAR_SHOW_DAY_NAMES,
1314 g_value_get_boolean (value));
1316 case PROP_NO_MONTH_CHANGE:
1317 calendar_set_display_option (calendar,
1318 GTK_CALENDAR_NO_MONTH_CHANGE,
1319 g_value_get_boolean (value));
1321 case PROP_SHOW_WEEK_NUMBERS:
1322 calendar_set_display_option (calendar,
1323 GTK_CALENDAR_SHOW_WEEK_NUMBERS,
1324 g_value_get_boolean (value));
1326 case PROP_SHOW_DETAILS:
1327 calendar_set_display_option (calendar,
1328 GTK_CALENDAR_SHOW_DETAILS,
1329 g_value_get_boolean (value));
1331 case PROP_DETAIL_WIDTH_CHARS:
1332 gtk_calendar_set_detail_width_chars (calendar,
1333 g_value_get_int (value));
1335 case PROP_DETAIL_HEIGHT_ROWS:
1336 gtk_calendar_set_detail_height_rows (calendar,
1337 g_value_get_int (value));
1340 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1346 gtk_calendar_get_property (GObject *object,
1351 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1352 GtkCalendar *calendar = GTK_CALENDAR (object);
1357 g_value_set_int (value, calendar->year);
1360 g_value_set_int (value, calendar->month);
1363 g_value_set_int (value, calendar->selected_day);
1365 case PROP_SHOW_HEADING:
1366 g_value_set_boolean (value, calendar_get_display_option (calendar,
1367 GTK_CALENDAR_SHOW_HEADING));
1369 case PROP_SHOW_DAY_NAMES:
1370 g_value_set_boolean (value, calendar_get_display_option (calendar,
1371 GTK_CALENDAR_SHOW_DAY_NAMES));
1373 case PROP_NO_MONTH_CHANGE:
1374 g_value_set_boolean (value, calendar_get_display_option (calendar,
1375 GTK_CALENDAR_NO_MONTH_CHANGE));
1377 case PROP_SHOW_WEEK_NUMBERS:
1378 g_value_set_boolean (value, calendar_get_display_option (calendar,
1379 GTK_CALENDAR_SHOW_WEEK_NUMBERS));
1381 case PROP_SHOW_DETAILS:
1382 g_value_set_boolean (value, calendar_get_display_option (calendar,
1383 GTK_CALENDAR_SHOW_DETAILS));
1385 case PROP_DETAIL_WIDTH_CHARS:
1386 g_value_set_int (value, priv->detail_width_chars);
1388 case PROP_DETAIL_HEIGHT_ROWS:
1389 g_value_set_int (value, priv->detail_height_rows);
1392 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1398 /****************************************
1400 ****************************************/
1403 calendar_realize_arrows (GtkCalendar *calendar)
1405 GtkWidget *widget = GTK_WIDGET (calendar);
1406 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1407 GdkWindowAttr attributes;
1408 gint attributes_mask;
1411 /* Arrow windows ------------------------------------- */
1412 if (! (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1413 && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
1415 attributes.wclass = GDK_INPUT_OUTPUT;
1416 attributes.window_type = GDK_WINDOW_CHILD;
1417 attributes.visual = gtk_widget_get_visual (widget);
1418 attributes.colormap = gtk_widget_get_colormap (widget);
1419 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1420 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1421 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1422 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1423 for (i = 0; i < 4; i++)
1426 calendar_arrow_rectangle (calendar, i, &rect);
1428 attributes.x = rect.x;
1429 attributes.y = rect.y;
1430 attributes.width = rect.width;
1431 attributes.height = rect.height;
1432 priv->arrow_win[i] = gdk_window_new (priv->header_win,
1435 if (GTK_WIDGET_IS_SENSITIVE (widget))
1436 priv->arrow_state[i] = GTK_STATE_NORMAL;
1438 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
1439 gdk_window_set_background (priv->arrow_win[i],
1440 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1441 gdk_window_show (priv->arrow_win[i]);
1442 gdk_window_set_user_data (priv->arrow_win[i], widget);
1447 for (i = 0; i < 4; i++)
1448 priv->arrow_win[i] = NULL;
1453 calendar_realize_header (GtkCalendar *calendar)
1455 GtkWidget *widget = GTK_WIDGET (calendar);
1456 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1457 GdkWindowAttr attributes;
1458 gint attributes_mask;
1460 /* Header window ------------------------------------- */
1461 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1463 attributes.wclass = GDK_INPUT_OUTPUT;
1464 attributes.window_type = GDK_WINDOW_CHILD;
1465 attributes.visual = gtk_widget_get_visual (widget);
1466 attributes.colormap = gtk_widget_get_colormap (widget);
1467 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1468 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1469 attributes.x = widget->style->xthickness;
1470 attributes.y = widget->style->ythickness;
1471 attributes.width = widget->allocation.width - 2 * attributes.x;
1472 attributes.height = priv->header_h;
1473 priv->header_win = gdk_window_new (widget->window,
1474 &attributes, attributes_mask);
1476 gdk_window_set_background (priv->header_win,
1477 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1478 gdk_window_show (priv->header_win);
1479 gdk_window_set_user_data (priv->header_win, widget);
1484 priv->header_win = NULL;
1486 calendar_realize_arrows (calendar);
1490 calendar_realize_day_names (GtkCalendar *calendar)
1492 GtkWidget *widget = GTK_WIDGET (calendar);
1493 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1494 GdkWindowAttr attributes;
1495 gint attributes_mask;
1497 /* Day names window --------------------------------- */
1498 if ( calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1500 attributes.wclass = GDK_INPUT_OUTPUT;
1501 attributes.window_type = GDK_WINDOW_CHILD;
1502 attributes.visual = gtk_widget_get_visual (widget);
1503 attributes.colormap = gtk_widget_get_colormap (widget);
1504 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1505 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1506 attributes.x = (widget->style->xthickness + INNER_BORDER);
1507 attributes.y = priv->header_h + (widget->style->ythickness
1509 attributes.width = (widget->allocation.width
1510 - (widget->style->xthickness + INNER_BORDER)
1512 attributes.height = priv->day_name_h;
1513 priv->day_name_win = gdk_window_new (widget->window,
1516 gdk_window_set_background (priv->day_name_win,
1517 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1518 gdk_window_show (priv->day_name_win);
1519 gdk_window_set_user_data (priv->day_name_win, widget);
1523 priv->day_name_win = NULL;
1528 calendar_realize_week_numbers (GtkCalendar *calendar)
1530 GtkWidget *widget = GTK_WIDGET (calendar);
1531 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1532 GdkWindowAttr attributes;
1533 gint attributes_mask;
1535 /* Week number window -------------------------------- */
1536 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1538 attributes.wclass = GDK_INPUT_OUTPUT;
1539 attributes.window_type = GDK_WINDOW_CHILD;
1540 attributes.visual = gtk_widget_get_visual (widget);
1541 attributes.colormap = gtk_widget_get_colormap (widget);
1542 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1544 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1545 attributes.x = widget->style->xthickness + INNER_BORDER;
1546 attributes.y = (priv->header_h + priv->day_name_h
1547 + (widget->style->ythickness + INNER_BORDER));
1548 attributes.width = priv->week_width;
1549 attributes.height = priv->main_h;
1550 priv->week_win = gdk_window_new (widget->window,
1551 &attributes, attributes_mask);
1552 gdk_window_set_background (priv->week_win,
1553 BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1554 gdk_window_show (priv->week_win);
1555 gdk_window_set_user_data (priv->week_win, widget);
1559 priv->week_win = NULL;
1564 gtk_calendar_realize (GtkWidget *widget)
1566 GtkCalendar *calendar = GTK_CALENDAR (widget);
1567 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1568 GdkWindowAttr attributes;
1569 gint attributes_mask;
1571 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1573 attributes.x = widget->allocation.x;
1574 attributes.y = widget->allocation.y;
1575 attributes.width = widget->allocation.width;
1576 attributes.height = widget->allocation.height;
1577 attributes.wclass = GDK_INPUT_OUTPUT;
1578 attributes.window_type = GDK_WINDOW_CHILD;
1579 attributes.event_mask = (gtk_widget_get_events (widget)
1580 | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1581 attributes.visual = gtk_widget_get_visual (widget);
1582 attributes.colormap = gtk_widget_get_colormap (widget);
1584 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1585 widget->window = gdk_window_new (widget->parent->window,
1586 &attributes, attributes_mask);
1588 widget->style = gtk_style_attach (widget->style, widget->window);
1590 /* Header window ------------------------------------- */
1591 calendar_realize_header (calendar);
1592 /* Day names window --------------------------------- */
1593 calendar_realize_day_names (calendar);
1594 /* Week number window -------------------------------- */
1595 calendar_realize_week_numbers (calendar);
1596 /* Main Window -------------------------------------- */
1597 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1598 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1599 | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1601 attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
1602 attributes.y = (priv->header_h + priv->day_name_h
1603 + (widget->style->ythickness + INNER_BORDER));
1604 attributes.width = (widget->allocation.width - attributes.x
1605 - (widget->style->xthickness + INNER_BORDER));
1606 attributes.height = priv->main_h;
1607 priv->main_win = gdk_window_new (widget->window,
1608 &attributes, attributes_mask);
1609 gdk_window_set_background (priv->main_win,
1610 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1611 gdk_window_show (priv->main_win);
1612 gdk_window_set_user_data (priv->main_win, widget);
1613 gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1614 gdk_window_show (widget->window);
1615 gdk_window_set_user_data (widget->window, widget);
1619 gtk_calendar_unrealize (GtkWidget *widget)
1621 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1624 if (priv->header_win)
1626 for (i = 0; i < 4; i++)
1628 if (priv->arrow_win[i])
1630 gdk_window_set_user_data (priv->arrow_win[i], NULL);
1631 gdk_window_destroy (priv->arrow_win[i]);
1632 priv->arrow_win[i] = NULL;
1635 gdk_window_set_user_data (priv->header_win, NULL);
1636 gdk_window_destroy (priv->header_win);
1637 priv->header_win = NULL;
1642 gdk_window_set_user_data (priv->week_win, NULL);
1643 gdk_window_destroy (priv->week_win);
1644 priv->week_win = NULL;
1649 gdk_window_set_user_data (priv->main_win, NULL);
1650 gdk_window_destroy (priv->main_win);
1651 priv->main_win = NULL;
1653 if (priv->day_name_win)
1655 gdk_window_set_user_data (priv->day_name_win, NULL);
1656 gdk_window_destroy (priv->day_name_win);
1657 priv->day_name_win = NULL;
1660 if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize)
1661 GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize (widget);
1665 gtk_calendar_get_detail (GtkCalendar *calendar,
1669 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1672 year = calendar->year;
1673 month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
1680 else if (month > 11)
1686 return priv->detail_func (calendar,
1688 calendar->day[row][column],
1689 priv->detail_func_user_data);
1693 gtk_calendar_query_tooltip (GtkWidget *widget,
1696 gboolean keyboard_mode,
1697 GtkTooltip *tooltip)
1699 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1700 GtkCalendar *calendar = GTK_CALENDAR (widget);
1701 gchar *detail = NULL;
1702 GdkRectangle day_rect;
1706 gint x0, y0, row, col;
1708 gdk_window_get_position (priv->main_win, &x0, &y0);
1709 col = calendar_column_from_x (calendar, x - x0);
1710 row = calendar_row_from_y (calendar, y - y0);
1712 if (col != -1 && row != -1 &&
1713 (0 != (priv->detail_overflow[row] & (1 << col)) ||
1714 0 == (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS)))
1716 detail = gtk_calendar_get_detail (calendar, row, col);
1717 calendar_day_rectangle (calendar, row, col, &day_rect);
1726 gtk_tooltip_set_tip_area (tooltip, &day_rect);
1727 gtk_tooltip_set_markup (tooltip, detail);
1734 if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip)
1735 return GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
1741 /****************************************
1742 * Size Request and Allocate *
1743 ****************************************/
1746 gtk_calendar_size_request (GtkWidget *widget,
1747 GtkRequisition *requisition)
1749 GtkCalendar *calendar = GTK_CALENDAR (widget);
1750 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1751 PangoLayout *layout;
1752 PangoRectangle logical_rect;
1756 gint calendar_margin = CALENDAR_MARGIN;
1757 gint header_width, main_width;
1758 gint max_header_height = 0;
1761 gint max_detail_height;
1763 gtk_widget_style_get (GTK_WIDGET (widget),
1764 "focus-line-width", &focus_width,
1765 "focus-padding", &focus_padding,
1768 layout = gtk_widget_create_pango_layout (widget, NULL);
1771 * Calculate the requisition width for the widget.
1776 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1778 priv->max_month_width = 0;
1779 for (i = 0; i < 12; i++)
1781 pango_layout_set_text (layout, default_monthname[i], -1);
1782 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1783 priv->max_month_width = MAX (priv->max_month_width,
1784 logical_rect.width + 8);
1785 max_header_height = MAX (max_header_height, logical_rect.height);
1788 priv->max_year_width = 0;
1789 /* Translators: This is a text measurement template.
1790 * Translate it to the widest year text.
1792 * Don't include the prefix "year measurement template|"
1793 * in the translation.
1795 * If you don't understand this, leave it as "2000"
1797 pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);
1798 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1799 priv->max_year_width = MAX (priv->max_year_width,
1800 logical_rect.width + 8);
1801 max_header_height = MAX (max_header_height, logical_rect.height);
1805 priv->max_month_width = 0;
1806 priv->max_year_width = 0;
1809 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1810 header_width = (priv->max_month_width
1811 + priv->max_year_width
1814 header_width = (priv->max_month_width
1815 + priv->max_year_width
1816 + 4 * priv->arrow_width + 3 * 3);
1818 /* Mainwindow labels width */
1820 priv->max_day_char_width = 0;
1821 priv->max_day_char_ascent = 0;
1822 priv->max_day_char_descent = 0;
1823 priv->min_day_width = 0;
1825 for (i = 0; i < 9; i++)
1828 g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), i * 11);
1829 pango_layout_set_text (layout, buffer, -1);
1830 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1831 priv->min_day_width = MAX (priv->min_day_width,
1832 logical_rect.width);
1834 priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1835 PANGO_ASCENT (logical_rect));
1836 priv->max_day_char_descent = MAX (priv->max_day_char_descent,
1837 PANGO_DESCENT (logical_rect));
1840 priv->max_label_char_ascent = 0;
1841 priv->max_label_char_descent = 0;
1842 if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1843 for (i = 0; i < 7; i++)
1845 pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1846 pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
1848 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1849 priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1850 PANGO_ASCENT (logical_rect));
1851 priv->max_label_char_descent = MAX (priv->max_label_char_descent,
1852 PANGO_DESCENT (logical_rect));
1855 priv->max_week_char_width = 0;
1856 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1857 for (i = 0; i < 9; i++)
1860 g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), i * 11);
1861 pango_layout_set_text (layout, buffer, -1);
1862 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1863 priv->max_week_char_width = MAX (priv->max_week_char_width,
1864 logical_rect.width / 2);
1867 /* Calculate detail extents. Do this as late as possible since
1868 * pango_layout_set_markup is called which alters font settings. */
1869 max_detail_height = 0;
1871 if (priv->detail_func && (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS))
1873 gchar *markup, *tail;
1875 if (priv->detail_width_chars || priv->detail_height_rows)
1877 gint rows = MAX (1, priv->detail_height_rows) - 1;
1878 gsize len = priv->detail_width_chars + rows + 16;
1880 markup = tail = g_alloca (len);
1882 memcpy (tail, "<small>", 7);
1885 memset (tail, 'm', priv->detail_width_chars);
1886 tail += priv->detail_width_chars;
1888 memset (tail, '\n', rows);
1891 memcpy (tail, "</small>", 9);
1894 g_assert (len == (tail - markup));
1896 pango_layout_set_markup (layout, markup, -1);
1897 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1899 if (priv->detail_width_chars)
1900 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1901 if (priv->detail_height_rows)
1902 max_detail_height = MAX (max_detail_height, logical_rect.height);
1905 if (!priv->detail_width_chars || !priv->detail_height_rows)
1906 for (r = 0; r < 6; r++)
1907 for (c = 0; c < 7; c++)
1909 gchar *detail = gtk_calendar_get_detail (calendar, r, c);
1913 markup = g_strconcat ("<small>", detail, "</small>", NULL);
1914 pango_layout_set_markup (layout, markup, -1);
1916 if (priv->detail_width_chars)
1918 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1919 pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
1922 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1924 if (!priv->detail_width_chars)
1925 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1926 if (!priv->detail_height_rows)
1927 max_detail_height = MAX (max_detail_height, logical_rect.height);
1935 /* We add one to max_day_char_width to be able to make the marked day "bold" */
1936 priv->max_day_char_width = priv->min_day_width / 2 + 1;
1938 main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1939 + (priv->max_week_char_width
1940 ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1944 requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1947 * Calculate the requisition height for the widget.
1950 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1952 priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1959 if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1961 priv->day_name_h = (priv->max_label_char_ascent
1962 + priv->max_label_char_descent
1963 + 2 * (focus_padding + focus_width) + calendar_margin);
1964 calendar_margin = CALENDAR_YSEP;
1968 priv->day_name_h = 0;
1971 priv->main_h = (CALENDAR_MARGIN + calendar_margin
1972 + 6 * (priv->max_day_char_ascent
1973 + priv->max_day_char_descent
1975 + 2 * (focus_padding + focus_width))
1978 height = (priv->header_h + priv->day_name_h
1981 requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1983 g_object_unref (layout);
1987 gtk_calendar_size_allocate (GtkWidget *widget,
1988 GtkAllocation *allocation)
1990 GtkCalendar *calendar = GTK_CALENDAR (widget);
1991 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1992 gint xthickness = widget->style->xthickness;
1993 gint ythickness = widget->style->xthickness;
1996 widget->allocation = *allocation;
1998 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2000 priv->day_width = (priv->min_day_width
2001 * ((allocation->width - (xthickness + INNER_BORDER) * 2
2002 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
2003 / (7 * priv->min_day_width + priv->max_week_char_width * 2));
2004 priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
2005 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
2006 - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
2010 priv->day_width = (allocation->width
2011 - (xthickness + INNER_BORDER) * 2
2012 - (CALENDAR_MARGIN * 2)
2013 - (DAY_XSEP * 6))/7;
2014 priv->week_width = 0;
2017 if (GTK_WIDGET_REALIZED (widget))
2019 gdk_window_move_resize (widget->window,
2020 allocation->x, allocation->y,
2021 allocation->width, allocation->height);
2022 if (priv->header_win)
2023 gdk_window_move_resize (priv->header_win,
2024 xthickness, ythickness,
2025 allocation->width - 2 * xthickness, priv->header_h);
2027 for (i = 0 ; i < 4 ; i++)
2029 if (priv->arrow_win[i])
2032 calendar_arrow_rectangle (calendar, i, &rect);
2034 gdk_window_move_resize (priv->arrow_win[i],
2035 rect.x, rect.y, rect.width, rect.height);
2039 if (priv->day_name_win)
2040 gdk_window_move_resize (priv->day_name_win,
2041 xthickness + INNER_BORDER,
2042 priv->header_h + (widget->style->ythickness + INNER_BORDER),
2043 allocation->width - (xthickness + INNER_BORDER) * 2,
2045 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2048 gdk_window_move_resize (priv->week_win,
2049 (xthickness + INNER_BORDER),
2050 priv->header_h + priv->day_name_h
2051 + (widget->style->ythickness + INNER_BORDER),
2054 gdk_window_move_resize (priv->main_win,
2055 priv->week_width + (xthickness + INNER_BORDER),
2056 priv->header_h + priv->day_name_h
2057 + (widget->style->ythickness + INNER_BORDER),
2060 - (xthickness + INNER_BORDER) * 2,
2065 gdk_window_move_resize (priv->main_win,
2066 (xthickness + INNER_BORDER),
2067 priv->header_h + priv->day_name_h
2068 + (widget->style->ythickness + INNER_BORDER),
2071 - (xthickness + INNER_BORDER) * 2,
2074 gdk_window_move_resize (priv->week_win,
2077 - (xthickness + INNER_BORDER),
2078 priv->header_h + priv->day_name_h
2079 + (widget->style->ythickness + INNER_BORDER),
2087 /****************************************
2089 ****************************************/
2092 calendar_paint_header (GtkCalendar *calendar)
2094 GtkWidget *widget = GTK_WIDGET (calendar);
2095 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2100 gint max_month_width;
2101 gint max_year_width;
2102 PangoLayout *layout;
2103 PangoRectangle logical_rect;
2109 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2110 year_left = priv->year_before;
2112 year_left = !priv->year_before;
2114 cr = gdk_cairo_create (priv->header_win);
2116 header_width = widget->allocation.width - 2 * widget->style->xthickness;
2118 max_month_width = priv->max_month_width;
2119 max_year_width = priv->max_year_width;
2121 gtk_paint_shadow (widget->style, priv->header_win,
2122 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2123 NULL, widget, "calendar",
2124 0, 0, header_width, priv->header_h);
2126 tmp_time = 1; /* Jan 1 1970, 00:00:01 UTC */
2127 tm = gmtime (&tmp_time);
2128 tm->tm_year = calendar->year - 1900;
2130 /* Translators: This dictates how the year is displayed in
2131 * gtkcalendar widget. See strftime() manual for the format.
2132 * Use only ASCII in the translation.
2134 * Also look for the msgid "year measurement template|2000".
2135 * Translate that entry to a year with the widest output of this
2138 * Don't include the prefix "calendar year format|" in the
2139 * translation. "%Y" is appropriate for most locales.
2141 strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
2142 str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
2143 layout = gtk_widget_create_pango_layout (widget, str);
2146 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2149 y = (priv->header_h - logical_rect.height) / 2;
2151 /* Draw year and its arrows */
2153 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2155 x = 3 + (max_year_width - logical_rect.width)/2;
2157 x = header_width - (3 + max_year_width
2158 - (max_year_width - logical_rect.width)/2);
2161 x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
2163 x = header_width - (3 + priv->arrow_width + max_year_width
2164 - (max_year_width - logical_rect.width)/2);
2167 gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2168 cairo_move_to (cr, x, y);
2169 pango_cairo_show_layout (cr, layout);
2172 g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
2173 pango_layout_set_text (layout, buffer, -1);
2174 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2176 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2178 x = header_width - (3 + max_month_width
2179 - (max_month_width - logical_rect.width)/2);
2181 x = 3 + (max_month_width - logical_rect.width) / 2;
2184 x = header_width - (3 + priv->arrow_width + max_month_width
2185 - (max_month_width - logical_rect.width)/2);
2187 x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
2189 cairo_move_to (cr, x, y);
2190 pango_cairo_show_layout (cr, layout);
2192 g_object_unref (layout);
2197 calendar_paint_day_names (GtkCalendar *calendar)
2199 GtkWidget *widget = GTK_WIDGET (calendar);
2200 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2204 int day_width, cal_width;
2206 PangoLayout *layout;
2207 PangoRectangle logical_rect;
2211 cr = gdk_cairo_create (priv->day_name_win);
2213 gtk_widget_style_get (GTK_WIDGET (widget),
2214 "focus-line-width", &focus_width,
2215 "focus-padding", &focus_padding,
2218 day_width = priv->day_width;
2219 cal_width = widget->allocation.width;
2220 day_wid_sep = day_width + DAY_XSEP;
2223 * Draw rectangles as inverted background for the labels.
2226 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2227 cairo_rectangle (cr,
2228 CALENDAR_MARGIN, CALENDAR_MARGIN,
2229 cal_width-CALENDAR_MARGIN * 2,
2230 priv->day_name_h - CALENDAR_MARGIN);
2233 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2235 cairo_rectangle (cr,
2237 priv->day_name_h - CALENDAR_YSEP,
2238 priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
2247 layout = gtk_widget_create_pango_layout (widget, NULL);
2249 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2250 for (i = 0; i < 7; i++)
2252 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2256 day = (day + priv->week_start) % 7;
2257 g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
2259 pango_layout_set_text (layout, buffer, -1);
2260 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2264 + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2265 (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2268 + (day_width - logical_rect.width)/2),
2269 CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2270 pango_cairo_show_layout (cr, layout);
2273 g_object_unref (layout);
2278 calendar_paint_week_numbers (GtkCalendar *calendar)
2280 GtkWidget *widget = GTK_WIDGET (calendar);
2281 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2284 guint week = 0, year;
2285 gint row, x_loc, y_loc;
2288 PangoLayout *layout;
2289 PangoRectangle logical_rect;
2293 cr = gdk_cairo_create (priv->week_win);
2295 gtk_widget_style_get (GTK_WIDGET (widget),
2296 "focus-line-width", &focus_width,
2297 "focus-padding", &focus_padding,
2301 * Draw a rectangle as inverted background for the labels.
2304 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2305 if (priv->day_name_win)
2306 cairo_rectangle (cr,
2309 priv->week_width - CALENDAR_MARGIN,
2310 priv->main_h - CALENDAR_MARGIN);
2312 cairo_rectangle (cr,
2315 priv->week_width - CALENDAR_MARGIN,
2316 priv->main_h - 2 * CALENDAR_MARGIN);
2323 layout = gtk_widget_create_pango_layout (widget, NULL);
2325 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2326 day_height = calendar_row_height (calendar);
2327 for (row = 0; row < 6; row++)
2331 year = calendar->year;
2332 if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2335 result = week_of_year (&week, &year,
2336 ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2337 + calendar->month) % 12 + 1, calendar->day[row][6]);
2338 g_return_if_fail (result);
2340 /* Translators: this defines whether the week numbers should use
2341 * localized digits or the ones used in English (0123...).
2343 * Translate to "%Id" if you want to use localized digits, or
2344 * translate to "%d" otherwise. Don't include the
2345 * "calendar:week:digits|" part in the translation.
2347 * Note that translating this doesn't guarantee that you get localized
2348 * digits. That needs support from your system and locale definition
2351 g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), week);
2352 pango_layout_set_text (layout, buffer, -1);
2353 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2355 y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2357 x_loc = (priv->week_width
2358 - logical_rect.width
2359 - CALENDAR_XSEP - focus_padding - focus_width);
2361 cairo_move_to (cr, x_loc, y_loc);
2362 pango_cairo_show_layout (cr, layout);
2365 g_object_unref (layout);
2370 calendar_invalidate_day_num (GtkCalendar *calendar,
2373 gint r, c, row, col;
2377 for (r = 0; r < 6; r++)
2378 for (c = 0; c < 7; c++)
2379 if (calendar->day_month[r][c] == MONTH_CURRENT &&
2380 calendar->day[r][c] == day)
2386 g_return_if_fail (row != -1);
2387 g_return_if_fail (col != -1);
2389 calendar_invalidate_day (calendar, row, col);
2393 calendar_invalidate_day (GtkCalendar *calendar,
2397 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2401 GdkRectangle day_rect;
2403 calendar_day_rectangle (calendar, row, col, &day_rect);
2404 gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2409 is_color_attribute (PangoAttribute *attribute,
2412 return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
2413 attribute->klass->type == PANGO_ATTR_BACKGROUND);
2417 calendar_paint_day (GtkCalendar *calendar,
2421 GtkWidget *widget = GTK_WIDGET (calendar);
2422 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2424 GdkColor *text_color;
2429 GdkRectangle day_rect;
2431 PangoLayout *layout;
2432 PangoRectangle logical_rect;
2433 gboolean overflow = FALSE;
2434 gboolean show_details;
2436 g_return_if_fail (row < 6);
2437 g_return_if_fail (col < 7);
2439 cr = gdk_cairo_create (priv->main_win);
2441 day = calendar->day[row][col];
2442 show_details = (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS);
2444 calendar_day_rectangle (calendar, row, col, &day_rect);
2446 if (calendar->day_month[row][col] == MONTH_PREV)
2448 text_color = PREV_MONTH_COLOR (widget);
2450 else if (calendar->day_month[row][col] == MONTH_NEXT)
2452 text_color = NEXT_MONTH_COLOR (widget);
2457 if (calendar->highlight_row == row && calendar->highlight_col == col)
2459 cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2460 gdk_cairo_rectangle (cr, &day_rect);
2464 if (calendar->selected_day == day)
2466 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2467 gdk_cairo_rectangle (cr, &day_rect);
2470 if (calendar->selected_day == day)
2471 text_color = SELECTED_FG_COLOR (widget);
2472 else if (calendar->marked_date[day-1])
2473 text_color = MARKED_COLOR (widget);
2475 text_color = NORMAL_DAY_COLOR (widget);
2478 /* Translators: this defines whether the day numbers should use
2479 * localized digits or the ones used in English (0123...).
2481 * Translate to "%Id" if you want to use localized digits, or
2482 * translate to "%d" otherwise. Don't include the "calendar:day:digits|"
2483 * part in the translation.
2485 * Note that translating this doesn't guarantee that you get localized
2486 * digits. That needs support from your system and locale definition
2489 g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), day);
2491 /* Get extra information to show, if any: */
2493 if (priv->detail_func)
2494 detail = gtk_calendar_get_detail (calendar, row, col);
2498 layout = gtk_widget_create_pango_layout (widget, buffer);
2499 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2500 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2502 x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2505 gdk_cairo_set_source_color (cr, text_color);
2506 cairo_move_to (cr, x_loc, y_loc);
2507 pango_cairo_show_layout (cr, layout);
2509 if (calendar->day_month[row][col] == MONTH_CURRENT &&
2510 (calendar->marked_date[day-1] || (detail && !show_details)))
2512 cairo_move_to (cr, x_loc - 1, y_loc);
2513 pango_cairo_show_layout (cr, layout);
2516 y_loc += priv->max_day_char_descent;
2518 if (priv->detail_func && show_details)
2522 if (calendar->selected_day == day)
2523 gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
2524 else if (calendar->day_month[row][col] == MONTH_CURRENT)
2525 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
2527 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
2529 cairo_set_line_width (cr, 1);
2530 cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
2531 cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
2539 if (detail && show_details)
2541 gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
2542 pango_layout_set_markup (layout, markup, -1);
2545 if (day == calendar->selected_day)
2547 /* Stripping colors as they conflict with selection marking. */
2549 PangoAttrList *attrs = pango_layout_get_attributes (layout);
2550 PangoAttrList *colors = NULL;
2553 colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
2555 pango_attr_list_unref (colors);
2558 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2559 pango_layout_set_width (layout, PANGO_SCALE * day_rect.width);
2561 if (priv->detail_height_rows)
2563 gint dy = day_rect.height - (y_loc - day_rect.y);
2564 pango_layout_set_height (layout, PANGO_SCALE * dy);
2565 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
2568 cairo_move_to (cr, day_rect.x, y_loc);
2569 pango_cairo_show_layout (cr, layout);
2572 if (GTK_WIDGET_HAS_FOCUS (calendar)
2573 && calendar->focus_row == row && calendar->focus_col == col)
2577 if (calendar->selected_day == day)
2578 state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2580 state = GTK_STATE_NORMAL;
2582 gtk_paint_focus (widget->style,
2585 NULL, widget, "calendar-day",
2586 day_rect.x, day_rect.y,
2587 day_rect.width, day_rect.height);
2591 priv->detail_overflow[row] |= (1 << col);
2593 priv->detail_overflow[row] &= ~(1 << col);
2595 g_object_unref (layout);
2601 calendar_paint_main (GtkCalendar *calendar)
2605 for (col = 0; col < 7; col++)
2606 for (row = 0; row < 6; row++)
2607 calendar_paint_day (calendar, row, col);
2611 calendar_invalidate_arrow (GtkCalendar *calendar,
2614 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2617 window = priv->arrow_win[arrow];
2619 gdk_window_invalidate_rect (window, NULL, FALSE);
2623 calendar_paint_arrow (GtkCalendar *calendar,
2626 GtkWidget *widget = GTK_WIDGET (calendar);
2627 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2630 window = priv->arrow_win[arrow];
2633 cairo_t *cr = gdk_cairo_create (window);
2637 state = priv->arrow_state[arrow];
2639 gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2643 gdk_drawable_get_size (window, &width, &height);
2644 if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2645 gtk_paint_arrow (widget->style, window, state,
2646 GTK_SHADOW_OUT, NULL, widget, "calendar",
2647 GTK_ARROW_LEFT, TRUE,
2648 width/2 - 3, height/2 - 4, 8, 8);
2650 gtk_paint_arrow (widget->style, window, state,
2651 GTK_SHADOW_OUT, NULL, widget, "calendar",
2652 GTK_ARROW_RIGHT, TRUE,
2653 width/2 - 4, height/2 - 4, 8, 8);
2658 gtk_calendar_expose (GtkWidget *widget,
2659 GdkEventExpose *event)
2661 GtkCalendar *calendar = GTK_CALENDAR (widget);
2662 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2665 if (GTK_WIDGET_DRAWABLE (widget))
2667 if (event->window == priv->main_win)
2668 calendar_paint_main (calendar);
2670 if (event->window == priv->header_win)
2671 calendar_paint_header (calendar);
2673 for (i = 0; i < 4; i++)
2674 if (event->window == priv->arrow_win[i])
2675 calendar_paint_arrow (calendar, i);
2677 if (event->window == priv->day_name_win)
2678 calendar_paint_day_names (calendar);
2680 if (event->window == priv->week_win)
2681 calendar_paint_week_numbers (calendar);
2682 if (event->window == widget->window)
2684 gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2685 GTK_SHADOW_IN, NULL, widget, "calendar",
2686 0, 0, widget->allocation.width, widget->allocation.height);
2694 /****************************************
2696 ****************************************/
2699 calendar_arrow_action (GtkCalendar *calendar,
2704 case ARROW_YEAR_LEFT:
2705 calendar_set_year_prev (calendar);
2707 case ARROW_YEAR_RIGHT:
2708 calendar_set_year_next (calendar);
2710 case ARROW_MONTH_LEFT:
2711 calendar_set_month_prev (calendar);
2713 case ARROW_MONTH_RIGHT:
2714 calendar_set_month_next (calendar);
2722 calendar_timer (gpointer data)
2724 GtkCalendar *calendar = data;
2725 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2726 gboolean retval = FALSE;
2730 calendar_arrow_action (calendar, priv->click_child);
2732 if (priv->need_timer)
2734 GtkSettings *settings;
2737 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2738 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2740 priv->need_timer = FALSE;
2741 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2742 timeout * SCROLL_DELAY_FACTOR,
2743 (GSourceFunc) calendar_timer,
2744 (gpointer) calendar, NULL);
2754 calendar_start_spinning (GtkCalendar *calendar,
2757 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2759 priv->click_child = click_child;
2763 GtkSettings *settings;
2766 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2767 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2769 priv->need_timer = TRUE;
2770 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2772 (GSourceFunc) calendar_timer,
2773 (gpointer) calendar, NULL);
2778 calendar_stop_spinning (GtkCalendar *calendar)
2780 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2784 g_source_remove (priv->timer);
2786 priv->need_timer = FALSE;
2791 calendar_main_button_press (GtkCalendar *calendar,
2792 GdkEventButton *event)
2794 GtkWidget *widget = GTK_WIDGET (calendar);
2795 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2801 x = (gint) (event->x);
2802 y = (gint) (event->y);
2804 row = calendar_row_from_y (calendar, y);
2805 col = calendar_column_from_x (calendar, x);
2807 /* If row or column isn't found, just return. */
2808 if (row == -1 || col == -1)
2811 day_month = calendar->day_month[row][col];
2813 if (event->type == GDK_BUTTON_PRESS)
2815 day = calendar->day[row][col];
2817 if (day_month == MONTH_PREV)
2818 calendar_set_month_prev (calendar);
2819 else if (day_month == MONTH_NEXT)
2820 calendar_set_month_next (calendar);
2822 if (!GTK_WIDGET_HAS_FOCUS (widget))
2823 gtk_widget_grab_focus (widget);
2825 if (event->button == 1)
2828 priv->drag_start_x = x;
2829 priv->drag_start_y = y;
2832 calendar_select_and_focus_day (calendar, day);
2834 else if (event->type == GDK_2BUTTON_PRESS)
2837 if (day_month == MONTH_CURRENT)
2838 g_signal_emit (calendar,
2839 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2845 gtk_calendar_button_press (GtkWidget *widget,
2846 GdkEventButton *event)
2848 GtkCalendar *calendar = GTK_CALENDAR (widget);
2849 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2852 if (event->window == priv->main_win)
2853 calendar_main_button_press (calendar, event);
2855 if (!GTK_WIDGET_HAS_FOCUS (widget))
2856 gtk_widget_grab_focus (widget);
2858 for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2860 if (event->window == priv->arrow_win[arrow])
2863 /* only call the action on single click, not double */
2864 if (event->type == GDK_BUTTON_PRESS)
2866 if (event->button == 1)
2867 calendar_start_spinning (calendar, arrow);
2869 calendar_arrow_action (calendar, arrow);
2880 gtk_calendar_button_release (GtkWidget *widget,
2881 GdkEventButton *event)
2883 GtkCalendar *calendar = GTK_CALENDAR (widget);
2884 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2886 if (event->button == 1)
2888 calendar_stop_spinning (calendar);
2898 gtk_calendar_motion_notify (GtkWidget *widget,
2899 GdkEventMotion *event)
2901 GtkCalendar *calendar = GTK_CALENDAR (widget);
2902 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2903 gint event_x, event_y;
2905 gint old_row, old_col;
2907 event_x = (gint) (event->x);
2908 event_y = (gint) (event->y);
2910 if (event->window == priv->main_win)
2915 if (gtk_drag_check_threshold (widget,
2916 priv->drag_start_x, priv->drag_start_y,
2917 event->x, event->y))
2919 GdkDragContext *context;
2920 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2921 gtk_target_list_add_text_targets (target_list, 0);
2922 context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
2923 1, (GdkEvent *)event);
2928 gtk_target_list_unref (target_list);
2929 gtk_drag_set_icon_default (context);
2934 row = calendar_row_from_y (calendar, event_y);
2935 col = calendar_column_from_x (calendar, event_x);
2937 if (row != calendar->highlight_row || calendar->highlight_col != col)
2939 old_row = calendar->highlight_row;
2940 old_col = calendar->highlight_col;
2941 if (old_row > -1 && old_col > -1)
2943 calendar->highlight_row = -1;
2944 calendar->highlight_col = -1;
2945 calendar_invalidate_day (calendar, old_row, old_col);
2948 calendar->highlight_row = row;
2949 calendar->highlight_col = col;
2951 if (row > -1 && col > -1)
2952 calendar_invalidate_day (calendar, row, col);
2960 gtk_calendar_enter_notify (GtkWidget *widget,
2961 GdkEventCrossing *event)
2963 GtkCalendar *calendar = GTK_CALENDAR (widget);
2964 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2966 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2968 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2969 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2972 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2974 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2975 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2978 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2980 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2981 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2984 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2986 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2987 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2994 gtk_calendar_leave_notify (GtkWidget *widget,
2995 GdkEventCrossing *event)
2997 GtkCalendar *calendar = GTK_CALENDAR (widget);
2998 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3002 if (event->window == priv->main_win)
3004 row = calendar->highlight_row;
3005 col = calendar->highlight_col;
3006 calendar->highlight_row = -1;
3007 calendar->highlight_col = -1;
3008 if (row > -1 && col > -1)
3009 calendar_invalidate_day (calendar, row, col);
3012 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3014 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3015 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3018 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3020 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3021 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3024 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3026 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3027 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3030 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3032 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3033 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3040 gtk_calendar_scroll (GtkWidget *widget,
3041 GdkEventScroll *event)
3043 GtkCalendar *calendar = GTK_CALENDAR (widget);
3045 if (event->direction == GDK_SCROLL_UP)
3047 if (!GTK_WIDGET_HAS_FOCUS (widget))
3048 gtk_widget_grab_focus (widget);
3049 calendar_set_month_prev (calendar);
3051 else if (event->direction == GDK_SCROLL_DOWN)
3053 if (!GTK_WIDGET_HAS_FOCUS (widget))
3054 gtk_widget_grab_focus (widget);
3055 calendar_set_month_next (calendar);
3064 /****************************************
3066 ****************************************/
3069 move_focus (GtkCalendar *calendar,
3072 GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3074 if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3075 (text_dir == GTK_TEXT_DIR_RTL && direction == 1))
3077 if (calendar->focus_col > 0)
3078 calendar->focus_col--;
3079 else if (calendar->focus_row > 0)
3081 calendar->focus_col = 6;
3082 calendar->focus_row--;
3085 if (calendar->focus_col < 0)
3086 calendar->focus_col = 6;
3087 if (calendar->focus_row < 0)
3088 calendar->focus_row = 5;
3092 if (calendar->focus_col < 6)
3093 calendar->focus_col++;
3094 else if (calendar->focus_row < 5)
3096 calendar->focus_col = 0;
3097 calendar->focus_row++;
3100 if (calendar->focus_col < 0)
3101 calendar->focus_col = 0;
3102 if (calendar->focus_row < 0)
3103 calendar->focus_row = 0;
3108 gtk_calendar_key_press (GtkWidget *widget,
3111 GtkCalendar *calendar;
3117 calendar = GTK_CALENDAR (widget);
3120 old_focus_row = calendar->focus_row;
3121 old_focus_col = calendar->focus_col;
3123 switch (event->keyval)
3128 if (event->state & GDK_CONTROL_MASK)
3129 calendar_set_month_prev (calendar);
3132 move_focus (calendar, -1);
3133 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3134 calendar_invalidate_day (calendar, calendar->focus_row,
3135 calendar->focus_col);
3141 if (event->state & GDK_CONTROL_MASK)
3142 calendar_set_month_next (calendar);
3145 move_focus (calendar, 1);
3146 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3147 calendar_invalidate_day (calendar, calendar->focus_row,
3148 calendar->focus_col);
3154 if (event->state & GDK_CONTROL_MASK)
3155 calendar_set_year_prev (calendar);
3158 if (calendar->focus_row > 0)
3159 calendar->focus_row--;
3160 if (calendar->focus_row < 0)
3161 calendar->focus_row = 5;
3162 if (calendar->focus_col < 0)
3163 calendar->focus_col = 6;
3164 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3165 calendar_invalidate_day (calendar, calendar->focus_row,
3166 calendar->focus_col);
3172 if (event->state & GDK_CONTROL_MASK)
3173 calendar_set_year_next (calendar);
3176 if (calendar->focus_row < 5)
3177 calendar->focus_row++;
3178 if (calendar->focus_col < 0)
3179 calendar->focus_col = 0;
3180 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3181 calendar_invalidate_day (calendar, calendar->focus_row,
3182 calendar->focus_col);
3187 row = calendar->focus_row;
3188 col = calendar->focus_col;
3190 if (row > -1 && col > -1)
3194 day = calendar->day[row][col];
3195 if (calendar->day_month[row][col] == MONTH_PREV)
3196 calendar_set_month_prev (calendar);
3197 else if (calendar->day_month[row][col] == MONTH_NEXT)
3198 calendar_set_month_next (calendar);
3200 calendar_select_and_focus_day (calendar, day);
3208 /****************************************
3209 * Misc widget methods *
3210 ****************************************/
3213 calendar_set_background (GtkWidget *widget)
3215 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3218 if (GTK_WIDGET_REALIZED (widget))
3220 for (i = 0; i < 4; i++)
3222 if (priv->arrow_win[i])
3223 gdk_window_set_background (priv->arrow_win[i],
3224 HEADER_BG_COLOR (widget));
3226 if (priv->header_win)
3227 gdk_window_set_background (priv->header_win,
3228 HEADER_BG_COLOR (widget));
3229 if (priv->day_name_win)
3230 gdk_window_set_background (priv->day_name_win,
3231 BACKGROUND_COLOR (widget));
3233 gdk_window_set_background (priv->week_win,
3234 BACKGROUND_COLOR (widget));
3236 gdk_window_set_background (priv->main_win,
3237 BACKGROUND_COLOR (widget));
3239 gdk_window_set_background (widget->window,
3240 BACKGROUND_COLOR (widget));
3245 gtk_calendar_style_set (GtkWidget *widget,
3246 GtkStyle *previous_style)
3248 if (previous_style && GTK_WIDGET_REALIZED (widget))
3249 calendar_set_background (widget);
3253 gtk_calendar_state_changed (GtkWidget *widget,
3254 GtkStateType previous_state)
3256 GtkCalendar *calendar = GTK_CALENDAR (widget);
3257 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3260 if (!GTK_WIDGET_IS_SENSITIVE (widget))
3263 calendar_stop_spinning (calendar);
3266 for (i = 0; i < 4; i++)
3267 if (GTK_WIDGET_IS_SENSITIVE (widget))
3268 priv->arrow_state[i] = GTK_STATE_NORMAL;
3270 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
3272 calendar_set_background (widget);
3276 gtk_calendar_grab_notify (GtkWidget *widget,
3277 gboolean was_grabbed)
3280 calendar_stop_spinning (GTK_CALENDAR (widget));
3284 gtk_calendar_focus_out (GtkWidget *widget,
3285 GdkEventFocus *event)
3287 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3288 GtkCalendar *calendar = GTK_CALENDAR (widget);
3290 calendar_queue_refresh (calendar);
3291 calendar_stop_spinning (calendar);
3299 /****************************************
3301 ****************************************/
3304 gtk_calendar_drag_data_get (GtkWidget *widget,
3305 GdkDragContext *context,
3306 GtkSelectionData *selection_data,
3310 GtkCalendar *calendar = GTK_CALENDAR (widget);
3315 date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
3316 len = g_date_strftime (str, 127, "%x", date);
3317 gtk_selection_data_set_text (selection_data, str, len);
3322 /* Get/set whether drag_motion requested the drag data and
3323 * drag_data_received should thus not actually insert the data,
3324 * since the data doesn't result from a drop.
3327 set_status_pending (GdkDragContext *context,
3328 GdkDragAction suggested_action)
3330 g_object_set_data (G_OBJECT (context),
3331 I_("gtk-calendar-status-pending"),
3332 GINT_TO_POINTER (suggested_action));
3335 static GdkDragAction
3336 get_status_pending (GdkDragContext *context)
3338 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
3339 "gtk-calendar-status-pending"));
3343 gtk_calendar_drag_leave (GtkWidget *widget,
3344 GdkDragContext *context,
3347 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3349 priv->drag_highlight = 0;
3350 gtk_drag_unhighlight (widget);
3355 gtk_calendar_drag_motion (GtkWidget *widget,
3356 GdkDragContext *context,
3361 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3364 if (!priv->drag_highlight)
3366 priv->drag_highlight = 1;
3367 gtk_drag_highlight (widget);
3370 target = gtk_drag_dest_find_target (widget, context, NULL);
3371 if (target == GDK_NONE || context->suggested_action == 0)
3372 gdk_drag_status (context, 0, time);
3375 set_status_pending (context, context->suggested_action);
3376 gtk_drag_get_data (widget, context, target, time);
3383 gtk_calendar_drag_drop (GtkWidget *widget,
3384 GdkDragContext *context,
3391 target = gtk_drag_dest_find_target (widget, context, NULL);
3392 if (target != GDK_NONE)
3394 gtk_drag_get_data (widget, context,
3404 gtk_calendar_drag_data_received (GtkWidget *widget,
3405 GdkDragContext *context,
3408 GtkSelectionData *selection_data,
3412 GtkCalendar *calendar = GTK_CALENDAR (widget);
3413 guint day, month, year;
3416 GdkDragAction suggested_action;
3418 suggested_action = get_status_pending (context);
3420 if (suggested_action)
3422 set_status_pending (context, 0);
3424 /* We are getting this data due to a request in drag_motion,
3425 * rather than due to a request in drag_drop, so we are just
3426 * supposed to call drag_status, not actually paste in the
3429 str = (gchar*) gtk_selection_data_get_text (selection_data);
3433 date = g_date_new ();
3434 g_date_set_parse (date, str);
3435 if (!g_date_valid (date))
3436 suggested_action = 0;
3441 suggested_action = 0;
3443 gdk_drag_status (context, suggested_action, time);
3448 date = g_date_new ();
3449 str = (gchar*) gtk_selection_data_get_text (selection_data);
3452 g_date_set_parse (date, str);
3456 if (!g_date_valid (date))
3458 g_warning ("Received invalid date data\n");
3460 gtk_drag_finish (context, FALSE, FALSE, time);
3464 day = g_date_get_day (date);
3465 month = g_date_get_month (date);
3466 year = g_date_get_year (date);
3469 gtk_drag_finish (context, TRUE, FALSE, time);
3472 g_object_freeze_notify (G_OBJECT (calendar));
3473 if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3474 && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
3475 gtk_calendar_select_month (calendar, month - 1, year);
3476 gtk_calendar_select_day (calendar, day);
3477 g_object_thaw_notify (G_OBJECT (calendar));
3481 /****************************************
3483 ****************************************/
3488 * Creates a new calendar, with the current date being selected.
3490 * Return value: a newly #GtkCalendar widget
3493 gtk_calendar_new (void)
3495 return g_object_new (GTK_TYPE_CALENDAR, NULL);
3499 * gtk_calendar_display_options:
3500 * @calendar: a #GtkCalendar.
3501 * @flags: the display options to set.
3503 * Sets display options (whether to display the heading and the month headings).
3505 * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
3508 gtk_calendar_display_options (GtkCalendar *calendar,
3509 GtkCalendarDisplayOptions flags)
3511 gtk_calendar_set_display_options (calendar, flags);
3515 * gtk_calendar_get_display_options:
3516 * @calendar: a #GtkCalendar
3518 * Returns the current display options of @calendar.
3520 * Return value: the display options.
3524 GtkCalendarDisplayOptions
3525 gtk_calendar_get_display_options (GtkCalendar *calendar)
3527 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3529 return calendar->display_flags;
3533 * gtk_calendar_set_display_options:
3534 * @calendar: a #GtkCalendar
3535 * @flags: the display options to set
3537 * Sets display options (whether to display the heading and the month
3543 gtk_calendar_set_display_options (GtkCalendar *calendar,
3544 GtkCalendarDisplayOptions flags)
3546 GtkWidget *widget = GTK_WIDGET (calendar);
3547 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3550 GtkCalendarDisplayOptions old_flags;
3552 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3554 old_flags = calendar->display_flags;
3556 if (GTK_WIDGET_REALIZED (widget))
3558 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3561 if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3562 && (priv->header_win))
3564 calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3565 calendar_realize_arrows (calendar);
3569 for (i = 0; i < 4; i++)
3571 if (priv->arrow_win[i])
3573 gdk_window_set_user_data (priv->arrow_win[i],
3575 gdk_window_destroy (priv->arrow_win[i]);
3576 priv->arrow_win[i] = NULL;
3582 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3586 if (flags & GTK_CALENDAR_SHOW_HEADING)
3588 calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3589 calendar_realize_header (calendar);
3593 for (i = 0; i < 4; i++)
3595 if (priv->arrow_win[i])
3597 gdk_window_set_user_data (priv->arrow_win[i],
3599 gdk_window_destroy (priv->arrow_win[i]);
3600 priv->arrow_win[i] = NULL;
3603 gdk_window_set_user_data (priv->header_win, NULL);
3604 gdk_window_destroy (priv->header_win);
3605 priv->header_win = NULL;
3610 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3614 if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3616 calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3617 calendar_realize_day_names (calendar);
3621 gdk_window_set_user_data (priv->day_name_win, NULL);
3622 gdk_window_destroy (priv->day_name_win);
3623 priv->day_name_win = NULL;
3627 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3631 if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3633 calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3634 calendar_realize_week_numbers (calendar);
3638 gdk_window_set_user_data (priv->week_win, NULL);
3639 gdk_window_destroy (priv->week_win);
3640 priv->week_win = NULL;
3644 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3645 g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3647 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DETAILS)
3650 calendar->display_flags = flags;
3652 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3656 calendar->display_flags = flags;
3658 g_object_freeze_notify (G_OBJECT (calendar));
3659 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3660 g_object_notify (G_OBJECT (calendar), "show-heading");
3661 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3662 g_object_notify (G_OBJECT (calendar), "show-day-names");
3663 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3664 g_object_notify (G_OBJECT (calendar), "no-month-change");
3665 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3666 g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3667 g_object_thaw_notify (G_OBJECT (calendar));
3671 * gtk_calendar_select_month:
3672 * @calendar: a #GtkCalendar
3673 * @month: a month number between 0 and 11.
3674 * @year: the year the month is in.
3676 * Shifts the calendar to a different month.
3678 * Return value: %TRUE, always
3681 gtk_calendar_select_month (GtkCalendar *calendar,
3685 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3686 g_return_val_if_fail (month <= 11, FALSE);
3688 calendar->month = month;
3689 calendar->year = year;
3691 calendar_compute_days (calendar);
3692 calendar_queue_refresh (calendar);
3694 g_object_freeze_notify (G_OBJECT (calendar));
3695 g_object_notify (G_OBJECT (calendar), "month");
3696 g_object_notify (G_OBJECT (calendar), "year");
3697 g_object_thaw_notify (G_OBJECT (calendar));
3699 g_signal_emit (calendar,
3700 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3706 * gtk_calendar_select_day:
3707 * @calendar: a #GtkCalendar.
3708 * @day: the day number between 1 and 31, or 0 to unselect
3709 * the currently selected day.
3711 * Selects a day from the current month.
3714 gtk_calendar_select_day (GtkCalendar *calendar,
3717 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3718 g_return_if_fail (day <= 31);
3720 /* Deselect the old day */
3721 if (calendar->selected_day > 0)
3725 selected_day = calendar->selected_day;
3726 calendar->selected_day = 0;
3727 if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3728 calendar_invalidate_day_num (calendar, selected_day);
3731 calendar->selected_day = day;
3733 /* Select the new day */
3736 if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3737 calendar_invalidate_day_num (calendar, day);
3740 g_object_notify (G_OBJECT (calendar), "day");
3742 g_signal_emit (calendar,
3743 gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3748 * gtk_calendar_clear_marks:
3749 * @calendar: a #GtkCalendar
3751 * Remove all visual markers.
3754 gtk_calendar_clear_marks (GtkCalendar *calendar)
3758 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3760 for (day = 0; day < 31; day++)
3762 calendar->marked_date[day] = FALSE;
3765 calendar->num_marked_dates = 0;
3766 calendar_queue_refresh (calendar);
3770 * gtk_calendar_mark_day:
3771 * @calendar: a #GtkCalendar
3772 * @day: the day number to mark between 1 and 31.
3774 * Places a visual marker on a particular day.
3776 * Return value: %TRUE, always
3779 gtk_calendar_mark_day (GtkCalendar *calendar,
3782 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3784 if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3786 calendar->marked_date[day - 1] = TRUE;
3787 calendar->num_marked_dates++;
3788 calendar_invalidate_day_num (calendar, day);
3795 * gtk_calendar_unmark_day:
3796 * @calendar: a #GtkCalendar.
3797 * @day: the day number to unmark between 1 and 31.
3799 * Removes the visual marker from a particular day.
3801 * Return value: %TRUE, always
3804 gtk_calendar_unmark_day (GtkCalendar *calendar,
3807 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3809 if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3811 calendar->marked_date[day - 1] = FALSE;
3812 calendar->num_marked_dates--;
3813 calendar_invalidate_day_num (calendar, day);
3820 * gtk_calendar_get_date:
3821 * @calendar: a #GtkCalendar
3822 * @year: location to store the year number, or %NULL
3823 * @month: location to store the month number (between 0 and 11), or %NULL
3824 * @day: location to store the day number (between 1 and 31), or %NULL
3826 * Obtains the selected date from a #GtkCalendar.
3829 gtk_calendar_get_date (GtkCalendar *calendar,
3834 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3837 *year = calendar->year;
3840 *month = calendar->month;
3843 *day = calendar->selected_day;
3847 * gtk_calendar_set_detail_func:
3848 * @calendar: a #GtkCalendar.
3849 * @func: a function providing details for each day.
3850 * @data: data to pass to @func invokations.
3851 * @destroy: a function for releasing @data.
3853 * Installs a function which provides Pango markup with detail information
3854 * for each day. Examples for such details are holidays or appointments. That
3855 * information is shown below each day when #GtkCalendar:show-details is set.
3856 * A tooltip containing with full detail information is provided, if the entire
3857 * text should not fit into the details area, or if #GtkCalendar:show-details
3860 * The size of the details area can be restricted by setting the
3861 * #GtkCalendar:detail-width-chars and #GtkCalendar:detail-height-rows
3867 gtk_calendar_set_detail_func (GtkCalendar *calendar,
3868 GtkCalendarDetailFunc func,
3870 GDestroyNotify destroy)
3872 GtkCalendarPrivate *priv;
3874 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3876 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3878 if (priv->detail_func_destroy)
3879 priv->detail_func_destroy (priv->detail_func_user_data);
3881 priv->detail_func = func;
3882 priv->detail_func_user_data = data;
3883 priv->detail_func_destroy = destroy;
3885 gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
3886 NULL != priv->detail_func);
3887 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3891 * gtk_calendar_set_detail_width_chars:
3892 * @calendar: a #GtkCalendar.
3893 * @chars: detail width in characters.
3895 * Updates the width of detail cells.
3896 * See #GtkCalendar:detail-width-chars.
3901 gtk_calendar_set_detail_width_chars (GtkCalendar *calendar,
3904 GtkCalendarPrivate *priv;
3906 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3908 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3910 if (chars != priv->detail_width_chars)
3912 priv->detail_width_chars = chars;
3913 g_object_notify (G_OBJECT (calendar), "detail-width-chars");
3914 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3919 * gtk_calendar_set_detail_height_rows:
3920 * @calendar: a #GtkCalendar.
3921 * @rows: detail height in rows.
3923 * Updates the height of detail cells.
3924 * See #GtkCalendar:detail-height-rows.
3929 gtk_calendar_set_detail_height_rows (GtkCalendar *calendar,
3932 GtkCalendarPrivate *priv;
3934 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3936 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3938 if (rows != priv->detail_height_rows)
3940 priv->detail_height_rows = rows;
3941 g_object_notify (G_OBJECT (calendar), "detail-height-rows");
3942 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3947 * gtk_calendar_get_detail_width_chars:
3948 * @calendar: a #GtkCalendar.
3950 * Queries the width of detail cells, in characters.
3951 * See #GtkCalendar:detail-width-chars.
3955 * Return value: The width of detail cells, in characters.
3958 gtk_calendar_get_detail_width_chars (GtkCalendar *calendar)
3960 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3961 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
3965 * gtk_calendar_get_detail_height_rows:
3966 * @calendar: a #GtkCalendar.
3968 * Queries the height of detail cells, in rows.
3969 * See #GtkCalendar:detail-width-chars.
3973 * Return value: The height of detail cells, in rows.
3976 gtk_calendar_get_detail_height_rows (GtkCalendar *calendar)
3978 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3979 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
3983 * gtk_calendar_freeze:
3984 * @calendar: a #GtkCalendar
3986 * Does nothing. Previously locked the display of the calendar until
3987 * it was thawed with gtk_calendar_thaw().
3992 gtk_calendar_freeze (GtkCalendar *calendar)
3994 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3998 * gtk_calendar_thaw:
3999 * @calendar: a #GtkCalendar
4001 * Does nothing. Previously defrosted a calendar; all the changes made
4002 * since the last gtk_calendar_freeze() were displayed.
4007 gtk_calendar_thaw (GtkCalendar *calendar)
4009 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4012 #define __GTK_CALENDAR_C__
4013 #include "gtkaliasdef.c"