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 (¶m);
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 (¶m);
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