evolution-3.6.4/calendar/gui/dialogs/task-details-page.c

No issues found

  1 /*
  2  * Evolution calendar - task details page
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  *
 18  * Authors:
 19  *		Federico Mena-Quintero <federico@ximian.com>
 20  *      Miguel de Icaza <miguel@ximian.com>
 21  *      Seth Alves <alves@hungry.com>
 22  *      JP Rosevear <jpr@ximian.com>
 23  *
 24  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 25  *
 26  */
 27 
 28 #ifdef HAVE_CONFIG_H
 29 #include <config.h>
 30 #endif
 31 
 32 #include <gtk/gtk.h>
 33 #include <glib/gi18n.h>
 34 #include <misc/e-dateedit.h>
 35 #include <misc/e-url-entry.h>
 36 #include "../e-timezone-entry.h"
 37 #include "comp-editor-util.h"
 38 #include "task-details-page.h"
 39 
 40 #include "e-util/e-util.h"
 41 #include "e-util/e-dialog-widgets.h"
 42 #include "e-util/e-util-private.h"
 43 
 44 #define TASK_DETAILS_PAGE_GET_PRIVATE(obj) \
 45 	(G_TYPE_INSTANCE_GET_PRIVATE \
 46 	((obj), TYPE_TASK_DETAILS_PAGE, TaskDetailsPagePrivate))
 47 
 48 struct _TaskDetailsPagePrivate {
 49 	GtkBuilder *builder;
 50 
 51 	/* Widgets from the UI file */
 52 	GtkWidget *main;
 53 
 54 	GtkWidget *status_combo;
 55 	GtkWidget *priority_combo;
 56 	GtkWidget *percent_complete;
 57 
 58 	GtkWidget *date_completed_label;
 59 	GtkWidget *completed_date;
 60 
 61 	GtkWidget *url_label;
 62 	GtkWidget *url_entry;
 63 	GtkWidget *url;
 64 };
 65 
 66 /* Note that these two arrays must match. */
 67 static const gint status_map[] = {
 68 	ICAL_STATUS_NONE,
 69 	ICAL_STATUS_INPROCESS,
 70 	ICAL_STATUS_COMPLETED,
 71 	ICAL_STATUS_CANCELLED,
 72 	-1
 73 };
 74 
 75 typedef enum {
 76 	PRIORITY_HIGH,
 77 	PRIORITY_NORMAL,
 78 	PRIORITY_LOW,
 79 	PRIORITY_UNDEFINED
 80 } TaskEditorPriority;
 81 
 82 static const gint priority_map[] = {
 83 	PRIORITY_HIGH,
 84 	PRIORITY_NORMAL,
 85 	PRIORITY_LOW,
 86 	PRIORITY_UNDEFINED,
 87 	-1
 88 };
 89 
 90 G_DEFINE_TYPE (TaskDetailsPage, task_details_page, TYPE_COMP_EDITOR_PAGE)
 91 
 92 static TaskEditorPriority
 93 priority_value_to_index (gint priority_value)
 94 {
 95 	TaskEditorPriority retval;
 96 
 97 	if (priority_value == 0)
 98 		retval = PRIORITY_UNDEFINED;
 99 	else if (priority_value <= 4)
100 		retval = PRIORITY_HIGH;
101 	else if (priority_value == 5)
102 		retval = PRIORITY_NORMAL;
103 	else
104 		retval = PRIORITY_LOW;
105 
106 	return retval;
107 }
108 
109 static gint
110 priority_index_to_value (TaskEditorPriority priority)
111 {
112 	gint retval;
113 
114 	switch (priority) {
115 	case PRIORITY_UNDEFINED:
116 		retval = 0;
117 		break;
118 	case PRIORITY_HIGH:
119 		retval = 3;
120 		break;
121 	case PRIORITY_NORMAL:
122 		retval = 5;
123 		break;
124 	case PRIORITY_LOW:
125 		retval = 7;
126 		break;
127 	default:
128 		retval = 0;
129 		break;
130 	}
131 
132 	return retval;
133 }
134 
135 /* Fills the widgets with default values */
136 static void
137 clear_widgets (TaskDetailsPage *tdpage)
138 {
139 	TaskDetailsPagePrivate *priv;
140 
141 	priv = tdpage->priv;
142 
143 	/* Date completed */
144 	e_date_edit_set_time (E_DATE_EDIT (priv->completed_date), -1);
145 
146 	/* URL */
147 	gtk_entry_set_text (GTK_ENTRY (priv->url), "");
148 }
149 
150 static void
151 sensitize_widgets (TaskDetailsPage *tdpage)
152 {
153 	TaskDetailsPagePrivate *priv = tdpage->priv;
154 	CompEditor *editor;
155 	GtkWidget *entry;
156 	ECalClient *client;
157 	gboolean read_only;
158 
159 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tdpage));
160 	client = comp_editor_get_client (editor);
161 
162 	read_only = e_client_is_readonly (E_CLIENT (client));
163 
164 	gtk_widget_set_sensitive (priv->status_combo, !read_only);
165 	gtk_widget_set_sensitive (priv->priority_combo, !read_only);
166 	gtk_widget_set_sensitive (priv->percent_complete, !read_only);
167 	gtk_widget_set_sensitive (priv->completed_date, !read_only);
168 	gtk_widget_set_sensitive (priv->url_label, !read_only);
169 
170 	entry = e_url_entry_get_entry (E_URL_ENTRY (priv->url_entry));
171 	gtk_editable_set_editable (GTK_EDITABLE (entry), !read_only);
172 }
173 
174 static void
175 task_details_page_dispose (GObject *object)
176 {
177 	TaskDetailsPagePrivate *priv;
178 
179 	priv = TASK_DETAILS_PAGE_GET_PRIVATE (object);
180 
181 	if (priv->main != NULL) {
182 		g_object_unref (priv->main);
183 		priv->main = NULL;
184 	}
185 
186 	if (priv->builder != NULL) {
187 		g_object_unref (priv->builder);
188 		priv->builder = NULL;
189 	}
190 
191 	/* Chain up to parent's dispose() method. */
192 	G_OBJECT_CLASS (task_details_page_parent_class)->dispose (object);
193 }
194 
195 static GtkWidget *
196 task_details_page_get_widget (CompEditorPage *page)
197 {
198 	TaskDetailsPage *tdpage;
199 	TaskDetailsPagePrivate *priv;
200 
201 	tdpage = TASK_DETAILS_PAGE (page);
202 	priv = tdpage->priv;
203 
204 	return priv->main;
205 }
206 
207 static void
208 task_details_page_focus_main_widget (CompEditorPage *page)
209 {
210 	TaskDetailsPage *tdpage;
211 	TaskDetailsPagePrivate *priv;
212 
213 	tdpage = TASK_DETAILS_PAGE (page);
214 	priv = tdpage->priv;
215 
216 	gtk_widget_grab_focus (priv->status_combo);
217 }
218 
219 static gboolean
220 task_details_page_fill_widgets (CompEditorPage *page,
221                                 ECalComponent *comp)
222 {
223 	TaskDetailsPage *tdpage;
224 	TaskDetailsPagePrivate *priv;
225 	gint *priority_value, *percent = NULL;
226 	TaskEditorPriority priority;
227 	icalproperty_status status;
228 	CompEditor *editor;
229 	const gchar *url;
230 	struct icaltimetype *completed = NULL;
231 
232 	tdpage = TASK_DETAILS_PAGE (page);
233 	priv = tdpage->priv;
234 
235 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tdpage));
236 
237 	/* Clean the screen */
238 	clear_widgets (tdpage);
239 
240 	/* Percent Complete. */
241 	e_cal_component_get_percent (comp, &percent);
242 	if (percent) {
243 		gtk_spin_button_set_value (
244 			GTK_SPIN_BUTTON (priv->percent_complete), *percent);
245 	} else {
246 		/* FIXME: Could check if task is completed and set 100%. */
247 		gtk_spin_button_set_value (
248 			GTK_SPIN_BUTTON (priv->percent_complete), 0);
249 	}
250 
251 	/* Status. */
252 	e_cal_component_get_status (comp, &status);
253 	if (status == ICAL_STATUS_NONE || status == ICAL_STATUS_NEEDSACTION) {
254 		/* Try to use the percent value. */
255 		if (percent) {
256 			if (*percent == 100)
257 				status = ICAL_STATUS_COMPLETED;
258 			else if (*percent > 0)
259 				status = ICAL_STATUS_INPROCESS;
260 			else
261 				status = ICAL_STATUS_NONE;
262 		} else
263 			status = ICAL_STATUS_NONE;
264 	}
265 	e_dialog_combo_box_set (priv->status_combo, status, status_map);
266 
267 	if (percent)
268 		e_cal_component_free_percent (percent);
269 
270 	/* Completed Date. */
271 	e_cal_component_get_completed (comp, &completed);
272 	if (completed) {
273 		icaltimezone *utc_zone, *zone;
274 
275 		/* Completed is in UTC, but that would confuse the user, so
276 		 * we convert it to local time. */
277 		utc_zone = icaltimezone_get_utc_timezone ();
278 		zone = comp_editor_get_timezone (editor);
279 
280 		icaltimezone_convert_time (completed, utc_zone, zone);
281 
282 		e_date_edit_set_date (
283 			E_DATE_EDIT (priv->completed_date),
284 			completed->year, completed->month,
285 			completed->day);
286 		e_date_edit_set_time_of_day (
287 			E_DATE_EDIT (priv->completed_date),
288 			completed->hour,
289 			completed->minute);
290 
291 		e_cal_component_free_icaltimetype (completed);
292 	}
293 
294 	/* Priority. */
295 	e_cal_component_get_priority (comp, &priority_value);
296 	if (priority_value) {
297 		priority = priority_value_to_index (*priority_value);
298 		e_cal_component_free_priority (priority_value);
299 	} else {
300 		priority = PRIORITY_UNDEFINED;
301 	}
302 	e_dialog_combo_box_set (priv->priority_combo, priority, priority_map);
303 
304 	/* URL */
305 	e_cal_component_get_url (comp, &url);
306 	gtk_entry_set_text (GTK_ENTRY (priv->url), url ? url : "");
307 
308 	sensitize_widgets (tdpage);
309 
310 	return TRUE;
311 }
312 
313 static gboolean
314 task_details_page_fill_component (CompEditorPage *page,
315                                   ECalComponent *comp)
316 {
317 	TaskDetailsPage *tdpage;
318 	TaskDetailsPagePrivate *priv;
319 	struct icaltimetype icalcomplete, icaltoday;
320 	icalproperty_status status;
321 	TaskEditorPriority priority;
322 	CompEditor *editor;
323 	gint priority_value, percent;
324 	const gchar *text;
325 	gboolean date_set;
326 	icaltimezone *zone;
327 
328 	tdpage = TASK_DETAILS_PAGE (page);
329 	priv = tdpage->priv;
330 
331 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tdpage));
332 	zone = comp_editor_get_timezone (editor);
333 
334 	/* Percent Complete. */
335 	percent = gtk_spin_button_get_value_as_int (
336 		GTK_SPIN_BUTTON (priv->percent_complete));
337 	e_cal_component_set_percent (comp, &percent);
338 
339 	/* Status. */
340 	status = e_dialog_combo_box_get (priv->status_combo, status_map);
341 	e_cal_component_set_status (comp, status);
342 
343 	/* Priority. */
344 	priority = e_dialog_combo_box_get (priv->priority_combo, priority_map);
345 	priority_value = priority_index_to_value (priority);
346 	e_cal_component_set_priority (comp, &priority_value);
347 
348 	icalcomplete = icaltime_null_time ();
349 
350 	/* COMPLETED must be in UTC. */
351 	icalcomplete.is_utc = 1;
352 
353 	/* Completed Date. */
354 	if (!e_date_edit_date_is_valid (E_DATE_EDIT (priv->completed_date)) ||
355 	    !e_date_edit_time_is_valid (E_DATE_EDIT (priv->completed_date))) {
356 		comp_editor_page_display_validation_error (
357 			page, _("Completed date is wrong"),
358 			priv->completed_date);
359 		return FALSE;
360 	}
361 
362 	date_set = e_date_edit_get_date (
363 		E_DATE_EDIT (priv->completed_date),
364 		&icalcomplete.year,
365 		&icalcomplete.month,
366 		&icalcomplete.day);
367 
368 	if (date_set) {
369 		e_date_edit_get_time_of_day (
370 			E_DATE_EDIT (priv->completed_date),
371 			&icalcomplete.hour,
372 			&icalcomplete.minute);
373 
374 		/* COMPLETED today or before */
375 		icaltoday = icaltime_current_time_with_zone (zone);
376 		icaltimezone_convert_time (
377 			&icaltoday, zone,
378 			icaltimezone_get_utc_timezone ());
379 
380 		if (icaltime_compare_date_only (icalcomplete, icaltoday) > 0) {
381 			comp_editor_page_display_validation_error (
382 				page, _("Completed date is wrong"),
383 				priv->completed_date);
384 			return FALSE;
385 		}
386 
387 		/* COMPLETED must be in UTC, so we assume that the date in the
388 		 * dialog is in the current timezone, and we now convert it
389 		 * to UTC. FIXME: We should really use one timezone for the
390 		 * entire time the dialog is shown. Otherwise if the user
391 		 * changes the timezone, the COMPLETED date may get changed
392 		 * as well. */
393 		icaltimezone_convert_time (
394 			&icalcomplete, zone,
395 				icaltimezone_get_utc_timezone ());
396 		e_cal_component_set_completed (comp, &icalcomplete);
397 	} else {
398 		e_cal_component_set_completed (comp, NULL);
399 	}
400 
401 	/* URL. */
402 	text = gtk_entry_get_text (GTK_ENTRY (priv->url));
403 	e_cal_component_set_url (comp, text);
404 
405 	return TRUE;
406 }
407 
408 static gboolean
409 task_details_page_fill_timezones (CompEditorPage *page,
410                                   GHashTable *timezones)
411 {
412 	icaltimezone *zone;
413 
414 	/* Add UTC timezone, which is the one
415 	 * used for the DATE-COMPLETED property. */
416 	zone = icaltimezone_get_utc_timezone ();
417 	if (zone != NULL) {
418 		gconstpointer tzid = icaltimezone_get_tzid (zone);
419 
420 		if (!g_hash_table_lookup (timezones, tzid))
421 			g_hash_table_insert (timezones, (gpointer) tzid, zone);
422 	}
423 
424 	return TRUE;
425 }
426 
427 static void
428 task_details_page_class_init (TaskDetailsPageClass *class)
429 {
430 	GObjectClass *object_class;
431 	CompEditorPageClass *editor_page_class;
432 
433 	g_type_class_add_private (class, sizeof (TaskDetailsPagePrivate));
434 
435 	object_class = G_OBJECT_CLASS (class);
436 	object_class->dispose = task_details_page_dispose;
437 
438 	editor_page_class = COMP_EDITOR_PAGE_CLASS (class);
439 	editor_page_class->get_widget = task_details_page_get_widget;
440 	editor_page_class->focus_main_widget = task_details_page_focus_main_widget;
441 	editor_page_class->fill_widgets = task_details_page_fill_widgets;
442 	editor_page_class->fill_component = task_details_page_fill_component;
443 	editor_page_class->fill_timezones = task_details_page_fill_timezones;
444 }
445 
446 static void
447 task_details_page_init (TaskDetailsPage *tdpage)
448 {
449 	tdpage->priv = TASK_DETAILS_PAGE_GET_PRIVATE (tdpage);
450 }
451 
452 /* Gets the widgets from the XML file and returns if they are all available. */
453 static gboolean
454 get_widgets (TaskDetailsPage *tdpage)
455 {
456 	CompEditorPage *page = COMP_EDITOR_PAGE (tdpage);
457 	TaskDetailsPagePrivate *priv;
458 	GSList *accel_groups;
459 	GtkWidget *toplevel;
460 	GtkWidget *parent;
461 
462 	priv = tdpage->priv;
463 
464 #define GW(name) e_builder_get_widget (priv->builder, name)
465 
466 	priv->main = GW ("task-details-page");
467 	if (!priv->main)
468 		return FALSE;
469 
470 	/* Get the GtkAccelGroup from the toplevel window, so we can install
471 	 * it when the notebook page is mapped. */
472 	toplevel = gtk_widget_get_toplevel (priv->main);
473 	accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel));
474 	if (accel_groups)
475 		page->accel_group = g_object_ref (accel_groups->data);
476 
477 	g_object_ref (priv->main);
478 	parent = gtk_widget_get_parent (priv->main);
479 	gtk_container_remove (GTK_CONTAINER (parent), priv->main);
480 
481 	priv->status_combo = GW ("status-combobox");
482 	priv->priority_combo = GW ("priority-combobox");
483 	priv->percent_complete = GW ("percent-complete");
484 
485 	priv->date_completed_label = GW ("date_completed_label");
486 
487 	priv->completed_date = GW ("completed-date");
488 	gtk_widget_show (priv->completed_date);
489 
490 	priv->url_label = GW ("url_label");
491 
492 	priv->url_entry = GW ("url_entry");
493 	gtk_widget_show (priv->url_entry);
494 	priv->url = e_url_entry_get_entry (E_URL_ENTRY (priv->url_entry));
495 	atk_object_set_name (gtk_widget_get_accessible (priv->url), _("Web Page"));
496 
497 #undef GW
498 
499 	return (priv->status_combo
500 		&& priv->priority_combo
501 		&& priv->percent_complete
502 		&& priv->date_completed_label
503 		&& priv->completed_date
504 		&& priv->url_label
505 		&& priv->url);
506 }
507 
508 static void
509 complete_date_changed (TaskDetailsPage *tdpage,
510                        time_t ctime,
511                        gboolean complete)
512 {
513 	CompEditorPageDates dates = {NULL, NULL, NULL, NULL};
514 	icaltimezone *zone;
515 	struct icaltimetype completed_tt = icaltime_null_time ();
516 
517 	/* Get the current time in UTC. */
518 	zone = icaltimezone_get_utc_timezone ();
519 	completed_tt = icaltime_from_timet_with_zone (ctime, FALSE, zone);
520 	completed_tt.is_utc = TRUE;
521 
522 	dates.start = NULL;
523 	dates.end = NULL;
524 	dates.due = NULL;
525 	if (complete)
526 		dates.complete = &completed_tt;
527 
528 	/* Notify upstream */
529 	comp_editor_page_notify_dates_changed (COMP_EDITOR_PAGE (tdpage),
530 					       &dates);
531 }
532 
533 static void
534 date_changed_cb (EDateEdit *dedit,
535                  TaskDetailsPage *tdpage)
536 {
537 	TaskDetailsPagePrivate *priv = tdpage->priv;
538 	CompEditorPageDates dates = {NULL, NULL, NULL, NULL};
539 	struct icaltimetype completed_tt = icaltime_null_time ();
540 	icalproperty_status status;
541 	gboolean date_set;
542 
543 	if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tdpage)))
544 		return;
545 
546 	comp_editor_page_set_updating (COMP_EDITOR_PAGE (tdpage), TRUE);
547 
548 	date_set = e_date_edit_get_date (
549 		E_DATE_EDIT (priv->completed_date),
550 		&completed_tt.year,
551 		&completed_tt.month,
552 		&completed_tt.day);
553 	e_date_edit_get_time_of_day (
554 		E_DATE_EDIT (priv->completed_date),
555 		&completed_tt.hour,
556 		&completed_tt.minute);
557 
558 	status = e_dialog_combo_box_get (priv->status_combo, status_map);
559 
560 	if (!date_set) {
561 		completed_tt = icaltime_null_time ();
562 		if (status == ICAL_STATUS_COMPLETED) {
563 			e_dialog_combo_box_set (
564 				priv->status_combo,
565 				ICAL_STATUS_NONE,
566 				status_map);
567 			gtk_spin_button_set_value (
568 				GTK_SPIN_BUTTON (priv->percent_complete), 0);
569 		}
570 	} else {
571 		if (status != ICAL_STATUS_COMPLETED) {
572 			e_dialog_combo_box_set (
573 				priv->status_combo,
574 				ICAL_STATUS_COMPLETED,
575 				status_map);
576 		}
577 		gtk_spin_button_set_value (
578 			GTK_SPIN_BUTTON (priv->percent_complete), 100);
579 	}
580 
581 	comp_editor_page_set_updating (COMP_EDITOR_PAGE (tdpage), FALSE);
582 
583 	/* Notify upstream */
584 	dates.complete = &completed_tt;
585 	comp_editor_page_notify_dates_changed (COMP_EDITOR_PAGE (tdpage), &dates);
586 }
587 
588 static void
589 status_changed (GtkWidget *combo,
590                 TaskDetailsPage *tdpage)
591 {
592 	TaskDetailsPagePrivate *priv;
593 	icalproperty_status status;
594 	CompEditor *editor;
595 	time_t ctime = -1;
596 
597 	priv = tdpage->priv;
598 
599 	if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tdpage)))
600 		return;
601 
602 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tdpage));
603 
604 	comp_editor_page_set_updating (COMP_EDITOR_PAGE (tdpage), TRUE);
605 
606 	status = e_dialog_combo_box_get (priv->status_combo, status_map);
607 	if (status == ICAL_STATUS_NONE) {
608 		gtk_spin_button_set_value (
609 			GTK_SPIN_BUTTON (priv->percent_complete), 0);
610 		e_date_edit_set_time (E_DATE_EDIT (priv->completed_date), ctime);
611 		complete_date_changed (tdpage, 0, FALSE);
612 	} else if (status == ICAL_STATUS_INPROCESS) {
613 		gint percent_complete = gtk_spin_button_get_value_as_int (
614 			GTK_SPIN_BUTTON (priv->percent_complete));
615 		if (percent_complete <= 0 || percent_complete >= 100)
616 			gtk_spin_button_set_value (
617 				GTK_SPIN_BUTTON (priv->percent_complete), 50);
618 
619 		e_date_edit_set_time (E_DATE_EDIT (priv->completed_date), ctime);
620 		complete_date_changed (tdpage, 0, FALSE);
621 	} else if (status == ICAL_STATUS_COMPLETED) {
622 		gtk_spin_button_set_value (
623 			GTK_SPIN_BUTTON (priv->percent_complete), 100);
624 		ctime = time (NULL);
625 		e_date_edit_set_time (E_DATE_EDIT (priv->completed_date), ctime);
626 		complete_date_changed (tdpage, ctime, TRUE);
627 	}
628 
629 	comp_editor_page_set_updating (COMP_EDITOR_PAGE (tdpage), FALSE);
630 
631 	comp_editor_set_changed (editor, TRUE);
632 }
633 
634 static void
635 percent_complete_changed (GtkAdjustment *adj,
636                           TaskDetailsPage *tdpage)
637 {
638 	TaskDetailsPagePrivate *priv;
639 	gint percent;
640 	icalproperty_status status;
641 	CompEditor *editor;
642 	gboolean complete;
643 	time_t ctime = -1;
644 
645 	priv = tdpage->priv;
646 
647 	if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tdpage)))
648 		return;
649 
650 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tdpage));
651 
652 	comp_editor_page_set_updating (COMP_EDITOR_PAGE (tdpage), TRUE);
653 
654 	percent = gtk_spin_button_get_value_as_int (
655 		GTK_SPIN_BUTTON (priv->percent_complete));
656 	if (percent == 100) {
657 		complete = TRUE;
658 		ctime = time (NULL);
659 		status = ICAL_STATUS_COMPLETED;
660 	} else {
661 		complete = FALSE;
662 
663 		if (percent == 0)
664 			status = ICAL_STATUS_NONE;
665 		else
666 			status = ICAL_STATUS_INPROCESS;
667 	}
668 
669 	e_dialog_combo_box_set (priv->status_combo, status, status_map);
670 	e_date_edit_set_time (E_DATE_EDIT (priv->completed_date), ctime);
671 	complete_date_changed (tdpage, ctime, complete);
672 
673 	comp_editor_page_set_updating (COMP_EDITOR_PAGE (tdpage), FALSE);
674 
675 	comp_editor_set_changed (editor, TRUE);
676 }
677 
678 /* Hooks the widget signals */
679 static void
680 init_widgets (TaskDetailsPage *tdpage)
681 {
682 	TaskDetailsPagePrivate *priv;
683 	GtkAdjustment *adjustment;
684 	CompEditor *editor;
685 
686 	priv = tdpage->priv;
687 
688 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tdpage));
689 
690 	/* Make sure the EDateEdit widgets use our timezones to get the
691 	 * current time. */
692 	e_date_edit_set_get_time_callback (
693 		E_DATE_EDIT (priv->completed_date),
694 		(EDateEditGetTimeCallback) comp_editor_get_current_time,
695 		g_object_ref (editor),
696 		(GDestroyNotify) g_object_unref);
697 
698 	/* These are created by hand, so hook the mnemonics manually */
699 	gtk_label_set_mnemonic_widget (
700 		GTK_LABEL (priv->date_completed_label),
701 		priv->completed_date);
702 	gtk_label_set_mnemonic_widget (
703 		GTK_LABEL (priv->url_label),
704 		priv->url_entry);
705 
706 	/* Connect signals. The Status, Percent Complete & Date Completed
707 	 * properties are closely related so whenever one changes we may need
708 	 * to update the other 2. */
709 	g_signal_connect (
710 		GTK_COMBO_BOX (priv->status_combo), "changed",
711 		G_CALLBACK (status_changed), tdpage);
712 
713 	adjustment = gtk_spin_button_get_adjustment (
714 		GTK_SPIN_BUTTON (priv->percent_complete));
715 	g_signal_connect (
716 		adjustment, "value_changed",
717 		G_CALLBACK (percent_complete_changed), tdpage);
718 
719 	/* Priority */
720 	g_signal_connect_swapped (
721 		GTK_COMBO_BOX (priv->priority_combo), "changed",
722 		G_CALLBACK (comp_editor_page_changed), tdpage);
723 
724 	/* Completed Date */
725 	g_signal_connect (
726 		priv->completed_date, "changed",
727 		G_CALLBACK (date_changed_cb), tdpage);
728 	g_signal_connect_swapped (
729 		priv->completed_date, "changed",
730 		G_CALLBACK (comp_editor_page_changed), tdpage);
731 
732 	/* URL */
733 	g_signal_connect_swapped (
734 		priv->url, "changed",
735 		G_CALLBACK (comp_editor_page_changed), tdpage);
736 }
737 
738 /**
739  * task_details_page_construct:
740  * @tdpage: An task details page.
741  *
742  * Constructs an task page by loading its Glade data.
743  *
744  * Return value: The same object as @tdpage, or NULL if the widgets could not
745  * be created.
746  **/
747 TaskDetailsPage *
748 task_details_page_construct (TaskDetailsPage *tdpage)
749 {
750 	TaskDetailsPagePrivate *priv = tdpage->priv;
751 	CompEditor *editor;
752 
753 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tdpage));
754 
755 	priv->builder = gtk_builder_new ();
756 	e_load_ui_builder_definition (priv->builder, "task-details-page.ui");
757 
758 	if (!get_widgets (tdpage)) {
759 		g_message (
760 			"task_details_page_construct(): "
761 			"Could not find all widgets in the XML file!");
762 		return NULL;
763 	}
764 
765 	init_widgets (tdpage);
766 
767 	g_signal_connect_swapped (
768 		editor, "notify::client",
769 		G_CALLBACK (sensitize_widgets), tdpage);
770 
771 	return tdpage;
772 }
773 
774 /**
775  * task_details_page_new:
776  *
777  * Creates a new task details page.
778  *
779  * Return value: A newly-created task details page, or NULL if the page could
780  * not be created.
781  **/
782 TaskDetailsPage *
783 task_details_page_new (CompEditor *editor)
784 {
785 	TaskDetailsPage *tdpage;
786 
787 	tdpage = g_object_new (TYPE_TASK_DETAILS_PAGE, "editor", editor, NULL);
788 	if (!task_details_page_construct (tdpage)) {
789 		g_object_unref (tdpage);
790 		g_return_val_if_reached (NULL);
791 	}
792 
793 	return tdpage;
794 }