1 /*
2  * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 #include "evolution-config.h"
19 
20 #include <glib.h>
21 #include <glib/gi18n-lib.h>
22 #include <gtk/gtk.h>
23 
24 #include <libedataserver/libedataserver.h>
25 #include <camel/camel.h>
26 #include <e-util/e-util.h>
27 
28 #include "comp-util.h"
29 #include "e-comp-editor.h"
30 #include "e-comp-editor-page.h"
31 #include "e-comp-editor-property-parts.h"
32 #include "e-meeting-list-view.h"
33 #include "itip-utils.h"
34 
35 #include "e-comp-editor-page-general.h"
36 
37 #define BACKEND_EMAIL_ID "backend-email-id"
38 
39 struct _ECompEditorPageGeneralPrivate {
40 	GtkWidget *source_label;
41 	GtkWidget *source_combo_box;
42 	GtkWidget *organizer_label;
43 	GtkWidget *organizer_combo_box;
44 	GtkWidget *organizer_hbox;
45 	GtkWidget *attendees_button;
46 	GtkWidget *attendees_hbox;
47 	GtkWidget *attendees_list_view;
48 	GtkWidget *attendees_button_box;
49 	GtkWidget *attendees_button_add;
50 	GtkWidget *attendees_button_edit;
51 	GtkWidget *attendees_button_remove;
52 	ECompEditorPropertyPart *comp_color;
53 	gulong comp_color_changed_handler_id;
54 	GtkWidget *source_and_color_hbox; /* has together source_combo_box and comp_color::edit_widget */
55 
56 	gint data_column_width;
57 	gchar *source_label_text;
58 	gchar *source_extension_name;
59 	ESource *select_source;
60 	gboolean show_attendees;
61 
62 	EMeetingStore *meeting_store;
63 	GSList *orig_attendees; /* gchar *mail_addresses */
64 	gchar *user_delegator;
65 };
66 
67 enum {
68 	PROP_0,
69 	PROP_DATA_COLUMN_WIDTH,
70 	PROP_SOURCE_LABEL,
71 	PROP_SOURCE_EXTENSION_NAME,
72 	PROP_SELECTED_SOURCE,
73 	PROP_SHOW_ATTENDEES
74 };
75 
76 G_DEFINE_TYPE (ECompEditorPageGeneral, e_comp_editor_page_general, E_TYPE_COMP_EDITOR_PAGE)
77 
78 static void ecep_general_sensitize_widgets (ECompEditorPage *page,
79 					    gboolean force_insensitive);
80 
81 static void
ecep_general_set_column_visible(ECompEditorPageGeneral * page_general,EMeetingStoreColumns column,gboolean visible)82 ecep_general_set_column_visible (ECompEditorPageGeneral *page_general,
83 				 EMeetingStoreColumns column,
84 				 gboolean visible)
85 {
86 	EMeetingListView *meeting_list_view;
87 
88 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
89 
90 	meeting_list_view = E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view);
91 	e_meeting_list_view_column_set_visible (meeting_list_view, column, visible);
92 }
93 
94 static void
action_view_role_cb(GtkToggleAction * action,ECompEditorPageGeneral * page_general)95 action_view_role_cb (GtkToggleAction *action,
96 		     ECompEditorPageGeneral *page_general)
97 {
98 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
99 
100 	ecep_general_set_column_visible (page_general, E_MEETING_STORE_ROLE_COL,
101 		gtk_toggle_action_get_active (action));
102 }
103 
104 static void
action_view_rsvp_cb(GtkToggleAction * action,ECompEditorPageGeneral * page_general)105 action_view_rsvp_cb (GtkToggleAction *action,
106 		     ECompEditorPageGeneral *page_general)
107 {
108 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
109 
110 	ecep_general_set_column_visible (page_general, E_MEETING_STORE_RSVP_COL,
111 		gtk_toggle_action_get_active (action));
112 }
113 
114 static void
action_view_status_cb(GtkToggleAction * action,ECompEditorPageGeneral * page_general)115 action_view_status_cb (GtkToggleAction *action,
116 		       ECompEditorPageGeneral *page_general)
117 {
118 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
119 
120 	ecep_general_set_column_visible (page_general, E_MEETING_STORE_STATUS_COL,
121 		gtk_toggle_action_get_active (action));
122 }
123 
124 static void
action_view_type_cb(GtkToggleAction * action,ECompEditorPageGeneral * page_general)125 action_view_type_cb (GtkToggleAction *action,
126 		     ECompEditorPageGeneral *page_general)
127 {
128 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
129 
130 	ecep_general_set_column_visible (page_general, E_MEETING_STORE_TYPE_COL,
131 		gtk_toggle_action_get_active (action));
132 }
133 
134 static void
ecep_general_fill_organizer_combo_box(ECompEditorPageGeneral * page_general)135 ecep_general_fill_organizer_combo_box (ECompEditorPageGeneral *page_general)
136 {
137 	GtkComboBoxText *combo_box_text;
138 	ECompEditor *comp_editor;
139 	EShell *shell;
140 	ESourceRegistry *registry;
141 	gchar **address_strings;
142 	gint ii;
143 
144 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
145 	g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box));
146 
147 	combo_box_text = GTK_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box);
148 	comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
149 	shell = e_comp_editor_get_shell (comp_editor);
150 	registry = e_shell_get_registry (shell);
151 	address_strings = itip_get_user_identities (registry);
152 
153 	for (ii = 0; address_strings && address_strings[ii]; ii++) {
154 		gtk_combo_box_text_append_text (combo_box_text, address_strings[ii]);
155 	}
156 
157 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box_text), 0);
158 
159 	g_strfreev (address_strings);
160 	g_clear_object (&comp_editor);
161 }
162 
163 static void
ecep_general_source_combo_box_changed_cb(ESourceComboBox * source_combo_box,ECompEditorPageGeneral * page_general)164 ecep_general_source_combo_box_changed_cb (ESourceComboBox *source_combo_box,
165 					  ECompEditorPageGeneral *page_general)
166 {
167 	ESource *source;
168 
169 	g_return_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box));
170 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
171 
172 	source = e_source_combo_box_ref_active (source_combo_box);
173 	e_comp_editor_page_general_set_selected_source (page_general, source);
174 	g_clear_object (&source);
175 }
176 
177 static void
ecep_general_attendees_clicked_cb(GtkWidget * widget,ECompEditorPageGeneral * page_general)178 ecep_general_attendees_clicked_cb (GtkWidget *widget,
179 				   ECompEditorPageGeneral *page_general)
180 {
181 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
182 
183 	e_meeting_list_view_invite_others_dialog (E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view));
184 }
185 
186 static void
ecep_general_attendees_add_clicked_cb(GtkButton * button,ECompEditorPageGeneral * page_general)187 ecep_general_attendees_add_clicked_cb (GtkButton *button,
188 				       ECompEditorPageGeneral *page_general)
189 {
190 	ECompEditor *comp_editor;
191 	EMeetingAttendee *attendee;
192 	guint32 flags;
193 
194 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
195 
196 	comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
197 	flags = e_comp_editor_get_flags (comp_editor);
198 
199 	attendee = e_meeting_store_add_attendee_with_defaults (page_general->priv->meeting_store);
200 
201 	if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0) {
202 		gchar *mailto;
203 
204 		mailto = g_strdup_printf ("mailto:%s",
205 			page_general->priv->user_delegator ? page_general->priv->user_delegator : "");
206 		e_meeting_attendee_set_delfrom (attendee, mailto);
207 		g_free (mailto);
208 	}
209 
210 	e_meeting_list_view_edit (E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), attendee);
211 
212 	g_clear_object (&comp_editor);
213 }
214 
215 static void
ecep_general_attendees_edit_clicked_cb(GtkButton * button,ECompEditorPageGeneral * page_general)216 ecep_general_attendees_edit_clicked_cb (GtkButton *button,
217 					ECompEditorPageGeneral *page_general)
218 {
219 	GtkTreeView *tree_view;
220 	GtkTreePath *path = NULL;
221 	GtkTreeViewColumn *focus_col;
222 
223 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
224 
225 	tree_view = GTK_TREE_VIEW (page_general->priv->attendees_list_view);
226 
227 	gtk_tree_view_get_cursor (tree_view, &path, NULL);
228 	g_return_if_fail (path != NULL);
229 
230 	gtk_tree_view_get_cursor (tree_view, &path, &focus_col);
231 	gtk_tree_view_set_cursor (tree_view, path, focus_col, TRUE);
232 	gtk_tree_path_free (path);
233 }
234 
235 static void
ecep_general_remove_attendee(ECompEditorPageGeneral * page_general,EMeetingAttendee * attendee)236 ecep_general_remove_attendee (ECompEditorPageGeneral *page_general,
237 			      EMeetingAttendee *attendee)
238 {
239 	ECompEditor *comp_editor;
240 	gint pos = 0;
241 
242 	/* If this was a delegatee, no longer delegate */
243 	if (e_meeting_attendee_is_set_delfrom (attendee)) {
244 		EMeetingAttendee *ib;
245 
246 		ib = e_meeting_store_find_attendee (page_general->priv->meeting_store, e_meeting_attendee_get_delfrom (attendee), &pos);
247 		if (ib != NULL) {
248 			ECompEditor *comp_editor;
249 			ECompEditorFlags flags;
250 
251 			e_meeting_attendee_set_delto (ib, NULL);
252 
253 			comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
254 			flags = e_comp_editor_get_flags (comp_editor);
255 
256 			if (!(flags & E_COMP_EDITOR_FLAG_DELEGATE))
257 				e_meeting_attendee_set_edit_level (ib, E_MEETING_ATTENDEE_EDIT_FULL);
258 
259 			g_clear_object (&comp_editor);
260 		}
261 	}
262 
263 	/* Handle deleting all attendees in the delegation chain */
264 	while (attendee) {
265 		EMeetingAttendee *ib = NULL;
266 
267 		if (e_meeting_attendee_get_delto (attendee)) {
268 			ib = e_meeting_store_find_attendee (page_general->priv->meeting_store,
269 				e_meeting_attendee_get_delto (attendee), NULL);
270 		}
271 
272 		e_meeting_list_view_remove_attendee_from_name_selector (
273 			E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), attendee);
274 		e_meeting_store_remove_attendee (page_general->priv->meeting_store, attendee);
275 
276 		attendee = ib;
277 	}
278 
279 	ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
280 
281 	comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
282 	e_comp_editor_set_changed (comp_editor, TRUE);
283 	g_clear_object (&comp_editor);
284 }
285 
286 static void
ecep_general_attendees_remove_clicked_cb(GtkButton * button,ECompEditorPageGeneral * page_general)287 ecep_general_attendees_remove_clicked_cb (GtkButton *button,
288 					  ECompEditorPageGeneral *page_general)
289 {
290 	EMeetingAttendee *attendee;
291 	GtkTreeSelection *selection;
292 	GList *paths = NULL, *tmp;
293 	GtkTreeIter iter;
294 	GtkTreePath *path = NULL;
295 	GtkTreeModel *model = NULL;
296 	gboolean valid_iter;
297 	gchar *address;
298 	gint failures = 0;
299 	GString *errors = NULL;
300 
301 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
302 
303 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
304 	paths = gtk_tree_selection_get_selected_rows (selection, &model);
305 	g_return_if_fail (paths != NULL);
306 
307 	paths = g_list_reverse (paths);
308 
309 	for (tmp = paths; tmp; tmp = tmp->next) {
310 		path = tmp->data;
311 
312 		gtk_tree_model_get_iter (model, &iter, path);
313 
314 		gtk_tree_model_get (model, &iter, E_MEETING_STORE_ADDRESS_COL, &address, -1);
315 		attendee = e_meeting_store_find_attendee (E_MEETING_STORE (model), address, NULL);
316 
317 		if (!attendee) {
318 			if (!errors)
319 				errors = g_string_new ("");
320 			else
321 				g_string_append_c (errors, '\n');
322 			g_string_append_printf (errors, _("Cannot find attendee “%s” in the list of attendees"), address);
323 			failures++;
324 		} else if (e_meeting_attendee_get_edit_level (attendee) != E_MEETING_ATTENDEE_EDIT_FULL) {
325 			if (!errors)
326 				errors = g_string_new ("");
327 			else
328 				g_string_append_c (errors, '\n');
329 			g_string_append_printf (errors, _("Not enough rights to delete attendee “%s”"), e_meeting_attendee_get_address (attendee));
330 			failures++;
331 		} else {
332 			ecep_general_remove_attendee (page_general, attendee);
333 		}
334 
335 		g_free (address);
336 	}
337 
338 	/* Select closest item after removal */
339 	valid_iter = gtk_tree_model_get_iter (model, &iter, path);
340 	if (!valid_iter) {
341 		gtk_tree_path_prev (path);
342 		valid_iter = gtk_tree_model_get_iter (model, &iter, path);
343 	}
344 
345 	if (valid_iter) {
346 		gtk_tree_selection_unselect_all (selection);
347 		gtk_tree_selection_select_iter (selection, &iter);
348 	}
349 
350 	g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
351 
352 	if (errors) {
353 		ECompEditor *comp_editor;
354 		EAlert *alert;
355 
356 		comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
357 
358 		alert = e_comp_editor_add_error (comp_editor, g_dngettext (GETTEXT_PACKAGE,
359 			"Failed to delete selected attendee",
360 			"Failed to delete selected attendees",
361 			failures), errors->str);
362 
363 		g_string_free (errors, TRUE);
364 		g_clear_object (&alert);
365 		g_clear_object (&comp_editor);
366 	}
367 }
368 
369 static void
ecep_general_attendees_selection_changed_cb(GtkTreeSelection * selection,ECompEditorPageGeneral * page_general)370 ecep_general_attendees_selection_changed_cb (GtkTreeSelection *selection,
371 					     ECompEditorPageGeneral *page_general)
372 {
373 	g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
374 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
375 
376 	ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
377 }
378 
379 static void
ecep_general_attendee_added_cb(EMeetingListView * meeting_list_view,EMeetingAttendee * attendee,ECompEditorPageGeneral * page_general)380 ecep_general_attendee_added_cb (EMeetingListView *meeting_list_view,
381 				EMeetingAttendee *attendee,
382 				ECompEditorPageGeneral *page_general)
383 {
384 	ECompEditor *comp_editor;
385 	ECompEditorFlags flags;
386 	ECalClient *client;
387 	gchar *mailto;
388 
389 	comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
390 	flags = e_comp_editor_get_flags (comp_editor);
391 
392 	e_comp_editor_set_changed (comp_editor, TRUE);
393 
394 	if (!(flags & E_COMP_EDITOR_FLAG_DELEGATE)) {
395 		g_clear_object (&comp_editor);
396 		return;
397 	}
398 
399 	client = e_comp_editor_get_target_client (comp_editor);
400 
401 	/* do not remove here, it did EMeetingListView already */
402 	mailto = g_strdup_printf ("mailto:%s", page_general->priv->user_delegator ? page_general->priv->user_delegator : "");
403 	e_meeting_attendee_set_delfrom (attendee, mailto);
404 	g_free (mailto);
405 
406 	if (client && !e_client_check_capability (E_CLIENT (client), E_CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY)) {
407 		EMeetingAttendee *delegator;
408 
409 		delegator = e_meeting_store_find_attendee (page_general->priv->meeting_store,
410 			page_general->priv->user_delegator, NULL);
411 		g_return_if_fail (delegator != NULL);
412 
413 		e_meeting_attendee_set_delto (delegator, e_meeting_attendee_get_address (attendee));
414 	}
415 
416 	ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
417 
418 	g_clear_object (&comp_editor);
419 }
420 
421 static void
ecep_general_attendee_row_changed_cb(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,ECompEditorPageGeneral * page_general)422 ecep_general_attendee_row_changed_cb (GtkTreeModel *model,
423 				      GtkTreePath *path,
424 				      GtkTreeIter *iter,
425 				      ECompEditorPageGeneral *page_general)
426 {
427 	ECompEditor *comp_editor;
428 
429 	comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
430 
431 	if (comp_editor)
432 		e_comp_editor_set_changed (comp_editor, TRUE);
433 
434 	g_clear_object (&comp_editor);
435 }
436 
437 static void
ecep_general_attendee_show_address_notify_cb(GObject * store,GParamSpec * param,ECompEditorPageGeneral * page_general)438 ecep_general_attendee_show_address_notify_cb (GObject *store,
439 					      GParamSpec *param,
440 					      ECompEditorPageGeneral *page_general)
441 {
442 	if (gtk_widget_get_realized (GTK_WIDGET (page_general)) &&
443 	    gtk_widget_get_realized (page_general->priv->attendees_list_view))
444 		gtk_widget_queue_draw (page_general->priv->attendees_list_view);
445 }
446 
447 static gboolean
ecep_general_get_organizer(ECompEditorPageGeneral * page_general,gchar ** out_name,gchar ** out_mailto,const gchar ** out_error_message)448 ecep_general_get_organizer (ECompEditorPageGeneral *page_general,
449 			    gchar **out_name,
450 			    gchar **out_mailto,
451 			    const gchar **out_error_message)
452 {
453 	gchar *organizer_text;
454 	gboolean valid = FALSE;
455 
456 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
457 
458 	organizer_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box));
459 	if (organizer_text) {
460 		CamelInternetAddress *address;
461 		const gchar *str_name, *str_address;
462 
463 		address = camel_internet_address_new ();
464 		if (camel_address_unformat (CAMEL_ADDRESS (address), organizer_text) == 1 &&
465 		    camel_internet_address_get (address, 0, &str_name, &str_address)) {
466 			valid = TRUE;
467 
468 			if (out_name)
469 				*out_name = g_strdup (str_name);
470 			if (out_mailto)
471 				*out_mailto = g_strconcat ("mailto:", itip_strip_mailto (str_address), NULL);
472 		} else if (out_error_message) {
473 			*out_error_message = _("Organizer address is not a valid user mail address");
474 		}
475 
476 		g_object_unref (address);
477 		g_free (organizer_text);
478 	}
479 
480 	return valid;
481 }
482 
483 static void
ecep_general_remove_organizer_backend_address(GtkComboBox * combo_box)484 ecep_general_remove_organizer_backend_address (GtkComboBox *combo_box)
485 {
486 	GtkTreeModel *model;
487 	GtkTreeIter iter;
488 
489 	model = gtk_combo_box_get_model (combo_box);
490 
491 	if (gtk_tree_model_get_iter_first (model, &iter)) {
492 		gint id_column;
493 
494 		id_column = gtk_combo_box_get_id_column (combo_box);
495 
496 		do {
497 			gchar *value = NULL;
498 
499 			gtk_tree_model_get (model, &iter, id_column, &value, -1);
500 
501 			if (g_strcmp0 (value, BACKEND_EMAIL_ID) == 0) {
502 				g_free (value);
503 				gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
504 				break;
505 			}
506 
507 			g_free (value);
508 		} while (gtk_tree_model_iter_next (model, &iter));
509 	}
510 }
511 
512 static gboolean
ecep_general_pick_organizer_for_email_address(ECompEditorPageGeneral * page_general,const gchar * email_address,gboolean can_add)513 ecep_general_pick_organizer_for_email_address (ECompEditorPageGeneral *page_general,
514 					       const gchar *email_address,
515 					       gboolean can_add)
516 {
517 	GtkTreeModel *model;
518 	GtkTreeIter iter;
519 	GtkComboBox *combo_box;
520 	gint ii = 0, entry_text_column;
521 
522 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
523 
524 	combo_box = GTK_COMBO_BOX (page_general->priv->organizer_combo_box);
525 	model = gtk_combo_box_get_model (combo_box);
526 
527 	if (can_add)
528 		ecep_general_remove_organizer_backend_address (combo_box);
529 
530 	email_address = itip_strip_mailto (email_address);
531 
532 	if (!email_address || !*email_address) {
533 		if (can_add && gtk_combo_box_get_active (combo_box) == -1 &&
534 		    gtk_tree_model_get_iter_first (model, &iter))
535 			gtk_combo_box_set_active (combo_box, 0);
536 
537 		return FALSE;
538 	}
539 
540 	entry_text_column = gtk_combo_box_get_entry_text_column (combo_box);
541 
542 	if (gtk_tree_model_get_iter_first (model, &iter)) {
543 		do {
544 			gchar *value = NULL;
545 
546 			gtk_tree_model_get (model, &iter, entry_text_column, &value, -1);
547 
548 			if (value && g_strrstr (value, email_address)) {
549 				g_free (value);
550 				gtk_combo_box_set_active (combo_box, ii);
551 				return TRUE;
552 			}
553 
554 			g_free (value);
555 
556 			ii++;
557 		} while (gtk_tree_model_iter_next (model, &iter));
558 	}
559 
560 	if (can_add) {
561 		/* The expected address is not in the list, thus add it */
562 		gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo_box), BACKEND_EMAIL_ID, email_address);
563 		gtk_combo_box_set_active (combo_box, ii);
564 
565 		return TRUE;
566 	}
567 
568 	return FALSE;
569 }
570 
571 static void
ecep_general_target_client_notify_cb(ECompEditor * comp_editor,GParamSpec * param,ECompEditorPageGeneral * page_general)572 ecep_general_target_client_notify_cb (ECompEditor *comp_editor,
573 				      GParamSpec *param,
574 				      ECompEditorPageGeneral *page_general)
575 {
576 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
577 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
578 
579 	if ((e_comp_editor_get_flags (comp_editor) & E_COMP_EDITOR_FLAG_IS_NEW) != 0 ||
580 	    (e_comp_editor_get_source_client (comp_editor) != NULL &&
581 	     e_comp_editor_get_target_client (comp_editor) != e_comp_editor_get_source_client (comp_editor))) {
582 		const gchar *cal_email_address;
583 
584 		cal_email_address = e_comp_editor_get_cal_email_address (comp_editor);
585 		ecep_general_pick_organizer_for_email_address (page_general, cal_email_address, TRUE);
586 	}
587 
588 	if (page_general->priv->comp_color) {
589 		ECalClient *target_client;
590 		gboolean supports_color = FALSE;
591 
592 		target_client = e_comp_editor_get_target_client (comp_editor);
593 		if (target_client)
594 			supports_color = e_client_check_capability (E_CLIENT (target_client), E_CAL_STATIC_CAPABILITY_COMPONENT_COLOR);
595 
596 		e_comp_editor_property_part_set_visible (page_general->priv->comp_color, supports_color);
597 	}
598 }
599 
600 static gboolean
ecep_general_list_view_event_cb(EMeetingListView * list_view,GdkEvent * event,ECompEditorPageGeneral * page_general)601 ecep_general_list_view_event_cb (EMeetingListView *list_view,
602 				 GdkEvent *event,
603 				 ECompEditorPageGeneral *page_general)
604 {
605 	g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (list_view), FALSE);
606 	g_return_val_if_fail (event != NULL, FALSE);
607 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
608 
609 	if (event->type == GDK_2BUTTON_PRESS && gtk_widget_get_sensitive (GTK_WIDGET (list_view)) &&
610 	    gtk_widget_get_sensitive (page_general->priv->attendees_button_add)) {
611 		EMeetingAttendee *attendee;
612 		ECompEditor *comp_editor;
613 		guint32 flags;
614 
615 		attendee = e_meeting_store_add_attendee_with_defaults (page_general->priv->meeting_store);
616 
617 		comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
618 		flags = e_comp_editor_get_flags (comp_editor);
619 
620 		if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0) {
621 			gchar *mailto;
622 
623 			mailto = g_strdup_printf ("mailto:%s", page_general->priv->user_delegator);
624 			e_meeting_attendee_set_delfrom (attendee, mailto);
625 			g_free (mailto);
626 		}
627 
628 		g_clear_object (&comp_editor);
629 		e_meeting_list_view_edit (list_view, attendee);
630 
631 		return TRUE;
632 	}
633 
634 	return FALSE;
635 }
636 
637 static gboolean
ecep_general_list_view_key_press_cb(EMeetingListView * list_view,GdkEventKey * event,ECompEditorPageGeneral * page_general)638 ecep_general_list_view_key_press_cb (EMeetingListView *list_view,
639 				     GdkEventKey *event,
640 				     ECompEditorPageGeneral *page_general)
641 {
642 	g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (list_view), FALSE);
643 	g_return_val_if_fail (event != NULL, FALSE);
644 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
645 
646 	if (event->keyval == GDK_KEY_Delete) {
647 		if (gtk_widget_get_sensitive (page_general->priv->attendees_button_remove))
648 			ecep_general_attendees_remove_clicked_cb (NULL, page_general);
649 		return TRUE;
650 	} else if (event->keyval == GDK_KEY_Insert) {
651 		if (gtk_widget_get_sensitive (page_general->priv->attendees_button_add))
652 			ecep_general_attendees_add_clicked_cb (NULL, page_general);
653 		return TRUE;
654 	}
655 
656 	return FALSE;
657 }
658 
659 static void
ecep_general_init_ui(ECompEditorPageGeneral * page_general,ECompEditor * comp_editor)660 ecep_general_init_ui (ECompEditorPageGeneral *page_general,
661 		      ECompEditor *comp_editor)
662 {
663 	const gchar *ui =
664 		"<ui>"
665 		"  <menubar action='main-menu'>"
666 		"    <menu action='view-menu'>"
667 		"      <placeholder name='columns'>"
668 		"        <menuitem action='view-role'/>"
669 		"        <menuitem action='view-rsvp'/>"
670 		"        <menuitem action='view-status'/>"
671 		"        <menuitem action='view-type'/>"
672 		"      </placeholder>"
673 		"    </menu>"
674 		"    <menu action='options-menu'>"
675 		"      <placeholder name='toggles'>"
676 		"        <menuitem action='option-attendees'/>"
677 		"      </placeholder>"
678 		"    </menu>"
679 		"  </menubar>"
680 		"</ui>";
681 
682 	const GtkToggleActionEntry attendees_toggle_entry[] = {
683 
684 		{ "option-attendees",
685 		  NULL,
686 		  N_("A_ttendees"),
687 		  NULL,
688 		  N_("Toggles whether the Attendees are displayed"),
689 		  NULL,
690 		  FALSE }
691 	};
692 
693 	const GtkToggleActionEntry columns_toggle_entries[] = {
694 
695 		{ "view-role",
696 		  NULL,
697 		  N_("R_ole Field"),
698 		  NULL,
699 		  N_("Toggles whether the Role field is displayed"),
700 		  G_CALLBACK (action_view_role_cb),
701 		  TRUE },
702 
703 		{ "view-rsvp",
704 		  NULL,
705 		  N_("_RSVP"),
706 		  NULL,
707 		  N_("Toggles whether the RSVP field is displayed"),
708 		  G_CALLBACK (action_view_rsvp_cb),
709 		  TRUE },
710 
711 		{ "view-status",
712 		  NULL,
713 		  N_("_Status Field"),
714 		  NULL,
715 		  N_("Toggles whether the Status field is displayed"),
716 		  G_CALLBACK (action_view_status_cb),
717 		  TRUE },
718 
719 		{ "view-type",
720 		  NULL,
721 		  N_("_Type Field"),
722 		  NULL,
723 		  N_("Toggles whether the Attendee Type is displayed"),
724 		  G_CALLBACK (action_view_type_cb),
725 		  TRUE }
726 	};
727 
728 	GtkUIManager *ui_manager;
729 	GtkActionGroup *action_group;
730 	GtkAction *action;
731 	GSettings *settings;
732 	GError *error = NULL;
733 
734 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
735 	g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
736 
737 	settings = e_comp_editor_get_settings (comp_editor);
738 	ui_manager = e_comp_editor_get_ui_manager (comp_editor);
739 
740 	action_group = gtk_action_group_new ("columns");
741 	gtk_action_group_set_translation_domain (
742 		action_group, GETTEXT_PACKAGE);
743 	gtk_action_group_add_toggle_actions (
744 		action_group, columns_toggle_entries,
745 		G_N_ELEMENTS (columns_toggle_entries), page_general);
746 	gtk_ui_manager_insert_action_group (
747 		ui_manager, action_group, 0);
748 
749 	e_binding_bind_property (
750 		page_general, "show-attendees",
751 		action_group, "sensitive",
752 		G_BINDING_SYNC_CREATE);
753 
754 	g_object_unref (action_group);
755 
756 	action_group = e_comp_editor_get_action_group (comp_editor, "individual");
757 	gtk_action_group_add_toggle_actions (
758 		action_group, attendees_toggle_entry,
759 		G_N_ELEMENTS (attendees_toggle_entry), page_general);
760 
761 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
762 
763 	if (error) {
764 		g_critical ("%s: %s", G_STRFUNC, error->message);
765 		g_error_free (error);
766 	}
767 
768 	action = e_comp_editor_get_action (comp_editor, "option-attendees");
769 	e_binding_bind_property (
770 		page_general, "show-attendees",
771 		action, "active",
772 		G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
773 
774 	action = e_comp_editor_get_action (comp_editor, "view-role");
775 	g_settings_bind (
776 		settings, "editor-show-role",
777 		action, "active",
778 		G_SETTINGS_BIND_DEFAULT);
779 
780 	action = e_comp_editor_get_action (comp_editor, "view-rsvp");
781 	g_settings_bind (
782 		settings, "editor-show-rsvp",
783 		action, "active",
784 		G_SETTINGS_BIND_DEFAULT);
785 
786 	action = e_comp_editor_get_action (comp_editor, "view-status");
787 	g_settings_bind (
788 		settings, "editor-show-status",
789 		action, "active",
790 		G_SETTINGS_BIND_DEFAULT);
791 
792 	action = e_comp_editor_get_action (comp_editor, "view-type");
793 	g_settings_bind (
794 		settings, "editor-show-type",
795 		action, "active",
796 		G_SETTINGS_BIND_DEFAULT);
797 }
798 
799 static void
ecep_general_sensitize_widgets(ECompEditorPage * page,gboolean force_insensitive)800 ecep_general_sensitize_widgets (ECompEditorPage *page,
801 				gboolean force_insensitive)
802 {
803 	ECompEditorPageGeneral *page_general;
804 	GtkTreeSelection *selection;
805 	GtkAction *action;
806 	gboolean sensitive, organizer_is_user, delegate, delegate_to_many = FALSE, read_only = TRUE, any_selected = FALSE;
807 	ECompEditor *comp_editor;
808 	ECalClient *client;
809 	guint32 flags;
810 
811 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page));
812 
813 	E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->sensitize_widgets (page, force_insensitive);
814 
815 	page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
816 
817 	comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
818 	flags = e_comp_editor_get_flags (comp_editor);
819 	client = e_comp_editor_get_target_client (comp_editor);
820 
821 	if (client) {
822 		EClient *cl = E_CLIENT (client);
823 
824 		read_only = e_client_is_readonly (cl);
825 		delegate_to_many = e_client_check_capability (cl, E_CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY);
826 	} else {
827 		force_insensitive = TRUE;
828 	}
829 
830 	delegate = (flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0;
831 	organizer_is_user = (flags & (E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER | E_COMP_EDITOR_FLAG_IS_NEW)) != 0 ||
832 		!e_comp_editor_page_general_get_show_attendees (page_general);
833 	sensitive = (!read_only && organizer_is_user) || delegate;
834 
835 	if (!delegate)
836 		delegate_to_many = TRUE;
837 
838 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
839 	any_selected = selection && gtk_tree_selection_count_selected_rows (selection) > 0;
840 
841 	gtk_widget_set_sensitive (page_general->priv->organizer_label, !force_insensitive);
842 	gtk_widget_set_sensitive (page_general->priv->organizer_combo_box, !read_only && !force_insensitive);
843 	gtk_widget_set_sensitive (page_general->priv->attendees_button, sensitive && delegate_to_many && !force_insensitive);
844 	gtk_widget_set_sensitive (page_general->priv->attendees_hbox, !force_insensitive);
845 	gtk_widget_set_sensitive (page_general->priv->attendees_button_add, sensitive && delegate_to_many && !force_insensitive);
846 	gtk_widget_set_sensitive (page_general->priv->attendees_button_edit, sensitive && delegate_to_many && !force_insensitive && any_selected);
847 	gtk_widget_set_sensitive (page_general->priv->attendees_button_remove, sensitive && !force_insensitive && any_selected);
848 	e_meeting_list_view_set_editable (E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), sensitive && !force_insensitive);
849 	gtk_widget_set_sensitive (page_general->priv->attendees_list_view, !read_only && !force_insensitive);
850 
851 	action = e_comp_editor_get_action (comp_editor, "option-attendees");
852 	gtk_action_set_sensitive (action, !force_insensitive && !read_only);
853 
854 	if (page_general->priv->comp_color &&
855 	    !e_comp_editor_property_part_get_sensitize_handled (page_general->priv->comp_color)) {
856 		e_comp_editor_property_part_sensitize_widgets (page_general->priv->comp_color, force_insensitive || read_only);
857 	}
858 
859 	g_clear_object (&comp_editor);
860 }
861 
862 static void
ecep_general_fill_widgets(ECompEditorPage * page,ICalComponent * component)863 ecep_general_fill_widgets (ECompEditorPage *page,
864 			   ICalComponent *component)
865 {
866 	ECompEditorPageGeneral *page_general;
867 	EMeetingListView *attendees_list_view;
868 	ICalProperty *prop;
869 
870 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page));
871 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
872 
873 	E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->fill_widgets (page, component);
874 
875 	page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
876 
877 	if (page_general->priv->comp_color)
878 		e_comp_editor_property_part_fill_widget (page_general->priv->comp_color, component);
879 
880 	g_slist_free_full (page_general->priv->orig_attendees, g_free);
881 	page_general->priv->orig_attendees = NULL;
882 
883 	for (prop = i_cal_component_get_first_property (component, I_CAL_ATTENDEE_PROPERTY);
884 	     prop;
885 	     g_object_unref (prop), prop = i_cal_component_get_next_property (component, I_CAL_ATTENDEE_PROPERTY)) {
886 		const gchar *address;
887 
888 		address = itip_strip_mailto (i_cal_property_get_attendee (prop));
889 		if (address)
890 			page_general->priv->orig_attendees = g_slist_prepend (page_general->priv->orig_attendees, g_strdup (address));
891 	}
892 
893 	page_general->priv->orig_attendees = g_slist_reverse (page_general->priv->orig_attendees);
894 
895 	prop = i_cal_component_get_first_property (component, I_CAL_ORGANIZER_PROPERTY);
896 	if (prop) {
897 		ICalParameter *param;
898 		const gchar *organizer;
899 
900 		organizer = i_cal_property_get_organizer (prop);
901 
902 		if (organizer && *organizer) {
903 			ECompEditor *comp_editor;
904 			ESourceRegistry *registry;
905 			guint32 flags;
906 			gchar *value = NULL;
907 
908 			comp_editor = e_comp_editor_page_ref_editor (page);
909 			flags = e_comp_editor_get_flags (comp_editor);
910 			registry = e_shell_get_registry (e_comp_editor_get_shell (comp_editor));
911 
912 			flags = flags & E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
913 
914 			if (itip_address_is_user (registry, itip_strip_mailto (organizer))) {
915 				flags = flags | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
916 			} else {
917 				param = i_cal_property_get_first_parameter (prop, I_CAL_SENTBY_PARAMETER);
918 				if (param) {
919 					const gchar *sentby = i_cal_parameter_get_sentby (param);
920 
921 					if (sentby && *sentby &&
922 					    itip_address_is_user (registry, itip_strip_mailto (organizer))) {
923 						flags = flags | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
924 					}
925 
926 					g_object_unref (param);
927 				}
928 			}
929 
930 			e_comp_editor_page_general_set_show_attendees (page_general, TRUE);
931 
932 			param = i_cal_property_get_first_parameter (prop, I_CAL_CN_PARAMETER);
933 			if (param) {
934 				const gchar *cn;
935 
936 				cn = i_cal_parameter_get_cn (param);
937 				if (cn && *cn) {
938 					value = camel_internet_address_format_address (cn, itip_strip_mailto (organizer));
939 				}
940 
941 				g_object_unref (param);
942 			}
943 
944 			if (!value)
945 				value = g_strdup (itip_strip_mailto (organizer));
946 
947 			if (!(flags & E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER) ||
948 			    !ecep_general_pick_organizer_for_email_address (page_general, organizer, FALSE)) {
949 				GtkComboBoxText *combo_box_text;
950 
951 				combo_box_text = GTK_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box);
952 				gtk_combo_box_text_remove_all (combo_box_text);
953 				gtk_combo_box_text_append_text (combo_box_text, value);
954 				gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box_text), 0);
955 			}
956 
957 			e_comp_editor_set_flags (comp_editor, flags);
958 
959 			g_clear_object (&comp_editor);
960 			g_free (value);
961 		}
962 
963 		g_object_unref (prop);
964 	}
965 
966 	attendees_list_view = E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view);
967 
968 	e_meeting_store_remove_all_attendees (page_general->priv->meeting_store);
969 	e_meeting_list_view_remove_all_attendees_from_name_selector (attendees_list_view);
970 
971 	for (prop = i_cal_component_get_first_property (component, I_CAL_ATTENDEE_PROPERTY);
972 	     prop;
973 	     g_object_unref (prop), prop = i_cal_component_get_next_property (component, I_CAL_ATTENDEE_PROPERTY)) {
974 		const gchar *address;
975 
976 		address = itip_strip_mailto (i_cal_property_get_attendee (prop));
977 		if (address) {
978 			EMeetingAttendee *attendee;
979 			ECalComponentAttendee *comp_attendee;
980 
981 			comp_attendee = e_cal_component_attendee_new_from_property (prop);
982 			if (!comp_attendee) {
983 				g_warn_if_reached ();
984 				continue;
985 			}
986 
987 			attendee = E_MEETING_ATTENDEE (e_meeting_attendee_new_from_e_cal_component_attendee (comp_attendee));
988 
989 			e_cal_component_attendee_free (comp_attendee);
990 
991 			e_meeting_store_add_attendee (page_general->priv->meeting_store, attendee);
992 			e_meeting_list_view_add_attendee_to_name_selector (attendees_list_view, attendee);
993 
994 			g_object_unref (attendee);
995 		}
996 	}
997 }
998 
999 static gboolean
ecep_general_fill_component(ECompEditorPage * page,ICalComponent * component)1000 ecep_general_fill_component (ECompEditorPage *page,
1001 			     ICalComponent *component)
1002 {
1003 	ECompEditorPageGeneral *page_general;
1004 	ICalProperty *prop;
1005 
1006 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page), FALSE);
1007 	g_return_val_if_fail (I_CAL_IS_COMPONENT (component), FALSE);
1008 
1009 	page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
1010 
1011 	if (page_general->priv->comp_color)
1012 		e_comp_editor_property_part_fill_component (page_general->priv->comp_color, component);
1013 
1014 	e_cal_util_component_remove_property_by_kind (component, I_CAL_ATTENDEE_PROPERTY, TRUE);
1015 
1016 	if (e_comp_editor_page_general_get_show_attendees (page_general)) {
1017 		const GPtrArray *attendees;
1018 		GHashTable *known_attendees;
1019 		ECompEditor *comp_editor;
1020 		gchar *organizer_name = NULL, *organizer_mailto = NULL;
1021 		guint32 flags;
1022 		gint ii, added_attendees = 0;
1023 		const gchar *error_message = NULL;
1024 
1025 		comp_editor = e_comp_editor_page_ref_editor (page);
1026 		flags = e_comp_editor_get_flags (comp_editor);
1027 
1028 		if ((flags & (E_COMP_EDITOR_FLAG_IS_NEW | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER)) != 0 &&
1029 		    !ecep_general_get_organizer (page_general, NULL, NULL, &error_message)) {
1030 			e_comp_editor_set_validation_error (comp_editor, page,
1031 				page_general->priv->organizer_combo_box,
1032 				error_message ? error_message : _("An organizer is required."));
1033 
1034 			g_clear_object (&comp_editor);
1035 
1036 			return FALSE;
1037 		}
1038 
1039 		if (e_meeting_store_count_actual_attendees (page_general->priv->meeting_store) < 1) {
1040 			e_comp_editor_set_validation_error (comp_editor, page,
1041 				page_general->priv->attendees_list_view,
1042 				_("At least one attendee is required."));
1043 
1044 			g_clear_object (&comp_editor);
1045 
1046 			return FALSE;
1047 		}
1048 
1049 		/* Organizer */
1050 		if ((flags & (E_COMP_EDITOR_FLAG_IS_NEW | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER)) != 0 &&
1051 		    ecep_general_get_organizer (page_general, &organizer_name, &organizer_mailto, NULL)) {
1052 			const gchar *cal_email_address;
1053 			ICalParameter *param;
1054 
1055 			prop = i_cal_component_get_first_property (component, I_CAL_ORGANIZER_PROPERTY);
1056 			if (!prop) {
1057 				prop = i_cal_property_new_organizer (organizer_mailto ? organizer_mailto : organizer_name);
1058 				i_cal_component_take_property (component, prop);
1059 				prop = i_cal_component_get_first_property (component, I_CAL_ORGANIZER_PROPERTY);
1060 			} else {
1061 				i_cal_property_set_organizer (prop, organizer_mailto ? organizer_mailto : organizer_name);
1062 			}
1063 
1064 			param = i_cal_property_get_first_parameter (prop, I_CAL_CN_PARAMETER);
1065 			if (organizer_name && *organizer_name) {
1066 				if (!param) {
1067 					param = i_cal_parameter_new_cn (organizer_name);
1068 					i_cal_property_add_parameter (prop, param);
1069 				} else {
1070 					i_cal_parameter_set_cn (param, organizer_name);
1071 				}
1072 			} else if (param) {
1073 				i_cal_property_remove_parameter_by_kind (prop, I_CAL_CN_PARAMETER);
1074 			}
1075 			g_clear_object (&param);
1076 
1077 			param = i_cal_property_get_first_parameter (prop, I_CAL_SENTBY_PARAMETER);
1078 			cal_email_address = e_comp_editor_get_cal_email_address (comp_editor);
1079 			if (cal_email_address && *cal_email_address) {
1080 				gchar *sentby;
1081 				gboolean differs;
1082 
1083 				sentby = g_strconcat ("mailto:", cal_email_address, NULL);
1084 				differs = !organizer_mailto || g_ascii_strcasecmp (sentby, organizer_mailto) != 0;
1085 
1086 				if (differs) {
1087 					if (!param) {
1088 						param = i_cal_parameter_new_sentby (sentby);
1089 						i_cal_property_add_parameter (prop, param);
1090 					} else {
1091 						i_cal_parameter_set_sentby (param, sentby);
1092 					}
1093 				} else if (param) {
1094 					i_cal_property_remove_parameter_by_kind (prop, I_CAL_SENTBY_PARAMETER);
1095 				}
1096 
1097 				g_free (sentby);
1098 			} else if (param) {
1099 				i_cal_property_remove_parameter_by_kind (prop, I_CAL_SENTBY_PARAMETER);
1100 			}
1101 			g_clear_object (&param);
1102 
1103 			g_object_unref (prop);
1104 		}
1105 
1106 		/* Attendees */
1107 		known_attendees = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
1108 		attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
1109 
1110 		for (ii = 0; ii < attendees->len; ii++) {
1111 			EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
1112 			const gchar *address;
1113 
1114 			address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
1115 			if (address) {
1116 				ICalParameter *param;
1117 
1118 				if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0 &&
1119 				    (e_meeting_attendee_is_set_delfrom (attendee) || e_meeting_attendee_is_set_delto (attendee)) &&
1120 				    g_hash_table_contains (known_attendees, address))
1121 					continue;
1122 
1123 				g_hash_table_insert (known_attendees, (gpointer) address, GINT_TO_POINTER (1));
1124 
1125 				prop = i_cal_property_new_attendee (e_meeting_attendee_get_address (attendee));
1126 
1127 				added_attendees++;
1128 
1129 				if (e_meeting_attendee_is_set_member (attendee)) {
1130 					param = i_cal_parameter_new_member (e_meeting_attendee_get_member (attendee));
1131 					i_cal_property_take_parameter (prop, param);
1132 				}
1133 
1134 				param = i_cal_parameter_new_cutype (e_meeting_attendee_get_cutype (attendee));
1135 				if (param)
1136 					i_cal_property_take_parameter (prop, param);
1137 
1138 				param = i_cal_parameter_new_role (e_meeting_attendee_get_role (attendee));
1139 				if (param)
1140 					i_cal_property_take_parameter (prop, param);
1141 
1142 				param = i_cal_parameter_new_partstat (e_meeting_attendee_get_partstat (attendee));
1143 				if (param)
1144 					i_cal_property_take_parameter (prop, param);
1145 
1146 				param = i_cal_parameter_new_rsvp (e_meeting_attendee_get_rsvp (attendee) ? I_CAL_RSVP_TRUE : I_CAL_RSVP_FALSE);
1147 				i_cal_property_take_parameter (prop, param);
1148 
1149 				if (e_meeting_attendee_is_set_delfrom (attendee)) {
1150 					param = i_cal_parameter_new_delegatedfrom (e_meeting_attendee_get_delfrom (attendee));
1151 					i_cal_property_take_parameter (prop, param);
1152 				}
1153 				if (e_meeting_attendee_is_set_delto (attendee)) {
1154 					param = i_cal_parameter_new_delegatedto (e_meeting_attendee_get_delto (attendee));
1155 					i_cal_property_take_parameter (prop, param);
1156 				}
1157 				if (e_meeting_attendee_is_set_sentby (attendee)) {
1158 					param = i_cal_parameter_new_sentby (e_meeting_attendee_get_sentby (attendee));
1159 					i_cal_property_take_parameter (prop, param);
1160 				}
1161 				if (e_meeting_attendee_is_set_cn (attendee)) {
1162 					param = i_cal_parameter_new_cn (e_meeting_attendee_get_cn (attendee));
1163 					i_cal_property_take_parameter (prop, param);
1164 				}
1165 				if (e_meeting_attendee_is_set_language (attendee)) {
1166 					param = i_cal_parameter_new_language (e_meeting_attendee_get_language (attendee));
1167 					i_cal_property_take_parameter (prop, param);
1168 				}
1169 
1170 				e_cal_component_parameter_bag_fill_property (e_meeting_attendee_get_parameter_bag (attendee), prop);
1171 
1172 				i_cal_component_take_property (component, prop);
1173 			}
1174 		}
1175 
1176 		g_hash_table_destroy (known_attendees);
1177 
1178 		g_free (organizer_name);
1179 		g_free (organizer_mailto);
1180 
1181 		if (!added_attendees) {
1182 			e_comp_editor_set_validation_error (comp_editor, page,
1183 				page_general->priv->attendees_list_view,
1184 				_("At least one attendee is required."));
1185 
1186 			g_clear_object (&comp_editor);
1187 
1188 			return FALSE;
1189 		}
1190 
1191 		g_clear_object (&comp_editor);
1192 	} else {
1193 		e_cal_util_component_remove_property_by_kind (component, I_CAL_ORGANIZER_PROPERTY, TRUE);
1194 	}
1195 
1196 	return E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->fill_component (page, component);
1197 }
1198 
1199 static void
ecep_general_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1200 ecep_general_set_property (GObject *object,
1201 			   guint property_id,
1202 			   const GValue *value,
1203 			   GParamSpec *pspec)
1204 {
1205 	switch (property_id) {
1206 		case PROP_DATA_COLUMN_WIDTH:
1207 			e_comp_editor_page_general_set_data_column_width (
1208 				E_COMP_EDITOR_PAGE_GENERAL (object),
1209 				g_value_get_int (value));
1210 			return;
1211 
1212 		case PROP_SOURCE_LABEL:
1213 			e_comp_editor_page_general_set_source_label (
1214 				E_COMP_EDITOR_PAGE_GENERAL (object),
1215 				g_value_get_string (value));
1216 			return;
1217 
1218 		case PROP_SOURCE_EXTENSION_NAME:
1219 			e_comp_editor_page_general_set_source_extension_name (
1220 				E_COMP_EDITOR_PAGE_GENERAL (object),
1221 				g_value_get_string (value));
1222 			return;
1223 
1224 		case PROP_SELECTED_SOURCE:
1225 			e_comp_editor_page_general_set_selected_source (
1226 				E_COMP_EDITOR_PAGE_GENERAL (object),
1227 				g_value_get_object (value));
1228 			return;
1229 
1230 		case PROP_SHOW_ATTENDEES:
1231 			e_comp_editor_page_general_set_show_attendees (
1232 				E_COMP_EDITOR_PAGE_GENERAL (object),
1233 				g_value_get_boolean (value));
1234 			return;
1235 	}
1236 
1237 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1238 }
1239 
1240 static void
ecep_general_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1241 ecep_general_get_property (GObject *object,
1242 			   guint property_id,
1243 			   GValue *value,
1244 			   GParamSpec *pspec)
1245 {
1246 	switch (property_id) {
1247 		case PROP_DATA_COLUMN_WIDTH:
1248 			g_value_set_int (
1249 				value,
1250 				e_comp_editor_page_general_get_data_column_width (
1251 				E_COMP_EDITOR_PAGE_GENERAL (object)));
1252 			return;
1253 
1254 		case PROP_SOURCE_LABEL:
1255 			g_value_set_string (
1256 				value,
1257 				e_comp_editor_page_general_get_source_label (
1258 				E_COMP_EDITOR_PAGE_GENERAL (object)));
1259 			return;
1260 
1261 		case PROP_SOURCE_EXTENSION_NAME:
1262 			g_value_set_string (
1263 				value,
1264 				e_comp_editor_page_general_get_source_extension_name (
1265 				E_COMP_EDITOR_PAGE_GENERAL (object)));
1266 			return;
1267 
1268 		case PROP_SELECTED_SOURCE:
1269 			g_value_take_object (
1270 				value,
1271 				e_comp_editor_page_general_ref_selected_source (
1272 				E_COMP_EDITOR_PAGE_GENERAL (object)));
1273 			return;
1274 
1275 		case PROP_SHOW_ATTENDEES:
1276 			g_value_set_boolean (
1277 				value,
1278 				e_comp_editor_page_general_get_show_attendees (
1279 				E_COMP_EDITOR_PAGE_GENERAL (object)));
1280 			return;
1281 	}
1282 
1283 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1284 }
1285 
1286 static void
ecep_general_constructed(GObject * object)1287 ecep_general_constructed (GObject *object)
1288 {
1289 	ECompEditor *comp_editor;
1290 	ECompEditorPageGeneral *page_general;
1291 	ECompEditorPropertyPart *part;
1292 	GtkWidget *widget, *scrolled_window;
1293 	GtkTreeSelection *selection;
1294 	GtkGrid *grid;
1295 	EShell *shell;
1296 
1297 	page_general = E_COMP_EDITOR_PAGE_GENERAL (object);
1298 
1299 	G_OBJECT_CLASS (e_comp_editor_page_general_parent_class)->constructed (object);
1300 
1301 	comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
1302 	g_return_if_fail (comp_editor != NULL);
1303 
1304 	page_general->priv->meeting_store = E_MEETING_STORE (e_meeting_store_new ());
1305 
1306 	grid = GTK_GRID (page_general);
1307 
1308 	widget = gtk_label_new_with_mnemonic (_("Or_ganizer:"));
1309 	g_object_set (G_OBJECT (widget),
1310 		"hexpand", FALSE,
1311 		"halign", GTK_ALIGN_END,
1312 		"vexpand", FALSE,
1313 		"valign", GTK_ALIGN_CENTER,
1314 		NULL);
1315 	gtk_grid_attach (grid, widget, 0, 0, 1, 1);
1316 	gtk_widget_hide (widget);
1317 
1318 	page_general->priv->organizer_label = widget;
1319 
1320 	widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
1321 	g_object_set (G_OBJECT (widget),
1322 		"hexpand", TRUE,
1323 		"halign", GTK_ALIGN_FILL,
1324 		"vexpand", FALSE,
1325 		"valign", GTK_ALIGN_FILL,
1326 		NULL);
1327 	gtk_grid_attach (grid, widget, 1, 0, page_general->priv->data_column_width, 1);
1328 	gtk_widget_hide (widget);
1329 
1330 	page_general->priv->organizer_hbox = widget;
1331 
1332 	widget = e_ellipsized_combo_box_text_new (FALSE);
1333 	g_object_set (G_OBJECT (widget),
1334 		"hexpand", TRUE,
1335 		"halign", GTK_ALIGN_FILL,
1336 		"vexpand", FALSE,
1337 		"valign", GTK_ALIGN_START,
1338 		NULL);
1339 	gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, TRUE, TRUE, 0);
1340 	gtk_widget_show (widget);
1341 
1342 	page_general->priv->organizer_combo_box = widget;
1343 
1344 	ecep_general_fill_organizer_combo_box (page_general);
1345 
1346 	g_signal_connect_swapped (page_general->priv->organizer_combo_box, "changed",
1347 		G_CALLBACK (e_comp_editor_ensure_changed), comp_editor);
1348 
1349 	widget = gtk_label_new_with_mnemonic (page_general->priv->source_label_text);
1350 	g_object_set (G_OBJECT (widget),
1351 		"hexpand", FALSE,
1352 		"halign", GTK_ALIGN_END,
1353 		"vexpand", FALSE,
1354 		"valign", GTK_ALIGN_CENTER,
1355 		NULL);
1356 	gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, FALSE, FALSE, 0);
1357 	gtk_widget_show (widget);
1358 
1359 	page_general->priv->source_label = widget;
1360 
1361 	widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
1362 	g_object_set (G_OBJECT (widget),
1363 		"hexpand", TRUE,
1364 		"halign", GTK_ALIGN_FILL,
1365 		"vexpand", FALSE,
1366 		"valign", GTK_ALIGN_CENTER,
1367 		NULL);
1368 	gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, TRUE, TRUE, 0);
1369 	gtk_widget_show (widget);
1370 
1371 	page_general->priv->source_and_color_hbox = widget;
1372 
1373 	shell = e_comp_editor_get_shell (comp_editor);
1374 	widget = e_source_combo_box_new (
1375 		e_shell_get_registry (shell),
1376 		page_general->priv->source_extension_name);
1377 	e_source_combo_box_set_show_colors (E_SOURCE_COMBO_BOX (widget), TRUE);
1378 	g_object_set (G_OBJECT (widget),
1379 		"hexpand", TRUE,
1380 		"halign", GTK_ALIGN_FILL,
1381 		"vexpand", FALSE,
1382 		"valign", GTK_ALIGN_START,
1383 		NULL);
1384 	gtk_box_pack_start (GTK_BOX (page_general->priv->source_and_color_hbox), widget, TRUE, TRUE, 0);
1385 	gtk_widget_show (widget);
1386 
1387 	page_general->priv->source_combo_box = widget;
1388 
1389 	gtk_label_set_mnemonic_widget (GTK_LABEL (page_general->priv->source_label),
1390 		page_general->priv->source_combo_box);
1391 	g_signal_connect (page_general->priv->source_combo_box, "changed",
1392 		G_CALLBACK (ecep_general_source_combo_box_changed_cb), page_general);
1393 
1394 	part = e_comp_editor_property_part_color_new ();
1395 	widget = e_comp_editor_property_part_get_edit_widget (part);
1396 
1397 	if (widget) {
1398 		const gchar *tooltip;
1399 
1400 		gtk_box_pack_start (GTK_BOX (page_general->priv->source_and_color_hbox), widget, FALSE, FALSE, 0);
1401 
1402 		if (g_strcmp0 (page_general->priv->source_extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0) {
1403 			tooltip = _("Override color of the event. If not set, then color of the calendar is used.");
1404 		} else if (g_strcmp0 (page_general->priv->source_extension_name, E_SOURCE_EXTENSION_MEMO_LIST) == 0) {
1405 			tooltip = _("Override color of the memo. If not set, then color of the memo list is used.");
1406 		} else { /* E_SOURCE_EXTENSION_TASK_LIST */
1407 			tooltip = _("Override color of the task. If not set, then color of the task list is used.");
1408 		}
1409 
1410 		gtk_widget_set_tooltip_text (widget, tooltip);
1411 	}
1412 
1413 	page_general->priv->comp_color_changed_handler_id = g_signal_connect_swapped (part, "changed",
1414 		G_CALLBACK (e_comp_editor_page_emit_changed), page_general);
1415 
1416 	page_general->priv->comp_color = part;
1417 
1418 	widget = gtk_button_new_with_mnemonic (C_("ECompEditor", "Atte_ndees…"));
1419 	g_object_set (G_OBJECT (widget),
1420 		"hexpand", FALSE,
1421 		"halign", GTK_ALIGN_END,
1422 		"vexpand", FALSE,
1423 		"valign", GTK_ALIGN_START,
1424 		NULL);
1425 	gtk_grid_attach (grid, widget, 0, 1, 1, 1);
1426 	gtk_widget_hide (widget);
1427 
1428 	page_general->priv->attendees_button = widget;
1429 
1430 	g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_clicked_cb), page_general);
1431 
1432 	widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1433 	g_object_set (G_OBJECT (widget),
1434 		"hexpand", TRUE,
1435 		"halign", GTK_ALIGN_FILL,
1436 		"vexpand", FALSE,
1437 		"valign", GTK_ALIGN_FILL,
1438 		NULL);
1439 	gtk_grid_attach (grid, widget, 1, 1, page_general->priv->data_column_width, 1);
1440 	gtk_widget_hide (widget);
1441 
1442 	page_general->priv->attendees_hbox = widget;
1443 
1444 	widget = GTK_WIDGET (e_meeting_list_view_new (page_general->priv->meeting_store));
1445 	g_object_set (G_OBJECT (widget),
1446 		"hexpand", FALSE,
1447 		"halign", GTK_ALIGN_FILL,
1448 		"vexpand", FALSE,
1449 		"valign", GTK_ALIGN_FILL,
1450 		NULL);
1451 	gtk_widget_show (widget);
1452 
1453 	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1454 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1455 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
1456 	gtk_widget_show (scrolled_window);
1457 
1458 	gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
1459 	gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_hbox), scrolled_window, TRUE, TRUE, 0);
1460 
1461 	page_general->priv->attendees_list_view = widget;
1462 
1463 	g_signal_connect_object (page_general->priv->attendees_list_view, "attendee-added",
1464 		G_CALLBACK (ecep_general_attendee_added_cb), page_general, 0);
1465 
1466 	g_signal_connect_object (page_general->priv->meeting_store, "row-changed",
1467 		G_CALLBACK (ecep_general_attendee_row_changed_cb), page_general, 0);
1468 
1469 	e_signal_connect_notify_object (page_general->priv->meeting_store, "notify::show-address",
1470 		G_CALLBACK (ecep_general_attendee_show_address_notify_cb), page_general, 0);
1471 
1472 	g_signal_connect_object (page_general->priv->attendees_list_view, "event",
1473 		G_CALLBACK (ecep_general_list_view_event_cb), page_general, 0);
1474 
1475 	g_signal_connect_object (page_general->priv->attendees_list_view, "key_press_event",
1476 		G_CALLBACK (ecep_general_list_view_key_press_cb), page_general, 0);
1477 
1478 	widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
1479 	g_object_set (G_OBJECT (widget),
1480 		"hexpand", FALSE,
1481 		"halign", GTK_ALIGN_START,
1482 		"vexpand", FALSE,
1483 		"valign", GTK_ALIGN_START,
1484 		NULL);
1485 	gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_hbox), widget, FALSE, FALSE, 0);
1486 	gtk_widget_show (widget);
1487 
1488 	page_general->priv->attendees_button_box = widget;
1489 
1490 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
1491 	g_signal_connect (selection, "changed", G_CALLBACK (ecep_general_attendees_selection_changed_cb), page_general);
1492 
1493 	widget = gtk_button_new_with_mnemonic (_("_Add"));
1494 	gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
1495 	gtk_widget_show (widget);
1496 
1497 	page_general->priv->attendees_button_add = widget;
1498 
1499 	g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_add_clicked_cb), page_general);
1500 
1501 	widget = gtk_button_new_with_mnemonic (_("_Edit"));
1502 	gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
1503 	gtk_widget_show (widget);
1504 
1505 	page_general->priv->attendees_button_edit = widget;
1506 
1507 	g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_edit_clicked_cb), page_general);
1508 
1509 	widget = gtk_button_new_with_mnemonic (_("_Remove"));
1510 	gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
1511 	gtk_widget_show (widget);
1512 
1513 	page_general->priv->attendees_button_remove = widget;
1514 
1515 	g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_remove_clicked_cb), page_general);
1516 
1517 	e_signal_connect_notify (comp_editor, "notify::target-client", G_CALLBACK (ecep_general_target_client_notify_cb), page_general);
1518 
1519 	ecep_general_init_ui (page_general, comp_editor);
1520 
1521 	g_clear_object (&comp_editor);
1522 }
1523 
1524 static void
ecep_general_finalize(GObject * object)1525 ecep_general_finalize (GObject *object)
1526 {
1527 	ECompEditorPageGeneral *page_general;
1528 
1529 	page_general = E_COMP_EDITOR_PAGE_GENERAL (object);
1530 
1531 	g_free (page_general->priv->source_label_text);
1532 	page_general->priv->source_label_text = NULL;
1533 
1534 	g_free (page_general->priv->source_extension_name);
1535 	page_general->priv->source_extension_name = NULL;
1536 
1537 	g_free (page_general->priv->user_delegator);
1538 	page_general->priv->user_delegator = NULL;
1539 
1540 	if (page_general->priv->comp_color && page_general->priv->comp_color_changed_handler_id) {
1541 		g_signal_handler_disconnect (page_general->priv->comp_color, page_general->priv->comp_color_changed_handler_id);
1542 		page_general->priv->comp_color_changed_handler_id = 0;
1543 	}
1544 
1545 	g_clear_object (&page_general->priv->comp_color);
1546 	g_clear_object (&page_general->priv->select_source);
1547 	g_clear_object (&page_general->priv->meeting_store);
1548 
1549 	g_slist_free_full (page_general->priv->orig_attendees, g_free);
1550 	page_general->priv->orig_attendees = NULL;
1551 
1552 	G_OBJECT_CLASS (e_comp_editor_page_general_parent_class)->finalize (object);
1553 }
1554 
1555 static void
e_comp_editor_page_general_init(ECompEditorPageGeneral * page_general)1556 e_comp_editor_page_general_init (ECompEditorPageGeneral *page_general)
1557 {
1558 	page_general->priv = G_TYPE_INSTANCE_GET_PRIVATE (page_general,
1559 		E_TYPE_COMP_EDITOR_PAGE_GENERAL,
1560 		ECompEditorPageGeneralPrivate);
1561 }
1562 
1563 static void
e_comp_editor_page_general_class_init(ECompEditorPageGeneralClass * klass)1564 e_comp_editor_page_general_class_init (ECompEditorPageGeneralClass *klass)
1565 {
1566 	ECompEditorPageClass *page_class;
1567 	GObjectClass *object_class;
1568 
1569 	g_type_class_add_private (klass, sizeof (ECompEditorPageGeneralPrivate));
1570 
1571 	page_class = E_COMP_EDITOR_PAGE_CLASS (klass);
1572 	page_class->sensitize_widgets = ecep_general_sensitize_widgets;
1573 	page_class->fill_widgets = ecep_general_fill_widgets;
1574 	page_class->fill_component = ecep_general_fill_component;
1575 
1576 	object_class = G_OBJECT_CLASS (klass);
1577 	object_class->set_property = ecep_general_set_property;
1578 	object_class->get_property = ecep_general_get_property;
1579 	object_class->constructed = ecep_general_constructed;
1580 	object_class->finalize = ecep_general_finalize;
1581 
1582 	g_object_class_install_property (
1583 		object_class,
1584 		PROP_DATA_COLUMN_WIDTH,
1585 		g_param_spec_int (
1586 			"data-column-width",
1587 			"Data Column Width",
1588 			"How many columns should the data column occupy",
1589 			1, G_MAXINT, 1,
1590 			G_PARAM_READWRITE |
1591 			G_PARAM_CONSTRUCT |
1592 			G_PARAM_STATIC_STRINGS));
1593 
1594 	g_object_class_install_property (
1595 		object_class,
1596 		PROP_SOURCE_LABEL,
1597 		g_param_spec_string (
1598 			"source-label",
1599 			"Source Label",
1600 			"Label to use for the source selector",
1601 			NULL,
1602 			G_PARAM_READWRITE |
1603 			G_PARAM_CONSTRUCT_ONLY |
1604 			G_PARAM_STATIC_STRINGS));
1605 
1606 	g_object_class_install_property (
1607 		object_class,
1608 		PROP_SOURCE_EXTENSION_NAME,
1609 		g_param_spec_string (
1610 			"source-extension-name",
1611 			"Source Extension Name",
1612 			"Extension name to use for the source selector",
1613 			NULL,
1614 			G_PARAM_READWRITE |
1615 			G_PARAM_CONSTRUCT_ONLY |
1616 			G_PARAM_STATIC_STRINGS));
1617 
1618 	g_object_class_install_property (
1619 		object_class,
1620 		PROP_SELECTED_SOURCE,
1621 		g_param_spec_object (
1622 			"selected-source",
1623 			"Selected Source",
1624 			"Which source is currently selected in the source selector",
1625 			E_TYPE_SOURCE,
1626 			G_PARAM_READWRITE |
1627 			G_PARAM_STATIC_STRINGS));
1628 
1629 	g_object_class_install_property (
1630 		object_class,
1631 		PROP_SHOW_ATTENDEES,
1632 		g_param_spec_boolean (
1633 			"show-attendees",
1634 			"Show Attendees",
1635 			"Whether to show also attendees",
1636 			TRUE,
1637 			G_PARAM_READWRITE |
1638 			G_PARAM_CONSTRUCT |
1639 			G_PARAM_STATIC_STRINGS));
1640 }
1641 
1642 ECompEditorPage *
e_comp_editor_page_general_new(ECompEditor * editor,const gchar * source_label,const gchar * source_extension_name,ESource * select_source,gboolean show_attendees,gint data_column_width)1643 e_comp_editor_page_general_new (ECompEditor *editor,
1644 				const gchar *source_label,
1645 				const gchar *source_extension_name,
1646 				ESource *select_source,
1647 				gboolean show_attendees,
1648 				gint data_column_width)
1649 {
1650 	g_return_val_if_fail (E_IS_COMP_EDITOR (editor), NULL);
1651 	g_return_val_if_fail (source_label != NULL, NULL);
1652 	g_return_val_if_fail (source_extension_name != NULL, NULL);
1653 
1654 	if (select_source)
1655 		g_return_val_if_fail (E_IS_SOURCE (select_source), NULL);
1656 
1657 	return g_object_new (E_TYPE_COMP_EDITOR_PAGE_GENERAL,
1658 		"editor", editor,
1659 		"source-label", source_label,
1660 		"source-extension-name", source_extension_name,
1661 		"selected-source", select_source,
1662 		"show-attendees", show_attendees,
1663 		"data-column-width", data_column_width,
1664 		NULL);
1665 }
1666 
1667 const gchar *
e_comp_editor_page_general_get_source_label(ECompEditorPageGeneral * page_general)1668 e_comp_editor_page_general_get_source_label (ECompEditorPageGeneral *page_general)
1669 {
1670 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1671 
1672 	if (page_general->priv->source_label)
1673 		return gtk_label_get_text (GTK_LABEL (page_general->priv->source_label));
1674 
1675 	return page_general->priv->source_label_text;
1676 }
1677 
1678 void
e_comp_editor_page_general_set_source_label(ECompEditorPageGeneral * page_general,const gchar * source_label)1679 e_comp_editor_page_general_set_source_label (ECompEditorPageGeneral *page_general,
1680 					     const gchar *source_label)
1681 {
1682 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1683 	g_return_if_fail (source_label != NULL);
1684 
1685 	if (page_general->priv->source_label) {
1686 		if (g_strcmp0 (source_label, gtk_label_get_text (GTK_LABEL (page_general->priv->source_label))) != 0) {
1687 			gtk_label_set_text (GTK_LABEL (page_general->priv->source_label), source_label);
1688 
1689 			g_object_notify (G_OBJECT (page_general), "source-label");
1690 		}
1691 	} else {
1692 		g_free (page_general->priv->source_label_text);
1693 		page_general->priv->source_label_text = g_strdup (source_label);
1694 
1695 		g_object_notify (G_OBJECT (page_general), "source-label");
1696 	}
1697 }
1698 
1699 const gchar *
e_comp_editor_page_general_get_source_extension_name(ECompEditorPageGeneral * page_general)1700 e_comp_editor_page_general_get_source_extension_name (ECompEditorPageGeneral *page_general)
1701 {
1702 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1703 
1704 	if (page_general->priv->source_combo_box)
1705 		return e_source_combo_box_get_extension_name (E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box));
1706 
1707 	return page_general->priv->source_extension_name;
1708 }
1709 
1710 void
e_comp_editor_page_general_set_source_extension_name(ECompEditorPageGeneral * page_general,const gchar * source_extension_name)1711 e_comp_editor_page_general_set_source_extension_name (ECompEditorPageGeneral *page_general,
1712 						      const gchar *source_extension_name)
1713 {
1714 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1715 
1716 	if (g_strcmp0 (page_general->priv->source_extension_name, source_extension_name) == 0)
1717 		return;
1718 
1719 	g_free (page_general->priv->source_extension_name);
1720 	page_general->priv->source_extension_name = g_strdup (source_extension_name);
1721 
1722 	g_object_notify (G_OBJECT (page_general), "source-extension-name");
1723 
1724 	if (page_general->priv->source_combo_box) {
1725 		e_source_combo_box_set_extension_name (
1726 			E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box),
1727 			source_extension_name);
1728 	}
1729 }
1730 
1731 ESource *
e_comp_editor_page_general_ref_selected_source(ECompEditorPageGeneral * page_general)1732 e_comp_editor_page_general_ref_selected_source (ECompEditorPageGeneral *page_general)
1733 {
1734 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1735 	g_return_val_if_fail (page_general->priv->source_combo_box != NULL, NULL);
1736 
1737 	return e_source_combo_box_ref_active (E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box));
1738 }
1739 
1740 void
e_comp_editor_page_general_set_selected_source(ECompEditorPageGeneral * page_general,ESource * source)1741 e_comp_editor_page_general_set_selected_source (ECompEditorPageGeneral *page_general,
1742 						ESource *source)
1743 {
1744 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1745 	if (source)
1746 		g_return_if_fail (E_IS_SOURCE (source));
1747 
1748 	if (!page_general->priv->source_combo_box) {
1749 		g_clear_object (&page_general->priv->select_source);
1750 		page_general->priv->select_source = g_object_ref (source);
1751 
1752 		g_object_notify (G_OBJECT (page_general), "selected-source");
1753 
1754 		return;
1755 	}
1756 
1757 	if (source)
1758 		e_source_combo_box_set_active (E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box), source);
1759 
1760 	g_object_notify (G_OBJECT (page_general), "selected-source");
1761 }
1762 
1763 gboolean
e_comp_editor_page_general_get_show_attendees(ECompEditorPageGeneral * page_general)1764 e_comp_editor_page_general_get_show_attendees (ECompEditorPageGeneral *page_general)
1765 {
1766 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
1767 
1768 	return page_general->priv->show_attendees;
1769 }
1770 
1771 void
e_comp_editor_page_general_set_show_attendees(ECompEditorPageGeneral * page_general,gboolean show_attendees)1772 e_comp_editor_page_general_set_show_attendees (ECompEditorPageGeneral *page_general,
1773 					       gboolean show_attendees)
1774 {
1775 	ECompEditor *comp_editor;
1776 
1777 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1778 
1779 	if ((show_attendees ? 1 : 0) == (page_general->priv->show_attendees ? 1 : 0))
1780 		return;
1781 
1782 	page_general->priv->show_attendees = show_attendees;
1783 
1784 	g_object_notify (G_OBJECT (page_general), "show-attendees");
1785 
1786 	e_comp_editor_page_general_update_view (page_general);
1787 
1788 	comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
1789 	if (comp_editor)
1790 		e_comp_editor_set_changed (comp_editor, TRUE);
1791 	g_clear_object (&comp_editor);
1792 }
1793 
1794 gint
e_comp_editor_page_general_get_data_column_width(ECompEditorPageGeneral * page_general)1795 e_comp_editor_page_general_get_data_column_width (ECompEditorPageGeneral *page_general)
1796 {
1797 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), 0);
1798 
1799 	return page_general->priv->data_column_width;
1800 }
1801 
1802 void
e_comp_editor_page_general_set_data_column_width(ECompEditorPageGeneral * page_general,gint data_column_width)1803 e_comp_editor_page_general_set_data_column_width (ECompEditorPageGeneral *page_general,
1804 						  gint data_column_width)
1805 {
1806 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1807 
1808 	if (data_column_width == page_general->priv->data_column_width)
1809 		return;
1810 
1811 	page_general->priv->data_column_width = data_column_width;
1812 
1813 	g_object_notify (G_OBJECT (page_general), "data-column-width");
1814 
1815 	e_comp_editor_page_general_update_view (page_general);
1816 }
1817 
1818 void
e_comp_editor_page_general_update_view(ECompEditorPageGeneral * page_general)1819 e_comp_editor_page_general_update_view (ECompEditorPageGeneral *page_general)
1820 {
1821 	GtkContainer *grid;
1822 
1823 	g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
1824 
1825 	if (!page_general->priv->source_label)
1826 		return;
1827 
1828 	grid = GTK_CONTAINER (page_general);
1829 
1830 	gtk_container_child_set (grid, page_general->priv->organizer_hbox,
1831 		"left-attach", 1, "width", page_general->priv->data_column_width, NULL);
1832 	gtk_container_child_set (grid, page_general->priv->attendees_hbox,
1833 		"width", page_general->priv->data_column_width, NULL);
1834 
1835 	if (page_general->priv->show_attendees) {
1836 		if (gtk_widget_get_parent (page_general->priv->source_label) == GTK_WIDGET (grid)) {
1837 
1838 			g_object_ref (page_general->priv->source_label);
1839 			g_object_ref (page_general->priv->source_and_color_hbox);
1840 
1841 			gtk_container_remove (grid, page_general->priv->source_label);
1842 			gtk_container_remove (grid, page_general->priv->source_and_color_hbox);
1843 
1844 			gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox),
1845 				page_general->priv->source_label, FALSE, FALSE, 0);
1846 			gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox),
1847 				page_general->priv->source_and_color_hbox, TRUE, TRUE, 0);
1848 
1849 			g_object_unref (page_general->priv->source_label);
1850 			g_object_unref (page_general->priv->source_and_color_hbox);
1851 		}
1852 
1853 		gtk_container_child_set (grid, page_general->priv->organizer_label,
1854 			"left-attach", 0, NULL);
1855 
1856 		gtk_widget_show (page_general->priv->organizer_label);
1857 		gtk_widget_show (page_general->priv->organizer_hbox);
1858 		gtk_widget_show (page_general->priv->attendees_button);
1859 		gtk_widget_show (page_general->priv->attendees_hbox);
1860 		gtk_widget_show (page_general->priv->attendees_list_view);
1861 		gtk_widget_show (page_general->priv->attendees_button_box);
1862 	} else {
1863 		if (gtk_widget_get_parent (page_general->priv->source_label) != GTK_WIDGET (grid)) {
1864 			GtkContainer *container;
1865 			GtkGrid *ggrid;
1866 
1867 			container = GTK_CONTAINER (page_general->priv->organizer_hbox);
1868 			ggrid = GTK_GRID (grid);
1869 
1870 			g_object_ref (page_general->priv->source_label);
1871 			g_object_ref (page_general->priv->source_and_color_hbox);
1872 
1873 			gtk_container_remove (container, page_general->priv->source_label);
1874 			gtk_container_remove (container, page_general->priv->source_and_color_hbox);
1875 
1876 			gtk_grid_attach (ggrid, page_general->priv->source_label, 0, 0, 1, 1);
1877 			gtk_grid_attach (ggrid, page_general->priv->source_and_color_hbox, 1, 0, 1, 1);
1878 
1879 			g_object_unref (page_general->priv->source_label);
1880 			g_object_unref (page_general->priv->source_and_color_hbox);
1881 		}
1882 
1883 		gtk_container_child_set (grid, page_general->priv->source_label,
1884 			"left-attach", 0, NULL);
1885 		gtk_container_child_set (grid, page_general->priv->source_and_color_hbox,
1886 			"left-attach", 1, "width", page_general->priv->data_column_width, NULL);
1887 
1888 		gtk_widget_hide (page_general->priv->organizer_label);
1889 		gtk_widget_hide (page_general->priv->organizer_hbox);
1890 		gtk_widget_hide (page_general->priv->attendees_button);
1891 		gtk_widget_hide (page_general->priv->attendees_hbox);
1892 		gtk_widget_hide (page_general->priv->attendees_list_view);
1893 		gtk_widget_hide (page_general->priv->attendees_button_box);
1894 	}
1895 }
1896 
1897 EMeetingStore *
e_comp_editor_page_general_get_meeting_store(ECompEditorPageGeneral * page_general)1898 e_comp_editor_page_general_get_meeting_store (ECompEditorPageGeneral *page_general)
1899 {
1900 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1901 
1902 	return page_general->priv->meeting_store;
1903 }
1904 
1905 /* Element is a string, an email address; free with g_slist_free_full (slist, g_free); */
1906 GSList *
e_comp_editor_page_general_get_added_attendees(ECompEditorPageGeneral * page_general)1907 e_comp_editor_page_general_get_added_attendees (ECompEditorPageGeneral *page_general)
1908 {
1909 	const GPtrArray *attendees;
1910 	GHashTable *orig_attendees = NULL;
1911 	GSList *added_attendees = NULL, *link;
1912 	gint ii;
1913 
1914 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1915 
1916 	if (!page_general->priv->show_attendees)
1917 		return NULL;
1918 
1919 	if (page_general->priv->orig_attendees) {
1920 		for (link = page_general->priv->orig_attendees; link; link = g_slist_next (link)) {
1921 			const gchar *address = link->data;
1922 
1923 			if (address) {
1924 				if (!orig_attendees)
1925 					orig_attendees = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
1926 				g_hash_table_insert (orig_attendees, (gpointer) address, GINT_TO_POINTER (1));
1927 			}
1928 		}
1929 	}
1930 
1931 	attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
1932 
1933 	for (ii = 0; ii < attendees->len; ii++) {
1934 		EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
1935 		const gchar *address;
1936 
1937 		address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
1938 
1939 		if (address && (!orig_attendees || !g_hash_table_contains (orig_attendees, address)))
1940 			added_attendees = g_slist_prepend (added_attendees, g_strdup (address));
1941 	}
1942 
1943 	if (orig_attendees)
1944 		g_hash_table_destroy (orig_attendees);
1945 
1946 	return g_slist_reverse (added_attendees);
1947 }
1948 
1949 /* Element is a string, an email address; free with g_slist_free_full (slist, g_free); */
1950 GSList *
e_comp_editor_page_general_get_removed_attendees(ECompEditorPageGeneral * page_general)1951 e_comp_editor_page_general_get_removed_attendees (ECompEditorPageGeneral *page_general)
1952 {
1953 	const GPtrArray *attendees;
1954 	GHashTable *new_attendees;
1955 	GSList *removed_attendees = NULL, *link;
1956 	gint ii;
1957 
1958 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
1959 
1960 	if (!page_general->priv->orig_attendees)
1961 		return NULL;
1962 
1963 	if (!page_general->priv->show_attendees) {
1964 		GSList *copy, *link;
1965 
1966 		copy = g_slist_copy (page_general->priv->orig_attendees);
1967 		for (link = copy; link; link = g_slist_next (link)) {
1968 			link->data = g_strdup (link->data);
1969 		}
1970 
1971 		return copy;
1972 	}
1973 
1974 	new_attendees = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
1975 	attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
1976 
1977 	for (ii = 0; ii < attendees->len; ii++) {
1978 		EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
1979 		const gchar *address;
1980 
1981 		address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
1982 		if (address)
1983 			g_hash_table_insert (new_attendees, (gpointer) address, GINT_TO_POINTER (1));
1984 	}
1985 
1986 	for (link = page_general->priv->orig_attendees; link; link = g_slist_next (link)) {
1987 		const gchar *address = link->data;
1988 
1989 		if (address && !g_hash_table_contains (new_attendees, address)) {
1990 			removed_attendees = g_slist_prepend (removed_attendees, g_strdup (address));
1991 		}
1992 	}
1993 
1994 	g_hash_table_destroy (new_attendees);
1995 
1996 	return g_slist_reverse (removed_attendees);
1997 }
1998 
1999 GtkWidget *
e_comp_editor_page_general_get_source_combo_box(ECompEditorPageGeneral * page_general)2000 e_comp_editor_page_general_get_source_combo_box (ECompEditorPageGeneral *page_general)
2001 {
2002 	g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
2003 
2004 	return page_general->priv->source_combo_box;
2005 }
2006