2 * ECalendarItem - canvas item displaying a calendar.
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.
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
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/>.
17 * Damon Chaplin <damon@ximian.com>
18 * Bolian Yin <bolian.yin@sun.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
23 #include "evolution-config.h"
25 #include <libebackend/libebackend.h>
27 #include "e-calendar-item.h"
33 #include <gdk/gdkkeysyms.h>
34 #include <glib/gi18n.h>
36 #include "ea-widgets.h"
37 #include "e-misc-utils.h"
38 #include "e-util-enumtypes.h"
40 static const gint e_calendar_item_days_in_month
[12] = {
41 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
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)
48 static void e_calendar_item_dispose (GObject
*object
);
49 static void e_calendar_item_get_property (GObject
*object
,
53 static void e_calendar_item_set_property (GObject
*object
,
57 static void e_calendar_item_realize (GnomeCanvasItem
*item
);
58 static void e_calendar_item_unmap (GnomeCanvasItem
*item
);
59 static void e_calendar_item_update (GnomeCanvasItem
*item
,
60 const cairo_matrix_t
*i2c
,
62 static void e_calendar_item_draw (GnomeCanvasItem
*item
,
68 static void e_calendar_item_draw_month (ECalendarItem
*calitem
,
76 static void e_calendar_item_draw_day_numbers
77 (ECalendarItem
*calitem
,
85 GDateWeekday start_weekday
,
88 static GnomeCanvasItem
*e_calendar_item_point (GnomeCanvasItem
*item
,
93 static void e_calendar_item_stop_selecting (ECalendarItem
*calitem
,
95 static void e_calendar_item_selection_add_days
96 (ECalendarItem
*calitem
,
98 gboolean multi_selection
);
99 static gint
e_calendar_item_key_press_event (ECalendarItem
*item
,
101 static gint
e_calendar_item_event (GnomeCanvasItem
*item
,
103 static void e_calendar_item_bounds (GnomeCanvasItem
*item
,
109 static gboolean
e_calendar_item_button_press (ECalendarItem
*calitem
,
111 static gboolean
e_calendar_item_button_release (ECalendarItem
*calitem
,
113 static gboolean
e_calendar_item_motion (ECalendarItem
*calitem
,
116 static gboolean e_calendar_item_convert_position_to_day
117 (ECalendarItem
*calitem
,
120 gboolean round_empty_positions
,
123 gboolean
*entire_week
);
124 static void e_calendar_item_get_month_info (ECalendarItem
*calitem
,
127 gint
*first_day_offset
,
129 gint
*days_in_prev_month
);
130 static void e_calendar_item_recalc_sizes (ECalendarItem
*calitem
);
132 static void e_calendar_item_get_day_style (ECalendarItem
*calitem
,
138 gboolean prev_or_next_month
,
141 gboolean drop_target
,
144 GdkColor
**box_color
,
147 GdkColor
*local_bg_color
,
148 GdkColor
*local_fg_color
);
149 static void e_calendar_item_check_selection_end
150 (ECalendarItem
*calitem
,
155 static void e_calendar_item_check_selection_start
156 (ECalendarItem
*calitem
,
161 static void e_calendar_item_add_days_to_selection
162 (ECalendarItem
*calitem
,
164 static void e_calendar_item_round_up_selection
165 (ECalendarItem
*calitem
,
168 static void e_calendar_item_round_down_selection
169 (ECalendarItem
*calitem
,
172 static gint e_calendar_item_get_inclusive_days
173 (ECalendarItem
*calitem
,
174 gint start_month_offset
,
176 gint end_month_offset
,
178 static void e_calendar_item_ensure_valid_day
179 (ECalendarItem
*calitem
,
182 static gboolean e_calendar_item_ensure_days_visible
183 (ECalendarItem
*calitem
,
191 static void e_calendar_item_show_popup_menu (ECalendarItem
*calitem
,
192 GdkEvent
*button_event
,
194 static void e_calendar_item_on_menu_item_activate
195 (GtkWidget
*menuitem
,
196 ECalendarItem
*calitem
);
197 static void e_calendar_item_date_range_changed
198 (ECalendarItem
*calitem
);
199 static void e_calendar_item_queue_signal_emission
200 (ECalendarItem
*calitem
);
201 static gboolean e_calendar_item_signal_emission_idle_cb
203 static void e_calendar_item_set_selection_if_emission
204 (ECalendarItem
*calitem
,
205 const GDate
*start_date
,
206 const GDate
*end_date
,
208 static void e_calendar_item_set_first_month_with_emit
209 (ECalendarItem
*calitem
,
212 gboolean emit_date_range_moved
);
224 PROP_WEEK_NUMBER_FONT
,
225 PROP_WEEK_NUMBER_FONT_DESC
,
229 PROP_MINIMUM_COLUMNS
,
231 PROP_MAXIMUM_COLUMNS
,
233 PROP_SHOW_WEEK_NUMBERS
,
234 PROP_KEEP_WDAYS_ON_WEEKNUM_CLICK
,
235 PROP_MAXIMUM_DAYS_SELECTED
,
236 PROP_DAYS_TO_START_WEEK_SELECTION
,
237 PROP_MOVE_SELECTION_WHEN_MOVING
,
238 PROP_PRESERVE_DAY_WHEN_MOVING
,
246 SELECTION_PREVIEW_CHANGED
,
251 static guint e_calendar_item_signals
[LAST_SIGNAL
] = { 0 };
253 G_DEFINE_TYPE_WITH_CODE (
256 GNOME_TYPE_CANVAS_ITEM
,
257 G_IMPLEMENT_INTERFACE (
258 E_TYPE_EXTENSIBLE
, NULL
))
261 e_calendar_item_class_init (ECalendarItemClass
*class)
263 GObjectClass
*object_class
;
264 GnomeCanvasItemClass
*item_class
;
266 object_class
= G_OBJECT_CLASS (class);
267 object_class
->dispose
= e_calendar_item_dispose
;
268 object_class
->get_property
= e_calendar_item_get_property
;
269 object_class
->set_property
= e_calendar_item_set_property
;
271 item_class
= GNOME_CANVAS_ITEM_CLASS (class);
272 item_class
->realize
= e_calendar_item_realize
;
273 item_class
->unmap
= e_calendar_item_unmap
;
274 item_class
->update
= e_calendar_item_update
;
275 item_class
->draw
= e_calendar_item_draw
;
276 item_class
->point
= e_calendar_item_point
;
277 item_class
->event
= e_calendar_item_event
;
278 item_class
->bounds
= e_calendar_item_bounds
;
280 class->date_range_changed
= NULL
;
281 class->selection_changed
= NULL
;
282 class->selection_preview_changed
= NULL
;
284 g_object_class_install_property (
296 g_object_class_install_property (
308 g_object_class_install_property (
311 g_param_spec_double (
320 g_object_class_install_property (
323 g_param_spec_double (
332 g_object_class_install_property (
335 g_param_spec_double (
344 g_object_class_install_property (
347 g_param_spec_double (
356 g_object_class_install_property (
363 PANGO_TYPE_FONT_DESCRIPTION
,
366 g_object_class_install_property (
368 PROP_WEEK_NUMBER_FONT_DESC
,
370 "week_number_font_desc",
373 PANGO_TYPE_FONT_DESCRIPTION
,
376 g_object_class_install_property (
388 g_object_class_install_property (
400 g_object_class_install_property (
412 g_object_class_install_property (
414 PROP_MINIMUM_COLUMNS
,
424 g_object_class_install_property (
436 g_object_class_install_property (
438 PROP_MAXIMUM_COLUMNS
,
448 g_object_class_install_property (
458 G_PARAM_STATIC_STRINGS
));
460 g_object_class_install_property (
462 PROP_SHOW_WEEK_NUMBERS
,
463 g_param_spec_boolean (
470 g_object_class_install_property (
472 PROP_KEEP_WDAYS_ON_WEEKNUM_CLICK
,
473 g_param_spec_boolean (
474 "keep_wdays_on_weeknum_click",
480 g_object_class_install_property (
482 PROP_MAXIMUM_DAYS_SELECTED
,
484 "maximum_days_selected",
492 g_object_class_install_property (
494 PROP_DAYS_TO_START_WEEK_SELECTION
,
496 "days_to_start_week_selection",
504 g_object_class_install_property (
506 PROP_MOVE_SELECTION_WHEN_MOVING
,
507 g_param_spec_boolean (
508 "move_selection_when_moving",
514 g_object_class_install_property (
516 PROP_PRESERVE_DAY_WHEN_MOVING
,
517 g_param_spec_boolean (
518 "preserve_day_when_moving",
524 g_object_class_install_property (
527 g_param_spec_boolean (
534 e_calendar_item_signals
[DATE_RANGE_CHANGED
] = g_signal_new (
535 "date_range_changed",
536 G_TYPE_FROM_CLASS (object_class
),
538 G_STRUCT_OFFSET (ECalendarItemClass
, date_range_changed
),
540 g_cclosure_marshal_VOID__VOID
,
543 /* Invoked when a user changes date range, by pressing month/year
544 arrows or any similar way, but not when selecting a day in the calendar. */
545 e_calendar_item_signals
[DATE_RANGE_MOVED
] = g_signal_new (
547 G_TYPE_FROM_CLASS (object_class
),
550 g_cclosure_marshal_VOID__VOID
,
553 e_calendar_item_signals
[SELECTION_CHANGED
] = g_signal_new (
555 G_TYPE_FROM_CLASS (object_class
),
557 G_STRUCT_OFFSET (ECalendarItemClass
, selection_changed
),
559 g_cclosure_marshal_VOID__VOID
,
562 e_calendar_item_signals
[SELECTION_PREVIEW_CHANGED
] = g_signal_new (
563 "selection_preview_changed",
564 G_TYPE_FROM_CLASS (object_class
),
566 G_STRUCT_OFFSET (ECalendarItemClass
, selection_preview_changed
),
568 g_cclosure_marshal_VOID__VOID
,
571 e_calendar_item_signals
[MONTH_WIDTH_CHANGED
] = g_signal_new (
572 "month-width-changed",
573 G_TYPE_FROM_CLASS (object_class
),
575 0 /* G_STRUCT_OFFSET (ECalendarItemClass, month_width_changed) */,
577 g_cclosure_marshal_VOID__VOID
,
580 e_calendar_item_a11y_init ();
584 e_calendar_item_init (ECalendarItem
*calitem
)
589 /* Set the default time to the current month. */
591 tmp_tm
= localtime (&t
);
592 calitem
->year
= tmp_tm
->tm_year
+ 1900;
593 calitem
->month
= tmp_tm
->tm_mon
;
595 calitem
->styles
= NULL
;
597 calitem
->min_cols
= 1;
598 calitem
->min_rows
= 1;
599 calitem
->max_cols
= -1;
600 calitem
->max_rows
= -1;
605 calitem
->show_week_numbers
= FALSE
;
606 calitem
->keep_wdays_on_weeknum_click
= FALSE
;
607 calitem
->week_start_day
= G_DATE_MONDAY
;
608 calitem
->expand
= TRUE
;
609 calitem
->max_days_selected
= 1;
610 calitem
->days_to_start_week_selection
= -1;
611 calitem
->move_selection_when_moving
= TRUE
;
612 calitem
->preserve_day_when_moving
= FALSE
;
613 calitem
->display_popup
= TRUE
;
620 calitem
->selecting
= FALSE
;
621 calitem
->selecting_axis
= NULL
;
623 calitem
->selection_set
= FALSE
;
625 calitem
->selection_changed
= FALSE
;
626 calitem
->date_range_changed
= FALSE
;
628 calitem
->style_callback
= NULL
;
629 calitem
->style_callback_data
= NULL
;
630 calitem
->style_callback_destroy
= NULL
;
632 calitem
->time_callback
= NULL
;
633 calitem
->time_callback_data
= NULL
;
634 calitem
->time_callback_destroy
= NULL
;
636 calitem
->signal_emission_idle_id
= 0;
640 e_calendar_item_dispose (GObject
*object
)
642 ECalendarItem
*calitem
;
644 calitem
= E_CALENDAR_ITEM (object
);
646 e_calendar_item_set_style_callback (calitem
, NULL
, NULL
, NULL
);
647 e_calendar_item_set_get_time_callback (calitem
, NULL
, NULL
, NULL
);
649 if (calitem
->styles
) {
650 g_free (calitem
->styles
);
651 calitem
->styles
= NULL
;
654 if (calitem
->signal_emission_idle_id
> 0) {
655 g_source_remove (calitem
->signal_emission_idle_id
);
656 calitem
->signal_emission_idle_id
= -1;
659 if (calitem
->font_desc
) {
660 pango_font_description_free (calitem
->font_desc
);
661 calitem
->font_desc
= NULL
;
664 if (calitem
->week_number_font_desc
) {
665 pango_font_description_free (calitem
->week_number_font_desc
);
666 calitem
->week_number_font_desc
= NULL
;
669 if (calitem
->selecting_axis
)
670 g_free (calitem
->selecting_axis
);
672 G_OBJECT_CLASS (e_calendar_item_parent_class
)->dispose (object
);
676 e_calendar_item_get_property (GObject
*object
,
681 ECalendarItem
*calitem
;
683 calitem
= E_CALENDAR_ITEM (object
);
685 switch (property_id
) {
687 g_value_set_int (value
, calitem
->year
);
690 g_value_set_int (value
, calitem
->month
);
693 g_value_set_double (value
, calitem
->x1
);
696 g_value_set_double (value
, calitem
->y1
);
699 g_value_set_double (value
, calitem
->x2
);
702 g_value_set_double (value
, calitem
->y2
);
705 g_value_set_boxed (value
, calitem
->font_desc
);
707 case PROP_WEEK_NUMBER_FONT_DESC
:
708 g_value_set_boxed (value
, calitem
->week_number_font_desc
);
710 case PROP_ROW_HEIGHT
:
711 e_calendar_item_recalc_sizes (calitem
);
712 g_value_set_int (value
, calitem
->min_month_height
);
714 case PROP_COLUMN_WIDTH
:
715 e_calendar_item_recalc_sizes (calitem
);
716 g_value_set_int (value
, calitem
->min_month_width
);
718 case PROP_MINIMUM_ROWS
:
719 g_value_set_int (value
, calitem
->min_rows
);
721 case PROP_MINIMUM_COLUMNS
:
722 g_value_set_int (value
, calitem
->min_cols
);
724 case PROP_MAXIMUM_ROWS
:
725 g_value_set_int (value
, calitem
->max_rows
);
727 case PROP_MAXIMUM_COLUMNS
:
728 g_value_set_int (value
, calitem
->max_cols
);
730 case PROP_WEEK_START_DAY
:
731 g_value_set_enum (value
, calitem
->week_start_day
);
733 case PROP_SHOW_WEEK_NUMBERS
:
734 g_value_set_boolean (value
, calitem
->show_week_numbers
);
736 case PROP_KEEP_WDAYS_ON_WEEKNUM_CLICK
:
737 g_value_set_boolean (value
, calitem
->keep_wdays_on_weeknum_click
);
739 case PROP_MAXIMUM_DAYS_SELECTED
:
740 g_value_set_int (value
, e_calendar_item_get_max_days_sel (calitem
));
742 case PROP_DAYS_TO_START_WEEK_SELECTION
:
743 g_value_set_int (value
, e_calendar_item_get_days_start_week_sel (calitem
));
745 case PROP_MOVE_SELECTION_WHEN_MOVING
:
746 g_value_set_boolean (value
, calitem
->move_selection_when_moving
);
748 case PROP_PRESERVE_DAY_WHEN_MOVING
:
749 g_value_set_boolean (value
, calitem
->preserve_day_when_moving
);
751 case PROP_DISPLAY_POPUP
:
752 g_value_set_boolean (value
, e_calendar_item_get_display_popup (calitem
));
756 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
760 e_calendar_item_set_property (GObject
*object
,
765 GnomeCanvasItem
*item
;
766 ECalendarItem
*calitem
;
767 PangoFontDescription
*font_desc
;
772 item
= GNOME_CANVAS_ITEM (object
);
773 calitem
= E_CALENDAR_ITEM (object
);
775 switch (property_id
) {
777 ivalue
= g_value_get_int (value
);
778 e_calendar_item_set_first_month (
779 calitem
, ivalue
, calitem
->month
);
782 ivalue
= g_value_get_int (value
);
783 e_calendar_item_set_first_month (
784 calitem
, calitem
->year
, ivalue
);
787 dvalue
= g_value_get_double (value
);
788 if (calitem
->x1
!= dvalue
) {
789 calitem
->x1
= dvalue
;
790 gnome_canvas_item_request_update (item
);
794 dvalue
= g_value_get_double (value
);
795 if (calitem
->y1
!= dvalue
) {
796 calitem
->y1
= dvalue
;
797 gnome_canvas_item_request_update (item
);
801 dvalue
= g_value_get_double (value
);
802 if (calitem
->x2
!= dvalue
) {
803 calitem
->x2
= dvalue
;
804 gnome_canvas_item_request_update (item
);
808 dvalue
= g_value_get_double (value
);
809 if (calitem
->y2
!= dvalue
) {
810 calitem
->y2
= dvalue
;
811 gnome_canvas_item_request_update (item
);
815 font_desc
= g_value_get_boxed (value
);
816 if (calitem
->font_desc
)
817 pango_font_description_free (calitem
->font_desc
);
818 calitem
->font_desc
= pango_font_description_copy (font_desc
);
819 gnome_canvas_item_request_update (item
);
821 case PROP_WEEK_NUMBER_FONT_DESC
:
822 font_desc
= g_value_get_boxed (value
);
823 if (calitem
->week_number_font_desc
)
824 pango_font_description_free (calitem
->week_number_font_desc
);
825 calitem
->week_number_font_desc
= pango_font_description_copy (font_desc
);
826 gnome_canvas_item_request_update (item
);
828 case PROP_MINIMUM_ROWS
:
829 ivalue
= g_value_get_int (value
);
830 ivalue
= MAX (1, ivalue
);
831 if (calitem
->min_rows
!= ivalue
) {
832 calitem
->min_rows
= ivalue
;
833 gnome_canvas_item_request_update (item
);
836 case PROP_MINIMUM_COLUMNS
:
837 ivalue
= g_value_get_int (value
);
838 ivalue
= MAX (1, ivalue
);
839 if (calitem
->min_cols
!= ivalue
) {
840 calitem
->min_cols
= ivalue
;
841 gnome_canvas_item_request_update (item
);
844 case PROP_MAXIMUM_ROWS
:
845 ivalue
= g_value_get_int (value
);
846 if (calitem
->max_rows
!= ivalue
) {
847 calitem
->max_rows
= ivalue
;
848 gnome_canvas_item_request_update (item
);
851 case PROP_MAXIMUM_COLUMNS
:
852 ivalue
= g_value_get_int (value
);
853 if (calitem
->max_cols
!= ivalue
) {
854 calitem
->max_cols
= ivalue
;
855 gnome_canvas_item_request_update (item
);
858 case PROP_WEEK_START_DAY
:
859 ivalue
= g_value_get_enum (value
);
860 if (calitem
->week_start_day
!= ivalue
) {
861 calitem
->week_start_day
= ivalue
;
862 gnome_canvas_item_request_update (item
);
865 case PROP_SHOW_WEEK_NUMBERS
:
866 bvalue
= g_value_get_boolean (value
);
867 if (calitem
->show_week_numbers
!= bvalue
) {
868 calitem
->show_week_numbers
= bvalue
;
869 gnome_canvas_item_request_update (item
);
872 case PROP_KEEP_WDAYS_ON_WEEKNUM_CLICK
:
873 calitem
->keep_wdays_on_weeknum_click
= g_value_get_boolean (value
);
875 case PROP_MAXIMUM_DAYS_SELECTED
:
876 ivalue
= g_value_get_int (value
);
877 e_calendar_item_set_max_days_sel (calitem
, ivalue
);
879 case PROP_DAYS_TO_START_WEEK_SELECTION
:
880 ivalue
= g_value_get_int (value
);
881 e_calendar_item_set_days_start_week_sel (calitem
, ivalue
);
883 case PROP_MOVE_SELECTION_WHEN_MOVING
:
884 bvalue
= g_value_get_boolean (value
);
885 calitem
->move_selection_when_moving
= bvalue
;
887 case PROP_PRESERVE_DAY_WHEN_MOVING
:
888 bvalue
= g_value_get_boolean (value
);
889 calitem
->preserve_day_when_moving
= bvalue
;
891 case PROP_DISPLAY_POPUP
:
892 bvalue
= g_value_get_boolean (value
);
893 e_calendar_item_set_display_popup (calitem
, bvalue
);
897 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
901 e_calendar_item_realize (GnomeCanvasItem
*item
)
903 ECalendarItem
*calitem
;
905 if (GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class
)->realize
)
906 (* GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class
)->realize
) (item
);
908 calitem
= E_CALENDAR_ITEM (item
);
910 e_calendar_item_style_updated (GTK_WIDGET (item
->canvas
), calitem
);
912 e_extensible_load_extensions (E_EXTENSIBLE (calitem
));
916 e_calendar_item_unmap (GnomeCanvasItem
*item
)
918 ECalendarItem
*calitem
;
920 calitem
= E_CALENDAR_ITEM (item
);
922 if (calitem
->selecting
) {
923 gnome_canvas_item_ungrab (item
, GDK_CURRENT_TIME
);
924 calitem
->selecting
= FALSE
;
927 if (GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class
)->unmap
)
928 (* GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class
)->unmap
) (item
);
932 e_calendar_item_update (GnomeCanvasItem
*item
,
933 const cairo_matrix_t
*i2c
,
936 GnomeCanvasItemClass
*item_class
;
937 ECalendarItem
*calitem
;
938 gint char_height
, width
, height
, space
, space_per_cal
, space_per_cell
;
939 gint rows
, cols
, xthickness
, ythickness
, old_month_width
;
940 PangoContext
*pango_context
;
941 PangoFontMetrics
*font_metrics
;
942 GtkStyleContext
*style_context
;
945 item_class
= GNOME_CANVAS_ITEM_CLASS (e_calendar_item_parent_class
);
946 if (item_class
->update
!= NULL
)
947 item_class
->update (item
, i2c
, flags
);
949 calitem
= E_CALENDAR_ITEM (item
);
950 style_context
= gtk_widget_get_style_context (GTK_WIDGET (item
->canvas
));
951 gtk_style_context_get_padding (style_context
, gtk_style_context_get_state (style_context
), &padding
);
952 xthickness
= padding
.left
;
953 ythickness
= padding
.top
;
955 item
->x1
= calitem
->x1
;
956 item
->y1
= calitem
->y1
;
957 item
->x2
= calitem
->x2
>= calitem
->x1
? calitem
->x2
: calitem
->x1
;
958 item
->y2
= calitem
->y2
>= calitem
->y1
? calitem
->y2
: calitem
->y1
;
960 /* Set up Pango prerequisites */
961 pango_context
= gtk_widget_get_pango_context (GTK_WIDGET (item
->canvas
));
962 font_metrics
= pango_context_get_metrics (
964 pango_context_get_language (pango_context
));
967 * Calculate the new layout of the calendar.
970 /* Make sure the minimum row width & cell height and the widths of
971 * all the digits and characters are up to date. */
972 e_calendar_item_recalc_sizes (calitem
);
974 /* Calculate how many rows & cols we can fit in. */
975 width
= item
->x2
- item
->x1
;
976 height
= item
->y2
- item
->y1
;
978 width
-= xthickness
* 2;
979 height
-= ythickness
* 2;
981 if (calitem
->min_month_height
== 0)
984 rows
= height
/ calitem
->min_month_height
;
985 rows
= MAX (rows
, calitem
->min_rows
);
986 if (calitem
->max_rows
> 0)
987 rows
= MIN (rows
, calitem
->max_rows
);
989 if (calitem
->min_month_width
== 0)
992 cols
= width
/ calitem
->min_month_width
;
993 cols
= MAX (cols
, calitem
->min_cols
);
994 if (calitem
->max_cols
> 0)
995 cols
= MIN (cols
, calitem
->max_cols
);
997 if (rows
!= calitem
->rows
|| cols
!= calitem
->cols
)
998 e_calendar_item_date_range_changed (calitem
);
1000 calitem
->rows
= rows
;
1001 calitem
->cols
= cols
;
1003 /* Split up the empty space according to the configuration.
1004 * If the calendar is set to expand, we divide the space between the
1005 * cells and the spaces around the calendar, otherwise we place the
1006 * calendars in the center of the available area. */
1009 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics
)) +
1010 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics
));
1012 old_month_width
= calitem
->month_width
;
1013 calitem
->month_width
= calitem
->min_month_width
;
1014 calitem
->month_height
= calitem
->min_month_height
;
1015 calitem
->cell_width
= MAX (calitem
->max_day_width
, (calitem
->max_digit_width
* 2))
1016 + E_CALENDAR_ITEM_MIN_CELL_XPAD
;
1017 calitem
->cell_height
= char_height
1018 + E_CALENDAR_ITEM_MIN_CELL_YPAD
;
1019 calitem
->month_tpad
= 0;
1020 calitem
->month_bpad
= 0;
1021 calitem
->month_lpad
= 0;
1022 calitem
->month_rpad
= 0;
1024 space
= height
- calitem
->rows
* calitem
->month_height
;
1026 space_per_cal
= space
/ calitem
->rows
;
1027 calitem
->month_height
+= space_per_cal
;
1029 if (calitem
->expand
) {
1030 space_per_cell
= space_per_cal
/ E_CALENDAR_ROWS_PER_MONTH
;
1031 calitem
->cell_height
+= space_per_cell
;
1032 space_per_cal
-= space_per_cell
* E_CALENDAR_ROWS_PER_MONTH
;
1035 calitem
->month_tpad
= space_per_cal
/ 2;
1036 calitem
->month_bpad
= space_per_cal
- calitem
->month_tpad
;
1039 space
= width
- calitem
->cols
* calitem
->month_width
;
1041 space_per_cal
= space
/ calitem
->cols
;
1042 calitem
->month_width
+= space_per_cal
;
1043 space
-= space_per_cal
* calitem
->cols
;
1045 if (calitem
->expand
) {
1046 space_per_cell
= space_per_cal
/ E_CALENDAR_COLS_PER_MONTH
;
1047 calitem
->cell_width
+= space_per_cell
;
1048 space_per_cal
-= space_per_cell
* E_CALENDAR_COLS_PER_MONTH
;
1051 calitem
->month_lpad
= space_per_cal
/ 2;
1052 calitem
->month_rpad
= space_per_cal
- calitem
->month_lpad
;
1055 space
= MAX (0, space
);
1056 calitem
->x_offset
= space
/ 2;
1058 gnome_canvas_request_redraw (
1059 item
->canvas
, item
->x1
, item
->y1
,
1060 item
->x2
, item
->y2
);
1062 pango_font_metrics_unref (font_metrics
);
1064 if (old_month_width
!= calitem
->month_width
) {
1065 g_signal_emit (calitem
, e_calendar_item_signals
[MONTH_WIDTH_CHANGED
], 0, NULL
);
1070 * DRAWING ROUTINES - functions to paint the canvas item.
1073 e_calendar_item_draw (GnomeCanvasItem
*canvas_item
,
1080 ECalendarItem
*calitem
;
1082 GtkStyleContext
*style_context
;
1083 gint char_height
, row
, col
, row_y
, bar_height
;
1084 PangoContext
*pango_context
;
1085 PangoFontMetrics
*font_metrics
;
1091 "In e_calendar_item_draw %i,%i %ix%i\n",
1092 x
, y
, width
, height
);
1094 calitem
= E_CALENDAR_ITEM (canvas_item
);
1096 widget
= GTK_WIDGET (canvas_item
->canvas
);
1097 style_context
= gtk_widget_get_style_context (widget
);
1099 /* Set up Pango prerequisites */
1100 pango_context
= gtk_widget_get_pango_context (
1101 GTK_WIDGET (canvas_item
->canvas
));
1102 /* It's OK when the calitem->font_desc is NUL, then the currently set font is used */
1103 font_metrics
= pango_context_get_metrics (
1104 pango_context
, calitem
->font_desc
,
1105 pango_context_get_language (pango_context
));
1108 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics
)) +
1109 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics
));
1111 e_utils_get_theme_color (widget
, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR
, &bg_color
);
1113 gtk_style_context_get_border (style_context
, gtk_style_context_get_state (style_context
), &border
);
1115 /* Clear the entire background. */
1117 gdk_cairo_set_source_rgba (cr
, &bg_color
);
1119 cr
, calitem
->x1
- x
, calitem
->y1
- y
,
1120 calitem
->x2
- calitem
->x1
+ 1,
1121 calitem
->y2
- calitem
->y1
+ 1);
1125 row_y
= canvas_item
->y1
+ border
.top
;
1127 border
.top
+ border
.bottom
+
1128 E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
+ char_height
+
1129 E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
;
1131 for (row
= 0; row
< calitem
->rows
; row
++) {
1132 /* Draw the background for the title bars and the shadow around
1133 * it, and the vertical lines between columns. */
1136 gdk_cairo_set_source_rgba (cr
, &bg_color
);
1138 cr
, calitem
->x1
+ border
.left
- x
,
1140 calitem
->x2
- calitem
->x1
+ 1 -
1141 (border
.left
+ border
.right
),
1146 gtk_style_context_save (style_context
);
1147 gtk_style_context_add_class (
1148 style_context
, GTK_STYLE_CLASS_HEADER
);
1152 (gdouble
) calitem
->x1
+ border
.left
- x
,
1153 (gdouble
) row_y
- y
,
1154 (gdouble
) calitem
->x2
- calitem
->x1
+ 1 -
1155 (border
.left
+ border
.right
),
1156 (gdouble
) bar_height
);
1158 gtk_style_context_restore (style_context
);
1160 for (col
= 0; col
< calitem
->cols
; col
++) {
1161 e_calendar_item_draw_month (
1163 width
, height
, row
, col
);
1166 row_y
+= calitem
->month_height
;
1169 /* Draw the shadow around the entire item. */
1170 gtk_style_context_save (style_context
);
1171 gtk_style_context_add_class (
1172 style_context
, GTK_STYLE_CLASS_ENTRY
);
1176 (gdouble
) calitem
->x1
- x
,
1177 (gdouble
) calitem
->y1
- y
,
1178 (gdouble
) calitem
->x2
- calitem
->x1
+ 1,
1179 (gdouble
) calitem
->y2
- calitem
->y1
+ 1);
1181 gtk_style_context_restore (style_context
);
1183 pango_font_metrics_unref (font_metrics
);
1187 layout_set_day_text (ECalendarItem
*calitem
,
1188 PangoLayout
*layout
,
1189 GDateWeekday weekday
)
1191 const gchar
*abbr_name
;
1193 abbr_name
= e_get_weekday_name (weekday
, TRUE
);
1194 pango_layout_set_text (layout
, abbr_name
, -1);
1198 e_calendar_item_draw_month (ECalendarItem
*calitem
,
1207 GnomeCanvasItem
*item
;
1210 GdkRectangle clip_rect
;
1211 GDateWeekday start_weekday
;
1212 gint char_height
, xthickness
, ythickness
;
1214 gint month_x
, month_y
, month_w
, month_h
;
1215 gint min_x
, max_x
, text_x
, text_y
;
1216 gint day
, cells_x
, cells_y
, min_cell_width
, text_width
, arrow_button_size
;
1217 gint clip_width
, clip_height
;
1219 GDateWeekday weekday
;
1220 PangoContext
*pango_context
;
1221 PangoFontMetrics
*font_metrics
;
1222 PangoLayout
*layout
;
1223 GtkStyleContext
*style_context
;
1225 PangoFontDescription
*font_desc
;
1230 "In e_calendar_item_draw_month: %i,%i %ix%i row:%i col:%i\n",
1231 x
, y
, width
, height
, row
, col
);
1233 item
= GNOME_CANVAS_ITEM (calitem
);
1234 widget
= GTK_WIDGET (item
->canvas
);
1236 /* Set up Pango prerequisites */
1237 font_desc
= calitem
->font_desc
;
1238 pango_context
= gtk_widget_get_pango_context (widget
);
1239 font_metrics
= pango_context_get_metrics (
1240 pango_context
, font_desc
,
1241 pango_context_get_language (pango_context
));
1243 font_desc
= pango_context_get_font_description (pango_context
);
1244 font_desc
= pango_font_description_copy (font_desc
);
1247 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics
)) +
1248 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics
));
1249 style_context
= gtk_widget_get_style_context (widget
);
1250 gtk_style_context_get_padding (style_context
, gtk_style_context_get_state (style_context
), &padding
);
1251 xthickness
= padding
.left
;
1252 ythickness
= padding
.top
;
1254 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics
))
1255 + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics
))
1256 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
1257 + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
1260 pango_font_metrics_unref (font_metrics
);
1262 /* Calculate the top-left position of the entire month display. */
1263 month_x
= item
->x1
+ xthickness
+ calitem
->x_offset
1264 + ((gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
)
1265 ? (calitem
->cols
- 1 - col
) : col
) * calitem
->month_width
- x
;
1266 month_w
= item
->x2
- item
->x1
- xthickness
* 2;
1267 month_w
= MIN (month_w
, calitem
->month_width
);
1268 month_y
= item
->y1
+ ythickness
+ row
* calitem
->month_height
- y
;
1269 month_h
= item
->y2
- item
->y1
- ythickness
* 2;
1270 month_h
= MIN (month_h
, calitem
->month_height
);
1272 /* Just return if the month is outside the given area. */
1273 if (month_x
>= width
|| month_x
+ calitem
->month_width
<= 0
1274 || month_y
>= height
|| month_y
+ calitem
->month_height
<= 0) {
1275 pango_font_description_free (font_desc
);
1279 month
= calitem
->month
+ row
* calitem
->cols
+ col
;
1280 year
= calitem
->year
+ month
/ 12;
1283 /* Draw the month name & year, with clipping. Note that the top row
1284 * needs extra space around it for the buttons. */
1286 layout
= gtk_widget_create_pango_layout (widget
, NULL
);
1288 if (row
== 0 && col
== 0)
1289 min_x
= E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME_WITH_BUTTON
;
1291 min_x
= E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME
;
1294 if (row
== 0 && col
== 0)
1295 max_x
-= E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME_WITH_BUTTON
;
1297 max_x
-= E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME
;
1299 text_y
= month_y
+ padding
.top
1300 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
;
1301 clip_rect
.x
= month_x
+ min_x
;
1302 clip_rect
.x
= MAX (0, clip_rect
.x
);
1303 clip_rect
.y
= MAX (0, text_y
);
1305 memset (&tmp_tm
, 0, sizeof (tmp_tm
));
1306 tmp_tm
.tm_year
= year
- 1900;
1307 tmp_tm
.tm_mon
= month
;
1309 tmp_tm
.tm_isdst
= -1;
1312 start_weekday
= e_weekday_from_tm_wday (tmp_tm
.tm_wday
);
1314 if (month_x
+ max_x
- clip_rect
.x
> 0) {
1317 clip_rect
.width
= month_x
+ max_x
- clip_rect
.x
;
1318 clip_rect
.height
= text_y
+ char_height
- clip_rect
.y
;
1319 gdk_cairo_rectangle (cr
, &clip_rect
);
1322 e_utils_get_theme_color (widget
, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR
, &rgba
);
1323 gdk_cairo_set_source_rgba (cr
, &rgba
);
1325 if (row
== 0 && col
== 0) {
1326 PangoLayout
*layout_yr
;
1327 gchar buffer_yr
[64];
1330 layout_yr
= gtk_widget_create_pango_layout (widget
, NULL
);
1332 /* This is a strftime() format. %B = Month name. */
1333 e_utf8_strftime (buffer
, sizeof (buffer
), C_("CalItem", "%B"), &tmp_tm
);
1334 /* This is a strftime() format. %Y = Year. */
1335 e_utf8_strftime (buffer_yr
, sizeof (buffer_yr
), C_("CalItem", "%Y"), &tmp_tm
);
1337 pango_layout_set_font_description (layout
, font_desc
);
1338 pango_layout_set_text (layout
, buffer
, -1);
1340 pango_layout_set_font_description (layout_yr
, font_desc
);
1341 pango_layout_set_text (layout_yr
, buffer_yr
, -1);
1343 if (gtk_widget_get_direction (widget
) != GTK_TEXT_DIR_RTL
) {
1344 max_width
= calitem
->max_month_name_width
;
1345 pango_layout_get_pixel_size (layout
, &text_width
, NULL
);
1347 cairo_move_to (cr
, month_x
+ min_x
+ arrow_button_size
+ (max_width
- text_width
) / 2, text_y
);
1348 pango_cairo_show_layout (cr
, layout
);
1350 max_width
= calitem
->max_digit_width
* 5;
1351 pango_layout_get_pixel_size (layout_yr
, &text_width
, NULL
);
1353 cairo_move_to (cr
, month_x
+ month_w
- arrow_button_size
- (max_width
- text_width
) / 2 - text_width
- min_x
, text_y
);
1354 pango_cairo_show_layout (cr
, layout_yr
);
1356 max_width
= calitem
->max_digit_width
* 5;
1357 pango_layout_get_pixel_size (layout_yr
, &text_width
, NULL
);
1359 cairo_move_to (cr
, month_x
+ min_x
+ arrow_button_size
+ (max_width
- text_width
) / 2, text_y
);
1360 pango_cairo_show_layout (cr
, layout_yr
);
1362 max_width
= calitem
->max_month_name_width
;
1363 pango_layout_get_pixel_size (layout
, &text_width
, NULL
);
1365 cairo_move_to (cr
, month_x
+ month_w
- arrow_button_size
- (max_width
- text_width
) / 2 - text_width
- min_x
, text_y
);
1366 pango_cairo_show_layout (cr
, layout
);
1369 g_object_unref (layout_yr
);
1371 /* This is a strftime() format. %B = Month name, %Y = Year. */
1372 e_utf8_strftime (buffer
, sizeof (buffer
), C_("CalItem", "%B %Y"), &tmp_tm
);
1374 pango_layout_set_font_description (layout
, font_desc
);
1375 pango_layout_set_text (layout
, buffer
, -1);
1377 /* Ideally we place the text centered in the month, but we
1378 * won't go to the left of the minimum x position. */
1379 pango_layout_get_pixel_size (layout
, &text_width
, NULL
);
1380 text_x
= (calitem
->month_width
- text_width
) / 2;
1381 text_x
= MAX (min_x
, text_x
);
1383 cairo_move_to (cr
, month_x
+ text_x
, text_y
);
1384 pango_cairo_show_layout (cr
, layout
);
1390 /* Set the clip rectangle for the main month display. */
1391 clip_rect
.x
= MAX (0, month_x
);
1392 clip_rect
.y
= MAX (0, month_y
);
1393 clip_width
= month_x
+ month_w
- clip_rect
.x
;
1394 clip_height
= month_y
+ month_h
- clip_rect
.y
;
1396 if (clip_width
<= 0 || clip_height
<= 0) {
1397 g_object_unref (layout
);
1398 pango_font_description_free (font_desc
);
1402 clip_rect
.width
= clip_width
;
1403 clip_rect
.height
= clip_height
;
1407 gdk_cairo_rectangle (cr
, &clip_rect
);
1410 /* Draw the day initials across the top of the month. */
1411 min_cell_width
= MAX (calitem
->max_day_width
, (calitem
->max_digit_width
* 2))
1412 + E_CALENDAR_ITEM_MIN_CELL_XPAD
;
1414 cells_x
= month_x
+ E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS
+ calitem
->month_lpad
1415 + E_CALENDAR_ITEM_XPAD_BEFORE_CELLS
;
1416 if (calitem
->show_week_numbers
)
1417 cells_x
+= calitem
->max_week_number_digit_width
* 2
1418 + E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS
+ 1;
1419 text_x
= cells_x
+ calitem
->cell_width
1420 - (calitem
->cell_width
- min_cell_width
) / 2;
1421 text_x
-= E_CALENDAR_ITEM_MIN_CELL_XPAD
/ 2;
1422 text_y
= month_y
+ ythickness
* 2
1423 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
1424 + char_height
+ E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
1425 + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS
+ calitem
->month_tpad
;
1427 cells_y
= text_y
+ char_height
1428 + E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS
+ 1
1429 + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS
;
1432 e_utils_get_theme_color (widget
, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR
, &rgba
);
1433 gdk_cairo_set_source_rgba (cr
, &rgba
);
1436 text_y
- E_CALENDAR_ITEM_YPAD_ABOVE_CELLS
- 1,
1437 calitem
->cell_width
* 7 , cells_y
- text_y
);
1441 weekday
= calitem
->week_start_day
;
1442 pango_layout_set_font_description (layout
, font_desc
);
1443 if (gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
)
1444 text_x
+= (7 - 1) * calitem
->cell_width
;
1445 e_utils_get_theme_color (widget
, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR
, &rgba
);
1446 gdk_cairo_set_source_rgba (cr
, &rgba
);
1447 for (day
= 0; day
< 7; day
++) {
1449 layout_set_day_text (calitem
, layout
, weekday
);
1452 text_x
- calitem
->day_widths
[weekday
],
1454 pango_cairo_show_layout (cr
, layout
);
1455 text_x
+= (gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
)
1456 ? -calitem
->cell_width
: calitem
->cell_width
;
1459 weekday
= e_weekday_get_next (weekday
);
1462 /* Draw the rectangle around the week number. */
1463 if (calitem
->show_week_numbers
) {
1465 e_utils_get_theme_color (widget
, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR
, &rgba
);
1466 gdk_cairo_set_source_rgba (cr
, &rgba
);
1468 cr
, cells_x
, cells_y
- (cells_y
- text_y
+ 2) ,
1469 -20, E_CALENDAR_ROWS_PER_MONTH
* calitem
->cell_height
+ 18);
1474 e_calendar_item_draw_day_numbers (
1475 calitem
, cr
, width
, height
, row
, col
,
1476 year
, month
, start_weekday
, cells_x
, cells_y
);
1478 g_object_unref (layout
);
1480 pango_font_description_free (font_desc
);
1483 static const gchar
*
1484 get_digit_fomat (void)
1487 #ifdef HAVE_GNU_GET_LIBC_VERSION
1488 #include <gnu/libc-version.h>
1490 const gchar
*libc_version
= gnu_get_libc_version ();
1491 gchar
**split
= g_strsplit (libc_version
, ".", -1);
1496 major
= atoi (split
[0]);
1497 minor
= atoi (split
[1]);
1499 if (g_strv_length (split
) > 2)
1500 revision
= atoi (split
[2]);
1503 if (major
> 2 || minor
> 2 || (minor
== 2 && revision
> 2)) {
1512 e_calendar_item_draw_day_numbers (ECalendarItem
*calitem
,
1520 GDateWeekday start_weekday
,
1524 GnomeCanvasItem
*item
;
1526 PangoFontDescription
*font_desc
;
1527 GdkColor
*bg_color
, *fg_color
, *box_color
;
1531 gint char_height
, min_cell_width
, min_cell_height
;
1532 gint day_num
, drow
, dcol
, day_x
, day_y
;
1533 gint text_x
, text_y
;
1534 gint num_chars
, digit
;
1535 gint week_num
, mon
, days_from_week_start
;
1536 gint years
[3], months
[3], days_in_month
[3];
1537 gboolean today
, selected
, has_focus
, drop_target
= FALSE
;
1538 gboolean bold
, italic
, draw_day
, finished
= FALSE
;
1539 gint today_year
, today_month
, today_mday
, month_offset
;
1542 PangoContext
*pango_context
;
1543 PangoFontMetrics
*font_metrics
;
1544 PangoLayout
*layout
;
1546 item
= GNOME_CANVAS_ITEM (calitem
);
1547 widget
= GTK_WIDGET (item
->canvas
);
1549 /* Set up Pango prerequisites */
1550 font_desc
= calitem
->font_desc
;
1552 pango_context
= gtk_widget_get_pango_context (widget
);
1553 font_metrics
= pango_context_get_metrics (
1554 pango_context
, font_desc
,
1555 pango_context_get_language (pango_context
));
1557 font_desc
= pango_context_get_font_description (pango_context
);
1558 font_desc
= pango_font_description_copy (font_desc
);
1561 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics
)) +
1562 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics
));
1564 min_cell_width
= MAX (calitem
->max_day_width
, (calitem
->max_digit_width
* 2))
1565 + E_CALENDAR_ITEM_MIN_CELL_XPAD
;
1566 min_cell_height
= char_height
+ E_CALENDAR_ITEM_MIN_CELL_YPAD
;
1568 layout
= gtk_widget_create_pango_layout (GTK_WIDGET (widget
), NULL
);
1570 /* Calculate the number of days in the previous, current, and next
1572 years
[0] = years
[1] = years
[2] = year
;
1573 months
[0] = month
- 1;
1575 months
[2] = month
+ 1;
1576 if (months
[0] == -1) {
1580 if (months
[2] == 12) {
1585 days_in_month
[0] = DAYS_IN_MONTH (years
[0], months
[0]);
1586 days_in_month
[1] = DAYS_IN_MONTH (years
[1], months
[1]);
1587 days_in_month
[2] = DAYS_IN_MONTH (years
[2], months
[2]);
1589 /* Mon 0 is the previous month, which we may show the end of. Mon 1 is
1590 * the current month, and mon 2 is the next month. */
1593 month_offset
= row
* calitem
->cols
+ col
- 1;
1594 day_num
= days_in_month
[0];
1595 days_from_week_start
= e_weekday_get_days_between (
1596 calitem
->week_start_day
, start_weekday
);
1597 /* For the top-left month we show the end of the previous month, and
1598 * if the new month starts on the first day of the week we show a
1599 * complete week from the previous month. */
1600 if (days_from_week_start
== 0) {
1601 if (row
== 0 && col
== 0) {
1609 day_num
-= days_from_week_start
- 1;
1612 /* Get today's date, so we can highlight it. */
1613 if (calitem
->time_callback
) {
1614 today_tm
= calitem
->time_callback (
1615 calitem
, calitem
->time_callback_data
);
1618 today_tm
= *localtime (&t
);
1620 today_year
= today_tm
.tm_year
+ 1900;
1621 today_month
= today_tm
.tm_mon
;
1622 today_mday
= today_tm
.tm_mday
;
1624 /* We usually skip the last days of the previous month (mon = 0),
1625 * except for the top-left month displayed. */
1626 draw_day
= (mon
== 1 || (row
== 0 && col
== 0));
1628 for (drow
= 0; drow
< 6; drow
++) {
1629 /* Draw the week number. */
1630 if (calitem
->show_week_numbers
) {
1631 week_num
= e_calendar_item_get_week_number (
1632 calitem
, day_num
, months
[mon
], years
[mon
]);
1634 text_x
= cells_x
- E_CALENDAR_ITEM_XPAD_BEFORE_CELLS
- 1
1635 - E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS
;
1636 text_y
= cells_y
+ drow
* calitem
->cell_height
+
1637 + (calitem
->cell_height
- min_cell_height
+ 1) / 2;
1640 if (week_num
>= 10) {
1641 digit
= week_num
/ 10;
1642 text_x
-= calitem
->week_number_digit_widths
[digit
];
1643 num_chars
+= sprintf (
1645 get_digit_fomat (), digit
);
1648 digit
= week_num
% 10;
1649 text_x
-= calitem
->week_number_digit_widths
[digit
] + 6;
1650 num_chars
+= sprintf (
1652 get_digit_fomat (), digit
);
1655 e_utils_get_theme_color (widget
, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR
, &rgba
);
1656 gdk_cairo_set_source_rgba (cr
, &rgba
);
1657 pango_layout_set_font_description (layout
, font_desc
);
1658 pango_layout_set_text (layout
, buffer
, num_chars
);
1659 cairo_move_to (cr
, text_x
, text_y
);
1660 pango_cairo_update_layout (cr
, layout
);
1661 pango_cairo_show_layout (cr
, layout
);
1665 for (dcol
= 0; dcol
< 7; dcol
++) {
1667 GdkColor local_bg_color
, local_fg_color
;
1670 ((gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
)
1671 ? 7 - 1 - dcol
: dcol
) * calitem
->cell_width
;
1673 day_y
= cells_y
+ drow
* calitem
->cell_height
;
1675 today
= years
[mon
] == today_year
1676 && months
[mon
] == today_month
1677 && day_num
== today_mday
;
1679 selected
= calitem
->selection_set
1680 && (calitem
->selection_start_month_offset
< month_offset
1681 || (calitem
->selection_start_month_offset
== month_offset
1682 && calitem
->selection_start_day
<= day_num
))
1683 && (calitem
->selection_end_month_offset
> month_offset
1684 || (calitem
->selection_end_month_offset
== month_offset
1685 && calitem
->selection_end_day
>= day_num
));
1687 if (calitem
->styles
)
1688 day_style
= calitem
->styles
[(month_offset
+ 1) * 32 + day_num
];
1690 /* Get the colors & style to use for the day.*/
1691 if ((gtk_widget_has_focus (GTK_WIDGET (item
->canvas
))) &&
1692 item
->canvas
->focused_item
== item
)
1700 if (calitem
->style_callback
)
1701 calitem
->style_callback (
1717 calitem
->style_callback_data
);
1719 e_calendar_item_get_day_style (
1738 /* Draw the background, if set. */
1741 gdk_cairo_set_source_color (cr
, bg_color
);
1744 calitem
->cell_width
,
1745 calitem
->cell_height
);
1750 /* Draw the box, if set. */
1753 gdk_cairo_set_source_color (cr
, box_color
);
1756 calitem
->cell_width
- 1,
1757 calitem
->cell_height
- 1);
1762 /* Draw the 1- or 2-digit day number. */
1763 day_x
+= calitem
->cell_width
-
1764 (calitem
->cell_width
-
1765 min_cell_width
) / 2;
1766 day_x
-= E_CALENDAR_ITEM_MIN_CELL_XPAD
/ 2;
1767 day_y
+= (calitem
->cell_height
- min_cell_height
+ 1) / 2;
1768 day_y
+= E_CALENDAR_ITEM_MIN_CELL_YPAD
/ 2;
1771 if (day_num
>= 10) {
1772 digit
= day_num
/ 10;
1773 day_x
-= calitem
->digit_widths
[digit
];
1774 num_chars
+= sprintf (
1776 get_digit_fomat (), digit
);
1779 digit
= day_num
% 10;
1780 day_x
-= calitem
->digit_widths
[digit
];
1781 num_chars
+= sprintf (
1783 get_digit_fomat (), digit
);
1787 gdk_cairo_set_source_color (
1790 e_utils_get_theme_color (widget
, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR
, &rgba
);
1791 gdk_cairo_set_source_rgba (cr
, &rgba
);
1795 pango_font_description_set_weight (
1796 font_desc
, PANGO_WEIGHT_BOLD
);
1798 pango_font_description_set_weight (
1799 font_desc
, PANGO_WEIGHT_NORMAL
);
1803 pango_font_description_set_style (
1804 font_desc
, PANGO_STYLE_ITALIC
);
1806 pango_font_description_set_style (
1807 font_desc
, PANGO_STYLE_NORMAL
);
1810 pango_layout_set_font_description (layout
, font_desc
);
1811 pango_layout_set_text (layout
, buffer
, num_chars
);
1812 cairo_move_to (cr
, day_x
, day_y
);
1813 pango_cairo_update_layout (cr
, layout
);
1814 pango_cairo_show_layout (cr
, layout
);
1818 /* See if we've reached the end of a month. */
1819 if (day_num
== days_in_month
[mon
]) {
1822 /* We only draw the start of the next month
1823 * for the bottom-right month displayed. */
1824 if (mon
== 2 && (row
!= calitem
->rows
- 1
1825 || col
!= calitem
->cols
- 1)) {
1826 /* Set a flag so we exit the loop. */
1837 /* Exit the loop if the flag is set. */
1842 /* Reset pango weight and style */
1843 pango_font_description_set_weight (font_desc
, PANGO_WEIGHT_NORMAL
);
1844 pango_font_description_set_style (font_desc
, PANGO_STYLE_NORMAL
);
1846 g_object_unref (layout
);
1848 pango_font_metrics_unref (font_metrics
);
1849 pango_font_description_free (font_desc
);
1853 e_calendar_item_get_week_number (ECalendarItem
*calitem
,
1859 GDateWeekday weekday
;
1863 g_date_clear (&date
, 1);
1864 g_date_set_dmy (&date
, day
, month
+ 1, year
);
1866 weekday
= g_date_get_weekday (&date
);
1868 if (g_date_valid_weekday (weekday
)) {
1871 /* We want always point to nearest Monday as the first day
1872 * of the week regardless of the calendar's week_start_day. */
1873 if (weekday
>= G_DATE_THURSDAY
) {
1874 days_between
= e_weekday_get_days_between (
1875 weekday
, G_DATE_MONDAY
);
1876 g_date_add_days (&date
, days_between
);
1878 days_between
= e_weekday_get_days_between (
1879 G_DATE_MONDAY
, weekday
);
1880 g_date_subtract_days (&date
, days_between
);
1884 /* Calculate the day of the year, from 0 to 365. */
1885 yearday
= g_date_get_day_of_year (&date
) - 1;
1887 /* If the week starts on or after 29th December, it is week 1 of the
1888 * next year, since there are 4 days in the next year. */
1889 if (g_date_get_month (&date
) == 12 && g_date_get_day (&date
) >= 29)
1892 /* Calculate the week number, from 0. */
1893 week_num
= yearday
/ 7;
1895 /* If the first week starts on or after Jan 5th, then we need to add
1896 * 1 since the previous week will really be the first week. */
1897 if (yearday
% 7 >= 4)
1900 /* Add 1 so week numbers are from 1 to 53. */
1901 return week_num
+ 1;
1904 /* This is supposed to return the nearest item the the point and the distance.
1905 * Since we are the only item we just return ourself and 0 for the distance.
1906 * This is needed so that we get button/motion events. */
1907 static GnomeCanvasItem
*
1908 e_calendar_item_point (GnomeCanvasItem
*item
,
1918 e_calendar_item_stop_selecting (ECalendarItem
*calitem
,
1921 if (!calitem
->selecting
)
1924 gnome_canvas_item_ungrab (GNOME_CANVAS_ITEM (calitem
), time
);
1926 calitem
->selecting
= FALSE
;
1928 /* If the user selects the grayed dates before the first month or
1929 * after the last month, we move backwards or forwards one month.
1930 * The set_month () call should take care of updating the selection. */
1931 if (calitem
->selection_end_month_offset
== -1)
1932 e_calendar_item_set_first_month_with_emit (
1933 calitem
, calitem
->year
,
1934 calitem
->month
- 1, FALSE
);
1935 else if (calitem
->selection_start_month_offset
== calitem
->rows
* calitem
->cols
)
1936 e_calendar_item_set_first_month_with_emit (
1937 calitem
, calitem
->year
,
1938 calitem
->month
+ 1, FALSE
);
1940 calitem
->selection_changed
= TRUE
;
1941 if (calitem
->selecting_axis
) {
1942 g_free (calitem
->selecting_axis
);
1943 calitem
->selecting_axis
= NULL
;
1946 e_calendar_item_queue_signal_emission (calitem
);
1947 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
1951 e_calendar_item_selection_add_days (ECalendarItem
*calitem
,
1953 gboolean multi_selection
)
1955 GDate gdate_start
, gdate_end
;
1957 g_return_if_fail (E_IS_CALENDAR_ITEM (calitem
));
1959 if (!e_calendar_item_get_selection (calitem
, &gdate_start
, &gdate_end
)) {
1960 /* We set the date to the first day of the month */
1961 g_date_set_dmy (&gdate_start
, 1, calitem
->month
+ 1, calitem
->year
);
1962 gdate_end
= gdate_start
;
1965 if (multi_selection
&& calitem
->max_days_selected
> 1) {
1968 days_between
= g_date_days_between (&gdate_start
, &gdate_end
);
1969 if (!calitem
->selecting_axis
) {
1970 calitem
->selecting_axis
= g_new (GDate
, 1);
1971 *(calitem
->selecting_axis
) = gdate_start
;
1973 if ((days_between
!= 0 &&
1974 g_date_compare (calitem
->selecting_axis
, &gdate_end
) == 0) ||
1975 (days_between
== 0 && n_days
< 0)) {
1976 if (days_between
- n_days
> calitem
->max_days_selected
- 1)
1977 n_days
= days_between
+ 1 - calitem
->max_days_selected
;
1978 g_date_add_days (&gdate_start
, n_days
);
1981 if (days_between
+ n_days
> calitem
->max_days_selected
- 1)
1982 n_days
= calitem
->max_days_selected
- 1 - days_between
;
1983 g_date_add_days (&gdate_end
, n_days
);
1986 if (g_date_compare (&gdate_end
, &gdate_start
) < 0) {
1988 tmp_date
= gdate_start
;
1989 gdate_start
= gdate_end
;
1990 gdate_end
= tmp_date
;
1994 /* clear "selecting_axis", it is only for mulit-selecting */
1995 if (calitem
->selecting_axis
) {
1996 g_free (calitem
->selecting_axis
);
1997 calitem
->selecting_axis
= NULL
;
1999 g_date_add_days (&gdate_start
, n_days
);
2000 gdate_end
= gdate_start
;
2003 calitem
->selecting
= TRUE
;
2005 e_calendar_item_set_selection_if_emission (
2006 calitem
, &gdate_start
, &gdate_end
, FALSE
);
2008 g_signal_emit_by_name (calitem
, "selection_preview_changed");
2012 e_calendar_item_key_press_event (ECalendarItem
*calitem
,
2015 guint keyval
= event
->key
.keyval
;
2017 gboolean multi_selection
;
2019 if (event
->key
.state
& GDK_CONTROL_MASK
||
2020 event
->key
.state
& GDK_MOD1_MASK
)
2023 is_rtl
= gtk_widget_get_direction (GTK_WIDGET (GNOME_CANVAS_ITEM (calitem
)->canvas
)) == GTK_TEXT_DIR_RTL
;
2024 multi_selection
= event
->key
.state
& GDK_SHIFT_MASK
;
2028 e_calendar_item_selection_add_days (
2033 e_calendar_item_selection_add_days (
2038 e_calendar_item_selection_add_days (
2039 calitem
, is_rtl
? 1 : -1,
2043 e_calendar_item_selection_add_days (
2044 calitem
, is_rtl
? -1 : 1,
2048 case GDK_KEY_Return
:
2049 e_calendar_item_stop_selecting (calitem
, event
->key
.time
);
2058 e_calendar_item_event (GnomeCanvasItem
*item
,
2061 ECalendarItem
*calitem
;
2063 calitem
= E_CALENDAR_ITEM (item
);
2065 switch (event
->type
) {
2066 case GDK_BUTTON_PRESS
:
2067 return e_calendar_item_button_press (calitem
, event
);
2068 case GDK_BUTTON_RELEASE
:
2069 return e_calendar_item_button_release (calitem
, event
);
2070 case GDK_MOTION_NOTIFY
:
2071 return e_calendar_item_motion (calitem
, event
);
2072 case GDK_FOCUS_CHANGE
:
2073 gnome_canvas_item_request_update (item
);
2076 return e_calendar_item_key_press_event (calitem
, event
);
2085 e_calendar_item_bounds (GnomeCanvasItem
*item
,
2091 ECalendarItem
*calitem
;
2093 g_return_if_fail (E_IS_CALENDAR_ITEM (item
));
2095 calitem
= E_CALENDAR_ITEM (item
);
2102 /* This checks if any fonts have changed, and if so it recalculates the
2103 * text sizes and the minimum month size. */
2105 e_calendar_item_recalc_sizes (ECalendarItem
*calitem
)
2107 GnomeCanvasItem
*canvas_item
;
2108 gint max_day_width
, digit
, max_digit_width
, max_week_number_digit_width
;
2109 gint char_height
, width
, min_cell_width
, min_cell_height
;
2112 PangoFontDescription
*font_desc
, *wkfont_desc
;
2113 PangoContext
*pango_context
;
2114 PangoFontMetrics
*font_metrics
;
2115 PangoLayout
*layout
;
2116 GDateWeekday weekday
;
2118 GtkStyleContext
*style_context
;
2121 canvas_item
= GNOME_CANVAS_ITEM (calitem
);
2122 widget
= GTK_WIDGET (canvas_item
->canvas
);
2123 style_context
= gtk_widget_get_style_context (widget
);
2124 gtk_style_context_get_padding (style_context
, gtk_style_context_get_state (style_context
), &padding
);
2126 /* Set up Pango prerequisites */
2127 font_desc
= calitem
->font_desc
;
2128 wkfont_desc
= calitem
->week_number_font_desc
;
2130 pango_context
= gtk_widget_create_pango_context (
2131 GTK_WIDGET (canvas_item
->canvas
));
2132 font_metrics
= pango_context_get_metrics (
2133 pango_context
, font_desc
,
2134 pango_context_get_language (pango_context
));
2136 font_desc
= pango_context_get_font_description (pango_context
);
2137 font_desc
= pango_font_description_copy (font_desc
);
2138 layout
= pango_layout_new (pango_context
);
2141 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics
)) +
2142 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics
));
2145 for (weekday
= G_DATE_MONDAY
; weekday
<= G_DATE_SUNDAY
; weekday
++) {
2146 layout_set_day_text (calitem
, layout
, weekday
);
2147 pango_layout_get_pixel_size (layout
, &width
, NULL
);
2149 calitem
->day_widths
[weekday
] = width
;
2150 max_day_width
= MAX (max_day_width
, width
);
2152 calitem
->max_day_width
= max_day_width
;
2154 max_digit_width
= 0;
2155 max_week_number_digit_width
= 0;
2156 for (digit
= 0; digit
< 10; digit
++) {
2157 gchar locale_digit
[5];
2158 gint locale_digit_len
;
2160 locale_digit_len
= sprintf (locale_digit
, get_digit_fomat (), digit
);
2162 pango_layout_set_text (layout
, locale_digit
, locale_digit_len
);
2163 pango_layout_get_pixel_size (layout
, &width
, NULL
);
2165 calitem
->digit_widths
[digit
] = width
;
2166 max_digit_width
= MAX (max_digit_width
, width
);
2169 pango_context_set_font_description (pango_context
, wkfont_desc
);
2170 pango_layout_context_changed (layout
);
2172 pango_layout_set_text (layout
, locale_digit
, locale_digit_len
);
2173 pango_layout_get_pixel_size (layout
, &width
, NULL
);
2175 calitem
->week_number_digit_widths
[digit
] = width
;
2176 max_week_number_digit_width
= MAX (max_week_number_digit_width
, width
);
2178 pango_context_set_font_description (pango_context
, font_desc
);
2179 pango_layout_context_changed (layout
);
2181 calitem
->week_number_digit_widths
[digit
] = width
;
2182 max_week_number_digit_width
= max_digit_width
;
2185 calitem
->max_digit_width
= max_digit_width
;
2186 calitem
->max_week_number_digit_width
= max_week_number_digit_width
;
2188 min_cell_width
= MAX (calitem
->max_day_width
, (calitem
->max_digit_width
* 2))
2189 + E_CALENDAR_ITEM_MIN_CELL_XPAD
;
2190 min_cell_height
= char_height
+ E_CALENDAR_ITEM_MIN_CELL_YPAD
;
2192 calitem
->min_month_width
= E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS
2193 + E_CALENDAR_ITEM_XPAD_BEFORE_CELLS
+ min_cell_width
* 7
2194 + E_CALENDAR_ITEM_XPAD_AFTER_CELLS
;
2195 if (calitem
->show_week_numbers
) {
2196 calitem
->min_month_width
+= calitem
->max_week_number_digit_width
* 2
2197 + E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS
+ 1;
2200 calitem
->min_month_height
= padding
.top
* 2
2201 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
+ char_height
2202 + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
+ 1
2203 + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS
2204 + char_height
+ E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS
+ 1
2205 + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS
+ min_cell_height
* 6
2206 + E_CALENDAR_ITEM_YPAD_BELOW_CELLS
;
2208 calitem
->max_month_name_width
= 50;
2209 memset (&tmp_tm
, 0, sizeof (tmp_tm
));
2210 tmp_tm
.tm_year
= 2000 - 100;
2212 tmp_tm
.tm_isdst
= -1;
2213 for (tmp_tm
.tm_mon
= 0; tmp_tm
.tm_mon
< 12; tmp_tm
.tm_mon
++) {
2216 e_utf8_strftime (buffer
, sizeof (buffer
), C_("CalItem", "%B"), &tmp_tm
);
2218 pango_layout_set_text (layout
, buffer
, -1);
2219 pango_layout_get_pixel_size (layout
, &width
, NULL
);
2221 if (width
> calitem
->max_month_name_width
)
2222 calitem
->max_month_name_width
= width
;
2225 g_object_unref (layout
);
2226 g_object_unref (pango_context
);
2227 pango_font_metrics_unref (font_metrics
);
2228 pango_font_description_free (font_desc
);
2232 e_calendar_item_get_day_style (ECalendarItem
*calitem
,
2238 gboolean prev_or_next_month
,
2241 gboolean drop_target
,
2242 GdkColor
**bg_color
,
2243 GdkColor
**fg_color
,
2244 GdkColor
**box_color
,
2247 GdkColor
*local_bg_color
,
2248 GdkColor
*local_fg_color
)
2252 widget
= GTK_WIDGET (GNOME_CANVAS_ITEM (calitem
)->canvas
);
2258 *bold
= (day_style
& E_CALENDAR_ITEM_MARK_BOLD
) ==
2259 E_CALENDAR_ITEM_MARK_BOLD
;
2260 *italic
= (day_style
& E_CALENDAR_ITEM_MARK_ITALIC
) ==
2261 E_CALENDAR_ITEM_MARK_ITALIC
;
2264 *box_color
= &calitem
->colors
[E_CALENDAR_ITEM_COLOR_TODAY_BOX
];
2266 if (prev_or_next_month
) {
2267 *fg_color
= local_fg_color
;
2268 e_utils_get_theme_color_color (widget
, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR
, local_fg_color
);
2272 *bg_color
= local_bg_color
;
2273 *fg_color
= local_fg_color
;
2276 e_utils_get_theme_color_color (widget
, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR
, local_bg_color
);
2277 e_utils_get_theme_color_color (widget
, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR
, local_fg_color
);
2281 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
);
2282 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
);
2284 e_utils_get_theme_color_color (widget
, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR
, &base_bg
);
2286 if (local_bg_color
->red
== base_bg
.red
&&
2287 local_bg_color
->green
== base_bg
.green
&&
2288 local_bg_color
->blue
== base_bg
.blue
) {
2289 e_utils_get_theme_color_color (widget
, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR
, local_bg_color
);
2290 e_utils_get_theme_color_color (widget
, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR
, local_fg_color
);
2297 e_calendar_item_button_press (ECalendarItem
*calitem
,
2298 GdkEvent
*button_event
)
2300 GdkGrabStatus grab_status
;
2301 GdkDevice
*event_device
;
2302 guint event_button
= 0;
2304 gdouble event_x_win
= 0;
2305 gdouble event_y_win
= 0;
2306 gint month_offset
, day
, add_days
= 0;
2307 gboolean all_week
, round_up_end
= FALSE
, round_down_start
= FALSE
;
2309 gdk_event_get_button (button_event
, &event_button
);
2310 gdk_event_get_coords (button_event
, &event_x_win
, &event_y_win
);
2311 event_device
= gdk_event_get_device (button_event
);
2312 event_time
= gdk_event_get_time (button_event
);
2314 if (event_button
== 4)
2315 e_calendar_item_set_first_month_with_emit (
2316 calitem
, calitem
->year
,
2317 calitem
->month
- 1, TRUE
);
2318 else if (event_button
== 5)
2319 e_calendar_item_set_first_month_with_emit (
2320 calitem
, calitem
->year
,
2321 calitem
->month
+ 1, TRUE
);
2323 if (!e_calendar_item_convert_position_to_day (calitem
,
2327 &month_offset
, &day
,
2331 if (event_button
== 3 && day
== -1
2332 && e_calendar_item_get_display_popup (calitem
)) {
2333 e_calendar_item_show_popup_menu (
2334 calitem
, button_event
, month_offset
);
2338 if (event_button
!= 1 || day
== -1)
2341 if (calitem
->max_days_selected
< 1)
2344 grab_status
= gnome_canvas_item_grab (
2345 GNOME_CANVAS_ITEM (calitem
),
2346 GDK_POINTER_MOTION_MASK
|
2347 GDK_BUTTON_RELEASE_MASK
,
2352 if (grab_status
!= GDK_GRAB_SUCCESS
)
2355 if (all_week
&& calitem
->keep_wdays_on_weeknum_click
) {
2356 gint tmp_start_moff
, tmp_start_day
;
2358 tmp_start_moff
= calitem
->selection_start_month_offset
;
2359 tmp_start_day
= calitem
->selection_start_day
;
2360 e_calendar_item_round_down_selection (
2361 calitem
, &tmp_start_moff
, &tmp_start_day
);
2363 e_calendar_item_round_down_selection (calitem
, &month_offset
, &day
);
2364 month_offset
+= calitem
->selection_start_month_offset
- tmp_start_moff
;
2365 day
+= calitem
->selection_start_day
- tmp_start_day
;
2367 /* keep same count of days selected */
2368 add_days
= e_calendar_item_get_inclusive_days (
2370 calitem
->selection_start_month_offset
,
2371 calitem
->selection_start_day
,
2372 calitem
->selection_end_month_offset
,
2373 calitem
->selection_end_day
) - 1;
2376 calitem
->selection_set
= TRUE
;
2377 calitem
->selection_start_month_offset
= month_offset
;
2378 calitem
->selection_start_day
= day
;
2379 calitem
->selection_end_month_offset
= month_offset
;
2380 calitem
->selection_end_day
= day
;
2383 e_calendar_item_add_days_to_selection (calitem
, add_days
);
2385 calitem
->selection_real_start_month_offset
= month_offset
;
2386 calitem
->selection_real_start_day
= day
;
2388 calitem
->selection_from_full_week
= FALSE
;
2389 calitem
->selecting
= TRUE
;
2390 calitem
->selection_dragging_end
= TRUE
;
2392 if (all_week
&& !calitem
->keep_wdays_on_weeknum_click
) {
2393 calitem
->selection_from_full_week
= TRUE
;
2394 round_up_end
= TRUE
;
2397 if (calitem
->days_to_start_week_selection
== 1) {
2398 round_down_start
= TRUE
;
2399 round_up_end
= TRUE
;
2402 /* Don't round up or down if we can't select a week or more,
2403 * or when keeping week days. */
2404 if (calitem
->max_days_selected
< 7 ||
2405 (all_week
&& calitem
->keep_wdays_on_weeknum_click
)) {
2406 round_down_start
= FALSE
;
2407 round_up_end
= FALSE
;
2411 e_calendar_item_round_up_selection (
2412 calitem
, &calitem
->selection_end_month_offset
,
2413 &calitem
->selection_end_day
);
2415 if (round_down_start
)
2416 e_calendar_item_round_down_selection (
2417 calitem
, &calitem
->selection_start_month_offset
,
2418 &calitem
->selection_start_day
);
2420 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
2426 e_calendar_item_button_release (ECalendarItem
*calitem
,
2427 GdkEvent
*button_event
)
2431 event_time
= gdk_event_get_time (button_event
);
2432 e_calendar_item_stop_selecting (calitem
, event_time
);
2438 e_calendar_item_motion (ECalendarItem
*calitem
,
2441 gint start_month
, start_day
, end_month
, end_day
, month_offset
, day
;
2442 gint tmp_month
, tmp_day
, days_in_selection
;
2443 gboolean all_week
, round_up_end
= FALSE
, round_down_start
= FALSE
;
2445 if (!calitem
->selecting
)
2448 if (!e_calendar_item_convert_position_to_day (calitem
,
2452 &month_offset
, &day
,
2459 if (calitem
->selection_dragging_end
) {
2460 start_month
= calitem
->selection_real_start_month_offset
;
2461 start_day
= calitem
->selection_real_start_day
;
2462 end_month
= month_offset
;
2465 start_month
= month_offset
;
2467 end_month
= calitem
->selection_real_start_month_offset
;
2468 end_day
= calitem
->selection_real_start_day
;
2471 if (start_month
> end_month
|| (start_month
== end_month
2472 && start_day
> end_day
)) {
2473 tmp_month
= start_month
;
2474 tmp_day
= start_day
;
2475 start_month
= end_month
;
2476 start_day
= end_day
;
2477 end_month
= tmp_month
;
2480 calitem
->selection_dragging_end
=
2481 !calitem
->selection_dragging_end
;
2484 if (calitem
->days_to_start_week_selection
> 0) {
2485 days_in_selection
= e_calendar_item_get_inclusive_days (
2486 calitem
, start_month
, start_day
, end_month
, end_day
);
2487 if (days_in_selection
>= calitem
->days_to_start_week_selection
) {
2488 round_down_start
= TRUE
;
2489 round_up_end
= TRUE
;
2493 /* If we are over a week number and we are dragging the end of the
2494 * selection, we round up to the end of this week. */
2495 if (all_week
&& calitem
->selection_dragging_end
)
2496 round_up_end
= TRUE
;
2498 /* If the selection was started from a week number and we are dragging
2499 * the start of the selection, we need to round up the end to include
2500 * all of the original week selected. */
2501 if (calitem
->selection_from_full_week
2502 && !calitem
->selection_dragging_end
)
2503 round_up_end
= TRUE
;
2505 /* Don't round up or down if we can't select a week or more. */
2506 if (calitem
->max_days_selected
< 7) {
2507 round_down_start
= FALSE
;
2508 round_up_end
= FALSE
;
2512 e_calendar_item_round_up_selection (
2513 calitem
, &end_month
,
2515 if (round_down_start
)
2516 e_calendar_item_round_down_selection (
2517 calitem
, &start_month
,
2520 /* Check we don't go over the maximum number of days to select. */
2521 if (calitem
->selection_dragging_end
) {
2522 e_calendar_item_check_selection_end (
2529 e_calendar_item_check_selection_start (
2537 if (start_month
== calitem
->selection_start_month_offset
2538 && start_day
== calitem
->selection_start_day
2539 && end_month
== calitem
->selection_end_month_offset
2540 && end_day
== calitem
->selection_end_day
)
2543 calitem
->selection_start_month_offset
= start_month
;
2544 calitem
->selection_start_day
= start_day
;
2545 calitem
->selection_end_month_offset
= end_month
;
2546 calitem
->selection_end_day
= end_day
;
2548 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
2554 e_calendar_item_check_selection_end (ECalendarItem
*calitem
,
2560 gint year
, month
, max_month
, max_day
, days_in_month
;
2562 if (calitem
->max_days_selected
<= 0)
2565 year
= calitem
->year
;
2566 month
= calitem
->month
+ start_month
;
2567 e_calendar_item_normalize_date (calitem
, &year
, &month
);
2569 max_month
= start_month
;
2570 max_day
= start_day
+ calitem
->max_days_selected
- 1;
2573 days_in_month
= DAYS_IN_MONTH (year
, month
);
2574 if (max_day
<= days_in_month
)
2582 max_day
-= days_in_month
;
2585 if (*end_month
> max_month
) {
2586 *end_month
= max_month
;
2588 } else if (*end_month
== max_month
&& *end_day
> max_day
) {
2594 e_calendar_item_check_selection_start (ECalendarItem
*calitem
,
2600 gint year
, month
, min_month
, min_day
, days_in_month
;
2602 if (calitem
->max_days_selected
<= 0)
2605 year
= calitem
->year
;
2606 month
= calitem
->month
+ end_month
;
2607 e_calendar_item_normalize_date (calitem
, &year
, &month
);
2609 min_month
= end_month
;
2610 min_day
= end_day
- calitem
->max_days_selected
+ 1;
2612 while (min_day
<= 0) {
2619 days_in_month
= DAYS_IN_MONTH (year
, month
);
2620 min_day
+= days_in_month
;
2623 if (*start_month
< min_month
) {
2624 *start_month
= min_month
;
2625 *start_day
= min_day
;
2626 } else if (*start_month
== min_month
&& *start_day
< min_day
) {
2627 *start_day
= min_day
;
2631 /* Converts a position within the item to a month & day.
2632 * The month returned is 0 for the top-left month displayed.
2633 * If the position is over the month heading -1 is returned for the day.
2634 * If the position is over a week number the first day of the week is returned
2635 * and entire_week is set to TRUE.
2636 * It returns FALSE if the position is completely outside all months. */
2638 e_calendar_item_convert_position_to_day (ECalendarItem
*calitem
,
2641 gboolean round_empty_positions
,
2644 gboolean
*entire_week
)
2646 GnomeCanvasItem
*item
;
2648 GtkStyleContext
*style_context
;
2650 gint xthickness
, ythickness
, char_height
;
2651 gint x
, y
, row
, col
, cells_x
, cells_y
, day_row
, day_col
;
2652 gint first_day_offset
, days_in_month
, days_in_prev_month
;
2653 gint week_num_x1
, week_num_x2
;
2654 PangoContext
*pango_context
;
2655 PangoFontMetrics
*font_metrics
;
2657 item
= GNOME_CANVAS_ITEM (calitem
);
2658 widget
= GTK_WIDGET (item
->canvas
);
2659 style_context
= gtk_widget_get_style_context (widget
);
2660 gtk_style_context_get_padding (style_context
, gtk_style_context_get_state (style_context
), &padding
);
2662 pango_context
= gtk_widget_create_pango_context (widget
);
2663 font_metrics
= pango_context_get_metrics (
2664 pango_context
, calitem
->font_desc
,
2665 pango_context_get_language (pango_context
));
2668 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics
)) +
2669 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics
));
2670 xthickness
= padding
.left
;
2671 ythickness
= padding
.top
;
2673 pango_font_metrics_unref (font_metrics
);
2674 g_object_unref (pango_context
);
2676 *entire_week
= FALSE
;
2678 x
= event_x
- xthickness
- calitem
->x_offset
;
2679 y
= event_y
- ythickness
;
2684 row
= y
/ calitem
->month_height
;
2685 col
= x
/ calitem
->month_width
;
2687 if (row
>= calitem
->rows
|| col
>= calitem
->cols
)
2689 if (gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
)
2690 col
= calitem
->cols
- 1 - col
;
2692 *month_offset
= row
* calitem
->cols
+ col
;
2694 x
= x
% calitem
->month_width
;
2695 y
= y
% calitem
->month_height
;
2697 if (y
< ythickness
* 2 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
2698 + char_height
+ E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
) {
2703 cells_y
= ythickness
* 2 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
2704 + char_height
+ E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
2705 + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS
+ calitem
->month_tpad
2706 + char_height
+ E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS
+ 1
2707 + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS
;
2711 day_row
= y
/ calitem
->cell_height
;
2712 if (day_row
>= E_CALENDAR_ROWS_PER_MONTH
)
2715 week_num_x1
= E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS
+ calitem
->month_lpad
;
2717 if (calitem
->show_week_numbers
) {
2718 week_num_x2
= week_num_x1
2719 + calitem
->max_week_number_digit_width
* 2;
2720 if (x
>= week_num_x1
&& x
< week_num_x2
)
2721 *entire_week
= TRUE
;
2722 cells_x
= week_num_x2
+ E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS
+ 1;
2724 cells_x
= week_num_x1
;
2730 cells_x
+= E_CALENDAR_ITEM_XPAD_BEFORE_CELLS
;
2734 day_col
= x
/ calitem
->cell_width
;
2735 if (gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
)
2736 day_col
= E_CALENDAR_COLS_PER_MONTH
- 1 - day_col
;
2737 if (day_col
>= E_CALENDAR_COLS_PER_MONTH
)
2741 *day
= day_row
* E_CALENDAR_COLS_PER_MONTH
+ day_col
;
2743 e_calendar_item_get_month_info (
2744 calitem
, row
, col
, &first_day_offset
,
2745 &days_in_month
, &days_in_prev_month
);
2746 if (*day
< first_day_offset
) {
2747 if (*entire_week
|| (row
== 0 && col
== 0)) {
2749 *day
= days_in_prev_month
+ 1 - first_day_offset
2752 } else if (round_empty_positions
) {
2753 *day
= first_day_offset
;
2759 *day
-= first_day_offset
- 1;
2761 if (*day
> days_in_month
) {
2762 if (row
== calitem
->rows
- 1 && col
== calitem
->cols
- 1) {
2764 *day
-= days_in_month
;
2766 } else if (round_empty_positions
) {
2767 *day
= days_in_month
;
2777 e_calendar_item_get_month_info (ECalendarItem
*calitem
,
2780 gint
*first_day_offset
,
2781 gint
*days_in_month
,
2782 gint
*days_in_prev_month
)
2784 GDateWeekday start_weekday
;
2785 gint year
, month
, first_day_of_month
;
2786 struct tm tmp_tm
= { 0 };
2788 month
= calitem
->month
+ row
* calitem
->cols
+ col
;
2789 year
= calitem
->year
+ month
/ 12;
2792 *days_in_month
= DAYS_IN_MONTH (year
, month
);
2794 *days_in_prev_month
= DAYS_IN_MONTH (year
- 1, 11);
2796 *days_in_prev_month
= DAYS_IN_MONTH (year
, month
- 1);
2798 tmp_tm
.tm_year
= year
- 1900;
2799 tmp_tm
.tm_mon
= month
;
2801 tmp_tm
.tm_isdst
= -1;
2804 start_weekday
= e_weekday_from_tm_wday (tmp_tm
.tm_wday
);
2806 first_day_of_month
= e_weekday_get_days_between (
2807 calitem
->week_start_day
, start_weekday
);
2809 if (row
== 0 && col
== 0 && first_day_of_month
== 0)
2810 *first_day_offset
= 7;
2812 *first_day_offset
= first_day_of_month
;
2816 e_calendar_item_get_first_month (ECalendarItem
*calitem
,
2820 *year
= calitem
->year
;
2821 *month
= calitem
->month
;
2825 e_calendar_item_convert_position_to_date (ECalendarItem
*calitem
,
2830 gint month_offset
= -1;
2831 gint day
= -1, dday
, dmonth
, dyear
;
2832 gboolean entire_week
= FALSE
;
2834 g_return_val_if_fail (E_IS_CALENDAR_ITEM (calitem
), FALSE
);
2835 g_return_val_if_fail (date
!= NULL
, FALSE
);
2837 if (calitem
->rows
== 0 || calitem
->cols
== 0)
2840 if (!e_calendar_item_convert_position_to_day (calitem
, event_x
, event_y
, FALSE
, &month_offset
, &day
, &entire_week
) ||
2841 day
< 0 || entire_week
)
2844 dyear
= calitem
->year
;
2845 dmonth
= calitem
->month
+ month_offset
;
2846 e_calendar_item_normalize_date (calitem
, &dyear
, &dmonth
);
2849 g_date_set_dmy (date
, dday
, dmonth
+ 1, dyear
);
2851 return g_date_valid (date
);
2855 e_calendar_item_preserve_day_selection (ECalendarItem
*calitem
,
2860 gint year
, month
, weekday
, days
, days_in_month
;
2861 struct tm tmp_tm
= { 0 };
2863 year
= calitem
->year
;
2864 month
= calitem
->month
+ *month_offset
;
2865 e_calendar_item_normalize_date (calitem
, &year
, &month
);
2867 tmp_tm
.tm_year
= year
- 1900;
2868 tmp_tm
.tm_mon
= month
;
2869 tmp_tm
.tm_mday
= *day
;
2870 tmp_tm
.tm_isdst
= -1;
2873 /* Convert to 0 (Monday) to 6 (Sunday). */
2874 weekday
= (tmp_tm
.tm_wday
+ 6) % 7;
2876 /* Calculate how many days to the start of the row. */
2877 days
= (weekday
+ 7 - selected_day
) % 7;
2886 days_in_month
= DAYS_IN_MONTH (year
, month
);
2888 *day
+= days_in_month
;
2892 /* This also handles values of month < 0 or > 11 by updating the year. */
2894 e_calendar_item_set_first_month_with_emit (ECalendarItem
*calitem
,
2897 gboolean emit_date_range_moved
)
2899 gint new_year
, new_month
, months_diff
, num_months
;
2900 gint old_days_in_selection
, new_days_in_selection
;
2904 e_calendar_item_normalize_date (calitem
, &new_year
, &new_month
);
2906 if (calitem
->year
== new_year
&& calitem
->month
== new_month
)
2909 /* Update the selection. */
2910 num_months
= calitem
->rows
* calitem
->cols
;
2911 months_diff
= (new_year
- calitem
->year
) * 12
2912 + new_month
- calitem
->month
;
2914 if (calitem
->selection_set
) {
2915 if (!calitem
->move_selection_when_moving
2916 || (calitem
->selection_start_month_offset
- months_diff
>= 0
2917 && calitem
->selection_end_month_offset
- months_diff
< num_months
)) {
2918 calitem
->selection_start_month_offset
-= months_diff
;
2919 calitem
->selection_end_month_offset
-= months_diff
;
2920 calitem
->selection_real_start_month_offset
-= months_diff
;
2922 calitem
->year
= new_year
;
2923 calitem
->month
= new_month
;
2926 struct tm tmp_tm
= { 0 };
2928 old_days_in_selection
= e_calendar_item_get_inclusive_days (
2930 calitem
->selection_start_month_offset
,
2931 calitem
->selection_start_day
,
2932 calitem
->selection_end_month_offset
,
2933 calitem
->selection_end_day
);
2935 /* Calculate the currently selected day */
2936 tmp_tm
.tm_year
= calitem
->year
- 1900;
2937 tmp_tm
.tm_mon
= calitem
->month
+ calitem
->selection_start_month_offset
;
2938 tmp_tm
.tm_mday
= calitem
->selection_start_day
;
2939 tmp_tm
.tm_isdst
= -1;
2942 selected_day
= (tmp_tm
.tm_wday
+ 6) % 7;
2944 /* Make sure the selection will be displayed. */
2945 if (calitem
->selection_start_month_offset
< 0
2946 || calitem
->selection_start_month_offset
>= num_months
) {
2947 calitem
->selection_end_month_offset
-=
2948 calitem
->selection_start_month_offset
;
2949 calitem
->selection_start_month_offset
= 0;
2952 /* We want to ensure that the same number of days are
2953 * selected after we have moved the selection. */
2954 calitem
->year
= new_year
;
2955 calitem
->month
= new_month
;
2957 e_calendar_item_ensure_valid_day (
2958 calitem
, &calitem
->selection_start_month_offset
,
2959 &calitem
->selection_start_day
);
2960 e_calendar_item_ensure_valid_day (
2961 calitem
, &calitem
->selection_end_month_offset
,
2962 &calitem
->selection_end_day
);
2964 if (calitem
->preserve_day_when_moving
) {
2965 e_calendar_item_preserve_day_selection (
2966 calitem
, selected_day
,
2967 &calitem
->selection_start_month_offset
,
2968 &calitem
->selection_start_day
);
2971 new_days_in_selection
= e_calendar_item_get_inclusive_days (
2973 calitem
->selection_start_month_offset
,
2974 calitem
->selection_start_day
,
2975 calitem
->selection_end_month_offset
,
2976 calitem
->selection_end_day
);
2978 if (old_days_in_selection
!= new_days_in_selection
)
2979 e_calendar_item_add_days_to_selection (
2980 calitem
, old_days_in_selection
-
2981 new_days_in_selection
);
2983 /* Flag that we need to emit the "selection_changed"
2984 * signal. We don't want to emit it here since setting
2985 * the "year" and "month" args would result in 2
2986 * signals emitted. */
2987 calitem
->selection_changed
= TRUE
;
2990 calitem
->year
= new_year
;
2991 calitem
->month
= new_month
;
2994 e_calendar_item_date_range_changed (calitem
);
2995 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
2997 if (emit_date_range_moved
)
2998 g_signal_emit (calitem
, e_calendar_item_signals
[DATE_RANGE_MOVED
], 0);
3001 /* This also handles values of month < 0 or > 11 by updating the year. */
3003 e_calendar_item_set_first_month (ECalendarItem
*calitem
,
3007 e_calendar_item_set_first_month_with_emit (calitem
, year
, month
, TRUE
);
3010 /* Get the maximum number of days selectable */
3012 e_calendar_item_get_max_days_sel (ECalendarItem
*calitem
)
3014 return calitem
->max_days_selected
;
3017 /* Set the maximum number of days selectable */
3019 e_calendar_item_set_max_days_sel (ECalendarItem
*calitem
,
3022 calitem
->max_days_selected
= MAX (0, days
);
3023 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
3026 /* Get the maximum number of days before whole weeks are selected */
3028 e_calendar_item_get_days_start_week_sel (ECalendarItem
*calitem
)
3030 return calitem
->days_to_start_week_selection
;
3033 /* Set the maximum number of days before whole weeks are selected */
3035 e_calendar_item_set_days_start_week_sel (ECalendarItem
*calitem
,
3038 calitem
->days_to_start_week_selection
= days
;
3042 e_calendar_item_get_display_popup (ECalendarItem
*calitem
)
3044 return calitem
->display_popup
;
3048 e_calendar_item_set_display_popup (ECalendarItem
*calitem
,
3051 calitem
->display_popup
= display
;
3054 /* This will make sure that the given year & month are valid, i.e. if month
3055 * is < 0 or > 11 the year and month will be updated accordingly. */
3057 e_calendar_item_normalize_date (ECalendarItem
*calitem
,
3062 *year
+= *month
/ 12;
3063 *month
= *month
% 12;
3065 *year
+= *month
/ 12 - 1;
3066 *month
= *month
% 12;
3072 /* Adds or subtracts days from the selection. It is used when we switch months
3073 * and the selection extends past the end of a month but we want to keep the
3074 * number of days selected the same. days should not be more than 30. */
3076 e_calendar_item_add_days_to_selection (ECalendarItem
*calitem
,
3079 gint year
, month
, days_in_month
;
3081 year
= calitem
->year
;
3082 month
= calitem
->month
+ calitem
->selection_end_month_offset
;
3083 e_calendar_item_normalize_date (calitem
, &year
, &month
);
3085 calitem
->selection_end_day
+= days
;
3086 if (calitem
->selection_end_day
<= 0) {
3088 e_calendar_item_normalize_date (calitem
, &year
, &month
);
3089 calitem
->selection_end_month_offset
--;
3090 calitem
->selection_end_day
+= DAYS_IN_MONTH (year
, month
);
3092 days_in_month
= DAYS_IN_MONTH (year
, month
);
3093 if (calitem
->selection_end_day
> days_in_month
) {
3094 calitem
->selection_end_month_offset
++;
3095 calitem
->selection_end_day
-= days_in_month
;
3100 /* Gets the range of dates actually shown. Months are 0 to 11.
3101 * This also includes the last days of the previous month and the first days
3102 * of the following month, which are normally shown in gray.
3103 * It returns FALSE if no dates are currently shown. */
3105 e_calendar_item_get_date_range (ECalendarItem
*calitem
,
3113 gint first_day_offset
, days_in_month
, days_in_prev_month
;
3115 if (calitem
->rows
== 0 || calitem
->cols
== 0)
3118 /* Calculate the first day shown. This will be one of the greyed-out
3119 * days before the first full month begins. */
3120 e_calendar_item_get_month_info (
3121 calitem
, 0, 0, &first_day_offset
,
3122 &days_in_month
, &days_in_prev_month
);
3123 *start_year
= calitem
->year
;
3124 *start_month
= calitem
->month
- 1;
3125 if (*start_month
== -1) {
3129 *start_day
= days_in_prev_month
+ 1 - first_day_offset
;
3131 /* Calculate the last day shown. This will be one of the greyed-out
3132 * days after the last full month ends. */
3133 e_calendar_item_get_month_info (
3134 calitem
, calitem
->rows
- 1,
3135 calitem
->cols
- 1, &first_day_offset
,
3136 &days_in_month
, &days_in_prev_month
);
3137 *end_month
= calitem
->month
+ calitem
->rows
* calitem
->cols
;
3138 *end_year
= calitem
->year
+ *end_month
/ 12;
3140 *end_day
= E_CALENDAR_ROWS_PER_MONTH
* E_CALENDAR_COLS_PER_MONTH
3141 - first_day_offset
- days_in_month
;
3146 /* Simple way to mark days so they appear bold.
3147 * A more flexible interface may be added later. */
3149 e_calendar_item_clear_marks (ECalendarItem
*calitem
)
3151 GnomeCanvasItem
*item
;
3153 item
= GNOME_CANVAS_ITEM (calitem
);
3155 g_free (calitem
->styles
);
3156 calitem
->styles
= NULL
;
3158 gnome_canvas_request_redraw (
3159 item
->canvas
, item
->x1
, item
->y1
,
3160 item
->x2
, item
->y2
);
3163 /* add_day_style - whether bit-or with the actual style or change the style fully */
3165 e_calendar_item_mark_day (ECalendarItem
*calitem
,
3170 gboolean add_day_style
)
3175 month_offset
= (year
- calitem
->year
) * 12 + month
- calitem
->month
;
3176 if (month_offset
< -1 || month_offset
> calitem
->rows
* calitem
->cols
)
3179 if (!calitem
->styles
)
3180 calitem
->styles
= g_new0 (guint8
, (calitem
->rows
* calitem
->cols
+ 2) * 32);
3182 index
= (month_offset
+ 1) * 32 + day
;
3183 calitem
->styles
[index
] = day_style
|
3184 (add_day_style
? calitem
->styles
[index
] : 0);
3186 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
3190 e_calendar_item_mark_days (ECalendarItem
*calitem
,
3198 gboolean add_day_style
)
3200 gint month_offset
, end_month_offset
, day
;
3202 month_offset
= (start_year
- calitem
->year
) * 12 + start_month
3205 if (month_offset
> calitem
->rows
* calitem
->cols
)
3207 if (month_offset
< -1) {
3212 end_month_offset
= (end_year
- calitem
->year
) * 12 + end_month
3214 if (end_month_offset
< -1)
3216 if (end_month_offset
> calitem
->rows
* calitem
->cols
) {
3217 end_month_offset
= calitem
->rows
* calitem
->cols
;
3221 if (month_offset
> end_month_offset
)
3224 if (!calitem
->styles
)
3225 calitem
->styles
= g_new0 (guint8
, (calitem
->rows
* calitem
->cols
+ 2) * 32);
3230 if (month_offset
== end_month_offset
&& day
> end_day
)
3233 if (month_offset
< -1 || month_offset
> calitem
->rows
* calitem
->cols
)
3234 g_warning ("Bad month offset: %i\n", month_offset
);
3235 if (day
< 1 || day
> 31)
3236 g_warning ("Bad day: %i\n", day
);
3239 g_print ("Marking Month:%i Day:%i\n", month_offset
, day
);
3241 index
= (month_offset
+ 1) * 32 + day
;
3242 calitem
->styles
[index
] = day_style
|
3243 (add_day_style
? calitem
->styles
[index
] : 0);
3249 if (month_offset
> end_month_offset
)
3254 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
3257 /* Rounds up the given day to the end of the week. */
3259 e_calendar_item_round_up_selection (ECalendarItem
*calitem
,
3263 GDateWeekday weekday
;
3264 gint year
, month
, days
, days_in_month
;
3265 struct tm tmp_tm
= { 0 };
3267 year
= calitem
->year
;
3268 month
= calitem
->month
+ *month_offset
;
3269 e_calendar_item_normalize_date (calitem
, &year
, &month
);
3271 tmp_tm
.tm_year
= year
- 1900;
3272 tmp_tm
.tm_mon
= month
;
3273 tmp_tm
.tm_mday
= *day
;
3274 tmp_tm
.tm_isdst
= -1;
3277 /* Calculate how many days to the end of the row. */
3278 weekday
= e_weekday_from_tm_wday (tmp_tm
.tm_wday
);
3279 days
= e_weekday_get_days_between (weekday
, calitem
->week_start_day
);
3282 days_in_month
= DAYS_IN_MONTH (year
, month
);
3283 if (*day
> days_in_month
) {
3285 *day
-= days_in_month
;
3289 /* Rounds down the given day to the start of the week. */
3291 e_calendar_item_round_down_selection (ECalendarItem
*calitem
,
3295 GDateWeekday weekday
;
3296 gint year
, month
, days
, days_in_month
;
3297 struct tm tmp_tm
= { 0 };
3299 year
= calitem
->year
;
3300 month
= calitem
->month
+ *month_offset
;
3301 e_calendar_item_normalize_date (calitem
, &year
, &month
);
3303 tmp_tm
.tm_year
= year
- 1900;
3304 tmp_tm
.tm_mon
= month
;
3305 tmp_tm
.tm_mday
= *day
;
3306 tmp_tm
.tm_isdst
= -1;
3309 /* Calculate how many days to the start of the row. */
3310 weekday
= e_weekday_from_tm_wday (tmp_tm
.tm_wday
);
3311 days
= e_weekday_get_days_between (weekday
, calitem
->week_start_day
);
3320 days_in_month
= DAYS_IN_MONTH (year
, month
);
3322 *day
+= days_in_month
;
3327 e_calendar_item_get_inclusive_days (ECalendarItem
*calitem
,
3328 gint start_month_offset
,
3330 gint end_month_offset
,
3333 gint start_year
, start_month
, end_year
, end_month
, days
= 0;
3335 start_year
= calitem
->year
;
3336 start_month
= calitem
->month
+ start_month_offset
;
3337 e_calendar_item_normalize_date (calitem
, &start_year
, &start_month
);
3339 end_year
= calitem
->year
;
3340 end_month
= calitem
->month
+ end_month_offset
;
3341 e_calendar_item_normalize_date (calitem
, &end_year
, &end_month
);
3343 while (start_year
< end_year
|| start_month
< end_month
) {
3344 days
+= DAYS_IN_MONTH (start_year
, start_month
);
3346 if (start_month
== 12) {
3352 days
+= end_day
- start_day
+ 1;
3357 /* If the day is off the end of the month it is set to the last day of the
3360 e_calendar_item_ensure_valid_day (ECalendarItem
*calitem
,
3364 gint year
, month
, days_in_month
;
3366 year
= calitem
->year
;
3367 month
= calitem
->month
+ *month_offset
;
3368 e_calendar_item_normalize_date (calitem
, &year
, &month
);
3370 days_in_month
= DAYS_IN_MONTH (year
, month
);
3371 if (*day
> days_in_month
)
3372 *day
= days_in_month
;
3376 e_calendar_item_get_selection (ECalendarItem
*calitem
,
3380 gint start_year
, start_month
, start_day
;
3381 gint end_year
, end_month
, end_day
;
3383 g_date_clear (start_date
, 1);
3384 g_date_clear (end_date
, 1);
3386 if (!calitem
->selection_set
)
3389 start_year
= calitem
->year
;
3390 start_month
= calitem
->month
+ calitem
->selection_start_month_offset
;
3391 e_calendar_item_normalize_date (calitem
, &start_year
, &start_month
);
3392 start_day
= calitem
->selection_start_day
;
3394 end_year
= calitem
->year
;
3395 end_month
= calitem
->month
+ calitem
->selection_end_month_offset
;
3396 e_calendar_item_normalize_date (calitem
, &end_year
, &end_month
);
3397 end_day
= calitem
->selection_end_day
;
3399 g_date_set_dmy (start_date
, start_day
, start_month
+ 1, start_year
);
3400 g_date_set_dmy (end_date
, end_day
, end_month
+ 1, end_year
);
3406 e_calendar_item_set_selection_if_emission (ECalendarItem
*calitem
,
3407 const GDate
*start_date
,
3408 const GDate
*end_date
,
3411 gint start_year
, start_month
, start_day
;
3412 gint end_year
, end_month
, end_day
;
3413 gint new_start_month_offset
, new_start_day
;
3414 gint new_end_month_offset
, new_end_day
;
3415 gboolean need_update
;
3417 g_return_if_fail (E_IS_CALENDAR_ITEM (calitem
));
3419 /* If start_date is NULL, we clear the selection without changing the
3421 if (start_date
== NULL
) {
3422 calitem
->selection_set
= FALSE
;
3423 calitem
->selection_changed
= TRUE
;
3424 e_calendar_item_queue_signal_emission (calitem
);
3425 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
3429 if (end_date
== NULL
)
3430 end_date
= start_date
;
3432 g_return_if_fail (g_date_compare (start_date
, end_date
) <= 0);
3434 start_year
= g_date_get_year (start_date
);
3435 start_month
= g_date_get_month (start_date
) - 1;
3436 start_day
= g_date_get_day (start_date
);
3437 end_year
= g_date_get_year (end_date
);
3438 end_month
= g_date_get_month (end_date
) - 1;
3439 end_day
= g_date_get_day (end_date
);
3441 need_update
= e_calendar_item_ensure_days_visible (
3451 new_start_month_offset
= (start_year
- calitem
->year
) * 12
3452 + start_month
- calitem
->month
;
3453 new_start_day
= start_day
;
3455 /* This may go outside the visible months, but we don't care. */
3456 new_end_month_offset
= (end_year
- calitem
->year
) * 12
3457 + end_month
- calitem
->month
;
3458 new_end_day
= end_day
;
3460 if (!calitem
->selection_set
3461 || calitem
->selection_start_month_offset
!= new_start_month_offset
3462 || calitem
->selection_start_day
!= new_start_day
3463 || calitem
->selection_end_month_offset
!= new_end_month_offset
3464 || calitem
->selection_end_day
!= new_end_day
) {
3467 calitem
->selection_changed
= TRUE
;
3468 e_calendar_item_queue_signal_emission (calitem
);
3470 calitem
->selection_set
= TRUE
;
3471 calitem
->selection_start_month_offset
= new_start_month_offset
;
3472 calitem
->selection_start_day
= new_start_day
;
3473 calitem
->selection_end_month_offset
= new_end_month_offset
;
3474 calitem
->selection_end_day
= new_end_day
;
3476 calitem
->selection_real_start_month_offset
= new_start_month_offset
;
3477 calitem
->selection_real_start_day
= new_start_day
;
3478 calitem
->selection_from_full_week
= FALSE
;
3484 e_calendar_item_signals
[DATE_RANGE_CHANGED
], 0);
3485 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem
));
3490 e_calendar_item_style_updated (GtkWidget
*widget
,
3491 ECalendarItem
*calitem
)
3493 GdkRGBA unfocused_selected_bg
, selected_bg
, fg
, base_bg
;
3495 e_utils_get_theme_color (widget
, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR
, &selected_bg
);
3496 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
);
3497 e_utils_get_theme_color (widget
, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR
, &fg
);
3498 e_utils_get_theme_color (widget
, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR
, &base_bg
);
3500 if (gdk_rgba_equal (&selected_bg
, &unfocused_selected_bg
))
3501 e_utils_get_theme_color (widget
, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR
, &selected_bg
);
3503 e_rgba_to_color (&selected_bg
, &calitem
->colors
[E_CALENDAR_ITEM_COLOR_TODAY_BOX
]);
3504 e_rgba_to_color (&base_bg
, &calitem
->colors
[E_CALENDAR_ITEM_COLOR_SELECTION_FG
]);
3505 e_rgba_to_color (&unfocused_selected_bg
, &calitem
->colors
[E_CALENDAR_ITEM_COLOR_SELECTION_BG_FOCUSED
]);
3506 e_rgba_to_color (&fg
, &calitem
->colors
[E_CALENDAR_ITEM_COLOR_SELECTION_BG
]);
3507 e_rgba_to_color (&fg
, &calitem
->colors
[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG
]);
3509 e_calendar_item_recalc_sizes (calitem
);
3513 e_calendar_item_set_selection (ECalendarItem
*calitem
,
3514 const GDate
*start_date
,
3515 const GDate
*end_date
)
3517 GDate current_start_date
, current_end_date
;
3519 /* If the user is in the middle of a selection, we must abort it. */
3520 if (calitem
->selecting
) {
3521 gnome_canvas_item_ungrab (
3522 GNOME_CANVAS_ITEM (calitem
),
3524 calitem
->selecting
= FALSE
;
3527 if (e_calendar_item_get_selection (calitem
, ¤t_start_date
, ¤t_end_date
)) {
3528 /* No change, no need to recalculate anything */
3529 if (start_date
&& end_date
&& g_date_valid (start_date
) && g_date_valid (end_date
) &&
3530 g_date_compare (start_date
, ¤t_start_date
) == 0 &&
3531 g_date_compare (end_date
, ¤t_end_date
) == 0)
3535 e_calendar_item_set_selection_if_emission (calitem
,
3536 start_date
, end_date
,
3540 /* This tries to ensure that the given time range is visible. If the range
3541 * given is longer than we can show, only the start of it will be visible.
3542 * Note that this will not update the selection. That should be done somewhere
3543 * else. It returns TRUE if the visible range has been changed. */
3545 e_calendar_item_ensure_days_visible (ECalendarItem
*calitem
,
3554 gint current_end_year
, current_end_month
;
3556 gint first_day_offset
, days_in_month
, days_in_prev_month
;
3557 gboolean need_update
= FALSE
;
3559 months_shown
= calitem
->rows
* calitem
->cols
;
3561 /* Calculate the range of months currently displayed. */
3562 current_end_year
= calitem
->year
;
3563 current_end_month
= calitem
->month
+ months_shown
- 1;
3564 e_calendar_item_normalize_date (
3565 calitem
, ¤t_end_year
,
3566 ¤t_end_month
);
3568 /* Try to ensure that the end month is shown. */
3569 if ((end_year
== current_end_year
+ 1 &&
3570 current_end_month
== 11 && end_month
== 0) ||
3571 (end_year
== current_end_year
&& end_month
== current_end_month
+ 1)) {
3572 /* See if the end of the selection will fit in the
3573 * leftover days of the month after the last one shown. */
3574 calitem
->month
+= (months_shown
- 1);
3575 e_calendar_item_normalize_date (
3576 calitem
, &calitem
->year
,
3579 e_calendar_item_get_month_info (
3583 &days_in_prev_month
);
3585 if (end_day
>= E_CALENDAR_ROWS_PER_MONTH
* E_CALENDAR_COLS_PER_MONTH
-
3586 first_day_offset
- days_in_month
) {
3589 calitem
->year
= end_year
;
3590 calitem
->month
= end_month
- months_shown
+ 1;
3592 calitem
->month
-= (months_shown
- 1);
3595 e_calendar_item_normalize_date (
3596 calitem
, &calitem
->year
,
3599 else if (end_year
> current_end_year
||
3600 (end_year
== current_end_year
&& end_month
> current_end_month
)) {
3601 /* The selection will definitely not fit in the leftover days
3602 * of the month after the last one shown. */
3605 calitem
->year
= end_year
;
3606 calitem
->month
= end_month
- months_shown
+ 1;
3608 e_calendar_item_normalize_date (
3609 calitem
, &calitem
->year
,
3613 /* Now try to ensure that the start month is shown. We do this after
3614 * the end month so that the start month will always be shown. */
3615 if (start_year
< calitem
->year
3616 || (start_year
== calitem
->year
3617 && start_month
< calitem
->month
)) {
3620 /* First we see if the start of the selection will fit in the
3621 * leftover days of the month before the first one shown. */
3622 calitem
->year
= start_year
;
3623 calitem
->month
= start_month
+ 1;
3624 e_calendar_item_normalize_date (
3625 calitem
, &calitem
->year
,
3628 e_calendar_item_get_month_info (
3632 &days_in_prev_month
);
3634 if (start_day
<= days_in_prev_month
- first_day_offset
) {
3635 calitem
->year
= start_year
;
3636 calitem
->month
= start_month
;
3640 if (need_update
&& emission
)
3641 e_calendar_item_date_range_changed (calitem
);
3647 e_calendar_item_show_popup_menu (ECalendarItem
*calitem
,
3648 GdkEvent
*button_event
,
3651 GtkWidget
*menu
, *submenu
, *menuitem
, *label
;
3652 GtkWidget
*canvas_widget
;
3657 menu
= gtk_menu_new ();
3659 for (year
= calitem
->year
- 2; year
<= calitem
->year
+ 2; year
++) {
3660 g_snprintf (buffer
, 64, "%i", year
);
3661 menuitem
= gtk_menu_item_new_with_label (buffer
);
3662 gtk_widget_show (menuitem
);
3663 gtk_container_add (GTK_CONTAINER (menu
), menuitem
);
3665 submenu
= gtk_menu_new ();
3666 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem
), submenu
);
3669 G_OBJECT (submenu
), "year",
3670 GINT_TO_POINTER (year
));
3672 G_OBJECT (submenu
), "month_offset",
3673 GINT_TO_POINTER (month_offset
));
3675 for (month
= 0; month
< 12; month
++) {
3676 name
= e_get_month_name (month
+ 1, FALSE
);
3678 menuitem
= gtk_menu_item_new ();
3679 gtk_widget_show (menuitem
);
3680 gtk_container_add (GTK_CONTAINER (submenu
), menuitem
);
3682 label
= gtk_label_new (name
);
3683 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
3684 gtk_widget_show (label
);
3685 gtk_container_add (GTK_CONTAINER (menuitem
), label
);
3688 G_OBJECT (menuitem
), "month",
3689 GINT_TO_POINTER (month
));
3692 menuitem
, "activate",
3693 G_CALLBACK (e_calendar_item_on_menu_item_activate
),
3700 G_CALLBACK (gtk_menu_detach
), NULL
);
3702 canvas_widget
= GTK_WIDGET (calitem
->canvas_item
.canvas
);
3703 gtk_menu_attach_to_widget (GTK_MENU (menu
), canvas_widget
, NULL
);
3704 gtk_menu_popup_at_pointer (GTK_MENU (menu
), button_event
);
3708 e_calendar_item_on_menu_item_activate (GtkWidget
*menuitem
,
3709 ECalendarItem
*calitem
)
3712 gint year
, month_offset
, month
;
3715 parent
= gtk_widget_get_parent (menuitem
);
3716 data
= g_object_get_data (G_OBJECT (parent
), "year");
3717 year
= GPOINTER_TO_INT (data
);
3719 parent
= gtk_widget_get_parent (menuitem
);
3720 data
= g_object_get_data (G_OBJECT (parent
), "month_offset");
3721 month_offset
= GPOINTER_TO_INT (data
);
3723 data
= g_object_get_data (G_OBJECT (menuitem
), "month");
3724 month
= GPOINTER_TO_INT (data
);
3726 month
-= month_offset
;
3727 e_calendar_item_normalize_date (calitem
, &year
, &month
);
3728 e_calendar_item_set_first_month_with_emit (calitem
, year
, month
, TRUE
);
3731 /* Sets the function to call to get the colors to use for a particular day. */
3733 e_calendar_item_set_style_callback (ECalendarItem
*calitem
,
3734 ECalendarItemStyleCallback cb
,
3736 GDestroyNotify destroy
)
3738 g_return_if_fail (E_IS_CALENDAR_ITEM (calitem
));
3740 if (calitem
->style_callback_data
&& calitem
->style_callback_destroy
)
3741 (*calitem
->style_callback_destroy
) (calitem
->style_callback_data
);
3743 calitem
->style_callback
= cb
;
3744 calitem
->style_callback_data
= data
;
3745 calitem
->style_callback_destroy
= destroy
;
3749 e_calendar_item_date_range_changed (ECalendarItem
*calitem
)
3751 g_free (calitem
->styles
);
3752 calitem
->styles
= NULL
;
3753 calitem
->date_range_changed
= TRUE
;
3754 e_calendar_item_queue_signal_emission (calitem
);
3758 e_calendar_item_queue_signal_emission (ECalendarItem
*calitem
)
3760 if (calitem
->signal_emission_idle_id
== 0) {
3761 calitem
->signal_emission_idle_id
= g_idle_add_full (
3762 G_PRIORITY_HIGH
, (GSourceFunc
)
3763 e_calendar_item_signal_emission_idle_cb
,
3769 e_calendar_item_signal_emission_idle_cb (gpointer data
)
3771 ECalendarItem
*calitem
;
3773 g_return_val_if_fail (E_IS_CALENDAR_ITEM (data
), FALSE
);
3775 calitem
= E_CALENDAR_ITEM (data
);
3777 calitem
->signal_emission_idle_id
= 0;
3779 /* We ref the calitem & check in case it gets destroyed, since we
3780 * were getting a free memory write here. */
3781 g_object_ref ((calitem
));
3783 if (calitem
->date_range_changed
) {
3784 calitem
->date_range_changed
= FALSE
;
3785 g_signal_emit (calitem
, e_calendar_item_signals
[DATE_RANGE_CHANGED
], 0);
3788 if (calitem
->selection_changed
) {
3789 calitem
->selection_changed
= FALSE
;
3790 g_signal_emit (calitem
, e_calendar_item_signals
[SELECTION_CHANGED
], 0);
3793 g_object_unref ((calitem
));
3798 /* Sets a callback to use to get the current time. This is useful if the
3799 * application needs to use its own timezone data rather than rely on the
3802 e_calendar_item_set_get_time_callback (ECalendarItem
*calitem
,
3803 ECalendarItemGetTimeCallback cb
,
3805 GDestroyNotify destroy
)
3807 g_return_if_fail (E_IS_CALENDAR_ITEM (calitem
));
3809 if (calitem
->time_callback_data
&& calitem
->time_callback_destroy
)
3810 (*calitem
->time_callback_destroy
) (calitem
->time_callback_data
);
3812 calitem
->time_callback
= cb
;
3813 calitem
->time_callback_data
= data
;
3814 calitem
->time_callback_destroy
= destroy
;