1 /*
2 * ECalendarItem - canvas item displaying a calendar.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Damon Chaplin <damon@ximian.com>
18 * Bolian Yin <bolian.yin@sun.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 */
22
23 #include "evolution-config.h"
24
25 #include <libebackend/libebackend.h>
26
27 #include "e-calendar-item.h"
28
29 #include <time.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <gtk/gtk.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <glib/gi18n.h>
35
36 #include "ea-widgets.h"
37 #include "e-misc-utils.h"
38 #include "e-util-enumtypes.h"
39
40 static const gint e_calendar_item_days_in_month[12] = {
41 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
42 };
43
44 #define DAYS_IN_MONTH(year, month) \
45 e_calendar_item_days_in_month[month] + (((month) == 1 \
46 && ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))) ? 1 : 0)
47
48 static void e_calendar_item_constructed (GObject *object);
49 static void e_calendar_item_dispose (GObject *object);
50 static void e_calendar_item_get_property (GObject *object,
51 guint property_id,
52 GValue *value,
53 GParamSpec *pspec);
54 static void e_calendar_item_set_property (GObject *object,
55 guint property_id,
56 const GValue *value,
57 GParamSpec *pspec);
58 static void e_calendar_item_realize (GnomeCanvasItem *item);
59 static void e_calendar_item_unmap (GnomeCanvasItem *item);
60 static void e_calendar_item_update (GnomeCanvasItem *item,
61 const cairo_matrix_t *i2c,
62 gint flags);
63 static void e_calendar_item_draw (GnomeCanvasItem *item,
64 cairo_t *cr,
65 gint x,
66 gint y,
67 gint width,
68 gint height);
69 static void e_calendar_item_draw_month (ECalendarItem *calitem,
70 cairo_t *cr,
71 gint x,
72 gint y,
73 gint width,
74 gint height,
75 gint row,
76 gint col);
77 static void e_calendar_item_draw_day_numbers
78 (ECalendarItem *calitem,
79 cairo_t *cr,
80 gint width,
81 gint height,
82 gint row,
83 gint col,
84 gint year,
85 gint month,
86 GDateWeekday start_weekday,
87 gint cells_x,
88 gint cells_y);
89 static GnomeCanvasItem *e_calendar_item_point (GnomeCanvasItem *item,
90 gdouble x,
91 gdouble y,
92 gint cx,
93 gint cy);
94 static void e_calendar_item_stop_selecting (ECalendarItem *calitem,
95 guint32 time);
96 static void e_calendar_item_selection_add_days
97 (ECalendarItem *calitem,
98 gint n_days,
99 gboolean multi_selection);
100 static gint e_calendar_item_key_press_event (ECalendarItem *item,
101 GdkEvent *event);
102 static gint e_calendar_item_event (GnomeCanvasItem *item,
103 GdkEvent *event);
104 static void e_calendar_item_bounds (GnomeCanvasItem *item,
105 gdouble *x1,
106 gdouble *y1,
107 gdouble *x2,
108 gdouble *y2);
109
110 static gboolean e_calendar_item_button_press (ECalendarItem *calitem,
111 GdkEvent *event);
112 static gboolean e_calendar_item_button_release (ECalendarItem *calitem,
113 GdkEvent *event);
114 static gboolean e_calendar_item_motion (ECalendarItem *calitem,
115 GdkEvent *event);
116
117 static gboolean e_calendar_item_convert_position_to_day
118 (ECalendarItem *calitem,
119 gint x,
120 gint y,
121 gboolean round_empty_positions,
122 gint *month_offset,
123 gint *day,
124 gboolean *entire_week);
125 static void e_calendar_item_get_month_info (ECalendarItem *calitem,
126 gint row,
127 gint col,
128 gint *first_day_offset,
129 gint *days_in_month,
130 gint *days_in_prev_month);
131 static void e_calendar_item_recalc_sizes (ECalendarItem *calitem);
132
133 static void e_calendar_item_get_day_style (ECalendarItem *calitem,
134 gint year,
135 gint month,
136 gint day,
137 gint day_style,
138 gboolean today,
139 gboolean prev_or_next_month,
140 gboolean selected,
141 gboolean has_focus,
142 gboolean drop_target,
143 GdkColor **bg_color,
144 GdkColor **fg_color,
145 GdkColor **box_color,
146 gboolean *bold,
147 gboolean *italic,
148 GdkColor *local_bg_color,
149 GdkColor *local_fg_color);
150 static void e_calendar_item_check_selection_end
151 (ECalendarItem *calitem,
152 gint start_month,
153 gint start_day,
154 gint *end_month,
155 gint *end_day);
156 static void e_calendar_item_check_selection_start
157 (ECalendarItem *calitem,
158 gint *start_month,
159 gint *start_day,
160 gint end_month,
161 gint end_day);
162 static void e_calendar_item_add_days_to_selection
163 (ECalendarItem *calitem,
164 gint days);
165 static void e_calendar_item_round_up_selection
166 (ECalendarItem *calitem,
167 gint *month_offset,
168 gint *day);
169 static void e_calendar_item_round_down_selection
170 (ECalendarItem *calitem,
171 gint *month_offset,
172 gint *day);
173 static gint e_calendar_item_get_inclusive_days
174 (ECalendarItem *calitem,
175 gint start_month_offset,
176 gint start_day,
177 gint end_month_offset,
178 gint end_day);
179 static void e_calendar_item_ensure_valid_day
180 (ECalendarItem *calitem,
181 gint *month_offset,
182 gint *day);
183 static gboolean e_calendar_item_ensure_days_visible
184 (ECalendarItem *calitem,
185 gint start_year,
186 gint start_month,
187 gint start_day,
188 gint end_year,
189 gint end_month,
190 gint end_day,
191 gboolean emission);
192 static void e_calendar_item_show_popup_menu (ECalendarItem *calitem,
193 GdkEvent *button_event,
194 gint month_offset);
195 static void e_calendar_item_on_menu_item_activate
196 (GtkWidget *menuitem,
197 ECalendarItem *calitem);
198 static void e_calendar_item_date_range_changed
199 (ECalendarItem *calitem);
200 static void e_calendar_item_queue_signal_emission
201 (ECalendarItem *calitem);
202 static gboolean e_calendar_item_signal_emission_idle_cb
203 (gpointer data);
204 static void e_calendar_item_set_selection_if_emission
205 (ECalendarItem *calitem,
206 const GDate *start_date,
207 const GDate *end_date,
208 gboolean emission);
209 static void e_calendar_item_set_first_month_with_emit
210 (ECalendarItem *calitem,
211 gint year,
212 gint month,
213 gboolean emit_date_range_moved);
214
215 /* Our arguments. */
216 enum {
217 PROP_0,
218 PROP_YEAR,
219 PROP_MONTH,
220 PROP_X1,
221 PROP_Y1,
222 PROP_X2,
223 PROP_Y2,
224 PROP_FONT_DESC,
225 PROP_WEEK_NUMBER_FONT,
226 PROP_WEEK_NUMBER_FONT_DESC,
227 PROP_ROW_HEIGHT,
228 PROP_COLUMN_WIDTH,
229 PROP_MINIMUM_ROWS,
230 PROP_MINIMUM_COLUMNS,
231 PROP_MAXIMUM_ROWS,
232 PROP_MAXIMUM_COLUMNS,
233 PROP_WEEK_START_DAY,
234 PROP_SHOW_WEEK_NUMBERS,
235 PROP_KEEP_WDAYS_ON_WEEKNUM_CLICK,
236 PROP_MAXIMUM_DAYS_SELECTED,
237 PROP_DAYS_TO_START_WEEK_SELECTION,
238 PROP_MOVE_SELECTION_WHEN_MOVING,
239 PROP_PRESERVE_DAY_WHEN_MOVING,
240 PROP_DISPLAY_POPUP
241 };
242
243 enum {
244 DATE_RANGE_CHANGED,
245 DATE_RANGE_MOVED,
246 SELECTION_CHANGED,
247 SELECTION_PREVIEW_CHANGED,
248 MONTH_WIDTH_CHANGED,
249 CALC_MIN_COLUMN_WIDTH,
250 LAST_SIGNAL
251 };
252
253 static guint e_calendar_item_signals[LAST_SIGNAL] = { 0 };
254
G_DEFINE_TYPE_WITH_CODE(ECalendarItem,e_calendar_item,GNOME_TYPE_CANVAS_ITEM,G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE,NULL))255 G_DEFINE_TYPE_WITH_CODE (
256 ECalendarItem,
257 e_calendar_item,
258 GNOME_TYPE_CANVAS_ITEM,
259 G_IMPLEMENT_INTERFACE (
260 E_TYPE_EXTENSIBLE, NULL))
261
262 static void
263 e_calendar_item_class_init (ECalendarItemClass *class)
264 {
265 GObjectClass *object_class;
266 GnomeCanvasItemClass *item_class;
267
268 object_class = G_OBJECT_CLASS (class);
269 object_class->constructed = e_calendar_item_constructed;
270 object_class->dispose = e_calendar_item_dispose;
271 object_class->get_property = e_calendar_item_get_property;
272 object_class->set_property = e_calendar_item_set_property;
273
274 item_class = GNOME_CANVAS_ITEM_CLASS (class);
275 item_class->realize = e_calendar_item_realize;
276 item_class->unmap = e_calendar_item_unmap;
277 item_class->update = e_calendar_item_update;
278 item_class->draw = e_calendar_item_draw;
279 item_class->point = e_calendar_item_point;
280 item_class->event = e_calendar_item_event;
281 item_class->bounds = e_calendar_item_bounds;
282
283 class->date_range_changed = NULL;
284 class->selection_changed = NULL;
285 class->selection_preview_changed = NULL;
286
287 g_object_class_install_property (
288 object_class,
289 PROP_YEAR,
290 g_param_spec_int (
291 "year",
292 NULL,
293 NULL,
294 G_MININT,
295 G_MAXINT,
296 0,
297 G_PARAM_READWRITE));
298
299 g_object_class_install_property (
300 object_class,
301 PROP_MONTH,
302 g_param_spec_int (
303 "month",
304 NULL,
305 NULL,
306 G_MININT,
307 G_MAXINT,
308 0,
309 G_PARAM_READWRITE));
310
311 g_object_class_install_property (
312 object_class,
313 PROP_X1,
314 g_param_spec_double (
315 "x1",
316 NULL,
317 NULL,
318 -G_MAXDOUBLE,
319 G_MAXDOUBLE,
320 0.,
321 G_PARAM_READWRITE));
322
323 g_object_class_install_property (
324 object_class,
325 PROP_Y1,
326 g_param_spec_double (
327 "y1",
328 NULL,
329 NULL,
330 -G_MAXDOUBLE,
331 G_MAXDOUBLE,
332 0.,
333 G_PARAM_READWRITE));
334
335 g_object_class_install_property (
336 object_class,
337 PROP_X2,
338 g_param_spec_double (
339 "x2",
340 NULL,
341 NULL,
342 -G_MAXDOUBLE,
343 G_MAXDOUBLE,
344 0.,
345 G_PARAM_READWRITE));
346
347 g_object_class_install_property (
348 object_class,
349 PROP_Y2,
350 g_param_spec_double (
351 "y2",
352 NULL,
353 NULL,
354 -G_MAXDOUBLE,
355 G_MAXDOUBLE,
356 0.,
357 G_PARAM_READWRITE));
358
359 g_object_class_install_property (
360 object_class,
361 PROP_FONT_DESC,
362 g_param_spec_boxed (
363 "font_desc",
364 NULL,
365 NULL,
366 PANGO_TYPE_FONT_DESCRIPTION,
367 G_PARAM_READWRITE));
368
369 g_object_class_install_property (
370 object_class,
371 PROP_WEEK_NUMBER_FONT_DESC,
372 g_param_spec_boxed (
373 "week_number_font_desc",
374 NULL,
375 NULL,
376 PANGO_TYPE_FONT_DESCRIPTION,
377 G_PARAM_READWRITE));
378
379 g_object_class_install_property (
380 object_class,
381 PROP_ROW_HEIGHT,
382 g_param_spec_int (
383 "row_height",
384 NULL,
385 NULL,
386 G_MININT,
387 G_MAXINT,
388 0,
389 G_PARAM_READABLE));
390
391 g_object_class_install_property (
392 object_class,
393 PROP_COLUMN_WIDTH,
394 g_param_spec_int (
395 "column_width",
396 NULL,
397 NULL,
398 G_MININT,
399 G_MAXINT,
400 0,
401 G_PARAM_READABLE));
402
403 g_object_class_install_property (
404 object_class,
405 PROP_MINIMUM_ROWS,
406 g_param_spec_int (
407 "minimum_rows",
408 NULL,
409 NULL,
410 G_MININT,
411 G_MAXINT,
412 0,
413 G_PARAM_READWRITE));
414
415 g_object_class_install_property (
416 object_class,
417 PROP_MINIMUM_COLUMNS,
418 g_param_spec_int (
419 "minimum_columns",
420 NULL,
421 NULL,
422 G_MININT,
423 G_MAXINT,
424 0,
425 G_PARAM_READWRITE));
426
427 g_object_class_install_property (
428 object_class,
429 PROP_MAXIMUM_ROWS,
430 g_param_spec_int (
431 "maximum_rows",
432 NULL,
433 NULL,
434 G_MININT,
435 G_MAXINT,
436 0,
437 G_PARAM_READWRITE));
438
439 g_object_class_install_property (
440 object_class,
441 PROP_MAXIMUM_COLUMNS,
442 g_param_spec_int (
443 "maximum_columns",
444 NULL,
445 NULL,
446 G_MININT,
447 G_MAXINT,
448 0,
449 G_PARAM_READWRITE));
450
451 g_object_class_install_property (
452 object_class,
453 PROP_WEEK_START_DAY,
454 g_param_spec_enum (
455 "week-start-day",
456 NULL,
457 NULL,
458 E_TYPE_DATE_WEEKDAY,
459 G_DATE_MONDAY,
460 G_PARAM_READWRITE |
461 G_PARAM_STATIC_STRINGS));
462
463 g_object_class_install_property (
464 object_class,
465 PROP_SHOW_WEEK_NUMBERS,
466 g_param_spec_boolean (
467 "show_week_numbers",
468 NULL,
469 NULL,
470 TRUE,
471 G_PARAM_READWRITE));
472
473 g_object_class_install_property (
474 object_class,
475 PROP_KEEP_WDAYS_ON_WEEKNUM_CLICK,
476 g_param_spec_boolean (
477 "keep_wdays_on_weeknum_click",
478 NULL,
479 NULL,
480 FALSE,
481 G_PARAM_READWRITE));
482
483 g_object_class_install_property (
484 object_class,
485 PROP_MAXIMUM_DAYS_SELECTED,
486 g_param_spec_int (
487 "maximum_days_selected",
488 NULL,
489 NULL,
490 G_MININT,
491 G_MAXINT,
492 0,
493 G_PARAM_READWRITE));
494
495 g_object_class_install_property (
496 object_class,
497 PROP_DAYS_TO_START_WEEK_SELECTION,
498 g_param_spec_int (
499 "days_to_start_week_selection",
500 NULL,
501 NULL,
502 G_MININT,
503 G_MAXINT,
504 0,
505 G_PARAM_READWRITE));
506
507 g_object_class_install_property (
508 object_class,
509 PROP_MOVE_SELECTION_WHEN_MOVING,
510 g_param_spec_boolean (
511 "move_selection_when_moving",
512 NULL,
513 NULL,
514 TRUE,
515 G_PARAM_READWRITE));
516
517 g_object_class_install_property (
518 object_class,
519 PROP_PRESERVE_DAY_WHEN_MOVING,
520 g_param_spec_boolean (
521 "preserve_day_when_moving",
522 NULL,
523 NULL,
524 TRUE,
525 G_PARAM_READWRITE));
526
527 g_object_class_install_property (
528 object_class,
529 PROP_DISPLAY_POPUP,
530 g_param_spec_boolean (
531 "display_popup",
532 NULL,
533 NULL,
534 TRUE,
535 G_PARAM_READWRITE));
536
537 e_calendar_item_signals[DATE_RANGE_CHANGED] = g_signal_new (
538 "date_range_changed",
539 G_TYPE_FROM_CLASS (object_class),
540 G_SIGNAL_RUN_FIRST,
541 G_STRUCT_OFFSET (ECalendarItemClass, date_range_changed),
542 NULL, NULL,
543 g_cclosure_marshal_VOID__VOID,
544 G_TYPE_NONE, 0);
545
546 /* Invoked when a user changes date range, by pressing month/year
547 arrows or any similar way, but not when selecting a day in the calendar. */
548 e_calendar_item_signals[DATE_RANGE_MOVED] = g_signal_new (
549 "date-range-moved",
550 G_TYPE_FROM_CLASS (object_class),
551 G_SIGNAL_RUN_FIRST,
552 0, NULL, NULL,
553 g_cclosure_marshal_VOID__VOID,
554 G_TYPE_NONE, 0);
555
556 e_calendar_item_signals[SELECTION_CHANGED] = g_signal_new (
557 "selection_changed",
558 G_TYPE_FROM_CLASS (object_class),
559 G_SIGNAL_RUN_FIRST,
560 G_STRUCT_OFFSET (ECalendarItemClass, selection_changed),
561 NULL, NULL,
562 g_cclosure_marshal_VOID__VOID,
563 G_TYPE_NONE, 0);
564
565 e_calendar_item_signals[SELECTION_PREVIEW_CHANGED] = g_signal_new (
566 "selection_preview_changed",
567 G_TYPE_FROM_CLASS (object_class),
568 G_SIGNAL_RUN_LAST,
569 G_STRUCT_OFFSET (ECalendarItemClass, selection_preview_changed),
570 NULL, NULL,
571 g_cclosure_marshal_VOID__VOID,
572 G_TYPE_NONE, 0);
573
574 e_calendar_item_signals[MONTH_WIDTH_CHANGED] = g_signal_new (
575 "month-width-changed",
576 G_TYPE_FROM_CLASS (object_class),
577 G_SIGNAL_RUN_LAST,
578 0 /* G_STRUCT_OFFSET (ECalendarItemClass, month_width_changed) */,
579 NULL, NULL,
580 g_cclosure_marshal_VOID__VOID,
581 G_TYPE_NONE, 0);
582
583 e_calendar_item_signals[CALC_MIN_COLUMN_WIDTH] = g_signal_new (
584 "calc-min-column-width",
585 G_TYPE_FROM_CLASS (object_class),
586 G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST,
587 0 /* G_STRUCT_OFFSET (ECalendarItemClass, calc_min_column_width) */,
588 NULL, NULL,
589 NULL,
590 G_TYPE_INT, 0);
591
592 e_calendar_item_a11y_init ();
593 }
594
595 static void
e_calendar_item_init(ECalendarItem * calitem)596 e_calendar_item_init (ECalendarItem *calitem)
597 {
598 struct tm *tmp_tm;
599 time_t t;
600
601 /* Set the default time to the current month. */
602 t = time (NULL);
603 tmp_tm = localtime (&t);
604 calitem->year = tmp_tm->tm_year + 1900;
605 calitem->month = tmp_tm->tm_mon;
606
607 calitem->styles = NULL;
608
609 calitem->min_cols = 1;
610 calitem->min_rows = 1;
611 calitem->max_cols = -1;
612 calitem->max_rows = -1;
613
614 calitem->rows = 0;
615 calitem->cols = 0;
616
617 calitem->show_week_numbers = FALSE;
618 calitem->keep_wdays_on_weeknum_click = FALSE;
619 calitem->week_start_day = G_DATE_MONDAY;
620 calitem->expand = TRUE;
621 calitem->max_days_selected = 1;
622 calitem->days_to_start_week_selection = -1;
623 calitem->move_selection_when_moving = TRUE;
624 calitem->preserve_day_when_moving = FALSE;
625 calitem->display_popup = TRUE;
626
627 calitem->x1 = 0.0;
628 calitem->y1 = 0.0;
629 calitem->x2 = 0.0;
630 calitem->y2 = 0.0;
631
632 calitem->selecting = FALSE;
633 calitem->selecting_axis = NULL;
634
635 calitem->selection_set = FALSE;
636
637 calitem->selection_changed = FALSE;
638 calitem->date_range_changed = FALSE;
639
640 calitem->style_callback = NULL;
641 calitem->style_callback_data = NULL;
642 calitem->style_callback_destroy = NULL;
643
644 calitem->time_callback = NULL;
645 calitem->time_callback_data = NULL;
646 calitem->time_callback_destroy = NULL;
647
648 calitem->signal_emission_idle_id = 0;
649 }
650
651 static void
e_calendar_item_constructed(GObject * object)652 e_calendar_item_constructed (GObject *object)
653 {
654 ECalendarItem *calitem = E_CALENDAR_ITEM (object);
655
656 G_OBJECT_CLASS (e_calendar_item_parent_class)->constructed (object);
657
658 e_extensible_load_extensions (E_EXTENSIBLE (calitem));
659 }
660
661 static void
e_calendar_item_dispose(GObject * object)662 e_calendar_item_dispose (GObject *object)
663 {
664 ECalendarItem *calitem;
665
666 calitem = E_CALENDAR_ITEM (object);
667
668 e_calendar_item_set_style_callback (calitem, NULL, NULL, NULL);
669 e_calendar_item_set_get_time_callback (calitem, NULL, NULL, NULL);
670
671 g_clear_pointer (&calitem->styles, g_free);
672
673 if (calitem->signal_emission_idle_id > 0) {
674 g_source_remove (calitem->signal_emission_idle_id);
675 calitem->signal_emission_idle_id = -1;
676 }
677
678 g_clear_pointer (&calitem->font_desc, pango_font_description_free);
679 g_clear_pointer (&calitem->week_number_font_desc, pango_font_description_free);
680
681 g_free (calitem->selecting_axis);
682
683 G_OBJECT_CLASS (e_calendar_item_parent_class)->dispose (object);
684 }
685
686 static void
e_calendar_item_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)687 e_calendar_item_get_property (GObject *object,
688 guint property_id,
689 GValue *value,
690 GParamSpec *pspec)
691 {
692 ECalendarItem *calitem;
693 gint min_column_width;
694
695 calitem = E_CALENDAR_ITEM (object);
696
697 switch (property_id) {
698 case PROP_YEAR:
699 g_value_set_int (value, calitem->year);
700 return;
701 case PROP_MONTH:
702 g_value_set_int (value, calitem->month);
703 return;
704 case PROP_X1:
705 g_value_set_double (value, calitem->x1);
706 return;
707 case PROP_Y1:
708 g_value_set_double (value, calitem->y1);
709 return;
710 case PROP_X2:
711 g_value_set_double (value, calitem->x2);
712 return;
713 case PROP_Y2:
714 g_value_set_double (value, calitem->y2);
715 return;
716 case PROP_FONT_DESC:
717 g_value_set_boxed (value, calitem->font_desc);
718 return;
719 case PROP_WEEK_NUMBER_FONT_DESC:
720 g_value_set_boxed (value, calitem->week_number_font_desc);
721 return;
722 case PROP_ROW_HEIGHT:
723 e_calendar_item_recalc_sizes (calitem);
724 g_value_set_int (value, calitem->min_month_height);
725 return;
726 case PROP_COLUMN_WIDTH:
727 e_calendar_item_recalc_sizes (calitem);
728
729 min_column_width = 0;
730 g_signal_emit (calitem, e_calendar_item_signals[CALC_MIN_COLUMN_WIDTH], 0, &min_column_width);
731
732 if (min_column_width < calitem->min_month_width)
733 min_column_width = calitem->min_month_width;
734
735 g_value_set_int (value, min_column_width);
736 return;
737 case PROP_MINIMUM_ROWS:
738 g_value_set_int (value, calitem->min_rows);
739 return;
740 case PROP_MINIMUM_COLUMNS:
741 g_value_set_int (value, calitem->min_cols);
742 return;
743 case PROP_MAXIMUM_ROWS:
744 g_value_set_int (value, calitem->max_rows);
745 return;
746 case PROP_MAXIMUM_COLUMNS:
747 g_value_set_int (value, calitem->max_cols);
748 return;
749 case PROP_WEEK_START_DAY:
750 g_value_set_enum (value, calitem->week_start_day);
751 return;
752 case PROP_SHOW_WEEK_NUMBERS:
753 g_value_set_boolean (value, calitem->show_week_numbers);
754 return;
755 case PROP_KEEP_WDAYS_ON_WEEKNUM_CLICK:
756 g_value_set_boolean (value, calitem->keep_wdays_on_weeknum_click);
757 return;
758 case PROP_MAXIMUM_DAYS_SELECTED:
759 g_value_set_int (value, e_calendar_item_get_max_days_sel (calitem));
760 return;
761 case PROP_DAYS_TO_START_WEEK_SELECTION:
762 g_value_set_int (value, e_calendar_item_get_days_start_week_sel (calitem));
763 return;
764 case PROP_MOVE_SELECTION_WHEN_MOVING:
765 g_value_set_boolean (value, calitem->move_selection_when_moving);
766 return;
767 case PROP_PRESERVE_DAY_WHEN_MOVING:
768 g_value_set_boolean (value, calitem->preserve_day_when_moving);
769 return;
770 case PROP_DISPLAY_POPUP:
771 g_value_set_boolean (value, e_calendar_item_get_display_popup (calitem));
772 return;
773 }
774
775 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
776 }
777
778 static void
e_calendar_item_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)779 e_calendar_item_set_property (GObject *object,
780 guint property_id,
781 const GValue *value,
782 GParamSpec *pspec)
783 {
784 GnomeCanvasItem *item;
785 ECalendarItem *calitem;
786 PangoFontDescription *font_desc;
787 gdouble dvalue;
788 gint ivalue;
789 gboolean bvalue;
790
791 item = GNOME_CANVAS_ITEM (object);
792 calitem = E_CALENDAR_ITEM (object);
793
794 switch (property_id) {
795 case PROP_YEAR:
796 ivalue = g_value_get_int (value);
797 e_calendar_item_set_first_month (
798 calitem, ivalue, calitem->month);
799 return;
800 case PROP_MONTH:
801 ivalue = g_value_get_int (value);
802 e_calendar_item_set_first_month (
803 calitem, calitem->year, ivalue);
804 return;
805 case PROP_X1:
806 dvalue = g_value_get_double (value);
807 if (calitem->x1 != dvalue) {
808 calitem->x1 = dvalue;
809 if (item->canvas)
810 gnome_canvas_item_request_update (item);
811 }
812 return;
813 case PROP_Y1:
814 dvalue = g_value_get_double (value);
815 if (calitem->y1 != dvalue) {
816 calitem->y1 = dvalue;
817 if (item->canvas)
818 gnome_canvas_item_request_update (item);
819 }
820 return;
821 case PROP_X2:
822 dvalue = g_value_get_double (value);
823 if (calitem->x2 != dvalue) {
824 calitem->x2 = dvalue;
825 if (item->canvas)
826 gnome_canvas_item_request_update (item);
827 }
828 return;
829 case PROP_Y2:
830 dvalue = g_value_get_double (value);
831 if (calitem->y2 != dvalue) {
832 calitem->y2 = dvalue;
833 if (item->canvas)
834 gnome_canvas_item_request_update (item);
835 }
836 return;
837 case PROP_FONT_DESC:
838 font_desc = g_value_get_boxed (value);
839 if (calitem->font_desc)
840 pango_font_description_free (calitem->font_desc);
841 calitem->font_desc = pango_font_description_copy (font_desc);
842 if (item->canvas)
843 gnome_canvas_item_request_update (item);
844 return;
845 case PROP_WEEK_NUMBER_FONT_DESC:
846 font_desc = g_value_get_boxed (value);
847 if (calitem->week_number_font_desc)
848 pango_font_description_free (calitem->week_number_font_desc);
849 calitem->week_number_font_desc = pango_font_description_copy (font_desc);
850 if (item->canvas)
851 gnome_canvas_item_request_update (item);
852 return;
853 case PROP_MINIMUM_ROWS:
854 ivalue = g_value_get_int (value);
855 ivalue = MAX (1, ivalue);
856 if (calitem->min_rows != ivalue) {
857 calitem->min_rows = ivalue;
858 if (item->canvas)
859 gnome_canvas_item_request_update (item);
860 }
861 return;
862 case PROP_MINIMUM_COLUMNS:
863 ivalue = g_value_get_int (value);
864 ivalue = MAX (1, ivalue);
865 if (calitem->min_cols != ivalue) {
866 calitem->min_cols = ivalue;
867 if (item->canvas)
868 gnome_canvas_item_request_update (item);
869 }
870 return;
871 case PROP_MAXIMUM_ROWS:
872 ivalue = g_value_get_int (value);
873 if (calitem->max_rows != ivalue) {
874 calitem->max_rows = ivalue;
875 if (item->canvas)
876 gnome_canvas_item_request_update (item);
877 }
878 return;
879 case PROP_MAXIMUM_COLUMNS:
880 ivalue = g_value_get_int (value);
881 if (calitem->max_cols != ivalue) {
882 calitem->max_cols = ivalue;
883 if (item->canvas)
884 gnome_canvas_item_request_update (item);
885 }
886 return;
887 case PROP_WEEK_START_DAY:
888 ivalue = g_value_get_enum (value);
889 if (calitem->week_start_day != ivalue) {
890 calitem->week_start_day = ivalue;
891 if (item->canvas)
892 gnome_canvas_item_request_update (item);
893 }
894 return;
895 case PROP_SHOW_WEEK_NUMBERS:
896 bvalue = g_value_get_boolean (value);
897 if (calitem->show_week_numbers != bvalue) {
898 calitem->show_week_numbers = bvalue;
899 if (item->canvas)
900 gnome_canvas_item_request_update (item);
901 }
902 return;
903 case PROP_KEEP_WDAYS_ON_WEEKNUM_CLICK:
904 calitem->keep_wdays_on_weeknum_click = g_value_get_boolean (value);
905 return;
906 case PROP_MAXIMUM_DAYS_SELECTED:
907 ivalue = g_value_get_int (value);
908 e_calendar_item_set_max_days_sel (calitem, ivalue);
909 return;
910 case PROP_DAYS_TO_START_WEEK_SELECTION:
911 ivalue = g_value_get_int (value);
912 e_calendar_item_set_days_start_week_sel (calitem, ivalue);
913 return;
914 case PROP_MOVE_SELECTION_WHEN_MOVING:
915 bvalue = g_value_get_boolean (value);
916 calitem->move_selection_when_moving = bvalue;
917 return;
918 case PROP_PRESERVE_DAY_WHEN_MOVING:
919 bvalue = g_value_get_boolean (value);
920 calitem->preserve_day_when_moving = bvalue;
921 return;
922 case PROP_DISPLAY_POPUP:
923 bvalue = g_value_get_boolean (value);
924 e_calendar_item_set_display_popup (calitem, bvalue);
925 return;
926 }
927
928 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
929 }
930
931 static void
e_calendar_item_realize(GnomeCanvasItem * item)932 e_calendar_item_realize (GnomeCanvasItem *item)
933 {
934 ECalendarItem *calitem;
935
936 if (GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class)->realize)
937 (* GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class)->realize) (item);
938
939 calitem = E_CALENDAR_ITEM (item);
940
941 e_calendar_item_style_updated (GTK_WIDGET (item->canvas), calitem);
942 }
943
944 static void
e_calendar_item_unmap(GnomeCanvasItem * item)945 e_calendar_item_unmap (GnomeCanvasItem *item)
946 {
947 ECalendarItem *calitem;
948
949 calitem = E_CALENDAR_ITEM (item);
950
951 if (calitem->selecting) {
952 gnome_canvas_item_ungrab (item, GDK_CURRENT_TIME);
953 calitem->selecting = FALSE;
954 }
955
956 if (GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class)->unmap)
957 (* GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class)->unmap) (item);
958 }
959
960 static void
e_calendar_item_update(GnomeCanvasItem * item,const cairo_matrix_t * i2c,gint flags)961 e_calendar_item_update (GnomeCanvasItem *item,
962 const cairo_matrix_t *i2c,
963 gint flags)
964 {
965 GnomeCanvasItemClass *item_class;
966 ECalendarItem *calitem;
967 gint char_height, width, height, space, space_per_cal, space_per_cell;
968 gint rows, cols, xthickness, ythickness, old_month_width, min_column_width;
969 PangoContext *pango_context;
970 PangoFontMetrics *font_metrics;
971 GtkStyleContext *style_context;
972 GtkBorder padding;
973
974 item_class = GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class);
975 if (item_class->update != NULL)
976 item_class->update (item, i2c, flags);
977
978 calitem = E_CALENDAR_ITEM (item);
979 style_context = gtk_widget_get_style_context (GTK_WIDGET (item->canvas));
980 gtk_style_context_get_padding (style_context, gtk_style_context_get_state (style_context), &padding);
981 xthickness = padding.left;
982 ythickness = padding.top;
983
984 item->x1 = calitem->x1;
985 item->y1 = calitem->y1;
986 item->x2 = calitem->x2 >= calitem->x1 ? calitem->x2 : calitem->x1;
987 item->y2 = calitem->y2 >= calitem->y1 ? calitem->y2 : calitem->y1;
988
989 /* Set up Pango prerequisites */
990 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (item->canvas));
991 font_metrics = pango_context_get_metrics (
992 pango_context, NULL,
993 pango_context_get_language (pango_context));
994
995 /*
996 * Calculate the new layout of the calendar.
997 */
998
999 /* Make sure the minimum row width & cell height and the widths of
1000 * all the digits and characters are up to date. */
1001 e_calendar_item_recalc_sizes (calitem);
1002
1003 /* Calculate how many rows & cols we can fit in. */
1004 width = item->x2 - item->x1;
1005 height = item->y2 - item->y1;
1006
1007 width -= xthickness * 2;
1008 height -= ythickness * 2;
1009
1010 if (calitem->min_month_height == 0)
1011 rows = 1;
1012 else
1013 rows = height / calitem->min_month_height;
1014 rows = MAX (rows, calitem->min_rows);
1015 if (calitem->max_rows > 0)
1016 rows = MIN (rows, calitem->max_rows);
1017
1018 min_column_width = 0;
1019 g_signal_emit (calitem, e_calendar_item_signals[CALC_MIN_COLUMN_WIDTH], 0, &min_column_width);
1020
1021 if (min_column_width < calitem->min_month_width)
1022 min_column_width = calitem->min_month_width;
1023
1024 if (min_column_width == 0)
1025 cols = 1;
1026 else
1027 cols = width / min_column_width;
1028 cols = MAX (cols, calitem->min_cols);
1029 if (calitem->max_cols > 0)
1030 cols = MIN (cols, calitem->max_cols);
1031
1032 if (rows != calitem->rows || cols != calitem->cols)
1033 e_calendar_item_date_range_changed (calitem);
1034
1035 calitem->rows = rows;
1036 calitem->cols = cols;
1037
1038 /* Split up the empty space according to the configuration.
1039 * If the calendar is set to expand, we divide the space between the
1040 * cells and the spaces around the calendar, otherwise we place the
1041 * calendars in the center of the available area. */
1042
1043 char_height =
1044 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1045 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
1046
1047 old_month_width = calitem->month_width;
1048 calitem->month_width = calitem->min_month_width;
1049 calitem->month_height = calitem->min_month_height;
1050 calitem->cell_width = MAX (calitem->max_day_width, (calitem->max_digit_width * 2))
1051 + E_CALENDAR_ITEM_MIN_CELL_XPAD;
1052 calitem->cell_height = char_height
1053 + E_CALENDAR_ITEM_MIN_CELL_YPAD;
1054 calitem->month_tpad = 0;
1055 calitem->month_bpad = 0;
1056 calitem->month_lpad = 0;
1057 calitem->month_rpad = 0;
1058
1059 space = height - calitem->rows * calitem->month_height;
1060 if (space > 0) {
1061 space_per_cal = space / calitem->rows;
1062 calitem->month_height += space_per_cal;
1063
1064 if (calitem->expand) {
1065 space_per_cell = space_per_cal / E_CALENDAR_ROWS_PER_MONTH;
1066 calitem->cell_height += space_per_cell;
1067 space_per_cal -= space_per_cell * E_CALENDAR_ROWS_PER_MONTH;
1068 }
1069
1070 calitem->month_tpad = space_per_cal / 2;
1071 calitem->month_bpad = space_per_cal - calitem->month_tpad;
1072 }
1073
1074 space = width - calitem->cols * calitem->month_width;
1075 if (space > 0) {
1076 space_per_cal = space / calitem->cols;
1077 calitem->month_width += space_per_cal;
1078 space -= space_per_cal * calitem->cols;
1079
1080 if (calitem->expand) {
1081 space_per_cell = space_per_cal / E_CALENDAR_COLS_PER_MONTH;
1082 calitem->cell_width += space_per_cell;
1083 space_per_cal -= space_per_cell * E_CALENDAR_COLS_PER_MONTH;
1084 }
1085
1086 calitem->month_lpad = space_per_cal / 2;
1087 calitem->month_rpad = space_per_cal - calitem->month_lpad;
1088 }
1089
1090 space = MAX (0, space);
1091 calitem->x_offset = space / 2;
1092
1093 gnome_canvas_request_redraw (
1094 item->canvas, item->x1, item->y1,
1095 item->x2, item->y2);
1096
1097 pango_font_metrics_unref (font_metrics);
1098
1099 if (old_month_width != calitem->month_width) {
1100 g_signal_emit (calitem, e_calendar_item_signals[MONTH_WIDTH_CHANGED], 0, NULL);
1101 }
1102 }
1103
1104 /*
1105 * DRAWING ROUTINES - functions to paint the canvas item.
1106 */
1107 static void
e_calendar_item_draw(GnomeCanvasItem * canvas_item,cairo_t * cr,gint x,gint y,gint width,gint height)1108 e_calendar_item_draw (GnomeCanvasItem *canvas_item,
1109 cairo_t *cr,
1110 gint x,
1111 gint y,
1112 gint width,
1113 gint height)
1114 {
1115 ECalendarItem *calitem;
1116 GtkWidget *widget;
1117 GtkStyleContext *style_context;
1118 gint char_height, row, col, row_y, bar_height;
1119 PangoContext *pango_context;
1120 PangoFontMetrics *font_metrics;
1121 GdkRGBA bg_color;
1122 GtkBorder border;
1123
1124 #if 0
1125 g_print (
1126 "In e_calendar_item_draw %i,%i %ix%i\n",
1127 x, y, width, height);
1128 #endif
1129 calitem = E_CALENDAR_ITEM (canvas_item);
1130
1131 widget = GTK_WIDGET (canvas_item->canvas);
1132 style_context = gtk_widget_get_style_context (widget);
1133
1134 /* Set up Pango prerequisites */
1135 pango_context = gtk_widget_get_pango_context (
1136 GTK_WIDGET (canvas_item->canvas));
1137 /* It's OK when the calitem->font_desc is NUL, then the currently set font is used */
1138 font_metrics = pango_context_get_metrics (
1139 pango_context, calitem->font_desc,
1140 pango_context_get_language (pango_context));
1141
1142 char_height =
1143 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1144 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
1145
1146 e_utils_get_theme_color (widget, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &bg_color);
1147
1148 gtk_style_context_get_border (style_context, gtk_style_context_get_state (style_context), &border);
1149
1150 /* Clear the entire background. */
1151 cairo_save (cr);
1152 gdk_cairo_set_source_rgba (cr, &bg_color);
1153 cairo_rectangle (
1154 cr, calitem->x1 - x, calitem->y1 - y,
1155 calitem->x2 - calitem->x1 + 1,
1156 calitem->y2 - calitem->y1 + 1);
1157 cairo_fill (cr);
1158 cairo_restore (cr);
1159
1160 row_y = canvas_item->y1 + border.top;
1161 bar_height =
1162 border.top + border.bottom +
1163 E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + char_height +
1164 E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME;
1165
1166 for (row = 0; row < calitem->rows; row++) {
1167 /* Draw the background for the title bars and the shadow around
1168 * it, and the vertical lines between columns. */
1169
1170 cairo_save (cr);
1171 gdk_cairo_set_source_rgba (cr, &bg_color);
1172 cairo_rectangle (
1173 cr, calitem->x1 + border.left - x,
1174 row_y - y,
1175 calitem->x2 - calitem->x1 + 1 -
1176 (border.left + border.right),
1177 bar_height);
1178 cairo_fill (cr);
1179 cairo_restore (cr);
1180
1181 gtk_style_context_save (style_context);
1182 gtk_style_context_add_class (
1183 style_context, GTK_STYLE_CLASS_HEADER);
1184 cairo_save (cr);
1185 gtk_render_frame (
1186 style_context, cr,
1187 (gdouble) calitem->x1 + border.left - x,
1188 (gdouble) row_y - y,
1189 (gdouble) calitem->x2 - calitem->x1 + 1 -
1190 (border.left + border.right),
1191 (gdouble) bar_height);
1192 cairo_restore (cr);
1193 gtk_style_context_restore (style_context);
1194
1195 for (col = 0; col < calitem->cols; col++) {
1196 e_calendar_item_draw_month (
1197 calitem, cr, x, y,
1198 width, height, row, col);
1199 }
1200
1201 row_y += calitem->month_height;
1202 }
1203
1204 /* Draw the shadow around the entire item. */
1205 gtk_style_context_save (style_context);
1206 gtk_style_context_add_class (
1207 style_context, GTK_STYLE_CLASS_ENTRY);
1208 cairo_save (cr);
1209 gtk_render_frame (
1210 style_context, cr,
1211 (gdouble) calitem->x1 - x,
1212 (gdouble) calitem->y1 - y,
1213 (gdouble) calitem->x2 - calitem->x1 + 1,
1214 (gdouble) calitem->y2 - calitem->y1 + 1);
1215 cairo_restore (cr);
1216 gtk_style_context_restore (style_context);
1217
1218 pango_font_metrics_unref (font_metrics);
1219 }
1220
1221 static void
layout_set_day_text(ECalendarItem * calitem,PangoLayout * layout,GDateWeekday weekday)1222 layout_set_day_text (ECalendarItem *calitem,
1223 PangoLayout *layout,
1224 GDateWeekday weekday)
1225 {
1226 const gchar *abbr_name;
1227
1228 abbr_name = e_get_weekday_name (weekday, TRUE);
1229 pango_layout_set_text (layout, abbr_name, -1);
1230 }
1231
1232 static void
e_calendar_item_draw_month(ECalendarItem * calitem,cairo_t * cr,gint x,gint y,gint width,gint height,gint row,gint col)1233 e_calendar_item_draw_month (ECalendarItem *calitem,
1234 cairo_t *cr,
1235 gint x,
1236 gint y,
1237 gint width,
1238 gint height,
1239 gint row,
1240 gint col)
1241 {
1242 GnomeCanvasItem *item;
1243 GtkWidget *widget;
1244 struct tm tmp_tm;
1245 GdkRectangle clip_rect;
1246 GDateWeekday start_weekday;
1247 gint char_height, xthickness, ythickness;
1248 gint year, month;
1249 gint month_x, month_y, month_w, month_h;
1250 gint min_x, max_x, text_x, text_y;
1251 gint day, cells_x, cells_y, min_cell_width, text_width, arrow_button_size;
1252 gint clip_width, clip_height;
1253 gchar buffer[64];
1254 GDateWeekday weekday;
1255 PangoContext *pango_context;
1256 PangoFontMetrics *font_metrics;
1257 PangoLayout *layout;
1258 GtkStyleContext *style_context;
1259 GtkBorder padding;
1260 PangoFontDescription *font_desc;
1261 GdkRGBA rgba;
1262
1263 #if 0
1264 g_print (
1265 "In e_calendar_item_draw_month: %i,%i %ix%i row:%i col:%i\n",
1266 x, y, width, height, row, col);
1267 #endif
1268 item = GNOME_CANVAS_ITEM (calitem);
1269 widget = GTK_WIDGET (item->canvas);
1270
1271 /* Set up Pango prerequisites */
1272 font_desc = calitem->font_desc;
1273 pango_context = gtk_widget_get_pango_context (widget);
1274 font_metrics = pango_context_get_metrics (
1275 pango_context, font_desc,
1276 pango_context_get_language (pango_context));
1277 if (!font_desc)
1278 font_desc = pango_context_get_font_description (pango_context);
1279 font_desc = pango_font_description_copy (font_desc);
1280
1281 char_height =
1282 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1283 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
1284 style_context = gtk_widget_get_style_context (widget);
1285 gtk_style_context_get_padding (style_context, gtk_style_context_get_state (style_context), &padding);
1286 xthickness = padding.left;
1287 ythickness = padding.top;
1288 arrow_button_size =
1289 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
1290 + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
1291 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
1292 + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
1293 + 2 * xthickness;
1294
1295 pango_font_metrics_unref (font_metrics);
1296
1297 /* Calculate the top-left position of the entire month display. */
1298 month_x = item->x1 + xthickness + calitem->x_offset
1299 + ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1300 ? (calitem->cols - 1 - col) : col) * calitem->month_width - x;
1301 month_w = item->x2 - item->x1 - xthickness * 2;
1302 month_w = MIN (month_w, calitem->month_width);
1303 month_y = item->y1 + ythickness + row * calitem->month_height - y;
1304 month_h = item->y2 - item->y1 - ythickness * 2;
1305 month_h = MIN (month_h, calitem->month_height);
1306
1307 /* Just return if the month is outside the given area. */
1308 if (month_x >= width || month_x + calitem->month_width <= 0
1309 || month_y >= height || month_y + calitem->month_height <= 0) {
1310 pango_font_description_free (font_desc);
1311 return;
1312 }
1313
1314 month = calitem->month + row * calitem->cols + col;
1315 year = calitem->year + month / 12;
1316 month %= 12;
1317
1318 /* Draw the month name & year, with clipping. Note that the top row
1319 * needs extra space around it for the buttons. */
1320
1321 layout = gtk_widget_create_pango_layout (widget, NULL);
1322
1323 if (row == 0 && col == 0)
1324 min_x = E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME_WITH_BUTTON;
1325 else
1326 min_x = E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME;
1327
1328 max_x = month_w;
1329 if (row == 0 && col == 0)
1330 max_x -= E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME_WITH_BUTTON;
1331 else
1332 max_x -= E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME;
1333
1334 text_y = month_y + padding.top
1335 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME;
1336 clip_rect.x = month_x + min_x;
1337 clip_rect.x = MAX (0, clip_rect.x);
1338 clip_rect.y = MAX (0, text_y);
1339
1340 memset (&tmp_tm, 0, sizeof (tmp_tm));
1341 tmp_tm.tm_year = year - 1900;
1342 tmp_tm.tm_mon = month;
1343 tmp_tm.tm_mday = 1;
1344 tmp_tm.tm_isdst = -1;
1345 mktime (&tmp_tm);
1346
1347 start_weekday = e_weekday_from_tm_wday (tmp_tm.tm_wday);
1348
1349 if (month_x + max_x - clip_rect.x > 0) {
1350 cairo_save (cr);
1351
1352 clip_rect.width = month_x + max_x - clip_rect.x;
1353 clip_rect.height = text_y + char_height - clip_rect.y;
1354 gdk_cairo_rectangle (cr, &clip_rect);
1355 cairo_clip (cr);
1356
1357 e_utils_get_theme_color (widget, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR, &rgba);
1358 gdk_cairo_set_source_rgba (cr, &rgba);
1359
1360 if (row == 0 && col == 0) {
1361 PangoLayout *layout_yr;
1362 gchar buffer_yr[64];
1363 gdouble max_width;
1364
1365 layout_yr = gtk_widget_create_pango_layout (widget, NULL);
1366
1367 /* This is a strftime() format. %B = Month name. */
1368 e_utf8_strftime (buffer, sizeof (buffer), C_("CalItem", "%B"), &tmp_tm);
1369 /* This is a strftime() format. %Y = Year. */
1370 e_utf8_strftime (buffer_yr, sizeof (buffer_yr), C_("CalItem", "%Y"), &tmp_tm);
1371
1372 pango_layout_set_font_description (layout, font_desc);
1373 pango_layout_set_text (layout, buffer, -1);
1374
1375 pango_layout_set_font_description (layout_yr, font_desc);
1376 pango_layout_set_text (layout_yr, buffer_yr, -1);
1377
1378 if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL) {
1379 max_width = calitem->max_month_name_width;
1380 pango_layout_get_pixel_size (layout, &text_width, NULL);
1381
1382 cairo_move_to (cr, month_x + min_x + arrow_button_size + (max_width - text_width) / 2, text_y);
1383 pango_cairo_show_layout (cr, layout);
1384
1385 max_width = calitem->max_digit_width * 5;
1386 pango_layout_get_pixel_size (layout_yr, &text_width, NULL);
1387
1388 cairo_move_to (cr, month_x + month_w - arrow_button_size - (max_width - text_width) / 2 - text_width - min_x, text_y);
1389 pango_cairo_show_layout (cr, layout_yr);
1390 } else {
1391 max_width = calitem->max_digit_width * 5;
1392 pango_layout_get_pixel_size (layout_yr, &text_width, NULL);
1393
1394 cairo_move_to (cr, month_x + min_x + arrow_button_size + (max_width - text_width) / 2, text_y);
1395 pango_cairo_show_layout (cr, layout_yr);
1396
1397 max_width = calitem->max_month_name_width;
1398 pango_layout_get_pixel_size (layout, &text_width, NULL);
1399
1400 cairo_move_to (cr, month_x + month_w - arrow_button_size - (max_width - text_width) / 2 - text_width - min_x, text_y);
1401 pango_cairo_show_layout (cr, layout);
1402 }
1403
1404 g_object_unref (layout_yr);
1405 } else {
1406 /* This is a strftime() format. %B = Month name, %Y = Year. */
1407 e_utf8_strftime (buffer, sizeof (buffer), C_("CalItem", "%B %Y"), &tmp_tm);
1408
1409 pango_layout_set_font_description (layout, font_desc);
1410 pango_layout_set_text (layout, buffer, -1);
1411
1412 /* Ideally we place the text centered in the month, but we
1413 * won't go to the left of the minimum x position. */
1414 pango_layout_get_pixel_size (layout, &text_width, NULL);
1415 text_x = (calitem->month_width - text_width) / 2;
1416 text_x = MAX (min_x, text_x);
1417
1418 cairo_move_to (cr, month_x + text_x, text_y);
1419 pango_cairo_show_layout (cr, layout);
1420 }
1421
1422 cairo_restore (cr);
1423 }
1424
1425 /* Set the clip rectangle for the main month display. */
1426 clip_rect.x = MAX (0, month_x);
1427 clip_rect.y = MAX (0, month_y);
1428 clip_width = month_x + month_w - clip_rect.x;
1429 clip_height = month_y + month_h - clip_rect.y;
1430
1431 if (clip_width <= 0 || clip_height <= 0) {
1432 g_object_unref (layout);
1433 pango_font_description_free (font_desc);
1434 return;
1435 }
1436
1437 clip_rect.width = clip_width;
1438 clip_rect.height = clip_height;
1439
1440 cairo_save (cr);
1441
1442 gdk_cairo_rectangle (cr, &clip_rect);
1443 cairo_clip (cr);
1444
1445 /* Draw the day initials across the top of the month. */
1446 min_cell_width = MAX (calitem->max_day_width, (calitem->max_digit_width * 2))
1447 + E_CALENDAR_ITEM_MIN_CELL_XPAD;
1448
1449 cells_x = month_x + E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS + calitem->month_lpad
1450 + E_CALENDAR_ITEM_XPAD_BEFORE_CELLS;
1451 if (calitem->show_week_numbers)
1452 cells_x += calitem->max_week_number_digit_width * 2
1453 + E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS + 1;
1454 text_x = cells_x + calitem->cell_width
1455 - (calitem->cell_width - min_cell_width) / 2;
1456 text_x -= E_CALENDAR_ITEM_MIN_CELL_XPAD / 2;
1457 text_y = month_y + ythickness * 2
1458 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
1459 + char_height + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
1460 + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS + calitem->month_tpad;
1461
1462 cells_y = text_y + char_height
1463 + E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS + 1
1464 + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS;
1465
1466 cairo_save (cr);
1467 e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &rgba);
1468 gdk_cairo_set_source_rgba (cr, &rgba);
1469 cairo_rectangle (
1470 cr, cells_x ,
1471 text_y - E_CALENDAR_ITEM_YPAD_ABOVE_CELLS - 1,
1472 calitem->cell_width * 7 , cells_y - text_y);
1473 cairo_fill (cr);
1474 cairo_restore (cr);
1475
1476 weekday = calitem->week_start_day;
1477 pango_layout_set_font_description (layout, font_desc);
1478 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1479 text_x += (7 - 1) * calitem->cell_width;
1480 e_utils_get_theme_color (widget, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR, &rgba);
1481 gdk_cairo_set_source_rgba (cr, &rgba);
1482 for (day = 0; day < 7; day++) {
1483 cairo_save (cr);
1484 layout_set_day_text (calitem, layout, weekday);
1485 cairo_move_to (
1486 cr,
1487 text_x - calitem->day_widths[weekday],
1488 text_y);
1489 pango_cairo_show_layout (cr, layout);
1490 text_x += (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1491 ? -calitem->cell_width : calitem->cell_width;
1492 cairo_restore (cr);
1493
1494 weekday = e_weekday_get_next (weekday);
1495 }
1496
1497 /* Draw the rectangle around the week number. */
1498 if (calitem->show_week_numbers) {
1499 cairo_save (cr);
1500 e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &rgba);
1501 gdk_cairo_set_source_rgba (cr, &rgba);
1502 cairo_rectangle (
1503 cr, cells_x, cells_y - (cells_y - text_y + 2) ,
1504 -20, E_CALENDAR_ROWS_PER_MONTH * calitem->cell_height + 18);
1505 cairo_fill (cr);
1506 cairo_restore (cr);
1507 }
1508
1509 e_calendar_item_draw_day_numbers (
1510 calitem, cr, width, height, row, col,
1511 year, month, start_weekday, cells_x, cells_y);
1512
1513 g_object_unref (layout);
1514 cairo_restore (cr);
1515 pango_font_description_free (font_desc);
1516 }
1517
1518 static const gchar *
get_digit_fomat(void)1519 get_digit_fomat (void)
1520 {
1521
1522 #ifdef HAVE_GNU_GET_LIBC_VERSION
1523 #include <gnu/libc-version.h>
1524
1525 const gchar *libc_version = gnu_get_libc_version ();
1526 gchar **split = g_strsplit (libc_version, ".", -1);
1527 gint major = 0;
1528 gint minor = 0;
1529 gint revision = 0;
1530
1531 major = atoi (split[0]);
1532 minor = atoi (split[1]);
1533
1534 if (g_strv_length (split) > 2)
1535 revision = atoi (split[2]);
1536 g_strfreev (split);
1537
1538 if (major > 2 || minor > 2 || (minor == 2 && revision > 2)) {
1539 return "%Id";
1540 }
1541 #endif
1542
1543 return "%d";
1544 }
1545
1546 static void
e_calendar_item_draw_day_numbers(ECalendarItem * calitem,cairo_t * cr,gint width,gint height,gint row,gint col,gint year,gint month,GDateWeekday start_weekday,gint cells_x,gint cells_y)1547 e_calendar_item_draw_day_numbers (ECalendarItem *calitem,
1548 cairo_t *cr,
1549 gint width,
1550 gint height,
1551 gint row,
1552 gint col,
1553 gint year,
1554 gint month,
1555 GDateWeekday start_weekday,
1556 gint cells_x,
1557 gint cells_y)
1558 {
1559 GnomeCanvasItem *item;
1560 GtkWidget *widget;
1561 PangoFontDescription *font_desc;
1562 GdkColor *bg_color, *fg_color, *box_color;
1563 GdkRGBA rgba;
1564 struct tm today_tm;
1565 time_t t;
1566 gint char_height, min_cell_width, min_cell_height;
1567 gint day_num, drow, dcol, day_x, day_y;
1568 gint text_x, text_y;
1569 gint num_chars, digit;
1570 gint week_num, mon, days_from_week_start;
1571 gint years[3], months[3], days_in_month[3];
1572 gboolean today, selected, has_focus, drop_target = FALSE;
1573 gboolean bold, italic, draw_day, finished = FALSE;
1574 gint today_year, today_month, today_mday, month_offset;
1575 gchar buffer[64];
1576 gint day_style = 0;
1577 PangoContext *pango_context;
1578 PangoFontMetrics *font_metrics;
1579 PangoLayout *layout;
1580 PangoAttrList *tnum;
1581 PangoAttribute *attr;
1582
1583 item = GNOME_CANVAS_ITEM (calitem);
1584 widget = GTK_WIDGET (item->canvas);
1585
1586 /* Set up Pango prerequisites */
1587 font_desc = calitem->font_desc;
1588
1589 pango_context = gtk_widget_get_pango_context (widget);
1590 font_metrics = pango_context_get_metrics (
1591 pango_context, font_desc,
1592 pango_context_get_language (pango_context));
1593 if (!font_desc)
1594 font_desc = pango_context_get_font_description (pango_context);
1595 font_desc = pango_font_description_copy (font_desc);
1596
1597 char_height =
1598 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1599 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
1600
1601 min_cell_width = MAX (calitem->max_day_width, (calitem->max_digit_width * 2))
1602 + E_CALENDAR_ITEM_MIN_CELL_XPAD;
1603 min_cell_height = char_height + E_CALENDAR_ITEM_MIN_CELL_YPAD;
1604
1605 layout = gtk_widget_create_pango_layout (GTK_WIDGET (widget), NULL);
1606
1607 /* Calculate the number of days in the previous, current, and next
1608 * months. */
1609 years[0] = years[1] = years[2] = year;
1610 months[0] = month - 1;
1611 months[1] = month;
1612 months[2] = month + 1;
1613 if (months[0] == -1) {
1614 months[0] = 11;
1615 years[0]--;
1616 }
1617 if (months[2] == 12) {
1618 months[2] = 0;
1619 years[2]++;
1620 }
1621
1622 days_in_month[0] = DAYS_IN_MONTH (years[0], months[0]);
1623 days_in_month[1] = DAYS_IN_MONTH (years[1], months[1]);
1624 days_in_month[2] = DAYS_IN_MONTH (years[2], months[2]);
1625
1626 /* Mon 0 is the previous month, which we may show the end of. Mon 1 is
1627 * the current month, and mon 2 is the next month. */
1628 mon = 0;
1629
1630 month_offset = row * calitem->cols + col - 1;
1631 day_num = days_in_month[0];
1632 days_from_week_start = e_weekday_get_days_between (
1633 calitem->week_start_day, start_weekday);
1634 /* For the top-left month we show the end of the previous month, and
1635 * if the new month starts on the first day of the week we show a
1636 * complete week from the previous month. */
1637 if (days_from_week_start == 0) {
1638 if (row == 0 && col == 0) {
1639 day_num -= 6;
1640 } else {
1641 mon++;
1642 month_offset++;
1643 day_num = 1;
1644 }
1645 } else {
1646 day_num -= days_from_week_start - 1;
1647 }
1648
1649 /* Get today's date, so we can highlight it. */
1650 if (calitem->time_callback) {
1651 today_tm = calitem->time_callback (
1652 calitem, calitem->time_callback_data);
1653 } else {
1654 t = time (NULL);
1655 today_tm = *localtime (&t);
1656 }
1657 today_year = today_tm.tm_year + 1900;
1658 today_month = today_tm.tm_mon;
1659 today_mday = today_tm.tm_mday;
1660
1661 /* We usually skip the last days of the previous month (mon = 0),
1662 * except for the top-left month displayed. */
1663 draw_day = (mon == 1 || (row == 0 && col == 0));
1664
1665 tnum = pango_attr_list_new ();
1666 attr = pango_attr_font_features_new ("tnum=1");
1667 pango_attr_list_insert_before (tnum, attr);
1668
1669 for (drow = 0; drow < 6; drow++) {
1670 /* Draw the week number. */
1671 if (calitem->show_week_numbers) {
1672 week_num = e_calendar_item_get_week_number (
1673 calitem, day_num, months[mon], years[mon]);
1674
1675 text_x = cells_x - E_CALENDAR_ITEM_XPAD_BEFORE_CELLS - 1
1676 - E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS;
1677 text_y = cells_y + drow * calitem->cell_height +
1678 + (calitem->cell_height - min_cell_height + 1) / 2;
1679
1680 num_chars = 0;
1681 if (week_num >= 10) {
1682 digit = week_num / 10;
1683 text_x -= calitem->week_number_digit_widths[digit];
1684 num_chars += sprintf (
1685 &buffer[num_chars],
1686 get_digit_fomat (), digit);
1687 }
1688
1689 digit = week_num % 10;
1690 text_x -= calitem->week_number_digit_widths[digit] + 6;
1691 num_chars += sprintf (
1692 &buffer[num_chars],
1693 get_digit_fomat (), digit);
1694
1695 cairo_save (cr);
1696 e_utils_get_theme_color (widget, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR, &rgba);
1697 gdk_cairo_set_source_rgba (cr, &rgba);
1698 pango_layout_set_font_description (layout, font_desc);
1699 pango_layout_set_text (layout, buffer, num_chars);
1700 cairo_move_to (cr, text_x, text_y);
1701 pango_cairo_update_layout (cr, layout);
1702 pango_cairo_show_layout (cr, layout);
1703 cairo_restore (cr);
1704 }
1705
1706 for (dcol = 0; dcol < 7; dcol++) {
1707 if (draw_day) {
1708 GdkColor local_bg_color, local_fg_color;
1709
1710 day_x = cells_x +
1711 ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1712 ? 7 - 1 - dcol : dcol) * calitem->cell_width;
1713
1714 day_y = cells_y + drow * calitem->cell_height;
1715
1716 today = years[mon] == today_year
1717 && months[mon] == today_month
1718 && day_num == today_mday;
1719
1720 selected = calitem->selection_set
1721 && (calitem->selection_start_month_offset < month_offset
1722 || (calitem->selection_start_month_offset == month_offset
1723 && calitem->selection_start_day <= day_num))
1724 && (calitem->selection_end_month_offset > month_offset
1725 || (calitem->selection_end_month_offset == month_offset
1726 && calitem->selection_end_day >= day_num));
1727
1728 if (calitem->styles)
1729 day_style = calitem->styles[(month_offset + 1) * 32 + day_num];
1730
1731 /* Get the colors & style to use for the day.*/
1732 if ((gtk_widget_has_focus (GTK_WIDGET (item->canvas))) &&
1733 item->canvas->focused_item == item)
1734 has_focus = TRUE;
1735 else
1736 has_focus = FALSE;
1737
1738 bold = FALSE;
1739 italic = FALSE;
1740
1741 if (calitem->style_callback)
1742 calitem->style_callback (
1743 calitem,
1744 years[mon],
1745 months[mon],
1746 day_num,
1747 day_style,
1748 today,
1749 mon != 1,
1750 selected,
1751 has_focus,
1752 drop_target,
1753 &bg_color,
1754 &fg_color,
1755 &box_color,
1756 &bold,
1757 &italic,
1758 calitem->style_callback_data);
1759 else
1760 e_calendar_item_get_day_style (
1761 calitem,
1762 years[mon],
1763 months[mon],
1764 day_num,
1765 day_style,
1766 today,
1767 mon != 1,
1768 selected,
1769 has_focus,
1770 drop_target,
1771 &bg_color,
1772 &fg_color,
1773 &box_color,
1774 &bold,
1775 &italic,
1776 &local_bg_color,
1777 &local_fg_color);
1778
1779 /* Draw the background, if set. */
1780 if (bg_color) {
1781 cairo_save (cr);
1782 gdk_cairo_set_source_color (cr, bg_color);
1783 cairo_rectangle (
1784 cr, day_x , day_y,
1785 calitem->cell_width,
1786 calitem->cell_height);
1787 cairo_fill (cr);
1788 cairo_restore (cr);
1789 }
1790
1791 /* Draw the box, if set. */
1792 if (box_color) {
1793 cairo_save (cr);
1794 gdk_cairo_set_source_color (cr, box_color);
1795 cairo_rectangle (
1796 cr, day_x , day_y,
1797 calitem->cell_width - 1,
1798 calitem->cell_height - 1);
1799 cairo_stroke (cr);
1800 cairo_restore (cr);
1801 }
1802
1803 /* Draw the 1- or 2-digit day number. */
1804 day_x += calitem->cell_width -
1805 (calitem->cell_width -
1806 min_cell_width) / 2;
1807 day_x -= E_CALENDAR_ITEM_MIN_CELL_XPAD / 2;
1808 day_y += (calitem->cell_height - min_cell_height + 1) / 2;
1809 day_y += E_CALENDAR_ITEM_MIN_CELL_YPAD / 2;
1810
1811 num_chars = 0;
1812 if (day_num >= 10) {
1813 digit = day_num / 10;
1814 day_x -= calitem->digit_widths[digit];
1815 num_chars += sprintf (
1816 &buffer[num_chars],
1817 get_digit_fomat (), digit);
1818 }
1819
1820 digit = day_num % 10;
1821 day_x -= calitem->digit_widths[digit];
1822 num_chars += sprintf (
1823 &buffer[num_chars],
1824 get_digit_fomat (), digit);
1825
1826 cairo_save (cr);
1827 if (fg_color) {
1828 gdk_cairo_set_source_color (
1829 cr, fg_color);
1830 } else {
1831 e_utils_get_theme_color (widget, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR, &rgba);
1832 gdk_cairo_set_source_rgba (cr, &rgba);
1833 }
1834
1835 if (bold) {
1836 pango_font_description_set_weight (
1837 font_desc, PANGO_WEIGHT_BOLD);
1838 } else {
1839 pango_font_description_set_weight (
1840 font_desc, PANGO_WEIGHT_NORMAL);
1841 }
1842
1843 if (italic) {
1844 pango_font_description_set_style (
1845 font_desc, PANGO_STYLE_ITALIC);
1846 } else {
1847 pango_font_description_set_style (
1848 font_desc, PANGO_STYLE_NORMAL);
1849 }
1850
1851 pango_layout_set_font_description (layout, font_desc);
1852 pango_layout_set_attributes (layout, tnum);
1853 pango_layout_set_text (layout, buffer, num_chars);
1854 cairo_move_to (cr, day_x, day_y);
1855 pango_cairo_update_layout (cr, layout);
1856 pango_cairo_show_layout (cr, layout);
1857 cairo_restore (cr);
1858 }
1859
1860 /* See if we've reached the end of a month. */
1861 if (day_num == days_in_month[mon]) {
1862 month_offset++;
1863 mon++;
1864 /* We only draw the start of the next month
1865 * for the bottom-right month displayed. */
1866 if (mon == 2 && (row != calitem->rows - 1
1867 || col != calitem->cols - 1)) {
1868 /* Set a flag so we exit the loop. */
1869 finished = TRUE;
1870 break;
1871 }
1872 day_num = 1;
1873 draw_day = TRUE;
1874 } else {
1875 day_num++;
1876 }
1877 }
1878
1879 /* Exit the loop if the flag is set. */
1880 if (finished)
1881 break;
1882 }
1883
1884 /* Reset pango weight and style */
1885 pango_font_description_set_weight (font_desc, PANGO_WEIGHT_NORMAL);
1886 pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL);
1887
1888 g_object_unref (layout);
1889
1890 pango_attr_list_unref (tnum);
1891 pango_font_metrics_unref (font_metrics);
1892 pango_font_description_free (font_desc);
1893 }
1894
1895 gint
e_calendar_item_get_week_number(ECalendarItem * calitem,gint day,gint month,gint year)1896 e_calendar_item_get_week_number (ECalendarItem *calitem,
1897 gint day,
1898 gint month,
1899 gint year)
1900 {
1901 GDate date;
1902 GDateWeekday weekday;
1903 guint yearday;
1904 gint week_num;
1905
1906 g_date_clear (&date, 1);
1907 g_date_set_dmy (&date, day, month + 1, year);
1908
1909 weekday = g_date_get_weekday (&date);
1910
1911 if (g_date_valid_weekday (weekday)) {
1912 guint days_between;
1913
1914 /* We want always point to nearest Monday as the first day
1915 * of the week regardless of the calendar's week_start_day. */
1916 if (weekday >= G_DATE_THURSDAY) {
1917 days_between = e_weekday_get_days_between (
1918 weekday, G_DATE_MONDAY);
1919 g_date_add_days (&date, days_between);
1920 } else {
1921 days_between = e_weekday_get_days_between (
1922 G_DATE_MONDAY, weekday);
1923 g_date_subtract_days (&date, days_between);
1924 }
1925 }
1926
1927 /* Calculate the day of the year, from 0 to 365. */
1928 yearday = g_date_get_day_of_year (&date) - 1;
1929
1930 /* If the week starts on or after 29th December, it is week 1 of the
1931 * next year, since there are 4 days in the next year. */
1932 if (g_date_get_month (&date) == 12 && g_date_get_day (&date) >= 29)
1933 return 1;
1934
1935 /* Calculate the week number, from 0. */
1936 week_num = yearday / 7;
1937
1938 /* If the first week starts on or after Jan 5th, then we need to add
1939 * 1 since the previous week will really be the first week. */
1940 if (yearday % 7 >= 4)
1941 week_num++;
1942
1943 /* Add 1 so week numbers are from 1 to 53. */
1944 return week_num + 1;
1945 }
1946
1947 /* This is supposed to return the nearest item to the point and the distance.
1948 * Since we are the only item we just return ourself and 0 for the distance.
1949 * This is needed so that we get button/motion events. */
1950 static GnomeCanvasItem *
e_calendar_item_point(GnomeCanvasItem * item,gdouble x,gdouble y,gint cx,gint cy)1951 e_calendar_item_point (GnomeCanvasItem *item,
1952 gdouble x,
1953 gdouble y,
1954 gint cx,
1955 gint cy)
1956 {
1957 return item;
1958 }
1959
1960 static void
e_calendar_item_stop_selecting(ECalendarItem * calitem,guint32 time)1961 e_calendar_item_stop_selecting (ECalendarItem *calitem,
1962 guint32 time)
1963 {
1964 if (!calitem->selecting)
1965 return;
1966
1967 gnome_canvas_item_ungrab (GNOME_CANVAS_ITEM (calitem), time);
1968
1969 calitem->selecting = FALSE;
1970
1971 /* If the user selects the grayed dates before the first month or
1972 * after the last month, we move backwards or forwards one month.
1973 * The set_month () call should take care of updating the selection. */
1974 if (calitem->selection_end_month_offset == -1)
1975 e_calendar_item_set_first_month_with_emit (
1976 calitem, calitem->year,
1977 calitem->month - 1, FALSE);
1978 else if (calitem->selection_start_month_offset == calitem->rows * calitem->cols)
1979 e_calendar_item_set_first_month_with_emit (
1980 calitem, calitem->year,
1981 calitem->month + 1, FALSE);
1982
1983 calitem->selection_changed = TRUE;
1984 g_clear_pointer (&calitem->selecting_axis, g_free);
1985
1986 e_calendar_item_queue_signal_emission (calitem);
1987 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
1988 }
1989
1990 static void
e_calendar_item_selection_add_days(ECalendarItem * calitem,gint n_days,gboolean multi_selection)1991 e_calendar_item_selection_add_days (ECalendarItem *calitem,
1992 gint n_days,
1993 gboolean multi_selection)
1994 {
1995 GDate gdate_start, gdate_end;
1996
1997 g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
1998
1999 if (!e_calendar_item_get_selection (calitem, &gdate_start, &gdate_end)) {
2000 /* We set the date to the first day of the month */
2001 g_date_set_dmy (&gdate_start, 1, calitem->month + 1, calitem->year);
2002 gdate_end = gdate_start;
2003 }
2004
2005 if (multi_selection && calitem->max_days_selected > 1) {
2006 gint days_between;
2007
2008 days_between = g_date_days_between (&gdate_start, &gdate_end);
2009 if (!calitem->selecting_axis) {
2010 calitem->selecting_axis = g_new (GDate, 1);
2011 *(calitem->selecting_axis) = gdate_start;
2012 }
2013 if ((days_between != 0 &&
2014 g_date_compare (calitem->selecting_axis, &gdate_end) == 0) ||
2015 (days_between == 0 && n_days < 0)) {
2016 if (days_between - n_days > calitem->max_days_selected - 1)
2017 n_days = days_between + 1 - calitem->max_days_selected;
2018 g_date_add_days (&gdate_start, n_days);
2019 }
2020 else {
2021 if (days_between + n_days > calitem->max_days_selected - 1)
2022 n_days = calitem->max_days_selected - 1 - days_between;
2023 g_date_add_days (&gdate_end, n_days);
2024 }
2025
2026 if (g_date_compare (&gdate_end, &gdate_start) < 0) {
2027 GDate tmp_date;
2028 tmp_date = gdate_start;
2029 gdate_start = gdate_end;
2030 gdate_end = tmp_date;
2031 }
2032 }
2033 else {
2034 /* clear "selecting_axis", it is only for mulit-selecting */
2035 g_clear_pointer (&calitem->selecting_axis, g_free);
2036 g_date_add_days (&gdate_start, n_days);
2037 gdate_end = gdate_start;
2038 }
2039
2040 calitem->selecting = TRUE;
2041
2042 e_calendar_item_set_selection_if_emission (
2043 calitem, &gdate_start, &gdate_end, FALSE);
2044
2045 g_signal_emit_by_name (calitem, "selection_preview_changed");
2046 }
2047
2048 static gint
e_calendar_item_key_press_event(ECalendarItem * calitem,GdkEvent * event)2049 e_calendar_item_key_press_event (ECalendarItem *calitem,
2050 GdkEvent *event)
2051 {
2052 guint keyval = event->key.keyval;
2053 gboolean is_rtl;
2054 gboolean multi_selection;
2055
2056 if (event->key.state & GDK_CONTROL_MASK ||
2057 event->key.state & GDK_MOD1_MASK)
2058 return FALSE;
2059
2060 is_rtl = gtk_widget_get_direction (GTK_WIDGET (GNOME_CANVAS_ITEM (calitem)->canvas)) == GTK_TEXT_DIR_RTL;
2061 multi_selection = event->key.state & GDK_SHIFT_MASK;
2062
2063 switch (keyval) {
2064 case GDK_KEY_Up:
2065 e_calendar_item_selection_add_days (
2066 calitem, -7,
2067 multi_selection);
2068 break;
2069 case GDK_KEY_Down:
2070 e_calendar_item_selection_add_days (
2071 calitem, 7,
2072 multi_selection);
2073 break;
2074 case GDK_KEY_Left:
2075 e_calendar_item_selection_add_days (
2076 calitem, is_rtl ? 1 : -1,
2077 multi_selection);
2078 break;
2079 case GDK_KEY_Right:
2080 e_calendar_item_selection_add_days (
2081 calitem, is_rtl ? -1 : 1,
2082 multi_selection);
2083 break;
2084 case GDK_KEY_space:
2085 case GDK_KEY_Return:
2086 e_calendar_item_stop_selecting (calitem, event->key.time);
2087 break;
2088 default:
2089 return FALSE;
2090 }
2091 return TRUE;
2092 }
2093
2094 static gint
e_calendar_item_event(GnomeCanvasItem * item,GdkEvent * event)2095 e_calendar_item_event (GnomeCanvasItem *item,
2096 GdkEvent *event)
2097 {
2098 ECalendarItem *calitem;
2099
2100 calitem = E_CALENDAR_ITEM (item);
2101
2102 switch (event->type) {
2103 case GDK_BUTTON_PRESS:
2104 return e_calendar_item_button_press (calitem, event);
2105 case GDK_BUTTON_RELEASE:
2106 return e_calendar_item_button_release (calitem, event);
2107 case GDK_MOTION_NOTIFY:
2108 return e_calendar_item_motion (calitem, event);
2109 case GDK_FOCUS_CHANGE:
2110 gnome_canvas_item_request_update (item);
2111 return FALSE;
2112 case GDK_KEY_PRESS:
2113 return e_calendar_item_key_press_event (calitem, event);
2114 default:
2115 break;
2116 }
2117
2118 return FALSE;
2119 }
2120
2121 static void
e_calendar_item_bounds(GnomeCanvasItem * item,gdouble * x1,gdouble * y1,gdouble * x2,gdouble * y2)2122 e_calendar_item_bounds (GnomeCanvasItem *item,
2123 gdouble *x1,
2124 gdouble *y1,
2125 gdouble *x2,
2126 gdouble *y2)
2127 {
2128 ECalendarItem *calitem;
2129
2130 g_return_if_fail (E_IS_CALENDAR_ITEM (item));
2131
2132 calitem = E_CALENDAR_ITEM (item);
2133 *x1 = calitem->x1;
2134 *y1 = calitem->y1;
2135 *x2 = calitem->x2;
2136 *y2 = calitem->y2;
2137 }
2138
2139 /* This checks if any fonts have changed, and if so it recalculates the
2140 * text sizes and the minimum month size. */
2141 static void
e_calendar_item_recalc_sizes(ECalendarItem * calitem)2142 e_calendar_item_recalc_sizes (ECalendarItem *calitem)
2143 {
2144 GnomeCanvasItem *canvas_item;
2145 gint max_day_width, digit, max_digit_width, max_week_number_digit_width;
2146 gint char_height, width, min_cell_width, min_cell_height;
2147 gchar buffer[64];
2148 struct tm tmp_tm;
2149 PangoFontDescription *font_desc, *wkfont_desc;
2150 PangoContext *pango_context;
2151 PangoFontMetrics *font_metrics;
2152 PangoLayout *layout;
2153 PangoAttrList *tnum;
2154 PangoAttribute *attr;
2155 GDateWeekday weekday;
2156 GtkWidget *widget;
2157 GtkStyleContext *style_context;
2158 GtkBorder padding;
2159
2160 canvas_item = GNOME_CANVAS_ITEM (calitem);
2161 widget = GTK_WIDGET (canvas_item->canvas);
2162 style_context = gtk_widget_get_style_context (widget);
2163 gtk_style_context_get_padding (style_context, gtk_style_context_get_state (style_context), &padding);
2164
2165 /* Set up Pango prerequisites */
2166 font_desc = calitem->font_desc;
2167 wkfont_desc = calitem->week_number_font_desc;
2168
2169 pango_context = gtk_widget_create_pango_context (
2170 GTK_WIDGET (canvas_item->canvas));
2171 font_metrics = pango_context_get_metrics (
2172 pango_context, font_desc,
2173 pango_context_get_language (pango_context));
2174 if (!font_desc)
2175 font_desc = pango_context_get_font_description (pango_context);
2176 font_desc = pango_font_description_copy (font_desc);
2177 layout = pango_layout_new (pango_context);
2178
2179 char_height =
2180 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
2181 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
2182
2183 max_day_width = 0;
2184 for (weekday = G_DATE_MONDAY; weekday <= G_DATE_SUNDAY; weekday++) {
2185 layout_set_day_text (calitem, layout, weekday);
2186 pango_layout_get_pixel_size (layout, &width, NULL);
2187
2188 calitem->day_widths[weekday] = width;
2189 max_day_width = MAX (max_day_width, width);
2190 }
2191 calitem->max_day_width = max_day_width;
2192
2193 tnum = pango_attr_list_new ();
2194 attr = pango_attr_font_features_new ("tnum=1");
2195 pango_attr_list_insert_before (tnum, attr);
2196 pango_layout_set_attributes (layout, tnum);
2197 pango_attr_list_unref (tnum);
2198
2199 max_digit_width = 0;
2200 max_week_number_digit_width = 0;
2201 for (digit = 0; digit < 10; digit++) {
2202 gchar locale_digit[5];
2203 gint locale_digit_len;
2204
2205 locale_digit_len = sprintf (locale_digit, get_digit_fomat (), digit);
2206
2207 pango_layout_set_text (layout, locale_digit, locale_digit_len);
2208 pango_layout_get_pixel_size (layout, &width, NULL);
2209
2210 calitem->digit_widths[digit] = width;
2211 max_digit_width = MAX (max_digit_width, width);
2212
2213 if (wkfont_desc) {
2214 pango_context_set_font_description (pango_context, wkfont_desc);
2215 pango_layout_context_changed (layout);
2216
2217 pango_layout_set_text (layout, locale_digit, locale_digit_len);
2218 pango_layout_get_pixel_size (layout, &width, NULL);
2219
2220 calitem->week_number_digit_widths[digit] = width;
2221 max_week_number_digit_width = MAX (max_week_number_digit_width, width);
2222
2223 pango_context_set_font_description (pango_context, font_desc);
2224 pango_layout_context_changed (layout);
2225 } else {
2226 calitem->week_number_digit_widths[digit] = width;
2227 max_week_number_digit_width = max_digit_width;
2228 }
2229 }
2230 calitem->max_digit_width = max_digit_width;
2231 calitem->max_week_number_digit_width = max_week_number_digit_width;
2232
2233 min_cell_width = MAX (calitem->max_day_width, (calitem->max_digit_width * 2))
2234 + E_CALENDAR_ITEM_MIN_CELL_XPAD;
2235 min_cell_height = char_height + E_CALENDAR_ITEM_MIN_CELL_YPAD;
2236
2237 calitem->min_month_width = E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS
2238 + E_CALENDAR_ITEM_XPAD_BEFORE_CELLS + min_cell_width * 7
2239 + E_CALENDAR_ITEM_XPAD_AFTER_CELLS;
2240 if (calitem->show_week_numbers) {
2241 calitem->min_month_width += calitem->max_week_number_digit_width * 2
2242 + E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS + 1;
2243 }
2244
2245 calitem->min_month_height = padding.top * 2
2246 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + char_height
2247 + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME + 1
2248 + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS
2249 + char_height + E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS + 1
2250 + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS + min_cell_height * 6
2251 + E_CALENDAR_ITEM_YPAD_BELOW_CELLS;
2252
2253 calitem->max_month_name_width = 50;
2254 memset (&tmp_tm, 0, sizeof (tmp_tm));
2255 tmp_tm.tm_year = 2000 - 100;
2256 tmp_tm.tm_mday = 1;
2257 tmp_tm.tm_isdst = -1;
2258 for (tmp_tm.tm_mon = 0; tmp_tm.tm_mon < 12; tmp_tm.tm_mon++) {
2259 mktime (&tmp_tm);
2260
2261 e_utf8_strftime (buffer, sizeof (buffer), C_("CalItem", "%B"), &tmp_tm);
2262
2263 pango_layout_set_text (layout, buffer, -1);
2264 pango_layout_get_pixel_size (layout, &width, NULL);
2265
2266 if (width > calitem->max_month_name_width)
2267 calitem->max_month_name_width = width;
2268 }
2269
2270 g_object_unref (layout);
2271 g_object_unref (pango_context);
2272 pango_font_metrics_unref (font_metrics);
2273 pango_font_description_free (font_desc);
2274 }
2275
2276 static void
e_calendar_item_get_day_style(ECalendarItem * calitem,gint year,gint month,gint day,gint day_style,gboolean today,gboolean prev_or_next_month,gboolean selected,gboolean has_focus,gboolean drop_target,GdkColor ** bg_color,GdkColor ** fg_color,GdkColor ** box_color,gboolean * bold,gboolean * italic,GdkColor * local_bg_color,GdkColor * local_fg_color)2277 e_calendar_item_get_day_style (ECalendarItem *calitem,
2278 gint year,
2279 gint month,
2280 gint day,
2281 gint day_style,
2282 gboolean today,
2283 gboolean prev_or_next_month,
2284 gboolean selected,
2285 gboolean has_focus,
2286 gboolean drop_target,
2287 GdkColor **bg_color,
2288 GdkColor **fg_color,
2289 GdkColor **box_color,
2290 gboolean *bold,
2291 gboolean *italic,
2292 GdkColor *local_bg_color,
2293 GdkColor *local_fg_color)
2294 {
2295 GtkWidget *widget;
2296
2297 widget = GTK_WIDGET (GNOME_CANVAS_ITEM (calitem)->canvas);
2298
2299 *bg_color = NULL;
2300 *fg_color = NULL;
2301 *box_color = NULL;
2302
2303 *bold = (day_style & E_CALENDAR_ITEM_MARK_BOLD) ==
2304 E_CALENDAR_ITEM_MARK_BOLD;
2305 *italic = (day_style & E_CALENDAR_ITEM_MARK_ITALIC) ==
2306 E_CALENDAR_ITEM_MARK_ITALIC;
2307
2308 if (today)
2309 *box_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_TODAY_BOX];
2310
2311 if (prev_or_next_month) {
2312 *fg_color = local_fg_color;
2313 e_utils_get_theme_color_color (widget, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR, local_fg_color);
2314 }
2315
2316 if (selected) {
2317 *bg_color = local_bg_color;
2318 *fg_color = local_fg_color;
2319
2320 if (has_focus) {
2321 e_utils_get_theme_color_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, local_bg_color);
2322 e_utils_get_theme_color_color (widget, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR, local_fg_color);
2323 } else {
2324 GdkColor base_bg;
2325
2326 e_utils_get_theme_color_color (widget, "theme_unfocused_selected_bg_color,theme_selected_bg_color", E_UTILS_DEFAULT_THEME_UNFOCUSED_SELECTED_BG_COLOR, local_bg_color);
2327 e_utils_get_theme_color_color (widget, "theme_unfocused_selected_fg_color,theme_selected_fg_color", E_UTILS_DEFAULT_THEME_UNFOCUSED_SELECTED_FG_COLOR, local_fg_color);
2328
2329 e_utils_get_theme_color_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &base_bg);
2330
2331 if (local_bg_color->red == base_bg.red &&
2332 local_bg_color->green == base_bg.green &&
2333 local_bg_color->blue == base_bg.blue) {
2334 e_utils_get_theme_color_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, local_bg_color);
2335 e_utils_get_theme_color_color (widget, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR, local_fg_color);
2336 }
2337 }
2338 }
2339 }
2340
2341 static gboolean
e_calendar_item_button_press(ECalendarItem * calitem,GdkEvent * button_event)2342 e_calendar_item_button_press (ECalendarItem *calitem,
2343 GdkEvent *button_event)
2344 {
2345 GdkGrabStatus grab_status;
2346 GdkDevice *event_device;
2347 guint event_button = 0;
2348 guint32 event_time;
2349 gdouble event_x_win = 0;
2350 gdouble event_y_win = 0;
2351 gint month_offset, day, add_days = 0;
2352 gboolean all_week, round_up_end = FALSE, round_down_start = FALSE;
2353
2354 gdk_event_get_button (button_event, &event_button);
2355 gdk_event_get_coords (button_event, &event_x_win, &event_y_win);
2356 event_device = gdk_event_get_device (button_event);
2357 event_time = gdk_event_get_time (button_event);
2358
2359 if (event_button == 4)
2360 e_calendar_item_set_first_month_with_emit (
2361 calitem, calitem->year,
2362 calitem->month - 1, TRUE);
2363 else if (event_button == 5)
2364 e_calendar_item_set_first_month_with_emit (
2365 calitem, calitem->year,
2366 calitem->month + 1, TRUE);
2367
2368 if (!e_calendar_item_convert_position_to_day (calitem,
2369 event_x_win,
2370 event_y_win,
2371 TRUE,
2372 &month_offset, &day,
2373 &all_week))
2374 return FALSE;
2375
2376 if (event_button == 3 && day == -1
2377 && e_calendar_item_get_display_popup (calitem)) {
2378 e_calendar_item_show_popup_menu (
2379 calitem, button_event, month_offset);
2380 return TRUE;
2381 }
2382
2383 if (event_button != 1 || day == -1)
2384 return FALSE;
2385
2386 if (calitem->max_days_selected < 1)
2387 return TRUE;
2388
2389 grab_status = gnome_canvas_item_grab (
2390 GNOME_CANVAS_ITEM (calitem),
2391 GDK_POINTER_MOTION_MASK |
2392 GDK_BUTTON_RELEASE_MASK,
2393 NULL,
2394 event_device,
2395 event_time);
2396
2397 if (grab_status != GDK_GRAB_SUCCESS)
2398 return FALSE;
2399
2400 if (all_week && calitem->keep_wdays_on_weeknum_click) {
2401 gint tmp_start_moff, tmp_start_day;
2402
2403 tmp_start_moff = calitem->selection_start_month_offset;
2404 tmp_start_day = calitem->selection_start_day;
2405 e_calendar_item_round_down_selection (
2406 calitem, &tmp_start_moff, &tmp_start_day);
2407
2408 e_calendar_item_round_down_selection (calitem, &month_offset, &day);
2409 month_offset += calitem->selection_start_month_offset - tmp_start_moff;
2410 day += calitem->selection_start_day - tmp_start_day;
2411
2412 /* keep same count of days selected */
2413 add_days = e_calendar_item_get_inclusive_days (
2414 calitem,
2415 calitem->selection_start_month_offset,
2416 calitem->selection_start_day,
2417 calitem->selection_end_month_offset,
2418 calitem->selection_end_day) - 1;
2419 }
2420
2421 calitem->selection_set = TRUE;
2422 calitem->selection_start_month_offset = month_offset;
2423 calitem->selection_start_day = day;
2424 calitem->selection_end_month_offset = month_offset;
2425 calitem->selection_end_day = day;
2426
2427 if (add_days > 0)
2428 e_calendar_item_add_days_to_selection (calitem, add_days);
2429
2430 calitem->selection_real_start_month_offset = month_offset;
2431 calitem->selection_real_start_day = day;
2432
2433 calitem->selection_from_full_week = FALSE;
2434 calitem->selecting = TRUE;
2435 calitem->selection_dragging_end = TRUE;
2436
2437 if (all_week && !calitem->keep_wdays_on_weeknum_click) {
2438 calitem->selection_from_full_week = TRUE;
2439 round_up_end = TRUE;
2440 }
2441
2442 if (calitem->days_to_start_week_selection == 1) {
2443 round_down_start = TRUE;
2444 round_up_end = TRUE;
2445 }
2446
2447 /* Don't round up or down if we can't select a week or more,
2448 * or when keeping week days. */
2449 if (calitem->max_days_selected < 7 ||
2450 (all_week && calitem->keep_wdays_on_weeknum_click)) {
2451 round_down_start = FALSE;
2452 round_up_end = FALSE;
2453 }
2454
2455 if (round_up_end)
2456 e_calendar_item_round_up_selection (
2457 calitem, &calitem->selection_end_month_offset,
2458 &calitem->selection_end_day);
2459
2460 if (round_down_start)
2461 e_calendar_item_round_down_selection (
2462 calitem, &calitem->selection_start_month_offset,
2463 &calitem->selection_start_day);
2464
2465 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
2466
2467 return TRUE;
2468 }
2469
2470 static gboolean
e_calendar_item_button_release(ECalendarItem * calitem,GdkEvent * button_event)2471 e_calendar_item_button_release (ECalendarItem *calitem,
2472 GdkEvent *button_event)
2473 {
2474 guint32 event_time;
2475
2476 event_time = gdk_event_get_time (button_event);
2477 e_calendar_item_stop_selecting (calitem, event_time);
2478
2479 return FALSE;
2480 }
2481
2482 static gboolean
e_calendar_item_motion(ECalendarItem * calitem,GdkEvent * event)2483 e_calendar_item_motion (ECalendarItem *calitem,
2484 GdkEvent *event)
2485 {
2486 gint start_month, start_day, end_month, end_day, month_offset, day;
2487 gint tmp_month, tmp_day, days_in_selection;
2488 gboolean all_week, round_up_end = FALSE, round_down_start = FALSE;
2489
2490 if (!calitem->selecting)
2491 return FALSE;
2492
2493 if (!e_calendar_item_convert_position_to_day (calitem,
2494 event->button.x,
2495 event->button.y,
2496 TRUE,
2497 &month_offset, &day,
2498 &all_week))
2499 return FALSE;
2500
2501 if (day == -1)
2502 return FALSE;
2503
2504 if (calitem->selection_dragging_end) {
2505 start_month = calitem->selection_real_start_month_offset;
2506 start_day = calitem->selection_real_start_day;
2507 end_month = month_offset;
2508 end_day = day;
2509 } else {
2510 start_month = month_offset;
2511 start_day = day;
2512 end_month = calitem->selection_real_start_month_offset;
2513 end_day = calitem->selection_real_start_day;
2514 }
2515
2516 if (start_month > end_month || (start_month == end_month
2517 && start_day > end_day)) {
2518 tmp_month = start_month;
2519 tmp_day = start_day;
2520 start_month = end_month;
2521 start_day = end_day;
2522 end_month = tmp_month;
2523 end_day = tmp_day;
2524
2525 calitem->selection_dragging_end =
2526 !calitem->selection_dragging_end;
2527 }
2528
2529 if (calitem->days_to_start_week_selection > 0) {
2530 days_in_selection = e_calendar_item_get_inclusive_days (
2531 calitem, start_month, start_day, end_month, end_day);
2532 if (days_in_selection >= calitem->days_to_start_week_selection) {
2533 round_down_start = TRUE;
2534 round_up_end = TRUE;
2535 }
2536 }
2537
2538 /* If we are over a week number and we are dragging the end of the
2539 * selection, we round up to the end of this week. */
2540 if (all_week && calitem->selection_dragging_end)
2541 round_up_end = TRUE;
2542
2543 /* If the selection was started from a week number and we are dragging
2544 * the start of the selection, we need to round up the end to include
2545 * all of the original week selected. */
2546 if (calitem->selection_from_full_week
2547 && !calitem->selection_dragging_end)
2548 round_up_end = TRUE;
2549
2550 /* Don't round up or down if we can't select a week or more. */
2551 if (calitem->max_days_selected < 7) {
2552 round_down_start = FALSE;
2553 round_up_end = FALSE;
2554 }
2555
2556 if (round_up_end)
2557 e_calendar_item_round_up_selection (
2558 calitem, &end_month,
2559 &end_day);
2560 if (round_down_start)
2561 e_calendar_item_round_down_selection (
2562 calitem, &start_month,
2563 &start_day);
2564
2565 /* Check we don't go over the maximum number of days to select. */
2566 if (calitem->selection_dragging_end) {
2567 e_calendar_item_check_selection_end (
2568 calitem,
2569 start_month,
2570 start_day,
2571 &end_month,
2572 &end_day);
2573 } else {
2574 e_calendar_item_check_selection_start (
2575 calitem,
2576 &start_month,
2577 &start_day,
2578 end_month,
2579 end_day);
2580 }
2581
2582 if (start_month == calitem->selection_start_month_offset
2583 && start_day == calitem->selection_start_day
2584 && end_month == calitem->selection_end_month_offset
2585 && end_day == calitem->selection_end_day)
2586 return FALSE;
2587
2588 calitem->selection_start_month_offset = start_month;
2589 calitem->selection_start_day = start_day;
2590 calitem->selection_end_month_offset = end_month;
2591 calitem->selection_end_day = end_day;
2592
2593 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
2594
2595 return TRUE;
2596 }
2597
2598 static void
e_calendar_item_check_selection_end(ECalendarItem * calitem,gint start_month,gint start_day,gint * end_month,gint * end_day)2599 e_calendar_item_check_selection_end (ECalendarItem *calitem,
2600 gint start_month,
2601 gint start_day,
2602 gint *end_month,
2603 gint *end_day)
2604 {
2605 gint year, month, max_month, max_day, days_in_month;
2606
2607 if (calitem->max_days_selected <= 0)
2608 return;
2609
2610 year = calitem->year;
2611 month = calitem->month + start_month;
2612 e_calendar_item_normalize_date (calitem, &year, &month);
2613
2614 max_month = start_month;
2615 max_day = start_day + calitem->max_days_selected - 1;
2616
2617 for (;;) {
2618 days_in_month = DAYS_IN_MONTH (year, month);
2619 if (max_day <= days_in_month)
2620 break;
2621 max_month++;
2622 month++;
2623 if (month == 12) {
2624 year++;
2625 month = 0;
2626 }
2627 max_day -= days_in_month;
2628 }
2629
2630 if (*end_month > max_month) {
2631 *end_month = max_month;
2632 *end_day = max_day;
2633 } else if (*end_month == max_month && *end_day > max_day) {
2634 *end_day = max_day;
2635 }
2636 }
2637
2638 static void
e_calendar_item_check_selection_start(ECalendarItem * calitem,gint * start_month,gint * start_day,gint end_month,gint end_day)2639 e_calendar_item_check_selection_start (ECalendarItem *calitem,
2640 gint *start_month,
2641 gint *start_day,
2642 gint end_month,
2643 gint end_day)
2644 {
2645 gint year, month, min_month, min_day, days_in_month;
2646
2647 if (calitem->max_days_selected <= 0)
2648 return;
2649
2650 year = calitem->year;
2651 month = calitem->month + end_month;
2652 e_calendar_item_normalize_date (calitem, &year, &month);
2653
2654 min_month = end_month;
2655 min_day = end_day - calitem->max_days_selected + 1;
2656
2657 while (min_day <= 0) {
2658 min_month--;
2659 month--;
2660 if (month == -1) {
2661 year--;
2662 month = 11;
2663 }
2664 days_in_month = DAYS_IN_MONTH (year, month);
2665 min_day += days_in_month;
2666 }
2667
2668 if (*start_month < min_month) {
2669 *start_month = min_month;
2670 *start_day = min_day;
2671 } else if (*start_month == min_month && *start_day < min_day) {
2672 *start_day = min_day;
2673 }
2674 }
2675
2676 /* Converts a position within the item to a month & day.
2677 * The month returned is 0 for the top-left month displayed.
2678 * If the position is over the month heading -1 is returned for the day.
2679 * If the position is over a week number the first day of the week is returned
2680 * and entire_week is set to TRUE.
2681 * It returns FALSE if the position is completely outside all months. */
2682 static gboolean
e_calendar_item_convert_position_to_day(ECalendarItem * calitem,gint event_x,gint event_y,gboolean round_empty_positions,gint * month_offset,gint * day,gboolean * entire_week)2683 e_calendar_item_convert_position_to_day (ECalendarItem *calitem,
2684 gint event_x,
2685 gint event_y,
2686 gboolean round_empty_positions,
2687 gint *month_offset,
2688 gint *day,
2689 gboolean *entire_week)
2690 {
2691 GnomeCanvasItem *item;
2692 GtkWidget *widget;
2693 GtkStyleContext *style_context;
2694 GtkBorder padding;
2695 gint xthickness, ythickness, char_height;
2696 gint x, y, row, col, cells_x, cells_y, day_row, day_col;
2697 gint first_day_offset, days_in_month, days_in_prev_month;
2698 gint week_num_x1, week_num_x2;
2699 PangoContext *pango_context;
2700 PangoFontMetrics *font_metrics;
2701
2702 item = GNOME_CANVAS_ITEM (calitem);
2703 widget = GTK_WIDGET (item->canvas);
2704 style_context = gtk_widget_get_style_context (widget);
2705 gtk_style_context_get_padding (style_context, gtk_style_context_get_state (style_context), &padding);
2706
2707 pango_context = gtk_widget_create_pango_context (widget);
2708 font_metrics = pango_context_get_metrics (
2709 pango_context, calitem->font_desc,
2710 pango_context_get_language (pango_context));
2711
2712 char_height =
2713 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
2714 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
2715 xthickness = padding.left;
2716 ythickness = padding.top;
2717
2718 pango_font_metrics_unref (font_metrics);
2719 g_object_unref (pango_context);
2720
2721 *entire_week = FALSE;
2722
2723 x = event_x - xthickness - calitem->x_offset;
2724 y = event_y - ythickness;
2725
2726 if (x < 0 || y < 0)
2727 return FALSE;
2728
2729 row = y / calitem->month_height;
2730 col = x / calitem->month_width;
2731
2732 if (row >= calitem->rows || col >= calitem->cols)
2733 return FALSE;
2734 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
2735 col = calitem->cols - 1 - col;
2736
2737 *month_offset = row * calitem->cols + col;
2738
2739 x = x % calitem->month_width;
2740 y = y % calitem->month_height;
2741
2742 if (y < ythickness * 2 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
2743 + char_height + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME) {
2744 *day = -1;
2745 return TRUE;
2746 }
2747
2748 cells_y = ythickness * 2 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
2749 + char_height + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
2750 + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS + calitem->month_tpad
2751 + char_height + E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS + 1
2752 + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS;
2753 y -= cells_y;
2754 if (y < 0)
2755 return FALSE;
2756 day_row = y / calitem->cell_height;
2757 if (day_row >= E_CALENDAR_ROWS_PER_MONTH)
2758 return FALSE;
2759
2760 week_num_x1 = E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS + calitem->month_lpad;
2761
2762 if (calitem->show_week_numbers) {
2763 week_num_x2 = week_num_x1
2764 + calitem->max_week_number_digit_width * 2;
2765 if (x >= week_num_x1 && x < week_num_x2)
2766 *entire_week = TRUE;
2767 cells_x = week_num_x2 + E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS + 1;
2768 } else {
2769 cells_x = week_num_x1;
2770 }
2771
2772 if (*entire_week) {
2773 day_col = 0;
2774 } else {
2775 cells_x += E_CALENDAR_ITEM_XPAD_BEFORE_CELLS;
2776 x -= cells_x;
2777 if (x < 0)
2778 return FALSE;
2779 day_col = x / calitem->cell_width;
2780 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
2781 day_col = E_CALENDAR_COLS_PER_MONTH - 1 - day_col;
2782 if (day_col >= E_CALENDAR_COLS_PER_MONTH)
2783 return FALSE;
2784 }
2785
2786 *day = day_row * E_CALENDAR_COLS_PER_MONTH + day_col;
2787
2788 e_calendar_item_get_month_info (
2789 calitem, row, col, &first_day_offset,
2790 &days_in_month, &days_in_prev_month);
2791 if (*day < first_day_offset) {
2792 if (*entire_week || (row == 0 && col == 0)) {
2793 (*month_offset)--;
2794 *day = days_in_prev_month + 1 - first_day_offset
2795 + *day;
2796 return TRUE;
2797 } else if (round_empty_positions) {
2798 *day = first_day_offset;
2799 } else {
2800 return FALSE;
2801 }
2802 }
2803
2804 *day -= first_day_offset - 1;
2805
2806 if (*day > days_in_month) {
2807 if (row == calitem->rows - 1 && col == calitem->cols - 1) {
2808 (*month_offset)++;
2809 *day -= days_in_month;
2810 return TRUE;
2811 } else if (round_empty_positions) {
2812 *day = days_in_month;
2813 } else {
2814 return FALSE;
2815 }
2816 }
2817
2818 return TRUE;
2819 }
2820
2821 static void
e_calendar_item_get_month_info(ECalendarItem * calitem,gint row,gint col,gint * first_day_offset,gint * days_in_month,gint * days_in_prev_month)2822 e_calendar_item_get_month_info (ECalendarItem *calitem,
2823 gint row,
2824 gint col,
2825 gint *first_day_offset,
2826 gint *days_in_month,
2827 gint *days_in_prev_month)
2828 {
2829 GDateWeekday start_weekday;
2830 gint year, month, first_day_of_month;
2831 struct tm tmp_tm = { 0 };
2832
2833 month = calitem->month + row * calitem->cols + col;
2834 year = calitem->year + month / 12;
2835 month = month % 12;
2836
2837 *days_in_month = DAYS_IN_MONTH (year, month);
2838 if (month == 0)
2839 *days_in_prev_month = DAYS_IN_MONTH (year - 1, 11);
2840 else
2841 *days_in_prev_month = DAYS_IN_MONTH (year, month - 1);
2842
2843 tmp_tm.tm_year = year - 1900;
2844 tmp_tm.tm_mon = month;
2845 tmp_tm.tm_mday = 1;
2846 tmp_tm.tm_isdst = -1;
2847 mktime (&tmp_tm);
2848
2849 start_weekday = e_weekday_from_tm_wday (tmp_tm.tm_wday);
2850
2851 first_day_of_month = e_weekday_get_days_between (
2852 calitem->week_start_day, start_weekday);
2853
2854 if (row == 0 && col == 0 && first_day_of_month == 0)
2855 *first_day_offset = 7;
2856 else
2857 *first_day_offset = first_day_of_month;
2858 }
2859
2860 void
e_calendar_item_get_first_month(ECalendarItem * calitem,gint * year,gint * month)2861 e_calendar_item_get_first_month (ECalendarItem *calitem,
2862 gint *year,
2863 gint *month)
2864 {
2865 *year = calitem->year;
2866 *month = calitem->month;
2867 }
2868
2869 gboolean
e_calendar_item_convert_position_to_date(ECalendarItem * calitem,gint event_x,gint event_y,GDate * date)2870 e_calendar_item_convert_position_to_date (ECalendarItem *calitem,
2871 gint event_x,
2872 gint event_y,
2873 GDate *date)
2874 {
2875 gint month_offset = -1;
2876 gint day = -1, dday, dmonth, dyear;
2877 gboolean entire_week = FALSE;
2878
2879 g_return_val_if_fail (E_IS_CALENDAR_ITEM (calitem), FALSE);
2880 g_return_val_if_fail (date != NULL, FALSE);
2881
2882 if (calitem->rows == 0 || calitem->cols == 0)
2883 return FALSE;
2884
2885 if (!e_calendar_item_convert_position_to_day (calitem, event_x, event_y, FALSE, &month_offset, &day, &entire_week) ||
2886 day < 0 || entire_week)
2887 return FALSE;
2888
2889 dyear = calitem->year;
2890 dmonth = calitem->month + month_offset;
2891 e_calendar_item_normalize_date (calitem, &dyear, &dmonth);
2892 dday = day;
2893
2894 g_date_set_dmy (date, dday, dmonth + 1, dyear);
2895
2896 return g_date_valid (date);
2897 }
2898
2899 static void
e_calendar_item_preserve_day_selection(ECalendarItem * calitem,gint selected_day,gint * month_offset,gint * day)2900 e_calendar_item_preserve_day_selection (ECalendarItem *calitem,
2901 gint selected_day,
2902 gint *month_offset,
2903 gint *day)
2904 {
2905 gint year, month, weekday, days, days_in_month;
2906 struct tm tmp_tm = { 0 };
2907
2908 year = calitem->year;
2909 month = calitem->month + *month_offset;
2910 e_calendar_item_normalize_date (calitem, &year, &month);
2911
2912 tmp_tm.tm_year = year - 1900;
2913 tmp_tm.tm_mon = month;
2914 tmp_tm.tm_mday = *day;
2915 tmp_tm.tm_isdst = -1;
2916 mktime (&tmp_tm);
2917
2918 /* Convert to 0 (Monday) to 6 (Sunday). */
2919 weekday = (tmp_tm.tm_wday + 6) % 7;
2920
2921 /* Calculate how many days to the start of the row. */
2922 days = (weekday + 7 - selected_day) % 7;
2923
2924 *day -= days;
2925 if (*day <= 0) {
2926 month--;
2927 if (month == -1) {
2928 year--;
2929 month = 11;
2930 }
2931 days_in_month = DAYS_IN_MONTH (year, month);
2932 (*month_offset)--;
2933 *day += days_in_month;
2934 }
2935 }
2936
2937 /* This also handles values of month < 0 or > 11 by updating the year. */
2938 static void
e_calendar_item_set_first_month_with_emit(ECalendarItem * calitem,gint year,gint month,gboolean emit_date_range_moved)2939 e_calendar_item_set_first_month_with_emit (ECalendarItem *calitem,
2940 gint year,
2941 gint month,
2942 gboolean emit_date_range_moved)
2943 {
2944 gint new_year, new_month, months_diff, num_months;
2945 gint old_days_in_selection, new_days_in_selection;
2946
2947 new_year = year;
2948 new_month = month;
2949 e_calendar_item_normalize_date (calitem, &new_year, &new_month);
2950
2951 if (calitem->year == new_year && calitem->month == new_month)
2952 return;
2953
2954 /* Update the selection. */
2955 num_months = calitem->rows * calitem->cols;
2956 months_diff = (new_year - calitem->year) * 12
2957 + new_month - calitem->month;
2958
2959 if (calitem->selection_set) {
2960 if (!calitem->move_selection_when_moving
2961 || (calitem->selection_start_month_offset - months_diff >= 0
2962 && calitem->selection_end_month_offset - months_diff < num_months)) {
2963 calitem->selection_start_month_offset -= months_diff;
2964 calitem->selection_end_month_offset -= months_diff;
2965 calitem->selection_real_start_month_offset -= months_diff;
2966
2967 calitem->year = new_year;
2968 calitem->month = new_month;
2969 } else {
2970 gint selected_day;
2971 struct tm tmp_tm = { 0 };
2972
2973 old_days_in_selection = e_calendar_item_get_inclusive_days (
2974 calitem,
2975 calitem->selection_start_month_offset,
2976 calitem->selection_start_day,
2977 calitem->selection_end_month_offset,
2978 calitem->selection_end_day);
2979
2980 /* Calculate the currently selected day */
2981 tmp_tm.tm_year = calitem->year - 1900;
2982 tmp_tm.tm_mon = calitem->month + calitem->selection_start_month_offset;
2983 tmp_tm.tm_mday = calitem->selection_start_day;
2984 tmp_tm.tm_isdst = -1;
2985 mktime (&tmp_tm);
2986
2987 selected_day = (tmp_tm.tm_wday + 6) % 7;
2988
2989 /* Make sure the selection will be displayed. */
2990 if (calitem->selection_start_month_offset < 0
2991 || calitem->selection_start_month_offset >= num_months) {
2992 calitem->selection_end_month_offset -=
2993 calitem->selection_start_month_offset;
2994 calitem->selection_start_month_offset = 0;
2995 }
2996
2997 /* We want to ensure that the same number of days are
2998 * selected after we have moved the selection. */
2999 calitem->year = new_year;
3000 calitem->month = new_month;
3001
3002 e_calendar_item_ensure_valid_day (
3003 calitem, &calitem->selection_start_month_offset,
3004 &calitem->selection_start_day);
3005 e_calendar_item_ensure_valid_day (
3006 calitem, &calitem->selection_end_month_offset,
3007 &calitem->selection_end_day);
3008
3009 if (calitem->preserve_day_when_moving) {
3010 e_calendar_item_preserve_day_selection (
3011 calitem, selected_day,
3012 &calitem->selection_start_month_offset,
3013 &calitem->selection_start_day);
3014 }
3015
3016 new_days_in_selection = e_calendar_item_get_inclusive_days (
3017 calitem,
3018 calitem->selection_start_month_offset,
3019 calitem->selection_start_day,
3020 calitem->selection_end_month_offset,
3021 calitem->selection_end_day);
3022
3023 if (old_days_in_selection != new_days_in_selection)
3024 e_calendar_item_add_days_to_selection (
3025 calitem, old_days_in_selection -
3026 new_days_in_selection);
3027
3028 /* Flag that we need to emit the "selection_changed"
3029 * signal. We don't want to emit it here since setting
3030 * the "year" and "month" args would result in 2
3031 * signals emitted. */
3032 calitem->selection_changed = TRUE;
3033 }
3034 } else {
3035 calitem->year = new_year;
3036 calitem->month = new_month;
3037 }
3038
3039 e_calendar_item_date_range_changed (calitem);
3040 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
3041
3042 if (emit_date_range_moved)
3043 g_signal_emit (calitem, e_calendar_item_signals[DATE_RANGE_MOVED], 0);
3044 }
3045
3046 /* This also handles values of month < 0 or > 11 by updating the year. */
3047 void
e_calendar_item_set_first_month(ECalendarItem * calitem,gint year,gint month)3048 e_calendar_item_set_first_month (ECalendarItem *calitem,
3049 gint year,
3050 gint month)
3051 {
3052 e_calendar_item_set_first_month_with_emit (calitem, year, month, TRUE);
3053 }
3054
3055 /* Get the maximum number of days selectable */
3056 gint
e_calendar_item_get_max_days_sel(ECalendarItem * calitem)3057 e_calendar_item_get_max_days_sel (ECalendarItem *calitem)
3058 {
3059 return calitem->max_days_selected;
3060 }
3061
3062 /* Set the maximum number of days selectable */
3063 void
e_calendar_item_set_max_days_sel(ECalendarItem * calitem,gint days)3064 e_calendar_item_set_max_days_sel (ECalendarItem *calitem,
3065 gint days)
3066 {
3067 calitem->max_days_selected = MAX (0, days);
3068 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
3069 }
3070
3071 /* Get the maximum number of days before whole weeks are selected */
3072 gint
e_calendar_item_get_days_start_week_sel(ECalendarItem * calitem)3073 e_calendar_item_get_days_start_week_sel (ECalendarItem *calitem)
3074 {
3075 return calitem->days_to_start_week_selection;
3076 }
3077
3078 /* Set the maximum number of days before whole weeks are selected */
3079 void
e_calendar_item_set_days_start_week_sel(ECalendarItem * calitem,gint days)3080 e_calendar_item_set_days_start_week_sel (ECalendarItem *calitem,
3081 gint days)
3082 {
3083 calitem->days_to_start_week_selection = days;
3084 }
3085
3086 gboolean
e_calendar_item_get_display_popup(ECalendarItem * calitem)3087 e_calendar_item_get_display_popup (ECalendarItem *calitem)
3088 {
3089 return calitem->display_popup;
3090 }
3091
3092 void
e_calendar_item_set_display_popup(ECalendarItem * calitem,gboolean display)3093 e_calendar_item_set_display_popup (ECalendarItem *calitem,
3094 gboolean display)
3095 {
3096 calitem->display_popup = display;
3097 }
3098
3099 /* This will make sure that the given year & month are valid, i.e. if month
3100 * is < 0 or > 11 the year and month will be updated accordingly. */
3101 void
e_calendar_item_normalize_date(ECalendarItem * calitem,gint * year,gint * month)3102 e_calendar_item_normalize_date (ECalendarItem *calitem,
3103 gint *year,
3104 gint *month)
3105 {
3106 if (*month >= 0) {
3107 *year += *month / 12;
3108 *month = *month % 12;
3109 } else {
3110 *year += *month / 12 - 1;
3111 *month = *month % 12;
3112 if (*month != 0)
3113 *month += 12;
3114 }
3115 }
3116
3117 /* Adds or subtracts days from the selection. It is used when we switch months
3118 * and the selection extends past the end of a month but we want to keep the
3119 * number of days selected the same. days should not be more than 30. */
3120 static void
e_calendar_item_add_days_to_selection(ECalendarItem * calitem,gint days)3121 e_calendar_item_add_days_to_selection (ECalendarItem *calitem,
3122 gint days)
3123 {
3124 gint year, month, days_in_month;
3125
3126 year = calitem->year;
3127 month = calitem->month + calitem->selection_end_month_offset;
3128 e_calendar_item_normalize_date (calitem, &year, &month);
3129
3130 calitem->selection_end_day += days;
3131 if (calitem->selection_end_day <= 0) {
3132 month--;
3133 e_calendar_item_normalize_date (calitem, &year, &month);
3134 calitem->selection_end_month_offset--;
3135 calitem->selection_end_day += DAYS_IN_MONTH (year, month);
3136 } else {
3137 days_in_month = DAYS_IN_MONTH (year, month);
3138 if (calitem->selection_end_day > days_in_month) {
3139 calitem->selection_end_month_offset++;
3140 calitem->selection_end_day -= days_in_month;
3141 }
3142 }
3143 }
3144
3145 /* Gets the range of dates actually shown. Months are 0 to 11.
3146 * This also includes the last days of the previous month and the first days
3147 * of the following month, which are normally shown in gray.
3148 * It returns FALSE if no dates are currently shown. */
3149 gboolean
e_calendar_item_get_date_range(ECalendarItem * calitem,gint * start_year,gint * start_month,gint * start_day,gint * end_year,gint * end_month,gint * end_day)3150 e_calendar_item_get_date_range (ECalendarItem *calitem,
3151 gint *start_year,
3152 gint *start_month,
3153 gint *start_day,
3154 gint *end_year,
3155 gint *end_month,
3156 gint *end_day)
3157 {
3158 gint first_day_offset, days_in_month, days_in_prev_month;
3159
3160 if (calitem->rows == 0 || calitem->cols == 0)
3161 return FALSE;
3162
3163 /* Calculate the first day shown. This will be one of the greyed-out
3164 * days before the first full month begins. */
3165 e_calendar_item_get_month_info (
3166 calitem, 0, 0, &first_day_offset,
3167 &days_in_month, &days_in_prev_month);
3168 *start_year = calitem->year;
3169 *start_month = calitem->month - 1;
3170 if (*start_month == -1) {
3171 (*start_year)--;
3172 *start_month = 11;
3173 }
3174 *start_day = days_in_prev_month + 1 - first_day_offset;
3175
3176 /* Calculate the last day shown. This will be one of the greyed-out
3177 * days after the last full month ends. */
3178 e_calendar_item_get_month_info (
3179 calitem, calitem->rows - 1,
3180 calitem->cols - 1, &first_day_offset,
3181 &days_in_month, &days_in_prev_month);
3182 *end_month = calitem->month + calitem->rows * calitem->cols;
3183 *end_year = calitem->year + *end_month / 12;
3184 *end_month %= 12;
3185 *end_day = E_CALENDAR_ROWS_PER_MONTH * E_CALENDAR_COLS_PER_MONTH
3186 - first_day_offset - days_in_month;
3187
3188 return TRUE;
3189 }
3190
3191 /* Simple way to mark days so they appear bold.
3192 * A more flexible interface may be added later. */
3193 void
e_calendar_item_clear_marks(ECalendarItem * calitem)3194 e_calendar_item_clear_marks (ECalendarItem *calitem)
3195 {
3196 GnomeCanvasItem *item;
3197
3198 item = GNOME_CANVAS_ITEM (calitem);
3199
3200 g_free (calitem->styles);
3201 calitem->styles = NULL;
3202
3203 gnome_canvas_request_redraw (
3204 item->canvas, item->x1, item->y1,
3205 item->x2, item->y2);
3206 }
3207
3208 /* add_day_style - whether bit-or with the actual style or change the style fully */
3209 void
e_calendar_item_mark_day(ECalendarItem * calitem,gint year,gint month,gint day,guint8 day_style,gboolean add_day_style)3210 e_calendar_item_mark_day (ECalendarItem *calitem,
3211 gint year,
3212 gint month,
3213 gint day,
3214 guint8 day_style,
3215 gboolean add_day_style)
3216 {
3217 gint month_offset;
3218 gint index;
3219
3220 month_offset = (year - calitem->year) * 12 + month - calitem->month;
3221 if (month_offset < -1 || month_offset > calitem->rows * calitem->cols)
3222 return;
3223
3224 if (!calitem->styles)
3225 calitem->styles = g_new0 (guint8, (calitem->rows * calitem->cols + 2) * 32);
3226
3227 index = (month_offset + 1) * 32 + day;
3228 calitem->styles[index] = day_style |
3229 (add_day_style ? calitem->styles[index] : 0);
3230
3231 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
3232 }
3233
3234 void
e_calendar_item_mark_days(ECalendarItem * calitem,gint start_year,gint start_month,gint start_day,gint end_year,gint end_month,gint end_day,guint8 day_style,gboolean add_day_style)3235 e_calendar_item_mark_days (ECalendarItem *calitem,
3236 gint start_year,
3237 gint start_month,
3238 gint start_day,
3239 gint end_year,
3240 gint end_month,
3241 gint end_day,
3242 guint8 day_style,
3243 gboolean add_day_style)
3244 {
3245 gint month_offset, end_month_offset, day;
3246
3247 month_offset = (start_year - calitem->year) * 12 + start_month
3248 - calitem->month;
3249 day = start_day;
3250 if (month_offset > calitem->rows * calitem->cols)
3251 return;
3252 if (month_offset < -1) {
3253 month_offset = -1;
3254 day = 1;
3255 }
3256
3257 end_month_offset = (end_year - calitem->year) * 12 + end_month
3258 - calitem->month;
3259 if (end_month_offset < -1)
3260 return;
3261 if (end_month_offset > calitem->rows * calitem->cols) {
3262 end_month_offset = calitem->rows * calitem->cols;
3263 end_day = 31;
3264 }
3265
3266 if (month_offset > end_month_offset)
3267 return;
3268
3269 if (!calitem->styles)
3270 calitem->styles = g_new0 (guint8, (calitem->rows * calitem->cols + 2) * 32);
3271
3272 for (;;) {
3273 gint index;
3274
3275 if (month_offset == end_month_offset && day > end_day)
3276 break;
3277
3278 if (month_offset < -1 || month_offset > calitem->rows * calitem->cols)
3279 g_warning ("Bad month offset: %i\n", month_offset);
3280 if (day < 1 || day > 31)
3281 g_warning ("Bad day: %i\n", day);
3282
3283 #if 0
3284 g_print ("Marking Month:%i Day:%i\n", month_offset, day);
3285 #endif
3286 index = (month_offset + 1) * 32 + day;
3287 calitem->styles[index] = day_style |
3288 (add_day_style ? calitem->styles[index] : 0);
3289
3290 day++;
3291 if (day == 32) {
3292 month_offset++;
3293 day = 1;
3294 if (month_offset > end_month_offset)
3295 break;
3296 }
3297 }
3298
3299 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
3300 }
3301
3302 /* Rounds up the given day to the end of the week. */
3303 static void
e_calendar_item_round_up_selection(ECalendarItem * calitem,gint * month_offset,gint * day)3304 e_calendar_item_round_up_selection (ECalendarItem *calitem,
3305 gint *month_offset,
3306 gint *day)
3307 {
3308 GDateWeekday weekday;
3309 gint year, month, days, days_in_month;
3310 struct tm tmp_tm = { 0 };
3311
3312 year = calitem->year;
3313 month = calitem->month + *month_offset;
3314 e_calendar_item_normalize_date (calitem, &year, &month);
3315
3316 tmp_tm.tm_year = year - 1900;
3317 tmp_tm.tm_mon = month;
3318 tmp_tm.tm_mday = *day;
3319 tmp_tm.tm_isdst = -1;
3320 mktime (&tmp_tm);
3321
3322 /* Calculate how many days to the end of the row. */
3323 weekday = e_weekday_from_tm_wday (tmp_tm.tm_wday);
3324 days = e_weekday_get_days_between (weekday, calitem->week_start_day);
3325
3326 *day += days;
3327 days_in_month = DAYS_IN_MONTH (year, month);
3328 if (*day > days_in_month) {
3329 (*month_offset)++;
3330 *day -= days_in_month;
3331 }
3332 }
3333
3334 /* Rounds down the given day to the start of the week. */
3335 static void
e_calendar_item_round_down_selection(ECalendarItem * calitem,gint * month_offset,gint * day)3336 e_calendar_item_round_down_selection (ECalendarItem *calitem,
3337 gint *month_offset,
3338 gint *day)
3339 {
3340 GDateWeekday weekday;
3341 gint year, month, days, days_in_month;
3342 struct tm tmp_tm = { 0 };
3343
3344 year = calitem->year;
3345 month = calitem->month + *month_offset;
3346 e_calendar_item_normalize_date (calitem, &year, &month);
3347
3348 tmp_tm.tm_year = year - 1900;
3349 tmp_tm.tm_mon = month;
3350 tmp_tm.tm_mday = *day;
3351 tmp_tm.tm_isdst = -1;
3352 mktime (&tmp_tm);
3353
3354 /* Calculate how many days to the start of the row. */
3355 weekday = e_weekday_from_tm_wday (tmp_tm.tm_wday);
3356 days = e_weekday_get_days_between (weekday, calitem->week_start_day);
3357
3358 *day -= days;
3359 if (*day <= 0) {
3360 month--;
3361 if (month == -1) {
3362 year--;
3363 month = 11;
3364 }
3365 days_in_month = DAYS_IN_MONTH (year, month);
3366 (*month_offset)--;
3367 *day += days_in_month;
3368 }
3369 }
3370
3371 static gint
e_calendar_item_get_inclusive_days(ECalendarItem * calitem,gint start_month_offset,gint start_day,gint end_month_offset,gint end_day)3372 e_calendar_item_get_inclusive_days (ECalendarItem *calitem,
3373 gint start_month_offset,
3374 gint start_day,
3375 gint end_month_offset,
3376 gint end_day)
3377 {
3378 gint start_year, start_month, end_year, end_month, days = 0;
3379
3380 start_year = calitem->year;
3381 start_month = calitem->month + start_month_offset;
3382 e_calendar_item_normalize_date (calitem, &start_year, &start_month);
3383
3384 end_year = calitem->year;
3385 end_month = calitem->month + end_month_offset;
3386 e_calendar_item_normalize_date (calitem, &end_year, &end_month);
3387
3388 while (start_year < end_year || start_month < end_month) {
3389 days += DAYS_IN_MONTH (start_year, start_month);
3390 start_month++;
3391 if (start_month == 12) {
3392 start_year++;
3393 start_month = 0;
3394 }
3395 }
3396
3397 days += end_day - start_day + 1;
3398
3399 return days;
3400 }
3401
3402 /* If the day is off the end of the month it is set to the last day of the
3403 * month. */
3404 static void
e_calendar_item_ensure_valid_day(ECalendarItem * calitem,gint * month_offset,gint * day)3405 e_calendar_item_ensure_valid_day (ECalendarItem *calitem,
3406 gint *month_offset,
3407 gint *day)
3408 {
3409 gint year, month, days_in_month;
3410
3411 year = calitem->year;
3412 month = calitem->month + *month_offset;
3413 e_calendar_item_normalize_date (calitem, &year, &month);
3414
3415 days_in_month = DAYS_IN_MONTH (year, month);
3416 if (*day > days_in_month)
3417 *day = days_in_month;
3418 }
3419
3420 gboolean
e_calendar_item_get_selection(ECalendarItem * calitem,GDate * start_date,GDate * end_date)3421 e_calendar_item_get_selection (ECalendarItem *calitem,
3422 GDate *start_date,
3423 GDate *end_date)
3424 {
3425 gint start_year, start_month, start_day;
3426 gint end_year, end_month, end_day;
3427
3428 g_date_clear (start_date, 1);
3429 g_date_clear (end_date, 1);
3430
3431 if (!calitem->selection_set)
3432 return FALSE;
3433
3434 start_year = calitem->year;
3435 start_month = calitem->month + calitem->selection_start_month_offset;
3436 e_calendar_item_normalize_date (calitem, &start_year, &start_month);
3437 start_day = calitem->selection_start_day;
3438
3439 end_year = calitem->year;
3440 end_month = calitem->month + calitem->selection_end_month_offset;
3441 e_calendar_item_normalize_date (calitem, &end_year, &end_month);
3442 end_day = calitem->selection_end_day;
3443
3444 g_date_set_dmy (start_date, start_day, start_month + 1, start_year);
3445 g_date_set_dmy (end_date, end_day, end_month + 1, end_year);
3446
3447 return TRUE;
3448 }
3449
3450 static void
e_calendar_item_set_selection_if_emission(ECalendarItem * calitem,const GDate * start_date,const GDate * end_date,gboolean emission)3451 e_calendar_item_set_selection_if_emission (ECalendarItem *calitem,
3452 const GDate *start_date,
3453 const GDate *end_date,
3454 gboolean emission)
3455 {
3456 gint start_year, start_month, start_day;
3457 gint end_year, end_month, end_day;
3458 gint new_start_month_offset, new_start_day;
3459 gint new_end_month_offset, new_end_day;
3460 gboolean need_update;
3461
3462 g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
3463
3464 /* If start_date is NULL, we clear the selection without changing the
3465 * month shown. */
3466 if (start_date == NULL) {
3467 calitem->selection_set = FALSE;
3468 calitem->selection_changed = TRUE;
3469 e_calendar_item_queue_signal_emission (calitem);
3470 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
3471 return;
3472 }
3473
3474 if (end_date == NULL)
3475 end_date = start_date;
3476
3477 g_return_if_fail (g_date_compare (start_date, end_date) <= 0);
3478
3479 start_year = g_date_get_year (start_date);
3480 start_month = g_date_get_month (start_date) - 1;
3481 start_day = g_date_get_day (start_date);
3482 end_year = g_date_get_year (end_date);
3483 end_month = g_date_get_month (end_date) - 1;
3484 end_day = g_date_get_day (end_date);
3485
3486 need_update = e_calendar_item_ensure_days_visible (
3487 calitem,
3488 start_year,
3489 start_month,
3490 start_day,
3491 end_year,
3492 end_month,
3493 end_day,
3494 emission);
3495
3496 new_start_month_offset = (start_year - calitem->year) * 12
3497 + start_month - calitem->month;
3498 new_start_day = start_day;
3499
3500 /* This may go outside the visible months, but we don't care. */
3501 new_end_month_offset = (end_year - calitem->year) * 12
3502 + end_month - calitem->month;
3503 new_end_day = end_day;
3504
3505 if (!calitem->selection_set
3506 || calitem->selection_start_month_offset != new_start_month_offset
3507 || calitem->selection_start_day != new_start_day
3508 || calitem->selection_end_month_offset != new_end_month_offset
3509 || calitem->selection_end_day != new_end_day) {
3510 need_update = TRUE;
3511 if (emission) {
3512 calitem->selection_changed = TRUE;
3513 e_calendar_item_queue_signal_emission (calitem);
3514 }
3515 calitem->selection_set = TRUE;
3516 calitem->selection_start_month_offset = new_start_month_offset;
3517 calitem->selection_start_day = new_start_day;
3518 calitem->selection_end_month_offset = new_end_month_offset;
3519 calitem->selection_end_day = new_end_day;
3520
3521 calitem->selection_real_start_month_offset = new_start_month_offset;
3522 calitem->selection_real_start_day = new_start_day;
3523 calitem->selection_from_full_week = FALSE;
3524 }
3525
3526 if (need_update) {
3527 g_signal_emit (
3528 calitem,
3529 e_calendar_item_signals[DATE_RANGE_CHANGED], 0);
3530 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
3531 }
3532 }
3533
3534 void
e_calendar_item_style_updated(GtkWidget * widget,ECalendarItem * calitem)3535 e_calendar_item_style_updated (GtkWidget *widget,
3536 ECalendarItem *calitem)
3537 {
3538 GdkRGBA unfocused_selected_bg, selected_bg, fg, base_bg;
3539
3540 e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &selected_bg);
3541 e_utils_get_theme_color (widget, "theme_unfocused_selected_bg_color,theme_selected_bg_color", E_UTILS_DEFAULT_THEME_UNFOCUSED_SELECTED_BG_COLOR, &unfocused_selected_bg);
3542 e_utils_get_theme_color (widget, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR, &fg);
3543 e_utils_get_theme_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &base_bg);
3544
3545 if (gdk_rgba_equal (&selected_bg, &unfocused_selected_bg))
3546 e_utils_get_theme_color (widget, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR, &selected_bg);
3547
3548 e_rgba_to_color (&selected_bg, &calitem->colors[E_CALENDAR_ITEM_COLOR_TODAY_BOX]);
3549 e_rgba_to_color (&base_bg, &calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_FG]);
3550 e_rgba_to_color (&unfocused_selected_bg, &calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_BG_FOCUSED]);
3551 e_rgba_to_color (&fg, &calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_BG]);
3552 e_rgba_to_color (&fg, &calitem->colors[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG]);
3553
3554 e_calendar_item_recalc_sizes (calitem);
3555 }
3556
3557 void
e_calendar_item_set_selection(ECalendarItem * calitem,const GDate * start_date,const GDate * end_date)3558 e_calendar_item_set_selection (ECalendarItem *calitem,
3559 const GDate *start_date,
3560 const GDate *end_date)
3561 {
3562 GDate current_start_date, current_end_date;
3563
3564 /* If the user is in the middle of a selection, we must abort it. */
3565 if (calitem->selecting) {
3566 gnome_canvas_item_ungrab (
3567 GNOME_CANVAS_ITEM (calitem),
3568 GDK_CURRENT_TIME);
3569 calitem->selecting = FALSE;
3570 }
3571
3572 if (e_calendar_item_get_selection (calitem, ¤t_start_date, ¤t_end_date)) {
3573 /* No change, no need to recalculate anything */
3574 if (start_date && end_date && g_date_valid (start_date) && g_date_valid (end_date) &&
3575 g_date_compare (start_date, ¤t_start_date) == 0 &&
3576 g_date_compare (end_date, ¤t_end_date) == 0)
3577 return;
3578 }
3579
3580 e_calendar_item_set_selection_if_emission (calitem,
3581 start_date, end_date,
3582 TRUE);
3583 }
3584
3585 /* This tries to ensure that the given time range is visible. If the range
3586 * given is longer than we can show, only the start of it will be visible.
3587 * Note that this will not update the selection. That should be done somewhere
3588 * else. It returns TRUE if the visible range has been changed. */
3589 static gboolean
e_calendar_item_ensure_days_visible(ECalendarItem * calitem,gint start_year,gint start_month,gint start_day,gint end_year,gint end_month,gint end_day,gboolean emission)3590 e_calendar_item_ensure_days_visible (ECalendarItem *calitem,
3591 gint start_year,
3592 gint start_month,
3593 gint start_day,
3594 gint end_year,
3595 gint end_month,
3596 gint end_day,
3597 gboolean emission)
3598 {
3599 gint current_end_year, current_end_month;
3600 gint months_shown;
3601 gint first_day_offset, days_in_month, days_in_prev_month;
3602 gboolean need_update = FALSE;
3603
3604 months_shown = calitem->rows * calitem->cols;
3605
3606 /* Calculate the range of months currently displayed. */
3607 current_end_year = calitem->year;
3608 current_end_month = calitem->month + months_shown - 1;
3609 e_calendar_item_normalize_date (
3610 calitem, ¤t_end_year,
3611 ¤t_end_month);
3612
3613 /* Try to ensure that the end month is shown. */
3614 if ((end_year == current_end_year + 1 &&
3615 current_end_month == 11 && end_month == 0) ||
3616 (end_year == current_end_year && end_month == current_end_month + 1)) {
3617 /* See if the end of the selection will fit in the
3618 * leftover days of the month after the last one shown. */
3619 calitem->month += (months_shown - 1);
3620 e_calendar_item_normalize_date (
3621 calitem, &calitem->year,
3622 &calitem->month);
3623
3624 e_calendar_item_get_month_info (
3625 calitem, 0, 0,
3626 &first_day_offset,
3627 &days_in_month,
3628 &days_in_prev_month);
3629
3630 if (end_day >= E_CALENDAR_ROWS_PER_MONTH * E_CALENDAR_COLS_PER_MONTH -
3631 first_day_offset - days_in_month) {
3632 need_update = TRUE;
3633
3634 calitem->year = end_year;
3635 calitem->month = end_month - months_shown + 1;
3636 } else {
3637 calitem->month -= (months_shown - 1);
3638 }
3639
3640 e_calendar_item_normalize_date (
3641 calitem, &calitem->year,
3642 &calitem->month);
3643 }
3644 else if (end_year > current_end_year ||
3645 (end_year == current_end_year && end_month > current_end_month)) {
3646 /* The selection will definitely not fit in the leftover days
3647 * of the month after the last one shown. */
3648 need_update = TRUE;
3649
3650 calitem->year = end_year;
3651 calitem->month = end_month - months_shown + 1;
3652
3653 e_calendar_item_normalize_date (
3654 calitem, &calitem->year,
3655 &calitem->month);
3656 }
3657
3658 /* Now try to ensure that the start month is shown. We do this after
3659 * the end month so that the start month will always be shown. */
3660 if (start_year < calitem->year
3661 || (start_year == calitem->year
3662 && start_month < calitem->month)) {
3663 need_update = TRUE;
3664
3665 /* First we see if the start of the selection will fit in the
3666 * leftover days of the month before the first one shown. */
3667 calitem->year = start_year;
3668 calitem->month = start_month + 1;
3669 e_calendar_item_normalize_date (
3670 calitem, &calitem->year,
3671 &calitem->month);
3672
3673 e_calendar_item_get_month_info (
3674 calitem, 0, 0,
3675 &first_day_offset,
3676 &days_in_month,
3677 &days_in_prev_month);
3678
3679 if (start_day <= days_in_prev_month - first_day_offset) {
3680 calitem->year = start_year;
3681 calitem->month = start_month;
3682 }
3683 }
3684
3685 if (need_update && emission)
3686 e_calendar_item_date_range_changed (calitem);
3687
3688 return need_update;
3689 }
3690
3691 static void
e_calendar_item_show_popup_menu(ECalendarItem * calitem,GdkEvent * button_event,gint month_offset)3692 e_calendar_item_show_popup_menu (ECalendarItem *calitem,
3693 GdkEvent *button_event,
3694 gint month_offset)
3695 {
3696 GtkWidget *menu, *submenu, *menuitem, *label;
3697 GtkWidget *canvas_widget;
3698 gint year, month;
3699 const gchar *name;
3700 gchar buffer[64];
3701
3702 menu = gtk_menu_new ();
3703
3704 for (year = calitem->year - 2; year <= calitem->year + 2; year++) {
3705 g_snprintf (buffer, 64, "%i", year);
3706 menuitem = gtk_menu_item_new_with_label (buffer);
3707 gtk_widget_show (menuitem);
3708 gtk_container_add (GTK_CONTAINER (menu), menuitem);
3709
3710 submenu = gtk_menu_new ();
3711 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
3712
3713 g_object_set_data (
3714 G_OBJECT (submenu), "year",
3715 GINT_TO_POINTER (year));
3716 g_object_set_data (
3717 G_OBJECT (submenu), "month_offset",
3718 GINT_TO_POINTER (month_offset));
3719
3720 for (month = 0; month < 12; month++) {
3721 name = e_get_month_name (month + 1, FALSE);
3722
3723 menuitem = gtk_menu_item_new ();
3724 gtk_widget_show (menuitem);
3725 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
3726
3727 label = gtk_label_new (name);
3728 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
3729 gtk_widget_show (label);
3730 gtk_container_add (GTK_CONTAINER (menuitem), label);
3731
3732 g_object_set_data (
3733 G_OBJECT (menuitem), "month",
3734 GINT_TO_POINTER (month));
3735
3736 g_signal_connect (
3737 menuitem, "activate",
3738 G_CALLBACK (e_calendar_item_on_menu_item_activate),
3739 calitem);
3740 }
3741 }
3742
3743 g_signal_connect (
3744 menu, "deactivate",
3745 G_CALLBACK (gtk_menu_detach), NULL);
3746
3747 canvas_widget = GTK_WIDGET (calitem->canvas_item.canvas);
3748 gtk_menu_attach_to_widget (GTK_MENU (menu), canvas_widget, NULL);
3749 gtk_menu_popup_at_pointer (GTK_MENU (menu), button_event);
3750 }
3751
3752 static void
e_calendar_item_on_menu_item_activate(GtkWidget * menuitem,ECalendarItem * calitem)3753 e_calendar_item_on_menu_item_activate (GtkWidget *menuitem,
3754 ECalendarItem *calitem)
3755 {
3756 GtkWidget *parent;
3757 gint year, month_offset, month;
3758 gpointer data;
3759
3760 parent = gtk_widget_get_parent (menuitem);
3761 data = g_object_get_data (G_OBJECT (parent), "year");
3762 year = GPOINTER_TO_INT (data);
3763
3764 parent = gtk_widget_get_parent (menuitem);
3765 data = g_object_get_data (G_OBJECT (parent), "month_offset");
3766 month_offset = GPOINTER_TO_INT (data);
3767
3768 data = g_object_get_data (G_OBJECT (menuitem), "month");
3769 month = GPOINTER_TO_INT (data);
3770
3771 month -= month_offset;
3772 e_calendar_item_normalize_date (calitem, &year, &month);
3773 e_calendar_item_set_first_month_with_emit (calitem, year, month, TRUE);
3774 }
3775
3776 /* Sets the function to call to get the colors to use for a particular day. */
3777 void
e_calendar_item_set_style_callback(ECalendarItem * calitem,ECalendarItemStyleCallback cb,gpointer data,GDestroyNotify destroy)3778 e_calendar_item_set_style_callback (ECalendarItem *calitem,
3779 ECalendarItemStyleCallback cb,
3780 gpointer data,
3781 GDestroyNotify destroy)
3782 {
3783 g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
3784
3785 if (calitem->style_callback_data && calitem->style_callback_destroy)
3786 (*calitem->style_callback_destroy) (calitem->style_callback_data);
3787
3788 calitem->style_callback = cb;
3789 calitem->style_callback_data = data;
3790 calitem->style_callback_destroy = destroy;
3791 }
3792
3793 static void
e_calendar_item_date_range_changed(ECalendarItem * calitem)3794 e_calendar_item_date_range_changed (ECalendarItem *calitem)
3795 {
3796 g_free (calitem->styles);
3797 calitem->styles = NULL;
3798 calitem->date_range_changed = TRUE;
3799 e_calendar_item_queue_signal_emission (calitem);
3800 }
3801
3802 static void
e_calendar_item_queue_signal_emission(ECalendarItem * calitem)3803 e_calendar_item_queue_signal_emission (ECalendarItem *calitem)
3804 {
3805 if (calitem->signal_emission_idle_id == 0) {
3806 calitem->signal_emission_idle_id = g_idle_add_full (
3807 G_PRIORITY_HIGH, (GSourceFunc)
3808 e_calendar_item_signal_emission_idle_cb,
3809 calitem, NULL);
3810 }
3811 }
3812
3813 static gboolean
e_calendar_item_signal_emission_idle_cb(gpointer data)3814 e_calendar_item_signal_emission_idle_cb (gpointer data)
3815 {
3816 ECalendarItem *calitem;
3817
3818 g_return_val_if_fail (E_IS_CALENDAR_ITEM (data), FALSE);
3819
3820 calitem = E_CALENDAR_ITEM (data);
3821
3822 calitem->signal_emission_idle_id = 0;
3823
3824 /* We ref the calitem & check in case it gets destroyed, since we
3825 * were getting a free memory write here. */
3826 g_object_ref ((calitem));
3827
3828 if (calitem->date_range_changed) {
3829 calitem->date_range_changed = FALSE;
3830 g_signal_emit (calitem, e_calendar_item_signals[DATE_RANGE_CHANGED], 0);
3831 }
3832
3833 if (calitem->selection_changed) {
3834 calitem->selection_changed = FALSE;
3835 g_signal_emit (calitem, e_calendar_item_signals[SELECTION_CHANGED], 0);
3836 }
3837
3838 g_object_unref ((calitem));
3839
3840 return FALSE;
3841 }
3842
3843 /* Sets a callback to use to get the current time. This is useful if the
3844 * application needs to use its own timezone data rather than rely on the
3845 * Unix timezone. */
3846 void
e_calendar_item_set_get_time_callback(ECalendarItem * calitem,ECalendarItemGetTimeCallback cb,gpointer data,GDestroyNotify destroy)3847 e_calendar_item_set_get_time_callback (ECalendarItem *calitem,
3848 ECalendarItemGetTimeCallback cb,
3849 gpointer data,
3850 GDestroyNotify destroy)
3851 {
3852 g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
3853
3854 if (calitem->time_callback_data && calitem->time_callback_destroy)
3855 (*calitem->time_callback_destroy) (calitem->time_callback_data);
3856
3857 calitem->time_callback = cb;
3858 calitem->time_callback_data = data;
3859 calitem->time_callback_destroy = destroy;
3860 }
3861