diff options
Diffstat (limited to 'desktop')
-rw-r--r-- | desktop/browser.c | 126 | ||||
-rw-r--r-- | desktop/browser.h | 48 | ||||
-rw-r--r-- | desktop/browser_private.h | 20 | ||||
-rw-r--r-- | desktop/frames.c | 2 | ||||
-rw-r--r-- | desktop/selection.c | 128 | ||||
-rw-r--r-- | desktop/textarea.c | 183 | ||||
-rw-r--r-- | desktop/textarea.h | 43 | ||||
-rw-r--r-- | desktop/textinput.c | 80 |
8 files changed, 277 insertions, 353 deletions
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); } |