/*
 * Copyright 2012 Ole Loots <ole@monochrom.net>
 *
 * This file is part of NetSurf, http://www.netsurf-browser.org/
 *
 * NetSurf 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; version 2 of the License.
 *
 * NetSurf 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, see <http://www.gnu.org/licenses/>.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>

#include "utils/log.h"
#include "utils/nsoption.h"
#include "utils/nsurl.h"
#include "utils/utf8.h"
#include "netsurf/browser_window.h"
#include "netsurf/mouse.h"
#include "netsurf/plotters.h"
#include "netsurf/keypress.h"
#include "desktop/plot_style.h"
#include "desktop/browser_history.h"
#include "desktop/tree.h"
#include "desktop/hotlist.h"
#include "desktop/textarea.h"

#include "atari/clipboard.h"
#include "atari/gui.h"
#include "atari/search.h"
#include "atari/toolbar.h"
#include "atari/rootwin.h"
#include "atari/clipboard.h"
#include "atari/misc.h"
#include "atari/plot/plot.h"
#include "cflib.h"
#include "atari/res/netsurf.rsh"
#include "atari/encoding.h"


#define TB_BUTTON_WIDTH 32
#define THROBBER_WIDTH 32
#define THROBBER_MIN_INDEX 1
#define THROBBER_MAX_INDEX 12
#define THROBBER_INACTIVE_INDEX 13

enum e_toolbar_button_states {
        button_on = 0,
        button_off = 1
};
#define TOOLBAR_BUTTON_NUM_STATES   2

struct s_toolbar;

struct s_tb_button {
	short rsc_id;
	void (*cb_click)(struct s_toolbar *tb);
	struct hlcache_handle *icon[TOOLBAR_BUTTON_NUM_STATES];
	struct s_toolbar *owner;
	enum e_toolbar_button_states state;
	short index;
	GRECT area;
};


extern char * option_homepage_url;
extern void * h_gem_rsrc;
extern struct gui_window * input_window;
extern long atari_plot_flags;
extern int atari_plot_vdi_handle;
extern EVMULT_OUT aes_event_out;

static OBJECT * aes_toolbar = NULL;
static OBJECT * throbber_form = NULL;
static bool init = false;
static int area_navigation_height = 0;
static int area_search_height = 0;
static int area_full_height = 0;
static float toolbar_url_scale = 1.0;

static plot_font_style_t font_style_url = {
	.family = PLOT_FONT_FAMILY_SANS_SERIF,
	.size = 14*FONT_SIZE_SCALE,
	.weight = 400,
	.flags = FONTF_NONE,
	.background = 0xffffff,
	.foreground = 0x0
};


/* prototypes & order for button widgets: */

static struct s_tb_button tb_buttons[] = {
	{
		TOOLBAR_BT_BACK,
		toolbar_back_click,
		{0,0},
		0, 0, 0, {0,0,0,0}
	},
	{
		TOOLBAR_BT_HOME,
		toolbar_home_click,
		{0,0},
		0, 0, 0, {0,0,0,0}
	},
	{
		TOOLBAR_BT_FORWARD,
		toolbar_forward_click,
		{0,0},
		0, 0, 0, {0,0,0,0}
	},
	{
		TOOLBAR_BT_STOP,
		toolbar_stop_click,
		{0,0},
		0, 0, 0, {0,0,0,0}
	},
	{
		TOOLBAR_BT_RELOAD,
		toolbar_reload_click,
		{0,0},
		0, 0, 0, {0,0,0,0}
	},
	{ 0, 0, {0,0}, 0, -1, 0, {0,0,0,0}}
};

struct s_toolbar_style {
	int font_height_pt;
};

static struct s_toolbar_style toolbar_styles[] = {
	/* small (18 px height) */
	{9},
	/* medium (default - 26 px height) */
	{14},
	/* large ( 49 px height ) */
	{18},
	/* custom style: */
	{18}
};

static const struct redraw_context toolbar_rdrw_ctx = {
	.interactive = true,
	.background_images = true,
	.plot = &atari_plotters
};

static void tb_txt_request_redraw(void *data, int x, int y, int w, int h );

/**
 *   Find a button for a specific resource ID
 */
static struct s_tb_button *find_button(struct s_toolbar *tb, int rsc_id)
{
	int i = 0;
	while (i < tb->btcnt) {
		if (tb->buttons[i].rsc_id == rsc_id) {
			return(&tb->buttons[i]);
		}
		i++;
	}
	return(NULL);
}

/**
 *   Callback for textarea redraw
 */
static void tb_txt_request_redraw(void *data, int x, int y, int w, int h)
{

	GRECT area;
	struct s_toolbar * tb = (struct s_toolbar *)data;

	if (tb->attached == false) {
		return;
	}

	toolbar_get_grect(tb, TOOLBAR_AREA_URL, &area);
	area.g_x += x;
	area.g_y += y;
	area.g_w = w;
	area.g_h = h;
	/* dbg_grect("tb_txt_request_redraw", &area); */
	window_schedule_redraw_grect(tb->owner, &area);
	return;
}

static void tb_txt_callback(void *data, struct textarea_msg *msg)
{
	switch (msg->type) {

	case TEXTAREA_MSG_DRAG_REPORT:
		break;

	case TEXTAREA_MSG_REDRAW_REQUEST:
		tb_txt_request_redraw(data,
				      msg->data.redraw.x0, msg->data.redraw.y0,
				      msg->data.redraw.x1 - msg->data.redraw.x0,
				      msg->data.redraw.y1 - msg->data.redraw.y0);
		break;

	default:
		break;
	}
}

static struct s_tb_button *button_init(struct s_toolbar *tb, OBJECT * tree, int index,
				       struct s_tb_button * instance)
{
	*instance = tb_buttons[index];
	instance->owner = tb;

	return(instance);
}


static short __CDECL toolbar_url_userdraw(PARMBLK *parmblock)
{
	return(0);
}

void toolbar_init( void )
{
	static USERBLK userblk;

	aes_toolbar = gemtk_obj_get_tree(TOOLBAR);
	throbber_form = gemtk_obj_get_tree(THROBBER);

	userblk.ub_code = toolbar_url_userdraw;
	userblk.ub_parm = (long) aes_toolbar[TOOLBAR_AREA_URL].ob_spec.userblk;
	aes_toolbar[TOOLBAR_AREA_URL].ob_spec.userblk = &userblk;

	aes_toolbar[TOOLBAR_CB_SHOWALL].ob_state &= ~OS_SELECTED;
	aes_toolbar[TOOLBAR_CB_CASESENSE].ob_state &= ~OS_SELECTED;

	/* init default values: */
	gemtk_obj_set_str_safe(aes_toolbar, TOOLBAR_TB_SRCH, (char*)"");

	area_full_height = aes_toolbar->ob_height;
	area_search_height = aes_toolbar[TOOLBAR_AREA_SEARCH].ob_height;
	area_navigation_height = aes_toolbar[TOOLBAR_AREA_NAVIGATION].ob_height;

	init = true;
}


void toolbar_exit(void)
{

}


struct s_toolbar *toolbar_create(struct s_gui_win_root *owner)
{
	int i;
	struct s_toolbar *t;

	LOG("owner %p", owner);

	assert(init == true);

	t = calloc(1, sizeof(struct s_toolbar));

	assert(t);

	/* initialize the toolbar values: */
	t->owner = owner;
	t->style = 1;
	t->search_visible = false;
	t->visible = true;
	t->reflow = true;

	/* dublicate the form template: */
	t->form = gemtk_obj_tree_copy(aes_toolbar);


	/* count buttons and add them as components: */
	i = 0;
	while(tb_buttons[i].rsc_id > 0) {
		i++;
	}
	t->btcnt = i;
	t->buttons = malloc(t->btcnt * sizeof(struct s_tb_button));
	memset(t->buttons, 0, t->btcnt * sizeof(struct s_tb_button));
	for (i=0; i < t->btcnt; i++) {
		button_init(t, aes_toolbar, i, &t->buttons[i]);
	}

	/* create the url widget: */
	font_style_url.size =
		toolbar_styles[t->style].font_height_pt * FONT_SIZE_SCALE;

	textarea_flags ta_flags = TEXTAREA_INTERNAL_CARET;
	textarea_setup ta_setup;
	ta_setup.width = 300;
	ta_setup.height = t->form[TOOLBAR_AREA_URL].ob_height;
	ta_setup.pad_top = 0;
	ta_setup.pad_right = 4;
	ta_setup.pad_bottom = 0;
	ta_setup.pad_left = 4;
	ta_setup.border_width = 1;
	ta_setup.border_col = 0x000000;
	ta_setup.selected_text = 0xffffff;
	ta_setup.selected_bg = 0x000000;
	ta_setup.text = font_style_url;
	ta_setup.text.foreground = 0x000000;
	ta_setup.text.background = 0xffffff;
	t->url.textarea = textarea_create(ta_flags, &ta_setup,
					  tb_txt_callback, t);

	/* create the throbber widget: */
	t->throbber.index = THROBBER_INACTIVE_INDEX;
	t->throbber.max_index = THROBBER_MAX_INDEX;
	t->throbber.running = false;

	LOG("created toolbar: %p, root: %p, textarea: %p, throbber: %p",
            t, owner, t->url.textarea, &t->throbber);
	return( t );
}


void toolbar_destroy(struct s_toolbar *tb)
{
	free(tb->buttons);
	free(tb->form);

	textarea_destroy(tb->url.textarea);

	free(tb);
}

static int toolbar_calculate_height(struct s_toolbar *tb)
{
	int r = 0;

	if (tb->visible == false) {
		return(0);
	}

	r += area_navigation_height;

	if (tb->search_visible) {
		r += area_search_height;
	}

	return(r);
}

static void toolbar_reflow(struct s_toolbar *tb)
{
	int i;
	short offx, offy;

	// position toolbar areas:
	tb->form->ob_x = tb->area.g_x;
	tb->form->ob_y = tb->area.g_y;
	tb->form->ob_width = tb->area.g_w;
	tb->form->ob_height = toolbar_calculate_height(tb);

	// expand the "main" areas to the current width:
	tb->form[TOOLBAR_AREA_NAVIGATION].ob_width = tb->area.g_w;
	tb->form[TOOLBAR_AREA_SEARCH].ob_width = tb->area.g_w;

	if (tb->search_visible) {
		tb->form[TOOLBAR_AREA_SEARCH].ob_state &= ~OF_HIDETREE;
	} else {
		tb->form[TOOLBAR_AREA_SEARCH].ob_state |= OF_HIDETREE;

	}

	// align TOOLBAR_AREA_RIGHT IBOX at right edge:
	tb->form[TOOLBAR_AREA_RIGHT].ob_x = tb->area.g_w
		- tb->form[TOOLBAR_AREA_RIGHT].ob_width;

	// center the URL area:
	tb->form[TOOLBAR_AREA_URL].ob_width = tb->area.g_w
		- (tb->form[TOOLBAR_AREA_LEFT].ob_width
		   + tb->form[TOOLBAR_AREA_RIGHT].ob_width);

	// position throbber image above IBOX:
	objc_offset(tb->form, TOOLBAR_THROBBER_AREA, &offx, &offy);
	throbber_form[tb->throbber.index].ob_x = offx;
	throbber_form[tb->throbber.index].ob_y = offy;

	// align the search button:
	tb->form[TOOLBAR_SEARCH_ALIGN_RIGHT].ob_x = tb->area.g_w
		- tb->form[TOOLBAR_SEARCH_ALIGN_RIGHT].ob_width;

	// set button states:
	for (i=0; i < tb->btcnt; i++ ) {
		if (tb->buttons[i].state == button_off) {
			tb->form[tb->buttons[i].rsc_id].ob_state |= OS_DISABLED;
		} else if (tb->buttons[i].state == button_on) {
			tb->form[tb->buttons[i].rsc_id].ob_state &= ~OS_DISABLED;
		}
	}
	tb->reflow = false;
	// TODO: iterate through all other toolbars and set reflow = true
}

void toolbar_redraw(struct s_toolbar *tb, GRECT *clip)
{
	GRECT area, area_ro;

	if (tb->attached == false) {
		return;
	}

	if(tb->reflow == true)
		toolbar_reflow(tb);

	//dbg_grect("toolbar redraw clip", clip);

	/* Redraw the AES objects: */
	objc_draw_grect(tb->form,0,8,clip);
	objc_draw_grect(&throbber_form[tb->throbber.index], 0, 1, clip);

	toolbar_get_grect(tb, TOOLBAR_AREA_URL, &area_ro);
	area = area_ro;

	if (rc_intersect(clip, &area)) {
		float old_scale;

		plot_set_dimensions(area_ro.g_x, area_ro.g_y, area_ro.g_w, area_ro.g_h);
		struct rect r = {
			.x0 = MAX(0,area.g_x - area_ro.g_x),
			.y0 = MAX(0,area.g_y - area_ro.g_y),
			.x1 = MAX(0,area.g_x - area_ro.g_x) + area.g_w,
			.y1 = MAX(0,area.g_y - area_ro.g_y) + area.g_h
		};
		//dbg_rect("tb textarea clip: ", &r);
		// TODO: let this be handled by an userdef object redraw function:
		/* Redraw the url input: */
		old_scale = plot_set_scale(toolbar_url_scale);
		textarea_redraw(tb->url.textarea, 0, 0, 0xffffff, 1.0, &r,
				&toolbar_rdrw_ctx);
		plot_set_scale(old_scale);
	}
}


void
toolbar_update_buttons(struct s_toolbar *tb,
                       struct browser_window *bw,
                       short button)
{
        LOG("tb %p", tb);

	struct s_tb_button * bt;
	bool enable = false;
	GRECT area;

	assert(bw != NULL);

	if (button == TOOLBAR_BT_BACK || button <= 0 ) {
		bt = find_button(tb, TOOLBAR_BT_BACK);
		enable = browser_window_back_available(bw);
		if (enable) {
			bt->state = button_on;
		} else {
			bt->state = button_off;
		}
	}

	if (button == TOOLBAR_BT_HOME || button <= 0 ) {

	}

	if (button == TOOLBAR_BT_FORWARD || button <= 0 ) {
		bt = find_button(tb, TOOLBAR_BT_FORWARD);
		enable = browser_window_forward_available(bw);
		if (enable) {
			bt->state = button_on;
		} else {
			bt->state = button_off;
		}
	}

	if (button == TOOLBAR_BT_RELOAD || button <= 0 ) {
		bt = find_button(tb, TOOLBAR_BT_RELOAD);
		enable = browser_window_reload_available(bw);
		if (enable) {
			bt->state = button_on;
		} else {
			bt->state = button_off;
		}
	}

	if (button == TOOLBAR_BT_STOP || button <= 0) {
		bt = find_button(tb, TOOLBAR_BT_STOP);
		enable = browser_window_stop_available(bw);
		if (enable) {
			bt->state = button_on;
		} else {
			bt->state = button_off;
		}
	}

	if (tb->attached) {
		if (button > 0) {
			toolbar_get_grect(tb, button, &area);
			window_schedule_redraw_grect(tb->owner, &area);
		} else {
			toolbar_get_grect(tb, TOOLBAR_AREA_LEFT, &area);
			window_schedule_redraw_grect(tb->owner, &area);

			toolbar_get_grect(tb, TOOLBAR_AREA_RIGHT, &area);
			window_schedule_redraw_grect(tb->owner, &area);
		}
	}
}

void toolbar_set_width(struct s_toolbar *tb, short w)
{
	GRECT cur;

	toolbar_get_grect(tb, 0, &cur);

	if (w != cur.g_w) {

		tb->area.g_w = w;

		/* reflow now, just for url input calucation: */
		toolbar_reflow(tb);
		/* this will request an textarea redraw: */
		textarea_set_dimensions(tb->url.textarea,
					tb->form[TOOLBAR_AREA_URL].ob_width,
					tb->form[TOOLBAR_AREA_URL].ob_height);
		tb->reflow = true;
	}
}

void toolbar_set_origin(struct s_toolbar *tb, short x, short y)
{
	GRECT cur;

	toolbar_get_grect(tb, 0, &cur);

	if (x != cur.g_x || y != cur.g_y) {
		tb->area.g_x = x;
		tb->area.g_y = y;
		tb->reflow = true;
	}
}

void toolbar_set_dimensions(struct s_toolbar *tb, GRECT *area)
{
	if (area->g_w != tb->area.g_w)  {

		tb->area = *area;

		/* reflow now, just for url input calucation: */
		toolbar_reflow(tb);
		/* this will request an textarea redraw: */
		textarea_set_dimensions(tb->url.textarea,
					tb->form[TOOLBAR_AREA_URL].ob_width,
					tb->form[TOOLBAR_AREA_URL].ob_height-1);
	}
	else {
		tb->area = *area;
	}
	/* reflow for next redraw: */
	/* TODO: that's only required because we do not reset others toolbars reflow
	   state on reflow */
	tb->reflow = true;
}


void toolbar_set_url(struct s_toolbar *tb, const char *text)
{
	LOG("tb %p", tb);

	textarea_set_text(tb->url.textarea, text);

	if (tb->attached && tb->visible) {
		GRECT area;
		toolbar_get_grect(tb, TOOLBAR_AREA_URL, &area);
		window_schedule_redraw_grect(tb->owner, &area);
		struct gui_window * gw = window_get_active_gui_window(tb->owner);
		assert(gw != NULL);
		toolbar_update_buttons(tb, gw->browser->bw , 0);
	}
}

void toolbar_set_throbber_state(struct s_toolbar *tb, bool active)
{
	GRECT throbber_area;

	tb->throbber.running = active;
	if (active) {
		tb->throbber.index = THROBBER_MIN_INDEX;
	} else {
		tb->throbber.index = THROBBER_INACTIVE_INDEX;
	}

	tb->reflow = true;
	toolbar_get_grect(tb, TOOLBAR_THROBBER_AREA, &throbber_area);
	window_schedule_redraw_grect(tb->owner, &throbber_area);
}

void toolbar_set_visible(struct s_toolbar *tb, short area, bool visible)
{
	if (area == 0) {
		if ((visible == false) && (tb->visible == true)) {
			tb->visible = false;
			tb->reflow = true;
		} else if((visible == true) && (tb->visible == false)) {
			tb->visible = false;
			tb->reflow = true;
		}
	} else if (area == TOOLBAR_AREA_SEARCH) {
		tb->search_visible = visible;
		tb->reflow = true;
		OBJECT *frm = toolbar_get_form(tb);
		if(visible == false){
			frm[TOOLBAR_AREA_SEARCH].ob_flags |= OF_HIDETREE;
		} else {
			frm[TOOLBAR_AREA_SEARCH].ob_flags &= ~OF_HIDETREE;
		}
	}
}

void toolbar_set_reflow(struct s_toolbar *tb, bool do_reflow)
{
	tb->reflow = do_reflow;
}

void toolbar_set_attached(struct s_toolbar *tb, bool attached)
{
	tb->attached = attached;

}

void toolbar_throbber_progress(struct s_toolbar *tb)
{
	GRECT throbber_area;

	assert(tb->throbber.running == true);

	if(tb->throbber.running == false)
		return;

	tb->throbber.index++;
	if (tb->throbber.index > THROBBER_MAX_INDEX) {
		tb->throbber.index = THROBBER_MIN_INDEX;
	}

	tb->reflow = true;
	toolbar_get_grect(tb, TOOLBAR_THROBBER_AREA, &throbber_area);
	window_schedule_redraw_grect(tb->owner, &throbber_area);
}

bool toolbar_text_input(struct s_toolbar *tb, char *text)
{
	bool handled = true;

	LOG("tb %p", tb);

	return(handled);
}

bool toolbar_key_input(struct s_toolbar *tb, short nkc)
{

	assert(tb!=NULL);
	bool ret = false;
	struct gui_window *gw = window_get_active_gui_window(tb->owner);

	assert( gw != NULL );

	long ucs4;
	long ik = nkc_to_input_key(nkc, &ucs4);

	if (ik == 0) {
		if ((nkc&0xFF) >= 9) {
			ret = textarea_keypress(tb->url.textarea, ucs4);
		}

	} else if (ik == NS_KEY_CR || ik == NS_KEY_NL) {
		nsurl *url;
		char tmp_url[PATH_MAX];
		if ( textarea_get_text( tb->url.textarea, tmp_url, PATH_MAX) > 0 ) {
			window_set_focus(tb->owner, BROWSER, gw->browser);
			if (nsurl_create((const char*)&tmp_url, &url) != NSERROR_OK) {
				atari_warn_user("NoMemory", 0);
			} else {
				browser_window_navigate(gw->browser->bw, url, NULL,
							BW_NAVIGATE_HISTORY,
							NULL, NULL, NULL);
				nsurl_unref(url);
			}

			ret = true;
		}

	} else if (ik == NS_KEY_COPY_SELECTION) {
		// copy whole text
		char * text;
		int len;
		len = textarea_get_text( tb->url.textarea, NULL, 0 );
		text = malloc( len+1 );
		if (text){
			textarea_get_text( tb->url.textarea, text, len+1 );
			scrap_txt_write(text);
			free( text );
		}

	} else if ( ik == NS_KEY_PASTE) {
		char * clip = scrap_txt_read();
		if ( clip != NULL ){
			int clip_length = strlen( clip );
			if ( clip_length > 0 ) {
				char *utf8;
				nserror res;
				/* Clipboard is in local encoding so
				 * convert to UTF8 */
				res = utf8_from_local_encoding( clip, clip_length, &utf8 );
				if ( res == NSERROR_OK ) {
					toolbar_set_url(tb, utf8);
					free(utf8);
					ret = true;
				}
			}
			free( clip );
		}

	} else if (ik == NS_KEY_ESCAPE) {
		textarea_keypress( tb->url.textarea, NS_KEY_SELECT_ALL );
		textarea_keypress( tb->url.textarea, NS_KEY_DELETE_LEFT );

	} else {
		ret = textarea_keypress( tb->url.textarea, ik );

	}

	return( ret );
}


void toolbar_mouse_input(struct s_toolbar *tb, short obj, short button)
{
	GRECT work;
	short mx, my, mb, kstat;
	struct gui_window * gw;

        LOG("tb %p", tb);


	if (obj==TOOLBAR_AREA_URL) {
		graf_mkstate(&mx, &my, &mb,  &kstat);
		toolbar_get_grect(tb, TOOLBAR_AREA_URL, &work);
		mx -= work.g_x;
		my -= work.g_y;

		/* TODO: reset mouse state of browser window? */
		/* select whole text when newly focused, otherwise set caret to
		   end of text */
		if (!window_url_widget_has_focus(tb->owner)) {
			window_set_focus(tb->owner, URL_WIDGET, (void*)&tb->url);

		} else if (mb & 1) {
			/* url widget has focus and mouse button is still pressed... */

			textarea_mouse_action(tb->url.textarea, BROWSER_MOUSE_DRAG_1,
					      mx, my );
			short prev_x = mx;
			short prev_y = my;
			do {
				if (abs(prev_x-mx) > 5 || abs(prev_y-my) > 5) {
					textarea_mouse_action( tb->url.textarea,
							       BROWSER_MOUSE_HOLDING_1, mx, my );
					prev_x = mx;
					prev_y = my;
					window_schedule_redraw_grect(tb->owner, &work);
					window_process_redraws(tb->owner);
				}
				graf_mkstate( &mx, &my, &mb,  &kstat );
				mx = mx - (work.g_x);
				my = my - (work.g_y);
			} while (mb & 1);

			textarea_mouse_action( tb->url.textarea, BROWSER_MOUSE_HOVER, mx,
					       my);

		} else if (button & 2) {
			// TODO: open a context popup

		} else {
			/* when execution reaches here, mouse input is a click or dclick */
			/* TODO: recognize click + shitoolbar_update_buttonsft key */
			if (aes_event_out.emo_mclicks == 2 ) {
				textarea_mouse_action( tb->url.textarea,
						       BROWSER_MOUSE_DOUBLE_CLICK | BROWSER_MOUSE_CLICK_1, mx,
						       my);
				toolbar_get_grect(tb, TOOLBAR_AREA_URL, &work);
				window_schedule_redraw_grect(tb->owner, &work);
			} else {
				textarea_mouse_action(tb->url.textarea,
						      BROWSER_MOUSE_PRESS_1, mx, my );
			}
		}

	} else if(obj==TOOLBAR_TB_SRCH) {
		window_set_focus(tb->owner, SEARCH_INPUT, NULL);

	} else if (obj==TOOLBAR_BT_SEARCH_FWD) {
		gw = tb->owner->active_gui_window;
		assert(gw->search);
		nsatari_search_perform(gw->search, tb->form, SEARCH_FLAG_FORWARDS);

	} else if (obj==TOOLBAR_BT_SEARCH_BACK) {
		gw = tb->owner->active_gui_window;
		assert(gw->search);
		nsatari_search_perform(gw->search, tb->form, 0);

	} else if (obj==TOOLBAR_BT_CLOSE_SEARCH) {
		tb->form[TOOLBAR_BT_CLOSE_SEARCH].ob_state &= ~OS_SELECTED;
		window_close_search(tb->owner);
	} else {
		struct s_tb_button *bt = find_button(tb, obj);
		if (bt != NULL && bt->state != button_off) {
			bt->cb_click(tb);
			struct gui_window * gw = window_get_active_gui_window(tb->owner);
			toolbar_update_buttons(tb, gw->browser->bw, 0);
		}
	}
}


/**
 * Receive a specific region of the toolbar.
 * @param tb - the toolbar pointer
 * @param which - the area to retrieve:  0 to receive the workarea,
 all other values must be
 an resource ID of the TOOLBAR tree.
 * @param dst - GRECT pointer receiving the area.
 */

void toolbar_get_grect(struct s_toolbar *tb, short which, GRECT *dst)
{
#define LAST_TOOLBAR_AREA TOOLBAR_AREA_SEARCH

	if (tb->reflow == true) {
		toolbar_reflow(tb);
	}

	objc_offset(tb->form, which, &dst->g_x, &dst->g_y);

	dst->g_w = tb->form[which].ob_width;
	dst->g_h = tb->form[which].ob_height;
	//tb->form[which].ob_height;

	//printf("Toolbar get grect (%d): ", which);
	//dbg_grect("", dst);

#undef LAST_TOOLBAR_AREA
}


struct textarea *toolbar_get_textarea(struct s_toolbar *tb,
				      enum toolbar_textarea which)
{
	return(tb->url.textarea);
}

char *toolbar_get_url(struct s_toolbar *tb)
{
	char * c_url = NULL;
	int c_url_len = 0;

	c_url_len = textarea_get_text(tb->url.textarea, NULL, 0);

	if (c_url_len > -1) {
		c_url = malloc(c_url_len+1);
		textarea_get_text(tb->url.textarea, c_url, c_url_len+1);
	}

	return(c_url);
}

nsurl * toolbar_get_nsurl(struct s_toolbar * tb)
{
	nsurl * ns_url = NULL;
	char * c_url;

	c_url = toolbar_get_url(tb);
	if (c_url) {
		nsurl_create(c_url, &ns_url);
	}

	return(ns_url);
}


OBJECT *toolbar_get_form(struct s_toolbar *tb)
{
	return(tb->form);
}


/* public event handler */
void toolbar_back_click(struct s_toolbar *tb)
{
	struct browser_window * bw;
	struct gui_window * gw;

	gw = window_get_active_gui_window(tb->owner);
	assert(gw != NULL);
	bw = gw->browser->bw;
	assert(bw != NULL);

	if( browser_window_back_available(bw) ) {
		browser_window_history_back(bw, false);
	}
}

void toolbar_reload_click(struct s_toolbar *tb)
{
	struct browser_window * bw;
	struct gui_window * gw;

	gw = window_get_active_gui_window(tb->owner);
	assert(gw != NULL);
	bw = gw->browser->bw;
	assert(bw != NULL);

	browser_window_reload(bw, true);
}

void toolbar_forward_click(struct s_toolbar *tb)
{
	struct browser_window * bw;
	struct gui_window * gw;

	gw = window_get_active_gui_window(tb->owner);
	assert(gw != NULL);
	bw = gw->browser->bw;
	assert(bw != NULL);

	if (browser_window_forward_available(bw)) {
		browser_window_history_forward(bw, false);
	}
}

void toolbar_home_click(struct s_toolbar *tb)
{
	struct browser_window * bw;
	struct gui_window * gw;
	nsurl *url;
	char * use_url = NULL;

	gw = window_get_active_gui_window(tb->owner);
	assert(gw != NULL);
	bw = gw->browser->bw;
	assert(bw != NULL);

	use_url = nsoption_charp(homepage_url);
	if (use_url == NULL || strlen(use_url) == 0){
		use_url = (char*)"about:welcome";
	}

	if (nsurl_create(use_url, &url) != NSERROR_OK) {
		atari_warn_user("NoMemory", 0);
	} else {
		browser_window_navigate(bw,
					url,
					NULL,
					BW_NAVIGATE_HISTORY,
					NULL,
					NULL,
					NULL);
		nsurl_unref(url);
	}
}


void toolbar_stop_click(struct s_toolbar *tb)
{
	struct browser_window * bw;
	struct gui_window * gw;

	gw = window_get_active_gui_window(tb->owner);

	assert(gw != NULL);

	bw = gw->browser->bw;

	assert(bw != NULL);

	browser_window_stop(bw);
}

void toolbar_favorite_click(struct s_toolbar *tb)
{
	nsurl * ns_url = NULL;
	char * c_url;
	int c_url_len = 0;

	c_url = toolbar_get_url(tb);
	c_url_len = strlen(c_url);

	nsurl_create(c_url, &ns_url);

	if (hotlist_has_url(ns_url)) {
		char msg[c_url_len+100];
		snprintf(msg, c_url_len+100, "Really delete from favorites: \"%s\"",
			 c_url);
		if (gemtk_msg_box_show(GEMTK_MSG_BOX_CONFIRM, msg)) {
			hotlist_remove_url(ns_url);
		}

	} else {
		hotlist_add_url(ns_url);

	}

	nsurl_unref(ns_url);
	free(c_url);
}

void toolbar_crypto_click(struct s_toolbar *tb)
{

}
