2 * EDayViewTimeItem - canvas item which displays the times down the left of
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 * Damon Chaplin <damon@ximian.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
23 #include "evolution-config.h"
27 #include <glib/gi18n.h>
29 #include "e-day-view-time-item.h"
30 #include "calendar-config.h"
32 /* The spacing between items in the time column. GRID_X_PAD is the space down
33 * either side of the column, i.e. outside the main horizontal grid lines.
34 * HOUR_L_PAD & HOUR_R_PAD are the spaces on the left & right side of the
35 * big hour number (this is inside the horizontal grid lines).
36 * MIN_X_PAD is the spacing either side of the minute number. The smaller
37 * horizontal grid lines match with this.
38 * 60_MIN_X_PAD is the space either side of the HH:MM display used when
39 * we are displaying 60 mins per row (inside the main grid lines).
40 * LARGE_HOUR_Y_PAD is the offset of the large hour string from the top of the
42 * SMALL_FONT_Y_PAD is the offset of the small time/minute string from the top
44 #define E_DVTMI_TIME_GRID_X_PAD 4
45 #define E_DVTMI_HOUR_L_PAD 4
46 #define E_DVTMI_HOUR_R_PAD 2
47 #define E_DVTMI_MIN_X_PAD 2
48 #define E_DVTMI_60_MIN_X_PAD 4
49 #define E_DVTMI_LARGE_HOUR_Y_PAD 1
50 #define E_DVTMI_SMALL_FONT_Y_PAD 1
52 #define E_DAY_VIEW_TIME_ITEM_GET_PRIVATE(obj) \
53 (G_TYPE_INSTANCE_GET_PRIVATE \
54 ((obj), E_TYPE_DAY_VIEW_TIME_ITEM, EDayViewTimeItemPrivate))
56 struct _EDayViewTimeItemPrivate
{
57 /* The parent EDayView widget. */
60 /* The width of the time column. */
63 /* TRUE if we are currently dragging the selection times. */
64 gboolean dragging_selection
;
66 /* The second timezone if shown, or else NULL. */
67 icaltimezone
*second_zone
;
70 static void e_day_view_time_item_update (GnomeCanvasItem
*item
,
71 const cairo_matrix_t
*i2c
,
73 static void e_day_view_time_item_draw (GnomeCanvasItem
*item
,
79 static GnomeCanvasItem
*
80 e_day_view_time_item_point (GnomeCanvasItem
*item
,
85 static gint
e_day_view_time_item_event (GnomeCanvasItem
*item
,
87 static void e_day_view_time_item_increment_time
91 static void e_day_view_time_item_show_popup_menu
92 (EDayViewTimeItem
*time_item
,
94 static void e_day_view_time_item_on_set_divisions
96 EDayViewTimeItem
*time_item
);
97 static void e_day_view_time_item_on_button_press
98 (EDayViewTimeItem
*time_item
,
100 static void e_day_view_time_item_on_button_release
101 (EDayViewTimeItem
*time_item
,
103 static void e_day_view_time_item_on_motion_notify
104 (EDayViewTimeItem
*time_item
,
106 static gint e_day_view_time_item_convert_position_to_row
107 (EDayViewTimeItem
*time_item
,
110 static void edvti_second_zone_changed_cb (GSettings
*settings
,
121 e_day_view_time_item
,
122 GNOME_TYPE_CANVAS_ITEM
)
125 day_view_time_item_set_property (GObject
*object
,
130 switch (property_id
) {
132 e_day_view_time_item_set_day_view (
133 E_DAY_VIEW_TIME_ITEM (object
),
134 g_value_get_object (value
));
138 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
142 day_view_time_item_get_property (GObject
*object
,
147 switch (property_id
) {
150 value
, e_day_view_time_item_get_day_view (
151 E_DAY_VIEW_TIME_ITEM (object
)));
155 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
159 day_view_time_item_dispose (GObject
*object
)
161 EDayViewTimeItemPrivate
*priv
;
163 priv
= E_DAY_VIEW_TIME_ITEM_GET_PRIVATE (object
);
165 if (priv
->day_view
!= NULL
) {
166 g_object_unref (priv
->day_view
);
167 priv
->day_view
= NULL
;
170 /* Chain up to parent's dispose() method. */
171 G_OBJECT_CLASS (e_day_view_time_item_parent_class
)->dispose (object
);
175 day_view_time_item_finalize (GObject
*object
)
177 EDayViewTimeItem
*time_item
;
179 time_item
= E_DAY_VIEW_TIME_ITEM (object
);
181 calendar_config_remove_notification (
182 (CalendarConfigChangedFunc
)
183 edvti_second_zone_changed_cb
, time_item
);
185 /* Chain up to parent's dispose() method. */
186 G_OBJECT_CLASS (e_day_view_time_item_parent_class
)->finalize (object
);
190 e_day_view_time_item_class_init (EDayViewTimeItemClass
*class)
192 GObjectClass
*object_class
;
193 GnomeCanvasItemClass
*item_class
;
195 g_type_class_add_private (class, sizeof (EDayViewTimeItemPrivate
));
197 object_class
= G_OBJECT_CLASS (class);
198 object_class
->set_property
= day_view_time_item_set_property
;
199 object_class
->get_property
= day_view_time_item_get_property
;
200 object_class
->dispose
= day_view_time_item_dispose
;
201 object_class
->finalize
= day_view_time_item_finalize
;
203 item_class
= GNOME_CANVAS_ITEM_CLASS (class);
204 item_class
->update
= e_day_view_time_item_update
;
205 item_class
->draw
= e_day_view_time_item_draw
;
206 item_class
->point
= e_day_view_time_item_point
;
207 item_class
->event
= e_day_view_time_item_event
;
209 g_object_class_install_property (
212 g_param_spec_object (
221 e_day_view_time_item_init (EDayViewTimeItem
*time_item
)
225 time_item
->priv
= E_DAY_VIEW_TIME_ITEM_GET_PRIVATE (time_item
);
227 last
= calendar_config_get_day_second_zone ();
231 time_item
->priv
->second_zone
=
232 icaltimezone_get_builtin_timezone (last
);
236 calendar_config_add_notification_day_second_zone (
237 (CalendarConfigChangedFunc
) edvti_second_zone_changed_cb
,
242 e_day_view_time_item_update (GnomeCanvasItem
*item
,
243 const cairo_matrix_t
*i2c
,
246 if (GNOME_CANVAS_ITEM_CLASS (e_day_view_time_item_parent_class
)->update
)
247 (* GNOME_CANVAS_ITEM_CLASS (e_day_view_time_item_parent_class
)->update
) (item
, i2c
, flags
);
249 /* The item covers the entire canvas area. */
257 * DRAWING ROUTINES - functions to paint the canvas item.
260 edvti_draw_zone (GnomeCanvasItem
*canvas_item
,
267 icaltimezone
*use_zone
)
270 EDayViewTimeItem
*time_item
;
271 ECalendarView
*cal_view
;
274 gchar buffer
[64], *midnight_day
= NULL
, *midnight_month
= NULL
;
276 gint hour
, display_hour
, minute
, row
;
277 gint row_y
, start_y
, large_hour_y_offset
, small_font_y_offset
;
278 gint long_line_x1
, long_line_x2
, short_line_x1
;
279 gint large_hour_x2
, minute_x2
;
280 gint hour_width
, minute_width
, suffix_width
;
281 gint max_suffix_width
, max_minute_or_suffix_width
;
283 PangoContext
*context
;
284 PangoFontMetrics
*large_font_metrics
, *small_font_metrics
;
289 time_item
= E_DAY_VIEW_TIME_ITEM (canvas_item
);
290 day_view
= e_day_view_time_item_get_day_view (time_item
);
291 g_return_if_fail (day_view
!= NULL
);
293 widget
= GTK_WIDGET (day_view
);
294 cal_view
= E_CALENDAR_VIEW (day_view
);
295 model
= e_calendar_view_get_model (cal_view
);
296 time_divisions
= e_calendar_view_get_time_divisions (cal_view
);
298 context
= gtk_widget_get_pango_context (GTK_WIDGET (day_view
));
299 small_font_metrics
= pango_context_get_metrics (
301 pango_context_get_language (context
));
302 large_font_metrics
= pango_context_get_metrics (
303 context
, day_view
->large_font_desc
,
304 pango_context_get_language (context
));
306 e_utils_get_theme_color (widget
, "theme_fg_color,theme_text_color", E_UTILS_DEFAULT_THEME_FG_COLOR
, &fg
);
307 e_utils_get_theme_color (widget
, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR
, &dark
);
309 /* The start and end of the long horizontal line between hours. */
311 (use_zone
? 0 : E_DVTMI_TIME_GRID_X_PAD
) - x
+ x_offset
;
313 time_item
->priv
->column_width
-
314 E_DVTMI_TIME_GRID_X_PAD
- x
-
315 (use_zone
? E_DVTMI_TIME_GRID_X_PAD
: 0) + x_offset
;
317 if (time_divisions
== 60) {
318 /* The right edge of the complete time string in 60-min
319 * divisions, e.g. "14:00" or "2 pm". */
320 minute_x2
= long_line_x2
- E_DVTMI_60_MIN_X_PAD
;
322 /* These aren't used for 60-minute divisions, but we initialize
323 * them to keep gcc happy. */
327 max_suffix_width
= MAX (
328 day_view
->am_string_width
,
329 day_view
->pm_string_width
);
331 max_minute_or_suffix_width
= MAX (
333 day_view
->max_minute_width
);
335 /* The start of the short horizontal line between the periods
336 * within each hour. */
337 short_line_x1
= long_line_x2
- E_DVTMI_MIN_X_PAD
* 2
338 - max_minute_or_suffix_width
;
340 /* The right edge of the large hour string. */
341 large_hour_x2
= short_line_x1
- E_DVTMI_HOUR_R_PAD
;
343 /* The right edge of the minute part of the time. */
344 minute_x2
= long_line_x2
- E_DVTMI_MIN_X_PAD
;
347 /* Start with the first hour & minute shown in the EDayView. */
348 hour
= day_view
->first_hour_shown
;
349 minute
= day_view
->first_minute_shown
;
352 /* shift time with a difference between
353 * local time and the other timezone */
354 icaltimezone
*cal_zone
;
355 struct icaltimetype tt
;
359 cal_zone
= e_calendar_view_get_timezone (
360 E_CALENDAR_VIEW (day_view
));
361 tt
= icaltime_from_timet_with_zone (
362 day_view
->day_starts
[0], 0, cal_zone
);
364 /* diff is number of minutes */
365 diff
=(icaltimezone_get_utc_offset (use_zone
, &tt
, NULL
) -
366 icaltimezone_get_utc_offset (cal_zone
, &tt
, NULL
)) / 60;
368 tt
= icaltime_from_timet_with_zone (day_view
->day_starts
[0], 0, cal_zone
);
370 icaltime_set_timezone (&tt
, cal_zone
);
371 tt
= icaltime_convert_to_zone (tt
, use_zone
);
374 /* shows the next midnight */
375 icaltime_adjust (&tt
, 1, 0, 0, 0);
378 mn
= icaltimetype_to_tm (&tt
);
380 /* up to two characters/numbers */
381 e_utf8_strftime (buffer
, sizeof (buffer
), "%d", &mn
);
382 midnight_day
= g_strdup (buffer
);
383 /* up to three characters, abbreviated month name */
384 e_utf8_strftime (buffer
, sizeof (buffer
), "%b", &mn
);
385 midnight_month
= g_strdup (buffer
);
387 minute
+= (diff
% 60);
388 hour
+= (diff
/ 60) + (minute
/ 60);
390 minute
= minute
% 60;
396 hour
= (hour
+ 48) % 24;
399 /* The offset of the large hour string from the top of the row. */
400 large_hour_y_offset
= E_DVTMI_LARGE_HOUR_Y_PAD
;
402 /* The offset of the small time/minute string from top of row. */
403 small_font_y_offset
= E_DVTMI_SMALL_FONT_Y_PAD
;
405 /* Calculate the minimum y position of the first row we need to draw.
406 * This is normally one row height above the 0 position, but if we
407 * are using the large font we may have to go back a bit further. */
408 start_y
= 0 - MAX (day_view
->row_height
,
409 (pango_font_metrics_get_ascent (large_font_metrics
) +
410 pango_font_metrics_get_descent (large_font_metrics
)) / PANGO_SCALE
+
411 E_DVTMI_LARGE_HOUR_Y_PAD
);
413 /* Draw the Marcus Bains Line first, so it appears under other elements. */
414 if (e_day_view_marcus_bains_get_show_line (day_view
)) {
415 struct icaltimetype time_now
;
416 const gchar
*marcus_bains_time_bar_color
;
420 gdk_cairo_set_source_color (
421 cr
, &day_view
->colors
[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE
]);
423 marcus_bains_time_bar_color
=
424 e_day_view_marcus_bains_get_time_bar_color (day_view
);
425 if (marcus_bains_time_bar_color
== NULL
)
426 marcus_bains_time_bar_color
= "";
428 if (gdk_color_parse (marcus_bains_time_bar_color
, &mb_color
)) {
429 gdk_cairo_set_source_color (cr
, &mb_color
);
431 mb_color
= day_view
->colors
[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE
];
434 time_now
= icaltime_current_time_with_zone (
435 e_calendar_view_get_timezone (
436 E_CALENDAR_VIEW (day_view
)));
438 (time_now
.hour
* 60 + time_now
.minute
) *
439 day_view
->row_height
/ time_divisions
- y
;
440 cairo_set_line_width (cr
, 1.5);
443 (use_zone
? E_DVTMI_TIME_GRID_X_PAD
: 0),
445 cairo_line_to (cr
, long_line_x2
, marcus_bains_y
);
449 const gchar
*marcus_bains_time_bar_color
;
451 marcus_bains_time_bar_color
=
452 e_day_view_marcus_bains_get_time_bar_color (day_view
);
453 if (marcus_bains_time_bar_color
== NULL
)
454 marcus_bains_time_bar_color
= "";
456 mb_color
= day_view
->colors
[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE
];
458 if (gdk_color_parse (marcus_bains_time_bar_color
, &mb_color
))
459 gdk_cairo_set_source_color (cr
, &mb_color
);
462 /* Step through each row, drawing the times and the horizontal lines
464 for (row
= 0, row_y
= 0 - y
;
465 row
< day_view
->rows
&& row_y
< height
;
466 row
++, row_y
+= day_view
->row_height
) {
467 gboolean show_midnight_date
;
470 use_zone
&& hour
== 0 &&
471 (minute
== 0 || time_divisions
== 60) &&
472 midnight_day
&& midnight_month
;
474 /* If the row is above the first row we want to draw just
475 * increment the time and skip to the next row. */
476 if (row_y
< start_y
) {
477 e_day_view_time_item_increment_time (
478 &hour
, &minute
, time_divisions
);
482 /* Calculate the actual hour number to display. For 12-hour
483 * format we convert 0-23 to 12-11am / 12 - 11pm. */
484 e_day_view_convert_time_to_display (
487 &suffix
, &suffix_width
);
489 if (time_divisions
== 60) {
490 /* 60 minute intervals - draw a long horizontal line
491 * between hours and display as one long string,
492 * e.g. "14:00" or "2 pm". */
494 gdk_cairo_set_source_rgba (cr
, &dark
);
496 cairo_set_line_width (cr
, 0.7);
497 cairo_move_to (cr
, long_line_x1
, row_y
);
498 cairo_line_to (cr
, long_line_x2
, row_y
);
502 if (show_midnight_date
) {
503 g_snprintf (buffer
, sizeof (buffer
), "%s %s", midnight_day
, midnight_month
);
504 } else if (e_cal_model_get_use_24_hour_format (model
)) {
506 buffer
, sizeof (buffer
), "%i:%02i",
507 display_hour
, minute
);
510 buffer
, sizeof (buffer
), "%i %s",
511 display_hour
, suffix
);
515 if (show_midnight_date
)
516 gdk_cairo_set_source_color (cr
, &mb_color
);
518 gdk_cairo_set_source_rgba (cr
, &fg
);
519 layout
= gtk_widget_create_pango_layout (GTK_WIDGET (day_view
), NULL
);
520 pango_layout_set_text (layout
, buffer
, -1);
521 pango_layout_get_pixel_size (layout
, &minute_width
, NULL
);
523 cr
, minute_x2
- minute_width
,
524 row_y
+ small_font_y_offset
);
525 pango_cairo_update_layout (cr
, layout
);
526 pango_cairo_show_layout (cr
, layout
);
529 g_object_unref (layout
);
533 /* 5/10/15/30 minute intervals. */
536 /* On the hour - draw a long horizontal line
537 * before the hour and display the hour in the
541 gdk_cairo_set_source_rgba (cr
, &dark
);
542 if (show_midnight_date
)
543 g_snprintf (buffer
, sizeof (buffer
), "%s", midnight_day
);
546 buffer
, sizeof (buffer
), "%i",
549 cairo_set_line_width (cr
, 0.7);
550 cairo_move_to (cr
, long_line_x1
, row_y
);
551 cairo_line_to (cr
, long_line_x2
, row_y
);
556 if (show_midnight_date
)
557 gdk_cairo_set_source_color (cr
, &mb_color
);
559 gdk_cairo_set_source_rgba (cr
, &fg
);
560 layout
= gtk_widget_create_pango_layout (GTK_WIDGET (day_view
), NULL
);
561 pango_layout_set_text (layout
, buffer
, -1);
562 pango_layout_set_font_description (
563 layout
, day_view
->large_font_desc
);
564 pango_layout_get_pixel_size (
565 layout
, &hour_width
, NULL
);
567 cr
, large_hour_x2
- hour_width
,
568 row_y
+ large_hour_y_offset
);
569 pango_cairo_update_layout (cr
, layout
);
570 pango_cairo_show_layout (cr
, layout
);
573 g_object_unref (layout
);
575 /* Within the hour - draw a short line before
578 gdk_cairo_set_source_rgba (cr
, &dark
);
579 cairo_set_line_width (cr
, 0.7);
580 cairo_move_to (cr
, short_line_x1
, row_y
);
581 cairo_line_to (cr
, long_line_x2
, row_y
);
586 /* Normally we display the minute in each
587 * interval, but when using 30-minute intervals
588 * we don't display the '30'. */
589 if (time_divisions
!= 30 || minute
!= 30) {
590 /* In 12-hour format we display 'am' or 'pm'
591 * instead of '00'. */
592 if (show_midnight_date
)
593 g_snprintf (buffer
, sizeof (buffer
), "%s", midnight_month
);
595 && !e_cal_model_get_use_24_hour_format (model
)) {
596 g_snprintf (buffer
, sizeof (buffer
), "%s", suffix
);
599 buffer
, sizeof (buffer
),
604 if (show_midnight_date
)
605 gdk_cairo_set_source_color (cr
, &mb_color
);
607 gdk_cairo_set_source_rgba (cr
, &fg
);
608 layout
= gtk_widget_create_pango_layout (GTK_WIDGET (day_view
), NULL
);
609 pango_layout_set_text (layout
, buffer
, -1);
610 pango_layout_set_font_description (
611 layout
, day_view
->small_font_desc
);
612 pango_layout_get_pixel_size (
613 layout
, &minute_width
, NULL
);
615 cr
, minute_x2
- minute_width
,
616 row_y
+ small_font_y_offset
);
617 pango_cairo_update_layout (cr
, layout
);
618 pango_cairo_show_layout (cr
, layout
);
621 g_object_unref (layout
);
625 e_day_view_time_item_increment_time (
630 pango_font_metrics_unref (large_font_metrics
);
631 pango_font_metrics_unref (small_font_metrics
);
633 g_free (midnight_day
);
634 g_free (midnight_month
);
638 e_day_view_time_item_draw (GnomeCanvasItem
*canvas_item
,
645 EDayViewTimeItem
*time_item
;
647 time_item
= E_DAY_VIEW_TIME_ITEM (canvas_item
);
648 g_return_if_fail (time_item
!= NULL
);
650 edvti_draw_zone (canvas_item
, cr
, x
, y
, width
, height
, 0, NULL
);
652 if (time_item
->priv
->second_zone
)
654 canvas_item
, cr
, x
, y
, width
, height
,
655 time_item
->priv
->column_width
,
656 time_item
->priv
->second_zone
);
659 /* Increment the time by the 5/10/15/30/60 minute interval.
660 * Note that time_divisions is never > 60, so we never have to
661 * worry about adding more than 60 minutes. */
663 e_day_view_time_item_increment_time (gint
*hour
,
667 *minute
+= time_divisions
;
670 /* Currently we never wrap around to the next day, but
671 * we may do if we display extra timezones. */
672 *hour
= (*hour
+ 1) % 24;
676 static GnomeCanvasItem
*
677 e_day_view_time_item_point (GnomeCanvasItem
*item
,
687 e_day_view_time_item_event (GnomeCanvasItem
*item
,
690 EDayViewTimeItem
*time_item
;
692 time_item
= E_DAY_VIEW_TIME_ITEM (item
);
694 switch (event
->type
) {
695 case GDK_BUTTON_PRESS
:
696 if (event
->button
.button
== 1) {
697 e_day_view_time_item_on_button_press (time_item
, event
);
698 } else if (event
->button
.button
== 3) {
699 e_day_view_time_item_show_popup_menu (time_item
, event
);
703 case GDK_BUTTON_RELEASE
:
704 if (event
->button
.button
== 1)
705 e_day_view_time_item_on_button_release (
709 case GDK_MOTION_NOTIFY
:
710 e_day_view_time_item_on_motion_notify (time_item
, event
);
721 edvti_second_zone_changed_cb (GSettings
*settings
,
725 EDayViewTimeItem
*time_item
= user_data
;
727 icaltimezone
*second_zone
;
730 g_return_if_fail (user_data
!= NULL
);
731 g_return_if_fail (E_IS_DAY_VIEW_TIME_ITEM (time_item
));
733 location
= calendar_config_get_day_second_zone ();
734 second_zone
= location
? icaltimezone_get_builtin_timezone (location
) : NULL
;
737 if (second_zone
== time_item
->priv
->second_zone
)
740 time_item
->priv
->second_zone
= second_zone
;
742 day_view
= e_day_view_time_item_get_day_view (time_item
);
743 gtk_widget_set_size_request (
744 day_view
->time_canvas
,
745 e_day_view_time_item_get_column_width (time_item
), -1);
746 gtk_widget_queue_draw (day_view
->time_canvas
);
748 e_day_view_update_timezone_name_labels (day_view
);
752 edvti_on_select_zone (GtkWidget
*item
,
753 EDayViewTimeItem
*time_item
)
755 calendar_config_select_day_second_zone (gtk_widget_get_toplevel (item
));
759 edvti_on_set_zone (GtkWidget
*item
,
760 EDayViewTimeItem
*time_item
)
762 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item
)))
765 calendar_config_set_day_second_zone (
766 g_object_get_data (G_OBJECT (item
), "timezone"));
770 e_day_view_time_item_show_popup_menu (EDayViewTimeItem
*time_item
,
773 static gint divisions
[] = { 60, 30, 15, 10, 5 };
775 ECalendarView
*cal_view
;
776 GtkWidget
*menu
, *item
, *submenu
;
778 GSList
*group
= NULL
, *recent_zones
, *s
;
779 gint current_divisions
, i
;
782 day_view
= e_day_view_time_item_get_day_view (time_item
);
783 g_return_if_fail (day_view
!= NULL
);
785 cal_view
= E_CALENDAR_VIEW (day_view
);
786 current_divisions
= e_calendar_view_get_time_divisions (cal_view
);
788 menu
= gtk_menu_new ();
790 /* Make sure the menu is destroyed when it disappears. */
792 menu
, "selection-done",
793 G_CALLBACK (gtk_widget_destroy
), NULL
);
795 for (i
= 0; i
< G_N_ELEMENTS (divisions
); i
++) {
797 buffer
, sizeof (buffer
),
798 /* Translators: %02i is the number of minutes;
799 * this is a context menu entry to change the
800 * length of the time division in the calendar
801 * day view, e.g. a day is displayed in
802 * 24 "60 minute divisions" or
803 * 48 "30 minute divisions". */
804 _("%02i minute divisions"), divisions
[i
]);
805 item
= gtk_radio_menu_item_new_with_label (group
, buffer
);
806 group
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item
));
807 gtk_widget_show (item
);
808 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
810 if (current_divisions
== divisions
[i
])
811 gtk_check_menu_item_set_active (
812 GTK_CHECK_MENU_ITEM (item
), TRUE
);
815 G_OBJECT (item
), "divisions",
816 GINT_TO_POINTER (divisions
[i
]));
820 G_CALLBACK (e_day_view_time_item_on_set_divisions
),
824 item
= gtk_separator_menu_item_new ();
825 gtk_widget_show (item
);
826 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
828 submenu
= gtk_menu_new ();
829 item
= gtk_menu_item_new_with_label (_("Show the second time zone"));
830 gtk_widget_show (item
);
831 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
832 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item
), submenu
);
834 zone
= e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view
));
836 item
= gtk_menu_item_new_with_label (icaltimezone_get_display_name (zone
));
838 item
= gtk_menu_item_new_with_label ("---");
839 gtk_widget_set_sensitive (item
, FALSE
);
840 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
842 item
= gtk_separator_menu_item_new ();
843 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
846 item
= gtk_radio_menu_item_new_with_label (group
, C_("cal-second-zone", "None"));
847 group
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item
));
848 if (!time_item
->priv
->second_zone
)
849 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item
), TRUE
);
850 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
853 G_CALLBACK (edvti_on_set_zone
), time_item
);
855 recent_zones
= calendar_config_get_day_second_zones ();
856 for (s
= recent_zones
; s
!= NULL
; s
= s
->next
) {
857 zone
= icaltimezone_get_builtin_timezone (s
->data
);
861 item
= gtk_radio_menu_item_new_with_label (
862 group
, icaltimezone_get_display_name (zone
));
863 group
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item
));
864 /* both comes from builtin, thus no problem to compare pointers */
865 if (zone
== time_item
->priv
->second_zone
)
866 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item
), TRUE
);
867 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
868 g_object_set_data_full (
869 G_OBJECT (item
), "timezone",
870 g_strdup (s
->data
), g_free
);
873 G_CALLBACK (edvti_on_set_zone
), time_item
);
875 calendar_config_free_day_second_zones (recent_zones
);
877 item
= gtk_separator_menu_item_new ();
878 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
880 item
= gtk_menu_item_new_with_label (_("Select..."));
883 G_CALLBACK (edvti_on_select_zone
), time_item
);
884 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
886 gtk_widget_show_all (submenu
);
888 gtk_menu_attach_to_widget (GTK_MENU (menu
),
889 GTK_WIDGET (day_view
),
891 gtk_menu_popup_at_pointer (GTK_MENU (menu
), event
);
895 e_day_view_time_item_on_set_divisions (GtkWidget
*item
,
896 EDayViewTimeItem
*time_item
)
899 ECalendarView
*cal_view
;
902 day_view
= e_day_view_time_item_get_day_view (time_item
);
903 g_return_if_fail (day_view
!= NULL
);
905 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item
)))
908 divisions
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "divisions"));
910 cal_view
= E_CALENDAR_VIEW (day_view
);
911 e_calendar_view_set_time_divisions (cal_view
, divisions
);
915 e_day_view_time_item_on_button_press (EDayViewTimeItem
*time_item
,
921 GdkGrabStatus grab_status
;
922 GdkDevice
*event_device
;
926 day_view
= e_day_view_time_item_get_day_view (time_item
);
927 g_return_if_fail (day_view
!= NULL
);
929 canvas
= GNOME_CANVAS_ITEM (time_item
)->canvas
;
931 row
= e_day_view_time_item_convert_position_to_row (
938 if (!gtk_widget_has_focus (GTK_WIDGET (day_view
)))
939 gtk_widget_grab_focus (GTK_WIDGET (day_view
));
941 window
= gtk_layout_get_bin_window (GTK_LAYOUT (canvas
));
943 event_device
= gdk_event_get_device (event
);
944 event_time
= gdk_event_get_time (event
);
946 grab_status
= gdk_device_grab (
951 GDK_POINTER_MOTION_MASK
|
952 GDK_BUTTON_RELEASE_MASK
,
956 if (grab_status
== GDK_GRAB_SUCCESS
) {
957 e_day_view_start_selection (day_view
, -1, row
);
958 time_item
->priv
->dragging_selection
= TRUE
;
963 e_day_view_time_item_on_button_release (EDayViewTimeItem
*time_item
,
968 day_view
= e_day_view_time_item_get_day_view (time_item
);
969 g_return_if_fail (day_view
!= NULL
);
971 if (time_item
->priv
->dragging_selection
) {
972 GdkDevice
*event_device
;
975 event_device
= gdk_event_get_device (event
);
976 event_time
= gdk_event_get_time (event
);
977 gdk_device_ungrab (event_device
, event_time
);
979 e_day_view_finish_selection (day_view
);
980 e_day_view_stop_auto_scroll (day_view
);
983 time_item
->priv
->dragging_selection
= FALSE
;
987 e_day_view_time_item_on_motion_notify (EDayViewTimeItem
*time_item
,
995 if (!time_item
->priv
->dragging_selection
)
998 day_view
= e_day_view_time_item_get_day_view (time_item
);
999 g_return_if_fail (day_view
!= NULL
);
1001 canvas
= GNOME_CANVAS_ITEM (time_item
)->canvas
;
1003 y
= event
->motion
.y
;
1004 row
= e_day_view_time_item_convert_position_to_row (time_item
, y
);
1007 gnome_canvas_world_to_window (
1008 canvas
, 0, event
->motion
.y
,
1010 e_day_view_update_selection (day_view
, -1, row
);
1011 e_day_view_check_auto_scroll (day_view
, -1, (gint
) window_y
);
1015 /* Returns the row corresponding to the y position, or -1. */
1017 e_day_view_time_item_convert_position_to_row (EDayViewTimeItem
*time_item
,
1023 day_view
= e_day_view_time_item_get_day_view (time_item
);
1024 g_return_val_if_fail (day_view
!= NULL
, -1);
1029 row
= y
/ day_view
->row_height
;
1030 if (row
>= day_view
->rows
)
1037 e_day_view_time_item_get_day_view (EDayViewTimeItem
*time_item
)
1039 g_return_val_if_fail (E_IS_DAY_VIEW_TIME_ITEM (time_item
), NULL
);
1041 return time_item
->priv
->day_view
;
1045 e_day_view_time_item_set_day_view (EDayViewTimeItem
*time_item
,
1048 g_return_if_fail (E_IS_DAY_VIEW_TIME_ITEM (time_item
));
1049 g_return_if_fail (E_IS_DAY_VIEW (day_view
));
1051 if (time_item
->priv
->day_view
== day_view
)
1054 if (time_item
->priv
->day_view
!= NULL
)
1055 g_object_unref (time_item
->priv
->day_view
);
1057 time_item
->priv
->day_view
= g_object_ref (day_view
);
1059 g_object_notify (G_OBJECT (time_item
), "day-view");
1062 /* Returns the minimum width needed for the column, by adding up all the
1063 * maximum widths of the strings. The string widths are all calculated in
1064 * the style_updated handlers of EDayView and EDayViewTimeCanvas. */
1066 e_day_view_time_item_get_column_width (EDayViewTimeItem
*time_item
)
1069 gint digit
, large_digit_width
, max_large_digit_width
= 0;
1070 gint max_suffix_width
, max_minute_or_suffix_width
;
1071 gint column_width_default
, column_width_60_min_rows
;
1073 day_view
= e_day_view_time_item_get_day_view (time_item
);
1074 g_return_val_if_fail (day_view
!= NULL
, 0);
1076 /* Find the maximum width a digit can have. FIXME: We could use pango's
1077 * approximation function, but I worry it won't be precise enough. Also
1078 * it needs a language tag that I don't know where to get. */
1079 for (digit
= '0'; digit
<= '9'; digit
++) {
1080 PangoLayout
*layout
;
1083 digit_str
[0] = digit
;
1084 digit_str
[1] = '\0';
1086 layout
= gtk_widget_create_pango_layout (GTK_WIDGET (day_view
), digit_str
);
1087 pango_layout_set_font_description (layout
, day_view
->large_font_desc
);
1088 pango_layout_get_pixel_size (layout
, &large_digit_width
, NULL
);
1090 g_object_unref (layout
);
1092 max_large_digit_width
= MAX (
1093 max_large_digit_width
,
1097 /* Calculate the width of each time column, using the maximum of the
1098 * default format with large hour numbers, and the 60-min divisions
1099 * format which uses small text. */
1100 max_suffix_width
= MAX (
1101 day_view
->am_string_width
,
1102 day_view
->pm_string_width
);
1104 max_minute_or_suffix_width
= MAX (
1106 day_view
->max_minute_width
);
1108 column_width_default
= max_large_digit_width
* 2
1109 + max_minute_or_suffix_width
1110 + E_DVTMI_MIN_X_PAD
* 2
1111 + E_DVTMI_HOUR_L_PAD
1112 + E_DVTMI_HOUR_R_PAD
1113 + E_DVTMI_TIME_GRID_X_PAD
* 2;
1115 column_width_60_min_rows
= day_view
->max_small_hour_width
1116 + day_view
->colon_width
1117 + max_minute_or_suffix_width
1118 + E_DVTMI_60_MIN_X_PAD
* 2
1119 + E_DVTMI_TIME_GRID_X_PAD
* 2;
1121 time_item
->priv
->column_width
=
1122 MAX (column_width_default
, column_width_60_min_rows
);
1124 if (time_item
->priv
->second_zone
)
1125 return (2 * time_item
->priv
->column_width
) -
1126 E_DVTMI_TIME_GRID_X_PAD
;
1128 return time_item
->priv
->column_width
;
1132 e_day_view_time_item_get_second_zone (EDayViewTimeItem
*time_item
)
1134 g_return_val_if_fail (E_IS_DAY_VIEW_TIME_ITEM (time_item
), NULL
);
1136 return time_item
->priv
->second_zone
;