summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2013-02-22 12:19:35 +0000
committerMichael Drake <tlsa@netsurf-browser.org>2013-02-22 12:19:35 +0000
commitc2a718075ad321a9cf4678e72645acda5c3471a9 (patch)
treea634c4c47c579aab35839ee3861ef90f92d89b48
parent2b0cc398bb5b8e5dc90fcc0a71a9a154dd9f2d74 (diff)
downloadnetsurf-c2a718075ad321a9cf4678e72645acda5c3471a9.tar.gz
netsurf-c2a718075ad321a9cf4678e72645acda5c3471a9.tar.bz2
A load of refactoring of how content selection and input work.
Keypresses now go via content interface. Contents don't shove the selection object into browser windows any more. Contents report selection existence by sending message. HTML content keeps track of where selections in it exist. Contents report whether they have input focus via caret setting msg. Caret can be hidden (can still input/paste) or removed. Consolidate textarea selection handling. Make textarea report its selection status changes to client. Various textarea fixes. Changed how we decide when to clear selections, and give focus.
-rw-r--r--content/content.c40
-rw-r--r--content/content.h25
-rw-r--r--content/content_protected.h4
-rw-r--r--desktop/browser.c126
-rw-r--r--desktop/browser.h48
-rw-r--r--desktop/browser_private.h20
-rw-r--r--desktop/frames.c2
-rw-r--r--desktop/selection.c128
-rw-r--r--desktop/textarea.c183
-rw-r--r--desktop/textarea.h43
-rw-r--r--desktop/textinput.c80
-rw-r--r--gtk/scaffolding.c14
-rw-r--r--gtk/window.c2
-rw-r--r--render/box_textarea.c66
-rw-r--r--render/box_textarea.h11
-rw-r--r--render/html.c112
-rw-r--r--render/html.h1
-rw-r--r--render/html_interaction.c359
-rw-r--r--render/html_internal.h70
-rw-r--r--render/textplain.c49
-rw-r--r--riscos/save.c22
-rw-r--r--riscos/save.h2
-rw-r--r--riscos/window.c56
-rw-r--r--utils/utf8.c39
-rw-r--r--utils/utf8.h2
25 files changed, 1006 insertions, 498 deletions
diff --git a/content/content.c b/content/content.c
index 1a92e408b..3533d943c 100644
--- a/content/content.c
+++ b/content/content.c
@@ -475,6 +475,26 @@ void content_mouse_action(hlcache_handle *h, struct browser_window *bw,
/**
+ * Handle keypresses.
+ *
+ * \param h Content handle
+ * \param key The UCS4 character codepoint
+ * \return true if key handled, false otherwise
+ */
+
+bool content_keypress(struct hlcache_handle *h, uint32_t key)
+{
+ struct content *c = hlcache_handle_get_content(h);
+ assert(c != NULL);
+
+ if (c->handler->keypress != NULL)
+ return c->handler->keypress(c, key);
+
+ return false;
+}
+
+
+/**
* Request a redraw of an area of a content
*
* \param h high-level cache handle
@@ -738,10 +758,26 @@ void content_close(hlcache_handle *h)
/**
- * Find this content's selection context, if it has one.
+ * Tell a content that any selection it has, or one of its objects has, must be
+ * cleared.
+ */
+
+void content_clear_selection(hlcache_handle *h)
+{
+ struct content *c = hlcache_handle_get_content(h);
+ assert(c != 0);
+
+ if (c->handler->get_selection != NULL)
+ c->handler->clear_selection(c);
+}
+
+
+/**
+ * Get a text selection from a content. Ownership is passed to the caller,
+ * who must free() it.
*/
-struct selection *content_get_selection(hlcache_handle *h)
+char * content_get_selection(hlcache_handle *h)
{
struct content *c = hlcache_handle_get_content(h);
assert(c != 0);
diff --git a/content/content.h b/content/content.h
index 2ae0b38be..3f93d603b 100644
--- a/content/content.h
+++ b/content/content.h
@@ -79,6 +79,8 @@ typedef enum {
CONTENT_MSG_DRAGSAVE, /**< Allow drag saving of content */
CONTENT_MSG_SAVELINK, /**< Allow URL to be saved */
CONTENT_MSG_POINTER, /**< Wants a specific mouse pointer set */
+ CONTENT_MSG_SELECTION, /**< A selection made or cleared */
+ CONTENT_MSG_CARET, /**< Caret movement / hiding */
CONTENT_MSG_DRAG /**< A drag started or ended */
} content_msg;
@@ -153,6 +155,25 @@ union content_msg_data {
} savelink;
/** CONTENT_MSG_POINTER - Mouse pointer to set */
browser_pointer_shape pointer;
+ /** CONTENT_MSG_SELECTION - Selection made or cleared */
+ struct {
+ bool selection; /**< false for selection cleared */
+ bool read_only;
+ } selection;
+ /** CONTENT_MSG_CARET - set caret position or, hide caret */
+ struct {
+ enum {
+ CONTENT_CARET_SET_POS,
+ CONTENT_CARET_HIDE,
+ CONTENT_CARET_REMOVE
+ } type;
+ struct {
+ int x; /**< Carret x-coord */
+ int y; /**< Carret y-coord */
+ int height; /**< Carret height */
+ const struct rect *clip; /**< Carret clip rect */
+ } pos; /**< With CONTENT_CARET_SET_POS */
+ } caret;
/** CONTENT_MSG_DRAG - Drag start or end */
struct {
enum {
@@ -219,12 +240,14 @@ void content_mouse_track(struct hlcache_handle *h, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
void content_mouse_action(struct hlcache_handle *h, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
+bool content_keypress(struct hlcache_handle *h, uint32_t key);
bool content_redraw(struct hlcache_handle *h, struct content_redraw_data *data,
const struct rect *clip, const struct redraw_context *ctx);
void content_open(struct hlcache_handle *h, struct browser_window *bw,
struct content *page, struct object_params *params);
void content_close(struct hlcache_handle *h);
-struct selection *content_get_selection(struct hlcache_handle *h);
+void content_clear_selection(struct hlcache_handle *h);
+char * content_get_selection(struct hlcache_handle *h);
void content_get_contextual_content(struct hlcache_handle *h,
int x, int y, struct contextual_content *data);
bool content_scroll_at_point(struct hlcache_handle *h,
diff --git a/content/content_protected.h b/content/content_protected.h
index ecbe17fc7..8efe763aa 100644
--- a/content/content_protected.h
+++ b/content/content_protected.h
@@ -58,13 +58,15 @@ struct content_handler {
browser_mouse_state mouse, int x, int y);
void (*mouse_action)(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
+ bool (*keypress)(struct content *c, uint32_t key);
bool (*redraw)(struct content *c, struct content_redraw_data *data,
const struct rect *clip,
const struct redraw_context *ctx);
void (*open)(struct content *c, struct browser_window *bw,
struct content *page, struct object_params *params);
void (*close)(struct content *c);
- struct selection * (*get_selection)(struct content *c);
+ void (*clear_selection)(struct content *c);
+ char * (*get_selection)(struct content *c);
void (*get_contextual_content)(struct content *c, int x, int y,
struct contextual_content *data);
bool (*scroll_at_point)(struct content *c, int x, int y,
diff --git a/desktop/browser.c b/desktop/browser.c
index ca72c41a2..fa0e6d121 100644
--- a/desktop/browser.c
+++ b/desktop/browser.c
@@ -428,42 +428,69 @@ struct browser_window * browser_window_get_root(struct browser_window *bw)
}
/* exported interface, documented in browser.h */
-bool browser_window_has_selection(struct browser_window *bw)
+browser_editor_flags browser_window_get_editor_flags(struct browser_window *bw)
{
+ browser_editor_flags ed_flags = BW_EDITOR_NONE;
assert(bw->window);
+ assert(bw->parent == NULL);
- if (bw->cur_sel != NULL && selection_defined(bw->cur_sel)) {
- return true;
- } else {
- return false;
+ if (bw->selection.bw != NULL) {
+ ed_flags |= BW_EDITOR_CAN_COPY;
+
+ if (!bw->selection.read_only)
+ ed_flags |= BW_EDITOR_CAN_CUT;
}
+
+ if (bw->can_edit)
+ ed_flags |= BW_EDITOR_CAN_PASTE;
+
+ return ed_flags;
}
/* exported interface, documented in browser.h */
-void browser_window_set_selection(struct browser_window *bw,
- struct selection *s)
+char * browser_window_get_selection(struct browser_window *bw)
{
assert(bw->window);
+ assert(bw->parent == NULL);
- if (bw->cur_sel != s && bw->cur_sel != NULL) {
- /* Clear any existing selection */
- selection_clear(bw->cur_sel, true);
- }
+ if (bw->selection.bw == NULL ||
+ bw->selection.bw->current_content == NULL)
+ return NULL;
- /* Replace current selection pointer */
- if (s == NULL && bw->current_content != NULL) {
- bw->cur_sel = content_get_selection(bw->current_content);
- } else {
- bw->cur_sel = s;
- }
+ return content_get_selection(bw->selection.bw->current_content);
}
-/* exported interface, documented in browser.h */
-struct selection *browser_window_get_selection(struct browser_window *bw)
+/**
+ * Set or remove a selection.
+ *
+ * \param bw browser window with selection
+ * \param selection true if bw has a selection, false if removing selection
+ * \param read_only true iff selection is read only (e.g. can't cut it)
+ */
+static void browser_window_set_selection(struct browser_window *bw,
+ bool selection, bool read_only)
{
- assert(bw->window);
+ struct browser_window *top;
+
+ assert(bw != NULL);
+
+ top = browser_window_get_root(bw);
+
+ assert(top != NULL);
- return bw->cur_sel;
+ if (bw != top->selection.bw && top->selection.bw != NULL &&
+ top->selection.bw->current_content != NULL) {
+ /* clear old selection */
+ content_clear_selection(top->selection.bw->current_content);
+ }
+
+ if (selection) {
+ top->selection.bw = bw;
+ } else {
+ top->selection.bw = NULL;
+ }
+
+ top->selection.read_only = read_only;
}
/* exported interface, documented in browser.h */
@@ -739,7 +766,6 @@ void browser_window_initialise_common(struct browser_window *bw,
bw->history = history_clone(clone->history);
/* window characteristics */
- bw->cur_sel = NULL;
bw->cur_search = NULL;
bw->refresh_interval = -1;
@@ -919,7 +945,7 @@ nserror browser_window_navigate(struct browser_window *bw,
}
browser_window_stop(bw);
- browser_window_remove_caret(bw);
+ browser_window_remove_caret(bw, false);
browser_window_destroy_children(bw);
LOG(("Loading '%s'", nsurl_access(url)));
@@ -1159,7 +1185,7 @@ browser_window_callback_errorcode(hlcache_handle *c,
bw->loading_content = NULL;
} else if (c == bw->current_content) {
bw->current_content = NULL;
- browser_window_remove_caret(bw);
+ browser_window_remove_caret(bw, false);
}
hlcache_handle_release(c);
@@ -1230,7 +1256,7 @@ nserror browser_window_callback(hlcache_handle *c,
browser_window_get_dimensions(bw, &width, &height, true);
content_reformat(c, false, width, height);
- browser_window_remove_caret(bw);
+ browser_window_remove_caret(bw, false);
if (bw->window)
gui_window_new_content(bw->window);
@@ -1260,11 +1286,6 @@ nserror browser_window_callback(hlcache_handle *c,
}
}
- if (bw->window != NULL) {
- browser_window_set_selection(bw,
- content_get_selection(c));
- }
-
/* frames */
if (content_get_type(c) == CONTENT_HTML &&
html_get_frameset(c) != NULL)
@@ -1315,7 +1336,7 @@ nserror browser_window_callback(hlcache_handle *c,
bw->loading_content = NULL;
else if (c == bw->current_content) {
bw->current_content = NULL;
- browser_window_remove_caret(bw);
+ browser_window_remove_caret(bw, false);
}
hlcache_handle_release(c);
@@ -1355,8 +1376,7 @@ nserror browser_window_callback(hlcache_handle *c,
browser_window_recalculate_iframes(bw);
}
- if (bw->move_callback)
- bw->move_callback(bw, bw->caret_p1, bw->caret_p2);
+ /* TODO: get caret pos redraw */
if (!(event->data.background)) {
/* Reformatted content should be redrawn */
@@ -1515,6 +1535,29 @@ nserror browser_window_callback(hlcache_handle *c,
}
break;
+ case CONTENT_MSG_CARET:
+ switch (event->data.caret.type) {
+ case CONTENT_CARET_REMOVE:
+ browser_window_remove_caret(bw, false);
+ break;
+ case CONTENT_CARET_HIDE:
+ browser_window_remove_caret(bw, true);
+ break;
+ case CONTENT_CARET_SET_POS:
+ browser_window_place_caret(bw,
+ event->data.caret.pos.x,
+ event->data.caret.pos.y,
+ event->data.caret.pos.height);
+ break;
+ }
+ break;
+
+ case CONTENT_MSG_SELECTION:
+ browser_window_set_selection(bw,
+ event->data.selection.selection,
+ event->data.selection.read_only);
+ break;
+
default:
assert(0);
}
@@ -2083,10 +2126,8 @@ void browser_window_destroy_internal(struct browser_window *bw)
if (top->focus == bw)
top->focus = top;
- if (bw->current_content != NULL &&
- top->cur_sel == content_get_selection(
- bw->current_content)) {
- browser_window_set_selection(top, NULL);
+ if (top->selection.bw == bw) {
+ browser_window_set_selection(top, false, false);
}
}
@@ -2734,7 +2775,18 @@ void browser_window_mouse_click(struct browser_window *bw,
switch (content_get_type(c)) {
case CONTENT_HTML:
case CONTENT_TEXTPLAIN:
+ {
+ /* Give bw focus */
+ struct browser_window *root_bw = browser_window_get_root(bw);
+ if (bw != root_bw->focus) {
+ browser_window_remove_caret(bw, false);
+ browser_window_set_selection(bw, false, true);
+ root_bw->focus = bw;
+ }
+
+ /* Pass mouse action to content */
content_mouse_action(c, bw, mouse, x, y);
+ }
break;
default:
if (mouse & BROWSER_MOUSE_MOD_2) {
diff --git a/desktop/browser.h b/desktop/browser.h
index cad8bf706..39f2253bf 100644
--- a/desktop/browser.h
+++ b/desktop/browser.h
@@ -40,14 +40,6 @@ struct history;
struct selection;
struct fetch_multipart_data;
-typedef bool (*browser_caret_callback)(struct browser_window *bw, uint32_t key,
- void *p1, void *p2);
-typedef void (*browser_move_callback)(struct browser_window *bw,
- void *p1, void *p2);
-typedef bool (*browser_paste_callback)(struct browser_window *bw,
- const char *utf8, unsigned utf8_len, bool last,
- void *p1, void *p2);
-
typedef enum {
DRAGGING_NONE,
@@ -60,6 +52,13 @@ typedef enum {
DRAGGING_OTHER
} browser_drag_type;
+typedef enum {
+ BW_EDITOR_NONE = 0, /**< No selection, no editing */
+ BW_EDITOR_CAN_COPY = (1 << 0), /**< Have selection */
+ BW_EDITOR_CAN_CUT = (1 << 1), /**< Selection not read-only */
+ BW_EDITOR_CAN_PASTE = (1 << 2) /**< Can paste, input */
+} browser_editor_flags;
+
extern bool browser_reformat_pending;
/** flags to browser window go */
@@ -206,15 +205,9 @@ bool browser_window_stop_available(struct browser_window *bw);
/* In desktop/textinput.c */
void browser_window_place_caret(struct browser_window *bw,
- int x, int y, int height,
- browser_caret_callback caret_cb,
- browser_paste_callback paste_cb,
- browser_move_callback move_cb,
- void *p1, void *p2);
-void browser_window_remove_caret(struct browser_window *bw);
+ int x, int y, int height);
+void browser_window_remove_caret(struct browser_window *bw, bool only_hide);
bool browser_window_key_press(struct browser_window *bw, uint32_t key);
-bool browser_window_paste_text(struct browser_window *bw, const char *utf8,
- unsigned utf8_len, bool last);
/**
@@ -327,29 +320,22 @@ browser_drag_type browser_window_get_drag_type(struct browser_window *bw);
struct browser_window * browser_window_get_root(struct browser_window *bw);
/**
- * Check whether browser window contains a selection
- *
- * \param bw The browser window
- * \return true if browser window contains a selection
- */
-bool browser_window_has_selection(struct browser_window *bw);
-
-/**
- * Set pointer to current selection. Clears any existing selection.
+ * Check whether browser window can accept a cut/copy/paste, or has a selection
+ * that could be saved.
*
* \param bw The browser window
- * \param s The new selection
+ * \return flags indicating editor flags
*/
-void browser_window_set_selection(struct browser_window *bw,
- struct selection *s);
+browser_editor_flags browser_window_get_editor_flags(struct browser_window *bw);
/**
- * Get the current selection context from a root browser window
+ * Get the current selection from a root browser window, ownership passed to
+ * caller, who must free() it.
*
* \param bw The browser window
- * \return the selection context, or NULL
+ * \return the selected text string, or NULL
*/
-struct selection *browser_window_get_selection(struct browser_window *bw);
+char * browser_window_get_selection(struct browser_window *bw);
/**
diff --git a/desktop/browser_private.h b/desktop/browser_private.h
index 91372acc3..4c14b1700 100644
--- a/desktop/browser_private.h
+++ b/desktop/browser_private.h
@@ -52,18 +52,6 @@ struct browser_window {
/** Window history structure. */
struct history *history;
- /** Handler for keyboard input, or 0. */
- browser_caret_callback caret_callback;
- /** Handler for pasting text, or 0. */
- browser_paste_callback paste_callback;
- /** Handler for repositioning caret, or 0. */
- browser_move_callback move_callback;
-
- /** User parameters for caret_callback, paste_callback, and
- * move_callback */
- void *caret_p1;
- void *caret_p2;
-
/** Platform specific window data. */
struct gui_window *window;
@@ -158,8 +146,12 @@ struct browser_window {
/** Last time a link was followed in this window */
unsigned int last_action;
- /** Current selection, or NULL if none */
- struct selection *cur_sel;
+ /** Current selection */
+ struct {
+ struct browser_window *bw;
+ bool read_only;
+ } selection;
+ bool can_edit;
/** Current context for free text search, or NULL if none */
struct search_context *cur_search;
diff --git a/desktop/frames.c b/desktop/frames.c
index 27085ef3d..dcc66df9d 100644
--- a/desktop/frames.c
+++ b/desktop/frames.c
@@ -219,7 +219,6 @@ void browser_window_create_iframes(struct browser_window *bw,
window->no_resize = true;
window->margin_width = cur->margin_width;
window->margin_height = cur->margin_height;
- window->cur_sel = bw->cur_sel;
window->scale = bw->scale;
if (cur->name) {
window->name = strdup(cur->name);
@@ -338,7 +337,6 @@ void browser_window_create_frameset(struct browser_window *bw,
warn_user("NoMemory", 0);
}
- window->cur_sel = bw->cur_sel;
window->scale = bw->scale;
/* linking */
diff --git a/desktop/selection.c b/desktop/selection.c
index fa82ad027..536df5c84 100644
--- a/desktop/selection.c
+++ b/desktop/selection.c
@@ -77,9 +77,6 @@ static bool redraw_handler(const char *text, size_t length, struct box *box,
size_t whitespace_length);
static void selection_redraw(struct selection *s, unsigned start_idx,
unsigned end_idx);
-static bool save_handler(const char *text, size_t length, struct box *box,
- void *handle, const char *whitespace_text,
- size_t whitespace_length);
static bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
unsigned *start_offset, unsigned *end_offset);
static bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
@@ -285,10 +282,6 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
(mouse & (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2));
int pos = -1; /* 0 = inside selection, 1 = after it */
struct browser_window *top = selection_get_browser_window(s);
-
- if (top == NULL)
- return false; /* not our problem */
-
top = browser_window_get_root(top);
if (selection_defined(s)) {
@@ -309,14 +302,12 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
}
else if (!modkeys) {
if (pos && (mouse & BROWSER_MOUSE_PRESS_1)) {
- /* Clear the selection if mouse is pressed outside the selection,
- * Otherwise clear on release (to allow for drags) */
- browser_window_set_selection(top, s);
+ /* Clear the selection if mouse is pressed outside the
+ * selection, Otherwise clear on release (to allow for drags) */
selection_clear(s, true);
} else if (mouse & BROWSER_MOUSE_DRAG_1) {
/* start new selection drag */
- browser_window_set_selection(top, s);
selection_clear(s, true);
@@ -333,8 +324,6 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
if (!selection_defined(s))
return false; /* ignore Adjust drags */
- browser_window_set_selection(top, s);
-
if (pos >= 0) {
selection_set_end(s, idx);
@@ -907,7 +896,6 @@ void selection_clear(struct selection *s, bool redraw)
{
int old_start, old_end;
bool was_defined;
- struct browser_window *top = selection_get_browser_window(s);
assert(s);
was_defined = selection_defined(s);
@@ -918,13 +906,6 @@ void selection_clear(struct selection *s, bool redraw)
s->start_idx = 0;
s->end_idx = 0;
- if (!top)
- return;
-
- top = browser_window_get_root(top);
-
- gui_clear_selection(top->window);
-
if (redraw && was_defined)
selection_redraw(s, old_start, old_end);
}
@@ -1110,111 +1091,6 @@ bool selection_highlighted(const struct selection *s,
/**
- * Selection traversal handler for saving the text to a file.
- *
- * \param text pointer to text being added, or NULL for newline
- * \param length length of text to be appended (bytes)
- * \param box pointer to text box (or NULL for textplain content)
- * \param handle our save_state workspace pointer
- * \param whitespace_text whitespace to place before text for formatting
- * may be NULL
- * \param whitespace_length length of whitespace_text
- * \return true iff the file writing succeeded and traversal should continue.
- */
-
-bool save_handler(const char *text, size_t length, struct box *box,
- void *handle, const char *whitespace_text,
- size_t whitespace_length)
-{
- struct save_text_state *sv = handle;
- size_t new_length;
- int space = 0;
-
- assert(sv);
-
- if (box && (box->space > 0))
- space = 1;
-
- if (whitespace_text)
- length += whitespace_length;
-
- new_length = sv->length + whitespace_length + length + space;
- if (new_length >= sv->alloc) {
- size_t new_alloc = sv->alloc + (sv->alloc / 4);
- char *new_block;
-
- if (new_alloc < new_length) new_alloc = new_length;
-
- new_block = realloc(sv->block, new_alloc);
- if (!new_block) return false;
-
- sv->block = new_block;
- sv->alloc = new_alloc;
- }
- if (whitespace_text) {
- memcpy(sv->block + sv->length, whitespace_text,
- whitespace_length);
- }
- memcpy(sv->block + sv->length + whitespace_length, text, length);
- sv->length += length;
-
- if (space == 1)
- sv->block[sv->length++] = ' ';
-
- return true;
-}
-
-
-/**
- * Save the given selection to a file.
- *
- * \param s selection object
- * \param path pathname to be used
- * \return true iff the save succeeded
- */
-
-bool selection_save_text(struct selection *s, const char *path)
-{
- struct save_text_state sv = { NULL, 0, 0 };
- utf8_convert_ret ret;
- char *result;
- FILE *out;
-
- if (!selection_traverse(s, save_handler, &sv)) {
- free(sv.block);
- return false;
- }
-
- if (!sv.block)
- return false;
-
- ret = utf8_to_local_encoding(sv.block, sv.length, &result);
- free(sv.block);
-
- if (ret != UTF8_CONVERT_OK) {
- LOG(("failed to convert to local encoding, return %d", ret));
- return false;
- }
-
- out = fopen(path, "w");
- if (out) {
- int res = fputs(result, out);
- if (res < 0) {
- LOG(("Warning: writing data failed"));
- }
-
- res = fputs("\n", out);
- fclose(out);
- free(result);
- return (res != EOF);
- }
- free(result);
-
- return false;
-}
-
-
-/**
* Adjust the selection to reflect a change in the selected text,
* eg. editing in a text area/input field.
*
diff --git a/desktop/textarea.c b/desktop/textarea.c
index 455e9a4ca..2da2c206b 100644
--- a/desktop/textarea.c
+++ b/desktop/textarea.c
@@ -180,6 +180,7 @@ static bool textarea_select(struct textarea *ta, int c_start, int c_end,
bool force_redraw)
{
int swap;
+ bool pre_existing_selection = (ta->sel_start != -1);
struct textarea_msg msg;
/* Ensure start is the beginning of the selection */
@@ -205,6 +206,24 @@ static bool textarea_select(struct textarea *ta, int c_start, int c_end,
ta->callback(ta->data, &msg);
+ if (!pre_existing_selection && ta->sel_start != -1) {
+ /* Didn't have a selection before, but do now */
+ msg.type = TEXTAREA_MSG_SELECTION_REPORT;
+
+ msg.data.selection.have_selection = true;
+ msg.data.selection.read_only = (ta->flags & TEXTAREA_READONLY);
+
+ ta->callback(ta->data, &msg);
+
+ if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Caret hidden, and client is responsible */
+ msg.type = TEXTAREA_MSG_CARET_UPDATE;
+ msg.data.caret.type = TEXTAREA_CARET_HIDE;
+
+ ta->callback(ta->data, &msg);
+ }
+ }
+
return true;
}
@@ -438,11 +457,12 @@ static void textarea_scrollbar_callback(void *client_data,
if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
/* Tell client where caret should be placed */
msg.ta = ta;
- msg.type = TEXTAREA_MSG_MOVED_CARET;
- msg.data.caret.hidden = false;
- msg.data.caret.x = ta->caret_x - ta->scroll_x;
- msg.data.caret.y = ta->caret_y - ta->scroll_y;
- msg.data.caret.height = ta->line_height;
+ msg.type = TEXTAREA_MSG_CARET_UPDATE;
+ msg.data.caret.type = TEXTAREA_CARET_SET_POS;
+ msg.data.caret.pos.x = ta->caret_x - ta->scroll_x;
+ msg.data.caret.pos.y = ta->caret_y - ta->scroll_y;
+ msg.data.caret.pos.height = ta->line_height;
+ msg.data.caret.pos.clip = NULL;
ta->callback(ta->data, &msg);
}
@@ -497,7 +517,7 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
int avail_width;
int h_extent; /* horizontal extent */
int v_extent; /* vertical extent */
- bool restart;
+ bool restart = false;
if (ta->lines == NULL) {
ta->lines =
@@ -781,10 +801,10 @@ static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
line = y / ta->line_height;
- if (line < 0)
- line = 0;
if (ta->line_count - 1 < line)
line = ta->line_count - 1;
+ if (line < 0)
+ line = 0;
/* Get byte position */
nsfont.font_position_in_string(&ta->fstyle,
@@ -798,9 +818,7 @@ static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
* after it. Otherwise, the caret will be placed at the start of the
* following line, which is undesirable.
*/
- if (ta->flags & TEXTAREA_MULTILINE &&
- ta->show->data[ta->lines[line].b_start +
- ta->lines[line].b_length] > 0 &&
+ if (ta->flags & TEXTAREA_MULTILINE && ta->lines[line].b_length > 1 &&
bpos == (unsigned)ta->lines[line].b_length &&
ta->show->data[ta->lines[line].b_start +
ta->lines[line].b_length - 1] == ' ')
@@ -838,9 +856,9 @@ static bool textarea_set_caret_xy(struct textarea *ta, int x, int y)
/**
- * Insert text into the text area
+ * Insert text into the textarea
*
- * \param ta Text area
+ * \param ta Textarea widget
* \param c_index 0-based character index to insert at
* \param text UTF-8 text to insert
* \param b_len Byte length of UTF-8 text
@@ -908,9 +926,9 @@ static inline void textarea_char_to_byte_offset(struct textarea_utf8 *text,
/**
- * Replace text in a text area
+ * Replace text in a textarea
*
- * \param ta Text area
+ * \param ta Textarea widget
* \param start Start character index of replaced section (inclusive)
* \param end End character index of replaced section (exclusive)
* \param rep Replacement UTF-8 text to insert
@@ -1421,11 +1439,12 @@ bool textarea_set_caret(struct textarea *ta, int caret)
if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
/* Tell client where caret should be placed */
msg.ta = ta;
- msg.type = TEXTAREA_MSG_MOVED_CARET;
- msg.data.caret.hidden = false;
- msg.data.caret.x = x - ta->scroll_x;
- msg.data.caret.y = y - ta->scroll_y;
- msg.data.caret.height = ta->line_height;
+ msg.type = TEXTAREA_MSG_CARET_UPDATE;
+ msg.data.caret.type = TEXTAREA_CARET_SET_POS;
+ msg.data.caret.pos.x = x - ta->scroll_x;
+ msg.data.caret.pos.y = y - ta->scroll_y;
+ msg.data.caret.pos.height = ta->line_height;
+ msg.data.caret.pos.clip = NULL;
ta->callback(ta->data, &msg);
}
@@ -1433,8 +1452,8 @@ bool textarea_set_caret(struct textarea *ta, int caret)
} else if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
/* Caret hidden, and client is responsible: tell client */
msg.ta = ta;
- msg.type = TEXTAREA_MSG_MOVED_CARET;
- msg.data.caret.hidden = true;
+ msg.type = TEXTAREA_MSG_CARET_UPDATE;
+ msg.data.caret.type = TEXTAREA_CARET_HIDE;
ta->callback(ta->data, &msg);
}
@@ -1778,8 +1797,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start + 1;
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
} else {
if (!textarea_replace_text(ta,
caret, caret,
@@ -1792,11 +1810,8 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
} else switch (key) {
case KEY_SELECT_ALL:
caret = ta->text.utf8_len;
-
- ta->sel_start = 0;
- ta->sel_end = ta->text.utf8_len;
- redraw = true;
- break;
+ textarea_select(ta, 0, ta->text.utf8_len, true);
+ return true;
case KEY_COPY_SELECTION:
if (ta->sel_start != -1) {
if (!textarea_replace_text(ta,
@@ -1816,15 +1831,15 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start;
- ta->sel_start = ta->sel_end = -1;
+ textarea_clear_selection(ta);
} else if (caret > 0) {
if (!textarea_replace_text(ta,
caret - 1,
caret, "", 0, false))
return false;
caret--;
+ redraw = true;
}
- redraw = true;
break;
case KEY_CR:
case KEY_NL:
@@ -1838,15 +1853,15 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start + 1;
- ta->sel_start = ta->sel_end = -1;
+ textarea_clear_selection(ta);
} else {
if (!textarea_replace_text(ta,
caret, caret,
"\n", 1, false))
return false;
caret++;
+ redraw = true;
}
- redraw = true;
break;
case KEY_DELETE_LINE:
if (readonly)
@@ -1856,7 +1871,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start,
ta->sel_end, "", 0, false))
return false;
- ta->sel_start = ta->sel_end = -1;
+ textarea_clear_selection(ta);
} else {
if (ta->lines[line].b_length != 0) {
/* Delete line */
@@ -1877,8 +1892,8 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
false))
return false;
}
+ redraw = true;
}
- redraw = true;
break;
case KEY_PASTE:
{
@@ -1903,7 +1918,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start + clipboard_chars;
- ta->sel_start = ta->sel_end = -1;
+ textarea_clear_selection(ta);
} else {
if (!textarea_replace_text(ta,
caret, caret,
@@ -1911,8 +1926,8 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
false))
return false;
caret += clipboard_chars;
+ redraw = true;
}
- redraw = true;
free(clipboard);
}
@@ -1927,8 +1942,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start;
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
break;
case KEY_ESCAPE:
@@ -1941,8 +1955,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
if (caret > 0)
caret--;
if (ta->sel_start != -1) {
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
break;
case KEY_RIGHT:
@@ -1951,8 +1964,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
if (caret < ta->text.utf8_len)
caret++;
if (ta->sel_start != -1) {
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
break;
case KEY_PAGE_UP:
@@ -1970,8 +1982,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
if (readonly)
break;
if (ta->sel_start != -1) {
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
if (!(ta->flags & TEXTAREA_MULTILINE))
break;
@@ -2012,16 +2023,14 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
/* -1 because one line is added in KEY_DOWN */
line = ta->caret_pos.line + (ta->vis_height +
ta->line_height - 1) /
- ta->line_height
- - 1;
+ ta->line_height - 1;
}
/* fall through */
case KEY_DOWN:
if (readonly)
break;
if (ta->sel_start != -1) {
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
if (!(ta->flags & TEXTAREA_MULTILINE))
break;
@@ -2065,8 +2074,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start;
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
} else {
if (caret < ta->text.utf8_len) {
if (!textarea_replace_text(ta,
@@ -2082,8 +2090,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
caret -= ta->caret_pos.char_off;
if (ta->sel_start != -1) {
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
break;
case KEY_LINE_END:
@@ -2099,8 +2106,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
- 1] == ' ')
caret--;
if (ta->sel_start != -1) {
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
break;
case KEY_TEXT_START:
@@ -2108,8 +2114,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
caret = 0;
if (ta->sel_start != -1) {
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
break;
case KEY_TEXT_END:
@@ -2117,8 +2122,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
caret = ta->text.utf8_len;
if (ta->sel_start != -1) {
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+ textarea_clear_selection(ta);
}
break;
case KEY_WORD_LEFT:
@@ -2132,7 +2136,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start,
ta->sel_end, "", 0, false))
return false;
- ta->sel_start = ta->sel_end = -1;
+ textarea_clear_selection(ta);
} else {
b_off = ta->lines[line].b_start;
b_len = ta->lines[line].b_length;
@@ -2142,8 +2146,8 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
if (!textarea_replace_text(ta, caret,
caret + l_len, "", 0, false))
return false;
+ redraw = true;
}
- redraw = true;
break;
case KEY_DELETE_LINE_START:
if (readonly)
@@ -2153,15 +2157,15 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start,
ta->sel_end, "", 0, false))
return false;
- ta->sel_start = ta->sel_end = -1;
+ textarea_clear_selection(ta);
} else {
if (!textarea_replace_text(ta,
caret - ta->caret_pos.char_off,
caret, "", 0, false))
return false;
caret -= ta->caret_pos.char_off;
+ redraw = true;
}
- redraw = true;
break;
default:
return false;
@@ -2346,6 +2350,7 @@ bool textarea_clear_selection(struct textarea *ta)
/* No selection to clear */
return false;
+ /* Clear selection and redraw */
ta->sel_start = ta->sel_end = -1;
msg.ta = ta;
@@ -2357,11 +2362,63 @@ bool textarea_clear_selection(struct textarea *ta)
ta->callback(ta->data, &msg);
+ /* No more selection */
+ msg.type = TEXTAREA_MSG_SELECTION_REPORT;
+
+ msg.data.selection.have_selection = false;
+ msg.data.selection.read_only = (ta->flags & TEXTAREA_READONLY);
+
+ ta->callback(ta->data, &msg);
+
+ if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Tell client where caret should be placed */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_CARET_UPDATE;
+ msg.data.caret.type = TEXTAREA_CARET_SET_POS;
+ msg.data.caret.pos.x = ta->caret_x - ta->scroll_x;
+ msg.data.caret.pos.y = ta->caret_y - ta->scroll_y;
+ msg.data.caret.pos.height = ta->line_height;
+ msg.data.caret.pos.clip = NULL;
+
+ ta->callback(ta->data, &msg);
+ }
+
return true;
}
/* exported interface, documented in textarea.h */
+char *textarea_get_selection(struct textarea *ta)
+{
+ char *ret;
+ size_t b_start, b_end, b_len;
+
+ if (ta->sel_start == -1)
+ /* No selection get */
+ return NULL;
+
+ textarea_char_to_byte_offset(ta->show, ta->sel_start, ta->sel_end,
+ &b_start, &b_end);
+
+ b_len = b_end - b_start;
+
+ if (b_len == 0)
+ /* No selection get */
+ return NULL;
+
+ ret = malloc(b_len + 1); /* Add space for '\0' */
+ if (ret == NULL)
+ /* Can't get selection; no memory */
+ return NULL;
+
+ memcpy(ret, ta->show->data + b_start, b_len);
+ ret[b_len] = '\0';
+
+ return ret;
+}
+
+
+/* exported interface, documented in textarea.h */
void textarea_get_dimensions(struct textarea *ta, int *width, int *height)
{
if (width != NULL)
diff --git a/desktop/textarea.h b/desktop/textarea.h
index 3fedeee35..befd6aa99 100644
--- a/desktop/textarea.h
+++ b/desktop/textarea.h
@@ -45,28 +45,39 @@ typedef enum {
TEXTAREA_DRAG_NONE,
TEXTAREA_DRAG_SCROLLBAR,
TEXTAREA_DRAG_SELECTION
-} textarea_drag_type;
+} textarea_drag_type; /**< Textarea drag status */
typedef enum {
TEXTAREA_MSG_DRAG_REPORT, /**< Textarea drag start/end report */
+ TEXTAREA_MSG_SELECTION_REPORT, /**< Textarea text selection presence */
TEXTAREA_MSG_REDRAW_REQUEST, /**< Textarea redraw request */
- TEXTAREA_MSG_MOVED_CARET /**< Textarea caret moved */
+ TEXTAREA_MSG_CARET_UPDATE /**< Textarea caret */
} textarea_msg_type;
struct textarea_msg {
- struct textarea *ta;
+ struct textarea *ta; /**< The textarea widget */
- textarea_msg_type type;
+ textarea_msg_type type; /**< Indicates message data type */
union {
- textarea_drag_type drag;
- struct rect redraw;
+ textarea_drag_type drag; /**< With _DRAG_REPORT */
struct {
- bool hidden;
- int x;
- int y;
- int height;
- } caret;
- } data;
+ bool have_selection; /**< Selection exists */
+ bool read_only; /**< Selection can't be cut */
+ } selection; /**< With _SELECTION_REPORT */
+ struct rect redraw; /**< With _REDRAW_REQUEST */
+ struct {
+ enum {
+ TEXTAREA_CARET_SET_POS, /**< Set coord/height */
+ TEXTAREA_CARET_HIDE /**< Hide */
+ } type;
+ struct {
+ int x; /**< Carret x-coord */
+ int y; /**< Carret y-coord */
+ int height; /**< Carret height */
+ struct rect *clip; /**< Carret clip rect */
+ } pos; /**< With _CARET_SET_POS */
+ } caret; /**< With _CARET_UPDATE */
+ } data; /**< Depends on msg type */
};
typedef struct textarea_setup {
@@ -206,6 +217,14 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
bool textarea_clear_selection(struct textarea *ta);
/**
+ * Get selected text, ownership passed to caller, which needs to free() it.
+ *
+ * \param ta Textarea widget
+ * \return Selected text, or NULL if none.
+ */
+char *textarea_get_selection(struct textarea *ta);
+
+/**
* Gets the dimensions of a textarea
*
* \param ta textarea widget
diff --git a/desktop/textinput.c b/desktop/textinput.c
index 660708932..6ffa02c86 100644
--- a/desktop/textinput.c
+++ b/desktop/textinput.c
@@ -63,11 +63,7 @@
* \param p2 Callback private data pointer, passed to callback function
*/
void browser_window_place_caret(struct browser_window *bw,
- int x, int y, int height,
- browser_caret_callback caret_cb,
- browser_paste_callback paste_cb,
- browser_move_callback move_cb,
- void *p1, void *p2)
+ int x, int y, int height)
{
struct browser_window *root_bw;
int pos_x = 0;
@@ -81,14 +77,10 @@ void browser_window_place_caret(struct browser_window *bw,
y = y * bw->scale + pos_y;
gui_window_place_caret(root_bw->window, x, y, height * bw->scale);
- bw->caret_callback = caret_cb;
- bw->paste_callback = paste_cb;
- bw->move_callback = move_cb;
- bw->caret_p1 = p1;
- bw->caret_p2 = p2;
/* Set focus browser window */
root_bw->focus = bw;
+ root_bw->can_edit = true;
}
@@ -97,20 +89,19 @@ void browser_window_place_caret(struct browser_window *bw,
*
* \param bw The browser window from which to remove caret
*/
-void browser_window_remove_caret(struct browser_window *bw)
+void browser_window_remove_caret(struct browser_window *bw, bool only_hide)
{
struct browser_window *root_bw;
root_bw = browser_window_get_root(bw);
+ if (only_hide)
+ root_bw->can_edit = true;
+ else
+ root_bw->can_edit = false;
+
if (root_bw && root_bw->window)
gui_window_remove_caret(root_bw->window);
-
- bw->caret_callback = NULL;
- bw->paste_callback = NULL;
- bw->move_callback = NULL;
- bw->caret_p1 = NULL;
- bw->caret_p2 = NULL;
}
@@ -127,59 +118,12 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
assert(bw->window != NULL);
- if (focus->caret_callback) {
- /* Pass keypress onto anything that has claimed input focus */
- return focus->caret_callback(focus, key,
- focus->caret_p1, focus->caret_p2);
- }
-
- /* TODO: pass these to content to deal with */
- switch (key) {
- case KEY_COPY_SELECTION:
- selection_copy_to_clipboard(bw->cur_sel);
- return true;
-
- case KEY_CLEAR_SELECTION:
- selection_clear(bw->cur_sel, true);
- return true;
-
- case KEY_SELECT_ALL:
- selection_select_all(bw->cur_sel);
- return true;
-
- case KEY_ESCAPE:
- if (bw->cur_sel && selection_defined(bw->cur_sel)) {
- selection_clear(bw->cur_sel, true);
- return true;
- }
- /* if there's no selection,
- * leave Escape for the caller */
- return false;
- }
-
- return false;
-}
-
-
-/**
- * Paste a block of text into a browser window 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)
- * \return true iff successful
- *
- * TODO: Remove this function.
- */
+ if (focus == NULL)
+ focus = bw;
-bool browser_window_paste_text(struct browser_window *bw, const char *utf8,
- unsigned utf8_len, bool last)
-{
- if (!bw->focus || !bw->focus->paste_callback)
+ if (focus->current_content == NULL)
return false;
- return bw->focus->paste_callback(bw->focus, utf8, utf8_len, last,
- bw->focus->caret_p1, bw->focus->caret_p2);
+ return content_keypress(focus->current_content, key);
}
diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c
index 6ab058269..4097837a9 100644
--- a/gtk/scaffolding.c
+++ b/gtk/scaffolding.c
@@ -374,13 +374,15 @@ static guint nsgtk_scaffolding_update_edit_actions_sensitivity(
} else {
struct browser_window *bw =
nsgtk_get_browser_window(g->top_level);
- has_selection = browser_window_has_selection(bw);
+ browser_editor_flags edit_f =
+ browser_window_get_editor_flags(bw);
- g->buttons[COPY_BUTTON]->sensitivity = has_selection;
- g->buttons[CUT_BUTTON]->sensitivity = (has_selection &&
- bw->caret_callback != 0);
+ g->buttons[COPY_BUTTON]->sensitivity =
+ edit_f & BW_EDITOR_CAN_COPY;
+ g->buttons[CUT_BUTTON]->sensitivity =
+ edit_f & BW_EDITOR_CAN_CUT;
g->buttons[PASTE_BUTTON]->sensitivity =
- (bw->paste_callback != 0);
+ edit_f & BW_EDITOR_CAN_PASTE;
}
nsgtk_scaffolding_set_sensitivity(g);
@@ -1120,7 +1122,7 @@ MULTIHANDLER(selectall)
gtk_editable_select_region(GTK_EDITABLE(g->url_bar), 0, -1);
} else {
LOG(("Selecting all document text"));
- selection_select_all(browser_window_get_selection(bw));
+ browser_window_key_press(bw, KEY_SELECT_ALL);
}
return TRUE;
diff --git a/gtk/window.c b/gtk/window.c
index d8a9dd43d..7a66c5111 100644
--- a/gtk/window.c
+++ b/gtk/window.c
@@ -312,7 +312,7 @@ static gboolean nsgtk_window_button_press_event(GtkWidget *widget,
break;
case 3: /* Right button, usually. Action button, context menu. */
- browser_window_remove_caret(g->bw);
+ browser_window_remove_caret(g->bw, true);
nsgtk_scaffolding_popup_menu(g->scaffold, g->mouse.pressed_x,
g->mouse.pressed_y);
return TRUE;
diff --git a/render/box_textarea.c b/render/box_textarea.c
index 3109904d0..3d9838fa1 100644
--- a/render/box_textarea.c
+++ b/render/box_textarea.c
@@ -31,14 +31,11 @@
#include "utils/log.h"
-static bool box_textarea_browser_caret_callback(struct browser_window *bw,
- uint32_t key, void *p1, void *p2)
+bool box_textarea_keypress(html_content *html, struct box *box, uint32_t key)
{
- 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);
@@ -104,21 +101,6 @@ static bool box_textarea_browser_caret_callback(struct browser_window *bw,
}
-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.
*/
@@ -175,23 +157,43 @@ static void box_textarea_callback(void *data, struct textarea_msg *msg)
html__redraw_a_box(html, box);
break;
- case TEXTAREA_MSG_MOVED_CARET:
+ case TEXTAREA_MSG_SELECTION_REPORT:
+ if (msg->data.selection.have_selection) {
+ /* Textarea now has a selection */
+ union html_selection_owner sel_owner;
+ sel_owner.textarea = box;
+
+ html_set_selection(d->html, HTML_SELECTION_TEXTAREA,
+ sel_owner,
+ msg->data.selection.read_only);
+ } else {
+ /* The textarea now has no selection */
+ union html_selection_owner sel_owner;
+ sel_owner.none = true;
+
+ html_set_selection(d->html, HTML_SELECTION_NONE,
+ sel_owner, true);
+ }
+ break;
+
+ case TEXTAREA_MSG_CARET_UPDATE:
if (html->bw == NULL)
break;
- if (msg->data.caret.hidden) {
- browser_window_remove_caret(html->bw);
+ if (msg->data.caret.type == TEXTAREA_CARET_HIDE) {
+ union html_focus_owner focus_owner;
+ focus_owner.textarea = box;
+ html_set_focus(d->html, HTML_FOCUS_TEXTAREA,
+ focus_owner, true, 0, 0, 0, NULL);
} 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);
+ union html_focus_owner focus_owner;
+ focus_owner.textarea = box;
+ html_set_focus(d->html, HTML_FOCUS_TEXTAREA,
+ focus_owner, false,
+ msg->data.caret.pos.x,
+ msg->data.caret.pos.y,
+ msg->data.caret.pos.height,
+ msg->data.caret.pos.clip);
}
break;
}
diff --git a/render/box_textarea.h b/render/box_textarea.h
index 30414e816..a7a377076 100644
--- a/render/box_textarea.h
+++ b/render/box_textarea.h
@@ -41,4 +41,15 @@ struct dom_node;
bool box_textarea_create_textarea(html_content *html,
struct box *box, struct dom_node *node);
+
+/**
+ * Handle form textarea keypress input
+ *
+ * \param html html content object
+ * \param box box with textarea widget
+ * \param key keypress
+ * \return true iff keypress handled
+ */
+bool box_textarea_keypress(html_content *html, struct box *box, uint32_t key);
+
#endif
diff --git a/render/html.c b/render/html.c
index 4fc152a84..e697c9f12 100644
--- a/render/html.c
+++ b/render/html.c
@@ -346,6 +346,10 @@ html_create_html_data(html_content *c, const http_parameter *params)
c->font_func = &nsfont;
c->drag_type = HTML_DRAG_NONE;
c->drag_owner.no_owner = true;
+ c->selection_type = HTML_SELECTION_NONE;
+ c->selection_owner.none = true;
+ c->focus_type = HTML_FOCUS_SELF;
+ c->focus_owner.self = true;
c->scripts_count = 0;
c->scripts = NULL;
c->jscontext = NULL;
@@ -1309,6 +1313,28 @@ html_object_callback(hlcache_handle *object,
content_broadcast(&c->base, event->type, event->data);
break;
+ case CONTENT_MSG_CARET:
+ {
+ union html_focus_owner focus_owner;
+ focus_owner.content = box;
+
+ switch (event->data.caret.type) {
+ case CONTENT_CARET_REMOVE:
+ case CONTENT_CARET_HIDE:
+ html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner,
+ true, 0, 0, 0, NULL);
+ break;
+ case CONTENT_CARET_SET_POS:
+ html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner,
+ false, event->data.caret.pos.x,
+ event->data.caret.pos.y,
+ event->data.caret.pos.height,
+ event->data.caret.pos.clip);
+ break;
+ }
+ }
+ break;
+
case CONTENT_MSG_DRAG:
{
html_drag_type drag_type = HTML_DRAG_NONE;
@@ -1332,6 +1358,23 @@ html_object_callback(hlcache_handle *object,
}
break;
+ case CONTENT_MSG_SELECTION:
+ {
+ html_selection_type sel_type;
+ union html_selection_owner sel_owner;
+
+ if (event->data.selection.selection) {
+ sel_type = HTML_SELECTION_CONTENT;
+ sel_owner.content = box;
+ } else {
+ sel_type = HTML_SELECTION_NONE;
+ sel_owner.none = true;
+ }
+ html_set_selection(c, sel_type, sel_owner,
+ event->data.selection.read_only);
+ }
+ break;
+
default:
assert(0);
}
@@ -1514,6 +1557,10 @@ html_convert_css_callback(hlcache_handle *css,
}
break;
+ case CONTENT_MSG_POINTER:
+ /* Really don't want this to continue after the switch */
+ return NSERROR_OK;
+
default:
assert(0);
}
@@ -2513,8 +2560,13 @@ html_open(struct content *c,
html->bw = bw;
html->page = (html_content *) page;
+ html->drag_type = HTML_DRAG_NONE;
+ html->drag_owner.no_owner = true;
+
/* text selection */
selection_init(&html->sel, html->layout);
+ html->selection_type = HTML_SELECTION_NONE;
+ html->selection_owner.none = true;
for (object = html->object_list; object != NULL; object = next) {
next = object->next;
@@ -2541,6 +2593,8 @@ static void html_close(struct content *c)
html_content *html = (html_content *) c;
struct content_html_object *object, *next;
+ selection_clear(&html->sel, false);
+
if (html->search != NULL)
search_destroy_context(html->search);
@@ -2567,11 +2621,63 @@ static void html_close(struct content *c)
* Return an HTML content's selection context
*/
-static struct selection *html_get_selection(struct content *c)
+static void html_clear_selection(struct content *c)
+{
+ html_content *html = (html_content *) c;
+
+ switch (html->selection_type) {
+ case HTML_SELECTION_NONE:
+ /* Nothing to do */
+ assert(html->selection_owner.none == true);
+ break;
+ case HTML_SELECTION_TEXTAREA:
+ textarea_clear_selection(html->selection_owner.textarea->
+ gadget->data.text.ta);
+ break;
+ case HTML_SELECTION_SELF:
+ assert(html->selection_owner.none == false);
+ selection_clear(&html->sel, true);
+ break;
+ case HTML_SELECTION_CONTENT:
+ content_clear_selection(html->selection_owner.content->object);
+ break;
+ default:
+ break;
+ }
+
+ /* There is no selection now. */
+ html->selection_type = HTML_SELECTION_NONE;
+ html->selection_owner.none = true;
+}
+
+
+/**
+ * Return an HTML content's selection context
+ */
+
+static char *html_get_selection(struct content *c)
{
html_content *html = (html_content *) c;
- return &html->sel;
+ switch (html->selection_type) {
+ case HTML_SELECTION_TEXTAREA:
+ return textarea_get_selection(html->selection_owner.textarea->
+ gadget->data.text.ta);
+ case HTML_SELECTION_SELF:
+ assert(html->selection_owner.none == false);
+ return selection_get_copy(&html->sel);
+ case HTML_SELECTION_CONTENT:
+ return content_get_selection(
+ html->selection_owner.content->object);
+ case HTML_SELECTION_NONE:
+ /* Nothing to do */
+ assert(html->selection_owner.none == true);
+ break;
+ default:
+ break;
+ }
+
+ return NULL;
}
@@ -3214,10 +3320,12 @@ static const content_handler html_content_handler = {
.stop = html_stop,
.mouse_track = html_mouse_track,
.mouse_action = html_mouse_action,
+ .keypress = html_keypress,
.redraw = html_redraw,
.open = html_open,
.close = html_close,
.get_selection = html_get_selection,
+ .clear_selection = html_clear_selection,
.get_contextual_content = html_get_contextual_content,
.scroll_at_point = html_scroll_at_point,
.drop_file_at_point = html_drop_file_at_point,
diff --git a/render/html.h b/render/html.h
index a9f7967f6..c208f47d6 100644
--- a/render/html.h
+++ b/render/html.h
@@ -50,6 +50,7 @@ struct textarea;
struct scrollbar;
struct scrollbar_msg_data;
struct search_context;
+struct selection;
/**
* Container for stylesheets used by an HTML document
diff --git a/render/html_interaction.c b/render/html_interaction.c
index 3f1bee475..d734c6b9f 100644
--- a/render/html_interaction.c
+++ b/render/html_interaction.c
@@ -223,54 +223,7 @@ static size_t html_selection_drag_end(struct html_content *html,
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;
- union html_drag_owner drag_owner;
-
- if (html->drag_type == HTML_DRAG_SELECTION && !mouse) {
- /* End of selection drag */
- int dir = -1;
- size_t idx;
-
- if (selection_dragging_start(&html->sel))
- dir = 1;
-
- idx = html_selection_drag_end(html, mouse, x, y, dir);
-
- if (idx != 0)
- selection_track(&html->sel, mouse, idx);
-
- drag_owner.no_owner = true;
- html_set_drag_type(html, HTML_DRAG_NONE, drag_owner, NULL);
- }
-
- 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;
-
- box = box_pick_text_box(html, x, y, dir, &dx, &dy);
-
- if (box != NULL) {
- int pixel_offset;
- size_t idx;
- plot_font_style_t fstyle;
-
- font_plot_style_from_css(box->style, &fstyle);
-
- nsfont.font_position_in_string(&fstyle,
- box->text, box->length,
- dx, &idx, &pixel_offset);
-
- selection_track(&html->sel, mouse,
- box->byte_offset + idx);
- }
- } else {
- html_mouse_action(c, bw, mouse, x, y);
- }
+ html_mouse_action(c, bw, mouse, x, y);
}
@@ -323,6 +276,11 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
browser_drag_type drag_type = browser_window_get_drag_type(bw);
union content_msg_data msg_data;
struct dom_node *node = NULL;
+ union html_drag_owner drag_owner;
+ union html_selection_owner sel_owner;
+ bool click = mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2 |
+ BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2);
if (drag_type != DRAGGING_NONE && !mouse &&
html->visible_select_menu != NULL) {
@@ -349,7 +307,55 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
&width, &height);
html->visible_select_menu = NULL;
browser_window_redraw_rect(bw, box_x, box_y,
- width, height);
+ width, height);
+ }
+ return;
+ }
+
+ if (html->drag_type == HTML_DRAG_SELECTION) {
+ /* Selection drag */
+
+ if (!mouse) {
+ /* End of selection drag */
+ int dir = -1;
+ size_t idx;
+
+ if (selection_dragging_start(&html->sel))
+ dir = 1;
+
+ idx = html_selection_drag_end(html, mouse, x, y, dir);
+
+ if (idx != 0)
+ selection_track(&html->sel, mouse, idx);
+
+ drag_owner.no_owner = true;
+ html_set_drag_type(html, HTML_DRAG_NONE,
+ drag_owner, NULL);
+ return;
+ }
+
+ struct box *box;
+ int dir = -1;
+ int dx, dy;
+
+ if (selection_dragging_start(&html->sel))
+ dir = 1;
+
+ box = box_pick_text_box(html, x, y, dir, &dx, &dy);
+
+ if (box != NULL) {
+ int pixel_offset;
+ size_t idx;
+ plot_font_style_t fstyle;
+
+ font_plot_style_from_css(box->style, &fstyle);
+
+ nsfont.font_position_in_string(&fstyle,
+ box->text, box->length,
+ dx, &idx, &pixel_offset);
+
+ selection_track(&html->sel, mouse,
+ box->byte_offset + idx);
}
return;
}
@@ -413,6 +419,16 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
return;
}
+ if (html->drag_type == HTML_DRAG_CONTENT_SELECTION) {
+ 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 */
assert(html->drag_type == HTML_DRAG_NONE);
@@ -536,7 +552,8 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
/* mouse inside padding box */
if ((box->scroll_y != NULL) &&
- (x > (padding_right - SCROLLBAR_WIDTH))) {
+ (x > (padding_right -
+ SCROLLBAR_WIDTH))) {
/* mouse above vertical box scroll */
scrollbar = box->scroll_y;
@@ -546,7 +563,8 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
break;
} else if ((box->scroll_x != NULL) &&
- (y > (padding_bottom - SCROLLBAR_WIDTH))) {
+ (y > (padding_bottom -
+ SCROLLBAR_WIDTH))) {
/* mouse above horizontal box scroll */
scrollbar = box->scroll_x;
@@ -628,6 +646,15 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
pointer = get_pointer_shape(gadget_box, false);
+ if (click && (html->selection_type !=
+ HTML_SELECTION_TEXTAREA ||
+ html->selection_owner.textarea !=
+ gadget_box)) {
+ sel_owner.none = true;
+ html_set_selection(html, HTML_SELECTION_NONE,
+ sel_owner, true);
+ }
+
textarea_mouse_action(gadget->data.text.ta, mouse,
x - gadget_box_x, y - gadget_box_y);
break;
@@ -679,6 +706,14 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
x - pos_x, y - pos_y);
}
} else if (html_object_box) {
+
+ if (click && (html->selection_type != HTML_SELECTION_CONTENT ||
+ html->selection_owner.content !=
+ html_object_box)) {
+ sel_owner.none = true;
+ html_set_selection(html, HTML_SELECTION_NONE,
+ sel_owner, true);
+ }
if (mouse & BROWSER_MOUSE_CLICK_1 ||
mouse & BROWSER_MOUSE_CLICK_2) {
content_mouse_action(html_object_box->object,
@@ -738,11 +773,18 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
/* if clicking in the main page, remove the selection from any
* text areas */
if (!done) {
- struct box *layout = html->layout;
-
- if (mouse && (mouse < BROWSER_MOUSE_MOD_1) &&
- selection_root(&html->sel) != layout) {
- selection_init(&html->sel, layout);
+
+ if (click && html->focus_type != HTML_FOCUS_SELF) {
+ union html_focus_owner fo;
+ fo.self = true;
+ html_set_focus(html, HTML_FOCUS_SELF, fo,
+ true, 0, 0, 0, NULL);
+ }
+ if (click && html->selection_type !=
+ HTML_SELECTION_SELF) {
+ sel_owner.none = true;
+ html_set_selection(html, HTML_SELECTION_NONE,
+ sel_owner, true);
}
if (text_box) {
@@ -781,8 +823,24 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
done = true;
}
- } else if (mouse & BROWSER_MOUSE_PRESS_1)
+ } else if (mouse & BROWSER_MOUSE_PRESS_1) {
+ union html_selection_owner sel_owner;
+ sel_owner.none = true;
selection_clear(&html->sel, true);
+ }
+
+ if (selection_defined(&html->sel)) {
+ sel_owner.none = false;
+ html_set_selection(html, HTML_SELECTION_SELF,
+ sel_owner,
+ selection_read_only(
+ &html->sel));
+ } else if (click && html->selection_type !=
+ HTML_SELECTION_NONE) {
+ sel_owner.none = true;
+ html_set_selection(html, HTML_SELECTION_NONE,
+ sel_owner, true);
+ }
}
if (!done) {
@@ -832,7 +890,10 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
}
if (mouse && mouse < BROWSER_MOUSE_MOD_1) {
/* ensure key presses still act on the browser window */
- browser_window_remove_caret(bw);
+ union html_focus_owner fo;
+ fo.self = true;
+ html_set_focus(html, HTML_FOCUS_SELF, fo,
+ true, 0, 0, 0, NULL);
}
}
@@ -876,6 +937,61 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
/**
+ * Handle keypresses.
+ *
+ * \param c content of type CONTENT_TEXTPLAIN
+ * \param key The UCS4 character codepoint
+ * \return true if key handled, false otherwise
+ */
+
+bool html_keypress(struct content *c, uint32_t key)
+{
+ html_content *html = (html_content *) c;
+ struct selection *sel = &html->sel;
+ struct box *box;
+
+ switch (html->focus_type) {
+ case HTML_FOCUS_CONTENT:
+ box = html->focus_owner.content;
+ return content_keypress(box->object, key);
+
+ case HTML_FOCUS_TEXTAREA:
+ box = html->focus_owner.textarea;
+ return textarea_keypress(box->gadget->data.text.ta, key);
+
+ default:
+ /* Deal with it below */
+ break;
+ }
+
+ switch (key) {
+ case KEY_COPY_SELECTION:
+ selection_copy_to_clipboard(sel);
+ return true;
+
+ case KEY_CLEAR_SELECTION:
+ selection_clear(sel, true);
+ return true;
+
+ case KEY_SELECT_ALL:
+ selection_select_all(sel);
+ return true;
+
+ case KEY_ESCAPE:
+ if (selection_defined(sel)) {
+ selection_clear(sel, true);
+ return true;
+ }
+
+ /* if there's no selection, leave Escape for the caller */
+ return false;
+ }
+
+ return false;
+}
+
+
+/**
* Callback for in-page scrollbars.
*/
void html_overflow_scroll_callback(void *client_data,
@@ -985,6 +1101,131 @@ void html_set_drag_type(html_content *html, html_drag_type drag_type,
}
msg_data.drag.rect = rect;
- /* Inform the content's drag status change */
+ /* Inform of the content's drag status change */
content_broadcast((struct content *)html, CONTENT_MSG_DRAG, msg_data);
}
+
+/* Documented in html_internal.h */
+void html_set_focus(html_content *html, html_focus_type focus_type,
+ union html_focus_owner focus_owner, bool hide_caret,
+ int x, int y, int height, const struct rect *clip)
+{
+ union content_msg_data msg_data;
+ int x_off = 0;
+ int y_off = 0;
+ bool textarea_lost_focus = html->focus_type == HTML_FOCUS_TEXTAREA &&
+ focus_type != HTML_FOCUS_TEXTAREA;
+
+ assert(html != NULL);
+
+ switch (focus_type) {
+ case HTML_FOCUS_SELF:
+ assert(focus_owner.self == true);
+ if (html->focus_type == HTML_FOCUS_SELF)
+ /* Don't need to tell anyone anything */
+ return;
+ break;
+
+ case HTML_FOCUS_CONTENT:
+ box_coords(focus_owner.content, &x_off, &y_off);
+ break;
+
+ case HTML_FOCUS_TEXTAREA:
+ box_coords(focus_owner.textarea, &x_off, &y_off);
+ break;
+ }
+
+ html->focus_type = focus_type;
+ html->focus_owner = focus_owner;
+
+ if (textarea_lost_focus) {
+ msg_data.caret.type = CONTENT_CARET_REMOVE;
+ } else if (focus_type != HTML_FOCUS_SELF && hide_caret) {
+ msg_data.caret.type = CONTENT_CARET_HIDE;
+ } else {
+ msg_data.caret.type = CONTENT_CARET_SET_POS;
+ msg_data.caret.pos.x = x + x_off;
+ msg_data.caret.pos.y = y + y_off;
+ msg_data.caret.pos.height = height;
+ msg_data.caret.pos.clip = clip;
+ }
+
+ /* Inform of the content's drag status change */
+ content_broadcast((struct content *)html, CONTENT_MSG_CARET, msg_data);
+}
+
+/* Documented in html_internal.h */
+void html_set_selection(html_content *html, html_selection_type selection_type,
+ union html_selection_owner selection_owner, bool read_only)
+{
+ union content_msg_data msg_data;
+ struct box *box;
+ bool changed = false;
+ bool same_type = html->selection_type == selection_type;
+
+ assert(html != NULL);
+
+ if ((selection_type == HTML_SELECTION_NONE &&
+ html->selection_type != HTML_SELECTION_NONE) ||
+ (selection_type != HTML_SELECTION_NONE &&
+ html->selection_type == HTML_SELECTION_NONE))
+ /* Existance of selection has changed, and we'll need to
+ * inform our owner */
+ changed = true;
+
+ /* Clear any existing selection */
+ if (html->selection_type != HTML_SELECTION_NONE) {
+ switch (html->selection_type) {
+ case HTML_SELECTION_SELF:
+ if (same_type)
+ break;
+ selection_clear(&html->sel, true);
+ break;
+ case HTML_SELECTION_TEXTAREA:
+ if (same_type && html->selection_owner.textarea ==
+ selection_owner.textarea)
+ break;
+ box = html->selection_owner.textarea;
+ textarea_clear_selection(box->gadget->data.text.ta);
+ break;
+ case HTML_SELECTION_CONTENT:
+ if (same_type && html->selection_owner.content ==
+ selection_owner.content)
+ break;
+ box = html->selection_owner.content;
+ content_clear_selection(box->object);
+ break;
+ default:
+ break;
+ }
+ }
+
+ html->selection_type = selection_type;
+ html->selection_owner = selection_owner;
+
+ if (!changed)
+ /* Don't need to report lack of change to owner */
+ return;
+
+ /* Prepare msg */
+ switch (selection_type) {
+ case HTML_SELECTION_NONE:
+ assert(selection_owner.none == true);
+ msg_data.selection.selection = false;
+ break;
+ case HTML_SELECTION_SELF:
+ assert(selection_owner.none == false);
+ /* fall through */
+ case HTML_SELECTION_TEXTAREA:
+ case HTML_SELECTION_CONTENT:
+ msg_data.selection.selection = true;
+ break;
+ default:
+ break;
+ }
+ msg_data.selection.read_only = read_only;
+
+ /* Inform of the content's selection status change */
+ content_broadcast((struct content *)html, CONTENT_MSG_SELECTION,
+ msg_data);
+}
diff --git a/render/html_internal.h b/render/html_internal.h
index 3e562bc39..80b126b25 100644
--- a/render/html_internal.h
+++ b/render/html_internal.h
@@ -43,6 +43,29 @@ union html_drag_owner {
struct box *textarea;
}; /**< For drags we don't own */
+typedef enum {
+ HTML_SELECTION_NONE, /** No selection */
+ HTML_SELECTION_TEXTAREA, /** Selection in one of our textareas */
+ HTML_SELECTION_SELF, /** Selection in this html content */
+ HTML_SELECTION_CONTENT /** Selection in child content */
+} html_selection_type;
+union html_selection_owner {
+ bool none;
+ struct box *textarea;
+ struct box *content;
+}; /**< For getting at selections in this content or things in this content */
+
+typedef enum {
+ HTML_FOCUS_SELF, /** Focus is our own */
+ HTML_FOCUS_CONTENT, /** Focus belongs to child content */
+ HTML_FOCUS_TEXTAREA /** Focus belongs to textarea */
+} html_focus_type;
+union html_focus_owner {
+ bool self;
+ struct box *textarea;
+ struct box *content;
+}; /**< For directing input */
+
/** Data specific to CONTENT_HTML. */
typedef struct html_content {
struct content base;
@@ -114,18 +137,28 @@ typedef struct html_content {
* object within a page. */
struct html_content *page;
- /* Current drag type */
+ /** Current drag type */
html_drag_type drag_type;
/** Widget capturing all mouse events */
union html_drag_owner drag_owner;
+ /** Current selection state */
+ html_selection_type selection_type;
+ /** Current selection owner */
+ union html_selection_owner selection_owner;
+
+ /** Current input focus target type */
+ html_focus_type focus_type;
+ /** Current input focus target */
+ union html_focus_owner focus_owner;
+
+ /** HTML content's own text selection object */
+ struct selection sel;
+
/** Open core-handled form SELECT menu,
* or NULL if none currently open. */
struct form_control *visible_select_menu;
- /** Selection state */
- struct selection sel;
-
/** Context for free text search, or NULL if none */
struct search_context *search;
@@ -152,6 +185,34 @@ void html__redraw_a_box(html_content *html, struct box *box);
void html_set_drag_type(html_content *html, html_drag_type drag_type,
union html_drag_owner drag_owner, const struct rect *rect);
+/**
+ * Set our selection status, and inform whatever owns the content
+ *
+ * \param html HTML content
+ * \param selection_type Type of selection
+ * \param selection_owner What owns the selection
+ * \param read_only True iff selection is read only
+ */
+void html_set_selection(html_content *html, html_selection_type selection_type,
+ union html_selection_owner selection_owner, bool read_only);
+
+/**
+ * Set our input focus, and inform whatever owns the content
+ *
+ * \param html HTML content
+ * \param focus_type Type of input focus
+ * \param focus_owner What owns the focus
+ * \param hide_caret True iff caret to be hidden
+ * \param x Carret x-coord rel to owner
+ * \param y Carret y-coord rel to owner
+ * \param height Carret height
+ * \param clip Carret clip rect
+ */
+void html_set_focus(html_content *html, html_focus_type focus_type,
+ union html_focus_owner focus_owner, bool hide_caret,
+ int x, int y, int height, const struct rect *clip);
+
+
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);
@@ -179,6 +240,7 @@ void html_mouse_track(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
void html_mouse_action(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
+bool html_keypress(struct content *c, uint32_t key);
void html_overflow_scroll_callback(void *client_data,
struct scrollbar_msg_data *scrollbar_data);
diff --git a/render/textplain.c b/render/textplain.c
index 6cf334fe2..b9d0b81e5 100644
--- a/render/textplain.c
+++ b/render/textplain.c
@@ -40,6 +40,7 @@
#include "desktop/plotters.h"
#include "desktop/search.h"
#include "desktop/selection.h"
+#include "desktop/textinput.h"
#include "render/font.h"
#include "render/search.h"
#include "render/textplain.h"
@@ -107,6 +108,7 @@ static void textplain_mouse_track(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
static void textplain_mouse_action(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
+static bool textplain_keypress(struct content *c, uint32_t key);
static void textplain_reformat(struct content *c, int width, int height);
static void textplain_destroy(struct content *c);
static bool textplain_redraw(struct content *c, struct content_redraw_data *data,
@@ -114,7 +116,7 @@ static bool textplain_redraw(struct content *c, struct content_redraw_data *data
static void textplain_open(struct content *c, struct browser_window *bw,
struct content *page, struct object_params *params);
void textplain_close(struct content *c);
-struct selection *textplain_get_selection(struct content *c);
+char *textplain_get_selection(struct content *c);
struct search_context *textplain_get_search(struct content *c);
static nserror textplain_clone(const struct content *old,
struct content **newc);
@@ -139,6 +141,7 @@ static const content_handler textplain_content_handler = {
.destroy = textplain_destroy,
.mouse_track = textplain_mouse_track,
.mouse_action = textplain_mouse_action,
+ .keypress = textplain_keypress,
.redraw = textplain_redraw,
.open = textplain_open,
.close = textplain_close,
@@ -716,6 +719,46 @@ void textplain_mouse_action(struct content *c, struct browser_window *bw,
/**
+ * Handle keypresses.
+ *
+ * \param c content of type CONTENT_TEXTPLAIN
+ * \param key The UCS4 character codepoint
+ * \return true if key handled, false otherwise
+ */
+
+bool textplain_keypress(struct content *c, uint32_t key)
+{
+ textplain_content *text = (textplain_content *) c;
+ struct selection *sel = &text->sel;
+
+ switch (key) {
+ case KEY_COPY_SELECTION:
+ selection_copy_to_clipboard(sel);
+ return true;
+
+ case KEY_CLEAR_SELECTION:
+ selection_clear(sel, true);
+ return true;
+
+ case KEY_SELECT_ALL:
+ selection_select_all(sel);
+ return true;
+
+ case KEY_ESCAPE:
+ if (selection_defined(sel)) {
+ selection_clear(sel, true);
+ return true;
+ }
+
+ /* if there's no selection, leave Escape for the caller */
+ return false;
+ }
+
+ return false;
+}
+
+
+/**
* Draw a CONTENT_TEXTPLAIN using the current set of plotters (plot).
*
* \param c content of type CONTENT_TEXTPLAIN
@@ -893,11 +936,11 @@ void textplain_close(struct content *c)
* Return an textplain content's selection context
*/
-struct selection *textplain_get_selection(struct content *c)
+char *textplain_get_selection(struct content *c)
{
textplain_content *text = (textplain_content *) c;
- return &text->sel;
+ return selection_get_copy(&text->sel);
}
diff --git a/riscos/save.c b/riscos/save.c
index 12af6ab22..549a2ab3e 100644
--- a/riscos/save.c
+++ b/riscos/save.c
@@ -79,7 +79,7 @@
static gui_save_type gui_save_current_type;
static hlcache_handle *gui_save_content = NULL;
-static struct selection *gui_save_selection = NULL;
+static char *gui_save_selection = NULL;
static const char *gui_save_url = NULL;
static const char *gui_save_title = NULL;
static int gui_save_filetype;
@@ -246,7 +246,7 @@ void ro_gui_saveas_quit(void)
*/
void ro_gui_save_prepare(gui_save_type save_type, hlcache_handle *h,
- struct selection *s, const char *url, const char *title)
+ char *s, const char *url, const char *title)
{
char name_buf[FILENAME_MAX];
size_t leaf_offset = 0;
@@ -259,6 +259,9 @@ void ro_gui_save_prepare(gui_save_type save_type, hlcache_handle *h,
(save_type == GUI_SAVE_HISTORY_EXPORT_HTML) ||
(save_type == GUI_SAVE_TEXT_SELECTION) || h);
+ if (gui_save_selection == NULL)
+ free(gui_save_selection);
+
gui_save_selection = s;
gui_save_url = url;
gui_save_title = title;
@@ -414,7 +417,11 @@ void gui_drag_save_selection(struct selection *s, struct gui_window *g)
return;
}
- gui_save_selection = s;
+
+ if (gui_save_selection == NULL)
+ free(gui_save_selection);
+
+ gui_save_selection = selection_get_copy(s);
ro_gui_save_set_state(NULL, GUI_SAVE_TEXT_SELECTION, NULL,
save_leafname, LEAFNAME_MAX,
@@ -890,8 +897,15 @@ bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite)
break;
case GUI_SAVE_TEXT_SELECTION:
- if (!selection_save_text(gui_save_selection, path))
+ if (gui_save_selection == NULL)
+ return false;
+ if (!utf8_save_text(gui_save_selection, path)) {
+ free(gui_save_selection);
+ gui_save_selection = NULL;
return false;
+ }
+ free(gui_save_selection);
+ gui_save_selection = NULL;
xosfile_set_type(path, 0xfff);
break;
diff --git a/riscos/save.h b/riscos/save.h
index c9a0f13e8..b219de4c8 100644
--- a/riscos/save.h
+++ b/riscos/save.h
@@ -30,7 +30,7 @@
wimp_w ro_gui_saveas_create(const char *template_name);
void ro_gui_saveas_quit(void);
void ro_gui_save_prepare(gui_save_type save_type, struct hlcache_handle *h,
- struct selection *s, const char *url,
+ char *s, const char *url,
const char *title);
void ro_gui_save_start_drag(wimp_pointer *pointer);
void ro_gui_drag_save_link(gui_save_type save_type, const char *url,
diff --git a/riscos/window.c b/riscos/window.c
index d82e855ac..0183dbb54 100644
--- a/riscos/window.c
+++ b/riscos/window.c
@@ -145,7 +145,7 @@ static void ro_gui_window_save_toolbar_buttons(void *data, char *config);
static void ro_gui_window_update_theme(void *data, bool ok);
static bool ro_gui_window_import_text(struct gui_window *g,
- const char *filename, bool toolbar);
+ const char *filename);
static void ro_gui_window_clone_options(struct browser_window *new_bw,
struct browser_window *old_bw);
@@ -2167,11 +2167,13 @@ bool ro_gui_window_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
hlcache_handle *h = NULL;
bool export_sprite, export_draw;
os_coord pos;
+ browser_editor_flags editor_flags;
g = (struct gui_window *) ro_gui_wimp_event_get_user_data(w);
toolbar = g->toolbar;
bw = g->bw;
h = g->bw->current_content;
+ editor_flags = browser_window_get_editor_flags(bw);
/* If this is the form select menu, handle it now and then exit.
* Otherwise, carry on to the main browser window menu.
@@ -2327,20 +2329,19 @@ bool ro_gui_window_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
* be selected */
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_SAVE,
- !browser_window_has_selection(bw));
+ editor_flags & ~BW_EDITOR_CAN_COPY);
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_COPY,
- !browser_window_has_selection(bw));
+ editor_flags & ~BW_EDITOR_CAN_COPY);
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_CUT,
- !browser_window_has_selection(bw) ||
- selection_read_only(browser_window_get_selection(bw)));
+ editor_flags & ~BW_EDITOR_CAN_CUT);
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_PASTE,
- h == NULL || bw->paste_callback == NULL);
+ editor_flags & ~BW_EDITOR_CAN_PASTE);
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_CLEAR,
- !browser_window_has_selection(bw));
+ editor_flags & ~BW_EDITOR_CAN_COPY);
/* Navigate Submenu */
@@ -2477,7 +2478,7 @@ void ro_gui_window_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
break;
case BROWSER_SELECTION_SAVE:
- if (browser_window_has_selection(bw))
+ if (browser_window_get_editor_flags(bw) & BW_EDITOR_CAN_COPY)
ro_gui_save_prepare(GUI_SAVE_TEXT_SELECTION, NULL,
browser_window_get_selection(bw),
NULL, NULL);
@@ -3722,14 +3723,15 @@ void ro_gui_window_toolbar_click(void *data,
bool ro_gui_toolbar_dataload(struct gui_window *g, wimp_message *message)
{
if (message->data.data_xfer.file_type == osfile_TYPE_TEXT &&
- ro_gui_window_import_text(g, message->data.data_xfer.file_name, true)) {
-
+ ro_gui_window_import_text(g,
+ message->data.data_xfer.file_name)) {
os_error *error;
/* send DataLoadAck */
message->action = message_DATA_LOAD_ACK;
message->your_ref = message->my_ref;
- error = xwimp_send_message(wimp_USER_MESSAGE, message, message->sender);
+ error = xwimp_send_message(wimp_USER_MESSAGE, message,
+ message->sender);
if (error) {
LOG(("xwimp_send_message: 0x%x: %s\n",
error->errnum, error->errmess));
@@ -4546,14 +4548,15 @@ void ro_gui_window_update_theme(void *data, bool ok)
* \return true iff successful
*/
-bool ro_gui_window_import_text(struct gui_window *g, const char *filename,
- bool toolbar)
+bool ro_gui_window_import_text(struct gui_window *g, const char *filename)
{
fileswitch_object_type obj_type;
os_error *error;
- char *buf, *utf8_buf;
+ char *buf, *utf8_buf, *sp;
int size;
utf8_convert_ret ret;
+ const char *ep;
+ char *p;
error = xosfile_read_stamped(filename, &obj_type, NULL, NULL,
&size, NULL, NULL);
@@ -4596,24 +4599,19 @@ bool ro_gui_window_import_text(struct gui_window *g, const char *filename,
}
size = strlen(utf8_buf);
- if (toolbar) {
- const char *ep = utf8_buf + size;
- const char *sp;
- char *p = utf8_buf;
+ ep = utf8_buf + size;
+ p = utf8_buf;
- /* skip leading whitespace */
- while (isspace(*p)) p++;
+ /* skip leading whitespace */
+ while (isspace(*p)) p++;
- sp = p;
- while (*p && *p != '\r' && *p != '\n')
- p += utf8_next(p, ep - p, 0);
- *p = '\0';
+ sp = p;
+ while (*p && *p != '\r' && *p != '\n')
+ p += utf8_next(p, ep - p, 0);
+ *p = '\0';
- if (p > sp)
- ro_gui_window_launch_url(g, sp);
- }
- else
- browser_window_paste_text(g->bw, utf8_buf, size, true);
+ if (p > sp)
+ ro_gui_window_launch_url(g, sp);
free(buf);
free(utf8_buf);
diff --git a/utils/utf8.c b/utils/utf8.c
index c0f6b106a..b4e308044 100644
--- a/utils/utf8.c
+++ b/utils/utf8.c
@@ -474,3 +474,42 @@ utf8_convert_ret utf8_to_html(const char *string, const char *encname,
}
+/**
+ * Save the given utf8 text to a file, converting to local encoding.
+ *
+ * \param utf8_text text to save to file
+ * \param path pathname to save to
+ * \return true iff the save succeeded
+ */
+
+bool utf8_save_text(const char *utf8_text, const char *path)
+{
+ utf8_convert_ret ret;
+ char *conv;
+ FILE *out;
+
+ ret = utf8_to_local_encoding(utf8_text, strlen(utf8_text), &conv);
+
+ if (ret != UTF8_CONVERT_OK) {
+ LOG(("failed to convert to local encoding, return %d", ret));
+ return false;
+ }
+
+ out = fopen(path, "w");
+ if (out) {
+ int res = fputs(conv, out);
+ if (res < 0) {
+ LOG(("Warning: writing data failed"));
+ }
+
+ res = fputs("\n", out);
+ fclose(out);
+ free(conv);
+ return (res != EOF);
+ }
+ free(conv);
+
+ return false;
+}
+
+
diff --git a/utils/utf8.h b/utils/utf8.h
index 22aee1afa..a48d47d55 100644
--- a/utils/utf8.h
+++ b/utils/utf8.h
@@ -50,6 +50,8 @@ utf8_convert_ret utf8_from_enc(const char *string, const char *encname,
utf8_convert_ret utf8_to_html(const char *string, const char *encname,
size_t len, char **result);
+bool utf8_save_text(const char *utf8_text, const char *path);
+
/* These two are platform specific */
utf8_convert_ret utf8_to_local_encoding(const char *string, size_t len,
char **result);