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