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