evolution-3.6.4/calendar/gui/e-day-view.c

Location Tool Test ID Function Issue
e-day-view.c:2045:13 clang-analyzer Null pointer passed as an argument to a 'nonnull' parameter
e-day-view.c:2045:13 clang-analyzer Null pointer passed as an argument to a 'nonnull' parameter
e-day-view.c:4825:37 clang-analyzer The right operand of '!=' is a garbage value
e-day-view.c:4825:37 clang-analyzer The right operand of '!=' is a garbage value
e-day-view.c:7878:15 clang-analyzer Access to field 'start_row_or_col' results in a dereference of a null pointer (loaded from variable 'event')
e-day-view.c:7878:15 clang-analyzer Access to field 'start_row_or_col' results in a dereference of a null pointer (loaded from variable 'event')
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
   2 /*
   3  * EDayView - displays the Day & Work-Week views of the calendar.
   4  *
   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.
   9  *
  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.
  14  *
  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/>
  17  *
  18  * Authors:
  19  *      Damon Chaplin <damon@ximian.com>
  20  *      Rodrigo Moya <rodrigo@ximian.com>
  21  *
  22  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  23  */
  24 
  25 #ifdef HAVE_CONFIG_H
  26 #include <config.h>
  27 #endif
  28 
  29 #include "e-day-view.h"
  30 #include "ea-calendar.h"
  31 
  32 #include <math.h>
  33 #include <time.h>
  34 #include <gdk/gdkkeysyms.h>
  35 #include <text/e-text.h>
  36 #include <misc/e-canvas-utils.h>
  37 #include <e-util/e-unicode.h>
  38 #include <libgnomecanvas/libgnomecanvas.h>
  39 #include <glib/gi18n.h>
  40 #include <e-util/e-categories-config.h>
  41 #include <e-util/e-dialog-utils.h>
  42 #include <e-util/e-selection.h>
  43 
  44 #include "dialogs/delete-comp.h"
  45 #include "dialogs/delete-error.h"
  46 #include "dialogs/send-comp.h"
  47 #include "dialogs/cancel-comp.h"
  48 #include "dialogs/recur-comp.h"
  49 #include "dialogs/goto-dialog.h"
  50 #include "print.h"
  51 #include "calendar-config.h"
  52 #include "comp-util.h"
  53 #include "itip-utils.h"
  54 #include "e-cal-model-calendar.h"
  55 #include "e-day-view-time-item.h"
  56 #include "e-day-view-top-item.h"
  57 #include "e-day-view-layout.h"
  58 #include "e-day-view-main-item.h"
  59 #include "misc.h"
  60 #include <e-util/e-icon-factory.h>
  61 
  62 /* The minimum amount of space wanted on each side of the date string. */
  63 #define E_DAY_VIEW_DATE_X_PAD	4
  64 
  65 #define E_DAY_VIEW_LARGE_FONT_PTSIZE 18
  66 #define E_DAY_VIEW_SMALL_FONT_PTSIZE 10
  67 
  68 /* The offset from the top/bottom of the canvas before auto-scrolling starts.*/
  69 #define E_DAY_VIEW_AUTO_SCROLL_OFFSET	16
  70 
  71 /* The time between each auto-scroll, in milliseconds. */
  72 #define E_DAY_VIEW_AUTO_SCROLL_TIMEOUT	50
  73 
  74 /* The number of timeouts we skip before we start scrolling. */
  75 #define E_DAY_VIEW_AUTO_SCROLL_DELAY	5
  76 
  77 /* The number of pixels the mouse has to be moved with the button down before
  78  * we start a drag. */
  79 #define E_DAY_VIEW_DRAG_START_OFFSET	4
  80 
  81 /* The amount we scroll the main canvas when the Page Up/Down keys are pressed,
  82  * as a fraction of the page size. */
  83 #define E_DAY_VIEW_PAGE_STEP		0.5
  84 
  85 /* The amount we scroll the main canvas when the mouse wheel buttons are
  86  * pressed, as a fraction of the page size. */
  87 #define E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE	0.25
  88 
  89 /* The timeout before we do a layout, so we don't do a layout for each event
  90  * we get from the server. */
  91 #define E_DAY_VIEW_LAYOUT_TIMEOUT	100
  92 
  93 /* How many rows can be shown at a top_canvas; there will be always + 2 for
  94  * caption item and DnD space */
  95 #define E_DAY_VIEW_MAX_ROWS_AT_TOP     6
  96 
  97 typedef struct {
  98 	EDayView *day_view;
  99 	ECalModelComponent *comp_data;
 100 } AddEventData;
 101 
 102 /* Drag and Drop stuff. */
 103 static GtkTargetEntry target_table[] = {
 104 	{ (gchar *) "application/x-e-calendar-event", 0, 0 }
 105 };
 106 
 107 static void e_day_view_dispose (GObject *object);
 108 static void e_day_view_realize (GtkWidget *widget);
 109 static void e_day_view_set_colors (EDayView *day_view, GtkWidget *widget);
 110 static void e_day_view_unrealize (GtkWidget *widget);
 111 static void e_day_view_style_set (GtkWidget *widget,
 112 				  GtkStyle  *previous_style);
 113 static void e_day_view_size_allocate (GtkWidget *widget,
 114 				      GtkAllocation *allocation);
 115 static gboolean e_day_view_update_scroll_regions (EDayView *day_view);
 116 static gint e_day_view_focus_in (GtkWidget *widget,
 117 				 GdkEventFocus *event);
 118 static gint e_day_view_focus_out (GtkWidget *widget,
 119 				  GdkEventFocus *event);
 120 static gboolean e_day_view_key_press (GtkWidget *widget,
 121 				      GdkEventKey *event);
 122 static gboolean e_day_view_focus (GtkWidget *widget,
 123 				  GtkDirectionType direction);
 124 static gboolean e_day_view_get_next_tab_event (EDayView *day_view,
 125 					       GtkDirectionType direction,
 126 					       gint *day, gint *event_num);
 127 static gboolean e_day_view_get_extreme_long_event (EDayView *day_view,
 128 						   gboolean first,
 129 						   gint *day_out,
 130 						   gint *event_num_out);
 131 static gboolean e_day_view_get_extreme_event (EDayView *day_view,
 132 					      gint start_day,
 133 					      gint end_day,
 134 					      gboolean first,
 135 					      gint *day_out,
 136 					      gint *event_num_out);
 137 static gboolean e_day_view_do_key_press (GtkWidget *widget,
 138 					 GdkEventKey *event);
 139 static gboolean e_day_view_popup_menu (GtkWidget *widget);
 140 static GList *e_day_view_get_selected_events (ECalendarView *cal_view);
 141 static gboolean e_day_view_get_selected_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time);
 142 static void e_day_view_set_selected_time_range (ECalendarView *cal_view, time_t start_time, time_t end_time);
 143 static gboolean e_day_view_get_visible_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time);
 144 static void e_day_view_paste_text (ECalendarView *day_view);
 145 static void e_day_view_update_query (EDayView *day_view);
 146 static void e_day_view_goto_start_of_work_day (EDayView *day_view);
 147 static void e_day_view_goto_end_of_work_day (EDayView *day_view);
 148 static void e_day_view_change_duration_to_start_of_work_day (EDayView *day_view);
 149 static void e_day_view_change_duration_to_end_of_work_day (EDayView *day_view);
 150 static void e_day_view_cursor_key_up_shifted (EDayView *day_view,
 151 					      GdkEventKey *event);
 152 static void e_day_view_cursor_key_down_shifted (EDayView *day_view,
 153 						GdkEventKey *event);
 154 static void e_day_view_cursor_key_left_shifted (EDayView *day_view,
 155 						GdkEventKey *event);
 156 static void e_day_view_cursor_key_right_shifted (EDayView *day_view,
 157 						 GdkEventKey *event);
 158 static void e_day_view_cursor_key_up (EDayView *day_view,
 159 				      GdkEventKey *event);
 160 static void e_day_view_cursor_key_down (EDayView *day_view,
 161 					GdkEventKey *event);
 162 static void e_day_view_cursor_key_left (EDayView *day_view,
 163 					GdkEventKey *event);
 164 static void e_day_view_cursor_key_right (EDayView *day_view,
 165 					 GdkEventKey *event);
 166 static void e_day_view_scroll	(EDayView	*day_view,
 167 				 gfloat		 pages_to_scroll);
 168 
 169 static void e_day_view_top_scroll (EDayView	*day_view,
 170 				   gfloat	 pages_to_scroll);
 171 
 172 static void e_day_view_update_top_scroll (EDayView *day_view, gboolean scroll_to_top);
 173 
 174 static void e_day_view_on_canvas_realized (GtkWidget *widget,
 175 					   EDayView *day_view);
 176 
 177 static gboolean e_day_view_on_top_canvas_button_press (GtkWidget *widget,
 178 						       GdkEventButton *event,
 179 						       EDayView *day_view);
 180 static gboolean e_day_view_on_top_canvas_button_release (GtkWidget *widget,
 181 							 GdkEventButton *event,
 182 							 EDayView *day_view);
 183 static gboolean e_day_view_on_top_canvas_motion (GtkWidget *widget,
 184 						 GdkEventMotion *event,
 185 						 EDayView *day_view);
 186 
 187 static gboolean e_day_view_on_main_canvas_button_press (GtkWidget *widget,
 188 							GdkEventButton *event,
 189 							EDayView *day_view);
 190 static gboolean e_day_view_on_main_canvas_button_release (GtkWidget *widget,
 191 							  GdkEventButton *event,
 192 							  EDayView *day_view);
 193 
 194 static gboolean e_day_view_on_top_canvas_scroll (GtkWidget *widget,
 195 						 GdkEventScroll *scroll,
 196 						 EDayView *day_view);
 197 
 198 static gboolean e_day_view_on_main_canvas_scroll (GtkWidget *widget,
 199 						  GdkEventScroll *scroll,
 200 						  EDayView *day_view);
 201 static gboolean e_day_view_on_time_canvas_scroll (GtkWidget *widget,
 202 						  GdkEventScroll *scroll,
 203 						  EDayView *day_view);
 204 static gboolean e_day_view_on_main_canvas_motion (GtkWidget *widget,
 205 						  GdkEventMotion *event,
 206 						  EDayView *day_view);
 207 static gboolean e_day_view_convert_event_coords (EDayView *day_view,
 208 						 GdkEvent *event,
 209 						 GdkWindow *window,
 210 						 gint *x_return,
 211 						 gint *y_return);
 212 static void e_day_view_update_long_event_resize (EDayView *day_view,
 213 						 gint day);
 214 static void e_day_view_update_resize (EDayView *day_view,
 215 				      gint row);
 216 static void e_day_view_finish_long_event_resize (EDayView *day_view);
 217 static void e_day_view_finish_resize (EDayView *day_view);
 218 static void e_day_view_abort_resize (EDayView *day_view);
 219 
 220 static gboolean e_day_view_on_long_event_button_press (EDayView		*day_view,
 221 						       gint		 event_num,
 222 						       GdkEventButton	*event,
 223 						       ECalendarViewPosition  pos,
 224 						       gint		 event_x,
 225 						       gint		 event_y);
 226 static gboolean e_day_view_on_event_button_press (EDayView	 *day_view,
 227 						  gint		  day,
 228 						  gint		  event_num,
 229 						  GdkEventButton *event,
 230 						  ECalendarViewPosition pos,
 231 						  gint		  event_x,
 232 						  gint		  event_y);
 233 static void e_day_view_on_long_event_click (EDayView *day_view,
 234 					    gint event_num,
 235 					    GdkEventButton  *bevent,
 236 					    ECalendarViewPosition pos,
 237 					    gint	     event_x,
 238 					    gint	     event_y);
 239 static void e_day_view_on_event_click (EDayView *day_view,
 240 				       gint day,
 241 				       gint event_num,
 242 				       GdkEventButton  *event,
 243 				       ECalendarViewPosition pos,
 244 				       gint		event_x,
 245 				       gint		event_y);
 246 static void e_day_view_on_event_double_click (EDayView *day_view,
 247 					      gint day,
 248 					      gint event_num);
 249 static void e_day_view_on_event_right_click (EDayView *day_view,
 250 					     GdkEventButton *bevent,
 251 					     gint day,
 252 					     gint event_num);
 253 static void e_day_view_show_popup_menu (EDayView *day_view,
 254 					GdkEventButton *event,
 255 					gint day,
 256 					gint event_num);
 257 
 258 static void e_day_view_recalc_day_starts (EDayView *day_view,
 259 					  time_t start_time);
 260 static void e_day_view_recalc_num_rows	(EDayView	*day_view);
 261 static void e_day_view_recalc_cell_sizes	(EDayView	*day_view);
 262 
 263 static ECalendarViewPosition e_day_view_convert_position_in_top_canvas (EDayView *day_view,
 264 								   gint x,
 265 								   gint y,
 266 								   gint *day_return,
 267 								   gint *event_num_return);
 268 static ECalendarViewPosition e_day_view_convert_position_in_main_canvas (EDayView *day_view,
 269 								    gint x,
 270 								    gint y,
 271 								    gint *day_return,
 272 								    gint *row_return,
 273 								    gint *event_num_return);
 274 static gboolean e_day_view_find_event_from_uid (EDayView *day_view,
 275 						ECalClient *client,
 276 						const gchar *uid,
 277 						const gchar *rid,
 278 						gint *day_return,
 279 						gint *event_num_return);
 280 
 281 typedef gboolean (* EDayViewForeachEventCallback) (EDayView *day_view,
 282 						   gint day,
 283 						   gint event_num,
 284 						   gpointer data);
 285 
 286 static void e_day_view_foreach_event		(EDayView	*day_view,
 287 						 EDayViewForeachEventCallback callback,
 288 						 gpointer	 data);
 289 static void e_day_view_foreach_event_with_uid (EDayView *day_view,
 290 					       const gchar *uid,
 291 					       EDayViewForeachEventCallback callback,
 292 					       gpointer data);
 293 
 294 static void e_day_view_free_events (EDayView *day_view);
 295 static void e_day_view_free_event_array (EDayView *day_view,
 296 					 GArray *array);
 297 static gint e_day_view_add_event (ESourceRegistry *registry,
 298 				 ECalComponent *comp,
 299 				 time_t	  start,
 300 				 time_t	  end,
 301 				 gpointer data);
 302 static void e_day_view_update_event_label (EDayView *day_view,
 303 					   gint day,
 304 					   gint event_num);
 305 static void e_day_view_update_long_event_label (EDayView *day_view,
 306 						gint event_num);
 307 
 308 static void e_day_view_reshape_long_events (EDayView *day_view);
 309 static void e_day_view_reshape_long_event (EDayView *day_view,
 310 					   gint event_num);
 311 static void e_day_view_reshape_day_events (EDayView *day_view,
 312 					   gint day);
 313 static void e_day_view_reshape_day_event (EDayView *day_view,
 314 					  gint	day,
 315 					  gint	event_num);
 316 static void e_day_view_reshape_main_canvas_resize_bars (EDayView *day_view);
 317 
 318 static void e_day_view_ensure_events_sorted (EDayView *day_view);
 319 
 320 static void e_day_view_start_editing_event (EDayView *day_view,
 321 					    gint day,
 322 					    gint event_num,
 323 					    GdkEventKey *key_event);
 324 static void e_day_view_stop_editing_event (EDayView *day_view);
 325 static gboolean e_day_view_on_text_item_event (GnomeCanvasItem *item,
 326 					       GdkEvent *event,
 327 					       EDayView *day_view);
 328 static gboolean e_day_view_event_move (ECalendarView *cal_view, ECalViewMoveDirection direction);
 329 static void e_day_view_change_event_time (EDayView *day_view, time_t start_dt,
 330 time_t end_dt);
 331 static void e_day_view_change_event_end_time_up (EDayView *day_view);
 332 static void e_day_view_change_event_end_time_down (EDayView *day_view);
 333 static void e_day_view_on_editing_started (EDayView *day_view,
 334 					   GnomeCanvasItem *item);
 335 static void e_day_view_on_editing_stopped (EDayView *day_view,
 336 					   GnomeCanvasItem *item);
 337 
 338 static time_t e_day_view_convert_grid_position_to_time (EDayView *day_view,
 339 							gint col,
 340 							gint row);
 341 static gboolean e_day_view_convert_time_to_grid_position (EDayView *day_view,
 342 							  time_t time,
 343 							  gint *col,
 344 							  gint *row);
 345 
 346 static void e_day_view_start_auto_scroll (EDayView *day_view,
 347 					  gboolean scroll_up);
 348 static gboolean e_day_view_auto_scroll_handler (gpointer data);
 349 
 350 static gboolean e_day_view_on_top_canvas_drag_motion (GtkWidget      *widget,
 351 						      GdkDragContext *context,
 352 						      gint            x,
 353 						      gint            y,
 354 						      guint           time,
 355 						      EDayView	  *day_view);
 356 static void e_day_view_update_top_canvas_drag (EDayView *day_view,
 357 					       gint day);
 358 static void e_day_view_reshape_top_canvas_drag_item (EDayView *day_view);
 359 static gboolean e_day_view_on_main_canvas_drag_motion (GtkWidget      *widget,
 360 						       GdkDragContext *context,
 361 						       gint            x,
 362 						       gint            y,
 363 						       guint           time,
 364 						       EDayView	  *day_view);
 365 static void e_day_view_reshape_main_canvas_drag_item (EDayView *day_view);
 366 static void e_day_view_update_main_canvas_drag (EDayView *day_view,
 367 						gint row,
 368 						gint day);
 369 static void e_day_view_on_top_canvas_drag_leave (GtkWidget      *widget,
 370 						 GdkDragContext *context,
 371 						 guint           time,
 372 						 EDayView	     *day_view);
 373 static void e_day_view_on_main_canvas_drag_leave (GtkWidget      *widget,
 374 						  GdkDragContext *context,
 375 						  guint           time,
 376 						  EDayView	 *day_view);
 377 static void e_day_view_on_drag_begin (GtkWidget      *widget,
 378 				      GdkDragContext *context,
 379 				      EDayView	   *day_view);
 380 static void e_day_view_on_drag_end (GtkWidget      *widget,
 381 				    GdkDragContext *context,
 382 				    EDayView       *day_view);
 383 static void e_day_view_on_drag_data_get (GtkWidget          *widget,
 384 					 GdkDragContext     *context,
 385 					 GtkSelectionData   *selection_data,
 386 					 guint               info,
 387 					 guint               time,
 388 					 EDayView		*day_view);
 389 static void e_day_view_on_top_canvas_drag_data_received (GtkWidget	*widget,
 390 							 GdkDragContext *context,
 391 							 gint            x,
 392 							 gint            y,
 393 							 GtkSelectionData *data,
 394 							 guint           info,
 395 							 guint           time,
 396 							 EDayView	*day_view);
 397 static void e_day_view_on_main_canvas_drag_data_received (GtkWidget      *widget,
 398 							  GdkDragContext *context,
 399 							  gint            x,
 400 							  gint            y,
 401 							  GtkSelectionData *data,
 402 							  guint           info,
 403 							  guint           time,
 404 							  EDayView	 *day_view);
 405 
 406 static gboolean e_day_view_remove_event_cb (EDayView *day_view,
 407 					    gint day,
 408 					    gint event_num,
 409 					    gpointer data);
 410 static void e_day_view_normalize_selection (EDayView *day_view);
 411 static gboolean e_day_view_set_show_times_cb	(EDayView	*day_view,
 412 						 gint		 day,
 413 						 gint		 event_num,
 414 						 gpointer	 data);
 415 static time_t e_day_view_find_work_week_start	(EDayView	*day_view,
 416 						 time_t		 start_time);
 417 static void e_day_view_recalc_work_week		(EDayView	*day_view);
 418 static void e_day_view_recalc_work_week_days_shown	(EDayView	*day_view);
 419 
 420 static void e_day_view_queue_layout (EDayView *day_view);
 421 static void e_day_view_cancel_layout (EDayView *day_view);
 422 static gboolean e_day_view_layout_timeout_cb (gpointer data);
 423 static void tooltip_destroy (EDayView *day_view, GnomeCanvasItem *item);
 424 
 425 enum {
 426 	PROP_0,
 427 	PROP_MARCUS_BAINS_SHOW_LINE,
 428 	PROP_MARCUS_BAINS_DAY_VIEW_COLOR,
 429 	PROP_MARCUS_BAINS_TIME_BAR_COLOR,
 430 	PROP_WORKING_DAYS
 431 };
 432 
 433 G_DEFINE_TYPE (EDayView, e_day_view, E_TYPE_CALENDAR_VIEW)
 434 
 435 static void
 436 day_view_notify_time_divisions_cb (EDayView *day_view)
 437 {
 438 	gint day;
 439 
 440 	e_day_view_recalc_num_rows (day_view);
 441 
 442 	/* If we aren't visible, we'll sort it out later. */
 443 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
 444 		e_day_view_free_events (day_view);
 445 		day_view->requires_update = TRUE;
 446 		return;
 447 	}
 448 
 449 	for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
 450 		day_view->need_layout[day] = TRUE;
 451 
 452 	/* We need to update all the day event labels since the start & end
 453 	 * times may or may not be on row boundaries any more. */
 454 	e_day_view_foreach_event (day_view,
 455 				  e_day_view_set_show_times_cb, NULL);
 456 
 457 	/* We must layout the events before updating the scroll region, since
 458 	 * that will result in a redraw which would crash otherwise. */
 459 	e_day_view_check_layout (day_view);
 460 	gtk_widget_queue_draw (day_view->time_canvas);
 461 	gtk_widget_queue_draw (day_view->main_canvas);
 462 
 463 	e_day_view_update_scroll_regions (day_view);
 464 }
 465 
 466 static void
 467 day_view_notify_week_start_day_cb (EDayView *day_view)
 468 {
 469 	/* XXX Write a EWorkWeekView subclass, like EMonthView. */
 470 
 471 	if (day_view->work_week_view)
 472 		e_day_view_recalc_work_week (day_view);
 473 }
 474 
 475 static void
 476 day_view_set_property (GObject *object,
 477                        guint property_id,
 478                        const GValue *value,
 479                        GParamSpec *pspec)
 480 {
 481 	switch (property_id) {
 482 		case PROP_MARCUS_BAINS_SHOW_LINE:
 483 			e_day_view_marcus_bains_set_show_line (
 484 				E_DAY_VIEW (object),
 485 				g_value_get_boolean (value));
 486 			return;
 487 
 488 		case PROP_MARCUS_BAINS_DAY_VIEW_COLOR:
 489 			e_day_view_marcus_bains_set_day_view_color (
 490 				E_DAY_VIEW (object),
 491 				g_value_get_string (value));
 492 			return;
 493 
 494 		case PROP_MARCUS_BAINS_TIME_BAR_COLOR:
 495 			e_day_view_marcus_bains_set_time_bar_color (
 496 				E_DAY_VIEW (object),
 497 				g_value_get_string (value));
 498 			return;
 499 
 500 		case PROP_WORKING_DAYS:
 501 			e_day_view_set_working_days (
 502 				E_DAY_VIEW (object),
 503 				g_value_get_int (value));
 504 			return;
 505 	}
 506 
 507 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 508 }
 509 
 510 static void
 511 day_view_get_property (GObject *object,
 512                        guint property_id,
 513                        GValue *value,
 514                        GParamSpec *pspec)
 515 {
 516 	switch (property_id) {
 517 		case PROP_MARCUS_BAINS_SHOW_LINE:
 518 			g_value_set_boolean (
 519 				value,
 520 				e_day_view_marcus_bains_get_show_line (
 521 				E_DAY_VIEW (object)));
 522 			return;
 523 
 524 		case PROP_MARCUS_BAINS_DAY_VIEW_COLOR:
 525 			g_value_set_string (
 526 				value,
 527 				e_day_view_marcus_bains_get_day_view_color (
 528 				E_DAY_VIEW (object)));
 529 			return;
 530 
 531 		case PROP_MARCUS_BAINS_TIME_BAR_COLOR:
 532 			g_value_set_string (
 533 				value,
 534 				e_day_view_marcus_bains_get_time_bar_color (
 535 				E_DAY_VIEW (object)));
 536 			return;
 537 
 538 		case PROP_WORKING_DAYS:
 539 			g_value_set_int (
 540 				value,
 541 				e_day_view_get_working_days (
 542 				E_DAY_VIEW (object)));
 543 			return;
 544 	}
 545 
 546 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 547 }
 548 
 549 static void
 550 day_view_constructed (GObject *object)
 551 {
 552 	EDayView *day_view;
 553 	ECalModel *model;
 554 
 555 	day_view = E_DAY_VIEW (object);
 556 
 557 	/* Chain up to parent's constructed() method. */
 558 	G_OBJECT_CLASS (e_day_view_parent_class)->constructed (object);
 559 
 560 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
 561 
 562 	g_signal_connect_swapped (
 563 		day_view, "notify::time-divisions",
 564 		G_CALLBACK (day_view_notify_time_divisions_cb), day_view);
 565 
 566 	g_signal_connect_swapped (
 567 		model, "notify::week-start-day",
 568 		G_CALLBACK (day_view_notify_week_start_day_cb), day_view);
 569 
 570 	g_signal_connect_swapped (
 571 		model, "notify::work-day-start-hour",
 572 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
 573 
 574 	g_signal_connect_swapped (
 575 		model, "notify::work-day-start-minute",
 576 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
 577 
 578 	g_signal_connect_swapped (
 579 		model, "notify::work-day-end-hour",
 580 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
 581 
 582 	g_signal_connect_swapped (
 583 		model, "notify::work-day-end-minute",
 584 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
 585 }
 586 
 587 static void
 588 e_day_view_class_init (EDayViewClass *class)
 589 {
 590 	GObjectClass *object_class;
 591 	GtkWidgetClass *widget_class;
 592 	ECalendarViewClass *view_class;
 593 
 594 	object_class = G_OBJECT_CLASS (class);
 595 	object_class->set_property = day_view_set_property;
 596 	object_class->get_property = day_view_get_property;
 597 	object_class->constructed = day_view_constructed;
 598 	object_class->dispose = e_day_view_dispose;
 599 
 600 	widget_class = GTK_WIDGET_CLASS (class);
 601 	widget_class->realize = e_day_view_realize;
 602 	widget_class->unrealize = e_day_view_unrealize;
 603 	widget_class->style_set = e_day_view_style_set;
 604 	widget_class->size_allocate = e_day_view_size_allocate;
 605 	widget_class->focus_in_event = e_day_view_focus_in;
 606 	widget_class->focus_out_event = e_day_view_focus_out;
 607 	widget_class->key_press_event = e_day_view_key_press;
 608 	widget_class->focus = e_day_view_focus;
 609 	widget_class->popup_menu = e_day_view_popup_menu;
 610 
 611 	view_class = E_CALENDAR_VIEW_CLASS (class);
 612 	view_class->get_selected_events = e_day_view_get_selected_events;
 613 	view_class->get_selected_time_range = e_day_view_get_selected_time_range;
 614 	view_class->set_selected_time_range = e_day_view_set_selected_time_range;
 615 	view_class->get_visible_time_range = e_day_view_get_visible_time_range;
 616 	view_class->paste_text = e_day_view_paste_text;
 617 
 618 	/* XXX Should these be constructor properties? */
 619 
 620 	g_object_class_install_property (
 621 		object_class,
 622 		PROP_MARCUS_BAINS_SHOW_LINE,
 623 		g_param_spec_boolean (
 624 			"marcus-bains-show-line",
 625 			"Marcus Bains Show Line",
 626 			NULL,
 627 			TRUE,
 628 			G_PARAM_READWRITE));
 629 
 630 	g_object_class_install_property (
 631 		object_class,
 632 		PROP_MARCUS_BAINS_DAY_VIEW_COLOR,
 633 		g_param_spec_string (
 634 			"marcus-bains-day-view-color",
 635 			"Marcus Bains Day View Color",
 636 			NULL,
 637 			NULL,
 638 			G_PARAM_READWRITE));
 639 
 640 	g_object_class_install_property (
 641 		object_class,
 642 		PROP_MARCUS_BAINS_TIME_BAR_COLOR,
 643 		g_param_spec_string (
 644 			"marcus-bains-time-bar-color",
 645 			"Marcus Bains Time Bar Color",
 646 			NULL,
 647 			NULL,
 648 			G_PARAM_READWRITE));
 649 
 650 	/* FIXME Make this a real GFlags type. */
 651 	g_object_class_install_property (
 652 		object_class,
 653 		PROP_WORKING_DAYS,
 654 		g_param_spec_int (
 655 			"working-days",
 656 			"Working Days",
 657 			NULL,
 658 			0x00,
 659 			0x7f,
 660 			0,
 661 			G_PARAM_READWRITE));
 662 
 663 	/* init the accessibility support for e_day_view */
 664 	e_day_view_a11y_init ();
 665 }
 666 
 667 static void
 668 time_range_changed_cb (ECalModel *model,
 669                        time_t start_time,
 670                        time_t end_time,
 671                        gpointer user_data)
 672 {
 673 	EDayView *day_view = E_DAY_VIEW (user_data);
 674 	EDayViewTimeItem *eti;
 675 	time_t lower;
 676 
 677 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
 678 
 679 	/* Calculate the first day that should be shown, based on start_time
 680 	 * and the days_shown setting. If we are showing 1 day it is just the
 681 	 * start of the day given by start_time, otherwise it is the previous
 682 	 * work-week start day. */
 683 	if (!day_view->work_week_view) {
 684 		lower = time_day_begin_with_zone (start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
 685 	} else {
 686 		lower = e_day_view_find_work_week_start (day_view, start_time);
 687 	}
 688 
 689 	/* See if we need to change the days shown. */
 690 	if (lower != day_view->lower)
 691 		e_day_view_recalc_day_starts (day_view, lower);
 692 
 693 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
 694 		e_day_view_free_events (day_view);
 695 		day_view->requires_update = TRUE;
 696 		return;
 697 	}
 698 
 699 	/* If we don't show the new selection, don't preserve it */
 700 	if (day_view->selection_start_day == -1 || day_view->days_shown <= day_view->selection_start_day)
 701 		e_day_view_set_selected_time_range (E_CALENDAR_VIEW (day_view), start_time, end_time);
 702 
 703 	if (day_view->selection_start_row != -1)
 704 		e_day_view_ensure_rows_visible (day_view, day_view->selection_start_row, day_view->selection_start_row);
 705 
 706 	/* update the time canvas to show proper date in it */
 707 	eti = E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item);
 708 	if (eti && e_day_view_time_item_get_second_zone (eti))
 709 		gtk_widget_queue_draw (day_view->time_canvas);
 710 }
 711 
 712 static void
 713 process_component (EDayView *day_view,
 714                    ECalModelComponent *comp_data)
 715 {
 716 	const gchar *uid;
 717 	gchar *rid = NULL;
 718 	ECalModel *model;
 719 	ECalComponent *comp;
 720 	ESourceRegistry *registry;
 721 	AddEventData add_event_data;
 722 
 723 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
 724 	registry = e_cal_model_get_registry (model);
 725 
 726 	/* If our time hasn't been set yet, just return. */
 727 	if (day_view->lower == 0 && day_view->upper == 0)
 728 		return;
 729 
 730 	comp = e_cal_component_new ();
 731 	if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp))) {
 732 		g_object_unref (comp);
 733 
 734 		g_message (G_STRLOC ": Could not set icalcomponent on ECalComponent");
 735 		return;
 736 	}
 737 
 738 	e_cal_component_get_uid (comp, &uid);
 739 	if (e_cal_component_is_instance (comp))
 740 		rid = e_cal_component_get_recurid_as_string (comp);
 741 	else
 742 		rid = NULL;
 743 	/* rid is never used below here, why? */
 744 
 745 	/* Add the object */
 746 	add_event_data.day_view = day_view;
 747 	add_event_data.comp_data = comp_data;
 748 	e_day_view_add_event (
 749 		registry, comp, comp_data->instance_start,
 750 		comp_data->instance_end, &add_event_data);
 751 
 752 	g_object_unref (comp);
 753 	g_free (rid);
 754 }
 755 
 756 static void
 757 update_row (EDayView *day_view,
 758             gint row)
 759 {
 760 	ECalModelComponent *comp_data;
 761 	ECalModel *model;
 762 	gint day, event_num;
 763 	const gchar *uid = NULL;
 764 	gchar *rid = NULL;
 765 
 766 	e_day_view_stop_editing_event (day_view);
 767 
 768 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
 769 	comp_data = e_cal_model_get_component_at (model, row);
 770 	g_return_if_fail (comp_data != NULL);
 771 
 772 	uid = icalcomponent_get_uid (comp_data->icalcomp);
 773 	if (e_cal_util_component_is_instance (comp_data->icalcomp)) {
 774 		icalproperty *prop;
 775 
 776 		prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_RECURRENCEID_PROPERTY);
 777 		if (prop)
 778 			rid = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (comp_data->icalcomp));
 779 	}
 780 
 781 	if (e_day_view_find_event_from_uid (day_view, comp_data->client, uid, rid, &day, &event_num))
 782 		e_day_view_remove_event_cb (day_view, day, event_num, NULL);
 783 
 784 	g_free (rid);
 785 
 786 	process_component (day_view, comp_data);
 787 
 788 	gtk_widget_queue_draw (day_view->top_canvas);
 789 	gtk_widget_queue_draw (day_view->main_canvas);
 790 	e_day_view_queue_layout (day_view);
 791 }
 792 
 793 static void
 794 model_row_changed_cb (ETableModel *etm,
 795                       gint row,
 796                       gpointer user_data)
 797 {
 798 	EDayView *day_view = E_DAY_VIEW (user_data);
 799 
 800 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
 801 		e_day_view_free_events (day_view);
 802 		day_view->requires_update = TRUE;
 803 		return;
 804 	}
 805 
 806 	update_row (day_view, row);
 807 }
 808 
 809 static void
 810 model_cell_changed_cb (ETableModel *etm,
 811                        gint col,
 812                        gint row,
 813                        gpointer user_data)
 814 {
 815 	EDayView *day_view = E_DAY_VIEW (user_data);
 816 
 817 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
 818 		e_day_view_free_events (day_view);
 819 		day_view->requires_update = TRUE;
 820 		return;
 821 	}
 822 
 823 	update_row (day_view, row);
 824 }
 825 
 826 static void
 827 model_rows_inserted_cb (ETableModel *etm,
 828                         gint row,
 829                         gint count,
 830                         gpointer user_data)
 831 {
 832 	EDayView *day_view = E_DAY_VIEW (user_data);
 833 	ECalModel *model;
 834 	gint i;
 835 
 836 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
 837 		e_day_view_free_events (day_view);
 838 		day_view->requires_update = TRUE;
 839 		return;
 840 	}
 841 
 842 	e_day_view_stop_editing_event (day_view);
 843 
 844 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
 845 	for (i = 0; i < count; i++) {
 846 		ECalModelComponent *comp_data;
 847 
 848 		comp_data = e_cal_model_get_component_at (model, row + i);
 849 		if (comp_data == NULL) {
 850 			g_warning ("comp_data is NULL\n");
 851 			continue;
 852 		}
 853 		process_component (day_view, comp_data);
 854 	}
 855 
 856 	gtk_widget_queue_draw (day_view->top_canvas);
 857 	gtk_widget_queue_draw (day_view->main_canvas);
 858 	e_day_view_queue_layout (day_view);
 859 
 860 }
 861 
 862 static void
 863 model_comps_deleted_cb (ETableModel *etm,
 864                         gpointer data,
 865                         gpointer user_data)
 866 {
 867 	EDayView *day_view = E_DAY_VIEW (user_data);
 868 	GSList *l, *list = data;
 869 
 870 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
 871 		e_day_view_free_events (day_view);
 872 		day_view->requires_update = TRUE;
 873 		return;
 874 	}
 875 
 876 	e_day_view_stop_editing_event (day_view);
 877 
 878 	for (l = list; l != NULL; l = g_slist_next (l)) {
 879 		ECalModelComponent *comp_data = l->data;
 880 		gint day, event_num;
 881 		const gchar *uid = NULL;
 882 		gchar *rid = NULL;
 883 
 884 		uid = icalcomponent_get_uid (comp_data->icalcomp);
 885 		if (e_cal_util_component_is_instance (comp_data->icalcomp)) {
 886 			icalproperty *prop;
 887 
 888 			prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_RECURRENCEID_PROPERTY);
 889 			if (prop)
 890 				rid = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (comp_data->icalcomp));
 891 		}
 892 
 893 		if (e_day_view_find_event_from_uid (day_view, comp_data->client, uid, rid, &day, &event_num))
 894 			e_day_view_remove_event_cb (day_view, day, event_num, NULL);
 895 
 896 		g_free (rid);
 897 	}
 898 
 899 	gtk_widget_queue_draw (day_view->top_canvas);
 900 	gtk_widget_queue_draw (day_view->main_canvas);
 901 	e_day_view_queue_layout (day_view);
 902 }
 903 
 904 static void
 905 timezone_changed_cb (ECalModel *cal_model,
 906                      icaltimezone *old_zone,
 907                      icaltimezone *new_zone,
 908                      gpointer user_data)
 909 {
 910 	struct icaltimetype tt;
 911 	time_t lower;
 912 	EDayView *day_view = (EDayView *) user_data;
 913 	ECalendarView *cal_view = (ECalendarView *) day_view;
 914 
 915 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
 916 
 917 	if (!cal_view->in_focus) {
 918 		e_day_view_free_events (day_view);
 919 		day_view->requires_update = TRUE;
 920 		return;
 921 	}
 922 
 923 	/* If our time hasn't been set yet, just return. */
 924 	if (day_view->lower == 0 && day_view->upper == 0)
 925 		return;
 926 
 927 	/* Recalculate the new start of the first day. We just use exactly
 928 	 * the same time, but with the new timezone. */
 929 	tt = icaltime_from_timet_with_zone (
 930 		day_view->lower, FALSE,
 931 		old_zone);
 932 
 933 	lower = icaltime_as_timet_with_zone (tt, new_zone);
 934 
 935 	e_day_view_recalc_day_starts (day_view, lower);
 936 	e_day_view_update_query (day_view);
 937 }
 938 
 939 static void
 940 e_day_view_init (EDayView *day_view)
 941 {
 942 	gint day;
 943 	GnomeCanvasGroup *canvas_group;
 944 	GtkAdjustment *adjustment;
 945 	GtkScrollable *scrollable;
 946 	GtkWidget *w;
 947 
 948 	gtk_widget_set_can_focus (GTK_WIDGET (day_view), TRUE);
 949 
 950 	day_view->long_events = g_array_new (
 951 		FALSE, FALSE,
 952 		sizeof (EDayViewEvent));
 953 	day_view->long_events_sorted = TRUE;
 954 	day_view->long_events_need_layout = FALSE;
 955 	day_view->long_events_need_reshape = FALSE;
 956 
 957 	day_view->layout_timeout_id = 0;
 958 
 959 	for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) {
 960 		day_view->events[day] = g_array_new (
 961 			FALSE, FALSE,
 962 			sizeof (EDayViewEvent));
 963 		day_view->events_sorted[day] = TRUE;
 964 		day_view->need_layout[day] = FALSE;
 965 		day_view->need_reshape[day] = FALSE;
 966 	}
 967 
 968 	/* These indicate that the times haven't been set. */
 969 	day_view->lower = 0;
 970 	day_view->upper = 0;
 971 
 972 	day_view->work_week_view = FALSE;
 973 	day_view->days_shown = 1;
 974 
 975 	day_view->date_format = E_DAY_VIEW_DATE_FULL;
 976 	day_view->rows_in_top_display = 0;
 977 
 978 	/* Note that these don't work yet. It would need a few fixes to the
 979 	 * way event->start_minute and event->end_minute are used, and there
 980 	 * may be problems with events that go outside the visible times. */
 981 	day_view->first_hour_shown = 0;
 982 	day_view->first_minute_shown = 0;
 983 	day_view->last_hour_shown = 24;
 984 	day_view->last_minute_shown = 0;
 985 
 986 	e_day_view_recalc_num_rows (day_view);
 987 
 988 	day_view->working_days = E_DAY_VIEW_MONDAY | E_DAY_VIEW_TUESDAY
 989 		| E_DAY_VIEW_WEDNESDAY | E_DAY_VIEW_THURSDAY
 990 		| E_DAY_VIEW_FRIDAY;
 991 
 992 	day_view->show_event_end_times = TRUE;
 993 	day_view->scroll_to_work_day = TRUE;
 994 
 995 	day_view->marcus_bains_show_line = TRUE;
 996 	day_view->marcus_bains_day_view_color = NULL;
 997 	day_view->marcus_bains_time_bar_color = NULL;
 998 
 999 	day_view->editing_event_day = -1;
1000 	day_view->editing_event_num = -1;
1001 
1002 	day_view->resize_event_num = -1;
1003 	day_view->resize_bars_event_day = -1;
1004 	day_view->resize_bars_event_num = -1;
1005 
1006 	day_view->last_edited_comp_string = NULL;
1007 
1008 	day_view->selection_start_row = -1;
1009 	day_view->selection_start_day = -1;
1010 	day_view->selection_end_row = -1;
1011 	day_view->selection_end_day = -1;
1012 	day_view->selection_is_being_dragged = FALSE;
1013 	day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
1014 	day_view->selection_in_top_canvas = FALSE;
1015 	day_view->drag_event_day = -1;
1016 	day_view->drag_event_num = -1;
1017 	day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
1018 
1019 	day_view->pressed_event_day = -1;
1020 
1021 	day_view->drag_event_day = -1;
1022 	day_view->drag_last_day = -1;
1023 
1024 	day_view->auto_scroll_timeout_id = 0;
1025 
1026 	day_view->large_font_desc = NULL;
1027 	day_view->small_font_desc = NULL;
1028 
1029 	/* String to use in 12-hour time format for times in the morning. */
1030 	day_view->am_string = _("am");
1031 
1032 	/* String to use in 12-hour time format for times in the afternoon. */
1033 	day_view->pm_string = _("pm");
1034 
1035 	day_view->bc_event_time = 0;
1036 	day_view->before_click_dtstart = 0;
1037 	day_view->before_click_dtend = 0;
1038 
1039 	day_view->week_number_label = gtk_label_new ("");
1040 	gtk_table_attach (GTK_TABLE (day_view), day_view->week_number_label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
1041 
1042 	/*
1043 	 * Top Canvas
1044 	 */
1045 	w = gtk_vbox_new (FALSE, 0);
1046 
1047 	day_view->top_dates_canvas = e_canvas_new ();
1048 	gtk_box_pack_start (GTK_BOX (w), day_view->top_dates_canvas, TRUE, TRUE, 0);
1049 	day_view->top_canvas = e_canvas_new ();
1050 	gtk_box_pack_end (GTK_BOX (w), day_view->top_canvas, TRUE, TRUE, 0);
1051 
1052 	gtk_table_attach (
1053 		GTK_TABLE (day_view), w,
1054 		1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1055 	gtk_widget_show_all (w);
1056 
1057 	g_signal_connect_after (
1058 		day_view->top_canvas, "button_press_event",
1059 		G_CALLBACK (e_day_view_on_top_canvas_button_press), day_view);
1060 	g_signal_connect (
1061 		day_view->top_canvas, "button_release_event",
1062 		G_CALLBACK (e_day_view_on_top_canvas_button_release), day_view);
1063 	g_signal_connect (
1064 		day_view->top_canvas, "scroll_event",
1065 		G_CALLBACK (e_day_view_on_top_canvas_scroll), day_view);
1066 	g_signal_connect (
1067 		day_view->top_canvas, "motion_notify_event",
1068 		G_CALLBACK (e_day_view_on_top_canvas_motion), day_view);
1069 	g_signal_connect (
1070 		day_view->top_canvas, "drag_motion",
1071 		G_CALLBACK (e_day_view_on_top_canvas_drag_motion), day_view);
1072 	g_signal_connect (
1073 		day_view->top_canvas, "drag_leave",
1074 		G_CALLBACK (e_day_view_on_top_canvas_drag_leave), day_view);
1075 
1076 	g_signal_connect (
1077 		day_view->top_canvas, "drag_begin",
1078 		G_CALLBACK (e_day_view_on_drag_begin), day_view);
1079 	g_signal_connect (
1080 		day_view->top_canvas, "drag_end",
1081 		G_CALLBACK (e_day_view_on_drag_end), day_view);
1082 	g_signal_connect (
1083 		day_view->top_canvas, "drag_data_get",
1084 		G_CALLBACK (e_day_view_on_drag_data_get), day_view);
1085 	g_signal_connect (
1086 		day_view->top_canvas, "drag_data_received",
1087 		G_CALLBACK (e_day_view_on_top_canvas_drag_data_received), day_view);
1088 
1089 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_dates_canvas)->root);
1090 
1091 	day_view->top_dates_canvas_item =
1092 		gnome_canvas_item_new (
1093 			canvas_group,
1094 			e_day_view_top_item_get_type (),
1095 			"EDayViewTopItem::day_view", day_view,
1096 			"EDayViewTopItem::show_dates", TRUE,
1097 			NULL);
1098 	gtk_widget_set_size_request (day_view->top_dates_canvas, -1, day_view->top_row_height);
1099 
1100 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_canvas)->root);
1101 
1102 	day_view->top_canvas_item =
1103 		gnome_canvas_item_new (
1104 			canvas_group,
1105 			e_day_view_top_item_get_type (),
1106 			"EDayViewTopItem::day_view", day_view,
1107 			"EDayViewTopItem::show_dates", FALSE,
1108 			NULL);
1109 
1110 	day_view->drag_long_event_rect_item =
1111 		gnome_canvas_item_new (
1112 			canvas_group,
1113 			gnome_canvas_rect_get_type (),
1114 			"line_width", 1.0,
1115 			NULL);
1116 	gnome_canvas_item_hide (day_view->drag_long_event_rect_item);
1117 
1118 	day_view->drag_long_event_item =
1119 		gnome_canvas_item_new (
1120 			canvas_group,
1121 			e_text_get_type (),
1122 			"line_wrap", TRUE,
1123 			"clip", TRUE,
1124 			"max_lines", 1,
1125 			"editable", TRUE,
1126 			"fill_color_rgba", GNOME_CANVAS_COLOR (0, 0, 0),
1127 			NULL);
1128 	gnome_canvas_item_hide (day_view->drag_long_event_item);
1129 
1130 	/*
1131 	 * Main Canvas
1132 	 */
1133 	day_view->main_canvas = e_canvas_new ();
1134 	gtk_table_attach (
1135 		GTK_TABLE (day_view), day_view->main_canvas,
1136 		1, 2, 1, 2,
1137 		GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1138 	gtk_widget_show (day_view->main_canvas);
1139 	g_signal_connect (
1140 		day_view->main_canvas, "realize",
1141 		G_CALLBACK (e_day_view_on_canvas_realized), day_view);
1142 
1143 	g_signal_connect (
1144 		day_view->main_canvas, "button_press_event",
1145 		G_CALLBACK (e_day_view_on_main_canvas_button_press), day_view);
1146 	g_signal_connect (
1147 		day_view->main_canvas, "button_release_event",
1148 		G_CALLBACK (e_day_view_on_main_canvas_button_release),
1149 		day_view);
1150 	g_signal_connect (
1151 		day_view->main_canvas, "scroll_event",
1152 		G_CALLBACK (e_day_view_on_main_canvas_scroll), day_view);
1153 	g_signal_connect (
1154 		day_view->main_canvas, "motion_notify_event",
1155 		G_CALLBACK (e_day_view_on_main_canvas_motion), day_view);
1156 	g_signal_connect (
1157 		day_view->main_canvas, "drag_motion",
1158 		G_CALLBACK (e_day_view_on_main_canvas_drag_motion), day_view);
1159 	g_signal_connect (
1160 		day_view->main_canvas, "drag_leave",
1161 		G_CALLBACK (e_day_view_on_main_canvas_drag_leave), day_view);
1162 
1163 	g_signal_connect (
1164 		day_view->main_canvas, "drag_begin",
1165 		G_CALLBACK (e_day_view_on_drag_begin), day_view);
1166 	g_signal_connect (
1167 		day_view->main_canvas, "drag_end",
1168 		G_CALLBACK (e_day_view_on_drag_end), day_view);
1169 	g_signal_connect (
1170 		day_view->main_canvas, "drag_data_get",
1171 		G_CALLBACK (e_day_view_on_drag_data_get), day_view);
1172 	g_signal_connect (
1173 		day_view->main_canvas, "drag_data_received",
1174 		G_CALLBACK (e_day_view_on_main_canvas_drag_data_received), day_view);
1175 
1176 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->main_canvas)->root);
1177 
1178 	day_view->main_canvas_item =
1179 		gnome_canvas_item_new (
1180 			canvas_group,
1181 			e_day_view_main_item_get_type (),
1182 			"EDayViewMainItem::day_view", day_view,
1183 			NULL);
1184 
1185 	day_view->drag_rect_item =
1186 		gnome_canvas_item_new (
1187 			canvas_group,
1188 			gnome_canvas_rect_get_type (),
1189 			"line_width", 1.0,
1190 			NULL);
1191 	gnome_canvas_item_hide (day_view->drag_rect_item);
1192 
1193 	day_view->drag_bar_item =
1194 		gnome_canvas_item_new (
1195 			canvas_group,
1196 			gnome_canvas_rect_get_type (),
1197 			"line_width", 1.0,
1198 			NULL);
1199 	gnome_canvas_item_hide (day_view->drag_bar_item);
1200 
1201 	day_view->drag_item =
1202 		gnome_canvas_item_new (
1203 			canvas_group,
1204 			e_text_get_type (),
1205 			"line_wrap", TRUE,
1206 			"clip", TRUE,
1207 			"editable", TRUE,
1208 			"fill_color_rgba", GNOME_CANVAS_COLOR (0, 0, 0),
1209 			NULL);
1210 	gnome_canvas_item_hide (day_view->drag_item);
1211 
1212 	/*
1213 	 * Times Canvas
1214 	 */
1215 	day_view->time_canvas = e_canvas_new ();
1216 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
1217 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
1218 	scrollable = GTK_SCROLLABLE (day_view->time_canvas);
1219 	gtk_scrollable_set_vadjustment (scrollable, adjustment);
1220 	gtk_table_attach (
1221 		GTK_TABLE (day_view), day_view->time_canvas,
1222 		0, 1, 1, 2,
1223 		GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1224 	gtk_widget_show (day_view->time_canvas);
1225 	g_signal_connect_after (
1226 		day_view->time_canvas, "scroll_event",
1227 		G_CALLBACK (e_day_view_on_time_canvas_scroll), day_view);
1228 
1229 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->time_canvas)->root);
1230 
1231 	day_view->time_canvas_item =
1232 		gnome_canvas_item_new (
1233 			canvas_group,
1234 			e_day_view_time_item_get_type (),
1235 			"EDayViewTimeItem::day_view", day_view,
1236 			NULL);
1237 
1238 	/*
1239 	 * Scrollbar.
1240 	 */
1241 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
1242 	adjustment = gtk_scrollable_get_hadjustment (scrollable);
1243 	day_view->mc_hscrollbar = gtk_hscrollbar_new (adjustment);
1244 	gtk_table_attach (GTK_TABLE (day_view), day_view->mc_hscrollbar, 1, 2, 2, 3, GTK_FILL, 0, 0, 0);
1245 	gtk_widget_show (day_view->mc_hscrollbar);
1246 
1247 	scrollable = GTK_SCROLLABLE (day_view->top_canvas);
1248 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
1249 	day_view->tc_vscrollbar = gtk_vscrollbar_new (adjustment);
1250 	gtk_table_attach (
1251 		GTK_TABLE (day_view), day_view->tc_vscrollbar,
1252 		2, 3, 0, 1, 0, GTK_FILL, 0, 0);
1253 	/* gtk_widget_show (day_view->tc_vscrollbar); */
1254 
1255 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
1256 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
1257 	day_view->vscrollbar = gtk_vscrollbar_new (adjustment);
1258 	gtk_table_attach (
1259 		GTK_TABLE (day_view), day_view->vscrollbar,
1260 		2, 3, 1, 2, 0, GTK_EXPAND | GTK_FILL, 0, 0);
1261 	gtk_widget_show (day_view->vscrollbar);
1262 
1263 	/* Create the cursors. */
1264 	day_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR);
1265 	day_view->move_cursor = gdk_cursor_new (GDK_FLEUR);
1266 	day_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
1267 	day_view->resize_height_cursor = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
1268 	day_view->last_cursor_set_in_top_canvas = NULL;
1269 	day_view->last_cursor_set_in_main_canvas = NULL;
1270 
1271 	/* Set up the drop sites. */
1272 	gtk_drag_dest_set (
1273 		day_view->top_canvas, GTK_DEST_DEFAULT_ALL,
1274 		target_table, G_N_ELEMENTS (target_table),
1275 		GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
1276 
1277 	e_drag_dest_add_calendar_targets (day_view->top_canvas);
1278 
1279 	gtk_drag_dest_set (
1280 		day_view->main_canvas, GTK_DEST_DEFAULT_ALL,
1281 		target_table, G_N_ELEMENTS (target_table),
1282 		GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
1283 
1284 	e_drag_dest_add_calendar_targets (day_view->main_canvas);
1285 
1286 	day_view->requires_update = FALSE;
1287 }
1288 
1289 static void
1290 init_model (EDayView *day_view,
1291             ECalModel *model)
1292 {
1293 	/* connect to ECalModel's signals */
1294 	g_signal_connect (
1295 		model, "time_range_changed",
1296 		G_CALLBACK (time_range_changed_cb), day_view);
1297 	g_signal_connect (
1298 		model, "model_row_changed",
1299 		G_CALLBACK (model_row_changed_cb), day_view);
1300 	g_signal_connect (
1301 		model, "model_cell_changed",
1302 		G_CALLBACK (model_cell_changed_cb), day_view);
1303 	g_signal_connect (
1304 		model, "model_rows_inserted",
1305 		G_CALLBACK (model_rows_inserted_cb), day_view);
1306 	g_signal_connect (
1307 		model, "comps_deleted",
1308 		G_CALLBACK (model_comps_deleted_cb), day_view);
1309 	g_signal_connect (
1310 		model, "timezone_changed",
1311 		G_CALLBACK (timezone_changed_cb), day_view);
1312 }
1313 
1314 /* Turn off the background of the canvas windows. This reduces flicker
1315  * considerably when scrolling. (Why isn't it in GnomeCanvas?). */
1316 static void
1317 e_day_view_on_canvas_realized (GtkWidget *widget,
1318                                EDayView *day_view)
1319 {
1320 	GdkWindow *window;
1321 
1322 	window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
1323 	gdk_window_set_background_pattern (window, NULL);
1324 }
1325 
1326 /**
1327  * e_day_view_new:
1328  * @Returns: a new #EDayView.
1329  *
1330  * Creates a new #EDayView.
1331  **/
1332 ECalendarView *
1333 e_day_view_new (ECalModel *model)
1334 {
1335 	ECalendarView *day_view;
1336 
1337 	g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1338 
1339 	day_view = g_object_new (E_TYPE_DAY_VIEW, "model", model, NULL);
1340 	init_model (E_DAY_VIEW (day_view), model);
1341 
1342 	return day_view;
1343 }
1344 
1345 static void
1346 e_day_view_dispose (GObject *object)
1347 {
1348 	EDayView *day_view;
1349 	gint day;
1350 
1351 	day_view = E_DAY_VIEW (object);
1352 
1353 	e_day_view_cancel_layout (day_view);
1354 
1355 	e_day_view_stop_auto_scroll (day_view);
1356 
1357 	if (day_view->large_font_desc) {
1358 		pango_font_description_free (day_view->large_font_desc);
1359 		day_view->large_font_desc = NULL;
1360 	}
1361 
1362 	if (day_view->small_font_desc) {
1363 		pango_font_description_free (day_view->small_font_desc);
1364 		day_view->small_font_desc = NULL;
1365 	}
1366 
1367 	if (day_view->normal_cursor) {
1368 		g_object_unref (day_view->normal_cursor);
1369 		day_view->normal_cursor = NULL;
1370 	}
1371 	if (day_view->move_cursor) {
1372 		g_object_unref (day_view->move_cursor);
1373 		day_view->move_cursor = NULL;
1374 	}
1375 	if (day_view->resize_width_cursor) {
1376 		g_object_unref (day_view->resize_width_cursor);
1377 		day_view->resize_width_cursor = NULL;
1378 	}
1379 	if (day_view->resize_height_cursor) {
1380 		g_object_unref (day_view->resize_height_cursor);
1381 		day_view->resize_height_cursor = NULL;
1382 	}
1383 
1384 	if (day_view->long_events) {
1385 		e_day_view_free_events (day_view);
1386 		g_array_free (day_view->long_events, TRUE);
1387 		day_view->long_events = NULL;
1388 	}
1389 
1390 	for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) {
1391 		if (day_view->events[day]) {
1392 			g_array_free (day_view->events[day], TRUE);
1393 			day_view->events[day] = NULL;
1394 		}
1395 	}
1396 
1397 	/* Chain up to parent's dispose() method. */
1398 	G_OBJECT_CLASS (e_day_view_parent_class)->dispose (object);
1399 }
1400 
1401 static void
1402 e_day_view_realize (GtkWidget *widget)
1403 {
1404 	EDayView *day_view;
1405 
1406 	if (GTK_WIDGET_CLASS (e_day_view_parent_class)->realize)
1407 		(*GTK_WIDGET_CLASS (e_day_view_parent_class)->realize)(widget);
1408 
1409 	day_view = E_DAY_VIEW (widget);
1410 
1411 	/* Allocate the colors. */
1412 
1413 	e_day_view_set_colors (day_view, widget);
1414 
1415 	/* Create the pixmaps. */
1416 	day_view->reminder_icon = e_icon_factory_get_icon ("stock_bell", GTK_ICON_SIZE_MENU);
1417 	day_view->recurrence_icon = e_icon_factory_get_icon ("view-refresh", GTK_ICON_SIZE_MENU);
1418 	day_view->timezone_icon = e_icon_factory_get_icon ("stock_timezone", GTK_ICON_SIZE_MENU);
1419 	day_view->meeting_icon = e_icon_factory_get_icon ("stock_people", GTK_ICON_SIZE_MENU);
1420 	day_view->attach_icon = e_icon_factory_get_icon ("mail-attachment", GTK_ICON_SIZE_MENU);
1421 
1422 	/* Set the canvas item colors. */
1423 	gnome_canvas_item_set (
1424 		day_view->drag_long_event_rect_item,
1425 		"fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
1426 		"outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1427 		NULL);
1428 
1429 	gnome_canvas_item_set (
1430 		day_view->drag_rect_item,
1431 		"fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
1432 		"outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1433 		NULL);
1434 
1435 	gnome_canvas_item_set (
1436 		day_view->drag_bar_item,
1437 		"fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR],
1438 		"outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1439 		NULL);
1440 }
1441 
1442 static void
1443 e_day_view_set_colors (EDayView *day_view,
1444                        GtkWidget *widget)
1445 {
1446 	GtkStyle *style;
1447 
1448 	style = gtk_widget_get_style (widget);
1449 
1450 	day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING] = style->base[GTK_STATE_NORMAL];
1451 	day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING] = style->bg[GTK_STATE_ACTIVE];
1452 	day_view->colors[E_DAY_VIEW_COLOR_BG_SELECTED] = style->base[GTK_STATE_SELECTED];
1453 	day_view->colors[E_DAY_VIEW_COLOR_BG_SELECTED_UNFOCUSSED] = style->bg[GTK_STATE_SELECTED];
1454 	day_view->colors[E_DAY_VIEW_COLOR_BG_GRID] = style->dark[GTK_STATE_NORMAL];
1455 	day_view->colors[E_DAY_VIEW_COLOR_BG_MULTIDAY_TODAY] = get_today_background (day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING]);
1456 	day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS] = style->dark[GTK_STATE_NORMAL];
1457 	day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS_SELECTED] = style->bg[GTK_STATE_SELECTED];
1458 	day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS_GRID] = style->light[GTK_STATE_NORMAL];
1459 	day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR] = style->base[GTK_STATE_SELECTED];
1460 	day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND] = style->base[GTK_STATE_NORMAL];
1461 	day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER] = style->dark[GTK_STATE_NORMAL];
1462 	day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BACKGROUND] = style->bg[GTK_STATE_ACTIVE];
1463 	day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BORDER] = style->dark[GTK_STATE_NORMAL];
1464 	day_view->colors[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE] = style->dark[GTK_STATE_PRELIGHT];
1465 }
1466 
1467 static void
1468 e_day_view_unrealize (GtkWidget *widget)
1469 {
1470 	EDayView *day_view;
1471 
1472 	day_view = E_DAY_VIEW (widget);
1473 
1474 	g_object_unref (day_view->reminder_icon);
1475 	day_view->reminder_icon = NULL;
1476 	g_object_unref (day_view->recurrence_icon);
1477 	day_view->recurrence_icon = NULL;
1478 	g_object_unref (day_view->timezone_icon);
1479 	day_view->timezone_icon = NULL;
1480 	g_object_unref (day_view->meeting_icon);
1481 	day_view->meeting_icon = NULL;
1482 	g_object_unref (day_view->attach_icon);
1483 	day_view->attach_icon = NULL;
1484 
1485 	if (GTK_WIDGET_CLASS (e_day_view_parent_class)->unrealize)
1486 		(*GTK_WIDGET_CLASS (e_day_view_parent_class)->unrealize)(widget);
1487 }
1488 
1489 static GdkColor
1490 e_day_view_get_text_color (EDayView *day_view,
1491                            EDayViewEvent *event,
1492                            GtkWidget *widget)
1493 {
1494 	GtkStyle *style;
1495 	GdkColor bg_color;
1496 	guint16 red, green, blue;
1497 	gdouble	cc = 65535.0;
1498 
1499 	red = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].red;
1500 	green = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].green;
1501 	blue = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].blue;
1502 
1503 	if (is_comp_data_valid (event) && gdk_color_parse (e_cal_model_get_color_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), event->comp_data),
1504 	     &bg_color)) {
1505 		red = bg_color.red;
1506 		green = bg_color.green;
1507 		blue = bg_color.blue;
1508 	}
1509 
1510 	style = gtk_widget_get_style (widget);
1511 
1512 	if ((red / cc > 0.7) || (green / cc > 0.7) || (blue / cc > 0.7))
1513 		return style->black;
1514 	else
1515 		return style->white;
1516 }
1517 
1518 static void
1519 e_day_view_update_top_scroll (EDayView *day_view,
1520                               gboolean scroll_to_top)
1521 {
1522 	GtkAllocation allocation;
1523 	gint top_rows, top_canvas_height;
1524 	gdouble old_x2, old_y2, new_x2, new_y2;
1525 
1526 	/* Set the height of the top canvas based on the row height and the
1527 	 * number of rows needed (min 1 + 1 for the dates + 1 space for DnD).*/
1528 	top_rows = MAX (1, day_view->rows_in_top_display);
1529 	top_canvas_height = (top_rows + 1) * day_view->top_row_height;
1530 	if (top_rows <= E_DAY_VIEW_MAX_ROWS_AT_TOP) {
1531 		gtk_widget_set_size_request (day_view->top_canvas, -1, top_canvas_height);
1532 		gtk_widget_hide (day_view->tc_vscrollbar);
1533 	} else {
1534 		gtk_widget_set_size_request (day_view->top_canvas, -1, (E_DAY_VIEW_MAX_ROWS_AT_TOP + 1) * day_view->top_row_height);
1535 		gtk_widget_show (day_view->tc_vscrollbar);
1536 	}
1537 
1538 	/* Set the scroll region of the top canvas */
1539 	gnome_canvas_get_scroll_region (
1540 		GNOME_CANVAS (day_view->top_canvas),
1541 		NULL, NULL, &old_x2, &old_y2);
1542 	gtk_widget_get_allocation (day_view->top_canvas, &allocation);
1543 	new_x2 = allocation.width - 1;
1544 	new_y2 = (MAX (1, day_view->rows_in_top_display) + 1) * day_view->top_row_height - 1;
1545 	if (old_x2 != new_x2 || old_y2 != new_y2) {
1546 		gnome_canvas_set_scroll_region (
1547 			GNOME_CANVAS (day_view->top_canvas),
1548 			0, 0, new_x2, new_y2);
1549 
1550 		if (scroll_to_top)
1551 			gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_canvas), 0, 0);
1552 	}
1553 	new_y2 = day_view->top_row_height - 1 - 2;
1554 	gnome_canvas_get_scroll_region (GNOME_CANVAS (day_view->top_dates_canvas), NULL, NULL, &old_x2, &old_y2);
1555 
1556 	if (old_x2 != new_x2 || old_y2 != new_y2) {
1557 		gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->top_dates_canvas), 0, 0, new_x2, new_y2);
1558 		gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_dates_canvas), 0, 0);
1559 	}
1560 }
1561 
1562 static void
1563 e_day_view_style_set (GtkWidget *widget,
1564                       GtkStyle *previous_style)
1565 {
1566 	EDayView *day_view;
1567 	gint hour;
1568 	gint minute, max_minute_width, i;
1569 	gint month, day, width;
1570 	gint longest_month_width, longest_abbreviated_month_width;
1571 	gint longest_weekday_width, longest_abbreviated_weekday_width;
1572 	gchar buffer[128];
1573 	const gchar *name;
1574 	gint times_width;
1575 	PangoFontDescription *font_desc;
1576 	PangoContext *pango_context;
1577 	PangoFontMetrics *font_metrics;
1578 	PangoLayout *layout;
1579 	gint week_day, event_num;
1580 	GtkAdjustment *adjustment;
1581 	EDayViewEvent *event;
1582 	GdkColor color;
1583 
1584 	if (GTK_WIDGET_CLASS (e_day_view_parent_class)->style_set)
1585 		(*GTK_WIDGET_CLASS (e_day_view_parent_class)->style_set)(widget, previous_style);
1586 
1587 	day_view = E_DAY_VIEW (widget);
1588 	e_day_view_set_colors (day_view, widget);
1589 
1590 	for (week_day = 0; week_day < E_DAY_VIEW_MAX_DAYS; week_day++) {
1591 		for (event_num = 0; event_num < day_view->events[week_day]->len; event_num++) {
1592 			event = &g_array_index (day_view->events[week_day], EDayViewEvent, event_num);
1593 			if (event->canvas_item) {
1594 				color = e_day_view_get_text_color (day_view, event, widget);
1595 				gnome_canvas_item_set (
1596 					event->canvas_item,
1597 					"fill_color_gdk", &color,
1598 					NULL);
1599 			}
1600 		}
1601 	}
1602 	for (event_num = 0; event_num < day_view->long_events->len; event_num++) {
1603 		event = &g_array_index (day_view->long_events, EDayViewEvent, event_num);
1604 		if (event->canvas_item) {
1605 			color = e_day_view_get_text_color (day_view, event, widget);
1606 			gnome_canvas_item_set (
1607 				event->canvas_item,
1608 				"fill_color_gdk", &color,
1609 				NULL);
1610 		}
1611 	}
1612 
1613 	/* Set up Pango prerequisites */
1614 	font_desc = gtk_widget_get_style (widget)->font_desc;
1615 	pango_context = gtk_widget_get_pango_context (widget);
1616 	font_metrics = pango_context_get_metrics (
1617 		pango_context, font_desc,
1618 		pango_context_get_language (pango_context));
1619 	layout = pango_layout_new (pango_context);
1620 
1621 	/* Create the large font. */
1622 	if (day_view->large_font_desc != NULL)
1623 		pango_font_description_free (day_view->large_font_desc);
1624 
1625 	day_view->large_font_desc = pango_font_description_copy (font_desc);
1626 	pango_font_description_set_size (
1627 		day_view->large_font_desc,
1628 		E_DAY_VIEW_LARGE_FONT_PTSIZE * PANGO_SCALE);
1629 
1630 	/* Create the small fonts. */
1631 	if (day_view->small_font_desc != NULL)
1632 		pango_font_description_free (day_view->small_font_desc);
1633 
1634 	day_view->small_font_desc = pango_font_description_copy (font_desc);
1635 	pango_font_description_set_size (
1636 		day_view->small_font_desc,
1637 		E_DAY_VIEW_SMALL_FONT_PTSIZE * PANGO_SCALE);
1638 
1639 	/* Recalculate the height of each row based on the font size. */
1640 	day_view->row_height =
1641 		PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1642 		PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
1643 		E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD * 2 + 2 /* FIXME */;
1644 	day_view->row_height = MAX (
1645 		day_view->row_height,
1646 		E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD + 2);
1647 
1648 	adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas));
1649 	gtk_adjustment_set_step_increment (adjustment, day_view->row_height);
1650 
1651 	day_view->top_row_height =
1652 		PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1653 		PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
1654 		E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT * 2 + E_DAY_VIEW_LONG_EVENT_Y_PAD * 2 +
1655 		E_DAY_VIEW_TOP_CANVAS_Y_GAP;
1656 	day_view->top_row_height =
1657 		MAX (
1658 			day_view->top_row_height,
1659 		E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD + 2 +
1660 		E_DAY_VIEW_TOP_CANVAS_Y_GAP);
1661 
1662 	adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->top_canvas));
1663 	gtk_adjustment_set_step_increment (adjustment, day_view->top_row_height);
1664 	gtk_widget_set_size_request (day_view->top_dates_canvas, -1, day_view->top_row_height - 2);
1665 
1666 	e_day_view_update_top_scroll (day_view, TRUE);
1667 
1668 	/* Find the longest full & abbreviated month names. */
1669 	longest_month_width = 0;
1670 	longest_abbreviated_month_width = 0;
1671 	for (month = 0; month < 12; month++) {
1672 		name = e_get_month_name (month + 1, FALSE);
1673 		pango_layout_set_text (layout, name, -1);
1674 		pango_layout_get_pixel_size (layout, &width, NULL);
1675 
1676 		if (width > longest_month_width) {
1677 			longest_month_width = width;
1678 			day_view->longest_month_name = month;
1679 		}
1680 
1681 		name = e_get_month_name (month + 1, TRUE);
1682 		pango_layout_set_text (layout, name, -1);
1683 		pango_layout_get_pixel_size (layout, &width, NULL);
1684 
1685 		if (width > longest_abbreviated_month_width) {
1686 			longest_abbreviated_month_width = width;
1687 			day_view->longest_abbreviated_month_name = month;
1688 		}
1689 	}
1690 
1691 	/* Find the longest full & abbreviated weekday names. */
1692 	longest_weekday_width = 0;
1693 	longest_abbreviated_weekday_width = 0;
1694 	for (day = 0; day < 7; day++) {
1695 		name = e_get_weekday_name (day + 1, FALSE);
1696 		pango_layout_set_text (layout, name, -1);
1697 		pango_layout_get_pixel_size (layout, &width, NULL);
1698 
1699 		if (width > longest_weekday_width) {
1700 			longest_weekday_width = width;
1701 			day_view->longest_weekday_name = day;
1702 		}
1703 
1704 		name = e_get_weekday_name (day + 1, TRUE);
1705 		pango_layout_set_text (layout, name, -1);
1706 		pango_layout_get_pixel_size (layout, &width, NULL);
1707 
1708 		if (width > longest_abbreviated_weekday_width) {
1709 			longest_abbreviated_weekday_width = width;
1710 			day_view->longest_abbreviated_weekday_name = day;
1711 		}
1712 	}
1713 
1714 	/* Calculate the widths of all the time strings necessary. */
1715 	day_view->max_small_hour_width = 0;
1716 	for (hour = 0; hour < 24; hour++) {
1717 		g_snprintf (buffer, sizeof (buffer), "%02i", hour);
1718 		pango_layout_set_text (layout, buffer, -1);
1719 		pango_layout_get_pixel_size (layout, &day_view->small_hour_widths[hour], NULL);
1720 
1721 		day_view->max_small_hour_width = MAX (day_view->max_small_hour_width, day_view->small_hour_widths[hour]);
1722 	}
1723 
1724 	max_minute_width = 0;
1725 	for (minute = 0, i = 0; minute < 60; minute += 5, i++) {
1726 		gint minute_width;
1727 
1728 		g_snprintf (buffer, sizeof (buffer), "%02i", minute);
1729 		pango_layout_set_text (layout, buffer, -1);
1730 		pango_layout_get_pixel_size (layout, &minute_width, NULL);
1731 
1732 		max_minute_width = MAX (max_minute_width, minute_width);
1733 	}
1734 	day_view->max_minute_width = max_minute_width;
1735 
1736 	pango_layout_set_text (layout, ":", 1);
1737 	pango_layout_get_pixel_size (layout, &day_view->colon_width, NULL);
1738 	pango_layout_set_text (layout, "0", 1);
1739 	pango_layout_get_pixel_size (layout, &day_view->digit_width, NULL);
1740 
1741 	pango_layout_set_text (layout, day_view->am_string, -1);
1742 	pango_layout_get_pixel_size (layout, &day_view->am_string_width, NULL);
1743 	pango_layout_set_text (layout, day_view->pm_string, -1);
1744 	pango_layout_get_pixel_size (layout, &day_view->pm_string_width, NULL);
1745 
1746 	/* Calculate the width of the time column. */
1747 	times_width = e_day_view_time_item_get_column_width (E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item));
1748 	gtk_widget_set_size_request (day_view->time_canvas, times_width, -1);
1749 
1750 	g_object_unref (layout);
1751 	pango_font_metrics_unref (font_metrics);
1752 }
1753 
1754 static void
1755 e_day_view_recalc_main_canvas_size (EDayView *day_view)
1756 {
1757 	ECalModel *model;
1758 	gint work_day_start_hour;
1759 	gint work_day_start_minute;
1760 	gint day, scroll_y;
1761 	gboolean need_reshape;
1762 
1763 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
1764 	work_day_start_hour = e_cal_model_get_work_day_start_hour (model);
1765 	work_day_start_minute = e_cal_model_get_work_day_start_minute (model);
1766 
1767 	/* Set the scroll region of the top canvas */
1768 	e_day_view_update_top_scroll (day_view, TRUE);
1769 
1770 	need_reshape = e_day_view_update_scroll_regions (day_view);
1771 
1772 	e_day_view_recalc_cell_sizes (day_view);
1773 
1774 	/* Scroll to the start of the working day, if this is the initial
1775 	 * allocation. */
1776 	if (day_view->scroll_to_work_day) {
1777 		scroll_y = e_day_view_convert_time_to_position (
1778 			day_view, work_day_start_hour, work_day_start_minute);
1779 		gnome_canvas_scroll_to (
1780 			GNOME_CANVAS (day_view->main_canvas), 0, scroll_y);
1781 		day_view->scroll_to_work_day = FALSE;
1782 	}
1783 
1784 	/* Flag that we need to reshape the events. Note that changes in height
1785 	 * don't matter, since the rows are always the same height. */
1786 	if (need_reshape) {
1787 		day_view->long_events_need_reshape = TRUE;
1788 		for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
1789 			day_view->need_reshape[day] = TRUE;
1790 
1791 		e_day_view_check_layout (day_view);
1792 	}
1793 }
1794 
1795 /* This recalculates the sizes of each column. */
1796 static void
1797 e_day_view_size_allocate (GtkWidget *widget,
1798                           GtkAllocation *allocation)
1799 {
1800 	(*GTK_WIDGET_CLASS (e_day_view_parent_class)->size_allocate) (widget, allocation);
1801 
1802 	e_day_view_recalc_main_canvas_size (E_DAY_VIEW (widget));
1803 }
1804 
1805 static void
1806 e_day_view_recalc_cell_sizes (EDayView *day_view)
1807 {
1808 	/* An array of dates, one for each month in the year 2000. They must
1809 	 * all be Sundays. */
1810 	static const gint days[12] = { 23, 20, 19, 23, 21, 18,
1811 				      23, 20, 17, 22, 19, 24 };
1812 	gfloat width, offset;
1813 	gint day, max_width;
1814 	struct tm date_tm;
1815 	gchar buffer[128];
1816 	GtkAllocation allocation;
1817 	PangoContext *pango_context;
1818 	PangoLayout *layout;
1819 	gint pango_width;
1820 
1821 	g_return_if_fail (gtk_widget_get_style (GTK_WIDGET (day_view)) != NULL);
1822 
1823 	gtk_widget_get_allocation (day_view->main_canvas, &allocation);
1824 
1825 	/* Set up Pango prerequisites */
1826 	pango_context = gtk_widget_get_pango_context (GTK_WIDGET (day_view));
1827 	layout = pango_layout_new (pango_context);
1828 
1829 	/* Calculate the column sizes, using floating point so that pixels
1830 	 * get divided evenly. Note that we use one more element than the
1831 	 * number of columns, to make it easy to get the column widths. */
1832 	width = allocation.width;
1833 	if (day_view->days_shown == 1)
1834 		width = MAX (width, day_view->max_cols * (E_DAY_VIEW_MIN_DAY_COL_WIDTH + E_DAY_VIEW_GAP_WIDTH) - E_DAY_VIEW_MIN_DAY_COL_WIDTH - 1);
1835 	width /= day_view->days_shown;
1836 	offset = 0;
1837 	for (day = 0; day <= day_view->days_shown; day++) {
1838 		day_view->day_offsets[day] = floor (offset + 0.5);
1839 		offset += width;
1840 	}
1841 
1842 	/* Calculate the days widths based on the offsets. */
1843 	for (day = 0; day < day_view->days_shown; day++) {
1844 		day_view->day_widths[day] = day_view->day_offsets[day + 1] - day_view->day_offsets[day];
1845 	}
1846 
1847 	/* Determine which date format to use, based on the column widths.
1848 	 * We want to check the widths using the longest full or abbreviated
1849 	 * month name and the longest full or abbreviated weekday name, as
1850 	 * appropriate. */
1851 	max_width = day_view->day_widths[0];
1852 
1853 	memset (&date_tm, 0, sizeof (date_tm));
1854 	date_tm.tm_year = 100;
1855 
1856 	/* Try "Thursday 21 January". */
1857 	date_tm.tm_mon = day_view->longest_month_name;
1858 	date_tm.tm_mday = days[date_tm.tm_mon]
1859 		+ day_view->longest_weekday_name;
1860 	date_tm.tm_wday = day_view->longest_weekday_name;
1861 	date_tm.tm_isdst = -1;
1862 	/* strftime format %A = full weekday name, %d = day of month,
1863 	 * %B = full month name. Don't use any other specifiers. */
1864 	e_utf8_strftime (buffer, sizeof (buffer), _("%A %d %B"), &date_tm);
1865 	pango_layout_set_text (layout, buffer, -1);
1866 	pango_layout_get_pixel_size (layout, &pango_width, NULL);
1867 
1868 	if (pango_width < max_width) {
1869 		day_view->date_format = E_DAY_VIEW_DATE_FULL;
1870 		goto exit;
1871 	}
1872 
1873 	/* Try "Thu 21 Jan". */
1874 	date_tm.tm_mon = day_view->longest_abbreviated_month_name;
1875 	date_tm.tm_mday = days[date_tm.tm_mon]
1876 		+ day_view->longest_abbreviated_weekday_name;
1877 	date_tm.tm_wday = day_view->longest_abbreviated_weekday_name;
1878 	date_tm.tm_isdst = -1;
1879 	/* strftime format %a = abbreviated weekday name, %d = day of month,
1880 	 * %b = abbreviated month name. Don't use any other specifiers. */
1881 	e_utf8_strftime (buffer, sizeof (buffer), _("%a %d %b"), &date_tm);
1882 	pango_layout_set_text (layout, buffer, -1);
1883 	pango_layout_get_pixel_size (layout, &pango_width, NULL);
1884 
1885 	if (pango_width < max_width) {
1886 		day_view->date_format = E_DAY_VIEW_DATE_ABBREVIATED;
1887 		goto exit;
1888 	}
1889 
1890 	/* Try "23 Jan". */
1891 	date_tm.tm_mon = day_view->longest_abbreviated_month_name;
1892 	date_tm.tm_mday = 23;
1893 	date_tm.tm_wday = 0;
1894 	date_tm.tm_isdst = -1;
1895 	/* strftime format %d = day of month, %b = abbreviated month name.
1896 	 * Don't use any other specifiers. */
1897 	e_utf8_strftime (buffer, sizeof (buffer), _("%d %b"), &date_tm);
1898 	pango_layout_set_text (layout, buffer, -1);
1899 	pango_layout_get_pixel_size (layout, &pango_width, NULL);
1900 
1901 	if (pango_width < max_width)
1902 		day_view->date_format = E_DAY_VIEW_DATE_NO_WEEKDAY;
1903 	else
1904 		day_view->date_format = E_DAY_VIEW_DATE_SHORT;
1905 
1906 exit:
1907 	g_object_unref (layout);
1908 }
1909 
1910 static gint
1911 e_day_view_focus_in (GtkWidget *widget,
1912                      GdkEventFocus *event)
1913 {
1914 	EDayView *day_view;
1915 
1916 	g_return_val_if_fail (widget != NULL, FALSE);
1917 	g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
1918 	g_return_val_if_fail (event != NULL, FALSE);
1919 
1920 	day_view = E_DAY_VIEW (widget);
1921 
1922 	/* XXX Can't access flags directly anymore, but is it really needed?
1923 	 *     If so, could we call gtk_widget_send_focus_change() instead? */
1924 #if 0
1925 	GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1926 #endif
1927 
1928 	if (E_CALENDAR_VIEW (day_view)->in_focus && day_view->requires_update) {
1929 		time_t my_start = 0, my_end = 0, model_start = 0, model_end = 0;
1930 
1931 		day_view->requires_update = FALSE;
1932 
1933 		e_cal_model_get_time_range (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), &model_start, &model_end);
1934 
1935 		if (e_calendar_view_get_visible_time_range (E_CALENDAR_VIEW (day_view), &my_start, &my_end) &&
1936 		    model_start == my_start && model_end == my_end) {
1937 			/* update only when the same time range is set in a view and in a model;
1938 			 * otherwise time range change invokes also query update */
1939 			e_day_view_recalc_day_starts (day_view, day_view->lower);
1940 			e_day_view_update_query (day_view);
1941 		}
1942 	}
1943 
1944 	gtk_widget_queue_draw (day_view->top_canvas);
1945 	gtk_widget_queue_draw (day_view->main_canvas);
1946 
1947 	return FALSE;
1948 }
1949 
1950 static gint
1951 e_day_view_focus_out (GtkWidget *widget,
1952                       GdkEventFocus *event)
1953 {
1954 	EDayView *day_view;
1955 
1956 	g_return_val_if_fail (widget != NULL, FALSE);
1957 	g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
1958 	g_return_val_if_fail (event != NULL, FALSE);
1959 
1960 	day_view = E_DAY_VIEW (widget);
1961 
1962 	/* XXX Can't access flags directly anymore, but is it really needed?
1963 	 *     If so, could we call gtk_widget_send_focus_change() instead? */
1964 #if 0
1965 	GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1966 #endif
1967 
1968 	gtk_widget_queue_draw (day_view->top_canvas);
1969 	gtk_widget_queue_draw (day_view->main_canvas);
1970 
1971 	return FALSE;
1972 }
1973 
1974 /* This calls a given function for each event instance (in both views).
1975  * If the callback returns FALSE the iteration is stopped.
1976  * Note that it is safe for the callback to remove the event (since we
1977  * step backwards through the arrays). */
1978 static void
1979 e_day_view_foreach_event (EDayView *day_view,
1980                           EDayViewForeachEventCallback callback,
1981                           gpointer data)
1982 {
1983 	gint day, event_num;
1984 
1985 	for (day = 0; day < day_view->days_shown; day++) {
1986 		for (event_num = day_view->events[day]->len - 1;
1987 		     event_num >= 0;
1988 		     event_num--) {
1989 			if (!(*callback) (day_view, day, event_num, data))
1990 				return;
1991 		}
1992 	}
1993 
1994 	for (event_num = day_view->long_events->len - 1;
1995 	     event_num >= 0;
1996 	     event_num--) {
1997 		if (!(*callback) (day_view, E_DAY_VIEW_LONG_EVENT, event_num,
1998 				  data))
1999 			return;
2000 	}
2001 }
2002 
2003 /* This calls a given function for each event instance that matches the given
2004  * uid. If the callback returns FALSE the iteration is stopped.
2005  * Note that it is safe for the callback to remove the event (since we
2006  * step backwards through the arrays). */
2007 static void
2008 e_day_view_foreach_event_with_uid (EDayView *day_view,
2009                                    const gchar *uid,
2010                                    EDayViewForeachEventCallback callback,
2011                                    gpointer data)
2012 {
2013 	EDayViewEvent *event;
2014 	gint day, event_num;
2015 	const gchar *u;
2016 
2017 	for (day = 0; day < day_view->days_shown; day++) {
2018 		for (event_num = day_view->events[day]->len - 1;
2019 		     event_num >= 0;
2020 		     event_num--) {
2021 			event = &g_array_index (day_view->events[day],
2022 						EDayViewEvent, event_num);
2023 
2024 			if (!is_comp_data_valid (event))
2025 				continue;
2026 
2027 			u = icalcomponent_get_uid (event->comp_data->icalcomp);
2028 			if (uid && !strcmp (uid, u)) {
2029 				if (!(*callback) (day_view, day, event_num, data))
2030 					return;
2031 			}
2032 		}
2033 	}
2034 
2035 	for (event_num = day_view->long_events->len - 1;
2036 	     event_num >= 0;
2037 	     event_num--) {
2038 		event = &g_array_index (day_view->long_events,
2039 					EDayViewEvent, event_num);
2040 
2041 		if (!is_comp_data_valid (event))
2042 			continue;
2043 
2044 		u = icalcomponent_get_uid (event->comp_data->icalcomp);
2045 		if (u && !strcmp (uid, u)) {
Null pointer passed as an argument to a 'nonnull' parameter
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Null pointer passed as an argument to a 'nonnull' parameter
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2046 if (!(*callback) (day_view, E_DAY_VIEW_LONG_EVENT, event_num, data)) 2047 return; 2048 } 2049 } 2050 } 2051 2052 static gboolean 2053 e_day_view_remove_event_cb (EDayView *day_view, 2054 gint day, 2055 gint event_num, 2056 gpointer data) 2057 { 2058 EDayViewEvent *event; 2059 2060 #if 0 2061 g_print ( 2062 "In e_day_view_remove_event_cb day:%i event_num:%i\n", 2063 day, event_num); 2064 #endif 2065 2066 if (day == E_DAY_VIEW_LONG_EVENT) { 2067 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 2068 return TRUE; 2069 2070 event = &g_array_index (day_view->long_events, 2071 EDayViewEvent, event_num); 2072 } else { 2073 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 2074 return TRUE; 2075 2076 event = &g_array_index (day_view->events[day], 2077 EDayViewEvent, event_num); 2078 } 2079 2080 if (!event) 2081 return TRUE; 2082 2083 /* If we were editing this event, set editing_event_day to -1 so 2084 * on_editing_stopped doesn't try to update the event. */ 2085 if (day_view->editing_event_num == event_num && day_view->editing_event_day == day) { 2086 day_view->editing_event_num = -1; 2087 day_view->editing_event_day = -1; 2088 } 2089 2090 if (day_view->popup_event_num == event_num && day_view->popup_event_day == day) { 2091 day_view->popup_event_num = -1; 2092 day_view->popup_event_day = -1; 2093 } 2094 2095 if (event->canvas_item) 2096 g_object_run_dispose (G_OBJECT (event->canvas_item)); 2097 2098 if (is_comp_data_valid (event)) 2099 g_object_unref (event->comp_data); 2100 event->comp_data = NULL; 2101 2102 if (day == E_DAY_VIEW_LONG_EVENT) { 2103 g_array_remove_index (day_view->long_events, event_num); 2104 day_view->long_events_need_layout = TRUE; 2105 gtk_widget_grab_focus (GTK_WIDGET (day_view->top_canvas)); 2106 } else { 2107 g_array_remove_index (day_view->events[day], event_num); 2108 day_view->need_layout[day] = TRUE; 2109 gtk_widget_grab_focus (GTK_WIDGET (day_view->main_canvas)); 2110 } 2111 return TRUE; 2112 } 2113 2114 /* Checks if the users participation status is NEEDS-ACTION and shows the summary as bold text */ 2115 static void 2116 set_text_as_bold (EDayViewEvent *event, 2117 ESourceRegistry *registry) 2118 { 2119 ECalComponent *comp; 2120 GSList *attendees = NULL, *l; 2121 gchar *address; 2122 ECalComponentAttendee *at = NULL; 2123 2124 if (!is_comp_data_valid (event)) 2125 return; 2126 2127 comp = e_cal_component_new (); 2128 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 2129 address = itip_get_comp_attendee ( 2130 registry, comp, event->comp_data->client); 2131 e_cal_component_get_attendee_list (comp, &attendees); 2132 for (l = attendees; l; l = l->next) { 2133 ECalComponentAttendee *attendee = l->data; 2134 2135 if ((g_str_equal (itip_strip_mailto (attendee->value), address)) 2136 || (attendee->sentby && g_str_equal (itip_strip_mailto (attendee->sentby), address))) { 2137 at = attendee; 2138 break; 2139 } 2140 } 2141 2142 /* The attendee has not yet accepted the meeting, display the summary as bolded. 2143 * If the attendee is not present, it might have come through a mailing list. 2144 * In that case, we never show the meeting as bold even if it is unaccepted. */ 2145 if (at && (at->status == ICAL_PARTSTAT_NEEDSACTION)) 2146 gnome_canvas_item_set (event->canvas_item, "bold", TRUE, NULL); 2147 2148 e_cal_component_free_attendee_list (attendees); 2149 g_free (address); 2150 g_object_unref (comp); 2151 } 2152 2153 /* This updates the text shown for an event. If the event start or end do not 2154 * lie on a row boundary, the time is displayed before the summary. */ 2155 static void 2156 e_day_view_update_event_label (EDayView *day_view, 2157 gint day, 2158 gint event_num) 2159 { 2160 EDayViewEvent *event; 2161 ECalendarView *cal_view; 2162 ESourceRegistry *registry; 2163 ECalModel *model; 2164 gboolean free_text = FALSE, editing_event = FALSE, short_event = FALSE; 2165 const gchar *summary; 2166 gchar *text; 2167 gint time_divisions; 2168 gint interval; 2169 2170 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 2171 return; 2172 2173 event = &g_array_index (day_view->events[day], EDayViewEvent, event_num); 2174 2175 /* If the event isn't visible just return. */ 2176 if (!event->canvas_item || !is_comp_data_valid (event)) 2177 return; 2178 2179 summary = icalcomponent_get_summary (event->comp_data->icalcomp); 2180 text = summary ? (gchar *) summary : (gchar *) ""; 2181 2182 if (day_view->editing_event_day == day 2183 && day_view->editing_event_num == event_num) 2184 editing_event = TRUE; 2185 2186 interval = event->end_minute - event->start_minute; 2187 2188 cal_view = E_CALENDAR_VIEW (day_view); 2189 model = e_calendar_view_get_model (cal_view); 2190 time_divisions = e_calendar_view_get_time_divisions (cal_view); 2191 2192 registry = e_cal_model_get_registry (model); 2193 2194 if ((interval / time_divisions) >= 2) 2195 short_event = FALSE; 2196 else if ((interval % time_divisions) == 0) { 2197 if (((event->end_minute % time_divisions) == 0) || 2198 ((event->start_minute % time_divisions) == 0)) { 2199 short_event = TRUE; 2200 } 2201 } else 2202 short_event = FALSE; 2203 2204 if (!editing_event) { 2205 if (!short_event) { 2206 const gchar *location = icalcomponent_get_location (event->comp_data->icalcomp); 2207 2208 if (location && *location) 2209 text = g_strdup_printf (" \n%s%c(%s)", text, day_view->days_shown == 1 ? ' ' : '\n', location); 2210 else 2211 text = g_strdup_printf (" \n%s", text); 2212 2213 free_text = TRUE; 2214 } 2215 } 2216 2217 gnome_canvas_item_set ( 2218 event->canvas_item, 2219 "text", text, 2220 NULL); 2221 2222 if (e_client_check_capability (E_CLIENT (event->comp_data->client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING) 2223 && e_cal_util_component_has_attendee (event->comp_data->icalcomp)) 2224 set_text_as_bold (event, registry); 2225 2226 if (free_text) 2227 g_free (text); 2228 } 2229 2230 static void 2231 e_day_view_update_long_event_label (EDayView *day_view, 2232 gint event_num) 2233 { 2234 EDayViewEvent *event; 2235 ECalendarView *cal_view; 2236 ECalModel *model; 2237 ESourceRegistry *registry; 2238 const gchar *summary; 2239 gboolean free_text = FALSE; 2240 2241 cal_view = E_CALENDAR_VIEW (day_view); 2242 model = e_calendar_view_get_model (cal_view); 2243 2244 registry = e_cal_model_get_registry (model); 2245 2246 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 2247 return; 2248 2249 event = &g_array_index (day_view->long_events, EDayViewEvent, 2250 event_num); 2251 2252 /* If the event isn't visible just return. */ 2253 if (!event->canvas_item || !is_comp_data_valid (event)) 2254 return; 2255 2256 summary = e_calendar_view_get_icalcomponent_summary (event->comp_data->client, event->comp_data->icalcomp, &free_text); 2257 2258 gnome_canvas_item_set ( 2259 event->canvas_item, 2260 "text", summary ? summary : "", 2261 NULL); 2262 2263 if (free_text) 2264 g_free ((gchar *) summary); 2265 2266 if (e_client_check_capability (E_CLIENT (event->comp_data->client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING) 2267 && e_cal_util_component_has_attendee (event->comp_data->icalcomp)) 2268 set_text_as_bold (event, registry); 2269 } 2270 2271 /* Finds the day and index of the event with the given canvas item. 2272 * If is is a long event, -1 is returned as the day. 2273 * Returns TRUE if the event was found. */ 2274 gboolean 2275 e_day_view_find_event_from_item (EDayView *day_view, 2276 GnomeCanvasItem *item, 2277 gint *day_return, 2278 gint *event_num_return) 2279 { 2280 EDayViewEvent *event; 2281 gint day, event_num; 2282 2283 for (day = 0; day < day_view->days_shown; day++) { 2284 for (event_num = 0; event_num < day_view->events[day]->len; 2285 event_num++) { 2286 event = &g_array_index (day_view->events[day], 2287 EDayViewEvent, event_num); 2288 if (event->canvas_item == item) { 2289 *day_return = day; 2290 *event_num_return = event_num; 2291 return TRUE; 2292 } 2293 } 2294 } 2295 2296 for (event_num = 0; event_num < day_view->long_events->len; 2297 event_num++) { 2298 event = &g_array_index (day_view->long_events, 2299 EDayViewEvent, event_num); 2300 if (event->canvas_item == item) { 2301 *day_return = E_DAY_VIEW_LONG_EVENT; 2302 *event_num_return = event_num; 2303 return TRUE; 2304 } 2305 } 2306 2307 return FALSE; 2308 } 2309 2310 /* Finds the day and index of the event with the given uid. 2311 * If is is a long event, E_DAY_VIEW_LONG_EVENT is returned as the day. 2312 * Returns TRUE if an event with the uid was found. 2313 * Note that for recurring events there may be several EDayViewEvents, one 2314 * for each instance, all with the same iCalObject and uid. So only use this 2315 * function if you know the event doesn't recur or you are just checking to 2316 * see if any events with the uid exist. */ 2317 static gboolean 2318 e_day_view_find_event_from_uid (EDayView *day_view, 2319 ECalClient *client, 2320 const gchar *uid, 2321 const gchar *rid, 2322 gint *day_return, 2323 gint *event_num_return) 2324 { 2325 EDayViewEvent *event; 2326 gint day, event_num; 2327 const gchar *u; 2328 gchar *r = NULL; 2329 2330 if (!uid) 2331 return FALSE; 2332 2333 for (day = 0; day < day_view->days_shown; day++) { 2334 for (event_num = 0; event_num < day_view->events[day]->len; 2335 event_num++) { 2336 event = &g_array_index (day_view->events[day], 2337 EDayViewEvent, event_num); 2338 2339 if (!is_comp_data_valid (event)) 2340 continue; 2341 2342 if (event->comp_data->client != client) 2343 continue; 2344 2345 u = icalcomponent_get_uid (event->comp_data->icalcomp); 2346 if (u && !strcmp (uid, u)) { 2347 if (rid && *rid) { 2348 r = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (event->comp_data->icalcomp)); 2349 if (!r || !*r) 2350 continue; 2351 if (strcmp (rid, r) != 0) { 2352 g_free (r); 2353 continue; 2354 } 2355 g_free (r); 2356 } 2357 2358 *day_return = day; 2359 *event_num_return = event_num; 2360 return TRUE; 2361 } 2362 } 2363 } 2364 2365 for (event_num = 0; event_num < day_view->long_events->len; 2366 event_num++) { 2367 event = &g_array_index (day_view->long_events, 2368 EDayViewEvent, event_num); 2369 2370 if (!is_comp_data_valid (event)) 2371 continue; 2372 2373 if (event->comp_data->client != client) 2374 continue; 2375 2376 u = icalcomponent_get_uid (event->comp_data->icalcomp); 2377 if (u && !strcmp (uid, u)) { 2378 *day_return = E_DAY_VIEW_LONG_EVENT; 2379 *event_num_return = event_num; 2380 return TRUE; 2381 } 2382 } 2383 2384 return FALSE; 2385 } 2386 2387 static void 2388 e_day_view_set_selected_time_range_in_top_visible (EDayView *day_view, 2389 time_t start_time, 2390 time_t end_time) 2391 { 2392 gint start_row, start_col, end_row, end_col; 2393 gboolean need_redraw = FALSE, start_in_grid, end_in_grid; 2394 2395 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2396 2397 /* Set the selection. */ 2398 start_in_grid = e_day_view_convert_time_to_grid_position ( 2399 day_view, 2400 start_time, 2401 &start_col, 2402 &start_row); 2403 end_in_grid = e_day_view_convert_time_to_grid_position ( 2404 day_view, 2405 end_time - 60, 2406 &end_col, 2407 &end_row); 2408 2409 if (!start_in_grid) 2410 start_col = 0; 2411 if (!end_in_grid) 2412 end_col = day_view->days_shown - 1; 2413 2414 if (start_row != day_view->selection_start_row 2415 || start_col != day_view->selection_start_day) { 2416 need_redraw = TRUE; 2417 day_view->selection_in_top_canvas = TRUE; 2418 day_view->selection_start_row = -1; 2419 day_view->selection_start_day = start_col; 2420 } 2421 2422 if (end_row != day_view->selection_end_row 2423 || end_col != day_view->selection_end_day) { 2424 need_redraw = TRUE; 2425 day_view->selection_in_top_canvas = TRUE; 2426 day_view->selection_end_row = -1; 2427 day_view->selection_end_day = end_col; 2428 } 2429 2430 if (need_redraw) { 2431 gtk_widget_queue_draw (day_view->top_canvas); 2432 gtk_widget_queue_draw (day_view->top_dates_canvas); 2433 gtk_widget_queue_draw (day_view->main_canvas); 2434 } 2435 } 2436 2437 static void 2438 e_day_view_set_selected_time_range_visible (EDayView *day_view, 2439 time_t start_time, 2440 time_t end_time) 2441 { 2442 ECalModel *model; 2443 gint work_day_start_hour; 2444 gint work_day_start_minute; 2445 gint start_row, start_col, end_row, end_col; 2446 gboolean need_redraw = FALSE, start_in_grid, end_in_grid; 2447 2448 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2449 2450 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 2451 work_day_start_hour = e_cal_model_get_work_day_start_hour (model); 2452 work_day_start_minute = e_cal_model_get_work_day_start_minute (model); 2453 2454 /* Set the selection. */ 2455 start_in_grid = e_day_view_convert_time_to_grid_position ( 2456 day_view, 2457 start_time, 2458 &start_col, 2459 &start_row); 2460 end_in_grid = e_day_view_convert_time_to_grid_position ( 2461 day_view, 2462 end_time - 60, 2463 &end_col, 2464 &end_row); 2465 2466 /* If either of the times isn't in the grid, or the selection covers 2467 * an entire day, we set the selection to 1 row from the start of the 2468 * working day, in the day corresponding to the start time. */ 2469 if (!start_in_grid || !end_in_grid 2470 || (start_row == 0 && end_row == day_view->rows - 1)) { 2471 end_col = start_col; 2472 2473 start_row = e_day_view_convert_time_to_row ( 2474 day_view, work_day_start_hour, work_day_start_minute); 2475 start_row = CLAMP (start_row, 0, day_view->rows - 1); 2476 end_row = start_row; 2477 } 2478 2479 if (start_row != day_view->selection_start_row 2480 || start_col != day_view->selection_start_day) { 2481 need_redraw = TRUE; 2482 day_view->selection_in_top_canvas = FALSE; 2483 day_view->selection_start_row = start_row; 2484 day_view->selection_start_day = start_col; 2485 } 2486 2487 if (end_row != day_view->selection_end_row 2488 || end_col != day_view->selection_end_day) { 2489 need_redraw = TRUE; 2490 day_view->selection_in_top_canvas = FALSE; 2491 day_view->selection_end_row = end_row; 2492 day_view->selection_end_day = end_col; 2493 } 2494 2495 if (need_redraw) { 2496 gtk_widget_queue_draw (day_view->top_canvas); 2497 gtk_widget_queue_draw (day_view->top_dates_canvas); 2498 gtk_widget_queue_draw (day_view->main_canvas); 2499 } 2500 } 2501 2502 /* Finds the start of the working week which includes the given time. */ 2503 static time_t 2504 e_day_view_find_work_week_start (EDayView *day_view, 2505 time_t start_time) 2506 { 2507 GDate date; 2508 ECalModel *model; 2509 gint week_start_day; 2510 gint weekday, day, i; 2511 guint offset; 2512 struct icaltimetype tt = icaltime_null_time (); 2513 2514 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 2515 week_start_day = e_cal_model_get_week_start_day (model); 2516 2517 time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 2518 2519 /* The start of the work-week is the first working day after the 2520 * week start day. */ 2521 2522 /* Get the weekday corresponding to start_time, 0 (Mon) to 6 (Sun). */ 2523 weekday = (g_date_get_weekday (&date) + 6) % 7; 2524 2525 /* Calculate the first working day of the week, 0 (Mon) to 6 (Sun). 2526 * It will automatically default to the week start day if no days 2527 * are set as working days. */ 2528 day = week_start_day % 7; 2529 for (i = 0; i < 7; i++) { 2530 /* the working_days has stored 0 (Sun) to 6 (Sat) */ 2531 if (day_view->working_days & (1 << ((day + 1) % 7))) 2532 break; 2533 day = (day + 1) % 7; 2534 } 2535 2536 /* Calculate how many days we need to go back to the first workday. */ 2537 if (weekday < day) { 2538 offset = (7 - day + weekday) % 7; 2539 } else { 2540 offset = (weekday - day) % 7; 2541 } 2542 2543 if (offset) 2544 g_date_subtract_days (&date, offset); 2545 2546 tt.year = g_date_get_year (&date); 2547 tt.month = g_date_get_month (&date); 2548 tt.day = g_date_get_day (&date); 2549 2550 return icaltime_as_timet_with_zone (tt, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 2551 } 2552 2553 /* This sets the selected time range. If the start_time & end_time are not equal 2554 * and are both visible in the view, then the selection is set to those times, 2555 * otherwise it is set to 1 hour from the start of the working day. */ 2556 static void 2557 e_day_view_set_selected_time_range (ECalendarView *cal_view, 2558 time_t start_time, 2559 time_t end_time) 2560 { 2561 ECalModel *model; 2562 EDayView *day_view; 2563 gint work_day_start_hour; 2564 gint work_day_start_minute; 2565 gint start_row, start_col, end_row, end_col; 2566 gboolean need_redraw = FALSE, start_in_grid, end_in_grid; 2567 2568 day_view = E_DAY_VIEW (cal_view); 2569 model = e_calendar_view_get_model (cal_view); 2570 work_day_start_hour = e_cal_model_get_work_day_start_hour (model); 2571 work_day_start_minute = e_cal_model_get_work_day_start_minute (model); 2572 2573 if (start_time == end_time) 2574 end_time += e_calendar_view_get_time_divisions (cal_view) * 60; 2575 2576 /* Set the selection. */ 2577 start_in_grid = e_day_view_convert_time_to_grid_position ( 2578 day_view, 2579 start_time, 2580 &start_col, 2581 &start_row); 2582 end_in_grid = e_day_view_convert_time_to_grid_position ( 2583 day_view, 2584 end_time - 60, 2585 &end_col, 2586 &end_row); 2587 2588 /* If either of the times isn't in the grid, or the selection covers 2589 * an entire day, we set the selection to 1 row from the start of the 2590 * working day, in the day corresponding to the start time. */ 2591 if (!start_in_grid || !end_in_grid 2592 || (start_row == 0 && end_row == day_view->rows - 1)) { 2593 end_col = start_col; 2594 2595 start_row = e_day_view_convert_time_to_row ( 2596 day_view, work_day_start_hour, work_day_start_minute); 2597 start_row = CLAMP (start_row, 0, day_view->rows - 1); 2598 end_row = start_row; 2599 } 2600 2601 if (start_row != day_view->selection_start_row 2602 || start_col != day_view->selection_start_day) { 2603 need_redraw = TRUE; 2604 day_view->selection_in_top_canvas = FALSE; 2605 day_view->selection_start_row = start_row; 2606 day_view->selection_start_day = start_col; 2607 } 2608 2609 if (end_row != day_view->selection_end_row 2610 || end_col != day_view->selection_end_day) { 2611 need_redraw = TRUE; 2612 day_view->selection_in_top_canvas = FALSE; 2613 day_view->selection_end_row = end_row; 2614 day_view->selection_end_day = end_col; 2615 } 2616 2617 if (need_redraw) { 2618 gtk_widget_queue_draw (day_view->top_canvas); 2619 gtk_widget_queue_draw (day_view->top_dates_canvas); 2620 gtk_widget_queue_draw (day_view->main_canvas); 2621 } 2622 } 2623 2624 /* Returns the selected time range. */ 2625 static gboolean 2626 e_day_view_get_selected_time_range (ECalendarView *cal_view, 2627 time_t *start_time, 2628 time_t *end_time) 2629 { 2630 gint start_col, start_row, end_col, end_row; 2631 time_t start, end; 2632 EDayView *day_view = E_DAY_VIEW (cal_view); 2633 2634 start_col = day_view->selection_start_day; 2635 start_row = day_view->selection_start_row; 2636 end_col = day_view->selection_end_day; 2637 end_row = day_view->selection_end_row; 2638 2639 if (start_col == -1) { 2640 start_col = 0; 2641 start_row = 0; 2642 end_col = 0; 2643 end_row = 0; 2644 } 2645 2646 /* Check if the selection is only in the top canvas, in which case 2647 * we can simply use the day_starts array. */ 2648 if (day_view->selection_in_top_canvas) { 2649 start = day_view->day_starts[start_col]; 2650 end = day_view->day_starts[end_col + 1]; 2651 } else { 2652 /* Convert the start col + row into a time. */ 2653 start = e_day_view_convert_grid_position_to_time (day_view, start_col, start_row); 2654 end = e_day_view_convert_grid_position_to_time (day_view, end_col, end_row + 1); 2655 } 2656 2657 if (start_time) 2658 *start_time = start; 2659 2660 if (end_time) 2661 *end_time = end; 2662 2663 return TRUE; 2664 } 2665 2666 /* Gets the visible time range. Returns FALSE if no time range has been set. */ 2667 static gboolean 2668 e_day_view_get_visible_time_range (ECalendarView *cal_view, 2669 time_t *start_time, 2670 time_t *end_time) 2671 { 2672 EDayView *day_view = E_DAY_VIEW (cal_view); 2673 2674 /* If the date isn't set, return FALSE. */ 2675 if (day_view->lower == 0 && day_view->upper == 0) 2676 return FALSE; 2677 2678 *start_time = day_view->day_starts[0]; 2679 *end_time = day_view->day_starts[day_view->days_shown]; 2680 2681 return TRUE; 2682 } 2683 2684 static void 2685 e_day_view_recalc_day_starts (EDayView *day_view, 2686 time_t start_time) 2687 { 2688 gint day; 2689 gchar *str; 2690 struct icaltimetype tt; 2691 GDate dt; 2692 2693 day_view->day_starts[0] = start_time; 2694 for (day = 1; day <= day_view->days_shown; day++) { 2695 day_view->day_starts[day] = time_add_day_with_zone (day_view->day_starts[day - 1], 1, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 2696 } 2697 2698 #if 0 2699 for (day = 0; day <= day_view->days_shown; day++) 2700 g_print ("Day Starts %i: %s", day, ctime (&day_view->day_starts[day])); 2701 #endif 2702 2703 day_view->lower = start_time; 2704 day_view->upper = day_view->day_starts[day_view->days_shown]; 2705 2706 tt = icaltime_from_timet_with_zone (day_view->day_starts[0], FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 2707 g_date_clear (&dt, 1); 2708 g_date_set_dmy (&dt, tt.day, tt.month, tt.year); 2709 /* To Translators: the %d stands for a week number, it's value between 1 and 52/53 */ 2710 str = g_strdup_printf (_("Week %d"), g_date_get_iso8601_week_of_year (&dt)); 2711 gtk_label_set_text (GTK_LABEL (day_view->week_number_label), str); 2712 g_free (str); 2713 2714 if (day_view->work_week_view) 2715 e_day_view_recalc_work_week (day_view); 2716 } 2717 2718 /* Whether we are displaying a work-week, in which case the display always 2719 * starts on the first day of the working week. */ 2720 gboolean 2721 e_day_view_get_work_week_view (EDayView *day_view) 2722 { 2723 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE); 2724 2725 return day_view->work_week_view; 2726 } 2727 2728 void 2729 e_day_view_set_work_week_view (EDayView *day_view, 2730 gboolean work_week_view) 2731 { 2732 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2733 2734 if (day_view->work_week_view == work_week_view) 2735 return; 2736 2737 day_view->work_week_view = work_week_view; 2738 2739 if (day_view->work_week_view) 2740 e_day_view_recalc_work_week (day_view); 2741 } 2742 2743 gint 2744 e_day_view_get_days_shown (EDayView *day_view) 2745 { 2746 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), -1); 2747 2748 return day_view->days_shown; 2749 } 2750 2751 void 2752 e_day_view_set_days_shown (EDayView *day_view, 2753 gint days_shown) 2754 { 2755 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2756 g_return_if_fail (days_shown >= 1); 2757 g_return_if_fail (days_shown <= E_DAY_VIEW_MAX_DAYS); 2758 2759 if (day_view->days_shown == days_shown) 2760 return; 2761 2762 day_view->days_shown = days_shown; 2763 2764 /* If the date isn't set, just return. */ 2765 if (day_view->lower == 0 && day_view->upper == 0) 2766 return; 2767 2768 e_day_view_recalc_day_starts (day_view, day_view->lower); 2769 e_day_view_recalc_cell_sizes (day_view); 2770 2771 e_day_view_update_query (day_view); 2772 } 2773 2774 /* This specifies the working days in the week. The value is a bitwise 2775 * combination of day flags. Defaults to Mon-Fri. */ 2776 EDayViewDays 2777 e_day_view_get_working_days (EDayView *day_view) 2778 { 2779 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), 0); 2780 2781 return day_view->working_days; 2782 } 2783 2784 void 2785 e_day_view_set_working_days (EDayView *day_view, 2786 EDayViewDays days) 2787 { 2788 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2789 2790 if (day_view->working_days == days) 2791 return; 2792 2793 day_view->working_days = days; 2794 2795 if (day_view->work_week_view) 2796 e_day_view_recalc_work_week (day_view); 2797 2798 /* We have to do this, as the new working days may have no effect on 2799 * the days shown, but we still want the background color to change. */ 2800 gtk_widget_queue_draw (day_view->main_canvas); 2801 2802 g_object_notify (G_OBJECT (day_view), "working-days"); 2803 } 2804 2805 static void 2806 e_day_view_recalc_work_week_days_shown (EDayView *day_view) 2807 { 2808 ECalModel *model; 2809 gint week_start_day; 2810 gint first_day, last_day, i, days_shown; 2811 gboolean has_working_days = FALSE; 2812 2813 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 2814 week_start_day = e_cal_model_get_week_start_day (model); 2815 2816 /* Find the first working day in the week, 0 (Mon) to 6 (Sun). */ 2817 first_day = week_start_day % 7; 2818 for (i = 0; i < 7; i++) { 2819 /* the working_days has stored 0 (Sun) to 6 (Sat) */ 2820 if (day_view->working_days & (1 << ((first_day + 1) % 7))) { 2821 has_working_days = TRUE; 2822 break; 2823 } 2824 first_day = (first_day + 1) % 7; 2825 } 2826 2827 if (has_working_days) { 2828 /* Now find the last working day of the week, backwards. */ 2829 last_day = (first_day + 6) % 7; 2830 for (i = 0; i < 7; i++) { 2831 /* the working_days has stored 0 (Sun) to 6 (Sat) */ 2832 if (day_view->working_days & (1 << ((last_day + 1) % 7))) 2833 break; 2834 last_day = (last_day + 6) % 7; 2835 } 2836 /* Now calculate the days we need to show to include all the 2837 * working days in the week. Add 1 to make it inclusive. */ 2838 days_shown = (last_day + 7 - first_day) % 7 + 1; 2839 } else { 2840 /* If no working days are set, just use 7. */ 2841 days_shown = 7; 2842 } 2843 2844 e_day_view_set_days_shown (day_view, days_shown); 2845 } 2846 2847 /* Force a redraw of the Marcus Bains Lines */ 2848 void 2849 e_day_view_marcus_bains_update (EDayView *day_view) 2850 { 2851 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2852 gtk_widget_queue_draw (day_view->main_canvas); 2853 gtk_widget_queue_draw (day_view->time_canvas); 2854 } 2855 2856 gboolean 2857 e_day_view_marcus_bains_get_show_line (EDayView *day_view) 2858 { 2859 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE); 2860 2861 return day_view->marcus_bains_show_line; 2862 } 2863 2864 void 2865 e_day_view_marcus_bains_set_show_line (EDayView *day_view, 2866 gboolean show_line) 2867 { 2868 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2869 2870 if (day_view->marcus_bains_show_line == show_line) 2871 return; 2872 2873 day_view->marcus_bains_show_line = show_line; 2874 2875 e_day_view_marcus_bains_update (day_view); 2876 2877 g_object_notify (G_OBJECT (day_view), "marcus-bains-show-line"); 2878 } 2879 2880 const gchar * 2881 e_day_view_marcus_bains_get_day_view_color (EDayView *day_view) 2882 { 2883 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL); 2884 2885 return day_view->marcus_bains_day_view_color; 2886 } 2887 2888 void 2889 e_day_view_marcus_bains_set_day_view_color (EDayView *day_view, 2890 const gchar *day_view_color) 2891 { 2892 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2893 2894 if (g_strcmp0 (day_view->marcus_bains_day_view_color, day_view_color) == 0) 2895 return; 2896 2897 g_free (day_view->marcus_bains_day_view_color); 2898 day_view->marcus_bains_day_view_color = g_strdup (day_view_color); 2899 2900 e_day_view_marcus_bains_update (day_view); 2901 2902 g_object_notify (G_OBJECT (day_view), "marcus-bains-day-view-color"); 2903 } 2904 2905 const gchar * 2906 e_day_view_marcus_bains_get_time_bar_color (EDayView *day_view) 2907 { 2908 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL); 2909 2910 return day_view->marcus_bains_time_bar_color; 2911 } 2912 2913 void 2914 e_day_view_marcus_bains_set_time_bar_color (EDayView *day_view, 2915 const gchar *time_bar_color) 2916 { 2917 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2918 2919 if (g_strcmp0 (day_view->marcus_bains_time_bar_color, time_bar_color) == 0) 2920 return; 2921 2922 g_free (day_view->marcus_bains_time_bar_color); 2923 day_view->marcus_bains_time_bar_color = g_strdup (time_bar_color); 2924 2925 e_day_view_marcus_bains_update (day_view); 2926 2927 g_object_notify (G_OBJECT (day_view), "marcus-bains-time-bar-color"); 2928 } 2929 2930 /* Whether we display event end times in the main canvas. */ 2931 gboolean 2932 e_day_view_get_show_event_end_times (EDayView *day_view) 2933 { 2934 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), TRUE); 2935 2936 return day_view->show_event_end_times; 2937 } 2938 2939 void 2940 e_day_view_set_show_event_end_times (EDayView *day_view, 2941 gboolean show) 2942 { 2943 g_return_if_fail (E_IS_DAY_VIEW (day_view)); 2944 2945 if (day_view->show_event_end_times != show) { 2946 day_view->show_event_end_times = show; 2947 e_day_view_foreach_event (day_view, 2948 e_day_view_set_show_times_cb, NULL); 2949 } 2950 } 2951 2952 /* This is a callback used to update all day event labels. */ 2953 static gboolean 2954 e_day_view_set_show_times_cb (EDayView *day_view, 2955 gint day, 2956 gint event_num, 2957 gpointer data) 2958 { 2959 if (day != E_DAY_VIEW_LONG_EVENT) { 2960 e_day_view_update_event_label (day_view, day, event_num); 2961 } 2962 2963 return TRUE; 2964 } 2965 2966 static void 2967 e_day_view_recalc_work_week (EDayView *day_view) 2968 { 2969 time_t lower; 2970 2971 /* If we aren't showing the work week, just return. */ 2972 if (!day_view->work_week_view) 2973 return; 2974 2975 e_day_view_recalc_work_week_days_shown (day_view); 2976 2977 /* If the date isn't set, just return. */ 2978 if (day_view->lower == 0 && day_view->upper == 0) 2979 return; 2980 2981 lower = e_day_view_find_work_week_start (day_view, day_view->lower); 2982 if (lower != day_view->lower) { 2983 /* Reset the selection, as it may disappear. */ 2984 day_view->selection_start_day = -1; 2985 2986 e_day_view_recalc_day_starts (day_view, lower); 2987 e_day_view_update_query (day_view); 2988 2989 /* This updates the date navigator. */ 2990 e_day_view_update_calendar_selection_time (day_view); 2991 } 2992 } 2993 2994 static gboolean 2995 e_day_view_update_scroll_regions (EDayView *day_view) 2996 { 2997 GtkAllocation main_canvas_allocation; 2998 GtkAllocation time_canvas_allocation; 2999 gdouble old_x2, old_y2, new_x2, new_y2; 3000 gboolean need_reshape = FALSE; 3001 3002 gtk_widget_get_allocation ( 3003 day_view->main_canvas, &main_canvas_allocation); 3004 gtk_widget_get_allocation ( 3005 day_view->time_canvas, &time_canvas_allocation); 3006 3007 /* Set the scroll region of the time canvas to its allocated width, 3008 * but with the height the same as the main canvas. */ 3009 gnome_canvas_get_scroll_region ( 3010 GNOME_CANVAS (day_view->time_canvas), 3011 NULL, NULL, &old_x2, &old_y2); 3012 new_x2 = time_canvas_allocation.width - 1; 3013 new_y2 = MAX ( 3014 day_view->rows * day_view->row_height, 3015 main_canvas_allocation.height) - 1; 3016 if (old_x2 != new_x2 || old_y2 != new_y2) 3017 gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->time_canvas), 3018 0, 0, new_x2, new_y2); 3019 3020 /* Set the scroll region of the main canvas to its allocated width, 3021 * but with the height depending on the number of rows needed. */ 3022 gnome_canvas_get_scroll_region ( 3023 GNOME_CANVAS (day_view->main_canvas), 3024 NULL, NULL, &old_x2, &old_y2); 3025 new_x2 = main_canvas_allocation.width - 1; 3026 3027 if (day_view->days_shown == 1) 3028 new_x2 = MAX (new_x2, day_view->max_cols * (E_DAY_VIEW_MIN_DAY_COL_WIDTH + E_DAY_VIEW_GAP_WIDTH) - E_DAY_VIEW_MIN_DAY_COL_WIDTH - 1); 3029 3030 if (old_x2 != new_x2 || old_y2 != new_y2) { 3031 need_reshape = TRUE; 3032 gnome_canvas_set_scroll_region ( 3033 GNOME_CANVAS (day_view->main_canvas), 3034 0, 0, new_x2, new_y2); 3035 } 3036 3037 if (new_x2 <= main_canvas_allocation.width - 1) 3038 gtk_widget_hide (day_view->mc_hscrollbar); 3039 else 3040 gtk_widget_show (day_view->mc_hscrollbar); 3041 3042 return need_reshape; 3043 } 3044 3045 /* This recalculates the number of rows to display, based on the time range 3046 * shown and the minutes per row. */ 3047 static void 3048 e_day_view_recalc_num_rows (EDayView *day_view) 3049 { 3050 ECalendarView *cal_view; 3051 gint time_divisions; 3052 gint hours, minutes, total_minutes; 3053 3054 cal_view = E_CALENDAR_VIEW (day_view); 3055 time_divisions = e_calendar_view_get_time_divisions (cal_view); 3056 3057 hours = day_view->last_hour_shown - day_view->first_hour_shown; 3058 /* This could be negative but it works out OK. */ 3059 minutes = day_view->last_minute_shown - day_view->first_minute_shown; 3060 total_minutes = hours * 60 + minutes; 3061 day_view->rows = total_minutes / time_divisions; 3062 } 3063 3064 /* Converts an hour and minute to a row in the canvas. Note that if we aren't 3065 * showing all 24 hours of the day, the returned row may be negative or 3066 * greater than day_view->rows. */ 3067 gint 3068 e_day_view_convert_time_to_row (EDayView *day_view, 3069 gint hour, 3070 gint minute) 3071 { 3072 ECalendarView *cal_view; 3073 gint time_divisions; 3074 gint total_minutes, start_minute, offset; 3075 3076 cal_view = E_CALENDAR_VIEW (day_view); 3077 time_divisions = e_calendar_view_get_time_divisions (cal_view); 3078 3079 total_minutes = hour * 60 + minute; 3080 start_minute = day_view->first_hour_shown * 60 3081 + day_view->first_minute_shown; 3082 offset = total_minutes - start_minute; 3083 if (offset < 0) 3084 return -1; 3085 else 3086 return offset / time_divisions; 3087 } 3088 3089 /* Converts an hour and minute to a y coordinate in the canvas. */ 3090 gint 3091 e_day_view_convert_time_to_position (EDayView *day_view, 3092 gint hour, 3093 gint minute) 3094 { 3095 ECalendarView *cal_view; 3096 gint time_divisions; 3097 gint total_minutes, start_minute, offset; 3098 3099 cal_view = E_CALENDAR_VIEW (day_view); 3100 time_divisions = e_calendar_view_get_time_divisions (cal_view); 3101 3102 total_minutes = hour * 60 + minute; 3103 start_minute = day_view->first_hour_shown * 60 3104 + day_view->first_minute_shown; 3105 offset = total_minutes - start_minute; 3106 3107 return offset * day_view->row_height / time_divisions; 3108 } 3109 3110 static gboolean 3111 e_day_view_on_top_canvas_button_press (GtkWidget *widget, 3112 GdkEventButton *event, 3113 EDayView *day_view) 3114 { 3115 gint event_x, event_y, day, event_num; 3116 ECalendarViewPosition pos; 3117 GtkLayout *layout; 3118 GdkWindow *window; 3119 3120 layout = GTK_LAYOUT (widget); 3121 window = gtk_layout_get_bin_window (layout); 3122 3123 if (day_view->resize_event_num != -1) 3124 day_view->resize_event_num = -1; 3125 3126 if (day_view->drag_event_num != -1) 3127 day_view->drag_event_num = -1; 3128 3129 /* Convert the coords to the main canvas window, or return if the 3130 * window is not found. */ 3131 if (!e_day_view_convert_event_coords ( 3132 day_view, (GdkEvent *) event, window, &event_x, &event_y)) 3133 return FALSE; 3134 3135 pos = e_day_view_convert_position_in_top_canvas ( 3136 day_view, 3137 event_x, event_y, 3138 &day, &event_num); 3139 3140 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE) 3141 return FALSE; 3142 3143 if (pos != E_CALENDAR_VIEW_POS_NONE) 3144 return e_day_view_on_long_event_button_press ( 3145 day_view, 3146 event_num, 3147 event, pos, 3148 event_x, 3149 event_y); 3150 3151 e_day_view_stop_editing_event (day_view); 3152 3153 if (event->button == 1) { 3154 if (event->type == GDK_2BUTTON_PRESS) { 3155 time_t dtstart, dtend; 3156 3157 e_day_view_get_selected_time_range ((ECalendarView *) day_view, &dtstart, &dtend); 3158 if (dtstart < day_view->before_click_dtend && dtend > day_view->before_click_dtstart) { 3159 dtstart = day_view->before_click_dtstart; 3160 dtend = day_view->before_click_dtend; 3161 e_day_view_set_selected_time_range ((ECalendarView *) day_view, dtstart, dtend); 3162 } 3163 3164 e_calendar_view_new_appointment_for (E_CALENDAR_VIEW (day_view), 3165 dtstart, dtend, 3166 TRUE, calendar_config_get_prefer_meeting ()); 3167 return TRUE; 3168 } 3169 3170 if (!gtk_widget_has_focus (GTK_WIDGET (day_view))) 3171 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 3172 3173 if (gdk_pointer_grab (window, FALSE, 3174 GDK_POINTER_MOTION_MASK 3175 | GDK_BUTTON_RELEASE_MASK, 3176 NULL, NULL, event->time) == 0) { 3177 if (event->time - day_view->bc_event_time > 250) 3178 e_day_view_get_selected_time_range ((ECalendarView *) day_view, &day_view->before_click_dtstart, &day_view->before_click_dtend); 3179 day_view->bc_event_time = event->time; 3180 e_day_view_start_selection (day_view, day, -1); 3181 } 3182 } else if (event->button == 3) { 3183 if (!gtk_widget_has_focus (GTK_WIDGET (day_view))) 3184 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 3185 3186 if (day < day_view->selection_start_day || day > day_view->selection_end_day) { 3187 e_day_view_start_selection (day_view, day, -1); 3188 e_day_view_finish_selection (day_view); 3189 } 3190 3191 e_day_view_on_event_right_click (day_view, event, -1, -1); 3192 } 3193 3194 return TRUE; 3195 } 3196 3197 static gboolean 3198 e_day_view_convert_event_coords (EDayView *day_view, 3199 GdkEvent *event, 3200 GdkWindow *window, 3201 gint *x_return, 3202 gint *y_return) 3203 { 3204 gint event_x, event_y, win_x, win_y; 3205 GdkWindow *event_window;; 3206 3207 /* Get the event window, x & y from the appropriate event struct. */ 3208 switch (event->type) { 3209 case GDK_BUTTON_PRESS: 3210 case GDK_2BUTTON_PRESS: 3211 case GDK_3BUTTON_PRESS: 3212 case GDK_BUTTON_RELEASE: 3213 event_x = event->button.x; 3214 event_y = event->button.y; 3215 event_window = event->button.window; 3216 break; 3217 case GDK_MOTION_NOTIFY: 3218 event_x = event->motion.x; 3219 event_y = event->motion.y; 3220 event_window = event->motion.window; 3221 break; 3222 case GDK_ENTER_NOTIFY: 3223 case GDK_LEAVE_NOTIFY: 3224 event_x = event->crossing.x; 3225 event_y = event->crossing.y; 3226 event_window = event->crossing.window; 3227 break; 3228 default: 3229 /* Shouldn't get here. */ 3230 g_return_val_if_reached (FALSE); 3231 } 3232 3233 while (event_window && event_window != window 3234 && event_window != gdk_get_default_root_window ()) { 3235 gdk_window_get_position (event_window, &win_x, &win_y); 3236 event_x += win_x; 3237 event_y += win_y; 3238 event_window = gdk_window_get_parent (event_window); 3239 } 3240 3241 *x_return = event_x; 3242 *y_return = event_y; 3243 3244 return (event_window == window) ? TRUE : FALSE; 3245 } 3246 3247 static gboolean 3248 e_day_view_on_main_canvas_button_press (GtkWidget *widget, 3249 GdkEventButton *event, 3250 EDayView *day_view) 3251 { 3252 gint event_x, event_y, row, day, event_num; 3253 ECalendarViewPosition pos; 3254 GtkLayout *layout; 3255 GdkWindow *window; 3256 3257 #if 0 3258 g_print ("In e_day_view_on_main_canvas_button_press\n"); 3259 #endif 3260 3261 layout = GTK_LAYOUT (widget); 3262 window = gtk_layout_get_bin_window (layout); 3263 3264 if (day_view->resize_event_num != -1) 3265 day_view->resize_event_num = -1; 3266 3267 if (day_view->drag_event_num != -1) 3268 day_view->drag_event_num = -1; 3269 3270 /* Convert the coords to the main canvas window, or return if the 3271 * window is not found. */ 3272 if (!e_day_view_convert_event_coords ( 3273 day_view, (GdkEvent *) event, window, &event_x, &event_y)) 3274 return FALSE; 3275 3276 /* Find out where the mouse is. */ 3277 pos = e_day_view_convert_position_in_main_canvas ( 3278 day_view, 3279 event_x, event_y, 3280 &day, &row, 3281 &event_num); 3282 3283 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE) 3284 return FALSE; 3285 3286 if (pos != E_CALENDAR_VIEW_POS_NONE) 3287 return e_day_view_on_event_button_press ( 3288 day_view, day, 3289 event_num, event, pos, 3290 event_x, event_y); 3291 3292 e_day_view_stop_editing_event (day_view); 3293 3294 /* Start the selection drag. */ 3295 if (event->button == 1) { 3296 if (event->type == GDK_2BUTTON_PRESS) { 3297 time_t dtstart, dtend; 3298 3299 e_day_view_get_selected_time_range ((ECalendarView *) day_view, &dtstart, &dtend); 3300 if (dtstart < day_view->before_click_dtend && dtend > day_view->before_click_dtstart) { 3301 dtstart = day_view->before_click_dtstart; 3302 dtend = day_view->before_click_dtend; 3303 e_day_view_set_selected_time_range ((ECalendarView *) day_view, dtstart, dtend); 3304 } 3305 e_calendar_view_new_appointment_for (E_CALENDAR_VIEW (day_view), 3306 dtstart, dtend, 3307 FALSE, calendar_config_get_prefer_meeting ()); 3308 return TRUE; 3309 } 3310 3311 if (!gtk_widget_has_focus (GTK_WIDGET (day_view)) && !gtk_widget_has_focus (GTK_WIDGET (day_view->main_canvas))) 3312 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 3313 3314 if (gdk_pointer_grab (window, FALSE, 3315 GDK_POINTER_MOTION_MASK 3316 | GDK_BUTTON_RELEASE_MASK, 3317 NULL, NULL, event->time) == 0) { 3318 if (event->time - day_view->bc_event_time > 250) 3319 e_day_view_get_selected_time_range ((ECalendarView *) day_view, &day_view->before_click_dtstart, &day_view->before_click_dtend); 3320 day_view->bc_event_time = event->time; 3321 e_day_view_start_selection (day_view, day, row); 3322 g_signal_emit_by_name (day_view, "selected_time_changed"); 3323 } 3324 } else if (event->button == 3) { 3325 if (!gtk_widget_has_focus (GTK_WIDGET (day_view))) 3326 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 3327 3328 if ((day < day_view->selection_start_day || day > day_view->selection_end_day) 3329 || (day == day_view->selection_start_day && row < day_view->selection_start_row) 3330 || (day == day_view->selection_end_day && row > day_view->selection_end_row)) { 3331 e_day_view_start_selection (day_view, day, row); 3332 e_day_view_finish_selection (day_view); 3333 } 3334 3335 e_day_view_on_event_right_click (day_view, event, -1, -1); 3336 } 3337 3338 return TRUE; 3339 } 3340 3341 static gboolean 3342 e_day_view_on_main_canvas_scroll (GtkWidget *widget, 3343 GdkEventScroll *scroll, 3344 EDayView *day_view) 3345 { 3346 switch (scroll->direction) { 3347 case GDK_SCROLL_UP: 3348 e_day_view_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE); 3349 return TRUE; 3350 case GDK_SCROLL_DOWN: 3351 e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE); 3352 return TRUE; 3353 #if GTK_CHECK_VERSION(3,3,18) 3354 case GDK_SCROLL_SMOOTH: 3355 if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) { 3356 e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y); 3357 return TRUE; 3358 } 3359 break; 3360 #endif 3361 default: 3362 break; 3363 } 3364 3365 return FALSE; 3366 } 3367 3368 static gboolean 3369 e_day_view_on_top_canvas_scroll (GtkWidget *widget, 3370 GdkEventScroll *scroll, 3371 EDayView *day_view) 3372 { 3373 switch (scroll->direction) { 3374 case GDK_SCROLL_UP: 3375 e_day_view_top_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE); 3376 return TRUE; 3377 case GDK_SCROLL_DOWN: 3378 e_day_view_top_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE); 3379 return TRUE; 3380 #if GTK_CHECK_VERSION(3,3,18) 3381 case GDK_SCROLL_SMOOTH: 3382 if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) { 3383 e_day_view_top_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y); 3384 return TRUE; 3385 } 3386 break; 3387 #endif 3388 default: 3389 break; 3390 } 3391 3392 return FALSE; 3393 } 3394 3395 static gboolean 3396 e_day_view_on_time_canvas_scroll (GtkWidget *widget, 3397 GdkEventScroll *scroll, 3398 EDayView *day_view) 3399 { 3400 GtkWidget *tool_window = g_object_get_data ((GObject *) day_view, "tooltip-window"); 3401 3402 if (tool_window) { 3403 gtk_widget_destroy (tool_window); 3404 g_object_set_data (G_OBJECT (day_view), "tooltip-window", NULL); 3405 } 3406 3407 switch (scroll->direction) { 3408 case GDK_SCROLL_UP: 3409 e_day_view_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE); 3410 return TRUE; 3411 case GDK_SCROLL_DOWN: 3412 e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE); 3413 return TRUE; 3414 #if GTK_CHECK_VERSION(3,3,18) 3415 case GDK_SCROLL_SMOOTH: 3416 if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) { 3417 e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y); 3418 return TRUE; 3419 } 3420 break; 3421 #endif 3422 default: 3423 break; 3424 } 3425 3426 return FALSE; 3427 } 3428 3429 static gboolean 3430 e_day_view_on_long_event_button_press (EDayView *day_view, 3431 gint event_num, 3432 GdkEventButton *event, 3433 ECalendarViewPosition pos, 3434 gint event_x, 3435 gint event_y) 3436 { 3437 if (event->button == 1) { 3438 if (event->type == GDK_BUTTON_PRESS) { 3439 e_day_view_on_long_event_click ( 3440 day_view, event_num, 3441 event, pos, 3442 event_x, event_y); 3443 return TRUE; 3444 } else if (event->type == GDK_2BUTTON_PRESS) { 3445 e_day_view_on_event_double_click ( 3446 day_view, -1, 3447 event_num); 3448 g_signal_stop_emission_by_name (day_view->top_canvas, "button_press_event"); 3449 return TRUE; 3450 } 3451 } else if (event->button == 3) { 3452 EDayViewEvent *e; 3453 3454 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 3455 return TRUE; 3456 3457 e = &g_array_index (day_view->long_events, EDayViewEvent, event_num); 3458 3459 e_day_view_set_selected_time_range_in_top_visible (day_view, e->start, e->end); 3460 3461 e_day_view_on_event_right_click ( 3462 day_view, event, 3463 E_DAY_VIEW_LONG_EVENT, 3464 event_num); 3465 3466 return TRUE; 3467 } 3468 return FALSE; 3469 } 3470 3471 static gboolean 3472 e_day_view_on_event_button_press (EDayView *day_view, 3473 gint day, 3474 gint event_num, 3475 GdkEventButton *event, 3476 ECalendarViewPosition pos, 3477 gint event_x, 3478 gint event_y) 3479 { 3480 if (event->button == 1) { 3481 if (event->type == GDK_BUTTON_PRESS) { 3482 e_day_view_on_event_click ( 3483 day_view, day, event_num, 3484 event, pos, 3485 event_x, event_y); 3486 return TRUE; 3487 } else if (event->type == GDK_2BUTTON_PRESS) { 3488 e_day_view_on_event_double_click ( 3489 day_view, day, 3490 event_num); 3491 3492 g_signal_stop_emission_by_name (day_view->main_canvas, "button_press_event"); 3493 return TRUE; 3494 } 3495 } else if (event->button == 3) { 3496 EDayViewEvent *e; 3497 3498 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 3499 return TRUE; 3500 3501 e = &g_array_index (day_view->events[day], EDayViewEvent, event_num); 3502 3503 e_day_view_set_selected_time_range_visible (day_view, e->start, e->end); 3504 3505 e_day_view_on_event_right_click ( 3506 day_view, event, 3507 day, event_num); 3508 3509 return TRUE; 3510 } 3511 return FALSE; 3512 } 3513 3514 static void 3515 e_day_view_on_long_event_click (EDayView *day_view, 3516 gint event_num, 3517 GdkEventButton *bevent, 3518 ECalendarViewPosition pos, 3519 gint event_x, 3520 gint event_y) 3521 { 3522 EDayViewEvent *event; 3523 GtkLayout *layout; 3524 GdkWindow *window; 3525 gint start_day, end_day, day; 3526 gint item_x, item_y, item_w, item_h; 3527 3528 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 3529 return; 3530 3531 event = &g_array_index (day_view->long_events, EDayViewEvent, 3532 event_num); 3533 3534 if (!is_comp_data_valid (event)) 3535 return; 3536 3537 /* Ignore clicks on the EText while editing. */ 3538 if (pos == E_CALENDAR_VIEW_POS_EVENT 3539 && E_TEXT (event->canvas_item)->editing) { 3540 GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (event->canvas_item, (GdkEvent *) bevent); 3541 return; 3542 } 3543 3544 if ((e_cal_util_component_is_instance (event->comp_data->icalcomp) || 3545 !e_cal_util_component_has_recurrences (event->comp_data->icalcomp)) 3546 && (pos == E_CALENDAR_VIEW_POS_LEFT_EDGE 3547 || pos == E_CALENDAR_VIEW_POS_RIGHT_EDGE)) { 3548 if (!e_day_view_find_long_event_days (event, 3549 day_view->days_shown, 3550 day_view->day_starts, 3551 &start_day, &end_day)) 3552 return; 3553 3554 /* Grab the keyboard focus, so the event being edited is saved 3555 * and we can use the Escape key to abort the resize. */ 3556 if (!gtk_widget_has_focus (GTK_WIDGET (day_view))) 3557 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 3558 3559 layout = GTK_LAYOUT (day_view->top_canvas); 3560 window = gtk_layout_get_bin_window (layout); 3561 3562 if (gdk_pointer_grab (window, FALSE, 3563 GDK_POINTER_MOTION_MASK 3564 | GDK_BUTTON_RELEASE_MASK, 3565 NULL, NULL, bevent->time) == 0) { 3566 3567 day_view->resize_event_day = E_DAY_VIEW_LONG_EVENT; 3568 day_view->resize_event_num = event_num; 3569 day_view->resize_drag_pos = pos; 3570 day_view->resize_start_row = start_day; 3571 day_view->resize_end_row = end_day; 3572 3573 /* Raise the event's item, above the rect as well. */ 3574 gnome_canvas_item_raise_to_top (event->canvas_item); 3575 } 3576 } else if (e_day_view_get_long_event_position (day_view, event_num, 3577 &start_day, &end_day, 3578 &item_x, &item_y, 3579 &item_w, &item_h)) { 3580 /* Remember the item clicked and the mouse position, 3581 * so we can start a drag if the mouse moves. */ 3582 day_view->pressed_event_day = E_DAY_VIEW_LONG_EVENT; 3583 day_view->pressed_event_num = event_num; 3584 3585 day_view->drag_event_x = event_x; 3586 day_view->drag_event_y = event_y; 3587 3588 e_day_view_convert_position_in_top_canvas ( 3589 day_view, 3590 event_x, event_y, 3591 &day, NULL); 3592 day_view->drag_event_offset = day - start_day; 3593 } 3594 } 3595 3596 static void 3597 e_day_view_on_event_click (EDayView *day_view, 3598 gint day, 3599 gint event_num, 3600 GdkEventButton *bevent, 3601 ECalendarViewPosition pos, 3602 gint event_x, 3603 gint event_y) 3604 { 3605 EDayViewEvent *event; 3606 ECalendarView *cal_view; 3607 GtkLayout *layout; 3608 GdkWindow *window; 3609 gint time_divisions; 3610 gint tmp_day, row, start_row; 3611 3612 cal_view = E_CALENDAR_VIEW (day_view); 3613 time_divisions = e_calendar_view_get_time_divisions (cal_view); 3614 3615 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 3616 return; 3617 3618 event = &g_array_index (day_view->events[day], EDayViewEvent, 3619 event_num); 3620 3621 if (!is_comp_data_valid (event)) 3622 return; 3623 3624 /* Ignore clicks on the EText while editing. */ 3625 if (pos == E_CALENDAR_VIEW_POS_EVENT 3626 && E_TEXT (event->canvas_item)->editing) { 3627 GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (event->canvas_item, (GdkEvent *) bevent); 3628 return; 3629 } 3630 3631 if ((e_cal_util_component_is_instance (event->comp_data->icalcomp) || 3632 !e_cal_util_component_has_recurrences (event->comp_data->icalcomp)) 3633 && (pos == E_CALENDAR_VIEW_POS_TOP_EDGE 3634 || pos == E_CALENDAR_VIEW_POS_BOTTOM_EDGE)) { 3635 if (event && (!event->is_editable || e_client_is_readonly (E_CLIENT (event->comp_data->client)))) { 3636 return; 3637 } 3638 3639 /* Grab the keyboard focus, so the event being edited is saved 3640 * and we can use the Escape key to abort the resize. */ 3641 if (!gtk_widget_has_focus (GTK_WIDGET (day_view))) 3642 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 3643 3644 layout = GTK_LAYOUT (day_view->main_canvas); 3645 window = gtk_layout_get_bin_window (layout); 3646 3647 if (gdk_pointer_grab (window, FALSE, 3648 GDK_POINTER_MOTION_MASK 3649 | GDK_BUTTON_RELEASE_MASK, 3650 NULL, NULL, bevent->time) == 0) { 3651 3652 day_view->resize_event_day = day; 3653 day_view->resize_event_num = event_num; 3654 day_view->resize_drag_pos = pos; 3655 day_view->resize_start_row = event->start_minute / time_divisions; 3656 day_view->resize_end_row = (event->end_minute - 1) / time_divisions; 3657 if (day_view->resize_end_row < day_view->resize_start_row) 3658 day_view->resize_end_row = day_view->resize_start_row; 3659 3660 day_view->resize_bars_event_day = day; 3661 day_view->resize_bars_event_num = event_num; 3662 3663 e_day_view_reshape_main_canvas_resize_bars (day_view); 3664 3665 /* Raise the event's item, above the rect as well. */ 3666 gnome_canvas_item_raise_to_top (event->canvas_item); 3667 } 3668 3669 } else { 3670 /* Remember the item clicked and the mouse position, 3671 * so we can start a drag if the mouse moves. */ 3672 day_view->pressed_event_day = day; 3673 day_view->pressed_event_num = event_num; 3674 3675 day_view->drag_event_x = event_x; 3676 day_view->drag_event_y = event_y; 3677 3678 e_day_view_convert_position_in_main_canvas ( 3679 day_view, 3680 event_x, event_y, 3681 &tmp_day, &row, 3682 NULL); 3683 start_row = event->start_minute / time_divisions; 3684 day_view->drag_event_offset = row - start_row; 3685 } 3686 } 3687 3688 static void 3689 e_day_view_on_event_double_click (EDayView *day_view, 3690 gint day, 3691 gint event_num) 3692 { 3693 EDayViewEvent *event; 3694 3695 if (day == -1) { 3696 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 3697 return; 3698 3699 event = &g_array_index (day_view->long_events, EDayViewEvent, 3700 event_num); 3701 } else { 3702 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 3703 return; 3704 3705 event = &g_array_index (day_view->events[day], EDayViewEvent, 3706 event_num); 3707 } 3708 3709 if (!is_comp_data_valid (event)) 3710 return; 3711 3712 e_calendar_view_edit_appointment ((ECalendarView *) day_view, event->comp_data->client, event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT); 3713 } 3714 3715 static void 3716 e_day_view_show_popup_menu (EDayView *day_view, 3717 GdkEventButton *event, 3718 gint day, 3719 gint event_num) 3720 { 3721 tooltip_destroy (day_view, NULL); 3722 3723 day_view->popup_event_day = day; 3724 day_view->popup_event_num = event_num; 3725 3726 e_calendar_view_popup_event (E_CALENDAR_VIEW (day_view), event); 3727 } 3728 3729 static gboolean 3730 e_day_view_popup_menu (GtkWidget *widget) 3731 { 3732 EDayView *day_view = E_DAY_VIEW (widget); 3733 e_day_view_show_popup_menu ( 3734 day_view, NULL, 3735 day_view->editing_event_day, 3736 day_view->editing_event_num); 3737 return TRUE; 3738 } 3739 3740 /* Returns the currently-selected event, or NULL if none */ 3741 static GList * 3742 e_day_view_get_selected_events (ECalendarView *cal_view) 3743 { 3744 EDayViewEvent *event = NULL; 3745 GList *list = NULL; 3746 EDayView *day_view = (EDayView *) cal_view; 3747 3748 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL); 3749 3750 if (day_view->editing_event_num != -1) { 3751 if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT) { 3752 if (!is_array_index_in_bounds (day_view->long_events, day_view->editing_event_num)) 3753 return NULL; 3754 3755 event = &g_array_index (day_view->long_events, 3756 EDayViewEvent, 3757 day_view->editing_event_num); 3758 } else { 3759 if (!is_array_index_in_bounds (day_view->events[day_view->editing_event_day], day_view->editing_event_num)) 3760 return NULL; 3761 3762 event = &g_array_index (day_view->events[day_view->editing_event_day], 3763 EDayViewEvent, 3764 day_view->editing_event_num); 3765 } 3766 } else if (day_view->popup_event_num != -1) { 3767 if (day_view->popup_event_day == E_DAY_VIEW_LONG_EVENT) { 3768 if (!is_array_index_in_bounds (day_view->long_events, day_view->popup_event_num)) 3769 return NULL; 3770 3771 event = &g_array_index (day_view->long_events, 3772 EDayViewEvent, 3773 day_view->popup_event_num); 3774 } else { 3775 if (!is_array_index_in_bounds (day_view->events[day_view->popup_event_day], day_view->popup_event_num)) 3776 return NULL; 3777 3778 event = &g_array_index (day_view->events[day_view->popup_event_day], 3779 EDayViewEvent, 3780 day_view->popup_event_num); 3781 } 3782 } 3783 3784 if (event) 3785 list = g_list_append (list, event); 3786 3787 return list; 3788 } 3789 3790 /* Restarts a query for the day view */ 3791 static void 3792 e_day_view_update_query (EDayView *day_view) 3793 { 3794 gint rows, r; 3795 3796 if (!E_CALENDAR_VIEW (day_view)->in_focus) { 3797 e_day_view_free_events (day_view); 3798 day_view->requires_update = TRUE; 3799 return; 3800 } 3801 3802 day_view->requires_update = FALSE; 3803 3804 e_day_view_stop_editing_event (day_view); 3805 3806 gtk_widget_queue_draw (day_view->top_canvas); 3807 gtk_widget_queue_draw (day_view->top_dates_canvas); 3808 gtk_widget_queue_draw (day_view->main_canvas); 3809 e_day_view_free_events (day_view); 3810 e_day_view_queue_layout (day_view); 3811 3812 rows = e_table_model_row_count (E_TABLE_MODEL (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)))); 3813 for (r = 0; r < rows; r++) { 3814 ECalModelComponent *comp_data; 3815 3816 comp_data = e_cal_model_get_component_at (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), r); 3817 g_return_if_fail (comp_data != NULL); 3818 process_component (day_view, comp_data); 3819 } 3820 } 3821 3822 static void 3823 e_day_view_on_event_right_click (EDayView *day_view, 3824 GdkEventButton *bevent, 3825 gint day, 3826 gint event_num) 3827 { 3828 e_day_view_show_popup_menu (day_view, bevent, day, event_num); 3829 } 3830 3831 static gboolean 3832 e_day_view_on_top_canvas_button_release (GtkWidget *widget, 3833 GdkEventButton *event, 3834 EDayView *day_view) 3835 { 3836 if (day_view->selection_is_being_dragged) { 3837 gdk_pointer_ungrab (event->time); 3838 e_day_view_finish_selection (day_view); 3839 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { 3840 gdk_pointer_ungrab (event->time); 3841 e_day_view_finish_long_event_resize (day_view); 3842 } else if (day_view->pressed_event_day != -1) { 3843 e_day_view_start_editing_event ( 3844 day_view, 3845 day_view->pressed_event_day, 3846 day_view->pressed_event_num, 3847 NULL); 3848 } 3849 3850 day_view->pressed_event_day = -1; 3851 3852 return FALSE; 3853 } 3854 3855 static gboolean 3856 e_day_view_on_main_canvas_button_release (GtkWidget *widget, 3857 GdkEventButton *event, 3858 EDayView *day_view) 3859 { 3860 #if 0 3861 g_print ("In e_day_view_on_main_canvas_button_release\n"); 3862 #endif 3863 3864 if (day_view->selection_is_being_dragged) { 3865 gdk_pointer_ungrab (event->time); 3866 e_day_view_finish_selection (day_view); 3867 e_day_view_stop_auto_scroll (day_view); 3868 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { 3869 gdk_pointer_ungrab (event->time); 3870 e_day_view_finish_resize (day_view); 3871 e_day_view_stop_auto_scroll (day_view); 3872 } else if (day_view->pressed_event_day != -1) { 3873 e_day_view_start_editing_event ( 3874 day_view, 3875 day_view->pressed_event_day, 3876 day_view->pressed_event_num, 3877 NULL); 3878 } 3879 3880 day_view->pressed_event_day = -1; 3881 3882 return FALSE; 3883 } 3884 3885 void 3886 e_day_view_update_calendar_selection_time (EDayView *day_view) 3887 { 3888 time_t start, end; 3889 #if 0 3890 GnomeCalendar *calendar; 3891 #endif 3892 e_day_view_get_selected_time_range ((ECalendarView *) day_view, &start, &end); 3893 3894 #if 0 3895 g_print ("Start: %s", ctime (&start)); 3896 g_print ("End : %s", ctime (&end)); 3897 #endif 3898 3899 #if 0 3900 calendar = e_calendar_view_get_calendar (E_CALENDAR_VIEW (day_view)); 3901 if (calendar) 3902 gnome_calendar_set_selected_time_range (calendar, start); 3903 #endif 3904 } 3905 3906 static gboolean 3907 e_day_view_on_top_canvas_motion (GtkWidget *widget, 3908 GdkEventMotion *mevent, 3909 EDayView *day_view) 3910 { 3911 EDayViewEvent *event = NULL; 3912 ECalendarViewPosition pos; 3913 gint event_x, event_y, canvas_x, canvas_y; 3914 gint day, event_num; 3915 GdkCursor *cursor; 3916 GdkWindow *window; 3917 3918 #if 0 3919 g_print ("In e_day_view_on_top_canvas_motion\n"); 3920 #endif 3921 3922 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget)); 3923 3924 /* Convert the coords to the main canvas window, or return if the 3925 * window is not found. */ 3926 if (!e_day_view_convert_event_coords ( 3927 day_view, (GdkEvent *) mevent, window, &event_x, &event_y)) 3928 return FALSE; 3929 3930 canvas_x = event_x; 3931 canvas_y = event_y; 3932 3933 pos = e_day_view_convert_position_in_top_canvas ( 3934 day_view, 3935 canvas_x, canvas_y, 3936 &day, &event_num); 3937 if (event_num != -1) { 3938 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 3939 return FALSE; 3940 3941 event = &g_array_index (day_view->long_events, EDayViewEvent, 3942 event_num); 3943 } 3944 3945 if (day_view->selection_is_being_dragged) { 3946 e_day_view_update_selection (day_view, day, -1); 3947 return TRUE; 3948 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { 3949 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) { 3950 e_day_view_update_long_event_resize (day_view, day); 3951 return TRUE; 3952 } 3953 } else if (day_view->pressed_event_day == E_DAY_VIEW_LONG_EVENT) { 3954 GtkTargetList *target_list; 3955 3956 if (!is_array_index_in_bounds (day_view->long_events, day_view->pressed_event_num)) 3957 return FALSE; 3958 3959 event = &g_array_index (day_view->long_events, EDayViewEvent, 3960 day_view->pressed_event_num); 3961 3962 if (!is_comp_data_valid (event)) 3963 return FALSE; 3964 3965 if (!e_cal_util_component_has_recurrences (event->comp_data->icalcomp) 3966 && (abs (canvas_x - day_view->drag_event_x) 3967 > E_DAY_VIEW_DRAG_START_OFFSET 3968 || abs (canvas_y - day_view->drag_event_y) 3969 > E_DAY_VIEW_DRAG_START_OFFSET)) { 3970 day_view->drag_event_day = day_view->pressed_event_day; 3971 day_view->drag_event_num = day_view->pressed_event_num; 3972 day_view->pressed_event_day = -1; 3973 3974 /* Hide the horizontal bars. */ 3975 if (day_view->resize_bars_event_day != -1) { 3976 day_view->resize_bars_event_day = -1; 3977 day_view->resize_bars_event_num = -1; 3978 } 3979 3980 target_list = gtk_target_list_new ( 3981 target_table, G_N_ELEMENTS (target_table)); 3982 e_target_list_add_calendar_targets (target_list, 0); 3983 gtk_drag_begin ( 3984 widget, target_list, 3985 GDK_ACTION_COPY | GDK_ACTION_MOVE, 3986 1, (GdkEvent *) mevent); 3987 gtk_target_list_unref (target_list); 3988 } 3989 } else { 3990 cursor = day_view->normal_cursor; 3991 3992 /* Recurring events can't be resized. */ 3993 if (event && is_comp_data_valid (event) && !e_cal_util_component_has_recurrences (event->comp_data->icalcomp)) { 3994 switch (pos) { 3995 case E_CALENDAR_VIEW_POS_LEFT_EDGE: 3996 case E_CALENDAR_VIEW_POS_RIGHT_EDGE: 3997 cursor = day_view->resize_width_cursor; 3998 break; 3999 default: 4000 break; 4001 } 4002 } 4003 4004 /* Only set the cursor if it is different to last one set. */ 4005 if (day_view->last_cursor_set_in_top_canvas != cursor) { 4006 GdkWindow *window; 4007 4008 day_view->last_cursor_set_in_top_canvas = cursor; 4009 4010 window = gtk_widget_get_window (widget); 4011 gdk_window_set_cursor (window, cursor); 4012 } 4013 4014 if (event && E_IS_TEXT (event->canvas_item) && E_TEXT (event->canvas_item)->editing) { 4015 GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (event->canvas_item, (GdkEvent *) mevent); 4016 } 4017 } 4018 4019 return FALSE; 4020 } 4021 4022 static gboolean 4023 e_day_view_on_main_canvas_motion (GtkWidget *widget, 4024 GdkEventMotion *mevent, 4025 EDayView *day_view) 4026 { 4027 EDayViewEvent *event = NULL; 4028 ECalendarViewPosition pos; 4029 gint event_x, event_y, canvas_x, canvas_y; 4030 gint row, day, event_num; 4031 GdkWindow *window; 4032 GdkCursor *cursor; 4033 4034 #if 0 4035 g_print ("In e_day_view_on_main_canvas_motion\n"); 4036 #endif 4037 4038 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget)); 4039 4040 /* Convert the coords to the main canvas window, or return if the 4041 * window is not found. */ 4042 if (!e_day_view_convert_event_coords ( 4043 day_view, (GdkEvent *) mevent, window, &event_x, &event_y)) 4044 return FALSE; 4045 4046 canvas_x = event_x; 4047 canvas_y = event_y; 4048 4049 pos = e_day_view_convert_position_in_main_canvas ( 4050 day_view, 4051 canvas_x, canvas_y, 4052 &day, &row, 4053 &event_num); 4054 if (event_num != -1) { 4055 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 4056 return FALSE; 4057 4058 event = &g_array_index (day_view->events[day], EDayViewEvent, 4059 event_num); 4060 } 4061 4062 if (day_view->selection_is_being_dragged) { 4063 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) { 4064 e_day_view_update_selection (day_view, day, row); 4065 e_day_view_check_auto_scroll ( 4066 day_view, 4067 event_x, event_y); 4068 return TRUE; 4069 } 4070 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { 4071 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) { 4072 e_day_view_update_resize (day_view, row); 4073 e_day_view_check_auto_scroll ( 4074 day_view, 4075 event_x, event_y); 4076 return TRUE; 4077 } 4078 } else if (day_view->pressed_event_day != -1 4079 && day_view->pressed_event_day != E_DAY_VIEW_LONG_EVENT) { 4080 GtkTargetList *target_list; 4081 4082 if ((abs (canvas_x - day_view->drag_event_x) 4083 > E_DAY_VIEW_DRAG_START_OFFSET 4084 || abs (canvas_y - day_view->drag_event_y)