1 /*
2  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.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  * Authors:
17  *	JP Rosevear <jpr@ximian.com>
18  *	Rodrigo Moya <rodrigo@ximian.com>
19  *	Federico Mena-Quintero <federico@ximian.com>
20  */
21 
22 #include "evolution-config.h"
23 
24 #include <glib.h>
25 #include <glib/gi18n-lib.h>
26 #include <gtk/gtk.h>
27 #include <libecal/libecal.h>
28 #include <e-util/e-util.h>
29 
30 #include "calendar-config.h"
31 #include "itip-utils.h"
32 #include "tag-calendar.h"
33 
34 #include "e-cal-dialogs.h"
35 
36 /* is_past_event:
37  *
38  * returns TRUE if @comp is in the past, FALSE otherwise.
39  * Comparision is based only on date part, time part is ignored.
40  */
41 static gboolean
is_past_event(ECalComponent * comp)42 is_past_event (ECalComponent *comp)
43 {
44 	ECalComponentDateTime *end_date;
45 	gboolean res;
46 
47 	if (!comp)
48 		return TRUE;
49 
50 	end_date = e_cal_component_get_dtend (comp);
51 
52 	if (!end_date)
53 		return FALSE;
54 
55 	res = i_cal_time_compare_date_only (
56 		e_cal_component_datetime_get_value (end_date),
57 		i_cal_time_new_current_with_zone (i_cal_time_get_timezone (e_cal_component_datetime_get_value (end_date)))) == -1;
58 
59 	e_cal_component_datetime_free (end_date);
60 
61 	return res;
62 }
63 
64 /**
65  * e_cal_dialogs_cancel_component:
66  *
67  * Pops up a dialog box asking the user whether he wants to send a
68  * cancel and delete an iTip/iMip message
69  *
70  * Return value: TRUE if the user clicked Yes, FALSE otherwise.
71  **/
72 gboolean
e_cal_dialogs_cancel_component(GtkWindow * parent,ECalClient * cal_client,ECalComponent * comp,gboolean deleting)73 e_cal_dialogs_cancel_component (GtkWindow *parent,
74 				ECalClient *cal_client,
75 				ECalComponent *comp,
76 				gboolean deleting)
77 {
78 	ECalComponentVType vtype;
79 	const gchar *id;
80 
81 	if (deleting && e_cal_client_check_save_schedules (cal_client))
82 		return TRUE;
83 
84 	vtype = e_cal_component_get_vtype (comp);
85 
86 	switch (vtype) {
87 	case E_CAL_COMPONENT_EVENT:
88 		if (is_past_event (comp)) {
89 			/* don't ask neither send notification to others on past events */
90 			return FALSE;
91 		}
92 		if (deleting)
93 			id = "calendar:prompt-cancel-meeting";
94 		else
95 			id = "calendar:prompt-delete-meeting";
96 		break;
97 
98 	case E_CAL_COMPONENT_TODO:
99 		if (deleting)
100 			id = "calendar:prompt-cancel-task";
101 		else
102 			id = "calendar:prompt-delete-task";
103 		break;
104 
105 	case E_CAL_COMPONENT_JOURNAL:
106 		if (deleting)
107 			id = "calendar:prompt-cancel-memo";
108 		else
109 			id = "calendar:prompt-delete-memo";
110 		break;
111 
112 	default:
113 		g_message (G_STRLOC ": Cannot handle object of type %d", vtype);
114 		return FALSE;
115 	}
116 
117 	if (e_alert_run_dialog_for_args (parent, id, NULL) == GTK_RESPONSE_YES)
118 		return TRUE;
119 	else
120 		return FALSE;
121 }
122 
123 typedef struct {
124 	ECalModel *model;
125 	ESource *from_source;
126 	ESource *to_source;
127 	ECalClient *to_client;
128 	const gchar *extension_name;
129 } CopySourceData;
130 
131 static void
copy_source_data_free(gpointer ptr)132 copy_source_data_free (gpointer ptr)
133 {
134 	CopySourceData *csd = ptr;
135 
136 	if (csd) {
137 		if (csd->to_client)
138 			e_cal_model_emit_object_created (csd->model, csd->to_client);
139 
140 		g_clear_object (&csd->model);
141 		g_clear_object (&csd->from_source);
142 		g_clear_object (&csd->to_source);
143 		g_clear_object (&csd->to_client);
144 		g_slice_free (CopySourceData, csd);
145 	}
146 }
147 
148 struct ForeachTzidData
149 {
150 	ECalClient *from_client;
151 	ECalClient *to_client;
152 	gboolean success;
153 	GCancellable *cancellable;
154 	GError **error;
155 };
156 
157 static void
add_timezone_to_cal_cb(ICalParameter * param,gpointer data)158 add_timezone_to_cal_cb (ICalParameter *param,
159                         gpointer data)
160 {
161 	struct ForeachTzidData *ftd = data;
162 	ICalTimezone *tz = NULL;
163 	const gchar *tzid;
164 
165 	g_return_if_fail (ftd != NULL);
166 	g_return_if_fail (ftd->from_client != NULL);
167 	g_return_if_fail (ftd->to_client != NULL);
168 
169 	if (!ftd->success)
170 		return;
171 
172 	tzid = i_cal_parameter_get_tzid (param);
173 	if (!tzid || !*tzid)
174 		return;
175 
176 	if (g_cancellable_set_error_if_cancelled (ftd->cancellable, ftd->error)) {
177 		ftd->success = FALSE;
178 		return;
179 	}
180 
181 	ftd->success = e_cal_client_get_timezone_sync (ftd->from_client, tzid, &tz, ftd->cancellable, ftd->error);
182 	if (ftd->success && tz != NULL)
183 		ftd->success = e_cal_client_add_timezone_sync (ftd->to_client, tz, ftd->cancellable, ftd->error);
184 }
185 
186 static void
copy_source_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)187 copy_source_thread (EAlertSinkThreadJobData *job_data,
188 		    gpointer user_data,
189 		    GCancellable *cancellable,
190 		    GError **error)
191 {
192 	CopySourceData *csd = user_data;
193 	EClient *client;
194 	ECalClient *from_client = NULL, *to_client = NULL;
195 	GSList *objects = NULL, *link;
196 	struct ForeachTzidData ftd;
197 	gint n_objects, ii, last_percent = 0;
198 
199 	if (!csd)
200 		goto out;
201 
202 	client = e_util_open_client_sync (job_data, e_cal_model_get_client_cache (csd->model), csd->extension_name, csd->from_source, 30, cancellable, error);
203 	if (client)
204 		from_client = E_CAL_CLIENT (client);
205 
206 	if (!from_client)
207 		goto out;
208 
209 	client = e_util_open_client_sync (job_data, e_cal_model_get_client_cache (csd->model), csd->extension_name, csd->to_source, 30, cancellable, error);
210 	if (client)
211 		to_client = E_CAL_CLIENT (client);
212 
213 	if (!to_client)
214 		goto out;
215 
216 	if (e_client_is_readonly (E_CLIENT (to_client))) {
217 		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_READ_ONLY, _("Destination is read only"));
218 		goto out;
219 	}
220 
221 	if (!e_cal_client_get_object_list_sync (from_client, "#t", &objects, cancellable, error))
222 		goto out;
223 
224 	ftd.from_client = from_client;
225 	ftd.to_client = to_client;
226 	ftd.success = TRUE;
227 	ftd.cancellable = cancellable;
228 	ftd.error = error;
229 
230 	n_objects = g_slist_length (objects);
231 
232 	for (link = objects, ii = 0; link && ftd.success && !g_cancellable_is_cancelled (cancellable); link = g_slist_next (link), ii++) {
233 		ICalComponent *icomp = link->data;
234 		ICalComponent *existing_icomp = NULL;
235 		gint percent = 100 * (ii + 1) / n_objects;
236 		GError *local_error = NULL;
237 
238 		if (e_cal_client_get_object_sync (to_client, i_cal_component_get_uid (icomp), NULL, &existing_icomp, cancellable, &local_error) &&
239 		    icomp != NULL) {
240 			if (!e_cal_client_modify_object_sync (to_client, icomp, E_CAL_OBJ_MOD_ALL, E_CAL_OPERATION_FLAG_NONE, cancellable, error))
241 				break;
242 
243 			g_object_unref (existing_icomp);
244 		} else if (local_error && !g_error_matches (local_error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND)) {
245 			g_propagate_error (error, local_error);
246 			break;
247 		} else {
248 			i_cal_component_foreach_tzid (icomp, add_timezone_to_cal_cb, &ftd);
249 
250 			g_clear_error (&local_error);
251 
252 			if (!ftd.success)
253 				break;
254 
255 			if (!e_cal_client_create_object_sync (to_client, icomp, E_CAL_OPERATION_FLAG_NONE, NULL, cancellable, error))
256 				break;
257 		}
258 
259 		if (percent != last_percent) {
260 			camel_operation_progress (cancellable, percent);
261 			last_percent = percent;
262 		}
263 	}
264 
265 	if (ii > 0 && ftd.success)
266 		csd->to_client = g_object_ref (to_client);
267  out:
268 	e_util_free_nullable_object_slist (objects);
269 	g_clear_object (&from_client);
270 	g_clear_object (&to_client);
271 }
272 
273 /**
274  * e_cal_dialogs_copy_source
275  *
276  * Implements the Copy command for sources, allowing the user to select a target
277  * source to copy to.
278  */
279 void
e_cal_dialogs_copy_source(GtkWindow * parent,ECalModel * model,ESource * from_source)280 e_cal_dialogs_copy_source (GtkWindow *parent,
281 			   ECalModel *model,
282 			   ESource *from_source)
283 {
284 	ECalClientSourceType obj_type;
285 	ESource *to_source;
286 	const gchar *extension_name;
287 	const gchar *format;
288 	const gchar *alert_ident;
289 
290 	g_return_if_fail (E_IS_CAL_MODEL (model));
291 	g_return_if_fail (E_IS_SOURCE (from_source));
292 
293 	switch (e_cal_model_get_component_kind (model)) {
294 		case I_CAL_VEVENT_COMPONENT:
295 			obj_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
296 			extension_name = E_SOURCE_EXTENSION_CALENDAR;
297 			format = _("Copying events to the calendar “%s”");
298 			alert_ident = "calendar:failed-copy-event";
299 			break;
300 		case I_CAL_VJOURNAL_COMPONENT:
301 			obj_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
302 			extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
303 			format = _("Copying memos to the memo list “%s”");
304 			alert_ident = "calendar:failed-copy-memo";
305 			break;
306 		case I_CAL_VTODO_COMPONENT:
307 			obj_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
308 			extension_name = E_SOURCE_EXTENSION_TASK_LIST;
309 			format = _("Copying tasks to the task list “%s”");
310 			alert_ident = "calendar:failed-copy-task";
311 			break;
312 		default:
313 			g_warn_if_reached ();
314 			return;
315 	}
316 
317 	to_source = e_cal_dialogs_select_source (parent, e_cal_model_get_registry (model), obj_type, from_source);
318 	if (to_source) {
319 		CopySourceData *csd;
320 		GCancellable *cancellable;
321 		ECalDataModel *data_model;
322 		gchar *display_name;
323 		gchar *description;
324 
325 		csd = g_slice_new0 (CopySourceData);
326 		csd->model = g_object_ref (model);
327 		csd->from_source = g_object_ref (from_source);
328 		csd->to_source = g_object_ref (to_source);
329 		csd->to_client = NULL;
330 		csd->extension_name = extension_name;
331 
332 		display_name = e_util_get_source_full_name (e_cal_model_get_registry (model), to_source);
333 		description = g_strdup_printf (format, display_name);
334 		data_model = e_cal_model_get_data_model (model);
335 
336 		cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident, display_name,
337 			copy_source_thread, csd, copy_source_data_free);
338 
339 		g_clear_object (&cancellable);
340 		g_free (display_name);
341 		g_free (description);
342 	}
343 
344 	g_clear_object (&to_source);
345 }
346 
347 /**
348  * e_cal_dialogs_delete_component:
349  * @comp: A calendar component if a single component is to be deleted, or NULL
350  * if more that one component is to be deleted.
351  * @consider_as_untitled: If deleting more than one component, this is ignored.
352  * Otherwise, whether to consider the component as not having a summary; if
353  * FALSE then the component's summary string will be used.
354  * @n_comps: Number of components that are to be deleted.
355  * @vtype: Type of the components that are to be deleted.  This is ignored
356  * if only one component is to be deleted, and the vtype is extracted from
357  * the component instead.
358  * @widget: A widget to use as a basis for conversion from UTF8 into font
359  * encoding.
360  *
361  * Pops up a dialog box asking the user whether he wants to delete a number of
362  * calendar components.  The dialog will not appear, however, if the
363  * configuration option for confirmation is turned off.
364  *
365  * Return value: TRUE if the user clicked Yes, FALSE otherwise.  If the
366  * configuration option for confirmation is turned off, this function will
367  * unconditionally return TRUE.
368  **/
369 gboolean
e_cal_dialogs_delete_component(ECalComponent * comp,gboolean consider_as_untitled,gint n_comps,ECalComponentVType vtype,GtkWidget * widget)370 e_cal_dialogs_delete_component (ECalComponent *comp,
371 				gboolean consider_as_untitled,
372 				gint n_comps,
373 				ECalComponentVType vtype,
374 				GtkWidget *widget)
375 {
376 	const gchar *id;
377 	gchar *arg0 = NULL;
378 	gint response;
379 	gboolean attendees;
380 
381 	if (comp) {
382 		g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
383 		g_return_val_if_fail (n_comps == 1, FALSE);
384 	} else {
385 		g_return_val_if_fail (n_comps > 1, FALSE);
386 		g_return_val_if_fail (vtype != E_CAL_COMPONENT_NO_TYPE, FALSE);
387 	}
388 
389 	g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
390 
391 	if (comp) {
392 		vtype = e_cal_component_get_vtype (comp);
393 
394 		if (!consider_as_untitled) {
395 			ECalComponentText *summary;
396 
397 			summary = e_cal_component_get_summary (comp);
398 			if (summary) {
399 				arg0 = g_strdup (e_cal_component_text_get_value (summary));
400 				e_cal_component_text_free (summary);
401 			}
402 		}
403 
404 		switch (vtype) {
405 		case E_CAL_COMPONENT_EVENT:
406 			attendees = e_cal_component_has_attendees (comp);
407 			if (arg0) {
408 				if (attendees)
409 					id = "calendar:prompt-delete-titled-meeting";
410 				else
411 					id = "calendar:prompt-delete-titled-appointment";
412 			} else {
413 				if (attendees)
414 					id = "calendar:prompt-delete-meeting";
415 				else
416 					id = "calendar:prompt-delete-appointment";
417 			}
418 			break;
419 
420 		case E_CAL_COMPONENT_TODO:
421 			if (arg0)
422 				id = "calendar:prompt-delete-named-task";
423 			else
424 				id = "calendar:prompt-delete-task";
425 			break;
426 
427 		case E_CAL_COMPONENT_JOURNAL:
428 			if (arg0)
429 				id = "calendar:prompt-delete-named-memo";
430 			else
431 				id = "calendar:prompt-delete-memo";
432 			break;
433 
434 		default:
435 			g_message ("%s: Cannot handle object of type %d", G_STRFUNC, vtype);
436 			g_free (arg0);
437 			return FALSE;
438 		}
439 	} else {
440 		switch (vtype) {
441 		case E_CAL_COMPONENT_EVENT:
442 			if (n_comps == 1)
443 				id = "calendar:prompt-delete-appointment";
444 			else
445 				id = "calendar:prompt-delete-appointments";
446 			break;
447 
448 		case E_CAL_COMPONENT_TODO:
449 			if (n_comps == 1)
450 				id = "calendar:prompt-delete-task";
451 			else
452 				id = "calendar:prompt-delete-tasks";
453 			break;
454 
455 		case E_CAL_COMPONENT_JOURNAL:
456 			if (n_comps == 1)
457 				id = "calendar:prompt-delete-memo";
458 			else
459 				id = "calendar:prompt-delete-memos";
460 			break;
461 
462 		default:
463 			g_message ("%s: Cannot handle objects of type %d", G_STRFUNC, vtype);
464 			return FALSE;
465 		}
466 
467 		if (n_comps > 1)
468 			arg0 = g_strdup_printf ("%d", n_comps);
469 	}
470 
471 	response = e_alert_run_dialog_for_args ((GtkWindow *) gtk_widget_get_toplevel (widget), id, arg0, NULL);
472 	g_free (arg0);
473 
474 	return response == GTK_RESPONSE_YES;
475 }
476 
477 static void
cb_toggled_cb(GtkToggleButton * toggle,gpointer data)478 cb_toggled_cb (GtkToggleButton *toggle,
479                gpointer data)
480 {
481 	gboolean active = FALSE;
482 	GtkWidget *entry = (GtkWidget *) data;
483 
484 	active = gtk_toggle_button_get_active (toggle);
485 	gtk_widget_set_sensitive (entry, active);
486 }
487 
488 gboolean
e_cal_dialogs_prompt_retract(GtkWidget * parent,ECalComponent * comp,gchar ** retract_text,gboolean * retract)489 e_cal_dialogs_prompt_retract (GtkWidget *parent,
490 			      ECalComponent *comp,
491 			      gchar **retract_text,
492 			      gboolean *retract)
493 {
494 	gchar *message = NULL;
495 	ECalComponentVType type = E_CAL_COMPONENT_NO_TYPE;
496 	GtkMessageDialog *dialog = NULL;
497 	GtkWidget *cb, *label, *entry, *vbox, *sw, *frame;
498 	gboolean ret_val = FALSE;
499 
500 	type = e_cal_component_get_vtype (comp);
501 
502 	switch (type) {
503 		case E_CAL_COMPONENT_EVENT:
504 			message = g_strdup_printf (_("Are you sure you want to delete this meeting?"));
505 			break;
506 		case E_CAL_COMPONENT_TODO:
507 			message = g_strdup_printf (_("Are you sure you want to delete this task?"));
508 			break;
509 		case E_CAL_COMPONENT_JOURNAL:
510 			message = g_strdup_printf (_("Are you sure you want to delete this memo?"));
511 			break;
512 		default:
513 			g_message ("Retract: Unsupported object type \n");
514 			return FALSE;
515 	}
516 
517 	dialog = (GtkMessageDialog *) gtk_message_dialog_new_with_markup
518 		((GtkWindow *) gtk_widget_get_toplevel (parent), GTK_DIALOG_MODAL,
519 		 GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, "<b>%s</b>", message);
520 	g_free (message);
521 
522 	gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
523 	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
524 
525 	vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
526 	gtk_box_set_spacing (GTK_BOX (vbox), 12);
527 
528 	cb = gtk_check_button_new_with_mnemonic (_("_Delete this item from all other recipient’s mailboxes?"));
529 	gtk_container_add (GTK_CONTAINER (vbox), cb);
530 
531 	label = gtk_label_new_with_mnemonic (_("_Retract comment"));
532 
533 	frame = gtk_frame_new (NULL);
534 	gtk_frame_set_label_widget ((GtkFrame *) frame, label);
535 	gtk_frame_set_label_align ((GtkFrame *) frame, 0, 0);
536 	gtk_container_add (GTK_CONTAINER (vbox), frame);
537 	gtk_frame_set_shadow_type ((GtkFrame *) frame, GTK_SHADOW_NONE);
538 
539 	sw = gtk_scrolled_window_new (NULL, NULL);
540 	gtk_scrolled_window_set_policy ((GtkScrolledWindow *) sw, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
541 
542 	entry = gtk_text_view_new ();
543 	gtk_scrolled_window_add_with_viewport ((GtkScrolledWindow *) sw, entry);
544 	gtk_label_set_mnemonic_widget ((GtkLabel *) label, entry);
545 	gtk_container_add (GTK_CONTAINER (frame), sw);
546 
547 	g_signal_connect (
548 		cb, "toggled",
549 		G_CALLBACK (cb_toggled_cb), entry);
550 
551 	gtk_widget_show_all ((GtkWidget *) dialog);
552 
553 	ret_val = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK);
554 
555 	if (ret_val) {
556 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cb))) {
557 			GtkTextIter text_iter_start, text_iter_end;
558 			GtkTextBuffer *text_buffer;
559 
560 			*retract = TRUE;
561 			text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
562 			gtk_text_buffer_get_start_iter (text_buffer, &text_iter_start);
563 			gtk_text_buffer_get_end_iter   (text_buffer, &text_iter_end);
564 
565 			*retract_text = gtk_text_buffer_get_text (text_buffer, &text_iter_start,
566 					&text_iter_end, FALSE);
567 		} else
568 			*retract = FALSE;
569 	}
570 
571 	gtk_widget_destroy ((GtkWidget *) dialog);
572 
573 	return ret_val;
574 }
575 
576 typedef struct {
577 	GtkWidget *dialog;
578 
579 	GtkWidget *month_combobox;
580 	GtkWidget *year;
581 	ECalendar *ecal;
582 	GtkWidget *grid;
583 
584 	gint year_val;
585 	gint month_val;
586 	gint day_val;
587 
588 	ETagCalendar *tag_calendar;
589 
590 	ECalDataModel *data_model;
591 	ECalendarViewMoveType *out_move_type;
592 	time_t *out_exact_date;
593 } GoToDialog;
594 
595 static GoToDialog *dlg = NULL;
596 
597 /* Callback used when the year adjustment is changed */
598 static void
year_changed(GtkAdjustment * adj,gpointer data)599 year_changed (GtkAdjustment *adj,
600               gpointer data)
601 {
602 	GtkSpinButton *spin_button;
603 	GoToDialog *dlg = data;
604 
605 	spin_button = GTK_SPIN_BUTTON (dlg->year);
606 	dlg->year_val = gtk_spin_button_get_value_as_int (spin_button);
607 
608 	e_calendar_item_set_first_month (
609 		e_calendar_get_item (dlg->ecal), dlg->year_val, dlg->month_val);
610 }
611 
612 /* Callback used when a month button is toggled */
613 static void
month_changed(GtkToggleButton * toggle,gpointer data)614 month_changed (GtkToggleButton *toggle,
615                gpointer data)
616 {
617 	GtkComboBox *combo_box;
618 	GoToDialog *dlg = data;
619 
620 	combo_box = GTK_COMBO_BOX (dlg->month_combobox);
621 	dlg->month_val = gtk_combo_box_get_active (combo_box);
622 
623 	e_calendar_item_set_first_month (
624 		e_calendar_get_item (dlg->ecal), dlg->year_val, dlg->month_val);
625 }
626 
627 /* Event handler for day groups in the month item.  A button press makes
628  * the calendar jump to the selected day and destroys the Go-to dialog box. */
629 static void
ecal_event(ECalendarItem * calitem,gpointer user_data)630 ecal_event (ECalendarItem *calitem,
631             gpointer user_data)
632 {
633 	GoToDialog *dlg = user_data;
634 	GDate start_date, end_date;
635 	ICalTime *tt = i_cal_time_new_null_time ();
636 	ICalTimezone *timezone;
637 	time_t et;
638 
639 	g_warn_if_fail (e_calendar_item_get_selection (calitem, &start_date, &end_date));
640 	timezone = e_cal_data_model_get_timezone (dlg->data_model);
641 
642 	i_cal_time_set_date (tt,
643 		g_date_get_year (&start_date),
644 		g_date_get_month (&start_date),
645 		g_date_get_day (&start_date));
646 
647 	et = i_cal_time_as_timet_with_zone (tt, timezone);
648 
649 	g_clear_object (&tt);
650 
651 	*(dlg->out_move_type) = E_CALENDAR_VIEW_MOVE_TO_EXACT_DAY;
652 	*(dlg->out_exact_date) = et;
653 
654 	gtk_dialog_response (GTK_DIALOG (dlg->dialog), GTK_RESPONSE_APPLY);
655 }
656 
657 /* Returns the current time, for the ECalendarItem. */
658 static struct tm
get_current_time(ECalendarItem * calitem,gpointer data)659 get_current_time (ECalendarItem *calitem,
660                   gpointer data)
661 {
662 	ICalTimezone *zone;
663 	ICalTime *tt;
664 	struct tm tmp_tm;
665 
666 	/* Get the current timezone. */
667 	zone = calendar_config_get_icaltimezone ();
668 
669 	tt = i_cal_time_new_from_timet_with_zone (time (NULL), FALSE, zone);
670 
671 	tmp_tm = e_cal_util_icaltime_to_tm (tt);
672 
673 	g_clear_object (&tt);
674 
675 	return tmp_tm;
676 }
677 
678 /* Gets the widgets from the XML file and returns if they are all available. */
679 static void
goto_dialog_create_widgets(GoToDialog * dlg,GtkWindow * parent)680 goto_dialog_create_widgets (GoToDialog *dlg,
681 			    GtkWindow *parent)
682 {
683 	ECalendarItem *calitem;
684 	GtkWidget *widget;
685 	GtkGrid *grid;
686 	GtkComboBoxText *text_combo;
687 
688 	dlg->dialog = gtk_dialog_new_with_buttons (_("Select Date"), parent, 0,
689 		_("Select _Today"), GTK_RESPONSE_ACCEPT,
690 		_("_Cancel"), GTK_RESPONSE_CANCEL,
691 		NULL);
692 
693 	g_object_set (G_OBJECT (dlg->dialog),
694 		"border-width", 12,
695 		NULL);
696 
697 	widget = gtk_grid_new ();
698 	dlg->grid = widget;
699 	grid = GTK_GRID (widget);
700 
701 	widget = gtk_dialog_get_content_area (GTK_DIALOG (dlg->dialog));
702 	gtk_box_pack_start (GTK_BOX (widget), dlg->grid, TRUE, TRUE, 0);
703 
704 	widget = gtk_combo_box_text_new ();
705 	dlg->month_combobox = widget;
706 
707 	text_combo = GTK_COMBO_BOX_TEXT (widget);
708 	gtk_combo_box_text_append_text (text_combo, _("January"));
709 	gtk_combo_box_text_append_text (text_combo, _("February"));
710 	gtk_combo_box_text_append_text (text_combo, _("March"));
711 	gtk_combo_box_text_append_text (text_combo, _("April"));
712 	gtk_combo_box_text_append_text (text_combo, _("May"));
713 	gtk_combo_box_text_append_text (text_combo, _("June"));
714 	gtk_combo_box_text_append_text (text_combo, _("July"));
715 	gtk_combo_box_text_append_text (text_combo, _("August"));
716 	gtk_combo_box_text_append_text (text_combo, _("September"));
717 	gtk_combo_box_text_append_text (text_combo, _("October"));
718 	gtk_combo_box_text_append_text (text_combo, _("November"));
719 	gtk_combo_box_text_append_text (text_combo, _("December"));
720 
721 	gtk_grid_attach (grid, widget, 0, 0, 1, 1);
722 
723 	widget = gtk_spin_button_new (NULL, 1, 0);
724 	gtk_spin_button_set_range (GTK_SPIN_BUTTON (widget), 1969, 9999);
725 	gtk_spin_button_set_increments (GTK_SPIN_BUTTON (widget), 1, 5);
726 	gtk_grid_attach (grid, widget, 1, 0, 1, 1);
727 
728 	dlg->year = widget;
729 
730 	dlg->ecal = E_CALENDAR (e_calendar_new ());
731 	dlg->tag_calendar = e_tag_calendar_new (dlg->ecal);
732 
733 	calitem = e_calendar_get_item (dlg->ecal);
734 
735 	gnome_canvas_item_set (
736 		GNOME_CANVAS_ITEM (calitem),
737 		"move_selection_when_moving", FALSE,
738 		NULL);
739 	e_calendar_item_set_display_popup (calitem, FALSE);
740 	g_object_set (G_OBJECT (dlg->ecal),
741 		"hexpand", TRUE,
742 		"halign", GTK_ALIGN_FILL,
743 		"vexpand", TRUE,
744 		"valign", GTK_ALIGN_FILL,
745 		NULL);
746 
747 	gtk_grid_attach (grid, GTK_WIDGET (dlg->ecal), 0, 1, 2, 1);
748 
749 	e_calendar_item_set_first_month (calitem, dlg->year_val, dlg->month_val);
750 	e_calendar_item_set_get_time_callback (calitem, get_current_time, dlg, NULL);
751 
752 	gtk_widget_show_all (GTK_WIDGET (grid));
753 }
754 
755 /* Create a copy, thus a move to a distant date will not cause large event lookups */
756 
757 /* Creates a "goto date" dialog and runs it */
758 gboolean
e_cal_dialogs_goto_run(GtkWindow * parent,ECalDataModel * data_model,const GDate * from_date,ECalendarViewMoveType * out_move_type,time_t * out_exact_date)759 e_cal_dialogs_goto_run (GtkWindow *parent,
760 			ECalDataModel *data_model,
761 			const GDate *from_date,
762 			ECalendarViewMoveType *out_move_type,
763 			time_t *out_exact_date)
764 {
765 	GtkAdjustment *adj;
766 	gint response;
767 
768 	if (dlg) {
769 		return FALSE;
770 	}
771 
772 	g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model), FALSE);
773 	g_return_val_if_fail (out_move_type != NULL, FALSE);
774 	g_return_val_if_fail (out_exact_date != NULL, FALSE);
775 
776 	dlg = g_new0 (GoToDialog, 1);
777 
778 	goto_dialog_create_widgets (dlg, parent);
779 
780 	dlg->data_model = e_cal_data_model_new_clone (data_model);
781 	dlg->out_move_type = out_move_type;
782 	dlg->out_exact_date = out_exact_date;
783 
784 	if (from_date) {
785 		dlg->year_val = g_date_get_year (from_date);
786 		dlg->month_val = g_date_get_month (from_date) - 1;
787 		dlg->day_val = g_date_get_day (from_date);
788 	} else {
789 		ICalTime *tt;
790 		ICalTimezone *timezone;
791 
792 		timezone = e_cal_data_model_get_timezone (dlg->data_model);
793 		tt = i_cal_time_new_current_with_zone (timezone);
794 
795 		dlg->year_val = i_cal_time_get_year (tt);
796 		dlg->month_val = i_cal_time_get_month (tt) - 1;
797 		dlg->day_val = i_cal_time_get_day (tt);
798 
799 		g_clear_object (&tt);
800 	}
801 
802 	g_signal_connect (
803 		dlg->month_combobox, "changed",
804 		G_CALLBACK (month_changed), dlg);
805 
806 	adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dlg->year));
807 	g_signal_connect (
808 		adj, "value_changed",
809 		G_CALLBACK (year_changed), dlg);
810 
811 	g_signal_connect (
812 		e_calendar_get_item (dlg->ecal), "selection_changed",
813 		G_CALLBACK (ecal_event), dlg);
814 
815 	gtk_combo_box_set_active (GTK_COMBO_BOX (dlg->month_combobox), dlg->month_val);
816 	gtk_spin_button_set_value (GTK_SPIN_BUTTON (dlg->year), dlg->year_val);
817 
818 	gtk_window_set_transient_for (GTK_WINDOW (dlg->dialog), parent);
819 
820 	/* set initial selection to current day */
821 
822 	e_calendar_get_item (dlg->ecal)->selection_set = TRUE;
823 	e_calendar_get_item (dlg->ecal)->selection_start_month_offset = 0;
824 	e_calendar_get_item (dlg->ecal)->selection_start_day = dlg->day_val;
825 	e_calendar_get_item (dlg->ecal)->selection_end_month_offset = 0;
826 	e_calendar_get_item (dlg->ecal)->selection_end_day = dlg->day_val;
827 
828 	gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (e_calendar_get_item (dlg->ecal)));
829 
830 	e_tag_calendar_subscribe (dlg->tag_calendar, dlg->data_model);
831 
832 	response = gtk_dialog_run (GTK_DIALOG (dlg->dialog));
833 
834 	e_tag_calendar_unsubscribe (dlg->tag_calendar, dlg->data_model);
835 
836 	gtk_widget_destroy (dlg->dialog);
837 
838 	if (response == GTK_RESPONSE_ACCEPT)
839 		*(dlg->out_move_type) = E_CALENDAR_VIEW_MOVE_TO_TODAY;
840 
841 	g_clear_object (&dlg->tag_calendar);
842 	g_clear_object (&dlg->data_model);
843 
844 	g_free (dlg);
845 	dlg = NULL;
846 
847 	return response == GTK_RESPONSE_ACCEPT || response == GTK_RESPONSE_APPLY;
848 }
849 
850 gboolean
e_cal_dialogs_recur_component(ECalClient * client,ECalComponent * comp,ECalObjModType * mod,GtkWindow * parent,gboolean delegated)851 e_cal_dialogs_recur_component (ECalClient *client,
852 			       ECalComponent *comp,
853 			       ECalObjModType *mod,
854 			       GtkWindow *parent,
855 			       gboolean delegated)
856 {
857 	gchar *str;
858 	GtkWidget *dialog, *rb_this, *rb_prior, *rb_future, *rb_all, *hbox;
859 	GtkWidget *placeholder, *vbox;
860 	GtkWidget *content_area;
861 	ECalComponentVType vtype;
862 	gboolean ret;
863 
864 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
865 
866 	vtype = e_cal_component_get_vtype (comp);
867 
868 	switch (vtype) {
869 	case E_CAL_COMPONENT_EVENT:
870 		if (!delegated)
871 			str = g_strdup_printf (_("You are modifying a recurring event. What would you like to modify?"));
872 		else
873 			str = g_strdup_printf (_("You are delegating a recurring event. What would you like to delegate?"));
874 		break;
875 
876 	case E_CAL_COMPONENT_TODO:
877 		str = g_strdup_printf (_("You are modifying a recurring task. What would you like to modify?"));
878 		break;
879 
880 	case E_CAL_COMPONENT_JOURNAL:
881 		str = g_strdup_printf (_("You are modifying a recurring memo. What would you like to modify?"));
882 		break;
883 
884 	default:
885 		g_message ("recur_component_dialog(): Cannot handle object of type %d", vtype);
886 		return FALSE;
887 	}
888 
889 	dialog = gtk_message_dialog_new (parent, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, "%s", str);
890 	g_free (str);
891 	gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
892 	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
893 
894 	content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
895 
896 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
897 	gtk_container_add (GTK_CONTAINER (content_area), hbox);
898 
899 	placeholder = gtk_label_new ("");
900 	gtk_widget_set_size_request (placeholder, 48, 48);
901 	gtk_box_pack_start (GTK_BOX (hbox), placeholder, FALSE, FALSE, 0);
902 	gtk_widget_show (placeholder);
903 
904 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
905 	gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
906 	gtk_widget_show (vbox);
907 
908 	rb_this = gtk_radio_button_new_with_label (NULL, _("This Instance Only"));
909 	gtk_container_add (GTK_CONTAINER (vbox), rb_this);
910 
911 	if (!e_client_check_capability (E_CLIENT (client), E_CAL_STATIC_CAPABILITY_NO_THISANDPRIOR)) {
912 		rb_prior = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this), _("This and Prior Instances"));
913 		gtk_container_add (GTK_CONTAINER (vbox), rb_prior);
914 	} else
915 		rb_prior = NULL;
916 
917 	if (!e_client_check_capability (E_CLIENT (client), E_CAL_STATIC_CAPABILITY_NO_THISANDFUTURE)) {
918 		rb_future = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this), _("This and Future Instances"));
919 		gtk_container_add (GTK_CONTAINER (vbox), rb_future);
920 	} else
921 		rb_future = NULL;
922 
923 	rb_all = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this), _("All Instances"));
924 	gtk_container_add (GTK_CONTAINER (vbox), rb_all);
925 
926 	gtk_widget_show_all (hbox);
927 
928 	placeholder = gtk_label_new ("");
929 	gtk_box_pack_start (GTK_BOX (content_area), placeholder, FALSE, FALSE, 0);
930 	gtk_widget_show (placeholder);
931 
932 	ret = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK;
933 
934 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_this)))
935 		*mod = E_CAL_OBJ_MOD_THIS;
936 	else if (rb_prior && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_prior)))
937 		*mod = E_CAL_OBJ_MOD_THIS_AND_PRIOR;
938 	else if (rb_future && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_future)))
939 		*mod = E_CAL_OBJ_MOD_THIS_AND_FUTURE;
940 	else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_all))) {
941 		*mod = E_CAL_OBJ_MOD_ALL;
942 	}
943 
944 	gtk_widget_destroy (dialog);
945 
946 	return ret;
947 }
948 
949 gboolean
e_cal_dialogs_recur_icalcomp(ECalClient * client,ICalComponent * icomp,ECalObjModType * mod,GtkWindow * parent,gboolean delegated)950 e_cal_dialogs_recur_icalcomp (ECalClient *client,
951 			      ICalComponent *icomp,
952 			      ECalObjModType *mod,
953 			      GtkWindow *parent,
954 			      gboolean delegated)
955 {
956 	ECalComponent *comp;
957 	gboolean res;
958 
959 	g_return_val_if_fail (icomp != NULL, FALSE);
960 
961 	if (!e_cal_util_component_is_instance (icomp)) {
962 		*mod = E_CAL_OBJ_MOD_ALL;
963 		return TRUE;
964 	}
965 
966 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
967 	if (!comp)
968 		return FALSE;
969 
970 	res = e_cal_dialogs_recur_component (client, comp, mod, parent, delegated);
971 
972 	g_object_unref (comp);
973 
974 	return res;
975 }
976 
977 /**
978  * e_cal_dialogs_select_source
979  *
980  * Implements dialog for allowing user to select a destination source.
981  */
982 ESource *
e_cal_dialogs_select_source(GtkWindow * parent,ESourceRegistry * registry,ECalClientSourceType obj_type,ESource * except_source)983 e_cal_dialogs_select_source (GtkWindow *parent,
984 			     ESourceRegistry *registry,
985 			     ECalClientSourceType obj_type,
986 			     ESource *except_source)
987 {
988 	GtkWidget *dialog;
989 	ESource *selected_source = NULL;
990 	const gchar *extension_name;
991 	const gchar *icon_name;
992 
993 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
994 
995 	if (obj_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
996 		extension_name = E_SOURCE_EXTENSION_CALENDAR;
997 		icon_name = "x-office-calendar";
998 	} else if (obj_type == E_CAL_CLIENT_SOURCE_TYPE_TASKS) {
999 		extension_name = E_SOURCE_EXTENSION_TASK_LIST;
1000 		icon_name = "stock_todo";
1001 	} else if (obj_type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
1002 		extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
1003 		icon_name = "stock_journal";
1004 	} else
1005 		return NULL;
1006 
1007 	/* create the dialog */
1008 	dialog = e_source_selector_dialog_new (parent, registry, extension_name);
1009 
1010 	if (icon_name)
1011 		gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name);
1012 
1013 	if (except_source)
1014 		e_source_selector_dialog_set_except_source (E_SOURCE_SELECTOR_DIALOG (dialog), except_source);
1015 
1016 	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
1017 		goto exit;
1018 
1019 	selected_source = e_source_selector_dialog_peek_primary_selection (
1020 		E_SOURCE_SELECTOR_DIALOG (dialog));
1021 	if (selected_source != NULL)
1022 		g_object_ref (selected_source);
1023 
1024  exit:
1025 	gtk_widget_destroy (dialog);
1026 
1027 	return selected_source;
1028 }
1029 
1030 static gboolean
component_has_new_attendees(ECalComponent * comp)1031 component_has_new_attendees (ECalComponent *comp)
1032 {
1033 	g_return_val_if_fail (comp != NULL, FALSE);
1034 
1035 	if (!e_cal_component_has_attendees (comp))
1036 		return FALSE;
1037 
1038 	return g_object_get_data (G_OBJECT (comp), "new-attendees") != NULL;
1039 }
1040 
1041 static gboolean
have_nonprocedural_alarm(ECalComponent * comp)1042 have_nonprocedural_alarm (ECalComponent *comp)
1043 {
1044 	GSList *uids, *link;
1045 
1046 	g_return_val_if_fail (comp != NULL, FALSE);
1047 
1048 	uids = e_cal_component_get_alarm_uids (comp);
1049 
1050 	for (link = uids; link; link = g_slist_next (link)) {
1051 		ECalComponentAlarm *alarm;
1052 		ECalComponentAlarmAction action = E_CAL_COMPONENT_ALARM_UNKNOWN;
1053 
1054 		alarm = e_cal_component_get_alarm (comp, link->data);
1055 		if (alarm) {
1056 			action = e_cal_component_alarm_get_action (alarm);
1057 			e_cal_component_alarm_free (alarm);
1058 
1059 			if (action != E_CAL_COMPONENT_ALARM_NONE &&
1060 			    action != E_CAL_COMPONENT_ALARM_PROCEDURE &&
1061 			    action != E_CAL_COMPONENT_ALARM_UNKNOWN) {
1062 				g_slist_free_full (uids, g_free);
1063 				return TRUE;
1064 			}
1065 		}
1066 	}
1067 
1068 	g_slist_free_full (uids, g_free);
1069 
1070 	return FALSE;
1071 }
1072 
1073 static GtkWidget *
add_checkbox(GtkBox * where,const gchar * caption)1074 add_checkbox (GtkBox *where,
1075               const gchar *caption)
1076 {
1077 	GtkWidget *checkbox, *align;
1078 
1079 	g_return_val_if_fail (where != NULL, NULL);
1080 	g_return_val_if_fail (caption != NULL, NULL);
1081 
1082 	checkbox = gtk_check_button_new_with_mnemonic (caption);
1083 	align = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1084 	gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 12);
1085 	gtk_container_add (GTK_CONTAINER (align), checkbox);
1086 	gtk_widget_show (checkbox);
1087 	gtk_box_pack_start (where, align, TRUE, TRUE, 2);
1088 	gtk_widget_show (align);
1089 
1090 	return checkbox;
1091 }
1092 
1093 /**
1094  * e_cal_dialogs_send_component:
1095  *
1096  * Pops up a dialog box asking the user whether he wants to send a
1097  * iTip/iMip message
1098  *
1099  * Return value: TRUE if the user clicked Yes, FALSE otherwise.
1100  **/
1101 gboolean
e_cal_dialogs_send_component(GtkWindow * parent,ECalClient * client,ECalComponent * comp,gboolean new,gboolean * strip_alarms,gboolean * only_new_attendees)1102 e_cal_dialogs_send_component (GtkWindow *parent,
1103 			      ECalClient *client,
1104 			      ECalComponent *comp,
1105 			      gboolean new,
1106 			      gboolean *strip_alarms,
1107 			      gboolean *only_new_attendees)
1108 {
1109 	ECalComponentVType vtype;
1110 	const gchar *id;
1111 	GtkWidget *dialog, *sa_checkbox = NULL, *ona_checkbox = NULL;
1112 	GtkWidget *content_area;
1113 	gboolean res;
1114 
1115 	if (strip_alarms)
1116 		*strip_alarms = TRUE;
1117 
1118 	if (e_cal_client_check_save_schedules (client))
1119 		return FALSE;
1120 
1121 	if (!itip_component_has_recipients (comp))
1122 		return FALSE;
1123 
1124 	vtype = e_cal_component_get_vtype (comp);
1125 
1126 	switch (vtype) {
1127 	case E_CAL_COMPONENT_EVENT:
1128 		if (new)
1129 			id = "calendar:prompt-meeting-invite";
1130 		else
1131 			id = "calendar:prompt-send-updated-meeting-info";
1132 		break;
1133 
1134 	case E_CAL_COMPONENT_TODO:
1135 		if (new)
1136 			id = "calendar:prompt-send-task";
1137 		else
1138 			id = "calendar:prompt-send-updated-task-info";
1139 		break;
1140 	case E_CAL_COMPONENT_JOURNAL:
1141 		if (new)
1142 			id = "calendar:prompt-send-memo";
1143 		else
1144 			id = "calendar:prompt-send-updated-memo-info";
1145 		break;
1146 	default:
1147 		g_message (
1148 			"send_component_dialog(): "
1149 			"Cannot handle object of type %d", vtype);
1150 		return FALSE;
1151 	}
1152 
1153 	if (only_new_attendees && !component_has_new_attendees (comp)) {
1154 		/* do not show the check if there is no new attendee and
1155 		 * set as all attendees are required to be notified */
1156 		*only_new_attendees = FALSE;
1157 
1158 		/* pretend it as being passed NULL to simplify code below */
1159 		only_new_attendees = NULL;
1160 	}
1161 
1162 	if (strip_alarms && !have_nonprocedural_alarm (comp)) {
1163 		/* pretend it as being passed NULL to simplify code below */
1164 		strip_alarms = NULL;
1165 	}
1166 
1167 	dialog = e_alert_dialog_new_for_args (parent, id, NULL);
1168 	content_area = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
1169 
1170 	if (strip_alarms)
1171 		sa_checkbox = add_checkbox (GTK_BOX (content_area), _("Send my reminders with this event"));
1172 	if (only_new_attendees)
1173 		ona_checkbox = add_checkbox (GTK_BOX (content_area), _("Notify new attendees _only"));
1174 
1175 	res = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
1176 
1177 	if (res && strip_alarms)
1178 		*strip_alarms = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sa_checkbox));
1179 	if (only_new_attendees)
1180 		*only_new_attendees = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ona_checkbox));
1181 
1182 	gtk_widget_destroy (GTK_WIDGET (dialog));
1183 
1184 	return res;
1185 }
1186 
1187 /**
1188  * e_cal_dialogs_send_dragged_or_resized_component:
1189  *
1190  * Pops up a dialog box asking the user whether he wants to send a
1191  * iTip/iMip message or cancel the drag/resize operations
1192  *
1193  * Return value: GTK_RESPONSE_YES if the user clicked Yes,
1194  *		 GTK_RESPONSE_NO if the user clicked No and
1195  *		 GTK_RESPONSE_CANCEL otherwise.
1196  **/
1197 GtkResponseType
e_cal_dialogs_send_dragged_or_resized_component(GtkWindow * parent,ECalClient * client,ECalComponent * comp,gboolean * strip_alarms,gboolean * only_new_attendees)1198 e_cal_dialogs_send_dragged_or_resized_component (GtkWindow *parent,
1199 						 ECalClient *client,
1200 						 ECalComponent *comp,
1201 						 gboolean *strip_alarms,
1202 						 gboolean *only_new_attendees)
1203 {
1204 	ECalComponentVType vtype;
1205 	const gchar *id;
1206 	GtkWidget *dialog, *sa_checkbox = NULL, *ona_checkbox = NULL;
1207 	GtkWidget *content_area;
1208 	gboolean save_schedules = FALSE;
1209 	GtkResponseType res;
1210 
1211 	if (strip_alarms)
1212 		*strip_alarms = TRUE;
1213 
1214 	if (e_cal_client_check_save_schedules (client))
1215 		save_schedules = TRUE;
1216 
1217 	if (!itip_component_has_recipients (comp))
1218 		save_schedules = TRUE;
1219 
1220 	vtype = e_cal_component_get_vtype (comp);
1221 
1222 	switch (vtype) {
1223 	case E_CAL_COMPONENT_EVENT:
1224 		id = save_schedules ?
1225 			"calendar:prompt-save-meeting-dragged-or-resized" :
1226 			"calendar:prompt-send-updated-meeting-info-dragged-or-resized";
1227 		break;
1228 	default:
1229 		g_message (
1230 			"send_component_dialog(): "
1231 			"Cannot handle object of type %d", vtype);
1232 		return GTK_RESPONSE_CANCEL;
1233 	}
1234 
1235 	if (only_new_attendees && !component_has_new_attendees (comp)) {
1236 		/* do not show the check if there is no new attendee and
1237 		 * set as all attendees are required to be notified */
1238 		*only_new_attendees = FALSE;
1239 
1240 		/* pretend it as being passed NULL to simplify code below */
1241 		only_new_attendees = NULL;
1242 	}
1243 
1244 	if (strip_alarms && !have_nonprocedural_alarm (comp)) {
1245 		/* pretend it as being passed NULL to simplify code below */
1246 		strip_alarms = NULL;
1247 	}
1248 
1249 	dialog = e_alert_dialog_new_for_args (parent, id, NULL);
1250 	content_area = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
1251 
1252 	if (strip_alarms)
1253 		sa_checkbox = add_checkbox (GTK_BOX (content_area), _("Send my reminders with this event"));
1254 	if (only_new_attendees)
1255 		ona_checkbox = add_checkbox (GTK_BOX (content_area), _("Notify new attendees _only"));
1256 
1257 	res = gtk_dialog_run (GTK_DIALOG (dialog));
1258 
1259 	/*
1260 	 * When the Escape key is pressed a GTK_RESPONSE_DELETE_EVENT is generated.
1261 	 * We should treat this event as the user cancelling the operation
1262 	 */
1263 	if (res == GTK_RESPONSE_DELETE_EVENT)
1264 		res = GTK_RESPONSE_CANCEL;
1265 
1266 	if (res == GTK_RESPONSE_YES && strip_alarms)
1267 		*strip_alarms = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sa_checkbox));
1268 	if (only_new_attendees)
1269 		*only_new_attendees = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ona_checkbox));
1270 
1271 	gtk_widget_destroy (GTK_WIDGET (dialog));
1272 
1273 	return res;
1274 }
1275 
1276 gboolean
e_cal_dialogs_send_component_prompt_subject(GtkWindow * parent,ICalComponent * component)1277 e_cal_dialogs_send_component_prompt_subject (GtkWindow *parent,
1278 					     ICalComponent *component)
1279 {
1280 	ICalComponentKind kind;
1281 	const gchar *id;
1282 
1283 	kind = i_cal_component_isa (component);
1284 
1285 	switch (kind) {
1286 	case I_CAL_VEVENT_COMPONENT:
1287 		id = "calendar:prompt-save-no-subject-calendar";
1288 		break;
1289 
1290 	case I_CAL_VTODO_COMPONENT:
1291 		id = "calendar:prompt-save-no-subject-task";
1292 		break;
1293 	case I_CAL_VJOURNAL_COMPONENT:
1294 		id = "calendar:prompt-send-no-subject-memo";
1295 		break;
1296 
1297 	default:
1298 		g_message ("%s: Cannot handle object of type %d", G_STRFUNC, kind);
1299 		return FALSE;
1300 	}
1301 
1302 	if (e_alert_run_dialog_for_args (parent, id, NULL) == GTK_RESPONSE_YES)
1303 		return TRUE;
1304 	else
1305 		return FALSE;
1306 }
1307