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