From dbfdafdf1820a95995c3b260e147c55125468874 Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Wed, 15 Feb 2006 23:09:55 +0000 Subject: [project @ 2006-02-15 23:09:53 by adrianl] Extend text selection, copying, saving and searching code to handle textplain contents; modified textplain code to accept other line terminators svn path=/import/netsurf/; revision=2081 --- desktop/browser.c | 249 ++++++++++++++++++++++++++++++++----- desktop/gui.h | 3 +- desktop/selection.c | 329 ++++++++++++++++++++----------------------------- desktop/selection.h | 21 ++-- desktop/textinput.c | 54 ++++++++ render/box.c | 27 +++- render/box.h | 7 ++ render/html.h | 14 +++ render/html_redraw.c | 164 ++++++++++++++++++------ render/textplain.c | 294 ++++++++++++++++++++++++++++++++++++++----- render/textplain.h | 23 +++- riscos/dialog.c | 1 + riscos/download.c | 3 +- riscos/gui.c | 28 ++++- riscos/gui.h | 20 --- riscos/message.c | 37 ++++++ riscos/message.h | 3 + riscos/save.c | 51 +++++--- riscos/textselection.c | 271 +++++++++++++++++++++++++++++++++++++--- riscos/ucstables.c | 3 +- riscos/wimp.c | 29 ----- riscos/wimp.h | 1 - 22 files changed, 1234 insertions(+), 398 deletions(-) diff --git a/desktop/browser.c b/desktop/browser.c index d20153721..8440bbaba 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -35,8 +35,10 @@ #include "netsurf/desktop/textinput.h" #include "netsurf/render/box.h" #include "netsurf/render/form.h" +#include "netsurf/render/font.h" #include "netsurf/render/imagemap.h" #include "netsurf/render/layout.h" +#include "netsurf/render/textplain.h" #include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" #include "netsurf/utils/talloc.h" @@ -62,8 +64,12 @@ static void download_window_callback(fetch_msg msg, void *p, const char *data, unsigned long size); static void browser_window_mouse_action_html(struct browser_window *bw, browser_mouse_state mouse, int x, int y); +static void browser_window_mouse_action_text(struct browser_window *bw, + browser_mouse_state mouse, int x, int y); static void browser_window_mouse_track_html(struct browser_window *bw, browser_mouse_state mouse, int x, int y); +static void browser_window_mouse_track_text(struct browser_window *bw, + browser_mouse_state mouse, int x, int y); static const char *browser_window_scrollbar_click(struct browser_window *bw, browser_mouse_state mouse, struct box *box, int box_x, int box_y, int x, int y); @@ -328,8 +334,16 @@ void browser_window_callback(content_msg msg, struct content *c, global_history_add(url_content); } } - if (c->type == CONTENT_HTML) - selection_init(bw->sel, bw->current_content->data.html.layout); + switch (c->type) { + case CONTENT_HTML: + selection_init(bw->sel, bw->current_content->data.html.layout); + break; + case CONTENT_TEXTPLAIN: + selection_init(bw->sel, NULL); + break; + default: + break; + } break; case CONTENT_MSG_DONE: @@ -729,6 +743,10 @@ void browser_window_mouse_click(struct browser_window *bw, browser_window_mouse_action_html(bw, mouse, x, y); break; + case CONTENT_TEXTPLAIN: + browser_window_mouse_action_text(bw, mouse, x, y); + break; + default: if (mouse & BROWSER_MOUSE_MOD_2) { if (mouse & BROWSER_MOUSE_DRAG_2) @@ -752,7 +770,7 @@ void browser_window_mouse_click(struct browser_window *bw, * Handle mouse clicks and movements in an HTML content window. * * \param bw browser window - * \param click type of mouse click + * \param mouse state of mouse buttons and modifier keys * \param x coordinate of mouse * \param y coordinate of mouse * @@ -919,7 +937,17 @@ void browser_window_mouse_action_html(struct browser_window *bw, } if (text_box) { - selection_click(bw->sel, text_box, mouse, x - box_x, y - box_y); + int pixel_offset; + int idx; + + nsfont_position_in_string(text_box->style, + text_box->text, + text_box->length, + x - box_x, + &idx, + &pixel_offset); + + selection_click(bw->sel, mouse, text_box->byte_offset + idx); if (selection_dragging(bw->sel)) { bw->drag_type = DRAGGING_SELECTION; @@ -943,12 +971,21 @@ void browser_window_mouse_action_html(struct browser_window *bw, y - gadget_box_y); } else if (text_box) { - if (mouse & (BROWSER_MOUSE_DRAG_1 | - BROWSER_MOUSE_DRAG_2)) + int pixel_offset; + int idx; + + 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); + nsfont_position_in_string(text_box->style, + text_box->text, + text_box->length, + x - box_x, + &idx, + &pixel_offset); + + selection_click(bw->sel, mouse, text_box->byte_offset + idx); + if (selection_dragging(bw->sel)) bw->drag_type = DRAGGING_SELECTION; } @@ -1016,6 +1053,7 @@ void browser_window_mouse_action_html(struct browser_window *bw, } } else { + bool done = false; /* if clicking in the main page, remove the selection from any text areas */ if (text_box && @@ -1023,22 +1061,34 @@ void browser_window_mouse_action_html(struct browser_window *bw, selection_root(bw->sel) != c->data.html.layout) selection_init(bw->sel, c->data.html.layout); - if (text_box && selection_click(bw->sel, text_box, mouse, - x - box_x, y - box_y)) { + if (text_box) { + int pixel_offset; + int idx; - /* key presses must be directed at the main browser - * window, paste text operations ignored */ - if (bw->caret_callback) bw->caret_callback = NULL; - if (bw->paste_callback) bw->paste_callback = NULL; + nsfont_position_in_string(text_box->style, + text_box->text, + text_box->length, + x - box_x, + &idx, + &pixel_offset); - if (selection_dragging(bw->sel)) { - bw->drag_type = DRAGGING_SELECTION; - status = messages_get("Selecting"); - } else - status = c->status_message; + if (selection_click(bw->sel, mouse, text_box->byte_offset + idx)) { + /* key presses must be directed at the main browser + * window, paste text operations ignored */ + if (bw->caret_callback) bw->caret_callback = NULL; + if (bw->paste_callback) bw->paste_callback = NULL; + + if (selection_dragging(bw->sel)) { + bw->drag_type = DRAGGING_SELECTION; + status = messages_get("Selecting"); + } else + status = c->status_message; + + done = true; + } } - else { + if (!done) { if (title) status = title; else if (bw->loading_content) @@ -1076,6 +1126,63 @@ void browser_window_mouse_action_html(struct browser_window *bw, } +/** + * Handle mouse clicks and movements in a TEXTPLAIN content window. + * + * \param bw browser window + * \param click type of mouse click + * \param x coordinate of mouse + * \param y coordinate of mouse + * + * This function handles both hovering and clicking. It is important that the + * code path is identical (except that hovering doesn't carry out the action), + * so that the status bar reflects exactly what will happen. Having separate + * code paths opens the possibility that an attacker will make the status bar + * show some harmless action where clicking will be harmful. + */ + +void browser_window_mouse_action_text(struct browser_window *bw, + browser_mouse_state mouse, int x, int y) +{ + struct content *c = bw->current_content; + gui_pointer_shape pointer = GUI_POINTER_DEFAULT; + const char *status = 0; + size_t idx; + int dir = 0; + + bw->drag_type = DRAGGING_NONE; + + if (!bw->sel) return; + + idx = textplain_offset_from_coords(c, x, y, dir); + if (selection_click(bw->sel, mouse, idx)) { + + if (selection_dragging(bw->sel)) { + bw->drag_type = DRAGGING_SELECTION; + status = messages_get("Selecting"); + } + else + status = c->status_message; + } + else { + if (bw->loading_content) + status = bw->loading_content->status_message; + else + status = c->status_message; + + if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { + browser_window_page_drag_start(bw, x, y); + pointer = GUI_POINTER_MOVE; + } + } + + assert(status); + + browser_window_set_status(bw, status); + browser_window_set_pointer(pointer); +} + + /** * Handle mouse movements in a browser window. * @@ -1118,6 +1225,10 @@ void browser_window_mouse_track(struct browser_window *bw, browser_window_mouse_track_html(bw, mouse, x, y); break; + case CONTENT_TEXTPLAIN: + browser_window_mouse_track_text(bw, mouse, x, y); + break; + default: break; } @@ -1128,6 +1239,7 @@ void browser_window_mouse_track(struct browser_window *bw, * Handle mouse tracking (including drags) in an HTML content window. * * \param bw browser window + * \param mouse state of mouse buttons and modifier keys * \param x coordinate of mouse * \param y coordinate of mouse */ @@ -1189,8 +1301,19 @@ void browser_window_mouse_track_html(struct browser_window *bw, box = browser_window_pick_text_box(bw, mouse, x, y, &dx, &dy, dir); - if (box) - selection_track(bw->sel, box, mouse, dx, dy); + if (box) { + int pixel_offset; + int idx; + + nsfont_position_in_string(box->style, + box->text, + box->length, + dx, + &idx, + &pixel_offset); + + selection_track(bw->sel, mouse, box->byte_offset + idx); + } } break; @@ -1202,7 +1325,7 @@ void browser_window_mouse_track_html(struct browser_window *bw, /** - * Handles the end of a drag operation in a browser window. + * Handle mouse tracking (including drags) in a TEXTPLAIN content window. * * \param bw browser window * \param mouse state of mouse buttons and modifier keys @@ -1210,20 +1333,82 @@ void browser_window_mouse_track_html(struct browser_window *bw, * \param y coordinate of mouse */ -void browser_window_mouse_drag_end(struct browser_window *bw, +void browser_window_mouse_track_text(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { switch (bw->drag_type) { + case DRAGGING_SELECTION: { - int dx, dy; - struct box *box; + struct content *c = bw->current_content; int dir = -1; + size_t idx; if (selection_dragging_start(bw->sel)) dir = 1; - box = browser_window_pick_text_box(bw, mouse, x, y, - &dx, &dy, dir); - selection_drag_end(bw->sel, box, mouse, dx, dy); + idx = textplain_offset_from_coords(c, x, y, dir); + selection_track(bw->sel, mouse, idx); + } + break; + + default: + browser_window_mouse_action_text(bw, mouse, x, y); + break; + } +} + + +/** + * Handles the end of a drag operation in a browser window. + * + * \param bw browser window + * \param mouse state of mouse buttons and modifier keys + * \param x coordinate of mouse + * \param y coordinate of mouse + */ + +void browser_window_mouse_drag_end(struct browser_window *bw, + browser_mouse_state mouse, int x, int y) +{ + switch (bw->drag_type) { + case DRAGGING_SELECTION: { + struct content *c = bw->current_content; + if (c) { + bool found = true; + int dir = -1; + int idx; + + if (selection_dragging_start(bw->sel)) dir = 1; + + if (c->type == CONTENT_HTML) { + int pixel_offset; + struct box *box; + int dx, dy; + + box = browser_window_pick_text_box(bw, mouse, x, y, + &dx, &dy, dir); + if (box) { + nsfont_position_in_string(box->style, + box->text, + box->length, + dx, + &idx, + &pixel_offset); + + idx += box->byte_offset; + selection_track(bw->sel, mouse, idx); + } + else + found = false; + } + else { + assert(c->type == CONTENT_TEXTPLAIN); + idx = textplain_offset_from_coords(c, x, y, dir); + } + + if (found) + selection_track(bw->sel, mouse, idx); + } + selection_drag_end(bw->sel); } break; @@ -1438,9 +1623,11 @@ void browser_window_redraw_rect(struct browser_window *bw, int x, int y, { struct content *c = bw->current_content; - if (c && c->type == CONTENT_HTML) { + if (c) { union content_msg_data data; +LOG(("REDRAW %d,%d,%d,%d", x, y, width, height)); + data.redraw.x = x; data.redraw.y = y; data.redraw.width = width; @@ -1792,7 +1979,7 @@ struct box *browser_window_pick_text_box(struct browser_window *bw, if (!text_box) { box = browser_window_nearest_text_box(box, x - box_x, y - box_y, dir); - if (box->text && !box->object) { + if (box && box->text && !box->object) { int w = (box->padding[LEFT] + box->width + box->padding[RIGHT]); int h = (box->padding[TOP] + box->height + box->padding[BOTTOM]); int x1, y1; diff --git a/desktop/gui.h b/desktop/gui.h index 7289500a7..dd7486e80 100644 --- a/desktop/gui.h +++ b/desktop/gui.h @@ -102,7 +102,8 @@ void gui_create_form_select_menu(struct browser_window *bw, void gui_launch_url(const char *url); -bool gui_search_term_highlighted(struct gui_window *g, struct box *box, +bool gui_search_term_highlighted(struct gui_window *g, + unsigned start_offset, unsigned end_offset, unsigned *start_idx, unsigned *end_idx); #endif diff --git a/desktop/selection.c b/desktop/selection.c index 689e2cbd9..829014f10 100644 --- a/desktop/selection.c +++ b/desktop/selection.c @@ -18,8 +18,8 @@ #include "netsurf/desktop/plotters.h" #include "netsurf/desktop/selection.h" #include "netsurf/render/box.h" -#include "netsurf/render/font.h" #include "netsurf/render/form.h" +#include "netsurf/render/textplain.h" #include "netsurf/utils/log.h" #include "netsurf/utils/utf8.h" #include "netsurf/utils/utils.h" @@ -37,7 +37,7 @@ #define IS_TEXT(box) ((box)->text && !(box)->object) -#define IS_INPUT(box) ((box)->gadget && \ +#define IS_INPUT(box) ((box) && (box)->gadget && \ ((box)->gadget->type == GADGET_TEXTAREA || (box)->gadget->type == GADGET_TEXTBOX)) /** check whether the given text box is in the same number space as the @@ -48,10 +48,7 @@ struct rdw_info { bool inited; - int x0; - int y0; - int x1; - int y1; + struct rect r; }; @@ -62,12 +59,13 @@ struct save_state { size_t alloc; }; -static inline bool after(const struct box *a, unsigned a_idx, unsigned b); -static inline bool before(const struct box *a, unsigned a_idx, unsigned b); -static bool redraw_handler(struct box *box, int offset, size_t length, void *handle); +static bool redraw_handler(const char *text, size_t length, bool space, + struct box *box, void *handle); static void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx); -static unsigned selection_label_subtree(struct selection *s, struct box *node, unsigned idx); -static bool save_handler(struct box *box, int offset, size_t length, void *handle); +static unsigned selection_label_subtree(struct selection *s, struct box *node, + unsigned idx); +static bool save_handler(const char *text, size_t length, bool space, + struct box *box, void *handle); 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, @@ -75,38 +73,6 @@ static bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, 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. - * - * \param a box being tested - * \param a_idx byte offset within text of box 'a' - * \param b position within textual representation - */ - -inline bool after(const struct box *a, unsigned a_idx, unsigned b) -{ - return (a->byte_offset + a_idx > b); -} - - -/** - * Decides whether the char at byte offset 'a_idx' in the box 'a' lies before - * position 'b' within the textual representation of the content. - * - * \param a box being tested - * \param a_idx byte offset within text of box 'a' - * \param b position within textual representation - */ - -inline bool before(const struct box *a, unsigned a_idx, unsigned b) -{ - return (a->byte_offset + a_idx < b); -} - /** * Creates a new selection object associated with a browser window. * @@ -177,8 +143,13 @@ void selection_reinit(struct selection *s, struct box *root) if (root) { s->max_idx = selection_label_subtree(s, root, root_idx); } - else - s->max_idx = 0; + else { + struct content *c = s->bw->current_content; + if (c && c->type == CONTENT_TEXTPLAIN) + s->max_idx = textplain_size(c); + else + s->max_idx = 0; + } if (s->defined) { if (s->end_idx > s->max_idx) s->end_idx = s->max_idx; @@ -244,35 +215,23 @@ unsigned selection_label_subtree(struct selection *s, struct box *node, unsigned * Handles mouse clicks (including drag starts) in or near a selection * * \param s selection object - * \param box text box containing the mouse pointer * \param mouse state of mouse buttons and modifier keys - * \param dx x position of mouse relative to top-left of box - * \param dy y position of mouse relative to top-left of box + * \param idx byte offset within textual representation * * \return true iff the click has been handled by the selection code */ -bool selection_click(struct selection *s, struct box *box, - browser_mouse_state mouse, int dx, int dy) +bool selection_click(struct selection *s, browser_mouse_state mouse, unsigned idx) { browser_mouse_state modkeys = (mouse & (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2)); - int pixel_offset; int pos = -1; /* 0 = inside selection, 1 = after it */ - int idx; - if (!s->root ||!SAME_SPACE(s, box->byte_offset)) + if (!SAME_SPACE(s, idx)) return false; /* not our problem */ - nsfont_position_in_string(box->style, - box->text, - box->length, - dx, - &idx, - &pixel_offset); - if (selection_defined(s)) { - if (!before(box, idx, s->start_idx)) { - if (before(box, idx, s->end_idx)) + if (idx >= s->start_idx) { + if (idx < s->end_idx) pos = 0; else pos = 1; @@ -280,7 +239,8 @@ bool selection_click(struct selection *s, struct box *box, } if (!pos && - (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2))) { + ((mouse & BROWSER_MOUSE_DRAG_1) || + (modkeys && (mouse & BROWSER_MOUSE_DRAG_2)))) { /* drag-saving selection */ assert(s->bw); gui_drag_save_selection(s, s->bw->window); @@ -291,8 +251,8 @@ bool selection_click(struct selection *s, struct box *box, /* start new selection drag */ selection_clear(s, true); - selection_set_start(s, box, idx); - selection_set_end(s, box, idx); + selection_set_start(s, idx); + selection_set_end(s, idx); s->drag_state = DRAG_END; @@ -305,13 +265,13 @@ bool selection_click(struct selection *s, struct box *box, return false; /* ignore Adjust drags */ if (pos > 0 || (!pos && s->last_was_end)) { - selection_set_end(s, box, idx); - + selection_set_end(s, idx); + s->drag_state = DRAG_END; } else { - selection_set_start(s, box, idx); - + selection_set_start(s, idx); + s->drag_state = DRAG_START; } gui_start_selection(s->bw->window); @@ -329,9 +289,9 @@ bool selection_click(struct selection *s, struct box *box, return false; if (pos > 0 || (!pos && s->last_was_end)) - selection_set_end(s, box, idx); + selection_set_end(s, idx); else - selection_set_start(s, box, idx); + selection_set_start(s, idx); s->drag_state = DRAG_NONE; } else @@ -352,50 +312,37 @@ bool selection_click(struct selection *s, struct box *box, * end points. * * \param s selection object - * \param box text box containing the mouse pointer * \param mouse state of mouse buttons and modifier keys - * \param dx x position of mouse relative to top-left of box - * \param dy y position of mouse relative to top-left of box + * \param idx byte offset within text representation */ -void selection_track(struct selection *s, struct box *box, - browser_mouse_state mouse, int dx, int dy) +void selection_track(struct selection *s, browser_mouse_state mouse, unsigned idx) { - int pixel_offset; - int idx; - - if (!SAME_SPACE(s, box->byte_offset)) + if (!SAME_SPACE(s, idx)) return; - nsfont_position_in_string(box->style, - box->text, - box->length, - dx, - &idx, - &pixel_offset); - switch (s->drag_state) { case DRAG_START: - if (after(box, idx, s->end_idx)) { + if (idx > s->end_idx) { unsigned old_end = s->end_idx; - selection_set_end(s, box, idx); - set_start(s, old_end); + selection_set_end(s, idx); + selection_set_start(s, old_end); s->drag_state = DRAG_END; } else - selection_set_start(s, box, idx); + selection_set_start(s, idx); break; case DRAG_END: - if (before(box, idx, s->start_idx)) { + if (idx < s->start_idx) { unsigned old_start = s->start_idx; - selection_set_start(s, box, idx); - set_end(s, old_start); + selection_set_start(s, idx); + selection_set_end(s, old_start); s->drag_state = DRAG_START; } else - selection_set_end(s, box, idx); + selection_set_end(s, idx); break; default: @@ -404,29 +351,6 @@ void selection_track(struct selection *s, struct box *box, } -/** - * Handles completion of a drag operation - * - * \param s selection object - * \param box text box containing the mouse pointer - * \param mouse state of mouse buttons and modifier keys - * \param dx x position of mouse relative to top-left of box - * \param dy y position of mouse relative to top-left of box - */ - -void selection_drag_end(struct selection *s, struct box *box, - browser_mouse_state mouse, int dx, int dy) -{ - if (box) { - /* selection_track() does all that we need to do - so avoid code duplication */ - selection_track(s, box, mouse, dx, dy); - } - - s->drag_state = DRAG_NONE; -} - - /** * Tests whether a text box lies partially within the given range of * byte offsets, returning the start and end indexes of the bytes @@ -510,15 +434,17 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, unsigned end_offset; if (selected_part(box, start_idx, end_idx, &start_offset, &end_offset) && - !handler(box, start_offset, end_offset - start_offset, handle)) + !handler(box->text + start_offset, + min(box->length, end_offset) - start_offset, + (end_offset >= box->length), box, handle)) return false; } else { /* make a guess at where the newlines should go */ if (box->byte_offset >= start_idx && box->byte_offset < end_idx) { - - if (!handler(NULL, 0, 0, handle)) + + if (!handler(NULL, 0, false, NULL, handle)) return false; } } @@ -561,8 +487,23 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, bool selection_traverse(struct selection *s, seln_traverse_handler handler, void *handle) { - if (s->root && selection_defined(s)) + struct content *c; + + if (!selection_defined(s)) + return true; /* easy case, nothing to do */ + + if (s->root) return traverse_tree(s->root, s->start_idx, s->end_idx, handler, handle); + + c = s->bw->current_content; + if (!c) return true; + + size_t length; + const char *text = textplain_get_raw_data(c, s->start_idx, s->end_idx, &length); + + if (text && !handler(text, length, false, NULL, handle)) + return false; + return true; } @@ -571,37 +512,41 @@ bool selection_traverse(struct selection *s, seln_traverse_handler handler, void * Selection traversal handler for redrawing the screen when the selection * has been altered. * - * \param box pointer to text box being (partially) added - * \param offset start offset of text within box (bytes) + * \param text pointer to text string * \param length length of text to be appended (bytes) + * \param space text string should be followed by a trailing space + * \param box pointer to text box being (partially) added * \param handle unused handle, we don't need one * \return true iff successful and traversal should continue */ -bool redraw_handler(struct box *box, int offset, size_t length, void *handle) +bool redraw_handler(const char *text, size_t length, bool space, + struct box *box, void *handle) { if (box) { struct rdw_info *r = (struct rdw_info*)handle; int width, height; int x, y; + /* \todo - it should be possible to reduce the redrawn area by + considering the 'text', 'length' and 'space' parameters */ box_coords(box, &x, &y); width = box->padding[LEFT] + box->width + box->padding[RIGHT]; height = box->padding[TOP] + box->height + box->padding[BOTTOM]; if (r->inited) { - if (x < r->x0) r->x0 = x; - if (y < r->y0) r->y0 = y; - if (x + width > r->x1) r->x1 = x + width; - if (y + height > r->y1) r->y1 = y + height; + if (x < r->r.x0) r->r.x0 = x; + if (y < r->r.y0) r->r.y0 = y; + if (x + width > r->r.x1) r->r.x1 = x + width; + if (y + height > r->r.y1) r->r.y1 = y + height; } else { r->inited = true; - r->x0 = x; - r->y0 = y; - r->x1 = x + width; - r->y1 = y + height; + r->r.x0 = x; + r->r.y0 = y; + r->r.x1 = x + width; + r->r.y1 = y + height; } } return true; @@ -620,14 +565,24 @@ void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx) { struct rdw_info rdw; -if (end_idx < start_idx) LOG(("*** asked to redraw from %d to %d", start_idx, end_idx)); assert(end_idx >= start_idx); rdw.inited = false; - if (traverse_tree(s->root, start_idx, end_idx, redraw_handler, &rdw) && - rdw.inited) { - browser_window_redraw_rect(s->bw, rdw.x0, rdw.y0, - rdw.x1 - rdw.x0, rdw.y1 - rdw.y0); + + if (s->root) { + if (!traverse_tree(s->root, start_idx, end_idx, redraw_handler, &rdw)) + return; } + else { + struct content *c = s->bw->current_content; + if (c && c->type == CONTENT_TEXTPLAIN && end_idx > start_idx) { + textplain_coords_from_range(c, start_idx, end_idx, &rdw.r); + rdw.inited = true; + } + } + + if (rdw.inited) + browser_window_redraw_rect(s->bw, rdw.r.x0, rdw.r.y0, + rdw.r.x1 - rdw.r.x0, rdw.r.y1 - rdw.r.y0); } @@ -689,7 +644,14 @@ void selection_select_all(struct selection *s) } -void set_start(struct selection *s, unsigned offset) +/** + * Set the start position of the current selection, updating the screen. + * + * \param s selection object + * \param offset byte offset within textual representation + */ + +void selection_set_start(struct selection *s, unsigned offset) { bool was_defined = selection_defined(s); unsigned old_start = s->start_idx; @@ -709,7 +671,14 @@ void set_start(struct selection *s, unsigned offset) } -void set_end(struct selection *s, unsigned offset) +/** + * Set the end position of the current selection, updating the screen. + * + * \param s selection object + * \param offset byte offset within textual representation + */ + +void selection_set_end(struct selection *s, unsigned offset) { bool was_defined = selection_defined(s); unsigned old_end = s->end_idx; @@ -729,34 +698,6 @@ void set_end(struct selection *s, unsigned offset) } -/** - * Set the start position of the current selection, updating the screen. - * - * \param s selection object - * \param box box object containing start point - * \param idx byte offset of starting point within box - */ - -void selection_set_start(struct selection *s, struct box *box, int idx) -{ - set_start(s, box->byte_offset + idx); -} - - -/** - * Set the end position of the current selection, updating the screen. - * - * \param s selection object - * \param box box object containing end point - * \param idx byte offset of end point within box - */ - -void selection_set_end(struct selection *s, struct box *box, int idx) -{ - set_end(s, box->byte_offset + idx); -} - - /** * Get the box and index of the specified byte offset within the * textual representation. @@ -825,63 +766,64 @@ struct box *selection_get_end(struct selection *s, int *pidx) /** - * Tests whether a text box lies partially within the selection, if there is + * Tests whether a text range lies partially within the selection, if there is * a selection defined, returning the start and end indexes of the bytes * that should be selected. * * \param s the selection object - * \param box the box to be tested + * \param start byte offset of start of text * \param start_idx receives the start index (in bytes) of the highlighted portion * \param end_idx receives the end index (in bytes) * \return true iff part of the given box lies within the selection */ -bool selection_highlighted(struct selection *s, struct box *box, +bool selection_highlighted(struct selection *s, unsigned start, unsigned end, unsigned *start_idx, unsigned *end_idx) { /* caller should have checked first for efficiency */ assert(s); assert(selection_defined(s)); - assert(box); - assert(IS_TEXT(box)); + if (end <= s->start_idx || start >= s->end_idx) + return false; + + *start_idx = (s->start_idx >= start) ? (s->start_idx - start) : 0; + *end_idx = min(end, s->end_idx) - start; + + return true; + +// assert(box); +// assert(IS_TEXT(box)); - return selected_part(box, s->start_idx, s->end_idx, start_idx, end_idx); +// return selected_part(box, s->start_idx, s->end_idx, start_idx, end_idx); } /** * Selection traversal handler for saving the text to a file. * - * \param box pointer to text box being (partially) added - * \param offset start offset of text within box (bytes) + * \param text pointer to text being added, or NULL for newline * \param length length of text to be appended (bytes) - * \param handle unused handle, we don't need one + * \param space trailing space required after text + * \param box pointer to text box (or NULL for textplain content) + * \param handle our save_state workspace pointer * \return true iff the file writing succeeded and traversal should continue. */ -bool save_handler(struct box *box, int offset, size_t length, void *handle) +bool save_handler(const char *text, size_t length, bool space, + struct box *box, void *handle) { struct save_state *sv = handle; size_t new_length; - const char *text; - int space = 0; - size_t len; assert(sv); - if (box) { - len = min(length, box->length - offset); - text = box->text + offset; - - if (box->space && length > len) space = 1; - } - else { + if (!text) { text = "\n"; - len = 1; + length = 1; } - new_length = sv->length + len + space; + new_length = sv->length + length + space; if (new_length >= sv->alloc) { size_t new_alloc = sv->alloc + (sv->alloc / 4); char *new_block; @@ -895,8 +837,8 @@ bool save_handler(struct box *box, int offset, size_t length, void *handle) sv->alloc = new_alloc; } - memcpy(sv->block + sv->length, text, len); - sv->length += len; + memcpy(sv->block + sv->length, text, length); + sv->length += length; if (space) sv->block[sv->length++] = ' '; @@ -915,11 +857,14 @@ bool save_handler(struct box *box, int offset, size_t length, void *handle) bool selection_save_text(struct selection *s, const char *path) { + struct content *c = s->bw->current_content; struct save_state sv = { NULL, 0, 0 }; utf8_convert_ret ret; char *result; FILE *out; + assert(c); + if (!selection_traverse(s, save_handler, &sv)) { free(sv.block); return false; diff --git a/desktop/selection.h b/desktop/selection.h index 57097f3d0..d0e3af886 100644 --- a/desktop/selection.h +++ b/desktop/selection.h @@ -44,8 +44,8 @@ struct selection }; -typedef bool (*seln_traverse_handler)(struct box *b, int offset, - size_t length, void *handle); +typedef bool (*seln_traverse_handler)(const char *text, size_t length, + bool space, struct box *box, void *handle); struct selection *selection_create(struct browser_window *bw); @@ -70,24 +70,23 @@ 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); +void selection_set_start(struct selection *s, unsigned idx); +void selection_set_end(struct selection *s, unsigned 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); +bool selection_click(struct selection *s, browser_mouse_state mouse, unsigned idx); +void selection_track(struct selection *s, browser_mouse_state mouse, unsigned idx); -void selection_drag_end(struct selection *s, struct box *box, - browser_mouse_state mouse, int dx, int dy); +/** Handles completion of a drag operation */ +/* void selection_drag_end(struct selection *s); */ +#define selection_drag_end(s) ((s)->drag_state = DRAG_NONE) bool selection_traverse(struct selection *s, seln_traverse_handler handler, void *handle); -bool selection_highlighted(struct selection *s, struct box *box, +bool selection_highlighted(struct selection *s, unsigned start, unsigned end, unsigned *start_idx, unsigned *end_idx); bool selection_save_text(struct selection *s, const char *path); diff --git a/desktop/textinput.c b/desktop/textinput.c index b2232e6bd..a941cc86b 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -74,6 +74,60 @@ static bool word_left(const char *text, int *poffset, int *pchars); static bool word_right(const char *text, int len, int *poffset, int *pchars); +/** + * Remove the given text caret from the window by invalidating it + * and causing its former position to be redrawn. + * + * \param c structure describing text caret + */ + +void caret_remove(struct caret *c) +{ + if (c->defined) { + int w = (c->height + 7) / 8; + int xc = c->x; + c->defined = false; + browser_window_redraw_rect(c->bw, xc - w, c->y, 2 * w, c->height); + } +} + + +/** + * Set the given text caret's position within the window (text box + * and byte/pixel offsets within the UTF-8 content of that text box) + * and draw it. + * + * \param c structure describing text caret + * \param bw browser window containing caret + * \param box INLINE box containing caret + * \param char_offset byte offset within UTF-8 representation + * \param pixel_offset from left side of box + */ + +void caret_set_position(struct caret *c, struct browser_window *bw, + struct box *text_box, int char_offset, int pixel_offset) +{ + struct rect r; + int xc; + int w; + + box_bounds(text_box, &r); + + c->bw = bw; + c->text_box = text_box; + c->char_offset = char_offset; + + c->x = xc = r.x0 + pixel_offset; + c->y = r.y0; + c->height = r.y1 - r.y0; + w = (c->height + 7) / 8; + + c->defined = true; + + browser_window_redraw_rect(c->bw, xc - w, c->y, w * 2, c->height); +} + + /** * Given the x,y co-ordinates of a point within a textarea, return the * INLINE box pointer, and the character and pixel offsets within that diff --git a/render/box.c b/render/box.c index e527cdde3..5ceb0376c 100644 --- a/render/box.c +++ b/render/box.c @@ -47,8 +47,9 @@ struct box * box_create(struct css_style *style, struct box *box; box = talloc(context, struct box); - if (!box) + if (!box) { return 0; + } box->type = BOX_INLINE; box->style = style; @@ -231,6 +232,27 @@ void box_coords(struct box *box, int *x, int *y) } +/** + * Find the bounds of a box. + * + * \param box the box to calculate bounds of + * \param r receives bounds + */ + +void box_bounds(struct box *box, struct rect *r) +{ + int width, height; + + box_coords(box, &r->x0, &r->y0); + + width = box->padding[LEFT] + box->width + box->padding[RIGHT]; + height = box->padding[TOP] + box->height + box->padding[BOTTOM]; + + r->x1 = r->x0 + width; + r->y1 = r->y0 + height; +} + + /** * Find the boxes at a point. * @@ -244,7 +266,7 @@ void box_coords(struct box *box, int *x, int *y) * \param content updated to content of object that returned box is in, if any * \return box at given point, or 0 if none found * - * To find all the boxes in the heirarchy at a certain point, use code like + * To find all the boxes in the hierarchy at a certain point, use code like * this: * \code * struct box *box = top_of_document_to_search; @@ -469,6 +491,7 @@ void box_dump(struct box *box, unsigned int depth) default: fprintf(stderr, "Unknown box type "); } + fprintf(stderr, "ofst %d", box->byte_offset); if (box->text) fprintf(stderr, "'%.*s' ", (int) box->length, box->text); if (box->space) diff --git a/render/box.h b/render/box.h index 92a48379c..3c62ecffe 100644 --- a/render/box.h +++ b/render/box.h @@ -96,6 +96,12 @@ typedef enum { BOX_INLINE_END } box_type; +struct rect { + int x0, y0; + int x1, y1; +}; + + /** Node in box tree. All dimensions are in pixels. */ struct box { /** Type of box. */ @@ -254,6 +260,7 @@ void box_unlink_and_free(struct box *box); void box_free(struct box *box); void box_free_box(struct box *box); void box_free_object_params(struct object_params *op); +void box_bounds(struct box *box, struct rect *r); void box_coords(struct box *box, int *x, int *y); struct box *box_at_point(struct box *box, int x, int y, int *box_x, int *box_y, diff --git a/render/html.h b/render/html.h index 959624d35..8ad0a7742 100644 --- a/render/html.h +++ b/render/html.h @@ -20,6 +20,7 @@ #include "netsurf/css/css.h" struct box; +struct rect; struct browser_window; struct content; struct form_successful_control; @@ -125,4 +126,17 @@ bool html_redraw(struct content *c, int x, int y, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, unsigned long background_colour); + +/* redraw a short text string, complete with highlighting + (for selection/search) and ghost caret */ + +bool text_redraw(const char *utf8_text, size_t utf8_len, + size_t offset, bool space, + struct css_style *style, + int x, int y, + struct rect *clip, + int height, + float scale, colour current_background_color, + bool excluded); + #endif diff --git a/render/html_redraw.c b/render/html_redraw.c index df7d5bfbb..820a56213 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -18,6 +18,7 @@ #include "netsurf/desktop/gui.h" #include "netsurf/desktop/plotters.h" #include "netsurf/desktop/selection.h" +#include "netsurf/desktop/textinput.h" #include "netsurf/render/box.h" #include "netsurf/render/font.h" #include "netsurf/render/form.h" @@ -34,6 +35,8 @@ static bool html_redraw_box(struct box *box, static bool html_redraw_text_box(struct box *box, int x, int y, int x0, int y0, int x1, int y1, float scale, colour current_background_color); +static bool html_redraw_caret(struct caret *caret, + os_colour current_background_color, float scale); static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, int padding_width, int padding_height, float scale); static bool html_redraw_border_plot(int i, int *p, colour c, @@ -369,26 +372,77 @@ bool html_redraw_box(struct box *box, bool html_redraw_text_box(struct box *box, int x, int y, int x0, int y0, int x1, int y1, float scale, colour current_background_color) +{ + bool excluded = (box->object != NULL); + struct rect clip; + + clip.x0 = x0; + clip.y0 = y0; + clip.x1 = x1; + clip.y1 = y1; + + if (!text_redraw(box->text, box->length, box->byte_offset, + box->space, box->style, x, y, + &clip, box->height, scale, + current_background_color, excluded)) + return false; + + /* does this textbox contain the ghost caret? */ + if (ghost_caret.defined && box == ghost_caret.text_box) { + + if (!html_redraw_caret(&ghost_caret, current_background_color, scale)) + return false; + } + return true; +} + + +/** + * Redraw a short text string, complete with highlighting + * (for selection/search) and ghost caret + * + * \param utf8_text pointer to UTF-8 text string + * \param utf8_len length of string, in bytes + * \param offset byte offset within textual representation + * \param space indicates whether string is followed by a space + * \param style text style to use + * \param x x ordinate at which to plot text + * \param y y ordinate at which to plot text + * \param clip pointer to current clip rectangle + * \param height height of text string + * \param scale current display scale (1.0 = 100%) + * \param current_background_color + * \param excluded exclude this text string from the selection + * \return true iff successful and redraw should proceed + */ + +bool text_redraw(const char *utf8_text, size_t utf8_len, + size_t offset, bool space, struct css_style *style, + int x, int y, struct rect *clip, + int height, + float scale, colour current_background_color, + bool excluded) { bool highlighted = false; /* is this box part of a selection? */ - if (!box->object && current_redraw_browser) { + if (!excluded && current_redraw_browser) { + unsigned len = utf8_len + (space ? 1 : 0); unsigned start_idx; unsigned end_idx; /* first try the browser window's current selection */ if (selection_defined(current_redraw_browser->sel) && selection_highlighted(current_redraw_browser->sel, - box, &start_idx, &end_idx)) { + offset, offset + len, &start_idx, &end_idx)) { highlighted = true; } - /* what about the current search operation, if any */ + /* what about the current search operation, if any? */ if (!highlighted && search_current_window == current_redraw_browser->window && gui_search_term_highlighted(current_redraw_browser->window, - box, &start_idx, &end_idx)) { + offset, offset + len, &start_idx, &end_idx)) { highlighted = true; } @@ -400,23 +454,23 @@ bool html_redraw_text_box(struct box *box, int x, int y, bool text_visible = true; int startx, endx; - if (end_idx > box->length) { - /* adjust for trailing space, not present in box->text */ - assert(end_idx == box->length + 1); - endtxt_idx = box->length; + if (end_idx > utf8_len) { + /* adjust for trailing space, not present in utf8_text */ + assert(end_idx == utf8_len + 1); + endtxt_idx = utf8_len; } - if (!nsfont_width(box->style, box->text, start_idx, &startx)) + if (!nsfont_width(style, utf8_text, start_idx, &startx)) startx = 0; - if (!nsfont_width(box->style, box->text, endtxt_idx, &endx)) + if (!nsfont_width(style, utf8_text, endtxt_idx, &endx)) endx = 0; /* is there a trailing space that should be highlighted as well? */ - if (end_idx > box->length) { + if (end_idx > utf8_len) { int spc_width; /* \todo is there a more elegant/efficient solution? */ - if (nsfont_width(box->style, " ", 1, &spc_width)) + if (nsfont_width(style, " ", 1, &spc_width)) endx += spc_width; } @@ -427,10 +481,10 @@ bool html_redraw_text_box(struct box *box, int x, int y, /* draw any text preceding highlighted portion */ if (start_idx > 0 && - !plot.text(x, y + (int) (box->height * 0.75 * scale), - box->style, box->text, start_idx, + !plot.text(x, y + (int) (height * 0.75 * scale), + style, utf8_text, start_idx, current_background_color, - /*print_text_black ? 0 :*/ box->style->color)) + /*print_text_black ? 0 :*/ style->color)) return false; /* decide whether highlighted portion is to be white-on-black or @@ -442,16 +496,16 @@ bool html_redraw_text_box(struct box *box, int x, int y, hfore_col = hback_col ^ 0xffffff; /* highlighted portion */ - if (!plot.fill(x + startx, y, x + endx, y + box->height * scale, + if (!plot.fill(x + startx, y, x + endx, y + height * scale, hback_col)) return false; if (start_idx > 0) { - int px0 = max(x + startx, x0); - int px1 = min(x + endx, x1); + int px0 = max(x + startx, clip->x0); + int px1 = min(x + endx, clip->x1); if (px0 < px1) { - if (!plot.clip(px0, y0, px1, y1)) + if (!plot.clip(px0, clip->y0, px1, clip->y1)) return false; clip_changed = true; } else @@ -459,46 +513,75 @@ bool html_redraw_text_box(struct box *box, int x, int y, } if (text_visible && - !plot.text(x, y + (int) (box->height * 0.75 * scale), - box->style, box->text, endtxt_idx, + !plot.text(x, y + (int) (height * 0.75 * scale), + style, utf8_text, endtxt_idx, hback_col, hfore_col)) return false; /* draw any text succeeding highlighted portion */ - if (endtxt_idx < box->length) { - int px0 = max(x + endx, x0); - if (px0 < x1) { + if (endtxt_idx < utf8_len) { + int px0 = max(x + endx, clip->x0); + if (px0 < clip->x1) { - if (!plot.clip(px0, y0, x1, y1)) + if (!plot.clip(px0, clip->y0, clip->x1, clip->y1)) return false; clip_changed = true; - if (!plot.text(x, y + (int) (box->height * 0.75 * scale), - box->style, box->text, box->length, + if (!plot.text(x, y + (int) (height * 0.75 * scale), + style, utf8_text, utf8_len, current_background_color, - /*print_text_black ? 0 :*/ box->style->color)) + /*print_text_black ? 0 :*/ style->color)) return false; } } - if (clip_changed && !plot.clip(x0, y0, x1, y1)) + if (clip_changed && + !plot.clip(clip->x0, clip->y0, clip->x1, clip->y1)) return false; } } if (!highlighted) { - if (!plot.text(x, y + (int) (box->height * 0.75 * scale), - box->style, box->text, box->length, + if (!plot.text(x, y + (int) (height * 0.75 * scale), + style, utf8_text, utf8_len, current_background_color, - /*print_text_black ? 0 :*/ box->style->color)) + /*print_text_black ? 0 :*/ style->color)) return false; } - return true; } +/** + * Draw text caret. + * + * \param c structure describing text caret + * \param current_background_color background colour under the caret + * \param scale current scale setting (1.0 = 100%) + * \return true iff successful and redraw should proceed + */ + +bool html_redraw_caret(struct caret *c, os_colour current_background_color, + float scale) +{ + os_colour caret_color = 0x808080; /* todo - choose a proper colour */ + int xc = c->x, y = c->y; + int h = c->height - 1; + int w = (h + 7) / 8; + + return (plot.line(xc * scale, y * scale, + xc * scale, (y + h) * scale, + 0, caret_color, false, false) && + plot.line((xc - w) * scale, y * scale, + (xc + w) * scale, y * scale, + 0, caret_color, false, false) && + plot.line((xc - w) * scale, (y + h) * scale, + (xc + w) * scale, (y + h) * scale, + 0, caret_color, false, false)); +} + + /** * Draw borders for a box. * @@ -514,10 +597,17 @@ bool html_redraw_text_box(struct box *box, int x, int y, bool html_redraw_borders(struct box *box, int x_parent, int y_parent, int padding_width, int padding_height, float scale) { - int top = box->border[TOP] * scale; - int right = box->border[RIGHT] * scale; - int bottom = box->border[BOTTOM] * scale; - int left = box->border[LEFT] * scale; + int top = box->border[TOP]; + int right = box->border[RIGHT]; + int bottom = box->border[BOTTOM]; + int left = box->border[LEFT]; + + if (scale != 1.0) { + top *= scale; + right *= scale; + bottom *= scale; + left *= scale; + } assert(box->style); diff --git a/render/textplain.c b/render/textplain.c index 26f98b671..d7561abd1 100644 --- a/render/textplain.c +++ b/render/textplain.c @@ -3,12 +3,14 @@ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license * Copyright 2006 James Bursa + * Copyright 2006 Adrian Lees */ /** \file * Content for text/plain (implementation). */ +#include #include #include #include @@ -16,6 +18,7 @@ #include "netsurf/css/css.h" #include "netsurf/desktop/gui.h" #include "netsurf/desktop/plotters.h" +#include "netsurf/render/box.h" #include "netsurf/render/font.h" #include "netsurf/render/textplain.h" #include "netsurf/utils/log.h" @@ -76,8 +79,9 @@ bool textplain_create(struct content *c, const char *params[]) c->data.textplain.utf8_data = utf8_data; c->data.textplain.utf8_data_size = 0; c->data.textplain.utf8_data_allocated = CHUNK; - c->data.textplain.physical_line_start = 0; + c->data.textplain.physical_line = 0; c->data.textplain.physical_line_count = 0; + c->data.textplain.formatted_width = 0; return true; @@ -169,11 +173,12 @@ void textplain_reformat(struct content *c, int width, int height) char *utf8_data = c->data.textplain.utf8_data; size_t utf8_data_size = c->data.textplain.utf8_data_size; unsigned long line_count = 0; - size_t *line_start = c->data.textplain.physical_line_start; - size_t *line_start1; + struct textplain_line *line = c->data.textplain.physical_line; + struct textplain_line *line1; size_t i, space, col; size_t columns = 80; int character_width; + size_t line_start; /* compute available columns (assuming monospaced font) - use 8 * characters for better accuracy */ @@ -181,30 +186,49 @@ void textplain_reformat(struct content *c, int width, int height) return; columns = (width - MARGIN - MARGIN) * 8 / character_width; + c->data.textplain.formatted_width = width; + c->data.textplain.physical_line_count = 0; - if (!line_start) { - c->data.textplain.physical_line_start = line_start = - talloc_array(c, size_t, 1024 + 3); - if (!line_start) + if (!line) { + c->data.textplain.physical_line = line = + talloc_array(c, struct textplain_line, 1024 + 3); + if (!line) goto no_memory; } - line_start[line_count++] = 0; + line[line_count++].start = line_start = 0; space = 0; for (i = 0, col = 0; i != utf8_data_size; i++) { - if (utf8_data[i] == '\n' || col + 1 == columns) { + bool term = (utf8_data[i] == '\n' || utf8_data[i] == '\r'); + if (term || col + 1 == columns) { if (line_count % 1024 == 0) { - line_start1 = talloc_realloc(c, line_start, - size_t, line_count + 1024 + 3); - if (!line_start1) + line1 = talloc_realloc(c, line, + struct textplain_line, line_count + 1024 + 3); + if (!line1) goto no_memory; - c->data.textplain.physical_line_start = - line_start = line_start1; - } - if (utf8_data[i] != '\n' && space) - i = space; - line_start[line_count++] = i + 1; + c->data.textplain.physical_line = + line = line1; + } + if (term) { + line[line_count-1].length = i - line_start; + + /* skip second char of CR/LF or LF/CR pair */ + if (i + 1 < utf8_data_size && + utf8_data[i+1] != utf8_data[i] && + (utf8_data[i+1] == '\n' || utf8_data[i+1] == '\r')) + i++; + } + else { + if (space) { + /* break at last space in line */ + i = space; + line[line_count-1].length = (i + 1) - line_start; + } + else + line[line_count-1].length = i - line_start; + } + line[line_count++].start = line_start = i + 1; col = 0; space = 0; } else { @@ -213,7 +237,8 @@ void textplain_reformat(struct content *c, int width, int height) space = i; } } - line_start[line_count] = utf8_data_size; + line[line_count-1].length = i - line[line_count-1].start; + line[line_count].start = utf8_data_size; c->data.textplain.physical_line_count = line_count; c->width = width; @@ -266,14 +291,21 @@ bool textplain_redraw(struct content *c, int x, int y, float scale, unsigned long background_colour) { char *utf8_data = c->data.textplain.utf8_data; - long line; + long lineno; unsigned long line_count = c->data.textplain.physical_line_count; float line_height = css_len2px(&textplain_style.font_size.value.length, - &textplain_style) * 1.2 * scale; - long line0 = clip_y0 / line_height - 1; - long line1 = clip_y1 / line_height + 1; - size_t *line_start = c->data.textplain.physical_line_start; + &textplain_style) * 1.2; + float scaled_line_height = line_height * scale; + long line0 = clip_y0 / scaled_line_height - 1; + long line1 = clip_y1 / scaled_line_height + 1; + struct textplain_line *line = c->data.textplain.physical_line; size_t length; + struct rect clip; + + clip.x0 = clip_x0; + clip.y0 = clip_y0; + clip.x1 = clip_x1; + clip.y1 = clip_y1; if (line0 < 0) line0 = 0; @@ -289,23 +321,219 @@ bool textplain_redraw(struct content *c, int x, int y, if (!plot.clg(0xffffff)) return false; - if (!line_start) + if (!line) return true; x += MARGIN * scale; y += MARGIN * scale; - for (line = line0; line != line1; line++) { - length = line_start[line + 1] - line_start[line]; + for (lineno = line0; lineno != line1; lineno++) { + length = line[lineno].length; if (!length) continue; - if (utf8_data[line_start[line] + length - 1] == '\n') - length--; - if (!plot.text(x, y + (line + 1) * line_height, - &textplain_style, - utf8_data + line_start[line], length, - 0xffffff, 0x000000)) + if (!text_redraw(utf8_data + line[lineno].start, length, + line[lineno].start, false, &textplain_style, + x, y + (lineno * scaled_line_height), + &clip, line_height, scale, + background_colour, false)) return false; } return true; } + + +/** + * Return byte offset within UTF8 textplain content, given the co-ordinates + * of a point within a textplain content. 'dir' specifies the direction in + * which to search (-1 = above-left, +1 = below-right) if the co-ordinates are not + * contained within a line. + * + * \param c content of type CONTENT_TEXTPLAIN + * \param x x ordinate of point + * \param y y ordinate of point + * \param dir direction of search if not within line + * \return ptr to start of line containing (or nearest to) point + */ + +unsigned textplain_offset_from_coords(struct content *c, int x, int y, int dir) +{ + float line_height = css_len2px(&textplain_style.font_size.value.length, + &textplain_style) * 1.2; + struct textplain_line *line; + int pixel_offset; + unsigned nlines; + int idx; + + assert(c->type == CONTENT_TEXTPLAIN); + + y = (int)((float)(y - MARGIN) / line_height); + x -= MARGIN; + + nlines = c->data.textplain.physical_line_count; + if (!nlines) + return 0; + + if (y < 0) y = 0; + else if (y >= nlines) + y = nlines - 1; + line = &c->data.textplain.physical_line[y]; + + if (x < 0) x = 0; + + nsfont_position_in_string(&textplain_style, + c->data.textplain.utf8_data + line->start, + line->length, + x, + &idx, + &pixel_offset); + + return line->start + idx; +} + + +/** + * Given a byte offset within the text, return the line number + * of the line containing that offset (or -1 if offset invalid) + * + * \param c content of type CONTENT_TEXTPLAIN + * \param offset byte offset within textual representation + * \return line number, or -1 if offset invalid (larger than size) + */ + +int textplain_find_line(struct content *c, unsigned offset) +{ + struct textplain_line *line = c->data.textplain.physical_line; + int nlines = c->data.textplain.physical_line_count; + int lineno = 0; + + assert(c->type == CONTENT_TEXTPLAIN); + + if (offset > c->data.textplain.utf8_data_size) + return -1; + +/* \todo - implement binary search here */ + while (lineno < nlines && line[lineno].start < offset) + lineno++; + if (line[lineno].start > offset) + lineno--; + + return lineno; +} + + +/** + * Given a range of byte offsets within a UTF8 textplain content, + * return a box that fully encloses the text + * + * \param c content of type CONTENT_TEXTPLAIN + * \param start byte offset of start of text range + * \param end byte offset of end + * \param r rectangle to be completed + */ + +void textplain_coords_from_range(struct content *c, unsigned start, unsigned end, + struct rect *r) +{ + float line_height = css_len2px(&textplain_style.font_size.value.length, + &textplain_style) * 1.2; + char *utf8_data = c->data.textplain.utf8_data; + struct textplain_line *line; + unsigned lineno = 0; + unsigned nlines; + + assert(c->type == CONTENT_TEXTPLAIN); + assert(start <= end); + assert(end <= c->data.textplain.utf8_data_size); + + nlines = c->data.textplain.physical_line_count; + line = c->data.textplain.physical_line; + + /* find start */ + lineno = textplain_find_line(c, start); + + r->y0 = (int)(MARGIN + lineno * line_height); + + if (lineno + 1 <= nlines || line[lineno + 1].start >= end) { + /* \todo - it may actually be more efficient just to run + forwards most of the time */ + + /* find end */ + lineno = textplain_find_line(c, end); + + r->x0 = 0; + r->x1 = c->data.textplain.formatted_width; + } + else { + /* single line */ + nsfont_width(&textplain_style, + utf8_data + line[lineno].start, + start - line[lineno].start, + &r->x0); + + nsfont_width(&textplain_style, + utf8_data + line[lineno].start, + min(line[lineno].length, end - line[lineno].start), + &r->x1); + } + + r->y1 = (int)(MARGIN + (lineno + 1) * line_height); +} + + +/** + * Return a pointer to the requested line of text. + * + * \param c content of type CONTENT_TEXTPLAIN + * \param lineno line number + * \param poffset receives byte offset of line start within text + * \param plen receives length of returned line + * \return pointer to text, or NULL if invalid line number + */ + +char *textplain_get_line(struct content *c, unsigned lineno, + size_t *poffset, size_t *plen) +{ + struct textplain_line *line; + + assert(c->type == CONTENT_TEXTPLAIN); + + if (lineno >= c->data.textplain.physical_line_count) + return NULL; + line = &c->data.textplain.physical_line[lineno]; + + *poffset = line->start; + *plen = line->length; + return c->data.textplain.utf8_data + line->start; +} + + +/** + * Return a pointer to the raw UTF-8 data, as opposed to the reformatted + * text to fit the window width. Thus only hard newlines are preserved + * in the saved/copied text of a selection. + * + * \param c content of type CONTENT_TEXTPLAIN + * \param start starting byte offset within UTF-8 text + * \param end ending byte offset + * \param plen receives validated length + * \return pointer to text, or NULL if no text + */ + +char *textplain_get_raw_data(struct content *c, unsigned start, unsigned end, + size_t *plen) +{ + size_t utf8_size = c->data.textplain.utf8_data_size; + + assert(c->type == CONTENT_TEXTPLAIN); + + /* any text at all? */ + if (!utf8_size) return NULL; + + /* clamp to valid offset range */ + if (start >= utf8_size) start = utf8_size; + if (end >= utf8_size) end = utf8_size; + + *plen = end - start; + + return c->data.textplain.utf8_data + start; +} diff --git a/render/textplain.h b/render/textplain.h index 27da768ee..0775381fc 100644 --- a/render/textplain.h +++ b/render/textplain.h @@ -3,6 +3,7 @@ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license * Copyright 2006 James Bursa + * Copyright 2006 Adrian Lees */ /** \file @@ -12,10 +13,16 @@ #ifndef _NETSURF_RENDER_TEXTPLAIN_H_ #define _NETSURF_RENDER_TEXTPLAIN_H_ +#include #include struct content; +struct textplain_line { + size_t start; + size_t length; +}; + struct content_textplain_data { const char *encoding; iconv_t iconv_cd; @@ -24,7 +31,8 @@ struct content_textplain_data { size_t utf8_data_size; size_t utf8_data_allocated; unsigned long physical_line_count; - size_t *physical_line_start; + struct textplain_line *physical_line; + int formatted_width; }; bool textplain_create(struct content *c, const char *params[]); @@ -37,4 +45,17 @@ bool textplain_redraw(struct content *c, int x, int y, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, unsigned long background_colour); +/* access to lines for text selection and searching */ +#define textplain_line_count(c) ((c)->data.textplain.physical_line_count) +#define textplain_size(c) ((c)->data.textplain.utf8_data_size) + +size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir); +void textplain_coords_from_range(struct content *c, + unsigned start, unsigned end, struct rect *r); +char *textplain_get_line(struct content *c, unsigned lineno, + size_t *poffset, size_t *plen); +int textplain_find_line(struct content *c, unsigned offset); +char *textplain_get_raw_data(struct content *c, + unsigned start, unsigned end, size_t *plen); + #endif diff --git a/riscos/dialog.c b/riscos/dialog.c index 0f6dcb043..e41dfb9b5 100644 --- a/riscos/dialog.c +++ b/riscos/dialog.c @@ -29,6 +29,7 @@ #include "netsurf/riscos/gui.h" #include "netsurf/riscos/menus.h" #include "netsurf/riscos/options.h" +#include "netsurf/riscos/save.h" #include "netsurf/riscos/theme.h" #include "netsurf/riscos/url_complete.h" #include "netsurf/riscos/wimp.h" diff --git a/riscos/download.c b/riscos/download.c index d377bb128..171b7ffcc 100644 --- a/riscos/download.c +++ b/riscos/download.c @@ -34,6 +34,7 @@ #include "netsurf/desktop/gui.h" #include "netsurf/riscos/dialog.h" #include "netsurf/riscos/options.h" +#include "netsurf/riscos/save.h" #include "netsurf/riscos/query.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" @@ -1294,7 +1295,7 @@ bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit) struct gui_download_window *d = download_window_list; while (d) { - ro_gui_open_window_at_front(d->window); + ro_gui_dialog_open_top(d->window, NULL, 0, 0); d = d->next; } } diff --git a/riscos/gui.c b/riscos/gui.c index 840cc0364..5d58e8e36 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -64,7 +64,9 @@ #include "netsurf/riscos/print.h" #endif #include "netsurf/riscos/query.h" +#include "netsurf/riscos/save.h" #include "netsurf/riscos/save_complete.h" +#include "netsurf/riscos/textselection.h" #include "netsurf/riscos/theme.h" #include "netsurf/riscos/treeview.h" #ifdef WITH_URI @@ -166,7 +168,7 @@ static struct { } prev_sigs; /** Accepted wimp user messages. */ -static wimp_MESSAGE_LIST(38) task_messages = { { +static wimp_MESSAGE_LIST(40) task_messages = { { message_HELP_REQUEST, message_DATA_SAVE, message_DATA_SAVE_ACK, @@ -180,6 +182,8 @@ static wimp_MESSAGE_LIST(38) task_messages = { { message_MODE_CHANGE, message_CLAIM_ENTITY, message_DATA_REQUEST, + message_DRAGGING, + message_DRAG_CLAIM, #ifdef WITH_URI message_URI_PROCESS, message_URI_RETURN_RESULT, @@ -396,6 +400,10 @@ void gui_init(int argc, char** argv) ro_msg_prequit); ro_message_register_route(message_SAVE_DESKTOP, ro_msg_save_desktop); + ro_message_register_route(message_DRAGGING, + ro_gui_selection_dragging); + ro_message_register_route(message_DRAG_CLAIM, + ro_gui_selection_drag_claim); /* end of handler registration */ nsfont_init(); @@ -930,6 +938,7 @@ void gui_multitask(void) ro_gui_handle_event(event, &block); } + /** * Handle Null_Reason_Code events. */ @@ -963,6 +972,10 @@ void ro_gui_null_reason_code(void) ro_gui_window_mouse_at(gui_track_gui_window, &pointer); break; +// case GUI_DRAG_SAVE: +// ro_gui_selection_send_dragging(&pointer); +// break; + default: if (gui_track_wimp_w == history_window) ro_gui_history_mouse_at(&pointer); @@ -1078,7 +1091,13 @@ void ro_gui_close_window_request(wimp_close *close) if (ro_gui_shift_pressed()) return; ro_gui_url_complete_close(NULL, 0); + + /* search must be closed before the main window so that + the content still exists */ + ro_gui_dialog_close_persistent(close->w); browser_window_destroy(g->bw); + return; + } else if ((dw = ro_gui_download_window_lookup(close->w)) != NULL) { ro_gui_download_window_destroy(dw, false); } else { @@ -1106,8 +1125,9 @@ void ro_gui_pointer_leaving_window(wimp_leaving *leaving) switch (gui_current_drag_type) { case GUI_DRAG_SELECTION: case GUI_DRAG_SCROLL: + case GUI_DRAG_SAVE: /* ignore Pointer_Leaving_Window event that the Wimp mysteriously - issues when a Wimp_DragBox drag operations is started */ + issues when a Wimp_DragBox drag operation is started */ break; default: @@ -1745,6 +1765,9 @@ void ro_msg_datasave(wimp_message *message) { wimp_full_message_data_xfer *dataxfer = (wimp_full_message_data_xfer*)message; + /* remove ghost caret if drag-and-drop protocol was used */ +// ro_gui_selection_drag_reset(); + ro_msg_terminate_filename(dataxfer); switch (dataxfer->file_type) { @@ -1801,6 +1824,7 @@ void ro_msg_datasave_ack(wimp_message *message) case GUI_DRAG_SAVE: ro_gui_save_datasave_ack(message); + gui_current_drag_type = GUI_DRAG_NONE; break; default: diff --git a/riscos/gui.h b/riscos/gui.h index 0526650e4..629316ccb 100644 --- a/riscos/gui.h +++ b/riscos/gui.h @@ -119,14 +119,6 @@ bool ro_gui_download_window_keypress(struct gui_download_window *dw, wimp_key *k /* in mouseactions.c */ void ro_gui_mouse_action(struct gui_window *g); -/* in textselection.c */ -void ro_gui_selection_drag_end(struct gui_window *g, wimp_dragged *drag); -void ro_gui_selection_claim_entity(wimp_full_message_claim_entity *claim); -void ro_gui_selection_data_request(wimp_full_message_data_request *req); -bool ro_gui_save_clipboard(const char *path); -void ro_gui_selection_dragging(wimp_full_message_dragging *drag); -void ro_gui_selection_drag_claim(wimp_full_message_drag_claim *drag); - /* in 401login.c */ #ifdef WITH_AUTH void ro_gui_401login_init(void); @@ -180,18 +172,6 @@ void ro_gui_hotlist_prepare_entry_dialog(struct node *node); bool ro_gui_hotlist_dialog_apply(wimp_w w); int ro_gui_hotlist_help(int x, int y); -/* in save.c */ -wimp_w ro_gui_saveas_create(const char *template_name); -void ro_gui_saveas_quit(void); -void ro_gui_save_prepare(gui_save_type save_type, struct content *c); -void ro_gui_save_start_drag(wimp_pointer *pointer); -void ro_gui_drag_icon(int x, int y, const char *sprite); -void ro_gui_save_drag_end(wimp_dragged *drag); -void ro_gui_send_datasave(gui_save_type save_type, wimp_full_message_data_xfer *message, wimp_t to); -void ro_gui_save_datasave_ack(wimp_message *message); -bool ro_gui_save_ok(wimp_w w); -void ro_gui_convert_save_path(char *dp, size_t len, const char *p); - /* in filetype.c */ int ro_content_filetype(struct content *content); int ro_content_filetype_from_type(content_type type); diff --git a/riscos/message.c b/riscos/message.c index 20ad8f37f..40a465e61 100644 --- a/riscos/message.c +++ b/riscos/message.c @@ -67,6 +67,43 @@ bool ro_message_send_message(wimp_event_no event, wimp_message *message, } +/** + * Sends a message and registers a return route for a bounce. + * + * \param event the message event type + * \param message the message to register a route back for + * \param to_w the window to send the message to + * \param to_i the icon + * \param callback the code to call on a bounce + * \param to_t receives the task handle of the window's creator + * \return true on success, false otherwise + */ +bool ro_message_send_message_to_window(wimp_event_no event, wimp_message *message, + wimp_w to_w, wimp_i to_i, void (*callback)(wimp_message *message), + wimp_t *to_t) { + os_error *error; + + assert(message); + + /* send a message */ + error = xwimp_send_message_to_window(event, message, to_w, to_i, to_t); + if (error) { + LOG(("xwimp_send_message_to_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return false; + } + + /* register the default bounce handler */ + if (callback) { + assert(event == wimp_USER_MESSAGE_RECORDED); + return ro_message_register_handler(message, message->action, + callback); + } + return true; +} + + /** * Registers a return route for a message. * diff --git a/riscos/message.h b/riscos/message.h index 3b05d3f03..1da372acb 100644 --- a/riscos/message.h +++ b/riscos/message.h @@ -17,6 +17,9 @@ bool ro_message_send_message(wimp_event_no event, wimp_message *message, wimp_t task, void (*callback)(wimp_message *message)); +bool ro_message_send_message_to_window(wimp_event_no event, wimp_message *message, + wimp_w to_w, wimp_i to_i, void (*callback)(wimp_message *message), + wimp_t *to_t); bool ro_message_register_handler(wimp_message *message, unsigned int message_code, void (*callback)(wimp_message *message)); diff --git a/riscos/save.c b/riscos/save.c index e2a315a56..50335e948 100644 --- a/riscos/save.c +++ b/riscos/save.c @@ -31,8 +31,10 @@ #include "netsurf/riscos/gui.h" #include "netsurf/riscos/menus.h" #include "netsurf/riscos/options.h" +#include "netsurf/riscos/save.h" #include "netsurf/riscos/save_complete.h" #include "netsurf/riscos/save_draw.h" +#include "netsurf/riscos/textselection.h" #include "netsurf/riscos/thumbnail.h" #include "netsurf/riscos/wimp.h" #include "netsurf/riscos/wimp_event.h" @@ -49,6 +51,7 @@ static struct content *gui_save_content = NULL; static struct selection *gui_save_selection = NULL; static int gui_save_filetype; +static bool dragbox_active = false; /** there is a Wimp_DragBox or DragASprite call in progress */ static bool using_dragasprite = true; static bool saving_from_dialog = true; static osspriteop_area *saveas_area = NULL; @@ -365,6 +368,7 @@ void ro_gui_drag_icon(int x, int y, const char *sprite) if (!error) { using_dragasprite = true; + dragbox_active = true; return; } @@ -386,6 +390,8 @@ void ro_gui_drag_icon(int x, int y, const char *sprite) error->errnum, error->errmess)); warn_user("DragError", error->errmess); } + else + dragbox_active = true; } @@ -413,6 +419,31 @@ void ro_gui_convert_save_path(char *dp, size_t len, const char *p) } +void ro_gui_drag_box_cancel(void) +{ + if (dragbox_active) { + os_error *error; + if (using_dragasprite) { + error = xdragasprite_stop(); + if (error) { + LOG(("xdragasprite_stop: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + } + else { + error = xwimp_drag_box(NULL); + if (error) { + LOG(("xwimp_drag_box: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + } + dragbox_active = false; + } +} + + /** * Handle User_Drag_Box event for a drag from the save dialog or browser window. */ @@ -427,22 +458,8 @@ void ro_gui_save_drag_end(wimp_dragged *drag) char *local_name = NULL; utf8_convert_ret err; - if (using_dragasprite) { - error = xdragasprite_stop(); - if (error) { - LOG(("xdragasprite_stop: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("WimpError", error->errmess); - } - } - else { - error = xwimp_drag_box(NULL); - if (error) { - LOG(("xwimp_drag_box: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("WimpError", error->errmess); - } - } + if (dragbox_active) + ro_gui_drag_box_cancel(); error = xwimp_get_pointer_info(&pointer); if (error) { @@ -488,7 +505,9 @@ void ro_gui_save_drag_end(wimp_dragged *drag) ro_gui_convert_save_path(dp, ep - dp, name); +/* \todo - we're supposed to set this if drag-n-drop used */ message.your_ref = 0; + message.action = message_DATA_SAVE; message.data.data_xfer.w = pointer.w; message.data.data_xfer.i = pointer.i; diff --git a/riscos/textselection.c b/riscos/textselection.c index 5e2ddf750..603f645ea 100644 --- a/riscos/textselection.c +++ b/riscos/textselection.c @@ -14,12 +14,32 @@ #include #include "oslib/osfile.h" #include "oslib/wimp.h" +#include "netsurf/desktop/gui.h" #include "netsurf/desktop/selection.h" +#include "netsurf/desktop/textinput.h" #include "netsurf/riscos/gui.h" +#include "netsurf/riscos/message.h" +#include "netsurf/riscos/save.h" +#include "netsurf/riscos/textselection.h" +#include "netsurf/render/form.h" #include "netsurf/utils/log.h" #include "netsurf/utils/utf8.h" #include "netsurf/utils/utils.h" +#ifndef wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX +#define wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX ((wimp_drag_claim_flags) 0x2u) +#endif + + + +/** Receive of Dragging message has claimed it */ +static bool dragging_claimed = false; +static wimp_t dragging_claimant; +static os_box dragging_box = { -34, -34, 34, 34 }; /* \todo - size properly */ +static wimp_drag_claim_flags last_claim_flags = 0; + + +static bool drag_claimed = false; static bool owns_clipboard = false; static bool owns_caret_and_selection = false; @@ -29,9 +49,10 @@ static char *clipboard = NULL; static size_t clip_alloc = 0; static size_t clip_length = 0; -static bool copy_handler(struct box *box, int offset, size_t length, - void *handle); +static bool copy_handler(const char *text, size_t length, bool space, + struct box *box, void *handle); static void ro_gui_discard_clipboard_contents(void); +static void ro_gui_dragging_bounced(wimp_message *message); /** @@ -166,30 +187,22 @@ void ro_gui_selection_drag_end(struct gui_window *g, wimp_dragged *drag) * Selection traversal routine for appending text to the current contents * of the clipboard. * - * \param box pointer to text box being (partially) added (or NULL for newline) - * \param offset start offset of text within box (bytes) - * \param length length of text to be appended (bytes) - * \param handle unused handle, we don't need one + * \param text pointer to text being added, or NULL for newline + * \param length length of text to be appended (bytes) + * \param space trailing space required after text + * \param box pointer to text box, or NULL if from textplain + * \param handle unused handle, we don't need one * \return true iff successful and traversal should continue */ -bool copy_handler(struct box *box, int offset, size_t length, void *handle) +bool copy_handler(const char *text, size_t length, bool space, + struct box *box, void *handle) { - bool space = false; - const char *text; - size_t len; - - if (box) { - len = min(length, box->length - offset); - text = box->text + offset; - if (box->space && length > len) space = true; - } - else { + if (!text) { text = "\n"; - len = 1; + length = 1; } - - return gui_add_to_clipboard(text, len, space); + return gui_add_to_clipboard(text, length, space); } @@ -483,3 +496,221 @@ bool ro_gui_save_clipboard(const char *path) } return true; } + + +/** + * Handler for Message_Dragging, used to implement auto-scrolling and + * ghost caret when a drag is in progress. + */ + +void ro_gui_selection_dragging(wimp_message *message) +{ + wimp_full_message_dragging *drag = (wimp_full_message_dragging*)message; + struct box *textarea = NULL; + struct box *text_box = NULL; + struct browser_window *bw; + wimp_window_state state; + struct content *content; + int gadget_box_x = 0; + int gadget_box_y = 0; + struct gui_window *g; + os_error *error; + int box_x = 0; + int box_y = 0; + int x, y; + +/* with autoscrolling, we will probably need to remember the gui_window and + override the drag->w window handle which could be any window on the desktop */ + g = ro_gui_window_lookup(drag->w); + + if ((drag->flags & wimp_DRAGGING_TERMINATE_DRAG) || !g) { + + if (drag_claimed) { + /* make sure that we erase the ghost caret */ + caret_remove(&ghost_caret); + } + + drag_claimed = false; + return; + } + + state.w = drag->w; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s\n", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + drag_claimed = false; + return; + } + + x = window_x_units(drag->pos.x, &state) / 2 / + g->option.scale; + y = -window_y_units(drag->pos.y, &state) / 2 / + g->option.scale; + + bw = g->bw; + content = bw->current_content; + if (content && content->type == CONTENT_HTML && + content->data.html.layout) { + + struct box *box = content->data.html.layout; + + while ((box = box_at_point(box, x, y, &box_x, &box_y, &content))) { + if (box->style && + box->style->visibility == CSS_VISIBILITY_HIDDEN) + continue; + + if (box->gadget) { + switch (box->gadget->type) { + + case GADGET_TEXTAREA: + textarea = box; + gadget_box_x = box_x; + gadget_box_y = box_y; + case GADGET_TEXTBOX: + case GADGET_PASSWORD: + text_box = box; + break; + + default: /* appease compiler */ + break; + } + } + } + } + + if (textarea) { + int pixel_offset; + int char_offset; + + /* draw/move the ghost caret */ + if (drag_claimed) + caret_remove(&ghost_caret); + else + gui_window_set_pointer(GUI_POINTER_CARET); + + text_box = textarea_get_position(textarea, x - gadget_box_x, + y - gadget_box_y, &char_offset, &pixel_offset); + + caret_set_position(&ghost_caret, bw, text_box, char_offset, pixel_offset); + drag_claimed = true; + } + else { + if (drag_claimed) { + /* make sure that we erase the ghost caret */ + caret_remove(&ghost_caret); + } + drag_claimed = false; + } + + if (drag_claimed) { + wimp_full_message_drag_claim claim; + os_error *error; + + claim.size = offsetof(wimp_full_message_drag_claim, file_types) + 8; + claim.your_ref = drag->my_ref; + claim.action = message_DRAG_CLAIM; + claim.flags = wimp_DRAG_CLAIM_POINTER_CHANGED | wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX; + claim.file_types[0] = osfile_TYPE_TEXT; + claim.file_types[1] = ~0; + + error = xwimp_send_message(wimp_USER_MESSAGE, (wimp_message*)&claim, + drag->sender); + if (error) { + LOG(("xwimp_send_message: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + drag_claimed = true; + } +} + + + +/** + * Reset drag-and-drop state when drag completes (DataSave received) + */ + +void ro_gui_selection_drag_reset(void) +{ + caret_remove(&ghost_caret); + drag_claimed = false; +} + + +/** + * + */ + +void ro_gui_selection_drag_claim(wimp_message *message) +{ + wimp_full_message_drag_claim *claim = (wimp_full_message_drag_claim*)message; + + dragging_claimant = message->sender; + dragging_claimed = true; + + /* have we been asked to remove the drag box/sprite? */ + if (claim->flags & wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX) { + ro_gui_drag_box_cancel(); + } + else { + /* \todo - restore it here? */ + } + + /* do we need to restore the default pointer shape? */ + if ((last_claim_flags & wimp_DRAG_CLAIM_POINTER_CHANGED) && + !(claim->flags & wimp_DRAG_CLAIM_POINTER_CHANGED)) { + gui_window_set_pointer(GUI_POINTER_DEFAULT); + } + + last_claim_flags = claim->flags; +} + + +void ro_gui_selection_send_dragging(wimp_pointer *pointer) +{ + wimp_full_message_dragging dragmsg; + + LOG(("sending DRAGGING to %p, %d", pointer->w, pointer->i)); + + dragmsg.size = offsetof(wimp_full_message_dragging, file_types) + 8; + dragmsg.your_ref = 0; + dragmsg.action = message_DRAGGING; + dragmsg.w = pointer->w; + dragmsg.i = pointer->i; + dragmsg.pos = pointer->pos; +/* \todo - this is interesting because it depends upon not just the state of the + shift key, but also whether it /can/ be deleted, ie. from text area/input + rather than page contents */ + dragmsg.flags = wimp_DRAGGING_FROM_SELECTION; + dragmsg.box = dragging_box; + dragmsg.file_types[0] = osfile_TYPE_TEXT; + dragmsg.file_types[1] = ~0; + + /* if the message_dragmsg messages have been claimed we must address them + to the claimant task, which is not necessarily the task that owns whatever + window happens to be under the pointer */ + + if (dragging_claimed) { + ro_message_send_message(wimp_USER_MESSAGE_RECORDED, + (wimp_message*)&dragmsg, dragging_claimant, ro_gui_dragging_bounced); + } + else { + ro_message_send_message_to_window(wimp_USER_MESSAGE_RECORDED, + (wimp_message*)&dragmsg, pointer->w, pointer->i, + ro_gui_dragging_bounced, &dragging_claimant); + } +} + + +/** + * Our message_DRAGGING message was bounced, ie. the intended recipient does not + * support the drag-and-drop protocol or cannot receive the data at the pointer + * position. + */ + +void ro_gui_dragging_bounced(wimp_message *message) +{ + dragging_claimed = false; +} diff --git a/riscos/ucstables.c b/riscos/ucstables.c index 92b9121f9..ffe92be30 100644 --- a/riscos/ucstables.c +++ b/riscos/ucstables.c @@ -451,7 +451,8 @@ utf8_convert_ret utf8_to_local_encoding(const char *string, size_t len, const char *enc; utf8_convert_ret err; - assert(string && result); + assert(string); + assert(result); /* get length, if necessary */ if (len == 0) diff --git a/riscos/wimp.c b/riscos/wimp.c index e9188aeb9..fa667d1d6 100644 --- a/riscos/wimp.c +++ b/riscos/wimp.c @@ -849,32 +849,3 @@ bool ro_gui_wimp_check_window_furniture(wimp_w w, wimp_window_flags mask) { } return state.flags & mask; } - - -/** - * Open/move a window to the front of the window stack. - */ - -bool ro_gui_open_window_at_front(wimp_w w) { - wimp_window_state state; - os_error *error; - - state.w = w; - error = xwimp_get_window_state(&state); - if (error) { - LOG(("xwimp_get_window_state: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("WimpError", error->errmess); - return false; - } - - state.next = wimp_TOP; - error = xwimp_open_window((wimp_open*)&state); - if (error) { - LOG(("xwimp_open_window: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("WimpError", error->errmess); - return false; - } - return true; -} diff --git a/riscos/wimp.h b/riscos/wimp.h index 35a1ddc30..a7777d28d 100644 --- a/riscos/wimp.h +++ b/riscos/wimp.h @@ -59,6 +59,5 @@ void ro_gui_user_redraw(wimp_draw *redraw, bool user_fill, os_colour user_colour void ro_gui_wimp_update_window_furniture(wimp_w w, wimp_window_flags bic_mask, wimp_window_flags xor_mask); bool ro_gui_wimp_check_window_furniture(wimp_w w, wimp_window_flags mask); -bool ro_gui_open_window_at_front(wimp_w w); #endif -- cgit v1.2.3