/*  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 "load_data.h"
#include "recipe_win.h"
#include "support.h"
#include "nutr_goal_dlg_ui.h"
#include "nutr_comp_dlg.h"
#include "food_srch_dlg.h"
#include "recipe_srch_dlg.h"
#include "edit_food_dlg.h"
#include "base_win.h"
#include "wrap_mysql.h"

static GladeXML *xml = NULL;
static void load_xml(void);
static int selected_row;
static int no_ingredients = 0;

static void load_xml(void);
static void connect_signals(void);
static void init_recipe_win( void);

/* the callbacks. */
static void on_clear_button_released(GtkButton *, gpointer);
static void on_clear_button_enter(GtkButton *, gpointer);
static void on_clear_button_leave(GtkButton *, gpointer);
static void on_open_button_released(GtkButton *, gpointer);
static void on_open_button_enter(GtkButton *, gpointer);
static void on_open_button_leave(GtkButton *, gpointer);
static void on_save_button_released(GtkButton *, gpointer);
static void on_save_button_enter(GtkButton *, gpointer);
static void on_save_button_leave(GtkButton *, gpointer);
static void on_add_button_released(GtkButton *, gpointer);
static void on_add_button_enter(GtkButton *, gpointer);
static void on_add_button_leave(GtkButton *, gpointer);
static void on_delete_button_released(GtkButton *, gpointer);
static void on_delete_button_enter(GtkButton *, gpointer);
static void on_delete_button_leave(GtkButton *, gpointer);
static void on_edit_button_released(GtkButton *, gpointer);
static void on_edit_button_enter(GtkButton *, gpointer);
static void on_edit_button_leave(GtkButton *, gpointer);
static void on_nutr_button_released(GtkButton *, gpointer);
static void on_nutr_button_enter(GtkButton *, gpointer);
static void on_nutr_button_leave(GtkButton *, gpointer);
static void on_goal_button_released(GtkButton *, gpointer);
static void on_goal_button_enter(GtkButton *, gpointer);
static void on_goal_button_leave(GtkButton *, gpointer);
static void on_clear_activate(GtkMenuItem *, gpointer);
static void on_open_activate(GtkMenuItem *, gpointer);
static void on_save_activate(GtkMenuItem *, gpointer);
static void on_exit_activate(GtkMenuItem *, gpointer);
static void on_add_food_activate(GtkMenuItem *, gpointer);
static void on_delete_food_activate(GtkMenuItem *, gpointer);
static void on_edit_food_activate(GtkMenuItem *, gpointer);
static void on_nutr_goal_activate(GtkMenuItem *menuitem, gpointer);
static void on_about_activate(GtkMenuItem *menuitem, gpointer);
static void on_manual_activate(GtkMenuItem *, gpointer);
static void on_food_view_activate( GtkMenuItem *, gpointer);
static void on_meal_plan_view_activate( GtkMenuItem *, gpointer);
static void on_clist_select_row(GtkCList *, int, int, GdkEvent *, gpointer);

/* connect the signals. We do it here rather than in the glade file
 * so that the callbacks can be static functions. */
static void
connect_signals()
{
	/* the tool bar buttons. */
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "clear_button")),
		"released", GTK_SIGNAL_FUNC(  on_clear_button_released), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "clear_button")),
		"enter", GTK_SIGNAL_FUNC( on_clear_button_enter), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml,"clear_button")),
		"leave", GTK_SIGNAL_FUNC( on_clear_button_leave), NULL);

	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "save_button")),
		"released", GTK_SIGNAL_FUNC( on_save_button_released), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "save_button")),
		"enter", GTK_SIGNAL_FUNC( on_save_button_enter), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "save_button")),
		"leave", GTK_SIGNAL_FUNC( on_save_button_leave), NULL);

	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "open_button")),
		"released", GTK_SIGNAL_FUNC( on_open_button_released), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "open_button")),
		"enter", GTK_SIGNAL_FUNC( on_open_button_enter), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "open_button")),
		"leave", GTK_SIGNAL_FUNC( on_open_button_leave), NULL);

	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "add_button")),
		"released", GTK_SIGNAL_FUNC( on_add_button_released), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "add_button")),
		"enter", GTK_SIGNAL_FUNC( on_add_button_enter), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "add_button")),
		"leave", GTK_SIGNAL_FUNC( on_add_button_leave), NULL);

	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "delete_button")),
		"released", GTK_SIGNAL_FUNC( on_delete_button_released), NULL);
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "delete_button")),
		"enter", GTK_SIGNAL_FUNC( on_delete_button_enter), NULL);
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "delete_button")),
		"leave", GTK_SIGNAL_FUNC( on_delete_button_leave), NULL);

	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "edit_button")),
		"released", GTK_SIGNAL_FUNC( on_edit_button_released), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "edit_button")),
		"enter", GTK_SIGNAL_FUNC( on_edit_button_enter), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "edit_button")),
		"leave", GTK_SIGNAL_FUNC( on_edit_button_leave), NULL);

	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "nutr_button")),
		"released", GTK_SIGNAL_FUNC( on_nutr_button_released), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "nutr_button")),
		"enter", GTK_SIGNAL_FUNC( on_nutr_button_enter), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "nutr_button")),
		"leave", GTK_SIGNAL_FUNC( on_nutr_button_leave), NULL);

	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "goal_button")),
		"released", GTK_SIGNAL_FUNC( on_goal_button_released), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "goal_button")),
		"enter", GTK_SIGNAL_FUNC( on_goal_button_enter), NULL);
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "goal_button")),
		"leave", GTK_SIGNAL_FUNC( on_goal_button_leave), NULL);

	/* the menubar items. */
	gtk_signal_connect( GTK_OBJECT( glade_xml_get_widget(xml, "clear")),
		"activate", GTK_SIGNAL_FUNC( on_clear_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( glade_xml_get_widget(xml, "open")),
		"activate", GTK_SIGNAL_FUNC( on_open_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( glade_xml_get_widget(xml, "save")),
		"activate", GTK_SIGNAL_FUNC( on_save_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( glade_xml_get_widget(xml, "exit")),
		"activate", GTK_SIGNAL_FUNC( on_exit_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( glade_xml_get_widget(xml, "add_food")),
		"activate", GTK_SIGNAL_FUNC( on_add_food_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "edit_food")),
		"activate", GTK_SIGNAL_FUNC( on_edit_food_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "delete_food")),
		"activate", GTK_SIGNAL_FUNC( on_delete_food_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "nutr_goal")),
		"activate", GTK_SIGNAL_FUNC( on_nutr_goal_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( glade_xml_get_widget(xml, "about")),
		"activate", GTK_SIGNAL_FUNC( on_about_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( glade_xml_get_widget(xml, "manual")),
		"activate", GTK_SIGNAL_FUNC( on_manual_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "meal_plan_view")),
		"activate", GTK_SIGNAL_FUNC( on_meal_plan_view_activate), NULL);
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget(xml, "food_view")),
		"activate", GTK_SIGNAL_FUNC( on_food_view_activate), NULL);

	/* the GtkCList. */
	gtk_signal_connect( GTK_OBJECT(
		glade_xml_get_widget(xml, "ingredient_clist")), "select_row", 
		GTK_SIGNAL_FUNC( on_clist_select_row), NULL);
}

int
gnutr_get_recipe_win_selected_row()
{
	return selected_row;
}

GtkCList *
gnutr_get_recipe_win_clist()
{
	GtkCList *clist;
	if ( !xml) load_xml();

	clist = (GtkCList *)glade_xml_get_widget( xml, "ingredient_clist");

	return clist;
}

/* 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 "/recipe_win.glade", NULL);
		loaded_xml = TRUE;
		if (xml) {
			connect_signals();
		} else {
			g_log("Gnutrition", G_LOG_LEVEL_ERROR,
				"cannot load glade file: recipe_win.glade\n");
			return;
		}
	}
	init_recipe_win();
}

/* initialize the recipe window. */
static void
init_recipe_win()
{
	GtkWidget *combo;
	GList *cat_list = NULL;

	selected_row = -1;

	/* put the category descriptions to the combo box. */
	cat_list = get_cat_desc_list();

	combo = glade_xml_get_widget( xml, "category_combo");
	gtk_combo_set_popdown_strings( GTK_COMBO( combo), cat_list);
}

/* Open the recipe search  dialog when "Open" is selected from
 * the window menu item. */
static void
on_open_activate(GtkMenuItem *menuitem,
                 gpointer     user_data)
{
	gnutr_show_recipe_srch_dlg();
}

/* Exit the application when "exit" is selected from the recipe
 * window menu bar. */
static void
on_exit_activate(GtkMenuItem *menuitem,
                 gpointer     user_data)
{
	gtk_main_quit();
}

/* Show the food selection/search dialog. */
static void
on_add_button_released(GtkButton *button,
                       gpointer   user_data)
{
	enum { PLAN_VIEW, RECIPE_VIEW, FOOD_VIEW };
	gnutr_show_srch_dlg( RECIPE_VIEW);
}

/* opens the edit food dialog, so an ingredient can be edited,
 * called when the "Edit" button is released in the ToolBar. */
static void
on_edit_button_released( GtkButton *button,
                         gpointer   user_data)
{
	if ( selected_row == -1) {
		GtkWidget *warn_dlg;
		warn_dlg = gnome_warning_dialog( "No ingredient is selected.");
		gtk_widget_show( warn_dlg);
		return;
	} else {
		char *amount, *measure, *food_no;
		GtkCList *clist = (GtkCList *)glade_xml_get_widget( xml, 
			"ingredient_clist");

		gtk_clist_get_text( clist, selected_row, 0, &amount);
		gtk_clist_get_text( clist, selected_row, 1, &measure);

		food_no = (char *)gtk_clist_get_row_data( clist, selected_row);

		/* show the edit dialog. */
		gnutr_show_edit_food_dlg(food_no, measure, amount, 0);
	}
}

/* when "clear recipe" is selected from the recipe window menu bar. */
static void
on_clear_activate( GtkMenuItem *menuitem,
                   gpointer     user_data)
{
	on_clear_button_released( NULL, NULL);
}

/* open the search dialog when "add food" is selected from the
 * recipe window menu bar. */
static void
on_add_food_activate(GtkMenuItem *menuitem,
                     gpointer     user_data)
{
	on_add_button_released(NULL, NULL);
}

/* open the search dialog when "edit food" is selected from the
 * recipe window menu bar. */
static void
on_edit_food_activate(GtkMenuItem *menuitem,
                      gpointer     user_data)
{
	on_edit_button_released(NULL, NULL);
}

/* delete the selected ingredient when "delete food" is selected from the
 * recipe window menu bar. */
static void
on_delete_food_activate(GtkMenuItem *menuitem,
                        gpointer     user_data)
{
	on_delete_button_released(NULL, NULL);
}

/* open the nutrient goal dialog when "nutrient goal" is selected from the
 * recipe window menu bar. */
static void
on_nutr_goal_activate(GtkMenuItem *menuitem,
                      gpointer     user_data)
{
	gnutr_show_nutr_goal_dlg();
}

/* show the about dialog. */
void
gnutr_show_about_dlg()
{
	GtkWidget *about_dlg = glade_xml_get_widget(xml, "gnutr_about");
	gnome_dialog_close_hides( GNOME_DIALOG( about_dlg), TRUE);
	gtk_widget_show(about_dlg); 
}

/* the about dialog - this is a public callback. */
static void
on_about_activate(GtkMenuItem *menuitem,
                  gpointer     user_data)
{
	gnutr_show_about_dlg();
}

/* when the "open" button is released on the appbar of the recipe window. */
static void
on_open_button_released( GtkButton *button,
                         gpointer   user_data)
{
	gnutr_show_recipe_srch_dlg();
}

/* removes an ingredient from the recipe when the delete button is
 * released in the appbar on the recipe window. */
static void
on_delete_button_released(GtkButton *button,
                          gpointer   user_data)
{
	/* check that an ingredient has been selected. */
	if (selected_row == -1) {
		GtkWidget *warn_dlg;

		warn_dlg = gnome_warning_dialog( "no ingredient is selected.");
		gtk_widget_show( warn_dlg);
		return;
	} else {
		GtkCList *clist = (GtkCList *)glade_xml_get_widget( xml, 
			"ingredient_clist");

		/* remove the row from the food list. */
		gtk_clist_remove(clist, selected_row);
		selected_row = -1;
		no_ingredients--;
	}
}

/* get the row that has been selected the the ingredient list. */
static void
on_clist_select_row( GtkCList *clist,
                     int      row,
                     int      column,
                     GdkEvent *event,
                     gpointer  user_data)
{
	selected_row = row;
}


/* just here for the future. */
static void
on_manual_activate(GtkMenuItem *menuitem,
                   gpointer     user_data)
{

}

/* update the status bar on entering the clear recipe button in the 
 * recipe window. */
static void
on_clear_button_enter(GtkButton *button,
                    gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_push( GNOME_APPBAR( widget), 
			" clear the recipe.");
}

/* update the status bar on entering the open recipe file button in the 
 * recipe window. */
static void
on_open_button_enter(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_push( GNOME_APPBAR( widget), 
			" open an existing recipe from the database");
}

/* update the status bar on entering the save recipe to file button in the 
 * recipe window. */
static void
on_save_button_enter(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_push( GNOME_APPBAR( widget), 
			" save the recipe to the database.");
}

/* update the status bar on entering the add ingredient to recipe button
 * in the recipe window. */
static void
on_add_button_enter(GtkButton *button,
                    gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_push( GNOME_APPBAR( widget), 
			" add an ingredient to the recipe.");
}

/* update the status bar on entering the delete ingredient from recipe 
 * button in the recipe window. */
static void
on_delete_button_enter(GtkButton *button,
                       gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_push( GNOME_APPBAR( widget), 
			" delete the selected ingredient from the recipe.");
}

/* update the status bar on entering the edit ingredient 
 * button in the recipe window. */
static void
on_edit_button_enter(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_push( GNOME_APPBAR( widget), 
			" edit the selected ingredient.");
}

/* update the status bar on entering the show nutrient composition dialog
 * button in the recipe window. */
static void
on_nutr_button_enter(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_push( GNOME_APPBAR( widget), 
			" open the recipe nutrient composition dialog.");
}

/* update the status bar on entering the show nutrient goal dialog
 * button in the recipe window. */
static void
on_goal_button_enter(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_push( GNOME_APPBAR( widget), 
			" open the nutrient goal dialog.");
}

/* on pointer leaving clear recipe button in recipe window. */
static void
on_clear_button_leave( GtkButton *button,
                       gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget( xml, "appbar");
	gnome_appbar_clear_stack( GNOME_APPBAR( widget));
}

/* on pointer leaving open recipe button in recipe window. */
static void
on_open_button_leave(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_clear_stack( GNOME_APPBAR( widget));
}

/* on pointer leaving save recipe button in recipe window. */
static void
on_save_button_leave(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_clear_stack( GNOME_APPBAR( widget));
}

/* on pointer leaving add ingredient button in recipe window. */
static void
on_add_button_leave(GtkButton *button,
                    gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_clear_stack( GNOME_APPBAR( widget));
}

/* on pointer leaving delete ingredient button in recipe window. */
static void
on_delete_button_leave(GtkButton *button,
                       gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_clear_stack( GNOME_APPBAR( widget));
}

/* on pointer leaving edit ingredient button in recipe window. */
static void
on_edit_button_leave(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_clear_stack( GNOME_APPBAR( widget));
}

/* on pointer leaving nutrient composition  button in recipe window. */
static void
on_nutr_button_leave(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_clear_stack( GNOME_APPBAR( widget));
}

/* on pointer leaving nutrition goal button in recipe window. */
static void
on_goal_button_leave(GtkButton *button,
                     gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget(xml, "appbar");
	gnome_appbar_clear_stack( GNOME_APPBAR( widget));
}

/* when the "nutrients" button is released in the recipe window, show
 * the nutritional composition dialog. */
static void
on_nutr_button_released(GtkButton *button,
                        gpointer   user_data)
{
	char *no_serv, *recipe_name = NULL;

	no_serv = gtk_entry_get_text( GTK_ENTRY(
			glade_xml_get_widget(xml, "no_serv_entry")));
	recipe_name = gtk_entry_get_text( GTK_ENTRY(
			glade_xml_get_widget(xml, "recipe_name_entry")));

	if ( no_ingredients == 0) {
		GtkWidget *warn_dlg;
		char *text;
		
		text = g_strdup( "There are no ingredients.");
		warn_dlg = gnome_warning_dialog( text);
		gtk_widget_show( warn_dlg);
		g_free( text);
		return;
	}

	if (!all_digits(no_serv)) {
		GtkWidget *warn_dlg;
		char *text;
		
		text = g_strconcat( "The number of servings is not specified,",
			"\nor is not a number.", NULL);
		warn_dlg = gnome_warning_dialog( text);
		gtk_widget_show( warn_dlg);
		g_free( text);
		return;
	}
	gnutr_show_nutr_comp_dlg( NULL, 0);
}

/* when the "goals" button is released in the recipe window, show
 * the nutrient goals dialog. */
static void
on_goal_button_released(GtkButton *button,
                        gpointer   user_data)
{
	gnutr_show_nutr_goal_dlg();
}

/* save a recipe when "save" is selected from the recipe 
 * window menu item. */
static void
on_save_activate(GtkMenuItem *menuitem,
                 gpointer     user_data)
{
	on_save_button_released( NULL, NULL);
}

/* When the "Clear" button is released on the recipe window AppBar,
 * ask if current recipe is to be saved. */
static void
on_clear_button_released(GtkButton *button,
                       gpointer   user_data)
{
	GtkWidget *entry;
	GtkWidget *clist;

	/* if there is a recipe defined, open a dialog to ask if the
	 * recipe is to be saved. */
	if ( no_ingredients !=0) {
		GtkWidget *dlg;
		GtkWidget *label;
		GtkWidget *base_win = gnutr_get_base_win();
		char *message;
		int reply;

		dlg = gnome_dialog_new( "Gnutrition: Save Recipe", 
			GNOME_STOCK_BUTTON_YES, GNOME_STOCK_BUTTON_NO, NULL);

		gnome_dialog_set_parent( GNOME_DIALOG( dlg), 
			GTK_WINDOW( base_win));

		message = g_strconcat( "You are about to clear the recipe.\n"
			"Do you wish to save it first?", 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) {
			gtk_widget_hide( dlg);
			on_save_button_released( NULL, NULL);
		} else {
			gtk_widget_hide( dlg);
		}
	}

	/* clear the recipe name entry. */
	entry = glade_xml_get_widget( xml, "recipe_name_entry");
	gtk_entry_set_text( GTK_ENTRY( entry), "");

	/* clear the number of servings entry. */
	entry = glade_xml_get_widget( xml, "no_serv_entry");
	gtk_entry_set_text( GTK_ENTRY( entry), "");

	/* clear the ingredient clist. */
	clist = glade_xml_get_widget( xml, "ingredient_clist");
	gtk_clist_clear( (GtkCList *)clist);
	no_ingredients = 0;
	/* FIXME: does this free the hidden data pointers? */
}

/* When the "Save" button is released on the AppBar of the recipe window. */
static void
on_save_button_released(GtkButton *button,
                        gpointer   user_data)
{
	char *recipe_name, *recipe_no;
	char *no_serv;
	char *cat_desc, *cat_no;
	GtkWidget *widget;
	GtkCList *clist;
	int row;
	char *amount, *msre_desc, *food_no;
	char **elm;
	GList *list_ingr = NULL;
	GHashTable *htbl;

	/* FIXME: when this is called for the first time, when there are
	 * no recipes in the database, I get a warning from
	 * wrap_mysql. */

	widget = glade_xml_get_widget( xml, "no_serv_entry");
	no_serv = gtk_entry_get_text( GTK_ENTRY( widget));
	widget = glade_xml_get_widget( xml, "recipe_name_entry");
	recipe_name = gtk_entry_get_text( GTK_ENTRY( widget));
	widget = glade_xml_get_widget( xml, "ingredient_clist");
	clist = (GtkCList *)widget;
	widget = glade_xml_get_widget( xml, "category_entry");
	cat_desc = gtk_entry_get_text( GTK_ENTRY( widget));

	/* Check that the recipe name is defined. */
	if (empty_text( recipe_name)) {
		GtkWidget *warn_dlg;

		warn_dlg = gnome_warning_dialog( 
			"No recipe name is defined.");
		gtk_widget_show( warn_dlg);
		return;
	}

	/* Check that the number of servings is defined. */
	if (!all_digits( no_serv)) {
		GtkWidget *warn_dlg;

		warn_dlg = gnome_warning_dialog( 
			"The number of servings is not defined.");
		gtk_widget_show( warn_dlg);
		return;
	}

	/* get the category number for the category description. */
	htbl = get_htbl_cat_desc_cat_no();
	cat_no = g_hash_table_lookup( htbl, (gpointer)cat_desc);

	/* create a glist of the ingredient clist. */
	for ( row = 0; row < no_ingredients; row++) {
		elm = (char **)g_malloc( 3 * sizeof( char *));

		gtk_clist_get_text( clist, row, 0, &amount);
		elm[0] = g_strdup( amount);

		gtk_clist_get_text( clist, row, 1, &msre_desc);
		elm[1] = g_strdup( msre_desc);

		food_no = (char *)gtk_clist_get_row_data( clist, row);
		elm[2] = g_strdup( food_no);

		list_ingr = g_list_append( list_ingr, (gpointer)elm);
	}

	recipe_no = gnutr_check_recipe_exists( recipe_name);

	if ( recipe_no) { 
		/* a recipe with the same name already exists 
		 * in the database. */
		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( "A recipe already exists in the ",
			"database with the same name.\n", 
			"Do you want to overwrite it?", 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_delete_recipe( recipe_no);
			gnutr_save_recipe( list_ingr, recipe_name, no_serv, 
				cat_no, no_ingredients);
			gtk_widget_hide( dlg);
		} else {
			gtk_widget_hide( dlg);
		}

		g_free( recipe_no);
	} else {
		gnutr_save_recipe( list_ingr, recipe_name, no_serv, cat_no,
			no_ingredients);
	}
}

GtkWidget *
gnutr_get_recipe_win_menubar()
{
	if ( !xml) load_xml();
	return glade_xml_get_widget(xml, "menubar_container");
}

GtkWidget *
gnutr_get_recipe_win_toolbar()
{
	if ( !xml) load_xml();
	return glade_xml_get_widget(xml, "toolbar_container");
}

GtkWidget *
gnutr_get_recipe_win_table()
{
	if ( !xml) load_xml();
	return glade_xml_get_widget(xml, "table_container");
}

GtkWidget *
gnutr_get_recipe_win_appbar()
{
	if ( !xml) load_xml();
	return glade_xml_get_widget(xml, "appbar_container");
}

/* add an ingredient to the recipe clist. */
void
gnutr_add_ingredient_to_recipe_clist( char *food_no, 
                                      char *amount, 
                                      char *measure)
{
	GtkCList *clist;
	char *text[3];
	int row;
	GHashTable *htbl;
	char *food_desc;
	char *hidden_data;
	gboolean match = FALSE;

	g_return_if_fail( food_no);
	g_return_if_fail( amount);
	g_return_if_fail( measure);

	clist = (GtkCList *)glade_xml_get_widget( xml, "ingredient_clist");

	/* get the food description from its number. */
	htbl = get_htbl_fd_no_fd_desc();
	food_desc = (char *)g_hash_table_lookup( htbl, food_no);

	/* put all the data in one array - so it can be inserted into
	 * a GtkCList. */
	text[0] = amount;
	text[1] = measure;
	text[2] = food_desc;

	for ( row = 0; row < no_ingredients; row++) {
		hidden_data = (char *)gtk_clist_get_row_data(clist, row);
		if ( strcmp( food_no, hidden_data) == 0) {
			match = TRUE;
			break;
		}
	}

	if ( !match) {    /* does not exist - append. */
		gtk_clist_append( clist, text);
		gtk_clist_set_row_data( clist, no_ingredients, 
			(gpointer)food_no);
		no_ingredients++;
	} else {            /* does exist - overwrite. */ 
		gtk_clist_insert( clist, row, text);
		gtk_clist_set_row_data( clist, row, (gpointer)food_no);
		gtk_clist_remove( clist, row+1);
	}
}

/* exit gnutrition. */
void
gnutr_exit_app()
{
	on_exit_activate( NULL, NULL);
}

/* set the recipe_win clist to the newly loaded recipe.
 * Each element in list contains: amount, msre_desc, food_desc, food_no. */
void
gnutr_set_recipe_win( char *recipe_name, 
                      char *recipe_no,
                      char *no_serv, 
                      char *cat_desc, 
                      GList *list)
{
	GList *ptr;
	GtkCList *clist;
	char **elm;
	GtkWidget *widget;
	char *text[3];
	char *food_no;

	g_return_if_fail( recipe_name);
	g_return_if_fail( recipe_no);
	g_return_if_fail( no_serv);
	g_return_if_fail( cat_desc);
	g_return_if_fail( list);

	clist = (GtkCList *)glade_xml_get_widget( xml, "ingredient_clist");

	/* clear any existing clist. */
	no_ingredients = 0;
	gtk_clist_clear( clist);

	/* set the amount, measure and food in the ingredient clist. */
	for ( ptr = list; ptr; ptr = ptr->next) {
		elm = (char **)ptr->data;
		text[0] = elm[0];
		text[1] = elm[1];
		text[2] = elm[2];

		gtk_clist_append( clist, text);

		/* add the food number as the hidden data pointer. 
		 * A duplicate is created so that it points to the 
		 * correct data when the list is freed. */
		food_no = g_strdup( elm[3]);
		gtk_clist_set_row_data( clist, no_ingredients, 
			(gpointer)food_no);

		no_ingredients++;
	}

	/* set the recipe name, number of servings, and category
	 * description entries. */
	widget = glade_xml_get_widget( xml, "recipe_name_entry");
	gtk_entry_set_text( GTK_ENTRY( widget), recipe_name);
	widget = glade_xml_get_widget( xml, "no_serv_entry");
	gtk_entry_set_text( GTK_ENTRY( widget), no_serv);
	widget = glade_xml_get_widget( xml, "category_entry");
	gtk_entry_set_text( GTK_ENTRY( widget), cat_desc);

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

char *
gnutr_get_recipe_no_serv()
{
	GtkWidget *entry;
	char *no_serv;

	if ( !xml) load_xml();

	entry = glade_xml_get_widget( xml, "no_serv_entry");
	no_serv = gtk_entry_get_text( GTK_ENTRY( entry));

	return no_serv;
}

GList *
gnutr_get_recipe_ingr_list()
{
	int i;
	GList *ret_list = NULL;
	GtkCList *clist;
	char **elm;
	char *text, *msre_no;
	GHashTable *htbl;

	clist = (GtkCList *)glade_xml_get_widget( xml, "ingredient_clist");

	htbl = get_htbl_msre_desc_msre_no();

	for ( i=0; i<no_ingredients; i++) {
		elm = (char **)g_malloc( 3 * sizeof( char *));

		/* amount. */
		gtk_clist_get_text( clist, i, 0, &text);
		elm[0] = g_strdup( text);

		/* measure. */
		gtk_clist_get_text( clist, i, 1, &text);
		if ( strcmp( text, "gm") == 0) {
			elm[1] = g_strdup( "99999");
		} else {
			msre_no = (char *)g_hash_table_lookup( htbl, 
				(gpointer)text);
			elm[1] = g_strdup( msre_no);
		}

		/* food_no. */
		text = (char *)gtk_clist_get_row_data( clist, i);
		elm[2] = g_strdup( text);

		ret_list = g_list_append( ret_list, (gpointer)elm);
	}
	return ret_list;
}

/* switch from the "Recipe" to the "Meal Plan" view. */
static void
on_meal_plan_view_activate(GtkMenuItem *menuitem,
                 gpointer     user_data)
{
	gnutr_switch_to_meal_plan_view();
}

/* switch from the "Recipe" to the "Food" view. */
static void
on_food_view_activate(GtkMenuItem *menuitem,
                 gpointer     user_data)
{
	gnutr_switch_to_food_view();
}
