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