evolution-3.6.4/calendar/gui/dialogs/event-page.c

Location Tool Test ID Function Issue
event-page.c:817:3 clang-analyzer Access to field 'year' results in a dereference of a null pointer (loaded from variable 'end_tt')
event-page.c:817:3 clang-analyzer Access to field 'year' results in a dereference of a null pointer (loaded from variable 'end_tt')
   1 /*
   2  * Evolution calendar - Main page of the event editor dialog
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Authors:
  19  *		Federico Mena-Quintero <federico@ximian.com>
  20  *      Miguel de Icaza <miguel@ximian.com>
  21  *      Seth Alves <alves@hungry.com>
  22  *      JP Rosevear <jpr@ximian.com>
  23  *
  24  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  25  *
  26  */
  27 
  28 #ifdef HAVE_CONFIG_H
  29 #include <config.h>
  30 #endif
  31 
  32 #include <errno.h>
  33 #include <string.h>
  34 #include <gtk/gtk.h>
  35 #include <glib/gi18n.h>
  36 #include <gdk/gdkkeysyms.h>
  37 
  38 #include <libedataserverui/libedataserverui.h>
  39 
  40 #include <e-util/e-util.h>
  41 #include <e-util/e-categories-config.h>
  42 #include <e-util/e-dialog-utils.h>
  43 #include <e-util/e-dialog-widgets.h>
  44 #include <e-util/e-util-private.h>
  45 
  46 #include <misc/e-dateedit.h>
  47 #include <misc/e-send-options.h>
  48 #include <misc/e-spell-entry.h>
  49 #include <misc/e-buffer-tagger.h>
  50 
  51 #include "../e-alarm-list.h"
  52 #include "../e-meeting-attendee.h"
  53 #include "../e-meeting-list-view.h"
  54 #include "../e-meeting-store.h"
  55 #include "../e-timezone-entry.h"
  56 
  57 #include "alarm-list-dialog.h"
  58 #include "comp-editor-util.h"
  59 #include "comp-editor.h"
  60 #include "e-send-options-utils.h"
  61 #include "event-page.h"
  62 
  63 #define EVENT_PAGE_GET_PRIVATE(obj) \
  64 	(G_TYPE_INSTANCE_GET_PRIVATE \
  65 	((obj), TYPE_EVENT_PAGE, EventPagePrivate))
  66 
  67 #define EVENT_PAGE_GET_PRIVATE(obj) \
  68 	(G_TYPE_INSTANCE_GET_PRIVATE \
  69 	((obj), TYPE_EVENT_PAGE, EventPagePrivate))
  70 
  71 enum {
  72 	ALARM_NONE,
  73 	ALARM_15_MINUTES,
  74 	ALARM_1_HOUR,
  75 	ALARM_1_DAY,
  76 	ALARM_USER_TIME,
  77 	ALARM_CUSTOM
  78 };
  79 
  80 static const gint alarm_map_with_user_time[] = {
  81 	ALARM_NONE,
  82 	ALARM_15_MINUTES,
  83 	ALARM_1_HOUR,
  84 	ALARM_1_DAY,
  85 	ALARM_USER_TIME,
  86 	ALARM_CUSTOM,
  87 	-1
  88 };
  89 
  90 static const gint alarm_map_without_user_time[] = {
  91 	ALARM_NONE,
  92 	ALARM_15_MINUTES,
  93 	ALARM_1_HOUR,
  94 	ALARM_1_DAY,
  95 	ALARM_CUSTOM,
  96 	-1
  97 };
  98 
  99 /* Private part of the EventPage structure */
 100 struct _EventPagePrivate {
 101 	GtkBuilder *builder;
 102 
 103 	/* Widgets from the UI file */
 104 	GtkWidget *main;
 105 
 106 	/* Generic informative messages placeholder */
 107 	GtkWidget *info_hbox;
 108 	GtkWidget *info_icon;
 109 	GtkWidget *info_string;
 110 
 111 	GtkWidget *summary;
 112 	GtkWidget *summary_label;
 113 	GtkWidget *location;
 114 	GtkWidget *location_label;
 115 	GtkEntryCompletion *location_completion;
 116 
 117 	gchar **address_strings;
 118 	gchar *fallback_address;
 119 	EMeetingAttendee *ia;
 120 	gchar *user_add;
 121 	ECalComponent *comp;
 122 
 123 	/* For meeting/event */
 124 	GtkWidget *calendar_label;
 125 	GtkWidget *org_cal_label;
 126 	GtkWidget *attendee_box;
 127 
 128 	/* Lists of attendees */
 129 	GPtrArray *deleted_attendees;
 130 
 131 	GtkWidget *start_time;
 132 	GtkWidget *end_time;
 133 	GtkWidget *end_time_combo;
 134 	GtkWidget *time_hour;
 135 	GtkWidget *hour_selector;
 136 	GtkWidget *minute_selector;
 137 	GtkWidget *start_timezone;
 138 	GtkWidget *end_timezone;
 139 	GtkWidget *timezone_label;
 140 	gboolean   all_day_event;
 141 	GtkWidget *status_icons;
 142 	GtkWidget *alarm_icon;
 143 	GtkWidget *recur_icon;
 144 
 145 	GtkWidget *description;
 146 
 147 	gboolean  show_time_as_busy;
 148 
 149 	GtkWidget *alarm_dialog;
 150 	GtkWidget *alarm_time_combo;
 151 	GtkWidget *alarm_warning;
 152 	GtkWidget *alarm_box;
 153 
 154 	GtkWidget *categories_btn;
 155 	GtkWidget *categories;
 156 
 157 	GtkWidget *source_combo_box;
 158 
 159 	/* Meeting related items */
 160 	GtkWidget *list_box;
 161 	GtkWidget *organizer_table;
 162 	GtkWidget *organizer;
 163 	GtkWidget *add;
 164 	GtkWidget *remove;
 165 	GtkWidget *edit;
 166 	GtkWidget *invite;
 167 	GtkWidget *invite_label;
 168 	GtkWidget *attendees_label;
 169 
 170 	/* ListView stuff */
 171 	EMeetingStore *meeting_store;
 172 	EMeetingListView *list_view;
 173 	gint row;
 174 
 175 	/* For handling who the organizer is */
 176 	gboolean user_org;
 177 	gboolean existing;
 178 
 179 	EAlarmList *alarm_list_store;
 180 
 181 	gboolean sendoptions_shown;
 182 
 183 	ESendOptionsDialog *sod;
 184 	gchar *old_summary;
 185 	EDurationType alarm_units;
 186 	gint alarm_interval;
 187 
 188 	/* This is TRUE if both the start & end timezone are the same. If the
 189 	 * start timezone is then changed, we updated the end timezone to the
 190 	 * same value, since 99% of events start and end in one timezone. */
 191 	gboolean sync_timezones;
 192 	gboolean is_meeting;
 193 
 194 	GtkWidget *alarm_list_dlg_widget;
 195 
 196 	/* either with-user-time or without it */
 197 	const gint *alarm_map;
 198 
 199 	GCancellable *open_cancellable;
 200 };
 201 
 202 static void event_page_set_dates (CompEditorPage *page, CompEditorPageDates *dates);
 203 static void notify_dates_changed (EventPage *epage, struct icaltimetype *start_tt, struct icaltimetype *end_tt);
 204 static gboolean check_start_before_end (struct icaltimetype *start_tt, icaltimezone *start_zone,
 205 					struct icaltimetype *end_tt, icaltimezone *end_zone, gboolean adjust_end_time);
 206 static void set_attendees (ECalComponent *comp, const GPtrArray *attendees);
 207 static void hour_sel_changed (GtkSpinButton *widget, EventPage *epage);
 208 static void minute_sel_changed (GtkSpinButton *widget, EventPage *epage);
 209 static void hour_minute_changed (EventPage *epage);
 210 static void update_end_time_combo (EventPage *epage);
 211 static void event_page_select_organizer (EventPage *epage, const gchar *backend_address);
 212 static void set_subscriber_info_string (EventPage *epage, const gchar *backend_address);
 213 
 214 G_DEFINE_TYPE (EventPage, event_page, TYPE_COMP_EDITOR_PAGE)
 215 
 216 static gboolean
 217 get_current_identity (EventPage *page,
 218                       gchar **name,
 219                       gchar **mailto)
 220 {
 221 	EShell *shell;
 222 	CompEditor *editor;
 223 	ESourceRegistry *registry;
 224 	GList *list, *iter;
 225 	GtkWidget *entry;
 226 	const gchar *extension_name;
 227 	const gchar *text;
 228 	gboolean match = FALSE;
 229 
 230 	entry = gtk_bin_get_child (GTK_BIN (page->priv->organizer));
 231 	text = gtk_entry_get_text (GTK_ENTRY (entry));
 232 
 233 	if (text == NULL || *text == '\0')
 234 		return FALSE;
 235 
 236 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
 237 	shell = comp_editor_get_shell (editor);
 238 
 239 	registry = e_shell_get_registry (shell);
 240 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
 241 
 242 	list = e_source_registry_list_sources (registry, extension_name);
 243 
 244 	for (iter = list; !match && iter != NULL; iter = g_list_next (iter)) {
 245 		ESource *source = E_SOURCE (iter->data);
 246 		ESourceMailIdentity *extension;
 247 		const gchar *id_name;
 248 		const gchar *id_address;
 249 		gchar *identity;
 250 
 251 		extension = e_source_get_extension (source, extension_name);
 252 
 253 		id_name = e_source_mail_identity_get_name (extension);
 254 		id_address = e_source_mail_identity_get_address (extension);
 255 
 256 		if (id_name == NULL || id_address == NULL)
 257 			continue;
 258 
 259 		identity = g_strdup_printf ("%s <%s>", id_name, id_address);
 260 		match = (g_ascii_strcasecmp (text, identity) == 0);
 261 		g_free (identity);
 262 
 263 		if (match && name != NULL)
 264 			*name = g_strdup (id_name);
 265 
 266 		if (match && mailto != NULL)
 267 			*mailto = g_strdup_printf ("MAILTO:%s", id_address);
 268 	}
 269 
 270 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
 271 
 272 	return match;
 273 }
 274 
 275 static void
 276 set_all_day_event_menu (EventPage *epage,
 277                         gboolean active)
 278 {
 279 	CompEditor *editor;
 280 	GtkAction *action;
 281 
 282 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage));
 283 	action = comp_editor_get_action (editor, "all-day-event");
 284 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
 285 }
 286 
 287 /* Sets the 'All Day Event' flag to the given value (without emitting signals),
 288  * and shows or hides the widgets as appropriate. */
 289 static void
 290 set_all_day (EventPage *epage,
 291              gboolean all_day)
 292 {
 293 	set_all_day_event_menu (epage, all_day);
 294 
 295 	/* TODO implement for in end time selector */
 296 	if (all_day)
 297 		gtk_combo_box_set_active (
 298 			GTK_COMBO_BOX (epage->priv->end_time_combo), 1);
 299 	gtk_widget_set_sensitive (epage->priv->end_time_combo, !all_day);
 300 
 301 	e_date_edit_set_show_time (
 302 		E_DATE_EDIT (epage->priv->start_time), !all_day);
 303 	e_date_edit_set_show_time (
 304 		E_DATE_EDIT (epage->priv->end_time), !all_day);
 305 }
 306 
 307 static void
 308 enable_busy_time_menu (EventPage *epage,
 309                        gboolean sensitive)
 310 {
 311 	CompEditor *editor;
 312 	GtkAction *action;
 313 
 314 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage));
 315 	action = comp_editor_get_action (editor, "show-time-busy");
 316 	gtk_action_set_sensitive (action, sensitive);
 317 }
 318 
 319 static void
 320 set_busy_time_menu (EventPage *epage,
 321                     gboolean active)
 322 {
 323 	CompEditor *editor;
 324 	GtkAction *action;
 325 
 326 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage));
 327 	action = comp_editor_get_action (editor, "show-time-busy");
 328 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
 329 }
 330 
 331 static void
 332 clear_widgets (EventPage *epage)
 333 {
 334 	EventPagePrivate *priv = epage->priv;
 335 	CompEditor *editor;
 336 
 337 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage));
 338 
 339 	/* Summary, description */
 340 	gtk_entry_set_text (GTK_ENTRY (priv->summary), "");
 341 	gtk_entry_set_text (GTK_ENTRY (priv->location), "");
 342 	gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)), "", 0);
 343 	e_buffer_tagger_update_tags (GTK_TEXT_VIEW (priv->description));
 344 
 345 	/* Start and end times */
 346 	g_signal_handlers_block_matched (priv->start_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage);
 347 	g_signal_handlers_block_matched (priv->end_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage);
 348 
 349 	e_date_edit_set_time (E_DATE_EDIT (priv->start_time), 0);
 350 	e_date_edit_set_time (E_DATE_EDIT (priv->end_time), 0);
 351 
 352 	g_signal_handlers_unblock_matched (priv->start_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage);
 353 	g_signal_handlers_unblock_matched (priv->end_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage);
 354 
 355 	epage->priv->all_day_event = FALSE;
 356 	set_all_day (epage, FALSE);
 357 
 358 	/* Classification */
 359 	comp_editor_set_classification (editor, E_CAL_COMPONENT_CLASS_PUBLIC);
 360 
 361 	/* Show Time As (Transparency) */
 362 	priv->show_time_as_busy = TRUE;
 363 	set_busy_time_menu (epage, TRUE);
 364 
 365 	/* Alarm */
 366 	e_dialog_combo_box_set (priv->alarm_time_combo, ALARM_NONE, priv->alarm_map);
 367 
 368 	/* Categories */
 369 	gtk_entry_set_text (GTK_ENTRY (priv->categories), "");
 370 }
 371 
 372 static gboolean
 373 is_custom_alarm (ECalComponentAlarm *ca,
 374                  gchar *old_summary,
 375                  EDurationType user_units,
 376                  gint user_interval,
 377                  gint *alarm_type)
 378 {
 379 	ECalComponentAlarmTrigger trigger;
 380 	ECalComponentAlarmRepeat repeat;
 381 	ECalComponentAlarmAction action;
 382 	ECalComponentText desc;
 383 	icalcomponent *icalcomp;
 384 	icalproperty *icalprop;
 385 	icalattach *attach;
 386 	gboolean needs_desc = FALSE;
 387 
 388 	e_cal_component_alarm_get_action (ca, &action);
 389 	if (action != E_CAL_COMPONENT_ALARM_DISPLAY)
 390 		return TRUE;
 391 
 392 	e_cal_component_alarm_get_attach (ca, &attach);
 393 	if (attach)
 394 		return TRUE;
 395 
 396 	icalcomp = e_cal_component_alarm_get_icalcomponent (ca);
 397 	icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
 398 	while (icalprop) {
 399 		const gchar *x_name;
 400 
 401 		x_name = icalproperty_get_x_name (icalprop);
 402 		if (!strcmp (x_name, "X-EVOLUTION-NEEDS-DESCRIPTION"))
 403 			needs_desc = TRUE;
 404 
 405 		icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
 406 	}
 407 
 408 	if (!needs_desc) {
 409 		e_cal_component_alarm_get_description (ca, &desc);
 410 		if (!desc.value || !old_summary || strcmp (desc.value, old_summary))
 411 			return TRUE;
 412 	}
 413 
 414 	e_cal_component_alarm_get_repeat (ca, &repeat);
 415 	if (repeat.repetitions != 0)
 416 		return TRUE;
 417 
 418 	if (e_cal_component_alarm_has_attendees (ca))
 419 		return TRUE;
 420 
 421 	e_cal_component_alarm_get_trigger (ca, &trigger);
 422 	if (trigger.type != E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START)
 423 		return TRUE;
 424 
 425 	if (trigger.u.rel_duration.is_neg != 1)
 426 		return TRUE;
 427 
 428 	if (trigger.u.rel_duration.weeks != 0)
 429 		return TRUE;
 430 
 431 	if (trigger.u.rel_duration.seconds != 0)
 432 		return TRUE;
 433 
 434 	if (trigger.u.rel_duration.days == 1
 435 	    && trigger.u.rel_duration.hours == 0
 436 	    && trigger.u.rel_duration.minutes == 0) {
 437 		if (alarm_type)
 438 			*alarm_type = ALARM_1_DAY;
 439 		return FALSE;
 440 	}
 441 
 442 	if (trigger.u.rel_duration.days == 0
 443 	    && trigger.u.rel_duration.hours == 1
 444 	    && trigger.u.rel_duration.minutes == 0) {
 445 		if (alarm_type)
 446 			*alarm_type = ALARM_1_HOUR;
 447 		return FALSE;
 448 	}
 449 
 450 	if (trigger.u.rel_duration.days == 0
 451 	    && trigger.u.rel_duration.hours == 0
 452 	    && trigger.u.rel_duration.minutes == 15) {
 453 		if (alarm_type)
 454 			*alarm_type = ALARM_15_MINUTES;
 455 		return FALSE;
 456 	}
 457 
 458 	if (user_interval != -1) {
 459 		switch (user_units) {
 460 		case E_DURATION_DAYS:
 461 			if (trigger.u.rel_duration.days == user_interval
 462 			    && trigger.u.rel_duration.hours == 0
 463 			    && trigger.u.rel_duration.minutes == 0) {
 464 				if (alarm_type)
 465 					*alarm_type = ALARM_USER_TIME;
 466 				return FALSE;
 467 			}
 468 			break;
 469 
 470 		case E_DURATION_HOURS:
 471 			if (trigger.u.rel_duration.days == 0
 472 			    && trigger.u.rel_duration.hours == user_interval
 473 			    && trigger.u.rel_duration.minutes == 0) {
 474 				if (alarm_type)
 475 					*alarm_type = ALARM_USER_TIME;
 476 				return FALSE;
 477 			}
 478 			break;
 479 
 480 		case E_DURATION_MINUTES:
 481 			if (trigger.u.rel_duration.days == 0
 482 			    && trigger.u.rel_duration.hours == 0
 483 			    && trigger.u.rel_duration.minutes == user_interval) {
 484 				if (alarm_type)
 485 					*alarm_type = ALARM_USER_TIME;
 486 				return FALSE;
 487 			}
 488 			break;
 489 		}
 490 	}
 491 
 492 	return TRUE;
 493 }
 494 
 495 static gboolean
 496 is_custom_alarm_store (EAlarmList *alarm_list_store,
 497                        gchar *old_summary,
 498                        EDurationType user_units,
 499                        gint user_interval,
 500                        gint *alarm_type)
 501 {
 502 	const ECalComponentAlarm *alarm;
 503 	GtkTreeModel *model;
 504 	GtkTreeIter iter;
 505 	gboolean valid_iter;
 506 
 507 	model = GTK_TREE_MODEL (alarm_list_store);
 508 
 509 	valid_iter = gtk_tree_model_get_iter_first (model, &iter);
 510 	if (!valid_iter)
 511 		return FALSE;
 512 
 513 	alarm = e_alarm_list_get_alarm (alarm_list_store, &iter);
 514 	if (is_custom_alarm ((ECalComponentAlarm *) alarm, old_summary, user_units, user_interval, alarm_type))
 515 		return TRUE;
 516 
 517 	valid_iter = gtk_tree_model_iter_next (model, &iter);
 518 	if (valid_iter)
 519 		return TRUE;
 520 
 521 	return FALSE;
 522 }
 523 
 524 static gboolean
 525 is_custom_alarm_uid_list (ECalComponent *comp,
 526                           GList *alarms,
 527                           gchar *old_summary,
 528                           EDurationType user_units,
 529                           gint user_interval,
 530                           gint *alarm_type)
 531 {
 532 	ECalComponentAlarm *ca;
 533 	gboolean result;
 534 
 535 	if (g_list_length (alarms) > 1)
 536 		return TRUE;
 537 
 538 	ca = e_cal_component_get_alarm (comp, alarms->data);
 539 	result = is_custom_alarm (
 540 		ca, old_summary, user_units, user_interval, alarm_type);
 541 	e_cal_component_alarm_free (ca);
 542 
 543 	return result;
 544 }
 545 
 546 /* returns whether changed info text */
 547 static gboolean
 548 check_starts_in_the_past (EventPage *epage)
 549 {
 550 	EventPagePrivate *priv;
 551 	struct icaltimetype start_tt = icaltime_null_time ();
 552 	gboolean date_set;
 553 
 554 	if ((comp_editor_get_flags (comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage))) & COMP_EDITOR_NEW_ITEM) == 0)
 555 		return FALSE;
 556 
 557 	priv = epage->priv;
 558 	date_set = e_date_edit_get_date (E_DATE_EDIT (priv->start_time), &start_tt.year, &start_tt.month, &start_tt.day);
 559 
 560 	g_return_val_if_fail (date_set, FALSE);
 561 
 562 	if (priv->all_day_event) {
 563 		start_tt.is_date = TRUE;
 564 	} else {
 565 		e_date_edit_get_time_of_day (E_DATE_EDIT (priv->start_time), &start_tt.hour, &start_tt.minute);
 566 		start_tt.zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->start_timezone));
 567 	}
 568 
 569 	if (comp_editor_test_time_in_the_past (start_tt)) {
 570 		gchar *tmp = g_strconcat ("<b>", _("Event's start time is in the past"), "</b>", NULL);
 571 		event_page_set_info_string (epage, GTK_STOCK_DIALOG_WARNING, tmp);
 572 		g_free (tmp);
 573 	} else {
 574 		event_page_set_info_string (epage, NULL, NULL);
 575 	}
 576 
 577 	return TRUE;
 578 }
 579 
 580 static void
 581 alarm_image_button_clicked_cb (GtkWidget *button,
 582                                EventPage *epage)
 583 {
 584 	CompEditor *editor;
 585 	GtkAction *action;
 586 
 587 	g_return_if_fail (IS_EVENT_PAGE (epage));
 588 
 589 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage));
 590 	action = comp_editor_get_action (editor, "alarms");
 591 	gtk_action_activate (action);
 592 }
 593 
 594 static GtkWidget *
 595 create_alarm_image_button (const gchar *image_text,
 596                            const gchar *tip_text,
 597                            EventPage *epage)
 598 {
 599 	GtkWidget *image, *button;
 600 
 601 	button = gtk_button_new ();
 602 	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
 603 	gtk_widget_set_can_focus (button, FALSE);
 604 
 605 	image = gtk_image_new_from_icon_name (image_text, GTK_ICON_SIZE_MENU);
 606 
 607 	gtk_container_add ((GtkContainer *) button, image);
 608 	gtk_widget_show_all (button);
 609 	gtk_widget_set_tooltip_text (button, tip_text);
 610 
 611 	g_signal_connect (
 612 		button, "clicked",
 613 		G_CALLBACK (alarm_image_button_clicked_cb), epage);
 614 
 615 	return button;
 616 }
 617 
 618 static void
 619 sensitize_widgets (EventPage *epage)
 620 {
 621 	ECalClient *client;
 622 	EShell *shell;
 623 	CompEditor *editor;
 624 	CompEditorFlags flags;
 625 	GtkActionGroup *action_group;
 626 	GtkAction *action;
 627 	gboolean read_only, custom, alarm, sens = TRUE, sensitize;
 628 	EventPagePrivate *priv;
 629 	gboolean delegate;
 630 
 631 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage));
 632 	client = comp_editor_get_client (editor);
 633 	flags = comp_editor_get_flags (editor);
 634 	shell = comp_editor_get_shell (editor);
 635 
 636 	priv = epage->priv;
 637 	if (flags & COMP_EDITOR_MEETING)
 638 		sens = flags & COMP_EDITOR_USER_ORG;
 639 
 640 	read_only = e_client_is_readonly (E_CLIENT (client));
 641 
 642 	delegate = flags & COMP_EDITOR_DELEGATE;
 643 
 644 	sensitize = !read_only && sens;
 645 
 646 	if (read_only) {
 647 		gchar *tmp = g_strconcat ("<b>", _("Event cannot be edited, because the selected calendar is read only"), "</b>", NULL);
 648 		event_page_set_info_string (epage, GTK_STOCK_DIALOG_INFO, tmp);
 649 		g_free (tmp);
 650 	} else if (!sens) {
 651 		gchar *tmp = g_strconcat ("<b>", _("Event cannot be fully edited, because you are not the organizer"), "</b>", NULL);
 652 		event_page_set_info_string (epage, GTK_STOCK_DIALOG_INFO, tmp);
 653 		g_free (tmp);
 654 	} else if (!check_starts_in_the_past (epage)) {
 655 		event_page_set_info_string (epage, NULL, NULL);
 656 	}
 657 
 658 	alarm = e_dialog_combo_box_get (priv->alarm_time_combo, priv->alarm_map) != ALARM_NONE;
 659 	custom = is_custom_alarm_store (priv->alarm_list_store, priv->old_summary, priv->alarm_units, priv->alarm_interval, NULL) ||
 660 		 e_dialog_combo_box_get (priv->alarm_time_combo, priv->alarm_map)  == ALARM_CUSTOM ? TRUE : FALSE;
 661 
 662 	if (alarm && !priv->alarm_icon) {
 663 		priv->alarm_icon = create_alarm_image_button ("stock_bell", _("This event has reminders"), epage);
 664 		gtk_box_pack_start ((GtkBox *) priv->status_icons, priv->alarm_icon, FALSE, FALSE, 6);
 665 	}
 666 
 667 	/* The list of organizers is set to be non-editable. Otherwise any
 668 	 * change in the displayed list causes an 'Account not found' error.
 669 	 */
 670 	gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (priv->organizer))), FALSE);
 671 
 672 	gtk_editable_set_editable (GTK_EDITABLE (priv->summary), !read_only);
 673 	gtk_editable_set_editable (GTK_EDITABLE (priv->location), sensitize);
 674 	gtk_widget_set_sensitive (priv->alarm_box, custom);
 675 	gtk_widget_set_sensitive (priv->start_time, sensitize);
 676 	gtk_widget_set_sensitive (priv->start_timezone, sensitize);
 677 	gtk_widget_set_sensitive (priv->end_time, sensitize);
 678 	gtk_widget_set_sensitive (priv->end_timezone, sensitize);
 679 	gtk_text_view_set_editable (GTK_TEXT_VIEW (priv->description), !read_only);
 680 	gtk_widget_set_sensitive (priv->alarm_time_combo, !read_only);
 681 	gtk_widget_set_sensitive (priv->categories_btn, !read_only);
 682 	/*TODO implement the for portion of the end time selector */
 683 	if (flags & COMP_EDITOR_NEW_ITEM) {
 684 		if (priv->all_day_event)
 685 			gtk_combo_box_set_active (GTK_COMBO_BOX (priv->end_time_combo), 1);
 686 		else
 687 			gtk_combo_box_set_active (GTK_COMBO_BOX (priv->end_time_combo), 0);
 688 	} else
 689 		gtk_combo_box_set_active (GTK_COMBO_BOX (priv->end_time_combo), 1);
 690 
 691 	gtk_widget_set_sensitive (priv->hour_selector, sensitize);
 692 	gtk_widget_set_sensitive (priv->minute_selector, sensitize);
 693 
 694 	gtk_editable_set_editable (GTK_EDITABLE (priv->categories), !read_only);
 695 
 696 	if (delegate) {
 697 		gtk_widget_set_sensitive (priv->source_combo_box, FALSE);
 698 	}
 699 
 700 	gtk_widget_set_sensitive (priv->organizer, !read_only);
 701 	gtk_widget_set_sensitive (priv->add, (!read_only &&  sens) || delegate);
 702 	gtk_widget_set_sensitive (priv->edit, (!read_only && sens) || delegate);
 703 	e_meeting_list_view_set_editable (priv->list_view, (!read_only && sens) || delegate);
 704 	gtk_widget_set_sensitive (priv->remove, (!read_only &&  sens) || delegate);
 705 	gtk_widget_set_sensitive (priv->invite, (!read_only &&  sens) || delegate);
 706 	gtk_widget_set_sensitive (GTK_WIDGET (priv->list_view), !read_only);
 707 
 708 	action_group = comp_editor_get_action_group (editor, "editable");
 709 	gtk_action_group_set_sensitive (action_group, !read_only);
 710 
 711 	action_group = comp_editor_get_action_group (editor, "individual");
 712 	gtk_action_group_set_sensitive (action_group, sensitize);
 713 
 714 	action = comp_editor_get_action (editor, "free-busy");
 715 	gtk_action_set_sensitive (action, sensitize);
 716 
 717 	if (!priv->is_meeting) {
 718 		gtk_widget_hide (priv->calendar_label);
 719 		gtk_widget_hide (priv->list_box);
 720 		gtk_widget_hide (priv->attendee_box);
 721 		gtk_widget_hide (priv->organizer);
 722 		gtk_label_set_text_with_mnemonic ((GtkLabel *) priv->org_cal_label, _("_Calendar:"));
 723 		gtk_label_set_mnemonic_widget ((GtkLabel *) priv->org_cal_label, priv->source_combo_box);
 724 	} else {
 725 		gtk_widget_show (priv->calendar_label);
 726 		gtk_widget_show (priv->list_box);
 727 		if (!e_shell_get_express_mode (shell))
 728 			gtk_widget_show (priv->attendee_box);
 729 		gtk_widget_show (priv->organizer);
 730 		gtk_label_set_text_with_mnemonic ((GtkLabel *) priv->org_cal_label, _("Or_ganizer:"));
 731 	}
 732 }
 733 
 734 static void
 735 update_time (EventPage *epage,
 736              ECalComponentDateTime *start_date,
 737              ECalComponentDateTime *end_date)
 738 {
 739 	CompEditor *editor;
 740 	ECalClient *client;
 741 	GtkAction *action;
 742 	struct icaltimetype *start_tt, *end_tt, implied_tt;
 743 	icaltimezone *start_zone = NULL, *def_zone = NULL;
 744 	gboolean all_day_event, homezone = TRUE;
 745 	gboolean show_timezone;
 746 
 747 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage));
 748 	client = comp_editor_get_client (editor);
 749 
 750 	if (start_date->tzid) {
 751 		/* Note that if we are creating a new event, the timezones may not be
 752 		 * on the server, so we try to get the builtin timezone with the TZID
 753 		 * first. */
 754 		start_zone = icaltimezone_get_builtin_timezone_from_tzid (start_date->tzid);
 755 		if (!start_zone) {
 756 			/* FIXME: Handle error better. */
 757 			GError *error = NULL;
 758 
 759 			e_cal_client_get_timezone_sync (
 760 				client, start_date->tzid,
 761 				&start_zone, NULL, &error);
 762 
 763 			if (error != NULL) {
 764 				g_warning (
 765 					"Couldn't get timezone '%s' from server: %s",
 766 					start_date->tzid ? start_date->tzid : "",
 767 					error->message);
 768 				g_error_free (error);
 769 			}
 770 		}
 771 	}
 772 
 773 	/* If both times are DATE values, we set the 'All Day Event' checkbox.
 774 	 * Also, if DTEND is after DTSTART, we subtract 1 day from it. */
 775 	all_day_event = FALSE;
 776 	start_tt = start_date->value;
 777 	end_tt = end_date->value;
 778 	if (!end_tt && start_tt->is_date) {
 779 		end_tt = &implied_tt;
 780 		*end_tt = *start_tt;
 781 		icaltime_adjust (end_tt, 1, 0, 0, 0);
 782 	}
 783 
 784 	if (start_tt->is_date && end_tt->is_date) {
 785 		all_day_event = TRUE;
 786 		if (icaltime_compare_date_only (*end_tt, *start_tt) > 0) {
 787 			icaltime_adjust (end_tt, -1, 0, 0, 0);
 788 		}
 789 	}
 790 
 791 	epage->priv->all_day_event = all_day_event;
 792 	set_all_day (epage, all_day_event);
 793 
 794 	/* If it is an all day event, we set both timezones to the current
 795 	 * timezone, so that if the user toggles the 'All Day Event' checkbox
 796 	 * the event uses the current timezone rather than none at all. */
 797 	if (all_day_event)
 798 		start_zone = e_meeting_store_get_timezone (
 799 			epage->priv->meeting_store);
 800 
 801 	g_signal_handlers_block_matched (
 802 		epage->priv->start_time, G_SIGNAL_MATCH_DATA,
 803 		0, 0, NULL, NULL, epage);
 804 	g_signal_handlers_block_matched (
 805 		epage->priv->end_time, G_SIGNAL_MATCH_DATA,
 806 		0, 0, NULL, NULL, epage);
 807 
 808 	e_date_edit_set_date (
 809 		E_DATE_EDIT (epage->priv->start_time),
 810 		start_tt->year, start_tt->month, start_tt->day);
 811 	e_date_edit_set_time_of_day (
 812 		E_DATE_EDIT (epage->priv->start_time),
 813 		start_tt->hour, start_tt->minute);
 814 
 815 	e_date_edit_set_date (
 816 		E_DATE_EDIT (epage->priv->end_time),
 817 		end_tt->year, end_tt->month, end_tt->day);
Access to field 'year' results in a dereference of a null pointer (loaded from variable 'end_tt')
(emitted by clang-analyzer)

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

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

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

818 e_date_edit_set_time_of_day ( 819 E_DATE_EDIT (epage->priv->end_time), 820 end_tt->hour, end_tt->minute); 821 822 g_signal_handlers_unblock_matched ( 823 epage->priv->start_time, G_SIGNAL_MATCH_DATA, 824 0, 0, NULL, NULL, epage); 825 g_signal_handlers_unblock_matched ( 826 epage->priv->end_time, G_SIGNAL_MATCH_DATA, 827 0, 0, NULL, NULL, epage); 828 829 /* Set the timezones, and set sync_timezones to TRUE if both timezones 830 * are the same. */ 831 g_signal_handlers_block_matched ( 832 epage->priv->start_timezone, G_SIGNAL_MATCH_DATA, 833 0, 0, NULL, NULL, epage); 834 g_signal_handlers_block_matched ( 835 epage->priv->end_timezone, G_SIGNAL_MATCH_DATA, 836 0, 0, NULL, NULL, epage); 837 838 if (start_zone) 839 e_timezone_entry_set_timezone ( 840 E_TIMEZONE_ENTRY (epage->priv->start_timezone), 841 start_zone); 842 def_zone = e_meeting_store_get_timezone (epage->priv->meeting_store); 843 if (!def_zone || !start_zone || strcmp (icaltimezone_get_tzid (def_zone), icaltimezone_get_tzid (start_zone))) 844 homezone = FALSE; 845 846 action = comp_editor_get_action (editor, "view-time-zone"); 847 show_timezone = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); 848 event_page_set_show_timezone (epage, (show_timezone || !homezone) & !all_day_event); 849 850 /*unblock the endtimezone widget*/ 851 g_signal_handlers_unblock_matched ( 852 epage->priv->end_timezone, G_SIGNAL_MATCH_DATA, 853 0, 0, NULL, NULL, epage); 854 g_signal_handlers_unblock_matched ( 855 epage->priv->start_timezone, G_SIGNAL_MATCH_DATA, 856 0, 0, NULL, NULL, epage); 857 858 epage->priv->sync_timezones = TRUE; 859 860 update_end_time_combo (epage); 861 } 862 863 static void 864 organizer_changed_cb (GtkEntry *entry, 865 EventPage *epage) 866 { 867 gchar *name; 868 gchar *mailto; 869 870 g_return_if_fail (GTK_IS_ENTRY (entry)); 871 g_return_if_fail (IS_EVENT_PAGE (epage)); 872 873 if (!epage->priv->ia) 874 return; 875 876 if (!get_current_identity (epage, &name, &mailto)) 877 return; 878 879 /* XXX EMeetingAttendee takes ownership of the strings. */ 880 e_meeting_attendee_set_cn (epage->priv->ia, name); 881 e_meeting_attendee_set_address (epage->priv->ia, mailto); 882 } 883 884 static void 885 event_page_dispose (GObject *object) 886 { 887 EventPagePrivate *priv; 888 889 priv = EVENT_PAGE_GET_PRIVATE (object); 890 891 if (priv->open_cancellable) { 892 g_cancellable_cancel (priv->open_cancellable); 893 g_object_unref (priv->open_cancellable); 894 priv->open_cancellable = NULL; 895 } 896 897 if (priv->location_completion != NULL) { 898 g_object_unref (priv->location_completion); 899 priv->location_completion = NULL; 900 } 901 902 if (priv->comp != NULL) { 903 g_object_unref (priv->comp); 904 priv->comp = NULL; 905 } 906 907 if (priv->main != NULL) { 908 g_object_unref (priv->main); 909 priv->main = NULL; 910 } 911 912 if (priv->builder != NULL) { 913 g_object_unref (priv->builder); 914 priv->builder = NULL; 915 } 916 917 if (priv->alarm_list_store != NULL) { 918 g_object_unref (priv->alarm_list_store); 919 priv->alarm_list_store = NULL; 920 } 921 922 if (priv->sod != NULL) { 923 g_object_unref (priv->sod); 924 priv->sod = NULL; 925 } 926 927 if (priv->alarm_dialog) { 928 gtk_widget_destroy (priv->alarm_dialog); 929 priv->alarm_dialog = NULL; 930 } 931 932 /* Chain up to parent's dispose() method. */ 933 G_OBJECT_CLASS (event_page_parent_class)->dispose (object); 934 } 935 936 static void 937 event_page_finalize (GObject *object) 938 { 939 EventPagePrivate *priv; 940 941 priv = EVENT_PAGE_GET_PRIVATE (object); 942 943 g_strfreev (priv->address_strings); 944 g_free (priv->fallback_address); 945 946 g_ptr_array_foreach ( 947 priv->deleted_attendees, (GFunc) g_object_unref, NULL); 948 g_ptr_array_free (priv->deleted_attendees, TRUE); 949 950 g_free (priv->old_summary); 951 952 priv->alarm_list_dlg_widget = NULL; 953 954 /* Chain up to parent's finalize() method. */ 955 G_OBJECT_CLASS (event_page_parent_class)->finalize (object); 956 } 957 958 static GtkWidget * 959 event_page_get_widget (CompEditorPage *page) 960 { 961 EventPage *event_page = EVENT_PAGE (page); 962 963 return event_page->priv->main; 964 } 965 966 static void 967 event_page_focus_main_widget (CompEditorPage *page) 968 { 969 EventPage *event_page = EVENT_PAGE (page); 970 971 gtk_widget_grab_focus (event_page->priv->summary); 972 } 973 974 static void 975 event_page_load_locations_list (CompEditorPage *page, 976 ECalComponent *comp) 977 { 978 EShell *shell; 979 EShellBackend *backend; 980 EventPagePrivate *priv; 981 CompEditor *editor; 982 GtkListStore *store; 983 GError *error; 984 985 const gchar *cache_dir; 986 gchar *file_name, *contents; 987 gchar **locations; 988 gint row; 989 990 priv = EVENT_PAGE (page)->priv; 991 editor = comp_editor_page_get_editor (page); 992 993 shell = comp_editor_get_shell (editor); 994 backend = e_shell_get_backend_by_name (shell, "calendar"); 995 cache_dir = e_shell_backend_get_config_dir (backend); 996 file_name = g_build_filename (cache_dir, "locations", NULL); 997 998 if (!g_file_test (file_name, G_FILE_TEST_EXISTS)) { 999 g_free (file_name); 1000 return; 1001 } 1002 1003 error = NULL; 1004 g_file_get_contents (file_name, &contents, NULL, &error); 1005 if (error) { 1006 g_warning ("%s: Failed to load locations list: %s", G_STRFUNC, error->message); 1007 g_error_free (error); 1008 g_free (file_name); 1009 return; 1010 } 1011 1012 locations = g_strsplit (contents, "\n", 0); 1013 if (!locations) { 1014 g_free (contents); 1015 g_free (file_name); 1016 return; 1017 } 1018 1019 row = 0; 1020 store = GTK_LIST_STORE (gtk_entry_completion_get_model (priv->location_completion)); 1021 while (locations[row] && *locations[row]) { 1022 GtkTreeIter iter; 1023 gtk_list_store_append (store, &iter); 1024 gtk_list_store_set (store, &iter, 0, locations[row], -1); 1025 row++; 1026 } 1027 1028 g_strfreev (locations); 1029 g_free (contents); 1030 g_free (file_name); 1031 } 1032 1033 static void 1034 event_page_save_locations_list (CompEditorPage *page, 1035 ECalComponent *comp) 1036 { 1037 EShell *shell; 1038 EShellBackend *backend; 1039 EventPagePrivate *priv; 1040 CompEditor *editor; 1041 GError *error; 1042 GtkTreeModel *model; 1043 GtkTreeIter iter; 1044 1045 const gchar *cache_dir; 1046 const gchar *current_location; 1047 gchar *file_name; 1048 GString *contents; 1049 1050 priv = EVENT_PAGE (page)->priv; 1051 editor = comp_editor_page_get_editor (page); 1052 1053 shell = comp_editor_get_shell (editor); 1054 backend = e_shell_get_backend_by_name (shell, "calendar"); 1055 cache_dir = e_shell_backend_get_config_dir (backend); 1056 file_name = g_build_filename (cache_dir, "locations", NULL); 1057 1058 if (!g_file_test (cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { 1059 gint r = g_mkdir_with_parents (cache_dir, 0700); 1060 if (r < 0) { 1061 g_warning ("%s: Failed to create %s: %s", G_STRFUNC, cache_dir, g_strerror (errno)); 1062 g_free (file_name); 1063 return; 1064 } 1065 } 1066 1067 current_location = gtk_entry_get_text (GTK_ENTRY (priv->location)); 1068 1069 /* Put current locatin on the very top of the list */ 1070 contents = g_string_new (current_location); 1071 g_string_append_c (contents, '\n'); 1072 1073 model = gtk_entry_completion_get_model (priv->location_completion); 1074 if (gtk_tree_model_get_iter_first (model, &iter)) { 1075 gint i = 0; 1076 do { 1077 gchar *str; 1078 1079 gtk_tree_model_get (model, &iter, 0, &str, -1); 1080 1081 /* Skip the current location */ 1082 if (str && *str && g_ascii_strcasecmp (str, current_location) != 0) 1083 g_string_append_printf (contents, "%s\n", str); 1084 1085 g_free (str); 1086 1087 i++; 1088 1089 } while (gtk_tree_model_iter_next (model, &iter) && (i < 20)); 1090 } 1091 1092 error = NULL; 1093 g_file_set_contents (file_name, contents->str, -1, &error); 1094 if (error) { 1095 g_warning ("%s: Failed to save locations: %s", G_STRFUNC, error->message); 1096 g_error_free (error); 1097 } 1098 1099 g_string_free (contents, TRUE); 1100 g_free (file_name); 1101 } 1102 1103 static gboolean 1104 event_page_fill_widgets (CompEditorPage *page, 1105 ECalComponent *comp) 1106 { 1107 ECalClient *client; 1108 CompEditor *editor; 1109 CompEditorFlags flags; 1110 EventPage *epage; 1111 EventPagePrivate *priv; 1112 ECalComponentText text; 1113 ECalComponentClassification cl; 1114 ECalComponentTransparency transparency; 1115 ECalComponentDateTime start_date, end_date; 1116 ESourceRegistry *registry; 1117 EShell *shell; 1118 const gchar *location, *uid = NULL; 1119 const gchar *categories; 1120 gchar *backend_addr = NULL; 1121 GSList *l; 1122 gboolean validated = TRUE; 1123 1124 epage = EVENT_PAGE (page); 1125 priv = epage->priv; 1126 1127 editor = comp_editor_page_get_editor (page); 1128 client = comp_editor_get_client (editor); 1129 flags = comp_editor_get_flags (editor); 1130 shell = comp_editor_get_shell (editor); 1131 1132 registry = e_shell_get_registry (shell); 1133 1134 if (!e_cal_component_has_organizer (comp)) { 1135 flags |= COMP_EDITOR_USER_ORG; 1136 comp_editor_set_flags (editor, flags); 1137 } 1138 1139 /* Clean out old data */ 1140 if (priv->comp != NULL) 1141 g_object_unref (priv->comp); 1142 priv->comp = NULL; 1143 1144 g_ptr_array_foreach ( 1145 priv->deleted_attendees, (GFunc) g_object_unref, NULL); 1146 g_ptr_array_set_size (priv->deleted_attendees, 0); 1147 1148 /* Clean the page */ 1149 clear_widgets (epage); 1150 1151 /* Summary, location, description(s) */ 1152 1153 /* Component for cancellation */ 1154 priv->comp = e_cal_component_clone (comp); 1155 comp_editor_copy_new_attendees (priv->comp, comp); 1156 1157 e_cal_component_get_summary (comp, &text); 1158 if (text.value != NULL) 1159 gtk_entry_set_text (GTK_ENTRY (priv->summary), text.value); 1160 else 1161 gtk_entry_set_text (GTK_ENTRY (priv->summary), ""); 1162 priv->old_summary = g_strdup (text.value); 1163 1164 e_cal_component_get_location (comp, &location); 1165 if (location != NULL) 1166 gtk_entry_set_text (GTK_ENTRY (priv->location), location); 1167 else 1168 gtk_entry_set_text (GTK_ENTRY (priv->location), ""); 1169 event_page_load_locations_list (page, comp); 1170 1171 e_cal_component_get_description_list (comp, &l); 1172 if (l && l->data) { 1173 ECalComponentText *dtext; 1174 1175 dtext = l->data; 1176 gtk_text_buffer_set_text ( 1177 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)), 1178 dtext->value ? dtext->value : "", -1); 1179 } else { 1180 gtk_text_buffer_set_text ( 1181 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)), 1182 "", 0); 1183 } 1184 e_cal_component_free_text_list (l); 1185 e_buffer_tagger_update_tags (GTK_TEXT_VIEW (priv->description)); 1186 1187 e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL); 1188 set_subscriber_info_string (epage, backend_addr); 1189 1190 if (priv->is_meeting) { 1191 ECalComponentOrganizer organizer; 1192 gchar *name = NULL; 1193 gchar *mailto = NULL; 1194 1195 priv->user_add = itip_get_comp_attendee ( 1196 registry, comp, client); 1197 1198 /* Organizer strings */ 1199 event_page_select_organizer (epage, backend_addr); 1200 1201 /* If there is an existing organizer show it properly */ 1202 if (e_cal_component_has_organizer (comp)) { 1203 e_cal_component_get_organizer (comp, &organizer); 1204 if (organizer.value != NULL) { 1205 const gchar *strip = itip_strip_mailto (organizer.value); 1206 gchar *string; 1207 1208 if (itip_organizer_is_user (registry, comp, client) || 1209 itip_sentby_is_user (registry, comp, client)) { 1210 if (e_client_check_capability ( 1211 E_CLIENT (client), 1212 CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) 1213 priv->user_org = TRUE; 1214 } else { 1215 if (e_client_check_capability ( 1216 E_CLIENT (client), 1217 CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) 1218 gtk_widget_set_sensitive (priv->invite, FALSE); 1219 gtk_widget_set_sensitive (priv->add, FALSE); 1220 gtk_widget_set_sensitive (priv->edit, FALSE); 1221 gtk_widget_set_sensitive (priv->remove, FALSE); 1222 priv->user_org = FALSE; 1223 } 1224 1225 if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_ORGANIZER) && (flags & COMP_EDITOR_DELEGATE)) 1226 string = g_strdup (backend_addr); 1227 else if (organizer.cn != NULL) 1228 string = g_strdup_printf ("%s <%s>", organizer.cn, strip); 1229 else 1230 string = g_strdup (strip); 1231 1232 g_signal_handlers_block_by_func (gtk_bin_get_child (GTK_BIN (priv->organizer)), organizer_changed_cb, epage); 1233 1234 if (!priv->user_org) { 1235 GtkComboBox *combo_box; 1236 GtkListStore *list_store; 1237 GtkTreeModel *model; 1238 GtkTreeIter iter; 1239 1240 combo_box = GTK_COMBO_BOX (priv->organizer); 1241 model = gtk_combo_box_get_model (combo_box); 1242 list_store = GTK_LIST_STORE (model); 1243 1244 gtk_list_store_clear (list_store); 1245 gtk_list_store_append (list_store, &iter); 1246 gtk_list_store_set (list_store, &iter, 0, string, -1); 1247 gtk_combo_box_set_active (combo_box, 0); 1248 gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (priv->organizer))), FALSE); 1249 } else { 1250 gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->organizer))), string); 1251 } 1252 1253 g_signal_handlers_unblock_by_func (gtk_bin_get_child (GTK_BIN (priv->organizer)), organizer_changed_cb, epage); 1254 1255 g_free (string); 1256 priv->existing = TRUE; 1257 } 1258 } else if (get_current_identity (epage, &name, &mailto)) { 1259 EMeetingAttendee *attendee; 1260 gchar *backend_mailto = NULL; 1261 1262 if (backend_addr != NULL && *backend_addr != '\0') { 1263 backend_mailto = g_strdup_printf ( 1264 "MAILTO:%s", backend_addr); 1265 if (g_ascii_strcasecmp (backend_mailto, mailto) == 0) { 1266 g_free (backend_mailto); 1267 backend_mailto = NULL; 1268 } 1269 } 1270 1271 attendee = 1272 e_meeting_store_add_attendee_with_defaults ( 1273 priv->meeting_store); 1274 priv->ia = g_object_ref (attendee); 1275 1276 if (backend_mailto == NULL) { 1277 e_meeting_attendee_set_cn (attendee, name); 1278 e_meeting_attendee_set_address (attendee, mailto); 1279 name = mailto = NULL; 1280 } else { 1281 e_meeting_attendee_set_address (attendee, backend_mailto); 1282 e_meeting_attendee_set_sentby (attendee, mailto); 1283 backend_mailto = mailto = NULL; 1284 } 1285 1286 if (client && e_cal_client_check_organizer_must_accept (client)) 1287 e_meeting_attendee_set_status ( 1288 attendee, ICAL_PARTSTAT_NEEDSACTION); 1289 else 1290 e_meeting_attendee_set_status ( 1291 attendee, ICAL_PARTSTAT_ACCEPTED); 1292 1293 e_meeting_list_view_add_attendee_to_name_selector ( 1294 E_MEETING_LIST_VIEW (priv->list_view), attendee); 1295 1296 g_free (backend_mailto); 1297 } 1298 1299 g_free (mailto); 1300 g_free (name); 1301 } 1302 1303 g_free (backend_addr); 1304 1305 /* Start and end times */ 1306 e_cal_component_get_dtstart (comp, &start_date); 1307 e_cal_component_get_dtend (comp, &end_date); 1308 if (!start_date.value) { 1309 comp_editor_page_display_validation_error (page, _("Event with no start date"), priv->start_time); 1310 validated = FALSE; 1311 } else if (!end_date.value) { 1312 comp_editor_page_display_validation_error (page, _("Event with no end date"), priv->end_time); 1313 validated = FALSE; 1314 } else 1315 update_time (epage, &start_date, &end_date); 1316 1317 e_cal_component_free_datetime (&start_date); 1318 e_cal_component_free_datetime (&end_date); 1319 1320 update_end_time_combo (epage); 1321 /* Classification */ 1322 e_cal_component_get_classification (comp, &cl); 1323 comp_editor_set_classification (editor, cl); 1324 1325 /* Show Time As (Transparency) */ 1326 e_cal_component_get_transparency (comp, &transparency); 1327 switch (transparency) { 1328 case E_CAL_COMPONENT_TRANSP_TRANSPARENT: 1329 priv->show_time_as_busy = FALSE; 1330 set_busy_time_menu (epage, FALSE); 1331 break; 1332 1333 default: 1334 priv->show_time_as_busy = TRUE; 1335 set_busy_time_menu (epage, TRUE); 1336 break; 1337 } 1338 1339 if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_TRANSPARENCY)) 1340 enable_busy_time_menu (epage, FALSE); 1341 else 1342 enable_busy_time_menu (epage, TRUE); 1343 1344 /* Alarms */ 1345 g_signal_handlers_block_matched (priv->alarm_time_combo, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 1346 g_signal_handlers_block_matched (priv->alarm_list_store, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 1347 1348 if (e_cal_component_has_alarms (comp)) { 1349 GList *alarms, *list; 1350 gint alarm_type; 1351 1352 alarms = e_cal_component_get_alarm_uids (comp); 1353 if (!is_custom_alarm_uid_list (comp, alarms, priv->old_summary, priv->alarm_units, priv->alarm_interval, &alarm_type)) 1354 e_dialog_combo_box_set (priv->alarm_time_combo, alarm_type, priv->alarm_map); 1355 else 1356 e_dialog_combo_box_set (priv->alarm_time_combo, ALARM_CUSTOM, priv->alarm_map); 1357 1358 for (list = alarms; list != NULL; list = list->next) { 1359 ECalComponentAlarm *ca; 1360 1361 ca = e_cal_component_get_alarm (comp, list->data); 1362 e_alarm_list_append (priv->alarm_list_store, NULL, ca); 1363 e_cal_component_alarm_free (ca); 1364 } 1365 1366 cal_obj_uid_list_free (alarms); 1367 } else { 1368 e_dialog_combo_box_set (priv->alarm_time_combo, ALARM_NONE, priv->alarm_map); 1369 } 1370 g_signal_handlers_unblock_matched (priv->alarm_time_combo, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 1371 g_signal_handlers_unblock_matched (priv->alarm_list_store, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 1372 1373 /* Categories */ 1374 e_cal_component_get_categories (comp, &categories); 1375 if (categories != NULL) 1376 gtk_entry_set_text (GTK_ENTRY (priv->categories), categories); 1377 else 1378 gtk_entry_set_text (GTK_ENTRY (priv->categories), ""); 1379 1380 /* Source */ 1381 e_source_combo_box_set_active ( 1382 E_SOURCE_COMBO_BOX (priv->source_combo_box), 1383 e_client_get_source (E_CLIENT (client))); 1384 1385 e_cal_component_get_uid (comp, &uid); 1386 if (!(flags & COMP_EDITOR_DELEGATE) 1387 && !(flags && COMP_EDITOR_NEW_ITEM)) { 1388 event_page_hide_options (epage); 1389 } 1390 1391 sensitize_widgets (epage); 1392 1393 return validated; 1394 } 1395 1396 static gboolean 1397 event_page_fill_component (CompEditorPage *page, 1398 ECalComponent *comp) 1399 { 1400 CompEditor *editor; 1401 CompEditorFlags flags; 1402 ECalClient *client; 1403 EventPage *epage; 1404 EventPagePrivate *priv; 1405 ECalComponentClassification classification; 1406 ECalComponentDateTime start_date, end_date; 1407 struct icaltimetype start_tt, end_tt; 1408 gboolean all_day_event, start_date_set, end_date_set, busy; 1409 gchar *cat, *str; 1410 GtkTextBuffer *text_buffer; 1411 GtkTextIter text_iter_start, text_iter_end; 1412 1413 epage = EVENT_PAGE (page); 1414 priv = epage->priv; 1415 1416 editor = comp_editor_page_get_editor (page); 1417 client = comp_editor_get_client (editor); 1418 flags = comp_editor_get_flags (editor); 1419 1420 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)); 1421 1422 comp_editor_copy_new_attendees (comp, priv->comp); 1423 1424 /* Summary */ 1425 1426 str = gtk_editable_get_chars (GTK_EDITABLE (priv->summary), 0, -1); 1427 if (str == NULL || *str == '\0') 1428 e_cal_component_set_summary (comp, NULL); 1429 else { 1430 ECalComponentText text; 1431 1432 text.value = str; 1433 text.altrep = NULL; 1434 1435 e_cal_component_set_summary (comp, &text); 1436 } 1437 1438 g_free (str); 1439 1440 /* Location */ 1441 1442 str = gtk_editable_get_chars (GTK_EDITABLE (priv->location), 0, -1); 1443 if (str == NULL || *str == '\0') 1444 e_cal_component_set_location (comp, NULL); 1445 else { 1446 e_cal_component_set_location (comp, str); 1447 event_page_save_locations_list (page, comp); 1448 } 1449 1450 g_free (str); 1451 1452 /* Description */ 1453 1454 gtk_text_buffer_get_start_iter (text_buffer, &text_iter_start); 1455 gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end); 1456 str = gtk_text_buffer_get_text (text_buffer, &text_iter_start, &text_iter_end, FALSE); 1457 1458 if (str == NULL || *str == '\0') 1459 e_cal_component_set_description_list (comp, NULL); 1460 else { 1461 GSList l; 1462 ECalComponentText text; 1463 1464 text.value = str; 1465 text.altrep = NULL; 1466 l.data = &text; 1467 l.next = NULL; 1468 1469 e_cal_component_set_description_list (comp, &l); 1470 } 1471 1472 g_free (str); 1473 1474 /* Dates */ 1475 1476 start_tt = icaltime_null_time (); 1477 start_date.value = &start_tt; 1478 start_date.tzid = NULL; 1479 1480 end_tt = icaltime_null_time (); 1481 end_date.value = &end_tt; 1482 end_date.tzid = NULL; 1483 1484 if (!e_date_edit_date_is_valid (E_DATE_EDIT (priv->start_time))) { 1485 comp_editor_page_display_validation_error (page, _("Start date is wrong"), priv->start_time); 1486 return FALSE; 1487 } 1488 start_date_set = e_date_edit_get_date ( 1489 E_DATE_EDIT (priv->start_time), 1490 &start_tt.year, 1491 &start_tt.month, 1492 &start_tt.day); 1493 g_return_val_if_fail (start_date_set, FALSE); 1494 1495 if (!e_date_edit_date_is_valid (E_DATE_EDIT (priv->end_time))) { 1496 comp_editor_page_display_validation_error (page, _("End date is wrong"), priv->end_time); 1497 return FALSE; 1498 } 1499 end_date_set = e_date_edit_get_date ( 1500 E_DATE_EDIT (priv->end_time), 1501 &end_tt.year, 1502 &end_tt.month, 1503 &end_tt.day); 1504 g_return_val_if_fail (end_date_set, FALSE); 1505 1506 /* If the all_day toggle is set, we use DATE values for DTSTART and 1507 * DTEND. If not, we fetch the hour & minute from the widgets. */ 1508 all_day_event = priv->all_day_event; 1509 1510 if (all_day_event) { 1511 start_tt.is_date = TRUE; 1512 end_tt.is_date = TRUE; 1513 1514 /* We have to add 1 day to DTEND, as it is not inclusive. */ 1515 icaltime_adjust (&end_tt, 1, 0, 0, 0); 1516 } else { 1517 icaltimezone *start_zone; 1518 1519 if (!e_date_edit_time_is_valid (E_DATE_EDIT (priv->start_time))) { 1520 comp_editor_page_display_validation_error (page, _("Start time is wrong"), priv->start_time); 1521 return FALSE; 1522 } 1523 e_date_edit_get_time_of_day ( 1524 E_DATE_EDIT (priv->start_time), 1525 &start_tt.hour, 1526 &start_tt.minute); 1527 if (!e_date_edit_time_is_valid (E_DATE_EDIT (priv->end_time))) { 1528 comp_editor_page_display_validation_error (page, _("End time is wrong"), priv->end_time); 1529 return FALSE; 1530 } 1531 e_date_edit_get_time_of_day ( 1532 E_DATE_EDIT (priv->end_time), 1533 &end_tt.hour, 1534 &end_tt.minute); 1535 start_zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->start_timezone)); 1536 start_date.tzid = icaltimezone_get_tzid (start_zone); 1537 end_date.tzid = icaltimezone_get_tzid (start_zone); 1538 } 1539 1540 e_cal_component_set_dtstart (comp, &start_date); 1541 e_cal_component_set_dtend (comp, &end_date); 1542 1543 /* Categories */ 1544 1545 cat = gtk_editable_get_chars (GTK_EDITABLE (priv->categories), 0, -1); 1546 str = comp_editor_strip_categories (cat); 1547 g_free (cat); 1548 1549 e_cal_component_set_categories (comp, str); 1550 1551 g_free (str); 1552 1553 /* Classification */ 1554 classification = comp_editor_get_classification (editor); 1555 e_cal_component_set_classification (comp, classification); 1556 1557 /* Show Time As (Transparency) */ 1558 busy = priv->show_time_as_busy; 1559 e_cal_component_set_transparency (comp, busy ? E_CAL_COMPONENT_TRANSP_OPAQUE : E_CAL_COMPONENT_TRANSP_TRANSPARENT); 1560 1561 /* send options */ 1562 if (priv->sendoptions_shown && priv->sod) { 1563 icaltimezone *zone = comp_editor_get_timezone (editor); 1564 e_send_options_utils_fill_component (priv->sod, comp, zone); 1565 } 1566 1567 /* Alarm */ 1568 e_cal_component_remove_all_alarms (comp); 1569 if (e_dialog_combo_box_get (priv->alarm_time_combo, priv->alarm_map) != ALARM_NONE) { 1570 if (e_dialog_combo_box_get (priv->alarm_time_combo, priv->alarm_map) == ALARM_CUSTOM) { 1571 GtkTreeModel *model; 1572 GtkTreeIter iter; 1573 gboolean valid_iter; 1574 1575 model = GTK_TREE_MODEL (priv->alarm_list_store); 1576 1577 for (valid_iter = gtk_tree_model_get_iter_first (model, &iter); valid_iter; 1578 valid_iter = gtk_tree_model_iter_next (model, &iter)) { 1579 ECalComponentAlarm *alarm, *alarm_copy; 1580 icalcomponent *icalcomp; 1581 icalproperty *icalprop; 1582 1583 alarm = (ECalComponentAlarm *) e_alarm_list_get_alarm (priv->alarm_list_store, &iter); 1584 if (!alarm) { 1585 g_warning ("alarm is NULL\n"); 1586 continue; 1587 } 1588 1589 /* We set the description of the alarm if it's got 1590 * the X-EVOLUTION-NEEDS-DESCRIPTION property. 1591 */ 1592 icalcomp = e_cal_component_alarm_get_icalcomponent (alarm); 1593 icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); 1594 while (icalprop) { 1595 const gchar *x_name; 1596 ECalComponentText summary; 1597 1598 x_name = icalproperty_get_x_name (icalprop); 1599 if (!strcmp (x_name, "X-EVOLUTION-NEEDS-DESCRIPTION")) { 1600 e_cal_component_get_summary (comp, &summary); 1601 e_cal_component_alarm_set_description (alarm, &summary); 1602 1603 icalcomponent_remove_property (icalcomp, icalprop); 1604 break; 1605 } 1606 1607 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); 1608 } 1609 1610 /* We clone the alarm to maintain the invariant that the alarm 1611 * structures in the list did *not* come from the component. 1612 */ 1613 1614 alarm_copy = e_cal_component_alarm_clone (alarm); 1615 e_cal_component_add_alarm (comp, alarm_copy); 1616 e_cal_component_alarm_free (alarm_copy); 1617 } 1618 } else { 1619 ECalComponentAlarm *ca; 1620 ECalComponentText summary; 1621 ECalComponentAlarmTrigger trigger; 1622 gint alarm_type; 1623 1624 ca = e_cal_component_alarm_new (); 1625 1626 e_cal_component_get_summary (comp, &summary); 1627 1628 if (summary.value) 1629 e_cal_component_alarm_set_description (ca, &summary); 1630 1631 e_cal_component_alarm_set_action (ca, E_CAL_COMPONENT_ALARM_DISPLAY); 1632 1633 memset (&trigger, 0, sizeof (ECalComponentAlarmTrigger)); 1634 trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START; 1635 trigger.u.rel_duration.is_neg = 1; 1636 1637 alarm_type = e_dialog_combo_box_get (priv->alarm_time_combo, priv->alarm_map); 1638 switch (alarm_type) { 1639 case ALARM_15_MINUTES: 1640 trigger.u.rel_duration.minutes = 15; 1641 break; 1642 1643 case ALARM_1_HOUR: 1644 trigger.u.rel_duration.hours = 1; 1645 break; 1646 1647 case ALARM_1_DAY: 1648 trigger.u.rel_duration.days = 1; 1649 break; 1650 1651 case ALARM_USER_TIME: 1652 switch (e_meeting_store_get_default_reminder_units (priv->meeting_store)) { 1653 case E_DURATION_DAYS: 1654 trigger.u.rel_duration.days = priv->alarm_interval; 1655 break; 1656 1657 case E_DURATION_HOURS: 1658 trigger.u.rel_duration.hours = priv->alarm_interval; 1659 break; 1660 1661 case E_DURATION_MINUTES: 1662 trigger.u.rel_duration.minutes = priv->alarm_interval; 1663 break; 1664 } 1665 break; 1666 1667 default: 1668 break; 1669 } 1670 e_cal_component_alarm_set_trigger (ca, trigger); 1671 1672 e_cal_component_add_alarm (comp, ca); 1673 e_cal_component_alarm_free (ca); 1674 } 1675 } 1676 1677 if (priv->is_meeting) { 1678 ECalComponentOrganizer organizer = {NULL, NULL, NULL, NULL}; 1679 1680 if (!priv->existing) { 1681 gchar *backend_addr = NULL; 1682 gchar *backend_mailto = NULL; 1683 gchar *name; 1684 gchar *mailto; 1685 1686 e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL); 1687 1688 /* Find the identity for the organizer or sentby field */ 1689 if (!get_current_identity (epage, &name, &mailto)) { 1690 e_notice ( 1691 priv->main, GTK_MESSAGE_ERROR, 1692 _("An organizer is required.")); 1693 return FALSE; 1694 } 1695 1696 /* Prefer the backend address if we have one. */ 1697 if (backend_addr != NULL && *backend_addr != '\0') { 1698 backend_mailto = g_strdup_printf ( 1699 "MAILTO:%s", backend_addr); 1700 if (g_ascii_strcasecmp (backend_mailto, mailto) == 0) { 1701 g_free (backend_mailto); 1702 backend_mailto = NULL; 1703 } 1704 } 1705 1706 if (backend_mailto == NULL) { 1707 organizer.cn = name; 1708 organizer.value = mailto; 1709 name = mailto = NULL; 1710 } else { 1711 organizer.value = backend_mailto; 1712 organizer.sentby = mailto; 1713 backend_mailto = mailto = NULL; 1714 } 1715 1716 e_cal_component_set_organizer (comp, &organizer); 1717 1718 g_free (backend_addr); 1719 g_free (backend_mailto); 1720 g_free (name); 1721 g_free (mailto); 1722 } 1723 1724 if (e_meeting_store_count_actual_attendees (priv->meeting_store) < 1) { 1725 e_notice ( 1726 priv->main, GTK_MESSAGE_ERROR, 1727 _("At least one attendee is required.")); 1728 return FALSE; 1729 } 1730 1731 if (flags & COMP_EDITOR_DELEGATE) { 1732 GSList *attendee_list, *l; 1733 gint i; 1734 const GPtrArray *attendees = e_meeting_store_get_attendees (priv->meeting_store); 1735 1736 e_cal_component_get_attendee_list (priv->comp, &attendee_list); 1737 1738 for (i = 0; i < attendees->len; i++) { 1739 EMeetingAttendee *ia = g_ptr_array_index (attendees, i); 1740 ECalComponentAttendee *ca; 1741 1742 /* Remove the duplicate user from the component if present */ 1743 if (e_meeting_attendee_is_set_delfrom (ia) || e_meeting_attendee_is_set_delto (ia)) { 1744 for (l = attendee_list; l; l = l->next) { 1745 ECalComponentAttendee *a = l->data; 1746 1747 if (g_str_equal (a->value, e_meeting_attendee_get_address (ia))) { 1748 attendee_list = g_slist_remove (attendee_list, l->data); 1749 break; 1750 } 1751 } 1752 } 1753 1754 ca = e_meeting_attendee_as_e_cal_component_attendee (ia); 1755 1756 attendee_list = g_slist_append (attendee_list, ca); 1757 } 1758 e_cal_component_set_attendee_list (comp, attendee_list); 1759 e_cal_component_free_attendee_list (attendee_list); 1760 } else 1761 set_attendees (comp, e_meeting_store_get_attendees (priv->meeting_store)); 1762 } 1763 1764 return TRUE; 1765 } 1766 1767 static gboolean 1768 event_page_fill_timezones (CompEditorPage *page, 1769 GHashTable *timezones) 1770 { 1771 EventPage *epage; 1772 EventPagePrivate *priv; 1773 icaltimezone *zone; 1774 1775 epage = EVENT_PAGE (page); 1776 priv = epage->priv; 1777 1778 /* add start date timezone */ 1779 zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->start_timezone)); 1780 if (zone) { 1781 if (!g_hash_table_lookup (timezones, icaltimezone_get_tzid (zone))) 1782 g_hash_table_insert (timezones, (gpointer) icaltimezone_get_tzid (zone), zone); 1783 } 1784 1785 /* add end date timezone */ 1786 zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->end_timezone)); 1787 if (zone) { 1788 if (!g_hash_table_lookup (timezones, icaltimezone_get_tzid (zone))) 1789 g_hash_table_insert (timezones, (gpointer) icaltimezone_get_tzid (zone), zone); 1790 } 1791 1792 return TRUE; 1793 } 1794 1795 static void 1796 event_page_set_dates (CompEditorPage *page, 1797 CompEditorPageDates *dates) 1798 { 1799 update_time (EVENT_PAGE (page), dates->start, dates->end); 1800 } 1801 1802 static void 1803 event_page_add_attendee (CompEditorPage *page, 1804 EMeetingAttendee *attendee) 1805 { 1806 CompEditor *editor; 1807 EventPagePrivate *priv; 1808 1809 priv = EVENT_PAGE_GET_PRIVATE (page); 1810 editor = comp_editor_page_get_editor (page); 1811 1812 if ((comp_editor_get_flags (editor) & COMP_EDITOR_DELEGATE) != 0) { 1813 gchar *delfrom; 1814 1815 /* EMeetingAttendee takes ownership of the string. */ 1816 delfrom = g_strdup_printf ("MAILTO:%s", priv->user_add); 1817 e_meeting_attendee_set_delfrom (attendee, delfrom); 1818 } 1819 1820 e_meeting_store_add_attendee (priv->meeting_store, attendee); 1821 e_meeting_list_view_add_attendee_to_name_selector ( 1822 E_MEETING_LIST_VIEW (priv->list_view), attendee); 1823 } 1824 1825 static void 1826 event_page_class_init (EventPageClass *class) 1827 { 1828 GObjectClass *object_class; 1829 CompEditorPageClass *editor_page_class; 1830 1831 g_type_class_add_private (class, sizeof (EventPagePrivate)); 1832 1833 object_class = G_OBJECT_CLASS (class); 1834 object_class->dispose = event_page_dispose; 1835 object_class->finalize = event_page_finalize; 1836 1837 editor_page_class = COMP_EDITOR_PAGE_CLASS (class); 1838 editor_page_class->get_widget = event_page_get_widget; 1839 editor_page_class->focus_main_widget = event_page_focus_main_widget; 1840 editor_page_class->fill_widgets = event_page_fill_widgets; 1841 editor_page_class->fill_component = event_page_fill_component; 1842 editor_page_class->fill_timezones = event_page_fill_timezones; 1843 editor_page_class->set_dates = event_page_set_dates; 1844 editor_page_class->add_attendee = event_page_add_attendee; 1845 } 1846 1847 static void 1848 event_page_init (EventPage *epage) 1849 { 1850 epage->priv = EVENT_PAGE_GET_PRIVATE (epage); 1851 epage->priv->deleted_attendees = g_ptr_array_new (); 1852 epage->priv->alarm_interval = -1; 1853 epage->priv->alarm_map = alarm_map_with_user_time; 1854 epage->priv->location_completion = gtk_entry_completion_new (); 1855 epage->priv->open_cancellable = NULL; 1856 } 1857 1858 void 1859 event_page_set_view_role (EventPage *epage, 1860 gboolean state) 1861 { 1862 e_meeting_list_view_column_set_visible ( 1863 epage->priv->list_view, E_MEETING_STORE_ROLE_COL, state); 1864 } 1865 1866 void 1867 event_page_set_view_status (EventPage *epage, 1868 gboolean state) 1869 { 1870 e_meeting_list_view_column_set_visible ( 1871 epage->priv->list_view, E_MEETING_STORE_STATUS_COL, state); 1872 } 1873 1874 void 1875 event_page_set_view_type (EventPage *epage, 1876 gboolean state) 1877 { 1878 e_meeting_list_view_column_set_visible ( 1879 epage->priv->list_view, E_MEETING_STORE_TYPE_COL, state); 1880 } 1881 1882 void 1883 event_page_set_view_rsvp (EventPage *epage, 1884 gboolean state) 1885 { 1886 e_meeting_list_view_column_set_visible ( 1887 epage->priv->list_view, E_MEETING_STORE_RSVP_COL, state); 1888 } 1889 1890 void 1891 event_page_hide_options (EventPage *page) 1892 { 1893 CompEditor *editor; 1894 GtkAction *action; 1895 1896 g_return_if_fail (IS_EVENT_PAGE (page)); 1897 1898 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page)); 1899 action = comp_editor_get_action (editor, "send-options"); 1900 gtk_action_set_visible (action, FALSE); 1901 } 1902 1903 void 1904 event_page_show_options (EventPage *page) 1905 { 1906 CompEditor *editor; 1907 GtkAction *action; 1908 1909 g_return_if_fail (IS_EVENT_PAGE (page)); 1910 1911 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page)); 1912 action = comp_editor_get_action (editor, "send-options"); 1913 gtk_action_set_visible (action, TRUE); 1914 } 1915 1916 void 1917 event_page_set_meeting (EventPage *page, 1918 gboolean set) 1919 { 1920 g_return_if_fail (IS_EVENT_PAGE (page)); 1921 1922 page->priv->is_meeting = set; 1923 if (page->priv->comp) 1924 sensitize_widgets (page); 1925 } 1926 1927 void 1928 event_page_set_delegate (EventPage *page, 1929 gboolean set) 1930 { 1931 g_return_if_fail (IS_EVENT_PAGE (page)); 1932 1933 if (set) 1934 gtk_label_set_text_with_mnemonic ((GtkLabel *) page->priv->attendees_label, _("_Delegatees")); 1935 else 1936 gtk_label_set_text_with_mnemonic ((GtkLabel *) page->priv->attendees_label, _("Atte_ndees")); 1937 } 1938 1939 static void 1940 time_sel_changed (GtkComboBox *combo, 1941 EventPage *epage) 1942 { 1943 EventPagePrivate *priv; 1944 gint selection = gtk_combo_box_get_active (combo); 1945 1946 priv = epage->priv; 1947 1948 if (selection == 1) { 1949 gtk_widget_hide (priv->time_hour); 1950 gtk_widget_show (priv->end_time); 1951 hour_sel_changed (GTK_SPIN_BUTTON (priv->hour_selector), epage); 1952 minute_sel_changed (GTK_SPIN_BUTTON (priv->minute_selector), epage); 1953 } else if (!selection) { 1954 gtk_widget_show (priv->time_hour); 1955 gtk_widget_hide (priv->end_time); 1956 1957 update_end_time_combo (epage); 1958 } 1959 } 1960 1961 static 1962 void update_end_time_combo (EventPage *epage) 1963 { 1964 EventPagePrivate *priv; 1965 struct icaltimetype start_tt = icaltime_null_time (); 1966 struct icaltimetype end_tt = icaltime_null_time (); 1967 time_t start_timet,end_timet; 1968 gint hours,minutes; 1969 1970 priv = epage->priv; 1971 1972 e_date_edit_get_date ( 1973 E_DATE_EDIT (priv->start_time), 1974 &start_tt.year, 1975 &start_tt.month, 1976 &start_tt.day); 1977 e_date_edit_get_time_of_day ( 1978 E_DATE_EDIT (priv->start_time), 1979 &start_tt.hour, 1980 &start_tt.minute); 1981 e_date_edit_get_date ( 1982 E_DATE_EDIT (priv->end_time), 1983 &end_tt.year, 1984 &end_tt.month, 1985 &end_tt.day); 1986 e_date_edit_get_time_of_day ( 1987 E_DATE_EDIT (priv->end_time), 1988 &end_tt.hour, 1989 &end_tt.minute); 1990 1991 end_timet = icaltime_as_timet (end_tt); 1992 start_timet = icaltime_as_timet (start_tt); 1993 1994 end_timet -= start_timet; 1995 hours = end_timet / (60 * 60); 1996 minutes = (end_timet / 60) - (hours * 60); 1997 1998 gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->hour_selector), hours); 1999 gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->minute_selector), minutes); 2000 } 2001 2002 static void 2003 hour_sel_changed (GtkSpinButton *widget, 2004 EventPage *epage) 2005 { 2006 hour_minute_changed (epage); 2007 } 2008 2009 static void 2010 minute_sel_changed (GtkSpinButton *widget, 2011 EventPage *epage) 2012 { 2013 hour_minute_changed (epage); 2014 } 2015 2016 static gboolean 2017 minute_sel_focus_out (GtkSpinButton *widget, 2018 GdkEvent *event, 2019 EventPage *epage) 2020 { 2021 const gchar *text; 2022 gint hours, minutes; 2023 2024 g_return_val_if_fail (widget != NULL, FALSE); 2025 g_return_val_if_fail (epage != NULL, FALSE); 2026 2027 text = gtk_entry_get_text (GTK_ENTRY (widget)); 2028 minutes = g_strtod (text, NULL); 2029 2030 if (minutes >= 60) { 2031 hours = minutes / 60; 2032 minutes = minutes % 60; 2033 2034 gtk_spin_button_set_value (GTK_SPIN_BUTTON (epage->priv->hour_selector), hours); 2035 gtk_spin_button_set_value (GTK_SPIN_BUTTON (epage->priv->minute_selector), minutes); 2036 } 2037 2038 return FALSE; 2039 } 2040 2041 static void 2042 hour_minute_changed (EventPage *epage) 2043 { 2044 EventPagePrivate *priv; 2045 gint for_hours, for_minutes; 2046 struct icaltimetype end_tt = icaltime_null_time (); 2047 2048 priv = epage->priv; 2049 2050 e_date_edit_get_date ( 2051 E_DATE_EDIT (priv->start_time), 2052 &end_tt.year, 2053 &end_tt.month, 2054 &end_tt.day); 2055 e_date_edit_get_time_of_day ( 2056 E_DATE_EDIT (priv->start_time), 2057 &end_tt.hour, 2058 &end_tt.minute); 2059 2060 for_hours = gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->hour_selector)); 2061 for_minutes = gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->minute_selector)); 2062 2063 icaltime_adjust (&end_tt, 0, for_hours, for_minutes, 0); 2064 2065 e_date_edit_set_date_and_time_of_day ( 2066 E_DATE_EDIT (priv->end_time), 2067 end_tt.year, 2068 end_tt.month, 2069 end_tt.day, 2070 end_tt.hour, 2071 end_tt.minute); 2072 } 2073 2074 static void 2075 edit_clicked_cb (GtkButton *btn, 2076 EventPage *epage) 2077 { 2078 EventPagePrivate *priv; 2079 GtkTreePath *path = NULL; 2080 GtkTreeViewColumn *focus_col; 2081 2082 priv = epage->priv; 2083 2084 gtk_tree_view_get_cursor ( 2085 GTK_TREE_VIEW (priv->list_view), &path, NULL); 2086 g_return_if_fail (path != NULL); 2087 2088 gtk_tree_view_get_cursor ( 2089 GTK_TREE_VIEW (priv->list_view), &path, &focus_col); 2090 gtk_tree_view_set_cursor ( 2091 GTK_TREE_VIEW (priv->list_view), path, focus_col, TRUE); 2092 gtk_tree_path_free (path); 2093 } 2094 2095 static void 2096 add_clicked_cb (GtkButton *btn, 2097 EventPage *epage) 2098 { 2099 CompEditor *editor; 2100 CompEditorFlags flags; 2101 EMeetingAttendee *attendee; 2102 2103 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 2104 flags = comp_editor_get_flags (editor); 2105 2106 attendee = e_meeting_store_add_attendee_with_defaults (epage->priv->meeting_store); 2107 2108 if (flags & COMP_EDITOR_DELEGATE) { 2109 e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s", epage->priv->user_add)); 2110 } 2111 2112 e_meeting_list_view_edit (epage->priv->list_view, attendee); 2113 } 2114 2115 static gboolean 2116 existing_attendee (EMeetingAttendee *ia, 2117 ECalComponent *comp) 2118 { 2119 GSList *attendees, *l; 2120 const gchar *ia_address; 2121 const gchar *ia_sentby = NULL; 2122 2123 ia_address = itip_strip_mailto (e_meeting_attendee_get_address (ia)); 2124 if (!ia_address) 2125 return FALSE; 2126 2127 if (e_meeting_attendee_is_set_sentby (ia)) 2128 ia_sentby = itip_strip_mailto (e_meeting_attendee_get_sentby (ia)); 2129 2130 e_cal_component_get_attendee_list (comp, &attendees); 2131 2132 for (l = attendees; l; l = l->next) { 2133 ECalComponentAttendee *attendee = l->data; 2134 const gchar *address; 2135 const gchar *sentby = NULL; 2136 2137 address = itip_strip_mailto (attendee->value); 2138 if (attendee->sentby) 2139 sentby = itip_strip_mailto (attendee->sentby); 2140 2141 if ((address && !g_ascii_strcasecmp (ia_address, address)) || (sentby && ia_sentby&& !g_ascii_strcasecmp (ia_sentby, sentby))) { 2142 e_cal_component_free_attendee_list (attendees); 2143 return TRUE; 2144 } 2145 } 2146 2147 e_cal_component_free_attendee_list (attendees); 2148 2149 return FALSE; 2150 } 2151 2152 static void 2153 remove_attendee (EventPage *epage, 2154 EMeetingAttendee *ia) 2155 { 2156 EventPagePrivate *priv = epage->priv; 2157 CompEditor *editor; 2158 CompEditorFlags flags; 2159 gint pos = 0; 2160 gboolean delegate; 2161 2162 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 2163 flags = comp_editor_get_flags (editor); 2164 2165 delegate = (flags & COMP_EDITOR_DELEGATE); 2166 2167 /* If the user deletes the organizer attendee explicitly, 2168 * assume they no longer want the organizer showing up */ 2169 if (ia == priv->ia) { 2170 g_object_unref (priv->ia); 2171 priv->ia = NULL; 2172 } 2173 2174 /* If this was a delegatee, no longer delegate */ 2175 if (e_meeting_attendee_is_set_delfrom (ia)) { 2176 EMeetingAttendee *ib; 2177 2178 ib = e_meeting_store_find_attendee (priv->meeting_store, e_meeting_attendee_get_delfrom (ia), &pos); 2179 if (ib != NULL) { 2180 e_meeting_attendee_set_delto (ib, NULL); 2181 2182 if (!delegate) 2183 e_meeting_attendee_set_edit_level (ib, E_MEETING_ATTENDEE_EDIT_FULL); 2184 } 2185 } 2186 2187 /* Handle deleting all attendees in the delegation chain */ 2188 while (ia != NULL) { 2189 EMeetingAttendee *ib = NULL; 2190 2191 /* do not add to deleted_attendees if user removed new attendee */ 2192 if (existing_attendee (ia, priv->comp) && !comp_editor_have_in_new_attendees (priv->comp, ia)) { 2193 g_object_ref (ia); 2194 g_ptr_array_add (priv->deleted_attendees, ia); 2195 } 2196 2197 if (e_meeting_attendee_get_delto (ia) != NULL) 2198 ib = e_meeting_store_find_attendee (priv->meeting_store, e_meeting_attendee_get_delto (ia), NULL); 2199 2200 comp_editor_manage_new_attendees (priv->comp, ia, FALSE); 2201 e_meeting_list_view_remove_attendee_from_name_selector (priv->list_view, ia); 2202 e_meeting_store_remove_attendee (priv->meeting_store, ia); 2203 2204 ia = ib; 2205 } 2206 2207 sensitize_widgets (epage); 2208 } 2209 2210 static void 2211 remove_clicked_cb (GtkButton *btn, 2212 EventPage *epage) 2213 { 2214 EventPagePrivate *priv; 2215 EMeetingAttendee *ia; 2216 GtkTreeSelection *selection; 2217 GList *paths = NULL, *tmp; 2218 GtkTreeIter iter; 2219 GtkTreePath *path = NULL; 2220 GtkTreeModel *model = NULL; 2221 gboolean valid_iter; 2222 gchar *address; 2223 2224 priv = epage->priv; 2225 2226 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view)); 2227 model = GTK_TREE_MODEL (priv->meeting_store); 2228 if (!(paths = gtk_tree_selection_get_selected_rows (selection, &model))) { 2229 g_warning ("Could not get a selection to delete."); 2230 return; 2231 } 2232 paths = g_list_reverse (paths); 2233 2234 for (tmp = paths; tmp; tmp = tmp->next) { 2235 path = tmp->data; 2236 2237 gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path); 2238 2239 gtk_tree_model_get (GTK_TREE_MODEL (priv->meeting_store), &iter, E_MEETING_STORE_ADDRESS_COL, &address, -1); 2240 ia = e_meeting_store_find_attendee (priv->meeting_store, address, NULL); 2241 g_free (address); 2242 if (!ia) { 2243 g_warning ("Cannot delete attendee\n"); 2244 continue; 2245 } else if (e_meeting_attendee_get_edit_level (ia) != E_MEETING_ATTENDEE_EDIT_FULL) { 2246 g_warning ("Not enough rights to delete attendee: %s\n", e_meeting_attendee_get_address (ia)); 2247 continue; 2248 } 2249 2250 remove_attendee (epage, ia); 2251 } 2252 2253 /* Select closest item after removal */ 2254 valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path); 2255 if (!valid_iter) { 2256 gtk_tree_path_prev (path); 2257 valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path); 2258 } 2259 2260 if (valid_iter) { 2261 gtk_tree_selection_unselect_all (selection); 2262 gtk_tree_selection_select_iter (selection, &iter); 2263 } 2264 2265 g_list_foreach (paths, (GFunc) gtk_tree_path_free, NULL); 2266 g_list_free (paths); 2267 } 2268 2269 static void 2270 invite_cb (GtkWidget *widget, 2271 EventPage *page) 2272 { 2273 e_meeting_list_view_invite_others_dialog (page->priv->list_view); 2274 } 2275 2276 static void 2277 attendee_added_cb (EMeetingListView *emlv, 2278 EMeetingAttendee *ia, 2279 EventPage *epage) 2280 { 2281 EventPagePrivate *priv = epage->priv; 2282 CompEditor *editor; 2283 CompEditorFlags flags; 2284 ECalClient *client; 2285 2286 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 2287 client = comp_editor_get_client (editor); 2288 flags = comp_editor_get_flags (editor); 2289 2290 if (!(flags & COMP_EDITOR_DELEGATE)) { 2291 comp_editor_manage_new_attendees (priv->comp, ia, TRUE); 2292 return; 2293 } 2294 2295 /* do not remove here, it did EMeetingListView already */ 2296 e_meeting_attendee_set_delfrom (ia, g_strdup_printf ("MAILTO:%s", priv->user_add ? priv->user_add : "")); 2297 2298 if (!e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY)) { 2299 EMeetingAttendee *delegator; 2300 2301 gtk_widget_set_sensitive (priv->invite, FALSE); 2302 gtk_widget_set_sensitive (priv->add, FALSE); 2303 gtk_widget_set_sensitive (priv->edit, FALSE); 2304 2305 delegator = e_meeting_store_find_attendee (priv->meeting_store, priv->user_add, NULL); 2306 g_return_if_fail (delegator != NULL); 2307 2308 e_meeting_attendee_set_delto (delegator, g_strdup (e_meeting_attendee_get_address (ia))); 2309 } 2310 } 2311 2312 static gboolean 2313 list_view_event (EMeetingListView *list_view, 2314 GdkEvent *event, 2315 EventPage *epage) 2316 { 2317 EventPagePrivate *priv = epage->priv; 2318 CompEditor *editor; 2319 CompEditorFlags flags; 2320 2321 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 2322 flags = comp_editor_get_flags (editor); 2323 2324 if (event->type == GDK_2BUTTON_PRESS && flags & COMP_EDITOR_USER_ORG) { 2325 EMeetingAttendee *attendee; 2326 2327 attendee = e_meeting_store_add_attendee_with_defaults (priv->meeting_store); 2328 2329 if (flags & COMP_EDITOR_DELEGATE) { 2330 e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s", epage->priv->user_add)); 2331 } 2332 2333 e_meeting_list_view_edit (epage->priv->list_view, attendee); 2334 return TRUE; 2335 } 2336 2337 return FALSE; 2338 } 2339 2340 static gboolean 2341 list_key_press (EMeetingListView *list_view, 2342 GdkEventKey *event, 2343 EventPage *epage) 2344 { 2345 if (event->keyval == GDK_KEY_Delete) { 2346 2347 remove_clicked_cb (NULL, epage); 2348 2349 return TRUE; 2350 } else if (event->keyval == GDK_KEY_Insert) { 2351 add_clicked_cb (NULL, epage); 2352 2353 return TRUE; 2354 } 2355 2356 return FALSE; 2357 } 2358 2359 void 2360 event_page_set_all_day_event (EventPage *epage, 2361 gboolean all_day) 2362 { 2363 EventPagePrivate *priv = epage->priv; 2364 struct icaltimetype start_tt = icaltime_null_time (); 2365 struct icaltimetype end_tt = icaltime_null_time (); 2366 CompEditor *editor; 2367 GtkAction *action; 2368 gboolean date_set; 2369 gboolean active; 2370 2371 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 2372 2373 epage->priv->all_day_event = all_day; 2374 e_date_edit_set_show_time (E_DATE_EDIT (priv->start_time), !all_day); 2375 e_date_edit_set_show_time (E_DATE_EDIT (priv->end_time), !all_day); 2376 2377 date_set = e_date_edit_get_date ( 2378 E_DATE_EDIT (priv->start_time), 2379 &start_tt.year, 2380 &start_tt.month, 2381 &start_tt.day); 2382 e_date_edit_get_time_of_day ( 2383 E_DATE_EDIT (priv->start_time), 2384 &start_tt.hour, 2385 &start_tt.minute); 2386 g_return_if_fail (date_set); 2387 2388 date_set = e_date_edit_get_date ( 2389 E_DATE_EDIT (priv->end_time), 2390 &end_tt.year, 2391 &end_tt.month, 2392 &end_tt.day); 2393 e_date_edit_get_time_of_day ( 2394 E_DATE_EDIT (priv->end_time), 2395 &end_tt.hour, 2396 &end_tt.minute); 2397 g_return_if_fail (date_set); 2398 2399 /* TODO implement the for portion in end time selector */ 2400 gtk_widget_set_sensitive (priv->end_time_combo, !all_day); 2401 if (all_day) 2402 gtk_combo_box_set_active (GTK_COMBO_BOX (priv->end_time_combo), 1); 2403 else 2404 gtk_combo_box_set_active (GTK_COMBO_BOX (priv->end_time_combo), 0); 2405 2406 action = comp_editor_get_action (editor, "view-time-zone"); 2407 gtk_action_set_sensitive (action, !all_day); 2408 2409 if (all_day) { 2410 /* Round down to the start of the day. */ 2411 start_tt.hour = 0; 2412 start_tt.minute = 0; 2413 start_tt.second = 0; 2414 start_tt.is_date = TRUE; 2415 2416 /* Round down to the start of the day, or the start of the 2417 * previous day if it is midnight. */ 2418 icaltime_adjust (&end_tt, 0, 0, 0, -1); 2419 end_tt.hour = 0; 2420 end_tt.minute = 0; 2421 end_tt.second = 0; 2422 end_tt.is_date = TRUE; 2423 } else { 2424 icaltimezone *start_zone; 2425 2426 if (end_tt.year == start_tt.year 2427 && end_tt.month == start_tt.month 2428 && end_tt.day == start_tt.day) { 2429 /* The event is within one day, so we set the event 2430 * start to the start of the working day, and the end 2431 * to one hour later. */ 2432 start_tt.hour = 2433 comp_editor_get_work_day_start_hour (editor); 2434 start_tt.minute = 2435 comp_editor_get_work_day_start_minute (editor); 2436 start_tt.second = 0; 2437 2438 end_tt = start_tt; 2439 icaltime_adjust (&end_tt, 0, 1, 0, 0); 2440 } else { 2441 /* The event is longer than 1 day, so we keep exactly 2442 * the same times, just using DATE-TIME rather than 2443 * DATE. */ 2444 icaltime_adjust (&end_tt, 1, 0, 0, 0); 2445 } 2446 2447 /* Make sure that end > start using the timezones. */ 2448 start_zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->start_timezone)); 2449 check_start_before_end (&start_tt, start_zone, 2450 &end_tt, start_zone, 2451 TRUE); 2452 } 2453 2454 action = comp_editor_get_action (editor, "view-time-zone"); 2455 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); 2456 event_page_set_show_timezone (epage, active & !all_day); 2457 2458 g_signal_handlers_block_matched (priv->start_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 2459 g_signal_handlers_block_matched (priv->end_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 2460 2461 e_date_edit_set_date ( 2462 E_DATE_EDIT (priv->start_time), start_tt.year, 2463 start_tt.month, start_tt.day); 2464 e_date_edit_set_time_of_day ( 2465 E_DATE_EDIT (priv->start_time), 2466 start_tt.hour, start_tt.minute); 2467 2468 e_date_edit_set_date ( 2469 E_DATE_EDIT (priv->end_time), end_tt.year, 2470 end_tt.month, end_tt.day); 2471 e_date_edit_set_time_of_day ( 2472 E_DATE_EDIT (priv->end_time), 2473 end_tt.hour, end_tt.minute); 2474 2475 g_signal_handlers_unblock_matched (priv->start_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 2476 g_signal_handlers_unblock_matched (priv->end_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 2477 2478 /* Notify upstream */ 2479 notify_dates_changed (epage, &start_tt, &end_tt); 2480 2481 comp_editor_page_changed (COMP_EDITOR_PAGE (epage)); 2482 } 2483 2484 void 2485 event_page_set_show_time_busy (EventPage *epage, 2486 gboolean state) 2487 { 2488 epage->priv->show_time_as_busy = state; 2489 comp_editor_page_changed (COMP_EDITOR_PAGE (epage)); 2490 } 2491 2492 void 2493 event_page_show_alarm (EventPage *epage) 2494 { 2495 gtk_widget_show (epage->priv->alarm_dialog); 2496 } 2497 2498 void 2499 event_page_set_show_timezone (EventPage *epage, 2500 gboolean state) 2501 { 2502 if (state) { 2503 gtk_widget_show_all (epage->priv->start_timezone); 2504 gtk_widget_show (epage->priv->timezone_label); 2505 } else { 2506 gtk_widget_hide (epage->priv->start_timezone); 2507 gtk_widget_hide (epage->priv->timezone_label); 2508 } 2509 2510 } 2511 2512 void 2513 event_page_set_show_categories (EventPage *epage, 2514 gboolean state) 2515 { 2516 if (state) { 2517 gtk_widget_show (epage->priv->categories_btn); 2518 gtk_widget_show (epage->priv->categories); 2519 } else { 2520 gtk_widget_hide (epage->priv->categories_btn); 2521 gtk_widget_hide (epage->priv->categories); 2522 } 2523 } 2524 2525 /*If the msg has some value set, the icon should always be set */ 2526 void 2527 event_page_set_info_string (EventPage *epage, 2528 const gchar *icon, 2529 const gchar *msg) 2530 { 2531 EventPagePrivate *priv; 2532 2533 priv = epage->priv; 2534 2535 gtk_image_set_from_stock (GTK_IMAGE (priv->info_icon), icon, GTK_ICON_SIZE_BUTTON); 2536 gtk_label_set_markup (GTK_LABEL (priv->info_string), msg); 2537 2538 if (msg && icon) 2539 gtk_widget_show (priv->info_hbox); 2540 else 2541 gtk_widget_hide (priv->info_hbox); 2542 } 2543 2544 /* Gets the widgets from the XML file and returns if they are all available. */ 2545 static gboolean 2546 get_widgets (EventPage *epage) 2547 { 2548 EShell *shell; 2549 ESourceRegistry *registry; 2550 CompEditor *editor; 2551 CompEditorPage *page = COMP_EDITOR_PAGE (epage); 2552 GtkEntryCompletion *completion; 2553 EventPagePrivate *priv; 2554 GSList *accel_groups; 2555 GtkAction *action; 2556 GtkWidget *parent; 2557 GtkWidget *toplevel; 2558 GtkWidget *sw; 2559 2560 priv = epage->priv; 2561 2562 #define GW(name) e_builder_get_widget (priv->builder, name) 2563 2564 editor = comp_editor_page_get_editor (page); 2565 shell = comp_editor_get_shell (editor); 2566 registry = e_shell_get_registry (shell); 2567 2568 priv->main = GW ("event-page"); 2569 if (!priv->main) 2570 return FALSE; 2571 2572 /* Get the GtkAccelGroup from the toplevel window, so we can install 2573 * it when the notebook page is mapped. */ 2574 toplevel = gtk_widget_get_toplevel (priv->main); 2575 accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel)); 2576 if (accel_groups) 2577 page->accel_group = g_object_ref (accel_groups->data); 2578 priv->alarm_dialog = GW ("alarm-dialog"); 2579 priv->alarm_box = GW ("custom_box"); 2580 priv->alarm_time_combo = GW ("alarm-time-combobox"); 2581 2582 priv->timezone_label = GW ("timezone-label"); 2583 priv->start_timezone = GW ("start-timezone"); 2584 priv->end_timezone = priv->start_timezone; 2585 priv->status_icons = GW ("status-icons"); 2586 2587 gtk_widget_show (priv->status_icons); 2588 2589 action = comp_editor_get_action (editor, "view-time-zone"); 2590 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) { 2591 gtk_widget_hide (priv->timezone_label); 2592 gtk_widget_hide (priv->start_timezone); 2593 } else { 2594 gtk_widget_show (priv->timezone_label); 2595 gtk_widget_show_all (priv->start_timezone); 2596 } 2597 priv->attendees_label = GW ("attendees-label"); 2598 2599 g_object_ref (priv->main); 2600 parent = gtk_widget_get_parent (priv->main); 2601 gtk_container_remove (GTK_CONTAINER (parent), priv->main); 2602 2603 priv->categories = GW ("categories"); 2604 priv->categories_btn = GW ("categories-button"); 2605 2606 priv->organizer = GW ("organizer"); 2607 gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (priv->organizer)))); 2608 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->organizer), 0); 2609 2610 priv->summary = GW ("summary"); 2611 priv->summary_label = GW ("summary-label"); 2612 priv->location = GW ("location"); 2613 priv->location_label = GW ("location-label"); 2614 2615 priv->info_hbox = GW ("generic-info"); 2616 priv->info_icon = GW ("generic-info-image"); 2617 priv->info_string = GW ("generic-info-msgs"); 2618 2619 priv->invite = GW ("invite"); 2620 priv->invite_label = GW ("invite-label"); 2621 if (e_shell_get_express_mode (shell)) 2622 gtk_widget_hide (priv->invite); 2623 else 2624 gtk_widget_hide (priv->invite_label); 2625 2626 priv->add = GW ("add-attendee"); 2627 priv->remove = GW ("remove-attendee"); 2628 priv->edit = GW ("edit-attendee"); 2629 priv->list_box = GW ("list-box"); 2630 2631 priv->calendar_label = GW ("calendar-label"); 2632 priv->attendee_box = GW ("attendee-box"); 2633 priv->org_cal_label = GW ("org-cal-label"); 2634 2635 priv->list_view = e_meeting_list_view_new (priv->meeting_store); 2636 2637 sw = gtk_scrolled_window_new (NULL, NULL); 2638 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 2639 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); 2640 gtk_widget_show (sw); 2641 gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (priv->list_view)); 2642 gtk_box_pack_start (GTK_BOX (priv->list_box), sw, TRUE, TRUE, 0); 2643 2644 /* Glade's visibility flag doesn't seem to work for custom widgets */ 2645 priv->start_time = GW ("start-time"); 2646 gtk_widget_show (priv->start_time); 2647 2648 priv->time_hour = GW ("time-hour"); 2649 priv->hour_selector = GW ("hour_selector"); 2650 priv->minute_selector = GW ("minute_selector"); 2651 priv->end_time_combo = GW ("end-time-combobox"); 2652 2653 priv->end_time = GW ("end-time"); 2654 gtk_widget_show_all (priv->time_hour); 2655 gtk_widget_hide (priv->end_time); 2656 2657 priv->description = GW ("description"); 2658 2659 priv->source_combo_box = GW ("source"); 2660 e_source_combo_box_set_registry ( 2661 E_SOURCE_COMBO_BOX (priv->source_combo_box), registry); 2662 2663 completion = e_category_completion_new (); 2664 gtk_entry_set_completion (GTK_ENTRY (priv->categories), completion); 2665 g_object_unref (completion); 2666 2667 if (priv->summary) { 2668 EShellSettings *shell_settings; 2669 2670 shell_settings = e_shell_get_shell_settings (shell); 2671 2672 g_object_bind_property ( 2673 shell_settings, "composer-inline-spelling", 2674 priv->summary, "checking-enabled", 2675 G_BINDING_SYNC_CREATE); 2676 } 2677 2678 return (priv->summary 2679 && priv->location 2680 && priv->start_time 2681 && priv->end_time 2682 && priv->description); 2683 } 2684 2685 static void 2686 summary_changed_cb (GtkEntry *entry, 2687 CompEditorPage *page) 2688 { 2689 CompEditor *editor; 2690 const gchar *text; 2691 2692 if (comp_editor_page_get_updating (page)) 2693 return; 2694 2695 editor = comp_editor_page_get_editor (page); 2696 text = gtk_entry_get_text (entry); 2697 comp_editor_set_summary (editor, text); 2698 } 2699 2700 /* Note that this assumes that the start_tt and end_tt passed to it are the 2701 * dates visible to the user. For DATE values, we have to add 1 day to the 2702 * end_tt before emitting the signal. */ 2703 static void 2704 notify_dates_changed (EventPage *epage, 2705 struct icaltimetype *start_tt, 2706 struct icaltimetype *end_tt) 2707 { 2708 EventPagePrivate *priv; 2709 CompEditorPageDates dates; 2710 ECalComponentDateTime start_dt, end_dt; 2711 gboolean all_day_event; 2712 icaltimezone *start_zone = NULL; 2713 priv = epage->priv; 2714 2715 all_day_event = priv->all_day_event; 2716 2717 start_dt.value = start_tt; 2718 end_dt.value = end_tt; 2719 2720 if (all_day_event) { 2721 /* The actual DTEND is 1 day after the displayed date for 2722 * DATE values. */ 2723 icaltime_adjust (end_tt, 1, 0, 0, 0); 2724 } else { 2725 start_zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->start_timezone)); 2726 } 2727 2728 start_dt.tzid = start_zone ? icaltimezone_get_tzid (start_zone) : NULL; 2729 end_dt.tzid = start_zone ? icaltimezone_get_tzid (start_zone) : NULL; 2730 2731 dates.start = &start_dt; 2732 dates.end = &end_dt; 2733 2734 dates.due = NULL; 2735 dates.complete = NULL; 2736 2737 comp_editor_page_notify_dates_changed (COMP_EDITOR_PAGE (epage), 2738 &dates); 2739 2740 check_starts_in_the_past (epage); 2741 } 2742 2743 static gboolean 2744 check_start_before_end (struct icaltimetype *start_tt, 2745 icaltimezone *start_zone, 2746 struct icaltimetype *end_tt, 2747 icaltimezone *end_zone, 2748 gboolean adjust_end_time) 2749 { 2750 struct icaltimetype end_tt_copy; 2751 gint cmp; 2752 2753 /* Convert the end time to the same timezone as the start time. */ 2754 end_tt_copy = *end_tt; 2755 icaltimezone_convert_time (&end_tt_copy, end_zone, start_zone); 2756 2757 /* Now check if the start time is after the end time. If it is, 2758 * we need to modify one of the times. */ 2759 cmp = icaltime_compare (*start_tt, end_tt_copy); 2760 if (cmp > 0) { 2761 if (adjust_end_time) { 2762 /* Modify the end time, to be the start + 1 hour. */ 2763 *end_tt = *start_tt; 2764 icaltime_adjust (end_tt, 0, 1, 0, 0); 2765 icaltimezone_convert_time ( 2766 end_tt, start_zone, 2767 end_zone); 2768 } else { 2769 /* Modify the start time, to be the end - 1 hour. */ 2770 *start_tt = *end_tt; 2771 icaltime_adjust (start_tt, 0, -1, 0, 0); 2772 icaltimezone_convert_time ( 2773 start_tt, end_zone, 2774 start_zone); 2775 } 2776 return TRUE; 2777 } 2778 2779 return FALSE; 2780 } 2781 2782 /* 2783 * This is called whenever the start or end dates or timezones is changed. 2784 * It makes sure that the start date < end date. It also emits the notification 2785 * signals so the other event editor pages update their labels etc. 2786 * 2787 * If adjust_end_time is TRUE, if the start time < end time it will adjust 2788 * the end time. If FALSE it will adjust the start time. If the user sets the 2789 * start or end time, the other time is adjusted to make it valid. 2790 */ 2791 static void 2792 times_updated (EventPage *epage, 2793 gboolean adjust_end_time) 2794 { 2795 EventPagePrivate *priv; 2796 struct icaltimetype start_tt = icaltime_null_time (); 2797 struct icaltimetype end_tt = icaltime_null_time (); 2798 gboolean date_set, all_day_event; 2799 gboolean set_start_date = FALSE, set_end_date = FALSE; 2800 icaltimezone *start_zone; 2801 2802 priv = epage->priv; 2803 2804 if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (epage))) 2805 return; 2806 2807 /* Fetch the start and end times and timezones from the widgets. */ 2808 all_day_event = priv->all_day_event; 2809 2810 date_set = e_date_edit_get_date ( 2811 E_DATE_EDIT (priv->start_time), 2812 &start_tt.year, 2813 &start_tt.month, 2814 &start_tt.day); 2815 g_return_if_fail (date_set); 2816 2817 date_set = e_date_edit_get_date ( 2818 E_DATE_EDIT (priv->end_time), 2819 &end_tt.year, 2820 &end_tt.month, 2821 &end_tt.day); 2822 g_return_if_fail (date_set); 2823 2824 if (all_day_event) { 2825 /* All Day Events are simple. We just compare the dates and if 2826 * start > end we copy one of them to the other. */ 2827 gint cmp = icaltime_compare_date_only (start_tt, end_tt); 2828 if (cmp > 0) { 2829 if (adjust_end_time) { 2830 end_tt = start_tt; 2831 set_end_date = TRUE; 2832 } else { 2833 start_tt = end_tt; 2834 set_start_date = TRUE; 2835 } 2836 } 2837 2838 start_tt.is_date = TRUE; 2839 end_tt.is_date = TRUE; 2840 } else { 2841 /* For DATE-TIME events, we have to convert to the same 2842 * timezone before comparing. */ 2843 e_date_edit_get_time_of_day ( 2844 E_DATE_EDIT (priv->start_time), 2845 &start_tt.hour, 2846 &start_tt.minute); 2847 e_date_edit_get_time_of_day ( 2848 E_DATE_EDIT (priv->end_time), 2849 &end_tt.hour, 2850 &end_tt.minute); 2851 2852 start_zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->start_timezone)); 2853 2854 if (check_start_before_end (&start_tt, start_zone, 2855 &end_tt, start_zone, 2856 adjust_end_time)) { 2857 if (adjust_end_time) 2858 set_end_date = TRUE; 2859 else 2860 set_start_date = TRUE; 2861 } 2862 } 2863 2864 if (set_start_date) { 2865 g_signal_handlers_block_matched (priv->start_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 2866 e_date_edit_set_date ( 2867 E_DATE_EDIT (priv->start_time), 2868 start_tt.year, start_tt.month, 2869 start_tt.day); 2870 e_date_edit_set_time_of_day ( 2871 E_DATE_EDIT (priv->start_time), 2872 start_tt.hour, start_tt.minute); 2873 g_signal_handlers_unblock_matched (priv->start_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 2874 } 2875 2876 if (set_end_date) { 2877 g_signal_handlers_block_matched (priv->end_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 2878 e_date_edit_set_date ( 2879 E_DATE_EDIT (priv->end_time), 2880 end_tt.year, end_tt.month, end_tt.day); 2881 e_date_edit_set_time_of_day ( 2882 E_DATE_EDIT (priv->end_time), 2883 end_tt.hour, end_tt.minute); 2884 g_signal_handlers_unblock_matched (priv->end_time, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, epage); 2885 } 2886 2887 /* Notify upstream */ 2888 notify_dates_changed (epage, &start_tt, &end_tt); 2889 } 2890 2891 static gboolean 2892 safe_to_process_date_changed_signal (GtkWidget *dedit_widget) 2893 { 2894 EDateEdit *dedit; 2895 GtkWidget *entry; 2896 2897 g_return_val_if_fail (dedit_widget != NULL, FALSE); 2898 2899 dedit = E_DATE_EDIT (dedit_widget); 2900 g_return_val_if_fail (dedit != NULL, FALSE); 2901 2902 entry = e_date_edit_get_entry (dedit); 2903 2904 return !entry || !gtk_widget_has_focus (entry); 2905 } 2906 2907 /* Callback used when the start date widget change. We check that the 2908 * start date < end date and we set the "all day event" button as appropriate. 2909 */ 2910 static void 2911 start_date_changed_cb (GtkWidget *dedit, 2912 EventPage *epage) 2913 { 2914 if (!safe_to_process_date_changed_signal (dedit)) 2915 return; 2916 2917 hour_minute_changed (epage); 2918 times_updated (epage, TRUE); 2919 } 2920 2921 /* Callback used when the end date widget change. We check that the 2922 * start date < end date and we set the "all day event" button as appropriate. 2923 */ 2924 static void 2925 end_date_changed_cb (GtkWidget *dedit, 2926 EventPage *epage) 2927 { 2928 if (!safe_to_process_date_changed_signal (dedit)) { 2929 return; 2930 } 2931 2932 times_updated (epage, FALSE); 2933 } 2934 2935 /* Callback used when the start timezone is changed. If sync_timezones is set, 2936 * we set the end timezone to the same value. It also updates the start time 2937 * labels on the other notebook pages. 2938 */ 2939 static void 2940 start_timezone_changed_cb (GtkWidget *widget, 2941 EventPage *epage) 2942 { 2943 EventPagePrivate *priv = epage->priv; 2944 2945 if (priv->sync_timezones) { 2946 comp_editor_page_set_updating (COMP_EDITOR_PAGE (epage), TRUE); 2947 /*the earlier method caused an infinite recursion*/ 2948 priv->end_timezone = priv->start_timezone; 2949 gtk_widget_show_all (priv->end_timezone); 2950 comp_editor_page_set_updating (COMP_EDITOR_PAGE (epage), FALSE); 2951 } 2952 2953 times_updated (epage, TRUE); 2954 } 2955 2956 /* Callback used when the categories button is clicked; we must bring up the 2957 * category list dialog. 2958 */ 2959 static void 2960 categories_clicked_cb (GtkWidget *button, 2961 EventPage *epage) 2962 { 2963 GtkEntry *entry; 2964 2965 entry = GTK_ENTRY (epage->priv->categories); 2966 e_categories_config_open_dialog_for_entry (entry); 2967 } 2968 2969 void 2970 event_page_send_options_clicked_cb (EventPage *epage) 2971 { 2972 EventPagePrivate *priv; 2973 CompEditor *editor; 2974 GtkWidget *toplevel; 2975 ESource *source; 2976 ECalClient *client; 2977 2978 priv = epage->priv; 2979 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 2980 client = comp_editor_get_client (editor); 2981 2982 if (!priv->sod) { 2983 priv->sod = e_send_options_dialog_new (); 2984 source = e_source_combo_box_ref_active ( 2985 E_SOURCE_COMBO_BOX (priv->source_combo_box)); 2986 e_send_options_utils_set_default_data ( 2987 priv->sod, source, "calendar"); 2988 priv->sod->data->initialized = TRUE; 2989 g_object_unref (source); 2990 } 2991 2992 if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_GEN_OPTIONS)) { 2993 e_send_options_set_need_general_options (priv->sod, FALSE); 2994 } 2995 2996 toplevel = gtk_widget_get_toplevel (priv->main); 2997 e_send_options_dialog_run (priv->sod, toplevel, E_ITEM_CALENDAR); 2998 } 2999 3000 static void 3001 epage_client_opened_cb (GObject *source_object, 3002 GAsyncResult *result, 3003 gpointer user_data) 3004 { 3005 ESource *source = E_SOURCE (source_object); 3006 EClient *client = NULL; 3007 EventPage *epage = user_data; 3008 EventPagePrivate *priv; 3009 CompEditor *editor; 3010 GError *error = NULL; 3011 3012 if (!e_client_utils_open_new_finish (source, result, &client, &error)) { 3013 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) || 3014 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { 3015 g_clear_error (&error); 3016 return; 3017 } 3018 } 3019 3020 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 3021 priv = epage->priv; 3022 3023 if (error) { 3024 GtkWidget *dialog; 3025 ECalClient *old_client; 3026 3027 old_client = comp_editor_get_client (editor); 3028 3029 e_source_combo_box_set_active ( 3030 E_SOURCE_COMBO_BOX (priv->source_combo_box), 3031 e_client_get_source (E_CLIENT (old_client))); 3032 3033 dialog = gtk_message_dialog_new ( 3034 NULL, GTK_DIALOG_MODAL, 3035 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, 3036 _("Unable to open the calendar '%s': %s"), 3037 e_source_get_display_name (source), 3038 error->message); 3039 gtk_dialog_run (GTK_DIALOG (dialog)); 3040 gtk_widget_destroy (dialog); 3041 3042 g_clear_error (&error); 3043 } else { 3044 gchar *backend_addr = NULL; 3045 icaltimezone *zone; 3046 ECalClient *cal_client = E_CAL_CLIENT (client); 3047 3048 g_return_if_fail (cal_client != NULL); 3049 3050 zone = e_meeting_store_get_timezone (priv->meeting_store); 3051 e_cal_client_set_default_timezone (cal_client, zone); 3052 3053 comp_editor_set_client (editor, cal_client); 3054 if (e_client_check_capability (client, CAL_STATIC_CAPABILITY_REQ_SEND_OPTIONS) && priv->is_meeting) 3055 event_page_show_options (epage); 3056 else 3057 event_page_hide_options (epage); 3058 3059 e_client_get_backend_property_sync (client, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL); 3060 3061 if (priv->is_meeting) 3062 event_page_select_organizer (epage, backend_addr); 3063 3064 set_subscriber_info_string (epage, backend_addr); 3065 g_free (backend_addr); 3066 3067 sensitize_widgets (epage); 3068 3069 alarm_list_dialog_set_client ( 3070 priv->alarm_list_dlg_widget, cal_client); 3071 } 3072 } 3073 3074 static void 3075 source_changed_cb (ESourceComboBox *source_combo_box, 3076 EventPage *epage) 3077 { 3078 EventPagePrivate *priv = epage->priv; 3079 ESource *source; 3080 3081 if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (epage))) 3082 return; 3083 3084 source = e_source_combo_box_ref_active (source_combo_box); 3085 g_return_if_fail (source != NULL); 3086 3087 if (priv->open_cancellable) { 3088 g_cancellable_cancel (priv->open_cancellable); 3089 g_object_unref (priv->open_cancellable); 3090 } 3091 priv->open_cancellable = g_cancellable_new (); 3092 3093 e_client_utils_open_new ( 3094 source, E_CLIENT_SOURCE_TYPE_EVENTS, 3095 FALSE, priv->open_cancellable, 3096 epage_client_opened_cb, epage); 3097 3098 g_object_unref (source); 3099 } 3100 3101 static void 3102 set_subscriber_info_string (EventPage *epage, 3103 const gchar *backend_address) 3104 { 3105 if (!check_starts_in_the_past (epage)) 3106 event_page_set_info_string (epage, NULL, NULL); 3107 } 3108 3109 static void 3110 alarm_changed_cb (GtkWidget *widget, 3111 EventPage *epage) 3112 { 3113 EventPagePrivate *priv = epage->priv; 3114 3115 if (e_dialog_combo_box_get (priv->alarm_time_combo, priv->alarm_map) != ALARM_NONE) { 3116 ECalComponentAlarm *ca; 3117 ECalComponentAlarmTrigger trigger; 3118 icalcomponent *icalcomp; 3119 icalproperty *icalprop; 3120 gint alarm_type; 3121 3122 ca = e_cal_component_alarm_new (); 3123 3124 e_cal_component_alarm_set_action (ca, E_CAL_COMPONENT_ALARM_DISPLAY); 3125 3126 memset (&trigger, 0, sizeof (ECalComponentAlarmTrigger)); 3127 trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START; 3128 trigger.u.rel_duration.is_neg = 1; 3129 3130 alarm_type = e_dialog_combo_box_get (priv->alarm_time_combo, priv->alarm_map); 3131 switch (alarm_type) { 3132 case ALARM_15_MINUTES: 3133 e_alarm_list_clear (priv->alarm_list_store); 3134 trigger.u.rel_duration.minutes = 15; 3135 break; 3136 3137 case ALARM_1_HOUR: 3138 e_alarm_list_clear (priv->alarm_list_store); 3139 trigger.u.rel_duration.hours = 1; 3140 break; 3141 3142 case ALARM_1_DAY: 3143 e_alarm_list_clear (priv->alarm_list_store); 3144 trigger.u.rel_duration.days = 1; 3145 break; 3146 3147 case ALARM_USER_TIME: 3148 e_alarm_list_clear (priv->alarm_list_store); 3149 switch (e_meeting_store_get_default_reminder_units (priv->meeting_store)) { 3150 case E_DURATION_DAYS: 3151 trigger.u.rel_duration.days = priv->alarm_interval; 3152 break; 3153 3154 case E_DURATION_HOURS: 3155 trigger.u.rel_duration.hours = priv->alarm_interval; 3156 break; 3157 3158 case E_DURATION_MINUTES: 3159 trigger.u.rel_duration.minutes = priv->alarm_interval; 3160 break; 3161 } 3162 break; 3163 case ALARM_CUSTOM: 3164 gtk_widget_set_sensitive (priv->alarm_box, TRUE); 3165 3166 default: 3167 break; 3168 } 3169 3170 if (alarm_type != ALARM_CUSTOM) { 3171 e_cal_component_alarm_set_trigger (ca, trigger); 3172 3173 icalcomp = e_cal_component_alarm_get_icalcomponent (ca); 3174 icalprop = icalproperty_new_x ("1"); 3175 icalproperty_set_x_name (icalprop, "X-EVOLUTION-NEEDS-DESCRIPTION"); 3176 icalcomponent_add_property (icalcomp, icalprop); 3177 3178 e_alarm_list_append (priv->alarm_list_store, NULL, ca); 3179 } 3180 if (!priv->alarm_icon) { 3181 priv->alarm_icon = create_alarm_image_button ("stock_bell", _("This event has reminders"), epage); 3182 gtk_box_pack_start ((GtkBox *) priv->status_icons, priv->alarm_icon, FALSE, FALSE, 6); 3183 } 3184 } else { 3185 e_alarm_list_clear (priv->alarm_list_store); 3186 if (priv->alarm_icon) { 3187 gtk_container_remove (GTK_CONTAINER (priv->status_icons), priv->alarm_icon); 3188 priv->alarm_icon = NULL; 3189 } 3190 } 3191 3192 sensitize_widgets (epage); 3193 } 3194 3195 #if 0 3196 static void 3197 alarm_custom_clicked_cb (GtkWidget *widget, 3198 EventPage *epage) 3199 { 3200 EventPagePrivate *priv = epage->priv; 3201 EAlarmList *temp_list_store; 3202 CompEditor *editor; 3203 GtkTreeModel *model; 3204 GtkTreeIter iter; 3205 gboolean valid_iter; 3206 GtkWidget *toplevel; 3207 ECalClient *client; 3208 3209 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 3210 client = comp_editor_get_client (editor); 3211 3212 /* Make a copy of the list store in case the user cancels */ 3213 temp_list_store = e_alarm_list_new (); 3214 model = GTK_TREE_MODEL (priv->alarm_list_store); 3215 3216 for (valid_iter = gtk_tree_model_get_iter_first (model, &iter); valid_iter; 3217 valid_iter = gtk_tree_model_iter_next (model, &iter)) { 3218 ECalComponentAlarm *alarm; 3219 3220 alarm = (ECalComponentAlarm *) e_alarm_list_get_alarm (priv->alarm_list_store, &iter); 3221 if (alarm == NULL) { 3222 g_warning ("alarm is NULL\n"); 3223 continue; 3224 } 3225 3226 e_alarm_list_append (temp_list_store, NULL, alarm); 3227 } 3228 3229 toplevel = gtk_widget_get_toplevel (priv->main); 3230 if (alarm_list_dialog_run (toplevel, client, temp_list_store)) { 3231 g_object_unref (priv->alarm_list_store); 3232 priv->alarm_list_store = temp_list_store; 3233 3234 comp_editor_set_changed (editor, TRUE); 3235 } else { 3236 g_object_unref (temp_list_store); 3237 } 3238 3239 /* If the user erases everything, uncheck the alarm toggle */ 3240 valid_iter = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->alarm_list_store), &iter); 3241 3242 sensitize_widgets (epage); 3243 } 3244 #endif 3245 3246 static gboolean 3247 alarm_dialog_delete_event_cb (GtkWidget *alarm_dialog) 3248 { 3249 gtk_widget_hide (alarm_dialog); 3250 3251 /* stop processing other handlers */ 3252 return TRUE; 3253 } 3254 3255 /* Hooks the widget signals */ 3256 static gboolean 3257 init_widgets (EventPage *epage) 3258 { 3259 EventPagePrivate *priv = epage->priv; 3260 EShell *shell; 3261 CompEditor *editor; 3262 ESourceRegistry *registry; 3263 GtkTextBuffer *text_buffer; 3264 icaltimezone *zone; 3265 gchar *combo_label = NULL; 3266 GtkAction *action; 3267 GtkTreeSelection *selection; 3268 gboolean active; 3269 ECalClient *client; 3270 GtkTreeIter iter; 3271 GtkListStore *store; 3272 3273 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 3274 3275 shell = comp_editor_get_shell (editor); 3276 client = comp_editor_get_client (editor); 3277 3278 registry = e_shell_get_registry (shell); 3279 3280 /* Make sure the EDateEdit widgets use our timezones to get the 3281 * current time. */ 3282 e_date_edit_set_get_time_callback ( 3283 E_DATE_EDIT (priv->start_time), 3284 (EDateEditGetTimeCallback) comp_editor_get_current_time, 3285 g_object_ref (editor), 3286 (GDestroyNotify) g_object_unref); 3287 e_date_edit_set_get_time_callback ( 3288 E_DATE_EDIT (priv->end_time), 3289 (EDateEditGetTimeCallback) comp_editor_get_current_time, 3290 g_object_ref (editor), 3291 (GDestroyNotify) g_object_unref); 3292 3293 /* Generic informative messages */ 3294 gtk_widget_hide (priv->info_hbox); 3295 3296 /* Summary */ 3297 g_signal_connect ( 3298 priv->summary, "changed", 3299 G_CALLBACK (summary_changed_cb), epage); 3300 3301 /* Description */ 3302 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)); 3303 3304 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->description), GTK_WRAP_WORD); 3305 3306 e_buffer_tagger_connect (GTK_TEXT_VIEW (priv->description)); 3307 3308 /* Start and end times */ 3309 g_signal_connect ( 3310 priv->start_time, "changed", 3311 G_CALLBACK (start_date_changed_cb), epage); 3312 g_signal_connect ( 3313 priv->end_time, "changed", 3314 G_CALLBACK (end_date_changed_cb), epage); 3315 3316 /* Categories */ 3317 g_signal_connect ( 3318 priv->categories_btn, "clicked", 3319 G_CALLBACK (categories_clicked_cb), epage); 3320 3321 /* Source selector */ 3322 g_signal_connect ( 3323 priv->source_combo_box, "changed", 3324 G_CALLBACK (source_changed_cb), epage); 3325 3326 /* Alarms */ 3327 priv->alarm_list_store = e_alarm_list_new (); 3328 g_signal_connect_swapped ( 3329 priv->alarm_list_store, "row-inserted", 3330 G_CALLBACK (comp_editor_page_changed), epage); 3331 g_signal_connect_swapped ( 3332 priv->alarm_list_store, "row-deleted", 3333 G_CALLBACK (comp_editor_page_changed), epage); 3334 g_signal_connect_swapped ( 3335 priv->alarm_list_store, "row-changed", 3336 G_CALLBACK (comp_editor_page_changed), epage); 3337 3338 /* Timezone changed */ 3339 g_signal_connect ( 3340 priv->start_timezone, "changed", 3341 G_CALLBACK (start_timezone_changed_cb), epage); 3342 3343 e_meeting_list_view_column_set_visible ( 3344 priv->list_view, E_MEETING_STORE_ATTENDEE_COL, TRUE); 3345 3346 action = comp_editor_get_action (editor, "view-role"); 3347 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); 3348 e_meeting_list_view_column_set_visible ( 3349 priv->list_view, E_MEETING_STORE_ROLE_COL, active); 3350 3351 action = comp_editor_get_action (editor, "view-rsvp"); 3352 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); 3353 e_meeting_list_view_column_set_visible ( 3354 priv->list_view, E_MEETING_STORE_RSVP_COL, active); 3355 3356 action = comp_editor_get_action (editor, "view-status"); 3357 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); 3358 e_meeting_list_view_column_set_visible ( 3359 priv->list_view, E_MEETING_STORE_STATUS_COL, active); 3360 3361 action = comp_editor_get_action (editor, "view-type"); 3362 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); 3363 e_meeting_list_view_column_set_visible ( 3364 priv->list_view, E_MEETING_STORE_TYPE_COL, active); 3365 3366 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view)); 3367 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); 3368 3369 g_signal_connect ( 3370 priv->list_view, "event", 3371 G_CALLBACK (list_view_event), epage); 3372 g_signal_connect ( 3373 priv->list_view, "key_press_event", 3374 G_CALLBACK (list_key_press), epage); 3375 3376 /* Add attendee button */ 3377 g_signal_connect ( 3378 priv->add, "clicked", 3379 G_CALLBACK (add_clicked_cb), epage); 3380 3381 /* Remove attendee button */ 3382 g_signal_connect ( 3383 priv->remove, "clicked", 3384 G_CALLBACK (remove_clicked_cb), epage); 3385 3386 /* Edit attendee button */ 3387 g_signal_connect ( 3388 priv->edit, "clicked", 3389 G_CALLBACK (edit_clicked_cb), epage); 3390 3391 /* Contacts button */ 3392 g_signal_connect ( 3393 priv->invite, "clicked", 3394 G_CALLBACK (invite_cb), epage); 3395 3396 /* Alarm dialog */ 3397 g_signal_connect ( 3398 priv->alarm_dialog, "response", 3399 G_CALLBACK (gtk_widget_hide), priv->alarm_dialog); 3400 g_signal_connect ( 3401 priv->alarm_dialog, "delete-event", 3402 G_CALLBACK (alarm_dialog_delete_event_cb), priv->alarm_dialog); 3403 priv->alarm_list_dlg_widget = alarm_list_dialog_peek ( 3404 registry, client, priv->alarm_list_store); 3405 gtk_widget_reparent (priv->alarm_list_dlg_widget, priv->alarm_box); 3406 gtk_widget_show_all (priv->alarm_list_dlg_widget); 3407 gtk_widget_hide (priv->alarm_dialog); 3408 gtk_window_set_modal (GTK_WINDOW (priv->alarm_dialog), TRUE); 3409 3410 /* Meeting List View */ 3411 g_signal_connect ( 3412 priv->list_view, "attendee_added", 3413 G_CALLBACK (attendee_added_cb), epage); 3414 3415 gtk_widget_show (GTK_WIDGET (priv->list_view)); 3416 3417 /* categories */ 3418 action = comp_editor_get_action (editor, "view-categories"); 3419 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) { 3420 gtk_widget_hide (priv->categories_btn); 3421 gtk_widget_hide (priv->categories); 3422 } else { 3423 gtk_widget_show (priv->categories_btn); 3424 gtk_widget_show (priv->categories); 3425 } 3426 3427 /* End time selector */ 3428 gtk_combo_box_set_active (GTK_COMBO_BOX (priv->end_time_combo), 1); 3429 gtk_widget_hide (priv->time_hour); 3430 gtk_widget_show (priv->end_time); 3431 g_signal_connect ( 3432 priv->end_time_combo, "changed", 3433 G_CALLBACK (time_sel_changed), epage); 3434 update_end_time_combo (epage); 3435 3436 /* Hour and Minute selector */ 3437 gtk_spin_button_set_range ((GtkSpinButton *) priv->hour_selector, 0, G_MAXINT); 3438 g_signal_connect ( 3439 priv->hour_selector, "value-changed", 3440 G_CALLBACK (hour_sel_changed), epage); 3441 g_signal_connect ( 3442 priv->minute_selector, "value-changed", 3443 G_CALLBACK (minute_sel_changed), epage); 3444 3445 g_signal_connect ( 3446 priv->minute_selector, "focus-out-event", 3447 G_CALLBACK (minute_sel_focus_out), epage); 3448 3449 /* Add the user defined time if necessary */ 3450 priv->alarm_units = 3451 e_meeting_store_get_default_reminder_units ( 3452 priv->meeting_store); 3453 priv->alarm_interval = 3454 e_meeting_store_get_default_reminder_interval ( 3455 priv->meeting_store); 3456 3457 combo_label = NULL; 3458 switch (priv->alarm_units) { 3459 case E_DURATION_DAYS: 3460 if (priv->alarm_interval != 1) { 3461 combo_label = g_strdup_printf (ngettext ("%d day before appointment", "%d days before appointment", priv->alarm_interval), priv->alarm_interval); 3462 } 3463 break; 3464 3465 case E_DURATION_HOURS: 3466 if (priv->alarm_interval != 1) { 3467 combo_label = g_strdup_printf (ngettext ("%d hour before appointment", "%d hours before appointment", priv->alarm_interval), priv->alarm_interval); 3468 } 3469 break; 3470 3471 case E_DURATION_MINUTES: 3472 if (priv->alarm_interval != 15) { 3473 combo_label = g_strdup_printf (ngettext ("%d minute before appointment", "%d minutes before appointment", priv->alarm_interval), priv->alarm_interval); 3474 } 3475 break; 3476 } 3477 3478 store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (priv->alarm_time_combo))); 3479 if (combo_label) { 3480 gtk_list_store_append (store, &iter); 3481 gtk_list_store_set ( 3482 store, &iter, 3483 0, combo_label, 3484 -1); 3485 g_free (combo_label); 3486 priv->alarm_map = alarm_map_with_user_time; 3487 } else { 3488 priv->alarm_map = alarm_map_without_user_time; 3489 } 3490 3491 gtk_list_store_append (store, &iter); 3492 gtk_list_store_set ( 3493 store, &iter, 3494 0, _("Customize"), 3495 -1); 3496 3497 gtk_list_store_insert (store, &iter, 0); 3498 gtk_list_store_set ( 3499 store, &iter, 3500 /* Translators: "None" for "No reminder set" */ 3501 0, C_("cal-reminders", "None"), 3502 -1); 3503 3504 g_signal_connect_swapped ( 3505 priv->alarm_time_combo, "changed", 3506 G_CALLBACK (comp_editor_page_changed), epage); 3507 g_signal_connect ( 3508 priv->alarm_time_combo, "changed", 3509 G_CALLBACK (alarm_changed_cb), epage); 3510 3511 /* Belongs to priv->description */ 3512 g_signal_connect_swapped ( 3513 text_buffer, "changed", 3514 G_CALLBACK (comp_editor_page_changed), epage); 3515 g_signal_connect_swapped ( 3516 priv->summary, "changed", 3517 G_CALLBACK (comp_editor_page_changed), epage); 3518 g_signal_connect_swapped ( 3519 priv->location, "changed", 3520 G_CALLBACK (comp_editor_page_changed), epage); 3521 g_signal_connect_swapped ( 3522 priv->start_time, "changed", 3523 G_CALLBACK (comp_editor_page_changed), epage); 3524 g_signal_connect_swapped ( 3525 priv->end_time, "changed", 3526 G_CALLBACK (comp_editor_page_changed), epage); 3527 g_signal_connect_swapped ( 3528 priv->categories, "changed", 3529 G_CALLBACK (comp_editor_page_changed), epage); 3530 g_signal_connect_swapped ( 3531 priv->source_combo_box, "changed", 3532 G_CALLBACK (comp_editor_page_changed), epage); 3533 g_signal_connect_swapped ( 3534 priv->start_timezone, "changed", 3535 G_CALLBACK (comp_editor_page_changed), epage); 3536 3537 /* Set the default timezone, so the timezone entry may be hidden. */ 3538 zone = e_meeting_store_get_timezone (priv->meeting_store); 3539 e_timezone_entry_set_default_timezone (E_TIMEZONE_ENTRY (priv->start_timezone), zone); 3540 e_timezone_entry_set_default_timezone (E_TIMEZONE_ENTRY (priv->end_timezone), zone); 3541 3542 action = comp_editor_get_action (editor, "view-time-zone"); 3543 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); 3544 event_page_set_show_timezone (epage, active); 3545 3546 return TRUE; 3547 } 3548 3549 static void 3550 event_page_select_organizer (EventPage *epage, 3551 const gchar *backend_address) 3552 { 3553 EventPagePrivate *priv = epage->priv; 3554 const gchar *default_address; 3555 gint ii; 3556 3557 /* Treat an empty backend address as NULL. */ 3558 if (backend_address != NULL && *backend_address == '\0') 3559 backend_address = NULL; 3560 3561 default_address = priv->fallback_address; 3562 3563 if (backend_address != NULL) { 3564 for (ii = 0; priv->address_strings[ii] != NULL; ii++) { 3565 if (g_strrstr (priv->address_strings[ii], backend_address) != NULL) { 3566 default_address = priv->address_strings[ii]; 3567 break; 3568 } 3569 } 3570 } 3571 3572 if (default_address != NULL) { 3573 if (!priv->comp || !e_cal_component_has_organizer (priv->comp)) { 3574 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->organizer))); 3575 3576 g_signal_handlers_block_by_func (entry, organizer_changed_cb, epage); 3577 gtk_entry_set_text (entry, default_address); 3578 g_signal_handlers_unblock_by_func (entry, organizer_changed_cb, epage); 3579 } 3580 } else 3581 g_warning ("No potential organizers!"); 3582 } 3583 3584 /** 3585 * event_page_construct: 3586 * @epage: An event page. 3587 * 3588 * Constructs an event page by loading its Glade data. 3589 * 3590 * Return value: The same object as @epage, or NULL if the widgets could not be 3591 * created. 3592 **/ 3593 EventPage * 3594 event_page_construct (EventPage *epage, 3595 EMeetingStore *meeting_store) 3596 { 3597 EventPagePrivate *priv; 3598 EShell *shell; 3599 CompEditor *editor; 3600 ESourceRegistry *registry; 3601 GtkComboBox *combo_box; 3602 GtkListStore *list_store; 3603 GtkTreeModel *model; 3604 GtkTreeIter iter; 3605 gint ii; 3606 3607 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (epage)); 3608 shell = comp_editor_get_shell (editor); 3609 3610 priv = epage->priv; 3611 priv->meeting_store = g_object_ref (meeting_store); 3612 3613 /* Make sure our custom widget classes are registered with 3614 * GType before we load the GtkBuilder definition file. */ 3615 E_TYPE_DATE_EDIT; 3616 E_TYPE_TIMEZONE_ENTRY; 3617 E_TYPE_SPELL_ENTRY; 3618 3619 priv->builder = gtk_builder_new (); 3620 e_load_ui_builder_definition (priv->builder, "event-page.ui"); 3621 3622 if (!get_widgets (epage)) { 3623 g_message ( 3624 "event_page_construct(): " 3625 "Could not find all widgets in the XML file!"); 3626 return NULL; 3627 } 3628 3629 /* Create entry completion and attach it to the entry */ 3630 priv->location_completion = gtk_entry_completion_new (); 3631 gtk_entry_set_completion ( 3632 GTK_ENTRY (priv->location), 3633 priv->location_completion); 3634 3635 /* Initialize completino model */ 3636 list_store = gtk_list_store_new (1, G_TYPE_STRING); 3637 gtk_entry_completion_set_model ( 3638 priv->location_completion, 3639 GTK_TREE_MODEL (list_store)); 3640 gtk_entry_completion_set_text_column (priv->location_completion, 0); 3641 3642 combo_box = GTK_COMBO_BOX (priv->organizer); 3643 model = gtk_combo_box_get_model (combo_box); 3644 list_store = GTK_LIST_STORE (model); 3645 3646 registry = e_shell_get_registry (shell); 3647 priv->address_strings = itip_get_user_identities (registry); 3648 priv->fallback_address = itip_get_fallback_identity (registry); 3649 3650 /* FIXME Could we just use a GtkComboBoxText? */ 3651 for (ii = 0; priv->address_strings[ii] != NULL; ii++) { 3652 gtk_list_store_append (list_store, &iter); 3653 gtk_list_store_set ( 3654 list_store, &iter, 3655 0, priv->address_strings[ii], -1); 3656 } 3657 3658 gtk_combo_box_set_active (combo_box, 0); 3659 3660 g_signal_connect ( 3661 gtk_bin_get_child (GTK_BIN (priv->organizer)), "changed", 3662 G_CALLBACK (organizer_changed_cb), epage); 3663 3664 if (!init_widgets (epage)) { 3665 g_message ( 3666 "event_page_construct(): " 3667 "Could not initialize the widgets!"); 3668 return NULL; 3669 } 3670 3671 return epage; 3672 } 3673 3674 /** 3675 * event_page_new: 3676 * 3677 * Creates a new event page. 3678 * 3679 * Return value: A newly-created event page, or NULL if the page could 3680 * not be created. 3681 **/ 3682 EventPage * 3683 event_page_new (EMeetingStore *meeting_store, 3684 CompEditor *editor) 3685 { 3686 EventPage *epage; 3687 3688 epage = g_object_new (TYPE_EVENT_PAGE, "editor", editor, NULL); 3689 if (!event_page_construct (epage, meeting_store)) { 3690 g_object_unref (epage); 3691 g_return_val_if_reached (NULL); 3692 } 3693 3694 return epage; 3695 } 3696 3697 static void 3698 set_attendees (ECalComponent *comp, 3699 const GPtrArray *attendees) 3700 { 3701 GSList *comp_attendees = NULL, *l; 3702 gint i; 3703 3704 for (i = 0; i < attendees->len; i++) { 3705 EMeetingAttendee *ia = g_ptr_array_index (attendees, i); 3706 ECalComponentAttendee *ca; 3707 3708 ca = e_meeting_attendee_as_e_cal_component_attendee (ia); 3709 3710 comp_attendees = g_slist_prepend (comp_attendees, ca); 3711 3712 } 3713 comp_attendees = g_slist_reverse (comp_attendees); 3714 3715 e_cal_component_set_attendee_list (comp, comp_attendees); 3716 3717 for (l = comp_attendees; l != NULL; l = l->next) 3718 g_free (l->data); 3719 g_slist_free (comp_attendees); 3720 } 3721 3722 ECalComponent * 3723 event_page_get_cancel_comp (EventPage *page) 3724 { 3725 EventPagePrivate *priv; 3726 3727 g_return_val_if_fail (page != NULL, NULL); 3728 g_return_val_if_fail (IS_EVENT_PAGE (page), NULL); 3729 3730 priv = page->priv; 3731 3732 if (priv->deleted_attendees->len == 0) 3733 return NULL; 3734 3735 set_attendees (priv->comp, priv->deleted_attendees); 3736 3737 return e_cal_component_clone (priv->comp); 3738 } 3739 3740 ENameSelector * 3741 event_page_get_name_selector (EventPage *epage) 3742 { 3743 EventPagePrivate *priv; 3744 3745 g_return_val_if_fail (epage != NULL, NULL); 3746 g_return_val_if_fail (IS_EVENT_PAGE (epage), NULL); 3747 3748 priv = epage->priv; 3749 3750 return e_meeting_list_view_get_name_selector (priv->list_view); 3751 } 3752 3753 /** 3754 * event_page_remove_all_attendees 3755 * @epage: an #EventPage 3756 * 3757 * Removes all attendees from the meeting store and name selector. 3758 **/ 3759 void 3760 event_page_remove_all_attendees (EventPage *epage) 3761 { 3762 EventPagePrivate *priv; 3763 3764 g_return_if_fail (epage != NULL); 3765 g_return_if_fail (IS_EVENT_PAGE (epage)); 3766 3767 priv = epage->priv; 3768 3769 e_meeting_store_remove_all_attendees (priv->meeting_store); 3770 e_meeting_list_view_remove_all_attendees_from_name_selector (E_MEETING_LIST_VIEW (priv->list_view)); 3771 } 3772 3773 GtkWidget * 3774 event_page_get_alarm_page (EventPage *epage) 3775 { 3776 EventPagePrivate *priv; 3777 GtkWidget *alarm_page, *tmp; 3778 3779 g_return_val_if_fail (epage != NULL, NULL); 3780 g_return_val_if_fail (IS_EVENT_PAGE (epage), NULL); 3781 3782 priv = epage->priv; 3783 3784 tmp = GW ("dialog-vbox1"); 3785 alarm_page = GW ("vbox2"); 3786 g_object_ref (alarm_page); 3787 gtk_container_remove ((GtkContainer *) tmp, alarm_page); 3788 3789 return alarm_page; 3790 } 3791 3792 GtkWidget * 3793 event_page_get_attendee_page (EventPage *epage) 3794 { 3795 EventPagePrivate *priv; 3796 GtkWidget *apage; 3797 3798 g_return_val_if_fail (epage != NULL, NULL); 3799 g_return_val_if_fail (IS_EVENT_PAGE (epage), NULL); 3800 3801 priv = epage->priv; 3802 3803 apage = priv->list_box; 3804 g_object_ref (apage); 3805 gtk_container_remove ((GtkContainer *) gtk_widget_get_parent (apage), apage); 3806 gtk_widget_hide (priv->attendee_box); 3807 3808 return apage; 3809 } 3810 3811 #undef GW