summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2009-06-27 13:59:25 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2009-06-27 13:59:25 +0000
commitca96353d9fac049057535ed7fdff19e43ac79666 (patch)
tree39237c6894679fc7434de9a490458c4d51227270
parentfa99a7a3ce6aea22368c75d38866e4a95ab8fd84 (diff)
downloadnetsurf-ca96353d9fac049057535ed7fdff19e43ac79666.tar.gz
netsurf-ca96353d9fac049057535ed7fdff19e43ac79666.tar.bz2
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
-rw-r--r--Makefile.sources2
-rw-r--r--desktop/browser.h17
-rw-r--r--desktop/textarea.c1379
-rw-r--r--desktop/textarea.h58
-rw-r--r--desktop/textinput.h1
-rw-r--r--gtk/font_pango.c1
-rw-r--r--gtk/gtk_gui.c70
-rw-r--r--gtk/gtk_gui.h3
-rw-r--r--gtk/gtk_plotters.c2
-rw-r--r--gtk/gtk_window.c43
-rw-r--r--riscos/sslcert.c24
-rw-r--r--riscos/textarea.c128
-rw-r--r--riscos/textarea.h20
-rw-r--r--riscos/treeview.c16
14 files changed, 1622 insertions, 142 deletions
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 <jmb@netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Single/Multi-line UTF-8 text area (implementation)
+ */
+
+#include <stdint.h>
+#include <string.h>
+#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 <jmb@netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Single/Multi-line UTF-8 text area (interface)
+ */
+
+#ifndef _NETSURF_DESKTOP_TEXTAREA_H_
+#define _NETSURF_DESKTOP_TEXTAREA_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#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 <stdbool.h>
+
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 <inttypes.h>
#include <stdbool.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
@@ -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;