Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtkhtml2/ChangeLog,v
retrieving revision 1.408
diff -u -p -r1.408 ChangeLog
--- ChangeLog	8 Jan 2004 13:55:15 -0000	1.408
+++ ChangeLog	12 Jan 2004 09:15:15 -0000
@@ -1,3 +1,38 @@
+2004-01-12  Padraig O'Briain  <padraig.obriain@sun.com>
+
+	Add support  for movement to start, end, next line, previous line,
+	next page, previous page when cursor is showing. Also some tidy up.
+
+	* libgtkhtml/view/htmlview.c:
+	(find_last_child): New function which finds last HtmlBox for a 
+	HtmlView.
+	(find_previous_box): New function which finds the HtmlBox immediately
+	previous to a specified HtmlBox.
+	(find_previous_box_text): New function which finds the first 
+	HtmlBoxText previous to a specified HtmlBox.
+	(find_next_box): New function which finds the HtmlBox immediately
+	after a specified HtmlBox.
+	(find_next_box_text): New function which finds the first HtmlBoxText
+	after a specified HtmlBox.
+	(find_box): This function is removed.
+	(html_view_get_box_text_for_offset): Update function to use
+	find_next_box_text instead of find_box.
+	(html_view_draw_cursor): Remove variable at_end. If cursor is at the
+	end html_view_get_box_text_for_offset will return the correct position.
+	(html_view_move_cursor_by_line): New function which move the cursor
+	to the next or previous line when cursor is showing..
+	(get_end_text_offset): Return the last HtmlBox which is a HtmlBoxText
+	and contains text.
+	(html_view_move_cursor_to_end): New function which moves cursor to
+	end when cursor is showing.
+	(html_view_move_cursor_to_start): New function which moves cursor to
+	start when cursor is showing.
+	(html_view_move_cursor_by_page): New function which move the cursor
+	to the next or previous page when cursor is showing.
+	(html_view_real_move_cursor): Add support for moving cursor, by line,
+	by page and to start and end when cursore is showing.
+
+	
 2004-01-08  Padraig O'Briain  <padraig.obriain@sun.com>
 
 	* libgtkhtml/view/htmlview.c (html_view_real_move_cursor): Separate
Index: libgtkhtml/a11y/htmlviewaccessible.c
===================================================================
RCS file: /cvs/gnome/gtkhtml2/libgtkhtml/a11y/htmlviewaccessible.c,v
retrieving revision 1.11
diff -u -p -r1.11 htmlviewaccessible.c
--- libgtkhtml/a11y/htmlviewaccessible.c	10 Oct 2003 09:21:38 -0000	1.11
+++ libgtkhtml/a11y/htmlviewaccessible.c	12 Jan 2004 09:15:15 -0000
@@ -24,7 +24,7 @@
 #include <libgtkhtml/layout/htmlboxinline.h>
 #include <libgtkhtml/layout/htmlboxtext.h>
 
-#undef DUMP_BOXES
+#define DUMP_BOXES
 
 static void       html_view_accessible_class_init          (HtmlViewAccessibleClass *klass);
 static void       html_view_accessible_finalize            (GObject            *obj);
Index: libgtkhtml/view/htmlview.c
===================================================================
RCS file: /cvs/gnome/gtkhtml2/libgtkhtml/view/htmlview.c,v
retrieving revision 1.163
diff -u -p -r1.163 htmlview.c
--- libgtkhtml/view/htmlview.c	8 Jan 2004 13:55:18 -0000	1.163
+++ libgtkhtml/view/htmlview.c	12 Jan 2004 09:15:16 -0000
@@ -94,31 +94,120 @@ static GQuark quark_blink_timeout = 0;
 static GQuark quark_button = 0;
 static gboolean cursor_showing = FALSE;
 
-static HtmlBoxText*
-find_box (HtmlBox *box, gint *offset, gboolean forward)
+static HtmlBox*
+find_last_child (HtmlBox *box)
 {
 	HtmlBox *child;
+
+	child = box->children;
+	while (child) {
+		while (child->next) {
+			child = child->next;
+		}
+		if (child->children) {
+			child = child->children;
+		} else {
+			break;
+		}
+	}
+	return child;
+}
+
+static HtmlBox*
+find_previous_box (HtmlBox *box)
+{
+	HtmlBox *prev;
+	HtmlBox *tmp;
+
+	prev = box;
+
+	if (prev->prev) {
+		tmp = find_last_child (prev->prev);
+		if (tmp) {
+			prev = tmp;
+		} else {
+			prev = prev->prev;
+		}
+	} else {
+		prev = prev->parent;
+		while (prev)  {
+			if (prev->prev) {
+				tmp = find_last_child (prev->prev);
+				if (tmp) {
+					prev = tmp;
+				} else {
+					prev = prev->prev;
+				}
+				break;
+			} else {
+				prev = prev->parent;
+			}
+		}
+	}
+	return prev;
+}
+
+static HtmlBoxText*
+find_previous_box_text (HtmlBox *box)
+{
+	HtmlBox *prev;
 	HtmlBoxText *text;
-	gchar *char_text;
-	gint len;
 
-	if (HTML_IS_BOX_TEXT (box)) {
-		text = HTML_BOX_TEXT (box);
-		char_text = html_box_text_get_text (text, &len);
-		len = g_utf8_strlen (char_text, len);
-		if (len > *offset)
-			return text;
-		else if (!forward && *offset == len)
-			return text;	
-		else
-			*offset -= len;
+	prev = box;
+
+	while (prev) {
+		prev = find_previous_box (prev);
+		if (HTML_IS_BOX_TEXT (prev)) {
+			text = HTML_BOX_TEXT (prev);
+			if (html_box_text_get_len (text)) {
+				return text;
+			}
+		}
 	}
-	child = box->children;	
-	while (child) {
-		text = find_box (child, offset, forward);
-		if (text)
-			return text;
-		child = child->next;
+	return NULL;
+}
+
+static HtmlBox*
+find_next_box (HtmlBox *box)
+{
+	HtmlBox *next;
+
+	next = box;
+
+	if (next->children) {
+		next = next->children;
+	} else  if (next->next) {
+		next = next->next;
+	} else {
+		next = next->parent;
+		while (next)  {
+			if (next->next) {
+				next = next->next;
+				break;
+			} else {
+				next = next->parent;
+			}
+		}
+	}
+	return next;
+}
+
+static HtmlBoxText*
+find_next_box_text (HtmlBox *box)
+{
+	HtmlBox *next;
+	HtmlBoxText *text;
+
+	next = box;
+
+	while (next) {
+		next = find_next_box (next);
+		if (HTML_IS_BOX_TEXT (next)) {
+			text = HTML_BOX_TEXT (next);
+			if (html_box_text_get_len (text)) {
+				return text;
+			}
+		}
 	}
 	return NULL;
 }
@@ -126,19 +215,39 @@ find_box (HtmlBox *box, gint *offset, gb
 static HtmlBoxText*
 html_view_get_box_text_for_offset (HtmlView *view, gint *offset, gboolean forward)
 {
-	HtmlBox *root;
-	gint temp_offset = *offset;
+	HtmlBox *box;
+	HtmlBoxText *last_text;
 	HtmlBoxText *text;
+	gchar *char_text;
+	gint len;
 
-	root = view->root;
+	box = view->root;
 
-	if (root) {
-		text = find_box (root, &temp_offset, forward);
-		if (text)
-			*offset = temp_offset;
-		return text;
-	} else
-		return NULL;
+	last_text = NULL;
+	while (box) {
+		text = find_next_box_text (box);
+		if (text) {
+			char_text = html_box_text_get_text (text, &len);
+			len = g_utf8_strlen (char_text, len);
+			if (len > 0) {
+				if (len > *offset) {
+					return text;
+				} else if (!forward && *offset == len) {
+					return text;	
+				}
+				*offset -= len;
+				if (*offset == 0) {
+					last_text = text;
+				}
+				box = HTML_BOX (text);
+			}
+		} else {
+			if (last_text) {
+				*offset = len + 1;
+			}
+			return last_text;
+		}
+	}
 }
 
 static gboolean
@@ -525,23 +634,18 @@ html_view_draw_cursor (HtmlView *view)
 	HtmlGdkPainter *gdk_painter;
 	gint direction;
 	GtkTextDirection text_direction;
-	gboolean at_end;
 
 	if (!cursor_showing)
 		return;
 
         cursor_position = html_view_get_cursor_position (view);
 	text = html_view_get_box_text_for_offset (view, &cursor_position, TRUE);
-	at_end = (text == NULL);
-	if (at_end) {
+	if (text == NULL) {
 		/*
 		 * Text may be deleted so do not attempt to display cursor
 		 */
-		if (cursor_position <= 0)
-			return;
-		cursor_position--;
-		text = html_view_get_box_text_for_offset (view, &cursor_position, TRUE);
-		g_return_if_fail (text);
+		g_assert (cursor_position <= 0);
+		return;
 	}
 	html_box_text_get_character_extents (text, cursor_position, &rect);
 	box = HTML_BOX (text);
@@ -551,8 +655,6 @@ html_view_draw_cursor (HtmlView *view)
 	text_direction = (direction == HTML_DIRECTION_RTL) ? GTK_TEXT_DIR_RTL
 							   : GTK_TEXT_DIR_LTR;
 	rect.x += x - box->x;
-	if (at_end)
-		rect.x += rect.width;
 	rect.y += y - box->y;
 	rect.width = 0;
 	gdk_painter = HTML_GDK_PAINTER (view->painter); 
@@ -857,6 +959,181 @@ set_adjustment_clamped (GtkAdjustment *a
     return FALSE;
 }
 
+static HtmlBox*
+html_view_move_cursor_by_line (HtmlView *view, gint count, gint *pos)
+{
+	HtmlBoxText *text;
+	HtmlBox *box;
+	HtmlBox *last_box;
+	gint x, y, height;
+	gint old_x, old_y, old_height;
+	GtkAdjustment *adj;
+	gint tmp_pos;
+
+	tmp_pos = *pos;
+	text = html_view_get_box_text_for_offset (view, &tmp_pos, TRUE);
+	box = HTML_BOX (text);
+	old_x = html_box_get_absolute_x (box);
+	old_y = html_box_get_absolute_y (box);
+	y = old_y;
+	old_height = box->height;
+	while (count > 0) {
+		text = find_next_box_text (box);
+		if (!text)
+			return NULL;
+		box = HTML_BOX (text);
+		x = html_box_get_absolute_x (box);
+		y = html_box_get_absolute_y (box);
+		if (old_y + old_height <= y) {
+			count--;
+		}
+	}
+	while (count < 0) {
+		text = find_previous_box_text (box);
+		if (!text)
+			return NULL;
+		box = HTML_BOX (text);
+		x = html_box_get_absolute_x (box);
+		y = html_box_get_absolute_y (box);
+		height = box->height;
+		if (y + height <= old_y) {
+			count++;
+		}
+	}
+	html_view_get_offset_for_box_text (view, text, pos);
+	adj = GTK_LAYOUT (view)->vadjustment;
+	if (!(adj->value && adj->value + adj->page_size > y)) {
+		set_adjustment_clamped (adj, y - adj->page_size + box->height);
+	}
+	return box;
+}
+
+static HtmlBox*
+get_end_text_offset (HtmlView *view, gint *pos)
+{
+	HtmlBox *box;
+	HtmlBoxText *text;
+	HtmlBoxText *temp;
+	gint offset;
+	gint temp_offset;
+	gchar *char_text;
+	gint len;
+
+	box = find_last_child (view->root);
+	if (box) {
+		text = NULL;
+		if (HTML_IS_BOX_TEXT (box)) {
+			text = HTML_BOX_TEXT (box);
+			if (html_box_text_get_len (text) == 0) {
+				text = NULL;
+			}
+		} 
+		if (text == NULL) {
+			text = find_previous_box_text (box);
+		}
+		if (text) {
+			char_text = html_box_text_get_text (text, &len);
+			len = g_utf8_strlen (char_text, len);
+			html_view_get_offset_for_box_text (view, text, &offset);
+			temp_offset = offset;
+			temp = html_view_get_box_text_for_offset (view, &temp_offset, TRUE);
+			offset += len;
+			*pos = offset;
+			return HTML_BOX (text);
+		}
+	}
+	return NULL;
+}
+
+static HtmlBox*
+html_view_move_cursor_to_end (HtmlView *view, gint *pos)
+{
+	HtmlBox *box;
+	GtkAdjustment *adj;
+
+	box = get_end_text_offset (view, pos);
+	adj = GTK_LAYOUT (view)->vadjustment;
+	if (adj->value < adj->upper - adj->page_size) {
+		set_adjustment_clamped (adj, adj->upper - adj->page_size);
+	}
+	return box;
+}
+
+static HtmlBox*
+html_view_move_cursor_to_start (HtmlView *view, gint *pos)
+{
+	HtmlBoxText *text;
+	GtkAdjustment *adj;
+
+	*pos = 0;
+	text = html_view_get_box_text_for_offset (view, pos, TRUE);
+	adj = GTK_LAYOUT (view)->vadjustment;
+	if (adj->value) {
+		set_adjustment_clamped (adj, 0);
+	}
+	return HTML_BOX (text);
+}
+
+static HtmlBox*
+html_view_move_cursor_by_page (HtmlView *view, gint count, gint *pos)
+{
+	HtmlBox *box;
+	HtmlBoxText *text;
+	GtkAdjustment *adj;
+	gint x, y, height;
+	gdouble new_value;
+	gdouble old_value;
+
+	adj = GTK_LAYOUT (view)->vadjustment;
+	if (count > 0 && adj->value >= (adj->upper - adj->page_size - 1e-12)) {
+		/* already at bottom, make sure we are at the end */
+		box = get_end_text_offset (view, pos);
+		return box;
+	} else if (count < 0 && adj->value <= (adj->lower + 1e-12)) {
+		/* already at top, make sure we are at ofset 0 */
+		*pos = 0;
+		text = html_view_get_box_text_for_offset (view, pos, TRUE);
+		return HTML_BOX (text);
+	}
+	new_value = adj->value;
+	old_value = adj->value;
+
+	new_value += count * adj->page_increment;
+	set_adjustment_clamped (adj, new_value);
+	
+	text = html_view_get_box_text_for_offset (view, pos, TRUE);
+	box = HTML_BOX (text);
+	y = html_box_get_absolute_y (box);
+	while (count > 0) {
+		text = find_next_box_text (box);
+		if (!text) {
+			return NULL;
+		}
+		box = HTML_BOX (text);
+		y = html_box_get_absolute_y (box);
+		if (y >= adj->value) {
+			count = 0;
+		}
+	}
+	while (count < 0) {
+		HtmlBox *previous_box;
+
+		previous_box = box;
+		text = find_previous_box_text (box);
+		if (!text) {
+			return NULL;
+		}
+		box = HTML_BOX (text);
+		y = html_box_get_absolute_y (box);
+		if (y < adj->value) {
+			count = 0;
+			box = previous_box;
+		}
+	}
+	html_view_get_offset_for_box_text (view, box, pos);
+	return box;
+}
+
 void
 html_view_scroll_to_node (HtmlView *view, DomNode *node, HtmlViewScrollToType type)
 {
@@ -1811,6 +2088,7 @@ static void
 html_view_real_move_cursor (HtmlView *html_view, GtkMovementStep step, gint count, gboolean extend_selection)
 {
 	GtkAdjustment *vertical, *horizontal;
+	HtmlBox *new_cursor_box;
 	gint new_pos;
 	gint selection_bound;
 	gint cursor_position;
@@ -1847,6 +2125,7 @@ html_view_real_move_cursor (HtmlView *ht
 	cursor_position = html_view_get_cursor_position (html_view);
 	selection_bound = html_view_get_selection_bound (html_view);
 	new_pos = cursor_position;
+	new_cursor_box = NULL;
 	if (cursor_position != selection_bound && !extend_selection) {
 	/*
 	 * If we have a current selection and are not extending it move to the
@@ -1891,13 +2170,17 @@ html_view_real_move_cursor (HtmlView *ht
 			}
 			break;
 		case GTK_MOVEMENT_DISPLAY_LINES:
-			g_warning ("Move by lines not implemented yet\n");
+			new_cursor_box = html_view_move_cursor_by_line (html_view, count, &new_pos);	
 			break;
 		case GTK_MOVEMENT_PAGES:
-			g_warning ("Move by pages not implemented yet\n");
+			new_cursor_box = html_view_move_cursor_by_page (html_view, count, &new_pos);	
 			break;
 		case GTK_MOVEMENT_BUFFER_ENDS:
-			g_warning ("Move to buffer end not implemented yet\n");
+			if (count < 0) {
+				new_cursor_box = html_view_move_cursor_to_start (html_view, &new_pos);
+			} else if (count > 0) {
+				new_cursor_box = html_view_move_cursor_to_end (html_view, &new_pos);
+			}
 			break;
 		default:
 			g_warning ("unknown step!\n");
@@ -1937,17 +2220,19 @@ html_view_real_move_cursor (HtmlView *ht
 		html_view->sel_end_index = end_pos;
 		html_view->sel_backwards = !forward;	
 	} else {
-		HtmlBox *box;
-		HtmlBoxText *text;
-
 		html_view_set_cursor_position (html_view, new_pos);
 		html_view_set_selection_bound (html_view, new_pos);
-		text = html_view_get_box_text_for_offset (html_view, &new_pos, TRUE);
-		box = HTML_BOX (text);
-		if (DOM_IS_ELEMENT (box->parent->dom_node)) {
+
+		if (new_cursor_box == NULL) {
+			HtmlBoxText *text;
+
+			text = html_view_get_box_text_for_offset (html_view, &new_pos, TRUE);
+			new_cursor_box = HTML_BOX (text);
+		}
+		if (DOM_IS_ELEMENT (new_cursor_box->parent->dom_node)) {
 			DomElement *element;
 
-			element = DOM_ELEMENT (box->parent->dom_node);
+			element = DOM_ELEMENT (new_cursor_box->parent->dom_node);
 			if (dom_element_is_focusable (element) &&
 			    element != html_view->document->focus_element) {
                 		html_document_update_focus_element (html_view->document, element);
