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 }