summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Lees <adrian@aemulor.com>2006-02-15 23:09:55 +0000
committerAdrian Lees <adrian@aemulor.com>2006-02-15 23:09:55 +0000
commitdbfdafdf1820a95995c3b260e147c55125468874 (patch)
tree8ef69f8cbc4733490f82b462edeb167c0ee08fb8
parent07d55db910095415d15cc2b6509fd44efa79a875 (diff)
downloadnetsurf-dbfdafdf1820a95995c3b260e147c55125468874.tar.gz
netsurf-dbfdafdf1820a95995c3b260e147c55125468874.tar.bz2
[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
-rw-r--r--desktop/browser.c249
-rw-r--r--desktop/gui.h3
-rw-r--r--desktop/selection.c329
-rw-r--r--desktop/selection.h21
-rw-r--r--desktop/textinput.c54
-rw-r--r--render/box.c27
-rw-r--r--render/box.h7
-rw-r--r--render/html.h14
-rw-r--r--render/html_redraw.c164
-rw-r--r--render/textplain.c294
-rw-r--r--render/textplain.h23
-rw-r--r--riscos/dialog.c1
-rw-r--r--riscos/download.c3
-rw-r--r--riscos/gui.c28
-rw-r--r--riscos/gui.h20
-rw-r--r--riscos/message.c37
-rw-r--r--riscos/message.h3
-rw-r--r--riscos/save.c51
-rw-r--r--riscos/textselection.c271
-rw-r--r--riscos/ucstables.c3
-rw-r--r--riscos/wimp.c29
-rw-r--r--riscos/wimp.h1
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)
@@ -1077,6 +1127,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.
*
* \param bw 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:
@@ -405,29 +352,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
* that are enclosed.
@@ -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;
@@ -730,34 +699,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
@@ -75,6 +75,60 @@ static bool word_right(const char *text, int len, int *poffset, int *pchars);
/**
+ * Remove the given text caret from the window by invalidating it
+ * and causing its former position to be redrawn.
+ *
+ * \param c structure describing text caret
+ */
+
+void caret_remove(struct caret *c)
+{
+ if (c->defined) {
+ int w = (c->height + 7) / 8;
+ int xc = c->x;
+ c->defined = false;
+ browser_window_redraw_rect(c->bw, xc - w, c->y, 2 * w, c->height);
+ }
+}
+
+
+/**
+ * Set the given text caret's position within the window (text box
+ * and byte/pixel offsets within the UTF-8 content of that text box)
+ * and draw it.
+ *
+ * \param c structure describing text caret
+ * \param bw browser window containing caret
+ * \param box INLINE box containing caret
+ * \param char_offset byte offset within UTF-8 representation
+ * \param pixel_offset from left side of box
+ */
+
+void caret_set_position(struct caret *c, struct browser_window *bw,
+ struct box *text_box, int char_offset, int pixel_offset)
+{
+ struct rect r;
+ int xc;
+ int w;
+
+ box_bounds(text_box, &r);
+
+ c->bw = bw;
+ c->text_box = text_box;
+ c->char_offset = char_offset;
+
+ c->x = xc = r.x0 + pixel_offset;
+ c->y = r.y0;
+ c->height = r.y1 - r.y0;
+ w = (c->height + 7) / 8;
+
+ c->defined = true;
+
+ browser_window_redraw_rect(c->bw, xc - w, c->y, w * 2, c->height);
+}
+
+
+/**
* Given the x,y co-ordinates of a point within a textarea, return the
* INLINE box pointer, and the character and pixel offsets within that
* box at which the caret should be positioned. (eg. for mouse clicks,
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;
@@ -232,6 +233,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.
*
* \param box box to search children of
@@ -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,
@@ -370,25 +373,76 @@ 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,47 +513,76 @@ 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.
*
* \param box box to draw
@@ -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 <bursa@users.sourceforge.net>
+ * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net>
*/
/** \file
* Content for text/plain (implementation).
*/
+#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <iconv.h>
@@ -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 <bursa@users.sourceforge.net>
+ * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net>
*/
/** \file
@@ -12,10 +13,16 @@
#ifndef _NETSURF_RENDER_TEXTPLAIN_H_
#define _NETSURF_RENDER_TEXTPLAIN_H_
+#include <stddef.h>
#include <iconv.h>
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
@@ -68,6 +68,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.
*
* This function must be called after wimp_send_message so that a
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 <string.h>
#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