[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
This commit is contained in:
John Mark Bell 2005-04-15 18:00:21 +00:00
parent 34b92e905f
commit ee9a4712cd
8 changed files with 1476 additions and 824 deletions

View File

@ -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);
}
}
@ -1385,788 +1365,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.
*
@ -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;
}

View File

@ -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);

949
desktop/textinput.c Normal file
View File

@ -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;
}

27
desktop/textinput.h Normal file
View File

@ -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);

View File

@ -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/

333
riscos/ucstables.c Normal file
View File

@ -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;
}

13
riscos/ucstables.h Normal file
View File

@ -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);

View File

@ -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];