1 /*
2  * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 #include "evolution-config.h"
19 
20 #include <glib/gi18n-lib.h>
21 #include <gtk/gtk.h>
22 
23 #include <libedataserver/libedataserver.h>
24 #include <libecal/libecal.h>
25 #include <e-util/e-util.h>
26 
27 #include "calendar-config.h"
28 #include "comp-util.h"
29 #include "e-cal-dialogs.h"
30 #include "itip-utils.h"
31 #include "print.h"
32 
33 #include "e-comp-editor-page-general.h"
34 #include "e-comp-editor-page-attachments.h"
35 #include "e-comp-editor-event.h"
36 #include "e-comp-editor-memo.h"
37 #include "e-comp-editor-task.h"
38 
39 #include "e-comp-editor.h"
40 
41 struct _ECompEditorPrivate {
42 	EAlertBar *alert_bar; /* not referenced */
43 	EActivityBar *activity_bar; /* not referenced */
44 	GtkNotebook *content; /* not referenced */
45 
46 	EAlert *validation_alert;
47 
48 	EShell *shell;
49 	GSettings *calendar_settings;
50 	ESource *origin_source;
51 	ICalComponent *component;
52 	guint32 flags;
53 
54 	EFocusTracker *focus_tracker;
55 	GtkUIManager *ui_manager;
56 
57 	GSList *pages; /* ECompEditorPage * */
58 	gulong show_attendees_handler_id;
59 
60 	ECompEditorPageGeneral *page_general; /* special page, can be added only once; not referenced */
61 
62 	EActivity *target_client_opening;
63 
64 	ECalClient *source_client;
65 	ECalClient *target_client;
66 	gchar *cal_email_address;
67 	gchar *alarm_email_address;
68 	gboolean changed;
69 	guint updating;
70 	gchar *title_suffix;
71 
72 	ECompEditorPropertyPart *dtstart_part;
73 	ECompEditorPropertyPart *dtend_part;
74 
75 	GtkWidget *restore_focus;
76 
77 	gulong target_backend_property_change_id;
78 };
79 
80 enum {
81 	PROP_0,
82 	PROP_ALARM_EMAIL_ADDRESS,
83 	PROP_CAL_EMAIL_ADDRESS,
84 	PROP_CHANGED,
85 	PROP_COMPONENT,
86 	PROP_FLAGS,
87 	PROP_ORIGIN_SOURCE,
88 	PROP_SHELL,
89 	PROP_SOURCE_CLIENT,
90 	PROP_TARGET_CLIENT,
91 	PROP_TITLE_SUFFIX
92 };
93 
94 enum {
95 	TIMES_CHANGED,
96 	OBJECT_CREATED,
97 	EDITOR_CLOSED,
98 	LAST_SIGNAL
99 };
100 
101 static guint signals[LAST_SIGNAL];
102 
103 static GSList *opened_editors = NULL;
104 
105 static void e_comp_editor_alert_sink_iface_init (EAlertSinkInterface *iface);
106 
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(ECompEditor,e_comp_editor,GTK_TYPE_WINDOW,G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK,e_comp_editor_alert_sink_iface_init)G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE,NULL))107 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ECompEditor, e_comp_editor, GTK_TYPE_WINDOW,
108 	G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, e_comp_editor_alert_sink_iface_init)
109 	G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
110 
111 static void
112 ece_restore_focus (ECompEditor *comp_editor)
113 {
114 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
115 
116 	if (comp_editor->priv->restore_focus) {
117 		if (GTK_IS_ENTRY (comp_editor->priv->restore_focus))
118 			gtk_entry_grab_focus_without_selecting (GTK_ENTRY (comp_editor->priv->restore_focus));
119 		else
120 			gtk_widget_grab_focus (comp_editor->priv->restore_focus);
121 
122 		comp_editor->priv->restore_focus = NULL;
123 	}
124 }
125 
126 static void
e_comp_editor_enable(ECompEditor * comp_editor,gboolean enable)127 e_comp_editor_enable (ECompEditor *comp_editor,
128 		      gboolean enable)
129 {
130 	GtkActionGroup *group;
131 	GtkWidget *current_focus;
132 
133 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
134 
135 	current_focus = gtk_window_get_focus (GTK_WINDOW (comp_editor));
136 
137 	gtk_widget_set_sensitive (GTK_WIDGET (comp_editor->priv->content), enable);
138 
139 	group = e_comp_editor_get_action_group (comp_editor, "individual");
140 	gtk_action_group_set_sensitive (group, enable);
141 
142 	group = e_comp_editor_get_action_group (comp_editor, "core");
143 	gtk_action_group_set_sensitive (group, enable);
144 
145 	group = e_comp_editor_get_action_group (comp_editor, "editable");
146 	gtk_action_group_set_sensitive (group, enable);
147 
148 	if (enable) {
149 		e_comp_editor_sensitize_widgets (comp_editor);
150 		ece_restore_focus (comp_editor);
151 	} else {
152 		comp_editor->priv->restore_focus = current_focus;
153 	}
154 }
155 
156 static void
ece_set_attendees_for_delegation(ECalComponent * comp,const gchar * address)157 ece_set_attendees_for_delegation (ECalComponent *comp,
158 				  const gchar *address)
159 {
160 	ICalProperty *prop;
161 	ICalParameter *param;
162 	ICalComponent *icomp;
163 	gboolean again;
164 
165 	icomp = e_cal_component_get_icalcomponent (comp);
166 
167 	for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
168 	     prop;
169 	     g_object_unref (prop), prop = again ? i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY) :
170 	     i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
171 		const gchar *attendee = i_cal_property_get_attendee (prop);
172 		const gchar *delfrom = NULL;
173 
174 		again = FALSE;
175 		param = i_cal_property_get_first_parameter (prop, I_CAL_DELEGATEDFROM_PARAMETER);
176 		if (param)
177 			delfrom = i_cal_parameter_get_delegatedfrom (param);
178 		if (!(g_str_equal (itip_strip_mailto (attendee), address) ||
179 		     ((delfrom && *delfrom) && g_str_equal (itip_strip_mailto (delfrom), address)))) {
180 			i_cal_component_remove_property (icomp, prop);
181 			again = TRUE;
182 		}
183 
184 		g_clear_object (&param);
185 	}
186 }
187 
188 /* Utility function to get the mime-attachment list from the attachment
189  * bar for sending the comp via itip. The list and its contents must
190  * be freed by the caller.
191  */
192 static GSList *
ece_get_mime_attach_list(ECompEditor * comp_editor)193 ece_get_mime_attach_list (ECompEditor *comp_editor)
194 {
195 	ECompEditorPage *page_attachments;
196 	EAttachmentStore *store;
197 	GtkTreeModel *model;
198 	GtkTreeIter iter;
199 	struct CalMimeAttach *cal_mime_attach;
200 	GSList *attach_list = NULL;
201 	gboolean valid;
202 
203 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
204 
205 	page_attachments = e_comp_editor_get_page (comp_editor, E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS);
206 	if (!page_attachments)
207 		return NULL;
208 
209 	store = e_comp_editor_page_attachments_get_store (E_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments));
210 	if (!store)
211 		return NULL;
212 
213 	model = GTK_TREE_MODEL (store);
214 	valid = gtk_tree_model_get_iter_first (model, &iter);
215 
216 	while (valid) {
217 		EAttachment *attachment;
218 		CamelDataWrapper *wrapper;
219 		CamelMimePart *mime_part;
220 		CamelStream *stream;
221 		GByteArray *byte_array;
222 		guchar *buffer = NULL;
223 		const gchar *description;
224 		const gchar *disposition;
225 		gint column_id;
226 
227 		column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
228 		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
229 		mime_part = e_attachment_ref_mime_part (attachment);
230 		g_object_unref (attachment);
231 
232 		valid = gtk_tree_model_iter_next (model, &iter);
233 
234 		if (mime_part == NULL)
235 			continue;
236 
237 		cal_mime_attach = g_malloc0 (sizeof (struct CalMimeAttach));
238 		wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
239 
240 		byte_array = g_byte_array_new ();
241 		stream = camel_stream_mem_new_with_byte_array (byte_array);
242 
243 		camel_data_wrapper_decode_to_stream_sync (
244 			wrapper, stream, NULL, NULL);
245 		buffer = g_memdup (byte_array->data, byte_array->len);
246 
247 		camel_mime_part_set_content_id (mime_part, NULL);
248 
249 		cal_mime_attach->encoded_data = (gchar *) buffer;
250 		cal_mime_attach->length = byte_array->len;
251 		cal_mime_attach->filename =
252 			g_strdup (camel_mime_part_get_filename (mime_part));
253 		description = camel_mime_part_get_description (mime_part);
254 		if (description == NULL || *description == '\0')
255 			description = _("attachment");
256 		cal_mime_attach->description = g_strdup (description);
257 		cal_mime_attach->content_type = camel_data_wrapper_get_mime_type (wrapper);
258 		cal_mime_attach->content_id = g_strdup (
259 			camel_mime_part_get_content_id (mime_part));
260 
261 		disposition = camel_mime_part_get_disposition (mime_part);
262 		cal_mime_attach->disposition =
263 			(disposition != NULL) &&
264 			(g_ascii_strcasecmp (disposition, "inline") == 0);
265 
266 		attach_list = g_slist_append (attach_list, cal_mime_attach);
267 
268 		g_object_unref (mime_part);
269 		g_object_unref (stream);
270 
271 	}
272 
273 	return attach_list;
274 }
275 
276 static void
e_comp_editor_set_component(ECompEditor * comp_editor,const ICalComponent * component)277 e_comp_editor_set_component (ECompEditor *comp_editor,
278 			     const ICalComponent *component)
279 {
280 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
281 	g_return_if_fail (I_CAL_IS_COMPONENT ((ICalComponent *) component));
282 
283 	if (comp_editor->priv->component != component) {
284 		g_clear_object (&comp_editor->priv->component);
285 		comp_editor->priv->component = i_cal_component_clone ((ICalComponent *) component);
286 	}
287 
288 	g_warn_if_fail (comp_editor->priv->component != NULL);
289 }
290 
291 typedef struct _SaveData {
292 	ECompEditor *comp_editor;
293 	ECalClient *source_client;
294 	ECalClient *target_client;
295 	ICalComponent *component;
296 	gboolean with_send;
297 	gboolean close_after_save;
298 	ECalObjModType recur_mod;
299 	gboolean success;
300 	GError *error;
301 	gchar *alert_ident;
302 	gchar *alert_arg_0;
303 
304 	gboolean object_created;
305 	ICalPropertyMethod first_send;
306 	ICalPropertyMethod second_send;
307 	ECalComponent *send_comp;
308 	EActivity *send_activity;
309 	gboolean strip_alarms;
310 	gboolean only_new_attendees;
311 	GSList *mime_attach_list;
312 } SaveData;
313 
314 static void
save_data_free(SaveData * sd)315 save_data_free (SaveData *sd)
316 {
317 	if (sd) {
318 		e_comp_editor_enable (sd->comp_editor, TRUE);
319 
320 		if (sd->success) {
321 			if (sd->close_after_save) {
322 				g_signal_emit (sd->comp_editor, signals[EDITOR_CLOSED], 0, TRUE, NULL);
323 				gtk_widget_destroy (GTK_WIDGET (sd->comp_editor));
324 			} else {
325 				e_comp_editor_set_component (sd->comp_editor, sd->component);
326 
327 				e_comp_editor_fill_widgets (sd->comp_editor, sd->component);
328 
329 				g_clear_object (&sd->comp_editor->priv->source_client);
330 				sd->comp_editor->priv->source_client = g_object_ref (sd->target_client);
331 
332 				sd->comp_editor->priv->flags = sd->comp_editor->priv->flags & (~E_COMP_EDITOR_FLAG_IS_NEW);
333 
334 				e_comp_editor_sensitize_widgets (sd->comp_editor);
335 				e_comp_editor_set_changed (sd->comp_editor, FALSE);
336 			}
337 		} else if (sd->alert_ident) {
338 			e_alert_submit (
339 				E_ALERT_SINK (sd->comp_editor), sd->alert_ident, sd->alert_arg_0,
340 				sd->error ? sd->error->message : _("Unknown error"), NULL);
341 		}
342 
343 		if (sd->send_activity && e_activity_get_state (sd->send_activity) != E_ACTIVITY_CANCELLED)
344 			e_activity_set_state (sd->send_activity, E_ACTIVITY_COMPLETED);
345 
346 		g_clear_object (&sd->comp_editor);
347 		g_clear_object (&sd->source_client);
348 		g_clear_object (&sd->target_client);
349 		g_clear_object (&sd->send_comp);
350 		g_clear_object (&sd->send_activity);
351 		g_clear_object (&sd->component);
352 		g_clear_error (&sd->error);
353 		g_slist_free_full (sd->mime_attach_list, itip_cal_mime_attach_free);
354 		g_free (sd->alert_ident);
355 		g_free (sd->alert_arg_0);
356 		g_slice_free (SaveData, sd);
357 	}
358 }
359 
360 static gboolean
ece_send_process_method(SaveData * sd,ICalPropertyMethod send_method,ECalComponent * send_comp,ESourceRegistry * registry,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)361 ece_send_process_method (SaveData *sd,
362 			 ICalPropertyMethod send_method,
363 			 ECalComponent *send_comp,
364 			 ESourceRegistry *registry,
365 			 GCancellable *cancellable,
366 			 GAsyncReadyCallback callback,
367 			 gpointer user_data)
368 {
369 	GSList *mime_attach_list = NULL;
370 
371 	g_return_val_if_fail (sd != NULL, FALSE);
372 	g_return_val_if_fail (E_IS_CAL_COMPONENT (send_comp), FALSE);
373 	g_return_val_if_fail (send_method != I_CAL_METHOD_NONE, FALSE);
374 
375 	if (e_cal_component_has_attachments (send_comp) &&
376 	    e_client_check_capability (E_CLIENT (sd->target_client), E_CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
377 		/* Clone the component with attachments set to CID:...  */
378 		GSList *attach_list = NULL;
379 		GSList *attach;
380 
381 		/* mime_attach_list is freed by itip_send_component() */
382 		mime_attach_list = sd->mime_attach_list;
383 		sd->mime_attach_list = NULL;
384 
385 		for (attach = mime_attach_list; attach; attach = attach->next) {
386 			struct CalMimeAttach *cma = (struct CalMimeAttach *) attach->data;
387 			gchar *url;
388 
389 			url = g_strconcat ("cid:", cma->content_id, NULL);
390 			attach_list = g_slist_prepend (attach_list, i_cal_attach_new_from_url (url));
391 			g_free (url);
392 		}
393 
394 		if (attach_list) {
395 			attach_list = g_slist_reverse (attach_list);
396 
397 			e_cal_component_set_attachments (send_comp, attach_list);
398 
399 			g_slist_free_full (attach_list, g_object_unref);
400 		}
401 	}
402 
403 	itip_send_component (
404 		registry, send_method, send_comp, sd->target_client,
405 		NULL, mime_attach_list, NULL,
406 		(sd->strip_alarms ? E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS : 0) |
407 		(sd->only_new_attendees ? E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES : 0),
408 		cancellable, callback, user_data);
409 
410 	return TRUE;
411 }
412 
413 static void
ecep_second_send_processed_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)414 ecep_second_send_processed_cb (GObject *source_object,
415 			       GAsyncResult *result,
416 			       gpointer user_data)
417 {
418 	SaveData *sd = user_data;
419 
420 	g_return_if_fail (sd != NULL);
421 
422 	sd->success = itip_send_component_finish (result, &sd->error);
423 
424 	save_data_free (sd);
425 }
426 
427 static void
ecep_first_send_processed_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)428 ecep_first_send_processed_cb (GObject *source_object,
429 			      GAsyncResult *result,
430 			      gpointer user_data)
431 {
432 	SaveData *sd = user_data;
433 
434 	g_return_if_fail (sd != NULL);
435 
436 	sd->success = itip_send_component_finish (result, &sd->error);
437 	if (!sd->success || sd->second_send == I_CAL_METHOD_NONE) {
438 		save_data_free (sd);
439 	} else {
440 		sd->success = ece_send_process_method (sd, sd->second_send, sd->send_comp,
441 			e_shell_get_registry (sd->comp_editor->priv->shell),
442 			e_activity_get_cancellable (sd->send_activity),
443 			ecep_second_send_processed_cb, sd);
444 		if (!sd->success)
445 			save_data_free (sd);
446 	}
447 }
448 
449 static void
ece_prepare_send_component_done(gpointer ptr)450 ece_prepare_send_component_done (gpointer ptr)
451 {
452 	SaveData *sd = ptr;
453 
454 	g_return_if_fail (sd != NULL);
455 	g_return_if_fail (E_IS_COMP_EDITOR (sd->comp_editor));
456 	g_return_if_fail (sd->send_activity != NULL);
457 
458 	sd->success = ece_send_process_method (sd, sd->first_send, sd->send_comp,
459 		e_shell_get_registry (sd->comp_editor->priv->shell),
460 		e_activity_get_cancellable (sd->send_activity),
461 		ecep_first_send_processed_cb, sd);
462 	if (!sd->success)
463 		save_data_free (sd);
464 }
465 
466 static void
ece_prepare_send_component_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)467 ece_prepare_send_component_thread (EAlertSinkThreadJobData *job_data,
468 				   gpointer user_data,
469 				   GCancellable *cancellable,
470 				   GError **error)
471 {
472 	SaveData *sd = user_data;
473 	const gchar *alert_ident;
474 	ECalComponent *send_comp = NULL;
475 	guint32 flags;
476 	ESourceRegistry *registry;
477 
478 	g_return_if_fail (sd != NULL);
479 	g_return_if_fail (E_IS_CAL_CLIENT (sd->target_client));
480 	g_return_if_fail (I_CAL_IS_COMPONENT (sd->component));
481 
482 	while (!sd->send_activity) {
483 		/* Give the main thread a chance to set this object
484 		   and give it a 50 milliseconds delay too */
485 		g_thread_yield ();
486 		g_usleep (50000);
487 	}
488 
489 	switch (i_cal_component_isa (sd->component)) {
490 		case I_CAL_VEVENT_COMPONENT:
491 			alert_ident = "calendar:failed-send-event";
492 			break;
493 		case I_CAL_VJOURNAL_COMPONENT:
494 			alert_ident = "calendar:failed-send-memo";
495 			break;
496 		case I_CAL_VTODO_COMPONENT:
497 			alert_ident = "calendar:failed-send-task";
498 			break;
499 		default:
500 			g_warning ("%s: Cannot send component of kind %d", G_STRFUNC, i_cal_component_isa (sd->component));
501 			sd->success = FALSE;
502 			sd->alert_ident = g_strdup ("calendar:failed-send-event");
503 			return;
504 	}
505 
506 	g_free (sd->alert_ident);
507 	sd->alert_ident = g_strdup (alert_ident);
508 
509 	e_alert_sink_thread_job_set_alert_ident (job_data, alert_ident);
510 
511 	flags = e_comp_editor_get_flags (sd->comp_editor);
512 	registry = e_shell_get_registry (sd->comp_editor->priv->shell);
513 
514 	if (sd->recur_mod == E_CAL_OBJ_MOD_ALL && e_cal_component_is_instance (sd->send_comp)) {
515 		/* Ensure we send the master object, not the instance only */
516 		ICalComponent *icomp = NULL;
517 		const gchar *uid = NULL;
518 
519 		uid = e_cal_component_get_uid (sd->send_comp);
520 		if (e_cal_client_get_object_sync (sd->target_client, uid, NULL, &icomp, cancellable, NULL) &&
521 		    icomp != NULL) {
522 			send_comp = e_cal_component_new_from_icalcomponent (icomp);
523 		}
524 	}
525 
526 	if (!send_comp)
527 		send_comp = e_cal_component_clone (sd->send_comp);
528 
529 	cal_comp_util_copy_new_attendees (send_comp, sd->send_comp);
530 
531 	/* The user updates the delegated status to the Organizer,
532 	 * so remove all other attendees. */
533 	if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0) {
534 		gchar *address;
535 
536 		address = itip_get_comp_attendee (registry, send_comp, sd->target_client);
537 
538 		if (address) {
539 			ece_set_attendees_for_delegation (send_comp, address);
540 			g_free (address);
541 		}
542 	}
543 
544 	g_clear_object (&sd->send_comp);
545 	sd->send_comp = send_comp;
546 }
547 
548 static void
ece_save_component_done(gpointer ptr)549 ece_save_component_done (gpointer ptr)
550 {
551 	SaveData *sd = ptr;
552 
553 	g_return_if_fail (sd != NULL);
554 	g_return_if_fail (E_IS_COMP_EDITOR (sd->comp_editor));
555 
556 	if (sd->success) {
557 		ECalComponent *comp;
558 		gboolean delegated, is_new_meeting;
559 		gboolean only_new_attendees = FALSE;
560 		gboolean strip_alarms = TRUE;
561 		guint32 flags;
562 
563 		if (sd->object_created)
564 			g_signal_emit (sd->comp_editor, signals[OBJECT_CREATED], 0, NULL);
565 
566 		comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (sd->component));
567 		if (sd->comp_editor->priv->page_general) {
568 			GSList *added_attendees;
569 
570 			added_attendees = e_comp_editor_page_general_get_added_attendees (sd->comp_editor->priv->page_general);
571 			cal_comp_util_set_added_attendees_mails (comp, added_attendees);
572 		}
573 
574 		flags = e_comp_editor_get_flags (sd->comp_editor);
575 		is_new_meeting = (flags & E_COMP_EDITOR_FLAG_WITH_ATTENDEES) == 0 ||
576 			(flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0;
577 		delegated = (flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0 &&
578 			e_cal_client_check_save_schedules (sd->target_client);
579 
580 		if (delegated || (sd->with_send && e_cal_dialogs_send_component (
581 			GTK_WINDOW (sd->comp_editor), sd->target_client, comp,
582 			is_new_meeting, &strip_alarms, &only_new_attendees))) {
583 			ESourceRegistry *registry;
584 			EActivity *activity;
585 
586 			registry = e_shell_get_registry (sd->comp_editor->priv->shell);
587 
588 			if (delegated)
589 				only_new_attendees = FALSE;
590 
591 			if ((itip_organizer_is_user (registry, comp, sd->target_client) ||
592 			     itip_sentby_is_user (registry, comp, sd->target_client))) {
593 				if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_JOURNAL)
594 					sd->first_send = I_CAL_METHOD_PUBLISH;
595 				else
596 					sd->first_send = I_CAL_METHOD_REQUEST;
597 			} else {
598 				sd->first_send = I_CAL_METHOD_REQUEST;
599 
600 				if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0)
601 					sd->second_send = I_CAL_METHOD_REPLY;
602 			}
603 
604 			sd->mime_attach_list = ece_get_mime_attach_list (sd->comp_editor);
605 			sd->strip_alarms = strip_alarms;
606 			sd->only_new_attendees = only_new_attendees;
607 			sd->send_comp = comp;
608 			sd->success = FALSE;
609 			sd->alert_ident = g_strdup ("calendar:failed-send-event");
610 			sd->alert_arg_0 = e_util_get_source_full_name (registry, e_client_get_source (E_CLIENT (sd->target_client)));
611 
612 			activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (sd->comp_editor),
613 				_("Sending notifications to attendees…"), sd->alert_ident, sd->alert_arg_0,
614 				ece_prepare_send_component_thread, sd, ece_prepare_send_component_done);
615 
616 			if (activity)
617 				e_activity_bar_set_activity (sd->comp_editor->priv->activity_bar, activity);
618 
619 			/* The thread is waiting for this to be set first */
620 			sd->send_activity = activity;
621 
622 			return;
623 		}
624 
625 		g_clear_object (&comp);
626 	}
627 
628 	save_data_free (sd);
629 }
630 
631 static gboolean
ece_save_component_attachments_sync(ECalClient * cal_client,ICalComponent * component,GCancellable * cancellable,GError ** error)632 ece_save_component_attachments_sync (ECalClient *cal_client,
633 				     ICalComponent *component,
634 				     GCancellable *cancellable,
635 				     GError **error)
636 {
637 	ICalProperty *prop;
638 	const gchar *local_store;
639 	gchar *target_filename_prefix, *filename_prefix, *tmp;
640 	gboolean success = TRUE;
641 
642 	g_return_val_if_fail (E_IS_CAL_CLIENT (cal_client), FALSE);
643 	g_return_val_if_fail (I_CAL_IS_COMPONENT (component), FALSE);
644 
645 	tmp = g_strdup (i_cal_component_get_uid (component));
646 	e_util_make_safe_filename (tmp);
647 	filename_prefix = g_strconcat (tmp, "-", NULL);
648 	g_free (tmp);
649 
650 	local_store = e_cal_client_get_local_attachment_store (cal_client);
651 	if (local_store && *local_store &&
652 	    g_mkdir_with_parents (local_store, 0700) < 0) {
653 		g_debug ("%s: Failed to create local store directory '%s'", G_STRFUNC, local_store);
654 	}
655 
656 	target_filename_prefix = g_build_filename (local_store, filename_prefix, NULL);
657 
658 	g_free (filename_prefix);
659 
660 	for (prop = i_cal_component_get_first_property (component, I_CAL_ATTACH_PROPERTY);
661 	     prop && success;
662 	     g_object_unref (prop), prop = i_cal_component_get_next_property (component, I_CAL_ATTACH_PROPERTY)) {
663 		ICalAttach *attach;
664 		gchar *uri = NULL;
665 
666 		attach = i_cal_property_get_attach (prop);
667 		if (!attach)
668 			continue;
669 
670 		if (i_cal_attach_get_is_url (attach)) {
671 			const gchar *data;
672 
673 			data = i_cal_attach_get_url (attach);
674 			uri = i_cal_value_decode_ical_string (data);
675 		}
676 
677 		if (uri) {
678 			if (g_ascii_strncasecmp (uri, "file://", 7) == 0 &&
679 			    !g_str_has_prefix (uri + 7, target_filename_prefix)) {
680 				GFile *source, *destination;
681 				gchar *decoded_filename;
682 				gchar *target_filename;
683 
684 				decoded_filename = g_uri_unescape_string (strrchr (uri, '/') + 1, NULL);
685 				target_filename = g_strconcat (target_filename_prefix, decoded_filename, NULL);
686 				g_free (decoded_filename);
687 
688 				source = g_file_new_for_uri (uri);
689 				destination = g_file_new_for_path (target_filename);
690 
691 				if (source && destination) {
692 					success = g_file_copy (source, destination, G_FILE_COPY_OVERWRITE, cancellable, NULL, NULL, error);
693 					if (success) {
694 						g_free (uri);
695 						uri = g_file_get_uri (destination);
696 
697 						if (uri) {
698 							ICalAttach *new_attach;
699 							gchar *buf;
700 
701 							buf = i_cal_value_encode_ical_string (uri);
702 							new_attach = i_cal_attach_new_from_url (buf);
703 
704 							i_cal_property_set_attach (prop, new_attach);
705 
706 							g_object_unref (new_attach);
707 							g_free (buf);
708 						}
709 					}
710 				}
711 
712 				g_clear_object (&source);
713 				g_clear_object (&destination);
714 				g_free (target_filename);
715 			}
716 
717 			g_free (uri);
718 		}
719 
720 		success = success & !g_cancellable_set_error_if_cancelled (cancellable, error);
721 	}
722 
723 	g_clear_object (&prop);
724 	g_free (target_filename_prefix);
725 
726 	return success;
727 }
728 
729 static void
ece_gather_tzids_cb(ICalParameter * param,gpointer user_data)730 ece_gather_tzids_cb (ICalParameter *param,
731 		     gpointer user_data)
732 {
733 	GHashTable *tzids = user_data;
734 	const gchar *tzid;
735 
736 	g_return_if_fail (param != NULL);
737 	g_return_if_fail (tzids != NULL);
738 
739 	tzid = i_cal_parameter_get_tzid (param);
740 	if (tzid && *tzid && g_ascii_strcasecmp (tzid, "UTC") != 0)
741 		g_hash_table_insert (tzids, g_strdup (tzid), NULL);
742 }
743 
744 static gboolean
ece_save_component_add_timezones_sync(SaveData * sd,GCancellable * cancellable,GError ** error)745 ece_save_component_add_timezones_sync (SaveData *sd,
746 				       GCancellable *cancellable,
747 				       GError **error)
748 {
749 	GHashTable *tzids;
750 	GHashTableIter iter;
751 	gpointer key, value;
752 	gboolean source_is_target;
753 
754 	g_return_val_if_fail (sd != NULL, FALSE);
755 	g_return_val_if_fail (I_CAL_IS_COMPONENT (sd->component), FALSE);
756 	g_return_val_if_fail (sd->target_client != NULL, FALSE);
757 
758 	sd->success = TRUE;
759 
760 	tzids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
761 	source_is_target = !sd->source_client ||
762 		e_source_equal (e_client_get_source (E_CLIENT (sd->target_client)),
763 				e_client_get_source (E_CLIENT (sd->source_client)));
764 
765 	i_cal_component_foreach_tzid (sd->component, ece_gather_tzids_cb, tzids);
766 
767 	g_hash_table_iter_init (&iter, tzids);
768 	while (sd->success && g_hash_table_iter_next (&iter, &key, &value)) {
769 		const gchar *tzid = key;
770 		ICalTimezone *zone = NULL;
771 		GError *local_error = NULL;
772 
773 		if (!e_cal_client_get_timezone_sync (source_is_target ? sd->target_client : sd->source_client,
774 			tzid, &zone, cancellable, &local_error)) {
775 			zone = i_cal_timezone_get_builtin_timezone_from_tzid (tzid);
776 			if (!zone)
777 				zone = i_cal_timezone_get_builtin_timezone (tzid);
778 			if (!zone) {
779 				g_propagate_error (error, local_error);
780 				local_error = NULL;
781 				sd->success = FALSE;
782 				break;
783 			}
784 		}
785 
786 		sd->success = e_cal_client_add_timezone_sync (sd->target_client, zone, cancellable, error);
787 
788 		g_clear_error (&local_error);
789 	}
790 
791 	g_hash_table_destroy (tzids);
792 
793 	return sd->success;
794 }
795 
796 static void
ece_save_component_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)797 ece_save_component_thread (EAlertSinkThreadJobData *job_data,
798 			   gpointer user_data,
799 			   GCancellable *cancellable,
800 			   GError **error)
801 {
802 	SaveData *sd = user_data;
803 	const gchar *create_alert_ident, *modify_alert_ident, *remove_alert_ident, *get_alert_ident;
804 	gchar *orig_uid, *new_uid = NULL;
805 
806 	g_return_if_fail (sd != NULL);
807 	g_return_if_fail (E_IS_CAL_CLIENT (sd->target_client));
808 	g_return_if_fail (I_CAL_IS_COMPONENT (sd->component));
809 
810 	switch (i_cal_component_isa (sd->component)) {
811 		case I_CAL_VEVENT_COMPONENT:
812 			create_alert_ident = "calendar:failed-create-event";
813 			modify_alert_ident = "calendar:failed-modify-event";
814 			remove_alert_ident = "calendar:failed-remove-event";
815 			get_alert_ident = "calendar:failed-get-event";
816 			break;
817 		case I_CAL_VJOURNAL_COMPONENT:
818 			create_alert_ident = "calendar:failed-create-memo";
819 			modify_alert_ident = "calendar:failed-modify-memo";
820 			remove_alert_ident = "calendar:failed-remove-memo";
821 			get_alert_ident = "calendar:failed-get-memo";
822 			break;
823 		case I_CAL_VTODO_COMPONENT:
824 			create_alert_ident = "calendar:failed-create-task";
825 			modify_alert_ident = "calendar:failed-modify-task";
826 			remove_alert_ident = "calendar:failed-remove-task";
827 			get_alert_ident = "calendar:failed-get-task";
828 			break;
829 		default:
830 			g_warning ("%s: Cannot save component of kind %d", G_STRFUNC, i_cal_component_isa (sd->component));
831 			return;
832 	}
833 
834 	sd->success = ece_save_component_add_timezones_sync (sd, cancellable, error);
835 	if (!sd->success) {
836 		e_alert_sink_thread_job_set_alert_ident (job_data, "calendar:failed-add-timezone");
837 		return;
838 	}
839 
840 	sd->success = ece_save_component_attachments_sync (sd->target_client, sd->component, cancellable, error);
841 	if (!sd->success) {
842 		e_alert_sink_thread_job_set_alert_ident (job_data, "calendar:failed-save-attachments");
843 		return;
844 	}
845 
846 	orig_uid = g_strdup (i_cal_component_get_uid (sd->component));
847 
848 	if (cal_comp_is_icalcomp_on_server_sync (sd->component, sd->target_client, cancellable, error)) {
849 		ECalComponent *comp;
850 		gboolean has_recurrences;
851 
852 		e_alert_sink_thread_job_set_alert_ident (job_data, modify_alert_ident);
853 
854 		comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (sd->component));
855 		g_return_if_fail (comp != NULL);
856 
857 		has_recurrences = e_cal_util_component_has_recurrences (sd->component);
858 
859 		if (has_recurrences && sd->recur_mod == E_CAL_OBJ_MOD_ALL)
860 			sd->success = comp_util_sanitize_recurrence_master_sync (comp, sd->target_client, cancellable, error);
861 		else
862 			sd->success = TRUE;
863 
864 		if (sd->recur_mod == E_CAL_OBJ_MOD_THIS) {
865 			e_cal_component_set_rdates (comp, NULL);
866 			e_cal_component_set_rrules (comp, NULL);
867 			e_cal_component_set_exdates (comp, NULL);
868 			e_cal_component_set_exrules (comp, NULL);
869 		}
870 
871 		sd->success = sd->success && e_cal_client_modify_object_sync (
872 			sd->target_client, e_cal_component_get_icalcomponent (comp), sd->recur_mod, E_CAL_OPERATION_FLAG_NONE, cancellable, error);
873 
874 		g_clear_object (&comp);
875 	} else {
876 		e_alert_sink_thread_job_set_alert_ident (job_data, create_alert_ident);
877 
878 		sd->success = e_cal_client_create_object_sync (sd->target_client, sd->component, E_CAL_OPERATION_FLAG_NONE, &new_uid, cancellable, error);
879 
880 		if (sd->success)
881 			sd->object_created = TRUE;
882 	}
883 
884 	if (sd->success && sd->source_client &&
885 	    !e_source_equal (e_client_get_source (E_CLIENT (sd->target_client)),
886 			     e_client_get_source (E_CLIENT (sd->source_client))) &&
887 	    cal_comp_is_icalcomp_on_server_sync (sd->component, sd->source_client, cancellable, NULL)) {
888 		ECalObjModType recur_mod = E_CAL_OBJ_MOD_THIS;
889 
890 		/* Comp found a new home. Remove it from old one. */
891 		if (e_cal_util_component_is_instance (sd->component) ||
892 		    e_cal_util_component_has_recurrences (sd->component))
893 			recur_mod = E_CAL_OBJ_MOD_ALL;
894 
895 		sd->success = e_cal_client_remove_object_sync (
896 			sd->source_client, orig_uid, NULL, recur_mod,
897 			E_CAL_OPERATION_FLAG_NONE, cancellable, error);
898 
899 		if (!sd->success) {
900 			gchar *source_display_name;
901 
902 			source_display_name = e_util_get_source_full_name (e_shell_get_registry (sd->comp_editor->priv->shell),
903 				e_client_get_source (E_CLIENT (sd->source_client)));
904 
905 			e_alert_sink_thread_job_set_alert_ident (job_data, remove_alert_ident);
906 			e_alert_sink_thread_job_set_alert_arg_0 (job_data, source_display_name);
907 
908 			g_free (source_display_name);
909 		}
910 	}
911 
912 	if (new_uid) {
913 		i_cal_component_set_uid (sd->component, new_uid);
914 		g_free (new_uid);
915 	}
916 
917 	g_free (orig_uid);
918 
919 	if (sd->success && !sd->close_after_save) {
920 		ICalComponent *comp = NULL;
921 		gchar *uid, *rid = NULL;
922 		GError *local_error = NULL;
923 
924 		uid = g_strdup (i_cal_component_get_uid (sd->component));
925 		rid = e_cal_util_component_get_recurid_as_string (sd->component);
926 
927 		sd->success = e_cal_client_get_object_sync (sd->target_client, uid, rid, &comp, cancellable, &local_error);
928 
929 		/* Re-read without recurrence ID and add it manually in case the edited component is not a detached instance. */
930 		if (!sd->success && rid && *rid && g_error_matches (local_error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND)) {
931 			g_clear_error (&local_error);
932 
933 			sd->success = e_cal_client_get_object_sync (sd->target_client, uid, NULL, &comp, cancellable, error);
934 
935 			if (sd->success && comp) {
936 				ICalProperty *rid_prop;
937 
938 				rid_prop = i_cal_component_get_first_property (sd->component, I_CAL_RECURRENCEID_PROPERTY);
939 
940 				if (rid_prop) {
941 					ICalProperty *clone;
942 
943 					clone = i_cal_property_clone (rid_prop);
944 
945 					if (clone)
946 						i_cal_component_take_property (comp, clone);
947 				}
948 
949 				g_clear_object (&rid_prop);
950 			}
951 		} else if (local_error) {
952 			g_propagate_error (error, local_error);
953 		}
954 
955 		if (sd->success && comp) {
956 			g_clear_object (&sd->component);
957 			sd->component = comp;
958 		} else {
959 			e_alert_sink_thread_job_set_alert_ident (job_data, get_alert_ident);
960 		}
961 
962 		g_free (uid);
963 		g_free (rid);
964 	}
965 }
966 
967 static void
ece_save_component(ECompEditor * comp_editor,ICalComponent * component,gboolean with_send,gboolean close_after_save)968 ece_save_component (ECompEditor *comp_editor,
969 		    ICalComponent *component,
970 		    gboolean with_send,
971 		    gboolean close_after_save)
972 {
973 	EActivity *activity;
974 	ECalComponent *comp;
975 	EShell *shell;
976 	ESourceRegistry *registry;
977 	const gchar *summary;
978 	ECalObjModType recur_mod = E_CAL_OBJ_MOD_THIS;
979 	SaveData *sd;
980 	gchar *source_display_name;
981 
982 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
983 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
984 
985 	summary = i_cal_component_get_summary (component);
986 	if (!summary || !*summary) {
987 		if (!e_cal_dialogs_send_component_prompt_subject (GTK_WINDOW (comp_editor), component)) {
988 			return;
989 		}
990 	}
991 
992 	if (e_cal_util_component_is_instance (component)) {
993 		if (!e_cal_dialogs_recur_icalcomp (comp_editor->priv->target_client,
994 			component, &recur_mod, GTK_WINDOW (comp_editor), FALSE)) {
995 			return;
996 		}
997 	} else if (e_cal_util_component_has_recurrences (component)) {
998 		recur_mod = E_CAL_OBJ_MOD_ALL;
999 	}
1000 
1001 	e_comp_editor_enable (comp_editor, FALSE);
1002 
1003 	shell = e_comp_editor_get_shell (comp_editor);
1004 	registry = e_shell_get_registry (shell);
1005 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (component));
1006 
1007 	sd = g_slice_new0 (SaveData);
1008 	sd->comp_editor = g_object_ref (comp_editor);
1009 	sd->source_client = comp_editor->priv->source_client ? g_object_ref (comp_editor->priv->source_client) : NULL;
1010 	sd->target_client = g_object_ref (comp_editor->priv->target_client);
1011 	sd->component = i_cal_component_clone (component);
1012 	sd->with_send = with_send && (!itip_has_any_attendees (comp) ||
1013 		    (itip_organizer_is_user (registry, comp, sd->target_client) ||
1014 		     itip_sentby_is_user (registry, comp, sd->target_client)));
1015 	sd->close_after_save = close_after_save;
1016 	sd->recur_mod = recur_mod;
1017 	sd->first_send = I_CAL_METHOD_NONE;
1018 	sd->second_send = I_CAL_METHOD_NONE;
1019 	sd->success = FALSE;
1020 
1021 	source_display_name = e_util_get_source_full_name (e_shell_get_registry (comp_editor->priv->shell),
1022 		e_client_get_source (E_CLIENT (sd->target_client)));
1023 
1024 	activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (comp_editor),
1025 		_("Saving changes…"), "calendar:failed-create-event", source_display_name,
1026 		ece_save_component_thread, sd, ece_save_component_done);
1027 
1028 	if (activity)
1029 		e_activity_bar_set_activity (comp_editor->priv->activity_bar, activity);
1030 
1031 	g_clear_object (&comp);
1032 	g_clear_object (&activity);
1033 	g_free (source_display_name);
1034 }
1035 
1036 typedef struct _OpenTargetClientData {
1037 	ECompEditor *comp_editor;
1038 	ESource *source;
1039 	gchar *extension_name;
1040 	EClient *client;
1041 	gchar *cal_email_address;
1042 	gchar *alarm_email_address;
1043 	gboolean is_target_client_change;
1044 	EActivity *activity;
1045 } OpenTargetClientData;
1046 
1047 static void
open_target_client_data_free(gpointer ptr)1048 open_target_client_data_free (gpointer ptr)
1049 {
1050 	OpenTargetClientData *otc = ptr;
1051 
1052 	if (otc) {
1053 		if (otc->comp_editor) {
1054 			if (otc->client) {
1055 				gboolean previous_changed = e_comp_editor_get_changed (otc->comp_editor);
1056 
1057 				e_comp_editor_set_alarm_email_address (otc->comp_editor, otc->alarm_email_address);
1058 				e_comp_editor_set_cal_email_address (otc->comp_editor, otc->cal_email_address);
1059 				e_comp_editor_set_target_client (otc->comp_editor, E_CAL_CLIENT (otc->client));
1060 
1061 				if (otc->is_target_client_change)
1062 					e_comp_editor_set_changed (otc->comp_editor, TRUE);
1063 				else
1064 					e_comp_editor_set_changed (otc->comp_editor, previous_changed);
1065 			}
1066 
1067 			if (otc->comp_editor->priv->activity_bar && otc->activity) {
1068 				if (otc->activity == e_activity_bar_get_activity (otc->comp_editor->priv->activity_bar))
1069 					e_activity_bar_set_activity (otc->comp_editor->priv->activity_bar, NULL);
1070 
1071 				if (otc->activity == otc->comp_editor->priv->target_client_opening)
1072 					g_clear_object (&otc->comp_editor->priv->target_client_opening);
1073 			}
1074 
1075 			if (otc->source) {
1076 				EShell *shell;
1077 				ECredentialsPrompter *credentials_prompter;
1078 
1079 				shell = e_comp_editor_get_shell (otc->comp_editor);
1080 				credentials_prompter = e_shell_get_credentials_prompter (shell);
1081 
1082 				e_credentials_prompter_set_auto_prompt_disabled_for (credentials_prompter, otc->source, TRUE);
1083 			}
1084 
1085 			e_comp_editor_sensitize_widgets (otc->comp_editor);
1086 		}
1087 
1088 		g_clear_object (&otc->comp_editor);
1089 		g_clear_object (&otc->source);
1090 		g_clear_object (&otc->client);
1091 		g_clear_object (&otc->activity);
1092 		g_free (otc->extension_name);
1093 		g_free (otc->cal_email_address);
1094 		g_free (otc->alarm_email_address);
1095 		g_slice_free (OpenTargetClientData, otc);
1096 	}
1097 }
1098 
1099 static void
comp_editor_open_target_client_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)1100 comp_editor_open_target_client_thread (EAlertSinkThreadJobData *job_data,
1101 				       gpointer user_data,
1102 				       GCancellable *cancellable,
1103 				       GError **error)
1104 {
1105 	OpenTargetClientData *otc = user_data;
1106 	EClientCache *client_cache;
1107 
1108 	g_return_if_fail (otc != NULL);
1109 
1110 	if (g_cancellable_set_error_if_cancelled (cancellable, error))
1111 		return;
1112 
1113 	g_return_if_fail (E_IS_COMP_EDITOR (otc->comp_editor));
1114 	g_return_if_fail (E_IS_SOURCE (otc->source));
1115 	g_return_if_fail (otc->extension_name != NULL);
1116 
1117 	client_cache = e_shell_get_client_cache (e_comp_editor_get_shell (otc->comp_editor));
1118 
1119 	otc->client = e_client_cache_get_client_sync (client_cache, otc->source, otc->extension_name,
1120 		30, cancellable, error);
1121 
1122 	if (otc->client) {
1123 		/* Cache some properties which require remote calls */
1124 
1125 		if (!g_cancellable_is_cancelled (cancellable)) {
1126 			e_client_get_capabilities (otc->client);
1127 		}
1128 
1129 		if (!g_cancellable_is_cancelled (cancellable)) {
1130 			e_client_get_backend_property_sync (otc->client,
1131 				E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
1132 				&otc->cal_email_address,
1133 				cancellable, error);
1134 		}
1135 
1136 		if (!g_cancellable_is_cancelled (cancellable)) {
1137 			e_client_get_backend_property_sync (otc->client,
1138 				E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS,
1139 				&otc->alarm_email_address,
1140 				cancellable, error);
1141 		}
1142 
1143 		if (g_cancellable_is_cancelled (cancellable))
1144 			g_clear_object (&otc->client);
1145 	}
1146 }
1147 
1148 typedef struct _UpdateActivityBarData {
1149 	ECompEditor *comp_editor;
1150 	EActivity *activity;
1151 } UpdateActivityBarData;
1152 
1153 static void
update_activity_bar_data_free(gpointer ptr)1154 update_activity_bar_data_free (gpointer ptr)
1155 {
1156 	UpdateActivityBarData *uab = ptr;
1157 
1158 	if (uab) {
1159 		g_clear_object (&uab->comp_editor);
1160 		g_clear_object (&uab->activity);
1161 		g_slice_free (UpdateActivityBarData, uab);
1162 	}
1163 }
1164 
1165 static gboolean
update_activity_bar_cb(gpointer user_data)1166 update_activity_bar_cb (gpointer user_data)
1167 {
1168 	UpdateActivityBarData *uab = user_data;
1169 
1170 	g_return_val_if_fail (uab != NULL, FALSE);
1171 	g_return_val_if_fail (E_IS_COMP_EDITOR (uab->comp_editor), FALSE);
1172 	g_return_val_if_fail (E_IS_ACTIVITY (uab->activity), FALSE);
1173 
1174 	if (uab->comp_editor->priv->target_client_opening == uab->activity &&
1175 	    e_activity_get_state (uab->activity) != E_ACTIVITY_CANCELLED &&
1176 	    e_activity_get_state (uab->activity) != E_ACTIVITY_COMPLETED) {
1177 		e_activity_bar_set_activity (uab->comp_editor->priv->activity_bar, uab->activity);
1178 	}
1179 
1180 	return FALSE;
1181 }
1182 
1183 static void
e_comp_editor_disconnect_target_backend_property_change_handler(ECompEditor * comp_editor)1184 e_comp_editor_disconnect_target_backend_property_change_handler (ECompEditor *comp_editor)
1185 {
1186 	if (comp_editor->priv->target_client && comp_editor->priv->target_backend_property_change_id) {
1187 		g_signal_handler_disconnect (comp_editor->priv->target_client, comp_editor->priv->target_backend_property_change_id);
1188 		comp_editor->priv->target_backend_property_change_id = 0;
1189 	}
1190 }
1191 
1192 static void
e_comp_editor_open_target_client(ECompEditor * comp_editor)1193 e_comp_editor_open_target_client (ECompEditor *comp_editor)
1194 {
1195 	OpenTargetClientData *otc;
1196 	ESource *source;
1197 	EActivity *activity;
1198 	ECredentialsPrompter *credentials_prompter;
1199 	gchar *source_display_name, *description = NULL, *alert_ident = NULL, *alert_arg_0 = NULL;
1200 	gboolean is_target_client_change;
1201 	const gchar *extension_name;
1202 
1203 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1204 	g_return_if_fail (comp_editor->priv->page_general != NULL);
1205 
1206 	source = e_comp_editor_page_general_ref_selected_source (comp_editor->priv->page_general);
1207 	if (!source)
1208 		return;
1209 
1210 	if (comp_editor->priv->target_client &&
1211 	    e_client_get_source (E_CLIENT (comp_editor->priv->target_client)) == source) {
1212 		g_clear_object (&source);
1213 		return;
1214 	}
1215 
1216 	if (comp_editor->priv->target_client_opening) {
1217 		e_activity_cancel (comp_editor->priv->target_client_opening);
1218 		g_clear_object (&comp_editor->priv->target_client_opening);
1219 	}
1220 
1221 	e_comp_editor_disconnect_target_backend_property_change_handler (comp_editor);
1222 	is_target_client_change = comp_editor->priv->target_client != NULL;
1223 	g_clear_object (&comp_editor->priv->target_client);
1224 
1225 	extension_name = e_comp_editor_page_general_get_source_extension_name (comp_editor->priv->page_general);
1226 	source_display_name = e_util_get_source_full_name (
1227 		e_shell_get_registry (e_comp_editor_get_shell (comp_editor)),
1228 		source);
1229 
1230 	g_return_if_fail (e_util_get_open_source_job_info (extension_name, source_display_name,
1231 		&description, &alert_ident, &alert_arg_0));
1232 
1233 	credentials_prompter = e_shell_get_credentials_prompter (e_comp_editor_get_shell (comp_editor));
1234 	e_credentials_prompter_set_auto_prompt_disabled_for (credentials_prompter, source, FALSE);
1235 
1236 	otc = g_slice_new0 (OpenTargetClientData);
1237 	otc->extension_name = g_strdup (extension_name);
1238 	otc->comp_editor = g_object_ref (comp_editor);
1239 	otc->source = g_object_ref (source);
1240 	otc->is_target_client_change = is_target_client_change;
1241 
1242 	activity = e_alert_sink_submit_thread_job (
1243 		E_ALERT_SINK (comp_editor), description, alert_ident, alert_arg_0,
1244 		comp_editor_open_target_client_thread, otc,
1245 		open_target_client_data_free);
1246 
1247 	otc->activity = g_object_ref (activity);
1248 	comp_editor->priv->target_client_opening = g_object_ref (activity);
1249 
1250 	/* Close all alerts */
1251 	while (e_alert_bar_close_alert (comp_editor->priv->alert_bar)) {
1252 		;
1253 	}
1254 
1255 	if (comp_editor->priv->activity_bar) {
1256 		UpdateActivityBarData *uab;
1257 
1258 		uab = g_slice_new0 (UpdateActivityBarData);
1259 		uab->comp_editor = g_object_ref (comp_editor);
1260 		uab->activity = g_object_ref (activity);
1261 
1262 		/* To avoid UI flickering when the source can be opened quickly */
1263 		g_timeout_add_seconds_full (G_PRIORITY_LOW, 1,
1264 			update_activity_bar_cb, uab, update_activity_bar_data_free);
1265 	}
1266 
1267 	g_free (description);
1268 	g_free (alert_ident);
1269 	g_free (alert_arg_0);
1270 	g_free (source_display_name);
1271 	g_clear_object (&source);
1272 	g_clear_object (&activity);
1273 }
1274 
1275 static void
e_comp_editor_update_window_title(ECompEditor * comp_editor)1276 e_comp_editor_update_window_title (ECompEditor *comp_editor)
1277 {
1278 	ECompEditorClass *comp_editor_class;
1279 	gboolean with_attendees = FALSE;
1280 	const gchar *format, *title_suffix;
1281 	gchar *title;
1282 
1283 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1284 
1285 	if (comp_editor->priv->page_general)
1286 		with_attendees = e_comp_editor_page_general_get_show_attendees (comp_editor->priv->page_general);
1287 
1288 	comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor);
1289 	if (with_attendees)
1290 		format = comp_editor_class->title_format_with_attendees;
1291 	else
1292 		format = comp_editor_class->title_format_without_attendees;
1293 
1294 	title_suffix = e_comp_editor_get_title_suffix (comp_editor);
1295 
1296 	title = g_strdup_printf (format, title_suffix && *title_suffix ? title_suffix : _("No Summary"));
1297 
1298 	gtk_window_set_icon_name (GTK_WINDOW (comp_editor), comp_editor_class->icon_name);
1299 	gtk_window_set_title (GTK_WINDOW (comp_editor), title);
1300 
1301 	g_free (title);
1302 }
1303 
1304 static void
e_comp_editor_close(ECompEditor * comp_editor)1305 e_comp_editor_close (ECompEditor *comp_editor)
1306 {
1307 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1308 
1309 	g_signal_emit (comp_editor, signals[EDITOR_CLOSED], 0, FALSE, NULL);
1310 
1311 	gtk_widget_destroy (GTK_WIDGET (comp_editor));
1312 }
1313 
1314 static void
e_comp_editor_save_and_close(ECompEditor * comp_editor,gboolean can_close)1315 e_comp_editor_save_and_close (ECompEditor *comp_editor,
1316 			      gboolean can_close)
1317 {
1318 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1319 
1320 	if (comp_editor->priv->component) {
1321 		ICalComponent *component = i_cal_component_clone (comp_editor->priv->component);
1322 		if (component && e_comp_editor_fill_component (comp_editor, component)) {
1323 			ece_save_component (comp_editor, component, TRUE, can_close);
1324 			g_clear_object (&component);
1325 		}
1326 	}
1327 }
1328 
1329 static GtkResponseType
ece_save_component_dialog(ECompEditor * comp_editor)1330 ece_save_component_dialog (ECompEditor *comp_editor)
1331 {
1332 	ICalComponent *component;
1333 	GtkWindow *parent;
1334 
1335 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), GTK_RESPONSE_NO);
1336 	g_return_val_if_fail (e_comp_editor_get_component (comp_editor) != NULL, GTK_RESPONSE_NO);
1337 
1338 	parent = GTK_WINDOW (comp_editor);
1339 	component = e_comp_editor_get_component (comp_editor);
1340 	switch (i_cal_component_isa (component)) {
1341 		case I_CAL_VEVENT_COMPONENT:
1342 			if (e_comp_editor_page_general_get_show_attendees (comp_editor->priv->page_general))
1343 				return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-meeting", NULL);
1344 			else
1345 				return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-appointment", NULL);
1346 		case I_CAL_VTODO_COMPONENT:
1347 			return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-task", NULL);
1348 		case I_CAL_VJOURNAL_COMPONENT:
1349 			return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-memo", NULL);
1350 		default:
1351 			return GTK_RESPONSE_NO;
1352 	}
1353 }
1354 
1355 static gboolean
e_comp_editor_prompt_and_save_changes(ECompEditor * comp_editor,gboolean with_send)1356 e_comp_editor_prompt_and_save_changes (ECompEditor *comp_editor,
1357 				       gboolean with_send)
1358 {
1359 	ICalComponent *component;
1360 
1361 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
1362 
1363 	if (!e_comp_editor_get_changed (comp_editor))
1364 		return TRUE;
1365 
1366 	switch (ece_save_component_dialog (comp_editor)) {
1367 	case GTK_RESPONSE_YES: /* Save */
1368 		if (e_client_is_readonly (E_CLIENT (comp_editor->priv->target_client))) {
1369 			e_alert_submit (
1370 				E_ALERT_SINK (comp_editor),
1371 				"calendar:prompt-read-only-cal-editor",
1372 				e_source_get_display_name (
1373 					e_client_get_source (E_CLIENT (comp_editor->priv->target_client))),
1374 				NULL);
1375 			/* don't discard changes when selected readonly calendar */
1376 			return FALSE;
1377 		}
1378 
1379 		if (comp_editor->priv->component &&
1380 		    e_comp_editor_page_general_get_show_attendees (comp_editor->priv->page_general) &&
1381 		    i_cal_component_isa (comp_editor->priv->component) == I_CAL_VTODO_COMPONENT
1382 		    && e_client_check_capability (E_CLIENT (comp_editor->priv->target_client), E_CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) {
1383 			e_alert_submit (
1384 				E_ALERT_SINK (comp_editor),
1385 				"calendar:prompt-no-task-assignment-editor",
1386 				e_source_get_display_name (
1387 					e_client_get_source (E_CLIENT (comp_editor->priv->target_client))),
1388 				NULL);
1389 			return FALSE;
1390 		}
1391 
1392 		component = i_cal_component_clone (comp_editor->priv->component);
1393 		if (!e_comp_editor_fill_component (comp_editor, component)) {
1394 			g_clear_object (&component);
1395 			return FALSE;
1396 		}
1397 
1398 		ece_save_component (comp_editor, component, with_send, TRUE);
1399 
1400 		g_clear_object (&component);
1401 
1402 		return FALSE;
1403 	case GTK_RESPONSE_NO: /* Discard */
1404 		return TRUE;
1405 	case GTK_RESPONSE_CANCEL: /* Cancel */
1406 	default:
1407 		return FALSE;
1408 	}
1409 
1410 	return FALSE;
1411 }
1412 
1413 static void
action_close_cb(GtkAction * action,ECompEditor * comp_editor)1414 action_close_cb (GtkAction *action,
1415                  ECompEditor *comp_editor)
1416 {
1417 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1418 
1419 	if (e_comp_editor_prompt_and_save_changes (comp_editor, TRUE))
1420 		e_comp_editor_close (comp_editor);
1421 }
1422 
1423 static void
action_help_cb(GtkAction * action,ECompEditor * comp_editor)1424 action_help_cb (GtkAction *action,
1425                 ECompEditor *comp_editor)
1426 {
1427 	ECompEditorClass *klass;
1428 
1429 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1430 
1431 	klass = E_COMP_EDITOR_GET_CLASS (comp_editor);
1432 	g_return_if_fail (klass->help_section != NULL);
1433 
1434 	e_display_help (GTK_WINDOW (comp_editor), klass->help_section);
1435 }
1436 
1437 static void
ece_print_or_preview(ECompEditor * comp_editor,GtkPrintOperationAction print_action)1438 ece_print_or_preview (ECompEditor *comp_editor,
1439 		      GtkPrintOperationAction print_action)
1440 {
1441 	ICalComponent *component;
1442 	ECalComponent *comp;
1443 
1444 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1445 	g_return_if_fail (e_comp_editor_get_component (comp_editor) != NULL);
1446 
1447 	component = i_cal_component_clone (e_comp_editor_get_component (comp_editor));
1448 	if (!e_comp_editor_fill_component (comp_editor, component)) {
1449 		g_clear_object (&component);
1450 		return;
1451 	}
1452 
1453 	comp = e_cal_component_new_from_icalcomponent (component);
1454 	g_return_if_fail (comp != NULL);
1455 
1456 	print_comp (comp,
1457 		e_comp_editor_get_target_client (comp_editor),
1458 		calendar_config_get_icaltimezone (),
1459 		calendar_config_get_24_hour_format (),
1460 		print_action);
1461 
1462 	g_object_unref (comp);
1463 }
1464 
1465 static void
action_print_cb(GtkAction * action,ECompEditor * comp_editor)1466 action_print_cb (GtkAction *action,
1467                  ECompEditor *comp_editor)
1468 {
1469 	ece_print_or_preview (comp_editor, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
1470 }
1471 
1472 static void
action_print_preview_cb(GtkAction * action,ECompEditor * comp_editor)1473 action_print_preview_cb (GtkAction *action,
1474                          ECompEditor *comp_editor)
1475 {
1476 	ece_print_or_preview (comp_editor, GTK_PRINT_OPERATION_ACTION_PREVIEW);
1477 }
1478 
1479 static void
action_save_cb(GtkAction * action,ECompEditor * comp_editor)1480 action_save_cb (GtkAction *action,
1481                 ECompEditor *comp_editor)
1482 {
1483 	e_comp_editor_save_and_close (comp_editor, FALSE);
1484 }
1485 
1486 static void
action_save_and_close_cb(GtkAction * action,ECompEditor * comp_editor)1487 action_save_and_close_cb (GtkAction *action,
1488                           ECompEditor *comp_editor)
1489 {
1490 	e_comp_editor_save_and_close (comp_editor, TRUE);
1491 }
1492 
1493 static gboolean
ece_organizer_email_address_is_user(ECompEditor * comp_editor,EClient * client,const gchar * email_address,gboolean is_organizer)1494 ece_organizer_email_address_is_user (ECompEditor *comp_editor,
1495 				     EClient *client,
1496 				     const gchar *email_address,
1497 				     gboolean is_organizer)
1498 {
1499 	ESourceRegistry *registry;
1500 	const gchar *cal_email_address;
1501 
1502 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
1503 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
1504 
1505 	email_address = itip_strip_mailto (email_address);
1506 
1507 	if (!email_address || !*email_address)
1508 		return FALSE;
1509 
1510 	cal_email_address = e_comp_editor_get_cal_email_address (comp_editor);
1511 	if (cal_email_address && *cal_email_address &&
1512 	    g_ascii_strcasecmp (cal_email_address, email_address) == 0) {
1513 		return TRUE;
1514 	}
1515 
1516 	if (is_organizer && e_client_check_capability (client, E_CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS))
1517 		return FALSE;
1518 
1519 	registry = e_shell_get_registry (e_comp_editor_get_shell (comp_editor));
1520 
1521 	return itip_address_is_user (registry, email_address);
1522 }
1523 
1524 static gboolean
ece_organizer_is_user(ECompEditor * comp_editor,ICalComponent * component,EClient * client)1525 ece_organizer_is_user (ECompEditor *comp_editor,
1526 		       ICalComponent *component,
1527 		       EClient *client)
1528 {
1529 	ICalProperty *prop;
1530 	const gchar *organizer;
1531 	gboolean res;
1532 
1533 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
1534 	g_return_val_if_fail (I_CAL_IS_COMPONENT (component), FALSE);
1535 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
1536 
1537 	prop = i_cal_component_get_first_property (component, I_CAL_ORGANIZER_PROPERTY);
1538 	if (!prop || e_client_check_capability (client, E_CAL_STATIC_CAPABILITY_NO_ORGANIZER)) {
1539 		g_clear_object (&prop);
1540 		return FALSE;
1541 	}
1542 
1543 	organizer = itip_strip_mailto (i_cal_property_get_organizer (prop));
1544 	if (!organizer || !*organizer) {
1545 		g_clear_object (&prop);
1546 		return FALSE;
1547 	}
1548 
1549 	res = ece_organizer_email_address_is_user (comp_editor, client, organizer, TRUE);
1550 
1551 	g_clear_object (&prop);
1552 
1553 	return res;
1554 }
1555 
1556 static gboolean
ece_sentby_is_user(ECompEditor * comp_editor,ICalComponent * component,EClient * client)1557 ece_sentby_is_user (ECompEditor *comp_editor,
1558 		    ICalComponent *component,
1559 		    EClient *client)
1560 {
1561 	ICalProperty *prop;
1562 	ICalParameter *param;
1563 	const gchar *sentby;
1564 	gboolean res;
1565 
1566 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
1567 	g_return_val_if_fail (I_CAL_IS_COMPONENT (component), FALSE);
1568 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
1569 
1570 	prop = i_cal_component_get_first_property (component, I_CAL_ORGANIZER_PROPERTY);
1571 	if (!prop || e_client_check_capability (client, E_CAL_STATIC_CAPABILITY_NO_ORGANIZER)) {
1572 		g_clear_object (&prop);
1573 		return FALSE;
1574 	}
1575 
1576 	param = i_cal_property_get_first_parameter (prop, I_CAL_SENTBY_PARAMETER);
1577 	if (!param) {
1578 		g_clear_object (&prop);
1579 		return FALSE;
1580 	}
1581 
1582 	sentby = i_cal_parameter_get_sentby (param);
1583 
1584 	res = ece_organizer_email_address_is_user (comp_editor, client, sentby, FALSE);
1585 
1586 	g_clear_object (&param);
1587 	g_clear_object (&prop);
1588 
1589 	return res;
1590 }
1591 
1592 static void
ece_emit_times_changed_cb(ECompEditor * comp_editor)1593 ece_emit_times_changed_cb (ECompEditor *comp_editor)
1594 {
1595 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1596 
1597 	g_signal_emit (comp_editor, signals[TIMES_CHANGED], 0, NULL);
1598 }
1599 
1600 static void
ece_connect_time_parts(ECompEditor * comp_editor,ECompEditorPropertyPart * dtstart_part,ECompEditorPropertyPart * dtend_part)1601 ece_connect_time_parts (ECompEditor *comp_editor,
1602 			ECompEditorPropertyPart *dtstart_part,
1603 			ECompEditorPropertyPart *dtend_part)
1604 {
1605 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1606 
1607 #define update_part(x) G_STMT_START { \
1608 	if (x) \
1609 		g_object_ref (x); \
1610 	if (comp_editor->priv->x) { \
1611 		g_signal_handlers_disconnect_by_func (comp_editor->priv->x, G_CALLBACK (ece_emit_times_changed_cb), comp_editor); \
1612 		g_clear_object (&comp_editor->priv->x); \
1613 	} \
1614 	if (x) { \
1615 		comp_editor->priv->x = x; \
1616 		g_signal_connect_swapped (comp_editor->priv->x, "changed", \
1617 			G_CALLBACK (ece_emit_times_changed_cb), comp_editor); \
1618 	} \
1619 	} G_STMT_END
1620 
1621 	update_part (dtstart_part);
1622 	update_part (dtend_part);
1623 
1624 #undef update_part
1625 }
1626 
1627 static void
ece_update_source_combo_box_by_flags(ECompEditor * comp_editor)1628 ece_update_source_combo_box_by_flags (ECompEditor *comp_editor)
1629 {
1630 	ECompEditorPage *page;
1631 
1632 	page = e_comp_editor_get_page (comp_editor, E_TYPE_COMP_EDITOR_PAGE_GENERAL);
1633 
1634 	if (page) {
1635 		GtkWidget *source_combo_box;
1636 
1637 		source_combo_box = e_comp_editor_page_general_get_source_combo_box (E_COMP_EDITOR_PAGE_GENERAL (page));
1638 
1639 		if (source_combo_box) {
1640 			if ((comp_editor->priv->flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0) {
1641 				e_source_combo_box_hide_sources (E_SOURCE_COMBO_BOX (source_combo_box),
1642 					"webcal-stub", "weather-stub", "contacts-stub",
1643 					"webcal", "weather", "contacts", "birthdays",
1644 					NULL);
1645 			} else {
1646 				e_source_combo_box_hide_sources (E_SOURCE_COMBO_BOX (source_combo_box), NULL);
1647 			}
1648 		}
1649 	}
1650 }
1651 
1652 static void
ece_sensitize_widgets(ECompEditor * comp_editor,gboolean force_insensitive)1653 ece_sensitize_widgets (ECompEditor *comp_editor,
1654 		       gboolean force_insensitive)
1655 {
1656 	GtkActionGroup *group;
1657 	GSList *link;
1658 
1659 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1660 
1661 	for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) {
1662 		ECompEditorPage *page = link->data;
1663 
1664 		g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page));
1665 		if (!E_IS_COMP_EDITOR_PAGE (page))
1666 			continue;
1667 
1668 		e_comp_editor_page_sensitize_widgets (page, force_insensitive);
1669 	}
1670 
1671 	group = e_comp_editor_get_action_group (comp_editor, "individual");
1672 	gtk_action_group_set_sensitive (group, !force_insensitive);
1673 
1674 	group = e_comp_editor_get_action_group (comp_editor, "editable");
1675 	gtk_action_group_set_sensitive (group, !force_insensitive);
1676 }
1677 
1678 static void
ece_fill_widgets(ECompEditor * comp_editor,ICalComponent * component)1679 ece_fill_widgets (ECompEditor *comp_editor,
1680 		  ICalComponent *component)
1681 {
1682 	GSList *link;
1683 
1684 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1685 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
1686 
1687 	for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) {
1688 		ECompEditorPage *page = link->data;
1689 
1690 		g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page));
1691 		if (!E_IS_COMP_EDITOR_PAGE (page))
1692 			continue;
1693 
1694 		e_comp_editor_page_fill_widgets (page, component);
1695 	}
1696 }
1697 
1698 static gboolean
ece_fill_component(ECompEditor * comp_editor,ICalComponent * component)1699 ece_fill_component (ECompEditor *comp_editor,
1700 		    ICalComponent *component)
1701 {
1702 	GSList *link;
1703 
1704 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
1705 	g_return_val_if_fail (I_CAL_IS_COMPONENT (component), FALSE);
1706 
1707 	for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) {
1708 		ECompEditorPage *page = link->data;
1709 
1710 		g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page));
1711 		if (!E_IS_COMP_EDITOR_PAGE (page) ||
1712 		    !gtk_widget_get_visible (GTK_WIDGET (page)))
1713 			continue;
1714 
1715 		if (!e_comp_editor_page_fill_component (page, component))
1716 			return FALSE;
1717 	}
1718 
1719 	return TRUE;
1720 }
1721 
1722 static void
comp_editor_realize_cb(ECompEditor * comp_editor)1723 comp_editor_realize_cb (ECompEditor *comp_editor)
1724 {
1725 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1726 
1727 	if (comp_editor->priv->component) {
1728 		e_comp_editor_fill_widgets (comp_editor, comp_editor->priv->component);
1729 		e_comp_editor_set_changed (comp_editor, FALSE);
1730 	}
1731 
1732 	e_comp_editor_update_window_title (comp_editor);
1733 	e_comp_editor_sensitize_widgets (comp_editor);
1734 	ece_update_source_combo_box_by_flags (comp_editor);
1735 
1736 	if (comp_editor->priv->page_general && comp_editor->priv->origin_source) {
1737 		e_comp_editor_page_general_set_selected_source (
1738 			comp_editor->priv->page_general,
1739 			comp_editor->priv->origin_source);
1740 		e_comp_editor_set_changed (comp_editor, FALSE);
1741 	}
1742 
1743 	if (comp_editor->priv->page_general) {
1744 		e_comp_editor_page_general_update_view (comp_editor->priv->page_general);
1745 
1746 		if (!comp_editor->priv->show_attendees_handler_id) {
1747 			comp_editor->priv->show_attendees_handler_id =
1748 				e_signal_connect_notify_swapped (comp_editor->priv->page_general,
1749 					"notify::show-attendees",
1750 					G_CALLBACK (e_comp_editor_update_window_title), comp_editor);
1751 		}
1752 	}
1753 
1754 	if (!comp_editor->priv->target_client)
1755 		e_comp_editor_open_target_client (comp_editor);
1756 }
1757 
1758 static void
comp_editor_unrealize_cb(ECompEditor * comp_editor)1759 comp_editor_unrealize_cb (ECompEditor *comp_editor)
1760 {
1761 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1762 
1763 	if (comp_editor->priv->page_general) {
1764 		e_signal_disconnect_notify_handler (comp_editor->priv->page_general,
1765 			&comp_editor->priv->show_attendees_handler_id);
1766 	}
1767 }
1768 
1769 static gboolean
comp_editor_delete_event(GtkWidget * widget,GdkEventAny * event)1770 comp_editor_delete_event (GtkWidget *widget,
1771 			  GdkEventAny *event)
1772 {
1773 	ECompEditor *comp_editor;
1774 
1775 	g_return_val_if_fail (E_IS_COMP_EDITOR (widget), FALSE);
1776 
1777 	comp_editor = E_COMP_EDITOR (widget);
1778 
1779 	/* It's disabled when the component is being saved */
1780 	if (gtk_widget_get_sensitive (GTK_WIDGET (comp_editor->priv->content)))
1781 		action_close_cb (NULL, comp_editor);
1782 
1783 	return TRUE;
1784 }
1785 
1786 static gboolean
comp_editor_key_press_event(GtkWidget * widget,GdkEventKey * event)1787 comp_editor_key_press_event (GtkWidget *widget,
1788 			     GdkEventKey *event)
1789 {
1790 	ECompEditor *comp_editor;
1791 
1792 	g_return_val_if_fail (E_IS_COMP_EDITOR (widget), FALSE);
1793 
1794 	comp_editor = E_COMP_EDITOR (widget);
1795 
1796 	if (event->keyval == GDK_KEY_Escape &&
1797 	    !e_alert_bar_close_alert (comp_editor->priv->alert_bar)) {
1798 		GtkAction *action;
1799 
1800 		action = e_comp_editor_get_action (comp_editor, "close");
1801 		gtk_action_activate (action);
1802 
1803 		return TRUE;
1804 	}
1805 
1806 	/* Chain up to parent's method. */
1807 	return GTK_WIDGET_CLASS (e_comp_editor_parent_class)->key_press_event (widget, event);
1808 }
1809 
1810 static void
comp_editor_selected_source_notify_cb(ECompEditorPageGeneral * page_general,GParamSpec * param,ECompEditor * comp_editor)1811 comp_editor_selected_source_notify_cb (ECompEditorPageGeneral *page_general,
1812 				       GParamSpec *param,
1813 				       ECompEditor *comp_editor)
1814 {
1815 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1816 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1817 	g_return_if_fail (comp_editor->priv->page_general == page_general);
1818 
1819 	e_comp_editor_open_target_client (comp_editor);
1820 }
1821 
1822 static gboolean
e_comp_editor_focus_in_event_cb(GtkWindow * comp_editor,GdkEvent * event,gpointer user_data)1823 e_comp_editor_focus_in_event_cb (GtkWindow *comp_editor,
1824 				 GdkEvent *event,
1825 				 gpointer user_data)
1826 {
1827 	gtk_window_set_urgency_hint (comp_editor, FALSE);
1828 
1829 	g_signal_handlers_disconnect_by_func (comp_editor,
1830 		G_CALLBACK (e_comp_editor_focus_in_event_cb), NULL);
1831 
1832 	return FALSE;
1833 }
1834 
1835 static void
e_comp_editor_set_urgency_hint(ECompEditor * comp_editor)1836 e_comp_editor_set_urgency_hint (ECompEditor *comp_editor)
1837 {
1838 	GtkWindow *window;
1839 
1840 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1841 
1842 	window = GTK_WINDOW (comp_editor);
1843 
1844 	if (gtk_widget_get_visible (GTK_WIDGET (window)) &&
1845 	    !gtk_window_is_active (window) &&
1846 	    !gtk_window_get_urgency_hint (window)) {
1847 		gtk_window_set_urgency_hint (window, TRUE);
1848 
1849 		g_signal_connect (
1850 			window, "focus-in-event",
1851 			G_CALLBACK (e_comp_editor_focus_in_event_cb), NULL);
1852 	}
1853 }
1854 
1855 static void
e_comp_editor_submit_alert(EAlertSink * alert_sink,EAlert * alert)1856 e_comp_editor_submit_alert (EAlertSink *alert_sink,
1857 			    EAlert *alert)
1858 {
1859 	ECompEditor *comp_editor;
1860 
1861 	g_return_if_fail (E_IS_COMP_EDITOR (alert_sink));
1862 	g_return_if_fail (E_IS_ALERT (alert));
1863 
1864 	comp_editor = E_COMP_EDITOR (alert_sink);
1865 
1866 	e_alert_bar_submit_alert (comp_editor->priv->alert_bar, alert);
1867 
1868 	e_comp_editor_set_urgency_hint (comp_editor);
1869 }
1870 
1871 static void
e_comp_editor_set_origin_source(ECompEditor * comp_editor,ESource * origin_source)1872 e_comp_editor_set_origin_source (ECompEditor *comp_editor,
1873 				 ESource *origin_source)
1874 {
1875 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1876 	if (origin_source)
1877 		g_return_if_fail (E_IS_SOURCE (origin_source));
1878 
1879 	g_clear_object (&comp_editor->priv->origin_source);
1880 	if (origin_source)
1881 		comp_editor->priv->origin_source = g_object_ref (origin_source);
1882 }
1883 
1884 static void
e_comp_editor_set_shell(ECompEditor * comp_editor,EShell * shell)1885 e_comp_editor_set_shell (ECompEditor *comp_editor,
1886 			 EShell *shell)
1887 {
1888 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1889 	g_return_if_fail (E_IS_SHELL (shell));
1890 
1891 	g_clear_object (&comp_editor->priv->shell);
1892 	comp_editor->priv->shell = g_object_ref (shell);
1893 }
1894 
1895 static void
e_comp_editor_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1896 e_comp_editor_set_property (GObject *object,
1897 			    guint property_id,
1898 			    const GValue *value,
1899 			    GParamSpec *pspec)
1900 {
1901 	switch (property_id) {
1902 		case PROP_ALARM_EMAIL_ADDRESS:
1903 			e_comp_editor_set_alarm_email_address (
1904 				E_COMP_EDITOR (object),
1905 				g_value_get_string (value));
1906 			return;
1907 
1908 		case PROP_CAL_EMAIL_ADDRESS:
1909 			e_comp_editor_set_cal_email_address (
1910 				E_COMP_EDITOR (object),
1911 				g_value_get_string (value));
1912 			return;
1913 
1914 		case PROP_CHANGED:
1915 			e_comp_editor_set_changed (
1916 				E_COMP_EDITOR (object),
1917 				g_value_get_boolean (value));
1918 			return;
1919 
1920 		case PROP_COMPONENT:
1921 			e_comp_editor_set_component (
1922 				E_COMP_EDITOR (object),
1923 				g_value_get_object (value));
1924 			return;
1925 
1926 		case PROP_FLAGS:
1927 			e_comp_editor_set_flags (
1928 				E_COMP_EDITOR (object),
1929 				g_value_get_uint (value));
1930 			return;
1931 
1932 		case PROP_ORIGIN_SOURCE:
1933 			e_comp_editor_set_origin_source (
1934 				E_COMP_EDITOR (object),
1935 				g_value_get_object (value));
1936 			return;
1937 
1938 		case PROP_SHELL:
1939 			e_comp_editor_set_shell (
1940 				E_COMP_EDITOR (object),
1941 				g_value_get_object (value));
1942 			return;
1943 
1944 		case PROP_SOURCE_CLIENT:
1945 			e_comp_editor_set_source_client (
1946 				E_COMP_EDITOR (object),
1947 				g_value_get_object (value));
1948 			return;
1949 
1950 		case PROP_TARGET_CLIENT:
1951 			e_comp_editor_set_target_client (
1952 				E_COMP_EDITOR (object),
1953 				g_value_get_object (value));
1954 			return;
1955 
1956 		case PROP_TITLE_SUFFIX:
1957 			e_comp_editor_set_title_suffix (
1958 				E_COMP_EDITOR (object),
1959 				g_value_get_string (value));
1960 			return;
1961 	}
1962 
1963 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1964 }
1965 
1966 static void
e_comp_editor_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1967 e_comp_editor_get_property (GObject *object,
1968 			    guint property_id,
1969 			    GValue *value,
1970 			    GParamSpec *pspec)
1971 {
1972 	switch (property_id) {
1973 		case PROP_ALARM_EMAIL_ADDRESS:
1974 			g_value_set_string (
1975 				value,
1976 				e_comp_editor_get_alarm_email_address (
1977 				E_COMP_EDITOR (object)));
1978 			return;
1979 
1980 		case PROP_CAL_EMAIL_ADDRESS:
1981 			g_value_set_string (
1982 				value,
1983 				e_comp_editor_get_cal_email_address (
1984 				E_COMP_EDITOR (object)));
1985 			return;
1986 
1987 		case PROP_CHANGED:
1988 			g_value_set_boolean (
1989 				value,
1990 				e_comp_editor_get_changed (
1991 				E_COMP_EDITOR (object)));
1992 			return;
1993 
1994 		case PROP_COMPONENT:
1995 			g_value_set_object (
1996 				value,
1997 				e_comp_editor_get_component (
1998 				E_COMP_EDITOR (object)));
1999 			return;
2000 
2001 		case PROP_FLAGS:
2002 			g_value_set_uint (
2003 				value,
2004 				e_comp_editor_get_flags (
2005 				E_COMP_EDITOR (object)));
2006 			return;
2007 
2008 		case PROP_ORIGIN_SOURCE:
2009 			g_value_set_object (
2010 				value,
2011 				e_comp_editor_get_origin_source (
2012 				E_COMP_EDITOR (object)));
2013 			return;
2014 
2015 		case PROP_SHELL:
2016 			g_value_set_object (
2017 				value,
2018 				e_comp_editor_get_shell (
2019 				E_COMP_EDITOR (object)));
2020 			return;
2021 
2022 		case PROP_SOURCE_CLIENT:
2023 			g_value_set_object (
2024 				value,
2025 				e_comp_editor_get_source_client (
2026 				E_COMP_EDITOR (object)));
2027 			return;
2028 
2029 		case PROP_TARGET_CLIENT:
2030 			g_value_set_object (
2031 				value,
2032 				e_comp_editor_get_target_client (
2033 				E_COMP_EDITOR (object)));
2034 			return;
2035 
2036 		case PROP_TITLE_SUFFIX:
2037 			g_value_set_string (
2038 				value,
2039 				e_comp_editor_get_title_suffix (
2040 				E_COMP_EDITOR (object)));
2041 			return;
2042 	}
2043 
2044 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2045 }
2046 
2047 static void
e_comp_editor_constructed(GObject * object)2048 e_comp_editor_constructed (GObject *object)
2049 {
2050 	const gchar *ui =
2051 		"<ui>"
2052 		"  <menubar action='main-menu'>"
2053 		"    <menu action='file-menu'>"
2054 		"      <menuitem action='save'/>"
2055 		"      <menuitem action='save-and-close'/>"
2056 		"      <separator/>"
2057 		"      <placeholder name='custom-actions-placeholder'/>"
2058 		"      <separator/>"
2059 		"      <menuitem action='print-preview'/>"
2060 		"      <menuitem action='print'/>"
2061 		"      <separator/>"
2062 		"      <menuitem action='close'/>"
2063 		"    </menu>"
2064 		"    <menu action='edit-menu'>"
2065 		"      <menuitem action='undo'/>"
2066 		"      <menuitem action='redo'/>"
2067 		"      <separator/>"
2068 		"      <menuitem action='cut-clipboard'/>"
2069 		"      <menuitem action='copy-clipboard'/>"
2070 		"      <menuitem action='paste-clipboard'/>"
2071 		"      <menuitem action='delete-selection'/>"
2072 		"      <separator/>"
2073 		"      <menuitem action='select-all'/>"
2074 		"    </menu>"
2075 		"    <menu action='view-menu'>"
2076 		"      <placeholder name='parts'/>"
2077 		"      <separator />"
2078 		"      <placeholder name='columns'/>"
2079 		"    </menu>"
2080 		"    <menu action='insert-menu'/>"
2081 		"    <menu action='options-menu'>"
2082 		"      <placeholder name='tabs'/>"
2083 		"      <placeholder name='toggles'/>"
2084 		"    </menu>"
2085 		"    <menu action='help-menu'>"
2086 		"      <menuitem action='help'/>"
2087 		"    </menu>"
2088 		"  </menubar>"
2089 		"  <toolbar name='main-toolbar'>"
2090 		"    <toolitem action='save-and-close'/>\n"
2091 		"    <toolitem action='save'/>"
2092 		"    <toolitem action='print'/>"
2093 		"    <separator/>"
2094 		"    <toolitem action='undo'/>"
2095 		"    <toolitem action='redo'/>"
2096 		"    <separator/>"
2097 		"    <placeholder name='content'/>"
2098 		"    <placeholder name='after-content'/>"
2099 		"  </toolbar>"
2100 		"</ui>";
2101 
2102 	GtkActionEntry core_entries[] = {
2103 
2104 		{ "close",
2105 		  "window-close",
2106 		  N_("_Close"),
2107 		  "<Control>w",
2108 		  N_("Close the current window"),
2109 		  G_CALLBACK (action_close_cb) },
2110 
2111 		{ "copy-clipboard",
2112 		  "edit-copy",
2113 		  N_("_Copy"),
2114 		  "<Control>c",
2115 		  N_("Copy the selection"),
2116 		  NULL },  /* Handled by EFocusTracker */
2117 
2118 		{ "cut-clipboard",
2119 		  "edit-cut",
2120 		  N_("Cu_t"),
2121 		  "<Control>x",
2122 		  N_("Cut the selection"),
2123 		  NULL },  /* Handled by EFocusTracker */
2124 
2125 		{ "delete-selection",
2126 		  "edit-delete",
2127 		  N_("_Delete"),
2128 		  NULL,
2129 		  N_("Delete the selection"),
2130 		  NULL },  /* Handled by EFocusTracker */
2131 
2132 		{ "help",
2133 		  "help-browser",
2134 		  N_("_Help"),
2135 		  "F1",
2136 		  N_("View help"),
2137 		  G_CALLBACK (action_help_cb) },
2138 
2139 		{ "paste-clipboard",
2140 		  "edit-paste",
2141 		  N_("_Paste"),
2142 		  "<Control>v",
2143 		  N_("Paste the clipboard"),
2144 		  NULL },  /* Handled by EFocusTracker */
2145 
2146 		{ "print",
2147 		  "document-print",
2148 		  N_("_Print…"),
2149 		  "<Control>p",
2150 		  NULL,
2151 		  G_CALLBACK (action_print_cb) },
2152 
2153 		{ "print-preview",
2154 		  "document-print-preview",
2155 		  N_("Pre_view…"),
2156 		  NULL,
2157 		  NULL,
2158 		  G_CALLBACK (action_print_preview_cb) },
2159 
2160 		{ "select-all",
2161 		  "edit-select-all",
2162 		  N_("Select _All"),
2163 		  "<Control>a",
2164 		  N_("Select all text"),
2165 		  NULL },  /* Handled by EFocusTracker */
2166 
2167 		{ "undo",
2168 		  "edit-undo",
2169 		  N_("_Undo"),
2170 		  "<Control>z",
2171 		  N_("Undo"),
2172 		  NULL },  /* Handled by EFocusTracker */
2173 
2174 		{ "redo",
2175 		  "edit-redo",
2176 		  N_("_Redo"),
2177 		  "<Control>y",
2178 		  N_("Redo"),
2179 		  NULL },  /* Handled by EFocusTracker */
2180 
2181 		/* Menus */
2182 
2183 		{ "classification-menu",
2184 		  NULL,
2185 		  N_("_Classification"),
2186 		  NULL,
2187 		  NULL,
2188 		  NULL },
2189 
2190 		{ "edit-menu",
2191 		  NULL,
2192 		  N_("_Edit"),
2193 		  NULL,
2194 		  NULL,
2195 		  NULL },
2196 
2197 		{ "file-menu",
2198 		  NULL,
2199 		  N_("_File"),
2200 		  NULL,
2201 		  NULL,
2202 		  NULL },
2203 
2204 		{ "help-menu",
2205 		  NULL,
2206 		  N_("_Help"),
2207 		  NULL,
2208 		  NULL,
2209 		  NULL },
2210 
2211 		{ "insert-menu",
2212 		  NULL,
2213 		  N_("_Insert"),
2214 		  NULL,
2215 		  NULL,
2216 		  NULL },
2217 
2218 		{ "options-menu",
2219 		  NULL,
2220 		  N_("_Options"),
2221 		  NULL,
2222 		  NULL,
2223 		  NULL },
2224 
2225 		{ "view-menu",
2226 		  NULL,
2227 		  N_("_View"),
2228 		  NULL,
2229 		  NULL,
2230 		  NULL }
2231 	};
2232 
2233 	GtkActionEntry editable_entries[] = {
2234 
2235 		{ "save",
2236 		  "document-save",
2237 		  N_("_Save"),
2238 		  "<Control>s",
2239 		  N_("Save current changes"),
2240 		  G_CALLBACK (action_save_cb) },
2241 
2242 		{ "save-and-close",
2243 		  NULL,
2244 		  N_("Save and Close"),
2245 		  "<Control>Return",
2246 		  N_("Save current changes and close editor"),
2247 		  G_CALLBACK (action_save_and_close_cb) }
2248 	};
2249 
2250 	ECompEditor *comp_editor = E_COMP_EDITOR (object);
2251 	GtkWidget *widget;
2252 	GtkBox *vbox;
2253 	GtkAction *action;
2254 	GtkActionGroup *action_group;
2255 	EFocusTracker *focus_tracker;
2256 	GError *error = NULL;
2257 
2258 	G_OBJECT_CLASS (e_comp_editor_parent_class)->constructed (object);
2259 
2260 	g_signal_connect (comp_editor, "key-press-event",
2261 		G_CALLBACK (e_util_check_gtk_bindings_in_key_press_event_cb), NULL);
2262 
2263 	comp_editor->priv->calendar_settings = e_util_ref_settings ("org.gnome.evolution.calendar");
2264 	comp_editor->priv->ui_manager = gtk_ui_manager_new ();
2265 
2266 	gtk_window_add_accel_group (
2267 		GTK_WINDOW (comp_editor),
2268 		gtk_ui_manager_get_accel_group (comp_editor->priv->ui_manager));
2269 
2270 	/* Setup Action Groups */
2271 
2272 	action_group = gtk_action_group_new ("individual");
2273 	gtk_action_group_set_translation_domain (
2274 		action_group, GETTEXT_PACKAGE);
2275 	gtk_ui_manager_insert_action_group (
2276 		comp_editor->priv->ui_manager, action_group, 0);
2277 	g_object_unref (action_group);
2278 
2279 	action_group = gtk_action_group_new ("core");
2280 	gtk_action_group_set_translation_domain (
2281 		action_group, GETTEXT_PACKAGE);
2282 	gtk_action_group_add_actions (
2283 		action_group, core_entries,
2284 		G_N_ELEMENTS (core_entries), comp_editor);
2285 	gtk_ui_manager_insert_action_group (
2286 		comp_editor->priv->ui_manager, action_group, 0);
2287 	g_object_unref (action_group);
2288 
2289 	action_group = gtk_action_group_new ("editable");
2290 	gtk_action_group_set_translation_domain (
2291 		action_group, GETTEXT_PACKAGE);
2292 	gtk_action_group_add_actions (
2293 		action_group, editable_entries,
2294 		G_N_ELEMENTS (editable_entries), comp_editor);
2295 	gtk_ui_manager_insert_action_group (
2296 		comp_editor->priv->ui_manager, action_group, 0);
2297 	g_object_unref (action_group);
2298 
2299 	action = gtk_action_group_get_action (action_group, "save-and-close");
2300 	if (action) {
2301 		GtkAction *save_action;
2302 		GIcon *icon;
2303 		GIcon *emblemed_icon;
2304 		GEmblem *emblem;
2305 
2306 		icon = g_themed_icon_new ("window-close");
2307 		emblemed_icon = g_themed_icon_new ("document-save");
2308 		emblem = g_emblem_new (emblemed_icon);
2309 		g_object_unref (emblemed_icon);
2310 
2311 		emblemed_icon = g_emblemed_icon_new (icon, emblem);
2312 		g_object_unref (emblem);
2313 		g_object_unref (icon);
2314 
2315 		gtk_action_set_gicon (action, emblemed_icon);
2316 
2317 		g_object_unref (emblemed_icon);
2318 
2319 		save_action = gtk_action_group_get_action (action_group, "save");
2320 		e_binding_bind_property (
2321 			save_action, "sensitive",
2322 			action, "sensitive",
2323 			G_BINDING_SYNC_CREATE);
2324 	}
2325 
2326 	gtk_ui_manager_add_ui_from_string (comp_editor->priv->ui_manager, ui, -1, &error);
2327 	if (error != NULL) {
2328 		g_warning ("%s: %s", G_STRFUNC, error->message);
2329 		g_error_free (error);
2330 	}
2331 
2332 	widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2333 	g_object_set (G_OBJECT (widget),
2334 		"hexpand", TRUE,
2335 		"halign", GTK_ALIGN_FILL,
2336 		"vexpand", TRUE,
2337 		"valign", GTK_ALIGN_FILL,
2338 		NULL);
2339 	gtk_widget_show (widget);
2340 
2341 	vbox = GTK_BOX (widget);
2342 
2343 	gtk_container_add (GTK_CONTAINER (comp_editor), widget);
2344 
2345 	widget = e_comp_editor_get_managed_widget (comp_editor, "/main-menu");
2346 	gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
2347 	gtk_widget_set_visible (widget, TRUE);
2348 
2349 	widget = e_comp_editor_get_managed_widget (comp_editor, "/main-toolbar");
2350 	gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
2351 	gtk_widget_show (widget);
2352 
2353 	gtk_style_context_add_class (
2354 		gtk_widget_get_style_context (widget),
2355 		GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
2356 
2357 	widget = e_alert_bar_new ();
2358 	g_object_set (G_OBJECT (widget),
2359 		"hexpand", FALSE,
2360 		"halign", GTK_ALIGN_FILL,
2361 		"vexpand", FALSE,
2362 		"valign", GTK_ALIGN_START,
2363 		NULL);
2364 
2365 	comp_editor->priv->alert_bar = E_ALERT_BAR (widget);
2366 
2367 	gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
2368 
2369 	widget = e_activity_bar_new ();
2370 	g_object_set (G_OBJECT (widget),
2371 		"hexpand", FALSE,
2372 		"halign", GTK_ALIGN_FILL,
2373 		"vexpand", FALSE,
2374 		"valign", GTK_ALIGN_START,
2375 		NULL);
2376 
2377 	comp_editor->priv->activity_bar = E_ACTIVITY_BAR (widget);
2378 
2379 	gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
2380 
2381 	widget = gtk_notebook_new ();
2382 	g_object_set (G_OBJECT (widget),
2383 		"hexpand", TRUE,
2384 		"halign", GTK_ALIGN_FILL,
2385 		"vexpand", TRUE,
2386 		"valign", GTK_ALIGN_FILL,
2387 		"show-tabs", TRUE,
2388 		"show-border", FALSE,
2389 		NULL);
2390 	gtk_widget_show (widget);
2391 
2392 	comp_editor->priv->content = GTK_NOTEBOOK (widget);
2393 
2394 	gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0);
2395 
2396 	/* Configure an EFocusTracker to manage selection actions. */
2397 
2398 	focus_tracker = e_focus_tracker_new (GTK_WINDOW (comp_editor));
2399 
2400 	action = e_comp_editor_get_action (comp_editor, "cut-clipboard");
2401 	e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
2402 
2403 	action = e_comp_editor_get_action (comp_editor, "copy-clipboard");
2404 	e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
2405 
2406 	action = e_comp_editor_get_action (comp_editor, "paste-clipboard");
2407 	e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
2408 
2409 	action = e_comp_editor_get_action (comp_editor, "delete-selection");
2410 	e_focus_tracker_set_delete_selection_action (focus_tracker, action);
2411 
2412 	action = e_comp_editor_get_action (comp_editor, "select-all");
2413 	e_focus_tracker_set_select_all_action (focus_tracker, action);
2414 
2415 	action = e_comp_editor_get_action (comp_editor, "undo");
2416 	e_focus_tracker_set_undo_action (focus_tracker, action);
2417 
2418 	action = e_comp_editor_get_action (comp_editor, "redo");
2419 	e_focus_tracker_set_redo_action (focus_tracker, action);
2420 
2421 	comp_editor->priv->focus_tracker = focus_tracker;
2422 
2423 	/* Desensitize the "save" action. */
2424 	action = e_comp_editor_get_action (comp_editor, "save");
2425 	gtk_action_set_sensitive (action, FALSE);
2426 
2427 	e_binding_bind_property (comp_editor, "changed", action, "sensitive", 0);
2428 
2429 	g_signal_connect (comp_editor, "realize", G_CALLBACK (comp_editor_realize_cb), NULL);
2430 	g_signal_connect (comp_editor, "unrealize", G_CALLBACK (comp_editor_unrealize_cb), NULL);
2431 
2432 	gtk_application_add_window (GTK_APPLICATION (comp_editor->priv->shell), GTK_WINDOW (comp_editor));
2433 
2434 	e_extensible_load_extensions (E_EXTENSIBLE (comp_editor));
2435 }
2436 
2437 static void
e_comp_editor_dispose(GObject * object)2438 e_comp_editor_dispose (GObject *object)
2439 {
2440 	ECompEditor *comp_editor = E_COMP_EDITOR (object);
2441 
2442 	if (comp_editor->priv->page_general) {
2443 		g_signal_handlers_disconnect_by_func (comp_editor->priv->page_general,
2444 			G_CALLBACK (comp_editor_selected_source_notify_cb), comp_editor);
2445 		comp_editor->priv->page_general = NULL;
2446 	}
2447 
2448 	if (comp_editor->priv->target_client_opening) {
2449 		e_activity_cancel (comp_editor->priv->target_client_opening);
2450 		g_clear_object (&comp_editor->priv->target_client_opening);
2451 	}
2452 
2453 	g_slist_free_full (comp_editor->priv->pages, g_object_unref);
2454 	comp_editor->priv->pages = NULL;
2455 
2456 	g_free (comp_editor->priv->alarm_email_address);
2457 	comp_editor->priv->alarm_email_address = NULL;
2458 
2459 	g_free (comp_editor->priv->cal_email_address);
2460 	comp_editor->priv->cal_email_address = NULL;
2461 
2462 	g_free (comp_editor->priv->title_suffix);
2463 	comp_editor->priv->title_suffix = NULL;
2464 
2465 	g_clear_object (&comp_editor->priv->component);
2466 
2467 	e_comp_editor_disconnect_target_backend_property_change_handler (comp_editor);
2468 	ece_connect_time_parts (comp_editor, NULL, NULL);
2469 
2470 	g_clear_object (&comp_editor->priv->origin_source);
2471 	g_clear_object (&comp_editor->priv->shell);
2472 	g_clear_object (&comp_editor->priv->focus_tracker);
2473 	g_clear_object (&comp_editor->priv->ui_manager);
2474 	g_clear_object (&comp_editor->priv->source_client);
2475 	g_clear_object (&comp_editor->priv->target_client);
2476 	g_clear_object (&comp_editor->priv->calendar_settings);
2477 	g_clear_object (&comp_editor->priv->validation_alert);
2478 
2479 	comp_editor->priv->activity_bar = NULL;
2480 
2481 	opened_editors = g_slist_remove (opened_editors, comp_editor);
2482 
2483 	G_OBJECT_CLASS (e_comp_editor_parent_class)->dispose (object);
2484 }
2485 
2486 static void
e_comp_editor_init(ECompEditor * comp_editor)2487 e_comp_editor_init (ECompEditor *comp_editor)
2488 {
2489 	comp_editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (comp_editor, E_TYPE_COMP_EDITOR, ECompEditorPrivate);
2490 }
2491 
2492 static void
e_comp_editor_alert_sink_iface_init(EAlertSinkInterface * iface)2493 e_comp_editor_alert_sink_iface_init (EAlertSinkInterface *iface)
2494 {
2495 	iface->submit_alert = e_comp_editor_submit_alert;
2496 }
2497 
2498 static void
e_comp_editor_class_init(ECompEditorClass * klass)2499 e_comp_editor_class_init (ECompEditorClass *klass)
2500 {
2501 	GtkWidgetClass *widget_class;
2502 	GObjectClass *object_class;
2503 
2504 	g_type_class_add_private (klass, sizeof (ECompEditorPrivate));
2505 
2506 	klass->sensitize_widgets = ece_sensitize_widgets;
2507 	klass->fill_widgets = ece_fill_widgets;
2508 	klass->fill_component = ece_fill_component;
2509 
2510 	widget_class = GTK_WIDGET_CLASS (klass);
2511 	widget_class->delete_event = comp_editor_delete_event;
2512 	widget_class->key_press_event = comp_editor_key_press_event;
2513 
2514 	object_class = G_OBJECT_CLASS (klass);
2515 	object_class->set_property = e_comp_editor_set_property;
2516 	object_class->get_property = e_comp_editor_get_property;
2517 	object_class->constructed = e_comp_editor_constructed;
2518 	object_class->dispose = e_comp_editor_dispose;
2519 
2520 	g_object_class_install_property (
2521 		object_class,
2522 		PROP_ALARM_EMAIL_ADDRESS,
2523 		g_param_spec_string (
2524 			"alarm-email-address",
2525 			"Alarm Email Address",
2526 			"Target client's alarm email address",
2527 			NULL,
2528 			G_PARAM_READWRITE |
2529 			G_PARAM_STATIC_STRINGS));
2530 
2531 	g_object_class_install_property (
2532 		object_class,
2533 		PROP_CAL_EMAIL_ADDRESS,
2534 		g_param_spec_string (
2535 			"cal-email-address",
2536 			"Calendar Email Address",
2537 			"Target client's calendar email address",
2538 			NULL,
2539 			G_PARAM_READWRITE |
2540 			G_PARAM_STATIC_STRINGS));
2541 
2542 	g_object_class_install_property (
2543 		object_class,
2544 		PROP_CHANGED,
2545 		g_param_spec_boolean (
2546 			"changed",
2547 			"Changed",
2548 			"Whether the editor content changed",
2549 			FALSE,
2550 			G_PARAM_READWRITE |
2551 			G_PARAM_STATIC_STRINGS));
2552 
2553 	g_object_class_install_property (
2554 		object_class,
2555 		PROP_COMPONENT,
2556 		g_param_spec_object (
2557 			"component",
2558 			"Component",
2559 			"ICalComponent currently edited",
2560 			I_CAL_TYPE_COMPONENT,
2561 			G_PARAM_READWRITE |
2562 			G_PARAM_CONSTRUCT_ONLY |
2563 			G_PARAM_STATIC_STRINGS));
2564 
2565 	g_object_class_install_property (
2566 		object_class,
2567 		PROP_FLAGS,
2568 		g_param_spec_uint (
2569 			"flags",
2570 			"Flags",
2571 			"Editor flags",
2572 			0, G_MAXUINT, 0,
2573 			G_PARAM_READWRITE |
2574 			G_PARAM_CONSTRUCT_ONLY |
2575 			G_PARAM_STATIC_STRINGS));
2576 
2577 	g_object_class_install_property (
2578 		object_class,
2579 		PROP_ORIGIN_SOURCE,
2580 		g_param_spec_object (
2581 			"origin-source",
2582 			"Origin Source",
2583 			"ESource of an ECalClient the component is stored in",
2584 			E_TYPE_SOURCE,
2585 			G_PARAM_READWRITE |
2586 			G_PARAM_CONSTRUCT_ONLY |
2587 			G_PARAM_STATIC_STRINGS));
2588 
2589 	g_object_class_install_property (
2590 		object_class,
2591 		PROP_SHELL,
2592 		g_param_spec_object (
2593 			"shell",
2594 			"Shell",
2595 			"EShell",
2596 			E_TYPE_SHELL,
2597 			G_PARAM_READWRITE |
2598 			G_PARAM_CONSTRUCT_ONLY |
2599 			G_PARAM_STATIC_STRINGS));
2600 
2601 	g_object_class_install_property (
2602 		object_class,
2603 		PROP_SOURCE_CLIENT,
2604 		g_param_spec_object (
2605 			"source-client",
2606 			"Source Client",
2607 			"ECalClient, the source calendar for the component",
2608 			E_TYPE_CAL_CLIENT,
2609 			G_PARAM_READWRITE |
2610 			G_PARAM_STATIC_STRINGS));
2611 
2612 	g_object_class_install_property (
2613 		object_class,
2614 		PROP_TARGET_CLIENT,
2615 		g_param_spec_object (
2616 			"target-client",
2617 			"Target Client",
2618 			"ECalClient currently set as the target calendar for the component",
2619 			E_TYPE_CAL_CLIENT,
2620 			G_PARAM_READWRITE |
2621 			G_PARAM_STATIC_STRINGS));
2622 
2623 	g_object_class_install_property (
2624 		object_class,
2625 		PROP_TITLE_SUFFIX,
2626 		g_param_spec_string (
2627 			"title-suffix",
2628 			"Title Suffix",
2629 			"Window title suffix, usually summary of the component",
2630 			NULL,
2631 			G_PARAM_READWRITE |
2632 			G_PARAM_STATIC_STRINGS));
2633 
2634 	signals[TIMES_CHANGED] = g_signal_new (
2635 		"times-changed",
2636 		G_TYPE_FROM_CLASS (klass),
2637 		G_SIGNAL_RUN_FIRST,
2638 		G_STRUCT_OFFSET (ECompEditorClass, times_changed),
2639 		NULL, NULL, NULL,
2640 		G_TYPE_NONE, 0,
2641 		G_TYPE_NONE);
2642 
2643 	signals[OBJECT_CREATED] = g_signal_new (
2644 		"object-created",
2645 		G_TYPE_FROM_CLASS (klass),
2646 		G_SIGNAL_RUN_FIRST,
2647 		G_STRUCT_OFFSET (ECompEditorClass, object_created),
2648 		NULL, NULL, NULL,
2649 		G_TYPE_NONE, 0,
2650 		G_TYPE_NONE);
2651 
2652 	signals[EDITOR_CLOSED] = g_signal_new (
2653 		"editor-closed",
2654 		G_TYPE_FROM_CLASS (klass),
2655 		G_SIGNAL_RUN_LAST,
2656 		G_STRUCT_OFFSET (ECompEditorClass, editor_closed),
2657 		NULL, NULL,
2658 		g_cclosure_marshal_VOID__BOOLEAN,
2659 		G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
2660 }
2661 
2662 void
e_comp_editor_sensitize_widgets(ECompEditor * comp_editor)2663 e_comp_editor_sensitize_widgets (ECompEditor *comp_editor)
2664 {
2665 	ECompEditorClass *comp_editor_class;
2666 	gboolean force_insensitive;
2667 	GtkWidget *current_focus;
2668 
2669 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
2670 
2671 	comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor);
2672 	g_return_if_fail (comp_editor_class != NULL);
2673 	g_return_if_fail (comp_editor_class->sensitize_widgets != NULL);
2674 
2675 	current_focus = gtk_window_get_focus (GTK_WINDOW (comp_editor));
2676 
2677 	force_insensitive = !comp_editor->priv->component;
2678 
2679 	if (!force_insensitive) {
2680 		ECalClient *target_client;
2681 
2682 		target_client = e_comp_editor_get_target_client (comp_editor);
2683 		if (target_client) {
2684 			EClient *client = E_CLIENT (target_client);
2685 
2686 			if (e_client_is_readonly (client)) {
2687 				force_insensitive = TRUE;
2688 			} else {
2689 				if (!e_cal_util_component_has_organizer (comp_editor->priv->component) ||
2690 				    ece_organizer_is_user (comp_editor, comp_editor->priv->component, client) ||
2691 				    ece_sentby_is_user (comp_editor, comp_editor->priv->component, client)) {
2692 					comp_editor->priv->flags = comp_editor->priv->flags | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
2693 				} else {
2694 					comp_editor->priv->flags = comp_editor->priv->flags & (~E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER);
2695 				}
2696 			}
2697 		} else {
2698 			force_insensitive = TRUE;
2699 		}
2700 	}
2701 
2702 	comp_editor_class->sensitize_widgets (comp_editor, force_insensitive);
2703 
2704 	if (force_insensitive)
2705 		comp_editor->priv->restore_focus = current_focus;
2706 	else
2707 		ece_restore_focus (comp_editor);
2708 }
2709 
2710 void
e_comp_editor_fill_widgets(ECompEditor * comp_editor,ICalComponent * component)2711 e_comp_editor_fill_widgets (ECompEditor *comp_editor,
2712 			    ICalComponent *component)
2713 {
2714 	ECompEditorClass *comp_editor_class;
2715 
2716 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
2717 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
2718 
2719 	comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor);
2720 	g_return_if_fail (comp_editor_class != NULL);
2721 	g_return_if_fail (comp_editor_class->fill_widgets != NULL);
2722 
2723 	e_comp_editor_set_updating (comp_editor, TRUE);
2724 
2725 	comp_editor_class->fill_widgets (comp_editor, component);
2726 
2727 	e_comp_editor_set_updating (comp_editor, FALSE);
2728 }
2729 
2730 gboolean
e_comp_editor_fill_component(ECompEditor * comp_editor,ICalComponent * component)2731 e_comp_editor_fill_component (ECompEditor *comp_editor,
2732 			      ICalComponent *component)
2733 {
2734 	ECompEditorClass *comp_editor_class;
2735 	GtkWidget *focused_widget;
2736 	gboolean is_valid;
2737 
2738 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
2739 	g_return_val_if_fail (I_CAL_IS_COMPONENT (component), FALSE);
2740 
2741 	comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor);
2742 	g_return_val_if_fail (comp_editor_class != NULL, FALSE);
2743 	g_return_val_if_fail (comp_editor_class->fill_component != NULL, FALSE);
2744 
2745 	focused_widget = gtk_window_get_focus (GTK_WINDOW (comp_editor));
2746 	if (focused_widget) {
2747 		GtkWidget *parent, *ce_widget = GTK_WIDGET (comp_editor);
2748 
2749 		/* When a cell-renderer is focused and editing the cell content,
2750 		   then unfocus it may mean to free the currently focused widget,
2751 		   thus get the GtkTreeView in such cases. */
2752 		parent = focused_widget;
2753 		while (parent = gtk_widget_get_parent (parent), parent && parent != ce_widget) {
2754 			if (GTK_IS_TREE_VIEW (parent)) {
2755 				focused_widget = parent;
2756 				break;
2757 			}
2758 		}
2759 
2760 		/* Save any pending changes */
2761 		gtk_window_set_focus (GTK_WINDOW (comp_editor), NULL);
2762 	}
2763 
2764 	is_valid = comp_editor_class->fill_component (comp_editor, component);
2765 
2766 	if (focused_widget) {
2767 		if (GTK_IS_ENTRY (focused_widget))
2768 			gtk_entry_grab_focus_without_selecting (GTK_ENTRY (focused_widget));
2769 		else
2770 			gtk_widget_grab_focus (focused_widget);
2771 	}
2772 
2773 	if (is_valid && comp_editor->priv->validation_alert) {
2774 		e_alert_response (comp_editor->priv->validation_alert, GTK_RESPONSE_CLOSE);
2775 		g_clear_object (&comp_editor->priv->validation_alert);
2776 	}
2777 
2778 	if (is_valid) {
2779 		ECalClient *target_client;
2780 		EClient *client = NULL;
2781 
2782 		target_client = e_comp_editor_get_target_client (comp_editor);
2783 		if (target_client)
2784 			client = E_CLIENT (target_client);
2785 
2786 		if (!e_cal_util_component_has_organizer (component) || (client && (
2787 		    ece_organizer_is_user (comp_editor, component, client) ||
2788 		    ece_sentby_is_user (comp_editor, component, client)))) {
2789 			gint sequence;
2790 
2791 			sequence = i_cal_component_get_sequence (component);
2792 			i_cal_component_set_sequence (component, sequence + 1);
2793 		}
2794 	}
2795 
2796 	return is_valid;
2797 }
2798 
2799 void
e_comp_editor_set_validation_error(ECompEditor * comp_editor,ECompEditorPage * error_page,GtkWidget * error_widget,const gchar * error_message)2800 e_comp_editor_set_validation_error (ECompEditor *comp_editor,
2801 				    ECompEditorPage *error_page,
2802 				    GtkWidget *error_widget,
2803 				    const gchar *error_message)
2804 {
2805 	EAlert *alert, *previous_alert;
2806 
2807 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
2808 	g_return_if_fail (error_message != NULL);
2809 
2810 	/* Ignore validation errors when the inner editor is currently updating. */
2811 	if (e_comp_editor_get_updating (comp_editor))
2812 		return;
2813 
2814 	alert = e_alert_new ("calendar:comp-editor-failed-validate", error_message, NULL);
2815 
2816 	e_alert_bar_add_alert (comp_editor->priv->alert_bar, alert);
2817 
2818 	previous_alert = comp_editor->priv->validation_alert;
2819 	comp_editor->priv->validation_alert = alert;
2820 
2821 	if (previous_alert) {
2822 		e_alert_response (previous_alert, GTK_RESPONSE_CLOSE);
2823 		g_clear_object (&previous_alert);
2824 	}
2825 
2826 	if (error_page)
2827 		e_comp_editor_select_page (comp_editor, error_page);
2828 
2829 	if (error_widget)
2830 		gtk_widget_grab_focus (error_widget);
2831 
2832 	e_comp_editor_set_urgency_hint (comp_editor);
2833 }
2834 
2835 EShell *
e_comp_editor_get_shell(ECompEditor * comp_editor)2836 e_comp_editor_get_shell (ECompEditor *comp_editor)
2837 {
2838 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2839 
2840 	return comp_editor->priv->shell;
2841 }
2842 
2843 GSettings *
e_comp_editor_get_settings(ECompEditor * comp_editor)2844 e_comp_editor_get_settings (ECompEditor *comp_editor)
2845 {
2846 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2847 
2848 	return comp_editor->priv->calendar_settings;
2849 }
2850 
2851 ESource *
e_comp_editor_get_origin_source(ECompEditor * comp_editor)2852 e_comp_editor_get_origin_source (ECompEditor *comp_editor)
2853 {
2854 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2855 
2856 	return comp_editor->priv->origin_source;
2857 }
2858 
2859 ICalComponent *
e_comp_editor_get_component(ECompEditor * comp_editor)2860 e_comp_editor_get_component (ECompEditor *comp_editor)
2861 {
2862 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2863 
2864 	return comp_editor->priv->component;
2865 }
2866 
2867 guint32
e_comp_editor_get_flags(ECompEditor * comp_editor)2868 e_comp_editor_get_flags (ECompEditor *comp_editor)
2869 {
2870 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), 0);
2871 
2872 	return comp_editor->priv->flags;
2873 }
2874 
2875 void
e_comp_editor_set_flags(ECompEditor * comp_editor,guint32 flags)2876 e_comp_editor_set_flags (ECompEditor *comp_editor,
2877 			 guint32 flags)
2878 {
2879 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
2880 
2881 	if (comp_editor->priv->flags == flags)
2882 		return;
2883 
2884 	comp_editor->priv->flags = flags;
2885 
2886 	ece_update_source_combo_box_by_flags (comp_editor);
2887 
2888 	g_object_notify (G_OBJECT (comp_editor), "flags");
2889 }
2890 
2891 EFocusTracker *
e_comp_editor_get_focus_tracker(ECompEditor * comp_editor)2892 e_comp_editor_get_focus_tracker (ECompEditor *comp_editor)
2893 {
2894 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2895 
2896 	return comp_editor->priv->focus_tracker;
2897 }
2898 
2899 GtkUIManager *
e_comp_editor_get_ui_manager(ECompEditor * comp_editor)2900 e_comp_editor_get_ui_manager (ECompEditor *comp_editor)
2901 {
2902 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2903 
2904 	return comp_editor->priv->ui_manager;
2905 }
2906 
2907 GtkAction *
e_comp_editor_get_action(ECompEditor * comp_editor,const gchar * action_name)2908 e_comp_editor_get_action (ECompEditor *comp_editor,
2909 			  const gchar *action_name)
2910 {
2911 	GtkUIManager *ui_manager;
2912 
2913 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2914 	g_return_val_if_fail (action_name != NULL, NULL);
2915 
2916 	ui_manager = e_comp_editor_get_ui_manager (comp_editor);
2917 
2918 	return e_lookup_action (ui_manager, action_name);
2919 }
2920 
2921 GtkActionGroup *
e_comp_editor_get_action_group(ECompEditor * comp_editor,const gchar * group_name)2922 e_comp_editor_get_action_group (ECompEditor *comp_editor,
2923 				const gchar *group_name)
2924 {
2925 	GtkUIManager *ui_manager;
2926 
2927 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2928 	g_return_val_if_fail (group_name != NULL, NULL);
2929 
2930 	ui_manager = e_comp_editor_get_ui_manager (comp_editor);
2931 
2932 	return e_lookup_action_group (ui_manager, group_name);
2933 }
2934 
2935 GtkWidget *
e_comp_editor_get_managed_widget(ECompEditor * comp_editor,const gchar * widget_path)2936 e_comp_editor_get_managed_widget (ECompEditor *comp_editor,
2937 				  const gchar *widget_path)
2938 {
2939 	GtkUIManager *ui_manager;
2940 	GtkWidget *widget;
2941 
2942 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2943 	g_return_val_if_fail (widget_path != NULL, NULL);
2944 
2945 	ui_manager = e_comp_editor_get_ui_manager (comp_editor);
2946 	widget = gtk_ui_manager_get_widget (ui_manager, widget_path);
2947 	g_return_val_if_fail (widget != NULL, NULL);
2948 
2949 	return widget;
2950 }
2951 
2952 static gchar *
e_comp_editor_extract_email_address(const gchar * email_address)2953 e_comp_editor_extract_email_address (const gchar *email_address)
2954 {
2955 	CamelInternetAddress *addr;
2956 	const gchar *str_addr;
2957 	gchar *address;
2958 
2959 	if (!email_address || !*email_address)
2960 		return NULL;
2961 
2962 	addr = camel_internet_address_new ();
2963 	if (camel_address_unformat (CAMEL_ADDRESS (addr), email_address) == 1 &&
2964 	    camel_internet_address_get (addr, 0, NULL, &str_addr)) {
2965 		address = g_strdup (str_addr);
2966 	} else {
2967 		address = g_strdup (email_address);
2968 	}
2969 	g_object_unref (addr);
2970 
2971 	return address;
2972 }
2973 
2974 const gchar *
e_comp_editor_get_alarm_email_address(ECompEditor * comp_editor)2975 e_comp_editor_get_alarm_email_address (ECompEditor *comp_editor)
2976 {
2977 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
2978 
2979 	return comp_editor->priv->alarm_email_address;
2980 }
2981 
2982 void
e_comp_editor_set_alarm_email_address(ECompEditor * comp_editor,const gchar * alarm_email_address)2983 e_comp_editor_set_alarm_email_address (ECompEditor *comp_editor,
2984 				       const gchar *alarm_email_address)
2985 {
2986 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
2987 
2988 	if (g_strcmp0 (alarm_email_address, comp_editor->priv->alarm_email_address) == 0)
2989 		return;
2990 
2991 	g_free (comp_editor->priv->alarm_email_address);
2992 	comp_editor->priv->alarm_email_address = e_comp_editor_extract_email_address (alarm_email_address);
2993 
2994 	g_object_notify (G_OBJECT (comp_editor), "alarm-email-address");
2995 }
2996 
2997 const gchar *
e_comp_editor_get_cal_email_address(ECompEditor * comp_editor)2998 e_comp_editor_get_cal_email_address (ECompEditor *comp_editor)
2999 {
3000 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
3001 
3002 	return comp_editor->priv->cal_email_address;
3003 }
3004 
3005 void
e_comp_editor_set_cal_email_address(ECompEditor * comp_editor,const gchar * cal_email_address)3006 e_comp_editor_set_cal_email_address (ECompEditor *comp_editor,
3007 				     const gchar *cal_email_address)
3008 {
3009 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3010 
3011 	if (g_strcmp0 (cal_email_address, comp_editor->priv->cal_email_address) == 0)
3012 		return;
3013 
3014 	g_free (comp_editor->priv->cal_email_address);
3015 	comp_editor->priv->cal_email_address = e_comp_editor_extract_email_address (cal_email_address);
3016 
3017 	g_object_notify (G_OBJECT (comp_editor), "cal-email-address");
3018 }
3019 
3020 gboolean
e_comp_editor_get_changed(ECompEditor * comp_editor)3021 e_comp_editor_get_changed (ECompEditor *comp_editor)
3022 {
3023 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
3024 
3025 	return comp_editor->priv->changed;
3026 }
3027 
3028 void
e_comp_editor_set_changed(ECompEditor * comp_editor,gboolean changed)3029 e_comp_editor_set_changed (ECompEditor *comp_editor,
3030 			   gboolean changed)
3031 {
3032 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3033 
3034 	if ((changed ? 1 : 0) == (comp_editor->priv->changed ? 1 : 0))
3035 		return;
3036 
3037 	comp_editor->priv->changed = changed;
3038 
3039 	g_object_notify (G_OBJECT (comp_editor), "changed");
3040 }
3041 
3042 void
e_comp_editor_ensure_changed(ECompEditor * comp_editor)3043 e_comp_editor_ensure_changed (ECompEditor *comp_editor)
3044 {
3045 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3046 
3047 	e_comp_editor_set_changed (comp_editor, TRUE);
3048 }
3049 
3050 gboolean
e_comp_editor_get_updating(ECompEditor * comp_editor)3051 e_comp_editor_get_updating (ECompEditor *comp_editor)
3052 {
3053 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
3054 
3055 	return comp_editor->priv->updating > 0;
3056 }
3057 
3058 void
e_comp_editor_set_updating(ECompEditor * comp_editor,gboolean updating)3059 e_comp_editor_set_updating (ECompEditor *comp_editor,
3060 			    gboolean updating)
3061 {
3062 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3063 
3064 	if (updating) {
3065 		comp_editor->priv->updating++;
3066 	} else if (comp_editor->priv->updating > 0) {
3067 		comp_editor->priv->updating--;
3068 	} else {
3069 		g_warn_if_reached ();
3070 	}
3071 }
3072 
3073 ECalClient *
e_comp_editor_get_source_client(ECompEditor * comp_editor)3074 e_comp_editor_get_source_client (ECompEditor *comp_editor)
3075 {
3076 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
3077 
3078 	return comp_editor->priv->source_client;
3079 }
3080 
3081 void
e_comp_editor_set_source_client(ECompEditor * comp_editor,ECalClient * client)3082 e_comp_editor_set_source_client (ECompEditor *comp_editor,
3083 				 ECalClient *client)
3084 {
3085 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3086 
3087 	if (client == comp_editor->priv->source_client)
3088 		return;
3089 
3090 	if (client)
3091 		g_object_ref (client);
3092 	g_clear_object (&comp_editor->priv->source_client);
3093 	comp_editor->priv->source_client = client;
3094 
3095 	g_object_notify (G_OBJECT (comp_editor), "source-client");
3096 }
3097 
3098 ECalClient *
e_comp_editor_get_target_client(ECompEditor * comp_editor)3099 e_comp_editor_get_target_client (ECompEditor *comp_editor)
3100 {
3101 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
3102 
3103 	return comp_editor->priv->target_client;
3104 }
3105 
3106 static void
comp_editor_target_backend_property_changed_cb(EClient * client,const gchar * property_name,const gchar * property_value,gpointer user_data)3107 comp_editor_target_backend_property_changed_cb (EClient *client,
3108 						const gchar *property_name,
3109 						const gchar *property_value,
3110 						gpointer user_data)
3111 {
3112 	ECompEditor *comp_editor = user_data;
3113 
3114 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3115 
3116 	if (!g_direct_equal (client, comp_editor->priv->target_client))
3117 		return;
3118 
3119 	if (g_strcmp0 (property_name, E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS) == 0)
3120 		e_comp_editor_set_cal_email_address (comp_editor, property_value);
3121 	else if (g_strcmp0 (property_name, E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS) == 0)
3122 		e_comp_editor_set_alarm_email_address (comp_editor, property_value);
3123 }
3124 
3125 void
e_comp_editor_set_target_client(ECompEditor * comp_editor,ECalClient * client)3126 e_comp_editor_set_target_client (ECompEditor *comp_editor,
3127 				 ECalClient *client)
3128 {
3129 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3130 
3131 	if (client == comp_editor->priv->target_client)
3132 		return;
3133 
3134 	if (client)
3135 		g_object_ref (client);
3136 
3137 	e_comp_editor_disconnect_target_backend_property_change_handler (comp_editor);
3138 	g_clear_object (&comp_editor->priv->target_client);
3139 	comp_editor->priv->target_client = client;
3140 
3141 	if (client && !comp_editor->priv->source_client && comp_editor->priv->origin_source &&
3142 	    e_source_equal (e_client_get_source (E_CLIENT (client)), comp_editor->priv->origin_source))
3143 		e_comp_editor_set_source_client (comp_editor, client);
3144 
3145 	if (client) {
3146 		comp_editor->priv->target_backend_property_change_id = g_signal_connect (client,
3147 			"backend-property-changed", G_CALLBACK (comp_editor_target_backend_property_changed_cb), comp_editor);
3148 	}
3149 	e_comp_editor_sensitize_widgets (comp_editor);
3150 
3151 	g_object_notify (G_OBJECT (comp_editor), "target-client");
3152 }
3153 
3154 const gchar *
e_comp_editor_get_title_suffix(ECompEditor * comp_editor)3155 e_comp_editor_get_title_suffix (ECompEditor *comp_editor)
3156 {
3157 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
3158 
3159 	return comp_editor->priv->title_suffix;
3160 }
3161 
3162 void
e_comp_editor_set_title_suffix(ECompEditor * comp_editor,const gchar * title_suffix)3163 e_comp_editor_set_title_suffix (ECompEditor *comp_editor,
3164 				const gchar *title_suffix)
3165 {
3166 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3167 
3168 	if (g_strcmp0 (title_suffix, comp_editor->priv->title_suffix) == 0)
3169 		return;
3170 
3171 	g_free (comp_editor->priv->title_suffix);
3172 	comp_editor->priv->title_suffix = g_strdup (title_suffix);
3173 
3174 	g_object_notify (G_OBJECT (comp_editor), "title-suffix");
3175 
3176 	e_comp_editor_update_window_title (comp_editor);
3177 }
3178 
3179 void
e_comp_editor_set_time_parts(ECompEditor * comp_editor,ECompEditorPropertyPart * dtstart_part,ECompEditorPropertyPart * dtend_part)3180 e_comp_editor_set_time_parts (ECompEditor *comp_editor,
3181 			      ECompEditorPropertyPart *dtstart_part,
3182 			      ECompEditorPropertyPart *dtend_part)
3183 {
3184 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3185 
3186 	if (dtstart_part)
3187 		g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part));
3188 	if (dtend_part)
3189 		g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part));
3190 
3191 	ece_connect_time_parts (comp_editor, dtstart_part, dtend_part);
3192 }
3193 
3194 void
e_comp_editor_get_time_parts(ECompEditor * comp_editor,ECompEditorPropertyPart ** out_dtstart_part,ECompEditorPropertyPart ** out_dtend_part)3195 e_comp_editor_get_time_parts (ECompEditor *comp_editor,
3196 			      ECompEditorPropertyPart **out_dtstart_part,
3197 			      ECompEditorPropertyPart **out_dtend_part)
3198 {
3199 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3200 
3201 	if (out_dtstart_part)
3202 		*out_dtstart_part = comp_editor->priv->dtstart_part;
3203 	if (out_dtend_part)
3204 		*out_dtend_part = comp_editor->priv->dtend_part;
3205 }
3206 
3207 /* This consumes the @page. */
3208 void
e_comp_editor_add_page(ECompEditor * comp_editor,const gchar * label,ECompEditorPage * page)3209 e_comp_editor_add_page (ECompEditor *comp_editor,
3210 			const gchar *label,
3211 			ECompEditorPage *page)
3212 {
3213 	ECompEditor *pages_comp_editor;
3214 
3215 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3216 	g_return_if_fail (label != NULL);
3217 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
3218 
3219 	pages_comp_editor = e_comp_editor_page_ref_editor (page);
3220 	if (pages_comp_editor != comp_editor) {
3221 		g_warn_if_fail (pages_comp_editor == comp_editor);
3222 		g_clear_object (&pages_comp_editor);
3223 		return;
3224 	}
3225 
3226 	g_clear_object (&pages_comp_editor);
3227 
3228 	/* One reference uses the GtkNotebook, the other the pages GSList */
3229 	gtk_notebook_append_page (comp_editor->priv->content,
3230 		GTK_WIDGET (page),
3231 		gtk_label_new_with_mnemonic (label));
3232 
3233 	comp_editor->priv->pages = g_slist_append (comp_editor->priv->pages, g_object_ref (page));
3234 
3235 	g_signal_connect_swapped (page, "changed", G_CALLBACK (e_comp_editor_ensure_changed), comp_editor);
3236 
3237 	if (E_IS_COMP_EDITOR_PAGE_GENERAL (page)) {
3238 		ECompEditorPageGeneral *page_general;
3239 
3240 		g_return_if_fail (comp_editor->priv->page_general == NULL);
3241 
3242 		page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
3243 
3244 		g_signal_connect (page_general, "notify::selected-source",
3245 			G_CALLBACK (comp_editor_selected_source_notify_cb), comp_editor);
3246 
3247 		comp_editor->priv->page_general = page_general;
3248 
3249 		if ((comp_editor->priv->flags & E_COMP_EDITOR_FLAG_WITH_ATTENDEES) != 0) {
3250 			e_comp_editor_page_general_set_show_attendees (page_general, TRUE);
3251 		}
3252 	}
3253 }
3254 
3255 /* The returned pointer is owned by the @comp_editor; returns the first instance,
3256    in order of the addition. */
3257 ECompEditorPage *
e_comp_editor_get_page(ECompEditor * comp_editor,GType page_type)3258 e_comp_editor_get_page (ECompEditor *comp_editor,
3259 			GType page_type)
3260 {
3261 	GSList *link;
3262 
3263 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
3264 	g_return_val_if_fail (g_type_is_a (page_type, E_TYPE_COMP_EDITOR_PAGE), NULL);
3265 	g_return_val_if_fail (page_type != E_TYPE_COMP_EDITOR_PAGE, NULL);
3266 
3267 	for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) {
3268 		ECompEditorPage *page = link->data;
3269 
3270 		if (G_TYPE_CHECK_INSTANCE_TYPE (page, page_type))
3271 			return page;
3272 	}
3273 
3274 	return NULL;
3275 }
3276 
3277 /* Free the returned GSList with g_slist_free(), the memebers are owned by the comp_editor */
3278 GSList *
e_comp_editor_get_pages(ECompEditor * comp_editor)3279 e_comp_editor_get_pages (ECompEditor *comp_editor)
3280 {
3281 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
3282 
3283 	return g_slist_copy (comp_editor->priv->pages);
3284 }
3285 
3286 void
e_comp_editor_select_page(ECompEditor * comp_editor,ECompEditorPage * page)3287 e_comp_editor_select_page (ECompEditor *comp_editor,
3288 			   ECompEditorPage *page)
3289 {
3290 	gint page_num;
3291 
3292 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3293 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
3294 
3295 	page_num = gtk_notebook_page_num (comp_editor->priv->content, GTK_WIDGET (page));
3296 	g_return_if_fail (page_num != -1);
3297 
3298 	gtk_notebook_set_current_page (comp_editor->priv->content, page_num);
3299 }
3300 
3301 /* Unref returned pointer when done with it. */
3302 static EAlert *
e_comp_editor_add_alert(ECompEditor * comp_editor,const gchar * alert_id,const gchar * primary_text,const gchar * secondary_text)3303 e_comp_editor_add_alert (ECompEditor *comp_editor,
3304 			 const gchar *alert_id,
3305 			 const gchar *primary_text,
3306 			 const gchar *secondary_text)
3307 {
3308 	EAlert *alert;
3309 
3310 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
3311 	g_return_val_if_fail (alert_id != NULL, NULL);
3312 	g_return_val_if_fail (primary_text != NULL || secondary_text != NULL, NULL);
3313 
3314 	alert = e_alert_new (alert_id,
3315 		primary_text ? primary_text : "",
3316 		secondary_text ? secondary_text : "",
3317 		NULL);
3318 
3319 	e_alert_bar_add_alert (comp_editor->priv->alert_bar, alert);
3320 	e_comp_editor_set_urgency_hint (comp_editor);
3321 
3322 	return alert;
3323 }
3324 
3325 /* Unref returned pointer when done with it. */
3326 EAlert *
e_comp_editor_add_information(ECompEditor * comp_editor,const gchar * primary_text,const gchar * secondary_text)3327 e_comp_editor_add_information (ECompEditor *comp_editor,
3328 			       const gchar *primary_text,
3329 			       const gchar *secondary_text)
3330 {
3331 	return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-information", primary_text, secondary_text);
3332 }
3333 
3334 /* Unref returned pointer when done with it. */
3335 EAlert *
e_comp_editor_add_warning(ECompEditor * comp_editor,const gchar * primary_text,const gchar * secondary_text)3336 e_comp_editor_add_warning (ECompEditor *comp_editor,
3337 			   const gchar *primary_text,
3338 			   const gchar *secondary_text)
3339 {
3340 	return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-warning", primary_text, secondary_text);
3341 }
3342 
3343 /* Unref returned pointer when done with it. */
3344 EAlert *
e_comp_editor_add_error(ECompEditor * comp_editor,const gchar * primary_text,const gchar * secondary_text)3345 e_comp_editor_add_error (ECompEditor *comp_editor,
3346 			 const gchar *primary_text,
3347 			 const gchar *secondary_text)
3348 {
3349 	return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-error", primary_text, secondary_text);
3350 }
3351 
3352 
3353 static gboolean
ece_check_start_before_end(ECompEditor * comp_editor,ICalTime ** pstart_tt,ICalTime ** pend_tt,gboolean adjust_end_time)3354 ece_check_start_before_end (ECompEditor *comp_editor,
3355 			    ICalTime **pstart_tt,
3356 			    ICalTime **pend_tt,
3357 			    gboolean adjust_end_time)
3358 {
3359 	ICalTime *start_tt, *end_tt, *end_tt_copy;
3360 	ICalTimezone *start_zone, *end_zone;
3361 	gint duration = -1;
3362 	gint cmp;
3363 
3364 	start_tt = *pstart_tt;
3365 	end_tt = *pend_tt;
3366 
3367 	if ((e_comp_editor_get_flags (comp_editor) & E_COMP_EDITOR_FLAG_IS_NEW) == 0) {
3368 		ICalComponent *icomp;
3369 
3370 		icomp = e_comp_editor_get_component (comp_editor);
3371 		if (icomp &&
3372 		    e_cal_util_component_has_property (icomp, I_CAL_DTSTART_PROPERTY) &&
3373 		    (e_cal_util_component_has_property (icomp, I_CAL_DTEND_PROPERTY) ||
3374 		     e_cal_util_component_has_property (icomp, I_CAL_DUE_PROPERTY))) {
3375 			ICalTime *orig_start, *orig_end;
3376 
3377 			orig_start = i_cal_component_get_dtstart (icomp);
3378 			orig_end = i_cal_component_get_dtend (icomp);
3379 
3380 			if (orig_start && i_cal_time_is_valid_time (orig_start) &&
3381 			    orig_end && i_cal_time_is_valid_time (orig_end)) {
3382 				duration = i_cal_time_as_timet (orig_end) - i_cal_time_as_timet (orig_start);
3383 			}
3384 
3385 			g_clear_object (&orig_start);
3386 			g_clear_object (&orig_end);
3387 		}
3388 	}
3389 
3390 	start_zone = i_cal_time_get_timezone (start_tt);
3391 	end_zone = i_cal_time_get_timezone (end_tt);
3392 
3393 	/* Convert the end time to the same timezone as the start time. */
3394 	end_tt_copy = i_cal_time_clone (end_tt);
3395 
3396 	if (start_zone && end_zone && start_zone != end_zone)
3397 		i_cal_time_convert_timezone (end_tt_copy, end_zone, start_zone);
3398 
3399 	/* Now check if the start time is after the end time. If it is,
3400 	 * we need to modify one of the times. */
3401 	cmp = i_cal_time_compare (start_tt, end_tt_copy);
3402 	if (cmp > 0) {
3403 		if (adjust_end_time) {
3404 			/* Try to switch only the date */
3405 			i_cal_time_set_date (end_tt,
3406 				i_cal_time_get_year (start_tt),
3407 				i_cal_time_get_month (start_tt),
3408 				i_cal_time_get_day (start_tt));
3409 
3410 			g_clear_object (&end_tt_copy);
3411 			end_tt_copy = i_cal_time_clone (end_tt);
3412 			if (start_zone && end_zone && start_zone != end_zone)
3413 				i_cal_time_convert_timezone (end_tt_copy, end_zone, start_zone);
3414 
3415 			if (duration > 0)
3416 				i_cal_time_adjust (end_tt_copy, 0, 0, 0, -duration);
3417 
3418 			if (i_cal_time_compare (start_tt, end_tt_copy) >= 0) {
3419 				g_clear_object (&end_tt);
3420 				end_tt = i_cal_time_clone (start_tt);
3421 
3422 				if (duration >= 0) {
3423 					i_cal_time_adjust (end_tt, 0, 0, 0, duration);
3424 				} else {
3425 					/* Modify the end time, to be the start + 1 hour/day. */
3426 					i_cal_time_adjust (end_tt, 0, i_cal_time_is_date (start_tt) ? 24 : 1, 0, 0);
3427 				}
3428 
3429 				if (start_zone && end_zone && start_zone != end_zone)
3430 					i_cal_time_convert_timezone (end_tt, start_zone, end_zone);
3431 			}
3432 		} else {
3433 			/* Try to switch only the date */
3434 			i_cal_time_set_date (start_tt,
3435 				i_cal_time_get_year (end_tt),
3436 				i_cal_time_get_month (end_tt),
3437 				i_cal_time_get_day (end_tt));
3438 
3439 			if (i_cal_time_compare (start_tt, end_tt_copy) >= 0) {
3440 				g_clear_object (&start_tt);
3441 				start_tt = i_cal_time_clone (end_tt);
3442 
3443 				if (duration >= 0) {
3444 					i_cal_time_adjust (start_tt, 0, 0, 0, -duration);
3445 				} else {
3446 					/* Modify the start time, to be the end - 1 hour/day. */
3447 					i_cal_time_adjust (start_tt, 0, i_cal_time_is_date (start_tt) ? -24 : -1, 0, 0);
3448 				}
3449 
3450 				if (start_zone && end_zone && start_zone != end_zone)
3451 					i_cal_time_convert_timezone (start_tt, end_zone, start_zone);
3452 			}
3453 		}
3454 
3455 		*pstart_tt = start_tt;
3456 		*pend_tt = end_tt;
3457 
3458 		g_clear_object (&end_tt_copy);
3459 
3460 		return TRUE;
3461 	}
3462 
3463 	g_clear_object (&end_tt_copy);
3464 
3465 	return FALSE;
3466 }
3467 
3468 void
e_comp_editor_ensure_start_before_end(ECompEditor * comp_editor,ECompEditorPropertyPart * start_datetime,ECompEditorPropertyPart * end_datetime,gboolean change_end_datetime)3469 e_comp_editor_ensure_start_before_end (ECompEditor *comp_editor,
3470 				       ECompEditorPropertyPart *start_datetime,
3471 				       ECompEditorPropertyPart *end_datetime,
3472 				       gboolean change_end_datetime)
3473 {
3474 	ECompEditorPropertyPartDatetime *start_dtm, *end_dtm;
3475 	ICalTime *start_tt, *end_tt;
3476 	gboolean set_dtstart = FALSE, set_dtend = FALSE;
3477 
3478 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
3479 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (start_datetime));
3480 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (end_datetime));
3481 
3482 	start_dtm = E_COMP_EDITOR_PROPERTY_PART_DATETIME (start_datetime);
3483 	end_dtm = E_COMP_EDITOR_PROPERTY_PART_DATETIME (end_datetime);
3484 
3485 	start_tt = e_comp_editor_property_part_datetime_get_value (start_dtm);
3486 	end_tt = e_comp_editor_property_part_datetime_get_value (end_dtm);
3487 
3488 	if (!start_tt || !end_tt ||
3489 	    i_cal_time_is_null_time (start_tt) ||
3490 	    i_cal_time_is_null_time (end_tt) ||
3491 	    !i_cal_time_is_valid_time (start_tt) ||
3492 	    !i_cal_time_is_valid_time (end_tt)) {
3493 		g_clear_object (&start_tt);
3494 		g_clear_object (&end_tt);
3495 		return;
3496 	}
3497 
3498 	if (i_cal_time_is_date (start_tt) || i_cal_time_is_date (end_tt)) {
3499 		/* All Day Events are simple. We just compare the dates and if
3500 		 * start > end we copy one of them to the other. */
3501 		gint cmp;
3502 
3503 		i_cal_time_set_is_date (start_tt, TRUE);
3504 		i_cal_time_set_is_date (end_tt, TRUE);
3505 
3506 		cmp = i_cal_time_compare_date_only (start_tt, end_tt);
3507 
3508 		if (cmp > 0) {
3509 			if (change_end_datetime) {
3510 				g_clear_object (&end_tt);
3511 				end_tt = start_tt;
3512 				start_tt = NULL;
3513 				set_dtend = TRUE;
3514 			} else {
3515 				g_clear_object (&start_tt);
3516 				start_tt = end_tt;
3517 				end_tt = NULL;
3518 				set_dtstart = TRUE;
3519 			}
3520 		}
3521 	} else {
3522 		if (ece_check_start_before_end (comp_editor, &start_tt, &end_tt, change_end_datetime)) {
3523 			if (change_end_datetime)
3524 				set_dtend = TRUE;
3525 			else
3526 				set_dtstart = TRUE;
3527 		}
3528 	}
3529 
3530 	if (set_dtstart || set_dtend) {
3531 		e_comp_editor_set_updating (comp_editor, TRUE);
3532 
3533 		if (set_dtstart)
3534 			e_comp_editor_property_part_datetime_set_value (start_dtm, start_tt);
3535 
3536 		if (set_dtend)
3537 			e_comp_editor_property_part_datetime_set_value (end_dtm, end_tt);
3538 
3539 		e_comp_editor_set_updating (comp_editor, FALSE);
3540 	}
3541 
3542 	g_clear_object (&start_tt);
3543 	g_clear_object (&end_tt);
3544 }
3545 
3546 static gboolean
e_comp_editor_holds_component(ECompEditor * comp_editor,ESource * origin_source,const ICalComponent * component)3547 e_comp_editor_holds_component (ECompEditor *comp_editor,
3548 			       ESource *origin_source,
3549 			       const ICalComponent *component)
3550 {
3551 	const gchar *component_uid, *editor_uid;
3552 	gboolean equal;
3553 
3554 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
3555 	g_return_val_if_fail (I_CAL_IS_COMPONENT ((ICalComponent *) component), FALSE);
3556 
3557 	if (!origin_source || !comp_editor->priv->origin_source ||
3558 	    !e_source_equal (origin_source, comp_editor->priv->origin_source))
3559 		return FALSE;
3560 
3561 	component_uid = i_cal_component_get_uid ((ICalComponent *) component);
3562 	editor_uid = i_cal_component_get_uid (comp_editor->priv->component);
3563 
3564 	if (!component_uid || !editor_uid)
3565 		return FALSE;
3566 
3567 	equal = g_strcmp0 (component_uid, editor_uid) == 0;
3568 	if (equal) {
3569 		ICalTime *component_rid, *editor_rid;
3570 
3571 		component_rid = i_cal_component_get_recurrenceid ((ICalComponent *) component);
3572 		editor_rid = i_cal_component_get_recurrenceid (comp_editor->priv->component);
3573 
3574 		if (!component_rid || i_cal_time_is_null_time (component_rid)) {
3575 			equal = !editor_rid || i_cal_time_is_null_time (editor_rid);
3576 		} else if (editor_rid && !i_cal_time_is_null_time (editor_rid)) {
3577 			equal = i_cal_time_compare (component_rid, editor_rid) == 0;
3578 		}
3579 
3580 		g_clear_object (&component_rid);
3581 		g_clear_object (&editor_rid);
3582 	}
3583 
3584 	return equal;
3585 }
3586 
3587 ECompEditor *
e_comp_editor_open_for_component(GtkWindow * parent,EShell * shell,ESource * origin_source,const ICalComponent * component,guint32 flags)3588 e_comp_editor_open_for_component (GtkWindow *parent,
3589 				  EShell *shell,
3590 				  ESource *origin_source,
3591 				  const ICalComponent *component,
3592 				  guint32 flags /* bit-or of ECompEditorFlags */)
3593 {
3594 	ECompEditor *comp_editor;
3595 	GType comp_editor_type;
3596 
3597 	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
3598 	if (origin_source)
3599 		g_return_val_if_fail (E_IS_SOURCE (origin_source), NULL);
3600 	g_return_val_if_fail (I_CAL_IS_COMPONENT ((ICalComponent *) component), NULL);
3601 
3602 	comp_editor = e_comp_editor_find_existing_for (origin_source, component);
3603 	if (comp_editor) {
3604 		gtk_window_present (GTK_WINDOW (comp_editor));
3605 		return comp_editor;
3606 	}
3607 
3608 	switch (i_cal_component_isa (component)) {
3609 		case I_CAL_VEVENT_COMPONENT:
3610 			comp_editor_type = E_TYPE_COMP_EDITOR_EVENT;
3611 			break;
3612 		case I_CAL_VTODO_COMPONENT:
3613 			comp_editor_type = E_TYPE_COMP_EDITOR_TASK;
3614 			break;
3615 		case I_CAL_VJOURNAL_COMPONENT:
3616 			comp_editor_type = E_TYPE_COMP_EDITOR_MEMO;
3617 			break;
3618 		default:
3619 			g_warn_if_reached ();
3620 			return NULL;
3621 	}
3622 
3623 	comp_editor = g_object_new (comp_editor_type,
3624 		"shell", shell,
3625 		"origin-source", origin_source,
3626 		"component", component,
3627 		"flags", flags,
3628 		NULL);
3629 
3630 	opened_editors = g_slist_prepend (opened_editors, comp_editor);
3631 
3632 	gtk_widget_show (GTK_WIDGET (comp_editor));
3633 
3634 	return comp_editor;
3635 }
3636 
3637 ECompEditor *
e_comp_editor_find_existing_for(ESource * origin_source,const ICalComponent * component)3638 e_comp_editor_find_existing_for (ESource *origin_source,
3639 				 const ICalComponent *component)
3640 {
3641 	ECompEditor *comp_editor;
3642 	GSList *link;
3643 
3644 	if (origin_source)
3645 		g_return_val_if_fail (E_IS_SOURCE (origin_source), NULL);
3646 	g_return_val_if_fail (I_CAL_IS_COMPONENT ((ICalComponent *) component), NULL);
3647 
3648 	for (link = opened_editors; link; link = g_slist_next (link)) {
3649 		comp_editor = link->data;
3650 
3651 		if (!comp_editor)
3652 			continue;
3653 
3654 		if (e_comp_editor_holds_component (comp_editor, origin_source, component)) {
3655 			gtk_window_present (GTK_WINDOW (comp_editor));
3656 			return comp_editor;
3657 		}
3658 	}
3659 
3660 	return NULL;
3661 }
3662 
3663 /* Returned pointer is owned by libical or ECalClient; can return NULL */
3664 ICalTimezone *
e_comp_editor_lookup_timezone(ECompEditor * comp_editor,const gchar * tzid)3665 e_comp_editor_lookup_timezone (ECompEditor *comp_editor,
3666 			       const gchar *tzid)
3667 {
3668 	ICalTimezone *zone;
3669 
3670 	g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
3671 
3672 	if (!tzid || !*tzid)
3673 		return NULL;
3674 
3675 	zone = i_cal_timezone_get_builtin_timezone_from_tzid (tzid);
3676 
3677 	if (!zone)
3678 		zone = i_cal_timezone_get_builtin_timezone (tzid);
3679 
3680 	if (!zone && comp_editor->priv->source_client) {
3681 		if (!e_cal_client_get_timezone_sync (comp_editor->priv->source_client, tzid, &zone, NULL, NULL))
3682 			zone = NULL;
3683 	}
3684 
3685 	if (!zone && comp_editor->priv->target_client && comp_editor->priv->source_client != comp_editor->priv->target_client) {
3686 		if (!e_cal_client_get_timezone_sync (comp_editor->priv->target_client, tzid, &zone, NULL, NULL))
3687 			zone = NULL;
3688 	}
3689 
3690 	return zone;
3691 }
3692 
3693 ICalTimezone *
e_comp_editor_lookup_timezone_cb(const gchar * tzid,gpointer user_data,GCancellable * cancellable,GError ** error)3694 e_comp_editor_lookup_timezone_cb (const gchar *tzid,
3695 				  gpointer user_data, /* ECompEditor * */
3696 				  GCancellable *cancellable,
3697 				  GError **error)
3698 {
3699 	return e_comp_editor_lookup_timezone (user_data, tzid);
3700 }
3701