]> Pileus Git - ~andy/gtk/blob - gtk/gtkcalendar.c
Avoid a possible array overrun. (Coverity)
[~andy/gtk] / gtk / gtkcalendar.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GTK Calendar Widget
5  * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Groenlund
6  * 
7  * lib_date routines
8  * Copyright (c) 1995, 1996, 1997, 1998 by Steffen Beyer
9  *
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.
14  *
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.
19  *
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.
23  */
24
25 /*
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/. 
30  */
31
32 #include <config.h>
33
34 #ifdef HAVE_SYS_TIME_H
35 #include <sys/time.h>
36 #endif
37 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
38 #include <langinfo.h>
39 #endif
40 #include <string.h>
41 #include <stdlib.h>
42 #include <time.h>
43
44 #include <glib.h>
45
46 #ifdef G_OS_WIN32
47 #include <windows.h>
48 #endif
49
50 #include <glib/gprintf.h>
51
52 #undef GTK_DISABLE_DEPRECATED
53 #include "gtkcalendar.h"
54 #define GTK_DISABLE_DEPRECATED
55
56 #include "gtkdnd.h"
57 #include "gtkintl.h"
58 #include "gtkmain.h"
59 #include "gtkmarshalers.h"
60 #include "gtkprivate.h"
61 #include "gtkintl.h"
62 #include "gdk/gdkkeysyms.h"
63 #include "gtkalias.h"
64
65 /***************************************************************************/
66 /* The following date routines are taken from the lib_date package. 
67  * They have been minimally edited to avoid conflict with types defined
68  * in win32 headers.
69  */
70
71 static const guint month_length[2][13] =
72 {
73   { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
74   { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
75 };
76
77 static const guint days_in_months[2][14] =
78 {
79   { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
80   { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
81 };
82
83 static glong  calc_days(guint year, guint mm, guint dd);
84 static guint  day_of_week(guint year, guint mm, guint dd);
85 static glong  dates_difference(guint year1, guint mm1, guint dd1,
86                                guint year2, guint mm2, guint dd2);
87 static guint  weeks_in_year(guint year);
88
89 static gboolean 
90 leap (guint year)
91 {
92   return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
93 }
94
95 static guint 
96 day_of_week (guint year, guint mm, guint dd)
97 {
98   glong  days;
99   
100   days = calc_days(year, mm, dd);
101   if (days > 0L)
102     {
103       days--;
104       days %= 7L;
105       days++;
106     }
107   return( (guint) days );
108 }
109
110 static guint weeks_in_year(guint year)
111 {
112   return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
113 }
114
115 static gboolean 
116 check_date(guint year, guint mm, guint dd)
117 {
118   if (year < 1) return FALSE;
119   if ((mm < 1) || (mm > 12)) return FALSE;
120   if ((dd < 1) || (dd > month_length[leap(year)][mm])) return FALSE;
121   return TRUE;
122 }
123
124 static guint 
125 week_number(guint year, guint mm, guint dd)
126 {
127   guint first;
128   
129   first = day_of_week(year,1,1) - 1;
130   return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
131           (first < 4) );
132 }
133
134 static glong 
135 year_to_days(guint year)
136 {
137   return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
138 }
139
140
141 static glong 
142 calc_days(guint year, guint mm, guint dd)
143 {
144   gboolean lp;
145   
146   if (year < 1) return(0L);
147   if ((mm < 1) || (mm > 12)) return(0L);
148   if ((dd < 1) || (dd > month_length[(lp = leap(year))][mm])) return(0L);
149   return( year_to_days(--year) + days_in_months[lp][mm] + dd );
150 }
151
152 static gboolean 
153 week_of_year(guint *week, guint *year, guint mm, guint dd)
154 {
155   if (check_date(*year,mm,dd))
156     {
157       *week = week_number(*year,mm,dd);
158       if (*week == 0) 
159         *week = weeks_in_year(--(*year));
160       else if (*week > weeks_in_year(*year))
161         {
162           *week = 1;
163           (*year)++;
164         }
165       return TRUE;
166     }
167   return FALSE;
168 }
169
170 static glong 
171 dates_difference(guint year1, guint mm1, guint dd1,
172                  guint year2, guint mm2, guint dd2)
173 {
174   return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
175 }
176
177 /*** END OF lib_date routines ********************************************/
178
179 /* Spacing around day/week headers and main area, inside those windows */
180 #define CALENDAR_MARGIN          0
181 /* Spacing around day/week headers and main area, outside those windows */
182 #define INNER_BORDER             4
183 /* Separation between day headers and main area */
184 #define CALENDAR_YSEP            4
185 /* Separation between week headers and main area */
186 #define CALENDAR_XSEP            4
187
188 #define DAY_XSEP                 0 /* not really good for small calendar */
189 #define DAY_YSEP                 0 /* not really good for small calendar */
190
191 /* Color usage */
192 #define HEADER_FG_COLOR(widget)          (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
193 #define HEADER_BG_COLOR(widget)          (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
194 #define SELECTED_BG_COLOR(widget)        (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
195 #define SELECTED_FG_COLOR(widget)        (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
196 #define NORMAL_DAY_COLOR(widget)         (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
197 #define PREV_MONTH_COLOR(widget)         (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
198 #define NEXT_MONTH_COLOR(widget)         (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
199 #define MARKED_COLOR(widget)             (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
200 #define BACKGROUND_COLOR(widget)         (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
201 #define HIGHLIGHT_BACK_COLOR(widget)     (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
202
203 enum {
204   ARROW_YEAR_LEFT,
205   ARROW_YEAR_RIGHT,
206   ARROW_MONTH_LEFT,
207   ARROW_MONTH_RIGHT
208 };
209
210 enum {
211   MONTH_PREV,
212   MONTH_CURRENT,
213   MONTH_NEXT
214 };
215
216 enum {
217   MONTH_CHANGED_SIGNAL,
218   DAY_SELECTED_SIGNAL,
219   DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
220   PREV_MONTH_SIGNAL,
221   NEXT_MONTH_SIGNAL,
222   PREV_YEAR_SIGNAL,
223   NEXT_YEAR_SIGNAL,
224   LAST_SIGNAL
225 };
226
227 enum
228 {
229   PROP_0,
230   PROP_YEAR,
231   PROP_MONTH,
232   PROP_DAY,
233   PROP_SHOW_HEADING,
234   PROP_SHOW_DAY_NAMES,
235   PROP_NO_MONTH_CHANGE,
236   PROP_SHOW_WEEK_NUMBERS,
237   PROP_LAST
238 };
239
240 static guint gtk_calendar_signals[LAST_SIGNAL] = { 0 };
241
242 struct _GtkCalendarPrivate
243 {
244   GdkWindow *header_win;
245   GdkWindow *day_name_win;
246   GdkWindow *main_win;
247   GdkWindow *week_win;
248   GdkWindow *arrow_win[4];
249
250   guint header_h;
251   guint day_name_h;
252   guint main_h;
253
254   guint      arrow_state[4];
255   guint      arrow_width;
256   guint      max_month_width;
257   guint      max_year_width;
258   
259   guint day_width;
260   guint week_width;
261
262   guint min_day_width;
263   guint max_day_char_width;
264   guint max_day_char_ascent;
265   guint max_day_char_descent;
266   guint max_label_char_ascent;
267   guint max_label_char_descent;
268   guint max_week_char_width;
269   
270   /* flags */
271   guint year_before : 1;
272
273   guint need_timer  : 1;
274
275   guint in_drag : 1;
276   guint drag_highlight : 1;
277
278   guint32 timer;
279   gint click_child;
280
281   gint week_start;
282
283   gint drag_start_x;
284   gint drag_start_y;
285 };
286
287 #define GTK_CALENDAR_GET_PRIVATE(widget)  (GTK_CALENDAR (widget)->priv)
288
289 static void gtk_calendar_finalize     (GObject      *calendar);
290 static void gtk_calendar_destroy      (GtkObject    *calendar);
291 static void gtk_calendar_set_property (GObject      *object,
292                                        guint         prop_id,
293                                        const GValue *value,
294                                        GParamSpec   *pspec);
295 static void gtk_calendar_get_property (GObject      *object,
296                                        guint         prop_id,
297                                        GValue       *value,
298                                        GParamSpec   *pspec);
299
300 static void     gtk_calendar_realize        (GtkWidget        *widget);
301 static void     gtk_calendar_unrealize      (GtkWidget        *widget);
302 static void     gtk_calendar_size_request   (GtkWidget        *widget,
303                                              GtkRequisition   *requisition);
304 static void     gtk_calendar_size_allocate  (GtkWidget        *widget,
305                                              GtkAllocation    *allocation);
306 static gboolean gtk_calendar_expose         (GtkWidget        *widget,
307                                              GdkEventExpose   *event);
308 static gboolean gtk_calendar_button_press   (GtkWidget        *widget,
309                                              GdkEventButton   *event);
310 static gboolean gtk_calendar_button_release (GtkWidget        *widget,
311                                              GdkEventButton   *event);
312 static gboolean gtk_calendar_motion_notify  (GtkWidget        *widget,
313                                              GdkEventMotion   *event);
314 static gboolean gtk_calendar_enter_notify   (GtkWidget        *widget,
315                                              GdkEventCrossing *event);
316 static gboolean gtk_calendar_leave_notify   (GtkWidget        *widget,
317                                              GdkEventCrossing *event);
318 static gboolean gtk_calendar_scroll         (GtkWidget        *widget,
319                                              GdkEventScroll   *event);
320 static gboolean gtk_calendar_key_press      (GtkWidget        *widget,
321                                              GdkEventKey      *event);
322 static gboolean gtk_calendar_focus_out      (GtkWidget        *widget,
323                                              GdkEventFocus    *event);
324 static void     gtk_calendar_grab_notify    (GtkWidget        *widget,
325                                              gboolean          was_grabbed);
326 static void     gtk_calendar_state_changed  (GtkWidget        *widget,
327                                              GtkStateType      previous_state);
328 static void     gtk_calendar_style_set      (GtkWidget        *widget,
329                                              GtkStyle         *previous_style);
330
331 static void     gtk_calendar_drag_data_get      (GtkWidget        *widget,
332                                                  GdkDragContext   *context,
333                                                  GtkSelectionData *selection_data,
334                                                  guint             info,
335                                                  guint             time);
336 static void     gtk_calendar_drag_data_received (GtkWidget        *widget,
337                                                  GdkDragContext   *context,
338                                                  gint              x,
339                                                  gint              y,
340                                                  GtkSelectionData *selection_data,
341                                                  guint             info,
342                                                  guint             time);
343 static gboolean gtk_calendar_drag_motion        (GtkWidget        *widget,
344                                                  GdkDragContext   *context,
345                                                  gint              x,
346                                                  gint              y,
347                                                  guint             time);
348 static void     gtk_calendar_drag_leave         (GtkWidget        *widget,
349                                                  GdkDragContext   *context,
350                                                  guint             time);
351 static gboolean gtk_calendar_drag_drop          (GtkWidget        *widget,
352                                                  GdkDragContext   *context,
353                                                  gint              x,
354                                                  gint              y,
355                                                  guint             time);
356
357 static void calendar_start_spinning (GtkCalendar *calendar,
358                                      gint         click_child);
359 static void calendar_stop_spinning  (GtkCalendar *calendar);
360
361 static void calendar_invalidate_day     (GtkCalendar *widget,
362                                          gint       row,
363                                          gint       col);
364 static void calendar_invalidate_day_num (GtkCalendar *widget,
365                                          gint       day);
366 static void calendar_invalidate_arrow   (GtkCalendar *widget,
367                                          guint      arrow);
368
369 static void calendar_compute_days      (GtkCalendar *calendar);
370      
371 static char    *default_abbreviated_dayname[7];
372 static char    *default_monthname[12];
373
374 G_DEFINE_TYPE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET)
375
376 static void
377 gtk_calendar_class_init (GtkCalendarClass *class)
378 {
379   GObjectClass   *gobject_class;
380   GtkObjectClass   *object_class;
381   GtkWidgetClass *widget_class;
382
383   gobject_class = (GObjectClass*)  class;
384   object_class = (GtkObjectClass*)  class;
385   widget_class = (GtkWidgetClass*) class;
386   
387   gobject_class->set_property = gtk_calendar_set_property;
388   gobject_class->get_property = gtk_calendar_get_property;
389   gobject_class->finalize = gtk_calendar_finalize;
390
391   object_class->destroy = gtk_calendar_destroy;
392
393   widget_class->realize = gtk_calendar_realize;
394   widget_class->unrealize = gtk_calendar_unrealize;
395   widget_class->expose_event = gtk_calendar_expose;
396   widget_class->size_request = gtk_calendar_size_request;
397   widget_class->size_allocate = gtk_calendar_size_allocate;
398   widget_class->button_press_event = gtk_calendar_button_press;
399   widget_class->button_release_event = gtk_calendar_button_release;
400   widget_class->motion_notify_event = gtk_calendar_motion_notify;
401   widget_class->enter_notify_event = gtk_calendar_enter_notify;
402   widget_class->leave_notify_event = gtk_calendar_leave_notify;
403   widget_class->key_press_event = gtk_calendar_key_press;
404   widget_class->scroll_event = gtk_calendar_scroll;
405   widget_class->style_set = gtk_calendar_style_set;
406   widget_class->state_changed = gtk_calendar_state_changed;
407   widget_class->grab_notify = gtk_calendar_grab_notify;
408   widget_class->focus_out_event = gtk_calendar_focus_out;
409
410   widget_class->drag_data_get = gtk_calendar_drag_data_get;
411   widget_class->drag_motion = gtk_calendar_drag_motion;
412   widget_class->drag_leave = gtk_calendar_drag_leave;
413   widget_class->drag_drop = gtk_calendar_drag_drop;
414   widget_class->drag_data_received = gtk_calendar_drag_data_received;
415   
416   g_object_class_install_property (gobject_class,
417                                    PROP_YEAR,
418                                    g_param_spec_int ("year",
419                                                      P_("Year"),
420                                                      P_("The selected year"),
421                                                      0, G_MAXINT, 0,
422                                                      GTK_PARAM_READWRITE));
423   g_object_class_install_property (gobject_class,
424                                    PROP_MONTH,
425                                    g_param_spec_int ("month",
426                                                      P_("Month"),
427                                                      P_("The selected month (as a number between 0 and 11)"),
428                                                      0, 11, 0,
429                                                      GTK_PARAM_READWRITE));
430   g_object_class_install_property (gobject_class,
431                                    PROP_DAY,
432                                    g_param_spec_int ("day",
433                                                      P_("Day"),
434                                                      P_("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
435                                                      0, 31, 0,
436                                                      GTK_PARAM_READWRITE));
437
438 /**
439  * GtkCalendar:show-heading:
440  *
441  * Determines whether a heading is displayed.
442  *
443  * Since: 2.4
444  */
445   g_object_class_install_property (gobject_class,
446                                    PROP_SHOW_HEADING,
447                                    g_param_spec_boolean ("show-heading",
448                                                          P_("Show Heading"),
449                                                          P_("If TRUE, a heading is displayed"),
450                                                          TRUE,
451                                                          GTK_PARAM_READWRITE));
452
453 /**
454  * GtkCalendar:show-day-names:
455  *
456  * Determines whether day names are displayed.
457  *
458  * Since: 2.4
459  */
460   g_object_class_install_property (gobject_class,
461                                    PROP_SHOW_DAY_NAMES,
462                                    g_param_spec_boolean ("show-day-names",
463                                                          P_("Show Day Names"),
464                                                          P_("If TRUE, day names are displayed"),
465                                                          TRUE,
466                                                          GTK_PARAM_READWRITE));
467 /**
468  * GtkCalendar:no-month-change:
469  *
470  * Determines whether the selected month can be changed.
471  *
472  * Since: 2.4
473  */
474   g_object_class_install_property (gobject_class,
475                                    PROP_NO_MONTH_CHANGE,
476                                    g_param_spec_boolean ("no-month-change",
477                                                          P_("No Month Change"),
478                                                          P_("If TRUE, the selected month cannot be changed"),
479                                                          FALSE,
480                                                          GTK_PARAM_READWRITE));
481
482 /**
483  * GtkCalendar:show-week-numbers:
484  *
485  * Determines whether week numbers are displayed.
486  *
487  * Since: 2.4
488  */
489   g_object_class_install_property (gobject_class,
490                                    PROP_SHOW_WEEK_NUMBERS,
491                                    g_param_spec_boolean ("show-week-numbers",
492                                                          P_("Show Week Numbers"),
493                                                          P_("If TRUE, week numbers are displayed"),
494                                                          FALSE,
495                                                          GTK_PARAM_READWRITE));
496
497   gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
498     g_signal_new (I_("month_changed"),
499                   G_OBJECT_CLASS_TYPE (gobject_class),
500                   G_SIGNAL_RUN_FIRST,
501                   G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
502                   NULL, NULL,
503                   _gtk_marshal_VOID__VOID,
504                   G_TYPE_NONE, 0);
505   gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
506     g_signal_new (I_("day_selected"),
507                   G_OBJECT_CLASS_TYPE (gobject_class),
508                   G_SIGNAL_RUN_FIRST,
509                   G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
510                   NULL, NULL,
511                   _gtk_marshal_VOID__VOID,
512                   G_TYPE_NONE, 0);
513   gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
514     g_signal_new (I_("day_selected_double_click"),
515                   G_OBJECT_CLASS_TYPE (gobject_class),
516                   G_SIGNAL_RUN_FIRST,
517                   G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
518                   NULL, NULL,
519                   _gtk_marshal_VOID__VOID,
520                   G_TYPE_NONE, 0);
521   gtk_calendar_signals[PREV_MONTH_SIGNAL] =
522     g_signal_new (I_("prev_month"),
523                   G_OBJECT_CLASS_TYPE (gobject_class),
524                   G_SIGNAL_RUN_FIRST,
525                   G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
526                   NULL, NULL,
527                   _gtk_marshal_VOID__VOID,
528                   G_TYPE_NONE, 0);
529   gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
530     g_signal_new (I_("next_month"),
531                   G_OBJECT_CLASS_TYPE (gobject_class),
532                   G_SIGNAL_RUN_FIRST,
533                   G_STRUCT_OFFSET (GtkCalendarClass, next_month),
534                   NULL, NULL,
535                   _gtk_marshal_VOID__VOID,
536                   G_TYPE_NONE, 0);
537   gtk_calendar_signals[PREV_YEAR_SIGNAL] =
538     g_signal_new (I_("prev_year"),
539                   G_OBJECT_CLASS_TYPE (gobject_class),
540                   G_SIGNAL_RUN_FIRST,
541                   G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
542                   NULL, NULL,
543                   _gtk_marshal_VOID__VOID,
544                   G_TYPE_NONE, 0);
545   gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
546     g_signal_new (I_("next_year"),
547                   G_OBJECT_CLASS_TYPE (gobject_class),
548                   G_SIGNAL_RUN_FIRST,
549                   G_STRUCT_OFFSET (GtkCalendarClass, next_year),
550                   NULL, NULL,
551                   _gtk_marshal_VOID__VOID,
552                   G_TYPE_NONE, 0);
553   
554   g_type_class_add_private (gobject_class, sizeof (GtkCalendarPrivate));
555 }
556
557 static void
558 gtk_calendar_init (GtkCalendar *calendar)
559 {
560   GtkWidget *widget = GTK_WIDGET (calendar);
561   time_t secs;
562   struct tm *tm;
563   gint i;
564   char buffer[255];
565 #ifdef G_OS_WIN32
566   wchar_t wbuffer[100];
567 #else
568   time_t tmp_time;
569 #endif
570   GtkCalendarPrivate *priv;
571   gchar *year_before;
572 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
573   gchar *langinfo;
574   gint week_1stday = 0;
575   gint first_weekday = 1;
576   guint week_origin;
577 #else
578   gchar *week_start;
579 #endif
580
581   priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
582                                                        GTK_TYPE_CALENDAR,
583                                                        GtkCalendarPrivate);
584
585   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
586   
587   if (!default_abbreviated_dayname[0])
588     for (i=0; i<7; i++)
589       {
590 #ifndef G_OS_WIN32
591         tmp_time= (i+3)*86400;
592         strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
593         default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
594 #else
595         if (G_WIN32_HAVE_WIDECHAR_API ())
596           {
597             if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
598                                  wbuffer, G_N_ELEMENTS (wbuffer)))
599               default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
600             else
601               default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
602           }
603         else
604           {
605             if (!GetLocaleInfoA (GetThreadLocale (),
606                                  (LOCALE_SABBREVDAYNAME1 + (i+6)%7) | LOCALE_USE_CP_ACP,
607                                  buffer, G_N_ELEMENTS (buffer)))
608               default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
609             else
610               default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
611           }
612 #endif
613       }
614   
615   if (!default_monthname[0])
616     for (i=0; i<12; i++)
617       {
618 #ifndef G_OS_WIN32
619         tmp_time=i*2764800;
620         strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
621         default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
622 #else
623         if (G_WIN32_HAVE_WIDECHAR_API ())
624           {
625             if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
626                                  wbuffer, G_N_ELEMENTS (wbuffer)))
627               default_monthname[i] = g_strdup_printf ("(%d)", i);
628             else
629               default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
630           }
631         else
632           {
633             if (!GetLocaleInfoA (GetThreadLocale (),
634                                  (LOCALE_SMONTHNAME1 + i) | LOCALE_USE_CP_ACP,
635                                  buffer, G_N_ELEMENTS (buffer)))
636               default_monthname[i] = g_strdup_printf ("(%d)", i);
637             else
638               default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
639           }
640 #endif
641       }
642   
643   /* Set defaults */
644   secs = time (NULL);
645   tm = localtime (&secs);
646   calendar->month = tm->tm_mon;
647   calendar->year  = 1900 + tm->tm_year;
648
649   for (i=0;i<31;i++)
650     calendar->marked_date[i] = FALSE;
651   calendar->num_marked_dates = 0;
652   calendar->selected_day = tm->tm_mday;
653   
654   calendar->display_flags = ( GTK_CALENDAR_SHOW_HEADING | 
655                               GTK_CALENDAR_SHOW_DAY_NAMES );
656   
657   calendar->highlight_row = -1;
658   calendar->highlight_col = -1;
659   
660   calendar->focus_row = -1;
661   calendar->focus_col = -1;
662
663   priv->max_year_width = 0;
664   priv->max_month_width = 0;
665   priv->max_day_char_width = 0;
666   priv->max_week_char_width = 0;
667
668   priv->max_day_char_ascent = 0;
669   priv->max_day_char_descent = 0;
670   priv->max_label_char_ascent = 0;
671   priv->max_label_char_descent = 0;
672
673   priv->arrow_width = 10;
674
675   priv->need_timer = 0;
676   priv->timer = 0;
677   priv->click_child = -1;
678
679   priv->in_drag = 0;
680   priv->drag_highlight = 0;
681
682   gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
683   gtk_drag_dest_add_text_targets (widget);
684
685   priv->year_before = 0;
686
687   /* Translate to calendar:YM if you want years to be displayed
688    * before months; otherwise translate to calendar:MY.
689    * Do *not* translate it to anything else, if it
690    * it isn't calendar:YM or calendar:MY it will not work.
691    *
692    * Note that this flipping is in top of the text direction flipping,
693    * so if you have a default text direction of RTL and YM, then
694    * the year will appear on the right.
695    */
696   year_before = _("calendar:MY");
697   if (strcmp (year_before, "calendar:YM") == 0)
698     priv->year_before = 1;
699   else if (strcmp (year_before, "calendar:MY") != 0)
700     g_warning ("Whoever translated calendar:MY did so wrongly.\n");
701
702 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
703   langinfo = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
704   first_weekday = langinfo[0];
705   langinfo = nl_langinfo (_NL_TIME_WEEK_1STDAY);
706   week_origin = GPOINTER_TO_INT (langinfo);
707   if (week_origin == 19971130) /* Sunday */
708     week_1stday = 0;
709   else if (week_origin == 19971201) /* Monday */
710     week_1stday = 1;
711   else
712     g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
713
714   priv->week_start = (week_1stday + first_weekday - 1) % 7;
715 #else
716   /* Translate to calendar:week_start:0 if you want Sunday to be the
717    * first day of the week to calendar:week_start:1 if you want Monday
718    * to be the first day of the week, and so on.
719    */  
720   week_start = _("calendar:week_start:0");
721
722   if (strncmp (week_start, "calendar:week_start:", 20) == 0)
723     priv->week_start = *(week_start + 20) - '0';
724   else 
725     priv->week_start = -1;
726   
727   if (priv->week_start < 0 || priv->week_start > 6)
728     {
729       g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
730       priv->week_start = 0;
731     }
732 #endif
733
734   calendar_compute_days (calendar);
735 }
736
737 \f
738 /****************************************
739  *          Utility Functions           *
740  ****************************************/
741
742 static void
743 calendar_set_month_next (GtkCalendar *calendar)
744 {
745   gint month_len;
746   
747   g_return_if_fail (GTK_IS_WIDGET (calendar));
748   
749   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
750     return;
751   
752   
753   if (calendar->month == 11)
754     {
755       calendar->month = 0;
756       calendar->year++;
757     } 
758   else 
759     calendar->month++;
760   
761   calendar_compute_days (calendar);
762   g_signal_emit (calendar,
763                  gtk_calendar_signals[NEXT_MONTH_SIGNAL],
764                  0);
765   g_signal_emit (calendar,
766                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
767                  0);
768   
769   month_len = month_length[leap (calendar->year)][calendar->month + 1];
770   
771   if (month_len < calendar->selected_day)
772     {
773       calendar->selected_day = 0;
774       gtk_calendar_select_day (calendar, month_len);
775     }
776   else
777     gtk_calendar_select_day (calendar, calendar->selected_day);
778
779   gtk_widget_queue_draw (GTK_WIDGET (calendar));
780 }
781
782 static void
783 calendar_set_year_prev (GtkCalendar *calendar)
784 {
785   gint month_len;
786   
787   g_return_if_fail (GTK_IS_WIDGET (calendar));
788   
789   calendar->year--;
790   calendar_compute_days (calendar);
791   g_signal_emit (calendar,
792                  gtk_calendar_signals[PREV_YEAR_SIGNAL],
793                  0);
794   g_signal_emit (calendar,
795                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
796                  0);
797   
798   month_len = month_length[leap (calendar->year)][calendar->month + 1];
799   
800   if (month_len < calendar->selected_day)
801     {
802       calendar->selected_day = 0;
803       gtk_calendar_select_day (calendar, month_len);
804     }
805   else
806     gtk_calendar_select_day (calendar, calendar->selected_day);
807   
808   gtk_widget_queue_draw (GTK_WIDGET (calendar));
809 }
810
811 static void
812 calendar_set_year_next (GtkCalendar *calendar)
813 {
814   gint month_len;
815   
816   g_return_if_fail (GTK_IS_WIDGET (calendar));
817   
818   calendar->year++;
819   calendar_compute_days (calendar);
820   g_signal_emit (calendar,
821                  gtk_calendar_signals[NEXT_YEAR_SIGNAL],
822                  0);
823   g_signal_emit (calendar,
824                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
825                  0);
826   
827   month_len = month_length[leap (calendar->year)][calendar->month + 1];
828   
829   if (month_len < calendar->selected_day)
830     {
831       calendar->selected_day = 0;
832       gtk_calendar_select_day (calendar, month_len);
833     }
834   else
835     gtk_calendar_select_day (calendar, calendar->selected_day);
836   
837   gtk_widget_queue_draw (GTK_WIDGET (calendar));
838 }
839
840 static void
841 calendar_compute_days (GtkCalendar *calendar)
842 {
843   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
844   gint month;
845   gint year;
846   gint ndays_in_month;
847   gint ndays_in_prev_month;
848   gint first_day;
849   gint row;
850   gint col;
851   gint day;
852
853   g_return_if_fail (GTK_IS_CALENDAR (calendar));
854
855   year = calendar->year;
856   month = calendar->month + 1;
857   
858   ndays_in_month = month_length[leap (year)][month];
859   
860   first_day = day_of_week (year, month, 1);
861   first_day = (first_day + 7 - priv->week_start) % 7;
862   
863   /* Compute days of previous month */
864   if (month > 1)
865     ndays_in_prev_month = month_length[leap (year)][month-1];
866   else
867     ndays_in_prev_month = month_length[leap (year)][12];
868   day = ndays_in_prev_month - first_day + 1;
869   
870   row = 0;
871   if (first_day > 0)
872     {
873       for (col = 0; col < first_day; col++)
874         {
875           calendar->day[row][col] = day;
876           calendar->day_month[row][col] = MONTH_PREV;
877           day++;
878         }
879     }
880   
881   /* Compute days of current month */
882   col = first_day;
883   for (day = 1; day <= ndays_in_month; day++)
884     {
885       calendar->day[row][col] = day;
886       calendar->day_month[row][col] = MONTH_CURRENT;
887       
888       col++;
889       if (col == 7)
890         {
891           row++;
892           col = 0;
893         }
894     }
895   
896   /* Compute days of next month */
897   day = 1;
898   for (; row <= 5; row++)
899     {
900       for (; col <= 6; col++)
901         {
902           calendar->day[row][col] = day;
903           calendar->day_month[row][col] = MONTH_NEXT;
904           day++;
905         }
906       col = 0;
907     }
908 }
909
910 static void
911 calendar_select_and_focus_day (GtkCalendar *calendar,
912                                guint        day)
913 {
914   gint old_focus_row = calendar->focus_row;
915   gint old_focus_col = calendar->focus_col;
916   gint row;
917   gint col;
918   
919   for (row = 0; row < 6; row ++)
920     for (col = 0; col < 7; col++)
921       {
922         if (calendar->day_month[row][col] == MONTH_CURRENT 
923             && calendar->day[row][col] == day)
924           {
925             calendar->focus_row = row;
926             calendar->focus_col = col;
927           }
928       }
929
930   if (old_focus_row != -1 && old_focus_col != -1)
931     calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
932   
933   gtk_calendar_select_day (calendar, day);
934 }
935
936 \f
937 /****************************************
938  *     Layout computation utilities     *
939  ****************************************/
940
941 static gint
942 calendar_row_height (GtkCalendar *calendar)
943 {
944   return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
945           - ((calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
946              ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
947 }
948
949
950 /* calendar_left_x_for_column: returns the x coordinate
951  * for the left of the column */
952 static gint
953 calendar_left_x_for_column (GtkCalendar *calendar,
954                             gint         column)
955 {
956   gint width;
957   gint x_left;
958   
959   if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
960     column = 6 - column;
961
962   width = GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
963   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
964     x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
965   else
966     x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
967   
968   return x_left;
969 }
970
971 /* column_from_x: returns the column 0-6 that the
972  * x pixel of the xwindow is in */
973 static gint
974 calendar_column_from_x (GtkCalendar *calendar,
975                         gint         event_x)
976 {
977   gint c, column;
978   gint x_left, x_right;
979   
980   column = -1;
981   
982   for (c = 0; c < 7; c++)
983     {
984       x_left = calendar_left_x_for_column (calendar, c);
985       x_right = x_left + GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
986       
987       if (event_x >= x_left && event_x < x_right)
988         {
989           column = c;
990           break;
991         }
992     }
993   
994   return column;
995 }
996
997 /* calendar_top_y_for_row: returns the y coordinate
998  * for the top of the row */
999 static gint
1000 calendar_top_y_for_row (GtkCalendar *calendar,
1001                         gint         row)
1002 {
1003   
1004   return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h 
1005           - (CALENDAR_MARGIN + (6 - row)
1006              * calendar_row_height (calendar)));
1007 }
1008
1009 /* row_from_y: returns the row 0-5 that the
1010  * y pixel of the xwindow is in */
1011 static gint
1012 calendar_row_from_y (GtkCalendar *calendar,
1013                      gint         event_y)
1014 {
1015   gint r, row;
1016   gint height;
1017   gint y_top, y_bottom;
1018   
1019   height = calendar_row_height (calendar);
1020   row = -1;
1021   
1022   for (r = 0; r < 6; r++)
1023     {
1024       y_top = calendar_top_y_for_row (calendar, r);
1025       y_bottom = y_top + height;
1026       
1027       if (event_y >= y_top && event_y < y_bottom)
1028         {
1029           row = r;
1030           break;
1031         }
1032     }
1033   
1034   return row;
1035 }
1036
1037 static void
1038 calendar_arrow_rectangle (GtkCalendar  *calendar,
1039                           guint         arrow,
1040                           GdkRectangle *rect)
1041 {
1042   GtkWidget *widget = GTK_WIDGET (calendar);
1043   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1044   gboolean year_left;
1045
1046   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1047     year_left = priv->year_before;
1048   else
1049     year_left = !priv->year_before;
1050     
1051   rect->y = 3;
1052   rect->width = priv->arrow_width;
1053   rect->height = priv->header_h - 7;
1054   
1055   switch (arrow)
1056     {
1057     case ARROW_MONTH_LEFT:
1058       if (year_left) 
1059         rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1060                    - (3 + 2*priv->arrow_width 
1061                       + priv->max_month_width));
1062       else
1063         rect->x = 3;
1064       break;
1065     case ARROW_MONTH_RIGHT:
1066       if (year_left) 
1067         rect->x = (widget->allocation.width - 2 * widget->style->xthickness 
1068                    - 3 - priv->arrow_width);
1069       else
1070         rect->x = (priv->arrow_width 
1071                    + priv->max_month_width);
1072       break;
1073     case ARROW_YEAR_LEFT:
1074       if (year_left) 
1075         rect->x = 3;
1076       else
1077         rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1078                    - (3 + 2*priv->arrow_width 
1079                       + priv->max_year_width));
1080       break;
1081     case ARROW_YEAR_RIGHT:
1082       if (year_left) 
1083         rect->x = (priv->arrow_width 
1084                    + priv->max_year_width);
1085       else
1086         rect->x = (widget->allocation.width - 2 * widget->style->xthickness 
1087                    - 3 - priv->arrow_width);
1088       break;
1089     }
1090 }
1091
1092 static void
1093 calendar_day_rectangle (GtkCalendar  *calendar,
1094                         gint          row,
1095                         gint          col,
1096                         GdkRectangle *rect)
1097 {
1098   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1099
1100   rect->x = calendar_left_x_for_column (calendar, col);
1101   rect->y = calendar_top_y_for_row (calendar, row);
1102   rect->height = calendar_row_height (calendar);
1103   rect->width = priv->day_width;
1104 }
1105
1106 static void
1107 calendar_set_month_prev (GtkCalendar *calendar)
1108 {
1109   gint month_len;
1110   
1111   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1112     return;
1113   
1114   if (calendar->month == 0)
1115     {
1116       calendar->month = 11;
1117       calendar->year--;
1118     } 
1119   else 
1120     calendar->month--;
1121   
1122   month_len = month_length[leap (calendar->year)][calendar->month + 1];
1123   
1124   calendar_compute_days (calendar);
1125   
1126   g_signal_emit (calendar,
1127                  gtk_calendar_signals[PREV_MONTH_SIGNAL],
1128                  0);
1129   g_signal_emit (calendar,
1130                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1131                  0);
1132   
1133   if (month_len < calendar->selected_day)
1134     {
1135       calendar->selected_day = 0;
1136       gtk_calendar_select_day (calendar, month_len);
1137     }
1138   else
1139     {
1140       if (calendar->selected_day < 0)
1141         calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
1142       gtk_calendar_select_day (calendar, calendar->selected_day);
1143     }
1144
1145   gtk_widget_queue_draw (GTK_WIDGET (calendar));
1146 }
1147
1148 \f
1149 /****************************************
1150  *           Basic object methods       *
1151  ****************************************/
1152
1153 static void
1154 gtk_calendar_finalize (GObject *object)
1155 {
1156   (* G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize) (object);
1157 }
1158
1159 static void
1160 gtk_calendar_destroy (GtkObject *object)
1161 {
1162   calendar_stop_spinning (GTK_CALENDAR (object));
1163   
1164   GTK_OBJECT_CLASS (gtk_calendar_parent_class)->destroy (object);
1165 }
1166
1167
1168 static void
1169 calendar_set_display_option (GtkCalendar              *calendar,
1170                              GtkCalendarDisplayOptions flag,
1171                              gboolean                  setting)
1172 {
1173   GtkCalendarDisplayOptions flags;
1174   if (setting) 
1175     flags = calendar->display_flags | flag;
1176   else
1177     flags = calendar->display_flags & ~flag; 
1178   gtk_calendar_display_options (calendar, flags);
1179 }
1180
1181 static gboolean
1182 calendar_get_display_option (GtkCalendar              *calendar,
1183                              GtkCalendarDisplayOptions flag)
1184 {
1185   return (calendar->display_flags & flag) != 0;
1186 }
1187
1188 static void 
1189 gtk_calendar_set_property (GObject      *object,
1190                            guint         prop_id,
1191                            const GValue *value,
1192                            GParamSpec   *pspec)
1193 {
1194   GtkCalendar *calendar;
1195
1196   calendar = GTK_CALENDAR (object);
1197
1198   switch (prop_id) 
1199     {
1200     case PROP_YEAR:
1201       gtk_calendar_select_month (calendar,
1202                                  calendar->month,
1203                                  g_value_get_int (value));
1204       break;
1205     case PROP_MONTH:
1206       gtk_calendar_select_month (calendar,
1207                                  g_value_get_int (value),
1208                                  calendar->year);
1209       break;
1210     case PROP_DAY:
1211       gtk_calendar_select_day (calendar,
1212                                g_value_get_int (value));
1213       break;
1214     case PROP_SHOW_HEADING:
1215       calendar_set_display_option (calendar,
1216                                    GTK_CALENDAR_SHOW_HEADING,
1217                                    g_value_get_boolean (value));
1218       break;
1219     case PROP_SHOW_DAY_NAMES:
1220       calendar_set_display_option (calendar,
1221                                    GTK_CALENDAR_SHOW_DAY_NAMES,
1222                                    g_value_get_boolean (value));
1223       break;
1224     case PROP_NO_MONTH_CHANGE:
1225       calendar_set_display_option (calendar,
1226                                    GTK_CALENDAR_NO_MONTH_CHANGE,
1227                                    g_value_get_boolean (value));
1228       break;
1229     case PROP_SHOW_WEEK_NUMBERS:
1230       calendar_set_display_option (calendar,
1231                                    GTK_CALENDAR_SHOW_WEEK_NUMBERS,
1232                                    g_value_get_boolean (value));
1233       break;
1234     default:
1235       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1236       break;
1237     }
1238 }
1239
1240 static void 
1241 gtk_calendar_get_property (GObject      *object,
1242                            guint         prop_id,
1243                            GValue       *value,
1244                            GParamSpec   *pspec)
1245 {
1246   GtkCalendar *calendar;
1247
1248   calendar = GTK_CALENDAR (object);
1249
1250   switch (prop_id) 
1251     {
1252     case PROP_YEAR:
1253       g_value_set_int (value, calendar->year);
1254       break;
1255     case PROP_MONTH:
1256       g_value_set_int (value, calendar->month);
1257       break;
1258     case PROP_DAY:
1259       g_value_set_int (value, calendar->selected_day);
1260       break;
1261     case PROP_SHOW_HEADING:
1262       g_value_set_boolean (value, calendar_get_display_option (calendar,
1263                                                                GTK_CALENDAR_SHOW_HEADING));
1264       break;
1265     case PROP_SHOW_DAY_NAMES:
1266       g_value_set_boolean (value, calendar_get_display_option (calendar,
1267                                                                GTK_CALENDAR_SHOW_DAY_NAMES));
1268       break;
1269     case PROP_NO_MONTH_CHANGE:
1270       g_value_set_boolean (value, calendar_get_display_option (calendar,
1271                                                                GTK_CALENDAR_NO_MONTH_CHANGE));
1272       break;
1273     case PROP_SHOW_WEEK_NUMBERS:
1274       g_value_set_boolean (value, calendar_get_display_option (calendar,
1275                                                                GTK_CALENDAR_SHOW_WEEK_NUMBERS));
1276       break;
1277     default:
1278       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1279       break;
1280     }
1281 }
1282
1283 \f
1284 /****************************************
1285  *             Realization              *
1286  ****************************************/
1287
1288 static void
1289 calendar_realize_arrows (GtkCalendar *calendar)
1290 {
1291   GtkWidget *widget = GTK_WIDGET (calendar);
1292   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1293   GdkWindowAttr attributes;
1294   gint attributes_mask;
1295   gint i;
1296   
1297   /* Arrow windows ------------------------------------- */
1298   if (! (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1299       && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
1300     {
1301       attributes.wclass = GDK_INPUT_OUTPUT;
1302       attributes.window_type = GDK_WINDOW_CHILD;
1303       attributes.visual = gtk_widget_get_visual (widget);
1304       attributes.colormap = gtk_widget_get_colormap (widget);
1305       attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1306                                | GDK_BUTTON_PRESS_MASK  | GDK_BUTTON_RELEASE_MASK
1307                                | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1308       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1309       for (i = 0; i < 4; i++)
1310         {
1311           GdkRectangle rect;
1312           calendar_arrow_rectangle (calendar, i, &rect);
1313           
1314           attributes.x = rect.x;
1315           attributes.y = rect.y;
1316           attributes.width = rect.width;
1317           attributes.height = rect.height;
1318           priv->arrow_win[i] = gdk_window_new (priv->header_win,
1319                                                &attributes, 
1320                                                attributes_mask);
1321           if (GTK_WIDGET_IS_SENSITIVE (widget))
1322             priv->arrow_state[i] = GTK_STATE_NORMAL;
1323           else 
1324             priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
1325           gdk_window_set_background (priv->arrow_win[i],
1326                                      HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1327           gdk_window_show (priv->arrow_win[i]);
1328           gdk_window_set_user_data (priv->arrow_win[i], widget);
1329         }
1330     }
1331   else
1332     {
1333       for (i = 0; i < 4; i++)
1334         priv->arrow_win[i] = NULL;
1335     }
1336 }
1337
1338 static void
1339 calendar_realize_header (GtkCalendar *calendar)
1340 {
1341   GtkWidget *widget = GTK_WIDGET (calendar);
1342   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1343   GdkWindowAttr attributes;
1344   gint attributes_mask;
1345   
1346   /* Header window ------------------------------------- */
1347   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1348     {
1349       attributes.wclass = GDK_INPUT_OUTPUT;
1350       attributes.window_type = GDK_WINDOW_CHILD;
1351       attributes.visual = gtk_widget_get_visual (widget);
1352       attributes.colormap = gtk_widget_get_colormap (widget);
1353       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1354       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1355       attributes.x = widget->style->xthickness;
1356       attributes.y = widget->style->ythickness;
1357       attributes.width = widget->allocation.width - 2 * attributes.x;
1358       attributes.height = priv->header_h - 2 * attributes.y;
1359       priv->header_win = gdk_window_new (widget->window,
1360                                          &attributes, attributes_mask);
1361       
1362       gdk_window_set_background (priv->header_win,
1363                                  HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1364       gdk_window_show (priv->header_win);
1365       gdk_window_set_user_data (priv->header_win, widget);
1366       
1367     }
1368   else
1369     {
1370       priv->header_win = NULL;
1371     }
1372   calendar_realize_arrows (calendar);
1373 }
1374
1375 static void
1376 calendar_realize_day_names (GtkCalendar *calendar)
1377 {
1378   GtkWidget *widget = GTK_WIDGET (calendar);
1379   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1380   GdkWindowAttr attributes;
1381   gint attributes_mask;
1382   
1383   /* Day names  window --------------------------------- */
1384   if ( calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1385     {
1386       attributes.wclass = GDK_INPUT_OUTPUT;
1387       attributes.window_type = GDK_WINDOW_CHILD;
1388       attributes.visual = gtk_widget_get_visual (widget);
1389       attributes.colormap = gtk_widget_get_colormap (widget);
1390       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1391       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1392       attributes.x = (widget->style->xthickness + INNER_BORDER);
1393       attributes.y = priv->header_h + (widget->style->ythickness 
1394                                            + INNER_BORDER);
1395       attributes.width = (widget->allocation.width 
1396                           - (widget->style->xthickness + INNER_BORDER) 
1397                           * 2);
1398       attributes.height = priv->day_name_h;
1399       priv->day_name_win = gdk_window_new (widget->window,
1400                                            &attributes, 
1401                                            attributes_mask);
1402       gdk_window_set_background (priv->day_name_win, 
1403                                  BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1404       gdk_window_show (priv->day_name_win);
1405       gdk_window_set_user_data (priv->day_name_win, widget);
1406     }
1407   else
1408     {
1409       priv->day_name_win = NULL;
1410     }
1411 }
1412
1413 static void
1414 calendar_realize_week_numbers (GtkCalendar *calendar)
1415 {
1416   GtkWidget *widget = GTK_WIDGET (calendar);
1417   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1418   GdkWindowAttr attributes;
1419   gint attributes_mask;
1420   
1421   /* Week number window -------------------------------- */
1422   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1423     {
1424       attributes.wclass = GDK_INPUT_OUTPUT;
1425       attributes.window_type = GDK_WINDOW_CHILD;
1426       attributes.visual = gtk_widget_get_visual (widget);
1427       attributes.colormap = gtk_widget_get_colormap (widget);
1428       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1429       
1430       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1431       attributes.x = widget->style->xthickness + INNER_BORDER;
1432       attributes.y = (priv->header_h + priv->day_name_h 
1433                       + (widget->style->ythickness + INNER_BORDER));
1434       attributes.width = priv->week_width;
1435       attributes.height = priv->main_h;
1436       priv->week_win = gdk_window_new (widget->window,
1437                                        &attributes, attributes_mask);
1438       gdk_window_set_background (priv->week_win,  
1439                                  BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1440       gdk_window_show (priv->week_win);
1441       gdk_window_set_user_data (priv->week_win, widget);
1442     } 
1443   else
1444     {
1445       priv->week_win = NULL;
1446     }
1447 }
1448
1449 static void
1450 gtk_calendar_realize (GtkWidget *widget)
1451 {
1452   GtkCalendar *calendar = GTK_CALENDAR (widget);
1453   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1454   GdkWindowAttr attributes;
1455   gint attributes_mask;
1456
1457   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1458   
1459   attributes.x = widget->allocation.x;
1460   attributes.y = widget->allocation.y;
1461   attributes.width = widget->allocation.width;
1462   attributes.height = widget->allocation.height;
1463   attributes.wclass = GDK_INPUT_OUTPUT;
1464   attributes.window_type = GDK_WINDOW_CHILD;
1465   attributes.event_mask =  (gtk_widget_get_events (widget) 
1466                             | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1467   attributes.visual = gtk_widget_get_visual (widget);
1468   attributes.colormap = gtk_widget_get_colormap (widget);
1469   
1470   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1471   widget->window = gdk_window_new (widget->parent->window,
1472                                    &attributes, attributes_mask);
1473   
1474   widget->style = gtk_style_attach (widget->style, widget->window);
1475   
1476   /* Header window ------------------------------------- */
1477   calendar_realize_header (calendar);
1478   /* Day names  window --------------------------------- */
1479   calendar_realize_day_names (calendar);
1480   /* Week number window -------------------------------- */
1481   calendar_realize_week_numbers (calendar);
1482   /* Main Window --------------------------------------  */
1483   attributes.event_mask =  (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1484                             | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1485                             | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1486   
1487   attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
1488   attributes.y = (priv->header_h + priv->day_name_h 
1489                   + (widget->style->ythickness + INNER_BORDER));
1490   attributes.width = (widget->allocation.width - attributes.x 
1491                       - (widget->style->xthickness + INNER_BORDER));
1492   attributes.height = priv->main_h;
1493   priv->main_win = gdk_window_new (widget->window,
1494                                    &attributes, attributes_mask);
1495   gdk_window_set_background (priv->main_win, 
1496                              BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1497   gdk_window_show (priv->main_win);
1498   gdk_window_set_user_data (priv->main_win, widget);
1499   gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1500   gdk_window_show (widget->window);
1501   gdk_window_set_user_data (widget->window, widget);
1502 }
1503
1504 static void
1505 gtk_calendar_unrealize (GtkWidget *widget)
1506 {
1507   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1508   gint i;
1509   
1510   if (priv->header_win)
1511     {
1512       for (i = 0; i < 4; i++)
1513         {
1514           if (priv->arrow_win[i])
1515             {
1516               gdk_window_set_user_data (priv->arrow_win[i], NULL);
1517               gdk_window_destroy (priv->arrow_win[i]);
1518               priv->arrow_win[i] = NULL;
1519             }
1520         }
1521       gdk_window_set_user_data (priv->header_win, NULL);
1522       gdk_window_destroy (priv->header_win);
1523       priv->header_win = NULL;
1524     }
1525   
1526   if (priv->week_win)
1527     {
1528       gdk_window_set_user_data (priv->week_win, NULL);
1529       gdk_window_destroy (priv->week_win);
1530       priv->week_win = NULL;      
1531     }
1532   
1533   if (priv->main_win)
1534     {
1535       gdk_window_set_user_data (priv->main_win, NULL);
1536       gdk_window_destroy (priv->main_win);
1537       priv->main_win = NULL;      
1538     }
1539   if (priv->day_name_win)
1540     {
1541       gdk_window_set_user_data (priv->day_name_win, NULL);
1542       gdk_window_destroy (priv->day_name_win);
1543       priv->day_name_win = NULL;      
1544     }
1545   
1546   if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize)
1547     (* GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize) (widget);
1548 }
1549
1550 \f
1551 /****************************************
1552  *       Size Request and Allocate      *
1553  ****************************************/
1554
1555 static void
1556 gtk_calendar_size_request (GtkWidget      *widget,
1557                            GtkRequisition *requisition)
1558 {
1559   GtkCalendar *calendar = GTK_CALENDAR (widget);
1560   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1561   PangoLayout *layout;
1562   PangoRectangle logical_rect;
1563
1564   gint height;
1565   gint i;
1566   gint calendar_margin = CALENDAR_MARGIN;
1567   gint header_width, main_width;
1568   gint max_header_height = 0;
1569   gint focus_width;
1570   gint focus_padding;
1571   
1572   gtk_widget_style_get (GTK_WIDGET (widget),
1573                         "focus-line-width", &focus_width,
1574                         "focus-padding", &focus_padding,
1575                         NULL);
1576
1577   layout = gtk_widget_create_pango_layout (widget, NULL);
1578   
1579   /*
1580    * Calculate the requisition  width for the widget.
1581    */
1582   
1583   /* Header width */
1584   
1585   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1586     {
1587       priv->max_month_width = 0;
1588       for (i = 0; i < 12; i++)
1589         {
1590           pango_layout_set_text (layout, default_monthname[i], -1);
1591           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1592           priv->max_month_width = MAX (priv->max_month_width,
1593                                                logical_rect.width + 8);
1594           max_header_height = MAX (max_header_height, logical_rect.height); 
1595         }
1596
1597       priv->max_year_width = 0;
1598       /* Translators:  This is a text measurement template.
1599        * Translate it to the widest year text. 
1600        * 
1601        * Don't include the prefix "year measurement template|" 
1602        * in the translation.
1603        *
1604        * If you don't understand this, leave it as "2000"
1605        */
1606       pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);   
1607       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1608       priv->max_year_width = MAX (priv->max_year_width,
1609                                   logical_rect.width + 8);
1610       max_header_height = MAX (max_header_height, logical_rect.height); 
1611     } 
1612   else 
1613     {
1614       priv->max_month_width = 0;
1615       priv->max_year_width = 0;
1616     }
1617   
1618   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1619     header_width = (priv->max_month_width 
1620                     + priv->max_year_width
1621                     + 3 * 3);
1622   else
1623     header_width = (priv->max_month_width 
1624                     + priv->max_year_width
1625                     + 4 * priv->arrow_width + 3 * 3);
1626
1627   /* Mainwindow labels width */
1628   
1629   priv->max_day_char_width = 0;
1630   priv->max_day_char_ascent = 0;
1631   priv->max_day_char_descent = 0;
1632   priv->min_day_width = 0;
1633
1634   for (i = 0; i < 9; i++)
1635     {
1636       gchar buffer[32];
1637       g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), i * 11);
1638       pango_layout_set_text (layout, buffer, -1);         
1639       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1640       priv->min_day_width = MAX (priv->min_day_width,
1641                                          logical_rect.width);
1642
1643       priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1644                                                PANGO_ASCENT (logical_rect));
1645       priv->max_day_char_descent = MAX (priv->max_day_char_descent, 
1646                                                 PANGO_DESCENT (logical_rect));
1647     }
1648   /* We add one to max_day_char_width to be able to make the marked day "bold" */
1649   priv->max_day_char_width = priv->min_day_width / 2 + 1;
1650   
1651   priv->max_label_char_ascent = 0;
1652   priv->max_label_char_descent = 0;
1653   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1654     for (i = 0; i < 7; i++)
1655       {
1656         pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1657         pango_layout_line_get_pixel_extents (pango_layout_get_lines (layout)->data, NULL, &logical_rect);
1658
1659         priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1660         priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1661                                                    PANGO_ASCENT (logical_rect));
1662         priv->max_label_char_descent = MAX (priv->max_label_char_descent, 
1663                                                     PANGO_DESCENT (logical_rect));
1664       }
1665   
1666   priv->max_week_char_width = 0;
1667   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1668     for (i = 0; i < 9; i++)
1669       {
1670         gchar buffer[32];
1671         g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), i * 11);
1672         pango_layout_set_text (layout, buffer, -1);       
1673         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1674         priv->max_week_char_width = MAX (priv->max_week_char_width,
1675                                            logical_rect.width / 2);
1676       }
1677   
1678   main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1679                 + (priv->max_week_char_width
1680                    ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1681                    : 0));
1682   
1683   
1684   requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1685   
1686   /*
1687    * Calculate the requisition height for the widget.
1688    */
1689   
1690   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1691     {
1692       priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1693     }
1694   else
1695     {
1696       priv->header_h = 0;
1697     }
1698   
1699   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1700     {
1701       priv->day_name_h = (priv->max_label_char_ascent
1702                                   + priv->max_label_char_descent
1703                                   + 2 * (focus_padding + focus_width) + calendar_margin);
1704       calendar_margin = CALENDAR_YSEP;
1705     } 
1706   else
1707     {
1708       priv->day_name_h = 0;
1709     }
1710
1711   priv->main_h = (CALENDAR_MARGIN + calendar_margin
1712                           + 6 * (priv->max_day_char_ascent
1713                                  + priv->max_day_char_descent 
1714                                  + 2 * (focus_padding + focus_width))
1715                           + DAY_YSEP * 5);
1716   
1717   height = (priv->header_h + priv->day_name_h 
1718             + priv->main_h);
1719   
1720   requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1721
1722   g_object_unref (layout);
1723 }
1724
1725 static void
1726 gtk_calendar_size_allocate (GtkWidget     *widget,
1727                             GtkAllocation *allocation)
1728 {
1729   GtkCalendar *calendar = GTK_CALENDAR (widget);
1730   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1731   gint xthickness = widget->style->xthickness;
1732   gint ythickness = widget->style->xthickness;
1733   guint i;
1734   
1735   widget->allocation = *allocation;
1736     
1737   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1738     {
1739       priv->day_width = (priv->min_day_width
1740                          * ((allocation->width - (xthickness + INNER_BORDER) * 2
1741                              - (CALENDAR_MARGIN * 2) -  (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
1742                          / (7 * priv->min_day_width + priv->max_week_char_width * 2));
1743       priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
1744                            - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
1745                           - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
1746     } 
1747   else 
1748     {
1749       priv->day_width = (allocation->width
1750                          - (xthickness + INNER_BORDER) * 2
1751                          - (CALENDAR_MARGIN * 2)
1752                          - (DAY_XSEP * 6))/7;
1753       priv->week_width = 0;
1754     }
1755   
1756   if (GTK_WIDGET_REALIZED (widget))
1757     {
1758       gdk_window_move_resize (widget->window,
1759                               allocation->x, allocation->y,
1760                               allocation->width, allocation->height);
1761       if (priv->header_win)
1762         gdk_window_move_resize (priv->header_win,
1763                                 xthickness, ythickness,
1764                                 allocation->width - 2 * xthickness, priv->header_h);
1765
1766       for (i = 0 ; i < 4 ; i++)
1767         {
1768           if (priv->arrow_win[i])
1769             {
1770               GdkRectangle rect;
1771               calendar_arrow_rectangle (calendar, i, &rect);
1772           
1773               gdk_window_move_resize (priv->arrow_win[i],
1774                                       rect.x, rect.y, rect.width, rect.height);
1775             }
1776         }
1777       
1778       if (priv->day_name_win)
1779         gdk_window_move_resize (priv->day_name_win,
1780                                 xthickness + INNER_BORDER,
1781                                 priv->header_h + (widget->style->ythickness + INNER_BORDER),
1782                                 allocation->width - (xthickness + INNER_BORDER) * 2,
1783                                 priv->day_name_h);
1784       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1785         {
1786           if (priv->week_win)
1787             gdk_window_move_resize (priv->week_win,
1788                                     (xthickness + INNER_BORDER),
1789                                     priv->header_h + priv->day_name_h
1790                                     + (widget->style->ythickness + INNER_BORDER),
1791                                     priv->week_width,
1792                                     priv->main_h);
1793           gdk_window_move_resize (priv->main_win,
1794                                   priv->week_width + (xthickness + INNER_BORDER),
1795                                   priv->header_h + priv->day_name_h
1796                                   + (widget->style->ythickness + INNER_BORDER),
1797                                   allocation->width 
1798                                   - priv->week_width 
1799                                   - (xthickness + INNER_BORDER) * 2,
1800                                   priv->main_h);
1801         }
1802       else 
1803         {
1804           gdk_window_move_resize (priv->main_win,
1805                                   (xthickness + INNER_BORDER),
1806                                   priv->header_h + priv->day_name_h
1807                                   + (widget->style->ythickness + INNER_BORDER),
1808                                   allocation->width 
1809                                   - priv->week_width 
1810                                   - (xthickness + INNER_BORDER) * 2,
1811                                   priv->main_h);
1812           if (priv->week_win)
1813             gdk_window_move_resize (priv->week_win,
1814                                     allocation->width 
1815                                     - priv->week_width 
1816                                     - (xthickness + INNER_BORDER),
1817                                     priv->header_h + priv->day_name_h
1818                                     + (widget->style->ythickness + INNER_BORDER),
1819                                     priv->week_width,
1820                                     priv->main_h);
1821         }
1822     }
1823 }
1824
1825 \f
1826 /****************************************
1827  *              Repainting              *
1828  ****************************************/
1829
1830 static void
1831 calendar_paint_header (GtkCalendar *calendar)
1832 {
1833   GtkWidget *widget = GTK_WIDGET (calendar);
1834   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1835   cairo_t *cr;
1836   char buffer[255];
1837   int x, y;
1838   gint header_width;
1839   gint max_month_width;
1840   gint max_year_width;
1841   PangoLayout *layout;
1842   PangoRectangle logical_rect;
1843   gboolean year_left;
1844   time_t tmp_time;
1845   struct tm *tm;
1846   gchar *str;
1847
1848   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1849     year_left = priv->year_before;
1850   else
1851     year_left = !priv->year_before;
1852
1853   cr = gdk_cairo_create (priv->header_win);
1854   
1855   header_width = widget->allocation.width - 2 * widget->style->xthickness;
1856   
1857   max_month_width = priv->max_month_width;
1858   max_year_width = priv->max_year_width;
1859   
1860   gtk_paint_shadow (widget->style, priv->header_win,
1861                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1862                     NULL, widget, "calendar",
1863                     0, 0, header_width, priv->header_h);
1864
1865   tmp_time = 1;  /* Jan 1 1970, 00:00:01 UTC */
1866   tm = gmtime (&tmp_time);
1867   tm->tm_year = calendar->year - 1900;
1868
1869   /* Translators: This dictates how the year is displayed in
1870    * gtkcalendar widget.  See strftime() manual for the format.
1871    * Use only ASCII in the translation.
1872    *
1873    * Also look for the msgid "year measurement template|2000".  
1874    * Translate that entry to a year with the widest output of this
1875    * msgid. 
1876    * 
1877    * Don't include the prefix "calendar year format|" in the 
1878    * translation. "%Y" is appropriate for most locales.
1879    */
1880   strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
1881   str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
1882   layout = gtk_widget_create_pango_layout (widget, str);
1883   g_free (str);
1884   
1885   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1886   
1887   /* Draw title */
1888   y = (priv->header_h - logical_rect.height) / 2;
1889   
1890   /* Draw year and its arrows */
1891   
1892   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1893     if (year_left)
1894       x = 3 + (max_year_width - logical_rect.width)/2;
1895     else
1896       x = header_width - (3 + max_year_width
1897                           - (max_year_width - logical_rect.width)/2);
1898   else
1899     if (year_left)
1900       x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
1901     else
1902       x = header_width - (3 + priv->arrow_width + max_year_width
1903                           - (max_year_width - logical_rect.width)/2);
1904   
1905
1906   gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
1907   cairo_move_to (cr, x, y);
1908   pango_cairo_show_layout (cr, layout);
1909   
1910   /* Draw month */
1911   g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
1912   pango_layout_set_text (layout, buffer, -1);
1913   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1914
1915   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1916     if (year_left)
1917       x = header_width - (3 + max_month_width
1918                           - (max_month_width - logical_rect.width)/2);      
1919     else
1920     x = 3 + (max_month_width - logical_rect.width) / 2;
1921   else
1922     if (year_left)
1923       x = header_width - (3 + priv->arrow_width + max_month_width
1924                           - (max_month_width - logical_rect.width)/2);
1925     else
1926     x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
1927
1928   cairo_move_to (cr, x, y);
1929   pango_cairo_show_layout (cr, layout);
1930
1931   g_object_unref (layout);
1932   cairo_destroy (cr);
1933 }
1934
1935 static void
1936 calendar_paint_day_names (GtkCalendar *calendar)
1937 {
1938   GtkWidget *widget = GTK_WIDGET (calendar);
1939   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1940   cairo_t *cr;
1941   char buffer[255];
1942   int day,i;
1943   int day_width, cal_width;
1944   int day_wid_sep;
1945   PangoLayout *layout;
1946   PangoRectangle logical_rect;
1947   gint focus_padding;
1948   gint focus_width;
1949   
1950   cr = gdk_cairo_create (priv->day_name_win);
1951   
1952   gtk_widget_style_get (GTK_WIDGET (widget),
1953                         "focus-line-width", &focus_width,
1954                         "focus-padding", &focus_padding,
1955                         NULL);
1956   
1957   day_width = priv->day_width;
1958   cal_width = widget->allocation.width;
1959   day_wid_sep = day_width + DAY_XSEP;
1960   
1961   /*
1962    * Draw rectangles as inverted background for the labels.
1963    */
1964
1965   gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
1966   cairo_rectangle (cr,
1967                    CALENDAR_MARGIN, CALENDAR_MARGIN,
1968                    cal_width-CALENDAR_MARGIN * 2,
1969                    priv->day_name_h - CALENDAR_MARGIN);
1970   cairo_fill (cr);
1971   
1972   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1973     {
1974       cairo_rectangle (cr, 
1975                        CALENDAR_MARGIN,
1976                        priv->day_name_h - CALENDAR_YSEP,
1977                        priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
1978                        CALENDAR_YSEP);
1979       cairo_fill (cr);
1980     }
1981   
1982   /*
1983    * Write the labels
1984    */
1985
1986   layout = gtk_widget_create_pango_layout (widget, NULL);
1987
1988   gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
1989   for (i = 0; i < 7; i++)
1990     {
1991       if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1992         day = 6 - i;
1993       else
1994         day = i;
1995       day = (day + priv->week_start) % 7;
1996       g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
1997
1998       pango_layout_set_text (layout, buffer, -1);
1999       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2000
2001       cairo_move_to (cr, 
2002                      (CALENDAR_MARGIN +
2003                       + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2004                          (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2005                          : 0)
2006                       + day_wid_sep * i
2007                       + (day_width - logical_rect.width)/2),
2008                      CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2009       pango_cairo_show_layout (cr, layout);
2010     }
2011   
2012   g_object_unref (layout);
2013   cairo_destroy (cr);
2014 }
2015
2016 static void
2017 calendar_paint_week_numbers (GtkCalendar *calendar)
2018 {
2019   GtkWidget *widget = GTK_WIDGET (calendar);
2020   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2021   cairo_t *cr;
2022   gint row, week = 0, year;
2023   gint x_loc;
2024   char buffer[32];
2025   gint y_loc, day_height;
2026   PangoLayout *layout;
2027   PangoRectangle logical_rect;
2028   gint focus_padding;
2029   gint focus_width;
2030   
2031   cr = gdk_cairo_create (priv->week_win);
2032   
2033   gtk_widget_style_get (GTK_WIDGET (widget),
2034                         "focus-line-width", &focus_width,
2035                         "focus-padding", &focus_padding,
2036                         NULL);
2037   
2038   /*
2039    * Draw a rectangle as inverted background for the labels.
2040    */
2041
2042   gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2043   if (priv->day_name_win)
2044     cairo_rectangle (cr, 
2045                      CALENDAR_MARGIN,
2046                      0,
2047                      priv->week_width - CALENDAR_MARGIN,
2048                      priv->main_h - CALENDAR_MARGIN);
2049   else
2050     cairo_rectangle (cr,
2051                      CALENDAR_MARGIN,
2052                      CALENDAR_MARGIN,
2053                      priv->week_width - CALENDAR_MARGIN,
2054                      priv->main_h - 2 * CALENDAR_MARGIN);
2055   cairo_fill (cr);
2056   
2057   /*
2058    * Write the labels
2059    */
2060   
2061   layout = gtk_widget_create_pango_layout (widget, NULL);
2062   
2063   gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2064   day_height = calendar_row_height (calendar);
2065   for (row = 0; row < 6; row++)
2066     {
2067       gboolean result;
2068       
2069       year = calendar->year;
2070       if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2071         year++;
2072
2073       result = week_of_year (&week, &year,              
2074                              ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2075                               + calendar->month) % 12 + 1, calendar->day[row][6]);
2076       g_return_if_fail (result);
2077
2078       /* Translators: this defines whether the week numbers should use
2079        * localized digits or the ones used in English (0123...).
2080        *
2081        * Translate to "%Id" if you want to use localized digits, or
2082        * translate to "%d" otherwise.  Don't include the
2083        * "calendar:week:digits|" part in the translation.
2084        *
2085        * Note that translating this doesn't guarantee that you get localized
2086        * digits.  That needs support from your system and locale definition
2087        * too.
2088        */
2089       g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), week);
2090       pango_layout_set_text (layout, buffer, -1);
2091       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2092
2093       y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2094
2095       x_loc = (priv->week_width
2096                - logical_rect.width
2097                - CALENDAR_XSEP - focus_padding - focus_width);
2098
2099       cairo_move_to (cr, x_loc, y_loc);
2100       pango_cairo_show_layout (cr, layout);
2101     }
2102   
2103   g_object_unref (layout);
2104   cairo_destroy (cr);
2105 }
2106
2107 static void
2108 calendar_invalidate_day_num (GtkCalendar *calendar,
2109                              gint         day)
2110 {
2111   gint r, c, row, col;
2112   
2113   row = -1;
2114   col = -1;
2115   for (r = 0; r < 6; r++)
2116     for (c = 0; c < 7; c++)
2117       if (calendar->day_month[r][c] == MONTH_CURRENT &&
2118           calendar->day[r][c] == day)
2119         {
2120           row = r;
2121           col = c;
2122         }
2123   
2124   g_return_if_fail (row != -1);
2125   g_return_if_fail (col != -1);
2126   
2127   calendar_invalidate_day (calendar, row, col);
2128 }
2129
2130 static void
2131 calendar_invalidate_day (GtkCalendar *calendar,
2132                          gint         row,
2133                          gint         col)
2134 {
2135   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2136
2137   if (priv->main_win)
2138     {
2139       GdkRectangle day_rect;
2140       
2141       calendar_day_rectangle (calendar, row, col, &day_rect);
2142       gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2143     }
2144 }
2145
2146 static void
2147 calendar_paint_day (GtkCalendar *calendar,
2148                     gint             row,
2149                     gint             col)
2150 {
2151   GtkWidget *widget = GTK_WIDGET (calendar);
2152   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2153   cairo_t *cr;
2154   GdkColor *text_color;
2155   gchar buffer[32];
2156   gint day;
2157   gint x_loc, y_loc;
2158   GdkRectangle day_rect;
2159
2160   PangoLayout *layout;
2161   PangoRectangle logical_rect;
2162   
2163   g_return_if_fail (row < 6);
2164   g_return_if_fail (col < 7);
2165
2166   cr = gdk_cairo_create (priv->main_win);
2167
2168   day = calendar->day[row][col];
2169
2170   calendar_day_rectangle (calendar, row, col, &day_rect);
2171   
2172   if (calendar->day_month[row][col] == MONTH_PREV)
2173     {
2174       text_color = PREV_MONTH_COLOR (widget);
2175     } 
2176   else if (calendar->day_month[row][col] == MONTH_NEXT)
2177     {
2178       text_color =  NEXT_MONTH_COLOR (widget);
2179     } 
2180   else 
2181     {
2182 #if 0      
2183       if (calendar->highlight_row == row && calendar->highlight_col == col)
2184         {
2185           cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2186           gdk_cairo_rectangle (cr, &day_rect);
2187           cairo_fill (cr);
2188         }
2189 #endif     
2190       if (calendar->selected_day == day)
2191         {
2192           gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2193           gdk_cairo_rectangle (cr, &day_rect);
2194           cairo_fill (cr);
2195         }
2196       if (calendar->selected_day == day)
2197         text_color = SELECTED_FG_COLOR (widget);
2198       else if (calendar->marked_date[day-1])
2199         text_color = MARKED_COLOR (widget);
2200       else
2201         text_color = NORMAL_DAY_COLOR (widget);
2202     }
2203
2204   /* Translators: this defines whether the day numbers should use
2205    * localized digits or the ones used in English (0123...).
2206    *
2207    * Translate to "%Id" if you want to use localized digits, or
2208    * translate to "%d" otherwise.  Don't include the "calendar:day:digits|"
2209    * part in the translation.
2210    *
2211    * Note that translating this doesn't guarantee that you get localized
2212    * digits.  That needs support from your system and locale definition
2213    * too.
2214    */
2215   g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), day);
2216   layout = gtk_widget_create_pango_layout (widget, buffer);
2217   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2218   
2219   x_loc = day_rect.x + day_rect.width / 2 + priv->max_day_char_width;
2220   x_loc -= logical_rect.width;
2221   y_loc = day_rect.y + (day_rect.height - logical_rect.height) / 2;
2222   
2223   gdk_cairo_set_source_color (cr, text_color);
2224   cairo_move_to (cr, x_loc, y_loc);
2225   pango_cairo_show_layout (cr, layout);
2226     
2227   if (calendar->marked_date[day-1]
2228       && calendar->day_month[row][col] == MONTH_CURRENT)
2229     {
2230       cairo_move_to (cr, x_loc - 1, y_loc);
2231       pango_cairo_show_layout (cr, layout);
2232     }
2233
2234   if (GTK_WIDGET_HAS_FOCUS (calendar) 
2235       && calendar->focus_row == row && calendar->focus_col == col)
2236     {
2237       GtkStateType state;
2238
2239       if (calendar->selected_day == day)
2240         state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2241       else
2242         state = GTK_STATE_NORMAL;
2243       
2244       gtk_paint_focus (widget->style, 
2245                        priv->main_win,
2246                        state,
2247 #if 0
2248                        (calendar->selected_day == day) 
2249                           ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, 
2250 #endif
2251                        NULL, widget, "calendar-day",
2252                        day_rect.x,     day_rect.y, 
2253                        day_rect.width, day_rect.height);
2254     }
2255
2256   g_object_unref (layout);
2257   cairo_destroy (cr);
2258 }
2259
2260 static void
2261 calendar_paint_main (GtkCalendar *calendar)
2262 {
2263   gint row, col;
2264   
2265   for (col = 0; col < 7; col++)
2266     for (row = 0; row < 6; row++)
2267       calendar_paint_day (calendar, row, col);
2268 }
2269
2270 static void
2271 calendar_invalidate_arrow (GtkCalendar *calendar,
2272                            guint        arrow)
2273 {
2274   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2275   GdkWindow *window;
2276   
2277   window = priv->arrow_win[arrow];
2278   if (window)
2279     gdk_window_invalidate_rect (window, NULL, FALSE);
2280 }
2281
2282 static void
2283 calendar_paint_arrow (GtkCalendar *calendar,
2284                       guint            arrow)
2285 {
2286   GtkWidget *widget = GTK_WIDGET (calendar);
2287   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2288   GdkWindow *window;
2289   
2290   window = priv->arrow_win[arrow];
2291   if (window)
2292     {
2293       cairo_t *cr = gdk_cairo_create (window);
2294       gint width, height;
2295       gint state;
2296         
2297       state = priv->arrow_state[arrow];
2298
2299       gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2300       cairo_paint (cr);
2301       cairo_destroy (cr);
2302       
2303       gdk_drawable_get_size (window, &width, &height);
2304       if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2305         gtk_paint_arrow (widget->style, window, state, 
2306                          GTK_SHADOW_OUT, NULL, widget, "calendar",
2307                          GTK_ARROW_LEFT, TRUE, 
2308                          width/2 - 3, height/2 - 4, 8, 8);
2309       else 
2310         gtk_paint_arrow (widget->style, window, state, 
2311                          GTK_SHADOW_OUT, NULL, widget, "calendar",
2312                          GTK_ARROW_RIGHT, TRUE, 
2313                          width/2 - 2, height/2 - 4, 8, 8);
2314     }
2315 }
2316
2317 static gboolean
2318 gtk_calendar_expose (GtkWidget      *widget,
2319                      GdkEventExpose *event)
2320 {
2321   GtkCalendar *calendar = GTK_CALENDAR (widget);
2322   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2323   int i;
2324
2325   if (GTK_WIDGET_DRAWABLE (widget))
2326     {
2327       if (event->window == priv->main_win)
2328         calendar_paint_main (calendar);
2329       
2330       if (event->window == priv->header_win)
2331         calendar_paint_header (calendar);
2332
2333       for (i = 0; i < 4; i++)
2334         if (event->window == priv->arrow_win[i])
2335           calendar_paint_arrow (calendar, i);
2336       
2337       if (event->window == priv->day_name_win)
2338         calendar_paint_day_names (calendar);
2339       
2340       if (event->window == priv->week_win)
2341         calendar_paint_week_numbers (calendar);
2342       if (event->window == widget->window)
2343         {
2344           gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2345                             GTK_SHADOW_IN, NULL, widget, "calendar",
2346                             0, 0, widget->allocation.width, widget->allocation.height);
2347         }
2348     }
2349   
2350   return FALSE;
2351 }
2352
2353 \f
2354 /****************************************
2355  *           Mouse handling             *
2356  ****************************************/
2357
2358 static void
2359 calendar_arrow_action (GtkCalendar *calendar,
2360                        guint        arrow)
2361 {
2362   switch (arrow)
2363     {
2364     case ARROW_YEAR_LEFT:
2365       calendar_set_year_prev (calendar);
2366       break;
2367     case ARROW_YEAR_RIGHT:
2368       calendar_set_year_next (calendar);
2369       break;
2370     case ARROW_MONTH_LEFT:
2371       calendar_set_month_prev (calendar);
2372       break;
2373     case ARROW_MONTH_RIGHT:
2374       calendar_set_month_next (calendar);
2375       break;
2376     default:;
2377       /* do nothing */
2378     }
2379 }
2380
2381 static gboolean
2382 calendar_timer (gpointer data)
2383 {
2384   GtkCalendar *calendar = data;
2385   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2386   gboolean retval = FALSE;
2387   
2388   GDK_THREADS_ENTER ();
2389
2390   if (priv->timer)
2391     {
2392       calendar_arrow_action (calendar, priv->click_child);
2393
2394       if (priv->need_timer)
2395         {
2396           GtkSettings *settings;
2397           guint        timeout;
2398
2399           settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2400           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2401
2402           priv->need_timer = FALSE;
2403           priv->timer = g_timeout_add (timeout,
2404                                        (GSourceFunc) calendar_timer,
2405                                        (gpointer) calendar);
2406         }
2407       else 
2408         retval = TRUE;
2409     }
2410
2411   GDK_THREADS_LEAVE ();
2412
2413   return retval;
2414 }
2415
2416 static void
2417 calendar_start_spinning (GtkCalendar *calendar,
2418                          gint         click_child)
2419 {
2420   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2421
2422   priv->click_child = click_child;
2423   
2424   if (!priv->timer)
2425     {
2426       GtkSettings *settings;
2427       guint        timeout;
2428
2429       settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2430       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2431
2432       priv->need_timer = TRUE;
2433       priv->timer = g_timeout_add (timeout,
2434                                    calendar_timer,
2435                                    calendar);
2436     }
2437 }
2438
2439 static void
2440 calendar_stop_spinning (GtkCalendar *calendar)
2441 {
2442   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2443
2444   if (priv->timer)
2445     {
2446       g_source_remove (priv->timer);
2447       priv->timer = 0;
2448       priv->need_timer = FALSE;
2449     }
2450 }
2451
2452 static void
2453 calendar_main_button_press (GtkCalendar    *calendar,
2454                             GdkEventButton *event)
2455 {
2456   GtkWidget *widget = GTK_WIDGET (calendar);
2457   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2458   gint x, y;
2459   gint row, col;
2460   gint day_month;
2461   gint day;
2462   
2463   x = (gint) (event->x);
2464   y = (gint) (event->y);
2465   
2466   row = calendar_row_from_y (calendar, y);
2467   col = calendar_column_from_x (calendar, x);
2468
2469   /* If row or column isn't found, just return. */
2470   if (row == -1 || col == -1)
2471     return;
2472   
2473   day_month = calendar->day_month[row][col];
2474
2475   if (event->type == GDK_BUTTON_PRESS)
2476     {
2477       day = calendar->day[row][col];
2478       
2479       if (day_month == MONTH_PREV)
2480         calendar_set_month_prev (calendar);
2481       else if (day_month == MONTH_NEXT)
2482         calendar_set_month_next (calendar);
2483       
2484       if (!GTK_WIDGET_HAS_FOCUS (widget))
2485         gtk_widget_grab_focus (widget);
2486           
2487       if (event->button == 1) 
2488         {
2489           priv->in_drag = 1;
2490           priv->drag_start_x = x;
2491           priv->drag_start_y = y;
2492         }
2493
2494       calendar_select_and_focus_day (calendar, day);
2495     }
2496   else if (event->type == GDK_2BUTTON_PRESS)
2497     {
2498       priv->in_drag = 0;
2499       if (day_month == MONTH_CURRENT)
2500         g_signal_emit (calendar,
2501                        gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2502                        0);
2503     }
2504 }
2505
2506 static gboolean
2507 gtk_calendar_button_press (GtkWidget      *widget,
2508                            GdkEventButton *event)
2509 {
2510   GtkCalendar *calendar = GTK_CALENDAR (widget);
2511   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2512   gint arrow = -1;
2513   
2514   if (event->window == priv->main_win)
2515     calendar_main_button_press (calendar, event);
2516
2517   if (!GTK_WIDGET_HAS_FOCUS (widget))
2518     gtk_widget_grab_focus (widget);
2519
2520   for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2521     {
2522       if (event->window == priv->arrow_win[arrow])
2523         {
2524           
2525           /* only call the action on single click, not double */
2526           if (event->type == GDK_BUTTON_PRESS)
2527             {
2528               if (event->button == 1)
2529                 calendar_start_spinning (calendar, arrow);
2530
2531               calendar_arrow_action (calendar, arrow);        
2532             }
2533
2534           return TRUE;
2535         }
2536     }
2537
2538   return FALSE;
2539 }
2540
2541 static gboolean
2542 gtk_calendar_button_release (GtkWidget    *widget,
2543                              GdkEventButton *event)
2544 {
2545   GtkCalendar *calendar = GTK_CALENDAR (widget);
2546   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2547
2548   if (event->button == 1) 
2549     {
2550       calendar_stop_spinning (calendar);
2551
2552       if (priv->in_drag)
2553         priv->in_drag = 0;
2554     }
2555
2556   return TRUE;
2557 }
2558
2559 static gboolean
2560 gtk_calendar_motion_notify (GtkWidget      *widget,
2561                             GdkEventMotion *event)
2562 {
2563   GtkCalendar *calendar = GTK_CALENDAR (widget);
2564   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2565   gint event_x, event_y;
2566   gint row, col;
2567   gint old_row, old_col;
2568   
2569   event_x = (gint) (event->x);
2570   event_y = (gint) (event->y);
2571   
2572   if (event->window == priv->main_win)
2573     {
2574       
2575       if (priv->in_drag) 
2576         {
2577           if (gtk_drag_check_threshold (widget,
2578                                         priv->drag_start_x, priv->drag_start_y,
2579                                         event->x, event->y))
2580             {
2581               GdkDragContext *context;
2582               GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2583               gtk_target_list_add_text_targets (target_list, 0);
2584               context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
2585                                         1, (GdkEvent *)event);
2586
2587           
2588               priv->in_drag = 0;
2589               
2590               gtk_target_list_unref (target_list);
2591               gtk_drag_set_icon_default (context);
2592             }
2593         }
2594       else 
2595         {
2596           row = calendar_row_from_y (calendar, event_y);
2597           col = calendar_column_from_x (calendar, event_x);
2598           
2599           if (row != calendar->highlight_row || calendar->highlight_col != col)
2600             {
2601               old_row = calendar->highlight_row;
2602               old_col = calendar->highlight_col;
2603               if (old_row > -1 && old_col > -1)
2604                 {
2605                   calendar->highlight_row = -1;
2606                   calendar->highlight_col = -1;
2607                   calendar_invalidate_day (calendar, old_row, old_col);
2608                 }
2609               
2610               calendar->highlight_row = row;
2611               calendar->highlight_col = col;
2612               
2613               if (row > -1 && col > -1)
2614                 calendar_invalidate_day (calendar, row, col);
2615             }
2616         }
2617     }
2618   return TRUE;
2619 }
2620
2621 static gboolean
2622 gtk_calendar_enter_notify (GtkWidget        *widget,
2623                            GdkEventCrossing *event)
2624 {
2625   GtkCalendar *calendar = GTK_CALENDAR (widget);
2626   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2627   
2628   if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2629     {
2630       priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2631       calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2632     }
2633   
2634   if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2635     {
2636       priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2637       calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2638     }
2639   
2640   if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2641     {
2642       priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2643       calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2644     }
2645   
2646   if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2647     {
2648       priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2649       calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2650     }
2651   
2652   return TRUE;
2653 }
2654
2655 static gboolean
2656 gtk_calendar_leave_notify (GtkWidget        *widget,
2657                            GdkEventCrossing *event)
2658 {
2659   GtkCalendar *calendar = GTK_CALENDAR (widget);
2660   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2661   gint row;
2662   gint col;
2663   
2664   if (event->window == priv->main_win)
2665     {
2666       row = calendar->highlight_row;
2667       col = calendar->highlight_col;
2668       calendar->highlight_row = -1;
2669       calendar->highlight_col = -1;
2670       if (row > -1 && col > -1)
2671         calendar_invalidate_day (calendar, row, col);
2672     }
2673   
2674   if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2675     {
2676       priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
2677       calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2678     }
2679   
2680   if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2681     {
2682       priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
2683       calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2684     }
2685   
2686   if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2687     {
2688       priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
2689       calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2690     }
2691   
2692   if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2693     {
2694       priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
2695       calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2696     }
2697   
2698   return TRUE;
2699 }
2700
2701 static gboolean
2702 gtk_calendar_scroll (GtkWidget      *widget,
2703                      GdkEventScroll *event)
2704 {
2705   GtkCalendar *calendar = GTK_CALENDAR (widget);
2706
2707   if (event->direction == GDK_SCROLL_UP) 
2708     {
2709       if (!GTK_WIDGET_HAS_FOCUS (widget))
2710         gtk_widget_grab_focus (widget);
2711       calendar_set_month_prev (calendar);
2712     }
2713   else if (event->direction == GDK_SCROLL_DOWN) 
2714     {
2715       if (!GTK_WIDGET_HAS_FOCUS (widget))
2716         gtk_widget_grab_focus (widget);
2717       calendar_set_month_next (calendar);
2718     }
2719   else
2720     return FALSE;
2721
2722   return TRUE;
2723 }
2724
2725 \f
2726 /****************************************
2727  *             Key handling              *
2728  ****************************************/
2729
2730 static void 
2731 move_focus (GtkCalendar *calendar, 
2732             gint         direction)
2733 {
2734   GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
2735
2736   if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
2737       (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
2738     {
2739       if (calendar->focus_col > 0)
2740           calendar->focus_col--;
2741       else if (calendar->focus_row > 0)
2742         {
2743           calendar->focus_col = 6;
2744           calendar->focus_row--;
2745         }
2746     }
2747   else 
2748     {
2749       if (calendar->focus_col < 6)
2750         calendar->focus_col++;
2751       else if (calendar->focus_row < 5)
2752         {
2753           calendar->focus_col = 0;
2754           calendar->focus_row++;
2755         }
2756     }
2757 }
2758
2759 static gboolean
2760 gtk_calendar_key_press (GtkWidget   *widget,
2761                         GdkEventKey *event)
2762 {
2763   GtkCalendar *calendar;
2764   gint return_val;
2765   gint old_focus_row;
2766   gint old_focus_col;
2767   gint row, col, day;
2768   
2769   calendar = GTK_CALENDAR (widget);
2770   return_val = FALSE;
2771   
2772   old_focus_row = calendar->focus_row;
2773   old_focus_col = calendar->focus_col;
2774
2775   switch (event->keyval)
2776     {
2777     case GDK_KP_Left:
2778     case GDK_Left:
2779       return_val = TRUE;
2780       if (event->state & GDK_CONTROL_MASK)
2781         calendar_set_month_prev (calendar);
2782       else
2783         {
2784           move_focus (calendar, -1);
2785           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2786           calendar_invalidate_day (calendar, calendar->focus_row,
2787                                    calendar->focus_col);
2788         }
2789       break;
2790     case GDK_KP_Right:
2791     case GDK_Right:
2792       return_val = TRUE;
2793       if (event->state & GDK_CONTROL_MASK)
2794         calendar_set_month_next (calendar);
2795       else
2796         {
2797           move_focus (calendar, 1);
2798           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2799           calendar_invalidate_day (calendar, calendar->focus_row,
2800                                    calendar->focus_col);
2801         }
2802       break;
2803     case GDK_KP_Up:
2804     case GDK_Up:
2805       return_val = TRUE;
2806       if (event->state & GDK_CONTROL_MASK)
2807         calendar_set_year_prev (calendar);
2808       else
2809         {
2810           if (calendar->focus_row > 0)
2811             calendar->focus_row--;
2812           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2813           calendar_invalidate_day (calendar, calendar->focus_row,
2814                                    calendar->focus_col);
2815         }
2816       break;
2817     case GDK_KP_Down:
2818     case GDK_Down:
2819       return_val = TRUE;
2820       if (event->state & GDK_CONTROL_MASK)
2821         calendar_set_year_next (calendar);
2822       else
2823         {
2824           if (calendar->focus_row < 5)
2825             calendar->focus_row++;
2826           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2827           calendar_invalidate_day (calendar, calendar->focus_row,
2828                                    calendar->focus_col);
2829         }
2830       break;
2831     case GDK_KP_Space:
2832     case GDK_space:
2833       row = calendar->focus_row;
2834       col = calendar->focus_col;
2835       
2836       if (row > -1 && col > -1)
2837         {
2838           return_val = TRUE;
2839
2840           day = calendar->day[row][col];
2841           if (calendar->day_month[row][col] == MONTH_PREV)
2842             calendar_set_month_prev (calendar);
2843           else if (calendar->day_month[row][col] == MONTH_NEXT)
2844             calendar_set_month_next (calendar);
2845
2846           calendar_select_and_focus_day (calendar, day);
2847         }
2848     }   
2849   
2850   return return_val;
2851 }
2852
2853 \f
2854 /****************************************
2855  *           Misc widget methods        *
2856  ****************************************/
2857
2858 static void
2859 calendar_set_background (GtkWidget *widget)
2860 {
2861   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2862   gint i;
2863   
2864   if (GTK_WIDGET_REALIZED (widget))
2865     {
2866       for (i = 0; i < 4; i++)
2867         {
2868           if (priv->arrow_win[i])
2869             gdk_window_set_background (priv->arrow_win[i], 
2870                                        HEADER_BG_COLOR (widget));
2871         }
2872       if (priv->header_win)
2873         gdk_window_set_background (priv->header_win, 
2874                                    HEADER_BG_COLOR (widget));
2875       if (priv->day_name_win)
2876         gdk_window_set_background (priv->day_name_win, 
2877                                    BACKGROUND_COLOR (widget));
2878       if (priv->week_win)
2879         gdk_window_set_background (priv->week_win,
2880                                    BACKGROUND_COLOR (widget));
2881       if (priv->main_win)
2882         gdk_window_set_background (priv->main_win,
2883                                    BACKGROUND_COLOR (widget));
2884       if (widget->window)
2885         gdk_window_set_background (widget->window,
2886                                    BACKGROUND_COLOR (widget)); 
2887     }
2888 }
2889
2890 static void
2891 gtk_calendar_style_set (GtkWidget *widget,
2892                         GtkStyle  *previous_style)
2893 {
2894   if (previous_style && GTK_WIDGET_REALIZED (widget))
2895     calendar_set_background (widget);
2896 }
2897
2898 static void
2899 gtk_calendar_state_changed (GtkWidget      *widget,
2900                             GtkStateType    previous_state)
2901 {
2902   GtkCalendar *calendar = GTK_CALENDAR (widget);
2903   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2904   int i;
2905   
2906   if (!GTK_WIDGET_IS_SENSITIVE (widget))
2907     {
2908       priv->in_drag = 0;
2909       calendar_stop_spinning (calendar);    
2910     }
2911
2912   for (i = 0; i < 4; i++)
2913     if (GTK_WIDGET_IS_SENSITIVE (widget))
2914       priv->arrow_state[i] = GTK_STATE_NORMAL;
2915     else 
2916       priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
2917   
2918   calendar_set_background (widget);
2919 }
2920
2921 static void
2922 gtk_calendar_grab_notify (GtkWidget *widget,
2923                           gboolean   was_grabbed)
2924 {
2925   if (!was_grabbed)
2926     calendar_stop_spinning (GTK_CALENDAR (widget));
2927 }
2928
2929 static gboolean
2930 gtk_calendar_focus_out (GtkWidget     *widget,
2931                         GdkEventFocus *event)
2932 {
2933   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2934
2935   gtk_widget_queue_draw (widget);
2936
2937   calendar_stop_spinning (GTK_CALENDAR (widget));
2938   
2939   priv->in_drag = 0; 
2940
2941   return FALSE;
2942 }
2943
2944 \f
2945 /****************************************
2946  *          Drag and Drop               *
2947  ****************************************/
2948
2949 static void
2950 gtk_calendar_drag_data_get (GtkWidget        *widget,
2951                             GdkDragContext   *context,
2952                             GtkSelectionData *selection_data,
2953                             guint             info,
2954                             guint             time)
2955 {
2956   GtkCalendar *calendar = GTK_CALENDAR (widget);
2957   GDate *date;
2958   gchar str[128];
2959   gsize len;
2960
2961   date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
2962   len = g_date_strftime (str, 127, "%x", date);
2963   gtk_selection_data_set_text (selection_data, str, len);
2964   
2965   g_free (date);
2966 }
2967
2968 /* Get/set whether drag_motion requested the drag data and
2969  * drag_data_received should thus not actually insert the data,
2970  * since the data doesn't result from a drop.
2971  */
2972 static void
2973 set_status_pending (GdkDragContext *context,
2974                     GdkDragAction   suggested_action)
2975 {
2976   g_object_set_data (G_OBJECT (context),
2977                      I_("gtk-calendar-status-pending"),
2978                      GINT_TO_POINTER (suggested_action));
2979 }
2980
2981 static GdkDragAction
2982 get_status_pending (GdkDragContext *context)
2983 {
2984   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
2985                                              "gtk-calendar-status-pending"));
2986 }
2987
2988 static void
2989 gtk_calendar_drag_leave (GtkWidget      *widget,
2990                          GdkDragContext *context,
2991                          guint           time)
2992 {
2993   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2994
2995   priv->drag_highlight = 0;
2996   gtk_drag_unhighlight (widget);
2997   
2998 }
2999
3000 static gboolean
3001 gtk_calendar_drag_motion (GtkWidget      *widget,
3002                           GdkDragContext *context,
3003                           gint            x,
3004                           gint            y,
3005                           guint           time)
3006 {
3007   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3008   GdkAtom target;
3009   
3010   if (!priv->drag_highlight) 
3011     {
3012       priv->drag_highlight = 1;
3013       gtk_drag_highlight (widget);
3014     }
3015   
3016   target = gtk_drag_dest_find_target (widget, context, NULL);
3017   if (target == GDK_NONE)
3018     gdk_drag_status (context, 0, time);
3019   else
3020     {
3021       set_status_pending (context, context->suggested_action);
3022       gtk_drag_get_data (widget, context, target, time);
3023     }
3024   
3025   return TRUE;
3026 }
3027
3028 static gboolean
3029 gtk_calendar_drag_drop (GtkWidget      *widget,
3030                         GdkDragContext *context,
3031                         gint            x,
3032                         gint            y,
3033                         guint           time)
3034 {
3035   GdkAtom target;
3036
3037   target = gtk_drag_dest_find_target (widget, context, NULL);  
3038   if (target != GDK_NONE)
3039     {
3040       gtk_drag_get_data (widget, context, 
3041                          target, 
3042                          time);
3043       return TRUE;
3044     }
3045
3046   return FALSE;
3047 }
3048
3049 static void
3050 gtk_calendar_drag_data_received (GtkWidget        *widget,
3051                                  GdkDragContext   *context,
3052                                  gint              x,
3053                                  gint              y,
3054                                  GtkSelectionData *selection_data,
3055                                  guint             info,
3056                                  guint             time)
3057 {
3058   GtkCalendar *calendar = GTK_CALENDAR (widget);
3059   guint day, month, year;
3060   gchar *str;
3061   GDate *date;
3062   GdkDragAction suggested_action;
3063
3064   suggested_action = get_status_pending (context);
3065
3066   if (suggested_action) 
3067     {
3068       set_status_pending (context, 0);
3069      
3070       /* We are getting this data due to a request in drag_motion,
3071        * rather than due to a request in drag_drop, so we are just
3072        * supposed to call drag_status, not actually paste in the
3073        * data.
3074        */
3075       str = gtk_selection_data_get_text (selection_data);
3076       if (str) 
3077         {
3078           date = g_date_new ();
3079           g_date_set_parse (date, str);
3080           if (!g_date_valid (date)) 
3081               suggested_action = 0;
3082           g_date_free (date);
3083           g_free (str);
3084         }
3085       else
3086         suggested_action = 0;
3087
3088       gdk_drag_status (context, suggested_action, time);
3089
3090       return;
3091     }
3092
3093   date = g_date_new ();
3094   str = gtk_selection_data_get_text (selection_data);
3095   if (str) 
3096     {
3097       g_date_set_parse (date, str);
3098       g_free (str);
3099     }
3100   
3101   if (!g_date_valid (date)) 
3102     {
3103       g_warning ("Received invalid date data\n");
3104       g_date_free (date);       
3105       gtk_drag_finish (context, FALSE, FALSE, time);
3106       return;
3107     }
3108
3109   day = g_date_get_day (date);
3110   month = g_date_get_month (date);
3111   year = g_date_get_year (date);
3112   g_date_free (date);   
3113
3114   gtk_drag_finish (context, TRUE, FALSE, time);
3115
3116   
3117   g_object_freeze_notify (G_OBJECT (calendar));
3118   if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3119       && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
3120     gtk_calendar_select_month (calendar, month - 1, year);
3121   gtk_calendar_select_day (calendar, day);
3122   g_object_thaw_notify (G_OBJECT (calendar));  
3123 }
3124
3125 \f
3126 /****************************************
3127  *              Public API              *
3128  ****************************************/
3129
3130 /**
3131  * gtk_calendar_new:
3132  * 
3133  * Creates a new calendar, with the current date being selected. 
3134  * 
3135  * Return value: a newly #GtkCalendar widget
3136  **/
3137 GtkWidget*
3138 gtk_calendar_new (void)
3139 {
3140   return g_object_new (GTK_TYPE_CALENDAR, NULL);
3141 }
3142
3143 /**
3144  * gtk_calendar_display_options:
3145  * @calendar: a #GtkCalendar.
3146  * @flags: the display options to set.
3147  *
3148  * Sets display options (whether to display the heading and the month headings).
3149  * 
3150  * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
3151  **/
3152 void
3153 gtk_calendar_display_options (GtkCalendar              *calendar,
3154                               GtkCalendarDisplayOptions flags)
3155 {
3156   gtk_calendar_set_display_options (calendar, flags);
3157 }
3158
3159 /**
3160  * gtk_calendar_get_display_options:
3161  * @calendar: a #GtkCalendar
3162  * 
3163  * Returns the current display options of @calendar. 
3164  * 
3165  * Return value: the display options.
3166  *
3167  * Since: 2.4
3168  **/
3169 GtkCalendarDisplayOptions 
3170 gtk_calendar_get_display_options (GtkCalendar         *calendar)
3171 {
3172   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3173
3174   return calendar->display_flags;
3175 }
3176
3177 /**
3178  * gtk_calendar_set_display_options:
3179  * @calendar: a #GtkCalendar
3180  * @flags: the display options to set
3181  * 
3182  * Sets display options (whether to display the heading and the month  
3183  * headings).
3184  *
3185  * Since: 2.4
3186  **/
3187 void
3188 gtk_calendar_set_display_options (GtkCalendar          *calendar,
3189                                   GtkCalendarDisplayOptions flags)
3190 {
3191   GtkWidget *widget = GTK_WIDGET (calendar);
3192   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3193   gint resize = 0;
3194   gint i;
3195   GtkCalendarDisplayOptions old_flags;
3196   
3197   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3198   
3199   old_flags = calendar->display_flags;
3200   
3201   if (GTK_WIDGET_REALIZED (widget))
3202     {
3203       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3204         {
3205           resize ++;
3206           if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3207               && (priv->header_win))
3208             {
3209               calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3210               calendar_realize_arrows (calendar);
3211             }
3212           else
3213             {
3214               for (i = 0; i < 4; i++)
3215                 {
3216                   if (priv->arrow_win[i])
3217                     {
3218                       gdk_window_set_user_data (priv->arrow_win[i], 
3219                                                 NULL);
3220                       gdk_window_destroy (priv->arrow_win[i]);
3221                       priv->arrow_win[i] = NULL;
3222                     }
3223                 }
3224             }
3225         }
3226       
3227       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3228         {
3229           resize++;
3230           
3231           if (flags & GTK_CALENDAR_SHOW_HEADING)
3232             {
3233               calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3234               calendar_realize_header (calendar);
3235             }
3236           else
3237             {
3238               for (i = 0; i < 4; i++)
3239                 {
3240                   if (priv->arrow_win[i])
3241                     {
3242                       gdk_window_set_user_data (priv->arrow_win[i], 
3243                                                 NULL);
3244                       gdk_window_destroy (priv->arrow_win[i]);
3245                       priv->arrow_win[i] = NULL;
3246                     }
3247                 }
3248               gdk_window_set_user_data (priv->header_win, NULL);
3249               gdk_window_destroy (priv->header_win);
3250               priv->header_win = NULL;
3251             }
3252         }
3253       
3254       
3255       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3256         {
3257           resize++;
3258           
3259           if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3260             {
3261               calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3262               calendar_realize_day_names (calendar);
3263             }
3264           else
3265             {
3266               gdk_window_set_user_data (priv->day_name_win, NULL);
3267               gdk_window_destroy (priv->day_name_win);
3268               priv->day_name_win = NULL;
3269             }
3270         }
3271       
3272       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3273         {
3274           resize++;
3275           
3276           if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3277             {
3278               calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3279               calendar_realize_week_numbers (calendar);
3280             }
3281           else
3282             {
3283               gdk_window_set_user_data (priv->week_win, NULL);
3284               gdk_window_destroy (priv->week_win);
3285               priv->week_win = NULL;
3286             }
3287         }
3288
3289       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3290         g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3291       
3292       calendar->display_flags = flags;
3293       if (resize)
3294         gtk_widget_queue_resize (GTK_WIDGET (calendar));
3295       
3296     } 
3297   else
3298     calendar->display_flags = flags;
3299   
3300   g_object_freeze_notify (G_OBJECT (calendar));
3301   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3302     g_object_notify (G_OBJECT (calendar), "show-heading");
3303   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3304     g_object_notify (G_OBJECT (calendar), "show-day-names");
3305   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3306     g_object_notify (G_OBJECT (calendar), "no-month-change");
3307   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3308     g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3309   g_object_thaw_notify (G_OBJECT (calendar));
3310 }
3311
3312 /**
3313  * gtk_calendar_select_month:
3314  * @calendar: a #GtkCalendar
3315  * @month: a month number between 0 and 11.
3316  * @year: the year the month is in.
3317  * 
3318  * Shifts the calendar to a different month.
3319  * 
3320  * Return value: %TRUE, always
3321  **/
3322 gboolean
3323 gtk_calendar_select_month (GtkCalendar *calendar,
3324                            guint        month,
3325                            guint        year)
3326 {
3327   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3328   g_return_val_if_fail (month <= 11, FALSE);
3329   
3330   calendar->month = month;
3331   calendar->year  = year;
3332   
3333   calendar_compute_days (calendar);
3334   
3335   gtk_widget_queue_draw (GTK_WIDGET (calendar));
3336
3337   g_object_freeze_notify (G_OBJECT (calendar));
3338   g_object_notify (G_OBJECT (calendar), "month");
3339   g_object_notify (G_OBJECT (calendar), "year");
3340   g_object_thaw_notify (G_OBJECT (calendar));
3341
3342   g_signal_emit (calendar,
3343                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3344                  0);
3345   return TRUE;
3346 }
3347
3348 /**
3349  * gtk_calendar_select_day:
3350  * @calendar: a #GtkCalendar.
3351  * @day: the day number between 1 and 31, or 0 to unselect 
3352  *   the currently selected day.
3353  * 
3354  * Selects a day from the current month.
3355  **/
3356 void
3357 gtk_calendar_select_day (GtkCalendar *calendar,
3358                          guint        day)
3359 {
3360   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3361   g_return_if_fail (day <= 31);
3362   
3363   /* Deselect the old day */
3364   if (calendar->selected_day > 0)
3365     {
3366       gint selected_day;
3367       
3368       selected_day = calendar->selected_day;
3369       calendar->selected_day = 0;
3370       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3371         calendar_invalidate_day_num (calendar, selected_day);
3372     }
3373   
3374   calendar->selected_day = day;
3375   
3376   /* Select the new day */
3377   if (day != 0)
3378     {
3379       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3380         calendar_invalidate_day_num (calendar, day);
3381     }
3382   
3383   g_object_notify (G_OBJECT (calendar), "day");
3384
3385   g_signal_emit (calendar,
3386                  gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3387                  0);
3388 }
3389
3390 /**
3391  * gtk_calendar_clear_marks:
3392  * @calendar: a #GtkCalendar
3393  * 
3394  * Remove all visual markers.
3395  **/
3396 void
3397 gtk_calendar_clear_marks (GtkCalendar *calendar)
3398 {
3399   guint day;
3400   
3401   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3402   
3403   for (day = 0; day < 31; day++)
3404     {
3405       calendar->marked_date[day] = FALSE;
3406     }
3407
3408   calendar->num_marked_dates = 0;
3409
3410   gtk_widget_queue_draw (GTK_WIDGET (calendar));
3411 }
3412
3413 /**
3414  * gtk_calendar_mark_day:
3415  * @calendar: a #GtkCalendar 
3416  * @day: the day number to mark between 1 and 31.
3417  * 
3418  * Places a visual marker on a particular day.
3419  * 
3420  * Return value: %TRUE, always
3421  **/
3422 gboolean
3423 gtk_calendar_mark_day (GtkCalendar *calendar,
3424                        guint        day)
3425 {
3426   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3427   
3428   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3429     {
3430       calendar->marked_date[day - 1] = TRUE;
3431       calendar->num_marked_dates++;
3432       calendar_invalidate_day_num (calendar, day);
3433     }
3434   
3435   return TRUE;
3436 }
3437
3438 /**
3439  * gtk_calendar_unmark_day:
3440  * @calendar: a #GtkCalendar.
3441  * @day: the day number to unmark between 1 and 31.
3442  * 
3443  * Removes the visual marker from a particular day.
3444  * 
3445  * Return value: %TRUE, always
3446  **/
3447 gboolean
3448 gtk_calendar_unmark_day (GtkCalendar *calendar,
3449                          guint        day)
3450 {
3451   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3452   
3453   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3454     {
3455       calendar->marked_date[day - 1] = FALSE;
3456       calendar->num_marked_dates--;
3457       calendar_invalidate_day_num (calendar, day);
3458     }
3459   
3460   return TRUE;
3461 }
3462
3463 /**
3464  * gtk_calendar_get_date:
3465  * @calendar: a #GtkCalendar
3466  * @year: location to store the year number, or %NULL
3467  * @month: location to store the month number (between 0 and 11), or %NULL
3468  * @day: location to store the day number (between 1 and 31), or %NULL
3469  * 
3470  * Obtains the selected date from a #GtkCalendar.
3471  **/
3472 void
3473 gtk_calendar_get_date (GtkCalendar *calendar,
3474                        guint       *year,
3475                        guint       *month,
3476                        guint       *day)
3477 {
3478   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3479   
3480   if (year)
3481     *year = calendar->year;
3482   
3483   if (month)
3484     *month = calendar->month;
3485   
3486   if (day)
3487     *day = calendar->selected_day;
3488 }
3489
3490 /**
3491  * gtk_calendar_freeze:
3492  * @calendar: a #GtkCalendar
3493  * 
3494  * Does nothing. Previously locked the display of the calendar until
3495  * it was thawed with gtk_calendar_thaw().
3496  *
3497  * Deprecated: 2.8: 
3498  **/
3499 void
3500 gtk_calendar_freeze (GtkCalendar *calendar)
3501 {
3502   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3503 }
3504
3505 /**
3506  * gtk_calendar_thaw:
3507  * @calendar: a #GtkCalendar
3508  * 
3509  * Does nothing. Previously defrosted a calendar; all the changes made
3510  * since the last gtk_calendar_freeze() were displayed.
3511  *
3512  * Deprecated: 2.8: 
3513  **/
3514 void
3515 gtk_calendar_thaw (GtkCalendar *calendar)
3516 {
3517   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3518 }
3519
3520 #define __GTK_CALENDAR_C__
3521 #include "gtkaliasdef.c"