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

Location Tool Test ID Function Issue
e-calendar-view.c:636:25 clang-analyzer The left operand of '-' is a garbage value
e-calendar-view.c:636:25 clang-analyzer The left operand of '-' is a garbage value
   1 /*
   2  * This program is free software; you can redistribute it and/or
   3  * modify it under the terms of the GNU Lesser General Public
   4  * License as published by the Free Software Foundation; either
   5  * version 2 of the License, or (at your option) version 3.
   6  *
   7  * This program is distributed in the hope that it will be useful,
   8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10  * Lesser General Public License for more details.
  11  *
  12  * You should have received a copy of the GNU Lesser General Public
  13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14  *
  15  *
  16  * Authors:
  17  *		Rodrigo Moya <rodrigo@ximian.com>
  18  *
  19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  20  *
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include <config.h>
  25 #endif
  26 
  27 #include <string.h>
  28 #include <time.h>
  29 #include <gtk/gtk.h>
  30 #include <glib/gi18n.h>
  31 #include <glib/gstdio.h>
  32 #include <gdk/gdkkeysyms.h>
  33 #include <libebackend/libebackend.h>
  34 #include <libedataserverui/libedataserverui.h>
  35 
  36 #include <e-util/e-util.h>
  37 #include <libevolution-utils/e-alert-dialog.h>
  38 #include <e-util/e-selection.h>
  39 #include <e-util/e-datetime-format.h>
  40 #include <e-util/e-dialog-utils.h>
  41 #include <e-util/e-icon-factory.h>
  42 #include <misc/e-selectable.h>
  43 #include <shell/e-shell.h>
  44 
  45 #include "comp-util.h"
  46 #include "ea-calendar.h"
  47 #include "e-cal-model-calendar.h"
  48 #include "e-calendar-view.h"
  49 #include "itip-utils.h"
  50 #include "dialogs/comp-editor-util.h"
  51 #include "dialogs/delete-comp.h"
  52 #include "dialogs/delete-error.h"
  53 #include "dialogs/event-editor.h"
  54 #include "dialogs/send-comp.h"
  55 #include "dialogs/cancel-comp.h"
  56 #include "dialogs/recur-comp.h"
  57 #include "dialogs/select-source-dialog.h"
  58 #include "dialogs/goto-dialog.h"
  59 #include "print.h"
  60 #include "misc.h"
  61 
  62 #define E_CALENDAR_VIEW_GET_PRIVATE(obj) \
  63 	(G_TYPE_INSTANCE_GET_PRIVATE \
  64 	((obj), E_TYPE_CALENDAR_VIEW, ECalendarViewPrivate))
  65 
  66 struct _ECalendarViewPrivate {
  67 	/* The GnomeCalendar we are associated to */
  68 	GnomeCalendar *calendar;
  69 
  70 	/* The calendar model we are monitoring */
  71 	ECalModel *model;
  72 
  73 	gchar *default_category;
  74 	gint time_divisions;
  75 	GSList *selected_cut_list;
  76 
  77 	GtkTargetList *copy_target_list;
  78 	GtkTargetList *paste_target_list;
  79 };
  80 
  81 enum {
  82 	PROP_0,
  83 	PROP_COPY_TARGET_LIST,
  84 	PROP_MODEL,
  85 	PROP_PASTE_TARGET_LIST,
  86 	PROP_TIME_DIVISIONS
  87 };
  88 
  89 /* FIXME Why are we emitting these event signals here? Can't the model just be listened to? */
  90 /* Signal IDs */
  91 enum {
  92 	POPUP_EVENT,
  93 	SELECTION_CHANGED,
  94 	SELECTED_TIME_CHANGED,
  95 	TIMEZONE_CHANGED,
  96 	EVENT_CHANGED,
  97 	EVENT_ADDED,
  98 	USER_CREATED,
  99 	OPEN_EVENT,
 100 	LAST_SIGNAL
 101 };
 102 
 103 static guint signals[LAST_SIGNAL];
 104 
 105 static void calendar_view_selectable_init (ESelectableInterface *interface);
 106 
 107 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
 108 	ECalendarView, e_calendar_view, GTK_TYPE_TABLE,
 109 	G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)
 110 	G_IMPLEMENT_INTERFACE (E_TYPE_SELECTABLE, calendar_view_selectable_init));
 111 
 112 static void
 113 calendar_view_add_retract_data (ECalComponent *comp,
 114                                 const gchar *retract_comment,
 115                                 CalObjModType mod)
 116 {
 117 	icalcomponent *icalcomp = NULL;
 118 	icalproperty *icalprop = NULL;
 119 
 120 	icalcomp = e_cal_component_get_icalcomponent (comp);
 121 	if (retract_comment && *retract_comment)
 122 		icalprop = icalproperty_new_x (retract_comment);
 123 	else
 124 		icalprop = icalproperty_new_x ("0");
 125 	icalproperty_set_x_name (icalprop, "X-EVOLUTION-RETRACT-COMMENT");
 126 	icalcomponent_add_property (icalcomp, icalprop);
 127 
 128 	if (mod == CALOBJ_MOD_ALL)
 129 		icalprop = icalproperty_new_x ("All");
 130 	else
 131 		icalprop = icalproperty_new_x ("This");
 132 	icalproperty_set_x_name (icalprop, "X-EVOLUTION-RECUR-MOD");
 133 	icalcomponent_add_property (icalcomp, icalprop);
 134 }
 135 
 136 static gboolean
 137 calendar_view_check_for_retract (ECalComponent *comp,
 138                                  ECalClient *client)
 139 {
 140 	ECalComponentOrganizer organizer;
 141 	const gchar *strip;
 142 	gchar *email = NULL;
 143 	gboolean ret_val;
 144 
 145 	if (!e_cal_component_has_attendees (comp))
 146 		return FALSE;
 147 
 148 	if (!e_cal_client_check_save_schedules (client))
 149 		return FALSE;
 150 
 151 	e_cal_component_get_organizer (comp, &organizer);
 152 	strip = itip_strip_mailto (organizer.value);
 153 
 154 	ret_val =
 155 		e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &email, NULL, NULL) &&
 156 		(g_ascii_strcasecmp (email, strip) == 0);
 157 
 158 	g_free (email);
 159 
 160 	return ret_val;
 161 }
 162 
 163 static void
 164 calendar_view_delete_event (ECalendarView *cal_view,
 165                             ECalendarViewEvent *event)
 166 {
 167 	ECalModel *model;
 168 	ECalComponent *comp;
 169 	ECalComponentVType vtype;
 170 	ESourceRegistry *registry;
 171 	gboolean delete = TRUE;
 172 	GError *error = NULL;
 173 
 174 	if (!is_comp_data_valid (event))
 175 		return;
 176 
 177 	model = e_calendar_view_get_model (cal_view);
 178 	registry = e_cal_model_get_registry (model);
 179 
 180 	comp = e_cal_component_new ();
 181 	e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
 182 	vtype = e_cal_component_get_vtype (comp);
 183 
 184 	/*FIXME remove it once the we dont set the recurrence id for all the generated instances */
 185 	if (!e_cal_client_check_recurrences_no_master (event->comp_data->client))
 186 		e_cal_component_set_recurid (comp, NULL);
 187 
 188 	/*FIXME Retract should be moved to Groupwise features plugin */
 189 	if (calendar_view_check_for_retract (comp, event->comp_data->client)) {
 190 		gchar *retract_comment = NULL;
 191 		gboolean retract = FALSE;
 192 
 193 		delete = prompt_retract_dialog (comp, &retract_comment, GTK_WIDGET (cal_view), &retract);
 194 		if (retract) {
 195 			GSList *users = NULL;
 196 			icalcomponent *icalcomp = NULL, *mod_comp = NULL;
 197 
 198 			calendar_view_add_retract_data (
 199 				comp, retract_comment, CALOBJ_MOD_ALL);
 200 			icalcomp = e_cal_component_get_icalcomponent (comp);
 201 			icalcomponent_set_method (icalcomp, ICAL_METHOD_CANCEL);
 202 			if (!e_cal_client_send_objects_sync (event->comp_data->client, icalcomp, &users,
 203 						&mod_comp, NULL, &error))	{
 204 				delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
 205 				g_clear_error (&error);
 206 				error = NULL;
 207 			} else {
 208 
 209 				if (mod_comp)
 210 					icalcomponent_free (mod_comp);
 211 
 212 				if (users) {
 213 					g_slist_foreach (users, (GFunc) g_free, NULL);
 214 					g_slist_free (users);
 215 				}
 216 			}
 217 		}
 218 	} else if (e_cal_model_get_confirm_delete (model))
 219 		delete = delete_component_dialog (
 220 			comp, FALSE, 1, vtype, GTK_WIDGET (cal_view));
 221 
 222 	if (delete) {
 223 		const gchar *uid;
 224 		gchar *rid = NULL;
 225 
 226 		if ((itip_organizer_is_user (registry, comp, event->comp_data->client) ||
 227 		     itip_sentby_is_user (registry, comp, event->comp_data->client))
 228 		    && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)),
 229 						event->comp_data->client,
 230 						comp, TRUE))
 231 			itip_send_comp (
 232 				registry, E_CAL_COMPONENT_METHOD_CANCEL,
 233 				comp, event->comp_data->client, NULL, NULL,
 234 				NULL, TRUE, FALSE);
 235 
 236 		e_cal_component_get_uid (comp, &uid);
 237 		if (!uid || !*uid) {
 238 			g_object_unref (comp);
 239 			return;
 240 		}
 241 		rid = e_cal_component_get_recurid_as_string (comp);
 242 		if (e_cal_util_component_is_instance (event->comp_data->icalcomp) || e_cal_util_component_has_recurrences (event->comp_data->icalcomp))
 243 			e_cal_client_remove_object_sync (
 244 				event->comp_data->client, uid,
 245 				rid, CALOBJ_MOD_ALL, NULL, &error);
 246 		else
 247 			e_cal_client_remove_object_sync (event->comp_data->client, uid, NULL, CALOBJ_MOD_THIS, NULL, &error);
 248 
 249 		delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
 250 		g_clear_error (&error);
 251 		g_free (rid);
 252 	}
 253 
 254 	g_object_unref (comp);
 255 }
 256 
 257 static void
 258 calendar_view_set_model (ECalendarView *calendar_view,
 259                          ECalModel *model)
 260 {
 261 	g_return_if_fail (calendar_view->priv->model == NULL);
 262 	g_return_if_fail (E_IS_CAL_MODEL (model));
 263 
 264 	calendar_view->priv->model = g_object_ref (model);
 265 }
 266 
 267 static void
 268 calendar_view_set_property (GObject *object,
 269                             guint property_id,
 270                             const GValue *value,
 271                             GParamSpec *pspec)
 272 {
 273 	switch (property_id) {
 274 		case PROP_MODEL:
 275 			calendar_view_set_model (
 276 				E_CALENDAR_VIEW (object),
 277 				g_value_get_object (value));
 278 			return;
 279 
 280 		case PROP_TIME_DIVISIONS:
 281 			e_calendar_view_set_time_divisions (
 282 				E_CALENDAR_VIEW (object),
 283 				g_value_get_int (value));
 284 			return;
 285 	}
 286 
 287 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 288 }
 289 
 290 static void
 291 calendar_view_get_property (GObject *object,
 292                             guint property_id,
 293                             GValue *value,
 294                             GParamSpec *pspec)
 295 {
 296 	switch (property_id) {
 297 		case PROP_COPY_TARGET_LIST:
 298 			g_value_set_boxed (
 299 				value, e_calendar_view_get_copy_target_list (
 300 				E_CALENDAR_VIEW (object)));
 301 			return;
 302 
 303 		case PROP_MODEL:
 304 			g_value_set_object (
 305 				value, e_calendar_view_get_model (
 306 				E_CALENDAR_VIEW (object)));
 307 			return;
 308 
 309 		case PROP_PASTE_TARGET_LIST:
 310 			g_value_set_boxed (
 311 				value, e_calendar_view_get_paste_target_list (
 312 				E_CALENDAR_VIEW (object)));
 313 			return;
 314 
 315 		case PROP_TIME_DIVISIONS:
 316 			g_value_set_int (
 317 				value, e_calendar_view_get_time_divisions (
 318 				E_CALENDAR_VIEW (object)));
 319 			return;
 320 	}
 321 
 322 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 323 }
 324 
 325 static void
 326 calendar_view_dispose (GObject *object)
 327 {
 328 	ECalendarViewPrivate *priv;
 329 
 330 	priv = E_CALENDAR_VIEW_GET_PRIVATE (object);
 331 
 332 	if (priv->model != NULL) {
 333 		g_signal_handlers_disconnect_matched (
 334 			priv->model, G_SIGNAL_MATCH_DATA,
 335 			0, 0, NULL, NULL, object);
 336 		g_object_unref (priv->model);
 337 		priv->model = NULL;
 338 	}
 339 
 340 	if (priv->copy_target_list != NULL) {
 341 		gtk_target_list_unref (priv->copy_target_list);
 342 		priv->copy_target_list = NULL;
 343 	}
 344 
 345 	if (priv->paste_target_list != NULL) {
 346 		gtk_target_list_unref (priv->paste_target_list);
 347 		priv->paste_target_list = NULL;
 348 	}
 349 
 350 	if (priv->selected_cut_list) {
 351 		g_slist_foreach (priv->selected_cut_list, (GFunc) g_object_unref, NULL);
 352 		g_slist_free (priv->selected_cut_list);
 353 		priv->selected_cut_list = NULL;
 354 	}
 355 
 356 	/* Chain up to parent's dispose() method. */
 357 	G_OBJECT_CLASS (e_calendar_view_parent_class)->dispose (object);
 358 }
 359 
 360 static void
 361 calendar_view_finalize (GObject *object)
 362 {
 363 	ECalendarViewPrivate *priv;
 364 
 365 	priv = E_CALENDAR_VIEW_GET_PRIVATE (object);
 366 
 367 	g_free (priv->default_category);
 368 
 369 	/* Chain up to parent's finalize() method. */
 370 	G_OBJECT_CLASS (e_calendar_view_parent_class)->finalize (object);
 371 }
 372 
 373 static void
 374 calendar_view_constructed (GObject *object)
 375 {
 376 	/* Do this after calendar_view_init() so extensions can query
 377 	 * the GType accurately.  See GInstanceInitFunc documentation
 378 	 * for details of the problem. */
 379 	e_extensible_load_extensions (E_EXTENSIBLE (object));
 380 
 381 	/* Chain up to parent's constructed() method. */
 382 	G_OBJECT_CLASS (e_calendar_view_parent_class)->constructed (object);
 383 }
 384 
 385 static void
 386 calendar_view_update_actions (ESelectable *selectable,
 387                               EFocusTracker *focus_tracker,
 388                               GdkAtom *clipboard_targets,
 389                               gint n_clipboard_targets)
 390 {
 391 	ECalendarView *view;
 392 	GtkAction *action;
 393 	GtkTargetList *target_list;
 394 	GList *list, *iter;
 395 	gboolean can_paste = FALSE;
 396 	gboolean sources_are_editable = TRUE;
 397 	gboolean recurring = FALSE;
 398 	gboolean sensitive;
 399 	const gchar *tooltip;
 400 	gint n_selected;
 401 	gint ii;
 402 
 403 	view = E_CALENDAR_VIEW (selectable);
 404 
 405 	list = e_calendar_view_get_selected_events (view);
 406 	n_selected = g_list_length (list);
 407 
 408 	for (iter = list; iter != NULL; iter = iter->next) {
 409 		ECalendarViewEvent *event = iter->data;
 410 		ECalClient *client;
 411 		icalcomponent *icalcomp;
 412 
 413 		if (event == NULL || event->comp_data == NULL)
 414 			continue;
 415 
 416 		client = event->comp_data->client;
 417 		icalcomp = event->comp_data->icalcomp;
 418 
 419 		sources_are_editable = sources_are_editable && !e_client_is_readonly (E_CLIENT (client));
 420 
 421 		recurring |=
 422 			e_cal_util_component_is_instance (icalcomp) ||
 423 			e_cal_util_component_has_recurrences (icalcomp);
 424 	}
 425 
 426 	g_list_free (list);
 427 
 428 	target_list = e_selectable_get_paste_target_list (selectable);
 429 	for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
 430 		can_paste = gtk_target_list_find (
 431 			target_list, clipboard_targets[ii], NULL);
 432 
 433 	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
 434 	sensitive = (n_selected > 0) && sources_are_editable;
 435 	tooltip = _("Cut selected events to the clipboard");
 436 	gtk_action_set_sensitive (action, sensitive);
 437 	gtk_action_set_tooltip (action, tooltip);
 438 
 439 	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
 440 	sensitive = (n_selected > 0);
 441 	tooltip = _("Copy selected events to the clipboard");
 442 	gtk_action_set_sensitive (action, sensitive);
 443 	gtk_action_set_tooltip (action, tooltip);
 444 
 445 	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
 446 	sensitive = sources_are_editable && can_paste;
 447 	tooltip = _("Paste events from the clipboard");
 448 	gtk_action_set_sensitive (action, sensitive);
 449 	gtk_action_set_tooltip (action, tooltip);
 450 
 451 	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
 452 	sensitive = (n_selected > 0) && sources_are_editable && !recurring;
 453 	tooltip = _("Delete selected events");
 454 	gtk_action_set_sensitive (action, sensitive);
 455 	gtk_action_set_tooltip (action, tooltip);
 456 }
 457 
 458 static void
 459 calendar_view_cut_clipboard (ESelectable *selectable)
 460 {
 461 	ECalendarView *cal_view;
 462 	ECalendarViewPrivate *priv;
 463 	GList *selected, *l;
 464 
 465 	cal_view = E_CALENDAR_VIEW (selectable);
 466 	priv = cal_view->priv;
 467 
 468 	selected = e_calendar_view_get_selected_events (cal_view);
 469 	if (!selected)
 470 		return;
 471 
 472 #if 0  /* KILL-BONOBO */
 473 	e_calendar_view_set_status_message (cal_view, _("Deleting selected objects"), -1);
 474 #endif
 475 
 476 	e_selectable_copy_clipboard (selectable);
 477 
 478 	for (l = selected; l != NULL; l = g_list_next (l)) {
 479 		ECalendarViewEvent *event = (ECalendarViewEvent *) l->data;
 480 
 481 		priv->selected_cut_list = g_slist_prepend (priv->selected_cut_list, g_object_ref (event->comp_data));
 482 	}
 483 
 484 #if 0  /* KILL-BONOBO */
 485 	e_calendar_view_set_status_message (cal_view, NULL, -1);
 486 #endif
 487 
 488 	g_list_free (selected);
 489 }
 490 
 491 static void
 492 add_related_timezones (icalcomponent *des_icalcomp,
 493                        icalcomponent *src_icalcomp,
 494                        ECalClient *client)
 495 {
 496 	icalproperty_kind look_in[] = {
 497 		ICAL_DTSTART_PROPERTY,
 498 		ICAL_DTEND_PROPERTY,
 499 		ICAL_NO_PROPERTY
 500 	};
 501 	gint i;
 502 
 503 	g_return_if_fail (des_icalcomp != NULL);
 504 	g_return_if_fail (src_icalcomp != NULL);
 505 	g_return_if_fail (client != NULL);
 506 
 507 	for (i = 0; look_in[i] != ICAL_NO_PROPERTY; i++) {
 508 		icalproperty *prop = icalcomponent_get_first_property (src_icalcomp, look_in[i]);
 509 
 510 		if (prop) {
 511 			icalparameter *par = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
 512 
 513 			if (par) {
 514 				const gchar *tzid = icalparameter_get_tzid (par);
 515 
 516 				if (tzid) {
 517 					GError *error = NULL;
 518 					icaltimezone *zone = NULL;
 519 
 520 					if (!e_cal_client_get_timezone_sync (client, tzid, &zone, NULL, &error)) {
 521 						g_warning ("%s: Cannot get timezone for '%s'. %s", G_STRFUNC, tzid, error ? error->message : "");
 522 						if (error)
 523 							g_error_free (error);
 524 					} else if (zone &&
 525 						icalcomponent_get_timezone (des_icalcomp, icaltimezone_get_tzid (zone)) == NULL) {
 526 						/* do not duplicate timezones in the component */
 527 						icalcomponent *vtz_comp;
 528 
 529 						vtz_comp = icaltimezone_get_component (zone);
 530 						if (vtz_comp)
 531 							icalcomponent_add_component (des_icalcomp, icalcomponent_new_clone (vtz_comp));
 532 					}
 533 				}
 534 			}
 535 		}
 536 	}
 537 }
 538 
 539 static void
 540 calendar_view_copy_clipboard (ESelectable *selectable)
 541 {
 542 	ECalendarView *cal_view;
 543 	ECalendarViewPrivate *priv;
 544 	GList *selected, *l;
 545 	gchar *comp_str;
 546 	icalcomponent *vcal_comp;
 547 	icalcomponent *new_icalcomp;
 548 	ECalendarViewEvent *event;
 549 	GtkClipboard *clipboard;
 550 
 551 	cal_view = E_CALENDAR_VIEW (selectable);
 552 	priv = cal_view->priv;
 553 
 554 	selected = e_calendar_view_get_selected_events (cal_view);
 555 	if (!selected)
 556 		return;
 557 
 558 	if (priv->selected_cut_list) {
 559 		g_slist_foreach (priv->selected_cut_list, (GFunc) g_object_unref, NULL);
 560 		g_slist_free (priv->selected_cut_list);
 561 		priv->selected_cut_list = NULL;
 562 	}
 563 
 564 	/* create top-level VCALENDAR component and add VTIMEZONE's */
 565 	vcal_comp = e_cal_util_new_top_level ();
 566 	for (l = selected; l != NULL; l = l->next) {
 567 		event = (ECalendarViewEvent *) l->data;
 568 
 569 		if (event && is_comp_data_valid (event)) {
 570 			e_cal_util_add_timezones_from_component (vcal_comp, event->comp_data->icalcomp);
 571 
 572 			add_related_timezones (vcal_comp, event->comp_data->icalcomp, event->comp_data->client);
 573 		}
 574 	}
 575 
 576 	for (l = selected; l != NULL; l = l->next) {
 577 		event = (ECalendarViewEvent *) l->data;
 578 
 579 		if (!is_comp_data_valid (event))
 580 			continue;
 581 
 582 		new_icalcomp = icalcomponent_new_clone (event->comp_data->icalcomp);
 583 
 584 		/* do not remove RECURRENCE-IDs from copied objects */
 585 		icalcomponent_add_component (vcal_comp, new_icalcomp);
 586 	}
 587 
 588 	comp_str = icalcomponent_as_ical_string_r (vcal_comp);
 589 
 590 	/* copy the VCALENDAR to the clipboard */
 591 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 592 	e_clipboard_set_calendar (clipboard, comp_str, -1);
 593 	gtk_clipboard_store (clipboard);
 594 
 595 	/* free memory */
 596 	icalcomponent_free (vcal_comp);
 597 	g_free (comp_str);
 598 	g_list_free (selected);
 599 }
 600 
 601 static gboolean
 602 clipboard_get_calendar_data (ECalendarView *cal_view,
 603                              const gchar *text,
 604                              GSList **copied_list)
 605 {
 606 	icalcomponent *icalcomp;
 607 	icalcomponent_kind kind;
 608 	time_t selected_time_start, selected_time_end;
 609 	icaltimezone *default_zone;
 610 	ECalClient *client;
 611 	gboolean in_top_canvas, ret = FALSE;
 612 
 613 	g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), FALSE);
 614 
 615 	if (!text || !*text)
 616 		return FALSE;
 617 
 618 	icalcomp = icalparser_parse_string (text);
 619 	if (!icalcomp)
 620 		return FALSE;
 621 
 622 	default_zone = e_cal_model_get_timezone (cal_view->priv->model);
 623 	client = e_cal_model_get_default_client (cal_view->priv->model);
 624 
 625 	/* check the type of the component */
 626 	/* FIXME An error dialog if we return? */
 627 	kind = icalcomponent_isa (icalcomp);
 628 	if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT)
 629 		return FALSE;
 630 
 631 #if 0  /* KILL-BONOBO */
 632 	e_calendar_view_set_status_message (cal_view, _("Updating objects"), -1);
 633 #endif
 634 	e_calendar_view_get_selected_time_range (cal_view, &selected_time_start, &selected_time_end);
 635 
 636 	if ((selected_time_end - selected_time_start) == 60 * 60 * 24)
The left 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 left 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)

637 in_top_canvas = TRUE; 638 else 639 in_top_canvas = FALSE; 640 641 if (kind == ICAL_VCALENDAR_COMPONENT) { 642 icalcomponent *subcomp; 643 644 /* add timezones first, to have them ready */ 645 for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VTIMEZONE_COMPONENT); 646 subcomp; 647 subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VTIMEZONE_COMPONENT)) { 648 icaltimezone *zone; 649 GError *error = NULL; 650 651 zone = icaltimezone_new (); 652 icaltimezone_set_component (zone, subcomp); 653 if (!e_cal_client_add_timezone_sync (client, zone, NULL, &error)) { 654 icalproperty *tzidprop = icalcomponent_get_first_property (subcomp, ICAL_TZID_PROPERTY); 655 656 g_warning ("%s: Add zone '%s' failed. %s", G_STRFUNC, tzidprop ? icalproperty_get_tzid (tzidprop) : "???", error ? error->message : ""); 657 if (error) 658 g_error_free (error); 659 } 660 661 icaltimezone_free (zone, 1); 662 } 663 664 for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VEVENT_COMPONENT); 665 subcomp; 666 subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VEVENT_COMPONENT)) { 667 if (e_cal_util_component_has_recurrences (subcomp)) { 668 icalproperty *icalprop = icalcomponent_get_first_property (subcomp, ICAL_RRULE_PROPERTY); 669 if (icalprop) 670 icalproperty_remove_parameter_by_name (icalprop, "X-EVOLUTION-ENDDATE"); 671 } 672 673 ret = e_calendar_view_add_event (cal_view, client, selected_time_start, default_zone, subcomp, in_top_canvas); 674 if (!ret) 675 break; 676 677 if (copied_list) 678 *copied_list = g_slist_prepend (*copied_list, g_strdup (icalcomponent_get_uid (subcomp))); 679 } 680 681 icalcomponent_free (icalcomp); 682 } else { 683 ret = e_calendar_view_add_event (cal_view, client, selected_time_start, default_zone, icalcomp, in_top_canvas); 684 if (ret && copied_list) 685 *copied_list = g_slist_prepend (*copied_list, g_strdup (icalcomponent_get_uid (icalcomp))); 686 } 687 688 return ret; 689 690 #if 0 /* KILL-BONOBO */ 691 e_calendar_view_set_status_message (cal_view, NULL, -1); 692 #endif 693 } 694 695 static void 696 calendar_view_paste_clipboard (ESelectable *selectable) 697 { 698 ECalModel *model; 699 ECalendarView *cal_view; 700 ECalendarViewPrivate *priv; 701 ESourceRegistry *registry; 702 GtkClipboard *clipboard; 703 704 cal_view = E_CALENDAR_VIEW (selectable); 705 priv = cal_view->priv; 706 707 model = e_calendar_view_get_model (cal_view); 708 registry = e_cal_model_get_registry (model); 709 710 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); 711 712 /* Paste text into an event being edited. */ 713 if (gtk_clipboard_wait_is_text_available (clipboard)) { 714 ECalendarViewClass *class; 715 716 class = E_CALENDAR_VIEW_GET_CLASS (cal_view); 717 g_return_if_fail (class->paste_text != NULL); 718 719 class->paste_text (cal_view); 720 721 /* Paste iCalendar data into the view. */ 722 } else if (e_clipboard_wait_is_calendar_available (clipboard)) { 723 gchar *calendar_source; 724 GSList *copied_list = NULL, *l; 725 726 calendar_source = e_clipboard_wait_for_calendar (clipboard); 727 728 if (priv->selected_cut_list) 729 clipboard_get_calendar_data (cal_view, calendar_source, &copied_list); 730 else 731 clipboard_get_calendar_data (cal_view, calendar_source, NULL); 732 733 if (copied_list && priv->selected_cut_list) { 734 for (l = priv->selected_cut_list; l != NULL; l = l->next) { 735 ECalComponent *comp; 736 ECalModelComponent *comp_data = (ECalModelComponent *) l->data; 737 const gchar *uid; 738 GError *error = NULL; 739 GSList *found = NULL; 740 741 /* Remove them one by one after ensuring it has been copied to the destination successfully */ 742 found = g_slist_find_custom (copied_list, icalcomponent_get_uid (comp_data->icalcomp), (GCompareFunc) strcmp); 743 if (!found) 744 continue; 745 746 g_free (found->data); 747 copied_list = g_slist_delete_link (copied_list, found); 748 749 comp = e_cal_component_new (); 750 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp)); 751 752 if ((itip_organizer_is_user (registry, comp, comp_data->client) || 753 itip_sentby_is_user (registry, comp, comp_data->client)) 754 && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)), 755 comp_data->client, comp, TRUE)) 756 itip_send_comp ( 757 registry, 758 E_CAL_COMPONENT_METHOD_CANCEL, 759 comp, comp_data->client, 760 NULL, NULL, NULL, TRUE, FALSE); 761 762 e_cal_component_get_uid (comp, &uid); 763 if (e_cal_component_is_instance (comp)) { 764 gchar *rid = NULL; 765 icalcomponent *icalcomp; 766 767 /* when cutting detached instances, only cut that instance */ 768 rid = e_cal_component_get_recurid_as_string (comp); 769 if (e_cal_client_get_object_sync (comp_data->client, uid, rid, &icalcomp, NULL, NULL)) { 770 e_cal_client_remove_object_sync (comp_data->client, uid, rid, CALOBJ_MOD_THIS, NULL, &error); 771 icalcomponent_free (icalcomp); 772 } else 773 e_cal_client_remove_object_sync (comp_data->client, uid, NULL, CALOBJ_MOD_ALL, NULL, &error); 774 g_free (rid); 775 } else 776 e_cal_client_remove_object_sync (comp_data->client, uid, NULL, CALOBJ_MOD_ALL, NULL, &error); 777 delete_error_dialog (error, E_CAL_COMPONENT_EVENT); 778 779 g_clear_error (&error); 780 g_object_unref (comp); 781 } 782 } 783 784 if (priv->selected_cut_list) { 785 g_slist_foreach (priv->selected_cut_list, (GFunc) g_object_unref, NULL); 786 g_slist_free (priv->selected_cut_list); 787 } 788 priv->selected_cut_list = NULL; 789 790 g_free (calendar_source); 791 792 } 793 } 794 795 static void 796 calendar_view_delete_selection (ESelectable *selectable) 797 { 798 ECalendarView *cal_view; 799 GList *selected, *iter; 800 801 cal_view = E_CALENDAR_VIEW (selectable); 802 803 selected = e_calendar_view_get_selected_events (cal_view); 804 805 for (iter = selected; iter != NULL; iter = iter->next) { 806 ECalendarViewEvent *event = iter->data; 807 808 /* XXX Why would this ever be NULL? */ 809 if (event == NULL) 810 continue; 811 812 calendar_view_delete_event (cal_view, event); 813 } 814 815 g_list_free (selected); 816 } 817 818 static void 819 e_calendar_view_class_init (ECalendarViewClass *class) 820 { 821 GObjectClass *object_class; 822 GtkBindingSet *binding_set; 823 824 g_type_class_add_private (class, sizeof (ECalendarViewPrivate)); 825 826 object_class = G_OBJECT_CLASS (class); 827 object_class->set_property = calendar_view_set_property; 828 object_class->get_property = calendar_view_get_property; 829 object_class->dispose = calendar_view_dispose; 830 object_class->finalize = calendar_view_finalize; 831 object_class->constructed = calendar_view_constructed; 832 833 class->selection_changed = NULL; 834 class->selected_time_changed = NULL; 835 class->event_changed = NULL; 836 class->event_added = NULL; 837 class->user_created = NULL; 838 839 class->get_selected_events = NULL; 840 class->get_selected_time_range = NULL; 841 class->set_selected_time_range = NULL; 842 class->get_visible_time_range = NULL; 843 class->update_query = NULL; 844 class->open_event = e_calendar_view_open_event; 845 class->paste_text = NULL; 846 847 /* Inherited from ESelectableInterface */ 848 g_object_class_override_property ( 849 object_class, 850 PROP_COPY_TARGET_LIST, 851 "copy-target-list"); 852 853 g_object_class_install_property ( 854 object_class, 855 PROP_MODEL, 856 g_param_spec_object ( 857 "model", 858 "Model", 859 NULL, 860 E_TYPE_CAL_MODEL, 861 G_PARAM_READWRITE | 862 G_PARAM_CONSTRUCT_ONLY)); 863 864 /* Inherited from ESelectableInterface */ 865 g_object_class_override_property ( 866 object_class, 867 PROP_PASTE_TARGET_LIST, 868 "paste-target-list"); 869 870 g_object_class_install_property ( 871 object_class, 872 PROP_TIME_DIVISIONS, 873 g_param_spec_int ( 874 "time-divisions", 875 "Time Divisions", 876 NULL, 877 G_MININT, 878 G_MAXINT, 879 30, 880 G_PARAM_READWRITE)); 881 882 signals[POPUP_EVENT] = g_signal_new ( 883 "popup-event", 884 G_TYPE_FROM_CLASS (class), 885 G_SIGNAL_RUN_FIRST, 886 G_STRUCT_OFFSET (ECalendarViewClass, popup_event), 887 NULL, NULL, 888 g_cclosure_marshal_VOID__BOXED, 889 G_TYPE_NONE, 1, 890 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); 891 892 signals[SELECTION_CHANGED] = g_signal_new ( 893 "selection-changed", 894 G_TYPE_FROM_CLASS (class), 895 G_SIGNAL_RUN_LAST, 896 G_STRUCT_OFFSET (ECalendarViewClass, selection_changed), 897 NULL, NULL, 898 g_cclosure_marshal_VOID__VOID, 899 G_TYPE_NONE, 0); 900 901 signals[SELECTED_TIME_CHANGED] = g_signal_new ( 902 "selected-time-changed", 903 G_TYPE_FROM_CLASS (class), 904 G_SIGNAL_RUN_LAST, 905 G_STRUCT_OFFSET (ECalendarViewClass, selected_time_changed), 906 NULL, NULL, 907 g_cclosure_marshal_VOID__VOID, 908 G_TYPE_NONE, 0); 909 910 signals[TIMEZONE_CHANGED] = g_signal_new ( 911 "timezone-changed", 912 G_TYPE_FROM_CLASS (class), 913 G_SIGNAL_RUN_LAST, 914 G_STRUCT_OFFSET (ECalendarViewClass, timezone_changed), 915 NULL, NULL, 916 e_marshal_VOID__POINTER_POINTER, 917 G_TYPE_NONE, 2, 918 G_TYPE_POINTER, 919 G_TYPE_POINTER); 920 921 signals[EVENT_CHANGED] = g_signal_new ( 922 "event-changed", 923 G_TYPE_FROM_CLASS (object_class), 924 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, 925 G_STRUCT_OFFSET (ECalendarViewClass, event_changed), 926 NULL, NULL, 927 g_cclosure_marshal_VOID__POINTER, 928 G_TYPE_NONE, 1, 929 G_TYPE_POINTER); 930 931 signals[EVENT_ADDED] = g_signal_new ( 932 "event-added", 933 G_TYPE_FROM_CLASS (object_class), 934 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, 935 G_STRUCT_OFFSET (ECalendarViewClass, event_added), 936 NULL, NULL, 937 g_cclosure_marshal_VOID__POINTER, 938 G_TYPE_NONE, 1, 939 G_TYPE_POINTER); 940 941 signals[USER_CREATED] = g_signal_new ( 942 "user-created", 943 G_TYPE_FROM_CLASS (class), 944 G_SIGNAL_RUN_LAST, 945 G_STRUCT_OFFSET (ECalendarViewClass, user_created), 946 NULL, NULL, 947 g_cclosure_marshal_VOID__OBJECT, 948 G_TYPE_NONE, 1, G_TYPE_OBJECT); 949 950 signals[OPEN_EVENT] = g_signal_new ( 951 "open-event", 952 G_TYPE_FROM_CLASS (class), 953 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, 954 G_STRUCT_OFFSET (ECalendarViewClass, open_event), 955 NULL, NULL, 956 g_cclosure_marshal_VOID__VOID, 957 G_TYPE_NONE, 0); 958 959 /* Key bindings */ 960 961 binding_set = gtk_binding_set_by_class (class); 962 963 gtk_binding_entry_add_signal ( 964 binding_set, GDK_KEY_o, GDK_CONTROL_MASK, "open-event", 0); 965 966 /* init the accessibility support for e_day_view */ 967 e_cal_view_a11y_init (); 968 } 969 970 static void 971 e_calendar_view_init (ECalendarView *calendar_view) 972 { 973 GtkTargetList *target_list; 974 975 calendar_view->priv = E_CALENDAR_VIEW_GET_PRIVATE (calendar_view); 976 977 /* Set this early to avoid a divide-by-zero during init. */ 978 calendar_view->priv->time_divisions = 30; 979 980 target_list = gtk_target_list_new (NULL, 0); 981 e_target_list_add_calendar_targets (target_list, 0); 982 calendar_view->priv->copy_target_list = target_list; 983 984 target_list = gtk_target_list_new (NULL, 0); 985 e_target_list_add_calendar_targets (target_list, 0); 986 calendar_view->priv->paste_target_list = target_list; 987 } 988 989 static void 990 calendar_view_selectable_init (ESelectableInterface *interface) 991 { 992 interface->update_actions = calendar_view_update_actions; 993 interface->cut_clipboard = calendar_view_cut_clipboard; 994 interface->copy_clipboard = calendar_view_copy_clipboard; 995 interface->paste_clipboard = calendar_view_paste_clipboard; 996 interface->delete_selection = calendar_view_delete_selection; 997 } 998 999 void 1000 e_calendar_view_popup_event (ECalendarView *calendar_view, 1001 GdkEventButton *event) 1002 { 1003 g_return_if_fail (E_IS_CALENDAR_VIEW (calendar_view)); 1004 g_return_if_fail (event != NULL); 1005 1006 g_signal_emit (calendar_view, signals[POPUP_EVENT], 0, event); 1007 } 1008 1009 gboolean 1010 e_calendar_view_add_event (ECalendarView *cal_view, 1011 ECalClient *client, 1012 time_t dtstart, 1013 icaltimezone *default_zone, 1014 icalcomponent *icalcomp, 1015 gboolean in_top_canvas) 1016 { 1017 ECalModel *model; 1018 ECalComponent *comp; 1019 ESourceRegistry *registry; 1020 struct icaltimetype itime, old_dtstart, old_dtend; 1021 time_t tt_start, tt_end, new_dtstart = 0; 1022 struct icaldurationtype ic_dur, ic_oneday; 1023 gchar *uid; 1024 gint start_offset, end_offset; 1025 gboolean all_day_event = FALSE; 1026 GnomeCalendarViewType view_type; 1027 gboolean ret = TRUE; 1028 GError *error = NULL; 1029 1030 model = e_calendar_view_get_model (cal_view); 1031 registry = e_cal_model_get_registry (model); 1032 1033 start_offset = 0; 1034 end_offset = 0; 1035 1036 old_dtstart = icalcomponent_get_dtstart (icalcomp); 1037 tt_start = icaltime_as_timet (old_dtstart); 1038 old_dtend = icalcomponent_get_dtend (icalcomp); 1039 tt_end = icaltime_as_timet (old_dtend); 1040 ic_dur = icaldurationtype_from_int (tt_end - tt_start); 1041 1042 if (icaldurationtype_as_int (ic_dur) > 60 *60 *24) { 1043 /* This is a long event */ 1044 start_offset = old_dtstart.hour * 60 + old_dtstart.minute; 1045 end_offset = old_dtstart.hour * 60 + old_dtend.minute; 1046 } 1047 1048 ic_oneday = icaldurationtype_null_duration (); 1049 ic_oneday.days = 1; 1050 1051 view_type = gnome_calendar_get_view (cal_view->priv->calendar); 1052 1053 switch (view_type) { 1054 case GNOME_CAL_DAY_VIEW: 1055 case GNOME_CAL_WORK_WEEK_VIEW: 1056 if (start_offset == 0 && end_offset == 0 && in_top_canvas) 1057 all_day_event = TRUE; 1058 1059 if (all_day_event) { 1060 ic_dur = ic_oneday; 1061 } else if (icaldurationtype_as_int (ic_dur) >= 60 *60 *24 1062 && !in_top_canvas) { 1063 /* copy & paste from top canvas to main canvas */ 1064 gint time_divisions; 1065 1066 time_divisions = e_calendar_view_get_time_divisions (cal_view); 1067 ic_dur = icaldurationtype_from_int (time_divisions * 60); 1068 } 1069 1070 if (in_top_canvas) 1071 new_dtstart = dtstart + start_offset * 60; 1072 else 1073 new_dtstart = dtstart; 1074 break; 1075 case GNOME_CAL_WEEK_VIEW: 1076 case GNOME_CAL_MONTH_VIEW: 1077 case GNOME_CAL_LIST_VIEW: 1078 if (old_dtstart.is_date && old_dtend.is_date 1079 && memcmp (&ic_dur, &ic_oneday, sizeof (ic_dur)) == 0) { 1080 all_day_event = TRUE; 1081 new_dtstart = dtstart; 1082 } else { 1083 icaltimetype new_time = icaltime_from_timet_with_zone (dtstart, FALSE, default_zone); 1084 1085 new_time.hour = old_dtstart.hour; 1086 new_time.minute = old_dtstart.minute; 1087 new_time.second = old_dtstart.second; 1088 1089 new_dtstart = icaltime_as_timet_with_zone (new_time, old_dtstart.zone ? old_dtstart.zone : default_zone); 1090 } 1091 break; 1092 default: 1093 g_return_val_if_reached (FALSE); 1094 } 1095 1096 itime = icaltime_from_timet_with_zone (new_dtstart, FALSE, old_dtstart.zone ? old_dtstart.zone : default_zone); 1097 /* set the timezone properly */ 1098 itime.zone = old_dtstart.zone ? old_dtstart.zone : default_zone; 1099 if (all_day_event) 1100 itime.is_date = TRUE; 1101 icalcomponent_set_dtstart (icalcomp, itime); 1102 1103 itime.is_date = FALSE; 1104 itime = icaltime_add (itime, ic_dur); 1105 if (all_day_event) 1106 itime.is_date = TRUE; 1107 icalcomponent_set_dtend (icalcomp, itime); 1108 1109 /* FIXME The new uid stuff can go away once we actually set it in the backend */ 1110 uid = e_cal_component_gen_uid (); 1111 comp = e_cal_component_new (); 1112 e_cal_component_set_icalcomponent ( 1113 comp, icalcomponent_new_clone (icalcomp)); 1114 e_cal_component_set_uid (comp, uid); 1115 g_free (uid); 1116 1117 e_cal_component_commit_sequence (comp); 1118 1119 uid = NULL; 1120 if (e_cal_client_create_object_sync (client, e_cal_component_get_icalcomponent (comp), &uid, NULL, &error)) { 1121 gboolean strip_alarms = TRUE; 1122 1123 if (uid) { 1124 e_cal_component_set_uid (comp, uid); 1125 g_free (uid); 1126 } 1127 1128 if ((itip_organizer_is_user (registry, comp, client) || 1129 itip_sentby_is_user (registry, comp, client)) && 1130 send_component_dialog ( 1131 (GtkWindow *) gtk_widget_get_toplevel ( 1132 GTK_WIDGET (cal_view)), 1133 client, comp, TRUE, &strip_alarms, NULL)) { 1134 itip_send_comp ( 1135 registry, E_CAL_COMPONENT_METHOD_REQUEST, 1136 comp, client, NULL, NULL, NULL, strip_alarms, 1137 FALSE); 1138 } 1139 } else { 1140 g_message (G_STRLOC ": Could not create the object! %s", error ? error->message : ""); 1141 if (error) 1142 g_error_free (error); 1143 ret = FALSE; 1144 } 1145 1146 g_object_unref (comp); 1147 return ret; 1148 } 1149 1150 GnomeCalendar * 1151 e_calendar_view_get_calendar (ECalendarView *cal_view) 1152 { 1153 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); 1154 1155 return cal_view->priv->calendar; 1156 } 1157 1158 void 1159 e_calendar_view_set_calendar (ECalendarView *cal_view, 1160 GnomeCalendar *calendar) 1161 { 1162 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1163 1164 cal_view->priv->calendar = calendar; 1165 } 1166 1167 ECalModel * 1168 e_calendar_view_get_model (ECalendarView *cal_view) 1169 { 1170 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); 1171 1172 return cal_view->priv->model; 1173 } 1174 1175 icaltimezone * 1176 e_calendar_view_get_timezone (ECalendarView *cal_view) 1177 { 1178 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); 1179 return e_cal_model_get_timezone (cal_view->priv->model); 1180 } 1181 1182 void 1183 e_calendar_view_set_timezone (ECalendarView *cal_view, 1184 icaltimezone *zone) 1185 { 1186 icaltimezone *old_zone; 1187 1188 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1189 1190 old_zone = e_cal_model_get_timezone (cal_view->priv->model); 1191 if (old_zone == zone) 1192 return; 1193 1194 e_cal_model_set_timezone (cal_view->priv->model, zone); 1195 g_signal_emit ( 1196 cal_view, signals[TIMEZONE_CHANGED], 0, 1197 old_zone, zone); 1198 } 1199 1200 const gchar * 1201 e_calendar_view_get_default_category (ECalendarView *cal_view) 1202 { 1203 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); 1204 1205 return cal_view->priv->default_category; 1206 } 1207 1208 /** 1209 * e_calendar_view_set_default_category 1210 * @cal_view: A calendar view. 1211 * @category: Default category name or NULL for no category. 1212 * 1213 * Sets the default category that will be used when creating new calendar 1214 * components from the given calendar view. 1215 */ 1216 void 1217 e_calendar_view_set_default_category (ECalendarView *cal_view, 1218 const gchar *category) 1219 { 1220 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1221 1222 g_free (cal_view->priv->default_category); 1223 cal_view->priv->default_category = g_strdup (category); 1224 } 1225 1226 GtkTargetList * 1227 e_calendar_view_get_copy_target_list (ECalendarView *cal_view) 1228 { 1229 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); 1230 1231 return cal_view->priv->copy_target_list; 1232 } 1233 1234 GtkTargetList * 1235 e_calendar_view_get_paste_target_list (ECalendarView *cal_view) 1236 { 1237 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); 1238 1239 return cal_view->priv->paste_target_list; 1240 } 1241 1242 gint 1243 e_calendar_view_get_time_divisions (ECalendarView *cal_view) 1244 { 1245 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), 0); 1246 1247 return cal_view->priv->time_divisions; 1248 } 1249 1250 void 1251 e_calendar_view_set_time_divisions (ECalendarView *cal_view, 1252 gint time_divisions) 1253 { 1254 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1255 1256 if (cal_view->priv->time_divisions == time_divisions) 1257 return; 1258 1259 cal_view->priv->time_divisions = time_divisions; 1260 1261 g_object_notify (G_OBJECT (cal_view), "time-divisions"); 1262 } 1263 1264 GList * 1265 e_calendar_view_get_selected_events (ECalendarView *cal_view) 1266 { 1267 ECalendarViewClass *class; 1268 1269 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); 1270 1271 class = E_CALENDAR_VIEW_GET_CLASS (cal_view); 1272 g_return_val_if_fail (class->get_selected_events != NULL, NULL); 1273 1274 return class->get_selected_events (cal_view); 1275 } 1276 1277 gboolean 1278 e_calendar_view_get_selected_time_range (ECalendarView *cal_view, 1279 time_t *start_time, 1280 time_t *end_time) 1281 { 1282 ECalendarViewClass *class; 1283 1284 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), FALSE); 1285 1286 class = E_CALENDAR_VIEW_GET_CLASS (cal_view); 1287 g_return_val_if_fail (class->get_selected_time_range != NULL, FALSE); 1288 1289 return class->get_selected_time_range (cal_view, start_time, end_time); 1290 } 1291 1292 void 1293 e_calendar_view_set_selected_time_range (ECalendarView *cal_view, 1294 time_t start_time, 1295 time_t end_time) 1296 { 1297 ECalendarViewClass *class; 1298 1299 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1300 1301 /* Not all views implement this, so return silently. */ 1302 class = E_CALENDAR_VIEW_GET_CLASS (cal_view); 1303 if (class->set_selected_time_range == NULL) 1304 return; 1305 1306 class->set_selected_time_range (cal_view, start_time, end_time); 1307 } 1308 1309 gboolean 1310 e_calendar_view_get_visible_time_range (ECalendarView *cal_view, 1311 time_t *start_time, 1312 time_t *end_time) 1313 { 1314 ECalendarViewClass *class; 1315 1316 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), FALSE); 1317 1318 class = E_CALENDAR_VIEW_GET_CLASS (cal_view); 1319 g_return_val_if_fail (class->get_visible_time_range != NULL, FALSE); 1320 1321 return class->get_visible_time_range (cal_view, start_time, end_time); 1322 } 1323 1324 void 1325 e_calendar_view_update_query (ECalendarView *cal_view) 1326 { 1327 ECalendarViewClass *class; 1328 1329 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1330 1331 class = E_CALENDAR_VIEW_GET_CLASS (cal_view); 1332 g_return_if_fail (class->update_query != NULL); 1333 1334 class->update_query (cal_view); 1335 } 1336 1337 void 1338 e_calendar_view_delete_selected_occurrence (ECalendarView *cal_view) 1339 { 1340 GList *selected; 1341 ECalModel *model; 1342 ECalComponent *comp; 1343 ECalendarViewEvent *event; 1344 ECalComponentVType vtype; 1345 ESourceRegistry *registry; 1346 gboolean delete = TRUE; 1347 GError *error = NULL; 1348 1349 model = e_calendar_view_get_model (cal_view); 1350 registry = e_cal_model_get_registry (model); 1351 1352 selected = e_calendar_view_get_selected_events (cal_view); 1353 if (!selected) 1354 return; 1355 event = (ECalendarViewEvent *) selected->data; 1356 if (!is_comp_data_valid (event)) 1357 return; 1358 1359 comp = e_cal_component_new (); 1360 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); 1361 vtype = e_cal_component_get_vtype (comp); 1362 1363 /*FIXME Retract should be moved to Groupwise features plugin */ 1364 if (calendar_view_check_for_retract (comp, event->comp_data->client)) { 1365 gchar *retract_comment = NULL; 1366 gboolean retract = FALSE; 1367 1368 delete = prompt_retract_dialog (comp, &retract_comment, GTK_WIDGET (cal_view), &retract); 1369 if (retract) { 1370 GSList *users = NULL; 1371 icalcomponent *icalcomp = NULL, *mod_comp = NULL; 1372 1373 calendar_view_add_retract_data ( 1374 comp, retract_comment, CALOBJ_MOD_THIS); 1375 icalcomp = e_cal_component_get_icalcomponent (comp); 1376 icalcomponent_set_method (icalcomp, ICAL_METHOD_CANCEL); 1377 if (!e_cal_client_send_objects_sync (event->comp_data->client, icalcomp, &users, 1378 &mod_comp, NULL, &error)) { 1379 delete_error_dialog (error, E_CAL_COMPONENT_EVENT); 1380 g_clear_error (&error); 1381 error = NULL; 1382 } else { 1383 if (mod_comp) 1384 icalcomponent_free (mod_comp); 1385 if (users) { 1386 g_slist_foreach (users, (GFunc) g_free, NULL); 1387 g_slist_free (users); 1388 } 1389 } 1390 } 1391 } else if (e_cal_model_get_confirm_delete (model)) 1392 delete = delete_component_dialog ( 1393 comp, FALSE, 1, vtype, GTK_WIDGET (cal_view)); 1394 1395 if (delete) { 1396 const gchar *uid; 1397 gchar *rid = NULL; 1398 ECalComponentDateTime dt; 1399 icaltimezone *zone = NULL; 1400 gboolean is_instance = FALSE; 1401 1402 e_cal_component_get_uid (comp, &uid); 1403 e_cal_component_get_dtstart (comp, &dt); 1404 is_instance = e_cal_component_is_instance (comp); 1405 1406 if (dt.tzid) { 1407 GError *error = NULL; 1408 1409 e_cal_client_get_timezone_sync (event->comp_data->client, dt.tzid, &zone, NULL, &error); 1410 if (error) { 1411 zone = e_calendar_view_get_timezone (cal_view); 1412 g_clear_error (&error); 1413 } 1414 } else 1415 zone = e_calendar_view_get_timezone (cal_view); 1416 1417 if (is_instance) 1418 rid = e_cal_component_get_recurid_as_string (comp); 1419 1420 e_cal_component_free_datetime (&dt); 1421 1422 if ((itip_organizer_is_user (registry, comp, event->comp_data->client) || 1423 itip_sentby_is_user (registry, comp, event->comp_data->client)) 1424 && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)), 1425 event->comp_data->client, 1426 comp, TRUE) && !e_cal_client_check_save_schedules (event->comp_data->client)) { 1427 if (!e_cal_component_is_instance (comp)) { 1428 ECalComponentRange range; 1429 1430 /* set the recurrence ID of the object we send */ 1431 range.type = E_CAL_COMPONENT_RANGE_SINGLE; 1432 e_cal_component_get_dtstart (comp, &range.datetime); 1433 range.datetime.value->is_date = 1; 1434 e_cal_component_set_recurid (comp, &range); 1435 1436 e_cal_component_free_datetime (&range.datetime); 1437 } 1438 1439 itip_send_comp ( 1440 registry, E_CAL_COMPONENT_METHOD_CANCEL, 1441 comp, event->comp_data->client, NULL, NULL, 1442 NULL, TRUE, FALSE); 1443 } 1444 1445 if (is_instance) 1446 e_cal_client_remove_object_sync (event->comp_data->client, uid, rid, CALOBJ_MOD_THIS, NULL, &error); 1447 else { 1448 struct icaltimetype instance_rid; 1449 1450 instance_rid = icaltime_from_timet_with_zone ( 1451 event->comp_data->instance_start, 1452 TRUE, zone ? zone : icaltimezone_get_utc_timezone ()); 1453 e_cal_util_remove_instances (event->comp_data->icalcomp, instance_rid, CALOBJ_MOD_THIS); 1454 e_cal_client_modify_object_sync (event->comp_data->client, event->comp_data->icalcomp, CALOBJ_MOD_THIS, NULL, &error); 1455 } 1456 1457 delete_error_dialog (error, E_CAL_COMPONENT_EVENT); 1458 g_clear_error (&error); 1459 g_free (rid); 1460 } 1461 1462 /* free memory */ 1463 g_list_free (selected); 1464 g_object_unref (comp); 1465 } 1466 1467 void 1468 e_calendar_view_open_event (ECalendarView *cal_view) 1469 { 1470 GList *selected; 1471 1472 selected = e_calendar_view_get_selected_events (cal_view); 1473 if (selected) { 1474 ECalendarViewEvent *event = (ECalendarViewEvent *) selected->data; 1475 if (event && is_comp_data_valid (event)) 1476 e_calendar_view_edit_appointment (cal_view, event->comp_data->client, event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT); 1477 1478 g_list_free (selected); 1479 } 1480 } 1481 1482 /** 1483 * e_calendar_view_new_appointment_for 1484 * @cal_view: A calendar view. 1485 * @dtstart: A Unix time_t that marks the beginning of the appointment. 1486 * @dtend: A Unix time_t that marks the end of the appointment. 1487 * @all_day: If TRUE, the dtstart and dtend are expanded to cover 1488 * the entire day, and the event is set to TRANSPARENT. 1489 * @meeting: Whether the appointment is a meeting or not. 1490 * 1491 * Opens an event editor dialog for a new appointment. 1492 */ 1493 void 1494 e_calendar_view_new_appointment_for (ECalendarView *cal_view, 1495 time_t dtstart, 1496 time_t dtend, 1497 gboolean all_day, 1498 gboolean meeting) 1499 { 1500 ECalendarViewPrivate *priv; 1501 struct icaltimetype itt; 1502 ECalComponentDateTime dt; 1503 ECalComponent *comp; 1504 icalcomponent *icalcomp; 1505 ECalComponentTransparency transparency; 1506 ECalClient *default_client = NULL; 1507 gpointer parent; 1508 guint32 flags = 0; 1509 1510 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1511 1512 parent = gtk_widget_get_toplevel (GTK_WIDGET (cal_view)); 1513 parent = gtk_widget_is_toplevel (parent) ? parent : NULL; 1514 1515 priv = cal_view->priv; 1516 1517 default_client = e_cal_model_get_default_client (priv->model); 1518 1519 if (!default_client || !e_client_is_opened (E_CLIENT (default_client))) { 1520 g_warning ("Default client not loaded \n"); 1521 return; 1522 } 1523 1524 if (e_client_is_readonly (E_CLIENT (default_client))) { 1525 GtkWidget *widget; 1526 ESource *source; 1527 1528 source = e_client_get_source (E_CLIENT (default_client)); 1529 1530 widget = e_alert_dialog_new_for_args ( 1531 parent, "calendar:prompt-read-only-cal", 1532 e_source_get_display_name (source), 1533 NULL); 1534 1535 g_signal_connect ( 1536 widget, "response", 1537 G_CALLBACK (gtk_widget_destroy), widget); 1538 gtk_widget_show (widget); 1539 return; 1540 } 1541 1542 dt.value = &itt; 1543 if (all_day) 1544 dt.tzid = NULL; 1545 else 1546 dt.tzid = icaltimezone_get_tzid (e_cal_model_get_timezone (cal_view->priv->model)); 1547 1548 icalcomp = e_cal_model_create_component_with_defaults (priv->model, all_day); 1549 comp = e_cal_component_new (); 1550 e_cal_component_set_icalcomponent (comp, icalcomp); 1551 1552 /* DTSTART, DTEND */ 1553 itt = icaltime_from_timet_with_zone (dtstart, FALSE, e_cal_model_get_timezone (cal_view->priv->model)); 1554 if (all_day) { 1555 itt.hour = itt.minute = itt.second = 0; 1556 itt.is_date = TRUE; 1557 } 1558 e_cal_component_set_dtstart (comp, &dt); 1559 1560 itt = icaltime_from_timet_with_zone (dtend, FALSE, e_cal_model_get_timezone (cal_view->priv->model)); 1561 if (all_day) { 1562 /* We round it up to the end of the day, unless it is 1563 * already set to midnight */ 1564 if (itt.hour != 0 || itt.minute != 0 || itt.second != 0) { 1565 icaltime_adjust (&itt, 1, 0, 0, 0); 1566 } 1567 itt.hour = itt.minute = itt.second = 0; 1568 itt.is_date = TRUE; 1569 } 1570 e_cal_component_set_dtend (comp, &dt); 1571 1572 /* TRANSPARENCY */ 1573 transparency = all_day ? E_CAL_COMPONENT_TRANSP_TRANSPARENT 1574 : E_CAL_COMPONENT_TRANSP_OPAQUE; 1575 e_cal_component_set_transparency (comp, transparency); 1576 1577 /* CATEGORY */ 1578 e_cal_component_set_categories (comp, priv->default_category); 1579 1580 /* edit the object */ 1581 e_cal_component_commit_sequence (comp); 1582 1583 flags |= COMP_EDITOR_NEW_ITEM; 1584 if (meeting) { 1585 flags |= COMP_EDITOR_MEETING; 1586 flags |= COMP_EDITOR_USER_ORG; 1587 } 1588 1589 e_calendar_view_open_event_with_flags ( 1590 cal_view, default_client, icalcomp, flags); 1591 1592 g_object_unref (comp); 1593 } 1594 1595 /** 1596 * e_calendar_view_new_appointment_full 1597 * @cal_view: an #ECalendarView 1598 * @all_day: Whether create all day event or not. 1599 * @meeting: This is a meeting or an appointment. 1600 * @no_past_date: Don't create event in past date, use actual date instead 1601 * (if %TRUE). 1602 * 1603 * Opens an event editor dialog for a new appointment. The appointment's 1604 * start and end times are set to the currently selected time range in 1605 * the calendar view. 1606 * 1607 * When the selection is for all day and we don't need @all_day event, 1608 * then this do a rounding to the actual hour for actual day (today) and 1609 * to the 'day begins' from preferences in other selected day. 1610 */ 1611 void 1612 e_calendar_view_new_appointment_full (ECalendarView *cal_view, 1613 gboolean all_day, 1614 gboolean meeting, 1615 gboolean no_past_date) 1616 { 1617 ECalModel *model; 1618 time_t dtstart, dtend, now; 1619 gboolean do_rounding = FALSE; 1620 1621 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1622 1623 model = e_calendar_view_get_model (cal_view); 1624 1625 now = time (NULL); 1626 1627 if (!e_calendar_view_get_selected_time_range (cal_view, &dtstart, &dtend)) { 1628 dtstart = now; 1629 dtend = dtstart + 3600; 1630 } 1631 1632 if (no_past_date && dtstart < now) { 1633 dtend = time_day_begin (now) + (dtend - dtstart); 1634 dtstart = time_day_begin (now); 1635 do_rounding = TRUE; 1636 } 1637 1638 /* We either need rounding or don't want to set all_day for this, we will rather use actual */ 1639 /* time in this cases; dtstart should be a midnight in this case */ 1640 if (do_rounding || (!all_day && (dtend - dtstart) == (60 * 60 * 24))) { 1641 struct tm local = *localtime (&now); 1642 gint time_div = e_calendar_view_get_time_divisions (cal_view); 1643 gint hours, mins; 1644 1645 if (!time_div) /* Possible if your settings values aren't so nice */ 1646 time_div = 30; 1647 1648 if (time_day_begin (now) == time_day_begin (dtstart)) { 1649 /* same day as today */ 1650 hours = local.tm_hour; 1651 mins = local.tm_min; 1652 1653 /* round minutes to nearest time division, up or down */ 1654 if ((mins % time_div) >= time_div / 2) 1655 mins += time_div; 1656 mins = (mins - (mins % time_div)); 1657 } else { 1658 /* other day than today */ 1659 hours = e_cal_model_get_work_day_start_hour (model); 1660 mins = e_cal_model_get_work_day_start_minute (model); 1661 } 1662 1663 dtstart = dtstart + (60 * 60 * hours) + (mins * 60); 1664 dtend = dtstart + (time_div * 60); 1665 } 1666 1667 e_calendar_view_new_appointment_for (cal_view, dtstart, dtend, all_day, meeting); 1668 } 1669 1670 void 1671 e_calendar_view_new_appointment (ECalendarView *cal_view) 1672 { 1673 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1674 1675 e_calendar_view_new_appointment_full (cal_view, FALSE, FALSE, FALSE); 1676 } 1677 1678 /* Ensures the calendar is selected */ 1679 static void 1680 object_created_cb (CompEditor *ce, 1681 ECalendarView *cal_view) 1682 { 1683 e_calendar_view_emit_user_created (cal_view, comp_editor_get_client (ce)); 1684 } 1685 1686 CompEditor * 1687 e_calendar_view_open_event_with_flags (ECalendarView *cal_view, 1688 ECalClient *client, 1689 icalcomponent *icalcomp, 1690 guint32 flags) 1691 { 1692 CompEditor *ce; 1693 const gchar *uid; 1694 ECalComponent *comp; 1695 EShell *shell; 1696 1697 /* FIXME ECalendarView should own an EShell pointer. */ 1698 shell = e_shell_get_default (); 1699 1700 uid = icalcomponent_get_uid (icalcomp); 1701 1702 ce = comp_editor_find_instance (uid); 1703 if (!ce) { 1704 ce = event_editor_new (client, shell, flags); 1705 1706 g_signal_connect ( 1707 ce, "object_created", 1708 G_CALLBACK (object_created_cb), cal_view); 1709 1710 comp = e_cal_component_new (); 1711 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp)); 1712 comp_editor_edit_comp (ce, comp); 1713 if (flags & COMP_EDITOR_MEETING) 1714 event_editor_show_meeting (EVENT_EDITOR (ce)); 1715 1716 g_object_unref (comp); 1717 } 1718 1719 gtk_window_present (GTK_WINDOW (ce)); 1720 1721 return ce; 1722 } 1723 1724 /** 1725 * e_calendar_view_edit_appointment 1726 * @cal_view: A calendar view. 1727 * @client: Calendar client. 1728 * @icalcomp: The object to be edited. 1729 * @mode: one of #EEditEventMode 1730 * 1731 * Opens an editor window to allow the user to edit the selected 1732 * object. 1733 */ 1734 void 1735 e_calendar_view_edit_appointment (ECalendarView *cal_view, 1736 ECalClient *client, 1737 icalcomponent *icalcomp, 1738 EEditEventMode mode) 1739 { 1740 ECalModel *model; 1741 ESourceRegistry *registry; 1742 guint32 flags = 0; 1743 1744 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1745 g_return_if_fail (E_IS_CAL_CLIENT (client)); 1746 g_return_if_fail (icalcomp != NULL); 1747 1748 model = e_calendar_view_get_model (cal_view); 1749 registry = e_cal_model_get_registry (model); 1750 1751 if ((mode == EDIT_EVENT_AUTODETECT && icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY) != NULL) 1752 || mode == EDIT_EVENT_FORCE_MEETING) { 1753 ECalComponent *comp = e_cal_component_new (); 1754 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp)); 1755 flags |= COMP_EDITOR_MEETING; 1756 if (itip_organizer_is_user (registry, comp, client) || 1757 itip_sentby_is_user (registry, comp, client) || 1758 !e_cal_component_has_attendees (comp)) 1759 flags |= COMP_EDITOR_USER_ORG; 1760 g_object_unref (comp); 1761 } 1762 1763 e_calendar_view_open_event_with_flags (cal_view, client, icalcomp, flags); 1764 } 1765 1766 void 1767 e_calendar_view_modify_and_send (ECalendarView *cal_view, 1768 ECalComponent *comp, 1769 ECalClient *client, 1770 CalObjModType mod, 1771 GtkWindow *toplevel, 1772 gboolean new) 1773 { 1774 ECalModel *model; 1775 ESourceRegistry *registry; 1776 gboolean only_new_attendees = FALSE; 1777 GError *error = NULL; 1778 1779 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 1780 1781 model = e_calendar_view_get_model (cal_view); 1782 registry = e_cal_model_get_registry (model); 1783 1784 e_cal_component_commit_sequence (comp); 1785 1786 e_cal_client_modify_object_sync ( 1787 client, e_cal_component_get_icalcomponent (comp), 1788 mod, NULL, &error); 1789 1790 if (error == NULL) { 1791 gboolean strip_alarms = TRUE; 1792 1793 if ((itip_organizer_is_user (registry, comp, client) || 1794 itip_sentby_is_user (registry, comp, client)) && 1795 send_component_dialog (toplevel, client, comp, new, &strip_alarms, &only_new_attendees)) { 1796 ECalComponent *send_comp = NULL; 1797 1798 if (mod == CALOBJ_MOD_ALL && e_cal_component_is_instance (comp)) { 1799 /* Ensure we send the master object, not the instance only */ 1800 icalcomponent *icalcomp = NULL; 1801 const gchar *uid = NULL; 1802 1803 e_cal_component_get_uid (comp, &uid); 1804 if (e_cal_client_get_object_sync (client, uid, NULL, &icalcomp, NULL, NULL) && icalcomp) { 1805 send_comp = e_cal_component_new (); 1806 if (!e_cal_component_set_icalcomponent (send_comp, icalcomp)) { 1807 icalcomponent_free (icalcomp); 1808 g_object_unref (send_comp); 1809 send_comp = NULL; 1810 } else if (only_new_attendees) { 1811 /* copy new-attendees information too if required for later use */ 1812 comp_editor_copy_new_attendees (send_comp, comp); 1813 } 1814 } 1815 } 1816 1817 itip_send_comp ( 1818 registry, E_CAL_COMPONENT_METHOD_REQUEST, 1819 send_comp ? send_comp : comp, client, NULL, 1820 NULL, NULL, strip_alarms, only_new_attendees); 1821 1822 if (send_comp) 1823 g_object_unref (send_comp); 1824 } 1825 } else { 1826 g_message ( 1827 G_STRLOC ": Could not update the object! %s", 1828 error->message); 1829 1830 g_error_free (error); 1831 } 1832 } 1833 1834 static gboolean 1835 tooltip_grab (GtkWidget *tooltip, 1836 GdkEventKey *event, 1837 ECalendarView *view) 1838 { 1839 GtkWidget *widget = (GtkWidget *) g_object_get_data (G_OBJECT (view), "tooltip-window"); 1840 1841 if (!widget) 1842 return TRUE; 1843 1844 gdk_keyboard_ungrab (GDK_CURRENT_TIME); 1845 gtk_widget_destroy (widget); 1846 g_object_set_data (G_OBJECT (view), "tooltip-window", NULL); 1847 1848 return FALSE; 1849 } 1850 1851 static gchar * 1852 get_label (struct icaltimetype *tt, 1853 icaltimezone *f_zone, 1854 icaltimezone *t_zone) 1855 { 1856 struct tm tmp_tm; 1857 1858 tmp_tm = icaltimetype_to_tm_with_zone (tt, f_zone, t_zone); 1859 1860 return e_datetime_format_format_tm ("calendar", "table", DTFormatKindDateTime, &tmp_tm); 1861 } 1862 1863 void 1864 e_calendar_view_move_tip (GtkWidget *widget, 1865 gint x, 1866 gint y) 1867 { 1868 GtkAllocation allocation; 1869 GtkRequisition requisition; 1870 gint w, h; 1871 GdkScreen *screen; 1872 GdkScreen *pointer_screen; 1873 gint monitor_num, px, py; 1874 GdkRectangle monitor; 1875 1876 screen = gtk_widget_get_screen (widget); 1877 1878 gtk_widget_get_preferred_size (widget, &requisition, NULL); 1879 w = requisition.width; 1880 h = requisition.height; 1881 1882 gdk_display_get_pointer ( 1883 gdk_screen_get_display (screen), 1884 &pointer_screen, &px, &py, NULL); 1885 if (pointer_screen != screen) { 1886 px = x; 1887 py = y; 1888 } 1889 monitor_num = gdk_screen_get_monitor_at_point (screen, px, py); 1890 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); 1891 1892 if ((x + w) > monitor.x + monitor.width) 1893 x -= (x + w) - (monitor.x + monitor.width); 1894 else if (x < monitor.x) 1895 x = monitor.x; 1896 1897 gtk_widget_get_allocation (widget, &allocation); 1898 1899 if ((y + h + allocation.height + 4) > monitor.y + monitor.height) 1900 y = y - h - 36; 1901 1902 gtk_window_move (GTK_WINDOW (widget), x, y); 1903 gtk_widget_show (widget); 1904 } 1905 1906 /* 1907 * It is expected to show the tooltips in this below format 1908 * 1909 * <B>SUBJECT OF THE MEETING</B> 1910 * Organiser: NameOfTheUser<email@ofuser.com> 1911 * Location: PlaceOfTheMeeting 1912 * Time : DateAndTime (xx Minutes) 1913 * Status: Accepted: X Declined: Y ... 1914 */ 1915 1916 gboolean 1917 e_calendar_view_get_tooltips (const ECalendarViewEventData *data) 1918 { 1919 GtkWidget *label, *box, *hbox, *ebox, *frame; 1920 const gchar *str; 1921 gchar *tmp, *tmp1, *tmp2; 1922 ECalComponentOrganizer organiser; 1923 ECalComponentDateTime dtstart, dtend; 1924 icalcomponent *clone_comp; 1925 time_t t_start, t_end; 1926 ECalendarViewEvent *pevent; 1927 GtkStyle *style = gtk_widget_get_default_style (); 1928 GtkWidget *widget; 1929 GdkWindow *window; 1930 ECalComponent *newcomp = e_cal_component_new (); 1931 icaltimezone *zone, *default_zone; 1932 ECalModel *model; 1933 ECalClient *client = NULL; 1934 gboolean free_text = FALSE; 1935 1936 /* This function is a timeout callback. */ 1937 1938 g_return_val_if_fail (data != NULL, FALSE); 1939 g_return_val_if_fail (E_IS_CALENDAR_VIEW (data->cal_view), FALSE); 1940 1941 model = e_calendar_view_get_model (data->cal_view); 1942 1943 /* Delete any stray tooltip if left */ 1944 widget = g_object_get_data ( 1945 G_OBJECT (data->cal_view), "tooltip-window"); 1946 if (GTK_IS_WIDGET (widget)) 1947 gtk_widget_destroy (widget); 1948 1949 default_zone = e_calendar_view_get_timezone (data->cal_view); 1950 pevent = data->get_view_event (data->cal_view, data->day, data->event_num); 1951 1952 if (!is_comp_data_valid (pevent)) 1953 return FALSE; 1954 1955 client = pevent->comp_data->client; 1956 1957 clone_comp = icalcomponent_new_clone (pevent->comp_data->icalcomp); 1958 if (!e_cal_component_set_icalcomponent (newcomp, clone_comp)) 1959 g_warning ("couldn't update calendar component with modified data from backend\n"); 1960 1961 box = gtk_vbox_new (FALSE, 0); 1962 1963 str = e_calendar_view_get_icalcomponent_summary (pevent->comp_data->client, pevent->comp_data->icalcomp, &free_text); 1964 1965 if (!(str && *str)) { 1966 g_object_unref (newcomp); 1967 gtk_widget_destroy (box); 1968 1969 return FALSE; 1970 } 1971 1972 tmp = g_markup_printf_escaped ("<b>%s</b>", str); 1973 label = gtk_label_new (NULL); 1974 gtk_label_set_line_wrap ((GtkLabel *) label, TRUE); 1975 gtk_label_set_markup ((GtkLabel *) label, tmp); 1976 1977 if (free_text) { 1978 g_free ((gchar *) str); 1979 str = NULL; 1980 } 1981 1982 hbox = gtk_hbox_new (FALSE, 0); 1983 gtk_box_pack_start ((GtkBox *) hbox, label, FALSE, FALSE, 0); 1984 ebox = gtk_event_box_new (); 1985 gtk_container_add ((GtkContainer *) ebox, hbox); 1986 gtk_widget_modify_bg (ebox, GTK_STATE_NORMAL, &(style->bg[GTK_STATE_SELECTED])); 1987 gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &(style->text[GTK_STATE_SELECTED])); 1988 1989 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0); 1990 g_free (tmp); 1991 1992 e_cal_component_get_organizer (newcomp, &organiser); 1993 if (organiser.cn) { 1994 gchar *ptr; 1995 ptr = strchr (organiser.value, ':'); 1996 1997 if (ptr) { 1998 ptr++; 1999 /* To Translators: It will display "Organiser: NameOfTheUser <email@ofuser.com>" */ 2000 tmp = g_strdup_printf (_("Organizer: %s <%s>"), organiser.cn, ptr); 2001 } 2002 else 2003 /* With SunOne accouts, there may be no ':' in organiser.value*/ 2004 tmp = g_strdup_printf (_("Organizer: %s"), organiser.cn); 2005 2006 label = gtk_label_new (tmp); 2007 hbox = gtk_hbox_new (FALSE, 0); 2008 gtk_box_pack_start ((GtkBox *) hbox, label, FALSE, FALSE, 0); 2009 ebox = gtk_event_box_new (); 2010 gtk_container_add ((GtkContainer *) ebox, hbox); 2011 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0); 2012 2013 g_free (tmp); 2014 } 2015 2016 e_cal_component_get_location (newcomp, &str); 2017 2018 if (str) { 2019 /* To Translators: It will display "Location: PlaceOfTheMeeting" */ 2020 tmp = g_markup_printf_escaped (_("Location: %s"), str); 2021 label = gtk_label_new (NULL); 2022 gtk_label_set_markup ((GtkLabel *) label, tmp); 2023 hbox = gtk_hbox_new (FALSE, 0); 2024 gtk_box_pack_start ((GtkBox *) hbox, label, FALSE, FALSE, 0); 2025 ebox = gtk_event_box_new (); 2026 gtk_container_add ((GtkContainer *) ebox, hbox); 2027 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0); 2028 g_free (tmp); 2029 } 2030 e_cal_component_get_dtstart (newcomp, &dtstart); 2031 e_cal_component_get_dtend (newcomp, &dtend); 2032 2033 if (dtstart.tzid) { 2034 zone = icalcomponent_get_timezone (e_cal_component_get_icalcomponent (newcomp), dtstart.tzid); 2035 if (!zone) 2036 e_cal_client_get_timezone_sync (client, dtstart.tzid, &zone, NULL, NULL); 2037 2038 if (!zone) 2039 zone = default_zone; 2040 2041 } else { 2042 zone = NULL; 2043 } 2044 t_start = icaltime_as_timet_with_zone (*dtstart.value, zone); 2045 t_end = icaltime_as_timet_with_zone (*dtend.value, zone); 2046 2047 tmp1 = get_label (dtstart.value, zone, default_zone); 2048 tmp = calculate_time (t_start, t_end); 2049 2050 /* To Translators: It will display "Time: ActualStartDateAndTime (DurationOfTheMeeting)"*/ 2051 tmp2 = g_strdup_printf (_("Time: %s %s"), tmp1, tmp); 2052 if (zone && !cal_comp_util_compare_event_timezones (newcomp, client, default_zone)) { 2053 g_free (tmp); 2054 g_free (tmp1); 2055 2056 tmp1 = get_label (dtstart.value, zone, zone); 2057 tmp = g_strconcat (tmp2, "\n\t[ ", tmp1, " ", icaltimezone_get_display_name (zone), " ]", NULL); 2058 } else { 2059 g_free (tmp); 2060 tmp = tmp2; 2061 tmp2 = NULL; 2062 } 2063 2064 e_cal_component_free_datetime (&dtstart); 2065 e_cal_component_free_datetime (&dtend); 2066 2067 hbox = gtk_hbox_new (FALSE, 0); 2068 gtk_box_pack_start ((GtkBox *) hbox, gtk_label_new_with_mnemonic (tmp), FALSE, FALSE, 0); 2069 ebox = gtk_event_box_new (); 2070 gtk_container_add ((GtkContainer *) ebox, hbox); 2071 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0); 2072 2073 g_free (tmp); 2074 g_free (tmp2); 2075 g_free (tmp1); 2076 2077 tmp = e_cal_model_get_attendees_status_info ( 2078 model, newcomp, pevent->comp_data->client); 2079 if (tmp) { 2080 hbox = gtk_hbox_new (FALSE, 0); 2081 gtk_box_pack_start ((GtkBox *) hbox, gtk_label_new (tmp), FALSE, FALSE, 0); 2082 ebox = gtk_event_box_new (); 2083 gtk_container_add ((GtkContainer *) ebox, hbox); 2084 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0); 2085 2086 g_free (tmp); 2087 } 2088 2089 pevent->tooltip = gtk_window_new (GTK_WINDOW_POPUP); 2090 frame = gtk_frame_new (NULL); 2091 gtk_frame_set_shadow_type ((GtkFrame *) frame, GTK_SHADOW_IN); 2092 2093 gtk_window_set_type_hint (GTK_WINDOW (pevent->tooltip), GDK_WINDOW_TYPE_HINT_TOOLTIP); 2094 gtk_window_move ((GtkWindow *) pevent->tooltip, pevent->x +16, pevent->y + 16); 2095 gtk_container_add ((GtkContainer *) frame, box); 2096 gtk_container_add ((GtkContainer *) pevent->tooltip, frame); 2097 2098 gtk_widget_show_all (pevent->tooltip); 2099 2100 e_calendar_view_move_tip (pevent->tooltip, pevent->x +16, pevent->y + 16); 2101 2102 window = gtk_widget_get_window (pevent->tooltip); 2103 gdk_keyboard_grab (window, FALSE, GDK_CURRENT_TIME); 2104 g_signal_connect ( 2105 pevent->tooltip, "key-press-event", 2106 G_CALLBACK (tooltip_grab), data->cal_view); 2107 pevent->timeout = -1; 2108 2109 g_object_set_data (G_OBJECT (data->cal_view), "tooltip-window", pevent->tooltip); 2110 g_object_unref (newcomp); 2111 2112 return FALSE; 2113 } 2114 2115 static gboolean 2116 icalcomp_contains_category (icalcomponent *icalcomp, 2117 const gchar *category) 2118 { 2119 icalproperty *property; 2120 2121 g_return_val_if_fail (icalcomp != NULL && category != NULL, FALSE); 2122 2123 for (property = icalcomponent_get_first_property (icalcomp, ICAL_CATEGORIES_PROPERTY); 2124 property != NULL; 2125 property = icalcomponent_get_next_property (icalcomp, ICAL_CATEGORIES_PROPERTY)) { 2126 gchar *value = icalproperty_get_value_as_string_r (property); 2127 2128 if (value && strcmp (category, value) == 0) { 2129 g_free (value); 2130 return TRUE; 2131 } 2132 g_free (value); 2133 } 2134 2135 return FALSE; 2136 } 2137 2138 /* e_calendar_view_get_icalcomponent_summary returns summary of calcomp, 2139 * and for type of birthday or anniversary it append number of years since 2140 * beginning. In this case, the free_text is set to TRUE and caller need 2141 * to g_free returned string, otherwise free_text is set to FALSE and 2142 * returned value is owned by calcomp. 2143 */ 2144 2145 const gchar * 2146 e_calendar_view_get_icalcomponent_summary (ECalClient *client, 2147 icalcomponent *icalcomp, 2148 gboolean *free_text) 2149 { 2150 const gchar *summary; 2151 2152 g_return_val_if_fail (icalcomp != NULL && free_text != NULL, NULL); 2153 2154 *free_text = FALSE; 2155 summary = icalcomponent_get_summary (icalcomp); 2156 2157 if (icalcomp_contains_category (icalcomp, _("Birthday")) || 2158 icalcomp_contains_category (icalcomp, _("Anniversary"))) { 2159 icalproperty *xprop; 2160 2161 for (xprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); 2162 xprop; 2163 xprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { 2164 const gchar *xname = icalproperty_get_x_name (xprop); 2165 2166 if (xname && g_ascii_strcasecmp (xname, "X-EVOLUTION-SINCE-YEAR") == 0) { 2167 struct icaltimetype dtnow; 2168 gint since_year; 2169 gchar *str; 2170 2171 str = icalproperty_get_value_as_string_r (xprop); 2172 since_year = str ? atoi (str) : 0; 2173 g_free (str); 2174 2175 dtnow = icalcomponent_get_dtstart (icalcomp); 2176 2177 if (since_year > 0 && dtnow.year - since_year > 0) { 2178 summary = g_strdup_printf ("%s (%d)", summary ? summary : "", dtnow.year - since_year); 2179 *free_text = summary != NULL; 2180 } 2181 2182 break; 2183 } 2184 } 2185 } 2186 2187 return summary; 2188 } 2189 2190 void 2191 e_calendar_view_emit_user_created (ECalendarView *cal_view, 2192 ECalClient *where_was_created) 2193 { 2194 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); 2195 2196 g_signal_emit (cal_view, signals[USER_CREATED], 0, where_was_created); 2197 } 2198 2199 void 2200 draw_curved_rectangle (cairo_t *cr, 2201 gdouble x0, 2202 gdouble y0, 2203 gdouble rect_width, 2204 gdouble rect_height, 2205 gdouble radius) 2206 { 2207 gdouble x1, y1; 2208 2209 x1 = x0 + rect_width; 2210 y1 = y0 + rect_height; 2211 2212 if (!rect_width || !rect_height) 2213 return; 2214 if (rect_width / 2 < radius) { 2215 if (rect_height / 2 < radius) { 2216 cairo_move_to (cr, x0, (y0 + y1) / 2); 2217 cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0); 2218 cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); 2219 cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); 2220 cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); 2221 } else { 2222 cairo_move_to (cr, x0, y0 + radius); 2223 cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0); 2224 cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); 2225 cairo_line_to (cr, x1 , y1 - radius); 2226 cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); 2227 cairo_curve_to (cr, x0, y1, x0, y1, x0, y1- radius); 2228 } 2229 } else { 2230 if (rect_height / 2 < radius) { 2231 cairo_move_to (cr, x0, (y0 + y1) / 2); 2232 cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0); 2233 cairo_line_to (cr, x1 - radius, y0); 2234 cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); 2235 cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); 2236 cairo_line_to (cr, x0 + radius, y1); 2237 cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); 2238 } else { 2239 cairo_move_to (cr, x0, y0 + radius); 2240 cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0); 2241 cairo_line_to (cr, x1 - radius, y0); 2242 cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); 2243 cairo_line_to (cr, x1 , y1 - radius); 2244 cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); 2245 cairo_line_to (cr, x0 + radius, y1); 2246 cairo_curve_to (cr, x0, y1, x0, y1, x0, y1- radius); 2247 } 2248 } 2249 cairo_close_path (cr); 2250 } 2251 2252 /* returns either light or dark yellow, based on the base_background, 2253 * which is the default background color */ 2254 GdkColor 2255 get_today_background (const GdkColor base_background) 2256 { 2257 GdkColor res = base_background; 2258 2259 if (res.red > 0x7FFF) { 2260 /* light yellow for a light theme */ 2261 res.red = 0xFFFF; 2262 res.green = 0xFFFF; 2263 res.blue = 0xC0C0; 2264 } else { 2265 /* dark yellow for a dark theme */ 2266 res.red = 0x3F3F; 2267 res.green = 0x3F3F; 2268 res.blue = 0x0000; 2269 } 2270 2271 return res; 2272 } 2273 2274 gboolean 2275 is_comp_data_valid_func (ECalendarViewEvent *event, 2276 const gchar *location) 2277 { 2278 g_return_val_if_fail (location != NULL, FALSE); 2279 2280 if (!event) { 2281 g_warning ("%s: event is NULL", location); 2282 return FALSE; 2283 } 2284 2285 if (!event->comp_data) { 2286 g_warning ("%s: event's (%p) comp_data is NULL", location, event); 2287 return FALSE; 2288 } 2289 2290 return TRUE; 2291 } 2292 2293 gboolean 2294 is_array_index_in_bounds_func (GArray *array, 2295 gint index, 2296 const gchar *location) 2297 { 2298 g_return_val_if_fail (location != NULL, FALSE); 2299 2300 if (!array) { 2301 g_warning ("%s: array is NULL", location); 2302 return FALSE; 2303 } 2304 2305 if (index < 0 || index >= array->len) { 2306 g_warning ("%s: index %d is out of bounds [0,%d) at array %p", location, index, array->len, array); 2307 return FALSE; 2308 } 2309 2310 return TRUE; 2311 }