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