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