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