summaryrefslogtreecommitdiff
path: root/render
diff options
context:
space:
mode:
Diffstat (limited to 'render')
-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
13 files changed, 645 insertions, 2585 deletions
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);
-}
-
-