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