summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.sources4
-rw-r--r--atari/toolbar.c5
-rw-r--r--content/content.h18
-rw-r--r--desktop/browser.c21
-rw-r--r--desktop/selection.c64
-rw-r--r--desktop/textarea.c557
-rw-r--r--desktop/textarea.h50
-rw-r--r--desktop/textinput.c25
-rw-r--r--desktop/tree.c8
-rw-r--r--render/box_construct.c164
-rw-r--r--render/box_textarea.c293
-rw-r--r--render/box_textarea.h (renamed from render/textinput.h)31
-rw-r--r--render/form.c87
-rw-r--r--render/form.h12
-rw-r--r--render/html.c53
-rw-r--r--render/html.h1
-rw-r--r--render/html_forms.c3
-rw-r--r--render/html_interaction.c295
-rw-r--r--render/html_internal.h37
-rw-r--r--render/html_redraw.c20
-rw-r--r--render/layout.c21
-rw-r--r--render/textinput.c2213
22 files changed, 1132 insertions, 2850 deletions
diff --git a/Makefile.sources b/Makefile.sources
index a38290d14..550c563bc 100644
--- a/Makefile.sources
+++ b/Makefile.sources
@@ -11,11 +11,11 @@ S_FETCHERS := curl.c data.c file.c about.c resource.c
S_CSS := css.c dump.c internal.c select.c utils.c
-S_RENDER := box.c box_construct.c box_normalise.c \
+S_RENDER := box.c box_construct.c box_normalise.c box_textarea.c \
font.c form.c \
html.c html_script.c html_interaction.c html_redraw.c \
html_forms.c imagemap.c layout.c list.c search.c table.c \
- textinput.c textplain.c
+ textplain.c
S_UTILS := base64.c corestrings.c filename.c filepath.c hashtable.c \
libdom.c locale.c log.c messages.c nsurl.c talloc.c url.c \
diff --git a/atari/toolbar.c b/atari/toolbar.c
index 480b69904..c472942de 100644
--- a/atari/toolbar.c
+++ b/atari/toolbar.c
@@ -322,8 +322,8 @@ struct s_toolbar *toolbar_create(struct s_gui_win_root *owner)
toolbar_get_grect(t, TOOLBAR_AREA_URL, &url_area);
url_area.g_h -= (TOOLBAR_URL_MARGIN_TOP + TOOLBAR_URL_MARGIN_BOTTOM);
+ textarea_flags ta_flags = TEXTAREA_INTERNAL_CARET;
textarea_setup ta_setup;
- ta_setup.flags = TEXTAREA_INTERNAL_CARET;
ta_setup.width = 300;
ta_setup.height = url_area.g_h;
ta_setup.pad_top = 0;
@@ -337,7 +337,8 @@ struct s_toolbar *toolbar_create(struct s_gui_win_root *owner)
ta_setup.text = font_style_url;
ta_setup.text.foreground = 0x000000;
ta_setup.text.background = 0xffffff;
- t->url.textarea = textarea_create(&ta_setup, tb_txt_callback, t);
+ t->url.textarea = textarea_create(ta_flags, &ta_setup,
+ tb_txt_callback, t);
/* create the throbber widget: */
t->throbber.index = THROBBER_INACTIVE_INDEX;
diff --git a/content/content.h b/content/content.h
index 7781ba9b8..2ae0b38be 100644
--- a/content/content.h
+++ b/content/content.h
@@ -78,7 +78,8 @@ typedef enum {
CONTENT_MSG_SCROLL, /**< Request to scroll content */
CONTENT_MSG_DRAGSAVE, /**< Allow drag saving of content */
CONTENT_MSG_SAVELINK, /**< Allow URL to be saved */
- CONTENT_MSG_POINTER /**< Wants a specific mouse pointer set */
+ CONTENT_MSG_POINTER, /**< Wants a specific mouse pointer set */
+ CONTENT_MSG_DRAG /**< A drag started or ended */
} content_msg;
/** RFC5988 metadata link */
@@ -152,14 +153,15 @@ union content_msg_data {
} savelink;
/** CONTENT_MSG_POINTER - Mouse pointer to set */
browser_pointer_shape pointer;
- /** CONTENT_MSG_PASTE - Content requests that clipboard is pasted */
+ /** CONTENT_MSG_DRAG - Drag start or end */
struct {
- /* TODO: Get rid of these coords.
- * browser_window_paste_text doesn't take coords, but
- * RISC OS front end is doing something different. */
- int x;
- int y;
- } paste;
+ enum {
+ CONTENT_DRAG_NONE,
+ CONTENT_DRAG_SCROLL,
+ CONTENT_DRAG_SELECTION
+ } type;
+ const struct rect *rect;
+ } drag;
};
/** parameters to content redraw */
diff --git a/desktop/browser.c b/desktop/browser.c
index 6a1688192..f9353afef 100644
--- a/desktop/browser.c
+++ b/desktop/browser.c
@@ -396,6 +396,8 @@ void browser_window_set_drag_type(struct browser_window *bw,
top_bw->drag_window = bw;
switch (type) {
+ case DRAGGING_SELECTION:
+ return;
case DRAGGING_SCR_X:
case DRAGGING_SCR_Y:
case DRAGGING_CONTENT_SCROLLBAR:
@@ -1545,6 +1547,25 @@ nserror browser_window_callback(hlcache_handle *c,
browser_window_set_pointer(bw, event->data.pointer);
break;
+ case CONTENT_MSG_DRAG:
+ {
+ browser_drag_type bdt = DRAGGING_NONE;
+
+ switch (event->data.drag.type) {
+ case CONTENT_DRAG_NONE:
+ bdt = DRAGGING_NONE;
+ break;
+ case CONTENT_DRAG_SCROLL:
+ bdt = DRAGGING_CONTENT_SCROLLBAR;
+ break;
+ case CONTENT_DRAG_SELECTION:
+ bdt = DRAGGING_SELECTION;
+ break;
+ }
+ browser_window_set_drag_type(bw, bdt, event->data.drag.rect);
+ }
+ break;
+
default:
assert(0);
}
diff --git a/desktop/selection.c b/desktop/selection.c
index ae9df5ec6..fa82ad027 100644
--- a/desktop/selection.c
+++ b/desktop/selection.c
@@ -47,24 +47,8 @@
/**
* Text selection works by labelling each node in the box tree with its
* start index in the textual representation of the tree's content.
- *
- * Text input fields and text areas have their own number spaces so that
- * they can be relabelled more efficiently when editing (rather than relabel
- * the entire box tree) and so that selections are either wholly within
- * or wholly without the textarea/input box.
*/
-#define IS_INPUT(box) ((box) && (box)->gadget && \
- ((box)->gadget->type == GADGET_TEXTAREA || \
- (box)->gadget->type == GADGET_TEXTBOX || \
- (box)->gadget->type == GADGET_PASSWORD))
-
-/** check whether the given text box is in the same number space as the
- current selection; number spaces are identified by their uppermost nybble */
-
-#define NUMBER_SPACE(x) ((x) & 0xF0000000U)
-#define SAME_SPACE(s, offset) (NUMBER_SPACE((s)->max_idx) == NUMBER_SPACE(offset))
-
#define SPACE_LEN(b) ((b->space == 0) ? 0 : 1)
@@ -99,7 +83,7 @@ static bool save_handler(const char *text, size_t length, struct box *box,
static bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
unsigned *start_offset, unsigned *end_offset);
static bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
- unsigned int num_space, seln_traverse_handler handler,
+ seln_traverse_handler handler,
void *handle, save_text_whitespace *before, bool *first,
bool do_marker);
static struct box *get_box(struct box *b, unsigned offset, size_t *pidx);
@@ -188,13 +172,7 @@ void selection_reinit(struct selection *s, struct box *root)
assert(s);
- if (IS_INPUT(root)) {
- static int next_idx = 0;
- if (!++next_idx) next_idx = 1;
- root_idx = next_idx << 28;
- }
- else
- root_idx = 0;
+ root_idx = 0;
// if (s->root == root) {
// /* keep the same number space as before, because we want
@@ -255,8 +233,7 @@ void selection_init(struct selection *s, struct box *root)
bool selection_read_only(struct selection *s)
{
- return !s->root || !NUMBER_SPACE(s->root->byte_offset);
-
+ return true;
}
@@ -280,13 +257,10 @@ unsigned selection_label_subtree(struct box *box, unsigned idx)
idx += box->length + SPACE_LEN(box);
while (child) {
- if (!IS_INPUT(child)) {
- if (child->list_marker)
- idx = selection_label_subtree(
- child->list_marker, idx);
+ if (child->list_marker)
+ idx = selection_label_subtree(child->list_marker, idx);
- idx = selection_label_subtree(child, idx);
- }
+ idx = selection_label_subtree(child, idx);
child = child->next;
}
@@ -317,9 +291,6 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
top = browser_window_get_root(top);
- if (!SAME_SPACE(s, idx))
- return false; /* not our problem */
-
if (selection_defined(s)) {
if (idx > s->start_idx) {
if (idx <= s->end_idx)
@@ -421,9 +392,6 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
void selection_track(struct selection *s, browser_mouse_state mouse,
unsigned idx)
{
- if (!SAME_SPACE(s, idx))
- return;
-
if (!mouse) {
s->drag_state = DRAG_NONE;
}
@@ -516,7 +484,6 @@ bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
* \param box box subtree
* \param start_idx start of range within textual representation (bytes)
* \param end_idx end of range
- * \param num_space number space of the selection
* \param handler handler function to call
* \param handle handle to pass
* \param before type of whitespace to place before next encountered text
@@ -526,7 +493,7 @@ bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
*/
bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
- unsigned int num_space, seln_traverse_handler handler,
+ seln_traverse_handler handler,
void *handle, save_text_whitespace *before, bool *first,
bool do_marker)
{
@@ -547,7 +514,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
/* do the marker box before continuing with the rest of the
* list element */
if (!traverse_tree(box->list_marker, start_idx, end_idx,
- num_space, handler, handle, before, first,
+ handler, handle, before, first,
true))
return false;
}
@@ -568,8 +535,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
else {
whitespace_text = NULL;
}
- if (num_space == NUMBER_SPACE(box->byte_offset) &&
- box->type != BOX_BR &&
+ if (box->type != BOX_BR &&
!((box->type == BOX_FLOAT_LEFT ||
box->type == BOX_FLOAT_RIGHT) &&
!box->text)) {
@@ -607,7 +573,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
* the tree */
struct box *next = child->next;
- if (!traverse_tree(child, start_idx, end_idx, num_space,
+ if (!traverse_tree(child, start_idx, end_idx,
handler, handle, before, first, false))
return false;
@@ -642,8 +608,7 @@ static bool selection_traverse(struct selection *s,
if (s->root) {
/* HTML */
return traverse_tree(s->root, s->start_idx, s->end_idx,
- NUMBER_SPACE(s->max_idx), handler, handle,
- &before, &first, false);
+ handler, handle, &before, &first, false);
}
/* Text */
@@ -727,7 +692,7 @@ void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx)
if (s->root) {
if (!traverse_tree(s->root, start_idx, end_idx,
- NUMBER_SPACE(s->max_idx), redraw_handler, &rdw,
+ redraw_handler, &rdw,
NULL, NULL, false))
return;
}
@@ -977,10 +942,7 @@ void selection_select_all(struct selection *s)
assert(s);
s->defined = true;
- if (IS_INPUT(s->root))
- selection_set_start(s, s->root->children->children->byte_offset);
- else
- selection_set_start(s, 0);
+ selection_set_start(s, 0);
selection_set_end(s, s->max_idx);
}
diff --git a/desktop/textarea.c b/desktop/textarea.c
index a048058f5..62ee05a12 100644
--- a/desktop/textarea.c
+++ b/desktop/textarea.c
@@ -82,6 +82,7 @@ struct textarea {
plot_font_style_t fstyle; /**< Text style, inc. textarea bg col */
plot_font_style_t sel_fstyle; /**< Selected text style */
+ int line_height; /**< Line height obtained from style */
struct textarea_utf8 text; /**< Textarea text content */
#define PASSWORD_REPLACEMENT "\xe2\x80\xa2"
@@ -102,10 +103,12 @@ struct textarea {
int h_extent; /**< Width of content in px */
int v_extent; /**< Height of content in px */
+
int line_count; /**< Count of lines */
+
#define LINE_CHUNK_SIZE 16
struct line_info *lines; /**< Line info array */
- int line_height; /**< Line height obtained from style */
+ unsigned int lines_alloc_size; /**< Number of LINE_CHUNK_SIZEs */
/** Callback function for messages to client */
textarea_client_callback callback;
@@ -260,67 +263,80 @@ static bool textarea_select_fragment(struct textarea * ta)
static bool textarea_scroll_visible(struct textarea *ta)
{
int x0, x1, y0, y1; /* area we want caret inside */
- int xc, yc; /* area centre */
int x, y; /* caret pos */
int xs = ta->scroll_x;
int ys = ta->scroll_y;
+ int vis;
+ int scrollbar_width;
bool scrolled = false;
if (ta->caret_pos.char_off == -1)
return false;
+ scrollbar_width = (ta->bar_y == NULL) ? 0 : SCROLLBAR_WIDTH;
x0 = ta->border_width + ta->pad_left;
x1 = ta->vis_width - (ta->border_width + ta->pad_right);
- y0 = 0;
- y1 = ta->vis_height - 2 * ta->border_width -
- ta->pad_top - ta->pad_bottom;
- xc = (x1 - x0) / 2 + x0;
- yc = (y1 - y0) / 2 + y0;
+ /* Adjust scroll pos for reduced extents */
+ vis = ta->vis_width - 2 * ta->border_width - scrollbar_width;
+ if (ta->h_extent - xs < vis)
+ xs -= vis - (ta->h_extent - xs);
+
+ /* Get caret pos on screen */
x = ta->caret_x - xs;
- y = ta->caret_y + ta->line_height / 2 - ys;
-
- /* horizontal scroll; centre caret */
- xs += x - xc;
-
- /* force back into range */
- if (xs < 0)
- xs = 0;
- else if (xs > ta->h_extent - (x1 - x0))
- xs = ta->h_extent - (x1 - x0);
-
- /* If scrolled, set new pos. */
- if (xs != ta->scroll_x && ta->bar_x != NULL) {
- scrollbar_set(ta->bar_x, xs, false);
- xs = scrollbar_get_offset(ta->bar_x);
- if (xs != ta->scroll_x) {
- ta->scroll_x = xs;
- scrolled = true;
- }
- } else if (ta->flags & TEXTAREA_MULTILINE && ta->bar_x == NULL &&
- ta->scroll_x != 0) {
+ /* scroll as required */
+ if (x < x0)
+ xs += (x - x0);
+ else if (x > x1)
+ xs += (x - x1);
+
+ if (ta->bar_x == NULL && ta->scroll_x != 0 &&
+ ta->flags & TEXTAREA_MULTILINE) {
+ /* Scrollbar removed, set to zero */
ta->scroll_x = 0;
scrolled = true;
- } else if (xs != ta->scroll_x && !(ta->flags & TEXTAREA_MULTILINE)) {
- ta->scroll_x = xs;
- scrolled = true;
+ } else if (xs != ta->scroll_x) {
+ /* Scrolled, set new pos. */
+ if (ta->bar_x != NULL) {
+ scrollbar_set(ta->bar_x, xs, false);
+ xs = scrollbar_get_offset(ta->bar_x);
+ if (xs != ta->scroll_x) {
+ ta->scroll_x = xs;
+ scrolled = true;
+ }
+
+ } else if (!(ta->flags & TEXTAREA_MULTILINE)) {
+ ta->scroll_x = xs;
+ scrolled = true;
+
+ }
}
/* check and change vertical scroll */
if (ta->flags & TEXTAREA_MULTILINE) {
- /* vertical scroll; centre caret */
- ys += y - yc;
+ scrollbar_width = (ta->bar_x == NULL) ? 0 : SCROLLBAR_WIDTH;
+ y0 = 0;
+ y1 = ta->vis_height - 2 * ta->border_width -
+ ta->pad_top - ta->pad_bottom;
+
+ /* Adjust scroll pos for reduced extents */
+ vis = ta->vis_height - 2 * ta->border_width - scrollbar_width;
+ if (ta->v_extent - ys < vis)
+ ys -= vis - (ta->v_extent - ys);
- /* force back into range */
- if (ys < 0)
- ys = 0;
- else if (ys > ta->v_extent - (y1 - y0))
- ys = ta->v_extent - (y1 - y0);
+ /* Get caret pos on screen */
+ y = ta->caret_y - ys;
+
+ /* scroll as required */
+ if (y < y0)
+ ys += (y - y0);
+ else if (y + ta->line_height > y1)
+ ys += (y + ta->line_height - y1);
- /* If scrolled, set new pos. */
if (ys != ta->scroll_y && ta->bar_y != NULL) {
+ /* Scrolled, set new pos. */
scrollbar_set(ta->bar_y, ys, false);
ys = scrollbar_get_offset(ta->bar_y);
if (ys != ta->scroll_y) {
@@ -329,6 +345,7 @@ static bool textarea_scroll_visible(struct textarea *ta)
}
} else if (ta->bar_y == NULL && ta->scroll_y != 0) {
+ /* Scrollbar removed, set to zero */
ta->scroll_y = 0;
scrolled = true;
}
@@ -361,6 +378,18 @@ static void textarea_scrollbar_callback(void *client_data,
msg.data.redraw.y1 = ta->vis_height;
ta->callback(ta->data, &msg);
+
+ if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Tell client where caret should be placed */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_MOVED_CARET;
+ msg.data.caret.hidden = false;
+ msg.data.caret.x = ta->caret_x - ta->scroll_x;
+ msg.data.caret.y = ta->caret_y - ta->scroll_y;
+ msg.data.caret.height = ta->line_height;
+
+ ta->callback(ta->data, &msg);
+ }
break;
case SCROLLBAR_MSG_SCROLL_START:
@@ -421,6 +450,7 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
LOG(("malloc failed"));
return false;
}
+ ta->lines_alloc_size = LINE_CHUNK_SIZE;
}
if (!(ta->flags & TEXTAREA_MULTILINE)) {
@@ -471,7 +501,7 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
if (x > w)
w = x;
- ta->h_extent = w + ta->pad_left - ta->pad_right;
+ ta->h_extent = w + ta->pad_left + ta->pad_right;
ta->line_count = 1;
return true;
@@ -489,7 +519,10 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
do {
/* Set line count to start point */
- line = start;
+ if (restart)
+ line = 0;
+ else
+ line = start;
/* Find available width */
avail_width = ta->vis_width - 2 * ta->border_width -
@@ -498,6 +531,14 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
avail_width = 0;
h_extent = avail_width;
+ if (ta->text.len == 1) {
+ /* Handle empty textarea */
+ assert(ta->text.data[0] == '\0');
+ ta->lines[line].b_start = 0;
+ ta->lines[line++].b_length = 0;
+ ta->line_count = 1;
+ }
+
restart = false;
for (len = ta->text.len - 1, text = ta->text.data; len > 0;
len -= b_off, text += b_off) {
@@ -536,9 +577,11 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
ta->line_height;
}
- if (line > 0 && line % LINE_CHUNK_SIZE == 0) {
+ /* Ensure enough storage for lines data */
+ if (line > ta->lines_alloc_size - 2) {
+ /* Up to two lines my be added in a pass */
struct line_info *temp = realloc(ta->lines,
- (line + LINE_CHUNK_SIZE) *
+ (line + 2 + LINE_CHUNK_SIZE) *
sizeof(struct line_info));
if (temp == NULL) {
LOG(("realloc failed"));
@@ -546,6 +589,8 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
}
ta->lines = temp;
+ ta->lines_alloc_size = line + 2 +
+ LINE_CHUNK_SIZE;
}
if (para_end == text + b_off && *para_end == '\n') {
@@ -658,7 +703,6 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
* \param ta Text area
* \param x X coordinate
* \param y Y coordinate
- * \param b_off Updated to byte offset
* \param c_off Updated to character offset
*/
static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
@@ -699,9 +743,11 @@ static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
* following line, which is undesirable.
*/
if (ta->flags & TEXTAREA_MULTILINE &&
+ ta->show->data[ta->lines[line].b_start +
+ ta->lines[line].b_length] > 0 &&
bpos == (unsigned)ta->lines[line].b_length &&
ta->show->data[ta->lines[line].b_start +
- ta->lines[line].b_length - 1] == ' ')
+ ta->lines[line].b_length - 1] == ' ')
bpos--;
/* Get character position */
@@ -948,14 +994,15 @@ static bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse,
/* exported interface, documented in textarea.h */
-struct textarea *textarea_create(const textarea_setup *setup,
+struct textarea *textarea_create(const textarea_flags flags,
+ const textarea_setup *setup,
textarea_client_callback callback, void *data)
{
struct textarea *ret;
/* Sanity check flags */
- assert(!(setup->flags & TEXTAREA_MULTILINE &&
- setup->flags & TEXTAREA_PASSWORD));
+ assert(!(flags & TEXTAREA_MULTILINE &&
+ flags & TEXTAREA_PASSWORD));
if (callback == NULL) {
LOG(("no callback provided"));
@@ -971,7 +1018,7 @@ struct textarea *textarea_create(const textarea_setup *setup,
ret->callback = callback;
ret->data = data;
- ret->flags = setup->flags;
+ ret->flags = flags;
ret->vis_width = setup->width;
ret->vis_height = setup->height;
@@ -1008,7 +1055,7 @@ struct textarea *textarea_create(const textarea_setup *setup,
ret->text.len = 1;
ret->text.utf8_len = 0;
- if (setup->flags & TEXTAREA_PASSWORD) {
+ if (flags & TEXTAREA_PASSWORD) {
ret->password.data = malloc(64);
if (ret->password.data == NULL) {
LOG(("malloc failed"));
@@ -1032,10 +1079,9 @@ struct textarea *textarea_create(const textarea_setup *setup,
ret->show = &ret->text;
}
- ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2),
- FMUL(nscss_screen_dpi,
- INTTOFIX((setup->text.size /
- FONT_SIZE_SCALE))))), F_72));
+ ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.3),
+ FMUL(nscss_screen_dpi, INTTOFIX((setup->text.size))))),
+ FONT_SIZE_SCALE * F_72));
ret->caret_pos.line = ret->caret_pos.char_off = -1;
ret->caret_x = 0;
@@ -1045,6 +1091,9 @@ struct textarea *textarea_create(const textarea_setup *setup,
ret->line_count = 0;
ret->lines = NULL;
+ ret->lines_alloc_size = 0;
+
+ textarea_reflow(ret, 0);
return ret;
}
@@ -1093,6 +1142,52 @@ bool textarea_set_text(struct textarea *ta, const char *text)
/* exported interface, documented in textarea.h */
+bool textarea_drop_text(struct textarea *ta, const char *text,
+ size_t text_length)
+{
+ struct textarea_msg msg;
+ unsigned int caret_pos;
+ size_t text_chars;
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return false;
+
+ if (text == NULL)
+ return false;
+
+ text_chars = utf8_bounded_length(text, text_length);
+ caret_pos = textarea_get_caret(ta);
+
+ if (ta->sel_start != -1) {
+ if (!textarea_replace_text(ta, ta->sel_start, ta->sel_end,
+ text, text_length, false))
+ return false;
+
+ caret_pos = ta->sel_start + text_chars;
+ ta->sel_start = ta->sel_end = -1;
+ } else {
+ if (!textarea_replace_text(ta, caret_pos, caret_pos,
+ text, text_length, false))
+ return false;
+ caret_pos += text_chars;
+ }
+
+ textarea_set_caret(ta, caret_pos);
+
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = 0;
+ msg.data.redraw.y0 = 0;
+ msg.data.redraw.x1 = ta->vis_width;
+ msg.data.redraw.y1 = ta->vis_height;
+
+ ta->callback(ta->data, &msg);
+
+ return true;
+}
+
+
+/* exported interface, documented in textarea.h */
int textarea_get_text(struct textarea *ta, char *buf, unsigned int len)
{
if (buf == NULL && len == 0) {
@@ -1202,17 +1297,19 @@ bool textarea_set_caret(struct textarea *ta, int caret)
x += ta->border_width + ta->pad_left;
ta->caret_x = x;
- y = ta->line_height * ta->caret_pos.line;
+ y = ta->line_height * ta->caret_pos.line + text_y_offset;
ta->caret_y = y;
- if (!textarea_scroll_visible(ta)) {
- /* No scroll, just caret moved, redraw it */
+ if (!textarea_scroll_visible(ta) &&
+ ta->flags & TEXTAREA_INTERNAL_CARET) {
+ /* Didn't scroll, just moved caret.
+ * Caret is internal caret, redraw it */
x -= ta->scroll_x;
y -= ta->scroll_y;
x0 = max(x - 1, ta->border_width);
- y0 = max(y + text_y_offset, 0);
+ y0 = max(y, 0);
x1 = min(x + 1, ta->vis_width - ta->border_width);
- y1 = min(y + ta->line_height + text_y_offset,
+ y1 = min(y + ta->line_height,
ta->vis_height);
width = x1 - x0;
@@ -1229,6 +1326,26 @@ bool textarea_set_caret(struct textarea *ta, int caret)
ta->callback(ta->data, &msg);
}
}
+
+ if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Tell client where caret should be placed */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_MOVED_CARET;
+ msg.data.caret.hidden = false;
+ msg.data.caret.x = x - ta->scroll_x;
+ msg.data.caret.y = y - ta->scroll_y;
+ msg.data.caret.height = ta->line_height;
+
+ ta->callback(ta->data, &msg);
+ }
+
+ } else if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Caret hidden, and client is responsible: tell client */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_MOVED_CARET;
+ msg.data.caret.hidden = true;
+
+ ta->callback(ta->data, &msg);
}
return true;
@@ -1248,6 +1365,9 @@ int textarea_get_caret(struct textarea *ta)
if (ta->text.utf8_len == 0)
return 0;
+ if (ta->caret_pos.line >= ta->line_count)
+ return ta->text.utf8_len;
+
/* Calculate character offset of this line's start */
for (b_off = 0; b_off < ta->lines[ta->caret_pos.line].b_start;
b_off = utf8_next(ta->text.data, ta->text.len, b_off))
@@ -1258,19 +1378,20 @@ int textarea_get_caret(struct textarea *ta)
/* exported interface, documented in textarea.h */
-void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
+void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale,
const struct rect *clip, const struct redraw_context *ctx)
{
- struct textarea_msg msg;
const struct plotter_table *plot = ctx->plot;
- int line0, line1, line, left, right;
+ int line0, line1, line, left, right, line_y;
int chars, text_y_offset, text_y_offset_baseline;
unsigned int c_pos, c_len, c_len_part, b_start, b_end, line_len;
unsigned int sel_start, sel_end;
char *line_text;
struct rect r, s;
bool selected = false;
- plot_font_style_t *fstyle;
+ plot_font_style_t fstyle;
+ int fsize = ta->fstyle.size;
+ int line_height = ta->line_height;
plot_style_t plot_style_fill_bg = {
.stroke_type = PLOT_OP_TYPE_NONE,
.stroke_width = 0,
@@ -1308,10 +1429,17 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
r.x0 = x;
if (r.y0 < y)
r.y0 = y;
- if (r.x1 > x + ta->vis_width)
- r.x1 = x + ta->vis_width;
- if (r.y1 > y + ta->vis_height)
- r.y1 = y + ta->vis_height;
+ if (scale == 1.0) {
+ if (r.x1 > x + ta->vis_width)
+ r.x1 = x + ta->vis_width;
+ if (r.y1 > y + ta->vis_height)
+ r.y1 = y + ta->vis_height;
+ } else {
+ if (r.x1 > x + ta->vis_width * scale)
+ r.x1 = x + ta->vis_width * scale;
+ if (r.y1 > y + ta->vis_height * scale)
+ r.y1 = y + ta->vis_height * scale;
+ }
plot->clip(&r);
if (ta->border_col != NS_TRANSPARENT &&
@@ -1329,16 +1457,32 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
&plot_style_fill_bg);
}
- if (r.x0 < x + ta->border_width)
- r.x0 = x + ta->border_width;
- if (r.x1 > x + ta->vis_width - ta->border_width)
- r.x1 = x + ta->vis_width - ta->border_width;
- if (r.y0 < y + ta->border_width)
- r.y0 = y + ta->border_width;
- if (r.y1 > y + ta->vis_height - ta->border_width -
- (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0))
- r.y1 = y + ta->vis_height - ta->border_width -
- (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0);
+ if (scale == 1.0) {
+ if (r.x0 < x + ta->border_width)
+ r.x0 = x + ta->border_width;
+ if (r.x1 > x + ta->vis_width - ta->border_width)
+ r.x1 = x + ta->vis_width - ta->border_width;
+ if (r.y0 < y + ta->border_width)
+ r.y0 = y + ta->border_width;
+ if (r.y1 > y + ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0))
+ r.y1 = y + ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH :
+ 0);
+ } else {
+ if (r.x0 < x + ta->border_width * scale)
+ r.x0 = x + ta->border_width * scale;
+ if (r.x1 > x + (ta->vis_width - ta->border_width) * scale)
+ r.x1 = x + (ta->vis_width - ta->border_width) * scale;
+ if (r.y0 < y + ta->border_width * scale)
+ r.y0 = y + ta->border_width * scale;
+ if (r.y1 > y + (ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0)) *
+ scale)
+ r.y1 = y + (ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH :
+ 0) * scale);
+ }
if (line0 > 0)
c_pos = utf8_bounded_length(ta->show->data,
@@ -1359,13 +1503,23 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
text_y_offset_baseline += (vis_height * 3 + 2) / 4;
}
+ if (scale != 1.0) {
+ text_y_offset *= scale;
+ text_y_offset_baseline *= scale;
+
+ fsize *= scale;
+ line_height *= scale;
+ }
+
plot_style_fill_bg.fill_colour = ta->sel_fstyle.background;
for (line = line0; (line <= line1) &&
(y + line * ta->line_height <= r.y1 + ta->scroll_y);
line++) {
- if (ta->lines[line].b_length == 0)
+ if (ta->lines[line].b_length == 0) {
+ c_pos++;
continue;
+ }
/* reset clip rectangle */
plot->clip(&r);
@@ -1387,30 +1541,31 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
/* rest of line unselected */
selected = false;
c_len_part = c_len;
- fstyle = &ta->fstyle;
+ fstyle = ta->fstyle;
} else if (sel_start <= c_pos &&
sel_end > c_pos + c_len) {
/* rest of line selected */
selected = true;
c_len_part = c_len;
- fstyle = &ta->sel_fstyle;
+ fstyle = ta->sel_fstyle;
} else if (sel_start > c_pos) {
/* next part of line unselected */
selected = false;
c_len_part = sel_start - c_pos;
- fstyle = &ta->fstyle;
+ fstyle = ta->fstyle;
} else if (sel_end > c_pos) {
/* next part of line selected */
selected = true;
c_len_part = sel_end - c_pos;
- fstyle = &ta->sel_fstyle;
+ fstyle = ta->sel_fstyle;
} else {
assert(0);
}
+ fstyle.size = fsize;
line_text = &(ta->show->data[ta->lines[line].b_start]);
line_len = ta->lines[line].b_length;
@@ -1424,7 +1579,7 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
/* find clip left/right for this part of line */
left = right;
- nsfont.font_width(&ta->fstyle, line_text,
+ nsfont.font_width(&fstyle, line_text,
b_end, &right);
right += x + ta->border_width + ta->pad_left -
ta->scroll_x;
@@ -1435,27 +1590,31 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
s.x0 = left;
if (s.x1 > right)
s.x1 = right;
+
plot->clip(&s);
+ line_y = line * ta->line_height - ta->scroll_y;
+
+ if (scale != 1.0) {
+ line_y *= scale;
+ }
+
if (selected) {
/* draw selection fill */
- plot->rectangle(s.x0,
- y + line * ta->line_height + 1 -
- ta->scroll_y + text_y_offset,
- s.x1,
- y + (line + 1) * ta->line_height + 1 -
- ta->scroll_y + text_y_offset,
+ plot->rectangle(s.x0, y + line_y +
+ text_y_offset,
+ s.x1, y + line_y + line_height +
+ text_y_offset,
&plot_style_fill_bg);
}
/* draw text */
plot->text(x + ta->border_width + ta->pad_left -
ta->scroll_x,
- y + line * ta->line_height +
- text_y_offset_baseline - ta->scroll_y,
+ y + line_y + text_y_offset_baseline,
ta->show->data +
ta->lines[line].b_start,
- ta->lines[line].b_length, fstyle);
+ ta->lines[line].b_length, &fstyle);
c_pos += c_len_part;
c_len -= c_len_part;
@@ -1475,7 +1634,7 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
if ((ta->sel_end == -1 || ta->sel_start == ta->sel_end) &&
ta->caret_pos.char_off >= 0) {
/* There is no selection, and caret visible: show caret */
- int caret_y = y - ta->scroll_y + ta->caret_y + text_y_offset;
+ int caret_y = y - ta->scroll_y + ta->caret_y;
if (ta->flags & TEXTAREA_INTERNAL_CARET) {
/* Render our own caret */
@@ -1483,39 +1642,22 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
x - ta->scroll_x + ta->caret_x,
caret_y + ta->line_height,
&pstyle_stroke_caret);
- } else {
- /* Tell client where caret should be placed */
- msg.ta = ta;
- msg.type = TEXTAREA_MSG_MOVED_CARET;
- msg.data.caret.hidden = false;
- msg.data.caret.x = x - ta->scroll_x + ta->caret_x;
- msg.data.caret.y = caret_y;
- msg.data.caret.height = ta->line_height;
-
- ta->callback(ta->data, &msg);
}
- } else if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
- /* Caret hidden, and client is responsible: tell client */
- msg.ta = ta;
- msg.type = TEXTAREA_MSG_MOVED_CARET;
- msg.data.caret.hidden = true;
-
- ta->callback(ta->data, &msg);
}
if (ta->bar_x != NULL)
scrollbar_redraw(ta->bar_x,
- x + ta->border_width,
- y + ta->vis_height - ta->border_width -
+ x / scale + ta->border_width,
+ y / scale + ta->vis_height - ta->border_width -
SCROLLBAR_WIDTH,
- clip, 1.0, ctx);
+ clip, scale, ctx);
if (ta->bar_y != NULL)
scrollbar_redraw(ta->bar_y,
- x + ta->vis_width - ta->border_width -
+ x / scale + ta->vis_width - ta->border_width -
SCROLLBAR_WIDTH,
- y + ta->border_width,
- clip, 1.0, ctx);
+ y / scale + ta->border_width,
+ clip, scale, ctx);
}
@@ -1594,14 +1736,28 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
redraw = true;
}
break;
+ case KEY_CR:
case KEY_NL:
if (readonly)
break;
- if(!textarea_insert_text(ta, caret, "\n", 1))
- return false;
- caret++;
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+
+ if (ta->sel_start != -1) {
+ if (!textarea_replace_text(ta,
+ ta->sel_start, ta->sel_end,
+ "\n", 1, false))
+ return false;
+
+ caret = ta->sel_start + 1;
+ ta->sel_start = ta->sel_end = -1;
+ redraw = true;
+ } else {
+ if (!textarea_replace_text(ta,
+ caret, caret,
+ "\n", 1, false))
+ return false;
+ caret++;
+ redraw = true;
+ }
break;
case KEY_CUT_LINE:
break;
@@ -1702,38 +1858,37 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start = ta->sel_end = -1;
redraw = true;
}
- if (ta->flags & TEXTAREA_MULTILINE) {
- line--;
- if (line < 0)
- line = 0;
- if (line == ta->caret_pos.line)
- break;
+ if (!(ta->flags & TEXTAREA_MULTILINE))
+ break;
- b_off = ta->lines[line].b_start;
- b_len = ta->lines[line].b_length;
+ line--;
+ if (line < 0)
+ line = 0;
+ if (line == ta->caret_pos.line)
+ break;
- c_line = ta->caret_pos.line;
- c_chars = ta->caret_pos.char_off;
+ b_off = ta->lines[line].b_start;
+ b_len = ta->lines[line].b_length;
- if (ta->text.data[b_off + b_len - 1] == ' '
- && line < ta->line_count - 1)
- b_len--;
+ c_line = ta->caret_pos.line;
+ c_chars = ta->caret_pos.char_off;
- l_len = utf8_bounded_length(
- &(ta->text.data[b_off]),
- b_len);
+ if (b_len > 0 && ta->text.data[b_off + b_len - 1] == ' '
+ && line < ta->line_count - 1)
+ b_len--;
+ l_len = utf8_bounded_length(&(ta->text.data[b_off]),
+ b_len);
- ta->caret_pos.line = line;
- ta->caret_pos.char_off = min(l_len,
- (unsigned)
- ta->caret_pos.char_off);
+ ta->caret_pos.line = line;
+ ta->caret_pos.char_off = min(l_len,
+ (unsigned)ta->caret_pos.char_off);
- caret = textarea_get_caret(ta);
+ caret = textarea_get_caret(ta);
+
+ ta->caret_pos.line = c_line;
+ ta->caret_pos.char_off = c_chars;
- ta->caret_pos.line = c_line;
- ta->caret_pos.char_off = c_chars;
- }
break;
case KEY_PAGE_DOWN:
if (readonly)
@@ -1753,38 +1908,37 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start = ta->sel_end = -1;
redraw = true;
}
- if (ta->flags & TEXTAREA_MULTILINE) {
- line++;
- if (line > ta->line_count - 1)
- line = ta->line_count - 1;
- if (line == ta->caret_pos.line)
- break;
+ if (!(ta->flags & TEXTAREA_MULTILINE))
+ break;
- b_off = ta->lines[line].b_start;
- b_len = ta->lines[line].b_length;
+ line++;
+ if (line > ta->line_count - 1)
+ line = ta->line_count - 1;
+ if (line == ta->caret_pos.line)
+ break;
- c_line = ta->caret_pos.line;
- c_chars = ta->caret_pos.char_off;
+ b_off = ta->lines[line].b_start;
+ b_len = ta->lines[line].b_length;
- if (ta->text.data[b_off + b_len - 1] == ' '
- && line < ta->line_count - 1)
- b_len--;
+ c_line = ta->caret_pos.line;
+ c_chars = ta->caret_pos.char_off;
- l_len = utf8_bounded_length(
- &(ta->text.data[b_off]),
- b_len);
+ if (ta->text.data[b_off + b_len - 1] == ' ' &&
+ line < ta->line_count - 1)
+ b_len--;
+ l_len = utf8_bounded_length(&(ta->text.data[b_off]),
+ b_len);
- ta->caret_pos.line = line;
- ta->caret_pos.char_off = min(l_len,
- (unsigned)
- ta->caret_pos.char_off);
+ ta->caret_pos.line = line;
+ ta->caret_pos.char_off = min(l_len,
+ (unsigned)ta->caret_pos.char_off);
- caret = textarea_get_caret(ta);
+ caret = textarea_get_caret(ta);
+
+ ta->caret_pos.line = c_line;
+ ta->caret_pos.char_off = c_chars;
- ta->caret_pos.line = c_line;
- ta->caret_pos.char_off = c_chars;
- }
break;
case KEY_DELETE_RIGHT:
if (readonly)
@@ -1899,8 +2053,9 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
}
- if (caret != caret_init)
+ if (caret != caret_init || redraw)
textarea_set_caret(ta, caret);
+
//TODO:redraw only the important part
if (redraw) {
msg.ta = ta;
@@ -2006,7 +2161,8 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
return textarea_select_fragment(ta);
}
- } else if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_HOLDING_1)) {
+ } else if (mouse & BROWSER_MOUSE_DRAG_1) {
+ /* Selection start */
textarea_get_xy_offset(ta, x, y, &c_off);
c_start = ta->drag_start_char;
c_end = c_off;
@@ -2019,6 +2175,30 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
ta->callback(ta->data, &msg);
return textarea_select(ta, c_start, c_end);
+ } else if (mouse & BROWSER_MOUSE_HOLDING_1 &&
+ ta->drag_info.type == TEXTAREA_DRAG_SELECTION) {
+ /* Selection track */
+ int scrx = 0;
+ int scry = 0;
+
+ textarea_get_xy_offset(ta, x, y, &c_off);
+ c_start = ta->drag_start_char;
+ c_end = c_off;
+
+ /* selection auto-scroll */
+ if (x < 0)
+ scrx = x / 4;
+ else if (x > ta->vis_width)
+ scrx = (x - ta->vis_width) / 4;
+
+ if (y < 0)
+ scry = y / 4;
+ else if (y > ta->vis_height)
+ scry = (y - ta->vis_height) / 4;
+
+ textarea_scroll(ta, scrx, scry);
+
+ return textarea_select(ta, c_start, c_end);
}
return true;
@@ -2038,18 +2218,37 @@ void textarea_get_dimensions(struct textarea *ta, int *width, int *height)
/* exported interface, documented in textarea.h */
void textarea_set_dimensions(struct textarea *ta, int width, int height)
{
- struct textarea_msg msg;
+ ta->vis_width = width;
+ ta->vis_height = height;
+ textarea_reflow(ta, 0);
+}
+
+/* exported interface, documented in textarea.h */
+void textarea_set_layout(struct textarea *ta, int width, int height,
+ int top, int right, int bottom, int left)
+{
ta->vis_width = width;
ta->vis_height = height;
+ ta->pad_top = top;
+ ta->pad_right = right + ((ta->bar_y == NULL) ? 0 : SCROLLBAR_WIDTH);
+ ta->pad_bottom = bottom + ((ta->bar_x == NULL) ? 0 : SCROLLBAR_WIDTH);
+ ta->pad_left = left;
textarea_reflow(ta, 0);
+}
- msg.ta = ta;
- msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
- msg.data.redraw.x0 = 0;
- msg.data.redraw.y0 = 0;
- msg.data.redraw.x1 = ta->vis_width;
- msg.data.redraw.y1 = ta->vis_height;
- ta->callback(ta->data, &msg);
+/* exported interface, documented in textarea.h */
+bool textarea_scroll(struct textarea *ta, int scrx, int scry)
+{
+ bool handled_scroll = false;
+
+ if (ta->bar_x != NULL && scrx != 0 &&
+ scrollbar_scroll(ta->bar_x, scrx))
+ handled_scroll = true;
+ if (ta->bar_y != NULL && scry != 0 &&
+ scrollbar_scroll(ta->bar_y, scry))
+ handled_scroll = true;
+
+ return handled_scroll;
}
diff --git a/desktop/textarea.h b/desktop/textarea.h
index d8e720bae..d01cd12c6 100644
--- a/desktop/textarea.h
+++ b/desktop/textarea.h
@@ -70,8 +70,6 @@ struct textarea_msg {
};
typedef struct textarea_setup {
- textarea_flags flags; /**< Setup flags */
-
int width; /**< Textarea width */
int height; /**< Textarea height */
@@ -105,7 +103,8 @@ typedef void(*textarea_client_callback)(void *data, struct textarea_msg *msg);
* \param data user specified data which will be passed to callbacks
* \return Opaque handle for textarea or 0 on error
*/
-struct textarea *textarea_create(const textarea_setup *setup,
+struct textarea *textarea_create(const textarea_flags flags,
+ const textarea_setup *setup,
textarea_client_callback callback, void *data);
/**
@@ -125,11 +124,21 @@ void textarea_destroy(struct textarea *ta);
bool textarea_set_text(struct textarea *ta, const char *text);
/**
+ * Insert the text in a text area at the caret, replacing any selection.
+ *
+ * \param ta Text area
+ * \param text UTF-8 text to set text area's contents to
+ * \return true on success, false on memory exhaustion or if ta lacks caret
+ */
+bool textarea_drop_text(struct textarea *ta, const char *text,
+ size_t text_length);
+
+/**
* Extract the text from a text area
*
* \param ta Text area
* \param buf Pointer to buffer to receive data, or NULL
- * to read length required
+ * to read length required (includes trailing '\0')
* \param len Length (bytes) of buffer pointed to by buf, or 0 to read length
* \return Length (bytes) written/required or -1 on error
*/
@@ -160,10 +169,11 @@ int textarea_get_caret(struct textarea *ta);
* \param x x coordinate of textarea top
* \param y y coordinate of textarea left
* \param bg background colour under textarea
+ * \param scale scale to render at
* \param clip clip rectangle
* \param ctx current redraw context
*/
-void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
+void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale,
const struct rect *clip, const struct redraw_context *ctx);
/**
@@ -190,6 +200,7 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
/**
* Gets the dimensions of a textarea
*
+ * \param ta textarea widget
* \param width if not NULL, gets updated to the width of the textarea
* \param height if not NULL, gets updated to the height of the textarea
*/
@@ -197,11 +208,38 @@ void textarea_get_dimensions(struct textarea *ta, int *width, int *height);
/**
* Set the dimensions of a textarea, causing a reflow and
- * emitting a redraw request.
+ * Does not emit a redraw request. Up to client to call textarea_redraw.
*
+ * \param ta textarea widget
* \param width the new width of the textarea
* \param height the new height of the textarea
*/
void textarea_set_dimensions(struct textarea *ta, int width, int height);
+
+/**
+ * Set the dimensions and padding of a textarea, causing a reflow.
+ * Does not emit a redraw request. Up to client to call textarea_redraw.
+ *
+ * \param ta textarea widget
+ * \param width the new width of the textarea
+ * \param height the new height of the textarea
+ * \param top the new top padding of the textarea
+ * \param right the new right padding of the textarea
+ * \param bottom the new bottom padding of the textarea
+ * \param left the new left padding of the textarea
+ */
+void textarea_set_layout(struct textarea *ta, int width, int height,
+ int top, int right, int bottom, int left);
+
+/**
+ * Scroll a textarea by an amount. Only does anything if multi-line textarea
+ * has scrollbars. If it scrolls, it will emit a redraw request.
+ *
+ * \param ta textarea widget
+ * \param scrx number of px try to scroll in x direction
+ * \param scry number of px try to scroll in y direction
+ * \return true iff the textarea was scrolled
+ */
+bool textarea_scroll(struct textarea *ta, int scrx, int scry);
#endif
diff --git a/desktop/textinput.c b/desktop/textinput.c
index b4fda5eef..660708932 100644
--- a/desktop/textinput.c
+++ b/desktop/textinput.c
@@ -127,7 +127,13 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
assert(bw->window != NULL);
- /* safe keys that can be handled whether input claimed or not */
+ if (focus->caret_callback) {
+ /* Pass keypress onto anything that has claimed input focus */
+ return focus->caret_callback(focus, key,
+ focus->caret_p1, focus->caret_p2);
+ }
+
+ /* TODO: pass these to content to deal with */
switch (key) {
case KEY_COPY_SELECTION:
selection_copy_to_clipboard(bw->cur_sel);
@@ -137,6 +143,10 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
selection_clear(bw->cur_sel, true);
return true;
+ case KEY_SELECT_ALL:
+ selection_select_all(bw->cur_sel);
+ return true;
+
case KEY_ESCAPE:
if (bw->cur_sel && selection_defined(bw->cur_sel)) {
selection_clear(bw->cur_sel, true);
@@ -147,19 +157,6 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
return false;
}
- if (focus->caret_callback) {
- /* Pass keypress onto anything that has claimed input focus */
- return focus->caret_callback(focus, key,
- focus->caret_p1, focus->caret_p2);
- }
-
- /* keys we can't handle here if cursor is in form */
- switch (key) {
- case KEY_SELECT_ALL:
- selection_select_all(bw->cur_sel);
- return true;
- }
-
return false;
}
diff --git a/desktop/tree.c b/desktop/tree.c
index 959b9870a..af64be83b 100644
--- a/desktop/tree.c
+++ b/desktop/tree.c
@@ -2079,7 +2079,7 @@ void tree_draw(struct tree *tree, int x, int y,
textarea_redraw(tree->textarea, x, y,
plot_style_fill_tree_background.
fill_colour,
- &clip, &new_ctx);
+ 1.0, &clip, &new_ctx);
}
}
@@ -2946,6 +2946,7 @@ void tree_start_edit(struct tree *tree, struct node_element *element)
struct node *parent;
int width, height;
textarea_setup ta_setup;
+ textarea_flags ta_flags;
assert(tree != NULL);
assert(element != NULL);
@@ -2972,7 +2973,8 @@ void tree_start_edit(struct tree *tree, struct node_element *element)
tree->ta_height = height;
- ta_setup.flags = TEXTAREA_INTERNAL_CARET;
+ ta_flags = TEXTAREA_INTERNAL_CARET;
+
ta_setup.width = width;
ta_setup.height = tree->ta_height;
ta_setup.pad_top = 0;
@@ -2987,7 +2989,7 @@ void tree_start_edit(struct tree *tree, struct node_element *element)
ta_setup.text.foreground = 0x000000;
ta_setup.text.background = 0xffffff;
- tree->textarea = textarea_create(&ta_setup,
+ tree->textarea = textarea_create(ta_flags, &ta_setup,
tree_textarea_callback, tree);
if (tree->textarea == NULL) {
tree_stop_edit(tree, false);
diff --git a/render/box_construct.c b/render/box_construct.c
index 52c8cfee5..e5800edf8 100644
--- a/render/box_construct.c
+++ b/render/box_construct.c
@@ -38,6 +38,7 @@
#include "css/select.h"
#include "desktop/options.h"
#include "render/box.h"
+#include "render/box_textarea.h"
#include "render/form.h"
#include "render/html_internal.h"
#include "utils/corestrings.h"
@@ -111,7 +112,6 @@ static bool box_image(BOX_SPECIAL_PARAMS);
static bool box_textarea(BOX_SPECIAL_PARAMS);
static bool box_select(BOX_SPECIAL_PARAMS);
static bool box_input(BOX_SPECIAL_PARAMS);
-static bool box_input_text(BOX_SPECIAL_PARAMS, bool password);
static bool box_button(BOX_SPECIAL_PARAMS);
static bool box_frameset(BOX_SPECIAL_PARAMS);
static bool box_create_frameset(struct content_html_frames *f, dom_node *n,
@@ -2497,6 +2497,38 @@ bool box_iframe(BOX_SPECIAL_PARAMS)
/**
+ * Helper function for adding textarea widget to box.
+ *
+ * This is a load of hacks to ensure boxes replaced with textareas
+ * can be handled by the layout code.
+ */
+
+static bool box_input_text(html_content *html, struct box *box,
+ struct dom_node *node)
+{
+ struct box *inline_container, *inline_box;
+
+ box->type = BOX_INLINE_BLOCK;
+
+ inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, html->bctx);
+ if (!inline_container)
+ return false;
+ inline_container->type = BOX_INLINE_CONTAINER;
+ inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0,
+ html->bctx);
+ if (!inline_box)
+ return false;
+ inline_box->type = BOX_TEXT;
+ inline_box->text = talloc_strdup(html->bctx, "");
+
+ box_add_child(inline_container, inline_box);
+ box_add_child(box, inline_container);
+
+ return box_textarea_create_textarea(html, box, node);
+}
+
+
+/**
* Form control [17.4].
*/
@@ -2518,7 +2550,7 @@ bool box_input(BOX_SPECIAL_PARAMS)
if (type && dom_string_caseless_lwc_isequal(type,
corestring_lwc_password)) {
- if (box_input_text(n, content, box, 0, true) == false)
+ if (box_input_text(content, box, n) == false)
goto no_memory;
} else if (type && dom_string_caseless_lwc_isequal(type,
@@ -2620,7 +2652,7 @@ bool box_input(BOX_SPECIAL_PARAMS)
}
} else {
/* the default type is "text" */
- if (box_input_text(n, content, box, 0, false) == false)
+ if (box_input_text(content, box, n) == false)
goto no_memory;
}
@@ -2639,52 +2671,6 @@ no_memory:
/**
- * Helper function for box_input().
- */
-
-bool box_input_text(BOX_SPECIAL_PARAMS, bool password)
-{
- struct box *inline_container, *inline_box;
-
- box->type = BOX_INLINE_BLOCK;
-
- inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, content->bctx);
- if (!inline_container)
- return false;
- inline_container->type = BOX_INLINE_CONTAINER;
- inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0,
- content->bctx);
- if (!inline_box)
- return false;
- inline_box->type = BOX_TEXT;
- if (password) {
- inline_box->length = strlen(box->gadget->value);
- inline_box->text = talloc_array(content->bctx, char,
- inline_box->length + 1);
- if (!inline_box->text)
- return false;
- memset(inline_box->text, '*', inline_box->length);
- inline_box->text[inline_box->length] = '\0';
- } else {
- /* replace spaces/TABs with hard spaces to prevent line
- * wrapping */
- char *text = cnv_space2nbsp(box->gadget->value);
- if (!text)
- return false;
- inline_box->text = talloc_strdup(content->bctx, text);
- free(text);
- if (!inline_box->text)
- return false;
- inline_box->length = strlen(inline_box->text);
- }
- box_add_child(inline_container, inline_box);
- box_add_child(box, inline_container);
-
- return true;
-}
-
-
-/**
* Push button [17.5].
*/
@@ -2924,90 +2910,16 @@ no_memory:
bool box_textarea(BOX_SPECIAL_PARAMS)
{
- /* A textarea is an INLINE_BLOCK containing a single INLINE_CONTAINER,
- * which contains the text as runs of TEXT separated by BR. There is
- * at least one TEXT. The first and last boxes are TEXT.
- * Consecutive BR may not be present. These constraints are satisfied
- * by using a 0-length TEXT for blank lines. */
-
- const char *current;
- dom_string *area_data = NULL;
- dom_exception err;
- struct box *inline_container, *inline_box, *br_box;
- char *s;
- size_t len;
-
- box->type = BOX_INLINE_BLOCK;
+ /* Get the form_control for the DOM node */
box->gadget = html_forms_get_control_for_node(content->forms, n);
if (box->gadget == NULL)
return false;
- box->gadget->box = box;
- inline_container = box_create(NULL, 0, false, 0, 0, box->title, 0,
- content->bctx);
- if (inline_container == NULL)
- return false;
- inline_container->type = BOX_INLINE_CONTAINER;
- box_add_child(box, inline_container);
+ box->gadget->box = box;
- err = dom_node_get_text_content(n, &area_data);
- if (err != DOM_NO_ERR)
+ if (!box_input_text(content, box, n))
return false;
- if (area_data != NULL) {
- current = dom_string_data(area_data);
- } else {
- /* No content, or failed reading it: use a blank string */
- current = "";
- }
-
- while (true) {
- /* BOX_TEXT */
- len = strcspn(current, "\r\n");
- s = talloc_strndup(content->bctx, current, len);
- if (s == NULL) {
- if (area_data != NULL)
- dom_string_unref(area_data);
- return false;
- }
-
- inline_box = box_create(NULL, box->style, false, 0, 0,
- box->title, 0, content->bctx);
- if (inline_box == NULL) {
- if (area_data != NULL)
- dom_string_unref(area_data);
- return false;
- }
- inline_box->type = BOX_TEXT;
- inline_box->text = s;
- inline_box->length = len;
- box_add_child(inline_container, inline_box);
-
- current += len;
- if (current[0] == 0)
- /* finished */
- break;
-
- /* BOX_BR */
- br_box = box_create(NULL, box->style, false, 0, 0, box->title,
- 0, content->bctx);
- if (br_box == NULL) {
- if (area_data != NULL)
- dom_string_unref(area_data);
- return false;
- }
- br_box->type = BOX_BR;
- box_add_child(inline_container, br_box);
-
- if (current[0] == '\r' && current[1] == '\n')
- current += 2;
- else
- current++;
- }
-
- if (area_data != NULL)
- dom_string_unref(area_data);
-
*convert_children = false;
return true;
}
diff --git a/render/box_textarea.c b/render/box_textarea.c
new file mode 100644
index 000000000..7f38ade5e
--- /dev/null
+++ b/render/box_textarea.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Box tree treeview box replacement (implementation).
+ */
+
+#include <dom/dom.h>
+
+#include "desktop/browser.h"
+#include "desktop/textarea.h"
+#include "desktop/textinput.h"
+#include "render/box_textarea.h"
+#include "render/font.h"
+#include "render/form.h"
+#include "utils/log.h"
+
+
+static bool box_textarea_browser_caret_callback(struct browser_window *bw,
+ uint32_t key, void *p1, void *p2)
+{
+ struct box *box = p1;
+ struct form_control *gadget = box->gadget;
+ struct textarea *ta = gadget->data.text.ta;
+ struct form* form = box->gadget->form;
+ html_content *html = p2;
+ struct content *c = (struct content *) html;
+
+ assert(ta != NULL);
+
+ if (gadget->type != GADGET_TEXTAREA) {
+ switch (key) {
+ case KEY_NL:
+ case KEY_CR:
+ if (form)
+ form_submit(content_get_url(c), html->bw,
+ form, 0);
+ return true;
+
+ case KEY_TAB:
+ {
+ struct form_control *next_input;
+ /* Find next text entry field that is actually
+ * displayed (i.e. has an associated box) */
+ for (next_input = gadget->next;
+ next_input &&
+ ((next_input->type != GADGET_TEXTBOX &&
+ next_input->type != GADGET_TEXTAREA &&
+ next_input->type != GADGET_PASSWORD) ||
+ !next_input->box);
+ next_input = next_input->next)
+ ;
+ if (!next_input)
+ return true;
+
+ textarea_set_caret(ta, -1);
+ textarea_set_caret(next_input->data.text.ta, 0);
+ }
+ return true;
+
+ case KEY_SHIFT_TAB:
+ {
+ struct form_control *prev_input;
+ /* Find previous text entry field that is actually
+ * displayed (i.e. has an associated box) */
+ for (prev_input = gadget->prev;
+ prev_input &&
+ ((prev_input->type != GADGET_TEXTBOX &&
+ prev_input->type != GADGET_TEXTAREA &&
+ prev_input->type != GADGET_PASSWORD) ||
+ !prev_input->box);
+ prev_input = prev_input->prev)
+ ;
+ if (!prev_input)
+ return true;
+
+ textarea_set_caret(ta, -1);
+ textarea_set_caret(prev_input->data.text.ta, 0);
+ }
+ return true;
+
+ default:
+ /* Pass to textarea widget */
+ break;
+ }
+ }
+
+ return textarea_keypress(ta, key);
+}
+
+
+static void box_textarea_browser_move_callback(struct browser_window *bw,
+ void *p1, void *p2)
+{
+}
+
+
+static bool box_textarea_browser_paste_callback(struct browser_window *bw,
+ const char *utf8, unsigned utf8_len, bool last,
+ void *p1, void *p2)
+{
+ printf("AWWOOOOOGA!\n");
+ return true;
+}
+
+
+/**
+ * Callback for html form textareas.
+ */
+static void box_textarea_callback(void *data, struct textarea_msg *msg)
+{
+ struct form_textarea_data *d = data;
+ struct html_content *html = d->html;
+ struct form_control *gadget = d->gadget;
+ struct box *box = gadget->box;
+
+ switch (msg->type) {
+ case TEXTAREA_MSG_DRAG_REPORT:
+ if (msg->data.drag == TEXTAREA_DRAG_NONE) {
+ /* Textarea drag finished */
+ html_drag_type drag_type = HTML_DRAG_NONE;
+ union html_drag_owner drag_owner;
+ drag_owner.no_owner = true;
+
+ html_set_drag_type(d->html, drag_type, drag_owner,
+ NULL);
+ } else {
+ /* Textarea drag started */
+ struct rect rect = {
+ .x0 = INT_MIN,
+ .y0 = INT_MIN,
+ .x1 = INT_MAX,
+ .y1 = INT_MAX
+ };
+ html_drag_type drag_type;
+ union html_drag_owner drag_owner;
+ drag_owner.textarea = box;
+
+ switch (msg->data.drag) {
+ case TEXTAREA_DRAG_SCROLLBAR:
+ drag_type = HTML_DRAG_TEXTAREA_SCROLLBAR;
+ break;
+ case TEXTAREA_DRAG_SELECTION:
+ drag_type = HTML_DRAG_TEXTAREA_SELECTION;
+ break;
+ default:
+ LOG(("Drag type not handled."));
+ assert(0);
+ break;
+ }
+
+ html_set_drag_type(d->html, drag_type, drag_owner,
+ &rect);
+ }
+ break;
+
+ case TEXTAREA_MSG_REDRAW_REQUEST:
+ /* Redraw the textarea */
+ /* TODO: don't redraw whole box, just the part asked for */
+ html__redraw_a_box(html, box);
+ break;
+
+ case TEXTAREA_MSG_MOVED_CARET:
+ if (html->bw == NULL)
+ break;
+
+ if (msg->data.caret.hidden) {
+ browser_window_remove_caret(html->bw);
+ } else {
+ int x, y;
+ box_coords(box, &x, &y);
+ browser_window_place_caret(html->bw,
+ x + msg->data.caret.x,
+ y + msg->data.caret.y,
+ msg->data.caret.height,
+ box_textarea_browser_caret_callback,
+ box_textarea_browser_paste_callback,
+ box_textarea_browser_move_callback,
+ box, html);
+ }
+ break;
+ }
+}
+
+
+/* Exported interface, documented in box_textarea.h */
+bool box_textarea_create_textarea(html_content *html,
+ struct box *box, struct dom_node *node)
+{
+ dom_string *dom_text = NULL;
+ dom_exception err;
+ textarea_setup ta_setup;
+ textarea_flags ta_flags;
+ plot_font_style_t fstyle;
+ struct form_control *gadget = box->gadget;
+ const char *text;
+
+ /** TODO: Read only textarea */
+
+ assert(gadget != NULL);
+ assert(gadget->type == GADGET_TEXTAREA ||
+ gadget->type == GADGET_TEXTBOX ||
+ gadget->type == GADGET_PASSWORD);
+
+ if (gadget->type == GADGET_TEXTAREA) {
+ ta_flags = TEXTAREA_MULTILINE;
+
+ /* Get the textarea's initial content */
+ err = dom_node_get_text_content(node, &dom_text);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ } else {
+ dom_html_input_element *input = (dom_html_input_element *) node;
+
+ if (gadget->type == GADGET_PASSWORD)
+ ta_flags = TEXTAREA_PASSWORD;
+ else
+ ta_flags = TEXTAREA_DEFAULT;
+
+ /* Get initial text */
+ err = dom_html_input_element_get_value(input, &dom_text);
+ if (err != DOM_NO_ERR)
+ return false;
+ }
+
+ if (dom_text != NULL) {
+ text = dom_string_data(dom_text);
+ } else {
+ /* No initial text, or failed reading it;
+ * use a blank string */
+ text = "";
+ }
+
+ gadget->data.text.data.html = html;
+ gadget->data.text.data.gadget = gadget;
+
+ font_plot_style_from_css(gadget->box->style, &fstyle);
+
+ /* Reset to correct values by layout */
+ ta_setup.width = 200;
+ ta_setup.height = 20;
+ ta_setup.pad_top = 4;
+ ta_setup.pad_right = 4;
+ ta_setup.pad_bottom = 4;
+ ta_setup.pad_left = 4;
+
+ /* Set remaining data */
+ ta_setup.border_width = 0;
+ ta_setup.border_col = 0x000000;
+ ta_setup.text = fstyle;
+ ta_setup.text.background = NS_TRANSPARENT;
+ /* Make selected text either black or white, as gives greatest contrast
+ * with background colour. (Calc lightness of background colour and
+ * choose the one the lightness is furthest from.) */
+ ta_setup.selected_text =
+ (((((fstyle.foreground & 0x0000ff) ) * 19) / 64 +
+ (((fstyle.foreground & 0x00ff00) >> 8) * 38) / 64 +
+ (((fstyle.foreground & 0xff0000) >> 16) * 7) / 64) >
+ (0xff / 2)) ? 0x000000 : 0xffffff;
+ ta_setup.selected_bg = fstyle.foreground;
+
+ /* Hand reference to dom text over to gadget */
+ gadget->data.text.initial = dom_text;
+
+ gadget->data.text.ta = textarea_create(ta_flags, &ta_setup,
+ box_textarea_callback, &gadget->data.text.data);
+
+ if (gadget->data.text.ta == NULL) {
+ return false;
+ }
+
+ if (!textarea_set_text(gadget->data.text.ta, text))
+ return false;
+
+ return true;
+}
+
diff --git a/render/textinput.h b/render/box_textarea.h
index 5aa747f75..30414e816 100644
--- a/render/textinput.h
+++ b/render/box_textarea.h
@@ -1,8 +1,5 @@
/*
- * 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 <joty@netsurf-browser.org>
+ * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -20,22 +17,28 @@
*/
/** \file
- * HTML form text input handling (interface)
+ * Box tree treeview box replacement (interface).
*/
-#ifndef _NETSURF_RENDER_TEXTINPUT_H_
-#define _NETSURF_RENDER_TEXTINPUT_H_
-#include <stdbool.h>
-struct box;
-struct content;
+#ifndef _NETSURF_RENDER_BOX_TEXTAREA_H_
+#define _NETSURF_RENDER_BOX_TEXTAREA_H_
-void textinput_textarea_click(struct content *c, browser_mouse_state mouse,
- struct box *textarea, int box_x, int box_y, int x, int y);
+#include "render/box.h"
+#include "render/html_internal.h"
-void textinput_input_click(struct content *c, struct box *input,
- int box_x, int box_y, int x, int y);
+struct dom_node;
+
+/**
+ * Create textarea widget for a form element
+ *
+ * \param html html content object
+ * \param box box with gadget to be given textarea widget
+ * \param node DOM node for form element
+ */
+bool box_textarea_create_textarea(html_content *html,
+ struct box *box, struct dom_node *node);
#endif
diff --git a/render/form.c b/render/form.c
index 42e76e1f2..c2819b479 100644
--- a/render/form.c
+++ b/render/form.c
@@ -43,6 +43,7 @@
#include "desktop/plot_style.h"
#include "desktop/plotters.h"
#include "desktop/scrollbar.h"
+#include "desktop/textarea.h"
#include "render/box.h"
#include "render/font.h"
#include "render/form.h"
@@ -85,7 +86,6 @@ static plot_font_style_t plot_fstyle_entry = {
.foreground = 0x000000,
};
-static char *form_textarea_value(struct form_control *textarea);
static char *form_acceptable_charset(struct form *form);
static char *form_encode_item(const char *item, const char *charset,
const char *fallback);
@@ -252,6 +252,17 @@ void form_free_control(struct form_control *control)
form_free_select_menu(control);
}
+ if (control->type == GADGET_TEXTAREA ||
+ control->type == GADGET_TEXTBOX ||
+ control->type == GADGET_PASSWORD) {
+
+ if (control->data.text.initial != NULL)
+ dom_string_unref(control->data.text.initial);
+
+ if (control->data.text.ta != NULL)
+ textarea_destroy(control->data.text.ta);
+ }
+
free(control);
}
@@ -350,8 +361,6 @@ bool form_successful_controls(struct form *form,
switch (control->type) {
case GADGET_HIDDEN:
- case GADGET_TEXTBOX:
- case GADGET_PASSWORD:
if (control->value)
value = ENCODE_ITEM(control->value);
else
@@ -416,17 +425,26 @@ bool form_successful_controls(struct form *form,
continue;
break;
+ case GADGET_TEXTBOX:
+ case GADGET_PASSWORD:
case GADGET_TEXTAREA:
- {
+ {
char *v2;
+ int ta_len = textarea_get_text(
+ control->data.text.ta,
+ NULL, 0);
- /* textarea */
- value = form_textarea_value(control);
+ value = malloc(ta_len);
if (!value) {
LOG(("failed handling textarea"));
goto no_memory;
}
- if (value[0] == 0) {
+ textarea_get_text(control->data.text.ta,
+ value, ta_len);
+
+ if (control->type == GADGET_TEXTAREA &&
+ value[0] == '\0') {
+ /* Textarea not submitted if empty */
free(value);
continue;
}
@@ -440,7 +458,7 @@ bool form_successful_controls(struct form *form,
free(value);
value = v2;
- }
+ }
break;
case GADGET_IMAGE: {
@@ -617,59 +635,6 @@ no_memory:
/**
- * Find the value for a textarea control.
- *
- * \param textarea control of type GADGET_TEXTAREA
- * \return the value as a UTF-8 string on heap, or 0 on memory exhaustion
- */
-char *form_textarea_value(struct form_control *textarea)
-{
- unsigned int len = 0;
- char *value, *s;
- struct box *text_box;
-
- /* Textarea may have no associated box if styled with display: none */
- if (textarea->box == NULL) {
- /* Return the empty string: caller treats this as a
- * non-successful control. */
- return strdup("");
- }
-
- /* find required length */
- for (text_box = textarea->box->children->children; text_box;
- text_box = text_box->next) {
- if (text_box->type == BOX_TEXT)
- len += text_box->length + 1;
- else /* BOX_BR */
- len += 2;
- }
-
- /* construct value */
- s = value = malloc(len + 1);
- if (!s)
- return NULL;
-
- for (text_box = textarea->box->children->children; text_box;
- text_box = text_box->next) {
- if (text_box->type == BOX_TEXT) {
- strncpy(s, text_box->text, text_box->length);
- s += text_box->length;
- if (text_box->next && text_box->next->type != BOX_BR)
- /* only add space if this isn't
- * the last box on a line (or in the area) */
- *s++ = ' ';
- } else { /* BOX_BR */
- *s++ = '\r';
- *s++ = '\n';
- }
- }
- *s = 0;
-
- return value;
-}
-
-
-/**
* Encode controls using application/x-www-form-urlencoded.
*
* \param form form to which successful controls relate
diff --git a/render/form.h b/render/form.h
index 67372d5d5..b5f6a7e2c 100644
--- a/render/form.h
+++ b/render/form.h
@@ -34,6 +34,7 @@ struct form_control;
struct form_option;
struct form_select_menu;
struct html_content;
+struct dom_string;
/** Form submit method. */
typedef enum {
@@ -73,6 +74,12 @@ typedef enum {
GADGET_BUTTON
} form_control_type;
+/** Data for textarea */
+struct form_textarea_data {
+ struct html_content *html;
+ struct form_control *gadget;
+};
+
/** Form control. */
struct form_control {
void *node; /**< Corresponding DOM node */
@@ -111,6 +118,11 @@ struct form_control {
struct form_option *current;
struct form_select_menu *menu;
} select;
+ struct {
+ struct textarea *ta;
+ struct dom_string *initial;
+ struct form_textarea_data data;
+ } text; /**< input type=text or textarea */
} data;
struct form_control *prev; /**< Previous control in this form */
diff --git a/render/html.c b/render/html.c
index 3e26928fd..4fc152a84 100644
--- a/render/html.c
+++ b/render/html.c
@@ -35,6 +35,7 @@
#include "desktop/options.h"
#include "desktop/selection.h"
#include "desktop/scrollbar.h"
+#include "desktop/textarea.h"
#include "image/bitmap.h"
#include "render/box.h"
#include "render/font.h"
@@ -343,7 +344,8 @@ html_create_html_data(html_content *c, const http_parameter *params)
c->iframe = NULL;
c->page = NULL;
c->font_func = &nsfont;
- c->scrollbar = NULL;
+ c->drag_type = HTML_DRAG_NONE;
+ c->drag_owner.no_owner = true;
c->scripts_count = 0;
c->scripts = NULL;
c->jscontext = NULL;
@@ -1307,6 +1309,29 @@ html_object_callback(hlcache_handle *object,
content_broadcast(&c->base, event->type, event->data);
break;
+ case CONTENT_MSG_DRAG:
+ {
+ html_drag_type drag_type = HTML_DRAG_NONE;
+ union html_drag_owner drag_owner;
+ drag_owner.content = box;
+
+ switch (event->data.drag.type) {
+ case CONTENT_DRAG_NONE:
+ drag_type = HTML_DRAG_NONE;
+ drag_owner.no_owner = true;
+ break;
+ case CONTENT_DRAG_SCROLL:
+ drag_type = HTML_DRAG_CONTENT_SCROLL;
+ break;
+ case CONTENT_DRAG_SELECTION:
+ drag_type = HTML_DRAG_CONTENT_SELECTION;
+ break;
+ }
+ html_set_drag_type(c, drag_type, drag_owner,
+ event->data.drag.rect);
+ }
+ break;
+
default:
assert(0);
}
@@ -2629,8 +2654,8 @@ html_get_contextual_content(struct content *c,
* \param c html content to look inside
* \param x x-coordinate of point of interest
* \param y y-coordinate of point of interest
- * \param scrx x-coordinate of point of interest
- * \param scry y-coordinate of point of interest
+ * \param scrx number of px try to scroll something in x direction
+ * \param scry number of px try to scroll something in y direction
* \return true iff scroll was consumed by something in the content
*/
static bool
@@ -2657,6 +2682,14 @@ html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry)
x - box_x, y - box_y, scrx, scry) == true)
return true;
+ /* Pass into textarea widget */
+ if (box->gadget && (box->gadget->type == GADGET_TEXTAREA ||
+ box->gadget->type == GADGET_PASSWORD ||
+ box->gadget->type == GADGET_TEXTBOX) &&
+ textarea_scroll(box->gadget->data.text.ta,
+ scrx, scry) == true)
+ return true;
+
/* Pass into object */
if (box->object != NULL && content_scroll_at_point(
box->object, x - box_x, y - box_y,
@@ -2763,7 +2796,7 @@ static bool html_drop_file_at_point(struct content *c, int x, int y, char *file)
/* Redraw box. */
html__redraw_a_box(html, file_box);
- } else if (html->bw != NULL) {
+ } else {
/* File dropped on text input */
size_t file_len;
@@ -2772,7 +2805,7 @@ static bool html_drop_file_at_point(struct content *c, int x, int y, char *file)
char *utf8_buff;
utf8_convert_ret ret;
unsigned int size;
- struct browser_window *bw;
+ int bx, by;
/* Open file */
fp = fopen(file, "rb");
@@ -2828,13 +2861,13 @@ static bool html_drop_file_at_point(struct content *c, int x, int y, char *file)
size = strlen(utf8_buff);
/* Simulate a click over the input box, to place caret */
- browser_window_mouse_click(html->bw,
- BROWSER_MOUSE_PRESS_1, x, y);
-
- bw = browser_window_get_root(html->bw);
+ box_coords(text_box, &bx, &by);
+ textarea_mouse_action(text_box->gadget->data.text.ta,
+ BROWSER_MOUSE_PRESS_1, x - bx, y - by);
/* Paste the file as text */
- browser_window_paste_text(bw, utf8_buff, size, true);
+ textarea_drop_text(text_box->gadget->data.text.ta,
+ utf8_buff, size);
free(utf8_buff);
}
diff --git a/render/html.h b/render/html.h
index e11fc76ac..a9f7967f6 100644
--- a/render/html.h
+++ b/render/html.h
@@ -46,6 +46,7 @@ struct http_parameter;
struct imagemap;
struct object_params;
struct plotters;
+struct textarea;
struct scrollbar;
struct scrollbar_msg_data;
struct search_context;
diff --git a/render/html_forms.c b/render/html_forms.c
index d1223819a..c80edc6bb 100644
--- a/render/html_forms.c
+++ b/render/html_forms.c
@@ -523,7 +523,8 @@ invent_fake_gadget(dom_node *node)
}
/* documented in html_internal.h */
-struct form_control *html_forms_get_control_for_node(struct form *forms, dom_node *node)
+struct form_control *html_forms_get_control_for_node(struct form *forms,
+ dom_node *node)
{
struct form *f;
struct form_control *ctl = NULL;
diff --git a/render/html_interaction.c b/render/html_interaction.c
index d22869edc..dfebc2577 100644
--- a/render/html_interaction.c
+++ b/render/html_interaction.c
@@ -35,13 +35,13 @@
#include "desktop/options.h"
#include "desktop/scrollbar.h"
#include "desktop/selection.h"
+#include "desktop/textarea.h"
#include "desktop/textinput.h"
#include "render/box.h"
#include "render/font.h"
#include "render/form.h"
#include "render/html_internal.h"
#include "render/imagemap.h"
-#include "render/textinput.h"
#include "javascript/js.h"
#include "utils/messages.h"
#include "utils/utils.h"
@@ -224,9 +224,10 @@ void html_mouse_track(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y)
{
html_content *html = (html_content*) c;
- browser_drag_type drag_type = browser_window_get_drag_type(bw);
+ union html_drag_owner drag_owner;
- if (drag_type == DRAGGING_SELECTION && !mouse) {
+ if (html->drag_type == HTML_DRAG_SELECTION && !mouse) {
+ /* End of selection drag */
int dir = -1;
size_t idx;
@@ -238,40 +239,37 @@ void html_mouse_track(struct content *c, struct browser_window *bw,
if (idx != 0)
selection_track(&html->sel, mouse, idx);
- browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);
+ drag_owner.no_owner = true;
+ html_set_drag_type(html, HTML_DRAG_NONE, drag_owner, NULL);
}
- switch (drag_type) {
- case DRAGGING_SELECTION: {
- struct box *box;
- int dir = -1;
- int dx, dy;
+ if (html->drag_type == HTML_DRAG_SELECTION) {
+ /* Selection drag */
+ struct box *box;
+ int dir = -1;
+ int dx, dy;
- if (selection_dragging_start(&html->sel))
- dir = 1;
+ if (selection_dragging_start(&html->sel))
+ dir = 1;
- box = box_pick_text_box(html, x, y, dir, &dx, &dy);
+ box = box_pick_text_box(html, x, y, dir, &dx, &dy);
- if (box) {
- int pixel_offset;
- size_t idx;
- plot_font_style_t fstyle;
+ if (box != NULL) {
+ int pixel_offset;
+ size_t idx;
+ plot_font_style_t fstyle;
- font_plot_style_from_css(box->style, &fstyle);
+ font_plot_style_from_css(box->style, &fstyle);
- nsfont.font_position_in_string(&fstyle,
- box->text, box->length,
- dx, &idx, &pixel_offset);
+ nsfont.font_position_in_string(&fstyle,
+ box->text, box->length,
+ dx, &idx, &pixel_offset);
- selection_track(&html->sel, mouse,
- box->byte_offset + idx);
- }
+ selection_track(&html->sel, mouse,
+ box->byte_offset + idx);
}
- break;
-
- default:
- html_mouse_action(c, bw, mouse, x, y);
- break;
+ } else {
+ html_mouse_action(c, bw, mouse, x, y);
}
}
@@ -356,29 +354,30 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
return;
}
- if (!mouse && html->scrollbar != NULL) {
- /* drag end: scrollbar */
- html_overflow_scroll_drag_end(html->scrollbar, mouse, x, y);
- }
+ if (html->drag_type == HTML_DRAG_SCROLLBAR) {
+ struct scrollbar *scr = html->drag_owner.scrollbar;
+ struct html_scrollbar_data *data = scrollbar_get_data(scr);
+
+ if (!mouse) {
+ /* drag end: scrollbar */
+ html_overflow_scroll_drag_end(scr, mouse, x, y);
+ }
- if (html->scrollbar != NULL) {
- struct html_scrollbar_data *data =
- scrollbar_get_data(html->scrollbar);
box = data->box;
box_coords(box, &box_x, &box_y);
- if (scrollbar_is_horizontal(html->scrollbar)) {
+ if (scrollbar_is_horizontal(scr)) {
scroll_mouse_x = x - box_x ;
scroll_mouse_y = y - (box_y + box->padding[TOP] +
box->height + box->padding[BOTTOM] -
SCROLLBAR_WIDTH);
- status = scrollbar_mouse_action(html->scrollbar, mouse,
+ status = scrollbar_mouse_action(scr, mouse,
scroll_mouse_x, scroll_mouse_y);
} else {
scroll_mouse_x = x - (box_x + box->padding[LEFT] +
box->width + box->padding[RIGHT] -
SCROLLBAR_WIDTH);
scroll_mouse_y = y - box_y;
- status = scrollbar_mouse_action(html->scrollbar, mouse,
+ status = scrollbar_mouse_action(scr, mouse,
scroll_mouse_x, scroll_mouse_y);
}
@@ -387,8 +386,35 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
return;
}
+ if (html->drag_type == HTML_DRAG_TEXTAREA_SELECTION ||
+ html->drag_type == HTML_DRAG_TEXTAREA_SCROLLBAR) {
+ box = html->drag_owner.textarea;
+ assert(box->gadget != NULL);
+ assert(box->gadget->type == GADGET_TEXTAREA ||
+ box->gadget->type == GADGET_PASSWORD ||
+ box->gadget->type == GADGET_TEXTBOX);
+
+ box_coords(box, &box_x, &box_y);
+ textarea_mouse_action(box->gadget->data.text.ta, mouse,
+ x - box_x, y - box_y);
+
+ /* TODO: Set appropriate statusbar message */
+ return;
+ }
+
+ if (html->drag_type == HTML_DRAG_CONTENT_SELECTION ||
+ html->drag_type == HTML_DRAG_CONTENT_SCROLL) {
+ box = html->drag_owner.content;
+ assert(box->object != NULL);
+
+ box_coords(box, &box_x, &box_y);
+ content_mouse_track(box->object, bw, mouse,
+ x - box_x, y - box_y);
+ return;
+ }
+
/* Content related drags handled by now */
- browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);
+ assert(html->drag_type == HTML_DRAG_NONE);
/* search the box tree for a link, imagemap, form control, or
* box with scrollbars
@@ -592,94 +618,18 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
status = messages_get("FormBadSubmit");
}
break;
- case GADGET_TEXTAREA:
- status = messages_get("FormTextarea");
- pointer = get_pointer_shape(gadget_box, false);
-
- if (mouse & (BROWSER_MOUSE_PRESS_1 |
- BROWSER_MOUSE_PRESS_2)) {
- if (text_box && selection_root(&html->sel) !=
- gadget_box)
- selection_init(&html->sel, gadget_box);
-
- textinput_textarea_click(c, mouse,
- gadget_box,
- gadget_box_x,
- gadget_box_y,
- x - gadget_box_x,
- y - gadget_box_y);
- }
-
- if (text_box) {
- int pixel_offset;
- size_t idx;
-
- font_plot_style_from_css(text_box->style,
- &fstyle);
-
- nsfont.font_position_in_string(&fstyle,
- text_box->text,
- text_box->length,
- x - gadget_box_x - text_box->x,
- &idx,
- &pixel_offset);
-
- selection_click(&html->sel, mouse,
- text_box->byte_offset + idx);
-
- if (selection_dragging(&html->sel)) {
- browser_window_set_drag_type(bw,
- DRAGGING_SELECTION,
- NULL);
- status = messages_get("Selecting");
- }
- }
- else if (mouse & BROWSER_MOUSE_PRESS_1)
- selection_clear(&html->sel, true);
- break;
case GADGET_TEXTBOX:
case GADGET_PASSWORD:
- status = messages_get("FormTextbox");
- pointer = get_pointer_shape(gadget_box, false);
-
- if ((mouse & BROWSER_MOUSE_PRESS_1) &&
- !(mouse & (BROWSER_MOUSE_MOD_1 |
- BROWSER_MOUSE_MOD_2))) {
- textinput_input_click(c,
- gadget_box,
- gadget_box_x,
- gadget_box_y,
- x - gadget_box_x,
- y - gadget_box_y);
- }
- if (text_box) {
- int pixel_offset;
- size_t idx;
-
- if (mouse & (BROWSER_MOUSE_DRAG_1 |
- BROWSER_MOUSE_DRAG_2))
- selection_init(&html->sel, gadget_box);
-
- font_plot_style_from_css(text_box->style,
- &fstyle);
-
- nsfont.font_position_in_string(&fstyle,
- text_box->text,
- text_box->length,
- x - gadget_box_x - text_box->x,
- &idx,
- &pixel_offset);
+ case GADGET_TEXTAREA:
+ if (gadget->type == GADGET_TEXTAREA)
+ status = messages_get("FormTextarea");
+ else
+ status = messages_get("FormTextbox");
- selection_click(&html->sel, mouse,
- text_box->byte_offset + idx);
+ pointer = get_pointer_shape(gadget_box, false);
- if (selection_dragging(&html->sel))
- browser_window_set_drag_type(bw,
- DRAGGING_SELECTION,
- NULL);
- }
- else if (mouse & BROWSER_MOUSE_PRESS_1)
- selection_clear(&html->sel, true);
+ textarea_mouse_action(gadget->data.text.ta, mouse,
+ x - gadget_box_x, y - gadget_box_y);
break;
case GADGET_HIDDEN:
/* not possible: no box generated */
@@ -810,11 +760,16 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
/* key presses must be directed at the
* main browser window, paste text
* operations ignored */
+ html_drag_type drag_type;
+ union html_drag_owner drag_owner;
if (selection_dragging(&html->sel)) {
- browser_window_set_drag_type(bw,
- DRAGGING_SELECTION,
- NULL);
+ drag_type = HTML_DRAG_SELECTION;
+ drag_owner.no_owner = true;
+ html_set_drag_type(html,
+ drag_type,
+ drag_owner,
+ NULL);
status = messages_get(
"Selecting");
}
@@ -921,35 +876,34 @@ void html_overflow_scroll_callback(void *client_data,
html_content *html = (html_content *)data->c;
struct box *box = data->box;
union content_msg_data msg_data;
+ html_drag_type drag_type;
+ union html_drag_owner drag_owner;
switch(scrollbar_data->msg) {
- case SCROLLBAR_MSG_MOVED:
- html__redraw_a_box(html, box);
- break;
- case SCROLLBAR_MSG_SCROLL_START:
- {
- struct rect rect = {
- .x0 = scrollbar_data->x0,
- .y0 = scrollbar_data->y0,
- .x1 = scrollbar_data->x1,
- .y1 = scrollbar_data->y1
- };
- browser_window_set_drag_type(html->bw,
- DRAGGING_CONTENT_SCROLLBAR, &rect);
-
- html->scrollbar = scrollbar_data->scrollbar;
- }
- break;
- case SCROLLBAR_MSG_SCROLL_FINISHED:
- html->scrollbar = NULL;
-
- browser_window_set_drag_type(html->bw,
- DRAGGING_NONE, NULL);
+ case SCROLLBAR_MSG_MOVED:
+ html__redraw_a_box(html, box);
+ break;
+ case SCROLLBAR_MSG_SCROLL_START:
+ {
+ struct rect rect = {
+ .x0 = scrollbar_data->x0,
+ .y0 = scrollbar_data->y0,
+ .x1 = scrollbar_data->x1,
+ .y1 = scrollbar_data->y1
+ };
+ drag_type = HTML_DRAG_SCROLLBAR;
+ drag_owner.scrollbar = scrollbar_data->scrollbar;
+ html_set_drag_type(html, drag_type, drag_owner, &rect);
+ }
+ break;
+ case SCROLLBAR_MSG_SCROLL_FINISHED:
+ drag_type = HTML_DRAG_NONE;
+ drag_owner.no_owner = true;
+ html_set_drag_type(html, drag_type, drag_owner, NULL);
- msg_data.pointer = BROWSER_POINTER_AUTO;
- content_broadcast(data->c, CONTENT_MSG_POINTER,
- msg_data);
- break;
+ msg_data.pointer = BROWSER_POINTER_AUTO;
+ content_broadcast(data->c, CONTENT_MSG_POINTER, msg_data);
+ break;
}
}
@@ -988,3 +942,40 @@ void html_overflow_scroll_drag_end(struct scrollbar *scrollbar,
scroll_mouse_x, scroll_mouse_y);
}
}
+
+/* Documented in html_internal.h */
+void html_set_drag_type(html_content *html, html_drag_type drag_type,
+ union html_drag_owner drag_owner, const struct rect *rect)
+{
+ union content_msg_data msg_data;
+
+ assert(html != NULL);
+
+ html->drag_type = drag_type;
+ html->drag_owner = drag_owner;
+
+ switch (drag_type) {
+ case HTML_DRAG_NONE:
+ assert(drag_owner.no_owner == true);
+ msg_data.drag.type = CONTENT_DRAG_NONE;
+ break;
+
+ case HTML_DRAG_SCROLLBAR:
+ case HTML_DRAG_TEXTAREA_SCROLLBAR:
+ case HTML_DRAG_CONTENT_SCROLL:
+ msg_data.drag.type = CONTENT_DRAG_SCROLL;
+ break;
+
+ case HTML_DRAG_SELECTION:
+ assert(drag_owner.no_owner == true);
+ /* Fall through */
+ case HTML_DRAG_TEXTAREA_SELECTION:
+ case HTML_DRAG_CONTENT_SELECTION:
+ msg_data.drag.type = CONTENT_DRAG_SELECTION;
+ break;
+ }
+ msg_data.drag.rect = rect;
+
+ /* Inform the content's drag status change */
+ content_broadcast((struct content *)html, CONTENT_MSG_DRAG, msg_data);
+}
diff --git a/render/html_internal.h b/render/html_internal.h
index 53021a15a..3e562bc39 100644
--- a/render/html_internal.h
+++ b/render/html_internal.h
@@ -27,6 +27,22 @@
#include "desktop/selection.h"
#include "render/html.h"
+typedef enum {
+ HTML_DRAG_NONE, /** No drag */
+ HTML_DRAG_SELECTION, /** Own; Text selection */
+ HTML_DRAG_SCROLLBAR, /** Not own; drag in scrollbar widget */
+ HTML_DRAG_TEXTAREA_SELECTION, /** Not own; drag in textarea widget */
+ HTML_DRAG_TEXTAREA_SCROLLBAR, /** Not own; drag in textarea widget */
+ HTML_DRAG_CONTENT_SELECTION, /** Not own; drag in child content */
+ HTML_DRAG_CONTENT_SCROLL /** Not own; drag in child content */
+} html_drag_type;
+union html_drag_owner {
+ bool no_owner;
+ struct box *content;
+ struct scrollbar *scrollbar;
+ struct box *textarea;
+}; /**< For drags we don't own */
+
/** Data specific to CONTENT_HTML. */
typedef struct html_content {
struct content base;
@@ -98,9 +114,10 @@ typedef struct html_content {
* object within a page. */
struct html_content *page;
- /** Scrollbar capturing all mouse events, updated to any active HTML
- * scrollbar, or NULL when no scrollbar drags active */
- struct scrollbar *scrollbar;
+ /* Current drag type */
+ html_drag_type drag_type;
+ /** Widget capturing all mouse events */
+ union html_drag_owner drag_owner;
/** Open core-handled form SELECT menu,
* or NULL if none currently open. */
@@ -124,6 +141,17 @@ void html_set_status(html_content *c, const char *extra);
void html__redraw_a_box(html_content *html, struct box *box);
+/**
+ * Set our drag status, and inform whatever owns the content
+ *
+ * \param html HTML content
+ * \param drag_type Type of drag
+ * \param drag_owner What owns the drag
+ * \param rect Pointer movement bounds
+ */
+void html_set_drag_type(html_content *html, html_drag_type drag_type,
+ union html_drag_owner drag_owner, const struct rect *rect);
+
struct browser_window *html_get_browser_window(struct content *c);
struct search_context *html_get_search(struct content *c);
void html_set_search(struct content *c, struct search_context *s);
@@ -162,7 +190,8 @@ bool html_scripts_exec(html_content *c);
/* in render/html_forms.c */
struct form *html_forms_get_forms(const char *docenc, dom_html_document *doc);
-struct form_control *html_forms_get_control_for_node(struct form *forms, dom_node *node);
+struct form_control *html_forms_get_control_for_node(struct form *forms,
+ dom_node *node);
/* Useful dom_string pointers */
struct dom_string;
diff --git a/render/html_redraw.c b/render/html_redraw.c
index e305b7b08..1dbe093e8 100644
--- a/render/html_redraw.c
+++ b/render/html_redraw.c
@@ -41,6 +41,7 @@
#include "desktop/options.h"
#include "desktop/print.h"
#include "desktop/scrollbar.h"
+#include "desktop/textarea.h"
#include "image/bitmap.h"
#include "render/box.h"
#include "render/font.h"
@@ -2109,7 +2110,11 @@ bool html_redraw_box(const html_content *html, struct box *box,
bg_box->type != BOX_TEXT &&
bg_box->type != BOX_INLINE_END &&
(bg_box->type != BOX_INLINE || bg_box->object ||
- bg_box->flags & IFRAME || box->flags & REPLACE_DIM)) {
+ bg_box->flags & IFRAME || box->flags & REPLACE_DIM ||
+ (bg_box->gadget != NULL &&
+ (bg_box->gadget->type == GADGET_TEXTAREA ||
+ bg_box->gadget->type == GADGET_TEXTBOX ||
+ bg_box->gadget->type == GADGET_PASSWORD)))) {
/* find intersection of clip box and border edge */
struct rect p;
p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left;
@@ -2154,7 +2159,11 @@ bool html_redraw_box(const html_content *html, struct box *box,
if (box->style && box->type != BOX_TEXT &&
box->type != BOX_INLINE_END &&
(box->type != BOX_INLINE || box->object ||
- box->flags & IFRAME || box->flags & REPLACE_DIM) &&
+ box->flags & IFRAME || box->flags & REPLACE_DIM ||
+ (box->gadget != NULL &&
+ (box->gadget->type == GADGET_TEXTAREA ||
+ box->gadget->type == GADGET_TEXTBOX ||
+ box->gadget->type == GADGET_PASSWORD))) &&
(border_top || border_right ||
border_bottom || border_left)) {
if (!html_redraw_borders(box, x_parent, y_parent,
@@ -2398,6 +2407,13 @@ bool html_redraw_box(const html_content *html, struct box *box,
current_background_color, ctx))
return false;
+ } else if (box->gadget &&
+ (box->gadget->type == GADGET_TEXTAREA ||
+ box->gadget->type == GADGET_PASSWORD ||
+ box->gadget->type == GADGET_TEXTBOX)) {
+ textarea_redraw(box->gadget->data.text.ta, x, y,
+ current_background_color, scale, &r, ctx);
+
} else if (box->text) {
if (!html_redraw_text_box(html, box, x, y, &r, scale,
current_background_color, ctx))
diff --git a/render/layout.c b/render/layout.c
index 3fd50d18d..80d470c7a 100644
--- a/render/layout.c
+++ b/render/layout.c
@@ -46,6 +46,7 @@
#include "content/content_protected.h"
#include "desktop/options.h"
#include "desktop/scrollbar.h"
+#include "desktop/textarea.h"
#include "render/box.h"
#include "render/font.h"
#include "render/form.h"
@@ -650,6 +651,20 @@ bool layout_block_context(struct box *block, int viewport_height,
layout_apply_minmax_height(block, NULL);
}
+ if (block->gadget &&
+ (block->gadget->type == GADGET_TEXTAREA ||
+ block->gadget->type == GADGET_PASSWORD ||
+ block->gadget->type == GADGET_TEXTBOX)) {
+ int ta_width = block->padding[LEFT] + block->width +
+ block->padding[RIGHT];
+ int ta_height = block->padding[TOP] + block->height +
+ block->padding[BOTTOM];
+ textarea_set_layout(block->gadget->data.text.ta,
+ ta_width, ta_height,
+ block->padding[TOP], block->padding[RIGHT],
+ block->padding[BOTTOM], block->padding[LEFT]);
+ }
+
return true;
}
@@ -1431,8 +1446,10 @@ void layout_float_find_dimensions(int available_width,
if (margin[RIGHT] == AUTO)
margin[RIGHT] = 0;
- padding[RIGHT] += scrollbar_width;
- padding[BOTTOM] += scrollbar_width;
+ if (box->gadget == NULL) {
+ padding[RIGHT] += scrollbar_width;
+ padding[BOTTOM] += scrollbar_width;
+ }
if (box->object && !(box->flags & REPLACE_DIM) &&
content_get_type(box->object) != CONTENT_HTML) {
diff --git a/render/textinput.c b/render/textinput.c
deleted file mode 100644
index e9c6df7b2..000000000
--- a/render/textinput.c
+++ /dev/null
@@ -1,2213 +0,0 @@
-/*
- * 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 <joty@netsurf-browser.org>
- * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
- *
- * This file is part of NetSurf, http://www.netsurf-browser.org/
- *
- * NetSurf is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * NetSurf is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/** \file
- * HTML form text input handling (implementation)
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <string.h>
-
-#include <dom/dom.h>
-
-#include "desktop/browser.h"
-#include "desktop/gui.h"
-#include "desktop/mouse.h"
-#include "desktop/scrollbar.h"
-#include "desktop/selection.h"
-#include "desktop/textinput.h"
-#include "render/box.h"
-#include "render/font.h"
-#include "render/form.h"
-#include "render/html_internal.h"
-#include "render/layout.h"
-#include "render/textinput.h"
-#include "utils/log.h"
-#include "utils/talloc.h"
-#include "utils/utf8.h"
-#include "utils/utils.h"
-
-/* Define to enable textinput debug */
-#undef TEXTINPUT_DEBUG
-
-
-static bool textinput_textbox_delete(struct content *c,
- struct box *text_box, unsigned char_offset,
- unsigned utf8_len);
-
-/* Textarea callbacks */
-static bool textinput_textarea_callback(struct browser_window *bw,
- uint32_t key, void *p1, void *p2);
-static void textinput_textarea_move_caret(struct browser_window *bw,
- void *p1, void *p2);
-static bool textinput_textarea_paste_text(struct browser_window *bw,
- const char *utf8, unsigned utf8_len, bool last,
- void *p1, void *p2);
-
-/* Text input callbacks */
-static bool textinput_input_callback(struct browser_window *bw,
- uint32_t key, void *p1, void *p2);
-static void textinput_input_move_caret(struct browser_window *bw,
- void *p1, void *p2);
-static bool textinput_input_paste_text(struct browser_window *bw,
- const char *utf8, unsigned utf8_len, bool last,
- void *p1, void *p2);
-
-#define SPACE_LEN(b) ((b->space == 0) ? 0 : 1)
-
-
-static struct textinput_buffer {
- char *buffer;
- size_t buffer_len;
- size_t length;
-} textinput_buffer;
-
-
-
-/**
- * Given the x,y co-ordinates of a point within a textarea, return the
- * TEXT box pointer, and the character and pixel offsets within that
- * box at which the caret should be positioned. (eg. for mouse clicks,
- * drag-and-drop insertions etc)
- *
- * \param textarea the textarea being considered
- * \param x x ordinate of point
- * \param y y ordinate of point
- * \param pchar_offset receives the char offset within the TEXT box
- * \param ppixel_offset receives the pixel offset within the TEXT box
- * \return pointer to TEXT box
- */
-
-static struct box *textinput_textarea_get_position(struct box *textarea,
- int x, int y, int *pchar_offset, int *ppixel_offset)
-{
- /* A textarea is an INLINE_BLOCK containing a single
- * INLINE_CONTAINER, which contains the text as runs of TEXT
- * separated by BR. There is at least one TEXT. The first and
- * last boxes are TEXT. Consecutive BR may not be present. These
- * constraints are satisfied by using a 0-length TEXT for blank
- * lines. */
-
- struct box *inline_container, *text_box;
- plot_font_style_t fstyle;
- size_t char_offset = 0;
-
- 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_TEXT);
- assert(text_box->text);
- font_plot_style_from_css(text_box->style, &fstyle);
- /** \todo handle errors */
- nsfont.font_position_in_string(&fstyle, text_box->text,
- text_box->length,
- (unsigned int)(x - text_box->x),
- &char_offset, ppixel_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_TEXT);
- assert(text_box->text);
- font_plot_style_from_css(text_box->style, &fstyle);
- nsfont.font_position_in_string(&fstyle,
- text_box->text,
- text_box->length,
- textarea->width,
- &char_offset,
- ppixel_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_TEXT);
- assert(text_box->text);
- font_plot_style_from_css(text_box->style, &fstyle);
- nsfont.font_position_in_string(&fstyle,
- text_box->text,
- text_box->length,
- (unsigned int)(x - text_box->x),
- &char_offset,
- ppixel_offset);
- }
- }
-
- *pchar_offset = char_offset;
-
- assert(text_box);
- return text_box;
-}
-
-
-/**
- * Delete some text from a box, or delete the box in its entirety
- *
- * \param c html content
- * \param b box
- * \param offset start offset of text to be deleted (in bytes)
- * \param length length of text to be deleted
- * \return true iff successful
- */
-
-static bool textinput_delete_handler(struct content *c, struct box *b,
- int offset, size_t length)
-{
- size_t text_length = b->length + SPACE_LEN(b);
-
- /* only remove if its not the first box */
- if (offset <= 0 && length >= text_length && b->prev != NULL) {
- /* remove the entire box */
- box_unlink_and_free(b);
-
- return true;
- } else
- return textinput_textbox_delete(c, b, offset,
- min(length, text_length - offset));
-}
-
-
-/**
- * Remove the selected text from a text box and gadget (if applicable)
- *
- * \param c The content containing the selection
- * \param s The selection to be removed
- */
-
-static void textinput_delete_selection(struct content *c, struct selection *s)
-{
- size_t start_offset, end_offset;
- struct box *text_box;
- struct box *end_box;
- struct box *next;
- size_t sel_len;
- int beginning = 0;
-
- assert(s->defined);
-
- text_box = selection_get_start(s, &start_offset);
- end_box = selection_get_end(s, &end_offset);
- sel_len = s->end_idx - s->start_idx;
-
- /* Clear selection so that deletion from textboxes proceeds */
- selection_clear(s, true);
-
- /* handle first box */
- textinput_delete_handler(c, text_box, start_offset, sel_len);
- if (text_box == end_box)
- return;
-
- for (text_box = text_box->next; text_box != end_box; text_box = next) {
- next = text_box->next;
- box_unlink_and_free(text_box);
- }
-
- textinput_delete_handler(c, end_box, beginning, end_offset);
-}
-
-
-/**
- * Insert a number of chars into a text box
- *
- * \param c html_content
- * \param text_box text box
- * \param char_offset offset (bytes) at which to insert text
- * \param utf8 UTF-8 text to insert
- * \param utf8_len length (bytes) of UTF-8 text to insert
- * \return true iff successful
- */
-
-static bool textinput_textbox_insert(struct content *c,
- struct box *text_box, unsigned char_offset, const char *utf8,
- unsigned utf8_len)
-{
- html_content *html = (html_content *)c;
- char *text;
- struct box *input = text_box->parent->parent;
- bool hide;
-
- if (html->bw && html->sel.defined)
- textinput_delete_selection(c, &html->sel);
-
- /* insert into form gadget (text and password inputs only) */
- if (input->gadget && (input->gadget->type == GADGET_TEXTBOX ||
- input->gadget->type == GADGET_PASSWORD) &&
- input->gadget->value) {
- size_t form_offset = input->gadget->caret_form_offset;
- char *value = realloc(input->gadget->value,
- input->gadget->length + utf8_len + 1);
- if (!value) {
- warn_user("NoMemory", 0);
- return true;
- }
- 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;
- }
-
- hide = (input->gadget && input->gadget->type == GADGET_PASSWORD);
- if (hide) {
- /* determine the number of '*'s to be inserted */
- const char *eutf8 = utf8 + utf8_len;
- utf8_len = 0;
- while (utf8 < eutf8) {
- utf8 += utf8_next(utf8, eutf8 - utf8, 0);
- utf8_len++;
- }
- }
-
- /* insert in text box */
- text = talloc_realloc(html->bctx, text_box->text,
- char,
- text_box->length + SPACE_LEN(text_box) + utf8_len + 1);
- if (!text) {
- warn_user("NoMemory", 0);
- return false;
- }
- text_box->text = text;
-
- if (text_box->space != 0 &&
- char_offset == text_box->length + SPACE_LEN(text_box)) {
- if (hide)
- text_box->space = 0;
- else {
- unsigned int last_off = utf8_prev(utf8, utf8_len);
- if (utf8[last_off] != ' ')
- text_box->space = 0;
- else
- utf8_len = last_off;
- }
- text_box->text[text_box->length++] = ' ';
- } else {
- memmove(text_box->text + char_offset + utf8_len,
- text_box->text + char_offset,
- text_box->length - char_offset);
- }
-
- if (hide)
- memset(text_box->text + char_offset, '*', utf8_len);
- else
- memcpy(text_box->text + char_offset, utf8, utf8_len);
- text_box->length += utf8_len;
-
- /* nothing should assume that the text is terminated,
- * but just in case */
- text_box->text[text_box->length] = 0;
-
- text_box->width = UNKNOWN_WIDTH;
-
- return true;
-}
-
-/**
- * Calculates the form_offset from the box_offset
- *
- * \param input The root box containing both the textbox and gadget
- * \param text_box The textbox containing the caret
- * \param char_offset The caret offset within text_box
- * \return the translated form_offset
- */
-
-static size_t textinput_get_form_offset(struct box* input, struct box* text_box,
- size_t char_offset)
-{
- int uchars;
- unsigned int 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);
- return offset;
-}
-
-
-/**
- * Delete a number of chars from a text box
- *
- * \param c html content
- * \param text_box text box
- * \param char_offset offset within text box (bytes) of first char to delete
- * \param utf8_len length (bytes) of chars to be deleted
- * \return true on success, false otherwise
- *
- * ::char_offset and ::utf8_len are only considered when there is no selection.
- * If there is a selection, the entire selected area is deleted.
- */
-
-bool textinput_textbox_delete(struct content *c, struct box *text_box,
- unsigned char_offset, unsigned utf8_len)
-{
- html_content *html = (html_content *)c;
- unsigned next_offset = char_offset + utf8_len;
- struct box *form = text_box->parent->parent;
-
- if (html->bw && html->sel.defined) {
- textinput_delete_selection(c, &html->sel);
- return true;
- }
-
- /* delete from form gadget (text and password inputs only) */
- if (form->gadget && (form->gadget->type == GADGET_TEXTBOX ||
- form->gadget->type == GADGET_PASSWORD) &&
- form->gadget->value) {
- size_t form_offset = textinput_get_form_offset(form, text_box,
- char_offset);
- size_t next_offset = textinput_get_form_offset(form, text_box,
- char_offset + utf8_len);
-
- memmove(form->gadget->value + form_offset,
- form->gadget->value + next_offset,
- form->gadget->length - next_offset);
- form->gadget->length -= (next_offset - form_offset);
- form->gadget->value[form->gadget->length] = 0;
- }
-
- /* delete from visible textbox */
- if (next_offset <= text_box->length + SPACE_LEN(text_box)) {
- /* handle removal of trailing space */
- if (text_box->space != 0 && next_offset > text_box->length) {
- if (char_offset > 0) {
- /* is the trailing character still a space? */
- int tmp = utf8_prev(text_box->text, char_offset);
- if (isspace(text_box->text[tmp]))
- char_offset = tmp;
- else
- text_box->space = 0;
- } else {
- text_box->space = 0;
- }
-
- text_box->length = char_offset;
- } else {
- memmove(text_box->text + char_offset,
- text_box->text + next_offset,
- text_box->length - next_offset);
- text_box->length -= utf8_len;
- }
-
- /* nothing should assume that the text is terminated,
- * but just in case */
- text_box->text[text_box->length] = 0;
-
- text_box->width = UNKNOWN_WIDTH;
-
- return true;
- }
-
- return false;
-}
-
-/**
- * Locate the first inline box at the start of this line
- *
- * \param text_box text box from which to start searching
- */
-
-static struct box *textinput_line_start(struct box *text_box)
-{
- while (text_box->prev && text_box->prev->type == BOX_TEXT)
- text_box = text_box->prev;
- return text_box;
-}
-
-
-/**
- * Locate the last inline box in this line
- *
- * \param text_box text box from which to start searching
- */
-
-static struct box *textinput_line_end(struct box *text_box)
-{
- while (text_box->next && text_box->next->type == BOX_TEXT)
- text_box = text_box->next;
- return text_box;
-}
-
-
-/**
- * Backtrack to the start of the previous line, if there is one.
- */
-
-static struct box *textinput_line_above(struct box *text_box)
-{
- struct box *prev;
-
- text_box = textinput_line_start(text_box);
-
- prev = text_box->prev;
- while (prev && prev->type == BOX_BR)
- prev = prev->prev;
-
- return prev ? textinput_line_start(prev) : text_box;
-}
-
-
-/**
- * Advance to the start of the next line, if there is one.
- */
-
-static struct box *textinput_line_below(struct box *text_box)
-{
- struct box *next;
-
- text_box = textinput_line_end(text_box);
-
- next = text_box->next;
- while (next && next->type == BOX_BR)
- next = next->next;
-
- return next ? next : text_box;
-}
-
-
-/**
- * Add some text to the buffer, optionally appending a trailing space.
- *
- * \param text text to be added
- * \param length length of text in bytes
- * \param space indicates whether a trailing space should be appended
- * \param fstyle The font style
- * \return true if successful
- */
-
-static bool textinput_add_to_buffer(const char *text, size_t length, bool space,
- const plot_font_style_t *fstyle)
-{
- size_t new_length = textinput_buffer.length + length + (space ? 1 : 0) + 1;
-
- if (new_length > textinput_buffer.buffer_len) {
- size_t new_alloc = new_length + (new_length / 4);
- char *new_buff;
-
- new_buff = realloc(textinput_buffer.buffer, new_alloc);
- if (new_buff == NULL)
- return false;
-
- textinput_buffer.buffer = new_buff;
- textinput_buffer.buffer_len = new_alloc;
- }
-
- memcpy(textinput_buffer.buffer + textinput_buffer.length, text, length);
- textinput_buffer.length += length;
-
- if (space)
- textinput_buffer.buffer[textinput_buffer.length++] = ' ';
-
- textinput_buffer.buffer[textinput_buffer.length] = '\0';
-
- return true;
-}
-
-
-/**
- * Empty the buffer, called prior to textinput_add_to_buffer sequence
- *
- * \return true iff successful
- */
-
-static bool textinput_empty_buffer(void)
-{
- const size_t init_size = 1024;
-
- if (textinput_buffer.buffer_len == 0) {
- textinput_buffer.buffer = malloc(init_size);
- if (textinput_buffer.buffer == NULL)
- return false;
-
- textinput_buffer.buffer_len = init_size;
- }
-
- textinput_buffer.length = 0;
-
- return true;
-}
-
-
-/**
- * Cut a range of text from a text box,
- * possibly placing it on the global clipboard.
- *
- * \param c html content
- * \param start_box text box at start of range
- * \param start_idx index (bytes) within start box
- * \param end_box text box at end of range
- * \param end_idx index (bytes) within end box
- * \param clipboard whether to place text on the clipboard
- * \return true iff successful
- */
-
-static bool textinput_textarea_cut(struct content *c,
- struct box *start_box, unsigned start_idx,
- struct box *end_box, unsigned end_idx,
- bool clipboard)
-{
- struct box *box = start_box;
- bool success = true;
- bool del = false; /* caller expects start_box to persist */
-
- if (textinput_empty_buffer() == false) {
- return false;
- }
-
- while (box && box != end_box) {
- /* read before deletion, in case the whole box goes */
- struct box *next = box->next;
-
- if (box->type == BOX_BR) {
- if (clipboard && !textinput_add_to_buffer("\n", 1,
- false, plot_style_font)) {
- return false;
- }
- box_unlink_and_free(box);
- } else {
- /* append box text to clipboard and then delete it */
- if (clipboard &&
- !textinput_add_to_buffer(box->text + start_idx,
- box->length - start_idx,
- SPACE_LEN(box), plot_style_font)) {
- return false;
- }
-
- if (del) {
- if (!textinput_delete_handler(c, box,
- start_idx,
- (box->length + SPACE_LEN(box)) -
- start_idx) && clipboard) {
- return false;
- }
- } else {
- textinput_textbox_delete(c, box, start_idx,
- (box->length + SPACE_LEN(box)) -
- start_idx);
- }
- }
-
- del = true;
- start_idx = 0;
- box = next;
- }
-
- /* and the last box */
- if (box) {
- if (clipboard && !textinput_add_to_buffer(box->text + start_idx,
- end_idx - start_idx, end_idx > box->length,
- plot_style_font)) {
- success = false;
- } else {
- if (del) {
- if (!textinput_delete_handler(c, box,
- start_idx, end_idx - start_idx))
- success = false;
- } else {
- textinput_textbox_delete(c, box, start_idx,
- end_idx - start_idx);
- }
- }
- }
-
- if (clipboard) {
- gui_set_clipboard(textinput_buffer.buffer,
- textinput_buffer.length, NULL, 0);
- }
-
- return true;
-}
-
-
-/**
- * Break a text box into two
- *
- * \param c html content
- * \param text_box text box to be split
- * \param char_offset offset (in bytes) at which text box is to be split
- */
-
-static struct box *textinput_textarea_insert_break(struct content *c,
- struct box *text_box, size_t char_offset)
-{
- html_content *html = (html_content *)c;
- struct box *new_br, *new_text;
- char *text;
-
- text = talloc_array(html->bctx, char, text_box->length + 1);
- if (!text) {
- warn_user("NoMemory", 0);
- return NULL;
- }
-
- new_br = box_create(NULL, text_box->style, false, 0, 0, text_box->title,
- 0, html->bctx);
- new_text = talloc(html->bctx, struct box);
- if (!new_text) {
- warn_user("NoMemory", 0);
- return NULL;
- }
-
- new_br->type = BOX_BR;
- box_insert_sibling(text_box, new_br);
-
- memcpy(new_text, text_box, sizeof (struct box));
- new_text->flags |= CLONE;
- 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);
-
- return new_text;
-}
-
-
-/**
- * Reflow textarea preserving width and height
- *
- * \param c html content
- * \param textarea text area box
- * \param inline_container container holding text box
- */
-
-static void textinput_textarea_reflow(struct content *c,
- struct box *textarea, struct box *inline_container)
-{
- int width = textarea->width;
- int height = textarea->height;
-
- assert(c != NULL);
-
- if (!layout_inline_container(inline_container, width,
- textarea, 0, 0, (struct html_content *) c))
- warn_user("NoMemory", 0);
- textarea->width = width;
- textarea->height = height;
- layout_calculate_descendant_bboxes(textarea);
- box_handle_scrollbars(c, textarea,
- box_hscrollbar_present(textarea),
- box_vscrollbar_present(textarea));
-}
-
-
-/**
- * Move to the start of the word containing the given character position,
- * or the start of the preceding word if already at the start of this one.
- *
- * \param text UTF-8 text string
- * \param poffset offset of caret within string (updated on exit)
- * \param pchars receives the number of characters skipped
- * \return true iff the start of a word was found before/at the string start
- */
-
-static bool textinput_word_left(const char *text,
- size_t *poffset, size_t *pchars)
-{
- size_t offset = *poffset;
- bool success = false;
- size_t nchars = 0;
-
- /* Skip any spaces immediately prior to the offset */
- while (offset > 0) {
- offset = utf8_prev(text, offset);
- nchars++;
- if (!isspace(text[offset])) break;
- }
-
- /* Now skip all non-space characters */
- while (offset > 0) {
- size_t prev = utf8_prev(text, offset);
- success = true;
- if (isspace(text[prev]))
- break;
- offset = prev;
- nchars++;
- }
-
- *poffset = offset;
- if (pchars) *pchars = nchars;
-
- return success;
-}
-
-
-/**
- * Move to the start of the first word following the given character position.
- *
- * \param text UTF-8 text string
- * \param len length of string in bytes
- * \param poffset offset of caret within string (updated on exit)
- * \param pchars receives the number of characters skipped
- * \return true iff the start of a word was found before the string end
- */
-
-static bool textinput_word_right(const char *text, size_t len,
- size_t *poffset, size_t *pchars)
-{
- size_t offset = *poffset;
- bool success = false;
- size_t nchars = 0;
-
- /* Skip all non-space characters after the offset */
- while (offset < len) {
- if (isspace(text[offset])) break;
- offset = utf8_next(text, len, offset);
- nchars++;
- }
-
- /* Now skip all space characters */
- while (offset < len) {
- offset = utf8_next(text, len, offset);
- nchars++;
- if (offset < len && !isspace(text[offset])) {
- success = true;
- break;
- }
- }
-
- *poffset = offset;
- if (pchars) *pchars = nchars;
-
- return success;
-}
-
-/**
- * Adjust scroll offsets so that the caret is visible
- *
- * \param c html content where click ocurred
- * \param textarea textarea box
- * \return true if a change in scroll offsets has occurred
-*/
-
-static bool textinput_ensure_caret_visible(struct content *c,
- struct box *textarea)
-{
- html_content *html = (html_content *)c;
- int cx, cy;
- int scrollx, scrolly;
-
- assert(textarea->gadget);
-
- scrollx = scrollbar_get_offset(textarea->scroll_x);
- scrolly = scrollbar_get_offset(textarea->scroll_y);
-
- /* Calculate the caret coordinates */
- cx = textarea->gadget->caret_pixel_offset +
- textarea->gadget->caret_text_box->x;
- cy = textarea->gadget->caret_text_box->y;
-
- /* Ensure they are visible */
- if (textarea->scroll_x == NULL) {
- scrollx = 0;
- } else if (cx - scrollbar_get_offset(textarea->scroll_x) < 0) {
- scrollx = cx;
- } else if (cx > scrollbar_get_offset(textarea->scroll_x) +
- textarea->width) {
- scrollx = cx - textarea->width;
- }
-
- if (textarea->scroll_y == NULL) {
- scrolly = 0;
- } else if (cy - scrollbar_get_offset(textarea->scroll_y) < 0) {
- scrolly = cy;
- } else if (cy + textarea->gadget->caret_text_box->height >
- scrollbar_get_offset(textarea->scroll_y) +
- textarea->height) {
- scrolly = (cy + textarea->gadget->caret_text_box->height) -
- textarea->height;
- }
-
- if ((scrollx == scrollbar_get_offset(textarea->scroll_x)) &&
- (scrolly == scrollbar_get_offset(textarea->scroll_y)))
- return false;
-
- if (textarea->scroll_x != NULL) {
- html->scrollbar = textarea->scroll_x;
- scrollbar_set(textarea->scroll_x, scrollx, false);
- html->scrollbar = NULL;
- }
- if (textarea->scroll_y != NULL) {
- html->scrollbar = textarea->scroll_x;
- scrollbar_set(textarea->scroll_y, scrolly, false);
- html->scrollbar = NULL;
- }
-
- return true;
-}
-
-
-/**
- * Paste a block of text into a textarea at the
- * current caret position.
- *
- * \param bw browser window
- * \param utf8 pointer to block of text
- * \param utf8_len length (bytes) of text block
- * \param last true iff this is the last chunk (update screen too)
- * \param p1 pointer to textarea
- * \param p2 html content with the text area box
- * \return true iff successful
- */
-
-bool textinput_textarea_paste_text(struct browser_window *bw,
- const char *utf8, unsigned utf8_len, bool last,
- void *p1, void *p2)
-{
- struct box *textarea = p1;
- struct content *c = p2;
- struct box *inline_container =
- textarea->gadget->caret_inline_container;
- struct box *text_box = textarea->gadget->caret_text_box;
- size_t char_offset = textarea->gadget->caret_box_offset;
- int pixel_offset = textarea->gadget->caret_pixel_offset;
- const char *ep = utf8 + utf8_len;
- const char *p = utf8;
- bool success = true;
- bool update = last;
-
- while (p < ep) {
- struct box *new_text;
- unsigned utf8_len;
-
- while (p < ep) {
- if (*p == '\n' || *p == '\r') break;
- p++;
- }
-
- utf8_len = p - utf8;
- if (!textinput_textbox_insert(c, text_box, char_offset,
- utf8, utf8_len))
- return false;
-
- char_offset += utf8_len;
- if (p == ep)
- break;
-
- new_text = textinput_textarea_insert_break(c, text_box,
- char_offset);
- if (!new_text) {
- /* we still need to update the screen */
- update = true;
- success = false;
- break;
- }
-
- /* place caret at start of new text box */
- text_box = new_text;
- char_offset = 0;
-
- /* handle CR/LF and LF/CR terminations */
- if ((*p == '\n' && p[1] == '\r') ||
- (*p == '\r' && p[1] == '\n'))
- p++;
- utf8 = ++p;
- }
-
-// textarea->gadget->caret_inline_container = inline_container;
- textarea->gadget->caret_text_box = text_box;
- textarea->gadget->caret_box_offset = char_offset;
-
- if (update) {
- int box_x, box_y;
- plot_font_style_t fstyle;
-
- /* reflow textarea preserving width and height */
- textinput_textarea_reflow(c, textarea, inline_container);
- /* reflowing may have broken our caret offset
- * this bit should hopefully continue to work if
- * textarea_reflow is fixed to update the caret itself */
- char_offset = textarea->gadget->caret_box_offset;
- text_box = textarea->gadget->caret_text_box;
-
- while ((char_offset > text_box->length + SPACE_LEN(text_box)) &&
- (text_box->next) &&
- (text_box->next->type == BOX_TEXT)) {
-#ifdef TEXTINPUT_DEBUG
- LOG(("Caret out of range: Was %d in boxlen %d "
- "space %d", char_offset,
- text_box->length, SPACE_LEN(text_box)));
-#endif
- char_offset -= text_box->length + SPACE_LEN(text_box);
- text_box = text_box->next;
- }
-
- /* not sure if this will happen or not...
- * but won't stick an assert here as we can recover from it */
- if (char_offset > text_box->length) {
-#ifdef TEXTINPUT_DEBUG
- LOG(("Caret moved beyond end of line: "
- "Was %d in boxlen %d", char_offset,
- text_box->length));
-#endif
- char_offset = text_box->length;
- }
-
- textarea->gadget->caret_text_box = text_box;
- textarea->gadget->caret_box_offset = char_offset;
-
- font_plot_style_from_css(text_box->style, &fstyle);
-
- nsfont.font_width(&fstyle, text_box->text,
- char_offset, &pixel_offset);
-
- textarea->gadget->caret_pixel_offset = pixel_offset;
-
- box_coords(textarea, &box_x, &box_y);
- box_x += scrollbar_get_offset(textarea->scroll_x);
- box_y += scrollbar_get_offset(textarea->scroll_y);
- textinput_ensure_caret_visible(c, textarea);
- box_x -= scrollbar_get_offset(textarea->scroll_x);
- box_y -= scrollbar_get_offset(textarea->scroll_y);
-
- 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,
- textinput_textarea_callback,
- textinput_textarea_paste_text,
- textinput_textarea_move_caret,
- textarea, c);
-
- html__redraw_a_box((html_content *)c, textarea);
- }
-
- return success;
-}
-
-
-/**
- * Move caret to new position after reformatting
- *
- * \param bw browser window
- * \param p1 pointer textarea box
- * \param p2 html content with the text area box
- * \return none
- */
-
-void textinput_textarea_move_caret(struct browser_window *bw,
- void *p1, void *p2)
-{
- struct box *textarea = p1;
- struct content *c = p2;
- struct box *inline_container = textarea->gadget->caret_inline_container;
- struct box *text_box = textarea->gadget->caret_text_box;
- size_t char_offset = textarea->gadget->caret_box_offset;
- int pixel_offset;
- int box_x, box_y;
- plot_font_style_t fstyle;
-
- font_plot_style_from_css(text_box->style, &fstyle);
-
- box_coords(textarea, &box_x, &box_y);
- box_x -= scrollbar_get_offset(textarea->scroll_x);
- box_y -= scrollbar_get_offset(textarea->scroll_y);
-
- nsfont.font_width(&fstyle, text_box->text,
- char_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,
- textinput_textarea_callback,
- textinput_textarea_paste_text,
- textinput_textarea_move_caret,
- textarea, c);
-}
-
-
-/**
- * Update display to reflect modified input field
- *
- * \param bw browser window
- * \param input input field
- * \param form_offset
- * \param box_offset offset of caret within text box
- * \param to_textarea caret is to be moved to a textarea
- * \param redraw force redraw even if field hasn't scrolled
- */
-
-static void textinput_input_update_display(struct content *c, struct box *input,
- unsigned box_offset, bool to_textarea, bool redraw)
-{
- struct box *text_box = input->children->children;
- unsigned pixel_offset;
- int box_x, box_y;
- int dx;
- plot_font_style_t fstyle;
- html_content *html = (html_content *)c;
-
- font_plot_style_from_css(text_box->style, &fstyle);
-
- if (redraw)
- nsfont.font_width(&fstyle, text_box->text, text_box->length,
- &text_box->width);
-
- box_coords(input, &box_x, &box_y);
-
- nsfont.font_width(&fstyle, text_box->text, box_offset,
- (int *) &pixel_offset);
-
- /* Shift text box horizontally, so caret is visible */
- dx = text_box->x;
- text_box->x = 0;
- if (input->width < text_box->width &&
- input->width / 2 < (int) pixel_offset) {
- /* Make caret appear in centre of text input */
- text_box->x = input->width / 2 - pixel_offset;
- /* Clamp if we've shifted too far left */
- 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;
-
- browser_window_place_caret(html->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 ? textinput_textarea_callback
- : textinput_input_callback,
- to_textarea ? textinput_textarea_paste_text
- : textinput_input_paste_text,
- to_textarea ? textinput_textarea_move_caret
- : textinput_input_move_caret,
- input, c);
-
- if (dx || redraw)
- html__redraw_a_box(html, input);
-}
-
-
-/**
- * Key press callback for text areas.
- *
- * \param bw The browser window containing the text area
- * \param key The ucs4 character codepoint
- * \param p1 The text area box
- * \param p2 The html content with the text area box
- * \return true if the keypress is dealt with, false otherwise. It can
- * return true even if it ran out of memory; this just means that
- * it would have claimed it if it could.
- */
-bool textinput_textarea_callback(struct browser_window *bw, uint32_t key,
- void *p1, void *p2)
-{
- struct box *textarea = p1;
- struct content *c = p2;
- html_content *html = (html_content *)c;
- struct box *inline_container =
- textarea->gadget->caret_inline_container;
- struct box *text_box = textarea->gadget->caret_text_box;
- struct box *new_text;
- size_t char_offset = textarea->gadget->caret_box_offset;
- int pixel_offset = textarea->gadget->caret_pixel_offset;
- int box_x, box_y;
- char utf8[6];
- unsigned int utf8_len;
- bool scrolled, reflow = false;
- bool selection_exists = html->sel.defined;
- plot_font_style_t fstyle;
-
- /* box_dump(textarea, 0); */
-#ifdef TEXTINPUT_DEBUG
- LOG(("key %i at %i in '%.*s'", key, char_offset,
- (int) text_box->length, text_box->text));
-#endif
-
- box_coords(textarea, &box_x, &box_y);
- box_x -= scrollbar_get_offset(textarea->scroll_x);
- box_y -= scrollbar_get_offset(textarea->scroll_y);
-
- if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
- /* normal character insertion */
- utf8_len = utf8_from_ucs4(key, utf8);
-
- if (!textinput_textbox_insert(c, text_box, char_offset,
- utf8, utf8_len))
- return true;
-
- char_offset += utf8_len;
- reflow = true;
-
- } else switch (key) {
- case KEY_DELETE_LEFT:
- if (selection_exists) {
- /* Have a selection; delete it */
- textinput_textbox_delete(c, text_box, 0, 0);
- } else if (char_offset == 0) {
- /* at the start of a text box */
- struct box *prev;
-
- if (text_box->prev && text_box->prev->type == BOX_BR) {
- /* previous box is BR: remove it */
- box_unlink_and_free(text_box->prev);
- }
-
- /* This needs to be after the BR removal, as that may
- * result in no previous box existing */
- if (!text_box->prev)
- /* at very beginning of text area: ignore */
- return true;
-
- /* delete space by merging with previous text box */
- prev = text_box->prev;
- assert(prev->type == BOX_TEXT);
- assert(prev->text);
-
- char_offset = prev->length; /* caret at join */
-
- if (!textinput_textbox_insert(c, prev, prev->length,
- text_box->text, text_box->length))
- return true;
-
- box_unlink_and_free(text_box);
-
- /* place caret at join (see above) */
- text_box = prev;
-
- } else {
- /* delete a character */
- size_t prev_offset = char_offset;
- size_t new_offset =
- utf8_prev(text_box->text, char_offset);
-
- if (textinput_textbox_delete(c, text_box, new_offset,
- prev_offset - new_offset))
- char_offset = new_offset;
- }
- reflow = true;
- break;
-
- case KEY_DELETE_LINE_START:
- {
- struct box *start_box = textinput_line_start(text_box);
-
- /* Clear the selection, if one exists */
- if (selection_exists)
- selection_clear(&html->sel, false);
-
- textinput_textarea_cut(c, start_box, 0, text_box,
- char_offset, false);
- text_box = start_box;
- char_offset = 0;
- reflow = true;
- }
- break;
-
- case KEY_DELETE_LINE_END:
- {
- struct box *end_box = textinput_line_end(text_box);
-
- /* Clear the selection, if one exists */
- if (selection_exists)
- selection_clear(&html->sel, false);
-
- if (end_box != text_box ||
- char_offset < text_box->length + SPACE_LEN(text_box)) {
- /* there's something at the end of the line to delete */
- textinput_textarea_cut(c, text_box,
- char_offset, end_box,
- end_box->length + SPACE_LEN(end_box),
- false);
- reflow = true;
- break;
- }
- }
- /* no break */
- case KEY_DELETE_RIGHT: /* delete to right */
- if (selection_exists) {
- /* Delete selection */
- textinput_textbox_delete(c, text_box, 0, 0);
- } else if (char_offset >= text_box->length) {
- /* at the end of a text box */
- struct box *next;
-
- if (text_box->next && text_box->next->type == BOX_BR) {
- /* next box is a BR: remove it */
- box_unlink_and_free(text_box->next);
- }
-
- /* This test is after the BR removal, as that may
- * result in no subsequent box being present */
- if (!text_box->next)
- /* at very end of text area: ignore */
- return true;
-
- /* delete space by merging with next text box */
-
- next = text_box->next;
- assert(next->type == BOX_TEXT);
- assert(next->text);
-
- if (!textinput_textbox_insert(c, text_box,
- text_box->length,
- next->text, next->length))
- return true;
-
- box_unlink_and_free(next);
-
- /* leave caret at join */
- } else {
- /* delete a character */
- size_t next_offset = utf8_next(text_box->text,
- text_box->length, char_offset);
-
- textinput_textbox_delete(c, text_box, char_offset,
- next_offset - char_offset);
- }
- reflow = true;
- break;
-
- case KEY_NL:
- case KEY_CR: /* paragraph break */
- if (selection_exists) {
- /* If we have a selection, then delete it,
- * so it's replaced by the break */
- textinput_textbox_delete(c, text_box, 0, 0);
- }
-
- new_text = textinput_textarea_insert_break(c, text_box,
- char_offset);
- if (!new_text)
- return true;
-
- /* place caret at start of new text box */
- text_box = new_text;
- char_offset = 0;
-
- reflow = true;
- break;
-
- case KEY_CUT_LINE:
- {
- struct box *start_box = textinput_line_start(text_box);
- struct box *end_box = textinput_line_end(text_box);
-
- /* Clear the selection, if one exists */
- if (selection_exists)
- selection_clear(&html->sel, false);
-
- textinput_textarea_cut(c, start_box, 0, end_box,
- end_box->length, false);
-
- text_box = start_box;
- char_offset = 0;
- reflow = true;
- }
- break;
-
- case KEY_PASTE:
- {
- char *buff = NULL;
- size_t buff_len;
- bool success;
-
- gui_get_clipboard(&buff, &buff_len);
- if (buff == NULL)
- return false;
-
- success = browser_window_paste_text(bw, buff, buff_len, true);
- free(buff);
-
- return success;
- }
-
- case KEY_CUT_SELECTION:
- {
- size_t start_idx, end_idx;
- struct box *start_box =
- selection_get_start(&html->sel, &start_idx);
- struct box *end_box = selection_get_end(&html->sel, &end_idx);
-
- if (start_box && end_box) {
- selection_clear(&html->sel, false);
- textinput_textarea_cut(c, start_box, start_idx,
- end_box, end_idx, true);
- text_box = start_box;
- char_offset = start_idx;
- reflow = true;
- }
- }
- break;
-
- case KEY_RIGHT:
- if (selection_exists) {
- /* In selection, move caret to end */
- text_box = selection_get_end(&html->sel, &char_offset);
- } else if (char_offset < text_box->length) {
- /* Within-box movement */
- char_offset = utf8_next(text_box->text,
- text_box->length, char_offset);
- } else {
- /* Between-box movement */
- if (!text_box->next)
- /* at end of text area: ignore */
- return true;
-
- text_box = text_box->next;
- if (text_box->type == BOX_BR)
- text_box = text_box->next;
- char_offset = 0;
- }
- break;
-
- case KEY_LEFT:
- if (selection_exists) {
- /* In selection, move caret to start */
- text_box = selection_get_start(&html->sel, &char_offset);
- } else if (char_offset > 0) {
- /* Within-box movement */
- char_offset = utf8_prev(text_box->text, char_offset);
- } else {
- /* Between-box movement */
- if (!text_box->prev)
- /* at start of text area: ignore */
- return true;
-
- text_box = text_box->prev;
- if (text_box->type == BOX_BR)
- text_box = text_box->prev;
- char_offset = text_box->length;
- }
- break;
-
- case KEY_UP:
- selection_clear(&html->sel, true);
- textinput_textarea_click(c, BROWSER_MOUSE_CLICK_1,
- textarea,
- box_x, box_y,
- text_box->x + pixel_offset,
- inline_container->y + text_box->y - 1);
- return true;
-
- case KEY_DOWN:
- selection_clear(&html->sel, true);
- textinput_textarea_click(c, 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 true;
-
- case KEY_LINE_START:
- text_box = textinput_line_start(text_box);
- char_offset = 0;
- break;
-
- case KEY_LINE_END:
- text_box = textinput_line_end(text_box);
- char_offset = text_box->length;
- break;
-
- case KEY_TEXT_START:
- assert(text_box->parent);
-
- /* place caret at start of first box */
- text_box = text_box->parent->children;
- char_offset = 0;
- break;
-
- case KEY_TEXT_END:
- assert(text_box->parent);
-
- /* place caret at end of last box */
- text_box = text_box->parent->last;
- char_offset = text_box->length;
- break;
-
- case KEY_WORD_LEFT:
- {
- bool start_of_word;
- /* if there is a selection, caret should stay at beginning */
- if (selection_exists)
- break;
-
- start_of_word = (char_offset <= 0 ||
- isspace(text_box->text[char_offset - 1]));
-
- while (!textinput_word_left(text_box->text,
- &char_offset, NULL)) {
- struct box *prev = NULL;
-
- assert(char_offset == 0);
-
- if (start_of_word) {
- /* find the preceding non-BR box */
- prev = text_box->prev;
- if (prev && prev->type == BOX_BR)
- prev = prev->prev;
- }
-
- if (!prev) {
- /* just stay at the start of this box */
- break;
- }
-
- assert(prev->type == BOX_TEXT);
-
- text_box = prev;
- char_offset = prev->length;
- }
- }
- break;
-
- case KEY_WORD_RIGHT:
- {
- bool in_word;
- /* if there is a selection, caret should move to the end */
- if (selection_exists) {
- text_box = selection_get_end(&html->sel, &char_offset);
- break;
- }
-
- in_word = (char_offset < text_box->length &&
- !isspace(text_box->text[char_offset]));
-
- while (!textinput_word_right(text_box->text, text_box->length,
- &char_offset, NULL)) {
- struct box *next = text_box->next;
-
- /* find the next non-BR box */
- if (next && next->type == BOX_BR)
- next = next->next;
-
- if (!next) {
- /* just stay at the end of this box */
- char_offset = text_box->length;
- break;
- }
-
- assert(next->type == BOX_TEXT);
-
- text_box = next;
- char_offset = 0;
-
- if (in_word && text_box->length > 0 &&
- !isspace(text_box->text[0])) {
- /* just stay at the start of this box */
- break;
- }
- }
- }
- break;
-
- case KEY_PAGE_UP:
- {
- int nlines = (textarea->height / text_box->height) - 1;
-
- while (nlines-- > 0)
- text_box = textinput_line_above(text_box);
-
- if (char_offset > text_box->length)
- char_offset = text_box->length;
- }
- break;
-
- case KEY_PAGE_DOWN:
- {
- int nlines = (textarea->height / text_box->height) - 1;
-
- while (nlines-- > 0)
- text_box = textinput_line_below(text_box);
-
- /* vague attempt to keep the caret at the same horizontal
- * position, given that the code currently cannot support it
- * being beyond the end of a line */
- if (char_offset > text_box->length)
- char_offset = text_box->length;
- }
- break;
-
- default:
- return false;
- }
-
- /*
- box_dump(textarea, 0);
- for (struct box *t = inline_container->children; t; t = t->next) {
- assert(t->type == BOX_TEXT);
- assert(t->text);
- assert(t->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)
- textinput_textarea_reflow(c, textarea, inline_container);
-
- if (text_box->length + SPACE_LEN(text_box) <= char_offset) {
- if (text_box->next && text_box->next->type == BOX_TEXT) {
- /* the text box has been split when reflowing and
- the caret is in the second part */
- char_offset -= (text_box->length + SPACE_LEN(text_box));
- text_box = text_box->next;
- assert(text_box);
- assert(char_offset <= text_box->length);
- /* Scroll back to the left */
- if (textarea->scroll_x != NULL) {
- box_x += scrollbar_get_offset(
- textarea->scroll_x);
- scrollbar_set(textarea->scroll_x, 0, false);
- }
- } else {
- assert(!text_box->next ||
- (text_box->next &&
- text_box->next->type == BOX_BR));
-
- char_offset = text_box->length + SPACE_LEN(text_box);
- }
- }
-
- font_plot_style_from_css(text_box->style, &fstyle);
-
- nsfont.font_width(&fstyle, text_box->text, char_offset, &pixel_offset);
-
- selection_clear(&html->sel, true);
-
- 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;
-
- box_x += scrollbar_get_offset(textarea->scroll_x);
- box_y += scrollbar_get_offset(textarea->scroll_y);
- scrolled = textinput_ensure_caret_visible(c, textarea);
- box_x -= scrollbar_get_offset(textarea->scroll_x);
- box_y -= scrollbar_get_offset(textarea->scroll_y);
-
- 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,
- textinput_textarea_callback,
- textinput_textarea_paste_text,
- textinput_textarea_move_caret,
- textarea, c);
-
- if (scrolled || reflow)
- html__redraw_a_box(html, textarea);
-
- return true;
-}
-
-
-/**
- * Handle clicks in a text area by placing the caret.
- *
- * \param c html content 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 textinput_textarea_click(struct content *c, 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 TEXT
- * separated by BR. There is at least one TEXT. The first and
- * last boxes are TEXT. Consecutive BR may not be present. These
- * constraints are satisfied by using a 0-length TEXT for blank
- * lines. */
-
- int char_offset = 0, pixel_offset = 0;
- struct box *inline_container = textarea->children;
- struct box *text_box;
- bool scrolled;
- html_content *html = (html_content *)c;
-
- text_box = textinput_textarea_get_position(textarea, x, y,
- &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;
-
- box_x += scrollbar_get_offset(textarea->scroll_x);
- box_y += scrollbar_get_offset(textarea->scroll_y);
- scrolled = textinput_ensure_caret_visible(c, textarea);
- box_x -= scrollbar_get_offset(textarea->scroll_x);
- box_y -= scrollbar_get_offset(textarea->scroll_y);
-
- browser_window_place_caret(html->bw,
- box_x + inline_container->x + text_box->x +
- pixel_offset,
- box_y + inline_container->y + text_box->y,
- text_box->height,
- textinput_textarea_callback,
- textinput_textarea_paste_text,
- textinput_textarea_move_caret,
- textarea, c);
-
- if (scrolled)
- html__redraw_a_box(html, textarea);
-}
-
-
-/**
- * Paste a block of text into an input field at the caret position.
- *
- * \param bw browser window
- * \param utf8 pointer to block of text
- * \param utf8_len length (bytes) of text block
- * \param last true iff this is the last chunk (update screen too)
- * \param p1 pointer to input box
- * \param p2 html content with the input box
- * \return true iff successful
- */
-
-bool textinput_input_paste_text(struct browser_window *bw,
- const char *utf8, unsigned utf8_len, bool last,
- void *p1, void *p2)
-{
- struct box *input = p1;
- struct content *c = p2;
- struct box *text_box = input->children->children;
- size_t box_offset = input->gadget->caret_box_offset;
- unsigned int nchars = utf8_length(input->gadget->value);
- const char *ep = utf8 + utf8_len;
- const char *p = utf8;
- bool success = true;
- bool update = last;
-
- /* keep adding chars until we've run out or would exceed
- the maximum length of the field (in which we silently
- ignore all others)
- */
- while (p < ep && nchars < input->gadget->maxlength) {
- char buf[80 + 6];
- int nbytes = 0;
-
- /* how many more chars can we insert in one go? */
- while (p < ep && nbytes < 80 &&
- nchars < input->gadget->maxlength &&
- *p != '\n' && *p != '\r') {
- unsigned len = utf8_next(p, ep - p, 0);
- if (*p == ' ')
- nbytes += utf8_from_ucs4(160, &buf[nbytes]);
- else {
- memcpy(&buf[nbytes], p, len);
- nbytes += len;
- }
-
- p += len;
- nchars++;
- }
-
- if (!textinput_textbox_insert(c, text_box, box_offset,
- buf, nbytes)) {
- /* we still need to update the screen */
- update = true;
- success = false;
- break;
- }
- box_offset += nbytes;
- /* Keep caret_form_offset in sync -- textbox_insert uses this
- * to determine where to insert into the gadget's value */
- input->gadget->caret_form_offset += nbytes;
-
- /* handle CR/LF and LF/CR terminations */
- if (*p == '\n') {
- p++;
- if (*p == '\r') p++;
- }
- else if (*p == '\r') {
- p++;
- if (*p == '\n') p++;
- }
- }
-
- if (update)
- textinput_input_update_display(c, input, box_offset,
- false, true);
-
- return success;
-}
-
-
-/**
- * Move caret to new position after reformatting
- *
- * \param bw browser window
- * \param p1 pointer to text input box
- * \param p2 html content with the input box
- * \return none
- */
-
-void textinput_input_move_caret(struct browser_window *bw,
- void *p1, void *p2)
-{
- struct box *input = (struct box *)p1;
- struct content *c = p2;
- struct box *text_box = input->children->children;
- unsigned int box_offset = input->gadget->caret_box_offset;
- int pixel_offset;
- int box_x, box_y;
- plot_font_style_t fstyle;
-
- font_plot_style_from_css(text_box->style, &fstyle);
-
- box_coords(input, &box_x, &box_y);
-
- nsfont.font_width(&fstyle, text_box->text, box_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,
- textinput_input_callback,
- textinput_input_paste_text,
- textinput_input_move_caret,
- input, c);
-}
-
-/**
- * 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 p1 The input box
- * \param p2 The html content with the input box
- * \return true if the keypress is dealt with, false otherwise. It can
- * return true even if it ran out of memory; this just means that
- * it would have claimed it if it could.
- */
-bool textinput_input_callback(struct browser_window *bw, uint32_t key,
- void *p1, void *p2)
-{
- struct box *input = (struct box *)p1;
- struct content *c = p2;
- html_content *html = (html_content *)c;
- struct box *text_box = input->children->children;
- size_t box_offset = input->gadget->caret_box_offset;
- size_t end_offset;
- 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;
- bool selection_exists = html->sel.defined;
-
- input->gadget->caret_form_offset =
- textinput_get_form_offset(input, text_box, box_offset);
-
- /* update the form offset */
- input->gadget->caret_form_offset =
- textinput_get_form_offset(input, text_box, box_offset);
-
- selection_get_end(&html->sel, &end_offset);
-
- box_coords(input, &box_x, &box_y);
-
- /* normal character insertion */
- if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
- /* have we exceeded max length of input? */
- utf8_len = utf8_length(input->gadget->value);
- if (utf8_len >= input->gadget->maxlength)
- return true;
-
- utf8_len = utf8_from_ucs4(key, utf8);
-
- if (!textinput_textbox_insert(c, text_box, box_offset,
- utf8, utf8_len))
- return true;
-
- box_offset += utf8_len;
-
- changed = true;
-
- } else switch (key) {
- case KEY_DELETE_LEFT:
- {
- int prev_offset, new_offset;
-
- if (selection_exists) {
- textinput_textbox_delete(c, text_box, 0, 0);
- } else {
- /* Can't delete left from text box start */
- if (box_offset == 0)
- return true;
-
- prev_offset = box_offset;
- new_offset = utf8_prev(text_box->text, box_offset);
-
- if (textinput_textbox_delete(c, text_box, new_offset,
- prev_offset - new_offset))
- box_offset = new_offset;
- }
-
- changed = true;
- }
- break;
-
- case KEY_DELETE_RIGHT:
- {
- unsigned next_offset;
-
- if (selection_exists) {
- textinput_textbox_delete(c, text_box, 0, 0);
- } else {
- /* Can't delete right from text box end */
- if (box_offset >= text_box->length)
- return true;
-
- /* Go to the next valid UTF-8 character */
- next_offset = utf8_next(text_box->text,
- text_box->length, box_offset);
-
- textinput_textbox_delete(c, text_box, box_offset,
- next_offset - box_offset);
- }
-
- changed = true;
- }
- break;
-
- case KEY_TAB:
- {
- struct form_control *next_input;
- /* Find next text entry field that is actually
- * displayed (i.e. has an associated box) */
- 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->box);
- next_input = next_input->next)
- ;
- if (!next_input)
- return true;
-
- input = next_input->box;
- box_offset = 0;
- to_textarea = next_input->type == GADGET_TEXTAREA;
- }
- break;
-
- case KEY_NL:
- case KEY_CR: /* Return/Enter hit */
- selection_clear(&html->sel, true);
-
- if (form)
- form_submit(content_get_url(c), bw, form, 0);
- return true;
-
- case KEY_SHIFT_TAB:
- {
- struct form_control *prev_input;
- /* Find previous text entry field that is actually
- * displayed (i.e. has an associated box) */
- 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->box);
- prev_input = prev_input->prev)
- ;
- if (!prev_input)
- return true;
-
- input = prev_input->box;
- box_offset = 0;
- to_textarea = prev_input->type == GADGET_TEXTAREA;
- }
- break;
-
- case KEY_CUT_LINE:
- /* Clear the selection, if one exists */
- if (selection_exists)
- selection_clear(&html->sel, false);
-
- textinput_textarea_cut(c, text_box, 0, text_box,
- text_box->length, false);
- box_offset = 0;
-
- changed = true;
- break;
-
- case KEY_PASTE:
- {
- char *buff = NULL;
- size_t buff_len;
- bool success;
-
- gui_get_clipboard(&buff, &buff_len);
- if (buff == NULL)
- return false;
-
- success = browser_window_paste_text(bw, buff, buff_len, true);
- free(buff);
-
- return success;
- }
-
- case KEY_CUT_SELECTION:
- {
- size_t start_idx, end_idx;
- struct box *start_box =
- selection_get_start(&html->sel, &start_idx);
- struct box *end_box = selection_get_end(&html->sel, &end_idx);
-
- if (start_box && end_box) {
- selection_clear(&html->sel, false);
- textinput_textarea_cut(c, start_box, start_idx,
- end_box, end_idx, true);
-
- box_offset = start_idx;
- changed = true;
- }
- }
- break;
-
- case KEY_RIGHT:
- if (selection_exists) {
- box_offset = end_offset;
- break;
- }
-
- if (box_offset < text_box->length) {
- /* Go to the next valid UTF-8 character */
- box_offset = utf8_next(text_box->text,
- text_box->length, box_offset);
- }
-
- break;
-
- case KEY_LEFT:
- /* If there is a selection, caret should remain at start */
- if (selection_exists)
- break;
-
- /* Go to the previous valid UTF-8 character */
- box_offset = utf8_prev(text_box->text, box_offset);
- break;
-
- case KEY_LINE_START:
- box_offset = 0;
- break;
-
- case KEY_LINE_END:
- box_offset = text_box->length;
- break;
-
- case KEY_WORD_LEFT:
- /* If there is a selection, caret should remain at start */
- if (selection_exists)
- break;
-
- if (!textinput_word_left(text_box->text, &box_offset, NULL))
- box_offset = 0;
-
- break;
-
- case KEY_WORD_RIGHT:
- if (selection_exists) {
- box_offset = end_offset;
- break;
- }
-
- if (!textinput_word_right(text_box->text, text_box->length,
- &box_offset, NULL))
- box_offset = text_box->length;
-
- break;
-
- case KEY_DELETE_LINE_START:
- if (selection_exists)
- selection_clear(&html->sel, true);
-
- if (box_offset == 0)
- return true;
-
- textinput_textarea_cut(c, text_box, 0, text_box,
- box_offset, false);
- box_offset = 0;
-
- changed = true;
- break;
-
- case KEY_DELETE_LINE_END:
- if (selection_exists)
- selection_clear(&html->sel, true);
-
- if (box_offset >= text_box->length)
- return true;
-
- textinput_textarea_cut(c, text_box, box_offset,
- text_box, text_box->length, false);
-
- changed = true;
- break;
-
- default:
- return false;
- }
-
- selection_clear(&html->sel, true);
- textinput_input_update_display(c, input, box_offset,
- to_textarea, changed);
-
- return true;
-}
-
-
-/**
- * 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 textinput_input_click(struct content *c, 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;
- plot_font_style_t fstyle;
- html_content *html = (html_content *)c;
-
- font_plot_style_from_css(text_box->style, &fstyle);
-
- nsfont.font_position_in_string(&fstyle, text_box->text,
- text_box->length, x - text_box->x,
- &char_offset, &pixel_offset);
- assert(char_offset <= text_box->length);
-
- /* Shift the text box horizontally to ensure that the
- * caret position is visible, and ideally centred */
- text_box->x = 0;
- if ((input->width < text_box->width) &&
- (input->width / 2 < pixel_offset)) {
- dx = text_box->x;
- /* Move left so caret is centred */
- text_box->x = input->width / 2 - pixel_offset;
- /* Clamp, so text box's right hand edge coincides
- * with the input's right hand edge */
- 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;
- input->gadget->caret_form_offset =
- textinput_get_form_offset(input, text_box, char_offset);
- input->gadget->caret_pixel_offset = pixel_offset;
-
- browser_window_place_caret(html->bw,
- box_x + input->children->x +
- text_box->x + pixel_offset,
- box_y + input->children->y + text_box->y,
- text_box->height,
- textinput_input_callback,
- textinput_input_paste_text,
- textinput_input_move_caret,
- input, c);
-
- if (dx)
- html__redraw_a_box(html, input);
-}
-
-