From ca96353d9fac049057535ed7fdff19e43ac79666 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sat, 27 Jun 2009 13:59:25 +0000 Subject: Merged revisions 7764-7977,7979-8058 via svnmerge from svn://svn.netsurf-browser.org/branches/paulblokus/textinput ........ r7769 | paulblokus | 2009-06-11 22:26:16 +0100 (Thu, 11 Jun 2009) | 4 lines replace global history window with an empty window for future tests add the necessary files first lines ported ........ r7771 | paulblokus | 2009-06-11 23:51:46 +0100 (Thu, 11 Jun 2009) | 1 line more functions ........ r7772 | paulblokus | 2009-06-12 02:07:36 +0100 (Fri, 12 Jun 2009) | 1 line redraw working ........ r7777 | paulblokus | 2009-06-12 11:35:45 +0100 (Fri, 12 Jun 2009) | 3 lines plotter fix make use of the provided clipping rectangle ........ r7781 | paulblokus | 2009-06-12 16:26:51 +0100 (Fri, 12 Jun 2009) | 3 lines callbacks for taxtarea to request a [caret]redraw basic caret handling drawing ........ r7782 | paulblokus | 2009-06-12 22:36:50 +0100 (Fri, 12 Jun 2009) | 1 line single character insertion ........ r7783 | paulblokus | 2009-06-12 22:41:37 +0100 (Fri, 12 Jun 2009) | 1 line single character insertion ........ r7784 | paulblokus | 2009-06-12 23:55:40 +0100 (Fri, 12 Jun 2009) | 3 lines fixed caret clipping arrows, delete and backspace ........ r7812 | paulblokus | 2009-06-16 14:55:41 +0100 (Tue, 16 Jun 2009) | 1 line remove bug causing NS hang on \n in textarea ........ r7816 | paulblokus | 2009-06-16 16:29:48 +0100 (Tue, 16 Jun 2009) | 1 line Enter, Home, End keys ........ r7817 | paulblokus | 2009-06-16 16:56:16 +0100 (Tue, 16 Jun 2009) | 1 line Ctrl + Home/End ........ r7818 | paulblokus | 2009-06-16 17:16:51 +0100 (Tue, 16 Jun 2009) | 1 line redraw caret only on caret moves ........ r7821 | paulblokus | 2009-06-16 20:18:30 +0100 (Tue, 16 Jun 2009) | 1 line line end/start delete ........ r7822 | paulblokus | 2009-06-16 23:43:42 +0100 (Tue, 16 Jun 2009) | 1 line selection drawing + select all ........ r7823 | paulblokus | 2009-06-17 02:31:07 +0100 (Wed, 17 Jun 2009) | 3 lines auto scrolling on caret moves clear selection ........ r7845 | paulblokus | 2009-06-18 17:35:03 +0100 (Thu, 18 Jun 2009) | 1 line page up/down ........ r7846 | paulblokus | 2009-06-18 17:38:45 +0100 (Thu, 18 Jun 2009) | 1 line remove unnecessary fix ........ r7847 | paulblokus | 2009-06-18 18:00:16 +0100 (Thu, 18 Jun 2009) | 1 line clipping fixes ........ r7849 | paulblokus | 2009-06-18 18:21:02 +0100 (Thu, 18 Jun 2009) | 1 line scroll fix ........ r7850 | paulblokus | 2009-06-18 18:45:13 +0100 (Thu, 18 Jun 2009) | 1 line simplified redraw request logic ........ r7855 | paulblokus | 2009-06-18 19:56:24 +0100 (Thu, 18 Jun 2009) | 1 line front end passing mouse events ........ r7858 | paulblokus | 2009-06-18 22:18:39 +0100 (Thu, 18 Jun 2009) | 3 lines drag selection bug fixes ........ r7860 | paulblokus | 2009-06-18 23:32:39 +0100 (Thu, 18 Jun 2009) | 3 lines take selection into account on keypress of different types a few bugs fixed ........ r7876 | paulblokus | 2009-06-19 13:43:07 +0100 (Fri, 19 Jun 2009) | 3 lines pango nsfont_split fix a few textarea fixes ........ r7879 | paulblokus | 2009-06-19 17:33:10 +0100 (Fri, 19 Jun 2009) | 4 lines newline handling seems to work this way clear selection on mouse click more bug fixes ........ r7880 | paulblokus | 2009-06-19 18:16:27 +0100 (Fri, 19 Jun 2009) | 3 lines no caret option selection follows drag ........ r7883 | paulblokus | 2009-06-19 19:08:44 +0100 (Fri, 19 Jun 2009) | 3 lines o width selection bug fix caret at correct side of drag selection ........ r7918 | paulblokus | 2009-06-22 21:01:28 +0100 (Mon, 22 Jun 2009) | 3 lines fix caret positioning at line end CR removal in input methods ........ r7919 | paulblokus | 2009-06-22 21:34:39 +0100 (Mon, 22 Jun 2009) | 1 line fix crash on 0 length text ........ r7926 | paulblokus | 2009-06-23 09:53:56 +0100 (Tue, 23 Jun 2009) | 3 lines change LF into spaces for single line widget text normalisation at one place ........ r7931 | paulblokus | 2009-06-23 10:51:25 +0100 (Tue, 23 Jun 2009) | 1 line cleanup ........ r7933 | paulblokus | 2009-06-23 11:17:22 +0100 (Tue, 23 Jun 2009) | 1 line fix selection draw ........ r7935 | paulblokus | 2009-06-23 11:41:30 +0100 (Tue, 23 Jun 2009) | 1 line guard readonly ........ r7942 | paulblokus | 2009-06-24 08:19:39 +0100 (Wed, 24 Jun 2009) | 1 line applied changes suggested by jmb ........ r7943 | paulblokus | 2009-06-24 09:04:49 +0100 (Wed, 24 Jun 2009) | 1 line little fixes ........ r7945 | paulblokus | 2009-06-24 12:50:14 +0100 (Wed, 24 Jun 2009) | 1 line correct line length and wrapping ........ r7947 | paulblokus | 2009-06-24 14:32:36 +0100 (Wed, 24 Jun 2009) | 3 lines fixed page up/down broken in last commit changed logic for caret positioning on soft breaks ........ r7949 | paulblokus | 2009-06-24 16:31:42 +0100 (Wed, 24 Jun 2009) | 1 line remove temporary/test code ........ r7975 | paulblokus | 2009-06-25 16:00:46 +0100 (Thu, 25 Jun 2009) | 1 line changes suggested by jmb ........ r7976 | paulblokus | 2009-06-25 16:33:23 +0100 (Thu, 25 Jun 2009) | 1 line added ro_ prefix to RISC OS textarea code ........ svn path=/trunk/netsurf/; revision=8060 --- Makefile.sources | 2 +- desktop/browser.h | 17 +- desktop/textarea.c | 1379 +++++++++++++++++++++++++++++++++++++++++++++++++++ desktop/textarea.h | 58 +++ desktop/textinput.h | 1 + gtk/font_pango.c | 1 + gtk/gtk_gui.c | 70 +++ gtk/gtk_gui.h | 3 + gtk/gtk_plotters.c | 2 +- gtk/gtk_window.c | 43 +- riscos/sslcert.c | 24 +- riscos/textarea.c | 128 ++--- riscos/textarea.h | 20 +- riscos/treeview.c | 16 +- 14 files changed, 1622 insertions(+), 142 deletions(-) create mode 100644 desktop/textarea.c create mode 100644 desktop/textarea.h diff --git a/Makefile.sources b/Makefile.sources index 5620404f4..886b6e176 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -13,7 +13,7 @@ S_RENDER := box.c box_construct.c box_normalise.c directory.c \ layout.c list.c loosen.c table.c textplain.c S_UTILS := base64.c filename.c hashtable.c locale.c messages.c talloc.c \ url.c utf8.c utils.c useragent.c -S_DESKTOP := knockout.c options.c print.c tree.c version.c +S_DESKTOP := knockout.c options.c print.c tree.c version.c textarea.c # S_COMMON are sources common to all builds S_COMMON := $(addprefix content/,$(S_CONTENT)) \ diff --git a/desktop/browser.h b/desktop/browser.h index 37c643520..1c88b4f44 100644 --- a/desktop/browser.h +++ b/desktop/browser.h @@ -191,21 +191,24 @@ typedef enum { * a drag. */ BROWSER_MOUSE_CLICK_1 = 4, /* button 1 clicked. */ BROWSER_MOUSE_CLICK_2 = 8, /* button 2 clicked. */ + BROWSER_MOUSE_DOUBLE_CLICK = 16, /* button 1 double clicked */ - BROWSER_MOUSE_DRAG_1 = 16, /* start of button 1 drag operation */ - BROWSER_MOUSE_DRAG_2 = 32, /* start of button 2 drag operation */ + BROWSER_MOUSE_DRAG_1 = 32, /* start of button 1 drag operation */ + BROWSER_MOUSE_DRAG_2 = 64, /* start of button 2 drag operation */ - BROWSER_MOUSE_DRAG_ON = 64, /* a drag operation was started and + BROWSER_MOUSE_DRAG_ON = 128, /* a drag operation was started and * a mouse button is still pressed */ - BROWSER_MOUSE_HOLDING_1 = 128, /* while button 1 drag is in progress */ - BROWSER_MOUSE_HOLDING_2 = 256, /* while button 2 drag is in progress */ + BROWSER_MOUSE_HOLDING_1 = 256, /* while button 1 drag is in progress */ + BROWSER_MOUSE_HOLDING_2 = 512, /* while button 2 drag is in progress */ - BROWSER_MOUSE_MOD_1 = 512, /* primary modifier key pressed + BROWSER_MOUSE_MOD_1 = 1024, /* primary modifier key pressed * (eg. Shift) */ - BROWSER_MOUSE_MOD_2 = 1024 /* secondary modifier key pressed + BROWSER_MOUSE_MOD_2 = 2048, /* secondary modifier key pressed * (eg. Ctrl) */ + BROWSER_MOUSE_MOD_3 = 4096 /* secondary modifier key pressed + * (eg. Alt) */ } browser_mouse_state; diff --git a/desktop/textarea.c b/desktop/textarea.c new file mode 100644 index 000000000..6fbcec0d8 --- /dev/null +++ b/desktop/textarea.c @@ -0,0 +1,1379 @@ +/* + * Copyright 2006 John-Mark Bell + * Copyright 2009 Paul Blokus + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + * Single/Multi-line UTF-8 text area (implementation) + */ + +#include +#include +#include "css/css.h" +#include "desktop/textarea.h" +#include "desktop/textinput.h" +#include "desktop/plotters.h" +#include "render/font.h" +#include "utils/log.h" +#include "utils/utf8.h" +#include "utils/utils.h" + +#define MARGIN_LEFT 2 +#define MARGIN_RIGHT 2 +#define CARET_COLOR 0x000000 +/* background color for readonly textarea */ +#define READONLY_BG 0xD9D9D9 +#define BACKGROUND_COL 0xFFFFFF +#define BORDER_COLOR 0x000000 +#define SELECTION_COL 0xFFDDDD + + +struct line_info { + unsigned int b_start; /**< Byte offset of line start */ + unsigned int b_length; /**< Byte length of line */ +}; + +struct text_area { + + int x, y; /**< Coordinates of the widget + * (top left corner) with respect to + * canvas origin(these don't change + * it is the canvas which gets + * scrolled) + */ + + int scroll_x, scroll_y; /**< scroll offsets of the textarea + * content + */ + + unsigned int flags; /**< Textarea flags */ + int vis_width; /**< Visible width, in pixels */ + int vis_height; /**< Visible height, in pixels */ + + char *text; /**< UTF-8 text */ + unsigned int text_alloc; /**< Size of allocated text */ + unsigned int text_len; /**< Length of text, in bytes */ + unsigned int text_utf8_len; /**< Length of text, in characters + * without the trailing NUL + */ + struct { + int line; /**< Line caret is on */ + int char_off; /**< Character index of caret within the + * specified line + */ + } caret_pos; + + int selection_start; /**< Character index of sel start(inclusive) */ + int selection_end; /**< Character index of sel end(exclusive) */ + + struct css_style *style; /**< Text style */ + + int line_count; /**< Count of lines */ +#define LINE_CHUNK_SIZE 16 + struct line_info *lines; /**< Line info array */ + int line_height; /**< Line height obtained from style */ + + /** Callback functions for a redraw request */ + textarea_start_redraw_callback redraw_start_callback; + textarea_start_redraw_callback redraw_end_callback; + + void *data; /** < Callback data for both callback functions */ + + int drag_start_char; /**< Character index at which the drag was + * started + */ +}; + + +static bool textarea_insert_text(struct text_area *ta, unsigned int index, + const char *text); +static bool textarea_replace_text(struct text_area *ta, unsigned int start, + unsigned int end, const char *text); +static bool textarea_reflow(struct text_area *ta, unsigned int line); +static unsigned int textarea_get_xy_offset(struct text_area *ta, int x, int y); +static bool textarea_set_caret_xy(struct text_area *ta, int x, int y); +static bool textarea_scroll_visible(struct text_area *ta); +static bool textarea_select(struct text_area *ta, int c_start, int c_end); +static void textarea_normalise_text(struct text_area *ta, + unsigned int b_start, unsigned int b_len); + +/** + * Create a text area + * + * \param x X coordinate of left border + * \param y Y coordinate of top border + * \param width width of the text area + * \param height width of the text area + * \param flags text area flags + * \param style css style (font style properties are used only) + * \param redraw_start_callback will be called when textarea wants to redraw + * \param redraw_end_callback will be called when textarea finisjes redrawing + * \param data user specified data which will be passed to redraw callbacks + * \return Opaque handle for textarea or 0 on error + */ +struct text_area *textarea_create(int x, int y, int width, int height, + unsigned int flags, const struct css_style *style, + textarea_start_redraw_callback redraw_start_callback, + textarea_end_redraw_callback redraw_end_callback, void *data) +{ + struct text_area *ret; + + if (redraw_start_callback == NULL || redraw_end_callback == NULL) { + LOG(("no callback provided")); + return NULL; + } + + ret = malloc(sizeof(struct text_area)); + if (ret == NULL) { + LOG(("malloc failed")); + return NULL; + } + + ret->redraw_start_callback = redraw_start_callback; + ret->redraw_end_callback = redraw_end_callback; + ret->data = data; + ret->x = x; + ret->y = y; + ret->vis_width = width; + ret->vis_height = height; + ret->scroll_x = 0; + ret->scroll_y = 0; + ret->drag_start_char = 0; + + + ret->flags = flags; + ret->text = malloc(64); + if (ret->text == NULL) { + LOG(("malloc failed")); + free(ret); + return NULL; + } + ret->text[0] = '\0'; + ret->text_alloc = 64; + ret->text_len = 1; + ret->text_utf8_len = 0; + + ret->style = malloc(sizeof(struct css_style)); + if (ret->style == NULL) { + LOG(("malloc failed")); + free(ret->text); + free(ret); + return NULL; + } + memcpy(ret->style, style, sizeof(struct css_style)); + ret->line_height = css_len2px(&(style->line_height.value.length), + style); + + ret->caret_pos.line = ret->caret_pos.char_off = 0; + ret->selection_start = -1; + ret->selection_end = -1; + + ret->line_count = 0; + ret->lines = NULL; + + return ret; +} + + +/** + * Destroy a text area + * + * \param ta Text area to destroy + */ +void textarea_destroy(struct text_area *ta) +{ + free(ta->text); + free(ta->style); + free(ta->lines); + free(ta); +} + + +/** + * Set the text in a text area, discarding any current text + * + * \param ta Text area + * \param text UTF-8 text to set text area's contents to + * \return true on success, false on memory exhaustion + */ +bool textarea_set_text(struct text_area *ta, const char *text) +{ + unsigned int len = strlen(text) + 1; + + if (len >= ta->text_alloc) { + char *temp = realloc(ta->text, len + 64); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + ta->text = temp; + ta->text_alloc = len + 64; + } + + memcpy(ta->text, text, len); + ta->text_len = len; + ta->text_utf8_len = utf8_length(ta->text); + + textarea_normalise_text(ta, 0, len); + + return textarea_reflow(ta, 0); +} + + +/** + * Extract the text from a text area + * + * \param ta Text area + * \param buf Pointer to buffer to receive data, or NULL + * to read length required + * \param len Length (bytes) of buffer pointed to by buf, or 0 to read length + * \return Length (bytes) written/required or -1 on error + */ +int textarea_get_text(struct text_area *ta, char *buf, unsigned int len) +{ + if (buf == NULL && len == 0) { + /* want length */ + return ta->text_len; + } + + if (len < ta->text_len) { + LOG(("buffer too small")); + return -1; + } + + memcpy(buf, ta->text, ta->text_len); + + return ta->text_len; +} + + +/** + * Insert text into the text area + * + * \param ta Text area + * \param index 0-based character index to insert at + * \param text UTF-8 text to insert + * \return false on memory exhaustion, true otherwise + */ +bool textarea_insert_text(struct text_area *ta, unsigned int index, + const char *text) +{ + unsigned int b_len = strlen(text); + size_t b_off; + + if (ta->flags & TEXTAREA_READONLY) + return true; + + /* Find insertion point */ + if (index > ta->text_utf8_len) + index = ta->text_utf8_len; + + /* find byte offset of insertion point */ + for (b_off = 0; index-- > 0; + b_off = utf8_next(ta->text, ta->text_len, b_off)) + ; /* do nothing */ + + if (b_len + ta->text_len >= ta->text_alloc) { + char *temp = realloc(ta->text, b_len + ta->text_len + 64); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + + ta->text = temp; + ta->text_alloc = b_len + ta->text_len + 64; + } + + /* Shift text following up */ + memmove(ta->text + b_off + b_len, ta->text + b_off, + ta->text_len - b_off); + /* Insert new text */ + memcpy(ta->text + b_off, text, b_len); + ta->text_len += b_len; + ta->text_utf8_len += utf8_length(text); + + textarea_normalise_text(ta, b_off, b_len); + + /** \todo calculate line to reflow from */ + return textarea_reflow(ta, 0); + +} + + +/** + * Replace text in a text area + * + * \param ta Text area + * \param start Start character index of replaced section (inclusive) + * \param end End character index of replaced section (exclusive) + * \param text UTF-8 text to insert + * \return false on memory exhaustion, true otherwise + */ +bool textarea_replace_text(struct text_area *ta, unsigned int start, + unsigned int end, const char *text) +{ + unsigned int b_len = strlen(text); + size_t b_start, b_end, diff; + + if (ta->flags & TEXTAREA_READONLY) + return true; + + if (start > ta->text_utf8_len) + start = ta->text_utf8_len; + if (end > ta->text_utf8_len) + end = ta->text_utf8_len; + + if (start == end) + return textarea_insert_text(ta, start, text); + + if (start > end) + return false; + + diff = end - start; + + /* find byte offset of replace start */ + for (b_start = 0; start-- > 0; + b_start = utf8_next(ta->text, ta->text_len, b_start)) + ; /* do nothing */ + + /* find byte length of replaced text */ + for (b_end = b_start; diff-- > 0; + b_end = utf8_next(ta->text, ta->text_len, b_end)) + ; /* do nothing */ + + if (b_len + ta->text_len - (b_end - b_start) >= ta->text_alloc) { + char *temp = realloc(ta->text, + b_len + ta->text_len - (b_end - b_start) + 64); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + + ta->text = temp; + ta->text_alloc = + b_len + ta->text_len - (b_end - b_start) + 64; + } + + /* Shift text following to new position */ + memmove(ta->text + b_start + b_len, ta->text + b_end, + ta->text_len - b_end); + + /* Insert new text */ + memcpy(ta->text + b_start, text, b_len); + + ta->text_len += b_len - (b_end - b_start); + ta->text_utf8_len = utf8_length(ta->text); + textarea_normalise_text(ta, b_start, b_len); + + /** \todo calculate line to reflow from */ + return textarea_reflow(ta, 0); +} + + +/** + * Set the caret's position + * + * \param ta Text area + * \param caret 0-based character index to place caret at, -1 removes + * the caret + * \return true on success false otherwise + */ +bool textarea_set_caret(struct text_area *ta, int caret) +{ + unsigned int c_len; + unsigned int b_off; + int i; + int index; + int x, y; + int x0, y0, x1, y1; + int height; + + + if (ta->flags & TEXTAREA_READONLY) + return true; + + ta->redraw_start_callback(ta->data); + + c_len = ta->text_utf8_len; + + if (caret != -1 && (unsigned)caret > c_len) + caret = c_len; + + height = css_len2px(&(ta->style->font_size.value.length), + ta->style); + /* Delete the old caret */ + if (ta->caret_pos.char_off != -1) { + index = textarea_get_caret(ta); + if (index == -1) + return false; + + /* the redraw might happen in response to a text-change and + the caret position might be beyond the current text */ + if ((unsigned)index > c_len) + index = c_len; + + /* find byte offset of caret position */ + for (b_off = 0; index-- > 0; + b_off = utf8_next(ta->text, + ta->text_len, b_off)) + ; /* do nothing */ + + nsfont.font_width(ta->style, + ta->text + + ta->lines[ta->caret_pos.line].b_start, + b_off - ta->lines[ta->caret_pos.line].b_start, + &x); + + x += ta->x + MARGIN_LEFT - ta->scroll_x; + + y = ta->line_height * ta->caret_pos.line + ta->y - ta->scroll_y; + + textarea_redraw(ta, x - 1, y - 1, x + 1, y + height + 1); + } + + /* check if the caret has to be drawn at all */ + if (caret != -1) { + /* Find byte offset of caret position */ + for (b_off = 0; caret > 0; caret--) + b_off = utf8_next(ta->text, ta->text_len, b_off); + + /* Now find line in which byte offset appears */ + for (i = 0; i < ta->line_count - 1; i++) + if (ta->lines[i + 1].b_start > b_off) + break; + + ta->caret_pos.line = i; + + /* Now calculate the char. offset of the caret in this line */ + for (c_len = 0, ta->caret_pos.char_off = 0; + c_len < b_off - ta->lines[i].b_start; + c_len = utf8_next(ta->text + + ta->lines[i].b_start, + ta->lines[i].b_length, c_len)) + ta->caret_pos.char_off++; + + if (textarea_scroll_visible(ta)) + textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width, + ta->y + ta->vis_height); + + /* Finally, redraw the caret */ + index = textarea_get_caret(ta); + if (index == -1) + return false; + + /* find byte offset of caret position */ + for (b_off = 0; index-- > 0; + b_off = utf8_next(ta->text, + ta->text_len, b_off)) + ; /* do nothing */ + + nsfont.font_width(ta->style, + ta->text + + ta->lines[ta->caret_pos.line].b_start, + b_off - ta->lines[ta->caret_pos.line].b_start, + &x); + + x += ta->x + MARGIN_LEFT - ta->scroll_x; + + y = ta->line_height * ta->caret_pos.line + ta->y - ta->scroll_y; + + x0 = max(x - 1, ta->x + MARGIN_LEFT); + y0 = max(y - 1, ta->y); + x1 = min(x + 1, ta->x + ta->vis_width - MARGIN_RIGHT); + y1 = min(y + height + 1, ta->y + ta->vis_height); + + plot.clip(x0, y0, x1, y1); + plot.line(x, y, x, y + height, 1, CARET_COLOR, false, false); + } + ta->redraw_end_callback(ta->data); + + return true; +} + + +/** + * get character offset from the beginning of the text for some coordinates + * + * \param ta Text area + * \param x X coordinate + * \param y Y coordinate + * \return character offset + */ +unsigned int textarea_get_xy_offset(struct text_area *ta, int x, int y) +{ + size_t b_off, temp; + unsigned int c_off; + int line; + + if (!ta->line_count) + return 0; + + x = x - ta->x - MARGIN_LEFT + ta->scroll_x; + y = y - ta->y + ta->scroll_y; + + if (x < 0) + x = 0; + + line = y / ta->line_height; + + if (line < 0) + line = 0; + if (ta->line_count - 1 < line) + line = ta->line_count - 1; + + nsfont.font_position_in_string(ta->style, + ta->text + ta->lines[line].b_start, + ta->lines[line].b_length, x, &b_off, &x); + + /* If the calculated byte offset corresponds with the number of bytes + * in the line, and the line has been soft-wrapped, then ensure the + * caret offset is before the trailing space character, rather than + * after it. Otherwise, the caret will be placed at the start of the + * following line, which is undesirable. + */ + if (b_off == (unsigned)ta->lines[line].b_length && + ta->text[ta->lines[line].b_start + + ta->lines[line].b_length - 1] == ' ') + b_off--; + + for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start; + temp = utf8_next(ta->text, ta->text_len, temp)) + c_off++; + + return c_off; +} + + +/** + * Set the caret's position + * + * \param ta Text area + * \param x X position of caret in a window relative to text area top left + * \param y Y position of caret in a window relative to text area top left + * \return true on success false otherwise + */ +bool textarea_set_caret_xy(struct text_area *ta, int x, int y) +{ + unsigned int c_off; + + if (ta->flags & TEXTAREA_READONLY) + return true; + + c_off = textarea_get_xy_offset(ta, x, y); + return textarea_set_caret(ta, c_off); +} + + +/** + * Get the caret's position + * + * \param ta Text area + * \return 0-based character index of caret location, or -1 on error + */ +int textarea_get_caret(struct text_area *ta) +{ + unsigned int c_off = 0, b_off; + + + /* if the text is a trailing NUL only */ + if (ta->text_utf8_len == 0) + return 0; + + /* Calculate character offset of this line's start */ + for (b_off = 0; b_off < ta->lines[ta->caret_pos.line].b_start; + b_off = utf8_next(ta->text, ta->text_len, b_off)) + c_off++; + + return c_off + ta->caret_pos.char_off; +} + +/** + * Reflow a text area from the given line onwards + * + * \param ta Text area to reflow + * \param line Line number to begin reflow on + * \return true on success false otherwise + */ +bool textarea_reflow(struct text_area *ta, unsigned int line) +{ + char *text; + unsigned int len; + size_t b_off; + int x; + char *space; + unsigned int line_count = 0; + + /** \todo pay attention to line parameter */ + /** \todo create horizontal scrollbar if needed */ + + ta->line_count = 0; + + if (ta->lines == NULL) { + ta->lines = + malloc(LINE_CHUNK_SIZE * sizeof(struct line_info)); + if (ta->lines == NULL) { + LOG(("malloc failed")); + return false; + } + } + + if (!(ta->flags & TEXTAREA_MULTILINE)) { + /* Single line */ + ta->lines[line_count].b_start = 0; + ta->lines[line_count++].b_length = ta->text_len - 1; + + ta->line_count = line_count; + + return true; + } + + for (len = ta->text_len - 1, text = ta->text; len > 0; + len -= b_off, text += b_off) { + + nsfont.font_split(ta->style, text, len, + ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT, + &b_off, &x); + + if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) { + struct line_info *temp = realloc(ta->lines, + (line_count + LINE_CHUNK_SIZE) * + sizeof(struct line_info)); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + + ta->lines = temp; + } + + /* handle LF */ + for (space = text; space <= text + b_off; space++) { + if (*space == '\n') + break; + } + + if (space <= text + b_off) { + /* Found newline; use it */ + ta->lines[line_count].b_start = text - ta->text; + ta->lines[line_count++].b_length = space - text; + + b_off = space + 1 - text; + + if (len - b_off == 0) { + /* reached end of input => add last line */ + ta->lines[line_count].b_start = + text + b_off - ta->text; + ta->lines[line_count++].b_length = 0; + } + + continue; + } + + if (len - b_off > 0) { + /* find last space (if any) */ + for (space = text + b_off; space > text; space--) + if (*space == ' ') + break; + + if (space != text) + b_off = space + 1 - text; + } + + ta->lines[line_count].b_start = text - ta->text; + ta->lines[line_count++].b_length = b_off; + } + + ta->line_count = line_count; + + return true; +} + +/** + * Handle redraw requests for text areas + * + * \param redraw Redraw request block + */ +void textarea_redraw(struct text_area *ta, int x0, int y0, int x1, int y1) +{ + int line0, line1, line; + int chars, offset; + unsigned int c_pos, c_len, b_start, b_end, line_len; + char *line_text; + + + if (x1 < ta->x || x0 > ta->x + ta->vis_width || y1 < ta->y || + y0 > ta->y + ta->vis_height) + /* Textarea outside the clipping rectangle */ + return; + + if (ta->lines == NULL) + /* Nothing to redraw */ + return; + + line0 = (y0 - ta->y + ta->scroll_y) / ta->line_height - 1; + line1 = (y1 - ta->y + ta->scroll_y) / ta->line_height + 1; + + if (line0 < 0) + line0 = 0; + if (line1 < 0) + line1 = 0; + if (ta->line_count - 1 < line0) + line0 = ta->line_count - 1; + if (ta->line_count - 1 < line1) + line1 = ta->line_count - 1; + if (line1 < line0) + line1 = line0; + + if (x0 < ta->x) + x0 = ta->x; + if (y0 < ta->y) + y0 = ta->y; + if (x1 > ta->x + ta->vis_width) + x1 = ta->x + ta->vis_width; + if (y1 > ta->y + ta->vis_height) + y1 = ta->y + ta->vis_height; + + plot.clip(x0, y0, x1, y1); + plot.fill(x0, y0, x1, y1, (ta->flags & TEXTAREA_READONLY) ? + READONLY_BG : BACKGROUND_COL); + plot.rectangle(ta->x, ta->y, ta->vis_width - 1, ta->vis_height - 1, 1, + BORDER_COLOR, false, false); + + if (x0 < ta->x + MARGIN_LEFT) + x0 = ta->x + MARGIN_LEFT; + if (x1 > ta->x + ta->vis_width - MARGIN_RIGHT) + x1 = ta->x + ta->vis_width - MARGIN_RIGHT; + plot.clip(x0, y0, x1, y1); + + if (line0 > 0) + c_pos = utf8_bounded_length(ta->text, + ta->lines[line0].b_start - 1); + else + c_pos = 0; + + for (line = line0; (line <= line1) && + (ta->y + line * ta->line_height <= y1 + ta->scroll_y); + line++) { + if (ta->lines[line].b_length == 0) + continue; + + c_len = utf8_bounded_length( + &(ta->text[ta->lines[line].b_start]), + ta->lines[line].b_length); + + /* if there is a newline between the lines count it too */ + if (line < ta->line_count - 1 && ta->lines[line + 1].b_start != + ta->lines[line].b_start + + ta->lines[line].b_length) + c_len++; + + /* check if a part of the line is selected, won't happen if no + selection (ta->selection_end = -1) */ + if (ta->selection_end != -1 && + c_pos < (unsigned)ta->selection_end && + c_pos + c_len > (unsigned)ta->selection_start) { + + /* offset from the beginning of the line */ + offset = ta->selection_start - c_pos; + chars = ta->selection_end - c_pos - + (offset > 0 ? offset:0); + + line_text = &(ta->text[ta->lines[line].b_start]); + line_len = ta->lines[line].b_length; + + if (offset > 0) { + + /* find byte start of the selected part */ + for (b_start = 0; offset > 0; offset--) + b_start = utf8_next(line_text, + line_len, + b_start); + nsfont.font_width(ta->style, line_text, + b_start, &x0); + x0 += ta->x + MARGIN_LEFT; + } + else { + x0 = ta->x + MARGIN_LEFT; + b_start = 0; + } + + + if (chars >= 0) { + + /* find byte end of the selected part */ + for (b_end = b_start; chars > 0 && + b_end < line_len; + chars--) { + b_end = utf8_next(line_text, line_len, + b_end); + } + } + else + b_end = ta->lines[line].b_length; + + b_end -= b_start; + nsfont.font_width(ta->style, + &(ta->text[ta->lines[line].b_start + + b_start]), + b_end, &x1); + x1 += x0; + plot.fill(x0 - ta->scroll_x, ta->y + + line * ta->line_height + + 1 - ta->scroll_y, + x1 - ta->scroll_x, + ta->y + (line + 1) * ta->line_height - + 1 - ta->scroll_y, + SELECTION_COL); + + } + + c_pos += c_len; + + y0 = ta->y + line * ta->line_height + 0.75 * ta->line_height; + + plot.text(ta->x + MARGIN_LEFT - ta->scroll_x, y0 - ta->scroll_y, + ta->style, + ta->text + ta->lines[line].b_start, + ta->lines[line].b_length, + (ta->flags & TEXTAREA_READONLY) ? + READONLY_BG : BACKGROUND_COL, + ta->style->color); + } +} + +/** + * Key press handling for text areas. + * + * \param ta The text area which got the keypress + * \param key The ucs4 character codepoint + * \return true if the keypress is dealt with, false otherwise. + */ +bool textarea_keypress(struct text_area *ta, uint32_t key) +{ + char utf8[6]; + unsigned int caret, caret_init, length, l_len, b_off, b_len; + int c_line, c_chars, line; + bool redraw = false; + bool readonly; + + caret_init = caret = textarea_get_caret(ta); + line = ta->caret_pos.line; + readonly = (ta->flags & TEXTAREA_READONLY ? true:false); + + + if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) { + /* normal character insertion */ + length = utf8_from_ucs4(key, utf8); + utf8[length] = '\0'; + + if (!textarea_insert_text(ta, caret, utf8)) + return false; + caret++; + redraw = true; + + } else switch (key) { + case KEY_SELECT_ALL: + caret = ta->text_utf8_len; + + ta->selection_start = 0; + ta->selection_end = ta->text_utf8_len; + redraw = true; + break; + case KEY_COPY_SELECTION: + break; + case KEY_DELETE_LEFT: + if (readonly) + break; + if (ta->selection_start != -1) { + if (!textarea_replace_text(ta, + ta->selection_start, + ta->selection_end, "")) + return false; + ta->selection_start = ta->selection_end = -1; + redraw = true; + } else { + if (caret) { + if (!textarea_replace_text(ta, + caret - 1, + caret, "")) + return false; + caret--; + redraw = true; + } + } + break; + break; + case KEY_NL: + if (readonly) + break; + if(!textarea_insert_text(ta, caret, "\n")) + return false; + caret++; + ta->selection_start = ta->selection_end = -1; + redraw = true; + break; + case KEY_CUT_LINE: + case KEY_PASTE: + case KEY_CUT_SELECTION: + break; + case KEY_ESCAPE: + case KEY_CLEAR_SELECTION: + ta->selection_start = -1; + ta->selection_end = -1; + redraw = true; + break; + case KEY_LEFT: + if (readonly) + break; + if (caret) + caret--; + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + redraw = true; + } + break; + case KEY_RIGHT: + if (readonly) + break; + if (caret < ta->text_utf8_len) + caret++; + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + redraw = true; + } + break; + case KEY_PAGE_UP: + if (readonly) + break; + if (ta->flags & TEXTAREA_MULTILINE) { + /* +1 because one line is subtracted in + KEY_UP */ + line = ta->caret_pos.line - (ta->vis_height + + ta->line_height - 1) / + ta->line_height + + 1; + } + /* fall through */ + case KEY_UP: + if (readonly) + break; + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + redraw = true; + } + if (ta->flags & TEXTAREA_MULTILINE) { + line--; + if (line < 0) + line = 0; + if (line == ta->caret_pos.line) + break; + + b_off = ta->lines[line].b_start; + b_len = ta->lines[line].b_length; + + c_line = ta->caret_pos.line; + c_chars = ta->caret_pos.char_off; + + if (ta->text[b_off + b_len - 1] == ' ' + && line < ta->line_count - 1) + b_len--; + + l_len = utf8_bounded_length(&(ta->text[b_off]), + b_len); + + + ta->caret_pos.line = line; + ta->caret_pos.char_off = min(l_len, + (unsigned) + ta->caret_pos.char_off); + + caret = textarea_get_caret(ta); + + ta->caret_pos.line = c_line; + ta->caret_pos.char_off = c_chars; + } + break; + case KEY_PAGE_DOWN: + if (readonly) + break; + if (ta->flags & TEXTAREA_MULTILINE) { + /* -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; + } + /* fall through */ + case KEY_DOWN: + if (readonly) + break; + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + redraw = true; + } + if (ta->flags & TEXTAREA_MULTILINE) { + line++; + if (line > ta->line_count - 1) + line = ta->line_count - 1; + if (line == ta->caret_pos.line) + break; + + b_off = ta->lines[line].b_start; + b_len = ta->lines[line].b_length; + + c_line = ta->caret_pos.line; + c_chars = ta->caret_pos.char_off; + + if (ta->text[b_off + b_len - 1] == ' ' + && line < ta->line_count - 1) + b_len--; + + l_len = utf8_bounded_length(&(ta->text[b_off]), + b_len); + + + ta->caret_pos.line = line; + ta->caret_pos.char_off = min(l_len, + (unsigned) + ta->caret_pos.char_off); + + caret = textarea_get_caret(ta); + + ta->caret_pos.line = c_line; + ta->caret_pos.char_off = c_chars; + } + break; + case KEY_DELETE_RIGHT: + if (readonly) + break; + if (ta->selection_start != -1) { + if (!textarea_replace_text(ta, + ta->selection_start, + ta->selection_end, "")) + return false; + + ta->selection_start = ta->selection_end = -1; + redraw = true; + } else { + if (caret < ta->text_utf8_len) { + if (!textarea_replace_text(ta, caret, + caret + 1, "")) + return false; + redraw = true; + } + } + break; + case KEY_LINE_START: + if (readonly) + break; + caret -= ta->caret_pos.char_off; + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + redraw = true; + } + break; + case KEY_LINE_END: + if (readonly) + break; + + caret = utf8_bounded_length(ta->text, + ta->lines[ta->caret_pos.line].b_start + + ta->lines[ta->caret_pos.line].b_length); + if (ta->text[ta->lines[ta->caret_pos.line].b_start + + ta->lines[ta->caret_pos.line].b_length + - 1] == ' ') + caret--; + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + redraw = true; + } + break; + case KEY_TEXT_START: + if (readonly) + break; + caret = 0; + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + redraw = true; + } + break; + case KEY_TEXT_END: + if (readonly) + break; + caret = ta->text_utf8_len; + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + redraw = true; + } + break; + case KEY_WORD_LEFT: + case KEY_WORD_RIGHT: + break; + case KEY_DELETE_LINE_END: + if (readonly) + break; + if (ta->selection_start != -1) { + if (!textarea_replace_text(ta, + ta->selection_start, + ta->selection_end, "")) + return false; + ta->selection_start = ta->selection_end = -1; + } else { + b_off = ta->lines[ta->caret_pos.line].b_start; + b_len = ta->lines[ta->caret_pos.line].b_length; + l_len = utf8_bounded_length(&(ta->text[b_off]), + b_len); + if (!textarea_replace_text(ta, caret, + caret + l_len, "")) + return false; + } + redraw = true; + break; + case KEY_DELETE_LINE_START: + if (readonly) + break; + if (ta->selection_start != -1) { + if (!textarea_replace_text(ta, + ta->selection_start, + ta->selection_end, "")) + return false; + ta->selection_start = ta->selection_end = -1; + } else { + if (textarea_replace_text(ta, + caret - ta->caret_pos.char_off, + caret, + "")) + return false; + caret -= ta->caret_pos.char_off; + } + redraw = true; + break; + default: + return false; + } + + //TODO:redraw only the important part + if (redraw) { + ta->redraw_start_callback(ta->data); + textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width, + ta->y + ta->vis_height); + ta->redraw_end_callback(ta->data); + } + + if (caret != caret_init || redraw) + return textarea_set_caret(ta, caret); + + return true; +} + +/** + * Scrolls a textarea to make the caret visible (doesn't perform a redraw) + * + * \param ta The text area to be scrolled + * \return true if textarea was scrolled false otherwise + */ +bool textarea_scroll_visible(struct text_area *ta) +{ + int x0, x1, y0, y1, x, y; + int index, b_off; + bool scrolled = false; + + if (ta->caret_pos.char_off == -1) + return false; + + x0 = ta->x + MARGIN_LEFT; + x1 = ta->x + ta->vis_width - MARGIN_RIGHT; + y0 = ta->y; + y1 = ta->y + ta->vis_height; + + index = textarea_get_caret(ta); + + /* find byte offset of caret position */ + for (b_off = 0; index-- > 0; + b_off = utf8_next(ta->text, ta->text_len, b_off)) + ; /* do nothing */ + + nsfont.font_width(ta->style, + ta->text + ta->lines[ta->caret_pos.line].b_start, + b_off - ta->lines[ta->caret_pos.line].b_start, + &x); + + /* top-left of caret */ + x += ta->x + MARGIN_LEFT - ta->scroll_x; + y = ta->line_height * ta->caret_pos.line + ta->y - ta->scroll_y; + + /* check and change vertical scroll */ + if (y < y0) { + ta->scroll_y -= y0 - y; + scrolled = true; + } else if (y + ta->line_height > y1) { + ta->scroll_y += y + ta->line_height - y1; + scrolled = true; + } + + + /* check and change horizontal scroll */ + if (x < x0) { + ta->scroll_x -= x0 - x ; + scrolled = true; + } else if (x > x1 - 1) { + ta->scroll_x += x - (x1 - 1); + scrolled = true; + } + + return scrolled; +} + + +/** + * Handles all kinds of mouse action + * + * \param ta Text area + * \param mouse the mouse state at action moment + * \param x X coordinate + * \param y Y coordinate + * \return true if action was handled false otherwise + */ +bool textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse, + int x, int y) +{ + int c_start, c_end; + + /* mouse button pressed above the text area, move caret */ + if (mouse & BROWSER_MOUSE_PRESS_1) { + if (ta->selection_start != -1) { + ta->selection_start = ta->selection_end = -1; + ta->redraw_start_callback(ta->data); + textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width, + ta->y + ta->vis_height); + ta->redraw_end_callback(ta->data); + } + if (!(ta->flags & TEXTAREA_READONLY)) + return textarea_set_caret_xy(ta, x, y); + } + else if (mouse & BROWSER_MOUSE_DRAG_1) { + ta->drag_start_char = textarea_get_xy_offset(ta, x, y); + if (!(ta->flags & TEXTAREA_READONLY)) + return textarea_set_caret(ta, -1); + } + else if (mouse & BROWSER_MOUSE_HOLDING_1) { + c_start = ta->drag_start_char; + c_end = textarea_get_xy_offset(ta, x, y); + return textarea_select(ta, c_start, c_end); + + } + + return true; +} + + +/** + * Handles the end of a drag operation + * + * \param ta Text area + * \param mouse the mouse state at drag end moment + * \param x X coordinate + * \param y Y coordinate + * \return true if drag end was handled false otherwise + */ +bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse, + int x, int y) +{ + int c_end; + + c_end = textarea_get_xy_offset(ta, x, y); + return textarea_select(ta, ta->drag_start_char, c_end); +} + +/** + * Selects a character range in the textarea and redraws it + * + * \param ta Text area + * \param c_start First character (inclusive) + * \param c_end Last character (exclusive) + * \return true on success false otherwise + */ +bool textarea_select(struct text_area *ta, int c_start, int c_end) +{ + int swap = -1; + + /* if start is after end they get swapped, start won't and end will + be selected this way */ + if (c_start > c_end) { + swap = c_start; + c_start = c_end; + c_end = swap; + } + + ta->selection_start = c_start; + ta->selection_end = c_end; + + ta->redraw_start_callback(ta->data); + textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width, + ta->y + ta->vis_height); + ta->redraw_end_callback(ta->data); + + if (!(ta->flags & TEXTAREA_READONLY)) { + if (swap == -1) + return textarea_set_caret(ta, c_end); + else + return textarea_set_caret(ta, c_start); + } + + return true; +} + + +/** + * Normalises any line endings within the text, replacing CRLF or CR with + * LF as necessary. If the textarea is single line, then all linebreaks are + * converted into spaces. + * + * \param ta Text area + * \param b_start Byte offset to start at + * \param b_len Byte length to check + */ +void textarea_normalise_text(struct text_area *ta, + unsigned int b_start, unsigned int b_len) +{ + bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false; + unsigned int index; + + /* Remove CR characters. If it's a CRLF pair delete it ot replace it + * with LF otherwise. + */ + for (index = 0; index < b_len; index++) { + if (ta->text[b_start + index] == '\r') { + if (b_start + index + 1 <= ta->text_len && + ta->text[b_start + index + 1] == '\n') { + ta->text_len--; + ta->text_utf8_len--; + memmove(ta->text + b_start + index, + ta->text + b_start + index + 1, + ta->text_len - b_start - index); + } + else + ta->text[b_start + index] = '\n'; + } + /* Replace newlines with spaces if this is a single line + * textarea. + */ + if (!multi && (ta->text[b_start + index] == '\n')) + ta->text[b_start + index] = ' '; + } + +} diff --git a/desktop/textarea.h b/desktop/textarea.h new file mode 100644 index 000000000..4213a5484 --- /dev/null +++ b/desktop/textarea.h @@ -0,0 +1,58 @@ +/* + * Copyright 2006 John-Mark Bell + * Copyright 2009 Paul Blokus + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + * Single/Multi-line UTF-8 text area (interface) + */ + +#ifndef _NETSURF_DESKTOP_TEXTAREA_H_ +#define _NETSURF_DESKTOP_TEXTAREA_H_ + +#include +#include +#include "css/css.h" +#include "desktop/browser.h" + +/* Text area flags */ +#define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */ +#define TEXTAREA_READONLY 0x02 /**< Text area is read only */ + +struct text_area; + +typedef void(*textarea_start_redraw_callback)(void *data); +typedef void(*textarea_end_redraw_callback)(void *data); + +struct text_area *textarea_create(int x, int y, int width, int height, + unsigned int flags, const struct css_style *style, + textarea_start_redraw_callback redraw_start_callback, + textarea_end_redraw_callback redraw_end_callback, void *data); +void textarea_destroy(struct text_area *ta); +bool textarea_set_text(struct text_area *ta, const char *text); +int textarea_get_text(struct text_area *ta, char *buf, unsigned int len); +bool textarea_set_caret(struct text_area *ta, int caret); +int textarea_get_caret(struct text_area *ta); +void textarea_redraw(struct text_area *ta, int x0, int y0, int x1, int y1); +bool textarea_keypress(struct text_area *ta, uint32_t key); +bool textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse, + int x, int y); +bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse, + int x, int y); + +#endif + diff --git a/desktop/textinput.h b/desktop/textinput.h index fb4d98b90..263638d3a 100644 --- a/desktop/textinput.h +++ b/desktop/textinput.h @@ -28,6 +28,7 @@ #include + struct browser_window; struct box; diff --git a/gtk/font_pango.c b/gtk/font_pango.c index e5994e71e..eda2f89c9 100644 --- a/gtk/font_pango.c +++ b/gtk/font_pango.c @@ -183,6 +183,7 @@ bool nsfont_split(const struct css_style *style, pango_layout_set_width(layout, x * PANGO_SCALE); pango_layout_set_wrap(layout, PANGO_WRAP_WORD); + pango_layout_set_single_paragraph_mode(layout, true); line = pango_layout_get_line(layout, 1); if (line) index = line->start_index - 1; diff --git a/gtk/gtk_gui.c b/gtk/gtk_gui.c index 763898186..b8217b7f1 100644 --- a/gtk/gtk_gui.c +++ b/gtk/gtk_gui.c @@ -43,6 +43,7 @@ #include "desktop/netsurf.h" #include "desktop/options.h" #include "desktop/save_pdf/pdf_plotters.h" +#include "desktop/textinput.h" #include "gtk/gtk_gui.h" #include "gtk/dialogs/gtk_options.h" #include "gtk/gtk_completion.h" @@ -734,3 +735,72 @@ static void nsgtk_PDF_no_pass(GtkButton *w, gpointer data) } #endif +uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *key) +{ + /* this function will need to become much more complex to support + * everything that the RISC OS version does. But this will do for + * now. I hope. + */ + switch (key->keyval) + { + case GDK_BackSpace: + if (key->state & GDK_SHIFT_MASK) + return KEY_DELETE_LINE_START; + else + return KEY_DELETE_LEFT; + case GDK_Delete: + if (key->state & GDK_SHIFT_MASK) + return KEY_DELETE_LINE_END; + else + return KEY_DELETE_RIGHT; + case GDK_Linefeed: return 13; + case GDK_Return: return 10; + case GDK_Left: return KEY_LEFT; + case GDK_Right: return KEY_RIGHT; + case GDK_Up: return KEY_UP; + case GDK_Down: return KEY_DOWN; + case GDK_Home: + if (key->state & GDK_CONTROL_MASK) + return KEY_TEXT_START; + else + return KEY_LINE_START; + case GDK_End: + if (key->state & GDK_CONTROL_MASK) + return KEY_TEXT_END; + else + return KEY_LINE_END; + case GDK_Page_Up: + return KEY_PAGE_UP; + case GDK_Page_Down: + return KEY_PAGE_DOWN; + case 'a': + if (key->state & GDK_CONTROL_MASK) + return KEY_SELECT_ALL; + return gdk_keyval_to_unicode(key->keyval); + case 'u': + if (key->state & GDK_CONTROL_MASK) + return KEY_CLEAR_SELECTION; + return gdk_keyval_to_unicode(key->keyval); + case GDK_Escape: + return KEY_ESCAPE; + + /* Modifiers - do nothing for now */ + case GDK_Shift_L: + case GDK_Shift_R: + case GDK_Control_L: + case GDK_Control_R: + case GDK_Caps_Lock: + case GDK_Shift_Lock: + case GDK_Meta_L: + case GDK_Meta_R: + case GDK_Alt_L: + case GDK_Alt_R: + case GDK_Super_L: + case GDK_Super_R: + case GDK_Hyper_L: + case GDK_Hyper_R: return 0; + + default: return gdk_keyval_to_unicode( + key->keyval); + } +} diff --git a/gtk/gtk_gui.h b/gtk/gtk_gui.h index eab25873b..8c9aa0afb 100644 --- a/gtk/gtk_gui.h +++ b/gtk/gtk_gui.h @@ -19,6 +19,7 @@ #ifndef GTK_GUI_H #define GTK_GUI_H +#include #include #include #include @@ -37,5 +38,7 @@ extern GtkLabel *labelTooltip; extern GtkDialog *wndOpenFile; +uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *); + #endif /* GTK_GUI_H */ diff --git a/gtk/gtk_plotters.c b/gtk/gtk_plotters.c index 935b07992..700493b4c 100644 --- a/gtk/gtk_plotters.c +++ b/gtk/gtk_plotters.c @@ -119,7 +119,7 @@ bool nsgtk_plot_rectangle(int x0, int y0, int width, int height, line_width = 1; cairo_set_line_width(current_cr, line_width); - cairo_rectangle(current_cr, x0, y0, width, height); + cairo_rectangle(current_cr, x0 + 0.5, y0 + 0.5, width, height); cairo_stroke(current_cr); return true; diff --git a/gtk/gtk_window.c b/gtk/gtk_window.c index 57ac59ca5..7649418c4 100644 --- a/gtk/gtk_window.c +++ b/gtk/gtk_window.c @@ -38,7 +38,7 @@ struct gui_window *window_list = 0; /**< first entry in win list*/ int temp_open_background = -1; -static uint32_t gdkkey_to_nskey(GdkEventKey *); + static void nsgtk_gui_window_attach_child(struct gui_window *parent, struct gui_window *child); /* Methods which apply only to a gui_window */ @@ -449,50 +449,11 @@ gboolean nsgtk_window_button_release_event(GtkWidget *widget, return TRUE; } -uint32_t gdkkey_to_nskey(GdkEventKey *key) -{ - /* this function will need to become much more complex to support - * everything that the RISC OS version does. But this will do for - * now. I hope. - */ - - switch (key->keyval) - { - case GDK_BackSpace: return KEY_DELETE_LEFT; - case GDK_Delete: return KEY_DELETE_RIGHT; - case GDK_Linefeed: return 13; - case GDK_Return: return 10; - case GDK_Left: return KEY_LEFT; - case GDK_Right: return KEY_RIGHT; - case GDK_Up: return KEY_UP; - case GDK_Down: return KEY_DOWN; - - /* Modifiers - do nothing for now */ - case GDK_Shift_L: - case GDK_Shift_R: - case GDK_Control_L: - case GDK_Control_R: - case GDK_Caps_Lock: - case GDK_Shift_Lock: - case GDK_Meta_L: - case GDK_Meta_R: - case GDK_Alt_L: - case GDK_Alt_R: - case GDK_Super_L: - case GDK_Super_R: - case GDK_Hyper_L: - case GDK_Hyper_R: return 0; - - default: return gdk_keyval_to_unicode( - key->keyval); - } -} - gboolean nsgtk_window_keypress_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct gui_window *g = data; - uint32_t nskey = gdkkey_to_nskey(event); + uint32_t nskey = gtk_gui_gdkkey_to_nskey(event); if (browser_window_key_press(g->bw, nskey)) return TRUE; diff --git a/riscos/sslcert.c b/riscos/sslcert.c index 7e9e8557c..9db500486 100644 --- a/riscos/sslcert.c +++ b/riscos/sslcert.c @@ -338,8 +338,8 @@ void ro_gui_cert_open(struct tree *tree, struct node *node) return; } if (session->issuer) - textarea_destroy(session->issuer); - session->issuer = textarea_create(w, ICON_CERT_ISSUER, + ro_textarea_destroy(session->issuer); + session->issuer = ro_textarea_create(w, ICON_CERT_ISSUER, TEXTAREA_MULTILINE | TEXTAREA_READONLY, ro_gui_desktop_font_family, ro_gui_desktop_font_size, ro_gui_desktop_font_style); @@ -348,28 +348,28 @@ void ro_gui_cert_open(struct tree *tree, struct node *node) warn_user("NoMemory", 0); return; } - if (!textarea_set_text(session->issuer, session->issuer_t)) { - textarea_destroy(session->issuer); + if (!ro_textarea_set_text(session->issuer, session->issuer_t)) { + ro_textarea_destroy(session->issuer); xwimp_delete_window(w); warn_user("NoMemory", 0); return; } if (session->subject) - textarea_destroy(session->subject); - session->subject = textarea_create(w, ICON_CERT_SUBJECT, + ro_textarea_destroy(session->subject); + session->subject = ro_textarea_create(w, ICON_CERT_SUBJECT, TEXTAREA_MULTILINE | TEXTAREA_READONLY, ro_gui_desktop_font_family, ro_gui_desktop_font_size, ro_gui_desktop_font_style); if (!session->subject) { - textarea_destroy(session->issuer); + ro_textarea_destroy(session->issuer); xwimp_delete_window(w); warn_user("NoMemory", 0); return; } - if (!textarea_set_text(session->subject, session->subject_t)) { - textarea_destroy(session->subject); - textarea_destroy(session->issuer); + if (!ro_textarea_set_text(session->subject, session->subject_t)) { + ro_textarea_destroy(session->subject); + ro_textarea_destroy(session->issuer); xwimp_delete_window(w); warn_user("NoMemory", 0); return; @@ -392,9 +392,9 @@ void ro_gui_cert_close(wimp_w w) for (i = 0; i < data->num; i++) { if (data->certs[i].subject) - textarea_destroy(data->certs[i].subject); + ro_textarea_destroy(data->certs[i].subject); if (data->certs[i].issuer) - textarea_destroy(data->certs[i].issuer); + ro_textarea_destroy(data->certs[i].issuer); } free(data->certs); free(data->url); diff --git a/riscos/textarea.c b/riscos/textarea.c index 365aa17a8..86774fad4 100644 --- a/riscos/textarea.c +++ b/riscos/textarea.c @@ -110,12 +110,12 @@ static wimp_window text_area_definition = { {} }; -static void textarea_reflow(struct text_area *ta, unsigned int line); -static bool textarea_mouse_click(wimp_pointer *pointer); -static bool textarea_key_press(wimp_key *key); -static void textarea_redraw(wimp_draw *redraw); -static void textarea_redraw_internal(wimp_draw *redraw, bool update); -static void textarea_open(wimp_open *open); +static void ro_textarea_reflow(struct text_area *ta, unsigned int line); +static bool ro_textarea_mouse_click(wimp_pointer *pointer); +static bool ro_textarea_key_press(wimp_key *key); +static void ro_textarea_redraw(wimp_draw *redraw); +static void ro_textarea_redraw_internal(wimp_draw *redraw, bool update); +static void ro_textarea_open(wimp_open *open); /** * Create a text area @@ -128,7 +128,7 @@ static void textarea_open(wimp_open *open); * \param font_style Font style to use, or 0 for default * \return Opaque handle for textarea or 0 on error */ -uintptr_t textarea_create(wimp_w parent, wimp_i icon, unsigned int flags, +uintptr_t ro_textarea_create(wimp_w parent, wimp_i icon, unsigned int flags, const char *font_family, unsigned int font_size, rufl_style font_style) { @@ -189,21 +189,21 @@ uintptr_t textarea_create(wimp_w parent, wimp_i icon, unsigned int flags, } /* set the window dimensions */ - if (!textarea_update((uintptr_t)ret)) { - textarea_destroy((uintptr_t)ret); + if (!ro_textarea_update((uintptr_t)ret)) { + ro_textarea_destroy((uintptr_t)ret); return 0; } /* and register our event handlers */ ro_gui_wimp_event_set_user_data(ret->window, ret); ro_gui_wimp_event_register_mouse_click(ret->window, - textarea_mouse_click); + ro_textarea_mouse_click); ro_gui_wimp_event_register_keypress(ret->window, - textarea_key_press); + ro_textarea_key_press); ro_gui_wimp_event_register_redraw_window(ret->window, - textarea_redraw); + ro_textarea_redraw); ro_gui_wimp_event_register_open_window(ret->window, - textarea_open); + ro_textarea_open); return (uintptr_t)ret; } @@ -213,7 +213,7 @@ uintptr_t textarea_create(wimp_w parent, wimp_i icon, unsigned int flags, * * \param self Text area to update */ -bool textarea_update(uintptr_t self) +bool ro_textarea_update(uintptr_t self) { struct text_area *ta; wimp_window_state state; @@ -291,7 +291,7 @@ bool textarea_update(uintptr_t self) } /* reflow the text */ - textarea_reflow(ta, 0); + ro_textarea_reflow(ta, 0); return true; } @@ -300,7 +300,7 @@ bool textarea_update(uintptr_t self) * * \param self Text area to destroy */ -void textarea_destroy(uintptr_t self) +void ro_textarea_destroy(uintptr_t self) { struct text_area *ta; os_error *error; @@ -329,7 +329,7 @@ void textarea_destroy(uintptr_t self) * \param text UTF-8 text to set text area's contents to * \return true on success, false on memory exhaustion */ -bool textarea_set_text(uintptr_t self, const char *text) +bool ro_textarea_set_text(uintptr_t self, const char *text) { struct text_area *ta; unsigned int len = strlen(text) + 1; @@ -353,7 +353,7 @@ bool textarea_set_text(uintptr_t self, const char *text) memcpy(ta->text, text, len); ta->text_len = len; - textarea_reflow(ta, 0); + ro_textarea_reflow(ta, 0); return true; } @@ -367,7 +367,7 @@ bool textarea_set_text(uintptr_t self, const char *text) * \param len Length (bytes) of buffer pointed to by buf, or 0 to read length * \return Length (bytes) written/required or -1 on error */ -int textarea_get_text(uintptr_t self, char *buf, unsigned int len) +int ro_textarea_get_text(uintptr_t self, char *buf, unsigned int len) { struct text_area *ta; @@ -399,7 +399,7 @@ int textarea_get_text(uintptr_t self, char *buf, unsigned int len) * \param index 0-based character index to insert at * \param text UTF-8 text to insert */ -void textarea_insert_text(uintptr_t self, unsigned int index, +void ro_textarea_insert_text(uintptr_t self, unsigned int index, const char *text) { struct text_area *ta; @@ -442,7 +442,7 @@ void textarea_insert_text(uintptr_t self, unsigned int index, ta->text_len += b_len; /** \todo calculate line to reflow from */ - textarea_reflow(ta, 0); + ro_textarea_reflow(ta, 0); } /** @@ -453,7 +453,7 @@ void textarea_insert_text(uintptr_t self, unsigned int index, * \param end End character index of replaced section (exclusive) * \param text UTF-8 text to insert */ -void textarea_replace_text(uintptr_t self, unsigned int start, +void ro_textarea_replace_text(uintptr_t self, unsigned int start, unsigned int end, const char *text) { struct text_area *ta; @@ -474,7 +474,7 @@ void textarea_replace_text(uintptr_t self, unsigned int start, end = c_len; if (start == end) - return textarea_insert_text(self, start, text); + return ro_textarea_insert_text(self, start, text); if (start > end) { int temp = end; @@ -515,7 +515,7 @@ void textarea_replace_text(uintptr_t self, unsigned int start, ta->text_len += b_len - (b_end - b_start); /** \todo calculate line to reflow from */ - textarea_reflow(ta, 0); + ro_textarea_reflow(ta, 0); } /** @@ -524,7 +524,7 @@ void textarea_replace_text(uintptr_t self, unsigned int start, * \param self Text area * \param caret 0-based character index to place caret at */ -void textarea_set_caret(uintptr_t self, unsigned int caret) +void ro_textarea_set_caret(uintptr_t self, unsigned int caret) { struct text_area *ta; size_t c_len, b_off; @@ -566,7 +566,7 @@ void textarea_set_caret(uintptr_t self, unsigned int caret) /* Finally, redraw the WIMP caret */ - index = textarea_get_caret(self); + index = ro_textarea_get_caret(self); os_line_height.x = 0; os_line_height.y = (int)((float)(ta->line_height - ta->line_spacing) * 0.62) + 1; ro_convert_pixels_to_os_units(&os_line_height, (os_mode)-1); @@ -605,7 +605,7 @@ void textarea_set_caret(uintptr_t self, unsigned int caret) * \param x X position of caret on the screen * \param y Y position of caret on the screen */ -void textarea_set_caret_xy(uintptr_t self, int x, int y) +void ro_textarea_set_caret_xy(uintptr_t self, int x, int y) { struct text_area *ta; wimp_window_state state; @@ -665,7 +665,7 @@ void textarea_set_caret_xy(uintptr_t self, int x, int y) temp = utf8_next(ta->text, ta->text_len, temp)) c_off++; - textarea_set_caret((uintptr_t)ta, c_off); + ro_textarea_set_caret((uintptr_t)ta, c_off); } /** @@ -674,7 +674,7 @@ void textarea_set_caret_xy(uintptr_t self, int x, int y) * \param self Text area * \return 0-based character index of caret location, or -1 on error */ -unsigned int textarea_get_caret(uintptr_t self) +unsigned int ro_textarea_get_caret(uintptr_t self) { struct text_area *ta; size_t c_off = 0, b_off; @@ -701,7 +701,7 @@ unsigned int textarea_get_caret(uintptr_t self) * \param ta Text area to reflow * \param line Line number to begin reflow on */ -void textarea_reflow(struct text_area *ta, unsigned int line) +void ro_textarea_reflow(struct text_area *ta, unsigned int line) { rufl_code code; char *text; @@ -846,7 +846,8 @@ void textarea_reflow(struct text_area *ta, unsigned int line) } /* Now, attempt to create vertical scrollbar */ - ro_gui_wimp_update_window_furniture(ta->window, wimp_WINDOW_VSCROLL, + ro_gui_wimp_update_window_furniture(ta->window, + wimp_WINDOW_VSCROLL, wimp_WINDOW_VSCROLL); /* Get new window state */ @@ -877,7 +878,7 @@ void textarea_reflow(struct text_area *ta, unsigned int line) ta->vis_width -= vscroll_width; /* Now we've done that, we have to reflow the text area */ - textarea_reflow(ta, 0); + ro_textarea_reflow(ta, 0); } } @@ -887,13 +888,13 @@ void textarea_reflow(struct text_area *ta, unsigned int line) * \param pointer Mouse click state block * \return true if click handled, false otherwise */ -bool textarea_mouse_click(wimp_pointer *pointer) +bool ro_textarea_mouse_click(wimp_pointer *pointer) { struct text_area *ta; ta = (struct text_area *)ro_gui_wimp_event_get_user_data(pointer->w); - textarea_set_caret_xy((uintptr_t)ta, pointer->pos.x, pointer->pos.y); + ro_textarea_set_caret_xy((uintptr_t)ta, pointer->pos.x, pointer->pos.y); return true; } @@ -903,7 +904,7 @@ bool textarea_mouse_click(wimp_pointer *pointer) * \param key Key pressed state block * \param true if press handled, false otherwise */ -bool textarea_key_press(wimp_key *key) +bool ro_textarea_key_press(wimp_key *key) { uint32_t c = (uint32_t) key->c; wimp_key keypress; @@ -925,49 +926,49 @@ bool textarea_key_press(wimp_key *key) utf8_len = utf8_from_ucs4(c, utf8); utf8[utf8_len] = '\0'; - c_pos = textarea_get_caret((uintptr_t)ta); - textarea_insert_text((uintptr_t)ta, c_pos, utf8); - textarea_set_caret((uintptr_t)ta, ++c_pos); + c_pos = ro_textarea_get_caret((uintptr_t)ta); + ro_textarea_insert_text((uintptr_t)ta, c_pos, utf8); + ro_textarea_set_caret((uintptr_t)ta, ++c_pos); redraw = true; } else { /** \todo handle command keys */ switch (c & ~IS_WIMP_KEY) { case 8: /* Backspace */ - c_pos = textarea_get_caret((uintptr_t)ta); + c_pos = ro_textarea_get_caret((uintptr_t)ta); if (c_pos > 0) { - textarea_replace_text((uintptr_t)ta, + ro_textarea_replace_text((uintptr_t)ta, c_pos - 1, c_pos, ""); - textarea_set_caret((uintptr_t)ta, c_pos - 1); + ro_textarea_set_caret((uintptr_t)ta, c_pos - 1); redraw = true; } break; case 21: /* Ctrl + U */ - textarea_set_text((uintptr_t)ta, ""); - textarea_set_caret((uintptr_t)ta, 0); + ro_textarea_set_text((uintptr_t)ta, ""); + ro_textarea_set_caret((uintptr_t)ta, 0); redraw = true; break; case wimp_KEY_DELETE: - c_pos = textarea_get_caret((uintptr_t)ta); + c_pos = ro_textarea_get_caret((uintptr_t)ta); if (os_version < RISCOS5 && c_pos > 0) { - textarea_replace_text((uintptr_t)ta, + ro_textarea_replace_text((uintptr_t)ta, c_pos - 1, c_pos, ""); - textarea_set_caret((uintptr_t)ta, c_pos - 1); + ro_textarea_set_caret((uintptr_t)ta, c_pos - 1); } else { - textarea_replace_text((uintptr_t)ta, c_pos, + ro_textarea_replace_text((uintptr_t)ta, c_pos, c_pos + 1, ""); } redraw = true; break; case wimp_KEY_LEFT: - c_pos = textarea_get_caret((uintptr_t)ta); + c_pos = ro_textarea_get_caret((uintptr_t)ta); if (c_pos > 0) - textarea_set_caret((uintptr_t)ta, c_pos - 1); + ro_textarea_set_caret((uintptr_t)ta, c_pos - 1); break; case wimp_KEY_RIGHT: - c_pos = textarea_get_caret((uintptr_t)ta); - textarea_set_caret((uintptr_t)ta, c_pos + 1); + c_pos = ro_textarea_get_caret((uintptr_t)ta); + ro_textarea_set_caret((uintptr_t)ta, c_pos + 1); break; case wimp_KEY_UP: /** \todo Move caret up a line */ @@ -984,16 +985,17 @@ bool textarea_key_press(wimp_key *key) /** \todo line end */ break; case wimp_KEY_CONTROL | wimp_KEY_UP: - textarea_set_caret((uintptr_t)ta, 0); + ro_textarea_set_caret((uintptr_t)ta, 0); break; case wimp_KEY_CONTROL | wimp_KEY_DOWN: - textarea_set_caret((uintptr_t)ta, utf8_length(ta->text)); + ro_textarea_set_caret((uintptr_t)ta, + utf8_length(ta->text)); break; case wimp_KEY_COPY: if (os_version < RISCOS5) { - c_pos = textarea_get_caret((uintptr_t)ta); - textarea_replace_text((uintptr_t)ta, c_pos, + c_pos = ro_textarea_get_caret((uintptr_t)ta); + ro_textarea_replace_text((uintptr_t)ta, c_pos, c_pos + 1, ""); } else { /** \todo line end */ @@ -1004,10 +1006,10 @@ bool textarea_key_press(wimp_key *key) case wimp_KEY_RETURN: if (ta->flags & TEXTAREA_MULTILINE) { /* Insert newline */ - c_pos = textarea_get_caret((uintptr_t)ta); - textarea_insert_text((uintptr_t)ta, c_pos, + c_pos = ro_textarea_get_caret((uintptr_t)ta); + ro_textarea_insert_text((uintptr_t)ta, c_pos, "\n"); - textarea_set_caret((uintptr_t)ta, ++c_pos); + ro_textarea_set_caret((uintptr_t)ta, ++c_pos); redraw = true; @@ -1038,7 +1040,7 @@ bool textarea_key_press(wimp_key *key) update.box.y1 = 0; update.box.x1 = ta->vis_width; update.box.y0 = -ta->line_height * (ta->line_count + 1); - textarea_redraw_internal(&update, true); + ro_textarea_redraw_internal(&update, true); } return true; @@ -1049,9 +1051,9 @@ bool textarea_key_press(wimp_key *key) * * \param redraw Redraw request block */ -void textarea_redraw(wimp_draw *redraw) +void ro_textarea_redraw(wimp_draw *redraw) { - textarea_redraw_internal(redraw, false); + ro_textarea_redraw_internal(redraw, false); } /** @@ -1060,7 +1062,7 @@ void textarea_redraw(wimp_draw *redraw) * \param redraw Redraw/update request block * \param update True if update, false if full redraw */ -void textarea_redraw_internal(wimp_draw *redraw, bool update) +void ro_textarea_redraw_internal(wimp_draw *redraw, bool update) { struct text_area *ta; int clip_x0, clip_y0, clip_x1, clip_y1; @@ -1170,7 +1172,7 @@ void textarea_redraw_internal(wimp_draw *redraw, bool update) * * \param open OpenWindow block */ -void textarea_open(wimp_open *open) +void ro_textarea_open(wimp_open *open) { os_error *error; diff --git a/riscos/textarea.h b/riscos/textarea.h index ac766d1c8..c726a0e78 100644 --- a/riscos/textarea.h +++ b/riscos/textarea.h @@ -31,20 +31,20 @@ #define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */ #define TEXTAREA_READONLY 0x02 /**< Text area is read only */ -uintptr_t textarea_create(wimp_w parent, wimp_i icon, unsigned int flags, +uintptr_t ro_textarea_create(wimp_w parent, wimp_i icon, unsigned int flags, const char *font_family, unsigned int font_size, rufl_style font_style); -bool textarea_update(uintptr_t self); -void textarea_destroy(uintptr_t self); -bool textarea_set_text(uintptr_t self, const char *text); -int textarea_get_text(uintptr_t self, char *buf, unsigned int len); -void textarea_insert_text(uintptr_t self, unsigned int index, +bool ro_textarea_update(uintptr_t self); +void ro_textarea_destroy(uintptr_t self); +bool ro_textarea_set_text(uintptr_t self, const char *text); +int ro_textarea_get_text(uintptr_t self, char *buf, unsigned int len); +void ro_textarea_insert_text(uintptr_t self, unsigned int index, const char *text); -void textarea_replace_text(uintptr_t self, unsigned int start, +void ro_textarea_replace_text(uintptr_t self, unsigned int start, unsigned int end, const char *text); -void textarea_set_caret(uintptr_t self, unsigned int caret); -void textarea_set_caret_xy(uintptr_t self, int x, int y); -unsigned int textarea_get_caret(uintptr_t self); +void ro_textarea_set_caret(uintptr_t self, unsigned int caret); +void ro_textarea_set_caret_xy(uintptr_t self, int x, int y); +unsigned int ro_textarea_get_caret(uintptr_t self); #endif diff --git a/riscos/treeview.c b/riscos/treeview.c index 5f826e865..ef23c5e3e 100644 --- a/riscos/treeview.c +++ b/riscos/treeview.c @@ -1080,7 +1080,7 @@ void ro_gui_tree_start_edit(struct tree *tree, struct node_element *element, LOG(("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess)); - tree->textarea_handle = textarea_create((wimp_w)tree->handle, + tree->textarea_handle = ro_textarea_create((wimp_w)tree->handle, (wimp_i)tree->edit_handle, 0, ro_gui_desktop_font_family, ro_gui_desktop_font_size, ro_gui_desktop_font_style); @@ -1088,12 +1088,13 @@ void ro_gui_tree_start_edit(struct tree *tree, struct node_element *element, ro_gui_tree_stop_edit(tree); return; } - textarea_set_text(tree->textarea_handle, element->text); + ro_textarea_set_text(tree->textarea_handle, element->text); if (pointer) - textarea_set_caret_xy(tree->textarea_handle, + ro_textarea_set_caret_xy(tree->textarea_handle, pointer->pos.x, pointer->pos.y); else - textarea_set_caret(tree->textarea_handle, strlen(element->text)); + ro_textarea_set_caret(tree->textarea_handle, + strlen(element->text)); tree_handle_node_element_changed(tree, element); ro_gui_tree_scroll_visible(tree, element); @@ -1114,7 +1115,7 @@ void ro_gui_tree_stop_edit(struct tree *tree) if (!tree->editing) return; if (tree->textarea_handle) { - textarea_destroy(tree->textarea_handle); + ro_textarea_destroy(tree->textarea_handle); tree->textarea_handle = 0; } error = xwimp_delete_icon((wimp_w)tree->handle, (wimp_i)tree->edit_handle); @@ -1308,7 +1309,8 @@ bool ro_gui_tree_keypress(wimp_key *key) return true; case wimp_KEY_RETURN: if ((tree->editing) && (tree->textarea_handle)) { - strlen = textarea_get_text(tree->textarea_handle, + strlen = ro_textarea_get_text( + tree->textarea_handle, NULL, 0); if (strlen == -1) { ro_gui_tree_stop_edit(tree); @@ -1321,7 +1323,7 @@ bool ro_gui_tree_keypress(wimp_key *key) warn_user("NoMemory", 0); return true; } - textarea_get_text(tree->textarea_handle, + ro_textarea_get_text(tree->textarea_handle, new_string, strlen); free((void *)tree->editing->text); tree->editing->text = new_string; -- cgit v1.2.3