/* Evolution calendar - Event editor dialog
*
* Copyright (C) 2000 Helix Code, Inc.
*
* Authors: Miguel de Icaza <miguel@helixcode.com>
* Federico Mena-Quintero <federico@helixcode.com>
* Seth Alves <alves@helixcode.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include <gnome.h>
#include <glade/glade.h>
#include <e-util/e-dialog-widgets.h>
#include <widgets/misc/e-dateedit.h>
#include <gal/widgets/e-unicode.h>
#include <cal-util/timeutil.h>
#include "event-editor.h"
typedef struct {
/* Glade XML data */
GladeXML *xml;
/* UI handler */
BonoboUIHandler *uih;
/* Client to use */
CalClient *client;
/* Calendar object/uid we are editing; this is an internal copy */
CalComponent *comp;
/* Widgets from the Glade file */
GtkWidget *app;
GtkWidget *general_summary;
GtkWidget *start_time;
GtkWidget *end_time;
GtkWidget *all_day_event;
GtkWidget *description;
GtkWidget *alarm_display;
GtkWidget *alarm_program;
GtkWidget *alarm_audio;
GtkWidget *alarm_mail;
GtkWidget *alarm_display_amount;
GtkWidget *alarm_display_unit;
GtkWidget *alarm_audio_amount;
GtkWidget *alarm_audio_unit;
GtkWidget *alarm_program_amount;
GtkWidget *alarm_program_unit;
GtkWidget *alarm_program_run_program;
GtkWidget *alarm_program_run_program_entry;
GtkWidget *alarm_mail_amount;
GtkWidget *alarm_mail_unit;
GtkWidget *alarm_mail_mail_to;
GtkWidget *classification_radio;
GtkWidget *recurrence_rule_notebook;
GtkWidget *recurrence_rule_none;
GtkWidget *recurrence_rule_daily;
GtkWidget *recurrence_rule_weekly;
GtkWidget *recurrence_rule_monthly;
GtkWidget *recurrence_rule_yearly;
GtkWidget *recurrence_rule_daily_days;
GtkWidget *recurrence_rule_weekly_weeks;
GtkWidget *recurrence_rule_weekly_sun;
GtkWidget *recurrence_rule_weekly_mon;
GtkWidget *recurrence_rule_weekly_tue;
GtkWidget *recurrence_rule_weekly_wed;
GtkWidget *recurrence_rule_weekly_thu;
GtkWidget *recurrence_rule_weekly_fri;
GtkWidget *recurrence_rule_weekly_sat;
GtkWidget *recurrence_rule_monthly_on_day;
GtkWidget *recurrence_rule_monthly_weekday;
GtkWidget *recurrence_rule_monthly_day_nth;
GtkWidget *recurrence_rule_monthly_week;
GtkWidget *recurrence_rule_monthly_weekpos;
GtkWidget *recurrence_rule_monthly_every_n_months;
GtkWidget *recurrence_rule_yearly_every_n_years;
GtkWidget *recurrence_ending_date_repeat_forever;
GtkWidget *recurrence_ending_date_end_on;
GtkWidget *recurrence_ending_date_end_on_date;
GtkWidget *recurrence_ending_date_end_after;
GtkWidget *recurrence_ending_date_end_after_count;
GtkWidget *recurrence_exceptions_date;
GtkWidget *recurrence_exceptions_list;
GtkWidget *recurrence_exception_add;
GtkWidget *recurrence_exception_delete;
GtkWidget *recurrence_exception_change;
GtkWidget *exception_list;
GtkWidget *exception_date;
} EventEditorPrivate;
static void event_editor_class_init (EventEditorClass *class);
static void event_editor_init (EventEditor *ee);
static void event_editor_destroy (GtkObject *object);
static GtkObjectClass *parent_class;
extern int day_begin, day_end;
extern char *user_name;
extern int am_pm_flag;
extern int week_starts_on_monday;
static void append_exception (EventEditor *ee, time_t t);
static void check_all_day (EventEditor *ee);
static void set_all_day (GtkWidget *toggle, EventEditor *ee);
static void alarm_toggle (GtkWidget *toggle, EventEditor *ee);
static void check_dates (EDateEdit *dedit, EventEditor *ee);
static void check_times (EDateEdit *dedit, EventEditor *ee);
static void recurrence_toggled (GtkWidget *radio, EventEditor *ee);
static void recurrence_exception_added (GtkWidget *widget, EventEditor *ee);
static void recurrence_exception_deleted (GtkWidget *widget, EventEditor *ee);
static void recurrence_exception_changed (GtkWidget *widget, EventEditor *ee);
/**
* event_editor_get_type:
* @void:
*
* Registers the #EventEditor class if necessary, and returns the type ID
* associated to it.
*
* Return value: The type ID of the #EventEditor class.
**/
GtkType
event_editor_get_type (void)
{
static GtkType event_editor_type = 0;
if (!event_editor_type) {
static const GtkTypeInfo event_editor_info = {
"EventEditor",
sizeof (EventEditor),
sizeof (EventEditorClass),
(GtkClassInitFunc) event_editor_class_init,
(GtkObjectInitFunc) event_editor_init,
NULL, /* reserved_1 */
NULL, /* reserved_2 */
(GtkClassInitFunc) NULL
};
event_editor_type = gtk_type_unique (GTK_TYPE_OBJECT, &event_editor_info);
}
return event_editor_type;
}
/* Class initialization function for the event editor */
static void
event_editor_class_init (EventEditorClass *class)
{
GtkObjectClass *object_class;
object_class = (GtkObjectClass *) class;
parent_class = gtk_type_class (GTK_TYPE_OBJECT);
object_class->destroy = event_editor_destroy;
}
/* Object initialization function for the event editor */
static void
event_editor_init (EventEditor *ee)
{
EventEditorPrivate *priv;
priv = g_new0 (EventEditorPrivate, 1);
ee->priv = priv;
}
/* Frees the rows and the row data in the recurrence exceptions GtkCList */
static void
free_exception_clist_data (GtkCList *clist)
{
int i;
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 event editor */
static void
event_editor_destroy (GtkObject *object)
{
EventEditor *ee;
EventEditorPrivate *priv;
g_return_if_fail (object != NULL);
g_return_if_fail (IS_EVENT_EDITOR (object));
ee = EVENT_EDITOR (object);
priv = ee->priv;
if (priv->uih) {
bonobo_object_unref (BONOBO_OBJECT (priv->uih));
priv->uih = NULL;
}
free_exception_clist_data (GTK_CLIST (priv->recurrence_exceptions_list));
if (priv->app) {
gtk_signal_disconnect_by_data (GTK_OBJECT (priv->app), ee);
gtk_widget_destroy (priv->app);
priv->app = NULL;
}
if (priv->comp) {
gtk_object_unref (GTK_OBJECT (priv->comp));
priv->comp = NULL;
}
if (priv->client) {
gtk_signal_disconnect_by_data (GTK_OBJECT (priv->client), ee);
gtk_object_unref (GTK_OBJECT (priv->client));
priv->client = NULL;
}
if (priv->xml) {
gtk_object_unref (GTK_OBJECT (priv->xml));
priv->xml = NULL;
}
g_free (priv);
ee->priv = NULL;
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
/* Creates an appropriate title for the event editor dialog */
static char *
make_title_from_comp (CalComponent *comp)
{
const char *summary;
CalComponentVType type;
CalComponentText text;
if (!comp)
return g_strdup (_("Edit Appointment"));
cal_component_get_summary (comp, &text);
if (text.value)
summary = text.value;
else
summary = _("No summary");
type = cal_component_get_vtype (comp);
switch (type) {
case CAL_COMPONENT_EVENT:
return g_strdup_printf (_("Appointment - %s"), summary);
case CAL_COMPONENT_TODO:
return g_strdup_printf (_("Task - %s"), summary);
case CAL_COMPONENT_JOURNAL:
return g_strdup_printf (_("Journal entry - %s"), summary);
default:
g_message ("make_title_from_comp(): Cannot handle object of type %d", type);
return NULL;
}
}
/* Gets the widgets from the XML file and returns if they are all available.
* For the widgets whose values can be simply set with e-dialog-utils, it does
* that as well.
*/
static gboolean
get_widgets (EventEditor *ee)
{
EventEditorPrivate *priv;
priv = ee->priv;
#define GW(name) glade_xml_get_widget (priv->xml, name)
priv->app = GW ("event-editor-dialog");
priv->general_summary = GW ("general-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->alarm_display = GW ("alarm-display");
priv->alarm_program = GW ("alarm-program");
priv->alarm_audio = GW ("alarm-audio");
priv->alarm_mail = GW ("alarm-mail");
priv->alarm_display_amount = GW ("alarm-display-amount");
priv->alarm_display_unit = GW ("alarm-display-unit");
priv->alarm_audio_amount = GW ("alarm-audio-amount");
priv->alarm_audio_unit = GW ("alarm-audio-unit");
priv->alarm_program_amount = GW ("alarm-program-amount");
priv->alarm_program_unit = GW ("alarm-program-unit");
priv->alarm_program_run_program = GW ("alarm-program-run-program");
priv->alarm_program_run_program_entry = GW ("alarm-program-run-program-entry");
priv->alarm_mail_amount = GW ("alarm-mail-amount");
priv->alarm_mail_unit = GW ("alarm-mail-unit");
priv->alarm_mail_mail_to = GW ("alarm-mail-mail-to");
priv->classification_radio = GW ("classification-radio");
priv->recurrence_rule_notebook = GW ("recurrence-rule-notebook");
priv->recurrence_rule_none = GW ("recurrence-rule-none");
priv->recurrence_rule_daily = GW ("recurrence-rule-daily");
priv->recurrence_rule_weekly = GW ("recurrence-rule-weekly");
priv->recurrence_rule_monthly = GW ("recurrence-rule-monthly");
priv->recurrence_rule_yearly = GW ("recurrence-rule-yearly");
priv->recurrence_rule_daily_days = GW ("recurrence-rule-daily-days");
priv->recurrence_rule_weekly_weeks = GW ("recurrence-rule-weekly-weeks");
priv->recurrence_rule_weekly_sun = GW ("recurrence-rule-weekly-sun");
priv->recurrence_rule_weekly_mon = GW ("recurrence-rule-weekly-mon");
priv->recurrence_rule_weekly_tue = GW ("recurrence-rule-weekly-tue");
priv->recurrence_rule_weekly_wed = GW ("recurrence-rule-weekly-wed");
priv->recurrence_rule_weekly_thu = GW ("recurrence-rule-weekly-thu");
priv->recurrence_rule_weekly_fri = GW ("recurrence-rule-weekly-fri");
priv->recurrence_rule_weekly_sat = GW ("recurrence-rule-weekly-sat");
priv->recurrence_rule_monthly_on_day = GW ("recurrence-rule-monthly-on-day");
priv->recurrence_rule_monthly_weekday = GW ("recurrence-rule-monthly-weekday");
priv->recurrence_rule_monthly_day_nth = GW ("recurrence-rule-monthly-day-nth");
priv->recurrence_rule_monthly_week = GW ("recurrence-rule-monthly-week");
priv->recurrence_rule_monthly_weekpos = GW ("recurrence-rule-monthly-weekpos");
priv->recurrence_rule_monthly_every_n_months = GW ("recurrence-rule-monthly-every-n-months");
priv->recurrence_rule_yearly_every_n_years = GW ("recurrence-rule-yearly-every-n-years");
priv->recurrence_ending_date_repeat_forever = GW ("recurrence-ending-date-repeat-forever");
priv->recurrence_ending_date_end_on = GW ("recurrence-ending-date-end-on");
priv->recurrence_ending_date_end_on_date = GW ("recurrence-ending-date-end-on-date");
priv->recurrence_ending_date_end_after = GW ("recurrence-ending-date-end-after");
priv->recurrence_ending_date_end_after_count = GW ("recurrence-ending-date-end-after-count");
priv->recurrence_exceptions_date = GW ("recurrence-exceptions-date");
priv->recurrence_exceptions_list = GW ("recurrence-exceptions-list");
priv->recurrence_exception_add = GW ("recurrence-exceptions-add");
priv->recurrence_exception_delete = GW ("recurrence-exceptions-delete");
priv->recurrence_exception_change = GW ("recurrence-exceptions-change");
priv->exception_list = GW ("recurrence-exceptions-list");
priv->exception_date = GW ("recurrence-exceptions-date");
#undef GW
return (priv->general_summary
&& priv->start_time
&& priv->end_time
&& priv->all_day_event
&& priv->description
&& priv->alarm_display
&& priv->alarm_program
&& priv->alarm_audio
&& priv->alarm_mail
&& priv->alarm_display_amount
&& priv->alarm_display_unit
&& priv->alarm_audio_amount
&& priv->alarm_audio_unit
&& priv->alarm_program_amount
&& priv->alarm_program_unit
&& priv->alarm_program_run_program
&& priv->alarm_program_run_program_entry
&& priv->alarm_mail_amount
&& priv->alarm_mail_unit
&& priv->alarm_mail_mail_to
&& priv->classification_radio
&& priv->recurrence_rule_notebook
&& priv->recurrence_rule_none
&& priv->recurrence_rule_daily
&& priv->recurrence_rule_weekly
&& priv->recurrence_rule_monthly
&& priv->recurrence_rule_yearly
&& priv->recurrence_rule_daily_days
&& priv->recurrence_rule_weekly_weeks
&& priv->recurrence_rule_monthly_on_day
&& priv->recurrence_rule_monthly_weekday
&& priv->recurrence_rule_monthly_day_nth
&& priv->recurrence_rule_monthly_week
&& priv->recurrence_rule_monthly_weekpos
&& priv->recurrence_rule_monthly_every_n_months
&& priv->recurrence_rule_yearly_every_n_years
&& priv->recurrence_ending_date_repeat_forever
&& priv->recurrence_ending_date_end_on
&& priv->recurrence_ending_date_end_on_date
&& priv->recurrence_ending_date_end_after
&& priv->recurrence_ending_date_end_after_count
&& priv->recurrence_exceptions_date
&& priv->recurrence_exceptions_list
&& priv->recurrence_exception_add
&& priv->recurrence_exception_delete
&& priv->recurrence_exception_change
&& priv->exception_list
&& priv->exception_date);
}
static const int classification_map[] = {
CAL_COMPONENT_CLASS_PUBLIC,
CAL_COMPONENT_CLASS_PRIVATE,
CAL_COMPONENT_CLASS_CONFIDENTIAL,
-1
};
#if 0
static const int alarm_unit_map[] = {
ALARM_MINUTES,
ALARM_HOURS,
ALARM_DAYS,
-1
};
static void
alarm_unit_set (GtkWidget *widget, enum AlarmUnit unit)
{
e_dialog_option_menu_set (widget, unit, alarm_unit_map);
}
static enum AlarmUnit
alarm_unit_get (GtkWidget *widget)
{
return e_dialog_option_menu_get (widget, alarm_unit_map);
}
#endif
/* Recurrence types for mapping them to radio buttons */
static const int recur_options_map[] = {
ICAL_NO_RECURRENCE,
ICAL_DAILY_RECURRENCE,
ICAL_WEEKLY_RECURRENCE,
ICAL_MONTHLY_RECURRENCE,
ICAL_YEARLY_RECURRENCE,
-1
};
static icalrecurrencetype_frequency
recur_options_get (GtkWidget *widget)
{
return e_dialog_radio_get (widget, recur_options_map);
}
static const int month_pos_map[] = { 0, 1, 2, 3, 4, -1 };
static const int weekday_map[] = { 0, 1, 2, 3, 4, 5, 6, -1 };
/* Hooks the widget signals */
static void
init_widgets (EventEditor *ee)
{
EventEditorPrivate *priv;
priv = ee->priv;
/* Start and end times */
gtk_signal_connect (GTK_OBJECT (priv->start_time), "date_changed",
GTK_SIGNAL_FUNC (check_dates), ee);
gtk_signal_connect (GTK_OBJECT (priv->start_time), "time_changed",
GTK_SIGNAL_FUNC (check_times), ee);
gtk_signal_connect (GTK_OBJECT (priv->end_time), "date_changed",
GTK_SIGNAL_FUNC (check_dates), ee);
gtk_signal_connect (GTK_OBJECT (priv->end_time), "time_changed",
GTK_SIGNAL_FUNC (check_times), ee);
gtk_signal_connect (GTK_OBJECT (priv->all_day_event), "toggled",
GTK_SIGNAL_FUNC (set_all_day), ee);
/* Alarms */
gtk_signal_connect (GTK_OBJECT (priv->alarm_display), "toggled",
GTK_SIGNAL_FUNC (alarm_toggle), ee);
gtk_signal_connect (GTK_OBJECT (priv->alarm_program), "toggled",
GTK_SIGNAL_FUNC (alarm_toggle), ee);
gtk_signal_connect (GTK_OBJECT (priv->alarm_audio), "toggled",
GTK_SIGNAL_FUNC (alarm_toggle), ee);
gtk_signal_connect (GTK_OBJECT (priv->alarm_mail), "toggled",
GTK_SIGNAL_FUNC (alarm_toggle), ee);
/* Recurrence types */
gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_none), "toggled",
GTK_SIGNAL_FUNC (recurrence_toggled), ee);
gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_daily), "toggled",
GTK_SIGNAL_FUNC (recurrence_toggled), ee);
gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_weekly), "toggled",
GTK_SIGNAL_FUNC (recurrence_toggled), ee);
gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_monthly), "toggled",
GTK_SIGNAL_FUNC (recurrence_toggled), ee);
gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_yearly), "toggled",
GTK_SIGNAL_FUNC (recurrence_toggled), ee);
/* Exception buttons */
gtk_signal_connect (GTK_OBJECT (priv->recurrence_exception_add), "clicked",
GTK_SIGNAL_FUNC (recurrence_exception_added), ee);
gtk_signal_connect (GTK_OBJECT (priv->recurrence_exception_delete), "clicked",
GTK_SIGNAL_FUNC (recurrence_exception_deleted), ee);
gtk_signal_connect (GTK_OBJECT (priv->recurrence_exception_change), "clicked",
GTK_SIGNAL_FUNC (recurrence_exception_changed), ee);
}
/* Fills the widgets with default values */
static void
clear_widgets (EventEditor *ee)
{
EventEditorPrivate *priv;
time_t now;
priv = ee->priv;
now = time (NULL);
/* Summary, description */
e_dialog_editable_set (priv->general_summary, NULL);
e_dialog_editable_set (priv->description, NULL);
/* Start and end times */
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->start_time), ee);
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->end_time), ee);
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), ee);
gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->end_time), ee);
check_all_day (ee);
/* Alarms */
/* FIXMe: these should use configurable defaults */
e_dialog_toggle_set (priv->alarm_display, FALSE);
e_dialog_toggle_set (priv->alarm_program, FALSE);
e_dialog_toggle_set (priv->alarm_audio, FALSE);
e_dialog_toggle_set (priv->alarm_mail, FALSE);
e_dialog_spin_set (priv->alarm_display_amount, 15);
e_dialog_spin_set (priv->alarm_audio_amount, 15);
e_dialog_spin_set (priv->alarm_program_amount, 15);
e_dialog_spin_set (priv->alarm_mail_amount, 15);
#if 0
alarm_unit_set (priv->alarm_display_unit, ALARM_MINUTES);
alarm_unit_set (priv->alarm_audio_unit, ALARM_MINUTES);
alarm_unit_set (priv->alarm_program_unit, ALARM_MINUTES);
alarm_unit_set (priv->alarm_mail_unit, ALARM_MINUTES);
#endif
e_dialog_editable_set (priv->alarm_program_run_program_entry, NULL);
e_dialog_editable_set (priv->alarm_mail_mail_to, NULL);
/* Classification */
e_dialog_radio_set (priv->classification_radio,
CAL_COMPONENT_CLASS_PRIVATE, classification_map);
/* Recurrences */
e_dialog_radio_set (priv->recurrence_rule_none, ICAL_NO_RECURRENCE, recur_options_map);
e_dialog_spin_set (priv->recurrence_rule_daily_days, 1);
e_dialog_spin_set (priv->recurrence_rule_weekly_weeks, 1);
e_dialog_toggle_set (priv->recurrence_rule_weekly_sun, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_mon, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_tue, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_wed, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_thu, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_fri, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_sat, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_monthly_on_day, TRUE);
e_dialog_spin_set (priv->recurrence_rule_monthly_day_nth, 1);
e_dialog_spin_set (priv->recurrence_rule_monthly_every_n_months, 1);
e_dialog_option_menu_set (priv->recurrence_rule_monthly_week, 0, month_pos_map);
e_dialog_option_menu_set (priv->recurrence_rule_monthly_weekpos, 0, weekday_map);
e_dialog_spin_set (priv->recurrence_rule_monthly_every_n_months, 1);
e_dialog_spin_set (priv->recurrence_rule_yearly_every_n_years, 1);
e_dialog_toggle_set (priv->recurrence_ending_date_repeat_forever, TRUE);
e_dialog_spin_set (priv->recurrence_ending_date_end_after_count, 1);
e_date_edit_set_time (E_DATE_EDIT (priv->recurrence_ending_date_end_on_date), time_add_day (time (NULL), 1));
/* Exceptions list */
free_exception_clist_data (GTK_CLIST (priv->recurrence_exceptions_list));
}
/* Fills in the widgets with the proper values */
static void
fill_widgets (EventEditor *ee)
{
EventEditorPrivate *priv;
CalComponentText text;
CalComponentClassification cl;
CalComponentDateTime d;
GSList *list, *l;
time_t dtstart, dtend;
priv = ee->priv;
clear_widgets (ee);
if (!priv->comp)
return;
cal_component_get_summary (priv->comp, &text);
e_dialog_editable_set (priv->general_summary, text.value);
cal_component_get_description_list (priv->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 */
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->start_time), ee);
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->end_time), ee);
/* 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 (priv->comp, &d);
dtstart = icaltime_as_timet (*d.value);
cal_component_get_dtend (priv->comp, &d);
dtend = icaltime_as_timet (*d.value);
if (time_day_begin (dtstart) == dtstart
&& time_day_begin (dtend) == dtend) {
dtend = time_add_day (dtend, -1);
}
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), ee);
gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->end_time), ee);
check_all_day (ee);
/* Alarms */
#if 0
e_dialog_toggle_set (priv->alarm_display, priv->ico->dalarm.enabled);
e_dialog_toggle_set (priv->alarm_program, priv->ico->palarm.enabled);
e_dialog_toggle_set (priv->alarm_audio, priv->ico->aalarm.enabled);
e_dialog_toggle_set (priv->alarm_mail, priv->ico->malarm.enabled);
#endif
/* Alarm data */
#if 0
e_dialog_spin_set (priv->alarm_display_amount, priv->ico->dalarm.count);
e_dialog_spin_set (priv->alarm_audio_amount, priv->ico->aalarm.count);
e_dialog_spin_set (priv->alarm_program_amount, priv->ico->palarm.count);
e_dialog_spin_set (priv->alarm_mail_amount, priv->ico->malarm.count);
alarm_unit_set (priv->alarm_display_unit, priv->ico->dalarm.units);
alarm_unit_set (priv->alarm_audio_unit, priv->ico->aalarm.units);
alarm_unit_set (priv->alarm_program_unit, priv->ico->palarm.units);
alarm_unit_set (priv->alarm_mail_unit, priv->ico->malarm.units);
e_dialog_editable_set (priv->alarm_program_run_program_entry, priv->ico->palarm.data);
e_dialog_editable_set (priv->alarm_mail_mail_to, priv->ico->malarm.data);
#endif
/* Classification */
cal_component_get_classification (priv->comp, &cl);
switch (cl) {
case CAL_COMPONENT_CLASS_PUBLIC:
e_dialog_radio_set (priv->classification_radio, CAL_COMPONENT_CLASS_PUBLIC,
classification_map);
case CAL_COMPONENT_CLASS_PRIVATE:
e_dialog_radio_set (priv->classification_radio, CAL_COMPONENT_CLASS_PRIVATE,
classification_map);
case CAL_COMPONENT_CLASS_CONFIDENTIAL:
e_dialog_radio_set (priv->classification_radio, CAL_COMPONENT_CLASS_CONFIDENTIAL,
classification_map);
default:
/* What do do? We can't g_assert_not_reached() since it is a
* value from an external file.
*/
}
/* Recurrences */
#ifndef NO_WARNINGS
#warning "FIX ME"
#endif
/* Need to handle recurrence dates as well as recurrence rules */
/* Need to handle more than one rrule */
if (cal_component_has_rrules (priv->comp)) {
struct icalrecurrencetype *r;
int i;
cal_component_get_rrule_list (priv->comp, &list);
r = list->data;
switch (r->freq) {
case ICAL_DAILY_RECURRENCE:
e_dialog_radio_set (priv->recurrence_rule_daily, ICAL_DAILY_RECURRENCE,
recur_options_map);
e_dialog_spin_set (priv->recurrence_rule_daily_days, r->interval);
break;
case ICAL_WEEKLY_RECURRENCE:
e_dialog_radio_set (priv->recurrence_rule_weekly, ICAL_WEEKLY_RECURRENCE,
recur_options_map);
e_dialog_spin_set (priv->recurrence_rule_weekly_weeks, r->interval);
e_dialog_toggle_set (priv->recurrence_rule_weekly_sun, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_mon, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_tue, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_wed, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_thu, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_fri, FALSE);
e_dialog_toggle_set (priv->recurrence_rule_weekly_sat, FALSE);
for (i=0; i<8 && r->by_day[i] != SHRT_MAX; i++) {
switch (r->by_day[i]) {
case ICAL_SUNDAY_WEEKDAY:
e_dialog_toggle_set (priv->recurrence_rule_weekly_sun, TRUE);
break;
case ICAL_MONDAY_WEEKDAY:
e_dialog_toggle_set (priv->recurrence_rule_weekly_mon, TRUE);
break;
case ICAL_TUESDAY_WEEKDAY:
e_dialog_toggle_set (priv->recurrence_rule_weekly_tue, TRUE);
break;
case ICAL_WEDNESDAY_WEEKDAY:
e_dialog_toggle_set (priv->recurrence_rule_weekly_wed, TRUE);
break;
case ICAL_THURSDAY_WEEKDAY:
e_dialog_toggle_set (priv->recurrence_rule_weekly_thu, TRUE);
break;
case ICAL_FRIDAY_WEEKDAY:
e_dialog_toggle_set (priv->recurrence_rule_weekly_fri, TRUE);
break;
case ICAL_SATURDAY_WEEKDAY:
e_dialog_toggle_set (priv->recurrence_rule_weekly_sat, TRUE);
break;
case ICAL_NO_WEEKDAY:
break;
}
}
break;
case ICAL_MONTHLY_RECURRENCE:
e_dialog_radio_set (priv->recurrence_rule_monthly, ICAL_MONTHLY_RECURRENCE,
recur_options_map);
if (r->by_month_day[0] != SHRT_MAX) {
e_dialog_toggle_set (priv->recurrence_rule_monthly_on_day, TRUE);
e_dialog_spin_set (priv->recurrence_rule_monthly_day_nth,
r->by_month_day[0]);
} else if (r->by_day[0] != SHRT_MAX) {
e_dialog_toggle_set (priv->recurrence_rule_monthly_weekday, TRUE);
/* libical does not handle ints in by day */
/* e_dialog_option_menu_set (priv->recurrence_rule_monthly_week, */
/* priv->ico->recur->u.month_pos, */
/* month_pos_map); */
/* e_dialog_option_menu_set (priv->recurrence_rule_monthly_weekpos, */
/* priv->ico->recur->weekday, */
/* weekday_map); */
}
e_dialog_spin_set (priv->recurrence_rule_monthly_every_n_months,
r->interval);
break;
case ICAL_YEARLY_RECURRENCE:
e_dialog_radio_set (priv->recurrence_rule_yearly, ICAL_YEARLY_RECURRENCE,
recur_options_map);
e_dialog_spin_set (priv->recurrence_rule_yearly_every_n_years,
r->interval);
break;
default:
g_assert_not_reached ();
}
if (r->until.year == 0) {
if (r->count == 0)
e_dialog_toggle_set (priv->recurrence_ending_date_repeat_forever,
TRUE);
else {
e_dialog_toggle_set (priv->recurrence_ending_date_end_after, TRUE);
e_dialog_spin_set (priv->recurrence_ending_date_end_after_count,
r->count);
}
} else {
time_t t = icaltime_as_timet (r->until);
e_dialog_toggle_set (priv->recurrence_ending_date_end_on, TRUE);
/* Shorten by one day, as we store end-on date a day ahead */
/* FIXME is this correct? */
e_date_edit_set_time (E_DATE_EDIT (priv->recurrence_ending_date_end_on_date), time_add_day (t, -1));
}
cal_component_free_recur_list (list);
}
/* Exceptions list */
#ifndef NO_WARNINGS
#warning "FIX ME"
#endif
/* Need to handle exception rules as well as dates */
cal_component_get_exdate_list (priv->comp, &list);
for (l = list; l; l = l->next) {
struct icaltimetype *t;
time_t ext;
t = l->data;
ext = icaltime_as_timet (*t);
append_exception (ee, ext);
}
cal_component_free_exdate_list (list);
}
/* Decode the radio button group for classifications */
static CalComponentClassification
classification_get (GtkWidget *widget)
{
return e_dialog_radio_get (widget, classification_map);
}
/* Get the values of the widgets in the event editor and put them in the iCalObject */
static void
dialog_to_comp_object (EventEditor *ee)
{
EventEditorPrivate *priv;
CalComponent *comp;
CalComponentText *text;
CalComponentDateTime date;
struct icalrecurrencetype recur;
time_t t;
gboolean all_day_event;
GtkCList *exception_list;
GSList *list;
int i, pos = 0;
priv = ee->priv;
comp = priv->comp;
text = g_new0 (CalComponentText, 1);
text->value = e_dialog_editable_get (priv->general_summary);
cal_component_set_summary (comp, text);
list = NULL;
text->value = e_dialog_editable_get (priv->description);
list = g_slist_prepend (list, text);
cal_component_set_description_list (comp, list);
cal_component_free_text_list (list);
date.value = g_new (struct icaltimetype, 1);
t = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
*date.value = icaltime_from_timet (t, FALSE, FALSE);
date.tzid = NULL;
cal_component_set_dtstart (comp, &date);
/* 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 (all_day_event)
t = time_day_end (t);
*date.value = icaltime_from_timet (t, FALSE, FALSE);
cal_component_set_dtend (comp, &date);
g_free (date.value);
#if 0
ico->dalarm.enabled = e_dialog_toggle_get (priv->alarm_display);
ico->aalarm.enabled = e_dialog_toggle_get (priv->alarm_program);
ico->palarm.enabled = e_dialog_toggle_get (priv->alarm_audio);
ico->malarm.enabled = e_dialog_toggle_get (priv->alarm_mail);
ico->dalarm.count = e_dialog_spin_get_int (priv->alarm_display_amount);
ico->aalarm.count = e_dialog_spin_get_int (priv->alarm_audio_amount);
ico->palarm.count = e_dialog_spin_get_int (priv->alarm_program_amount);
ico->malarm.count = e_dialog_spin_get_int (priv->alarm_mail_amount);
ico->dalarm.units = alarm_unit_get (priv->alarm_display_unit);
ico->aalarm.units = alarm_unit_get (priv->alarm_audio_unit);
ico->palarm.units = alarm_unit_get (priv->alarm_program_unit);
ico->malarm.units = alarm_unit_get (priv->alarm_mail_unit);
if (ico->palarm.data)
g_free (ico->palarm.data);
if (ico->malarm.data)
g_free (ico->malarm.data);
ico->palarm.data = e_dialog_editable_get (priv->alarm_program_run_program_entry);
ico->malarm.data = e_dialog_editable_get (priv->alarm_mail_mail_to);
#endif
cal_component_set_classification (comp, classification_get (priv->classification_radio));
/* Recurrence information */
icalrecurrencetype_clear (&recur);
recur.freq = recur_options_get (priv->recurrence_rule_none);
switch (recur.freq) {
case ICAL_NO_RECURRENCE:
/* nothing */
break;
case ICAL_DAILY_RECURRENCE:
recur.interval = e_dialog_spin_get_int (priv->recurrence_rule_daily_days);
break;
case ICAL_WEEKLY_RECURRENCE:
recur.interval = e_dialog_spin_get_int (priv->recurrence_rule_weekly_weeks);
if (e_dialog_toggle_get (priv->recurrence_rule_weekly_sun))
recur.by_day[pos++] = ICAL_SUNDAY_WEEKDAY;
if (e_dialog_toggle_get (priv->recurrence_rule_weekly_mon))
recur.by_day[pos++] = ICAL_MONDAY_WEEKDAY;
if (e_dialog_toggle_get (priv->recurrence_rule_weekly_tue))
recur.by_day[pos++] = ICAL_TUESDAY_WEEKDAY;
if (e_dialog_toggle_get (priv->recurrence_rule_weekly_wed))
recur.by_day[pos++] = ICAL_WEDNESDAY_WEEKDAY;
if (e_dialog_toggle_get (priv->recurrence_rule_weekly_thu))
recur.by_day[pos++] = ICAL_THURSDAY_WEEKDAY;
if (e_dialog_toggle_get (priv->recurrence_rule_weekly_fri))
recur.by_day[pos++] = ICAL_FRIDAY_WEEKDAY;
if (e_dialog_toggle_get (priv->recurrence_rule_weekly_sat))
recur.by_day[pos++] = ICAL_SATURDAY_WEEKDAY;
break;
case ICAL_MONTHLY_RECURRENCE:
if (e_dialog_toggle_get (priv->recurrence_rule_monthly_on_day)) {
/* by day of in the month (ex: the 5th) */
recur.by_month_day[0] =
e_dialog_spin_get_int (priv->recurrence_rule_monthly_day_nth);
} else if (e_dialog_toggle_get (priv->recurrence_rule_monthly_weekday)) {
/* "recurrence-rule-monthly-weekday" is TRUE */
/* by position on the calendar (ex: 2nd monday) */
/* libical does not handle this yet */
/* ico->recur->u.month_pos = e_dialog_option_menu_get ( */
/* priv->recurrence_rule_monthly_week, */
/* month_pos_map); */
/* ico->recur->weekday = e_dialog_option_menu_get ( */
/* priv->recurrence_rule_monthly_weekpos, */
/* weekday_map); */
} else
g_assert_not_reached ();
recur.interval = e_dialog_spin_get_int (priv->recurrence_rule_monthly_every_n_months);
break;
case ICAL_YEARLY_RECURRENCE:
recur.interval = e_dialog_spin_get_int (priv->recurrence_rule_yearly_every_n_years);
break;
default:
g_assert_not_reached ();
}
if (recur.freq != ICAL_NO_RECURRENCE) {
/* recurrence start of week */
if (week_starts_on_monday)
recur.week_start = ICAL_MONDAY_WEEKDAY;
else
recur.week_start = ICAL_SUNDAY_WEEKDAY;
/* recurrence ending date */
if (e_dialog_toggle_get (priv->recurrence_ending_date_end_on)) {
/* Also here, to ensure that the event is used, we add a day
* to get the next day, in accordance to the RFC
*/
t = e_date_edit_get_time (E_DATE_EDIT (priv->recurrence_ending_date_end_on_date));
t = time_add_day (t, 1);
recur.until = icaltime_from_timet (t, TRUE, FALSE);
} else if (e_dialog_toggle_get (priv->recurrence_ending_date_end_after)) {
recur.count = e_dialog_spin_get_int (priv->recurrence_ending_date_end_after_count);
}
list = NULL;
list = g_slist_append (list, &recur);
cal_component_set_rrule_list (comp, list);
g_slist_free (list);
} else {
list = NULL;
cal_component_set_rrule_list (comp, list);
}
/* Set exceptions */
list = NULL;
exception_list = GTK_CLIST (priv->recurrence_exceptions_list);
for (i = 0; i < exception_list->rows; i++) {
struct icaltimetype *tt;
time_t *t;
t = gtk_clist_get_row_data (exception_list, i);
tt = g_new0 (struct icaltimetype, 1);
*tt = icaltime_from_timet (*t, FALSE, FALSE);
list = g_slist_prepend (list, tt);
}
cal_component_set_exdate_list (comp, list);
if (list)
cal_component_free_exdate_list (list);
cal_component_commit_sequence (comp);
}
static void
save_event_object (EventEditor *ee)
{
EventEditorPrivate *priv;
char *title;
priv = ee->priv;
if (!priv->comp)
return;
dialog_to_comp_object (ee);
title = make_title_from_comp (priv->comp);
gtk_window_set_title (GTK_WINDOW (priv->app), title);
g_free (title);
if (!cal_client_update_object (priv->client, priv->comp))
g_message ("save_event_object(): Could not update the object!");
}
/* Closes the dialog box and emits the appropriate signals */
static void
close_dialog (EventEditor *ee)
{
EventEditorPrivate *priv;
priv = ee->priv;
g_assert (priv->app != NULL);
gtk_object_destroy (GTK_OBJECT (ee));
}
/* File/Save callback */
static void
file_save_cb (GtkWidget *widget, gpointer data)
{
EventEditor *ee;
ee = EVENT_EDITOR (data);
save_event_object (ee);
}
/* File/Save and Close callback */
static void
file_save_and_close_cb (GtkWidget *widget, gpointer data)
{
EventEditor *ee;
ee = EVENT_EDITOR (data);
save_event_object (ee);
close_dialog (ee);
}
/* File/Delete callback */
static void
file_delete_cb (GtkWidget *widget, gpointer data)
{
EventEditor *ee;
EventEditorPrivate *priv;
const char *uid;
ee = EVENT_EDITOR (data);
g_return_if_fail (IS_EVENT_EDITOR (ee));
priv = ee->priv;
g_return_if_fail (priv->comp);
cal_component_get_uid (priv->comp, &uid);
/* We don't check the return value; FALSE can mean the object was not in
* the server anyways.
*/
cal_client_remove_object (priv->client, uid);
close_dialog (ee);
}
/* File/Close callback */
static void
file_close_cb (GtkWidget *widget, gpointer data)
{
EventEditor *ee;
ee = EVENT_EDITOR (data);
g_return_if_fail (IS_EVENT_EDITOR (ee));
close_dialog (ee);
}
/* Menu bar */
static GnomeUIInfo file_new_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Appointment"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Meeting Re_quest"), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Mail Message"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Contact"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Task"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Task _Request"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Journal Entry"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Note"), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Ch_oose Form..."), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo file_page_setup_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Memo Style"), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Define Print _Styles..."), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo file_menu[] = {
GNOMEUIINFO_MENU_NEW_SUBTREE (file_new_menu),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: S_end"), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_MENU_SAVE_ITEM (file_save_cb, NULL),
GNOMEUIINFO_MENU_SAVE_AS_ITEM (NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Save Attac_hments..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_STOCK (N_("_Delete"), NULL,
file_delete_cb, GNOME_STOCK_PIXMAP_TRASH),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Move to Folder..."), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Cop_y to Folder..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_SUBTREE (N_("Page Set_up"), file_page_setup_menu),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Print Pre_view"), NULL, NULL),
GNOMEUIINFO_MENU_PRINT_ITEM (NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_MENU_PROPERTIES_ITEM (NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_MENU_CLOSE_ITEM (file_close_cb, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo edit_object_menu[] = {
GNOMEUIINFO_ITEM_NONE ("FIXME: what goes here?", NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo edit_menu[] = {
GNOMEUIINFO_MENU_UNDO_ITEM (NULL, NULL),
GNOMEUIINFO_MENU_REDO_ITEM (NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_MENU_CUT_ITEM (NULL, NULL),
GNOMEUIINFO_MENU_COPY_ITEM (NULL, NULL),
GNOMEUIINFO_MENU_PASTE_ITEM (NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Paste _Special..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_MENU_CLEAR_ITEM (NULL, NULL),
GNOMEUIINFO_MENU_SELECT_ALL_ITEM (NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Mark as U_nread"), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_MENU_FIND_ITEM (NULL, NULL),
GNOMEUIINFO_MENU_FIND_AGAIN_ITEM (NULL, NULL),
GNOMEUIINFO_SUBTREE (N_("_Object"), edit_object_menu),
GNOMEUIINFO_END
};
static GnomeUIInfo view_previous_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Item"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Unread Item"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Fi_rst Item in Folder"), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo view_next_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Item"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Unread Item"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Last Item in Folder"), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo view_toolbars_menu[] = {
{ GNOME_APP_UI_TOGGLEITEM, N_("FIXME: _Standard"), NULL, NULL, NULL, NULL,
GNOME_APP_PIXMAP_NONE, NULL, 0, 0, NULL },
{ GNOME_APP_UI_TOGGLEITEM, N_("FIXME: __Formatting"), NULL, NULL, NULL, NULL,
GNOME_APP_PIXMAP_NONE, NULL, 0, 0, NULL },
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Customize..."), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo view_menu[] = {
GNOMEUIINFO_SUBTREE (N_("Pre_vious"), view_previous_menu),
GNOMEUIINFO_SUBTREE (N_("Ne_xt"), view_next_menu),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Ca_lendar..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_SUBTREE (N_("_Toolbars"), view_toolbars_menu),
GNOMEUIINFO_END
};
static GnomeUIInfo insert_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _File..."), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: It_em..."), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Object..."), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo format_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Font..."), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Paragraph..."), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo tools_forms_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Ch_oose Form..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Desi_gn This Form"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: D_esign a Form..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Publish _Form..."), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Pu_blish Form As..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Script _Debugger"), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo tools_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _Spelling..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Chec_k Names"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Address _Book..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_SUBTREE (N_("_Forms"), tools_forms_menu),
GNOMEUIINFO_END
};
static GnomeUIInfo actions_menu[] = {
GNOMEUIINFO_ITEM_NONE (N_("FIXME: _New Appointment"), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Rec_urrence..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Invite _Attendees..."), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: C_ancel Invitation..."), NULL, NULL),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("FIXME: Forward as v_Calendar"), NULL, NULL),
GNOMEUIINFO_ITEM_NONE (N_("FIXME: For_ward"), NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo help_menu[] = {
GNOMEUIINFO_ITEM_NONE ("FIXME: fix Bonobo so it supports help items!", NULL, NULL),
GNOMEUIINFO_END
};
static GnomeUIInfo main_menu[] = {
GNOMEUIINFO_MENU_FILE_TREE (file_menu),
GNOMEUIINFO_MENU_EDIT_TREE (edit_menu),
GNOMEUIINFO_MENU_VIEW_TREE (view_menu),
GNOMEUIINFO_SUBTREE (N_("_Insert"), insert_menu),
GNOMEUIINFO_SUBTREE (N_("F_ormat"), format_menu),
GNOMEUIINFO_SUBTREE (N_("_Tools"), tools_menu),
GNOMEUIINFO_SUBTREE (N_("Actio_ns"), actions_menu),
GNOMEUIINFO_MENU_HELP_TREE (help_menu),
GNOMEUIINFO_END
};
/* Creates the menu bar for the event editor */
static void
create_menu (EventEditor *ee)
{
EventEditorPrivate *priv;
BonoboUIHandlerMenuItem *list;
priv = ee->priv;
bonobo_ui_handler_create_menubar (priv->uih);
list = bonobo_ui_handler_menu_parse_uiinfo_list_with_data (main_menu, ee);
bonobo_ui_handler_menu_add_list (priv->uih, "/", list);
}
/* Toolbar */
static GnomeUIInfo toolbar[] = {
GNOMEUIINFO_ITEM_STOCK (N_("Save and Close"),
N_("Save and close this appointment"),
file_save_and_close_cb,
GNOME_STOCK_PIXMAP_SAVE),
GNOMEUIINFO_ITEM_STOCK (N_("Delete"),
N_("Delete this appointment"),
file_delete_cb,
GNOME_STOCK_PIXMAP_TRASH),
GNOMEUIINFO_ITEM_STOCK (N_("Close"),
N_("Close this appointment"),
file_close_cb,
GNOME_STOCK_PIXMAP_CLOSE),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_STOCK (N_("FIXME: Print..."),
N_("Print this item"), NULL,
GNOME_STOCK_PIXMAP_PRINT),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_STOCK (N_("FIXME: Insert File..."),
N_("Insert a file as an attachment"), NULL,
GNOME_STOCK_PIXMAP_ATTACH),
#if 0
GNOMEUIINFO_ITEM_STOCK (N_("FIXME: Invite Attendees..."),
N_("Invite attendees to a meeting"), NULL,
GNOME_STOCK_PIXMAP_MULTIPLE),
#endif
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_STOCK (N_("FIXME: Previous"),
N_("Go to the previous item"), NULL,
GNOME_STOCK_PIXMAP_BACK),
GNOMEUIINFO_ITEM_STOCK (N_("FIXME: Next"),
N_("Go to the next item"), NULL,
GNOME_STOCK_PIXMAP_FORWARD),
GNOMEUIINFO_ITEM_STOCK (N_("FIXME: Help"),
N_("See online help"), NULL,
GNOME_STOCK_PIXMAP_HELP),
GNOMEUIINFO_END
};
/* Creates the toolbar for the event editor */
static void
create_toolbar (EventEditor *ee)
{
EventEditorPrivate *priv;
BonoboUIHandlerToolbarItem *list;
priv = ee->priv;
bonobo_ui_handler_create_toolbar (priv->uih, "Toolbar");
list = bonobo_ui_handler_toolbar_parse_uiinfo_list_with_data (toolbar, ee);
bonobo_ui_handler_toolbar_add_list (priv->uih, "/Toolbar", list);
}
/* Callback used when the dialog box is destroyed */
static gint
app_delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
{
EventEditor *ee;
/* FIXME: need to check for a dirty object */
ee = EVENT_EDITOR (data);
close_dialog (ee);
return TRUE;
}
/**
* event_editor_construct:
* @ee: An event editor.
*
* Constructs an event editor by loading its Glade data.
*
* Return value: The same object as @ee, or NULL if the widgets could not be
* created. In the latter case, the event editor will automatically be
* destroyed.
**/
EventEditor *
event_editor_construct (EventEditor *ee)
{
EventEditorPrivate *priv;
GtkWidget *bonobo_win;
g_return_val_if_fail (ee != NULL, NULL);
g_return_val_if_fail (IS_EVENT_EDITOR (ee), NULL);
priv = ee->priv;
/* Load the content widgets */
priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/event-editor-dialog.glade", NULL);
if (!priv->xml) {
g_message ("event_editor_construct(): Could not load the Glade XML file!");
goto error;
}
if (!get_widgets (ee)) {
g_message ("event_editor_construct(): Could not find all widgets in the XML file!");
goto error;
}
init_widgets (ee);
/* Construct the app */
bonobo_win = bonobo_win_new ("event-editor-dialog", "Event Editor");
/* FIXME: The sucking bit */
{
GtkWidget *contents;
contents = gnome_dock_get_client_area (
GNOME_DOCK (GNOME_APP (priv->app)->dock));
if (!contents) {
g_message ("event_editor_construct(): Could not get contents");
goto error;
}
gtk_widget_ref (contents);
gtk_container_remove (GTK_CONTAINER (contents->parent), contents);
bonobo_win_set_contents (BONOBO_WIN (bonobo_win), contents);
gtk_widget_destroy (priv->app);
priv->app = bonobo_win;
}
priv->uih = bonobo_ui_handler_new ();
if (!priv->uih) {
g_message ("event_editor_construct(): Could not create the UI handler");
goto error;
}
bonobo_ui_handler_set_app (priv->uih, BONOBO_WIN (priv->app));
create_menu (ee);
create_toolbar (ee);
/* Hook to destruction of the dialog */
gtk_signal_connect (GTK_OBJECT (priv->app), "delete_event",
GTK_SIGNAL_FUNC (app_delete_event_cb), ee);
/* Show the dialog */
gtk_widget_show (priv->app);
return ee;
error:
gtk_object_unref (GTK_OBJECT (ee));
return NULL;
}
/**
* event_editor_new:
*
* Creates a new event editor dialog.
*
* Return value: A newly-created event editor dialog, or NULL if the event
* editor could not be created.
**/
EventEditor *
event_editor_new (void)
{
EventEditor *ee;
ee = EVENT_EDITOR (gtk_type_new (TYPE_EVENT_EDITOR));
return event_editor_construct (EVENT_EDITOR (ee));
}
/* Brings attention to a window by raising it and giving it focus */
static void
raise_and_focus (GtkWidget *widget)
{
g_assert (GTK_WIDGET_REALIZED (widget));
gdk_window_show (widget->window);
gtk_widget_grab_focus (widget);
}
/* Callback used when the calendar client tells us that an object changed */
static void
obj_updated_cb (CalClient *client, const char *uid, gpointer data)
{
EventEditor *ee;
EventEditorPrivate *priv;
CalComponent *comp;
CalClientGetStatus status;
const gchar *editing_uid;
ee = EVENT_EDITOR (data);
g_return_if_fail (IS_EVENT_EDITOR (ee));
priv = ee->priv;
/* If we aren't showing the object which has been updated, return. */
if (!priv->comp)
return;
cal_component_get_uid (priv->comp, &editing_uid);
if (strcmp (uid, editing_uid))
return;
/* Get the event from the server. */
status = cal_client_get_object (priv->client, uid, &comp);
switch (status) {
case CAL_CLIENT_GET_SUCCESS:
/* Everything is fine */
break;
case CAL_CLIENT_GET_SYNTAX_ERROR:
g_message ("obj_updated_cb(): Syntax error when getting object `%s'", uid);
return;
case CAL_CLIENT_GET_NOT_FOUND:
/* The object is no longer in the server, so do nothing */
return;
default:
g_assert_not_reached ();
return;
}
raise_and_focus (priv->app);
}
/* Callback used when the calendar client tells us that an object was removed */
static void
obj_removed_cb (CalClient *client, const char *uid, gpointer data)
{
EventEditor *ee;
EventEditorPrivate *priv;
const gchar *editing_uid;
ee = EVENT_EDITOR (data);
g_return_if_fail (ee != NULL);
g_return_if_fail (IS_EVENT_EDITOR (ee));
priv = ee->priv;
/* If we aren't showing the object which has been updated, return. */
if (!priv->comp)
return;
cal_component_get_uid (priv->comp, &editing_uid);
if (strcmp (uid, editing_uid))
return;
raise_and_focus (priv->app);
}
void
event_editor_set_cal_client (EventEditor *ee, CalClient *client)
{
EventEditorPrivate *priv;
g_return_if_fail (ee != NULL);
g_return_if_fail (IS_EVENT_EDITOR (ee));
priv = ee->priv;
if (client == priv->client)
return;
if (client)
g_return_if_fail (IS_CAL_CLIENT (client));
if (client)
g_return_if_fail (cal_client_is_loaded (client));
if (client)
gtk_object_ref (GTK_OBJECT (client));
if (priv->client) {
gtk_signal_disconnect_by_data (GTK_OBJECT (priv->client), ee);
gtk_object_unref (GTK_OBJECT (priv->client));
}
priv->client = client;
if (priv->client) {
gtk_signal_connect (GTK_OBJECT (priv->client), "obj_updated",
GTK_SIGNAL_FUNC (obj_updated_cb), ee);
gtk_signal_connect (GTK_OBJECT (priv->client), "obj_removed",
GTK_SIGNAL_FUNC (obj_removed_cb), ee);
}
}
/**
* event_editor_set_event_object:
* @ee: An event editor.
* @comp: A calendar object.
*
* Sets the calendar object that an event editor dialog will manipulate.
**/
void
event_editor_set_event_object (EventEditor *ee, CalComponent *comp)
{
EventEditorPrivate *priv;
char *title;
g_return_if_fail (ee != NULL);
g_return_if_fail (IS_EVENT_EDITOR (ee));
priv = ee->priv;
if (priv->comp) {
gtk_object_unref (GTK_OBJECT (priv->comp));
priv->comp = NULL;
}
if (comp) {
priv->comp = cal_component_clone (comp);
}
title = make_title_from_comp (priv->comp);
gtk_window_set_title (GTK_WINDOW (priv->app), title);
g_free (title);
fill_widgets (ee);
}
/**
* event_editor_focus:
* @ee: An event editor.
*
* Makes sure an event editor is shown, on top of other windows, and focused.
**/
void
event_editor_focus (EventEditor *ee)
{
EventEditorPrivate *priv;
g_return_if_fail (ee != NULL);
g_return_if_fail (IS_EVENT_EDITOR (ee));
priv = ee->priv;
gtk_widget_show_now (priv->app);
raise_and_focus (priv->app);
}
static void
alarm_toggle (GtkWidget *toggle, EventEditor *ee)
{
EventEditorPrivate *priv;
GtkWidget *alarm_amount = NULL;
GtkWidget *alarm_unit = NULL;
gboolean active;
priv = ee->priv;
active = GTK_TOGGLE_BUTTON (toggle)->active;
if (toggle == priv->alarm_display) {
alarm_amount = priv->alarm_display_amount;
alarm_unit = priv->alarm_display_unit;
} else if (toggle == priv->alarm_audio) {
alarm_amount = priv->alarm_audio_amount;
alarm_unit = priv->alarm_audio_unit;
} else if (toggle == priv->alarm_program) {
alarm_amount = priv->alarm_program_amount;
alarm_unit = priv->alarm_program_unit;
gtk_widget_set_sensitive (priv->alarm_program_run_program, active);
} else if (toggle == priv->alarm_mail) {
alarm_amount = priv->alarm_mail_amount;
alarm_unit = priv->alarm_mail_unit;
gtk_widget_set_sensitive (priv->alarm_mail_mail_to, active);
} else
g_assert_not_reached ();
gtk_widget_set_sensitive (alarm_amount, active);
gtk_widget_set_sensitive (alarm_unit, active);
}
/*
* Checks if the day range occupies all the day, and if so, check the
* box accordingly
*/
static void
check_all_day (EventEditor *ee)
{
EventEditorPrivate *priv;
time_t ev_start, ev_end;
priv = ee->priv;
ev_start = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
ev_end = e_date_edit_get_time (E_DATE_EDIT (priv->end_time));
/* all day event checkbox */
if (time_day_begin (ev_start) == ev_start
&& time_day_begin (ev_end) == ev_end)
e_dialog_toggle_set (priv->all_day_event, TRUE);
else
e_dialog_toggle_set (priv->all_day_event, FALSE);
}
/*
* Callback: all day event box clicked
*/
static void
set_all_day (GtkWidget *toggle, EventEditor *ee)
{
EventEditorPrivate *priv;
struct tm start_tm, end_tm;
time_t start_t, end_t;
gboolean all_day;
priv = ee->priv;
/* If the all_day toggle is set, the end date is inclusive of the
entire day on which it points to. */
all_day = GTK_TOGGLE_BUTTON (toggle)->active;
start_t = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
start_tm = *localtime (&start_t);
start_tm.tm_min = 0;
start_tm.tm_sec = 0;
if (all_day)
start_tm.tm_hour = 0;
else
start_tm.tm_hour = day_begin;
e_date_edit_set_time (E_DATE_EDIT (priv->start_time),
mktime (&start_tm));
end_t = e_date_edit_get_time (E_DATE_EDIT (priv->end_time));
end_tm = *localtime (&end_t);
end_tm.tm_min = 0;
end_tm.tm_sec = 0;
if (all_day) {
/* mktime() will fix this if we go past the end of the month.*/
end_tm.tm_hour = 0;
} else {
if (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)
end_tm.tm_hour = start_tm.tm_hour + 1;
}
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);
e_date_edit_set_time (E_DATE_EDIT (priv->end_time), mktime (&end_tm));
}
/*
* Callback: checks that the dates are start < end
*/
static void
check_dates (EDateEdit *dedit, EventEditor *ee)
{
EventEditorPrivate *priv;
time_t start, end;
struct tm tm_start, tm_end;
priv = ee->priv;
start = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
end = e_date_edit_get_time (E_DATE_EDIT (priv->end_time));
if (start > end) {
tm_start = *localtime (&start);
tm_end = *localtime (&end);
if (GTK_WIDGET (dedit) == priv->start_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;
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->end_time), ee);
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), ee);
} else if (GTK_WIDGET (dedit) == priv->end_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;
#if 0
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->start_time), ee);
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), ee);
#endif
}
}
}
/*
* Callback: checks that start_time < end_time and whether the
* selected hour range spans all of the day
*/
static void
check_times (EDateEdit *dedit, EventEditor *ee)
{
EventEditorPrivate *priv;
time_t start, end;
struct tm tm_start, tm_end;
priv = ee->priv;
#if 0
gdk_pointer_ungrab (GDK_CURRENT_TIME);
gdk_flush ();
#endif
start = e_date_edit_get_time (E_DATE_EDIT (priv->start_time));
end = e_date_edit_get_time (E_DATE_EDIT (priv->end_time));
if (start > end) {
tm_start = *localtime (&start);
tm_end = *localtime (&end);
if (GTK_WIDGET (dedit) == priv->start_time) {
tm_end.tm_min = tm_start.tm_min;
tm_end.tm_sec = tm_start.tm_sec;
tm_end.tm_hour = tm_start.tm_hour + 1;
if (tm_end.tm_hour >= 24) {
tm_end.tm_hour = 24; /* mktime() will bump the day */
tm_end.tm_min = 0;
tm_end.tm_sec = 0;
}
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->end_time), ee);
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), ee);
} else if (GTK_WIDGET (dedit) == priv->end_time) {
tm_start.tm_min = tm_end.tm_min;
tm_start.tm_sec = tm_end.tm_sec;
tm_start.tm_hour = tm_end.tm_hour - 1;
if (tm_start.tm_hour < 0) {
tm_start.tm_hour = 0;
tm_start.tm_min = 0;
tm_start.tm_min = 0;
}
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->start_time), ee);
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), ee);
}
}
/* Check whether the event spans the whole day */
check_all_day (ee);
}
static void
recurrence_toggled (GtkWidget *radio, EventEditor *ee)
{
EventEditorPrivate *priv;
icalrecurrencetype_frequency rf;
priv = ee->priv;
if (!GTK_TOGGLE_BUTTON (radio)->active)
return;
rf = e_dialog_radio_get (radio, recur_options_map);
/* This is a hack to get things working */
gtk_notebook_set_page (GTK_NOTEBOOK (priv->recurrence_rule_notebook),
(int) (rf - ICAL_HOURLY_RECURRENCE));
}
static char *
get_exception_string (time_t t)
{
static char buf[256];
strftime (buf, sizeof (buf), _("%a %b %d %Y"), localtime (&t));
return buf;
}
static void
append_exception (EventEditor *ee, time_t t)
{
EventEditorPrivate *priv;
time_t *tt;
char *c[1];
int i;
GtkCList *clist;
priv = ee->priv;
tt = g_new (time_t, 1);
*tt = t;
clist = GTK_CLIST (priv->recurrence_exceptions_list);
c[0] = get_exception_string (t);
i = e_utf8_gtk_clist_append (clist, c);
gtk_clist_set_row_data (clist, i, tt);
gtk_clist_select_row (clist, i, 0);
/* gtk_widget_set_sensitive (ee->recur_ex_vbox, TRUE); */
}
static void
recurrence_exception_added (GtkWidget *widget, EventEditor *ee)
{
EventEditorPrivate *priv;
time_t t;
priv = ee->priv;
t = e_date_edit_get_time (E_DATE_EDIT (priv->recurrence_exceptions_date));
append_exception (ee, t);
}
static void
recurrence_exception_deleted (GtkWidget *widget, EventEditor *ee)
{
EventEditorPrivate *priv;
GtkCList *clist;
int sel;
priv = ee->priv;
clist = GTK_CLIST (priv->recurrence_exceptions_list);
if (!clist->selection)
return;
sel = GPOINTER_TO_INT (clist->selection->data);
g_free (gtk_clist_get_row_data (clist, sel)); /* free the time_t stored there */
gtk_clist_remove (clist, sel);
if (sel >= clist->rows)
sel--;
gtk_clist_select_row (clist, sel, 0);
}
static void
recurrence_exception_changed (GtkWidget *widget, EventEditor *ee)
{
EventEditorPrivate *priv;
GtkCList *clist;
time_t *t;
int sel;
priv = ee->priv;
clist = GTK_CLIST (priv->recurrence_exceptions_list);
if (!clist->selection)
return;
sel = GPOINTER_TO_INT (clist->selection->data);
t = gtk_clist_get_row_data (clist, sel);
*t = e_date_edit_get_time (E_DATE_EDIT (priv->recurrence_exceptions_date));
e_utf8_gtk_clist_set_text (clist, sel, 0, get_exception_string (*t));
}
GtkWidget *
make_date_edit (void)
{
return date_edit_new (time (NULL), FALSE);
}
GtkWidget *
make_date_edit_with_time (void)
{
return date_edit_new (time (NULL), TRUE);
}
GtkWidget *
date_edit_new (time_t the_time, int show_time)
{
GtkWidget *dedit;
dedit = e_date_edit_new ();
/* FIXME: Set other options. */
e_date_edit_set_show_time (E_DATE_EDIT (dedit), show_time);
e_date_edit_set_time_popup_range (E_DATE_EDIT (dedit), 8, 18);
return dedit;
}
GtkWidget *
make_spin_button (int val, int low, int high)
{
GtkAdjustment *adj;
GtkWidget *spin;
adj = GTK_ADJUSTMENT (gtk_adjustment_new (val, low, high, 1, 10, 10));
spin = gtk_spin_button_new (adj, 0.5, 0);
gtk_widget_set_usize (spin, 60, 0);
return spin;
}
/* todo
build some of the recur stuff by hand to take into account
the start-on-monday preference?
get the apply button to work right
make the properties stuff unglobal
figure out why alarm units aren't sticking between edits
closing the dialog window with the wm caused a crash
Gtk-WARNING **: invalid cast from `(unknown)' to `GnomeDialog'
on line 669: gnome_dialog_close (GNOME_DIALOG(dialog->dialog));
*/