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