mirror of
https://github.com/netsurf-browser/netsurf
synced 2025-01-01 00:34:31 +03:00
[project @ 2006-02-11 18:33:05 by adrianl]
Textarea/selection improvements and fixes svn path=/import/netsurf/; revision=2072
This commit is contained in:
parent
6b6841f07e
commit
edded10c0f
@ -903,17 +903,12 @@ void browser_window_mouse_action_html(struct browser_window *bw,
|
||||
case GADGET_TEXTAREA:
|
||||
status = messages_get("FormTextarea");
|
||||
pointer = GUI_POINTER_CARET;
|
||||
if (mouse & (BROWSER_MOUSE_MOD_1 |
|
||||
BROWSER_MOUSE_MOD_2)) {
|
||||
if (text_box) {
|
||||
selection_click(bw->sel, text_box,
|
||||
mouse, x - box_x,
|
||||
y - box_y);
|
||||
if (selection_dragging(bw->sel))
|
||||
bw->drag_type =
|
||||
DRAGGING_SELECTION;
|
||||
}
|
||||
} else if (mouse & BROWSER_MOUSE_CLICK_1) {
|
||||
|
||||
if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) {
|
||||
|
||||
if (text_box && selection_root(bw->sel) != gadget_box)
|
||||
selection_init(bw->sel, gadget_box);
|
||||
|
||||
browser_window_textarea_click(bw,
|
||||
mouse,
|
||||
gadget_box,
|
||||
@ -921,15 +916,16 @@ void browser_window_mouse_action_html(struct browser_window *bw,
|
||||
gadget_box_y,
|
||||
x - gadget_box_x,
|
||||
y - gadget_box_y);
|
||||
} else if (text_box) {
|
||||
if (mouse & (BROWSER_MOUSE_DRAG_1 |
|
||||
BROWSER_MOUSE_DRAG_2))
|
||||
selection_init(bw->sel, gadget_box);
|
||||
}
|
||||
|
||||
selection_click(bw->sel, text_box, mouse,
|
||||
x - box_x, y - box_y);
|
||||
if (selection_dragging(bw->sel))
|
||||
if (text_box) {
|
||||
selection_click(bw->sel, text_box, mouse, x - box_x, y - box_y);
|
||||
|
||||
if (selection_dragging(bw->sel)) {
|
||||
bw->drag_type = DRAGGING_SELECTION;
|
||||
status = messages_get("Selecting");
|
||||
} else
|
||||
status = c->status_message;
|
||||
}
|
||||
break;
|
||||
case GADGET_TEXTBOX:
|
||||
@ -1021,6 +1017,12 @@ void browser_window_mouse_action_html(struct browser_window *bw,
|
||||
|
||||
} else {
|
||||
|
||||
/* if clicking in the main page, remove the selection from any text areas */
|
||||
if (text_box &&
|
||||
(mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) &&
|
||||
selection_root(bw->sel) != c->data.html.layout)
|
||||
selection_init(bw->sel, c->data.html.layout);
|
||||
|
||||
if (text_box && selection_click(bw->sel, text_box, mouse,
|
||||
x - box_x, y - box_y)) {
|
||||
|
||||
|
@ -19,13 +19,31 @@
|
||||
#include "netsurf/desktop/selection.h"
|
||||
#include "netsurf/render/box.h"
|
||||
#include "netsurf/render/font.h"
|
||||
#include "netsurf/render/form.h"
|
||||
#include "netsurf/utils/log.h"
|
||||
#include "netsurf/utils/utf8.h"
|
||||
#include "netsurf/utils/utils.h"
|
||||
|
||||
|
||||
/**
|
||||
* Text selection works by labelling each node in the box tree with its
|
||||
* start index in the textual representation of the tree's content.
|
||||
*
|
||||
* Text input fields and text areas have their own number spaces so that
|
||||
* they can be relabelled more efficiently when editing (rather than relabel
|
||||
* the entire box tree) and so that selections are either wholly within
|
||||
* or wholly without the textarea/input box.
|
||||
*/
|
||||
|
||||
#define IS_TEXT(box) ((box)->text && !(box)->object)
|
||||
|
||||
#define IS_INPUT(box) ((box)->gadget && \
|
||||
((box)->gadget->type == GADGET_TEXTAREA || (box)->gadget->type == GADGET_TEXTBOX))
|
||||
|
||||
/** check whether the given text box is in the same number space as the
|
||||
current selection; number spaces are identified by their uppermost nybble */
|
||||
|
||||
#define SAME_SPACE(s, offset) (((s)->max_idx & 0xF0000000U) == ((offset) & 0xF0000000U))
|
||||
|
||||
|
||||
struct rdw_info {
|
||||
@ -132,13 +150,22 @@ void selection_destroy(struct selection *s)
|
||||
|
||||
void selection_reinit(struct selection *s, struct box *root)
|
||||
{
|
||||
unsigned root_idx;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (s->root == root) {
|
||||
/* keep the same number space as before, because we want
|
||||
to keep the selection too */
|
||||
root_idx = (s->max_idx & 0xF0000000U);
|
||||
}
|
||||
else {
|
||||
static int next_idx = 0;
|
||||
root_idx = (next_idx++) << 28;
|
||||
}
|
||||
|
||||
s->root = root;
|
||||
if (root) {
|
||||
int root_idx = 0;
|
||||
if (root->gadget) root_idx = 0x10000000;
|
||||
|
||||
s->max_idx = selection_label_subtree(s, root, root_idx);
|
||||
}
|
||||
else
|
||||
@ -195,7 +222,8 @@ unsigned selection_label_subtree(struct selection *s, struct box *node, unsigned
|
||||
idx += node->length + node->space;
|
||||
|
||||
while (child) {
|
||||
idx = selection_label_subtree(s, child, idx);
|
||||
if (!IS_INPUT(child))
|
||||
idx = selection_label_subtree(s, child, idx);
|
||||
child = child->next;
|
||||
}
|
||||
|
||||
@ -223,7 +251,7 @@ bool selection_click(struct selection *s, struct box *box,
|
||||
int pos = -1; /* 0 = inside selection, 1 = after it */
|
||||
int idx;
|
||||
|
||||
if (!s->root)
|
||||
if (!s->root ||!SAME_SPACE(s, box->byte_offset))
|
||||
return false; /* not our problem */
|
||||
|
||||
nsfont_position_in_string(box->style,
|
||||
@ -327,6 +355,9 @@ void selection_track(struct selection *s, struct box *box,
|
||||
int pixel_offset;
|
||||
int idx;
|
||||
|
||||
if (!SAME_SPACE(s, box->byte_offset))
|
||||
return;
|
||||
|
||||
nsfont_position_in_string(box->style,
|
||||
box->text,
|
||||
box->length,
|
||||
@ -580,6 +611,7 @@ void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx)
|
||||
{
|
||||
struct rdw_info rdw;
|
||||
|
||||
if (end_idx < start_idx) LOG(("*** asked to redraw from %d to %d", start_idx, end_idx));
|
||||
assert(end_idx >= start_idx);
|
||||
rdw.inited = false;
|
||||
if (traverse_tree(s->root, start_idx, end_idx, redraw_handler, &rdw) &&
|
||||
@ -635,6 +667,8 @@ void selection_select_all(struct selection *s)
|
||||
old_start = s->start_idx;
|
||||
old_end = s->end_idx;
|
||||
|
||||
LOG(("selection was %d: %u to %u, max %u", was_defined, old_start, old_end, s->max_idx));
|
||||
|
||||
s->defined = true;
|
||||
s->start_idx = 0;
|
||||
s->end_idx = s->max_idx;
|
||||
@ -926,3 +960,4 @@ void selection_update(struct selection *s, size_t byte_offset,
|
||||
s->end_idx += max(change, byte_offset - s->end_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,9 @@ void selection_destroy(struct selection *s);
|
||||
void selection_init(struct selection *s, struct box *root);
|
||||
void selection_reinit(struct selection *s, struct box *root);
|
||||
|
||||
/* struct box *selection_root(struct selection *s); */
|
||||
#define selection_root(s) ((s)->root)
|
||||
|
||||
/* bool selection_defined(struct selection *s); */
|
||||
#define selection_defined(s) ((s)->defined)
|
||||
|
||||
|
@ -31,6 +31,12 @@
|
||||
#include "netsurf/utils/utf8.h"
|
||||
#include "netsurf/utils/utils.h"
|
||||
|
||||
|
||||
/** ghost caret used to indicate the insertion point when dragging text
|
||||
into a textarea/input field */
|
||||
struct caret ghost_caret;
|
||||
|
||||
|
||||
static void browser_window_textarea_callback(struct browser_window *bw,
|
||||
wchar_t key, void *p);
|
||||
static void browser_window_input_callback(struct browser_window *bw,
|
||||
@ -49,11 +55,12 @@ static void input_update_display(struct browser_window *bw, struct box *input,
|
||||
bool redraw);
|
||||
static bool textbox_insert(struct browser_window *bw, struct box *text_box,
|
||||
unsigned char_offset, const char *utf8, unsigned utf8_len);
|
||||
static bool textbox_delete(struct box *text_box, unsigned char_offset,
|
||||
unsigned utf8_len);
|
||||
static bool textbox_delete(struct browser_window *bw, struct box *text_box,
|
||||
unsigned char_offset, unsigned utf8_len);
|
||||
static struct box *textarea_insert_break(struct browser_window *bw,
|
||||
struct box *text_box, size_t char_offset);
|
||||
static bool delete_handler(struct box *b, int offset, size_t length);
|
||||
static bool delete_handler(struct browser_window *bw, struct box *b,
|
||||
int offset, size_t length);
|
||||
static struct box *line_start(struct box *text_box);
|
||||
static struct box *line_end(struct box *text_box);
|
||||
static struct box *line_above(struct box *text_box);
|
||||
@ -67,6 +74,148 @@ static bool word_left(const char *text, int *poffset, int *pchars);
|
||||
static bool word_right(const char *text, int len, int *poffset, int *pchars);
|
||||
|
||||
|
||||
/**
|
||||
* Remove the given text caret from the window by invalidating it
|
||||
* and causing its former position to be redrawn.
|
||||
*
|
||||
* \param c structure describing text caret
|
||||
*/
|
||||
|
||||
void caret_remove(struct caret *c)
|
||||
{
|
||||
if (c->defined) {
|
||||
int w = (c->height + 7) / 8;
|
||||
int xc = c->x;
|
||||
c->defined = false;
|
||||
browser_window_redraw_rect(c->bw, xc - w, c->y, 2 * w, c->height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the given text caret's position within the window (text box
|
||||
* and byte/pixel offsets within the UTF-8 content of that text box)
|
||||
* and draw it.
|
||||
*
|
||||
* \param c structure describing text caret
|
||||
* \param bw browser window containing caret
|
||||
* \param box INLINE box containing caret
|
||||
* \param char_offset byte offset within UTF-8 representation
|
||||
* \param pixel_offset from left side of box
|
||||
*/
|
||||
|
||||
void caret_set_position(struct caret *c, struct browser_window *bw,
|
||||
struct box *text_box, int char_offset, int pixel_offset)
|
||||
{
|
||||
struct rect r;
|
||||
int xc;
|
||||
int w;
|
||||
|
||||
box_bounds(text_box, &r);
|
||||
|
||||
c->bw = bw;
|
||||
c->text_box = text_box;
|
||||
c->char_offset = char_offset;
|
||||
|
||||
c->x = xc = r.x0 + pixel_offset;
|
||||
c->y = r.y0;
|
||||
c->height = r.y1 - r.y0;
|
||||
w = (c->height + 7) / 8;
|
||||
|
||||
c->defined = true;
|
||||
|
||||
browser_window_redraw_rect(c->bw, xc - w, c->y, w * 2, c->height);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given the x,y co-ordinates of a point within a textarea, return the
|
||||
* INLINE box pointer, and the character and pixel offsets within that
|
||||
* box at which the caret should be positioned. (eg. for mouse clicks,
|
||||
* drag-and-drop insertions etc)
|
||||
*
|
||||
* \param textarea the textarea being considered
|
||||
* \param x x ordinate of point
|
||||
* \param y y ordinate of point
|
||||
* \param pchar_offset receives the char offset within the INLINE box
|
||||
* \param ppixel_offset receives the pixel offset within the INLINE box
|
||||
* \return pointer to INLINE box
|
||||
*/
|
||||
|
||||
struct box *textarea_get_position(struct box *textarea, int x, int y,
|
||||
int *pchar_offset, int *ppixel_offset)
|
||||
{
|
||||
/* A textarea is an INLINE_BLOCK containing a single
|
||||
* INLINE_CONTAINER, which contains the text as runs of INLINE
|
||||
* separated by BR. There is at least one INLINE. The first and
|
||||
* last boxes are INLINE. Consecutive BR may not be present. These
|
||||
* constraints are satisfied by using a 0-length INLINE for blank
|
||||
* lines. */
|
||||
|
||||
struct box *inline_container, *text_box;
|
||||
|
||||
inline_container = textarea->children;
|
||||
|
||||
if (inline_container->y + inline_container->height < y) {
|
||||
/* below the bottom of the textarea: place caret at end */
|
||||
text_box = inline_container->last;
|
||||
assert(text_box->type == BOX_TEXT);
|
||||
assert(text_box->text);
|
||||
/** \todo handle errors */
|
||||
nsfont_position_in_string(text_box->style, text_box->text,
|
||||
text_box->length,
|
||||
(unsigned int)(x - text_box->x),
|
||||
pchar_offset, ppixel_offset);
|
||||
} else {
|
||||
/* find the relevant text box */
|
||||
y -= inline_container->y;
|
||||
for (text_box = inline_container->children;
|
||||
text_box &&
|
||||
text_box->y + text_box->height < y;
|
||||
text_box = text_box->next)
|
||||
;
|
||||
for (; text_box && text_box->type != BOX_BR &&
|
||||
text_box->y <= y &&
|
||||
text_box->x + text_box->width < x;
|
||||
text_box = text_box->next)
|
||||
;
|
||||
if (!text_box) {
|
||||
/* past last text box */
|
||||
text_box = inline_container->last;
|
||||
assert(text_box->type == BOX_TEXT);
|
||||
assert(text_box->text);
|
||||
nsfont_position_in_string(text_box->style,
|
||||
text_box->text,
|
||||
text_box->length,
|
||||
textarea->width,
|
||||
pchar_offset, ppixel_offset);
|
||||
} else {
|
||||
/* in a text box */
|
||||
if (text_box->type == BOX_BR)
|
||||
text_box = text_box->prev;
|
||||
else if (y < text_box->y && text_box->prev) {
|
||||
if (text_box->prev->type == BOX_BR) {
|
||||
assert(text_box->prev->prev);
|
||||
text_box = text_box->prev->prev;
|
||||
}
|
||||
else
|
||||
text_box = text_box->prev;
|
||||
}
|
||||
assert(text_box->type == BOX_TEXT);
|
||||
assert(text_box->text);
|
||||
nsfont_position_in_string(text_box->style,
|
||||
text_box->text,
|
||||
text_box->length,
|
||||
(unsigned int)(x - text_box->x),
|
||||
pchar_offset, ppixel_offset);
|
||||
}
|
||||
}
|
||||
|
||||
assert(text_box);
|
||||
return text_box;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle clicks in a text area by placing the caret.
|
||||
*
|
||||
@ -92,64 +241,11 @@ void browser_window_textarea_click(struct browser_window *bw,
|
||||
* lines. */
|
||||
|
||||
int char_offset = 0, pixel_offset = 0, new_scroll_y;
|
||||
struct box *inline_container, *text_box;
|
||||
struct box *inline_container = textarea->children;
|
||||
struct box *text_box;
|
||||
|
||||
inline_container = textarea->children;
|
||||
|
||||
if (inline_container->y + inline_container->height < y) {
|
||||
/* below the bottom of the textarea: place caret at end */
|
||||
text_box = inline_container->last;
|
||||
assert(text_box->type == BOX_TEXT);
|
||||
assert(text_box->text);
|
||||
/** \todo handle errors */
|
||||
nsfont_position_in_string(text_box->style, text_box->text,
|
||||
text_box->length,
|
||||
textarea->width,
|
||||
&char_offset, &pixel_offset);
|
||||
} else {
|
||||
/* find the relevant text box */
|
||||
y -= inline_container->y;
|
||||
for (text_box = inline_container->children;
|
||||
text_box &&
|
||||
text_box->y + text_box->height < y;
|
||||
text_box = text_box->next)
|
||||
;
|
||||
for (; text_box && text_box->type != BOX_BR &&
|
||||
text_box->y <= y &&
|
||||
text_box->x + text_box->width < x;
|
||||
text_box = text_box->next)
|
||||
;
|
||||
if (!text_box) {
|
||||
/* past last text box */
|
||||
text_box = inline_container->last;
|
||||
assert(text_box->type == BOX_TEXT);
|
||||
assert(text_box->text);
|
||||
nsfont_position_in_string(text_box->style,
|
||||
text_box->text,
|
||||
text_box->length,
|
||||
textarea->width,
|
||||
&char_offset, &pixel_offset);
|
||||
} else {
|
||||
/* in a text box */
|
||||
if (text_box->type == BOX_BR)
|
||||
text_box = text_box->prev;
|
||||
else if (y < text_box->y && text_box->prev) {
|
||||
if (text_box->prev->type == BOX_BR) {
|
||||
assert(text_box->prev->prev);
|
||||
text_box = text_box->prev->prev;
|
||||
}
|
||||
else
|
||||
text_box = text_box->prev;
|
||||
}
|
||||
assert(text_box->type == BOX_TEXT);
|
||||
assert(text_box->text);
|
||||
nsfont_position_in_string(text_box->style,
|
||||
text_box->text,
|
||||
text_box->length,
|
||||
(unsigned int)(x - text_box->x),
|
||||
&char_offset, &pixel_offset);
|
||||
}
|
||||
}
|
||||
text_box = textarea_get_position(textarea, x, y,
|
||||
&char_offset, &pixel_offset);
|
||||
|
||||
/* scroll to place the caret in the centre of the visible region */
|
||||
new_scroll_y = inline_container->y + text_box->y +
|
||||
@ -258,11 +354,24 @@ void browser_window_textarea_callback(struct browser_window *bw,
|
||||
int prev_offset = char_offset;
|
||||
char_offset = utf8_prev(text_box->text, char_offset);
|
||||
|
||||
textbox_delete(text_box, char_offset, prev_offset - char_offset);
|
||||
textbox_delete(bw, text_box, char_offset,
|
||||
prev_offset - char_offset);
|
||||
}
|
||||
reflow = true;
|
||||
break;
|
||||
|
||||
case KEY_DELETE_LINE_END: {
|
||||
struct box *end_box = line_end(text_box);
|
||||
if (end_box != text_box ||
|
||||
char_offset < text_box->length + text_box->space) {
|
||||
/* there's something at the end of the line to delete */
|
||||
textarea_cut(bw, text_box, char_offset,
|
||||
end_box, end_box->length + end_box->space);
|
||||
reflow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* no break */
|
||||
case KEY_DELETE_RIGHT: /* delete to right */
|
||||
if (char_offset >= text_box->length) {
|
||||
/* at the end of a text box */
|
||||
@ -296,7 +405,8 @@ void browser_window_textarea_callback(struct browser_window *bw,
|
||||
/* delete a character */
|
||||
int next_offset = utf8_next(text_box->text, text_box->length,
|
||||
char_offset);
|
||||
textbox_delete(text_box, char_offset, next_offset - char_offset);
|
||||
textbox_delete(bw, text_box, char_offset,
|
||||
next_offset - char_offset);
|
||||
}
|
||||
reflow = true;
|
||||
break;
|
||||
@ -505,16 +615,12 @@ void browser_window_textarea_callback(struct browser_window *bw,
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_DELETE_LINE_START:
|
||||
textarea_cut(bw, line_start(text_box), 0, text_box, char_offset);
|
||||
case KEY_DELETE_LINE_START: {
|
||||
struct box *start_box = line_start(text_box);
|
||||
textarea_cut(bw, start_box, 0, text_box, char_offset);
|
||||
text_box = start_box;
|
||||
char_offset = 0;
|
||||
reflow = true;
|
||||
break;
|
||||
|
||||
case KEY_DELETE_LINE_END: {
|
||||
struct box *end_box = line_end(text_box);
|
||||
textarea_cut(bw, text_box, char_offset, end_box, end_box->length);
|
||||
reflow = true;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -522,11 +628,11 @@ void browser_window_textarea_callback(struct browser_window *bw,
|
||||
return;
|
||||
}
|
||||
|
||||
/* box_dump(textarea, 0); */
|
||||
/* for (struct box *t = inline_container->children; t; t = t->next) {
|
||||
/*
|
||||
box_dump(textarea, 0);
|
||||
for (struct box *t = inline_container->children; t; t = t->next) {
|
||||
assert(t->type == BOX_TEXT);
|
||||
assert(t->text);
|
||||
assert(t->font);
|
||||
assert(t->parent == inline_container);
|
||||
if (t->next) assert(t->next->prev == t);
|
||||
if (t->prev) assert(t->prev->next == t);
|
||||
@ -543,13 +649,17 @@ void browser_window_textarea_callback(struct browser_window *bw,
|
||||
if (reflow)
|
||||
textarea_reflow(bw, textarea, inline_container);
|
||||
|
||||
if (text_box->length < char_offset) {
|
||||
/* the text box has been split and the caret is in the
|
||||
* second part */
|
||||
char_offset -= (text_box->length + 1); /* +1 for the space */
|
||||
text_box = text_box->next;
|
||||
assert(text_box);
|
||||
assert(char_offset <= text_box->length);
|
||||
if (text_box->length + text_box->space <= char_offset) {
|
||||
if (text_box->next && text_box->next->type == BOX_TEXT) {
|
||||
/* the text box has been split when reflowing and
|
||||
the caret is in the second part */
|
||||
char_offset -= (text_box->length + text_box->space);
|
||||
text_box = text_box->next;
|
||||
assert(text_box);
|
||||
assert(char_offset <= text_box->length);
|
||||
}
|
||||
else
|
||||
char_offset = text_box->length + text_box->space;
|
||||
}
|
||||
|
||||
nsfont_width(text_box->style, text_box->text,
|
||||
@ -755,7 +865,7 @@ void browser_window_input_callback(struct browser_window *bw,
|
||||
/* Go to the previous valid UTF-8 character */
|
||||
box_offset = utf8_prev(text_box->text, box_offset);
|
||||
|
||||
textbox_delete(text_box, box_offset,
|
||||
textbox_delete(bw, text_box, box_offset,
|
||||
prev_offset - box_offset);
|
||||
changed = true;
|
||||
}
|
||||
@ -784,7 +894,7 @@ void browser_window_input_callback(struct browser_window *bw,
|
||||
next_offset = utf8_next(text_box->text, text_box->length,
|
||||
box_offset);
|
||||
|
||||
textbox_delete(text_box, box_offset,
|
||||
textbox_delete(bw, text_box, box_offset,
|
||||
next_offset - box_offset);
|
||||
changed = true;
|
||||
}
|
||||
@ -915,11 +1025,10 @@ void browser_window_input_callback(struct browser_window *bw,
|
||||
break;
|
||||
|
||||
case KEY_DELETE_LINE_START:
|
||||
|
||||
if (box_offset <= 0) return;
|
||||
|
||||
/* Text box */
|
||||
textbox_delete(text_box, 0, box_offset);
|
||||
textbox_delete(bw, text_box, 0, box_offset);
|
||||
box_offset = 0;
|
||||
|
||||
/* Gadget */
|
||||
@ -932,12 +1041,11 @@ void browser_window_input_callback(struct browser_window *bw,
|
||||
break;
|
||||
|
||||
case KEY_DELETE_LINE_END:
|
||||
|
||||
if (box_offset >= text_box->length)
|
||||
return;
|
||||
|
||||
/* Text box */
|
||||
textbox_delete(text_box, box_offset, text_box->length - box_offset);
|
||||
textbox_delete(bw, text_box, box_offset, text_box->length - box_offset);
|
||||
/* Gadget */
|
||||
input->gadget->length = form_offset;
|
||||
input->gadget->value[form_offset] = 0;
|
||||
@ -1326,7 +1434,14 @@ void input_update_display(struct browser_window *bw, struct box *input,
|
||||
bool textbox_insert(struct browser_window *bw, struct box *text_box,
|
||||
unsigned char_offset, const char *utf8, unsigned utf8_len)
|
||||
{
|
||||
char *text = talloc_realloc(bw->current_content, text_box->text,
|
||||
char *text;
|
||||
|
||||
/* code does not support appending after the optional trailing space
|
||||
(this would require inserting a real space and determining whether
|
||||
the resultant string ends in a space) */
|
||||
assert(char_offset <= text_box->length);
|
||||
|
||||
text = talloc_realloc(bw->current_content, text_box->text,
|
||||
char, text_box->length + utf8_len + 1);
|
||||
if (!text) {
|
||||
warn_user("NoMemory", 0);
|
||||
@ -1342,6 +1457,9 @@ bool textbox_insert(struct browser_window *bw, struct box *text_box,
|
||||
/* nothing should assume that the text is terminated, but just in case */
|
||||
text_box->text[text_box->length] = 0;
|
||||
|
||||
selection_update(bw->sel, text_box->byte_offset + char_offset,
|
||||
utf8_len, false);
|
||||
|
||||
text_box->width = UNKNOWN_WIDTH;
|
||||
|
||||
return true;
|
||||
@ -1351,23 +1469,44 @@ bool textbox_insert(struct browser_window *bw, struct box *text_box,
|
||||
/**
|
||||
* Delete a number of chars from a text box
|
||||
*
|
||||
* \param bw browser window
|
||||
* \param text_box text box
|
||||
* \param char_offset offset within text box (bytes) of first char to delete
|
||||
* \param utf8_len length (bytes) of chars to be deleted
|
||||
*/
|
||||
|
||||
bool textbox_delete(struct box *text_box, unsigned char_offset, unsigned utf8_len)
|
||||
bool textbox_delete(struct browser_window *bw, struct box *text_box,
|
||||
unsigned char_offset, unsigned utf8_len)
|
||||
{
|
||||
unsigned prev_offset = char_offset + utf8_len;
|
||||
if (prev_offset <= text_box->length) {
|
||||
memmove(text_box->text + char_offset,
|
||||
text_box->text + prev_offset,
|
||||
text_box->length - prev_offset);
|
||||
text_box->length -= (prev_offset - char_offset);
|
||||
unsigned next_offset = char_offset + utf8_len;
|
||||
if (next_offset <= text_box->length + text_box->space) {
|
||||
/* handle removal of trailing space */
|
||||
if (text_box->space && next_offset > text_box->length) {
|
||||
if (char_offset > 0) {
|
||||
/* is the trailing character still a space? */
|
||||
int tmp = utf8_prev(text_box->text, char_offset);
|
||||
if (isspace(text_box->text[tmp]))
|
||||
char_offset = tmp;
|
||||
else
|
||||
text_box->space = false;
|
||||
}
|
||||
else
|
||||
text_box->space = false;
|
||||
text_box->length = char_offset;
|
||||
}
|
||||
else {
|
||||
memmove(text_box->text + char_offset,
|
||||
text_box->text + next_offset,
|
||||
text_box->length - next_offset);
|
||||
text_box->length -= utf8_len;
|
||||
}
|
||||
|
||||
/* nothing should assume that the text is terminated, but just in case */
|
||||
text_box->text[text_box->length] = 0;
|
||||
|
||||
selection_update(bw->sel, text_box->byte_offset + char_offset,
|
||||
-(int)utf8_len, false);
|
||||
|
||||
text_box->width = UNKNOWN_WIDTH;
|
||||
return true;
|
||||
}
|
||||
@ -1378,21 +1517,27 @@ bool textbox_delete(struct box *text_box, unsigned char_offset, unsigned utf8_le
|
||||
/**
|
||||
* Delete some text from a box, or delete the box in its entirety
|
||||
*
|
||||
* \param bw browser window
|
||||
* \param b box
|
||||
* \param offset start offset of text to be deleted (in bytes)
|
||||
* \param length length of text to be deleted
|
||||
* \return true iff successful
|
||||
*/
|
||||
|
||||
bool delete_handler(struct box *b, int offset, size_t length)
|
||||
bool delete_handler(struct browser_window *bw, struct box *b,
|
||||
int offset, size_t length)
|
||||
{
|
||||
if (offset <= 0 && length >= b->length) {
|
||||
if (offset <= 0 && length >= b->length + b->space) {
|
||||
selection_update(bw->sel, b->byte_offset,
|
||||
-(b->length + b->space), false);
|
||||
|
||||
/* remove the entire box */
|
||||
box_unlink_and_free(b);
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return textbox_delete(b, offset, length);
|
||||
return textbox_delete(bw, b, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1521,17 +1666,11 @@ bool textarea_cut(struct browser_window *bw,
|
||||
{
|
||||
struct box *box = start_box;
|
||||
bool success = true;
|
||||
bool del = true;
|
||||
bool del = false; /* caller expects start_box to persist */
|
||||
|
||||
if (!gui_empty_clipboard())
|
||||
return false;
|
||||
|
||||
if (!start_idx && (!start_box->prev || start_box->prev->type == BOX_BR)) {
|
||||
/* deletion would leave two adjacent BRs, so just collapse
|
||||
the start box to an empty TEXT rather than deleting it */
|
||||
del = false;
|
||||
}
|
||||
|
||||
while (box && box != end_box) {
|
||||
/* read before deletion, in case the whole box goes */
|
||||
struct box *next = box->next;
|
||||
@ -1552,14 +1691,15 @@ bool textarea_cut(struct browser_window *bw,
|
||||
}
|
||||
|
||||
if (del) {
|
||||
if (!delete_handler(box, start_idx,
|
||||
box->length - start_idx)) {
|
||||
if (!delete_handler(bw, box, start_idx,
|
||||
(box->length + box->space) - start_idx)) {
|
||||
gui_commit_clipboard();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
textbox_delete(box, start_idx, box->length - start_idx);
|
||||
textbox_delete(bw, box, start_idx,
|
||||
(box->length + box->space) - start_idx);
|
||||
}
|
||||
|
||||
del = true;
|
||||
@ -1569,13 +1709,13 @@ bool textarea_cut(struct browser_window *bw,
|
||||
|
||||
/* and the last box */
|
||||
if (box) {
|
||||
if (gui_add_to_clipboard(box->text + start_idx, end_idx, box->space)) {
|
||||
if (gui_add_to_clipboard(box->text + start_idx, end_idx - start_idx, box->space)) {
|
||||
if (del) {
|
||||
if (!delete_handler(box, start_idx, end_idx - start_idx))
|
||||
if (!delete_handler(bw, box, start_idx, end_idx - start_idx))
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
textbox_delete(box, start_idx, end_idx - start_idx);
|
||||
textbox_delete(bw, box, start_idx, end_idx - start_idx);
|
||||
}
|
||||
else
|
||||
success = false;
|
||||
|
@ -46,12 +46,41 @@ enum input_key {
|
||||
};
|
||||
|
||||
|
||||
struct caret
|
||||
{
|
||||
bool defined;
|
||||
|
||||
struct browser_window *bw;
|
||||
struct box *text_box;
|
||||
size_t char_offset;
|
||||
|
||||
/* document co-ordinates of bottom left of caret */
|
||||
int x;
|
||||
int y;
|
||||
int height;
|
||||
};
|
||||
|
||||
|
||||
/** There's a single ghost caret used to implement
|
||||
* drag-and-drop of text into text areas and input fields.
|
||||
*/
|
||||
|
||||
extern struct caret ghost_caret;
|
||||
|
||||
|
||||
void caret_set_position(struct caret *c, struct browser_window *bw,
|
||||
struct box *text_box, int char_offset, int pixel_offset);
|
||||
void caret_remove(struct caret *c);
|
||||
|
||||
|
||||
struct box *textarea_get_position(struct box *textarea, int x, int y,
|
||||
int *pchar_offset, int *ppixel_offset);
|
||||
|
||||
void browser_window_textarea_click(struct browser_window *bw,
|
||||
browser_mouse_state mouse,
|
||||
struct box *textarea,
|
||||
int box_x, int box_y,
|
||||
int x, int y);
|
||||
//bool browser_window_textarea_paste(struct browser_window *bw,
|
||||
|
||||
void browser_window_input_click(struct browser_window* bw,
|
||||
struct box *input,
|
||||
|
@ -120,6 +120,8 @@ void ro_gui_selection_drag_end(struct gui_window *g, wimp_dragged *drag)
|
||||
os_error *error;
|
||||
int x, y;
|
||||
|
||||
LOG(("ending text selection drag"));
|
||||
|
||||
gui_current_drag_type = GUI_DRAG_NONE;
|
||||
|
||||
scroll.w = g->window;
|
||||
@ -163,8 +165,8 @@ void ro_gui_selection_drag_end(struct gui_window *g, wimp_dragged *drag)
|
||||
/**
|
||||
* Selection traversal routine for appending text to the current contents
|
||||
* of the clipboard.
|
||||
|
||||
* \param box pointer to text box being (partially) added
|
||||
*
|
||||
* \param box pointer to text box being (partially) added (or NULL for newline)
|
||||
* \param offset start offset of text within box (bytes)
|
||||
* \param length length of text to be appended (bytes)
|
||||
* \param handle unused handle, we don't need one
|
||||
@ -178,7 +180,7 @@ bool copy_handler(struct box *box, int offset, size_t length, void *handle)
|
||||
size_t len;
|
||||
|
||||
if (box) {
|
||||
len = min(length, box->length - offset);
|
||||
len = min(length, box->length - offset);
|
||||
text = box->text + offset;
|
||||
if (box->space && length > len) space = true;
|
||||
}
|
||||
@ -260,15 +262,17 @@ bool gui_add_to_clipboard(const char *text, size_t length, bool space)
|
||||
|
||||
bool gui_commit_clipboard(void)
|
||||
{
|
||||
utf8_convert_ret res;
|
||||
char *new_cb;
|
||||
if (clip_length) {
|
||||
utf8_convert_ret res;
|
||||
char *new_cb;
|
||||
|
||||
res = utf8_to_local_encoding(clipboard, clip_length, &new_cb);
|
||||
if (res == UTF8_CONVERT_OK) {
|
||||
free(clipboard);
|
||||
clipboard = new_cb;
|
||||
res = utf8_to_local_encoding(clipboard, clip_length, &new_cb);
|
||||
if (res == UTF8_CONVERT_OK) {
|
||||
free(clipboard);
|
||||
clipboard = new_cb;
|
||||
/* \todo utf8_to_local_encoding should return the length! */
|
||||
clip_alloc = clip_length = strlen(new_cb);
|
||||
clip_alloc = clip_length = strlen(new_cb);
|
||||
}
|
||||
}
|
||||
|
||||
if (!owns_clipboard) {
|
||||
|
Loading…
Reference in New Issue
Block a user