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 #undef GTK_DISABLE_DEPRECATED
51 #include "gtkcalendar.h"
56 #include "gtkmarshalers.h"
57 #include "gtktooltip.h"
58 #include "gtkprivate.h"
59 #include "gdk/gdkkeysyms.h"
62 /***************************************************************************/
63 /* The following date routines are taken from the lib_date package.
64 * They have been minimally edited to avoid conflict with types defined
68 static const guint month_length[2][13] =
70 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
71 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
74 static const guint days_in_months[2][14] =
76 { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
77 { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
80 static glong calc_days(guint year, guint mm, guint dd);
81 static guint day_of_week(guint year, guint mm, guint dd);
82 static glong dates_difference(guint year1, guint mm1, guint dd1,
83 guint year2, guint mm2, guint dd2);
84 static guint weeks_in_year(guint year);
89 return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
93 day_of_week (guint year, guint mm, guint dd)
97 days = calc_days(year, mm, dd);
104 return( (guint) days );
107 static guint weeks_in_year(guint year)
109 return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
113 check_date(guint year, guint mm, guint dd)
115 if (year < 1) return FALSE;
116 if ((mm < 1) || (mm > 12)) return FALSE;
117 if ((dd < 1) || (dd > month_length[leap(year)][mm])) return FALSE;
122 week_number(guint year, guint mm, guint dd)
126 first = day_of_week(year,1,1) - 1;
127 return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
132 year_to_days(guint year)
134 return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
139 calc_days(guint year, guint mm, guint dd)
143 if (year < 1) return(0L);
144 if ((mm < 1) || (mm > 12)) return(0L);
145 if ((dd < 1) || (dd > month_length[(lp = leap(year))][mm])) return(0L);
146 return( year_to_days(--year) + days_in_months[lp][mm] + dd );
150 week_of_year(guint *week, guint *year, guint mm, guint dd)
152 if (check_date(*year,mm,dd))
154 *week = week_number(*year,mm,dd);
156 *week = weeks_in_year(--(*year));
157 else if (*week > weeks_in_year(*year))
168 dates_difference(guint year1, guint mm1, guint dd1,
169 guint year2, guint mm2, guint dd2)
171 return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
174 /*** END OF lib_date routines ********************************************/
176 /* Spacing around day/week headers and main area, inside those windows */
177 #define CALENDAR_MARGIN 0
178 /* Spacing around day/week headers and main area, outside those windows */
179 #define INNER_BORDER 4
180 /* Separation between day headers and main area */
181 #define CALENDAR_YSEP 4
182 /* Separation between week headers and main area */
183 #define CALENDAR_XSEP 4
185 #define DAY_XSEP 0 /* not really good for small calendar */
186 #define DAY_YSEP 0 /* not really good for small calendar */
188 #define SCROLL_DELAY_FACTOR 5
191 #define HEADER_FG_COLOR(widget) (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
192 #define HEADER_BG_COLOR(widget) (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
193 #define SELECTED_BG_COLOR(widget) (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
194 #define SELECTED_FG_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
195 #define NORMAL_DAY_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
196 #define PREV_MONTH_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
197 #define NEXT_MONTH_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
198 #define MARKED_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
199 #define BACKGROUND_COLOR(widget) (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
200 #define HIGHLIGHT_BACK_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
216 MONTH_CHANGED_SIGNAL,
218 DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
234 PROP_NO_MONTH_CHANGE,
235 PROP_SHOW_WEEK_NUMBERS,
237 PROP_DETAIL_WIDTH_CHARS,
238 PROP_DETAIL_HEIGHT_ROWS
241 static guint gtk_calendar_signals[LAST_SIGNAL] = { 0 };
243 struct _GtkCalendarPrivate
245 GdkWindow *header_win;
246 GdkWindow *day_name_win;
249 GdkWindow *arrow_win[4];
255 guint arrow_state[4];
257 guint max_month_width;
258 guint max_year_width;
264 guint max_day_char_width;
265 guint max_day_char_ascent;
266 guint max_day_char_descent;
267 guint max_label_char_ascent;
268 guint max_label_char_descent;
269 guint max_week_char_width;
272 guint year_before : 1;
274 guint need_timer : 1;
277 guint drag_highlight : 1;
287 /* Optional callback, used to display extra information for each day. */
288 GtkCalendarDetailFunc detail_func;
289 gpointer detail_func_user_data;
290 GDestroyNotify detail_func_destroy;
292 /* Size requistion for details provided by the hook. */
293 gint detail_height_rows;
294 gint detail_width_chars;
295 gint detail_overflow[6];
298 #define GTK_CALENDAR_GET_PRIVATE(widget) (GTK_CALENDAR (widget)->priv)
300 static void gtk_calendar_finalize (GObject *calendar);
301 static void gtk_calendar_destroy (GtkObject *calendar);
302 static void gtk_calendar_set_property (GObject *object,
306 static void gtk_calendar_get_property (GObject *object,
311 static void gtk_calendar_realize (GtkWidget *widget);
312 static void gtk_calendar_unrealize (GtkWidget *widget);
313 static void gtk_calendar_size_request (GtkWidget *widget,
314 GtkRequisition *requisition);
315 static void gtk_calendar_size_allocate (GtkWidget *widget,
316 GtkAllocation *allocation);
317 static gboolean gtk_calendar_expose (GtkWidget *widget,
318 GdkEventExpose *event);
319 static gboolean gtk_calendar_button_press (GtkWidget *widget,
320 GdkEventButton *event);
321 static gboolean gtk_calendar_button_release (GtkWidget *widget,
322 GdkEventButton *event);
323 static gboolean gtk_calendar_motion_notify (GtkWidget *widget,
324 GdkEventMotion *event);
325 static gboolean gtk_calendar_enter_notify (GtkWidget *widget,
326 GdkEventCrossing *event);
327 static gboolean gtk_calendar_leave_notify (GtkWidget *widget,
328 GdkEventCrossing *event);
329 static gboolean gtk_calendar_scroll (GtkWidget *widget,
330 GdkEventScroll *event);
331 static gboolean gtk_calendar_key_press (GtkWidget *widget,
333 static gboolean gtk_calendar_focus_out (GtkWidget *widget,
334 GdkEventFocus *event);
335 static void gtk_calendar_grab_notify (GtkWidget *widget,
336 gboolean was_grabbed);
337 static void gtk_calendar_state_changed (GtkWidget *widget,
338 GtkStateType previous_state);
339 static void gtk_calendar_style_set (GtkWidget *widget,
340 GtkStyle *previous_style);
341 static gboolean gtk_calendar_query_tooltip (GtkWidget *widget,
344 gboolean keyboard_mode,
345 GtkTooltip *tooltip);
347 static void gtk_calendar_drag_data_get (GtkWidget *widget,
348 GdkDragContext *context,
349 GtkSelectionData *selection_data,
352 static void gtk_calendar_drag_data_received (GtkWidget *widget,
353 GdkDragContext *context,
356 GtkSelectionData *selection_data,
359 static gboolean gtk_calendar_drag_motion (GtkWidget *widget,
360 GdkDragContext *context,
364 static void gtk_calendar_drag_leave (GtkWidget *widget,
365 GdkDragContext *context,
367 static gboolean gtk_calendar_drag_drop (GtkWidget *widget,
368 GdkDragContext *context,
373 static void calendar_start_spinning (GtkCalendar *calendar,
375 static void calendar_stop_spinning (GtkCalendar *calendar);
377 static void calendar_invalidate_day (GtkCalendar *widget,
380 static void calendar_invalidate_day_num (GtkCalendar *widget,
382 static void calendar_invalidate_arrow (GtkCalendar *widget,
385 static void calendar_compute_days (GtkCalendar *calendar);
387 static char *default_abbreviated_dayname[7];
388 static char *default_monthname[12];
390 G_DEFINE_TYPE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET)
393 gtk_calendar_class_init (GtkCalendarClass *class)
395 GObjectClass *gobject_class;
396 GtkObjectClass *object_class;
397 GtkWidgetClass *widget_class;
399 gobject_class = (GObjectClass*) class;
400 object_class = (GtkObjectClass*) class;
401 widget_class = (GtkWidgetClass*) class;
403 gobject_class->set_property = gtk_calendar_set_property;
404 gobject_class->get_property = gtk_calendar_get_property;
405 gobject_class->finalize = gtk_calendar_finalize;
407 object_class->destroy = gtk_calendar_destroy;
409 widget_class->realize = gtk_calendar_realize;
410 widget_class->unrealize = gtk_calendar_unrealize;
411 widget_class->expose_event = gtk_calendar_expose;
412 widget_class->size_request = gtk_calendar_size_request;
413 widget_class->size_allocate = gtk_calendar_size_allocate;
414 widget_class->button_press_event = gtk_calendar_button_press;
415 widget_class->button_release_event = gtk_calendar_button_release;
416 widget_class->motion_notify_event = gtk_calendar_motion_notify;
417 widget_class->enter_notify_event = gtk_calendar_enter_notify;
418 widget_class->leave_notify_event = gtk_calendar_leave_notify;
419 widget_class->key_press_event = gtk_calendar_key_press;
420 widget_class->scroll_event = gtk_calendar_scroll;
421 widget_class->style_set = gtk_calendar_style_set;
422 widget_class->state_changed = gtk_calendar_state_changed;
423 widget_class->grab_notify = gtk_calendar_grab_notify;
424 widget_class->focus_out_event = gtk_calendar_focus_out;
425 widget_class->query_tooltip = gtk_calendar_query_tooltip;
427 widget_class->drag_data_get = gtk_calendar_drag_data_get;
428 widget_class->drag_motion = gtk_calendar_drag_motion;
429 widget_class->drag_leave = gtk_calendar_drag_leave;
430 widget_class->drag_drop = gtk_calendar_drag_drop;
431 widget_class->drag_data_received = gtk_calendar_drag_data_received;
437 * This property gets initially set to the current year.
439 g_object_class_install_property (gobject_class,
441 g_param_spec_int ("year",
443 P_("The selected year"),
445 GTK_PARAM_READWRITE));
450 * The selected month (as a number between 0 and 11).
451 * This property gets initially set to the current month.
453 g_object_class_install_property (gobject_class,
455 g_param_spec_int ("month",
457 P_("The selected month (as a number between 0 and 11)"),
459 GTK_PARAM_READWRITE));
464 * The selected day (as a number between 1 and 31, or 0
465 * to unselect the currently selected day).
466 * This property gets initially set to the current day.
468 g_object_class_install_property (gobject_class,
470 g_param_spec_int ("day",
472 P_("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
474 GTK_PARAM_READWRITE));
477 * GtkCalendar:show-heading:
479 * Determines whether a heading is displayed.
483 g_object_class_install_property (gobject_class,
485 g_param_spec_boolean ("show-heading",
487 P_("If TRUE, a heading is displayed"),
489 GTK_PARAM_READWRITE));
492 * GtkCalendar:show-day-names:
494 * Determines whether day names are displayed.
498 g_object_class_install_property (gobject_class,
500 g_param_spec_boolean ("show-day-names",
501 P_("Show Day Names"),
502 P_("If TRUE, day names are displayed"),
504 GTK_PARAM_READWRITE));
506 * GtkCalendar:no-month-change:
508 * Determines whether the selected month can be changed.
512 g_object_class_install_property (gobject_class,
513 PROP_NO_MONTH_CHANGE,
514 g_param_spec_boolean ("no-month-change",
515 P_("No Month Change"),
516 P_("If TRUE, the selected month cannot be changed"),
518 GTK_PARAM_READWRITE));
521 * GtkCalendar:show-week-numbers:
523 * Determines whether week numbers are displayed.
527 g_object_class_install_property (gobject_class,
528 PROP_SHOW_WEEK_NUMBERS,
529 g_param_spec_boolean ("show-week-numbers",
530 P_("Show Week Numbers"),
531 P_("If TRUE, week numbers are displayed"),
533 GTK_PARAM_READWRITE));
536 * GtkCalendar:detail-width-chars:
538 * Width of a detail cell, in characters.
539 * A value of 0 allows any width. See gtk_calendar_set_detail_func().
543 g_object_class_install_property (gobject_class,
544 PROP_DETAIL_WIDTH_CHARS,
545 g_param_spec_int ("detail-width-chars",
547 P_("Details width in characters"),
549 GTK_PARAM_READWRITE));
552 * GtkCalendar:detail-height-rows:
554 * Height of a detail cell, in rows.
555 * A value of 0 allows any width. See gtk_calendar_set_detail_func().
559 g_object_class_install_property (gobject_class,
560 PROP_DETAIL_HEIGHT_ROWS,
561 g_param_spec_int ("detail-height-rows",
562 P_("Details Height"),
563 P_("Details height in rows"),
565 GTK_PARAM_READWRITE));
568 * GtkCalendar:show-details:
570 * Determines whether details are shown directly in the widget, or if they are
571 * available only as tooltip. When this property is set days with details are
576 g_object_class_install_property (gobject_class,
578 g_param_spec_boolean ("show-details",
580 P_("If TRUE, details are shown"),
582 GTK_PARAM_READWRITE));
584 gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
585 g_signal_new (I_("month_changed"),
586 G_OBJECT_CLASS_TYPE (gobject_class),
588 G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
590 _gtk_marshal_VOID__VOID,
592 gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
593 g_signal_new (I_("day_selected"),
594 G_OBJECT_CLASS_TYPE (gobject_class),
596 G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
598 _gtk_marshal_VOID__VOID,
600 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
601 g_signal_new (I_("day_selected_double_click"),
602 G_OBJECT_CLASS_TYPE (gobject_class),
604 G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
606 _gtk_marshal_VOID__VOID,
608 gtk_calendar_signals[PREV_MONTH_SIGNAL] =
609 g_signal_new (I_("prev_month"),
610 G_OBJECT_CLASS_TYPE (gobject_class),
612 G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
614 _gtk_marshal_VOID__VOID,
616 gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
617 g_signal_new (I_("next_month"),
618 G_OBJECT_CLASS_TYPE (gobject_class),
620 G_STRUCT_OFFSET (GtkCalendarClass, next_month),
622 _gtk_marshal_VOID__VOID,
624 gtk_calendar_signals[PREV_YEAR_SIGNAL] =
625 g_signal_new (I_("prev_year"),
626 G_OBJECT_CLASS_TYPE (gobject_class),
628 G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
630 _gtk_marshal_VOID__VOID,
632 gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
633 g_signal_new (I_("next_year"),
634 G_OBJECT_CLASS_TYPE (gobject_class),
636 G_STRUCT_OFFSET (GtkCalendarClass, next_year),
638 _gtk_marshal_VOID__VOID,
641 g_type_class_add_private (gobject_class, sizeof (GtkCalendarPrivate));
645 gtk_calendar_init (GtkCalendar *calendar)
647 GtkWidget *widget = GTK_WIDGET (calendar);
652 wchar_t wbuffer[100];
657 GtkCalendarPrivate *priv;
659 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
660 union { unsigned int word; char *string; } langinfo;
661 gint week_1stday = 0;
662 gint first_weekday = 1;
668 priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
672 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
674 if (!default_abbreviated_dayname[0])
678 tmp_time= (i+3)*86400;
679 strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
680 default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
682 if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
683 wbuffer, G_N_ELEMENTS (wbuffer)))
684 default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
686 default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
690 if (!default_monthname[0])
695 strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
696 default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
698 if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
699 wbuffer, G_N_ELEMENTS (wbuffer)))
700 default_monthname[i] = g_strdup_printf ("(%d)", i);
702 default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
708 tm = localtime (&secs);
709 calendar->month = tm->tm_mon;
710 calendar->year = 1900 + tm->tm_year;
713 calendar->marked_date[i] = FALSE;
714 calendar->num_marked_dates = 0;
715 calendar->selected_day = tm->tm_mday;
717 calendar->display_flags = (GTK_CALENDAR_SHOW_HEADING |
718 GTK_CALENDAR_SHOW_DAY_NAMES |
719 GTK_CALENDAR_SHOW_DETAILS);
721 calendar->highlight_row = -1;
722 calendar->highlight_col = -1;
724 calendar->focus_row = -1;
725 calendar->focus_col = -1;
727 priv->max_year_width = 0;
728 priv->max_month_width = 0;
729 priv->max_day_char_width = 0;
730 priv->max_week_char_width = 0;
732 priv->max_day_char_ascent = 0;
733 priv->max_day_char_descent = 0;
734 priv->max_label_char_ascent = 0;
735 priv->max_label_char_descent = 0;
737 priv->arrow_width = 10;
739 priv->need_timer = 0;
741 priv->click_child = -1;
744 priv->drag_highlight = 0;
746 gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
747 gtk_drag_dest_add_text_targets (widget);
749 priv->year_before = 0;
751 /* Translate to calendar:YM if you want years to be displayed
752 * before months; otherwise translate to calendar:MY.
753 * Do *not* translate it to anything else, if it
754 * it isn't calendar:YM or calendar:MY it will not work.
756 * Note that this flipping is in top of the text direction flipping,
757 * so if you have a default text direction of RTL and YM, then
758 * the year will appear on the right.
760 year_before = _("calendar:MY");
761 if (strcmp (year_before, "calendar:YM") == 0)
762 priv->year_before = 1;
763 else if (strcmp (year_before, "calendar:MY") != 0)
764 g_warning ("Whoever translated calendar:MY did so wrongly.\n");
767 priv->week_start = 0;
770 if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
771 wbuffer, G_N_ELEMENTS (wbuffer)))
772 week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
774 if (week_start != NULL)
776 priv->week_start = (week_start[0] - '0' + 1) % 7;
780 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
781 langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
782 first_weekday = langinfo.string[0];
783 langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
784 week_origin = langinfo.word;
785 if (week_origin == 19971130) /* Sunday */
787 else if (week_origin == 19971201) /* Monday */
790 g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
792 priv->week_start = (week_1stday + first_weekday - 1) % 7;
794 /* Translate to calendar:week_start:0 if you want Sunday to be the
795 * first day of the week to calendar:week_start:1 if you want Monday
796 * to be the first day of the week, and so on.
798 week_start = _("calendar:week_start:0");
800 if (strncmp (week_start, "calendar:week_start:", 20) == 0)
801 priv->week_start = *(week_start + 20) - '0';
803 priv->week_start = -1;
805 if (priv->week_start < 0 || priv->week_start > 6)
807 g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
808 priv->week_start = 0;
813 calendar_compute_days (calendar);
817 /****************************************
818 * Utility Functions *
819 ****************************************/
822 calendar_queue_refresh (GtkCalendar *calendar)
824 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
826 if (!(priv->detail_func) ||
827 !(calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS) ||
828 (priv->detail_width_chars && priv->detail_height_rows))
829 gtk_widget_queue_draw (GTK_WIDGET (calendar));
831 gtk_widget_queue_resize (GTK_WIDGET (calendar));
835 calendar_set_month_next (GtkCalendar *calendar)
839 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
843 if (calendar->month == 11)
851 calendar_compute_days (calendar);
852 g_signal_emit (calendar,
853 gtk_calendar_signals[NEXT_MONTH_SIGNAL],
855 g_signal_emit (calendar,
856 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
859 month_len = month_length[leap (calendar->year)][calendar->month + 1];
861 if (month_len < calendar->selected_day)
863 calendar->selected_day = 0;
864 gtk_calendar_select_day (calendar, month_len);
867 gtk_calendar_select_day (calendar, calendar->selected_day);
869 calendar_queue_refresh (calendar);
873 calendar_set_year_prev (GtkCalendar *calendar)
878 calendar_compute_days (calendar);
879 g_signal_emit (calendar,
880 gtk_calendar_signals[PREV_YEAR_SIGNAL],
882 g_signal_emit (calendar,
883 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
886 month_len = month_length[leap (calendar->year)][calendar->month + 1];
888 if (month_len < calendar->selected_day)
890 calendar->selected_day = 0;
891 gtk_calendar_select_day (calendar, month_len);
894 gtk_calendar_select_day (calendar, calendar->selected_day);
896 calendar_queue_refresh (calendar);
900 calendar_set_year_next (GtkCalendar *calendar)
905 calendar_compute_days (calendar);
906 g_signal_emit (calendar,
907 gtk_calendar_signals[NEXT_YEAR_SIGNAL],
909 g_signal_emit (calendar,
910 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
913 month_len = month_length[leap (calendar->year)][calendar->month + 1];
915 if (month_len < calendar->selected_day)
917 calendar->selected_day = 0;
918 gtk_calendar_select_day (calendar, month_len);
921 gtk_calendar_select_day (calendar, calendar->selected_day);
923 calendar_queue_refresh (calendar);
927 calendar_compute_days (GtkCalendar *calendar)
929 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
933 gint ndays_in_prev_month;
939 year = calendar->year;
940 month = calendar->month + 1;
942 ndays_in_month = month_length[leap (year)][month];
944 first_day = day_of_week (year, month, 1);
945 first_day = (first_day + 7 - priv->week_start) % 7;
947 /* Compute days of previous month */
949 ndays_in_prev_month = month_length[leap (year)][month-1];
951 ndays_in_prev_month = month_length[leap (year)][12];
952 day = ndays_in_prev_month - first_day + 1;
957 for (col = 0; col < first_day; col++)
959 calendar->day[row][col] = day;
960 calendar->day_month[row][col] = MONTH_PREV;
965 /* Compute days of current month */
967 for (day = 1; day <= ndays_in_month; day++)
969 calendar->day[row][col] = day;
970 calendar->day_month[row][col] = MONTH_CURRENT;
980 /* Compute days of next month */
982 for (; row <= 5; row++)
984 for (; col <= 6; col++)
986 calendar->day[row][col] = day;
987 calendar->day_month[row][col] = MONTH_NEXT;
995 calendar_select_and_focus_day (GtkCalendar *calendar,
998 gint old_focus_row = calendar->focus_row;
999 gint old_focus_col = calendar->focus_col;
1003 for (row = 0; row < 6; row ++)
1004 for (col = 0; col < 7; col++)
1006 if (calendar->day_month[row][col] == MONTH_CURRENT
1007 && calendar->day[row][col] == day)
1009 calendar->focus_row = row;
1010 calendar->focus_col = col;
1014 if (old_focus_row != -1 && old_focus_col != -1)
1015 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
1017 gtk_calendar_select_day (calendar, day);
1021 /****************************************
1022 * Layout computation utilities *
1023 ****************************************/
1026 calendar_row_height (GtkCalendar *calendar)
1028 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
1029 - ((calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1030 ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
1034 /* calendar_left_x_for_column: returns the x coordinate
1035 * for the left of the column */
1037 calendar_left_x_for_column (GtkCalendar *calendar,
1043 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1044 column = 6 - column;
1046 width = GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1047 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1048 x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
1050 x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
1055 /* column_from_x: returns the column 0-6 that the
1056 * x pixel of the xwindow is in */
1058 calendar_column_from_x (GtkCalendar *calendar,
1062 gint x_left, x_right;
1066 for (c = 0; c < 7; c++)
1068 x_left = calendar_left_x_for_column (calendar, c);
1069 x_right = x_left + GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1071 if (event_x >= x_left && event_x < x_right)
1081 /* calendar_top_y_for_row: returns the y coordinate
1082 * for the top of the row */
1084 calendar_top_y_for_row (GtkCalendar *calendar,
1088 return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h
1089 - (CALENDAR_MARGIN + (6 - row)
1090 * calendar_row_height (calendar)));
1093 /* row_from_y: returns the row 0-5 that the
1094 * y pixel of the xwindow is in */
1096 calendar_row_from_y (GtkCalendar *calendar,
1101 gint y_top, y_bottom;
1103 height = calendar_row_height (calendar);
1106 for (r = 0; r < 6; r++)
1108 y_top = calendar_top_y_for_row (calendar, r);
1109 y_bottom = y_top + height;
1111 if (event_y >= y_top && event_y < y_bottom)
1122 calendar_arrow_rectangle (GtkCalendar *calendar,
1126 GtkWidget *widget = GTK_WIDGET (calendar);
1127 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1130 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1131 year_left = priv->year_before;
1133 year_left = !priv->year_before;
1136 rect->width = priv->arrow_width;
1137 rect->height = priv->header_h - 7;
1141 case ARROW_MONTH_LEFT:
1143 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1144 - (3 + 2*priv->arrow_width
1145 + priv->max_month_width));
1149 case ARROW_MONTH_RIGHT:
1151 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1152 - 3 - priv->arrow_width);
1154 rect->x = (priv->arrow_width
1155 + priv->max_month_width);
1157 case ARROW_YEAR_LEFT:
1161 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1162 - (3 + 2*priv->arrow_width
1163 + priv->max_year_width));
1165 case ARROW_YEAR_RIGHT:
1167 rect->x = (priv->arrow_width
1168 + priv->max_year_width);
1170 rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1171 - 3 - priv->arrow_width);
1177 calendar_day_rectangle (GtkCalendar *calendar,
1182 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1184 rect->x = calendar_left_x_for_column (calendar, col);
1185 rect->y = calendar_top_y_for_row (calendar, row);
1186 rect->height = calendar_row_height (calendar);
1187 rect->width = priv->day_width;
1191 calendar_set_month_prev (GtkCalendar *calendar)
1195 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1198 if (calendar->month == 0)
1200 calendar->month = 11;
1206 month_len = month_length[leap (calendar->year)][calendar->month + 1];
1208 calendar_compute_days (calendar);
1210 g_signal_emit (calendar,
1211 gtk_calendar_signals[PREV_MONTH_SIGNAL],
1213 g_signal_emit (calendar,
1214 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1217 if (month_len < calendar->selected_day)
1219 calendar->selected_day = 0;
1220 gtk_calendar_select_day (calendar, month_len);
1224 if (calendar->selected_day < 0)
1225 calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
1226 gtk_calendar_select_day (calendar, calendar->selected_day);
1229 calendar_queue_refresh (calendar);
1233 /****************************************
1234 * Basic object methods *
1235 ****************************************/
1238 gtk_calendar_finalize (GObject *object)
1240 (* G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize) (object);
1244 gtk_calendar_destroy (GtkObject *object)
1246 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1248 calendar_stop_spinning (GTK_CALENDAR (object));
1250 /* Call the destroy function for the extra display callback: */
1251 if (priv->detail_func_destroy && priv->detail_func_user_data)
1253 priv->detail_func_destroy (priv->detail_func_user_data);
1254 priv->detail_func_user_data = NULL;
1255 priv->detail_func_destroy = NULL;
1258 GTK_OBJECT_CLASS (gtk_calendar_parent_class)->destroy (object);
1263 calendar_set_display_option (GtkCalendar *calendar,
1264 GtkCalendarDisplayOptions flag,
1267 GtkCalendarDisplayOptions flags;
1269 flags = calendar->display_flags | flag;
1271 flags = calendar->display_flags & ~flag;
1272 gtk_calendar_display_options (calendar, flags);
1276 calendar_get_display_option (GtkCalendar *calendar,
1277 GtkCalendarDisplayOptions flag)
1279 return (calendar->display_flags & flag) != 0;
1283 gtk_calendar_set_property (GObject *object,
1285 const GValue *value,
1288 GtkCalendar *calendar;
1290 calendar = GTK_CALENDAR (object);
1295 gtk_calendar_select_month (calendar,
1297 g_value_get_int (value));
1300 gtk_calendar_select_month (calendar,
1301 g_value_get_int (value),
1305 gtk_calendar_select_day (calendar,
1306 g_value_get_int (value));
1308 case PROP_SHOW_HEADING:
1309 calendar_set_display_option (calendar,
1310 GTK_CALENDAR_SHOW_HEADING,
1311 g_value_get_boolean (value));
1313 case PROP_SHOW_DAY_NAMES:
1314 calendar_set_display_option (calendar,
1315 GTK_CALENDAR_SHOW_DAY_NAMES,
1316 g_value_get_boolean (value));
1318 case PROP_NO_MONTH_CHANGE:
1319 calendar_set_display_option (calendar,
1320 GTK_CALENDAR_NO_MONTH_CHANGE,
1321 g_value_get_boolean (value));
1323 case PROP_SHOW_WEEK_NUMBERS:
1324 calendar_set_display_option (calendar,
1325 GTK_CALENDAR_SHOW_WEEK_NUMBERS,
1326 g_value_get_boolean (value));
1328 case PROP_SHOW_DETAILS:
1329 calendar_set_display_option (calendar,
1330 GTK_CALENDAR_SHOW_DETAILS,
1331 g_value_get_boolean (value));
1333 case PROP_DETAIL_WIDTH_CHARS:
1334 gtk_calendar_set_detail_width_chars (calendar,
1335 g_value_get_int (value));
1337 case PROP_DETAIL_HEIGHT_ROWS:
1338 gtk_calendar_set_detail_height_rows (calendar,
1339 g_value_get_int (value));
1342 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1348 gtk_calendar_get_property (GObject *object,
1353 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1354 GtkCalendar *calendar = GTK_CALENDAR (object);
1359 g_value_set_int (value, calendar->year);
1362 g_value_set_int (value, calendar->month);
1365 g_value_set_int (value, calendar->selected_day);
1367 case PROP_SHOW_HEADING:
1368 g_value_set_boolean (value, calendar_get_display_option (calendar,
1369 GTK_CALENDAR_SHOW_HEADING));
1371 case PROP_SHOW_DAY_NAMES:
1372 g_value_set_boolean (value, calendar_get_display_option (calendar,
1373 GTK_CALENDAR_SHOW_DAY_NAMES));
1375 case PROP_NO_MONTH_CHANGE:
1376 g_value_set_boolean (value, calendar_get_display_option (calendar,
1377 GTK_CALENDAR_NO_MONTH_CHANGE));
1379 case PROP_SHOW_WEEK_NUMBERS:
1380 g_value_set_boolean (value, calendar_get_display_option (calendar,
1381 GTK_CALENDAR_SHOW_WEEK_NUMBERS));
1383 case PROP_SHOW_DETAILS:
1384 g_value_set_boolean (value, calendar_get_display_option (calendar,
1385 GTK_CALENDAR_SHOW_DETAILS));
1387 case PROP_DETAIL_WIDTH_CHARS:
1388 g_value_set_int (value, priv->detail_width_chars);
1390 case PROP_DETAIL_HEIGHT_ROWS:
1391 g_value_set_int (value, priv->detail_height_rows);
1394 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1400 /****************************************
1402 ****************************************/
1405 calendar_realize_arrows (GtkCalendar *calendar)
1407 GtkWidget *widget = GTK_WIDGET (calendar);
1408 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1409 GdkWindowAttr attributes;
1410 gint attributes_mask;
1413 /* Arrow windows ------------------------------------- */
1414 if (! (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1415 && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
1417 attributes.wclass = GDK_INPUT_OUTPUT;
1418 attributes.window_type = GDK_WINDOW_CHILD;
1419 attributes.visual = gtk_widget_get_visual (widget);
1420 attributes.colormap = gtk_widget_get_colormap (widget);
1421 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1422 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1423 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1424 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1425 for (i = 0; i < 4; i++)
1428 calendar_arrow_rectangle (calendar, i, &rect);
1430 attributes.x = rect.x;
1431 attributes.y = rect.y;
1432 attributes.width = rect.width;
1433 attributes.height = rect.height;
1434 priv->arrow_win[i] = gdk_window_new (priv->header_win,
1437 if (GTK_WIDGET_IS_SENSITIVE (widget))
1438 priv->arrow_state[i] = GTK_STATE_NORMAL;
1440 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
1441 gdk_window_set_background (priv->arrow_win[i],
1442 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1443 gdk_window_show (priv->arrow_win[i]);
1444 gdk_window_set_user_data (priv->arrow_win[i], widget);
1449 for (i = 0; i < 4; i++)
1450 priv->arrow_win[i] = NULL;
1455 calendar_realize_header (GtkCalendar *calendar)
1457 GtkWidget *widget = GTK_WIDGET (calendar);
1458 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1459 GdkWindowAttr attributes;
1460 gint attributes_mask;
1462 /* Header window ------------------------------------- */
1463 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1465 attributes.wclass = GDK_INPUT_OUTPUT;
1466 attributes.window_type = GDK_WINDOW_CHILD;
1467 attributes.visual = gtk_widget_get_visual (widget);
1468 attributes.colormap = gtk_widget_get_colormap (widget);
1469 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1470 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1471 attributes.x = widget->style->xthickness;
1472 attributes.y = widget->style->ythickness;
1473 attributes.width = widget->allocation.width - 2 * attributes.x;
1474 attributes.height = priv->header_h;
1475 priv->header_win = gdk_window_new (widget->window,
1476 &attributes, attributes_mask);
1478 gdk_window_set_background (priv->header_win,
1479 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1480 gdk_window_show (priv->header_win);
1481 gdk_window_set_user_data (priv->header_win, widget);
1486 priv->header_win = NULL;
1488 calendar_realize_arrows (calendar);
1492 calendar_realize_day_names (GtkCalendar *calendar)
1494 GtkWidget *widget = GTK_WIDGET (calendar);
1495 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1496 GdkWindowAttr attributes;
1497 gint attributes_mask;
1499 /* Day names window --------------------------------- */
1500 if ( calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1502 attributes.wclass = GDK_INPUT_OUTPUT;
1503 attributes.window_type = GDK_WINDOW_CHILD;
1504 attributes.visual = gtk_widget_get_visual (widget);
1505 attributes.colormap = gtk_widget_get_colormap (widget);
1506 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1507 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1508 attributes.x = (widget->style->xthickness + INNER_BORDER);
1509 attributes.y = priv->header_h + (widget->style->ythickness
1511 attributes.width = (widget->allocation.width
1512 - (widget->style->xthickness + INNER_BORDER)
1514 attributes.height = priv->day_name_h;
1515 priv->day_name_win = gdk_window_new (widget->window,
1518 gdk_window_set_background (priv->day_name_win,
1519 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1520 gdk_window_show (priv->day_name_win);
1521 gdk_window_set_user_data (priv->day_name_win, widget);
1525 priv->day_name_win = NULL;
1530 calendar_realize_week_numbers (GtkCalendar *calendar)
1532 GtkWidget *widget = GTK_WIDGET (calendar);
1533 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1534 GdkWindowAttr attributes;
1535 gint attributes_mask;
1537 /* Week number window -------------------------------- */
1538 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1540 attributes.wclass = GDK_INPUT_OUTPUT;
1541 attributes.window_type = GDK_WINDOW_CHILD;
1542 attributes.visual = gtk_widget_get_visual (widget);
1543 attributes.colormap = gtk_widget_get_colormap (widget);
1544 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1546 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1547 attributes.x = widget->style->xthickness + INNER_BORDER;
1548 attributes.y = (priv->header_h + priv->day_name_h
1549 + (widget->style->ythickness + INNER_BORDER));
1550 attributes.width = priv->week_width;
1551 attributes.height = priv->main_h;
1552 priv->week_win = gdk_window_new (widget->window,
1553 &attributes, attributes_mask);
1554 gdk_window_set_background (priv->week_win,
1555 BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1556 gdk_window_show (priv->week_win);
1557 gdk_window_set_user_data (priv->week_win, widget);
1561 priv->week_win = NULL;
1566 gtk_calendar_realize (GtkWidget *widget)
1568 GtkCalendar *calendar = GTK_CALENDAR (widget);
1569 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1570 GdkWindowAttr attributes;
1571 gint attributes_mask;
1573 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1575 attributes.x = widget->allocation.x;
1576 attributes.y = widget->allocation.y;
1577 attributes.width = widget->allocation.width;
1578 attributes.height = widget->allocation.height;
1579 attributes.wclass = GDK_INPUT_OUTPUT;
1580 attributes.window_type = GDK_WINDOW_CHILD;
1581 attributes.event_mask = (gtk_widget_get_events (widget)
1582 | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1583 attributes.visual = gtk_widget_get_visual (widget);
1584 attributes.colormap = gtk_widget_get_colormap (widget);
1586 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1587 widget->window = gdk_window_new (widget->parent->window,
1588 &attributes, attributes_mask);
1590 widget->style = gtk_style_attach (widget->style, widget->window);
1592 /* Header window ------------------------------------- */
1593 calendar_realize_header (calendar);
1594 /* Day names window --------------------------------- */
1595 calendar_realize_day_names (calendar);
1596 /* Week number window -------------------------------- */
1597 calendar_realize_week_numbers (calendar);
1598 /* Main Window -------------------------------------- */
1599 attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1600 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1601 | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1603 attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
1604 attributes.y = (priv->header_h + priv->day_name_h
1605 + (widget->style->ythickness + INNER_BORDER));
1606 attributes.width = (widget->allocation.width - attributes.x
1607 - (widget->style->xthickness + INNER_BORDER));
1608 attributes.height = priv->main_h;
1609 priv->main_win = gdk_window_new (widget->window,
1610 &attributes, attributes_mask);
1611 gdk_window_set_background (priv->main_win,
1612 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1613 gdk_window_show (priv->main_win);
1614 gdk_window_set_user_data (priv->main_win, widget);
1615 gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1616 gdk_window_show (widget->window);
1617 gdk_window_set_user_data (widget->window, widget);
1621 gtk_calendar_unrealize (GtkWidget *widget)
1623 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1626 if (priv->header_win)
1628 for (i = 0; i < 4; i++)
1630 if (priv->arrow_win[i])
1632 gdk_window_set_user_data (priv->arrow_win[i], NULL);
1633 gdk_window_destroy (priv->arrow_win[i]);
1634 priv->arrow_win[i] = NULL;
1637 gdk_window_set_user_data (priv->header_win, NULL);
1638 gdk_window_destroy (priv->header_win);
1639 priv->header_win = NULL;
1644 gdk_window_set_user_data (priv->week_win, NULL);
1645 gdk_window_destroy (priv->week_win);
1646 priv->week_win = NULL;
1651 gdk_window_set_user_data (priv->main_win, NULL);
1652 gdk_window_destroy (priv->main_win);
1653 priv->main_win = NULL;
1655 if (priv->day_name_win)
1657 gdk_window_set_user_data (priv->day_name_win, NULL);
1658 gdk_window_destroy (priv->day_name_win);
1659 priv->day_name_win = NULL;
1662 if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize)
1663 (* GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize) (widget);
1667 gtk_calendar_get_detail (GtkCalendar *calendar,
1671 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1674 year = calendar->year;
1675 month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
1682 else if (month > 11)
1688 return priv->detail_func (calendar,
1690 calendar->day[row][column],
1691 priv->detail_func_user_data);
1695 gtk_calendar_query_tooltip (GtkWidget *widget,
1698 gboolean keyboard_mode,
1699 GtkTooltip *tooltip)
1701 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1702 GtkCalendar *calendar = GTK_CALENDAR (widget);
1703 gchar *detail = NULL;
1704 GdkRectangle day_rect;
1708 gint x0, y0, row, col;
1710 gdk_window_get_position (priv->main_win, &x0, &y0);
1711 col = calendar_column_from_x (calendar, x - x0);
1712 row = calendar_row_from_y (calendar, y - y0);
1714 if (0 != (priv->detail_overflow[row] & (1 << col)) ||
1715 0 == (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS))
1717 detail = gtk_calendar_get_detail (calendar, row, col);
1718 calendar_day_rectangle (calendar, row, col, &day_rect);
1727 gtk_tooltip_set_tip_area (tooltip, &day_rect);
1728 gtk_tooltip_set_markup (tooltip, detail);
1735 if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip)
1736 return GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
1742 /****************************************
1743 * Size Request and Allocate *
1744 ****************************************/
1747 gtk_calendar_size_request (GtkWidget *widget,
1748 GtkRequisition *requisition)
1750 GtkCalendar *calendar = GTK_CALENDAR (widget);
1751 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1752 PangoLayout *layout;
1753 PangoRectangle logical_rect;
1757 gint calendar_margin = CALENDAR_MARGIN;
1758 gint header_width, main_width;
1759 gint max_header_height = 0;
1762 gint max_detail_height;
1764 gtk_widget_style_get (GTK_WIDGET (widget),
1765 "focus-line-width", &focus_width,
1766 "focus-padding", &focus_padding,
1769 layout = gtk_widget_create_pango_layout (widget, NULL);
1772 * Calculate the requisition width for the widget.
1777 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1779 priv->max_month_width = 0;
1780 for (i = 0; i < 12; i++)
1782 pango_layout_set_text (layout, default_monthname[i], -1);
1783 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1784 priv->max_month_width = MAX (priv->max_month_width,
1785 logical_rect.width + 8);
1786 max_header_height = MAX (max_header_height, logical_rect.height);
1789 priv->max_year_width = 0;
1790 /* Translators: This is a text measurement template.
1791 * Translate it to the widest year text.
1793 * Don't include the prefix "year measurement template|"
1794 * in the translation.
1796 * If you don't understand this, leave it as "2000"
1798 pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);
1799 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1800 priv->max_year_width = MAX (priv->max_year_width,
1801 logical_rect.width + 8);
1802 max_header_height = MAX (max_header_height, logical_rect.height);
1806 priv->max_month_width = 0;
1807 priv->max_year_width = 0;
1810 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1811 header_width = (priv->max_month_width
1812 + priv->max_year_width
1815 header_width = (priv->max_month_width
1816 + priv->max_year_width
1817 + 4 * priv->arrow_width + 3 * 3);
1819 /* Mainwindow labels width */
1821 priv->max_day_char_width = 0;
1822 priv->max_day_char_ascent = 0;
1823 priv->max_day_char_descent = 0;
1824 priv->min_day_width = 0;
1826 for (i = 0; i < 9; i++)
1829 g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), i * 11);
1830 pango_layout_set_text (layout, buffer, -1);
1831 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1832 priv->min_day_width = MAX (priv->min_day_width,
1833 logical_rect.width);
1835 priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1836 PANGO_ASCENT (logical_rect));
1837 priv->max_day_char_descent = MAX (priv->max_day_char_descent,
1838 PANGO_DESCENT (logical_rect));
1841 priv->max_label_char_ascent = 0;
1842 priv->max_label_char_descent = 0;
1843 if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1844 for (i = 0; i < 7; i++)
1846 pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1847 pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
1849 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1850 priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1851 PANGO_ASCENT (logical_rect));
1852 priv->max_label_char_descent = MAX (priv->max_label_char_descent,
1853 PANGO_DESCENT (logical_rect));
1856 priv->max_week_char_width = 0;
1857 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1858 for (i = 0; i < 9; i++)
1861 g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), i * 11);
1862 pango_layout_set_text (layout, buffer, -1);
1863 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1864 priv->max_week_char_width = MAX (priv->max_week_char_width,
1865 logical_rect.width / 2);
1868 /* Calculate detail extents. Do this as late as possible since
1869 * pango_layout_set_markup is called which alters font settings. */
1870 max_detail_height = 0;
1872 if (priv->detail_func && (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS))
1874 gchar *markup, *tail;
1876 if (priv->detail_width_chars || priv->detail_height_rows)
1878 gint rows = MAX (1, priv->detail_height_rows) - 1;
1879 gsize len = priv->detail_width_chars + rows + 16;
1881 markup = tail = g_alloca (len);
1883 memcpy (tail, "<small>", 7);
1886 memset (tail, 'm', priv->detail_width_chars);
1887 tail += priv->detail_width_chars;
1889 memset (tail, '\n', rows);
1892 memcpy (tail, "</small>", 9);
1895 g_assert (len == (tail - markup));
1897 pango_layout_set_markup (layout, markup, -1);
1898 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1900 if (priv->detail_width_chars)
1901 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1902 if (priv->detail_height_rows)
1903 max_detail_height = MAX (max_detail_height, logical_rect.height);
1906 if (!priv->detail_width_chars || !priv->detail_height_rows)
1907 for (r = 0; r < 6; r++)
1908 for (c = 0; c < 7; c++)
1910 gchar *detail = gtk_calendar_get_detail (calendar, r, c);
1914 markup = g_strconcat ("<small>", detail, "</small>", NULL);
1915 pango_layout_set_markup (layout, markup, -1);
1917 if (priv->detail_width_chars)
1919 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1920 pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
1923 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1925 if (!priv->detail_width_chars)
1926 priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1927 if (!priv->detail_height_rows)
1928 max_detail_height = MAX (max_detail_height, logical_rect.height);
1936 /* We add one to max_day_char_width to be able to make the marked day "bold" */
1937 priv->max_day_char_width = priv->min_day_width / 2 + 1;
1939 main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1940 + (priv->max_week_char_width
1941 ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1945 requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1948 * Calculate the requisition height for the widget.
1951 if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1953 priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1960 if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1962 priv->day_name_h = (priv->max_label_char_ascent
1963 + priv->max_label_char_descent
1964 + 2 * (focus_padding + focus_width) + calendar_margin);
1965 calendar_margin = CALENDAR_YSEP;
1969 priv->day_name_h = 0;
1972 priv->main_h = (CALENDAR_MARGIN + calendar_margin
1973 + 6 * (priv->max_day_char_ascent
1974 + priv->max_day_char_descent
1976 + 2 * (focus_padding + focus_width))
1979 height = (priv->header_h + priv->day_name_h
1982 requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1984 g_object_unref (layout);
1988 gtk_calendar_size_allocate (GtkWidget *widget,
1989 GtkAllocation *allocation)
1991 GtkCalendar *calendar = GTK_CALENDAR (widget);
1992 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1993 gint xthickness = widget->style->xthickness;
1994 gint ythickness = widget->style->xthickness;
1997 widget->allocation = *allocation;
1999 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2001 priv->day_width = (priv->min_day_width
2002 * ((allocation->width - (xthickness + INNER_BORDER) * 2
2003 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
2004 / (7 * priv->min_day_width + priv->max_week_char_width * 2));
2005 priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
2006 - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
2007 - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
2011 priv->day_width = (allocation->width
2012 - (xthickness + INNER_BORDER) * 2
2013 - (CALENDAR_MARGIN * 2)
2014 - (DAY_XSEP * 6))/7;
2015 priv->week_width = 0;
2018 if (GTK_WIDGET_REALIZED (widget))
2020 gdk_window_move_resize (widget->window,
2021 allocation->x, allocation->y,
2022 allocation->width, allocation->height);
2023 if (priv->header_win)
2024 gdk_window_move_resize (priv->header_win,
2025 xthickness, ythickness,
2026 allocation->width - 2 * xthickness, priv->header_h);
2028 for (i = 0 ; i < 4 ; i++)
2030 if (priv->arrow_win[i])
2033 calendar_arrow_rectangle (calendar, i, &rect);
2035 gdk_window_move_resize (priv->arrow_win[i],
2036 rect.x, rect.y, rect.width, rect.height);
2040 if (priv->day_name_win)
2041 gdk_window_move_resize (priv->day_name_win,
2042 xthickness + INNER_BORDER,
2043 priv->header_h + (widget->style->ythickness + INNER_BORDER),
2044 allocation->width - (xthickness + INNER_BORDER) * 2,
2046 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2049 gdk_window_move_resize (priv->week_win,
2050 (xthickness + INNER_BORDER),
2051 priv->header_h + priv->day_name_h
2052 + (widget->style->ythickness + INNER_BORDER),
2055 gdk_window_move_resize (priv->main_win,
2056 priv->week_width + (xthickness + INNER_BORDER),
2057 priv->header_h + priv->day_name_h
2058 + (widget->style->ythickness + INNER_BORDER),
2061 - (xthickness + INNER_BORDER) * 2,
2066 gdk_window_move_resize (priv->main_win,
2067 (xthickness + INNER_BORDER),
2068 priv->header_h + priv->day_name_h
2069 + (widget->style->ythickness + INNER_BORDER),
2072 - (xthickness + INNER_BORDER) * 2,
2075 gdk_window_move_resize (priv->week_win,
2078 - (xthickness + INNER_BORDER),
2079 priv->header_h + priv->day_name_h
2080 + (widget->style->ythickness + INNER_BORDER),
2088 /****************************************
2090 ****************************************/
2093 calendar_paint_header (GtkCalendar *calendar)
2095 GtkWidget *widget = GTK_WIDGET (calendar);
2096 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2101 gint max_month_width;
2102 gint max_year_width;
2103 PangoLayout *layout;
2104 PangoRectangle logical_rect;
2110 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2111 year_left = priv->year_before;
2113 year_left = !priv->year_before;
2115 cr = gdk_cairo_create (priv->header_win);
2117 header_width = widget->allocation.width - 2 * widget->style->xthickness;
2119 max_month_width = priv->max_month_width;
2120 max_year_width = priv->max_year_width;
2122 gtk_paint_shadow (widget->style, priv->header_win,
2123 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2124 NULL, widget, "calendar",
2125 0, 0, header_width, priv->header_h);
2127 tmp_time = 1; /* Jan 1 1970, 00:00:01 UTC */
2128 tm = gmtime (&tmp_time);
2129 tm->tm_year = calendar->year - 1900;
2131 /* Translators: This dictates how the year is displayed in
2132 * gtkcalendar widget. See strftime() manual for the format.
2133 * Use only ASCII in the translation.
2135 * Also look for the msgid "year measurement template|2000".
2136 * Translate that entry to a year with the widest output of this
2139 * Don't include the prefix "calendar year format|" in the
2140 * translation. "%Y" is appropriate for most locales.
2142 strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
2143 str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
2144 layout = gtk_widget_create_pango_layout (widget, str);
2147 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2150 y = (priv->header_h - logical_rect.height) / 2;
2152 /* Draw year and its arrows */
2154 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2156 x = 3 + (max_year_width - logical_rect.width)/2;
2158 x = header_width - (3 + max_year_width
2159 - (max_year_width - logical_rect.width)/2);
2162 x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
2164 x = header_width - (3 + priv->arrow_width + max_year_width
2165 - (max_year_width - logical_rect.width)/2);
2168 gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2169 cairo_move_to (cr, x, y);
2170 pango_cairo_show_layout (cr, layout);
2173 g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
2174 pango_layout_set_text (layout, buffer, -1);
2175 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2177 if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2179 x = header_width - (3 + max_month_width
2180 - (max_month_width - logical_rect.width)/2);
2182 x = 3 + (max_month_width - logical_rect.width) / 2;
2185 x = header_width - (3 + priv->arrow_width + max_month_width
2186 - (max_month_width - logical_rect.width)/2);
2188 x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
2190 cairo_move_to (cr, x, y);
2191 pango_cairo_show_layout (cr, layout);
2193 g_object_unref (layout);
2198 calendar_paint_day_names (GtkCalendar *calendar)
2200 GtkWidget *widget = GTK_WIDGET (calendar);
2201 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2205 int day_width, cal_width;
2207 PangoLayout *layout;
2208 PangoRectangle logical_rect;
2212 cr = gdk_cairo_create (priv->day_name_win);
2214 gtk_widget_style_get (GTK_WIDGET (widget),
2215 "focus-line-width", &focus_width,
2216 "focus-padding", &focus_padding,
2219 day_width = priv->day_width;
2220 cal_width = widget->allocation.width;
2221 day_wid_sep = day_width + DAY_XSEP;
2224 * Draw rectangles as inverted background for the labels.
2227 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2228 cairo_rectangle (cr,
2229 CALENDAR_MARGIN, CALENDAR_MARGIN,
2230 cal_width-CALENDAR_MARGIN * 2,
2231 priv->day_name_h - CALENDAR_MARGIN);
2234 if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2236 cairo_rectangle (cr,
2238 priv->day_name_h - CALENDAR_YSEP,
2239 priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
2248 layout = gtk_widget_create_pango_layout (widget, NULL);
2250 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2251 for (i = 0; i < 7; i++)
2253 if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2257 day = (day + priv->week_start) % 7;
2258 g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
2260 pango_layout_set_text (layout, buffer, -1);
2261 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2265 + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2266 (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2269 + (day_width - logical_rect.width)/2),
2270 CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2271 pango_cairo_show_layout (cr, layout);
2274 g_object_unref (layout);
2279 calendar_paint_week_numbers (GtkCalendar *calendar)
2281 GtkWidget *widget = GTK_WIDGET (calendar);
2282 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2285 guint week = 0, year;
2286 gint row, x_loc, y_loc;
2289 PangoLayout *layout;
2290 PangoRectangle logical_rect;
2294 cr = gdk_cairo_create (priv->week_win);
2296 gtk_widget_style_get (GTK_WIDGET (widget),
2297 "focus-line-width", &focus_width,
2298 "focus-padding", &focus_padding,
2302 * Draw a rectangle as inverted background for the labels.
2305 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2306 if (priv->day_name_win)
2307 cairo_rectangle (cr,
2310 priv->week_width - CALENDAR_MARGIN,
2311 priv->main_h - CALENDAR_MARGIN);
2313 cairo_rectangle (cr,
2316 priv->week_width - CALENDAR_MARGIN,
2317 priv->main_h - 2 * CALENDAR_MARGIN);
2324 layout = gtk_widget_create_pango_layout (widget, NULL);
2326 gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2327 day_height = calendar_row_height (calendar);
2328 for (row = 0; row < 6; row++)
2332 year = calendar->year;
2333 if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2336 result = week_of_year (&week, &year,
2337 ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2338 + calendar->month) % 12 + 1, calendar->day[row][6]);
2339 g_return_if_fail (result);
2341 /* Translators: this defines whether the week numbers should use
2342 * localized digits or the ones used in English (0123...).
2344 * Translate to "%Id" if you want to use localized digits, or
2345 * translate to "%d" otherwise. Don't include the
2346 * "calendar:week:digits|" part in the translation.
2348 * Note that translating this doesn't guarantee that you get localized
2349 * digits. That needs support from your system and locale definition
2352 g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), week);
2353 pango_layout_set_text (layout, buffer, -1);
2354 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2356 y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2358 x_loc = (priv->week_width
2359 - logical_rect.width
2360 - CALENDAR_XSEP - focus_padding - focus_width);
2362 cairo_move_to (cr, x_loc, y_loc);
2363 pango_cairo_show_layout (cr, layout);
2366 g_object_unref (layout);
2371 calendar_invalidate_day_num (GtkCalendar *calendar,
2374 gint r, c, row, col;
2378 for (r = 0; r < 6; r++)
2379 for (c = 0; c < 7; c++)
2380 if (calendar->day_month[r][c] == MONTH_CURRENT &&
2381 calendar->day[r][c] == day)
2387 g_return_if_fail (row != -1);
2388 g_return_if_fail (col != -1);
2390 calendar_invalidate_day (calendar, row, col);
2394 calendar_invalidate_day (GtkCalendar *calendar,
2398 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2402 GdkRectangle day_rect;
2404 calendar_day_rectangle (calendar, row, col, &day_rect);
2405 gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2410 is_color_attribute (PangoAttribute *attribute,
2413 return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
2414 attribute->klass->type == PANGO_ATTR_BACKGROUND);
2418 calendar_paint_day (GtkCalendar *calendar,
2422 GtkWidget *widget = GTK_WIDGET (calendar);
2423 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2425 GdkColor *text_color;
2430 GdkRectangle day_rect;
2432 PangoLayout *layout;
2433 PangoRectangle logical_rect;
2434 gboolean overflow = FALSE;
2435 gboolean show_details;
2437 g_return_if_fail (row < 6);
2438 g_return_if_fail (col < 7);
2440 cr = gdk_cairo_create (priv->main_win);
2442 day = calendar->day[row][col];
2443 show_details = (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS);
2445 calendar_day_rectangle (calendar, row, col, &day_rect);
2447 if (calendar->day_month[row][col] == MONTH_PREV)
2449 text_color = PREV_MONTH_COLOR (widget);
2451 else if (calendar->day_month[row][col] == MONTH_NEXT)
2453 text_color = NEXT_MONTH_COLOR (widget);
2458 if (calendar->highlight_row == row && calendar->highlight_col == col)
2460 cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2461 gdk_cairo_rectangle (cr, &day_rect);
2465 if (calendar->selected_day == day)
2467 gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2468 gdk_cairo_rectangle (cr, &day_rect);
2471 if (calendar->selected_day == day)
2472 text_color = SELECTED_FG_COLOR (widget);
2473 else if (calendar->marked_date[day-1])
2474 text_color = MARKED_COLOR (widget);
2476 text_color = NORMAL_DAY_COLOR (widget);
2479 /* Translators: this defines whether the day numbers should use
2480 * localized digits or the ones used in English (0123...).
2482 * Translate to "%Id" if you want to use localized digits, or
2483 * translate to "%d" otherwise. Don't include the "calendar:day:digits|"
2484 * part in the translation.
2486 * Note that translating this doesn't guarantee that you get localized
2487 * digits. That needs support from your system and locale definition
2490 g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), day);
2492 /* Get extra information to show, if any: */
2494 if (priv->detail_func)
2495 detail = gtk_calendar_get_detail (calendar, row, col);
2499 layout = gtk_widget_create_pango_layout (widget, buffer);
2500 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2501 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2503 x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2506 gdk_cairo_set_source_color (cr, text_color);
2507 cairo_move_to (cr, x_loc, y_loc);
2508 pango_cairo_show_layout (cr, layout);
2510 if (calendar->day_month[row][col] == MONTH_CURRENT &&
2511 (calendar->marked_date[day-1] || (detail && !show_details)))
2513 cairo_move_to (cr, x_loc - 1, y_loc);
2514 pango_cairo_show_layout (cr, layout);
2517 y_loc += priv->max_day_char_descent;
2519 if (priv->detail_func && show_details)
2523 if (calendar->selected_day == day)
2524 gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
2525 else if (calendar->day_month[row][col] == MONTH_CURRENT)
2526 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
2528 gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
2530 cairo_set_line_width (cr, 1);
2531 cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
2532 cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
2540 if (detail && show_details)
2542 gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
2543 pango_layout_set_markup (layout, markup, -1);
2546 if (day == calendar->selected_day)
2548 /* Stripping colors as they conflict with selection marking. */
2550 PangoAttrList *attrs = pango_layout_get_attributes (layout);
2551 PangoAttrList *colors = NULL;
2554 colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
2556 pango_attr_list_unref (colors);
2559 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2560 pango_layout_set_width (layout, PANGO_SCALE * day_rect.width);
2562 if (priv->detail_height_rows)
2564 gint dy = day_rect.height - (y_loc - day_rect.y);
2565 pango_layout_set_height (layout, PANGO_SCALE * dy);
2566 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
2569 cairo_move_to (cr, day_rect.x, y_loc);
2570 pango_cairo_show_layout (cr, layout);
2573 if (GTK_WIDGET_HAS_FOCUS (calendar)
2574 && calendar->focus_row == row && calendar->focus_col == col)
2578 if (calendar->selected_day == day)
2579 state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2581 state = GTK_STATE_NORMAL;
2583 gtk_paint_focus (widget->style,
2586 NULL, widget, "calendar-day",
2587 day_rect.x, day_rect.y,
2588 day_rect.width, day_rect.height);
2592 priv->detail_overflow[row] |= (1 << col);
2594 priv->detail_overflow[row] &= ~(1 << col);
2596 g_object_unref (layout);
2602 calendar_paint_main (GtkCalendar *calendar)
2606 for (col = 0; col < 7; col++)
2607 for (row = 0; row < 6; row++)
2608 calendar_paint_day (calendar, row, col);
2612 calendar_invalidate_arrow (GtkCalendar *calendar,
2615 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2618 window = priv->arrow_win[arrow];
2620 gdk_window_invalidate_rect (window, NULL, FALSE);
2624 calendar_paint_arrow (GtkCalendar *calendar,
2627 GtkWidget *widget = GTK_WIDGET (calendar);
2628 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2631 window = priv->arrow_win[arrow];
2634 cairo_t *cr = gdk_cairo_create (window);
2638 state = priv->arrow_state[arrow];
2640 gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2644 gdk_drawable_get_size (window, &width, &height);
2645 if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2646 gtk_paint_arrow (widget->style, window, state,
2647 GTK_SHADOW_OUT, NULL, widget, "calendar",
2648 GTK_ARROW_LEFT, TRUE,
2649 width/2 - 3, height/2 - 4, 8, 8);
2651 gtk_paint_arrow (widget->style, window, state,
2652 GTK_SHADOW_OUT, NULL, widget, "calendar",
2653 GTK_ARROW_RIGHT, TRUE,
2654 width/2 - 4, height/2 - 4, 8, 8);
2659 gtk_calendar_expose (GtkWidget *widget,
2660 GdkEventExpose *event)
2662 GtkCalendar *calendar = GTK_CALENDAR (widget);
2663 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2666 if (GTK_WIDGET_DRAWABLE (widget))
2668 if (event->window == priv->main_win)
2669 calendar_paint_main (calendar);
2671 if (event->window == priv->header_win)
2672 calendar_paint_header (calendar);
2674 for (i = 0; i < 4; i++)
2675 if (event->window == priv->arrow_win[i])
2676 calendar_paint_arrow (calendar, i);
2678 if (event->window == priv->day_name_win)
2679 calendar_paint_day_names (calendar);
2681 if (event->window == priv->week_win)
2682 calendar_paint_week_numbers (calendar);
2683 if (event->window == widget->window)
2685 gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2686 GTK_SHADOW_IN, NULL, widget, "calendar",
2687 0, 0, widget->allocation.width, widget->allocation.height);
2695 /****************************************
2697 ****************************************/
2700 calendar_arrow_action (GtkCalendar *calendar,
2705 case ARROW_YEAR_LEFT:
2706 calendar_set_year_prev (calendar);
2708 case ARROW_YEAR_RIGHT:
2709 calendar_set_year_next (calendar);
2711 case ARROW_MONTH_LEFT:
2712 calendar_set_month_prev (calendar);
2714 case ARROW_MONTH_RIGHT:
2715 calendar_set_month_next (calendar);
2723 calendar_timer (gpointer data)
2725 GtkCalendar *calendar = data;
2726 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2727 gboolean retval = FALSE;
2731 calendar_arrow_action (calendar, priv->click_child);
2733 if (priv->need_timer)
2735 GtkSettings *settings;
2738 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2739 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2741 priv->need_timer = FALSE;
2742 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2743 timeout * SCROLL_DELAY_FACTOR,
2744 (GSourceFunc) calendar_timer,
2745 (gpointer) calendar, NULL);
2755 calendar_start_spinning (GtkCalendar *calendar,
2758 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2760 priv->click_child = click_child;
2764 GtkSettings *settings;
2767 settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2768 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2770 priv->need_timer = TRUE;
2771 priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2773 (GSourceFunc) calendar_timer,
2774 (gpointer) calendar, NULL);
2779 calendar_stop_spinning (GtkCalendar *calendar)
2781 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2785 g_source_remove (priv->timer);
2787 priv->need_timer = FALSE;
2792 calendar_main_button_press (GtkCalendar *calendar,
2793 GdkEventButton *event)
2795 GtkWidget *widget = GTK_WIDGET (calendar);
2796 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2802 x = (gint) (event->x);
2803 y = (gint) (event->y);
2805 row = calendar_row_from_y (calendar, y);
2806 col = calendar_column_from_x (calendar, x);
2808 /* If row or column isn't found, just return. */
2809 if (row == -1 || col == -1)
2812 day_month = calendar->day_month[row][col];
2814 if (event->type == GDK_BUTTON_PRESS)
2816 day = calendar->day[row][col];
2818 if (day_month == MONTH_PREV)
2819 calendar_set_month_prev (calendar);
2820 else if (day_month == MONTH_NEXT)
2821 calendar_set_month_next (calendar);
2823 if (!GTK_WIDGET_HAS_FOCUS (widget))
2824 gtk_widget_grab_focus (widget);
2826 if (event->button == 1)
2829 priv->drag_start_x = x;
2830 priv->drag_start_y = y;
2833 calendar_select_and_focus_day (calendar, day);
2835 else if (event->type == GDK_2BUTTON_PRESS)
2838 if (day_month == MONTH_CURRENT)
2839 g_signal_emit (calendar,
2840 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2846 gtk_calendar_button_press (GtkWidget *widget,
2847 GdkEventButton *event)
2849 GtkCalendar *calendar = GTK_CALENDAR (widget);
2850 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2853 if (event->window == priv->main_win)
2854 calendar_main_button_press (calendar, event);
2856 if (!GTK_WIDGET_HAS_FOCUS (widget))
2857 gtk_widget_grab_focus (widget);
2859 for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2861 if (event->window == priv->arrow_win[arrow])
2864 /* only call the action on single click, not double */
2865 if (event->type == GDK_BUTTON_PRESS)
2867 if (event->button == 1)
2868 calendar_start_spinning (calendar, arrow);
2870 calendar_arrow_action (calendar, arrow);
2881 gtk_calendar_button_release (GtkWidget *widget,
2882 GdkEventButton *event)
2884 GtkCalendar *calendar = GTK_CALENDAR (widget);
2885 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2887 if (event->button == 1)
2889 calendar_stop_spinning (calendar);
2899 gtk_calendar_motion_notify (GtkWidget *widget,
2900 GdkEventMotion *event)
2902 GtkCalendar *calendar = GTK_CALENDAR (widget);
2903 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2904 gint event_x, event_y;
2906 gint old_row, old_col;
2908 event_x = (gint) (event->x);
2909 event_y = (gint) (event->y);
2911 if (event->window == priv->main_win)
2916 if (gtk_drag_check_threshold (widget,
2917 priv->drag_start_x, priv->drag_start_y,
2918 event->x, event->y))
2920 GdkDragContext *context;
2921 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2922 gtk_target_list_add_text_targets (target_list, 0);
2923 context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
2924 1, (GdkEvent *)event);
2929 gtk_target_list_unref (target_list);
2930 gtk_drag_set_icon_default (context);
2935 row = calendar_row_from_y (calendar, event_y);
2936 col = calendar_column_from_x (calendar, event_x);
2938 if (row != calendar->highlight_row || calendar->highlight_col != col)
2940 old_row = calendar->highlight_row;
2941 old_col = calendar->highlight_col;
2942 if (old_row > -1 && old_col > -1)
2944 calendar->highlight_row = -1;
2945 calendar->highlight_col = -1;
2946 calendar_invalidate_day (calendar, old_row, old_col);
2949 calendar->highlight_row = row;
2950 calendar->highlight_col = col;
2952 if (row > -1 && col > -1)
2953 calendar_invalidate_day (calendar, row, col);
2961 gtk_calendar_enter_notify (GtkWidget *widget,
2962 GdkEventCrossing *event)
2964 GtkCalendar *calendar = GTK_CALENDAR (widget);
2965 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2967 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2969 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2970 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2973 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2975 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2976 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2979 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2981 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2982 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2985 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2987 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2988 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2995 gtk_calendar_leave_notify (GtkWidget *widget,
2996 GdkEventCrossing *event)
2998 GtkCalendar *calendar = GTK_CALENDAR (widget);
2999 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3003 if (event->window == priv->main_win)
3005 row = calendar->highlight_row;
3006 col = calendar->highlight_col;
3007 calendar->highlight_row = -1;
3008 calendar->highlight_col = -1;
3009 if (row > -1 && col > -1)
3010 calendar_invalidate_day (calendar, row, col);
3013 if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3015 priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3016 calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3019 if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3021 priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3022 calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3025 if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3027 priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3028 calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3031 if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3033 priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3034 calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3041 gtk_calendar_scroll (GtkWidget *widget,
3042 GdkEventScroll *event)
3044 GtkCalendar *calendar = GTK_CALENDAR (widget);
3046 if (event->direction == GDK_SCROLL_UP)
3048 if (!GTK_WIDGET_HAS_FOCUS (widget))
3049 gtk_widget_grab_focus (widget);
3050 calendar_set_month_prev (calendar);
3052 else if (event->direction == GDK_SCROLL_DOWN)
3054 if (!GTK_WIDGET_HAS_FOCUS (widget))
3055 gtk_widget_grab_focus (widget);
3056 calendar_set_month_next (calendar);
3065 /****************************************
3067 ****************************************/
3070 move_focus (GtkCalendar *calendar,
3073 GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3075 if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3076 (text_dir == GTK_TEXT_DIR_RTL && direction == 1))
3078 if (calendar->focus_col > 0)
3079 calendar->focus_col--;
3080 else if (calendar->focus_row > 0)
3082 calendar->focus_col = 6;
3083 calendar->focus_row--;
3086 if (calendar->focus_col < 0)
3087 calendar->focus_col = 6;
3088 if (calendar->focus_row < 0)
3089 calendar->focus_row = 5;
3093 if (calendar->focus_col < 6)
3094 calendar->focus_col++;
3095 else if (calendar->focus_row < 5)
3097 calendar->focus_col = 0;
3098 calendar->focus_row++;
3101 if (calendar->focus_col < 0)
3102 calendar->focus_col = 0;
3103 if (calendar->focus_row < 0)
3104 calendar->focus_row = 0;
3109 gtk_calendar_key_press (GtkWidget *widget,
3112 GtkCalendar *calendar;
3118 calendar = GTK_CALENDAR (widget);
3121 old_focus_row = calendar->focus_row;
3122 old_focus_col = calendar->focus_col;
3124 switch (event->keyval)
3129 if (event->state & GDK_CONTROL_MASK)
3130 calendar_set_month_prev (calendar);
3133 move_focus (calendar, -1);
3134 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3135 calendar_invalidate_day (calendar, calendar->focus_row,
3136 calendar->focus_col);
3142 if (event->state & GDK_CONTROL_MASK)
3143 calendar_set_month_next (calendar);
3146 move_focus (calendar, 1);
3147 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3148 calendar_invalidate_day (calendar, calendar->focus_row,
3149 calendar->focus_col);
3155 if (event->state & GDK_CONTROL_MASK)
3156 calendar_set_year_prev (calendar);
3159 if (calendar->focus_row > 0)
3160 calendar->focus_row--;
3161 if (calendar->focus_row < 0)
3162 calendar->focus_row = 5;
3163 if (calendar->focus_col < 0)
3164 calendar->focus_col = 6;
3165 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3166 calendar_invalidate_day (calendar, calendar->focus_row,
3167 calendar->focus_col);
3173 if (event->state & GDK_CONTROL_MASK)
3174 calendar_set_year_next (calendar);
3177 if (calendar->focus_row < 5)
3178 calendar->focus_row++;
3179 if (calendar->focus_col < 0)
3180 calendar->focus_col = 0;
3181 calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3182 calendar_invalidate_day (calendar, calendar->focus_row,
3183 calendar->focus_col);
3188 row = calendar->focus_row;
3189 col = calendar->focus_col;
3191 if (row > -1 && col > -1)
3195 day = calendar->day[row][col];
3196 if (calendar->day_month[row][col] == MONTH_PREV)
3197 calendar_set_month_prev (calendar);
3198 else if (calendar->day_month[row][col] == MONTH_NEXT)
3199 calendar_set_month_next (calendar);
3201 calendar_select_and_focus_day (calendar, day);
3209 /****************************************
3210 * Misc widget methods *
3211 ****************************************/
3214 calendar_set_background (GtkWidget *widget)
3216 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3219 if (GTK_WIDGET_REALIZED (widget))
3221 for (i = 0; i < 4; i++)
3223 if (priv->arrow_win[i])
3224 gdk_window_set_background (priv->arrow_win[i],
3225 HEADER_BG_COLOR (widget));
3227 if (priv->header_win)
3228 gdk_window_set_background (priv->header_win,
3229 HEADER_BG_COLOR (widget));
3230 if (priv->day_name_win)
3231 gdk_window_set_background (priv->day_name_win,
3232 BACKGROUND_COLOR (widget));
3234 gdk_window_set_background (priv->week_win,
3235 BACKGROUND_COLOR (widget));
3237 gdk_window_set_background (priv->main_win,
3238 BACKGROUND_COLOR (widget));
3240 gdk_window_set_background (widget->window,
3241 BACKGROUND_COLOR (widget));
3246 gtk_calendar_style_set (GtkWidget *widget,
3247 GtkStyle *previous_style)
3249 if (previous_style && GTK_WIDGET_REALIZED (widget))
3250 calendar_set_background (widget);
3254 gtk_calendar_state_changed (GtkWidget *widget,
3255 GtkStateType previous_state)
3257 GtkCalendar *calendar = GTK_CALENDAR (widget);
3258 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3261 if (!GTK_WIDGET_IS_SENSITIVE (widget))
3264 calendar_stop_spinning (calendar);
3267 for (i = 0; i < 4; i++)
3268 if (GTK_WIDGET_IS_SENSITIVE (widget))
3269 priv->arrow_state[i] = GTK_STATE_NORMAL;
3271 priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
3273 calendar_set_background (widget);
3277 gtk_calendar_grab_notify (GtkWidget *widget,
3278 gboolean was_grabbed)
3281 calendar_stop_spinning (GTK_CALENDAR (widget));
3285 gtk_calendar_focus_out (GtkWidget *widget,
3286 GdkEventFocus *event)
3288 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3289 GtkCalendar *calendar = GTK_CALENDAR (widget);
3291 calendar_queue_refresh (calendar);
3292 calendar_stop_spinning (calendar);
3300 /****************************************
3302 ****************************************/
3305 gtk_calendar_drag_data_get (GtkWidget *widget,
3306 GdkDragContext *context,
3307 GtkSelectionData *selection_data,
3311 GtkCalendar *calendar = GTK_CALENDAR (widget);
3316 date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
3317 len = g_date_strftime (str, 127, "%x", date);
3318 gtk_selection_data_set_text (selection_data, str, len);
3323 /* Get/set whether drag_motion requested the drag data and
3324 * drag_data_received should thus not actually insert the data,
3325 * since the data doesn't result from a drop.
3328 set_status_pending (GdkDragContext *context,
3329 GdkDragAction suggested_action)
3331 g_object_set_data (G_OBJECT (context),
3332 I_("gtk-calendar-status-pending"),
3333 GINT_TO_POINTER (suggested_action));
3336 static GdkDragAction
3337 get_status_pending (GdkDragContext *context)
3339 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
3340 "gtk-calendar-status-pending"));
3344 gtk_calendar_drag_leave (GtkWidget *widget,
3345 GdkDragContext *context,
3348 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3350 priv->drag_highlight = 0;
3351 gtk_drag_unhighlight (widget);
3356 gtk_calendar_drag_motion (GtkWidget *widget,
3357 GdkDragContext *context,
3362 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3365 if (!priv->drag_highlight)
3367 priv->drag_highlight = 1;
3368 gtk_drag_highlight (widget);
3371 target = gtk_drag_dest_find_target (widget, context, NULL);
3372 if (target == GDK_NONE || context->suggested_action == 0)
3373 gdk_drag_status (context, 0, time);
3376 set_status_pending (context, context->suggested_action);
3377 gtk_drag_get_data (widget, context, target, time);
3384 gtk_calendar_drag_drop (GtkWidget *widget,
3385 GdkDragContext *context,
3392 target = gtk_drag_dest_find_target (widget, context, NULL);
3393 if (target != GDK_NONE)
3395 gtk_drag_get_data (widget, context,
3405 gtk_calendar_drag_data_received (GtkWidget *widget,
3406 GdkDragContext *context,
3409 GtkSelectionData *selection_data,
3413 GtkCalendar *calendar = GTK_CALENDAR (widget);
3414 guint day, month, year;
3417 GdkDragAction suggested_action;
3419 suggested_action = get_status_pending (context);
3421 if (suggested_action)
3423 set_status_pending (context, 0);
3425 /* We are getting this data due to a request in drag_motion,
3426 * rather than due to a request in drag_drop, so we are just
3427 * supposed to call drag_status, not actually paste in the
3430 str = (gchar*) gtk_selection_data_get_text (selection_data);
3434 date = g_date_new ();
3435 g_date_set_parse (date, str);
3436 if (!g_date_valid (date))
3437 suggested_action = 0;
3442 suggested_action = 0;
3444 gdk_drag_status (context, suggested_action, time);
3449 date = g_date_new ();
3450 str = (gchar*) gtk_selection_data_get_text (selection_data);
3453 g_date_set_parse (date, str);
3457 if (!g_date_valid (date))
3459 g_warning ("Received invalid date data\n");
3461 gtk_drag_finish (context, FALSE, FALSE, time);
3465 day = g_date_get_day (date);
3466 month = g_date_get_month (date);
3467 year = g_date_get_year (date);
3470 gtk_drag_finish (context, TRUE, FALSE, time);
3473 g_object_freeze_notify (G_OBJECT (calendar));
3474 if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3475 && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
3476 gtk_calendar_select_month (calendar, month - 1, year);
3477 gtk_calendar_select_day (calendar, day);
3478 g_object_thaw_notify (G_OBJECT (calendar));
3482 /****************************************
3484 ****************************************/
3489 * Creates a new calendar, with the current date being selected.
3491 * Return value: a newly #GtkCalendar widget
3494 gtk_calendar_new (void)
3496 return g_object_new (GTK_TYPE_CALENDAR, NULL);
3500 * gtk_calendar_display_options:
3501 * @calendar: a #GtkCalendar.
3502 * @flags: the display options to set.
3504 * Sets display options (whether to display the heading and the month headings).
3506 * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
3509 gtk_calendar_display_options (GtkCalendar *calendar,
3510 GtkCalendarDisplayOptions flags)
3512 gtk_calendar_set_display_options (calendar, flags);
3516 * gtk_calendar_get_display_options:
3517 * @calendar: a #GtkCalendar
3519 * Returns the current display options of @calendar.
3521 * Return value: the display options.
3525 GtkCalendarDisplayOptions
3526 gtk_calendar_get_display_options (GtkCalendar *calendar)
3528 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3530 return calendar->display_flags;
3534 * gtk_calendar_set_display_options:
3535 * @calendar: a #GtkCalendar
3536 * @flags: the display options to set
3538 * Sets display options (whether to display the heading and the month
3544 gtk_calendar_set_display_options (GtkCalendar *calendar,
3545 GtkCalendarDisplayOptions flags)
3547 GtkWidget *widget = GTK_WIDGET (calendar);
3548 GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3551 GtkCalendarDisplayOptions old_flags;
3553 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3555 old_flags = calendar->display_flags;
3557 if (GTK_WIDGET_REALIZED (widget))
3559 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3562 if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3563 && (priv->header_win))
3565 calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3566 calendar_realize_arrows (calendar);
3570 for (i = 0; i < 4; i++)
3572 if (priv->arrow_win[i])
3574 gdk_window_set_user_data (priv->arrow_win[i],
3576 gdk_window_destroy (priv->arrow_win[i]);
3577 priv->arrow_win[i] = NULL;
3583 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3587 if (flags & GTK_CALENDAR_SHOW_HEADING)
3589 calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3590 calendar_realize_header (calendar);
3594 for (i = 0; i < 4; i++)
3596 if (priv->arrow_win[i])
3598 gdk_window_set_user_data (priv->arrow_win[i],
3600 gdk_window_destroy (priv->arrow_win[i]);
3601 priv->arrow_win[i] = NULL;
3604 gdk_window_set_user_data (priv->header_win, NULL);
3605 gdk_window_destroy (priv->header_win);
3606 priv->header_win = NULL;
3611 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3615 if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3617 calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3618 calendar_realize_day_names (calendar);
3622 gdk_window_set_user_data (priv->day_name_win, NULL);
3623 gdk_window_destroy (priv->day_name_win);
3624 priv->day_name_win = NULL;
3628 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3632 if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3634 calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3635 calendar_realize_week_numbers (calendar);
3639 gdk_window_set_user_data (priv->week_win, NULL);
3640 gdk_window_destroy (priv->week_win);
3641 priv->week_win = NULL;
3645 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3646 g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3648 if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DETAILS)
3651 calendar->display_flags = flags;
3653 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3657 calendar->display_flags = flags;
3659 g_object_freeze_notify (G_OBJECT (calendar));
3660 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3661 g_object_notify (G_OBJECT (calendar), "show-heading");
3662 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3663 g_object_notify (G_OBJECT (calendar), "show-day-names");
3664 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3665 g_object_notify (G_OBJECT (calendar), "no-month-change");
3666 if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3667 g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3668 g_object_thaw_notify (G_OBJECT (calendar));
3672 * gtk_calendar_select_month:
3673 * @calendar: a #GtkCalendar
3674 * @month: a month number between 0 and 11.
3675 * @year: the year the month is in.
3677 * Shifts the calendar to a different month.
3679 * Return value: %TRUE, always
3682 gtk_calendar_select_month (GtkCalendar *calendar,
3686 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3687 g_return_val_if_fail (month <= 11, FALSE);
3689 calendar->month = month;
3690 calendar->year = year;
3692 calendar_compute_days (calendar);
3693 calendar_queue_refresh (calendar);
3695 g_object_freeze_notify (G_OBJECT (calendar));
3696 g_object_notify (G_OBJECT (calendar), "month");
3697 g_object_notify (G_OBJECT (calendar), "year");
3698 g_object_thaw_notify (G_OBJECT (calendar));
3700 g_signal_emit (calendar,
3701 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3707 * gtk_calendar_select_day:
3708 * @calendar: a #GtkCalendar.
3709 * @day: the day number between 1 and 31, or 0 to unselect
3710 * the currently selected day.
3712 * Selects a day from the current month.
3715 gtk_calendar_select_day (GtkCalendar *calendar,
3718 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3719 g_return_if_fail (day <= 31);
3721 /* Deselect the old day */
3722 if (calendar->selected_day > 0)
3726 selected_day = calendar->selected_day;
3727 calendar->selected_day = 0;
3728 if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3729 calendar_invalidate_day_num (calendar, selected_day);
3732 calendar->selected_day = day;
3734 /* Select the new day */
3737 if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3738 calendar_invalidate_day_num (calendar, day);
3741 g_object_notify (G_OBJECT (calendar), "day");
3743 g_signal_emit (calendar,
3744 gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3749 * gtk_calendar_clear_marks:
3750 * @calendar: a #GtkCalendar
3752 * Remove all visual markers.
3755 gtk_calendar_clear_marks (GtkCalendar *calendar)
3759 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3761 for (day = 0; day < 31; day++)
3763 calendar->marked_date[day] = FALSE;
3766 calendar->num_marked_dates = 0;
3767 calendar_queue_refresh (calendar);
3771 * gtk_calendar_mark_day:
3772 * @calendar: a #GtkCalendar
3773 * @day: the day number to mark between 1 and 31.
3775 * Places a visual marker on a particular day.
3777 * Return value: %TRUE, always
3780 gtk_calendar_mark_day (GtkCalendar *calendar,
3783 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3785 if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3787 calendar->marked_date[day - 1] = TRUE;
3788 calendar->num_marked_dates++;
3789 calendar_invalidate_day_num (calendar, day);
3796 * gtk_calendar_unmark_day:
3797 * @calendar: a #GtkCalendar.
3798 * @day: the day number to unmark between 1 and 31.
3800 * Removes the visual marker from a particular day.
3802 * Return value: %TRUE, always
3805 gtk_calendar_unmark_day (GtkCalendar *calendar,
3808 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3810 if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3812 calendar->marked_date[day - 1] = FALSE;
3813 calendar->num_marked_dates--;
3814 calendar_invalidate_day_num (calendar, day);
3821 * gtk_calendar_get_date:
3822 * @calendar: a #GtkCalendar
3823 * @year: location to store the year number, or %NULL
3824 * @month: location to store the month number (between 0 and 11), or %NULL
3825 * @day: location to store the day number (between 1 and 31), or %NULL
3827 * Obtains the selected date from a #GtkCalendar.
3830 gtk_calendar_get_date (GtkCalendar *calendar,
3835 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3838 *year = calendar->year;
3841 *month = calendar->month;
3844 *day = calendar->selected_day;
3848 * gtk_calendar_set_detail_func:
3849 * @calendar: a #GtkCalendar.
3850 * @func: a function providing details for each day.
3851 * @data: data to pass to @func invokations.
3852 * @destroy: a function for releasing @data.
3854 * Installs a function which provides Pango markup with detail information
3855 * for each day. Examples for such details are holidays or appointments. That
3856 * information is shown below each day when #GtkCalendar:show-details is set.
3857 * A tooltip containing with full detail information is provided, if the entire
3858 * text should not fit into the details area, or if #GtkCalendar:show-details
3861 * The size of the details area can be restricted by setting the
3862 * #GtkCalendar:detail-width-chars and #GtkCalendar:detail-height-rows
3868 gtk_calendar_set_detail_func (GtkCalendar *calendar,
3869 GtkCalendarDetailFunc func,
3871 GDestroyNotify destroy)
3873 GtkCalendarPrivate *priv;
3875 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3877 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3879 if (priv->detail_func_destroy)
3880 priv->detail_func_destroy (priv->detail_func_user_data);
3882 priv->detail_func = func;
3883 priv->detail_func_user_data = data;
3884 priv->detail_func_destroy = destroy;
3886 gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
3887 NULL != priv->detail_func);
3888 gtk_widget_queue_resize (GTK_WIDGET (calendar));
3892 * gtk_calendar_set_detail_width_chars:
3893 * @calendar: a #GtkCalendar.
3894 * @chars: detail width in characters.
3896 * Updates the width of detail cells.
3897 * See #GtkCalendar:detail-width-chars.
3902 gtk_calendar_set_detail_width_chars (GtkCalendar *calendar,
3905 GtkCalendarPrivate *priv;
3907 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3909 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3911 if (chars != priv->detail_width_chars)
3913 priv->detail_width_chars = chars;
3914 g_object_notify (G_OBJECT (calendar), "detail-width-chars");
3915 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3920 * gtk_calendar_set_detail_height_rows:
3921 * @calendar: a #GtkCalendar.
3922 * @rows: detail height in rows.
3924 * Updates the height of detail cells.
3925 * See #GtkCalendar:detail-height-rows.
3930 gtk_calendar_set_detail_height_rows (GtkCalendar *calendar,
3933 GtkCalendarPrivate *priv;
3935 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3937 priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3939 if (rows != priv->detail_height_rows)
3941 priv->detail_height_rows = rows;
3942 g_object_notify (G_OBJECT (calendar), "detail-height-rows");
3943 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3948 * gtk_calendar_get_detail_width_chars:
3949 * @calendar: a #GtkCalendar.
3951 * Queries the width of detail cells, in characters.
3952 * See #GtkCalendar:detail-width-chars.
3956 * Return value: The width of detail cells, in characters.
3959 gtk_calendar_get_detail_width_chars (GtkCalendar *calendar)
3961 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3962 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
3966 * gtk_calendar_get_detail_height_rows:
3967 * @calendar: a #GtkCalendar.
3969 * Queries the height of detail cells, in rows.
3970 * See #GtkCalendar:detail-width-chars.
3974 * Return value: The height of detail cells, in rows.
3977 gtk_calendar_get_detail_height_rows (GtkCalendar *calendar)
3979 g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3980 return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
3984 * gtk_calendar_freeze:
3985 * @calendar: a #GtkCalendar
3987 * Does nothing. Previously locked the display of the calendar until
3988 * it was thawed with gtk_calendar_thaw().
3993 gtk_calendar_freeze (GtkCalendar *calendar)
3995 g_return_if_fail (GTK_IS_CALENDAR (calendar));
3999 * gtk_calendar_thaw:
4000 * @calendar: a #GtkCalendar
4002 * Does nothing. Previously defrosted a calendar; all the changes made
4003 * since the last gtk_calendar_freeze() were displayed.
4008 gtk_calendar_thaw (GtkCalendar *calendar)
4010 g_return_if_fail (GTK_IS_CALENDAR (calendar));
4013 #define __GTK_CALENDAR_C__
4014 #include "gtkaliasdef.c"