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