2 * EDayViewTimeItem - canvas item which displays the times down the left of
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) version 3.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with the program; if not, see <http://www.gnu.org/licenses/>
19 * Damon Chaplin <damon@ximian.com>
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
30 #include <glib/gi18n.h>
31 #include "e-day-view-time-item.h"
32 #include "calendar-config.h"
33 #include <libecal/e-cal-time-util.h>
34 #include <widgets/e-timezone-dialog/e-timezone-dialog.h>
35 #include <libedataserver/e-data-server-util.h>
37 /* The spacing between items in the time column. GRID_X_PAD is the space down
38 either side of the column, i.e. outside the main horizontal grid lines.
39 HOUR_L_PAD & HOUR_R_PAD are the spaces on the left & right side of the
40 big hour number (this is inside the horizontal grid lines).
41 MIN_X_PAD is the spacing either side of the minute number. The smaller
42 horizontal grid lines match with this.
43 60_MIN_X_PAD is the space either side of the HH:MM display used when
44 we are displaying 60 mins per row (inside the main grid lines).
45 LARGE_HOUR_Y_PAD is the offset of the large hour string from the top of the
47 SMALL_FONT_Y_PAD is the offset of the small time/minute string from the top
49 #define E_DVTMI_TIME_GRID_X_PAD 4
50 #define E_DVTMI_HOUR_L_PAD 4
51 #define E_DVTMI_HOUR_R_PAD 2
52 #define E_DVTMI_MIN_X_PAD 2
53 #define E_DVTMI_60_MIN_X_PAD 4
54 #define E_DVTMI_LARGE_HOUR_Y_PAD 1
55 #define E_DVTMI_SMALL_FONT_Y_PAD 1
57 #define E_DAY_VIEW_TIME_ITEM_GET_PRIVATE(obj) \
58 (G_TYPE_INSTANCE_GET_PRIVATE \
59 ((obj), E_TYPE_DAY_VIEW_TIME_ITEM, EDayViewTimeItemPrivate))
61 struct _EDayViewTimeItemPrivate
{
62 /* The parent EDayView widget. */
65 /* The width of the time column. */
68 /* TRUE if we are currently dragging the selection times. */
69 gboolean dragging_selection
;
71 /* The second timezone if shown, or else NULL. */
72 guint second_zone_changed_id
;
73 icaltimezone
*second_zone
;
76 static void e_day_view_time_item_update (GnomeCanvasItem
*item
,
77 const cairo_matrix_t
*i2c
,
79 static void e_day_view_time_item_draw (GnomeCanvasItem
*item
,
80 GdkDrawable
*drawable
,
82 gint width
, gint height
);
83 static GnomeCanvasItem
*e_day_view_time_item_point (GnomeCanvasItem
*item
,
86 static gint
e_day_view_time_item_event (GnomeCanvasItem
*item
,
88 static void e_day_view_time_item_increment_time (gint
*hour
,
91 static void e_day_view_time_item_show_popup_menu (EDayViewTimeItem
*time_item
,
93 static void e_day_view_time_item_on_set_divisions (GtkWidget
*item
,
94 EDayViewTimeItem
*time_item
);
95 static void e_day_view_time_item_on_button_press (EDayViewTimeItem
*time_item
,
97 static void e_day_view_time_item_on_button_release (EDayViewTimeItem
*time_item
,
99 static void e_day_view_time_item_on_motion_notify (EDayViewTimeItem
*time_item
,
101 static gint
e_day_view_time_item_convert_position_to_row (EDayViewTimeItem
*time_item
,
104 static void edvti_second_zone_changed_cb (GConfClient
*client
, guint cnxn_id
, GConfEntry
*entry
, gpointer user_data
);
111 static gpointer parent_class
;
114 day_view_time_item_set_property (GObject
*object
,
119 switch (property_id
) {
121 e_day_view_time_item_set_day_view (
122 E_DAY_VIEW_TIME_ITEM (object
),
123 g_value_get_object (value
));
127 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
131 day_view_time_item_get_property (GObject
*object
,
136 switch (property_id
) {
139 value
, e_day_view_time_item_get_day_view (
140 E_DAY_VIEW_TIME_ITEM (object
)));
144 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
148 day_view_time_item_dispose (GObject
*object
)
150 EDayViewTimeItemPrivate
*priv
;
152 priv
= E_DAY_VIEW_TIME_ITEM_GET_PRIVATE (object
);
154 if (priv
->day_view
!= NULL
) {
155 g_object_unref (priv
->day_view
);
156 priv
->day_view
= NULL
;
159 /* Chain up to parent's dispose() method. */
160 G_OBJECT_CLASS (parent_class
)->dispose (object
);
164 day_view_time_item_finalize (GObject
*object
)
166 EDayViewTimeItem
*time_item
;
168 time_item
= E_DAY_VIEW_TIME_ITEM (object
);
170 if (time_item
->priv
->second_zone_changed_id
)
171 calendar_config_remove_notification (time_item
->priv
->second_zone_changed_id
);
172 time_item
->priv
->second_zone_changed_id
= 0;
174 /* Chain up to parent's dispose() method. */
175 G_OBJECT_CLASS (parent_class
)->finalize (object
);
179 day_view_time_item_class_init (EDayViewTimeItemClass
*class)
181 GObjectClass
*object_class
;
182 GnomeCanvasItemClass
*item_class
;
184 parent_class
= g_type_class_peek_parent (class);
185 g_type_class_add_private (class, sizeof (EDayViewTimeItemPrivate
));
187 object_class
= G_OBJECT_CLASS (class);
188 object_class
->set_property
= day_view_time_item_set_property
;
189 object_class
->get_property
= day_view_time_item_get_property
;
190 object_class
->dispose
= day_view_time_item_dispose
;
191 object_class
->finalize
= day_view_time_item_finalize
;
193 item_class
= GNOME_CANVAS_ITEM_CLASS (class);
194 item_class
->update
= e_day_view_time_item_update
;
195 item_class
->draw
= e_day_view_time_item_draw
;
196 item_class
->point
= e_day_view_time_item_point
;
197 item_class
->event
= e_day_view_time_item_event
;
199 g_object_class_install_property (
202 g_param_spec_object (
211 day_view_time_item_init (EDayViewTimeItem
*time_item
)
215 time_item
->priv
= E_DAY_VIEW_TIME_ITEM_GET_PRIVATE (time_item
);
217 time_item
->priv
->dragging_selection
= FALSE
;
218 time_item
->priv
->second_zone
= NULL
;
220 last
= calendar_config_get_day_second_zone ();
224 time_item
->priv
->second_zone
= icaltimezone_get_builtin_timezone (last
);
228 time_item
->priv
->second_zone_changed_id
= calendar_config_add_notification_day_second_zone (edvti_second_zone_changed_cb
, time_item
);
232 e_day_view_time_item_get_type (void)
234 static GType type
= 0;
236 if (G_UNLIKELY (type
== 0)) {
237 const GTypeInfo type_info
= {
238 sizeof (EDayViewTimeItemClass
),
239 (GBaseInitFunc
) NULL
,
240 (GBaseFinalizeFunc
) NULL
,
241 (GClassInitFunc
) day_view_time_item_class_init
,
242 (GClassFinalizeFunc
) NULL
,
243 NULL
, /* class_data */
244 sizeof (EDayViewTimeItem
),
246 (GInstanceInitFunc
) day_view_time_item_init
,
247 NULL
/* value_table */
250 type
= g_type_register_static (
251 GNOME_TYPE_CANVAS_ITEM
, "EDayViewTimeItem",
259 e_day_view_time_item_update (GnomeCanvasItem
*item
,
260 const cairo_matrix_t
*i2c
,
263 if (GNOME_CANVAS_ITEM_CLASS (parent_class
)->update
)
264 (* GNOME_CANVAS_ITEM_CLASS (parent_class
)->update
) (item
, i2c
, flags
);
266 /* The item covers the entire canvas area. */
274 * DRAWING ROUTINES - functions to paint the canvas item.
277 edvti_draw_zone (GnomeCanvasItem
*canvas_item
,
278 GdkDrawable
*drawable
,
284 icaltimezone
*use_zone
)
287 EDayViewTimeItem
*time_item
;
288 ECalendarView
*cal_view
;
292 gchar buffer
[64], *midnight_day
= NULL
, *midnight_month
= NULL
;
294 gint hour
, display_hour
, minute
, row
;
295 gint row_y
, start_y
, large_hour_y_offset
, small_font_y_offset
;
296 gint long_line_x1
, long_line_x2
, short_line_x1
;
297 gint large_hour_x2
, minute_x2
;
298 gint hour_width
, minute_width
, suffix_width
;
299 gint max_suffix_width
, max_minute_or_suffix_width
;
301 PangoContext
*context
;
302 PangoFontDescription
*small_font_desc
;
303 PangoFontMetrics
*large_font_metrics
, *small_font_metrics
;
308 cr
= gdk_cairo_create (drawable
);
310 time_item
= E_DAY_VIEW_TIME_ITEM (canvas_item
);
311 day_view
= e_day_view_time_item_get_day_view (time_item
);
312 g_return_if_fail (day_view
!= NULL
);
314 cal_view
= E_CALENDAR_VIEW (day_view
);
315 model
= e_calendar_view_get_model (cal_view
);
316 time_divisions
= e_calendar_view_get_time_divisions (cal_view
);
318 style
= gtk_widget_get_style (GTK_WIDGET (day_view
));
319 small_font_desc
= style
->font_desc
;
321 context
= gtk_widget_get_pango_context (GTK_WIDGET (day_view
));
322 large_font_metrics
= pango_context_get_metrics (context
, day_view
->large_font_desc
,
323 pango_context_get_language (context
));
324 small_font_metrics
= pango_context_get_metrics (context
, small_font_desc
,
325 pango_context_get_language (context
));
327 fg
= style
->fg
[GTK_STATE_NORMAL
];
328 dark
= style
->dark
[GTK_STATE_NORMAL
];
330 /* The start and end of the long horizontal line between hours. */
331 long_line_x1
= (use_zone
? 0 : E_DVTMI_TIME_GRID_X_PAD
) - x
+ x_offset
;
332 long_line_x2
= time_item
->priv
->column_width
- E_DVTMI_TIME_GRID_X_PAD
- x
- (use_zone
? E_DVTMI_TIME_GRID_X_PAD
: 0) + x_offset
;
334 if (time_divisions
== 60) {
335 /* The right edge of the complete time string in 60-min
336 divisions, e.g. "14:00" or "2 pm". */
337 minute_x2
= long_line_x2
- E_DVTMI_60_MIN_X_PAD
;
339 /* These aren't used for 60-minute divisions, but we initialize
340 them to keep gcc happy. */
344 max_suffix_width
= MAX (day_view
->am_string_width
,
345 day_view
->pm_string_width
);
347 max_minute_or_suffix_width
= MAX (max_suffix_width
,
348 day_view
->max_minute_width
);
350 /* The start of the short horizontal line between the periods
352 short_line_x1
= long_line_x2
- E_DVTMI_MIN_X_PAD
* 2
353 - max_minute_or_suffix_width
;
355 /* The right edge of the large hour string. */
356 large_hour_x2
= short_line_x1
- E_DVTMI_HOUR_R_PAD
;
358 /* The right edge of the minute part of the time. */
359 minute_x2
= long_line_x2
- E_DVTMI_MIN_X_PAD
;
362 /* Start with the first hour & minute shown in the EDayView. */
363 hour
= day_view
->first_hour_shown
;
364 minute
= day_view
->first_minute_shown
;
367 /* shift time with a difference between local time and the other timezone */
368 icaltimezone
*cal_zone
= e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view
));
369 struct icaltimetype tt
;
373 tt
= icaltime_from_timet_with_zone (day_view
->day_starts
[0], 0, cal_zone
);
375 /* diff is number of minutes */
376 diff
= (icaltimezone_get_utc_offset (use_zone
, &tt
, NULL
) -
377 icaltimezone_get_utc_offset (cal_zone
, &tt
, NULL
)
380 tt
= icaltime_from_timet_with_zone (day_view
->day_starts
[0], 0, cal_zone
);
382 icaltime_set_timezone (&tt
, cal_zone
);
383 tt
= icaltime_convert_to_zone (tt
, use_zone
);
386 /* shows the next midnight */
387 icaltime_adjust (&tt
, 1, 0, 0, 0);
390 mn
= icaltimetype_to_tm (&tt
);
392 /* up to two characters/numbers */
393 e_utf8_strftime (buffer
, sizeof (buffer
), "%d", &mn
);
394 midnight_day
= g_strdup (buffer
);
395 /* up to three characters, abbreviated month name */
396 e_utf8_strftime (buffer
, sizeof (buffer
), "%b", &mn
);
397 midnight_month
= g_strdup (buffer
);
399 minute
+= (diff
% 60);
400 hour
+= (diff
/ 60) + (minute
/ 60);
402 minute
= minute
% 60;
408 hour
= (hour
+ 48) % 24;
411 /* The offset of the large hour string from the top of the row. */
412 large_hour_y_offset
= E_DVTMI_LARGE_HOUR_Y_PAD
;
414 /* The offset of the small time/minute string from top of row. */
415 small_font_y_offset
= E_DVTMI_SMALL_FONT_Y_PAD
;
417 /* Calculate the minimum y position of the first row we need to draw.
418 This is normally one row height above the 0 position, but if we
419 are using the large font we may have to go back a bit further. */
420 start_y
= 0 - MAX (day_view
->row_height
,
421 (pango_font_metrics_get_ascent (large_font_metrics
) +
422 pango_font_metrics_get_descent (large_font_metrics
)) / PANGO_SCALE
+
423 E_DVTMI_LARGE_HOUR_Y_PAD
);
425 /* Draw the Marcus Bains Line first, so it appears under other elements. */
426 if (e_day_view_marcus_bains_get_show_line (day_view
)) {
427 struct icaltimetype time_now
;
431 gdk_cairo_set_source_color (cr
, &day_view
->colors
[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE
]);
433 if (day_view
->marcus_bains_time_bar_color
&& gdk_color_parse (day_view
->marcus_bains_time_bar_color
, &mb_color
)) {
434 GdkColormap
*colormap
;
436 colormap
= gtk_widget_get_colormap (GTK_WIDGET (day_view
));
437 if (gdk_colormap_alloc_color (colormap
, &mb_color
, TRUE
, TRUE
)) {
438 gdk_cairo_set_source_color (cr
, &mb_color
);
441 mb_color
= day_view
->colors
[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE
];
443 time_now
= icaltime_current_time_with_zone (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view
)));
444 marcus_bains_y
= (time_now
.hour
* 60 + time_now
.minute
) * day_view
->row_height
/ time_divisions
- y
;
445 cairo_set_line_width (cr
, 1.5);
446 cairo_move_to (cr
, long_line_x1
- (use_zone
? E_DVTMI_TIME_GRID_X_PAD
: 0), marcus_bains_y
);
447 cairo_line_to (cr
, long_line_x2
, marcus_bains_y
);
451 mb_color
= day_view
->colors
[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE
];
453 if (day_view
->marcus_bains_time_bar_color
&& gdk_color_parse (day_view
->marcus_bains_time_bar_color
, &mb_color
)) {
454 GdkColormap
*colormap
;
456 colormap
= gtk_widget_get_colormap (GTK_WIDGET (day_view
));
457 if (gdk_colormap_alloc_color (colormap
, &mb_color
, TRUE
, TRUE
)) {
458 gdk_cairo_set_source_color (cr
, &mb_color
);
463 /* Step through each row, drawing the times and the horizontal lines
465 for (row
= 0, row_y
= 0 - y
;
466 row
< day_view
->rows
&& row_y
< height
;
467 row
++, row_y
+= day_view
->row_height
) {
468 gboolean show_midnight_date
= use_zone
&& hour
== 0 && (minute
== 0 || time_divisions
== 60) && midnight_day
&& midnight_month
;
470 /* If the row is above the first row we want to draw just
471 increment the time and skip to the next row. */
472 if (row_y
< start_y
) {
473 e_day_view_time_item_increment_time (&hour
, &minute
,
478 /* Calculate the actual hour number to display. For 12-hour
479 format we convert 0-23 to 12-11am/12-11pm. */
480 e_day_view_convert_time_to_display (day_view
, hour
,
482 &suffix
, &suffix_width
);
484 if (time_divisions
== 60) {
485 /* 60 minute intervals - draw a long horizontal line
486 between hours and display as one long string,
487 e.g. "14:00" or "2 pm". */
489 gdk_cairo_set_source_color (cr
, &dark
);
491 cairo_set_line_width (cr
, 0.7);
492 cairo_move_to (cr
, long_line_x1
, row_y
);
493 cairo_line_to (cr
, long_line_x2
, row_y
);
497 if (show_midnight_date
) {
498 strcpy (buffer
, midnight_day
);
499 strcat (buffer
, " ");
500 strcat (buffer
, midnight_month
);
501 } else if (e_cal_model_get_use_24_hour_format (model
)) {
502 g_snprintf (buffer
, sizeof (buffer
), "%i:%02i",
503 display_hour
, minute
);
505 g_snprintf (buffer
, sizeof (buffer
), "%i %s",
506 display_hour
, suffix
);
510 if (show_midnight_date
)
511 gdk_cairo_set_source_color (cr
, &mb_color
);
513 gdk_cairo_set_source_color (cr
, &fg
);
514 layout
= pango_cairo_create_layout (cr
);
515 pango_layout_set_text (layout
, buffer
, -1);
516 pango_layout_get_pixel_size (layout
, &minute_width
, NULL
);
517 cairo_translate (cr
, minute_x2
- minute_width
, row_y
+ small_font_y_offset
);
518 pango_cairo_update_layout (cr
, layout
);
519 pango_cairo_show_layout (cr
, layout
);
522 g_object_unref (layout
);
524 /* 5/10/15/30 minute intervals. */
527 /* On the hour - draw a long horizontal line
528 before the hour and display the hour in the
532 gdk_cairo_set_source_color (cr
, &dark
);
533 if (show_midnight_date
)
534 strcpy (buffer
, midnight_day
);
536 g_snprintf (buffer
, sizeof (buffer
), "%i",
539 cairo_set_line_width (cr
, 0.7);
540 cairo_move_to (cr
, long_line_x1
, row_y
);
541 cairo_line_to (cr
, long_line_x2
, row_y
);
546 if (show_midnight_date
)
547 gdk_cairo_set_source_color (cr
, &mb_color
);
549 gdk_cairo_set_source_color (cr
, &fg
);
550 layout
= pango_cairo_create_layout (cr
);
551 pango_layout_set_text (layout
, buffer
, -1);
552 pango_layout_set_font_description (layout
, day_view
->large_font_desc
);
553 pango_layout_get_pixel_size (layout
, &hour_width
, NULL
);
554 cairo_translate (cr
, large_hour_x2
- hour_width
, row_y
+ large_hour_y_offset
);
555 pango_cairo_update_layout (cr
, layout
);
556 pango_cairo_show_layout (cr
, layout
);
559 g_object_unref (layout
);
561 /* Within the hour - draw a short line before
564 gdk_cairo_set_source_color (cr
, &dark
);
565 cairo_set_line_width (cr
, 0.7);
566 cairo_move_to (cr
, short_line_x1
, row_y
);
567 cairo_line_to (cr
, long_line_x2
, row_y
);
572 /* Normally we display the minute in each
573 interval, but when using 30-minute intervals
574 we don't display the '30'. */
575 if (time_divisions
!= 30 || minute
!= 30) {
576 /* In 12-hour format we display 'am' or 'pm'
578 if (show_midnight_date
)
579 strcpy (buffer
, midnight_month
);
581 && !e_cal_model_get_use_24_hour_format (model
)) {
582 strcpy (buffer
, suffix
);
584 g_snprintf (buffer
, sizeof (buffer
),
589 if (show_midnight_date
)
590 gdk_cairo_set_source_color (cr
, &mb_color
);
592 gdk_cairo_set_source_color (cr
, &fg
);
593 layout
= pango_cairo_create_layout (cr
);
594 pango_layout_set_text (layout
, buffer
, -1);
595 pango_layout_set_font_description (layout
, day_view
->small_font_desc
);
596 pango_layout_get_pixel_size (layout
, &minute_width
, NULL
);
597 cairo_translate (cr
, minute_x2
- minute_width
, row_y
+ small_font_y_offset
);
598 pango_cairo_update_layout (cr
, layout
);
599 pango_cairo_show_layout (cr
, layout
);
602 g_object_unref (layout
);
606 e_day_view_time_item_increment_time (&hour
, &minute
,
610 pango_font_metrics_unref (large_font_metrics
);
611 pango_font_metrics_unref (small_font_metrics
);
614 g_free (midnight_day
);
615 g_free (midnight_month
);
619 e_day_view_time_item_draw (GnomeCanvasItem
*canvas_item
,
620 GdkDrawable
*drawable
,
626 EDayViewTimeItem
*time_item
;
628 time_item
= E_DAY_VIEW_TIME_ITEM (canvas_item
);
629 g_return_if_fail (time_item
!= NULL
);
631 edvti_draw_zone (canvas_item
, drawable
, x
, y
, width
, height
, 0, NULL
);
633 if (time_item
->priv
->second_zone
)
634 edvti_draw_zone (canvas_item
, drawable
, x
, y
, width
, height
, time_item
->priv
->column_width
, time_item
->priv
->second_zone
);
637 /* Increment the time by the 5/10/15/30/60 minute interval.
638 Note that time_divisions is never > 60, so we never have to
639 worry about adding more than 60 minutes. */
641 e_day_view_time_item_increment_time (gint
*hour
,
645 *minute
+= time_divisions
;
648 /* Currently we never wrap around to the next day, but
649 we may do if we display extra timezones. */
650 *hour
= (*hour
+ 1) % 24;
654 static GnomeCanvasItem
*
655 e_day_view_time_item_point (GnomeCanvasItem
*item
, double x
, double y
,
662 e_day_view_time_item_event (GnomeCanvasItem
*item
,
665 EDayViewTimeItem
*time_item
;
667 time_item
= E_DAY_VIEW_TIME_ITEM (item
);
669 switch (event
->type
) {
670 case GDK_BUTTON_PRESS
:
671 if (event
->button
.button
== 1) {
672 e_day_view_time_item_on_button_press (time_item
, event
);
673 } else if (event
->button
.button
== 3) {
674 e_day_view_time_item_show_popup_menu (time_item
, event
);
678 case GDK_BUTTON_RELEASE
:
679 if (event
->button
.button
== 1)
680 e_day_view_time_item_on_button_release (time_item
,
684 case GDK_MOTION_NOTIFY
:
685 e_day_view_time_item_on_motion_notify (time_item
, event
);
696 edvti_second_zone_changed_cb (GConfClient
*client
, guint cnxn_id
, GConfEntry
*entry
, gpointer user_data
)
698 EDayViewTimeItem
*time_item
= user_data
;
702 g_return_if_fail (user_data
!= NULL
);
703 g_return_if_fail (E_IS_DAY_VIEW_TIME_ITEM (time_item
));
705 location
= calendar_config_get_day_second_zone ();
706 time_item
->priv
->second_zone
= location
? icaltimezone_get_builtin_timezone (location
) : NULL
;
709 day_view
= e_day_view_time_item_get_day_view (time_item
);
710 gtk_widget_set_size_request (day_view
->time_canvas
, e_day_view_time_item_get_column_width (time_item
), -1);
711 gtk_widget_queue_draw (day_view
->time_canvas
);
715 edvti_on_select_zone (GtkWidget
*item
, EDayViewTimeItem
*time_item
)
717 calendar_config_select_day_second_zone ();
721 edvti_on_set_zone (GtkWidget
*item
, EDayViewTimeItem
*time_item
)
723 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item
)))
726 calendar_config_set_day_second_zone (g_object_get_data (G_OBJECT (item
), "timezone"));
730 e_day_view_time_item_show_popup_menu (EDayViewTimeItem
*time_item
,
733 static gint divisions
[] = { 60, 30, 15, 10, 5 };
735 ECalendarView
*cal_view
;
736 GtkWidget
*menu
, *item
, *submenu
;
738 GSList
*group
= NULL
, *recent_zones
, *s
;
739 gint current_divisions
, i
;
742 day_view
= e_day_view_time_item_get_day_view (time_item
);
743 g_return_if_fail (day_view
!= NULL
);
745 cal_view
= E_CALENDAR_VIEW (day_view
);
746 current_divisions
= e_calendar_view_get_time_divisions (cal_view
);
748 menu
= gtk_menu_new ();
750 /* Make sure the menu is destroyed when it disappears. */
752 menu
, "selection-done",
753 G_CALLBACK (gtk_widget_destroy
), NULL
);
755 for (i
= 0; i
< G_N_ELEMENTS (divisions
); i
++) {
756 g_snprintf (buffer
, sizeof (buffer
),
757 /* TO TRANSLATORS: %02i is the number of minutes; this is a context menu entry
758 * to change the length of the time division in the calendar day view, e.g.
759 * a day is displayed in 24 "60 minute divisions" or 48 "30 minute divisions"
761 _("%02i minute divisions"), divisions
[i
]);
762 item
= gtk_radio_menu_item_new_with_label (group
, buffer
);
763 group
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item
));
764 gtk_widget_show (item
);
765 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
767 if (current_divisions
== divisions
[i
])
768 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item
), TRUE
);
770 g_object_set_data (G_OBJECT (item
), "divisions",
771 GINT_TO_POINTER (divisions
[i
]));
773 g_signal_connect (item
, "toggled",
774 G_CALLBACK (e_day_view_time_item_on_set_divisions
), time_item
);
777 item
= gtk_separator_menu_item_new ();
778 gtk_widget_show (item
);
779 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
781 submenu
= gtk_menu_new ();
782 item
= gtk_menu_item_new_with_label (_("Show the second time zone"));
783 gtk_widget_show (item
);
784 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
785 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item
), submenu
);
787 zone
= e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view
));
789 item
= gtk_menu_item_new_with_label (icaltimezone_get_display_name (zone
));
791 item
= gtk_menu_item_new_with_label ("---");
792 gtk_widget_set_sensitive (item
, FALSE
);
793 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
795 item
= gtk_separator_menu_item_new ();
796 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
799 item
= gtk_radio_menu_item_new_with_label (group
, C_("cal-second-zone", "None"));
800 group
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item
));
801 if (!time_item
->priv
->second_zone
)
802 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item
), TRUE
);
803 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
804 g_signal_connect (item
, "toggled", G_CALLBACK (edvti_on_set_zone
), time_item
);
806 recent_zones
= calendar_config_get_day_second_zones ();
807 for (s
= recent_zones
; s
!= NULL
; s
= s
->next
) {
808 zone
= icaltimezone_get_builtin_timezone (s
->data
);
812 item
= gtk_radio_menu_item_new_with_label (group
, icaltimezone_get_display_name (zone
));
813 group
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item
));
814 /* both comes from builtin, thus no problem to compare pointers */
815 if (zone
== time_item
->priv
->second_zone
)
816 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item
), TRUE
);
817 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
818 g_object_set_data_full (G_OBJECT (item
), "timezone", g_strdup (s
->data
), g_free
);
819 g_signal_connect (item
, "toggled", G_CALLBACK (edvti_on_set_zone
), time_item
);
821 calendar_config_free_day_second_zones (recent_zones
);
823 item
= gtk_separator_menu_item_new ();
824 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
826 item
= gtk_menu_item_new_with_label (_("Select..."));
827 g_signal_connect (item
, "activate", G_CALLBACK (edvti_on_select_zone
), time_item
);
828 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), item
);
830 gtk_widget_show_all (submenu
);
832 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
833 event
->button
.button
, event
->button
.time
);
837 e_day_view_time_item_on_set_divisions (GtkWidget
*item
,
838 EDayViewTimeItem
*time_item
)
841 ECalendarView
*cal_view
;
844 day_view
= e_day_view_time_item_get_day_view (time_item
);
845 g_return_if_fail (day_view
!= NULL
);
847 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item
)))
850 divisions
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "divisions"));
852 cal_view
= E_CALENDAR_VIEW (day_view
);
853 e_calendar_view_set_time_divisions (cal_view
, divisions
);
857 e_day_view_time_item_on_button_press (EDayViewTimeItem
*time_item
,
865 day_view
= e_day_view_time_item_get_day_view (time_item
);
866 g_return_if_fail (day_view
!= NULL
);
868 canvas
= GNOME_CANVAS_ITEM (time_item
)->canvas
;
870 row
= e_day_view_time_item_convert_position_to_row (time_item
,
876 if (!gtk_widget_has_focus (GTK_WIDGET (day_view
)))
877 gtk_widget_grab_focus (GTK_WIDGET (day_view
));
879 window
= gtk_layout_get_bin_window (GTK_LAYOUT (canvas
));
881 if (gdk_pointer_grab (window
, FALSE
,
882 GDK_POINTER_MOTION_MASK
883 | GDK_BUTTON_RELEASE_MASK
,
884 NULL
, NULL
, event
->button
.time
) == 0) {
885 e_day_view_start_selection (day_view
, -1, row
);
886 time_item
->priv
->dragging_selection
= TRUE
;
891 e_day_view_time_item_on_button_release (EDayViewTimeItem
*time_item
,
896 day_view
= e_day_view_time_item_get_day_view (time_item
);
897 g_return_if_fail (day_view
!= NULL
);
899 if (time_item
->priv
->dragging_selection
) {
900 gdk_pointer_ungrab (event
->button
.time
);
901 e_day_view_finish_selection (day_view
);
902 e_day_view_stop_auto_scroll (day_view
);
905 time_item
->priv
->dragging_selection
= FALSE
;
909 e_day_view_time_item_on_motion_notify (EDayViewTimeItem
*time_item
,
917 if (!time_item
->priv
->dragging_selection
)
920 day_view
= e_day_view_time_item_get_day_view (time_item
);
921 g_return_if_fail (day_view
!= NULL
);
923 canvas
= GNOME_CANVAS_ITEM (time_item
)->canvas
;
926 row
= e_day_view_time_item_convert_position_to_row (time_item
, y
);
929 gnome_canvas_world_to_window (canvas
, 0, event
->motion
.y
,
931 e_day_view_update_selection (day_view
, -1, row
);
932 e_day_view_check_auto_scroll (day_view
, -1, (gint
) window_y
);
936 /* Returns the row corresponding to the y position, or -1. */
938 e_day_view_time_item_convert_position_to_row (EDayViewTimeItem
*time_item
,
944 day_view
= e_day_view_time_item_get_day_view (time_item
);
945 g_return_val_if_fail (day_view
!= NULL
, -1);
950 row
= y
/ day_view
->row_height
;
951 if (row
>= day_view
->rows
)
958 e_day_view_time_item_get_day_view (EDayViewTimeItem
*time_item
)
960 g_return_val_if_fail (E_IS_DAY_VIEW_TIME_ITEM (time_item
), NULL
);
962 return time_item
->priv
->day_view
;
966 e_day_view_time_item_set_day_view (EDayViewTimeItem
*time_item
,
969 g_return_if_fail (E_IS_DAY_VIEW_TIME_ITEM (time_item
));
970 g_return_if_fail (E_IS_DAY_VIEW (day_view
));
972 if (time_item
->priv
->day_view
!= NULL
)
973 g_object_unref (time_item
->priv
->day_view
);
975 time_item
->priv
->day_view
= g_object_ref (day_view
);
977 g_object_notify (G_OBJECT (time_item
), "day-view");
980 /* Returns the minimum width needed for the column, by adding up all the
981 maximum widths of the strings. The string widths are all calculated in
982 the style_set handlers of EDayView and EDayViewTimeCanvas. */
984 e_day_view_time_item_get_column_width (EDayViewTimeItem
*time_item
)
988 gint digit
, large_digit_width
, max_large_digit_width
= 0;
989 gint max_suffix_width
, max_minute_or_suffix_width
;
990 gint column_width_default
, column_width_60_min_rows
;
992 day_view
= e_day_view_time_item_get_day_view (time_item
);
993 g_return_val_if_fail (day_view
!= NULL
, 0);
995 style
= gtk_widget_get_style (GTK_WIDGET (day_view
));
996 g_return_val_if_fail (style
!= NULL
, 0);
998 /* Find the maximum width a digit can have. FIXME: We could use pango's
999 * approximation function, but I worry it won't be precise enough. Also
1000 * it needs a language tag that I don't know where to get. */
1001 for (digit
= '0'; digit
<= '9'; digit
++) {
1002 PangoLayout
*layout
;
1005 digit_str
[0] = digit
;
1006 digit_str
[1] = '\0';
1008 layout
= gtk_widget_create_pango_layout (GTK_WIDGET (day_view
), digit_str
);
1009 pango_layout_set_font_description (layout
, day_view
->large_font_desc
);
1010 pango_layout_get_pixel_size (layout
, &large_digit_width
, NULL
);
1012 g_object_unref (layout
);
1014 max_large_digit_width
= MAX (max_large_digit_width
,
1018 /* Calculate the width of each time column, using the maximum of the
1019 default format with large hour numbers, and the 60-min divisions
1020 format which uses small text. */
1021 max_suffix_width
= MAX (day_view
->am_string_width
,
1022 day_view
->pm_string_width
);
1024 max_minute_or_suffix_width
= MAX (max_suffix_width
,
1025 day_view
->max_minute_width
);
1027 column_width_default
= max_large_digit_width
* 2
1028 + max_minute_or_suffix_width
1029 + E_DVTMI_MIN_X_PAD
* 2
1030 + E_DVTMI_HOUR_L_PAD
1031 + E_DVTMI_HOUR_R_PAD
1032 + E_DVTMI_TIME_GRID_X_PAD
* 2;
1034 column_width_60_min_rows
= day_view
->max_small_hour_width
1035 + day_view
->colon_width
1036 + max_minute_or_suffix_width
1037 + E_DVTMI_60_MIN_X_PAD
* 2
1038 + E_DVTMI_TIME_GRID_X_PAD
* 2;
1040 time_item
->priv
->column_width
=
1041 MAX (column_width_default
, column_width_60_min_rows
);
1043 if (time_item
->priv
->second_zone
)
1044 return (2 * time_item
->priv
->column_width
) -
1045 E_DVTMI_TIME_GRID_X_PAD
;
1047 return time_item
->priv
->column_width
;
1051 e_day_view_time_item_get_second_zone (EDayViewTimeItem
*time_item
)
1053 g_return_val_if_fail (E_IS_DAY_VIEW_TIME_ITEM (time_item
), NULL
);
1055 return time_item
->priv
->second_zone
;