summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);