summaryrefslogtreecommitdiff
path: root/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'desktop')
-rw-r--r--desktop/browser.c21
-rw-r--r--desktop/selection.c64
-rw-r--r--desktop/textarea.c557
-rw-r--r--desktop/textarea.h50
-rw-r--r--desktop/textinput.c25
-rw-r--r--desktop/tree.c8
6 files changed, 472 insertions, 253 deletions
diff --git a/desktop/browser.c b/desktop/browser.c
index 6a1688192..f9353afef 100644
--- a/desktop/browser.c
+++ b/desktop/browser.c
@@ -396,6 +396,8 @@ void browser_window_set_drag_type(struct browser_window *bw,
top_bw->drag_window = bw;
switch (type) {
+ case DRAGGING_SELECTION:
+ return;
case DRAGGING_SCR_X:
case DRAGGING_SCR_Y:
case DRAGGING_CONTENT_SCROLLBAR:
@@ -1545,6 +1547,25 @@ nserror browser_window_callback(hlcache_handle *c,
browser_window_set_pointer(bw, event->data.pointer);
break;
+ case CONTENT_MSG_DRAG:
+ {
+ browser_drag_type bdt = DRAGGING_NONE;
+
+ switch (event->data.drag.type) {
+ case CONTENT_DRAG_NONE:
+ bdt = DRAGGING_NONE;
+ break;
+ case CONTENT_DRAG_SCROLL:
+ bdt = DRAGGING_CONTENT_SCROLLBAR;
+ break;
+ case CONTENT_DRAG_SELECTION:
+ bdt = DRAGGING_SELECTION;
+ break;
+ }
+ browser_window_set_drag_type(bw, bdt, event->data.drag.rect);
+ }
+ break;
+
default:
assert(0);
}
diff --git a/desktop/selection.c b/desktop/selection.c
index ae9df5ec6..fa82ad027 100644
--- a/desktop/selection.c
+++ b/desktop/selection.c
@@ -47,24 +47,8 @@
/**
* 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_INPUT(box) ((box) && (box)->gadget && \
- ((box)->gadget->type == GADGET_TEXTAREA || \
- (box)->gadget->type == GADGET_TEXTBOX || \
- (box)->gadget->type == GADGET_PASSWORD))
-
-/** 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 NUMBER_SPACE(x) ((x) & 0xF0000000U)
-#define SAME_SPACE(s, offset) (NUMBER_SPACE((s)->max_idx) == NUMBER_SPACE(offset))
-
#define SPACE_LEN(b) ((b->space == 0) ? 0 : 1)
@@ -99,7 +83,7 @@ static bool save_handler(const char *text, size_t length, struct box *box,
static bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
unsigned *start_offset, unsigned *end_offset);
static bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
- unsigned int num_space, seln_traverse_handler handler,
+ seln_traverse_handler handler,
void *handle, save_text_whitespace *before, bool *first,
bool do_marker);
static struct box *get_box(struct box *b, unsigned offset, size_t *pidx);
@@ -188,13 +172,7 @@ void selection_reinit(struct selection *s, struct box *root)
assert(s);
- if (IS_INPUT(root)) {
- static int next_idx = 0;
- if (!++next_idx) next_idx = 1;
- root_idx = next_idx << 28;
- }
- else
- root_idx = 0;
+ root_idx = 0;
// if (s->root == root) {
// /* keep the same number space as before, because we want
@@ -255,8 +233,7 @@ void selection_init(struct selection *s, struct box *root)
bool selection_read_only(struct selection *s)
{
- return !s->root || !NUMBER_SPACE(s->root->byte_offset);
-
+ return true;
}
@@ -280,13 +257,10 @@ unsigned selection_label_subtree(struct box *box, unsigned idx)
idx += box->length + SPACE_LEN(box);
while (child) {
- if (!IS_INPUT(child)) {
- if (child->list_marker)
- idx = selection_label_subtree(
- child->list_marker, idx);
+ if (child->list_marker)
+ idx = selection_label_subtree(child->list_marker, idx);
- idx = selection_label_subtree(child, idx);
- }
+ idx = selection_label_subtree(child, idx);
child = child->next;
}
@@ -317,9 +291,6 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
top = browser_window_get_root(top);
- if (!SAME_SPACE(s, idx))
- return false; /* not our problem */
-
if (selection_defined(s)) {
if (idx > s->start_idx) {
if (idx <= s->end_idx)
@@ -421,9 +392,6 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
void selection_track(struct selection *s, browser_mouse_state mouse,
unsigned idx)
{
- if (!SAME_SPACE(s, idx))
- return;
-
if (!mouse) {
s->drag_state = DRAG_NONE;
}
@@ -516,7 +484,6 @@ bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
* \param box box subtree
* \param start_idx start of range within textual representation (bytes)
* \param end_idx end of range
- * \param num_space number space of the selection
* \param handler handler function to call
* \param handle handle to pass
* \param before type of whitespace to place before next encountered text
@@ -526,7 +493,7 @@ bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
*/
bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
- unsigned int num_space, seln_traverse_handler handler,
+ seln_traverse_handler handler,
void *handle, save_text_whitespace *before, bool *first,
bool do_marker)
{
@@ -547,7 +514,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
/* do the marker box before continuing with the rest of the
* list element */
if (!traverse_tree(box->list_marker, start_idx, end_idx,
- num_space, handler, handle, before, first,
+ handler, handle, before, first,
true))
return false;
}
@@ -568,8 +535,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
else {
whitespace_text = NULL;
}
- if (num_space == NUMBER_SPACE(box->byte_offset) &&
- box->type != BOX_BR &&
+ if (box->type != BOX_BR &&
!((box->type == BOX_FLOAT_LEFT ||
box->type == BOX_FLOAT_RIGHT) &&
!box->text)) {
@@ -607,7 +573,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
* the tree */
struct box *next = child->next;
- if (!traverse_tree(child, start_idx, end_idx, num_space,
+ if (!traverse_tree(child, start_idx, end_idx,
handler, handle, before, first, false))
return false;
@@ -642,8 +608,7 @@ static bool selection_traverse(struct selection *s,
if (s->root) {
/* HTML */
return traverse_tree(s->root, s->start_idx, s->end_idx,
- NUMBER_SPACE(s->max_idx), handler, handle,
- &before, &first, false);
+ handler, handle, &before, &first, false);
}
/* Text */
@@ -727,7 +692,7 @@ void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx)
if (s->root) {
if (!traverse_tree(s->root, start_idx, end_idx,
- NUMBER_SPACE(s->max_idx), redraw_handler, &rdw,
+ redraw_handler, &rdw,
NULL, NULL, false))
return;
}
@@ -977,10 +942,7 @@ void selection_select_all(struct selection *s)
assert(s);
s->defined = true;
- if (IS_INPUT(s->root))
- selection_set_start(s, s->root->children->children->byte_offset);
- else
- selection_set_start(s, 0);
+ selection_set_start(s, 0);
selection_set_end(s, s->max_idx);
}
diff --git a/desktop/textarea.c b/desktop/textarea.c
index a048058f5..62ee05a12 100644
--- a/desktop/textarea.c
+++ b/desktop/textarea.c
@@ -82,6 +82,7 @@ struct textarea {
plot_font_style_t fstyle; /**< Text style, inc. textarea bg col */
plot_font_style_t sel_fstyle; /**< Selected text style */
+ int line_height; /**< Line height obtained from style */
struct textarea_utf8 text; /**< Textarea text content */
#define PASSWORD_REPLACEMENT "\xe2\x80\xa2"
@@ -102,10 +103,12 @@ struct textarea {
int h_extent; /**< Width of content in px */
int v_extent; /**< Height of content in px */
+
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 */
+ unsigned int lines_alloc_size; /**< Number of LINE_CHUNK_SIZEs */
/** Callback function for messages to client */
textarea_client_callback callback;
@@ -260,67 +263,80 @@ static bool textarea_select_fragment(struct textarea * ta)
static bool textarea_scroll_visible(struct textarea *ta)
{
int x0, x1, y0, y1; /* area we want caret inside */
- int xc, yc; /* area centre */
int x, y; /* caret pos */
int xs = ta->scroll_x;
int ys = ta->scroll_y;
+ int vis;
+ int scrollbar_width;
bool scrolled = false;
if (ta->caret_pos.char_off == -1)
return false;
+ scrollbar_width = (ta->bar_y == NULL) ? 0 : SCROLLBAR_WIDTH;
x0 = ta->border_width + ta->pad_left;
x1 = ta->vis_width - (ta->border_width + ta->pad_right);
- y0 = 0;
- y1 = ta->vis_height - 2 * ta->border_width -
- ta->pad_top - ta->pad_bottom;
- xc = (x1 - x0) / 2 + x0;
- yc = (y1 - y0) / 2 + y0;
+ /* Adjust scroll pos for reduced extents */
+ vis = ta->vis_width - 2 * ta->border_width - scrollbar_width;
+ if (ta->h_extent - xs < vis)
+ xs -= vis - (ta->h_extent - xs);
+
+ /* Get caret pos on screen */
x = ta->caret_x - xs;
- y = ta->caret_y + ta->line_height / 2 - ys;
-
- /* horizontal scroll; centre caret */
- xs += x - xc;
-
- /* force back into range */
- if (xs < 0)
- xs = 0;
- else if (xs > ta->h_extent - (x1 - x0))
- xs = ta->h_extent - (x1 - x0);
-
- /* If scrolled, set new pos. */
- if (xs != ta->scroll_x && ta->bar_x != NULL) {
- scrollbar_set(ta->bar_x, xs, false);
- xs = scrollbar_get_offset(ta->bar_x);
- if (xs != ta->scroll_x) {
- ta->scroll_x = xs;
- scrolled = true;
- }
- } else if (ta->flags & TEXTAREA_MULTILINE && ta->bar_x == NULL &&
- ta->scroll_x != 0) {
+ /* scroll as required */
+ if (x < x0)
+ xs += (x - x0);
+ else if (x > x1)
+ xs += (x - x1);
+
+ if (ta->bar_x == NULL && ta->scroll_x != 0 &&
+ ta->flags & TEXTAREA_MULTILINE) {
+ /* Scrollbar removed, set to zero */
ta->scroll_x = 0;
scrolled = true;
- } else if (xs != ta->scroll_x && !(ta->flags & TEXTAREA_MULTILINE)) {
- ta->scroll_x = xs;
- scrolled = true;
+ } else if (xs != ta->scroll_x) {
+ /* Scrolled, set new pos. */
+ if (ta->bar_x != NULL) {
+ scrollbar_set(ta->bar_x, xs, false);
+ xs = scrollbar_get_offset(ta->bar_x);
+ if (xs != ta->scroll_x) {
+ ta->scroll_x = xs;
+ scrolled = true;
+ }
+
+ } else if (!(ta->flags & TEXTAREA_MULTILINE)) {
+ ta->scroll_x = xs;
+ scrolled = true;
+
+ }
}
/* check and change vertical scroll */
if (ta->flags & TEXTAREA_MULTILINE) {
- /* vertical scroll; centre caret */
- ys += y - yc;
+ scrollbar_width = (ta->bar_x == NULL) ? 0 : SCROLLBAR_WIDTH;
+ y0 = 0;
+ y1 = ta->vis_height - 2 * ta->border_width -
+ ta->pad_top - ta->pad_bottom;
+
+ /* Adjust scroll pos for reduced extents */
+ vis = ta->vis_height - 2 * ta->border_width - scrollbar_width;
+ if (ta->v_extent - ys < vis)
+ ys -= vis - (ta->v_extent - ys);
- /* force back into range */
- if (ys < 0)
- ys = 0;
- else if (ys > ta->v_extent - (y1 - y0))
- ys = ta->v_extent - (y1 - y0);
+ /* Get caret pos on screen */
+ y = ta->caret_y - ys;
+
+ /* scroll as required */
+ if (y < y0)
+ ys += (y - y0);
+ else if (y + ta->line_height > y1)
+ ys += (y + ta->line_height - y1);
- /* If scrolled, set new pos. */
if (ys != ta->scroll_y && ta->bar_y != NULL) {
+ /* Scrolled, set new pos. */
scrollbar_set(ta->bar_y, ys, false);
ys = scrollbar_get_offset(ta->bar_y);
if (ys != ta->scroll_y) {
@@ -329,6 +345,7 @@ static bool textarea_scroll_visible(struct textarea *ta)
}
} else if (ta->bar_y == NULL && ta->scroll_y != 0) {
+ /* Scrollbar removed, set to zero */
ta->scroll_y = 0;
scrolled = true;
}
@@ -361,6 +378,18 @@ static void textarea_scrollbar_callback(void *client_data,
msg.data.redraw.y1 = ta->vis_height;
ta->callback(ta->data, &msg);
+
+ if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Tell client where caret should be placed */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_MOVED_CARET;
+ msg.data.caret.hidden = false;
+ msg.data.caret.x = ta->caret_x - ta->scroll_x;
+ msg.data.caret.y = ta->caret_y - ta->scroll_y;
+ msg.data.caret.height = ta->line_height;
+
+ ta->callback(ta->data, &msg);
+ }
break;
case SCROLLBAR_MSG_SCROLL_START:
@@ -421,6 +450,7 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
LOG(("malloc failed"));
return false;
}
+ ta->lines_alloc_size = LINE_CHUNK_SIZE;
}
if (!(ta->flags & TEXTAREA_MULTILINE)) {
@@ -471,7 +501,7 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
if (x > w)
w = x;
- ta->h_extent = w + ta->pad_left - ta->pad_right;
+ ta->h_extent = w + ta->pad_left + ta->pad_right;
ta->line_count = 1;
return true;
@@ -489,7 +519,10 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
do {
/* Set line count to start point */
- line = start;
+ if (restart)
+ line = 0;
+ else
+ line = start;
/* Find available width */
avail_width = ta->vis_width - 2 * ta->border_width -
@@ -498,6 +531,14 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
avail_width = 0;
h_extent = avail_width;
+ if (ta->text.len == 1) {
+ /* Handle empty textarea */
+ assert(ta->text.data[0] == '\0');
+ ta->lines[line].b_start = 0;
+ ta->lines[line++].b_length = 0;
+ ta->line_count = 1;
+ }
+
restart = false;
for (len = ta->text.len - 1, text = ta->text.data; len > 0;
len -= b_off, text += b_off) {
@@ -536,9 +577,11 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
ta->line_height;
}
- if (line > 0 && line % LINE_CHUNK_SIZE == 0) {
+ /* Ensure enough storage for lines data */
+ if (line > ta->lines_alloc_size - 2) {
+ /* Up to two lines my be added in a pass */
struct line_info *temp = realloc(ta->lines,
- (line + LINE_CHUNK_SIZE) *
+ (line + 2 + LINE_CHUNK_SIZE) *
sizeof(struct line_info));
if (temp == NULL) {
LOG(("realloc failed"));
@@ -546,6 +589,8 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
}
ta->lines = temp;
+ ta->lines_alloc_size = line + 2 +
+ LINE_CHUNK_SIZE;
}
if (para_end == text + b_off && *para_end == '\n') {
@@ -658,7 +703,6 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
* \param ta Text area
* \param x X coordinate
* \param y Y coordinate
- * \param b_off Updated to byte offset
* \param c_off Updated to character offset
*/
static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
@@ -699,9 +743,11 @@ static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
* following line, which is undesirable.
*/
if (ta->flags & TEXTAREA_MULTILINE &&
+ ta->show->data[ta->lines[line].b_start +
+ ta->lines[line].b_length] > 0 &&
bpos == (unsigned)ta->lines[line].b_length &&
ta->show->data[ta->lines[line].b_start +
- ta->lines[line].b_length - 1] == ' ')
+ ta->lines[line].b_length - 1] == ' ')
bpos--;
/* Get character position */
@@ -948,14 +994,15 @@ static bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse,
/* exported interface, documented in textarea.h */
-struct textarea *textarea_create(const textarea_setup *setup,
+struct textarea *textarea_create(const textarea_flags flags,
+ const textarea_setup *setup,
textarea_client_callback callback, void *data)
{
struct textarea *ret;
/* Sanity check flags */
- assert(!(setup->flags & TEXTAREA_MULTILINE &&
- setup->flags & TEXTAREA_PASSWORD));
+ assert(!(flags & TEXTAREA_MULTILINE &&
+ flags & TEXTAREA_PASSWORD));
if (callback == NULL) {
LOG(("no callback provided"));
@@ -971,7 +1018,7 @@ struct textarea *textarea_create(const textarea_setup *setup,
ret->callback = callback;
ret->data = data;
- ret->flags = setup->flags;
+ ret->flags = flags;
ret->vis_width = setup->width;
ret->vis_height = setup->height;
@@ -1008,7 +1055,7 @@ struct textarea *textarea_create(const textarea_setup *setup,
ret->text.len = 1;
ret->text.utf8_len = 0;
- if (setup->flags & TEXTAREA_PASSWORD) {
+ if (flags & TEXTAREA_PASSWORD) {
ret->password.data = malloc(64);
if (ret->password.data == NULL) {
LOG(("malloc failed"));
@@ -1032,10 +1079,9 @@ struct textarea *textarea_create(const textarea_setup *setup,
ret->show = &ret->text;
}
- ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2),
- FMUL(nscss_screen_dpi,
- INTTOFIX((setup->text.size /
- FONT_SIZE_SCALE))))), F_72));
+ ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.3),
+ FMUL(nscss_screen_dpi, INTTOFIX((setup->text.size))))),
+ FONT_SIZE_SCALE * F_72));
ret->caret_pos.line = ret->caret_pos.char_off = -1;
ret->caret_x = 0;
@@ -1045,6 +1091,9 @@ struct textarea *textarea_create(const textarea_setup *setup,
ret->line_count = 0;
ret->lines = NULL;
+ ret->lines_alloc_size = 0;
+
+ textarea_reflow(ret, 0);
return ret;
}
@@ -1093,6 +1142,52 @@ bool textarea_set_text(struct textarea *ta, const char *text)
/* exported interface, documented in textarea.h */
+bool textarea_drop_text(struct textarea *ta, const char *text,
+ size_t text_length)
+{
+ struct textarea_msg msg;
+ unsigned int caret_pos;
+ size_t text_chars;
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return false;
+
+ if (text == NULL)
+ return false;
+
+ text_chars = utf8_bounded_length(text, text_length);
+ caret_pos = textarea_get_caret(ta);
+
+ if (ta->sel_start != -1) {
+ if (!textarea_replace_text(ta, ta->sel_start, ta->sel_end,
+ text, text_length, false))
+ return false;
+
+ caret_pos = ta->sel_start + text_chars;
+ ta->sel_start = ta->sel_end = -1;
+ } else {
+ if (!textarea_replace_text(ta, caret_pos, caret_pos,
+ text, text_length, false))
+ return false;
+ caret_pos += text_chars;
+ }
+
+ textarea_set_caret(ta, caret_pos);
+
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = 0;
+ msg.data.redraw.y0 = 0;
+ msg.data.redraw.x1 = ta->vis_width;
+ msg.data.redraw.y1 = ta->vis_height;
+
+ ta->callback(ta->data, &msg);
+
+ return true;
+}
+
+
+/* exported interface, documented in textarea.h */
int textarea_get_text(struct textarea *ta, char *buf, unsigned int len)
{
if (buf == NULL && len == 0) {
@@ -1202,17 +1297,19 @@ bool textarea_set_caret(struct textarea *ta, int caret)
x += ta->border_width + ta->pad_left;
ta->caret_x = x;
- y = ta->line_height * ta->caret_pos.line;
+ y = ta->line_height * ta->caret_pos.line + text_y_offset;
ta->caret_y = y;
- if (!textarea_scroll_visible(ta)) {
- /* No scroll, just caret moved, redraw it */
+ if (!textarea_scroll_visible(ta) &&
+ ta->flags & TEXTAREA_INTERNAL_CARET) {
+ /* Didn't scroll, just moved caret.
+ * Caret is internal caret, redraw it */
x -= ta->scroll_x;
y -= ta->scroll_y;
x0 = max(x - 1, ta->border_width);
- y0 = max(y + text_y_offset, 0);
+ y0 = max(y, 0);
x1 = min(x + 1, ta->vis_width - ta->border_width);
- y1 = min(y + ta->line_height + text_y_offset,
+ y1 = min(y + ta->line_height,
ta->vis_height);
width = x1 - x0;
@@ -1229,6 +1326,26 @@ bool textarea_set_caret(struct textarea *ta, int caret)
ta->callback(ta->data, &msg);
}
}
+
+ if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Tell client where caret should be placed */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_MOVED_CARET;
+ msg.data.caret.hidden = false;
+ msg.data.caret.x = x - ta->scroll_x;
+ msg.data.caret.y = y - ta->scroll_y;
+ msg.data.caret.height = ta->line_height;
+
+ ta->callback(ta->data, &msg);
+ }
+
+ } else if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Caret hidden, and client is responsible: tell client */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_MOVED_CARET;
+ msg.data.caret.hidden = true;
+
+ ta->callback(ta->data, &msg);
}
return true;
@@ -1248,6 +1365,9 @@ int textarea_get_caret(struct textarea *ta)
if (ta->text.utf8_len == 0)
return 0;
+ if (ta->caret_pos.line >= ta->line_count)
+ return ta->text.utf8_len;
+
/* 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.data, ta->text.len, b_off))
@@ -1258,19 +1378,20 @@ int textarea_get_caret(struct textarea *ta)
/* exported interface, documented in textarea.h */
-void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
+void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale,
const struct rect *clip, const struct redraw_context *ctx)
{
- struct textarea_msg msg;
const struct plotter_table *plot = ctx->plot;
- int line0, line1, line, left, right;
+ int line0, line1, line, left, right, line_y;
int chars, text_y_offset, text_y_offset_baseline;
unsigned int c_pos, c_len, c_len_part, b_start, b_end, line_len;
unsigned int sel_start, sel_end;
char *line_text;
struct rect r, s;
bool selected = false;
- plot_font_style_t *fstyle;
+ plot_font_style_t fstyle;
+ int fsize = ta->fstyle.size;
+ int line_height = ta->line_height;
plot_style_t plot_style_fill_bg = {
.stroke_type = PLOT_OP_TYPE_NONE,
.stroke_width = 0,
@@ -1308,10 +1429,17 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
r.x0 = x;
if (r.y0 < y)
r.y0 = y;
- if (r.x1 > x + ta->vis_width)
- r.x1 = x + ta->vis_width;
- if (r.y1 > y + ta->vis_height)
- r.y1 = y + ta->vis_height;
+ if (scale == 1.0) {
+ if (r.x1 > x + ta->vis_width)
+ r.x1 = x + ta->vis_width;
+ if (r.y1 > y + ta->vis_height)
+ r.y1 = y + ta->vis_height;
+ } else {
+ if (r.x1 > x + ta->vis_width * scale)
+ r.x1 = x + ta->vis_width * scale;
+ if (r.y1 > y + ta->vis_height * scale)
+ r.y1 = y + ta->vis_height * scale;
+ }
plot->clip(&r);
if (ta->border_col != NS_TRANSPARENT &&
@@ -1329,16 +1457,32 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
&plot_style_fill_bg);
}
- if (r.x0 < x + ta->border_width)
- r.x0 = x + ta->border_width;
- if (r.x1 > x + ta->vis_width - ta->border_width)
- r.x1 = x + ta->vis_width - ta->border_width;
- if (r.y0 < y + ta->border_width)
- r.y0 = y + ta->border_width;
- if (r.y1 > y + ta->vis_height - ta->border_width -
- (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0))
- r.y1 = y + ta->vis_height - ta->border_width -
- (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0);
+ if (scale == 1.0) {
+ if (r.x0 < x + ta->border_width)
+ r.x0 = x + ta->border_width;
+ if (r.x1 > x + ta->vis_width - ta->border_width)
+ r.x1 = x + ta->vis_width - ta->border_width;
+ if (r.y0 < y + ta->border_width)
+ r.y0 = y + ta->border_width;
+ if (r.y1 > y + ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0))
+ r.y1 = y + ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH :
+ 0);
+ } else {
+ if (r.x0 < x + ta->border_width * scale)
+ r.x0 = x + ta->border_width * scale;
+ if (r.x1 > x + (ta->vis_width - ta->border_width) * scale)
+ r.x1 = x + (ta->vis_width - ta->border_width) * scale;
+ if (r.y0 < y + ta->border_width * scale)
+ r.y0 = y + ta->border_width * scale;
+ if (r.y1 > y + (ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0)) *
+ scale)
+ r.y1 = y + (ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH :
+ 0) * scale);
+ }
if (line0 > 0)
c_pos = utf8_bounded_length(ta->show->data,
@@ -1359,13 +1503,23 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
text_y_offset_baseline += (vis_height * 3 + 2) / 4;
}
+ if (scale != 1.0) {
+ text_y_offset *= scale;
+ text_y_offset_baseline *= scale;
+
+ fsize *= scale;
+ line_height *= scale;
+ }
+
plot_style_fill_bg.fill_colour = ta->sel_fstyle.background;
for (line = line0; (line <= line1) &&
(y + line * ta->line_height <= r.y1 + ta->scroll_y);
line++) {
- if (ta->lines[line].b_length == 0)
+ if (ta->lines[line].b_length == 0) {
+ c_pos++;
continue;
+ }
/* reset clip rectangle */
plot->clip(&r);
@@ -1387,30 +1541,31 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
/* rest of line unselected */
selected = false;
c_len_part = c_len;
- fstyle = &ta->fstyle;
+ fstyle = ta->fstyle;
} else if (sel_start <= c_pos &&
sel_end > c_pos + c_len) {
/* rest of line selected */
selected = true;
c_len_part = c_len;
- fstyle = &ta->sel_fstyle;
+ fstyle = ta->sel_fstyle;
} else if (sel_start > c_pos) {
/* next part of line unselected */
selected = false;
c_len_part = sel_start - c_pos;
- fstyle = &ta->fstyle;
+ fstyle = ta->fstyle;
} else if (sel_end > c_pos) {
/* next part of line selected */
selected = true;
c_len_part = sel_end - c_pos;
- fstyle = &ta->sel_fstyle;
+ fstyle = ta->sel_fstyle;
} else {
assert(0);
}
+ fstyle.size = fsize;
line_text = &(ta->show->data[ta->lines[line].b_start]);
line_len = ta->lines[line].b_length;
@@ -1424,7 +1579,7 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
/* find clip left/right for this part of line */
left = right;
- nsfont.font_width(&ta->fstyle, line_text,
+ nsfont.font_width(&fstyle, line_text,
b_end, &right);
right += x + ta->border_width + ta->pad_left -
ta->scroll_x;
@@ -1435,27 +1590,31 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
s.x0 = left;
if (s.x1 > right)
s.x1 = right;
+
plot->clip(&s);
+ line_y = line * ta->line_height - ta->scroll_y;
+
+ if (scale != 1.0) {
+ line_y *= scale;
+ }
+
if (selected) {
/* draw selection fill */
- plot->rectangle(s.x0,
- y + line * ta->line_height + 1 -
- ta->scroll_y + text_y_offset,
- s.x1,
- y + (line + 1) * ta->line_height + 1 -
- ta->scroll_y + text_y_offset,
+ plot->rectangle(s.x0, y + line_y +
+ text_y_offset,
+ s.x1, y + line_y + line_height +
+ text_y_offset,
&plot_style_fill_bg);
}
/* draw text */
plot->text(x + ta->border_width + ta->pad_left -
ta->scroll_x,
- y + line * ta->line_height +
- text_y_offset_baseline - ta->scroll_y,
+ y + line_y + text_y_offset_baseline,
ta->show->data +
ta->lines[line].b_start,
- ta->lines[line].b_length, fstyle);
+ ta->lines[line].b_length, &fstyle);
c_pos += c_len_part;
c_len -= c_len_part;
@@ -1475,7 +1634,7 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
if ((ta->sel_end == -1 || ta->sel_start == ta->sel_end) &&
ta->caret_pos.char_off >= 0) {
/* There is no selection, and caret visible: show caret */
- int caret_y = y - ta->scroll_y + ta->caret_y + text_y_offset;
+ int caret_y = y - ta->scroll_y + ta->caret_y;
if (ta->flags & TEXTAREA_INTERNAL_CARET) {
/* Render our own caret */
@@ -1483,39 +1642,22 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
x - ta->scroll_x + ta->caret_x,
caret_y + ta->line_height,
&pstyle_stroke_caret);
- } else {
- /* Tell client where caret should be placed */
- msg.ta = ta;
- msg.type = TEXTAREA_MSG_MOVED_CARET;
- msg.data.caret.hidden = false;
- msg.data.caret.x = x - ta->scroll_x + ta->caret_x;
- msg.data.caret.y = caret_y;
- msg.data.caret.height = ta->line_height;
-
- ta->callback(ta->data, &msg);
}
- } else if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
- /* Caret hidden, and client is responsible: tell client */
- msg.ta = ta;
- msg.type = TEXTAREA_MSG_MOVED_CARET;
- msg.data.caret.hidden = true;
-
- ta->callback(ta->data, &msg);
}
if (ta->bar_x != NULL)
scrollbar_redraw(ta->bar_x,
- x + ta->border_width,
- y + ta->vis_height - ta->border_width -
+ x / scale + ta->border_width,
+ y / scale + ta->vis_height - ta->border_width -
SCROLLBAR_WIDTH,
- clip, 1.0, ctx);
+ clip, scale, ctx);
if (ta->bar_y != NULL)
scrollbar_redraw(ta->bar_y,
- x + ta->vis_width - ta->border_width -
+ x / scale + ta->vis_width - ta->border_width -
SCROLLBAR_WIDTH,
- y + ta->border_width,
- clip, 1.0, ctx);
+ y / scale + ta->border_width,
+ clip, scale, ctx);
}
@@ -1594,14 +1736,28 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
redraw = true;
}
break;
+ case KEY_CR:
case KEY_NL:
if (readonly)
break;
- if(!textarea_insert_text(ta, caret, "\n", 1))
- return false;
- caret++;
- ta->sel_start = ta->sel_end = -1;
- redraw = true;
+
+ if (ta->sel_start != -1) {
+ if (!textarea_replace_text(ta,
+ ta->sel_start, ta->sel_end,
+ "\n", 1, false))
+ return false;
+
+ caret = ta->sel_start + 1;
+ ta->sel_start = ta->sel_end = -1;
+ redraw = true;
+ } else {
+ if (!textarea_replace_text(ta,
+ caret, caret,
+ "\n", 1, false))
+ return false;
+ caret++;
+ redraw = true;
+ }
break;
case KEY_CUT_LINE:
break;
@@ -1702,38 +1858,37 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start = ta->sel_end = -1;
redraw = true;
}
- if (ta->flags & TEXTAREA_MULTILINE) {
- line--;
- if (line < 0)
- line = 0;
- if (line == ta->caret_pos.line)
- break;
+ if (!(ta->flags & TEXTAREA_MULTILINE))
+ break;
- b_off = ta->lines[line].b_start;
- b_len = ta->lines[line].b_length;
+ line--;
+ if (line < 0)
+ line = 0;
+ if (line == ta->caret_pos.line)
+ break;
- c_line = ta->caret_pos.line;
- c_chars = ta->caret_pos.char_off;
+ b_off = ta->lines[line].b_start;
+ b_len = ta->lines[line].b_length;
- if (ta->text.data[b_off + b_len - 1] == ' '
- && line < ta->line_count - 1)
- b_len--;
+ c_line = ta->caret_pos.line;
+ c_chars = ta->caret_pos.char_off;
- l_len = utf8_bounded_length(
- &(ta->text.data[b_off]),
- b_len);
+ if (b_len > 0 && ta->text.data[b_off + b_len - 1] == ' '
+ && line < ta->line_count - 1)
+ b_len--;
+ l_len = utf8_bounded_length(&(ta->text.data[b_off]),
+ b_len);
- ta->caret_pos.line = line;
- ta->caret_pos.char_off = min(l_len,
- (unsigned)
- ta->caret_pos.char_off);
+ ta->caret_pos.line = line;
+ ta->caret_pos.char_off = min(l_len,
+ (unsigned)ta->caret_pos.char_off);
- caret = textarea_get_caret(ta);
+ caret = textarea_get_caret(ta);
+
+ ta->caret_pos.line = c_line;
+ ta->caret_pos.char_off = c_chars;
- ta->caret_pos.line = c_line;
- ta->caret_pos.char_off = c_chars;
- }
break;
case KEY_PAGE_DOWN:
if (readonly)
@@ -1753,38 +1908,37 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start = ta->sel_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;
+ if (!(ta->flags & TEXTAREA_MULTILINE))
+ break;
- b_off = ta->lines[line].b_start;
- b_len = ta->lines[line].b_length;
+ line++;
+ if (line > ta->line_count - 1)
+ line = ta->line_count - 1;
+ if (line == ta->caret_pos.line)
+ break;
- c_line = ta->caret_pos.line;
- c_chars = ta->caret_pos.char_off;
+ b_off = ta->lines[line].b_start;
+ b_len = ta->lines[line].b_length;
- if (ta->text.data[b_off + b_len - 1] == ' '
- && line < ta->line_count - 1)
- b_len--;
+ c_line = ta->caret_pos.line;
+ c_chars = ta->caret_pos.char_off;
- l_len = utf8_bounded_length(
- &(ta->text.data[b_off]),
- b_len);
+ if (ta->text.data[b_off + b_len - 1] == ' ' &&
+ line < ta->line_count - 1)
+ b_len--;
+ l_len = utf8_bounded_length(&(ta->text.data[b_off]),
+ b_len);
- ta->caret_pos.line = line;
- ta->caret_pos.char_off = min(l_len,
- (unsigned)
- ta->caret_pos.char_off);
+ ta->caret_pos.line = line;
+ ta->caret_pos.char_off = min(l_len,
+ (unsigned)ta->caret_pos.char_off);
- caret = textarea_get_caret(ta);
+ caret = textarea_get_caret(ta);
+
+ ta->caret_pos.line = c_line;
+ ta->caret_pos.char_off = c_chars;
- ta->caret_pos.line = c_line;
- ta->caret_pos.char_off = c_chars;
- }
break;
case KEY_DELETE_RIGHT:
if (readonly)
@@ -1899,8 +2053,9 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
}
- if (caret != caret_init)
+ if (caret != caret_init || redraw)
textarea_set_caret(ta, caret);
+
//TODO:redraw only the important part
if (redraw) {
msg.ta = ta;
@@ -2006,7 +2161,8 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
return textarea_select_fragment(ta);
}
- } else if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_HOLDING_1)) {
+ } else if (mouse & BROWSER_MOUSE_DRAG_1) {
+ /* Selection start */
textarea_get_xy_offset(ta, x, y, &c_off);
c_start = ta->drag_start_char;
c_end = c_off;
@@ -2019,6 +2175,30 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
ta->callback(ta->data, &msg);
return textarea_select(ta, c_start, c_end);
+ } else if (mouse & BROWSER_MOUSE_HOLDING_1 &&
+ ta->drag_info.type == TEXTAREA_DRAG_SELECTION) {
+ /* Selection track */
+ int scrx = 0;
+ int scry = 0;
+
+ textarea_get_xy_offset(ta, x, y, &c_off);
+ c_start = ta->drag_start_char;
+ c_end = c_off;
+
+ /* selection auto-scroll */
+ if (x < 0)
+ scrx = x / 4;
+ else if (x > ta->vis_width)
+ scrx = (x - ta->vis_width) / 4;
+
+ if (y < 0)
+ scry = y / 4;
+ else if (y > ta->vis_height)
+ scry = (y - ta->vis_height) / 4;
+
+ textarea_scroll(ta, scrx, scry);
+
+ return textarea_select(ta, c_start, c_end);
}
return true;
@@ -2038,18 +2218,37 @@ void textarea_get_dimensions(struct textarea *ta, int *width, int *height)
/* exported interface, documented in textarea.h */
void textarea_set_dimensions(struct textarea *ta, int width, int height)
{
- struct textarea_msg msg;
+ ta->vis_width = width;
+ ta->vis_height = height;
+ textarea_reflow(ta, 0);
+}
+
+/* exported interface, documented in textarea.h */
+void textarea_set_layout(struct textarea *ta, int width, int height,
+ int top, int right, int bottom, int left)
+{
ta->vis_width = width;
ta->vis_height = height;
+ ta->pad_top = top;
+ ta->pad_right = right + ((ta->bar_y == NULL) ? 0 : SCROLLBAR_WIDTH);
+ ta->pad_bottom = bottom + ((ta->bar_x == NULL) ? 0 : SCROLLBAR_WIDTH);
+ ta->pad_left = left;
textarea_reflow(ta, 0);
+}
- msg.ta = ta;
- msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
- msg.data.redraw.x0 = 0;
- msg.data.redraw.y0 = 0;
- msg.data.redraw.x1 = ta->vis_width;
- msg.data.redraw.y1 = ta->vis_height;
- ta->callback(ta->data, &msg);
+/* exported interface, documented in textarea.h */
+bool textarea_scroll(struct textarea *ta, int scrx, int scry)
+{
+ bool handled_scroll = false;
+
+ if (ta->bar_x != NULL && scrx != 0 &&
+ scrollbar_scroll(ta->bar_x, scrx))
+ handled_scroll = true;
+ if (ta->bar_y != NULL && scry != 0 &&
+ scrollbar_scroll(ta->bar_y, scry))
+ handled_scroll = true;
+
+ return handled_scroll;
}
diff --git a/desktop/textarea.h b/desktop/textarea.h
index d8e720bae..d01cd12c6 100644
--- a/desktop/textarea.h
+++ b/desktop/textarea.h
@@ -70,8 +70,6 @@ struct textarea_msg {
};
typedef struct textarea_setup {
- textarea_flags flags; /**< Setup flags */
-
int width; /**< Textarea width */
int height; /**< Textarea height */
@@ -105,7 +103,8 @@ typedef void(*textarea_client_callback)(void *data, struct textarea_msg *msg);
* \param data user specified data which will be passed to callbacks
* \return Opaque handle for textarea or 0 on error
*/
-struct textarea *textarea_create(const textarea_setup *setup,
+struct textarea *textarea_create(const textarea_flags flags,
+ const textarea_setup *setup,
textarea_client_callback callback, void *data);
/**
@@ -125,11 +124,21 @@ void textarea_destroy(struct textarea *ta);
bool textarea_set_text(struct textarea *ta, const char *text);
/**
+ * Insert the text in a text area at the caret, replacing any selection.
+ *
+ * \param ta Text area
+ * \param text UTF-8 text to set text area's contents to
+ * \return true on success, false on memory exhaustion or if ta lacks caret
+ */
+bool textarea_drop_text(struct textarea *ta, const char *text,
+ size_t text_length);
+
+/**
* 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
+ * to read length required (includes trailing '\0')
* \param len Length (bytes) of buffer pointed to by buf, or 0 to read length
* \return Length (bytes) written/required or -1 on error
*/
@@ -160,10 +169,11 @@ int textarea_get_caret(struct textarea *ta);
* \param x x coordinate of textarea top
* \param y y coordinate of textarea left
* \param bg background colour under textarea
+ * \param scale scale to render at
* \param clip clip rectangle
* \param ctx current redraw context
*/
-void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
+void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale,
const struct rect *clip, const struct redraw_context *ctx);
/**
@@ -190,6 +200,7 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
/**
* Gets the dimensions of a textarea
*
+ * \param ta textarea widget
* \param width if not NULL, gets updated to the width of the textarea
* \param height if not NULL, gets updated to the height of the textarea
*/
@@ -197,11 +208,38 @@ void textarea_get_dimensions(struct textarea *ta, int *width, int *height);
/**
* Set the dimensions of a textarea, causing a reflow and
- * emitting a redraw request.
+ * Does not emit a redraw request. Up to client to call textarea_redraw.
*
+ * \param ta textarea widget
* \param width the new width of the textarea
* \param height the new height of the textarea
*/
void textarea_set_dimensions(struct textarea *ta, int width, int height);
+
+/**
+ * Set the dimensions and padding of a textarea, causing a reflow.
+ * Does not emit a redraw request. Up to client to call textarea_redraw.
+ *
+ * \param ta textarea widget
+ * \param width the new width of the textarea
+ * \param height the new height of the textarea
+ * \param top the new top padding of the textarea
+ * \param right the new right padding of the textarea
+ * \param bottom the new bottom padding of the textarea
+ * \param left the new left padding of the textarea
+ */
+void textarea_set_layout(struct textarea *ta, int width, int height,
+ int top, int right, int bottom, int left);
+
+/**
+ * Scroll a textarea by an amount. Only does anything if multi-line textarea
+ * has scrollbars. If it scrolls, it will emit a redraw request.
+ *
+ * \param ta textarea widget
+ * \param scrx number of px try to scroll in x direction
+ * \param scry number of px try to scroll in y direction
+ * \return true iff the textarea was scrolled
+ */
+bool textarea_scroll(struct textarea *ta, int scrx, int scry);
#endif
diff --git a/desktop/textinput.c b/desktop/textinput.c
index b4fda5eef..660708932 100644
--- a/desktop/textinput.c
+++ b/desktop/textinput.c
@@ -127,7 +127,13 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
assert(bw->window != NULL);
- /* safe keys that can be handled whether input claimed or not */
+ if (focus->caret_callback) {
+ /* Pass keypress onto anything that has claimed input focus */
+ return focus->caret_callback(focus, key,
+ focus->caret_p1, focus->caret_p2);
+ }
+
+ /* TODO: pass these to content to deal with */
switch (key) {
case KEY_COPY_SELECTION:
selection_copy_to_clipboard(bw->cur_sel);
@@ -137,6 +143,10 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
selection_clear(bw->cur_sel, true);
return true;
+ case KEY_SELECT_ALL:
+ selection_select_all(bw->cur_sel);
+ return true;
+
case KEY_ESCAPE:
if (bw->cur_sel && selection_defined(bw->cur_sel)) {
selection_clear(bw->cur_sel, true);
@@ -147,19 +157,6 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
return false;
}
- if (focus->caret_callback) {
- /* Pass keypress onto anything that has claimed input focus */
- return focus->caret_callback(focus, key,
- focus->caret_p1, focus->caret_p2);
- }
-
- /* keys we can't handle here if cursor is in form */
- switch (key) {
- case KEY_SELECT_ALL:
- selection_select_all(bw->cur_sel);
- return true;
- }
-
return false;
}
diff --git a/desktop/tree.c b/desktop/tree.c
index 959b9870a..af64be83b 100644
--- a/desktop/tree.c
+++ b/desktop/tree.c
@@ -2079,7 +2079,7 @@ void tree_draw(struct tree *tree, int x, int y,
textarea_redraw(tree->textarea, x, y,
plot_style_fill_tree_background.
fill_colour,
- &clip, &new_ctx);
+ 1.0, &clip, &new_ctx);
}
}
@@ -2946,6 +2946,7 @@ void tree_start_edit(struct tree *tree, struct node_element *element)
struct node *parent;
int width, height;
textarea_setup ta_setup;
+ textarea_flags ta_flags;
assert(tree != NULL);
assert(element != NULL);
@@ -2972,7 +2973,8 @@ void tree_start_edit(struct tree *tree, struct node_element *element)
tree->ta_height = height;
- ta_setup.flags = TEXTAREA_INTERNAL_CARET;
+ ta_flags = TEXTAREA_INTERNAL_CARET;
+
ta_setup.width = width;
ta_setup.height = tree->ta_height;
ta_setup.pad_top = 0;
@@ -2987,7 +2989,7 @@ void tree_start_edit(struct tree *tree, struct node_element *element)
ta_setup.text.foreground = 0x000000;
ta_setup.text.background = 0xffffff;
- tree->textarea = textarea_create(&ta_setup,
+ tree->textarea = textarea_create(ta_flags, &ta_setup,
tree_textarea_callback, tree);
if (tree->textarea == NULL) {
tree_stop_edit(tree, false);