Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / e-comp-editor-page-general.c
blobbfba1d9418402fb6bb853d90925e9213f2f576a0
1 /*
2 * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-config.h"
20 #include <glib.h>
21 #include <glib/gi18n-lib.h>
22 #include <gtk/gtk.h>
24 #include <libedataserver/libedataserver.h>
25 #include <camel/camel.h>
26 #include <e-util/e-util.h>
28 #include "comp-util.h"
29 #include "e-comp-editor.h"
30 #include "e-comp-editor-page.h"
31 #include "e-meeting-list-view.h"
32 #include "itip-utils.h"
34 #include "e-comp-editor-page-general.h"
36 struct _ECompEditorPageGeneralPrivate {
37 GtkWidget *source_label;
38 GtkWidget *source_combo_box;
39 GtkWidget *organizer_label;
40 GtkWidget *organizer_combo_box;
41 GtkWidget *organizer_hbox;
42 GtkWidget *attendees_button;
43 GtkWidget *attendees_hbox;
44 GtkWidget *attendees_list_view;
45 GtkWidget *attendees_button_box;
46 GtkWidget *attendees_button_add;
47 GtkWidget *attendees_button_edit;
48 GtkWidget *attendees_button_remove;
50 gint data_column_width;
51 gchar *source_label_text;
52 gchar *source_extension_name;
53 ESource *select_source;
54 gboolean show_attendees;
56 EMeetingStore *meeting_store;
57 GSList *orig_attendees; /* gchar *mail_addresses */
58 gchar *user_delegator;
61 enum {
62 PROP_0,
63 PROP_DATA_COLUMN_WIDTH,
64 PROP_SOURCE_LABEL,
65 PROP_SOURCE_EXTENSION_NAME,
66 PROP_SELECTED_SOURCE,
67 PROP_SHOW_ATTENDEES
70 G_DEFINE_TYPE (ECompEditorPageGeneral, e_comp_editor_page_general, E_TYPE_COMP_EDITOR_PAGE)
72 static void ecep_general_sensitize_widgets (ECompEditorPage *page,
73 gboolean force_insensitive);
75 static void
76 ecep_general_set_column_visible (ECompEditorPageGeneral *page_general,
77 EMeetingStoreColumns column,
78 gboolean visible)
80 EMeetingListView *meeting_list_view;
82 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
84 meeting_list_view = E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view);
85 e_meeting_list_view_column_set_visible (meeting_list_view, column, visible);
88 static void
89 action_view_role_cb (GtkToggleAction *action,
90 ECompEditorPageGeneral *page_general)
92 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
94 ecep_general_set_column_visible (page_general, E_MEETING_STORE_ROLE_COL,
95 gtk_toggle_action_get_active (action));
98 static void
99 action_view_rsvp_cb (GtkToggleAction *action,
100 ECompEditorPageGeneral *page_general)
102 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
104 ecep_general_set_column_visible (page_general, E_MEETING_STORE_RSVP_COL,
105 gtk_toggle_action_get_active (action));
108 static void
109 action_view_status_cb (GtkToggleAction *action,
110 ECompEditorPageGeneral *page_general)
112 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
114 ecep_general_set_column_visible (page_general, E_MEETING_STORE_STATUS_COL,
115 gtk_toggle_action_get_active (action));
118 static void
119 action_view_type_cb (GtkToggleAction *action,
120 ECompEditorPageGeneral *page_general)
122 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
124 ecep_general_set_column_visible (page_general, E_MEETING_STORE_TYPE_COL,
125 gtk_toggle_action_get_active (action));
128 static void
129 ecep_general_fill_organizer_combo_box (ECompEditorPageGeneral *page_general)
131 GtkComboBoxText *combo_box_text;
132 ECompEditor *comp_editor;
133 EShell *shell;
134 ESourceRegistry *registry;
135 gchar **address_strings;
136 gint ii;
138 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
139 g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box));
141 combo_box_text = GTK_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box);
142 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
143 shell = e_comp_editor_get_shell (comp_editor);
144 registry = e_shell_get_registry (shell);
145 address_strings = itip_get_user_identities (registry);
147 for (ii = 0; address_strings && address_strings[ii]; ii++) {
148 gtk_combo_box_text_append_text (combo_box_text, address_strings[ii]);
151 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box_text), 0);
153 g_strfreev (address_strings);
154 g_clear_object (&comp_editor);
157 static void
158 ecep_general_source_combo_box_changed_cb (ESourceComboBox *source_combo_box,
159 ECompEditorPageGeneral *page_general)
161 ESource *source;
163 g_return_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box));
164 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
166 source = e_source_combo_box_ref_active (source_combo_box);
167 e_comp_editor_page_general_set_selected_source (page_general, source);
168 g_clear_object (&source);
171 static void
172 ecep_general_attendees_clicked_cb (GtkWidget *widget,
173 ECompEditorPageGeneral *page_general)
175 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
177 e_meeting_list_view_invite_others_dialog (E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view));
180 static void
181 ecep_general_attendees_add_clicked_cb (GtkButton *button,
182 ECompEditorPageGeneral *page_general)
184 ECompEditor *comp_editor;
185 EMeetingAttendee *attendee;
186 guint32 flags;
188 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
190 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
191 flags = e_comp_editor_get_flags (comp_editor);
193 attendee = e_meeting_store_add_attendee_with_defaults (page_general->priv->meeting_store);
195 if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0)
196 e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s",
197 page_general->priv->user_delegator ? page_general->priv->user_delegator : ""));
199 e_meeting_list_view_edit (E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), attendee);
201 g_clear_object (&comp_editor);
204 static void
205 ecep_general_attendees_edit_clicked_cb (GtkButton *button,
206 ECompEditorPageGeneral *page_general)
208 GtkTreeView *tree_view;
209 GtkTreePath *path = NULL;
210 GtkTreeViewColumn *focus_col;
212 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
214 tree_view = GTK_TREE_VIEW (page_general->priv->attendees_list_view);
216 gtk_tree_view_get_cursor (tree_view, &path, NULL);
217 g_return_if_fail (path != NULL);
219 gtk_tree_view_get_cursor (tree_view, &path, &focus_col);
220 gtk_tree_view_set_cursor (tree_view, path, focus_col, TRUE);
221 gtk_tree_path_free (path);
224 static void
225 ecep_general_remove_attendee (ECompEditorPageGeneral *page_general,
226 EMeetingAttendee *attendee)
228 ECompEditor *comp_editor;
229 gint pos = 0;
231 /* If this was a delegatee, no longer delegate */
232 if (e_meeting_attendee_is_set_delfrom (attendee)) {
233 EMeetingAttendee *ib;
235 ib = e_meeting_store_find_attendee (page_general->priv->meeting_store, e_meeting_attendee_get_delfrom (attendee), &pos);
236 if (ib != NULL) {
237 ECompEditor *comp_editor;
238 ECompEditorFlags flags;
240 e_meeting_attendee_set_delto (ib, NULL);
242 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
243 flags = e_comp_editor_get_flags (comp_editor);
245 if (!(flags & E_COMP_EDITOR_FLAG_DELEGATE))
246 e_meeting_attendee_set_edit_level (ib, E_MEETING_ATTENDEE_EDIT_FULL);
248 g_clear_object (&comp_editor);
252 /* Handle deleting all attendees in the delegation chain */
253 while (attendee) {
254 EMeetingAttendee *ib = NULL;
256 if (e_meeting_attendee_get_delto (attendee)) {
257 ib = e_meeting_store_find_attendee (page_general->priv->meeting_store,
258 e_meeting_attendee_get_delto (attendee), NULL);
261 e_meeting_list_view_remove_attendee_from_name_selector (
262 E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), attendee);
263 e_meeting_store_remove_attendee (page_general->priv->meeting_store, attendee);
265 attendee = ib;
268 ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
270 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
271 e_comp_editor_set_changed (comp_editor, TRUE);
272 g_clear_object (&comp_editor);
275 static void
276 ecep_general_attendees_remove_clicked_cb (GtkButton *button,
277 ECompEditorPageGeneral *page_general)
279 EMeetingAttendee *attendee;
280 GtkTreeSelection *selection;
281 GList *paths = NULL, *tmp;
282 GtkTreeIter iter;
283 GtkTreePath *path = NULL;
284 GtkTreeModel *model = NULL;
285 gboolean valid_iter;
286 gchar *address;
287 gint failures = 0;
288 GString *errors = NULL;
290 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
292 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
293 paths = gtk_tree_selection_get_selected_rows (selection, &model);
294 g_return_if_fail (paths != NULL);
296 paths = g_list_reverse (paths);
298 for (tmp = paths; tmp; tmp = tmp->next) {
299 path = tmp->data;
301 gtk_tree_model_get_iter (model, &iter, path);
303 gtk_tree_model_get (model, &iter, E_MEETING_STORE_ADDRESS_COL, &address, -1);
304 attendee = e_meeting_store_find_attendee (E_MEETING_STORE (model), address, NULL);
306 if (!attendee) {
307 if (!errors)
308 errors = g_string_new ("");
309 else
310 g_string_append (errors, "\n");
311 g_string_append_printf (errors, _("Cannot find attendee “%s” in the list of attendees"), address);
312 failures++;
313 } else if (e_meeting_attendee_get_edit_level (attendee) != E_MEETING_ATTENDEE_EDIT_FULL) {
314 if (!errors)
315 errors = g_string_new ("");
316 else
317 g_string_append (errors, "\n");
318 g_string_append_printf (errors, _("Not enough rights to delete attendee “%s”"), e_meeting_attendee_get_address (attendee));
319 failures++;
320 } else {
321 ecep_general_remove_attendee (page_general, attendee);
324 g_free (address);
327 /* Select closest item after removal */
328 valid_iter = gtk_tree_model_get_iter (model, &iter, path);
329 if (!valid_iter) {
330 gtk_tree_path_prev (path);
331 valid_iter = gtk_tree_model_get_iter (model, &iter, path);
334 if (valid_iter) {
335 gtk_tree_selection_unselect_all (selection);
336 gtk_tree_selection_select_iter (selection, &iter);
339 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
341 if (errors) {
342 ECompEditor *comp_editor;
343 EAlert *alert;
345 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
347 alert = e_comp_editor_add_error (comp_editor, g_dngettext (GETTEXT_PACKAGE,
348 "Failed to delete selected attendee",
349 "Failed to delete selected attendees",
350 failures), errors->str);
352 g_string_free (errors, TRUE);
353 g_clear_object (&alert);
354 g_clear_object (&comp_editor);
358 static void
359 ecep_general_attendees_selection_changed_cb (GtkTreeSelection *selection,
360 ECompEditorPageGeneral *page_general)
362 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
363 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
365 ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
368 static void
369 ecep_general_attendee_added_cb (EMeetingListView *meeting_list_view,
370 EMeetingAttendee *attendee,
371 ECompEditorPageGeneral *page_general)
373 ECompEditor *comp_editor;
374 ECompEditorFlags flags;
375 ECalClient *client;
377 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
378 flags = e_comp_editor_get_flags (comp_editor);
380 e_comp_editor_set_changed (comp_editor, TRUE);
382 if (!(flags & E_COMP_EDITOR_FLAG_DELEGATE)) {
383 g_clear_object (&comp_editor);
384 return;
387 client = e_comp_editor_get_target_client (comp_editor);
389 /* do not remove here, it did EMeetingListView already */
390 e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s",
391 page_general->priv->user_delegator ? page_general->priv->user_delegator : ""));
393 if (client && !e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY)) {
394 EMeetingAttendee *delegator;
396 delegator = e_meeting_store_find_attendee (page_general->priv->meeting_store,
397 page_general->priv->user_delegator, NULL);
398 g_return_if_fail (delegator != NULL);
400 e_meeting_attendee_set_delto (delegator, g_strdup (e_meeting_attendee_get_address (attendee)));
403 ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
405 g_clear_object (&comp_editor);
408 static gboolean
409 ecep_general_get_organizer (ECompEditorPageGeneral *page_general,
410 gchar **out_name,
411 gchar **out_mailto,
412 const gchar **out_error_message)
414 gchar *organizer_text;
415 gboolean valid = FALSE;
417 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
419 organizer_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box));
420 if (organizer_text) {
421 CamelInternetAddress *address;
422 const gchar *str_name, *str_address;
424 address = camel_internet_address_new ();
425 if (camel_address_unformat (CAMEL_ADDRESS (address), organizer_text) == 1 &&
426 camel_internet_address_get (address, 0, &str_name, &str_address)) {
427 valid = TRUE;
429 if (out_name)
430 *out_name = g_strdup (str_name);
431 if (out_mailto)
432 *out_mailto = g_strconcat ("MAILTO:", itip_strip_mailto (str_address), NULL);
433 } else if (out_error_message) {
434 *out_error_message = _("Organizer address is not a valid user mail address");
437 g_object_unref (address);
438 g_free (organizer_text);
441 return valid;
444 static gboolean
445 ecep_general_pick_organizer_for_email_address (ECompEditorPageGeneral *page_general,
446 const gchar *email_address)
448 GtkTreeModel *model;
449 GtkTreeIter iter;
450 GtkComboBox *combo_box;
451 gint ii, entry_text_column;
453 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
455 email_address = itip_strip_mailto (email_address);
457 if (!email_address || !*email_address)
458 return FALSE;
460 combo_box = GTK_COMBO_BOX (page_general->priv->organizer_combo_box);
461 model = gtk_combo_box_get_model (combo_box);
462 entry_text_column = gtk_combo_box_get_entry_text_column (combo_box);
464 if (!gtk_tree_model_get_iter_first (model, &iter))
465 return FALSE;
467 ii = 0;
469 do {
470 gchar *value = NULL;
472 gtk_tree_model_get (model, &iter, entry_text_column, &value, -1);
474 if (value && g_strrstr (value, email_address)) {
475 g_free (value);
476 gtk_combo_box_set_active (combo_box, ii);
477 return TRUE;
480 g_free (value);
482 ii++;
483 } while (gtk_tree_model_iter_next (model, &iter));
485 return FALSE;
488 static void
489 ecep_general_target_client_notify_cb (ECompEditor *comp_editor,
490 GParamSpec *param,
491 ECompEditorPageGeneral *page_general)
493 const gchar *cal_email_address;
495 g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
496 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
498 cal_email_address = e_comp_editor_get_cal_email_address (comp_editor);
499 ecep_general_pick_organizer_for_email_address (page_general, cal_email_address);
502 static gboolean
503 ecep_general_list_view_event_cb (EMeetingListView *list_view,
504 GdkEvent *event,
505 ECompEditorPageGeneral *page_general)
507 g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (list_view), FALSE);
508 g_return_val_if_fail (event != NULL, FALSE);
509 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
511 if (event->type == GDK_2BUTTON_PRESS && gtk_widget_get_sensitive (GTK_WIDGET (list_view)) &&
512 gtk_widget_get_sensitive (page_general->priv->attendees_button_add)) {
513 EMeetingAttendee *attendee;
514 ECompEditor *comp_editor;
515 guint32 flags;
517 attendee = e_meeting_store_add_attendee_with_defaults (page_general->priv->meeting_store);
519 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
520 flags = e_comp_editor_get_flags (comp_editor);
522 if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0) {
523 e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s", page_general->priv->user_delegator));
526 g_clear_object (&comp_editor);
527 e_meeting_list_view_edit (list_view, attendee);
529 return TRUE;
532 return FALSE;
535 static gboolean
536 ecep_general_list_view_key_press_cb (EMeetingListView *list_view,
537 GdkEventKey *event,
538 ECompEditorPageGeneral *page_general)
540 g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (list_view), FALSE);
541 g_return_val_if_fail (event != NULL, FALSE);
542 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
544 if (event->keyval == GDK_KEY_Delete) {
545 if (gtk_widget_get_sensitive (page_general->priv->attendees_button_remove))
546 ecep_general_attendees_remove_clicked_cb (NULL, page_general);
547 return TRUE;
548 } else if (event->keyval == GDK_KEY_Insert) {
549 if (gtk_widget_get_sensitive (page_general->priv->attendees_button_add))
550 ecep_general_attendees_add_clicked_cb (NULL, page_general);
551 return TRUE;
554 return FALSE;
557 static void
558 ecep_general_init_ui (ECompEditorPageGeneral *page_general,
559 ECompEditor *comp_editor)
561 const gchar *ui =
562 "<ui>"
563 " <menubar action='main-menu'>"
564 " <menu action='view-menu'>"
565 " <placeholder name='columns'>"
566 " <menuitem action='view-role'/>"
567 " <menuitem action='view-rsvp'/>"
568 " <menuitem action='view-status'/>"
569 " <menuitem action='view-type'/>"
570 " </placeholder>"
571 " </menu>"
572 " <menu action='options-menu'>"
573 " <placeholder name='toggles'>"
574 " <menuitem action='option-attendees'/>"
575 " </placeholder>"
576 " </menu>"
577 " </menubar>"
578 "</ui>";
580 const GtkToggleActionEntry attendees_toggle_entry[] = {
582 { "option-attendees",
583 NULL,
584 N_("A_ttendees"),
585 NULL,
586 N_("Toggles whether the Attendees are displayed"),
587 NULL,
588 FALSE }
591 const GtkToggleActionEntry columns_toggle_entries[] = {
593 { "view-role",
594 NULL,
595 N_("R_ole Field"),
596 NULL,
597 N_("Toggles whether the Role field is displayed"),
598 G_CALLBACK (action_view_role_cb),
599 TRUE },
601 { "view-rsvp",
602 NULL,
603 N_("_RSVP"),
604 NULL,
605 N_("Toggles whether the RSVP field is displayed"),
606 G_CALLBACK (action_view_rsvp_cb),
607 TRUE },
609 { "view-status",
610 NULL,
611 N_("_Status Field"),
612 NULL,
613 N_("Toggles whether the Status field is displayed"),
614 G_CALLBACK (action_view_status_cb),
615 TRUE },
617 { "view-type",
618 NULL,
619 N_("_Type Field"),
620 NULL,
621 N_("Toggles whether the Attendee Type is displayed"),
622 G_CALLBACK (action_view_type_cb),
623 TRUE }
626 GtkUIManager *ui_manager;
627 GtkActionGroup *action_group;
628 GtkAction *action;
629 GSettings *settings;
630 GError *error = NULL;
632 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
633 g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
635 settings = e_comp_editor_get_settings (comp_editor);
636 ui_manager = e_comp_editor_get_ui_manager (comp_editor);
638 action_group = gtk_action_group_new ("columns");
639 gtk_action_group_set_translation_domain (
640 action_group, GETTEXT_PACKAGE);
641 gtk_action_group_add_toggle_actions (
642 action_group, columns_toggle_entries,
643 G_N_ELEMENTS (columns_toggle_entries), page_general);
644 gtk_ui_manager_insert_action_group (
645 ui_manager, action_group, 0);
647 e_binding_bind_property (
648 page_general, "show-attendees",
649 action_group, "sensitive",
650 G_BINDING_SYNC_CREATE);
652 g_object_unref (action_group);
654 action_group = e_comp_editor_get_action_group (comp_editor, "individual");
655 gtk_action_group_add_toggle_actions (
656 action_group, attendees_toggle_entry,
657 G_N_ELEMENTS (attendees_toggle_entry), page_general);
659 gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
661 if (error) {
662 g_critical ("%s: %s", G_STRFUNC, error->message);
663 g_error_free (error);
666 action = e_comp_editor_get_action (comp_editor, "option-attendees");
667 e_binding_bind_property (
668 page_general, "show-attendees",
669 action, "active",
670 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
672 action = e_comp_editor_get_action (comp_editor, "view-role");
673 g_settings_bind (
674 settings, "editor-show-role",
675 action, "active",
676 G_SETTINGS_BIND_DEFAULT);
678 action = e_comp_editor_get_action (comp_editor, "view-rsvp");
679 g_settings_bind (
680 settings, "editor-show-rsvp",
681 action, "active",
682 G_SETTINGS_BIND_DEFAULT);
684 action = e_comp_editor_get_action (comp_editor, "view-status");
685 g_settings_bind (
686 settings, "editor-show-status",
687 action, "active",
688 G_SETTINGS_BIND_DEFAULT);
690 action = e_comp_editor_get_action (comp_editor, "view-type");
691 g_settings_bind (
692 settings, "editor-show-type",
693 action, "active",
694 G_SETTINGS_BIND_DEFAULT);
697 static void
698 ecep_general_sensitize_widgets (ECompEditorPage *page,
699 gboolean force_insensitive)
701 ECompEditorPageGeneral *page_general;
702 GtkTreeSelection *selection;
703 GtkAction *action;
704 gboolean sensitive, organizer_is_user, delegate, delegate_to_many = FALSE, read_only = TRUE, any_selected = FALSE;
705 ECompEditor *comp_editor;
706 ECalClient *client;
707 guint32 flags;
709 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page));
711 E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->sensitize_widgets (page, force_insensitive);
713 page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
715 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
716 flags = e_comp_editor_get_flags (comp_editor);
717 client = e_comp_editor_get_target_client (comp_editor);
719 if (client) {
720 EClient *cl = E_CLIENT (client);
722 read_only = e_client_is_readonly (cl);
723 delegate_to_many = e_client_check_capability (cl, CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY);
724 } else {
725 force_insensitive = TRUE;
728 delegate = (flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0;
729 organizer_is_user = (flags & (E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER | E_COMP_EDITOR_FLAG_IS_NEW)) != 0 ||
730 !e_comp_editor_page_general_get_show_attendees (page_general);
731 sensitive = (!read_only && organizer_is_user) || delegate;
733 if (!delegate)
734 delegate_to_many = TRUE;
736 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
737 any_selected = selection && gtk_tree_selection_count_selected_rows (selection) > 0;
739 gtk_widget_set_sensitive (page_general->priv->organizer_label, !force_insensitive);
740 gtk_widget_set_sensitive (page_general->priv->organizer_combo_box, !read_only && !force_insensitive);
741 gtk_widget_set_sensitive (page_general->priv->attendees_button, sensitive && delegate_to_many && !force_insensitive);
742 gtk_widget_set_sensitive (page_general->priv->attendees_hbox, !force_insensitive);
743 gtk_widget_set_sensitive (page_general->priv->attendees_button_add, sensitive && delegate_to_many && !force_insensitive);
744 gtk_widget_set_sensitive (page_general->priv->attendees_button_edit, sensitive && delegate_to_many && !force_insensitive && any_selected);
745 gtk_widget_set_sensitive (page_general->priv->attendees_button_remove, sensitive && !force_insensitive && any_selected);
746 e_meeting_list_view_set_editable (E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), sensitive && !force_insensitive);
747 gtk_widget_set_sensitive (page_general->priv->attendees_list_view, !read_only && !force_insensitive);
749 action = e_comp_editor_get_action (comp_editor, "option-attendees");
750 gtk_action_set_sensitive (action, !force_insensitive && !read_only && organizer_is_user);
752 g_clear_object (&comp_editor);
755 static void
756 ecep_general_fill_widgets (ECompEditorPage *page,
757 icalcomponent *component)
759 ECompEditorPageGeneral *page_general;
760 icalproperty *prop;
762 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page));
763 g_return_if_fail (component != NULL);
765 E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->fill_widgets (page, component);
767 page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
769 g_slist_free_full (page_general->priv->orig_attendees, g_free);
770 page_general->priv->orig_attendees = NULL;
772 for (prop = icalcomponent_get_first_property (component, ICAL_ATTENDEE_PROPERTY);
773 prop;
774 prop = icalcomponent_get_next_property (component, ICAL_ATTENDEE_PROPERTY)) {
775 const gchar *address;
777 address = itip_strip_mailto (icalproperty_get_attendee (prop));
778 if (address)
779 page_general->priv->orig_attendees = g_slist_prepend (page_general->priv->orig_attendees, g_strdup (address));
782 page_general->priv->orig_attendees = g_slist_reverse (page_general->priv->orig_attendees);
784 prop = icalcomponent_get_first_property (component, ICAL_ORGANIZER_PROPERTY);
785 if (prop) {
786 icalparameter *param;
787 const gchar *organizer;
789 organizer = icalproperty_get_organizer (prop);
791 if (organizer && *organizer) {
792 ECompEditor *comp_editor;
793 ESourceRegistry *registry;
794 GtkEntry *combo_box_entry;
795 guint32 flags;
796 gchar *value = NULL;
798 comp_editor = e_comp_editor_page_ref_editor (page);
799 flags = e_comp_editor_get_flags (comp_editor);
800 registry = e_shell_get_registry (e_comp_editor_get_shell (comp_editor));
802 flags = flags & E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
804 if (itip_address_is_user (registry, itip_strip_mailto (organizer))) {
805 flags = flags | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
806 } else {
807 param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
808 if (param) {
809 const gchar *sentby = icalparameter_get_sentby (param);
811 if (sentby && *sentby &&
812 itip_address_is_user (registry, itip_strip_mailto (organizer))) {
813 flags = flags | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
818 e_comp_editor_page_general_set_show_attendees (page_general, TRUE);
820 param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
821 if (param) {
822 const gchar *cn;
824 cn = icalparameter_get_cn (param);
825 if (cn && *cn) {
826 value = camel_internet_address_format_address (cn, itip_strip_mailto (organizer));
830 if (!value)
831 value = g_strdup (itip_strip_mailto (organizer));
833 combo_box_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (page_general->priv->organizer_combo_box)));
835 if (!(flags & E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER)) {
836 GtkComboBoxText *combo_box_text;
838 combo_box_text = GTK_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box);
839 gtk_combo_box_text_remove_all (combo_box_text);
840 gtk_combo_box_text_append_text (combo_box_text, value);
841 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box_text), 0);
842 gtk_editable_set_editable (GTK_EDITABLE (combo_box_entry), FALSE);
843 } else if (!ecep_general_pick_organizer_for_email_address (page_general, organizer)) {
844 gtk_entry_set_text (combo_box_entry, value);
847 e_comp_editor_set_flags (comp_editor, flags);
849 g_clear_object (&comp_editor);
850 g_free (value);
854 e_meeting_store_remove_all_attendees (page_general->priv->meeting_store);
856 for (prop = icalcomponent_get_first_property (component, ICAL_ATTENDEE_PROPERTY);
857 prop;
858 prop = icalcomponent_get_next_property (component, ICAL_ATTENDEE_PROPERTY)) {
859 const gchar *address;
861 address = itip_strip_mailto (icalproperty_get_attendee (prop));
862 if (address) {
863 EMeetingAttendee *attendee;
864 icalparameter *param;
866 attendee = E_MEETING_ATTENDEE (e_meeting_attendee_new ());
868 e_meeting_attendee_set_address (attendee, g_strdup (address));
870 param = icalproperty_get_first_parameter (prop, ICAL_MEMBER_PARAMETER);
871 if (param)
872 e_meeting_attendee_set_member (attendee, g_strdup (icalparameter_get_member (param)));
874 param = icalproperty_get_first_parameter (prop, ICAL_CUTYPE_PARAMETER);
875 if (param)
876 e_meeting_attendee_set_cutype (attendee, icalparameter_get_cutype (param));
878 param = icalproperty_get_first_parameter (prop, ICAL_ROLE_PARAMETER);
879 if (param)
880 e_meeting_attendee_set_role (attendee, icalparameter_get_role (param));
882 param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
883 if (param)
884 e_meeting_attendee_set_rsvp (attendee, icalparameter_get_rsvp (param) == ICAL_RSVP_TRUE);
886 param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDTO_PARAMETER);
887 if (param)
888 e_meeting_attendee_set_delto (attendee, g_strdup (icalparameter_get_delegatedto (param)));
890 param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER);
891 if (param)
892 e_meeting_attendee_set_delfrom (attendee, g_strdup (icalparameter_get_delegatedfrom (param)));
894 param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
895 if (param)
896 e_meeting_attendee_set_status (attendee, icalparameter_get_partstat (param));
898 param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
899 if (param)
900 e_meeting_attendee_set_sentby (attendee, g_strdup (icalparameter_get_sentby (param)));
902 param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
903 if (param)
904 e_meeting_attendee_set_cn (attendee, g_strdup (icalparameter_get_cn (param)));
906 param = icalproperty_get_first_parameter (prop, ICAL_LANGUAGE_PARAMETER);
907 if (param)
908 e_meeting_attendee_set_language (attendee, g_strdup (icalparameter_get_language (param)));
910 e_meeting_store_add_attendee (page_general->priv->meeting_store, attendee);
912 g_object_unref (attendee);
917 static gboolean
918 ecep_general_fill_component (ECompEditorPage *page,
919 icalcomponent *component)
921 ECompEditorPageGeneral *page_general;
922 icalproperty *prop;
924 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page), FALSE);
925 g_return_val_if_fail (component != NULL, FALSE);
927 page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
929 cal_comp_util_remove_all_properties (component, ICAL_ATTENDEE_PROPERTY);
931 if (e_comp_editor_page_general_get_show_attendees (page_general)) {
932 const GPtrArray *attendees;
933 GHashTable *known_attendees;
934 ECompEditor *comp_editor;
935 gchar *organizer_name = NULL, *organizer_mailto = NULL;
936 guint32 flags;
937 gint ii, added_attendees = 0;
938 const gchar *error_message = NULL;
940 comp_editor = e_comp_editor_page_ref_editor (page);
941 flags = e_comp_editor_get_flags (comp_editor);
943 if ((flags & (E_COMP_EDITOR_FLAG_IS_NEW | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER)) != 0 &&
944 !ecep_general_get_organizer (page_general, NULL, NULL, &error_message)) {
945 e_comp_editor_set_validation_error (comp_editor, page,
946 page_general->priv->organizer_combo_box,
947 error_message ? error_message : _("An organizer is required."));
949 g_clear_object (&comp_editor);
951 return FALSE;
954 if (e_meeting_store_count_actual_attendees (page_general->priv->meeting_store) < 1) {
955 e_comp_editor_set_validation_error (comp_editor, page,
956 page_general->priv->attendees_list_view,
957 _("At least one attendee is required."));
959 g_clear_object (&comp_editor);
961 return FALSE;
964 /* Organizer */
965 if ((flags & (E_COMP_EDITOR_FLAG_IS_NEW | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER)) != 0 &&
966 ecep_general_get_organizer (page_general, &organizer_name, &organizer_mailto, NULL)) {
967 const gchar *cal_email_address;
968 icalparameter *param;
970 prop = icalcomponent_get_first_property (component, ICAL_ORGANIZER_PROPERTY);
971 if (!prop) {
972 prop = icalproperty_new_organizer (organizer_mailto ? organizer_mailto : organizer_name);
973 icalcomponent_add_property (component, prop);
974 } else {
975 icalproperty_set_organizer (prop, organizer_mailto ? organizer_mailto : organizer_name);
978 param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
979 if (organizer_name && *organizer_name) {
980 if (!param) {
981 param = icalparameter_new_cn (organizer_name);
982 icalproperty_add_parameter (prop, param);
983 } else {
984 icalparameter_set_cn (param, organizer_name);
986 } else if (param) {
987 icalproperty_remove_parameter_by_kind (prop, ICAL_CN_PARAMETER);
990 param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
991 cal_email_address = e_comp_editor_get_cal_email_address (comp_editor);
992 if (cal_email_address && *cal_email_address) {
993 gchar *sentby;
994 gboolean differs;
996 sentby = g_strconcat ("MAILTO:", cal_email_address, NULL);
997 differs = !organizer_mailto || g_ascii_strcasecmp (sentby, organizer_mailto) != 0;
999 if (differs) {
1000 if (!param) {
1001 param = icalparameter_new_sentby (sentby);
1002 icalproperty_add_parameter (prop, param);
1003 } else {
1004 icalparameter_set_sentby (param, sentby);
1006 } else if (param) {
1007 icalproperty_remove_parameter_by_kind (prop, ICAL_SENTBY_PARAMETER);
1010 g_free (sentby);
1011 } else if (param) {
1012 icalproperty_remove_parameter_by_kind (prop, ICAL_SENTBY_PARAMETER);
1016 /* Attendees */
1017 known_attendees = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
1018 attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
1020 for (ii = 0; ii < attendees->len; ii++) {
1021 EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
1022 const gchar *address;
1024 address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
1025 if (address) {
1026 icalparameter *param;
1028 if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0 &&
1029 (e_meeting_attendee_is_set_delfrom (attendee) || e_meeting_attendee_is_set_delto (attendee)) &&
1030 g_hash_table_contains (known_attendees, address))
1031 continue;
1033 g_hash_table_insert (known_attendees, (gpointer) address, GINT_TO_POINTER (1));
1035 prop = icalproperty_new_attendee (e_meeting_attendee_get_address (attendee));
1036 icalcomponent_add_property (component, prop);
1038 added_attendees++;
1040 if (e_meeting_attendee_is_set_member (attendee)) {
1041 param = icalparameter_new_member (e_meeting_attendee_get_member (attendee));
1042 icalproperty_add_parameter (prop, param);
1045 param = icalparameter_new_cutype (e_meeting_attendee_get_cutype (attendee));
1046 icalproperty_add_parameter (prop, param);
1048 param = icalparameter_new_role (e_meeting_attendee_get_role (attendee));
1049 icalproperty_add_parameter (prop, param);
1051 param = icalparameter_new_partstat (e_meeting_attendee_get_status (attendee));
1052 icalproperty_add_parameter (prop, param);
1054 param = icalparameter_new_rsvp (e_meeting_attendee_get_rsvp (attendee) ? ICAL_RSVP_TRUE : ICAL_RSVP_FALSE);
1055 icalproperty_add_parameter (prop, param);
1057 if (e_meeting_attendee_is_set_delfrom (attendee)) {
1058 param = icalparameter_new_delegatedfrom (e_meeting_attendee_get_delfrom (attendee));
1059 icalproperty_add_parameter (prop, param);
1061 if (e_meeting_attendee_is_set_delto (attendee)) {
1062 param = icalparameter_new_delegatedto (e_meeting_attendee_get_delto (attendee));
1063 icalproperty_add_parameter (prop, param);
1065 if (e_meeting_attendee_is_set_sentby (attendee)) {
1066 param = icalparameter_new_sentby (e_meeting_attendee_get_sentby (attendee));
1067 icalproperty_add_parameter (prop, param);
1069 if (e_meeting_attendee_is_set_cn (attendee)) {
1070 param = icalparameter_new_cn (e_meeting_attendee_get_cn (attendee));
1071 icalproperty_add_parameter (prop, param);
1073 if (e_meeting_attendee_is_set_language (attendee)) {
1074 param = icalparameter_new_language (e_meeting_attendee_get_language (attendee));
1075 icalproperty_add_parameter (prop, param);
1080 g_hash_table_destroy (known_attendees);
1082 g_free (organizer_name);
1083 g_free (organizer_mailto);
1085 if (!added_attendees) {
1086 e_comp_editor_set_validation_error (comp_editor, page,
1087 page_general->priv->attendees_list_view,
1088 _("At least one attendee is required."));
1090 g_clear_object (&comp_editor);
1092 return FALSE;
1095 g_clear_object (&comp_editor);
1096 } else {
1097 cal_comp_util_remove_all_properties (component, ICAL_ORGANIZER_PROPERTY);
1100 return E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->fill_component (page, component);
1103 static void
1104 ecep_general_set_property (GObject *object,
1105 guint property_id,
1106 const GValue *value,
1107 GParamSpec *pspec)
1109 switch (property_id) {
1110 case PROP_DATA_COLUMN_WIDTH:
1111 e_comp_editor_page_general_set_data_column_width (
1112 E_COMP_EDITOR_PAGE_GENERAL (object),
1113 g_value_get_int (value));
1114 return;
1116 case PROP_SOURCE_LABEL:
1117 e_comp_editor_page_general_set_source_label (
1118 E_COMP_EDITOR_PAGE_GENERAL (object),
1119 g_value_get_string (value));
1120 return;
1122 case PROP_SOURCE_EXTENSION_NAME:
1123 e_comp_editor_page_general_set_source_extension_name (
1124 E_COMP_EDITOR_PAGE_GENERAL (object),
1125 g_value_get_string (value));
1126 return;
1128 case PROP_SELECTED_SOURCE:
1129 e_comp_editor_page_general_set_selected_source (
1130 E_COMP_EDITOR_PAGE_GENERAL (object),
1131 g_value_get_object (value));
1132 return;
1134 case PROP_SHOW_ATTENDEES:
1135 e_comp_editor_page_general_set_show_attendees (
1136 E_COMP_EDITOR_PAGE_GENERAL (object),
1137 g_value_get_boolean (value));
1138 return;
1141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1144 static void
1145 ecep_general_get_property (GObject *object,
1146 guint property_id,
1147 GValue *value,
1148 GParamSpec *pspec)
1150 switch (property_id) {
1151 case PROP_DATA_COLUMN_WIDTH:
1152 g_value_set_int (
1153 value,
1154 e_comp_editor_page_general_get_data_column_width (
1155 E_COMP_EDITOR_PAGE_GENERAL (object)));
1156 return;
1158 case PROP_SOURCE_LABEL:
1159 g_value_set_string (
1160 value,
1161 e_comp_editor_page_general_get_source_label (
1162 E_COMP_EDITOR_PAGE_GENERAL (object)));
1163 return;
1165 case PROP_SOURCE_EXTENSION_NAME:
1166 g_value_set_string (
1167 value,
1168 e_comp_editor_page_general_get_source_extension_name (
1169 E_COMP_EDITOR_PAGE_GENERAL (object)));
1170 return;
1172 case PROP_SELECTED_SOURCE:
1173 g_value_take_object (
1174 value,
1175 e_comp_editor_page_general_ref_selected_source (
1176 E_COMP_EDITOR_PAGE_GENERAL (object)));
1177 return;
1179 case PROP_SHOW_ATTENDEES:
1180 g_value_set_boolean (
1181 value,
1182 e_comp_editor_page_general_get_show_attendees (
1183 E_COMP_EDITOR_PAGE_GENERAL (object)));
1184 return;
1187 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1190 static void
1191 ecep_general_constructed (GObject *object)
1193 ECompEditor *comp_editor;
1194 ECompEditorPageGeneral *page_general;
1195 GtkWidget *widget, *scrolled_window;
1196 GtkTreeSelection *selection;
1197 GtkGrid *grid;
1198 EShell *shell;
1200 page_general = E_COMP_EDITOR_PAGE_GENERAL (object);
1202 G_OBJECT_CLASS (e_comp_editor_page_general_parent_class)->constructed (object);
1204 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
1205 g_return_if_fail (comp_editor != NULL);
1207 page_general->priv->meeting_store = E_MEETING_STORE (e_meeting_store_new ());
1209 grid = GTK_GRID (page_general);
1211 widget = gtk_label_new_with_mnemonic (_("Or_ganizer:"));
1212 g_object_set (G_OBJECT (widget),
1213 "hexpand", FALSE,
1214 "halign", GTK_ALIGN_END,
1215 "vexpand", FALSE,
1216 "valign", GTK_ALIGN_CENTER,
1217 NULL);
1218 gtk_grid_attach (grid, widget, 0, 0, 1, 1);
1219 gtk_widget_hide (widget);
1221 page_general->priv->organizer_label = widget;
1223 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
1224 g_object_set (G_OBJECT (widget),
1225 "hexpand", TRUE,
1226 "halign", GTK_ALIGN_FILL,
1227 "vexpand", FALSE,
1228 "valign", GTK_ALIGN_FILL,
1229 NULL);
1230 gtk_grid_attach (grid, widget, 1, 0, page_general->priv->data_column_width, 1);
1231 gtk_widget_hide (widget);
1233 page_general->priv->organizer_hbox = widget;
1235 widget = gtk_combo_box_text_new_with_entry ();
1236 g_object_set (G_OBJECT (widget),
1237 "hexpand", TRUE,
1238 "halign", GTK_ALIGN_FILL,
1239 "vexpand", FALSE,
1240 "valign", GTK_ALIGN_START,
1241 NULL);
1242 gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, TRUE, TRUE, 0);
1243 gtk_widget_show (widget);
1245 page_general->priv->organizer_combo_box = widget;
1247 /* The list of organizers is set to be non-editable. Otherwise any
1248 * change in the displayed list causes an 'Account not found' error.
1250 gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (widget))), FALSE);
1252 ecep_general_fill_organizer_combo_box (page_general);
1254 g_signal_connect_swapped (page_general->priv->organizer_combo_box, "changed",
1255 G_CALLBACK (e_comp_editor_ensure_changed), comp_editor);
1257 widget = gtk_label_new_with_mnemonic (page_general->priv->source_label_text);
1258 g_object_set (G_OBJECT (widget),
1259 "hexpand", FALSE,
1260 "halign", GTK_ALIGN_END,
1261 "vexpand", FALSE,
1262 "valign", GTK_ALIGN_CENTER,
1263 NULL);
1264 gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, FALSE, FALSE, 0);
1265 gtk_widget_show (widget);
1267 page_general->priv->source_label = widget;
1269 shell = e_comp_editor_get_shell (comp_editor);
1270 widget = e_source_combo_box_new (
1271 e_shell_get_registry (shell),
1272 page_general->priv->source_extension_name);
1273 e_source_combo_box_set_show_colors (E_SOURCE_COMBO_BOX (widget), TRUE);
1274 g_object_set (G_OBJECT (widget),
1275 "hexpand", TRUE,
1276 "halign", GTK_ALIGN_FILL,
1277 "vexpand", FALSE,
1278 "valign", GTK_ALIGN_START,
1279 NULL);
1280 gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, TRUE, TRUE, 0);
1281 gtk_widget_show (widget);
1283 page_general->priv->source_combo_box = widget;
1285 gtk_label_set_mnemonic_widget (GTK_LABEL (page_general->priv->source_label),
1286 page_general->priv->source_combo_box);
1287 g_signal_connect (page_general->priv->source_combo_box, "changed",
1288 G_CALLBACK (ecep_general_source_combo_box_changed_cb), page_general);
1290 widget = gtk_button_new_with_mnemonic (C_("ECompEditor", "Atte_ndees..."));
1291 g_object_set (G_OBJECT (widget),
1292 "hexpand", FALSE,
1293 "halign", GTK_ALIGN_END,
1294 "vexpand", FALSE,
1295 "valign", GTK_ALIGN_START,
1296 NULL);
1297 gtk_grid_attach (grid, widget, 0, 1, 1, 1);
1298 gtk_widget_hide (widget);
1300 page_general->priv->attendees_button = widget;
1302 g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_clicked_cb), page_general);
1304 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1305 g_object_set (G_OBJECT (widget),
1306 "hexpand", TRUE,
1307 "halign", GTK_ALIGN_FILL,
1308 "vexpand", FALSE,
1309 "valign", GTK_ALIGN_FILL,
1310 NULL);
1311 gtk_grid_attach (grid, widget, 1, 1, page_general->priv->data_column_width, 1);
1312 gtk_widget_hide (widget);
1314 page_general->priv->attendees_hbox = widget;
1316 widget = GTK_WIDGET (e_meeting_list_view_new (page_general->priv->meeting_store));
1317 g_object_set (G_OBJECT (widget),
1318 "hexpand", FALSE,
1319 "halign", GTK_ALIGN_FILL,
1320 "vexpand", FALSE,
1321 "valign", GTK_ALIGN_FILL,
1322 NULL);
1323 gtk_widget_show (widget);
1325 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1326 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1327 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
1328 gtk_widget_show (scrolled_window);
1330 gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
1331 gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_hbox), scrolled_window, TRUE, TRUE, 0);
1333 page_general->priv->attendees_list_view = widget;
1335 g_signal_connect (page_general->priv->attendees_list_view, "attendee-added",
1336 G_CALLBACK (ecep_general_attendee_added_cb), page_general);
1338 g_signal_connect (page_general->priv->attendees_list_view, "event",
1339 G_CALLBACK (ecep_general_list_view_event_cb), page_general);
1341 g_signal_connect (page_general->priv->attendees_list_view, "key_press_event",
1342 G_CALLBACK (ecep_general_list_view_key_press_cb), page_general);
1344 widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
1345 g_object_set (G_OBJECT (widget),
1346 "hexpand", FALSE,
1347 "halign", GTK_ALIGN_START,
1348 "vexpand", FALSE,
1349 "valign", GTK_ALIGN_START,
1350 NULL);
1351 gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_hbox), widget, FALSE, FALSE, 0);
1352 gtk_widget_show (widget);
1354 page_general->priv->attendees_button_box = widget;
1356 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
1357 g_signal_connect (selection, "changed", G_CALLBACK (ecep_general_attendees_selection_changed_cb), page_general);
1359 widget = gtk_button_new_with_mnemonic (_("_Add"));
1360 gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
1361 gtk_widget_show (widget);
1363 page_general->priv->attendees_button_add = widget;
1365 g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_add_clicked_cb), page_general);
1367 widget = gtk_button_new_with_mnemonic (_("_Edit"));
1368 gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
1369 gtk_widget_show (widget);
1371 page_general->priv->attendees_button_edit = widget;
1373 g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_edit_clicked_cb), page_general);
1375 widget = gtk_button_new_with_mnemonic (_("_Remove"));
1376 gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
1377 gtk_widget_show (widget);
1379 page_general->priv->attendees_button_remove = widget;
1381 g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_remove_clicked_cb), page_general);
1383 e_signal_connect_notify (comp_editor, "notify::target-client", G_CALLBACK (ecep_general_target_client_notify_cb), page_general);
1385 ecep_general_init_ui (page_general, comp_editor);
1387 g_clear_object (&comp_editor);
1390 static void
1391 ecep_general_finalize (GObject *object)
1393 ECompEditorPageGeneral *page_general;
1395 page_general = E_COMP_EDITOR_PAGE_GENERAL (object);
1397 g_free (page_general->priv->source_label_text);
1398 page_general->priv->source_label_text = NULL;
1400 g_free (page_general->priv->source_extension_name);
1401 page_general->priv->source_extension_name = NULL;
1403 g_free (page_general->priv->user_delegator);
1404 page_general->priv->user_delegator = NULL;
1406 g_clear_object (&page_general->priv->select_source);
1407 g_clear_object (&page_general->priv->meeting_store);
1409 g_slist_free_full (page_general->priv->orig_attendees, g_free);
1410 page_general->priv->orig_attendees = NULL;
1412 G_OBJECT_CLASS (e_comp_editor_page_general_parent_class)->finalize (object);
1415 static void
1416 e_comp_editor_page_general_init (ECompEditorPageGeneral *page_general)
1418 page_general->priv = G_TYPE_INSTANCE_GET_PRIVATE (page_general,
1419 E_TYPE_COMP_EDITOR_PAGE_GENERAL,
1420 ECompEditorPageGeneralPrivate);
1423 static void
1424 e_comp_editor_page_general_class_init (ECompEditorPageGeneralClass *klass)
1426 ECompEditorPageClass *page_class;
1427 GObjectClass *object_class;
1429 g_type_class_add_private (klass, sizeof (ECompEditorPageGeneralPrivate));
1431 page_class = E_COMP_EDITOR_PAGE_CLASS (klass);
1432 page_class->sensitize_widgets = ecep_general_sensitize_widgets;
1433 page_class->fill_widgets = ecep_general_fill_widgets;
1434 page_class->fill_component = ecep_general_fill_component;
1436 object_class = G_OBJECT_CLASS (klass);
1437 object_class->set_property = ecep_general_set_property;
1438 object_class->get_property = ecep_general_get_property;
1439 object_class->constructed = ecep_general_constructed;
1440 object_class->finalize = ecep_general_finalize;
1442 g_object_class_install_property (
1443 object_class,
1444 PROP_DATA_COLUMN_WIDTH,
1445 g_param_spec_int (
1446 "data-column-width",
1447 "Data Column Width",
1448 "How many columns should the data column occupy",
1449 1, G_MAXINT, 1,
1450 G_PARAM_READWRITE |
1451 G_PARAM_CONSTRUCT |
1452 G_PARAM_STATIC_STRINGS));
1454 g_object_class_install_property (
1455 object_class,
1456 PROP_SOURCE_LABEL,
1457 g_param_spec_string (
1458 "source-label",
1459 "Source Label",
1460 "Label to use for the source selector",
1461 NULL,
1462 G_PARAM_READWRITE |
1463 G_PARAM_CONSTRUCT_ONLY |
1464 G_PARAM_STATIC_STRINGS));
1466 g_object_class_install_property (
1467 object_class,
1468 PROP_SOURCE_EXTENSION_NAME,
1469 g_param_spec_string (
1470 "source-extension-name",
1471 "Source Extension Name",
1472 "Extension name to use for the source selector",
1473 NULL,
1474 G_PARAM_READWRITE |
1475 G_PARAM_CONSTRUCT_ONLY |
1476 G_PARAM_STATIC_STRINGS));
1478 g_object_class_install_property (
1479 object_class,
1480 PROP_SELECTED_SOURCE,
1481 g_param_spec_object (
1482 "selected-source",
1483 "Selected Source",
1484 "Which source is currently selected in the source selector",
1485 E_TYPE_SOURCE,
1486 G_PARAM_READWRITE |
1487 G_PARAM_STATIC_STRINGS));
1489 g_object_class_install_property (
1490 object_class,
1491 PROP_SHOW_ATTENDEES,
1492 g_param_spec_boolean (
1493 "show-attendees",
1494 "Show Attendees",
1495 "Whether to show also attendees",
1496 TRUE,
1497 G_PARAM_READWRITE |
1498 G_PARAM_CONSTRUCT |
1499 G_PARAM_STATIC_STRINGS));
1502 ECompEditorPage *
1503 e_comp_editor_page_general_new (ECompEditor *editor,
1504 const gchar *source_label,
1505 const gchar *source_extension_name,
1506 ESource *select_source,
1507 gboolean show_attendees,
1508 gint data_column_width)
1510 g_return_val_if_fail (E_IS_COMP_EDITOR (editor), NULL);
1511 g_return_val_if_fail (source_label != NULL, NULL);
1512 g_return_val_if_fail (source_extension_name != NULL, NULL);
1514 if (select_source)
1515 g_return_val_if_fail (E_IS_SOURCE (select_source), NULL);
1517 return g_object_new (E_TYPE_COMP_EDITOR_PAGE_GENERAL,
1518 "editor", editor,
1519 "source-label", source_label,
1520 "source-extension-name", source_extension_name,
1521 "selected-source", select_source,
1522 "show-attendees", show_attendees,
1523 "data-column-width", data_column_width,
1524 NULL);
1527 const gchar *
1528 e_comp_editor_page_general_get_source_label (ECompEditorPageGeneral *page_general)
1530 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1532 if (page_general->priv->source_label)
1533 return gtk_label_get_text (GTK_LABEL (page_general->priv->source_label));
1535 return page_general->priv->source_label_text;
1538 void
1539 e_comp_editor_page_general_set_source_label (ECompEditorPageGeneral *page_general,
1540 const gchar *source_label)
1542 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1543 g_return_if_fail (source_label != NULL);
1545 if (page_general->priv->source_label) {
1546 if (g_strcmp0 (source_label, gtk_label_get_text (GTK_LABEL (page_general->priv->source_label))) != 0) {
1547 gtk_label_set_text (GTK_LABEL (page_general->priv->source_label), source_label);
1549 g_object_notify (G_OBJECT (page_general), "source-label");
1551 } else {
1552 g_free (page_general->priv->source_label_text);
1553 page_general->priv->source_label_text = g_strdup (source_label);
1555 g_object_notify (G_OBJECT (page_general), "source-label");
1559 const gchar *
1560 e_comp_editor_page_general_get_source_extension_name (ECompEditorPageGeneral *page_general)
1562 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1564 if (page_general->priv->source_combo_box)
1565 return e_source_combo_box_get_extension_name (E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box));
1567 return page_general->priv->source_extension_name;
1570 void
1571 e_comp_editor_page_general_set_source_extension_name (ECompEditorPageGeneral *page_general,
1572 const gchar *source_extension_name)
1574 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1576 if (g_strcmp0 (page_general->priv->source_extension_name, source_extension_name) == 0)
1577 return;
1579 g_free (page_general->priv->source_extension_name);
1580 page_general->priv->source_extension_name = g_strdup (source_extension_name);
1582 g_object_notify (G_OBJECT (page_general), "source-extension-name");
1584 if (page_general->priv->source_combo_box) {
1585 e_source_combo_box_set_extension_name (
1586 E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box),
1587 source_extension_name);
1591 ESource *
1592 e_comp_editor_page_general_ref_selected_source (ECompEditorPageGeneral *page_general)
1594 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1595 g_return_val_if_fail (page_general->priv->source_combo_box != NULL, NULL);
1597 return e_source_combo_box_ref_active (E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box));
1600 void
1601 e_comp_editor_page_general_set_selected_source (ECompEditorPageGeneral *page_general,
1602 ESource *source)
1604 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1605 if (source)
1606 g_return_if_fail (E_IS_SOURCE (source));
1608 if (!page_general->priv->source_combo_box) {
1609 g_clear_object (&page_general->priv->select_source);
1610 page_general->priv->select_source = g_object_ref (source);
1612 g_object_notify (G_OBJECT (page_general), "selected-source");
1614 return;
1617 if (source)
1618 e_source_combo_box_set_active (E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box), source);
1620 g_object_notify (G_OBJECT (page_general), "selected-source");
1623 gboolean
1624 e_comp_editor_page_general_get_show_attendees (ECompEditorPageGeneral *page_general)
1626 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
1628 return page_general->priv->show_attendees;
1631 void
1632 e_comp_editor_page_general_set_show_attendees (ECompEditorPageGeneral *page_general,
1633 gboolean show_attendees)
1635 ECompEditor *comp_editor;
1637 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1639 if ((show_attendees ? 1 : 0) == (page_general->priv->show_attendees ? 1 : 0))
1640 return;
1642 page_general->priv->show_attendees = show_attendees;
1644 g_object_notify (G_OBJECT (page_general), "show-attendees");
1646 e_comp_editor_page_general_update_view (page_general);
1648 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
1649 if (comp_editor)
1650 e_comp_editor_set_changed (comp_editor, TRUE);
1651 g_clear_object (&comp_editor);
1654 gint
1655 e_comp_editor_page_general_get_data_column_width (ECompEditorPageGeneral *page_general)
1657 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), 0);
1659 return page_general->priv->data_column_width;
1662 void
1663 e_comp_editor_page_general_set_data_column_width (ECompEditorPageGeneral *page_general,
1664 gint data_column_width)
1666 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1668 if (data_column_width == page_general->priv->data_column_width)
1669 return;
1671 page_general->priv->data_column_width = data_column_width;
1673 g_object_notify (G_OBJECT (page_general), "data-column-width");
1675 e_comp_editor_page_general_update_view (page_general);
1678 void
1679 e_comp_editor_page_general_update_view (ECompEditorPageGeneral *page_general)
1681 GtkContainer *grid;
1683 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1685 if (!page_general->priv->source_label)
1686 return;
1688 grid = GTK_CONTAINER (page_general);
1690 gtk_container_child_set (grid, page_general->priv->organizer_hbox,
1691 "left-attach", 1, "width", page_general->priv->data_column_width, NULL);
1692 gtk_container_child_set (grid, page_general->priv->attendees_hbox,
1693 "width", page_general->priv->data_column_width, NULL);
1695 if (page_general->priv->show_attendees) {
1696 if (gtk_widget_get_parent (page_general->priv->source_label) == GTK_WIDGET (grid)) {
1697 g_object_ref (page_general->priv->source_label);
1698 g_object_ref (page_general->priv->source_combo_box);
1700 gtk_container_remove (grid, page_general->priv->source_label);
1701 gtk_container_remove (grid, page_general->priv->source_combo_box);
1703 gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox),
1704 page_general->priv->source_label, FALSE, FALSE, 0);
1705 gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox),
1706 page_general->priv->source_combo_box, TRUE, TRUE, 0);
1708 g_object_unref (page_general->priv->source_label);
1709 g_object_unref (page_general->priv->source_combo_box);
1712 gtk_container_child_set (grid, page_general->priv->organizer_label,
1713 "left-attach", 0, NULL);
1715 gtk_widget_show (page_general->priv->organizer_label);
1716 gtk_widget_show (page_general->priv->organizer_hbox);
1717 gtk_widget_show (page_general->priv->attendees_button);
1718 gtk_widget_show (page_general->priv->attendees_hbox);
1719 gtk_widget_show (page_general->priv->attendees_list_view);
1720 gtk_widget_show (page_general->priv->attendees_button_box);
1721 } else {
1722 if (gtk_widget_get_parent (page_general->priv->source_label) != GTK_WIDGET (grid)) {
1723 GtkContainer *container;
1724 GtkGrid *ggrid;
1726 container = GTK_CONTAINER (page_general->priv->organizer_hbox);
1727 ggrid = GTK_GRID (grid);
1729 g_object_ref (page_general->priv->source_label);
1730 g_object_ref (page_general->priv->source_combo_box);
1732 gtk_container_remove (container, page_general->priv->source_label);
1733 gtk_container_remove (container, page_general->priv->source_combo_box);
1735 gtk_grid_attach (ggrid, page_general->priv->source_label, 0, 0, 1, 1);
1736 gtk_grid_attach (ggrid, page_general->priv->source_combo_box, 1, 0, 1, 1);
1738 g_object_unref (page_general->priv->source_label);
1739 g_object_unref (page_general->priv->source_combo_box);
1742 gtk_container_child_set (grid, page_general->priv->source_label,
1743 "left-attach", 0, NULL);
1744 gtk_container_child_set (grid, page_general->priv->source_combo_box,
1745 "left-attach", 1, "width", page_general->priv->data_column_width, NULL);
1747 gtk_widget_hide (page_general->priv->organizer_label);
1748 gtk_widget_hide (page_general->priv->organizer_hbox);
1749 gtk_widget_hide (page_general->priv->attendees_button);
1750 gtk_widget_hide (page_general->priv->attendees_hbox);
1751 gtk_widget_hide (page_general->priv->attendees_list_view);
1752 gtk_widget_hide (page_general->priv->attendees_button_box);
1756 EMeetingStore *
1757 e_comp_editor_page_general_get_meeting_store (ECompEditorPageGeneral *page_general)
1759 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1761 return page_general->priv->meeting_store;
1764 /* Element is a string, an email address; free with g_slist_free_full (slist, g_free); */
1765 GSList *
1766 e_comp_editor_page_general_get_added_attendees (ECompEditorPageGeneral *page_general)
1768 const GPtrArray *attendees;
1769 GHashTable *orig_attendees = NULL;
1770 GSList *added_attendees = NULL, *link;
1771 gint ii;
1773 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1775 if (!page_general->priv->show_attendees)
1776 return NULL;
1778 if (page_general->priv->orig_attendees) {
1779 for (link = page_general->priv->orig_attendees; link; link = g_slist_next (link)) {
1780 const gchar *address = link->data;
1782 if (address) {
1783 if (!orig_attendees)
1784 orig_attendees = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
1785 g_hash_table_insert (orig_attendees, (gpointer) address, GINT_TO_POINTER (1));
1790 attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
1792 for (ii = 0; ii < attendees->len; ii++) {
1793 EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
1794 const gchar *address;
1796 address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
1798 if (address && (!orig_attendees || !g_hash_table_contains (orig_attendees, address)))
1799 added_attendees = g_slist_prepend (added_attendees, g_strdup (address));
1802 if (orig_attendees)
1803 g_hash_table_destroy (orig_attendees);
1805 return g_slist_reverse (added_attendees);
1808 /* Element is a string, an email address; free with g_slist_free_full (slist, g_free); */
1809 GSList *
1810 e_comp_editor_page_general_get_removed_attendees (ECompEditorPageGeneral *page_general)
1812 const GPtrArray *attendees;
1813 GHashTable *new_attendees;
1814 GSList *removed_attendees = NULL, *link;
1815 gint ii;
1817 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1819 if (!page_general->priv->orig_attendees)
1820 return NULL;
1822 if (!page_general->priv->show_attendees) {
1823 GSList *copy, *link;
1825 copy = g_slist_copy (page_general->priv->orig_attendees);
1826 for (link = copy; link; link = g_slist_next (link)) {
1827 link->data = g_strdup (link->data);
1830 return copy;
1833 new_attendees = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
1834 attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
1836 for (ii = 0; ii < attendees->len; ii++) {
1837 EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
1838 const gchar *address;
1840 address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
1841 if (address)
1842 g_hash_table_insert (new_attendees, (gpointer) address, GINT_TO_POINTER (1));
1845 for (link = page_general->priv->orig_attendees; link; link = g_slist_next (link)) {
1846 const gchar *address = link->data;
1848 if (address && !g_hash_table_contains (new_attendees, address)) {
1849 removed_attendees = g_slist_prepend (removed_attendees, g_strdup (address));
1853 g_hash_table_destroy (new_attendees);
1855 return g_slist_reverse (removed_attendees);