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