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