/*
 * Copyright 2015 Adrián Arroyo Calle <adrian.arroyocalle@gmail.com>
 * Copyright 2008 François Revol <mmu_man@users.sourceforge.net>
 * Copyright 2006 Rob Kendrick <rjek@rjek.com>
 *
 * 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/>.
 */

#define __STDBOOL_H__	1
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <BeBuild.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <Dragger.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <Node.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Resources.h>
#include <Roster.h>
#include <Screen.h>
#include <ScrollView.h>
#include <String.h>
#include <StringView.h>
#include <TextControl.h>
#include <View.h>
#include <Window.h>

#if defined(__HAIKU__)
#include <IconUtils.h>
#include "WindowStack.h"
#endif

#include <fs_attr.h>

extern "C" {
#include "utils/messages.h"
#include "utils/utils.h"
#include "utils/log.h"
#include "utils/nsurl.h"
#include "utils/nsoption.h"
#include "netsurf/netsurf.h"
#include "netsurf/plotters.h"
#include "netsurf/clipboard.h"
#include "netsurf/content.h"
#include "netsurf/content_type.h"
#include "netsurf/browser_window.h"
#include "netsurf/form.h"
#include "netsurf/keypress.h"
#include "desktop/browser_history.h"
#include "desktop/version.h"
#include "desktop/searchweb.h"
#include "desktop/search.h"
}

#include "beos/about.h"
#include "beos/bitmap.h"
#include "beos/gui.h"
#include "beos/plotters.h"
#include "beos/scaffolding.h"
#include "beos/gui_options.h"
//#include "beos/completion.h"
#include "beos/throbber.h"
#include "beos/window.h"
#include "beos/schedule.h"
//#include "beos/download.h"
#include "beos/cookies.h"

#define TOOLBAR_HEIGHT 32
#define DRAGGER_WIDTH 8

struct beos_history_window;

class NSIconTextControl;
class NSBrowserWindow;
class NSThrobber;

struct beos_scaffolding {
	NSBrowserWindow		*window;	// top-level container object

	// top-level view, contains toolbar & top-level browser view
	NSBaseView		*top_view;

	BMenuBar		*menu_bar;

	BPopUpMenu		*popup_menu;

#ifdef ENABLE_DRAGGER
	BDragger		*dragger;
#endif

	BView			*tool_bar;

	BControl		*back_button;
	BControl		*forward_button;
	BControl		*stop_button;
	BControl		*reload_button;
	BControl		*home_button;

	NSIconTextControl	*url_bar;
	BTextControl	*search_bar;
	//BMenuField	*url_bar_completion;

	NSThrobber		*throbber;

	BStringView		*status_bar;

	BScrollView		*scroll_view;

	struct beos_history_window *history_window;

	int			throb_frame;
	struct gui_window	*top_level;
	int			being_destroyed;

	bool			fullscreen;

	/** Object under menu, or 0 if no object. */
	struct hlcache_handle		*current_menu_object;
};

struct beos_history_window {
	struct beos_scaffolding 	*g;
	BWindow		*window;

};

struct menu_events {
	const char *widget;
};

// passed to the replicant main thread
struct replicant_thread_info {
	char app[B_PATH_NAME_LENGTH];
	BString url;
	char *args[3];
};


static int open_windows = 0;		/**< current number of open browsers */
static NSBaseView *replicant_view = NULL; /**< if not NULL, the replicant View we are running NetSurf for */
static sem_id replicant_done_sem = -1;
static thread_id replicant_thread = -1;

static void nsbeos_window_update_back_forward(struct beos_scaffolding *);
static void nsbeos_throb(void *);
static int32 nsbeos_replicant_main_thread(void *_arg);

// in beos_gui.cpp
extern int main(int argc, char** argv);

// in fetch_rsrc.cpp
extern BResources *gAppResources;

// #pragma mark - class NSIconTextControl

#define ICON_WIDTH 16

class NSIconTextControl : public BTextControl {
public:
		NSIconTextControl(BRect frame, const char* name,
						const char* label, const char* initialText,
						BMessage* message,
						uint32 resizeMode
							= B_FOLLOW_LEFT | B_FOLLOW_TOP,
						uint32 flags
							= B_WILL_DRAW | B_NAVIGABLE | B_DRAW_ON_CHILDREN);
virtual	~NSIconTextControl();

virtual	void	FrameResized(float newWidth, float newHeight);
virtual void	Draw(BRect updateRect);
virtual void	DrawAfterChildren(BRect updateRect);
virtual void	AttachedToWindow();

void	SetBitmap(const BBitmap *bitmap);
void	FixupTextRect();

private:
	BPoint fIconOffset;
	BRect fIconFrame;
	const BBitmap *fIconBitmap;
};

NSIconTextControl::NSIconTextControl(BRect frame, const char* name,
						const char* label, const char* initialText,
						BMessage* message,
						uint32 resizeMode,
						uint32 flags)
	: BTextControl(frame, name, label, initialText, message, resizeMode, flags),
	fIconOffset(0,0),
	fIconBitmap(NULL)
{
	BRect r(Bounds());
	fIconFrame = r;
	fIconFrame.right = fIconFrame.left + ICON_WIDTH - 1;
	fIconFrame.bottom = fIconFrame.top + ICON_WIDTH - 1;
	fIconFrame.OffsetBy((int32)((r.IntegerHeight() - ICON_WIDTH + 3) / 2),
		(int32)((r.IntegerHeight() - ICON_WIDTH + 1) / 2));
	FixupTextRect();
}


NSIconTextControl::~NSIconTextControl()
{
	delete fIconBitmap;
}


void
NSIconTextControl::FrameResized(float newWidth, float newHeight)
{
	BTextControl::FrameResized(newWidth, newHeight);
	FixupTextRect();
}


void
NSIconTextControl::Draw(BRect updateRect)
{
	FixupTextRect();
	BTextControl::Draw(updateRect);
}


void
NSIconTextControl::DrawAfterChildren(BRect updateRect)
{
	BTextControl::DrawAfterChildren(updateRect);

	PushState();

	SetDrawingMode(B_OP_ALPHA);
	DrawBitmap(fIconBitmap, fIconFrame);

	//XXX: is this needed?
	PopState();
}


void
NSIconTextControl::AttachedToWindow()
{
	BTextControl::AttachedToWindow();
	FixupTextRect();
}


void
NSIconTextControl::SetBitmap(const BBitmap *bitmap)
{
	delete fIconBitmap;
	fIconBitmap = NULL;

	// keep a copy
	if (bitmap)
		fIconBitmap = new BBitmap(bitmap);
	// invalidate just the icon area
	Invalidate(fIconFrame);
}


void
NSIconTextControl::FixupTextRect()
{
	// FIXME: this flickers on resize, quite ugly
	BRect r(TextView()->TextRect());

	// don't fix the fix
	if (r.left > ICON_WIDTH)
		return;

	r.left += r.bottom - r.top;
	TextView()->SetTextRect(r);
}


#undef ICON_WIDTH

// #pragma mark - class NSResizeKnob

class NSResizeKnob : public BView {
public:
		NSResizeKnob(BRect frame, BView *target);
virtual	~NSResizeKnob();

virtual	void	MouseDown(BPoint where);
virtual	void	MouseUp(BPoint where);
virtual	void	MouseMoved(BPoint where, uint32 code,
							const BMessage* dragMessage);

virtual void	Draw(BRect updateRect);

void			SetBitmap(const BBitmap *bitmap);

private:
	const BBitmap *fBitmap;
	BView *fTarget;
	BPoint fOffset;
};

NSResizeKnob::NSResizeKnob(BRect frame, BView *target)
	: BView(frame, "NSResizeKnob", B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT, B_WILL_DRAW),
	fBitmap(NULL),
	fTarget(target),
	fOffset(-1, -1)
{
	SetViewColor(0, 255, 0);
}


NSResizeKnob::~NSResizeKnob()
{
}


void
NSResizeKnob::MouseDown(BPoint where)
{
	SetMouseEventMask(B_POINTER_EVENTS,
		B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
	fOffset = where;
}


void
NSResizeKnob::MouseUp(BPoint where)
{
	fOffset.Set(-1, -1);
}


void
NSResizeKnob::MouseMoved(BPoint where, uint32 code,
						const BMessage* dragMessage)
{
	if (fOffset.x >= 0) {
		fTarget->ResizeBy(where.x - fOffset.x, where.y - fOffset.y);
	}
}


void
NSResizeKnob::Draw(BRect updateRect)
{
	if (!fBitmap)
		return;
	DrawBitmap(fBitmap);
}


void
NSResizeKnob::SetBitmap(const BBitmap *bitmap)
{
	fBitmap = bitmap;
	Invalidate();
}


// #pragma mark - class NSThrobber

class NSThrobber : public BView {
public:
		NSThrobber(BRect frame);
virtual	~NSThrobber();

virtual void	MessageReceived(BMessage *message);
virtual void	Draw(BRect updateRect);
void			SetBitmap(const BBitmap *bitmap);

private:
	const BBitmap *fBitmap;
};

NSThrobber::NSThrobber(BRect frame)
	: BView(frame, "NSThrobber", B_FOLLOW_TOP | B_FOLLOW_RIGHT, B_WILL_DRAW),
	fBitmap(NULL)
{
}


NSThrobber::~NSThrobber()
{
}


void
NSThrobber::MessageReceived(BMessage *message)
{
	BView::MessageReceived(message);
}


void
NSThrobber::Draw(BRect updateRect)
{
	if (!fBitmap)
		return;
	DrawBitmap(fBitmap);
}


void
NSThrobber::SetBitmap(const BBitmap *bitmap)
{
	fBitmap = bitmap;
	Invalidate();
}


// #pragma mark - class NSBaseView


NSBaseView::NSBaseView(BRect frame)
	: BView(frame, "NetSurf", B_FOLLOW_ALL_SIDES, 
		0 /*B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS*/ /*| B_SUBPIXEL_PRECISE*/),
	fScaffolding(NULL)
{
}

NSBaseView::NSBaseView(BMessage *archive)
	: BView(archive),
	fScaffolding(NULL)
{
}


NSBaseView::~NSBaseView()
{
	//beos_warn_user("~NSBaseView()", NULL);
	if (replicated) {
		BMessage *message = new BMessage(B_QUIT_REQUESTED);
		nsbeos_pipe_message_top(message, NULL, fScaffolding);
		while (acquire_sem(replicant_done_sem) == EINTR);
		//debugger("plop");
		status_t status = -1;
		wait_for_thread(replicant_thread, &status);
	}
}


void
NSBaseView::MessageReceived(BMessage *message)
{
	switch (message->what) {
		case B_SIMPLE_DATA:
		case B_ABOUT_REQUESTED:
		case B_ARGV_RECEIVED:
		case B_REFS_RECEIVED:
		case B_COPY:
		case B_CUT:
		case B_PASTE:
		case B_SELECT_ALL:
		//case B_MOUSE_WHEEL_CHANGED:
		case B_UI_SETTINGS_CHANGED:
		// NetPositive messages
		case B_NETPOSITIVE_OPEN_URL:
		case B_NETPOSITIVE_BACK:
		case B_NETPOSITIVE_FORWARD:
		case B_NETPOSITIVE_HOME:
		case B_NETPOSITIVE_RELOAD:
		case B_NETPOSITIVE_STOP:
		case B_NETPOSITIVE_DOWN:
		case B_NETPOSITIVE_UP:
		// messages for top-level
		case 'back':
		case 'forw':
		case 'stop':
		case 'relo':
		case 'home':
		case 'urlc':
		case 'urle':
		case 'sear':
		case 'menu':
		case NO_ACTION:
		case HELP_OPEN_CONTENTS:
		case HELP_OPEN_GUIDE:
		case HELP_OPEN_INFORMATION:
		case HELP_OPEN_ABOUT:
		case HELP_OPEN_LICENCE:
		case HELP_LAUNCH_INTERACTIVE:
		case HISTORY_SHOW_LOCAL:
		case HISTORY_SHOW_GLOBAL:
		case HOTLIST_ADD_URL:
		case HOTLIST_SHOW:
		case COOKIES_SHOW:
		case COOKIES_DELETE:
		case BROWSER_PAGE:
		case BROWSER_PAGE_INFO:
		case BROWSER_PRINT:
		case BROWSER_NEW_WINDOW:
		case BROWSER_VIEW_SOURCE:
		case BROWSER_OBJECT:
		case BROWSER_OBJECT_INFO:
		case BROWSER_OBJECT_RELOAD:
		case BROWSER_OBJECT_SAVE:
		case BROWSER_OBJECT_EXPORT_SPRITE:
		case BROWSER_OBJECT_SAVE_URL_URI:
		case BROWSER_OBJECT_SAVE_URL_URL:
		case BROWSER_OBJECT_SAVE_URL_TEXT:
		case BROWSER_SAVE:
		case BROWSER_SAVE_COMPLETE:
		case BROWSER_EXPORT_DRAW:
		case BROWSER_EXPORT_TEXT:
		case BROWSER_SAVE_URL_URI:
		case BROWSER_SAVE_URL_URL:
		case BROWSER_SAVE_URL_TEXT:
		case HOTLIST_EXPORT:
		case HISTORY_EXPORT:
		case BROWSER_NAVIGATE_HOME:
		case BROWSER_NAVIGATE_BACK:
		case BROWSER_NAVIGATE_FORWARD:
		case BROWSER_NAVIGATE_UP:
		case BROWSER_NAVIGATE_RELOAD:
		case BROWSER_NAVIGATE_RELOAD_ALL:
		case BROWSER_NAVIGATE_STOP:
		case BROWSER_NAVIGATE_URL:
		case BROWSER_SCALE_VIEW:
		case BROWSER_FIND_TEXT:
		case BROWSER_IMAGES_FOREGROUND:
		case BROWSER_IMAGES_BACKGROUND:
		case BROWSER_BUFFER_ANIMS:
		case BROWSER_BUFFER_ALL:
		case BROWSER_SAVE_VIEW:
		case BROWSER_WINDOW_DEFAULT:
		case BROWSER_WINDOW_STAGGER:
		case BROWSER_WINDOW_COPY:
		case BROWSER_WINDOW_RESET:
		case TREE_NEW_FOLDER:
		case TREE_NEW_LINK:
		case TREE_EXPAND_ALL:
		case TREE_EXPAND_FOLDERS:
		case TREE_EXPAND_LINKS:
		case TREE_COLLAPSE_ALL:
		case TREE_COLLAPSE_FOLDERS:
		case TREE_COLLAPSE_LINKS:
		case TREE_SELECTION:
		case TREE_SELECTION_EDIT:
		case TREE_SELECTION_LAUNCH:
		case TREE_SELECTION_DELETE:
		case TREE_SELECT_ALL:
		case TREE_CLEAR_SELECTION:
		case TOOLBAR_BUTTONS:
		case TOOLBAR_ADDRESS_BAR:
		case TOOLBAR_THROBBER:
		case TOOLBAR_EDIT:
		case CHOICES_SHOW:
		case APPLICATION_QUIT:
			if (Window())
				Window()->DetachCurrentMessage();
			nsbeos_pipe_message_top(message, NULL, fScaffolding);
			break;
		default:
			//message->PrintToStream();
			BView::MessageReceived(message);
	}
}


status_t
NSBaseView::Archive(BMessage *archive, bool deep) const
{
	// force archiving only the base view
	deep = false;
	status_t err;
	err = BView::Archive(archive, deep);
	if (err < B_OK)
		return err;
	// add our own fields
	// we try to reuse the same fields as NetPositive
	archive->AddString("add_on", "application/x-vnd.NetSurf");
	//archive->AddInt32("version", 2);
	archive->AddString("url", fScaffolding->url_bar->Text());
	archive->AddBool("openAsText", false);
	archive->AddInt32("encoding", 258);
	return err;
}


BArchivable	*
NSBaseView::Instantiate(BMessage *archive)
{
	if (!validate_instantiation(archive, "NSBaseView"))
		return NULL;
	const char *url;
	if (archive->FindString("url", &url) < B_OK
		|| url == NULL || strlen(url) == 0) {
		url = "about:";
	}

	struct replicant_thread_info *info = new replicant_thread_info;
	info->url = BString(url);
	if (nsbeos_find_app_path(info->app) < B_OK) {
                delete info;
		return NULL;
        }
	info->args[0] = info->app;
	info->args[1] = (char *)info->url.String();
	info->args[2] = NULL;
	NSBaseView *view = new NSBaseView(archive);
	replicant_view = view;
	replicated = true;

	//TODO:FIXME: fix replicants
	// do as much as possible in this thread to avoid deadlocks
	
	gui_init_replicant(2, info->args);

	replicant_done_sem = create_sem(0, "NS Replicant created");
	replicant_thread = spawn_thread(nsbeos_replicant_main_thread,
		"NetSurf Main Thread", B_NORMAL_PRIORITY, info);
	if (replicant_thread < B_OK) {
		delete_sem(replicant_done_sem);
		delete info;
		delete view;
		return NULL;
	}
	resume_thread(replicant_thread);
	//XXX: deadlocks BeHappy
	//while (acquire_sem(replicant_done_sem) == EINTR);

	return view;
}


void
NSBaseView::SetScaffolding(struct beos_scaffolding *scaf)
{
	fScaffolding = scaf;
}


// AttachedToWindow() is not enough to get the dragger and status bar
// stick to the panel color
void
NSBaseView::AllAttached()
{
	BView::AllAttached();

	struct beos_scaffolding *g = fScaffolding;
	if (!g)
		return;
	// set targets to the topmost ns view
	g->back_button->SetTarget(this);
	g->forward_button->SetTarget(this);
	g->stop_button->SetTarget(this);
	g->reload_button->SetTarget(this);
	g->home_button->SetTarget(this);

	rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR);
	SetViewColor(c);

	g->tool_bar->SetViewColor(c);
	g->back_button->SetViewColor(c);
	g->back_button->SetLowColor(c);
	g->forward_button->SetViewColor(c);
	g->forward_button->SetLowColor(c);
	g->stop_button->SetViewColor(c);
	g->stop_button->SetLowColor(c);
	g->reload_button->SetViewColor(c);
	g->reload_button->SetLowColor(c);
	g->home_button->SetViewColor(c);
	g->home_button->SetLowColor(c);
	g->url_bar->SetViewColor(c);
	g->search_bar->SetViewColor(c);
	g->throbber->SetViewColor(c);
	g->scroll_view->SetViewColor(c);

#ifdef ENABLE_DRAGGER
	g->dragger->SetViewColor(c);
#endif

	g->status_bar->SetViewColor(c);
	g->status_bar->SetLowColor(c);
#if defined(__HAIKU__) || defined(B_DANO_VERSION)
	g->status_bar->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
#endif
}


// #pragma mark - class NSBrowserWindow


NSBrowserWindow::NSBrowserWindow(BRect frame, struct beos_scaffolding *scaf)
	: BWindow(frame, "NetSurf", B_DOCUMENT_WINDOW, 0),
	fScaffolding(scaf)
{
}


NSBrowserWindow::~NSBrowserWindow()
{
	if(activeWindow == this)
		activeWindow = NULL;
}


void
NSBrowserWindow::DispatchMessage(BMessage *message, BHandler *handler)
{
	BMessage *msg;
	switch (message->what) {
		case B_UI_SETTINGS_CHANGED:
			msg = new BMessage(*message);
			nsbeos_pipe_message_top(msg, this, fScaffolding);
			break;
	}
	BWindow::DispatchMessage(message, handler);
}


void
NSBrowserWindow::MessageReceived(BMessage *message)
{
	switch (message->what) {
		case B_ARGV_RECEIVED:
		case B_REFS_RECEIVED:
		case B_UI_SETTINGS_CHANGED:
		// NetPositive messages
		case B_NETPOSITIVE_OPEN_URL:
		case B_NETPOSITIVE_BACK:
		case B_NETPOSITIVE_FORWARD:
		case B_NETPOSITIVE_HOME:
		case B_NETPOSITIVE_RELOAD:
		case B_NETPOSITIVE_STOP:
		case B_NETPOSITIVE_DOWN:
		case B_NETPOSITIVE_UP:
			DetachCurrentMessage();
			nsbeos_pipe_message_top(message, this, fScaffolding);
			break;
		default:
			BWindow::MessageReceived(message);
	}
}

bool
NSBrowserWindow::QuitRequested(void)
{
	BWindow::QuitRequested();
	BMessage *message = DetachCurrentMessage();
	// BApplication::Quit() calls us directly...
	if (message == NULL)
		message = new BMessage(B_QUIT_REQUESTED);
	nsbeos_pipe_message_top(message, this, fScaffolding);
	return false; // we will Quit() ourselves from the main thread
}


void
NSBrowserWindow::WindowActivated(bool active)
{
	if(active)
		activeWindow = this;
	else if(activeWindow == this)
		activeWindow = NULL;
}


// #pragma mark - implementation

int32 nsbeos_replicant_main_thread(void *_arg)
{
	struct replicant_thread_info *info = (struct replicant_thread_info *)_arg;
	int32 ret = 0;

	while (!nsbeos_done) {
		nsbeos_gui_poll();
	}

	netsurf_exit();
	delete info;
	delete_sem(replicant_done_sem);
	return ret;
}


/* event handlers and support functions for them */

static void nsbeos_window_destroy_event(NSBrowserWindow *window, nsbeos_scaffolding *g, BMessage *event)
{
	NSLOG(netsurf, INFO, "Being Destroyed = %d", g->being_destroyed);

	if (--open_windows == 0)
		nsbeos_done = true;

	if (window) {
		window->Lock();
		window->Quit();
	}

	if (!g->being_destroyed) {
		g->being_destroyed = 1;
		nsbeos_window_destroy_browser(g->top_level);
	}
}


static void nsbeos_scaffolding_update_colors(nsbeos_scaffolding *g)
{
	if (!g->top_view->LockLooper())
		return;
	rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR);
	g->top_view->SetViewColor(c);

	g->tool_bar->SetViewColor(c);
	g->back_button->SetViewColor(c);
	g->forward_button->SetViewColor(c);
	g->stop_button->SetViewColor(c);
	g->reload_button->SetViewColor(c);
	g->home_button->SetViewColor(c);
	g->url_bar->SetViewColor(c);
	g->search_bar->SetViewColor(c);
	g->throbber->SetViewColor(c);
	g->scroll_view->SetViewColor(c);

#ifdef ENABLE_DRAGGER
	g->dragger->SetViewColor(c);
#endif

	g->status_bar->SetViewColor(c);
	g->status_bar->SetLowColor(c);
#if defined(__HAIKU__) || defined(B_DANO_VERSION)
	g->status_bar->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
#endif
	g->top_view->UnlockLooper();
}


/*static*/ BWindow*
NSBrowserWindow::activeWindow = NULL;


void nsbeos_scaffolding_dispatch_event(nsbeos_scaffolding *scaffold, BMessage *message)
{
	struct browser_window *bw;
	bw = nsbeos_get_browser_for_gui(scaffold->top_level);
	bool reloadAll = false;

	NSLOG(netsurf, INFO,
	      "nsbeos_scaffolding_dispatch_event() what = 0x%08lx",
	      message->what);
	switch (message->what) {
		case B_QUIT_REQUESTED:
			nsbeos_scaffolding_destroy(scaffold);
			break;
		case B_ABOUT_REQUESTED:
		{
			nsbeos_about(scaffold->top_level);
			break;
		}
		case B_NETPOSITIVE_DOWN:
			//XXX WRITEME
			break;
		case B_SIMPLE_DATA:
		{
			if (!message->HasRef("refs")) {
				// XXX handle DnD
				break;
			}
			// FALL THROUGH
			// handle refs
		}
		case B_REFS_RECEIVED:
		{
			int32 i;
			entry_ref ref;

			for (i = 0; message->FindRef("refs", i, &ref) >= B_OK; i++) {
				BString url("file://");
				BPath path(&ref);
				if (path.InitCheck() < B_OK)
					break;

				BNode node(path.Path());
				if (node.InitCheck() < B_OK)
					break;
				if (node.IsSymLink()) {
					// dereference the symlink
					BEntry entry(path.Path(), true);
					if (entry.InitCheck() < B_OK)
						break;
					if (entry.GetPath(&path) < B_OK)
						break;
					if (node.SetTo(path.Path()) < B_OK)
						break;
				}

				attr_info ai;
				if (node.GetAttrInfo("META:url", &ai) >= B_OK) {
					char data[(size_t)ai.size + 1];
					memset(data, 0, (size_t)ai.size + 1);
					if (node.ReadAttr("META:url", B_STRING_TYPE, 0LL, data, (size_t)ai.size) < 4)
						break;
					url = data;
				} else
					url << path.Path();

                nsurl *nsurl;
                nserror error;

                error = nsurl_create(url.String(), &nsurl);
				if (error == NSERROR_OK) {
					if (/*message->WasDropped() &&*/ i == 0) {
						browser_window_navigate(bw, nsurl, NULL,
							(browser_window_nav_flags)
							(BW_NAVIGATE_HISTORY),
							NULL, NULL, NULL);
					} else {
						error = browser_window_create(BW_CREATE_CLONE,
								nsurl,
								NULL,
								bw,
								NULL);
					}
					nsurl_unref(nsurl);
				}
				if (error != NSERROR_OK) {
					beos_warn_user(messages_get_errorcode(error), 0);
				}
			}
			break;
		}
		case B_ARGV_RECEIVED:
		{
			int32 i;
			BString urltxt;
                        nsurl *url;
                        nserror error;

			for (i = 1; message->FindString("argv", i, &urltxt) >= B_OK; i++) {
                                error = nsurl_create(urltxt.String(), &url);
                                if (error == NSERROR_OK) {
                                        error = browser_window_create(BW_CREATE_CLONE,
                                                                      url,
                                                                      NULL,
                                                                      bw,
                                                                      NULL);
                                        nsurl_unref(url);
                                }
                                if (error != NSERROR_OK) {
                                        beos_warn_user(messages_get_errorcode(error), 0);
                                }
			}
			break;
		}
		case B_UI_SETTINGS_CHANGED:
			nsbeos_update_system_ui_colors();
			nsbeos_scaffolding_update_colors(scaffold);
			break;
		case B_NETPOSITIVE_OPEN_URL:
		{
			BString url;
			if (message->FindString("be:url", &url) < B_OK)
				break;

			nsurl *nsurl;
			nserror error;

			error = nsurl_create(url.String(), &nsurl);
			if (error != NSERROR_OK) {
				beos_warn_user(messages_get_errorcode(error), 0);
			} else {
				browser_window_navigate(bw,
						nsurl,
						NULL,
						(browser_window_nav_flags)(BW_NAVIGATE_HISTORY | BW_NAVIGATE_UNVERIFIABLE),
						NULL,
						NULL,
						NULL);
				nsurl_unref(nsurl);
			}
			break;
		}
		case B_COPY:
			browser_window_key_press(bw, NS_KEY_COPY_SELECTION);
			break;
		case B_CUT:
			browser_window_key_press(bw, NS_KEY_CUT_SELECTION);
			break;
		case B_PASTE:
			browser_window_key_press(bw, NS_KEY_PASTE);
			break;
		case B_SELECT_ALL:
			NSLOG(netsurf, INFO, "Selecting all text");
			browser_window_key_press(bw, NS_KEY_SELECT_ALL);
			break;
		case B_NETPOSITIVE_BACK:
		case BROWSER_NAVIGATE_BACK:
		case 'back':
			if (!browser_window_history_back_available(bw))
				break;
			browser_window_history_back(bw, false);
			nsbeos_window_update_back_forward(scaffold);
			break;
		case B_NETPOSITIVE_FORWARD:
		case BROWSER_NAVIGATE_FORWARD:
		case 'forw':
			if (!browser_window_history_forward_available(bw))
				break;
			browser_window_history_forward(bw, false);
			nsbeos_window_update_back_forward(scaffold);
			break;
		case B_NETPOSITIVE_STOP:
		case BROWSER_NAVIGATE_STOP:
		case 'stop':
			browser_window_stop(bw);
			break;
		case B_NETPOSITIVE_RELOAD:
		case BROWSER_NAVIGATE_RELOAD_ALL:
		case 'relo':
			reloadAll = true;
			// FALLTHRU
		case BROWSER_NAVIGATE_RELOAD:
			browser_window_reload(bw, reloadAll);
			break;
		case B_NETPOSITIVE_HOME:
		case BROWSER_NAVIGATE_HOME:
		case 'home':
		{
			nsurl *url;
			nserror error;

			static const char *addr = NETSURF_HOMEPAGE;

			if (nsoption_charp(homepage_url) != NULL) {
				addr = nsoption_charp(homepage_url);
			}

			error = nsurl_create(addr, &url);
			if (error != NSERROR_OK) {
				beos_warn_user(messages_get_errorcode(error), 0);
			} else {
				browser_window_navigate(bw,
					url,
					NULL,
					(browser_window_nav_flags)(BW_NAVIGATE_HISTORY),
					NULL,
					NULL,
					NULL);
				nsurl_unref(url);
			}
			break;
		}
		case 'urle':
		{
            nsurl *url;
            nserror error;
			BString text;

			if (!scaffold->url_bar->LockLooper())
				break;

			text = scaffold->url_bar->Text();
			scaffold->scroll_view->Target()->MakeFocus();
			scaffold->url_bar->UnlockLooper();

                        error = nsurl_create(text.String(), &url);
                        if (error != NSERROR_OK) {
                                beos_warn_user(messages_get_errorcode(error), 0);
                        } else {
                                browser_window_navigate(bw,
					url,
					NULL,
					(browser_window_nav_flags)(BW_NAVIGATE_HISTORY),
					NULL,
					NULL,
					NULL);
                                nsurl_unref(url);
                        }
			break;
		}
		case 'urlc':
		{
			BString text;
			if (!scaffold->url_bar->LockLooper())
				break;
			text = scaffold->url_bar->Text();
			scaffold->url_bar->UnlockLooper();
			//nsbeos_completion_update(text.String());
			break;
		}
		case 'sear':
		{
			nserror ret;
			nsurl* url;
			BString text;
			if (!scaffold->search_bar->LockLooper())
				break;
			text = scaffold->search_bar->Text();
			scaffold->search_bar->UnlockLooper();

			char t[PATH_MAX];
			find_resource(t,"SearchEngines","./beos/res/SearchEngines");

			search_web_init(&t[0]);

			ret = search_web_omni(text.String(),SEARCH_WEB_OMNI_SEARCHONLY
				,&url);
			if (ret == NSERROR_OK) {
				ret = browser_window_create(
					(browser_window_create_flags)(BW_CREATE_HISTORY | BW_CREATE_TAB),
					url,
					NULL,
					bw,
					NULL);
				nsurl_unref(url);
			}

			if (ret != NSERROR_OK) {
				beos_warn_user(messages_get_errorcode(ret), 0);
			}

			search_web_finalise();

			break;
		}
/*
		case 'menu':
		{
			menu_action action;
			if (message->FindInt32("action", (int32 *)&action) < B_OK)
				break;
			switch (action) {
				case NO_ACTION:
				case HELP_OPEN_CONTENTS:
				case HELP_OPEN_GUIDE:
				case HELP_OPEN_INFORMATION:
				case HELP_OPEN_ABOUT:
				case HELP_LAUNCH_INTERACTIVE:

					break;
			}
#warning XXX
			break;
		}
*/
		case NO_ACTION:
			break;
		case HELP_OPEN_CONTENTS:
			break;
		case HELP_OPEN_GUIDE:
			break;
		case HELP_OPEN_INFORMATION:
			break;
		case HELP_OPEN_ABOUT:
		{
			const char *goto_url = "about:credits";
			nserror nserr;
			nsurl *url;
			nserr = nsurl_create(goto_url, &url);
			if (nserr == NSERROR_OK) {
				nserr = browser_window_navigate(bw,
		    			url, NULL,
						(browser_window_nav_flags)(BW_NAVIGATE_HISTORY),
					    NULL, NULL, NULL);
				nsurl_unref(url);
			}
			if (nserr != NSERROR_OK) {
				beos_warn_user(messages_get_errorcode(nserr), 0);
			}
		}
			break;
		case HELP_OPEN_LICENCE:
		{
			const char *goto_url = "about:licence";
			nserror nserr;
			nsurl *url;
			nserr = nsurl_create(goto_url, &url);
			if (nserr == NSERROR_OK) {
				nserr = browser_window_navigate(bw,
		    			url, NULL,
						(browser_window_nav_flags)(BW_NAVIGATE_HISTORY),
					    NULL, NULL, NULL);
				nsurl_unref(url);
			}
			if (nserr != NSERROR_OK) {
				beos_warn_user(messages_get_errorcode(nserr), 0);
			}
		}
			break;
		case HELP_LAUNCH_INTERACTIVE:
			break;
		case HISTORY_SHOW_LOCAL:
			break;
		case HISTORY_SHOW_GLOBAL:
			break;
		case HOTLIST_ADD_URL:
			break;
		case HOTLIST_SHOW:
			break;
		case COOKIES_SHOW:
		{
			nsbeos_cookies_init();
			break;
		}
		case COOKIES_DELETE:
		{
			nsbeos_cookies_init();
			break;
		}
		case BROWSER_PAGE:
			break;
		case BROWSER_PAGE_INFO:
			break;
		case BROWSER_PRINT:
			break;
		case BROWSER_NEW_WINDOW:
		{
			BString text;
                        nsurl *url;
                        nserror error;

			if (!scaffold->url_bar->LockLooper())
				break;
			text = scaffold->url_bar->Text();
			scaffold->url_bar->UnlockLooper();

			NSBrowserWindow::activeWindow = scaffold->window;

                        error = nsurl_create(text.String(), &url);
                        if (error == NSERROR_OK) {
                                error = browser_window_create(BW_CREATE_CLONE,
                                                              url,
                                                              NULL,
                                                              bw,
                                                              NULL);
                                nsurl_unref(url);
                        }
                        if (error != NSERROR_OK) {
                                beos_warn_user(messages_get_errorcode(error), 0);
                        }
			break;
		}
		case BROWSER_VIEW_SOURCE:
		{
			if (!bw || browser_window_has_content(bw) == false)
				break;
			nsbeos_gui_view_source(browser_window_get_content(bw));
			break;
		}
		case BROWSER_OBJECT:
			break;
		case BROWSER_OBJECT_INFO:
			break;
		case BROWSER_OBJECT_RELOAD:
			if (scaffold->current_menu_object != NULL) {
				content_invalidate_reuse_data(scaffold->current_menu_object);
				browser_window_reload(bw, false);
				scaffold->current_menu_object = NULL;
			}
			break;
		case BROWSER_OBJECT_SAVE:
			break;
		case BROWSER_OBJECT_EXPORT_SPRITE:
			break;
		case BROWSER_OBJECT_SAVE_URL_URI:
			break;
		case BROWSER_OBJECT_SAVE_URL_URL:
			break;
		case BROWSER_OBJECT_SAVE_URL_TEXT:
			break;
		case BROWSER_SAVE:
			break;
		case BROWSER_SAVE_COMPLETE:
			break;
		case BROWSER_EXPORT_DRAW:
			break;
		case BROWSER_EXPORT_TEXT:
			break;
		case BROWSER_SAVE_URL_URI:
			break;
		case BROWSER_SAVE_URL_URL:
			break;
		case BROWSER_SAVE_URL_TEXT:
			break;
		case HOTLIST_EXPORT:
			break;
		case HISTORY_EXPORT:
			break;
		case B_NETPOSITIVE_UP:
		case BROWSER_NAVIGATE_UP:
			break;
		case BROWSER_NAVIGATE_URL:
			if (!scaffold->url_bar->LockLooper())
				break;
			scaffold->url_bar->MakeFocus();
			scaffold->url_bar->UnlockLooper();
			break;
		case BROWSER_SCALE_VIEW:
			break;
		case BROWSER_FIND_TEXT:
			break;
		case BROWSER_IMAGES_FOREGROUND:
			break;
		case BROWSER_IMAGES_BACKGROUND:
			break;
		case BROWSER_BUFFER_ANIMS:
			break;
		case BROWSER_BUFFER_ALL:
			break;
		case BROWSER_SAVE_VIEW:
			break;
		case BROWSER_WINDOW_DEFAULT:
			break;
		case BROWSER_WINDOW_STAGGER:
			break;
		case BROWSER_WINDOW_COPY:
			break;
		case BROWSER_WINDOW_RESET:
			break;
		case TREE_NEW_FOLDER:
		case TREE_NEW_LINK:
		case TREE_EXPAND_ALL:
		case TREE_EXPAND_FOLDERS:
		case TREE_EXPAND_LINKS:
		case TREE_COLLAPSE_ALL:
		case TREE_COLLAPSE_FOLDERS:
		case TREE_COLLAPSE_LINKS:
		case TREE_SELECTION:
		case TREE_SELECTION_EDIT:
		case TREE_SELECTION_LAUNCH:
		case TREE_SELECTION_DELETE:
		case TREE_SELECT_ALL:
		case TREE_CLEAR_SELECTION:
			break;
		case TOOLBAR_BUTTONS:
			break;
		case TOOLBAR_ADDRESS_BAR:
			break;
		case TOOLBAR_THROBBER:
			break;
		case TOOLBAR_EDIT:
			break;
		case CHOICES_SHOW:
			break;
		case APPLICATION_QUIT:
			nsbeos_done = true;
			break;
		default:
			break;
	}
}

void nsbeos_scaffolding_destroy(nsbeos_scaffolding *scaffold)
{
	NSLOG(netsurf, INFO, "Being Destroyed = %d",
	      scaffold->being_destroyed);
	if (scaffold->being_destroyed) return;
	scaffold->being_destroyed = 1;
	nsbeos_window_destroy_event(scaffold->window, scaffold, NULL);
}


void nsbeos_window_update_back_forward(struct beos_scaffolding *g)
{
	struct browser_window *bw = nsbeos_get_browser_for_gui(g->top_level);

	if (!g->top_view->LockLooper())
		return;

	g->back_button->SetEnabled(browser_window_history_back_available(bw));
	g->forward_button->SetEnabled(browser_window_history_forward_available(bw));

	g->top_view->UnlockLooper();

}

void nsbeos_throb(void *p)
{
	struct beos_scaffolding *g = (struct beos_scaffolding *)p;

	if (g->throb_frame >= (nsbeos_throbber->nframes - 1))
		g->throb_frame = 1;
	else
		g->throb_frame++;

	if (!g->top_view->LockLooper())
		return;

	g->throbber->SetBitmap(nsbeos_throbber->framedata[g->throb_frame]);
	g->throbber->Invalidate();

	g->top_view->UnlockLooper();

	beos_schedule(100, nsbeos_throb, p);

}


NSBrowserWindow *nsbeos_find_last_window(void)
{
	int32 i;
	if (!be_app || !be_app->Lock())
		return NULL;
	for (i = be_app->CountWindows() - 1; i >= 0; i--) {
		if (be_app->WindowAt(i) == NULL)
			continue;
		NSBrowserWindow *win;
		win = dynamic_cast<NSBrowserWindow *>(be_app->WindowAt(i));
		if (win) {
			win->Lock();
			be_app->Unlock();
			return win;
		}
	}
	be_app->Unlock();
	return NULL;
}

NSBrowserWindow *nsbeos_get_bwindow_for_scaffolding(nsbeos_scaffolding *scaffold)
{
	 return scaffold->window;
}

NSBaseView *nsbeos_get_baseview_for_scaffolding(nsbeos_scaffolding *scaffold)
{
	 return scaffold->top_view;
}

static void recursively_set_menu_items_target(BMenu *menu, BHandler *handler)
{
	menu->SetTargetForItems(handler);
	for (int i = 0; menu->ItemAt(i); i++) {
		if (!menu->SubmenuAt(i))
			continue;
		recursively_set_menu_items_target(menu->SubmenuAt(i), handler);
	}
}

void nsbeos_attach_toplevel_view(nsbeos_scaffolding *g, BView *view)
{
	NSLOG(netsurf, INFO, "Attaching view to scaffolding %p", g);

	// this is a replicant,... and it went bad
	if (!g->window) {
		if (g->top_view->Looper() && !g->top_view->LockLooper())
			return;
	}

	BRect rect(g->top_view->Bounds());
	rect.top += TOOLBAR_HEIGHT;
	rect.right -= B_V_SCROLL_BAR_WIDTH;
	rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
	
	view->ResizeTo(rect.Width() /*+ 1*/, rect.Height() /*+ 1*/);
	view->MoveTo(rect.LeftTop());


	g->scroll_view = new BScrollView("NetSurfScrollView", view, 
		B_FOLLOW_ALL, 0, true, true, B_NO_BORDER);

	g->top_view->AddChild(g->scroll_view);

	// for replicants, add a NSResizeKnob to allow resizing
	if (!g->window) {
		BRect frame = g->scroll_view->Bounds();
		frame.left = frame.right - B_V_SCROLL_BAR_WIDTH;
		frame.top = frame.bottom - B_H_SCROLL_BAR_HEIGHT;
		NSResizeKnob *knob = new NSResizeKnob(frame, g->top_view);
		//TODO: set bitmap
		g->scroll_view->AddChild(knob);
	}

	view->MakeFocus();

	// resize the horiz scrollbar to make room for the status bar and add it.

	BScrollBar *sb = g->scroll_view->ScrollBar(B_HORIZONTAL);
	rect = sb->Frame();
	float divider = rect.Width() + 1;
	//divider /= 2;
	divider *= 67.0/100; // 67%

	sb->ResizeBy(-divider, 0);
	sb->MoveBy(divider, 0);

	rect.right = rect.left + divider - 1;

	/*
	BBox *statusBarBox = new BBox(rect, "StatusBarBox", 
		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM,
		B_WILL_DRAW | B_FRAME_EVENTS,
		B_RAISED_BORDER);
	*/

	g->status_bar->MoveTo(rect.LeftTop());
	g->status_bar->ResizeTo(rect.Width() + 1, rect.Height() + 1);
	g->scroll_view->AddChild(g->status_bar);
	g->status_bar->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	g->status_bar->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)) ;
#if defined(__HAIKU__) || defined(B_DANO_VERSION)
	g->status_bar->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
#endif



	// set targets to the topmost ns view,
	// we might not have a window later (replicant ?)
	// this won't work for replicants, since the base view isn't attached yet
	// we'll redo this in NSBaseView::AllAttached
	g->back_button->SetTarget(view);
	g->forward_button->SetTarget(view);
	g->stop_button->SetTarget(view);
	g->reload_button->SetTarget(view);
	g->home_button->SetTarget(view);

	g->url_bar->SetTarget(view);
	g->search_bar->SetTarget(view);

	nsbeos_scaffolding_update_colors(g);

	recursively_set_menu_items_target(g->popup_menu, view);

	if (g->window) {
		recursively_set_menu_items_target(g->menu_bar, view);

		// add toolbar shortcuts
		BMessage *message;

		message = new BMessage('back');
		message->AddPointer("scaffolding", g);
		g->window->AddShortcut(B_LEFT_ARROW, 0, message, view);

		message = new BMessage('forw');
		message->AddPointer("scaffolding", g);
		g->window->AddShortcut(B_RIGHT_ARROW, 0, message, view);

		message = new BMessage('stop');
		message->AddPointer("scaffolding", g);
		g->window->AddShortcut('S', 0, message, view);

		message = new BMessage('relo');
		message->AddPointer("scaffolding", g);
		g->window->AddShortcut('R', 0, message, view);

		message = new BMessage('home');
		message->AddPointer("scaffolding", g);
		g->window->AddShortcut('H', 0, message, view);


#if defined(__HAIKU__)
		// Make sure the window is layouted and answering to events, but do not
		// show it before it is actually resized
		g->window->Hide();
		g->window->Show();

		if(NSBrowserWindow::activeWindow) {
			BWindowStack stack(NSBrowserWindow::activeWindow);
			stack.AddWindow(g->window);
		}
#endif
		g->window->Show();

	} else {
		if (g->top_view->Looper())
			g->top_view->UnlockLooper();
	}


}

static BMenuItem *make_menu_item(const char *name, BMessage *message, bool enabled=false)
{
	BMenuItem *item;
	BString label(messages_get(name));
	BString accelKey(name);
	accelKey += "Accel";
	BString accel(messages_get(accelKey));
	if (accel == accelKey)
		accel = "";
	uint32 mods = 0;
	char key = 0;
	// try to understand accelerators
	if (!accel.IsEmpty()) {
		if (accel.FindFirst("\xe2\x87\x91") > -1) {
			accel.RemoveFirst("\xe2\x87\x91");
			mods |= B_SHIFT_KEY;
		}
		if (accel.FindFirst("^") > -1) {
			accel.RemoveFirst("^");
			mods |= B_CONTROL_KEY;
		}
		if (accel.FindFirst("PRINT") > -1) {
			accel.RemoveFirst("PRINT");
			//mods |= ; // ALT!!!
			key = B_PRINT_KEY;
		}

		/* replace UTF-8 glyphs (arrows...) with API codes */
		accel.ReplaceAll("\xE2\x86\x90", (BString()+=B_LEFT_ARROW).String());
		accel.ReplaceAll("\xE2\x86\x92", (BString()+=B_RIGHT_ARROW).String());
		accel.ReplaceAll("\xE2\x86\x91", (BString()+=B_UP_ARROW).String());

		if (accel.Length() > 1 && accel[0] == 'F') { // Function key
			int num;
			if (sscanf(accel.String(), "F%d", &num) > 0) {
				//
			}
		} else if (accel.Length() > 0) {
			key = accel[0];
		}
		//printf("MENU: detected 	accel '%s' mods 0x%08lx, key %d\n", accel.String(), mods, key);
	}

	// turn ... into ellipsis
	label.ReplaceAll("...", B_UTF8_ELLIPSIS);

	item = new BMenuItem(label.String(), message, key, mods);

	item->SetEnabled(enabled);

	return item;
}


class BBitmapButton: public BButton
{
	public:
		BBitmapButton(BRect rect, const char* name, const char* label,
			BMessage* message);
		~BBitmapButton();

		void Draw(BRect updateRect);
		void SetBitmap(const char* attrName);
	private:
		BBitmap* fBitmap;
		BBitmap* fDisabledBitmap;
};


BBitmapButton::BBitmapButton(BRect rect, const char* name, const char* label,
		BMessage* message)
	: BButton(rect, name, label, message)
{
	SetBitmap(name);
}


BBitmapButton::~BBitmapButton()
{
	delete fBitmap;
	delete fDisabledBitmap;
}


void BBitmapButton::Draw(BRect updateRect)
{
	if(fBitmap == NULL) {
		BButton::Draw(updateRect);
		return;
	}

	SetDrawingMode(B_OP_COPY);
	FillRect(updateRect, B_SOLID_LOW);
	rgb_color color = LowColor();

	SetDrawingMode(B_OP_ALPHA);
	if(IsEnabled()) {
		if(Value() != 0) {
			// button is clicked
			DrawBitmap(fBitmap, BPoint(1, 1));
		} else {
			// button is released
			DrawBitmap(fBitmap, BPoint(0, 0));
		}
	} else
		DrawBitmap(fDisabledBitmap, BPoint(0, 0));
}


void BBitmapButton::SetBitmap(const char* attrname)
{
#ifdef __HAIKU__
	size_t size = 0;
	const void* data = gAppResources->LoadResource('VICN', attrname, &size);

	if (!data) {
		printf("CANT LOAD RESOURCE %s\n", attrname);
		return;
	}

	fBitmap = new BBitmap(BRect(0, 0, 32, 32), B_RGB32);
	status_t status = BIconUtils::GetVectorIcon((const uint8*)data, size, fBitmap);
	
	if(status != B_OK) {
		fprintf(stderr, "%s > oops %s\n", attrname, strerror(status));
		delete fBitmap;
		fBitmap = NULL;
	}

	fDisabledBitmap = new BBitmap(fBitmap);
	rgb_color* pixel = (rgb_color*)fDisabledBitmap->Bits();
	for(int i = 0; i < fDisabledBitmap->BitsLength()/4; i++)
	{
		*pixel = tint_color(*pixel, B_DISABLED_MARK_TINT);
		pixel++;
	}
#else
	// No vector icon support on BeOS. We could try to load a bitmap one
	fBitmap = NULL;
	fDisabledBitmap = NULL;
#endif
}


nsbeos_scaffolding *nsbeos_new_scaffolding(struct gui_window *toplevel)
{
	struct beos_scaffolding *g = (struct beos_scaffolding *)malloc(sizeof(*g));

	NSLOG(netsurf, INFO,
	      "Constructing a scaffold of %p for gui_window %p", g, toplevel);

	g->top_level = toplevel;
	g->being_destroyed = 0;
	g->fullscreen = false;

	open_windows++;

	BMessage *message;
	BRect rect;
	BMenuItem *item;

	g->window = NULL;
	g->menu_bar = NULL;

	if (replicated && !replicant_view) {
		beos_warn_user("Error: No subwindow allowed when replicated.", NULL);
		return NULL;
	}


	if (!replicant_view) {
		BRect frame(0, 0, 600-1, 500-1);
		if (nsoption_int(window_width) > 0) {
			frame.Set(0, 0, nsoption_int(window_width) - 1, nsoption_int(window_height) - 1);
			frame.OffsetToSelf(nsoption_int(window_x), nsoption_int(window_y));
		} else {
			BPoint pos(50, 50);
			// XXX: use last BApplication::WindowAt()'s dynamic_cast<NSBrowserWindow *> Frame()
			NSBrowserWindow *win = nsbeos_find_last_window();
			if (win) {
				pos = win->Frame().LeftTop();
				win->UnlockLooper();
			}
			pos += BPoint(20, 20);
			BScreen screen;
			BRect screenFrame(screen.Frame());
			if (pos.y + frame.Height() >= screenFrame.Height()) {
				pos.y = 50;
				pos.x += 50;
			}
			if (pos.x + frame.Width() >= screenFrame.Width()) {
				pos.x = 50;
				pos.y = 50;
			}
			frame.OffsetToSelf(pos);
		}

		g->window = new NSBrowserWindow(frame, g);

		rect = frame.OffsetToCopy(0,0);
		rect.bottom = rect.top + 20;

		// build menus
		g->menu_bar = new BMenuBar(rect, "menu_bar");
		g->window->AddChild(g->menu_bar);

		BMenu *menu;

		// App menu
		//XXX: use icon item ?

		menu = new BMenu(messages_get("NetSurf"));
		g->menu_bar->AddItem(menu);

		message = new BMessage(B_ABOUT_REQUESTED);
		item = make_menu_item("Info", message, true);
		menu->AddItem(item);

#if 0
		message = new BMessage(NO_ACTION);
		item = make_menu_item("AppHelp", message);
		menu->AddItem(item);

		submenu = new BMenu(messages_get("Open"));
		menu->AddItem(submenu);

		message = new BMessage(NO_ACTION);
		item = make_menu_item("OpenURL", message);
		submenu->AddItem(item);

		message = new BMessage(CHOICES_SHOW);
		item = make_menu_item("Choices", message);
		menu->AddItem(item);
#endif

		message = new BMessage(APPLICATION_QUIT);
		item = make_menu_item("Quit", message, true);
		menu->AddItem(item);

		// Page menu

		menu = new BMenu(messages_get("Page"));
		g->menu_bar->AddItem(menu);

#if 0
		message = new BMessage(BROWSER_PAGE_INFO);
		item = make_menu_item("PageInfo", message);
		menu->AddItem(item);

		message = new BMessage(BROWSER_SAVE);
		item = make_menu_item("SaveAsNS", message);
		menu->AddItem(item);

		message = new BMessage(BROWSER_SAVE_COMPLETE);
		item = make_menu_item("SaveCompNS", message);
		menu->AddItem(item);

		submenu = new BMenu(messages_get("Export"));
		menu->AddItem(submenu);

		/*
		message = new BMessage(BROWSER_EXPORT_DRAW);
		item = make_menu_item("Draw", message);
		submenu->AddItem(item);
		*/

		message = new BMessage(BROWSER_EXPORT_TEXT);
		item = make_menu_item("LinkText", message);
		submenu->AddItem(item);


		submenu = new BMenu(messages_get("SaveURL"));
		menu->AddItem(submenu);

		//XXX
		message = new BMessage(BROWSER_OBJECT_SAVE_URL_URL);
		item = make_menu_item("URL", message);
		submenu->AddItem(item);


		message = new BMessage(BROWSER_PRINT);
		item = make_menu_item("PrintNS", message);
		menu->AddItem(item);
#endif

		message = new BMessage(BROWSER_NEW_WINDOW);
		item = make_menu_item("NewWindowNS", message, true);
		menu->AddItem(item);

		message = new BMessage(BROWSER_VIEW_SOURCE);
		item = make_menu_item("ViewSrc", message, true);
		menu->AddItem(item);

#if 0 // FIXME This is supposed to be a popup menu!
		// Object menu

		menu = new BMenu(messages_get("Object"));
		g->menu_bar->AddItem(menu);

		message = new BMessage(BROWSER_OBJECT_INFO);
		item = make_menu_item("ObjInfo", message);
		menu->AddItem(item);

		message = new BMessage(BROWSER_OBJECT_SAVE);
		item = make_menu_item("ObjSave", message);
		menu->AddItem(item);
		// XXX: submenu: Sprite ?

		message = new BMessage(BROWSER_OBJECT_RELOAD);
		item = make_menu_item("ObjReload", message);
		menu->AddItem(item);
#endif

		// Navigate menu

		menu = new BMenu(messages_get("Navigate"));
		g->menu_bar->AddItem(menu);

		message = new BMessage(BROWSER_NAVIGATE_HOME);
		item = make_menu_item("Home", message, true);
		menu->AddItem(item);

		message = new BMessage(BROWSER_NAVIGATE_BACK);
		item = make_menu_item("Back", message, true);
		menu->AddItem(item);

		message = new BMessage(BROWSER_NAVIGATE_FORWARD);
		item = make_menu_item("Forward", message, true);
		menu->AddItem(item);

		message = new BMessage(BROWSER_NAVIGATE_UP);
		item = make_menu_item("UpLevel", message);
		menu->AddItem(item);

		message = new BMessage(BROWSER_NAVIGATE_RELOAD);
		item = make_menu_item("Reload", message, true);
		menu->AddItem(item);

		message = new BMessage(BROWSER_NAVIGATE_STOP);
		item = make_menu_item("Stop", message, true);
		menu->AddItem(item);

#if 0
		// View menu

		menu = new BMenu(messages_get("View"));
		g->menu_bar->AddItem(menu);

		message = new BMessage(BROWSER_SCALE_VIEW);
		item = make_menu_item("ScaleView", message);
		menu->AddItem(item);

		submenu = new BMenu(messages_get("Images"));
		menu->AddItem(submenu);

		message = new BMessage(BROWSER_IMAGES_FOREGROUND);
		item = make_menu_item("ForeImg", message);
		submenu->AddItem(item);

		message = new BMessage(BROWSER_IMAGES_BACKGROUND);
		item = make_menu_item("BackImg", message);
		submenu->AddItem(item);


		submenu = new BMenu(messages_get("Toolbars"));
		menu->AddItem(submenu);
		submenu->SetEnabled(false);

		message = new BMessage(NO_ACTION);
		item = make_menu_item("ToolButtons", message);
		submenu->AddItem(item);

		message = new BMessage(NO_ACTION);
		item = make_menu_item("ToolAddress", message);
		submenu->AddItem(item);

		message = new BMessage(NO_ACTION);
		item = make_menu_item("ToolThrob", message);
		submenu->AddItem(item);

		message = new BMessage(NO_ACTION);
		item = make_menu_item("ToolStatus", message);
		submenu->AddItem(item);


		submenu = new BMenu(messages_get("Render"));
		menu->AddItem(submenu);

		message = new BMessage(BROWSER_BUFFER_ANIMS);
		item = make_menu_item("RenderAnims", message);
		submenu->AddItem(item);

		message = new BMessage(BROWSER_BUFFER_ALL);
		item = make_menu_item("RenderAll", message);
		submenu->AddItem(item);


		message = new BMessage(NO_ACTION);
		item = make_menu_item("OptDefault", message);
		menu->AddItem(item);
#endif

		// Utilities menu

		menu = new BMenu(messages_get("Utilities"));
		g->menu_bar->AddItem(menu);

#if 0
		submenu = new BMenu(messages_get("Hotlist"));
		menu->AddItem(submenu);

		message = new BMessage(HOTLIST_ADD_URL);
		item = make_menu_item("HotlistAdd", message);
		submenu->AddItem(item);

		message = new BMessage(HOTLIST_SHOW);
		item = make_menu_item("HotlistShowNS", message);
		submenu->AddItem(item);


		submenu = new BMenu(messages_get("History"));
		menu->AddItem(submenu);

		message = new BMessage(HISTORY_SHOW_LOCAL);
		item = make_menu_item("HistLocal", message);
		submenu->AddItem(item);

		message = new BMessage(HISTORY_SHOW_GLOBAL);
		item = make_menu_item("HistGlobal", message);
		submenu->AddItem(item);
#endif

		message = new BMessage(COOKIES_SHOW);
		item = make_menu_item("Cookie manager", message, true);
		menu->AddItem(item);

#if 0
		message = new BMessage(BROWSER_FIND_TEXT);
		item = make_menu_item("FindText", message);
		menu->AddItem(item);

		submenu = new BMenu(messages_get("Window"));
		menu->AddItem(submenu);

		message = new BMessage(BROWSER_WINDOW_DEFAULT);
		item = make_menu_item("WindowSave", message);
		submenu->AddItem(item);

		message = new BMessage(BROWSER_WINDOW_STAGGER);
		item = make_menu_item("WindowStagr", message);
		submenu->AddItem(item);

		message = new BMessage(BROWSER_WINDOW_COPY);
		item = make_menu_item("WindowSize", message);
		submenu->AddItem(item);

		message = new BMessage(BROWSER_WINDOW_RESET);
		item = make_menu_item("WindowReset", message);
		submenu->AddItem(item);
#endif


		// Help menu

		menu = new BMenu(messages_get("Help"));
		g->menu_bar->AddItem(menu);

#if 0
		message = new BMessage(HELP_OPEN_CONTENTS);
		item = make_menu_item("HelpContent", message);
		menu->AddItem(item);

		message = new BMessage(HELP_OPEN_GUIDE);
		item = make_menu_item("HelpGuide", message);
		menu->AddItem(item);

		message = new BMessage(HELP_OPEN_INFORMATION);
		item = make_menu_item("HelpInfo", message);
		menu->AddItem(item);
#endif

		message = new BMessage(HELP_OPEN_ABOUT);
		item = make_menu_item("HelpCredits", message, true);
		menu->AddItem(item);

		message = new BMessage(HELP_OPEN_LICENCE);
		item = make_menu_item("HelpLicence", message, true);
		menu->AddItem(item);

#if 0
		message = new BMessage(HELP_LAUNCH_INTERACTIVE);
		item = make_menu_item("HelpInter", message);
		menu->AddItem(item);
#endif

		// the base view that receives the toolbar, statusbar and top-level view.
		rect = frame.OffsetToCopy(0,0);
		rect.top = g->menu_bar->Bounds().Height() + 1;
		//rect.top = 20 + 1; // XXX
		//rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
		g->top_view = new NSBaseView(rect);
		// add the top view to the window
		g->window->AddChild(g->top_view);
	} else { // replicant_view
		// the base view has already been created with the archive constructor
		g->top_view = replicant_view;
	}
	g->top_view->SetScaffolding(g);

	// build popup menu
	g->popup_menu = new BPopUpMenu("popup", false, false);

#if 0
	message = new BMessage(BROWSER_OBJECT_INFO);
	item = make_menu_item("ObjInfo", message);
	g->popup_menu->AddItem(item);

	message = new BMessage(BROWSER_OBJECT_SAVE);
	item = make_menu_item("ObjSave", message);
	g->popup_menu->AddItem(item);
	// XXX: submenu: Sprite ?
#endif

	message = new BMessage(BROWSER_OBJECT_RELOAD);
	item = make_menu_item("ObjReload", message, true);
	g->popup_menu->AddItem(item);


#ifdef ENABLE_DRAGGER
	// the dragger to allow replicating us
	// XXX: try to stuff it in the status bar at the bottom
	// (BDragger *must* be a parent, sibiling or direct child of NSBaseView!)
	rect = g->top_view->Bounds();
	rect.bottom = rect.top + TOOLBAR_HEIGHT - 1;
	rect.left = rect.right - DRAGGER_WIDTH + 1;
	g->dragger = new BDragger(rect, g->top_view, 
		B_FOLLOW_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW);
	g->top_view->AddChild(g->dragger);
	g->dragger->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	g->dragger->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)) ;
#endif

	// tool_bar
	// the toolbar is also the dragger for now
	// XXX: try to stuff it in the status bar at the bottom
	// (BDragger *must* be a parent, sibiling or direct child of NSBaseView!)
	// XXX: B_FULL_UPDATE_ON_RESIZE avoids leaving bits on resize,
	// but causes flicker
	rect = g->top_view->Bounds();
	rect.bottom = rect.top + TOOLBAR_HEIGHT - 1;
#ifdef ENABLE_DRAGGER
	rect.right = rect.right - DRAGGER_WIDTH;
#else
	rect.right = rect.right + 1;
#endif
	g->tool_bar = new BBox(rect, "Toolbar", 
		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS
		| B_FULL_UPDATE_ON_RESIZE | B_NAVIGABLE_JUMP, B_PLAIN_BORDER);
	g->top_view->AddChild(g->tool_bar);
	g->tool_bar->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	g->tool_bar->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)) ;

	// buttons
	rect = g->tool_bar->Bounds();
	rect.right = TOOLBAR_HEIGHT;
	rect.InsetBySelf(5, 5);
	rect.OffsetBySelf(0, -1);
	int nButtons = 0;

	message = new BMessage('back');
	message->AddPointer("scaffolding", g);
	g->back_button = new BBitmapButton(rect, "back_button", "<", message);
	g->tool_bar->AddChild(g->back_button);
	nButtons++;

	rect.OffsetBySelf(TOOLBAR_HEIGHT, 0);
	message = new BMessage('forw');
	message->AddPointer("scaffolding", g);
	g->forward_button = new BBitmapButton(rect, "forward_button", ">", message);
	g->tool_bar->AddChild(g->forward_button);
	nButtons++;

	rect.OffsetBySelf(TOOLBAR_HEIGHT, 0);
	message = new BMessage('stop');
	message->AddPointer("scaffolding", g);
	g->stop_button = new BBitmapButton(rect, "stop_button", "S", message);
	g->tool_bar->AddChild(g->stop_button);
	nButtons++;

	rect.OffsetBySelf(TOOLBAR_HEIGHT, 0);
	message = new BMessage('relo');
	message->AddPointer("scaffolding", g);
	g->reload_button = new BBitmapButton(rect, "reload_button", "R", message);
	g->tool_bar->AddChild(g->reload_button);
	nButtons++;

	rect.OffsetBySelf(TOOLBAR_HEIGHT, 0);
	message = new BMessage('home');
	message->AddPointer("scaffolding", g);
	g->home_button = new BBitmapButton(rect, "home_button", "H", message);
	g->tool_bar->AddChild(g->home_button);
	nButtons++;


	// url bar
	rect = g->tool_bar->Bounds();
	rect.left += TOOLBAR_HEIGHT * nButtons;
	rect.right -= TOOLBAR_HEIGHT * 1 + 100;
	rect.InsetBySelf(5, 5);
	message = new BMessage('urle');
	message->AddPointer("scaffolding", g);
	g->url_bar = new NSIconTextControl(rect, "url_bar", "", "", message, 
		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
	g->url_bar->SetDivider(0);
	rect = g->url_bar->TextView()->TextRect();
	rect.left += 16;
	g->url_bar->TextView()->SetTextRect(rect);
	g->tool_bar->AddChild(g->url_bar);

	// search bar

	rect = g->tool_bar->Bounds();
	rect.left = g->url_bar->Frame().right;
	rect.right -= TOOLBAR_HEIGHT * 1;
	rect.InsetBy(5,5);
	message = new BMessage('sear');
	message->AddPointer("scaffolding", g);
	g->search_bar = new BTextControl(rect, "search_bar", "",
		"Search" B_UTF8_ELLIPSIS, message, B_FOLLOW_RIGHT | B_FOLLOW_TOP);
	g->search_bar->SetDivider(0);
	g->tool_bar->AddChild(g->search_bar);

	// throbber
	rect.Set(0, 0, 24, 24);
	rect.OffsetTo(g->tool_bar->Bounds().right - 24 - (TOOLBAR_HEIGHT - 24) / 2,
		(TOOLBAR_HEIGHT - 24) / 2);
	g->throbber = new NSThrobber(rect);
	g->tool_bar->AddChild(g->throbber);
	g->throbber->SetViewColor(g->tool_bar->ViewColor());
	g->throbber->SetLowColor(g->tool_bar->ViewColor());
	g->throbber->SetDrawingMode(B_OP_ALPHA);
	g->throbber->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
	/* set up the throbber. */
	g->throbber->SetBitmap(nsbeos_throbber->framedata[0]);
	g->throb_frame = 0;


	// the status bar at the bottom
	BString status("NetSurf");
	status << " " << netsurf_version;
	g->status_bar = new BStringView(BRect(0,0,-1,-1), "StatusBar", 
		status.String(), B_FOLLOW_LEFT/*_RIGHT*/ | B_FOLLOW_BOTTOM);

	// will be added to the scrollview when adding the top view.

	// notify the thread creating the replicant that we're done
	if (replicant_view)
		release_sem(replicant_done_sem);

	replicant_view = NULL;

	return g;
}

void gui_window_set_title(struct gui_window *_g, const char *title)
{
	struct beos_scaffolding *g = nsbeos_get_scaffold(_g);
	if (g->top_level != _g) return;

	// if we're a replicant, discard
	if (!g->window)
		return;

	BString nt(title);
	if (nt.Length())
		nt << " - ";
	nt << "NetSurf";

	if (!g->top_view->LockLooper())
		return;

	g->window->SetTitle(nt.String());

	g->top_view->UnlockLooper();
}

void gui_window_set_status(struct gui_window *_g, const char *text)
{
	struct beos_scaffolding *g = nsbeos_get_scaffold(_g);
	assert(g);
	assert(g->status_bar);

	if (!g->top_view->LockLooper())
		return;

	if (text == NULL || text[0] == '\0')
	{
		BString status("NetSurf");
		status << " " << netsurf_version;
		g->status_bar->SetText(status.String());
	}
	else
	{
		g->status_bar->SetText(text);
	}
	g->top_view->UnlockLooper();
}

nserror gui_window_set_url(struct gui_window *gw, nsurl *url)
{
	struct beos_scaffolding *g;

        g = nsbeos_get_scaffold(gw);
	if (g->top_level != gw)
                return NSERROR_OK;

	assert(g->status_bar);

	if (g->top_view->LockLooper()) {
                g->url_bar->SetText(nsurl_access(url));

                g->top_view->UnlockLooper();
        }

        return NSERROR_OK;
}

void gui_window_start_throbber(struct gui_window* _g)
{
	struct beos_scaffolding *g = nsbeos_get_scaffold(_g);

	if (!g->top_view->LockLooper())
		return;

	g->stop_button->SetEnabled(true);
	g->reload_button->SetEnabled(false);

	g->top_view->UnlockLooper();

	nsbeos_window_update_back_forward(g);

	beos_schedule(100, nsbeos_throb, g);
}

void gui_window_stop_throbber(struct gui_window* _g)
{
	struct beos_scaffolding *g = nsbeos_get_scaffold(_g);

	nsbeos_window_update_back_forward(g);

	beos_schedule(-1, nsbeos_throb, g);

	if (!g->top_view->LockLooper())
		return;

	g->stop_button->SetEnabled(false);
	g->reload_button->SetEnabled(true);

	g->throbber->SetBitmap(nsbeos_throbber->framedata[0]);
	g->throbber->Invalidate();

	g->top_view->UnlockLooper();
}

/**
 * add retrieved favicon to the gui
 */
void gui_window_set_icon(struct gui_window *_g, hlcache_handle *icon)
{
	BBitmap *bitmap = NULL;
    struct bitmap *bmp_icon;

    bmp_icon = (icon != NULL) ? content_get_bitmap(icon) : NULL;

	if (bmp_icon) {
		bitmap = nsbeos_bitmap_get_primary(bmp_icon);
	}

	struct beos_scaffolding *g = nsbeos_get_scaffold(_g);

	if (!g->top_view->LockLooper())
		return;

	g->url_bar->SetBitmap(bitmap);

	g->top_view->UnlockLooper();
}


void nsbeos_scaffolding_popup_menu(nsbeos_scaffolding *scaffold, struct browser_window *bw, BPoint where, BPoint screenWhere)
{
	struct browser_window_features cont;

	browser_window_get_features(bw, (int)where.x, (int)where.y, &cont);

	scaffold->current_menu_object = cont.object;
	bool enabled = !!scaffold->current_menu_object;

	for (int i = 0; scaffold->popup_menu->ItemAt(i); i++) {
		scaffold->popup_menu->ItemAt(i)->SetEnabled(enabled);
	}

	scaffold->popup_menu->Go(screenWhere, true, false, true);
}

