No issues found
1 /*
2 * Evolution calendar - Framework for a calendar component editor dialog
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Authors:
19 * Federico Mena-Quintero <federico@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <errno.h>
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <glib/gi18n-lib.h>
36 #include <glib/gstdio.h>
37 #include <gio/gio.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <libebackend/libebackend.h>
40
41 #include <e-util/e-util.h>
42 #include <libevolution-utils/e-alert-sink.h>
43 #include <e-util/e-dialog-utils.h>
44 #include <e-util/e-util-private.h>
45 #include <shell/e-shell.h>
46
47 #include "../print.h"
48 #include "../comp-util.h"
49 #include "save-comp.h"
50 #include "delete-comp.h"
51 #include "send-comp.h"
52 #include "changed-comp.h"
53 #include "cancel-comp.h"
54 #include "recur-comp.h"
55 #include "comp-editor.h"
56 #include "comp-editor-util.h"
57 #include "../calendar-config-keys.h"
58 #include "widgets/misc/e-attachment-view.h"
59 #include "widgets/misc/e-attachment-paned.h"
60
61 #include "libevolution-utils/e-alert-dialog.h"
62 #include "e-util/e-ui-manager.h"
63
64 #define COMP_EDITOR_GET_PRIVATE(obj) \
65 (G_TYPE_INSTANCE_GET_PRIVATE \
66 ((obj), TYPE_COMP_EDITOR, CompEditorPrivate))
67
68 #define d(x)
69
70 /* Private part of the CompEditor structure */
71 struct _CompEditorPrivate {
72
73 gpointer shell; /* weak pointer */
74
75 GSettings *calendar_settings;
76
77 /* EFocusTracker keeps selection actions up-to-date. */
78 EFocusTracker *focus_tracker;
79
80 /* Each CompEditor window gets its own GtkWindowGroup, so it
81 * doesn't block the main window or other CompEditor windows. */
82 GtkWindowGroup *window_group;
83
84 /* Client to use */
85 ECalClient *cal_client;
86
87 /* Source client (where comp lives currently) */
88 ECalClient *source_client;
89
90 /* View to listen for changes */
91 ECalClientView *view;
92 GCancellable *view_cancellable;
93
94 /* Calendar object/uid we are editing; this is an internal copy */
95 ECalComponent *comp;
96
97 /* The pages we have */
98 GList *pages;
99
100 /* Notebook to hold the pages */
101 GtkNotebook *notebook;
102
103 /* Attachment handling */
104 GtkWidget *attachment_view;
105
106 /* Manages menus and toolbars */
107 GtkUIManager *ui_manager;
108
109 gchar *summary;
110
111 guint32 attachment_bar_visible : 1;
112
113 /* TODO use this flags for setting all the boolean variables
114 * below */
115 CompEditorFlags flags;
116
117 icaltimezone *zone;
118 gboolean use_24_hour_format;
119
120 gint work_day_end_hour;
121 gint work_day_end_minute;
122 gint work_day_start_hour;
123 gint work_day_start_minute;
124
125 gboolean changed;
126 gboolean needs_send;
127
128 gboolean saved;
129
130 CalObjModType mod;
131
132 gboolean existing_org;
133 gboolean user_org;
134 gboolean is_group_item;
135
136 gboolean warned;
137 };
138
139 enum {
140 PROP_0,
141 PROP_CHANGED,
142 PROP_CLIENT,
143 PROP_FLAGS,
144 PROP_FOCUS_TRACKER,
145 PROP_SHELL,
146 PROP_SUMMARY,
147 PROP_TIMEZONE,
148 PROP_USE_24_HOUR_FORMAT,
149 PROP_WORK_DAY_END_HOUR,
150 PROP_WORK_DAY_END_MINUTE,
151 PROP_WORK_DAY_START_HOUR,
152 PROP_WORK_DAY_START_MINUTE
153 };
154
155 static const gchar *ui =
156 "<ui>"
157 " <menubar action='main-menu'>"
158 " <menu action='file-menu'>"
159 " <menuitem action='save'/>"
160 " <menuitem action='save-and-close'/>"
161 " <separator/>"
162 " <menuitem action='print-preview'/>"
163 " <menuitem action='print'/>"
164 " <separator/>"
165 " <menuitem action='close'/>"
166 " </menu>"
167 " <menu action='edit-menu'>"
168 " <menuitem action='cut-clipboard'/>"
169 " <menuitem action='copy-clipboard'/>"
170 " <menuitem action='paste-clipboard'/>"
171 " <menuitem action='delete-selection'/>"
172 " <separator/>"
173 " <menuitem action='select-all'/>"
174 " </menu>"
175 " <menu action='view-menu'/>"
176 " <menu action='insert-menu'>"
177 " <menuitem action='attach'/>"
178 " </menu>"
179 " <menu action='options-menu'/>"
180 " <menu action='help-menu'>"
181 " <menuitem action='help'/>"
182 " </menu>"
183 " </menubar>"
184 " <toolbar name='main-toolbar'>"
185 " <toolitem action='save-and-close'/>\n"
186 " <toolitem action='save'/>\n"
187 "#if !EXPRESS\n"
188 " <toolitem action='print'/>\n"
189 "#endif\n"
190 " <separator/>"
191 " <placeholder name='content'/>\n"
192 "#if EXPRESS\n"
193 " <separator expand='true'/>\n"
194 " <toolitem action='close'/>\n"
195 "#endif\n"
196 " </toolbar>"
197 "</ui>";
198
199 static void comp_editor_show_help (CompEditor *editor);
200
201 static void real_edit_comp (CompEditor *editor,
202 ECalComponent *comp);
203 static gboolean real_send_comp (CompEditor *editor,
204 ECalComponentItipMethod method,
205 gboolean strip_alarms);
206 static gboolean prompt_and_save_changes (CompEditor *editor,
207 gboolean send);
208 static void close_dialog (CompEditor *editor);
209
210 static void page_dates_changed_cb (CompEditor *editor,
211 CompEditorPageDates *dates,
212 CompEditorPage *page);
213
214 static void obj_modified_cb (ECalClientView *view,
215 const GSList *objs,
216 CompEditor *editor);
217 static void obj_removed_cb (ECalClientView *view,
218 const GSList *uids,
219 CompEditor *editor);
220
221 G_DEFINE_TYPE_WITH_CODE (
222 CompEditor, comp_editor, GTK_TYPE_WINDOW,
223 G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, NULL)
224 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
225
226 enum {
227 OBJECT_CREATED,
228 COMP_CLOSED,
229 LAST_SIGNAL
230 };
231
232 static guint signals[LAST_SIGNAL];
233 static GList *active_editors;
234
235 static void
236 comp_editor_weak_notify_cb (gpointer unused,
237 GObject *where_the_object_was)
238 {
239 active_editors = g_list_remove (active_editors, where_the_object_was);
240 }
241
242 static void
243 attachment_store_changed_cb (CompEditor *editor)
244 {
245 /* Mark the editor as changed so it prompts about unsaved
246 * changes on close */
247 comp_editor_set_changed (editor, TRUE);
248 }
249
250 static void
251 attachment_save_finished (EAttachmentStore *store,
252 GAsyncResult *result,
253 gpointer user_data)
254 {
255 GtkWidget *dialog;
256 const gchar *primary_text;
257 gchar **uris;
258 GError *error = NULL;
259
260 struct {
261 gchar **uris;
262 gboolean done;
263 GtkWindow *parent;
264 } *status = user_data;
265
266 uris = e_attachment_store_save_finish (store, result, &error);
267
268 status->uris = uris;
269 status->done = TRUE;
270
271 if (uris != NULL)
272 goto exit;
273
274 /* Ignore cancellations. */
275 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
276 goto exit;
277
278 primary_text = _("Could not save attachments");
279
280 dialog = gtk_message_dialog_new_with_markup (
281 status->parent, GTK_DIALOG_DESTROY_WITH_PARENT,
282 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
283 "<big><b>%s</b></big>", primary_text);
284
285 gtk_message_dialog_format_secondary_text (
286 GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
287
288 gtk_dialog_run (GTK_DIALOG (dialog));
289
290 gtk_widget_destroy (dialog);
291
292 exit:
293 if (error != NULL)
294 g_error_free (error);
295
296 g_object_unref (status->parent);
297 }
298
299 static GSList *
300 get_attachment_list (CompEditor *editor)
301 {
302 EAttachmentStore *store;
303 EAttachmentView *view;
304 GFile *destination;
305 GSList *list = NULL;
306 const gchar *comp_uid = NULL;
307 const gchar *local_store;
308 gchar *filename_prefix, *tmp;
309 gint ii;
310
311 struct {
312 gchar **uris;
313 gboolean done;
314 GtkWindow *parent;
315 } status;
316
317 e_cal_component_get_uid (editor->priv->comp, &comp_uid);
318 g_return_val_if_fail (comp_uid != NULL, NULL);
319
320 status.uris = NULL;
321 status.done = FALSE;
322 status.parent = g_object_ref (editor);
323
324 view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
325 store = e_attachment_view_get_store (view);
326
327 tmp = g_strdup (comp_uid);
328 e_filename_make_safe (tmp);
329 filename_prefix = g_strconcat (tmp, "-", NULL);
330 g_free (tmp);
331
332 local_store = e_cal_client_get_local_attachment_store (editor->priv->cal_client);
333 destination = g_file_new_for_path (local_store);
334
335 e_attachment_store_save_async (
336 store, destination, filename_prefix,
337 (GAsyncReadyCallback) attachment_save_finished, &status);
338
339 g_object_unref (destination);
340 g_free (filename_prefix);
341
342 /* We can't return until we have results, so crank
343 * the main loop until the callback gets triggered. */
344 while (!status.done)
345 gtk_main_iteration ();
346
347 if (status.uris == NULL)
348 return NULL;
349
350 /* Transfer the URI strings to the GSList. */
351 for (ii = 0; status.uris[ii] != NULL; ii++) {
352 list = g_slist_prepend (list, status.uris[ii]);
353 status.uris[ii] = NULL;
354 }
355
356 g_free (status.uris);
357
358 return g_slist_reverse (list);
359 }
360
361 /* This sets the focus to the toplevel, so any field being edited is committed.
362 * FIXME: In future we may also want to check some of the fields are valid,
363 * e.g. the EDateEdit fields. */
364 static void
365 commit_all_fields (CompEditor *editor)
366 {
367 gtk_window_set_focus (GTK_WINDOW (editor), NULL);
368 }
369
370 static void
371 changes_view_ready_cb (GObject *source_object,
372 GAsyncResult *result,
373 gpointer user_data)
374 {
375 CompEditor *editor = user_data;
376 ECalClientView *view = NULL;
377 gboolean success;
378 GError *error = NULL;
379
380 g_return_if_fail (editor != NULL);
381
382 success = e_cal_client_get_view_finish (
383 E_CAL_CLIENT (source_object), result, &view, &error);
384
385 if (!success)
386 view = NULL;
387
388 if (view) {
389 editor->priv->view = view;
390 g_signal_connect (
391 view, "objects_modified",
392 G_CALLBACK (obj_modified_cb), editor);
393 g_signal_connect (
394 view, "objects_removed",
395 G_CALLBACK (obj_removed_cb), editor);
396
397 e_cal_client_view_start (view, &error);
398
399 if (error != NULL) {
400 g_warning (
401 "%s: Failed to start view: %s",
402 G_STRFUNC, error->message);
403 g_error_free (error);
404 }
405 } else if (error) {
406 if (!g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) &&
407 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
408 g_warning (
409 "%s: Failed to get view: %s",
410 G_STRFUNC, error->message);
411 g_error_free (error);
412 }
413 }
414
415 static void
416 listen_for_changes (CompEditor *editor)
417 {
418 CompEditorPrivate *priv;
419 const gchar *uid = NULL;
420
421 priv = editor->priv;
422
423 /* Discard change listener */
424 if (priv->view_cancellable) {
425 g_cancellable_cancel (priv->view_cancellable);
426 g_object_unref (priv->view_cancellable);
427 priv->view_cancellable = NULL;
428 }
429
430 if (priv->view) {
431 g_signal_handlers_disconnect_matched (
432 priv->view, G_SIGNAL_MATCH_DATA,
433 0, 0, NULL, NULL, editor);
434
435 g_object_unref (priv->view);
436 priv->view = NULL;
437 }
438
439 /* Listen for changes */
440 if (priv->comp)
441 e_cal_component_get_uid (priv->comp, &uid);
442
443 if (uid) {
444 gchar *query;
445
446 priv->view_cancellable = g_cancellable_new ();
447 query = g_strdup_printf ("(uid? \"%s\")", uid);
448 e_cal_client_get_view (
449 priv->source_client,
450 query, priv->view_cancellable,
451 changes_view_ready_cb, editor);
452 g_free (query);
453 }
454 }
455
456 static void
457 send_timezone (gpointer key,
458 gpointer value,
459 gpointer user_data)
460 {
461 icaltimezone *zone = value;
462 CompEditor *editor = user_data;
463 GError *error = NULL;
464
465 e_cal_client_add_timezone_sync (editor->priv->cal_client, zone, NULL, &error);
466
467 if (error != NULL) {
468 g_warning (
469 "%s: Failed to add timezone: %s",
470 G_STRFUNC, error->message);
471 g_error_free (error);
472 }
473 }
474
475 static gboolean
476 save_comp (CompEditor *editor)
477 {
478 CompEditorPrivate *priv;
479 CompEditorFlags flags;
480 ECalComponent *clone;
481 ESourceRegistry *registry;
482 EShell *shell;
483 GList *l;
484 gboolean result;
485 GError *error = NULL;
486 GHashTable *timezones;
487 const gchar *orig_uid = NULL;
488 gchar *orig_uid_copy;
489 icalcomponent *icalcomp;
490
491 priv = editor->priv;
492
493 if (!priv->changed)
494 return TRUE;
495
496 flags = comp_editor_get_flags (editor);
497 shell = comp_editor_get_shell (editor);
498
499 registry = e_shell_get_registry (shell);
500
501 /* Stop listening because we are about to change things */
502 if (priv->view) {
503 g_signal_handlers_disconnect_matched (
504 priv->view,
505 G_SIGNAL_MATCH_DATA,
506 0, 0, NULL, NULL,
507 editor);
508
509 g_object_unref (priv->view);
510 priv->view = NULL;
511 }
512
513 /* Update on the server */
514 timezones = g_hash_table_new (g_str_hash, g_str_equal);
515
516 clone = e_cal_component_clone (priv->comp);
517 comp_editor_copy_new_attendees (clone, priv->comp);
518 for (l = priv->pages; l != NULL; l = l->next) {
519 if (IS_COMP_EDITOR_PAGE (l->data) &&
520 !comp_editor_page_fill_component (l->data, clone)) {
521 g_object_unref (clone);
522 g_hash_table_destroy (timezones);
523 comp_editor_show_page (editor, COMP_EDITOR_PAGE (l->data));
524 return FALSE;
525 }
526
527 /* retrieve all timezones */
528 if (IS_COMP_EDITOR_PAGE (l->data))
529 comp_editor_page_fill_timezones (l->data, timezones);
530 }
531
532 /* If we are not the organizer, we don't update the sequence number */
533 if (!e_cal_component_has_organizer (clone) ||
534 itip_organizer_is_user (registry, clone, priv->cal_client) ||
535 itip_sentby_is_user (registry, clone, priv->cal_client))
536 e_cal_component_commit_sequence (clone);
537 else
538 e_cal_component_abort_sequence (clone);
539
540 g_object_unref (priv->comp);
541 priv->comp = clone;
542
543 e_cal_component_get_uid (priv->comp, &orig_uid);
544 /* Make a copy of it, because call of e_cal_create_object()
545 * rewrites the internal uid. */
546 orig_uid_copy = g_strdup (orig_uid);
547
548 /* send timezones */
549 g_hash_table_foreach (timezones, (GHFunc) send_timezone, editor);
550 g_hash_table_destroy (timezones);
551
552 /* Attachments*/
553
554 e_cal_component_set_attachment_list (
555 priv->comp, get_attachment_list (editor));
556 icalcomp = e_cal_component_get_icalcomponent (priv->comp);
557 /* send the component to the server */
558 if (!cal_comp_is_on_server (priv->comp, priv->cal_client)) {
559 gchar *uid = NULL;
560 result = e_cal_client_create_object_sync (
561 priv->cal_client, icalcomp, &uid, NULL, &error);
562 if (result) {
563 icalcomponent_set_uid (icalcomp, uid);
564 g_free (uid);
565 g_signal_emit_by_name (editor, "object_created");
566 }
567 } else {
568 gboolean has_recurrences;
569
570 has_recurrences =
571 e_cal_component_has_recurrences (priv->comp);
572
573 if (has_recurrences && priv->mod == CALOBJ_MOD_ALL)
574 comp_util_sanitize_recurrence_master (
575 priv->comp, priv->cal_client);
576
577 if (priv->mod == CALOBJ_MOD_THIS) {
578 e_cal_component_set_rdate_list (priv->comp, NULL);
579 e_cal_component_set_rrule_list (priv->comp, NULL);
580 e_cal_component_set_exdate_list (priv->comp, NULL);
581 e_cal_component_set_exrule_list (priv->comp, NULL);
582 }
583 result = e_cal_client_modify_object_sync (
584 priv->cal_client, icalcomp, priv->mod, NULL, &error);
585
586 if (priv->mod == CALOBJ_MOD_THIS) {
587 if (result && ((flags & COMP_EDITOR_DELEGATE) ||
588 !e_cal_component_has_organizer (clone) ||
589 itip_organizer_is_user (registry, clone, priv->cal_client) ||
590 itip_sentby_is_user (registry, clone, priv->cal_client)))
591 e_cal_component_commit_sequence (clone);
592 else
593 e_cal_component_abort_sequence (clone);
594 }
595 }
596
597 /* If the delay delivery is set, the items will not be created in
598 * the server immediately, so we need not show them in the view.
599 * They will appear as soon as the server creates it after the
600 * delay period. */
601 if (result && e_cal_component_has_attendees (priv->comp)) {
602 gboolean delay_set = FALSE;
603 icalproperty *icalprop;
604 icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
605 while (icalprop) {
606 const gchar *x_name;
607
608 x_name = icalproperty_get_x_name (icalprop);
609 if (!strcmp (x_name, "X-EVOLUTION-OPTIONS-DELAY")) {
610 delay_set = TRUE;
611 break;
612 }
613
614 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
615 }
616 if (delay_set) {
617 g_free (orig_uid_copy);
618 return TRUE;
619 }
620 }
621
622 if (!result) {
623 GtkWidget *dialog;
624
625 dialog = gtk_message_dialog_new (
626 NULL, 0,
627 GTK_MESSAGE_ERROR,
628 GTK_BUTTONS_OK,
629 "%s", (error != NULL) ? error->message :
630 _("Could not update object"));
631 gtk_dialog_run (GTK_DIALOG (dialog));
632 gtk_widget_destroy (dialog);
633
634 if (error)
635 g_error_free (error);
636
637 g_free (orig_uid_copy);
638
639 return FALSE;
640 } else {
641 if (priv->source_client &&
642 !e_source_equal (e_client_get_source (E_CLIENT (priv->cal_client)),
643 e_client_get_source (E_CLIENT (priv->source_client))) &&
644 cal_comp_is_on_server (priv->comp, priv->source_client)) {
645 /* Comp found a new home. Remove it from old one. */
646 GError *error = NULL;
647
648 if (e_cal_component_is_instance (priv->comp) ||
649 e_cal_component_has_recurrences (priv->comp))
650 e_cal_client_remove_object_sync (
651 priv->source_client, orig_uid_copy,
652 NULL, CALOBJ_MOD_ALL, NULL, &error);
653 else
654 e_cal_client_remove_object_sync (
655 priv->source_client,
656 orig_uid_copy, NULL, CALOBJ_MOD_THIS, NULL, &error);
657
658 if (error != NULL) {
659 g_warning (
660 "%s: Failed to remove object: %s",
661 G_STRFUNC, error->message);
662 g_error_free (error);
663 }
664
665 /* Let priv->source_client point to new home,
666 * so we can move it again this session. */
667 g_object_unref (priv->source_client);
668 priv->source_client = g_object_ref (priv->cal_client);
669
670 listen_for_changes (editor);
671 }
672
673 comp_editor_set_changed (editor, FALSE);
674 priv->saved = TRUE;
675 }
676
677 g_free (orig_uid_copy);
678
679 return TRUE;
680 }
681
682 static gboolean
683 save_comp_with_send (CompEditor *editor)
684 {
685 CompEditorPrivate *priv;
686 CompEditorFlags flags;
687 ESourceRegistry *registry;
688 EShell *shell;
689 gboolean send, delegated, only_new_attendees = FALSE;
690 gboolean delegate;
691 gboolean strip_alarms = TRUE;
692
693 priv = editor->priv;
694
695 flags = comp_editor_get_flags (editor);
696 shell = comp_editor_get_shell (editor);
697
698 registry = e_shell_get_registry (shell);
699
700 send = priv->changed && priv->needs_send;
701 delegate = flags & COMP_EDITOR_DELEGATE;
702
703 if (delegate) {
704 icalcomponent *icalcomp = e_cal_component_get_icalcomponent (priv->comp);
705 icalproperty *icalprop;
706
707 icalprop = icalproperty_new_x ("1");
708 icalproperty_set_x_name (icalprop, "X-EVOLUTION-DELEGATED");
709 icalcomponent_add_property (icalcomp, icalprop);
710 }
711
712 if (!save_comp (editor))
713 return FALSE;
714
715 delegated = delegate && !e_cal_client_check_save_schedules (priv->cal_client);
716 if (delegated || (send && send_component_dialog (
717 (GtkWindow *) editor, priv->cal_client, priv->comp,
718 !priv->existing_org, &strip_alarms, !priv->existing_org ?
719 NULL : &only_new_attendees))) {
720 if (delegated)
721 only_new_attendees = FALSE;
722
723 comp_editor_set_flags (
724 editor, (comp_editor_get_flags (editor) &
725 (~COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) |
726 (only_new_attendees ?
727 COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY : 0));
728
729 if ((itip_organizer_is_user (registry, priv->comp, priv->cal_client) ||
730 itip_sentby_is_user (registry, priv->comp, priv->cal_client))) {
731 if (e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_JOURNAL)
732 return comp_editor_send_comp (
733 editor, E_CAL_COMPONENT_METHOD_PUBLISH,
734 strip_alarms);
735 else
736 return comp_editor_send_comp (
737 editor, E_CAL_COMPONENT_METHOD_REQUEST,
738 strip_alarms);
739 } else {
740 if (!comp_editor_send_comp (
741 editor, E_CAL_COMPONENT_METHOD_REQUEST,
742 strip_alarms))
743 return FALSE;
744
745 if (delegate)
746 return comp_editor_send_comp (
747 editor, E_CAL_COMPONENT_METHOD_REPLY,
748 strip_alarms);
749 }
750 }
751
752 return TRUE;
753 }
754
755 static void
756 update_window_border (CompEditor *editor,
757 const gchar *description)
758 {
759 const gchar *icon_name;
760 const gchar *format;
761 gchar *title;
762
763 if (editor->priv->comp == NULL) {
764 title = g_strdup (_("Edit Appointment"));
765 icon_name = "x-office-calendar";
766 goto exit;
767
768 } else switch (e_cal_component_get_vtype (editor->priv->comp)) {
769 case E_CAL_COMPONENT_EVENT:
770 if (editor->priv->is_group_item)
771 format = _("Meeting - %s");
772 else
773 format = _("Appointment - %s");
774 icon_name = "appointment-new";
775 break;
776
777 case E_CAL_COMPONENT_TODO:
778 if (editor->priv->is_group_item)
779 format = _("Assigned Task - %s");
780 else
781 format = _("Task - %s");
782 icon_name = "stock_task";
783 break;
784
785 case E_CAL_COMPONENT_JOURNAL:
786 format = _("Memo - %s");
787 icon_name = "stock_insert-note";
788 break;
789
790 default:
791 g_return_if_reached ();
792 }
793
794 if (description == NULL || *description == '\0') {
795 ECalComponentText text;
796
797 e_cal_component_get_summary (editor->priv->comp, &text);
798 description = text.value;
799 }
800
801 if (description == NULL || *description == '\0')
802 description = _("No Summary");
803
804 title = g_strdup_printf (format, description);
805
806 exit:
807 gtk_window_set_icon_name (GTK_WINDOW (editor), icon_name);
808 gtk_window_set_title (GTK_WINDOW (editor), title);
809
810 g_free (title);
811 }
812
813 static void
814 action_attach_cb (GtkAction *action,
815 CompEditor *editor)
816 {
817 EAttachmentStore *store;
818 EAttachmentView *view;
819
820 view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
821 store = e_attachment_view_get_store (view);
822
823 e_attachment_store_run_load_dialog (store, GTK_WINDOW (editor));
824 }
825
826 static void
827 action_classification_cb (GtkRadioAction *action,
828 GtkRadioAction *current,
829 CompEditor *editor)
830 {
831 comp_editor_set_changed (editor, TRUE);
832 }
833
834 static void
835 action_close_cb (GtkAction *action,
836 CompEditor *editor)
837 {
838 commit_all_fields (editor);
839
840 if (prompt_and_save_changes (editor, TRUE))
841 close_dialog (editor);
842 }
843
844 static void
845 action_help_cb (GtkAction *action,
846 CompEditor *editor)
847 {
848 comp_editor_show_help (editor);
849 }
850
851 static void
852 action_print_cb (GtkAction *action,
853 CompEditor *editor)
854 {
855 CompEditorPrivate *priv = editor->priv;
856 ECalComponent *comp;
857 GList *l;
858 icalcomponent *component;
859 icalcomponent *clone;
860 icaltimezone *zone;
861 gboolean use_24_hour_format;
862
863 comp = e_cal_component_new ();
864 component = e_cal_component_get_icalcomponent (priv->comp);
865 clone = icalcomponent_new_clone (component);
866 e_cal_component_set_icalcomponent (comp, clone);
867
868 for (l = priv->pages; l != NULL; l = l->next)
869 comp_editor_page_fill_component (l->data, comp);
870
871 zone = comp_editor_get_timezone (editor);
872 use_24_hour_format = comp_editor_get_use_24_hour_format (editor);
873
874 print_comp (
875 comp, priv->cal_client, zone, use_24_hour_format,
876 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
877
878 g_object_unref (comp);
879 }
880
881 static void
882 action_print_preview_cb (GtkAction *action,
883 CompEditor *editor)
884 {
885 CompEditorPrivate *priv = editor->priv;
886 ECalComponent *comp;
887 GList *l;
888 icalcomponent *component;
889 icalcomponent *clone;
890 icaltimezone *zone;
891 gboolean use_24_hour_format;
892
893 comp = e_cal_component_new ();
894 component = e_cal_component_get_icalcomponent (priv->comp);
895 clone = icalcomponent_new_clone (component);
896 e_cal_component_set_icalcomponent (comp, clone);
897
898 for (l = priv->pages; l != NULL; l = l->next)
899 comp_editor_page_fill_component (l->data, comp);
900
901 zone = comp_editor_get_timezone (editor);
902 use_24_hour_format = comp_editor_get_use_24_hour_format (editor);
903
904 print_comp (
905 comp, priv->cal_client, zone, use_24_hour_format,
906 GTK_PRINT_OPERATION_ACTION_PREVIEW);
907
908 g_object_unref (comp);
909 }
910
911 static gboolean
912 remove_event_dialog (ECalClient *client,
913 ECalComponent *comp,
914 GtkWindow *parent)
915 {
916 GtkWidget *dialog;
917 gboolean ret;
918
919 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), TRUE);
920
921 dialog = gtk_message_dialog_new (
922 parent, 0, GTK_MESSAGE_QUESTION,
923 GTK_BUTTONS_YES_NO, "%s", _("Keep original item?"));
924 gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
925 ret = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
926 gtk_widget_destroy (dialog);
927
928 return ret;
929 }
930
931 static void
932 save_and_close_editor (CompEditor *editor,
933 gboolean can_close)
934 {
935 CompEditorPrivate *priv = editor->priv;
936 EAttachmentStore *store;
937 EAttachmentView *view;
938 ECalComponentText text;
939 gboolean delegated = FALSE;
940 gboolean correct = FALSE;
941 ECalComponent *comp;
942
943 view = E_ATTACHMENT_VIEW (priv->attachment_view);
944 store = e_attachment_view_get_store (view);
945
946 if (e_attachment_store_get_num_loading (store) > 0) {
947 gboolean response = 1;
948 /*FIXME: Cannot use mail functions from calendar!!!! */
949 #if 0
950 ECalComponentVType vtype = e_cal_component_get_vtype (editor->priv->comp);
951
952 if (vtype == E_CAL_COMPONENT_EVENT)
953 response = em_utils_prompt_user (
954 (GtkWindow *) widget,
955 NULL,
956 "calendar:ask-send-event-pending-download",
957 NULL);
958 else
959 response = em_utils_prompt_user (
960 (GtkWindow *) widget,
961 NULL,
962 "calendar:ask-send-task-pending-download",
963 NULL);
964 #endif
965 if (!response)
966 return;
967 }
968
969 if (e_client_is_readonly (E_CLIENT (priv->cal_client))) {
970 e_alert_submit (
971 E_ALERT_SINK (editor),
972 "calendar:prompt-read-only-cal-editor",
973 e_source_get_display_name (
974 e_client_get_source (E_CLIENT (priv->cal_client))),
975 NULL);
976 return;
977 }
978
979 if ((comp_editor_get_flags (editor) & COMP_EDITOR_IS_ASSIGNED) != 0
980 && e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_TODO
981 && e_client_check_capability (E_CLIENT (priv->cal_client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) {
982 e_alert_submit (
983 E_ALERT_SINK (editor),
984 "calendar:prompt-no-task-assignment-editor",
985 e_source_get_display_name (
986 e_client_get_source (E_CLIENT (priv->cal_client))),
987 NULL);
988 return;
989 }
990
991 commit_all_fields (editor);
992 if (e_cal_component_has_recurrences (priv->comp)) {
993 if (!recur_component_dialog (
994 priv->cal_client, priv->comp, &priv->mod,
995 GTK_WINDOW (editor), delegated))
996 return;
997 } else if (e_cal_component_is_instance (priv->comp))
998 priv->mod = CALOBJ_MOD_THIS;
999
1000 comp = comp_editor_get_current_comp (editor, &correct);
1001 e_cal_component_get_summary (comp, &text);
1002 g_object_unref (comp);
1003
1004 if (!correct)
1005 return;
1006
1007 if (!text.value)
1008 if (!send_component_prompt_subject (
1009 (GtkWindow *) editor, priv->cal_client, priv->comp))
1010 return;
1011
1012 gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
1013
1014 if (save_comp_with_send (editor)) {
1015 CompEditorFlags flags;
1016 gboolean delegate;
1017
1018 flags = comp_editor_get_flags (editor);
1019 delegate = flags & COMP_EDITOR_DELEGATE;
1020
1021 if (delegate && !remove_event_dialog (
1022 priv->cal_client, priv->comp, GTK_WINDOW (editor))) {
1023 const gchar *uid = NULL;
1024 GError *error = NULL;
1025
1026 e_cal_component_get_uid (priv->comp, &uid);
1027
1028 if (e_cal_component_is_instance (priv->comp) ||
1029 e_cal_component_has_recurrences (priv->comp)) {
1030 gchar *rid;
1031 rid = e_cal_component_get_recurid_as_string (priv->comp);
1032 e_cal_client_remove_object_sync (
1033 priv->cal_client, uid, rid,
1034 priv->mod, NULL, &error);
1035 g_free (rid);
1036 } else
1037 e_cal_client_remove_object_sync (
1038 priv->cal_client, uid, NULL,
1039 CALOBJ_MOD_THIS, NULL, &error);
1040
1041 g_clear_error (&error);
1042 }
1043 } else
1044 correct = FALSE;
1045
1046 gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
1047
1048 if (correct) {
1049 if (can_close)
1050 close_dialog (editor);
1051 else
1052 comp_editor_set_changed (editor, FALSE);
1053 }
1054 }
1055
1056 static void
1057 action_save_cb (GtkAction *action,
1058 CompEditor *editor)
1059 {
1060 save_and_close_editor (editor, FALSE);
1061 }
1062
1063 static void
1064 action_save_and_close_cb (GtkAction *action,
1065 CompEditor *editor)
1066 {
1067 save_and_close_editor (editor, TRUE);
1068 }
1069
1070 static void
1071 action_view_categories_cb (GtkToggleAction *action,
1072 CompEditor *editor)
1073 {
1074 CompEditorClass *class;
1075 gboolean active;
1076
1077 class = COMP_EDITOR_GET_CLASS (editor);
1078 active = gtk_toggle_action_get_active (action);
1079
1080 if (class->show_categories != NULL)
1081 class->show_categories (editor, active);
1082 }
1083
1084 static void
1085 action_view_role_cb (GtkToggleAction *action,
1086 CompEditor *editor)
1087 {
1088 CompEditorClass *class;
1089 gboolean active;
1090
1091 class = COMP_EDITOR_GET_CLASS (editor);
1092 active = gtk_toggle_action_get_active (action);
1093
1094 if (class->show_role != NULL)
1095 class->show_role (editor, active);
1096 }
1097
1098 static void
1099 action_view_rsvp_cb (GtkToggleAction *action,
1100 CompEditor *editor)
1101 {
1102 CompEditorClass *class;
1103 gboolean active;
1104
1105 class = COMP_EDITOR_GET_CLASS (editor);
1106 active = gtk_toggle_action_get_active (action);
1107
1108 if (class->show_rsvp != NULL)
1109 class->show_rsvp (editor, active);
1110 }
1111
1112 static void
1113 action_view_status_cb (GtkToggleAction *action,
1114 CompEditor *editor)
1115 {
1116 CompEditorClass *class;
1117 gboolean active;
1118
1119 class = COMP_EDITOR_GET_CLASS (editor);
1120 active = gtk_toggle_action_get_active (action);
1121
1122 if (class->show_status != NULL)
1123 class->show_status (editor, active);
1124 }
1125
1126 static void
1127 action_view_time_zone_cb (GtkToggleAction *action,
1128 CompEditor *editor)
1129 {
1130 CompEditorClass *class;
1131 gboolean active;
1132
1133 class = COMP_EDITOR_GET_CLASS (editor);
1134 active = gtk_toggle_action_get_active (action);
1135
1136 if (class->show_time_zone != NULL)
1137 class->show_time_zone (editor, active);
1138 }
1139
1140 static void
1141 action_view_type_cb (GtkToggleAction *action,
1142 CompEditor *editor)
1143 {
1144 CompEditorClass *class;
1145 gboolean active;
1146
1147 class = COMP_EDITOR_GET_CLASS (editor);
1148 active = gtk_toggle_action_get_active (action);
1149
1150 if (class->show_type != NULL)
1151 class->show_type (editor, active);
1152 }
1153
1154 static GtkActionEntry core_entries[] = {
1155
1156 { "close",
1157 GTK_STOCK_CLOSE,
1158 NULL,
1159 NULL,
1160 N_("Close the current window"),
1161 G_CALLBACK (action_close_cb) },
1162
1163 { "copy-clipboard",
1164 GTK_STOCK_COPY,
1165 NULL,
1166 NULL,
1167 N_("Copy the selection"),
1168 NULL }, /* Handled by EFocusTracker */
1169
1170 { "cut-clipboard",
1171 GTK_STOCK_CUT,
1172 NULL,
1173 NULL,
1174 N_("Cut the selection"),
1175 NULL }, /* Handled by EFocusTracker */
1176
1177 { "delete-selection",
1178 GTK_STOCK_DELETE,
1179 NULL,
1180 NULL,
1181 N_("Delete the selection"),
1182 NULL }, /* Handled by EFocusTracker */
1183
1184 { "help",
1185 GTK_STOCK_HELP,
1186 NULL,
1187 NULL,
1188 N_("View help"),
1189 G_CALLBACK (action_help_cb) },
1190
1191 { "paste-clipboard",
1192 GTK_STOCK_PASTE,
1193 NULL,
1194 NULL,
1195 N_("Paste the clipboard"),
1196 NULL }, /* Handled by EFocusTracker */
1197
1198 { "print",
1199 GTK_STOCK_PRINT,
1200 NULL,
1201 "<Control>p",
1202 NULL,
1203 G_CALLBACK (action_print_cb) },
1204
1205 { "print-preview",
1206 GTK_STOCK_PRINT_PREVIEW,
1207 NULL,
1208 NULL,
1209 NULL,
1210 G_CALLBACK (action_print_preview_cb) },
1211
1212 { "save",
1213 GTK_STOCK_SAVE,
1214 NULL,
1215 NULL,
1216 N_("Save current changes"),
1217 G_CALLBACK (action_save_cb) },
1218
1219 { "save-and-close",
1220 NULL,
1221 N_("Save and Close"),
1222 NULL,
1223 N_("Save current changes and close editor"),
1224 G_CALLBACK (action_save_and_close_cb) },
1225
1226 { "select-all",
1227 GTK_STOCK_SELECT_ALL,
1228 NULL,
1229 "<Control>a",
1230 N_("Select all text"),
1231 NULL }, /* Handled by EFocusTracker */
1232
1233 /* Menus */
1234
1235 { "classification-menu",
1236 NULL,
1237 N_("_Classification"),
1238 NULL,
1239 NULL,
1240 NULL },
1241
1242 { "edit-menu",
1243 NULL,
1244 N_("_Edit"),
1245 NULL,
1246 NULL,
1247 NULL },
1248
1249 { "file-menu",
1250 NULL,
1251 N_("_File"),
1252 NULL,
1253 NULL,
1254 NULL },
1255
1256 { "help-menu",
1257 NULL,
1258 N_("_Help"),
1259 NULL,
1260 NULL,
1261 NULL },
1262
1263 { "insert-menu",
1264 NULL,
1265 N_("_Insert"),
1266 NULL,
1267 NULL,
1268 NULL },
1269
1270 { "options-menu",
1271 NULL,
1272 N_("_Options"),
1273 NULL,
1274 NULL,
1275 NULL },
1276
1277 { "view-menu",
1278 NULL,
1279 N_("_View"),
1280 NULL,
1281 NULL,
1282 NULL }
1283 };
1284
1285 static GtkActionEntry individual_entries[] = {
1286
1287 { "attach",
1288 "mail-attachment",
1289 N_("_Attachment..."),
1290 "<Control>m",
1291 N_("Attach a file"),
1292 G_CALLBACK (action_attach_cb) }
1293 };
1294
1295 static GtkToggleActionEntry core_toggle_entries[] = {
1296
1297 { "view-categories",
1298 NULL,
1299 N_("_Categories"),
1300 NULL,
1301 N_("Toggles whether to display categories"),
1302 G_CALLBACK (action_view_categories_cb),
1303 FALSE },
1304
1305 { "view-time-zone",
1306 "stock_timezone",
1307 N_("Time _Zone"),
1308 NULL,
1309 N_("Toggles whether the time zone is displayed"),
1310 G_CALLBACK (action_view_time_zone_cb),
1311 FALSE }
1312 };
1313
1314 static GtkRadioActionEntry classification_radio_entries[] = {
1315
1316 { "classify-public",
1317 NULL,
1318 N_("Pu_blic"),
1319 NULL,
1320 N_("Classify as public"),
1321 E_CAL_COMPONENT_CLASS_PUBLIC },
1322
1323 { "classify-private",
1324 NULL,
1325 N_("_Private"),
1326 NULL,
1327 N_("Classify as private"),
1328 E_CAL_COMPONENT_CLASS_PRIVATE },
1329
1330 { "classify-confidential",
1331 NULL,
1332 N_("_Confidential"),
1333 NULL,
1334 N_("Classify as confidential"),
1335 E_CAL_COMPONENT_CLASS_CONFIDENTIAL }
1336 };
1337
1338 static GtkToggleActionEntry coordinated_toggle_entries[] = {
1339
1340 { "view-role",
1341 NULL,
1342 N_("R_ole Field"),
1343 NULL,
1344 N_("Toggles whether the Role field is displayed"),
1345 G_CALLBACK (action_view_role_cb),
1346 FALSE },
1347
1348 { "view-rsvp",
1349 NULL,
1350 N_("_RSVP"),
1351 NULL,
1352 N_("Toggles whether the RSVP field is displayed"),
1353 G_CALLBACK (action_view_rsvp_cb),
1354 FALSE },
1355
1356 { "view-status",
1357 NULL,
1358 N_("_Status Field"),
1359 NULL,
1360 N_("Toggles whether the Status field is displayed"),
1361 G_CALLBACK (action_view_status_cb),
1362 FALSE },
1363
1364 { "view-type",
1365 NULL,
1366 N_("_Type Field"),
1367 NULL,
1368 N_("Toggles whether the Attendee Type is displayed"),
1369 G_CALLBACK (action_view_type_cb),
1370 FALSE }
1371 };
1372
1373 static void
1374 comp_editor_set_shell (CompEditor *editor,
1375 EShell *shell)
1376 {
1377 g_return_if_fail (E_IS_SHELL (shell));
1378 g_return_if_fail (editor->priv->shell == NULL);
1379
1380 editor->priv->shell = shell;
1381
1382 g_object_add_weak_pointer (G_OBJECT (shell), &editor->priv->shell);
1383 }
1384
1385 static void
1386 comp_editor_set_property (GObject *object,
1387 guint property_id,
1388 const GValue *value,
1389 GParamSpec *pspec)
1390 {
1391 switch (property_id) {
1392 case PROP_CHANGED:
1393 comp_editor_set_changed (
1394 COMP_EDITOR (object),
1395 g_value_get_boolean (value));
1396 return;
1397
1398 case PROP_CLIENT:
1399 comp_editor_set_client (
1400 COMP_EDITOR (object),
1401 g_value_get_object (value));
1402 return;
1403
1404 case PROP_FLAGS:
1405 comp_editor_set_flags (
1406 COMP_EDITOR (object),
1407 g_value_get_int (value));
1408 return;
1409
1410 case PROP_SHELL:
1411 comp_editor_set_shell (
1412 COMP_EDITOR (object),
1413 g_value_get_object (value));
1414 return;
1415
1416 case PROP_SUMMARY:
1417 comp_editor_set_summary (
1418 COMP_EDITOR (object),
1419 g_value_get_string (value));
1420 return;
1421
1422 case PROP_TIMEZONE:
1423 comp_editor_set_timezone (
1424 COMP_EDITOR (object),
1425 g_value_get_pointer (value));
1426 return;
1427
1428 case PROP_USE_24_HOUR_FORMAT:
1429 comp_editor_set_use_24_hour_format (
1430 COMP_EDITOR (object),
1431 g_value_get_boolean (value));
1432 return;
1433
1434 case PROP_WORK_DAY_END_HOUR:
1435 comp_editor_set_work_day_end_hour (
1436 COMP_EDITOR (object),
1437 g_value_get_int (value));
1438 return;
1439
1440 case PROP_WORK_DAY_END_MINUTE:
1441 comp_editor_set_work_day_end_minute (
1442 COMP_EDITOR (object),
1443 g_value_get_int (value));
1444 return;
1445
1446 case PROP_WORK_DAY_START_HOUR:
1447 comp_editor_set_work_day_start_hour (
1448 COMP_EDITOR (object),
1449 g_value_get_int (value));
1450 return;
1451
1452 case PROP_WORK_DAY_START_MINUTE:
1453 comp_editor_set_work_day_start_minute (
1454 COMP_EDITOR (object),
1455 g_value_get_int (value));
1456 return;
1457 }
1458
1459 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1460 }
1461
1462 static void
1463 comp_editor_get_property (GObject *object,
1464 guint property_id,
1465 GValue *value,
1466 GParamSpec *pspec)
1467 {
1468 switch (property_id) {
1469 case PROP_CHANGED:
1470 g_value_set_boolean (
1471 value, comp_editor_get_changed (
1472 COMP_EDITOR (object)));
1473 return;
1474
1475 case PROP_CLIENT:
1476 g_value_set_object (
1477 value, comp_editor_get_client (
1478 COMP_EDITOR (object)));
1479 return;
1480
1481 case PROP_FLAGS:
1482 g_value_set_int (
1483 value, comp_editor_get_flags (
1484 COMP_EDITOR (object)));
1485 return;
1486
1487 case PROP_FOCUS_TRACKER:
1488 g_value_set_object (
1489 value, comp_editor_get_focus_tracker (
1490 COMP_EDITOR (object)));
1491 return;
1492
1493 case PROP_SHELL:
1494 g_value_set_object (
1495 value, comp_editor_get_shell (
1496 COMP_EDITOR (object)));
1497 return;
1498
1499 case PROP_SUMMARY:
1500 g_value_set_string (
1501 value, comp_editor_get_summary (
1502 COMP_EDITOR (object)));
1503 return;
1504
1505 case PROP_TIMEZONE:
1506 g_value_set_pointer (
1507 value, comp_editor_get_timezone (
1508 COMP_EDITOR (object)));
1509 return;
1510
1511 case PROP_USE_24_HOUR_FORMAT:
1512 g_value_set_boolean (
1513 value, comp_editor_get_use_24_hour_format (
1514 COMP_EDITOR (object)));
1515 return;
1516
1517 case PROP_WORK_DAY_END_HOUR:
1518 g_value_set_int (
1519 value, comp_editor_get_work_day_end_hour (
1520 COMP_EDITOR (object)));
1521 return;
1522
1523 case PROP_WORK_DAY_END_MINUTE:
1524 g_value_set_int (
1525 value, comp_editor_get_work_day_end_minute (
1526 COMP_EDITOR (object)));
1527 return;
1528
1529 case PROP_WORK_DAY_START_HOUR:
1530 g_value_set_int (
1531 value, comp_editor_get_work_day_start_hour (
1532 COMP_EDITOR (object)));
1533 return;
1534
1535 case PROP_WORK_DAY_START_MINUTE:
1536 g_value_set_int (
1537 value, comp_editor_get_work_day_start_minute (
1538 COMP_EDITOR (object)));
1539 return;
1540 }
1541
1542 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1543 }
1544
1545 static void
1546 unref_page_cb (gpointer editor_page,
1547 gpointer comp_editor)
1548 {
1549 if (IS_COMP_EDITOR_PAGE (editor_page)) {
1550 GtkWidget *page_widget;
1551 CompEditorPage *page = COMP_EDITOR_PAGE (editor_page);
1552 CompEditor *editor = COMP_EDITOR (comp_editor);
1553
1554 g_return_if_fail (page != NULL);
1555 g_return_if_fail (editor != NULL);
1556
1557 page_widget = comp_editor_page_get_widget (page);
1558 g_signal_handlers_disconnect_matched (
1559 page_widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page);
1560 }
1561
1562 g_signal_handlers_disconnect_matched (
1563 editor_page, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, comp_editor);
1564 g_object_unref (editor_page);
1565 }
1566
1567 static void
1568 comp_editor_dispose (GObject *object)
1569 {
1570 CompEditorPrivate *priv;
1571
1572 priv = COMP_EDITOR_GET_PRIVATE (object);
1573
1574 if (priv->shell != NULL) {
1575 g_object_remove_weak_pointer (
1576 G_OBJECT (priv->shell), &priv->shell);
1577 priv->shell = NULL;
1578 }
1579
1580 if (priv->focus_tracker != NULL) {
1581 g_object_unref (priv->focus_tracker);
1582 priv->focus_tracker = NULL;
1583 }
1584
1585 if (priv->window_group != NULL) {
1586 g_object_unref (priv->window_group);
1587 priv->window_group = NULL;
1588 }
1589
1590 if (priv->cal_client) {
1591 g_object_unref (priv->cal_client);
1592 priv->cal_client = NULL;
1593 }
1594
1595 if (priv->source_client) {
1596 g_object_unref (priv->source_client);
1597 priv->source_client = NULL;
1598 }
1599
1600 if (priv->view_cancellable) {
1601 g_cancellable_cancel (priv->view_cancellable);
1602 g_object_unref (priv->view_cancellable);
1603 priv->view_cancellable = NULL;
1604 }
1605
1606 if (priv->view) {
1607 g_signal_handlers_disconnect_matched (
1608 priv->view, G_SIGNAL_MATCH_DATA,
1609 0, 0, NULL, NULL, object);
1610 g_object_unref (priv->view);
1611 priv->view = NULL;
1612 }
1613
1614 if (priv->attachment_view) {
1615 EAttachmentStore *store;
1616
1617 store = e_attachment_view_get_store (
1618 E_ATTACHMENT_VIEW (priv->attachment_view));
1619 g_signal_handlers_disconnect_matched (
1620 store, G_SIGNAL_MATCH_DATA,
1621 0, 0, NULL, NULL, object);
1622 g_object_unref (priv->attachment_view);
1623 priv->attachment_view = NULL;
1624 }
1625
1626 /* We want to destroy the pages after the widgets get destroyed,
1627 * since they have lots of signal handlers connected to the widgets
1628 * with the pages as the data. */
1629 g_list_foreach (priv->pages, (GFunc) unref_page_cb, object);
1630 g_list_free (priv->pages);
1631 priv->pages = NULL;
1632
1633 if (priv->comp) {
1634 g_object_unref (priv->comp);
1635 priv->comp = NULL;
1636 }
1637
1638 if (priv->ui_manager != NULL) {
1639 g_object_unref (priv->ui_manager);
1640 priv->ui_manager = NULL;
1641 }
1642
1643 /* Chain up to parent's dispose() method. */
1644 G_OBJECT_CLASS (comp_editor_parent_class)->dispose (object);
1645 }
1646
1647 static void
1648 comp_editor_finalize (GObject *object)
1649 {
1650 CompEditorPrivate *priv;
1651
1652 priv = COMP_EDITOR_GET_PRIVATE (object);
1653
1654 g_object_unref (priv->calendar_settings);
1655 g_free (priv->summary);
1656
1657 /* Chain up to parent's finalize() method. */
1658 G_OBJECT_CLASS (comp_editor_parent_class)->finalize (object);
1659 }
1660
1661 static void
1662 comp_editor_constructed (GObject *object)
1663 {
1664 e_extensible_load_extensions (E_EXTENSIBLE (object));
1665
1666 /* Chain up to parent's constructed() method. */
1667 G_OBJECT_CLASS (comp_editor_parent_class)->constructed (object);
1668 }
1669
1670 static void
1671 comp_editor_bind_settings (CompEditor *editor)
1672 {
1673 GtkAction *action;
1674
1675 g_return_if_fail (editor != NULL);
1676
1677 action = comp_editor_get_action (editor, "view-categories");
1678 g_settings_bind (
1679 editor->priv->calendar_settings, "editor-show-categories",
1680 action, "active",
1681 G_SETTINGS_BIND_DEFAULT);
1682
1683 action = comp_editor_get_action (editor, "view-role");
1684 g_settings_bind (
1685 editor->priv->calendar_settings, "editor-show-role",
1686 action, "active",
1687 G_SETTINGS_BIND_DEFAULT);
1688
1689 action = comp_editor_get_action (editor, "view-rsvp");
1690 g_settings_bind (
1691 editor->priv->calendar_settings, "editor-show-rsvp",
1692 action, "active",
1693 G_SETTINGS_BIND_DEFAULT);
1694
1695 action = comp_editor_get_action (editor, "view-status");
1696 g_settings_bind (
1697 editor->priv->calendar_settings, "editor-show-status",
1698 action, "active",
1699 G_SETTINGS_BIND_DEFAULT);
1700
1701 action = comp_editor_get_action (editor, "view-time-zone");
1702 g_settings_bind (
1703 editor->priv->calendar_settings, "editor-show-timezone",
1704 action, "active",
1705 G_SETTINGS_BIND_DEFAULT);
1706
1707 action = comp_editor_get_action (editor, "view-type");
1708 g_settings_bind (
1709 editor->priv->calendar_settings, "editor-show-type",
1710 action, "active",
1711 G_SETTINGS_BIND_DEFAULT);
1712 }
1713
1714 static gboolean
1715 comp_editor_delete_event (GtkWidget *widget,
1716 GdkEventAny *event)
1717 {
1718 CompEditor *editor;
1719
1720 editor = COMP_EDITOR (widget);
1721
1722 commit_all_fields (editor);
1723
1724 if (prompt_and_save_changes (editor, TRUE))
1725 close_dialog (editor);
1726
1727 return TRUE;
1728 }
1729
1730 static gboolean
1731 comp_editor_key_press_event (GtkWidget *widget,
1732 GdkEventKey *event)
1733 {
1734 CompEditor *editor;
1735
1736 editor = COMP_EDITOR (widget);
1737
1738 if (event->keyval == GDK_KEY_Escape) {
1739 commit_all_fields (editor);
1740
1741 if (prompt_and_save_changes (editor, TRUE))
1742 close_dialog (editor);
1743
1744 return TRUE;
1745 }
1746
1747 /* Chain up to parent's key_press_event() method. */
1748 return GTK_WIDGET_CLASS (comp_editor_parent_class)->
1749 key_press_event (widget, event);
1750 }
1751
1752 static gboolean
1753 comp_editor_drag_motion (GtkWidget *widget,
1754 GdkDragContext *context,
1755 gint x,
1756 gint y,
1757 guint time)
1758 {
1759 CompEditorPrivate *priv;
1760 EAttachmentView *view;
1761
1762 priv = COMP_EDITOR_GET_PRIVATE (widget);
1763 view = E_ATTACHMENT_VIEW (priv->attachment_view);
1764
1765 return e_attachment_view_drag_motion (view, context, x, y, time);
1766 }
1767
1768 static void
1769 comp_editor_drag_data_received (GtkWidget *widget,
1770 GdkDragContext *context,
1771 gint x,
1772 gint y,
1773 GtkSelectionData *selection,
1774 guint info,
1775 guint time)
1776 {
1777 CompEditorPrivate *priv;
1778 EAttachmentView *view;
1779
1780 priv = COMP_EDITOR_GET_PRIVATE (widget);
1781 view = E_ATTACHMENT_VIEW (priv->attachment_view);
1782
1783 /* Forward the data to the attachment view. Note that calling
1784 * e_attachment_view_drag_data_received() will not work because
1785 * that function only handles the case where all the other drag
1786 * handlers have failed. */
1787 e_attachment_paned_drag_data_received (
1788 E_ATTACHMENT_PANED (view),
1789 context, x, y, selection, info, time);
1790 }
1791
1792 static void
1793 comp_editor_class_init (CompEditorClass *class)
1794 {
1795 GObjectClass *object_class;
1796 GtkWidgetClass *widget_class;
1797
1798 g_type_class_add_private (class, sizeof (CompEditorPrivate));
1799
1800 object_class = G_OBJECT_CLASS (class);
1801 object_class->set_property = comp_editor_set_property;
1802 object_class->get_property = comp_editor_get_property;
1803 object_class->dispose = comp_editor_dispose;
1804 object_class->finalize = comp_editor_finalize;
1805 object_class->constructed = comp_editor_constructed;
1806
1807 widget_class = GTK_WIDGET_CLASS (class);
1808 widget_class->delete_event = comp_editor_delete_event;
1809 widget_class->key_press_event = comp_editor_key_press_event;
1810 widget_class->drag_motion = comp_editor_drag_motion;
1811 widget_class->drag_data_received = comp_editor_drag_data_received;
1812
1813 class->help_section = "memos-usage";
1814 class->edit_comp = real_edit_comp;
1815 class->send_comp = real_send_comp;
1816 class->object_created = NULL;
1817
1818 g_object_class_install_property (
1819 object_class,
1820 PROP_CHANGED,
1821 g_param_spec_boolean (
1822 "changed",
1823 NULL,
1824 NULL,
1825 FALSE,
1826 G_PARAM_READWRITE));
1827
1828 g_object_class_install_property (
1829 object_class,
1830 PROP_CLIENT,
1831 g_param_spec_object (
1832 "client",
1833 NULL,
1834 NULL,
1835 E_TYPE_CAL_CLIENT,
1836 G_PARAM_READWRITE |
1837 G_PARAM_CONSTRUCT));
1838
1839 /* FIXME: Use a proper flags type instead of int. */
1840 g_object_class_install_property (
1841 object_class,
1842 PROP_FLAGS,
1843 g_param_spec_int (
1844 "flags",
1845 NULL,
1846 NULL,
1847 G_MININT,
1848 G_MAXINT,
1849 0,
1850 G_PARAM_READWRITE |
1851 G_PARAM_CONSTRUCT));
1852
1853 g_object_class_install_property (
1854 object_class,
1855 PROP_FOCUS_TRACKER,
1856 g_param_spec_object (
1857 "focus-tracker",
1858 NULL,
1859 NULL,
1860 E_TYPE_FOCUS_TRACKER,
1861 G_PARAM_READABLE));
1862
1863 g_object_class_install_property (
1864 object_class,
1865 PROP_SHELL,
1866 g_param_spec_object (
1867 "shell",
1868 NULL,
1869 NULL,
1870 E_TYPE_SHELL,
1871 G_PARAM_READWRITE |
1872 G_PARAM_CONSTRUCT_ONLY));
1873
1874 g_object_class_install_property (
1875 object_class,
1876 PROP_SUMMARY,
1877 g_param_spec_string (
1878 "summary",
1879 NULL,
1880 NULL,
1881 NULL,
1882 G_PARAM_READWRITE));
1883
1884 g_object_class_install_property (
1885 object_class,
1886 PROP_TIMEZONE,
1887 g_param_spec_pointer (
1888 "timezone",
1889 "Time Zone",
1890 NULL,
1891 G_PARAM_READWRITE));
1892
1893 g_object_class_install_property (
1894 object_class,
1895 PROP_USE_24_HOUR_FORMAT,
1896 g_param_spec_boolean (
1897 "use-24-hour-format",
1898 "Use 24-hour Format",
1899 NULL,
1900 FALSE,
1901 G_PARAM_READWRITE));
1902
1903 g_object_class_install_property (
1904 object_class,
1905 PROP_WORK_DAY_END_HOUR,
1906 g_param_spec_int (
1907 "work-day-end-hour",
1908 "Work Day End Hour",
1909 NULL,
1910 0,
1911 23,
1912 0,
1913 G_PARAM_READWRITE));
1914
1915 g_object_class_install_property (
1916 object_class,
1917 PROP_WORK_DAY_END_MINUTE,
1918 g_param_spec_int (
1919 "work-day-end-minute",
1920 "Work Day End Minute",
1921 NULL,
1922 0,
1923 59,
1924 0,
1925 G_PARAM_READWRITE));
1926
1927 g_object_class_install_property (
1928 object_class,
1929 PROP_WORK_DAY_START_HOUR,
1930 g_param_spec_int (
1931 "work-day-start-hour",
1932 "Work Day Start Hour",
1933 NULL,
1934 0,
1935 23,
1936 0,
1937 G_PARAM_READWRITE));
1938
1939 g_object_class_install_property (
1940 object_class,
1941 PROP_WORK_DAY_START_MINUTE,
1942 g_param_spec_int (
1943 "work-day-start-minute",
1944 "Work Day Start Minute",
1945 NULL,
1946 0,
1947 59,
1948 0,
1949 G_PARAM_READWRITE));
1950
1951 signals[OBJECT_CREATED] = g_signal_new (
1952 "object_created",
1953 G_TYPE_FROM_CLASS (class),
1954 G_SIGNAL_RUN_LAST,
1955 G_STRUCT_OFFSET (CompEditorClass, object_created),
1956 NULL, NULL,
1957 g_cclosure_marshal_VOID__VOID,
1958 G_TYPE_NONE, 0);
1959
1960 signals[COMP_CLOSED] = g_signal_new (
1961 "comp_closed",
1962 G_TYPE_FROM_CLASS (class),
1963 G_SIGNAL_RUN_LAST,
1964 G_STRUCT_OFFSET (CompEditorClass, comp_closed),
1965 NULL, NULL,
1966 g_cclosure_marshal_VOID__BOOLEAN,
1967 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
1968 }
1969
1970 static void
1971 comp_editor_init (CompEditor *editor)
1972 {
1973 CompEditorPrivate *priv;
1974 EAttachmentView *view;
1975 EAttachmentStore *store;
1976 EFocusTracker *focus_tracker;
1977 GdkDragAction drag_actions;
1978 GtkTargetList *target_list;
1979 GtkTargetEntry *targets;
1980 GtkActionGroup *action_group;
1981 GtkActionGroup *action_group_2;
1982 GtkAction *action;
1983 GtkWidget *container;
1984 GtkWidget *widget;
1985 GtkWindow *window;
1986 EShell *shell;
1987 gboolean express_mode;
1988 gboolean meego_mode;
1989 gint n_targets;
1990 GError *error = NULL;
1991
1992 /* FIXME We already have a 'shell' property. Move stuff
1993 * that depends on it to a constructed() method. */
1994 shell = e_shell_get_default ();
1995 express_mode = e_shell_get_express_mode (shell);
1996 meego_mode = e_shell_get_meego_mode (shell);
1997
1998 editor->priv = priv = COMP_EDITOR_GET_PRIVATE (editor);
1999
2000 g_object_weak_ref (
2001 G_OBJECT (editor), (GWeakNotify)
2002 comp_editor_weak_notify_cb, NULL);
2003
2004 active_editors = g_list_prepend (active_editors, editor);
2005
2006 priv->calendar_settings = g_settings_new ("org.gnome.evolution.calendar");
2007
2008 /* Each editor window gets its own window group. */
2009 window = GTK_WINDOW (editor);
2010 priv->window_group = gtk_window_group_new ();
2011 gtk_window_group_add_window (priv->window_group, window);
2012
2013 priv->pages = NULL;
2014 priv->changed = FALSE;
2015 priv->needs_send = FALSE;
2016 priv->mod = CALOBJ_MOD_ALL;
2017 priv->existing_org = FALSE;
2018 priv->user_org = FALSE;
2019 priv->warned = FALSE;
2020 priv->is_group_item = FALSE;
2021 priv->saved = FALSE;
2022
2023 priv->ui_manager = e_ui_manager_new ();
2024 e_ui_manager_set_express_mode (
2025 E_UI_MANAGER (priv->ui_manager), express_mode);
2026
2027 gtk_window_add_accel_group (
2028 GTK_WINDOW (editor),
2029 gtk_ui_manager_get_accel_group (priv->ui_manager));
2030
2031 /* Setup Action Groups */
2032
2033 action_group = gtk_action_group_new ("core");
2034 gtk_action_group_set_translation_domain (
2035 action_group, GETTEXT_PACKAGE);
2036 gtk_action_group_add_actions (
2037 action_group, core_entries,
2038 G_N_ELEMENTS (core_entries), editor);
2039 gtk_action_group_add_toggle_actions (
2040 action_group, core_toggle_entries,
2041 G_N_ELEMENTS (core_toggle_entries), editor);
2042 gtk_ui_manager_insert_action_group (
2043 priv->ui_manager, action_group, 0);
2044 g_object_unref (action_group);
2045
2046 action = gtk_action_group_get_action (action_group, "save-and-close");
2047 if (action) {
2048 GtkAction *save_action;
2049 GIcon *icon;
2050 GIcon *emblemed_icon;
2051 GEmblem *emblem;
2052
2053 icon = g_themed_icon_new (GTK_STOCK_CLOSE);
2054 emblemed_icon = g_themed_icon_new (GTK_STOCK_SAVE);
2055 emblem = g_emblem_new (emblemed_icon);
2056 g_object_unref (emblemed_icon);
2057
2058 emblemed_icon = g_emblemed_icon_new (icon, emblem);
2059 g_object_unref (emblem);
2060 g_object_unref (icon);
2061
2062 gtk_action_set_gicon (action, emblemed_icon);
2063
2064 g_object_unref (emblemed_icon);
2065
2066 save_action = gtk_action_group_get_action (action_group, "save");
2067 g_object_bind_property (
2068 save_action, "sensitive",
2069 action, "sensitive",
2070 G_BINDING_SYNC_CREATE);
2071 }
2072
2073 action_group = gtk_action_group_new ("individual");
2074 gtk_action_group_set_translation_domain (
2075 action_group, GETTEXT_PACKAGE);
2076 gtk_action_group_add_actions (
2077 action_group, individual_entries,
2078 G_N_ELEMENTS (individual_entries), editor);
2079 gtk_action_group_add_radio_actions (
2080 action_group, classification_radio_entries,
2081 G_N_ELEMENTS (classification_radio_entries),
2082 E_CAL_COMPONENT_CLASS_PUBLIC,
2083 G_CALLBACK (action_classification_cb), editor);
2084 gtk_ui_manager_insert_action_group (
2085 priv->ui_manager, action_group, 0);
2086 g_object_unref (action_group);
2087
2088 action_group = gtk_action_group_new ("editable");
2089 gtk_action_group_set_translation_domain (
2090 action_group, GETTEXT_PACKAGE);
2091 gtk_ui_manager_insert_action_group (
2092 priv->ui_manager, action_group, 0);
2093 g_object_unref (action_group);
2094
2095 action_group = gtk_action_group_new ("coordinated");
2096 gtk_action_group_set_translation_domain (
2097 action_group, GETTEXT_PACKAGE);
2098 gtk_action_group_add_toggle_actions (
2099 action_group, coordinated_toggle_entries,
2100 G_N_ELEMENTS (coordinated_toggle_entries), editor);
2101 gtk_ui_manager_insert_action_group (
2102 priv->ui_manager, action_group, 0);
2103 g_object_unref (action_group);
2104
2105 /* Configure an EFocusTracker to manage selection actions. */
2106
2107 focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor));
2108
2109 action = comp_editor_get_action (editor, "cut-clipboard");
2110 e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
2111
2112 action = comp_editor_get_action (editor, "copy-clipboard");
2113 e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
2114
2115 action = comp_editor_get_action (editor, "paste-clipboard");
2116 e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
2117
2118 action = comp_editor_get_action (editor, "delete-selection");
2119 e_focus_tracker_set_delete_selection_action (focus_tracker, action);
2120
2121 action = comp_editor_get_action (editor, "select-all");
2122 e_focus_tracker_set_select_all_action (focus_tracker, action);
2123
2124 priv->focus_tracker = focus_tracker;
2125
2126 /* Fine Tuning */
2127
2128 action = comp_editor_get_action (editor, "attach");
2129 g_object_set (action, "short-label", _("Attach"), NULL);
2130
2131 /* Desensitize the "save" action. */
2132 action = comp_editor_get_action (editor, "save");
2133 gtk_action_set_sensitive (action, FALSE);
2134
2135 e_ui_manager_add_ui_from_string (E_UI_MANAGER (priv->ui_manager), ui, &error);
2136 if (error != NULL) {
2137 g_warning ("%s: %s", G_STRFUNC, error->message);
2138 g_error_free (error);
2139 }
2140
2141 /* Setup Widgets */
2142
2143 container = GTK_WIDGET (editor);
2144
2145 widget = gtk_vbox_new (FALSE, 0);
2146 gtk_container_add (GTK_CONTAINER (container), widget);
2147 gtk_widget_show (widget);
2148
2149 container = widget;
2150
2151 if (!express_mode) {
2152 widget = comp_editor_get_managed_widget (editor, "/main-menu");
2153 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
2154 gtk_widget_set_visible (widget, !meego_mode);
2155 }
2156
2157 widget = comp_editor_get_managed_widget (editor, "/main-toolbar");
2158 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
2159 gtk_widget_show (widget);
2160
2161 gtk_style_context_add_class (
2162 gtk_widget_get_style_context (widget),
2163 GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
2164
2165 widget = e_attachment_paned_new ();
2166 e_attachment_paned_set_resize_toplevel (
2167 E_ATTACHMENT_PANED (widget), TRUE);
2168 gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
2169 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
2170 priv->attachment_view = g_object_ref (widget);
2171 gtk_widget_show (widget);
2172
2173 if (express_mode) {
2174 widget = e_attachment_paned_get_view_combo (
2175 E_ATTACHMENT_PANED (widget));
2176 gtk_widget_hide (widget);
2177 }
2178
2179 container = e_attachment_paned_get_content_area (
2180 E_ATTACHMENT_PANED (priv->attachment_view));
2181
2182 if (meego_mode) {
2183 widget = gtk_scrolled_window_new (NULL, NULL);
2184 gtk_scrolled_window_set_policy (
2185 GTK_SCROLLED_WINDOW (widget),
2186 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2187 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
2188 gtk_widget_set_size_request (widget, 300, -1);
2189 gtk_widget_show (widget);
2190
2191 container = widget;
2192 }
2193
2194 widget = gtk_notebook_new ();
2195 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), express_mode);
2196 if (!meego_mode)
2197 gtk_box_pack_start (
2198 GTK_BOX (container), widget, TRUE, TRUE, 0);
2199 else
2200 gtk_scrolled_window_add_with_viewport (
2201 GTK_SCROLLED_WINDOW (container), widget);
2202 priv->notebook = GTK_NOTEBOOK (widget);
2203 gtk_widget_show (widget);
2204
2205 /* Drag-and-Drop Support */
2206
2207 view = E_ATTACHMENT_VIEW (priv->attachment_view);
2208 target_list = e_attachment_view_get_target_list (view);
2209 drag_actions = e_attachment_view_get_drag_actions (view);
2210
2211 targets = gtk_target_table_new_from_list (target_list, &n_targets);
2212
2213 gtk_drag_dest_set (
2214 GTK_WIDGET (editor), GTK_DEST_DEFAULT_ALL,
2215 targets, n_targets, drag_actions);
2216
2217 gtk_target_table_free (targets, n_targets);
2218
2219 gtk_window_set_type_hint (
2220 GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_NORMAL);
2221
2222 action_group = comp_editor_get_action_group (editor, "individual");
2223 action_group_2 = e_attachment_view_get_action_group (view, "editable");
2224
2225 g_object_bind_property (
2226 action_group, "sensitive",
2227 action_group_2, "sensitive",
2228 G_BINDING_SYNC_CREATE);
2229
2230 /* Listen for attachment store changes. */
2231
2232 store = e_attachment_view_get_store (view);
2233
2234 g_signal_connect_swapped (
2235 store, "row-deleted",
2236 G_CALLBACK (attachment_store_changed_cb), editor);
2237
2238 g_signal_connect_swapped (
2239 store, "row-inserted",
2240 G_CALLBACK (attachment_store_changed_cb), editor);
2241
2242 comp_editor_bind_settings (editor);
2243
2244 gtk_application_add_window (
2245 GTK_APPLICATION (shell), GTK_WINDOW (editor));
2246 e_shell_adapt_window_size (shell, GTK_WINDOW (editor));
2247 }
2248
2249 static gboolean
2250 prompt_and_save_changes (CompEditor *editor,
2251 gboolean send)
2252 {
2253 CompEditorPrivate *priv;
2254 gboolean correct = FALSE;
2255 ECalComponent *comp;
2256 ECalComponentText text;
2257
2258 priv = editor->priv;
2259
2260 if (!priv->changed)
2261 return TRUE;
2262
2263 switch (save_component_dialog (GTK_WINDOW (editor), priv->comp)) {
2264 case GTK_RESPONSE_YES: /* Save */
2265 if (e_client_is_readonly (E_CLIENT (priv->cal_client))) {
2266 e_alert_submit (
2267 E_ALERT_SINK (editor),
2268 "calendar:prompt-read-only-cal-editor",
2269 e_source_get_display_name (
2270 e_client_get_source (E_CLIENT (priv->cal_client))),
2271 NULL);
2272 /* don't discard changes when selected readonly calendar */
2273 return FALSE;
2274 }
2275
2276 if ((comp_editor_get_flags (editor) & COMP_EDITOR_IS_ASSIGNED) != 0
2277 && e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_TODO
2278 && e_client_check_capability (E_CLIENT (priv->cal_client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) {
2279 e_alert_submit (
2280 E_ALERT_SINK (editor),
2281 "calendar:prompt-no-task-assignment-editor",
2282 e_source_get_display_name (
2283 e_client_get_source (E_CLIENT (priv->cal_client))),
2284 NULL);
2285 return FALSE;
2286 }
2287
2288 comp = comp_editor_get_current_comp (editor, &correct);
2289 e_cal_component_get_summary (comp, &text);
2290 g_object_unref (comp);
2291
2292 if (!correct)
2293 return FALSE;
2294
2295 if (!text.value)
2296 if (!send_component_prompt_subject (
2297 (GtkWindow *) editor, priv->cal_client, priv->comp))
2298 return FALSE;
2299
2300 if (e_cal_component_is_instance (priv->comp))
2301 if (!recur_component_dialog (
2302 priv->cal_client, priv->comp, &priv->mod,
2303 GTK_WINDOW (editor), FALSE))
2304 return FALSE;
2305
2306 if (send && save_comp_with_send (editor))
2307 return TRUE;
2308 else if (!send && save_comp (editor))
2309 return TRUE;
2310 else
2311 return FALSE;
2312 case GTK_RESPONSE_NO: /* Discard */
2313 return TRUE;
2314 case GTK_RESPONSE_CANCEL: /* Cancel */
2315 default:
2316 return FALSE;
2317 }
2318 }
2319
2320 /* Menu callbacks */
2321
2322 static void
2323 comp_editor_show_help (CompEditor *editor)
2324 {
2325 CompEditorClass *class;
2326
2327 class = COMP_EDITOR_GET_CLASS (editor);
2328 g_return_if_fail (class->help_section != NULL);
2329
2330 e_display_help (GTK_WINDOW (editor), class->help_section);
2331 }
2332
2333 /* Closes the dialog box and emits the appropriate signals */
2334 static void
2335 close_dialog (CompEditor *editor)
2336 {
2337 CompEditorPrivate *priv = editor->priv;
2338
2339 g_signal_emit_by_name (editor, "comp_closed", priv->saved);
2340
2341 /* FIXME Unfortunately we do this here because otherwise corba
2342 * calls happen during destruction and we might get a change
2343 * notification back when we are in an inconsistent state */
2344 if (priv->view)
2345 g_signal_handlers_disconnect_matched (
2346 priv->view, G_SIGNAL_MATCH_DATA,
2347 0, 0, NULL, NULL, editor);
2348
2349 gtk_widget_destroy (GTK_WIDGET (editor));
2350 }
2351
2352 static gint
2353 comp_editor_compare (CompEditor *editor_a,
2354 const gchar *uid_b)
2355 {
2356 const gchar *uid_a = NULL;
2357
2358 e_cal_component_get_uid (editor_a->priv->comp, &uid_a);
2359
2360 return g_strcmp0 (uid_a, uid_b);
2361 }
2362
2363 void
2364 comp_editor_set_existing_org (CompEditor *editor,
2365 gboolean existing_org)
2366 {
2367 g_return_if_fail (IS_COMP_EDITOR (editor));
2368
2369 editor->priv->existing_org = existing_org;
2370 }
2371
2372 gboolean
2373 comp_editor_get_existing_org (CompEditor *editor)
2374 {
2375 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
2376
2377 return editor->priv->existing_org;
2378 }
2379
2380 void
2381 comp_editor_set_user_org (CompEditor *editor,
2382 gboolean user_org)
2383 {
2384 g_return_if_fail (IS_COMP_EDITOR (editor));
2385
2386 editor->priv->user_org = user_org;
2387 }
2388
2389 gboolean
2390 comp_editor_get_user_org (CompEditor *editor)
2391 {
2392 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
2393
2394 return editor->priv->user_org;
2395 }
2396
2397 void
2398 comp_editor_set_group_item (CompEditor *editor,
2399 gboolean group_item)
2400 {
2401 g_return_if_fail (IS_COMP_EDITOR (editor));
2402
2403 editor->priv->is_group_item = group_item;
2404 }
2405
2406 gboolean
2407 comp_editor_get_group_item (CompEditor *editor)
2408 {
2409 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
2410
2411 return editor->priv->is_group_item;
2412 }
2413
2414 void
2415 comp_editor_set_classification (CompEditor *editor,
2416 ECalComponentClassification classification)
2417 {
2418 GtkAction *action;
2419
2420 g_return_if_fail (IS_COMP_EDITOR (editor));
2421
2422 switch (classification) {
2423 case E_CAL_COMPONENT_CLASS_PUBLIC:
2424 case E_CAL_COMPONENT_CLASS_PRIVATE:
2425 case E_CAL_COMPONENT_CLASS_CONFIDENTIAL:
2426 break;
2427 default:
2428 classification = E_CAL_COMPONENT_CLASS_PUBLIC;
2429 break;
2430 }
2431
2432 action = comp_editor_get_action (editor, "classify-public");
2433 gtk_radio_action_set_current_value (
2434 GTK_RADIO_ACTION (action), classification);
2435 }
2436
2437 ECalComponentClassification
2438 comp_editor_get_classification (CompEditor *editor)
2439 {
2440 GtkAction *action;
2441
2442 g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);
2443
2444 action = comp_editor_get_action (editor, "classify-public");
2445 return gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
2446 }
2447
2448 EShell *
2449 comp_editor_get_shell (CompEditor *editor)
2450 {
2451 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
2452
2453 return editor->priv->shell;
2454 }
2455
2456 void
2457 comp_editor_set_summary (CompEditor *editor,
2458 const gchar *summary)
2459 {
2460 gboolean show_warning;
2461
2462 g_return_if_fail (IS_COMP_EDITOR (editor));
2463
2464 if (g_strcmp0 (editor->priv->summary, summary) == 0)
2465 return;
2466
2467 g_free (editor->priv->summary);
2468 editor->priv->summary = g_strdup (summary);
2469
2470 show_warning =
2471 !editor->priv->warned &&
2472 !(editor->priv->flags & COMP_EDITOR_DELEGATE) &&
2473 editor->priv->existing_org &&
2474 !editor->priv->user_org && !(editor->priv->flags & COMP_EDITOR_NEW_ITEM);
2475
2476 if (show_warning) {
2477 e_notice (
2478 editor->priv->notebook, GTK_MESSAGE_INFO,
2479 _("Changes made to this item may be "
2480 "discarded if an update arrives"));
2481 editor->priv->warned = TRUE;
2482 }
2483
2484 update_window_border (editor, summary);
2485
2486 g_object_notify (G_OBJECT (editor), "summary");
2487 }
2488
2489 const gchar *
2490 comp_editor_get_summary (CompEditor *editor)
2491 {
2492 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
2493
2494 return editor->priv->summary;
2495 }
2496
2497 icaltimezone *
2498 comp_editor_get_timezone (CompEditor *editor)
2499 {
2500 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
2501
2502 return editor->priv->zone;
2503 }
2504
2505 void
2506 comp_editor_set_timezone (CompEditor *editor,
2507 icaltimezone *zone)
2508 {
2509 g_return_if_fail (IS_COMP_EDITOR (editor));
2510
2511 if (editor->priv->zone == zone)
2512 return;
2513
2514 editor->priv->zone = zone;
2515
2516 g_object_notify (G_OBJECT (editor), "timezone");
2517 }
2518
2519 gboolean
2520 comp_editor_get_use_24_hour_format (CompEditor *editor)
2521 {
2522 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
2523
2524 return editor->priv->use_24_hour_format;
2525 }
2526
2527 void
2528 comp_editor_set_use_24_hour_format (CompEditor *editor,
2529 gboolean use_24_hour_format)
2530 {
2531 g_return_if_fail (IS_COMP_EDITOR (editor));
2532
2533 if (editor->priv->use_24_hour_format == use_24_hour_format)
2534 return;
2535
2536 editor->priv->use_24_hour_format = use_24_hour_format;
2537
2538 g_object_notify (G_OBJECT (editor), "use-24-hour-format");
2539 }
2540
2541 gint
2542 comp_editor_get_work_day_end_hour (CompEditor *editor)
2543 {
2544 g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);
2545
2546 return editor->priv->work_day_end_hour;
2547 }
2548
2549 void
2550 comp_editor_set_work_day_end_hour (CompEditor *editor,
2551 gint work_day_end_hour)
2552 {
2553 g_return_if_fail (IS_COMP_EDITOR (editor));
2554
2555 if (editor->priv->work_day_end_hour == work_day_end_hour)
2556 return;
2557
2558 editor->priv->work_day_end_hour = work_day_end_hour;
2559
2560 g_object_notify (G_OBJECT (editor), "work-day-end-hour");
2561 }
2562
2563 gint
2564 comp_editor_get_work_day_end_minute (CompEditor *editor)
2565 {
2566 g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);
2567
2568 return editor->priv->work_day_end_minute;
2569 }
2570
2571 void
2572 comp_editor_set_work_day_end_minute (CompEditor *editor,
2573 gint work_day_end_minute)
2574 {
2575 g_return_if_fail (IS_COMP_EDITOR (editor));
2576
2577 if (editor->priv->work_day_end_minute == work_day_end_minute)
2578 return;
2579
2580 editor->priv->work_day_end_minute = work_day_end_minute;
2581
2582 g_object_notify (G_OBJECT (editor), "work-day-end-minute");
2583 }
2584
2585 gint
2586 comp_editor_get_work_day_start_hour (CompEditor *editor)
2587 {
2588 g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);
2589
2590 return editor->priv->work_day_start_hour;
2591 }
2592
2593 void
2594 comp_editor_set_work_day_start_hour (CompEditor *editor,
2595 gint work_day_start_hour)
2596 {
2597 g_return_if_fail (IS_COMP_EDITOR (editor));
2598
2599 if (editor->priv->work_day_start_hour == work_day_start_hour)
2600 return;
2601
2602 editor->priv->work_day_start_hour = work_day_start_hour;
2603
2604 g_object_notify (G_OBJECT (editor), "work-day-start-hour");
2605 }
2606
2607 gint
2608 comp_editor_get_work_day_start_minute (CompEditor *editor)
2609 {
2610 g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);
2611
2612 return editor->priv->work_day_start_minute;
2613 }
2614
2615 void
2616 comp_editor_set_work_day_start_minute (CompEditor *editor,
2617 gint work_day_start_minute)
2618 {
2619 g_return_if_fail (IS_COMP_EDITOR (editor));
2620
2621 if (editor->priv->work_day_start_minute == work_day_start_minute)
2622 return;
2623
2624 editor->priv->work_day_start_minute = work_day_start_minute;
2625
2626 g_object_notify (G_OBJECT (editor), "work-day-start-minute");
2627 }
2628
2629 /**
2630 * comp_editor_set_changed:
2631 * @editor: A component editor
2632 * @changed: Value to set the changed state to
2633 *
2634 * Set the dialog changed state to the given value
2635 **/
2636 void
2637 comp_editor_set_changed (CompEditor *editor,
2638 gboolean changed)
2639 {
2640 GtkAction *action;
2641 gboolean show_warning;
2642
2643 g_return_if_fail (IS_COMP_EDITOR (editor));
2644
2645 /* always process below changes, because other parts of
2646 * the editor listen for 'changed' notifications to update
2647 * its widgets, thus do it even the value actually didn't change
2648 */
2649 if (editor->priv->changed != changed) {
2650 editor->priv->changed = changed;
2651
2652 action = comp_editor_get_action (editor, "save");
2653 g_return_if_fail (action != NULL);
2654 gtk_action_set_sensitive (action, changed);
2655 }
2656
2657 show_warning =
2658 changed && !editor->priv->warned &&
2659 !(editor->priv->flags & COMP_EDITOR_DELEGATE) &&
2660 editor->priv->existing_org && !editor->priv->user_org
2661 && !(editor->priv->flags & COMP_EDITOR_NEW_ITEM);
2662
2663 if (show_warning) {
2664 e_notice (
2665 editor->priv->notebook, GTK_MESSAGE_INFO,
2666 _("Changes made to this item may be "
2667 "discarded if an update arrives"));
2668 editor->priv->warned = TRUE;
2669 }
2670
2671 g_object_notify (G_OBJECT (editor), "changed");
2672 }
2673
2674 /**
2675 * comp_editor_get_changed:
2676 * @editor: A component editor
2677 *
2678 * Gets the changed state of the dialog
2679 *
2680 * Return value: A boolean indicating if the dialog is in a changed
2681 * state
2682 **/
2683 gboolean
2684 comp_editor_get_changed (CompEditor *editor)
2685 {
2686 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
2687
2688 return editor->priv->changed;
2689 }
2690
2691 EFocusTracker *
2692 comp_editor_get_focus_tracker (CompEditor *editor)
2693 {
2694 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
2695
2696 return editor->priv->focus_tracker;
2697 }
2698
2699 void
2700 comp_editor_set_flags (CompEditor *editor,
2701 CompEditorFlags flags)
2702 {
2703 g_return_if_fail (IS_COMP_EDITOR (editor));
2704
2705 if (editor->priv->flags == flags)
2706 return;
2707
2708 editor->priv->flags = flags;
2709 editor->priv->user_org = flags & COMP_EDITOR_USER_ORG;
2710
2711 g_object_notify (G_OBJECT (editor), "flags");
2712 }
2713
2714 CompEditorFlags
2715 comp_editor_get_flags (CompEditor *editor)
2716 {
2717 g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);
2718
2719 return editor->priv->flags;
2720 }
2721
2722 GtkUIManager *
2723 comp_editor_get_ui_manager (CompEditor *editor)
2724 {
2725 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
2726
2727 return editor->priv->ui_manager;
2728 }
2729
2730 GtkAction *
2731 comp_editor_get_action (CompEditor *editor,
2732 const gchar *action_name)
2733 {
2734 GtkUIManager *ui_manager;
2735
2736 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
2737 g_return_val_if_fail (action_name != NULL, NULL);
2738
2739 ui_manager = comp_editor_get_ui_manager (editor);
2740
2741 return e_lookup_action (ui_manager, action_name);
2742 }
2743
2744 GtkActionGroup *
2745 comp_editor_get_action_group (CompEditor *editor,
2746 const gchar *group_name)
2747 {
2748 GtkUIManager *ui_manager;
2749
2750 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
2751 g_return_val_if_fail (group_name != NULL, NULL);
2752
2753 ui_manager = comp_editor_get_ui_manager (editor);
2754
2755 return e_lookup_action_group (ui_manager, group_name);
2756 }
2757
2758 GtkWidget *
2759 comp_editor_get_managed_widget (CompEditor *editor,
2760 const gchar *widget_path)
2761 {
2762 GtkUIManager *ui_manager;
2763 GtkWidget *widget;
2764
2765 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
2766 g_return_val_if_fail (widget_path != NULL, NULL);
2767
2768 ui_manager = comp_editor_get_ui_manager (editor);
2769 widget = gtk_ui_manager_get_widget (ui_manager, widget_path);
2770 g_return_val_if_fail (widget != NULL, NULL);
2771
2772 return widget;
2773 }
2774
2775 CompEditor *
2776 comp_editor_find_instance (const gchar *uid)
2777 {
2778 GList *link;
2779
2780 g_return_val_if_fail (uid != NULL, NULL);
2781
2782 link = g_list_find_custom (
2783 active_editors, uid,
2784 (GCompareFunc) comp_editor_compare);
2785
2786 return (link != NULL) ? link->data : NULL;
2787 }
2788
2789 /**
2790 * comp_editor_set_needs_send:
2791 * @editor: A component editor
2792 * @needs_send: Value to set the needs send state to
2793 *
2794 * Set the dialog needs send state to the given value
2795 **/
2796 void
2797 comp_editor_set_needs_send (CompEditor *editor,
2798 gboolean needs_send)
2799 {
2800 g_return_if_fail (IS_COMP_EDITOR (editor));
2801
2802 editor->priv->needs_send = needs_send;
2803 }
2804
2805 /**
2806 * comp_editor_get_needs_send:
2807 * @editor: A component editor
2808 *
2809 * Gets the needs send state of the dialog
2810 *
2811 * Return value: A boolean indicating if the dialog is in a needs send
2812 * state
2813 **/
2814 gboolean
2815 comp_editor_get_needs_send (CompEditor *editor)
2816 {
2817 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
2818
2819 return editor->priv->needs_send;
2820 }
2821
2822 static void
2823 page_mapped_cb (GtkWidget *page_widget,
2824 CompEditorPage *page)
2825 {
2826 GtkWidget *toplevel;
2827
2828 toplevel = gtk_widget_get_toplevel (page_widget);
2829 if (!GTK_IS_WINDOW (toplevel))
2830 return;
2831
2832 if (page->accel_group) {
2833 gtk_window_add_accel_group (
2834 GTK_WINDOW (toplevel),
2835 page->accel_group);
2836 }
2837 }
2838
2839 static void
2840 page_unmapped_cb (GtkWidget *page_widget,
2841 CompEditorPage *page)
2842 {
2843 GtkWidget *toplevel;
2844
2845 toplevel = gtk_widget_get_toplevel (page_widget);
2846 if (!GTK_IS_WINDOW (toplevel))
2847 return;
2848
2849 if (page->accel_group) {
2850 gtk_window_remove_accel_group (GTK_WINDOW (toplevel), page->accel_group);
2851 }
2852 }
2853
2854 /**
2855 * comp_editor_append_widget:
2856 * @editor: A component editor
2857 * @page: A component editor page
2858 * @label: Label of the page. Should be NULL if add is FALSE.
2859 * @add: Add's the page into the notebook if TRUE
2860 *
2861 * Appends a page to the notebook if add is TRUE else
2862 * just adds it to the list of pages.
2863 **/
2864 void
2865 comp_editor_append_widget (CompEditor *editor,
2866 GtkWidget *page,
2867 const gchar *label,
2868 gboolean add)
2869 {
2870 CompEditorPrivate *priv;
2871 GtkWidget *label_widget = NULL;
2872
2873 g_return_if_fail (IS_COMP_EDITOR (editor));
2874
2875 priv = editor->priv;
2876
2877 g_object_ref (page);
2878
2879 if (label)
2880 label_widget = gtk_label_new_with_mnemonic (label);
2881
2882 priv->pages = g_list_append (priv->pages, page);
2883
2884 if (add) {
2885 gtk_notebook_append_page (priv->notebook, page, label_widget);
2886 gtk_container_child_set (
2887 GTK_CONTAINER (priv->notebook), page,
2888 "tab-fill", FALSE, "tab-expand", FALSE, NULL);
2889 }
2890
2891 /* Listen for when the page is mapped/unmapped so we can
2892 * install/uninstall the appropriate GtkAccelGroup.
2893 g_signal_connect (
2894 page, "map",
2895 G_CALLBACK (page_mapped_cb), page);
2896 g_signal_connect (
2897 page, "unmap",
2898 G_CALLBACK (page_unmapped_cb), page);
2899 */
2900
2901 }
2902
2903 /**
2904 * comp_editor_append_page:
2905 * @editor: A component editor
2906 * @page: A component editor page
2907 * @label: Label of the page. Should be NULL if add is FALSE.
2908 * @add: Add's the page into the notebook if TRUE
2909 *
2910 * Appends a page to the notebook if add is TRUE else
2911 * just adds it to the list of pages.
2912 **/
2913 void
2914 comp_editor_append_page (CompEditor *editor,
2915 CompEditorPage *page,
2916 const gchar *label,
2917 gboolean add)
2918 {
2919 CompEditorPrivate *priv;
2920 GtkWidget *page_widget;
2921 GtkWidget *label_widget = NULL;
2922 gboolean is_first_page;
2923
2924 g_return_if_fail (IS_COMP_EDITOR (editor));
2925 g_return_if_fail (IS_COMP_EDITOR_PAGE (page));
2926
2927 priv = editor->priv;
2928
2929 g_object_ref (page);
2930
2931 /* If we are editing something, fill the widgets with current info */
2932 if (priv->comp != NULL) {
2933 ECalComponent *comp;
2934
2935 comp = comp_editor_get_current_comp (editor, NULL);
2936 comp_editor_page_fill_widgets (page, comp);
2937 g_object_unref (comp);
2938 }
2939
2940 page_widget = comp_editor_page_get_widget (page);
2941 g_return_if_fail (page_widget != NULL);
2942
2943 if (label)
2944 label_widget = gtk_label_new_with_mnemonic (label);
2945
2946 is_first_page = (priv->pages == NULL);
2947
2948 priv->pages = g_list_append (priv->pages, page);
2949
2950 if (add) {
2951 gtk_notebook_append_page (
2952 priv->notebook, page_widget, label_widget);
2953 gtk_container_child_set (
2954 GTK_CONTAINER (priv->notebook), page_widget,
2955 "tab-fill", FALSE, "tab-expand", FALSE, NULL);
2956 }
2957
2958 /* Listen for things happening on the page */
2959 g_signal_connect_swapped (
2960 page, "dates_changed",
2961 G_CALLBACK (page_dates_changed_cb), editor);
2962
2963 /* Listen for when the page is mapped/unmapped so we can
2964 * install/uninstall the appropriate GtkAccelGroup. */
2965 g_signal_connect (
2966 page_widget, "map",
2967 G_CALLBACK (page_mapped_cb), page);
2968 g_signal_connect (
2969 page_widget, "unmap",
2970 G_CALLBACK (page_unmapped_cb), page);
2971
2972 /* The first page is the main page of the editor, so we ask it to focus
2973 * its main widget.
2974 */
2975 if (is_first_page)
2976 comp_editor_page_focus_main_widget (page);
2977 }
2978
2979 /**
2980 * comp_editor_remove_page:
2981 * @editor: A component editor
2982 * @page: A component editor page
2983 *
2984 * Removes the page from the component editor
2985 **/
2986 void
2987 comp_editor_remove_page (CompEditor *editor,
2988 CompEditorPage *page)
2989 {
2990 CompEditorPrivate *priv;
2991 GtkWidget *page_widget;
2992 gint page_num;
2993
2994 g_return_if_fail (IS_COMP_EDITOR (editor));
2995 g_return_if_fail (IS_COMP_EDITOR_PAGE (page));
2996
2997 priv = editor->priv;
2998
2999 page_widget = comp_editor_page_get_widget (page);
3000 page_num = gtk_notebook_page_num (priv->notebook, page_widget);
3001 if (page_num == -1)
3002 return;
3003
3004 /* Disconnect all the signals added in append_page(). */
3005 g_signal_handlers_disconnect_matched (
3006 page, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
3007 g_signal_handlers_disconnect_matched (
3008 page_widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page);
3009
3010 gtk_notebook_remove_page (priv->notebook, page_num);
3011
3012 priv->pages = g_list_remove (priv->pages, page);
3013 g_object_unref (page);
3014 }
3015
3016 /**
3017 * comp_editor_show_page:
3018 * @editor:
3019 * @page:
3020 *
3021 *
3022 **/
3023 void
3024 comp_editor_show_page (CompEditor *editor,
3025 CompEditorPage *page)
3026 {
3027 CompEditorPrivate *priv;
3028 GtkWidget *page_widget;
3029 gint page_num;
3030
3031 g_return_if_fail (IS_COMP_EDITOR (editor));
3032 g_return_if_fail (IS_COMP_EDITOR_PAGE (page));
3033
3034 priv = editor->priv;
3035
3036 page_widget = comp_editor_page_get_widget (page);
3037 page_num = gtk_notebook_page_num (priv->notebook, page_widget);
3038 gtk_notebook_set_current_page (priv->notebook, page_num);
3039 }
3040
3041 /**
3042 * comp_editor_set_client:
3043 * @editor: A component editor
3044 * @cal_client: The calendar client to use
3045 *
3046 * Sets the calendar client used by the editor to update components
3047 **/
3048 void
3049 comp_editor_set_client (CompEditor *editor,
3050 ECalClient *cal_client)
3051 {
3052 g_return_if_fail (IS_COMP_EDITOR (editor));
3053 g_return_if_fail (cal_client == NULL || E_IS_CAL_CLIENT (cal_client));
3054
3055 if (editor->priv->cal_client == cal_client)
3056 return;
3057
3058 if (cal_client != NULL)
3059 g_object_ref (cal_client);
3060
3061 if (editor->priv->cal_client != NULL)
3062 g_object_unref (editor->priv->cal_client);
3063
3064 editor->priv->cal_client = cal_client;
3065
3066 if (editor->priv->source_client == NULL && cal_client != NULL)
3067 editor->priv->source_client = g_object_ref (cal_client);
3068
3069 g_object_notify (G_OBJECT (editor), "client");
3070 }
3071
3072 /**
3073 * comp_editor_get_client:
3074 * @editor: A component editor
3075 *
3076 * Returns the calendar client of the editor
3077 *
3078 * Return value: The calendar client of the editor
3079 **/
3080 ECalClient *
3081 comp_editor_get_client (CompEditor *editor)
3082 {
3083 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
3084
3085 return editor->priv->cal_client;
3086 }
3087
3088 static void
3089 attachment_loaded_cb (EAttachment *attachment,
3090 GAsyncResult *result,
3091 GtkWindow *parent)
3092 {
3093 GFileInfo *file_info;
3094 const gchar *display_name;
3095 const gchar *uid;
3096 gchar *new_name;
3097
3098 /* Prior to 2.27.2, attachment files were named:
3099 *
3100 * <component-uid> '-' <actual-filename>
3101 * -------------------------------------
3102 * (one long filename)
3103 *
3104 * Here we fix the display name if this form is detected so we
3105 * don't show the component UID in the user interface. If the
3106 * user saves changes in the editor, the attachment will be
3107 * written to disk as:
3108 *
3109 * <component-uid> / <actual-filename>
3110 * --------------- -----------------
3111 * (directory) (original name)
3112 *
3113 * So this is a lazy migration from the old form to the new.
3114 */
3115
3116 file_info = e_attachment_get_file_info (attachment);
3117 if (!file_info) {
3118 /* failed to load an attachment file */
3119 e_attachment_load_handle_error (attachment, result, parent);
3120 return;
3121 }
3122
3123 display_name = g_file_info_get_display_name (file_info);
3124 uid = g_object_get_data (G_OBJECT (attachment), "uid");
3125
3126 if (g_str_has_prefix (display_name, uid)) {
3127 new_name = g_strdup (display_name + strlen (uid) + 1);
3128 g_file_info_set_display_name (file_info, new_name);
3129 g_object_notify (G_OBJECT (attachment), "file-info");
3130 g_free (new_name);
3131 }
3132
3133 e_attachment_load_handle_error (attachment, result, parent);
3134 }
3135
3136 static void
3137 set_attachment_list (CompEditor *editor,
3138 GSList *uri_list)
3139 {
3140 EAttachmentStore *store;
3141 EAttachmentView *view;
3142 const gchar *uid = NULL;
3143 GSList *iter;
3144
3145 view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
3146 store = e_attachment_view_get_store (view);
3147
3148 if (e_attachment_store_get_num_attachments (store) > 0) {
3149 /* To prevent repopulating the
3150 * bar due to redraw functions in fill_widget.
3151 * Assumes it can be set only once.
3152 */
3153 return;
3154 }
3155
3156 /* XXX What an awkward API this is. Takes a return location
3157 * for a constant string instead of just returning it. */
3158 e_cal_component_get_uid (editor->priv->comp, &uid);
3159
3160 for (iter = uri_list; iter != NULL; iter = iter->next) {
3161 EAttachment *attachment;
3162
3163 attachment = e_attachment_new_for_uri (iter->data);
3164 e_attachment_store_add_attachment (store, attachment);
3165 g_object_set_data_full (
3166 G_OBJECT (attachment),
3167 "uid", g_strdup (uid),
3168 (GDestroyNotify) g_free);
3169 e_attachment_load_async (
3170 attachment, (GAsyncReadyCallback)
3171 attachment_loaded_cb, editor);
3172 g_object_unref (attachment);
3173 }
3174 }
3175
3176 static void
3177 fill_widgets (CompEditor *editor)
3178 {
3179 EAttachmentStore *store;
3180 EAttachmentView *view;
3181 CompEditorPrivate *priv;
3182 GtkAction *action;
3183 GList *iter;
3184
3185 view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
3186 store = e_attachment_view_get_store (view);
3187
3188 priv = editor->priv;
3189
3190 /*Check if attachments are available here and set them*/
3191 if (e_cal_component_has_attachments (priv->comp)) {
3192 GSList *attachment_list = NULL;
3193 e_cal_component_get_attachment_list (priv->comp, &attachment_list);
3194 g_signal_handlers_block_by_func (
3195 store, G_CALLBACK (attachment_store_changed_cb),
3196 editor);
3197 set_attachment_list (editor, attachment_list);
3198 g_signal_handlers_unblock_by_func (
3199 store, G_CALLBACK (attachment_store_changed_cb),
3200 editor);
3201 g_slist_foreach (attachment_list, (GFunc) g_free, NULL);
3202 g_slist_free (attachment_list);
3203 }
3204
3205 action = comp_editor_get_action (editor, "classify-public");
3206 g_signal_handlers_block_by_func (
3207 action, G_CALLBACK (action_classification_cb), editor);
3208
3209 for (iter = priv->pages; iter != NULL; iter = iter->next) {
3210 if (IS_COMP_EDITOR_PAGE (iter->data))
3211 comp_editor_page_fill_widgets (iter->data, priv->comp);
3212 }
3213
3214 g_signal_handlers_unblock_by_func (
3215 action, G_CALLBACK (action_classification_cb), editor);
3216 }
3217
3218 static void
3219 real_edit_comp (CompEditor *editor,
3220 ECalComponent *comp)
3221 {
3222 CompEditorPrivate *priv;
3223
3224 g_return_if_fail (IS_COMP_EDITOR (editor));
3225
3226 priv = editor->priv;
3227
3228 if (priv->comp) {
3229 g_object_unref (priv->comp);
3230 priv->comp = NULL;
3231 }
3232
3233 if (comp) {
3234 priv->comp = e_cal_component_clone (comp);
3235 comp_editor_copy_new_attendees (priv->comp, comp);
3236 }
3237
3238 priv->existing_org = e_cal_component_has_organizer (comp);
3239 priv->warned = FALSE;
3240
3241 update_window_border (editor, NULL);
3242
3243 fill_widgets (editor);
3244
3245 comp_editor_set_changed (editor, FALSE);
3246
3247 listen_for_changes (editor);
3248 }
3249
3250 /* TODO These functions should be available in e-cal-component.c */
3251 static void
3252 set_attendees_for_delegation (ECalComponent *comp,
3253 const gchar *address,
3254 ECalComponentItipMethod method)
3255 {
3256 icalproperty *prop;
3257 icalparameter *param;
3258 icalcomponent *icalcomp;
3259
3260 icalcomp = e_cal_component_get_icalcomponent (comp);
3261
3262 for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
3263 prop;
3264 prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
3265 const gchar *attendee = icalproperty_get_attendee (prop);
3266 const gchar *delfrom = NULL;
3267
3268 param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER);
3269 if (param)
3270 delfrom = icalparameter_get_delegatedfrom (param);
3271 if (!(g_str_equal (itip_strip_mailto (attendee), address) ||
3272 ((delfrom && *delfrom) &&
3273 g_str_equal (itip_strip_mailto (delfrom), address)))) {
3274 icalcomponent_remove_property (icalcomp, prop);
3275 }
3276
3277 }
3278
3279 }
3280
3281 static void
3282 get_users_from_memo_comp (ECalComponent *comp,
3283 GSList **users)
3284 {
3285 icalcomponent *icalcomp;
3286 icalproperty *icalprop;
3287 const gchar *attendees = NULL;
3288 gchar **emails, **iter;
3289
3290 icalcomp = e_cal_component_get_icalcomponent (comp);
3291
3292 for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
3293 icalprop != NULL;
3294 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
3295 const gchar *x_name;
3296
3297 x_name = icalproperty_get_x_name (icalprop);
3298
3299 if (g_str_equal (x_name, "X-EVOLUTION-RECIPIENTS"))
3300 break;
3301 }
3302
3303 if (icalprop) {
3304 attendees = icalproperty_get_x (icalprop);
3305 emails = g_strsplit (attendees, ";", -1);
3306
3307 iter = emails;
3308 while (*iter) {
3309 *users = g_slist_append (*users, g_strdup (*iter));
3310 iter++;
3311 }
3312 g_strfreev (emails);
3313 }
3314 }
3315
3316 static gboolean
3317 real_send_comp (CompEditor *editor,
3318 ECalComponentItipMethod method,
3319 gboolean strip_alarms)
3320 {
3321 CompEditorPrivate *priv;
3322 CompEditorFlags flags;
3323 EShell *shell;
3324 ESourceRegistry *registry;
3325 ECalComponent *send_comp = NULL;
3326 gchar *address = NULL;
3327 GSList *users = NULL;
3328
3329 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
3330
3331 priv = editor->priv;
3332
3333 flags = comp_editor_get_flags (editor);
3334 shell = comp_editor_get_shell (editor);
3335
3336 registry = e_shell_get_registry (shell);
3337
3338 if (priv->mod == CALOBJ_MOD_ALL && e_cal_component_is_instance (priv->comp)) {
3339 /* Ensure we send the master object, not the instance only */
3340 icalcomponent *icalcomp = NULL;
3341 const gchar *uid = NULL;
3342
3343 e_cal_component_get_uid (priv->comp, &uid);
3344 if (e_cal_client_get_object_sync (priv->cal_client, uid, NULL, &icalcomp, NULL, NULL) && icalcomp) {
3345 send_comp = e_cal_component_new ();
3346 if (!e_cal_component_set_icalcomponent (send_comp, icalcomp)) {
3347 icalcomponent_free (icalcomp);
3348 g_object_unref (send_comp);
3349 send_comp = NULL;
3350 }
3351 }
3352 }
3353
3354 if (!send_comp)
3355 send_comp = e_cal_component_clone (priv->comp);
3356
3357 comp_editor_copy_new_attendees (send_comp, priv->comp);
3358
3359 if (e_cal_component_get_vtype (send_comp) == E_CAL_COMPONENT_JOURNAL)
3360 get_users_from_memo_comp (send_comp, &users);
3361
3362 /* The user updates the delegated status to the Organizer,
3363 * so remove all other attendees. */
3364 if (flags & COMP_EDITOR_DELEGATE) {
3365 address = itip_get_comp_attendee (
3366 registry, send_comp, priv->cal_client);
3367
3368 if (address)
3369 set_attendees_for_delegation (send_comp, address, method);
3370 }
3371
3372 if (!e_cal_component_has_attachments (priv->comp) ||
3373 e_client_check_capability (
3374 E_CLIENT (priv->cal_client),
3375 CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
3376 if (itip_send_comp (
3377 registry, method, send_comp, priv->cal_client,
3378 NULL, NULL, users, strip_alarms,
3379 priv->flags & COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) {
3380 g_object_unref (send_comp);
3381 return TRUE;
3382 }
3383 } else {
3384 /* Clone the component with attachments set to CID:... */
3385 GSList *attach_list = NULL;
3386 GSList *mime_attach_list, *attach;
3387
3388 /* mime_attach_list is freed by itip_send_comp */
3389 mime_attach_list = comp_editor_get_mime_attach_list (editor);
3390
3391 for (attach = mime_attach_list; attach; attach = attach->next) {
3392 struct CalMimeAttach *cma = (struct CalMimeAttach *) attach->data;
3393
3394 attach_list = g_slist_append (
3395 attach_list, g_strconcat (
3396 "cid:", cma->content_id, NULL));
3397 }
3398
3399 if (attach_list) {
3400 e_cal_component_set_attachment_list (send_comp, attach_list);
3401
3402 g_slist_foreach (attach_list, (GFunc) g_free, NULL);
3403 g_slist_free (attach_list);
3404 }
3405
3406 if (itip_send_comp (
3407 registry, method, send_comp, priv->cal_client,
3408 NULL, mime_attach_list, users, strip_alarms,
3409 priv->flags & COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) {
3410 gboolean saved = save_comp (editor);
3411
3412 g_object_unref (send_comp);
3413
3414 if (!saved)
3415 comp_editor_set_changed (editor, TRUE);
3416
3417 return saved;
3418 }
3419 }
3420
3421 g_object_unref (send_comp);
3422 g_free (address);
3423 comp_editor_set_changed (editor, TRUE);
3424
3425 return FALSE;
3426
3427 }
3428
3429 /**
3430 * comp_editor_edit_comp:
3431 * @editor: A component editor
3432 * @comp: A calendar component
3433 *
3434 * Starts the editor editing the given component
3435 **/
3436 void
3437 comp_editor_edit_comp (CompEditor *editor,
3438 ECalComponent *comp)
3439 {
3440 CompEditorClass *class;
3441
3442 g_return_if_fail (IS_COMP_EDITOR (editor));
3443 g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3444
3445 class = COMP_EDITOR_GET_CLASS (editor);
3446
3447 if (class->edit_comp)
3448 class->edit_comp (editor, comp);
3449 }
3450
3451 ECalComponent *
3452 comp_editor_get_comp (CompEditor *editor)
3453 {
3454 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
3455
3456 return editor->priv->comp;
3457 }
3458
3459 /**
3460 * comp_editor_get_current_comp
3461 * @editor: a #CompEditor
3462 * @correct: Set this no non-%NULL if you are interested to know if all
3463 * pages reported success when filling component.
3464 *
3465 * Returns: Newly allocated component, should be unref-ed by g_object_unref().
3466 **/
3467 ECalComponent *
3468 comp_editor_get_current_comp (CompEditor *editor,
3469 gboolean *correct)
3470 {
3471 CompEditorPrivate *priv;
3472 ECalComponent *comp;
3473 GList *l;
3474 gboolean all_ok = TRUE;
3475
3476 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
3477
3478 priv = editor->priv;
3479
3480 comp = e_cal_component_clone (priv->comp);
3481 comp_editor_copy_new_attendees (comp, priv->comp);
3482 if (priv->changed) {
3483 for (l = priv->pages; l != NULL; l = l->next) {
3484 if (IS_COMP_EDITOR_PAGE (l->data))
3485 all_ok = comp_editor_page_fill_component (l->data, comp) && all_ok;
3486 }
3487 }
3488
3489 if (correct)
3490 *correct = all_ok;
3491
3492 return comp;
3493 }
3494
3495 /**
3496 * comp_editor_save_comp:
3497 * @editor:
3498 *
3499 *
3500 **/
3501 gboolean
3502 comp_editor_save_comp (CompEditor *editor,
3503 gboolean send)
3504 {
3505 return prompt_and_save_changes (editor, send);
3506 }
3507
3508 /**
3509 * comp_editor_delete_comp:
3510 * @editor:
3511 *
3512 *
3513 **/
3514 void
3515 comp_editor_delete_comp (CompEditor *editor)
3516 {
3517 CompEditorPrivate *priv;
3518 const gchar *uid;
3519
3520 g_return_if_fail (IS_COMP_EDITOR (editor));
3521
3522 priv = editor->priv;
3523
3524 e_cal_component_get_uid (priv->comp, &uid);
3525 if (e_cal_component_is_instance (priv->comp) ||
3526 e_cal_component_has_recurrences (priv->comp))
3527 e_cal_client_remove_object_sync (
3528 priv->cal_client, uid, NULL,
3529 CALOBJ_MOD_ALL, NULL, NULL);
3530 else
3531 e_cal_client_remove_object_sync (
3532 priv->cal_client, uid, NULL,
3533 CALOBJ_MOD_THIS, NULL, NULL);
3534 close_dialog (editor);
3535 }
3536
3537 /**
3538 * comp_editor_send_comp:
3539 * @editor:
3540 * @method:
3541 *
3542 *
3543 **/
3544 gboolean
3545 comp_editor_send_comp (CompEditor *editor,
3546 ECalComponentItipMethod method,
3547 gboolean strip_alarms)
3548 {
3549 CompEditorClass *class;
3550
3551 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
3552
3553 class = COMP_EDITOR_GET_CLASS (editor);
3554
3555 if (class->send_comp)
3556 return class->send_comp (editor, method, strip_alarms);
3557
3558 return FALSE;
3559 }
3560
3561 gboolean
3562 comp_editor_close (CompEditor *editor)
3563 {
3564 gboolean close;
3565
3566 g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);
3567
3568 commit_all_fields (editor);
3569
3570 close = prompt_and_save_changes (editor, TRUE);
3571 if (close)
3572 close_dialog (editor);
3573
3574 return close;
3575 }
3576
3577 /* Utility function to get the mime-attachment list from the attachment
3578 * bar for sending the comp via itip. The list and its contents must
3579 * be freed by the caller.
3580 */
3581 GSList *
3582 comp_editor_get_mime_attach_list (CompEditor *editor)
3583 {
3584 EAttachmentStore *store;
3585 EAttachmentView *view;
3586 GtkTreeModel *model;
3587 GtkTreeIter iter;
3588 struct CalMimeAttach *cal_mime_attach;
3589 GSList *attach_list = NULL;
3590 gboolean valid;
3591
3592 view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
3593 store = e_attachment_view_get_store (view);
3594
3595 model = GTK_TREE_MODEL (store);
3596 valid = gtk_tree_model_get_iter_first (model, &iter);
3597
3598 while (valid) {
3599 EAttachment *attachment;
3600 CamelDataWrapper *wrapper;
3601 CamelMimePart *mime_part;
3602 CamelStream *stream;
3603 GByteArray *byte_array;
3604 guchar *buffer = NULL;
3605 const gchar *desc, *disp;
3606 gint column_id;
3607
3608 column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
3609 gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
3610 mime_part = e_attachment_get_mime_part (attachment);
3611 g_object_unref (attachment);
3612
3613 valid = gtk_tree_model_iter_next (model, &iter);
3614
3615 if (mime_part == NULL)
3616 continue;
3617
3618 cal_mime_attach = g_malloc0 (sizeof (struct CalMimeAttach));
3619 wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
3620
3621 byte_array = g_byte_array_new ();
3622 stream = camel_stream_mem_new_with_byte_array (byte_array);
3623
3624 camel_data_wrapper_decode_to_stream_sync (
3625 wrapper, stream, NULL, NULL);
3626 buffer = g_memdup (byte_array->data, byte_array->len);
3627
3628 camel_mime_part_set_content_id (mime_part, NULL);
3629
3630 cal_mime_attach->encoded_data = (gchar *) buffer;
3631 cal_mime_attach->length = byte_array->len;
3632 cal_mime_attach->filename =
3633 g_strdup (camel_mime_part_get_filename (mime_part));
3634 desc = camel_mime_part_get_description (mime_part);
3635 if (!desc || *desc == '\0')
3636 desc = _("attachment");
3637 cal_mime_attach->description = g_strdup (desc);
3638 cal_mime_attach->content_type = g_strdup (
3639 camel_data_wrapper_get_mime_type (wrapper));
3640 cal_mime_attach->content_id = g_strdup (
3641 camel_mime_part_get_content_id (mime_part));
3642
3643 disp = camel_mime_part_get_disposition (mime_part);
3644 if (disp && !g_ascii_strcasecmp (disp, "inline"))
3645 cal_mime_attach->disposition = TRUE;
3646
3647 attach_list = g_slist_append (attach_list, cal_mime_attach);
3648
3649 g_object_unref (stream);
3650
3651 }
3652
3653 return attach_list;
3654 }
3655
3656 static void
3657 page_dates_changed_cb (CompEditor *editor,
3658 CompEditorPageDates *dates,
3659 CompEditorPage *page)
3660 {
3661 CompEditorPrivate *priv = editor->priv;
3662 GList *l;
3663
3664 for (l = priv->pages; l != NULL; l = l->next)
3665 if (page != (CompEditorPage *) l->data && IS_COMP_EDITOR_PAGE (l->data))
3666 comp_editor_page_set_dates (l->data, dates);
3667
3668 if (!priv->warned && priv->existing_org && !priv->user_org &&
3669 !(editor->priv->flags & COMP_EDITOR_NEW_ITEM)) {
3670 e_notice (
3671 priv->notebook, GTK_MESSAGE_INFO,
3672 _("Changes made to this item may be discarded "
3673 "if an update arrives"));
3674 priv->warned = TRUE;
3675 }
3676 }
3677
3678 static void
3679 obj_modified_cb (ECalClientView *view,
3680 const GSList *objects,
3681 CompEditor *editor)
3682 {
3683 CompEditorPrivate *priv;
3684 ECalComponent *comp = NULL;
3685
3686 priv = editor->priv;
3687
3688 /* We queried based on a specific UID so we definitely changed */
3689 if (changed_component_dialog (
3690 (GtkWindow *) editor, priv->comp, FALSE, priv->changed)) {
3691 icalcomponent *icalcomp = icalcomponent_new_clone (objects->data);
3692
3693 comp = e_cal_component_new ();
3694 if (e_cal_component_set_icalcomponent (comp, icalcomp)) {
3695 comp_editor_edit_comp (editor, comp);
3696 } else {
3697 GtkWidget *dialog;
3698
3699 dialog = gtk_message_dialog_new (
3700 NULL, 0,
3701 GTK_MESSAGE_ERROR,
3702 GTK_BUTTONS_OK,
3703 "%s",
3704 _("Unable to use current version!"));
3705 gtk_dialog_run (GTK_DIALOG (dialog));
3706 gtk_widget_destroy (dialog);
3707
3708 icalcomponent_free (icalcomp);
3709 }
3710
3711 g_object_unref (comp);
3712 }
3713 }
3714
3715 static void
3716 obj_removed_cb (ECalClientView *view,
3717 const GSList *uids,
3718 CompEditor *editor)
3719 {
3720 CompEditorPrivate *priv = editor->priv;
3721
3722 if (changed_component_dialog (
3723 GTK_WINDOW (editor), priv->comp, TRUE, priv->changed))
3724 close_dialog (editor);
3725 }