aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/gui/dialogs/event-page.c
diff options
context:
space:
mode:
authorFederico Mena Quintero <federico@ximian.com>2001-05-17 07:38:58 +0800
committerFederico Mena Quintero <federico@src.gnome.org>2001-05-17 07:38:58 +0800
commitf00e9367382fb81cdb4b689f96645357f122b051 (patch)
tree1750ce1d4c5104dd77a2d509bd00ca657915ebae /calendar/gui/dialogs/event-page.c
parentd0243a91821d311699347c11d454314bdbdfc0fe (diff)
downloadgsoc2013-evolution-f00e9367382fb81cdb4b689f96645357f122b051.tar.gz
gsoc2013-evolution-f00e9367382fb81cdb4b689f96645357f122b051.tar.zst
gsoc2013-evolution-f00e9367382fb81cdb4b689f96645357f122b051.zip
Split the event and task editors into different objects for the separate
2001-05-16 Federico Mena Quintero <federico@ximian.com> Split the event and task editors into different objects for the separate pages; this way they can be shared by both editors. * gui/dialogs/editor-page.[ch]: New abstract class for a page in a calendar component editor. * gui/dialogs/event-page.[ch]: Main page of an event editor. * gui/dialogs/alarm-page.[ch]: Alarm page of a calendar component editor. * gui/dialogs/recurrence-page.[ch]: Recurrence page of a calendar component editor. * gui/dialogs/event-page.c (event_page_fill_widgets): Eeek, this was missing a bunch of break statements. (event_page_fill_component): Use a temporary variable rather than allocating a struct icaltimetype. * gui/dialogs/alarm-page.c (get_alarm_string): Do not use fixed-size buffers. (alarm_page_fill_widgets): Use cal_obj_uid_list_free(). (append_reminder): Now the list stores the plain CalComponentAlarm structures in the row data. We do *NOT* want to be frobbing the component's data directly. Rather, we clone the alarms from the component and maintain them on our own. (alarm_page_fill_component): Clone the alarms before adding them to the component so that we maintain the invariant that the alarm structures in the list did *not* come from the component. * cal-util/cal-component.c (cal_component_add_alarm): Added documentation. (cal_component_remove_alarm): Added documentation. (cal_component_remove_alarm): Do a lookup in our hash table of alarms instead of scanning the whole component. (CalComponentAlarm): Removed the `parent' field, since it was unused. (cal_component_free_alarm_uids): Removed function, since cal_component_get_alarm_uids() is documented so that the user will know that he must use cal_obj_uid_list_free(). (cal_component_alarm_clone): New function. svn path=/trunk/; revision=9861
Diffstat (limited to 'calendar/gui/dialogs/event-page.c')
-rw-r--r--calendar/gui/dialogs/event-page.c908
1 files changed, 908 insertions, 0 deletions
diff --git a/calendar/gui/dialogs/event-page.c b/calendar/gui/dialogs/event-page.c
new file mode 100644
index 0000000000..f67b898dce
--- /dev/null
+++ b/calendar/gui/dialogs/event-page.c
@@ -0,0 +1,908 @@
+/* Evolution calendar - Main page of the event editor dialog
+ *
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Authors: Federico Mena-Quintero <federico@ximian.com>
+ * Miguel de Icaza <miguel@ximian.com>
+ * Seth Alves <alves@hungry.com>
+ * JP Rosevear <jpr@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtksignal.h>
+#include <gtk/gtktogglebutton.h>
+#include <glade/glade.h>
+#include <gal/widgets/e-unicode.h>
+#include <gal/widgets/e-categories.h>
+#include "cal-util/timeutil.h"
+#include "e-util/e-dialog-widgets.h"
+#include "widgets/misc/e-dateedit.h"
+#include "../calendar-config.h"
+#include "event-page.h"
+
+
+
+/* Private part of the EventPage structure */
+struct _EventPagePrivate {
+ /* Glade XML data */
+ GladeXML *xml;
+
+ /* Widgets from the Glade file */
+
+ GtkWidget *main;
+
+ GtkWidget *summary;
+
+ GtkWidget *start_time;
+ GtkWidget *end_time;
+ GtkWidget *all_day_event;
+
+ GtkWidget *description;
+
+ GtkWidget *classification_public;
+ GtkWidget *classification_private;
+ GtkWidget *classification_confidential;
+
+ GtkWidget *contacts_btn;
+ GtkWidget *contacts;
+
+ GtkWidget *categories_btn;
+ GtkWidget *categories;
+};
+
+
+
+static void event_page_class_init (EventPageClass *class);
+static void event_page_init (EventPage *epage);
+static void event_page_destroy (GtkObject *object);
+
+static GtkWidget *event_page_get_widget (EditorPage *page);
+static void event_page_fill_widgets (EditorPage *page, CalComponent *comp);
+static void event_page_fill_component (EditorPage *page, CalComponent *comp);
+static void event_page_set_summary (EditorPage *page, const char *summary);
+static char *event_page_get_summary (EditorPage *page);
+static void event_page_set_dtstart (EditorPage *page, time_t start);
+
+/* Signal IDs */
+enum {
+ DATES_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint event_page_signals[LAST_SIGNAL] = { 0 };
+
+static EditorPageClass *parent_class = NULL;
+
+
+
+/**
+ * event_page_get_type:
+ *
+ * Registers the #EventPage class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the #EventPage class.
+ **/
+GtkType
+event_page_get_type (void)
+{
+ static GtkType event_page_type;
+
+ if (!event_page_type) {
+ static const GtkTypeInfo event_page_info = {
+ "EventPage",
+ sizeof (EventPage),
+ sizeof (EventPageClass),
+ (GtkClassInitFunc) event_page_class_init,
+ (GtkObjectInitFunc) event_page_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ event_page_type = gtk_type_unique (TYPE_EDITOR_PAGE, &event_page_info);
+ }
+
+ return event_page_type;
+}
+
+/* Class initialization function for the event page */
+static void
+event_page_class_init (EventPageClass *class)
+{
+ EditorPageClass *editor_page_class;
+ GtkObjectClass *object_class;
+
+ editor_page_class = (EditorPageClass *) class;
+ object_class = (GtkObjectClass *) class;
+
+ parent_class = gtk_type_class (TYPE_EDITOR_PAGE);
+
+ event_page_signals[DATES_CHANGED] =
+ gtk_signal_new ("dates_changed",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EventPageClass, dates_changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, event_page_signals, LAST_SIGNAL);
+
+ class->dates_changed = NULL;
+
+ editor_page_class->get_widget = event_page_get_widget;
+ editor_page_class->fill_widgets = event_page_fill_widgets;
+ editor_page_class->fill_component = event_page_fill_component;
+ editor_page_class->set_summary = event_page_set_summary;
+ editor_page_class->get_summary = event_page_get_summary;
+ editor_page_class->set_dtstart = event_page_set_dtstart;
+
+ object_class->destroy = event_page_destroy;
+}
+
+/* Object initialization function for the event page */
+static void
+event_page_init (EventPage *epage)
+{
+ EventPagePrivate *priv;
+
+ priv = g_new0 (EventPagePrivate, 1);
+ epage->priv = priv;
+
+ priv->xml = NULL;
+
+ priv->main = NULL;
+ priv->summary = NULL;
+ priv->start_time = NULL;
+ priv->end_time = NULL;
+ priv->all_day_event = NULL;
+ priv->description = NULL;
+ priv->classification_public = NULL;
+ priv->classification_private = NULL;
+ priv->classification_confidential = NULL;
+ priv->contacts_btn = NULL;
+ priv->contacts = NULL;
+ priv->categories_btn = NULL;
+ priv->categories = NULL;
+}
+
+/* Destroy handler for the event page */
+static void
+event_page_destroy (GtkObject *object)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_EVENT_PAGE (object));
+
+ epage = EVENT_PAGE (object);
+ priv = epage->priv;
+
+ if (priv->xml) {
+ gtk_object_unref (GTK_OBJECT (priv->xml));
+ priv->xml = NULL;
+ }
+
+ g_free (priv);
+ epage->priv = NULL;
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+
+static const int classification_map[] = {
+ CAL_COMPONENT_CLASS_PUBLIC,
+ CAL_COMPONENT_CLASS_PRIVATE,
+ CAL_COMPONENT_CLASS_CONFIDENTIAL,
+ -1
+};
+
+/* get_widget handler for the event page */
+static GtkWidget *
+event_page_get_widget (EditorPage *page)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+
+ epage = EVENT_PAGE (page);
+ priv = epage->priv;
+
+ return priv->main;
+}
+
+/* Checks if the event's time starts and ends at midnight, and sets the "all day
+ * event" box accordingly.
+ */
+static void
+check_all_day (EventPage *epage)
+{
+ EventPagePrivate *priv;
+ time_t ev_start, ev_end;
+ gboolean all_day = FALSE;
+
+ priv = epage->priv;
+
+ /* Currently we just return if the date is not set or not valid.
+ I'm not entirely sure this is the corrent thing to do. */
+ ev_start = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
+ g_assert (ev_start != -1);
+
+ ev_end = e_date_edit_get_time (E_DATE_EDIT (priv->end_time));
+ g_assert (ev_end != -1);
+
+ /* all day event checkbox */
+ if (time_day_begin (ev_start) == ev_start && time_day_begin (ev_end) == ev_end)
+ all_day = TRUE;
+
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->all_day_event), epage);
+ e_dialog_toggle_set (priv->all_day_event, all_day);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->all_day_event), epage);
+
+ e_date_edit_set_show_time (E_DATE_EDIT (priv->start_time), !all_day);
+ e_date_edit_set_show_time (E_DATE_EDIT (priv->end_time), !all_day);
+}
+
+/* Fills the widgets with default values */
+static void
+clear_widgets (EventPage *epage)
+{
+ EventPagePrivate *priv;
+ time_t now;
+
+ priv = epage->priv;
+
+ now = time (NULL);
+
+ /* Summary, description */
+ e_dialog_editable_set (priv->summary, NULL);
+ e_dialog_editable_set (priv->description, NULL);
+
+ /* Start and end times */
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->start_time), epage);
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->end_time), epage);
+
+ e_date_edit_set_time (E_DATE_EDIT (priv->start_time), now);
+ e_date_edit_set_time (E_DATE_EDIT (priv->end_time), now);
+
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->start_time), epage);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->end_time), epage);
+
+ check_all_day (epage);
+
+ /* Classification */
+ e_dialog_radio_set (priv->classification_public,
+ CAL_COMPONENT_CLASS_PRIVATE, classification_map);
+
+ /* Categories */
+ e_dialog_editable_set (priv->categories, NULL);
+}
+
+/* fill_widgets handler for the event page */
+static void
+event_page_fill_widgets (EditorPage *page, CalComponent *comp)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+ CalComponentText text;
+ CalComponentClassification cl;
+ CalComponentDateTime d;
+ GSList *l;
+ time_t dtstart, dtend;
+ const char *categories;
+
+ epage = EVENT_PAGE (page);
+ priv = epage->priv;
+
+ clear_widgets (epage);
+
+ /* Summary, description(s) */
+
+ cal_component_get_summary (comp, &text);
+ e_dialog_editable_set (priv->summary, text.value);
+
+ cal_component_get_description_list (comp, &l);
+ if (l) {
+ text = *(CalComponentText *)l->data;
+ e_dialog_editable_set (priv->description, text.value);
+ }
+ cal_component_free_text_list (l);
+
+ /* Start and end times */
+
+ /* All-day events are inclusive, i.e. if the end date shown is 2nd Feb
+ then the event includes all of the 2nd Feb. We would normally show
+ 3rd Feb as the end date, since it really ends at midnight on 3rd,
+ so we have to subtract a day so we only show the 2nd. */
+ cal_component_get_dtstart (comp, &d);
+ dtstart = icaltime_as_timet (*d.value);
+ cal_component_free_datetime (&d);
+
+ cal_component_get_dtend (comp, &d);
+ dtend = icaltime_as_timet (*d.value);
+ cal_component_free_datetime (&d);
+
+ if (time_day_begin (dtstart) == dtstart && time_day_begin (dtend) == dtend)
+ dtend = time_add_day (dtend, -1);
+
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->start_time), epage);
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->end_time), epage);
+
+ e_date_edit_set_time (E_DATE_EDIT (priv->start_time), dtstart);
+ e_date_edit_set_time (E_DATE_EDIT (priv->end_time), dtend);
+
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->start_time), epage);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->end_time), epage);
+
+ check_all_day (epage);
+
+ /* Classification */
+
+ cal_component_get_classification (comp, &cl);
+
+ switch (cl) {
+ case CAL_COMPONENT_CLASS_PUBLIC:
+ e_dialog_radio_set (priv->classification_public, CAL_COMPONENT_CLASS_PUBLIC,
+ classification_map);
+ break;
+
+ case CAL_COMPONENT_CLASS_PRIVATE:
+ e_dialog_radio_set (priv->classification_public, CAL_COMPONENT_CLASS_PRIVATE,
+ classification_map);
+ break;
+
+ case CAL_COMPONENT_CLASS_CONFIDENTIAL:
+ e_dialog_radio_set (priv->classification_public, CAL_COMPONENT_CLASS_CONFIDENTIAL,
+ classification_map);
+ break;
+
+ default:
+ /* What do do? We can't g_assert_not_reached() since it is a
+ * value from an external file.
+ */
+ }
+
+ /* Categories */
+
+ cal_component_get_categories (comp, &categories);
+ e_dialog_editable_set (priv->categories, categories);
+}
+
+/* fill_component handler for the event page */
+static void
+event_page_fill_component (EditorPage *page, CalComponent *comp)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+ CalComponentDateTime date;
+ struct icaltimetype icaltime;
+ time_t t;
+ gboolean all_day_event;
+ char *cat, *str;
+ CalComponentClassification classif;
+
+ epage = EVENT_PAGE (page);
+ priv = epage->priv;
+
+ /* Summary */
+
+ str = e_dialog_editable_get (priv->summary);
+ if (!str || strlen (str) == 0)
+ cal_component_set_summary (comp, NULL);
+ else {
+ CalComponentText text;
+
+ text.value = str;
+ text.altrep = NULL;
+
+ cal_component_set_summary (comp, &text);
+ }
+
+ if (str)
+ g_free (str);
+
+ /* Description */
+
+ str = e_dialog_editable_get (priv->description);
+ if (!str || strlen (str) == 0)
+ cal_component_set_description_list (comp, NULL);
+ else {
+ GSList l;
+ CalComponentText text;
+
+ text.value = str;
+ text.altrep = NULL;
+ l.data = &text;
+ l.next = NULL;
+
+ cal_component_set_description_list (comp, &l);
+ }
+
+ if (str)
+ g_free (str);
+
+ /* Dates */
+
+ date.value = &icaltime;
+ date.tzid = NULL;
+
+ t = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
+ if (t != -1) {
+ *date.value = icaltime_from_timet (t, FALSE);
+ cal_component_set_dtstart (comp, &date);
+ } else {
+ /* FIXME: What do we do here? */
+ }
+
+ /* If the all_day toggle is set, the end date is inclusive of the
+ entire day on which it points to. */
+ all_day_event = e_dialog_toggle_get (priv->all_day_event);
+ t = e_date_edit_get_time (E_DATE_EDIT (priv->end_time));
+ if (t != -1) {
+ if (all_day_event)
+ t = time_day_end (t);
+
+ *date.value = icaltime_from_timet (t, FALSE);
+ cal_component_set_dtend (comp, &date);
+ } else {
+ /* FIXME: What do we do here? */
+ }
+
+ /* Categories */
+
+ cat = e_dialog_editable_get (priv->categories);
+ cal_component_set_categories (comp, cat);
+
+ if (cat)
+ g_free (cat);
+
+ /* Classification */
+
+ classif = e_dialog_radio_get (priv->classification_public, classification_map);
+ cal_component_set_classification (comp, classif);
+}
+
+/* set_summary handler for the event page */
+static void
+event_page_set_summary (EditorPage *page, const char *summary)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+
+ epage = EVENT_PAGE (page);
+ priv = epage->priv;
+
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->summary), epage);
+ e_utf8_gtk_entry_set_text (GTK_ENTRY (priv->summary), summary);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->summary), epage);
+}
+
+/* get_summary handler for the event page */
+static char *
+event_page_get_summary (EditorPage *page)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+
+ epage = EVENT_PAGE (page);
+ priv = epage->priv;
+
+ return e_utf8_gtk_entry_get_text (GTK_ENTRY (priv->summary));
+}
+
+/* set_dtstart handler for the event page. We do nothing since we are *the*
+ * only provider of the dtstart value.
+ */
+static void
+event_page_set_dtstart (EditorPage *page, time_t start)
+{
+ /* nothing */
+}
+
+
+
+/* Gets the widgets from the XML file and returns if they are all available. */
+static gboolean
+get_widgets (EventPage *epage)
+{
+ EventPagePrivate *priv;
+ GtkWidget *toplevel;
+
+ priv = epage->priv;
+
+#define GW(name) glade_xml_get_widget (priv->xml, name)
+
+ toplevel = GW ("event-toplevel");
+ priv->main = GW ("event-page");
+ if (!(toplevel && priv->main))
+ return FALSE;
+
+ gtk_widget_ref (priv->main);
+ gtk_widget_unparent (priv->main);
+ gtk_widget_destroy (toplevel);
+
+ priv->summary = GW ("summary");
+
+ priv->start_time = GW ("start-time");
+ priv->end_time = GW ("end-time");
+ priv->all_day_event = GW ("all-day-event");
+
+ priv->description = GW ("description");
+
+ priv->classification_public = GW ("classification-public");
+ priv->classification_private = GW ("classification-private");
+ priv->classification_confidential = GW ("classification-confidential");
+
+ priv->contacts_btn = GW ("contacts-button");
+ priv->contacts = GW ("contacts");
+
+ priv->categories_btn = GW ("categories-button");
+ priv->categories = GW ("categories");
+
+#undef GW
+
+ return (priv->summary
+ && priv->start_time
+ && priv->end_time
+ && priv->all_day_event
+ && priv->description
+ && priv->classification_public
+ && priv->classification_private
+ && priv->classification_confidential
+ && priv->contacts_btn
+ && priv->contacts
+ && priv->categories_btn
+ && priv->categories);
+}
+
+/* Callback used when the summary changes; we emit the notification signal. */
+static void
+summary_changed_cb (GtkEditable *editable, gpointer data)
+{
+ EventPage *epage;
+
+ epage = EVENT_PAGE (data);
+ editor_page_notify_summary_changed (EDITOR_PAGE (epage));
+}
+
+/* Callback used when the start or end date widgets change. We check that the
+ * start date < end date and we set the "all day event" button as appropriate.
+ */
+static void
+date_changed_cb (EDateEdit *dedit, gpointer data)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+ time_t start, end;
+ struct tm tm_start, tm_end;
+
+ epage = EVENT_PAGE (data);
+ priv = epage->priv;
+
+ /* Ensure that start < end */
+
+ start = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
+ g_assert (start != -1);
+ end = e_date_edit_get_time (E_DATE_EDIT (priv->end_time));
+ g_assert (end != -1);
+
+ if (start >= end) {
+ tm_start = *localtime (&start);
+ tm_end = *localtime (&end);
+
+ if (start == end && tm_start.tm_hour == 0
+ && tm_start.tm_min == 0 && tm_start.tm_sec == 0) {
+ /* If the start and end times are the same, but both are
+ * on day boundaries, then that is OK since it means we
+ * have an all-day event lasting 1 day. So we do
+ * nothing here.
+ */
+ } else if (GTK_WIDGET (dedit) == priv->start_time) {
+ /* Modify the end time */
+
+ tm_end.tm_year = tm_start.tm_year;
+ tm_end.tm_mon = tm_start.tm_mon;
+ tm_end.tm_mday = tm_start.tm_mday;
+ tm_end.tm_hour = tm_start.tm_hour + 1;
+ tm_end.tm_min = tm_start.tm_min;
+ tm_end.tm_sec = tm_start.tm_sec;
+
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->end_time), epage);
+ e_date_edit_set_time (E_DATE_EDIT (priv->end_time), mktime (&tm_end));
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->end_time), epage);
+ } else if (GTK_WIDGET (dedit) == priv->end_time) {
+ /* Modify the start time */
+
+ tm_start.tm_year = tm_end.tm_year;
+ tm_start.tm_mon = tm_end.tm_mon;
+ tm_start.tm_mday = tm_end.tm_mday;
+ tm_start.tm_hour = tm_end.tm_hour - 1;
+ tm_start.tm_min = tm_end.tm_min;
+ tm_start.tm_sec = tm_end.tm_sec;
+
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->start_time), epage);
+ e_date_edit_set_time (E_DATE_EDIT (priv->start_time), mktime (&tm_start));
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->start_time), epage);
+ } else
+ g_assert_not_reached ();
+ }
+
+ /* Set the "all day event" button as appropriate */
+ check_all_day (epage);
+
+ /* Notify upstream */
+ gtk_signal_emit (GTK_OBJECT (epage), event_page_signals[DATES_CHANGED]);
+}
+
+/* Callback: all day event button toggled.
+ * Note that this should only be called when the user explicitly toggles the
+ * button. Be sure to block this handler when the toggle button's state is set
+ * within the code.
+ */
+static void
+all_day_event_toggled_cb (GtkWidget *toggle, gpointer data)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+ struct tm start_tm, end_tm;
+ time_t start_t, end_t;
+ gboolean all_day;
+
+ epage = EVENT_PAGE (data);
+ priv = epage->priv;
+
+ /* When the all_day toggle is turned on, the start date is rounded down
+ * to the start of the day, and end date is rounded down to the start of
+ * the day on which the event ends. The event is then taken to be
+ * inclusive of the days between the start and end days. Note that if
+ * the event end is at midnight, we do not round it down to the previous
+ * day, since if we do that and the user repeatedly turns the all_day
+ * toggle on and off, the event keeps shrinking. (We'd also need to
+ * make sure we didn't adjust the time when the radio button is
+ * initially set.)
+ *
+ * When the all_day_toggle is turned off, we set the event start to the
+ * start of the working day, and if the event end is on or before the
+ * day of the event start we set it to one hour after the event start.
+ */
+ all_day = GTK_TOGGLE_BUTTON (toggle)->active;
+
+ /*
+ * Start time.
+ */
+ start_t = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
+ g_assert (start_t != -1);
+
+ start_tm = *localtime (&start_t);
+
+ if (all_day) {
+ /* Round down to the start of the day. */
+ start_tm.tm_hour = 0;
+ start_tm.tm_min = 0;
+ start_tm.tm_sec = 0;
+ } else {
+ /* Set to the start of the working day. */
+ start_tm.tm_hour = calendar_config_get_day_start_hour ();
+ start_tm.tm_min = calendar_config_get_day_start_minute ();
+ start_tm.tm_sec = 0;
+ }
+
+ /*
+ * End time.
+ */
+ end_t = e_date_edit_get_time (E_DATE_EDIT (priv->end_time));
+ g_assert (end_t != -1);
+
+ end_tm = *localtime (&end_t);
+
+ if (all_day) {
+ /* Round down to the start of the day. */
+ end_tm.tm_hour = 0;
+ end_tm.tm_min = 0;
+ end_tm.tm_sec = 0;
+ } else {
+ /* If the event end is now on or before the event start day,
+ * make it end one hour after the start. mktime() will fix any
+ * overflows.
+ */
+ if (end_tm.tm_year < start_tm.tm_year
+ || (end_tm.tm_year == start_tm.tm_year
+ && end_tm.tm_mon < start_tm.tm_mon)
+ || (end_tm.tm_year == start_tm.tm_year
+ && end_tm.tm_mon == start_tm.tm_mon
+ && end_tm.tm_mday <= start_tm.tm_mday)) {
+ end_tm.tm_year = start_tm.tm_year;
+ end_tm.tm_mon = start_tm.tm_mon;
+ end_tm.tm_mday = start_tm.tm_mday;
+ end_tm.tm_hour = start_tm.tm_hour + 1;
+ }
+ }
+
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->start_time), epage);
+ gtk_signal_handler_block_by_data (GTK_OBJECT (priv->end_time), epage);
+
+ e_date_edit_set_time (E_DATE_EDIT (priv->start_time), mktime (&start_tm));
+ e_date_edit_set_time (E_DATE_EDIT (priv->end_time), mktime (&end_tm));
+
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->start_time), epage);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->end_time), epage);
+
+ e_date_edit_set_show_time (E_DATE_EDIT (priv->start_time), !all_day);
+ e_date_edit_set_show_time (E_DATE_EDIT (priv->end_time), !all_day);
+
+ /* Notify upstream */
+ gtk_signal_emit (GTK_OBJECT (epage), event_page_signals[DATES_CHANGED]);
+}
+
+/* Callback used when the categories button is clicked; we must bring up the
+ * category list dialog.
+ */
+static void
+categories_clicked_cb (GtkWidget *button, gpointer data)
+{
+ EventPage *epage;
+ EventPagePrivate *priv;
+ char *categories;
+ GnomeDialog *dialog;
+ int result;
+ GtkWidget *entry;
+
+ epage = EVENT_PAGE (data);
+ priv = epage->priv;
+
+ entry = priv->categories;
+ categories = e_utf8_gtk_entry_get_text (GTK_ENTRY (entry));
+
+ dialog = GNOME_DIALOG (e_categories_new (categories));
+ result = gnome_dialog_run (dialog);
+ g_free (categories);
+
+ if (result == 0) {
+ gtk_object_get (GTK_OBJECT (dialog),
+ "categories", &categories,
+ NULL);
+ e_utf8_gtk_entry_set_text (GTK_ENTRY (entry), categories);
+ g_free (categories);
+ }
+
+ gtk_object_destroy (GTK_OBJECT (dialog));
+}
+
+/* This is called when any field is changed; it notifies upstream. */
+static void
+field_changed_cb (GtkWidget *widget, gpointer data)
+{
+ EventPage *epage;
+
+ epage = EVENT_PAGE (data);
+ editor_page_notify_changed (EDITOR_PAGE (epage));
+}
+
+/* Hooks the widget signals */
+static void
+init_widgets (EventPage *epage)
+{
+ EventPagePrivate *priv;
+
+ priv = epage->priv;
+
+ /* Summary */
+ gtk_signal_connect (GTK_OBJECT (priv->summary), "changed",
+ GTK_SIGNAL_FUNC (summary_changed_cb), epage);
+
+ /* Start and end times */
+ gtk_signal_connect (GTK_OBJECT (priv->start_time), "changed",
+ GTK_SIGNAL_FUNC (date_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->end_time), "changed",
+ GTK_SIGNAL_FUNC (date_changed_cb), epage);
+
+ gtk_signal_connect (GTK_OBJECT (priv->all_day_event), "toggled",
+ GTK_SIGNAL_FUNC (all_day_event_toggled_cb), epage);
+
+ /* Categories button */
+ gtk_signal_connect (GTK_OBJECT (priv->categories_btn), "clicked",
+ GTK_SIGNAL_FUNC (categories_clicked_cb), epage);
+
+ /* Connect the default signal handler to use to make sure we notify
+ * upstream of changes to the widget values.
+ */
+
+ gtk_signal_connect (GTK_OBJECT (priv->summary), "changed",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->start_time), "changed",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->end_time), "changed",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->all_day_event), "toggled",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->description), "changed",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->classification_public), "toggled",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->classification_private), "toggled",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->classification_confidential), "toggled",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+ gtk_signal_connect (GTK_OBJECT (priv->categories), "changed",
+ GTK_SIGNAL_FUNC (field_changed_cb), epage);
+
+ /* FIXME: we do not support these fields yet, so we disable them */
+
+ gtk_widget_set_sensitive (priv->contacts_btn, FALSE);
+ gtk_widget_set_sensitive (priv->contacts, FALSE);
+}
+
+
+
+/**
+ * event_page_construct:
+ * @epage: An event page.
+ *
+ * Constructs an event page by loading its Glade data.
+ *
+ * Return value: The same object as @epage, or NULL if the widgets could not be
+ * created.
+ **/
+EventPage *
+event_page_construct (EventPage *epage)
+{
+ EventPagePrivate *priv;
+
+ priv = epage->priv;
+
+ priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/event-page.glade", NULL);
+ if (!priv->xml) {
+ g_message ("event_page_construct(): Could not load the Glade XML file!");
+ return NULL;
+ }
+
+ if (!get_widgets (epage)) {
+ g_message ("event_page_construct(): Could not find all widgets in the XML file!");
+ return NULL;
+ }
+
+ init_widgets (epage);
+
+ return epage;
+}
+
+/**
+ * event_page_new:
+ *
+ * Creates a new event page.
+ *
+ * Return value: A newly-created event page, or NULL if the page could
+ * not be created.
+ **/
+EventPage *
+event_page_new (void)
+{
+ EventPage *epage;
+
+ epage = gtk_type_new (TYPE_EVENT_PAGE);
+ if (!event_page_construct (epage)) {
+ gtk_object_unref (GTK_OBJECT (epage));
+ return NULL;
+ }
+
+ return epage;
+}