2 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
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/>.
17 * JP Rosevear <jpr@ximian.com>
18 * Rodrigo Moya <rodrigo@ximian.com>
19 * Federico Mena-Quintero <federico@ximian.com>
22 #include "evolution-config.h"
25 #include <glib/gi18n-lib.h>
27 #include <libecal/libecal.h>
28 #include <e-util/e-util.h>
30 #include "calendar-config.h"
31 #include "itip-utils.h"
32 #include "tag-calendar.h"
34 #include "e-cal-dialogs.h"
38 * returns TRUE if @comp is in the past, FALSE otherwise.
39 * Comparision is based only on date part, time part is ignored.
42 is_past_event (ECalComponent
*comp
)
44 ECalComponentDateTime end_date
;
50 end_date
.value
= NULL
;
52 e_cal_component_get_dtend (comp
, &end_date
);
57 res
= icaltime_compare_date_only (
59 icaltime_current_time_with_zone (
60 icaltime_get_timezone (*end_date
.value
))) == -1;
61 e_cal_component_free_datetime (&end_date
);
67 * e_cal_dialogs_cancel_component:
69 * Pops up a dialog box asking the user whether he wants to send a
70 * cancel and delete an iTip/iMip message
72 * Return value: TRUE if the user clicked Yes, FALSE otherwise.
75 e_cal_dialogs_cancel_component (GtkWindow
*parent
,
76 ECalClient
*cal_client
,
80 ECalComponentVType vtype
;
83 if (deleting
&& e_cal_client_check_save_schedules (cal_client
))
86 vtype
= e_cal_component_get_vtype (comp
);
89 case E_CAL_COMPONENT_EVENT
:
90 if (is_past_event (comp
)) {
91 /* don't ask neither send notification to others on past events */
95 id
= "calendar:prompt-cancel-meeting";
97 id
= "calendar:prompt-delete-meeting";
100 case E_CAL_COMPONENT_TODO
:
102 id
= "calendar:prompt-cancel-task";
104 id
= "calendar:prompt-delete-task";
107 case E_CAL_COMPONENT_JOURNAL
:
109 id
= "calendar:prompt-cancel-memo";
111 id
= "calendar:prompt-delete-memo";
115 g_message (G_STRLOC
": Cannot handle object of type %d", vtype
);
119 if (e_alert_run_dialog_for_args (parent
, id
, NULL
) == GTK_RESPONSE_YES
)
127 ESource
*from_source
;
129 ECalClient
*to_client
;
130 const gchar
*extension_name
;
134 copy_source_data_free (gpointer ptr
)
136 CopySourceData
*csd
= ptr
;
140 e_cal_model_emit_object_created (csd
->model
, csd
->to_client
);
142 g_clear_object (&csd
->model
);
143 g_clear_object (&csd
->from_source
);
144 g_clear_object (&csd
->to_source
);
145 g_clear_object (&csd
->to_client
);
150 struct ForeachTzidData
152 ECalClient
*from_client
;
153 ECalClient
*to_client
;
155 GCancellable
*cancellable
;
160 add_timezone_to_cal_cb (icalparameter
*param
,
163 struct ForeachTzidData
*ftd
= data
;
164 icaltimezone
*tz
= NULL
;
167 g_return_if_fail (ftd
!= NULL
);
168 g_return_if_fail (ftd
->from_client
!= NULL
);
169 g_return_if_fail (ftd
->to_client
!= NULL
);
174 tzid
= icalparameter_get_tzid (param
);
178 if (g_cancellable_set_error_if_cancelled (ftd
->cancellable
, ftd
->error
)) {
179 ftd
->success
= FALSE
;
183 ftd
->success
= e_cal_client_get_timezone_sync (ftd
->from_client
, tzid
, &tz
, ftd
->cancellable
, ftd
->error
);
184 if (ftd
->success
&& tz
!= NULL
)
185 ftd
->success
= e_cal_client_add_timezone_sync (ftd
->to_client
, tz
, ftd
->cancellable
, ftd
->error
);
189 copy_source_thread (EAlertSinkThreadJobData
*job_data
,
191 GCancellable
*cancellable
,
194 CopySourceData
*csd
= user_data
;
196 ECalClient
*from_client
= NULL
, *to_client
= NULL
;
197 GSList
*objects
= NULL
, *link
;
198 struct ForeachTzidData ftd
;
199 gint n_objects
, ii
, last_percent
= 0;
204 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
);
206 from_client
= E_CAL_CLIENT (client
);
211 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
);
213 to_client
= E_CAL_CLIENT (client
);
218 if (e_client_is_readonly (E_CLIENT (to_client
))) {
219 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_READ_ONLY
, _("Destination is read only"));
223 if (!e_cal_client_get_object_list_sync (from_client
, "#t", &objects
, cancellable
, error
))
226 ftd
.from_client
= from_client
;
227 ftd
.to_client
= to_client
;
229 ftd
.cancellable
= cancellable
;
232 n_objects
= g_slist_length (objects
);
234 for (link
= objects
, ii
= 0; link
&& ftd
.success
&& !g_cancellable_is_cancelled (cancellable
); link
= g_slist_next (link
), ii
++) {
235 icalcomponent
*icalcomp
= link
->data
;
236 icalcomponent
*existing_icalcomp
= NULL
;
237 gint percent
= 100 * (ii
+ 1) / n_objects
;
238 GError
*local_error
= NULL
;
240 if (e_cal_client_get_object_sync (to_client
, icalcomponent_get_uid (icalcomp
), NULL
, &existing_icalcomp
, cancellable
, &local_error
) &&
242 if (!e_cal_client_modify_object_sync (to_client
, icalcomp
, E_CAL_OBJ_MOD_ALL
, cancellable
, error
))
245 icalcomponent_free (existing_icalcomp
);
246 } else if (local_error
&& !g_error_matches (local_error
, E_CAL_CLIENT_ERROR
, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND
)) {
247 g_propagate_error (error
, local_error
);
250 icalcomponent_foreach_tzid (icalcomp
, add_timezone_to_cal_cb
, &ftd
);
252 g_clear_error (&local_error
);
257 if (!e_cal_client_create_object_sync (to_client
, icalcomp
, NULL
, cancellable
, error
))
261 if (percent
!= last_percent
) {
262 camel_operation_progress (cancellable
, percent
);
263 last_percent
= percent
;
267 if (ii
> 0 && ftd
.success
)
268 csd
->to_client
= g_object_ref (to_client
);
270 e_cal_client_free_icalcomp_slist (objects
);
271 g_clear_object (&from_client
);
272 g_clear_object (&to_client
);
276 * e_cal_dialogs_copy_source
278 * Implements the Copy command for sources, allowing the user to select a target
282 e_cal_dialogs_copy_source (GtkWindow
*parent
,
284 ESource
*from_source
)
286 ECalClientSourceType obj_type
;
288 const gchar
*extension_name
;
290 const gchar
*alert_ident
;
292 g_return_if_fail (E_IS_CAL_MODEL (model
));
293 g_return_if_fail (E_IS_SOURCE (from_source
));
295 switch (e_cal_model_get_component_kind (model
)) {
296 case ICAL_VEVENT_COMPONENT
:
297 obj_type
= E_CAL_CLIENT_SOURCE_TYPE_EVENTS
;
298 extension_name
= E_SOURCE_EXTENSION_CALENDAR
;
299 format
= _("Copying events to the calendar “%s”");
300 alert_ident
= "calendar:failed-copy-event";
302 case ICAL_VJOURNAL_COMPONENT
:
303 obj_type
= E_CAL_CLIENT_SOURCE_TYPE_MEMOS
;
304 extension_name
= E_SOURCE_EXTENSION_MEMO_LIST
;
305 format
= _("Copying memos to the memo list “%s”");
306 alert_ident
= "calendar:failed-copy-memo";
308 case ICAL_VTODO_COMPONENT
:
309 obj_type
= E_CAL_CLIENT_SOURCE_TYPE_TASKS
;
310 extension_name
= E_SOURCE_EXTENSION_TASK_LIST
;
311 format
= _("Copying tasks to the task list “%s”");
312 alert_ident
= "calendar:failed-copy-task";
315 g_warn_if_reached ();
319 to_source
= e_cal_dialogs_select_source (parent
, e_cal_model_get_registry (model
), obj_type
, from_source
);
322 GCancellable
*cancellable
;
323 ECalDataModel
*data_model
;
327 csd
= g_new0 (CopySourceData
, 1);
328 csd
->model
= g_object_ref (model
);
329 csd
->from_source
= g_object_ref (from_source
);
330 csd
->to_source
= g_object_ref (to_source
);
331 csd
->to_client
= NULL
;
332 csd
->extension_name
= extension_name
;
334 display_name
= e_util_get_source_full_name (e_cal_model_get_registry (model
), to_source
);
335 description
= g_strdup_printf (format
, display_name
);
336 data_model
= e_cal_model_get_data_model (model
);
338 cancellable
= e_cal_data_model_submit_thread_job (data_model
, description
, alert_ident
, display_name
,
339 copy_source_thread
, csd
, copy_source_data_free
);
341 g_clear_object (&cancellable
);
342 g_free (display_name
);
343 g_free (description
);
346 g_clear_object (&to_source
);
350 * e_cal_dialogs_delete_component:
351 * @comp: A calendar component if a single component is to be deleted, or NULL
352 * if more that one component is to be deleted.
353 * @consider_as_untitled: If deleting more than one component, this is ignored.
354 * Otherwise, whether to consider the component as not having a summary; if
355 * FALSE then the component's summary string will be used.
356 * @n_comps: Number of components that are to be deleted.
357 * @vtype: Type of the components that are to be deleted. This is ignored
358 * if only one component is to be deleted, and the vtype is extracted from
359 * the component instead.
360 * @widget: A widget to use as a basis for conversion from UTF8 into font
363 * Pops up a dialog box asking the user whether he wants to delete a number of
364 * calendar components. The dialog will not appear, however, if the
365 * configuration option for confirmation is turned off.
367 * Return value: TRUE if the user clicked Yes, FALSE otherwise. If the
368 * configuration option for confirmation is turned off, this function will
369 * unconditionally return TRUE.
372 e_cal_dialogs_delete_component (ECalComponent
*comp
,
373 gboolean consider_as_untitled
,
375 ECalComponentVType vtype
,
384 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp
), FALSE
);
385 g_return_val_if_fail (n_comps
== 1, FALSE
);
387 g_return_val_if_fail (n_comps
> 1, FALSE
);
388 g_return_val_if_fail (vtype
!= E_CAL_COMPONENT_NO_TYPE
, FALSE
);
391 g_return_val_if_fail (GTK_IS_WIDGET (widget
), FALSE
);
394 ECalComponentText summary
;
396 vtype
= e_cal_component_get_vtype (comp
);
398 if (!consider_as_untitled
) {
399 e_cal_component_get_summary (comp
, &summary
);
400 arg0
= g_strdup (summary
.value
);
404 case E_CAL_COMPONENT_EVENT
:
405 attendees
= e_cal_component_has_attendees (comp
);
408 id
= "calendar:prompt-delete-titled-meeting";
410 id
= "calendar:prompt-delete-titled-appointment";
413 id
= "calendar:prompt-delete-meeting";
415 id
= "calendar:prompt-delete-appointment";
419 case E_CAL_COMPONENT_TODO
:
421 id
= "calendar:prompt-delete-named-task";
423 id
= "calendar:prompt-delete-task";
426 case E_CAL_COMPONENT_JOURNAL
:
428 id
= "calendar:prompt-delete-named-memo";
430 id
= "calendar:prompt-delete-memo";
435 "delete_component_dialog(): Cannot handle object of type %d",
442 case E_CAL_COMPONENT_EVENT
:
444 id
= "calendar:prompt-delete-appointment";
446 id
= "calendar:prompt-delete-appointments";
449 case E_CAL_COMPONENT_TODO
:
451 id
= "calendar:prompt-delete-task";
453 id
= "calendar:prompt-delete-tasks";
456 case E_CAL_COMPONENT_JOURNAL
:
458 id
= "calendar:prompt-delete-memo";
460 id
= "calendar:prompt-delete-memos";
465 "delete_component_dialog(): Cannot handle objects of type %d",
471 arg0
= g_strdup_printf ("%d", n_comps
);
474 response
= e_alert_run_dialog_for_args ((GtkWindow
*) gtk_widget_get_toplevel (widget
), id
, arg0
, NULL
);
477 return response
== GTK_RESPONSE_YES
;
481 cb_toggled_cb (GtkToggleButton
*toggle
,
484 gboolean active
= FALSE
;
485 GtkWidget
*entry
= (GtkWidget
*) data
;
487 active
= gtk_toggle_button_get_active (toggle
);
488 gtk_widget_set_sensitive (entry
, active
);
492 e_cal_dialogs_prompt_retract (GtkWidget
*parent
,
494 gchar
**retract_text
,
497 gchar
*message
= NULL
;
498 ECalComponentVType type
= E_CAL_COMPONENT_NO_TYPE
;
499 GtkMessageDialog
*dialog
= NULL
;
500 GtkWidget
*cb
, *label
, *entry
, *vbox
, *sw
, *frame
;
501 gboolean ret_val
= FALSE
;
503 type
= e_cal_component_get_vtype (comp
);
506 case E_CAL_COMPONENT_EVENT
:
507 message
= g_strdup_printf (_("Are you sure you want to delete this meeting?"));
509 case E_CAL_COMPONENT_TODO
:
510 message
= g_strdup_printf (_("Are you sure you want to delete this task?"));
512 case E_CAL_COMPONENT_JOURNAL
:
513 message
= g_strdup_printf (_("Are you sure you want to delete this memo?"));
516 g_message ("Retract: Unsupported object type \n");
520 dialog
= (GtkMessageDialog
*) gtk_message_dialog_new_with_markup
521 ((GtkWindow
*) gtk_widget_get_toplevel (parent
), GTK_DIALOG_MODAL
,
522 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_OK_CANCEL
, "<b>%s</b>", message
);
525 gtk_window_set_resizable (GTK_WINDOW (dialog
), TRUE
);
527 vbox
= gtk_dialog_get_content_area (GTK_DIALOG (dialog
));
528 gtk_box_set_spacing (GTK_BOX (vbox
), 12);
530 cb
= gtk_check_button_new_with_mnemonic (_("_Delete this item from all other recipient’s mailboxes?"));
531 gtk_container_add (GTK_CONTAINER (vbox
), cb
);
533 label
= gtk_label_new_with_mnemonic (_("_Retract comment"));
535 frame
= gtk_frame_new (NULL
);
536 gtk_frame_set_label_widget ((GtkFrame
*) frame
, label
);
537 gtk_frame_set_label_align ((GtkFrame
*) frame
, 0, 0);
538 gtk_container_add (GTK_CONTAINER (vbox
), frame
);
539 gtk_frame_set_shadow_type ((GtkFrame
*) frame
, GTK_SHADOW_NONE
);
541 sw
= gtk_scrolled_window_new (NULL
, NULL
);
542 gtk_scrolled_window_set_policy ((GtkScrolledWindow
*) sw
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
544 entry
= gtk_text_view_new ();
545 gtk_scrolled_window_add_with_viewport ((GtkScrolledWindow
*) sw
, entry
);
546 gtk_label_set_mnemonic_widget ((GtkLabel
*) label
, entry
);
547 gtk_container_add (GTK_CONTAINER (frame
), sw
);
551 G_CALLBACK (cb_toggled_cb
), entry
);
553 gtk_widget_show_all ((GtkWidget
*) dialog
);
555 ret_val
= (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_OK
);
558 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cb
))) {
559 GtkTextIter text_iter_start
, text_iter_end
;
560 GtkTextBuffer
*text_buffer
;
563 text_buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry
));
564 gtk_text_buffer_get_start_iter (text_buffer
, &text_iter_start
);
565 gtk_text_buffer_get_end_iter (text_buffer
, &text_iter_end
);
567 *retract_text
= gtk_text_buffer_get_text (text_buffer
, &text_iter_start
,
568 &text_iter_end
, FALSE
);
573 gtk_widget_destroy ((GtkWidget
*) dialog
);
581 GtkWidget
*month_combobox
;
590 ETagCalendar
*tag_calendar
;
592 ECalDataModel
*data_model
;
593 ECalendarViewMoveType
*out_move_type
;
594 time_t *out_exact_date
;
597 static GoToDialog
*dlg
= NULL
;
599 /* Callback used when the year adjustment is changed */
601 year_changed (GtkAdjustment
*adj
,
604 GtkSpinButton
*spin_button
;
605 GoToDialog
*dlg
= data
;
607 spin_button
= GTK_SPIN_BUTTON (dlg
->year
);
608 dlg
->year_val
= gtk_spin_button_get_value_as_int (spin_button
);
610 e_calendar_item_set_first_month (
611 e_calendar_get_item (dlg
->ecal
), dlg
->year_val
, dlg
->month_val
);
614 /* Callback used when a month button is toggled */
616 month_changed (GtkToggleButton
*toggle
,
619 GtkComboBox
*combo_box
;
620 GoToDialog
*dlg
= data
;
622 combo_box
= GTK_COMBO_BOX (dlg
->month_combobox
);
623 dlg
->month_val
= gtk_combo_box_get_active (combo_box
);
625 e_calendar_item_set_first_month (
626 e_calendar_get_item (dlg
->ecal
), dlg
->year_val
, dlg
->month_val
);
629 /* Event handler for day groups in the month item. A button press makes
630 * the calendar jump to the selected day and destroys the Go-to dialog box. */
632 ecal_event (ECalendarItem
*calitem
,
635 GoToDialog
*dlg
= user_data
;
636 GDate start_date
, end_date
;
637 struct icaltimetype tt
= icaltime_null_time ();
638 icaltimezone
*timezone
;
641 e_calendar_item_get_selection (calitem
, &start_date
, &end_date
);
642 timezone
= e_cal_data_model_get_timezone (dlg
->data_model
);
644 tt
.year
= g_date_get_year (&start_date
);
645 tt
.month
= g_date_get_month (&start_date
);
646 tt
.day
= g_date_get_day (&start_date
);
648 et
= icaltime_as_timet_with_zone (tt
, timezone
);
650 *(dlg
->out_move_type
) = E_CALENDAR_VIEW_MOVE_TO_EXACT_DAY
;
651 *(dlg
->out_exact_date
) = et
;
653 gtk_dialog_response (GTK_DIALOG (dlg
->dialog
), GTK_RESPONSE_APPLY
);
656 /* Returns the current time, for the ECalendarItem. */
658 get_current_time (ECalendarItem
*calitem
,
662 struct tm tmp_tm
= { 0 };
663 struct icaltimetype tt
;
665 /* Get the current timezone. */
666 zone
= calendar_config_get_icaltimezone ();
668 tt
= icaltime_from_timet_with_zone (time (NULL
), FALSE
, zone
);
670 /* Now copy it to the struct tm and return it. */
671 tmp_tm
.tm_year
= tt
.year
- 1900;
672 tmp_tm
.tm_mon
= tt
.month
- 1;
673 tmp_tm
.tm_mday
= tt
.day
;
674 tmp_tm
.tm_hour
= tt
.hour
;
675 tmp_tm
.tm_min
= tt
.minute
;
676 tmp_tm
.tm_sec
= tt
.second
;
677 tmp_tm
.tm_isdst
= -1;
682 /* Gets the widgets from the XML file and returns if they are all available. */
684 goto_dialog_create_widgets (GoToDialog
*dlg
,
687 ECalendarItem
*calitem
;
690 GtkComboBoxText
*text_combo
;
692 dlg
->dialog
= gtk_dialog_new_with_buttons (_("Select Date"), parent
, 0,
693 _("Select _Today"), GTK_RESPONSE_ACCEPT
,
694 _("_Cancel"), GTK_RESPONSE_CANCEL
,
697 g_object_set (G_OBJECT (dlg
->dialog
),
701 widget
= gtk_grid_new ();
703 grid
= GTK_GRID (widget
);
705 widget
= gtk_dialog_get_content_area (GTK_DIALOG (dlg
->dialog
));
706 gtk_box_pack_start (GTK_BOX (widget
), dlg
->grid
, TRUE
, TRUE
, 0);
708 widget
= gtk_combo_box_text_new ();
709 dlg
->month_combobox
= widget
;
711 text_combo
= GTK_COMBO_BOX_TEXT (widget
);
712 gtk_combo_box_text_append_text (text_combo
, _("January"));
713 gtk_combo_box_text_append_text (text_combo
, _("February"));
714 gtk_combo_box_text_append_text (text_combo
, _("March"));
715 gtk_combo_box_text_append_text (text_combo
, _("April"));
716 gtk_combo_box_text_append_text (text_combo
, _("May"));
717 gtk_combo_box_text_append_text (text_combo
, _("June"));
718 gtk_combo_box_text_append_text (text_combo
, _("July"));
719 gtk_combo_box_text_append_text (text_combo
, _("August"));
720 gtk_combo_box_text_append_text (text_combo
, _("September"));
721 gtk_combo_box_text_append_text (text_combo
, _("October"));
722 gtk_combo_box_text_append_text (text_combo
, _("November"));
723 gtk_combo_box_text_append_text (text_combo
, _("December"));
725 gtk_grid_attach (grid
, widget
, 0, 0, 1, 1);
727 widget
= gtk_spin_button_new (NULL
, 1, 0);
728 gtk_spin_button_set_range (GTK_SPIN_BUTTON (widget
), 1969, 9999);
729 gtk_spin_button_set_increments (GTK_SPIN_BUTTON (widget
), 1, 5);
730 gtk_grid_attach (grid
, widget
, 1, 0, 1, 1);
734 dlg
->ecal
= E_CALENDAR (e_calendar_new ());
735 dlg
->tag_calendar
= e_tag_calendar_new (dlg
->ecal
);
737 calitem
= e_calendar_get_item (dlg
->ecal
);
739 gnome_canvas_item_set (
740 GNOME_CANVAS_ITEM (calitem
),
741 "move_selection_when_moving", FALSE
,
743 e_calendar_item_set_display_popup (calitem
, FALSE
);
744 g_object_set (G_OBJECT (dlg
->ecal
),
746 "halign", GTK_ALIGN_FILL
,
748 "valign", GTK_ALIGN_FILL
,
751 gtk_grid_attach (grid
, GTK_WIDGET (dlg
->ecal
), 0, 1, 2, 1);
753 e_calendar_item_set_first_month (calitem
, dlg
->year_val
, dlg
->month_val
);
754 e_calendar_item_set_get_time_callback (calitem
, get_current_time
, dlg
, NULL
);
756 gtk_widget_show_all (GTK_WIDGET (grid
));
759 /* Create a copy, thus a move to a distant date will not cause large event lookups */
761 /* Creates a "goto date" dialog and runs it */
763 e_cal_dialogs_goto_run (GtkWindow
*parent
,
764 ECalDataModel
*data_model
,
765 const GDate
*from_date
,
766 ECalendarViewMoveType
*out_move_type
,
767 time_t *out_exact_date
)
776 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), FALSE
);
777 g_return_val_if_fail (out_move_type
!= NULL
, FALSE
);
778 g_return_val_if_fail (out_exact_date
!= NULL
, FALSE
);
780 dlg
= g_new0 (GoToDialog
, 1);
782 goto_dialog_create_widgets (dlg
, parent
);
784 dlg
->data_model
= e_cal_data_model_new_clone (data_model
);
785 dlg
->out_move_type
= out_move_type
;
786 dlg
->out_exact_date
= out_exact_date
;
789 dlg
->year_val
= g_date_get_year (from_date
);
790 dlg
->month_val
= g_date_get_month (from_date
) - 1;
791 dlg
->day_val
= g_date_get_day (from_date
);
793 struct icaltimetype tt
;
794 icaltimezone
*timezone
;
796 timezone
= e_cal_data_model_get_timezone (dlg
->data_model
);
797 tt
= icaltime_current_time_with_zone (timezone
);
799 dlg
->year_val
= tt
.year
;
800 dlg
->month_val
= tt
.month
- 1;
801 dlg
->day_val
= tt
.day
;
805 dlg
->month_combobox
, "changed",
806 G_CALLBACK (month_changed
), dlg
);
808 adj
= gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dlg
->year
));
810 adj
, "value_changed",
811 G_CALLBACK (year_changed
), dlg
);
814 e_calendar_get_item (dlg
->ecal
), "selection_changed",
815 G_CALLBACK (ecal_event
), dlg
);
817 gtk_combo_box_set_active (GTK_COMBO_BOX (dlg
->month_combobox
), dlg
->month_val
);
818 gtk_spin_button_set_value (GTK_SPIN_BUTTON (dlg
->year
), dlg
->year_val
);
820 gtk_window_set_transient_for (GTK_WINDOW (dlg
->dialog
), parent
);
822 /* set initial selection to current day */
824 e_calendar_get_item (dlg
->ecal
)->selection_set
= TRUE
;
825 e_calendar_get_item (dlg
->ecal
)->selection_start_month_offset
= 0;
826 e_calendar_get_item (dlg
->ecal
)->selection_start_day
= dlg
->day_val
;
827 e_calendar_get_item (dlg
->ecal
)->selection_end_month_offset
= 0;
828 e_calendar_get_item (dlg
->ecal
)->selection_end_day
= dlg
->day_val
;
830 gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (e_calendar_get_item (dlg
->ecal
)));
832 e_tag_calendar_subscribe (dlg
->tag_calendar
, dlg
->data_model
);
834 response
= gtk_dialog_run (GTK_DIALOG (dlg
->dialog
));
836 e_tag_calendar_unsubscribe (dlg
->tag_calendar
, dlg
->data_model
);
838 gtk_widget_destroy (dlg
->dialog
);
840 if (response
== GTK_RESPONSE_ACCEPT
)
841 *(dlg
->out_move_type
) = E_CALENDAR_VIEW_MOVE_TO_TODAY
;
843 g_clear_object (&dlg
->tag_calendar
);
844 g_clear_object (&dlg
->data_model
);
849 return response
== GTK_RESPONSE_ACCEPT
|| response
== GTK_RESPONSE_APPLY
;
853 e_cal_dialogs_recur_component (ECalClient
*client
,
860 GtkWidget
*dialog
, *rb_this
, *rb_prior
, *rb_future
, *rb_all
, *hbox
;
861 GtkWidget
*placeholder
, *vbox
;
862 GtkWidget
*content_area
;
863 ECalComponentVType vtype
;
866 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp
), FALSE
);
868 vtype
= e_cal_component_get_vtype (comp
);
871 case E_CAL_COMPONENT_EVENT
:
873 str
= g_strdup_printf (_("You are modifying a recurring event. What would you like to modify?"));
875 str
= g_strdup_printf (_("You are delegating a recurring event. What would you like to delegate?"));
878 case E_CAL_COMPONENT_TODO
:
879 str
= g_strdup_printf (_("You are modifying a recurring task. What would you like to modify?"));
882 case E_CAL_COMPONENT_JOURNAL
:
883 str
= g_strdup_printf (_("You are modifying a recurring memo. What would you like to modify?"));
887 g_message ("recur_component_dialog(): Cannot handle object of type %d", vtype
);
891 dialog
= gtk_message_dialog_new (parent
, 0, GTK_MESSAGE_QUESTION
, GTK_BUTTONS_OK_CANCEL
, "%s", str
);
893 gtk_window_set_resizable (GTK_WINDOW (dialog
), TRUE
);
895 content_area
= gtk_dialog_get_content_area (GTK_DIALOG (dialog
));
897 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 12);
898 gtk_container_add (GTK_CONTAINER (content_area
), hbox
);
900 placeholder
= gtk_label_new ("");
901 gtk_widget_set_size_request (placeholder
, 48, 48);
902 gtk_box_pack_start (GTK_BOX (hbox
), placeholder
, FALSE
, FALSE
, 0);
903 gtk_widget_show (placeholder
);
905 vbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 6);
906 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, TRUE
, TRUE
, 0);
907 gtk_widget_show (vbox
);
909 rb_this
= gtk_radio_button_new_with_label (NULL
, _("This Instance Only"));
910 gtk_container_add (GTK_CONTAINER (vbox
), rb_this
);
912 if (!e_client_check_capability (E_CLIENT (client
), CAL_STATIC_CAPABILITY_NO_THISANDPRIOR
)) {
913 rb_prior
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this
), _("This and Prior Instances"));
914 gtk_container_add (GTK_CONTAINER (vbox
), rb_prior
);
918 if (!e_client_check_capability (E_CLIENT (client
), CAL_STATIC_CAPABILITY_NO_THISANDFUTURE
)) {
919 rb_future
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this
), _("This and Future Instances"));
920 gtk_container_add (GTK_CONTAINER (vbox
), rb_future
);
924 rb_all
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this
), _("All Instances"));
925 gtk_container_add (GTK_CONTAINER (vbox
), rb_all
);
927 gtk_widget_show_all (hbox
);
929 placeholder
= gtk_label_new ("");
930 gtk_box_pack_start (GTK_BOX (content_area
), placeholder
, FALSE
, FALSE
, 0);
931 gtk_widget_show (placeholder
);
933 ret
= gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_OK
;
935 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_this
)))
936 *mod
= E_CAL_OBJ_MOD_THIS
;
937 else if (rb_prior
&& gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_prior
)))
938 *mod
= E_CAL_OBJ_MOD_THIS_AND_PRIOR
;
939 else if (rb_future
&& gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_future
)))
940 *mod
= E_CAL_OBJ_MOD_THIS_AND_FUTURE
;
941 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_all
))) {
942 *mod
= E_CAL_OBJ_MOD_ALL
;
945 gtk_widget_destroy (dialog
);
951 e_cal_dialogs_recur_icalcomp (ECalClient
*client
,
952 icalcomponent
*icalcomp
,
960 g_return_val_if_fail (icalcomp
!= NULL
, FALSE
);
962 comp
= e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp
));
966 if (!e_cal_component_is_instance (comp
)) {
967 *mod
= E_CAL_OBJ_MOD_ALL
;
968 g_object_unref (comp
);
973 res
= e_cal_dialogs_recur_component (client
, comp
, mod
, parent
, delegated
);
975 g_object_unref (comp
);
981 * e_cal_dialogs_select_source
983 * Implements dialog for allowing user to select a destination source.
986 e_cal_dialogs_select_source (GtkWindow
*parent
,
987 ESourceRegistry
*registry
,
988 ECalClientSourceType obj_type
,
989 ESource
*except_source
)
992 ESource
*selected_source
= NULL
;
993 const gchar
*extension_name
;
994 const gchar
*icon_name
;
996 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry
), NULL
);
998 if (obj_type
== E_CAL_CLIENT_SOURCE_TYPE_EVENTS
) {
999 extension_name
= E_SOURCE_EXTENSION_CALENDAR
;
1000 icon_name
= "x-office-calendar";
1001 } else if (obj_type
== E_CAL_CLIENT_SOURCE_TYPE_TASKS
) {
1002 extension_name
= E_SOURCE_EXTENSION_TASK_LIST
;
1003 icon_name
= "stock_todo";
1004 } else if (obj_type
== E_CAL_CLIENT_SOURCE_TYPE_MEMOS
) {
1005 extension_name
= E_SOURCE_EXTENSION_MEMO_LIST
;
1006 icon_name
= "stock_journal";
1010 /* create the dialog */
1011 dialog
= e_source_selector_dialog_new (parent
, registry
, extension_name
);
1014 gtk_window_set_icon_name (GTK_WINDOW (dialog
), icon_name
);
1017 e_source_selector_dialog_set_except_source (E_SOURCE_SELECTOR_DIALOG (dialog
), except_source
);
1019 if (gtk_dialog_run (GTK_DIALOG (dialog
)) != GTK_RESPONSE_OK
)
1022 selected_source
= e_source_selector_dialog_peek_primary_selection (
1023 E_SOURCE_SELECTOR_DIALOG (dialog
));
1024 if (selected_source
!= NULL
)
1025 g_object_ref (selected_source
);
1028 gtk_widget_destroy (dialog
);
1030 return selected_source
;
1034 component_has_new_attendees (ECalComponent
*comp
)
1036 g_return_val_if_fail (comp
!= NULL
, FALSE
);
1038 if (!e_cal_component_has_attendees (comp
))
1041 return g_object_get_data (G_OBJECT (comp
), "new-attendees") != NULL
;
1045 have_nonprocedural_alarm (ECalComponent
*comp
)
1049 g_return_val_if_fail (comp
!= NULL
, FALSE
);
1051 uids
= e_cal_component_get_alarm_uids (comp
);
1053 for (l
= uids
; l
; l
= l
->next
) {
1054 ECalComponentAlarm
*alarm
;
1055 ECalComponentAlarmAction action
= E_CAL_COMPONENT_ALARM_UNKNOWN
;
1057 alarm
= e_cal_component_get_alarm (comp
, (const gchar
*) l
->data
);
1059 e_cal_component_alarm_get_action (alarm
, &action
);
1060 e_cal_component_alarm_free (alarm
);
1062 if (action
!= E_CAL_COMPONENT_ALARM_NONE
&&
1063 action
!= E_CAL_COMPONENT_ALARM_PROCEDURE
&&
1064 action
!= E_CAL_COMPONENT_ALARM_UNKNOWN
) {
1065 cal_obj_uid_list_free (uids
);
1071 cal_obj_uid_list_free (uids
);
1077 add_checkbox (GtkBox
*where
,
1078 const gchar
*caption
)
1080 GtkWidget
*checkbox
, *align
;
1082 g_return_val_if_fail (where
!= NULL
, NULL
);
1083 g_return_val_if_fail (caption
!= NULL
, NULL
);
1085 checkbox
= gtk_check_button_new_with_mnemonic (caption
);
1086 align
= gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1087 gtk_alignment_set_padding (GTK_ALIGNMENT (align
), 0, 0, 12, 12);
1088 gtk_container_add (GTK_CONTAINER (align
), checkbox
);
1089 gtk_widget_show (checkbox
);
1090 gtk_box_pack_start (where
, align
, TRUE
, TRUE
, 2);
1091 gtk_widget_show (align
);
1097 * e_cal_dialogs_send_component:
1099 * Pops up a dialog box asking the user whether he wants to send a
1102 * Return value: TRUE if the user clicked Yes, FALSE otherwise.
1105 e_cal_dialogs_send_component (GtkWindow
*parent
,
1107 ECalComponent
*comp
,
1109 gboolean
*strip_alarms
,
1110 gboolean
*only_new_attendees
)
1112 ECalComponentVType vtype
;
1114 GtkWidget
*dialog
, *sa_checkbox
= NULL
, *ona_checkbox
= NULL
;
1115 GtkWidget
*content_area
;
1119 *strip_alarms
= TRUE
;
1121 if (e_cal_client_check_save_schedules (client
))
1124 if (!itip_component_has_recipients (comp
))
1127 vtype
= e_cal_component_get_vtype (comp
);
1130 case E_CAL_COMPONENT_EVENT
:
1132 id
= "calendar:prompt-meeting-invite";
1134 id
= "calendar:prompt-send-updated-meeting-info";
1137 case E_CAL_COMPONENT_TODO
:
1139 id
= "calendar:prompt-send-task";
1141 id
= "calendar:prompt-send-updated-task-info";
1143 case E_CAL_COMPONENT_JOURNAL
:
1145 id
= "calendar:prompt-send-memo";
1147 id
= "calendar:prompt-send-updated-memo-info";
1151 "send_component_dialog(): "
1152 "Cannot handle object of type %d", vtype
);
1156 if (only_new_attendees
&& !component_has_new_attendees (comp
)) {
1157 /* do not show the check if there is no new attendee and
1158 * set as all attendees are required to be notified */
1159 *only_new_attendees
= FALSE
;
1161 /* pretend it as being passed NULL to simplify code below */
1162 only_new_attendees
= NULL
;
1165 if (strip_alarms
&& !have_nonprocedural_alarm (comp
)) {
1166 /* pretend it as being passed NULL to simplify code below */
1167 strip_alarms
= NULL
;
1170 dialog
= e_alert_dialog_new_for_args (parent
, id
, NULL
);
1171 content_area
= e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog
));
1174 sa_checkbox
= add_checkbox (GTK_BOX (content_area
), _("Send my reminders with this event"));
1175 if (only_new_attendees
)
1176 ona_checkbox
= add_checkbox (GTK_BOX (content_area
), _("Notify new attendees _only"));
1178 res
= gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_YES
;
1180 if (res
&& strip_alarms
)
1181 *strip_alarms
= !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sa_checkbox
));
1182 if (only_new_attendees
)
1183 *only_new_attendees
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ona_checkbox
));
1185 gtk_widget_destroy (GTK_WIDGET (dialog
));
1191 * e_cal_dialogs_send_dragged_or_resized_component:
1193 * Pops up a dialog box asking the user whether he wants to send a
1194 * iTip/iMip message or cancel the drag/resize operations
1196 * Return value: GTK_RESPONSE_YES if the user clicked Yes,
1197 * GTK_RESPONSE_NO if the user clicked No and
1198 * GTK_RESPONSE_CANCEL otherwise.
1201 e_cal_dialogs_send_dragged_or_resized_component (GtkWindow
*parent
,
1203 ECalComponent
*comp
,
1204 gboolean
*strip_alarms
,
1205 gboolean
*only_new_attendees
)
1207 ECalComponentVType vtype
;
1209 GtkWidget
*dialog
, *sa_checkbox
= NULL
, *ona_checkbox
= NULL
;
1210 GtkWidget
*content_area
;
1211 gboolean save_schedules
= FALSE
;
1212 GtkResponseType res
;
1215 *strip_alarms
= TRUE
;
1217 if (e_cal_client_check_save_schedules (client
))
1218 save_schedules
= TRUE
;
1220 if (!itip_component_has_recipients (comp
))
1221 save_schedules
= TRUE
;
1223 vtype
= e_cal_component_get_vtype (comp
);
1226 case E_CAL_COMPONENT_EVENT
:
1227 id
= save_schedules
?
1228 "calendar:prompt-save-meeting-dragged-or-resized" :
1229 "calendar:prompt-send-updated-meeting-info-dragged-or-resized";
1233 "send_component_dialog(): "
1234 "Cannot handle object of type %d", vtype
);
1235 return GTK_RESPONSE_CANCEL
;
1238 if (only_new_attendees
&& !component_has_new_attendees (comp
)) {
1239 /* do not show the check if there is no new attendee and
1240 * set as all attendees are required to be notified */
1241 *only_new_attendees
= FALSE
;
1243 /* pretend it as being passed NULL to simplify code below */
1244 only_new_attendees
= NULL
;
1247 if (strip_alarms
&& !have_nonprocedural_alarm (comp
)) {
1248 /* pretend it as being passed NULL to simplify code below */
1249 strip_alarms
= NULL
;
1252 dialog
= e_alert_dialog_new_for_args (parent
, id
, NULL
);
1253 content_area
= e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog
));
1256 sa_checkbox
= add_checkbox (GTK_BOX (content_area
), _("Send my reminders with this event"));
1257 if (only_new_attendees
)
1258 ona_checkbox
= add_checkbox (GTK_BOX (content_area
), _("Notify new attendees _only"));
1260 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
1263 * When the Escape key is pressed a GTK_RESPONSE_DELETE_EVENT is generated.
1264 * We should treat this event as the user cancelling the operation
1266 if (res
== GTK_RESPONSE_DELETE_EVENT
)
1267 res
= GTK_RESPONSE_CANCEL
;
1269 if (res
== GTK_RESPONSE_YES
&& strip_alarms
)
1270 *strip_alarms
= !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sa_checkbox
));
1271 if (only_new_attendees
)
1272 *only_new_attendees
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ona_checkbox
));
1274 gtk_widget_destroy (GTK_WIDGET (dialog
));
1280 e_cal_dialogs_send_component_prompt_subject (GtkWindow
*parent
,
1281 icalcomponent
*component
)
1283 icalcomponent_kind kind
;
1286 kind
= icalcomponent_isa (component
);
1289 case ICAL_VEVENT_COMPONENT
:
1290 id
= "calendar:prompt-save-no-subject-calendar";
1293 case ICAL_VTODO_COMPONENT
:
1294 id
= "calendar:prompt-save-no-subject-task";
1296 case ICAL_VJOURNAL_COMPONENT
:
1297 id
= "calendar:prompt-send-no-subject-memo";
1301 g_message ("%s: Cannot handle object of type %d", G_STRFUNC
, kind
);
1305 if (e_alert_run_dialog_for_args (parent
, id
, NULL
) == GTK_RESPONSE_YES
)