Replace g_idle_add/g_timeout_add with their gdk_threads counterparts
[hildon] / hildon / hildon-calendar.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
5  *
6  * GTK Calendar Widget
7  * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Groenlund
8  * 
9  * lib_date routines
10  * Copyright (C) 1995, 1996, 1997, 1998 by Steffen Beyer
11  * 
12  * HldonCalendar modifications
13  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
14  *
15  * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
16  *
17  * This library is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Lesser General Public
19  * License as published by the Free Software Foundation; either
20  * version 2 of the License, or (at your option) any later version. or (at your option) any later version.
21  *
22  * This library is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * Lesser General Public License for more details.
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free
29  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30  */
31
32 /*
33  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
34  * file for a list of people on the GTK+ Team.  See the ChangeLog
35  * files for a list of changes.  These files are distributed with
36  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
37  */
38
39 /**
40  * SECTION:hildon-calendar
41  * @short_description: A calendar widget
42  *
43  * #HildonCalendar is a slightly modified #GtkCalendar. It has an almost same API
44  * but a slightly different look and behaviour. Use this widget instead of
45  * standard #GtkCalendar or use #HildonDateEditor for more higher-level date setting
46  * operations.
47  *
48  * <note>
49  *   <para>
50  * #HildonCalendar has been deprecated since Hildon 2.2
51  * See <link linkend="hildon-migrating-date-widgets">Migrating Date Widgets</link>
52  * section to know how to migrate this deprecated widget.
53  *   </para>
54  * </note>
55  */
56
57 #undef                                          HILDON_DISABLE_DEPRECATED
58
59 #ifdef                                          HAVE_CONFIG_H
60 #include                                        <config.h>
61 #endif
62
63 #define                                         _GNU_SOURCE /* needed for GNU nl_langinfo_l */
64
65 #include                                        <locale.h>
66
67 #ifdef                                          HAVE_SYS_TIME_H
68 #include                                        <sys/time.h>
69 #endif
70
71 #include                                        <string.h>
72 #include                                        <stdlib.h>
73 #include                                        <time.h>
74 #include                                        <langinfo.h>
75
76 #include                                        <glib/gprintf.h>
77 #include                                        <gdk/gdkkeysyms.h>
78 #include                                        <gtk/gtkprivate.h>
79
80 #include                                        "hildon-calendar.h"
81 #include                                        "hildon-marshalers.h"
82 #include                                        "hildon-calendar-private.h"
83
84 /***************************************************************************/
85 /* The following date routines are taken from the lib_date package.  Keep
86  * them separate in case we want to update them if a newer lib_date comes
87  * out with fixes.  */
88
89 typedef unsigned int                            N_int;
90
91 typedef unsigned long                           N_long;
92
93 typedef signed long                             Z_long;
94
95 typedef enum                                    { false = FALSE , true = TRUE } boolean;
96
97 #define                                         and &&      /* logical (boolean) operators: lower case */
98
99 #define                                         or ||
100
101 static const                                    N_int month_length [2][13] =
102 {
103     { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
104     { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
105 };
106
107 static const N_int days_in_months[2][14] =
108 {
109     { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
110     { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
111 };
112
113 static                                          Z_long calc_days (N_int year, N_int mm, N_int dd);
114
115 static                                          N_int day_of_week (N_int year, N_int mm, N_int dd);
116
117 static                                          Z_long dates_difference (N_int year1, N_int mm1, N_int dd1, 
118                                                                          N_int year2, N_int mm2, N_int dd2);
119
120 static N_int                                    weeks_in_year (N_int year);
121
122 static boolean 
123 leap                                            (N_int year)
124 {
125     return ((((year % 4) == 0) and ((year % 100) != 0)) or ((year % 400) == 0));
126 }
127
128 static N_int 
129 day_of_week                                     (N_int year, 
130                                                  N_int mm, 
131                                                  N_int dd)
132 {
133     Z_long days;
134
135     days = calc_days (year, mm, dd);
136     if (days > 0L)
137     {
138         days--;
139         days %= 7L;
140         days++;
141     }
142     return( (N_int) days );
143 }
144
145 static N_int 
146 weeks_in_year                                   (N_int year)
147 {
148     return (52 + ((day_of_week(year,1,1)==4) or (day_of_week(year,12,31)==4)));
149 }
150
151 static boolean 
152 check_date                                      (N_int year, 
153                                                  N_int mm, 
154                                                  N_int dd)
155 {
156     if (year < 1) return(false);
157     if ((mm < 1) or (mm > 12)) return(false);
158     if ((dd < 1) or (dd > month_length[leap(year)][mm])) return(false);
159     return(true);
160 }
161
162 static N_int 
163 week_number                                     (N_int year, 
164                                                  N_int mm, 
165                                                  N_int dd)
166 {
167     N_int first;
168
169     first = day_of_week (year,1,1) - 1;
170     return( (N_int) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
171             (first < 4) );
172 }
173
174 static Z_long 
175 year_to_days                                    (N_int year)
176 {
177     return ( year * 365L + (year / 4) - (year / 100) + (year / 400) );
178 }
179
180 static Z_long 
181 calc_days                                       (N_int year, 
182                                                  N_int mm, 
183                                                  N_int dd)
184 {
185     boolean lp;
186
187     if (year < 1) return(0L);
188     if ((mm < 1) or (mm > 12)) return(0L);
189     if ((dd < 1) or (dd > month_length[(lp = leap(year))][mm])) return(0L);
190     return( year_to_days(--year) + days_in_months[lp][mm] + dd );
191 }
192
193 static boolean 
194 week_of_year                                    (N_int *week, 
195                                                  N_int *year, 
196                                                  N_int mm, 
197                                                  N_int dd)
198 {
199     if (check_date(*year,mm,dd))
200     {
201         *week = week_number(*year,mm,dd);
202         if (*week == 0) 
203             *week = weeks_in_year(--(*year));
204         else if (*week > weeks_in_year(*year))
205         {
206             *week = 1;
207             (*year)++;
208         }
209         return(true);
210     }
211     return(false);
212 }
213
214 static Z_long 
215 dates_difference                                (N_int year1, 
216                                                  N_int mm1, 
217                                                  N_int dd1,
218                                                  N_int year2, 
219                                                  N_int mm2, 
220                                                  N_int dd2)
221 {
222     return (calc_days (year2, mm2, dd2) - calc_days (year1, mm1, dd1));
223 }
224
225 /*** END OF lib_date routines ********************************************/
226
227 /* HILDON: Spacings modified */
228 #define                                         HILDON_ARROW_SEP 5 /* Space between arrows and data */
229
230 #define                                         HILDON_DAY_WIDTH 26
231
232 #define                                         HILDON_DAY_HEIGHT 25 
233
234 /* additional widths given to week number and day windows */
235
236 #define                                         HILDON_WEEKS_EXTRA_WIDTH 8
237
238 #define                                         HILDON_DAYS_EXTRA_WIDTH 8
239
240 /* Spacing around day/week headers and main area, inside those windows */
241
242 #define                                         CALENDAR_MARGIN 0
243
244 /* Spacing around day/week headers and main area, outside those windows */
245
246 #define                                         INNER_BORDER 0 /* 4 */
247
248 /* Separation between day headers and main area */
249
250 #define                                         CALENDAR_YSEP 3 /* 4 */
251
252 /* Separation between week headers and main area */
253
254 #define                                         CALENDAR_XSEP 6 /* 4 */
255
256 #define                                         DAY_XSEP 0 /* not really good for small calendar */
257
258 #define                                         DAY_YSEP 0 /* not really good for small calendar */
259
260 /* Color usage */
261 #define                                         HEADER_FG_COLOR(widget) \
262                                                 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
263
264 #define                                         HEADER_BG_COLOR(widget) \
265                                                 (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
266
267 #define                                         SELECTED_BG_COLOR(widget) \
268                                                 (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
269
270 #define                                         SELECTED_FG_COLOR(widget) \
271                                                 (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
272
273 #define                                         NORMAL_DAY_COLOR(widget) \
274                                                 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
275
276 #define                                         PREV_MONTH_COLOR(widget) \
277                                                 (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
278
279 #define                                         NEXT_MONTH_COLOR(widget) \
280                                                 (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
281
282 #define                                         MARKED_COLOR(widget) \
283                                                 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
284
285 #define                                         BACKGROUND_COLOR(widget) \
286                                                 (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
287
288 #define                                         HIGHLIGHT_BACK_COLOR(widget) \
289                                                 (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
290
291 #define                                         CALENDAR_INITIAL_TIMER_DELAY    200
292
293 #define                                         CALENDAR_TIMER_DELAY            20
294
295 enum {
296     ARROW_YEAR_LEFT,
297     ARROW_YEAR_RIGHT,
298     ARROW_MONTH_LEFT,
299     ARROW_MONTH_RIGHT
300 };
301
302 enum {
303     MONTH_PREV,
304     MONTH_CURRENT,
305     MONTH_NEXT
306 };
307
308 enum {
309     MONTH_CHANGED_SIGNAL,
310     DAY_SELECTED_SIGNAL,
311     DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
312     PREV_MONTH_SIGNAL,
313     NEXT_MONTH_SIGNAL,
314     PREV_YEAR_SIGNAL,
315     NEXT_YEAR_SIGNAL,
316     ERRONEOUS_DATE_SIGNAL,
317     SELECTED_DATE_SIGNAL,
318     LAST_SIGNAL
319 };
320
321 enum
322 {
323     PROP_0,
324     PROP_YEAR,
325     PROP_MONTH,
326     PROP_DAY,
327     PROP_SHOW_HEADING,
328     PROP_SHOW_DAY_NAMES,
329     PROP_NO_MONTH_CHANGE,
330     PROP_SHOW_WEEK_NUMBERS,
331     PROP_WEEK_START,
332     PROP_MIN_YEAR,
333     PROP_MAX_YEAR,
334     PROP_LAST
335 };
336
337 static gint                                     hildon_calendar_signals [LAST_SIGNAL] = { 0 };
338
339 static GtkWidgetClass*                          parent_class = NULL;
340
341 typedef void (*HildonCalendarSignalDate) (GtkObject *object, guint arg1, guint arg2, guint arg3, gpointer data);
342
343 static void
344 hildon_calendar_class_init                      (HildonCalendarClass *class);
345
346 static void 
347 hildon_calendar_init                            (HildonCalendar *calendar);
348
349 static void
350 hildon_calendar_finalize                        (GObject *calendar);
351
352 static void 
353 hildon_calendar_destroy                         (GtkObject *calendar);
354
355 static void 
356 hildon_calendar_set_property                    (GObject *object,
357                                                  guint prop_id,
358                                                  const GValue *value,
359                                                  GParamSpec *pspec);
360
361 static void
362 hildon_calendar_get_property                    (GObject *object,
363                                                  guint prop_id,
364                                                  GValue *value,
365                                                  GParamSpec *pspec);
366
367 static void
368 hildon_calendar_realize                         (GtkWidget *widget);
369
370 static void
371 hildon_calendar_unrealize                       (GtkWidget *widget);
372
373 static void 
374 hildon_calendar_size_request                    (GtkWidget *widget,
375                                                  GtkRequisition *requisition);
376
377 static void 
378 hildon_calendar_size_allocate                   (GtkWidget *widget,
379                                                  GtkAllocation *allocation);
380
381 static gint 
382 hildon_calendar_expose                          (GtkWidget *widget,
383                                                  GdkEventExpose *event);
384
385 static gint 
386 hildon_calendar_button_press                    (GtkWidget *widget,
387                                                  GdkEventButton *event);
388
389 static gint 
390 hildon_calendar_button_release                  (GtkWidget *widget,
391                                                  GdkEventButton *event);
392
393 static void
394 hildon_calendar_main_button                     (GtkWidget *widget,
395                                                  GdkEventButton *event);
396
397 static gint 
398 hildon_calendar_motion_notify                   (GtkWidget *widget,
399                                                  GdkEventMotion *event);
400
401 static gint
402 hildon_calendar_enter_notify                    (GtkWidget *widget,
403                                                  GdkEventCrossing *event);
404
405 static gint 
406 hildon_calendar_leave_notify                    (GtkWidget *widget,
407                                                  GdkEventCrossing *event);
408
409 static gint 
410 hildon_calendar_key_press                       (GtkWidget *widget,
411                                                  GdkEventKey *event);
412
413 static gint 
414 hildon_calendar_scroll                          (GtkWidget *widget,
415                                                  GdkEventScroll *event);
416
417 static void 
418 hildon_calendar_grab_notify                     (GtkWidget *widget,
419                                                  gboolean was_grabbed);
420
421 static gboolean 
422 hildon_calendar_focus_out                       (GtkWidget *widget,
423                                                  GdkEventFocus *event);
424
425 static void 
426 hildon_calendar_state_changed                   (GtkWidget *widget,
427                                                  GtkStateType previous_state);
428
429 static void
430 hildon_calendar_style_set                       (GtkWidget *widget,
431                                                  GtkStyle *previous_style);
432
433 static void
434 hildon_calendar_paint_header                    (GtkWidget *widget);
435
436 static void 
437 hildon_calendar_paint_footer                    (GtkWidget *widget);
438
439 static void
440 hildon_calendar_paint_day_names                 (GtkWidget *widget);
441
442 static void
443 hildon_calendar_paint_week_numbers              (GtkWidget *widget);
444
445 static void 
446 hildon_calendar_paint_main                      (GtkWidget *widget);
447
448 static void 
449 hildon_calendar_select_and_focus_day            (HildonCalendar *calendar,
450                                                  guint day);
451
452 static void 
453 hildon_calendar_paint_arrow                     (GtkWidget *widget,
454                                                  guint arrow);
455
456 static void 
457 hildon_calendar_paint_day_num                   (GtkWidget *widget,
458                                                  gint day);
459
460 static void 
461 hildon_calendar_paint_day                       (GtkWidget *widget,
462                                                  gint row,
463                                                  gint col);
464
465 static void
466 hildon_calendar_compute_days                    (HildonCalendar *calendar);
467
468 static gint 
469 left_x_for_column                               (HildonCalendar  *calendar,
470                                                  gint column);
471
472 static gint 
473 top_y_for_row                                   (HildonCalendar  *calendar,
474                                                  gint row);
475
476 static void 
477 hildon_calendar_drag_data_get                   (GtkWidget *widget,
478                                                  GdkDragContext *context,
479                                                  GtkSelectionData *selection_data,
480                                                  guint info,
481                                                  guint time);
482
483 static void 
484 hildon_calendar_drag_data_received              (GtkWidget *widget,
485                                                  GdkDragContext *context,
486                                                  gint x,
487                                                  gint y,
488                                                  GtkSelectionData *selection_data,
489                                                  guint info,
490                                                  guint time);
491
492 static gboolean 
493 hildon_calendar_drag_motion                     (GtkWidget *widget,
494                                                  GdkDragContext *context,
495                                                  gint x,
496                                                  gint y,
497                                                  guint time);
498
499 static void 
500 hildon_calendar_drag_leave                      (GtkWidget *widget,
501                                                  GdkDragContext *context,
502                                                  guint time);
503
504 static gboolean
505 hildon_calendar_drag_drop                       (GtkWidget *widget,
506                                                  GdkDragContext *context,
507                                                  gint x,
508                                                  gint y,
509                                                  guint time);
510
511 /* This function was added because we need to mark current day according to
512  * specifications
513  */
514
515 static void
516 hildon_calendar_check_current_date              (HildonCalendar *calendar, 
517                                                  gint x, 
518                                                  gint y);
519
520 GType G_GNUC_CONST
521 hildon_calendar_get_type                        (void)
522 {
523     static GType calendar_type = 0;
524
525     if (!calendar_type)
526     {
527         static const GTypeInfo calendar_info =
528         {
529             sizeof (HildonCalendarClass),
530             NULL,           /* base_init */
531             NULL,           /* base_finalize */
532             (GClassInitFunc) hildon_calendar_class_init,
533             NULL,           /* class_finalize */
534             NULL,           /* class_data */
535             sizeof (HildonCalendar),
536             0,              /* n_preallocs */
537             (GInstanceInitFunc) hildon_calendar_init,
538         };
539
540         calendar_type = g_type_register_static (GTK_TYPE_WIDGET, "HildonCalendar",
541                 &calendar_info, 0);
542     }
543
544     return calendar_type;
545 }
546
547 static void
548 locales_init                                    (HildonCalendarPrivate *priv)
549 {
550     /* Hildon: This is not exactly portable, see
551      * http://bugzilla.gnome.org/show_bug.cgi?id=343415
552      * The labels need to be instance variables as the startup wizard changes
553      * locale on runtime.
554      */
555     locale_t l;
556
557     l = newlocale (LC_TIME_MASK, setlocale (LC_MESSAGES, NULL), NULL);
558
559     priv->abbreviated_dayname[0] = g_locale_to_utf8 (nl_langinfo_l(ABDAY_1, l),
560             -1, NULL, NULL, NULL);
561     priv->abbreviated_dayname[1] = g_locale_to_utf8 (nl_langinfo_l(ABDAY_2, l),
562             -1, NULL, NULL, NULL);
563     priv->abbreviated_dayname[2] = g_locale_to_utf8 (nl_langinfo_l(ABDAY_3, l),
564             -1, NULL, NULL, NULL) ;
565     priv->abbreviated_dayname[3] = g_locale_to_utf8 (nl_langinfo_l(ABDAY_4, l),
566             -1, NULL, NULL, NULL);
567     priv->abbreviated_dayname[4] = g_locale_to_utf8 (nl_langinfo_l(ABDAY_5, l),
568             -1, NULL, NULL, NULL);
569     priv->abbreviated_dayname[5] = g_locale_to_utf8 (nl_langinfo_l(ABDAY_6, l),
570             -1, NULL, NULL, NULL);
571     priv->abbreviated_dayname[6] = g_locale_to_utf8 (nl_langinfo_l(ABDAY_7, l),
572             -1, NULL, NULL, NULL);
573     priv->monthname[0] = g_locale_to_utf8 (nl_langinfo_l(MON_1, l),
574             -1, NULL, NULL, NULL);
575     priv->monthname[1] = g_locale_to_utf8 (nl_langinfo_l(MON_2, l),
576             -1, NULL, NULL, NULL);
577     priv->monthname[2] = g_locale_to_utf8 (nl_langinfo_l(MON_3, l),
578             -1, NULL, NULL, NULL);
579     priv->monthname[3] = g_locale_to_utf8 (nl_langinfo_l(MON_4, l),
580             -1, NULL, NULL, NULL);
581     priv->monthname[4] = g_locale_to_utf8 (nl_langinfo_l(MON_5, l),
582             -1, NULL, NULL, NULL);
583     priv->monthname[5] = g_locale_to_utf8 (nl_langinfo_l(MON_6, l),
584             -1, NULL, NULL, NULL);
585     priv->monthname[6] = g_locale_to_utf8 (nl_langinfo_l(MON_7, l),
586             -1, NULL, NULL, NULL);
587     priv->monthname[7] = g_locale_to_utf8 (nl_langinfo_l(MON_8, l),
588             -1, NULL, NULL, NULL);
589     priv->monthname[8] = g_locale_to_utf8 (nl_langinfo_l(MON_9, l),
590             -1, NULL, NULL, NULL);
591     priv->monthname[9] = g_locale_to_utf8 (nl_langinfo_l(MON_10, l),
592             -1, NULL, NULL, NULL);
593     priv->monthname[10] = g_locale_to_utf8 (nl_langinfo_l(MON_11, l),
594             -1, NULL, NULL, NULL);
595     priv->monthname[11] = g_locale_to_utf8 (nl_langinfo_l(MON_12, l),
596             -1, NULL, NULL, NULL);
597
598     freelocale (l);
599 }
600
601 static void
602 hildon_calendar_class_init                      (HildonCalendarClass *class)
603 {
604     GObjectClass *gobject_class;
605     GtkObjectClass *object_class;
606     GtkWidgetClass *widget_class;
607
608     gobject_class = (GObjectClass*) class;
609     object_class = (GtkObjectClass*) class;
610     widget_class = (GtkWidgetClass*) class;
611
612     parent_class = g_type_class_peek_parent (class);
613
614     gobject_class->set_property             = hildon_calendar_set_property;
615     gobject_class->get_property             = hildon_calendar_get_property;
616     gobject_class->finalize                 = hildon_calendar_finalize;
617
618     object_class->destroy                   = hildon_calendar_destroy;
619
620     widget_class->realize                   = hildon_calendar_realize;
621     widget_class->unrealize                 = hildon_calendar_unrealize;
622     widget_class->expose_event              = hildon_calendar_expose;
623     widget_class->size_request              = hildon_calendar_size_request;
624     widget_class->size_allocate             = hildon_calendar_size_allocate;
625     widget_class->button_press_event        = hildon_calendar_button_press;
626     widget_class->button_release_event      = hildon_calendar_button_release;
627     widget_class->motion_notify_event       = hildon_calendar_motion_notify;
628     widget_class->enter_notify_event        = hildon_calendar_enter_notify;
629     widget_class->leave_notify_event        = hildon_calendar_leave_notify;
630     widget_class->key_press_event           = hildon_calendar_key_press;
631     widget_class->scroll_event              = hildon_calendar_scroll;
632     widget_class->style_set                 = hildon_calendar_style_set;
633     widget_class->state_changed             = hildon_calendar_state_changed;
634     widget_class->grab_notify               = hildon_calendar_grab_notify;
635     widget_class->focus_out_event           = hildon_calendar_focus_out;
636
637     widget_class->drag_data_get             = hildon_calendar_drag_data_get;
638     widget_class->drag_motion               = hildon_calendar_drag_motion;
639     widget_class->drag_leave                = hildon_calendar_drag_leave;
640     widget_class->drag_drop                 = hildon_calendar_drag_drop;
641     widget_class->drag_data_received        = hildon_calendar_drag_data_received;
642
643     class->month_changed = NULL;
644     class->day_selected = NULL;
645     class->day_selected_double_click = NULL;
646     class->prev_month = NULL;
647     class->next_month = NULL;
648     class->prev_year = NULL;
649     class->next_year = NULL;
650
651     /**
652      * HildonCalendar:year:
653      *
654      * The selected year.
655      */
656     g_object_class_install_property (gobject_class,
657             PROP_YEAR,
658             g_param_spec_int ("year",
659                 "Year",
660                 "The selected year",
661                 0, G_MAXINT, 0,
662                 GTK_PARAM_READWRITE));
663
664     /**
665      * HildonCalendar:month:
666      *
667      * The selected month as number between 0 and 11.
668      */
669     g_object_class_install_property (gobject_class,
670             PROP_MONTH,
671             g_param_spec_int ("month",
672                 "Month",
673                 "The selected month (as a number between 0 and 11)",
674                 0, 11, 0,
675                 GTK_PARAM_READWRITE));
676
677     /**
678      * HildonCalendar:day:
679      *
680      * The selected day as number between 1 and 31 or 0 to unselect the currently selected day.
681      */
682     g_object_class_install_property (gobject_class,
683             PROP_DAY,
684             g_param_spec_int ("day",
685                 "Day",
686                 "The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)",
687                 0, 31, 0,
688                 GTK_PARAM_READWRITE));
689
690     /**
691      * HildonCalendar:show-heading:
692      *
693      * Determines whether a heading is displayed.
694      *
695      */
696     g_object_class_install_property (gobject_class,
697             PROP_SHOW_HEADING,
698             g_param_spec_boolean ("show-heading",
699                 "Show Heading",
700                 "If TRUE, a heading is displayed",
701                 TRUE,
702                 GTK_PARAM_READWRITE));
703
704     /**
705      * HildonCalendar:show-day-names:
706      *
707      * Determines whether day names are displayed.
708      *
709      */
710     g_object_class_install_property (gobject_class,
711             PROP_SHOW_DAY_NAMES,
712             g_param_spec_boolean ("show-day-names",
713                 "Show Day Names",
714                 "If TRUE, day names are displayed",
715                 TRUE,
716                 GTK_PARAM_READWRITE));
717     /**
718      * HildonCalendar:no-month-change:
719      *
720      * Determines whether the selected month can be changed.
721      *
722      */
723     g_object_class_install_property (gobject_class,
724             PROP_NO_MONTH_CHANGE,
725             g_param_spec_boolean ("no-month-change",
726                 "No Month Change",
727                 "If TRUE, the selected month cannot be changed",
728                 FALSE,
729                 GTK_PARAM_READWRITE));
730
731     /**
732      * HildonCalendar:show-week-numbers:
733      *
734      * Determines whether week numbers are displayed.
735      *
736      */
737     g_object_class_install_property (gobject_class,
738             PROP_SHOW_WEEK_NUMBERS,
739             g_param_spec_boolean ("show-week-numbers",
740                 "Show Week Numbers",
741                 "If TRUE, week numbers are displayed",
742                 FALSE,
743                 GTK_PARAM_READWRITE));
744
745     /**
746      * HildonCalendar:week-start:
747      *
748      * Determines the start day of the week (0 for Sunday, 1 for Monday etc.)
749      *
750      */
751     g_object_class_install_property (gobject_class,
752             PROP_WEEK_START,
753             g_param_spec_int ("week-start",
754                 "Week start day",
755                 "First day of the week; 0 for Sunday, 1 for Monday etc.",
756                 0, 6, 0,
757                 GTK_PARAM_READWRITE));
758
759     /**
760      * HildonCalendar:min-year:
761      *
762      * Minimum valid year (0 if no limit).
763      * 
764      */
765     g_object_class_install_property (gobject_class,
766             PROP_MIN_YEAR,
767             g_param_spec_int ("min-year",
768                 "Minimum valid year",
769                 "Minimum valid year (0 if no limit)",
770                 0, 10000, 0,
771                 GTK_PARAM_READWRITE));
772
773     /**
774      * HildonCalendar:max-year:
775      *
776      * Maximum valid year (0 if no limit).
777      * 
778      */
779     g_object_class_install_property (gobject_class,
780             PROP_MAX_YEAR,
781             g_param_spec_int ("max-year",
782                 "Maximum valid year",
783                 "Maximum valid year (0 if no limit)",
784                 0, 10000, 0,
785                 GTK_PARAM_READWRITE));
786
787     hildon_calendar_signals[MONTH_CHANGED_SIGNAL] =
788         g_signal_new ("month_changed",
789                 G_OBJECT_CLASS_TYPE (gobject_class),
790                 G_SIGNAL_RUN_FIRST,
791                 G_STRUCT_OFFSET (HildonCalendarClass, month_changed),
792                 NULL, NULL,
793                 _hildon_marshal_VOID__VOID,
794                 G_TYPE_NONE, 0);
795     
796     hildon_calendar_signals[DAY_SELECTED_SIGNAL] =
797         g_signal_new ("day_selected",
798                 G_OBJECT_CLASS_TYPE (gobject_class),
799                 G_SIGNAL_RUN_FIRST,
800                 G_STRUCT_OFFSET (HildonCalendarClass, day_selected),
801                 NULL, NULL,
802                 _hildon_marshal_VOID__VOID,
803                 G_TYPE_NONE, 0);
804     
805     hildon_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
806         g_signal_new ("day_selected_double_click",
807                 G_OBJECT_CLASS_TYPE (gobject_class),
808                 G_SIGNAL_RUN_FIRST,
809                 G_STRUCT_OFFSET (HildonCalendarClass, day_selected_double_click),
810                 NULL, NULL,
811                 _hildon_marshal_VOID__VOID,
812                 G_TYPE_NONE, 0);
813     
814     hildon_calendar_signals[PREV_MONTH_SIGNAL] =
815         g_signal_new ("prev_month",
816                 G_OBJECT_CLASS_TYPE (gobject_class),
817                 G_SIGNAL_RUN_FIRST,
818                 G_STRUCT_OFFSET (HildonCalendarClass, prev_month),
819                 NULL, NULL,
820                 _hildon_marshal_VOID__VOID,
821                 G_TYPE_NONE, 0);
822     
823     hildon_calendar_signals[NEXT_MONTH_SIGNAL] =
824         g_signal_new ("next_month",
825                 G_OBJECT_CLASS_TYPE (gobject_class),
826                 G_SIGNAL_RUN_FIRST,
827                 G_STRUCT_OFFSET (HildonCalendarClass, next_month),
828                 NULL, NULL,
829                 _hildon_marshal_VOID__VOID,
830                 G_TYPE_NONE, 0);
831     
832     hildon_calendar_signals[PREV_YEAR_SIGNAL] =
833         g_signal_new ("prev_year",
834                 G_OBJECT_CLASS_TYPE (gobject_class),
835                 G_SIGNAL_RUN_FIRST,
836                 G_STRUCT_OFFSET (HildonCalendarClass, prev_year),
837                 NULL, NULL,
838                 _hildon_marshal_VOID__VOID,
839                 G_TYPE_NONE, 0);
840     
841     hildon_calendar_signals[NEXT_YEAR_SIGNAL] =
842         g_signal_new ("next_year",
843                 G_OBJECT_CLASS_TYPE (gobject_class),
844                 G_SIGNAL_RUN_FIRST,
845                 G_STRUCT_OFFSET (HildonCalendarClass, next_year),
846                 NULL, NULL,
847                 _hildon_marshal_VOID__VOID,
848                 G_TYPE_NONE, 0);
849     
850     /**
851      * HildonCalendar::erroneous-date:
852      *
853      * Emitted when the user tries to set a date which is outside the boundaries 
854      * set by min-year and max-year properties.
855      * 
856      */
857     hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL] =
858         g_signal_new ("erroneous_date",
859                 G_OBJECT_CLASS_TYPE (gobject_class),
860                 G_SIGNAL_RUN_FIRST,
861                 0,
862                 NULL, NULL,
863                 _hildon_marshal_VOID__VOID,
864                 G_TYPE_NONE, 0);
865     /**
866      * HildonCalendar::selected-date:
867      *
868      * Emitted on button-release when the user has selected a date.
869      * 
870      */
871     hildon_calendar_signals[SELECTED_DATE_SIGNAL] =
872         g_signal_new ("selected_date",
873                 G_OBJECT_CLASS_TYPE(gobject_class),
874                 G_SIGNAL_RUN_FIRST,
875                 0,
876                 NULL, NULL,
877                 _hildon_marshal_VOID__VOID,
878                 G_TYPE_NONE, 0);
879 }
880
881 static void
882 hildon_calendar_init                            (HildonCalendar *calendar)
883 {
884     time_t secs;
885     struct tm *tm;
886     gint i;
887     /*  char buffer[255];*/
888     /*  time_t tmp_time;*/
889     GtkWidget *widget;
890     HildonCalendarPrivate *private_data;
891     /*  gchar *year_before;*/
892     /*  gint row;
893         gint col; */
894     gchar *langinfo;
895     GDateWeekday week_1stday;
896     gint first_weekday;
897     guint week_origin;
898
899     widget = GTK_WIDGET (calendar);
900     GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
901
902     calendar->private_data = g_malloc (sizeof (HildonCalendarPrivate));
903     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
904
905     /* Set defaults */
906     secs = time (NULL);
907     tm = localtime (&secs);
908     calendar->month = tm->tm_mon;
909     calendar->year  = 1900 + tm->tm_year;
910
911     for (i=0;i<31;i++)
912         calendar->marked_date[i] = FALSE;
913     calendar->num_marked_dates = 0;
914     calendar->selected_day = tm->tm_mday; 
915
916     calendar->display_flags = ( HILDON_CALENDAR_SHOW_HEADING | 
917             HILDON_CALENDAR_SHOW_DAY_NAMES );
918
919     /* Hildon: we should mark current day  and we need to store current date */
920     private_data->current_day  = tm->tm_mday;
921     private_data->current_month = tm->tm_mon;
922     private_data->current_year = tm->tm_year + 1900;
923
924     /* Hildon: following lines are for stylus sliding */   
925     private_data->slide_stylus = FALSE;
926     private_data->prev_row = -1;
927     private_data->prev_col = -1;
928
929     /* Hildon: is_bad_day indicate if day was selected out of legal range */
930     private_data->is_bad_day = FALSE;
931
932     calendar->highlight_row = -1;
933     calendar->highlight_col = -1; 
934
935     calendar->focus_row = -1;
936     calendar->focus_col = -1; 
937     calendar->xor_gc = NULL;
938
939     private_data->max_year_width = 0;
940     private_data->max_month_width = 0;
941     private_data->max_day_char_width = 0;
942     private_data->max_week_char_width = 0;
943
944     private_data->max_day_char_ascent = 0;
945     private_data->max_day_char_descent = 0;
946     private_data->max_label_char_ascent = 0;
947     private_data->max_label_char_descent = 0;
948
949     /*  private_data->arrow_width = 10;*/
950
951     private_data->freeze_count = 0;
952
953     private_data->dirty_header = 0;
954     private_data->dirty_day_names = 0;
955     private_data->dirty_week = 0;
956     private_data->dirty_main = 0;
957
958     private_data->need_timer = 0;
959     private_data->timer = 0;
960     private_data->click_child = -1;
961
962     private_data->in_drag = 0;
963     private_data->drag_highlight = 0;
964
965     private_data->min_year = 0;
966     private_data->max_year = 0;
967
968     gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
969     gtk_drag_dest_add_text_targets (widget);
970
971 #if 0
972     private_data->year_before = 0;
973
974     /* Translate to calendar:YM if you want years to be displayed
975      * before months; otherwise translate to calendar:MY.
976      * Do *not* translate it to anything else, if it
977      * it isn't calendar:YM or calendar:MY it will not work.
978      *
979      * Note that this flipping is in top the text direction flipping,
980      * so if you have a default text direction of RTL and YM, then
981      * the year will appear on the right.
982      */
983     year_before = _("calendar:MY");
984     if (strcmp (year_before, "calendar:YM") == 0)
985         private_data->year_before = 1;
986     else if (strcmp (year_before, "calendar:MY") != 0)
987         g_warning ("Whoever translated calendar:MY did so wrongly.\n");
988 #endif
989     langinfo = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
990     first_weekday = langinfo[0];
991     langinfo = nl_langinfo (_NL_TIME_WEEK_1STDAY);
992     week_origin = GPOINTER_TO_UINT (langinfo);
993     if (week_origin == 19971130)
994         week_1stday = G_DATE_SUNDAY;
995     else if (week_origin == 19971201)
996         week_1stday = G_DATE_MONDAY;
997     else if (g_date_valid_dmy ((week_origin % 100),
998                 (week_origin / 100) % 100,
999                 (week_origin / 10000)))
1000     {
1001         GDate *date;
1002         date = g_date_new_dmy ((week_origin % 100),
1003                 (week_origin / 100) % 100,
1004                 (week_origin / 10000));
1005         week_1stday = g_date_get_weekday (date);
1006         g_date_free (date);
1007     }
1008     else
1009     {
1010         g_warning ("Invalid value set for _NL_TIME_WEEK_1STDAY"); 
1011         week_1stday = G_DATE_SUNDAY;
1012     }
1013
1014     private_data->week_start = (week_1stday + first_weekday - 1) % 7;
1015
1016     locales_init (private_data);
1017 }
1018
1019 GtkWidget*
1020 hildon_calendar_new                             (void)
1021 {
1022     return g_object_new (HILDON_TYPE_CALENDAR, NULL);
1023 }
1024
1025 /* column_from_x: returns the column 0-6 that the
1026  * x pixel of the xwindow is in */
1027 static gint
1028 column_from_x                                   (HildonCalendar *calendar,
1029                                                  gint event_x)
1030 {
1031     gint c, column;
1032     gint x_left, x_right;
1033
1034     column = -1;
1035
1036     for (c = 0; c < 7; c++)
1037     {
1038         x_left = left_x_for_column (calendar, c);
1039         x_right = x_left + HILDON_CALENDAR_GET_PRIVATE (calendar)->day_width;
1040
1041         if (event_x >= x_left && event_x < x_right)
1042         {
1043             column = c;
1044             break;
1045         }
1046     }
1047
1048     return column;
1049 }
1050 #if 0
1051     static gint
1052 row_height (HildonCalendar *calendar)
1053 {
1054     return (HILDON_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
1055             - ((calendar->display_flags & HILDON_CALENDAR_SHOW_DAY_NAMES)
1056                 ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
1057 }
1058 #endif
1059
1060 /* row_from_y: returns the row 0-5 that the
1061  * y pixel of the xwindow is in */
1062 static gint
1063 row_from_y                                      (HildonCalendar *calendar,
1064                                                  gint event_y)
1065 {
1066     gint r, row;
1067     /*gint height;*/
1068     gint y_top, y_bottom;
1069
1070     row = -1;
1071
1072     for (r = 0; r < 6; r++)
1073     {
1074         y_top = top_y_for_row (calendar, r);
1075         y_bottom = y_top + HILDON_DAY_HEIGHT /*height*/;
1076
1077         if (event_y >= y_top && event_y < y_bottom)
1078         {
1079             row = r;
1080             break;
1081         }
1082     }
1083
1084     return row;
1085 }
1086
1087 /* left_x_for_column: returns the x coordinate
1088   * for the left of the column */
1089 static gint
1090 left_x_for_column                               (HildonCalendar *calendar,
1091                                                  gint column)
1092 {
1093     gint width;
1094     gint x_left;
1095
1096     if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1097         column = 6 - column;
1098
1099     width = HILDON_CALENDAR_GET_PRIVATE (calendar)->day_width;
1100     if (calendar->display_flags & HILDON_CALENDAR_SHOW_WEEK_NUMBERS)
1101         x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
1102     else
1103         x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
1104
1105     return x_left;
1106 }
1107
1108 /* top_y_for_row: returns the y coordinate
1109  * for the top of the row */
1110 static gint
1111 top_y_for_row                                   (HildonCalendar *calendar,
1112                                                  gint row)
1113 {
1114     return (HILDON_CALENDAR_GET_PRIVATE (calendar)->main_h 
1115             - (CALENDAR_MARGIN + (6 - row)
1116                 * HILDON_DAY_HEIGHT));
1117 }
1118
1119 static void
1120 hildon_calendar_set_month_prev                  (HildonCalendar *calendar)
1121 {
1122     HildonCalendarPrivate *priv = HILDON_CALENDAR_GET_PRIVATE (calendar);
1123     gint month_len;
1124
1125     if (calendar->display_flags & HILDON_CALENDAR_NO_MONTH_CHANGE)
1126         return;
1127
1128     if (calendar->month == 0)
1129     {
1130         if (!priv->min_year || calendar->year > priv->min_year)
1131         {
1132             calendar->month = 11;
1133             calendar->year--;
1134         }
1135     }
1136     else
1137         calendar->month--;
1138
1139     month_len = month_length[leap (calendar->year)][calendar->month + 1];
1140
1141     hildon_calendar_freeze (calendar);
1142     hildon_calendar_compute_days (calendar);
1143
1144     g_signal_emit (calendar,
1145             hildon_calendar_signals[PREV_MONTH_SIGNAL],
1146             0);
1147     g_signal_emit (calendar,
1148             hildon_calendar_signals[MONTH_CHANGED_SIGNAL],
1149             0);
1150
1151     if (month_len < calendar->selected_day)
1152     {
1153         calendar->selected_day = 0;
1154         hildon_calendar_select_day (calendar, month_len);
1155     }
1156     else
1157     {
1158         if (calendar->selected_day < 0)
1159             calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
1160         hildon_calendar_select_day (calendar, calendar->selected_day);
1161     }
1162
1163     gtk_widget_queue_draw (GTK_WIDGET (calendar));
1164     hildon_calendar_thaw (calendar);
1165 }
1166
1167 static void
1168 hildon_calendar_set_month_next                  (HildonCalendar *calendar)
1169 {
1170     HildonCalendarPrivate *priv;
1171     gint month_len;
1172
1173     g_return_if_fail (GTK_IS_WIDGET (calendar));
1174
1175     priv = HILDON_CALENDAR_GET_PRIVATE (calendar);
1176
1177     if (calendar->display_flags & HILDON_CALENDAR_NO_MONTH_CHANGE)
1178         return;
1179
1180     if (calendar->month == 11)
1181     {
1182         if (!priv->max_year || calendar->year < priv->max_year)
1183         {
1184             calendar->month = 0;
1185             calendar->year++;
1186         }
1187     } 
1188     else 
1189         calendar->month++;
1190
1191     hildon_calendar_freeze (calendar);
1192     hildon_calendar_compute_days (calendar);
1193     g_signal_emit (calendar,
1194             hildon_calendar_signals[NEXT_MONTH_SIGNAL],
1195             0);
1196     g_signal_emit (calendar,
1197             hildon_calendar_signals[MONTH_CHANGED_SIGNAL],
1198             0);
1199
1200     month_len = month_length[leap (calendar->year)][calendar->month + 1];
1201
1202     if (month_len < calendar->selected_day)
1203     {
1204         calendar->selected_day = 0;
1205         hildon_calendar_select_day (calendar, month_len);
1206     }
1207     else
1208         hildon_calendar_select_day (calendar, calendar->selected_day);
1209
1210     gtk_widget_queue_draw (GTK_WIDGET (calendar));
1211     hildon_calendar_thaw (calendar);
1212 }
1213
1214 static void
1215 hildon_calendar_set_year_prev                   (HildonCalendar *calendar)
1216 {
1217     HildonCalendarPrivate *priv;
1218     gint month_len;
1219
1220     g_return_if_fail (GTK_IS_WIDGET (calendar));
1221
1222     priv = HILDON_CALENDAR_GET_PRIVATE (calendar);
1223
1224     if (!priv->min_year || priv->min_year < calendar->year)
1225         calendar->year--;
1226
1227     hildon_calendar_freeze (calendar);
1228     hildon_calendar_compute_days (calendar);
1229     g_signal_emit (calendar,
1230             hildon_calendar_signals[PREV_YEAR_SIGNAL],
1231             0);
1232     g_signal_emit (calendar,
1233             hildon_calendar_signals[MONTH_CHANGED_SIGNAL],
1234             0);
1235
1236     month_len = month_length[leap (calendar->year)][calendar->month + 1];
1237
1238     if (month_len < calendar->selected_day)
1239     {
1240         calendar->selected_day = 0;
1241         hildon_calendar_select_day (calendar, month_len);
1242     }
1243     else
1244         hildon_calendar_select_day (calendar, calendar->selected_day);
1245
1246     gtk_widget_queue_draw (GTK_WIDGET (calendar));
1247     hildon_calendar_thaw (calendar);
1248 }
1249
1250 static void
1251 hildon_calendar_set_year_next                   (HildonCalendar *calendar)
1252 {
1253     HildonCalendarPrivate *priv;
1254     gint month_len;
1255
1256     g_return_if_fail (GTK_IS_WIDGET (calendar));
1257     priv = HILDON_CALENDAR_GET_PRIVATE (calendar);
1258
1259     hildon_calendar_freeze (calendar);
1260
1261     if (!priv->max_year || priv->max_year > calendar->year)
1262         calendar->year++;
1263
1264     hildon_calendar_compute_days (calendar);
1265     g_signal_emit (calendar,
1266             hildon_calendar_signals[NEXT_YEAR_SIGNAL],
1267             0);
1268     g_signal_emit (calendar,
1269             hildon_calendar_signals[MONTH_CHANGED_SIGNAL],
1270             0);
1271
1272     month_len = month_length[leap (calendar->year)][calendar->month + 1];
1273
1274     if (month_len < calendar->selected_day)
1275     {
1276         calendar->selected_day = 0;
1277         hildon_calendar_select_day (calendar, month_len);
1278     }
1279     else
1280         hildon_calendar_select_day (calendar, calendar->selected_day);
1281     gtk_widget_queue_draw (GTK_WIDGET (calendar));
1282     hildon_calendar_thaw (calendar);
1283 }
1284
1285 static void
1286 hildon_calendar_main_button                     (GtkWidget *widget,
1287                                                  GdkEventButton *event)
1288 {
1289     HildonCalendar *calendar;
1290     HildonCalendarPrivate *private_data;
1291     gint x, y;
1292     gint row, col;
1293     gint day_month;
1294     gint day;
1295
1296     calendar = HILDON_CALENDAR (widget);
1297     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1298
1299     x = (gint) (event->x);
1300     y = (gint) (event->y);
1301
1302     row = row_from_y (calendar, y);
1303     col = column_from_x (calendar, x);
1304
1305     /* If row or column isn't found, just return. */
1306     if (row == -1 || col == -1)
1307         return;
1308
1309     day_month = calendar->day_month[row][col];
1310
1311     if ((calendar->year == private_data->min_year &&
1312                 calendar->month == 0 && day_month == MONTH_PREV) ||
1313             (calendar->year == private_data->max_year &&
1314              calendar->month == 11 && day_month == MONTH_NEXT)) 
1315     {
1316         private_data->is_bad_day = TRUE;
1317         g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
1318         return;
1319     }
1320
1321     if (event->type == GDK_BUTTON_RELEASE)
1322     {
1323         day = calendar->day[row][col];
1324
1325         if (day_month == MONTH_PREV)
1326         {  
1327             hildon_calendar_set_month_prev (calendar);
1328         }
1329         else if (day_month == MONTH_NEXT)
1330         {
1331             hildon_calendar_set_month_next (calendar);
1332         }
1333
1334         if (!GTK_WIDGET_HAS_FOCUS (widget))
1335             gtk_widget_grab_focus (widget);
1336
1337         if (event->button == 1) 
1338         {
1339             private_data->in_drag = 1;
1340             private_data->drag_start_x = x;
1341             private_data->drag_start_y = y;
1342         }
1343
1344         hildon_calendar_select_and_focus_day (calendar, day);
1345     }
1346     else if (event->type == GDK_2BUTTON_PRESS)
1347     {
1348         private_data->in_drag = 0;
1349         private_data->slide_stylus = FALSE;
1350         if (day_month == MONTH_CURRENT)
1351             g_signal_emit (calendar,
1352                     hildon_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL], 0);
1353     }
1354 }
1355
1356 static void
1357 hildon_calendar_realize_arrows                  (GtkWidget *widget)
1358 {
1359     HildonCalendar *calendar;
1360     HildonCalendarPrivate *private_data;
1361     GdkWindowAttr attributes;
1362     gint attributes_mask;
1363     gint i;
1364     guint arrow_vlength, arrow_hlength;
1365     /*gboolean year_left;*/
1366
1367     g_return_if_fail (HILDON_IS_CALENDAR (widget));
1368
1369     calendar = HILDON_CALENDAR (widget);
1370     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1371
1372     gtk_widget_style_get (widget,
1373             "scroll-arrow-hlength", &arrow_hlength,
1374             "scroll-arrow-vlength", &arrow_vlength,
1375             NULL);
1376     /*
1377        if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1378        year_left = private_data->year_before;
1379        else
1380        year_left = !private_data->year_before;
1381        */    
1382     /* Arrow windows ------------------------------------- */
1383     if (! (calendar->display_flags & HILDON_CALENDAR_NO_MONTH_CHANGE)
1384             && (calendar->display_flags & HILDON_CALENDAR_SHOW_HEADING))
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                 | GDK_BUTTON_PRESS_MASK  | GDK_BUTTON_RELEASE_MASK
1392                 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1393         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1394         attributes.y = 0;
1395         attributes.width = arrow_vlength;
1396         attributes.height = arrow_hlength;
1397
1398         attributes.x = (widget->allocation.width - private_data->max_year_width) / 2 - arrow_vlength - HILDON_ARROW_SEP;    
1399         private_data->arrow_win[ARROW_YEAR_LEFT] = gdk_window_new (private_data->header_win,
1400                 &attributes, attributes_mask);
1401
1402         attributes.x = (widget->allocation.width + private_data->max_year_width) / 2 + HILDON_ARROW_SEP;
1403         private_data->arrow_win[ARROW_YEAR_RIGHT] = gdk_window_new (private_data->header_win,
1404                 &attributes, attributes_mask);
1405         attributes.x = (widget->allocation.width - private_data->max_month_width) / 2 - arrow_vlength - HILDON_ARROW_SEP;
1406         private_data->arrow_win[ARROW_MONTH_LEFT] = gdk_window_new (private_data->footer_win,
1407                 &attributes, attributes_mask);
1408         attributes.x = (widget->allocation.width + private_data->max_month_width) / 2 + HILDON_ARROW_SEP;
1409         private_data->arrow_win[ARROW_MONTH_RIGHT] = gdk_window_new (private_data->footer_win,
1410                 &attributes, attributes_mask);
1411
1412         /*
1413            for (i = 0; i < 4; i++)
1414            {
1415            switch (i)
1416            {
1417            case ARROW_MONTH_LEFT:
1418            if (year_left) 
1419            attributes.x = (widget->allocation.width - 2 * widget->style->xthickness
1420            - (3 + 2*private_data->arrow_width 
1421            + private_data->max_month_width));
1422            else
1423            attributes.x = 3;
1424            break;
1425            case ARROW_MONTH_RIGHT:
1426            if (year_left) 
1427            attributes.x = (widget->allocation.width - 2 * widget->style->xthickness 
1428            - 3 - private_data->arrow_width);
1429            else
1430            attributes.x = (private_data->arrow_width 
1431            + private_data->max_month_width);
1432            break;
1433            case ARROW_YEAR_LEFT:
1434            if (year_left) 
1435            attributes.x = 3;
1436            else
1437            attributes.x = (widget->allocation.width - 2 * widget->style->xthickness
1438            - (3 + 2*private_data->arrow_width 
1439            + private_data->max_year_width));
1440            break;
1441            case ARROW_YEAR_RIGHT:
1442            if (year_left) 
1443            attributes.x = (private_data->arrow_width 
1444            + private_data->max_year_width);
1445            else
1446            attributes.x = (widget->allocation.width - 2 * widget->style->xthickness 
1447            - 3 - private_data->arrow_width);
1448            break;
1449            }
1450            private_data->arrow_win[i] = gdk_window_new (private_data->header_win,
1451            &attributes, 
1452            attributes_mask);*/
1453
1454         for (i = 0; i < 4; i++)
1455         {
1456             if (GTK_WIDGET_IS_SENSITIVE (widget))
1457                 private_data->arrow_state[i] = GTK_STATE_NORMAL;
1458             else 
1459                 private_data->arrow_state[i] = GTK_STATE_INSENSITIVE;
1460             gdk_window_set_background (private_data->arrow_win[i],
1461                     HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1462             gdk_window_show (private_data->arrow_win[i]);
1463             gdk_window_set_user_data (private_data->arrow_win[i], widget);
1464         }
1465     }
1466     else
1467     {
1468         for (i = 0; i < 4; i++)
1469             private_data->arrow_win[i] = NULL;
1470     }
1471 }
1472
1473 static void
1474 hildon_calendar_realize_header                  (GtkWidget *widget)
1475 {
1476     HildonCalendar *calendar;
1477     HildonCalendarPrivate *private_data;
1478     GdkWindowAttr attributes;
1479     gint attributes_mask;
1480     guint arrow_hlength;
1481
1482     g_return_if_fail (HILDON_IS_CALENDAR (widget));
1483
1484     calendar = HILDON_CALENDAR (widget);
1485     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1486
1487     gtk_widget_style_get (widget,
1488             "scroll-arrow-hlength", &arrow_hlength,
1489             NULL);
1490     /* Header window ------------------------------------- */
1491     if (calendar->display_flags & HILDON_CALENDAR_SHOW_HEADING)
1492     {
1493         attributes.wclass = GDK_INPUT_OUTPUT;
1494         attributes.window_type = GDK_WINDOW_CHILD;
1495         attributes.visual = gtk_widget_get_visual (widget);
1496         attributes.colormap = gtk_widget_get_colormap (widget);
1497         attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1498         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1499         attributes.x = 0 /*widget->style->xthickness*/;
1500         attributes.y = 0 /*widget->style->ythickness*/;
1501         attributes.width = widget->allocation.width; /* - 2 * attributes.x */;
1502         attributes.height = arrow_hlength /*private_data->header_h - 2 * attributes.y*/;
1503         private_data->header_win = gdk_window_new (widget->window,
1504                 &attributes, attributes_mask);
1505
1506         attributes.y = arrow_hlength + 2 * CALENDAR_YSEP + private_data->main_h + private_data->day_name_h;
1507
1508         private_data->footer_win = gdk_window_new(widget->window, 
1509                 &attributes, attributes_mask);
1510
1511         gdk_window_set_background (private_data->header_win,
1512                 HEADER_BG_COLOR (widget));
1513         gdk_window_set_background (private_data->footer_win,
1514                 HEADER_BG_COLOR (widget));
1515
1516         gdk_window_show (private_data->header_win);
1517         gdk_window_show (private_data->footer_win);
1518         gdk_window_set_user_data (private_data->header_win, widget);
1519         gdk_window_set_user_data (private_data->footer_win, widget);
1520     }
1521     else
1522     {
1523         private_data->header_win = NULL;
1524         private_data->footer_win = NULL;
1525     }
1526     hildon_calendar_realize_arrows (widget);
1527 }
1528
1529 static void
1530 hildon_calendar_realize_day_names               (GtkWidget *widget)
1531 {
1532     HildonCalendar *calendar;
1533     HildonCalendarPrivate *private_data;
1534     GdkWindowAttr attributes;
1535     gint attributes_mask;
1536
1537     g_return_if_fail (HILDON_IS_CALENDAR (widget));
1538
1539     calendar = HILDON_CALENDAR (widget);
1540     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1541
1542     /* Day names  window --------------------------------- */
1543     if ( calendar->display_flags & HILDON_CALENDAR_SHOW_DAY_NAMES)
1544     {
1545         attributes.wclass = GDK_INPUT_OUTPUT;
1546         attributes.window_type = GDK_WINDOW_CHILD;
1547         attributes.visual = gtk_widget_get_visual (widget);
1548         attributes.colormap = gtk_widget_get_colormap (widget);
1549         attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1550         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1551         attributes.x = HILDON_DAY_WIDTH + HILDON_WEEKS_EXTRA_WIDTH/*(widget->style->xthickness + INNER_BORDER)*/;
1552         attributes.y = private_data->header_h;
1553         attributes.width = widget->allocation.width - attributes.x;
1554         attributes.height = private_data->day_name_h;
1555         private_data->day_name_win = gdk_window_new (widget->window,
1556                 &attributes, 
1557                 attributes_mask);
1558         gdk_window_set_background (private_data->day_name_win, 
1559                 BACKGROUND_COLOR ( GTK_WIDGET (calendar)));
1560
1561         gdk_window_show (private_data->day_name_win);
1562         gdk_window_set_user_data (private_data->day_name_win, widget);
1563     }
1564     else
1565     {
1566         private_data->day_name_win = NULL;
1567     }
1568 }
1569
1570 static void
1571 hildon_calendar_realize_week_numbers            (GtkWidget *widget)
1572 {
1573     HildonCalendar *calendar;
1574     HildonCalendarPrivate *private_data;
1575     GdkWindowAttr attributes;
1576     gint attributes_mask;
1577
1578     g_return_if_fail (HILDON_IS_CALENDAR (widget));
1579
1580     calendar = HILDON_CALENDAR (widget);
1581     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1582
1583     /* Week number window -------------------------------- */
1584     if (calendar->display_flags & HILDON_CALENDAR_SHOW_WEEK_NUMBERS)
1585     {
1586         attributes.wclass = GDK_INPUT_OUTPUT;
1587         attributes.window_type = GDK_WINDOW_CHILD;
1588         attributes.visual = gtk_widget_get_visual (widget);
1589         attributes.colormap = gtk_widget_get_colormap (widget);
1590         attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1591
1592         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1593         attributes.x = 0 /*widget->style->xthickness + INNER_BORDER*/;
1594         attributes.y = private_data->header_h; 
1595         /*+ (widget->style->ythickness + INNER_BORDER))*/;
1596         attributes.width = HILDON_DAY_WIDTH + HILDON_WEEKS_EXTRA_WIDTH;
1597         attributes.height = private_data->main_h + private_data->day_name_h;
1598         private_data->week_win = gdk_window_new (widget->window,
1599                 &attributes, attributes_mask);
1600         gdk_window_set_background (private_data->week_win,  
1601                 BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1602         gdk_window_show (private_data->week_win);
1603         gdk_window_set_user_data (private_data->week_win, widget);
1604     } 
1605     else
1606     {
1607         private_data->week_win = NULL;
1608     }
1609 }
1610
1611 static void
1612 hildon_calendar_realize                         (GtkWidget *widget)
1613 {
1614     HildonCalendar *calendar;
1615     HildonCalendarPrivate *private_data;
1616     GdkWindowAttr attributes;
1617     gint attributes_mask;
1618     GdkGCValues values;
1619
1620     calendar = HILDON_CALENDAR (widget);
1621     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1622
1623     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1624     hildon_calendar_compute_days (calendar);
1625
1626     attributes.x = widget->allocation.x;
1627     attributes.y = widget->allocation.y;
1628     attributes.width = widget->allocation.width;
1629     attributes.height = widget->allocation.height;
1630     attributes.wclass = GDK_INPUT_OUTPUT;
1631     attributes.window_type = GDK_WINDOW_CHILD;
1632     attributes.event_mask =  (gtk_widget_get_events (widget) 
1633             | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1634     attributes.visual = gtk_widget_get_visual (widget);
1635     attributes.colormap = gtk_widget_get_colormap (widget);
1636
1637     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1638     widget->window = gdk_window_new (widget->parent->window,
1639             &attributes, attributes_mask);
1640
1641     widget->style = gtk_style_attach (widget->style, widget->window);
1642
1643     /* Header window ------------------------------------- */
1644     hildon_calendar_realize_header (widget);
1645     /* Day names  window --------------------------------- */
1646     hildon_calendar_realize_day_names (widget);
1647     /* Week number window -------------------------------- */
1648     hildon_calendar_realize_week_numbers (widget);
1649     /* Main Window --------------------------------------  */
1650     attributes.event_mask =  (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1651             | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1652             | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1653
1654     attributes.x = HILDON_DAY_WIDTH + HILDON_WEEKS_EXTRA_WIDTH /*private_data->week_width + (widget->style->ythickness + INNER_BORDER)*/;
1655     attributes.y = (private_data->header_h + private_data->day_name_h 
1656             + (widget->style->ythickness + INNER_BORDER));
1657     attributes.width = (widget->allocation.width - attributes.x 
1658             /*- (widget->style->xthickness + INNER_BORDER)*/);
1659     attributes.height = private_data->main_h;
1660     private_data->main_win = gdk_window_new (widget->window,
1661             &attributes, attributes_mask);
1662     gdk_window_set_background (private_data->main_win, 
1663             BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1664     gdk_window_show (private_data->main_win);
1665     gdk_window_set_user_data (private_data->main_win, widget);
1666     gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1667     gdk_window_show (widget->window);
1668     gdk_window_set_user_data (widget->window, widget);
1669
1670     /* Set widgets gc */
1671     calendar->gc = gdk_gc_new (widget->window);
1672
1673     values.foreground = widget->style->white;
1674     values.function = GDK_XOR;
1675     calendar->xor_gc = gdk_gc_new_with_values (widget->window,
1676             &values,
1677             GDK_GC_FOREGROUND |
1678             GDK_GC_FUNCTION);
1679 }
1680
1681 static void
1682 hildon_calendar_unrealize                       (GtkWidget *widget)
1683 {
1684     HildonCalendar *calendar;
1685     HildonCalendarPrivate *private_data;
1686     gint i;
1687
1688     calendar = HILDON_CALENDAR (widget);
1689     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1690
1691     if (private_data->header_win)
1692     {
1693         for (i = 0; i < 4; i++)
1694         {
1695             if (private_data->arrow_win[i])
1696             {
1697                 gdk_window_set_user_data (private_data->arrow_win[i], NULL);
1698                 gdk_window_destroy (private_data->arrow_win[i]);
1699                 private_data->arrow_win[i] = NULL;
1700             }
1701         }
1702         gdk_window_set_user_data (private_data->header_win, NULL);
1703         gdk_window_destroy (private_data->header_win);
1704         private_data->header_win = NULL;
1705         gdk_window_set_user_data (private_data->footer_win, NULL);
1706         gdk_window_destroy (private_data->footer_win);
1707         private_data->footer_win = NULL;  
1708     }
1709
1710     if (private_data->week_win)
1711     {
1712         gdk_window_set_user_data (private_data->week_win, NULL);
1713         gdk_window_destroy (private_data->week_win);
1714         private_data->week_win = NULL;      
1715     }
1716
1717     if (private_data->main_win)
1718     {
1719         gdk_window_set_user_data (private_data->main_win, NULL);
1720         gdk_window_destroy (private_data->main_win);
1721         private_data->main_win = NULL;      
1722     }
1723     if (private_data->day_name_win)
1724     {
1725         gdk_window_set_user_data (private_data->day_name_win, NULL);
1726         gdk_window_destroy (private_data->day_name_win);
1727         private_data->day_name_win = NULL;      
1728     }
1729     if (calendar->xor_gc)
1730         g_object_unref (calendar->xor_gc);
1731     if (calendar->gc)
1732         g_object_unref (calendar->gc);
1733
1734     if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1735         (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1736 }
1737
1738 static void
1739 hildon_calendar_size_request                    (GtkWidget *widget,
1740                                                  GtkRequisition *requisition)
1741 {
1742     HildonCalendar *calendar;
1743     HildonCalendarPrivate *private_data;
1744     PangoLayout *layout;
1745     PangoRectangle logical_rect;
1746
1747     /*gint height;*/
1748     gint i;
1749     gchar buffer[255];
1750     /*gint calendar_margin = CALENDAR_MARGIN;*/
1751     gint header_width, main_width;
1752     gint max_header_height = 0;
1753     gint focus_width;
1754     gint focus_padding;
1755     gint arrow_hlength;
1756
1757     calendar = HILDON_CALENDAR (widget);
1758     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1759     gtk_widget_style_get (GTK_WIDGET (widget),
1760             "focus-line-width", &focus_width,
1761             "focus-padding", &focus_padding,
1762             "scroll-arrow-hlength", &arrow_hlength,
1763             NULL);
1764
1765     layout = gtk_widget_create_pango_layout (widget, NULL);
1766
1767     /*
1768      * Calculate the requisition width for the widget.
1769      */
1770
1771     /* Header width */
1772
1773     if (calendar->display_flags & HILDON_CALENDAR_SHOW_HEADING)
1774     {
1775         private_data->max_month_width = 0;
1776         for (i = 0; i < 12; i++)
1777         {
1778             pango_layout_set_text (layout, private_data->monthname[i], -1);
1779             pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1780             private_data->max_month_width = MAX (private_data->max_month_width,
1781                     logical_rect.width + 8);
1782             max_header_height = MAX (max_header_height, logical_rect.height); 
1783         }
1784         private_data->max_year_width = 0;
1785         for (i=0; i<10; i++)
1786         {
1787             g_snprintf (buffer, sizeof (buffer), "%d%d%d%d", i,i,i,i);
1788             pango_layout_set_text (layout, buffer, -1);     
1789             pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1790             private_data->max_year_width = MAX (private_data->max_year_width,
1791                     logical_rect.width + 8);
1792             max_header_height = MAX (max_header_height, logical_rect.height); 
1793         }
1794     }
1795     else 
1796     {
1797         private_data->max_month_width = 0;
1798         private_data->max_year_width = 0;
1799     }
1800
1801     if (calendar->display_flags & HILDON_CALENDAR_NO_MONTH_CHANGE)
1802         header_width = (private_data->max_month_width 
1803                 + private_data->max_year_width
1804                 + 3 * 3);
1805     else
1806         header_width = (private_data->max_month_width 
1807                 + private_data->max_year_width
1808                 + 4 * private_data->arrow_width + 3 * 3);
1809
1810     /* Mainwindow labels width */
1811
1812     private_data->max_day_char_width = 0;
1813     private_data->min_day_width = 0;
1814     private_data->max_label_char_ascent = 0;
1815     private_data->max_label_char_descent = 0;
1816
1817     for (i = 0; i < 9; i++)
1818     {
1819         g_snprintf (buffer, sizeof (buffer), "%d%d", i, i);
1820         pango_layout_set_text (layout, buffer, -1);         
1821         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1822         private_data->min_day_width = MAX (private_data->min_day_width,
1823                 logical_rect.width);
1824
1825         private_data->max_day_char_ascent = MAX (private_data->max_label_char_ascent,
1826                 PANGO_ASCENT (logical_rect));
1827         private_data->max_day_char_descent = MAX (private_data->max_label_char_descent, 
1828                 PANGO_DESCENT (logical_rect));
1829     }
1830     /* We add one to max_day_char_width to be able to make the marked day "bold" */
1831     private_data->max_day_char_width = private_data->min_day_width / 2 + 1;
1832
1833     if (calendar->display_flags & HILDON_CALENDAR_SHOW_DAY_NAMES)
1834         for (i = 0; i < 7; i++)
1835         {
1836             pango_layout_set_text (layout, private_data->abbreviated_dayname[i], -1);
1837             pango_layout_line_get_pixel_extents (pango_layout_get_lines (layout)->data, NULL, &logical_rect);
1838
1839             /* Hildon: add 4 so that passive focus wouldn't overlap day names */
1840             private_data->min_day_width = MAX (private_data->min_day_width, logical_rect.width + 4);
1841             private_data->max_label_char_ascent = MAX (private_data->max_label_char_ascent,
1842                     PANGO_ASCENT (logical_rect));
1843             private_data->max_label_char_descent = MAX (private_data->max_label_char_descent, 
1844                     PANGO_DESCENT (logical_rect));
1845         }
1846
1847     private_data->max_week_char_width = 0;
1848     if (calendar->display_flags & HILDON_CALENDAR_SHOW_WEEK_NUMBERS)
1849         for (i = 0; i < 9; i++)
1850         {
1851             g_snprintf (buffer, sizeof (buffer), "%d%d", i, i);
1852             pango_layout_set_text (layout, buffer, -1);       
1853             pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1854             private_data->max_week_char_width = MAX (private_data->max_week_char_width,
1855                     logical_rect.width / 2);
1856         }
1857
1858     main_width = (7 * (private_data->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1859             + (private_data->max_week_char_width
1860                 ? private_data->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1861                 : 0));
1862
1863     /* requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1864      *
1865      * FIXME: header_width is broken, when Calendar is themed ! 
1866      *  Next line is workaround for this bug
1867      */
1868     requisition->width = (main_width + INNER_BORDER * 2) + widget->style->xthickness * 2 + HILDON_WEEKS_EXTRA_WIDTH + HILDON_DAYS_EXTRA_WIDTH;
1869
1870     /*
1871      * Calculate the requisition height for the widget.
1872      * This is Hildon calculation
1873      */
1874
1875     if (calendar->display_flags & HILDON_CALENDAR_SHOW_HEADING)
1876         private_data->header_h = arrow_hlength + CALENDAR_YSEP;
1877     else
1878         private_data->header_h = 0;
1879
1880     if (calendar->display_flags & HILDON_CALENDAR_SHOW_DAY_NAMES)
1881         private_data->day_name_h = HILDON_DAY_HEIGHT;
1882     else
1883         private_data->day_name_h = 0;
1884
1885     private_data->main_h = 6 * HILDON_DAY_HEIGHT;
1886     requisition->height = 2 * private_data->header_h + private_data->day_name_h + private_data->main_h;
1887
1888     g_object_unref (layout);
1889 }
1890
1891 static void
1892 hildon_calendar_size_allocate                   (GtkWidget *widget,
1893                                                  GtkAllocation *allocation)
1894 {
1895     HildonCalendar *calendar;
1896     HildonCalendarPrivate *private_data;
1897     gint xthickness = widget->style->xthickness;
1898     /*gint ythickness = widget->style->xthickness;*/
1899     gboolean year_left;
1900     gint arrow_vlength, arrow_hlength;
1901
1902     widget->allocation = *allocation;
1903
1904     calendar = HILDON_CALENDAR (widget);
1905     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
1906
1907     if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1908         year_left = private_data->year_before;
1909     else
1910         year_left = !private_data->year_before;
1911
1912     gtk_widget_style_get (widget,
1913             "scroll-arrow-vlength", &arrow_vlength,
1914             "scroll-arrow-hlength", &arrow_hlength,
1915             NULL);
1916
1917     if (calendar->display_flags & HILDON_CALENDAR_SHOW_WEEK_NUMBERS)
1918     {
1919         /* this variable is introduced to avoid breaking week_width because
1920            of HILDON_WEEKS_EXTRA_WIDTH and HILDON_DAYS_EXTRA_WIDTH appearing
1921            in calculation of day_width */
1922         int real_day_width = (private_data->min_day_width
1923                 * ((allocation->width
1924                         - (xthickness + INNER_BORDER) * 2
1925                         - (CALENDAR_MARGIN * 2) -  (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
1926                 / (7 * private_data->min_day_width + private_data->max_week_char_width * 2));
1927
1928         private_data->day_width = (private_data->min_day_width
1929                 * ((allocation->width
1930                         - (HILDON_WEEKS_EXTRA_WIDTH + HILDON_DAYS_EXTRA_WIDTH)
1931                         - (xthickness + INNER_BORDER) * 2
1932                         - (CALENDAR_MARGIN * 2) -  (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
1933                 / (7 * private_data->min_day_width + private_data->max_week_char_width * 2));
1934         private_data->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
1935                     - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
1936                 - real_day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
1937     }
1938     else 
1939     {
1940         private_data->day_width = (allocation->width
1941                 - (xthickness + INNER_BORDER) * 2
1942                 - (CALENDAR_MARGIN * 2)
1943                 - (DAY_XSEP * 6))/7;
1944         private_data->week_width = 0;
1945     }
1946
1947     if (GTK_WIDGET_REALIZED (widget))
1948     {
1949         gdk_window_move_resize (widget->window,
1950                 allocation->x, allocation->y,
1951                 allocation->width, allocation->height);
1952         if (private_data->header_win)
1953             gdk_window_move_resize (private_data->header_win,
1954                     0, 0, widget->allocation.width, arrow_hlength);
1955         if (private_data->arrow_win[ARROW_YEAR_LEFT])
1956         {
1957             /* if (year_left)
1958                gdk_window_move_resize (private_data->arrow_win[ARROW_YEAR_LEFT],
1959                3, 3,
1960                private_data->arrow_width,
1961                private_data->header_h - 7);
1962                else
1963                gdk_window_move_resize (private_data->arrow_win[ARROW_YEAR_LEFT],
1964                (allocation->width - 2 * xthickness
1965                - (3 + 2*private_data->arrow_width 
1966                + private_data->max_year_width)),
1967                3,
1968                private_data->arrow_width,
1969                private_data->header_h - 7);*/
1970
1971             gdk_window_move (private_data->arrow_win[ARROW_YEAR_LEFT],
1972                     (widget->allocation.width - private_data->max_year_width) / 2 - arrow_vlength - HILDON_ARROW_SEP, 0);
1973         }
1974         if (private_data->arrow_win[ARROW_YEAR_RIGHT])
1975         {
1976             /*        if (year_left)
1977                       gdk_window_move_resize (private_data->arrow_win[ARROW_YEAR_RIGHT],
1978                       (private_data->arrow_width 
1979                       + private_data->max_year_width), 
1980                       3,
1981                       private_data->arrow_width,
1982                       private_data->header_h - 7);
1983                       else
1984                       gdk_window_move_resize (private_data->arrow_win[ARROW_YEAR_RIGHT],
1985                       (allocation->width - 2 * xthickness 
1986                       - 3 - private_data->arrow_width), 
1987                       3,
1988                       private_data->arrow_width,
1989                       private_data->header_h - 7);*/
1990             gdk_window_move (private_data->arrow_win[ARROW_YEAR_RIGHT],
1991                     (widget->allocation.width + private_data->max_year_width) / 2 + HILDON_ARROW_SEP,  0);
1992         }
1993         if (private_data->footer_win)
1994             gdk_window_move_resize (private_data->footer_win,
1995                     0, private_data->header_h + private_data->day_name_h +  private_data->main_h + CALENDAR_YSEP,
1996                     widget->allocation.width, arrow_hlength);
1997
1998         if (private_data->arrow_win[ARROW_MONTH_LEFT])
1999         {
2000             /*        if (year_left)
2001                       gdk_window_move_resize (private_data->arrow_win[ARROW_MONTH_LEFT],
2002                       (allocation->width - 2 * xthickness
2003                       - (3 + 2*private_data->arrow_width 
2004                       + private_data->max_month_width)),
2005                       3,
2006                       private_data->arrow_width,
2007                       private_data->header_h - 7);
2008                       else
2009                       gdk_window_move_resize (private_data->arrow_win[ARROW_MONTH_LEFT],
2010                       3, 3,
2011                       private_data->arrow_width,
2012                       private_data->header_h - 7);
2013                       */
2014
2015             gdk_window_move (private_data->arrow_win[ARROW_MONTH_LEFT],
2016                     (widget->allocation.width - private_data->max_month_width) / 2 - arrow_vlength - HILDON_ARROW_SEP, 0);
2017         }
2018         if (private_data->arrow_win[ARROW_MONTH_RIGHT])
2019         {
2020             /*        if (year_left)
2021                       gdk_window_move_resize (private_data->arrow_win[ARROW_MONTH_RIGHT],
2022                       (allocation->width - 2 * xthickness 
2023                       - 3 - private_data->arrow_width), 
2024                       3,
2025                       private_data->arrow_width,
2026                       private_data->header_h - 7);
2027                       else
2028                       gdk_window_move_resize (private_data->arrow_win[ARROW_MONTH_RIGHT],
2029                       (private_data->arrow_width 
2030                       + private_data->max_month_width), 
2031                       3,
2032                       private_data->arrow_width,
2033                       private_data->header_h - 7);*/
2034             gdk_window_move (private_data->arrow_win[ARROW_MONTH_RIGHT],
2035                     (widget->allocation.width + private_data->max_month_width) / 2 + HILDON_ARROW_SEP, 0); 
2036         }
2037
2038
2039         if (private_data->day_name_win)
2040             gdk_window_move_resize (private_data->day_name_win,
2041                     private_data->week_width, /*xthickness + INNER_BORDER*/
2042                     private_data->header_h /*+ (widget->style->ythickness + INNER_BORDER)*/,
2043                     widget->allocation.width - private_data->week_width /*- (xthickness + INNER_BORDER) * 2*/,
2044                     private_data->day_name_h);
2045         if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
2046         {
2047             if (private_data->week_win)
2048                 gdk_window_move_resize (private_data->week_win,
2049                         0 /*(xthickness + INNER_BORDER)*/,
2050                         private_data->header_h   /*+ (widget->style->ythickness + INNER_BORDER)*/,
2051                         HILDON_DAY_WIDTH,
2052                         private_data->main_h + private_data->day_name_h);
2053             gdk_window_move_resize (private_data->main_win,
2054                     private_data->week_width /* + (xthickness + INNER_BORDER)*/,
2055                     private_data->header_h + private_data->day_name_h
2056                     /*+ (widget->style->ythickness + INNER_BORDER)*/,
2057                     widget->allocation.width - private_data->week_width 
2058                     /*- (xthickness + INNER_BORDER) * 2*/,
2059                     private_data->main_h);
2060         }
2061         else 
2062         {
2063             gdk_window_move_resize (private_data->main_win,
2064                     0 /*(xthickness + INNER_BORDER)*/,
2065                     private_data->header_h + private_data->day_name_h
2066                     /*+ (widget->style->ythickness + INNER_BORDER)*/,
2067                     widget->allocation.width 
2068                     - private_data->week_width 
2069                     /*- (xthickness + INNER_BORDER) * 2*/,
2070                     private_data->main_h);
2071             if (private_data->week_win)
2072                 gdk_window_move_resize (private_data->week_win,
2073                         widget->allocation.width 
2074                         - private_data->week_width 
2075                         /*- (xthickness + INNER_BORDER)*/,
2076                         private_data->header_h + private_data->day_name_h
2077                         /*+ (widget->style->ythickness + INNER_BORDER)*/,
2078                         private_data->week_width,
2079                         private_data->main_h);
2080         }
2081     }
2082 }
2083
2084 static gboolean
2085 hildon_calendar_expose                          (GtkWidget *widget,
2086                                                  GdkEventExpose *event)
2087 {
2088     HildonCalendarPrivate *private_data;
2089
2090     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
2091
2092     if (GTK_WIDGET_DRAWABLE (widget))
2093     {
2094         if (event->window == private_data->main_win)
2095             hildon_calendar_paint_main (widget);
2096
2097         if (event->window == private_data->header_win)
2098             hildon_calendar_paint_header (widget);
2099         if (event->window == private_data->footer_win)
2100             hildon_calendar_paint_footer(widget);
2101
2102         if (event->window == private_data->day_name_win) 
2103             hildon_calendar_paint_day_names (widget);
2104
2105         if (event->window == private_data->week_win) 
2106             hildon_calendar_paint_week_numbers (widget);
2107     }
2108
2109     /* FIXME This appeared after 2.6 -> 2.10 migration. Without this the 
2110      * arrows disappear when spinning through the calendar. Apparently, something 
2111      * overdraws them somehow or the expose events go into blackhole. This is 
2112      * a dirty fix... but kinda works */
2113
2114     hildon_calendar_paint_header (widget);
2115     hildon_calendar_paint_footer (widget);
2116
2117     return FALSE;
2118 }
2119
2120 static void
2121 hildon_calendar_paint_header                    (GtkWidget *widget)
2122 {
2123     HildonCalendar *calendar;
2124     GdkGC *gc;
2125     char buffer[255];
2126     int x, y;
2127     gint header_width, cal_height;
2128     HildonCalendarPrivate *private_data;
2129     PangoLayout *layout;
2130     PangoRectangle logical_rect;
2131     gint arrow_hlength;
2132
2133     calendar = HILDON_CALENDAR (widget);
2134     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
2135
2136     if (private_data->freeze_count)
2137     {
2138         private_data->dirty_header = 1;
2139         return;
2140     }
2141
2142     private_data->dirty_header = 0;
2143     gc = calendar->gc;
2144
2145     /* Clear window */
2146     gdk_window_clear (private_data->header_win);
2147
2148     header_width = widget->allocation.width /*- 2 * widget->style->xthickness*/;
2149     cal_height = widget->allocation.height;
2150
2151     g_snprintf (buffer, sizeof (buffer), "%d", calendar->year);
2152     layout = gtk_widget_create_pango_layout (widget, buffer);
2153     pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2154
2155     gtk_widget_style_get (widget, "scroll-arrow-hlength", &arrow_hlength, NULL);
2156
2157     y = (arrow_hlength - logical_rect.height) / 2;
2158     x = (widget->allocation.width - logical_rect.width) / 2;
2159
2160     /* Draw year and its arrows */
2161     gdk_gc_set_foreground (gc, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2162     gdk_draw_layout (private_data->header_win, gc, x, y, layout);  
2163
2164     hildon_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
2165     hildon_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
2166
2167     g_object_unref (layout);
2168 }
2169
2170 static void
2171 hildon_calendar_paint_footer                    (GtkWidget *widget)
2172 {
2173     HildonCalendar *calendar;
2174     GdkGC *gc;
2175     char buffer[255];
2176     int x, y;
2177     gint header_width, cal_height;
2178     HildonCalendarPrivate *private_data;
2179     PangoLayout *layout;
2180     PangoRectangle logical_rect;
2181     gint arrow_hlength;
2182
2183     calendar = HILDON_CALENDAR (widget);
2184     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
2185
2186     if (private_data->freeze_count)
2187     {
2188         private_data->dirty_header = 1;
2189         return;
2190     }
2191
2192     private_data->dirty_header = 0;
2193     gc = calendar->gc;
2194
2195     /* Clear window */
2196     gdk_window_clear (private_data->footer_win);
2197
2198     header_width = widget->allocation.width - 2 * widget->style->xthickness;
2199     cal_height = widget->allocation.height;
2200
2201     /* Draw month and its arrows */
2202     g_snprintf (buffer, sizeof (buffer), "%s", private_data->monthname[calendar->month]);
2203     layout = gtk_widget_create_pango_layout (widget, buffer);
2204     pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2205
2206     gtk_widget_style_get (widget, "scroll-arrow-hlength", &arrow_hlength, NULL);
2207
2208     x = (widget->allocation.width - logical_rect.width) / 2;
2209     y = (arrow_hlength - logical_rect.height) / 2;
2210
2211     gdk_gc_set_foreground (gc, HEADER_FG_COLOR(GTK_WIDGET (calendar)));
2212     gdk_draw_layout (private_data->footer_win, gc, x, y, layout);
2213
2214     hildon_calendar_paint_arrow (widget, ARROW_MONTH_LEFT);
2215     hildon_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
2216
2217     g_object_unref(layout);
2218 }
2219
2220 static void
2221 hildon_calendar_paint_day_names                 (GtkWidget *widget)
2222 {
2223     HildonCalendar *calendar;
2224     GdkGC *gc;
2225     char buffer[255];
2226     int day,i;
2227     int day_width, cal_width;
2228     gint cal_height;
2229     int day_wid_sep;
2230     PangoLayout *layout;
2231     PangoRectangle logical_rect;
2232     HildonCalendarPrivate *private_data;
2233     gint focus_padding;
2234     gint focus_width;
2235
2236     g_return_if_fail (HILDON_IS_CALENDAR (widget));
2237     calendar = HILDON_CALENDAR (widget);
2238     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
2239     gc = calendar->gc;
2240
2241     gtk_widget_style_get (GTK_WIDGET (widget),
2242             "focus-line-width", &focus_width,
2243             "focus-padding", &focus_padding,
2244             NULL);
2245     /*
2246      * Handle freeze/thaw functionality
2247      */
2248
2249     if (private_data->freeze_count)
2250     {
2251         private_data->dirty_day_names = 1;
2252         return;
2253     }
2254     private_data->dirty_day_names = 0;
2255
2256     /*
2257      * Clear the window
2258      */
2259
2260     gdk_window_clear (private_data->day_name_win);
2261
2262     day_width = private_data->day_width;
2263     cal_width = widget->allocation.width;
2264     cal_height = widget->allocation.height;
2265     day_wid_sep = day_width + DAY_XSEP;
2266
2267     /*
2268      * Draw rectangles as inverted background for the labels.
2269      */
2270
2271     /*
2272      * Write the labels
2273      */
2274
2275     layout = gtk_widget_create_pango_layout (widget, NULL);
2276
2277     gdk_gc_set_foreground (gc, SELECTED_FG_COLOR (widget));
2278     for (i = 0; i < 7; i++)
2279     { 
2280         guint x = left_x_for_column (calendar, i);
2281
2282         if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2283             day = 6 - i;
2284         else
2285             day = i;
2286         day = (day + private_data->week_start) % 7;
2287         g_snprintf (buffer, sizeof (buffer), "%s", private_data->abbreviated_dayname[day]);
2288
2289         pango_layout_set_text (layout, buffer, -1);
2290         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2291
2292         /* Hildon: draw passive focus for day name */
2293         if (calendar->focus_col == i)
2294             gtk_paint_box(GTK_WIDGET (calendar)->style,
2295                     private_data->day_name_win,
2296                     GTK_STATE_NORMAL,
2297                     GTK_SHADOW_OUT, NULL,
2298                     GTK_WIDGET (calendar), "passive-focus",
2299                     x,
2300                     0,
2301                     logical_rect.width + 4,
2302                     HILDON_DAY_HEIGHT);
2303
2304         gdk_draw_layout (private_data->day_name_win, gc,
2305                 x + 2,
2306                 CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y,
2307                 layout);
2308     }
2309     g_object_unref (layout);
2310 }
2311
2312 static void
2313 hildon_calendar_paint_week_numbers              (GtkWidget *widget)
2314 {
2315     HildonCalendar *calendar;
2316     GdkGC *gc; 
2317     guint row, week = 0, year;
2318     gint x_loc;
2319     char buffer[10];
2320     gint y_loc;
2321     HildonCalendarPrivate *private_data;
2322     PangoLayout *layout;
2323     PangoRectangle logical_rect;
2324     gint focus_padding;
2325     gint focus_width;
2326
2327     g_return_if_fail (HILDON_IS_CALENDAR (widget));
2328     g_return_if_fail (widget->window != NULL);
2329     calendar = HILDON_CALENDAR (widget);
2330     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
2331     gc = calendar->gc;
2332
2333     /*
2334      * Handle freeze/thaw functionality
2335      */
2336
2337     if (private_data->freeze_count)
2338     {
2339         private_data->dirty_week = 1;
2340         return;
2341     }
2342     private_data->dirty_week = 0;
2343
2344     gtk_widget_style_get (GTK_WIDGET (widget),
2345             "focus-line-width", &focus_width,
2346             "focus-padding", &focus_padding,
2347             NULL);
2348
2349     /*
2350      * Clear the window
2351      */
2352
2353     gdk_window_clear (private_data->week_win);
2354
2355     /*
2356      * Draw a rectangle as inverted background for the labels.
2357      */
2358
2359     gdk_gc_set_foreground (gc, SELECTED_BG_COLOR (widget));
2360
2361     /* Hildon: don't paint background for weekday window */
2362
2363     /*
2364      * Write the labels
2365      */
2366
2367     layout = gtk_widget_create_pango_layout (widget, NULL);
2368
2369     gdk_gc_set_foreground (gc, SELECTED_FG_COLOR (widget));
2370     gdk_draw_line(private_data->week_win, gc, 
2371             HILDON_DAY_WIDTH + 7,
2372             0,
2373             HILDON_DAY_WIDTH + 7,
2374             private_data->main_h + private_data->day_name_h);
2375
2376     for (row = 0; row < 6; row++)
2377     {
2378         year = calendar->year;
2379         if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2380             year++;
2381
2382         g_return_if_fail (week_of_year (&week, &year,             
2383                     ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2384                      + calendar->month) % 12 + 1, calendar->day[row][6]));
2385
2386         g_snprintf (buffer, sizeof (buffer), "%d", week);
2387         pango_layout_set_text (layout, buffer, -1); 
2388         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2389
2390         /* Hildon: draw passive focus for week */
2391         if (calendar->focus_row == row) 
2392         {
2393             guint y = top_y_for_row (calendar, calendar->focus_row + 1);
2394
2395             gtk_paint_box(GTK_WIDGET (calendar)->style,
2396                     private_data->week_win,
2397                     GTK_STATE_NORMAL,
2398                     GTK_SHADOW_OUT, NULL,
2399                     GTK_WIDGET (calendar), "passive-focus",
2400                     0, y,
2401                     private_data->week_width/* - 4*/,
2402                     HILDON_DAY_HEIGHT);
2403         }
2404
2405         y_loc = private_data->day_name_h + top_y_for_row (calendar, row) + (HILDON_DAY_HEIGHT - logical_rect.height) / 2;
2406         x_loc = (HILDON_DAY_WIDTH - logical_rect.width) / 2;
2407
2408         gdk_draw_layout (private_data->week_win, gc, x_loc, y_loc, layout);
2409     }
2410
2411     g_object_unref (layout);
2412 }
2413
2414 static void
2415 hildon_calendar_paint_day_num                   (GtkWidget *widget,
2416                                                  gint day)
2417 {
2418     HildonCalendar *calendar;
2419     gint r, c, row, col;
2420     HildonCalendarPrivate *private_data;  
2421     g_return_if_fail (HILDON_IS_CALENDAR (widget));
2422
2423     calendar = HILDON_CALENDAR (widget);
2424
2425     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
2426
2427     row = -1;
2428     col = -1;
2429     for (r = 0; r < 6; r++)
2430         for (c = 0; c < 7; c++)
2431             if (calendar->day_month[r][c] == MONTH_CURRENT &&
2432                     calendar->day[r][c] == day)
2433             {
2434                 row = r;
2435                 col = c;
2436             }
2437
2438     g_return_if_fail (row != -1);
2439     g_return_if_fail (col != -1);
2440
2441     hildon_calendar_paint_day (widget, row, col);
2442 }
2443
2444 static void
2445 hildon_calendar_paint_day                       (GtkWidget *widget,
2446                                                  gint row,
2447                                                  gint col)
2448 {
2449     HildonCalendar *calendar;
2450     GdkGC *gc;
2451     gchar buffer[255];
2452     gint day;
2453     gint x_left;
2454     gint x_loc;
2455     gint y_top;
2456     gint y_loc;
2457     gint focus_width;
2458
2459     HildonCalendarPrivate *private_data;
2460     PangoLayout *layout;
2461     PangoRectangle logical_rect;
2462
2463     g_return_if_fail (HILDON_IS_CALENDAR (widget));
2464     g_return_if_fail (row < 6);
2465     g_return_if_fail (col < 7);
2466     calendar = HILDON_CALENDAR (widget);
2467     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
2468
2469     if (private_data->main_win == NULL) return;
2470
2471     /*
2472      * Handle freeze/thaw functionality
2473      */
2474
2475     if (private_data->freeze_count)
2476     {
2477         private_data->dirty_main = 1;
2478         return;
2479     }
2480
2481     gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2482
2483     day = calendar->day[row][col];
2484     x_left = left_x_for_column (calendar, col);
2485     y_top = top_y_for_row (calendar, row);
2486
2487     gdk_window_clear_area (private_data->main_win, x_left, y_top,
2488             HILDON_DAY_WIDTH, HILDON_DAY_HEIGHT);
2489
2490     gc = calendar->gc;
2491
2492     if (calendar->day_month[row][col] == MONTH_PREV)
2493     {
2494         gdk_gc_set_foreground (gc, PREV_MONTH_COLOR (GTK_WIDGET (calendar)));
2495     } 
2496     else if (calendar->day_month[row][col] == MONTH_NEXT)
2497     {
2498         gdk_gc_set_foreground (gc, NEXT_MONTH_COLOR (GTK_WIDGET (calendar)));
2499     } 
2500     else 
2501     {
2502         if (calendar->selected_day == day)
2503         {
2504             /* Hildon: use custom graphics */
2505             gtk_paint_box(GTK_WIDGET (calendar)->style,
2506                     private_data->main_win,
2507                     GTK_STATE_NORMAL,
2508                     GTK_SHADOW_NONE, NULL,
2509                     GTK_WIDGET (calendar), "active-day",
2510                     x_left, y_top,
2511                     HILDON_DAY_WIDTH,
2512                     HILDON_DAY_HEIGHT);
2513         } 
2514         if (calendar->marked_date[day-1])
2515             gdk_gc_set_foreground (gc, MARKED_COLOR    (GTK_WIDGET (calendar)));
2516         else
2517             gdk_gc_set_foreground (gc, NORMAL_DAY_COLOR (GTK_WIDGET (calendar)));
2518         if (calendar->selected_day == day)
2519             gdk_gc_set_foreground (gc, SELECTED_FG_COLOR (GTK_WIDGET (calendar)));
2520         else
2521             gdk_gc_set_foreground (gc, & (GTK_WIDGET (calendar)->style->fg[GTK_WIDGET_STATE (calendar)]));
2522     }
2523
2524     if (GTK_WIDGET_HAS_FOCUS (calendar) &&
2525             calendar->focus_row == row &&
2526             calendar->focus_col == col)
2527     {
2528         GtkStateType state;
2529
2530         if (calendar->selected_day == day)
2531             state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2532         else
2533             state = GTK_STATE_NORMAL;
2534
2535         gtk_paint_focus (widget->style, 
2536                 private_data->main_win,
2537                 (calendar->selected_day == day) 
2538                 ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, 
2539                 NULL, widget, "calendar-day",
2540                 x_left, y_top, 
2541                 HILDON_DAY_WIDTH, 
2542                 HILDON_DAY_HEIGHT);
2543     }
2544
2545     /* Hildon: paint green indicator for current day */
2546     if ((day == private_data->current_day && calendar->selected_day !=
2547                 private_data->current_day) && (calendar->day_month[row][col] == MONTH_CURRENT))
2548         hildon_calendar_check_current_date (calendar, x_left, y_top);
2549
2550     g_snprintf (buffer, sizeof (buffer), "%d", day);
2551     layout = gtk_widget_create_pango_layout (widget, buffer);
2552     pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2553
2554     x_loc = x_left + (HILDON_DAY_WIDTH - logical_rect.width) / 2;
2555     y_loc = y_top + (HILDON_DAY_HEIGHT - logical_rect.height) / 2;
2556
2557     gdk_draw_layout (private_data->main_win, gc,
2558             x_loc, y_loc, layout);
2559     if (calendar->marked_date[day-1] &&
2560             calendar->day_month[row][col] == MONTH_CURRENT)
2561         gdk_draw_layout (private_data->main_win, gc,
2562                 x_loc-1, y_loc, layout);
2563
2564     g_object_unref (layout);
2565 }
2566
2567 static void
2568 hildon_calendar_paint_main                      (GtkWidget *widget)
2569 {
2570     HildonCalendarPrivate *private_data;
2571     gint row, col;
2572
2573     g_return_if_fail (HILDON_IS_CALENDAR (widget));
2574     g_return_if_fail (widget->window != NULL);
2575
2576     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
2577
2578     if (private_data->freeze_count)
2579     {
2580         private_data->dirty_main = 1;
2581         return;
2582     }
2583     private_data->dirty_main = 0;
2584     gdk_window_clear (private_data->main_win);
2585
2586     for (col = 0; col < 7; col++)
2587         for (row = 0; row < 6; row++)
2588             hildon_calendar_paint_day (widget, row, col);
2589 }
2590
2591 static void
2592 hildon_calendar_compute_days                    (HildonCalendar *calendar)
2593 {
2594     HildonCalendarPrivate *private_data;
2595     gint month;
2596     gint year;
2597     gint ndays_in_month;
2598     gint ndays_in_prev_month;
2599     gint first_day;
2600     gint row;
2601     gint col;
2602     gint day;
2603
2604     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
2605
2606     private_data = HILDON_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
2607
2608     year = calendar->year;
2609     month = calendar->month + 1;
2610
2611     ndays_in_month = month_length[leap (year)][month];
2612
2613     first_day = day_of_week (year, month, 1);
2614     first_day = (first_day + 7 - private_data->week_start) % 7;
2615
2616     /* Compute days of previous month */
2617     if (month > 1)
2618         ndays_in_prev_month = month_length[leap (year)][month-1];
2619     else
2620         ndays_in_prev_month = month_length[leap (year)][12];
2621     day = ndays_in_prev_month - first_day + 1;
2622
2623     row = 0;
2624     if (first_day > 0)
2625     {
2626         for (col = 0; col < first_day; col++)
2627         {
2628             calendar->day[row][col] = day;
2629             calendar->day_month[row][col] = MONTH_PREV;
2630             day++;
2631         }
2632     }
2633
2634     /* Compute days of current month */
2635     col = first_day;
2636     for (day = 1; day <= ndays_in_month; day++)
2637     {
2638         calendar->day[row][col] = day;
2639         calendar->day_month[row][col] = MONTH_CURRENT;
2640
2641         col++;
2642         if (col == 7)
2643         {
2644             row++;
2645             col = 0;
2646         }
2647     }
2648
2649     /* Compute days of next month */
2650     day = 1;
2651     for (; row <= 5; row++)
2652     {
2653         for (; col <= 6; col++)
2654         {
2655             calendar->day[row][col] = day;
2656             calendar->day_month[row][col] = MONTH_NEXT;
2657             day++;
2658         }
2659         col = 0;
2660     }
2661 }
2662
2663 /**
2664  * hildon_calendar_get_display_options:
2665  * @calendar: a #HildonCalendar
2666  * 
2667  * Returns the current display options of @calendar. 
2668  * 
2669  * Return value: the display options.
2670  **/
2671 HildonCalendarDisplayOptions 
2672 hildon_calendar_get_display_options             (HildonCalendar *calendar)
2673 {
2674     g_return_val_if_fail (HILDON_IS_CALENDAR (calendar), 0);
2675
2676     return calendar->display_flags;
2677 }
2678
2679 /**
2680  * hildon_calendar_set_display_options:
2681  * @calendar: a #HildonCalendar
2682  * @flags: the display options to set
2683  * 
2684  * Sets display options (whether to display the heading and the month  
2685  * headings).
2686  *
2687  **/
2688 void
2689 hildon_calendar_set_display_options             (HildonCalendar *calendar,
2690                                                  HildonCalendarDisplayOptions flags)
2691 {
2692     HildonCalendarPrivate *private_data;
2693     gint resize = 0;
2694     GtkWidget *widget;
2695     gint i;
2696     HildonCalendarDisplayOptions old_flags;
2697
2698     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
2699
2700     widget = GTK_WIDGET (calendar);
2701     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
2702     old_flags = calendar->display_flags;
2703
2704     if (GTK_WIDGET_REALIZED (widget))
2705     {
2706         if ((flags ^ calendar->display_flags) & HILDON_CALENDAR_NO_MONTH_CHANGE)
2707         {
2708             resize ++;
2709             if (! (flags & HILDON_CALENDAR_NO_MONTH_CHANGE)
2710                     && (private_data->header_win))
2711             {
2712                 calendar->display_flags &= ~HILDON_CALENDAR_NO_MONTH_CHANGE;
2713                 hildon_calendar_realize_arrows (widget);
2714             }
2715             else
2716             {
2717                 for (i = 0; i < 4; i++)
2718                 {
2719                     if (private_data->arrow_win[i])
2720                     {
2721                         gdk_window_set_user_data (private_data->arrow_win[i], 
2722                                 NULL);
2723                         gdk_window_destroy (private_data->arrow_win[i]);
2724                         private_data->arrow_win[i] = NULL;
2725                     }
2726                 }
2727             }
2728         }
2729
2730         if ((flags ^ calendar->display_flags) & HILDON_CALENDAR_SHOW_HEADING)
2731         {
2732             resize++;
2733
2734             if (flags & HILDON_CALENDAR_SHOW_HEADING)
2735             {
2736                 calendar->display_flags |= HILDON_CALENDAR_SHOW_HEADING;
2737                 hildon_calendar_realize_header (widget);
2738             }
2739             else
2740             {
2741                 for (i = 0; i < 4; i++)
2742                 {
2743                     if (private_data->arrow_win[i])
2744                     {
2745                         gdk_window_set_user_data (private_data->arrow_win[i], 
2746                                 NULL);
2747                         gdk_window_destroy (private_data->arrow_win[i]);
2748                         private_data->arrow_win[i] = NULL;
2749                     }
2750                 }
2751                 gdk_window_set_user_data (private_data->header_win, NULL);
2752                 gdk_window_destroy (private_data->header_win);
2753                 private_data->header_win = NULL;
2754             }
2755         }
2756
2757
2758         if ((flags ^ calendar->display_flags) & HILDON_CALENDAR_SHOW_DAY_NAMES)
2759         {
2760             resize++;
2761
2762             if (flags & HILDON_CALENDAR_SHOW_DAY_NAMES)
2763             {
2764                 calendar->display_flags |= HILDON_CALENDAR_SHOW_DAY_NAMES;
2765                 hildon_calendar_realize_day_names (widget);
2766             }
2767             else
2768             {
2769                 gdk_window_set_user_data (private_data->day_name_win, NULL);
2770                 gdk_window_destroy (private_data->day_name_win);
2771                 private_data->day_name_win = NULL;
2772             }
2773         }
2774
2775         if ((flags ^ calendar->display_flags) & HILDON_CALENDAR_SHOW_WEEK_NUMBERS)
2776         {
2777             resize++;
2778
2779             if (flags & HILDON_CALENDAR_SHOW_WEEK_NUMBERS)
2780             {
2781                 calendar->display_flags |= HILDON_CALENDAR_SHOW_WEEK_NUMBERS;
2782                 hildon_calendar_realize_week_numbers (widget);
2783             }
2784             else
2785             {
2786                 gdk_window_set_user_data (private_data->week_win, NULL);
2787                 gdk_window_destroy (private_data->week_win);
2788                 private_data->week_win = NULL;
2789             }
2790         }
2791
2792         if ((flags ^ calendar->display_flags) & HILDON_CALENDAR_WEEK_START_MONDAY)
2793             g_warning ("HILDON_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
2794
2795         calendar->display_flags = flags;
2796         if (resize)
2797             gtk_widget_queue_resize (GTK_WIDGET (calendar));
2798
2799     } 
2800     else
2801         calendar->display_flags = flags;
2802
2803     g_object_freeze_notify (G_OBJECT (calendar));
2804     if ((old_flags ^ calendar->display_flags) & HILDON_CALENDAR_SHOW_HEADING)
2805         g_object_notify (G_OBJECT (calendar), "show-heading");
2806     if ((old_flags ^ calendar->display_flags) & HILDON_CALENDAR_SHOW_DAY_NAMES)
2807         g_object_notify (G_OBJECT (calendar), "show-day-names");
2808     if ((old_flags ^ calendar->display_flags) & HILDON_CALENDAR_NO_MONTH_CHANGE)
2809         g_object_notify (G_OBJECT (calendar), "no-month-change");
2810     if ((old_flags ^ calendar->display_flags) & HILDON_CALENDAR_SHOW_WEEK_NUMBERS)
2811         g_object_notify (G_OBJECT (calendar), "show-week-numbers");
2812     g_object_thaw_notify (G_OBJECT (calendar));
2813 }
2814
2815 gboolean
2816 hildon_calendar_select_month                    (HildonCalendar *calendar,
2817                                                  guint month,
2818                                                  guint year)
2819 {
2820     HildonCalendarPrivate *priv;
2821
2822     g_return_val_if_fail (HILDON_IS_CALENDAR (calendar), FALSE);
2823     g_return_val_if_fail (month <= 11, FALSE);
2824
2825     priv = HILDON_CALENDAR_GET_PRIVATE (calendar);
2826
2827     if (priv->max_year && year > priv->max_year)
2828         year = priv->max_year;
2829     if (priv->min_year && year < priv->min_year)
2830         year = priv->min_year;
2831
2832     calendar->month = month;
2833     calendar->year  = year;
2834
2835     hildon_calendar_compute_days (calendar);
2836
2837     gtk_widget_queue_draw (GTK_WIDGET (calendar));
2838
2839     g_object_freeze_notify (G_OBJECT (calendar));
2840     g_object_notify (G_OBJECT (calendar), "month");
2841     g_object_notify (G_OBJECT (calendar), "year");
2842     g_object_thaw_notify (G_OBJECT (calendar));
2843
2844     g_signal_emit (calendar,
2845             hildon_calendar_signals[MONTH_CHANGED_SIGNAL],
2846             0);
2847     return TRUE;
2848 }
2849
2850 void
2851 hildon_calendar_select_day                      (HildonCalendar *calendar,
2852                                                  guint day)
2853 {
2854     gint row, col;
2855     HildonCalendarPrivate *priv;
2856     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
2857     g_return_if_fail (day <= 31);
2858     priv = HILDON_CALENDAR_GET_PRIVATE (calendar);
2859
2860     for (row = 0; row < 6; row ++)
2861         for (col = 0; col < 7; col++)
2862         {
2863             if (calendar->day_month[row][col] == MONTH_CURRENT
2864                     && calendar->day[row][col] == day)
2865             {
2866                 calendar->focus_row = row;
2867                 calendar->focus_col = col;
2868             }
2869         }
2870
2871     if (calendar->month != priv->current_month || 
2872             calendar->year != priv->current_year)
2873         hildon_calendar_unmark_day (calendar, priv->current_day);
2874
2875     /* Deselect the old day */
2876     if (calendar->selected_day > 0)
2877     {
2878         gint selected_day;
2879
2880         selected_day = calendar->selected_day;
2881         calendar->selected_day = 0;
2882         if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
2883         {
2884             hildon_calendar_paint_day_num (GTK_WIDGET (calendar), selected_day);
2885         }
2886     }
2887
2888     calendar->selected_day = day;
2889
2890     /*printf("Selected day = %d\n", day);*/
2891
2892     /* Select the new day */
2893     if (day != 0)
2894     {
2895         if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
2896         {
2897             hildon_calendar_paint_day_num (GTK_WIDGET (calendar), day);
2898         }
2899     }
2900
2901     g_object_notify (G_OBJECT (calendar), "day");
2902     g_signal_emit (calendar,
2903             hildon_calendar_signals[DAY_SELECTED_SIGNAL],
2904             0);
2905 }
2906
2907 static void
2908 hildon_calendar_select_and_focus_day            (HildonCalendar *calendar,
2909                                                  guint day)
2910 {
2911     gint old_focus_row = calendar->focus_row;
2912     gint old_focus_col = calendar->focus_col;
2913     gint row;
2914     gint col;
2915
2916     for (row = 0; row < 6; row ++)
2917         for (col = 0; col < 7; col++)
2918         {
2919             if (calendar->day_month[row][col] == MONTH_CURRENT 
2920                     && calendar->day[row][col] == day)
2921             {
2922                 calendar->focus_row = row;
2923                 calendar->focus_col = col;
2924             }
2925         }
2926
2927     if (old_focus_row != -1 && old_focus_col != -1)
2928         hildon_calendar_paint_day (GTK_WIDGET (calendar), old_focus_row, old_focus_col);
2929
2930     hildon_calendar_select_day (calendar, day);
2931 }
2932
2933 void
2934 hildon_calendar_clear_marks                     (HildonCalendar *calendar)
2935 {
2936     guint day;
2937
2938     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
2939
2940     for (day = 0; day < 31; day++)
2941     {
2942         calendar->marked_date[day] = FALSE;
2943     }
2944
2945     calendar->num_marked_dates = 0;
2946
2947     if (GTK_WIDGET_DRAWABLE (calendar))
2948     {
2949         hildon_calendar_paint_main (GTK_WIDGET (calendar));
2950     }
2951 }
2952
2953 gboolean
2954 hildon_calendar_mark_day                        (HildonCalendar *calendar,
2955                                                  guint day)
2956 {
2957     g_return_val_if_fail (HILDON_IS_CALENDAR (calendar), FALSE);
2958     if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
2959     {
2960         calendar->marked_date[day - 1] = TRUE;
2961         calendar->num_marked_dates++;
2962
2963         if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
2964             hildon_calendar_paint_day_num (GTK_WIDGET (calendar), day-1);
2965     }
2966
2967     return TRUE;
2968 }
2969
2970 gboolean
2971 hildon_calendar_unmark_day                      (HildonCalendar *calendar,
2972                                                  guint day)
2973 {
2974     g_return_val_if_fail (HILDON_IS_CALENDAR (calendar), FALSE);
2975
2976     if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
2977     {
2978         calendar->marked_date[day - 1] = FALSE;
2979         calendar->num_marked_dates--;
2980
2981         if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
2982             hildon_calendar_paint_day_num (GTK_WIDGET (calendar), day-1);
2983     }
2984
2985     return TRUE;
2986 }
2987
2988 void
2989 hildon_calendar_get_date                        (HildonCalendar *calendar,
2990                                                  guint *year,
2991                                                  guint *month,
2992                                                  guint *day)
2993 {
2994     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
2995
2996     if (year)
2997         *year = calendar->year;
2998
2999     if (month)
3000         *month = calendar->month;
3001
3002     if (day)
3003         *day = calendar->selected_day;
3004 }
3005
3006 static void
3007 arrow_action                                    (HildonCalendar *calendar,
3008                                                  guint arrow)
3009 {
3010     switch (arrow)
3011     {
3012         case ARROW_YEAR_LEFT:
3013             hildon_calendar_set_year_prev (calendar);
3014             break;
3015         case ARROW_YEAR_RIGHT:
3016             hildon_calendar_set_year_next (calendar);
3017             break;
3018         case ARROW_MONTH_LEFT:
3019             hildon_calendar_set_month_prev (calendar);
3020             break;
3021         case ARROW_MONTH_RIGHT:
3022             hildon_calendar_set_month_next (calendar);
3023             break;
3024         default:;
3025                 /* do nothing */
3026     }
3027
3028     hildon_calendar_select_and_focus_day(calendar, calendar->selected_day); 
3029 }
3030
3031 static gboolean
3032 calendar_timer                                  (gpointer data)
3033 {
3034     HildonCalendar *calendar = data;
3035     HildonCalendarPrivate *private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
3036     gboolean retval = FALSE;
3037     GtkSettings *settings;
3038     guint timeout;
3039
3040     gdk_window_process_updates (((GtkWidget *) calendar)->window, TRUE);
3041
3042     settings = gtk_settings_get_default ();
3043     g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3044     timeout *= 8;
3045
3046     if (private_data->timer)
3047     {
3048         arrow_action (calendar, private_data->click_child);
3049
3050         if (private_data->need_timer)
3051         {
3052             private_data->need_timer = FALSE;
3053             private_data->timer = gdk_threads_add_timeout (/*CALENDAR_TIMER_DELAY*/timeout,
3054                     (GSourceFunc) calendar_timer, 
3055                     (gpointer) calendar);
3056         }
3057         else 
3058             retval = TRUE;
3059     }
3060
3061     return retval;
3062 }
3063
3064 static void
3065 start_spinning                                  (GtkWidget *widget,
3066                                                  gint click_child)
3067 {
3068     HildonCalendarPrivate *private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3069     GtkSettings *settings;
3070     guint timeout;
3071
3072     settings = gtk_settings_get_default ();
3073     g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3074     timeout *= 8;
3075
3076     private_data->click_child = click_child;
3077
3078     if (!private_data->timer)
3079     {
3080         private_data->need_timer = TRUE;
3081         private_data->timer = gdk_threads_add_timeout (/*CALENDAR_INITIAL_TIMER_DELAY*/timeout,
3082                 calendar_timer,
3083                 (gpointer) widget);
3084     }
3085 }
3086
3087 static void
3088 stop_spinning                                   (GtkWidget *widget)
3089 {
3090     HildonCalendarPrivate *private_data;
3091
3092     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3093
3094     if (private_data->timer)
3095     {
3096         g_source_remove (private_data->timer);
3097         private_data->timer = 0;
3098         private_data->need_timer = FALSE;
3099     }
3100 }
3101
3102 static void
3103 hildon_calendar_destroy                         (GtkObject *object)
3104 {
3105     stop_spinning (GTK_WIDGET (object));
3106
3107     GTK_OBJECT_CLASS (parent_class)->destroy (object);
3108 }
3109
3110 static void
3111 hildon_calendar_grab_notify                     (GtkWidget *widget,
3112                                                  gboolean   was_grabbed)
3113 {
3114     if (!was_grabbed)
3115         stop_spinning (widget);
3116 }
3117
3118 static gboolean
3119 hildon_calendar_focus_out                       (GtkWidget *widget,
3120                                                  GdkEventFocus *event)
3121 {
3122     HildonCalendarPrivate *private_data;
3123
3124     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3125
3126     stop_spinning (widget);
3127
3128     private_data->in_drag = 0; 
3129
3130     return FALSE;
3131 }
3132
3133 static gboolean
3134 hildon_calendar_button_press                    (GtkWidget *widget,
3135                                                  GdkEventButton *event)
3136 {
3137     HildonCalendar *calendar;
3138     HildonCalendarPrivate *private_data;
3139     gint arrow = -1;
3140
3141     calendar = HILDON_CALENDAR (widget);
3142     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3143
3144
3145     if (event->type == GDK_2BUTTON_PRESS)
3146     {
3147         if (event->window == private_data->main_win)
3148             hildon_calendar_main_button (widget, event);
3149     }
3150     else if (event->window == private_data->main_win)
3151     {
3152         gint x = (gint) (event->x);
3153         gint y = (gint) (event->y);
3154         gint row = row_from_y (calendar, y);
3155         gint col = column_from_x (calendar, x);
3156         private_data->pressed_day = calendar->day[row][col];
3157
3158         if ((calendar->year == private_data->min_year &&
3159                     calendar->month == 0
3160                     && calendar->day_month[row][col] == MONTH_PREV) ||
3161                 (calendar->year == private_data->max_year &&
3162                  calendar->month == 11 &&
3163                  calendar->day_month[row][col] == MONTH_NEXT))
3164         {}
3165         else if (calendar->day_month[row][col] == MONTH_CURRENT)
3166             hildon_calendar_select_and_focus_day (calendar, private_data->pressed_day);
3167
3168         /* Remember month where button was pressed */
3169         private_data->pressed_month = calendar->month;
3170         private_data->slide_stylus = TRUE;
3171     } 
3172
3173     if (!GTK_WIDGET_HAS_FOCUS (widget))
3174         gtk_widget_grab_focus (widget);
3175
3176     for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
3177     {
3178         if (event->window == private_data->arrow_win[arrow])
3179         {
3180
3181             /* only call the action on single click, not double */
3182             if (event->type == GDK_BUTTON_PRESS)
3183             {
3184                 if (event->button == 1)
3185                     start_spinning (widget, arrow);
3186
3187                 arrow_action (calendar, arrow);         
3188             }
3189
3190             return TRUE;
3191         }
3192     }
3193
3194     return TRUE;
3195 }
3196
3197 static gboolean
3198 hildon_calendar_button_release                  (GtkWidget *widget,
3199                                                  GdkEventButton *event)
3200 {
3201     HildonCalendar *calendar;
3202     HildonCalendarPrivate *private_data;
3203
3204     calendar = HILDON_CALENDAR (widget);
3205     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3206
3207     if (event->window == private_data->main_win)
3208     {
3209         hildon_calendar_main_button (widget, event);
3210         gint x = (gint) (event->x);
3211         gint y = (gint) (event->y);
3212         gint row = row_from_y (calendar, y);
3213         gint col = column_from_x (calendar, x);
3214         private_data->prev_col = -1;
3215         private_data->prev_row = -1;
3216
3217         if ((private_data->pressed_day == calendar->day[row][col]) &&
3218                 (private_data->pressed_month == calendar->month))
3219         {
3220             if (!private_data->is_bad_day)
3221             {
3222                 g_signal_emit (calendar, hildon_calendar_signals[SELECTED_DATE_SIGNAL], 0);
3223             }
3224             else
3225             {
3226                 private_data->is_bad_day = FALSE;
3227             }
3228         }
3229     }
3230
3231     if (event->button == 1) 
3232     {
3233         stop_spinning (widget);
3234
3235         if (private_data->in_drag)
3236             private_data->in_drag = 0;
3237     }
3238
3239     private_data->slide_stylus = FALSE;
3240     return TRUE;
3241 }
3242
3243 static gboolean
3244 hildon_calendar_motion_notify                   (GtkWidget *widget,
3245                                                  GdkEventMotion *event)
3246 {
3247     HildonCalendar *calendar;
3248     HildonCalendarPrivate *private_data;
3249     gint event_x, event_y;
3250     gint row, col;
3251     gint old_row, old_col;
3252
3253     calendar = HILDON_CALENDAR (widget);
3254     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3255     event_x = (gint) (event->x);
3256     event_y = (gint) (event->y);
3257
3258     if (event->window == private_data->main_win)
3259     {
3260         /* Hildon: make active day to move, when stylus is slided */
3261         if (private_data->slide_stylus)
3262         {
3263             gint c_row = row_from_y (calendar, event_y);
3264             gint c_col = column_from_x (calendar, event_x);
3265
3266             if (calendar->day_month[c_row][c_col] == MONTH_PREV ||
3267                     calendar->day_month[c_row][c_col] == MONTH_NEXT)
3268             { }
3269             else if ((private_data->prev_row != c_row || private_data->prev_col != c_col) &&
3270                     (calendar->highlight_row != -1 && calendar->highlight_col != -1))
3271             {
3272                 hildon_calendar_select_and_focus_day (calendar, 
3273                         calendar->day[c_row][c_col]);
3274                 /* Update passive focus indicators work weekday number and name */
3275                 hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3276                 hildon_calendar_paint_day_names (GTK_WIDGET (calendar));
3277             }
3278             private_data->prev_col = c_col;
3279             private_data->prev_row = c_row;    
3280         }
3281         if (private_data->in_drag) 
3282         {
3283             if (gtk_drag_check_threshold (widget,
3284                         private_data->drag_start_x, private_data->drag_start_y,
3285                         event->x, event->y))
3286             {
3287                 GdkDragContext *context;
3288                 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
3289                 gtk_target_list_add_text_targets (target_list, 0);
3290                 context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
3291                         1, (GdkEvent *)event);
3292
3293
3294                 private_data->in_drag = 0;
3295
3296                 gtk_target_list_unref (target_list);
3297                 gtk_drag_set_icon_default (context);
3298             }
3299         }
3300         else 
3301         {
3302             row = row_from_y (calendar, event_y);
3303             col = column_from_x (calendar, event_x);
3304
3305             if (row != calendar->highlight_row || calendar->highlight_col != col)
3306             {
3307                 old_row = calendar->highlight_row;
3308                 old_col = calendar->highlight_col;
3309                 if (old_row > -1 && old_col > -1)
3310                 {
3311                     calendar->highlight_row = -1;
3312                     calendar->highlight_col = -1;
3313                     hildon_calendar_paint_day (widget, old_row, old_col);
3314                 }
3315
3316                 calendar->highlight_row = row;
3317                 calendar->highlight_col = col;
3318
3319                 if (row > -1 && col > -1)
3320                     hildon_calendar_paint_day (widget, row, col);
3321             }
3322         }
3323     }
3324
3325     return TRUE;
3326 }
3327
3328 static gboolean
3329 hildon_calendar_enter_notify                    (GtkWidget *widget,
3330                                                  GdkEventCrossing *event)
3331 {
3332     HildonCalendarPrivate *private_data;
3333
3334     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3335
3336     if (event->window == private_data->arrow_win[ARROW_MONTH_LEFT])
3337     {
3338         private_data->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
3339         hildon_calendar_paint_arrow (widget, ARROW_MONTH_LEFT);
3340     }
3341
3342     if (event->window == private_data->arrow_win[ARROW_MONTH_RIGHT])
3343     {
3344         private_data->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
3345         hildon_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
3346     }
3347
3348     if (event->window == private_data->arrow_win[ARROW_YEAR_LEFT])
3349     {
3350         private_data->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
3351         hildon_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
3352     }
3353
3354     if (event->window == private_data->arrow_win[ARROW_YEAR_RIGHT])
3355     {
3356         private_data->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
3357         hildon_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
3358     }
3359
3360     return TRUE;
3361 }
3362
3363 static gboolean
3364 hildon_calendar_leave_notify                    (GtkWidget *widget,
3365                                                  GdkEventCrossing *event)
3366 {
3367     HildonCalendar *calendar;
3368     HildonCalendarPrivate *private_data;
3369     gint row;
3370     gint col;
3371
3372     calendar = HILDON_CALENDAR (widget);
3373     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3374
3375     if (event->window == private_data->main_win)
3376     {
3377         row = calendar->highlight_row;
3378         col = calendar->highlight_col;
3379         calendar->highlight_row = -1;
3380         calendar->highlight_col = -1;
3381         if (row > -1 && col > -1)
3382             hildon_calendar_paint_day (widget, row, col);
3383     }
3384
3385     if (event->window == private_data->arrow_win[ARROW_MONTH_LEFT])
3386     {
3387         private_data->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3388         hildon_calendar_paint_arrow (widget, ARROW_MONTH_LEFT);
3389     }
3390
3391     if (event->window == private_data->arrow_win[ARROW_MONTH_RIGHT])
3392     {
3393         private_data->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3394         hildon_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
3395     }
3396
3397     if (event->window == private_data->arrow_win[ARROW_YEAR_LEFT])
3398     {
3399         private_data->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3400         hildon_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
3401     }
3402
3403     if (event->window == private_data->arrow_win[ARROW_YEAR_RIGHT])
3404     {
3405         private_data->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3406         hildon_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
3407     }
3408
3409     return TRUE;
3410 }
3411
3412 static void
3413 hildon_calendar_paint_arrow                     (GtkWidget *widget,
3414                                                  guint      arrow)
3415 {
3416     HildonCalendarPrivate *private_data;
3417     GdkWindow *window;
3418     GdkGC *gc;
3419     HildonCalendar *calendar;
3420     gint state;
3421     guint arrow_hlength, arrow_vlength;
3422     /*  gint width, height;*/
3423
3424     calendar = HILDON_CALENDAR (widget);
3425     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3426     gtk_widget_style_get (widget,
3427             "scroll-arrow-hlength", &arrow_hlength,
3428             "scroll-arrow-vlength", &arrow_vlength,
3429             NULL);
3430
3431     if (private_data->freeze_count)
3432     {
3433         private_data->dirty_header = 1;
3434         return;
3435     }
3436     window = private_data->arrow_win[arrow];
3437     if (window)
3438     {
3439         state = private_data->arrow_state[arrow];
3440         gc = calendar->gc;
3441
3442         /*      gdk_window_clear (window);*/
3443         gdk_window_set_background (window, &(widget)->style->bg[state]);
3444         /*      gdk_drawable_get_size (window, &width, &height);*/
3445         /*      gdk_window_clear_area (window,
3446                 0,0,
3447                 width,height);*/
3448
3449         gdk_window_clear(window);
3450
3451         /* Hildon: added support for dimmed arrows */
3452         if (((private_data->min_year && calendar->year <= private_data->min_year) ||
3453              (private_data->max_year && calendar->year >= private_data->max_year)))
3454         {
3455             if (private_data->min_year &&
3456                     calendar->year <= private_data->min_year)
3457             {
3458                 if (arrow == ARROW_YEAR_LEFT)
3459                     gtk_paint_arrow (widget->style, window, GTK_STATE_INSENSITIVE,
3460                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3461                             GTK_ARROW_LEFT, TRUE,
3462                             0, 0, arrow_vlength, arrow_hlength);
3463                 else if (arrow == ARROW_YEAR_RIGHT || arrow == ARROW_MONTH_RIGHT)
3464                     gtk_paint_arrow (widget->style, window, state,
3465                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3466                             GTK_ARROW_RIGHT, TRUE, 
3467                             0, 0, arrow_vlength, arrow_hlength);
3468                 else if (arrow == ARROW_MONTH_LEFT && calendar->month != 0)
3469                     gtk_paint_arrow (widget->style, window, state,
3470                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3471                             GTK_ARROW_LEFT, TRUE,
3472                             0, 0, arrow_vlength, arrow_hlength);
3473                 else if (arrow == ARROW_MONTH_LEFT && !calendar->month)
3474                     gtk_paint_arrow (widget->style, window, GTK_STATE_INSENSITIVE,
3475                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3476                             GTK_ARROW_LEFT, TRUE,
3477                             0, 0, arrow_vlength, arrow_hlength);
3478             }
3479             else if (private_data->max_year &&
3480                     calendar->year >= private_data->max_year)
3481             {
3482                 if (arrow == ARROW_YEAR_RIGHT)
3483                     gtk_paint_arrow (widget->style, window, GTK_STATE_INSENSITIVE, 
3484                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3485                             GTK_ARROW_RIGHT, TRUE, 
3486                             0, 0, arrow_vlength, arrow_hlength);
3487                 else if (arrow == ARROW_YEAR_LEFT || arrow == ARROW_MONTH_LEFT)
3488                     gtk_paint_arrow (widget->style, window, state, 
3489                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3490                             GTK_ARROW_LEFT, TRUE, 
3491                             0, 0, arrow_vlength, arrow_hlength);
3492                 else if (arrow == ARROW_MONTH_RIGHT && calendar->month != 11)
3493                     gtk_paint_arrow (widget->style, window, state,
3494                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3495                             GTK_ARROW_RIGHT, TRUE,
3496                             0, 0, arrow_vlength, arrow_hlength);
3497                 else if (arrow == ARROW_MONTH_RIGHT && calendar->month == 11)
3498                     gtk_paint_arrow (widget->style, window, GTK_STATE_INSENSITIVE,
3499                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3500                             GTK_ARROW_RIGHT, TRUE,
3501                             0, 0, arrow_vlength, arrow_hlength);
3502             }
3503         }
3504         else
3505         { 
3506             if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
3507                 gtk_paint_arrow (widget->style, window, state, 
3508                         GTK_SHADOW_OUT, NULL, widget, "calendar",
3509                         GTK_ARROW_LEFT, TRUE, 
3510                         /*                     width/2 - 3, height/2 - 4, 8, 8);*/
3511                                 0, 0, arrow_vlength, arrow_hlength);
3512             else 
3513                 gtk_paint_arrow (widget->style, window, state,
3514                         GTK_SHADOW_OUT, NULL, widget, "calendar",
3515                         GTK_ARROW_RIGHT, TRUE, 
3516                         /*                     width/2 - 2, height/2 - 4, 8, 8);*/
3517                                 0, 0, arrow_vlength, arrow_hlength);
3518         }
3519     }
3520 }
3521
3522 void
3523 hildon_calendar_freeze                          (HildonCalendar *calendar)
3524 {
3525     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
3526
3527     HILDON_CALENDAR_GET_PRIVATE (calendar)->freeze_count++;
3528 }
3529
3530 void
3531 hildon_calendar_thaw                            (HildonCalendar *calendar)
3532 {
3533     HildonCalendarPrivate *private_data;
3534
3535     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
3536
3537     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
3538
3539     if (private_data->freeze_count)
3540         if (!(--private_data->freeze_count))
3541         {
3542             if (private_data->dirty_header)
3543                 if (GTK_WIDGET_DRAWABLE (calendar))
3544                     hildon_calendar_paint_header (GTK_WIDGET (calendar));
3545
3546             if (private_data->dirty_day_names)
3547                 if (GTK_WIDGET_DRAWABLE (calendar))
3548                     hildon_calendar_paint_day_names (GTK_WIDGET (calendar));
3549
3550             if (private_data->dirty_week)
3551                 if (GTK_WIDGET_DRAWABLE (calendar))
3552                     hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3553
3554             if (private_data->dirty_main)
3555                 if (GTK_WIDGET_DRAWABLE (calendar))
3556                     hildon_calendar_paint_main (GTK_WIDGET (calendar));
3557         }
3558 }
3559
3560 static void
3561 hildon_calendar_set_background                  (GtkWidget *widget)
3562 {
3563     HildonCalendarPrivate *private_data;
3564     gint i;
3565
3566     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3567
3568     if (GTK_WIDGET_REALIZED (widget))
3569     {
3570         for (i = 0; i < 4; i++)
3571         {
3572             if (private_data->arrow_win[i])
3573                 gdk_window_set_background (private_data->arrow_win[i], 
3574                         HEADER_BG_COLOR (widget));
3575         }
3576         if (private_data->header_win)
3577             gdk_window_set_background (private_data->header_win, 
3578                     HEADER_BG_COLOR (widget));
3579         if (private_data->day_name_win)
3580             gdk_window_set_background (private_data->day_name_win, 
3581                     BACKGROUND_COLOR (widget));
3582         if (private_data->week_win)
3583             gdk_window_set_background (private_data->week_win,
3584                     BACKGROUND_COLOR (widget));
3585         if (private_data->main_win)
3586             gdk_window_set_background (private_data->main_win,
3587                     BACKGROUND_COLOR (widget));
3588         if (widget->window)
3589             gdk_window_set_background (widget->window,
3590                     BACKGROUND_COLOR (widget)); 
3591     }
3592 }
3593
3594 static void
3595 hildon_calendar_style_set                       (GtkWidget *widget,
3596                                                  GtkStyle *previous_style)
3597 {
3598     if (previous_style && GTK_WIDGET_REALIZED (widget))
3599         hildon_calendar_set_background(widget);
3600 }
3601
3602 static void
3603 hildon_calendar_state_changed                   (GtkWidget *widget,
3604                                                  GtkStateType previous_state)
3605 {
3606     HildonCalendarPrivate *private_data;
3607     int i;
3608
3609     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3610
3611     if (!GTK_WIDGET_IS_SENSITIVE (widget))
3612     {
3613         private_data->in_drag = 0;
3614         stop_spinning (widget);    
3615     }
3616
3617     for (i = 0; i < 4; i++)
3618         if (GTK_WIDGET_IS_SENSITIVE (widget))
3619             private_data->arrow_state[i] = GTK_STATE_NORMAL;
3620         else 
3621             private_data->arrow_state[i] = GTK_STATE_INSENSITIVE;
3622
3623     hildon_calendar_set_background (widget);
3624 }
3625
3626 static void
3627 hildon_calendar_finalize                        (GObject *object)
3628 {
3629     HildonCalendarPrivate *private_data;
3630     int i;
3631
3632     private_data = HILDON_CALENDAR_GET_PRIVATE (object);
3633
3634     for (i = 0; i < 7; i++)
3635         g_free (private_data->abbreviated_dayname[i]);
3636     for (i = 0; i < 12; i++)
3637         g_free (private_data->monthname[i]);
3638     g_free (private_data);
3639
3640     (* G_OBJECT_CLASS (parent_class)->finalize) (object);
3641 }
3642
3643 static gboolean
3644 hildon_calendar_scroll                          (GtkWidget *widget,
3645                                                  GdkEventScroll *event)
3646 {
3647     HildonCalendar *calendar = HILDON_CALENDAR (widget);
3648
3649     if (event->direction == GDK_SCROLL_UP) 
3650     {
3651         if (!GTK_WIDGET_HAS_FOCUS (widget))
3652             gtk_widget_grab_focus (widget);
3653         hildon_calendar_set_month_prev (calendar);
3654     }
3655     else if (event->direction == GDK_SCROLL_DOWN) 
3656     {
3657         if (!GTK_WIDGET_HAS_FOCUS (widget))
3658             gtk_widget_grab_focus (widget);
3659         hildon_calendar_set_month_next (calendar);
3660     }
3661     else
3662         return FALSE;
3663
3664     return TRUE;
3665 }
3666
3667 static void 
3668 move_focus                                      (HildonCalendar *calendar, 
3669                                                  gint direction)
3670 {
3671     GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3672
3673     if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3674             (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
3675     {
3676         if (calendar->focus_col > 0)
3677             calendar->focus_col--;
3678         else if (calendar->focus_row > 0)
3679         {
3680             calendar->focus_col = 6;
3681             calendar->focus_row--;
3682         }
3683     }
3684     else 
3685     {
3686         if (calendar->focus_col < 6)
3687             calendar->focus_col++;
3688         else if (calendar->focus_row < 5)
3689         {
3690             calendar->focus_col = 0;
3691             calendar->focus_row++;
3692         }
3693     }
3694 }
3695
3696 static gboolean
3697 hildon_calendar_key_press                       (GtkWidget *widget,
3698                                                  GdkEventKey *event)
3699 {
3700     HildonCalendar *calendar;
3701     HildonCalendarPrivate *priv;
3702     GtkSettings *settings;
3703     gint return_val;
3704     gint old_focus_row;
3705     gint old_focus_col;
3706     gint row, col, day;
3707     gboolean knav;
3708
3709     calendar = HILDON_CALENDAR (widget);
3710     priv = HILDON_CALENDAR_GET_PRIVATE (calendar);
3711     return_val = FALSE;
3712
3713     old_focus_row = calendar->focus_row;
3714     old_focus_col = calendar->focus_col;
3715
3716     settings = gtk_settings_get_default ();
3717     g_object_get (settings, "hildon-keyboard-navigation", &knav, NULL);
3718
3719     switch (event->keyval)
3720     {
3721         case GDK_KP_Left:
3722         case GDK_Left:
3723             return_val = TRUE;
3724             if (event->state & GDK_CONTROL_MASK)
3725                 hildon_calendar_set_month_prev (calendar);
3726             else
3727             {
3728                 /* if we are at the first allowed day of the minimum year/month then do nothing */
3729                 if (calendar->year == priv->min_year && calendar->month == 0 &&
3730                         calendar->day_month[old_focus_row][old_focus_col-1] == MONTH_PREV) 
3731                 {
3732                     g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
3733                     return TRUE;
3734                 }
3735                 else /* else normal */
3736                 {
3737                     move_focus (calendar, -1);
3738                     if (!knav)
3739                     {
3740                         hildon_calendar_paint_day (widget, old_focus_row, old_focus_col);
3741                         hildon_calendar_paint_day (widget, calendar->focus_row,
3742                                 calendar->focus_col);
3743                     }
3744                     else if (knav)
3745                     {
3746                         gint day_month = calendar->day_month[calendar->focus_row][calendar->focus_col];
3747                         if (day_month == MONTH_CURRENT && calendar->selected_day != 1)
3748                         {
3749                             hildon_calendar_select_day(calendar, calendar->selected_day - 1);
3750                         }
3751                         else
3752                         {
3753                             if (calendar->month != 0) {
3754                                 calendar->selected_day = month_length[leap (calendar->year)][calendar->month];
3755                             } else {
3756                                 calendar->selected_day = month_length[leap (calendar->year -1)][12];
3757                             }
3758                             hildon_calendar_set_month_prev (calendar);
3759                         }
3760                         hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3761                         hildon_calendar_paint_day_names (GTK_WIDGET (calendar));
3762                     }
3763                 }
3764             }
3765             break;
3766         case GDK_KP_Right:
3767         case GDK_Right:
3768             return_val = TRUE;
3769             if (event->state & GDK_CONTROL_MASK)
3770                 hildon_calendar_set_month_next (calendar);
3771             else
3772             {
3773                 if (calendar->year == priv->max_year && calendar->month == 11 &&
3774                         calendar->day_month[old_focus_row][old_focus_col+1] == MONTH_NEXT)
3775                 {
3776                     g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
3777                     return TRUE;
3778                 }
3779                 else 
3780                 {
3781                     move_focus (calendar, 1);
3782                     if (!knav)
3783                     {
3784                         hildon_calendar_paint_day (widget, old_focus_row, old_focus_col);
3785                         hildon_calendar_paint_day (widget, calendar->focus_row,
3786                                 calendar->focus_col);
3787                     }
3788                     else if (knav)
3789                     {
3790                         gint day_month = calendar->day_month[calendar->focus_row][calendar->focus_col];
3791                         if (day_month == MONTH_CURRENT)
3792                         {  
3793                             hildon_calendar_select_day (calendar, calendar->selected_day + 1);
3794                         }
3795                         else
3796                         {
3797                             calendar->selected_day = 1;
3798                             hildon_calendar_set_month_next (calendar);
3799                         }
3800                         hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3801                         hildon_calendar_paint_day_names (GTK_WIDGET (calendar)); 
3802                     } 
3803                 }
3804             }
3805             break;
3806         case GDK_KP_Up:
3807         case GDK_Up:
3808             return_val = TRUE;
3809             if (event->state & GDK_CONTROL_MASK)
3810                 hildon_calendar_set_year_prev (calendar);
3811             else
3812             {
3813                 if (calendar->year == priv->min_year && calendar->month == 0 &&
3814                         calendar->day_month[old_focus_row-1][old_focus_col] == MONTH_PREV)
3815                 {
3816                     g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
3817                     return TRUE;
3818                 }
3819                 else 
3820                 {
3821                     if (calendar->focus_row > 0)
3822                         calendar->focus_row--;
3823                     if (!knav)
3824                     {
3825                         hildon_calendar_paint_day (widget, old_focus_row, old_focus_col);
3826                         hildon_calendar_paint_day (widget, calendar->focus_row,
3827                                 calendar->focus_col);
3828                     }
3829                     else if (knav)
3830                     {
3831                         gint day_month = calendar->day_month[calendar->focus_row][calendar->focus_col];
3832                         if (day_month == MONTH_CURRENT)
3833                         {
3834                             if ((calendar->selected_day - 7) <= 0)
3835                             {
3836                                 if (calendar->month != 0)
3837                                     calendar->selected_day = month_length[leap (calendar->year)][calendar->month];
3838                                 else
3839                                     calendar->selected_day = month_length[leap (calendar->year - 1)][12];
3840                                 hildon_calendar_set_month_prev (calendar); 
3841                             }
3842                             else
3843                             {
3844                                 hildon_calendar_select_day (calendar, calendar->selected_day - 7);
3845                             }
3846                         }
3847                         else
3848                         {
3849                             calendar->selected_day = calendar->day[calendar->focus_row][calendar->focus_col];
3850                             hildon_calendar_set_month_prev (calendar);
3851                         }
3852                         hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3853                         hildon_calendar_paint_day_names (GTK_WIDGET (calendar)); 
3854                     }
3855                 }
3856             }
3857             break;
3858         case GDK_KP_Down:
3859         case GDK_Down:
3860             return_val = TRUE;
3861             if (event->state & GDK_CONTROL_MASK)
3862                 hildon_calendar_set_year_next (calendar);
3863             else
3864             {
3865                 if (calendar->year == priv->max_year && calendar->month == 11 &&
3866                         calendar->day_month[old_focus_row+1][old_focus_col] == MONTH_NEXT)
3867                 {
3868                     g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
3869                     return TRUE;
3870                 }
3871                 else 
3872                 {
3873
3874                     if (calendar->focus_row < 5)
3875                         calendar->focus_row++;
3876                     if (!knav)
3877                     {
3878                         hildon_calendar_paint_day (widget, old_focus_row, old_focus_col);
3879                         hildon_calendar_paint_day (widget, calendar->focus_row,
3880                                 calendar->focus_col);
3881                     }
3882                     else if (knav)
3883                     {
3884                         gint day_month = calendar->day_month[calendar->focus_row][calendar->focus_col];
3885                         if (day_month == MONTH_CURRENT)
3886                         {
3887                             if ((calendar->selected_day + 7) > 
3888                                     month_length[leap (calendar->year)][calendar->month + 1])
3889                             {
3890                                 calendar->selected_day = 1;
3891                                 hildon_calendar_set_month_next (calendar);
3892                             }
3893                             else
3894                             {
3895                                 hildon_calendar_select_day (calendar, calendar->selected_day + 7);
3896                             }
3897                         }
3898                         else
3899                         {
3900                             calendar->selected_day = calendar->day[calendar->focus_row][calendar->focus_col];
3901                             hildon_calendar_set_month_next (calendar);
3902                         }
3903                         hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3904                         hildon_calendar_paint_day_names (GTK_WIDGET (calendar));
3905                     } 
3906                 }
3907             }
3908
3909             break;
3910         case GDK_KP_Space:
3911         case GDK_space:
3912             row = calendar->focus_row;
3913             col = calendar->focus_col;
3914             day = calendar->day[row][col];
3915
3916             if (row > -1 && col > -1)
3917             {
3918                 return_val = TRUE;
3919                 hildon_calendar_freeze (calendar);         
3920
3921                 if (calendar->day_month[row][col] == MONTH_PREV)
3922                     hildon_calendar_set_month_prev (calendar);
3923                 else if (calendar->day_month[row][col] == MONTH_NEXT)
3924                     hildon_calendar_set_month_next (calendar);
3925
3926                 hildon_calendar_select_and_focus_day (calendar, day);
3927
3928                 hildon_calendar_thaw (calendar);   
3929             }
3930     }   
3931
3932     return return_val;
3933 }
3934
3935 static void
3936 hildon_calendar_set_display_option              (HildonCalendar *calendar,
3937                                                  HildonCalendarDisplayOptions flag,
3938                                                  gboolean setting)
3939 {
3940     HildonCalendarDisplayOptions flags;
3941
3942     if (setting) 
3943         flags = calendar->display_flags | flag;
3944     else
3945         flags = calendar->display_flags & ~flag; 
3946
3947     hildon_calendar_set_display_options (calendar, flags);
3948 }
3949
3950 static gboolean
3951 hildon_calendar_get_display_option              (HildonCalendar *calendar,
3952                                                  HildonCalendarDisplayOptions flag)
3953 {
3954     return (calendar->display_flags & flag) != 0;
3955 }
3956
3957
3958 static void 
3959 hildon_calendar_set_property                    (GObject *object,
3960                                                  guint prop_id,
3961                                                  const GValue *value,
3962                                                  GParamSpec   *pspec)
3963 {
3964     HildonCalendar *calendar;
3965     HildonCalendarPrivate *private_data;
3966     gint val;
3967
3968     calendar = HILDON_CALENDAR (object);
3969     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
3970
3971     switch (prop_id) 
3972     {
3973         case PROP_YEAR:
3974             hildon_calendar_select_month (calendar,
3975                     calendar->month,
3976                     g_value_get_int (value));
3977             break;
3978         case PROP_MONTH:
3979             hildon_calendar_select_month (calendar,
3980                     g_value_get_int (value),
3981                     calendar->year);
3982             break;
3983         case PROP_DAY:
3984             hildon_calendar_select_day (calendar,
3985                     g_value_get_int (value));
3986             break;
3987         case PROP_SHOW_HEADING:
3988             hildon_calendar_set_display_option (calendar,
3989                     HILDON_CALENDAR_SHOW_HEADING,
3990                     g_value_get_boolean (value));
3991             break;
3992         case PROP_SHOW_DAY_NAMES:
3993             hildon_calendar_set_display_option (calendar,
3994                     HILDON_CALENDAR_SHOW_DAY_NAMES,
3995                     g_value_get_boolean (value));
3996             break;
3997         case PROP_NO_MONTH_CHANGE:
3998             hildon_calendar_set_display_option (calendar,
3999                     HILDON_CALENDAR_NO_MONTH_CHANGE,
4000                     g_value_get_boolean (value));
4001             break;
4002         case PROP_SHOW_WEEK_NUMBERS:
4003             hildon_calendar_set_display_option (calendar,
4004                     HILDON_CALENDAR_SHOW_WEEK_NUMBERS,
4005                     g_value_get_boolean (value));
4006             break;
4007         case PROP_WEEK_START:
4008             private_data->week_start = g_value_get_int (value);
4009             break;
4010         case PROP_MIN_YEAR:
4011             val = g_value_get_int (value);
4012             if (val <= private_data->max_year ||
4013                     val == 0 || private_data->max_year == 0)
4014             {
4015                 private_data->min_year = val;
4016                 if (val && (calendar->year < val))
4017                     hildon_calendar_select_month (calendar,
4018                             calendar->month,
4019                             private_data->min_year);
4020             }
4021             else
4022                 g_warning("min-year cannot be greater than max-year");
4023             break;
4024         case PROP_MAX_YEAR:
4025             val = g_value_get_int (value);
4026             if (val >= private_data->min_year ||
4027                     val == 0 || private_data->min_year == 0)
4028             {
4029                 private_data->max_year = val;
4030                 if (val && (calendar->year > val))
4031                     hildon_calendar_select_month (calendar,
4032                             calendar->month,
4033                             private_data->max_year);
4034             }
4035             else
4036                 g_warning("max-year cannot be less than min-year");
4037             break;
4038         default:
4039             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4040             break;
4041     }
4042 }
4043
4044 static void 
4045 hildon_calendar_get_property                    (GObject *object,
4046                                                  guint prop_id,
4047                                                  GValue *value,
4048                                                  GParamSpec *pspec)
4049 {
4050     HildonCalendar *calendar;
4051     HildonCalendarPrivate *private_data;
4052
4053     calendar = HILDON_CALENDAR (object);
4054     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
4055
4056     switch (prop_id) 
4057     {
4058         case PROP_YEAR:
4059             g_value_set_int (value, calendar->year);
4060             break;
4061         case PROP_MONTH:
4062             g_value_set_int (value, calendar->month);
4063             break;
4064         case PROP_DAY:
4065             g_value_set_int (value, calendar->selected_day);
4066             break;
4067         case PROP_SHOW_HEADING:
4068             g_value_set_boolean (value, hildon_calendar_get_display_option (calendar,
4069                         HILDON_CALENDAR_SHOW_HEADING));
4070             break;
4071         case PROP_SHOW_DAY_NAMES:
4072             g_value_set_boolean (value, hildon_calendar_get_display_option (calendar,
4073                         HILDON_CALENDAR_SHOW_DAY_NAMES));
4074             break;
4075         case PROP_NO_MONTH_CHANGE:
4076             g_value_set_boolean (value, hildon_calendar_get_display_option (calendar,
4077                         HILDON_CALENDAR_NO_MONTH_CHANGE));
4078             break;
4079         case PROP_SHOW_WEEK_NUMBERS:
4080             g_value_set_boolean (value, hildon_calendar_get_display_option (calendar,
4081                         HILDON_CALENDAR_SHOW_WEEK_NUMBERS));
4082             break;
4083         case PROP_WEEK_START:
4084             g_value_set_int (value, private_data->week_start);
4085             break;
4086         case PROP_MIN_YEAR:
4087             g_value_set_int (value, private_data->min_year);
4088             break;
4089         case PROP_MAX_YEAR:
4090             g_value_set_int (value, private_data->max_year);
4091             break;
4092         default:
4093             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4094             break;
4095     }
4096
4097 }
4098
4099 static void
4100 hildon_calendar_drag_data_get                   (GtkWidget *widget,
4101                                                  GdkDragContext *context,
4102                                                  GtkSelectionData *selection_data,
4103                                                  guint info,
4104                                                  guint time)
4105 {
4106     HildonCalendar *calendar = HILDON_CALENDAR (widget);
4107     GDate *date;
4108     gchar str[128];
4109     gsize len;
4110
4111     date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
4112     len = g_date_strftime (str, 127, "%x", date);
4113     gtk_selection_data_set_text (selection_data, str, len);
4114
4115     g_free (date);
4116 }
4117
4118 /* Get/set whether drag_motion requested the drag data and
4119  * drag_data_received should thus not actually insert the data,
4120  * since the data doesn't result from a drop.
4121  */
4122 static void
4123 set_status_pending                              (GdkDragContext *context,
4124                                                  GdkDragAction suggested_action)
4125 {
4126     g_object_set_data (G_OBJECT (context),
4127             "gtk-calendar-status-pending",
4128             GINT_TO_POINTER (suggested_action));
4129 }
4130
4131 static GdkDragAction
4132 get_status_pending                              (GdkDragContext *context)
4133 {
4134     return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
4135                 "gtk-calendar-status-pending"));
4136 }
4137
4138 static void
4139 hildon_calendar_drag_leave                      (GtkWidget *widget,
4140                                                  GdkDragContext *context,
4141                                                  guint time)
4142 {
4143     HildonCalendarPrivate *private_data;
4144
4145     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
4146     private_data->drag_highlight = 0;
4147     gtk_drag_unhighlight (widget);
4148
4149 }
4150
4151 static gboolean
4152 hildon_calendar_drag_motion                     (GtkWidget *widget,
4153                                                  GdkDragContext *context,
4154                                                  gint x,
4155                                                  gint y,
4156                                                  guint time)
4157 {
4158     HildonCalendarPrivate *private_data;
4159     GdkAtom target;
4160
4161     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
4162
4163     if (!private_data->drag_highlight) 
4164     {
4165         private_data->drag_highlight = 1;
4166         gtk_drag_highlight (widget);
4167     }
4168
4169     target = gtk_drag_dest_find_target (widget, context, NULL);
4170     if (target == GDK_NONE)
4171         gdk_drag_status (context, 0, time);
4172     else {
4173         set_status_pending (context, context->suggested_action);
4174         gtk_drag_get_data (widget, context, target, time);
4175     }
4176
4177     return TRUE;
4178 }
4179
4180 static gboolean
4181 hildon_calendar_drag_drop                       (GtkWidget *widget,
4182                                                  GdkDragContext *context,
4183                                                  gint x,
4184                                                  gint y,
4185                                                  guint time)
4186 {
4187     GdkAtom target;
4188
4189     target = gtk_drag_dest_find_target (widget, context, NULL);  
4190     if (target != GDK_NONE)
4191     {
4192         gtk_drag_get_data (widget, context, 
4193                 target, 
4194                 time);
4195         return TRUE;
4196     }
4197
4198     return FALSE;
4199 }
4200
4201 static void
4202 hildon_calendar_drag_data_received              (GtkWidget *widget,
4203                                                  GdkDragContext *context,
4204                                                  gint x,
4205                                                  gint y,
4206                                                  GtkSelectionData *selection_data,
4207                                                  guint info,
4208                                                  guint time)
4209 {
4210     HildonCalendar *calendar = HILDON_CALENDAR (widget);
4211     guint day, month, year;
4212     gchar *str;
4213     GDate *date;
4214     GdkDragAction suggested_action;
4215
4216     suggested_action = get_status_pending (context);
4217
4218     if (suggested_action) 
4219     {
4220         set_status_pending (context, 0);
4221
4222         /* We are getting this data due to a request in drag_motion,
4223          * rather than due to a request in drag_drop, so we are just
4224          * supposed to call drag_status, not actually paste in the
4225          * data.
4226          */
4227         str = (gchar *) gtk_selection_data_get_text (selection_data);
4228         if (str) 
4229         {
4230             date = g_date_new ();
4231             g_date_set_parse (date, str);
4232             if (!g_date_valid (date)) 
4233                 suggested_action = 0;
4234             g_date_free (date);
4235             g_free (str);
4236         }
4237         else
4238             suggested_action = 0;
4239
4240         gdk_drag_status (context, suggested_action, time);
4241
4242         return;
4243     }
4244
4245     date = g_date_new ();
4246     str = (gchar *) gtk_selection_data_get_text (selection_data);
4247     if (str) 
4248     {
4249         g_date_set_parse (date, str);
4250         g_free (str);
4251     }
4252
4253     if (!g_date_valid (date)) 
4254     {
4255         g_warning ("Received invalid date data\n");
4256         g_date_free (date);       
4257         gtk_drag_finish (context, FALSE, FALSE, time);
4258         return;
4259     }
4260
4261     day = g_date_get_day (date);
4262     month = g_date_get_month (date);
4263     year = g_date_get_year (date);
4264     g_date_free (date);   
4265
4266     gtk_drag_finish (context, TRUE, FALSE, time);
4267
4268
4269     g_object_freeze_notify (G_OBJECT (calendar));
4270     if (!(calendar->display_flags & HILDON_CALENDAR_NO_MONTH_CHANGE)
4271             && (calendar->display_flags & HILDON_CALENDAR_SHOW_HEADING))
4272         hildon_calendar_select_month (calendar, month - 1, year);
4273     hildon_calendar_select_day (calendar, day);
4274     g_object_thaw_notify (G_OBJECT (calendar));  
4275 }
4276
4277 /* This function return TRUE if we should mark date and FALSE
4278  *  otherwise
4279  */
4280 static void
4281 hildon_calendar_check_current_date              (HildonCalendar *calendar, 
4282                                                  gint x, 
4283                                                  gint y)
4284 {
4285     HildonCalendarPrivate *private_data;
4286
4287     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
4288
4289     if (calendar->month == private_data->current_month && 
4290             calendar->year == private_data->current_year)
4291     {
4292         gtk_paint_box( GTK_WIDGET (calendar)->style,
4293                 private_data->main_win,
4294                 GTK_STATE_NORMAL,
4295                 GTK_SHADOW_NONE, NULL,
4296                 GTK_WIDGET (calendar), "current-day",
4297                 x, y,
4298                 HILDON_DAY_WIDTH,
4299                 HILDON_DAY_HEIGHT);
4300     }
4301 }
4302
4303 #define                                         __HILDON_CALENDAR_C__