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