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