Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / e-comp-editor-page-schedule.c
blobdc44ea6df66a59f4b5b88d422610a2f7e3b7442c
1 /*
2 * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-config.h"
20 #include <glib.h>
21 #include <glib/gi18n-lib.h>
22 #include <gtk/gtk.h>
24 #include <e-util/e-util.h>
26 #include "e-comp-editor-page-schedule.h"
28 struct _ECompEditorPageSchedulePrivate {
29 EMeetingStore *store;
30 EMeetingTimeSelector *selector;
33 enum {
34 PROP_0,
35 PROP_STORE
38 G_DEFINE_TYPE (ECompEditorPageSchedule, e_comp_editor_page_schedule, E_TYPE_COMP_EDITOR_PAGE)
40 static void
41 ecep_schedule_get_work_day_range_for (GSettings *settings,
42 gint weekday,
43 gint *start_hour,
44 gint *start_minute,
45 gint *end_hour,
46 gint *end_minute)
48 gint start_adept = -1, end_adept = -1;
49 const gchar *start_key = NULL, *end_key = NULL;
51 g_return_if_fail (G_IS_SETTINGS (settings));
52 g_return_if_fail (start_hour != NULL);
53 g_return_if_fail (start_minute != NULL);
54 g_return_if_fail (end_hour != NULL);
55 g_return_if_fail (end_minute != NULL);
57 switch (weekday) {
58 case G_DATE_MONDAY:
59 start_key = "day-start-mon";
60 end_key = "day-end-mon";
61 break;
62 case G_DATE_TUESDAY:
63 start_key = "day-start-tue";
64 end_key = "day-end-tue";
65 break;
66 case G_DATE_WEDNESDAY:
67 start_key = "day-start-wed";
68 end_key = "day-end-wed";
69 break;
70 case G_DATE_THURSDAY:
71 start_key = "day-start-thu";
72 end_key = "day-end-thu";
73 break;
74 case G_DATE_FRIDAY:
75 start_key = "day-start-fri";
76 end_key = "day-end-fri";
77 break;
78 case G_DATE_SATURDAY:
79 start_key = "day-start-sat";
80 end_key = "day-end-sat";
81 break;
82 case G_DATE_SUNDAY:
83 start_key = "day-start-sun";
84 end_key = "day-end-sun";
85 break;
86 default:
87 break;
90 if (start_key && end_key) {
91 start_adept = g_settings_get_int (settings, start_key);
92 end_adept = g_settings_get_int (settings, end_key);
95 if (start_adept > 0 && (start_adept / 100) >= 0 && (start_adept / 100) <= 23 &&
96 (start_adept % 100) >= 0 && (start_adept % 100) <= 59) {
97 *start_hour = start_adept / 100;
98 *start_minute = start_adept % 100;
99 } else {
100 *start_hour = g_settings_get_int (settings, "day-start-hour");
101 *start_minute = g_settings_get_int (settings, "day-start-minute");
104 if (end_adept > 0 && (end_adept / 100) >= 0 && (end_adept / 100) <= 23 &&
105 (end_adept % 100) >= 0 && (end_adept % 100) <= 59) {
106 *end_hour = end_adept / 100;
107 *end_minute = end_adept % 100;
108 } else {
109 *end_hour = g_settings_get_int (settings, "day-end-hour");
110 *end_minute = g_settings_get_int (settings, "day-end-minute");
114 static void
115 ecep_schedule_editor_times_changed_cb (ECompEditor *comp_editor,
116 ECompEditorPageSchedule *page_schedule)
118 ECompEditorPropertyPartDatetime *dtstart, *dtend;
119 ECompEditorPropertyPart *dtstart_part = NULL, *dtend_part = NULL;
120 EDateEdit *start_date_edit, *end_date_edit;
121 struct icaltimetype start_tt, end_tt;
123 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
124 g_return_if_fail (page_schedule->priv->selector != NULL);
126 e_comp_editor_get_time_parts (comp_editor, &dtstart_part, &dtend_part);
128 if (!dtstart_part || !dtend_part)
129 return;
131 dtstart = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part);
132 dtend = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part);
134 start_tt = e_comp_editor_property_part_datetime_get_value (dtstart);
135 end_tt = e_comp_editor_property_part_datetime_get_value (dtend);
137 /* For All Day Events, if DTEND is after DTSTART, we subtract 1 day from it. */
138 if (start_tt.is_date && end_tt.is_date &&
139 icaltime_compare_date_only (end_tt, start_tt) > 0)
140 icaltime_adjust (&end_tt, -1, 0, 0, 0);
142 e_comp_editor_page_set_updating (E_COMP_EDITOR_PAGE (page_schedule), TRUE);
144 start_date_edit = E_DATE_EDIT (page_schedule->priv->selector->start_date_edit);
145 end_date_edit = E_DATE_EDIT (page_schedule->priv->selector->end_date_edit);
147 e_date_edit_set_date (start_date_edit, start_tt.year, start_tt.month, start_tt.day);
148 e_date_edit_set_time_of_day (start_date_edit, start_tt.hour, start_tt.minute);
150 e_date_edit_set_date (end_date_edit, end_tt.year, end_tt.month, end_tt.day);
151 e_date_edit_set_time_of_day (end_date_edit, end_tt.hour, end_tt.minute);
153 e_comp_editor_page_set_updating (E_COMP_EDITOR_PAGE (page_schedule), FALSE);
156 static void
157 ecep_schedule_editor_target_client_notify_cb (GObject *comp_editor,
158 GParamSpec *param,
159 gpointer user_data)
161 ECompEditorPageSchedule *page_schedule = user_data;
162 ECalClient *target_client;
164 g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
165 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
166 g_return_if_fail (page_schedule->priv->store != NULL);
167 g_return_if_fail (page_schedule->priv->selector != NULL);
169 target_client = e_comp_editor_get_target_client (E_COMP_EDITOR (comp_editor));
170 e_meeting_store_set_client (page_schedule->priv->store, target_client);
171 e_meeting_time_selector_refresh_free_busy (page_schedule->priv->selector, -1, TRUE);
174 static void
175 ecep_schedule_set_time_to_editor (ECompEditorPageSchedule *page_schedule)
177 EMeetingTimeSelector *selector;
178 ECompEditorPropertyPartDatetime *dtstart, *dtend;
179 ECompEditorPropertyPart *dtstart_part = NULL, *dtend_part = NULL;
180 ECompEditor *comp_editor;
181 struct icaltimetype start_tt, end_tt;
183 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
184 g_return_if_fail (E_IS_MEETING_TIME_SELECTOR (page_schedule->priv->selector));
186 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
187 if (comp_editor)
188 e_comp_editor_get_time_parts (comp_editor, &dtstart_part, &dtend_part);
190 if (!dtstart_part || !dtend_part) {
191 g_clear_object (&comp_editor);
192 return;
195 selector = page_schedule->priv->selector;
196 dtstart = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part);
197 dtend = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part);
199 start_tt = e_comp_editor_property_part_datetime_get_value (dtstart);
200 end_tt = e_comp_editor_property_part_datetime_get_value (dtend);
202 e_date_edit_get_date (
203 E_DATE_EDIT (selector->start_date_edit),
204 &start_tt.year,
205 &start_tt.month,
206 &start_tt.day);
207 e_date_edit_get_time_of_day (
208 E_DATE_EDIT (selector->start_date_edit),
209 &start_tt.hour,
210 &start_tt.minute);
211 e_date_edit_get_date (
212 E_DATE_EDIT (selector->end_date_edit),
213 &end_tt.year,
214 &end_tt.month,
215 &end_tt.day);
216 e_date_edit_get_time_of_day (
217 E_DATE_EDIT (selector->end_date_edit),
218 &end_tt.hour,
219 &end_tt.minute);
221 if (!e_date_edit_get_show_time (E_DATE_EDIT (selector->start_date_edit))) {
222 /* For All-Day Events, we set the timezone to NULL, and add 1 day to DTEND. */
223 start_tt.is_date = TRUE;
224 start_tt.zone = NULL;
225 end_tt.is_date = TRUE;
226 end_tt.zone = NULL;
228 icaltime_adjust (&end_tt, 1, 0, 0, 0);
229 } else {
230 start_tt.is_date = FALSE;
231 end_tt.is_date = FALSE;
234 e_comp_editor_property_part_datetime_set_value (dtstart, start_tt);
235 e_comp_editor_property_part_datetime_set_value (dtend, end_tt);
237 g_clear_object (&comp_editor);
240 static void
241 ecep_schedule_selector_changed_cb (EMeetingTimeSelector *selector,
242 ECompEditorPageSchedule *page_schedule)
244 ECompEditorPage *page;
246 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
247 g_return_if_fail (page_schedule->priv->selector == selector);
249 page = E_COMP_EDITOR_PAGE (page_schedule);
251 if (e_comp_editor_page_get_updating (page))
252 return;
254 e_comp_editor_page_set_updating (page, TRUE);
256 ecep_schedule_set_time_to_editor (page_schedule);
258 e_comp_editor_page_set_updating (page, FALSE);
259 e_comp_editor_page_emit_changed (page);
262 static void
263 ecep_schedule_sensitize_widgets (ECompEditorPage *page,
264 gboolean force_insensitive)
266 ECompEditorPageSchedule *page_schedule;
268 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page));
270 E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_schedule_parent_class)->sensitize_widgets (page, force_insensitive);
272 page_schedule = E_COMP_EDITOR_PAGE_SCHEDULE (page);
274 e_meeting_time_selector_set_read_only (page_schedule->priv->selector, force_insensitive);
277 static void
278 ecep_schedule_fill_widgets (ECompEditorPage *page,
279 icalcomponent *component)
281 ECompEditorPageSchedule *page_schedule;
282 ECompEditorPropertyPartDatetime *dtstart, *dtend;
283 ECompEditorPropertyPart *dtstart_part = NULL, *dtend_part = NULL;
284 ECompEditor *comp_editor;
285 EMeetingTimeSelector *selector;
286 struct icaltimetype start_tt, end_tt;
288 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page));
289 g_return_if_fail (component != NULL);
291 E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_schedule_parent_class)->fill_widgets (page, component);
293 page_schedule = E_COMP_EDITOR_PAGE_SCHEDULE (page);
295 /* dtstart/dtend parts should be already populated, thus
296 get values from them, instead of from the component */
298 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
299 g_return_if_fail (E_IS_MEETING_TIME_SELECTOR (page_schedule->priv->selector));
301 comp_editor = e_comp_editor_page_ref_editor (page);
302 if (comp_editor)
303 e_comp_editor_get_time_parts (comp_editor, &dtstart_part, &dtend_part);
305 if (!dtstart_part || !dtend_part) {
306 g_clear_object (&comp_editor);
307 return;
310 selector = page_schedule->priv->selector;
311 dtstart = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part);
312 dtend = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part);
314 start_tt = e_comp_editor_property_part_datetime_get_value (dtstart);
315 end_tt = e_comp_editor_property_part_datetime_get_value (dtend);
317 if (start_tt.is_date) {
318 /* For All-Day Events, we set the timezone to NULL, and add 1 day to DTEND. */
319 start_tt.is_date = TRUE;
320 start_tt.zone = NULL;
321 end_tt.is_date = TRUE;
322 end_tt.zone = NULL;
324 icaltime_adjust (&end_tt, 1, 0, 0, 0);
325 } else {
326 start_tt.is_date = FALSE;
327 end_tt.is_date = FALSE;
330 e_comp_editor_page_set_updating (page, TRUE);
332 e_date_edit_set_date (
333 E_DATE_EDIT (selector->start_date_edit),
334 start_tt.year,
335 start_tt.month,
336 start_tt.day);
337 e_date_edit_set_time_of_day (
338 E_DATE_EDIT (selector->start_date_edit),
339 start_tt.hour,
340 start_tt.minute);
341 e_date_edit_set_date (
342 E_DATE_EDIT (selector->end_date_edit),
343 end_tt.year,
344 end_tt.month,
345 end_tt.day);
346 e_date_edit_set_time_of_day (
347 E_DATE_EDIT (selector->end_date_edit),
348 end_tt.hour,
349 end_tt.minute);
351 e_comp_editor_page_set_updating (page, FALSE);
353 g_clear_object (&comp_editor);
356 static gboolean
357 ecep_schedule_fill_component (ECompEditorPage *page,
358 icalcomponent *component)
360 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page), FALSE);
361 g_return_val_if_fail (component != NULL, FALSE);
363 return E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_schedule_parent_class)->fill_component (page, component);
366 static void
367 e_comp_editor_page_schedule_set_store (ECompEditorPageSchedule *page_schedule,
368 EMeetingStore *store)
370 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
371 g_return_if_fail (E_IS_MEETING_STORE (store));
372 g_return_if_fail (page_schedule->priv->store == NULL);
374 page_schedule->priv->store = g_object_ref (store);
377 static void
378 e_comp_editor_page_schedule_set_property (GObject *object,
379 guint property_id,
380 const GValue *value,
381 GParamSpec *pspec)
383 switch (property_id) {
384 case PROP_STORE:
385 e_comp_editor_page_schedule_set_store (
386 E_COMP_EDITOR_PAGE_SCHEDULE (object),
387 g_value_get_object (value));
388 return;
391 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
394 static void
395 e_comp_editor_page_schedule_get_property (GObject *object,
396 guint property_id,
397 GValue *value,
398 GParamSpec *pspec)
400 switch (property_id) {
401 case PROP_STORE:
402 g_value_set_object (
403 value,
404 e_comp_editor_page_schedule_get_store (
405 E_COMP_EDITOR_PAGE_SCHEDULE (object)));
406 return;
409 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
412 static void
413 ecep_schedule_select_page_cb (GtkAction *action,
414 ECompEditorPage *page)
416 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page));
418 e_comp_editor_page_select (page);
421 static void
422 ecep_schedule_setup_ui (ECompEditorPageSchedule *page_schedule)
424 const gchar *ui =
425 "<ui>"
426 " <menubar action='main-menu'>"
427 " <menu action='options-menu'>"
428 " <placeholder name='tabs'>"
429 " <menuitem action='page-schedule'/>"
430 " </placeholder>"
431 " </menu>"
432 " </menubar>"
433 " <toolbar name='main-toolbar'>"
434 " <placeholder name='after-content'>\n"
435 " <toolitem action='page-schedule'/>\n"
436 " </placeholder>"
437 " </toolbar>"
438 "</ui>";
440 const GtkActionEntry options_actions[] = {
441 { "page-schedule",
442 "query-free-busy",
443 N_("_Schedule"),
444 NULL,
445 N_("Query free / busy information for the attendees"),
446 G_CALLBACK (ecep_schedule_select_page_cb) }
449 ECompEditor *comp_editor;
450 GtkUIManager *ui_manager;
451 GtkAction *action;
452 GtkActionGroup *action_group;
453 GError *error = NULL;
455 g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
457 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
458 ui_manager = e_comp_editor_get_ui_manager (comp_editor);
459 action_group = e_comp_editor_get_action_group (comp_editor, "individual");
461 gtk_action_group_add_actions (action_group,
462 options_actions, G_N_ELEMENTS (options_actions), page_schedule);
464 gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
466 if (error) {
467 g_critical ("%s: %s", G_STRFUNC, error->message);
468 g_error_free (error);
471 action = e_comp_editor_get_action (comp_editor, "page-schedule");
472 e_binding_bind_property (
473 page_schedule, "visible",
474 action, "visible",
475 G_BINDING_SYNC_CREATE);
477 g_clear_object (&comp_editor);
480 static void
481 e_comp_editor_page_schedule_constructed (GObject *object)
483 ECompEditorPageSchedule *page_schedule;
484 ECompEditor *comp_editor;
485 GSettings *settings;
486 GtkWidget *widget;
487 gint weekday;
489 G_OBJECT_CLASS (e_comp_editor_page_schedule_parent_class)->constructed (object);
491 page_schedule = E_COMP_EDITOR_PAGE_SCHEDULE (object);
493 g_return_if_fail (page_schedule->priv->store != NULL);
495 widget = e_meeting_time_selector_new (page_schedule->priv->store);
496 g_object_set (G_OBJECT (widget),
497 "hexpand", TRUE,
498 "halign", GTK_ALIGN_FILL,
499 "vexpand", TRUE,
500 "valign", GTK_ALIGN_FILL,
501 NULL);
502 gtk_widget_show (widget);
503 gtk_grid_attach (GTK_GRID (page_schedule), widget, 0, 0, 1, 1);
505 page_schedule->priv->selector = E_MEETING_TIME_SELECTOR (widget);
507 settings = e_util_ref_settings ("org.gnome.evolution.calendar");
509 for (weekday = G_DATE_BAD_WEEKDAY; weekday <= G_DATE_SUNDAY; weekday++) {
510 gint start_hour = 8, start_minute = 0, end_hour = 17, end_minute = 0;
512 ecep_schedule_get_work_day_range_for (settings, weekday,
513 &start_hour, &start_minute, &end_hour, &end_minute);
515 e_meeting_time_selector_set_working_hours (page_schedule->priv->selector,
516 weekday, start_hour, start_minute, end_hour, end_minute);
519 g_clear_object (&settings);
521 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
522 if (comp_editor) {
523 g_signal_connect (comp_editor, "times-changed",
524 G_CALLBACK (ecep_schedule_editor_times_changed_cb), page_schedule);
526 g_signal_connect (comp_editor, "notify::target-client",
527 G_CALLBACK (ecep_schedule_editor_target_client_notify_cb), page_schedule);
530 g_clear_object (&comp_editor);
532 g_signal_connect (page_schedule->priv->selector, "changed",
533 G_CALLBACK (ecep_schedule_selector_changed_cb), page_schedule);
535 ecep_schedule_setup_ui (page_schedule);
538 static void
539 e_comp_editor_page_schedule_dispose (GObject *object)
541 ECompEditorPageSchedule *page_schedule;
542 ECompEditor *comp_editor;
544 page_schedule = E_COMP_EDITOR_PAGE_SCHEDULE (object);
546 comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
547 if (comp_editor) {
548 g_signal_handlers_disconnect_by_func (comp_editor,
549 G_CALLBACK (ecep_schedule_editor_times_changed_cb), page_schedule);
550 g_clear_object (&comp_editor);
553 g_clear_object (&page_schedule->priv->store);
555 G_OBJECT_CLASS (e_comp_editor_page_schedule_parent_class)->dispose (object);
558 static void
559 e_comp_editor_page_schedule_init (ECompEditorPageSchedule *page_schedule)
561 page_schedule->priv = G_TYPE_INSTANCE_GET_PRIVATE (page_schedule,
562 E_TYPE_COMP_EDITOR_PAGE_SCHEDULE,
563 ECompEditorPageSchedulePrivate);
566 static void
567 e_comp_editor_page_schedule_class_init (ECompEditorPageScheduleClass *klass)
569 ECompEditorPageClass *page_class;
570 GObjectClass *object_class;
572 g_type_class_add_private (klass, sizeof (ECompEditorPageSchedulePrivate));
574 page_class = E_COMP_EDITOR_PAGE_CLASS (klass);
575 page_class->sensitize_widgets = ecep_schedule_sensitize_widgets;
576 page_class->fill_widgets = ecep_schedule_fill_widgets;
577 page_class->fill_component = ecep_schedule_fill_component;
579 object_class = G_OBJECT_CLASS (klass);
580 object_class->set_property = e_comp_editor_page_schedule_set_property;
581 object_class->get_property = e_comp_editor_page_schedule_get_property;
582 object_class->constructed = e_comp_editor_page_schedule_constructed;
583 object_class->dispose = e_comp_editor_page_schedule_dispose;
585 g_object_class_install_property (
586 object_class,
587 PROP_STORE,
588 g_param_spec_object (
589 "store",
590 "store",
591 "an EMeetingStore",
592 E_TYPE_MEETING_STORE,
593 G_PARAM_READWRITE |
594 G_PARAM_CONSTRUCT_ONLY |
595 G_PARAM_STATIC_STRINGS));
598 ECompEditorPage *
599 e_comp_editor_page_schedule_new (ECompEditor *editor,
600 EMeetingStore *meeting_store)
602 g_return_val_if_fail (E_IS_COMP_EDITOR (editor), NULL);
604 return g_object_new (E_TYPE_COMP_EDITOR_PAGE_SCHEDULE,
605 "editor", editor,
606 "store", meeting_store,
607 NULL);
610 EMeetingStore *
611 e_comp_editor_page_schedule_get_store (ECompEditorPageSchedule *page_schedule)
613 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule), NULL);
615 return page_schedule->priv->store;
618 EMeetingTimeSelector *
619 e_comp_editor_page_schedule_get_time_selector (ECompEditorPageSchedule *page_schedule)
621 g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule), NULL);
623 return page_schedule->priv->selector;