[project @ 2005-07-20 23:27:27 by adrianl]
2D scrolling of text areas/frames; First cut at selection in textareas; Further text editing actions (Word left/right; Page up/down; Cut block; Delete line start/end) svn path=/import/netsurf/; revision=1812
This commit is contained in:
parent
5e14874115
commit
1a1901d19b
|
@ -73,6 +73,9 @@ static struct box *browser_window_pick_text_box(struct browser_window *bw,
|
|||
browser_mouse_state mouse, int x, int y, int *dx, int *dy);
|
||||
static void browser_window_page_drag_start(struct browser_window *bw, int x, int y);
|
||||
|
||||
static void browser_window_scroll_box(struct browser_window *bw, struct box *box,
|
||||
int scroll_x, int scroll_y);
|
||||
|
||||
|
||||
/**
|
||||
* Create and open a new browser window with the given page.
|
||||
|
@ -852,14 +855,31 @@ 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_CLICK_1)
|
||||
browser_window_textarea_click(bw,
|
||||
mouse,
|
||||
gadget_box,
|
||||
gadget_box_x,
|
||||
gadget_box_y,
|
||||
x - gadget_box_x,
|
||||
y - gadget_box_y);
|
||||
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) {
|
||||
browser_window_textarea_click(bw,
|
||||
mouse,
|
||||
gadget_box,
|
||||
gadget_box_x,
|
||||
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))
|
||||
bw->drag_type = DRAGGING_SELECTION;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GADGET_TEXTBOX:
|
||||
case GADGET_PASSWORD:
|
||||
|
@ -874,9 +894,13 @@ void browser_window_mouse_action_html(struct browser_window *bw,
|
|||
x - gadget_box_x,
|
||||
y - gadget_box_y);
|
||||
}
|
||||
else {
|
||||
selection_init(bw->sel, gadget_box);
|
||||
selection_click(bw->sel, gadget_box, mouse, x - box_x, y - 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))
|
||||
bw->drag_type = DRAGGING_SELECTION;
|
||||
}
|
||||
break;
|
||||
case GADGET_HIDDEN:
|
||||
|
@ -1051,43 +1075,47 @@ void browser_window_mouse_track_html(struct browser_window *bw,
|
|||
browser_mouse_state mouse, int x, int y)
|
||||
{
|
||||
switch (bw->drag_type) {
|
||||
case DRAGGING_VSCROLL: {
|
||||
int scroll_y;
|
||||
struct box *box = bw->scrolling_box;
|
||||
assert(box);
|
||||
scroll_y = bw->scrolling_start_scroll_y +
|
||||
(float) (y - bw->scrolling_start_y) /
|
||||
(float) bw->scrolling_well_height *
|
||||
(float) (box->descendant_y1 -
|
||||
box->descendant_y0);
|
||||
if (scroll_y < box->descendant_y0)
|
||||
scroll_y = box->descendant_y0;
|
||||
else if (box->descendant_y1 - box->height < scroll_y)
|
||||
scroll_y = box->descendant_y1 - box->height;
|
||||
if (scroll_y == box->scroll_y)
|
||||
return;
|
||||
box->scroll_y = scroll_y;
|
||||
browser_redraw_box(bw->current_content, bw->scrolling_box);
|
||||
}
|
||||
break;
|
||||
|
||||
case DRAGGING_HSCROLL: {
|
||||
int scroll_x;
|
||||
case DRAGGING_HSCROLL:
|
||||
case DRAGGING_VSCROLL:
|
||||
case DRAGGING_2DSCROLL: {
|
||||
struct box *box = bw->scrolling_box;
|
||||
int scroll_y;
|
||||
int scroll_x;
|
||||
|
||||
assert(box);
|
||||
scroll_x = bw->scrolling_start_scroll_x +
|
||||
(float) (x - bw->scrolling_start_x) /
|
||||
(float) bw->scrolling_well_width *
|
||||
(float) (box->descendant_x1 -
|
||||
box->descendant_x0);
|
||||
if (scroll_x < box->descendant_x0)
|
||||
scroll_x = box->descendant_x0;
|
||||
else if (box->descendant_x1 - box->width < scroll_x)
|
||||
scroll_x = box->descendant_x1 - box->width;
|
||||
if (scroll_x == box->scroll_x)
|
||||
return;
|
||||
box->scroll_x = scroll_x;
|
||||
browser_redraw_box(bw->current_content, bw->scrolling_box);
|
||||
|
||||
if (bw->drag_type == DRAGGING_HSCROLL) {
|
||||
scroll_y = box->scroll_y;
|
||||
} else {
|
||||
scroll_y = bw->scrolling_start_scroll_y +
|
||||
(float) (y - bw->scrolling_start_y) /
|
||||
(float) bw->scrolling_well_height *
|
||||
(float) (box->descendant_y1 -
|
||||
box->descendant_y0);
|
||||
if (scroll_y < box->descendant_y0)
|
||||
scroll_y = box->descendant_y0;
|
||||
else if (box->descendant_y1 - box->height < scroll_y)
|
||||
scroll_y = box->descendant_y1 - box->height;
|
||||
if (scroll_y == box->scroll_y)
|
||||
return;
|
||||
}
|
||||
|
||||
if (bw->drag_type == DRAGGING_VSCROLL) {
|
||||
scroll_x = box->scroll_x;
|
||||
} else {
|
||||
scroll_x = bw->scrolling_start_scroll_x +
|
||||
(float) (x - bw->scrolling_start_x) /
|
||||
(float) bw->scrolling_well_width *
|
||||
(float) (box->descendant_x1 -
|
||||
box->descendant_x0);
|
||||
if (scroll_x < box->descendant_x0)
|
||||
scroll_x = box->descendant_x0;
|
||||
else if (box->descendant_x1 - box->width < scroll_x)
|
||||
scroll_x = box->descendant_x1 - box->width;
|
||||
}
|
||||
|
||||
browser_window_scroll_box(bw, box, scroll_x, scroll_y);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1129,6 +1157,7 @@ void browser_window_mouse_drag_end(struct browser_window *bw,
|
|||
}
|
||||
break;
|
||||
|
||||
case DRAGGING_2DSCROLL:
|
||||
case DRAGGING_PAGE_SCROLL:
|
||||
browser_window_set_pointer(GUI_POINTER_DEFAULT);
|
||||
break;
|
||||
|
@ -1218,9 +1247,32 @@ const char *browser_window_scrollbar_click(struct browser_window *bw,
|
|||
scroll += page;
|
||||
} else if (z < w + bar_start + bar_size - w / 4) {
|
||||
status = messages_get(vert ? "ScrollV" : "ScrollH");
|
||||
if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2))
|
||||
bw->drag_type = vert ? DRAGGING_VSCROLL :
|
||||
DRAGGING_HSCROLL;
|
||||
|
||||
/* respond on the click rather than the drag because it gives
|
||||
the scrollbars a more solid, RISC OS feel */
|
||||
if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) {
|
||||
int x0 = 0, x1 = 0;
|
||||
int y0 = 0, y1 = 0;
|
||||
|
||||
if (mouse & BROWSER_MOUSE_CLICK_1) {
|
||||
bw->drag_type = vert ? DRAGGING_VSCROLL :
|
||||
DRAGGING_HSCROLL;
|
||||
} else
|
||||
bw->drag_type = DRAGGING_2DSCROLL;
|
||||
|
||||
/* \todo - some proper numbers please! */
|
||||
if (bw->drag_type != DRAGGING_VSCROLL) {
|
||||
x0 = -1024;
|
||||
x1 = 1024;
|
||||
}
|
||||
if (bw->drag_type != DRAGGING_HSCROLL) {
|
||||
y0 = -1024;
|
||||
y1 = 1024;
|
||||
}
|
||||
gui_window_box_scroll_start(bw->window, x0, y0, x1, y1);
|
||||
if (bw->drag_type == DRAGGING_2DSCROLL)
|
||||
gui_window_hide_pointer();
|
||||
}
|
||||
} else if (z < w + well_size) {
|
||||
status = messages_get(vert ? "ScrollPDown" : "ScrollPRight");
|
||||
if (mouse & BROWSER_MOUSE_CLICK_1)
|
||||
|
@ -1241,19 +1293,16 @@ const char *browser_window_scrollbar_click(struct browser_window *bw,
|
|||
scroll = box->descendant_y0;
|
||||
else if (box->descendant_y1 - box->height < scroll)
|
||||
scroll = box->descendant_y1 - box->height;
|
||||
if (scroll != box->scroll_y) {
|
||||
box->scroll_y = scroll;
|
||||
browser_redraw_box(bw->current_content, box);
|
||||
}
|
||||
if (scroll != box->scroll_y)
|
||||
browser_window_scroll_box(bw, box, box->scroll_x, scroll);
|
||||
|
||||
} else {
|
||||
if (scroll < box->descendant_x0)
|
||||
scroll = box->descendant_x0;
|
||||
else if (box->descendant_x1 - box->width < scroll)
|
||||
scroll = box->descendant_x1 - box->width;
|
||||
if (scroll != box->scroll_x) {
|
||||
box->scroll_x = scroll;
|
||||
browser_redraw_box(bw->current_content, box);
|
||||
}
|
||||
if (scroll != box->scroll_x)
|
||||
browser_window_scroll_box(bw, box, scroll, box->scroll_y);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -1368,6 +1417,26 @@ void browser_redraw_box(struct content *c, struct box *box)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the scroll offsets of a box within a browser window
|
||||
* (In future, copying where possible, rather than redrawing the entire box)
|
||||
*
|
||||
* \param bw browser window
|
||||
* \param box box to be updated
|
||||
* \param scroll_x new horizontal scroll offset
|
||||
* \param scroll_y new vertical scroll offset
|
||||
*/
|
||||
|
||||
void browser_window_scroll_box(struct browser_window *bw, struct box *box, int scroll_x, int scroll_y)
|
||||
{
|
||||
box->scroll_x = scroll_x;
|
||||
box->scroll_y = scroll_y;
|
||||
|
||||
/* fall back to redrawing the whole box */
|
||||
browser_redraw_box(bw->current_content, box);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process a selection from a form select menu.
|
||||
*
|
||||
|
|
|
@ -73,7 +73,8 @@ struct browser_window {
|
|||
DRAGGING_VSCROLL,
|
||||
DRAGGING_HSCROLL,
|
||||
DRAGGING_SELECTION,
|
||||
DRAGGING_PAGE_SCROLL
|
||||
DRAGGING_PAGE_SCROLL,
|
||||
DRAGGING_2DSCROLL
|
||||
} drag_type;
|
||||
|
||||
/** Box currently being scrolled, or 0. */
|
||||
|
|
|
@ -64,6 +64,7 @@ int gui_window_get_height(struct gui_window *g);
|
|||
void gui_window_set_extent(struct gui_window *g, int width, int height);
|
||||
void gui_window_set_status(struct gui_window *g, const char *text);
|
||||
void gui_window_set_pointer(gui_pointer_shape shape);
|
||||
void gui_window_hide_pointer(void);
|
||||
void gui_window_set_url(struct gui_window *g, const char *url);
|
||||
void gui_window_start_throbber(struct gui_window *g);
|
||||
void gui_window_stop_throbber(struct gui_window *g);
|
||||
|
@ -71,6 +72,8 @@ void gui_window_place_caret(struct gui_window *g, int x, int y, int height);
|
|||
void gui_window_remove_caret(struct gui_window *g);
|
||||
void gui_window_new_content(struct gui_window *g);
|
||||
bool gui_window_scroll_start(struct gui_window *g);
|
||||
bool gui_window_box_scroll_start(struct gui_window *g,
|
||||
int x0, int y0, int x1, int y1);
|
||||
|
||||
struct gui_download_window *gui_download_window_create(const char *url,
|
||||
const char *mime_type, struct fetch *fetch,
|
||||
|
@ -87,6 +90,9 @@ void gui_drag_save_selection(struct selection *s, struct gui_window *g);
|
|||
void gui_start_selection(struct gui_window *g);
|
||||
|
||||
void gui_paste_from_clipboard(struct gui_window *g, int x, int y);
|
||||
bool gui_empty_clipboard(void);
|
||||
bool gui_add_to_clipboard(const char *text, size_t length, bool space);
|
||||
bool gui_commit_clipboard(void);
|
||||
bool gui_copy_to_clipboard(struct selection *s);
|
||||
|
||||
void gui_create_form_select_menu(struct browser_window *bw,
|
||||
|
@ -97,5 +103,5 @@ void gui_launch_url(const char *url);
|
|||
bool gui_search_term_highlighted(struct gui_window *g, struct box *box,
|
||||
unsigned *start_idx, unsigned *end_idx);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "netsurf/render/box.h"
|
||||
#include "netsurf/render/font.h"
|
||||
#include "netsurf/utils/log.h"
|
||||
#include "netsurf/utils/utils.h"
|
||||
|
||||
|
||||
#define IS_TEXT(box) ((box)->text && !(box)->object)
|
||||
|
@ -43,8 +44,12 @@ static unsigned selection_label_subtree(struct selection *s, struct box *node, u
|
|||
static bool save_handler(struct box *box, int offset, size_t length, void *handle);
|
||||
static bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
|
||||
seln_traverse_handler handler, void *handle);
|
||||
static struct box *get_box(struct box *b, unsigned offset, int *pidx);
|
||||
|
||||
|
||||
void set_start(struct selection *s, unsigned offset);
|
||||
void set_end(struct selection *s, unsigned offset);
|
||||
|
||||
/**
|
||||
* Decides whether the char at byte offset 'a_idx' in the box 'a' lies after
|
||||
* position 'b' within the textual representation of the content.
|
||||
|
@ -120,8 +125,12 @@ void selection_reinit(struct selection *s, struct box *root)
|
|||
assert(s);
|
||||
|
||||
s->root = root;
|
||||
if (root)
|
||||
s->max_idx = selection_label_subtree(s, root, 0);
|
||||
if (root) {
|
||||
int root_idx = 0;
|
||||
if (root->gadget) root_idx = 0x10000000;
|
||||
|
||||
s->max_idx = selection_label_subtree(s, root, root_idx);
|
||||
}
|
||||
else
|
||||
s->max_idx = 0;
|
||||
|
||||
|
@ -143,6 +152,9 @@ void selection_reinit(struct selection *s, struct box *root)
|
|||
|
||||
void selection_init(struct selection *s, struct box *root)
|
||||
{
|
||||
if (s->defined)
|
||||
selection_clear(s, true);
|
||||
|
||||
s->defined = false;
|
||||
s->start_idx = 0;
|
||||
s->end_idx = 0;
|
||||
|
@ -169,7 +181,6 @@ unsigned selection_label_subtree(struct selection *s, struct box *node, unsigned
|
|||
|
||||
node->byte_offset = idx;
|
||||
|
||||
|
||||
if (node->text && !node->object) {
|
||||
idx += node->length;
|
||||
if (node->space) idx++;
|
||||
|
@ -319,7 +330,9 @@ void selection_track(struct selection *s, struct box *box,
|
|||
|
||||
case DRAG_START:
|
||||
if (after(box, idx, s->end_idx)) {
|
||||
unsigned old_end = s->end_idx;
|
||||
selection_set_end(s, box, idx);
|
||||
set_start(s, old_end);
|
||||
s->drag_state = DRAG_END;
|
||||
}
|
||||
else
|
||||
|
@ -328,7 +341,9 @@ void selection_track(struct selection *s, struct box *box,
|
|||
|
||||
case DRAG_END:
|
||||
if (before(box, idx, s->start_idx)) {
|
||||
unsigned old_start = s->start_idx;
|
||||
selection_set_start(s, box, idx);
|
||||
set_end(s, old_start);
|
||||
s->drag_state = DRAG_START;
|
||||
}
|
||||
else
|
||||
|
@ -386,6 +401,9 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
|
|||
if (box->byte_offset >= end_idx)
|
||||
return true;
|
||||
|
||||
/* read before calling the handler in case it modifies the tree */
|
||||
child = box->children;
|
||||
|
||||
if (IS_TEXT(box) && box->length > 0) {
|
||||
|
||||
if (box->byte_offset >= start_idx &&
|
||||
|
@ -426,7 +444,6 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
|
|||
this is important at the top-levels of the tree for pruning subtrees
|
||||
that lie entirely before the selection */
|
||||
|
||||
child = box->children;
|
||||
if (child) {
|
||||
struct box *next = child->next;
|
||||
|
||||
|
@ -436,9 +453,13 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
|
|||
}
|
||||
|
||||
while (child) {
|
||||
/* read before calling the handler in case it modifies the tree */
|
||||
struct box *next = child->next;
|
||||
|
||||
if (!traverse_tree(child, start_idx, end_idx, handler, handle))
|
||||
return false;
|
||||
child = child->next;
|
||||
|
||||
child = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,7 +584,7 @@ void selection_clear(struct selection *s, bool redraw)
|
|||
|
||||
void selection_select_all(struct selection *s)
|
||||
{
|
||||
int old_start, old_end;
|
||||
unsigned old_start, old_end;
|
||||
bool was_defined;
|
||||
|
||||
assert(s);
|
||||
|
@ -584,6 +605,46 @@ void selection_select_all(struct selection *s)
|
|||
}
|
||||
|
||||
|
||||
void set_start(struct selection *s, unsigned offset)
|
||||
{
|
||||
bool was_defined = selection_defined(s);
|
||||
unsigned old_start = s->start_idx;
|
||||
|
||||
s->start_idx = offset;
|
||||
s->last_was_end = false;
|
||||
s->defined = (s->start_idx < s->end_idx);
|
||||
|
||||
if (was_defined) {
|
||||
if (offset < old_start)
|
||||
selection_redraw(s, s->start_idx, old_start);
|
||||
else
|
||||
selection_redraw(s, old_start, s->start_idx);
|
||||
}
|
||||
else if (selection_defined(s))
|
||||
selection_redraw(s, s->start_idx, s->end_idx);
|
||||
}
|
||||
|
||||
|
||||
void set_end(struct selection *s, unsigned offset)
|
||||
{
|
||||
bool was_defined = selection_defined(s);
|
||||
unsigned old_end = s->end_idx;
|
||||
|
||||
s->end_idx = offset;
|
||||
s->last_was_end = true;
|
||||
s->defined = (s->start_idx < s->end_idx);
|
||||
|
||||
if (was_defined) {
|
||||
if (offset < old_end)
|
||||
selection_redraw(s, s->end_idx, old_end);
|
||||
else
|
||||
selection_redraw(s, old_end, s->end_idx);
|
||||
}
|
||||
else if (selection_defined(s))
|
||||
selection_redraw(s, s->start_idx, s->end_idx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the start position of the current selection, updating the screen.
|
||||
*
|
||||
|
@ -594,21 +655,7 @@ void selection_select_all(struct selection *s)
|
|||
|
||||
void selection_set_start(struct selection *s, struct box *box, int idx)
|
||||
{
|
||||
int old_start = s->start_idx;
|
||||
bool was_defined = selection_defined(s);
|
||||
|
||||
s->start_idx = box->byte_offset + idx;
|
||||
s->last_was_end = false;
|
||||
s->defined = (s->start_idx < s->end_idx);
|
||||
|
||||
if (was_defined) {
|
||||
if (before(box, idx, old_start))
|
||||
selection_redraw(s, s->start_idx, old_start);
|
||||
else
|
||||
selection_redraw(s, old_start, s->start_idx);
|
||||
}
|
||||
else if (selection_defined(s))
|
||||
selection_redraw(s, s->start_idx, s->end_idx);
|
||||
set_start(s, box->byte_offset + idx);
|
||||
}
|
||||
|
||||
|
||||
|
@ -622,21 +669,74 @@ void selection_set_start(struct selection *s, struct box *box, int idx)
|
|||
|
||||
void selection_set_end(struct selection *s, struct box *box, int idx)
|
||||
{
|
||||
int old_end = s->end_idx;
|
||||
bool was_defined = selection_defined(s);
|
||||
set_end(s, box->byte_offset + idx);
|
||||
}
|
||||
|
||||
s->end_idx = box->byte_offset + idx;
|
||||
s->last_was_end = true;
|
||||
s->defined = (s->start_idx < s->end_idx);
|
||||
|
||||
if (was_defined) {
|
||||
if (before(box, idx, old_end))
|
||||
selection_redraw(s, s->end_idx, old_end);
|
||||
else
|
||||
selection_redraw(s, old_end, s->end_idx);
|
||||
/**
|
||||
* Get the box and index of the specified byte offset within the
|
||||
* textual representation.
|
||||
*
|
||||
* \param b root node of search
|
||||
* \param offset byte offset within textual representation
|
||||
* \param pidx receives byte index of selection start point within box
|
||||
* \return ptr to box, or NULL if no selection defined
|
||||
*/
|
||||
|
||||
struct box *get_box(struct box *b, unsigned offset, int *pidx)
|
||||
{
|
||||
struct box *child = b->children;
|
||||
|
||||
if (b->text && !b->object) {
|
||||
|
||||
if (offset >= b->byte_offset &&
|
||||
offset < b->byte_offset + b->length + b->space) {
|
||||
|
||||
/* it's in this box */
|
||||
*pidx = offset - b->byte_offset;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
else if (selection_defined(s))
|
||||
selection_redraw(s, s->start_idx, s->end_idx);
|
||||
|
||||
/* find the first child that could contain this offset */
|
||||
if (child) {
|
||||
struct box *next = child->next;
|
||||
while (next && next->byte_offset < offset) {
|
||||
child = next;
|
||||
next = child->next;
|
||||
}
|
||||
return get_box(child, offset, pidx);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the box and index of the selection start, if defined.
|
||||
*
|
||||
* \param s selection object
|
||||
* \param pidx receives byte index of selection start point within box
|
||||
* \return ptr to box, or NULL if no selection defined
|
||||
*/
|
||||
|
||||
struct box *selection_get_start(struct selection *s, int *pidx)
|
||||
{
|
||||
return (s->defined ? get_box(s->root, s->start_idx, pidx) : NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the box and index of the selection end, if defined.
|
||||
*
|
||||
* \param s selection object
|
||||
* \param pidx receives byte index of selection end point within box
|
||||
* \return ptr to box, or NULL if no selection defined.
|
||||
*/
|
||||
|
||||
struct box *selection_get_end(struct selection *s, int *pidx)
|
||||
{
|
||||
return (s->defined ? get_box(s->root, s->end_idx, pidx) : NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -703,10 +803,12 @@ bool save_handler(struct box *box, int offset, size_t length, void *handle)
|
|||
assert(out);
|
||||
|
||||
if (box) {
|
||||
if (fwrite(box->text + offset, 1, length, out) < length)
|
||||
size_t len = min(length, box->length - offset);
|
||||
|
||||
if (fwrite(box->text + offset, 1, len, out) < len)
|
||||
return false;
|
||||
|
||||
if (box->space)
|
||||
if (box->space && length > len)
|
||||
return (EOF != fputc(' ', out));
|
||||
|
||||
return true;
|
||||
|
|
|
@ -62,9 +62,13 @@ void selection_reinit(struct selection *s, struct box *root);
|
|||
|
||||
void selection_clear(struct selection *s, bool redraw);
|
||||
void selection_select_all(struct selection *s);
|
||||
|
||||
void selection_set_start(struct selection *s, struct box *box, int idx);
|
||||
void selection_set_end(struct selection *s, struct box *box, int idx);
|
||||
|
||||
struct box *selection_get_start(struct selection *s, int *pidx);
|
||||
struct box *selection_get_end(struct selection *s, int *pidx);
|
||||
|
||||
bool selection_click(struct selection *s, struct box *box, browser_mouse_state mouse, int dx, int dy);
|
||||
void selection_track(struct selection *s, struct box *box, browser_mouse_state mouse, int dx, int dy);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "netsurf/desktop/browser.h"
|
||||
|
@ -48,14 +49,22 @@ 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 browser_window *bw, struct box *text_box,
|
||||
unsigned char_offset, unsigned utf8_len);
|
||||
static bool textbox_delete(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,
|
||||
void *handle);
|
||||
static bool delete_handler(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);
|
||||
static struct box *line_below(struct box *text_box);
|
||||
static bool textarea_cut(struct browser_window *bw,
|
||||
struct box *start_box, unsigned start_idx,
|
||||
struct box *end_box, unsigned end_idx);
|
||||
static void textarea_reflow(struct browser_window *bw, struct box *textarea,
|
||||
struct box *inline_container);
|
||||
static bool word_left(const char *text, int *poffset, int *pchars);
|
||||
static bool word_right(const char *text, int len, int *poffset, int *pchars);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -249,7 +258,7 @@ 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(bw, text_box, char_offset, prev_offset - char_offset);
|
||||
textbox_delete(text_box, char_offset, prev_offset - char_offset);
|
||||
}
|
||||
reflow = true;
|
||||
break;
|
||||
|
@ -287,8 +296,7 @@ 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(bw, text_box, char_offset,
|
||||
next_offset - char_offset);
|
||||
textbox_delete(text_box, char_offset, next_offset - char_offset);
|
||||
}
|
||||
reflow = true;
|
||||
break;
|
||||
|
@ -306,48 +314,39 @@ void browser_window_textarea_callback(struct browser_window *bw,
|
|||
break;
|
||||
|
||||
case 21: { /* Ctrl + U */
|
||||
struct box *next;
|
||||
struct box *start_box = line_start(text_box);
|
||||
struct box *end_box = line_end(text_box);
|
||||
|
||||
/* run back to start of line */
|
||||
while (text_box->prev && text_box->prev->type == BOX_TEXT)
|
||||
text_box = text_box->prev;
|
||||
textarea_cut(bw, start_box, 0, end_box, end_box->length);
|
||||
|
||||
/* run forwards to end */
|
||||
next = text_box->next;
|
||||
while (next && next->type == BOX_TEXT) {
|
||||
box_unlink_and_free(text_box);
|
||||
text_box = next;
|
||||
next = text_box->next;
|
||||
}
|
||||
|
||||
/* can we take out this last inline and and the following BR? */
|
||||
if (next && next->type == BOX_BR && next->next) {
|
||||
box_unlink_and_free(text_box);
|
||||
|
||||
/* place caret at start of next box */
|
||||
text_box = next->next;
|
||||
box_unlink_and_free(next);
|
||||
}
|
||||
else
|
||||
textbox_delete(bw, text_box, 0, text_box->length);
|
||||
|
||||
char_offset = 0;
|
||||
reflow = true;
|
||||
}
|
||||
break;
|
||||
text_box = start_box;
|
||||
char_offset = 0;
|
||||
reflow = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 22: /* Ctrl + V */
|
||||
gui_paste_from_clipboard(bw->window,
|
||||
box_x + inline_container->x + text_box->x + pixel_offset,
|
||||
box_y + inline_container->y + text_box->y);
|
||||
break;
|
||||
|
||||
case 24: /* Ctrl + X */
|
||||
if (gui_copy_to_clipboard(bw->sel)) {
|
||||
selection_traverse(bw->sel, delete_handler, bw);
|
||||
/* screen updated and caret repositioned already */
|
||||
return;
|
||||
|
||||
case 24: { /* Ctrl + X */
|
||||
int start_idx, end_idx;
|
||||
struct box *start_box = selection_get_start(bw->sel, &start_idx);
|
||||
struct box *end_box = selection_get_end(bw->sel, &end_idx);
|
||||
if (start_box && end_box) {
|
||||
selection_clear(bw->sel, false);
|
||||
textarea_cut(bw, start_box, start_idx, end_box, end_idx);
|
||||
|
||||
text_box = start_box;
|
||||
char_offset = start_idx;
|
||||
reflow = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_RIGHT:
|
||||
if ((unsigned int) char_offset < text_box->length) {
|
||||
|
@ -398,14 +397,12 @@ void browser_window_textarea_callback(struct browser_window *bw,
|
|||
return;
|
||||
|
||||
case KEY_LINE_START:
|
||||
while (text_box->prev && text_box->prev->type == BOX_TEXT)
|
||||
text_box = text_box->prev;
|
||||
text_box = line_start(text_box);
|
||||
char_offset = 0;
|
||||
break;
|
||||
|
||||
case KEY_LINE_END:
|
||||
while (text_box->next && text_box->next->type == BOX_TEXT)
|
||||
text_box = text_box->next;
|
||||
text_box = line_end(text_box);
|
||||
char_offset = text_box->length;
|
||||
break;
|
||||
|
||||
|
@ -425,41 +422,101 @@ void browser_window_textarea_callback(struct browser_window *bw,
|
|||
char_offset = text_box->length;
|
||||
break;
|
||||
|
||||
case KEY_WORD_LEFT:
|
||||
// while () {
|
||||
// }
|
||||
break;
|
||||
case KEY_WORD_LEFT: {
|
||||
bool start_of_word = (char_offset <= 0 ||
|
||||
isspace(text_box->text[char_offset - 1]));
|
||||
|
||||
case KEY_WORD_RIGHT:
|
||||
// while () {
|
||||
// }
|
||||
break;
|
||||
while (!word_left(text_box->text, &char_offset, NULL)) {
|
||||
struct box *prev = NULL;
|
||||
|
||||
assert(char_offset == 0);
|
||||
|
||||
if (start_of_word) {
|
||||
/* find the preceding non-BR box */
|
||||
prev = text_box->prev;
|
||||
while (prev && prev->type == BOX_BR)
|
||||
prev = prev->prev;
|
||||
}
|
||||
|
||||
if (!prev) {
|
||||
/* just stay at the start of this box */
|
||||
break;
|
||||
}
|
||||
|
||||
text_box = prev;
|
||||
char_offset = prev->length;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_WORD_RIGHT: {
|
||||
bool in_word = (char_offset < text_box->length &&
|
||||
!isspace(text_box->text[char_offset]));
|
||||
|
||||
while (!word_right(text_box->text, text_box->length,
|
||||
&char_offset, NULL)) {
|
||||
struct box *next = text_box->next;
|
||||
|
||||
/* find the next non-BR box */
|
||||
while (next && next->type == BOX_BR)
|
||||
next = next->next;
|
||||
|
||||
if (!next) {
|
||||
/* just stay at the end of this box */
|
||||
char_offset = text_box->length;
|
||||
break;
|
||||
}
|
||||
|
||||
text_box = next;
|
||||
char_offset = 0;
|
||||
|
||||
if (in_word &&
|
||||
text_box->length > 0 &&
|
||||
!isspace(text_box->text[0])) {
|
||||
/* just stay at the start of this box */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_PAGE_UP: {
|
||||
int nlines = (textarea->height / text_box->height) - 1;
|
||||
|
||||
while (nlines-- > 0)
|
||||
text_box = line_above(text_box);
|
||||
|
||||
case KEY_PAGE_UP:
|
||||
// while () {
|
||||
// }
|
||||
if (char_offset > text_box->length)
|
||||
char_offset = text_box->length;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_PAGE_DOWN:
|
||||
case KEY_PAGE_DOWN: {
|
||||
int nlines = (textarea->height / text_box->height) - 1;
|
||||
while (nlines-- > 0)
|
||||
text_box = line_below(text_box);
|
||||
|
||||
/* vague attempt to keep the caret at the same horizontal position,
|
||||
given that the code currently cannot support it being beyond the
|
||||
end of a line */
|
||||
|
||||
// while () {
|
||||
// }
|
||||
if (char_offset > text_box->length)
|
||||
char_offset = text_box->length;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_DELETE_LINE_START:
|
||||
textbox_delete(bw, text_box, 0, char_offset);
|
||||
textarea_cut(bw, line_start(text_box), 0, text_box, char_offset);
|
||||
char_offset = 0;
|
||||
reflow = true;
|
||||
break;
|
||||
|
||||
case KEY_DELETE_LINE_END:
|
||||
textbox_delete(bw, text_box, char_offset,
|
||||
text_box->length - char_offset);
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
|
@ -673,17 +730,13 @@ void browser_window_input_callback(struct browser_window *bw,
|
|||
return;
|
||||
|
||||
box_offset += utf8_len;
|
||||
|
||||
nsfont_width(text_box->style, text_box->text,
|
||||
text_box->length, &text_box->width);
|
||||
changed = true;
|
||||
|
||||
} else switch (key) {
|
||||
case KEY_DELETE_LEFT: {
|
||||
int prev_offset;
|
||||
|
||||
if (box_offset == 0)
|
||||
return;
|
||||
if (box_offset <= 0) return;
|
||||
|
||||
/* Gadget */
|
||||
prev_offset = form_offset;
|
||||
|
@ -702,12 +755,8 @@ 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(bw, text_box, box_offset,
|
||||
prev_offset - box_offset);
|
||||
|
||||
nsfont_width(text_box->style, text_box->text,
|
||||
text_box->length, &text_box->width);
|
||||
|
||||
textbox_delete(text_box, box_offset,
|
||||
prev_offset - box_offset);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
@ -735,12 +784,8 @@ void browser_window_input_callback(struct browser_window *bw,
|
|||
next_offset = utf8_next(text_box->text, text_box->length,
|
||||
box_offset);
|
||||
|
||||
textbox_delete(bw, text_box, box_offset,
|
||||
textbox_delete(text_box, box_offset,
|
||||
next_offset - box_offset);
|
||||
|
||||
nsfont_width(text_box->style, text_box->text,
|
||||
text_box->length, &text_box->width);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
@ -797,8 +842,6 @@ void browser_window_input_callback(struct browser_window *bw,
|
|||
input->gadget->value[0] = 0;
|
||||
input->gadget->length = 0;
|
||||
form_offset = 0;
|
||||
|
||||
text_box->width = 0;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
|
@ -806,7 +849,10 @@ void browser_window_input_callback(struct browser_window *bw,
|
|||
gui_paste_from_clipboard(bw->window,
|
||||
box_x + input->children->x + text_box->x + pixel_offset,
|
||||
box_y + input->children->y + text_box->y);
|
||||
break;
|
||||
|
||||
/* screen updated and caret repositioned already */
|
||||
return;
|
||||
|
||||
|
||||
case KEY_RIGHT:
|
||||
/* Text box */
|
||||
|
@ -837,6 +883,67 @@ void browser_window_input_callback(struct browser_window *bw,
|
|||
form_offset = input->gadget->length;
|
||||
break;
|
||||
|
||||
case KEY_WORD_LEFT: {
|
||||
int nchars;
|
||||
/* Text box */
|
||||
if (word_left(input->gadget->value, &form_offset, &nchars)) {
|
||||
/* Gadget */
|
||||
while (box_offset > 0 && nchars-- > 0)
|
||||
box_offset = utf8_prev(text_box->text, box_offset);
|
||||
} else {
|
||||
box_offset = 0;
|
||||
form_offset = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_WORD_RIGHT: {
|
||||
int nchars;
|
||||
/* Text box */
|
||||
if (word_right(input->gadget->value, input->gadget->length,
|
||||
&form_offset, &nchars)) {
|
||||
/* Gadget */
|
||||
const char *text = text_box->text;
|
||||
unsigned len = text_box->length;
|
||||
while (box_offset < len && nchars-- > 0)
|
||||
box_offset = utf8_next(text, len, box_offset);
|
||||
} else {
|
||||
box_offset = text_box->length;
|
||||
form_offset = input->gadget->length;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_DELETE_LINE_START:
|
||||
|
||||
if (box_offset <= 0) return;
|
||||
|
||||
/* Text box */
|
||||
textbox_delete(text_box, 0, box_offset);
|
||||
box_offset = 0;
|
||||
|
||||
/* Gadget */
|
||||
memmove(input->gadget->value,
|
||||
input->gadget->value + form_offset,
|
||||
(input->gadget->length - form_offset) + 1); /* inc NUL */
|
||||
input->gadget->length -= form_offset;
|
||||
form_offset = 0;
|
||||
changed = true;
|
||||
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);
|
||||
/* Gadget */
|
||||
input->gadget->length = form_offset;
|
||||
input->gadget->value[form_offset] = 0;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -1133,13 +1240,8 @@ bool browser_window_input_paste_text(struct browser_window *bw,
|
|||
utf8 = p;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
|
||||
nsfont_width(text_box->style, text_box->text, text_box->length,
|
||||
&text_box->width);
|
||||
|
||||
if (update)
|
||||
input_update_display(bw, input, form_offset, box_offset, false, true);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
@ -1165,6 +1267,10 @@ void input_update_display(struct browser_window *bw, struct box *input,
|
|||
int box_x, box_y;
|
||||
int dx;
|
||||
|
||||
if (redraw)
|
||||
nsfont_width(text_box->style, text_box->text, text_box->length,
|
||||
&text_box->width);
|
||||
|
||||
box_coords(input, &box_x, &box_y);
|
||||
|
||||
nsfont_width(text_box->style, text_box->text, box_offset,
|
||||
|
@ -1172,7 +1278,7 @@ void input_update_display(struct browser_window *bw, struct box *input,
|
|||
dx = text_box->x;
|
||||
text_box->x = 0;
|
||||
if (input->width < text_box->width &&
|
||||
input->width / 2 < pixel_offset) {
|
||||
input->width / 2 < (int)pixel_offset) {
|
||||
text_box->x = input->width / 2 - pixel_offset;
|
||||
if (text_box->x < input->width - text_box->width)
|
||||
text_box->x = input->width - text_box->width;
|
||||
|
@ -1245,14 +1351,12 @@ 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 browser_window *bw, struct box *text_box,
|
||||
unsigned char_offset, unsigned utf8_len)
|
||||
bool textbox_delete(struct box *text_box, unsigned char_offset, unsigned utf8_len)
|
||||
{
|
||||
unsigned prev_offset = char_offset + utf8_len;
|
||||
if (prev_offset <= text_box->length) {
|
||||
|
@ -1271,10 +1375,89 @@ bool textbox_delete(struct browser_window *bw, struct box *text_box,
|
|||
}
|
||||
|
||||
|
||||
bool delete_handler(struct box *b, int offset, size_t length, void *handle)
|
||||
/**
|
||||
* Delete some text from a box, or delete the box in its entirety
|
||||
*
|
||||
* \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)
|
||||
{
|
||||
struct browser_window *bw = handle;
|
||||
return textbox_delete(bw, b, offset, length);
|
||||
if (offset <= 0 && length >= b->length) {
|
||||
/* remove the entire box */
|
||||
box_unlink_and_free(b);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return textbox_delete(b, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locate the first inline box at the start of this line
|
||||
*
|
||||
* \param text_box text box from which to start searching
|
||||
*/
|
||||
|
||||
struct box *line_start(struct box *text_box)
|
||||
{
|
||||
while (text_box->prev && text_box->prev->type == BOX_TEXT)
|
||||
text_box = text_box->prev;
|
||||
return text_box;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locate the last inline box in this line
|
||||
*
|
||||
* \param text_box text box from which to start searching
|
||||
*/
|
||||
|
||||
struct box *line_end(struct box *text_box)
|
||||
{
|
||||
while (text_box->next && text_box->next->type == BOX_TEXT)
|
||||
text_box = text_box->next;
|
||||
return text_box;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Backtrack to the start of the previous line, if there is one.
|
||||
*/
|
||||
|
||||
struct box *line_above(struct box *text_box)
|
||||
{
|
||||
struct box *prev;
|
||||
|
||||
text_box = line_start(text_box);
|
||||
|
||||
prev = text_box->prev;
|
||||
while (prev && prev->type == BOX_BR)
|
||||
prev = prev->prev;
|
||||
|
||||
return prev ? line_start(prev) : text_box;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Advance to the start of the next line, if there is one.
|
||||
*/
|
||||
|
||||
struct box *line_below(struct box *text_box)
|
||||
{
|
||||
struct box *next;
|
||||
|
||||
text_box = line_end(text_box);
|
||||
|
||||
next = text_box->next;
|
||||
while (next && next->type == BOX_BR)
|
||||
next = next->next;
|
||||
|
||||
return next ? next : text_box;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1322,6 +1505,86 @@ struct box *textarea_insert_break(struct browser_window *bw, struct box *text_bo
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cut a range of text to the global clipboard.
|
||||
*
|
||||
* \param bw browser window
|
||||
* \param start_box text box at start of range
|
||||
* \param start_idx index (bytes) within start box
|
||||
* \param end_box text box at end of range
|
||||
* \param end_idx index (bytes) within end box
|
||||
*/
|
||||
|
||||
bool textarea_cut(struct browser_window *bw,
|
||||
struct box *start_box, unsigned start_idx,
|
||||
struct box *end_box, unsigned end_idx)
|
||||
{
|
||||
struct box *box = start_box;
|
||||
bool success = true;
|
||||
bool del = true;
|
||||
|
||||
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;
|
||||
|
||||
if (box->type == BOX_BR) {
|
||||
if (!gui_add_to_clipboard("\n", 1, false)) {
|
||||
gui_commit_clipboard();
|
||||
return false;
|
||||
}
|
||||
box_unlink_and_free(box);
|
||||
}
|
||||
else {
|
||||
/* append box text to clipboard and then delete it */
|
||||
if (!gui_add_to_clipboard(box->text + start_idx,
|
||||
box->length - start_idx, box->space)) {
|
||||
gui_commit_clipboard();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (del) {
|
||||
if (!delete_handler(box, start_idx,
|
||||
box->length - start_idx)) {
|
||||
gui_commit_clipboard();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
textbox_delete(box, start_idx, box->length - start_idx);
|
||||
}
|
||||
|
||||
del = true;
|
||||
start_idx = 0;
|
||||
box = next;
|
||||
}
|
||||
|
||||
/* and the last box */
|
||||
if (box) {
|
||||
if (gui_add_to_clipboard(box->text + start_idx, end_idx, box->space)) {
|
||||
if (del) {
|
||||
if (!delete_handler(box, start_idx, end_idx - start_idx))
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
textbox_delete(box, start_idx, end_idx - start_idx);
|
||||
}
|
||||
else
|
||||
success = false;
|
||||
}
|
||||
|
||||
return gui_commit_clipboard() ? success : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reflow textarea preserving width and height
|
||||
*
|
||||
|
@ -1344,3 +1607,79 @@ void textarea_reflow(struct browser_window *bw, struct box *textarea,
|
|||
layout_calculate_descendant_bboxes(textarea);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move to the start of the word containing the given character position,
|
||||
* or the start of the preceding word if already at the start of this one.
|
||||
*
|
||||
* \param text UTF-8 text string
|
||||
* \param poffset offset of caret within string (updated on exit)
|
||||
* \param pchars receives the number of characters skipped
|
||||
* \return true iff the start of a word was found before/at the string start
|
||||
*/
|
||||
|
||||
bool word_left(const char *text, int *poffset, int *pchars)
|
||||
{
|
||||
int offset = *poffset;
|
||||
bool success = false;
|
||||
int nchars = 0;
|
||||
|
||||
while (offset > 0) {
|
||||
offset = utf8_prev(text, offset);
|
||||
nchars++;
|
||||
if (!isspace(text[offset])) break;
|
||||
}
|
||||
|
||||
while (offset > 0) {
|
||||
int prev = utf8_prev(text, offset);
|
||||
success = true;
|
||||
if (isspace(text[prev]))
|
||||
break;
|
||||
offset = prev;
|
||||
nchars++;
|
||||
}
|
||||
|
||||
*poffset = offset;
|
||||
if (pchars) *pchars = nchars;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move to the start of the first word following the given character position.
|
||||
*
|
||||
* \param text UTF-8 text string
|
||||
* \param len length of string in bytes
|
||||
* \param poffset offset of caret within string (updated on exit)
|
||||
* \param pchars receives the number of characters skipped
|
||||
* \return true iff the start of a word was found before the string end
|
||||
*/
|
||||
|
||||
bool word_right(const char *text, int len, int *poffset, int *pchars)
|
||||
{
|
||||
int offset = *poffset;
|
||||
bool success = false;
|
||||
int nchars = 0;
|
||||
|
||||
while (offset < len) {
|
||||
if (isspace(text[offset])) break;
|
||||
offset = utf8_next(text, len, offset);
|
||||
nchars++;
|
||||
}
|
||||
|
||||
while (offset < len) {
|
||||
offset = utf8_next(text, len, offset);
|
||||
nchars++;
|
||||
if (offset < len && !isspace(text[offset])) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*poffset = offset;
|
||||
if (pchars) *pchars = nchars;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
@ -333,6 +333,11 @@ void gui_window_set_pointer(gui_pointer_shape shape)
|
|||
}
|
||||
|
||||
|
||||
void gui_window_hide_pointer(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void gui_window_set_url(struct gui_window *g, const char *url)
|
||||
{
|
||||
gtk_entry_set_text(GTK_ENTRY(g->url_bar), url);
|
||||
|
@ -369,6 +374,11 @@ bool gui_window_scroll_start(struct gui_window *g)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool gui_window_box_scroll_start(struct gui_window *g,
|
||||
int x0, int y0, int x1, int y1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_drag_save_object(gui_save_type type, struct content *c,
|
||||
struct gui_window *g)
|
||||
|
@ -391,8 +401,32 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y)
|
|||
}
|
||||
|
||||
|
||||
bool gui_empty_clipboard(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool gui_add_to_clipboard(const char *text, size_t length, bool space)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool gui_commit_clipboard(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool gui_copy_to_clipboard(struct selection *s)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool gui_window_copy_rectangle(struct gui_window *g, int sx, int sy,
|
||||
int dx, int dy, int w, int h)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,10 @@ void ro_gui_search_prepare(struct gui_window *g)
|
|||
|
||||
assert(g != NULL);
|
||||
|
||||
/* if the search dialogue is reopened over a new window, we still
|
||||
need to cancel the previous search */
|
||||
ro_gui_search_end();
|
||||
|
||||
search_current_window = g;
|
||||
|
||||
ro_gui_set_icon_string(dialog_search, ICON_SEARCH_TEXT, "");
|
||||
|
|
|
@ -171,20 +171,61 @@ void ro_gui_selection_drag_end(struct gui_window *g, wimp_dragged *drag)
|
|||
bool copy_handler(struct box *box, int offset, size_t length, void *handle)
|
||||
{
|
||||
size_t len = min(length, box->length - offset);
|
||||
size_t new_length;
|
||||
const char *text;
|
||||
int space = 0;
|
||||
bool space = false;
|
||||
|
||||
if (box) {
|
||||
text = box->text + offset;
|
||||
if (box->space && length > len) space = 1;
|
||||
if (box->space && length > len) space = true;
|
||||
}
|
||||
else {
|
||||
text = "\n";
|
||||
len = 1;
|
||||
}
|
||||
|
||||
new_length = clip_length + len + space;
|
||||
return gui_add_to_clipboard(text, len, space);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Empty the clipboard, called prior to gui_add_to_clipboard and
|
||||
* gui_commit_clipboard
|
||||
*
|
||||
* \return true iff successful
|
||||
*/
|
||||
|
||||
bool gui_empty_clipboard(void)
|
||||
{
|
||||
const int init_size = 1024;
|
||||
|
||||
if (!clip_alloc) {
|
||||
clipboard = malloc(init_size);
|
||||
if (!clipboard) {
|
||||
LOG(("out of memory"));
|
||||
warn_user("NoMemory", 0);
|
||||
return false;
|
||||
}
|
||||
clip_alloc = init_size;
|
||||
}
|
||||
|
||||
clip_length = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add some text to the clipboard, optionally appending a trailing space.
|
||||
*
|
||||
* \param text text to be added
|
||||
* \param length length of text in bytes
|
||||
* \param space indicates whether a trailing space should be appended also
|
||||
* \return true iff successful
|
||||
*/
|
||||
|
||||
bool gui_add_to_clipboard(const char *text, size_t length, bool space)
|
||||
{
|
||||
size_t new_length = clip_length + length + (space ? 1 : 0);
|
||||
|
||||
if (new_length > clip_alloc) {
|
||||
size_t new_alloc = clip_alloc + (clip_alloc / 4);
|
||||
|
@ -199,8 +240,8 @@ bool copy_handler(struct box *box, int offset, size_t length, void *handle)
|
|||
clip_alloc = new_alloc;
|
||||
}
|
||||
|
||||
memcpy(clipboard + clip_length, text, len);
|
||||
clip_length += len;
|
||||
memcpy(clipboard + clip_length, text, length);
|
||||
clip_length += length;
|
||||
if (space) clipboard[clip_length++] = ' ';
|
||||
|
||||
return true;
|
||||
|
@ -208,33 +249,16 @@ bool copy_handler(struct box *box, int offset, size_t length, void *handle)
|
|||
|
||||
|
||||
/**
|
||||
* Copy the selected contents to the global clipboard,
|
||||
* and claim ownership of the clipboard from other apps.
|
||||
* Commit the changes made by gui_empty_clipboard and gui_add_to_clipboard.
|
||||
*
|
||||
* \param s selection
|
||||
* \return true iff successful, ie. cut operation can proceed without losing data
|
||||
* \return true iff successful
|
||||
*/
|
||||
|
||||
bool gui_copy_to_clipboard(struct selection *s)
|
||||
bool gui_commit_clipboard(void)
|
||||
{
|
||||
const int init_size = 1024;
|
||||
utf8_convert_ret res;
|
||||
char *new_cb;
|
||||
|
||||
|
||||
if (!clip_alloc) {
|
||||
clipboard = malloc(init_size);
|
||||
if (!clipboard) {
|
||||
LOG(("out of memory"));
|
||||
warn_user("NoMemory", 0);
|
||||
return false;
|
||||
}
|
||||
clip_alloc = init_size;
|
||||
}
|
||||
|
||||
clip_length = 0;
|
||||
selection_traverse(s, copy_handler, NULL);
|
||||
|
||||
res = utf8_to_local_encoding(clipboard, clip_length, &new_cb);
|
||||
if (res == UTF8_CONVERT_OK) {
|
||||
free(clipboard);
|
||||
|
@ -270,6 +294,26 @@ bool gui_copy_to_clipboard(struct selection *s)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Copy the selected contents to the global clipboard,
|
||||
* and claim ownership of the clipboard from other apps.
|
||||
*
|
||||
* \param s selection
|
||||
* \return true iff successful, ie. cut operation can proceed without losing data
|
||||
*/
|
||||
|
||||
bool gui_copy_to_clipboard(struct selection *s)
|
||||
{
|
||||
if (!gui_empty_clipboard())
|
||||
return false;
|
||||
|
||||
selection_traverse(s, copy_handler, NULL);
|
||||
|
||||
return gui_commit_clipboard();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request to paste the clipboard contents into a textarea/input field
|
||||
* at a given position. Note that the actual operation may take place
|
||||
|
|
|
@ -2438,6 +2438,23 @@ void gui_window_set_pointer(gui_pointer_shape shape)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the mouse pointer from the screen
|
||||
*/
|
||||
|
||||
void gui_window_hide_pointer(void)
|
||||
{
|
||||
os_error *error;
|
||||
|
||||
error = xwimpspriteop_set_pointer_shape(NULL, 0x30, 0, 0, 0, 0);
|
||||
if (error) {
|
||||
LOG(("xwimpspriteop_set_pointer_shape: 0x%x: %s",
|
||||
error->errnum, error->errmess));
|
||||
warn_user("WimpError", error->errmess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the gui_window has new content.
|
||||
*
|
||||
|
@ -2532,6 +2549,50 @@ bool ro_gui_ctrl_pressed(void)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Platform-dependent part of starting a box scrolling operation,
|
||||
* for frames and textareas.
|
||||
*
|
||||
* \param x0 minimum x ordinate of box relative to mouse pointer
|
||||
* \param y0 minimum y ordinate
|
||||
* \param x1 maximum x ordinate
|
||||
* \param y1 maximum y ordinate
|
||||
* \return true iff succesful
|
||||
*/
|
||||
|
||||
bool gui_window_box_scroll_start(struct gui_window *g, int x0, int y0, int x1, int y1)
|
||||
{
|
||||
wimp_pointer pointer;
|
||||
os_error *error;
|
||||
wimp_drag drag;
|
||||
|
||||
error = xwimp_get_pointer_info(&pointer);
|
||||
if (error) {
|
||||
LOG(("xwimp_get_pointer_info 0x%x : %s",
|
||||
error->errnum, error->errmess));
|
||||
warn_user("WimpError", error->errmess);
|
||||
return false;
|
||||
}
|
||||
|
||||
drag.type = wimp_DRAG_USER_POINT;
|
||||
drag.bbox.x0 = pointer.pos.x + (int)(x0 * 2 * g->option.scale);
|
||||
drag.bbox.y0 = pointer.pos.y + (int)(y0 * 2 * g->option.scale);
|
||||
drag.bbox.x1 = pointer.pos.x + (int)(x1 * 2 * g->option.scale);
|
||||
drag.bbox.y1 = pointer.pos.y + (int)(y1 * 2 * g->option.scale);
|
||||
|
||||
error = xwimp_drag_box(&drag);
|
||||
if (error) {
|
||||
LOG(("xwimp_drag_box: 0x%x : %s",
|
||||
error->errnum, error->errmess));
|
||||
warn_user("WimpError", error->errmess);
|
||||
return false;
|
||||
}
|
||||
|
||||
gui_current_drag_type = GUI_DRAG_SCROLL;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts drag scrolling of a browser window
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue