/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* To-do widget for gncal
 *
 * Copyright (C) 1998 The Free Software Foundation
 *
 * Author: Federico Mena <quartic@gimp.org>
 */

#include <config.h>
#include <string.h>
#include <gnome.h>
#include "gncal-todo.h"
#include "calendar-commands.h"
#include "popup-menu.h"
#include "eventedit.h"

int todo_show_due_date = 0;
int todo_show_priority = 0;
int todo_show_time_remaining = 0;

int todo_item_dstatus_highlight_overdue = 0;
int todo_item_dstatus_highlight_due_today = 0;
int todo_item_dstatus_highlight_not_due_yet = 0;


char *todo_overdue_font_text;
gint todo_current_sort_column = 0;
gint todo_current_sort_type = GTK_SORT_ASCENDING;

gboolean todo_style_changed =0;
gboolean todo_list_autoresize = 1;
gboolean todo_list_redraw_in_progess = 0;
static void gncal_todo_init (GncalTodo *todo);


guint
gncal_todo_get_type (void)
{
	static guint todo_type = 0;

	if (!todo_type) {
		GtkTypeInfo todo_info = {
			"GncalTodo",
			sizeof (GncalTodo),
			sizeof (GncalTodoClass),
			(GtkClassInitFunc) NULL,
			(GtkObjectInitFunc) gncal_todo_init,
			(GtkArgSetFunc) NULL,
			(GtkArgGetFunc) NULL
		};

		todo_type = gtk_type_unique (gtk_vbox_get_type (), &todo_info);
	}

	return todo_type;
}

static void
ok_button (GtkWidget *widget, GnomeDialog *dialog)
{
	iCalObject *ico;
	GncalTodo *todo;
	GtkEntry *entry;
	GnomeDateEdit *due_date;
	GtkText *comment;
	GtkSpinButton *priority;

	ico = gtk_object_get_user_data (GTK_OBJECT (dialog));

	todo = GNCAL_TODO (gtk_object_get_data (GTK_OBJECT (dialog), "gncal_todo"));
	entry = GTK_ENTRY (gtk_object_get_data (GTK_OBJECT (dialog), "summary_entry"));
	due_date = GNOME_DATE_EDIT (gtk_object_get_data(GTK_OBJECT(dialog), "due_date"));
	priority = GTK_SPIN_BUTTON (gtk_object_get_data(GTK_OBJECT(dialog), "priority"));
	comment = GTK_TEXT(gtk_object_get_data (GTK_OBJECT(dialog), "comment"));
	if (ico->summary)
	  g_free (ico->summary);
	if (ico->comment)
	  g_free (ico->comment);
	ico->dtend = gnome_date_edit_get_date (due_date);
	ico->summary = g_strdup (gtk_entry_get_text (entry));
	ico->priority = gtk_spin_button_get_value_as_int (priority);
	ico->comment = gtk_editable_get_chars( GTK_EDITABLE(comment), 0, -1);
	ico->user_data = NULL;

	if (ico->new) {
		gnome_calendar_add_object (todo->calendar, ico);
		ico->new = FALSE;
	} else 
		gnome_calendar_object_changed (todo->calendar, ico, CHANGE_ALL); /* ok, summary only... */

	ical_object_unref (ico);

	gtk_widget_destroy (GTK_WIDGET (dialog));
}

static void
cancel_button (GtkWidget *widget, GnomeDialog *dialog)
{
	iCalObject *ico;

	ico = gtk_object_get_user_data (GTK_OBJECT (dialog));

	ico->user_data = NULL;

	ical_object_unref (ico);

	gtk_widget_destroy (GTK_WIDGET (dialog));


}

static gint
delete_event (GtkWidget *widget, GdkEvent *event, GnomeDialog *dialog)
{
	cancel_button (NULL, dialog);
	return TRUE;
}

static void
simple_todo_editor (GncalTodo *todo, iCalObject *ico)
{
	GtkWidget *dialog;
	GtkWidget *hbox;
	GtkWidget *due_box;
	GtkWidget *due_label;
	GtkWidget *due_entry;
	GtkWidget *comment_box;
	GtkWidget *comment_label;
	GtkWidget *comment_text;
	GtkWidget *comment_internal_box;
	GtkWidget *comment_sep;
	GtkWidget *w;
	GtkWidget *pri_box;
	GtkWidget *pri_label;
	GtkWidget *pri_spin;
	GtkObject *pri_adj;

	GtkWidget *entry;

	dialog = gnome_dialog_new (ico->new ? _("Create to-do item") : _("Edit to-do item"),
				   GNOME_STOCK_BUTTON_OK,
				   GNOME_STOCK_BUTTON_CANCEL,
				   NULL);
	gnome_dialog_set_parent (GNOME_DIALOG (dialog),
	  GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (todo->calendar))));
	hbox = gtk_hbox_new (FALSE, 4);
	gtk_container_border_width (GTK_CONTAINER (hbox), 4);
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
			    hbox, FALSE, FALSE, 0);
	gtk_widget_show (hbox);


	due_box = gtk_hbox_new (FALSE, 4);
	gtk_container_border_width (GTK_CONTAINER (due_box), 4);
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), due_box, FALSE, FALSE, 0);
	gtk_widget_show (due_box);

	pri_box = gtk_hbox_new (FALSE, 4);
	gtk_container_border_width (GTK_CONTAINER (pri_box), 4);
	gtk_box_pack_start(GTK_BOX (GNOME_DIALOG (dialog)->vbox), pri_box, FALSE, FALSE, 0);
	gtk_widget_show (pri_box);
	
	comment_box = gtk_hbox_new (FALSE, 4);
	gtk_container_border_width (GTK_CONTAINER (comment_box), 4);
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), comment_box, FALSE, FALSE, 0);
	gtk_widget_show (comment_box);

	comment_internal_box = gtk_vbox_new(FALSE,2);
	gtk_container_border_width (GTK_CONTAINER (comment_internal_box), 4);

	gtk_box_pack_start (GTK_BOX (comment_box), comment_internal_box, TRUE, TRUE, 0);
	gtk_widget_show (comment_internal_box);

	w = gtk_label_new (_("Summary:"));
	gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
	gtk_widget_show (w);

	entry = gtk_entry_new ();
	gtk_entry_set_text (GTK_ENTRY (entry), ico->summary);
	gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
	gtk_widget_show (entry);


	due_label = gtk_label_new (_("Due Date:"));
	gtk_box_pack_start (GTK_BOX (due_box), due_label, FALSE, FALSE, 0);
	gtk_widget_show (due_label);

	due_entry = gtk_entry_new ();
	due_entry = date_edit_new (ico->dtend, TRUE);
	gtk_box_pack_start (GTK_BOX (due_box), due_entry, TRUE, TRUE, 0);
	gtk_widget_show (due_entry);

	pri_label = gtk_label_new (_("Priority:"));
	gtk_box_pack_start (GTK_BOX (pri_box), pri_label, FALSE, FALSE, 0);
	gtk_widget_show (pri_label);

	pri_adj = gtk_adjustment_new (5.0, 1.0, 9.0, 1.0, 3.0, 0.0);
	pri_spin = gtk_spin_button_new (GTK_ADJUSTMENT(pri_adj), 0.0, 0);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (pri_spin), TRUE);
	gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (pri_spin), FALSE);
	gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (pri_spin), FALSE);
	gtk_box_pack_start (GTK_BOX (pri_box), pri_spin, FALSE, FALSE, 0);
	gtk_widget_show (pri_spin);
       
	comment_sep = gtk_hseparator_new ();
	gtk_box_pack_start (GTK_BOX (comment_box), comment_sep, FALSE, FALSE, 0);
	gtk_widget_show(comment_sep);

	comment_label = gtk_label_new (_("Item Comments:"));
	gtk_label_set_justify(GTK_LABEL(comment_label), GTK_JUSTIFY_LEFT);
	gtk_box_pack_start (GTK_BOX (comment_internal_box), comment_label, TRUE, TRUE, 0);
	gtk_widget_show (comment_label);




	comment_text = gtk_text_new (NULL, NULL);
	gtk_text_set_editable (GTK_TEXT (comment_text), TRUE);
	gtk_text_set_word_wrap( GTK_TEXT(comment_text), TRUE);
	gtk_text_freeze(GTK_TEXT(comment_text));
	if(ico->comment) {
	  gtk_text_insert(GTK_TEXT(comment_text), NULL, NULL, NULL, ico->comment, strlen(ico->comment));
	}
	gtk_text_thaw(GTK_TEXT(comment_text));
	gtk_box_pack_start (GTK_BOX (comment_internal_box), comment_text, FALSE, TRUE, 0);
	gtk_widget_show (comment_text);

	ico->user_data = dialog;

	gtk_object_set_user_data (GTK_OBJECT (dialog), ico);
	ical_object_ref (ico);

	gtk_object_set_data (GTK_OBJECT (dialog), "gncal_todo", todo);
	gtk_object_set_data (GTK_OBJECT (dialog), "summary_entry", entry);
	gtk_object_set_data (GTK_OBJECT (dialog), "due_date", due_entry);
	gtk_object_set_data (GTK_OBJECT (dialog), "priority", pri_spin);
	gtk_object_set_data (GTK_OBJECT (dialog), "comment", comment_text);
	
	gnome_dialog_button_connect (GNOME_DIALOG (dialog), 0, (GtkSignalFunc) ok_button, dialog);
	gnome_dialog_button_connect (GNOME_DIALOG (dialog), 1, (GtkSignalFunc) cancel_button, dialog);

	gtk_signal_connect (GTK_OBJECT (dialog), "delete_event",
			    (GtkSignalFunc) delete_event,
			    dialog);

	gnome_dialog_set_default (GNOME_DIALOG (dialog), 0);
	gnome_dialog_editable_enters (GNOME_DIALOG (dialog), GTK_EDITABLE(entry));

	gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
	gtk_widget_show (dialog);
	gtk_widget_grab_focus (entry);
}

static iCalObject *
get_clist_selected_ico (GtkCList *clist)
{
	gint sel;

	if (!clist->selection)
		return NULL;

	sel = GPOINTER_TO_INT(clist->selection->data);

	return gtk_clist_get_row_data (clist, sel);
}

static void
add_todo (GncalTodo *todo)
{
	iCalObject *ico;

	ico = ical_new ("", user_name, "");
	ico->type = ICAL_TODO;
	ico->new = TRUE;

	simple_todo_editor (todo, ico);
	ical_object_unref (ico);
}

static void
edit_todo (GncalTodo *todo)
{
	simple_todo_editor (todo, get_clist_selected_ico (todo->clist));
}

static void
delete_todo (GncalTodo *todo)
{
	gnome_calendar_remove_object (todo->calendar, get_clist_selected_ico (todo->clist));
}

static void
add_activated (GtkWidget *widget, GncalTodo *todo)
{
	GtkWidget *w;

	while ((w = gtk_grab_get_current ()) != NULL)
		gtk_grab_remove (w);
	
	add_todo (todo);
}

static void
edit_activated (GtkWidget *widget, GncalTodo *todo)
{
	GtkWidget *w;

	while ((w = gtk_grab_get_current ()) != NULL)
		gtk_grab_remove (w);
	
	edit_todo (todo);
}

static void
delete_activated (GtkWidget *widget, GncalTodo *todo)
{
	delete_todo (todo);
}


static void
clist_row_selected (GtkCList *clist, gint row, gint column, GdkEventButton *event, GncalTodo *todo)
{
	static struct menu_item items[] = {
		{ N_("Add to-do item..."), (GtkSignalFunc) add_activated, NULL, TRUE },
		{ N_("Edit this item..."), (GtkSignalFunc) edit_activated, NULL, TRUE },
		{ N_("Delete this item"), (GtkSignalFunc) delete_activated, NULL, TRUE }
	};

	int i;

	gtk_widget_set_sensitive (todo->edit_button, (todo->clist->selection != NULL));
	gtk_widget_set_sensitive (todo->delete_button, (todo->clist->selection != NULL));

	if (!event)
		return;

	switch (event->button) {
	case 1:
		if (event->type == GDK_2BUTTON_PRESS)
			edit_todo (todo);
		break;

	case 3:
		for (i = 0; i < (sizeof (items) / sizeof (items[0])); i++)
			items[i].data = todo;

		popup_menu (items, sizeof (items) / sizeof (items[0]), event);
		break;

	default:
		break;
	}
}

/*
 * once we get a call back stating that a column
 * has been resized never ever automatically resize again
 */
static void
column_resized (GtkWidget *widget, GncalTodo *todo)
{
	/* disabling autoresize of columns */
	if (todo_list_autoresize && !todo_list_redraw_in_progess){
		todo_list_autoresize = 0;
	}
}

/*
 * restore the previously set settings for sorting the 
 * todo list
 */
static void
init_column_sorting (GtkCList *clist)
{

	/* due date isn't shown so we can't sort by it */
	if (todo_current_sort_column == 1 && ! todo_show_due_date) 
		todo_current_sort_column = 0;
	
	clist->sort_type = todo_current_sort_type;
	clist->sort_column = todo_current_sort_column;
	
	gtk_clist_set_sort_column (clist, todo_current_sort_column);
	gtk_clist_sort (clist);
}

static void 
todo_click_column (GtkCList *clist, gint column, gpointer data)
{
	if (column == clist->sort_column)
	{
		if (clist->sort_type == GTK_SORT_ASCENDING) {
			clist->sort_type = GTK_SORT_DESCENDING;
			todo_current_sort_type = GTK_SORT_DESCENDING;
		} else {
			clist->sort_type = GTK_SORT_ASCENDING;
			todo_current_sort_type = GTK_SORT_ASCENDING;
		}
	}
	else {
		gtk_clist_set_sort_column (clist, column);
		todo_current_sort_column = column;
	}
	
	gtk_clist_sort (clist); 
  
	/*
	 * save the sorting preferences cause I hate to have the user
	 * click twice
	 */

	gnome_config_set_int("/calendar/Todo/sort_column", todo_current_sort_column);
	gnome_config_set_int("/calendar/Todo/sort_type", todo_current_sort_type);
	gnome_config_sync();
}

static void
gncal_todo_init (GncalTodo *todo)
{
	GtkWidget *w;
	GtkWidget *sw;
	GtkWidget *hbox;
	gchar *titles[4] = {
	    N_("Summary"),
	    N_("Due Date"),
	    N_("Priority"),
	    N_("Time Left")
	};
	char *tmp[4];
	tmp[0] = _(titles[0]);
	tmp[1] = _(titles[1]);
	tmp[2] = _(titles[2]);
	tmp[3] = _(titles[3]);

	gtk_box_set_spacing (GTK_BOX (todo), 4);

	/* Label */

	w = gtk_label_new (_("To-do list"));
	gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
	gtk_box_pack_start (GTK_BOX (todo), w, FALSE, FALSE, 0);
	gtk_widget_show (w);

	/* Clist */

	sw = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_box_pack_start (GTK_BOX (todo), sw, TRUE, TRUE, 0);
	gtk_widget_show (sw);


	w = gtk_clist_new_with_titles(4, tmp);

	todo->clist = GTK_CLIST (w);
	gtk_clist_set_selection_mode (todo->clist, GTK_SELECTION_BROWSE);

	gtk_signal_connect (GTK_OBJECT (todo->clist), "select_row",
			    (GtkSignalFunc) clist_row_selected,
			    todo);
	gtk_clist_set_button_actions (todo->clist, 2, GTK_BUTTON_SELECTS);
	gtk_signal_connect (GTK_OBJECT (todo->clist), "resize_column",
			    (GtkSignalFunc) column_resized,
			    todo);
	gtk_signal_connect (GTK_OBJECT (todo->clist), "click_column",
			  (GtkSignalFunc) todo_click_column, NULL);

	gtk_container_add (GTK_CONTAINER (sw), w);
	gtk_widget_show (w);

	/* Box for buttons */

	hbox = gtk_hbox_new (TRUE, 4);
	gtk_box_pack_start (GTK_BOX (todo), hbox, FALSE, FALSE, 0);
	gtk_widget_show (hbox);

	/* Add */

	w = gtk_button_new_with_label (_("Add..."));
	gtk_signal_connect (GTK_OBJECT (w), "clicked",
			    (GtkSignalFunc) add_activated,
			    todo);
	gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
	gtk_widget_show (w);

	/* Edit */

	w = gtk_button_new_with_label (_("Edit..."));
	todo->edit_button = w;
	gtk_widget_set_sensitive (w, FALSE);
	gtk_signal_connect (GTK_OBJECT (w), "clicked",
			    (GtkSignalFunc) edit_activated,
			    todo);
	gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
	gtk_widget_show (w);

	/* Delete */

	w = gtk_button_new_with_label (_("Delete"));
	todo->delete_button = w;
	gtk_widget_set_sensitive (w, FALSE);
	gtk_signal_connect (GTK_OBJECT (w), "clicked",
			    (GtkSignalFunc) delete_activated,
			    todo);
	gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
	gtk_widget_show (w);
}

GtkWidget *
gncal_todo_new (GnomeCalendar *calendar)
{
	GncalTodo *todo;

	g_return_val_if_fail (calendar != NULL, NULL);

	todo = gtk_type_new (gncal_todo_get_type ());

	todo->calendar = calendar;

	gncal_todo_update (todo, NULL, 0);

	return GTK_WIDGET (todo);
}

static char *
convert_time_t_to_char (time_t t)
{
	char buf[100];
	struct tm tm;

	tm = *localtime (&t);
	strftime(buf, sizeof (buf), "%m/%d/%Y", &tm);

	return g_strdup (buf);
}


enum todo_styles {
	TODO_STYLE_OVERDUE,
	TODO_STYLE_DUE_TODAY,
	TODO_STYLE_NOT_DUE
};


enum todo_status {
	TODO_ITEM_DSTATUS_NOT_DUE_YET,
	TODO_ITEM_DSTATUS_DUE_TODAY,
	TODO_ITEM_DSTATUS_OVERDUE,
	TODO_ITEM_DSTATUS_LAST_DUE_STATUS
};
typedef enum todo_status todo_status;

static GtkStyle *
make_todo_style(GncalTodo *todo, todo_status style_type) 
{
	GtkStyle *style = NULL;
	GdkColor style_color;
	int color_prop = 0;
	switch(style_type) {
	case TODO_ITEM_DSTATUS_NOT_DUE_YET:
	  color_prop = COLOR_PROP_TODO_NOT_DUE_YET;
	  break;
	case TODO_ITEM_DSTATUS_DUE_TODAY:
	  color_prop = COLOR_PROP_TODO_DUE_TODAY;
	  break;
	case TODO_ITEM_DSTATUS_OVERDUE:
	  color_prop = COLOR_PROP_TODO_OVERDUE;
	  break;
	case TODO_ITEM_DSTATUS_LAST_DUE_STATUS:
	}
	
	style_color.red   = color_props[color_prop].r;
	style_color.green = color_props[color_prop].g;
	style_color.blue  = color_props[color_prop].b;
	
	style = gtk_style_copy (GTK_WIDGET (todo->clist)->style);
	style->base[GTK_STATE_NORMAL] = style_color;
	return style;
}


static
todo_status todo_item_due_status(time_t *todo_due_time) {
	struct tm due_tm_time;
	struct tm current_time;
	struct tm *temp_tm;
	time_t current_time_val = time(NULL);
	temp_tm = localtime(todo_due_time);
	/* make a copy so it dosen't get over written */
	memcpy(&due_tm_time, temp_tm, sizeof(struct tm));
	
	
	temp_tm = localtime(&current_time_val);
	memcpy(&current_time, temp_tm, sizeof(struct tm));
	
	if(due_tm_time.tm_mon == current_time.tm_mon &&
	   due_tm_time.tm_mday == current_time.tm_mday &&
	   due_tm_time.tm_year == current_time.tm_year) {
		return TODO_ITEM_DSTATUS_DUE_TODAY;
	}
	
	if((*todo_due_time) < current_time_val) {
		return TODO_ITEM_DSTATUS_OVERDUE;
	}
	
	return TODO_ITEM_DSTATUS_NOT_DUE_YET;
}


enum todo_remaining_time_form {
	TODO_ITEM_REMAINING_WEEKS,
	TODO_ITEM_REMAINING_DAYS,
	TODO_ITEM_REMAINING_HOURS,
	TODO_ITEM_REMAINING_MINUTES,
	TODO_ITEM_REMAINING_SECONDS
};
typedef enum todo_remaining_time_form todo_remaining_time_form;


static void
insert_in_clist (GncalTodo *todo, iCalObject *ico)
{
	int i;
	char *text[4];
	char time_remaining_buffer[100];
	time_t time_remain;
	todo_remaining_time_form time_remaining_form;
	int sec_in_week = 3600*7*24;
	int sec_in_day = 3600*24;
	int sec_in_hour = 3600;
	int sec_in_minute = 60;
	int weeks = 0;
	int days = 0;
	int hours = 0;
	int minutes = 0;
	int seconds = 0; 
	
	
	/* an array for the styles of items */
	static GtkStyle *dstatus_styles[TODO_ITEM_DSTATUS_LAST_DUE_STATUS];
	/* we want to remake the styles when the status is changed,
	   also we need to check for the null value in the pointer so we init them
	   at startup */
	if (todo_style_changed || !dstatus_styles[TODO_ITEM_DSTATUS_NOT_DUE_YET]) {
		g_free(dstatus_styles[TODO_ITEM_DSTATUS_NOT_DUE_YET]);
		g_free(dstatus_styles[TODO_ITEM_DSTATUS_OVERDUE]);
		g_free(dstatus_styles[TODO_ITEM_DSTATUS_DUE_TODAY]);
		
		dstatus_styles[TODO_ITEM_DSTATUS_NOT_DUE_YET] = make_todo_style(todo, TODO_ITEM_DSTATUS_NOT_DUE_YET);
		dstatus_styles[TODO_ITEM_DSTATUS_OVERDUE] = make_todo_style(todo, TODO_ITEM_DSTATUS_OVERDUE);
		dstatus_styles[TODO_ITEM_DSTATUS_DUE_TODAY] = make_todo_style(todo, TODO_ITEM_DSTATUS_DUE_TODAY);
		
		todo_style_changed = 0;
	}

	  
       


	text[0] =  ico->summary;

	if(todo_show_time_remaining) {
	  memset(time_remaining_buffer, 0, 100);
	  /* we need to make a string that represents the amount of time remaining
	     before this task is due */
	  
	  /* for right now all I'll do is up to the hours. */
	  time_remain = (ico->dtend - time(NULL));
	  if(time_remain < 0) {
	    text[3] = "Overdue!";
	  }
	  else {

	    /* lets determine a decent denomination to display */
	    if(time_remain / (sec_in_week)) 
	      {
	    	/* we have weeks available */
	    	time_remaining_form = TODO_ITEM_REMAINING_WEEKS;
		weeks = time_remain / sec_in_week;
		days = (time_remain % (sec_in_week))/sec_in_day;
	      }
	    else if(time_remain / (sec_in_day)) 
	      {
		/* we have days available */
	    	time_remaining_form = TODO_ITEM_REMAINING_DAYS;
		days = time_remain / sec_in_day;
		hours = (time_remain % sec_in_day)/sec_in_hour;
	      }
	    else if(time_remain / (sec_in_hour)) 
	      {
		/* we have hours available */
	    	time_remaining_form = TODO_ITEM_REMAINING_HOURS;
		hours = time_remain /sec_in_hour;
		minutes = (time_remain % sec_in_hour) / sec_in_minute;
	      }
	    else if(time_remain / sec_in_minute) 
	      {
	    	time_remaining_form = TODO_ITEM_REMAINING_MINUTES;
		minutes = time_remain / sec_in_minute;
		seconds = time_remain % sec_in_minute;
	      }
	    else 
	      {
	    	time_remaining_form = TODO_ITEM_REMAINING_SECONDS;
		seconds = time_remain;
	      }

	    switch(time_remaining_form) 
	      {
	      case TODO_ITEM_REMAINING_WEEKS:
		snprintf(time_remaining_buffer, 100, "%d %s %d %s", weeks,
			 (weeks > 1) ? _("Weeks") : _("Week"),
			 days, (days > 1) ? _("Days") : _("Day"));
		break;
	      case TODO_ITEM_REMAINING_DAYS:
		snprintf(time_remaining_buffer, 100, "%d %s %d %s", days,
			 (days > 1) ? _("Days") : _("Day"),
			 hours, (hours > 1) ? _("Hours") : _("Hour"));
		break;
	      case TODO_ITEM_REMAINING_HOURS:
		snprintf(time_remaining_buffer, 100, "%d %s %d %s", hours,
			 (hours > 1) ? _("Hours") : _("Hour"),
			 minutes, (minutes > 1) ? _("Minutes") : _("Minute"));
		break;
	      case TODO_ITEM_REMAINING_MINUTES:
		snprintf(time_remaining_buffer, 100, "%d %s %d %s", minutes,
			 (minutes > 1) ? _("Minutes") : _("Minute"),
			 seconds, (seconds > 1) ? _("Seconds") : _("Second"));
		break;
	      case TODO_ITEM_REMAINING_SECONDS:
		snprintf(time_remaining_buffer, 100, "%d %s", seconds,
			 (seconds > 1) ? _("Seconds") : _("Second")); 
		break;
	      }
	    text[3] = g_strdup(time_remaining_buffer);
	    todo->data_ptrs = g_slist_append(todo->data_ptrs, text[3]);
	  }

	}
	else {
	  text[3] = "Loose penguini!";
	}
	/*
	 * right now column 0 will be the summary
	 * and column 1 will be the due date. 
	 * WISH:  this should be able to be changed on the fly
	 */

	if(ico->dtend && todo_show_due_date)
	  {
	    text[1] = convert_time_t_to_char (ico->dtend);
	    /* Append the data's pointer so later it can be properly freed */
	    todo->data_ptrs = g_slist_append (todo->data_ptrs, text[1]);
	  }
	else
		text[1] = NULL;
	
	if(ico->priority && todo_show_priority)
	  {
	    text[2] = g_strdup_printf ("%d", ico->priority);
	    todo->data_ptrs = g_slist_append (todo->data_ptrs, text[2]);
	  }
	else
	  text[2] = NULL;

	i = gtk_clist_append (todo->clist, text);
	
	gtk_clist_set_row_data_full (todo->clist, i, ico,
				     (GtkDestroyNotify) ical_object_unref);
	ical_object_ref (ico);

	/*
	 * determine if the task is overdue..
	 * if so mark with the apropriate style
	 */

	switch(todo_item_due_status(&ico->dtend)) {
	case TODO_ITEM_DSTATUS_NOT_DUE_YET:
	  if(todo_item_dstatus_highlight_not_due_yet) 
	    {
	      gtk_clist_set_row_style(todo->clist, i, dstatus_styles[TODO_ITEM_DSTATUS_NOT_DUE_YET]);
	    }
	  break;
	case TODO_ITEM_DSTATUS_DUE_TODAY:
	  if(todo_item_dstatus_highlight_due_today)
	    {
	      gtk_clist_set_row_style(todo->clist, i, dstatus_styles[TODO_ITEM_DSTATUS_DUE_TODAY]);
	    }
	  break;
	case TODO_ITEM_DSTATUS_OVERDUE:
	  if(todo_item_dstatus_highlight_overdue) 
	    {
	      gtk_clist_set_row_style(todo->clist, i, dstatus_styles[TODO_ITEM_DSTATUS_OVERDUE]);
	    }
	  break;
	case TODO_ITEM_DSTATUS_LAST_DUE_STATUS:
	}
	
	/* keep the list in order */
	gtk_clist_sort (todo->clist); 
}


void
gncal_todo_update (GncalTodo *todo, iCalObject *ico, int flags)
{
	GSList *current_list;

	CalObjFindStatus st;
	GList *l, *uids;

	g_return_if_fail (todo != NULL);
	g_return_if_fail (GNCAL_IS_TODO (todo));
	
	/*
	 * shut down the resize handler cause we are playing with the list. 
	 * In otherwords turn off the event handler
	 */
	todo_list_redraw_in_progess =1;
	
	/* freeze the list */
	gtk_clist_freeze (todo->clist);
	init_column_sorting (todo->clist);

	/*
	 * before here we have to free some of the memory that
	 * stores the due date, or else we have a memory leak. 
	 * luckily all of the pointers are stored in todo->data_ptrs;
	 */

	/* check on the columns that we should display */
	/* check for due date */

	if(todo_show_due_date) {
	  gtk_clist_set_column_visibility (todo->clist, 1, 1);
	}
	else {
	  gtk_clist_set_column_visibility (todo->clist, 1, 0);
	}

	if(todo_show_time_remaining) {
	  gtk_clist_set_column_visibility (todo->clist, 3, 1);
	}
	else {
	  gtk_clist_set_column_visibility (todo->clist, 3, 0);
	}
				       

	if(todo_show_priority)
	  gtk_clist_set_column_visibility (todo->clist, 2, 1);
	else
	  gtk_clist_set_column_visibility (todo->clist, 2, 0);
	
	/* free the memory locations that were used in the previous display */
	for (current_list = todo->data_ptrs;
	     current_list != NULL;
	     current_list = g_slist_next(current_list)){
		g_free(current_list->data);
	}

	/* free the list and clear out the pointer */
	g_slist_free(todo->data_ptrs);
	todo->data_ptrs = NULL;

	gtk_clist_clear (todo->clist);


	uids = cal_client_get_uids (todo->calendar->client,
				    CALOBJ_TYPE_TODO);
	for (l = uids; l; l = l->next){
		char *uid = l->data;
		char *obj_string =
			cal_client_get_object (todo->calendar->client, uid);
		iCalObject *obj = NULL;
			
		st = ical_object_find_in_string (uid, obj_string, &obj);
		g_free (obj_string);

		insert_in_clist (todo, obj);
		ical_object_unref (obj);
		g_free (uid);
	}
	g_list_free (uids);

	/* if we are autoresizing then do it now */
	if(todo_list_autoresize && todo->clist->rows != 0) 
		gtk_clist_columns_autosize (todo->clist);

	gtk_clist_thaw (todo->clist);
	
	gtk_widget_set_sensitive (todo->edit_button,
				  (todo->clist->selection != NULL));
	gtk_widget_set_sensitive (todo->delete_button,
				  (todo->clist->selection != NULL));
	todo_list_redraw_in_progess = 0;
}