Revision 26881 (by juha, 2008/04/21 20:49:01) minor changes from a trial version.
/*      Orage - Calendar and alarm handler
 *
 * Copyright (c) 2005-2008 Juha Kautto  (juha at xfce.org)
 * Copyright (c) 2004-2005 Mickael Graf (korbinus at xfce.org)
 *
 * 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, 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
       51 Franklin Street, 5th Floor
       Boston, MA 02110-1301 USA

 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#define _XOPEN_SOURCE /* glibc2 needs this */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <locale.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>

#include <libxfcegui4/libxfcegui4.h>

#include "functions.h"
#include "mainbox.h"
#include "ical-code.h"
#include "event-list.h"
#include "day-view.h"
#include "appointment.h"
#include "parameters.h"

#define BORDER_SIZE 20
#define FILETYPE_SIZE 38


typedef struct _orage_category_win
{
    GtkWidget *window;
    GtkWidget *vbox;

    GtkWidget *new_frame;
    GtkWidget *new_entry;
    GtkWidget *new_color_button;
    GtkWidget *new_add_button;

    GtkWidget *cur_frame;
    GtkWidget *cur_frame_vbox;

    GtkTooltips *tooltips;
    GtkAccelGroup *accelgroup;

    gpointer *apptw;
} category_win_struct;

static void refresh_categories(category_win_struct *catw);
static void read_default_alarm(xfical_appt *appt);

static void fill_appt_window(appt_win *apptw, char *action, char *par);
/*  
 *  the previous and the following are the main functions in this file
static gboolean fill_appt_from_apptw(xfical_appt *appt, appt_win *apptw)
*/ 


static GtkWidget *datetime_hbox_new(GtkWidget *date_button
        , GtkWidget *spin_hh, GtkWidget *spin_mm
        , GtkWidget *timezone_button)
{
    GtkWidget *hbox, *space_label;

    hbox = gtk_hbox_new(FALSE, 0);

    gtk_box_pack_start(GTK_BOX(hbox), date_button, FALSE, FALSE, 0);

    space_label = gtk_label_new("  ");
    gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);

    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spin_hh), TRUE);
    gtk_widget_set_size_request(spin_hh, 40, -1);
    gtk_box_pack_start(GTK_BOX(hbox), spin_hh, FALSE, FALSE, 0);

    space_label = gtk_label_new(":");
    gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);

    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spin_mm), TRUE);
    gtk_widget_set_size_request(spin_mm, 40, -1);
    gtk_box_pack_start(GTK_BOX(hbox), spin_mm, FALSE, FALSE, 0);

    space_label = gtk_label_new("  ");
    gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);

    gtk_box_pack_start(GTK_BOX(hbox), timezone_button, TRUE, TRUE, 0);

    return(hbox);
}

static GtkWidget *period_hbox_new(gboolean head_space, gboolean tail_space
        , GtkWidget *spin_dd, GtkWidget *dd_label
        , GtkWidget *spin_hh, GtkWidget *hh_label
        , GtkWidget *spin_mm, GtkWidget *mm_label)
{
    GtkWidget *hbox, *space_label;

    hbox = gtk_hbox_new(FALSE, 0);

    if (head_space) {
        space_label = gtk_label_new("   ");
        gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
    }

    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spin_dd), TRUE);
    gtk_box_pack_start(GTK_BOX(hbox), spin_dd, FALSE, FALSE, 0);

    gtk_box_pack_start(GTK_BOX(hbox), dd_label, FALSE, FALSE, 5);
    
    space_label = gtk_label_new("   ");
    gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);

    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spin_hh), TRUE);
    gtk_widget_set_size_request(spin_hh, 40, -1);
    gtk_box_pack_start(GTK_BOX(hbox), spin_hh, FALSE, FALSE, 0);

    gtk_box_pack_start(GTK_BOX(hbox), hh_label, FALSE, FALSE, 5);
    
    space_label = gtk_label_new("   ");
    gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);

    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spin_mm), TRUE);
    gtk_widget_set_size_request(spin_mm, 40, -1);
    gtk_box_pack_start(GTK_BOX(hbox), spin_mm, FALSE, FALSE, 0);

    gtk_box_pack_start(GTK_BOX(hbox), mm_label, FALSE, FALSE, 5);

    if (tail_space) {
        space_label = gtk_label_new("   ");
        gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
    }

    return hbox;
}

static void mark_appointment_changed(appt_win *apptw)
{
    if (!apptw->appointment_changed) {
        apptw->appointment_changed = TRUE;
        gtk_widget_set_sensitive(apptw->Revert, TRUE);
        gtk_widget_set_sensitive(apptw->File_menu_revert, TRUE);
    }
}

static void mark_appointment_unchanged(appt_win *apptw)
{
    if (apptw->appointment_changed) {
        apptw->appointment_changed = FALSE;
        gtk_widget_set_sensitive(apptw->Revert, FALSE);
        gtk_widget_set_sensitive(apptw->File_menu_revert, FALSE);
    }
}

static void set_time_sensitivity(appt_win *apptw)
{
    gboolean dur_act, allDay_act, comp_act, due_act;

    dur_act = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Dur_checkbutton));
    allDay_act = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->AllDay_checkbutton));
    comp_act = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Completed_checkbutton));
        /* todo can be without due date, but event always has end date.
         * Journal has none, but we show it similarly than event */
    if (gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Type_todo_rb)))
        due_act = gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->End_checkbutton));
    else 
        due_act = TRUE;

    gtk_widget_set_sensitive(apptw->Dur_checkbutton, due_act);
    if (allDay_act) {
        gtk_widget_set_sensitive(apptw->StartTime_spin_hh, FALSE);
        gtk_widget_set_sensitive(apptw->StartTime_spin_mm, FALSE);
        gtk_widget_set_sensitive(apptw->StartTimezone_button, FALSE);
        gtk_widget_set_sensitive(apptw->EndTime_spin_hh, FALSE);
        gtk_widget_set_sensitive(apptw->EndTime_spin_mm, FALSE);
        gtk_widget_set_sensitive(apptw->EndTimezone_button, FALSE);
        gtk_widget_set_sensitive(apptw->Dur_spin_hh, FALSE);
        gtk_widget_set_sensitive(apptw->Dur_spin_hh_label, FALSE);
        gtk_widget_set_sensitive(apptw->Dur_spin_mm, FALSE);
        gtk_widget_set_sensitive(apptw->Dur_spin_mm_label, FALSE);
        gtk_widget_set_sensitive(apptw->CompletedTime_spin_hh, FALSE);
        gtk_widget_set_sensitive(apptw->CompletedTime_spin_mm, FALSE);
        gtk_widget_set_sensitive(apptw->CompletedTimezone_button, FALSE);
        gtk_widget_set_sensitive(apptw->CompletedDate_button, comp_act);
        if (dur_act) {
            gtk_widget_set_sensitive(apptw->EndDate_button, FALSE);
            gtk_widget_set_sensitive(apptw->Dur_spin_dd, due_act);
            gtk_widget_set_sensitive(apptw->Dur_spin_dd_label, due_act);
        }
        else {
            gtk_widget_set_sensitive(apptw->EndDate_button, due_act);
            gtk_widget_set_sensitive(apptw->Dur_spin_dd, FALSE);
            gtk_widget_set_sensitive(apptw->Dur_spin_dd_label, FALSE);
        }
    }
    else {
        gtk_widget_set_sensitive(apptw->StartTime_spin_hh, TRUE);
        gtk_widget_set_sensitive(apptw->StartTime_spin_mm, TRUE);
        gtk_widget_set_sensitive(apptw->StartTimezone_button, TRUE);
        if (dur_act) {
            gtk_widget_set_sensitive(apptw->EndDate_button, FALSE);
            gtk_widget_set_sensitive(apptw->EndTime_spin_hh, FALSE);
            gtk_widget_set_sensitive(apptw->EndTime_spin_mm, FALSE);
            gtk_widget_set_sensitive(apptw->EndTimezone_button, FALSE);
            gtk_widget_set_sensitive(apptw->Dur_spin_dd, due_act);
            gtk_widget_set_sensitive(apptw->Dur_spin_dd_label, due_act);
            gtk_widget_set_sensitive(apptw->Dur_spin_hh, due_act);
            gtk_widget_set_sensitive(apptw->Dur_spin_hh_label, due_act);
            gtk_widget_set_sensitive(apptw->Dur_spin_mm, due_act);
            gtk_widget_set_sensitive(apptw->Dur_spin_mm_label, due_act);
        }
        else {
            gtk_widget_set_sensitive(apptw->EndDate_button, due_act);
            gtk_widget_set_sensitive(apptw->EndTime_spin_hh, due_act);
            gtk_widget_set_sensitive(apptw->EndTime_spin_mm, due_act);
            gtk_widget_set_sensitive(apptw->EndTimezone_button, due_act);
            gtk_widget_set_sensitive(apptw->Dur_spin_dd, FALSE);
            gtk_widget_set_sensitive(apptw->Dur_spin_dd_label, FALSE);
            gtk_widget_set_sensitive(apptw->Dur_spin_hh, FALSE);
            gtk_widget_set_sensitive(apptw->Dur_spin_hh_label, FALSE);
            gtk_widget_set_sensitive(apptw->Dur_spin_mm, FALSE);
            gtk_widget_set_sensitive(apptw->Dur_spin_mm_label, FALSE);
        }
        gtk_widget_set_sensitive(apptw->CompletedDate_button, comp_act);
        gtk_widget_set_sensitive(apptw->CompletedTime_spin_hh, comp_act);
        gtk_widget_set_sensitive(apptw->CompletedTime_spin_mm, comp_act);
        gtk_widget_set_sensitive(apptw->CompletedTimezone_button, comp_act);
    }
}

static void set_repeat_sensitivity(appt_win *apptw)
{
    gint freq, i;
    
    freq = gtk_combo_box_get_active(GTK_COMBO_BOX(apptw->Recur_freq_cb));
    if (freq == XFICAL_FREQ_NONE) {
        gtk_widget_set_sensitive(apptw->Recur_limit_rb, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_count_rb, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_count_spin, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_count_label, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_until_rb, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_until_button, FALSE);
        for (i=0; i <= 6; i++) {
            gtk_widget_set_sensitive(apptw->Recur_byday_cb[i], FALSE);
            gtk_widget_set_sensitive(apptw->Recur_byday_spin[i], FALSE);
        }
        gtk_widget_set_sensitive(apptw->Recur_int_spin, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_int_spin_label1, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_int_spin_label2, FALSE);
    }
    else {
        gtk_widget_set_sensitive(apptw->Recur_limit_rb, TRUE);
        gtk_widget_set_sensitive(apptw->Recur_count_rb, TRUE);
        if (gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_count_rb))) {
            gtk_widget_set_sensitive(apptw->Recur_count_spin, TRUE);
            gtk_widget_set_sensitive(apptw->Recur_count_label, TRUE);
        }
        else {
            gtk_widget_set_sensitive(apptw->Recur_count_spin, FALSE);
            gtk_widget_set_sensitive(apptw->Recur_count_label, FALSE);
        }
        gtk_widget_set_sensitive(apptw->Recur_until_rb, TRUE);
        if (gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_until_rb))) {
            gtk_widget_set_sensitive(apptw->Recur_until_button, TRUE);
        }
        else {
            gtk_widget_set_sensitive(apptw->Recur_until_button, FALSE);
        }
        for (i=0; i <= 6; i++) {
            gtk_widget_set_sensitive(apptw->Recur_byday_cb[i], TRUE);
        }
        if (freq == XFICAL_FREQ_MONTHLY || freq == XFICAL_FREQ_YEARLY) {
            for (i=0; i <= 6; i++) {
                gtk_widget_set_sensitive(apptw->Recur_byday_spin[i], TRUE);
            }
        }
        else {
            for (i=0; i <= 6; i++) {
                gtk_widget_set_sensitive(apptw->Recur_byday_spin[i], FALSE);
            }
        }
        gtk_widget_set_sensitive(apptw->Recur_int_spin, TRUE);
        gtk_widget_set_sensitive(apptw->Recur_int_spin_label1, TRUE);
        gtk_widget_set_sensitive(apptw->Recur_int_spin_label2, TRUE);
    }
}

static void recur_hide_show(appt_win *apptw)
{
    if (gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Recur_feature_normal_rb))) {
        gtk_widget_hide(apptw->Recur_byday_label);
        gtk_widget_hide(apptw->Recur_byday_hbox);
        gtk_widget_hide(apptw->Recur_byday_spin_label);
        gtk_widget_hide(apptw->Recur_byday_spin_hbox);
    }
    else {
        gtk_widget_show(apptw->Recur_byday_label);
        gtk_widget_show(apptw->Recur_byday_hbox);
        gtk_widget_show(apptw->Recur_byday_spin_label);
        gtk_widget_show(apptw->Recur_byday_spin_hbox);
    }
}

static void type_hide_show(appt_win *apptw)
{
    if (gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Type_event_rb))) {
        gtk_label_set_text(GTK_LABEL(apptw->End_label), _("End"));
        gtk_widget_show(apptw->Availability_label);
        gtk_widget_show(apptw->Availability_cb);
        gtk_widget_hide(apptw->End_checkbutton);
        gtk_widget_hide(apptw->Completed_label);
        gtk_widget_hide(apptw->Completed_hbox);
        gtk_widget_set_sensitive(apptw->Alarm_notebook_page, TRUE);
        gtk_widget_set_sensitive(apptw->Alarm_tab_label, TRUE);
        gtk_widget_set_sensitive(apptw->Recur_notebook_page, TRUE);
        gtk_widget_set_sensitive(apptw->Recur_tab_label, TRUE);
        gtk_widget_set_sensitive(apptw->Location_label, TRUE);
        gtk_widget_set_sensitive(apptw->Location_entry, TRUE);
        gtk_widget_set_sensitive(apptw->End_label, TRUE);
        gtk_widget_set_sensitive(apptw->EndTime_hbox, TRUE);
        gtk_widget_set_sensitive(apptw->Dur_hbox, TRUE);
    }
    else if (gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Type_todo_rb))) {
        gtk_label_set_text(GTK_LABEL(apptw->End_label), _("Due"));
        gtk_widget_hide(apptw->Availability_label);
        gtk_widget_hide(apptw->Availability_cb);
        gtk_widget_show(apptw->End_checkbutton);
        gtk_widget_show(apptw->Completed_label);
        gtk_widget_show(apptw->Completed_hbox);
        gtk_widget_set_sensitive(apptw->Alarm_notebook_page, TRUE);
        gtk_widget_set_sensitive(apptw->Alarm_tab_label, TRUE);
        gtk_widget_set_sensitive(apptw->Recur_notebook_page, TRUE);
        gtk_widget_set_sensitive(apptw->Recur_tab_label, TRUE);
        gtk_widget_set_sensitive(apptw->Location_label, TRUE);
        gtk_widget_set_sensitive(apptw->Location_entry, TRUE);
        gtk_widget_set_sensitive(apptw->End_label, TRUE);
        gtk_widget_set_sensitive(apptw->EndTime_hbox, TRUE);
        gtk_widget_set_sensitive(apptw->Dur_hbox, TRUE);
    }
    else /* if (gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Type_journal_rb))) */ {
        gtk_label_set_text(GTK_LABEL(apptw->End_label), _("End"));
        gtk_widget_hide(apptw->Availability_label);
        gtk_widget_hide(apptw->Availability_cb);
        gtk_widget_hide(apptw->End_checkbutton);
        gtk_widget_hide(apptw->Completed_label);
        gtk_widget_hide(apptw->Completed_hbox);
        gtk_widget_set_sensitive(apptw->Alarm_notebook_page, FALSE);
        gtk_widget_set_sensitive(apptw->Alarm_tab_label, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_notebook_page, FALSE);
        gtk_widget_set_sensitive(apptw->Recur_tab_label, FALSE);
        gtk_widget_set_sensitive(apptw->Location_label, FALSE);
        gtk_widget_set_sensitive(apptw->Location_entry, FALSE);
        gtk_widget_set_sensitive(apptw->End_label, FALSE);
        gtk_widget_set_sensitive(apptw->EndTime_hbox, FALSE);
        gtk_widget_set_sensitive(apptw->Dur_hbox, FALSE);
    }
    set_time_sensitivity(apptw); /* todo has different settings */
}

static void set_sound_sensitivity(appt_win *apptw)
{
    gboolean sound_act, repeat_act;

    sound_act = gtk_toggle_button_get_active(
	    GTK_TOGGLE_BUTTON(apptw->Sound_checkbutton));
    repeat_act = gtk_toggle_button_get_active(
	    GTK_TOGGLE_BUTTON(apptw->SoundRepeat_checkbutton));

    if (sound_act) {
        gtk_widget_set_sensitive(apptw->Sound_entry, TRUE);
        gtk_widget_set_sensitive(apptw->Sound_button, TRUE);
        gtk_widget_set_sensitive(apptw->SoundRepeat_hbox, TRUE);
        gtk_widget_set_sensitive(apptw->SoundRepeat_checkbutton, TRUE);
        if (repeat_act) {
            gtk_widget_set_sensitive(apptw->SoundRepeat_spin_cnt, TRUE);
            gtk_widget_set_sensitive(apptw->SoundRepeat_spin_cnt_label, TRUE);
            gtk_widget_set_sensitive(apptw->SoundRepeat_spin_len, TRUE);
            gtk_widget_set_sensitive(apptw->SoundRepeat_spin_len_label, TRUE);
        }
        else {
            gtk_widget_set_sensitive(apptw->SoundRepeat_spin_cnt, FALSE);
            gtk_widget_set_sensitive(apptw->SoundRepeat_spin_cnt_label, FALSE);
            gtk_widget_set_sensitive(apptw->SoundRepeat_spin_len, FALSE);
            gtk_widget_set_sensitive(apptw->SoundRepeat_spin_len_label, FALSE);
        }
    }
    else {
        gtk_widget_set_sensitive(apptw->Sound_entry, FALSE);
        gtk_widget_set_sensitive(apptw->Sound_button, FALSE);
        gtk_widget_set_sensitive(apptw->SoundRepeat_hbox, FALSE); /* enough */
    }
}

static void set_notify_sensitivity(appt_win *apptw)
{
#ifdef HAVE_NOTIFY
    gboolean notify_act, expire_act;

    notify_act = gtk_toggle_button_get_active(
	    GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_notify));
    expire_act = gtk_toggle_button_get_active(
	    GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_expire_notify));

    if (notify_act) {
        gtk_widget_set_sensitive(apptw->Display_checkbutton_expire_notify
                , TRUE);
        if (expire_act) {
            gtk_widget_set_sensitive(apptw->Display_spin_expire_notify, TRUE);
            gtk_widget_set_sensitive(apptw->Display_spin_expire_notify_label
                    , TRUE);
        }
        else {
            gtk_widget_set_sensitive(apptw->Display_spin_expire_notify, FALSE);
            gtk_widget_set_sensitive(apptw->Display_spin_expire_notify_label
                    , FALSE);
        }
    }
    else {
        gtk_widget_set_sensitive(apptw->Display_checkbutton_expire_notify
                , FALSE);
        gtk_widget_set_sensitive(apptw->Display_spin_expire_notify, FALSE);
        gtk_widget_set_sensitive(apptw->Display_spin_expire_notify_label
                , FALSE);
    }
#endif
}

static void set_proc_sensitivity(appt_win *apptw)
{
    gboolean proc_act;

    proc_act = gtk_toggle_button_get_active(
	    GTK_TOGGLE_BUTTON(apptw->Proc_checkbutton));

    if (proc_act) {
        gtk_widget_set_sensitive(apptw->Proc_entry, TRUE);
    }
    else {
        gtk_widget_set_sensitive(apptw->Proc_entry, FALSE);
    }
}

static void app_type_checkbutton_clicked_cb(GtkCheckButton *cb
        , gpointer user_data)
{
    type_hide_show((appt_win *)user_data);
    mark_appointment_changed((appt_win *)user_data);
}

static void on_appTitle_entry_changed_cb(GtkEditable *entry
        , gpointer user_data)
{
    gchar *title, *application_name;
    const gchar *appointment_name;
    appt_win *apptw = (appt_win *)user_data;

    appointment_name = gtk_entry_get_text((GtkEntry *)apptw->Title_entry);
    application_name = _("Orage");

    if (ORAGE_STR_EXISTS(appointment_name))
        title = g_strdup_printf("%s - %s", appointment_name, application_name);
    else
        title = g_strdup_printf("%s", application_name);

    gtk_window_set_title(GTK_WINDOW(apptw->Window), (const gchar *)title);

    g_free(title);
    mark_appointment_changed((appt_win *)user_data);
}

static void app_time_checkbutton_clicked_cb(GtkCheckButton *cb
        , gpointer user_data)
{
    set_time_sensitivity((appt_win *)user_data);
    mark_appointment_changed((appt_win *)user_data);
}

static void app_recur_checkbutton_clicked_cb(GtkCheckButton *checkbutton
        , gpointer user_data)
{
    set_repeat_sensitivity((appt_win *)user_data);
    mark_appointment_changed((appt_win *)user_data);
}

static void app_recur_feature_checkbutton_clicked_cb(GtkCheckButton *cb
        , gpointer user_data)
{
    recur_hide_show((appt_win *)user_data);
    mark_appointment_changed((appt_win *)user_data);
}

static void app_sound_checkbutton_clicked_cb(GtkCheckButton *cb
        , gpointer user_data)
{
    set_sound_sensitivity((appt_win *)user_data);
    mark_appointment_changed((appt_win *)user_data);
}

#ifdef HAVE_NOTIFY
static void app_notify_checkbutton_clicked_cb(GtkCheckButton *cb
        , gpointer user_data)
{
    set_notify_sensitivity((appt_win *)user_data);
    mark_appointment_changed((appt_win *)user_data);
}
#endif

static void app_proc_checkbutton_clicked_cb(GtkCheckButton *cb
        , gpointer user_data)
{
    set_proc_sensitivity((appt_win *)user_data);
    mark_appointment_changed((appt_win *)user_data);
}

static void app_checkbutton_clicked_cb(GtkCheckButton *cb, gpointer user_data)
{
    mark_appointment_changed((appt_win *)user_data);
}

static void refresh_dependent_data(appt_win *apptw)
{
    if (apptw->el != NULL)
        refresh_el_win((el_win *)apptw->el);
    if (apptw->dw != NULL)
        refresh_day_win((day_win *)apptw->dw);
    orage_mark_appointments();
}

static void on_appNote_buffer_changed_cb(GtkTextBuffer *b, gpointer user_data)
{
    appt_win *apptw = (appt_win *)user_data;
    GtkTextIter start, end, match_start, match_end;
    GtkTextBuffer *tb;
    gchar *cdate, ctime[6], *cdatetime;
    struct tm *tm;

    tb = apptw->Note_buffer;
    gtk_text_buffer_get_bounds(tb, &start, &end);
    if (gtk_text_iter_forward_search(&start, "<D>"
                , GTK_TEXT_SEARCH_TEXT_ONLY
                , &match_start, &match_end, &end)) { /* found it */
        cdate = orage_localdate_i18();
        gtk_text_buffer_delete(tb, &match_start, &match_end);
        gtk_text_buffer_insert(tb, &match_start, cdate, -1);
    }
    else if (gtk_text_iter_forward_search(&start, "<T>"
                , GTK_TEXT_SEARCH_TEXT_ONLY
                , &match_start, &match_end, &end)) { /* found it */
        tm = orage_localtime();
        g_sprintf(ctime, "%02d:%02d", tm->tm_hour, tm->tm_min);
        gtk_text_buffer_delete(tb, &match_start, &match_end);
        gtk_text_buffer_insert(tb, &match_start, ctime, -1);
    }
    else if (gtk_text_iter_forward_search(&start, "<DT>"
                , GTK_TEXT_SEARCH_TEXT_ONLY
                , &match_start, &match_end, &end)) { /* found it */
        tm = orage_localtime();
        cdate = orage_tm_date_to_i18_date(tm);
        g_sprintf(ctime, "%02d:%02d", tm->tm_hour, tm->tm_min);
        cdatetime = g_strconcat(cdate, " ", ctime, NULL);
        gtk_text_buffer_delete(tb, &match_start, &match_end);
        gtk_text_buffer_insert(tb, &match_start, cdatetime, -1);
        g_free(cdatetime);
    }
   
    mark_appointment_changed((appt_win *)user_data);
}

static void on_app_entry_changed_cb(GtkEditable *entry, gpointer user_data)
{
    mark_appointment_changed((appt_win *)user_data);
}

static void on_freq_combobox_changed_cb(GtkComboBox *cb, gpointer user_data)
{
    set_repeat_sensitivity((appt_win *)user_data);
    mark_appointment_changed((appt_win *)user_data);
}

static void on_app_combobox_changed_cb(GtkComboBox *cb, gpointer user_data)
{
    mark_appointment_changed((appt_win *)user_data);
}

static void on_app_spin_button_changed_cb(GtkSpinButton *sb, gpointer user_data)
{
    mark_appointment_changed((appt_win *)user_data);
}

static void on_appSound_button_clicked_cb(GtkButton *button, gpointer user_data)
{
    appt_win *apptw = (appt_win *)user_data;
    GtkWidget *file_chooser;
    GtkFileFilter *filter;
    gchar *appSound_entry_filename;
    gchar *sound_file;
    const gchar *filetype[FILETYPE_SIZE] = {
        "*.aiff", "*.al", "*.alsa", "*.au", "*.auto", "*.avr",
        "*.cdr", "*.cvs", "*.dat", "*.vms", "*.gsm", "*.hcom", 
        "*.la", "*.lu", "*.maud", "*.mp3", "*.nul", "*.ogg", "*.ossdsp",
        "*.prc", "*.raw", "*.sb", "*.sf", "*.sl", "*.smp", "*.sndt", 
        "*.sph", "*.8svx", "*.sw", "*.txw", "*.ub", "*.ul", "*.uw",
        "*.voc", "*.vorbis", "*.vox", "*.wav", "*.wve"};
    register int i;

    appSound_entry_filename = g_strdup(gtk_entry_get_text(
            (GtkEntry *)apptw->Sound_entry));
    file_chooser = gtk_file_chooser_dialog_new(_("Select a file..."),
            GTK_WINDOW(apptw->Window),
            GTK_FILE_CHOOSER_ACTION_OPEN,
            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
            GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
            NULL);

    filter = gtk_file_filter_new();
    gtk_file_filter_set_name(filter, _("Sound Files"));
    for (i = 0; i < FILETYPE_SIZE; i++) {
        gtk_file_filter_add_pattern(filter, filetype[i]);
    }
    gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), filter);

    filter = gtk_file_filter_new();
    gtk_file_filter_set_name(filter, _("All Files"));
    gtk_file_filter_add_pattern(filter, "*");
    gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), filter);

    gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(file_chooser)
            , PACKAGE_DATA_DIR "/orage/sounds", NULL);

    if (strlen(appSound_entry_filename) > 0)
        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(file_chooser)
                , (const gchar *) appSound_entry_filename);
    else
        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_chooser)
            , PACKAGE_DATA_DIR "/orage/sounds");

    if (gtk_dialog_run(GTK_DIALOG(file_chooser)) == GTK_RESPONSE_ACCEPT) {
        sound_file = 
            gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser));
        if (sound_file) {
            gtk_entry_set_text(GTK_ENTRY(apptw->Sound_entry), sound_file);
            gtk_editable_set_position(GTK_EDITABLE(apptw->Sound_entry), -1);
        }
    }

    gtk_widget_destroy(file_chooser);
    g_free(appSound_entry_filename);
}

static void app_free_memory(appt_win *apptw)
{
    /* remove myself from event list appointment monitoring list */
    if (apptw->el)
        ((el_win *)apptw->el)->apptw_list = 
                g_list_remove(((el_win *)apptw->el)->apptw_list, apptw);
    /* remove myself from day list appointment monitoring list */
    else if (apptw->dw)
        ((day_win *)apptw->dw)->apptw_list = 
                g_list_remove(((day_win *)apptw->dw)->apptw_list, apptw);
    gtk_widget_destroy(apptw->Window);
    gtk_object_destroy(GTK_OBJECT(apptw->Tooltips));
    g_free(apptw->xf_uid);
    g_free(apptw->par);
    xfical_appt_free((xfical_appt *)apptw->xf_appt);
    g_free(apptw);
}

static gboolean appWindow_check_and_close(appt_win *apptw)
{
    gint result;

    if (apptw->appointment_changed == TRUE) {
        result = xfce_message_dialog(GTK_WINDOW(apptw->Window)
                , _("Warning")
                , GTK_STOCK_DIALOG_WARNING
                , _("The appointment information has been modified.")
                , _("Do you want to continue?")
                , GTK_STOCK_NO, GTK_RESPONSE_CANCEL
                , GTK_STOCK_YES, GTK_RESPONSE_ACCEPT
                , NULL);

        if (result == GTK_RESPONSE_ACCEPT) {
            app_free_memory(apptw);
        }
    }
    else {
        app_free_memory(apptw);
    }
    return(TRUE);
}

static gboolean on_appWindow_delete_event_cb(GtkWidget *widget, GdkEvent *event
    , gpointer user_data)
{
    return(appWindow_check_and_close((appt_win *)user_data));
}

static gboolean orage_validate_datetime(appt_win *apptw, xfical_appt *appt)
{
    gint result;

    /* Journal does not have end time so no need to check */
    if (appt->type == XFICAL_TYPE_JOURNAL)
        return(TRUE);
    if (xfical_compare_times(appt) > 0) {
        result = xfce_message_dialog(GTK_WINDOW(apptw->Window)
                , _("Error")
                , GTK_STOCK_DIALOG_ERROR
                , _("The end of this appointment is earlier than the beginning.")
                , NULL
                , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
                , NULL);
        return(FALSE);
    }
    else {
        return(TRUE);
    }
}

static gboolean fill_appt_from_apptw_alarm(xfical_appt *appt, appt_win *apptw)
{
    gint i, j, k;
    gchar *tmp;

    /* reminder time */
    appt->alarmtime = gtk_spin_button_get_value_as_int(
            GTK_SPIN_BUTTON(apptw->Alarm_spin_dd)) * 24*60*60
                    + gtk_spin_button_get_value_as_int(
            GTK_SPIN_BUTTON(apptw->Alarm_spin_hh)) *    60*60
                    + gtk_spin_button_get_value_as_int(
            GTK_SPIN_BUTTON(apptw->Alarm_spin_mm)) *       60
                    ;
    appt->display_alarm_orage = appt->alarmtime ? TRUE : FALSE;

    /* reminder before/after related to start/end */
    /*
    char *when_array[4] = {_("Before Start"), _("Before End")
        , _("After Start"), _("After End")};
        */
    switch (gtk_combo_box_get_active(GTK_COMBO_BOX(apptw->Alarm_when_cb))) {
        case 0:
            appt->alarm_before = TRUE;
            appt->alarm_related_start = TRUE;
            break;
        case 1:
            appt->alarm_before = TRUE;
            appt->alarm_related_start = FALSE;
            break;
        case 2:
            appt->alarm_before = FALSE;
            appt->alarm_related_start = TRUE;
            break;
        case 3:
            appt->alarm_before = FALSE;
            appt->alarm_related_start = FALSE;
            break;
        default:
            appt->alarm_before = TRUE;
            appt->alarm_related_start = TRUE;
            break;
    }

    /* Do we use persistent alarm */
    appt->alarm_persistent = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Per_checkbutton));

    /* Do we use audio alarm */
    appt->sound_alarm = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Sound_checkbutton));

    /* Which sound file will be played */
    if (appt->sound) {
        g_free(appt->sound);
        appt->sound = NULL;
    }
    appt->sound = g_strdup(gtk_entry_get_text(GTK_ENTRY(apptw->Sound_entry)));

    /* the alarm will repeat until someone shuts it off 
     * or it has been played soundrepeat_cnt times */
    appt->soundrepeat = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->SoundRepeat_checkbutton));
    appt->soundrepeat_cnt = gtk_spin_button_get_value_as_int(
            GTK_SPIN_BUTTON(apptw->SoundRepeat_spin_cnt));
    appt->soundrepeat_len = gtk_spin_button_get_value_as_int(
            GTK_SPIN_BUTTON(apptw->SoundRepeat_spin_len));

    /* Do we use orage display alarm */
    appt->display_alarm_orage = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_orage));

    /* Do we use notify display alarm */
#ifdef HAVE_NOTIFY
    appt->display_alarm_notify = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_notify));
#else
    appt->display_alarm_notify = FALSE;
#endif

    /* notify display alarm timeout */
#ifdef HAVE_NOTIFY
    if (gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_expire_notify)))
        appt->display_notify_timeout = gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(apptw->Display_spin_expire_notify));
    else 
        appt->display_notify_timeout = -1;
#else
    appt->display_notify_timeout = -1;
#endif

    /* Do we use procedure alarm */
    appt->procedure_alarm = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Proc_checkbutton));

    /* The actual command. 
     * Note that we need to split this into cmd and the parameters 
     * since that is how libical stores it */
    if (appt->procedure_cmd) {
        g_free(appt->procedure_cmd);
        appt->procedure_cmd = NULL;
    }
    if (appt->procedure_params) {
        g_free(appt->procedure_params);
        appt->procedure_params = NULL;
    }
    tmp = (char *)gtk_entry_get_text(GTK_ENTRY(apptw->Proc_entry));
    j = strlen(tmp);
    for (i = 0; i < j && g_ascii_isspace(tmp[i]); i++)
        ; /* skip blanks */
    for (k = i; k < j && !g_ascii_isspace(tmp[k]); k++)
        ; /* find first blank after the cmd */
        /* now i points to start of cmd and k points to end of cmd */
    if (k-i)
        appt->procedure_cmd = g_strndup(tmp+i, k-i);
    if (j-k)
        appt->procedure_params = g_strndup(tmp+k, j-k);
}

/*
 * Function fills an appointment with the contents of an appointment 
 * window
 */
static gboolean fill_appt_from_apptw(xfical_appt *appt, appt_win *apptw)
{
    GtkTextIter start, end;
    const char *time_format="%H:%M";
    struct tm current_t;
    gchar starttime[6], endtime[6], completedtime[6];
    gint i;
    gchar *tmp, *tmp2;

/* Next line is fix for bug 2811.
 * We need to make sure spin buttons do not have values which are not
 * yet updated = visible.
 * gtk_spin_button_update call would do it, but it seems to cause
 * crash if done in on_app_spin_button_changed_cb and here we should
 * either do it for all spin fields, which takes too many lines of code.
 * if (GTK_WIDGET_HAS_FOCUS(apptw->StartTime_spin_hh))
 *      gtk_spin_button_update;
 * So we just change the focus field and then spin button value gets set.
 * It would be nice to not to have to change the field though.
 */
    gtk_widget_grab_focus(apptw->Title_entry);

            /*********** GENERAL TAB ***********/
    /* type */
    if (gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Type_event_rb)))
        appt->type = XFICAL_TYPE_EVENT;
    else if (gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Type_todo_rb)))
        appt->type = XFICAL_TYPE_TODO;
    else if (gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Type_journal_rb)))
        appt->type = XFICAL_TYPE_JOURNAL;
    else
        g_warning("fill_appt_from_apptw: coding error, illegal type");

    /* title */
    appt->title = g_strdup(gtk_entry_get_text(GTK_ENTRY(apptw->Title_entry)));

    /* location */
    appt->location = g_strdup(gtk_entry_get_text(
            GTK_ENTRY(apptw->Location_entry)));

    /* all day */
    appt->allDay = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->AllDay_checkbutton));

    /* start date and time. 
     * Note that timezone is kept upto date all the time 
     */
    current_t = orage_i18_date_to_tm_date(gtk_button_get_label(
            GTK_BUTTON(apptw->StartDate_button)));
    g_sprintf(starttime, "%02d:%02d"
            , gtk_spin_button_get_value_as_int(
                    GTK_SPIN_BUTTON(apptw->StartTime_spin_hh))
            , gtk_spin_button_get_value_as_int(
                    GTK_SPIN_BUTTON(apptw->StartTime_spin_mm)));
    strptime(starttime, time_format, &current_t);
    g_sprintf(appt->starttime, XFICAL_APPT_TIME_FORMAT
            , current_t.tm_year + 1900, current_t.tm_mon + 1, current_t.tm_mday
            , current_t.tm_hour, current_t.tm_min, 0);

    /* end date and time. 
     * Note that timezone is kept upto date all the time 
     */ 
    appt->use_due_time = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->End_checkbutton));
    current_t = orage_i18_date_to_tm_date(gtk_button_get_label(
            GTK_BUTTON(apptw->EndDate_button)));
    g_sprintf(endtime, "%02d:%02d"
            , gtk_spin_button_get_value_as_int(
                    GTK_SPIN_BUTTON(apptw->EndTime_spin_hh))
            , gtk_spin_button_get_value_as_int(
                    GTK_SPIN_BUTTON(apptw->EndTime_spin_mm)));
    strptime(endtime, time_format, &current_t);
    g_sprintf(appt->endtime, XFICAL_APPT_TIME_FORMAT
            , current_t.tm_year + 1900, current_t.tm_mon + 1, current_t.tm_mday
            , current_t.tm_hour, current_t.tm_min, 0);

    /* duration */
    appt->use_duration = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Dur_checkbutton));
    if (appt->allDay)
        appt->duration = gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(apptw->Dur_spin_dd)) * 24*60*60;
    else
        appt->duration = gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(apptw->Dur_spin_dd)) * 24*60*60
                        + gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(apptw->Dur_spin_hh)) *    60*60
                        + gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(apptw->Dur_spin_mm)) *       60;

    /* Check that end time is after start time. */
    if (!orage_validate_datetime(apptw, appt))
        return(FALSE);

    /* completed date and time. 
     * Note that timezone is kept upto date all the time 
     */ 
    appt->completed = gtk_toggle_button_get_active(
            GTK_TOGGLE_BUTTON(apptw->Completed_checkbutton));
    current_t = orage_i18_date_to_tm_date(gtk_button_get_label(
            GTK_BUTTON(apptw->CompletedDate_button)));
    g_sprintf(completedtime, "%02d:%02d"
            , gtk_spin_button_get_value_as_int(
                    GTK_SPIN_BUTTON(apptw->CompletedTime_spin_hh))
            , gtk_spin_button_get_value_as_int(
                    GTK_SPIN_BUTTON(apptw->CompletedTime_spin_mm)));
    strptime(completedtime, time_format, &current_t);
    g_sprintf(appt->completedtime, XFICAL_APPT_TIME_FORMAT
            , current_t.tm_year + 1900, current_t.tm_mon + 1, current_t.tm_mday
            , current_t.tm_hour, current_t.tm_min, 0);

    /* availability */
    appt->availability = gtk_combo_box_get_active(
            GTK_COMBO_BOX(apptw->Availability_cb));

    /* categories */
    tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(apptw->Categories_entry)));
    tmp2 = gtk_combo_box_get_active_text(GTK_COMBO_BOX(apptw->Categories_cb));
    if (!strcmp(tmp2, _("Not set"))) {
        g_free(tmp2);
        tmp2 = NULL;
    }
    if (ORAGE_STR_EXISTS(tmp)) {
        appt->categories = g_strjoin(",", tmp, tmp2, NULL);
        g_free(tmp);
        g_free(tmp2);
    }
    else
        appt->categories = tmp2;

    /* notes */
    gtk_text_buffer_get_bounds(apptw->Note_buffer, &start, &end);
    appt->note = gtk_text_iter_get_text(&start, &end);

            /*********** ALARM TAB ***********/
    fill_appt_from_apptw_alarm(appt, apptw);

            /*********** RECURRENCE TAB ***********/
    /* recurrence */
    appt->freq = gtk_combo_box_get_active(GTK_COMBO_BOX(apptw->Recur_freq_cb));

    /* recurrence interval */
    appt->interval = gtk_spin_button_get_value_as_int(
            GTK_SPIN_BUTTON(apptw->Recur_int_spin));

    /* recurrence ending */
    if (gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_limit_rb))) {
        appt->recur_limit = 0;    /* no limit */
        appt->recur_count = 0;    /* special: means no repeat count limit */
        appt->recur_until[0] = 0; /* special: means no until time limit */
    }
    else if (gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_count_rb))) {
        appt->recur_limit = 1;    /* count limit */
        appt->recur_count = gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(apptw->Recur_count_spin));
        appt->recur_until[0] = 0; /* special: means no until time limit */
    }
    else if (gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_until_rb))) {
        appt->recur_limit = 2;    /* until limit */
        appt->recur_count = 0;    /* special: means no repeat count limit */

        current_t = orage_i18_date_to_tm_date(gtk_button_get_label(
                GTK_BUTTON(apptw->Recur_until_button)));
        g_sprintf(appt->recur_until, XFICAL_APPT_TIME_FORMAT
                , current_t.tm_year + 1900, current_t.tm_mon + 1
                , current_t.tm_mday, 23, 59, 10);
    }
    else
        g_warning("fill_appt_from_apptw: coding error, illegal recurrence");

    /* recurrence weekdays */
    for (i=0; i <= 6; i++) {
        appt->recur_byday[i] = gtk_toggle_button_get_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_byday_cb[i]));
    /* recurrence weekday selection for month/yearly case */
        appt->recur_byday_cnt[i] = gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(apptw->Recur_byday_spin[i]));
    }

    return(TRUE);
}

static gboolean save_xfical_from_appt_win(appt_win *apptw)
{
    gboolean ok = FALSE;
    xfical_appt *appt = (xfical_appt *)apptw->xf_appt;

    if (fill_appt_from_apptw(appt, apptw)) {
        /* Here we try to save the event... */
        if (!xfical_file_open(TRUE))
            return(FALSE);
        if (apptw->appointment_add) {
            apptw->xf_uid = g_strdup(xfical_appt_add(appt));
            ok = (apptw->xf_uid ? TRUE : FALSE);
            if (ok) {
                apptw->appointment_add = FALSE;
                gtk_widget_set_sensitive(apptw->Duplicate, TRUE);
                gtk_widget_set_sensitive(apptw->File_menu_duplicate, TRUE);
                orage_message(10, "Added: %s", apptw->xf_uid);
            }
            else
                orage_message(150, "Addition failed: %s", apptw->xf_uid);
            }
        else {
            ok = xfical_appt_mod(apptw->xf_uid, appt);
            if (ok)
                orage_message(10, "Modified: %s", apptw->xf_uid);
            else
                orage_message(150, "Modification failed: %s", apptw->xf_uid);
        }
        xfical_file_close(TRUE);
        if (ok) {
            apptw->appointment_new = FALSE;
            mark_appointment_unchanged(apptw);
            refresh_dependent_data(apptw);
        }
    }
    return(ok);
}

static void on_appFileSave_menu_activate_cb(GtkMenuItem *mi, gpointer user_data)
{
    save_xfical_from_appt_win((appt_win *)user_data);
}

static void on_appSave_clicked_cb(GtkButton *b, gpointer user_data)
{
    save_xfical_from_appt_win((appt_win *)user_data);
}

static void save_xfical_from_appt_win_and_close(appt_win *apptw)
{
    if (save_xfical_from_appt_win(apptw)) {
        app_free_memory(apptw);
    }
}

static void on_appFileSaveClose_menu_activate_cb(GtkMenuItem *mi
        , gpointer user_data)
{
    save_xfical_from_appt_win_and_close((appt_win *)user_data);
}

static void on_appSaveClose_clicked_cb(GtkButton *b, gpointer user_data)
{
    save_xfical_from_appt_win_and_close((appt_win *)user_data);
}

static void delete_xfical_from_appt_win(appt_win *apptw)
{
    gint result;

    result = xfce_message_dialog(GTK_WINDOW(apptw->Window)
            , _("Warning")
            , GTK_STOCK_DIALOG_WARNING
            , _("This appointment will be permanently removed.")
            , _("Do you want to continue?")
            , GTK_STOCK_NO, GTK_RESPONSE_REJECT
            , GTK_STOCK_YES, GTK_RESPONSE_ACCEPT
            , NULL);
                                 
    if (result == GTK_RESPONSE_ACCEPT) {
        if (!apptw->appointment_add) {
            if (!xfical_file_open(TRUE))
                    return;
            result = xfical_appt_del(apptw->xf_uid);
            if (result)
                orage_message(10, "Removed: %s", apptw->xf_uid);
            else
                g_warning("Removal failed: %s", apptw->xf_uid);
            xfical_file_close(TRUE);
        }

        refresh_dependent_data(apptw);
        app_free_memory(apptw);
    }
}

static void on_appDelete_clicked_cb(GtkButton *b, gpointer user_data)
{
    delete_xfical_from_appt_win((appt_win *)user_data);
}

static void on_appFileDelete_menu_activate_cb(GtkMenuItem *mi
        , gpointer user_data)
{
    delete_xfical_from_appt_win((appt_win *)user_data);
}

static void on_appFileClose_menu_activate_cb(GtkMenuItem *mi
        , gpointer user_data)
{
    appWindow_check_and_close((appt_win *)user_data);
}

static void duplicate_xfical_from_appt_win(appt_win *apptw)
{
    gint x, y;
    appt_win *apptw2;

    /* do not keep track of appointments created here */
    apptw2 = create_appt_win("COPY", apptw->xf_uid);
    gtk_window_get_position(GTK_WINDOW(apptw->Window), &x, &y);
    gtk_window_move(GTK_WINDOW(apptw2->Window), x+20, y+20);
}

static void on_appFileDuplicate_menu_activate_cb(GtkMenuItem *mi
        , gpointer user_data)
{
    duplicate_xfical_from_appt_win((appt_win *)user_data);
}

static void on_appDuplicate_clicked_cb(GtkButton *b, gpointer user_data)
{
    duplicate_xfical_from_appt_win((appt_win *)user_data);
}

static void revert_xfical_to_last_saved(appt_win *apptw)
{
    if (!apptw->appointment_new) {
        fill_appt_window(apptw, "UPDATE", apptw->xf_uid);
    }
    else {
        fill_appt_window(apptw, "NEW", apptw->par);
    }
}

static void on_appFileRevert_menu_activate_cb(GtkMenuItem *mi
        , gpointer user_data)
{
    revert_xfical_to_last_saved((appt_win *)user_data);
}

static void on_appRevert_clicked_cb(GtkWidget *b, gpointer *user_data)
{
    revert_xfical_to_last_saved((appt_win *)user_data);
}

static void on_Date_button_clicked_cb(GtkWidget *button, gpointer *user_data)
{
    appt_win *apptw = (appt_win *)user_data;

    if (orage_date_button_clicked(button, apptw->Window))
        mark_appointment_changed(apptw);
}

static void on_appStartTimezone_clicked_cb(GtkButton *button
        , gpointer *user_data)
{
    appt_win *apptw = (appt_win *)user_data;
    xfical_appt *appt;

    appt = (xfical_appt *)apptw->xf_appt;
    if (xfical_timezone_button_clicked(button, GTK_WINDOW(apptw->Window)
            , &appt->start_tz_loc))
        mark_appointment_changed(apptw);
}

static void on_appEndTimezone_clicked_cb(GtkButton *button
        , gpointer *user_data)
{
    appt_win *apptw = (appt_win *)user_data;
    xfical_appt *appt;

    appt = (xfical_appt *)apptw->xf_appt;
    if (xfical_timezone_button_clicked(button, GTK_WINDOW(apptw->Window)
            , &appt->end_tz_loc))
        mark_appointment_changed(apptw);
}

static void on_appCompletedTimezone_clicked_cb(GtkButton *button
        , gpointer *user_data)
{
    appt_win *apptw = (appt_win *)user_data;
    xfical_appt *appt;

    appt = (xfical_appt *)apptw->xf_appt;
    if (xfical_timezone_button_clicked(button, GTK_WINDOW(apptw->Window)
            , &appt->completed_tz_loc))
        mark_appointment_changed(apptw);
}

static void fill_appt_window_times(appt_win *apptw, xfical_appt *appt)
{
    char *startdate_to_display, *enddate_to_display, *completeddate_to_display;
    int days, hours, minutes;
    struct tm tm_date;

    /* all day ? */
    gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->AllDay_checkbutton), appt->allDay);

    /* start time */
    if (strlen(appt->starttime) > 6 ) {
        tm_date = orage_icaltime_to_tm_time(appt->starttime, TRUE);
        startdate_to_display = orage_tm_date_to_i18_date(&tm_date);
        gtk_button_set_label(GTK_BUTTON(apptw->StartDate_button)
                , (const gchar *)startdate_to_display);
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(apptw->StartTime_spin_hh)
                        , (gdouble)tm_date.tm_hour);
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(apptw->StartTime_spin_mm)
                        , (gdouble)tm_date.tm_min);
        if (appt->start_tz_loc)
            gtk_button_set_label(GTK_BUTTON(apptw->StartTimezone_button)
                    , _(appt->start_tz_loc));
        else /* we should never get here */
            g_warning("fill_appt_window_times: start_tz_loc is null");
    }
    else
        g_warning("fill_appt_window_times: starttime wrong %s", appt->uid);

    /* end time */
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
            apptw->End_checkbutton), appt->use_due_time);
    if (strlen(appt->endtime) > 6 ) {
        tm_date = orage_icaltime_to_tm_time(appt->endtime, TRUE);
        enddate_to_display = orage_tm_date_to_i18_date(&tm_date);
        gtk_button_set_label(GTK_BUTTON(apptw->EndDate_button)
                , (const gchar *)enddate_to_display);
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(apptw->EndTime_spin_hh)
                        , (gdouble)tm_date.tm_hour);
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(apptw->EndTime_spin_mm)
                        , (gdouble)tm_date.tm_min);
        if (appt->end_tz_loc) {
            gtk_button_set_label(GTK_BUTTON(apptw->EndTimezone_button)
                    , _(appt->end_tz_loc));
        }
        else /* we should never get here */
            g_warning("fill_appt_window_times: end_tz_loc is null");
    }
    else
        g_warning("fill_appt_window_times: endtime wrong %s", appt->uid);

    /* duration */
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(apptw->Dur_checkbutton)
            , appt->use_duration);
    days = appt->duration/(24*60*60);
    hours = (appt->duration-days*(24*60*60))/(60*60);
    minutes = (appt->duration-days*(24*60*60)-hours*(60*60))/(60);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->Dur_spin_dd)
            , (gdouble)days);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->Dur_spin_hh)
            , (gdouble)hours);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->Dur_spin_mm)
            , (gdouble)minutes);

    /* completed time (only available for todo) */
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
            apptw->Completed_checkbutton), appt->completed);
    if (strlen(appt->completedtime) > 6 ) {
        tm_date = orage_icaltime_to_tm_time(appt->completedtime, TRUE);
        completeddate_to_display = orage_tm_date_to_i18_date(&tm_date);
        gtk_button_set_label(GTK_BUTTON(apptw->CompletedDate_button)
                , (const gchar *)completeddate_to_display);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->CompletedTime_spin_hh)
                , (gdouble)tm_date.tm_hour);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->CompletedTime_spin_mm)
                , (gdouble)tm_date.tm_min);
        if (appt->completed_tz_loc) {
            gtk_button_set_label(GTK_BUTTON(apptw->CompletedTimezone_button)
                    , _(appt->completed_tz_loc));
        }
        else /* we should never get here */
            g_warning("fill_appt_window_times: completed_tz_loc is null");
    }
    else
        g_warning("fill_appt_window_times: completedtime wrong %s", appt->uid);
}

static xfical_appt *fill_appt_window_get_appt(appt_win *apptw
    , char *action, char *par)
{
    xfical_appt *appt=NULL;
    struct tm *t;
    gchar today[9];

    if (strcmp(action, "NEW") == 0) {
/* par contains XFICAL_APPT_DATE_FORMAT (yyyymmdd) date for NEW appointment */
        appt = xfical_appt_alloc();
        t = orage_localtime();
        g_sprintf(today, "%04d%02d%02d", t->tm_year+1900, t->tm_mon+1
                , t->tm_mday);
        /* If we're today, we propose an appointment the next half-hour */
        /* hour 24 is wrong, we use 00 */
        if (strcmp(par, today) == 0 && t->tm_hour < 23) { 
            if (t->tm_min <= 30) {
                g_sprintf(appt->starttime,"%sT%02d%02d00"
                        , par, t->tm_hour, 30);
                g_sprintf(appt->endtime,"%sT%02d%02d00"
                        , par, t->tm_hour + 1, 00);
            }
            else {
                g_sprintf(appt->starttime,"%sT%02d%02d00"
                        , par, t->tm_hour + 1, 00);
                g_sprintf(appt->endtime,"%sT%02d%02d00"
                        , par, t->tm_hour + 1, 30);
            }
        }
        /* otherwise we suggest it at 09:00 in the morning. */
        else {
            g_sprintf(appt->starttime,"%sT090000", par);
            g_sprintf(appt->endtime,"%sT093000", par);
        }
        if (g_par.local_timezone_utc)
            appt->start_tz_loc = g_strdup("UTC");
        else if (g_par.local_timezone) 
            appt->start_tz_loc = g_strdup(g_par.local_timezone);
        else
            appt->start_tz_loc = g_strdup("floating");
        appt->end_tz_loc = g_strdup(appt->start_tz_loc);
        appt->duration = 30*60;
        /* use duration by default for new appointments */
        appt->use_duration = TRUE;
        g_sprintf(appt->completedtime,"%sT%02d%02d00"
                    , today, t->tm_hour, t->tm_min);
        appt->completed_tz_loc = g_strdup(appt->start_tz_loc);

        read_default_alarm(appt);
    }
    else if ((strcmp(action, "UPDATE") == 0) || (strcmp(action, "COPY") == 0)) {
        /* par contains ical uid */
        if (!xfical_file_open(TRUE))
            return(NULL);
        if ((appt = xfical_appt_get(par)) == NULL) {
            xfce_message_dialog(GTK_WINDOW(apptw->Window)
                , _("Info")
                , GTK_STOCK_DIALOG_INFO
                , _("This appointment does not exist.")
                , _("It was probably removed, please refresh your screen.")
                , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
                , NULL);
        }
        xfical_file_close(TRUE);
    }
    else {
        g_error("unknown parameter\n");
    }

    return(appt);
}

/************************************************************/
/* categories start.                                        */
/************************************************************/

typedef struct _orage_category
{
    gchar *category;
    GdkColor color;
} orage_category_struct;

GList *orage_category_list = NULL;

static OrageRc *orage_category_file_open(gboolean read_only)
{
    gchar *fpath;
    OrageRc *orc;

    fpath = orage_data_file_location(ORAGE_CATEGORIES_FILE);
    if ((orc = (OrageRc *)orage_rc_file_open(fpath, read_only)) == NULL) {
        orage_message(150, "orage_category_file_open: category file open failed.");
    }
    g_free(fpath);

    return(orc);
}

GdkColor *orage_category_list_contains(char *categories)
{
    GList *cat_l;
    orage_category_struct *cat;
    
    if (categories == NULL)
        return(NULL);
    cat_l = orage_category_list;
    for (cat_l = g_list_first(cat_l);
         cat_l != NULL;
         cat_l = g_list_next(cat_l)) {
        cat = (orage_category_struct *)cat_l->data;
        if (g_str_has_suffix(categories, cat->category)) {
            return(&cat->color);
        }
    }
    /* not found */
    return(NULL);
}

static void orage_category_free(gpointer gcat, gpointer dummy)
{
    orage_category_struct *cat = (orage_category_struct *)gcat;

    g_free(cat->category);
    g_free(cat);
}

void orage_category_free_list()
{
    g_list_foreach(orage_category_list, orage_category_free, NULL);
    g_list_free(orage_category_list);
    orage_category_list = NULL;
}

void orage_category_get_list()
{
    OrageRc *orc;
    gchar **cat_groups, *color;
    gint i;
    orage_category_struct *cat;
    GdkColormap *pic1_cmap;

    if (orage_category_list != NULL)
        orage_category_free_list();
    pic1_cmap = gdk_colormap_get_system();
    orc = orage_category_file_open(TRUE);
    cat_groups = orage_rc_get_groups(orc);
    for (i=1; cat_groups[i] != NULL; i++) {
        orage_rc_set_group(orc, cat_groups[i]);
        color = orage_rc_get_str(orc, "Color", NULL);
        if (color) {
            cat = g_new(orage_category_struct, 1);
            cat->category = g_strdup(cat_groups[i]);
            sscanf(color, ORAGE_COLOR_FORMAT, &(cat->color.red)
                    , &(cat->color.green), &(cat->color.blue));
            gdk_colormap_alloc_color(pic1_cmap, &cat->color, FALSE, TRUE);
            orage_category_list = g_list_prepend(orage_category_list, cat);
            g_free(color);
        }
    }
    g_strfreev(cat_groups);
    orage_rc_file_close(orc);
}

gboolean category_fill_cb(GtkComboBox *cb, char *select)
{
    OrageRc *orc;
    gchar **cat_gourps;
    gint i;
    gboolean found=FALSE;

    orc = orage_category_file_open(TRUE);
    cat_gourps = orage_rc_get_groups(orc);
    /* cat_groups[0] is special [NULL] entry always */
    gtk_combo_box_append_text(cb, _("Not set"));
    gtk_combo_box_set_active(cb, 0);
    for (i=1; cat_gourps[i] != NULL; i++) {
        gtk_combo_box_append_text(cb, (const gchar *)cat_gourps[i]);
        if (!found && select && !strcmp(select, cat_gourps[i])) {
            gtk_combo_box_set_active(cb, i);
            found = TRUE;
        }
    }
    g_strfreev(cat_gourps);
    orage_rc_file_close(orc);
    return(found);
}

void orage_category_refill_cb(appt_win *apptw)
{
    gchar *tmp;

    /* first remember the currently selected value */
    tmp = gtk_combo_box_get_active_text(GTK_COMBO_BOX(apptw->Categories_cb));

    /* then clear the values by removing the widget and putting it back */
    gtk_widget_destroy(apptw->Categories_cb);
    apptw->Categories_cb = gtk_combo_box_new_text();
    gtk_container_add(GTK_CONTAINER(apptw->Categories_cb_event)
            , apptw->Categories_cb);

    /* and finally fill it with new values */
    category_fill_cb(GTK_COMBO_BOX(apptw->Categories_cb), tmp);

    g_free(tmp);
    gtk_widget_show(apptw->Categories_cb);
}

void fill_category_data(appt_win *apptw, xfical_appt *appt)
{
    gchar *tmp = NULL;

    /* first search the last entry. which is the special color value */
    if (appt->categories) {
        tmp = g_strrstr(appt->categories, ",");
        if (!tmp) /* , not found, let's take the whole string */
            tmp = appt->categories;
        while (*(tmp) == ' ' || *(tmp) == ',') /* skip blanks and , */
            tmp++; 
    }
    if (category_fill_cb(GTK_COMBO_BOX(apptw->Categories_cb), tmp)) {
        /* we found match. Let's try to hide that from the entry text */
        while (tmp != appt->categories 
                && (*(tmp-1) == ' ' || *(tmp-1) == ','))
            tmp--;
        *tmp = '\0'; /* note that this goes to appt->categories */
    }
    gtk_entry_set_text(GTK_ENTRY(apptw->Categories_entry)
            , (appt->categories ? appt->categories : ""));
}

void orage_category_write_entry(gchar *category, GdkColor *color)
{
    OrageRc *orc;
    char *color_str;

    if (!ORAGE_STR_EXISTS(category)) {
        orage_message(50, "orage_category_write_entry: empty category. Not written");
        return;
    }
    color_str = g_strdup_printf(ORAGE_COLOR_FORMAT
            , color->red, color->green, color->blue);
    orc = orage_category_file_open(FALSE);
    orage_rc_set_group(orc, category);
    orage_rc_put_str(orc, "Color", color_str);
    g_free(color_str);
    orage_rc_file_close(orc);
}

static void orage_category_remove_entry(gchar *category)
{
    OrageRc *orc;

    if (!ORAGE_STR_EXISTS(category)) {
        orage_message(50, "orage_category_remove_entry: empty category. Not removed");
        return;
    }
    orc = orage_category_file_open(FALSE);
    orage_rc_del_group(orc, category);
    orage_rc_file_close(orc);
}

static void close_cat_window(gpointer user_data)
{
    category_win_struct *catw = (category_win_struct *)user_data;

    orage_category_refill_cb((appt_win *)catw->apptw);
    gtk_widget_destroy(catw->window);
    gtk_object_destroy(GTK_OBJECT(catw->tooltips));
    g_free(catw);
}

static gboolean on_cat_window_delete_event(GtkWidget *w, GdkEvent *e
        , gpointer user_data)
{
    close_cat_window(user_data);
    return(FALSE);
}

static void cat_add_button_clicked(GtkButton *button, gpointer user_data)
{
    category_win_struct *catw = (category_win_struct *)user_data;
    gchar *entry_category;
    GdkColor color;

    entry_category = g_strdup(gtk_entry_get_text((GtkEntry *)catw->new_entry));
    g_strstrip(entry_category);
    gtk_color_button_get_color((GtkColorButton *)catw->new_color_button
            , &color);
    orage_category_write_entry(entry_category, &color);
    g_free(entry_category);
    refresh_categories(catw);
}

static void cat_color_button_changed(GtkColorButton *color_button
        , gpointer user_data)
{
    category_win_struct *catw = (category_win_struct *)user_data;
    gchar *category;
    GdkColor color;

    category = g_object_get_data(G_OBJECT(color_button), "CATEGORY");
    gtk_color_button_get_color(color_button, &color);
    orage_category_write_entry(category, &color);
}

static void cat_del_button_clicked(GtkButton *button, gpointer user_data)
{
    category_win_struct *catw = (category_win_struct *)user_data;
    gchar *category;

    category = g_object_get_data(G_OBJECT(button), "CATEGORY");
    orage_category_remove_entry(category);
    refresh_categories(catw);
}

static void show_category(gpointer elem, gpointer user_data)
{
    orage_category_struct *cat = (orage_category_struct *)elem;
    category_win_struct *catw = (category_win_struct *)user_data;
    GtkWidget *label, *hbox, *button, *color_button;

    hbox = gtk_hbox_new(FALSE, 0);
    label = gtk_label_new(cat->category);
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 5);

    color_button = gtk_color_button_new_with_color(&cat->color);
    gtk_box_pack_start(GTK_BOX(hbox), color_button, FALSE, FALSE, 5);
    g_object_set_data_full(G_OBJECT(color_button), "CATEGORY"
            , g_strdup(cat->category), g_free);
    g_signal_connect((gpointer)color_button, "color-set"
            , G_CALLBACK(cat_color_button_changed), catw);

    button = gtk_button_new_from_stock("gtk-remove");
    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
    g_object_set_data_full(G_OBJECT(button), "CATEGORY"
            , g_strdup(cat->category), g_free);
    g_signal_connect((gpointer)button, "clicked"
            , G_CALLBACK(cat_del_button_clicked), catw);

    gtk_box_pack_start(GTK_BOX(catw->cur_frame_vbox), hbox, FALSE, FALSE, 5);
}

static void refresh_categories(category_win_struct *catw)
{
    GtkWidget *swin;

    gtk_widget_destroy(catw->cur_frame);

    catw->cur_frame_vbox = gtk_vbox_new(FALSE, 0);
    swin = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin)
            , GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swin)
            , catw->cur_frame_vbox);
    catw->cur_frame = xfce_create_framebox_with_content(
            _("Current categories"), swin);
    gtk_box_pack_start(GTK_BOX(catw->vbox), catw->cur_frame, TRUE, TRUE, 5);

    orage_category_get_list();
    g_list_foreach(orage_category_list, show_category, catw);
    gtk_widget_show_all(catw->cur_frame);
}

static void create_cat_win(category_win_struct *catw)
{
    GtkWidget *label, *hbox, *vbox;

    /***** New category *****/
    vbox = gtk_vbox_new(FALSE, 0);
    catw->new_frame = xfce_create_framebox_with_content(
            _("Add new category with color"), vbox);
    gtk_box_pack_start(GTK_BOX(catw->vbox), catw->new_frame, FALSE, FALSE, 5);

    hbox = gtk_hbox_new(FALSE, 0);
    label = gtk_label_new(_("Category:"));
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
    catw->new_entry = gtk_entry_new();
    gtk_box_pack_start(GTK_BOX(hbox), catw->new_entry, TRUE, TRUE, 0);
    catw->new_color_button = gtk_color_button_new();
    gtk_box_pack_start(GTK_BOX(hbox), catw->new_color_button, FALSE, FALSE, 5);
    catw->new_add_button = gtk_button_new_from_stock("gtk-add");
    gtk_box_pack_start(GTK_BOX(hbox), catw->new_add_button, FALSE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
    g_signal_connect((gpointer)catw->new_add_button, "clicked"
            , G_CALLBACK(cat_add_button_clicked), catw);

    /***** Current categories *****/
    /* refresh_categories always destroys frame first, so let's create
     * a dummy for it for the first time */
    vbox = gtk_vbox_new(FALSE, 0);
    catw->cur_frame = xfce_create_framebox_with_content(("dummy"), vbox);
    refresh_categories(catw);
}

static void on_categories_button_clicked_cb(GtkWidget *button
        , gpointer *user_data)
{
    appt_win *apptw = (appt_win *)user_data;
    category_win_struct *catw;

    catw = g_new(category_win_struct, 1);
    catw->apptw = (gpointer)apptw; /* remember the caller */
    catw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_modal(GTK_WINDOW(catw->window), TRUE);
    gtk_window_set_title(GTK_WINDOW(catw->window)
            , _("Colors of categories - Orage"));
    gtk_window_set_default_size(GTK_WINDOW(catw->window), 390, 360);

    catw->tooltips = gtk_tooltips_new();
    catw->accelgroup = gtk_accel_group_new();
    gtk_window_add_accel_group(GTK_WINDOW(catw->window), catw->accelgroup);

    catw->vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(catw->window), catw->vbox);

    create_cat_win(catw);

    g_signal_connect((gpointer)catw->window, "delete_event",
        G_CALLBACK(on_cat_window_delete_event), catw);
    gtk_widget_show_all(catw->window);
}

/**********************************************************/
/* categories end.                                        */
/**********************************************************/

static void fill_appt_window_alarm(xfical_appt *appt, appt_win *apptw)
{
    int day, hours, minutes;
    char *tmp;

    /* alarmtime (comes in seconds) */
    day = appt->alarmtime/(24*60*60);
    hours = (appt->alarmtime-day*(24*60*60))/(60*60);
    minutes = (appt->alarmtime-day*(24*60*60)-hours*(60*60))/(60);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->Alarm_spin_dd)
                , (gdouble)day);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->Alarm_spin_hh)
                , (gdouble)hours);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->Alarm_spin_mm)
                , (gdouble)minutes);

    /* alarmrelation */
    /*
    char *when_array[4] = {
    _("Before Start"), _("Before End"), _("After Start"), _("After End")};
    */
    if (appt->alarm_before)
        if (appt->alarm_related_start)
            gtk_combo_box_set_active(GTK_COMBO_BOX(apptw->Alarm_when_cb), 0);
        else
            gtk_combo_box_set_active(GTK_COMBO_BOX(apptw->Alarm_when_cb), 1);
    else
        if (appt->alarm_related_start)
            gtk_combo_box_set_active(GTK_COMBO_BOX(apptw->Alarm_when_cb), 2);
        else
            gtk_combo_box_set_active(GTK_COMBO_BOX(apptw->Alarm_when_cb), 3);

    /* persistent */
    gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->Per_checkbutton), appt->alarm_persistent);

    /* sound */
    gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->Sound_checkbutton), appt->sound_alarm);
    gtk_entry_set_text(GTK_ENTRY(apptw->Sound_entry)
            , (appt->sound ? appt->sound : 
                PACKAGE_DATA_DIR "/orage/sounds/Spo.wav"));

    /* sound repeat */
    gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->SoundRepeat_checkbutton)
                    , appt->soundrepeat);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->SoundRepeat_spin_cnt)
            , (gdouble)appt->soundrepeat_cnt);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(apptw->SoundRepeat_spin_len)
            , (gdouble)appt->soundrepeat_len);

    /* display */
    gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_orage)
                    , appt->display_alarm_orage);
    /* display:notify */
#ifdef HAVE_NOTIFY
    gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_notify)
                    , appt->display_alarm_notify);
    if (!appt->display_alarm_notify || appt->display_notify_timeout == -1) { 
        /* no timeout */
        gtk_toggle_button_set_active(
                GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_expire_notify)
                        , FALSE);
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(apptw->Display_spin_expire_notify)
                , (gdouble)0);
    }
    else {
        gtk_toggle_button_set_active(
                GTK_TOGGLE_BUTTON(apptw->Display_checkbutton_expire_notify)
                        , TRUE);
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(apptw->Display_spin_expire_notify)
                , (gdouble)appt->display_notify_timeout);
    }
#endif

    /* procedure */
    gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->Proc_checkbutton), appt->procedure_alarm);
    tmp = g_strconcat(appt->procedure_cmd, " ", appt->procedure_params, NULL);
    gtk_entry_set_text(GTK_ENTRY(apptw->Proc_entry), tmp ? tmp : "");
    g_free(tmp);
}

/* Fill appointment window with data */
static void fill_appt_window(appt_win *apptw, char *action, char *par)
{
    xfical_appt *appt;
    struct tm *t, tm_date;
    char *untildate_to_display;
    int i;

    orage_message(10, "%s appointment: %s", action, par);
    if ((appt = fill_appt_window_get_appt(apptw, action, par)) == NULL) {
        apptw->xf_appt = NULL;
        return;
    }
    apptw->xf_appt = appt;

    /* first flags */
    apptw->xf_uid = g_strdup(appt->uid);
    apptw->par = g_strdup((const gchar *)par);
    apptw->appointment_changed = FALSE;
    if (strcmp(action, "NEW") == 0) {
        apptw->appointment_add = TRUE;
        apptw->appointment_new = TRUE;
    }
    else if (strcmp(action, "UPDATE") == 0) {
        apptw->appointment_add = FALSE;
        apptw->appointment_new = FALSE;
    }
    else if (strcmp(action, "COPY") == 0) {
            /* COPY uses old uid as base and adds new, so
             * add == TRUE && new == FALSE */
        apptw->appointment_add = TRUE;
        apptw->appointment_new = FALSE;
    }
    else {
        g_error("fill_appt_window: unknown parameter\n");
        return;
    }
    if (!appt->completed) { /* some nice default */
        t = orage_localtime(); /* probably completed today? */
        g_sprintf(appt->completedtime, XFICAL_APPT_TIME_FORMAT
                , t->tm_year+1900, t->tm_mon+1 , t->tm_mday
                , t->tm_hour, t->tm_min, 0);
        appt->completed_tz_loc = g_strdup(appt->start_tz_loc);
    }
    /* we only want to enable duplication if we are working with an old
     * existing app (=not adding new) */
    gtk_widget_set_sensitive(apptw->Duplicate, !apptw->appointment_add);
    gtk_widget_set_sensitive(apptw->File_menu_duplicate
            , !apptw->appointment_add);

    /* window title */
    gtk_window_set_title(GTK_WINDOW(apptw->Window)
            , _("New appointment - Orage"));

    /********************* GENERAL tab *********************/
    /* type */
    if (appt->type == XFICAL_TYPE_EVENT)
        gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->Type_event_rb), TRUE);
    else if (appt->type == XFICAL_TYPE_TODO)
        gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->Type_todo_rb), TRUE);
    else if (appt->type == XFICAL_TYPE_JOURNAL)
        gtk_toggle_button_set_active(
            GTK_TOGGLE_BUTTON(apptw->Type_journal_rb), TRUE);
    else
        g_warning("fill_appt_window: Illegal value for type\n");

    /* appointment name */
    gtk_entry_set_text(GTK_ENTRY(apptw->Title_entry)
            , (appt->title ? appt->title : ""));

    if (strcmp(action, "COPY") == 0) {
        gtk_editable_set_position(GTK_EDITABLE(apptw->Title_entry), -1);
        i = gtk_editable_get_position(GTK_EDITABLE(apptw->Title_entry));
        gtk_editable_insert_text(GTK_EDITABLE(apptw->Title_entry)
                , _(" *** COPY ***"), strlen(_(" *** COPY ***")), &i);
    }

    /* location */
    gtk_entry_set_text(GTK_ENTRY(apptw->Location_entry)
            , (appt->location ? appt->location : ""));

    /* times */
    fill_appt_window_times(apptw, appt);

    /* availability */
    if (appt->availability != -1) {
        gtk_combo_box_set_active(GTK_COMBO_BOX(apptw->Availability_cb)
                   , appt->availability);
    }

    /* categories */
    fill_category_data(apptw, appt);

    /* note */
    gtk_text_buffer_set_text(apptw->Note_buffer
            , (appt->note ? (const gchar *) appt->note : ""), -1);

    /********************* ALARM tab *********************/
    fill_appt_window_alarm(appt, apptw);

    /********************* RECURRENCE tab *********************/
    /* recurrence */
    gtk_combo_box_set_active(GTK_COMBO_BOX(apptw->Recur_freq_cb)
            , appt->freq);
    switch(appt->recur_limit) {
        case 0: /* no limit */
            gtk_toggle_button_set_active(
                    GTK_TOGGLE_BUTTON(apptw->Recur_limit_rb), TRUE);
            gtk_spin_button_set_value(
                    GTK_SPIN_BUTTON(apptw->Recur_count_spin), (gdouble)1);
            untildate_to_display = orage_localdate_i18();
            gtk_button_set_label(GTK_BUTTON(apptw->Recur_until_button)
                    , (const gchar *)untildate_to_display);
            break;
        case 1: /* count */
            gtk_toggle_button_set_active(
                    GTK_TOGGLE_BUTTON(apptw->Recur_count_rb), TRUE);
            gtk_spin_button_set_value(
                    GTK_SPIN_BUTTON(apptw->Recur_count_spin)
                    , (gdouble)appt->recur_count);
            untildate_to_display = orage_localdate_i18();
            gtk_button_set_label(GTK_BUTTON(apptw->Recur_until_button)
                    , (const gchar *)untildate_to_display);
            break;
        case 2: /* until */
            gtk_toggle_button_set_active(
                    GTK_TOGGLE_BUTTON(apptw->Recur_until_rb), TRUE);
            gtk_spin_button_set_value(
                    GTK_SPIN_BUTTON(apptw->Recur_count_spin), (gdouble)1);
            tm_date = orage_icaltime_to_tm_time(appt->recur_until, TRUE);
            untildate_to_display = orage_tm_date_to_i18_date(&tm_date);
            gtk_button_set_label(GTK_BUTTON(apptw->Recur_until_button)
                    , (const gchar *)untildate_to_display);
            break;
        default: /* error */
            g_warning("fill_appt_window: Unsupported recur_limit %d",
                    appt->recur_limit);
    }

    /* weekdays */
    for (i=0; i <= 6; i++) {
        gtk_toggle_button_set_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_byday_cb[i])
                , appt->recur_byday[i]);
        /* which weekday of month / year */
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(apptw->Recur_byday_spin[i])
                , (gdouble)appt->recur_byday_cnt[i]);
    }

    /* interval */
    gtk_spin_button_set_value(
            GTK_SPIN_BUTTON(apptw->Recur_int_spin)
            , (gdouble)appt->interval);

    if (appt->interval > 1
    || !appt->recur_byday[0] || !appt->recur_byday[1] || !appt->recur_byday[2] 
    || !appt->recur_byday[3] || !appt->recur_byday[4] || !appt->recur_byday[5] 
    || !appt->recur_byday[6]) {
        gtk_toggle_button_set_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_feature_advanced_rb), TRUE);
    }
    else {
        gtk_toggle_button_set_active(
                GTK_TOGGLE_BUTTON(apptw->Recur_feature_normal_rb), TRUE);
    }

    set_time_sensitivity(apptw);
    set_repeat_sensitivity(apptw);
    set_sound_sensitivity(apptw);
    set_notify_sensitivity(apptw);
    set_proc_sensitivity(apptw);
    mark_appointment_unchanged(apptw);
}

static void build_menu(appt_win *apptw)
{
    GtkWidget *menu_separator;

    /* Menu bar */
    apptw->Menubar = gtk_menu_bar_new();
    gtk_box_pack_start(GTK_BOX(apptw->Vbox), apptw->Menubar, FALSE, FALSE, 0);

    /* File menu stuff */
    apptw->File_menu = orage_menu_new(_("_File"), apptw->Menubar);

    apptw->File_menu_save = orage_image_menu_item_new_from_stock("gtk-save"
            , apptw->File_menu, apptw->accel_group);

    apptw->File_menu_saveclose = 
            orage_menu_item_new_with_mnemonic(_("Sav_e and close")
                    , apptw->File_menu);
    gtk_widget_add_accelerator(apptw->File_menu_saveclose
            , "activate", apptw->accel_group
            , GDK_w, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);

    menu_separator = orage_separator_menu_item_new(apptw->File_menu);

    apptw->File_menu_revert = 
            orage_image_menu_item_new_from_stock("gtk-revert-to-saved"
                    , apptw->File_menu, apptw->accel_group);

    apptw->File_menu_duplicate = 
            orage_menu_item_new_with_mnemonic(_("D_uplicate")
                    , apptw->File_menu);
    gtk_widget_add_accelerator(apptw->File_menu_duplicate
            , "activate", apptw->accel_group
            , GDK_d, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

    menu_separator = orage_separator_menu_item_new(apptw->File_menu);

    apptw->File_menu_delete = orage_image_menu_item_new_from_stock("gtk-delete"
            , apptw->File_menu, apptw->accel_group);

    menu_separator = orage_separator_menu_item_new(apptw->File_menu);

    apptw->File_menu_close = orage_image_menu_item_new_from_stock("gtk-close"
            , apptw->File_menu, apptw->accel_group);

    g_signal_connect((gpointer)apptw->File_menu_save, "activate"
            , G_CALLBACK(on_appFileSave_menu_activate_cb), apptw);
    g_signal_connect((gpointer)apptw->File_menu_saveclose, "activate"
            , G_CALLBACK(on_appFileSaveClose_menu_activate_cb), apptw);
    g_signal_connect((gpointer)apptw->File_menu_duplicate, "activate"
            , G_CALLBACK(on_appFileDuplicate_menu_activate_cb), apptw);
    g_signal_connect((gpointer)apptw->File_menu_revert, "activate"
            , G_CALLBACK(on_appFileRevert_menu_activate_cb), apptw);
    g_signal_connect((gpointer)apptw->File_menu_delete, "activate"
            , G_CALLBACK(on_appFileDelete_menu_activate_cb), apptw);
    g_signal_connect((gpointer)apptw->File_menu_close, "activate"
            , G_CALLBACK(on_appFileClose_menu_activate_cb), apptw);
}

static OrageRc *orage_alarm_file_open(gboolean read_only)
{
    gchar *fpath;
    OrageRc *orc;

    fpath = orage_config_file_location(ORAGE_DEFAULT_ALARM_FILE);
    if (!read_only)  /* we need to empty it before each write */
        g_remove(fpath);
    if ((orc = (OrageRc *)orage_rc_file_open(fpath, read_only)) == NULL) {
        orage_message(150, "orage_alarm_file_open: default alarm file open failed.");
    }
    g_free(fpath);

    return(orc);
}

static void store_default_alarm(xfical_appt *appt)
{
    OrageRc *orc;

    orc = orage_alarm_file_open(FALSE);
    orage_rc_put_int(orc, "TIME", appt->alarmtime);
    orage_rc_put_bool(orc, "BEFORE", appt->alarm_before);
    orage_rc_put_bool(orc, "RELATED_START", appt->alarm_related_start);
    orage_rc_put_bool(orc, "PERSISTENT", appt->alarm_persistent);
    orage_rc_put_bool(orc, "SOUND_USE", appt->sound_alarm);
    orage_rc_put_str(orc, "SOUND", appt->sound);
    orage_rc_put_bool(orc, "SOUND_REPEAT_USE", appt->soundrepeat);
    orage_rc_put_int(orc, "SOUND_REPEAT_CNT", appt->soundrepeat_cnt);
    orage_rc_put_int(orc, "SOUND_REPEAT_LEN", appt->soundrepeat_len);
    orage_rc_put_bool(orc, "DISPLAY_ORAGE_USE", appt->display_alarm_orage);
    orage_rc_put_bool(orc, "DISPLAY_NOTIFY_USE", appt->display_alarm_notify);
    orage_rc_put_int(orc, "DISPLAY_NOTIFY_TIMEOUT"
            , appt->display_notify_timeout);
    orage_rc_put_bool(orc, "PROCEDURE_USE", appt->procedure_alarm);
    orage_rc_put_str(orc, "PROCEDURE_CMD", appt->procedure_cmd);
    orage_rc_put_str(orc, "PROCEDURE_PARAMS", appt->procedure_params);
    orage_rc_file_close(orc);
}

static void read_default_alarm(xfical_appt *appt)
{
    OrageRc *orc;

    orc = orage_alarm_file_open(TRUE);
    appt->alarmtime = orage_rc_get_int(orc, "TIME", 5*60); /* 5 mins */
    appt->alarm_before = orage_rc_get_bool(orc, "BEFORE", TRUE);
    appt->alarm_related_start = orage_rc_get_bool(orc, "RELATED_START", TRUE);
    appt->alarm_persistent = orage_rc_get_bool(orc, "PERSISTENT", FALSE);
    appt->sound_alarm = orage_rc_get_bool(orc, "SOUND_USE", TRUE);
    if (appt->sound)
        g_free(appt->sound);
    appt->sound = orage_rc_get_str(orc, "SOUND"
            , PACKAGE_DATA_DIR "/orage/sounds/Spo.wav");
    appt->soundrepeat = orage_rc_get_bool(orc, "SOUND_REPEAT_USE", FALSE);
    appt->soundrepeat_cnt = orage_rc_get_int(orc, "SOUND_REPEAT_CNT", 500);
    appt->soundrepeat_len = orage_rc_get_int(orc, "SOUND_REPEAT_LEN", 2);
    appt->display_alarm_orage = orage_rc_get_bool(orc, "DISPLAY_ORAGE_USE"
            , TRUE);
    appt->display_alarm_notify = orage_rc_get_bool(orc, "DISPLAY_NOTIFY_USE"
            , FALSE);
    appt->display_notify_timeout = orage_rc_get_int(orc
            , "DISPLAY_NOTIFY_TIMEOUT", 0);
    appt->procedure_alarm = orage_rc_get_bool(orc, "PROCEDURE_USE", FALSE);
    if (appt->procedure_cmd)
        g_free(appt->procedure_cmd);
    appt->procedure_cmd = orage_rc_get_str(orc, "PROCEDURE_CMD", "");
    if (appt->procedure_params)
        g_free(appt->procedure_params);
    appt->procedure_params = orage_rc_get_str(orc, "PROCEDURE_PARAMS", "");
    orage_rc_file_close(orc);
}

static void on_appDefault_save_button_clicked_cb(GtkButton *button
        , gpointer user_data)
{
    appt_win *apptw = (appt_win *)user_data;
    xfical_appt *appt = (xfical_appt *)apptw->xf_appt;

    fill_appt_from_apptw_alarm(appt, apptw);
    store_default_alarm(appt);
}

static void on_appDefault_read_button_clicked_cb(GtkButton *button
        , gpointer user_data)
{
    appt_win *apptw = (appt_win *)user_data;
    xfical_appt *appt = (xfical_appt *)apptw->xf_appt;

    read_default_alarm(appt);
    fill_appt_window_alarm(appt, apptw);
}

static void build_toolbar(appt_win *apptw)
{
    GtkWidget *toolbar_separator;
    gint i = 0;

    apptw->Toolbar = gtk_toolbar_new();
    gtk_box_pack_start(GTK_BOX(apptw->Vbox), apptw->Toolbar, FALSE, FALSE, 0);
    gtk_toolbar_set_tooltips(GTK_TOOLBAR(apptw->Toolbar), TRUE);

    /* Add buttons to the toolbar */
    apptw->Save = orage_toolbar_append_button(apptw->Toolbar
            , "gtk-save", apptw->Tooltips, _("Save"), i++);
    apptw->SaveClose = orage_toolbar_append_button(apptw->Toolbar
            , "gtk-close", apptw->Tooltips, _("Save and close"), i++);

    toolbar_separator = orage_toolbar_append_separator(apptw->Toolbar, i++);

    apptw->Revert = orage_toolbar_append_button(apptw->Toolbar
            , "gtk-revert-to-saved", apptw->Tooltips, _("Revert"), i++);
    apptw->Duplicate = orage_toolbar_append_button(apptw->Toolbar
            , "gtk-copy", apptw->Tooltips, _("Duplicate"), i++);

    toolbar_separator = orage_toolbar_append_separator(apptw->Toolbar, i++);

    apptw->Delete = orage_toolbar_append_button(apptw->Toolbar
            , "gtk-delete", apptw->Tooltips, _("Delete"), i++);

    g_signal_connect((gpointer)apptw->Save, "clicked"
            , G_CALLBACK(on_appSave_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->SaveClose, "clicked"
            , G_CALLBACK(on_appSaveClose_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Revert, "clicked"
            , G_CALLBACK(on_appRevert_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Duplicate, "clicked"
            , G_CALLBACK(on_appDuplicate_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Delete, "clicked"
            , G_CALLBACK(on_appDelete_clicked_cb), apptw);
}

static void build_general_page(appt_win *apptw)
{
    gint row;
    GtkWidget *label, *event, *hbox;
    char *availability_array[2] = {_("Free"), _("Busy")};

    apptw->TableGeneral = orage_table_new(11, BORDER_SIZE);
    apptw->General_notebook_page = apptw->TableGeneral;
    apptw->General_tab_label = gtk_label_new(_("General"));

    gtk_notebook_append_page(GTK_NOTEBOOK(apptw->Notebook)
            , apptw->General_notebook_page, apptw->General_tab_label);

    /* type */
    apptw->Type_label = gtk_label_new(_("Type "));
    hbox =  gtk_hbox_new(FALSE, 0);
    apptw->Type_event_rb = gtk_radio_button_new_with_label(NULL, _("Event"));
    gtk_box_pack_start(GTK_BOX(hbox), apptw->Type_event_rb, FALSE, FALSE, 15);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Type_event_rb
            , _("Event that will happen sometime. For example:\nMeeting or birthday or TV show."), NULL);

    apptw->Type_todo_rb = gtk_radio_button_new_with_mnemonic_from_widget(
            GTK_RADIO_BUTTON(apptw->Type_event_rb) , _("Todo"));
    gtk_box_pack_start(GTK_BOX(hbox), apptw->Type_todo_rb, FALSE, FALSE, 15);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Type_todo_rb
            , _("Something that you should do sometime. For example:\nWash your car or test new version of Orage."), NULL);

    apptw->Type_journal_rb = gtk_radio_button_new_with_mnemonic_from_widget(
            GTK_RADIO_BUTTON(apptw->Type_event_rb) , _("Journal"));
    gtk_box_pack_start(GTK_BOX(hbox), apptw->Type_journal_rb
            , FALSE, FALSE, 15);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Type_journal_rb
            , _("Make a note that something happened. For example:\nRemark that your mother called or first snow came."), NULL);

    orage_table_add_row(apptw->TableGeneral
            , apptw->Type_label, hbox
            , row = 0, (GTK_EXPAND | GTK_FILL), (0));

    /* title */
    apptw->Title_label = gtk_label_new(_("Title "));
    apptw->Title_entry = gtk_entry_new();
    orage_table_add_row(apptw->TableGeneral
            , apptw->Title_label, apptw->Title_entry
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    /* location */
    apptw->Location_label = gtk_label_new(_("Location"));
    apptw->Location_entry = gtk_entry_new();
    orage_table_add_row(apptw->TableGeneral
            , apptw->Location_label, apptw->Location_entry
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    /* All day */
    apptw->AllDay_checkbutton = 
            gtk_check_button_new_with_mnemonic(_("All day event"));
    orage_table_add_row(apptw->TableGeneral
            , NULL, apptw->AllDay_checkbutton
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    /* start time */
    apptw->Start_label = gtk_label_new(_("Start"));
    apptw->StartDate_button = gtk_button_new();
    apptw->StartTime_spin_hh = gtk_spin_button_new_with_range(0, 23, 1);
    apptw->StartTime_spin_mm = gtk_spin_button_new_with_range(0, 59, 1);
    apptw->StartTimezone_button = gtk_button_new();
    apptw->StartTime_hbox = datetime_hbox_new(
            apptw->StartDate_button, 
            apptw->StartTime_spin_hh, 
            apptw->StartTime_spin_mm, 
            apptw->StartTimezone_button);
    orage_table_add_row(apptw->TableGeneral
            , apptw->Start_label, apptw->StartTime_hbox
            , ++row, (GTK_SHRINK | GTK_FILL), (GTK_SHRINK | GTK_FILL));

    /* end time */
    apptw->End_label = gtk_label_new(_("End"));
    apptw->End_hbox = gtk_hbox_new(FALSE, 0);
    /* translators: leave some spaces after this so that it looks better on
     * the screen */
    apptw->End_checkbutton = 
            gtk_check_button_new_with_mnemonic(_("Set      "));
    gtk_box_pack_start(GTK_BOX(apptw->End_hbox)
            , apptw->End_checkbutton, FALSE, FALSE, 0);
    apptw->EndDate_button = gtk_button_new();
    apptw->EndTime_spin_hh = gtk_spin_button_new_with_range(0, 23, 1);
    apptw->EndTime_spin_mm = gtk_spin_button_new_with_range(0, 59, 1);
    apptw->EndTimezone_button = gtk_button_new();
    apptw->EndTime_hbox = datetime_hbox_new(
            apptw->EndDate_button, 
            apptw->EndTime_spin_hh, 
            apptw->EndTime_spin_mm, 
            apptw->EndTimezone_button);
    gtk_box_pack_end(GTK_BOX(apptw->End_hbox)
            , apptw->EndTime_hbox, TRUE, TRUE, 0);
    orage_table_add_row(apptw->TableGeneral
            , apptw->End_label, apptw->End_hbox
            , ++row, (GTK_SHRINK | GTK_FILL), (GTK_SHRINK | GTK_FILL));

    /* duration */
    apptw->Dur_hbox = gtk_hbox_new(FALSE, 0);
    apptw->Dur_checkbutton = 
            gtk_check_button_new_with_mnemonic(_("Duration"));
    gtk_box_pack_start(GTK_BOX(apptw->Dur_hbox), apptw->Dur_checkbutton
            , FALSE, FALSE, 0);
    apptw->Dur_spin_dd = gtk_spin_button_new_with_range(0, 1000, 1);
    apptw->Dur_spin_dd_label = gtk_label_new(_("days"));
    apptw->Dur_spin_hh = gtk_spin_button_new_with_range(0, 23, 1);
    apptw->Dur_spin_hh_label = gtk_label_new(_("hours"));
    apptw->Dur_spin_mm = gtk_spin_button_new_with_range(0, 59, 5);
    apptw->Dur_spin_mm_label = gtk_label_new(_("mins"));
    apptw->Dur_time_hbox = period_hbox_new(TRUE, FALSE
            , apptw->Dur_spin_dd, apptw->Dur_spin_dd_label
            , apptw->Dur_spin_hh, apptw->Dur_spin_hh_label
            , apptw->Dur_spin_mm, apptw->Dur_spin_mm_label);
    gtk_box_pack_start(GTK_BOX(apptw->Dur_hbox), apptw->Dur_time_hbox
            , FALSE, FALSE, 0);
    orage_table_add_row(apptw->TableGeneral
            , NULL, apptw->Dur_hbox
            , ++row, (GTK_FILL), (GTK_FILL));
    
    /* Availability (only for EVENT) */
    apptw->Availability_label = gtk_label_new(_("Availability"));
    apptw->Availability_cb = orage_create_combo_box_with_content(
            availability_array, 2);
    orage_table_add_row(apptw->TableGeneral
            , apptw->Availability_label, apptw->Availability_cb
            , ++row, (GTK_FILL), (GTK_FILL));
    
    /* completed (only for TODO) */
    apptw->Completed_label = gtk_label_new(_("Completed"));
    apptw->Completed_hbox = gtk_hbox_new(FALSE, 0);
    apptw->Completed_checkbutton = 
            gtk_check_button_new_with_mnemonic(_("Done"));
    gtk_box_pack_start(GTK_BOX(apptw->Completed_hbox)
            , apptw->Completed_checkbutton, FALSE, FALSE, 0);
    label = gtk_label_new(" ");
    gtk_box_pack_start(GTK_BOX(apptw->Completed_hbox), label, FALSE, FALSE, 4);
    apptw->CompletedDate_button = gtk_button_new();
    apptw->CompletedTime_spin_hh = gtk_spin_button_new_with_range(0, 23, 1);
    apptw->CompletedTime_spin_mm = gtk_spin_button_new_with_range(0, 59, 1);
    apptw->CompletedTimezone_button = gtk_button_new();
    apptw->CompletedTime_hbox = datetime_hbox_new(
            apptw->CompletedDate_button, 
            apptw->CompletedTime_spin_hh, 
            apptw->CompletedTime_spin_mm, 
            apptw->CompletedTimezone_button);
    gtk_box_pack_end(GTK_BOX(apptw->Completed_hbox)
            , apptw->CompletedTime_hbox, TRUE, TRUE, 0);
    orage_table_add_row(apptw->TableGeneral
            , apptw->Completed_label, apptw->Completed_hbox
            , ++row, (GTK_FILL), (GTK_FILL));

    /* categories */
    apptw->Categories_label = gtk_label_new(_("Categories"));
    apptw->Categories_hbox = gtk_hbox_new(FALSE, 0);
    apptw->Categories_entry = gtk_entry_new();
    gtk_box_pack_start(GTK_BOX(apptw->Categories_hbox), apptw->Categories_entry
            , TRUE, TRUE, 0);
    apptw->Categories_cb = gtk_combo_box_new_text();
    apptw->Categories_cb_event =  gtk_event_box_new(); /* needed for tooltips */
    gtk_container_add(GTK_CONTAINER(apptw->Categories_cb_event)
            , apptw->Categories_cb);
    gtk_box_pack_start(GTK_BOX(apptw->Categories_hbox)
            , apptw->Categories_cb_event, FALSE, FALSE, 4);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Categories_cb_event
            , _("This is special category, which can be used to color this appointment in list views."), NULL);
    apptw->Categories_button =gtk_button_new_from_stock(GTK_STOCK_SELECT_COLOR);
    gtk_box_pack_start(GTK_BOX(apptw->Categories_hbox), apptw->Categories_button
            , FALSE, FALSE, 0);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Categories_button
            , _("update colors for categories."), NULL);
    orage_table_add_row(apptw->TableGeneral
            , apptw->Categories_label, apptw->Categories_hbox
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    /* note */
    apptw->Note = gtk_label_new(_("Note"));
    apptw->Note_Scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
    event =  gtk_event_box_new(); /* only needed for tooltips */
    gtk_container_add(GTK_CONTAINER(event), apptw->Note_Scrolledwindow);
    gtk_scrolled_window_set_policy(
            GTK_SCROLLED_WINDOW(apptw->Note_Scrolledwindow)
            , GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_set_shadow_type(
            GTK_SCROLLED_WINDOW(apptw->Note_Scrolledwindow), GTK_SHADOW_IN);
    apptw->Note_buffer = gtk_text_buffer_new(NULL);
    apptw->Note_textview = 
            gtk_text_view_new_with_buffer(GTK_TEXT_BUFFER(apptw->Note_buffer));
    gtk_container_add(GTK_CONTAINER(apptw->Note_Scrolledwindow)
            , apptw->Note_textview);
    orage_table_add_row(apptw->TableGeneral
            , apptw->Note, event
            , ++row, (GTK_EXPAND | GTK_FILL), (GTK_EXPAND | GTK_FILL));

    gtk_tooltips_set_tip(apptw->Tooltips, event
            , _("<D> inserts current date in local date format.\n<T> inserts time and\n<DT> inserts date and time."), NULL);

    g_signal_connect((gpointer)apptw->Type_event_rb, "clicked"
            , G_CALLBACK(app_type_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Type_todo_rb, "clicked"
            , G_CALLBACK(app_type_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Type_journal_rb, "clicked"
            , G_CALLBACK(app_type_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->AllDay_checkbutton, "clicked"
            , G_CALLBACK(app_time_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->StartDate_button, "clicked"
            , G_CALLBACK(on_Date_button_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->StartTime_spin_hh, "changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->StartTime_spin_mm, "changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->StartTimezone_button, "clicked"
            , G_CALLBACK(on_appStartTimezone_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->End_checkbutton, "clicked"
            , G_CALLBACK(app_time_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->EndDate_button, "clicked"
            , G_CALLBACK(on_Date_button_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->EndTime_spin_hh, "changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->EndTime_spin_mm, "changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->EndTimezone_button, "clicked"
            , G_CALLBACK(on_appEndTimezone_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Dur_checkbutton, "clicked"
            , G_CALLBACK(app_time_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Dur_spin_dd, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Dur_spin_hh, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Dur_spin_mm, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Completed_checkbutton, "clicked"
            , G_CALLBACK(app_time_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->CompletedDate_button, "clicked"
            , G_CALLBACK(on_Date_button_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->CompletedTime_spin_hh, "changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->CompletedTime_spin_mm, "changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->CompletedTimezone_button, "clicked"
            , G_CALLBACK(on_appCompletedTimezone_clicked_cb), apptw);
    /* Take care of the title entry to build the appointment window title */
    g_signal_connect((gpointer)apptw->Title_entry, "changed"
            , G_CALLBACK(on_appTitle_entry_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Location_entry, "changed"
            , G_CALLBACK(on_app_entry_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Categories_entry, "changed"
            , G_CALLBACK(on_app_entry_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Categories_cb, "changed"
            , G_CALLBACK(on_app_combobox_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Categories_button, "clicked"
            , G_CALLBACK(on_categories_button_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Availability_cb, "changed"
            , G_CALLBACK(on_app_combobox_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Note_buffer, "changed"
            , G_CALLBACK(on_appNote_buffer_changed_cb), apptw);
}

static void build_alarm_page(appt_win *apptw)
{
    gint row;
    GtkWidget *label, *event;
    char *when_array[4] = {_("Before Start"), _("Before End")
        , _("After Start"), _("After End")};

    /***** Header *****/
    apptw->TableAlarm = orage_table_new(8, BORDER_SIZE);
    apptw->Alarm_notebook_page = apptw->TableAlarm;
    apptw->Alarm_tab_label = gtk_label_new(_("Alarm"));

    gtk_notebook_append_page(GTK_NOTEBOOK(apptw->Notebook)
            , apptw->Alarm_notebook_page, apptw->Alarm_tab_label);

    /***** ALARM TIME *****/
    apptw->Alarm_label = gtk_label_new(_("Alarm time"));
    apptw->Alarm_hbox = gtk_hbox_new(FALSE, 0);
    apptw->Alarm_spin_dd = gtk_spin_button_new_with_range(0, 100, 1);
    apptw->Alarm_spin_dd_label = gtk_label_new(_("days"));
    apptw->Alarm_spin_hh = gtk_spin_button_new_with_range(0, 23, 1);
    apptw->Alarm_spin_hh_label = gtk_label_new(_("hours"));
    apptw->Alarm_spin_mm = gtk_spin_button_new_with_range(0, 59, 5);
    apptw->Alarm_spin_mm_label = gtk_label_new(_("mins"));
    apptw->Alarm_time_hbox = period_hbox_new(FALSE, TRUE
            , apptw->Alarm_spin_dd, apptw->Alarm_spin_dd_label
            , apptw->Alarm_spin_hh, apptw->Alarm_spin_hh_label
            , apptw->Alarm_spin_mm, apptw->Alarm_spin_mm_label);
    gtk_box_pack_start(GTK_BOX(apptw->Alarm_hbox), apptw->Alarm_time_hbox
            , FALSE, FALSE, 0);
    apptw->Alarm_when_cb = orage_create_combo_box_with_content(when_array, 4);
    event =  gtk_event_box_new(); /* only needed for tooltips */
    gtk_container_add(GTK_CONTAINER(event), apptw->Alarm_when_cb);
    gtk_box_pack_start(GTK_BOX(apptw->Alarm_hbox)
            , event, FALSE, FALSE, 0);

    orage_table_add_row(apptw->TableAlarm
            , apptw->Alarm_label, apptw->Alarm_hbox
            , row = 0, (GTK_FILL), (GTK_FILL));
    gtk_tooltips_set_tip(apptw->Tooltips, event
            , _("Often you want to get alarm:\n 1) before Event start\n 2) before Todo end\n 3) after Todo start"), NULL);

    /***** Persistent Alarm *****/
    apptw->Per_hbox = gtk_hbox_new(FALSE, 6);
    apptw->Per_checkbutton = 
            gtk_check_button_new_with_mnemonic(_("Persistent alarm"));
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Per_checkbutton
            , _("Select this if you want Orage to remind you even if it has not been active when the alarm happened."), NULL);
    gtk_box_pack_start(GTK_BOX(apptw->Per_hbox), apptw->Per_checkbutton
            , FALSE, TRUE, 0);

    orage_table_add_row(apptw->TableAlarm
            , NULL, apptw->Per_hbox
            , ++row, (GTK_FILL), (GTK_FILL));

    /***** Audio Alarm *****/
    apptw->Sound_label = gtk_label_new(_("Sound"));

    apptw->Sound_hbox = gtk_hbox_new(FALSE, 6);
    apptw->Sound_checkbutton = 
            gtk_check_button_new_with_mnemonic(_("Use"));
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Sound_checkbutton
            , _("Select this if you want audible alarm"), NULL);
    gtk_box_pack_start(GTK_BOX(apptw->Sound_hbox), apptw->Sound_checkbutton
            , FALSE, TRUE, 0);

    apptw->Sound_entry = gtk_entry_new();
    gtk_box_pack_start(GTK_BOX(apptw->Sound_hbox), apptw->Sound_entry
            , TRUE, TRUE, 0);
    apptw->Sound_button = gtk_button_new_from_stock("gtk-open");
    gtk_box_pack_start(GTK_BOX(apptw->Sound_hbox), apptw->Sound_button
            , FALSE, TRUE, 0);

    orage_table_add_row(apptw->TableAlarm
            , apptw->Sound_label, apptw->Sound_hbox
            , ++row, (GTK_FILL), (GTK_FILL));

    apptw->SoundRepeat_hbox = gtk_hbox_new(FALSE, 0);
    apptw->SoundRepeat_checkbutton = 
            gtk_check_button_new_with_mnemonic(_("Repeat alarm sound"));
    gtk_box_pack_start(GTK_BOX(apptw->SoundRepeat_hbox)
            , apptw->SoundRepeat_checkbutton
            , FALSE, FALSE, 0);

    label = gtk_label_new(" ");
    gtk_box_pack_start(GTK_BOX(apptw->SoundRepeat_hbox), label
            , FALSE, FALSE, 10);

    apptw->SoundRepeat_spin_cnt = gtk_spin_button_new_with_range(1, 999, 10);
    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(apptw->SoundRepeat_spin_cnt)
            , TRUE);
    gtk_box_pack_start(GTK_BOX(apptw->SoundRepeat_hbox)
            , apptw->SoundRepeat_spin_cnt
            , FALSE, FALSE, 0);

    apptw->SoundRepeat_spin_cnt_label = gtk_label_new(_("times"));
    gtk_box_pack_start(GTK_BOX(apptw->SoundRepeat_hbox)
            , apptw->SoundRepeat_spin_cnt_label
            , FALSE, FALSE, 5);
    
    label = gtk_label_new(" ");
    gtk_box_pack_start(GTK_BOX(apptw->SoundRepeat_hbox), label
            , FALSE, FALSE, 10);

    apptw->SoundRepeat_spin_len = gtk_spin_button_new_with_range(1, 250, 1);
    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(apptw->SoundRepeat_spin_len)
            , TRUE);
    gtk_box_pack_start(GTK_BOX(apptw->SoundRepeat_hbox)
            , apptw->SoundRepeat_spin_len
            , FALSE, FALSE, 0);

    apptw->SoundRepeat_spin_len_label = gtk_label_new(_("sec interval"));
    gtk_box_pack_start(GTK_BOX(apptw->SoundRepeat_hbox)
            , apptw->SoundRepeat_spin_len_label
            , FALSE, FALSE, 5);

    orage_table_add_row(apptw->TableAlarm
            , NULL, apptw->SoundRepeat_hbox
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    /***** Display Alarm *****/
    apptw->Display_label = gtk_label_new(_("Visual"));

    apptw->Display_hbox_orage = gtk_hbox_new(FALSE, 0);
    apptw->Display_checkbutton_orage = 
            gtk_check_button_new_with_mnemonic(_("Use Orage window"));
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Display_checkbutton_orage
            , _("Select this if you want Orage window alarm"), NULL);
    gtk_box_pack_start(GTK_BOX(apptw->Display_hbox_orage)
            , apptw->Display_checkbutton_orage
            , FALSE, TRUE, 0);

    orage_table_add_row(apptw->TableAlarm
            , apptw->Display_label, apptw->Display_hbox_orage
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

#ifdef HAVE_NOTIFY
    apptw->Display_hbox_notify = gtk_hbox_new(FALSE, 0);
    apptw->Display_checkbutton_notify = 
            gtk_check_button_new_with_mnemonic(_("Use notification"));
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Display_checkbutton_notify
            , _("Select this if you want notification alarm"), NULL);
    gtk_box_pack_start(GTK_BOX(apptw->Display_hbox_notify)
            , apptw->Display_checkbutton_notify
            , FALSE, TRUE, 0);

    apptw->Display_checkbutton_expire_notify = 
            gtk_check_button_new_with_mnemonic(_("Set timeout"));
    gtk_tooltips_set_tip(apptw->Tooltips
            , apptw->Display_checkbutton_expire_notify
            , _("Select this if you want notification to expire automatically")
            , NULL);
    gtk_box_pack_start(GTK_BOX(apptw->Display_hbox_notify)
            , apptw->Display_checkbutton_expire_notify
            , FALSE, TRUE, 10);

    apptw->Display_spin_expire_notify = 
            gtk_spin_button_new_with_range(0, 999, 1);
    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(apptw->Display_spin_expire_notify)
            , TRUE);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Display_spin_expire_notify
            , _("0 = system default expiration time"), NULL);
    gtk_box_pack_start(GTK_BOX(apptw->Display_hbox_notify)
            , apptw->Display_spin_expire_notify
            , FALSE, TRUE, 10);

    apptw->Display_spin_expire_notify_label = gtk_label_new(_("seconds"));
    gtk_box_pack_start(GTK_BOX(apptw->Display_hbox_notify)
            , apptw->Display_spin_expire_notify_label
            , FALSE, TRUE, 0);

    orage_table_add_row(apptw->TableAlarm
            , NULL, apptw->Display_hbox_notify
            , ++row, (GTK_EXPAND | GTK_FILL), (0));
#endif

    /***** Procedure Alarm *****/
    apptw->Proc_label = gtk_label_new(_("Procedure"));

    apptw->Proc_hbox = gtk_hbox_new(FALSE, 6);
    apptw->Proc_checkbutton = 
            gtk_check_button_new_with_mnemonic(_("Use"));
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Proc_checkbutton
            , _("Select this if you want procedure or script alarm"), NULL);
    gtk_box_pack_start(GTK_BOX(apptw->Proc_hbox), apptw->Proc_checkbutton
            , FALSE, TRUE, 0);

    apptw->Proc_entry = gtk_entry_new();
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Proc_entry
            , _("You must enter all escape etc characters yourself.\n This string is just given to shell to process"), NULL);
    gtk_box_pack_start(GTK_BOX(apptw->Proc_hbox), apptw->Proc_entry
            , TRUE, TRUE, 0);

    orage_table_add_row(apptw->TableAlarm
            , apptw->Proc_label, apptw->Proc_hbox
            , ++row, (GTK_FILL), (GTK_FILL));

    /***** Default Alarm Settings *****/
    apptw->Default_label = gtk_label_new(_("Default alarm"));

    apptw->Default_hbox = gtk_hbox_new(FALSE, 6);
    apptw->Default_savebutton =gtk_button_new_from_stock("gtk-save");
    gtk_box_pack_start(GTK_BOX(apptw->Default_hbox), apptw->Default_savebutton
            , FALSE, FALSE, 0);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Default_savebutton
            , _("Store current settings as default alarm"), NULL);
    apptw->Default_readbutton =gtk_button_new_from_stock("gtk-revert-to-saved");
    gtk_box_pack_start(GTK_BOX(apptw->Default_hbox), apptw->Default_readbutton
            , FALSE, TRUE, 0);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Default_readbutton
            , _("Set current settings from default alarm"), NULL);

    orage_table_add_row(apptw->TableAlarm
            , apptw->Default_label, apptw->Default_hbox
            , ++row, (GTK_FILL), (GTK_FILL));
    gtk_table_set_row_spacing(GTK_TABLE(apptw->TableAlarm), row-1, 50);

    g_signal_connect((gpointer)apptw->Alarm_spin_dd, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Alarm_spin_hh, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Alarm_spin_mm, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Alarm_when_cb, "changed"
            , G_CALLBACK(on_app_combobox_changed_cb), apptw);

    g_signal_connect((gpointer)apptw->Sound_checkbutton, "clicked"
            , G_CALLBACK(app_sound_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Sound_entry, "changed"
            , G_CALLBACK(on_app_entry_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Sound_button, "clicked"
            , G_CALLBACK(on_appSound_button_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->SoundRepeat_checkbutton, "clicked"
            , G_CALLBACK(app_sound_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->SoundRepeat_spin_cnt, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->SoundRepeat_spin_len, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);

    g_signal_connect((gpointer)apptw->Display_checkbutton_orage, "clicked"
            , G_CALLBACK(app_checkbutton_clicked_cb), apptw);
#ifdef HAVE_NOTIFY
    g_signal_connect((gpointer)apptw->Display_checkbutton_notify, "clicked"
            , G_CALLBACK(app_notify_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Display_checkbutton_expire_notify
            , "clicked"
            , G_CALLBACK(app_notify_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Display_spin_expire_notify
            , "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
#endif

    g_signal_connect((gpointer)apptw->Proc_checkbutton, "clicked"
            , G_CALLBACK(app_proc_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Proc_entry, "changed"
            , G_CALLBACK(on_app_entry_changed_cb), apptw);

    g_signal_connect((gpointer)apptw->Default_savebutton, "clicked"
            , G_CALLBACK(on_appDefault_save_button_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Default_readbutton, "clicked"
            , G_CALLBACK(on_appDefault_read_button_clicked_cb), apptw);
}

static void build_recurrence_page(appt_win *apptw)
{
    gint row, i;
    char *recur_freq_array[5] = {
        _("None"), _("Daily"), _("Weekly"), _("Monthly"), _("Yearly")};
    char *weekday_array[7] = {
        _("Mon"), _("Tue"), _("Wed"), _("Thu"), _("Fri"), _("Sat"), _("Sun")};

    apptw->TableRecur = orage_table_new(8, BORDER_SIZE);
    apptw->Recur_notebook_page = apptw->TableRecur;
    apptw->Recur_tab_label = gtk_label_new(_("Recurrence"));

    gtk_notebook_append_page(GTK_NOTEBOOK(apptw->Notebook)
            , apptw->Recur_notebook_page, apptw->Recur_tab_label);

    apptw->Recur_feature_label = gtk_label_new(_("Complexity"));
    apptw->Recur_feature_hbox = gtk_hbox_new(FALSE, 0);
    apptw->Recur_feature_normal_rb = gtk_radio_button_new_with_label(NULL
            , _("Basic"));
    gtk_box_pack_start(GTK_BOX(apptw->Recur_feature_hbox)
            , apptw->Recur_feature_normal_rb, FALSE, FALSE, 15);
    apptw->Recur_feature_advanced_rb = 
            gtk_radio_button_new_with_mnemonic_from_widget(
                    GTK_RADIO_BUTTON(apptw->Recur_feature_normal_rb)
                    , _("Advanced"));
    gtk_box_pack_start(GTK_BOX(apptw->Recur_feature_hbox)
            , apptw->Recur_feature_advanced_rb, FALSE, FALSE, 15);
    orage_table_add_row(apptw->TableRecur
            , apptw->Recur_feature_label, apptw->Recur_feature_hbox
            , row = 0, (GTK_EXPAND | GTK_FILL), (0));
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Recur_feature_normal_rb
            , _("Use this if you want regular repeating event"), NULL);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Recur_feature_advanced_rb
            , _("Use this if you need complex times like:\n Every Saturday and Sunday or \n First Tuesday every month")
            , NULL);

    apptw->Recur_freq_label = gtk_label_new(_("Frequency"));
    apptw->Recur_freq_hbox = gtk_hbox_new(FALSE, 0);
    apptw->Recur_freq_cb = orage_create_combo_box_with_content(
            recur_freq_array, 5);
    gtk_box_pack_start(GTK_BOX(apptw->Recur_freq_hbox)
            , apptw->Recur_freq_cb, FALSE, FALSE, 15);
    apptw->Recur_int_spin_label1 = gtk_label_new(_("Each"));
    gtk_box_pack_start(GTK_BOX(apptw->Recur_freq_hbox)
            , apptw->Recur_int_spin_label1, FALSE, FALSE, 0);
    apptw->Recur_int_spin = gtk_spin_button_new_with_range(1, 100, 1);
    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(apptw->Recur_int_spin), TRUE);
    gtk_box_pack_start(GTK_BOX(apptw->Recur_freq_hbox)
            , apptw->Recur_int_spin, FALSE, FALSE, 5);
    apptw->Recur_int_spin_label2 = gtk_label_new(_("occurrence"));
    gtk_box_pack_start(GTK_BOX(apptw->Recur_freq_hbox)
            , apptw->Recur_int_spin_label2, FALSE, FALSE, 0);
    gtk_tooltips_set_tip(apptw->Tooltips, apptw->Recur_int_spin
            , _("Limit frequency to certain interval.\n For example: Every third day:\n Frequency = Daily and Interval = 3")
            , NULL);
    orage_table_add_row(apptw->TableRecur
            , apptw->Recur_freq_label, apptw->Recur_freq_hbox
            , ++row, (GTK_EXPAND | GTK_FILL), (GTK_FILL));

    apptw->Recur_limit_label = gtk_label_new(_("Limit"));
    apptw->Recur_limit_rb = gtk_radio_button_new_with_label(NULL
            , _("Repeat forever"));
    orage_table_add_row(apptw->TableRecur
            , apptw->Recur_limit_label, apptw->Recur_limit_rb
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    apptw->Recur_count_hbox = gtk_hbox_new(FALSE, 0);
    apptw->Recur_count_rb = gtk_radio_button_new_with_mnemonic_from_widget(
            GTK_RADIO_BUTTON(apptw->Recur_limit_rb), _("Repeat "));
    gtk_box_pack_start(GTK_BOX(apptw->Recur_count_hbox)
            , apptw->Recur_count_rb, FALSE, FALSE, 0);
    apptw->Recur_count_spin = gtk_spin_button_new_with_range(1, 100, 1);
    gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(apptw->Recur_count_spin)
            , TRUE);
    gtk_box_pack_start(GTK_BOX(apptw->Recur_count_hbox)
            , apptw->Recur_count_spin, FALSE, FALSE, 0);
    apptw->Recur_count_label = gtk_label_new(_("times"));
    gtk_box_pack_start(GTK_BOX(apptw->Recur_count_hbox)
            , apptw->Recur_count_label, FALSE, FALSE, 5);
    orage_table_add_row(apptw->TableRecur
            , NULL, apptw->Recur_count_hbox
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    apptw->Recur_until_hbox = gtk_hbox_new(FALSE, 0);
    apptw->Recur_until_rb = gtk_radio_button_new_with_mnemonic_from_widget(
            GTK_RADIO_BUTTON(apptw->Recur_limit_rb), _("Repeat until "));
    gtk_box_pack_start(GTK_BOX(apptw->Recur_until_hbox)
            , apptw->Recur_until_rb, FALSE, FALSE, 0);
    apptw->Recur_until_button = gtk_button_new();
    gtk_box_pack_start(GTK_BOX(apptw->Recur_until_hbox)
            , apptw->Recur_until_button, FALSE, FALSE, 0);
    orage_table_add_row(apptw->TableRecur
            , NULL, apptw->Recur_until_hbox
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    apptw->Recur_byday_label = gtk_label_new(_("Weekdays"));
    apptw->Recur_byday_hbox = gtk_hbox_new(TRUE, 0);
    for (i=0; i <= 6; i++) {
        apptw->Recur_byday_cb[i] = 
                gtk_check_button_new_with_mnemonic(weekday_array[i]);
        gtk_box_pack_start(GTK_BOX(apptw->Recur_byday_hbox)
                , apptw->Recur_byday_cb[i], FALSE, FALSE, 0);
    }
    orage_table_add_row(apptw->TableRecur
            , apptw->Recur_byday_label, apptw->Recur_byday_hbox
            , ++row, (GTK_EXPAND | GTK_FILL), (0));

    apptw->Recur_byday_spin_label = gtk_label_new(_("Which day"));
    apptw->Recur_byday_spin_hbox = gtk_hbox_new(TRUE, 0);
    for (i=0; i <= 6; i++) {
        apptw->Recur_byday_spin[i] = 
                gtk_spin_button_new_with_range(-9, 9, 1);
        gtk_box_pack_start(GTK_BOX(apptw->Recur_byday_spin_hbox)
                , apptw->Recur_byday_spin[i], FALSE, FALSE, 0);
        gtk_tooltips_set_tip(apptw->Tooltips, apptw->Recur_byday_spin[i]
                , _("Specify which weekday for monthly and yearly events.\n For example:\n Second Wednesday each month:\n\tFrequency = Monthly,\n\tWeekdays = check only Wednesday,\n\tWhich day = select 2 from the number below Wednesday")
                , NULL);
    }
    orage_table_add_row(apptw->TableRecur
            , apptw->Recur_byday_spin_label, apptw->Recur_byday_spin_hbox
            , ++row ,(GTK_EXPAND | GTK_FILL), (0));

    g_signal_connect((gpointer)apptw->Recur_feature_normal_rb, "clicked"
            , G_CALLBACK(app_recur_feature_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Recur_feature_advanced_rb, "clicked"
            , G_CALLBACK(app_recur_feature_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Recur_freq_cb, "changed"
            , G_CALLBACK(on_freq_combobox_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Recur_int_spin, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Recur_limit_rb, "clicked"
            , G_CALLBACK(app_recur_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Recur_count_rb, "clicked"
            , G_CALLBACK(app_recur_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Recur_count_spin, "value-changed"
            , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    g_signal_connect((gpointer)apptw->Recur_until_rb, "clicked"
            , G_CALLBACK(app_recur_checkbutton_clicked_cb), apptw);
    g_signal_connect((gpointer)apptw->Recur_until_button, "clicked"
            , G_CALLBACK(on_Date_button_clicked_cb), apptw);
    for (i=0; i <= 6; i++) {
        g_signal_connect((gpointer)apptw->Recur_byday_cb[i], "clicked"
                , G_CALLBACK(app_checkbutton_clicked_cb), apptw);
        g_signal_connect((gpointer)apptw->Recur_byday_spin[i], "value-changed"
                , G_CALLBACK(on_app_spin_button_changed_cb), apptw);
    }
}

appt_win *create_appt_win(char *action, char *par)
{
    appt_win *apptw;
    GdkWindow *window;

    /*  initialisation + main window + base vbox */
    apptw = g_new(appt_win, 1);
    apptw->xf_uid = NULL;
    apptw->par = NULL;
    apptw->xf_appt = NULL;
    apptw->el = NULL;
    apptw->dw = NULL;
    apptw->appointment_changed = FALSE;
    apptw->Tooltips = gtk_tooltips_new();
    apptw->accel_group = gtk_accel_group_new();

    apptw->Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    /*
    gtk_window_set_default_size(GTK_WINDOW(apptw->Window), 450, 325);
    */
    gtk_window_add_accel_group(GTK_WINDOW(apptw->Window), apptw->accel_group);

    apptw->Vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(apptw->Window), apptw->Vbox);

    build_menu(apptw);
    build_toolbar(apptw);

    /* ********** Here begins tabs ********** */
    apptw->Notebook = gtk_notebook_new();
    gtk_container_add(GTK_CONTAINER(apptw->Vbox), apptw->Notebook);
    gtk_container_set_border_width(GTK_CONTAINER (apptw->Notebook), 5);

    build_general_page(apptw);
    build_alarm_page(apptw);
    build_recurrence_page(apptw);

    g_signal_connect((gpointer)apptw->Window, "delete-event"
            , G_CALLBACK(on_appWindow_delete_event_cb), apptw);

    fill_appt_window(apptw, action, par);
    if (apptw->xf_appt) { /* all fine */
        gtk_widget_show_all(apptw->Window);
        recur_hide_show(apptw);
        type_hide_show(apptw);
        gtk_widget_grab_focus(apptw->Title_entry);
        window = GTK_WIDGET(apptw->Window)->window;
        gdk_x11_window_set_user_time(window, gdk_x11_get_server_time(window));
        gtk_window_present(GTK_WINDOW(apptw->Window));

    }
    else { /* failed to get data */
        app_free_memory(apptw);
        apptw = NULL;
    }

    return(apptw);
}