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