/*  gnutrition - a nutrition and diet analysis program.
 *  Copyright(C) 2000, 2001 Edgar Denny(e.denny@ic.ac.uk)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gnome.h>
#include <glade/glade.h>
#include <ctype.h>

#include "text_util.h"
#include "food.h"
#include "recipe.h"
#include "base_win.h"
#include "plan.h"
#include "load_data.h"
#include "plan_win.h"
#include "recipe_win.h"
#include "base_win.h"
#include "plan_add_dlg.h"
#include "food_srch_dlg.h"
#include "nutr_goal_dlg_ui.h"
#include "edit_food_dlg.h"
#include "edit_recipe_dlg.h"
#include "init_database.h"
#include "wrap_mysql.h"
#include "plan_compute_dlg.h"

#define TM_YEAR_BASE 1900

typedef struct Plan_s {
	GtkWidget *calendar;
	GtkWidget *calendar_win;
	GtkWidget *menubar_container;
	GtkWidget *toolbar_container;
	GtkWidget *table_container;
	GtkWidget *appbar_container;

	GnomeAppBar *appbar;

	GtkCList *clist;

	GtkEntry *date_entry;

	GtkButton *save_button;
	GtkButton *add_button;
	GtkButton *delete_button;
	GtkButton *edit_button;
	GtkButton *compute_button;
	GtkButton *date_button;

	GtkMenuItem *save;
	GtkMenuItem *exit;
	GtkMenuItem *add;
	GtkMenuItem *delete;
	GtkMenuItem *edit;
	GtkMenuItem *meal_plan;
	GtkMenuItem *recipe;
	GtkMenuItem *food;
	GtkMenuItem *preferences;
	GtkMenuItem *about;

	int selected_row;
} Plan_t;

static GladeXML *xml = NULL;

static void load_xml( void);
static void connect_signals( Plan_t *);
static void initialize_plan_win( Plan_t *);
static void set_times( Plan_t *);
static Plan_t *create_plan_view( void);
static Plan_t *get_plan_view( void);

/* private callbacks. */
static void on_add_button_released( GtkButton *, gpointer);
static void on_save_button_released( GtkButton *, gpointer);
static void on_delete_button_released( GtkButton *, gpointer);
static void on_edit_button_released( GtkButton *, gpointer);
static void on_compute_button_released( GtkButton *, gpointer);
static void on_add_button_enter( GtkButton *, gpointer);
static void on_save_button_enter( GtkButton *, gpointer);
static void on_delete_button_enter( GtkButton *, gpointer);
static void on_edit_button_enter( GtkButton *, gpointer);
static void on_compute_button_enter( GtkButton *, gpointer);
static void on_add_button_leave( GtkButton *, gpointer);
static void on_save_button_leave( GtkButton *, gpointer);
static void on_delete_button_leave( GtkButton *, gpointer);
static void on_edit_button_leave( GtkButton *, gpointer);
static void on_compute_button_leave( GtkButton *, gpointer);
static void on_clist_select_row( GtkCList *, int, int, GdkEvent *, gpointer);
static void on_date_button_released( GtkButton *, gpointer);
static void on_day_selected( GtkCalendar *, gpointer);
static void on_save_activate( GtkMenuItem *, gpointer);
static void on_exit_activate( GtkMenuItem *, gpointer);
static void on_add_activate( GtkMenuItem *, gpointer);
static void on_delete_activate( GtkMenuItem *, gpointer);
static void on_edit_activate( GtkMenuItem *, gpointer);
static void on_meal_plan_activate( GtkMenuItem *, gpointer);
static void on_recipe_activate( GtkMenuItem *, gpointer);
static void on_food_activate( GtkMenuItem *, gpointer);
static void on_preferences_activate( GtkMenuItem *, gpointer);
static void on_about_activate( GtkMenuItem *, gpointer);

static Plan_t *
create_plan_view()
{
	static Plan_t *plan = NULL;

	if ( !xml) load_xml();

	if ( !plan) {
		plan = (Plan_t *)g_malloc( sizeof( Plan_t));

		plan->calendar = glade_xml_get_widget( xml, "calendar");
		plan->calendar_win = glade_xml_get_widget( xml, "calendar_win");
		plan->menubar_container = glade_xml_get_widget( xml,
			"menubar_container");
		plan->toolbar_container = glade_xml_get_widget( xml,
			"toolbar_container");
		plan->table_container = glade_xml_get_widget( xml,
			"table_container");
		plan->appbar_container = glade_xml_get_widget( xml,
			"appbar_container");

		plan->appbar = GNOME_APPBAR( glade_xml_get_widget( xml,
			"appbar"));

		plan->clist = (GtkCList *)glade_xml_get_widget( xml, "clist");

		plan->date_entry = GTK_ENTRY( glade_xml_get_widget( xml,
			"date_entry"));

		plan->save_button = GTK_BUTTON( glade_xml_get_widget( xml,
			"save_button"));
		plan->add_button = GTK_BUTTON( glade_xml_get_widget( xml,
			"add_button"));
		plan->delete_button = GTK_BUTTON( glade_xml_get_widget( xml,
			"delete_button"));
		plan->edit_button = GTK_BUTTON( glade_xml_get_widget( xml,
			"edit_button"));
		plan->compute_button = GTK_BUTTON( glade_xml_get_widget( xml,
			"compute_button"));
		plan->date_button = GTK_BUTTON( glade_xml_get_widget( xml,
			"date_button"));

		plan->save = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"save"));
		plan->exit = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"exit"));
		plan->add = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"add"));
		plan->delete = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"delete"));
		plan->edit = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"edit"));
		plan->meal_plan = GTK_MENU_ITEM( glade_xml_get_widget( xml, 
			"meal_plan"));
		plan->recipe = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"recipe"));
		plan->food = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"food"));
		plan->preferences = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"preferences"));
		plan->about = GTK_MENU_ITEM( glade_xml_get_widget( xml,
			"about"));

		plan->selected_row = -1;
	}
	return plan;
}

static Plan_t *
get_plan_view()
{
	return create_plan_view();
}

/* set times in the clist. */
static void
set_times( Plan_t *plan)
{
	GdkColor color;
	int i, j;
	char *text[4];

	color.red = 50000;
	color.green = 50000;
	color.blue = 50000;

	/* add the times of day on alternate lines of clist. */
	for (i=0; i<24; i++) {
		text[0] = g_strconcat( itoa( i), ":00", NULL);
		for (j=1;j<4;j++) text[j] = g_strdup( "");
		gtk_clist_append( plan->clist, text);
		for (j=0; j<4; j++) g_free(text[j]);

		for (j=0;j<4;j++) text[j] = g_strdup( "");
		gtk_clist_append( plan->clist, text);
		for (j=0; j<4; j++) g_free( text[j]);
	}

	/* set the background colours for the rows. */
	for (i=0; i<48; i++) {
		if (i < 12 || i > 43) 
			gtk_clist_set_background( plan->clist, i, &color);
	}
}

void
gnutr_set_plan_clist_position()
{
	Plan_t *plan;

	plan = get_plan_view();
	gtk_clist_moveto( plan->clist, 11, 0, 0.0, 1.0);
}

/* initialize the plan window. */
static void
initialize_plan_win( Plan_t *plan)
{
	char *query, *date = NULL;

	set_times( plan);

	/* get today's date. */
	query = g_strdup( "SELECT CURDATE()");
	date = single_ret_val_query_db( query);
	g_assert( date);
	gtk_entry_set_text( GTK_ENTRY( plan->date_entry), date);
	g_free( query);
	g_free( date);

	gnutr_update_plan_win();
	gtk_clist_moveto( plan->clist, 11, 0, 0.0, 1.0);
}

/* load the glade xml if not already loaded. */
static void
load_xml()
{
	static gboolean loaded_xml = FALSE;

	/* load the glade interface. */
	if ( !loaded_xml) {
		xml = glade_xml_new(
			GNUTRITION_GLADEDIR "/plan_win.glade", NULL);
		loaded_xml = TRUE;

		if ( xml) {
			Plan_t *plan;

			plan = create_plan_view();
			connect_signals( plan);
			initialize_plan_win( plan);
		} else {
			g_log( "Gnutrition", G_LOG_LEVEL_ERROR,
				"cannot load glade file: plan_win.glade\n");
			return;
		}
	}
}

/* connect the signals. */
static void
connect_signals( Plan_t *plan)
{
	/* toolbar button release callbacks. */
	gtk_signal_connect( GTK_OBJECT( plan->save_button), "released", 
		GTK_SIGNAL_FUNC( on_save_button_released), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->save_button), "enter", 
		GTK_SIGNAL_FUNC( on_save_button_enter), (gpointer)plan);
	gtk_signal_connect(GTK_OBJECT( plan->save_button), "leave", 
		GTK_SIGNAL_FUNC( on_save_button_leave), (gpointer)plan);

	gtk_signal_connect( GTK_OBJECT( plan->add_button), "released", 
		GTK_SIGNAL_FUNC( on_add_button_released), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->add_button), "enter", 
		GTK_SIGNAL_FUNC( on_add_button_enter), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->add_button), "leave", 
		GTK_SIGNAL_FUNC( on_add_button_leave), (gpointer)plan);

	gtk_signal_connect( GTK_OBJECT( plan->delete_button), "released", 
		GTK_SIGNAL_FUNC( on_delete_button_released), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->delete_button), "enter", 
		GTK_SIGNAL_FUNC( on_delete_button_enter), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->delete_button), "leave", 
		GTK_SIGNAL_FUNC( on_delete_button_leave), (gpointer)plan);

	gtk_signal_connect( GTK_OBJECT( plan->edit_button), "released", 
		GTK_SIGNAL_FUNC( on_edit_button_released), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->edit_button), "enter", 
		GTK_SIGNAL_FUNC( on_edit_button_enter), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->edit_button), "leave", 
		GTK_SIGNAL_FUNC( on_edit_button_leave), (gpointer)plan);

	gtk_signal_connect( GTK_OBJECT( plan->compute_button), "released", 
		GTK_SIGNAL_FUNC( on_compute_button_released), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->compute_button), "enter", 
		GTK_SIGNAL_FUNC( on_compute_button_enter), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->compute_button), "leave", 
		GTK_SIGNAL_FUNC( on_compute_button_leave), (gpointer)plan);

	/* menubar items. */
	gtk_signal_connect( GTK_OBJECT( plan->save), "activate", 
		GTK_SIGNAL_FUNC( on_save_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->exit), "activate", 
		GTK_SIGNAL_FUNC( on_exit_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->add), "activate", 
		GTK_SIGNAL_FUNC( on_add_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->delete), "activate", 
		GTK_SIGNAL_FUNC( on_delete_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->edit), "activate", 
		GTK_SIGNAL_FUNC( on_edit_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->meal_plan), "activate", 
		GTK_SIGNAL_FUNC( on_meal_plan_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->recipe), "activate", 
		GTK_SIGNAL_FUNC( on_recipe_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->food), "activate", 
		GTK_SIGNAL_FUNC( on_food_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->preferences), "activate", 
		GTK_SIGNAL_FUNC( on_preferences_activate), (gpointer)plan);
	gtk_signal_connect( GTK_OBJECT( plan->about), "activate", 
		GTK_SIGNAL_FUNC( on_about_activate), (gpointer)plan);

	/* the remaining callbacks. */
	gtk_signal_connect( GTK_OBJECT( plan->clist), "select_row", 
		GTK_SIGNAL_FUNC( on_clist_select_row), (gpointer)plan);

	gtk_signal_connect( GTK_OBJECT( plan->date_button), "released", 
		GTK_SIGNAL_FUNC( on_date_button_released), (gpointer)plan);

	gtk_signal_connect( GTK_OBJECT( plan->calendar), "day_selected", 
		GTK_SIGNAL_FUNC( on_day_selected), (gpointer)plan);
}

/* callback when the "Add" button is clicked on the toolbar. */
static void
on_add_button_released( GtkButton *button,
                        gpointer data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	/* check that a row in the clist has been selected. */
	if ( plan->selected_row == -1) {
		GtkWidget *warn_dlg;
		warn_dlg = gnome_warning_dialog( "No time has been selected.");
		gtk_widget_show( warn_dlg);
		return;
	}
	gnutr_show_plan_add_dlg();
}

/* callback when a row of the clist is selected. */
static void
on_clist_select_row( GtkCList *clist,
                     int      row,
                     int      column,
                     GdkEvent *event,
                     gpointer  data)
{
	Plan_t *plan;
	char *date_text;
		
	date_text = gnutr_get_date();
	if( !date_text) return;

	/* check that a date has been selected. */
	if( strcmp( date_text, "") == 0) {
		GtkWidget *warn_dlg;
		char *text = g_strconcat( "You must select a date\n",
			"before you can select a time.", NULL);
		warn_dlg = gnome_warning_dialog( text);
		gtk_widget_show( warn_dlg);
		g_free( text);
		return;
	}
	plan = (Plan_t *)data;
	plan->selected_row = row;
}

/* return the menubar widget. */
GtkWidget *
gnutr_get_plan_win_menubar()
{
	Plan_t *plan;

	plan = get_plan_view();
	return plan->menubar_container;
}

/* return the toolbar widget. */
GtkWidget *
gnutr_get_plan_win_toolbar()
{
	Plan_t *plan;

	plan = get_plan_view();
	return plan->toolbar_container;
}

/* return the main table widget. */
GtkWidget *
gnutr_get_plan_win_table()
{
	Plan_t *plan;

	plan = get_plan_view();
	return plan->table_container;
}

/* return the appbar widget. */
GtkWidget *
gnutr_get_plan_win_appbar()
{
	Plan_t *plan;

	plan = get_plan_view();
	return plan->appbar_container;
}

/* Exit app when "Exit" selected from meal plan window menu bar. */
static void
on_exit_activate( GtkMenuItem *menuitem,
                  gpointer     data)
{
	gnutr_exit_app();
}

/* return the date. */
char *
gnutr_get_date()
{
	Plan_t *plan;

	plan = get_plan_view();

	return gtk_entry_get_text( plan->date_entry);
}

/* return the selected time of day. */
char *
gnutr_get_time_of_day()
{
	Plan_t *plan;
	char *text;
	char *time;
	int num = 1;

	plan = get_plan_view();

	/* FIXME: I must check that a row has infact been selected
	 * (selected_row != -1). */
	gtk_clist_get_text( plan->clist, plan->selected_row, 0, &text);

	while ( strcmp( text, "") == 0) {
		gtk_clist_get_text( plan->clist, plan->selected_row - num, 0, 
			&text);
		num++;
	}
	time = g_strconcat( text, ":00", NULL);

	return time;
}

/* update the plan win after a recipe or food has been added to it. */
void
gnutr_update_plan_win()
{
	Plan_t *plan;
	char *date;
	int i, row;
	char *text1, *text2;
	char *text3[4];
	GHashTable *htbl1, *htbl2;
	GList *ptr, *list_foods, *list_recipes;
	char **elm;
	char *time_of_day, *no_portions, *recipe_no, *recipe_name;
	char *amount, *msre_no, *msre_desc, *food_no, *food_desc;
	char *person_name;

	plan = get_plan_view();

	date = gnutr_get_date();
	person_name = get_person_name();

	gtk_clist_freeze( plan->clist);

	/* clear any existing entries in the clist. */
	gtk_clist_clear( plan->clist);

	/* FIXME: when I clear the list, the clist moves back to the
	 * top rather than staying on the current item. */

	/* FIXME: delete any hidden data pointers from the clist. */

	/* get the needed hash tables. */
	htbl1 = get_htbl_msre_no_msre_desc();
	htbl2 = get_htbl_fd_no_fd_desc();

	/* the list has been cleared. We need to add the times to the 
	 * first column. */
	set_times( plan);

	/* get the foods and the recipes for the specified date in
	 * the plan. */
	list_foods = gnutr_get_foods_for_date( date);
	list_recipes = gnutr_get_recipes_for_date( date);

	/* add the recipes to the plan win clist. */
	for (ptr = list_recipes; ptr; ptr = ptr->next) {
		elm = (char **)ptr->data;

		time_of_day = elm[0];
		no_portions = elm[1];
		recipe_no = g_strdup( elm[2]);
		recipe_name = elm[3];

		/* find the row for the time of day. */
		row = 0;
		while ( gtk_clist_get_text(plan->clist, row, 0, &text1)) {
			/* text1 is HH:MM while time_of_day is HH:MM:SS. */
			if ( row < 20 ) {
				text2 = g_strconcat( "0", text1, ":00", NULL);
			} else {
				text2 = g_strconcat( text1, ":00", NULL);
			}
			if ( strcmp( text2, time_of_day) == 0) {
				g_free( text2);
				break;
			}
			g_free( text2);
			row += 2;
		}

		/* is there already a recipe or a food in either of the two
		 * rows available for the specific time? */
		gtk_clist_get_text( plan->clist, row, 3, &text1);
		gtk_clist_get_text( plan->clist, row+1, 3, &text2);
		if ( strcmp( text1, "") == 0) {
			/* first row is empty, o.k. put it there. */
			gtk_clist_set_text( plan->clist, row, 1, no_portions);
			gtk_clist_set_text( plan->clist, row, 3, recipe_name);
			gtk_clist_set_row_data( plan->clist, row, 
				(gpointer)recipe_no);
		} else if ( strcmp( text2, "") == 0) {
			/* second row is empty, o.k., put it there. */
			gtk_clist_set_text( plan->clist, row+1, 1, no_portions);
			gtk_clist_set_text( plan->clist, row+1, 3, recipe_name);
			gtk_clist_set_row_data( plan->clist, row+1, 
				(gpointer)recipe_no);
		} else {
			/* no empty rows, create a new row. */
			text3[0] = g_strdup( "");
			text3[1] = g_strdup( no_portions);
			text3[2] = g_strdup( "");
			text3[3] = g_strdup( recipe_name);
			gtk_clist_insert( plan->clist, row+2, text3);
			gtk_clist_set_row_data( plan->clist, row+2, 
				(gpointer)recipe_no);

			for (i=0; i<4; i++) g_free( text3[i]);
		}
	}
	/* free the list. */
	if ( list_recipes) gnutr_free_row_list(list_recipes, 4);

	/* add the foods to the plan win clist. */
	for (ptr = list_foods; ptr; ptr = ptr->next) {
		elm = (char **)ptr->data;
		/* elm[0] = time, elm[1] = amount, elm[2] = msre_no, 
		 * elm[3] = fd_no. */
		time_of_day = elm[0];
		amount = elm[1];
		msre_no = elm[2];
		msre_desc = (char *)g_hash_table_lookup(htbl1, 
			(gpointer)msre_no);
		food_no = g_strdup(elm[3]);
		food_desc = (char *)g_hash_table_lookup(htbl2, 
			(gpointer)food_no);

		/* find the row for the time of day. */
		row = 0;
		while ( gtk_clist_get_text(plan->clist, row, 0, &text1)) {
			/* text1 is HH:MM while time_of_day is HH:MM:SS. */
			if ( row < 20 ) {
				text2 = g_strconcat( "0", text1, ":00", NULL);
			} else {
				text2 = g_strconcat( text1, ":00", NULL);
			}
			if ( strcmp( text2, time_of_day) == 0) {
				g_free( text2);
				break;
			}
			g_free( text2);
			row += 2;
		}

		/* is there already a recipe or a food in either of the two
		 * rows available for the specific time? */
		gtk_clist_get_text( plan->clist, row, 3, &text1);
		gtk_clist_get_text( plan->clist, row+1, 3, &text2);
		if( strcmp( text1, "") == 0) {
			/* first row is empty, o.k. put it there. */
			gtk_clist_set_text( plan->clist, row, 1, amount);
			gtk_clist_set_text( plan->clist, row, 2, msre_desc);
			gtk_clist_set_text( plan->clist, row, 3, food_desc);
			gtk_clist_set_row_data( plan->clist, row, 
				(gpointer)food_no);
		} else if( strcmp( text2, "") == 0) {
			/* second row is empty, o.k., put it there. */
			gtk_clist_set_text( plan->clist, row+1, 1, amount);
			gtk_clist_set_text( plan->clist, row+1, 2, msre_desc);
			gtk_clist_set_text( plan->clist, row+1, 3, food_desc);
			gtk_clist_set_row_data( plan->clist, row+1, 
				(gpointer)food_no);
		} else {
			/* no empty rows, create a new row. */
			text3[0] = g_strdup( "");
			text3[1] = g_strdup( amount);
			text3[2] = g_strdup( msre_desc);
			text3[3] = g_strdup( food_desc);
			gtk_clist_insert( plan->clist, row+2, text3);
			gtk_clist_set_row_data( plan->clist, row+2, 
				(gpointer)food_no);

			for (i=0; i<4; i++) g_free( text3[i]);
		}
	}

	/* free the list. */
	if ( list_foods) gnutr_free_row_list( list_foods, 4);

	/* set widths of the clist columns. */
/*	for (i=0; i<4; i++) {
		width = gtk_clist_optimal_column_width( plan->clist, i);
		gtk_clist_set_column_width( clist, i, plan->width+15);
	} */

	gtk_clist_moveto( plan->clist, plan->selected_row, 0, 0.5, 0.0);
	gtk_clist_thaw( plan->clist);
	plan->selected_row = -1;
}

/* when the date down arrow button is clicked in table. */
static void
on_date_button_released( GtkButton *button, 
                         gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;

	if( !GTK_WIDGET_VISIBLE( plan->calendar_win)) {
		GdkWindow *base_win_gdk = gtk_widget_get_parent_window(
			gnutr_get_base_win_container());
		GdkWindow *app_container_gdk = gtk_widget_get_parent_window(
			gnutr_get_base_app_container());
		int x1, y1, x2, y2, x3, y3, x4, y4;
		int w2, h2, w3;

		/* get the top left coords of the base cal_win relative to 
		 * the root window. */
		gdk_window_get_origin( base_win_gdk, &x1, &y1);

		/* get the top left coords of the base app container widget 
		 * w.r.t. to the base cal_win. */
		gdk_window_get_position( app_container_gdk, &x4, &y4);

		/* get the coords of the calendar arrow button widget w.r.t
		 * the app_conatiner widget. */
		x2 = GTK_WIDGET( plan->date_button)->allocation.x;
		y2 = GTK_WIDGET( plan->date_button)->allocation.y;

		w2 = GTK_WIDGET( plan->date_button)->allocation.width;
		h2 = GTK_WIDGET( plan->date_button)->allocation.height; 
		
		/* need to know the width of the widget before it is shown, so
		 * it must be realized first. */
		if( !GTK_WIDGET_REALIZED( plan->calendar_win)) 
			gtk_widget_realize( plan->calendar_win);
		w3 = plan->calendar_win->allocation.width;

		/* position the popup calendar cal_win so that its top right
		 * corner is shown below the button. */
		x3 = x1 + x2 + w2 - w3;
		y3 = y1 + y2 + y4 + h2;

		gtk_widget_set_uposition( plan->calendar_win, x3, y3);
		gtk_widget_show( plan->calendar_win);

	} else {
		/* position to the 11th row of the clist. */
		gtk_clist_moveto( plan->clist, 11, 0, 0.0, 1.0);

		gtk_widget_hide( plan->calendar_win);
	}
}

/* callback when the "Save" button is released. */
static void
on_save_button_released( GtkButton *button, 
                         gpointer   data)
{
	GtkWidget *dlg;
	GtkWidget *label;
	GtkWidget *base_win = gnutr_get_base_win();
	char *message;
	int reply;

	dlg = gnome_dialog_new( "Gnutrition: Save Plan", 
		GNOME_STOCK_BUTTON_YES, GNOME_STOCK_BUTTON_NO, NULL);
	
	gnome_dialog_set_parent( GNOME_DIALOG( dlg), GTK_WINDOW( base_win));

	message = g_strconcat( "The old plan will be overwritten by "
		"the new one.\n", "Are you sure you want to do this?", NULL);
	label = gtk_label_new(message);

	gtk_box_pack_start( GTK_BOX( GNOME_DIALOG( dlg)->vbox), label, 
		TRUE, TRUE, 5);
	g_free( message);

	gtk_widget_show_all( dlg);

	reply = gnome_dialog_run( GNOME_DIALOG( dlg));
	
	if (reply == 0) {
		gnutr_save_plan();
		gtk_widget_hide( dlg);
	} else {
		gtk_widget_hide( dlg);
	}
}

/* callback when a day is selected from the calendar. */
static void 
on_day_selected( GtkCalendar *calender, 
                 gpointer     data)
{
	struct tm tm;
	time_t time;
	static char buff[150];
	Plan_t *plan;

	plan = (Plan_t *)data;

	memset( &tm, 0, sizeof( tm));
	gtk_calendar_get_date( GTK_CALENDAR( plan->calendar), &tm.tm_year, 
			&tm.tm_mon, &tm.tm_mday); 
	tm.tm_year -= TM_YEAR_BASE;
	time = mktime( &tm);

	strftime( buff, 149, "%A %e %B %Y", gmtime( &time));
	strftime( buff, 149, "%Y-%m-%d", gmtime( &time));
	gtk_entry_set_text( plan->date_entry, buff);

	gnutr_update_plan_win();
	gtk_clist_moveto( plan->clist, 11, 0, 0.0, 1.0);
}

/* callback when the "Delete" button is released. */
static void 
on_delete_button_released( GtkButton *button, 
                           gpointer   data) 
{
	Plan_t *plan;
	char *text;

	plan = (Plan_t *)data;

	/* check that a row has been selected. */
	if ( plan->selected_row == -1) { 
		GtkWidget *warn_dlg;
		char *text;

		text = g_strconcat("You must select a food or\n",
			"recipe to delete.", NULL);
		warn_dlg = gnome_warning_dialog( text);

		gtk_widget_show( warn_dlg);
		g_free( text);
		return;
	} 

	/* check that there is a food or recipe for the selected
	 * row in the clist. If not, return. */
	gtk_clist_get_text( plan->clist, plan->selected_row, 1, &text);
	if ( strcmp( text, "") == 0) return;

	{
		char *date;
		char *time_of_day;
		char *hidden_data;
		enum { FOOD_ROW, RECIPE_ROW } type;

		/* get the text that is in the 3rd column of the clist. This
		 * is the "Measure" column - if there is a measure, the row
		 * contains a food, else it contains a recipe. */
		gtk_clist_get_text( plan->clist, plan->selected_row, 2, &text);
		if ( strcmp( text, "") == 0) {
			type = RECIPE_ROW;
		} else {
			type = FOOD_ROW;
		}

		date = gnutr_get_date();
		time_of_day = gnutr_get_time_of_day();
		hidden_data = (char *)gtk_clist_get_row_data( plan->clist, 
			plan->selected_row);

		/* delete the row from the plan list temporary database. */
		gnutr_delete_from_plan_temp_db( date, time_of_day, type, 
			hidden_data);

		/* delete from the clist. */
		gtk_clist_set_text( plan->clist, plan->selected_row, 1, "");
		gtk_clist_set_text( plan->clist, plan->selected_row, 2, "");
		gtk_clist_set_text( plan->clist, plan->selected_row, 3, "");
		if ( hidden_data) g_free( hidden_data);
	}
}

int
gnutr_get_plan_selected_row()
{
	Plan_t *plan;

	plan = get_plan_view();
	return plan->selected_row;
}

/* when the "Edit" button to the toolbar is released. */
static void 
on_edit_button_released( GtkButton *button, 
                         gpointer   data) 
{
	Plan_t *plan;

	plan = (Plan_t *)data;

	if (plan->selected_row == -1) { 
		/* check that a row has been selected. */
		char *text = g_strconcat( "You must select a food or\n",
			"recipe to edit.", NULL);
		GtkWidget *warn_dlg = gnome_warning_dialog( text);

		gtk_widget_show( warn_dlg);
		g_free( text);
		return;
	} else {
		char *msre_desc;

		/* if 'Measure' column is "" then the selected row is a 
		 * recipe, else, its a food. */
		gtk_clist_get_text( plan->clist, plan->selected_row, 2, 
			&msre_desc);
		if ( strcmp( msre_desc, "") == 0) {
			/* a recipe. */
			char *recipe_name;
			char *no_serv;

			gtk_clist_get_text( plan->clist, plan->selected_row, 1, 
				&no_serv);
			gtk_clist_get_text( plan->clist, plan->selected_row, 3, 
				&recipe_name);

			gnutr_show_edit_recipe_dlg( recipe_name, no_serv);

		} else {
			/* a food. */
			char *fd_no;
			char *msre_desc;
			char *amount;

			gtk_clist_get_text( plan->clist, plan->selected_row, 1,
				&amount);
			gtk_clist_get_text( plan->clist, plan->selected_row, 2,
				&msre_desc);
			fd_no = (char *)gtk_clist_get_row_data( plan->clist, 
				plan->selected_row);

			gnutr_show_edit_food_dlg( fd_no, msre_desc, amount, 1);
		}
	}
}

/* callback when the "Compute" button to the toolbar is released. */
static void 
on_compute_button_released( GtkButton *button, 
                            gpointer   data) 
{
	/* show the dialog that is used to compute the nutrient
	 * totals for the plan. */
	gnutr_show_plan_compute_dlg();
}

/* callback when the "Save" item is selected from the menubar. */
static void
on_save_activate( GtkMenuItem *menuitem,
                  gpointer     data)
{
	on_save_button_released( NULL, data);
}

/* callback when the "Add" item is selected from the menubar. */
static void
on_add_activate( GtkMenuItem *menuitem,
                 gpointer     data)
{
	on_add_button_released( NULL, data);
}

/* callback when the "Delete" item is selected from the menubar. */
static void
on_delete_activate( GtkMenuItem *menuitem,
                    gpointer     data)
{
	on_delete_button_released( NULL, data);
}

/* callback when the "Edit" item is selected from the menubar. */
static void
on_edit_activate( GtkMenuItem *menuitem,
                  gpointer     data)
{
	on_edit_button_released( NULL, data);
}

/* callback when the "Meal Plan" item is selected from the menubar. */
static void
on_meal_plan_activate(GtkMenuItem *menuitem,
                      gpointer     data)
{
	gnutr_switch_to_meal_plan_view();
}

/* callback when the "Recipe" item is selected from the menubar. */
static void
on_recipe_activate( GtkMenuItem *menuitem,
                    gpointer     data)
{
	gnutr_switch_to_recipe_view();
}

/* callback when the "Food" item is selected from the menubar. */
static void
on_food_activate( GtkMenuItem *menuitem,
                  gpointer     data)
{
	gnutr_switch_to_food_view();
}

/* callback when the "Preferences" item is selected from the menubar. */
static void
on_preferences_activate( GtkMenuItem *menuitem,
                         gpointer     data)
{
	gnutr_show_nutr_goal_dlg();
}

/* callback when the "About" item is selected from the menubar. */
static void
on_about_activate( GtkMenuItem *menuitem,
                   gpointer     data)
{
	gnutr_show_about_dlg();	
}

/* update appbar when pointer enters "Save" button on toolbar. */
static void
on_save_button_enter( GtkButton *button,
                      gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_push( plan->appbar,
		" Save the Meal Plan to the Database.");
}

/* update appbar when pointer leaves "Save" button on toolbar. */
static void
on_save_button_leave( GtkButton *button,
                      gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_clear_stack( plan->appbar);
}

/* update appbar when pointer enters "Add" button on toolbar. */
static void
on_add_button_enter( GtkButton *button,
                     gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_push( plan->appbar,
		" Add a Recipe or Food to the Day's Meal Plan.");
}

/* update appbar when pointer leaves "Add" button on toolbar. */
static void
on_add_button_leave( GtkButton *button,
                     gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_clear_stack( plan->appbar);
}

/* update appbar when pointer enters "Delete" button on toolbar. */
static void
on_delete_button_enter( GtkButton *button,
                        gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_push( plan->appbar,
		" Delete a Selected Recipe or Food from the Meal Plan.");
}

/* update appbar when pointer leaves "Delete" button on toolbar. */
static void
on_delete_button_leave( GtkButton *button,
                        gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_clear_stack( plan->appbar);
}

/* update appbar when pointer enters "Edit" button on toolbar. */
static void
on_edit_button_enter( GtkButton *button,
                      gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_push( plan->appbar,
		" Edit a Selected Recipe or Food in the Meal Plan.");
}

/* update appbar when pointer leaves "Edit" button on toolbar. */
static void
on_edit_button_leave( GtkButton *button,
                      gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_clear_stack( plan->appbar);
}

/* update appbar when pointer enters "Compute" button on toolbar. */
static void
on_compute_button_enter( GtkButton *button,
                         gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_push( plan->appbar,
		" Compute the Nutrient Composition of the Meal Plan.");
}

/* update appbar when pointer leaves "Compute" button on toolbar. */
static void
on_compute_button_leave( GtkButton *button,
                         gpointer   data)
{
	Plan_t *plan;

	plan = (Plan_t *)data;
	gnome_appbar_clear_stack( plan->appbar);
}

/* return a pointer the meal plan clist for the current day. */
GtkCList *
gnutr_get_plan_win_clist()
{
	Plan_t *plan;

	plan = get_plan_view();

	return plan->clist;
}
