1 /*
2  * GTK - The GIMP Toolkit
3  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4  *
5  * GTK Calendar Widget
6  * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Groenlund
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free
20  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  *
23  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
24  * file for a list of people on the GTK+ Team.  See the ChangeLog
25  * files for a list of changes.  These files are distributed with
26  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27  *
28  * Modified for OSMO organizer by pasp@users.sourceforge.net
29  */
30 
31 #include <string.h>
32 #include <stdlib.h>
33 #include <time.h>
34 #include <math.h>
35 #include <sys/time.h>
36 
37 #include <glib.h>
38 
39 #include "i18n.h"
40 #include "gui.h"
41 #include "calendar_widget.h"
42 #include "calendar_utils.h"
43 #include "utils.h"
44 #include "utils_date.h"
45 
46 static void     gui_calendar_finalize           (GObject      *calendar);
47 static void     gui_calendar_dispose            (GObject      *calendar);
48 static void     gui_calendar_set_property       (GObject      *object,
49                                                  guint         prop_id,
50                                                  const GValue *value,
51                                                  GParamSpec   *pspec);
52 static void     gui_calendar_get_property       (GObject      *object,
53                                                  guint         prop_id,
54                                                  GValue       *value,
55                                                  GParamSpec   *pspec);
56 static void     gui_calendar_realize            (GtkWidget        *widget);
57 static void     gui_calendar_unrealize          (GtkWidget        *widget);
58 static void     gui_calendar_get_preferred_width(GtkWidget *widget,
59                                                  gint      *minimal_width,
60                                                  gint      *natural_width);
61 static void     gui_calendar_get_preferred_height(GtkWidget *widget,
62                                                  gint      *minimal_height,
63                                                  gint      *natural_height);
64 static void     gui_calendar_size_allocate      (GtkWidget        *widget,
65 											     GtkAllocation    *allocation);
66 static gboolean gui_calendar_draw               (GtkWidget        *widget,
67 											     cairo_t *cr);
68 static gboolean gui_calendar_button_press       (GtkWidget        *widget,
69 											     GdkEventButton   *event);
70 static gboolean gui_calendar_button_release     (GtkWidget        *widget,
71 											     GdkEventButton   *event);
72 static gboolean gui_calendar_motion_notify      (GtkWidget        *widget,
73 											     GdkEventMotion   *event);
74 static gboolean gui_calendar_enter_notify       (GtkWidget        *widget,
75 											     GdkEventCrossing *event);
76 static gboolean gui_calendar_leave_notify       (GtkWidget        *widget,
77 											     GdkEventCrossing *event);
78 static gboolean gui_calendar_scroll             (GtkWidget        *widget,
79 											     GdkEventScroll   *event);
80 static gboolean gui_calendar_key_press          (GtkWidget        *widget,
81 											     GdkEventKey      *event);
82 static gboolean gui_calendar_focus_out          (GtkWidget        *widget,
83 											     GdkEventFocus    *event);
84 static void     gui_calendar_grab_notify        (GtkWidget        *widget,
85 											     gboolean          was_grabbed);
86 static void     gui_calendar_state_changed      (GtkWidget        *widget,
87 											     GtkStateType      previous_state);
88 static void     gui_calendar_style_set          (GtkWidget        *widget,
89 											     GtkStyle         *previous_style);
90 static void     gui_calendar_drag_data_get      (GtkWidget        *widget,
91 											     GdkDragContext   *context,
92 											     GtkSelectionData *selection_data,
93 											     guint             info,
94 											     guint             time);
95 static void     gui_calendar_drag_data_received (GtkWidget        *widget,
96 												 GdkDragContext   *context,
97 												 gint              x,
98 												 gint              y,
99 												 GtkSelectionData *selection_data,
100 												 guint             info,
101 												 guint             time);
102 static gboolean gui_calendar_drag_motion        (GtkWidget        *widget,
103 												 GdkDragContext   *context,
104 												 gint              x,
105 												 gint              y,
106 												 guint             time);
107 static void     gui_calendar_drag_leave         (GtkWidget        *widget,
108 												 GdkDragContext   *context,
109 												 guint             time);
110 static gboolean gui_calendar_drag_drop          (GtkWidget        *widget,
111 												 GdkDragContext   *context,
112 												 gint              x,
113 												 gint              y,
114 												 guint             time);
115 static void     calendar_start_spinning         (GuiCalendar *calendar,
116                                                  gint         click_child);
117 static void     calendar_stop_spinning          (GuiCalendar *calendar);
118 static void     calendar_invalidate_day         (GuiCalendar *widget,
119 												 gint       row,
120 												 gint       col);
121 static void     calendar_invalidate_day_num     (GuiCalendar *widget,
122 												 gint       day);
123 static void     calendar_invalidate_arrow       (GuiCalendar *widget,
124 												 guint      arrow);
125 static void     calendar_compute_days           (GuiCalendar *calendar);
126 
127 
128 static guint gui_calendar_signals[LAST_SIGNAL] = { 0 };
129 static char    *default_abbreviated_dayname[7];
130 static char    *default_monthname[12];
131 
132 G_DEFINE_TYPE (GuiCalendar, gui_calendar, GTK_TYPE_WIDGET)
133 
134 
135 /*------------------------------------------------------------------------------*/
136 static void get_header_bg_color(GtkWidget *widget, GdkRGBA *color);
137 static void get_background_color(GtkWidget *widget, GdkRGBA *color);
138 /*------------------------------------------------------------------------------*/
139 
140 static void
gui_calendar_class_init(GuiCalendarClass * class)141 gui_calendar_class_init (GuiCalendarClass *class) {
142 
143     GObjectClass   *object_class;
144     GtkWidgetClass *widget_class;
145 
146     object_class = G_OBJECT_CLASS(class);
147     widget_class = GTK_WIDGET_CLASS(class);
148 
149     object_class->set_property = gui_calendar_set_property;
150     object_class->get_property = gui_calendar_get_property;
151     object_class->finalize = gui_calendar_finalize;
152     object_class->dispose = gui_calendar_dispose;
153 
154     widget_class->realize = gui_calendar_realize;
155     widget_class->unrealize = gui_calendar_unrealize;
156     widget_class->draw = gui_calendar_draw;
157     widget_class->get_preferred_width = gui_calendar_get_preferred_width;
158     widget_class->get_preferred_height = gui_calendar_get_preferred_height;
159     widget_class->size_allocate = gui_calendar_size_allocate;
160     widget_class->button_press_event = gui_calendar_button_press;
161     widget_class->button_release_event = gui_calendar_button_release;
162     widget_class->motion_notify_event = gui_calendar_motion_notify;
163     widget_class->enter_notify_event = gui_calendar_enter_notify;
164     widget_class->leave_notify_event = gui_calendar_leave_notify;
165     widget_class->key_press_event = gui_calendar_key_press;
166     widget_class->scroll_event = gui_calendar_scroll;
167     widget_class->style_set = gui_calendar_style_set;
168     widget_class->state_changed = gui_calendar_state_changed;
169     widget_class->grab_notify = gui_calendar_grab_notify;
170     widget_class->focus_out_event = gui_calendar_focus_out;
171 
172     widget_class->drag_data_get = gui_calendar_drag_data_get;
173     widget_class->drag_motion = gui_calendar_drag_motion;
174     widget_class->drag_leave = gui_calendar_drag_leave;
175     widget_class->drag_drop = gui_calendar_drag_drop;
176     widget_class->drag_data_received = gui_calendar_drag_data_received;
177 
178     g_object_class_install_property (object_class,
179                                      PROP_YEAR,
180                                      g_param_spec_int ("year",
181                                                        "Year",
182                                                        "The selected year",
183                                                        0, G_MAXINT, 0,
184                                                        G_PARAM_READWRITE));
185     g_object_class_install_property (object_class,
186                                      PROP_MONTH,
187                                      g_param_spec_int ("month",
188                                                        "Month",
189                                                        "The selected month (as a number between 0 and 11)",
190                                                        0, 11, 0,
191                                                        G_PARAM_READWRITE));
192     g_object_class_install_property (object_class,
193                                      PROP_DAY,
194                                      g_param_spec_int ("day",
195                                                        "Day",
196                                                        "The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)",
197                                                        0, 31, 0,
198                                                        G_PARAM_READWRITE));
199     g_object_class_install_property (object_class,
200                                      PROP_SHOW_HEADING,
201                                      g_param_spec_boolean ("show-heading",
202                                                            "Show Heading",
203                                                            "If TRUE, a heading is displayed", TRUE, G_PARAM_READWRITE));
204     g_object_class_install_property (object_class,
205                                      PROP_SHOW_DAY_NAMES,
206                                      g_param_spec_boolean ("show-day-names",
207                                                            "Show Day Names",
208                                                            "If TRUE, day names are displayed", TRUE, G_PARAM_READWRITE));
209     g_object_class_install_property (object_class,
210                                      PROP_NO_MONTH_CHANGE,
211                                      g_param_spec_boolean ("no-month-change",
212                                                            "No Month Change",
213                                                            "If TRUE, the selected month cannot be changed",
214                                                            FALSE,
215                                                            G_PARAM_READWRITE));
216     g_object_class_install_property (object_class,
217                                      PROP_SHOW_WEEK_NUMBERS,
218                                      g_param_spec_boolean ("show-week-numbers",
219                                                            "Show Week Numbers",
220                                                            "If TRUE, week numbers are displayed",
221                                                            FALSE,
222                                                            G_PARAM_READWRITE));
223     g_object_class_install_property (object_class,
224                                      PROP_WEEK_START_MONDAY,
225                                      g_param_spec_boolean ("week-start-monday",
226                                                            "Week Start Monday",
227                                                            "If TRUE, week starts at monday",
228                                                            FALSE,
229                                                            G_PARAM_READWRITE));
230 
231     gui_calendar_signals[MONTH_CHANGED_SIGNAL] =
232         g_signal_new ("month_changed",
233                       G_OBJECT_CLASS_TYPE (object_class),
234                       G_SIGNAL_RUN_FIRST,
235                       G_STRUCT_OFFSET (GuiCalendarClass, month_changed),
236                       NULL, NULL,
237                       g_cclosure_marshal_VOID__VOID,
238                       G_TYPE_NONE, 0);
239 
240     gui_calendar_signals[DAY_SELECTED_SIGNAL] =
241         g_signal_new ("day_selected",
242                       G_OBJECT_CLASS_TYPE (object_class),
243                       G_SIGNAL_RUN_FIRST,
244                       G_STRUCT_OFFSET (GuiCalendarClass, day_selected),
245                       NULL, NULL,
246                       g_cclosure_marshal_VOID__VOID,
247                       G_TYPE_NONE, 0);
248 
249     gui_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
250         g_signal_new ("day_selected_double_click",
251                       G_OBJECT_CLASS_TYPE (object_class),
252                       G_SIGNAL_RUN_FIRST,
253                       G_STRUCT_OFFSET (GuiCalendarClass, day_selected_double_click),
254                       NULL, NULL,
255                       g_cclosure_marshal_VOID__VOID,
256                       G_TYPE_NONE, 0);
257 
258     gui_calendar_signals[PREV_MONTH_SIGNAL] =
259         g_signal_new ("prev_month",
260                       G_OBJECT_CLASS_TYPE (object_class),
261                       G_SIGNAL_RUN_FIRST,
262                       G_STRUCT_OFFSET (GuiCalendarClass, prev_month),
263                       NULL, NULL,
264                       g_cclosure_marshal_VOID__VOID,
265                       G_TYPE_NONE, 0);
266 
267     gui_calendar_signals[NEXT_MONTH_SIGNAL] =
268         g_signal_new ("next_month",
269                       G_OBJECT_CLASS_TYPE (object_class),
270                       G_SIGNAL_RUN_FIRST,
271                       G_STRUCT_OFFSET (GuiCalendarClass, next_month),
272                       NULL, NULL,
273                       g_cclosure_marshal_VOID__VOID,
274                       G_TYPE_NONE, 0);
275 
276     gui_calendar_signals[PREV_YEAR_SIGNAL] =
277         g_signal_new ("prev_year",
278                       G_OBJECT_CLASS_TYPE (object_class),
279                       G_SIGNAL_RUN_FIRST,
280                       G_STRUCT_OFFSET (GuiCalendarClass, prev_year),
281                       NULL, NULL,
282                       g_cclosure_marshal_VOID__VOID,
283                       G_TYPE_NONE, 0);
284 
285     gui_calendar_signals[NEXT_YEAR_SIGNAL] =
286         g_signal_new ("next_year",
287                       G_OBJECT_CLASS_TYPE (object_class),
288                       G_SIGNAL_RUN_FIRST,
289                       G_STRUCT_OFFSET (GuiCalendarClass, next_year),
290                       NULL, NULL,
291                       g_cclosure_marshal_VOID__VOID,
292                       G_TYPE_NONE, 0);
293 
294     g_type_class_add_private (object_class, sizeof (GuiCalendarPrivate));
295 }
296 
297 /*------------------------------------------------------------------------------*/
298 
299 static void
gui_calendar_init(GuiCalendar * calendar)300 gui_calendar_init (GuiCalendar *calendar) {
301 
302 	GtkWidget *widget = GTK_WIDGET (calendar);
303     time_t secs;
304     struct tm *tm;
305     gint i;
306     char buffer[255];
307     time_t tmp_time;
308     GuiCalendarPrivate *priv;
309 
310     priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
311                             GUI_TYPE_CALENDAR,
312                             GuiCalendarPrivate);
313 
314     gtk_widget_set_can_focus (widget, TRUE);
315 
316     if (!default_abbreviated_dayname[0])
317         for (i=0; i<7; i++) {
318             tmp_time= (i+3)*86400;
319             strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
320             default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
321         }
322 
323     if (!default_monthname[0])
324         for (i=0; i<12; i++) {
325             tmp_time=i*2764800;
326             strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
327             default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
328         }
329 
330     /* Set defaults */
331     secs = time (NULL);
332     tm = localtime (&secs);
333     calendar->month = tm->tm_mon;
334     calendar->year  = 1900 + tm->tm_year;
335 
336     for (i=0;i<31;i++) {
337         calendar->marked_date[i] = FALSE;
338         calendar->event_marked_date[i] = FALSE;
339         calendar->birthday_marked_date[i] = FALSE;
340     }
341     calendar->num_marked_dates = 0;
342     calendar->event_num_marked_dates = 0;
343     calendar->birthday_num_marked_dates = 0;
344     calendar->selected_day = tm->tm_mday;
345 
346     calendar->display_flags = ( GUI_CALENDAR_SHOW_HEADING |
347                                 GUI_CALENDAR_SHOW_DAY_NAMES );
348 
349     calendar->highlight_row = -1;
350     calendar->highlight_col = -1;
351 
352     calendar->focus_row = -1;
353     calendar->focus_col = -1;
354 
355     priv->max_year_width = 0;
356     priv->max_month_width = 0;
357     priv->max_day_char_width = 0;
358     priv->max_week_char_width = 0;
359 
360     priv->max_day_char_ascent = 0;
361     priv->max_day_char_descent = 0;
362     priv->max_label_char_ascent = 0;
363     priv->max_label_char_descent = 0;
364 
365     priv->arrow_width = 10;
366 
367     priv->need_timer = 0;
368     priv->timer = 0;
369     priv->click_child = -1;
370 
371     priv->in_drag = 0;
372     priv->drag_highlight = 0;
373 
374     gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
375     gtk_drag_dest_add_text_targets (widget);
376 
377     priv->year_before = 0;
378 
379     gdk_rgba_parse(&calendar->background_color,      "#FFFFFF");
380     gdk_rgba_parse(&calendar->header_bg_color,       "#404048");
381     gdk_rgba_parse(&calendar->header_fg_color,       "#DDDDDD");
382     gdk_rgba_parse(&calendar->weekend_color,         "#880000");
383     gdk_rgba_parse(&calendar->selector_color,        "#86ABD9");
384     gdk_rgba_parse(&calendar->day_color,             "#555555");
385     gdk_rgba_parse(&calendar->pf_day_color,          "#CCCCCC");
386     gdk_rgba_parse(&calendar->event_marker_color,    "#CBE140");
387     gdk_rgba_parse(&calendar->today_marker_color,    "#E37751");
388     gdk_rgba_parse(&calendar->birthday_marker_color, "#EDD400");
389 
390     calendar->mark_sign = '\'';
391     calendar->frame_cursor_thickness = 2;
392     calendar->enable_cursor = TRUE;
393 
394     priv->year_before = 0;  /* years to be displayed before months */
395     priv->week_start  = 0;  /* sunday */
396 
397     calendar_compute_days (calendar);
398 }
399 
400 /*------------------------------------------------------------------------------*/
401 
402 static void
calendar_set_month_next(GuiCalendar * calendar)403 calendar_set_month_next (GuiCalendar *calendar) {
404     gint month_len;
405 
406     g_return_if_fail (GTK_IS_WIDGET (calendar));
407 
408     if (calendar->display_flags & GUI_CALENDAR_NO_MONTH_CHANGE)
409         return;
410 
411     if (calendar->month == 11) {
412         calendar->month = 0;
413         calendar->year++;
414     } else {
415         calendar->month++;
416     }
417 
418     calendar_compute_days (calendar);
419     g_signal_emit (calendar,
420                    gui_calendar_signals[NEXT_MONTH_SIGNAL],
421                    0);
422     g_signal_emit (calendar,
423                    gui_calendar_signals[MONTH_CHANGED_SIGNAL],
424                    0);
425 
426     month_len = utl_get_month_length(g_date_is_leap_year (calendar->year), calendar->month + 1);
427 
428     if (month_len < calendar->selected_day) {
429         calendar->selected_day = 0;
430         gui_calendar_select_day (calendar, month_len);
431     } else {
432         gui_calendar_select_day (calendar, calendar->selected_day);
433     }
434 
435     gtk_widget_queue_draw (GTK_WIDGET (calendar));
436 }
437 
438 /*------------------------------------------------------------------------------*/
439 
440 static void
calendar_set_year_prev(GuiCalendar * calendar)441 calendar_set_year_prev (GuiCalendar *calendar) {
442     gint month_len;
443 
444     g_return_if_fail (GTK_IS_WIDGET (calendar));
445 
446     calendar->year--;
447     calendar_compute_days (calendar);
448     g_signal_emit (calendar,
449                    gui_calendar_signals[PREV_YEAR_SIGNAL],
450                    0);
451     g_signal_emit (calendar,
452                    gui_calendar_signals[MONTH_CHANGED_SIGNAL],
453                    0);
454 
455     month_len = utl_get_month_length(g_date_is_leap_year (calendar->year), calendar->month + 1);
456 
457     if (month_len < calendar->selected_day) {
458         calendar->selected_day = 0;
459         gui_calendar_select_day (calendar, month_len);
460     } else {
461         gui_calendar_select_day (calendar, calendar->selected_day);
462     }
463 
464     gtk_widget_queue_draw (GTK_WIDGET (calendar));
465 }
466 
467 /*------------------------------------------------------------------------------*/
468 
469 static void
calendar_set_year_next(GuiCalendar * calendar)470 calendar_set_year_next (GuiCalendar *calendar) {
471     gint month_len;
472 
473     g_return_if_fail (GTK_IS_WIDGET (calendar));
474 
475     calendar->year++;
476     calendar_compute_days (calendar);
477     g_signal_emit (calendar,
478                    gui_calendar_signals[NEXT_YEAR_SIGNAL],
479                    0);
480     g_signal_emit (calendar,
481                    gui_calendar_signals[MONTH_CHANGED_SIGNAL],
482                    0);
483 
484     month_len = utl_get_month_length(g_date_is_leap_year (calendar->year), calendar->month + 1);
485 
486     if (month_len < calendar->selected_day) {
487         calendar->selected_day = 0;
488         gui_calendar_select_day (calendar, month_len);
489     } else {
490         gui_calendar_select_day (calendar, calendar->selected_day);
491     }
492 
493     gtk_widget_queue_draw (GTK_WIDGET (calendar));
494 }
495 
496 /*------------------------------------------------------------------------------*/
497 
498 static void
calendar_compute_days(GuiCalendar * calendar)499 calendar_compute_days (GuiCalendar *calendar) {
500 
501 	GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
502     gint month;
503     gint year;
504     gint ndays_in_month;
505     gint ndays_in_prev_month;
506     gint first_day;
507     gint row;
508     gint col;
509     gint day;
510 
511     g_return_if_fail (GUI_IS_CALENDAR (calendar));
512 
513     year = calendar->year;
514     month = calendar->month + 1;
515 
516     ndays_in_month = utl_get_month_length(g_date_is_leap_year (year), month);
517 
518     first_day = utl_day_of_week (year, month, 1);
519     first_day = (first_day + 7 - priv->week_start) % 7;
520 
521     /* Compute days of previous month */
522     if (month > 1) {
523         ndays_in_prev_month = utl_get_month_length(g_date_is_leap_year (year), month-1);
524     } else {
525         ndays_in_prev_month = utl_get_month_length(g_date_is_leap_year (year), 12);
526     }
527 
528     day = ndays_in_prev_month - first_day + 1;
529 
530     row = 0;
531     if (first_day > 0) {
532         for (col = 0; col < first_day; col++) {
533             calendar->day[row][col] = day;
534             calendar->day_month[row][col] = MONTH_PREV;
535             day++;
536         }
537     }
538 
539     /* Compute days of current month */
540     col = first_day;
541     for (day = 1; day <= ndays_in_month; day++) {
542         calendar->day[row][col] = day;
543         calendar->day_month[row][col] = MONTH_CURRENT;
544 
545         col++;
546         if (col == 7) {
547             row++;
548             col = 0;
549         }
550     }
551 
552     /* Compute days of next month */
553     day = 1;
554     for (; row <= 5; row++) {
555         for (; col <= 6; col++) {
556             calendar->day[row][col] = day;
557             calendar->day_month[row][col] = MONTH_NEXT;
558             day++;
559         }
560         col = 0;
561     }
562 }
563 
564 /*------------------------------------------------------------------------------*/
565 
566 static void
calendar_select_and_focus_day(GuiCalendar * calendar,guint day)567 calendar_select_and_focus_day (GuiCalendar *calendar,
568                                guint        day) {
569 
570 	gint old_focus_row = calendar->focus_row;
571     gint old_focus_col = calendar->focus_col;
572     gint row;
573     gint col;
574 
575     for (row = 0; row < 6; row ++)
576         for (col = 0; col < 7; col++) {
577             if (calendar->day_month[row][col] == MONTH_CURRENT
578                     && calendar->day[row][col] == day) {
579                 calendar->focus_row = row;
580                 calendar->focus_col = col;
581             }
582         }
583 
584     if (old_focus_row != -1 && old_focus_col != -1) {
585         calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
586     }
587 
588     gui_calendar_select_day (calendar, day);
589 }
590 
591 /*------------------------------------------------------------------------------*/
592 
593 static gint
calendar_row_height(GuiCalendar * calendar)594 calendar_row_height (GuiCalendar *calendar) {
595     return (GUI_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
596             - ((calendar->display_flags & GUI_CALENDAR_SHOW_DAY_NAMES)
597                ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
598 }
599 
600 /*------------------------------------------------------------------------------*/
601 /* calendar_left_x_for_column: returns the x coordinate
602  * for the left of the column */
603 
604 static gint
calendar_left_x_for_column(GuiCalendar * calendar,gint column)605 calendar_left_x_for_column (GuiCalendar *calendar,
606                             gint     column) {
607     gint width;
608     gint x_left;
609 
610     if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
611         column = 6 - column;
612 
613     width = GUI_CALENDAR_GET_PRIVATE (calendar)->day_width;
614 
615     if (calendar->display_flags & GUI_CALENDAR_SHOW_WEEK_NUMBERS) {
616         x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
617     } else {
618         x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
619     }
620 
621     return x_left;
622 }
623 
624 /*------------------------------------------------------------------------------*/
625 /* column_from_x: returns the column 0-6 that the
626  * x pixel of the xwindow is in */
627 
628 static gint
calendar_column_from_x(GuiCalendar * calendar,gint event_x)629 calendar_column_from_x (GuiCalendar *calendar,
630                         gint         event_x) {
631     gint c, column;
632     gint x_left, x_right;
633 
634     column = -1;
635 
636     for (c = 0; c < 7; c++) {
637         x_left = calendar_left_x_for_column (calendar, c);
638         x_right = x_left + GUI_CALENDAR_GET_PRIVATE (calendar)->day_width;
639 
640         if (event_x >= x_left && event_x < x_right) {
641             column = c;
642             break;
643         }
644     }
645 
646     return column;
647 }
648 
649 /*------------------------------------------------------------------------------*/
650 /* calendar_top_y_for_row: returns the y coordinate
651  * for the top of the row */
652 
653 static gint
calendar_top_y_for_row(GuiCalendar * calendar,gint row)654 calendar_top_y_for_row (GuiCalendar *calendar,
655                         gint         row) {
656 
657     return (GUI_CALENDAR_GET_PRIVATE (calendar)->main_h
658             - (CALENDAR_MARGIN + (6 - row)
659                * calendar_row_height (calendar)));
660 }
661 
662 /*------------------------------------------------------------------------------*/
663 /* row_from_y: returns the row 0-5 that the
664  * y pixel of the xwindow is in */
665 
666 static gint
calendar_row_from_y(GuiCalendar * calendar,gint event_y)667 calendar_row_from_y (GuiCalendar *calendar,
668                      gint     event_y) {
669     gint r, row;
670     gint height;
671     gint y_top, y_bottom;
672 
673     height = calendar_row_height (calendar);
674     row = -1;
675 
676     for (r = 0; r < 6; r++) {
677         y_top = calendar_top_y_for_row (calendar, r);
678         y_bottom = y_top + height;
679 
680         if (event_y >= y_top && event_y < y_bottom) {
681             row = r;
682             break;
683         }
684     }
685 
686     return row;
687 }
688 
689 /*------------------------------------------------------------------------------*/
690 
691 static void
calendar_arrow_rectangle(GuiCalendar * calendar,guint arrow,GdkRectangle * rect)692 calendar_arrow_rectangle (GuiCalendar  *calendar,
693                           guint         arrow,
694                           GdkRectangle *rect) {
695 
696 	GtkWidget *widget = GTK_WIDGET (calendar);
697     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
698     gboolean year_left;
699     GtkBorder border;
700 
701     if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) {
702         year_left = priv->year_before;
703     } else {
704         year_left = !priv->year_before;
705     }
706 
707     rect->y = 3;
708     rect->width = priv->arrow_width;
709     rect->height = priv->header_h - 7;
710     gtk_style_context_get_border(gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, &border);
711 
712     switch (arrow) {
713     case ARROW_MONTH_LEFT:
714         if (year_left) {
715             rect->x = (gtk_widget_get_allocated_width(widget) - border.left - border.right
716                        - (3 + 2*priv->arrow_width
717                           + priv->max_month_width));
718         } else {
719             rect->x = 3;
720         }
721         break;
722     case ARROW_MONTH_RIGHT:
723         if (year_left) {
724             rect->x = (gtk_widget_get_allocated_width(widget) - border.left - border.right
725                        - 3 - priv->arrow_width);
726         } else {
727             rect->x = (priv->arrow_width
728                        + priv->max_month_width);
729         }
730         break;
731     case ARROW_YEAR_LEFT:
732         if (year_left) {
733             rect->x = 3;
734         } else {
735             rect->x = (gtk_widget_get_allocated_width(widget) - border.left - border.right
736                        - (3 + 2*priv->arrow_width
737                           + priv->max_year_width));
738         }
739         break;
740     case ARROW_YEAR_RIGHT:
741         if (year_left) {
742             rect->x = (priv->arrow_width
743                        + priv->max_year_width);
744         } else {
745             rect->x = (gtk_widget_get_allocated_width(widget) - border.left - border.right
746                        - 3 - priv->arrow_width);
747         }
748         break;
749     }
750 }
751 
752 /*------------------------------------------------------------------------------*/
753 
754 static void
calendar_day_rectangle(GuiCalendar * calendar,gint row,gint col,GdkRectangle * rect)755 calendar_day_rectangle (GuiCalendar  *calendar,
756                         gint          row,
757                         gint          col,
758                         GdkRectangle *rect) {
759 
760 	GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
761 
762     rect->x = calendar_left_x_for_column (calendar, col);
763     rect->y = calendar_top_y_for_row (calendar, row);
764     rect->height = calendar_row_height (calendar);
765     rect->width = priv->day_width;
766 }
767 
768 /*------------------------------------------------------------------------------*/
769 
770 static void
calendar_set_month_prev(GuiCalendar * calendar)771 calendar_set_month_prev (GuiCalendar *calendar) {
772     gint month_len;
773 
774     if (calendar->display_flags & GUI_CALENDAR_NO_MONTH_CHANGE)
775         return;
776 
777     if (calendar->month == 0) {
778         calendar->month = 11;
779         calendar->year--;
780     } else {
781         calendar->month--;
782     }
783 
784     month_len = utl_get_month_length(g_date_is_leap_year (calendar->year), calendar->month + 1);
785 
786     calendar_compute_days (calendar);
787 
788     g_signal_emit (calendar,
789                    gui_calendar_signals[PREV_MONTH_SIGNAL],
790                    0);
791     g_signal_emit (calendar,
792                    gui_calendar_signals[MONTH_CHANGED_SIGNAL],
793                    0);
794 
795     if (month_len < calendar->selected_day) {
796         calendar->selected_day = 0;
797         gui_calendar_select_day (calendar, month_len);
798     } else {
799         if (calendar->selected_day < 0)
800             calendar->selected_day = calendar->selected_day + 1 + utl_get_month_length(g_date_is_leap_year (calendar->year), calendar->month + 1);
801         gui_calendar_select_day (calendar, calendar->selected_day);
802     }
803 
804     gtk_widget_queue_draw (GTK_WIDGET (calendar));
805 }
806 
807 /*------------------------------------------------------------------------------*/
808 
809 static void
gui_calendar_finalize(GObject * object)810 gui_calendar_finalize (GObject *object) {
811     (* G_OBJECT_CLASS (gui_calendar_parent_class)->finalize) (object);
812 }
813 
814 /*------------------------------------------------------------------------------*/
815 
816 static void
gui_calendar_dispose(GObject * object)817 gui_calendar_dispose (GObject *object) {
818     calendar_stop_spinning (GUI_CALENDAR (object));
819 
820     G_OBJECT_CLASS (gui_calendar_parent_class)->dispose (object);
821 }
822 
823 /*------------------------------------------------------------------------------*/
824 
825 static void
calendar_set_display_option(GuiCalendar * calendar,GuiCalendarDisplayOptions flag,gboolean setting)826 calendar_set_display_option (GuiCalendar              *calendar,
827                              GuiCalendarDisplayOptions flag,
828                              gboolean                  setting) {
829     GuiCalendarDisplayOptions flags;
830     if (setting) {
831         flags = calendar->display_flags | flag;
832     } else {
833         flags = calendar->display_flags & ~flag;
834     }
835     gui_calendar_set_display_options (calendar, flags);
836 }
837 
838 /*------------------------------------------------------------------------------*/
839 
840 static gboolean
calendar_get_display_option(GuiCalendar * calendar,GuiCalendarDisplayOptions flag)841 calendar_get_display_option (GuiCalendar              *calendar,
842                              GuiCalendarDisplayOptions flag) {
843     return (calendar->display_flags & flag) != 0;
844 }
845 
846 /*------------------------------------------------------------------------------*/
847 
848 static void
gui_calendar_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)849 gui_calendar_set_property (GObject      *object,
850                            guint         prop_id,
851                            const GValue *value,
852                            GParamSpec   *pspec) {
853     GuiCalendar *calendar;
854 
855     calendar = GUI_CALENDAR (object);
856 
857     switch (prop_id) {
858     case PROP_YEAR:
859         gui_calendar_select_month (calendar,
860                                    calendar->month,
861                                    g_value_get_int (value));
862         break;
863     case PROP_MONTH:
864         gui_calendar_select_month (calendar,
865                                    g_value_get_int (value),
866                                    calendar->year);
867         break;
868     case PROP_DAY:
869         gui_calendar_select_day (calendar,
870                                  g_value_get_int (value));
871         break;
872     case PROP_SHOW_HEADING:
873         calendar_set_display_option (calendar,
874                                      GUI_CALENDAR_SHOW_HEADING,
875                                      g_value_get_boolean (value));
876         break;
877     case PROP_SHOW_DAY_NAMES:
878         calendar_set_display_option (calendar,
879                                      GUI_CALENDAR_SHOW_DAY_NAMES,
880                                      g_value_get_boolean (value));
881         break;
882     case PROP_NO_MONTH_CHANGE:
883         calendar_set_display_option (calendar,
884                                      GUI_CALENDAR_NO_MONTH_CHANGE,
885                                      g_value_get_boolean (value));
886         break;
887     case PROP_SHOW_WEEK_NUMBERS:
888         calendar_set_display_option (calendar,
889                                      GUI_CALENDAR_SHOW_WEEK_NUMBERS,
890                                      g_value_get_boolean (value));
891         break;
892     case PROP_WEEK_START_MONDAY:
893         calendar_set_display_option (calendar,
894                                      GUI_CALENDAR_WEEK_START_MONDAY,
895                                      g_value_get_boolean (value));
896         break;
897     default:
898         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
899         break;
900     }
901 }
902 
903 /*------------------------------------------------------------------------------*/
904 
905 static void
gui_calendar_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)906 gui_calendar_get_property (GObject      *object,
907                            guint         prop_id,
908                            GValue       *value,
909                            GParamSpec   *pspec) {
910     GuiCalendar *calendar;
911 
912     calendar = GUI_CALENDAR (object);
913 
914     switch (prop_id) {
915     case PROP_YEAR:
916         g_value_set_int (value, calendar->year);
917         break;
918     case PROP_MONTH:
919         g_value_set_int (value, calendar->month);
920         break;
921     case PROP_DAY:
922         g_value_set_int (value, calendar->selected_day);
923         break;
924     case PROP_SHOW_HEADING:
925         g_value_set_boolean (value, calendar_get_display_option (calendar,
926                              GUI_CALENDAR_SHOW_HEADING));
927         break;
928     case PROP_SHOW_DAY_NAMES:
929         g_value_set_boolean (value, calendar_get_display_option (calendar,
930                              GUI_CALENDAR_SHOW_DAY_NAMES));
931         break;
932     case PROP_NO_MONTH_CHANGE:
933         g_value_set_boolean (value, calendar_get_display_option (calendar,
934                              GUI_CALENDAR_NO_MONTH_CHANGE));
935         break;
936     case PROP_SHOW_WEEK_NUMBERS:
937         g_value_set_boolean (value, calendar_get_display_option (calendar,
938                              GUI_CALENDAR_SHOW_WEEK_NUMBERS));
939         break;
940     case PROP_WEEK_START_MONDAY:
941         g_value_set_boolean (value, calendar_get_display_option (calendar,
942                              GUI_CALENDAR_WEEK_START_MONDAY));
943         break;
944     default:
945         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
946         break;
947     }
948 }
949 
950 /*------------------------------------------------------------------------------*/
951 
952 static void
calendar_realize_arrows(GuiCalendar * calendar)953 calendar_realize_arrows (GuiCalendar *calendar) {
954     GtkWidget *widget = GTK_WIDGET (calendar);
955     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
956     GdkWindowAttr attributes;
957     GdkRGBA color;
958     gint attributes_mask;
959     gint i;
960 
961     /* arrow windows */
962     if (! (calendar->display_flags & GUI_CALENDAR_NO_MONTH_CHANGE)
963             && (calendar->display_flags & GUI_CALENDAR_SHOW_HEADING)) {
964         attributes.wclass = GDK_INPUT_OUTPUT;
965         attributes.window_type = GDK_WINDOW_CHILD;
966         attributes.visual = gtk_widget_get_visual (widget);
967         attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
968                                  | GDK_BUTTON_PRESS_MASK    | GDK_BUTTON_RELEASE_MASK
969                                  | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
970         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
971         for (i = 0; i < 4; i++) {
972             GdkRectangle rect;
973             calendar_arrow_rectangle (calendar, i, &rect);
974 
975             attributes.x = rect.x;
976             attributes.y = rect.y;
977             attributes.width = rect.width;
978             attributes.height = rect.height;
979             priv->arrow_win[i] = gdk_window_new (priv->header_win,
980                                                  &attributes,
981                                                  attributes_mask);
982             if (gtk_widget_is_sensitive (widget))
983                 priv->arrow_state[i] = GTK_STATE_FLAG_NORMAL;
984             else
985                 priv->arrow_state[i] = GTK_STATE_FLAG_INSENSITIVE;
986             get_header_bg_color(widget, &color);
987             gdk_window_set_background_rgba(priv->arrow_win[i], &color);
988             gdk_window_show (priv->arrow_win[i]);
989             gdk_window_set_user_data (priv->arrow_win[i], widget);
990         }
991     } else {
992         for (i = 0; i < 4; i++)
993             priv->arrow_win[i] = NULL;
994     }
995 }
996 
997 /*------------------------------------------------------------------------------*/
998 
999 static void
calendar_realize_header(GuiCalendar * calendar)1000 calendar_realize_header (GuiCalendar *calendar) {
1001     GtkWidget *widget = GTK_WIDGET (calendar);
1002     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
1003     GdkWindowAttr attributes;
1004     GdkRGBA color;
1005     gint attributes_mask;
1006 
1007     /* header window */
1008     if (calendar->display_flags & GUI_CALENDAR_SHOW_HEADING) {
1009         GtkBorder border;
1010         attributes.wclass = GDK_INPUT_OUTPUT;
1011         attributes.window_type = GDK_WINDOW_CHILD;
1012         attributes.visual = gtk_widget_get_visual (widget);
1013         attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1014         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1015         gtk_style_context_get_border(gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, &border);
1016         attributes.x = border.left;
1017         attributes.y = border.top;
1018         attributes.width = gtk_widget_get_allocated_width(widget) - 2 * attributes.x;
1019         attributes.height = priv->header_h - 2 * attributes.y;
1020         priv->header_win = gdk_window_new (gtk_widget_get_window(widget),
1021                                            &attributes, attributes_mask);
1022 
1023         get_header_bg_color(widget, &color);
1024         gdk_window_set_background_rgba (priv->header_win, &color);
1025         gdk_window_show (priv->header_win);
1026         gdk_window_set_user_data (priv->header_win, widget);
1027 
1028     } else {
1029         priv->header_win = NULL;
1030     }
1031     calendar_realize_arrows (calendar);
1032 }
1033 
1034 /*------------------------------------------------------------------------------*/
1035 
1036 static void
calendar_realize_day_names(GuiCalendar * calendar)1037 calendar_realize_day_names (GuiCalendar *calendar) {
1038     GtkWidget *widget = GTK_WIDGET (calendar);
1039     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
1040     GdkWindowAttr attributes;
1041     gint attributes_mask;
1042 
1043     /* day names window */
1044     if ( calendar->display_flags & GUI_CALENDAR_SHOW_DAY_NAMES) {
1045         GtkBorder border;
1046         attributes.wclass = GDK_INPUT_OUTPUT;
1047         attributes.window_type = GDK_WINDOW_CHILD;
1048         attributes.visual = gtk_widget_get_visual (widget);
1049         attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1050         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1051         gtk_style_context_get_border(gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, &border);
1052         attributes.x = (border.left + INNER_BORDER);
1053         attributes.y = priv->header_h + (border.top + INNER_BORDER);
1054         attributes.width = gtk_widget_get_allocated_width(widget) - border.left - border.right - 2 * INNER_BORDER;
1055         attributes.height = priv->day_name_h;
1056         priv->day_name_win = gdk_window_new (gtk_widget_get_window(widget),
1057                                              &attributes,
1058                                              attributes_mask);
1059         gdk_window_show (priv->day_name_win);
1060         gdk_window_set_user_data (priv->day_name_win, widget);
1061     } else {
1062         priv->day_name_win = NULL;
1063     }
1064 }
1065 
1066 /*------------------------------------------------------------------------------*/
1067 
1068 static void
calendar_realize_week_numbers(GuiCalendar * calendar)1069 calendar_realize_week_numbers (GuiCalendar *calendar) {
1070     GtkWidget *widget = GTK_WIDGET (calendar);
1071     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
1072     GdkWindowAttr attributes;
1073     gint attributes_mask;
1074 
1075     /* week number window */
1076     if (calendar->display_flags & GUI_CALENDAR_SHOW_WEEK_NUMBERS) {
1077         GtkBorder border;
1078         attributes.wclass = GDK_INPUT_OUTPUT;
1079         attributes.window_type = GDK_WINDOW_CHILD;
1080         attributes.visual = gtk_widget_get_visual (widget);
1081         attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1082 
1083         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1084         gtk_style_context_get_border(gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, &border);
1085         attributes.x = border.left + INNER_BORDER;
1086         attributes.y = (priv->header_h + priv->day_name_h + (border.top + INNER_BORDER));
1087         attributes.width = priv->week_width;
1088         attributes.height = priv->main_h;
1089         priv->week_win = gdk_window_new (gtk_widget_get_window(widget),
1090                                          &attributes, attributes_mask);
1091         gdk_window_show (priv->week_win);
1092         gdk_window_set_user_data (priv->week_win, widget);
1093     } else {
1094         priv->week_win = NULL;
1095     }
1096 }
1097 
1098 /*------------------------------------------------------------------------------*/
1099 
1100 static void
gui_calendar_realize(GtkWidget * widget)1101 gui_calendar_realize (GtkWidget *widget) {
1102     GuiCalendar *calendar = GUI_CALENDAR (widget);
1103     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
1104     GdkWindowAttr attributes;
1105     GtkAllocation allocation;
1106     GdkWindow *window;
1107     GtkBorder border;
1108     gint attributes_mask;
1109 
1110     gtk_widget_set_realized (widget, TRUE);
1111     gtk_widget_get_allocation(widget, &allocation);
1112 
1113     attributes.x = allocation.x;
1114     attributes.y = allocation.y;
1115     attributes.width = allocation.width;
1116     attributes.height = allocation.height;
1117     attributes.wclass = GDK_INPUT_OUTPUT;
1118     attributes.window_type = GDK_WINDOW_CHILD;
1119     attributes.event_mask =  (gtk_widget_get_events (widget)
1120                               | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1121     attributes.visual = gtk_widget_get_visual (widget);
1122 
1123     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1124     window = gdk_window_new (gtk_widget_get_parent_window(widget),
1125                                      &attributes, attributes_mask);
1126     gtk_widget_set_window (widget, window);
1127     gtk_widget_register_window (widget, window);
1128 
1129     /* header window */
1130     calendar_realize_header (calendar);
1131     /* day names window */
1132     calendar_realize_day_names (calendar);
1133     /* week number window */
1134     calendar_realize_week_numbers (calendar);
1135     /* main window */
1136     attributes.event_mask =  (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1137                               | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1138                               | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1139 
1140     gtk_style_context_get_border(gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, &border);
1141 
1142     attributes.x = priv->week_width + (border.left + INNER_BORDER);
1143     attributes.y = (priv->header_h + priv->day_name_h + (border.top + INNER_BORDER));
1144     attributes.width = (allocation.width - attributes.x - (border.right + INNER_BORDER));
1145     attributes.height = priv->main_h;
1146     priv->main_win = gdk_window_new (gtk_widget_get_window(widget),
1147                                      &attributes, attributes_mask);
1148     gdk_window_show (priv->main_win);
1149     gdk_window_set_user_data (priv->main_win, widget);
1150     gdk_window_show (gtk_widget_get_window(widget));
1151     gdk_window_set_user_data (gtk_widget_get_window(widget), widget);
1152 }
1153 
1154 /*------------------------------------------------------------------------------*/
1155 
1156 static void
gui_calendar_unrealize(GtkWidget * widget)1157 gui_calendar_unrealize (GtkWidget *widget) {
1158     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
1159     gint i;
1160 
1161     if (priv->header_win) {
1162         for (i = 0; i < 4; i++) {
1163             if (priv->arrow_win[i]) {
1164                 gdk_window_set_user_data (priv->arrow_win[i], NULL);
1165                 gdk_window_destroy (priv->arrow_win[i]);
1166                 priv->arrow_win[i] = NULL;
1167             }
1168         }
1169         gdk_window_set_user_data (priv->header_win, NULL);
1170         gdk_window_destroy (priv->header_win);
1171         priv->header_win = NULL;
1172     }
1173 
1174     if (priv->week_win) {
1175         gdk_window_set_user_data (priv->week_win, NULL);
1176         gdk_window_destroy (priv->week_win);
1177         priv->week_win = NULL;
1178     }
1179 
1180     if (priv->main_win) {
1181         gdk_window_set_user_data (priv->main_win, NULL);
1182         gdk_window_destroy (priv->main_win);
1183         priv->main_win = NULL;
1184     }
1185     if (priv->day_name_win) {
1186         gdk_window_set_user_data (priv->day_name_win, NULL);
1187         gdk_window_destroy (priv->day_name_win);
1188         priv->day_name_win = NULL;
1189     }
1190 
1191     if (GTK_WIDGET_CLASS (gui_calendar_parent_class)->unrealize)
1192         (* GTK_WIDGET_CLASS (gui_calendar_parent_class)->unrealize) (widget);
1193 }
1194 
1195 /*------------------------------------------------------------------------------*/
1196 
1197 static void
gui_calendar_size_request(GtkWidget * widget,GtkRequisition * requisition)1198 gui_calendar_size_request (GtkWidget      *widget,
1199                            GtkRequisition *requisition) {
1200     GuiCalendar *calendar = GUI_CALENDAR (widget);
1201     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
1202     PangoLayout *layout;
1203     PangoRectangle logical_rect;
1204 
1205     gint height;
1206     gint i;
1207     gint calendar_margin = CALENDAR_MARGIN;
1208     gint header_width, main_width;
1209     gint max_header_height = 0;
1210     gint focus_width;
1211     gint focus_padding;
1212     GtkBorder border;
1213 
1214     gtk_widget_style_get (GTK_WIDGET (widget),
1215                           "focus-line-width", &focus_width,
1216                           "focus-padding", &focus_padding,
1217                           NULL);
1218 
1219     layout = gtk_widget_create_pango_layout (widget, NULL);
1220 
1221     /* calculate the requisition width for the widget */
1222 
1223     /* header width */
1224 
1225     if (calendar->display_flags & GUI_CALENDAR_SHOW_HEADING) {
1226         priv->max_month_width = 0;
1227         for (i = 0; i < 12; i++) {
1228             pango_layout_set_text (layout, default_monthname[i], -1);
1229             pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1230             priv->max_month_width = MAX (priv->max_month_width,
1231                                          logical_rect.width + 8);
1232             max_header_height = MAX (max_header_height, logical_rect.height);
1233         }
1234 
1235         priv->max_year_width = 0;
1236         pango_layout_set_text (layout, "2000", -1); /* the widest year text */
1237         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1238         priv->max_year_width = MAX (priv->max_year_width,
1239                                     logical_rect.width + 8);
1240         max_header_height = MAX (max_header_height, logical_rect.height);
1241     } else {
1242         priv->max_month_width = 0;
1243         priv->max_year_width = 0;
1244     }
1245 
1246     if (calendar->display_flags & GUI_CALENDAR_NO_MONTH_CHANGE)
1247         header_width = (priv->max_month_width
1248                         + priv->max_year_width
1249                         + 3 * 3);
1250     else
1251         header_width = (priv->max_month_width
1252                         + priv->max_year_width
1253                         + 4 * priv->arrow_width + 3 * 3);
1254 
1255     /* mainwindow labels width */
1256 
1257     priv->max_day_char_width = 0;
1258     priv->max_day_char_ascent = 0;
1259     priv->max_day_char_descent = 0;
1260     priv->min_day_width = 0;
1261 
1262     for (i = 0; i < 9; i++) {
1263         gchar buffer[32];
1264 
1265         g_snprintf (buffer, sizeof (buffer), "%d", i * 11);     /* %d - calendar day digits */
1266         pango_layout_set_text (layout, buffer, -1);
1267         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1268         priv->min_day_width = MAX (priv->min_day_width,
1269                                    logical_rect.width);
1270 
1271         priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1272                                          PANGO_ASCENT (logical_rect));
1273         priv->max_day_char_descent = MAX (priv->max_day_char_descent,
1274                                           PANGO_DESCENT (logical_rect));
1275     }
1276     /* We add one to max_day_char_width to be able to make the marked day "bold" */
1277     priv->max_day_char_width = priv->min_day_width / 2 + 1;
1278 
1279     priv->max_label_char_ascent = 0;
1280     priv->max_label_char_descent = 0;
1281     if (calendar->display_flags & GUI_CALENDAR_SHOW_DAY_NAMES)
1282         for (i = 0; i < 7; i++) {
1283             pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1284             pango_layout_line_get_pixel_extents (pango_layout_get_lines (layout)->data, NULL, &logical_rect);
1285 
1286             priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1287             priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1288                                                PANGO_ASCENT (logical_rect));
1289             priv->max_label_char_descent = MAX (priv->max_label_char_descent,
1290                                                 PANGO_DESCENT (logical_rect));
1291         }
1292 
1293     priv->max_week_char_width = 0;
1294     if (calendar->display_flags & GUI_CALENDAR_SHOW_WEEK_NUMBERS)
1295         for (i = 0; i < 9; i++) {
1296             gchar buffer[32];
1297             g_snprintf (buffer, sizeof (buffer), "%d", i * 11);     /* %d - calendar week digits */
1298             pango_layout_set_text (layout, buffer, -1);
1299             pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1300             priv->max_week_char_width = MAX (priv->max_week_char_width,
1301                                              logical_rect.width / 2);
1302         }
1303 
1304     main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1305                   + (priv->max_week_char_width
1306                      ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1307                      : 0));
1308 
1309     gtk_style_context_get_border(gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, &border);
1310     requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + border.left + border.right;
1311 
1312     /* calculate the requisition height for the widget */
1313 
1314     if (calendar->display_flags & GUI_CALENDAR_SHOW_HEADING) {
1315         priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1316     } else {
1317         priv->header_h = 0;
1318     }
1319 
1320     if (calendar->display_flags & GUI_CALENDAR_SHOW_DAY_NAMES) {
1321         priv->day_name_h = (priv->max_label_char_ascent
1322                             + priv->max_label_char_descent
1323                             + 2 * (focus_padding + focus_width) + calendar_margin);
1324         calendar_margin = CALENDAR_YSEP;
1325     } else {
1326         priv->day_name_h = 0;
1327     }
1328 
1329     priv->main_h = (CALENDAR_MARGIN + calendar_margin
1330                     + 6 * (priv->max_day_char_ascent
1331                            + priv->max_day_char_descent
1332                            + 2 * (focus_padding + focus_width))
1333                     + DAY_YSEP * 5);
1334 
1335     height = (priv->header_h + priv->day_name_h
1336               + priv->main_h);
1337 
1338     requisition->height = height + border.top + border.bottom + 2 * INNER_BORDER;
1339 
1340     g_object_unref (layout);
1341 }
1342 
1343 static void
gui_calendar_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)1344 gui_calendar_get_preferred_width (GtkWidget *widget,
1345                                gint      *minimal_width,
1346                                gint      *natural_width)
1347 {
1348   GtkRequisition requisition;
1349 
1350   gui_calendar_size_request (widget, &requisition);
1351 
1352   *minimal_width = *natural_width = requisition.width;
1353 }
1354 
1355 static void
gui_calendar_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)1356 gui_calendar_get_preferred_height (GtkWidget *widget,
1357                                 gint      *minimal_height,
1358                                 gint      *natural_height)
1359 {
1360   GtkRequisition requisition;
1361 
1362   gui_calendar_size_request (widget, &requisition);
1363 
1364   *minimal_height = *natural_height = requisition.height;
1365 }
1366 
1367 /*------------------------------------------------------------------------------*/
1368 
1369 static void
gui_calendar_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1370 gui_calendar_size_allocate (GtkWidget     *widget,
1371                             GtkAllocation *allocation) {
1372     GuiCalendar *calendar = GUI_CALENDAR (widget);
1373     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
1374     GtkBorder border;
1375     guint i;
1376 
1377     gtk_style_context_get_border(gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, &border);
1378 
1379     gtk_widget_set_allocation(widget, allocation);
1380 
1381     if (calendar->display_flags & GUI_CALENDAR_SHOW_WEEK_NUMBERS) {
1382         priv->day_width = (priv->min_day_width
1383                            * ((allocation->width - border.left - border.right - 2 *INNER_BORDER
1384                                - (CALENDAR_MARGIN * 2) -  (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
1385                            / (7 * priv->min_day_width + priv->max_week_char_width * 2));
1386         priv->week_width = ((allocation->width - border.left - border.right - 2 *INNER_BORDER
1387                              - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
1388                             - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
1389     } else {
1390         priv->day_width = (allocation->width
1391                            - border.left - border.right - 2 *INNER_BORDER
1392                            - (CALENDAR_MARGIN * 2)
1393                            - (DAY_XSEP * 6))/7;
1394         priv->week_width = 0;
1395     }
1396 
1397     if (gtk_widget_get_realized (widget)) {
1398         gdk_window_move_resize (gtk_widget_get_window(widget),
1399                                 allocation->x, allocation->y,
1400                                 allocation->width, allocation->height);
1401         if (priv->header_win)
1402             gdk_window_move_resize (priv->header_win,
1403                                     border.left, border.top,
1404                                     allocation->width - border.left - border.right, priv->header_h);
1405 
1406         for (i = 0 ; i < 4 ; i++) {
1407             if (priv->arrow_win[i]) {
1408                 GdkRectangle rect;
1409                 calendar_arrow_rectangle (calendar, i, &rect);
1410 
1411                 gdk_window_move_resize (priv->arrow_win[i],
1412                                         rect.x, rect.y, rect.width, rect.height);
1413             }
1414         }
1415 
1416         if (priv->day_name_win)
1417             gdk_window_move_resize (priv->day_name_win,
1418                                     border.left + INNER_BORDER,
1419                                     priv->header_h + (border.top + INNER_BORDER),
1420                                     allocation->width - border.left - border.right - 2 * INNER_BORDER,
1421                                     priv->day_name_h);
1422         if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) {
1423             if (priv->week_win)
1424                 gdk_window_move_resize (priv->week_win,
1425                                         border.left + INNER_BORDER,
1426                                         priv->header_h + priv->day_name_h + (border.top + INNER_BORDER),
1427                                         priv->week_width,
1428                                         priv->main_h);
1429             gdk_window_move_resize (priv->main_win,
1430                                     priv->week_width + (border.left + INNER_BORDER),
1431                                     priv->header_h + priv->day_name_h + (border.top + INNER_BORDER),
1432                                     allocation->width - priv->week_width - - border.left - border.right - 2 * INNER_BORDER,
1433                                     priv->main_h);
1434         } else {
1435             gdk_window_move_resize (priv->main_win,
1436                                     border.left + INNER_BORDER,
1437                                     priv->header_h + priv->day_name_h + (border.top + INNER_BORDER),
1438                                     allocation->width - priv->week_width - - border.left - border.right - 2 * INNER_BORDER,
1439                                     priv->main_h);
1440             if (priv->week_win)
1441                 gdk_window_move_resize (priv->week_win,
1442                                         allocation->width - priv->week_width - (border.left + INNER_BORDER),
1443                                         priv->header_h + priv->day_name_h + (border.top + INNER_BORDER),
1444                                         priv->week_width,
1445                                         priv->main_h);
1446         }
1447     }
1448 }
1449 
1450 /*------------------------------------------------------------------------------*/
1451 
1452 static void
calendar_paint_header(GuiCalendar * calendar,cairo_t * cr)1453 calendar_paint_header (GuiCalendar *calendar, cairo_t *cr) {
1454     GtkWidget *widget = GTK_WIDGET (calendar);
1455     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
1456     char buffer[255];
1457     int x, y;
1458     gint header_width;
1459     gint max_month_width;
1460     gint max_year_width;
1461     PangoLayout *layout;
1462     PangoRectangle logical_rect;
1463     gboolean year_left;
1464     time_t tmp_time;
1465     struct tm *tm;
1466     gchar *str;
1467     GtkBorder border;
1468 
1469     if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) {
1470         year_left = priv->year_before;
1471 	} else {
1472         year_left = !priv->year_before;
1473 	}
1474 
1475     gtk_style_context_get_border(gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, &border);
1476     header_width = gtk_widget_get_allocated_width(widget) - border.left - border.right;
1477 
1478     max_month_width = priv->max_month_width;
1479     max_year_width = priv->max_year_width;
1480 
1481     tmp_time = 1;  /* Jan 1 1970, 00:00:01 UTC */
1482     tm = gmtime (&tmp_time);
1483     tm->tm_year = calendar->year - 1900;
1484     strftime (buffer, sizeof (buffer), "%Y", tm);       /* year format */
1485     str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
1486     layout = gtk_widget_create_pango_layout (widget, str);
1487     g_free (str);
1488 
1489     pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1490 
1491     /* draw title */
1492     y = (priv->header_h - logical_rect.height) / 2;
1493 
1494     /* draw year and its arrows */
1495 
1496     if (calendar->display_flags & GUI_CALENDAR_NO_MONTH_CHANGE) {
1497         if (year_left) {
1498             x = 3 + (max_year_width - logical_rect.width)/2;
1499         } else {
1500             x = header_width - (3 + max_year_width
1501                                 - (max_year_width - logical_rect.width)/2);
1502         }
1503     } else {
1504         if (year_left) {
1505             x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
1506         } else {
1507             x = header_width - (3 + priv->arrow_width + max_year_width
1508                                 - (max_year_width - logical_rect.width)/2);
1509         }
1510     }
1511 
1512     cairo_move_to (cr, x, y);
1513     pango_cairo_show_layout (cr, layout);
1514 
1515     /* draw month */
1516     g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
1517     pango_layout_set_text (layout, buffer, -1);
1518     pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1519 
1520     if (calendar->display_flags & GUI_CALENDAR_NO_MONTH_CHANGE) {
1521         if (year_left) {
1522             x = header_width - (3 + max_month_width
1523                                 - (max_month_width - logical_rect.width)/2);
1524         } else {
1525             x = 3 + (max_month_width - logical_rect.width) / 2;
1526         }
1527     } else {
1528         if (year_left) {
1529             x = header_width - (3 + priv->arrow_width + max_month_width
1530                                 - (max_month_width - logical_rect.width)/2);
1531         } else {
1532             x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
1533         }
1534     }
1535 
1536     cairo_move_to (cr, x, y);
1537     pango_cairo_show_layout (cr, layout);
1538 
1539     g_object_unref (layout);
1540 }
1541 
1542 /*------------------------------------------------------------------------------*/
1543 
1544 static void
calendar_paint_day_names(GuiCalendar * calendar)1545 calendar_paint_day_names (GuiCalendar *calendar) {
1546     GtkWidget *widget = GTK_WIDGET (calendar);
1547     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
1548     cairo_t *cr;
1549     char buffer[255];
1550     int day,i;
1551     int day_width, cal_width;
1552     int day_wid_sep;
1553     PangoLayout *layout;
1554     PangoRectangle logical_rect;
1555     gint focus_padding;
1556     gint focus_width;
1557 
1558     cr = gdk_cairo_create (priv->day_name_win);
1559 
1560     gtk_widget_style_get (GTK_WIDGET (widget),
1561                           "focus-line-width", &focus_width,
1562                           "focus-padding", &focus_padding,
1563                           NULL);
1564 
1565     day_width = priv->day_width;
1566     cal_width = gtk_widget_get_allocated_width(widget);
1567     day_wid_sep = day_width + DAY_XSEP;
1568 
1569     /* draw rectangles as inverted background for the labels */
1570 
1571     gdk_cairo_set_source_rgba (cr, &calendar->header_bg_color);
1572     cairo_rectangle (cr,
1573                      CALENDAR_MARGIN, CALENDAR_MARGIN,
1574                      cal_width-CALENDAR_MARGIN * 2,
1575                      priv->day_name_h - CALENDAR_MARGIN);
1576     cairo_fill (cr);
1577 
1578     if (calendar->display_flags & GUI_CALENDAR_SHOW_WEEK_NUMBERS) {
1579         cairo_rectangle (cr,
1580                          CALENDAR_MARGIN,
1581                          priv->day_name_h - CALENDAR_YSEP,
1582                          priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
1583                          CALENDAR_YSEP);
1584         cairo_fill (cr);
1585     }
1586 
1587     /* write the labels */
1588 
1589     layout = gtk_widget_create_pango_layout (widget, NULL);
1590 
1591     gdk_cairo_set_source_rgba (cr, &calendar->header_fg_color);
1592 
1593     for (i = 0; i < 7; i++) {
1594 
1595         if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL) {
1596             day = 6 - i;
1597         } else {
1598             day = i;
1599         }
1600 
1601         day = (day + priv->week_start) % 7;
1602         g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
1603 
1604         pango_layout_set_text (layout, buffer, -1);
1605         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1606 
1607         cairo_move_to (cr,
1608                        (CALENDAR_MARGIN +
1609                         + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
1610                            (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
1611                                    : 0)
1612                                 + day_wid_sep * i
1613                                 + (day_width - logical_rect.width)/2),
1614                                CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
1615         pango_cairo_show_layout (cr, layout);
1616     }
1617 
1618     g_object_unref (layout);
1619     cairo_destroy (cr);
1620 }
1621 
1622 /*------------------------------------------------------------------------------*/
1623 
1624 static void
calendar_paint_week_numbers(GuiCalendar * calendar)1625 calendar_paint_week_numbers (GuiCalendar *calendar) {
1626     GtkWidget *widget = GTK_WIDGET (calendar);
1627     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
1628     cairo_t *cr;
1629     guint row, week = 0, year;
1630     gint x_loc;
1631     char buffer[32];
1632     gint y_loc, day_height;
1633     PangoLayout *layout;
1634     PangoRectangle logical_rect;
1635     gint focus_padding;
1636     gint focus_width;
1637 
1638     cr = gdk_cairo_create (priv->week_win);
1639 
1640     gtk_widget_style_get (GTK_WIDGET (widget),
1641                           "focus-line-width", &focus_width,
1642                           "focus-padding", &focus_padding,
1643                           NULL);
1644 
1645     /* draw a rectangle as inverted background for the labels */
1646 
1647     gdk_cairo_set_source_rgba (cr, &calendar->header_bg_color);
1648     if (priv->day_name_win) {
1649         cairo_rectangle (cr,
1650                          CALENDAR_MARGIN,
1651                          0,
1652                          priv->week_width - CALENDAR_MARGIN,
1653                          priv->main_h - CALENDAR_MARGIN);
1654     } else {
1655         cairo_rectangle (cr,
1656                          CALENDAR_MARGIN,
1657                          CALENDAR_MARGIN,
1658                          priv->week_width - CALENDAR_MARGIN,
1659                          priv->main_h - 2 * CALENDAR_MARGIN);
1660     }
1661     cairo_fill (cr);
1662 
1663     /* write the labels */
1664 
1665     layout = gtk_widget_create_pango_layout (widget, NULL);
1666 
1667     gdk_cairo_set_source_rgba (cr, &calendar->header_fg_color);     /* week numbers */
1668 
1669     day_height = calendar_row_height (calendar);
1670     for (row = 0; row < 6; row++) {
1671         gboolean result;
1672 
1673         year = calendar->year;
1674         if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
1675             year++;
1676 
1677         result = utl_week_of_year (&week, &year,
1678                                ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
1679                                 + calendar->month) % 12 + 1, calendar->day[row][6]);
1680         g_return_if_fail (result);
1681 
1682         g_snprintf (buffer, sizeof (buffer), "%d", week);   /* %d - calendar week digits, %Id - for localized digits */
1683         pango_layout_set_text (layout, buffer, -1);
1684         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1685 
1686         y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
1687 
1688         x_loc = (priv->week_width
1689                  - logical_rect.width
1690                  - CALENDAR_XSEP - focus_padding - focus_width);
1691 
1692         cairo_move_to (cr, x_loc, y_loc);
1693         pango_cairo_show_layout (cr, layout);
1694     }
1695 
1696     g_object_unref (layout);
1697     cairo_destroy (cr);
1698 }
1699 
1700 /*------------------------------------------------------------------------------*/
1701 
1702 static void
calendar_invalidate_day_num(GuiCalendar * calendar,gint day)1703 calendar_invalidate_day_num (GuiCalendar *calendar,
1704                              gint         day) {
1705     gint r, c, row, col;
1706 
1707     row = -1;
1708     col = -1;
1709     for (r = 0; r < 6; r++)
1710         for (c = 0; c < 7; c++)
1711             if (calendar->day_month[r][c] == MONTH_CURRENT && calendar->day[r][c] == day) {
1712                 row = r;
1713                 col = c;
1714             }
1715 
1716     g_return_if_fail (row != -1);
1717     g_return_if_fail (col != -1);
1718 
1719     calendar_invalidate_day (calendar, row, col);
1720 }
1721 
1722 /*------------------------------------------------------------------------------*/
1723 
1724 static void
calendar_invalidate_day(GuiCalendar * calendar,gint row,gint col)1725 calendar_invalidate_day (GuiCalendar *calendar,
1726                          gint         row,
1727                          gint         col) {
1728     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
1729 
1730     if (priv->main_win) {
1731         GdkRectangle day_rect;
1732 
1733         calendar_day_rectangle (calendar, row, col, &day_rect);
1734         gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
1735     }
1736 }
1737 
1738 /*------------------------------------------------------------------------------*/
1739 
1740 static void
cal_draw_rounded_rectangle(cairo_t * cr,GdkRectangle * rect,GdkRGBA * color,gint stroke)1741 cal_draw_rounded_rectangle (cairo_t *cr, GdkRectangle *rect, GdkRGBA *color, gint stroke)
1742 {
1743 	gdk_cairo_set_source_rgba (cr, color);
1744 	utl_draw_rounded_rectangle (cr, rect->x, rect->y, rect->width, rect->height, rect->height * 0.2, 1);
1745 	utl_cairo_draw (cr, stroke);
1746 }
1747 
1748 /*------------------------------------------------------------------------------*/
1749 
1750 static void
cal_draw_left_arrow(cairo_t * cr,GdkRectangle * rect,GdkRGBA * color)1751 cal_draw_left_arrow (cairo_t *cr, GdkRectangle *rect, GdkRGBA *color)
1752 {
1753 	gdk_cairo_set_source_rgba (cr, color);
1754 	utl_draw_left_arrow (cr, rect->x + rect->width * 0.8, rect->y + rect->height * 0.5,
1755 	                         rect->width * 0.13, rect->height * 0.55, 0.65);
1756 	cairo_fill (cr);
1757 }
1758 
1759 /*------------------------------------------------------------------------------*/
1760 
1761 static void
cal_draw_ellipse(cairo_t * cr,GdkRectangle * rect,GdkRGBA * color)1762 cal_draw_ellipse (cairo_t *cr, GdkRectangle *rect, GdkRGBA *color)
1763 {
1764 	gdk_cairo_set_source_rgba (cr, color);
1765 	cairo_save (cr);
1766 	cairo_scale (cr, 2, 1);
1767 	cairo_arc (cr, (rect->x + (rect->width * 0.5)) * 0.5, rect->y + rect->height * 0.5,
1768 	           rect->height * 0.5 - 3.0, 0.0, 2.0 * M_PI);
1769 	cairo_fill (cr);
1770 	cairo_restore (cr);
1771 }
1772 
1773 /*------------------------------------------------------------------------------*/
1774 
1775 static void
cal_draw_wave(cairo_t * cr,GdkRectangle * rect,GdkRGBA * color)1776 cal_draw_wave (cairo_t *cr, GdkRectangle *rect, GdkRGBA *color)
1777 {
1778 	gdk_cairo_set_source_rgba (cr, color);
1779 	cairo_set_line_width (cr, rect->height * 0.7);
1780 	cairo_move_to (cr, rect->x + rect->width * 0.2, rect->y + rect->height * 0.5);
1781 	cairo_curve_to (cr, rect->x + rect->width * 0.5, rect->y + rect->height * 0.3,
1782 	                    rect->x + rect->width * 0.5, rect->y + rect->height * 0.7,
1783 	                    rect->x + rect->width * 0.8, rect->y + rect->height * 0.5);
1784 	cairo_stroke (cr);
1785 }
1786 
1787 /*------------------------------------------------------------------------------*/
1788 
1789 static void
cal_draw_circle(cairo_t * cr,GdkRectangle * rect,GdkRGBA * color)1790 cal_draw_circle (cairo_t *cr, GdkRectangle *rect, GdkRGBA *color)
1791 {
1792 	gdk_cairo_set_source_rgba (cr, color);
1793 	cairo_arc (cr, rect->x + rect->width * 0.5, rect->y + rect->height * 0.5, rect->height * 0.5 - 3.0, 0.0, 2.0 * M_PI);
1794 	cairo_fill (cr);
1795 }
1796 
1797 /*------------------------------------------------------------------------------*/
1798 
1799 static void
cal_draw_freehand_circle(cairo_t * cr,GdkRectangle * rect,GdkRGBA * color)1800 cal_draw_freehand_circle (cairo_t *cr, GdkRectangle *rect, GdkRGBA *color)
1801 {
1802 	gdouble dtop, dbottom, dleft, dright;
1803 	gdouble dwidth, dheight;
1804 
1805 	dheight = rect->height;
1806 	dwidth = dheight * 2.0;
1807 	dtop = rect->y;
1808 	dbottom = rect->y + dheight;
1809 	dleft = rect->x + (rect->width - dwidth) / 2.0;
1810 	dright = dleft + dwidth;
1811 
1812 	gdk_cairo_set_source_rgba (cr, color);
1813 	cairo_move_to (cr, dleft + dwidth * 0.60, dtop);
1814 	cairo_curve_to (cr, dleft + dwidth * 0.05, dtop, dleft, dbottom, dleft + dwidth * 0.50, dbottom);
1815 	cairo_curve_to (cr, dright, dbottom,
1816 						dright - dwidth * 0.05, dtop - dheight * 0.15,
1817 						dleft + dwidth * 0.40, dtop + dheight * 0.20);
1818 	cairo_line_to (cr, dleft + dwidth * 0.40, dtop + dheight * 0.25);
1819 	cairo_curve_to (cr, dright - dwidth * 0.15, dtop - dheight * 0.05,
1820 						dright - dwidth * 0.05, dbottom - dheight * 0.15,
1821 						dleft + dwidth * 0.50, dbottom - dheight * 0.15);
1822 	cairo_curve_to (cr, dleft + dwidth * 0.05, dbottom - dheight * 0.15,
1823 						dleft + dwidth * 0.20, dtop,
1824 						dleft + dwidth * 0.60, dtop + dheight * 0.05);
1825 	cairo_close_path (cr);
1826 	cairo_fill (cr);
1827 }
1828 
1829 /*------------------------------------------------------------------------------*/
1830 
1831 static void
cal_cairo_draw_cursor(cairo_t * cr,GdkRectangle * rect,gint thickness)1832 cal_cairo_draw_cursor (cairo_t *cr, GdkRectangle *rect, gint thickness)
1833 {
1834 	gint bias_x, bias_y, bias;
1835 
1836 	bias = thickness % 2;
1837 	bias_x = thickness >> 1;
1838 	bias_y = bias_x + bias;
1839 
1840 	cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
1841 
1842 	cairo_move_to  (cr, rect->x+bias, rect->y+bias_y-bias);
1843 	cairo_line_to (cr, rect->x+rect->width-bias_x, rect->y+bias_y-bias);
1844 	cairo_line_to (cr, rect->x+rect->width-bias_x, rect->y+rect->height-bias_y);
1845 	cairo_line_to (cr, rect->x+bias_x+bias, rect->y+rect->height-bias_y);
1846 	cairo_line_to (cr, rect->x+bias_x+bias, rect->y+bias_y-bias_y*bias);
1847 	cairo_set_line_width (cr, thickness);
1848 	cairo_stroke (cr);
1849 
1850 	cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
1851 }
1852 
1853 /*------------------------------------------------------------------------------*/
1854 
1855 static void
calendar_paint_day(GuiCalendar * calendar,cairo_t * cr,gint row,gint col)1856 calendar_paint_day (GuiCalendar *calendar, cairo_t *cr,
1857                     gint         row,
1858                     gint         col) {
1859     GtkWidget *widget = GTK_WIDGET (calendar);
1860     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
1861     GdkRGBA text_color;
1862     gchar buffer[32];
1863     gint day;
1864     gint x_loc, y_loc;
1865     GdkRectangle day_rect;
1866     time_t secs;
1867     struct tm *tm;
1868 
1869     PangoLayout *layout;
1870     PangoRectangle logical_rect;
1871 
1872     g_return_if_fail (row < 6);
1873     g_return_if_fail (col < 7);
1874 
1875     day = calendar->day[row][col];
1876 
1877     calendar_day_rectangle (calendar, row, col, &day_rect);
1878 
1879     if (calendar->mark_sign) {
1880         if (calendar->marked_date[day-1] && calendar->day_month[row][col] == MONTH_CURRENT) {
1881             g_snprintf (buffer, sizeof (buffer), "%lc%d", calendar->mark_sign, day);
1882         } else {
1883             g_snprintf (buffer, sizeof (buffer), "%d", day);
1884         }
1885     } else {
1886         g_snprintf (buffer, sizeof (buffer), "%d", day);
1887     }
1888 
1889     if (g_utf8_validate(buffer, -1, NULL) == FALSE) {
1890         g_snprintf (buffer, sizeof (buffer), "%d", day);
1891     }
1892 
1893     layout = gtk_widget_create_pango_layout (widget, buffer);
1894     pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1895 
1896     x_loc = day_rect.x + day_rect.width / 2 + priv->max_day_char_width;
1897     x_loc -= logical_rect.width;
1898     y_loc = day_rect.y + (day_rect.height - logical_rect.height) / 2;
1899 
1900 	/* mark day note */
1901 	if (calendar->marked_date[day-1] && calendar->day_month[row][col] == MONTH_CURRENT) {
1902 		cal_draw_rounded_rectangle (cr, &day_rect, &calendar->marked_date_color[day-1], 0);
1903 	}
1904 
1905 	/* mark event */
1906 	if (calendar->event_marked_date[day-1] && calendar->day_month[row][col] == MONTH_CURRENT) {
1907 
1908 		if (calendar->event_marker_type == EVENT_MARKER_ELLIPSE) {
1909 			cal_draw_ellipse (cr, &day_rect, &calendar->event_marker_color);
1910 		} else if (calendar->event_marker_type == EVENT_MARKER_WAVE) {
1911 			cal_draw_wave (cr, &day_rect, &calendar->event_marker_color);
1912 		} else {
1913 			cal_draw_circle (cr, &day_rect, &calendar->event_marker_color);
1914 		}
1915 
1916 	}
1917 
1918 	/* mark birthday */
1919 	if (calendar->birthday_marked_date[day-1] && calendar->day_month[row][col] == MONTH_CURRENT) {
1920 		gdk_cairo_set_source_rgba (cr, &calendar->birthday_marker_color);
1921 		utl_draw_rounded_rectangle (cr, day_rect.x + day_rect.width - day_rect.height * 0.35,
1922 									day_rect.y + (day_rect.height - day_rect.height * 0.7) * 0.5,
1923 									day_rect.height * 0.25, day_rect.height * 0.7, 4, 1.0);
1924 		utl_cairo_draw (cr, 0);
1925 	}
1926 
1927     /* mark current day */
1928     secs = time (NULL);
1929     tm = localtime (&secs);
1930 
1931 	if (calendar->month == tm->tm_mon && calendar->year == 1900 + tm->tm_year
1932 	    && calendar->day[row][col] == tm->tm_mday && calendar->day_month[row][col] == MONTH_CURRENT) {
1933 
1934 		if (calendar->today_marker_type == TODAY_MARKER_FREEHAND_CIRCLE) {
1935 			cal_draw_freehand_circle (cr, &day_rect, &(calendar->today_marker_color));
1936 		} else {
1937 			cal_draw_left_arrow (cr, &day_rect, &(calendar->today_marker_color));
1938 		}
1939 
1940 	}
1941 
1942     if (calendar->day_month[row][col] == MONTH_PREV) {
1943         memcpy(&text_color, &calendar->pf_day_color, sizeof(GdkRGBA));
1944     } else if (calendar->day_month[row][col] == MONTH_NEXT) {
1945         memcpy(&text_color, &calendar->pf_day_color, sizeof(GdkRGBA));
1946     } else {
1947 
1948 		if (calendar->selected_day == day && calendar->enable_cursor == TRUE) {
1949 			gdk_cairo_set_source_rgba(cr, &calendar->selector_color);
1950 
1951 			if (calendar->cursor_type == CURSOR_FRAME) {
1952 				cal_cairo_draw_cursor (cr, &day_rect, calendar->frame_cursor_thickness);
1953 			} else {
1954 				gdk_cairo_rectangle (cr, &day_rect);
1955 				cairo_fill (cr);
1956 			}
1957 
1958 		}
1959 
1960         memcpy(&text_color, &calendar->day_color, sizeof(GdkRGBA));
1961 
1962         /* mark saturday and sunday */
1963         if (((col == 5 || col == 6) && (priv->week_start == 1)) || ((!col || col == 6) && !priv->week_start)) {
1964             if (calendar->day_month[row][col] != MONTH_NEXT && calendar->day_month[row][col] != MONTH_PREV) {
1965                 memcpy(&text_color, &calendar->weekend_color, sizeof(GdkRGBA));
1966             }
1967         }
1968 
1969         if (calendar->enable_cursor == TRUE && calendar->cursor_type == CURSOR_BLOCK
1970             && calendar->selected_day == day && calendar->selector_color.alpha == 1.0) {
1971 			gtk_style_context_get_color(gtk_widget_get_style_context(widget),
1972 										gtk_widget_has_focus(widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE, &text_color);
1973         }
1974     }
1975 
1976     gdk_cairo_set_source_rgba (cr, &text_color);
1977     cairo_move_to (cr, x_loc, y_loc);
1978     pango_cairo_show_layout (cr, layout);
1979 
1980     if (calendar->marked_date[day-1] && calendar->day_month[row][col] == MONTH_CURRENT) {
1981         gdk_cairo_set_source_rgba (cr, &text_color);
1982         cairo_move_to (cr, x_loc - 1, y_loc);
1983         pango_cairo_show_layout (cr, layout);
1984     }
1985 
1986 
1987     if (gtk_widget_has_focus (widget) && calendar->focus_row == row && calendar->focus_col == col) {
1988         gtk_render_focus (gtk_widget_get_style_context(widget),
1989                          cr,
1990                          day_rect.x,     day_rect.y,
1991                          day_rect.width, day_rect.height);
1992     }
1993 
1994     g_object_unref (layout);
1995 }
1996 
1997 /*------------------------------------------------------------------------------*/
1998 
1999 static void
calendar_paint_main(GuiCalendar * calendar,cairo_t * cr)2000 calendar_paint_main (GuiCalendar *calendar, cairo_t *cr) {
2001     gint row, col;
2002 
2003     for (col = 0; col < 7; col++)
2004         for (row = 0; row < 6; row++)
2005             calendar_paint_day (calendar, cr, row, col);
2006 }
2007 
2008 /*------------------------------------------------------------------------------*/
2009 
2010 static void
calendar_invalidate_arrow(GuiCalendar * calendar,guint arrow)2011 calendar_invalidate_arrow (GuiCalendar *calendar,
2012                            guint        arrow) {
2013     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
2014     GdkWindow *window;
2015 
2016     window = priv->arrow_win[arrow];
2017     if (window)
2018         gdk_window_invalidate_rect (window, NULL, FALSE);
2019 }
2020 
2021 /*------------------------------------------------------------------------------*/
2022 
2023 static void
calendar_paint_arrow(GuiCalendar * calendar,cairo_t * cr,guint arrow)2024 calendar_paint_arrow (GuiCalendar *calendar, cairo_t *cr,
2025                       guint        arrow) {
2026     GtkWidget *widget = GTK_WIDGET (calendar);
2027     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2028     GdkWindow *window;
2029 
2030     window = priv->arrow_win[arrow];
2031     if (window) {
2032         cairo_t *cr = gdk_cairo_create (window);
2033         GtkStyleContext* ctx = gtk_widget_get_style_context(widget);
2034         gint width, height;
2035         gint state;
2036         GdkRGBA color;
2037 
2038         state = priv->arrow_state[arrow];
2039 
2040         gtk_style_context_get_background_color(ctx, state, &color);
2041         gdk_cairo_set_source_rgba(cr, &color);
2042         cairo_paint(cr);
2043 
2044         width = gdk_window_get_width(window);
2045         height = gdk_window_get_height(window);
2046 
2047         if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2048             gtk_render_arrow (ctx, cr,
2049                              G_PI * 3 / 2, width/2 - 3, height/2 - 4, 8);
2050         else
2051             gtk_render_arrow (ctx, cr,
2052                              G_PI / 2, width/2 - 3, height/2 - 4, 8);
2053         cairo_destroy (cr);
2054     }
2055 }
2056 
2057 /*------------------------------------------------------------------------------*/
2058 
2059 static gboolean
gui_calendar_draw(GtkWidget * widget,cairo_t * cr)2060 gui_calendar_draw (GtkWidget *widget, cairo_t *cr) {
2061     GuiCalendar *calendar = GUI_CALENDAR (widget);
2062     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2063 	GdkRGBA *color;
2064     int i;
2065 
2066     if (calendar->display_flags & GUI_CALENDAR_WEEK_START_MONDAY) {
2067         priv->week_start = 1; /* monday */
2068     } else {
2069         priv->week_start = 0; /* sunday */
2070     }
2071 
2072     calendar_compute_days(calendar);
2073 
2074     if (priv->main_win && gtk_cairo_should_draw_window(cr, priv->main_win)) {
2075         cairo_save(cr);
2076         gtk_cairo_transform_to_window(cr, widget, priv->main_win);
2077         color = &calendar->background_color;
2078 		cairo_set_source_rgb(cr, color->red, color->green, color->blue);
2079 		cairo_rectangle(cr, 0, 0, gtk_widget_get_allocated_width(widget),
2080 						gtk_widget_get_allocated_height(widget));
2081 		cairo_fill(cr);
2082         calendar_paint_main(calendar, cr);
2083         cairo_restore(cr);
2084     }
2085 
2086     if (priv->header_win && gtk_cairo_should_draw_window(cr, priv->header_win)) {
2087         cairo_save(cr);
2088         gtk_cairo_transform_to_window(cr, widget, priv->header_win);
2089         calendar_paint_header(calendar, cr);
2090         cairo_restore(cr);
2091     }
2092 
2093     for (i = 0; i < 4; i++)
2094         if (priv->arrow_win[i] && gtk_cairo_should_draw_window(cr, priv->arrow_win[i])) {
2095             cairo_save(cr);
2096             gtk_cairo_transform_to_window(cr, widget, priv->arrow_win[i]);
2097             calendar_paint_arrow(calendar, cr, i);
2098             cairo_restore(cr);
2099         }
2100 
2101     if (priv->day_name_win && gtk_cairo_should_draw_window(cr, priv->day_name_win)) {
2102         cairo_save(cr);
2103         gtk_cairo_transform_to_window(cr, widget, priv->day_name_win);
2104         calendar_paint_day_names(calendar);
2105         cairo_restore(cr);
2106     }
2107 
2108     if (priv->week_win && gtk_cairo_should_draw_window(cr, priv->week_win)) {
2109         cairo_save(cr);
2110         gtk_cairo_transform_to_window(cr, widget, priv->week_win);
2111         calendar_paint_week_numbers(calendar);
2112         cairo_restore(cr);
2113     }
2114 
2115     return FALSE;
2116 }
2117 
2118 /*------------------------------------------------------------------------------*/
2119 
2120 static void
calendar_arrow_action(GuiCalendar * calendar,guint arrow)2121 calendar_arrow_action (GuiCalendar *calendar,
2122                        guint        arrow) {
2123     switch (arrow) {
2124     case ARROW_YEAR_LEFT:
2125         calendar_set_year_prev (calendar);
2126         break;
2127     case ARROW_YEAR_RIGHT:
2128         calendar_set_year_next (calendar);
2129         break;
2130     case ARROW_MONTH_LEFT:
2131         calendar_set_month_prev (calendar);
2132         break;
2133     case ARROW_MONTH_RIGHT:
2134         calendar_set_month_next (calendar);
2135         break;
2136     default:
2137         ;
2138         /* do nothing */
2139     }
2140 }
2141 
2142 /*------------------------------------------------------------------------------*/
2143 
2144 static gboolean
calendar_timer(gpointer data)2145 calendar_timer (gpointer data) {
2146 
2147 	GuiCalendar *calendar = data;
2148     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
2149     gboolean retval = FALSE;
2150 
2151     if (priv->timer) {
2152         calendar_arrow_action (calendar, priv->click_child);
2153 
2154         if (priv->need_timer) {
2155             GtkSettings *settings;
2156             guint        timeout;
2157 
2158             settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2159             g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2160 
2161             priv->need_timer = FALSE;
2162             priv->timer = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
2163                                               timeout * SCROLL_DELAY_FACTOR,
2164                                               (GSourceFunc) calendar_timer,
2165                                               (gpointer) calendar, NULL);
2166         } else
2167             retval = TRUE;
2168     }
2169 
2170     return retval;
2171 }
2172 
2173 /*------------------------------------------------------------------------------*/
2174 
2175 static void
calendar_start_spinning(GuiCalendar * calendar,gint click_child)2176 calendar_start_spinning (GuiCalendar *calendar,
2177                          gint         click_child) {
2178     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
2179 
2180     priv->click_child = click_child;
2181 
2182     if (!priv->timer) {
2183         GtkSettings *settings;
2184         guint        timeout;
2185 
2186         settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2187         g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2188 
2189         priv->need_timer = TRUE;
2190         priv->timer = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
2191                                           timeout,
2192                                           (GSourceFunc) calendar_timer,
2193                                           (gpointer) calendar, NULL);
2194     }
2195 }
2196 
2197 /*------------------------------------------------------------------------------*/
2198 
2199 static void
calendar_stop_spinning(GuiCalendar * calendar)2200 calendar_stop_spinning (GuiCalendar *calendar) {
2201     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
2202 
2203     if (priv->timer) {
2204         g_source_remove (priv->timer);
2205         priv->timer = 0;
2206         priv->need_timer = FALSE;
2207     }
2208 }
2209 
2210 /*------------------------------------------------------------------------------*/
2211 
2212 static void
calendar_main_button_press(GuiCalendar * calendar,GdkEventButton * event)2213 calendar_main_button_press (GuiCalendar    *calendar,
2214                             GdkEventButton *event) {
2215     GtkWidget *widget = GTK_WIDGET (calendar);
2216     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
2217     gint x, y;
2218     gint row, col;
2219     gint day_month;
2220     gint day;
2221 
2222     x = (gint) (event->x);
2223     y = (gint) (event->y);
2224 
2225     row = calendar_row_from_y (calendar, y);
2226     col = calendar_column_from_x (calendar, x);
2227 
2228     /* If row or column isn't found, just return. */
2229     if (row == -1 || col == -1)
2230         return;
2231 
2232     day_month = calendar->day_month[row][col];
2233 
2234     if (event->type == GDK_BUTTON_PRESS) {
2235         if (event->button == 1) {
2236             day = calendar->day[row][col];
2237 
2238             if (day_month == MONTH_PREV)
2239                 calendar_set_month_prev (calendar);
2240             else if (day_month == MONTH_NEXT)
2241                 calendar_set_month_next (calendar);
2242 
2243             if (!gtk_widget_has_focus (widget))
2244                 gtk_widget_grab_focus (widget);
2245 
2246             if (event->button == 1) {
2247                 priv->in_drag = 0;                /* TODO: drag enable */
2248                 priv->drag_start_x = x;
2249                 priv->drag_start_y = y;
2250             }
2251 
2252             if (calendar->display_flags & GUI_CALENDAR_NO_MONTH_CHANGE) {
2253                 if (day_month != MONTH_PREV && day_month != MONTH_NEXT) {
2254                     calendar_select_and_focus_day (calendar, day);
2255                 }
2256             } else {
2257                 calendar_select_and_focus_day (calendar, day);
2258             }
2259        }
2260     } else if (event->type == GDK_2BUTTON_PRESS) {
2261         priv->in_drag = 0;
2262         if (day_month == MONTH_CURRENT && event->button == 1)
2263             g_signal_emit (calendar,
2264                            gui_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2265                            0);
2266     }
2267 }
2268 
2269 /*------------------------------------------------------------------------------*/
2270 
2271 static gboolean
gui_calendar_button_press(GtkWidget * widget,GdkEventButton * event)2272 gui_calendar_button_press (GtkWidget      *widget,
2273                            GdkEventButton *event) {
2274     GuiCalendar *calendar = GUI_CALENDAR (widget);
2275     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2276     gint arrow = -1;
2277 
2278     if (event->window == priv->main_win)
2279         calendar_main_button_press (calendar, event);
2280 
2281     if (!gtk_widget_has_focus (widget))
2282         gtk_widget_grab_focus (widget);
2283 
2284     for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++) {
2285         if (event->window == priv->arrow_win[arrow]) {
2286 
2287             /* only call the action on single click, not double */
2288             if (event->type == GDK_BUTTON_PRESS) {
2289                 if (event->button == 1)
2290                     calendar_start_spinning (calendar, arrow);
2291 
2292                 calendar_arrow_action (calendar, arrow);
2293             }
2294 
2295             return TRUE;
2296         }
2297     }
2298 
2299     if (event->type == GDK_BUTTON_PRESS) {
2300         if (event->button == 1)
2301             return TRUE;
2302     }
2303 
2304     return FALSE;
2305 }
2306 
2307 /*------------------------------------------------------------------------------*/
2308 
2309 static gboolean
gui_calendar_button_release(GtkWidget * widget,GdkEventButton * event)2310 gui_calendar_button_release (GtkWidget    *widget,
2311                              GdkEventButton *event) {
2312 
2313 	GuiCalendar *calendar = GUI_CALENDAR (widget);
2314     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2315 
2316     if (event->button == 1) {
2317         calendar_stop_spinning (calendar);
2318 
2319         if (priv->in_drag)
2320             priv->in_drag = 0;
2321     }
2322 
2323     return TRUE;
2324 }
2325 
2326 /*------------------------------------------------------------------------------*/
2327 
2328 static gboolean
gui_calendar_motion_notify(GtkWidget * widget,GdkEventMotion * event)2329 gui_calendar_motion_notify (GtkWidget      *widget,
2330                             GdkEventMotion *event) {
2331     GuiCalendar *calendar = GUI_CALENDAR (widget);
2332     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2333     gint event_x, event_y;
2334     gint row, col;
2335     gint old_row, old_col;
2336 
2337     event_x = (gint) (event->x);
2338     event_y = (gint) (event->y);
2339 
2340     if (event->window == priv->main_win) {
2341 
2342         if (priv->in_drag) {
2343             if (gtk_drag_check_threshold (widget,
2344                                           priv->drag_start_x, priv->drag_start_y,
2345                                           event->x, event->y)) {
2346                 GdkDragContext *context;
2347                 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2348                 gtk_target_list_add_text_targets (target_list, 0);
2349                 context = gtk_drag_begin_with_coordinates (widget, target_list, GDK_ACTION_COPY,
2350                                           1, (GdkEvent *)event, event->x, event->y);
2351                 priv->in_drag = 0;
2352 
2353                 gtk_target_list_unref (target_list);
2354                 gtk_drag_set_icon_default (context);
2355             }
2356         } else {
2357             row = calendar_row_from_y (calendar, event_y);
2358             col = calendar_column_from_x (calendar, event_x);
2359 
2360             if (row != calendar->highlight_row || calendar->highlight_col != col) {
2361                 old_row = calendar->highlight_row;
2362                 old_col = calendar->highlight_col;
2363                 if (old_row > -1 && old_col > -1) {
2364                     calendar->highlight_row = -1;
2365                     calendar->highlight_col = -1;
2366                     calendar_invalidate_day (calendar, old_row, old_col);
2367                 }
2368 
2369                 calendar->highlight_row = row;
2370                 calendar->highlight_col = col;
2371 
2372                 if (row > -1 && col > -1)
2373                     calendar_invalidate_day (calendar, row, col);
2374             }
2375         }
2376     }
2377     return TRUE;
2378 }
2379 
2380 /*------------------------------------------------------------------------------*/
2381 
2382 static gboolean
gui_calendar_enter_notify(GtkWidget * widget,GdkEventCrossing * event)2383 gui_calendar_enter_notify (GtkWidget        *widget,
2384                            GdkEventCrossing *event) {
2385     GuiCalendar *calendar = GUI_CALENDAR (widget);
2386     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2387 
2388     if (event->window == priv->arrow_win[ARROW_MONTH_LEFT]) {
2389         priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_FLAG_PRELIGHT;
2390         calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2391     }
2392 
2393     if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT]) {
2394         priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_FLAG_PRELIGHT;
2395         calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2396     }
2397 
2398     if (event->window == priv->arrow_win[ARROW_YEAR_LEFT]) {
2399         priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_FLAG_PRELIGHT;
2400         calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2401     }
2402 
2403     if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT]) {
2404         priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_FLAG_PRELIGHT;
2405         calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2406     }
2407 
2408     return TRUE;
2409 }
2410 
2411 /*------------------------------------------------------------------------------*/
2412 
2413 static gboolean
gui_calendar_leave_notify(GtkWidget * widget,GdkEventCrossing * event)2414 gui_calendar_leave_notify (GtkWidget        *widget,
2415                            GdkEventCrossing *event) {
2416     GuiCalendar *calendar = GUI_CALENDAR (widget);
2417     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2418     gint row;
2419     gint col;
2420 
2421     if (event->window == priv->main_win) {
2422         row = calendar->highlight_row;
2423         col = calendar->highlight_col;
2424         calendar->highlight_row = -1;
2425         calendar->highlight_col = -1;
2426         if (row > -1 && col > -1)
2427             calendar_invalidate_day (calendar, row, col);
2428     }
2429 
2430     if (event->window == priv->arrow_win[ARROW_MONTH_LEFT]) {
2431         priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_FLAG_NORMAL;
2432         calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2433     }
2434 
2435     if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT]) {
2436         priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_FLAG_NORMAL;
2437         calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2438     }
2439 
2440     if (event->window == priv->arrow_win[ARROW_YEAR_LEFT]) {
2441         priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_FLAG_NORMAL;
2442         calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2443     }
2444 
2445     if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT]) {
2446         priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_FLAG_NORMAL;
2447         calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2448     }
2449 
2450     return TRUE;
2451 }
2452 
2453 /*------------------------------------------------------------------------------*/
2454 
2455 static gboolean
gui_calendar_scroll(GtkWidget * widget,GdkEventScroll * event)2456 gui_calendar_scroll (GtkWidget      *widget,
2457                      GdkEventScroll *event) {
2458     GuiCalendar *calendar = GUI_CALENDAR (widget);
2459 
2460     if (event->direction == GDK_SCROLL_UP) {
2461         if (!gtk_widget_has_focus (widget))
2462             gtk_widget_grab_focus (widget);
2463         calendar_set_month_prev (calendar);
2464     } else if (event->direction == GDK_SCROLL_DOWN) {
2465         if (!gtk_widget_has_focus (widget))
2466             gtk_widget_grab_focus (widget);
2467         calendar_set_month_next (calendar);
2468     } else
2469         return FALSE;
2470 
2471     return TRUE;
2472 }
2473 
2474 /*------------------------------------------------------------------------------*/
2475 
2476 static void
move_focus(GuiCalendar * calendar,gint direction)2477 move_focus (GuiCalendar *calendar,
2478             gint         direction) {
2479     GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
2480 
2481     if (calendar->enable_cursor == FALSE) {
2482         return;
2483     }
2484 
2485     if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
2486             (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) {
2487         if (calendar->focus_col > 0)
2488             calendar->focus_col--;
2489         else if (calendar->focus_row > 0) {
2490             calendar->focus_col = 6;
2491             calendar->focus_row--;
2492         }
2493 
2494         if (calendar->focus_col < 0)
2495             calendar->focus_col = 6;
2496         if (calendar->focus_row < 0)
2497             calendar->focus_row = 5;
2498     } else {
2499         if (calendar->focus_col < 6)
2500             calendar->focus_col++;
2501         else if (calendar->focus_row < 5) {
2502             calendar->focus_col = 0;
2503             calendar->focus_row++;
2504         }
2505 
2506         if (calendar->focus_col < 0)
2507             calendar->focus_col = 0;
2508         if (calendar->focus_row < 0)
2509             calendar->focus_row = 0;
2510     }
2511 }
2512 
2513 /*------------------------------------------------------------------------------*/
2514 
2515 static gboolean
gui_calendar_key_press(GtkWidget * widget,GdkEventKey * event)2516 gui_calendar_key_press (GtkWidget   *widget,
2517                         GdkEventKey *event) {
2518     GuiCalendar *calendar;
2519     gint return_val;
2520     gint old_focus_row;
2521     gint old_focus_col;
2522     gint row, col, day;
2523 
2524     calendar = GUI_CALENDAR (widget);
2525     return_val = FALSE;
2526 
2527     if (calendar->enable_cursor == FALSE) {
2528         return return_val;
2529     }
2530 
2531     old_focus_row = calendar->focus_row;
2532     old_focus_col = calendar->focus_col;
2533 
2534     switch (event->keyval) {
2535     case GDK_KEY_KP_Left:
2536     case GDK_KEY_Left:
2537         return_val = TRUE;
2538         if (event->state & GDK_CONTROL_MASK)
2539             calendar_set_month_prev (calendar);
2540         else {
2541             move_focus (calendar, -1);
2542             calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2543             calendar_invalidate_day (calendar, calendar->focus_row,
2544                                      calendar->focus_col);
2545         }
2546         break;
2547     case GDK_KEY_KP_Right:
2548     case GDK_KEY_Right:
2549         return_val = TRUE;
2550         if (event->state & GDK_CONTROL_MASK)
2551             calendar_set_month_next (calendar);
2552         else {
2553             move_focus (calendar, 1);
2554             calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2555             calendar_invalidate_day (calendar, calendar->focus_row,
2556                                      calendar->focus_col);
2557         }
2558         break;
2559     case GDK_KEY_KP_Up:
2560     case GDK_KEY_Up:
2561         return_val = TRUE;
2562         if (event->state & GDK_CONTROL_MASK)
2563             calendar_set_year_prev (calendar);
2564         else {
2565             if (calendar->focus_row > 0)
2566                 calendar->focus_row--;
2567             if (calendar->focus_row < 0)
2568                 calendar->focus_row = 5;
2569             if (calendar->focus_col < 0)
2570                 calendar->focus_col = 6;
2571             calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2572             calendar_invalidate_day (calendar, calendar->focus_row,
2573                                      calendar->focus_col);
2574         }
2575         break;
2576     case GDK_KEY_KP_Down:
2577     case GDK_KEY_Down:
2578         return_val = TRUE;
2579         if (event->state & GDK_CONTROL_MASK)
2580             calendar_set_year_next (calendar);
2581         else {
2582             if (calendar->focus_row < 5)
2583                 calendar->focus_row++;
2584             if (calendar->focus_col < 0)
2585                 calendar->focus_col = 0;
2586             calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
2587             calendar_invalidate_day (calendar, calendar->focus_row,
2588                                      calendar->focus_col);
2589         }
2590         break;
2591     case GDK_KEY_KP_Space:
2592     case GDK_KEY_space:
2593         row = calendar->focus_row;
2594         col = calendar->focus_col;
2595 
2596         if (row > -1 && col > -1) {
2597             return_val = TRUE;
2598 
2599             day = calendar->day[row][col];
2600             if (calendar->day_month[row][col] == MONTH_PREV)
2601                 calendar_set_month_prev (calendar);
2602             else if (calendar->day_month[row][col] == MONTH_NEXT)
2603                 calendar_set_month_next (calendar);
2604 
2605             calendar_select_and_focus_day (calendar, day);
2606         }
2607     }
2608 
2609     return return_val;
2610 }
2611 
2612 /*------------------------------------------------------------------------------*/
2613 
2614 static void
gui_calendar_style_set(GtkWidget * widget,GtkStyle * previous_style)2615 gui_calendar_style_set (GtkWidget *widget, GtkStyle  *previous_style) { }
2616 
2617 /*------------------------------------------------------------------------------*/
2618 
2619 static void
gui_calendar_state_changed(GtkWidget * widget,GtkStateType previous_state)2620 gui_calendar_state_changed (GtkWidget      *widget,
2621                             GtkStateType    previous_state) {
2622     GuiCalendar *calendar = GUI_CALENDAR (widget);
2623     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2624     int i;
2625 
2626     if (!gtk_widget_is_sensitive (widget)) {
2627         priv->in_drag = 0;
2628         calendar_stop_spinning (calendar);
2629     }
2630 
2631     for (i = 0; i < 4; i++)
2632         if (gtk_widget_is_sensitive (widget))
2633             priv->arrow_state[i] = GTK_STATE_FLAG_NORMAL;
2634         else
2635             priv->arrow_state[i] = GTK_STATE_FLAG_INSENSITIVE;
2636 }
2637 
2638 /*------------------------------------------------------------------------------*/
2639 
2640 static void
gui_calendar_grab_notify(GtkWidget * widget,gboolean was_grabbed)2641 gui_calendar_grab_notify (GtkWidget *widget,
2642                           gboolean   was_grabbed) {
2643     if (!was_grabbed)
2644         calendar_stop_spinning (GUI_CALENDAR (widget));
2645 }
2646 
2647 /*------------------------------------------------------------------------------*/
2648 
2649 static gboolean
gui_calendar_focus_out(GtkWidget * widget,GdkEventFocus * event)2650 gui_calendar_focus_out (GtkWidget     *widget,
2651                         GdkEventFocus *event) {
2652     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2653 
2654     gtk_widget_queue_draw (widget);
2655 
2656     calendar_stop_spinning (GUI_CALENDAR (widget));
2657 
2658     priv->in_drag = 0;
2659 
2660     return FALSE;
2661 }
2662 
2663 /*------------------------------------------------------------------------------*/
2664 
2665 static void
gui_calendar_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)2666 gui_calendar_drag_data_get (GtkWidget        *widget,
2667                             GdkDragContext   *context,
2668                             GtkSelectionData *selection_data,
2669                             guint             info,
2670                             guint             time) {
2671     GuiCalendar *calendar = GUI_CALENDAR (widget);
2672     GDate *date;
2673     gchar str[128];
2674     gsize len;
2675 
2676     date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
2677     len = g_date_strftime (str, 127, "%x", date);
2678     gtk_selection_data_set_text (selection_data, str, len);
2679 
2680     g_date_free (date);
2681 }
2682 
2683 /*------------------------------------------------------------------------------*/
2684 /* Get/set whether drag_motion requested the drag data and
2685  * drag_data_received should thus not actually insert the data,
2686  * since the data doesn't result from a drop.
2687  */
2688 
2689 static void
set_status_pending(GdkDragContext * context,GdkDragAction suggested_action)2690 set_status_pending (GdkDragContext *context,
2691                     GdkDragAction   suggested_action) {
2692     g_object_set_data (G_OBJECT (context),
2693                        "gtk-calendar-status-pending",
2694                        GINT_TO_POINTER (suggested_action));
2695 }
2696 
2697 /*------------------------------------------------------------------------------*/
2698 
2699 static GdkDragAction
get_status_pending(GdkDragContext * context)2700 get_status_pending (GdkDragContext *context) {
2701     return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
2702                             "gtk-calendar-status-pending"));
2703 }
2704 
2705 /*------------------------------------------------------------------------------*/
2706 
2707 static void
gui_calendar_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time)2708 gui_calendar_drag_leave (GtkWidget      *widget,
2709                          GdkDragContext *context,
2710                          guint           time) {
2711     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2712 
2713     priv->drag_highlight = 0;
2714     gtk_drag_unhighlight (widget);
2715 
2716 }
2717 
2718 /*------------------------------------------------------------------------------*/
2719 
2720 static gboolean
gui_calendar_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)2721 gui_calendar_drag_motion (GtkWidget      *widget,
2722                           GdkDragContext *context,
2723                           gint            x,
2724                           gint            y,
2725                           guint           time) {
2726     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (widget);
2727     GdkAtom target;
2728 
2729     if (!priv->drag_highlight) {
2730         priv->drag_highlight = 1;
2731         gtk_drag_highlight (widget);
2732     }
2733 
2734     target = gtk_drag_dest_find_target (widget, context, NULL);
2735     if (target == GDK_NONE || gdk_drag_context_get_suggested_action(context) == GDK_ACTION_DEFAULT)
2736         gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
2737     else {
2738         set_status_pending (context, gdk_drag_context_get_suggested_action(context));
2739         gtk_drag_get_data (widget, context, target, time);
2740     }
2741 
2742     return TRUE;
2743 }
2744 
2745 /*------------------------------------------------------------------------------*/
2746 
2747 static gboolean
gui_calendar_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)2748 gui_calendar_drag_drop (GtkWidget      *widget,
2749                         GdkDragContext *context,
2750                         gint            x,
2751                         gint            y,
2752                         guint           time) {
2753     GdkAtom target;
2754 
2755     target = gtk_drag_dest_find_target (widget, context, NULL);
2756     if (target != GDK_NONE) {
2757         gtk_drag_get_data (widget, context,
2758                            target,
2759                            time);
2760         return TRUE;
2761     }
2762 
2763     return FALSE;
2764 }
2765 
2766 /*------------------------------------------------------------------------------*/
2767 
2768 static void
gui_calendar_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)2769 gui_calendar_drag_data_received (GtkWidget        *widget,
2770                                  GdkDragContext   *context,
2771                                  gint              x,
2772                                  gint              y,
2773                                  GtkSelectionData *selection_data,
2774                                  guint             info,
2775                                  guint             time) {
2776     GuiCalendar *calendar = GUI_CALENDAR (widget);
2777     guint day, month, year;
2778     gchar *str;
2779     GDate *date;
2780     GdkDragAction suggested_action;
2781 
2782     suggested_action = get_status_pending (context);
2783 
2784     if (suggested_action) {
2785         set_status_pending (context, 0);
2786 
2787         /* We are getting this data due to a request in drag_motion,
2788          * rather than due to a request in drag_drop, so we are just
2789          * supposed to call drag_status, not actually paste in the
2790          * data.
2791          */
2792         str = (gchar *) gtk_selection_data_get_text (selection_data);
2793         if (str) {
2794             date = g_date_new ();
2795             g_date_set_parse (date, str);
2796             if (!g_date_valid (date))
2797                 suggested_action = 0;
2798             g_date_free (date);
2799             g_free (str);
2800         } else
2801             suggested_action = 0;
2802 
2803         gdk_drag_status (context, suggested_action, time);
2804 
2805         return;
2806     }
2807 
2808     date = g_date_new ();
2809     str = (gchar *) gtk_selection_data_get_text (selection_data);
2810     if (str) {
2811         g_date_set_parse (date, str);
2812         g_free (str);
2813     }
2814 
2815     if (!g_date_valid (date)) {
2816         g_warning ("Received invalid date data\n");
2817         g_date_free (date);
2818         gtk_drag_finish (context, FALSE, FALSE, time);
2819         return;
2820     }
2821 
2822     day = g_date_get_day (date);
2823     month = g_date_get_month (date);
2824     year = g_date_get_year (date);
2825     g_date_free (date);
2826 
2827     gtk_drag_finish (context, TRUE, FALSE, time);
2828 
2829 
2830     g_object_freeze_notify (G_OBJECT (calendar));
2831     if (!(calendar->display_flags & GUI_CALENDAR_NO_MONTH_CHANGE)
2832             && (calendar->display_flags & GUI_CALENDAR_SHOW_HEADING))
2833         gui_calendar_select_month (calendar, month - 1, year);
2834     gui_calendar_select_day (calendar, day);
2835     g_object_thaw_notify (G_OBJECT (calendar));
2836 }
2837 
2838 /*------------------------------------------------------------------------------*/
2839 /* Public API */
2840 /*------------------------------------------------------------------------------*/
2841 
2842 GtkWidget*
gui_calendar_new(void)2843 gui_calendar_new (void) {
2844     return g_object_new (GUI_TYPE_CALENDAR, NULL);
2845 }
2846 
2847 /*------------------------------------------------------------------------------*/
2848 
2849 GuiCalendarDisplayOptions
gui_calendar_get_display_options(GuiCalendar * calendar)2850 gui_calendar_get_display_options (GuiCalendar         *calendar) {
2851     g_return_val_if_fail (GUI_IS_CALENDAR (calendar), 0);
2852 
2853     return calendar->display_flags;
2854 }
2855 
2856 /*------------------------------------------------------------------------------*/
2857 
2858 void
gui_calendar_set_display_options(GuiCalendar * calendar,GuiCalendarDisplayOptions flags)2859 gui_calendar_set_display_options (GuiCalendar          *calendar,
2860                                   GuiCalendarDisplayOptions flags) {
2861     GtkWidget *widget = GTK_WIDGET (calendar);
2862     GuiCalendarPrivate *priv = GUI_CALENDAR_GET_PRIVATE (calendar);
2863     gint resize = 0;
2864     gint i;
2865     GuiCalendarDisplayOptions old_flags;
2866 
2867     g_return_if_fail (GUI_IS_CALENDAR (calendar));
2868 
2869     old_flags = calendar->display_flags;
2870 
2871     if (gtk_widget_get_realized (widget)) {
2872         if ((flags ^ calendar->display_flags) & GUI_CALENDAR_NO_MONTH_CHANGE) {
2873             resize ++;
2874             if (! (flags & GUI_CALENDAR_NO_MONTH_CHANGE)
2875                     && (priv->header_win)) {
2876                 calendar->display_flags &= ~GUI_CALENDAR_NO_MONTH_CHANGE;
2877                 calendar_realize_arrows (calendar);
2878             } else {
2879                 for (i = 0; i < 4; i++) {
2880                     if (priv->arrow_win[i]) {
2881                         gdk_window_set_user_data (priv->arrow_win[i],
2882                                                   NULL);
2883                         gdk_window_destroy (priv->arrow_win[i]);
2884                         priv->arrow_win[i] = NULL;
2885                     }
2886                 }
2887             }
2888         }
2889 
2890         if ((flags ^ calendar->display_flags) & GUI_CALENDAR_SHOW_HEADING) {
2891             resize++;
2892 
2893             if (flags & GUI_CALENDAR_SHOW_HEADING) {
2894                 calendar->display_flags |= GUI_CALENDAR_SHOW_HEADING;
2895                 calendar_realize_header (calendar);
2896             } else {
2897                 for (i = 0; i < 4; i++) {
2898                     if (priv->arrow_win[i]) {
2899                         gdk_window_set_user_data (priv->arrow_win[i],
2900                                                   NULL);
2901                         gdk_window_destroy (priv->arrow_win[i]);
2902                         priv->arrow_win[i] = NULL;
2903                     }
2904                 }
2905                 gdk_window_set_user_data (priv->header_win, NULL);
2906                 gdk_window_destroy (priv->header_win);
2907                 priv->header_win = NULL;
2908             }
2909         }
2910 
2911 
2912         if ((flags ^ calendar->display_flags) & GUI_CALENDAR_SHOW_DAY_NAMES) {
2913             resize++;
2914 
2915             if (flags & GUI_CALENDAR_SHOW_DAY_NAMES) {
2916                 calendar->display_flags |= GUI_CALENDAR_SHOW_DAY_NAMES;
2917                 calendar_realize_day_names (calendar);
2918             } else {
2919                 gdk_window_set_user_data (priv->day_name_win, NULL);
2920                 gdk_window_destroy (priv->day_name_win);
2921                 priv->day_name_win = NULL;
2922             }
2923         }
2924 
2925         if ((flags ^ calendar->display_flags) & GUI_CALENDAR_SHOW_WEEK_NUMBERS) {
2926             resize++;
2927 
2928             if (flags & GUI_CALENDAR_SHOW_WEEK_NUMBERS) {
2929                 calendar->display_flags |= GUI_CALENDAR_SHOW_WEEK_NUMBERS;
2930                 calendar_realize_week_numbers (calendar);
2931             } else {
2932                 gdk_window_set_user_data (priv->week_win, NULL);
2933                 gdk_window_destroy (priv->week_win);
2934                 priv->week_win = NULL;
2935             }
2936         }
2937 
2938         calendar->display_flags = flags;
2939         if (resize)
2940             gtk_widget_queue_resize (GTK_WIDGET (calendar));
2941 
2942     } else
2943         calendar->display_flags = flags;
2944 
2945     g_object_freeze_notify (G_OBJECT (calendar));
2946 
2947 	if ((old_flags ^ calendar->display_flags) & GUI_CALENDAR_SHOW_HEADING)
2948         g_object_notify (G_OBJECT (calendar), "show-heading");
2949 
2950 	if ((old_flags ^ calendar->display_flags) & GUI_CALENDAR_SHOW_DAY_NAMES)
2951         g_object_notify (G_OBJECT (calendar), "show-day-names");
2952 
2953 	if ((old_flags ^ calendar->display_flags) & GUI_CALENDAR_NO_MONTH_CHANGE)
2954         g_object_notify (G_OBJECT (calendar), "no-month-change");
2955 
2956 	if ((old_flags ^ calendar->display_flags) & GUI_CALENDAR_SHOW_WEEK_NUMBERS)
2957         g_object_notify (G_OBJECT (calendar), "show-week-numbers");
2958 
2959 	if ((old_flags ^ calendar->display_flags) & GUI_CALENDAR_WEEK_START_MONDAY)
2960         g_object_notify (G_OBJECT (calendar), "week-start-monday");
2961 
2962 	g_object_thaw_notify (G_OBJECT (calendar));
2963 }
2964 
2965 /*------------------------------------------------------------------------------*/
2966 
2967 gboolean
gui_calendar_select_month(GuiCalendar * calendar,guint month,guint year)2968 gui_calendar_select_month (GuiCalendar *calendar,
2969                            guint    month,
2970                            guint    year) {
2971     g_return_val_if_fail (GUI_IS_CALENDAR (calendar), FALSE);
2972     g_return_val_if_fail (month <= 11, FALSE);
2973 
2974     calendar->month = month;
2975     calendar->year  = year;
2976 
2977     calendar_compute_days (calendar);
2978 
2979     gtk_widget_queue_draw (GTK_WIDGET (calendar));
2980 
2981     g_object_freeze_notify (G_OBJECT (calendar));
2982     g_object_notify (G_OBJECT (calendar), "month");
2983     g_object_notify (G_OBJECT (calendar), "year");
2984     g_object_thaw_notify (G_OBJECT (calendar));
2985 
2986     g_signal_emit (calendar,
2987                    gui_calendar_signals[MONTH_CHANGED_SIGNAL],
2988                    0);
2989     return TRUE;
2990 }
2991 
2992 /*------------------------------------------------------------------------------*/
2993 
2994 void
gui_calendar_select_day(GuiCalendar * calendar,guint day)2995 gui_calendar_select_day (GuiCalendar *calendar,
2996                          guint        day) {
2997     g_return_if_fail (GUI_IS_CALENDAR (calendar));
2998     g_return_if_fail (day <= 31);
2999 
3000     /* deselect the old day */
3001     if (calendar->selected_day > 0) {
3002         gint selected_day;
3003 
3004         selected_day = calendar->selected_day;
3005         calendar->selected_day = 0;
3006         if (gtk_widget_is_drawable (GTK_WIDGET (calendar)))
3007             calendar_invalidate_day_num (calendar, selected_day);
3008     }
3009 
3010     calendar->selected_day = day;
3011 
3012     /* select the new day */
3013     if (day != 0) {
3014         if (gtk_widget_is_drawable (GTK_WIDGET (calendar)))
3015             calendar_invalidate_day_num (calendar, day);
3016     }
3017 
3018     g_object_notify (G_OBJECT (calendar), "day");
3019 
3020     g_signal_emit (calendar,
3021                    gui_calendar_signals[DAY_SELECTED_SIGNAL],
3022                    0);
3023 }
3024 
3025 /*------------------------------------------------------------------------------*/
3026 
3027 void
gui_calendar_clear_marks(GuiCalendar * calendar,gint mark_type)3028 gui_calendar_clear_marks (GuiCalendar *calendar, gint mark_type) {
3029 
3030 guint day;
3031 
3032 	g_return_if_fail (GUI_IS_CALENDAR (calendar));
3033 
3034 	switch (mark_type) {
3035 
3036 		case DAY_NOTE_MARK:
3037 			for (day = 0; day < 31; day++) {
3038 				calendar->marked_date[day] = FALSE;
3039 			}
3040 
3041 			calendar->num_marked_dates = 0;
3042 			break;
3043 
3044 		case EVENT_MARK:
3045 			for (day = 0; day < 31; day++) {
3046 				calendar->event_marked_date[day] = FALSE;
3047 			}
3048 
3049 			calendar->event_num_marked_dates = 0;
3050 			break;
3051 
3052 		case BIRTHDAY_MARK:
3053 			for (day = 0; day < 31; day++) {
3054 				calendar->birthday_marked_date[day] = FALSE;
3055 			}
3056 
3057 			calendar->birthday_num_marked_dates = 0;
3058 			break;
3059 	}
3060 
3061     gtk_widget_queue_draw (GTK_WIDGET (calendar));
3062 }
3063 
3064 /*------------------------------------------------------------------------------*/
3065 
3066 gboolean
gui_calendar_mark_day(GuiCalendar * calendar,guint day,gint mark_type)3067 gui_calendar_mark_day (GuiCalendar *calendar, guint day, gint mark_type) {
3068 
3069 	g_return_val_if_fail (GUI_IS_CALENDAR (calendar), FALSE);
3070 
3071 	switch (mark_type) {
3072 
3073 		case DAY_NOTE_MARK:
3074 			if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE) {
3075 				calendar->marked_date[day - 1] = TRUE;
3076 				GtkWidget *widget = GTK_WIDGET (calendar);
3077                 get_background_color(widget, &calendar->marked_date_color[day - 1]);
3078 				calendar->num_marked_dates++;
3079 				calendar_invalidate_day_num (calendar, day);
3080 			}
3081 			break;
3082 
3083 		case EVENT_MARK:
3084 			if (day >= 1 && day <= 31 && calendar->event_marked_date[day-1] == FALSE) {
3085 				calendar->event_marked_date[day - 1] = TRUE;
3086 				calendar->event_num_marked_dates++;
3087 				calendar_invalidate_day_num (calendar, day);
3088 			}
3089 			break;
3090 
3091 		case BIRTHDAY_MARK:
3092 			if (day >= 1 && day <= 31 && calendar->birthday_marked_date[day-1] == FALSE) {
3093 				calendar->birthday_marked_date[day - 1] = TRUE;
3094 				calendar->birthday_num_marked_dates++;
3095 				calendar_invalidate_day_num (calendar, day);
3096 			}
3097 			break;
3098 	}
3099 
3100 	return TRUE;
3101 }
3102 
3103 /*------------------------------------------------------------------------------*/
3104 
3105 gboolean
gui_calendar_unmark_day(GuiCalendar * calendar,guint day,gint mark_type)3106 gui_calendar_unmark_day (GuiCalendar *calendar, guint day, gint mark_type) {
3107 
3108 	g_return_val_if_fail (GUI_IS_CALENDAR (calendar), FALSE);
3109 
3110 	switch (mark_type) {
3111 
3112 		case DAY_NOTE_MARK:
3113 			if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE) {
3114 				calendar->marked_date[day - 1] = FALSE;
3115 				GtkWidget *widget = GTK_WIDGET (calendar);
3116 				get_background_color(widget, &calendar->marked_date_color[day - 1]);
3117 				calendar->num_marked_dates--;
3118 				calendar_invalidate_day_num (calendar, day);
3119 			}
3120 			break;
3121 
3122 		case EVENT_MARK:
3123 			if (day >= 1 && day <= 31 && calendar->event_marked_date[day-1] == TRUE) {
3124 				calendar->event_marked_date[day - 1] = FALSE;
3125 				calendar->event_num_marked_dates--;
3126 				calendar_invalidate_day_num (calendar, day);
3127 			}
3128 			break;
3129 
3130 		case BIRTHDAY_MARK:
3131 			if (day >= 1 && day <= 31 && calendar->birthday_marked_date[day-1] == TRUE) {
3132 				calendar->birthday_marked_date[day - 1] = FALSE;
3133 				calendar->birthday_num_marked_dates--;
3134 				calendar_invalidate_day_num (calendar, day);
3135 			}
3136 			break;
3137 	}
3138 
3139 	return TRUE;
3140 }
3141 
3142 /*------------------------------------------------------------------------------*/
3143 
3144 gboolean
gui_calendar_set_day_color(GuiCalendar * calendar,guint day,gchar * color_str)3145 gui_calendar_set_day_color (GuiCalendar *calendar,
3146                             guint        day,
3147                             gchar       *color_str) {
3148 
3149 	g_return_val_if_fail (GUI_IS_CALENDAR (calendar), FALSE);
3150 
3151     if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE) {
3152         calendar->marked_date[day - 1] = TRUE;
3153         GtkWidget *widget = GTK_WIDGET (calendar);
3154         get_background_color(widget, &calendar->marked_date_color[day - 1]);
3155         if (color_str != NULL) {
3156             gdk_rgba_parse(&calendar->marked_date_color[day - 1], color_str);
3157         }
3158         calendar->num_marked_dates++;
3159         calendar_invalidate_day_num (calendar, day);
3160     }
3161 
3162     return TRUE;
3163 }
3164 
3165 /*------------------------------------------------------------------------------*/
3166 
3167 void
gui_calendar_get_date(GuiCalendar * calendar,guint * year,guint * month,guint * day)3168 gui_calendar_get_date (GuiCalendar *calendar,
3169                        guint       *year,
3170                        guint       *month,
3171                        guint       *day) {
3172     g_return_if_fail (GUI_IS_CALENDAR (calendar));
3173 
3174     if (year)
3175         *year = calendar->year;
3176 
3177     if (month)
3178         *month = calendar->month;
3179 
3180     if (day)
3181         *day = calendar->selected_day;
3182 }
3183 
3184 /*------------------------------------------------------------------------------*/
3185 
3186 void
gui_calendar_get_gdate(GuiCalendar * calendar,GDate * cdate)3187 gui_calendar_get_gdate (GuiCalendar *calendar, GDate *cdate)
3188 {
3189     g_return_if_fail (GUI_IS_CALENDAR (calendar));
3190     g_date_set_dmy (cdate, calendar->selected_day, calendar->month + 1, calendar->year);
3191 }
3192 
3193 /*------------------------------------------------------------------------------*/
3194 
3195 void
gui_calendar_set_color(GuiCalendar * calendar,const gchar * color,gint alpha_value,gint color_type)3196 gui_calendar_set_color (GuiCalendar *calendar, const gchar *color, gint alpha_value, gint color_type) {
3197 
3198 	g_return_if_fail (GUI_IS_CALENDAR (calendar));
3199 
3200 	switch (color_type) {
3201 
3202 		case BACKGROUND_COLOR:
3203             gdk_rgba_parse(&calendar->background_color, color);
3204 			break;
3205 		case HEADER_BG_COLOR:
3206             gdk_rgba_parse(&calendar->header_bg_color, color);
3207 			break;
3208 		case HEADER_FG_COLOR:
3209             gdk_rgba_parse(&calendar->header_fg_color, color);
3210 			break;
3211 		case WEEKEND_COLOR:
3212             gdk_rgba_parse(&calendar->weekend_color, color);
3213 			break;
3214 		case SELECTOR_COLOR:
3215             gdk_rgba_parse(&calendar->selector_color, color);
3216             calendar->selector_color.alpha = (gdouble) alpha_value / 65535.0;
3217 			break;
3218 		case EVENT_MARKER_COLOR:
3219             gdk_rgba_parse(&calendar->event_marker_color, color);
3220 			break;
3221 		case TODAY_MARKER_COLOR:
3222             gdk_rgba_parse(&calendar->today_marker_color, color);
3223             calendar->today_marker_color.alpha = (gdouble) alpha_value / 65535.0;
3224 			break;
3225 		case BIRTHDAY_MARKER_COLOR:
3226             gdk_rgba_parse(&calendar->birthday_marker_color, color);
3227 			break;
3228 		case DAY_COLOR:
3229             gdk_rgba_parse(&calendar->day_color, color);
3230 			break;
3231 		case PF_DAY_COLOR:
3232             gdk_rgba_parse(&calendar->pf_day_color, color);
3233 			break;
3234 	}
3235 }
3236 
3237 /*------------------------------------------------------------------------------*/
3238 
3239 void
gui_calendar_set_marker(GuiCalendar * calendar,gint marker_type,gint marker)3240 gui_calendar_set_marker (GuiCalendar *calendar, gint marker_type, gint marker) {
3241 
3242 	g_return_if_fail (GUI_IS_CALENDAR (calendar));
3243 
3244 	switch (marker) {
3245 
3246 		case TODAY_MARKER:
3247             calendar->today_marker_type = marker_type;
3248 			break;
3249 		case EVENT_MARKER:
3250             calendar->event_marker_type = marker_type;
3251 			break;
3252 	}
3253 }
3254 
3255 /*------------------------------------------------------------------------------*/
3256 
3257 void
gui_calendar_set_day_note_marker_symbol(GuiCalendar * calendar,gchar * symbol)3258 gui_calendar_set_day_note_marker_symbol (GuiCalendar *calendar, gchar *symbol) {
3259     g_return_if_fail (GUI_IS_CALENDAR (calendar));
3260     calendar->mark_sign = g_utf8_get_char (symbol);
3261 }
3262 
3263 /*------------------------------------------------------------------------------*/
3264 
3265 void
gui_calendar_enable_cursor(GuiCalendar * calendar,gboolean state)3266 gui_calendar_enable_cursor (GuiCalendar *calendar, gboolean state) {
3267     g_return_if_fail (GUI_IS_CALENDAR (calendar));
3268     calendar->enable_cursor = state;
3269 }
3270 
3271 /*------------------------------------------------------------------------------*/
3272 
3273 void
gui_calendar_set_cursor_type(GuiCalendar * calendar,gint cursor_type)3274 gui_calendar_set_cursor_type (GuiCalendar *calendar, gint cursor_type) {
3275     g_return_if_fail (GUI_IS_CALENDAR (calendar));
3276     calendar->cursor_type = cursor_type;
3277 }
3278 
3279 /*------------------------------------------------------------------------------*/
3280 
3281 void
gui_calendar_set_frame_cursor_thickness(GuiCalendar * calendar,gint thickness)3282 gui_calendar_set_frame_cursor_thickness (GuiCalendar *calendar, gint thickness) {
3283     g_return_if_fail (GUI_IS_CALENDAR (calendar));
3284     g_return_if_fail (thickness > 0 && thickness < 6);
3285     calendar->frame_cursor_thickness = thickness;
3286 }
3287 /*------------------------------------------------------------------------------*/
3288 
3289 static void
get_header_bg_color(GtkWidget * widget,GdkRGBA * color)3290 get_header_bg_color(GtkWidget *widget, GdkRGBA *color) {
3291     GuiCalendar *calendar = GUI_CALENDAR (widget);
3292 	color = &calendar->header_bg_color;
3293 }
3294 
3295 static void
get_background_color(GtkWidget * widget,GdkRGBA * color)3296 get_background_color(GtkWidget *widget, GdkRGBA *color) {
3297     GuiCalendar *calendar = GUI_CALENDAR (widget);
3298 	color = &calendar->background_color;
3299 }
3300 
3301