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