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