]> Pileus Git - ~andy/gtk/blob - gtk/gtkcalendar.c
Doc updates
[~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 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   gchar buffer[255];
1567   gint calendar_margin = CALENDAR_MARGIN;
1568   gint header_width, main_width;
1569   gint max_header_height = 0;
1570   gint focus_width;
1571   gint focus_padding;
1572   
1573   gtk_widget_style_get (GTK_WIDGET (widget),
1574                         "focus-line-width", &focus_width,
1575                         "focus-padding", &focus_padding,
1576                         NULL);
1577
1578   layout = gtk_widget_create_pango_layout (widget, NULL);
1579   
1580   /*
1581    * Calculate the requisition  width for the widget.
1582    */
1583   
1584   /* Header width */
1585   
1586   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1587     {
1588       priv->max_month_width = 0;
1589       for (i = 0; i < 12; i++)
1590         {
1591           pango_layout_set_text (layout, default_monthname[i], -1);
1592           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1593           priv->max_month_width = MAX (priv->max_month_width,
1594                                                logical_rect.width + 8);
1595           max_header_height = MAX (max_header_height, logical_rect.height); 
1596         }
1597
1598       priv->max_year_width = 0;
1599       /* Translators:  This is a text measurement template.
1600        * Translate it to the widest year text. 
1601        * 
1602        * Don't include the prefix "year measurement template|" 
1603        * in the translation.
1604        *
1605        * If you don't understand this, leave it as "2000"
1606        */
1607       pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);   
1608       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1609       priv->max_year_width = MAX (priv->max_year_width,
1610                                   logical_rect.width + 8);
1611       max_header_height = MAX (max_header_height, logical_rect.height); 
1612     } 
1613   else 
1614     {
1615       priv->max_month_width = 0;
1616       priv->max_year_width = 0;
1617     }
1618   
1619   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1620     header_width = (priv->max_month_width 
1621                     + priv->max_year_width
1622                     + 3 * 3);
1623   else
1624     header_width = (priv->max_month_width 
1625                     + priv->max_year_width
1626                     + 4 * priv->arrow_width + 3 * 3);
1627
1628   /* Mainwindow labels width */
1629   
1630   priv->max_day_char_width = 0;
1631   priv->min_day_width = 0;
1632   priv->max_label_char_ascent = 0;
1633   priv->max_label_char_descent = 0;
1634
1635   for (i = 0; i < 9; i++)
1636     {
1637       g_snprintf (buffer, sizeof (buffer), "%d%d", i, i);
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_label_char_ascent,
1644                                                PANGO_ASCENT (logical_rect));
1645       priv->max_day_char_descent = MAX (priv->max_label_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   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1652     for (i = 0; i < 7; i++)
1653       {
1654         pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1655         pango_layout_line_get_pixel_extents (pango_layout_get_lines (layout)->data, NULL, &logical_rect);
1656
1657         priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1658         priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1659                                                    PANGO_ASCENT (logical_rect));
1660         priv->max_label_char_descent = MAX (priv->max_label_char_descent, 
1661                                                     PANGO_DESCENT (logical_rect));
1662       }
1663   
1664   priv->max_week_char_width = 0;
1665   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1666     for (i = 0; i < 9; i++)
1667       {
1668         g_snprintf (buffer, sizeof (buffer), "%d%d", i, i);
1669         pango_layout_set_text (layout, buffer, -1);       
1670         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1671         priv->max_week_char_width = MAX (priv->max_week_char_width,
1672                                                  logical_rect.width / 2);
1673       }
1674   
1675   main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1676                 + (priv->max_week_char_width
1677                    ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1678                    : 0));
1679   
1680   
1681   requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1682   
1683   /*
1684    * Calculate the requisition height for the widget.
1685    */
1686   
1687   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1688     {
1689       priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1690     }
1691   else
1692     {
1693       priv->header_h = 0;
1694     }
1695   
1696   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1697     {
1698       priv->day_name_h = (priv->max_label_char_ascent
1699                                   + priv->max_label_char_descent
1700                                   + 2 * (focus_padding + focus_width) + calendar_margin);
1701       calendar_margin = CALENDAR_YSEP;
1702     } 
1703   else
1704     {
1705       priv->day_name_h = 0;
1706     }
1707
1708   priv->main_h = (CALENDAR_MARGIN + calendar_margin
1709                           + 6 * (priv->max_day_char_ascent
1710                                  + priv->max_day_char_descent 
1711                                  + 2 * (focus_padding + focus_width))
1712                           + DAY_YSEP * 5);
1713   
1714   height = (priv->header_h + priv->day_name_h 
1715             + priv->main_h);
1716   
1717   requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1718
1719   g_object_unref (layout);
1720 }
1721
1722 static void
1723 gtk_calendar_size_allocate (GtkWidget     *widget,
1724                             GtkAllocation *allocation)
1725 {
1726   GtkCalendar *calendar = GTK_CALENDAR (widget);
1727   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1728   gint xthickness = widget->style->xthickness;
1729   gint ythickness = widget->style->xthickness;
1730   guint i;
1731   
1732   widget->allocation = *allocation;
1733     
1734   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1735     {
1736       priv->day_width = (priv->min_day_width
1737                          * ((allocation->width - (xthickness + INNER_BORDER) * 2
1738                              - (CALENDAR_MARGIN * 2) -  (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
1739                          / (7 * priv->min_day_width + priv->max_week_char_width * 2));
1740       priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
1741                            - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
1742                           - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
1743     } 
1744   else 
1745     {
1746       priv->day_width = (allocation->width
1747                          - (xthickness + INNER_BORDER) * 2
1748                          - (CALENDAR_MARGIN * 2)
1749                          - (DAY_XSEP * 6))/7;
1750       priv->week_width = 0;
1751     }
1752   
1753   if (GTK_WIDGET_REALIZED (widget))
1754     {
1755       gdk_window_move_resize (widget->window,
1756                               allocation->x, allocation->y,
1757                               allocation->width, allocation->height);
1758       if (priv->header_win)
1759         gdk_window_move_resize (priv->header_win,
1760                                 xthickness, ythickness,
1761                                 allocation->width - 2 * xthickness, priv->header_h);
1762
1763       for (i = 0 ; i < 4 ; i++)
1764         {
1765           if (priv->arrow_win[i])
1766             {
1767               GdkRectangle rect;
1768               calendar_arrow_rectangle (calendar, i, &rect);
1769           
1770               gdk_window_move_resize (priv->arrow_win[i],
1771                                       rect.x, rect.y, rect.width, rect.height);
1772             }
1773         }
1774       
1775       if (priv->day_name_win)
1776         gdk_window_move_resize (priv->day_name_win,
1777                                 xthickness + INNER_BORDER,
1778                                 priv->header_h + (widget->style->ythickness + INNER_BORDER),
1779                                 allocation->width - (xthickness + INNER_BORDER) * 2,
1780                                 priv->day_name_h);
1781       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1782         {
1783           if (priv->week_win)
1784             gdk_window_move_resize (priv->week_win,
1785                                     (xthickness + INNER_BORDER),
1786                                     priv->header_h + priv->day_name_h
1787                                     + (widget->style->ythickness + INNER_BORDER),
1788                                     priv->week_width,
1789                                     priv->main_h);
1790           gdk_window_move_resize (priv->main_win,
1791                                   priv->week_width + (xthickness + INNER_BORDER),
1792                                   priv->header_h + priv->day_name_h
1793                                   + (widget->style->ythickness + INNER_BORDER),
1794                                   allocation->width 
1795                                   - priv->week_width 
1796                                   - (xthickness + INNER_BORDER) * 2,
1797                                   priv->main_h);
1798         }
1799       else 
1800         {
1801           gdk_window_move_resize (priv->main_win,
1802                                   (xthickness + INNER_BORDER),
1803                                   priv->header_h + priv->day_name_h
1804                                   + (widget->style->ythickness + INNER_BORDER),
1805                                   allocation->width 
1806                                   - priv->week_width 
1807                                   - (xthickness + INNER_BORDER) * 2,
1808                                   priv->main_h);
1809           if (priv->week_win)
1810             gdk_window_move_resize (priv->week_win,
1811                                     allocation->width 
1812                                     - priv->week_width 
1813                                     - (xthickness + INNER_BORDER),
1814                                     priv->header_h + priv->day_name_h
1815                                     + (widget->style->ythickness + INNER_BORDER),
1816                                     priv->week_width,
1817                                     priv->main_h);
1818         }
1819     }
1820 }
1821
1822 \f
1823 /****************************************
1824  *              Repainting              *
1825  ****************************************/
1826
1827 static void
1828 calendar_paint_header (GtkCalendar *calendar)
1829 {
1830   GtkWidget *widget = GTK_WIDGET (calendar);
1831   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1832   cairo_t *cr;
1833   char buffer[255];
1834   int x, y;
1835   gint header_width;
1836   gint max_month_width;
1837   gint max_year_width;
1838   PangoLayout *layout;
1839   PangoRectangle logical_rect;
1840   gboolean year_left;
1841   time_t tmp_time;
1842   struct tm *tm;
1843   gchar *str;
1844
1845   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1846     year_left = priv->year_before;
1847   else
1848     year_left = !priv->year_before;
1849
1850   cr = gdk_cairo_create (priv->header_win);
1851   
1852   header_width = widget->allocation.width - 2 * widget->style->xthickness;
1853   
1854   max_month_width = priv->max_month_width;
1855   max_year_width = priv->max_year_width;
1856   
1857   gtk_paint_shadow (widget->style, priv->header_win,
1858                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1859                     NULL, widget, "calendar",
1860                     0, 0, header_width, priv->header_h);
1861
1862   tmp_time = 1;  /* Jan 1 1970, 00:00:01 UTC */
1863   tm = gmtime (&tmp_time);
1864   tm->tm_year = calendar->year - 1900;
1865
1866   /* Translators: This dictates how the year is displayed in
1867    * gtkcalendar widget.  See strftime() manual for the format.
1868    * Use only ASCII in the translation.
1869    *
1870    * Also look for the msgid "year measurement template|2000".  
1871    * Translate that entry to a year with the widest output of this
1872    * msgid. 
1873    * 
1874    * Don't include the prefix "calendar year format|" in the 
1875    * translation. "%Y" is appropriate for most locales.
1876    */
1877   strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
1878   str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
1879   layout = gtk_widget_create_pango_layout (widget, str);
1880   g_free (str);
1881   
1882   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1883   
1884   /* Draw title */
1885   y = (priv->header_h - logical_rect.height) / 2;
1886   
1887   /* Draw year and its arrows */
1888   
1889   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1890     if (year_left)
1891       x = 3 + (max_year_width - logical_rect.width)/2;
1892     else
1893       x = header_width - (3 + max_year_width
1894                           - (max_year_width - logical_rect.width)/2);
1895   else
1896     if (year_left)
1897       x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
1898     else
1899       x = header_width - (3 + priv->arrow_width + max_year_width
1900                           - (max_year_width - logical_rect.width)/2);
1901   
1902
1903   gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
1904   cairo_move_to (cr, x, y);
1905   pango_cairo_show_layout (cr, layout);
1906   
1907   /* Draw month */
1908   g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
1909   pango_layout_set_text (layout, buffer, -1);
1910   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1911
1912   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1913     if (year_left)
1914       x = header_width - (3 + max_month_width
1915                           - (max_month_width - logical_rect.width)/2);      
1916     else
1917     x = 3 + (max_month_width - logical_rect.width) / 2;
1918   else
1919     if (year_left)
1920       x = header_width - (3 + priv->arrow_width + max_month_width
1921                           - (max_month_width - logical_rect.width)/2);
1922     else
1923     x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
1924
1925   cairo_move_to (cr, x, y);
1926   pango_cairo_show_layout (cr, layout);
1927
1928   g_object_unref (layout);
1929   cairo_destroy (cr);
1930 }
1931
1932 static void
1933 calendar_paint_day_names (GtkCalendar *calendar)
1934 {
1935   GtkWidget *widget = GTK_WIDGET (calendar);
1936   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1937   cairo_t *cr;
1938   char buffer[255];
1939   int day,i;
1940   int day_width, cal_width;
1941   int day_wid_sep;
1942   PangoLayout *layout;
1943   PangoRectangle logical_rect;
1944   gint focus_padding;
1945   gint focus_width;
1946   
1947   cr = gdk_cairo_create (priv->day_name_win);
1948   
1949   gtk_widget_style_get (GTK_WIDGET (widget),
1950                         "focus-line-width", &focus_width,
1951                         "focus-padding", &focus_padding,
1952                         NULL);
1953   
1954   day_width = priv->day_width;
1955   cal_width = widget->allocation.width;
1956   day_wid_sep = day_width + DAY_XSEP;
1957   
1958   /*
1959    * Draw rectangles as inverted background for the labels.
1960    */
1961
1962   gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
1963   cairo_rectangle (cr,
1964                    CALENDAR_MARGIN, CALENDAR_MARGIN,
1965                    cal_width-CALENDAR_MARGIN * 2,
1966                    priv->day_name_h - CALENDAR_MARGIN);
1967   cairo_fill (cr);
1968   
1969   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1970     {
1971       cairo_rectangle (cr, 
1972                        CALENDAR_MARGIN,
1973                        priv->day_name_h - CALENDAR_YSEP,
1974                        priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
1975                        CALENDAR_YSEP);
1976       cairo_fill (cr);
1977     }
1978   
1979   /*
1980    * Write the labels
1981    */
1982
1983   layout = gtk_widget_create_pango_layout (widget, NULL);
1984
1985   gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
1986   for (i = 0; i < 7; i++)
1987     {
1988       if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1989         day = 6 - i;
1990       else
1991         day = i;
1992       day = (day + priv->week_start) % 7;
1993       g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
1994
1995       pango_layout_set_text (layout, buffer, -1);
1996       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1997
1998       cairo_move_to (cr, 
1999                      (CALENDAR_MARGIN +
2000                       + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2001                          (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2002                          : 0)
2003                       + day_wid_sep * i
2004                       + (day_width - logical_rect.width)/2),
2005                      CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2006       pango_cairo_show_layout (cr, layout);
2007     }
2008   
2009   g_object_unref (layout);
2010   cairo_destroy (cr);
2011 }
2012
2013 static void
2014 calendar_paint_week_numbers (GtkCalendar *calendar)
2015 {
2016   GtkWidget *widget = GTK_WIDGET (calendar);
2017   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2018   cairo_t *cr;
2019   gint row, week = 0, year;
2020   gint x_loc;
2021   char buffer[3];
2022   gint y_loc, day_height;
2023   PangoLayout *layout;
2024   PangoRectangle logical_rect;
2025   gint focus_padding;
2026   gint focus_width;
2027   
2028   cr = gdk_cairo_create (priv->week_win);
2029   
2030   gtk_widget_style_get (GTK_WIDGET (widget),
2031                         "focus-line-width", &focus_width,
2032                         "focus-padding", &focus_padding,
2033                         NULL);
2034   
2035   /*
2036    * Draw a rectangle as inverted background for the labels.
2037    */
2038
2039   gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2040   if (priv->day_name_win)
2041     cairo_rectangle (cr, 
2042                      CALENDAR_MARGIN,
2043                      0,
2044                      priv->week_width - CALENDAR_MARGIN,
2045                      priv->main_h - CALENDAR_MARGIN);
2046   else
2047     cairo_rectangle (cr,
2048                      CALENDAR_MARGIN,
2049                      CALENDAR_MARGIN,
2050                      priv->week_width - CALENDAR_MARGIN,
2051                      priv->main_h - 2 * CALENDAR_MARGIN);
2052   cairo_fill (cr);
2053   
2054   /*
2055    * Write the labels
2056    */
2057   
2058   layout = gtk_widget_create_pango_layout (widget, NULL);
2059   
2060   gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2061   day_height = calendar_row_height (calendar);
2062   for (row = 0; row < 6; row++)
2063     {
2064       gboolean result;
2065       
2066       year = calendar->year;
2067       if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2068         year++;
2069
2070       result = week_of_year (&week, &year,              
2071                              ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2072                               + calendar->month) % 12 + 1, calendar->day[row][6]);
2073       g_return_if_fail (result);
2074
2075       g_snprintf (buffer, sizeof (buffer), "%d", week);
2076       pango_layout_set_text (layout, buffer, -1);
2077       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2078
2079       y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2080
2081       x_loc = (priv->week_width
2082                - logical_rect.width
2083                - CALENDAR_XSEP - focus_padding - focus_width);
2084
2085       cairo_move_to (cr, x_loc, y_loc);
2086       pango_cairo_show_layout (cr, layout);
2087     }
2088   
2089   g_object_unref (layout);
2090   cairo_destroy (cr);
2091 }
2092
2093 static void
2094 calendar_invalidate_day_num (GtkCalendar *calendar,
2095                              gint         day)
2096 {
2097   gint r, c, row, col;
2098   
2099   row = -1;
2100   col = -1;
2101   for (r = 0; r < 6; r++)
2102     for (c = 0; c < 7; c++)
2103       if (calendar->day_month[r][c] == MONTH_CURRENT &&
2104           calendar->day[r][c] == day)
2105         {
2106           row = r;
2107           col = c;
2108         }
2109   
2110   g_return_if_fail (row != -1);
2111   g_return_if_fail (col != -1);
2112   
2113   calendar_invalidate_day (calendar, row, col);
2114 }
2115
2116 static void
2117 calendar_invalidate_day (GtkCalendar *calendar,
2118                          gint         row,
2119                          gint         col)
2120 {
2121   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2122
2123   if (priv->main_win)
2124     {
2125       GdkRectangle day_rect;
2126       
2127       calendar_day_rectangle (calendar, row, col, &day_rect);
2128       gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2129     }
2130 }
2131
2132 static void
2133 calendar_paint_day (GtkCalendar *calendar,
2134                     gint             row,
2135                     gint             col)
2136 {
2137   GtkWidget *widget = GTK_WIDGET (calendar);
2138   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2139   cairo_t *cr;
2140   GdkColor *text_color;
2141   gchar buffer[255];
2142   gint day;
2143   gint x_loc, y_loc;
2144   GdkRectangle day_rect;
2145
2146   PangoLayout *layout;
2147   PangoRectangle logical_rect;
2148   
2149   g_return_if_fail (row < 6);
2150   g_return_if_fail (col < 7);
2151
2152   cr = gdk_cairo_create (priv->main_win);
2153
2154   day = calendar->day[row][col];
2155
2156   calendar_day_rectangle (calendar, row, col, &day_rect);
2157   
2158   if (calendar->day_month[row][col] == MONTH_PREV)
2159     {
2160       text_color = PREV_MONTH_COLOR (widget);
2161     } 
2162   else if (calendar->day_month[row][col] == MONTH_NEXT)
2163     {
2164       text_color =  NEXT_MONTH_COLOR (widget);
2165     } 
2166   else 
2167     {
2168 #if 0      
2169       if (calendar->highlight_row == row && calendar->highlight_col == col)
2170         {
2171           cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2172           gdk_cairo_rectangle (cr, &day_rect);
2173           cairo_fill (cr);
2174         }
2175 #endif     
2176       if (calendar->selected_day == day)
2177         {
2178           gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2179           gdk_cairo_rectangle (cr, &day_rect);
2180           cairo_fill (cr);
2181         }
2182       if (calendar->selected_day == day)
2183         text_color = SELECTED_FG_COLOR (widget);
2184       else if (calendar->marked_date[day-1])
2185         text_color = MARKED_COLOR (widget);
2186       else
2187         text_color = NORMAL_DAY_COLOR (widget);
2188     }
2189
2190   g_snprintf (buffer, sizeof (buffer), "%d", day);
2191   layout = gtk_widget_create_pango_layout (widget, buffer);
2192   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2193   
2194   x_loc = day_rect.x + day_rect.width / 2 + priv->max_day_char_width;
2195   x_loc -= logical_rect.width;
2196   y_loc = day_rect.y + (day_rect.height - logical_rect.height) / 2;
2197   
2198   gdk_cairo_set_source_color (cr, text_color);
2199   cairo_move_to (cr, x_loc, y_loc);
2200   pango_cairo_show_layout (cr, layout);
2201     
2202   if (calendar->marked_date[day-1]
2203       && calendar->day_month[row][col] == MONTH_CURRENT)
2204     {
2205       cairo_move_to (cr, x_loc - 1, y_loc);
2206       pango_cairo_show_layout (cr, layout);
2207     }
2208
2209   if (GTK_WIDGET_HAS_FOCUS (calendar) 
2210       && calendar->focus_row == row && calendar->focus_col == col)
2211     {
2212       GtkStateType state;
2213
2214       if (calendar->selected_day == day)
2215         state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2216       else
2217         state = GTK_STATE_NORMAL;
2218       
2219       gtk_paint_focus (widget->style, 
2220                        priv->main_win,
2221                        state,
2222 #if 0
2223                        (calendar->selected_day == day) 
2224                           ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, 
2225 #endif
2226                        NULL, widget, "calendar-day",
2227                        day_rect.x,     day_rect.y, 
2228                        day_rect.width, day_rect.height);
2229     }
2230
2231   g_object_unref (layout);
2232   cairo_destroy (cr);
2233 }
2234
2235 static void
2236 calendar_paint_main (GtkCalendar *calendar)
2237 {
2238   gint row, col;
2239   
2240   for (col = 0; col < 7; col++)
2241     for (row = 0; row < 6; row++)
2242       calendar_paint_day (calendar, row, col);
2243 }
2244
2245 static void
2246 calendar_invalidate_arrow (GtkCalendar *calendar,
2247                            guint        arrow)
2248 {
2249   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2250   GdkWindow *window;
2251   
2252   window = priv->arrow_win[arrow];
2253   if (window)
2254     gdk_window_invalidate_rect (window, NULL, FALSE);
2255 }
2256
2257 static void
2258 calendar_paint_arrow (GtkCalendar *calendar,
2259                       guint            arrow)
2260 {
2261   GtkWidget *widget = GTK_WIDGET (calendar);
2262   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2263   GdkWindow *window;
2264   
2265   window = priv->arrow_win[arrow];
2266   if (window)
2267     {
2268       cairo_t *cr = gdk_cairo_create (window);
2269       gint width, height;
2270       gint state;
2271         
2272       state = priv->arrow_state[arrow];
2273
2274       gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2275       cairo_paint (cr);
2276       cairo_destroy (cr);
2277       
2278       gdk_drawable_get_size (window, &width, &height);
2279       if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2280         gtk_paint_arrow (widget->style, window, state, 
2281                          GTK_SHADOW_OUT, NULL, widget, "calendar",
2282                          GTK_ARROW_LEFT, TRUE, 
2283                          width/2 - 3, height/2 - 4, 8, 8);
2284       else 
2285         gtk_paint_arrow (widget->style, window, state, 
2286                          GTK_SHADOW_OUT, NULL, widget, "calendar",
2287                          GTK_ARROW_RIGHT, TRUE, 
2288                          width/2 - 2, height/2 - 4, 8, 8);
2289     }
2290 }
2291
2292 static gboolean
2293 gtk_calendar_expose (GtkWidget      *widget,
2294                      GdkEventExpose *event)
2295 {
2296   GtkCalendar *calendar = GTK_CALENDAR (widget);
2297   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2298   int i;
2299
2300   if (GTK_WIDGET_DRAWABLE (widget))
2301     {
2302       if (event->window == priv->main_win)
2303         calendar_paint_main (calendar);
2304       
2305       if (event->window == priv->header_win)
2306         calendar_paint_header (calendar);
2307
2308       for (i = 0; i < 4; i++)
2309         if (event->window == priv->arrow_win[i])
2310           calendar_paint_arrow (calendar, i);
2311       
2312       if (event->window == priv->day_name_win)
2313         calendar_paint_day_names (calendar);
2314       
2315       if (event->window == priv->week_win)
2316         calendar_paint_week_numbers (calendar);
2317       if (event->window == widget->window)
2318         {
2319           gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2320                             GTK_SHADOW_IN, NULL, widget, "calendar",
2321                             0, 0, widget->allocation.width, widget->allocation.height);
2322         }
2323     }
2324   
2325   return FALSE;
2326 }
2327
2328 \f
2329 /****************************************
2330  *           Mouse handling             *
2331  ****************************************/
2332
2333 static void
2334 calendar_arrow_action (GtkCalendar *calendar,
2335                        guint        arrow)
2336 {
2337   switch (arrow)
2338     {
2339     case ARROW_YEAR_LEFT:
2340       calendar_set_year_prev (calendar);
2341       break;
2342     case ARROW_YEAR_RIGHT:
2343       calendar_set_year_next (calendar);
2344       break;
2345     case ARROW_MONTH_LEFT:
2346       calendar_set_month_prev (calendar);
2347       break;
2348     case ARROW_MONTH_RIGHT:
2349       calendar_set_month_next (calendar);
2350       break;
2351     default:;
2352       /* do nothing */
2353     }
2354 }
2355
2356 static gboolean
2357 calendar_timer (gpointer data)
2358 {
2359   GtkCalendar *calendar = data;
2360   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2361   gboolean retval = FALSE;
2362   
2363   GDK_THREADS_ENTER ();
2364
2365   if (priv->timer)
2366     {
2367       calendar_arrow_action (calendar, priv->click_child);
2368
2369       if (priv->need_timer)
2370         {
2371           GtkSettings *settings;
2372           guint        timeout;
2373
2374           settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2375           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2376
2377           priv->need_timer = FALSE;
2378           priv->timer = g_timeout_add (timeout,
2379                                        (GSourceFunc) calendar_timer,
2380                                        (gpointer) calendar);
2381         }
2382       else 
2383         retval = TRUE;
2384     }
2385
2386   GDK_THREADS_LEAVE ();
2387
2388   return retval;
2389 }
2390
2391 static void
2392 calendar_start_spinning (GtkCalendar *calendar,
2393                          gint         click_child)
2394 {
2395   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2396
2397   priv->click_child = click_child;
2398   
2399   if (!priv->timer)
2400     {
2401       GtkSettings *settings;
2402       guint        timeout;
2403
2404       settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2405       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2406
2407       priv->need_timer = TRUE;
2408       priv->timer = g_timeout_add (timeout,
2409                                    calendar_timer,
2410                                    calendar);
2411     }
2412 }
2413
2414 static void
2415 calendar_stop_spinning (GtkCalendar *calendar)
2416 {
2417   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2418
2419   if (priv->timer)
2420     {
2421       g_source_remove (priv->timer);
2422       priv->timer = 0;
2423       priv->need_timer = FALSE;
2424     }
2425 }
2426
2427 static void
2428 calendar_main_button_press (GtkCalendar    *calendar,
2429                             GdkEventButton *event)
2430 {
2431   GtkWidget *widget = GTK_WIDGET (calendar);
2432   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2433   gint x, y;
2434   gint row, col;
2435   gint day_month;
2436   gint day;
2437   
2438   x = (gint) (event->x);
2439   y = (gint) (event->y);
2440   
2441   row = calendar_row_from_y (calendar, y);
2442   col = calendar_column_from_x (calendar, x);
2443
2444   /* If row or column isn't found, just return. */
2445   if (row == -1 || col == -1)
2446     return;
2447   
2448   day_month = calendar->day_month[row][col];
2449
2450   if (event->type == GDK_BUTTON_PRESS)
2451     {
2452       day = calendar->day[row][col];
2453       
2454       if (day_month == MONTH_PREV)
2455         calendar_set_month_prev (calendar);
2456       else if (day_month == MONTH_NEXT)
2457         calendar_set_month_next (calendar);
2458       
2459       if (!GTK_WIDGET_HAS_FOCUS (widget))
2460         gtk_widget_grab_focus (widget);
2461           
2462       if (event->button == 1) 
2463         {
2464           priv->in_drag = 1;
2465           priv->drag_start_x = x;
2466           priv->drag_start_y = y;
2467         }
2468
2469       calendar_select_and_focus_day (calendar, day);
2470     }
2471   else if (event->type == GDK_2BUTTON_PRESS)
2472     {
2473       priv->in_drag = 0;
2474       if (day_month == MONTH_CURRENT)
2475         g_signal_emit (calendar,
2476                        gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2477                        0);
2478     }
2479 }
2480
2481 static gboolean
2482 gtk_calendar_button_press (GtkWidget      *widget,
2483                            GdkEventButton *event)
2484 {
2485   GtkCalendar *calendar = GTK_CALENDAR (widget);
2486   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2487   gint arrow = -1;
2488   
2489   if (event->window == priv->main_win)
2490     calendar_main_button_press (calendar, event);
2491
2492   if (!GTK_WIDGET_HAS_FOCUS (widget))
2493     gtk_widget_grab_focus (widget);
2494
2495   for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2496     {
2497       if (event->window == priv->arrow_win[arrow])
2498         {
2499           
2500           /* only call the action on single click, not double */
2501           if (event->type == GDK_BUTTON_PRESS)
2502             {
2503               if (event->button == 1)
2504                 calendar_start_spinning (calendar, arrow);
2505
2506               calendar_arrow_action (calendar, arrow);        
2507             }
2508
2509           return TRUE;
2510         }
2511     }
2512
2513   return FALSE;
2514 }
2515
2516 static gboolean
2517 gtk_calendar_button_release (GtkWidget    *widget,
2518                              GdkEventButton *event)
2519 {
2520   GtkCalendar *calendar = GTK_CALENDAR (widget);
2521   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2522
2523   if (event->button == 1) 
2524     {
2525       calendar_stop_spinning (calendar);
2526
2527       if (priv->in_drag)
2528         priv->in_drag = 0;
2529     }
2530
2531   return TRUE;
2532 }
2533
2534 static gboolean
2535 gtk_calendar_motion_notify (GtkWidget      *widget,
2536                             GdkEventMotion *event)
2537 {
2538   GtkCalendar *calendar = GTK_CALENDAR (widget);
2539   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2540   gint event_x, event_y;
2541   gint row, col;
2542   gint old_row, old_col;
2543   
2544   event_x = (gint) (event->x);
2545   event_y = (gint) (event->y);
2546   
2547   if (event->window == priv->main_win)
2548     {
2549       
2550       if (priv->in_drag) 
2551         {
2552           if (gtk_drag_check_threshold (widget,
2553                                         priv->drag_start_x, priv->drag_start_y,
2554                                         event->x, event->y))
2555             {
2556               GdkDragContext *context;
2557               GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2558               gtk_target_list_add_text_targets (target_list, 0);
2559               context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
2560                                         1, (GdkEvent *)event);
2561
2562           
2563               priv->in_drag = 0;
2564               
2565               gtk_target_list_unref (target_list);
2566               gtk_drag_set_icon_default (context);
2567             }
2568         }
2569       else 
2570         {
2571           row = calendar_row_from_y (calendar, event_y);
2572           col = calendar_column_from_x (calendar, event_x);
2573           
2574           if (row != calendar->highlight_row || calendar->highlight_col != col)
2575             {
2576               old_row = calendar->highlight_row;
2577               old_col = calendar->highlight_col;
2578               if (old_row > -1 && old_col > -1)
2579                 {
2580                   calendar->highlight_row = -1;
2581                   calendar->highlight_col = -1;
2582                   calendar_invalidate_day (calendar, old_row, old_col);
2583                 }
2584               
2585               calendar->highlight_row = row;
2586               calendar->highlight_col = col;
2587               
2588               if (row > -1 && col > -1)
2589                 calendar_invalidate_day (calendar, row, col);
2590             }
2591         }
2592     }
2593   return TRUE;
2594 }
2595
2596 static gboolean
2597 gtk_calendar_enter_notify (GtkWidget        *widget,
2598                            GdkEventCrossing *event)
2599 {
2600   GtkCalendar *calendar = GTK_CALENDAR (widget);
2601   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2602   
2603   if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2604     {
2605       priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2606       calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2607     }
2608   
2609   if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2610     {
2611       priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2612       calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2613     }
2614   
2615   if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2616     {
2617       priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2618       calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2619     }
2620   
2621   if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2622     {
2623       priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2624       calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2625     }
2626   
2627   return TRUE;
2628 }
2629
2630 static gboolean
2631 gtk_calendar_leave_notify (GtkWidget        *widget,
2632                            GdkEventCrossing *event)
2633 {
2634   GtkCalendar *calendar = GTK_CALENDAR (widget);
2635   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2636   gint row;
2637   gint col;
2638   
2639   if (event->window == priv->main_win)
2640     {
2641       row = calendar->highlight_row;
2642       col = calendar->highlight_col;
2643       calendar->highlight_row = -1;
2644       calendar->highlight_col = -1;
2645       if (row > -1 && col > -1)
2646         calendar_invalidate_day (calendar, row, col);
2647     }
2648   
2649   if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2650     {
2651       priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
2652       calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2653     }
2654   
2655   if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2656     {
2657       priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
2658       calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2659     }
2660   
2661   if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2662     {
2663       priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
2664       calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2665     }
2666   
2667   if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2668     {
2669       priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
2670       calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2671     }
2672   
2673   return TRUE;
2674 }
2675
2676 static gboolean
2677 gtk_calendar_scroll (GtkWidget      *widget,
2678                      GdkEventScroll *event)
2679 {
2680   GtkCalendar *calendar = GTK_CALENDAR (widget);
2681
2682   if (event->direction == GDK_SCROLL_UP) 
2683     {
2684       if (!GTK_WIDGET_HAS_FOCUS (widget))
2685         gtk_widget_grab_focus (widget);
2686       calendar_set_month_prev (calendar);
2687     }
2688   else if (event->direction == GDK_SCROLL_DOWN) 
2689     {
2690       if (!GTK_WIDGET_HAS_FOCUS (widget))
2691         gtk_widget_grab_focus (widget);
2692       calendar_set_month_next (calendar);
2693     }
2694   else
2695     return FALSE;
2696
2697   return TRUE;
2698 }
2699
2700 \f
2701 /****************************************
2702  *             Key handling              *
2703  ****************************************/
2704
2705 static void 
2706 move_focus (GtkCalendar *calendar, 
2707             gint         direction)
2708 {
2709   GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
2710
2711   if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
2712       (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
2713     {
2714       if (calendar->focus_col > 0)
2715           calendar->focus_col--;
2716       else if (calendar->focus_row > 0)
2717         {
2718           calendar->focus_col = 6;
2719           calendar->focus_row--;
2720         }
2721     }
2722   else 
2723     {
2724       if (calendar->focus_col < 6)
2725         calendar->focus_col++;
2726       else if (calendar->focus_row < 5)
2727         {
2728           calendar->focus_col = 0;
2729           calendar->focus_row++;
2730         }
2731     }
2732 }
2733
2734 static gboolean
2735 gtk_calendar_key_press (GtkWidget   *widget,
2736                         GdkEventKey *event)
2737 {
2738   GtkCalendar *calendar;
2739   gint return_val;
2740   gint old_focus_row;
2741   gint old_focus_col;
2742   gint row, col, day;
2743   
2744   calendar = GTK_CALENDAR (widget);
2745   return_val = FALSE;
2746   
2747   old_focus_row = calendar->focus_row;
2748   old_focus_col = calendar->focus_col;
2749
2750   switch (event->keyval)
2751     {
2752     case GDK_KP_Left:
2753     case GDK_Left:
2754       return_val = TRUE;
2755       if (event->state & GDK_CONTROL_MASK)
2756         calendar_set_month_prev (calendar);
2757       else
2758         {
2759           move_focus (calendar, -1);
2760           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2761           calendar_invalidate_day (calendar, calendar->focus_row,
2762                                    calendar->focus_col);
2763         }
2764       break;
2765     case GDK_KP_Right:
2766     case GDK_Right:
2767       return_val = TRUE;
2768       if (event->state & GDK_CONTROL_MASK)
2769         calendar_set_month_next (calendar);
2770       else
2771         {
2772           move_focus (calendar, 1);
2773           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2774           calendar_invalidate_day (calendar, calendar->focus_row,
2775                                    calendar->focus_col);
2776         }
2777       break;
2778     case GDK_KP_Up:
2779     case GDK_Up:
2780       return_val = TRUE;
2781       if (event->state & GDK_CONTROL_MASK)
2782         calendar_set_year_prev (calendar);
2783       else
2784         {
2785           if (calendar->focus_row > 0)
2786             calendar->focus_row--;
2787           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2788           calendar_invalidate_day (calendar, calendar->focus_row,
2789                                    calendar->focus_col);
2790         }
2791       break;
2792     case GDK_KP_Down:
2793     case GDK_Down:
2794       return_val = TRUE;
2795       if (event->state & GDK_CONTROL_MASK)
2796         calendar_set_year_next (calendar);
2797       else
2798         {
2799           if (calendar->focus_row < 5)
2800             calendar->focus_row++;
2801           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2802           calendar_invalidate_day (calendar, calendar->focus_row,
2803                                    calendar->focus_col);
2804         }
2805       break;
2806     case GDK_KP_Space:
2807     case GDK_space:
2808       row = calendar->focus_row;
2809       col = calendar->focus_col;
2810       day = calendar->day[row][col];
2811       
2812       if (row > -1 && col > -1)
2813         {
2814           return_val = TRUE;
2815
2816           if (calendar->day_month[row][col] == MONTH_PREV)
2817             calendar_set_month_prev (calendar);
2818           else if (calendar->day_month[row][col] == MONTH_NEXT)
2819             calendar_set_month_next (calendar);
2820
2821           calendar_select_and_focus_day (calendar, day);
2822         }
2823     }   
2824   
2825   return return_val;
2826 }
2827
2828 \f
2829 /****************************************
2830  *           Misc widget methods        *
2831  ****************************************/
2832
2833 static void
2834 calendar_set_background (GtkWidget *widget)
2835 {
2836   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2837   gint i;
2838   
2839   if (GTK_WIDGET_REALIZED (widget))
2840     {
2841       for (i = 0; i < 4; i++)
2842         {
2843           if (priv->arrow_win[i])
2844             gdk_window_set_background (priv->arrow_win[i], 
2845                                        HEADER_BG_COLOR (widget));
2846         }
2847       if (priv->header_win)
2848         gdk_window_set_background (priv->header_win, 
2849                                    HEADER_BG_COLOR (widget));
2850       if (priv->day_name_win)
2851         gdk_window_set_background (priv->day_name_win, 
2852                                    BACKGROUND_COLOR (widget));
2853       if (priv->week_win)
2854         gdk_window_set_background (priv->week_win,
2855                                    BACKGROUND_COLOR (widget));
2856       if (priv->main_win)
2857         gdk_window_set_background (priv->main_win,
2858                                    BACKGROUND_COLOR (widget));
2859       if (widget->window)
2860         gdk_window_set_background (widget->window,
2861                                    BACKGROUND_COLOR (widget)); 
2862     }
2863 }
2864
2865 static void
2866 gtk_calendar_style_set (GtkWidget *widget,
2867                         GtkStyle  *previous_style)
2868 {
2869   if (previous_style && GTK_WIDGET_REALIZED (widget))
2870     calendar_set_background (widget);
2871 }
2872
2873 static void
2874 gtk_calendar_state_changed (GtkWidget      *widget,
2875                             GtkStateType    previous_state)
2876 {
2877   GtkCalendar *calendar = GTK_CALENDAR (widget);
2878   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2879   int i;
2880   
2881   if (!GTK_WIDGET_IS_SENSITIVE (widget))
2882     {
2883       priv->in_drag = 0;
2884       calendar_stop_spinning (calendar);    
2885     }
2886
2887   for (i = 0; i < 4; i++)
2888     if (GTK_WIDGET_IS_SENSITIVE (widget))
2889       priv->arrow_state[i] = GTK_STATE_NORMAL;
2890     else 
2891       priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
2892   
2893   calendar_set_background (widget);
2894 }
2895
2896 static void
2897 gtk_calendar_grab_notify (GtkWidget *widget,
2898                           gboolean   was_grabbed)
2899 {
2900   if (!was_grabbed)
2901     calendar_stop_spinning (GTK_CALENDAR (widget));
2902 }
2903
2904 static gboolean
2905 gtk_calendar_focus_out (GtkWidget     *widget,
2906                         GdkEventFocus *event)
2907 {
2908   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2909
2910   calendar_stop_spinning (GTK_CALENDAR (widget));
2911   
2912   priv->in_drag = 0; 
2913
2914   return FALSE;
2915 }
2916
2917 \f
2918 /****************************************
2919  *          Drag and Drop               *
2920  ****************************************/
2921
2922 static void
2923 gtk_calendar_drag_data_get (GtkWidget        *widget,
2924                             GdkDragContext   *context,
2925                             GtkSelectionData *selection_data,
2926                             guint             info,
2927                             guint             time)
2928 {
2929   GtkCalendar *calendar = GTK_CALENDAR (widget);
2930   GDate *date;
2931   gchar str[128];
2932   gsize len;
2933
2934   date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
2935   len = g_date_strftime (str, 127, "%x", date);
2936   gtk_selection_data_set_text (selection_data, str, len);
2937   
2938   g_free (date);
2939 }
2940
2941 /* Get/set whether drag_motion requested the drag data and
2942  * drag_data_received should thus not actually insert the data,
2943  * since the data doesn't result from a drop.
2944  */
2945 static void
2946 set_status_pending (GdkDragContext *context,
2947                     GdkDragAction   suggested_action)
2948 {
2949   g_object_set_data (G_OBJECT (context),
2950                      I_("gtk-calendar-status-pending"),
2951                      GINT_TO_POINTER (suggested_action));
2952 }
2953
2954 static GdkDragAction
2955 get_status_pending (GdkDragContext *context)
2956 {
2957   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
2958                                              "gtk-calendar-status-pending"));
2959 }
2960
2961 static void
2962 gtk_calendar_drag_leave (GtkWidget      *widget,
2963                          GdkDragContext *context,
2964                          guint           time)
2965 {
2966   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2967
2968   priv->drag_highlight = 0;
2969   gtk_drag_unhighlight (widget);
2970   
2971 }
2972
2973 static gboolean
2974 gtk_calendar_drag_motion (GtkWidget      *widget,
2975                           GdkDragContext *context,
2976                           gint            x,
2977                           gint            y,
2978                           guint           time)
2979 {
2980   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2981   GdkAtom target;
2982   
2983   if (!priv->drag_highlight) 
2984     {
2985       priv->drag_highlight = 1;
2986       gtk_drag_highlight (widget);
2987     }
2988   
2989   target = gtk_drag_dest_find_target (widget, context, NULL);
2990   if (target == GDK_NONE)
2991     gdk_drag_status (context, 0, time);
2992   else
2993     {
2994       set_status_pending (context, context->suggested_action);
2995       gtk_drag_get_data (widget, context, target, time);
2996     }
2997   
2998   return TRUE;
2999 }
3000
3001 static gboolean
3002 gtk_calendar_drag_drop (GtkWidget      *widget,
3003                         GdkDragContext *context,
3004                         gint            x,
3005                         gint            y,
3006                         guint           time)
3007 {
3008   GdkAtom target;
3009
3010   target = gtk_drag_dest_find_target (widget, context, NULL);  
3011   if (target != GDK_NONE)
3012     {
3013       gtk_drag_get_data (widget, context, 
3014                          target, 
3015                          time);
3016       return TRUE;
3017     }
3018
3019   return FALSE;
3020 }
3021
3022 static void
3023 gtk_calendar_drag_data_received (GtkWidget        *widget,
3024                                  GdkDragContext   *context,
3025                                  gint              x,
3026                                  gint              y,
3027                                  GtkSelectionData *selection_data,
3028                                  guint             info,
3029                                  guint             time)
3030 {
3031   GtkCalendar *calendar = GTK_CALENDAR (widget);
3032   guint day, month, year;
3033   gchar *str;
3034   GDate *date;
3035   GdkDragAction suggested_action;
3036
3037   suggested_action = get_status_pending (context);
3038
3039   if (suggested_action) 
3040     {
3041       set_status_pending (context, 0);
3042      
3043       /* We are getting this data due to a request in drag_motion,
3044        * rather than due to a request in drag_drop, so we are just
3045        * supposed to call drag_status, not actually paste in the
3046        * data.
3047        */
3048       str = gtk_selection_data_get_text (selection_data);
3049       if (str) 
3050         {
3051           date = g_date_new ();
3052           g_date_set_parse (date, str);
3053           if (!g_date_valid (date)) 
3054               suggested_action = 0;
3055           g_date_free (date);
3056           g_free (str);
3057         }
3058       else
3059         suggested_action = 0;
3060
3061       gdk_drag_status (context, suggested_action, time);
3062
3063       return;
3064     }
3065
3066   date = g_date_new ();
3067   str = gtk_selection_data_get_text (selection_data);
3068   if (str) 
3069     {
3070       g_date_set_parse (date, str);
3071       g_free (str);
3072     }
3073   
3074   if (!g_date_valid (date)) 
3075     {
3076       g_warning ("Received invalid date data\n");
3077       g_date_free (date);       
3078       gtk_drag_finish (context, FALSE, FALSE, time);
3079       return;
3080     }
3081
3082   day = g_date_get_day (date);
3083   month = g_date_get_month (date);
3084   year = g_date_get_year (date);
3085   g_date_free (date);   
3086
3087   gtk_drag_finish (context, TRUE, FALSE, time);
3088
3089   
3090   g_object_freeze_notify (G_OBJECT (calendar));
3091   if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3092       && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
3093     gtk_calendar_select_month (calendar, month - 1, year);
3094   gtk_calendar_select_day (calendar, day);
3095   g_object_thaw_notify (G_OBJECT (calendar));  
3096 }
3097
3098 \f
3099 /****************************************
3100  *              Public API              *
3101  ****************************************/
3102
3103 /**
3104  * gtk_calendar_new:
3105  * 
3106  * Creates a new calendar, with the current date being selected. 
3107  * 
3108  * Return value: a newly #GtkCalendar widget
3109  **/
3110 GtkWidget*
3111 gtk_calendar_new (void)
3112 {
3113   return g_object_new (GTK_TYPE_CALENDAR, NULL);
3114 }
3115
3116 /**
3117  * gtk_calendar_display_options:
3118  * @calendar: a #GtkCalendar.
3119  * @flags: the display options to set.
3120  *
3121  * Sets display options (whether to display the heading and the month headings).
3122  * 
3123  * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
3124  **/
3125 void
3126 gtk_calendar_display_options (GtkCalendar              *calendar,
3127                               GtkCalendarDisplayOptions flags)
3128 {
3129   gtk_calendar_set_display_options (calendar, flags);
3130 }
3131
3132 /**
3133  * gtk_calendar_get_display_options:
3134  * @calendar: a #GtkCalendar
3135  * 
3136  * Returns the current display options of @calendar. 
3137  * 
3138  * Return value: the display options.
3139  *
3140  * Since: 2.4
3141  **/
3142 GtkCalendarDisplayOptions 
3143 gtk_calendar_get_display_options (GtkCalendar         *calendar)
3144 {
3145   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3146
3147   return calendar->display_flags;
3148 }
3149
3150 /**
3151  * gtk_calendar_set_display_options:
3152  * @calendar: a #GtkCalendar
3153  * @flags: the display options to set
3154  * 
3155  * Sets display options (whether to display the heading and the month  
3156  * headings).
3157  *
3158  * Since: 2.4
3159  **/
3160 void
3161 gtk_calendar_set_display_options (GtkCalendar          *calendar,
3162                                   GtkCalendarDisplayOptions flags)
3163 {
3164   GtkWidget *widget = GTK_WIDGET (calendar);
3165   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3166   gint resize = 0;
3167   gint i;
3168   GtkCalendarDisplayOptions old_flags;
3169   
3170   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3171   
3172   old_flags = calendar->display_flags;
3173   
3174   if (GTK_WIDGET_REALIZED (widget))
3175     {
3176       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3177         {
3178           resize ++;
3179           if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3180               && (priv->header_win))
3181             {
3182               calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3183               calendar_realize_arrows (calendar);
3184             }
3185           else
3186             {
3187               for (i = 0; i < 4; i++)
3188                 {
3189                   if (priv->arrow_win[i])
3190                     {
3191                       gdk_window_set_user_data (priv->arrow_win[i], 
3192                                                 NULL);
3193                       gdk_window_destroy (priv->arrow_win[i]);
3194                       priv->arrow_win[i] = NULL;
3195                     }
3196                 }
3197             }
3198         }
3199       
3200       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3201         {
3202           resize++;
3203           
3204           if (flags & GTK_CALENDAR_SHOW_HEADING)
3205             {
3206               calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3207               calendar_realize_header (calendar);
3208             }
3209           else
3210             {
3211               for (i = 0; i < 4; i++)
3212                 {
3213                   if (priv->arrow_win[i])
3214                     {
3215                       gdk_window_set_user_data (priv->arrow_win[i], 
3216                                                 NULL);
3217                       gdk_window_destroy (priv->arrow_win[i]);
3218                       priv->arrow_win[i] = NULL;
3219                     }
3220                 }
3221               gdk_window_set_user_data (priv->header_win, NULL);
3222               gdk_window_destroy (priv->header_win);
3223               priv->header_win = NULL;
3224             }
3225         }
3226       
3227       
3228       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3229         {
3230           resize++;
3231           
3232           if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3233             {
3234               calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3235               calendar_realize_day_names (calendar);
3236             }
3237           else
3238             {
3239               gdk_window_set_user_data (priv->day_name_win, NULL);
3240               gdk_window_destroy (priv->day_name_win);
3241               priv->day_name_win = NULL;
3242             }
3243         }
3244       
3245       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3246         {
3247           resize++;
3248           
3249           if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3250             {
3251               calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3252               calendar_realize_week_numbers (calendar);
3253             }
3254           else
3255             {
3256               gdk_window_set_user_data (priv->week_win, NULL);
3257               gdk_window_destroy (priv->week_win);
3258               priv->week_win = NULL;
3259             }
3260         }
3261
3262       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3263         g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3264       
3265       calendar->display_flags = flags;
3266       if (resize)
3267         gtk_widget_queue_resize (GTK_WIDGET (calendar));
3268       
3269     } 
3270   else
3271     calendar->display_flags = flags;
3272   
3273   g_object_freeze_notify (G_OBJECT (calendar));
3274   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3275     g_object_notify (G_OBJECT (calendar), "show-heading");
3276   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3277     g_object_notify (G_OBJECT (calendar), "show-day-names");
3278   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3279     g_object_notify (G_OBJECT (calendar), "no-month-change");
3280   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3281     g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3282   g_object_thaw_notify (G_OBJECT (calendar));
3283 }
3284
3285 /**
3286  * gtk_calendar_select_month:
3287  * @calendar: a #GtkCalendar
3288  * @month: a month number between 0 and 11.
3289  * @year: the year the month is in.
3290  * 
3291  * Shifts the calendar to a different month.
3292  * 
3293  * Return value: %TRUE, always
3294  **/
3295 gboolean
3296 gtk_calendar_select_month (GtkCalendar *calendar,
3297                            guint        month,
3298                            guint        year)
3299 {
3300   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3301   g_return_val_if_fail (month <= 11, FALSE);
3302   
3303   calendar->month = month;
3304   calendar->year  = year;
3305   
3306   calendar_compute_days (calendar);
3307   
3308   gtk_widget_queue_draw (GTK_WIDGET (calendar));
3309
3310   g_object_freeze_notify (G_OBJECT (calendar));
3311   g_object_notify (G_OBJECT (calendar), "month");
3312   g_object_notify (G_OBJECT (calendar), "year");
3313   g_object_thaw_notify (G_OBJECT (calendar));
3314
3315   g_signal_emit (calendar,
3316                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3317                  0);
3318   return TRUE;
3319 }
3320
3321 /**
3322  * gtk_calendar_select_day:
3323  * @calendar: a #GtkCalendar.
3324  * @day: the day number between 1 and 31, or 0 to unselect 
3325  *   the currently selected day.
3326  * 
3327  * Selects a day from the current month.
3328  **/
3329 void
3330 gtk_calendar_select_day (GtkCalendar *calendar,
3331                          guint        day)
3332 {
3333   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3334   g_return_if_fail (day <= 31);
3335   
3336   /* Deselect the old day */
3337   if (calendar->selected_day > 0)
3338     {
3339       gint selected_day;
3340       
3341       selected_day = calendar->selected_day;
3342       calendar->selected_day = 0;
3343       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3344         calendar_invalidate_day_num (calendar, selected_day);
3345     }
3346   
3347   calendar->selected_day = day;
3348   
3349   /* Select the new day */
3350   if (day != 0)
3351     {
3352       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3353         calendar_invalidate_day_num (calendar, day);
3354     }
3355   
3356   g_object_notify (G_OBJECT (calendar), "day");
3357
3358   g_signal_emit (calendar,
3359                  gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3360                  0);
3361 }
3362
3363 /**
3364  * gtk_calendar_clear_marks:
3365  * @calendar: a #GtkCalendar
3366  * 
3367  * Remove all visual markers.
3368  **/
3369 void
3370 gtk_calendar_clear_marks (GtkCalendar *calendar)
3371 {
3372   guint day;
3373   
3374   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3375   
3376   for (day = 0; day < 31; day++)
3377     {
3378       calendar->marked_date[day] = FALSE;
3379     }
3380
3381   calendar->num_marked_dates = 0;
3382
3383   gtk_widget_queue_draw (GTK_WIDGET (calendar));
3384 }
3385
3386 /**
3387  * gtk_calendar_mark_day:
3388  * @calendar: a #GtkCalendar 
3389  * @day: the day number to mark between 1 and 31.
3390  * 
3391  * Places a visual marker on a particular day.
3392  * 
3393  * Return value: %TRUE, always
3394  **/
3395 gboolean
3396 gtk_calendar_mark_day (GtkCalendar *calendar,
3397                        guint        day)
3398 {
3399   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3400   
3401   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3402     {
3403       calendar->marked_date[day - 1] = TRUE;
3404       calendar->num_marked_dates++;
3405       calendar_invalidate_day_num (calendar, day);
3406     }
3407   
3408   return TRUE;
3409 }
3410
3411 /**
3412  * gtk_calendar_unmark_day:
3413  * @calendar: a #GtkCalendar.
3414  * @day: the day number to unmark between 1 and 31.
3415  * 
3416  * Removes the visual marker from a particular day.
3417  * 
3418  * Return value: %TRUE, always
3419  **/
3420 gboolean
3421 gtk_calendar_unmark_day (GtkCalendar *calendar,
3422                          guint        day)
3423 {
3424   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3425   
3426   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3427     {
3428       calendar->marked_date[day - 1] = FALSE;
3429       calendar->num_marked_dates--;
3430       calendar_invalidate_day_num (calendar, day);
3431     }
3432   
3433   return TRUE;
3434 }
3435
3436 /**
3437  * gtk_calendar_get_date:
3438  * @calendar: a #GtkCalendar
3439  * @year: location to store the year number, or %NULL
3440  * @month: location to store the month number (between 0 and 11), or %NULL
3441  * @day: location to store the day number (between 1 and 31), or %NULL
3442  * 
3443  * Obtains the selected date from a #GtkCalendar.
3444  **/
3445 void
3446 gtk_calendar_get_date (GtkCalendar *calendar,
3447                        guint       *year,
3448                        guint       *month,
3449                        guint       *day)
3450 {
3451   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3452   
3453   if (year)
3454     *year = calendar->year;
3455   
3456   if (month)
3457     *month = calendar->month;
3458   
3459   if (day)
3460     *day = calendar->selected_day;
3461 }
3462
3463 /**
3464  * gtk_calendar_freeze:
3465  * @calendar: a #GtkCalendar
3466  * 
3467  * Does nothing. Previously locked the display of the calendar until
3468  * it was thawed with gtk_calendar_thaw().
3469  *
3470  * Deprecated: 2.8: 
3471  **/
3472 void
3473 gtk_calendar_freeze (GtkCalendar *calendar)
3474 {
3475   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3476 }
3477
3478 /**
3479  * gtk_calendar_thaw:
3480  * @calendar: a #GtkCalendar
3481  * 
3482  * Does nothing. Previously defrosted a calendar; all the changes made
3483  * since the last gtk_calendar_freeze() were displayed.
3484  *
3485  * Deprecated: 2.8: 
3486  **/
3487 void
3488 gtk_calendar_thaw (GtkCalendar *calendar)
3489 {
3490   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3491 }
3492
3493 #define __GTK_CALENDAR_C__
3494 #include "gtkaliasdef.c"