summaryrefslogtreecommitdiff
path: root/desktop/textinput.c
diff options
context:
space:
mode:
authorAdrian Lees <adrian@aemulor.com>2006-02-11 18:33:05 +0000
committerAdrian Lees <adrian@aemulor.com>2006-02-11 18:33:05 +0000
commitedded10c0fab01984b43de8d857cce7b6027b242 (patch)
tree159e9ee70fa8fe88e72500f57fe5481fe4ec08a1 /desktop/textinput.c
parent6b6841f07e7f4afea7fc025214abedbc79d3aa03 (diff)
downloadnetsurf-edded10c0fab01984b43de8d857cce7b6027b242.tar.gz
netsurf-edded10c0fab01984b43de8d857cce7b6027b242.tar.bz2
[project @ 2006-02-11 18:33:05 by adrianl]
Textarea/selection improvements and fixes svn path=/import/netsurf/; revision=2072
Diffstat (limited to 'desktop/textinput.c')
-rw-r--r--desktop/textinput.c282
1 files changed, 211 insertions, 71 deletions
diff --git a/desktop/textinput.c b/desktop/textinput.c
index 9b2863351..a941cc86b 100644
--- a/desktop/textinput.c
+++ b/desktop/textinput.c
@@ -31,6 +31,12 @@
#include "netsurf/utils/utf8.h"
#include "netsurf/utils/utils.h"
+
+/** ghost caret used to indicate the insertion point when dragging text
+ into a textarea/input field */
+struct caret ghost_caret;
+
+
static void browser_window_textarea_callback(struct browser_window *bw,
wchar_t key, void *p);
static void browser_window_input_callback(struct browser_window *bw,
@@ -49,11 +55,12 @@ static void input_update_display(struct browser_window *bw, struct box *input,
bool redraw);
static bool textbox_insert(struct browser_window *bw, struct box *text_box,
unsigned char_offset, const char *utf8, unsigned utf8_len);
-static bool textbox_delete(struct box *text_box, unsigned char_offset,
- unsigned utf8_len);
+static bool textbox_delete(struct browser_window *bw, struct box *text_box,
+ unsigned char_offset, unsigned utf8_len);
static struct box *textarea_insert_break(struct browser_window *bw,
struct box *text_box, size_t char_offset);
-static bool delete_handler(struct box *b, int offset, size_t length);
+static bool delete_handler(struct browser_window *bw, struct box *b,
+ int offset, size_t length);
static struct box *line_start(struct box *text_box);
static struct box *line_end(struct box *text_box);
static struct box *line_above(struct box *text_box);
@@ -68,21 +75,75 @@ static bool word_right(const char *text, int len, int *poffset, int *pchars);
/**
- * Handle clicks in a text area by placing the caret.
+ * Remove the given text caret from the window by invalidating it
+ * and causing its former position to be redrawn.
*
- * \param bw browser window where click occurred
- * \param mouse state of mouse buttons and modifier keys
- * \param textarea textarea box
- * \param box_x position of textarea in global document coordinates
- * \param box_y position of textarea in global document coordinates
- * \param x coordinate of click relative to textarea
- * \param y coordinate of click relative to textarea
+ * \param c structure describing text caret
*/
-void browser_window_textarea_click(struct browser_window *bw,
- browser_mouse_state mouse,
- struct box *textarea,
- int box_x, int box_y,
- int x, int y)
+
+void caret_remove(struct caret *c)
+{
+ if (c->defined) {
+ int w = (c->height + 7) / 8;
+ int xc = c->x;
+ c->defined = false;
+ browser_window_redraw_rect(c->bw, xc - w, c->y, 2 * w, c->height);
+ }
+}
+
+
+/**
+ * Set the given text caret's position within the window (text box
+ * and byte/pixel offsets within the UTF-8 content of that text box)
+ * and draw it.
+ *
+ * \param c structure describing text caret
+ * \param bw browser window containing caret
+ * \param box INLINE box containing caret
+ * \param char_offset byte offset within UTF-8 representation
+ * \param pixel_offset from left side of box
+ */
+
+void caret_set_position(struct caret *c, struct browser_window *bw,
+ struct box *text_box, int char_offset, int pixel_offset)
+{
+ struct rect r;
+ int xc;
+ int w;
+
+ box_bounds(text_box, &r);
+
+ c->bw = bw;
+ c->text_box = text_box;
+ c->char_offset = char_offset;
+
+ c->x = xc = r.x0 + pixel_offset;
+ c->y = r.y0;
+ c->height = r.y1 - r.y0;
+ w = (c->height + 7) / 8;
+
+ c->defined = true;
+
+ browser_window_redraw_rect(c->bw, xc - w, c->y, w * 2, c->height);
+}
+
+
+/**
+ * Given the x,y co-ordinates of a point within a textarea, return the
+ * INLINE box pointer, and the character and pixel offsets within that
+ * box at which the caret should be positioned. (eg. for mouse clicks,
+ * drag-and-drop insertions etc)
+ *
+ * \param textarea the textarea being considered
+ * \param x x ordinate of point
+ * \param y y ordinate of point
+ * \param pchar_offset receives the char offset within the INLINE box
+ * \param ppixel_offset receives the pixel offset within the INLINE box
+ * \return pointer to INLINE box
+ */
+
+struct box *textarea_get_position(struct box *textarea, int x, int y,
+ int *pchar_offset, int *ppixel_offset)
{
/* A textarea is an INLINE_BLOCK containing a single
* INLINE_CONTAINER, which contains the text as runs of INLINE
@@ -91,7 +152,6 @@ void browser_window_textarea_click(struct browser_window *bw,
* constraints are satisfied by using a 0-length INLINE for blank
* lines. */
- int char_offset = 0, pixel_offset = 0, new_scroll_y;
struct box *inline_container, *text_box;
inline_container = textarea->children;
@@ -104,8 +164,8 @@ void browser_window_textarea_click(struct browser_window *bw,
/** \todo handle errors */
nsfont_position_in_string(text_box->style, text_box->text,
text_box->length,
- textarea->width,
- &char_offset, &pixel_offset);
+ (unsigned int)(x - text_box->x),
+ pchar_offset, ppixel_offset);
} else {
/* find the relevant text box */
y -= inline_container->y;
@@ -128,7 +188,7 @@ void browser_window_textarea_click(struct browser_window *bw,
text_box->text,
text_box->length,
textarea->width,
- &char_offset, &pixel_offset);
+ pchar_offset, ppixel_offset);
} else {
/* in a text box */
if (text_box->type == BOX_BR)
@@ -147,10 +207,46 @@ void browser_window_textarea_click(struct browser_window *bw,
text_box->text,
text_box->length,
(unsigned int)(x - text_box->x),
- &char_offset, &pixel_offset);
+ pchar_offset, ppixel_offset);
}
}
+ assert(text_box);
+ return text_box;
+}
+
+
+/**
+ * Handle clicks in a text area by placing the caret.
+ *
+ * \param bw browser window where click occurred
+ * \param mouse state of mouse buttons and modifier keys
+ * \param textarea textarea box
+ * \param box_x position of textarea in global document coordinates
+ * \param box_y position of textarea in global document coordinates
+ * \param x coordinate of click relative to textarea
+ * \param y coordinate of click relative to textarea
+ */
+void browser_window_textarea_click(struct browser_window *bw,
+ browser_mouse_state mouse,
+ struct box *textarea,
+ int box_x, int box_y,
+ int x, int y)
+{
+ /* A textarea is an INLINE_BLOCK containing a single
+ * INLINE_CONTAINER, which contains the text as runs of INLINE
+ * separated by BR. There is at least one INLINE. The first and
+ * last boxes are INLINE. Consecutive BR may not be present. These
+ * constraints are satisfied by using a 0-length INLINE for blank
+ * lines. */
+
+ int char_offset = 0, pixel_offset = 0, new_scroll_y;
+ struct box *inline_container = textarea->children;
+ struct box *text_box;
+
+ text_box = textarea_get_position(textarea, x, y,
+ &char_offset, &pixel_offset);
+
/* scroll to place the caret in the centre of the visible region */
new_scroll_y = inline_container->y + text_box->y +
text_box->height / 2 -
@@ -258,11 +354,24 @@ void browser_window_textarea_callback(struct browser_window *bw,
int prev_offset = char_offset;
char_offset = utf8_prev(text_box->text, char_offset);
- textbox_delete(text_box, char_offset, prev_offset - char_offset);
+ textbox_delete(bw, text_box, char_offset,
+ prev_offset - char_offset);
}
reflow = true;
break;
+ case KEY_DELETE_LINE_END: {
+ struct box *end_box = line_end(text_box);
+ if (end_box != text_box ||
+ char_offset < text_box->length + text_box->space) {
+ /* there's something at the end of the line to delete */
+ textarea_cut(bw, text_box, char_offset,
+ end_box, end_box->length + end_box->space);
+ reflow = true;
+ break;
+ }
+ }
+ /* no break */
case KEY_DELETE_RIGHT: /* delete to right */
if (char_offset >= text_box->length) {
/* at the end of a text box */
@@ -296,7 +405,8 @@ void browser_window_textarea_callback(struct browser_window *bw,
/* delete a character */
int next_offset = utf8_next(text_box->text, text_box->length,
char_offset);
- textbox_delete(text_box, char_offset, next_offset - char_offset);
+ textbox_delete(bw, text_box, char_offset,
+ next_offset - char_offset);
}
reflow = true;
break;
@@ -505,16 +615,12 @@ void browser_window_textarea_callback(struct browser_window *bw,
}
break;
- case KEY_DELETE_LINE_START:
- textarea_cut(bw, line_start(text_box), 0, text_box, char_offset);
+ case KEY_DELETE_LINE_START: {
+ struct box *start_box = line_start(text_box);
+ textarea_cut(bw, start_box, 0, text_box, char_offset);
+ text_box = start_box;
char_offset = 0;
reflow = true;
- break;
-
- case KEY_DELETE_LINE_END: {
- struct box *end_box = line_end(text_box);
- textarea_cut(bw, text_box, char_offset, end_box, end_box->length);
- reflow = true;
}
break;
@@ -522,11 +628,11 @@ void browser_window_textarea_callback(struct browser_window *bw,
return;
}
- /* box_dump(textarea, 0); */
- /* for (struct box *t = inline_container->children; t; t = t->next) {
+ /*
+ box_dump(textarea, 0);
+ for (struct box *t = inline_container->children; t; t = t->next) {
assert(t->type == BOX_TEXT);
assert(t->text);
- assert(t->font);
assert(t->parent == inline_container);
if (t->next) assert(t->next->prev == t);
if (t->prev) assert(t->prev->next == t);
@@ -543,13 +649,17 @@ void browser_window_textarea_callback(struct browser_window *bw,
if (reflow)
textarea_reflow(bw, textarea, inline_container);
- if (text_box->length < char_offset) {
- /* the text box has been split and the caret is in the
- * second part */
- char_offset -= (text_box->length + 1); /* +1 for the space */
- text_box = text_box->next;
- assert(text_box);
- assert(char_offset <= text_box->length);
+ if (text_box->length + text_box->space <= char_offset) {
+ if (text_box->next && text_box->next->type == BOX_TEXT) {
+ /* the text box has been split when reflowing and
+ the caret is in the second part */
+ char_offset -= (text_box->length + text_box->space);
+ text_box = text_box->next;
+ assert(text_box);
+ assert(char_offset <= text_box->length);
+ }
+ else
+ char_offset = text_box->length + text_box->space;
}
nsfont_width(text_box->style, text_box->text,
@@ -755,7 +865,7 @@ void browser_window_input_callback(struct browser_window *bw,
/* Go to the previous valid UTF-8 character */
box_offset = utf8_prev(text_box->text, box_offset);
- textbox_delete(text_box, box_offset,
+ textbox_delete(bw, text_box, box_offset,
prev_offset - box_offset);
changed = true;
}
@@ -784,7 +894,7 @@ void browser_window_input_callback(struct browser_window *bw,
next_offset = utf8_next(text_box->text, text_box->length,
box_offset);
- textbox_delete(text_box, box_offset,
+ textbox_delete(bw, text_box, box_offset,
next_offset - box_offset);
changed = true;
}
@@ -915,11 +1025,10 @@ void browser_window_input_callback(struct browser_window *bw,
break;
case KEY_DELETE_LINE_START:
-
if (box_offset <= 0) return;
/* Text box */
- textbox_delete(text_box, 0, box_offset);
+ textbox_delete(bw, text_box, 0, box_offset);
box_offset = 0;
/* Gadget */
@@ -932,12 +1041,11 @@ void browser_window_input_callback(struct browser_window *bw,
break;
case KEY_DELETE_LINE_END:
-
if (box_offset >= text_box->length)
return;
/* Text box */
- textbox_delete(text_box, box_offset, text_box->length - box_offset);
+ textbox_delete(bw, text_box, box_offset, text_box->length - box_offset);
/* Gadget */
input->gadget->length = form_offset;
input->gadget->value[form_offset] = 0;
@@ -1326,7 +1434,14 @@ void input_update_display(struct browser_window *bw, struct box *input,
bool textbox_insert(struct browser_window *bw, struct box *text_box,
unsigned char_offset, const char *utf8, unsigned utf8_len)
{
- char *text = talloc_realloc(bw->current_content, text_box->text,
+ char *text;
+
+ /* code does not support appending after the optional trailing space
+ (this would require inserting a real space and determining whether
+ the resultant string ends in a space) */
+ assert(char_offset <= text_box->length);
+
+ text = talloc_realloc(bw->current_content, text_box->text,
char, text_box->length + utf8_len + 1);
if (!text) {
warn_user("NoMemory", 0);
@@ -1342,6 +1457,9 @@ bool textbox_insert(struct browser_window *bw, struct box *text_box,
/* nothing should assume that the text is terminated, but just in case */
text_box->text[text_box->length] = 0;
+ selection_update(bw->sel, text_box->byte_offset + char_offset,
+ utf8_len, false);
+
text_box->width = UNKNOWN_WIDTH;
return true;
@@ -1351,23 +1469,44 @@ bool textbox_insert(struct browser_window *bw, struct box *text_box,
/**
* Delete a number of chars from a text box
*
+ * \param bw browser window
* \param text_box text box
* \param char_offset offset within text box (bytes) of first char to delete
* \param utf8_len length (bytes) of chars to be deleted
*/
-bool textbox_delete(struct box *text_box, unsigned char_offset, unsigned utf8_len)
+bool textbox_delete(struct browser_window *bw, struct box *text_box,
+ unsigned char_offset, unsigned utf8_len)
{
- unsigned prev_offset = char_offset + utf8_len;
- if (prev_offset <= text_box->length) {
- memmove(text_box->text + char_offset,
- text_box->text + prev_offset,
- text_box->length - prev_offset);
- text_box->length -= (prev_offset - char_offset);
+ unsigned next_offset = char_offset + utf8_len;
+ if (next_offset <= text_box->length + text_box->space) {
+ /* handle removal of trailing space */
+ if (text_box->space && next_offset > text_box->length) {
+ if (char_offset > 0) {
+ /* is the trailing character still a space? */
+ int tmp = utf8_prev(text_box->text, char_offset);
+ if (isspace(text_box->text[tmp]))
+ char_offset = tmp;
+ else
+ text_box->space = false;
+ }
+ else
+ text_box->space = false;
+ text_box->length = char_offset;
+ }
+ else {
+ memmove(text_box->text + char_offset,
+ text_box->text + next_offset,
+ text_box->length - next_offset);
+ text_box->length -= utf8_len;
+ }
/* nothing should assume that the text is terminated, but just in case */
text_box->text[text_box->length] = 0;
+ selection_update(bw->sel, text_box->byte_offset + char_offset,
+ -(int)utf8_len, false);
+
text_box->width = UNKNOWN_WIDTH;
return true;
}
@@ -1378,21 +1517,27 @@ bool textbox_delete(struct box *text_box, unsigned char_offset, unsigned utf8_le
/**
* Delete some text from a box, or delete the box in its entirety
*
+ * \param bw browser window
* \param b box
* \param offset start offset of text to be deleted (in bytes)
* \param length length of text to be deleted
* \return true iff successful
*/
-bool delete_handler(struct box *b, int offset, size_t length)
+bool delete_handler(struct browser_window *bw, struct box *b,
+ int offset, size_t length)
{
- if (offset <= 0 && length >= b->length) {
+ if (offset <= 0 && length >= b->length + b->space) {
+ selection_update(bw->sel, b->byte_offset,
+ -(b->length + b->space), false);
+
/* remove the entire box */
box_unlink_and_free(b);
+
return true;
}
else {
- return textbox_delete(b, offset, length);
+ return textbox_delete(bw, b, offset, length);
}
}
@@ -1521,17 +1666,11 @@ bool textarea_cut(struct browser_window *bw,
{
struct box *box = start_box;
bool success = true;
- bool del = true;
+ bool del = false; /* caller expects start_box to persist */
if (!gui_empty_clipboard())
return false;
- if (!start_idx && (!start_box->prev || start_box->prev->type == BOX_BR)) {
- /* deletion would leave two adjacent BRs, so just collapse
- the start box to an empty TEXT rather than deleting it */
- del = false;
- }
-
while (box && box != end_box) {
/* read before deletion, in case the whole box goes */
struct box *next = box->next;
@@ -1552,14 +1691,15 @@ bool textarea_cut(struct browser_window *bw,
}
if (del) {
- if (!delete_handler(box, start_idx,
- box->length - start_idx)) {
+ if (!delete_handler(bw, box, start_idx,
+ (box->length + box->space) - start_idx)) {
gui_commit_clipboard();
return false;
}
}
else
- textbox_delete(box, start_idx, box->length - start_idx);
+ textbox_delete(bw, box, start_idx,
+ (box->length + box->space) - start_idx);
}
del = true;
@@ -1569,13 +1709,13 @@ bool textarea_cut(struct browser_window *bw,
/* and the last box */
if (box) {
- if (gui_add_to_clipboard(box->text + start_idx, end_idx, box->space)) {
+ if (gui_add_to_clipboard(box->text + start_idx, end_idx - start_idx, box->space)) {
if (del) {
- if (!delete_handler(box, start_idx, end_idx - start_idx))
+ if (!delete_handler(bw, box, start_idx, end_idx - start_idx))
success = false;
}
else
- textbox_delete(box, start_idx, end_idx - start_idx);
+ textbox_delete(bw, box, start_idx, end_idx - start_idx);
}
else
success = false;