From c2a718075ad321a9cf4678e72645acda5c3471a9 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Fri, 22 Feb 2013 12:19:35 +0000 Subject: 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. --- desktop/browser.c | 126 +++++++++++++++++++++---------- desktop/browser.h | 48 +++++------- desktop/browser_private.h | 20 ++--- desktop/frames.c | 2 - desktop/selection.c | 128 +------------------------------- desktop/textarea.c | 183 ++++++++++++++++++++++++++++++---------------- desktop/textarea.h | 43 ++++++++--- desktop/textinput.c | 80 +++----------------- 8 files changed, 277 insertions(+), 353 deletions(-) (limited to 'desktop') 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); } @@ -1109,111 +1090,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,10 +2362,62 @@ 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) { 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 { @@ -205,6 +216,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 * 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); } -- cgit v1.2.3