From f00e9367382fb81cdb4b689f96645357f122b051 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Wed, 16 May 2001 23:38:58 +0000 Subject: Split the event and task editors into different objects for the separate 2001-05-16 Federico Mena Quintero 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 --- calendar/ChangeLog | 49 ++ calendar/cal-util/cal-component.c | 171 ++--- calendar/cal-util/cal-component.h | 2 +- calendar/gui/Makefile.am | 2 + calendar/gui/dialogs/Makefile.am | 19 +- calendar/gui/dialogs/alarm-page.c | 778 ++++++++++++++++++++++ calendar/gui/dialogs/alarm-page.glade | 381 +++++++++++ calendar/gui/dialogs/alarm-page.h | 64 ++ calendar/gui/dialogs/editor-page.c | 259 ++++++++ calendar/gui/dialogs/editor-page.h | 86 +++ calendar/gui/dialogs/event-page.c | 908 ++++++++++++++++++++++++++ calendar/gui/dialogs/event-page.glade | 429 ++++++++++++ calendar/gui/dialogs/event-page.h | 70 ++ calendar/gui/dialogs/recurrence-page.c | 900 +++++++++++++++++++++++++ calendar/gui/dialogs/recurrence-page.glade | 607 +++++++++++++++++ calendar/gui/dialogs/recurrence-page.h | 64 ++ calendar/gui/dialogs/task-details-page.glade | 137 ++++ calendar/gui/dialogs/task-page.glade | 944 +++++++++++---------------- calendar/gui/e-day-view.c | 4 +- calendar/gui/e-week-view.c | 1 - calendar/gui/event-editor.c | 2 +- 21 files changed, 5239 insertions(+), 638 deletions(-) create mode 100644 calendar/gui/dialogs/alarm-page.c create mode 100644 calendar/gui/dialogs/alarm-page.glade create mode 100644 calendar/gui/dialogs/alarm-page.h create mode 100644 calendar/gui/dialogs/editor-page.c create mode 100644 calendar/gui/dialogs/editor-page.h create mode 100644 calendar/gui/dialogs/event-page.c create mode 100644 calendar/gui/dialogs/event-page.glade create mode 100644 calendar/gui/dialogs/event-page.h create mode 100644 calendar/gui/dialogs/recurrence-page.c create mode 100644 calendar/gui/dialogs/recurrence-page.glade create mode 100644 calendar/gui/dialogs/recurrence-page.h create mode 100644 calendar/gui/dialogs/task-details-page.glade diff --git a/calendar/ChangeLog b/calendar/ChangeLog index fecd71e071..c903d61031 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,52 @@ +2001-05-16 Federico Mena Quintero + + 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. + +2001-05-09 Federico Mena Quintero + + * gui/Makefile.am (evolution_calendar_SOURCES): Added + editor-page.[ch] to the list of sources. + 2001-05-09 JP Rosevear * gui/event-editor.c (reminder_add_cb): switch on the correct diff --git a/calendar/cal-util/cal-component.c b/calendar/cal-util/cal-component.c index d43234617a..d716c0e581 100644 --- a/calendar/cal-util/cal-component.c +++ b/calendar/cal-util/cal-component.c @@ -110,9 +110,6 @@ struct _CalComponentPrivate { /* Private structure for alarms */ struct _CalComponentAlarm { - /* Our parent component */ - CalComponent *parent; - /* Alarm icalcomponent we wrap */ icalcomponent *icalcomp; @@ -348,7 +345,7 @@ cal_component_gen_uid (void) iso = isodate_from_time_t (t); ret = g_strdup_printf ("%s-%d-%d-%d-%d@%s", - iso, + iso, getpid (), getgid (), getppid (), @@ -692,17 +689,6 @@ add_alarm (CalComponent *comp, icalcomponent *alarm, const char *auid) return auid; } -static void -remove_alarm (CalComponent *comp, const char *auid) -{ - CalComponentPrivate *priv; - - priv = comp->priv; - - g_hash_table_remove (priv->alarm_uid_hash, auid); -} - - /* Scans an alarm subcomponent, adds an UID extension property to it (so that we * can reference alarms by unique IDs), and adds its mapping to the component. */ static void @@ -1129,12 +1115,12 @@ cal_component_set_uid (CalComponent *comp, const char *uid) /** * cal_component_get_categories: - * @comp: A calendar component object. - * @categories: - * - * + * @comp: A calendar component object. + * @categories: + * + * **/ -void +void cal_component_get_categories (CalComponent *comp, const char **categories) { CalComponentPrivate *priv; @@ -1155,11 +1141,11 @@ cal_component_get_categories (CalComponent *comp, const char **categories) /** * cal_component_set_categories: * @comp: A calendar component object. - * @categories: - * - * + * @categories: + * + * **/ -void +void cal_component_set_categories (CalComponent *comp, const char *categories) { CalComponentPrivate *priv; @@ -1286,7 +1272,7 @@ cal_component_set_categories_list (CalComponent *comp, GSList *categ_list) icalcomponent_remove_property (priv->icalcomp, priv->categories); icalproperty_free (priv->categories); } - + return; } @@ -3457,63 +3443,64 @@ cal_component_has_alarms (CalComponent *comp) return g_hash_table_size (priv->alarm_uid_hash) != 0; } -void +/** + * cal_component_add_alarm: + * @comp: A calendar component. + * @alarm: An alarm. + * + * Adds an alarm subcomponent to a calendar component. You should have created + * the @alarm by using cal_component_alarm_new(); it is invalid to use a + * #CalComponentAlarm structure that came from cal_component_get_alarm(). After + * adding the alarm, the @alarm structure is no longer valid because the + * internal structures may change and you should get rid of it by using + * cal_component_alarm_free(). + **/ +void cal_component_add_alarm (CalComponent *comp, CalComponentAlarm *alarm) { CalComponentPrivate *priv; - + g_return_if_fail (comp != NULL); g_return_if_fail (IS_CAL_COMPONENT (comp)); g_return_if_fail (alarm != NULL); - + priv = comp->priv; - - alarm->parent = comp; + add_alarm (comp, alarm->icalcomp, icalproperty_get_x (alarm->uid)); icalcomponent_add_component (priv->icalcomp, alarm->icalcomp); } -void +/** + * cal_component_remove_alarm: + * @comp: A calendar component. + * @auid: UID of the alarm to remove. + * + * Removes an alarm subcomponent from a calendar component. If the alarm that + * corresponds to the specified @auid had been fetched with + * cal_component_get_alarm(), then those alarm structures will be invalid; you + * should get rid of them with cal_component_alarm_free() before using this + * function. + **/ +void cal_component_remove_alarm (CalComponent *comp, const char *auid) { CalComponentPrivate *priv; - icalcompiter iter; + icalcomponent *alarm; g_return_if_fail (comp != NULL); g_return_if_fail (IS_CAL_COMPONENT (comp)); g_return_if_fail (auid != NULL); priv = comp->priv; + g_return_if_fail (priv->icalcomp != NULL); - for (iter = icalcomponent_begin_component (priv->icalcomp, ICAL_VALARM_COMPONENT); - icalcompiter_deref (&iter) != NULL; - icalcompiter_next (&iter)) { - icalproperty *prop; - icalcomponent *subcomp; + alarm = g_hash_table_lookup (priv->alarm_uid_hash, auid); + if (!alarm) + return; - subcomp = icalcompiter_deref (&iter); - for (prop = icalcomponent_get_first_property (subcomp, ICAL_X_PROPERTY); - prop; - prop = icalcomponent_get_next_property (subcomp, ICAL_X_PROPERTY)) { - const char *xname; - const char *alarm_uid; - - xname = icalproperty_get_x_name (prop); - g_assert (xname != NULL); - - if (strcmp (xname, EVOLUTION_ALARM_UID_PROPERTY) == 0) { - alarm_uid = alarm_uid_from_prop (prop); - if (strcmp (alarm_uid, auid) == 0) { - remove_alarm (comp, auid); - icalcomponent_remove_component (priv->icalcomp, subcomp); - icalcomponent_free (subcomp); - break; - } - - return; - } - } - } + g_hash_table_remove (priv->alarm_uid_hash, auid); + icalcomponent_remove_component (priv->icalcomp, alarm); + icalcomponent_free (alarm); } @@ -3553,14 +3540,13 @@ scan_alarm_property (CalComponentAlarm *alarm, icalproperty *prop) /* Creates a CalComponentAlarm from a libical alarm subcomponent */ static CalComponentAlarm * -make_alarm (CalComponent *comp, icalcomponent *subcomp) +make_alarm (icalcomponent *subcomp) { CalComponentAlarm *alarm; icalproperty *prop; alarm = g_new (CalComponentAlarm, 1); - alarm->parent = comp; alarm->icalcomp = subcomp; alarm->uid = NULL; @@ -3590,10 +3576,10 @@ add_alarm_uid (gpointer key, gpointer value, gpointer data) /** * cal_component_get_alarm_uids: * @comp: A calendar component. - * + * * Builds a list of the unique identifiers of the alarm subcomponents inside a * calendar component. - * + * * Return value: List of unique identifiers for alarms. This should be freed * using cal_obj_uid_list_free(). **/ @@ -3619,9 +3605,9 @@ cal_component_get_alarm_uids (CalComponent *comp) * cal_component_get_alarm: * @comp: A calendar component. * @auid: Unique identifier for the sought alarm subcomponent. - * + * * Queries a particular alarm subcomponent of a calendar component. - * + * * Return value: The alarm subcomponent that corresponds to the specified @auid, * or #NULL if no alarm exists with that UID. This should be freed using * cal_component_alarm_free(). @@ -3643,21 +3629,15 @@ cal_component_get_alarm (CalComponent *comp, const char *auid) alarm = g_hash_table_lookup (priv->alarm_uid_hash, auid); if (alarm) - return make_alarm (comp, alarm); + return make_alarm (alarm); else return NULL; } -void -cal_component_free_alarm_uids (GList *alarm_uids) -{ - g_list_foreach (alarm_uids, (GFunc)g_free, NULL); -} - /** * cal_component_alarms_free: * @alarms: Component alarms structure. - * + * * Frees a #CalComponentAlarms structure. **/ void @@ -3684,17 +3664,19 @@ cal_component_alarms_free (CalComponentAlarms *alarms) /** * cal_component_alarm_new: - * - * - * + * + * + * * Return value: a new alarm component **/ CalComponentAlarm * cal_component_alarm_new (void) { - CalComponentAlarm *alarm = g_new0 (CalComponentAlarm, 1); + CalComponentAlarm *alarm; char *new_auid ; + alarm = g_new (CalComponentAlarm, 1); + alarm->icalcomp = icalcomponent_new (ICAL_VALARM_COMPONENT); new_auid = cal_component_gen_uid (); @@ -3702,16 +3684,39 @@ cal_component_alarm_new (void) icalproperty_set_x_name (alarm->uid, EVOLUTION_ALARM_UID_PROPERTY); icalcomponent_add_property (alarm->icalcomp, alarm->uid); g_free (new_auid); - + + alarm->action = NULL; + alarm->trigger = NULL; + return alarm; } /** - * cal_component_alarm_get_uid: + * cal_component_alarm_clone: * @alarm: An alarm subcomponent. * - * Queries the unique identifier of an alarm subcomponent. + * Creates a new alarm subcomponent by copying the information from another one. * + * Return value: A newly-created alarm subcomponent with the same values as the + * original one. Should be freed with cal_component_alarm_free(). + **/ +CalComponentAlarm * +cal_component_alarm_clone (CalComponentAlarm *alarm) +{ + icalcomponent *icalcomp; + + g_return_val_if_fail (alarm != NULL, NULL); + + icalcomp = icalcomponent_new_clone (alarm->icalcomp); + return make_alarm (icalcomp); +} + +/** + * cal_component_alarm_get_uid: + * @alarm: An alarm subcomponent. + * + * Queries the unique identifier of an alarm subcomponent. + * * Return value: UID of the alarm. **/ const char * @@ -4007,9 +4012,9 @@ cal_component_alarm_free (CalComponentAlarm *alarm) icalcomponent_free (alarm->icalcomp); alarm->icalcomp = NULL; - - alarm->parent = NULL; + alarm->uid = NULL; alarm->action = NULL; + alarm->trigger = NULL; g_free (alarm); } diff --git a/calendar/cal-util/cal-component.h b/calendar/cal-util/cal-component.h index 95d2b1eb0b..e4a2e2421e 100644 --- a/calendar/cal-util/cal-component.h +++ b/calendar/cal-util/cal-component.h @@ -336,12 +336,12 @@ void cal_component_remove_alarm (CalComponent *comp, const char *auid); GList *cal_component_get_alarm_uids (CalComponent *comp); CalComponentAlarm *cal_component_get_alarm (CalComponent *comp, const char *auid); -void cal_component_free_alarm_uids (GList *alarm_uids); void cal_component_alarms_free (CalComponentAlarms *alarms); /* CalComponentAlarms */ CalComponentAlarm *cal_component_alarm_new (void); +CalComponentAlarm *cal_component_alarm_clone (CalComponentAlarm *alarm); const char *cal_component_alarm_get_uid (CalComponentAlarm *alarm); diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am index 292e1ebd28..8cd202c7c0 100644 --- a/calendar/gui/Makefile.am +++ b/calendar/gui/Makefile.am @@ -87,6 +87,8 @@ evolution_calendar_SOURCES = \ e-week-view.h \ e-tasks.c \ e-tasks.h \ + editor-page.c \ + editor-page.h \ event-editor.c \ event-editor.h \ gnome-cal.c \ diff --git a/calendar/gui/dialogs/Makefile.am b/calendar/gui/dialogs/Makefile.am index 12342538ab..ec4e31379d 100644 --- a/calendar/gui/dialogs/Makefile.am +++ b/calendar/gui/dialogs/Makefile.am @@ -20,19 +20,30 @@ noinst_LIBRARIES = libcal-dialogs.a libcal_dialogs_a_SOURCES = \ alarm-notify-dialog.c \ alarm-notify-dialog.h \ + alarm-page.c \ + alarm-page.h \ cal-prefs-dialog.c \ cal-prefs-dialog.h \ delete-comp.c \ delete-comp.h \ + editor-page.c \ + editor-page.h \ + event-page.c \ + event-page.h \ save-comp.c \ save-comp.h \ task-editor.c \ task-editor.h -glade_DATA = \ - alarm-notify.glade \ - cal-prefs-dialog.glade \ - task-editor-dialog.glade +glade_DATA = \ + alarm-notify.glade \ + alarm-page.glade \ + cal-prefs-dialog.glade \ + event-page.glade \ + recurrence-page.glade \ + task-details-page.glade \ + task-editor-dialog.glade \ + task-page.glade EXTRA_DIST = \ $(glade_DATA) diff --git a/calendar/gui/dialogs/alarm-page.c b/calendar/gui/dialogs/alarm-page.c new file mode 100644 index 0000000000..a4e0054c6b --- /dev/null +++ b/calendar/gui/dialogs/alarm-page.c @@ -0,0 +1,778 @@ +/* Evolution calendar - Alarm page of the calendar component dialogs + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero + * Miguel de Icaza + * Seth Alves + * JP Rosevear + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include "cal-util/cal-util.h" +#include "e-util/e-dialog-widgets.h" +#include "alarm-page.h" + + + +/* Private part of the AlarmPage structure */ +struct _AlarmPagePrivate { + /* Glade XML data */ + GladeXML *xml; + + /* Widgets from the Glade file */ + + GtkWidget *main; + + GtkWidget *summary; + GtkWidget *starting_date; + + GtkWidget *list; + GtkWidget *add; + GtkWidget *delete; + + GtkWidget *action; + GtkWidget *interval_value; + GtkWidget *value_units; + GtkWidget *relative; + GtkWidget *time; +}; + + + +static void alarm_page_class_init (AlarmPageClass *class); +static void alarm_page_init (AlarmPage *apage); +static void alarm_page_destroy (GtkObject *object); + +static GtkWidget *alarm_page_get_widget (EditorPage *page); +static void alarm_page_fill_widgets (EditorPage *page, CalComponent *comp); +static void alarm_page_fill_component (EditorPage *page, CalComponent *comp); +static void alarm_page_set_summary (EditorPage *page, const char *summary); +static char *alarm_page_get_summary (EditorPage *page); +static void alarm_page_set_dtstart (EditorPage *page, time_t start); + +static EditorPageClass *parent_class = NULL; + + + +/** + * alarm_page_get_type: + * + * Registers the #AlarmPage class if necessary, and returns the type ID + * associated to it. + * + * Return value: The type ID of the #AlarmPage class. + **/ +GtkType +alarm_page_get_type (void) +{ + static GtkType alarm_page_type; + + if (!alarm_page_type) { + static const GtkTypeInfo alarm_page_info = { + "AlarmPage", + sizeof (AlarmPage), + sizeof (AlarmPageClass), + (GtkClassInitFunc) alarm_page_class_init, + (GtkObjectInitFunc) alarm_page_init, + NULL, /* reserved_1 */ + NULL, /* reserved_2 */ + (GtkClassInitFunc) NULL + }; + + alarm_page_type = gtk_type_unique (TYPE_EDITOR_PAGE, &alarm_page_info); + } + + return alarm_page_type; +} + +/* Class initialization function for the alarm page */ +static void +alarm_page_class_init (AlarmPageClass *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); + + editor_page_class->get_widget = alarm_page_get_widget; + editor_page_class->fill_widgets = alarm_page_fill_widgets; + editor_page_class->fill_component = alarm_page_fill_component; + editor_page_class->set_summary = alarm_page_set_summary; + editor_page_class->get_summary = alarm_page_get_summary; + editor_page_class->set_dtstart = alarm_page_set_dtstart; + + object_class->destroy = alarm_page_destroy; +} + +/* Object initialization function for the alarm page */ +static void +alarm_page_init (AlarmPage *apage) +{ + AlarmPagePrivate *priv; + + priv = g_new0 (AlarmPagePrivate, 1); + apage->priv = priv; + + priv->xml = NULL; + + priv->main = NULL; + priv->summary = NULL; + priv->starting_date = NULL; + priv->list = NULL; + priv->add = NULL; + priv->delete = NULL; + priv->action = NULL; + priv->interval_value = NULL; + priv->value_units = NULL; + priv->relative = NULL; + priv->time = NULL; +} + +/* Frees all the alarm data and empties the list */ +static void +free_alarms (AlarmPage *apage) +{ + AlarmPagePrivate *priv; + GtkCList *clist; + int i; + + priv = apage->priv; + + clist = GTK_CLIST (priv->list); + + for (i = 0; i < clist->rows; i++) { + CalComponentAlarm *alarm; + + alarm = gtk_clist_get_row_data (clist, i); + g_assert (alarm != NULL); + cal_component_alarm_free (alarm); + + gtk_clist_set_row_data (clist, i, NULL); + } + + gtk_clist_clear (clist); +} + +/* Destroy handler for the alarm page */ +static void +alarm_page_destroy (GtkObject *object) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_ALARM_PAGE (object)); + + apage = ALARM_PAGE (object); + priv = apage->priv; + + if (priv->xml) { + gtk_object_unref (GTK_OBJECT (priv->xml)); + priv->xml = NULL; + } + + free_alarms (apage); + + g_free (priv); + apage->priv = NULL; + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + + + +/* get_widget handler for the alarm page */ +static GtkWidget * +alarm_page_get_widget (EditorPage *page) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + + apage = ALARM_PAGE (page); + priv = apage->priv; + + return priv->main; +} + +/* Fills the widgets with default values */ +static void +clear_widgets (AlarmPage *apage) +{ + AlarmPagePrivate *priv; + + priv = apage->priv; + + /* Summary */ + e_dialog_editable_set (priv->summary, NULL); + + /* Start date */ + gtk_label_set_text (GTK_LABEL (priv->starting_date), ""); + + /* List data */ + free_alarms (apage); +} + +static char * +get_alarm_duration_string (struct icaldurationtype *duration) +{ + GString *string = g_string_new (NULL); + char *ret; + + if (duration->days > 1) + g_string_sprintf (string, _("%d days"), duration->days); + else if (duration->days == 1) + g_string_append (string, _("1 day")); + + if (duration->weeks > 1) + g_string_sprintf (string, _("%d weeks"), duration->weeks); + else if (duration->weeks == 1) + g_string_append (string, _("1 week")); + + if (duration->hours > 1) + g_string_sprintf (string, _("%d hours"), duration->hours); + else if (duration->hours == 1) + g_string_append (string, _("1 hour")); + + if (duration->minutes > 1) + g_string_sprintf (string, _("%d minutes"), duration->minutes); + else if (duration->minutes == 1) + g_string_append (string, _("1 minute")); + + if (duration->seconds > 1) + g_string_sprintf (string, _("%d seconds"), duration->seconds); + else if (duration->seconds == 1) + g_string_append (string, _("1 second")); + + ret = string->str; + g_string_free (string, FALSE); + + return ret; +} + +static char * +get_alarm_string (CalComponentAlarm *alarm) +{ + CalAlarmAction action; + CalAlarmTrigger trigger; + char string[256]; + char *base; + char *str; + char *dur; + + string [0] = '\0'; + + cal_component_alarm_get_action (alarm, &action); + cal_component_alarm_get_trigger (alarm, &trigger); + + switch (action) { + case CAL_ALARM_AUDIO: + base = _("Play a sound"); + break; + + case CAL_ALARM_DISPLAY: + base = _("Show a dialog"); + break; + + case CAL_ALARM_EMAIL: + base = _("Send an email"); + break; + + case CAL_ALARM_PROCEDURE: + base = _("Run a program"); + break; + + case CAL_ALARM_NONE: + case CAL_ALARM_UNKNOWN: + base = _("Unknown"); + break; + } + + /* FIXME: This does not look like it will localize correctly. */ + + switch (trigger.type) { + case CAL_ALARM_TRIGGER_RELATIVE_START: + dur = get_alarm_duration_string (&trigger.u.rel_duration); + + if (trigger.u.rel_duration.is_neg) + str = g_strdup_printf ("%s %s %s", base, dur, + _(" before start of appointment")); + else + str = g_strdup_printf ("%s %s %s", base, dur, + _(" after start of appointment")); + + g_free (dur); + break; + + case CAL_ALARM_TRIGGER_RELATIVE_END: + dur = get_alarm_duration_string (&trigger.u.rel_duration); + + if (trigger.u.rel_duration.is_neg) + str = g_strdup_printf ("%s %s %s", base, dur, + _(" before end of appointment")); + else + str = g_strdup_printf ("%s %s %s", base, dur, + _(" after end of appointment")); + + g_free (dur); + break; + case CAL_ALARM_TRIGGER_NONE: + case CAL_ALARM_TRIGGER_ABSOLUTE: + str = g_strdup_printf ("%s %s", base, + _("Unknown")); + break; + } + + return str; +} + +/* Appends an alarm to the list */ +static void +append_reminder (AlarmPage *apage, CalComponentAlarm *alarm) +{ + AlarmPagePrivate *priv; + GtkCList *clist; + char *c[1]; + int i; + + priv = apage->priv; + + clist = GTK_CLIST (priv->list); + + c[0] = get_alarm_string (alarm); + i = gtk_clist_append (clist, c); + + gtk_clist_set_row_data (clist, i, alarm); + gtk_clist_select_row (clist, i, 0); + g_free (c[0]); + + gtk_widget_set_sensitive (priv->delete, TRUE); +} + +/* fill_widgets handler for the alarm page */ +static void +alarm_page_fill_widgets (EditorPage *page, CalComponent *comp) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + CalComponentText text; + GList *alarms, *l; + GtkCList *clist; + + apage = ALARM_PAGE (page); + priv = apage->priv; + + clear_widgets (apage); + + /* Summary */ + cal_component_get_summary (comp, &text); + e_dialog_editable_set (priv->summary, text.value); + + /* List */ + if (!cal_component_has_alarms (comp)) + return; + + alarms = cal_component_get_alarm_uids (comp); + + clist = GTK_CLIST (priv->list); + for (l = alarms; l != NULL; l = l->next) { + CalComponentAlarm *ca, *ca_copy; + const char *auid; + + auid = l->data; + ca = cal_component_get_alarm (comp, auid); + g_assert (ca != NULL); + + ca_copy = cal_component_alarm_clone (ca); + cal_component_alarm_free (ca); + + append_reminder (apage, ca_copy); + } + cal_obj_uid_list_free (alarms); +} + +/* fill_component handler for the alarm page */ +static void +alarm_page_fill_component (EditorPage *page, CalComponent *comp) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + GList *list, *l; + GtkCList *clist; + int i; + + apage = ALARM_PAGE (page); + priv = apage->priv; + + /* Remove all the alarms from the component */ + + list = cal_component_get_alarm_uids (comp); + for (l = list; l; l = l->next) { + const char *auid; + + auid = l->data; + cal_component_remove_alarm (comp, auid); + } + cal_obj_uid_list_free (list); + + /* Add the new alarms */ + + clist = GTK_CLIST (priv->list); + for (i = 0; i < clist->rows; i++) { + CalComponentAlarm *alarm, *alarm_copy; + + alarm = gtk_clist_get_row_data (clist, i); + g_assert (alarm != NULL); + + /* We clone the alarm to maintain the invariant that the alarm + * structures in the list did *not* come from the component. + */ + + alarm_copy = cal_component_alarm_clone (alarm); + cal_component_add_alarm (comp, alarm); + cal_component_alarm_free (alarm_copy); + } +} + +/* set_summary handler for the alarm page */ +static void +alarm_page_set_summary (EditorPage *page, const char *summary) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + + apage = ALARM_PAGE (page); + priv = apage->priv; + + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->summary), apage); + e_utf8_gtk_entry_set_text (GTK_ENTRY (priv->summary), summary); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->summary), apage); +} + +/* get_summary handler for the alarm page */ +static char * +alarm_page_get_summary (EditorPage *page) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + + apage = ALARM_PAGE (page); + priv = apage->priv; + + return e_utf8_gtk_entry_get_text (GTK_ENTRY (priv->summary)); +} + +/* set_dtstart handler for the alarm page */ +static void +alarm_page_set_dtstart (EditorPage *page, time_t start) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + char str[128]; + struct tm tm; + + apage = ALARM_PAGE (page); + priv = apage->priv; + + tm = *localtime (&start); + strftime (str, sizeof (str), _("%A %b %d %Y %H:%M:%S"), &tm); + + gtk_entry_set_text (GTK_ENTRY (priv->starting_date), str); +} + + + +/* "relative" types */ +enum { + BEFORE, + AFTER +}; + +/* Time units */ +enum { + MINUTES, + HOURS, + DAYS +}; + +/* Option menu maps */ +static const int action_map[] = { + CAL_ALARM_DISPLAY, + CAL_ALARM_AUDIO, + CAL_ALARM_EMAIL, + CAL_ALARM_PROCEDURE, + -1 +}; + +static const int value_map[] = { + MINUTES, + HOURS, + DAYS, + -1 +}; + +static const int relative_map[] = { + BEFORE, + AFTER, + -1 +}; + +static const int time_map[] = { + CAL_ALARM_TRIGGER_RELATIVE_START, + CAL_ALARM_TRIGGER_RELATIVE_END, + -1 +}; + +/* Gets the widgets from the XML file and returns if they are all available. */ +static gboolean +get_widgets (AlarmPage *apage) +{ + AlarmPagePrivate *priv; + GtkWidget *toplevel; + + priv = apage->priv; + +#define GW(name) glade_xml_get_widget (priv->xml, name) + + toplevel = GW ("alarm-toplevel"); + priv->main = GW ("alarm-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->starting_date = GW ("starting-date"); + + priv->list = GW ("list"); + priv->add = GW ("add"); + priv->delete = GW ("delete"); + + priv->action = GW ("action"); + priv->interval_value = GW ("interval-value"); + priv->value_units = GW ("value-units"); + priv->relative = GW ("relative"); + priv->time = GW ("time"); + +#undef GW + + return (priv->summary + && priv->starting_date + && priv->list + && priv->add + && priv->delete + && priv->action + && priv->interval_value + && priv->value_units + && priv->relative + && priv->time); +} + +/* Callback used when the summary changes; we emit the notification signal. */ +static void +summary_changed_cb (GtkEditable *editable, gpointer data) +{ + AlarmPage *apage; + + apage = ALARM_PAGE (data); + editor_page_notify_summary_changed (EDITOR_PAGE (apage)); +} + +/* This is called when any field is changed; it notifies upstream. */ +static void +field_changed_cb (GtkWidget *widget, gpointer data) +{ + AlarmPage *apage; + + apage = ALARM_PAGE (data); + editor_page_notify_changed (EDITOR_PAGE (apage)); +} + +/* Callback used for the "add reminder" button */ +static void +add_clicked_cb (GtkButton *button, gpointer data) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + CalComponentAlarm *alarm; + CalAlarmTrigger trigger; + + apage = ALARM_PAGE (data); + priv = apage->priv; + + alarm = cal_component_alarm_new (); + + memset (&trigger, 0, sizeof (CalAlarmTrigger)); + trigger.type = e_dialog_option_menu_get (priv->time, time_map); + if (e_dialog_option_menu_get (priv->relative, relative_map) == BEFORE) + trigger.u.rel_duration.is_neg = 1; + else + trigger.u.rel_duration.is_neg = 0; + + switch (e_dialog_option_menu_get (priv->value_units, value_map)) { + case MINUTES: + trigger.u.rel_duration.minutes = e_dialog_spin_get_int (priv->interval_value); + break; + + case HOURS: + trigger.u.rel_duration.hours = e_dialog_spin_get_int (priv->interval_value); + break; + + case DAYS: + trigger.u.rel_duration.days = e_dialog_spin_get_int (priv->interval_value); + break; + + default: + g_assert_not_reached (); + } + cal_component_alarm_set_trigger (alarm, trigger); + + cal_component_alarm_set_action (alarm, e_dialog_option_menu_get (priv->action, action_map)); + + append_reminder (apage, alarm); +} + +/* Callback used for the "delete reminder" button */ +static void +delete_clicked_cb (GtkButton *button, gpointer data) +{ + AlarmPage *apage; + AlarmPagePrivate *priv; + GtkCList *clist; + CalComponentAlarm *alarm; + int sel; + + apage = ALARM_PAGE (data); + priv = apage->priv; + + clist = GTK_CLIST (priv->list); + if (!clist->selection) + return; + + sel = GPOINTER_TO_INT (clist->selection->data); + + alarm = gtk_clist_get_row_data (clist, sel); + g_assert (alarm != NULL); + cal_component_alarm_free (alarm); + gtk_clist_set_row_data (clist, sel, NULL); + + gtk_clist_remove (clist, sel); + if (sel >= clist->rows) + sel--; + + if (clist->rows > 0) + gtk_clist_select_row (clist, sel, 0); + else + gtk_widget_set_sensitive (priv->delete, FALSE); +} + +/* Hooks the widget signals */ +static void +init_widgets (AlarmPage *apage) +{ + AlarmPagePrivate *priv; + + priv = apage->priv; + + /* Summary */ + gtk_signal_connect (GTK_OBJECT (priv->summary), "changed", + GTK_SIGNAL_FUNC (summary_changed_cb), apage); + + /* Reminder buttons */ + gtk_signal_connect (GTK_OBJECT (priv->add), "clicked", + GTK_SIGNAL_FUNC (add_clicked_cb), apage); + gtk_signal_connect (GTK_OBJECT (priv->delete), "clicked", + GTK_SIGNAL_FUNC (delete_clicked_cb), apage); + + /* 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->add), "clicked", + GTK_SIGNAL_FUNC (field_changed_cb), apage); + gtk_signal_connect (GTK_OBJECT (priv->delete), "clicked", + GTK_SIGNAL_FUNC (field_changed_cb), apage); +} + + + +/** + * alarm_page_construct: + * @apage: An alarm page. + * + * Constructs an alarm page by loading its Glade data. + * + * Return value: The same object as @apage, or NULL if the widgets could not be + * created. + **/ +AlarmPage * +alarm_page_construct (AlarmPage *apage) +{ + AlarmPagePrivate *priv; + + priv = apage->priv; + + priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/alarm-page.glade", NULL); + if (!priv->xml) { + g_message ("alarm_page_construct(): Could not load the Glade XML file!"); + return NULL; + } + + if (!get_widgets (apage)) { + g_message ("alarm_page_construct(): Could not find all widgets in the XML file!"); + return NULL; + } + + init_widgets (apage); + + return apage; +} + +/** + * alarm_page_new: + * + * Creates a new alarm page. + * + * Return value: A newly-created alarm page, or NULL if the page could not be + * created. + **/ +AlarmPage * +alarm_page_new (void) +{ + AlarmPage *apage; + + apage = gtk_type_new (TYPE_ALARM_PAGE); + if (!alarm_page_construct (apage)) { + gtk_object_unref (GTK_OBJECT (apage)); + return NULL; + } + + return apage; +} diff --git a/calendar/gui/dialogs/alarm-page.glade b/calendar/gui/dialogs/alarm-page.glade new file mode 100644 index 0000000000..3a4a8d6765 --- /dev/null +++ b/calendar/gui/dialogs/alarm-page.glade @@ -0,0 +1,381 @@ + + + + + alarm-page + alarm-page + + . + pixmaps + C + True + True + False + False + False + + + + GtkWindow + alarm-toplevel + window1 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + True + False + + + GtkVBox + alarm-page + 4 + False + 4 + + + GtkFrame + frame33 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + False + True + + + + GtkTable + table13 + 4 + 2 + 2 + False + 2 + 2 + + + GtkLabel + label62 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + reminder-summary + + 0 + 1 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + GtkEntry + reminder-summary + True + True + True + 0 + + + 1 + 2 + 0 + 1 + 0 + 0 + True + False + True + False + True + False + + + + + GtkLabel + label63 + + GTK_JUSTIFY_CENTER + False + 0 + 0 + 0 + 0 + + 0 + 1 + 1 + 2 + 0 + 0 + False + False + False + False + True + True + + + + + GtkLabel + starting date + + GTK_JUSTIFY_CENTER + False + 0 + 0 + 0 + 0 + + 1 + 2 + 1 + 2 + 0 + 0 + False + False + False + False + True + True + + + + + + + GtkFrame + frame34 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + True + True + + + + GtkVBox + vbox53 + 4 + False + 4 + + + GtkHBox + hbox54 + False + 4 + + 0 + False + True + + + + GtkOptionMenu + reminder-action + True + Show a dialog +Play a sound +Send an email +Run a program + + 0 + + 0 + False + False + + + + + GtkSpinButton + reminder-interval-value + True + 1 + 0 + True + GTK_UPDATE_ALWAYS + False + False + 1 + 0 + 100 + 1 + 10 + 10 + + 0 + False + True + + + + + GtkOptionMenu + reminder-value-units + True + minute(s) +hour(s) +day(s) + + 0 + + 0 + False + False + + + + + GtkOptionMenu + reminder-relative + True + before +after + + 0 + + 0 + False + False + + + + + GtkOptionMenu + reminder-time + True + start of appointment +end of appointment + + 0 + + 0 + False + False + + + + + GtkButton + button9 + True + + GTK_RELIEF_NORMAL + + 0 + False + False + + + + + + GtkHBox + hbox55 + False + 0 + + 0 + True + True + + + + GtkScrolledWindow + scrolledwindow13 + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + 0 + True + True + + + + GtkCList + reminder-list + True + 1 + 80 + GTK_SELECTION_BROWSE + False + GTK_SHADOW_IN + + + GtkLabel + CList:title + label64 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + + + GtkVButtonBox + vbuttonbox2 + GTK_BUTTONBOX_START + 10 + 85 + 27 + 7 + 0 + + 0 + False + False + + + + GtkButton + reminder-add + True + True + + GTK_RELIEF_NORMAL + + + + GtkButton + reminder-delete + True + True + + GTK_RELIEF_NORMAL + + + + + + + + + diff --git a/calendar/gui/dialogs/alarm-page.h b/calendar/gui/dialogs/alarm-page.h new file mode 100644 index 0000000000..1cb0b301bd --- /dev/null +++ b/calendar/gui/dialogs/alarm-page.h @@ -0,0 +1,64 @@ +/* Evolution calendar - Alarm page of the calendar component dialogs + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero + * Miguel de Icaza + * Seth Alves + * JP Rosevear + * + * 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. + */ + +#ifndef ALARM_PAGE_H +#define ALARM_PAGE_H + +#include "editor-page.h" + +BEGIN_GNOME_DECLS + + + +#define TYPE_ALARM_PAGE (alarm_page_get_type ()) +#define ALARM_PAGE(obj) (GTK_CHECK_CAST ((obj), TYPE_ALARM_PAGE, AlarmPage)) +#define ALARM_PAGE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), TYPE_ALARM_PAGE, \ + AlarmPageClass)) +#define IS_ALARM_PAGE(obj) (GTK_CHECK_TYPE ((obj), TYPE_ALARM_PAGE)) +#define IS_ALARM_PAGE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), TYPE_ALARM_PAGE)) + +typedef struct _AlarmPagePrivate AlarmPagePrivate; + +typedef struct { + EditorPage page; + + /* Private data */ + AlarmPagePrivate *priv; +} AlarmPage; + +typedef struct { + EditorPageClass parent_class; +} AlarmPageClass; + +GtkType alarm_page_get_type (void); + +AlarmPage *alarm_page_construct (AlarmPage *apage); + +AlarmPage *alarm_page_new (void); + + + +END_GNOME_DECLS + +#endif diff --git a/calendar/gui/dialogs/editor-page.c b/calendar/gui/dialogs/editor-page.c new file mode 100644 index 0000000000..b46b247191 --- /dev/null +++ b/calendar/gui/dialogs/editor-page.c @@ -0,0 +1,259 @@ +/* Evolution calendar - Base class for calendar component editor pages + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero + * + * 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 +#endif + +#include +#include "editor-page.h" + + + +static void editor_page_class_init (EditorPageClass *class); + +/* Signal IDs */ + +enum { + CHANGED, + SUMMARY_CHANGED, + LAST_SIGNAL +}; + +static guint editor_page_signals[LAST_SIGNAL]; + +#define CLASS(page) (EDITOR_PAGE_CLASS (GTK_OBJECT (page)->klass)) + + + +/** + * editor_page_get_type: + * + * Registers the #EditorPage class if necessary, and returns the type ID + * associated to it. + * + * Return value: The type ID of the #EditorPage class. + **/ +GtkType +editor_page_get_type (void) +{ + static GtkType editor_page_type = 0; + + if (!editor_page_type) { + static const GtkTypeInfo editor_page_info = { + "EditorPage", + sizeof (EditorPage), + sizeof (EditorPageClass), + (GtkClassInitFunc) editor_page_class_init, + (GtkObjectInitFunc) NULL, + NULL, /* reserved_1 */ + NULL, /* reserved_2 */ + (GtkClassInitFunc) NULL + }; + + editor_page_type = gtk_type_unique (GTK_TYPE_OBJECT, &editor_page_info); + } + + return editor_page_type; +} + +/* Class initialization function for the abstract editor page */ +static void +editor_page_class_init (EditorPageClass *class) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass *) class; + + editor_page_signals[CHANGED] = + gtk_signal_new ("changed", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (EditorPageClass, changed), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + + editor_page_signals[SUMMARY_CHANGED] = + gtk_signal_new ("summary_changed", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (EditorPageClass, summary_changed), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + + gtk_object_class_add_signals (object_class, editor_page_signals, LAST_SIGNAL); + + class->changed = NULL; + class->summary_changed = NULL; + + class->get_widget = NULL; + class->fill_widgets = NULL; + class->fill_component = NULL; + class->set_summary = NULL; + class->get_summary = NULL; + class->set_dtstart = NULL; +} + + + +/** + * editor_page_get_widget: + * @page: An editor page. + * + * Queries the main widget of an editor page. + * + * Return value: The widget that is the page's upper container. It should + * normally be inserted in a notebook widget. + **/ +GtkWidget * +editor_page_get_widget (EditorPage *page) +{ + g_return_val_if_fail (page != NULL, NULL); + g_return_val_if_fail (IS_EDITOR_PAGE (page), NULL); + + g_assert (CLASS (page)->get_widget != NULL); + return (* CLASS (page)->get_widget) (page); +} + +/** + * editor_page_fill_widgets: + * @page: An editor page. + * @comp: A calendar component. + * + * Fills the widgets of an editor page with the data from a calendar component. + **/ +void +editor_page_fill_widgets (EditorPage *page, CalComponent *comp) +{ + g_return_if_fail (page != NULL); + g_return_if_fail (IS_EDITOR_PAGE (page)); + g_return_if_fail (comp != NULL); + + g_assert (CLASS (page)->fill_widgets != NULL); + (* CLASS (page)->fill_widgets) (page, comp); +} + +/** + * editor_page_fill_component: + * @page: An editor page. + * @comp: A calendar component. + * + * Takes the data from the widgets of an editor page and sets it on a calendar + * component, replacing the contents of the properties that the editor page + * knows how to manipulate. + **/ +void +editor_page_fill_component (EditorPage *page, CalComponent *comp) +{ + g_return_if_fail (page != NULL); + g_return_if_fail (IS_EDITOR_PAGE (page)); + g_return_if_fail (comp != NULL); + + g_assert (CLASS (page)->fill_component != NULL); + (* CLASS (page)->fill_component) (page, comp); +} + +/** + * editor_page_set_summary: + * @page: An editor page. + * @summary: Summary string to set in the page's widgets, which must be encoded + * in UTF8. + * + * Sets the calendar component summary string in an editor page. + **/ +void +editor_page_set_summary (EditorPage *page, const char *summary) +{ + g_return_if_fail (page != NULL); + g_return_if_fail (IS_EDITOR_PAGE (page)); + g_return_if_fail (summary != NULL); + + g_assert (CLASS (page)->set_summary != NULL); + (* CLASS (page)->set_summary) (page, summary); +} + +/** + * editor_page_get_summary: + * @page: An editor page. + * + * Queries the current summary string in an editor page. + * + * Return value: Summary string in UTF8; must be freed by the caller. + **/ +char * +editor_page_get_summary (EditorPage *page) +{ + g_return_val_if_fail (page != NULL, NULL); + g_return_val_if_fail (IS_EDITOR_PAGE (page), NULL); + + g_assert (CLASS (page)->get_summary != NULL); + return (* CLASS (page)->get_summary) (page); +} + +/** + * editor_page_set_dtstart: + * @page: An editor page. + * @start: Start date for calendar component. + * + * Sets the calendar component DTSTART in an editor page. + **/ +void +editor_page_set_dtstart (EditorPage *page, time_t start) +{ + g_return_if_fail (page != NULL); + g_return_if_fail (IS_EDITOR_PAGE (page)); + g_return_if_fail (start != -1); + + g_assert (CLASS (page)->set_dtstart != NULL); + (* CLASS (page)->set_dtstart) (page, start); +} + +/** + * editor_page_notify_changed: + * @page: An editor page. + * + * Makes an editor page emit the "changed" signal. This is meant to be + * used only by page implementations. + **/ +void +editor_page_notify_changed (EditorPage *page) +{ + g_return_if_fail (page != NULL); + g_return_if_fail (IS_EDITOR_PAGE (page)); + + gtk_signal_emit (GTK_OBJECT (page), editor_page_signals[CHANGED]); +} + +/** + * editor_page_notify_summary_changed: + * @page: An editor page. + * + * Makes an editor page emit the "summary_changed" signal. This is meant to be + * used only by page implementations. + **/ +void +editor_page_notify_summary_changed (EditorPage *page) +{ + g_return_if_fail (page != NULL); + g_return_if_fail (IS_EDITOR_PAGE (page)); + + gtk_signal_emit (GTK_OBJECT (page), editor_page_signals[SUMMARY_CHANGED]); +} diff --git a/calendar/gui/dialogs/editor-page.h b/calendar/gui/dialogs/editor-page.h new file mode 100644 index 0000000000..3f9587c9de --- /dev/null +++ b/calendar/gui/dialogs/editor-page.h @@ -0,0 +1,86 @@ +/* Evolution calendar - Base class for calendar component editor pages + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero + * + * 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. + */ + +#ifndef EDITOR_PAGE_H +#define EDITOR_PAGE_H + +#include +#include +#include +#include + +BEGIN_GNOME_DECLS + + + +#define TYPE_EDITOR_PAGE (editor_page_get_type ()) +#define EDITOR_PAGE(obj) (GTK_CHECK_CAST ((obj), TYPE_EDITOR_PAGE, EditorPage)) +#define EDITOR_PAGE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), TYPE_EDITOR_PAGE, \ + EditorPageClass)) +#define IS_EDITOR_PAGE(obj) (GTK_CHECK_TYPE ((obj), TYPE_EDITOR_PAGE)) +#define IS_EDITOR_PAGE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), TYPE_EDITOR_PAGE)) + +typedef struct { + GtkObject object; +} EditorPage; + +typedef struct { + GtkObjectClass parent_class; + + /* Notification signals */ + + void (* changed) (EditorPage *page); + void (* summary_changed) (EditorPage *page); + void (* dtstart_changed) (EditorPage *page); + + /* Virtual methods */ + + GtkWidget *(* get_widget) (EditorPage *page); + + void (* fill_widgets) (EditorPage *page, CalComponent *comp); + void (* fill_component) (EditorPage *page, CalComponent *comp); + + void (* set_summary) (EditorPage *page, const char *summary); + char *(* get_summary) (EditorPage *page); + + void (* set_dtstart) (EditorPage *page, time_t start); +} EditorPageClass; + +GtkType editor_page_get_type (void); + +GtkWidget *editor_page_get_widget (EditorPage *page); + +void editor_page_fill_widgets (EditorPage *page, CalComponent *comp); +void editor_page_fill_component (EditorPage *page, CalComponent *comp); + +void editor_page_set_summary (EditorPage *page, const char *summary); +char *editor_page_get_summary (EditorPage *page); + +void editor_page_set_dtstart (EditorPage *page, time_t start); + +void editor_page_notify_changed (EditorPage *page); +void editor_page_notify_summary_changed (EditorPage *page); + + + +END_GNOME_DECLS + +#endif 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 + * Miguel de Icaza + * Seth Alves + * JP Rosevear + * + * 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 +#endif + +#include +#include +#include +#include +#include +#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; +} diff --git a/calendar/gui/dialogs/event-page.glade b/calendar/gui/dialogs/event-page.glade new file mode 100644 index 0000000000..76d2d4043e --- /dev/null +++ b/calendar/gui/dialogs/event-page.glade @@ -0,0 +1,429 @@ + + + + + event-page + event-page + + . + pixmaps + C + True + True + False + False + False + + + + GtkWindow + event-toplevel + window1 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + True + False + + + GtkVBox + event-page + 4 + False + 6 + + + GtkTable + table11 + 1 + 2 + False + 4 + 4 + + 0 + False + True + + + + GtkLabel + label56 + + GTK_JUSTIFY_CENTER + False + 7.45058e-09 + 0.5 + 0 + 0 + general-summary + + 0 + 1 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + GtkEntry + general-summary + True + True + True + 0 + + + 1 + 2 + 0 + 1 + 0 + 0 + True + False + False + False + True + False + + + + + + GtkFrame + frame31 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + False + False + + + + GtkTable + table12 + 4 + 2 + 3 + False + 4 + 4 + + + GtkLabel + label57 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 0 + 1 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label58 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 0 + 1 + 1 + 2 + 0 + 0 + False + False + False + False + True + False + + + + + GtkCheckButton + all-day-event + True + + False + True + + 2 + 3 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + Custom + start-time + make_date_edit + + + 0 + 0 + Tue, 16 May 2000 19:11:05 GMT + + 1 + 2 + 0 + 1 + 0 + 0 + False + True + False + False + False + True + + + + + Custom + end-time + make_date_edit + 0 + 0 + Tue, 16 May 2000 19:11:10 GMT + + 1 + 2 + 1 + 2 + 0 + 0 + False + True + False + False + False + True + + + + + + + GtkScrolledWindow + scrolledwindow12 + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + 0 + True + True + + + + GtkText + description + True + True + + + + + + GtkFrame + frame32 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + False + False + + + + GtkHBox + hbox52 + 2 + False + 4 + + + GtkRadioButton + classification-public + True + + True + True + classification_radio_group + + 0 + False + False + + + + + GtkRadioButton + classification-private + True + + False + True + classification_radio_group + + 0 + False + False + + + + + GtkRadioButton + classification-confidential + True + + False + True + classification_radio_group + + 0 + False + False + + + + + + + GtkHBox + hbox53 + False + 2 + + 0 + False + True + + + + GtkButton + contacts-button + True + GTK_RELIEF_NORMAL + + 0 + False + False + + + + GtkLabel + label59 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 4 + 0 + + + + + GtkEntry + contacts + True + True + True + 0 + + + 0 + True + True + + + + + GtkButton + categories-button + True + GTK_RELIEF_NORMAL + + 0 + False + False + + + + GtkLabel + label60 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 4 + 0 + + + + + GtkEntry + categories + True + True + True + 0 + + + 0 + True + True + + + + + + + diff --git a/calendar/gui/dialogs/event-page.h b/calendar/gui/dialogs/event-page.h new file mode 100644 index 0000000000..85281b9800 --- /dev/null +++ b/calendar/gui/dialogs/event-page.h @@ -0,0 +1,70 @@ +/* Evolution calendar - Main page of the event editor dialog + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero + * Miguel de Icaza + * Seth Alves + * JP Rosevear + * + * 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. + */ + +#ifndef EVENT_PAGE_H +#define EVENT_PAGE_H + +#include "editor-page.h" + +BEGIN_GNOME_DECLS + + + +#define TYPE_EVENT_PAGE (event_page_get_type ()) +#define EVENT_PAGE(obj) (GTK_CHECK_CAST ((obj), TYPE_EVENT_PAGE, EventPage)) +#define EVENT_PAGE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), TYPE_EVENT_PAGE, \ + EventPageClass)) +#define IS_EVENT_PAGE(obj) (GTK_CHECK_TYPE ((obj), TYPE_EVENT_PAGE)) +#define IS_EVENT_PAGE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), TYPE_EVENT_PAGE)) + +typedef struct _EventPagePrivate EventPagePrivate; + +typedef struct { + EditorPage page; + + /* Private data */ + EventPagePrivate *priv; +} EventPage; + +typedef struct { + EditorPageClass parent_class; + + /* Notification signals */ + + void (* dates_changed) (EventPage *epage); +} EventPageClass; + +GtkType event_page_get_type (void); + +EventPage *event_page_construct (EventPage *epage); + +EventPage *event_page_new (void); + +time_t event_page_get_dtstart (EventPage *epage); + + + +END_GNOME_DECLS + +#endif diff --git a/calendar/gui/dialogs/recurrence-page.c b/calendar/gui/dialogs/recurrence-page.c new file mode 100644 index 0000000000..0b0eace9cf --- /dev/null +++ b/calendar/gui/dialogs/recurrence-page.c @@ -0,0 +1,900 @@ +/* Evolution calendar - Recurrence page of the calendar component dialogs + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero + * Miguel de Icaza + * Seth Alves + * JP Rosevear + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include "e-util/e-dialog-widgets.h" +#include "recurrence-page.h" + + + +/* Private part of the RecurrencePage structure */ +struct _RecurrencePagePrivate { + /* Glade XML data */ + GladeXML *xml; + + /* Widgets from the Glade file */ + + GtkWidget *main; + + GtkWidget *summary; + GtkWidget *starting_date; + + GtkWidget *none; + GtkWidget *simple; + GtkWidget *custom; + + GtkWidget *params; + GtkWidget *interval_value; + GtkWidget *interval_unit; + GtkWidget *special; + GtkWidget *ending_menu; + GtkWidget *ending_special; + GtkWidget *custom_warning_bin; + + /* For weekly recurrences, created by hand */ + GtkWidget *weekday_picker; + guint8 weekday_day_mask; + guint8 weekday_blocked_day_mask; + + /* For monthly recurrences, created by hand */ + GtkWidget *month_index_spin; + int month_index; + + GtkWidget *month_day_menu; + enum month_day_options month_day; + + /* For ending date, created by hand */ + GtkWidget *ending_date_edit; + time_t ending_date; + + /* For ending count of occurrences, created by hand */ + GtkWidget *ending_count_spin; + int ending_count; + + /* More widgets from the Glade file */ + + GtkWidget *exception_date; + GtkWidget *exception_list; + GtkWidget *exception_add; + GtkWidget *exception_modify; + GtkWidget *exception_delete; + + GtkWidget *preview_bin; + + /* For the recurrence preview, the actual widget */ + GtkWidget *preview_calendar; +}; + + + +static void recurrence_page_class_init (RecurrencePageClass *class); +static void recurrence_page_init (RecurrencePage *rpage); +static void recurrence_page_destroy (RecurrencePage *rpage); + +static GtkWidget *recurrence_page_get_widget (EditorPage *page); +static void recurrence_page_fill_widgets (EditorPage *page, CalComponent *comp); +static void recurrence_page_fill_component (EditorPage *page, CalComponent *comp); +static void recurrence_page_set_summary (EditorPage *page, const char *summary); +static char *recurrence_page_get_summary (EditorPage *page); +static void recurrence_page_set_dtstart (EditorPage *page, time_t start); + +static EditorPageClass *parent_class = NULL; + + + +/** + * recurrence_page_get_type: + * + * Registers the #RecurrencePage class if necessary, and returns the type ID + * associated to it. + * + * Return value: The type ID of the #RecurrencePage class. + **/ +GtkType +recurrence_page_get_type (void) +{ + static GtkType recurrence_page_type; + + if (!recurrence_page_type) { + static const GtkTypeInfo recurrence_page_info = { + "RecurrencePage", + sizeof (RecurrencePage), + sizeof (RecurrencePageClass), + (GtkClassInitFunc) recurrence_page_class_init, + (GtkObjectInitFunc) recurrence_page_init, + NULL, /* reserved_1 */ + NULL, /* reserved_2 */ + (GtkClassInitFunc) NULL + }; + + recurrence_page_type = gtk_type_unique (EDITOR_PAGE_TYPE, &recurrence_page_info); + } + + return recurrence_page_type; +} + +/* Class initialization function for the recurrence page */ +static void +recurrence_page_class_init (RecurrencePageClass *class) +{ + EditorPageClass *editor_page_class; + GtkObjectClass *object_class; + + editor_page_class = (EditorPageClass *) class; + object_class = (GtkObjectClass *) class; + + parent_class = gtk_type_class (EDITOR_PAGE_TYPE); + + editor_page_class->get_widget = recurrence_page_get_widget; + editor_page_class->fill_widgets = recurrence_page_fill_widgets; + editor_page_class->fill_component = recurrence_page_fill_component; + editor_page_class->set_summary = recurrence_page_set_summary; + editor_page_class->get_summary = recurrence_page_get_summary; + editor_page_class->set_dtstart = recurrence_page_set_dtstart; + + object_class->destroy = recurrence_page_destroy; +} + +/* Object initialization function for the recurrence page */ +static void +recurrence_page_init (RecurrencePage *rpage) +{ + RecurrencePagePrivate *priv; + + priv = g_new0 (RecurrencePagePrivate, 1); + rpage->priv = priv; + + priv->xml = NULL; + + priv->main = NULL; + priv->summary = NULL; + priv->starting_date = NULL; + priv->none = NULL; + priv->simple = NULL; + priv->custom = NULL; + priv->params = NULL; + priv->interval_value = NULL; + priv->interval_unit = NULL; + priv->special = NULL; + priv->ending_menu = NULL; + priv->ending_special = NULL; + priv->custom_warning_bin = NULL; + priv->weekday_picker = NULL; + priv->month_index_spin = NULL; + priv->month_day_menu = NULL; + priv->ending_date_edit = NULL; + priv->ending_count_spin = NULL; + priv->exception_date = NULL; + priv->exception_list = NULL; + priv->exception_add = NULL; + priv->exception_modify = NULL; + priv->exception_delete = NULL; + priv->preview_bin = NULL; + priv->preview_calendar = NULL; +} + +/* Frees the rows and the row data in the exceptions GtkCList */ +static void +free_exception_clist_data (RecurrencePage *rpage) +{ + RecurrencePagePrivate *priv; + GtkCList *clist; + int i; + + priv = rpage->priv; + + clist = GTK_CLIST (priv->exception_list); + + for (i = 0; i < clist->rows; i++) { + gpointer data; + + data = gtk_clist_get_row_data (clist, i); + g_free (data); + gtk_clist_set_row_data (clist, i, NULL); + } + + gtk_clist_clear (clist); +} + +/* Destroy handler for the recurrence page */ +static void +recurrence_page_destroy (GtkObject *object) +{ + RecurrencePage *rpage; + RecurrencePagePrivate *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_RECURRENCE_PAGE (object)); + + rpage = RECURRENCE_PAGE (object); + priv = rpage->priv; + + if (priv->xml) { + gtk_object_unref (GTK_OBJECT (priv->xml)); + priv->xml = NULL; + } + + free_exception_clist_data (rpage); + + g_free (priv); + rpage->priv = NULL; + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + + + +/* get_widget handler for the recurrence page */ +static GtkWidget * +recurrence_page_get_widget (EditorPage *page) +{ + RecurrencePage *rpage; + RecurrencePagePrivate *priv; + + rpage = RECURRENCE_PAGE (page); + priv = rpage->priv; + + return priv->main; +} + +/* Fills the widgets with default values */ +static void +clear_widgets (RecurrencePage *rpage) +{ + RecurrencePagePrivate *priv; + + priv = rpage->priv; + + priv->weekday_day_mask = 0; + + priv->month_index = 1; + priv->month_day = MONTH_DAY_NTH; + + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->none), rpage); + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->simple), rpage); + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->custom), rpage); + e_dialog_radio_set (priv->none, RECUR_NONE, type_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->none), rpage); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->simple), rpage); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->custom), rpage); + + adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->interval_value)); + gtk_signal_handler_block_by_data (GTK_OBJECT (adj), rpage); + e_dialog_spin_set (priv->interval_value, 1); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (adj), rpage); + + menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (priv->interval_unit)); + gtk_signal_handler_block_by_data (GTK_OBJECT (menu), rpage); + e_dialog_option_menu_set (priv->interval_unit, ICAL_DAILY_RECURRENCE, freq_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (menu), rpage); + + priv->ending_date = time (NULL); + priv->ending_count = 1; + + menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (priv->ending_menu)); + gtk_signal_handler_block_by_data (GTK_OBJECT (menu), rpage); + e_dialog_option_menu_set (priv->ending_menu, ENDING_FOREVER, ending_types_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (menu), rpage); + + /* Exceptions list */ + free_exception_clist_data (GTK_CLIST (priv->exception_list)); +} + +/* Builds a static string out of an exception date */ +static char * +get_exception_string (time_t t) +{ + static char buf[256]; + + strftime (buf, sizeof (buf), _("%a %b %d %Y"), localtime (&t)); + return buf; +} + +/* Appends an exception date to the list */ +static void +append_exception (RecurrencePage *rpage, time_t t) +{ + RecurrencePagePrivate *priv; + time_t *tt; + char *c[1]; + int i; + GtkCList *clist; + + priv = rpage->priv; + + tt = g_new (time_t, 1); + *tt = t; + + clist = GTK_CLIST (priv->exception_list); + + gtk_signal_handler_block_by_data (GTK_OBJECT (clist), rpage); + + c[0] = get_exception_string (t); + i = gtk_clist_append (clist, c); + + gtk_clist_set_row_data (clist, i, tt); + + gtk_clist_select_row (clist, i, 0); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), rpage); + + e_date_edit_set_time (E_DATE_EDIT (priv->exception_date), t); + + gtk_widget_set_sensitive (priv->exception_modify, TRUE); + gtk_widget_set_sensitive (priv->exception_delete, TRUE); +} + +/* Fills in the exception widgets with the data from the calendar component */ +static void +fill_exception_widgets (RecurrencePage *rpage, CalComponent *comp) +{ + RecurrencePagePrivate *priv; + GSList *list, *l; + gboolean added; + + priv = rpage->priv; + + cal_component_get_exdate_list (comp, &list); + + added = FALSE; + + for (l = list; l; l = l->next) { + CalComponentDateTime *cdt; + time_t ext; + + added = TRUE; + + cdt = l->data; + ext = icaltime_as_timet (*cdt->value); + append_exception (rpage, ext); + } + + cal_component_free_exdate_list (list); + + if (added) + gtk_clist_select_row (GTK_CLIST (priv->exception_list), 0, 0); +} + +/* Computes a weekday mask for the start day of a calendar component, for use in + * a WeekdayPicker widget. + */ +static guint8 +get_start_weekday_mask (CalComponent *comp) +{ + CalComponentDateTime dt; + guint8 retval; + + cal_component_get_dtstart (comp, &dt); + + if (dt.value) { + time_t t; + struct tm tm; + + t = icaltime_as_timet (*dt.value); + tm = *localtime (&t); + + retval = 0x1 << tm.tm_wday; + } else + retval = 0; + + cal_component_free_datetime (&dt); + + return retval; +} + +/* Sets some sane defaults for the data sources for the recurrence special + * widgets, even if they will not be used immediately. + */ +static void +set_special_defaults (RecurrencePage *rpage) +{ + RecurrencePagePrivate *priv; + guint8 mask; + + priv = rpage->priv; + + mask = get_start_weekday_mask (priv->comp); + + priv->weekday_day_mask = mask; + priv->weekday_blocked_day_mask = mask; +} + +/* Sensitizes the recurrence widgets based on the state of the recurrence type + * radio group. + */ +static void +sensitize_recur_widgets (RecurrencePage *rpage) +{ + RecurrencePagePrivate *priv; + enum recur_type type; + GtkWidget *label; + + priv = rpage->priv; + + type = e_dialog_radio_get (priv->none, type_map); + + if (GTK_BIN (priv->custom_warning_bin)->child) + gtk_widget_destroy (GTK_BIN (priv->custom_warning_bin)->child); + + switch (type) { + case RECUR_NONE: + gtk_widget_set_sensitive (priv->params, FALSE); + gtk_widget_show (priv->params); + gtk_widget_hide (priv->custom_warning_bin); + break; + + case RECUR_SIMPLE: + gtk_widget_set_sensitive (priv->params, TRUE); + gtk_widget_show (priv->params); + gtk_widget_hide (priv->custom_warning_bin); + break; + + case RECUR_CUSTOM: + gtk_widget_set_sensitive (priv->params, FALSE); + gtk_widget_hide (priv->params); + + label = gtk_label_new (_("This appointment contains recurrences that Evolution " + "cannot edit.")); + gtk_container_add (GTK_CONTAINER (priv->custom_warning_bin), label); + gtk_widget_show_all (priv->custom_warning_bin); + break; + + default: + g_assert_not_reached (); + } +} + +/* Re-tags the recurrence preview calendar based on the current information of + * the widgets in the recurrence page. + */ +static void +preview_recur (RecurrencePage *rpage) +{ + RecurrencePagePrivate *priv; + CalComponent *comp; + CalComponentDateTime cdt; + GSList *l; + + priv = rpage->priv; + g_assert (priv->comp != NULL); + + /* Create a scratch component with the start/end and + * recurrence/excepttion information from the one we are editing. + */ + + comp = cal_component_new (); + cal_component_set_new_vtype (comp, CAL_COMPONENT_EVENT); + + cal_component_get_dtstart (priv->comp, &cdt); + cal_component_set_dtstart (comp, &cdt); + cal_component_free_datetime (&cdt); + + cal_component_get_dtend (priv->comp, &cdt); + cal_component_set_dtend (comp, &cdt); + cal_component_free_datetime (&cdt); + + cal_component_get_exdate_list (priv->comp, &l); + cal_component_set_exdate_list (comp, l); + cal_component_free_exdate_list (l); + + cal_component_get_exrule_list (priv->comp, &l); + cal_component_set_exrule_list (comp, l); + cal_component_free_recur_list (l); + + cal_component_get_rdate_list (priv->comp, &l); + cal_component_set_rdate_list (comp, l); + cal_component_free_period_list (l); + + cal_component_get_rrule_list (priv->comp, &l); + cal_component_set_rrule_list (comp, l); + cal_component_free_recur_list (l); + + recur_to_comp_object (rpage, comp); + + tag_calendar_by_comp (E_CALENDAR (priv->preview_calendar), comp); + gtk_object_unref (GTK_OBJECT (comp)); +} + +/* fill_widgets handler for the recurrence page. This function is particularly + * tricky because it has to discriminate between recurrences we support for + * editing and the ones we don't. We only support at most one recurrence rule; + * no rdates or exrules (exdates are handled just fine elsewhere). + */ +static void +recurrence_page_fill_widgets (EditorPage *page, CalComponent *comp) +{ + RecurrencePage *rpage; + RecurrencePagePrivate *priv; + GSList *rrule_list; + int len; + struct icalrecurrencetype *r; + int n_by_second, n_by_minute, n_by_hour; + int n_by_day, n_by_month_day, n_by_year_day; + int n_by_week_no, n_by_month, n_by_set_pos; + GtkWidget *menu; + GtkAdjustment *adj; + + rpage = RECURRENCE_PAGE (page); + priv = rpage->priv; + + clear_widgets (rpage); + + fill_exception_widgets (rpage, comp); + + /* Set up defaults for the special widgets */ + set_special_defaults (rpage); + + /* No recurrences? */ + + if (!cal_component_has_rdates (comp) + && !cal_component_has_rrules (comp) + && !cal_component_has_exrules (comp)) { + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->none), rpage); + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->simple), rpage); + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->custom), rpage); + e_dialog_radio_set (priv->none, RECUR_NONE, type_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->none), rpage); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->simple), rpage); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->custom), rpage); + + gtk_widget_set_sensitive (priv->custom, FALSE); + + sensitize_recur_widgets (rpage); + preview_recur (rpage); + return; + } + + /* See if it is a custom set we don't support */ + + cal_component_get_rrule_list (comp, &rrule_list); + len = g_slist_length (rrule_list); + if (len > 1 + || cal_component_has_rdates (comp) + || cal_component_has_exrules (comp)) + goto custom; + + /* Down to one rule, so test that one */ + + g_assert (len == 1); + r = rrule_list->data; + + /* Any funky frequency? */ + + if (r->freq == ICAL_SECONDLY_RECURRENCE + || r->freq == ICAL_MINUTELY_RECURRENCE + || r->freq == ICAL_HOURLY_RECURRENCE) + goto custom; + + /* Any funky shit? */ + +#define N_HAS_BY(field) (count_by_xxx (field, sizeof (field) / sizeof (field[0]))) + + n_by_second = N_HAS_BY (r->by_second); + n_by_minute = N_HAS_BY (r->by_minute); + n_by_hour = N_HAS_BY (r->by_hour); + n_by_day = N_HAS_BY (r->by_day); + n_by_month_day = N_HAS_BY (r->by_month_day); + n_by_year_day = N_HAS_BY (r->by_year_day); + n_by_week_no = N_HAS_BY (r->by_week_no); + n_by_month = N_HAS_BY (r->by_month); + n_by_set_pos = N_HAS_BY (r->by_set_pos); + + if (n_by_second != 0 + || n_by_minute != 0 + || n_by_hour != 0) + goto custom; + + /* Filter the funky shit based on the frequency; if there is nothing + * weird we can actually set the widgets. + */ + + switch (r->freq) { + case ICAL_DAILY_RECURRENCE: + if (n_by_day != 0 + || n_by_month_day != 0 + || n_by_year_day != 0 + || n_by_week_no != 0 + || n_by_month != 0 + || n_by_set_pos != 0) + goto custom; + + menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (priv->interval_unit)); + gtk_signal_handler_block_by_data (GTK_OBJECT (menu), rpage); + e_dialog_option_menu_set (priv->interval_unit, ICAL_DAILY_RECURRENCE, freq_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (menu), rpage); + break; + + case ICAL_WEEKLY_RECURRENCE: { + int i; + guint8 day_mask; + + if (n_by_month_day != 0 + || n_by_year_day != 0 + || n_by_week_no != 0 + || n_by_month != 0 + || n_by_set_pos != 0) + goto custom; + + day_mask = 0; + + for (i = 0; i < 8 && r->by_day[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) { + enum icalrecurrencetype_weekday weekday; + int pos; + + weekday = icalrecurrencetype_day_day_of_week (r->by_day[i]); + pos = icalrecurrencetype_day_position (r->by_day[i]); + + if (pos != 0) + goto custom; + + switch (weekday) { + case ICAL_SUNDAY_WEEKDAY: + day_mask |= 1 << 0; + break; + + case ICAL_MONDAY_WEEKDAY: + day_mask |= 1 << 1; + break; + + case ICAL_TUESDAY_WEEKDAY: + day_mask |= 1 << 2; + break; + + case ICAL_WEDNESDAY_WEEKDAY: + day_mask |= 1 << 3; + break; + + case ICAL_THURSDAY_WEEKDAY: + day_mask |= 1 << 4; + break; + + case ICAL_FRIDAY_WEEKDAY: + day_mask |= 1 << 5; + break; + + case ICAL_SATURDAY_WEEKDAY: + day_mask |= 1 << 6; + break; + + default: + break; + } + } + + priv->weekday_day_mask = day_mask; + + menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (priv->interval_unit)); + gtk_signal_handler_block_by_data (GTK_OBJECT (menu), rpage); + e_dialog_option_menu_set (priv->interval_unit, ICAL_WEEKLY_RECURRENCE, freq_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (menu), rpage); + break; + } + + case ICAL_MONTHLY_RECURRENCE: + if (n_by_year_day != 0 + || n_by_week_no != 0 + || n_by_month != 0 + || n_by_set_pos != 0) + goto custom; + + if (n_by_month_day == 1) { + int nth; + + nth = r->by_month_day[0]; + if (nth < 1) + goto custom; + + priv->month_index = nth; + priv->month_day = MONTH_DAY_NTH; + } else if (n_by_day == 1) { + enum icalrecurrencetype_weekday weekday; + int pos; + enum month_day_options month_day; + + weekday = icalrecurrencetype_day_day_of_week (r->by_day[0]); + pos = icalrecurrencetype_day_position (r->by_day[0]); + + if (pos < 1) + goto custom; + + switch (weekday) { + case ICAL_MONDAY_WEEKDAY: + month_day = MONTH_DAY_MON; + break; + + case ICAL_TUESDAY_WEEKDAY: + month_day = MONTH_DAY_TUE; + break; + + case ICAL_WEDNESDAY_WEEKDAY: + month_day = MONTH_DAY_WED; + break; + + case ICAL_THURSDAY_WEEKDAY: + month_day = MONTH_DAY_THU; + break; + + case ICAL_FRIDAY_WEEKDAY: + month_day = MONTH_DAY_FRI; + break; + + case ICAL_SATURDAY_WEEKDAY: + month_day = MONTH_DAY_SAT; + break; + + case ICAL_SUNDAY_WEEKDAY: + month_day = MONTH_DAY_SUN; + break; + + default: + goto custom; + } + + priv->month_index = pos; + priv->month_day = month_day; + } else + goto custom; + + menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (priv->interval_unit)); + gtk_signal_handler_block_by_data (GTK_OBJECT (menu), rpage); + e_dialog_option_menu_set (priv->interval_unit, ICAL_MONTHLY_RECURRENCE, freq_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (menu), rpage); + break; + + case ICAL_YEARLY_RECURRENCE: + if (n_by_day != 0 + || n_by_month_day != 0 + || n_by_year_day != 0 + || n_by_week_no != 0 + || n_by_month != 0 + || n_by_set_pos != 0) + goto custom; + + menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (priv->interval_unit)); + gtk_signal_handler_block_by_data (GTK_OBJECT (menu), rpage); + e_dialog_option_menu_set (priv->interval_unit, ICAL_YEARLY_RECURRENCE, freq_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (menu), rpage); + break; + + default: + goto custom; + } + + /* If we got here it means it is a simple recurrence */ + + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->none), rpage); + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->simple), rpage); + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->custom), rpage); + e_dialog_radio_set (priv->simple, RECUR_SIMPLE, type_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->none), rpage); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->simple), rpage); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->custom), rpage); + + gtk_widget_set_sensitive (priv->custom, FALSE); + + sensitize_recur_widgets (rpage); + make_recurrence_special (rpage); + + adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->interval_value)); + gtk_signal_handler_block_by_data (GTK_OBJECT (adj), rpage); + e_dialog_spin_set (priv->interval_value, r->interval); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (adj), rpage); + + fill_ending_date (rpage, r); + + goto out; + + custom: + + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->none), rpage); + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->simple), rpage); + gtk_signal_handler_block_by_data (GTK_OBJECT (priv->custom), rpage); + e_dialog_radio_set (priv->custom, RECUR_CUSTOM, type_map); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->none), rpage); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->simple), rpage); + gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->custom), rpage); + + gtk_widget_set_sensitive (priv->custom, TRUE); + sensitize_recur_widgets (rpage); + + out: + + cal_component_free_recur_list (rrule_list); + preview_recur (rpage); +} + + + +/* Gets the widgets from the XML file and returns if they are all available. */ +static gboolean +get_widgets (RecurrencePage *rpage) +{ + RecurrencePagePrivate *priv; + GtkWidget *toplevel; + + priv = rpage->priv; + +#define GW(name) glade_xml_get_widget (priv->xml, name) + + toplevel = GW ("recurrence-toplevel"); + priv->main = GW ("recurrence-page"); + if (!(toplevel && priv->main)) + return NULL; + + gtk_widget_ref (priv->main); + gtk_widget_unparent (priv->main); + gtk_widget_destroy (toplevel); + + priv->summary = GW ("summary"); + priv->starting_date = GW ("starting-date"); + + priv->none = GW ("none"); + priv->simple = GW ("simple"); + priv->custom = GW ("custom"); + priv->params = GW ("params"); + + priv->interval_value = GW ("interval-value"); + priv->interval_unit = GW ("interval-unit"); + priv->special = GW ("special"); + priv->ending_menu = GW ("ending-menu"); + priv->ending_special = GW ("ending-special"); + priv->custom_warning_bin = GW ("custom-warning-bin"); + + priv->exception_date = GW ("exception-date"); + priv->exception_list = GW ("exception-list"); + priv->exception_add = GW ("exception-add"); + priv->exception_modify = GW ("exception-modify"); + priv->exception_delete = GW ("exception-delete"); + + priv->preview_bin = GW ("preview-bin"); + +#undef GW + + return (priv->summary + && priv->starting_date + && priv->none + && priv->simple + && priv->custom + && priv->params + && priv->interval_value + && priv->interval_unit + && priv->special + && priv->ending_menu + && priv->ending_special + && priv->custom_warning_bin + && priv->exception_date + && priv->exception_list + && priv->exception_add + && priv->exception_modify + && priv->exception_delete + && priv->preview_bin); +} diff --git a/calendar/gui/dialogs/recurrence-page.glade b/calendar/gui/dialogs/recurrence-page.glade new file mode 100644 index 0000000000..b84a3c288c --- /dev/null +++ b/calendar/gui/dialogs/recurrence-page.glade @@ -0,0 +1,607 @@ + + + + + recurrence-page + recurrence-page + + . + pixmaps + C + True + True + False + False + False + + + + GtkWindow + recurrence-toplevel + window1 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + True + False + + + GtkVBox + recurrence-page + 4 + False + 4 + + + GtkFrame + frame35 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + False + False + + + + GtkTable + table14 + 4 + 2 + 2 + False + 2 + 2 + + + GtkLabel + label66 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + recurrence-summary + + 0 + 1 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label67 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 0 + 1 + 1 + 2 + 0 + 0 + False + False + False + False + True + False + + + + + GtkEntry + recurrence-summary + True + True + True + 0 + + + 1 + 2 + 0 + 1 + 0 + 0 + True + False + True + False + True + False + + + + + GtkAlignment + alignment40 + 0 + 0.5 + 0 + 0 + + 1 + 2 + 1 + 2 + 0 + 0 + False + False + False + False + True + True + + + + Custom + recurrence-starting-date + make_date_edit + 0 + 0 + Fri, 22 Sep 2000 20:51:38 GMT + + + + + + + GtkVBox + vbox55 + False + 4 + + 0 + True + True + + + + GtkFrame + frame36 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + False + False + + + + GtkVBox + vbox56 + 4 + False + 4 + + + GtkHBox + hbox56 + False + 4 + + 0 + False + False + + + + GtkRadioButton + recurrence-none + True + + False + True + recurrence-radio + + 0 + False + False + + + + + GtkRadioButton + recurrence-simple + True + + False + True + recurrence-radio + + 0 + False + False + + + + + GtkRadioButton + recurrence-custom + True + + False + True + recurrence-radio + + 0 + False + False + + + + + + GtkHBox + hbox57 + False + 0 + + 0 + False + False + + + + GtkHBox + recurrence-params + False + 2 + + 0 + False + False + + + + GtkLabel + label68 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkSpinButton + recurrence-interval-value + True + 1 + 0 + True + GTK_UPDATE_ALWAYS + False + False + 1 + 1 + 10000 + 1 + 10 + 10 + + 0 + False + False + + + + + GtkOptionMenu + recurrence-interval-unit + True + day(s) +week(s) +month(s) +year(s) + + 0 + + 0 + False + False + + + + + GtkAlignment + recurrence-special + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + + + + Placeholder + + + + + GtkOptionMenu + recurrence-ending-menu + True + for +until +forever + + 0 + + 0 + False + False + + + + + GtkAlignment + recurrence-ending-special + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + + + + Placeholder + + + + + + GtkAlignment + recurrence-custom-warning-bin + 0 + 0.5 + 1 + 1 + + 0 + True + True + + + + Placeholder + + + + + + + + GtkHBox + hbox59 + False + 4 + + 0 + True + True + + + + GtkFrame + frame37 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + True + True + + + + GtkHBox + hbox60 + 4 + False + 4 + + + GtkVBox + vbox57 + False + 4 + + 0 + False + False + + + + GtkButton + recurrence-exception-add + True + + GTK_RELIEF_NORMAL + + 0 + False + False + + + + + GtkButton + recurrence-exception-modify + True + + GTK_RELIEF_NORMAL + + 0 + False + False + + + + + GtkButton + recurrence-exception-delete + True + + GTK_RELIEF_NORMAL + + 0 + False + False + + + + + + GtkVBox + vbox58 + False + 4 + + 0 + True + True + + + + Custom + recurrence-exception-date + make_date_edit + 0 + 0 + Tue, 16 May 2000 01:42:29 GMT + + 0 + False + False + + + + + GtkScrolledWindow + scrolledwindow14 + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + 0 + True + True + + + + GtkCList + recurrence-exception-list + True + 1 + 80 + GTK_SELECTION_BROWSE + False + GTK_SHADOW_IN + + + GtkLabel + CList:title + label69 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + + + + + + GtkVBox + vbox59 + False + 0 + + 0 + False + False + + + + GtkLabel + label70 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkAlignment + recurrence-preview-bin + 0 + 0 + 1 + 1 + + 0 + True + True + + + + Placeholder + + + + + + + + + diff --git a/calendar/gui/dialogs/recurrence-page.h b/calendar/gui/dialogs/recurrence-page.h new file mode 100644 index 0000000000..39123644b4 --- /dev/null +++ b/calendar/gui/dialogs/recurrence-page.h @@ -0,0 +1,64 @@ +/* Evolution calendar - Recurrence page of the calendar component dialogs + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero + * Miguel de Icaza + * Seth Alves + * JP Rosevear + * + * 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. + */ + +#ifndef RECURRENCE_PAGE_H +#define RECURRENCE_PAGE_H + +#include "editor-page.h" + +BEGIN_GNOME_DECLS + + + +#define TYPE_RECURRENCE_PAGE (recurrence_page_get_type ()) +#define RECURRENCE_PAGE(obj) (GTK_CHECK_CAST ((obj), TYPE_RECURRENCE_PAGE, RecurrencePage)) +#define RECURRENCE_PAGE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), TYPE_RECURRENCE_PAGE, \ + RecurrencePageClass)) +#define IS_RECURRENCE_PAGE(obj) (GTK_CHECK_TYPE ((obj), TYPE_RECURRENCE_PAGE)) +#define IS_RECURRENCE_PAGE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), TYPE_RECURRENCE_PAGE)) + +typedef struct _RecurrencePagePrivate RecurrencePagePrivate; + +typedef struct { + EditorPage page; + + /* Private data */ + RecurrencePagePrivate *priv; +} RecurrencePage; + +typedef struct { + EditorPageClass parent_class; +} RecurrencePageClass; + +GtkType recurrence_page_get_type (void); + +RecurrencePage *recurrence_page_construct (RecurrencePage *rpage); + +RecurrencePage *recurrence_page_new (void); + + + +END_GNOME_DECLS + +#endif diff --git a/calendar/gui/dialogs/task-details-page.glade b/calendar/gui/dialogs/task-details-page.glade new file mode 100644 index 0000000000..31e3aba6c4 --- /dev/null +++ b/calendar/gui/dialogs/task-details-page.glade @@ -0,0 +1,137 @@ + + + + + task-details-page + task-details-page + + src + pixmaps + C + True + True + + + + GtkWindow + task-details-toplevel + window1 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + True + False + + + GtkTable + task-details-page + 4 + 2 + 2 + False + 2 + 4 + + + GtkLabel + label12 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 0 + 1 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label14 + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 0 + 1 + 1 + 2 + 0 + 0 + False + False + False + False + True + False + + + + + GtkEntry + url + True + True + True + 0 + + + 1 + 2 + 1 + 2 + 0 + 0 + True + False + False + False + True + False + + + + + Custom + completed-date + task_editor_create_date_edit + 0 + 0 + Sun, 10 Sep 2000 17:34:07 GMT + + 1 + 2 + 0 + 1 + 0 + 0 + True + False + False + False + True + False + + + + + + diff --git a/calendar/gui/dialogs/task-page.glade b/calendar/gui/dialogs/task-page.glade index ae7ea9681a..e9f0d4700d 100644 --- a/calendar/gui/dialogs/task-page.glade +++ b/calendar/gui/dialogs/task-page.glade @@ -2,8 +2,8 @@ - task-editor-dialog - task-editor-dialog + task-page + task-page src pixmaps @@ -13,681 +13,533 @@ - GnomePropertyBox - task-editor-dialog - False + GtkWindow + task-toplevel + window1 + GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False False - False + True False - GtkNotebook - GnomeDock:contents - notebook1 - 2 - True - True - True - GTK_POS_TOP - False - 2 - 2 - False + GtkVBox + task-page + 4 + False + 4 - GtkVBox - vbox1 - 4 + GtkTable + table3 + 1 + 2 False - 4 + 4 + 4 + + 0 + False + True + - GtkTable - table3 - 1 - 2 - False - 4 - 4 + GtkLabel + label3 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + summary - 0 - False - True - - - - GtkLabel - label3 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 + 0 + 1 + 0 + 1 0 0 - summary - - 0 - 1 - 0 - 1 - 0 - 0 - False - False - False - False - False - False - - - - - GtkEntry - summary - True - True - True - 0 - - - 1 - 2 - 0 - 1 - 0 - 0 - True - False - False - False - True - False - - - - - - GtkFrame - frame2 - - 0 - GTK_SHADOW_ETCHED_IN - - 0 - False - True + False + False + False + False + False + False - - - GtkHBox - hbox4 - 4 - False - 0 - - - GtkTable - table1 - 2 - 2 - False - 2 - 4 - - 0 - False - True - - - - GtkLabel - label6 - - GTK_JUSTIFY_CENTER - False - 0 - 0.5 - 0 - 0 - - 0 - 1 - 1 - 2 - 0 - 0 - False - False - False - False - True - False - - - - - GtkLabel - label5 - - GTK_JUSTIFY_CENTER - False - 0 - 0.5 - 0 - 0 - - 0 - 1 - 0 - 1 - 0 - 0 - False - False - False - False - True - False - - - - - Custom - due-date - task_editor_create_date_edit - 0 - 0 - Sun, 10 Sep 2000 17:32:18 GMT - - 1 - 2 - 0 - 1 - 0 - 0 - True - False - False - False - True - False - - - - - Custom - start-date - task_editor_create_date_edit - 0 - 0 - Sun, 10 Sep 2000 17:33:31 GMT - - 1 - 2 - 1 - 2 - 0 - 0 - True - False - False - False - True - False - - - - - GtkScrolledWindow - scrolledwindow1 - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - GTK_UPDATE_CONTINUOUS - GTK_UPDATE_CONTINUOUS + GtkEntry + summary + True + True + True + 0 + - 0 - False - True + 1 + 2 + 0 + 1 + 0 + 0 + True + False + False + False + True + False - - - GtkText - description - 80 - True - True - - + + + + GtkFrame + frame2 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + False + True + - GtkFrame - frame23 - - 0 - GTK_SHADOW_ETCHED_IN - - 0 - False - True - + GtkHBox + hbox4 + 4 + False + 0 - GtkHBox - hbox3 - 4 + GtkTable + table1 + 2 + 2 False - 4 + 2 + 4 + + 0 + False + True + GtkLabel - label7 - + label6 + GTK_JUSTIFY_CENTER False - 0.5 + 0 0.5 0 0 - status - - 0 - False - False - - - - - GtkOptionMenu - status - True - Not Started -In Progress -Completed -Cancelled - - 0 - 0 - False - False + 0 + 1 + 1 + 2 + 0 + 0 + False + False + False + False + True + False GtkLabel - label8 - + label5 + GTK_JUSTIFY_CENTER False - 0.5 + 0 0.5 0 0 - priority - 0 - False - False - - - - - GtkOptionMenu - priority - True - High -Normal -Low -Undefined - - 0 - - 0 - False - False + 0 + 1 + 0 + 1 + 0 + 0 + False + False + False + False + True + False - GtkLabel - label9 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 - percent-complete + Custom + due-date + task_editor_create_date_edit + 0 + 0 + Sun, 10 Sep 2000 17:32:18 GMT - 0 - False - False + 1 + 2 + 0 + 1 + 0 + 0 + True + False + False + False + True + False - GtkSpinButton - percent-complete - 60 - True - 1 - 0 - False - GTK_UPDATE_ALWAYS - False - False - 0 - 0 - 100 - 10 - 10 - 10 + Custom + start-date + task_editor_create_date_edit + 0 + 0 + Sun, 10 Sep 2000 17:33:31 GMT - 0 - False - False + 1 + 2 + 1 + 2 + 0 + 0 + True + False + False + False + True + False + - - GtkFrame - frame24 - - 0 - GTK_SHADOW_ETCHED_IN - - 0 - False - True - - - - GtkHBox - hbox6 - 2 - False - 4 - - - GtkRadioButton - classification-public - True - - True - True - classification_radio_group - - 0 - False - False - - - - - GtkRadioButton - classification-private - True - - False - True - classification_radio_group - - 0 - False - False - - + + GtkScrolledWindow + scrolledwindow1 + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + 0 + False + True + - - GtkRadioButton - classification-confidential - True - - False - True - classification_radio_group - - 0 - False - False - - - + + GtkText + description + 80 + True + True + + + + + GtkFrame + frame23 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + False + True + GtkHBox - hbox2 + hbox3 + 4 False - 2 - - 0 - True - True - + 4 - GtkButton - contacts-button - True + GtkLabel + label7 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + status 0 False False - - - GtkLabel - label16 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 4 - 0 - - GtkEntry - contacts + GtkOptionMenu + status True - True - True - 0 - + Not Started +In Progress +Completed +Cancelled + + 0 0 - True - True + False + False - GtkButton - categories-button + GtkLabel + label8 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + priority + + 0 + False + False + + + + + GtkOptionMenu + priority True + High +Normal +Low +Undefined + + 0 0 False False + - - GtkLabel - label17 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 4 - 0 - + + GtkLabel + label9 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + percent-complete + + 0 + False + False + - GtkEntry - categories + GtkSpinButton + percent-complete + 60 True - True - True - 0 - + 1 + 0 + False + GTK_UPDATE_ALWAYS + False + False + 0 + 0 + 100 + 10 + 10 + 10 0 - True - True + False + False - GtkLabel - Notebook:tab - label1 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 + GtkFrame + frame24 + + 0 + GTK_SHADOW_ETCHED_IN + + 0 + False + True + + + + GtkHBox + hbox6 + 2 + False + 4 + + + GtkRadioButton + classification-public + True + + True + True + classification_radio_group + + 0 + False + False + + + + + GtkRadioButton + classification-private + True + + False + True + classification_radio_group + + 0 + False + False + + + + + GtkRadioButton + classification-confidential + True + + False + True + classification_radio_group + + 0 + False + False + + + - GtkTable - table4 - 4 - 2 - 2 + GtkHBox + hbox2 False - 2 - 4 + 2 + + 0 + True + True + - GtkLabel - label12 - - GTK_JUSTIFY_CENTER - False - 0 - 0.5 - 0 - 0 + GtkButton + contacts-button + True + GTK_RELIEF_NORMAL - 0 - 1 - 0 - 1 - 0 - 0 - False - False - False - False - True - False + 0 + False + False - - - GtkLabel - label14 - - GTK_JUSTIFY_CENTER - False - 0 - 0.5 - 0 - 0 - - 0 - 1 - 1 - 2 - 0 + + GtkLabel + label16 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 4 0 - False - False - False - False - True - False - + GtkEntry - url + contacts True True True 0 - 1 - 2 - 1 - 2 - 0 - 0 - True - False - False - False - True - False + 0 + True + True - Custom - completed-date - task_editor_create_date_edit - 0 - 0 - Sun, 10 Sep 2000 17:34:07 GMT + GtkButton + categories-button + True + GTK_RELIEF_NORMAL - 1 - 2 - 0 - 1 - 0 - 0 - True - False - False - False - True - False + 0 + False + False + + + GtkLabel + label17 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 4 + 0 + - - - GtkLabel - Notebook:tab - label2 - - GTK_JUSTIFY_CENTER - False - 0.5 - 0.5 - 0 - 0 + + GtkEntry + categories + True + True + True + 0 + + + 0 + True + True + + diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c index b10382a3ea..b19c7ff310 100644 --- a/calendar/gui/e-day-view.c +++ b/calendar/gui/e-day-view.c @@ -803,11 +803,11 @@ e_day_view_init (EDayView *day_view) gtk_drag_dest_set (day_view->top_canvas, GTK_DEST_DEFAULT_ALL, target_table, n_targets, - GDK_ACTION_COPY | GDK_ACTION_MOVE); + GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK); gtk_drag_dest_set (day_view->main_canvas, GTK_DEST_DEFAULT_ALL, target_table, n_targets, - GDK_ACTION_COPY | GDK_ACTION_MOVE); + GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK); } diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c index c06b568d9d..96270c04aa 100644 --- a/calendar/gui/e-week-view.c +++ b/calendar/gui/e-week-view.c @@ -379,7 +379,6 @@ e_week_view_init (EWeekView *week_view) 2, 3, 1, 2, 0, GTK_EXPAND | GTK_FILL, 0, 0); gtk_widget_show (week_view->vscrollbar); - /* Create the cursors. */ week_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR); week_view->move_cursor = gdk_cursor_new (GDK_FLEUR); diff --git a/calendar/gui/event-editor.c b/calendar/gui/event-editor.c index 242ef67226..c96c94dadd 100644 --- a/calendar/gui/event-editor.c +++ b/calendar/gui/event-editor.c @@ -1656,7 +1656,7 @@ fill_reminder_widgets (EventEditor *ee) /* Add it to the clist */ append_reminder (ee, ca, EXISTING_ALARM); } - cal_component_free_alarm_uids (alarms); + cal_obj_uid_list_free (alarms); } /* Fills in the recurrence widgets with the values from the calendar component. -- cgit v1.2.3