summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2005-04-15 18:00:21 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2005-04-15 18:00:21 +0000
commitee9a4712cddef9c318977b16b76b8c525d0f8908 (patch)
tree2dfc05c332ba06dce83f9ea787d2929ebb1982c6
parent34b92e905fa29ba7e7c279a72f3b0fb9bc626a4a (diff)
downloadnetsurf-ee9a4712cddef9c318977b16b76b8c525d0f8908.tar.gz
netsurf-ee9a4712cddef9c318977b16b76b8c525d0f8908.tar.bz2
[project @ 2005-04-15 18:00:19 by jmb]
Split out generic text input code. Support internationalised text input. Fix textarea-related bugs. svn path=/import/netsurf/; revision=1642
-rw-r--r--desktop/browser.c820
-rw-r--r--desktop/browser.h4
-rw-r--r--desktop/textinput.c949
-rw-r--r--desktop/textinput.h27
-rw-r--r--makefile4
-rw-r--r--riscos/ucstables.c333
-rw-r--r--riscos/ucstables.h13
-rw-r--r--riscos/window.c150
8 files changed, 1476 insertions, 824 deletions
diff --git a/desktop/browser.c b/desktop/browser.c
index ae912c194..32eb65dc2 100644
--- a/desktop/browser.c
+++ b/desktop/browser.c
@@ -32,8 +32,8 @@
#include "netsurf/desktop/imagemap.h"
#include "netsurf/desktop/options.h"
#include "netsurf/desktop/selection.h"
+#include "netsurf/desktop/textinput.h"
#include "netsurf/render/box.h"
-#include "netsurf/render/font.h"
#include "netsurf/render/form.h"
#include "netsurf/render/layout.h"
#include "netsurf/utils/log.h"
@@ -66,29 +66,8 @@ static const char *browser_window_scrollbar_click(struct browser_window *bw,
int box_x, int box_y, int x, int y);
static void browser_radio_set(struct content *content,
struct form_control *radio);
-static void browser_redraw_box(struct content *c, struct box *box);
-static 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);
-static void browser_window_textarea_callback(struct browser_window *bw,
- wchar_t key, void *p);
-static void browser_window_input_click(struct browser_window* bw,
- struct box *input,
- int box_x, int box_y,
- int x, int y);
-static void browser_window_input_callback(struct browser_window *bw,
- wchar_t key, void *p);
-static void browser_window_place_caret(struct browser_window *bw,
- int x, int y, int height,
- void (*callback)(struct browser_window *bw,
- wchar_t key, void *p),
- void *p);
-static void browser_window_remove_caret(struct browser_window *bw);
static gui_pointer_shape get_pointer_shape(css_cursor cursor);
-static void browser_form_submit(struct browser_window *bw, struct form *form,
- struct form_control *submit_button);
+
static struct box *browser_window_pick_text_box(struct browser_window *bw,
browser_mouse_state mouse, int x, int y, int *dx, int *dy);
static void browser_window_page_drag_start(struct browser_window *bw, int x, int y);
@@ -195,6 +174,7 @@ void browser_window_go_post(struct browser_window *bw, const char *url,
return;
}
+ /* find any fragment identifier on end of URL */
hash = strchr(url2, '#');
if (bw->frag_id) {
free(bw->frag_id);
@@ -205,7 +185,7 @@ void browser_window_go_post(struct browser_window *bw, const char *url,
/* if we're simply moving to another ID on the same page,
* don't bother to fetch, just update the window
*/
- if (bw->current_content &&
+ if (bw->current_content && bw->current_content->url &&
strncasecmp(bw->current_content->url,
url2, hash - url2) == 0 &&
strlen(bw->current_content->url) ==
@@ -1333,20 +1313,20 @@ void browser_window_redraw_rect(struct browser_window *bw, int x, int y, int wid
if (c && c->type == CONTENT_HTML) {
union content_msg_data data;
-
+
data.redraw.x = x;
data.redraw.y = y;
data.redraw.width = width;
data.redraw.height = height;
-
+
data.redraw.full_redraw = true;
-
+
data.redraw.object = c;
data.redraw.object_x = 0;
data.redraw.object_y = 0;
data.redraw.object_width = c->width;
data.redraw.object_height = c->height;
-
+
content_broadcast(c, CONTENT_MSG_REDRAW, data);
}
}
@@ -1386,788 +1366,6 @@ void browser_redraw_box(struct content *c, struct box *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, *text_box;
-
- inline_container = textarea->children;
-
- if (inline_container->y + inline_container->height < y) {
- /* below the bottom of the textarea: place caret at end */
- text_box = inline_container->last;
- assert(text_box->type == BOX_INLINE);
- assert(text_box->text);
- /** \todo handle errors */
- nsfont_position_in_string(text_box->style, text_box->text,
- text_box->length,
- textarea->width,
- &char_offset, &pixel_offset);
- } else {
- /* find the relevant text box */
- y -= inline_container->y;
- for (text_box = inline_container->children;
- text_box && text_box->y + text_box->height < y;
- text_box = text_box->next)
- ;
- for (; text_box && text_box->type != BOX_BR &&
- text_box->y <= y &&
- text_box->x + text_box->width < x;
- text_box = text_box->next)
- ;
- if (!text_box) {
- /* past last text box */
- text_box = inline_container->last;
- assert(text_box->type == BOX_INLINE);
- assert(text_box->text);
- nsfont_position_in_string(text_box->style,
- text_box->text,
- text_box->length,
- textarea->width,
- &char_offset, &pixel_offset);
- } else {
- /* in a text box */
- if (text_box->type == BOX_BR)
- text_box = text_box->prev;
- else if (y < text_box->y && text_box->prev)
- text_box = text_box->prev;
- assert(text_box->type == BOX_INLINE);
- assert(text_box->text);
- nsfont_position_in_string(text_box->style,
- text_box->text,
- text_box->length,
- (unsigned int)(x - text_box->x),
- &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 -
- textarea->height / 2;
- if (textarea->descendant_y1 - textarea->height < new_scroll_y)
- new_scroll_y = textarea->descendant_y1 - textarea->height;
- if (new_scroll_y < 0)
- new_scroll_y = 0;
- box_y += textarea->scroll_y - new_scroll_y;
-
- textarea->gadget->caret_inline_container = inline_container;
- textarea->gadget->caret_text_box = text_box;
- textarea->gadget->caret_box_offset = char_offset;
- textarea->gadget->caret_pixel_offset = pixel_offset;
- browser_window_place_caret(bw,
- box_x + inline_container->x + text_box->x +
- pixel_offset,
- box_y + inline_container->y + text_box->y,
- text_box->height,
- browser_window_textarea_callback, textarea);
-
- if (new_scroll_y != textarea->scroll_y) {
- textarea->scroll_y = new_scroll_y;
- browser_redraw_box(bw->current_content, textarea);
- }
-}
-
-
-/**
- * Key press callback for text areas.
- */
-
-void browser_window_textarea_callback(struct browser_window *bw,
- wchar_t key, void *p)
-{
- struct box *textarea = p;
- struct box *inline_container = textarea->gadget->caret_inline_container;
- struct box *text_box = textarea->gadget->caret_text_box;
- struct box *new_br, *new_text, *t;
- struct box *prev;
- size_t char_offset = textarea->gadget->caret_box_offset;
- int pixel_offset = textarea->gadget->caret_pixel_offset;
- int new_scroll_y;
- int box_x, box_y;
- char utf8[5];
- unsigned int utf8_len, i;
- char *text;
- int width = 0, height = 0;
- bool reflow = false;
-
- /* box_dump(textarea, 0); */
- LOG(("key %i at %i in '%.*s'", key, char_offset,
- (int) text_box->length, text_box->text));
-
- box_coords(textarea, &box_x, &box_y);
- box_x -= textarea->scroll_x;
- box_y -= textarea->scroll_y;
-
- if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
- /* normal character insertion */
- /** \todo convert key to UTF-8 properly */
- utf8[0] = key;
- utf8_len = 1;
-
- text = talloc_realloc(bw->current_content, text_box->text,
- char, text_box->length + 8);
- if (!text) {
- warn_user("NoMemory", 0);
- return;
- }
- text_box->text = text;
- memmove(text_box->text + char_offset + utf8_len,
- text_box->text + char_offset,
- text_box->length - char_offset);
- for (i = 0; i != utf8_len; i++)
- text_box->text[char_offset + i] = utf8[i];
- text_box->length += utf8_len;
- text_box->text[text_box->length] = 0;
- text_box->width = UNKNOWN_WIDTH;
- char_offset += utf8_len;
-
- reflow = true;
- }
- else switch (key) {
-
- case 8:
- case 127: /* delete to left */
- if (char_offset == 0) {
- /* at the start of a text box */
- if (!text_box->prev)
- /* at very beginning of text area: ignore */
- return;
-
- if (text_box->prev->type == BOX_BR) {
- /* previous box is BR: remove it */
- t = text_box->prev;
- t->prev->next = t->next;
- t->next->prev = t->prev;
- box_free(t);
- }
-
- /* delete space by merging with previous text box */
- prev = text_box->prev;
- assert(prev->text);
- text = talloc_realloc(bw->current_content, prev->text,
- char,
- prev->length + text_box->length + 1);
- if (!text) {
- warn_user("NoMemory", 0);
- return;
- }
- prev->text = text;
- memcpy(prev->text + prev->length, text_box->text,
- text_box->length);
- char_offset = prev->length; /* caret at join */
- prev->length += text_box->length;
- prev->text[prev->length] = 0;
- prev->width = UNKNOWN_WIDTH;
- prev->next = text_box->next;
- if (prev->next)
- prev->next->prev = prev;
- else
- prev->parent->last = prev;
- box_free(text_box);
-
- /* place caret at join (see above) */
- text_box = prev;
-
- } else {
- /* delete a character */
- /** \todo delete entire UTF-8 character */
- utf8_len = 1;
- memmove(text_box->text + char_offset - utf8_len,
- text_box->text + char_offset,
- text_box->length - char_offset);
- text_box->length -= utf8_len;
- text_box->width = UNKNOWN_WIDTH;
- char_offset -= utf8_len;
- }
-
- reflow = true;
- break;
-
- case 10:
- case 13: /* paragraph break */
- text = talloc_array(bw->current_content, char,
- text_box->length + 1);
- if (!text) {
- warn_user("NoMemory", 0);
- return;
- }
-
- new_br = box_create(text_box->style, 0, text_box->title, 0,
- bw->current_content);
- new_text = talloc(bw->current_content, struct box);
- if (!new_text) {
- warn_user("NoMemory", 0);
- return;
- }
-
- new_br->type = BOX_BR;
- box_insert_sibling(text_box, new_br);
-
- memcpy(new_text, text_box, sizeof (struct box));
- new_text->clone = 1;
- new_text->text = text;
- memcpy(new_text->text, text_box->text + char_offset,
- text_box->length - char_offset);
- new_text->length = text_box->length - char_offset;
- text_box->length = char_offset;
- text_box->width = new_text->width = UNKNOWN_WIDTH;
- box_insert_sibling(new_br, new_text);
-
- /* place caret at start of new text box */
- text_box = new_text;
- char_offset = 0;
-
- reflow = true;
- break;
-
- case 22: /* Ctrl+V */
-// gui_paste_from_clipboard();
- break;
-
- case 24: /* Ctrl+X */
- if (gui_copy_to_clipboard(bw->sel)) {
- /* \todo block delete */
- }
- break;
-
- case 28: /* Right cursor -> */
- if ((unsigned int) char_offset != text_box->length) {
- /** \todo move by a UTF-8 character */
- utf8_len = 1;
- char_offset += utf8_len;
- } else {
- if (!text_box->next)
- /* at end of text area: ignore */
- return;
-
- text_box = text_box->next;
- if (text_box->type == BOX_BR)
- text_box = text_box->next;
- char_offset = 0;
- }
- break;
-
- case 29: /* Left cursor <- */
- if (char_offset != 0) {
- /** \todo move by a UTF-8 character */
- utf8_len = 1;
- char_offset -= utf8_len;
- } else {
- if (!text_box->prev)
- /* at start of text area: ignore */
- return;
-
- text_box = text_box->prev;
- if (text_box->type == BOX_BR)
- text_box = text_box->prev;
- char_offset = text_box->length;
- }
- break;
-
- case 30: /* Up Cursor */
- browser_window_textarea_click(bw,
- BROWSER_MOUSE_CLICK_1, textarea,
- box_x, box_y,
- text_box->x + pixel_offset,
- inline_container->y + text_box->y - 1);
- return;
-
- case 31: /* Down cursor */
- browser_window_textarea_click(bw,
- BROWSER_MOUSE_CLICK_1, textarea,
- box_x, box_y,
- text_box->x + pixel_offset,
- inline_container->y + text_box->y +
- text_box->height + 1);
- return;
-
- default:
- return;
- }
-
- /* box_dump(textarea, 0); */
- /* for (struct box *t = inline_container->children; t; t = t->next) {
- assert(t->type == BOX_INLINE);
- 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);
- if (!t->next) {
- assert(inline_container->last == t);
- break;
- }
- if (t->next->type == BOX_BR) {
- assert(t->next->next);
- t = t->next;
- }
- } */
-
- if (reflow) {
- /* reflow textarea preserving width and height */
- width = textarea->width;
- height = textarea->height;
- if (!layout_inline_container(inline_container, width,
- textarea, 0, 0,
- bw->current_content))
- warn_user("NoMemory", 0);
- textarea->width = width;
- textarea->height = height;
- layout_calculate_descendant_bboxes(textarea);
- }
-
- 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);
- }
-
- /* 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 -
- textarea->height / 2;
- if (textarea->descendant_y1 - textarea->height < new_scroll_y)
- new_scroll_y = textarea->descendant_y1 - textarea->height;
- if (new_scroll_y < 0)
- new_scroll_y = 0;
- box_y += textarea->scroll_y - new_scroll_y;
-
- nsfont_width(text_box->style, text_box->text,
- char_offset, &pixel_offset);
-
- textarea->gadget->caret_inline_container = inline_container;
- textarea->gadget->caret_text_box = text_box;
- textarea->gadget->caret_box_offset = char_offset;
- textarea->gadget->caret_pixel_offset = pixel_offset;
- browser_window_place_caret(bw,
- box_x + inline_container->x + text_box->x +
- pixel_offset,
- box_y + inline_container->y + text_box->y,
- text_box->height,
- browser_window_textarea_callback, textarea);
-
- if (new_scroll_y != textarea->scroll_y || reflow) {
- textarea->scroll_y = new_scroll_y;
- browser_redraw_box(bw->current_content, textarea);
- }
-}
-
-
-/**
- * Handle clicks in a text or password input box by placing the caret.
- *
- * \param bw browser window where click occurred
- * \param input input box
- * \param box_x position of input in global document coordinates
- * \param box_y position of input in global document coordinates
- * \param x coordinate of click relative to input
- * \param y coordinate of click relative to input
- */
-
-void browser_window_input_click(struct browser_window* bw,
- struct box *input,
- int box_x, int box_y,
- int x, int y)
-{
- size_t char_offset = 0;
- int pixel_offset = 0, dx = 0;
- struct box *text_box = input->children->children;
- int uchars;
- unsigned int offset;
-
- nsfont_position_in_string(text_box->style, text_box->text,
- text_box->length, x - text_box->x,
- &char_offset, &pixel_offset);
- assert(char_offset <= text_box->length);
-
- text_box->x = 0;
- if ((input->width < text_box->width) &&
- (input->width / 2 < pixel_offset)) {
- dx = text_box->x;
- text_box->x = input->width / 2 - pixel_offset;
- if (text_box->x < input->width - text_box->width)
- text_box->x = input->width - text_box->width;
- dx -= text_box->x;
- }
- input->gadget->caret_box_offset = char_offset;
- /* Update caret_form_offset */
- for (uchars = 0, offset = 0; offset < char_offset; ++uchars) {
- if ((text_box->text[offset] & 0x80) == 0x00) {
- ++offset;
- continue;
- }
- assert((text_box->text[offset] & 0xC0) == 0xC0);
- for (++offset; offset < char_offset && (text_box->text[offset] & 0xC0) == 0x80; ++offset)
- ;
- }
- /* uchars is the number of real Unicode characters at the left
- * side of the caret.
- */
- for (offset = 0; uchars > 0 && offset < input->gadget->length; --uchars) {
- if ((input->gadget->value[offset] & 0x80) == 0x00) {
- ++offset;
- continue;
- }
- assert((input->gadget->value[offset] & 0xC0) == 0xC0);
- for (++offset; offset < input->gadget->length && (input->gadget->value[offset] & 0xC0) == 0x80; ++offset)
- ;
- }
- assert(uchars == 0);
- input->gadget->caret_form_offset = offset;
- input->gadget->caret_pixel_offset = pixel_offset;
- browser_window_place_caret(bw,
- box_x + input->children->x + text_box->x + pixel_offset,
- box_y + input->children->y + text_box->y,
- text_box->height,
- browser_window_input_callback, input);
-
- if (dx)
- browser_redraw_box(bw->current_content, input);
-}
-
-
-/**
- * Key press callback for text or password input boxes.
- */
-
-void browser_window_input_callback(struct browser_window *bw,
- wchar_t key, void *p)
-{
- struct box *input = (struct box *)p;
- struct box *text_box = input->children->children;
- unsigned int box_offset = input->gadget->caret_box_offset;
- unsigned int form_offset = input->gadget->caret_form_offset;
- int pixel_offset, dx;
- int box_x, box_y;
- struct form* form = input->gadget->form;
- bool changed = false;
-
- box_coords(input, &box_x, &box_y);
-
- if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
- char key_to_insert;
- char *utf8key;
- size_t utf8keySize;
- char *value;
-
- /** \todo: text_box has data in UTF-8 and its length in
- * bytes is not necessarily equal to number of characters.
- */
- if (input->gadget->length >= input->gadget->maxlength)
- return;
-
- /* normal character insertion */
-
- /* Insert key in gadget */
- key_to_insert = (char)key;
- if ((utf8key = cnv_local_enc_str(&key_to_insert, 1)) == NULL)
- return;
- utf8keySize = strlen(utf8key);
-
- value = realloc(input->gadget->value,
- input->gadget->length + utf8keySize + 1);
- if (!value) {
- free(utf8key);
- warn_user("NoMemory", 0);
- return;
- }
- input->gadget->value = value;
-
- memmove(input->gadget->value + form_offset + utf8keySize,
- input->gadget->value + form_offset,
- input->gadget->length - form_offset);
- memcpy(input->gadget->value + form_offset, utf8key, utf8keySize);
- input->gadget->length += utf8keySize;
- input->gadget->value[input->gadget->length] = 0;
- form_offset += utf8keySize;
- free(utf8key);
-
- /* Insert key in text box */
- /* Convert space into NBSP */
- key_to_insert = (input->gadget->type == GADGET_PASSWORD) ? '*' : (key == ' ') ? 160 : key;
- if ((utf8key = cnv_local_enc_str(&key_to_insert, 1)) == NULL)
- return;
- utf8keySize = strlen(utf8key);
-
- value = talloc_realloc(bw->current_content, text_box->text,
- char, text_box->length + utf8keySize + 1);
- if (!value) {
- free(utf8key);
- warn_user("NoMemory", 0);
- return;
- }
- text_box->text = value;
-
- memmove(text_box->text + box_offset + utf8keySize,
- text_box->text + box_offset,
- text_box->length - box_offset);
- memcpy(text_box->text + box_offset, utf8key, utf8keySize);
- text_box->length += utf8keySize;
- text_box->text[text_box->length] = 0;
- box_offset += utf8keySize;
- free(utf8key);
-
- nsfont_width(text_box->style, text_box->text, text_box->length,
- &text_box->width);
- changed = true;
-
- } else switch (key) {
-
- case 8:
- case 127: { /* delete to left */
- int prev_offset;
-
- if (box_offset == 0)
- return;
-
- /* Gadget */
- prev_offset = form_offset;
- /* Go to the previous valid UTF-8 character */
- while (form_offset != 0
- && !((input->gadget->value[--form_offset] & 0x80) == 0x00 || (input->gadget->value[form_offset] & 0xC0) == 0xC0))
- ;
- memmove(input->gadget->value + form_offset,
- input->gadget->value + prev_offset,
- input->gadget->length - prev_offset);
- input->gadget->length -= prev_offset - form_offset;
- input->gadget->value[input->gadget->length] = 0;
-
- /* Text box */
- prev_offset = box_offset;
- /* Go to the previous valid UTF-8 character */
- while (box_offset != 0
- && !((text_box->text[--box_offset] & 0x80) == 0x00 || (text_box->text[box_offset] & 0xC0) == 0xC0))
- ;
- memmove(text_box->text + box_offset,
- text_box->text + prev_offset,
- text_box->length - prev_offset);
- text_box->length -= prev_offset - box_offset;
- text_box->text[text_box->length] = 0;
-
- nsfont_width(text_box->style, text_box->text, text_box->length,
- &text_box->width);
-
- changed = true;
- }
- break;
-
- case 9: { /* Tab */
- struct form_control *next_input;
- for (next_input = input->gadget->next;
- next_input &&
- next_input->type != GADGET_TEXTBOX &&
- next_input->type != GADGET_TEXTAREA &&
- next_input->type != GADGET_PASSWORD;
- next_input = next_input->next)
- ;
- if (!next_input)
- return;
-
- input = next_input->box;
- text_box = input->children->children;
- box_coords(input, &box_x, &box_y);
- form_offset = box_offset = 0;
- }
- break;
-
- case 10:
- case 13: /* Return/Enter hit */
- /* Return/Enter hit */
- if (form)
- browser_form_submit(bw, form, 0);
- return;
-
- case 11: { /* Shift+Tab */
- struct form_control *prev_input;
- for (prev_input = input->gadget->prev;
- prev_input &&
- prev_input->type != GADGET_TEXTBOX &&
- prev_input->type != GADGET_TEXTAREA &&
- prev_input->type != GADGET_PASSWORD;
- prev_input = prev_input->prev)
- ;
- if (!prev_input)
- return;
-
- input = prev_input->box;
- text_box = input->children->children;
- box_coords(input, &box_x, &box_y);
- form_offset = box_offset = 0;
- }
- break;
-
- case 128: /* Ctrl+Left */
- box_offset = form_offset = 0;
- break;
-
- case 129: /* Ctrl+Right */
- box_offset = text_box->length;
- form_offset = input->gadget->length;
- break;
-
- case 21: /* Ctrl+U */
- text_box->text[0] = 0;
- text_box->length = 0;
- box_offset = 0;
-
- input->gadget->value[0] = 0;
- input->gadget->length = 0;
- form_offset = 0;
-
- text_box->width = 0;
- changed = true;
- break;
-
- case 22: /* Ctrl+V */
-// gui_paste_from_clipboard();
- break;
-
- case 28: /* Right cursor -> */
- /* Text box */
- /* Go to the next valid UTF-8 character */
- while (box_offset != text_box->length
- && !((text_box->text[++box_offset] & 0x80) == 0x00 || (text_box->text[box_offset] & 0xC0) == 0xC0))
- ;
- /* Gadget */
- /* Go to the next valid UTF-8 character */
- while (form_offset != input->gadget->length
- && !((input->gadget->value[++form_offset] & 0x80) == 0x00 || (input->gadget->value[form_offset] & 0xC0) == 0xC0))
- ;
- break;
-
- case 29: /* Left cursor <- */
- /* Text box */
- /* Go to the previous valid UTF-8 character */
- while (box_offset != 0
- && !((text_box->text[--box_offset] & 0x80) == 0x00 || (text_box->text[box_offset] & 0xC0) == 0xC0))
- ;
- /* Gadget */
- /* Go to the previous valid UTF-8 character */
- while (form_offset != 0
- && !((input->gadget->value[--form_offset] & 0x80) == 0x00 || (input->gadget->value[form_offset] & 0xC0) == 0xC0))
- ;
- break;
-
- default:
- return;
- }
-
- nsfont_width(text_box->style, text_box->text, box_offset,
- &pixel_offset);
- dx = text_box->x;
- text_box->x = 0;
- if (input->width < text_box->width && input->width / 2 < pixel_offset) {
- text_box->x = input->width / 2 - pixel_offset;
- if (text_box->x < input->width - text_box->width)
- text_box->x = input->width - text_box->width;
- }
- dx -= text_box->x;
- input->gadget->caret_pixel_offset = pixel_offset;
-
- input->gadget->caret_box_offset = box_offset;
- input->gadget->caret_form_offset = form_offset;
-
- browser_window_place_caret(bw,
- box_x + input->children->x + text_box->x + pixel_offset,
- box_y + input->children->y + text_box->y,
- text_box->height,
- browser_window_input_callback, input);
-
- if (dx || changed)
- browser_redraw_box(bw->current_content, input);
-}
-
-
-/**
- * Position the caret and assign a callback for key presses.
- */
-
-void browser_window_place_caret(struct browser_window *bw,
- int x, int y, int height,
- void (*callback)(struct browser_window *bw,
- wchar_t key, void *p),
- void *p)
-{
- gui_window_place_caret(bw->window, x, y, height);
- bw->caret_callback = callback;
- bw->caret_p = p;
-}
-
-
-/**
- * Removes the caret and callback for key process.
- */
-
-void browser_window_remove_caret(struct browser_window *bw)
-{
- gui_window_remove_caret(bw->window);
- bw->caret_callback = NULL;
- bw->caret_p = NULL;
-}
-
-
-/**
- * Handle key presses in a browser window.
- */
-
-bool browser_window_key_press(struct browser_window *bw, wchar_t key)
-{
- /* keys that take effect wherever the caret is positioned */
- switch (key) {
- case 1: /* Ctrl+A */
- selection_select_all(bw->sel);
- return true;
-
- case 3: /* Ctrl+C */
- gui_copy_to_clipboard(bw->sel);
- return true;
-
- case 26: /* Ctrl+Z */
- selection_clear(bw->sel, true);
- return true;
-
- case 27:
- if (selection_defined(bw->sel)) {
- selection_clear(bw->sel, true);
- return true;
- }
- break;
- }
-
- /* pass on to the appropriate field */
- if (!bw->caret_callback)
- return false;
- bw->caret_callback(bw, key, bw->caret_p);
- return true;
-}
-
-
-/**
* Process a selection from a form select menu.
*
* \param bw browser window with menu
@@ -2378,7 +1576,7 @@ struct box *browser_window_pick_text_box(struct browser_window *bw,
while ((box = box_at_point(box, x, y, &box_x, &box_y, &content)) !=
NULL) {
-
+
if (box->text && !box->object)
text_box = box;
}
diff --git a/desktop/browser.h b/desktop/browser.h
index 34e6c7c6d..c8764da2c 100644
--- a/desktop/browser.h
+++ b/desktop/browser.h
@@ -19,6 +19,7 @@
struct box;
struct content;
+struct form;
struct form_control;
struct form_successful_control;
struct gui_window;
@@ -126,6 +127,9 @@ void browser_window_mouse_drag_end(struct browser_window *bw,
bool browser_window_key_press(struct browser_window *bw, wchar_t key);
void browser_window_form_select(struct browser_window *bw,
struct form_control *control, int item);
+void browser_redraw_box(struct content *c, struct box *box);
+void browser_form_submit(struct browser_window *bw, struct form *form,
+ struct form_control *submit_button);
void browser_window_redraw_rect(struct browser_window *bw, int x, int y,
int width, int height);
diff --git a/desktop/textinput.c b/desktop/textinput.c
new file mode 100644
index 000000000..afa13f76e
--- /dev/null
+++ b/desktop/textinput.c
@@ -0,0 +1,949 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
+ * Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
+ */
+
+/** \file
+ * Textual input handling (implementation)
+ */
+
+#include <assert.h>
+
+#include "netsurf/desktop/browser.h"
+#include "netsurf/desktop/gui.h"
+#include "netsurf/desktop/selection.h"
+#include "netsurf/desktop/textinput.h"
+#include "netsurf/render/box.h"
+#include "netsurf/render/font.h"
+#include "netsurf/render/form.h"
+#include "netsurf/render/layout.h"
+#define NDEBUG
+#include "netsurf/utils/log.h"
+#include "netsurf/utils/talloc.h"
+#include "netsurf/utils/utils.h"
+
+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,
+ wchar_t key, void *p);
+static void browser_window_place_caret(struct browser_window *bw,
+ int x, int y, int height,
+ void (*callback)(struct browser_window *bw,
+ wchar_t key, void *p),
+ void *p);
+
+/**
+ * Convert a single UCS4 character into a UTF8 multibyte sequence
+ *
+ * Encoding of UCS values outside the UTF16 plane has been removed from
+ * RFC3629. This macro conforms to RFC2279, however, as it is possible
+ * that the platform specific keyboard input handler will generate a UCS4
+ * value outside the UTF16 plane.
+ *
+ * \param c The character to process (0 <= c <= 0x7FFFFFFF)
+ * \param s Pointer to 6 byte long output buffer
+ * \param l Integer in which to store length of multibyte sequence
+ */
+#define ucs4_to_utf8(c, s, l) \
+ do { \
+ if ((c) < 0) \
+ assert(0); \
+ else if ((c) < 0x80) { \
+ *(s) = (char)(c); \
+ (l) = 1; \
+ } \
+ else if ((c) < 0x800) { \
+ *(s) = 0xC0 | (((c) >> 6) & 0x1F); \
+ *((s)+1) = 0x80 | ((c) & 0x3F); \
+ (l) = 2; \
+ } \
+ else if ((c) < 0x10000) { \
+ *(s) = 0xE0 | (((c) >> 12) & 0xF); \
+ *((s)+1) = 0x80 | (((c) >> 6) & 0x3F); \
+ *((s)+2) = 0x80 | ((c) & 0x3F); \
+ (l) = 3; \
+ } \
+ else if ((c) < 0x200000) { \
+ *(s) = 0xF0 | (((c) >> 18) & 0x7); \
+ *((s)+1) = 0x80 | (((c) >> 12) & 0x3F); \
+ *((s)+2) = 0x80 | (((c) >> 6) & 0x3F); \
+ *((s)+3) = 0x80 | ((c) & 0x3F); \
+ (l) = 4; \
+ } \
+ else if ((c) < 0x4000000) { \
+ *(s) = 0xF8 | (((c) >> 24) & 0x3); \
+ *((s)+1) = 0x80 | (((c) >> 18) & 0x3F); \
+ *((s)+2) = 0x80 | (((c) >> 12) & 0x3F); \
+ *((s)+3) = 0x80 | (((c) >> 6) & 0x3F); \
+ *((s)+4) = 0x80 | ((c) & 0x3F); \
+ (l) = 5; \
+ } \
+ else if ((c) <= 0x7FFFFFFF) { \
+ *(s) = 0xFC | (((c) >> 30) & 0x1); \
+ *((s)+1) = 0x80 | (((c) >> 24) & 0x3F); \
+ *((s)+2) = 0x80 | (((c) >> 18) & 0x3F); \
+ *((s)+3) = 0x80 | (((c) >> 12) & 0x3F); \
+ *((s)+4) = 0x80 | (((c) >> 6) & 0x3F); \
+ *((s)+5) = 0x80 | ((c) & 0x3F); \
+ (l) = 6; \
+ } \
+ } while(0)
+
+/**
+ * Calculate the length (in characters) of a NULL-terminated UTF8 string
+ *
+ * \param s The string
+ * \param l Integer in which to store length
+ */
+#define utf8_length(s, l) \
+ do { \
+ char *__s = (s); \
+ (l) = 0; \
+ while (*__s != '\0') { \
+ if ((*__s & 0x80) == 0x00) \
+ __s += 1; \
+ else if ((*__s & 0xE0) == 0xC0) \
+ __s += 2; \
+ else if ((*__s & 0xF0) == 0xE0) \
+ __s += 3; \
+ else if ((*__s & 0xF8) == 0xF0) \
+ __s += 4; \
+ else if ((*__s & 0xFC) == 0xF8) \
+ __s += 5; \
+ else if ((*__s & 0xFE) == 0xFC) \
+ __s += 6; \
+ else \
+ assert(0); \
+ (l)++; \
+ } \
+ } while (0)
+
+/**
+ * Find previous legal UTF8 char in string
+ *
+ * \param s The string
+ * \param o Offset in the string to start at (updated on exit)
+ */
+#define utf8_prev(s, o) \
+ do { \
+ while ((o) != 0 && \
+ !((((s)[--(o)] & 0x80) == 0x00) || \
+ (((s)[(o)] & 0xC0) == 0xC0))) \
+ /* do nothing */; \
+ } while(0)
+
+/**
+ * Find next legal UTF8 char in string
+ *
+ * \param s The string
+ * \param l Maximum offset in string
+ * \param o Offset in the string to start at (updated on exit)
+ */
+#define utf8_next(s, l, o) \
+ do { \
+ while ((o) != (l) && \
+ !((((s)[++(o)] & 0x80) == 0x00) || \
+ (((s)[(o)] & 0xC0) == 0xC0))) \
+ /* do nothing */; \
+ } while(0)
+
+/**
+ * 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, *text_box;
+
+ inline_container = textarea->children;
+
+ if (inline_container->y + inline_container->height < y) {
+ /* below the bottom of the textarea: place caret at end */
+ text_box = inline_container->last;
+ assert(text_box->type == BOX_INLINE);
+ assert(text_box->text);
+ /** \todo handle errors */
+ nsfont_position_in_string(text_box->style, text_box->text,
+ text_box->length,
+ textarea->width,
+ &char_offset, &pixel_offset);
+ } else {
+ /* find the relevant text box */
+ y -= inline_container->y;
+ for (text_box = inline_container->children;
+ text_box &&
+ text_box->y + text_box->height < y;
+ text_box = text_box->next)
+ ;
+ for (; text_box && text_box->type != BOX_BR &&
+ text_box->y <= y &&
+ text_box->x + text_box->width < x;
+ text_box = text_box->next)
+ ;
+ if (!text_box) {
+ /* past last text box */
+ text_box = inline_container->last;
+ assert(text_box->type == BOX_INLINE);
+ assert(text_box->text);
+ nsfont_position_in_string(text_box->style,
+ text_box->text,
+ text_box->length,
+ textarea->width,
+ &char_offset, &pixel_offset);
+ } else {
+ /* in a text box */
+ if (text_box->type == BOX_BR)
+ text_box = text_box->prev;
+ else if (y < text_box->y && text_box->prev) {
+ if (text_box->prev->type == BOX_BR) {
+ assert(text_box->prev->prev);
+ text_box = text_box->prev->prev;
+ }
+ else
+ text_box = text_box->prev;
+ }
+ assert(text_box->type == BOX_INLINE);
+ assert(text_box->text);
+ nsfont_position_in_string(text_box->style,
+ text_box->text,
+ text_box->length,
+ (unsigned int)(x - text_box->x),
+ &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 -
+ textarea->height / 2;
+ if (textarea->descendant_y1 - textarea->height < new_scroll_y)
+ new_scroll_y = textarea->descendant_y1 - textarea->height;
+ if (new_scroll_y < 0)
+ new_scroll_y = 0;
+ box_y += textarea->scroll_y - new_scroll_y;
+
+ textarea->gadget->caret_inline_container = inline_container;
+ textarea->gadget->caret_text_box = text_box;
+ textarea->gadget->caret_box_offset = char_offset;
+ textarea->gadget->caret_pixel_offset = pixel_offset;
+ browser_window_place_caret(bw,
+ box_x + inline_container->x + text_box->x +
+ pixel_offset,
+ box_y + inline_container->y + text_box->y,
+ text_box->height,
+ browser_window_textarea_callback, textarea);
+
+ if (new_scroll_y != textarea->scroll_y) {
+ textarea->scroll_y = new_scroll_y;
+ browser_redraw_box(bw->current_content, textarea);
+ }
+}
+
+
+/**
+ * Key press callback for text areas.
+ *
+ * \param bw The browser window containing the text area
+ * \param key The ucs4 character codepoint
+ * \param p The text area box
+ */
+void browser_window_textarea_callback(struct browser_window *bw,
+ wchar_t key, void *p)
+{
+ struct box *textarea = p;
+ struct box *inline_container =
+ textarea->gadget->caret_inline_container;
+ struct box *text_box = textarea->gadget->caret_text_box;
+ struct box *new_br, *new_text, *t;
+ struct box *prev;
+ size_t char_offset = textarea->gadget->caret_box_offset;
+ int pixel_offset = textarea->gadget->caret_pixel_offset;
+ int new_scroll_y;
+ int box_x, box_y;
+ char utf8[6];
+ unsigned int utf8_len, i;
+ char *text;
+ int width = 0, height = 0;
+ bool reflow = false;
+
+ /* box_dump(textarea, 0); */
+ LOG(("key %i at %i in '%.*s'", key, char_offset,
+ (int) text_box->length, text_box->text));
+
+ box_coords(textarea, &box_x, &box_y);
+ box_x -= textarea->scroll_x;
+ box_y -= textarea->scroll_y;
+
+ if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
+ /* normal character insertion */
+ ucs4_to_utf8(key, utf8, utf8_len);
+
+ text = talloc_realloc(bw->current_content, text_box->text,
+ char, text_box->length + 8);
+ if (!text) {
+ warn_user("NoMemory", 0);
+ return;
+ }
+ text_box->text = text;
+ memmove(text_box->text + char_offset + utf8_len,
+ text_box->text + char_offset,
+ text_box->length - char_offset);
+ for (i = 0; i != utf8_len; i++)
+ text_box->text[char_offset + i] = utf8[i];
+ text_box->length += utf8_len;
+ text_box->text[text_box->length] = 0;
+ text_box->width = UNKNOWN_WIDTH;
+ char_offset += utf8_len;
+
+ reflow = true;
+
+ } else switch (key) {
+ case 8:
+ case 127: /* delete to left */
+ if (char_offset == 0) {
+ /* at the start of a text box */
+ if (!text_box->prev)
+ /* at very beginning of text area: ignore */
+ return;
+
+ if (text_box->prev->type == BOX_BR) {
+ /* previous box is BR: remove it */
+ t = text_box->prev;
+ t->prev->next = t->next;
+ t->next->prev = t->prev;
+ box_free(t);
+ }
+
+ /* delete space by merging with previous text box */
+ prev = text_box->prev;
+ assert(prev->text);
+ text = talloc_realloc(bw->current_content,
+ prev->text, char,
+ prev->length + text_box->length + 1);
+ if (!text) {
+ warn_user("NoMemory", 0);
+ return;
+ }
+ prev->text = text;
+ memcpy(prev->text + prev->length, text_box->text,
+ text_box->length);
+ char_offset = prev->length; /* caret at join */
+ prev->length += text_box->length;
+ prev->text[prev->length] = 0;
+ prev->width = UNKNOWN_WIDTH;
+ prev->next = text_box->next;
+ if (prev->next)
+ prev->next->prev = prev;
+ else
+ prev->parent->last = prev;
+ box_free(text_box);
+
+ /* place caret at join (see above) */
+ text_box = prev;
+
+ } else {
+ /* delete a character */
+ int prev_offset = char_offset;
+ utf8_prev(text_box->text, char_offset);
+
+ memmove(text_box->text + char_offset,
+ text_box->text + prev_offset,
+ text_box->length - prev_offset);
+ text_box->length -= (prev_offset - char_offset);
+ text_box->width = UNKNOWN_WIDTH;
+ }
+
+ reflow = true;
+ break;
+
+ case 10:
+ case 13: /* paragraph break */
+ text = talloc_array(bw->current_content, char,
+ text_box->length + 1);
+ if (!text) {
+ warn_user("NoMemory", 0);
+ return;
+ }
+
+ new_br = box_create(text_box->style, 0, text_box->title, 0,
+ bw->current_content);
+ new_text = talloc(bw->current_content, struct box);
+ if (!new_text) {
+ warn_user("NoMemory", 0);
+ return;
+ }
+
+ new_br->type = BOX_BR;
+ box_insert_sibling(text_box, new_br);
+
+ memcpy(new_text, text_box, sizeof (struct box));
+ new_text->clone = 1;
+ new_text->text = text;
+ memcpy(new_text->text, text_box->text + char_offset,
+ text_box->length - char_offset);
+ new_text->length = text_box->length - char_offset;
+ text_box->length = char_offset;
+ text_box->width = new_text->width = UNKNOWN_WIDTH;
+ box_insert_sibling(new_br, new_text);
+
+ /* place caret at start of new text box */
+ text_box = new_text;
+ char_offset = 0;
+
+ reflow = true;
+ break;
+
+ case 22: /* Ctrl + V */
+// gui_paste_from_clipboard();
+ break;
+
+ case 24: /* Ctrl + X */
+ if (gui_copy_to_clipboard(bw->sel)) {
+ /** \todo block delete */
+ }
+ break;
+
+ case 28: /* Right cursor -> */
+ if ((unsigned int) char_offset != text_box->length) {
+ utf8_next(text_box->text, text_box->length,
+ char_offset);
+ } else {
+ if (!text_box->next)
+ /* at end of text area: ignore */
+ return;
+
+ text_box = text_box->next;
+ if (text_box->type == BOX_BR)
+ text_box = text_box->next;
+ char_offset = 0;
+ }
+ break;
+
+ case 29: /* Left cursor <- */
+ if (char_offset != 0) {
+ utf8_prev(text_box->text, char_offset);
+ } else {
+ if (!text_box->prev)
+ /* at start of text area: ignore */
+ return;
+
+ text_box = text_box->prev;
+ if (text_box->type == BOX_BR)
+ text_box = text_box->prev;
+ char_offset = text_box->length;
+ }
+ break;
+
+ case 30: /* Up cursor */
+ browser_window_textarea_click(bw, BROWSER_MOUSE_CLICK_1,
+ textarea,
+ box_x, box_y,
+ text_box->x + pixel_offset,
+ inline_container->y + text_box->y - 1);
+ return;
+
+ case 31: /* Down cursor */
+ browser_window_textarea_click(bw, BROWSER_MOUSE_CLICK_1,
+ textarea,
+ box_x, box_y,
+ text_box->x + pixel_offset,
+ inline_container->y + text_box->y +
+ text_box->height + 1);
+ return;
+
+ default:
+ return;
+ }
+
+ /* box_dump(textarea, 0); */
+ /* for (struct box *t = inline_container->children; t; t = t->next) {
+ assert(t->type == BOX_INLINE);
+ 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);
+ if (!t->next) {
+ assert(inline_container->last == t);
+ break;
+ }
+ if (t->next->type == BOX_BR) {
+ assert(t->next->next);
+ t = t->next;
+ }
+ } */
+
+ if (reflow) {
+ /* reflow textarea preserving width and height */
+ width = textarea->width;
+ height = textarea->height;
+ if (!layout_inline_container(inline_container, width,
+ textarea, 0, 0,
+ bw->current_content))
+ warn_user("NoMemory", 0);
+ textarea->width = width;
+ textarea->height = height;
+ layout_calculate_descendant_bboxes(textarea);
+ }
+
+ 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);
+ }
+
+ /* 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 -
+ textarea->height / 2;
+ if (textarea->descendant_y1 - textarea->height < new_scroll_y)
+ new_scroll_y = textarea->descendant_y1 - textarea->height;
+ if (new_scroll_y < 0)
+ new_scroll_y = 0;
+ box_y += textarea->scroll_y - new_scroll_y;
+
+ nsfont_width(text_box->style, text_box->text,
+ char_offset, &pixel_offset);
+
+ textarea->gadget->caret_inline_container = inline_container;
+ textarea->gadget->caret_text_box = text_box;
+ textarea->gadget->caret_box_offset = char_offset;
+ textarea->gadget->caret_pixel_offset = pixel_offset;
+ browser_window_place_caret(bw,
+ box_x + inline_container->x + text_box->x +
+ pixel_offset,
+ box_y + inline_container->y + text_box->y,
+ text_box->height,
+ browser_window_textarea_callback, textarea);
+
+ if (new_scroll_y != textarea->scroll_y || reflow) {
+ textarea->scroll_y = new_scroll_y;
+ browser_redraw_box(bw->current_content, textarea);
+ }
+}
+
+
+/**
+ * Handle clicks in a text or password input box by placing the caret.
+ *
+ * \param bw browser window where click occurred
+ * \param input input box
+ * \param box_x position of input in global document coordinates
+ * \param box_y position of input in global document coordinates
+ * \param x coordinate of click relative to input
+ * \param y coordinate of click relative to input
+ */
+void browser_window_input_click(struct browser_window* bw,
+ struct box *input,
+ int box_x, int box_y,
+ int x, int y)
+{
+ size_t char_offset = 0;
+ int pixel_offset = 0, dx = 0;
+ struct box *text_box = input->children->children;
+ int uchars;
+ unsigned int offset;
+
+ nsfont_position_in_string(text_box->style, text_box->text,
+ text_box->length, x - text_box->x,
+ &char_offset, &pixel_offset);
+ assert(char_offset <= text_box->length);
+
+ text_box->x = 0;
+ if ((input->width < text_box->width) &&
+ (input->width / 2 < pixel_offset)) {
+ dx = text_box->x;
+ text_box->x = input->width / 2 - pixel_offset;
+ if (text_box->x < input->width - text_box->width)
+ text_box->x = input->width - text_box->width;
+ dx -= text_box->x;
+ }
+ input->gadget->caret_box_offset = char_offset;
+ /* Update caret_form_offset */
+ for (uchars = 0, offset = 0; offset < char_offset; uchars++) {
+ if ((text_box->text[offset] & 0x80) == 0x00) {
+ offset++;
+ continue;
+ }
+ assert((text_box->text[offset] & 0xC0) == 0xC0);
+ for (++offset; offset < char_offset &&
+ (text_box->text[offset] & 0xC0) == 0x80;
+ offset++)
+ /* do nothing */;
+ }
+ /* uchars is the number of real Unicode characters at the left
+ * side of the caret.
+ */
+ for (offset = 0; uchars > 0 && offset < input->gadget->length;
+ uchars--) {
+ if ((input->gadget->value[offset] & 0x80) == 0x00) {
+ offset++;
+ continue;
+ }
+ assert((input->gadget->value[offset] & 0xC0) == 0xC0);
+ for (++offset; offset < input->gadget->length &&
+ (input->gadget->value[offset] & 0xC0) == 0x80;
+ offset++)
+ /* do nothing */;
+ }
+ assert(uchars == 0);
+ input->gadget->caret_form_offset = offset;
+ input->gadget->caret_pixel_offset = pixel_offset;
+ browser_window_place_caret(bw,
+ box_x + input->children->x +
+ text_box->x + pixel_offset,
+ box_y + input->children->y + text_box->y,
+ text_box->height,
+ browser_window_input_callback, input);
+
+ if (dx)
+ browser_redraw_box(bw->current_content, input);
+}
+
+
+/**
+ * Key press callback for text or password input boxes.
+ *
+ * \param bw The browser window containing the input box
+ * \param key The UCS4 character codepoint
+ * \param p The input box
+ */
+void browser_window_input_callback(struct browser_window *bw,
+ wchar_t key, void *p)
+{
+ struct box *input = (struct box *)p;
+ struct box *text_box = input->children->children;
+ unsigned int box_offset = input->gadget->caret_box_offset;
+ unsigned int form_offset = input->gadget->caret_form_offset;
+ int pixel_offset, dx;
+ int box_x, box_y;
+ struct form* form = input->gadget->form;
+ bool changed = false;
+ char utf8[6];
+ unsigned int utf8_len;
+ bool to_textarea = false;
+
+ box_coords(input, &box_x, &box_y);
+
+ if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
+ char *value;
+
+ /* have we exceeded max length of input? */
+ utf8_length(input->gadget->value, utf8_len);
+ if (utf8_len >= input->gadget->maxlength)
+ return;
+
+ /* normal character insertion */
+
+ /* Insert key in gadget */
+ ucs4_to_utf8(key, utf8, utf8_len);
+
+ value = realloc(input->gadget->value,
+ input->gadget->length + utf8_len + 1);
+ if (!value) {
+ warn_user("NoMemory", 0);
+ return;
+ }
+ input->gadget->value = value;
+
+ memmove(input->gadget->value + form_offset + utf8_len,
+ input->gadget->value + form_offset,
+ input->gadget->length - form_offset);
+ memcpy(input->gadget->value + form_offset, utf8, utf8_len);
+ input->gadget->length += utf8_len;
+ input->gadget->value[input->gadget->length] = 0;
+ form_offset += utf8_len;
+
+ /* Insert key in text box */
+ /* Convert space into NBSP */
+ ucs4_to_utf8((input->gadget->type == GADGET_PASSWORD) ?
+ '*' : (key == ' ') ? 160 : key,
+ utf8, utf8_len);
+
+ value = talloc_realloc(bw->current_content, text_box->text,
+ char, text_box->length + utf8_len + 1);
+ if (!value) {
+ warn_user("NoMemory", 0);
+ return;
+ }
+ text_box->text = value;
+
+ memmove(text_box->text + box_offset + utf8_len,
+ text_box->text + box_offset,
+ text_box->length - box_offset);
+ memcpy(text_box->text + box_offset, utf8, utf8_len);
+ text_box->length += utf8_len;
+ text_box->text[text_box->length] = 0;
+ box_offset += utf8_len;
+
+ nsfont_width(text_box->style, text_box->text,
+ text_box->length, &text_box->width);
+ changed = true;
+
+ } else switch (key) {
+ case 8:
+ case 127: { /* Delete to left */
+ int prev_offset;
+
+ if (box_offset == 0)
+ return;
+
+ /* Gadget */
+ prev_offset = form_offset;
+ /* Go to the previous valid UTF-8 character */
+ utf8_prev(input->gadget->value, form_offset);
+
+ memmove(input->gadget->value + form_offset,
+ input->gadget->value + prev_offset,
+ input->gadget->length - prev_offset);
+ input->gadget->length -= prev_offset - form_offset;
+ input->gadget->value[input->gadget->length] = 0;
+
+ /* Text box */
+ prev_offset = box_offset;
+ /* Go to the previous valid UTF-8 character */
+ utf8_prev(text_box->text, box_offset);
+
+ memmove(text_box->text + box_offset,
+ text_box->text + prev_offset,
+ text_box->length - prev_offset);
+ text_box->length -= prev_offset - box_offset;
+ text_box->text[text_box->length] = 0;
+
+ nsfont_width(text_box->style, text_box->text,
+ text_box->length, &text_box->width);
+
+ changed = true;
+ }
+ break;
+
+ case 9: { /* Tab */
+ struct form_control *next_input;
+ for (next_input = input->gadget->next;
+ next_input &&
+ next_input->type != GADGET_TEXTBOX &&
+ next_input->type != GADGET_TEXTAREA &&
+ next_input->type != GADGET_PASSWORD;
+ next_input = next_input->next)
+ ;
+ if (!next_input)
+ return;
+
+ input = next_input->box;
+ text_box = input->children->children;
+ box_coords(input, &box_x, &box_y);
+ form_offset = box_offset = 0;
+ to_textarea = next_input->type == GADGET_TEXTAREA;
+ }
+ break;
+
+ case 10:
+ case 13: /* Return/Enter hit */
+ if (form)
+ browser_form_submit(bw, form, 0);
+ return;
+
+ case 11: { /* Shift + Tab */
+ struct form_control *prev_input;
+ for (prev_input = input->gadget->prev;
+ prev_input &&
+ prev_input->type != GADGET_TEXTBOX &&
+ prev_input->type != GADGET_TEXTAREA &&
+ prev_input->type != GADGET_PASSWORD;
+ prev_input = prev_input->prev)
+ ;
+ if (!prev_input)
+ return;
+
+ input = prev_input->box;
+ text_box = input->children->children;
+ box_coords(input, &box_x, &box_y);
+ form_offset = box_offset = 0;
+ to_textarea = prev_input->type == GADGET_TEXTAREA;
+ }
+ break;
+
+ case 21: /* Ctrl + U */
+ text_box->text[0] = 0;
+ text_box->length = 0;
+ box_offset = 0;
+
+ input->gadget->value[0] = 0;
+ input->gadget->length = 0;
+ form_offset = 0;
+
+ text_box->width = 0;
+ changed = true;
+ break;
+
+ case 22: /* Ctrl + V */
+// gui_paste_from_clipboard();
+ break;
+
+ case 28: /* Right cursor -> */
+ /* Text box */
+ /* Go to the next valid UTF-8 character */
+ utf8_next(text_box->text, text_box->length, box_offset);
+ /* Gadget */
+ /* Go to the next valid UTF-8 character */
+ utf8_next(input->gadget->value, input->gadget->length,
+ form_offset);
+ break;
+
+ case 29: /* Left cursor -> */
+ /* Text box */
+ /* Go to the previous valid UTF-8 character */
+ utf8_prev(text_box->text, box_offset);
+ /* Gadget */
+ /* Go to the previous valid UTF-8 character */
+ utf8_prev(input->gadget->value, form_offset);
+ break;
+
+ case 128: /* Ctrl + Left */
+ box_offset = form_offset = 0;
+ break;
+
+ case 129: /* Ctrl + Right */
+ box_offset = text_box->length;
+ form_offset = input->gadget->length;
+ break;
+
+ default:
+ return;
+ }
+
+ nsfont_width(text_box->style, text_box->text, box_offset,
+ &pixel_offset);
+ dx = text_box->x;
+ text_box->x = 0;
+ if (input->width < text_box->width &&
+ input->width / 2 < pixel_offset) {
+ text_box->x = input->width / 2 - pixel_offset;
+ if (text_box->x < input->width - text_box->width)
+ text_box->x = input->width - text_box->width;
+ }
+ dx -= text_box->x;
+ input->gadget->caret_pixel_offset = pixel_offset;
+
+ if (to_textarea) {
+ /* moving to textarea so need to set these up */
+ input->gadget->caret_inline_container = input->children;
+ input->gadget->caret_text_box = text_box;
+ }
+
+ input->gadget->caret_box_offset = box_offset;
+ input->gadget->caret_form_offset = form_offset;
+
+ browser_window_place_caret(bw,
+ box_x + input->children->x +
+ text_box->x + pixel_offset,
+ box_y + input->children->y + text_box->y,
+ text_box->height,
+ /* use the appropriate callback */
+ to_textarea ? browser_window_textarea_callback
+ : browser_window_input_callback, input);
+
+ if (dx || changed)
+ browser_redraw_box(bw->current_content, input);
+}
+
+
+/**
+ * Position the caret and assign a callback for key presses.
+ *
+ * \param bw The browser window in which to place the caret
+ * \param x X coordinate of the caret
+ * \param y Y coordinate
+ * \param height Height of caret
+ * \param callback Callback function for keypresses
+ * \param p Callback private data pointer, passed to callback function
+ */
+void browser_window_place_caret(struct browser_window *bw,
+ int x, int y, int height,
+ void (*callback)(struct browser_window *bw,
+ wchar_t key, void *p),
+ void *p)
+{
+ gui_window_place_caret(bw->window, x, y, height);
+ bw->caret_callback = callback;
+ bw->caret_p = p;
+}
+
+
+/**
+ * Removes the caret and callback for key process.
+ *
+ * \param bw The browser window from which to remove caret
+ */
+void browser_window_remove_caret(struct browser_window *bw)
+{
+ gui_window_remove_caret(bw->window);
+ bw->caret_callback = NULL;
+ bw->caret_p = NULL;
+}
+
+
+/**
+ * Handle key presses in a browser window.
+ *
+ * \param bw The browser window with input focus
+ * \param key The UCS4 character codepoint
+ * \return true if key handled, false otherwise
+ */
+bool browser_window_key_press(struct browser_window *bw, wchar_t key)
+{
+ /* keys that take effect wherever the caret is positioned */
+ switch (key) {
+ case 1: /* Ctrl + A */
+ selection_select_all(bw->sel);
+ return true;
+
+ case 3: /* Ctrl + C */
+ gui_copy_to_clipboard(bw->sel);
+ return true;
+
+ case 26: /* Ctrl + Z */
+ selection_clear(bw->sel, true);
+ return true;
+
+ case 27: /** Escape */
+ if (selection_defined(bw->sel)) {
+ selection_clear(bw->sel, true);
+ return true;
+ }
+ break;
+ }
+
+ /* pass on to the appropriate field */
+ if (!bw->caret_callback)
+ return false;
+ bw->caret_callback(bw, key, bw->caret_p);
+ return true;
+}
diff --git a/desktop/textinput.h b/desktop/textinput.h
new file mode 100644
index 000000000..155f526a0
--- /dev/null
+++ b/desktop/textinput.h
@@ -0,0 +1,27 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
+ * Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
+ */
+
+/** \file
+ * Textual input handling (interface)
+ */
+
+struct browser_window;
+struct box;
+
+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 browser_window_input_click(struct browser_window* bw,
+ struct box *input,
+ int box_x, int box_y,
+ int x, int y);
+void browser_window_remove_caret(struct browser_window *bw);
diff --git a/makefile b/makefile
index 5e1eb0c80..6a682236a 100644
--- a/makefile
+++ b/makefile
@@ -22,8 +22,8 @@ OBJECTS_COMMON += css.o css_enum.o parser.o ruleset.o scanner.o # css/
OBJECTS_COMMON += box.o box_construct.o box_normalise.o form.o html.o \
html_redraw.o layout.o list.o textplain.o # render/
OBJECTS_COMMON += messages.o pool.o talloc.o url.o utils.o # utils/
-OBJECTS_COMMON += imagemap.o loginlist.o options.o \
- selection.o tree.o # desktop/
+OBJECTS_COMMON += imagemap.o loginlist.o options.o selection.o \
+ textinput.o tree.o # desktop/
OBJECTS_IMAGE = jpeg.o mng.o gif.o gifread.o # image/
diff --git a/riscos/ucstables.c b/riscos/ucstables.c
new file mode 100644
index 000000000..3dc38e066
--- /dev/null
+++ b/riscos/ucstables.c
@@ -0,0 +1,333 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
+ */
+
+/** \file
+ * UCS conversion tables
+ */
+
+#include "oslib/territory.h"
+#include "netsurf/riscos/ucstables.h"
+
+/* Common values (ASCII) */
+#define common \
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, \
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, \
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, \
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, \
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, \
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, \
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, \
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127 \
+
+/* 0x8c->0x9F, used by many of the encodings */
+#define common2 \
+ 0x2026, 0x2122, 0x2030, 0x2022, 0x2018, 0x2019, 0x2039, 0x203a, \
+ 0x201c, 0x201d, 0x201e, 0x2013, 0x2014, 0x2212, 0x0152, 0x0153, \
+ 0x2020, 0x2021, 0xfb01, 0xfb02
+
+static int latin1_table[256] =
+{
+ common,
+ 0x20ac, 0x0174, 0x0175, -1, -1, 0x0176, 0x0177, -1, -1, -1, -1, -1,
+ common2,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+};
+
+static int latin2_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7,
+ 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B,
+ 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7,
+ 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C,
+ 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
+ 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
+ 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
+ 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
+ 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
+ 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
+ 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
+ 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
+};
+
+static int latin3_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, -1, 0x0124, 0x00A7,
+ 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, -1, 0x017B,
+ 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7,
+ 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, -1, 0x017C,
+ 0x00C0, 0x00C1, 0x00C2, -1, 0x00C4, 0x010A, 0x0108, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ -1, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7,
+ 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, -1, 0x00E4, 0x010B, 0x0109, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ -1, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7,
+ 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9
+};
+
+static int latin4_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7,
+ 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF,
+ 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7,
+ 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B,
+ 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E,
+ 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A,
+ 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF,
+ 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F,
+ 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B,
+ 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9
+};
+
+static int latin5_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+ 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
+};
+
+static int latin6_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7,
+ 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A,
+ 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7,
+ 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B,
+ 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E,
+ 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF,
+ 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168,
+ 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F,
+ 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169,
+ 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138
+};
+
+static int latin7_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x2026, 0x2122, 0x2030, 0x2022, 0x2018, -1, 0x2039, 0x203a,
+ -1, -1, -1, 0x2013, 0x2014, 0x2212, 0x0152, 0x0153,
+ 0x2020, 0x2021, 0xfb01, 0xfb02,
+ 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7,
+ 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7,
+ 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
+ 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
+ 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
+ 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,
+ 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
+ 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,
+ 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
+ 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,
+ 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019
+};
+
+static int latin8_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7,
+ 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178,
+ 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56,
+ 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A,
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF
+};
+
+static int latin9_table[256] =
+{
+ common,
+ -1, 0x0174, 0x0175, -1, -1, 0x0176, 0x0177, -1, -1, -1, -1, -1,
+ 0x2026, 0x2122, 0x2030, 0x2022, 0x2018, 0x2019, 0x2039, 0x203a,
+ 0x201c, 0x201d, 0x201e, 0x2013, 0x2014, 0x2212, -1, -1,
+ 0x2020, 0x2021, 0xfb01, 0xfb02,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7,
+ 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7,
+ 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
+};
+
+static int welsh_table[256] =
+{
+ common,
+ 0x20ac, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ common2,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x1E80, 0x00A9, 0x1E82, 0x00AB, 0x1EF2, 0x00AD, 0x00AE, 0x0178,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+ 0x1E81, 0x00B9, 0x1E83, 0x00BB, 0x1EF3, 0x1E84, 0x1E85, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF
+};
+
+static int greek_table[256] =
+{
+ common,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00A0, 0x2018, 0x2019, 0x00A3, 0x20AC, 0x20AF, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x037A, 0x00AB, 0x00AC, 0x00AD, 0x037E, 0x2015,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x0387,
+ 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
+ 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+ 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+ 0x03A0, 0x03A1, -1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,
+ 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+ 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, -1
+};
+
+static int cyrillic_table[256] =
+{
+ common,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
+ 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F,
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+ 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+ 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F
+};
+
+static int hebrew_table[256] =
+{
+ common,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00A0, -1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x203E,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+ 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 0x2017,
+ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+ 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+ 0x05E8, 0x05E9, 0x05EA, -1, -1, 0x200E, 0x200F, -1
+};
+
+/**
+ * Retrieve UCS table (above), given alphabet number
+ *
+ * \param alphabet The RISC OS alphabet number
+ * \return pointer to table, or NULL if not found
+ */
+int *ucstable_from_alphabet(int alphabet)
+{
+ int *ucstable = NULL;
+
+ switch (alphabet) {
+ case territory_ALPHABET_LATIN1:
+ ucstable = latin1_table;
+ break;
+ case territory_ALPHABET_LATIN2:
+ ucstable = latin2_table;
+ break;
+ case territory_ALPHABET_LATIN3:
+ ucstable = latin3_table;
+ break;
+ case territory_ALPHABET_LATIN4:
+ ucstable = latin4_table;
+ break;
+ case territory_ALPHABET_LATIN5:
+ ucstable = latin5_table;
+ break;
+ case territory_ALPHABET_LATIN6:
+ ucstable = latin6_table;
+ break;
+ case 114: /* Latin7 */
+ ucstable = latin7_table;
+ break;
+ case 115: /* Latin8 */
+ ucstable = latin8_table;
+ break;
+ case territory_ALPHABET_LATIN9:
+ ucstable = latin9_table;
+ break;
+ case territory_ALPHABET_WELSH:
+ ucstable = welsh_table;
+ break;
+ case territory_ALPHABET_GREEK:
+ ucstable = greek_table;
+ break;
+ case territory_ALPHABET_CYRILLIC:
+ ucstable = cyrillic_table;
+ break;
+ case territory_ALPHABET_HEBREW:
+ ucstable = hebrew_table;
+ break;
+ default:
+ ucstable = NULL;
+ break;
+ }
+
+ return ucstable;
+}
diff --git a/riscos/ucstables.h b/riscos/ucstables.h
new file mode 100644
index 000000000..e6cef41da
--- /dev/null
+++ b/riscos/ucstables.h
@@ -0,0 +1,13 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
+ */
+
+/** \file
+ * UCS conversion tables (interface)
+ * This is only used if nothing claims Service_International,8
+ */
+
+int *ucstable_from_alphabet(int alphabet);
diff --git a/riscos/window.c b/riscos/window.c
index fcd0c3206..33b5993a1 100644
--- a/riscos/window.c
+++ b/riscos/window.c
@@ -21,6 +21,7 @@
#include "oslib/colourtrans.h"
#include "oslib/osbyte.h"
#include "oslib/osspriteop.h"
+#include "oslib/serviceinternational.h"
#include "oslib/wimp.h"
#include "oslib/wimpspriteop.h"
#include "netsurf/utils/config.h"
@@ -38,9 +39,11 @@
#include "netsurf/riscos/theme.h"
#include "netsurf/riscos/thumbnail.h"
#include "netsurf/riscos/treeview.h"
+#include "netsurf/riscos/ucstables.h"
#include "netsurf/riscos/url_complete.h"
#include "netsurf/riscos/wimp.h"
#include "netsurf/utils/log.h"
+#include "netsurf/utils/talloc.h"
#include "netsurf/utils/url.h"
#include "netsurf/utils/utils.h"
#include "netsurf/utils/messages.h"
@@ -341,7 +344,7 @@ void gui_window_set_title(struct gui_window *g, const char *title)
assert(g);
assert(title);
-
+
if (g->option.scale != 1.0) {
scale_disp = g->option.scale * 100;
if ((float)scale_disp != g->option.scale * 100)
@@ -473,7 +476,7 @@ void ro_gui_window_redraw(struct gui_window *g, wimp_draw *redraw)
clear_background = true;
scale = g->option.scale;
break;
-
+
#ifdef WITH_SPRITE
case CONTENT_SPRITE:
@@ -1084,7 +1087,7 @@ void ro_gui_window_open(struct gui_window *g, wimp_open *open)
/* first resize stops any flickering by making the URL window on top */
ro_gui_url_complete_resize(g, open);
-
+
error = xwimp_open_window(open);
if (error) {
LOG(("xwimp_open_window: 0x%x: %s",
@@ -1253,12 +1256,12 @@ void ro_gui_toolbar_click(struct gui_window *g, wimp_pointer *pointer)
pointer->pos.y, g->window);
return;
}
-
+
/* Handle toolbar edits
*/
if ((g->toolbar->editor) && (pointer->i < ICON_TOOLBAR_URL)) {
ro_gui_theme_toolbar_editor_click(g->toolbar, pointer);
- return;
+ return;
}
/* Handle the buttons appropriately
@@ -1535,13 +1538,15 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar)
{
struct content *content = g->bw->current_content;
wimp_window_state state;
- int y;
+ int y, t_alphabet;
char *url;
char *toolbar_url;
os_error *error;
wimp_pointer pointer;
url_func_result res;
float old_scale;
+ static int *ucstable = NULL;
+ static int alphabet = 0;
error = xwimp_get_pointer_info(&pointer);
if (error) {
@@ -1551,24 +1556,147 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar)
return false;
}
+ /* In order to make sensible use of the 0x80->0xFF ranges specified
+ * in the RISC OS 8bit alphabets, we must do the following:
+ *
+ * + Read the currently selected alphabet
+ * + Acquire a pointer to the UCS conversion table for this alphabet:
+ * + Try using ServiceInternational 8 to get the table
+ * + If that fails, use our internal table (see ucstables.c)
+ * + If the alphabet is not UTF8 and the conversion table exists:
+ * + Lookup UCS code in the conversion table
+ * + If code is -1 (i.e. undefined):
+ * + Use codepoint 0xFFFD instead
+ * + If the alphabet is UTF8, we must buffer input, thus:
+ * + If the keycode is < 0x80:
+ * + Handle it directly
+ * + If the keycode is a UTF8 sequence start:
+ * + Initialise the buffer appropriately
+ * + Otherwise:
+ * + OR in relevant bits from keycode to buffer
+ * + If we've received an entire UTF8 character:
+ * + Handle UCS code
+ * + Otherwise:
+ * + Simply handle the keycode directly, as there's no easy way
+ * of performing the mapping from keycode -> UCS4 codepoint.
+ */
+ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &t_alphabet);
+ if (error) {
+ LOG(("failed reading alphabet: 0x%x: %s",
+ error->errnum, error->errmess));
+ /* prevent any corruption of ucstable */
+ t_alphabet = alphabet;
+ }
+
+ if (t_alphabet != alphabet) {
+ osbool unclaimed;
+ /* Alphabet has changed, so read UCS table location */
+ alphabet = t_alphabet;
+ error = xserviceinternational_get_ucs_conversion_table(
+ alphabet, &unclaimed,
+ (void**)&ucstable);
+ if (error) {
+ LOG(("failed reading UCS conversion table: 0x%x: %s",
+ error->errnum, error->errmess));
+ /* try using our own table instead */
+ ucstable = ucstable_from_alphabet(alphabet);
+ }
+ if (unclaimed)
+ /* Service wasn't claimed so use our own ucstable */
+ ucstable = ucstable_from_alphabet(alphabet);
+ }
+
/* First send the key to the browser window, eg. form fields. */
if (!toolbar) {
wchar_t c = (wchar_t)key;
+ static wchar_t wc = 0; /* buffer for UTF8 alphabet */
+ static int shift = 0;
/* Munge cursor keys into unused control chars */
/* We can't map onto 1->26 (reserved for ctrl+<qwerty>
That leaves 27->31 and 128->159 */
if (c == 394) c = 9; /* Tab */
else if (c == 410) c = 11; /* Shift+Tab */
- else if (c == 428) c = 128; /* Ctrl+Left */
- else if (c == 429) c = 129; /* Ctrl+Right*/
+ else if (c == 428) c = 128; /* Ctrl+Left */
+ else if (c == 429) c = 129; /* Ctrl+Right*/
else if (c == 396) c = 29; /* Left */
else if (c == 397) c = 28; /* Right */
else if (c == 398) c = 31; /* Down */
else if (c == 399) c = 30; /* Up */
- if (c < 256)
+ if (c < 256) {
+ if (alphabet != 111 /* UTF8 */ && ucstable != NULL)
+ /* read UCS4 value out of table */
+ c = ucstable[c] == -1 ? 0xFFFD : ucstable[c];
+ else if (alphabet == 111 /* UTF8 */) {
+ if ((c & 0x80) == 0x00 ||
+ (c & 0xC0) == 0xC0) {
+ /* UTF8 start sequence */
+ if ((c & 0xE0) == 0xC0) {
+ wc = ((c & 0x1F) << 6);
+ shift = 1;
+ return true;
+ }
+ else if ((c & 0xF0) == 0xE0) {
+ wc = ((c & 0x0F) << 12);
+ shift = 2;
+ return true;
+ }
+ else if ((c & 0xF8) == 0xF0) {
+ wc = ((c & 0x07) << 18);
+ shift = 3;
+ return true;
+ }
+ /* These next two have been removed
+ * from RFC3629, but there's no
+ * guarantee that RISC OS won't
+ * generate a UCS4 value outside the
+ * UTF16 plane, so we handle them
+ * anyway. */
+ else if ((c & 0xFC) == 0xF8) {
+ wc = ((c & 0x03) << 24);
+ shift = 4;
+ }
+ else if ((c & 0xFE) == 0xFC) {
+ wc = ((c & 0x01) << 30);
+ shift = 5;
+ }
+ else if (c >= 0x80) {
+ /* If this ever happens,
+ * RISC OS' UTF8 keyboard
+ * drivers are broken */
+ LOG(("unexpected UTF8 start"
+ " byte %x (ignoring)",
+ c));
+ return true;
+ }
+ /* Anything else is ASCII, so just
+ * handle it directly. */
+ }
+ else {
+ if ((c & 0xC0) != 0x80) {
+ /* If this ever happens,
+ * RISC OS' UTF8 keyboard
+ * drivers are broken */
+ LOG(("unexpected keycode: "
+ "%x (ignoring)", c));
+ return true;
+ }
+
+ /* Continuation of UTF8 character */
+ wc |= ((c & 0x3F) << (6 * --shift));
+ if (shift > 0)
+ /* partial character */
+ return true;
+ else
+ /* got entire character, so
+ * fetch from buffer and
+ * handle it */
+ c = wc;
+ }
+ }
if (browser_window_key_press(g->bw, c))
return true;
+ }
}
switch (key) {
@@ -1706,13 +1834,13 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar)
if (scale_snap_to[i] < old_scale) {
g->option.scale = scale_snap_to[i];
break;
- }
+ }
} else {
for (unsigned int i = 0; i < SCALE_SNAP_TO_SIZE; i++)
if (scale_snap_to[i] > old_scale) {
g->option.scale = scale_snap_to[i];
break;
- }
+ }
}
if (g->option.scale < scale_snap_to[0])
g->option.scale = scale_snap_to[0];