Renamed src/ to hildon/
[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     GDK_THREADS_ENTER ();
3047
3048     if (private_data->timer)
3049     {
3050         arrow_action (calendar, private_data->click_child);
3051
3052         if (private_data->need_timer)
3053         {
3054             private_data->need_timer = FALSE;
3055             private_data->timer = g_timeout_add (/*CALENDAR_TIMER_DELAY*/timeout, 
3056                     (GSourceFunc) calendar_timer, 
3057                     (gpointer) calendar);
3058         }
3059         else 
3060             retval = TRUE;
3061     }
3062
3063     GDK_THREADS_LEAVE ();
3064
3065     return retval;
3066 }
3067
3068 static void
3069 start_spinning                                  (GtkWidget *widget,
3070                                                  gint click_child)
3071 {
3072     HildonCalendarPrivate *private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3073     GtkSettings *settings;
3074     guint timeout;
3075
3076     settings = gtk_settings_get_default ();
3077     g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3078     timeout *= 8;
3079
3080     private_data->click_child = click_child;
3081
3082     if (!private_data->timer)
3083     {
3084         private_data->need_timer = TRUE;
3085         private_data->timer = g_timeout_add (/*CALENDAR_INITIAL_TIMER_DELAY*/timeout, 
3086                 calendar_timer,
3087                 (gpointer) widget);
3088     }
3089 }
3090
3091 static void
3092 stop_spinning                                   (GtkWidget *widget)
3093 {
3094     HildonCalendarPrivate *private_data;
3095
3096     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3097
3098     if (private_data->timer)
3099     {
3100         g_source_remove (private_data->timer);
3101         private_data->timer = 0;
3102         private_data->need_timer = FALSE;
3103     }
3104 }
3105
3106 static void
3107 hildon_calendar_destroy                         (GtkObject *object)
3108 {
3109     stop_spinning (GTK_WIDGET (object));
3110
3111     GTK_OBJECT_CLASS (parent_class)->destroy (object);
3112 }
3113
3114 static void
3115 hildon_calendar_grab_notify                     (GtkWidget *widget,
3116                                                  gboolean   was_grabbed)
3117 {
3118     if (!was_grabbed)
3119         stop_spinning (widget);
3120 }
3121
3122 static gboolean
3123 hildon_calendar_focus_out                       (GtkWidget *widget,
3124                                                  GdkEventFocus *event)
3125 {
3126     HildonCalendarPrivate *private_data;
3127
3128     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3129
3130     stop_spinning (widget);
3131
3132     private_data->in_drag = 0; 
3133
3134     return FALSE;
3135 }
3136
3137 static gboolean
3138 hildon_calendar_button_press                    (GtkWidget *widget,
3139                                                  GdkEventButton *event)
3140 {
3141     HildonCalendar *calendar;
3142     HildonCalendarPrivate *private_data;
3143     gint arrow = -1;
3144
3145     calendar = HILDON_CALENDAR (widget);
3146     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3147
3148
3149     if (event->type == GDK_2BUTTON_PRESS)
3150     {
3151         if (event->window == private_data->main_win)
3152             hildon_calendar_main_button (widget, event);
3153     }
3154     else if (event->window == private_data->main_win)
3155     {
3156         gint x = (gint) (event->x);
3157         gint y = (gint) (event->y);
3158         gint row = row_from_y (calendar, y);
3159         gint col = column_from_x (calendar, x);
3160         private_data->pressed_day = calendar->day[row][col];
3161
3162         if ((calendar->year == private_data->min_year &&
3163                     calendar->month == 0
3164                     && calendar->day_month[row][col] == MONTH_PREV) ||
3165                 (calendar->year == private_data->max_year &&
3166                  calendar->month == 11 &&
3167                  calendar->day_month[row][col] == MONTH_NEXT))
3168         {}
3169         else if (calendar->day_month[row][col] == MONTH_CURRENT)
3170             hildon_calendar_select_and_focus_day (calendar, private_data->pressed_day);
3171
3172         /* Remember month where button was pressed */
3173         private_data->pressed_month = calendar->month;
3174         private_data->slide_stylus = TRUE;
3175     } 
3176
3177     if (!GTK_WIDGET_HAS_FOCUS (widget))
3178         gtk_widget_grab_focus (widget);
3179
3180     for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
3181     {
3182         if (event->window == private_data->arrow_win[arrow])
3183         {
3184
3185             /* only call the action on single click, not double */
3186             if (event->type == GDK_BUTTON_PRESS)
3187             {
3188                 if (event->button == 1)
3189                     start_spinning (widget, arrow);
3190
3191                 arrow_action (calendar, arrow);         
3192             }
3193
3194             return TRUE;
3195         }
3196     }
3197
3198     return TRUE;
3199 }
3200
3201 static gboolean
3202 hildon_calendar_button_release                  (GtkWidget *widget,
3203                                                  GdkEventButton *event)
3204 {
3205     HildonCalendar *calendar;
3206     HildonCalendarPrivate *private_data;
3207
3208     calendar = HILDON_CALENDAR (widget);
3209     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3210
3211     if (event->window == private_data->main_win)
3212     {
3213         hildon_calendar_main_button (widget, event);
3214         gint x = (gint) (event->x);
3215         gint y = (gint) (event->y);
3216         gint row = row_from_y (calendar, y);
3217         gint col = column_from_x (calendar, x);
3218         private_data->prev_col = -1;
3219         private_data->prev_row = -1;
3220
3221         if ((private_data->pressed_day == calendar->day[row][col]) &&
3222                 (private_data->pressed_month == calendar->month))
3223         {
3224             if (!private_data->is_bad_day)
3225             {
3226                 g_signal_emit (calendar, hildon_calendar_signals[SELECTED_DATE_SIGNAL], 0);
3227             }
3228             else
3229             {
3230                 private_data->is_bad_day = FALSE;
3231             }
3232         }
3233     }
3234
3235     if (event->button == 1) 
3236     {
3237         stop_spinning (widget);
3238
3239         if (private_data->in_drag)
3240             private_data->in_drag = 0;
3241     }
3242
3243     private_data->slide_stylus = FALSE;
3244     return TRUE;
3245 }
3246
3247 static gboolean
3248 hildon_calendar_motion_notify                   (GtkWidget *widget,
3249                                                  GdkEventMotion *event)
3250 {
3251     HildonCalendar *calendar;
3252     HildonCalendarPrivate *private_data;
3253     gint event_x, event_y;
3254     gint row, col;
3255     gint old_row, old_col;
3256
3257     calendar = HILDON_CALENDAR (widget);
3258     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3259     event_x = (gint) (event->x);
3260     event_y = (gint) (event->y);
3261
3262     if (event->window == private_data->main_win)
3263     {
3264         /* Hildon: make active day to move, when stylus is slided */
3265         if (private_data->slide_stylus)
3266         {
3267             gint c_row = row_from_y (calendar, event_y);
3268             gint c_col = column_from_x (calendar, event_x);
3269
3270             if (calendar->day_month[c_row][c_col] == MONTH_PREV ||
3271                     calendar->day_month[c_row][c_col] == MONTH_NEXT)
3272             { }
3273             else if ((private_data->prev_row != c_row || private_data->prev_col != c_col) &&
3274                     (calendar->highlight_row != -1 && calendar->highlight_col != -1))
3275             {
3276                 hildon_calendar_select_and_focus_day (calendar, 
3277                         calendar->day[c_row][c_col]);
3278                 /* Update passive focus indicators work weekday number and name */
3279                 hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3280                 hildon_calendar_paint_day_names (GTK_WIDGET (calendar));
3281             }
3282             private_data->prev_col = c_col;
3283             private_data->prev_row = c_row;    
3284         }
3285         if (private_data->in_drag) 
3286         {
3287             if (gtk_drag_check_threshold (widget,
3288                         private_data->drag_start_x, private_data->drag_start_y,
3289                         event->x, event->y))
3290             {
3291                 GdkDragContext *context;
3292                 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
3293                 gtk_target_list_add_text_targets (target_list, 0);
3294                 context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
3295                         1, (GdkEvent *)event);
3296
3297
3298                 private_data->in_drag = 0;
3299
3300                 gtk_target_list_unref (target_list);
3301                 gtk_drag_set_icon_default (context);
3302             }
3303         }
3304         else 
3305         {
3306             row = row_from_y (calendar, event_y);
3307             col = column_from_x (calendar, event_x);
3308
3309             if (row != calendar->highlight_row || calendar->highlight_col != col)
3310             {
3311                 old_row = calendar->highlight_row;
3312                 old_col = calendar->highlight_col;
3313                 if (old_row > -1 && old_col > -1)
3314                 {
3315                     calendar->highlight_row = -1;
3316                     calendar->highlight_col = -1;
3317                     hildon_calendar_paint_day (widget, old_row, old_col);
3318                 }
3319
3320                 calendar->highlight_row = row;
3321                 calendar->highlight_col = col;
3322
3323                 if (row > -1 && col > -1)
3324                     hildon_calendar_paint_day (widget, row, col);
3325             }
3326         }
3327     }
3328
3329     return TRUE;
3330 }
3331
3332 static gboolean
3333 hildon_calendar_enter_notify                    (GtkWidget *widget,
3334                                                  GdkEventCrossing *event)
3335 {
3336     HildonCalendarPrivate *private_data;
3337
3338     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3339
3340     if (event->window == private_data->arrow_win[ARROW_MONTH_LEFT])
3341     {
3342         private_data->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
3343         hildon_calendar_paint_arrow (widget, ARROW_MONTH_LEFT);
3344     }
3345
3346     if (event->window == private_data->arrow_win[ARROW_MONTH_RIGHT])
3347     {
3348         private_data->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
3349         hildon_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
3350     }
3351
3352     if (event->window == private_data->arrow_win[ARROW_YEAR_LEFT])
3353     {
3354         private_data->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
3355         hildon_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
3356     }
3357
3358     if (event->window == private_data->arrow_win[ARROW_YEAR_RIGHT])
3359     {
3360         private_data->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
3361         hildon_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
3362     }
3363
3364     return TRUE;
3365 }
3366
3367 static gboolean
3368 hildon_calendar_leave_notify                    (GtkWidget *widget,
3369                                                  GdkEventCrossing *event)
3370 {
3371     HildonCalendar *calendar;
3372     HildonCalendarPrivate *private_data;
3373     gint row;
3374     gint col;
3375
3376     calendar = HILDON_CALENDAR (widget);
3377     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3378
3379     if (event->window == private_data->main_win)
3380     {
3381         row = calendar->highlight_row;
3382         col = calendar->highlight_col;
3383         calendar->highlight_row = -1;
3384         calendar->highlight_col = -1;
3385         if (row > -1 && col > -1)
3386             hildon_calendar_paint_day (widget, row, col);
3387     }
3388
3389     if (event->window == private_data->arrow_win[ARROW_MONTH_LEFT])
3390     {
3391         private_data->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3392         hildon_calendar_paint_arrow (widget, ARROW_MONTH_LEFT);
3393     }
3394
3395     if (event->window == private_data->arrow_win[ARROW_MONTH_RIGHT])
3396     {
3397         private_data->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3398         hildon_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
3399     }
3400
3401     if (event->window == private_data->arrow_win[ARROW_YEAR_LEFT])
3402     {
3403         private_data->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3404         hildon_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
3405     }
3406
3407     if (event->window == private_data->arrow_win[ARROW_YEAR_RIGHT])
3408     {
3409         private_data->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3410         hildon_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
3411     }
3412
3413     return TRUE;
3414 }
3415
3416 static void
3417 hildon_calendar_paint_arrow                     (GtkWidget *widget,
3418                                                  guint      arrow)
3419 {
3420     HildonCalendarPrivate *private_data;
3421     GdkWindow *window;
3422     GdkGC *gc;
3423     HildonCalendar *calendar;
3424     gint state;
3425     guint arrow_hlength, arrow_vlength;
3426     /*  gint width, height;*/
3427
3428     calendar = HILDON_CALENDAR (widget);
3429     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3430     gtk_widget_style_get (widget,
3431             "scroll-arrow-hlength", &arrow_hlength,
3432             "scroll-arrow-vlength", &arrow_vlength,
3433             NULL);
3434
3435     if (private_data->freeze_count)
3436     {
3437         private_data->dirty_header = 1;
3438         return;
3439     }
3440     window = private_data->arrow_win[arrow];
3441     if (window)
3442     {
3443         state = private_data->arrow_state[arrow];
3444         gc = calendar->gc;
3445
3446         /*      gdk_window_clear (window);*/
3447         gdk_window_set_background (window, &(widget)->style->bg[state]);
3448         /*      gdk_drawable_get_size (window, &width, &height);*/
3449         /*      gdk_window_clear_area (window,
3450                 0,0,
3451                 width,height);*/
3452
3453         gdk_window_clear(window);
3454
3455         /* Hildon: added support for dimmed arrows */
3456         if (((private_data->min_year && calendar->year <= private_data->min_year) ||
3457              (private_data->max_year && calendar->year >= private_data->max_year)))
3458         {
3459             if (private_data->min_year &&
3460                     calendar->year <= private_data->min_year)
3461             {
3462                 if (arrow == ARROW_YEAR_LEFT)
3463                     gtk_paint_arrow (widget->style, window, GTK_STATE_INSENSITIVE,
3464                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3465                             GTK_ARROW_LEFT, TRUE,
3466                             0, 0, arrow_vlength, arrow_hlength);
3467                 else if (arrow == ARROW_YEAR_RIGHT || arrow == ARROW_MONTH_RIGHT)
3468                     gtk_paint_arrow (widget->style, window, state,
3469                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3470                             GTK_ARROW_RIGHT, TRUE, 
3471                             0, 0, arrow_vlength, arrow_hlength);
3472                 else if (arrow == ARROW_MONTH_LEFT && calendar->month != 0)
3473                     gtk_paint_arrow (widget->style, window, state,
3474                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3475                             GTK_ARROW_LEFT, TRUE,
3476                             0, 0, arrow_vlength, arrow_hlength);
3477                 else if (arrow == ARROW_MONTH_LEFT && !calendar->month)
3478                     gtk_paint_arrow (widget->style, window, GTK_STATE_INSENSITIVE,
3479                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3480                             GTK_ARROW_LEFT, TRUE,
3481                             0, 0, arrow_vlength, arrow_hlength);
3482             }
3483             else if (private_data->max_year &&
3484                     calendar->year >= private_data->max_year)
3485             {
3486                 if (arrow == ARROW_YEAR_RIGHT)
3487                     gtk_paint_arrow (widget->style, window, GTK_STATE_INSENSITIVE, 
3488                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3489                             GTK_ARROW_RIGHT, TRUE, 
3490                             0, 0, arrow_vlength, arrow_hlength);
3491                 else if (arrow == ARROW_YEAR_LEFT || arrow == ARROW_MONTH_LEFT)
3492                     gtk_paint_arrow (widget->style, window, state, 
3493                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3494                             GTK_ARROW_LEFT, TRUE, 
3495                             0, 0, arrow_vlength, arrow_hlength);
3496                 else if (arrow == ARROW_MONTH_RIGHT && calendar->month != 11)
3497                     gtk_paint_arrow (widget->style, window, state,
3498                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3499                             GTK_ARROW_RIGHT, TRUE,
3500                             0, 0, arrow_vlength, arrow_hlength);
3501                 else if (arrow == ARROW_MONTH_RIGHT && calendar->month == 11)
3502                     gtk_paint_arrow (widget->style, window, GTK_STATE_INSENSITIVE,
3503                             GTK_SHADOW_OUT, NULL, widget, "calendar",
3504                             GTK_ARROW_RIGHT, TRUE,
3505                             0, 0, arrow_vlength, arrow_hlength);
3506             }
3507         }
3508         else
3509         { 
3510             if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
3511                 gtk_paint_arrow (widget->style, window, state, 
3512                         GTK_SHADOW_OUT, NULL, widget, "calendar",
3513                         GTK_ARROW_LEFT, TRUE, 
3514                         /*                     width/2 - 3, height/2 - 4, 8, 8);*/
3515                                 0, 0, arrow_vlength, arrow_hlength);
3516             else 
3517                 gtk_paint_arrow (widget->style, window, state,
3518                         GTK_SHADOW_OUT, NULL, widget, "calendar",
3519                         GTK_ARROW_RIGHT, TRUE, 
3520                         /*                     width/2 - 2, height/2 - 4, 8, 8);*/
3521                                 0, 0, arrow_vlength, arrow_hlength);
3522         }
3523     }
3524 }
3525
3526 void
3527 hildon_calendar_freeze                          (HildonCalendar *calendar)
3528 {
3529     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
3530
3531     HILDON_CALENDAR_GET_PRIVATE (calendar)->freeze_count++;
3532 }
3533
3534 void
3535 hildon_calendar_thaw                            (HildonCalendar *calendar)
3536 {
3537     HildonCalendarPrivate *private_data;
3538
3539     g_return_if_fail (HILDON_IS_CALENDAR (calendar));
3540
3541     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
3542
3543     if (private_data->freeze_count)
3544         if (!(--private_data->freeze_count))
3545         {
3546             if (private_data->dirty_header)
3547                 if (GTK_WIDGET_DRAWABLE (calendar))
3548                     hildon_calendar_paint_header (GTK_WIDGET (calendar));
3549
3550             if (private_data->dirty_day_names)
3551                 if (GTK_WIDGET_DRAWABLE (calendar))
3552                     hildon_calendar_paint_day_names (GTK_WIDGET (calendar));
3553
3554             if (private_data->dirty_week)
3555                 if (GTK_WIDGET_DRAWABLE (calendar))
3556                     hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3557
3558             if (private_data->dirty_main)
3559                 if (GTK_WIDGET_DRAWABLE (calendar))
3560                     hildon_calendar_paint_main (GTK_WIDGET (calendar));
3561         }
3562 }
3563
3564 static void
3565 hildon_calendar_set_background                  (GtkWidget *widget)
3566 {
3567     HildonCalendarPrivate *private_data;
3568     gint i;
3569
3570     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3571
3572     if (GTK_WIDGET_REALIZED (widget))
3573     {
3574         for (i = 0; i < 4; i++)
3575         {
3576             if (private_data->arrow_win[i])
3577                 gdk_window_set_background (private_data->arrow_win[i], 
3578                         HEADER_BG_COLOR (widget));
3579         }
3580         if (private_data->header_win)
3581             gdk_window_set_background (private_data->header_win, 
3582                     HEADER_BG_COLOR (widget));
3583         if (private_data->day_name_win)
3584             gdk_window_set_background (private_data->day_name_win, 
3585                     BACKGROUND_COLOR (widget));
3586         if (private_data->week_win)
3587             gdk_window_set_background (private_data->week_win,
3588                     BACKGROUND_COLOR (widget));
3589         if (private_data->main_win)
3590             gdk_window_set_background (private_data->main_win,
3591                     BACKGROUND_COLOR (widget));
3592         if (widget->window)
3593             gdk_window_set_background (widget->window,
3594                     BACKGROUND_COLOR (widget)); 
3595     }
3596 }
3597
3598 static void
3599 hildon_calendar_style_set                       (GtkWidget *widget,
3600                                                  GtkStyle *previous_style)
3601 {
3602     if (previous_style && GTK_WIDGET_REALIZED (widget))
3603         hildon_calendar_set_background(widget);
3604 }
3605
3606 static void
3607 hildon_calendar_state_changed                   (GtkWidget *widget,
3608                                                  GtkStateType previous_state)
3609 {
3610     HildonCalendarPrivate *private_data;
3611     int i;
3612
3613     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
3614
3615     if (!GTK_WIDGET_IS_SENSITIVE (widget))
3616     {
3617         private_data->in_drag = 0;
3618         stop_spinning (widget);    
3619     }
3620
3621     for (i = 0; i < 4; i++)
3622         if (GTK_WIDGET_IS_SENSITIVE (widget))
3623             private_data->arrow_state[i] = GTK_STATE_NORMAL;
3624         else 
3625             private_data->arrow_state[i] = GTK_STATE_INSENSITIVE;
3626
3627     hildon_calendar_set_background (widget);
3628 }
3629
3630 static void
3631 hildon_calendar_finalize                        (GObject *object)
3632 {
3633     HildonCalendarPrivate *private_data;
3634     int i;
3635
3636     private_data = HILDON_CALENDAR_GET_PRIVATE (object);
3637
3638     for (i = 0; i < 7; i++)
3639         g_free (private_data->abbreviated_dayname[i]);
3640     for (i = 0; i < 12; i++)
3641         g_free (private_data->monthname[i]);
3642     g_free (private_data);
3643
3644     (* G_OBJECT_CLASS (parent_class)->finalize) (object);
3645 }
3646
3647 static gboolean
3648 hildon_calendar_scroll                          (GtkWidget *widget,
3649                                                  GdkEventScroll *event)
3650 {
3651     HildonCalendar *calendar = HILDON_CALENDAR (widget);
3652
3653     if (event->direction == GDK_SCROLL_UP) 
3654     {
3655         if (!GTK_WIDGET_HAS_FOCUS (widget))
3656             gtk_widget_grab_focus (widget);
3657         hildon_calendar_set_month_prev (calendar);
3658     }
3659     else if (event->direction == GDK_SCROLL_DOWN) 
3660     {
3661         if (!GTK_WIDGET_HAS_FOCUS (widget))
3662             gtk_widget_grab_focus (widget);
3663         hildon_calendar_set_month_next (calendar);
3664     }
3665     else
3666         return FALSE;
3667
3668     return TRUE;
3669 }
3670
3671 static void 
3672 move_focus                                      (HildonCalendar *calendar, 
3673                                                  gint direction)
3674 {
3675     GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3676
3677     if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3678             (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
3679     {
3680         if (calendar->focus_col > 0)
3681             calendar->focus_col--;
3682         else if (calendar->focus_row > 0)
3683         {
3684             calendar->focus_col = 6;
3685             calendar->focus_row--;
3686         }
3687     }
3688     else 
3689     {
3690         if (calendar->focus_col < 6)
3691             calendar->focus_col++;
3692         else if (calendar->focus_row < 5)
3693         {
3694             calendar->focus_col = 0;
3695             calendar->focus_row++;
3696         }
3697     }
3698 }
3699
3700 static gboolean
3701 hildon_calendar_key_press                       (GtkWidget *widget,
3702                                                  GdkEventKey *event)
3703 {
3704     HildonCalendar *calendar;
3705     HildonCalendarPrivate *priv;
3706     GtkSettings *settings;
3707     gint return_val;
3708     gint old_focus_row;
3709     gint old_focus_col;
3710     gint row, col, day;
3711     gboolean knav;
3712
3713     calendar = HILDON_CALENDAR (widget);
3714     priv = HILDON_CALENDAR_GET_PRIVATE (calendar);
3715     return_val = FALSE;
3716
3717     old_focus_row = calendar->focus_row;
3718     old_focus_col = calendar->focus_col;
3719
3720     settings = gtk_settings_get_default ();
3721     g_object_get (settings, "hildon-keyboard-navigation", &knav, NULL);
3722
3723     switch (event->keyval)
3724     {
3725         case GDK_KP_Left:
3726         case GDK_Left:
3727             return_val = TRUE;
3728             if (event->state & GDK_CONTROL_MASK)
3729                 hildon_calendar_set_month_prev (calendar);
3730             else
3731             {
3732                 /* if we are at the first allowed day of the minimum year/month then do nothing */
3733                 if (calendar->year == priv->min_year && calendar->month == 0 &&
3734                         calendar->day_month[old_focus_row][old_focus_col-1] == MONTH_PREV) 
3735                 {
3736                     g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
3737                     return TRUE;
3738                 }
3739                 else /* else normal */
3740                 {
3741                     move_focus (calendar, -1);
3742                     if (!knav)
3743                     {
3744                         hildon_calendar_paint_day (widget, old_focus_row, old_focus_col);
3745                         hildon_calendar_paint_day (widget, calendar->focus_row,
3746                                 calendar->focus_col);
3747                     }
3748                     else if (knav)
3749                     {
3750                         gint day_month = calendar->day_month[calendar->focus_row][calendar->focus_col];
3751                         if (day_month == MONTH_CURRENT && calendar->selected_day != 1)
3752                         {
3753                             hildon_calendar_select_day(calendar, calendar->selected_day - 1);
3754                         }
3755                         else
3756                         {
3757                             if (calendar->month != 0) {
3758                                 calendar->selected_day = month_length[leap (calendar->year)][calendar->month];
3759                             } else {
3760                                 calendar->selected_day = month_length[leap (calendar->year -1)][12];
3761                             }
3762                             hildon_calendar_set_month_prev (calendar);
3763                         }
3764                         hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3765                         hildon_calendar_paint_day_names (GTK_WIDGET (calendar));
3766                     }
3767                 }
3768             }
3769             break;
3770         case GDK_KP_Right:
3771         case GDK_Right:
3772             return_val = TRUE;
3773             if (event->state & GDK_CONTROL_MASK)
3774                 hildon_calendar_set_month_next (calendar);
3775             else
3776             {
3777                 if (calendar->year == priv->max_year && calendar->month == 11 &&
3778                         calendar->day_month[old_focus_row][old_focus_col+1] == MONTH_NEXT)
3779                 {
3780                     g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
3781                     return TRUE;
3782                 }
3783                 else 
3784                 {
3785                     move_focus (calendar, 1);
3786                     if (!knav)
3787                     {
3788                         hildon_calendar_paint_day (widget, old_focus_row, old_focus_col);
3789                         hildon_calendar_paint_day (widget, calendar->focus_row,
3790                                 calendar->focus_col);
3791                     }
3792                     else if (knav)
3793                     {
3794                         gint day_month = calendar->day_month[calendar->focus_row][calendar->focus_col];
3795                         if (day_month == MONTH_CURRENT)
3796                         {  
3797                             hildon_calendar_select_day (calendar, calendar->selected_day + 1);
3798                         }
3799                         else
3800                         {
3801                             calendar->selected_day = 1;
3802                             hildon_calendar_set_month_next (calendar);
3803                         }
3804                         hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3805                         hildon_calendar_paint_day_names (GTK_WIDGET (calendar)); 
3806                     } 
3807                 }
3808             }
3809             break;
3810         case GDK_KP_Up:
3811         case GDK_Up:
3812             return_val = TRUE;
3813             if (event->state & GDK_CONTROL_MASK)
3814                 hildon_calendar_set_year_prev (calendar);
3815             else
3816             {
3817                 if (calendar->year == priv->min_year && calendar->month == 0 &&
3818                         calendar->day_month[old_focus_row-1][old_focus_col] == MONTH_PREV)
3819                 {
3820                     g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
3821                     return TRUE;
3822                 }
3823                 else 
3824                 {
3825                     if (calendar->focus_row > 0)
3826                         calendar->focus_row--;
3827                     if (!knav)
3828                     {
3829                         hildon_calendar_paint_day (widget, old_focus_row, old_focus_col);
3830                         hildon_calendar_paint_day (widget, calendar->focus_row,
3831                                 calendar->focus_col);
3832                     }
3833                     else if (knav)
3834                     {
3835                         gint day_month = calendar->day_month[calendar->focus_row][calendar->focus_col];
3836                         if (day_month == MONTH_CURRENT)
3837                         {
3838                             if ((calendar->selected_day - 7) <= 0)
3839                             {
3840                                 if (calendar->month != 0)
3841                                     calendar->selected_day = month_length[leap (calendar->year)][calendar->month];
3842                                 else
3843                                     calendar->selected_day = month_length[leap (calendar->year - 1)][12];
3844                                 hildon_calendar_set_month_prev (calendar); 
3845                             }
3846                             else
3847                             {
3848                                 hildon_calendar_select_day (calendar, calendar->selected_day - 7);
3849                             }
3850                         }
3851                         else
3852                         {
3853                             calendar->selected_day = calendar->day[calendar->focus_row][calendar->focus_col];
3854                             hildon_calendar_set_month_prev (calendar);
3855                         }
3856                         hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3857                         hildon_calendar_paint_day_names (GTK_WIDGET (calendar)); 
3858                     }
3859                 }
3860             }
3861             break;
3862         case GDK_KP_Down:
3863         case GDK_Down:
3864             return_val = TRUE;
3865             if (event->state & GDK_CONTROL_MASK)
3866                 hildon_calendar_set_year_next (calendar);
3867             else
3868             {
3869                 if (calendar->year == priv->max_year && calendar->month == 11 &&
3870                         calendar->day_month[old_focus_row+1][old_focus_col] == MONTH_NEXT)
3871                 {
3872                     g_signal_emit (calendar, hildon_calendar_signals[ERRONEOUS_DATE_SIGNAL], 0);
3873                     return TRUE;
3874                 }
3875                 else 
3876                 {
3877
3878                     if (calendar->focus_row < 5)
3879                         calendar->focus_row++;
3880                     if (!knav)
3881                     {
3882                         hildon_calendar_paint_day (widget, old_focus_row, old_focus_col);
3883                         hildon_calendar_paint_day (widget, calendar->focus_row,
3884                                 calendar->focus_col);
3885                     }
3886                     else if (knav)
3887                     {
3888                         gint day_month = calendar->day_month[calendar->focus_row][calendar->focus_col];
3889                         if (day_month == MONTH_CURRENT)
3890                         {
3891                             if ((calendar->selected_day + 7) > 
3892                                     month_length[leap (calendar->year)][calendar->month + 1])
3893                             {
3894                                 calendar->selected_day = 1;
3895                                 hildon_calendar_set_month_next (calendar);
3896                             }
3897                             else
3898                             {
3899                                 hildon_calendar_select_day (calendar, calendar->selected_day + 7);
3900                             }
3901                         }
3902                         else
3903                         {
3904                             calendar->selected_day = calendar->day[calendar->focus_row][calendar->focus_col];
3905                             hildon_calendar_set_month_next (calendar);
3906                         }
3907                         hildon_calendar_paint_week_numbers (GTK_WIDGET (calendar));
3908                         hildon_calendar_paint_day_names (GTK_WIDGET (calendar));
3909                     } 
3910                 }
3911             }
3912
3913             break;
3914         case GDK_KP_Space:
3915         case GDK_space:
3916             row = calendar->focus_row;
3917             col = calendar->focus_col;
3918             day = calendar->day[row][col];
3919
3920             if (row > -1 && col > -1)
3921             {
3922                 return_val = TRUE;
3923                 hildon_calendar_freeze (calendar);         
3924
3925                 if (calendar->day_month[row][col] == MONTH_PREV)
3926                     hildon_calendar_set_month_prev (calendar);
3927                 else if (calendar->day_month[row][col] == MONTH_NEXT)
3928                     hildon_calendar_set_month_next (calendar);
3929
3930                 hildon_calendar_select_and_focus_day (calendar, day);
3931
3932                 hildon_calendar_thaw (calendar);   
3933             }
3934     }   
3935
3936     return return_val;
3937 }
3938
3939 static void
3940 hildon_calendar_set_display_option              (HildonCalendar *calendar,
3941                                                  HildonCalendarDisplayOptions flag,
3942                                                  gboolean setting)
3943 {
3944     HildonCalendarDisplayOptions flags;
3945
3946     if (setting) 
3947         flags = calendar->display_flags | flag;
3948     else
3949         flags = calendar->display_flags & ~flag; 
3950
3951     hildon_calendar_set_display_options (calendar, flags);
3952 }
3953
3954 static gboolean
3955 hildon_calendar_get_display_option              (HildonCalendar *calendar,
3956                                                  HildonCalendarDisplayOptions flag)
3957 {
3958     return (calendar->display_flags & flag) != 0;
3959 }
3960
3961
3962 static void 
3963 hildon_calendar_set_property                    (GObject *object,
3964                                                  guint prop_id,
3965                                                  const GValue *value,
3966                                                  GParamSpec   *pspec)
3967 {
3968     HildonCalendar *calendar;
3969     HildonCalendarPrivate *private_data;
3970     gint val;
3971
3972     calendar = HILDON_CALENDAR (object);
3973     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
3974
3975     switch (prop_id) 
3976     {
3977         case PROP_YEAR:
3978             hildon_calendar_select_month (calendar,
3979                     calendar->month,
3980                     g_value_get_int (value));
3981             break;
3982         case PROP_MONTH:
3983             hildon_calendar_select_month (calendar,
3984                     g_value_get_int (value),
3985                     calendar->year);
3986             break;
3987         case PROP_DAY:
3988             hildon_calendar_select_day (calendar,
3989                     g_value_get_int (value));
3990             break;
3991         case PROP_SHOW_HEADING:
3992             hildon_calendar_set_display_option (calendar,
3993                     HILDON_CALENDAR_SHOW_HEADING,
3994                     g_value_get_boolean (value));
3995             break;
3996         case PROP_SHOW_DAY_NAMES:
3997             hildon_calendar_set_display_option (calendar,
3998                     HILDON_CALENDAR_SHOW_DAY_NAMES,
3999                     g_value_get_boolean (value));
4000             break;
4001         case PROP_NO_MONTH_CHANGE:
4002             hildon_calendar_set_display_option (calendar,
4003                     HILDON_CALENDAR_NO_MONTH_CHANGE,
4004                     g_value_get_boolean (value));
4005             break;
4006         case PROP_SHOW_WEEK_NUMBERS:
4007             hildon_calendar_set_display_option (calendar,
4008                     HILDON_CALENDAR_SHOW_WEEK_NUMBERS,
4009                     g_value_get_boolean (value));
4010             break;
4011         case PROP_WEEK_START:
4012             private_data->week_start = g_value_get_int (value);
4013             break;
4014         case PROP_MIN_YEAR:
4015             val = g_value_get_int (value);
4016             if (val <= private_data->max_year ||
4017                     val == 0 || private_data->max_year == 0)
4018             {
4019                 private_data->min_year = val;
4020                 if (val && (calendar->year < val))
4021                     hildon_calendar_select_month (calendar,
4022                             calendar->month,
4023                             private_data->min_year);
4024             }
4025             else
4026                 g_warning("min-year cannot be greater than max-year");
4027             break;
4028         case PROP_MAX_YEAR:
4029             val = g_value_get_int (value);
4030             if (val >= private_data->min_year ||
4031                     val == 0 || private_data->min_year == 0)
4032             {
4033                 private_data->max_year = val;
4034                 if (val && (calendar->year > val))
4035                     hildon_calendar_select_month (calendar,
4036                             calendar->month,
4037                             private_data->max_year);
4038             }
4039             else
4040                 g_warning("max-year cannot be less than min-year");
4041             break;
4042         default:
4043             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4044             break;
4045     }
4046 }
4047
4048 static void 
4049 hildon_calendar_get_property                    (GObject *object,
4050                                                  guint prop_id,
4051                                                  GValue *value,
4052                                                  GParamSpec *pspec)
4053 {
4054     HildonCalendar *calendar;
4055     HildonCalendarPrivate *private_data;
4056
4057     calendar = HILDON_CALENDAR (object);
4058     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
4059
4060     switch (prop_id) 
4061     {
4062         case PROP_YEAR:
4063             g_value_set_int (value, calendar->year);
4064             break;
4065         case PROP_MONTH:
4066             g_value_set_int (value, calendar->month);
4067             break;
4068         case PROP_DAY:
4069             g_value_set_int (value, calendar->selected_day);
4070             break;
4071         case PROP_SHOW_HEADING:
4072             g_value_set_boolean (value, hildon_calendar_get_display_option (calendar,
4073                         HILDON_CALENDAR_SHOW_HEADING));
4074             break;
4075         case PROP_SHOW_DAY_NAMES:
4076             g_value_set_boolean (value, hildon_calendar_get_display_option (calendar,
4077                         HILDON_CALENDAR_SHOW_DAY_NAMES));
4078             break;
4079         case PROP_NO_MONTH_CHANGE:
4080             g_value_set_boolean (value, hildon_calendar_get_display_option (calendar,
4081                         HILDON_CALENDAR_NO_MONTH_CHANGE));
4082             break;
4083         case PROP_SHOW_WEEK_NUMBERS:
4084             g_value_set_boolean (value, hildon_calendar_get_display_option (calendar,
4085                         HILDON_CALENDAR_SHOW_WEEK_NUMBERS));
4086             break;
4087         case PROP_WEEK_START:
4088             g_value_set_int (value, private_data->week_start);
4089             break;
4090         case PROP_MIN_YEAR:
4091             g_value_set_int (value, private_data->min_year);
4092             break;
4093         case PROP_MAX_YEAR:
4094             g_value_set_int (value, private_data->max_year);
4095             break;
4096         default:
4097             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4098             break;
4099     }
4100
4101 }
4102
4103 static void
4104 hildon_calendar_drag_data_get                   (GtkWidget *widget,
4105                                                  GdkDragContext *context,
4106                                                  GtkSelectionData *selection_data,
4107                                                  guint info,
4108                                                  guint time)
4109 {
4110     HildonCalendar *calendar = HILDON_CALENDAR (widget);
4111     GDate *date;
4112     gchar str[128];
4113     gsize len;
4114
4115     date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
4116     len = g_date_strftime (str, 127, "%x", date);
4117     gtk_selection_data_set_text (selection_data, str, len);
4118
4119     g_free (date);
4120 }
4121
4122 /* Get/set whether drag_motion requested the drag data and
4123  * drag_data_received should thus not actually insert the data,
4124  * since the data doesn't result from a drop.
4125  */
4126 static void
4127 set_status_pending                              (GdkDragContext *context,
4128                                                  GdkDragAction suggested_action)
4129 {
4130     g_object_set_data (G_OBJECT (context),
4131             "gtk-calendar-status-pending",
4132             GINT_TO_POINTER (suggested_action));
4133 }
4134
4135 static GdkDragAction
4136 get_status_pending                              (GdkDragContext *context)
4137 {
4138     return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
4139                 "gtk-calendar-status-pending"));
4140 }
4141
4142 static void
4143 hildon_calendar_drag_leave                      (GtkWidget *widget,
4144                                                  GdkDragContext *context,
4145                                                  guint time)
4146 {
4147     HildonCalendarPrivate *private_data;
4148
4149     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
4150     private_data->drag_highlight = 0;
4151     gtk_drag_unhighlight (widget);
4152
4153 }
4154
4155 static gboolean
4156 hildon_calendar_drag_motion                     (GtkWidget *widget,
4157                                                  GdkDragContext *context,
4158                                                  gint x,
4159                                                  gint y,
4160                                                  guint time)
4161 {
4162     HildonCalendarPrivate *private_data;
4163     GdkAtom target;
4164
4165     private_data = HILDON_CALENDAR_GET_PRIVATE (widget);
4166
4167     if (!private_data->drag_highlight) 
4168     {
4169         private_data->drag_highlight = 1;
4170         gtk_drag_highlight (widget);
4171     }
4172
4173     target = gtk_drag_dest_find_target (widget, context, NULL);
4174     if (target == GDK_NONE)
4175         gdk_drag_status (context, 0, time);
4176     else {
4177         set_status_pending (context, context->suggested_action);
4178         gtk_drag_get_data (widget, context, target, time);
4179     }
4180
4181     return TRUE;
4182 }
4183
4184 static gboolean
4185 hildon_calendar_drag_drop                       (GtkWidget *widget,
4186                                                  GdkDragContext *context,
4187                                                  gint x,
4188                                                  gint y,
4189                                                  guint time)
4190 {
4191     GdkAtom target;
4192
4193     target = gtk_drag_dest_find_target (widget, context, NULL);  
4194     if (target != GDK_NONE)
4195     {
4196         gtk_drag_get_data (widget, context, 
4197                 target, 
4198                 time);
4199         return TRUE;
4200     }
4201
4202     return FALSE;
4203 }
4204
4205 static void
4206 hildon_calendar_drag_data_received              (GtkWidget *widget,
4207                                                  GdkDragContext *context,
4208                                                  gint x,
4209                                                  gint y,
4210                                                  GtkSelectionData *selection_data,
4211                                                  guint info,
4212                                                  guint time)
4213 {
4214     HildonCalendar *calendar = HILDON_CALENDAR (widget);
4215     guint day, month, year;
4216     gchar *str;
4217     GDate *date;
4218     GdkDragAction suggested_action;
4219
4220     suggested_action = get_status_pending (context);
4221
4222     if (suggested_action) 
4223     {
4224         set_status_pending (context, 0);
4225
4226         /* We are getting this data due to a request in drag_motion,
4227          * rather than due to a request in drag_drop, so we are just
4228          * supposed to call drag_status, not actually paste in the
4229          * data.
4230          */
4231         str = (gchar *) gtk_selection_data_get_text (selection_data);
4232         if (str) 
4233         {
4234             date = g_date_new ();
4235             g_date_set_parse (date, str);
4236             if (!g_date_valid (date)) 
4237                 suggested_action = 0;
4238             g_date_free (date);
4239             g_free (str);
4240         }
4241         else
4242             suggested_action = 0;
4243
4244         gdk_drag_status (context, suggested_action, time);
4245
4246         return;
4247     }
4248
4249     date = g_date_new ();
4250     str = (gchar *) gtk_selection_data_get_text (selection_data);
4251     if (str) 
4252     {
4253         g_date_set_parse (date, str);
4254         g_free (str);
4255     }
4256
4257     if (!g_date_valid (date)) 
4258     {
4259         g_warning ("Received invalid date data\n");
4260         g_date_free (date);       
4261         gtk_drag_finish (context, FALSE, FALSE, time);
4262         return;
4263     }
4264
4265     day = g_date_get_day (date);
4266     month = g_date_get_month (date);
4267     year = g_date_get_year (date);
4268     g_date_free (date);   
4269
4270     gtk_drag_finish (context, TRUE, FALSE, time);
4271
4272
4273     g_object_freeze_notify (G_OBJECT (calendar));
4274     if (!(calendar->display_flags & HILDON_CALENDAR_NO_MONTH_CHANGE)
4275             && (calendar->display_flags & HILDON_CALENDAR_SHOW_HEADING))
4276         hildon_calendar_select_month (calendar, month - 1, year);
4277     hildon_calendar_select_day (calendar, day);
4278     g_object_thaw_notify (G_OBJECT (calendar));  
4279 }
4280
4281 /* This function return TRUE if we should mark date and FALSE
4282  *  otherwise
4283  */
4284 static void
4285 hildon_calendar_check_current_date              (HildonCalendar *calendar, 
4286                                                  gint x, 
4287                                                  gint y)
4288 {
4289     HildonCalendarPrivate *private_data;
4290
4291     private_data = HILDON_CALENDAR_GET_PRIVATE (calendar);
4292
4293     if (calendar->month == private_data->current_month && 
4294             calendar->year == private_data->current_year)
4295     {
4296         gtk_paint_box( GTK_WIDGET (calendar)->style,
4297                 private_data->main_win,
4298                 GTK_STATE_NORMAL,
4299                 GTK_SHADOW_NONE, NULL,
4300                 GTK_WIDGET (calendar), "current-day",
4301                 x, y,
4302                 HILDON_DAY_WIDTH,
4303                 HILDON_DAY_HEIGHT);
4304     }
4305 }
4306
4307 #define                                         __HILDON_CALENDAR_C__