[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:
Adrian Lees 2005-07-20 23:27:28 +00:00
parent 5e14874115
commit 1a1901d19b
10 changed files with 880 additions and 216 deletions

View File

@ -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.
*

View File

@ -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. */

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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, "");

View File

@ -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

View File

@ -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
*