Review: Paul Blokus -- select/scroll

Michael Drake mike at netsurf-browser.org
Thu Oct 29 20:25:06 GMT 2009


Added files


Index: desktop/scroll.c
===================================================================
--- /dev/null	2009-07-16 10:59:57.000000000 +0100
+++ desktop/scroll.c	2009-08-10 21:36:53.000000000 +0100
@@ -0,0 +1,695 @@
+/*
+ * Copyright 2004-2008 James Bursa <bursa at users.sourceforge.net>
+ * Copyright 2008 Michael Drake <tlsa at netsurf-browser.org> 
+ * Copyright 2009 Paul Blokus <paul_pl at users.sourceforge.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/>.
+ */
+
+/** \file
+ * Scroll widget (implementation).
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "desktop/scroll.h"
+#include "desktop/plotters.h"
+#include "desktop/plot_style.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+
+struct scroll {
+	bool horizontal;	/* horizontal scroll if true vertical if false
+				 */
+	int length;		/* Length of the scroll */
+	
+	int scrolled_d;		/* The dimension of the scrolled area */
+	int scrolled_vis;	/* The visible part of the scrolled area */	
+	
+	int area_scroll;	/* scroll value of the scrolled area */
+	int bar_off;		/* offset of the scrollbar */
+	int bar_len;		/* length of the scrollbar */
+	
+	scroll_client_callback client_callback;
+	void *client_data;
+	
+	bool dragging;
+	int drag_start_coord;
+	int drag_start_bar_off;
+	bool reverse;
+};
+
+/** Overflow scrollbar colours
+ *
+ * Overflow scrollbar colours can be set by front end code to try to match
+ * scrollbar colours used on the desktop.
+ *
+ * If a front end doesn't set scrollbar colours, these defaults are used.
+ */
+colour css_scrollbar_fg_colour = 0x00d9d9d9; /* light grey */
+colour css_scrollbar_bg_colour = 0x006b6b6b; /* mid grey */
+colour css_scrollbar_arrow_colour = 0x00444444; /* dark grey */
+
+/**
+ * Create a scroll.
+ *
+ * \param horizontal		true for a horizontal scrollbar false for a
+ * 				vertical one
+ * \param length		full length of the scrollbar
+ * \param scrolled_dimension	full length of the scrolled area
+ * \param scrolled_visible	length of the visible part of the scrolled area
+ * \param client_data		data for th client callback
+ * \param client_callback	client callback for scroll events
+ * \param scroll_pt		gets updated to point at te newly created scroll
+ * \return			true if the scroll has been created succesfully
+ *				or false on memory exhaustion
+ */
+bool scroll_create(bool horizontal, int length,
+		int scrolled_dimension, int scrolled_visible,
+  		void *client_data, scroll_client_callback client_callback,
+		struct scroll **scroll_pt)
+{
+	struct scroll *scroll;
+	int well_length;
+	struct scroll_msg_data msg;
+	
+	scroll = malloc(sizeof(struct scroll));
+	if (scroll == NULL) {
+		LOG(("malloc failed"));
+		warn_user("NoMemory", 0);
+		*scroll_pt = NULL;
+		return false;
+	}
+	
+	scroll->horizontal = horizontal;
+	scroll->length = length;
+	scroll->scrolled_d = scrolled_dimension;
+	scroll->scrolled_vis = scrolled_visible;
+	scroll->area_scroll = 0;
+	scroll->bar_off = 0;
+	scroll->reverse = false;
+	
+	well_length = length - 2 * SCROLLBAR_WIDTH;
+	scroll->bar_len = ((float) well_length * scrolled_visible) /
+			scrolled_dimension;
+	
+	scroll->client_callback = client_callback;
+	scroll->client_data = client_data;
+	
+	scroll->dragging = false;
+	
+	*scroll_pt = scroll;
+	
+	msg.scroll = scroll;
+	msg.msg = SCROLL_MSG_REDRAW;
+	msg.x0 = 0;
+	msg.y0 = 0;
+	msg.x1 = horizontal ? length : SCROLLBAR_WIDTH;
+	msg.y1 = horizontal ? SCROLLBAR_WIDTH : length;
+	
+	client_callback(client_data, &msg);
+	
+	return true;
+}
+
+/**
+ * Destroy a scroll.
+ * 
+ * \param scroll	the scroll to be destroyed
+ */
+void scroll_destroy(struct scroll *scroll)
+{
+	free(scroll);
+}
+
+/**
+ * Draw an outline rectangle common to a few of scroll elements.
+ *
+ * \param x0	left border of the outline
+ * \param y0	top border of the outline
+ * \param x1	right border of the outline
+ * \param y1	bottom border of the outline
+ * \param c	base colour of the outline, the other colours are created by
+ * 		lightening or darkening this one
+ * \param inset true for inset outline, false for an outset one
+ * \return
+ */
+static inline bool scroll_redraw_scrollbar_rectangle(
+		int x0, int y0, int x1, int y1, colour c, bool inset)
+{
+	static plot_style_t c0 = {
+		.stroke_type = PLOT_OP_TYPE_SOLID,
+		.stroke_width = 1,
+	};
+
+	static plot_style_t c1 = {
+		.stroke_type = PLOT_OP_TYPE_SOLID,
+		.stroke_width = 1,
+	};
+
+	static plot_style_t c2 = {
+		.stroke_type = PLOT_OP_TYPE_SOLID,
+		.stroke_width = 1,
+	};
+
+	if (inset) {
+		c0.stroke_colour = darken_colour(c);
+		c1.stroke_colour = lighten_colour(c);
+	} else {
+		c0.stroke_colour = lighten_colour(c);
+		c1.stroke_colour = darken_colour(c);
+	}
+	c2.stroke_colour = blend_colour(c0.stroke_colour, c1.stroke_colour);
+
+	if (!plot.line(x0, y0, x1, y0, &c0)) return false;
+	if (!plot.line(x1, y0, x1, y1 + 1, &c1)) return false;
+	if (!plot.line(x1, y0, x1, y0 + 1, &c2)) return false;
+	if (!plot.line(x1, y1, x0, y1, &c1)) return false;
+	if (!plot.line(x0, y1, x0, y0, &c0)) return false;
+	if (!plot.line(x0, y1, x0, y1 + 1, &c2)) return false;
+	return true;
+}
+
+/**
+ * Redraw a part of the scroll.
+ *
+ * \param scroll	the scroll to be redrawn
+ * \param x		the X coordinate to draw the scroll at
+ * \param y		the Y coordinate to draw the scroll at
+ * \param clip_x0	minimum x of the clipping rectangle
+ * \param clip_y0	minimum y of the clipping rectangle
+ * \param clip_x1	maximum x of the clipping rectangle
+ * \param clip_y1	maximum y of the clipping rectangle
+ * \param scale		scale for the redraw
+ * \return		true on succes false otherwise
+ */
+bool scroll_redraw(struct scroll *scroll, int x, int y,
+		int clip_x0, int clip_y0, int clip_x1, int clip_y1,
+		float scale)
+{
+	int w = SCROLLBAR_WIDTH;
+	int well_length, bar_off, bar_c0, bar_c1;
+	int v[6]; /* array of triangle vertices */
+	int x0, y0, x1, y1;
+	plot_style_t pstyle_css_scrollbar_bg_colour = {
+		.fill_type = PLOT_OP_TYPE_SOLID,
+		.fill_colour = css_scrollbar_bg_colour,
+	};
+	plot_style_t pstyle_css_scrollbar_fg_colour = {
+		.fill_type = PLOT_OP_TYPE_SOLID,
+		.fill_colour = css_scrollbar_fg_colour,
+	};
+	plot_style_t pstyle_css_scrollbar_arrow_colour = {
+		.fill_type = PLOT_OP_TYPE_SOLID,
+		.fill_colour = css_scrollbar_arrow_colour,
+	};
+
+	well_length = scroll->length - 2 * SCROLLBAR_WIDTH;
+	x0 = x;
+	y0 = y;
+	x1 = x + (scroll->horizontal ?
+			scroll->length : SCROLLBAR_WIDTH) - 1;
+	y1 = y + (scroll->horizontal ? SCROLLBAR_WIDTH : scroll->length) - 1;
+	bar_off = scroll->bar_off;
+	bar_c1 = (scroll->horizontal ? x0 : y0) + SCROLLBAR_WIDTH +
+			scroll->bar_off + scroll->bar_len - 1;
+			
+	if (scale != 1.0) {
+		w *= scale;
+		well_length *= scale;
+		x0 *= scale;
+		y0 *= scale;
+		x1 *= scale;
+		y1 *= scale;
+		bar_off *= scale;
+		bar_c1 *= scale;
+	}
+	
+	bar_c0 = (scroll->horizontal ? x0 : y0) + w + bar_off;
+	
+	if (clip_x0 < x0)
+		clip_x0 = x0;
+	
+	if (clip_y0 < y0)
+		clip_y0 = y0;
+	
+	if (clip_x1 > x1 + 1)
+		clip_x1 = x1 + 1;
+	
+	if (clip_y1 > y1 + 1)
+		clip_y1 = y1 + 1;
+	
+	/* return if the clipping rectangle is outside the scrollbar area */
+	if (clip_x0 > clip_x1 || clip_y0 > clip_y1)
+		return true;
+	
+	plot.clip(clip_x0, clip_y0, clip_x1, clip_y1);
+	
+	/* horizontal scrollbar */
+	if (scroll->horizontal) {
+		/* scrollbar outline */
+		if (!scroll_redraw_scrollbar_rectangle(x0, y0, x1, y1,
+				css_scrollbar_bg_colour, true))
+			return false;
+		/* left arrow icon border */
+		if (!scroll_redraw_scrollbar_rectangle(x0 + 1,
+		     		y0 + 1,
+				x0 + w - 2,
+    				y1 - 1,
+				css_scrollbar_fg_colour, false))
+			return false;
+		/* left arrow icon background */
+		if (!plot.rectangle(x0 + 2,
+		     		y0 + 2,
+				x0 + w - 2,
+    				y1 - 1,
+				&pstyle_css_scrollbar_fg_colour))
+			return false;
+		/* left arrow */
+		v[0] = x0 + w / 4;
+		v[1] = y0 + w / 2;
+		v[2] = x0 + w * 3 / 4;
+		v[3] = y0 + w / 4;
+		v[4] = x0 + w * 3 / 4;
+		v[5] = y0 + w * 3 / 4;
+		if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour))
+			return false;
+		/* scroll well background */
+		if (!plot.rectangle(x0 + w - 1,
+		     		y0 + 1,
+	 			x1 - w + 2,
+    				y1,
+				&pstyle_css_scrollbar_bg_colour))
+			return false;
+		/* scroll position indicator bar */
+		if (!scroll_redraw_scrollbar_rectangle(bar_c0,
+				y0 + 1,
+				bar_c1,
+				y1 - 1,
+				css_scrollbar_fg_colour, false))
+			return false;
+		if (!plot.rectangle(bar_c0 + 1,
+		     		y0 + 2,
+				bar_c1,
+    				y1 - 1,
+				&pstyle_css_scrollbar_fg_colour))
+			return false;
+		/* right arrow icon border */
+		if (!scroll_redraw_scrollbar_rectangle(x1 - w + 2,
+				y0 + 1,
+				x1 - 1,
+				y1 - 1,
+				css_scrollbar_fg_colour, false))
+			return false;
+		/* right arrow icon background */
+		if (!plot.rectangle(x1 - w + 3,
+		     		y0 + 2,
+				x1 - 1,
+    				y1 - 1,
+				&pstyle_css_scrollbar_fg_colour))
+			return false;
+		/* right arrow */
+		v[0] = x1 - w / 4 + 1;
+		v[1] = y0 + w / 2;
+		v[2] = x1 - w * 3 / 4 + 1;
+		v[3] = y0 + w / 4;
+		v[4] = x1 - w * 3 / 4 + 1;
+		v[5] = y0 + w * 3 / 4;
+		if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour))
+			return false;
+	} else {
+		/* outline */
+		if (!scroll_redraw_scrollbar_rectangle(x0, y0, x1, y1,
+				css_scrollbar_bg_colour,
+				true))
+			return false;
+ 		/* top arrow background */
+		if (!scroll_redraw_scrollbar_rectangle(x0 + 1,
+				y0 + 1,
+				x1 - 1,
+				y0 + w - 2,
+				css_scrollbar_fg_colour,
+				false))
+			return false;
+		if (!plot.rectangle(x0 + 2,
+				y0 + 2,
+				x1 - 1,
+				y0 + w - 2,
+				&pstyle_css_scrollbar_fg_colour))
+			return false;
+		/* up arrow */
+		v[0] = x0 + w / 2;
+		v[1] = y0 + w / 4;
+		v[2] = x0 + w / 4;
+		v[3] = y0 + w * 3 / 4;
+		v[4] = x0 + w * 3 / 4;
+		v[5] = y0 + w * 3 / 4;
+		if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour))
+			return false;
+		/* scroll well background */
+		if (!plot.rectangle(x0 + 1,
+				y0 + w - 1,
+				x1,
+				y1 - w + 2,
+				&pstyle_css_scrollbar_bg_colour))
+			return false;
+		/* scroll position indicator bar */
+		if (!scroll_redraw_scrollbar_rectangle(x0 + 1,
+				bar_c0,
+				x1 - 1,
+				bar_c1,
+				css_scrollbar_fg_colour, false))
+			return false;
+		if (!plot.rectangle(x0 + 2,
+				bar_c0 + 1,
+				x1 - 1,
+				bar_c1,
+				&pstyle_css_scrollbar_fg_colour))
+			return false;
+		/* bottom arrow background */
+		if (!scroll_redraw_scrollbar_rectangle(x0 + 1,
+				y1 - w + 2,
+				x1 - 1,
+				y1 - 1,
+				css_scrollbar_fg_colour, false))
+			return false;
+		if (!plot.rectangle(x0 + 2,
+				y1 - w + 3,
+				x1 - 1,
+				y1 - 1,
+				&pstyle_css_scrollbar_fg_colour))
+			return false;
+		/* down arrow */
+		v[0] = x0 + w / 2;
+		v[1] = y1 - w / 4 + 1;
+		v[2] = x0 + w / 4;
+		v[3] = y1 - w * 3 / 4 + 1;
+		v[4] = x0 + w * 3 / 4;
+		v[5] = y1 - w * 3 / 4 + 1;
+		if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour))
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * Set the value of the scroll.
+ *
+ * \param scroll	the scroll to have the value set
+ * \param scroll_val	the new value to be set
+ * \param bar		true if the value is for the scroll indication bar
+ *			offset, false if it is for the scrolled area one
+ */
+void scroll_set(struct scroll *scroll, int scroll_val, bool bar)
+{
+	int well_length;
+	struct scroll_msg_data msg;
+	
+	if (scroll_val < 0)
+		scroll_val = 0;
+	
+	if (scroll->scrolled_d == scroll->scrolled_vis)
+		return;
+	
+	well_length = scroll->length - 2 * SCROLLBAR_WIDTH;
+	if (bar) {
+		if (scroll_val > well_length - scroll->bar_len)
+			scroll->bar_off = well_length - scroll->bar_len;
+		else
+			scroll->bar_off = scroll_val;
+		
+		scroll->area_scroll = (float) (scroll->scrolled_d -
+				scroll->scrolled_vis) *
+				(float) (scroll->bar_off)/
+				(float) (well_length - scroll->bar_len);
+		
+	} else {
+		if (scroll_val > scroll->scrolled_d - scroll->scrolled_vis)
+			scroll->area_scroll = scroll->scrolled_d -
+					scroll->scrolled_vis;
+		else
+			scroll->area_scroll = scroll_val;
+		
+		scroll->bar_off = (float) well_length *
+				(float)  scroll->area_scroll /
+				(float) scroll->scrolled_d;
+	}
+	
+	msg.scroll = scroll;
+	msg.msg = SCROLL_MSG_MOVED;
+	msg.new_scroll = scroll->area_scroll;
+	scroll->client_callback(scroll->client_data, &msg);
+	
+	/* TODO: redraw only the necessary part */
+	msg.msg = SCROLL_MSG_REDRAW;
+	msg.x0 = 0;
+	msg.y0 = 0;
+	msg.x1 = (scroll->horizontal ? scroll->length : SCROLLBAR_WIDTH);
+	msg.y1 = (scroll->horizontal ?
+			SCROLLBAR_WIDTH : scroll->length);
+	scroll->client_callback(scroll->client_data, &msg);
+}
+
+/**
+ * Get the value of the scroll.
+ *
+ * \param scroll	the scroll to get the value of 
+ * \return		scroll offset for the scrolled area
+ */
+int scroll_get(struct scroll *scroll)
+{
+	if (scroll == NULL)
+		return 0;
+	return scroll->area_scroll;
+}
+
+
+/**
+ * Set the length of the scroll and the visible part of the scrolled area.
+ *
+ * \param scroll		the scroll to set the values for
+ * \param length		the new scroll length to be set
+ * \param scrolled_visible	the new value of the visible part of the
+ * 				scrolled area to be set
+ */
+void scroll_set_length_and_visible(struct scroll *scroll, int length,
+		int scrolled_visible)
+{
+	int well_length; 
+	
+	scroll->length = length;
+	scroll->scrolled_vis = scrolled_visible;
+	well_length = length - 2 * SCROLLBAR_WIDTH;
+
+	scroll->bar_len = ((float) well_length * scrolled_visible) /
+			scroll->scrolled_d;
+}
+
+/**
+ * Check the orientation of the scroll.
+ *
+ * \param scroll	the scroll to check the orientation of
+ * \return		true for a horizontal scroll, false for a vertical one
+ */
+bool scroll_is_horizontal(struct scroll *scroll)
+{
+	return scroll->horizontal;
+}
+
+/**
+ * Handle mouse actions other then movements and drag ends.
+ *
+ * \param scroll	the scroll which gets the mouse action
+ * \param mouse		mouse state
+ * \param x		X coordinate of the mouse
+ * \param y		Y coordinate of the mouse
+ * \return		message for the status bar or NULL on failure
+ */
+const char *scroll_mouse_action(struct scroll *scroll,
+		browser_mouse_state mouse, int x, int y)
+{
+	struct scroll_msg_data msg;
+	int x0, y0, x1, y1;
+	int val;
+	const char *status;
+	bool h;
+	bool but1 = ((mouse & BROWSER_MOUSE_PRESS_1) ||
+			((mouse & BROWSER_MOUSE_HOLDING_1) &&
+			(mouse & BROWSER_MOUSE_DRAG_ON) &&
+			!scroll->dragging));
+	bool but2 = ((mouse & BROWSER_MOUSE_PRESS_2) ||
+			((mouse & BROWSER_MOUSE_HOLDING_2) &&
+			(mouse & BROWSER_MOUSE_DRAG_ON) &&
+			!scroll->dragging));
+	
+	h = scroll->horizontal;
+	
+	x0 = 0;
+	y0 = 0;
+	x1 = h ? scroll->length : SCROLLBAR_WIDTH;
+	y1 = h ? SCROLLBAR_WIDTH : scroll->length;
+	
+	if (!scroll->dragging && !(x >= x0 && x <= x1 && y >= y0 && y <= y1)) {
+		return NULL;
+	}
+
+	
+	if (h)
+		val = x;
+	else
+		val = y;
+	
+	/* left/up arrow */
+	if (val < SCROLLBAR_WIDTH) {
+		status = messages_get(h ? "ScrollLeft" : "ScrollUp");
+		if (but1)
+			scroll_set(scroll, scroll->bar_off - SCROLLBAR_WIDTH,
+					true);
+		else if (but2)
+			scroll_set(scroll, scroll->bar_off + SCROLLBAR_WIDTH,
+					true);
+		
+	/* well between left/up arrow and bar */
+	} else if (val < SCROLLBAR_WIDTH + scroll->bar_off) {
+		
+		status = messages_get(h ? "ScrollPLeft" : "ScrollPUp");
+				
+		if (but1)
+			scroll_set(scroll, scroll->area_scroll - scroll->length,
+					false);
+		else if (but2)
+			scroll_set(scroll, scroll->area_scroll + scroll->length,
+					false);
+		
+	/* right/down arrow*/
+	} else if (val > scroll->length - SCROLLBAR_WIDTH) {
+		
+		status = messages_get(h ? "ScrollRight" : "ScrollDown");
+		
+		if (but1)
+			scroll_set(scroll, scroll->bar_off + SCROLLBAR_WIDTH,
+					true);
+		else if (but2)
+			scroll_set(scroll, scroll->bar_off - SCROLLBAR_WIDTH,
+					true);
+				
+	/* well between right/down arrow and bar */
+	} else if (val > SCROLLBAR_WIDTH + scroll->bar_off + scroll->bar_len) {
+		
+		status = messages_get(h ? "ScrollPRight" : "ScrollPDown");
+		if (but1)
+			scroll_set(scroll, scroll->area_scroll + scroll->length,
+					false);
+		else if (but2)
+			scroll_set(scroll, scroll->area_scroll - scroll->length,
+					false);
+	}
+	else {
+		status = messages_get(h ? "ScrollH" : "ScrollV");
+	}
+	
+	if (scroll->dragging) {
+		val -= scroll->drag_start_coord;
+		if (scroll->reverse)
+			val = -val;
+		if (val != 0)
+			scroll_set(scroll, scroll->drag_start_bar_off + val,
+					true);
+		return status;
+	}
+	
+	if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2) &&
+		   	(val >= SCROLLBAR_WIDTH + scroll->bar_off
+		   	&& val < SCROLLBAR_WIDTH + scroll->bar_off +
+		   			scroll->bar_len)) {
+			
+			scroll->drag_start_coord = scroll->horizontal ? x : y;
+			scroll->drag_start_bar_off = scroll->bar_off;
+			
+			scroll->dragging = true;
+			
+			msg.scroll = scroll;
+			msg.msg = SCROLL_MSG_CAPTURE_MOUSE;
+			scroll->client_callback(scroll->client_data, &msg);
+			if (mouse & BROWSER_MOUSE_DRAG_2) {
+				msg.msg = SCROLL_MSG_2D_DRAG;
+				msg.x0 = x;
+				msg.y0 = y;
+				scroll->client_callback(scroll->client_data,
+						&msg);
+			}
+	}
+	
+	return status;
+}
+
+/**
+ * Handle end of mouse drags.
+ *
+ * \param scroll	the scroll for which the drag ends
+ * \param mouse		mouse state
+ * \param x		X coordinate of the mouse
+ * \param y		Y coordinate of the mouse
+ */
+void scroll_mouse_drag_end(struct scroll *scroll, browser_mouse_state mouse,
+		int x, int y)
+{
+	struct scroll_msg_data msg;
+	int val;
+	
+	assert(scroll->dragging);
+	
+	val = (scroll->horizontal ? x : y) - scroll->drag_start_coord;
+	if (scroll->reverse)
+		val = -val;
+	if (val != 0)
+		scroll_set(scroll, scroll->drag_start_bar_off + val, true);
+	
+	scroll->dragging = false;
+	scroll->reverse = false;
+	msg.scroll = scroll;
+	msg.msg = SCROLL_MSG_RELEASE_MOUSE;
+	scroll->client_callback(scroll->client_data, &msg);
+}
+
+/**
+ * Force a drag to be started. This is used when a 2D drag was started on the
+ * parpendicular scroll and we want the future mouse actions to take effect for
+ * this scroll too.
+ *
+ * \param scroll	the scroll for which the drag start will be forced
+ * \param x		X coordinate of mouse during drag start
+ * \param y		Y coordinate of mouse during drag start
+ * \param reverse	if true mouse moves will cause the scroll to move in the
+ *			opposite direction
+ */
+void scroll_force_drag_start(struct scroll *scroll, int x, int y, bool reverse)
+{
+	struct scroll_msg_data msg;
+	
+	
+	scroll->reverse = reverse;
+			
+	scroll->drag_start_coord = scroll->horizontal ? x : y;
+	scroll->drag_start_bar_off = scroll->bar_off;
+			
+	scroll->dragging = true;
+	msg.scroll = scroll;
+	msg.msg = SCROLL_MSG_CAPTURE_MOUSE;
+	scroll->client_callback(scroll->client_data, &msg);
+}
Index: desktop/scroll.h
===================================================================
--- /dev/null	2009-07-16 10:59:57.000000000 +0100
+++ desktop/scroll.h	2009-08-10 21:36:53.000000000 +0100
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl at users.sourceforge.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/>.
+ */
+
+/** \file
+ * Scroll widget (interface).
+ */
+
+#ifndef _NETSURF_DESKTOP_SCROLL_H_
+#define _NETSURF_DESKTOP_SCROLL_H_
+
+#include <stdbool.h>
+
+#include "desktop/browser.h"
+
+#define SCROLLBAR_WIDTH 16
+
+struct scroll;
+
+typedef enum {
+	SCROLL_MSG_REDRAW,		/* the scrollbar requests a redraw */
+	SCROLL_MSG_MOVED,		/* the scroll value has changed */
+	SCROLL_MSG_CAPTURE_MOUSE,	/* all mouse events should be passed to
+					 * the scrollbar regardless of the
+					 * coordinates
+					 */
+	SCROLL_MSG_RELEASE_MOUSE,	/* cancel the above */
+	SCROLL_MSG_2D_DRAG		/* the user started a DRAG_2 drag on the
+					 * scroll which means that, if present,
+					 * the parpendicular scroll schould be
+					 * dragged together with this one to 
+					 * provide 2D scrolling functionallity
+					 */
+} scroll_msg;
+
+struct scroll_msg_data {
+	struct scroll *scroll;
+	scroll_msg msg;
+	int new_scroll;
+	int x0, y0, x1, y1;
+};
+
+/**
+ * Client callback for the scroll.
+ * 
+ * \param client_data	user data passed at scroll creation
+ * \param scroll_data	struct all necessary message data
+ */
+typedef void(*scroll_client_callback)(void *client_data,
+		struct scroll_msg_data *scroll_data);
+
+bool scroll_create(bool horizontal, int length,
+		int scrolled_dimension, int scrolled_visible,
+  		void *client_data, scroll_client_callback client_callback,
+    		struct scroll **scroll_pt);
+void scroll_destroy(struct scroll *scroll);
+bool scroll_redraw(struct scroll *scroll, int x, int y,
+		int clip_x0, int clip_y0, int clip_x1, int clip_y1,
+		float scale);
+		
+void scroll_set(struct scroll *scroll, int scroll_val, bool bar);
+int scroll_get(struct scroll *scroll);
+
+void scroll_set_length_and_visible(struct scroll *scroll, int length,
+		int scrolled_visible);
+
+bool scroll_is_horizontal(struct scroll *scroll);
+
+const char *scroll_mouse_action(struct scroll *scroll,
+		browser_mouse_state mouse, int x, int y);
+void scroll_mouse_drag_end(struct scroll *scroll, browser_mouse_state mouse,
+		int x, int y);
+void scroll_force_drag_start(struct scroll *scroll, int x, int y, bool reverse);
+
+#endif


Changed files


 !NetSurf/Resources/de/Messages |    4 
 !NetSurf/Resources/en/Messages |    4 
 !NetSurf/Resources/fr/Messages |    6 
 !NetSurf/Resources/it/Messages |    4 
 !NetSurf/Resources/nl/Messages |    4 
 Makefile.sources               |    2 
 amiga/thumbnail.c              |    2 
 desktop/browser.c              |  577 ++++++++++++++++++++++-------------------
 desktop/browser.h              |   20 -
 desktop/textinput.c            |   61 ++--
 framebuffer/gui.c              |    5 
 gtk/gtk_plotters.h             |    1 
 gtk/gtk_scaffolding.c          |    1 
 gtk/gtk_window.c               |   15 -
 render/box.c                   |   73 +++--
 render/box.h                   |   13 
 render/form.c                  |  475 +++++++++++++++++++++++++++++++++
 render/form.h                  |   24 +
 render/html_redraw.c           |  497 ++++++++++-------------------------
 render/layout.c                |    7 
 render/layout.h                |    2 
 21 files changed, 1112 insertions(+), 685 deletions(-)


Index: render/form.c
===================================================================
--- render/form.c	(revision 9172)
+++ render/form.c	(working copy)
@@ -2,6 +2,7 @@
  * Copyright 2004 James Bursa <bursa at users.sourceforge.net>
  * Copyright 2003 Phil Mellor <monkeyson at users.sourceforge.net>
  * Copyright 2005-9 John-Mark Bell <jmb at netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl at users.sourceforge.net>
  *
  * This file is part of NetSurf, http://www.netsurf-browser.org/
  *
@@ -29,18 +30,60 @@
 #include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
+#include "css/css.h"
+#include "css/utils.h"
+#include "desktop/gui.h"
+#include "desktop/knockout.h"
+#include "desktop/plot_style.h"
+#include "desktop/plotters.h"
+#include "desktop/scroll.h"
 #include "render/box.h"
+#include "render/font.h"
 #include "render/form.h"
+#include "render/layout.h"
 #include "utils/log.h"
+#include "utils/messages.h"
 #include "utils/url.h"
 #include "utils/utf8.h"
 #include "utils/utils.h"
 
+#define MAX_SELECT_HEIGHT 210
+#define SELECT_LINE_SPACING 0.2
+#define SELECT_BORDER_WIDTH 1
+#define SELECT_SELECTED_COLOUR 0xDB9370
 
+struct form_select_menu {
+	int line_height;
+	int width, height;
+	struct scroll *scroll;
+	int f_size;
+	bool scroll_capture;
+	select_menu_callback callback;
+	void *client_data;
+	struct browser_window *bw;
+};
+
+static plot_style_t plot_style_fill_selected = {
+	.fill_type = PLOT_OP_TYPE_SOLID,
+	.fill_colour = SELECT_SELECTED_COLOUR,
+};
+
+static plot_font_style_t plot_fstyle_entry = {
+	.family = PLOT_FONT_FAMILY_SANS_SERIF,
+	.weight = 400,
+	.flags = FONTF_NONE,
+	.background = 0xffffff,
+	.foreground = 0x000000,
+};
+
 static char *form_textarea_value(struct form_control *textarea);
 static char *form_acceptable_charset(struct form *form);
 static char *form_encode_item(const char *item, const char *charset,
 		const char *fallback);
+static void form_select_menu_clicked(struct form_control *control,
+		int x, int y);
+static void form_select_menu_scroll_callback(void *client_data,
+		struct scroll_msg_data *scroll_data);
 
 /**
  * Create a struct form.
@@ -199,6 +242,8 @@
 			free(option->value);
 			free(option);
 		}
+		if (control->data.select.menu != NULL)
+			form_free_select_menu(control);
 	}
 
 	free(control);
@@ -812,3 +857,433 @@
 	return ret;
 }
 
+/**
+ * Open a select menu for a select form control, creating it if necessary.
+ *
+ * \param client_data	data passed to the redraw callback
+ * \param control	the select form control for which the menu is being
+ * 			opened
+ * \param callback	redraw callback for the select menu
+ * \param bw		the browser window in which the select menu is being
+ * 			opened
+ * \return		false on memory exhaustion, true otherwise
+ */
+bool form_open_select_menu(void *client_data,
+		struct form_control *control,
+		select_menu_callback callback,
+		struct browser_window *bw)
+{
+	int i, line_height_with_spacing, scroll;
+	struct form_option *option;
+	struct box *box;
+	plot_font_style_t fstyle;
+	int total_height;
+	struct form_select_menu *menu;
+		
+
+	/* if the menu is opened for the first time */
+	if (control->data.select.menu == NULL) {
+		
+		menu = calloc(1, sizeof (struct form_select_menu));
+		if (menu == NULL) {
+			warn_user("NoMemory", 0);
+			return false;
+		}
+		
+		control->data.select.menu = menu;
+				
+		box = control->box;
+
+		menu->width = box->width +
+				box->border[RIGHT].width +
+				box->border[LEFT].width +
+				box->padding[RIGHT] + box->padding[LEFT];
+		
+		font_plot_style_from_css(control->box->style,
+				&fstyle);
+		menu->f_size = fstyle.size;
+		
+		menu->line_height =
+				FIXTOINT(FDIVI((FMUL(FLTTOFIX(1.2),
+				FMULI(nscss_screen_dpi, 
+				(fstyle.size / FONT_SIZE_SCALE)))), 72));
+		
+		line_height_with_spacing = menu->line_height +
+				menu->line_height *
+				SELECT_LINE_SPACING;
+		
+		total_height = control->data.select.num_items *
+				line_height_with_spacing;
+		menu->height = total_height;
+	
+		scroll = 0;
+		if (menu->height > MAX_SELECT_HEIGHT) {
+			
+			menu->height = MAX_SELECT_HEIGHT;
+			
+			if (control->data.select.num_selected > 0) {
+				i = 0;
+				option = control->data.select.items;
+				while (!option->selected) {
+					option = option->next;
+					i++;
+				}
+				
+				if ((i + 1) * line_height_with_spacing >
+						MAX_SELECT_HEIGHT)
+					scroll = (i + 1) *
+							line_height_with_spacing
+							- MAX_SELECT_HEIGHT;
+			}
+		}
+		menu->client_data = client_data;
+		menu->callback = callback;
+		if (!scroll_create(false,
+				menu->height,
+    				total_height,
+				menu->height,
+				control,
+				form_select_menu_scroll_callback,
+				&(menu->scroll))) {
+			free(menu);
+			return false;
+		}
+		menu->bw = bw;
+	}
+	else menu = control->data.select.menu;
+		
+	menu->callback(client_data, 0, 0, menu->width, menu->height);
+	
+	return true;
+}
+
+
+/**
+ * Destroy a select menu and free allocated memory.
+ * 
+ * \param control	the select form control owning the select menu being
+ * 			destroyed
+ */
+void form_free_select_menu(struct form_control *control)
+{
+	if (control->data.select.menu->scroll != NULL)
+		scroll_destroy(control->data.select.menu->scroll);
+	free(control->data.select.menu);
+	control->data.select.menu = NULL;
+}
+
+/**
+ * Redraw an opened select menu.
+ * 
+ * \param control	the select menu being redrawn
+ * \param x		the X coordinate to draw the menu at
+ * \param x		the Y coordinate to draw the menu at
+ * \param scale		current redraw scale
+ * \param clip_x0	minimum x of clipping rectangle
+ * \param clip_y0	minimum y of clipping rectangle
+ * \param clip_x1	maximum x of clipping rectangle
+ * \param clip_y1	maximum y of clipping rectangle
+ * \return		true on success, false otherwise
+ */
+bool form_redraw_select_menu(struct form_control *control, int x, int y,
+		float scale, int clip_x0, int clip_y0, int clip_x1, int clip_y1)
+{
+	struct box *box;
+	struct form_select_menu *menu = control->data.select.menu;
+	struct form_option *option;
+	bool result;
+	int line_height, line_height_with_spacing;
+	int width, height;
+	int x0, y0, x1, scrollbar_x, y1, y2, y3;
+	int item_y;
+	int text_pos_offset, text_x;
+	int scrollbar_width = SCROLLBAR_WIDTH;
+	int i;
+	int scroll;
+	int x_cp, y_cp;
+	
+	box = control->box;
+	
+	x_cp = x;
+	y_cp = y;
+	width = menu->width;
+	height = menu->height;
+	line_height = menu->line_height;
+	
+	line_height_with_spacing = line_height +
+			line_height * SELECT_LINE_SPACING;
+	scroll = scroll_get(menu->scroll);
+	
+	if (scale != 1.0) {
+		x *= scale;
+		y *= scale;
+		width *= scale;
+		height *= scale;
+		scrollbar_width *= scale;
+		
+		i = scroll / line_height_with_spacing;
+		scroll -= i * line_height_with_spacing;
+		line_height *= scale;
+		line_height_with_spacing *= scale;
+		scroll *= scale;
+		scroll += i * line_height_with_spacing;
+	}
+	
+	
+	x0 = x;
+	y0 = y;
+	x1 = x + width - 1;
+	y1 = y + height - 1;
+	scrollbar_x = x1 - scrollbar_width;
+	
+	result = plot.clip(x0, y0, x1 + 1, y1 + 1);
+	result &= plot.rectangle(x0, y0, x1, y1 ,plot_style_stroke_darkwbasec);
+		
+	
+	x0 = x0 + SELECT_BORDER_WIDTH;
+	y0 = y0 + SELECT_BORDER_WIDTH;
+	x1 = x1 - SELECT_BORDER_WIDTH;
+	y1 = y1 - SELECT_BORDER_WIDTH;
+	height = height - 2 * SELECT_BORDER_WIDTH;
+
+	result = plot.clip(x0, y0, x1 + 1, y1 + 1);
+	result &= plot.rectangle(x0, y0, x1 + 1, y1 + 1,
+			plot_style_fill_lightwbasec);
+	option = control->data.select.items;
+	item_y = line_height_with_spacing;
+	
+	while (item_y < scroll) {
+		option = option->next;
+		item_y += line_height_with_spacing;
+	}
+	item_y -= line_height_with_spacing;
+	text_pos_offset = y - scroll +
+			(int) (line_height * (0.75 + SELECT_LINE_SPACING));
+	text_x = x + (box->border[LEFT].width + box->padding[LEFT]) * scale;
+	
+	plot_fstyle_entry.size = menu->f_size;
+	
+	while (option && item_y - scroll < height) {
+		
+ 		if (option->selected) {
+ 			y2 = y + item_y - scroll;
+ 			y3 = y + item_y + line_height_with_spacing - scroll;
+ 			result &= plot.rectangle(x0, (y0 > y2 ? y0 : y2),
+					scrollbar_x + 1,
+     					(y3 < y1 + 1 ? y3 : y1 + 1),
+					&plot_style_fill_selected);
+ 		}
+		
+		y2 = text_pos_offset + item_y;
+		result &= plot.text(text_x, y2, option->text,
+				strlen(option->text), &plot_fstyle_entry);
+		
+		item_y += line_height_with_spacing;
+		option = option->next;
+	}
+		
+	result &= scroll_redraw(menu->scroll,
+			x_cp + menu->width - SCROLLBAR_WIDTH,
+      			y_cp,
+			clip_x0, clip_y0, clip_x1, clip_y1, scale);
+	
+	return result;
+}
+
+/**
+ * Check whether a clipping rectangle is completely contained in the
+ * select menu.
+ *
+ * \param control	the select menu to check the clipping rectangle for
+ * \param scale		the current browser window scale
+ * \param clip_x0	minimum x of clipping rectangle
+ * \param clip_y0	minimum y of clipping rectangle
+ * \param clip_x1	maximum x of clipping rectangle
+ * \param clip_y1	maximum y of clipping rectangle
+ * \return		true if inside false otherwise
+ */
+bool form_clip_inside_select_menu(struct form_control *control, float scale,
+		int clip_x0, int clip_y0, int clip_x1, int clip_y1)
+{
+	struct form_select_menu *menu = control->data.select.menu;
+	int width, height;
+	
+
+	width = menu->width;
+	height = menu->height;
+	
+	if (scale != 1.0) {
+		width *= scale;
+		height *= scale;
+	}
+	
+	if (clip_x0 >= 0 && clip_x1 <= width &&
+			clip_y0 >= 0 && clip_y1 <= height)
+		return true;
+
+	return false;
+}
+
+/**
+ * Handle a click on the area of the currently opened select menu.
+ * 
+ * \param control	the select menu which received the click
+ * \param x		X coordinate of click
+ * \param y		Y coordinate of click
+ */
+void form_select_menu_clicked(struct form_control *control, int x, int y)
+{	
+	struct form_select_menu *menu = control->data.select.menu;
+	struct form_option *option;	
+	int line_height, line_height_with_spacing;	
+	int item_bottom_y;
+	int scroll, i;
+	
+	scroll = scroll_get(menu->scroll);
+	
+	line_height = menu->line_height;
+	line_height_with_spacing = line_height +
+			line_height * SELECT_LINE_SPACING;
+	
+	option = control->data.select.items;
+	item_bottom_y = line_height_with_spacing;
+	i = 0;
+	while (option && item_bottom_y < scroll + y) {
+		item_bottom_y += line_height_with_spacing;
+		option = option->next;
+		i++;
+	}
+	
+	if (option != NULL)
+		browser_window_form_select(menu->bw, control, i);
+	
+	menu->callback(menu->client_data, 0, 0, menu->width, menu->height);
+}
+
+/**
+ * Handle mouse action for the currently opened select menu.
+ *
+ * \param control	the select menu which received the mouse action
+ * \param mouse		current mouse state
+ * \param x		X coordinate of click
+ * \param y		Y coordinate of click
+ * \return		text for the browser status bar or NULL on failure
+ */
+const char *form_select_mouse_action(struct form_control *control,
+		browser_mouse_state mouse, int x, int y)
+{
+	struct form_select_menu *menu = control->data.select.menu;
+	int x0, y0, x1, y1, x3;
+	const char *status = NULL;
+	bool closed = false;
+	
+	x0 = 0;
+	y0 = 0;
+	x1 = menu->width;
+	y1 = menu->height;
+	x3 = x1 - SCROLLBAR_WIDTH;
+	
+	if (menu->scroll_capture ||
+			(x > x3 && x < x1 && y > y0 && y < y1)) {
+		x -= menu->width - SCROLLBAR_WIDTH;
+		return scroll_mouse_action(menu->scroll,
+				    mouse, x, y);
+	}
+		
+	if (mouse & BROWSER_MOUSE_CLICK_1) {
+		if (x > x0 && x < x1 && y > y0 && y < y1) {
+			if (x < x1 - SCROLLBAR_WIDTH)
+				form_select_menu_clicked(control, x, y);
+			else {
+				x -=  menu->width - SCROLLBAR_WIDTH;
+				status = scroll_mouse_action(menu->scroll,
+						mouse, x, y);
+			}
+		}
+		else
+			closed = true;
+	}
+	if (status == NULL && !closed)
+		status = messages_get(control->data.select.multiple ?
+				"SelectMClick" : "SelectClick");
+	return status;
+}
+
+/**
+ * Handle mouse drag end for the currently opened select menu.
+ *
+ * \param control	the select menu which received the mouse drag end
+ * \param mouse		current mouse state
+ * \param x		X coordinate of drag end
+ * \param y		Y coordinate of drag end
+ */
+void form_select_mouse_drag_end(struct form_control *control,
+		browser_mouse_state mouse, int x, int y)
+{
+	int x0, y0, x1, y1;
+	struct form_select_menu *menu = control->data.select.menu;
+	
+	if (menu->scroll_capture) {
+		x -= menu->width - SCROLLBAR_WIDTH;
+		scroll_mouse_drag_end(menu->scroll,
+				    mouse, x, y);
+		return;
+	}
+	
+	x0 = 0;
+	y0 = 0;
+	x1 = menu->width;
+	y1 = menu->height;
+		
+	if (x > x0 && x < x1 - SCROLLBAR_WIDTH && y >  y0 && y < y1)
+		form_select_menu_clicked(control, x, y);
+}
+
+/**
+ * Callback for the select menus scroll
+ */
+void form_select_menu_scroll_callback(void *client_data,
+		struct scroll_msg_data *scroll_data)
+{
+	struct form_control *control = client_data;
+	struct form_select_menu *menu = control->data.select.menu;
+	
+	switch (scroll_data->msg) {
+		case SCROLL_MSG_REDRAW:
+			menu->callback(menu->client_data,
+				       	menu->width -
+					SCROLLBAR_WIDTH + scroll_data->x0,
+     					scroll_data->y0,
+					scroll_data->x1 - scroll_data->x0,
+					scroll_data->y1 - scroll_data->y0);
+			break;
+		case SCROLL_MSG_MOVED:
+			menu->callback(menu->client_data,
+				    	0, 0,
+					menu->width - SCROLLBAR_WIDTH,
+     					menu->height);
+			break;
+		case SCROLL_MSG_CAPTURE_MOUSE:
+			menu->scroll_capture = true;
+			break;
+		case SCROLL_MSG_RELEASE_MOUSE:
+			menu->scroll_capture = false;
+			break;
+		default:
+			break;
+	}
+}
+
+/**
+ * Get the dimensions of a select menu.
+ *
+ * \param control	the select menu to get the dimensions of
+ * \param width		gets updated to menu width
+ * \param height	gets updated to menu height
+ */
+void form_select_get_dimensions(struct form_control *control,
+		int *width, int *height)
+{
+	*width = control->data.select.menu->width;
+	*height = control->data.select.menu->height;
+}
Index: render/form.h
===================================================================
--- render/form.h	(revision 9172)
+++ render/form.h	(working copy)
@@ -1,6 +1,7 @@
 /*
  * Copyright 2003 Phil Mellor <monkeyson at users.sourceforge.net>
  * Copyright 2003 James Bursa <bursa at users.sourceforge.net>
+ * Copyright 2009 Paul Blokus <paul_pl at users.sourceforge.net> 
  *
  * This file is part of NetSurf, http://www.netsurf-browser.org/
  *
@@ -25,11 +26,13 @@
 #define _NETSURF_RENDER_FORM_H_
 
 #include <stdbool.h>
+#include "desktop/browser.h"
 #include "utils/config.h"
 
 struct box;
 struct form_control;
 struct form_option;
+struct form_select_menu;
 
 /** Form submit method. */
 typedef enum {
@@ -105,6 +108,7 @@
 			int num_selected;
 			/** Currently selected item, if num_selected == 1. */
 			struct form_option *current;
+			struct form_select_menu *menu;
 		} select;
 	} data;
 
@@ -129,6 +133,9 @@
 	struct form_successful_control *next;	/**< Next in linked list. */
 };
 
+typedef void(*select_menu_callback)(void *client_data,
+		int x, int y, int width, int height);
+
 struct form *form_new(void *node, const char *action, const char *target, 
 		form_method method, const char *charset, 
 		const char *doc_charset);
@@ -145,4 +152,21 @@
 		struct form_successful_control *control);
 void form_free_successful(struct form_successful_control *control);
 
+bool form_open_select_menu(void *client_data,
+		struct form_control *control,
+		select_menu_callback redraw_callback,
+  		struct browser_window *bw);
+void form_free_select_menu(struct form_control *control);
+bool form_redraw_select_menu(struct form_control *control, int x, int y,
+		float scale,
+  		int clip_x0, int clip_y0, int clip_x1, int clip_y1);
+bool form_clip_inside_select_menu(struct form_control *control, float scale,
+		int clip_x0, int clip_y0, int clip_x1, int clip_y1);
+const char *form_select_mouse_action(struct form_control *control,
+		browser_mouse_state mouse, int x, int y);
+void form_select_mouse_drag_end(struct form_control *control,
+		browser_mouse_state mouse, int x, int y);
+void form_select_get_dimensions(struct form_control *control,
+		int *width, int *height);
+
 #endif
Index: render/html_redraw.c
===================================================================
--- render/html_redraw.c	(revision 9172)
+++ render/html_redraw.c	(working copy)
@@ -5,6 +5,7 @@
  * Copyright 2005-2006 Adrian Lees <adrianl at users.sourceforge.net>
  * Copyright 2006 Rob Kendrick <rjek at netsurf-browser.org>
  * Copyright 2008 Michael Drake <tlsa at netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl at users.sourceforge.net> 
  *
  * This file is part of NetSurf, http://www.netsurf-browser.org/
  *
@@ -40,6 +41,7 @@
 #include "desktop/textinput.h"
 #include "desktop/options.h"
 #include "desktop/print.h"
+#include "desktop/scroll.h"
 #include "image/bitmap.h"
 #include "render/box.h"
 #include "render/font.h"
@@ -89,23 +91,9 @@
 		float scale, colour colour, float ratio);
 static bool html_redraw_text_decoration_block(struct box *box, int x, int y,
 		float scale, colour colour, float ratio);
-static bool html_redraw_scrollbars(struct box *box, float scale,
-		int x, int y, int padding_width, int padding_height,
-		colour background_colour);
 
 bool html_redraw_debug = false;
 
-/** Overflow scrollbar colours
- *
- * Overflow scrollbar colours can be set by front end code to try to match
- * scrollbar colours used on the desktop.
- *
- * If a front end doesn't set scrollbar colours, these defaults are used.
- */
-colour css_scrollbar_fg_colour = 0x00d9d9d9; /* light grey */
-colour css_scrollbar_bg_colour = 0x006b6b6b; /* mid grey */
-colour css_scrollbar_arrow_colour = 0x00444444; /* dark grey */
-
 /**
  * Draw a CONTENT_HTML using the current set of plotters (plot).
  *
@@ -131,7 +119,8 @@
 		float scale, colour background_colour)
 {
 	struct box *box;
-	bool result, want_knockout;
+	bool result = true, want_knockout;
+	bool select, select_only;
 	plot_style_t pstyle_fill_bg = {
 		.fill_type = PLOT_OP_TYPE_SOLID,
 		.fill_colour = background_colour,
@@ -144,18 +133,53 @@
 	if (want_knockout)
 		knockout_plot_start(&plot);
 
-	/* clear to background colour */
-	result = plot.clip(clip_x0, clip_y0, clip_x1, clip_y1);
+	/* The select menu needs special treating because, when opened, it
+	 * reaches beyond its layout box.
+	 */
+	select = false;
+	select_only = false;
+	if (current_redraw_browser != NULL &&
+			current_redraw_browser->visible_select_menu != NULL) {
+		struct form_control *control =
+				current_redraw_browser->visible_select_menu;
+		select = true;
+		/* check if the redraw rectangle is completely inside of the
+		   select menu */
+		select_only = form_clip_inside_select_menu(control, scale,
+				clip_x0, clip_y0, clip_x1, clip_y1);
+	}
+	
+	if (!select_only) {
+		/* clear to background colour */
+		result = plot.clip(clip_x0, clip_y0, clip_x1, clip_y1);
+	
+		if (c->data.html.background_colour != NS_TRANSPARENT)
+			pstyle_fill_bg.fill_colour =
+					c->data.html.background_colour;
+	
+		result &= plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1,
+				&pstyle_fill_bg);
+	
+		result &= html_redraw_box(box, x, y,
+				clip_x0, clip_y0, clip_x1, clip_y1,
+				scale, pstyle_fill_bg.fill_colour);
+	}
 
-	if (c->data.html.background_colour != NS_TRANSPARENT)
-		pstyle_fill_bg.fill_colour = c->data.html.background_colour;
-
-	result &= plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1, &pstyle_fill_bg);
-
-	result &= html_redraw_box(box, x, y,
-			clip_x0, clip_y0, clip_x1, clip_y1,
-			scale, pstyle_fill_bg.fill_colour);
-
+	if (select) {
+		int menu_x, menu_y;
+		box = current_redraw_browser->visible_select_menu->box;
+		box_coords(box, &menu_x, &menu_y);
+	
+		menu_x -= box->border[LEFT].width;
+		menu_y += box->height + box->border[BOTTOM].width +
+				box->padding[BOTTOM] + box->padding[TOP];
+		result &= form_redraw_select_menu(
+				current_redraw_browser->visible_select_menu,
+				x + menu_x, y + menu_y,
+    				current_redraw_browser->scale,
+				clip_x0, clip_y0, clip_x1, clip_y1);
+	}
+	
 	if (want_knockout)
 		knockout_plot_end();
 
@@ -195,6 +219,7 @@
 	int x_scrolled, y_scrolled;
 	struct box *bg_box = NULL;
 	css_color bgcol = 0;
+	bool has_x_scroll, has_y_scroll;
 
 	if (html_redraw_printing && box->printed)
 		return true;
@@ -622,8 +647,8 @@
 			return false;
 
 	if (box->object) {
-		x_scrolled = x - box->scroll_x * scale;
-		y_scrolled = y - box->scroll_y * scale;
+		x_scrolled = x - scroll_get(box->scroll_x) * scale;
+		y_scrolled = y - scroll_get(box->scroll_y) * scale;
 		if (!content_redraw(box->object,
 				x_scrolled + padding_left,
 				y_scrolled + padding_top,
@@ -664,24 +689,51 @@
 	/* list marker */
 	if (box->list_marker)
 		if (!html_redraw_box(box->list_marker,
-				x_parent + box->x - box->scroll_x,
-				y_parent + box->y - box->scroll_y,
+				x_parent + box->x - scroll_get(box->scroll_x),
+				y_parent + box->y - scroll_get(box->scroll_y),
 				clip_x0, clip_y0, clip_x1, clip_y1,
 				scale, current_background_color))
 			return false;
 
 	/* scrollbars */
-	if (box->style && box->type != BOX_BR && box->type != BOX_TABLE &&
-			box->type != BOX_INLINE &&
+	if (current_redraw_browser && box->style && box->type != BOX_BR &&
+			box->type != BOX_TABLE && box->type != BOX_INLINE &&
 			(css_computed_overflow(box->style) == 
 				CSS_OVERFLOW_SCROLL ||
 			css_computed_overflow(box->style) == 
-				CSS_OVERFLOW_AUTO))
-		if (!html_redraw_scrollbars(box, scale, x, y,
-				padding_width, padding_height,
-				current_background_color))
+				CSS_OVERFLOW_AUTO)) {
+		
+		has_x_scroll = false;
+		has_y_scroll = false;
+		
+		has_x_scroll = box_hscrollbar_present(box);
+		
+		has_y_scroll = box_vscrollbar_present(box);
+		
+		if (!box_handle_scrollbars(box,
+		     		x_parent + box->x, y_parent + box->y,
+				has_x_scroll, has_y_scroll))
 			return false;
+		
+		if (box->scroll_x != NULL)
+			scroll_redraw(box->scroll_x,
+					x_parent + box->x,
+     					y_parent + box->y + box->padding[TOP] +
+					box->height + box->padding[BOTTOM] -
+					SCROLLBAR_WIDTH,				
+					clip_x0, clip_y0, clip_x1, clip_y1,
+     					scale);
+		if (box->scroll_y != NULL)
+			scroll_redraw(box->scroll_y,
+					x_parent + box->x + box->padding[LEFT] +
+					box->width + box->padding[RIGHT] -
+					SCROLLBAR_WIDTH,
+					y_parent + box->y,
+					clip_x0, clip_y0, clip_x1, clip_y1,
+     					scale);
+	}
 
+
 	if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
 			box->type == BOX_TABLE_CELL || box->object)
 		if (!plot.clip(clip_x0, clip_y0, clip_x1, clip_y1))
@@ -717,16 +769,18 @@
 
 		if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT)
 			if (!html_redraw_box(c,
-					x_parent + box->x - box->scroll_x,
-					y_parent + box->y - box->scroll_y,
+					x_parent + box->x -
+					scroll_get(box->scroll_x),
+					y_parent + box->y -
+					scroll_get(box->scroll_y),
 					clip_x0, clip_y0, clip_x1, clip_y1,
 					scale, current_background_color))
 				return false;
 	}
 	for (c = box->float_children; c; c = c->next_float)
 		if (!html_redraw_box(c,
-				x_parent + box->x - box->scroll_x,
-				y_parent + box->y - box->scroll_y,
+				x_parent + box->x - scroll_get(box->scroll_x),
+				y_parent + box->y - scroll_get(box->scroll_y),
 				clip_x0, clip_y0, clip_x1, clip_y1,
 				scale, current_background_color))
 			return false;
@@ -1958,318 +2012,69 @@
 	return true;
 }
 
-static inline bool
-html_redraw_scrollbar_rectangle(int x0, int y0, int x1, int y1, colour c,
-		bool inset)
-{
-	static plot_style_t c0 = {
-		.stroke_type = PLOT_OP_TYPE_SOLID,
-		.stroke_width = 1,
-	};
-
-	static plot_style_t c1 = {
-		.stroke_type = PLOT_OP_TYPE_SOLID,
-		.stroke_width = 1,
-	};
-
-	static plot_style_t c2 = {
-		.stroke_type = PLOT_OP_TYPE_SOLID,
-		.stroke_width = 1,
-	};
-
-	if (inset) {
-		c0.stroke_colour = darken_colour(c);
-		c1.stroke_colour = lighten_colour(c);
-	} else {
-		c0.stroke_colour = lighten_colour(c);
-		c1.stroke_colour = darken_colour(c);
-	}
-	c2.stroke_colour = blend_colour(c0.stroke_colour, c1.stroke_colour);
-
-	if (!plot.line(x0, y0, x1, y0, &c0)) return false;
-	if (!plot.line(x1, y0, x1, y1 + 1, &c1)) return false;
-	if (!plot.line(x1, y0, x1, y0 + 1, &c2)) return false;
-	if (!plot.line(x1, y1, x0, y1, &c1)) return false;
-	if (!plot.line(x0, y1, x0, y0, &c0)) return false;
-	if (!plot.line(x0, y1, x0, y1 + 1, &c2)) return false;
-	return true;
-}
-
 /**
- * Plot scrollbars for a scrolling box.
+ * Applies the given scroll setup to a box. This includes scroll
+ * creation/deletion as well as scroll dimension updates.
  *
- * \param  box	  scrolling box
- * \param  scale  scale for redraw
- * \param  x	  coordinate of box
- * \param  y	  coordinate of box
- * \param  padding_width   width of padding box
- * \param  padding_height  height of padding box
- * \return true if successful, false otherwise
+ * \param box		the box to handle the scrolls for
+ * \param x		X coordinate of the box
+ * \param y		Y coordinate of the box
+ * \param bottom	whether the horizontal scrollbar should be present
+ * \param right		whether the vertical scrollbar should be present
+ * \return		true on success false otherwise
  */
-
-bool html_redraw_scrollbars(struct box *box, float scale,
-		int x, int y, int padding_width, int padding_height,
-		colour background_colour)
+bool box_handle_scrollbars(struct box *box, int x, int y, bool bottom,
+		bool right)
 {
-	const int w = SCROLLBAR_WIDTH * scale;
-	bool vscroll, hscroll;
-	int well_height, bar_top, bar_height;
-	int well_width, bar_left, bar_width;
-	int v[6]; /* array of triangle vertices */
-	plot_style_t pstyle_css_scrollbar_bg_colour = {
-		.fill_type = PLOT_OP_TYPE_SOLID,
-		.fill_colour = css_scrollbar_bg_colour,
-	};
-	plot_style_t pstyle_css_scrollbar_fg_colour = {
-		.fill_type = PLOT_OP_TYPE_SOLID,
-		.fill_colour = css_scrollbar_fg_colour,
-	};
-	plot_style_t pstyle_css_scrollbar_arrow_colour = {
-		.fill_type = PLOT_OP_TYPE_SOLID,
-		.fill_colour = css_scrollbar_arrow_colour,
-	};
-
-	box_scrollbar_dimensions(box, padding_width, padding_height, w,
-			&vscroll, &hscroll,
-			&well_height, &bar_top, &bar_height,
-			&well_width, &bar_left, &bar_width);
-
-
-	/* horizontal scrollbar */
-	if (hscroll) {
-		/* scrollbar outline */
-		if (!html_redraw_scrollbar_rectangle(x,
-				y + padding_height - w,
-				x + padding_width - 1,
-				y + padding_height - 1,
-				css_scrollbar_bg_colour, true))
-			return false;
-		/* left arrow icon border */
-		if (!html_redraw_scrollbar_rectangle(x + 1,
-				y + padding_height - w + 1,
-				x + w - 2,
-				y + padding_height - 2,
-				css_scrollbar_fg_colour, false))
-			return false;
-		/* left arrow icon background */
-		if (!plot.rectangle(x + 2,
-				y + padding_height - w + 2,
-				x + w - 2,
-				y + padding_height - 2,
-				&pstyle_css_scrollbar_fg_colour))
-			return false;
-		/* left arrow */
-		v[0] = x + w / 4;
-		v[1] = y + padding_height - w / 2;
-		v[2] = x + w * 3 / 4;
-		v[3] = y + padding_height - w * 3 / 4;
-		v[4] = x + w * 3 / 4;
-		v[5] = y + padding_height - w / 4;
-		if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour))
-			return false;
-		/* scroll well background */
-		if (!plot.rectangle(x + w - 1,
-				y + padding_height - w + 1,
-				x + w + well_width + (vscroll ? 2 : 1),
-				y + padding_height - 1,
-				&pstyle_css_scrollbar_bg_colour))
-			return false;
-		/* scroll position indicator bar */
-		if (!html_redraw_scrollbar_rectangle(x + w + bar_left,
-				y + padding_height - w + 1,
-				x + w + bar_left + bar_width + (vscroll? 1 : 0),
-				y + padding_height - 2,
-				css_scrollbar_fg_colour, false))
-			return false;
-		if (!plot.rectangle(x + w + bar_left + 1,
-				y + padding_height - w + 2,
-				x + w + bar_left + bar_width + (vscroll? 1 : 0),
-				y + padding_height - 2,
-				&pstyle_css_scrollbar_fg_colour))
-			return false;
-		/* right arrow icon border */
-		if (!html_redraw_scrollbar_rectangle(x + w + well_width + 2,
-				y + padding_height - w + 1,
-				x + w + well_width + w - (vscroll ? 1 : 2),
-				y + padding_height - 2,
-				css_scrollbar_fg_colour, false))
-			return false;
-		/* right arrow icon background */
-		if (!plot.rectangle(x + w + well_width + 3,
-				y + padding_height - w + 2,
-				x + w + well_width + w - (vscroll ? 1 : 2),
-				y + padding_height - 2,
-				&pstyle_css_scrollbar_fg_colour))
-			return false;
-		/* right arrow */
-		v[0] = x + w + well_width + w * 3 / 4 + (vscroll ? 1 : 0);
-		v[1] = y + padding_height - w / 2;
-		v[2] = x + w + well_width + w / 4 + (vscroll ? 1 : 0);
-		v[3] = y + padding_height - w * 3 / 4;
-		v[4] = x + w + well_width + w / 4 + (vscroll ? 1 : 0);
-		v[5] = y + padding_height - w / 4;
-		if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour))
-			return false;
+	int padding_width, padding_height;
+	
+	padding_width = box->width + box->padding[RIGHT] + box->padding[LEFT];
+	padding_height = box->height + box->padding[TOP] + box->padding[BOTTOM];
+	
+	if (!bottom && box->scroll_x != NULL) {
+		scroll_destroy(box->scroll_x);
+		box->scroll_x = NULL;
 	}
-
-	/* vertical scrollbar */
-	if (vscroll) {
-		/* outline */
-		if (!html_redraw_scrollbar_rectangle(x + padding_width - w,
-						     y,
-						     x + padding_width - 1,
-						     y + padding_height - 1,
-						     css_scrollbar_bg_colour,
-						     true))
-			return false;
-		/* top arrow background */
-		if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1,
-						     y + 1,
-						     x + padding_width - 2,
-						     y + w - 2,
-						     css_scrollbar_fg_colour,
-						     false))
-			return false;
-		if (!plot.rectangle(x + padding_width - w + 2,
-				    y + 2,
-				    x + padding_width - 2,
-				    y + w - 2,
-				    &pstyle_css_scrollbar_fg_colour))
-			return false;
-		/* up arrow */
-		v[0] = x + padding_width - w / 2;
-		v[1] = y + w / 4;
-		v[2] = x + padding_width - w * 3 / 4;
-		v[3] = y + w * 3 / 4;
-		v[4] = x + padding_width - w / 4;
-		v[5] = y + w * 3 / 4;
-		if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour))
-			return false;
-		/* scroll well background */
-		if (!plot.rectangle(x + padding_width - w + 1,
-				y + w - 1,
-				x + padding_width - 1,
-				y + padding_height - w + 1,
-				&pstyle_css_scrollbar_bg_colour))
-			return false;
-		/* scroll position indicator bar */
-		if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1,
-				y + w + bar_top,
-				x + padding_width - 2,
-				y + w + bar_top + bar_height,
-				css_scrollbar_fg_colour, false))
-			return false;
-		if (!plot.rectangle(x + padding_width - w + 2,
-				y + w + bar_top + 1,
-				x + padding_width - 2,
-				y + w + bar_top + bar_height,
-				&pstyle_css_scrollbar_fg_colour))
-			return false;
-		/* bottom arrow background */
-		if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1,
-				y + padding_height - w + 1,
-				x + padding_width - 2,
-				y + padding_height - 2,
-				css_scrollbar_fg_colour, false))
-			return false;
-		if (!plot.rectangle(x + padding_width - w + 2,
-				y + padding_height - w + 2,
-				x + padding_width - 2,
-				y + padding_height - 2,
-				&pstyle_css_scrollbar_fg_colour))
-			return false;
-		/* down arrow */
-		v[0] = x + padding_width - w / 2;
-		v[1] = y + w + well_height + w * 3 / 4;
-		v[2] = x + padding_width - w * 3 / 4;
-		v[3] = y + w + well_height + w / 4;
-		v[4] = x + padding_width - w / 4;
-		v[5] = y + w + well_height + w / 4;
-		if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour))
-			return false;
+	
+	if (!right && box->scroll_y != NULL) {
+		scroll_destroy(box->scroll_y);
+		box->scroll_y = NULL;
 	}
-
-	return true;
-}
-
-
-/**
- * Determine if a box has a vertical scrollbar.
- *
- * \param  box  scrolling box
- * \return the box has a vertical scrollbar
- */
-
-bool box_vscrollbar_present(const struct box * const box)
-{
-	return box->descendant_y0 < -box->border[TOP].width ||
-			box->padding[TOP] + box->height + box->padding[BOTTOM] +
-			box->border[BOTTOM].width < box->descendant_y1;
-}
-
-
-/**
- * Determine if a box has a horizontal scrollbar.
- *
- * \param  box  scrolling box
- * \return the box has a horizontal scrollbar
- */
-
-bool box_hscrollbar_present(const struct box * const box)
-{
-	return box->descendant_x0 < -box->border[LEFT].width ||
-			box->padding[LEFT] + box->width + box->padding[RIGHT] +
-			box->border[RIGHT].width < box->descendant_x1;
-}
-
-
-/**
- * Calculate scrollbar dimensions and positions for a box.
- *
- * \param  box		   scrolling box
- * \param  padding_width   scaled width of padding box
- * \param  padding_height  scaled height of padding box
- * \param  w		   scaled scrollbar width
- * \param  vscroll	   updated to vertical scrollbar present
- * \param  hscroll	   updated to horizontal scrollbar present
- * \param  well_height	   updated to vertical well height
- * \param  bar_top	   updated to top position of vertical scrollbar
- * \param  bar_height	   updated to height of vertical scrollbar
- * \param  well_width	   updated to horizontal well width
- * \param  bar_left	   updated to left position of horizontal scrollbar
- * \param  bar_width	   updated to width of horizontal scrollbar
- */
-
-void box_scrollbar_dimensions(const struct box * const box,
-		const int padding_width, const int padding_height, const int w,
-		bool * const vscroll, bool * const hscroll,
-		int * const well_height,
-		int * const bar_top, int * const bar_height,
-		int * const well_width,
-		int * const bar_left, int * const bar_width)
-{
-	*vscroll = box_vscrollbar_present(box);
-	*hscroll = box_hscrollbar_present(box);
-	*well_height = padding_height - w - w;
-	*bar_top = 0;
-	*bar_height = *well_height;
-	if (box->descendant_y1 - box->descendant_y0 != 0) {
-		*bar_top = (float) *well_height * (float) box->scroll_y /
-				(float) (box->descendant_y1 -
-				box->descendant_y0);
-		*bar_height = (float) *well_height * (float) box->height /
-				(float) (box->descendant_y1 -
-				box->descendant_y0);
+	
+	if (!bottom && !right)
+		return true;
+	
+	if (right) {
+		if (box->scroll_y == NULL) {
+			if (!scroll_create(false,
+					padding_height,
+					box->descendant_y1 - box->descendant_y0,
+     					box->height,
+					current_redraw_browser,
+     					browser_scroll_callback,
+					&(box->scroll_y)))
+				return false;
+		} else 
+			scroll_set_length_and_visible(box->scroll_y,
+					padding_height, box->height);
 	}
-	*well_width = padding_width - w - w - (*vscroll ? w : 0);
-	*bar_left = 0;
-	*bar_width = *well_width;
-	if (box->descendant_x1 - box->descendant_x0 != 0) {
-		*bar_left = (float) *well_width * (float) box->scroll_x /
-				(float) (box->descendant_x1 -
-				box->descendant_x0);
-		*bar_width = (float) *well_width * (float) box->width /
-				(float) (box->descendant_x1 -
-				box->descendant_x0);
+	if (bottom) {
+		if (box->scroll_x == NULL) {
+			if (!scroll_create(true,
+					padding_width -
+					(right ? SCROLLBAR_WIDTH : 0),
+					box->descendant_x1 - box->descendant_x0,
+     					box->width,
+					current_redraw_browser,
+     					browser_scroll_callback,
+					&box->scroll_x))
+				return false;
+		} else
+			scroll_set_length_and_visible(box->scroll_x,
+					padding_width -
+					(right ? SCROLLBAR_WIDTH : 0),
+					box->width);
 	}
+	
+	return true;
 }
Index: render/layout.c
===================================================================
--- render/layout.c	(revision 9172)
+++ render/layout.c	(working copy)
@@ -44,6 +44,7 @@
 #include "content/content.h"
 #include "desktop/gui.h"
 #include "desktop/options.h"
+#include "desktop/scroll.h"
 #include "render/box.h"
 #include "render/font.h"
 #include "render/form.h"
@@ -1990,7 +1991,8 @@
 							opt_maxwidth =opt_width;
 					}
 
-					b->width = opt_maxwidth;
+					b->width = opt_maxwidth +
+							SCROLLBAR_WIDTH;
 				} else {
 					font_func->font_width(&fstyle, b->text,
 						b->length, &b->width);
@@ -2623,7 +2625,8 @@
 							opt_maxwidth =opt_width;
 					}
 
-					b->width = opt_maxwidth;
+					b->width = opt_maxwidth +
+							SCROLLBAR_WIDTH;
 				} else {
 					font_func->font_width(&fstyle, b->text,
 						b->length, &b->width);
Index: render/layout.h
===================================================================
--- render/layout.h	(revision 9172)
+++ render/layout.h	(working copy)
@@ -27,8 +27,6 @@
 #ifndef _NETSURF_RENDER_LAYOUT_H_
 #define _NETSURF_RENDER_LAYOUT_H_
 
-#define SCROLLBAR_WIDTH 16
-
 struct box;
 
 bool layout_document(struct content *content, int width, int height);
Index: render/box.c
===================================================================
--- render/box.c	(revision 9172)
+++ render/box.c	(working copy)
@@ -29,6 +29,7 @@
 #include "content/content.h"
 #include "css/css.h"
 #include "css/dump.h"
+#include "desktop/scroll.h"
 #include "desktop/options.h"
 #include "render/box.h"
 #include "render/form.h"
@@ -80,7 +81,7 @@
 	box->descendant_x1 = box->descendant_y1 = 0;
 	for (i = 0; i != 4; i++)
 		box->margin[i] = box->padding[i] = box->border[i].width = 0;
-	box->scroll_x = box->scroll_y = 0;
+	box->scroll_x = box->scroll_y = NULL;
 	box->min_width = 0;
 	box->max_width = UNKNOWN_MAX_WIDTH;
 	box->byte_offset = 0;
@@ -226,6 +227,10 @@
 	if (!box->clone) {
 		if (box->gadget)
 			form_free_control(box->gadget);
+		if (box->scroll_x != NULL)
+			scroll_destroy(box->scroll_x);
+		if (box->scroll_y != NULL)
+			scroll_destroy(box->scroll_y);
 	}
 
 	talloc_free(box);
@@ -251,8 +256,8 @@
 			} while (!box->float_children);
 		} else
 			box = box->parent;
-		*x += box->x - box->scroll_x;
-		*y += box->y - box->scroll_y;
+		*x += box->x - scroll_get(box->scroll_x);
+		*y += box->y - scroll_get(box->scroll_y);
 	}
 }
 
@@ -328,8 +333,8 @@
 	/* consider floats second, since they will often overlap other boxes */
 	for (child = box->float_children; child; child = child->next_float) {
 		if (box_contains_point(child, x - bx, y - by, &physically)) {
-			*box_x = bx + child->x - child->scroll_x;
-			*box_y = by + child->y - child->scroll_y;
+			*box_x = bx + child->x - scroll_get(child->scroll_x);
+			*box_y = by + child->y - scroll_get(child->scroll_y);
 
 			if (physically)
 				return child;
@@ -345,8 +350,8 @@
 		if (box_is_float(child))
 			continue;
 		if (box_contains_point(child, x - bx, y - by, &physically)) {
-			*box_x = bx + child->x - child->scroll_x;
-			*box_y = by + child->y - child->scroll_y;
+			*box_x = bx + child->x - scroll_get(child->scroll_x);
+			*box_y = by + child->y - scroll_get(child->scroll_y);
 
 			if (physically)
 				return child;
@@ -370,16 +375,16 @@
 	/* siblings and siblings of ancestors */
 	while (box) {
 		if (box_is_float(box)) {
-			bx -= box->x - box->scroll_x;
-			by -= box->y - box->scroll_y;
+			bx -= box->x - scroll_get(box->scroll_x);
+			by -= box->y - scroll_get(box->scroll_y);
 			for (sibling = box->next_float; sibling;
 					sibling = sibling->next_float) {
 				if (box_contains_point(sibling,
 						x - bx, y - by, &physically)) {
-					*box_x = bx + sibling->x -
-							sibling->scroll_x;
-					*box_y = by + sibling->y -
-							sibling->scroll_y;
+					*box_x = bx + sibling->x - scroll_get(
+							sibling->scroll_x);
+					*box_y = by + sibling->y - scroll_get(
+							sibling->scroll_y);
 
 					if (physically)
 						return sibling;
@@ -398,18 +403,18 @@
 			goto non_float_children;
 
 		} else {
-			bx -= box->x - box->scroll_x;
-			by -= box->y - box->scroll_y;
+			bx -= box->x - scroll_get(box->scroll_x);
+			by -= box->y - scroll_get(box->scroll_y);
 			for (sibling = box->next; sibling;
 					sibling = sibling->next) {
 				if (box_is_float(sibling))
 					continue;
 				if (box_contains_point(sibling, x - bx, y - by,
 						&physically)) {
-					*box_x = bx + sibling->x -
-							sibling->scroll_x;
-					*box_y = by + sibling->y -
-							sibling->scroll_y;
+					*box_x = bx + sibling->x - scroll_get(
+							sibling->scroll_x);
+					*box_y = by + sibling->y - scroll_get(
+							sibling->scroll_y);
 
 					if (physically)
 						return sibling;
@@ -978,3 +983,33 @@
 		box->next_float = box_dict_element->new;
 	}
 }
+
+
+/**
+ * Determine if a box has a vertical scrollbar.
+ *
+ * \param  box  scrolling box
+ * \return the box has a vertical scrollbar
+ */
+
+bool box_vscrollbar_present(const struct box * const box)
+{
+	return box->descendant_y0 < -box->border[TOP].width ||
+			box->padding[TOP] + box->height + box->padding[BOTTOM] +
+			box->border[BOTTOM].width < box->descendant_y1;
+}
+
+
+/**
+ * Determine if a box has a horizontal scrollbar.
+ *
+ * \param  box  scrolling box
+ * \return the box has a horizontal scrollbar
+ */
+
+bool box_hscrollbar_present(const struct box * const box)
+{
+	return box->descendant_x0 < -box->border[LEFT].width ||
+			box->padding[LEFT] + box->width + box->padding[RIGHT] +
+			box->border[RIGHT].width < box->descendant_x1;
+}
Index: render/box.h
===================================================================
--- render/box.h	(revision 9172)
+++ render/box.h	(working copy)
@@ -167,8 +167,8 @@
 	int padding[4];  /**< Padding: TOP, RIGHT, BOTTOM, LEFT. */
 	struct box_border border[4];   /**< Border: TOP, RIGHT, BOTTOM, LEFT. */
 
-	int scroll_x;  /**< Horizontal scroll of descendants. */
-	int scroll_y;  /**< Vertical scroll of descendants. */
+	struct scroll *scroll_x;  /**< Horizontal scroll. */
+	struct scroll *scroll_y;  /**< Vertical scroll. */
 
 	/** Width of box taking all line breaks (including margins etc). Must
 	 * be non-negative. */
@@ -314,15 +314,10 @@
 void box_dump(FILE *stream, struct box *box, unsigned int depth);
 bool box_extract_link(const char *rel, const char *base, char **result);
 
+bool box_handle_scrollbars(struct box *box, int x, int y, bool bottom,
+		bool right);
 bool box_vscrollbar_present(const struct box *box);
 bool box_hscrollbar_present(const struct box *box);
-void box_scrollbar_dimensions(const struct box *box,
-		int padding_width, int padding_height, int w,
-		bool *vscroll, bool *hscroll,
-		int *well_height,
-		int *bar_top, int *bar_height,
-		int *well_width,
-		int *bar_left, int *bar_width);
 
 bool xml_to_box(xmlNode *n, struct content *c);
 
Index: framebuffer/gui.c
===================================================================
--- framebuffer/gui.c	(revision 9172)
+++ framebuffer/gui.c	(working copy)
@@ -41,6 +41,7 @@
 #include "utils/messages.h"
 #include "utils/utils.h"
 #include "desktop/textinput.h"
+#include "render/form.h"
 
 #include "framebuffer/gui.h"
 #include "framebuffer/fbtk.h"
@@ -292,14 +293,15 @@
         nsfb_claim(fbtk_get_nsfb(widget), &bwidget->redraw_box);
 
         /* redraw bounding box is relative to window */
+	current_redraw_browser = bw;
         content_redraw(c,
                        x - bwidget->scrollx, y - bwidget->scrolly,
                        width, height,
                        bwidget->redraw_box.x0, bwidget->redraw_box.y0,
                        bwidget->redraw_box.x1, bwidget->redraw_box.y1,
                        bw->scale, 0xFFFFFF);
+	current_redraw_browser = NULL;
 
-
         nsfb_release(fbtk_get_nsfb(widget), &bwidget->redraw_box);
 
         bwidget->redraw_box.y0 = bwidget->redraw_box.x0 = INT_MAX;
@@ -1281,6 +1283,7 @@
 void gui_create_form_select_menu(struct browser_window *bw,
                                  struct form_control *control)
 {
+	form_open_select_menu(bw, control, browser_select_menu_callback);
 }
 
 void gui_launch_url(const char *url)
Index: gtk/gtk_window.c
===================================================================
--- gtk/gtk_window.c	(revision 9172)
+++ gtk/gtk_window.c	(working copy)
@@ -305,8 +305,8 @@
 	if (c->type == CONTENT_HTML)
 		scale = 1;
 
-        current_widget = widget;
-	current_drawable = widget->window;
+	current_widget = (GtkWidget *)g->drawing_area;
+	current_drawable = current_widget->window;
 	current_gc = gdk_gc_new(current_drawable);
 #ifdef CAIRO_VERSION
 	current_cr = gdk_cairo_create(current_drawable);
@@ -315,6 +315,7 @@
 	plot = nsgtk_plotters;
 	nsgtk_plot_set_scale(g->bw->scale);
 	current_redraw_browser = g->bw;
+	
 	content_redraw(c, 0, 0,
 			widget->allocation.width * scale,
 			widget->allocation.height * scale,
@@ -328,6 +329,7 @@
 	if (g->careth != 0)
 		nsgtk_plot_caret(g->caretx, g->carety, g->careth);
 
+	current_widget = NULL;
 	g_object_unref(current_gc);
 #ifdef CAIRO_VERSION
 	cairo_destroy(current_cr);
@@ -442,7 +444,8 @@
 		browser_window_mouse_click(g->bw, g->mouse->state, event->x / g->bw->scale,
 			event->y / g->bw->scale);
 	else
-		browser_window_mouse_drag_end(g->bw, 0, event->x, event->y);
+		browser_window_mouse_drag_end(g->bw, 0, event->x / g->bw->scale,
+				event->y / g->bw->scale);
 
 	g->mouse->state = 0;
 	return TRUE;
@@ -654,8 +657,10 @@
 		return;
 
 	gtk_widget_queue_draw_area(GTK_WIDGET(g->drawing_area),
-                                   data->redraw.x, data->redraw.y,
-                                   data->redraw.width, data->redraw.height);
+                                   data->redraw.x * g->bw->scale,
+				   data->redraw.y * g->bw->scale,
+                                   data->redraw.width * g->bw->scale,
+				   data->redraw.height * g->bw->scale);
 }
 
 bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
Index: gtk/gtk_scaffolding.c
===================================================================
--- gtk/gtk_scaffolding.c	(revision 9172)
+++ gtk/gtk_scaffolding.c	(working copy)
@@ -1182,6 +1182,7 @@
 
 	history_redraw(bw->history);
 
+	current_widget = NULL;
 	g_object_unref(current_gc);
 #ifdef CAIRO_VERSION
 	cairo_destroy(current_cr);
Index: gtk/gtk_plotters.h
===================================================================
--- gtk/gtk_plotters.h	(revision 9172)
+++ gtk/gtk_plotters.h	(working copy)
@@ -29,6 +29,7 @@
 
 extern const struct plotter_table nsgtk_plotters;
 
+/* make sure this is NULL if no redraw is in progress */
 extern GtkWidget *current_widget;
 extern GdkDrawable *current_drawable;
 extern GdkGC *current_gc;
Index: !NetSurf/Resources/de/Messages
===================================================================
--- !NetSurf/Resources/de/Messages	(revision 9172)
+++ !NetSurf/Resources/de/Messages	(working copy)
@@ -700,6 +700,10 @@
 ScrollPRight:Anklicken scrollt eine Seite nach rechts
 ScrollRight:Pfeil anklicken scrollt rechts
 
+# Select menu - displayed in status bar
+#
+SelectClick:Click on entry to select it
+SelectMClick:Click on entry to select it, (Ctrl + click) for multiple select)
 
 # Saving
 # ======
Index: !NetSurf/Resources/en/Messages
===================================================================
--- !NetSurf/Resources/en/Messages	(revision 9172)
+++ !NetSurf/Resources/en/Messages	(working copy)
@@ -705,6 +705,10 @@
 ScrollPRight:Click to scroll right one page
 ScrollRight:Click the arrow to scroll right
 
+# Select menu - displayed in status bar
+#
+SelectClick:Click on entry to select it
+SelectMClick:Click on entry to select it, (Ctrl + click) for multiple select)
 
 # Saving
 # ======
Index: !NetSurf/Resources/fr/Messages
===================================================================
--- !NetSurf/Resources/fr/Messages	(revision 9172)
+++ !NetSurf/Resources/fr/Messages	(working copy)
@@ -700,6 +700,10 @@
 ScrollPRight:Cliquez pour faire défiler d'une page vers la droite
 ScrollRight:Cliquez sur la flèche pour faire défiler vers la droite
 
+# Select menu - displayed in status bar
+#
+SelectClick:Click on entry to select it
+SelectMClick:Click on entry to select it, (Ctrl + click) for multiple select)
 
 # Saving
 # ======
@@ -761,7 +765,7 @@
 HelpToolbar4:\Tle bouton Accueil.|M\Saller à votre page d'accueil.
 HelpToolbar5:\Tle bouton d'historique.|M\Souvrir la \w d'historique locale.|M\Aopen the global history \w.
 HelpToolbar6:\Tle bouton de sauvegarde.|M\Ssauver le document en cours.
-HelpToolbar7:\Tle bouton d'impression.|M\Simprimer cette page.|MOuvre une boîte de dialogue pour l'impression.
+HelpToolbar7:\Tle bouton d'impression.|M\Simprimer cette page.|MOuvre une bo�te de dialogue pour l'impression.
 HelpToolbar8:\Tle bouton de favoris.|M\Souvrir la \w de gestion des favoris.|M\Aajouter cette adresse aux favoris.
 HelpToolbar9:\Tle bouton de changement d'échelle.|M\Sredimensionner la page, texte et images comprises.
 HelpToolbar10:\Tle bouton de recherche.|M\Strouver des occurences de fragment de texte dans une page.
Index: !NetSurf/Resources/nl/Messages
===================================================================
--- !NetSurf/Resources/nl/Messages	(revision 9172)
+++ !NetSurf/Resources/nl/Messages	(working copy)
@@ -700,6 +700,10 @@
 ScrollPRight:Click to scroll right one page
 ScrollRight:Click the arrow to scroll right
 
+# Select menu - displayed in status bar
+#
+SelectClick:Click on entry to select it
+SelectMClick:Click on entry to select it, (Ctrl + click) for multiple select)
 
 # Saving
 # ======
Index: !NetSurf/Resources/it/Messages
===================================================================
--- !NetSurf/Resources/it/Messages	(revision 9172)
+++ !NetSurf/Resources/it/Messages	(working copy)
@@ -707,6 +707,10 @@
 ScrollPRight:Clicca per scrollare a destra di una pagina
 ScrollRight:Clicca sulla freccia per scrollare a destra
 
+# Select menu - displayed in status bar
+#
+SelectClick:Click on entry to select it
+SelectMClick:Click on entry to select it, (Ctrl + click) for multiple select)
 
 # Saving
 # ======
Index: Makefile.sources
===================================================================
--- Makefile.sources	(revision 9172)
+++ Makefile.sources	(working copy)
@@ -14,7 +14,7 @@
 S_UTILS := base64.c filename.c hashtable.c locale.c messages.c talloc.c	\
 	url.c utf8.c utils.c useragent.c
 S_DESKTOP := knockout.c options.c print.c tree.c version.c textarea.c \
-	plot_style.c
+	plot_style.c scroll.c
 
 # S_COMMON are sources common to all builds
 S_COMMON := $(addprefix content/,$(S_CONTENT))				\
Index: desktop/textinput.c
===================================================================
--- desktop/textinput.c	(revision 9172)
+++ desktop/textinput.c	(working copy)
@@ -30,6 +30,7 @@
 
 #include "desktop/browser.h"
 #include "desktop/gui.h"
+#include "desktop/scroll.h"
 #include "desktop/selection.h"
 #include "desktop/textinput.h"
 #include "render/box.h"
@@ -279,11 +280,11 @@
 	textarea->gadget->caret_box_offset = char_offset;
 	textarea->gadget->caret_pixel_offset = pixel_offset;
 
-	box_x += textarea->scroll_x;
-	box_y += textarea->scroll_y;
+	box_x += scroll_get(textarea->scroll_x);
+	box_y += scroll_get(textarea->scroll_y);
 	scrolled = ensure_caret_visible(textarea);
-	box_x -= textarea->scroll_x;
-	box_y -= textarea->scroll_y;
+	box_x -= scroll_get(textarea->scroll_x);
+	box_y -= scroll_get(textarea->scroll_y);
 	
 	browser_window_place_caret(bw,
 			box_x + inline_container->x + text_box->x +
@@ -332,8 +333,8 @@
 			(int) text_box->length, text_box->text));
 
 	box_coords(textarea, &box_x, &box_y);
-	box_x -= textarea->scroll_x;
-	box_y -= textarea->scroll_y;
+	box_x -= scroll_get(textarea->scroll_x);
+	box_y -= scroll_get(textarea->scroll_y);
 
 	if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
 		/* normal character insertion */		
@@ -756,8 +757,8 @@
 			assert(text_box);
 			assert(char_offset <= text_box->length);
 			/* Scroll back to the left */
-			box_x += textarea->scroll_x;
-			textarea->scroll_x = 0;
+			box_x += scroll_get(textarea->scroll_x);
+			scroll_set(textarea->scroll_x, 0, false);
 		} else {
 			assert(!text_box->next ||
 					(text_box->next &&
@@ -778,11 +779,11 @@
 	textarea->gadget->caret_box_offset = char_offset;
 	textarea->gadget->caret_pixel_offset = pixel_offset;
 
-	box_x += textarea->scroll_x;
-	box_y += textarea->scroll_y;
+	box_x += scroll_get(textarea->scroll_x);
+	box_y += scroll_get(textarea->scroll_y);
 	scrolled = ensure_caret_visible(textarea);
-	box_x -= textarea->scroll_x;
-	box_y -= textarea->scroll_y;
+	box_x -= scroll_get(textarea->scroll_x);
+	box_y -= scroll_get(textarea->scroll_y);
 
 	browser_window_place_caret(bw,
 			box_x + inline_container->x + text_box->x +
@@ -1398,11 +1399,11 @@
 		textarea->gadget->caret_pixel_offset = pixel_offset;
 
 		box_coords(textarea, &box_x, &box_y);
-		box_x += textarea->scroll_x;
-		box_y += textarea->scroll_y;
+		box_x += scroll_get(textarea->scroll_x);
+		box_y += scroll_get(textarea->scroll_y);
 		ensure_caret_visible(textarea);
-		box_x -= textarea->scroll_x;
-		box_y -= textarea->scroll_y;
+		box_x -= scroll_get(textarea->scroll_x);
+		box_y -= scroll_get(textarea->scroll_y);
 
 		browser_window_place_caret(bw,
 				box_x + inline_container->x + text_box->x +
@@ -1518,8 +1519,8 @@
 	font_plot_style_from_css(text_box->style, &fstyle);
 
 	box_coords(textarea, &box_x, &box_y);
-	box_x -= textarea->scroll_x;
-	box_y -= textarea->scroll_y;
+	box_x -= scroll_get(textarea->scroll_x);
+	box_y -= scroll_get(textarea->scroll_y);
 
 	nsfont.font_width(&fstyle, text_box->text,
 			char_offset, &pixel_offset);
@@ -2191,8 +2192,8 @@
 
 	assert(textarea->gadget);
 
-	scrollx = textarea->scroll_x;
-	scrolly = textarea->scroll_y;
+	scrollx = scroll_get(textarea->scroll_x);
+	scrolly = scroll_get(textarea->scroll_y);
 
 	/* Calculate the caret coordinates */
 	cx = textarea->gadget->caret_pixel_offset + 
@@ -2200,29 +2201,31 @@
 	cy = textarea->gadget->caret_text_box->y;
 
 	/* Ensure they are visible */
-	if (!box_hscrollbar_present(textarea)) {
+	if (textarea->scroll_x == NULL) {
 		scrollx = 0;
-	} else if (cx-textarea->scroll_x < 0) {
+	} else if (cx - scroll_get(textarea->scroll_x) < 0) {
 		scrollx = cx;
-	} else if (cx > textarea->scroll_x + textarea->width) {
+	} else if (cx > scroll_get(textarea->scroll_x) + textarea->width) {
 		scrollx = cx - textarea->width;
 	}
 
-	if (!box_vscrollbar_present(textarea)) {
+	if (textarea->scroll_y == NULL) {
 		scrolly = 0;
-	} else if (cy - textarea->scroll_y < 0) {
+	} else if (cy - scroll_get(textarea->scroll_y) < 0) {
 		scrolly = cy;
 	} else if (cy + textarea->gadget->caret_text_box->height > 
-			textarea->scroll_y + textarea->height) {
+			  scroll_get(textarea->scroll_y) + textarea->height) {
 		scrolly = (cy + textarea->gadget->caret_text_box->height) - 
 				textarea->height;
 	}
 
-	if ((scrollx == textarea->scroll_x) && (scrolly == textarea->scroll_y))
+	if ((scrollx == scroll_get(textarea->scroll_x)) &&
+			(scrolly == scroll_get(textarea->scroll_y)))
 		return false;
 
-	textarea->scroll_x = scrollx;
-	textarea->scroll_y = scrolly;
+	
+	scroll_set(textarea->scroll_x, scrollx, false);
+	scroll_set(textarea->scroll_y, scrolly, false);
 
 	return true;
 }
Index: desktop/browser.c
===================================================================
--- desktop/browser.c	(revision 9172)
+++ desktop/browser.c	(working copy)
@@ -5,6 +5,7 @@
  * Copyright 2004 John Tytgat <joty at netsurf-browser.org>
  * Copyright 2006 Richard Wilson <info at tinct.net>
  * Copyright 2008 Michael Drake <tlsa at netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl at users.sourceforge.net>  
  *
  * This file is part of NetSurf, http://www.netsurf-browser.org/
  *
@@ -46,6 +47,7 @@
 #include "desktop/history_core.h"
 #include "desktop/gui.h"
 #include "desktop/options.h"
+#include "desktop/scroll.h"
 #include "desktop/selection.h"
 #include "desktop/textinput.h"
 #include "render/box.h"
@@ -109,9 +111,6 @@
 		browser_mouse_state mouse, int x, int y);
 static void browser_window_mouse_track_text(struct browser_window *bw,
 		browser_mouse_state mouse, int x, int y);
-static const char *browser_window_scrollbar_click(struct browser_window *bw,
-		browser_mouse_state mouse, struct box *box,
-		int box_x, int box_y, int x, int y);
 static void browser_radio_set(struct content *content,
 		struct form_control *radio);
 static gui_pointer_shape get_pointer_shape(struct browser_window *bw,
@@ -126,10 +125,9 @@
 		int x, int y, int dir, int *dx, int *dy);
 static void browser_window_page_drag_start(struct browser_window *bw,
 		int x, int y);
-static void browser_window_scroll_box(struct browser_window *bw,
-		struct box *box, int scroll_x, int scroll_y);
+static void browser_window_box_drag_start(struct browser_window *bw,
+		struct box *box, int x, int y);
 
-
 /**
  * Create and open a new browser window with the given page.
  *
@@ -444,6 +442,8 @@
 		bw->loading_content = NULL;
 		browser_window_remove_caret(bw);
 		bw->scrolling_box = NULL;
+		bw->scroll_x = NULL;
+		bw->scroll_y = NULL;
 		gui_window_new_content(bw->window);
 		if (bw->current_content) {
 			browser_window_refresh_url_bar(bw,
@@ -515,6 +515,8 @@
 			bw->current_content = 0;
 			browser_window_remove_caret(bw);
 			bw->scrolling_box = NULL;
+			bw->scroll_x = NULL;
+			bw->scroll_y = NULL;
 			selection_init(bw->sel, NULL);
 		}
 		browser_window_stop_throbber(bw);
@@ -592,6 +594,8 @@
 			bw->current_content = 0;
 			browser_window_remove_caret(bw);
 			bw->scrolling_box = NULL;
+			bw->scroll_x = NULL;
+			bw->scroll_y = NULL;
 			selection_init(bw->sel, NULL);
 		}
 		browser_window_stop_throbber(bw);
@@ -607,6 +611,8 @@
 			bw->current_content = 0;
 			browser_window_remove_caret(bw);
 			bw->scrolling_box = NULL;
+			bw->scroll_x = NULL;
+			bw->scroll_y = NULL;
 			selection_init(bw->sel, NULL);
 		}
 		browser_window_stop_throbber(bw);
@@ -1103,6 +1109,8 @@
 
 	assert(bw);
 	assert(url);
+	
+	bw->visible_select_menu = NULL;
 
 	if (frag == NULL) {
 		/* With no fragment, we may as well pass url straight through
@@ -1392,11 +1400,9 @@
 	bool imagemap = false;
 	int box_x = 0, box_y = 0;
 	int gadget_box_x = 0, gadget_box_y = 0;
-	int scroll_box_x = 0, scroll_box_y = 0;
 	int text_box_x = 0;
 	struct box *url_box = 0;
 	struct box *gadget_box = 0;
-	struct box *scroll_box = 0;
 	struct box *text_box = 0;
 	struct content *c = bw->current_content;
 	struct box *box;
@@ -1405,8 +1411,58 @@
 	struct form_control *gadget = 0;
 	struct content *object = NULL;
 	struct box *next_box;
+	struct box *drag_candidate = NULL;
+	struct scroll *scroll = NULL;
 	plot_font_style_t fstyle;
+	int scroll_mouse_x = 0, scroll_mouse_y = 0;
+	
 
+	if (bw->visible_select_menu != NULL) {
+		box = bw->visible_select_menu->box;
+		box_coords(box, &box_x, &box_y);
+
+		box_x -= box->border[LEFT].width;
+		box_y += box->height + box->border[BOTTOM].width +
+				box->padding[BOTTOM] + box->padding[TOP];
+		status = form_select_mouse_action(bw->visible_select_menu,
+				mouse, x - box_x, y - box_y);
+		if (status != NULL)
+			browser_window_set_status(bw, status);
+		else {
+			int width, height;
+			form_select_get_dimensions(bw->visible_select_menu,
+					&width, &height);
+			bw->visible_select_menu = NULL;
+			browser_window_redraw_rect(bw, box_x, box_y,
+					width, height);					
+		}
+		return;
+	}
+	
+	if (bw->scroll_x != NULL || bw->scroll_y != NULL) {
+		box = bw->scrolling_box;
+		box_coords(box, &box_x, &box_y);
+		if (bw->scroll_x != NULL) {
+			scroll_mouse_x = x - box_x ;
+			scroll_mouse_y = y - (box_y + box->padding[TOP] +
+					box->height + box->padding[BOTTOM] -
+					SCROLLBAR_WIDTH);
+			status = scroll_mouse_action(bw->scroll_x, mouse,
+					scroll_mouse_x, scroll_mouse_y);
+		}
+		if (bw->scroll_y != NULL) {
+			scroll_mouse_x = x - (box_x + box->padding[LEFT] +
+					box->width + box->padding[RIGHT] -
+					SCROLLBAR_WIDTH);
+			scroll_mouse_y = y - box_y;
+			status = scroll_mouse_action(bw->scroll_y, mouse, 
+					scroll_mouse_x, scroll_mouse_y);
+		}
+		
+		browser_window_set_status(bw, status);
+		return;
+	}
+	
 	bw->drag_type = DRAGGING_NONE;
 	bw->scrolling_box = NULL;
 
@@ -1465,20 +1521,38 @@
 		if (box->style)
 			overflow = css_computed_overflow(box->style);
 
-		if (box->style && box->type != BOX_BR &&
-				box->type != BOX_INLINE &&
-				box->type != BOX_TEXT &&
-				(overflow == CSS_OVERFLOW_SCROLL ||
-				 overflow == CSS_OVERFLOW_AUTO) &&
-				((box_vscrollbar_present(box) &&
-				  box_x + box->scroll_x + box->padding[LEFT] +
-				  box->width < x) ||
-				 (box_hscrollbar_present(box) &&
-				  box_y + box->scroll_y + box->padding[TOP] +
-				  box->height < y))) {
-			scroll_box = box;
-			scroll_box_x = box_x + box->scroll_x;
-			scroll_box_y = box_y + box->scroll_y;
+		if ((box->scroll_x != NULL || box->scroll_y != NULL) &&
+				   drag_candidate == NULL)
+			drag_candidate = box;
+		
+		if (box->scroll_y != NULL &&
+				x > box_x + scroll_get(box->scroll_x) +
+				box->padding[LEFT] + box->width +
+				box->padding[RIGHT] - SCROLLBAR_WIDTH) {
+			bw->scrolling_box = box;
+			scroll = box->scroll_y;
+			scroll_mouse_x = x - (box_x +
+					scroll_get(box->scroll_x) +
+					box->padding[LEFT] +
+					box->width + box->padding[RIGHT] -
+					SCROLLBAR_WIDTH);
+			scroll_mouse_y = y -
+					(box_y + scroll_get(box->scroll_y));
+			break;
+		} else if (box->scroll_x != NULL &&
+				y > box_y + scroll_get(box->scroll_y) +
+				box->padding[TOP] + box->height +
+				box->padding[BOTTOM] - SCROLLBAR_WIDTH) {
+			bw->scrolling_box = box;
+			scroll = box->scroll_x;
+			scroll_mouse_x = x -
+					(box_x + scroll_get(box->scroll_x));
+			scroll_mouse_y = y - (box_y +
+					scroll_get(box->scroll_y) +
+					box->padding[TOP] +
+					box->height + box->padding[BOTTOM] -
+					SCROLLBAR_WIDTH);
+			break;
 		}
 
 		if (box->text && !box->object) {
@@ -1490,18 +1564,22 @@
 	/* use of box_x, box_y, or content below this point is probably a
 	 * mistake; they will refer to the last box returned by box_at_point */
 
-	if (scroll_box) {
-		status = browser_window_scrollbar_click(bw, mouse, scroll_box,
-				scroll_box_x, scroll_box_y,
-				x - scroll_box_x, y - scroll_box_y);
+	if (scroll) {
+		status = scroll_mouse_action(scroll, mouse,
+				scroll_mouse_x, scroll_mouse_y);
 		pointer = GUI_POINTER_DEFAULT;
 	} else if (gadget) {
 		switch (gadget->type) {
 		case GADGET_SELECT:
 			status = messages_get("FormSelect");
 			pointer = GUI_POINTER_MENU;
-			if (mouse & BROWSER_MOUSE_CLICK_1)
-				gui_create_form_select_menu(bw, gadget);
+			if (mouse & BROWSER_MOUSE_CLICK_1) {
+				bw->visible_select_menu = gadget;
+				form_open_select_menu(bw, gadget,
+						browser_select_menu_callback,
+      						bw);
+				pointer =  GUI_POINTER_DEFAULT;
+			}
 			break;
 		case GADGET_CHECKBOX:
 			status = messages_get("FormCheckbox");
@@ -1749,8 +1827,15 @@
 					gui_drag_save_object(GUI_SAVE_COMPLETE,
 							c, bw->window);
 				} else {
-					browser_window_page_drag_start(bw,
-							x, y);
+					if (drag_candidate == NULL)
+						browser_window_page_drag_start(
+								bw, x, y);
+					else {
+						browser_window_box_drag_start(
+								bw,
+								drag_candidate,
+								x, y);
+					}
 					pointer = GUI_POINTER_MOVE;
 				}
 			}
@@ -1759,8 +1844,15 @@
 					gui_drag_save_object(GUI_SAVE_SOURCE,
 							c, bw->window);
 				} else {
-					browser_window_page_drag_start(bw,
-							x, y);
+					if (drag_candidate == NULL)
+						browser_window_page_drag_start(
+								bw, x, y);
+					else {
+						browser_window_box_drag_start(
+								bw,
+								drag_candidate,
+								x, y);
+					}
 					pointer = GUI_POINTER_MOVE;
 				}
 			}
@@ -1926,54 +2018,8 @@
 void browser_window_mouse_track_html(struct browser_window *bw,
 		browser_mouse_state mouse, int x, int y)
 {
+	
 	switch (bw->drag_type) {
-		case DRAGGING_HSCROLL:
-		case DRAGGING_VSCROLL:
-		case DRAGGING_2DSCROLL: {
-			struct box *box = bw->scrolling_box;
-			int scroll_y;
-			int scroll_x;
-
-			assert(box);
-
-			if (bw->drag_type == DRAGGING_HSCROLL) {
-				scroll_y = box->scroll_y;
-			} else {
-				scroll_y = bw->drag_start_scroll_y +
-						(float) (y - bw->drag_start_y) /
-						(float) bw->drag_well_height *
-						(float) (box->descendant_y1 -
-						box->descendant_y0);
-				if (scroll_y < box->descendant_y0)
-					scroll_y = box->descendant_y0;
-				else if (box->descendant_y1 - box->height <
-						scroll_y)
-					scroll_y = box->descendant_y1 -
-							box->height;
-				if (scroll_y == box->scroll_y)
-					return;
-			}
-
-			if (bw->drag_type == DRAGGING_VSCROLL) {
-				scroll_x = box->scroll_x;
-			} else {
-				scroll_x = bw->drag_start_scroll_x +
-						(float) (x - bw->drag_start_x) /
-						(float) bw->drag_well_width *
-						(float) (box->descendant_x1 -
-						box->descendant_x0);
-				if (scroll_x < box->descendant_x0)
-					scroll_x = box->descendant_x0;
-				else if (box->descendant_x1 - box->width <
-						scroll_x)
-					scroll_x = box->descendant_x1 -
-							box->width;
-			}
-
-			browser_window_scroll_box(bw, box, scroll_x, scroll_y);
-		}
-		break;
-
 		case DRAGGING_SELECTION: {
 			struct box *box;
 			int dir = -1;
@@ -2052,6 +2098,43 @@
 void browser_window_mouse_drag_end(struct browser_window *bw,
 		browser_mouse_state mouse, int x, int y)
 {
+	struct box *box;
+	int scroll_mouse_x, scroll_mouse_y, box_x, box_y;
+	
+	if (bw->visible_select_menu != NULL) {
+		box = bw->visible_select_menu->box;
+		box_coords(box, &box_x, &box_y);
+
+		box_x -= box->border[LEFT].width;
+		box_y += box->height + box->border[BOTTOM].width +
+				box->padding[BOTTOM] + box->padding[TOP];
+		form_select_mouse_drag_end(bw->visible_select_menu,
+				mouse, x - box_x, y - box_y);
+		return;
+	}
+	
+	if (bw->scroll_x != NULL || bw->scroll_y != NULL) {
+		box = bw->scrolling_box;
+		box_coords(box, &box_x, &box_y);
+		if (bw->scroll_x != NULL) {
+			scroll_mouse_x = x - box_x;
+			scroll_mouse_y = y - box_y + box->padding[TOP] +
+					box->height + box->padding[BOTTOM] -
+					SCROLLBAR_WIDTH;
+			scroll_mouse_drag_end(bw->scroll_x, mouse,
+					scroll_mouse_x, scroll_mouse_y);
+		}
+		if (bw->scroll_y != NULL) {
+			scroll_mouse_x = x - box_x + box->padding[LEFT] +
+					box->width + box->padding[RIGHT] -
+					SCROLLBAR_WIDTH;
+			scroll_mouse_y = y - box_y;
+			scroll_mouse_drag_end(bw->scroll_y, mouse,
+					scroll_mouse_x, scroll_mouse_y);
+		}
+		return;
+	}
+	
 	switch (bw->drag_type) {
 		case DRAGGING_SELECTION: {
 			struct content *c = bw->current_content;
@@ -2104,13 +2187,6 @@
 		}
 		break;
 
-		case DRAGGING_2DSCROLL:
-		case DRAGGING_PAGE_SCROLL:
-		case DRAGGING_FRAME:
-			browser_window_set_pointer(bw->window,
-					GUI_POINTER_DEFAULT);
-			break;
-
 		default:
 			break;
 	}
@@ -2120,157 +2196,6 @@
 
 
 /**
- * Handle mouse clicks in a box scrollbar.
- *
- * \param  bw	  browser window
- * \param  mouse  state of mouse buttons and modifier keys
- * \param  box	  scrolling box
- * \param  box_x  position of box in global document coordinates
- * \param  box_y  position of box in global document coordinates
- * \param  x	  coordinate of click relative to box position
- * \param  y	  coordinate of click relative to box position
- * \return status bar message
- */
-
-const char *browser_window_scrollbar_click(struct browser_window *bw,
-		browser_mouse_state mouse, struct box *box,
-		int box_x, int box_y, int x, int y)
-{
-	bool but1 = ((mouse & BROWSER_MOUSE_PRESS_1) ||
-			((mouse & BROWSER_MOUSE_HOLDING_1) &&
-			 (mouse & BROWSER_MOUSE_DRAG_ON)));
-	bool but2 = ((mouse & BROWSER_MOUSE_PRESS_2) ||
-			((mouse & BROWSER_MOUSE_HOLDING_2) &&
-			 (mouse & BROWSER_MOUSE_DRAG_ON)));
-	const int w = SCROLLBAR_WIDTH;
-	bool vscroll, hscroll;
-	int well_height, bar_top, bar_height;
-	int well_width, bar_left, bar_width;
-	const char *status = 0;
-	bool vert;
-	int z, scroll, bar_start, bar_size, well_size, page;
-
-	box_scrollbar_dimensions(box,
-			box->padding[LEFT] + box->width + box->padding[RIGHT],
-			box->padding[TOP] + box->height + box->padding[BOTTOM],
-			w,
-			&vscroll, &hscroll,
-			&well_height, &bar_top, &bar_height,
-			&well_width, &bar_left, &bar_width);
-
-	/* store some data for scroll drags */
-	bw->scrolling_box = box;
-	bw->drag_start_x = box_x + x;
-	bw->drag_start_y = box_y + y;
-	bw->drag_start_scroll_x = box->scroll_x;
-	bw->drag_start_scroll_y = box->scroll_y;
-	bw->drag_well_width = well_width;
-	bw->drag_well_height = well_height;
-
-	/* determine which scrollbar was clicked */
-	if (box_vscrollbar_present(box) &&
-			box->padding[LEFT] + box->width < x) {
-		vert = true;
-		z = y;
-		scroll = box->scroll_y;
-		well_size = well_height;
-		bar_start = bar_top;
-		bar_size = bar_height;
-		page = box->height;
-	} else {
-		vert = false;
-		z = x;
-		scroll = box->scroll_x;
-		well_size = well_width;
-		bar_start = bar_left;
-		bar_size = bar_width;
-		page = box->width;
-	}
-
-	/* find icon in scrollbar and calculate scroll */
-	if (z < w) {
-		/* on scrollbar bump arrow button */
-		status = messages_get(vert ? "ScrollUp" : "ScrollLeft");
-		if (but1)
-			scroll -= 16;
-		else if (but2)
-			scroll += 16;
-	} else if (z < w + bar_start + w / 4) {
-		/* in scrollbar well */
-		status = messages_get(vert ? "ScrollPUp" : "ScrollPLeft");
-		if (but1)
-			scroll -= page;
-		else if (but2)
-			scroll += page;
-	} else if (z < w + bar_start + bar_size - w / 4) {
-		/* in scrollbar */
-		status = messages_get(vert ? "ScrollV" : "ScrollH");
-
-		if (mouse & (BROWSER_MOUSE_HOLDING_1 |
-				BROWSER_MOUSE_HOLDING_2)) {
-			int x0 = 0, x1 = 0;
-			int y0 = 0, y1 = 0;
-
-			if (mouse & BROWSER_MOUSE_HOLDING_1) {
-				bw->drag_type = vert ? DRAGGING_VSCROLL :
-						DRAGGING_HSCROLL;
-			} else
-				bw->drag_type = DRAGGING_2DSCROLL;
-
-			/* \todo - some proper numbers please! */
-			if (bw->drag_type != DRAGGING_VSCROLL) {
-				x0 = -1024;
-				x1 = 1024;
-			}
-			if (bw->drag_type != DRAGGING_HSCROLL) {
-				y0 = -1024;
-				y1 = 1024;
-			}
-			gui_window_box_scroll_start(bw->window, x0, y0, x1, y1);
-			if (bw->drag_type == DRAGGING_2DSCROLL)
-				gui_window_hide_pointer(bw->window);
-		}
-	} else if (z < w + well_size) {
-		/* in scrollbar well */
-		status = messages_get(vert ? "ScrollPDown" : "ScrollPRight");
-		if (but1)
-			scroll += page;
-		else if (but2)
-			scroll -= page;
-	} else {
-		/* on scrollbar bump arrow button */
-		status = messages_get(vert ? "ScrollDown" : "ScrollRight");
-		if (but1)
-			scroll += 16;
-		else if (but2)
-			scroll -= 16;
-	}
-
-	/* update box and redraw */
-	if (vert) {
-		if (scroll < box->descendant_y0)
-			scroll = box->descendant_y0;
-		else if (box->descendant_y1 - box->height < scroll)
-			scroll = box->descendant_y1 - box->height;
-		if (scroll != box->scroll_y)
-			browser_window_scroll_box(bw, box, box->scroll_x,
-					scroll);
-
-	} else {
-		if (scroll < box->descendant_x0)
-			scroll = box->descendant_x0;
-		else if (box->descendant_x1 - box->width < scroll)
-			scroll = box->descendant_x1 - box->width;
-		if (scroll != box->scroll_x)
-			browser_window_scroll_box(bw, box, scroll,
-					box->scroll_y);
-	}
-
-	return status;
-}
-
-
-/**
  * Set a radio form control and clear the others in the group.
  *
  * \param  content  content containing the form, of type CONTENT_TYPE
@@ -2380,27 +2305,6 @@
 
 
 /**
- * Update the scroll offsets of a box within a browser window
- * (In future, copying where possible, rather than redrawing the entire box)
- *
- * \param  bw	     browser window
- * \param  box	     box to be updated
- * \param  scroll_x  new horizontal scroll offset
- * \param  scroll_y  new vertical scroll offset
- */
-
-void browser_window_scroll_box(struct browser_window *bw, struct box *box,
-		int scroll_x, int scroll_y)
-{
-	box->scroll_x = scroll_x;
-	box->scroll_y = scroll_y;
-
-	/* fall back to redrawing the whole box */
-	browser_redraw_box(bw->current_content, box);
-}
-
-
-/**
  * Process a selection from a form select menu.
  *
  * \param  bw	    browser window with menu
@@ -2643,8 +2547,117 @@
 	free(url);
 }
 
+/**
+ * Callback for in-page scrolls.
+ */
+void browser_scroll_callback(void *client_data,
+		struct scroll_msg_data *scroll_data)
+{
+	struct browser_window *bw = client_data;
+	struct box *box = bw->scrolling_box;
+	int x0 = 0, x1 = 0;
+	int y0 = 0, y1 = 0;
+	int x = 0, y = 0, box_x, box_y, diff_x = 0, diff_y = 0;
+	
+	if (box != NULL) {
+		diff_x = box->padding[LEFT] + box->width + box->padding[RIGHT] -
+				SCROLLBAR_WIDTH;
+		diff_y = box->padding[TOP] + box->height + box->padding[BOTTOM] -
+				SCROLLBAR_WIDTH;
+		
+		box_coords(box, &box_x, &box_y);
+		if (scroll_is_horizontal(scroll_data->scroll)) {
+			x = box_x + scroll_get(box->scroll_x);
+			y = box_y + scroll_get(box->scroll_y) + diff_y;
+		} else {
+			x = box_x + scroll_get(box->scroll_x) + diff_x;
+			y = box_y + scroll_get(box->scroll_y);
+		}
+	}
+	
+	switch(scroll_data->msg) {
+		case SCROLL_MSG_REDRAW:
+			browser_window_redraw_rect(bw,
+					x + scroll_data->x0,
+					y + scroll_data->y0,
+     					scroll_data->x1 - scroll_data->x0,
+					scroll_data->y1 - scroll_data->y0);
+			break;
+		case SCROLL_MSG_MOVED:
+			browser_redraw_box(bw->current_content, box);
+			break;
+		case SCROLL_MSG_CAPTURE_MOUSE:
+			if (scroll_is_horizontal(scroll_data->scroll))
+				bw->scroll_x = scroll_data->scroll;
+			else
+				bw->scroll_y = scroll_data->scroll;
+			/* \todo - some proper numbers please! */
+			if (bw->scroll_y != NULL && bw->scroll_x != NULL) {
+				x0 = -1024;
+				x1 = 1024;
+				y0 = -1024;
+				y1 = 1024;
+			} else if (bw->scroll_y != NULL) {
+					y0 = -1024;
+					y1 = 1024;
+			} else if (bw->scroll_x != NULL) {
+					x0 = -1024;
+					x1 = 1024;
+			}
+			gui_window_box_scroll_start(bw->window, x0, y0, x1, y1);
+			break;
+		case SCROLL_MSG_RELEASE_MOUSE:
+			if (scroll_data->scroll == bw->scroll_x)
+				bw->scroll_x = NULL;
+			else 
+				bw->scroll_y = NULL;
+			
+			if (bw->scroll_x == NULL && bw->scroll_y == NULL)
+				bw->scrolling_box = NULL;
+			
+			browser_window_set_pointer(bw->window,
+					GUI_POINTER_DEFAULT);
+			break;
+		case SCROLL_MSG_2D_DRAG:
+			if (scroll_data->scroll == bw->scroll_x &&
+					box->scroll_y != NULL) {
+				scroll_force_drag_start(box->scroll_y,
+						scroll_data->x0 - diff_x,
+						scroll_data->y0 + diff_y,
+      						false);
+			} else if (scroll_data->scroll == bw->scroll_y &&
+					box->scroll_x != NULL) {
+				scroll_force_drag_start(box->scroll_x,
+						scroll_data->x0 + diff_x,
+						scroll_data->y0 - diff_y,
+      						false);
+			}
+			break;
+	}
+}
 
+/**
+ * Callback for the core select menu.
+ */
+void browser_select_menu_callback(void *client_data,
+		int x, int y, int width, int height)
+{
+	struct browser_window *bw = client_data;
+	int menu_x, menu_y;
+	struct box *box;
+	
+	box = bw->visible_select_menu->box;
+	box_coords(box, &menu_x, &menu_y);
+		
+	menu_x -= box->border[LEFT].width;
+	menu_y += box->height + box->border[BOTTOM].width +
+			box->padding[BOTTOM] +
+			box->padding[TOP];
+	browser_window_redraw_rect(bw, menu_x + x, menu_y + y,
+			width, height);
+}
 
+
 /**
  * Check whether box is nearer mouse coordinates than current nearest box
  *
@@ -2766,11 +2779,11 @@
 	while (child) {
 		if (child->type == BOX_FLOAT_LEFT ||
 				child->type == BOX_FLOAT_RIGHT) {
-			c_bx = fx + child->x - child->scroll_x;
-			c_by = fy + child->y - child->scroll_y;
+			c_bx = fx + child->x - scroll_get(child->scroll_x);
+			c_by = fy + child->y - scroll_get(child->scroll_y);
 		} else {
-			c_bx = bx + child->x - child->scroll_x;
-			c_by = by + child->y - child->scroll_y;
+			c_bx = bx + child->x - scroll_get(child->scroll_x);
+			c_by = by + child->y - scroll_get(child->scroll_y);
 		}
 		if (child->float_children) {
 			c_fx = c_bx;
@@ -2880,12 +2893,50 @@
 	bw->drag_start_x = x;
 	bw->drag_start_y = y;
 
-	gui_window_get_scroll(bw->window, &bw->drag_start_scroll_x, &bw->drag_start_scroll_y);
+	gui_window_get_scroll(bw->window, &bw->drag_start_scroll_x,
+			&bw->drag_start_scroll_y);
 
 	gui_window_scroll_start(bw->window);
 }
 
+/**
+ * Start drag scrolling the contents of a box
+ *
+ * \param bw	browser window
+ * \param box	the box to be scrolled
+ * \param x	x ordinate of initial mouse position
+ * \param y	y ordinate
+ */
 
+void browser_window_box_drag_start(struct browser_window *bw,
+		struct box *box, int x, int y)
+{
+	int box_x, box_y, scroll_mouse_x, scroll_mouse_y;
+	
+	box_coords(box, &box_x, &box_y);
+	
+	if (box->scroll_x != NULL) {
+		scroll_mouse_x = x - box_x ;
+		scroll_mouse_y = y - (box_y + box->padding[TOP] +
+				box->height + box->padding[BOTTOM] -
+				SCROLLBAR_WIDTH);
+		bw->scrolling_box = box;
+		scroll_force_drag_start(box->scroll_x,
+				scroll_mouse_x, scroll_mouse_y, true);
+	}
+	if (box->scroll_y != NULL) {
+		scroll_mouse_x = x - (box_x + box->padding[LEFT] +
+				box->width + box->padding[RIGHT] -
+				SCROLLBAR_WIDTH);
+		scroll_mouse_y = y - box_y;
+		
+		bw->scrolling_box = box;
+		scroll_force_drag_start(box->scroll_y,
+				scroll_mouse_x, scroll_mouse_y, true);
+	}
+}
+
+
 /**
  * Check availability of Back action for a given browser window
  *
Index: desktop/browser.h
===================================================================
--- desktop/browser.h	(revision 9172)
+++ desktop/browser.h	(working copy)
@@ -27,6 +27,7 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <time.h>
+
 #include "render/html.h"
 
 struct box;
@@ -40,8 +41,8 @@
 struct browser_window;
 struct url_data;
 struct bitmap;
+struct scroll_msg_data;
 
-
 typedef bool (*browser_caret_callback)(struct browser_window *bw,
 	uint32_t key, void *p);
 typedef bool (*browser_paste_callback)(struct browser_window *bw,
@@ -88,11 +89,8 @@
 	/** Current drag status. */
 	enum {
 		DRAGGING_NONE,
-		DRAGGING_VSCROLL,
-		DRAGGING_HSCROLL,
 		DRAGGING_SELECTION,
 		DRAGGING_PAGE_SCROLL,
-		DRAGGING_2DSCROLL,
 		DRAGGING_FRAME
 	} drag_type;
 
@@ -104,14 +102,15 @@
 	/** Scroll offsets at start of current scroll draw. */
 	int drag_start_scroll_x;
 	int drag_start_scroll_y;
-	/** Well dimensions for current scroll drag. */
-	int drag_well_width;
-	int drag_well_height;
 	/** Frame resize directions for current frame resize drag. */
 	unsigned int drag_resize_left : 1;
 	unsigned int drag_resize_right : 1;
 	unsigned int drag_resize_up : 1;
 	unsigned int drag_resize_down : 1;
+	
+	/** Scrolls capturing all mouse events */
+	struct scroll *scroll_x;
+	struct scroll *scroll_y;
 
 	/** Referrer for current fetch, or 0. */
 	char *referer;
@@ -174,6 +173,8 @@
 
 	/** Last time a link was followed in this window */
 	unsigned int last_action;
+	
+	struct form_control *visible_select_menu;
 };
 
 
@@ -257,6 +258,11 @@
 void browser_form_submit(struct browser_window *bw, struct browser_window *target,
 		struct form *form, struct form_control *submit_button);
 
+void browser_scroll_callback(void *client_data,
+		struct scroll_msg_data *scroll_data);
+void browser_select_menu_callback(void *client_data,
+		int x, int y, int width, int height);
+		
 void browser_window_redraw_rect(struct browser_window *bw, int x, int y,
 		int width, int height);
 
Index: amiga/thumbnail.c
===================================================================
--- amiga/thumbnail.c	(revision 9172)
+++ amiga/thumbnail.c	(working copy)
@@ -26,6 +26,7 @@
 #include "amiga/bitmap.h"
 #include "amiga/options.h"
 #include "content/urldb.h"
+#include "desktop/plotters.h"
 
 bool thumbnail_create(struct content *content, struct bitmap *bitmap,
 	const char *url)
@@ -39,6 +40,7 @@
 	bitmap->nativebmwidth = bitmap->width;
 	bitmap->nativebmheight = bitmap->height;
 	ami_clearclipreg(&browserglob.rp);
+	plot = amiplot;
 	content_redraw(content, 0, 0, content->width, content->width,
 	0, 0, content->width, content->width, 1.0, 0xFFFFFF);
 


Conflicted files




Removed files






More information about the netsurf-dev mailing list