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) 4085 > E_DAY_VIEW_DRAG_START_OFFSET)) { 4086 day_view->drag_event_day = day_view->pressed_event_day; 4087 day_view->drag_event_num = day_view->pressed_event_num; 4088 day_view->pressed_event_day = -1; 4089 4090 /* Hide the horizontal bars. */ 4091 if (day_view->resize_bars_event_day != -1) { 4092 day_view->resize_bars_event_day = -1; 4093 day_view->resize_bars_event_num = -1; 4094 } 4095 4096 target_list = gtk_target_list_new ( 4097 target_table, G_N_ELEMENTS (target_table)); 4098 e_target_list_add_calendar_targets (target_list, 0); 4099 gtk_drag_begin ( 4100 widget, target_list, 4101 GDK_ACTION_COPY | GDK_ACTION_MOVE, 4102 1, (GdkEvent *) mevent); 4103 gtk_target_list_unref (target_list); 4104 } 4105 } else { 4106 cursor = day_view->normal_cursor; 4107 4108 /* Check if the event is editable and client is not readonly while changing the cursor */ 4109 if (event && event->is_editable && is_comp_data_valid (event) && !e_client_is_readonly (E_CLIENT (event->comp_data->client))) { 4110 4111 switch (pos) { 4112 case E_CALENDAR_VIEW_POS_LEFT_EDGE: 4113 cursor = day_view->move_cursor; 4114 break; 4115 case E_CALENDAR_VIEW_POS_TOP_EDGE: 4116 case E_CALENDAR_VIEW_POS_BOTTOM_EDGE: 4117 cursor = day_view->resize_height_cursor; 4118 break; 4119 default: 4120 break; 4121 } 4122 } 4123 4124 /* Only set the cursor if it is different to last one set. */ 4125 if (day_view->last_cursor_set_in_main_canvas != cursor) { 4126 GdkWindow *window; 4127 4128 day_view->last_cursor_set_in_main_canvas = cursor; 4129 4130 window = gtk_widget_get_window (widget); 4131 gdk_window_set_cursor (window, cursor); 4132 } 4133 4134 if (event && E_IS_TEXT (event->canvas_item) && E_TEXT (event->canvas_item)->editing) { 4135 GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (event->canvas_item, (GdkEvent *) mevent); 4136 } 4137 } 4138 4139 return FALSE; 4140 } 4141 4142 /* This sets the selection to a single cell. If day is -1 then the current 4143 * start day is reused. If row is -1 then the selection is in the top canvas. 4144 */ 4145 void 4146 e_day_view_start_selection (EDayView *day_view, 4147 gint day, 4148 gint row) 4149 { 4150 if (day == -1) { 4151 day = day_view->selection_start_day; 4152 if (day == -1) 4153 day = 0; 4154 } 4155 4156 day_view->selection_start_day = day; 4157 day_view->selection_end_day = day; 4158 4159 day_view->selection_start_row = row; 4160 day_view->selection_end_row = row; 4161 4162 day_view->selection_is_being_dragged = TRUE; 4163 day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END; 4164 day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE; 4165 4166 /* FIXME: Optimise? */ 4167 gtk_widget_queue_draw (day_view->top_canvas); 4168 gtk_widget_queue_draw (day_view->main_canvas); 4169 } 4170 4171 /* Updates the selection during a drag. If day is -1 the selection day is 4172 * unchanged. */ 4173 void 4174 e_day_view_update_selection (EDayView *day_view, 4175 gint day, 4176 gint row) 4177 { 4178 gboolean need_redraw = FALSE; 4179 4180 #if 0 4181 g_print ("Updating selection %i,%i\n", day, row); 4182 #endif 4183 4184 day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE; 4185 4186 if (day == -1) 4187 day = (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) 4188 ? day_view->selection_start_day 4189 : day_view->selection_end_day; 4190 4191 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) { 4192 if (row != day_view->selection_start_row 4193 || day != day_view->selection_start_day) { 4194 need_redraw = TRUE; 4195 day_view->selection_start_row = row; 4196 day_view->selection_start_day = day; 4197 } 4198 } else { 4199 if (row != day_view->selection_end_row 4200 || day != day_view->selection_end_day) { 4201 need_redraw = TRUE; 4202 day_view->selection_end_row = row; 4203 day_view->selection_end_day = day; 4204 } 4205 } 4206 4207 e_day_view_normalize_selection (day_view); 4208 4209 /* FIXME: Optimise? */ 4210 if (need_redraw) { 4211 gtk_widget_queue_draw (day_view->top_canvas); 4212 gtk_widget_queue_draw (day_view->main_canvas); 4213 } 4214 } 4215 4216 static void 4217 e_day_view_normalize_selection (EDayView *day_view) 4218 { 4219 gint tmp_row, tmp_day; 4220 4221 /* Switch the drag position if necessary. */ 4222 if (day_view->selection_start_day > day_view->selection_end_day 4223 || (day_view->selection_start_day == day_view->selection_end_day 4224 && day_view->selection_start_row > day_view->selection_end_row)) { 4225 tmp_row = day_view->selection_start_row; 4226 tmp_day = day_view->selection_start_day; 4227 day_view->selection_start_day = day_view->selection_end_day; 4228 day_view->selection_start_row = day_view->selection_end_row; 4229 day_view->selection_end_day = tmp_day; 4230 day_view->selection_end_row = tmp_row; 4231 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) 4232 day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END; 4233 else 4234 day_view->selection_drag_pos = E_DAY_VIEW_DRAG_START; 4235 } 4236 } 4237 4238 void 4239 e_day_view_finish_selection (EDayView *day_view) 4240 { 4241 day_view->selection_is_being_dragged = FALSE; 4242 e_day_view_update_calendar_selection_time (day_view); 4243 } 4244 4245 static void 4246 e_day_view_update_long_event_resize (EDayView *day_view, 4247 gint day) 4248 { 4249 gint event_num; 4250 gboolean need_reshape = FALSE; 4251 4252 #if 0 4253 g_print ("Updating resize Day:%i\n", day); 4254 #endif 4255 4256 event_num = day_view->resize_event_num; 4257 4258 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE) { 4259 day = MIN (day, day_view->resize_end_row); 4260 if (day != day_view->resize_start_row) { 4261 need_reshape = TRUE; 4262 day_view->resize_start_row = day; 4263 4264 } 4265 } else { 4266 day = MAX (day, day_view->resize_start_row); 4267 if (day != day_view->resize_end_row) { 4268 need_reshape = TRUE; 4269 day_view->resize_end_row = day; 4270 } 4271 } 4272 4273 /* FIXME: Optimise? */ 4274 if (need_reshape) { 4275 e_day_view_reshape_long_event (day_view, event_num); 4276 gtk_widget_queue_draw (day_view->top_canvas); 4277 } 4278 } 4279 4280 static void 4281 e_day_view_update_resize (EDayView *day_view, 4282 gint row) 4283 { 4284 /* Same thing again? */ 4285 EDayViewEvent *event; 4286 gint day, event_num; 4287 gboolean need_reshape = FALSE; 4288 4289 #if 0 4290 g_print ("Updating resize Row:%i\n", row); 4291 #endif 4292 4293 if (day_view->resize_event_num == -1) 4294 return; 4295 4296 day = day_view->resize_event_day; 4297 event_num = day_view->resize_event_num; 4298 4299 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 4300 return; 4301 4302 event = &g_array_index (day_view->events[day], EDayViewEvent, 4303 event_num); 4304 4305 if (event && (!event->is_editable || !is_comp_data_valid (event) || e_client_is_readonly (E_CLIENT (event->comp_data->client)))) { 4306 return; 4307 } 4308 4309 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) { 4310 row = MIN (row, day_view->resize_end_row); 4311 if (row != day_view->resize_start_row) { 4312 need_reshape = TRUE; 4313 day_view->resize_start_row = row; 4314 4315 } 4316 } else { 4317 row = MAX (row, day_view->resize_start_row); 4318 if (row != day_view->resize_end_row) { 4319 need_reshape = TRUE; 4320 day_view->resize_end_row = row; 4321 } 4322 } 4323 4324 /* FIXME: Optimise? */ 4325 if (need_reshape) { 4326 e_day_view_reshape_day_event (day_view, day, event_num); 4327 e_day_view_reshape_main_canvas_resize_bars (day_view); 4328 gtk_widget_queue_draw (day_view->main_canvas); 4329 } 4330 } 4331 4332 /* This converts the resize start or end row back to a time and updates the 4333 * event. */ 4334 static void 4335 e_day_view_finish_long_event_resize (EDayView *day_view) 4336 { 4337 EDayViewEvent *event; 4338 gint event_num; 4339 ECalComponent *comp; 4340 ECalComponentDateTime date; 4341 struct icaltimetype itt; 4342 time_t dt; 4343 ECalModel *model; 4344 ECalClient *client; 4345 ESourceRegistry *registry; 4346 CalObjModType mod = CALOBJ_MOD_ALL; 4347 GtkWindow *toplevel; 4348 gint is_date; 4349 4350 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 4351 registry = e_cal_model_get_registry (model); 4352 4353 event_num = day_view->resize_event_num; 4354 4355 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 4356 return; 4357 4358 event = &g_array_index (day_view->long_events, EDayViewEvent, 4359 event_num); 4360 4361 if (!is_comp_data_valid (event)) 4362 return; 4363 4364 client = event->comp_data->client; 4365 4366 /* We use a temporary copy of the comp since we don't want to 4367 * change the original comp here. Otherwise we would not detect that 4368 * the event's time had changed in the "update_event" callback. */ 4369 comp = e_cal_component_new (); 4370 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 4371 4372 if (e_cal_component_has_attendees (comp) && 4373 !itip_organizer_is_user (registry, comp, client)) { 4374 g_object_unref (comp); 4375 e_day_view_abort_resize (day_view); 4376 return; 4377 } 4378 4379 date.value = &itt; 4380 date.tzid = NULL; 4381 4382 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE) { 4383 ECalComponentDateTime ecdt; 4384 4385 e_cal_component_get_dtstart (comp, &ecdt); 4386 is_date = ecdt.value && ecdt.value->is_date; 4387 if (!is_date) 4388 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4389 dt = day_view->day_starts[day_view->resize_start_row]; 4390 *date.value = icaltime_from_timet_with_zone (dt, is_date, 4391 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4392 cal_comp_set_dtstart_with_oldzone (client, comp, &date); 4393 e_cal_component_free_datetime (&ecdt); 4394 date.tzid = NULL; /* do not reuse it later */ 4395 } else { 4396 ECalComponentDateTime ecdt; 4397 4398 e_cal_component_get_dtend (comp, &ecdt); 4399 is_date = ecdt.value && ecdt.value->is_date; 4400 if (!is_date) 4401 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4402 dt = day_view->day_starts[day_view->resize_end_row + 1]; 4403 *date.value = icaltime_from_timet_with_zone (dt, is_date, 4404 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4405 cal_comp_set_dtend_with_oldzone (client, comp, &date); 4406 e_cal_component_free_datetime (&ecdt); 4407 date.tzid = NULL; /* do not reuse it later */ 4408 } 4409 4410 e_cal_component_commit_sequence (comp); 4411 if (e_cal_component_has_recurrences (comp)) { 4412 if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) { 4413 gtk_widget_queue_draw (day_view->top_canvas); 4414 goto out; 4415 } 4416 4417 if (mod == CALOBJ_MOD_ALL) 4418 comp_util_sanitize_recurrence_master (comp, client); 4419 4420 if (mod == CALOBJ_MOD_THIS) { 4421 /* set the correct DTSTART/DTEND on the individual recurrence */ 4422 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) { 4423 *date.value = icaltime_from_timet_with_zone ( 4424 event->comp_data->instance_end, FALSE, 4425 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4426 cal_comp_set_dtend_with_oldzone (client, comp, &date); 4427 } else { 4428 *date.value = icaltime_from_timet_with_zone ( 4429 event->comp_data->instance_start, FALSE, 4430 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4431 cal_comp_set_dtstart_with_oldzone (client, comp, &date); 4432 } 4433 4434 e_cal_component_set_rdate_list (comp, NULL); 4435 e_cal_component_set_rrule_list (comp, NULL); 4436 e_cal_component_set_exdate_list (comp, NULL); 4437 e_cal_component_set_exrule_list (comp, NULL); 4438 4439 e_cal_component_commit_sequence (comp); 4440 } 4441 } else if (e_cal_component_is_instance (comp)) 4442 mod = CALOBJ_MOD_THIS; 4443 4444 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view))); 4445 4446 e_calendar_view_modify_and_send ( 4447 E_CALENDAR_VIEW (day_view), 4448 comp, client, mod, toplevel, TRUE); 4449 4450 out: 4451 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE; 4452 4453 g_object_unref (comp); 4454 } 4455 4456 /* This converts the resize start or end row back to a time and updates the 4457 * event. */ 4458 static void 4459 e_day_view_finish_resize (EDayView *day_view) 4460 { 4461 EDayViewEvent *event; 4462 gint day, event_num; 4463 ECalComponent *comp; 4464 ECalComponentDateTime date; 4465 struct icaltimetype itt; 4466 time_t dt; 4467 ECalModel *model; 4468 ECalClient *client; 4469 ESourceRegistry *registry; 4470 CalObjModType mod = CALOBJ_MOD_ALL; 4471 GtkWindow *toplevel; 4472 4473 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 4474 registry = e_cal_model_get_registry (model); 4475 4476 if (day_view->resize_event_num == -1) 4477 return; 4478 4479 day = day_view->resize_event_day; 4480 event_num = day_view->resize_event_num; 4481 4482 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 4483 return; 4484 4485 event = &g_array_index (day_view->events[day], EDayViewEvent, 4486 event_num); 4487 4488 if (!is_comp_data_valid (event)) 4489 return; 4490 4491 client = event->comp_data->client; 4492 4493 /* We use a temporary shallow copy of the ico since we don't want to 4494 * change the original ico here. Otherwise we would not detect that 4495 * the event's time had changed in the "update_event" callback. */ 4496 comp = e_cal_component_new (); 4497 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 4498 4499 if (e_cal_component_has_attendees (comp) && 4500 !itip_organizer_is_user (registry, comp, client)) { 4501 g_object_unref (comp); 4502 e_day_view_abort_resize (day_view); 4503 return; 4504 } 4505 4506 date.value = &itt; 4507 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4508 4509 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) { 4510 dt = e_day_view_convert_grid_position_to_time (day_view, day, day_view->resize_start_row); 4511 *date.value = icaltime_from_timet_with_zone (dt, FALSE, 4512 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4513 cal_comp_set_dtstart_with_oldzone (client, comp, &date); 4514 } else { 4515 dt = e_day_view_convert_grid_position_to_time (day_view, day, day_view->resize_end_row + 1); 4516 *date.value = icaltime_from_timet_with_zone (dt, FALSE, 4517 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4518 cal_comp_set_dtend_with_oldzone (client, comp, &date); 4519 } 4520 4521 e_cal_component_commit_sequence (comp); 4522 4523 if (day_view->last_edited_comp_string != NULL) { 4524 g_free (day_view->last_edited_comp_string); 4525 day_view->last_edited_comp_string = NULL; 4526 } 4527 4528 day_view->last_edited_comp_string = e_cal_component_get_as_string (comp); 4529 4530 /* Hide the horizontal bars. */ 4531 day_view->resize_bars_event_day = -1; 4532 day_view->resize_bars_event_num = -1; 4533 4534 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE; 4535 4536 if (e_cal_component_has_recurrences (comp)) { 4537 if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) { 4538 gtk_widget_queue_draw (day_view->top_canvas); 4539 goto out; 4540 } 4541 4542 if (mod == CALOBJ_MOD_ALL) 4543 comp_util_sanitize_recurrence_master (comp, client); 4544 4545 if (mod == CALOBJ_MOD_THIS) { 4546 /* set the correct DTSTART/DTEND on the individual recurrence */ 4547 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) { 4548 *date.value = icaltime_from_timet_with_zone ( 4549 event->comp_data->instance_end, FALSE, 4550 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4551 cal_comp_set_dtend_with_oldzone (client, comp, &date); 4552 } else { 4553 *date.value = icaltime_from_timet_with_zone ( 4554 event->comp_data->instance_start, FALSE, 4555 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 4556 cal_comp_set_dtstart_with_oldzone (client, comp, &date); 4557 } 4558 4559 e_cal_component_set_rdate_list (comp, NULL); 4560 e_cal_component_set_rrule_list (comp, NULL); 4561 e_cal_component_set_exdate_list (comp, NULL); 4562 e_cal_component_set_exrule_list (comp, NULL); 4563 } 4564 } else if (e_cal_component_is_instance (comp)) 4565 mod = CALOBJ_MOD_THIS; 4566 4567 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view))); 4568 4569 e_cal_component_commit_sequence (comp); 4570 4571 e_calendar_view_modify_and_send ( 4572 E_CALENDAR_VIEW (day_view), 4573 comp, client, mod, toplevel, TRUE); 4574 4575 out: 4576 g_object_unref (comp); 4577 } 4578 4579 static void 4580 e_day_view_abort_resize (EDayView *day_view) 4581 { 4582 GdkWindow *window; 4583 gint day, event_num; 4584 4585 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_NONE) 4586 return; 4587 4588 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE; 4589 4590 day = day_view->resize_event_day; 4591 event_num = day_view->resize_event_num; 4592 4593 if (day == E_DAY_VIEW_LONG_EVENT) { 4594 e_day_view_reshape_long_event (day_view, event_num); 4595 gtk_widget_queue_draw (day_view->top_canvas); 4596 4597 day_view->last_cursor_set_in_top_canvas = day_view->normal_cursor; 4598 window = gtk_widget_get_window (day_view->top_canvas); 4599 gdk_window_set_cursor (window, day_view->normal_cursor); 4600 } else { 4601 e_day_view_reshape_day_event (day_view, day, event_num); 4602 e_day_view_reshape_main_canvas_resize_bars (day_view); 4603 gtk_widget_queue_draw (day_view->main_canvas); 4604 4605 day_view->last_cursor_set_in_main_canvas = day_view->normal_cursor; 4606 window = gtk_widget_get_window (day_view->main_canvas); 4607 gdk_window_set_cursor (window, day_view->normal_cursor); 4608 } 4609 } 4610 4611 static void 4612 e_day_view_free_events (EDayView *day_view) 4613 { 4614 gint day; 4615 4616 /* Reset all our indices. */ 4617 day_view->editing_event_day = -1; 4618 day_view->popup_event_day = -1; 4619 day_view->resize_bars_event_day = -1; 4620 day_view->resize_event_day = -1; 4621 day_view->pressed_event_day = -1; 4622 day_view->drag_event_day = -1; 4623 day_view->editing_event_num = -1; 4624 day_view->popup_event_num = -1; 4625 4626 e_day_view_free_event_array (day_view, day_view->long_events); 4627 4628 for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) 4629 e_day_view_free_event_array (day_view, day_view->events[day]); 4630 } 4631 4632 static void 4633 e_day_view_free_event_array (EDayView *day_view, 4634 GArray *array) 4635 { 4636 EDayViewEvent *event; 4637 gint event_num; 4638 4639 for (event_num = 0; event_num < array->len; event_num++) { 4640 event = &g_array_index (array, EDayViewEvent, event_num); 4641 if (event->canvas_item) 4642 g_object_run_dispose (G_OBJECT (event->canvas_item)); 4643 4644 if (is_comp_data_valid (event)) 4645 g_object_unref (event->comp_data); 4646 } 4647 4648 g_array_set_size (array, 0); 4649 } 4650 4651 /* This adds one event to the view, adding it to the appropriate array. */ 4652 static gboolean 4653 e_day_view_add_event (ESourceRegistry *registry, 4654 ECalComponent *comp, 4655 time_t start, 4656 time_t end, 4657 gpointer data) 4658 4659 { 4660 EDayViewEvent event; 4661 gint day, offset; 4662 struct icaltimetype start_tt, end_tt; 4663 AddEventData *add_event_data; 4664 4665 add_event_data = data; 4666 4667 /*if (end < start || start >= add_event_data->day_view->upper || end < add_event_data->day_view->lower) { 4668 g_print ("%s: day_view: %p\n", G_STRFUNC, add_event_data->day_view); 4669 g_print ("\tDay view lower: %s", ctime (&add_event_data->day_view->lower)); 4670 g_print ("\tDay view upper: %s", ctime (&add_event_data->day_view->upper)); 4671 g_print ("\tEvent start: %s", ctime (&start)); 4672 g_print ("\tEvent end : %s\n", ctime (&end)); 4673 }*/ 4674 4675 /* Check that the event times are valid. */ 4676 g_return_val_if_fail (start <= end, TRUE); 4677 g_return_val_if_fail (start < add_event_data->day_view->upper, TRUE); 4678 g_return_val_if_fail (end > add_event_data->day_view->lower, TRUE); 4679 4680 start_tt = icaltime_from_timet_with_zone ( 4681 start, FALSE, 4682 e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->day_view))); 4683 end_tt = icaltime_from_timet_with_zone ( 4684 end, FALSE, 4685 e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->day_view))); 4686 4687 if (add_event_data->comp_data) { 4688 event.comp_data = g_object_ref (add_event_data->comp_data); 4689 } else { 4690 event.comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL); 4691 4692 event.comp_data->client = g_object_ref (e_cal_model_get_default_client (e_calendar_view_get_model (E_CALENDAR_VIEW (add_event_data->day_view)))); 4693 e_cal_component_abort_sequence (comp); 4694 event.comp_data->icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp)); 4695 } 4696 4697 event.start = start; 4698 event.tooltip = NULL; 4699 event.color = NULL; 4700 event.timeout = -1; 4701 event.end = end; 4702 event.canvas_item = NULL; 4703 event.comp_data->instance_start = start; 4704 event.comp_data->instance_end = end; 4705 4706 /* Calculate the start & end minute, relative to the top of the 4707 * display. */ 4708 offset = add_event_data->day_view->first_hour_shown * 60 4709 + add_event_data->day_view->first_minute_shown; 4710 event.start_minute = start_tt.hour * 60 + start_tt.minute - offset; 4711 event.end_minute = end_tt.hour * 60 + end_tt.minute - offset; 4712 4713 event.start_row_or_col = 0; 4714 event.num_columns = 0; 4715 4716 event.different_timezone = FALSE; 4717 if (!cal_comp_util_compare_event_timezones (comp, 4718 event.comp_data->client, 4719 e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->day_view)))) 4720 event.different_timezone = TRUE; 4721 4722 if (!e_cal_component_has_attendees (comp) || 4723 itip_organizer_is_user (registry, comp, event.comp_data->client) || 4724 itip_sentby_is_user (registry, comp, event.comp_data->client)) 4725 event.is_editable = TRUE; 4726 else 4727 event.is_editable = FALSE; 4728 4729 /* Find out which array to add the event to. */ 4730 for (day = 0; day < add_event_data->day_view->days_shown; day++) { 4731 if (start >= add_event_data->day_view->day_starts[day] 4732 && end <= add_event_data->day_view->day_starts[day + 1]) { 4733 4734 /* Special case for when the appointment ends at 4735 * midnight, i.e. the start of the next day. */ 4736 if (end == add_event_data->day_view->day_starts[day + 1]) { 4737 4738 /* If the event last the entire day, then we 4739 * skip it here so it gets added to the top 4740 * canvas. */ 4741 if (start == add_event_data->day_view->day_starts[day]) 4742 break; 4743 4744 event.end_minute = 24 * 60; 4745 } 4746 4747 g_array_append_val (add_event_data->day_view->events[day], event); 4748 add_event_data->day_view->events_sorted[day] = FALSE; 4749 add_event_data->day_view->need_layout[day] = TRUE; 4750 return TRUE; 4751 } 4752 } 4753 4754 /* The event wasn't within one day so it must be a long event, 4755 * i.e. shown in the top canvas. */ 4756 g_array_append_val (add_event_data->day_view->long_events, event); 4757 add_event_data->day_view->long_events_sorted = FALSE; 4758 add_event_data->day_view->long_events_need_layout = TRUE; 4759 return TRUE; 4760 } 4761 4762 /* This lays out the short (less than 1 day) events in the columns. 4763 * Any long events are simply skipped. */ 4764 void 4765 e_day_view_check_layout (EDayView *day_view) 4766 { 4767 ECalendarView *cal_view; 4768 gint time_divisions; 4769 gint day, rows_in_top_display; 4770 gint max_cols = -1; 4771 4772 cal_view = E_CALENDAR_VIEW (day_view); 4773 time_divisions = e_calendar_view_get_time_divisions (cal_view); 4774 4775 /* Don't bother if we aren't visible. */ 4776 if (!E_CALENDAR_VIEW (day_view)->in_focus) { 4777 e_day_view_free_events (day_view); 4778 day_view->requires_update = TRUE; 4779 return; 4780 } 4781 4782 /* Make sure the events are sorted (by start and size). */ 4783 e_day_view_ensure_events_sorted (day_view); 4784 4785 for (day = 0; day < day_view->days_shown; day++) { 4786 if (day_view->need_layout[day]) { 4787 gint cols; 4788 4789 cols = e_day_view_layout_day_events ( 4790 day_view->events[day], 4791 day_view->rows, 4792 time_divisions, 4793 day_view->cols_per_row[day], 4794 day_view->days_shown == 1 ? -1 : 4795 E_DAY_VIEW_MULTI_DAY_MAX_COLUMNS); 4796 4797 max_cols = MAX (cols, max_cols); 4798 } 4799 4800 if (day_view->need_layout[day] 4801 || day_view->need_reshape[day]) { 4802 e_day_view_reshape_day_events (day_view, day); 4803 4804 if (day_view->resize_bars_event_day == day) 4805 e_day_view_reshape_main_canvas_resize_bars (day_view); 4806 } 4807 4808 day_view->need_layout[day] = FALSE; 4809 day_view->need_reshape[day] = FALSE; 4810 } 4811 4812 if (day_view->long_events_need_layout) { 4813 e_day_view_layout_long_events ( 4814 day_view->long_events, 4815 day_view->days_shown, 4816 day_view->day_starts, 4817 &rows_in_top_display); 4818 } 4819 4820 if (day_view->long_events_need_layout 4821 || day_view->long_events_need_reshape) 4822 e_day_view_reshape_long_events (day_view); 4823 4824 if (day_view->long_events_need_layout 4825 && day_view->rows_in_top_display != rows_in_top_display) {
The right operand of '!=' is a garbage value
(emitted by clang-analyzer)

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

The right operand of '!=' is a garbage value
(emitted by clang-analyzer)

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

4826 day_view->rows_in_top_display = rows_in_top_display; 4827 e_day_view_update_top_scroll (day_view, FALSE); 4828 } 4829 4830 day_view->long_events_need_layout = FALSE; 4831 day_view->long_events_need_reshape = FALSE; 4832 4833 if (max_cols != -1 && max_cols != day_view->max_cols) { 4834 day_view->max_cols = max_cols; 4835 e_day_view_recalc_main_canvas_size (day_view); 4836 } 4837 } 4838 4839 static void 4840 e_day_view_reshape_long_events (EDayView *day_view) 4841 { 4842 EDayViewEvent *event; 4843 gint event_num; 4844 4845 for (event_num = 0; event_num < day_view->long_events->len; 4846 event_num++) { 4847 event = &g_array_index (day_view->long_events, EDayViewEvent, 4848 event_num); 4849 4850 if (event->num_columns == 0) { 4851 if (event->canvas_item) { 4852 g_object_run_dispose (G_OBJECT (event->canvas_item)); 4853 event->canvas_item = NULL; 4854 } 4855 } else { 4856 e_day_view_reshape_long_event (day_view, event_num); 4857 } 4858 } 4859 } 4860 4861 static void 4862 e_day_view_reshape_long_event (EDayView *day_view, 4863 gint event_num) 4864 { 4865 EDayViewEvent *event; 4866 gint start_day, end_day, item_x, item_y, item_w, item_h; 4867 gint text_x, text_w, num_icons, icons_width, width, time_width; 4868 ECalComponent *comp; 4869 gint min_text_x, max_text_w, text_width, line_len; 4870 gchar *text, *end_of_line; 4871 gboolean show_icons = TRUE, use_max_width = FALSE; 4872 PangoContext *pango_context; 4873 PangoLayout *layout; 4874 4875 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 4876 return; 4877 4878 event = &g_array_index (day_view->long_events, EDayViewEvent, 4879 event_num); 4880 4881 if (!e_day_view_get_long_event_position (day_view, event_num, 4882 &start_day, &end_day, 4883 &item_x, &item_y, 4884 &item_w, &item_h)) { 4885 if (event->canvas_item) { 4886 g_object_run_dispose (G_OBJECT (event->canvas_item)); 4887 event->canvas_item = NULL; 4888 } 4889 return; 4890 } 4891 4892 if (!is_comp_data_valid (event)) 4893 return; 4894 4895 /* Take off the border and padding. */ 4896 item_x += E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD; 4897 item_w -= (E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD) * 2; 4898 item_y += E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD; 4899 item_h -= (E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD) * 2; 4900 4901 /* We don't show the icons while resizing, since we'd have to 4902 * draw them on top of the resize rect. Nor when editing. */ 4903 num_icons = 0; 4904 comp = e_cal_component_new (); 4905 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 4906 4907 /* Set up Pango prerequisites */ 4908 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (day_view)); 4909 layout = pango_layout_new (pango_context); 4910 4911 if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE 4912 && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT 4913 && day_view->resize_event_num == event_num) 4914 show_icons = FALSE; 4915 4916 if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT 4917 && day_view->editing_event_num == event_num) { 4918 show_icons = FALSE; 4919 use_max_width = TRUE; 4920 } 4921 4922 if (show_icons) { 4923 if (e_cal_component_has_alarms (comp)) 4924 num_icons++; 4925 if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp)) 4926 num_icons++; 4927 if (event->different_timezone) 4928 num_icons++; 4929 4930 if (e_cal_component_has_attendees (comp)) 4931 num_icons++; 4932 if (e_cal_component_has_attachments (comp)) 4933 num_icons++; 4934 num_icons += cal_comp_util_get_n_icons (comp, NULL); 4935 } 4936 4937 if (!event->canvas_item) { 4938 GtkWidget *widget; 4939 GdkColor color; 4940 4941 widget = (GtkWidget *) day_view; 4942 4943 color = e_day_view_get_text_color (day_view, event, widget); 4944 4945 event->canvas_item = 4946 gnome_canvas_item_new ( 4947 GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_canvas)->root), 4948 e_text_get_type (), 4949 "clip", TRUE, 4950 "max_lines", 1, 4951 "editable", TRUE, 4952 "use_ellipsis", TRUE, 4953 "fill_color_gdk", &color, 4954 "im_context", E_CANVAS (day_view->top_canvas)->im_context, 4955 NULL); 4956 g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num)); 4957 g_object_set_data (G_OBJECT (event->canvas_item), "event-day", GINT_TO_POINTER (E_DAY_VIEW_LONG_EVENT)); 4958 g_signal_connect ( 4959 event->canvas_item, "event", 4960 G_CALLBACK (e_day_view_on_text_item_event), day_view); 4961 g_signal_emit_by_name (day_view, "event_added", event); 4962 4963 e_day_view_update_long_event_label (day_view, event_num); 4964 } 4965 4966 /* Calculate its position. We first calculate the ideal position which 4967 * is centered with the icons. We then make sure we haven't gone off 4968 * the left edge of the available space. Finally we make sure we don't 4969 * go off the right edge. */ 4970 icons_width = (E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD) 4971 * num_icons + E_DAY_VIEW_LONG_EVENT_ICON_R_PAD; 4972 time_width = e_day_view_get_time_string_width (day_view); 4973 4974 if (use_max_width) { 4975 text_x = item_x; 4976 text_w = item_w; 4977 } else { 4978 /* Get the requested size of the label. */ 4979 g_object_get (event->canvas_item, "text", &text, NULL); 4980 text_width = 0; 4981 if (text) { 4982 end_of_line = strchr (text, '\n'); 4983 if (end_of_line) 4984 line_len = end_of_line - text; 4985 else 4986 line_len = strlen (text); 4987 pango_layout_set_text (layout, text, line_len); 4988 pango_layout_get_pixel_size (layout, &text_width, NULL); 4989 g_free (text); 4990 } 4991 4992 width = text_width + icons_width; 4993 text_x = item_x + (item_w - width) / 2; 4994 4995 min_text_x = item_x; 4996 if (event->start > day_view->day_starts[start_day]) 4997 min_text_x += time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD; 4998 4999 text_x = MAX (text_x, min_text_x); 5000 5001 max_text_w = item_x + item_w - text_x; 5002 if (event->end < day_view->day_starts[end_day + 1]) 5003 max_text_w -= time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD; 5004 5005 text_w = MIN (width, max_text_w); 5006 5007 /* Now take out the space for the icons. */ 5008 text_x += icons_width; 5009 text_w -= icons_width; 5010 } 5011 5012 text_w = MAX (text_w, 0); 5013 gnome_canvas_item_set ( 5014 event->canvas_item, 5015 "clip_width", (gdouble) text_w, 5016 "clip_height", (gdouble) item_h, 5017 NULL); 5018 e_canvas_item_move_absolute ( 5019 event->canvas_item, 5020 text_x, item_y); 5021 5022 g_object_unref (layout); 5023 g_object_unref (comp); 5024 } 5025 5026 /* This creates or updates the sizes of the canvas items for one day of the 5027 * main canvas. */ 5028 static void 5029 e_day_view_reshape_day_events (EDayView *day_view, 5030 gint day) 5031 { 5032 gint event_num; 5033 5034 for (event_num = 0; event_num < day_view->events[day]->len; 5035 event_num++) { 5036 EDayViewEvent *event; 5037 gchar *current_comp_string; 5038 5039 e_day_view_reshape_day_event (day_view, day, event_num); 5040 event = &g_array_index (day_view->events[day], EDayViewEvent, event_num); 5041 5042 if (!is_comp_data_valid (event)) 5043 continue; 5044 5045 current_comp_string = icalcomponent_as_ical_string_r (event->comp_data->icalcomp); 5046 if (day_view->last_edited_comp_string == NULL) { 5047 g_free (current_comp_string); 5048 continue; 5049 } 5050 5051 if (strncmp (current_comp_string, day_view->last_edited_comp_string,50) == 0) { 5052 e_canvas_item_grab_focus (event->canvas_item, TRUE); 5053 g_free (day_view->last_edited_comp_string); 5054 day_view-> last_edited_comp_string = NULL; 5055 } 5056 g_free (current_comp_string); 5057 } 5058 } 5059 5060 static void 5061 e_day_view_reshape_day_event (EDayView *day_view, 5062 gint day, 5063 gint event_num) 5064 { 5065 EDayViewEvent *event; 5066 gint item_x, item_y, item_w, item_h; 5067 gint num_icons, icons_offset; 5068 5069 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 5070 return; 5071 5072 event = &g_array_index (day_view->events[day], EDayViewEvent, 5073 event_num); 5074 5075 if (!e_day_view_get_event_position (day_view, day, event_num, 5076 &item_x, &item_y, 5077 &item_w, &item_h)) { 5078 if (event->canvas_item) { 5079 g_object_run_dispose (G_OBJECT (event->canvas_item)); 5080 event->canvas_item = NULL; 5081 } 5082 } else { 5083 /* Skip the border and padding. */ 5084 item_x += E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD; 5085 item_w -= E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD * 2; 5086 item_y += E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD; 5087 item_h -= (E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD) * 2; 5088 5089 /* We don't show the icons while resizing, since we'd have to 5090 * draw them on top of the resize rect. */ 5091 icons_offset = 0; 5092 num_icons = 0; 5093 if (is_comp_data_valid (event) && (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_NONE 5094 || day_view->resize_event_day != day 5095 || day_view->resize_event_num != event_num)) { 5096 ECalComponent *comp; 5097 5098 comp = e_cal_component_new (); 5099 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 5100 5101 if (e_cal_component_has_alarms (comp)) 5102 num_icons++; 5103 if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp)) 5104 num_icons++; 5105 if (e_cal_component_has_attachments (comp)) 5106 num_icons++; 5107 if (event->different_timezone) 5108 num_icons++; 5109 if (e_cal_component_has_attendees (comp)) 5110 num_icons++; 5111 5112 num_icons += cal_comp_util_get_n_icons (comp, NULL); 5113 g_object_unref (comp); 5114 } 5115 5116 if (num_icons > 0) { 5117 if (item_h >= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD) * num_icons) 5118 icons_offset = E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD * 2; 5119 else if (item_h <= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD) * 2 || num_icons == 1) 5120 icons_offset = (E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD) * num_icons + E_DAY_VIEW_ICON_X_PAD; 5121 else 5122 icons_offset = E_DAY_VIEW_ICON_X_PAD; 5123 } 5124 5125 if (!event->canvas_item) { 5126 GtkWidget *widget; 5127 GdkColor color; 5128 5129 widget = (GtkWidget *) day_view; 5130 5131 color = e_day_view_get_text_color (day_view, event, widget); 5132 5133 event->canvas_item = gnome_canvas_item_new ( 5134 GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->main_canvas)->root), 5135 e_text_get_type (), 5136 "line_wrap", TRUE, 5137 "editable", TRUE, 5138 "clip", TRUE, 5139 "use_ellipsis", TRUE, 5140 "fill_color_gdk", &color, 5141 "im_context", E_CANVAS (day_view->main_canvas)->im_context, 5142 NULL); 5143 g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num)); 5144 g_object_set_data (G_OBJECT (event->canvas_item), "event-day", GINT_TO_POINTER (day)); 5145 g_signal_connect ( 5146 event->canvas_item, "event", 5147 G_CALLBACK (e_day_view_on_text_item_event), day_view); 5148 g_signal_emit_by_name (day_view, "event_added", event); 5149 5150 e_day_view_update_event_label (day_view, day, event_num); 5151 } 5152 5153 item_w = MAX (item_w, 0); 5154 gnome_canvas_item_set ( 5155 event->canvas_item, 5156 "clip_width", (gdouble) item_w, 5157 "clip_height", (gdouble) item_h, 5158 "x_offset", (gdouble) icons_offset, 5159 NULL); 5160 e_canvas_item_move_absolute ( 5161 event->canvas_item, 5162 item_x, item_y); 5163 } 5164 } 5165 5166 /* This creates or resizes the horizontal bars used to resize events in the 5167 * main canvas. */ 5168 static void 5169 e_day_view_reshape_main_canvas_resize_bars (EDayView *day_view) 5170 { 5171 gint day, event_num; 5172 gint item_x, item_y, item_w, item_h; 5173 gdouble x, y, w, h; 5174 5175 day = day_view->resize_bars_event_day; 5176 event_num = day_view->resize_bars_event_num; 5177 5178 /* If we're not editing an event, or the event is not shown, 5179 * hide the resize bars. */ 5180 if (day != -1 && day == day_view->drag_event_day 5181 && event_num == day_view->drag_event_num) { 5182 g_object_get ( 5183 day_view->drag_rect_item, 5184 "x1", &x, 5185 "y1", &y, 5186 "x2", &w, 5187 "y2", &h, 5188 NULL); 5189 w -= x; 5190 x++; 5191 h -= y; 5192 } else if (day != -1 5193 && e_day_view_get_event_position (day_view, day, event_num, 5194 &item_x, &item_y, 5195 &item_w, &item_h)) { 5196 x = item_x + E_DAY_VIEW_BAR_WIDTH; 5197 y = item_y; 5198 w = item_w - E_DAY_VIEW_BAR_WIDTH; 5199 h = item_h; 5200 5201 gtk_widget_queue_draw (day_view->main_canvas); 5202 } else { 5203 return; 5204 } 5205 } 5206 5207 static void 5208 e_day_view_ensure_events_sorted (EDayView *day_view) 5209 { 5210 gint day; 5211 5212 /* Sort the long events. */ 5213 if (!day_view->long_events_sorted) { 5214 qsort ( 5215 day_view->long_events->data, 5216 day_view->long_events->len, 5217 sizeof (EDayViewEvent), 5218 e_day_view_event_sort_func); 5219 day_view->long_events_sorted = TRUE; 5220 } 5221 5222 /* Sort the events for each day. */ 5223 for (day = 0; day < day_view->days_shown; day++) { 5224 if (!day_view->events_sorted[day]) { 5225 qsort ( 5226 day_view->events[day]->data, 5227 day_view->events[day]->len, 5228 sizeof (EDayViewEvent), 5229 e_day_view_event_sort_func); 5230 day_view->events_sorted[day] = TRUE; 5231 } 5232 } 5233 } 5234 5235 gint 5236 e_day_view_event_sort_func (gconstpointer arg1, 5237 gconstpointer arg2) 5238 { 5239 EDayViewEvent *event1, *event2; 5240 5241 event1 = (EDayViewEvent *) arg1; 5242 event2 = (EDayViewEvent *) arg2; 5243 5244 if (event1->start < event2->start) 5245 return -1; 5246 if (event1->start > event2->start) 5247 return 1; 5248 5249 if (event1->end > event2->end) 5250 return -1; 5251 if (event1->end < event2->end) 5252 return 1; 5253 5254 return 0; 5255 } 5256 5257 static gboolean 5258 e_day_view_add_new_event_in_selected_range (EDayView *day_view, 5259 GdkEventKey *key_event) 5260 { 5261 icalcomponent *icalcomp; 5262 ECalClient *client; 5263 ECalModel *model; 5264 ECalComponent *comp; 5265 gint day, event_num; 5266 time_t dtstart, dtend; 5267 ECalComponentDateTime start_dt, end_dt; 5268 struct icaltimetype start_tt, end_tt; 5269 const gchar *uid; 5270 AddEventData add_event_data; 5271 ESourceRegistry *registry; 5272 5273 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 5274 5275 registry = e_cal_model_get_registry (model); 5276 client = e_cal_model_get_default_client (model); 5277 5278 /* Check if the client is read only */ 5279 if (e_client_is_readonly (E_CLIENT (client))) 5280 return FALSE; 5281 5282 icalcomp = e_cal_model_create_component_with_defaults (model, day_view->selection_in_top_canvas); 5283 if (!icalcomp) 5284 return FALSE; 5285 5286 uid = icalcomponent_get_uid (icalcomp); 5287 5288 comp = e_cal_component_new (); 5289 e_cal_component_set_icalcomponent (comp, icalcomp); 5290 5291 e_day_view_get_selected_time_range ((ECalendarView *) day_view, &dtstart, &dtend); 5292 5293 start_tt = icaltime_from_timet_with_zone ( 5294 dtstart, FALSE, 5295 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 5296 5297 end_tt = icaltime_from_timet_with_zone ( 5298 dtend, FALSE, 5299 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 5300 5301 if (day_view->selection_in_top_canvas) { 5302 start_dt.tzid = NULL; 5303 start_tt.is_date = 1; 5304 end_tt.is_date = 1; 5305 5306 /* Editor default in day/work-week view - top canvas */ 5307 e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT); 5308 } else { 5309 start_dt.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 5310 5311 /* Editor default in day/work-week view - main canvas */ 5312 e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_OPAQUE); 5313 } 5314 5315 start_dt.value = &start_tt; 5316 end_dt.value = &end_tt; 5317 end_dt.tzid = start_dt.tzid; 5318 e_cal_component_set_dtstart (comp, &start_dt); 5319 e_cal_component_set_dtend (comp, &end_dt); 5320 5321 e_cal_component_set_categories ( 5322 comp, e_calendar_view_get_default_category (E_CALENDAR_VIEW (day_view))); 5323 5324 /* We add the event locally and start editing it. We don't send it 5325 * to the server until the user finishes editing it. */ 5326 add_event_data.day_view = day_view; 5327 add_event_data.comp_data = NULL; 5328 e_day_view_add_event (registry, comp, dtstart, dtend, &add_event_data); 5329 e_day_view_check_layout (day_view); 5330 gtk_widget_queue_draw (day_view->top_canvas); 5331 gtk_widget_queue_draw (day_view->main_canvas); 5332 5333 if (!e_day_view_find_event_from_uid (day_view, client, uid, NULL, &day, &event_num)) { 5334 g_warning ("Couldn't find event to start editing.\n"); 5335 g_object_unref (comp); 5336 return FALSE; 5337 } 5338 5339 e_day_view_start_editing_event (day_view, day, event_num, key_event); 5340 5341 g_object_unref (comp); 5342 return TRUE; 5343 } 5344 5345 static gboolean 5346 e_day_view_do_key_press (GtkWidget *widget, 5347 GdkEventKey *event) 5348 { 5349 EDayView *day_view; 5350 guint keyval; 5351 gboolean stop_emission; 5352 5353 g_return_val_if_fail (widget != NULL, FALSE); 5354 g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE); 5355 g_return_val_if_fail (event != NULL, FALSE); 5356 5357 day_view = E_DAY_VIEW (widget); 5358 keyval = event->keyval; 5359 5360 /* The Escape key aborts a resize operation. */ 5361 if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { 5362 if (keyval == GDK_KEY_Escape) { 5363 gdk_pointer_ungrab (event->time); 5364 e_day_view_abort_resize (day_view); 5365 } 5366 return FALSE; 5367 } 5368 5369 /* Alt + Arrow Keys to move a selected event through time lines */ 5370 if (((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK) 5371 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK) 5372 &&((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK)) { 5373 if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) 5374 return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_UP); 5375 else if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) 5376 return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_DOWN); 5377 else if (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left) 5378 return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_LEFT); 5379 else if (keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right) 5380 return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_RIGHT); 5381 } 5382 5383 /*Go to the start/end of a work day*/ 5384 if ((keyval == GDK_KEY_Home) 5385 &&((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK) 5386 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK) 5387 &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) { 5388 e_day_view_goto_start_of_work_day (day_view); 5389 return TRUE; 5390 } 5391 if ((keyval == GDK_KEY_End) 5392 &&((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK) 5393 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK) 5394 &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) { 5395 e_day_view_goto_end_of_work_day (day_view); 5396 return TRUE; 5397 } 5398 5399 /* In DayView, Shift+Home/End, Change the duration to the time that begins/ends the current work day */ 5400 if ((keyval == GDK_KEY_Home) 5401 &&((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) 5402 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK) 5403 &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) { 5404 e_day_view_change_duration_to_start_of_work_day (day_view); 5405 return TRUE; 5406 } 5407 if ((keyval == GDK_KEY_End) 5408 &&((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) 5409 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK) 5410 &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) { 5411 e_day_view_change_duration_to_end_of_work_day (day_view); 5412 return TRUE; 5413 } 5414 5415 /* Handle the cursor keys for moving & extending the selection. */ 5416 stop_emission = TRUE; 5417 if (event->state & GDK_SHIFT_MASK) { 5418 switch (keyval) { 5419 case GDK_KEY_Up: 5420 e_day_view_cursor_key_up_shifted (day_view, event); 5421 break; 5422 case GDK_KEY_Down: 5423 e_day_view_cursor_key_down_shifted (day_view, event); 5424 break; 5425 case GDK_KEY_Left: 5426 e_day_view_cursor_key_left_shifted (day_view, event); 5427 break; 5428 case GDK_KEY_Right: 5429 e_day_view_cursor_key_right_shifted (day_view, event); 5430 break; 5431 default: 5432 stop_emission = FALSE; 5433 break; 5434 } 5435 } else if (!(event->state & GDK_MOD1_MASK)) { 5436 switch (keyval) { 5437 case GDK_KEY_Up: 5438 e_day_view_cursor_key_up (day_view, event); 5439 break; 5440 case GDK_KEY_Down: 5441 e_day_view_cursor_key_down (day_view, event); 5442 break; 5443 case GDK_KEY_Left: 5444 e_day_view_cursor_key_left (day_view, event); 5445 break; 5446 case GDK_KEY_Right: 5447 e_day_view_cursor_key_right (day_view, event); 5448 break; 5449 case GDK_KEY_Page_Up: 5450 e_day_view_scroll (day_view, E_DAY_VIEW_PAGE_STEP); 5451 break; 5452 case GDK_KEY_Page_Down: 5453 e_day_view_scroll (day_view, -E_DAY_VIEW_PAGE_STEP); 5454 break; 5455 default: 5456 stop_emission = FALSE; 5457 break; 5458 } 5459 } 5460 else 5461 stop_emission = FALSE; 5462 if (stop_emission) 5463 return TRUE; 5464 5465 if (day_view->selection_start_day == -1) 5466 return FALSE; 5467 5468 /* We only want to start an edit with a return key or a simple 5469 * character. */ 5470 if ((keyval != GDK_KEY_Return) && 5471 (((keyval >= 0x20) && (keyval <= 0xFF) 5472 && (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))) 5473 || (event->length == 0) 5474 || (keyval == GDK_KEY_Tab))) { 5475 return FALSE; 5476 } 5477 5478 return e_day_view_add_new_event_in_selected_range (day_view, event); 5479 } 5480 5481 static gboolean 5482 e_day_view_key_press (GtkWidget *widget, 5483 GdkEventKey *event) 5484 { 5485 gboolean handled = FALSE; 5486 handled = e_day_view_do_key_press (widget, event); 5487 5488 /* if not handled, try key bindings */ 5489 if (!handled) 5490 handled = GTK_WIDGET_CLASS (e_day_view_parent_class)->key_press_event (widget, event); 5491 return handled; 5492 } 5493 5494 /* Select the time that begins a work day*/ 5495 static void 5496 e_day_view_goto_start_of_work_day (EDayView *day_view) 5497 { 5498 ECalModel *model; 5499 gint work_day_start_hour; 5500 gint work_day_start_minute; 5501 5502 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 5503 work_day_start_hour = e_cal_model_get_work_day_start_hour (model); 5504 work_day_start_minute = e_cal_model_get_work_day_start_minute (model); 5505 5506 if (day_view->selection_in_top_canvas) 5507 return; 5508 else 5509 day_view->selection_start_row = 5510 e_day_view_convert_time_to_row ( 5511 day_view, work_day_start_hour, work_day_start_minute); 5512 day_view->selection_end_row = day_view->selection_start_row; 5513 5514 e_day_view_ensure_rows_visible ( 5515 day_view, 5516 day_view->selection_start_row, 5517 day_view->selection_end_row); 5518 5519 e_day_view_update_calendar_selection_time (day_view); 5520 5521 gtk_widget_queue_draw (day_view->top_canvas); 5522 gtk_widget_queue_draw (day_view->top_dates_canvas); 5523 gtk_widget_queue_draw (day_view->main_canvas); 5524 } 5525 5526 /* Select the time that ends a work day*/ 5527 static void 5528 e_day_view_goto_end_of_work_day (EDayView *day_view) 5529 { 5530 ECalModel *model; 5531 gint work_day_end_hour; 5532 gint work_day_end_minute; 5533 5534 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 5535 work_day_end_hour = e_cal_model_get_work_day_end_hour (model); 5536 work_day_end_minute = e_cal_model_get_work_day_end_minute (model); 5537 5538 if (day_view->selection_in_top_canvas) 5539 return; 5540 else 5541 day_view->selection_start_row = 5542 e_day_view_convert_time_to_row ( 5543 day_view, work_day_end_hour - 1, work_day_end_minute + 30); 5544 day_view->selection_end_row = day_view->selection_start_row; 5545 5546 e_day_view_ensure_rows_visible ( 5547 day_view, 5548 day_view->selection_start_row, 5549 day_view->selection_end_row); 5550 5551 e_day_view_update_calendar_selection_time (day_view); 5552 5553 gtk_widget_queue_draw (day_view->top_canvas); 5554 gtk_widget_queue_draw (day_view->top_dates_canvas); 5555 gtk_widget_queue_draw (day_view->main_canvas); 5556 } 5557 5558 /* Change the duration to the time that begins the current work day */ 5559 static void 5560 e_day_view_change_duration_to_start_of_work_day (EDayView *day_view) 5561 { 5562 ECalModel *model; 5563 gint work_day_start_hour; 5564 gint work_day_start_minute; 5565 5566 g_return_if_fail (day_view != NULL); 5567 5568 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 5569 work_day_start_hour = e_cal_model_get_work_day_start_hour (model); 5570 work_day_start_minute = e_cal_model_get_work_day_start_minute (model); 5571 5572 if (day_view->selection_in_top_canvas) 5573 return; 5574 else { 5575 /* These are never used after being set? */ 5576 gint work_start_row,selection_start_row; 5577 5578 work_start_row = e_day_view_convert_time_to_row ( 5579 day_view, work_day_start_hour, work_day_start_minute); 5580 selection_start_row = day_view->selection_start_row; 5581 if (selection_start_row < work_start_row) 5582 day_view->selection_end_row = work_start_row - 1; 5583 else day_view->selection_start_row = work_start_row; 5584 } 5585 5586 e_day_view_ensure_rows_visible ( 5587 day_view, 5588 day_view->selection_start_row, 5589 day_view->selection_end_row); 5590 5591 e_day_view_update_calendar_selection_time (day_view); 5592 5593 gtk_widget_queue_draw (day_view->top_canvas); 5594 gtk_widget_queue_draw (day_view->top_dates_canvas); 5595 gtk_widget_queue_draw (day_view->main_canvas); 5596 } 5597 5598 /* Change the duration to the time that ends the current work day */ 5599 static void 5600 e_day_view_change_duration_to_end_of_work_day (EDayView *day_view) 5601 { 5602 ECalModel *model; 5603 gint work_day_end_hour; 5604 gint work_day_end_minute; 5605 5606 g_return_if_fail (day_view != NULL); 5607 5608 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 5609 work_day_end_hour = e_cal_model_get_work_day_end_hour (model); 5610 work_day_end_minute = e_cal_model_get_work_day_end_minute (model); 5611 5612 if (day_view->selection_in_top_canvas) 5613 return; 5614 else { 5615 gint work_end_row,selection_start_row; 5616 5617 work_end_row = e_day_view_convert_time_to_row ( 5618 day_view, work_day_end_hour - 1, work_day_end_minute + 30); 5619 selection_start_row = day_view->selection_start_row; 5620 if (selection_start_row <= work_end_row) 5621 day_view->selection_end_row = work_end_row; 5622 else { 5623 day_view->selection_start_row = work_end_row + 1; 5624 day_view->selection_end_row = selection_start_row; 5625 } 5626 } 5627 5628 e_day_view_ensure_rows_visible ( 5629 day_view, 5630 day_view->selection_start_row, 5631 day_view->selection_end_row); 5632 5633 e_day_view_update_calendar_selection_time (day_view); 5634 5635 gtk_widget_queue_draw (day_view->top_canvas); 5636 gtk_widget_queue_draw (day_view->top_dates_canvas); 5637 gtk_widget_queue_draw (day_view->main_canvas); 5638 } 5639 5640 static void 5641 e_day_view_cursor_key_up_shifted (EDayView *day_view, 5642 GdkEventKey *event) 5643 { 5644 gint *row; 5645 5646 if (day_view->selection_in_top_canvas) 5647 return; 5648 5649 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) 5650 row = &day_view->selection_start_row; 5651 else 5652 row = &day_view->selection_end_row; 5653 5654 if (*row == 0) 5655 return; 5656 5657 *row = *row - 1; 5658 5659 e_day_view_ensure_rows_visible (day_view, *row, *row); 5660 5661 e_day_view_normalize_selection (day_view); 5662 5663 e_day_view_update_calendar_selection_time (day_view); 5664 5665 /* FIXME: Optimise? */ 5666 gtk_widget_queue_draw (day_view->top_canvas); 5667 gtk_widget_queue_draw (day_view->main_canvas); 5668 } 5669 5670 static gboolean 5671 e_day_view_focus (GtkWidget *widget, 5672 GtkDirectionType direction) 5673 { 5674 EDayView *day_view; 5675 gint new_day; 5676 gint new_event_num; 5677 gint start_row, end_row; 5678 5679 g_return_val_if_fail (widget != NULL, FALSE); 5680 g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE); 5681 day_view = E_DAY_VIEW (widget); 5682 5683 if (!e_day_view_get_next_tab_event (day_view, direction, 5684 &new_day, &new_event_num)) 5685 return FALSE; 5686 5687 if ((new_day == -1) && (new_event_num == -1)) { 5688 /* focus should go to the day view widget itself 5689 */ 5690 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 5691 return TRUE; 5692 } 5693 5694 if (new_day != E_DAY_VIEW_LONG_EVENT && new_day != -1) { 5695 if (e_day_view_get_event_rows (day_view, new_day, 5696 new_event_num, 5697 &start_row, &end_row)) 5698 /* ensure the event to be seen */ 5699 e_day_view_ensure_rows_visible ( 5700 day_view, 5701 start_row, end_row); 5702 } 5703 e_day_view_start_editing_event ( 5704 day_view, new_day, 5705 new_event_num, NULL); 5706 5707 return TRUE; 5708 } 5709 5710 /** 5711 * e_day_view_get_extreme_event 5712 * @day_view: the day view widget operates on 5713 * @start_day, @end_day: range of search, both inclusive 5714 * @first: %TURE indicate to return the data for the first event in the range, 5715 * %FALSE to return data for the last event in the range. 5716 * @day_out: out value, day of the event found. -1 for no event found. 5717 * @event_num_out: out value, event number of the event found. 5718 * -1 for no event found. 5719 * 5720 * Get day and event_num value for the first or last event found in the day range. 5721 * 5722 * Return value: %TRUE, if a event found. 5723 **/ 5724 static gboolean 5725 e_day_view_get_extreme_event (EDayView *day_view, 5726 gint start_day, 5727 gint end_day, 5728 gboolean first, 5729 gint *day_out, 5730 gint *event_num_out) 5731 { 5732 gint loop_day; 5733 5734 g_return_val_if_fail (day_view != NULL, FALSE); 5735 g_return_val_if_fail (start_day >= 0, FALSE); 5736 g_return_val_if_fail (end_day <= E_DAY_VIEW_LONG_EVENT, FALSE); 5737 g_return_val_if_fail (day_out && event_num_out, FALSE); 5738 5739 if (start_day > end_day) 5740 return FALSE; 5741 if (first) { 5742 for (loop_day = start_day; loop_day <= end_day; ++loop_day) 5743 if (day_view->events[loop_day]->len > 0) { 5744 *day_out = loop_day; 5745 *event_num_out = 0; 5746 return TRUE; 5747 } 5748 } 5749 else { 5750 for (loop_day = end_day; loop_day >= start_day; --loop_day) 5751 if (day_view->events[loop_day]->len > 0) { 5752 *day_out = loop_day; 5753 *event_num_out = 5754 day_view->events[loop_day]->len - 1; 5755 return TRUE; 5756 } 5757 } 5758 *day_out = -1; 5759 *event_num_out = -1; 5760 return FALSE; 5761 } 5762 5763 /** 5764 * e_day_view_get_extreme_long_event 5765 * @day_view: the day view widget operates on 5766 * @first: %TURE indicate to return the data for the first event in the range, 5767 * %FALSE to return data for the last event in the range. 5768 * @event_num_out: out value, event number of the event found. 5769 * -1 for no event found. 5770 * 5771 * Similar to e_day_view_get_extreme_event, but run for long events. 5772 * 5773 * Return value: %TRUE, if a event found. 5774 **/ 5775 static gboolean 5776 e_day_view_get_extreme_long_event (EDayView *day_view, 5777 gboolean first, 5778 gint *day_out, 5779 gint *event_num_out) 5780 { 5781 g_return_val_if_fail (day_view != NULL, FALSE); 5782 g_return_val_if_fail (day_out && event_num_out, FALSE); 5783 5784 if (first && (day_view->long_events->len > 0)) { 5785 *day_out = E_DAY_VIEW_LONG_EVENT; 5786 *event_num_out = 0; 5787 return TRUE; 5788 } 5789 if ((!first) && (day_view->long_events->len > 0)) { 5790 *day_out = E_DAY_VIEW_LONG_EVENT; 5791 *event_num_out = day_view->long_events->len - 1; 5792 return TRUE; 5793 } 5794 *day_out = -1; 5795 *event_num_out = -1; 5796 return FALSE; 5797 } 5798 5799 /** 5800 * e_day_view_get_next_tab_event 5801 * @day_view: the day view widget operates on 5802 * @direction: GTK_DIR_TAB_BACKWARD or GTK_DIR_TAB_FORWARD 5803 * @day_out: out value, day of the event found. -1 for no event found. 5804 * @event_num_out: out value, event number of the event found. 5805 * -1 for no event found. 5806 * 5807 * Decide on which event the focus should go next. 5808 * if ((day_out == -1) && (event_num_out == -1)) is true, focus should go 5809 * to day_view widget itself. 5810 * 5811 * Return value: %TRUE, if a event found. 5812 **/ 5813 static gboolean 5814 e_day_view_get_next_tab_event (EDayView *day_view, 5815 GtkDirectionType direction, 5816 gint *day_out, 5817 gint *event_num_out) 5818 { 5819 gint new_day; 5820 gint new_event_num; 5821 gint days_shown; 5822 5823 g_return_val_if_fail (day_view != NULL, FALSE); 5824 g_return_val_if_fail (day_out != NULL, FALSE); 5825 g_return_val_if_fail (event_num_out != NULL, FALSE); 5826 5827 days_shown = e_day_view_get_days_shown (day_view); 5828 *day_out = -1; 5829 *event_num_out = -1; 5830 5831 g_return_val_if_fail (days_shown > 0, FALSE); 5832 5833 switch (direction) { 5834 case GTK_DIR_TAB_BACKWARD: 5835 new_event_num = day_view->editing_event_num - 1; 5836 break; 5837 case GTK_DIR_TAB_FORWARD: 5838 new_event_num = day_view->editing_event_num + 1; 5839 break; 5840 default: 5841 return FALSE; 5842 } 5843 5844 new_day = day_view->editing_event_day; 5845 5846 /* not current editing event, set to first long event if there is one 5847 */ 5848 if (new_day == -1) { 5849 if (direction == GTK_DIR_TAB_FORWARD) { 5850 if (e_day_view_get_extreme_long_event (day_view, TRUE, 5851 day_out, 5852 event_num_out)) 5853 return TRUE; 5854 5855 /* no long event, set to first event if there is 5856 */ 5857 e_day_view_get_extreme_event ( 5858 day_view, 0, 5859 days_shown - 1, TRUE, 5860 day_out, event_num_out); 5861 /* go to event if found, or day view widget 5862 */ 5863 return TRUE; 5864 } 5865 else { 5866 if (e_day_view_get_extreme_event (day_view, 0, 5867 days_shown - 1, FALSE, 5868 day_out, event_num_out)) 5869 return TRUE; 5870 5871 /* no event, set to last long event if there is 5872 */ 5873 e_day_view_get_extreme_long_event ( 5874 day_view, FALSE, 5875 day_out, 5876 event_num_out); 5877 5878 /* go to long event if found, or day view widget 5879 */ 5880 return TRUE; 5881 } 5882 } 5883 /* go backward from the first long event */ 5884 else if ((new_day == E_DAY_VIEW_LONG_EVENT) && (new_event_num < 0)) { 5885 /* let focus go to day view widget in this case 5886 */ 5887 return TRUE; 5888 } 5889 /* go forward from the last long event */ 5890 else if ((new_day == E_DAY_VIEW_LONG_EVENT) && 5891 (new_event_num >= day_view->long_events->len)) { 5892 e_day_view_get_extreme_event ( 5893 day_view, 0, 5894 days_shown - 1, TRUE, 5895 day_out, event_num_out); 5896 /* go to the next main item event if found or day view widget 5897 */ 5898 return TRUE; 5899 } 5900 5901 /* go backward from the first event in current editting day */ 5902 else if ((new_day < E_DAY_VIEW_LONG_EVENT) && (new_event_num < 0)) { 5903 /* try to find a event from the previous day in days shown 5904 */ 5905 if (e_day_view_get_extreme_event (day_view, 0, 5906 new_day - 1, FALSE, 5907 day_out, event_num_out)) 5908 return TRUE; 5909 /* try to find a long event 5910 */ 5911 e_day_view_get_extreme_long_event ( 5912 day_view, FALSE, 5913 day_out, event_num_out); 5914 /* go to a long event if found, or day view widget 5915 */ 5916 return TRUE; 5917 } 5918 /* go forward from the last event in current editting day */ 5919 else if ((new_day < E_DAY_VIEW_LONG_EVENT) && 5920 (new_event_num >= day_view->events[new_day]->len)) { 5921 /* try to find a event from the next day in days shown 5922 */ 5923 e_day_view_get_extreme_event ( 5924 day_view, (new_day + 1), 5925 days_shown - 1, TRUE, 5926 day_out, event_num_out); 5927 /* go to a event found, or day view widget 5928 */ 5929 return TRUE; 5930 } 5931 /* in the normal case 5932 */ 5933 *day_out = new_day; 5934 *event_num_out = new_event_num; 5935 return TRUE; 5936 } 5937 5938 static void 5939 e_day_view_cursor_key_down_shifted (EDayView *day_view, 5940 GdkEventKey *event) 5941 { 5942 gint *row; 5943 5944 if (day_view->selection_in_top_canvas) 5945 return; 5946 5947 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) 5948 row = &day_view->selection_start_row; 5949 else 5950 row = &day_view->selection_end_row; 5951 5952 if (*row >= day_view->rows - 1) 5953 return; 5954 5955 *row = *row + 1; 5956 5957 e_day_view_ensure_rows_visible (day_view, *row, *row); 5958 5959 e_day_view_normalize_selection (day_view); 5960 5961 e_day_view_update_calendar_selection_time (day_view); 5962 5963 /* FIXME: Optimise? */ 5964 gtk_widget_queue_draw (day_view->top_canvas); 5965 gtk_widget_queue_draw (day_view->main_canvas); 5966 } 5967 5968 static void 5969 e_day_view_cursor_key_left_shifted (EDayView *day_view, 5970 GdkEventKey *event) 5971 { 5972 gint *day; 5973 5974 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) 5975 day = &day_view->selection_start_day; 5976 else 5977 day = &day_view->selection_end_day; 5978 5979 if (*day == 0) 5980 return; 5981 5982 *day = *day - 1; 5983 5984 e_day_view_normalize_selection (day_view); 5985 5986 e_day_view_update_calendar_selection_time (day_view); 5987 5988 /* FIXME: Optimise? */ 5989 gtk_widget_queue_draw (day_view->top_canvas); 5990 gtk_widget_queue_draw (day_view->main_canvas); 5991 } 5992 5993 static void 5994 e_day_view_cursor_key_right_shifted (EDayView *day_view, 5995 GdkEventKey *event) 5996 { 5997 gint *day; 5998 5999 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) 6000 day = &day_view->selection_start_day; 6001 else 6002 day = &day_view->selection_end_day; 6003 6004 if (*day >= day_view->days_shown - 1) 6005 return; 6006 6007 *day = *day + 1; 6008 6009 e_day_view_normalize_selection (day_view); 6010 6011 e_day_view_update_calendar_selection_time (day_view); 6012 6013 /* FIXME: Optimise? */ 6014 gtk_widget_queue_draw (day_view->top_canvas); 6015 gtk_widget_queue_draw (day_view->main_canvas); 6016 } 6017 6018 static void 6019 e_day_view_cursor_key_up (EDayView *day_view, 6020 GdkEventKey *event) 6021 { 6022 if (day_view->selection_start_day == -1) { 6023 day_view->selection_start_day = 0; 6024 day_view->selection_start_row = 0; 6025 } 6026 day_view->selection_end_day = day_view->selection_start_day; 6027 6028 if (day_view->selection_in_top_canvas) { 6029 return; 6030 } else if (day_view->selection_start_row == 0) { 6031 day_view->selection_in_top_canvas = TRUE; 6032 day_view->selection_start_row = -1; 6033 } else { 6034 day_view->selection_start_row--; 6035 } 6036 day_view->selection_end_row = day_view->selection_start_row; 6037 6038 if (!day_view->selection_in_top_canvas) 6039 e_day_view_ensure_rows_visible ( 6040 day_view, 6041 day_view->selection_start_row, 6042 day_view->selection_end_row); 6043 6044 g_signal_emit_by_name (day_view, "selected_time_changed"); 6045 e_day_view_update_calendar_selection_time (day_view); 6046 6047 /* FIXME: Optimise? */ 6048 gtk_widget_queue_draw (day_view->top_canvas); 6049 gtk_widget_queue_draw (day_view->main_canvas); 6050 } 6051 6052 static void 6053 e_day_view_cursor_key_down (EDayView *day_view, 6054 GdkEventKey *event) 6055 { 6056 if (day_view->selection_start_day == -1) { 6057 day_view->selection_start_day = 0; 6058 day_view->selection_start_row = 0; 6059 } 6060 day_view->selection_end_day = day_view->selection_start_day; 6061 6062 if (day_view->selection_in_top_canvas) { 6063 day_view->selection_in_top_canvas = FALSE; 6064 day_view->selection_start_row = 0; 6065 } else if (day_view->selection_start_row >= day_view->rows - 1) { 6066 return; 6067 } else { 6068 day_view->selection_start_row++; 6069 } 6070 day_view->selection_end_row = day_view->selection_start_row; 6071 6072 if (!day_view->selection_in_top_canvas) 6073 e_day_view_ensure_rows_visible ( 6074 day_view, 6075 day_view->selection_start_row, 6076 day_view->selection_end_row); 6077 6078 g_signal_emit_by_name (day_view, "selected_time_changed"); 6079 e_day_view_update_calendar_selection_time (day_view); 6080 6081 /* FIXME: Optimise? */ 6082 gtk_widget_queue_draw (day_view->top_canvas); 6083 gtk_widget_queue_draw (day_view->main_canvas); 6084 } 6085 6086 static void 6087 e_day_view_cursor_key_left (EDayView *day_view, 6088 GdkEventKey *event) 6089 { 6090 if (day_view->selection_start_day == 0) { 6091 gnome_calendar_previous (e_calendar_view_get_calendar (E_CALENDAR_VIEW (day_view))); 6092 } else { 6093 day_view->selection_start_day--; 6094 day_view->selection_end_day--; 6095 6096 e_day_view_update_calendar_selection_time (day_view); 6097 6098 /* FIXME: Optimise? */ 6099 gtk_widget_queue_draw (day_view->top_canvas); 6100 gtk_widget_queue_draw (day_view->main_canvas); 6101 } 6102 g_signal_emit_by_name (day_view, "selected_time_changed"); 6103 } 6104 6105 static void 6106 e_day_view_cursor_key_right (EDayView *day_view, 6107 GdkEventKey *event) 6108 { 6109 if (day_view->selection_end_day == day_view->days_shown - 1) { 6110 gnome_calendar_next (e_calendar_view_get_calendar (E_CALENDAR_VIEW (day_view))); 6111 } else { 6112 day_view->selection_start_day++; 6113 day_view->selection_end_day++; 6114 6115 e_day_view_update_calendar_selection_time (day_view); 6116 6117 /* FIXME: Optimise? */ 6118 gtk_widget_queue_draw (day_view->top_canvas); 6119 gtk_widget_queue_draw (day_view->main_canvas); 6120 } 6121 g_signal_emit_by_name (day_view, "selected_time_changed"); 6122 } 6123 6124 /* Scrolls the main canvas up or down. The pages_to_scroll argument 6125 * is multiplied with the adjustment's page size and added to the adjustment's 6126 * value, while ensuring we stay within the bounds. A positive value will 6127 * scroll the canvas down and a negative value will scroll it up. */ 6128 static void 6129 e_day_view_scroll (EDayView *day_view, 6130 gfloat pages_to_scroll) 6131 { 6132 GtkAdjustment *adjustment; 6133 GtkScrollable *scrollable; 6134 gdouble new_value; 6135 gdouble page_size; 6136 gdouble lower; 6137 gdouble upper; 6138 gdouble value; 6139 6140 scrollable = GTK_SCROLLABLE (day_view->main_canvas); 6141 adjustment = gtk_scrollable_get_vadjustment (scrollable); 6142 6143 page_size = gtk_adjustment_get_page_size (adjustment); 6144 lower = gtk_adjustment_get_lower (adjustment); 6145 upper = gtk_adjustment_get_upper (adjustment); 6146 value = gtk_adjustment_get_value (adjustment); 6147 6148 new_value = value - page_size * pages_to_scroll; 6149 new_value = CLAMP (new_value, lower, upper - page_size); 6150 gtk_adjustment_set_value (adjustment, new_value); 6151 } 6152 6153 static void 6154 e_day_view_top_scroll (EDayView *day_view, 6155 gfloat pages_to_scroll) 6156 { 6157 GtkAdjustment *adjustment; 6158 GtkScrollable *scrollable; 6159 gdouble new_value; 6160 gdouble page_size; 6161 gdouble lower; 6162 gdouble upper; 6163 gdouble value; 6164 6165 scrollable = GTK_SCROLLABLE (day_view->top_canvas); 6166 adjustment = gtk_scrollable_get_vadjustment (scrollable); 6167 6168 page_size = gtk_adjustment_get_page_size (adjustment); 6169 lower = gtk_adjustment_get_lower (adjustment); 6170 upper = gtk_adjustment_get_upper (adjustment); 6171 value = gtk_adjustment_get_value (adjustment); 6172 6173 new_value = value - page_size * pages_to_scroll; 6174 new_value = CLAMP (new_value, lower, upper - page_size); 6175 gtk_adjustment_set_value (adjustment, new_value); 6176 } 6177 6178 void 6179 e_day_view_ensure_rows_visible (EDayView *day_view, 6180 gint start_row, 6181 gint end_row) 6182 { 6183 GtkAdjustment *adjustment; 6184 GtkScrollable *scrollable; 6185 gdouble max_value; 6186 gdouble min_value; 6187 gdouble page_size; 6188 gdouble value; 6189 6190 scrollable = GTK_SCROLLABLE (day_view->main_canvas); 6191 adjustment = gtk_scrollable_get_vadjustment (scrollable); 6192 6193 value = gtk_adjustment_get_value (adjustment); 6194 page_size = gtk_adjustment_get_page_size (adjustment); 6195 6196 min_value = (end_row + 1) * day_view->row_height - page_size; 6197 if (value < min_value) 6198 value = min_value; 6199 6200 max_value = start_row * day_view->row_height; 6201 if (value > max_value) 6202 value = max_value; 6203 6204 gtk_adjustment_set_value (adjustment, value); 6205 } 6206 6207 static void 6208 e_day_view_start_editing_event (EDayView *day_view, 6209 gint day, 6210 gint event_num, 6211 GdkEventKey *key_event) 6212 { 6213 EDayViewEvent *event; 6214 ETextEventProcessor *event_processor = NULL; 6215 ETextEventProcessorCommand command; 6216 6217 #if 0 6218 g_print ("In e_day_view_start_editing_event\n"); 6219 #endif 6220 6221 /* If we are already editing the event, just return. */ 6222 if (day == day_view->editing_event_day 6223 && event_num == day_view->editing_event_num) 6224 return; 6225 6226 if (day == E_DAY_VIEW_LONG_EVENT) { 6227 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 6228 return; 6229 6230 event = &g_array_index (day_view->long_events, EDayViewEvent, 6231 event_num); 6232 } else { 6233 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 6234 return; 6235 6236 event = &g_array_index (day_view->events[day], EDayViewEvent, 6237 event_num); 6238 } 6239 6240 if (!is_comp_data_valid (event)) 6241 return; 6242 6243 if (e_client_is_readonly (E_CLIENT (event->comp_data->client))) 6244 return; 6245 6246 /* If the event is not shown, don't try to edit it. */ 6247 if (!event->canvas_item) 6248 return; 6249 6250 /* We must grab the focus before setting the initial text, since 6251 * grabbing the focus will result in a call to 6252 * e_day_view_on_editing_started (), which will reset the text to get 6253 * rid of the start and end times. */ 6254 e_canvas_item_grab_focus (event->canvas_item, TRUE); 6255 6256 if (key_event) { 6257 if (gtk_im_context_filter_keypress (((EText *)(event->canvas_item))->im_context, key_event)) { 6258 ((EText *)(event->canvas_item))->need_im_reset = TRUE; 6259 } 6260 else { 6261 gchar *initial_text; 6262 6263 initial_text = e_utf8_from_gtk_event_key (GTK_WIDGET (day_view), key_event->keyval, key_event->string); 6264 gnome_canvas_item_set ( 6265 event->canvas_item, 6266 "text", initial_text, 6267 NULL); 6268 if (initial_text) 6269 g_free (initial_text); 6270 } 6271 } 6272 6273 /* Try to move the cursor to the end of the text. */ 6274 g_object_get ( 6275 event->canvas_item, 6276 "event_processor", &event_processor, 6277 NULL); 6278 if (event_processor) { 6279 command.action = E_TEP_MOVE; 6280 command.position = E_TEP_END_OF_BUFFER; 6281 g_signal_emit_by_name ( 6282 event_processor, 6283 "command", &command); 6284 } 6285 } 6286 6287 /* This stops the current edit. If accept is TRUE the event summary is updated, 6288 * else the edit is cancelled. */ 6289 static void 6290 e_day_view_stop_editing_event (EDayView *day_view) 6291 { 6292 GtkWidget *toplevel; 6293 6294 /* Check we are editing an event. */ 6295 if (day_view->editing_event_day == -1) 6296 return; 6297 6298 /* Set focus to the toplevel so the item loses focus. */ 6299 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (day_view)); 6300 if (toplevel && GTK_IS_WINDOW (toplevel)) 6301 gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); 6302 } 6303 6304 /* Cancels the current edition by resetting the appointment's text to its original value */ 6305 static void 6306 cancel_editing (EDayView *day_view) 6307 { 6308 gint day, event_num; 6309 EDayViewEvent *event; 6310 const gchar *summary; 6311 6312 day = day_view->editing_event_day; 6313 event_num = day_view->editing_event_num; 6314 6315 g_return_if_fail (day != -1); 6316 6317 if (day == E_DAY_VIEW_LONG_EVENT) { 6318 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 6319 return; 6320 6321 event = &g_array_index (day_view->long_events, EDayViewEvent, event_num); 6322 } else { 6323 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 6324 return; 6325 6326 event = &g_array_index (day_view->events[day], EDayViewEvent, event_num); 6327 } 6328 6329 if (!is_comp_data_valid (event)) 6330 return; 6331 6332 /* Reset the text to what was in the component */ 6333 6334 summary = icalcomponent_get_summary (event->comp_data->icalcomp); 6335 g_object_set ( 6336 event->canvas_item, 6337 "text", summary ? summary : "", 6338 NULL); 6339 6340 /* Stop editing */ 6341 e_day_view_stop_editing_event (day_view); 6342 } 6343 6344 static EDayViewEvent * 6345 tooltip_get_view_event (EDayView *day_view, 6346 gint day, 6347 gint event_num) 6348 { 6349 EDayViewEvent *pevent; 6350 6351 if (day == E_DAY_VIEW_LONG_EVENT) { 6352 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 6353 return NULL; 6354 6355 pevent = &g_array_index (day_view->long_events, EDayViewEvent, 6356 event_num); 6357 } else { 6358 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 6359 return NULL; 6360 6361 pevent = &g_array_index (day_view->events[day], EDayViewEvent, 6362 event_num); 6363 } 6364 6365 return pevent; 6366 } 6367 6368 static void 6369 tooltip_destroy (EDayView *day_view, 6370 GnomeCanvasItem *item) 6371 { 6372 GtkWidget *tooltip = g_object_get_data (G_OBJECT (day_view), "tooltip-window"); 6373 6374 if (tooltip) { 6375 gtk_widget_destroy (tooltip); 6376 g_object_set_data (G_OBJECT (day_view), "tooltip-window", NULL); 6377 } 6378 6379 if (item) { 6380 EDayViewEvent *pevent; 6381 gint event_num = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-num")); 6382 gint day = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-day")); 6383 6384 pevent = tooltip_get_view_event (day_view, day, event_num); 6385 if (pevent) { 6386 pevent->tooltip = NULL; 6387 if (pevent->timeout != -1) { 6388 g_source_remove (pevent->timeout); 6389 pevent->timeout = -1; 6390 } 6391 } 6392 } 6393 } 6394 6395 static gboolean 6396 e_day_view_on_text_item_event (GnomeCanvasItem *item, 6397 GdkEvent *event, 6398 EDayView *day_view) 6399 { 6400 switch (event->type) { 6401 case GDK_KEY_PRESS: 6402 tooltip_destroy (day_view, item); 6403 if (!E_TEXT (item)->preedit_len && event && event->key.keyval == GDK_KEY_Return) { 6404 day_view->resize_event_num = -1; 6405 6406 /* We set the keyboard focus to the EDayView, so the 6407 * EText item loses it and stops the edit. */ 6408 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 6409 6410 /* Stop the signal last or we will also stop any 6411 * other events getting to the EText item. */ 6412 g_signal_stop_emission_by_name (item, "event"); 6413 return TRUE; 6414 } else if (event->key.keyval == GDK_KEY_Escape) { 6415 cancel_editing (day_view); 6416 g_signal_stop_emission_by_name (item, "event"); 6417 /* focus should go to day view when stop editing */ 6418 gtk_widget_grab_focus (GTK_WIDGET (day_view)); 6419 return TRUE; 6420 } else if ((event->key.keyval == GDK_KEY_Up) 6421 && (event->key.state & GDK_SHIFT_MASK) 6422 && (event->key.state & GDK_CONTROL_MASK) 6423 && !(event->key.state & GDK_MOD1_MASK)) { 6424 e_day_view_change_event_end_time_up (day_view); 6425 return TRUE; 6426 } else if ((event->key.keyval == GDK_KEY_Down) 6427 && (event->key.state & GDK_SHIFT_MASK) 6428 && (event->key.state & GDK_CONTROL_MASK) 6429 && !(event->key.state & GDK_MOD1_MASK)) { 6430 e_day_view_change_event_end_time_down (day_view); 6431 return TRUE; 6432 } 6433 break; 6434 case GDK_2BUTTON_PRESS: 6435 #if 0 6436 g_print ("Item got double-click\n"); 6437 #endif 6438 break; 6439 6440 case GDK_BUTTON_RELEASE: 6441 if (day_view->resize_event_num != -1) 6442 day_view->resize_event_num = -1; 6443 6444 if (day_view->drag_event_num != -1) 6445 day_view->drag_event_num = -1; 6446 6447 case GDK_BUTTON_PRESS: 6448 tooltip_destroy (day_view, item); 6449 /* Only let the EText handle the event while editing. */ 6450 if (!E_TEXT (item)->editing) 6451 g_signal_stop_emission_by_name (item, "event"); 6452 break; 6453 case GDK_FOCUS_CHANGE: 6454 if (event->focus_change.in) 6455 e_day_view_on_editing_started (day_view, item); 6456 else 6457 e_day_view_on_editing_stopped (day_view, item); 6458 6459 return FALSE; 6460 case GDK_ENTER_NOTIFY: 6461 { 6462 EDayViewEvent *pevent; 6463 ECalendarViewEventData *data; 6464 gint event_x, event_y, row, day, event_num; 6465 ECalendarViewPosition pos; 6466 gboolean main_canvas = TRUE; 6467 GdkWindow *window; 6468 GtkLayout *layout; 6469 6470 if (day_view->editing_event_num != -1) 6471 break; 6472 6473 if (day_view->resize_event_num != -1) 6474 break; 6475 6476 if (day_view->drag_event_num != -1) 6477 break; 6478 6479 /* Convert the coords to the main canvas window, or return if the 6480 * window is not found. */ 6481 layout = GTK_LAYOUT (day_view->main_canvas); 6482 window = gtk_layout_get_bin_window (layout); 6483 if (!e_day_view_convert_event_coords ( 6484 day_view, (GdkEvent *) event, 6485 window, &event_x, &event_y)) { 6486 6487 main_canvas = FALSE; 6488 6489 layout = GTK_LAYOUT (day_view->top_canvas); 6490 window = gtk_layout_get_bin_window (layout); 6491 if (!e_day_view_convert_event_coords ( 6492 day_view, (GdkEvent *) event, 6493 window, &event_x, &event_y)) { 6494 return FALSE; 6495 } 6496 } 6497 /* Find out where the mouse is. */ 6498 if (main_canvas) { 6499 pos = e_day_view_convert_position_in_main_canvas ( 6500 day_view, 6501 event_x, event_y, 6502 &day, &row, 6503 &event_num); 6504 } else { 6505 gint tmp; 6506 6507 pos = e_day_view_convert_position_in_top_canvas ( 6508 day_view, 6509 event_x, event_y, 6510 &tmp, &event_num); 6511 day = E_DAY_VIEW_LONG_EVENT; 6512 } 6513 6514 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE) 6515 break; 6516 6517 /* even when returns position inside, or other, then the day and/or event_num 6518 * can be unknown, thus check for this here, otherwise it will crash later */ 6519 if (day == -1 || event_num == -1) 6520 break; 6521 6522 pevent = tooltip_get_view_event (day_view, day, event_num); 6523 if (!pevent) 6524 break; 6525 6526 g_object_set_data (G_OBJECT (item), "event-num", GINT_TO_POINTER (event_num)); 6527 g_object_set_data (G_OBJECT (item), "event-day", GINT_TO_POINTER (day)); 6528 6529 data = g_malloc (sizeof (ECalendarViewEventData)); 6530 pevent->x = ((GdkEventCrossing *) event)->x_root; 6531 pevent->y = ((GdkEventCrossing *) event)->y_root; 6532 pevent->tooltip = NULL; 6533 6534 data->cal_view = (ECalendarView *) day_view; 6535 data->day = day; 6536 data->event_num = event_num; 6537 data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event; 6538 pevent->timeout = g_timeout_add_full ( 6539 G_PRIORITY_DEFAULT, 500, 6540 (GSourceFunc) e_calendar_view_get_tooltips, 6541 data, (GDestroyNotify) g_free); 6542 6543 return TRUE; 6544 } 6545 case GDK_LEAVE_NOTIFY: 6546 tooltip_destroy (day_view, item); 6547 return TRUE; 6548 case GDK_MOTION_NOTIFY: 6549 { 6550 EDayViewEvent *pevent; 6551 gint event_num = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-num")); 6552 gint day = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-day")); 6553 6554 pevent = tooltip_get_view_event (day_view, day, event_num); 6555 if (!pevent) 6556 break; 6557 6558 pevent->x = ((GdkEventMotion *) event)->x_root; 6559 pevent->y = ((GdkEventMotion *) event)->y_root; 6560 pevent->tooltip = (GtkWidget *) g_object_get_data (G_OBJECT (day_view), "tooltip-window"); 6561 6562 if (pevent->tooltip) { 6563 e_calendar_view_move_tip (pevent->tooltip, pevent->x + 16, pevent->y + 16); 6564 } 6565 6566 return TRUE; 6567 } 6568 default: 6569 break; 6570 } 6571 6572 return FALSE; 6573 } 6574 6575 static gboolean 6576 e_day_view_event_move (ECalendarView *cal_view, 6577 ECalViewMoveDirection direction) 6578 { 6579 EDayViewEvent *event; 6580 EDayView *day_view; 6581 gint time_divisions; 6582 gint day, event_num, resize_start_row, resize_end_row; 6583 time_t start_dt, end_dt; 6584 struct icaltimetype start_time, end_time; 6585 6586 day_view = E_DAY_VIEW (cal_view); 6587 day = day_view->editing_event_day; 6588 event_num = day_view->editing_event_num; 6589 6590 time_divisions = e_calendar_view_get_time_divisions (cal_view); 6591 6592 if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT)) 6593 return FALSE; 6594 6595 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 6596 return FALSE; 6597 6598 event = &g_array_index (day_view->events[day], EDayViewEvent, 6599 event_num); 6600 day_view->resize_event_day = day; 6601 day_view->resize_event_num = event_num; 6602 day_view->resize_bars_event_day = day; 6603 day_view->resize_bars_event_num = event_num; 6604 resize_start_row = event->start_minute / time_divisions; 6605 resize_end_row = (event->end_minute - 1) / time_divisions; 6606 if (resize_end_row < resize_start_row) 6607 resize_end_row = resize_start_row; 6608 6609 switch (direction) { 6610 case E_CAL_VIEW_MOVE_UP: 6611 if (resize_start_row <= 0) 6612 return FALSE; 6613 resize_start_row--; 6614 resize_end_row--; 6615 start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row); 6616 end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1); 6617 break; 6618 case E_CAL_VIEW_MOVE_DOWN: 6619 if (resize_end_row >= day_view->rows - 1) 6620 return FALSE; 6621 resize_start_row++; 6622 resize_end_row++; 6623 start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row); 6624 end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1); 6625 break; 6626 case E_CAL_VIEW_MOVE_LEFT: 6627 if (day <= 0) 6628 return TRUE; 6629 start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row); 6630 end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1); 6631 start_time = icaltime_from_timet (start_dt, 0); 6632 end_time = icaltime_from_timet (end_dt, 0); 6633 icaltime_adjust (&start_time ,-1,0,0,0); 6634 icaltime_adjust (&end_time ,-1,0,0,0); 6635 start_dt = icaltime_as_timet (start_time); 6636 end_dt = icaltime_as_timet (end_time); 6637 break; 6638 case E_CAL_VIEW_MOVE_RIGHT: 6639 if (day + 1 >= day_view->days_shown) 6640 return TRUE; 6641 start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row); 6642 end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1); 6643 start_time = icaltime_from_timet (start_dt, 0); 6644 end_time = icaltime_from_timet (end_dt, 0); 6645 icaltime_adjust (&start_time ,1,0,0,0); 6646 icaltime_adjust (&end_time ,1,0,0,0); 6647 start_dt = icaltime_as_timet (start_time); 6648 end_dt = icaltime_as_timet (end_time); 6649 break; 6650 default: 6651 return FALSE; 6652 } 6653 6654 e_day_view_change_event_time (day_view, start_dt, end_dt); 6655 e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row); 6656 6657 return TRUE; 6658 } 6659 6660 static void 6661 e_day_view_change_event_time (EDayView *day_view, 6662 time_t start_dt, 6663 time_t end_dt) 6664 { 6665 EDayViewEvent *event; 6666 gint day, event_num; 6667 ECalComponent *comp; 6668 ECalComponentDateTime date; 6669 struct icaltimetype itt; 6670 ECalModel *model; 6671 ECalClient *client; 6672 ESourceRegistry *registry; 6673 CalObjModType mod = CALOBJ_MOD_ALL; 6674 GtkWindow *toplevel; 6675 6676 day = day_view->editing_event_day; 6677 event_num = day_view->editing_event_num; 6678 6679 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 6680 registry = e_cal_model_get_registry (model); 6681 6682 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 6683 return; 6684 6685 event = &g_array_index (day_view->events[day], EDayViewEvent, 6686 event_num); 6687 6688 if (!is_comp_data_valid (event)) 6689 return; 6690 6691 client = event->comp_data->client; 6692 6693 /* We use a temporary shallow copy of the ico since we don't want to 6694 * change the original ico here. Otherwise we would not detect that 6695 * the event's time had changed in the "update_event" callback. */ 6696 comp = e_cal_component_new (); 6697 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 6698 6699 if (e_cal_component_has_attendees (comp) && 6700 !itip_organizer_is_user (registry, comp, client)) { 6701 g_object_unref (comp); 6702 return; 6703 } 6704 6705 date.value = &itt; 6706 /* FIXME: Should probably keep the timezone of the original start 6707 * and end times. */ 6708 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 6709 6710 *date.value = icaltime_from_timet_with_zone (start_dt, FALSE, 6711 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 6712 cal_comp_set_dtstart_with_oldzone (client, comp, &date); 6713 *date.value = icaltime_from_timet_with_zone (end_dt, FALSE, 6714 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 6715 cal_comp_set_dtend_with_oldzone (client, comp, &date); 6716 6717 e_cal_component_commit_sequence (comp); 6718 6719 if (day_view->last_edited_comp_string != NULL) { 6720 g_free (day_view->last_edited_comp_string); 6721 day_view->last_edited_comp_string = NULL; 6722 } 6723 6724 day_view->last_edited_comp_string = e_cal_component_get_as_string (comp); 6725 6726 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE; 6727 6728 if (e_cal_component_has_recurrences (comp)) { 6729 if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) { 6730 gtk_widget_queue_draw (day_view->top_canvas); 6731 goto out; 6732 } 6733 6734 if (mod == CALOBJ_MOD_ALL) 6735 comp_util_sanitize_recurrence_master (comp, client); 6736 6737 if (mod == CALOBJ_MOD_THIS) { 6738 e_cal_component_set_rdate_list (comp, NULL); 6739 e_cal_component_set_rrule_list (comp, NULL); 6740 e_cal_component_set_exdate_list (comp, NULL); 6741 e_cal_component_set_exrule_list (comp, NULL); 6742 } 6743 } else if (e_cal_component_is_instance (comp)) 6744 mod = CALOBJ_MOD_THIS; 6745 6746 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view))); 6747 6748 e_cal_component_commit_sequence (comp); 6749 6750 e_calendar_view_modify_and_send ( 6751 E_CALENDAR_VIEW (day_view), 6752 comp, client, mod, toplevel, TRUE); 6753 6754 out: 6755 g_object_unref (comp); 6756 } 6757 6758 static void 6759 e_day_view_change_event_end_time_up (EDayView *day_view) 6760 { 6761 EDayViewEvent *event; 6762 ECalendarView *cal_view; 6763 gint time_divisions; 6764 gint day, event_num, resize_start_row, resize_end_row; 6765 6766 day = day_view->editing_event_day; 6767 event_num = day_view->editing_event_num; 6768 if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT)) 6769 return; 6770 6771 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 6772 return; 6773 6774 cal_view = E_CALENDAR_VIEW (day_view); 6775 time_divisions = e_calendar_view_get_time_divisions (cal_view); 6776 6777 event = &g_array_index (day_view->events[day], EDayViewEvent, 6778 event_num); 6779 day_view->resize_event_day = day; 6780 day_view->resize_event_num = event_num; 6781 day_view->resize_bars_event_day = day; 6782 day_view->resize_bars_event_num = event_num; 6783 resize_start_row = event->start_minute / time_divisions; 6784 resize_end_row = (event->end_minute - 1) / time_divisions; 6785 if (resize_end_row < resize_start_row) 6786 resize_end_row = resize_start_row; 6787 if (resize_end_row == resize_start_row) 6788 return; 6789 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_BOTTOM_EDGE; 6790 resize_end_row--; 6791 day_view->resize_start_row = resize_start_row; 6792 day_view->resize_end_row = resize_end_row; 6793 e_day_view_finish_resize (day_view); 6794 e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row); 6795 } 6796 6797 static void 6798 e_day_view_change_event_end_time_down (EDayView *day_view) 6799 { 6800 EDayViewEvent *event; 6801 ECalendarView *cal_view; 6802 gint time_divisions; 6803 gint day, event_num, resize_start_row, resize_end_row; 6804 6805 cal_view = E_CALENDAR_VIEW (day_view); 6806 time_divisions = e_calendar_view_get_time_divisions (cal_view); 6807 6808 day = day_view->editing_event_day; 6809 event_num = day_view->editing_event_num; 6810 if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT)) 6811 return; 6812 6813 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 6814 return; 6815 6816 event = &g_array_index (day_view->events[day], EDayViewEvent, 6817 event_num); 6818 day_view->resize_event_day = day; 6819 day_view->resize_event_num = event_num; 6820 day_view->resize_bars_event_day = day; 6821 day_view->resize_bars_event_num = event_num; 6822 resize_start_row = event->start_minute / time_divisions; 6823 resize_end_row = (event->end_minute - 1) / time_divisions; 6824 if (resize_end_row < resize_start_row) 6825 resize_end_row = resize_start_row; 6826 if (resize_end_row == day_view->rows -1) 6827 return; 6828 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_BOTTOM_EDGE; 6829 resize_end_row++; 6830 day_view->resize_start_row = resize_start_row; 6831 day_view->resize_end_row = resize_end_row; 6832 e_day_view_finish_resize (day_view); 6833 e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row); 6834 } 6835 6836 static void 6837 e_day_view_on_editing_started (EDayView *day_view, 6838 GnomeCanvasItem *item) 6839 { 6840 GtkAllocation allocation; 6841 gint day, event_num; 6842 6843 if (!e_day_view_find_event_from_item (day_view, item, 6844 &day, &event_num)) 6845 return; 6846 6847 #if 0 6848 g_print ( 6849 "In e_day_view_on_editing_started Day:%i Event:%i\n", 6850 day, event_num); 6851 #endif 6852 6853 /* FIXME: This is a temporary workaround for a bug which seems to stop 6854 * us getting focus_out signals. It is not a complete fix since if we 6855 * don't get focus_out signals we don't save the appointment text so 6856 * this may be lost. */ 6857 if (day_view->editing_event_day == day 6858 && day_view->editing_event_num == event_num) 6859 return; 6860 6861 day_view->editing_event_day = day; 6862 day_view->editing_event_num = event_num; 6863 6864 gtk_widget_get_allocation (day_view->top_canvas, &allocation); 6865 6866 if (day == E_DAY_VIEW_LONG_EVENT) { 6867 gint item_x, item_y, item_w, item_h, scroll_y; 6868 gint start_day, end_day; 6869 6870 e_day_view_reshape_long_event (day_view, event_num); 6871 6872 if (e_day_view_get_long_event_position (day_view, event_num, 6873 &start_day, &end_day, 6874 &item_x, &item_y, 6875 &item_w, &item_h)) { 6876 GtkAdjustment *adjustment; 6877 GtkScrollable *scrollable; 6878 6879 scrollable = GTK_SCROLLABLE (day_view->top_canvas); 6880 adjustment = gtk_scrollable_get_vadjustment (scrollable); 6881 6882 /* and ensure it's visible too */ 6883 /*item_y = (event_num * (day_view->top_row_height + 1)) - 1;*/ 6884 scroll_y = gtk_adjustment_get_value (adjustment); 6885 if (item_y + day_view->top_row_height > allocation.height + scroll_y || item_y < scroll_y) 6886 gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_canvas), 0, item_y); 6887 } 6888 } else { 6889 day_view->resize_bars_event_day = day; 6890 day_view->resize_bars_event_num = event_num; 6891 e_day_view_update_event_label (day_view, day, event_num); 6892 e_day_view_reshape_main_canvas_resize_bars (day_view); 6893 } 6894 6895 g_signal_emit_by_name (day_view, "selection_changed"); 6896 } 6897 6898 static void 6899 e_day_view_on_editing_stopped (EDayView *day_view, 6900 GnomeCanvasItem *item) 6901 { 6902 gint day, event_num; 6903 EDayViewEvent *event; 6904 gchar *text = NULL; 6905 ECalComponentText summary; 6906 ECalComponent *comp; 6907 ECalClient *client; 6908 gboolean on_server; 6909 6910 /* Note: the item we are passed here isn't reliable, so we just stop 6911 * the edit of whatever item was being edited. We also receive this 6912 * event twice for some reason. */ 6913 day = day_view->editing_event_day; 6914 event_num = day_view->editing_event_num; 6915 6916 #if 0 6917 g_print ( 6918 "In e_day_view_on_editing_stopped Day:%i Event:%i\n", 6919 day, event_num); 6920 #endif 6921 6922 /* If no item is being edited, just return. */ 6923 if (day == -1) 6924 return; 6925 6926 if (day == E_DAY_VIEW_LONG_EVENT) { 6927 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 6928 return; 6929 6930 event = &g_array_index (day_view->long_events, EDayViewEvent, 6931 event_num); 6932 } else { 6933 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 6934 return; 6935 6936 event = &g_array_index (day_view->events[day], EDayViewEvent, 6937 event_num); 6938 6939 } 6940 6941 if (!is_comp_data_valid (event)) 6942 return; 6943 6944 /* Reset the edit fields. */ 6945 day_view->editing_event_day = -1; 6946 day_view->editing_event_num = -1; 6947 6948 day_view->resize_bars_event_day = -1; 6949 day_view->resize_bars_event_num = -1; 6950 6951 g_object_set (event->canvas_item, "handle_popup", FALSE, NULL); 6952 g_object_get ( 6953 event->canvas_item, 6954 "text", &text, 6955 NULL); 6956 g_return_if_fail (text != NULL); 6957 6958 comp = e_cal_component_new (); 6959 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 6960 6961 client = event->comp_data->client; 6962 on_server = cal_comp_is_on_server (comp, client); 6963 6964 if (string_is_empty (text) && !on_server) { 6965 const gchar *uid; 6966 6967 e_cal_component_get_uid (comp, &uid); 6968 6969 e_day_view_foreach_event_with_uid (day_view, uid, 6970 e_day_view_remove_event_cb, NULL); 6971 e_day_view_check_layout (day_view); 6972 gtk_widget_queue_draw (day_view->top_canvas); 6973 gtk_widget_queue_draw (day_view->main_canvas); 6974 goto out; 6975 } 6976 6977 /* Only update the summary if necessary. */ 6978 e_cal_component_get_summary (comp, &summary); 6979 if (summary.value && !strcmp (text, summary.value)) { 6980 if (day == E_DAY_VIEW_LONG_EVENT) 6981 e_day_view_reshape_long_event (day_view, event_num); 6982 else 6983 e_day_view_update_event_label ( 6984 day_view, day, 6985 event_num); 6986 } else if (summary.value || !string_is_empty (text)) { 6987 icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp); 6988 6989 summary.value = text; 6990 summary.altrep = NULL; 6991 e_cal_component_set_summary (comp, &summary); 6992 e_cal_component_commit_sequence (comp); 6993 6994 if (!on_server) { 6995 gchar *uid = NULL; 6996 GError *error = NULL; 6997 6998 e_cal_client_create_object_sync ( 6999 client, icalcomp, &uid, NULL, &error); 7000 7001 if (error != NULL) { 7002 uid = NULL; 7003 g_warning ( 7004 "%s: Could not create the object! %s", 7005 G_STRFUNC, error->message); 7006 g_error_free (error); 7007 } else { 7008 icalcomponent_set_uid (icalcomp, uid); 7009 e_calendar_view_emit_user_created ( 7010 E_CALENDAR_VIEW (day_view), client); 7011 } 7012 7013 g_free (uid); 7014 7015 /* we remove the object since we either got the update from the server or failed */ 7016 e_day_view_remove_event_cb (day_view, day, event_num, NULL); 7017 } else { 7018 CalObjModType mod = CALOBJ_MOD_ALL; 7019 GtkWindow *toplevel; 7020 if (e_cal_component_has_recurrences (comp)) { 7021 if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) { 7022 goto out; 7023 } 7024 7025 if (mod == CALOBJ_MOD_ALL) 7026 comp_util_sanitize_recurrence_master (comp, client); 7027 7028 if (mod == CALOBJ_MOD_THIS) { 7029 ECalComponentDateTime olddt, dt; 7030 icaltimetype itt; 7031 7032 dt.value = &itt; 7033 7034 e_cal_component_get_dtstart (comp, &olddt); 7035 if (olddt.value->zone) { 7036 *dt.value = icaltime_from_timet_with_zone ( 7037 event->comp_data->instance_start, 7038 olddt.value->is_date, 7039 olddt.value->zone); 7040 } else { 7041 *dt.value = icaltime_from_timet_with_zone ( 7042 event->comp_data->instance_start, 7043 olddt.value->is_date, 7044 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 7045 } 7046 dt.tzid = olddt.tzid; 7047 e_cal_component_set_dtstart (comp, &dt); 7048 dt.tzid = NULL; 7049 e_cal_component_free_datetime (&olddt); 7050 7051 e_cal_component_get_dtend (comp, &olddt); 7052 if (olddt.value->zone) { 7053 *dt.value = icaltime_from_timet_with_zone ( 7054 event->comp_data->instance_end, 7055 olddt.value->is_date, 7056 olddt.value->zone); 7057 } else { 7058 *dt.value = icaltime_from_timet_with_zone ( 7059 event->comp_data->instance_end, 7060 olddt.value->is_date, 7061 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 7062 } 7063 dt.tzid = olddt.tzid; 7064 e_cal_component_set_dtend (comp, &dt); 7065 dt.tzid = NULL; 7066 e_cal_component_free_datetime (&olddt); 7067 7068 e_cal_component_set_rdate_list (comp, NULL); 7069 e_cal_component_set_rrule_list (comp, NULL); 7070 e_cal_component_set_exdate_list (comp, NULL); 7071 e_cal_component_set_exrule_list (comp, NULL); 7072 7073 e_cal_component_commit_sequence (comp); 7074 } 7075 } else if (e_cal_component_is_instance (comp)) 7076 mod = CALOBJ_MOD_THIS; 7077 7078 /* FIXME When sending here, what exactly should we send? */ 7079 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view))); 7080 7081 e_calendar_view_modify_and_send ( 7082 E_CALENDAR_VIEW (day_view), 7083 comp, client, mod, toplevel, FALSE); 7084 } 7085 7086 } 7087 gtk_widget_queue_draw (day_view->main_canvas); 7088 7089 out: 7090 7091 g_object_unref (comp); 7092 g_free (text); 7093 7094 g_signal_emit_by_name (day_view, "selection_changed"); 7095 } 7096 7097 /* FIXME: It is possible that we may produce an invalid time due to daylight 7098 * saving times (i.e. when clocks go forward there is a range of time which 7099 * is not valid). I don't know the best way to handle daylight saving time. */ 7100 static time_t 7101 e_day_view_convert_grid_position_to_time (EDayView *day_view, 7102 gint col, 7103 gint row) 7104 { 7105 ECalendarView *cal_view; 7106 gint time_divisions; 7107 struct icaltimetype tt; 7108 time_t val; 7109 gint minutes; 7110 7111 cal_view = E_CALENDAR_VIEW (day_view); 7112 time_divisions = e_calendar_view_get_time_divisions (cal_view); 7113 7114 /* Calulate the number of minutes since the start of the day. */ 7115 minutes = day_view->first_hour_shown * 60 7116 + day_view->first_minute_shown 7117 + row * time_divisions; 7118 7119 /* A special case for midnight, where we can use the start of the 7120 * next day. */ 7121 if (minutes == 60 * 24) 7122 return day_view->day_starts[col + 1]; 7123 7124 /* Create an icaltimetype and convert to a time_t. */ 7125 tt = icaltime_from_timet_with_zone ( 7126 day_view->day_starts[col], 7127 FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 7128 tt.hour = minutes / 60; 7129 tt.minute = minutes % 60; 7130 tt.second = 0; 7131 7132 val = icaltime_as_timet_with_zone (tt, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 7133 return val; 7134 } 7135 7136 static gboolean 7137 e_day_view_convert_time_to_grid_position (EDayView *day_view, 7138 time_t time, 7139 gint *col, 7140 gint *row) 7141 { 7142 ECalendarView *cal_view; 7143 struct icaltimetype tt; 7144 gint time_divisions; 7145 gint day, minutes; 7146 7147 *col = *row = 0; 7148 7149 cal_view = E_CALENDAR_VIEW (day_view); 7150 time_divisions = e_calendar_view_get_time_divisions (cal_view); 7151 7152 if (time < day_view->lower || time >= day_view->upper) 7153 return FALSE; 7154 7155 /* We can find the column easily using the day_starts array. */ 7156 for (day = 1; day <= day_view->days_shown; day++) { 7157 if (time < day_view->day_starts[day]) { 7158 *col = day - 1; 7159 break; 7160 } 7161 } 7162 7163 /* To find the row we need to convert the time to an icaltimetype, 7164 * calculate the offset in minutes from the top of the display and 7165 * divide it by the mins per row setting. */ 7166 tt = icaltime_from_timet_with_zone (time, FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 7167 7168 minutes = tt.hour * 60 + tt.minute; 7169 minutes -= day_view->first_hour_shown * 60 + day_view->first_minute_shown; 7170 7171 *row = minutes / time_divisions; 7172 7173 if (*row < 0 || *row >= day_view->rows) 7174 return FALSE; 7175 7176 return TRUE; 7177 } 7178 7179 /* This starts or stops auto-scrolling when dragging a selection or resizing 7180 * an event. */ 7181 void 7182 e_day_view_check_auto_scroll (EDayView *day_view, 7183 gint event_x, 7184 gint event_y) 7185 { 7186 GtkAllocation allocation; 7187 gint scroll_x, scroll_y; 7188 7189 gnome_canvas_get_scroll_offsets ( 7190 GNOME_CANVAS (day_view->main_canvas), 7191 &scroll_x, &scroll_y); 7192 7193 event_x -= scroll_x; 7194 event_y -= scroll_y; 7195 7196 day_view->last_mouse_x = event_x; 7197 day_view->last_mouse_y = event_y; 7198 7199 gtk_widget_get_allocation (day_view->main_canvas, &allocation); 7200 7201 if (event_y < E_DAY_VIEW_AUTO_SCROLL_OFFSET) 7202 e_day_view_start_auto_scroll (day_view, TRUE); 7203 else if (event_y >= allocation.height - E_DAY_VIEW_AUTO_SCROLL_OFFSET) 7204 e_day_view_start_auto_scroll (day_view, FALSE); 7205 else 7206 e_day_view_stop_auto_scroll (day_view); 7207 } 7208 7209 static void 7210 e_day_view_start_auto_scroll (EDayView *day_view, 7211 gboolean scroll_up) 7212 { 7213 if (day_view->auto_scroll_timeout_id == 0) { 7214 day_view->auto_scroll_timeout_id = g_timeout_add (E_DAY_VIEW_AUTO_SCROLL_TIMEOUT, e_day_view_auto_scroll_handler, day_view); 7215 day_view->auto_scroll_delay = E_DAY_VIEW_AUTO_SCROLL_DELAY; 7216 } 7217 day_view->auto_scroll_up = scroll_up; 7218 } 7219 7220 void 7221 e_day_view_stop_auto_scroll (EDayView *day_view) 7222 { 7223 if (day_view->auto_scroll_timeout_id != 0) { 7224 g_source_remove (day_view->auto_scroll_timeout_id); 7225 day_view->auto_scroll_timeout_id = 0; 7226 } 7227 } 7228 7229 static gboolean 7230 e_day_view_auto_scroll_handler (gpointer data) 7231 { 7232 EDayView *day_view; 7233 ECalendarViewPosition pos; 7234 gint scroll_x, scroll_y, new_scroll_y, canvas_x, canvas_y, row, day; 7235 GtkAdjustment *adjustment; 7236 GtkScrollable *scrollable; 7237 gdouble step_increment; 7238 gdouble page_size; 7239 gdouble upper; 7240 7241 g_return_val_if_fail (E_IS_DAY_VIEW (data), FALSE); 7242 7243 day_view = E_DAY_VIEW (data); 7244 7245 if (day_view->auto_scroll_delay > 0) { 7246 day_view->auto_scroll_delay--; 7247 return TRUE; 7248 } 7249 7250 gnome_canvas_get_scroll_offsets ( 7251 GNOME_CANVAS (day_view->main_canvas), 7252 &scroll_x, &scroll_y); 7253 7254 scrollable = GTK_SCROLLABLE (day_view->main_canvas); 7255 adjustment = gtk_scrollable_get_vadjustment (scrollable); 7256 7257 step_increment = gtk_adjustment_get_step_increment (adjustment); 7258 page_size = gtk_adjustment_get_page_size (adjustment); 7259 upper = gtk_adjustment_get_upper (adjustment); 7260 7261 if (day_view->auto_scroll_up) 7262 new_scroll_y = MAX (scroll_y - step_increment, 0); 7263 else 7264 new_scroll_y = MIN ( 7265 scroll_y + step_increment, 7266 upper - page_size); 7267 7268 if (new_scroll_y != scroll_y) { 7269 /* NOTE: This reduces flicker, but only works if we don't use 7270 * canvas items which have X windows. */ 7271 gnome_canvas_scroll_to ( 7272 GNOME_CANVAS (day_view->main_canvas), 7273 scroll_x, new_scroll_y); 7274 } 7275 7276 canvas_x = day_view->last_mouse_x + scroll_x; 7277 canvas_y = day_view->last_mouse_y + new_scroll_y; 7278 7279 /* The last_mouse_x position is set to -1 when we are selecting using 7280 * the time column. In this case we set canvas_x to 0 and we ignore 7281 * the resulting day. */ 7282 if (day_view->last_mouse_x == -1) 7283 canvas_x = 0; 7284 7285 /* Update the selection/resize/drag if necessary. */ 7286 pos = e_day_view_convert_position_in_main_canvas ( 7287 day_view, 7288 canvas_x, canvas_y, 7289 &day, &row, NULL); 7290 7291 if (day_view->last_mouse_x == -1) 7292 day = -1; 7293 7294 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) { 7295 if (day_view->selection_is_being_dragged) { 7296 e_day_view_update_selection (day_view, day, row); 7297 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { 7298 e_day_view_update_resize (day_view, row); 7299 } else if (day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE) { 7300 e_day_view_update_main_canvas_drag (day_view, row, day); 7301 } 7302 } 7303 7304 return TRUE; 7305 } 7306 7307 gboolean 7308 e_day_view_get_event_rows (EDayView *day_view, 7309 gint day, 7310 gint event_num, 7311 gint *start_row_out, 7312 gint *end_row_out) 7313 { 7314 ECalendarView *cal_view; 7315 EDayViewEvent *event; 7316 gint time_divisions; 7317 gint start_row, end_row; 7318 7319 g_return_val_if_fail (day >= 0, FALSE); 7320 g_return_val_if_fail (day < E_DAY_VIEW_LONG_EVENT, FALSE); 7321 g_return_val_if_fail (event_num >= 0, FALSE); 7322 7323 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 7324 return FALSE; 7325 7326 cal_view = E_CALENDAR_VIEW (day_view); 7327 time_divisions = e_calendar_view_get_time_divisions (cal_view); 7328 7329 event = &g_array_index (day_view->events[day], EDayViewEvent, 7330 event_num); 7331 start_row = event->start_minute / time_divisions; 7332 end_row = (event->end_minute - 1) / time_divisions; 7333 if (end_row < start_row) 7334 end_row = start_row; 7335 7336 *start_row_out = start_row; 7337 *end_row_out = end_row; 7338 return TRUE; 7339 } 7340 7341 gboolean 7342 e_day_view_get_event_position (EDayView *day_view, 7343 gint day, 7344 gint event_num, 7345 gint *item_x, 7346 gint *item_y, 7347 gint *item_w, 7348 gint *item_h) 7349 { 7350 EDayViewEvent *event; 7351 gint start_row, end_row, cols_in_row, start_col, num_columns; 7352 7353 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 7354 return FALSE; 7355 7356 event = &g_array_index (day_view->events[day], EDayViewEvent, 7357 event_num); 7358 7359 /* If the event is flagged as not displayed, return FALSE. */ 7360 if (event->num_columns == 0) 7361 return FALSE; 7362 7363 e_day_view_get_event_rows (day_view, day, event_num, &start_row, &end_row); 7364 7365 cols_in_row = day_view->cols_per_row[day][start_row]; 7366 start_col = event->start_row_or_col; 7367 num_columns = event->num_columns; 7368 7369 if (cols_in_row == 0) 7370 return FALSE; 7371 7372 /* If the event is being resize, use the resize position. */ 7373 if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE 7374 && day_view->resize_event_day == day 7375 && day_view->resize_event_num == event_num) { 7376 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) 7377 start_row = day_view->resize_start_row; 7378 else if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_BOTTOM_EDGE) 7379 end_row = day_view->resize_end_row; 7380 } 7381 7382 *item_x = day_view->day_offsets[day] 7383 + day_view->day_widths[day] * start_col / cols_in_row; 7384 *item_w = day_view->day_widths[day] * num_columns / cols_in_row 7385 - E_DAY_VIEW_GAP_WIDTH; 7386 *item_w = MAX (*item_w, 0); 7387 *item_y = start_row * day_view->row_height; 7388 #if 0 7389 *item_h = (end_row - start_row + 1) * day_view->row_height; 7390 #else 7391 /* This makes the event end on the grid line of the next row, 7392 * which maybe looks nicer if you have 2 events on consecutive rows. */ 7393 *item_h = (end_row - start_row + 1) * day_view->row_height + 1; 7394 #endif 7395 return TRUE; 7396 } 7397 7398 gboolean 7399 e_day_view_get_long_event_position (EDayView *day_view, 7400 gint event_num, 7401 gint *start_day, 7402 gint *end_day, 7403 gint *item_x, 7404 gint *item_y, 7405 gint *item_w, 7406 gint *item_h) 7407 { 7408 EDayViewEvent *event; 7409 7410 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 7411 return FALSE; 7412 7413 event = &g_array_index (day_view->long_events, EDayViewEvent, 7414 event_num); 7415 7416 /* If the event is flagged as not displayed, return FALSE. */ 7417 if (event->num_columns == 0) 7418 return FALSE; 7419 7420 if (!e_day_view_find_long_event_days (event, 7421 day_view->days_shown, 7422 day_view->day_starts, 7423 start_day, end_day)) 7424 return FALSE; 7425 7426 /* If the event is being resize, use the resize position. */ 7427 if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE 7428 && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT 7429 && day_view->resize_event_num == event_num) { 7430 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE) 7431 *start_day = day_view->resize_start_row; 7432 else if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_RIGHT_EDGE) 7433 *end_day = day_view->resize_end_row; 7434 } 7435 7436 *item_x = day_view->day_offsets[*start_day] + E_DAY_VIEW_BAR_WIDTH; 7437 if (day_view->days_shown == 1) { 7438 GtkAllocation allocation; 7439 7440 gtk_widget_get_allocation (day_view->top_canvas, &allocation); 7441 *item_w = allocation.width; 7442 } else 7443 *item_w = day_view->day_offsets[*end_day + 1]; 7444 *item_w = MAX (*item_w - *item_x - E_DAY_VIEW_GAP_WIDTH, 0); 7445 *item_y = (event->start_row_or_col) * day_view->top_row_height; 7446 *item_h = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP; 7447 return TRUE; 7448 } 7449 7450 /* Converts a position within the entire top canvas to a day & event and 7451 * a place within the event if appropriate. If event_num_return is NULL, it 7452 * simply returns the grid position without trying to find the event. */ 7453 static ECalendarViewPosition 7454 e_day_view_convert_position_in_top_canvas (EDayView *day_view, 7455 gint x, 7456 gint y, 7457 gint *day_return, 7458 gint *event_num_return) 7459 { 7460 EDayViewEvent *event; 7461 gint day, row, col; 7462 gint event_num, start_day, end_day, item_x, item_y, item_w, item_h; 7463 7464 *day_return = -1; 7465 if (event_num_return) 7466 *event_num_return = -1; 7467 7468 if (x < 0 || y < 0) 7469 return E_CALENDAR_VIEW_POS_OUTSIDE; 7470 7471 row = y / day_view->top_row_height; 7472 7473 day = -1; 7474 for (col = 1; col <= day_view->days_shown; col++) { 7475 if (x < day_view->day_offsets[col]) { 7476 day = col - 1; 7477 break; 7478 } 7479 } 7480 if (day == -1) 7481 return E_CALENDAR_VIEW_POS_OUTSIDE; 7482 7483 *day_return = day; 7484 7485 /* If only the grid position is wanted, return. */ 7486 if (event_num_return == NULL) 7487 return E_CALENDAR_VIEW_POS_NONE; 7488 7489 for (event_num = 0; event_num < day_view->long_events->len; 7490 event_num++) { 7491 event = &g_array_index (day_view->long_events, EDayViewEvent, 7492 event_num); 7493 7494 if (event->start_row_or_col != row) 7495 continue; 7496 7497 if (!e_day_view_get_long_event_position (day_view, event_num, 7498 &start_day, &end_day, 7499 &item_x, &item_y, 7500 &item_w, &item_h)) 7501 continue; 7502 7503 if (x < item_x) 7504 continue; 7505 7506 if (x >= item_x + item_w) 7507 continue; 7508 7509 *event_num_return = event_num; 7510 7511 if (x < item_x + E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH 7512 + E_DAY_VIEW_LONG_EVENT_X_PAD) 7513 return E_CALENDAR_VIEW_POS_LEFT_EDGE; 7514 7515 if (x >= item_x + item_w - E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH 7516 - E_DAY_VIEW_LONG_EVENT_X_PAD) 7517 return E_CALENDAR_VIEW_POS_RIGHT_EDGE; 7518 7519 return E_CALENDAR_VIEW_POS_EVENT; 7520 } 7521 7522 return E_CALENDAR_VIEW_POS_NONE; 7523 } 7524 7525 /* Converts a position within the entire main canvas to a day, row, event and 7526 * a place within the event if appropriate. If event_num_return is NULL, it 7527 * simply returns the grid position without trying to find the event. */ 7528 static ECalendarViewPosition 7529 e_day_view_convert_position_in_main_canvas (EDayView *day_view, 7530 gint x, 7531 gint y, 7532 gint *day_return, 7533 gint *row_return, 7534 gint *event_num_return) 7535 { 7536 gint day, row, col, event_num; 7537 gint item_x, item_y, item_w, item_h; 7538 7539 #if 0 7540 g_print ("e_day_view_convert_position_in_main_canvas: (%d, %d)\n", x, y); 7541 #endif 7542 7543 *day_return = -1; 7544 *row_return = -1; 7545 if (event_num_return) 7546 *event_num_return = -1; 7547 7548 /* Check the position is inside the canvas, and determine the day 7549 * and row. */ 7550 if (x < 0 || y < 0) 7551 return E_CALENDAR_VIEW_POS_OUTSIDE; 7552 7553 row = y / day_view->row_height; 7554 if (row >= day_view->rows) 7555 return E_CALENDAR_VIEW_POS_OUTSIDE; 7556 7557 day = -1; 7558 for (col = 1; col <= day_view->days_shown; col++) { 7559 if (x < day_view->day_offsets[col]) { 7560 day = col - 1; 7561 break; 7562 } 7563 } 7564 if (day == -1) 7565 return E_CALENDAR_VIEW_POS_OUTSIDE; 7566 7567 *day_return = day; 7568 *row_return = row; 7569 7570 /* If only the grid position is wanted, return. */ 7571 if (event_num_return == NULL) 7572 return E_CALENDAR_VIEW_POS_NONE; 7573 7574 /* Check the selected item first, since the horizontal resizing bars 7575 * may be above other events. */ 7576 if (day_view->resize_bars_event_day == day) { 7577 if (e_day_view_get_event_position (day_view, day, 7578 day_view->resize_bars_event_num, 7579 &item_x, &item_y, 7580 &item_w, &item_h)) { 7581 if (x >= item_x && x < item_x + item_w) { 7582 *event_num_return = day_view->resize_bars_event_num; 7583 if (y >= item_y - E_DAY_VIEW_BAR_HEIGHT 7584 && y < item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT) 7585 return E_CALENDAR_VIEW_POS_TOP_EDGE; 7586 if (y >= item_y + item_h - E_DAY_VIEW_EVENT_BORDER_HEIGHT 7587 && y < item_y + item_h + E_DAY_VIEW_BAR_HEIGHT) 7588 return E_CALENDAR_VIEW_POS_BOTTOM_EDGE; 7589 } 7590 } 7591 } 7592 7593 /* Try to find the event at the found position. */ 7594 *event_num_return = -1; 7595 for (event_num = 0; event_num < day_view->events[day]->len; 7596 event_num++) { 7597 if (!e_day_view_get_event_position (day_view, day, event_num, 7598 &item_x, &item_y, 7599 &item_w, &item_h)) 7600 continue; 7601 7602 if (x < item_x || x >= item_x + item_w 7603 || y < item_y || y >= item_y + item_h) 7604 continue; 7605 7606 *event_num_return = event_num; 7607 7608 if (x < item_x + E_DAY_VIEW_BAR_WIDTH) 7609 return E_CALENDAR_VIEW_POS_LEFT_EDGE; 7610 7611 if (y < item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT 7612 + E_DAY_VIEW_EVENT_Y_PAD) 7613 return E_CALENDAR_VIEW_POS_TOP_EDGE; 7614 7615 if (y >= item_y + item_h - E_DAY_VIEW_EVENT_BORDER_HEIGHT 7616 - E_DAY_VIEW_EVENT_Y_PAD) 7617 return E_CALENDAR_VIEW_POS_BOTTOM_EDGE; 7618 7619 return E_CALENDAR_VIEW_POS_EVENT; 7620 } 7621 7622 return E_CALENDAR_VIEW_POS_NONE; 7623 } 7624 7625 static gboolean 7626 e_day_view_on_top_canvas_drag_motion (GtkWidget *widget, 7627 GdkDragContext *context, 7628 gint x, 7629 gint y, 7630 guint time, 7631 EDayView *day_view) 7632 { 7633 gint scroll_x, scroll_y; 7634 7635 gnome_canvas_get_scroll_offsets ( 7636 GNOME_CANVAS (widget), 7637 &scroll_x, &scroll_y); 7638 day_view->drag_event_x = x + scroll_x; 7639 day_view->drag_event_y = y + scroll_y; 7640 7641 e_day_view_reshape_top_canvas_drag_item (day_view); 7642 7643 return TRUE; 7644 } 7645 7646 static void 7647 e_day_view_reshape_top_canvas_drag_item (EDayView *day_view) 7648 { 7649 ECalendarViewPosition pos; 7650 gint x, y, day; 7651 7652 /* Calculate the day & start row of the event being dragged, using 7653 * the current mouse position. */ 7654 x = day_view->drag_event_x; 7655 y = day_view->drag_event_y; 7656 pos = e_day_view_convert_position_in_top_canvas ( 7657 day_view, x, y, 7658 &day, NULL); 7659 /* This shouldn't really happen in a drag. */ 7660 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE) 7661 return; 7662 7663 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) 7664 day -= day_view->drag_event_offset; 7665 day = MAX (day, 0); 7666 7667 e_day_view_update_top_canvas_drag (day_view, day); 7668 } 7669 7670 static void 7671 e_day_view_update_top_canvas_drag (EDayView *day_view, 7672 gint day) 7673 { 7674 EDayViewEvent *event = NULL; 7675 gint row, num_days, start_day, end_day; 7676 gdouble item_x, item_y, item_w, item_h; 7677 gchar *text; 7678 7679 /* Calculate the event's position. If the event is in the same 7680 * position we started in, we use the same columns. */ 7681 row = day_view->rows_in_top_display + 1; 7682 num_days = 1; 7683 7684 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) { 7685 if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num)) 7686 return; 7687 7688 event = &g_array_index (day_view->long_events, EDayViewEvent, 7689 day_view->drag_event_num); 7690 row = event->start_row_or_col + 1; 7691 7692 if (!e_day_view_find_long_event_days (event, 7693 day_view->days_shown, 7694 day_view->day_starts, 7695 &start_day, &end_day)) 7696 return; 7697 7698 num_days = end_day - start_day + 1; 7699 7700 /* Make sure we don't go off the screen. */ 7701 day = MIN (day, day_view->days_shown - num_days); 7702 7703 } else if (day_view->drag_event_day != -1) { 7704 if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num)) 7705 return; 7706 7707 event = &g_array_index (day_view->events[day_view->drag_event_day], 7708 EDayViewEvent, 7709 day_view->drag_event_num); 7710 } 7711 7712 /* If the position hasn't changed, just return. */ 7713 if (day_view->drag_last_day == day 7714 && (day_view->drag_long_event_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) 7715 return; 7716 7717 day_view->drag_last_day = day; 7718 7719 item_x = day_view->day_offsets[day] + E_DAY_VIEW_BAR_WIDTH; 7720 item_w = day_view->day_offsets[day + num_days] - item_x 7721 - E_DAY_VIEW_GAP_WIDTH; 7722 item_y = row * day_view->top_row_height; 7723 item_h = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP; 7724 7725 /* Set the positions of the event & associated items. */ 7726 gnome_canvas_item_set ( 7727 day_view->drag_long_event_rect_item, 7728 "x1", item_x, 7729 "y1", item_y, 7730 "x2", item_x + item_w - 1, 7731 "y2", item_y + item_h - 1, 7732 NULL); 7733 7734 gnome_canvas_item_set ( 7735 day_view->drag_long_event_item, 7736 "clip_width", item_w - (E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD) * 2, 7737 "clip_height", item_h - (E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD) * 2, 7738 NULL); 7739 e_canvas_item_move_absolute ( 7740 day_view->drag_long_event_item, 7741 item_x + E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD, 7742 item_y + E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD); 7743 7744 if (!(day_view->drag_long_event_rect_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) { 7745 gnome_canvas_item_raise_to_top (day_view->drag_long_event_rect_item); 7746 gnome_canvas_item_show (day_view->drag_long_event_rect_item); 7747 } 7748 7749 /* Set the text, if necessary. We don't want to set the text every 7750 * time it moves, so we check if it is currently invisible and only 7751 * set the text then. */ 7752 if (!(day_view->drag_long_event_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) { 7753 const gchar *summary; 7754 7755 if (event && is_comp_data_valid (event)) { 7756 summary = icalcomponent_get_summary (event->comp_data->icalcomp); 7757 text = g_strdup (summary); 7758 } else { 7759 text = NULL; 7760 } 7761 7762 gnome_canvas_item_set ( 7763 day_view->drag_long_event_item, 7764 "text", text ? text : "", 7765 NULL); 7766 gnome_canvas_item_raise_to_top (day_view->drag_long_event_item); 7767 gnome_canvas_item_show (day_view->drag_long_event_item); 7768 7769 g_free (text); 7770 } 7771 } 7772 7773 static gboolean 7774 e_day_view_on_main_canvas_drag_motion (GtkWidget *widget, 7775 GdkDragContext *context, 7776 gint x, 7777 gint y, 7778 guint time, 7779 EDayView *day_view) 7780 { 7781 gint scroll_x, scroll_y; 7782 7783 gnome_canvas_get_scroll_offsets ( 7784 GNOME_CANVAS (widget), 7785 &scroll_x, &scroll_y); 7786 7787 day_view->drag_event_x = x + scroll_x; 7788 day_view->drag_event_y = y + scroll_y; 7789 7790 e_day_view_reshape_main_canvas_drag_item (day_view); 7791 e_day_view_reshape_main_canvas_resize_bars (day_view); 7792 7793 e_day_view_check_auto_scroll (day_view, day_view->drag_event_x, day_view->drag_event_y); 7794 7795 return TRUE; 7796 } 7797 7798 static void 7799 e_day_view_reshape_main_canvas_drag_item (EDayView *day_view) 7800 { 7801 ECalendarViewPosition pos; 7802 gint x, y, day, row; 7803 7804 /* Calculate the day & start row of the event being dragged, using 7805 * the current mouse position. */ 7806 x = day_view->drag_event_x; 7807 y = day_view->drag_event_y; 7808 pos = e_day_view_convert_position_in_main_canvas ( 7809 day_view, x, y, 7810 &day, &row, NULL); 7811 /* This shouldn't really happen in a drag. */ 7812 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE) 7813 return; 7814 7815 if (day_view->drag_event_day != -1 7816 && day_view->drag_event_day != E_DAY_VIEW_LONG_EVENT) 7817 row -= day_view->drag_event_offset; 7818 row = MAX (row, 0); 7819 7820 e_day_view_update_main_canvas_drag (day_view, row, day); 7821 } 7822 7823 static void 7824 e_day_view_update_main_canvas_drag (EDayView *day_view, 7825 gint row, 7826 gint day) 7827 { 7828 EDayViewEvent *event = NULL; 7829 ECalendarView *cal_view; 7830 gint time_divisions; 7831 gint cols_in_row, start_col, num_columns, num_rows, start_row, end_row; 7832 gdouble item_x, item_y, item_w, item_h; 7833 gchar *text; 7834 7835 cal_view = E_CALENDAR_VIEW (day_view); 7836 time_divisions = e_calendar_view_get_time_divisions (cal_view); 7837 7838 /* If the position hasn't changed, just return. */ 7839 if (day_view->drag_last_day == day 7840 && day_view->drag_last_row == row 7841 && (day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) 7842 return; 7843 7844 day_view->drag_last_day = day; 7845 day_view->drag_last_row = row; 7846 7847 /* Calculate the event's position. If the event is in the same 7848 * position we started in, we use the same columns. */ 7849 cols_in_row = 1; 7850 start_row = 0; 7851 start_col = 0; 7852 num_columns = 1; 7853 num_rows = 1; 7854 7855 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) { 7856 if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num)) 7857 return; 7858 7859 event = &g_array_index (day_view->long_events, EDayViewEvent, 7860 day_view->drag_event_num); 7861 } else if (day_view->drag_event_day != -1) { 7862 if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num)) 7863 return; 7864 7865 event = &g_array_index (day_view->events[day_view->drag_event_day], 7866 EDayViewEvent, 7867 day_view->drag_event_num); 7868 start_row = event->start_minute / time_divisions; 7869 end_row = (event->end_minute - 1) / time_divisions; 7870 if (end_row < start_row) 7871 end_row = start_row; 7872 7873 num_rows = end_row - start_row + 1; 7874 } 7875 7876 if (day_view->drag_event_day == day && start_row == row) { 7877 cols_in_row = day_view->cols_per_row[day][row]; 7878 start_col = event->start_row_or_col;
Access to field 'start_row_or_col' results in a dereference of a null pointer (loaded from variable 'event')
(emitted by clang-analyzer)

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

Access to field 'start_row_or_col' results in a dereference of a null pointer (loaded from variable 'event')
(emitted by clang-analyzer)

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

7879 num_columns = event->num_columns; 7880 } 7881 7882 item_x = day_view->day_offsets[day] 7883 + day_view->day_widths[day] * start_col / cols_in_row; 7884 item_w = day_view->day_widths[day] * num_columns / cols_in_row 7885 - E_DAY_VIEW_GAP_WIDTH; 7886 item_y = row * day_view->row_height; 7887 item_h = num_rows * day_view->row_height; 7888 7889 /* Set the positions of the event & associated items. */ 7890 gnome_canvas_item_set ( 7891 day_view->drag_rect_item, 7892 "x1", item_x + E_DAY_VIEW_BAR_WIDTH - 1, 7893 "y1", item_y, 7894 "x2", item_x + item_w - 1, 7895 "y2", item_y + item_h - 1, 7896 NULL); 7897 7898 gnome_canvas_item_set ( 7899 day_view->drag_bar_item, 7900 "x1", item_x, 7901 "y1", item_y, 7902 "x2", item_x + E_DAY_VIEW_BAR_WIDTH - 1, 7903 "y2", item_y + item_h - 1, 7904 NULL); 7905 7906 gnome_canvas_item_set ( 7907 day_view->drag_item, 7908 "clip_width", item_w - E_DAY_VIEW_BAR_WIDTH - E_DAY_VIEW_EVENT_X_PAD * 2, 7909 "clip_height", item_h - (E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD) * 2, 7910 NULL); 7911 e_canvas_item_move_absolute ( 7912 day_view->drag_item, 7913 item_x + E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD, 7914 item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD); 7915 7916 if (!(day_view->drag_bar_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) { 7917 gnome_canvas_item_raise_to_top (day_view->drag_bar_item); 7918 gnome_canvas_item_show (day_view->drag_bar_item); 7919 } 7920 7921 if (!(day_view->drag_rect_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) { 7922 gnome_canvas_item_raise_to_top (day_view->drag_rect_item); 7923 gnome_canvas_item_show (day_view->drag_rect_item); 7924 } 7925 7926 /* Set the text, if necessary. We don't want to set the text every 7927 * time it moves, so we check if it is currently invisible and only 7928 * set the text then. */ 7929 if (!(day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) { 7930 const gchar *summary; 7931 7932 if (event && is_comp_data_valid (event)) { 7933 summary = icalcomponent_get_summary (event->comp_data->icalcomp); 7934 text = g_strdup (summary); 7935 } else { 7936 text = NULL; 7937 } 7938 7939 gnome_canvas_item_set ( 7940 day_view->drag_item, 7941 "text", text ? text : "", 7942 NULL); 7943 gnome_canvas_item_raise_to_top (day_view->drag_item); 7944 gnome_canvas_item_show (day_view->drag_item); 7945 7946 g_free (text); 7947 } 7948 } 7949 7950 static void 7951 e_day_view_on_top_canvas_drag_leave (GtkWidget *widget, 7952 GdkDragContext *context, 7953 guint time, 7954 EDayView *day_view) 7955 { 7956 day_view->drag_last_day = -1; 7957 7958 gnome_canvas_item_hide (day_view->drag_long_event_rect_item); 7959 gnome_canvas_item_hide (day_view->drag_long_event_item); 7960 } 7961 7962 static void 7963 e_day_view_on_main_canvas_drag_leave (GtkWidget *widget, 7964 GdkDragContext *context, 7965 guint time, 7966 EDayView *day_view) 7967 { 7968 day_view->drag_last_day = -1; 7969 7970 e_day_view_stop_auto_scroll (day_view); 7971 7972 gnome_canvas_item_hide (day_view->drag_rect_item); 7973 gnome_canvas_item_hide (day_view->drag_bar_item); 7974 gnome_canvas_item_hide (day_view->drag_item); 7975 7976 /* Hide the resize bars if they are being used in the drag. */ 7977 if (day_view->drag_event_day == day_view->resize_bars_event_day 7978 && day_view->drag_event_num == day_view->resize_bars_event_num) { 7979 } 7980 } 7981 7982 static void 7983 e_day_view_on_drag_begin (GtkWidget *widget, 7984 GdkDragContext *context, 7985 EDayView *day_view) 7986 { 7987 EDayViewEvent *event; 7988 gint day, event_num; 7989 7990 day = day_view->drag_event_day; 7991 event_num = day_view->drag_event_num; 7992 7993 /* These should both be set. */ 7994 g_return_if_fail (day != -1); 7995 g_return_if_fail (event_num != -1); 7996 7997 if (day == E_DAY_VIEW_LONG_EVENT) { 7998 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 7999 return; 8000 8001 event = &g_array_index (day_view->long_events, EDayViewEvent, 8002 event_num); 8003 } else { 8004 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 8005 return; 8006 8007 event = &g_array_index (day_view->events[day], EDayViewEvent, 8008 event_num); 8009 } 8010 8011 /* Hide the text item, since it will be shown in the special drag 8012 * items. */ 8013 gnome_canvas_item_hide (event->canvas_item); 8014 } 8015 8016 static void 8017 e_day_view_on_drag_end (GtkWidget *widget, 8018 GdkDragContext *context, 8019 EDayView *day_view) 8020 { 8021 EDayViewEvent *event; 8022 gint day, event_num; 8023 8024 day = day_view->drag_event_day; 8025 event_num = day_view->drag_event_num; 8026 8027 /* If the calendar has already been updated in drag_data_received() 8028 * we just return. */ 8029 if (day == -1 || event_num == -1) 8030 return; 8031 8032 if (day == E_DAY_VIEW_LONG_EVENT) { 8033 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 8034 return; 8035 8036 event = &g_array_index (day_view->long_events, EDayViewEvent, 8037 event_num); 8038 gtk_widget_queue_draw (day_view->top_canvas); 8039 } else { 8040 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 8041 return; 8042 8043 event = &g_array_index (day_view->events[day], EDayViewEvent, 8044 event_num); 8045 gtk_widget_queue_draw (day_view->main_canvas); 8046 } 8047 8048 /* Show the text item again. */ 8049 gnome_canvas_item_show (event->canvas_item); 8050 8051 day_view->drag_event_day = -1; 8052 day_view->drag_event_num = -1; 8053 } 8054 8055 static void 8056 e_day_view_on_drag_data_get (GtkWidget *widget, 8057 GdkDragContext *context, 8058 GtkSelectionData *selection_data, 8059 guint info, 8060 guint time, 8061 EDayView *day_view) 8062 { 8063 EDayViewEvent *event; 8064 icalcomponent *vcal; 8065 gint day, event_num; 8066 gchar *comp_str; 8067 8068 day = day_view->drag_event_day; 8069 event_num = day_view->drag_event_num; 8070 8071 /* These should both be set. */ 8072 g_return_if_fail (day != -1); 8073 g_return_if_fail (event_num != -1); 8074 8075 if (day == E_DAY_VIEW_LONG_EVENT) { 8076 if (!is_array_index_in_bounds (day_view->long_events, event_num)) 8077 return; 8078 8079 event = &g_array_index (day_view->long_events, 8080 EDayViewEvent, event_num); 8081 } else { 8082 if (!is_array_index_in_bounds (day_view->events[day], event_num)) 8083 return; 8084 8085 event = &g_array_index (day_view->events[day], 8086 EDayViewEvent, event_num); 8087 } 8088 8089 if (!is_comp_data_valid (event)) 8090 return; 8091 8092 vcal = e_cal_util_new_top_level (); 8093 e_cal_util_add_timezones_from_component ( 8094 vcal, event->comp_data->icalcomp); 8095 icalcomponent_add_component ( 8096 vcal, icalcomponent_new_clone (event->comp_data->icalcomp)); 8097 8098 comp_str = icalcomponent_as_ical_string_r (vcal); 8099 if (comp_str) { 8100 ESource *source; 8101 const gchar *source_uid; 8102 GdkAtom target; 8103 gchar *tmp; 8104 8105 source = e_client_get_source (E_CLIENT (event->comp_data->client)); 8106 source_uid = e_source_get_uid (source); 8107 8108 tmp = g_strconcat (source_uid, "\n", comp_str, NULL); 8109 target = gtk_selection_data_get_target (selection_data); 8110 gtk_selection_data_set ( 8111 selection_data, target, 8, 8112 (guchar *) tmp, strlen (tmp)); 8113 8114 g_free (tmp); 8115 } 8116 8117 icalcomponent_free (vcal); 8118 g_free (comp_str); 8119 } 8120 8121 static void 8122 e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget, 8123 GdkDragContext *context, 8124 gint x, 8125 gint y, 8126 GtkSelectionData *selection_data, 8127 guint info, 8128 guint time, 8129 EDayView *day_view) 8130 { 8131 EDayViewEvent *event = NULL; 8132 ECalendarViewPosition pos; 8133 gint day, start_day, end_day, num_days; 8134 gint start_offset, end_offset; 8135 ECalComponent *comp; 8136 ECalComponentDateTime date; 8137 ESourceRegistry *registry; 8138 struct icaltimetype itt; 8139 time_t dt; 8140 gboolean all_day_event; 8141 ECalClient *client; 8142 ECalModel *model; 8143 ECalendarView *cal_view; 8144 gboolean drag_from_same_window; 8145 const guchar *data; 8146 gint format, length; 8147 8148 data = gtk_selection_data_get_data (selection_data); 8149 format = gtk_selection_data_get_format (selection_data); 8150 length = gtk_selection_data_get_length (selection_data); 8151 8152 if (day_view->drag_event_day != -1) 8153 drag_from_same_window = TRUE; 8154 else 8155 drag_from_same_window = FALSE; 8156 8157 cal_view = E_CALENDAR_VIEW (day_view); 8158 model = e_calendar_view_get_model (cal_view); 8159 8160 registry = e_cal_model_get_registry (model); 8161 client = e_cal_model_get_default_client (model); 8162 8163 /* Note that we only support DnD within the EDayView at present. */ 8164 if (length >= 0 && format == 8 && day_view->drag_event_day != -1) { 8165 /* We are dragging in the same window */ 8166 8167 pos = e_day_view_convert_position_in_top_canvas ( 8168 day_view, 8169 x, y, &day, 8170 NULL); 8171 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) { 8172 CalObjModType mod = CALOBJ_MOD_ALL; 8173 GtkWindow *toplevel; 8174 8175 num_days = 1; 8176 start_offset = 0; 8177 end_offset = 0; 8178 8179 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) { 8180 if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num)) 8181 return; 8182 8183 event = &g_array_index (day_view->long_events, EDayViewEvent, 8184 day_view->drag_event_num); 8185 8186 if (!is_comp_data_valid (event)) 8187 return; 8188 8189 day -= day_view->drag_event_offset; 8190 day = MAX (day, 0); 8191 8192 e_day_view_find_long_event_days ( 8193 event, 8194 day_view->days_shown, 8195 day_view->day_starts, 8196 &start_day, 8197 &end_day); 8198 num_days = end_day - start_day + 1; 8199 /* Make sure we don't go off the screen. */ 8200 day = MIN (day, day_view->days_shown - num_days); 8201 8202 start_offset = event->start_minute; 8203 end_offset = event->end_minute; 8204 } else { 8205 if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num)) 8206 return; 8207 8208 event = &g_array_index (day_view->events[day_view->drag_event_day], 8209 EDayViewEvent, 8210 day_view->drag_event_num); 8211 8212 if (!is_comp_data_valid (event)) 8213 return; 8214 } 8215 8216 client = event->comp_data->client; 8217 8218 /* We clone the event since we don't want to change 8219 * the original comp here. 8220 * Otherwise we would not detect that the event's time 8221 * had changed in the "update_event" callback. */ 8222 8223 comp = e_cal_component_new (); 8224 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 8225 8226 if (e_cal_component_has_attendees (comp) && 8227 !itip_organizer_is_user (registry, comp, client)) { 8228 g_object_unref (comp); 8229 return; 8230 } 8231 8232 if (start_offset == 0 && end_offset == 0) 8233 all_day_event = TRUE; 8234 else 8235 all_day_event = FALSE; 8236 8237 date.value = &itt; 8238 8239 dt = day_view->day_starts[day] + start_offset * 60; 8240 itt = icaltime_from_timet_with_zone ( 8241 dt, FALSE, 8242 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 8243 if (all_day_event) { 8244 itt.is_date = TRUE; 8245 date.tzid = NULL; 8246 } else { 8247 /* FIXME: Should probably keep the timezone of 8248 * the original start and end times. */ 8249 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 8250 } 8251 cal_comp_set_dtstart_with_oldzone (client, comp, &date); 8252 8253 if (end_offset == 0) 8254 dt = day_view->day_starts[day + num_days]; 8255 else 8256 dt = day_view->day_starts[day + num_days - 1] + end_offset * 60; 8257 itt = icaltime_from_timet_with_zone ( 8258 dt, FALSE, 8259 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 8260 if (all_day_event) { 8261 itt.is_date = TRUE; 8262 date.tzid = NULL; 8263 } else { 8264 /* FIXME: Should probably keep the timezone of 8265 * the original start and end times. */ 8266 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 8267 } 8268 cal_comp_set_dtend_with_oldzone (client, comp, &date); 8269 8270 gtk_drag_finish (context, TRUE, TRUE, time); 8271 8272 /* Reset this since it will be invalid. */ 8273 day_view->drag_event_day = -1; 8274 8275 /* Show the text item again, just in case it hasn't 8276 * moved. If we don't do this it may not appear. */ 8277 if (event->canvas_item) 8278 gnome_canvas_item_show (event->canvas_item); 8279 8280 e_cal_component_commit_sequence (comp); 8281 if (e_cal_component_has_recurrences (comp)) { 8282 if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) { 8283 g_object_unref (comp); 8284 return; 8285 } 8286 8287 if (mod == CALOBJ_MOD_ALL) 8288 comp_util_sanitize_recurrence_master (comp, client); 8289 8290 if (mod == CALOBJ_MOD_THIS) { 8291 e_cal_component_set_rdate_list (comp, NULL); 8292 e_cal_component_set_rrule_list (comp, NULL); 8293 e_cal_component_set_exdate_list (comp, NULL); 8294 e_cal_component_set_exrule_list (comp, NULL); 8295 } 8296 } else if (e_cal_component_is_instance (comp)) 8297 mod = CALOBJ_MOD_THIS; 8298 8299 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view))); 8300 8301 e_calendar_view_modify_and_send ( 8302 E_CALENDAR_VIEW (day_view), 8303 comp, client, mod, toplevel, FALSE); 8304 8305 g_object_unref (comp); 8306 8307 return; 8308 } 8309 } 8310 8311 if (length >= 0 && format == 8 && !drag_from_same_window) { 8312 /* We are dragging between different window */ 8313 8314 icalcomponent *icalcomp; 8315 icalcomponent_kind kind; 8316 time_t dtstart; 8317 icaltimezone *default_zone; 8318 8319 pos = e_day_view_convert_position_in_top_canvas ( 8320 day_view, 8321 x, y, &day, 8322 NULL); 8323 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE) 8324 goto error; 8325 8326 icalcomp = icalparser_parse_string ((const gchar *) data); 8327 if (!icalcomp) 8328 goto error; 8329 8330 default_zone = e_cal_model_get_timezone (model); 8331 8332 /* check the type of the component */ 8333 kind = icalcomponent_isa (icalcomp); 8334 if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT) 8335 goto error; 8336 8337 dtstart = day_view->day_starts[day]; 8338 8339 if (kind == ICAL_VCALENDAR_COMPONENT) { 8340 icalcomponent_kind child_kind; 8341 icalcomponent *subcomp; 8342 8343 subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT); 8344 while (subcomp) { 8345 child_kind = icalcomponent_isa (subcomp); 8346 if (child_kind == ICAL_VEVENT_COMPONENT) 8347 e_calendar_view_add_event ( 8348 E_CALENDAR_VIEW (day_view), client, dtstart, 8349 default_zone, subcomp, TRUE); 8350 else if (child_kind == ICAL_VTIMEZONE_COMPONENT) { 8351 icaltimezone *zone; 8352 8353 zone = icaltimezone_new (); 8354 icaltimezone_set_component (zone, subcomp); 8355 e_cal_client_add_timezone_sync (client, zone, NULL, NULL); 8356 8357 icaltimezone_free (zone, 1); 8358 } 8359 8360 subcomp = icalcomponent_get_next_component ( 8361 icalcomp, ICAL_ANY_COMPONENT); 8362 } 8363 8364 icalcomponent_free (icalcomp); 8365 8366 } else { 8367 e_calendar_view_add_event (E_CALENDAR_VIEW (day_view), client, dtstart, default_zone, icalcomp, TRUE); 8368 } 8369 8370 gtk_drag_finish (context, TRUE, TRUE, time); 8371 return; 8372 } 8373 8374 error: 8375 gtk_drag_finish (context, FALSE, FALSE, time); 8376 } 8377 8378 static void 8379 e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget, 8380 GdkDragContext *context, 8381 gint x, 8382 gint y, 8383 GtkSelectionData *selection_data, 8384 guint info, 8385 guint time, 8386 EDayView *day_view) 8387 { 8388 ECalendarView *cal_view; 8389 EDayViewEvent *event = NULL; 8390 ECalendarViewPosition pos; 8391 gint time_divisions; 8392 gint day, row, start_row, end_row, num_rows, scroll_x, scroll_y; 8393 gint start_offset, end_offset; 8394 ECalModel *model; 8395 ECalComponent *comp; 8396 ECalComponentDateTime date; 8397 ESourceRegistry *registry; 8398 struct icaltimetype itt; 8399 time_t dt; 8400 ECalClient *client; 8401 gboolean drag_from_same_window; 8402 const guchar *data; 8403 gint format, length; 8404 8405 cal_view = E_CALENDAR_VIEW (day_view); 8406 model = e_calendar_view_get_model (cal_view); 8407 time_divisions = e_calendar_view_get_time_divisions (cal_view); 8408 8409 registry = e_cal_model_get_registry (model); 8410 8411 data = gtk_selection_data_get_data (selection_data); 8412 format = gtk_selection_data_get_format (selection_data); 8413 length = gtk_selection_data_get_length (selection_data); 8414 8415 if (day_view->drag_event_day != -1) 8416 drag_from_same_window = TRUE; 8417 else 8418 drag_from_same_window = FALSE; 8419 8420 client = e_cal_model_get_default_client (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view))); 8421 8422 gnome_canvas_get_scroll_offsets ( 8423 GNOME_CANVAS (widget), 8424 &scroll_x, &scroll_y); 8425 x += scroll_x; 8426 y += scroll_y; 8427 8428 /* Note that we only support DnD within the EDayView at present. */ 8429 if (length >= 0 && format == 8 && (day_view->drag_event_day != -1)) { 8430 /* We are dragging in the same window */ 8431 8432 pos = e_day_view_convert_position_in_main_canvas ( 8433 day_view, 8434 x, y, &day, 8435 &row, NULL); 8436 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) { 8437 CalObjModType mod = CALOBJ_MOD_ALL; 8438 GtkWindow *toplevel; 8439 8440 num_rows = 1; 8441 start_offset = 0; 8442 end_offset = 0; 8443 8444 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) { 8445 if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num)) 8446 return; 8447 8448 event = &g_array_index (day_view->long_events, EDayViewEvent, 8449 day_view->drag_event_num); 8450 8451 if (!is_comp_data_valid (event)) 8452 return; 8453 } else { 8454 if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num)) 8455 return; 8456 8457 event = &g_array_index (day_view->events[day_view->drag_event_day], 8458 EDayViewEvent, 8459 day_view->drag_event_num); 8460 8461 if (!is_comp_data_valid (event)) 8462 return; 8463 8464 row -= day_view->drag_event_offset; 8465 8466 /* Calculate time offset from start row. */ 8467 start_row = event->start_minute / time_divisions; 8468 end_row = (event->end_minute - 1) / time_divisions; 8469 if (end_row < start_row) 8470 end_row = start_row; 8471 8472 num_rows = end_row - start_row + 1; 8473 8474 start_offset = event->start_minute % time_divisions; 8475 end_offset = event->end_minute % time_divisions; 8476 if (end_offset != 0) 8477 end_offset = time_divisions - end_offset; 8478 } 8479 8480 client = event->comp_data->client; 8481 8482 /* We use a temporary shallow copy of comp since we 8483 * don't want to change the original comp here. 8484 * Otherwise we would not detect that the event's time 8485 * had changed in the "update_event" callback. */ 8486 comp = e_cal_component_new (); 8487 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 8488 8489 if (e_cal_component_has_attendees (comp) && 8490 !itip_organizer_is_user (registry, comp, client)) { 8491 g_object_unref (comp); 8492 return; 8493 } 8494 8495 date.value = &itt; 8496 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 8497 8498 dt = e_day_view_convert_grid_position_to_time (day_view, day, row) + start_offset * 60; 8499 *date.value = icaltime_from_timet_with_zone (dt, FALSE, 8500 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 8501 cal_comp_set_dtstart_with_oldzone (client, comp, &date); 8502 dt = e_day_view_convert_grid_position_to_time (day_view, day, row + num_rows) - end_offset * 60; 8503 *date.value = icaltime_from_timet_with_zone (dt, FALSE, 8504 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); 8505 cal_comp_set_dtend_with_oldzone (client, comp, &date); 8506 e_cal_component_abort_sequence (comp); 8507 8508 gtk_drag_finish (context, TRUE, TRUE, time); 8509 8510 /* Reset this since it will be invalid. */ 8511 day_view->drag_event_day = -1; 8512 8513 /* Show the text item again, just in case it hasn't 8514 * moved. If we don't do this it may not appear. */ 8515 if (event->canvas_item) 8516 gnome_canvas_item_show (event->canvas_item); 8517 8518 e_cal_component_commit_sequence (comp); 8519 if (e_cal_component_has_recurrences (comp)) { 8520 if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) { 8521 g_object_unref (comp); 8522 return; 8523 } 8524 8525 if (mod == CALOBJ_MOD_ALL) 8526 comp_util_sanitize_recurrence_master (comp, client); 8527 8528 if (mod == CALOBJ_MOD_THIS) { 8529 e_cal_component_set_rdate_list (comp, NULL); 8530 e_cal_component_set_rrule_list (comp, NULL); 8531 e_cal_component_set_exdate_list (comp, NULL); 8532 e_cal_component_set_exrule_list (comp, NULL); 8533 } 8534 } else if (e_cal_component_is_instance (comp)) 8535 mod = CALOBJ_MOD_THIS; 8536 8537 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view))); 8538 8539 e_calendar_view_modify_and_send ( 8540 E_CALENDAR_VIEW (day_view), 8541 comp, client, mod, toplevel, FALSE); 8542 8543 g_object_unref (comp); 8544 8545 return; 8546 } 8547 } 8548 8549 if (length >= 0 && format == 8 && !drag_from_same_window) { 8550 /* We are dragging between different window */ 8551 8552 icalcomponent *icalcomp; 8553 icalcomponent_kind kind; 8554 time_t dtstart; 8555 icaltimezone *default_zone; 8556 8557 pos = e_day_view_convert_position_in_main_canvas ( 8558 day_view, 8559 x, y, &day, 8560 &row, NULL); 8561 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE) 8562 goto error; 8563 8564 icalcomp = icalparser_parse_string ((const gchar *) data); 8565 if (!icalcomp) 8566 goto error; 8567 8568 default_zone = e_cal_model_get_timezone (model); 8569 8570 /* check the type of the component */ 8571 kind = icalcomponent_isa (icalcomp); 8572 if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT) 8573 goto error; 8574 8575 dtstart = e_day_view_convert_grid_position_to_time (day_view, day, row); 8576 8577 if (kind == ICAL_VCALENDAR_COMPONENT) { 8578 icalcomponent_kind child_kind; 8579 icalcomponent *subcomp; 8580 8581 subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT); 8582 while (subcomp) { 8583 child_kind = icalcomponent_isa (subcomp); 8584 if (child_kind == ICAL_VEVENT_COMPONENT) 8585 e_calendar_view_add_event ( 8586 E_CALENDAR_VIEW (day_view), client, dtstart, 8587 default_zone, subcomp, FALSE); 8588 else if (child_kind == ICAL_VTIMEZONE_COMPONENT) { 8589 icaltimezone *zone; 8590 8591 zone = icaltimezone_new (); 8592 icaltimezone_set_component (zone, subcomp); 8593 e_cal_client_add_timezone_sync (client, zone, NULL, NULL); 8594 8595 icaltimezone_free (zone, 1); 8596 } 8597 8598 subcomp = icalcomponent_get_next_component ( 8599 icalcomp, ICAL_ANY_COMPONENT); 8600 } 8601 8602 icalcomponent_free (icalcomp); 8603 8604 } else { 8605 e_calendar_view_add_event (E_CALENDAR_VIEW (day_view), client, dtstart, default_zone, icalcomp, FALSE); 8606 } 8607 8608 gtk_drag_finish (context, TRUE, TRUE, time); 8609 return; 8610 } 8611 8612 error: 8613 gtk_drag_finish (context, FALSE, FALSE, time); 8614 } 8615 8616 /* Converts an hour from 0-23 to the preferred time format, and returns the 8617 * suffix to add and the width of it in the normal font. */ 8618 void 8619 e_day_view_convert_time_to_display (EDayView *day_view, 8620 gint hour, 8621 gint *display_hour, 8622 const gchar **suffix, 8623 gint *suffix_width) 8624 { 8625 ECalModel *model; 8626 8627 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 8628 8629 /* Calculate the actual hour number to display. For 12-hour 8630 * format we convert 0-23 to 12-11am/12-11pm. */ 8631 *display_hour = hour; 8632 if (e_cal_model_get_use_24_hour_format (model)) { 8633 *suffix = ""; 8634 *suffix_width = 0; 8635 } else { 8636 if (hour < 12) { 8637 *suffix = day_view->am_string; 8638 *suffix_width = day_view->am_string_width; 8639 } else { 8640 *display_hour -= 12; 8641 *suffix = day_view->pm_string; 8642 *suffix_width = day_view->pm_string_width; 8643 } 8644 8645 /* 12-hour uses 12:00 rather than 0:00. */ 8646 if (*display_hour == 0) 8647 *display_hour = 12; 8648 } 8649 } 8650 8651 gint 8652 e_day_view_get_time_string_width (EDayView *day_view) 8653 { 8654 ECalModel *model; 8655 gint time_width; 8656 8657 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)); 8658 time_width = day_view->digit_width * 4 + day_view->colon_width; 8659 8660 if (!e_cal_model_get_use_24_hour_format (model)) 8661 time_width += MAX (day_view->am_string_width, 8662 day_view->pm_string_width); 8663 8664 return time_width; 8665 } 8666 8667 /* Queues a layout, unless one is already queued. */ 8668 static void 8669 e_day_view_queue_layout (EDayView *day_view) 8670 { 8671 if (day_view->layout_timeout_id == 0) { 8672 day_view->layout_timeout_id = g_timeout_add (E_DAY_VIEW_LAYOUT_TIMEOUT, e_day_view_layout_timeout_cb, day_view); 8673 } 8674 } 8675 8676 /* Removes any queued layout. */ 8677 static void 8678 e_day_view_cancel_layout (EDayView *day_view) 8679 { 8680 if (day_view->layout_timeout_id != 0) { 8681 g_source_remove (day_view->layout_timeout_id); 8682 day_view->layout_timeout_id = 0; 8683 } 8684 } 8685 8686 static gboolean 8687 e_day_view_layout_timeout_cb (gpointer data) 8688 { 8689 EDayView *day_view = E_DAY_VIEW (data); 8690 8691 gtk_widget_queue_draw (day_view->top_canvas); 8692 gtk_widget_queue_draw (day_view->top_dates_canvas); 8693 gtk_widget_queue_draw (day_view->main_canvas); 8694 e_day_view_check_layout (day_view); 8695 8696 day_view->layout_timeout_id = 0; 8697 return FALSE; 8698 } 8699 8700 /* Returns the number of selected events (0 or 1 at present). */ 8701 gint 8702 e_day_view_get_num_events_selected (EDayView *day_view) 8703 { 8704 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), 0); 8705 8706 return (day_view->editing_event_day != -1) ? 1 : 0; 8707 } 8708 8709 static void 8710 e_day_view_paste_text (ECalendarView *cal_view) 8711 { 8712 EDayView *day_view; 8713 EDayViewEvent *event; 8714 8715 g_return_if_fail (E_IS_DAY_VIEW (cal_view)); 8716 8717 day_view = E_DAY_VIEW (cal_view); 8718 8719 if (day_view->editing_event_num == -1 && 8720 !e_day_view_add_new_event_in_selected_range (day_view, NULL)) 8721 return; 8722 8723 if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT) { 8724 if (!is_array_index_in_bounds (day_view->long_events, day_view->editing_event_num)) 8725 return; 8726 8727 event = &g_array_index (day_view->long_events, 8728 EDayViewEvent, 8729 day_view->editing_event_num); 8730 } else { 8731 if (!is_array_index_in_bounds (day_view->events[day_view->editing_event_day], day_view->editing_event_num)) 8732 return; 8733 8734 event = &g_array_index (day_view->events[day_view->editing_event_day], 8735 EDayViewEvent, 8736 day_view->editing_event_num); 8737 } 8738 8739 if (event->canvas_item && 8740 E_IS_TEXT (event->canvas_item) && 8741 E_TEXT (event->canvas_item)->editing) { 8742 e_text_paste_clipboard (E_TEXT (event->canvas_item)); 8743 } 8744 }