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