A load of refactoring of how content selection and input work.

Keypresses now go via content interface.
Contents don't shove the selection object into browser windows any more.
Contents report selection existence by sending message.
HTML content keeps track of where selections in it exist.
Contents report whether they have input focus via caret setting msg.
Caret can be hidden (can still input/paste) or removed.
Consolidate textarea selection handling.
Make textarea report its selection status changes to client.
Various textarea fixes.
Changed how we decide when to clear selections, and give focus.
This commit is contained in:
Michael Drake 2013-02-22 12:19:35 +00:00
parent 2b0cc398bb
commit c2a718075a
25 changed files with 1006 additions and 498 deletions

View File

@ -474,6 +474,26 @@ void content_mouse_action(hlcache_handle *h, struct browser_window *bw,
}
/**
* Handle keypresses.
*
* \param h Content handle
* \param key The UCS4 character codepoint
* \return true if key handled, false otherwise
*/
bool content_keypress(struct hlcache_handle *h, uint32_t key)
{
struct content *c = hlcache_handle_get_content(h);
assert(c != NULL);
if (c->handler->keypress != NULL)
return c->handler->keypress(c, key);
return false;
}
/**
* Request a redraw of an area of a content
*
@ -738,10 +758,26 @@ void content_close(hlcache_handle *h)
/**
* Find this content's selection context, if it has one.
* Tell a content that any selection it has, or one of its objects has, must be
* cleared.
*/
struct selection *content_get_selection(hlcache_handle *h)
void content_clear_selection(hlcache_handle *h)
{
struct content *c = hlcache_handle_get_content(h);
assert(c != 0);
if (c->handler->get_selection != NULL)
c->handler->clear_selection(c);
}
/**
* Get a text selection from a content. Ownership is passed to the caller,
* who must free() it.
*/
char * content_get_selection(hlcache_handle *h)
{
struct content *c = hlcache_handle_get_content(h);
assert(c != 0);

View File

@ -79,6 +79,8 @@ typedef enum {
CONTENT_MSG_DRAGSAVE, /**< Allow drag saving of content */
CONTENT_MSG_SAVELINK, /**< Allow URL to be saved */
CONTENT_MSG_POINTER, /**< Wants a specific mouse pointer set */
CONTENT_MSG_SELECTION, /**< A selection made or cleared */
CONTENT_MSG_CARET, /**< Caret movement / hiding */
CONTENT_MSG_DRAG /**< A drag started or ended */
} content_msg;
@ -153,6 +155,25 @@ union content_msg_data {
} savelink;
/** CONTENT_MSG_POINTER - Mouse pointer to set */
browser_pointer_shape pointer;
/** CONTENT_MSG_SELECTION - Selection made or cleared */
struct {
bool selection; /**< false for selection cleared */
bool read_only;
} selection;
/** CONTENT_MSG_CARET - set caret position or, hide caret */
struct {
enum {
CONTENT_CARET_SET_POS,
CONTENT_CARET_HIDE,
CONTENT_CARET_REMOVE
} type;
struct {
int x; /**< Carret x-coord */
int y; /**< Carret y-coord */
int height; /**< Carret height */
const struct rect *clip; /**< Carret clip rect */
} pos; /**< With CONTENT_CARET_SET_POS */
} caret;
/** CONTENT_MSG_DRAG - Drag start or end */
struct {
enum {
@ -219,12 +240,14 @@ void content_mouse_track(struct hlcache_handle *h, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
void content_mouse_action(struct hlcache_handle *h, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
bool content_keypress(struct hlcache_handle *h, uint32_t key);
bool content_redraw(struct hlcache_handle *h, struct content_redraw_data *data,
const struct rect *clip, const struct redraw_context *ctx);
void content_open(struct hlcache_handle *h, struct browser_window *bw,
struct content *page, struct object_params *params);
void content_close(struct hlcache_handle *h);
struct selection *content_get_selection(struct hlcache_handle *h);
void content_clear_selection(struct hlcache_handle *h);
char * content_get_selection(struct hlcache_handle *h);
void content_get_contextual_content(struct hlcache_handle *h,
int x, int y, struct contextual_content *data);
bool content_scroll_at_point(struct hlcache_handle *h,

View File

@ -58,13 +58,15 @@ struct content_handler {
browser_mouse_state mouse, int x, int y);
void (*mouse_action)(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
bool (*keypress)(struct content *c, uint32_t key);
bool (*redraw)(struct content *c, struct content_redraw_data *data,
const struct rect *clip,
const struct redraw_context *ctx);
void (*open)(struct content *c, struct browser_window *bw,
struct content *page, struct object_params *params);
void (*close)(struct content *c);
struct selection * (*get_selection)(struct content *c);
void (*clear_selection)(struct content *c);
char * (*get_selection)(struct content *c);
void (*get_contextual_content)(struct content *c, int x, int y,
struct contextual_content *data);
bool (*scroll_at_point)(struct content *c, int x, int y,

View File

@ -428,42 +428,69 @@ struct browser_window * browser_window_get_root(struct browser_window *bw)
}
/* exported interface, documented in browser.h */
bool browser_window_has_selection(struct browser_window *bw)
browser_editor_flags browser_window_get_editor_flags(struct browser_window *bw)
{
browser_editor_flags ed_flags = BW_EDITOR_NONE;
assert(bw->window);
assert(bw->parent == NULL);
if (bw->cur_sel != NULL && selection_defined(bw->cur_sel)) {
return true;
} else {
return false;
if (bw->selection.bw != NULL) {
ed_flags |= BW_EDITOR_CAN_COPY;
if (!bw->selection.read_only)
ed_flags |= BW_EDITOR_CAN_CUT;
}
if (bw->can_edit)
ed_flags |= BW_EDITOR_CAN_PASTE;
return ed_flags;
}
/* exported interface, documented in browser.h */
void browser_window_set_selection(struct browser_window *bw,
struct selection *s)
char * browser_window_get_selection(struct browser_window *bw)
{
assert(bw->window);
assert(bw->parent == NULL);
if (bw->cur_sel != s && bw->cur_sel != NULL) {
/* Clear any existing selection */
selection_clear(bw->cur_sel, true);
}
if (bw->selection.bw == NULL ||
bw->selection.bw->current_content == NULL)
return NULL;
/* Replace current selection pointer */
if (s == NULL && bw->current_content != NULL) {
bw->cur_sel = content_get_selection(bw->current_content);
} else {
bw->cur_sel = s;
}
return content_get_selection(bw->selection.bw->current_content);
}
/* exported interface, documented in browser.h */
struct selection *browser_window_get_selection(struct browser_window *bw)
/**
* Set or remove a selection.
*
* \param bw browser window with selection
* \param selection true if bw has a selection, false if removing selection
* \param read_only true iff selection is read only (e.g. can't cut it)
*/
static void browser_window_set_selection(struct browser_window *bw,
bool selection, bool read_only)
{
assert(bw->window);
struct browser_window *top;
return bw->cur_sel;
assert(bw != NULL);
top = browser_window_get_root(bw);
assert(top != NULL);
if (bw != top->selection.bw && top->selection.bw != NULL &&
top->selection.bw->current_content != NULL) {
/* clear old selection */
content_clear_selection(top->selection.bw->current_content);
}
if (selection) {
top->selection.bw = bw;
} else {
top->selection.bw = NULL;
}
top->selection.read_only = read_only;
}
/* exported interface, documented in browser.h */
@ -739,7 +766,6 @@ void browser_window_initialise_common(struct browser_window *bw,
bw->history = history_clone(clone->history);
/* window characteristics */
bw->cur_sel = NULL;
bw->cur_search = NULL;
bw->refresh_interval = -1;
@ -919,7 +945,7 @@ nserror browser_window_navigate(struct browser_window *bw,
}
browser_window_stop(bw);
browser_window_remove_caret(bw);
browser_window_remove_caret(bw, false);
browser_window_destroy_children(bw);
LOG(("Loading '%s'", nsurl_access(url)));
@ -1159,7 +1185,7 @@ browser_window_callback_errorcode(hlcache_handle *c,
bw->loading_content = NULL;
} else if (c == bw->current_content) {
bw->current_content = NULL;
browser_window_remove_caret(bw);
browser_window_remove_caret(bw, false);
}
hlcache_handle_release(c);
@ -1230,7 +1256,7 @@ nserror browser_window_callback(hlcache_handle *c,
browser_window_get_dimensions(bw, &width, &height, true);
content_reformat(c, false, width, height);
browser_window_remove_caret(bw);
browser_window_remove_caret(bw, false);
if (bw->window)
gui_window_new_content(bw->window);
@ -1260,11 +1286,6 @@ nserror browser_window_callback(hlcache_handle *c,
}
}
if (bw->window != NULL) {
browser_window_set_selection(bw,
content_get_selection(c));
}
/* frames */
if (content_get_type(c) == CONTENT_HTML &&
html_get_frameset(c) != NULL)
@ -1315,7 +1336,7 @@ nserror browser_window_callback(hlcache_handle *c,
bw->loading_content = NULL;
else if (c == bw->current_content) {
bw->current_content = NULL;
browser_window_remove_caret(bw);
browser_window_remove_caret(bw, false);
}
hlcache_handle_release(c);
@ -1355,8 +1376,7 @@ nserror browser_window_callback(hlcache_handle *c,
browser_window_recalculate_iframes(bw);
}
if (bw->move_callback)
bw->move_callback(bw, bw->caret_p1, bw->caret_p2);
/* TODO: get caret pos redraw */
if (!(event->data.background)) {
/* Reformatted content should be redrawn */
@ -1515,6 +1535,29 @@ nserror browser_window_callback(hlcache_handle *c,
}
break;
case CONTENT_MSG_CARET:
switch (event->data.caret.type) {
case CONTENT_CARET_REMOVE:
browser_window_remove_caret(bw, false);
break;
case CONTENT_CARET_HIDE:
browser_window_remove_caret(bw, true);
break;
case CONTENT_CARET_SET_POS:
browser_window_place_caret(bw,
event->data.caret.pos.x,
event->data.caret.pos.y,
event->data.caret.pos.height);
break;
}
break;
case CONTENT_MSG_SELECTION:
browser_window_set_selection(bw,
event->data.selection.selection,
event->data.selection.read_only);
break;
default:
assert(0);
}
@ -2083,10 +2126,8 @@ void browser_window_destroy_internal(struct browser_window *bw)
if (top->focus == bw)
top->focus = top;
if (bw->current_content != NULL &&
top->cur_sel == content_get_selection(
bw->current_content)) {
browser_window_set_selection(top, NULL);
if (top->selection.bw == bw) {
browser_window_set_selection(top, false, false);
}
}
@ -2734,7 +2775,18 @@ void browser_window_mouse_click(struct browser_window *bw,
switch (content_get_type(c)) {
case CONTENT_HTML:
case CONTENT_TEXTPLAIN:
{
/* Give bw focus */
struct browser_window *root_bw = browser_window_get_root(bw);
if (bw != root_bw->focus) {
browser_window_remove_caret(bw, false);
browser_window_set_selection(bw, false, true);
root_bw->focus = bw;
}
/* Pass mouse action to content */
content_mouse_action(c, bw, mouse, x, y);
}
break;
default:
if (mouse & BROWSER_MOUSE_MOD_2) {

View File

@ -40,14 +40,6 @@ struct history;
struct selection;
struct fetch_multipart_data;
typedef bool (*browser_caret_callback)(struct browser_window *bw, uint32_t key,
void *p1, void *p2);
typedef void (*browser_move_callback)(struct browser_window *bw,
void *p1, void *p2);
typedef bool (*browser_paste_callback)(struct browser_window *bw,
const char *utf8, unsigned utf8_len, bool last,
void *p1, void *p2);
typedef enum {
DRAGGING_NONE,
@ -60,6 +52,13 @@ typedef enum {
DRAGGING_OTHER
} browser_drag_type;
typedef enum {
BW_EDITOR_NONE = 0, /**< No selection, no editing */
BW_EDITOR_CAN_COPY = (1 << 0), /**< Have selection */
BW_EDITOR_CAN_CUT = (1 << 1), /**< Selection not read-only */
BW_EDITOR_CAN_PASTE = (1 << 2) /**< Can paste, input */
} browser_editor_flags;
extern bool browser_reformat_pending;
/** flags to browser window go */
@ -206,15 +205,9 @@ bool browser_window_stop_available(struct browser_window *bw);
/* In desktop/textinput.c */
void browser_window_place_caret(struct browser_window *bw,
int x, int y, int height,
browser_caret_callback caret_cb,
browser_paste_callback paste_cb,
browser_move_callback move_cb,
void *p1, void *p2);
void browser_window_remove_caret(struct browser_window *bw);
int x, int y, int height);
void browser_window_remove_caret(struct browser_window *bw, bool only_hide);
bool browser_window_key_press(struct browser_window *bw, uint32_t key);
bool browser_window_paste_text(struct browser_window *bw, const char *utf8,
unsigned utf8_len, bool last);
/**
@ -327,29 +320,22 @@ browser_drag_type browser_window_get_drag_type(struct browser_window *bw);
struct browser_window * browser_window_get_root(struct browser_window *bw);
/**
* Check whether browser window contains a selection
* Check whether browser window can accept a cut/copy/paste, or has a selection
* that could be saved.
*
* \param bw The browser window
* \return true if browser window contains a selection
* \return flags indicating editor flags
*/
bool browser_window_has_selection(struct browser_window *bw);
browser_editor_flags browser_window_get_editor_flags(struct browser_window *bw);
/**
* Set pointer to current selection. Clears any existing selection.
* Get the current selection from a root browser window, ownership passed to
* caller, who must free() it.
*
* \param bw The browser window
* \param s The new selection
* \return the selected text string, or NULL
*/
void browser_window_set_selection(struct browser_window *bw,
struct selection *s);
/**
* Get the current selection context from a root browser window
*
* \param bw The browser window
* \return the selection context, or NULL
*/
struct selection *browser_window_get_selection(struct browser_window *bw);
char * browser_window_get_selection(struct browser_window *bw);
/**

View File

@ -52,18 +52,6 @@ struct browser_window {
/** Window history structure. */
struct history *history;
/** Handler for keyboard input, or 0. */
browser_caret_callback caret_callback;
/** Handler for pasting text, or 0. */
browser_paste_callback paste_callback;
/** Handler for repositioning caret, or 0. */
browser_move_callback move_callback;
/** User parameters for caret_callback, paste_callback, and
* move_callback */
void *caret_p1;
void *caret_p2;
/** Platform specific window data. */
struct gui_window *window;
@ -158,8 +146,12 @@ struct browser_window {
/** Last time a link was followed in this window */
unsigned int last_action;
/** Current selection, or NULL if none */
struct selection *cur_sel;
/** Current selection */
struct {
struct browser_window *bw;
bool read_only;
} selection;
bool can_edit;
/** Current context for free text search, or NULL if none */
struct search_context *cur_search;

View File

@ -219,7 +219,6 @@ void browser_window_create_iframes(struct browser_window *bw,
window->no_resize = true;
window->margin_width = cur->margin_width;
window->margin_height = cur->margin_height;
window->cur_sel = bw->cur_sel;
window->scale = bw->scale;
if (cur->name) {
window->name = strdup(cur->name);
@ -338,7 +337,6 @@ void browser_window_create_frameset(struct browser_window *bw,
warn_user("NoMemory", 0);
}
window->cur_sel = bw->cur_sel;
window->scale = bw->scale;
/* linking */

View File

@ -77,9 +77,6 @@ static bool redraw_handler(const char *text, size_t length, struct box *box,
size_t whitespace_length);
static void selection_redraw(struct selection *s, unsigned start_idx,
unsigned end_idx);
static bool save_handler(const char *text, size_t length, struct box *box,
void *handle, const char *whitespace_text,
size_t whitespace_length);
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,
@ -285,10 +282,6 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
(mouse & (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2));
int pos = -1; /* 0 = inside selection, 1 = after it */
struct browser_window *top = selection_get_browser_window(s);
if (top == NULL)
return false; /* not our problem */
top = browser_window_get_root(top);
if (selection_defined(s)) {
@ -309,14 +302,12 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
}
else if (!modkeys) {
if (pos && (mouse & BROWSER_MOUSE_PRESS_1)) {
/* Clear the selection if mouse is pressed outside the selection,
* Otherwise clear on release (to allow for drags) */
browser_window_set_selection(top, s);
/* Clear the selection if mouse is pressed outside the
* selection, Otherwise clear on release (to allow for drags) */
selection_clear(s, true);
} else if (mouse & BROWSER_MOUSE_DRAG_1) {
/* start new selection drag */
browser_window_set_selection(top, s);
selection_clear(s, true);
@ -333,8 +324,6 @@ bool selection_click(struct selection *s, browser_mouse_state mouse,
if (!selection_defined(s))
return false; /* ignore Adjust drags */
browser_window_set_selection(top, s);
if (pos >= 0) {
selection_set_end(s, idx);
@ -907,7 +896,6 @@ void selection_clear(struct selection *s, bool redraw)
{
int old_start, old_end;
bool was_defined;
struct browser_window *top = selection_get_browser_window(s);
assert(s);
was_defined = selection_defined(s);
@ -918,13 +906,6 @@ void selection_clear(struct selection *s, bool redraw)
s->start_idx = 0;
s->end_idx = 0;
if (!top)
return;
top = browser_window_get_root(top);
gui_clear_selection(top->window);
if (redraw && was_defined)
selection_redraw(s, old_start, old_end);
}
@ -1109,111 +1090,6 @@ bool selection_highlighted(const struct selection *s,
}
/**
* Selection traversal handler for saving the text to a file.
*
* \param text pointer to text being added, or NULL for newline
* \param length length of text to be appended (bytes)
* \param box pointer to text box (or NULL for textplain content)
* \param handle our save_state workspace pointer
* \param whitespace_text whitespace to place before text for formatting
* may be NULL
* \param whitespace_length length of whitespace_text
* \return true iff the file writing succeeded and traversal should continue.
*/
bool save_handler(const char *text, size_t length, struct box *box,
void *handle, const char *whitespace_text,
size_t whitespace_length)
{
struct save_text_state *sv = handle;
size_t new_length;
int space = 0;
assert(sv);
if (box && (box->space > 0))
space = 1;
if (whitespace_text)
length += whitespace_length;
new_length = sv->length + whitespace_length + length + space;
if (new_length >= sv->alloc) {
size_t new_alloc = sv->alloc + (sv->alloc / 4);
char *new_block;
if (new_alloc < new_length) new_alloc = new_length;
new_block = realloc(sv->block, new_alloc);
if (!new_block) return false;
sv->block = new_block;
sv->alloc = new_alloc;
}
if (whitespace_text) {
memcpy(sv->block + sv->length, whitespace_text,
whitespace_length);
}
memcpy(sv->block + sv->length + whitespace_length, text, length);
sv->length += length;
if (space == 1)
sv->block[sv->length++] = ' ';
return true;
}
/**
* Save the given selection to a file.
*
* \param s selection object
* \param path pathname to be used
* \return true iff the save succeeded
*/
bool selection_save_text(struct selection *s, const char *path)
{
struct save_text_state sv = { NULL, 0, 0 };
utf8_convert_ret ret;
char *result;
FILE *out;
if (!selection_traverse(s, save_handler, &sv)) {
free(sv.block);
return false;
}
if (!sv.block)
return false;
ret = utf8_to_local_encoding(sv.block, sv.length, &result);
free(sv.block);
if (ret != UTF8_CONVERT_OK) {
LOG(("failed to convert to local encoding, return %d", ret));
return false;
}
out = fopen(path, "w");
if (out) {
int res = fputs(result, out);
if (res < 0) {
LOG(("Warning: writing data failed"));
}
res = fputs("\n", out);
fclose(out);
free(result);
return (res != EOF);
}
free(result);
return false;
}
/**
* Adjust the selection to reflect a change in the selected text,
* eg. editing in a text area/input field.

View File

@ -180,6 +180,7 @@ static bool textarea_select(struct textarea *ta, int c_start, int c_end,
bool force_redraw)
{
int swap;
bool pre_existing_selection = (ta->sel_start != -1);
struct textarea_msg msg;
/* Ensure start is the beginning of the selection */
@ -205,6 +206,24 @@ static bool textarea_select(struct textarea *ta, int c_start, int c_end,
ta->callback(ta->data, &msg);
if (!pre_existing_selection && ta->sel_start != -1) {
/* Didn't have a selection before, but do now */
msg.type = TEXTAREA_MSG_SELECTION_REPORT;
msg.data.selection.have_selection = true;
msg.data.selection.read_only = (ta->flags & TEXTAREA_READONLY);
ta->callback(ta->data, &msg);
if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
/* Caret hidden, and client is responsible */
msg.type = TEXTAREA_MSG_CARET_UPDATE;
msg.data.caret.type = TEXTAREA_CARET_HIDE;
ta->callback(ta->data, &msg);
}
}
return true;
}
@ -438,11 +457,12 @@ static void textarea_scrollbar_callback(void *client_data,
if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
/* Tell client where caret should be placed */
msg.ta = ta;
msg.type = TEXTAREA_MSG_MOVED_CARET;
msg.data.caret.hidden = false;
msg.data.caret.x = ta->caret_x - ta->scroll_x;
msg.data.caret.y = ta->caret_y - ta->scroll_y;
msg.data.caret.height = ta->line_height;
msg.type = TEXTAREA_MSG_CARET_UPDATE;
msg.data.caret.type = TEXTAREA_CARET_SET_POS;
msg.data.caret.pos.x = ta->caret_x - ta->scroll_x;
msg.data.caret.pos.y = ta->caret_y - ta->scroll_y;
msg.data.caret.pos.height = ta->line_height;
msg.data.caret.pos.clip = NULL;
ta->callback(ta->data, &msg);
}
@ -497,7 +517,7 @@ static bool textarea_reflow(struct textarea *ta, unsigned int start)
int avail_width;
int h_extent; /* horizontal extent */
int v_extent; /* vertical extent */
bool restart;
bool restart = false;
if (ta->lines == NULL) {
ta->lines =
@ -781,10 +801,10 @@ static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
line = y / ta->line_height;
if (line < 0)
line = 0;
if (ta->line_count - 1 < line)
line = ta->line_count - 1;
if (line < 0)
line = 0;
/* Get byte position */
nsfont.font_position_in_string(&ta->fstyle,
@ -798,9 +818,7 @@ static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
* after it. Otherwise, the caret will be placed at the start of the
* following line, which is undesirable.
*/
if (ta->flags & TEXTAREA_MULTILINE &&
ta->show->data[ta->lines[line].b_start +
ta->lines[line].b_length] > 0 &&
if (ta->flags & TEXTAREA_MULTILINE && ta->lines[line].b_length > 1 &&
bpos == (unsigned)ta->lines[line].b_length &&
ta->show->data[ta->lines[line].b_start +
ta->lines[line].b_length - 1] == ' ')
@ -838,9 +856,9 @@ static bool textarea_set_caret_xy(struct textarea *ta, int x, int y)
/**
* Insert text into the text area
* Insert text into the textarea
*
* \param ta Text area
* \param ta Textarea widget
* \param c_index 0-based character index to insert at
* \param text UTF-8 text to insert
* \param b_len Byte length of UTF-8 text
@ -908,9 +926,9 @@ static inline void textarea_char_to_byte_offset(struct textarea_utf8 *text,
/**
* Replace text in a text area
* Replace text in a textarea
*
* \param ta Text area
* \param ta Textarea widget
* \param start Start character index of replaced section (inclusive)
* \param end End character index of replaced section (exclusive)
* \param rep Replacement UTF-8 text to insert
@ -1421,11 +1439,12 @@ bool textarea_set_caret(struct textarea *ta, int caret)
if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
/* Tell client where caret should be placed */
msg.ta = ta;
msg.type = TEXTAREA_MSG_MOVED_CARET;
msg.data.caret.hidden = false;
msg.data.caret.x = x - ta->scroll_x;
msg.data.caret.y = y - ta->scroll_y;
msg.data.caret.height = ta->line_height;
msg.type = TEXTAREA_MSG_CARET_UPDATE;
msg.data.caret.type = TEXTAREA_CARET_SET_POS;
msg.data.caret.pos.x = x - ta->scroll_x;
msg.data.caret.pos.y = y - ta->scroll_y;
msg.data.caret.pos.height = ta->line_height;
msg.data.caret.pos.clip = NULL;
ta->callback(ta->data, &msg);
}
@ -1433,8 +1452,8 @@ bool textarea_set_caret(struct textarea *ta, int caret)
} else if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
/* Caret hidden, and client is responsible: tell client */
msg.ta = ta;
msg.type = TEXTAREA_MSG_MOVED_CARET;
msg.data.caret.hidden = true;
msg.type = TEXTAREA_MSG_CARET_UPDATE;
msg.data.caret.type = TEXTAREA_CARET_HIDE;
ta->callback(ta->data, &msg);
}
@ -1778,8 +1797,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start + 1;
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
} else {
if (!textarea_replace_text(ta,
caret, caret,
@ -1792,11 +1810,8 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
} else switch (key) {
case KEY_SELECT_ALL:
caret = ta->text.utf8_len;
ta->sel_start = 0;
ta->sel_end = ta->text.utf8_len;
redraw = true;
break;
textarea_select(ta, 0, ta->text.utf8_len, true);
return true;
case KEY_COPY_SELECTION:
if (ta->sel_start != -1) {
if (!textarea_replace_text(ta,
@ -1816,15 +1831,15 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start;
ta->sel_start = ta->sel_end = -1;
textarea_clear_selection(ta);
} else if (caret > 0) {
if (!textarea_replace_text(ta,
caret - 1,
caret, "", 0, false))
return false;
caret--;
redraw = true;
}
redraw = true;
break;
case KEY_CR:
case KEY_NL:
@ -1838,15 +1853,15 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start + 1;
ta->sel_start = ta->sel_end = -1;
textarea_clear_selection(ta);
} else {
if (!textarea_replace_text(ta,
caret, caret,
"\n", 1, false))
return false;
caret++;
redraw = true;
}
redraw = true;
break;
case KEY_DELETE_LINE:
if (readonly)
@ -1856,7 +1871,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start,
ta->sel_end, "", 0, false))
return false;
ta->sel_start = ta->sel_end = -1;
textarea_clear_selection(ta);
} else {
if (ta->lines[line].b_length != 0) {
/* Delete line */
@ -1877,8 +1892,8 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
false))
return false;
}
redraw = true;
}
redraw = true;
break;
case KEY_PASTE:
{
@ -1903,7 +1918,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start + clipboard_chars;
ta->sel_start = ta->sel_end = -1;
textarea_clear_selection(ta);
} else {
if (!textarea_replace_text(ta,
caret, caret,
@ -1911,8 +1926,8 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
false))
return false;
caret += clipboard_chars;
redraw = true;
}
redraw = true;
free(clipboard);
}
@ -1927,8 +1942,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start;
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
break;
case KEY_ESCAPE:
@ -1941,8 +1955,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
if (caret > 0)
caret--;
if (ta->sel_start != -1) {
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
break;
case KEY_RIGHT:
@ -1951,8 +1964,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
if (caret < ta->text.utf8_len)
caret++;
if (ta->sel_start != -1) {
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
break;
case KEY_PAGE_UP:
@ -1970,8 +1982,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
if (readonly)
break;
if (ta->sel_start != -1) {
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
if (!(ta->flags & TEXTAREA_MULTILINE))
break;
@ -2012,16 +2023,14 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
/* -1 because one line is added in KEY_DOWN */
line = ta->caret_pos.line + (ta->vis_height +
ta->line_height - 1) /
ta->line_height
- 1;
ta->line_height - 1;
}
/* fall through */
case KEY_DOWN:
if (readonly)
break;
if (ta->sel_start != -1) {
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
if (!(ta->flags & TEXTAREA_MULTILINE))
break;
@ -2065,8 +2074,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
return false;
caret = ta->sel_start;
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
} else {
if (caret < ta->text.utf8_len) {
if (!textarea_replace_text(ta,
@ -2082,8 +2090,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
caret -= ta->caret_pos.char_off;
if (ta->sel_start != -1) {
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
break;
case KEY_LINE_END:
@ -2099,8 +2106,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
- 1] == ' ')
caret--;
if (ta->sel_start != -1) {
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
break;
case KEY_TEXT_START:
@ -2108,8 +2114,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
caret = 0;
if (ta->sel_start != -1) {
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
break;
case KEY_TEXT_END:
@ -2117,8 +2122,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
caret = ta->text.utf8_len;
if (ta->sel_start != -1) {
ta->sel_start = ta->sel_end = -1;
redraw = true;
textarea_clear_selection(ta);
}
break;
case KEY_WORD_LEFT:
@ -2132,7 +2136,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start,
ta->sel_end, "", 0, false))
return false;
ta->sel_start = ta->sel_end = -1;
textarea_clear_selection(ta);
} else {
b_off = ta->lines[line].b_start;
b_len = ta->lines[line].b_length;
@ -2142,8 +2146,8 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
if (!textarea_replace_text(ta, caret,
caret + l_len, "", 0, false))
return false;
redraw = true;
}
redraw = true;
break;
case KEY_DELETE_LINE_START:
if (readonly)
@ -2153,15 +2157,15 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
ta->sel_start,
ta->sel_end, "", 0, false))
return false;
ta->sel_start = ta->sel_end = -1;
textarea_clear_selection(ta);
} else {
if (!textarea_replace_text(ta,
caret - ta->caret_pos.char_off,
caret, "", 0, false))
return false;
caret -= ta->caret_pos.char_off;
redraw = true;
}
redraw = true;
break;
default:
return false;
@ -2346,6 +2350,7 @@ bool textarea_clear_selection(struct textarea *ta)
/* No selection to clear */
return false;
/* Clear selection and redraw */
ta->sel_start = ta->sel_end = -1;
msg.ta = ta;
@ -2357,10 +2362,62 @@ bool textarea_clear_selection(struct textarea *ta)
ta->callback(ta->data, &msg);
/* No more selection */
msg.type = TEXTAREA_MSG_SELECTION_REPORT;
msg.data.selection.have_selection = false;
msg.data.selection.read_only = (ta->flags & TEXTAREA_READONLY);
ta->callback(ta->data, &msg);
if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
/* Tell client where caret should be placed */
msg.ta = ta;
msg.type = TEXTAREA_MSG_CARET_UPDATE;
msg.data.caret.type = TEXTAREA_CARET_SET_POS;
msg.data.caret.pos.x = ta->caret_x - ta->scroll_x;
msg.data.caret.pos.y = ta->caret_y - ta->scroll_y;
msg.data.caret.pos.height = ta->line_height;
msg.data.caret.pos.clip = NULL;
ta->callback(ta->data, &msg);
}
return true;
}
/* exported interface, documented in textarea.h */
char *textarea_get_selection(struct textarea *ta)
{
char *ret;
size_t b_start, b_end, b_len;
if (ta->sel_start == -1)
/* No selection get */
return NULL;
textarea_char_to_byte_offset(ta->show, ta->sel_start, ta->sel_end,
&b_start, &b_end);
b_len = b_end - b_start;
if (b_len == 0)
/* No selection get */
return NULL;
ret = malloc(b_len + 1); /* Add space for '\0' */
if (ret == NULL)
/* Can't get selection; no memory */
return NULL;
memcpy(ret, ta->show->data + b_start, b_len);
ret[b_len] = '\0';
return ret;
}
/* exported interface, documented in textarea.h */
void textarea_get_dimensions(struct textarea *ta, int *width, int *height)
{

View File

@ -45,28 +45,39 @@ typedef enum {
TEXTAREA_DRAG_NONE,
TEXTAREA_DRAG_SCROLLBAR,
TEXTAREA_DRAG_SELECTION
} textarea_drag_type;
} textarea_drag_type; /**< Textarea drag status */
typedef enum {
TEXTAREA_MSG_DRAG_REPORT, /**< Textarea drag start/end report */
TEXTAREA_MSG_SELECTION_REPORT, /**< Textarea text selection presence */
TEXTAREA_MSG_REDRAW_REQUEST, /**< Textarea redraw request */
TEXTAREA_MSG_MOVED_CARET /**< Textarea caret moved */
TEXTAREA_MSG_CARET_UPDATE /**< Textarea caret */
} textarea_msg_type;
struct textarea_msg {
struct textarea *ta;
struct textarea *ta; /**< The textarea widget */
textarea_msg_type type;
textarea_msg_type type; /**< Indicates message data type */
union {
textarea_drag_type drag;
struct rect redraw;
textarea_drag_type drag; /**< With _DRAG_REPORT */
struct {
bool hidden;
int x;
int y;
int height;
} caret;
} data;
bool have_selection; /**< Selection exists */
bool read_only; /**< Selection can't be cut */
} selection; /**< With _SELECTION_REPORT */
struct rect redraw; /**< With _REDRAW_REQUEST */
struct {
enum {
TEXTAREA_CARET_SET_POS, /**< Set coord/height */
TEXTAREA_CARET_HIDE /**< Hide */
} type;
struct {
int x; /**< Carret x-coord */
int y; /**< Carret y-coord */
int height; /**< Carret height */
struct rect *clip; /**< Carret clip rect */
} pos; /**< With _CARET_SET_POS */
} caret; /**< With _CARET_UPDATE */
} data; /**< Depends on msg type */
};
typedef struct textarea_setup {
@ -205,6 +216,14 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
*/
bool textarea_clear_selection(struct textarea *ta);
/**
* Get selected text, ownership passed to caller, which needs to free() it.
*
* \param ta Textarea widget
* \return Selected text, or NULL if none.
*/
char *textarea_get_selection(struct textarea *ta);
/**
* Gets the dimensions of a textarea
*

View File

@ -63,11 +63,7 @@
* \param p2 Callback private data pointer, passed to callback function
*/
void browser_window_place_caret(struct browser_window *bw,
int x, int y, int height,
browser_caret_callback caret_cb,
browser_paste_callback paste_cb,
browser_move_callback move_cb,
void *p1, void *p2)
int x, int y, int height)
{
struct browser_window *root_bw;
int pos_x = 0;
@ -81,14 +77,10 @@ void browser_window_place_caret(struct browser_window *bw,
y = y * bw->scale + pos_y;
gui_window_place_caret(root_bw->window, x, y, height * bw->scale);
bw->caret_callback = caret_cb;
bw->paste_callback = paste_cb;
bw->move_callback = move_cb;
bw->caret_p1 = p1;
bw->caret_p2 = p2;
/* Set focus browser window */
root_bw->focus = bw;
root_bw->can_edit = true;
}
@ -97,20 +89,19 @@ void browser_window_place_caret(struct browser_window *bw,
*
* \param bw The browser window from which to remove caret
*/
void browser_window_remove_caret(struct browser_window *bw)
void browser_window_remove_caret(struct browser_window *bw, bool only_hide)
{
struct browser_window *root_bw;
root_bw = browser_window_get_root(bw);
if (only_hide)
root_bw->can_edit = true;
else
root_bw->can_edit = false;
if (root_bw && root_bw->window)
gui_window_remove_caret(root_bw->window);
bw->caret_callback = NULL;
bw->paste_callback = NULL;
bw->move_callback = NULL;
bw->caret_p1 = NULL;
bw->caret_p2 = NULL;
}
@ -127,59 +118,12 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key)
assert(bw->window != NULL);
if (focus->caret_callback) {
/* Pass keypress onto anything that has claimed input focus */
return focus->caret_callback(focus, key,
focus->caret_p1, focus->caret_p2);
}
if (focus == NULL)
focus = bw;
/* TODO: pass these to content to deal with */
switch (key) {
case KEY_COPY_SELECTION:
selection_copy_to_clipboard(bw->cur_sel);
return true;
case KEY_CLEAR_SELECTION:
selection_clear(bw->cur_sel, true);
return true;
case KEY_SELECT_ALL:
selection_select_all(bw->cur_sel);
return true;
case KEY_ESCAPE:
if (bw->cur_sel && selection_defined(bw->cur_sel)) {
selection_clear(bw->cur_sel, true);
return true;
}
/* if there's no selection,
* leave Escape for the caller */
return false;
}
return false;
}
/**
* Paste a block of text into a browser window at the caret position.
*
* \param bw browser window
* \param utf8 pointer to block of text
* \param utf8_len length (bytes) of text block
* \param last true iff this is the last chunk (update screen too)
* \return true iff successful
*
* TODO: Remove this function.
*/
bool browser_window_paste_text(struct browser_window *bw, const char *utf8,
unsigned utf8_len, bool last)
{
if (!bw->focus || !bw->focus->paste_callback)
if (focus->current_content == NULL)
return false;
return bw->focus->paste_callback(bw->focus, utf8, utf8_len, last,
bw->focus->caret_p1, bw->focus->caret_p2);
return content_keypress(focus->current_content, key);
}

View File

@ -374,13 +374,15 @@ static guint nsgtk_scaffolding_update_edit_actions_sensitivity(
} else {
struct browser_window *bw =
nsgtk_get_browser_window(g->top_level);
has_selection = browser_window_has_selection(bw);
browser_editor_flags edit_f =
browser_window_get_editor_flags(bw);
g->buttons[COPY_BUTTON]->sensitivity = has_selection;
g->buttons[CUT_BUTTON]->sensitivity = (has_selection &&
bw->caret_callback != 0);
g->buttons[COPY_BUTTON]->sensitivity =
edit_f & BW_EDITOR_CAN_COPY;
g->buttons[CUT_BUTTON]->sensitivity =
edit_f & BW_EDITOR_CAN_CUT;
g->buttons[PASTE_BUTTON]->sensitivity =
(bw->paste_callback != 0);
edit_f & BW_EDITOR_CAN_PASTE;
}
nsgtk_scaffolding_set_sensitivity(g);
@ -1120,7 +1122,7 @@ MULTIHANDLER(selectall)
gtk_editable_select_region(GTK_EDITABLE(g->url_bar), 0, -1);
} else {
LOG(("Selecting all document text"));
selection_select_all(browser_window_get_selection(bw));
browser_window_key_press(bw, KEY_SELECT_ALL);
}
return TRUE;

View File

@ -312,7 +312,7 @@ static gboolean nsgtk_window_button_press_event(GtkWidget *widget,
break;
case 3: /* Right button, usually. Action button, context menu. */
browser_window_remove_caret(g->bw);
browser_window_remove_caret(g->bw, true);
nsgtk_scaffolding_popup_menu(g->scaffold, g->mouse.pressed_x,
g->mouse.pressed_y);
return TRUE;

View File

@ -31,14 +31,11 @@
#include "utils/log.h"
static bool box_textarea_browser_caret_callback(struct browser_window *bw,
uint32_t key, void *p1, void *p2)
bool box_textarea_keypress(html_content *html, struct box *box, uint32_t key)
{
struct box *box = p1;
struct form_control *gadget = box->gadget;
struct textarea *ta = gadget->data.text.ta;
struct form* form = box->gadget->form;
html_content *html = p2;
struct content *c = (struct content *) html;
assert(ta != NULL);
@ -104,21 +101,6 @@ static bool box_textarea_browser_caret_callback(struct browser_window *bw,
}
static void box_textarea_browser_move_callback(struct browser_window *bw,
void *p1, void *p2)
{
}
static bool box_textarea_browser_paste_callback(struct browser_window *bw,
const char *utf8, unsigned utf8_len, bool last,
void *p1, void *p2)
{
printf("AWWOOOOOGA!\n");
return true;
}
/**
* Callback for html form textareas.
*/
@ -175,23 +157,43 @@ static void box_textarea_callback(void *data, struct textarea_msg *msg)
html__redraw_a_box(html, box);
break;
case TEXTAREA_MSG_MOVED_CARET:
case TEXTAREA_MSG_SELECTION_REPORT:
if (msg->data.selection.have_selection) {
/* Textarea now has a selection */
union html_selection_owner sel_owner;
sel_owner.textarea = box;
html_set_selection(d->html, HTML_SELECTION_TEXTAREA,
sel_owner,
msg->data.selection.read_only);
} else {
/* The textarea now has no selection */
union html_selection_owner sel_owner;
sel_owner.none = true;
html_set_selection(d->html, HTML_SELECTION_NONE,
sel_owner, true);
}
break;
case TEXTAREA_MSG_CARET_UPDATE:
if (html->bw == NULL)
break;
if (msg->data.caret.hidden) {
browser_window_remove_caret(html->bw);
if (msg->data.caret.type == TEXTAREA_CARET_HIDE) {
union html_focus_owner focus_owner;
focus_owner.textarea = box;
html_set_focus(d->html, HTML_FOCUS_TEXTAREA,
focus_owner, true, 0, 0, 0, NULL);
} else {
int x, y;
box_coords(box, &x, &y);
browser_window_place_caret(html->bw,
x + msg->data.caret.x,
y + msg->data.caret.y,
msg->data.caret.height,
box_textarea_browser_caret_callback,
box_textarea_browser_paste_callback,
box_textarea_browser_move_callback,
box, html);
union html_focus_owner focus_owner;
focus_owner.textarea = box;
html_set_focus(d->html, HTML_FOCUS_TEXTAREA,
focus_owner, false,
msg->data.caret.pos.x,
msg->data.caret.pos.y,
msg->data.caret.pos.height,
msg->data.caret.pos.clip);
}
break;
}

View File

@ -41,4 +41,15 @@ struct dom_node;
bool box_textarea_create_textarea(html_content *html,
struct box *box, struct dom_node *node);
/**
* Handle form textarea keypress input
*
* \param html html content object
* \param box box with textarea widget
* \param key keypress
* \return true iff keypress handled
*/
bool box_textarea_keypress(html_content *html, struct box *box, uint32_t key);
#endif

View File

@ -346,6 +346,10 @@ html_create_html_data(html_content *c, const http_parameter *params)
c->font_func = &nsfont;
c->drag_type = HTML_DRAG_NONE;
c->drag_owner.no_owner = true;
c->selection_type = HTML_SELECTION_NONE;
c->selection_owner.none = true;
c->focus_type = HTML_FOCUS_SELF;
c->focus_owner.self = true;
c->scripts_count = 0;
c->scripts = NULL;
c->jscontext = NULL;
@ -1309,6 +1313,28 @@ html_object_callback(hlcache_handle *object,
content_broadcast(&c->base, event->type, event->data);
break;
case CONTENT_MSG_CARET:
{
union html_focus_owner focus_owner;
focus_owner.content = box;
switch (event->data.caret.type) {
case CONTENT_CARET_REMOVE:
case CONTENT_CARET_HIDE:
html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner,
true, 0, 0, 0, NULL);
break;
case CONTENT_CARET_SET_POS:
html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner,
false, event->data.caret.pos.x,
event->data.caret.pos.y,
event->data.caret.pos.height,
event->data.caret.pos.clip);
break;
}
}
break;
case CONTENT_MSG_DRAG:
{
html_drag_type drag_type = HTML_DRAG_NONE;
@ -1332,6 +1358,23 @@ html_object_callback(hlcache_handle *object,
}
break;
case CONTENT_MSG_SELECTION:
{
html_selection_type sel_type;
union html_selection_owner sel_owner;
if (event->data.selection.selection) {
sel_type = HTML_SELECTION_CONTENT;
sel_owner.content = box;
} else {
sel_type = HTML_SELECTION_NONE;
sel_owner.none = true;
}
html_set_selection(c, sel_type, sel_owner,
event->data.selection.read_only);
}
break;
default:
assert(0);
}
@ -1514,6 +1557,10 @@ html_convert_css_callback(hlcache_handle *css,
}
break;
case CONTENT_MSG_POINTER:
/* Really don't want this to continue after the switch */
return NSERROR_OK;
default:
assert(0);
}
@ -2513,8 +2560,13 @@ html_open(struct content *c,
html->bw = bw;
html->page = (html_content *) page;
html->drag_type = HTML_DRAG_NONE;
html->drag_owner.no_owner = true;
/* text selection */
selection_init(&html->sel, html->layout);
html->selection_type = HTML_SELECTION_NONE;
html->selection_owner.none = true;
for (object = html->object_list; object != NULL; object = next) {
next = object->next;
@ -2541,6 +2593,8 @@ static void html_close(struct content *c)
html_content *html = (html_content *) c;
struct content_html_object *object, *next;
selection_clear(&html->sel, false);
if (html->search != NULL)
search_destroy_context(html->search);
@ -2567,11 +2621,63 @@ static void html_close(struct content *c)
* Return an HTML content's selection context
*/
static struct selection *html_get_selection(struct content *c)
static void html_clear_selection(struct content *c)
{
html_content *html = (html_content *) c;
return &html->sel;
switch (html->selection_type) {
case HTML_SELECTION_NONE:
/* Nothing to do */
assert(html->selection_owner.none == true);
break;
case HTML_SELECTION_TEXTAREA:
textarea_clear_selection(html->selection_owner.textarea->
gadget->data.text.ta);
break;
case HTML_SELECTION_SELF:
assert(html->selection_owner.none == false);
selection_clear(&html->sel, true);
break;
case HTML_SELECTION_CONTENT:
content_clear_selection(html->selection_owner.content->object);
break;
default:
break;
}
/* There is no selection now. */
html->selection_type = HTML_SELECTION_NONE;
html->selection_owner.none = true;
}
/**
* Return an HTML content's selection context
*/
static char *html_get_selection(struct content *c)
{
html_content *html = (html_content *) c;
switch (html->selection_type) {
case HTML_SELECTION_TEXTAREA:
return textarea_get_selection(html->selection_owner.textarea->
gadget->data.text.ta);
case HTML_SELECTION_SELF:
assert(html->selection_owner.none == false);
return selection_get_copy(&html->sel);
case HTML_SELECTION_CONTENT:
return content_get_selection(
html->selection_owner.content->object);
case HTML_SELECTION_NONE:
/* Nothing to do */
assert(html->selection_owner.none == true);
break;
default:
break;
}
return NULL;
}
@ -3214,10 +3320,12 @@ static const content_handler html_content_handler = {
.stop = html_stop,
.mouse_track = html_mouse_track,
.mouse_action = html_mouse_action,
.keypress = html_keypress,
.redraw = html_redraw,
.open = html_open,
.close = html_close,
.get_selection = html_get_selection,
.clear_selection = html_clear_selection,
.get_contextual_content = html_get_contextual_content,
.scroll_at_point = html_scroll_at_point,
.drop_file_at_point = html_drop_file_at_point,

View File

@ -50,6 +50,7 @@ struct textarea;
struct scrollbar;
struct scrollbar_msg_data;
struct search_context;
struct selection;
/**
* Container for stylesheets used by an HTML document

View File

@ -223,54 +223,7 @@ static size_t html_selection_drag_end(struct html_content *html,
void html_mouse_track(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y)
{
html_content *html = (html_content*) c;
union html_drag_owner drag_owner;
if (html->drag_type == HTML_DRAG_SELECTION && !mouse) {
/* End of selection drag */
int dir = -1;
size_t idx;
if (selection_dragging_start(&html->sel))
dir = 1;
idx = html_selection_drag_end(html, mouse, x, y, dir);
if (idx != 0)
selection_track(&html->sel, mouse, idx);
drag_owner.no_owner = true;
html_set_drag_type(html, HTML_DRAG_NONE, drag_owner, NULL);
}
if (html->drag_type == HTML_DRAG_SELECTION) {
/* Selection drag */
struct box *box;
int dir = -1;
int dx, dy;
if (selection_dragging_start(&html->sel))
dir = 1;
box = box_pick_text_box(html, x, y, dir, &dx, &dy);
if (box != NULL) {
int pixel_offset;
size_t idx;
plot_font_style_t fstyle;
font_plot_style_from_css(box->style, &fstyle);
nsfont.font_position_in_string(&fstyle,
box->text, box->length,
dx, &idx, &pixel_offset);
selection_track(&html->sel, mouse,
box->byte_offset + idx);
}
} else {
html_mouse_action(c, bw, mouse, x, y);
}
html_mouse_action(c, bw, mouse, x, y);
}
@ -323,6 +276,11 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
browser_drag_type drag_type = browser_window_get_drag_type(bw);
union content_msg_data msg_data;
struct dom_node *node = NULL;
union html_drag_owner drag_owner;
union html_selection_owner sel_owner;
bool click = mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2 |
BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2 |
BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2);
if (drag_type != DRAGGING_NONE && !mouse &&
html->visible_select_menu != NULL) {
@ -349,7 +307,55 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
&width, &height);
html->visible_select_menu = NULL;
browser_window_redraw_rect(bw, box_x, box_y,
width, height);
width, height);
}
return;
}
if (html->drag_type == HTML_DRAG_SELECTION) {
/* Selection drag */
if (!mouse) {
/* End of selection drag */
int dir = -1;
size_t idx;
if (selection_dragging_start(&html->sel))
dir = 1;
idx = html_selection_drag_end(html, mouse, x, y, dir);
if (idx != 0)
selection_track(&html->sel, mouse, idx);
drag_owner.no_owner = true;
html_set_drag_type(html, HTML_DRAG_NONE,
drag_owner, NULL);
return;
}
struct box *box;
int dir = -1;
int dx, dy;
if (selection_dragging_start(&html->sel))
dir = 1;
box = box_pick_text_box(html, x, y, dir, &dx, &dy);
if (box != NULL) {
int pixel_offset;
size_t idx;
plot_font_style_t fstyle;
font_plot_style_from_css(box->style, &fstyle);
nsfont.font_position_in_string(&fstyle,
box->text, box->length,
dx, &idx, &pixel_offset);
selection_track(&html->sel, mouse,
box->byte_offset + idx);
}
return;
}
@ -413,6 +419,16 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
return;
}
if (html->drag_type == HTML_DRAG_CONTENT_SELECTION) {
box = html->drag_owner.content;
assert(box->object != NULL);
box_coords(box, &box_x, &box_y);
content_mouse_track(box->object, bw, mouse,
x - box_x, y - box_y);
return;
}
/* Content related drags handled by now */
assert(html->drag_type == HTML_DRAG_NONE);
@ -536,7 +552,8 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
/* mouse inside padding box */
if ((box->scroll_y != NULL) &&
(x > (padding_right - SCROLLBAR_WIDTH))) {
(x > (padding_right -
SCROLLBAR_WIDTH))) {
/* mouse above vertical box scroll */
scrollbar = box->scroll_y;
@ -546,7 +563,8 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
break;
} else if ((box->scroll_x != NULL) &&
(y > (padding_bottom - SCROLLBAR_WIDTH))) {
(y > (padding_bottom -
SCROLLBAR_WIDTH))) {
/* mouse above horizontal box scroll */
scrollbar = box->scroll_x;
@ -628,6 +646,15 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
pointer = get_pointer_shape(gadget_box, false);
if (click && (html->selection_type !=
HTML_SELECTION_TEXTAREA ||
html->selection_owner.textarea !=
gadget_box)) {
sel_owner.none = true;
html_set_selection(html, HTML_SELECTION_NONE,
sel_owner, true);
}
textarea_mouse_action(gadget->data.text.ta, mouse,
x - gadget_box_x, y - gadget_box_y);
break;
@ -679,6 +706,14 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
x - pos_x, y - pos_y);
}
} else if (html_object_box) {
if (click && (html->selection_type != HTML_SELECTION_CONTENT ||
html->selection_owner.content !=
html_object_box)) {
sel_owner.none = true;
html_set_selection(html, HTML_SELECTION_NONE,
sel_owner, true);
}
if (mouse & BROWSER_MOUSE_CLICK_1 ||
mouse & BROWSER_MOUSE_CLICK_2) {
content_mouse_action(html_object_box->object,
@ -738,11 +773,18 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
/* if clicking in the main page, remove the selection from any
* text areas */
if (!done) {
struct box *layout = html->layout;
if (mouse && (mouse < BROWSER_MOUSE_MOD_1) &&
selection_root(&html->sel) != layout) {
selection_init(&html->sel, layout);
if (click && html->focus_type != HTML_FOCUS_SELF) {
union html_focus_owner fo;
fo.self = true;
html_set_focus(html, HTML_FOCUS_SELF, fo,
true, 0, 0, 0, NULL);
}
if (click && html->selection_type !=
HTML_SELECTION_SELF) {
sel_owner.none = true;
html_set_selection(html, HTML_SELECTION_NONE,
sel_owner, true);
}
if (text_box) {
@ -781,8 +823,24 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
done = true;
}
} else if (mouse & BROWSER_MOUSE_PRESS_1)
} else if (mouse & BROWSER_MOUSE_PRESS_1) {
union html_selection_owner sel_owner;
sel_owner.none = true;
selection_clear(&html->sel, true);
}
if (selection_defined(&html->sel)) {
sel_owner.none = false;
html_set_selection(html, HTML_SELECTION_SELF,
sel_owner,
selection_read_only(
&html->sel));
} else if (click && html->selection_type !=
HTML_SELECTION_NONE) {
sel_owner.none = true;
html_set_selection(html, HTML_SELECTION_NONE,
sel_owner, true);
}
}
if (!done) {
@ -832,7 +890,10 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
}
if (mouse && mouse < BROWSER_MOUSE_MOD_1) {
/* ensure key presses still act on the browser window */
browser_window_remove_caret(bw);
union html_focus_owner fo;
fo.self = true;
html_set_focus(html, HTML_FOCUS_SELF, fo,
true, 0, 0, 0, NULL);
}
}
@ -875,6 +936,61 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
}
/**
* Handle keypresses.
*
* \param c content of type CONTENT_TEXTPLAIN
* \param key The UCS4 character codepoint
* \return true if key handled, false otherwise
*/
bool html_keypress(struct content *c, uint32_t key)
{
html_content *html = (html_content *) c;
struct selection *sel = &html->sel;
struct box *box;
switch (html->focus_type) {
case HTML_FOCUS_CONTENT:
box = html->focus_owner.content;
return content_keypress(box->object, key);
case HTML_FOCUS_TEXTAREA:
box = html->focus_owner.textarea;
return textarea_keypress(box->gadget->data.text.ta, key);
default:
/* Deal with it below */
break;
}
switch (key) {
case KEY_COPY_SELECTION:
selection_copy_to_clipboard(sel);
return true;
case KEY_CLEAR_SELECTION:
selection_clear(sel, true);
return true;
case KEY_SELECT_ALL:
selection_select_all(sel);
return true;
case KEY_ESCAPE:
if (selection_defined(sel)) {
selection_clear(sel, true);
return true;
}
/* if there's no selection, leave Escape for the caller */
return false;
}
return false;
}
/**
* Callback for in-page scrollbars.
*/
@ -985,6 +1101,131 @@ void html_set_drag_type(html_content *html, html_drag_type drag_type,
}
msg_data.drag.rect = rect;
/* Inform the content's drag status change */
/* Inform of the content's drag status change */
content_broadcast((struct content *)html, CONTENT_MSG_DRAG, msg_data);
}
/* Documented in html_internal.h */
void html_set_focus(html_content *html, html_focus_type focus_type,
union html_focus_owner focus_owner, bool hide_caret,
int x, int y, int height, const struct rect *clip)
{
union content_msg_data msg_data;
int x_off = 0;
int y_off = 0;
bool textarea_lost_focus = html->focus_type == HTML_FOCUS_TEXTAREA &&
focus_type != HTML_FOCUS_TEXTAREA;
assert(html != NULL);
switch (focus_type) {
case HTML_FOCUS_SELF:
assert(focus_owner.self == true);
if (html->focus_type == HTML_FOCUS_SELF)
/* Don't need to tell anyone anything */
return;
break;
case HTML_FOCUS_CONTENT:
box_coords(focus_owner.content, &x_off, &y_off);
break;
case HTML_FOCUS_TEXTAREA:
box_coords(focus_owner.textarea, &x_off, &y_off);
break;
}
html->focus_type = focus_type;
html->focus_owner = focus_owner;
if (textarea_lost_focus) {
msg_data.caret.type = CONTENT_CARET_REMOVE;
} else if (focus_type != HTML_FOCUS_SELF && hide_caret) {
msg_data.caret.type = CONTENT_CARET_HIDE;
} else {
msg_data.caret.type = CONTENT_CARET_SET_POS;
msg_data.caret.pos.x = x + x_off;
msg_data.caret.pos.y = y + y_off;
msg_data.caret.pos.height = height;
msg_data.caret.pos.clip = clip;
}
/* Inform of the content's drag status change */
content_broadcast((struct content *)html, CONTENT_MSG_CARET, msg_data);
}
/* Documented in html_internal.h */
void html_set_selection(html_content *html, html_selection_type selection_type,
union html_selection_owner selection_owner, bool read_only)
{
union content_msg_data msg_data;
struct box *box;
bool changed = false;
bool same_type = html->selection_type == selection_type;
assert(html != NULL);
if ((selection_type == HTML_SELECTION_NONE &&
html->selection_type != HTML_SELECTION_NONE) ||
(selection_type != HTML_SELECTION_NONE &&
html->selection_type == HTML_SELECTION_NONE))
/* Existance of selection has changed, and we'll need to
* inform our owner */
changed = true;
/* Clear any existing selection */
if (html->selection_type != HTML_SELECTION_NONE) {
switch (html->selection_type) {
case HTML_SELECTION_SELF:
if (same_type)
break;
selection_clear(&html->sel, true);
break;
case HTML_SELECTION_TEXTAREA:
if (same_type && html->selection_owner.textarea ==
selection_owner.textarea)
break;
box = html->selection_owner.textarea;
textarea_clear_selection(box->gadget->data.text.ta);
break;
case HTML_SELECTION_CONTENT:
if (same_type && html->selection_owner.content ==
selection_owner.content)
break;
box = html->selection_owner.content;
content_clear_selection(box->object);
break;
default:
break;
}
}
html->selection_type = selection_type;
html->selection_owner = selection_owner;
if (!changed)
/* Don't need to report lack of change to owner */
return;
/* Prepare msg */
switch (selection_type) {
case HTML_SELECTION_NONE:
assert(selection_owner.none == true);
msg_data.selection.selection = false;
break;
case HTML_SELECTION_SELF:
assert(selection_owner.none == false);
/* fall through */
case HTML_SELECTION_TEXTAREA:
case HTML_SELECTION_CONTENT:
msg_data.selection.selection = true;
break;
default:
break;
}
msg_data.selection.read_only = read_only;
/* Inform of the content's selection status change */
content_broadcast((struct content *)html, CONTENT_MSG_SELECTION,
msg_data);
}

View File

@ -43,6 +43,29 @@ union html_drag_owner {
struct box *textarea;
}; /**< For drags we don't own */
typedef enum {
HTML_SELECTION_NONE, /** No selection */
HTML_SELECTION_TEXTAREA, /** Selection in one of our textareas */
HTML_SELECTION_SELF, /** Selection in this html content */
HTML_SELECTION_CONTENT /** Selection in child content */
} html_selection_type;
union html_selection_owner {
bool none;
struct box *textarea;
struct box *content;
}; /**< For getting at selections in this content or things in this content */
typedef enum {
HTML_FOCUS_SELF, /** Focus is our own */
HTML_FOCUS_CONTENT, /** Focus belongs to child content */
HTML_FOCUS_TEXTAREA /** Focus belongs to textarea */
} html_focus_type;
union html_focus_owner {
bool self;
struct box *textarea;
struct box *content;
}; /**< For directing input */
/** Data specific to CONTENT_HTML. */
typedef struct html_content {
struct content base;
@ -114,18 +137,28 @@ typedef struct html_content {
* object within a page. */
struct html_content *page;
/* Current drag type */
/** Current drag type */
html_drag_type drag_type;
/** Widget capturing all mouse events */
union html_drag_owner drag_owner;
/** Current selection state */
html_selection_type selection_type;
/** Current selection owner */
union html_selection_owner selection_owner;
/** Current input focus target type */
html_focus_type focus_type;
/** Current input focus target */
union html_focus_owner focus_owner;
/** HTML content's own text selection object */
struct selection sel;
/** Open core-handled form SELECT menu,
* or NULL if none currently open. */
struct form_control *visible_select_menu;
/** Selection state */
struct selection sel;
/** Context for free text search, or NULL if none */
struct search_context *search;
@ -152,6 +185,34 @@ void html__redraw_a_box(html_content *html, struct box *box);
void html_set_drag_type(html_content *html, html_drag_type drag_type,
union html_drag_owner drag_owner, const struct rect *rect);
/**
* Set our selection status, and inform whatever owns the content
*
* \param html HTML content
* \param selection_type Type of selection
* \param selection_owner What owns the selection
* \param read_only True iff selection is read only
*/
void html_set_selection(html_content *html, html_selection_type selection_type,
union html_selection_owner selection_owner, bool read_only);
/**
* Set our input focus, and inform whatever owns the content
*
* \param html HTML content
* \param focus_type Type of input focus
* \param focus_owner What owns the focus
* \param hide_caret True iff caret to be hidden
* \param x Carret x-coord rel to owner
* \param y Carret y-coord rel to owner
* \param height Carret height
* \param clip Carret clip rect
*/
void html_set_focus(html_content *html, html_focus_type focus_type,
union html_focus_owner focus_owner, bool hide_caret,
int x, int y, int height, const struct rect *clip);
struct browser_window *html_get_browser_window(struct content *c);
struct search_context *html_get_search(struct content *c);
void html_set_search(struct content *c, struct search_context *s);
@ -179,6 +240,7 @@ void html_mouse_track(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
void html_mouse_action(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
bool html_keypress(struct content *c, uint32_t key);
void html_overflow_scroll_callback(void *client_data,
struct scrollbar_msg_data *scrollbar_data);

View File

@ -40,6 +40,7 @@
#include "desktop/plotters.h"
#include "desktop/search.h"
#include "desktop/selection.h"
#include "desktop/textinput.h"
#include "render/font.h"
#include "render/search.h"
#include "render/textplain.h"
@ -107,6 +108,7 @@ static void textplain_mouse_track(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
static void textplain_mouse_action(struct content *c, struct browser_window *bw,
browser_mouse_state mouse, int x, int y);
static bool textplain_keypress(struct content *c, uint32_t key);
static void textplain_reformat(struct content *c, int width, int height);
static void textplain_destroy(struct content *c);
static bool textplain_redraw(struct content *c, struct content_redraw_data *data,
@ -114,7 +116,7 @@ static bool textplain_redraw(struct content *c, struct content_redraw_data *data
static void textplain_open(struct content *c, struct browser_window *bw,
struct content *page, struct object_params *params);
void textplain_close(struct content *c);
struct selection *textplain_get_selection(struct content *c);
char *textplain_get_selection(struct content *c);
struct search_context *textplain_get_search(struct content *c);
static nserror textplain_clone(const struct content *old,
struct content **newc);
@ -139,6 +141,7 @@ static const content_handler textplain_content_handler = {
.destroy = textplain_destroy,
.mouse_track = textplain_mouse_track,
.mouse_action = textplain_mouse_action,
.keypress = textplain_keypress,
.redraw = textplain_redraw,
.open = textplain_open,
.close = textplain_close,
@ -715,6 +718,46 @@ void textplain_mouse_action(struct content *c, struct browser_window *bw,
}
/**
* Handle keypresses.
*
* \param c content of type CONTENT_TEXTPLAIN
* \param key The UCS4 character codepoint
* \return true if key handled, false otherwise
*/
bool textplain_keypress(struct content *c, uint32_t key)
{
textplain_content *text = (textplain_content *) c;
struct selection *sel = &text->sel;
switch (key) {
case KEY_COPY_SELECTION:
selection_copy_to_clipboard(sel);
return true;
case KEY_CLEAR_SELECTION:
selection_clear(sel, true);
return true;
case KEY_SELECT_ALL:
selection_select_all(sel);
return true;
case KEY_ESCAPE:
if (selection_defined(sel)) {
selection_clear(sel, true);
return true;
}
/* if there's no selection, leave Escape for the caller */
return false;
}
return false;
}
/**
* Draw a CONTENT_TEXTPLAIN using the current set of plotters (plot).
*
@ -893,11 +936,11 @@ void textplain_close(struct content *c)
* Return an textplain content's selection context
*/
struct selection *textplain_get_selection(struct content *c)
char *textplain_get_selection(struct content *c)
{
textplain_content *text = (textplain_content *) c;
return &text->sel;
return selection_get_copy(&text->sel);
}

View File

@ -79,7 +79,7 @@
static gui_save_type gui_save_current_type;
static hlcache_handle *gui_save_content = NULL;
static struct selection *gui_save_selection = NULL;
static char *gui_save_selection = NULL;
static const char *gui_save_url = NULL;
static const char *gui_save_title = NULL;
static int gui_save_filetype;
@ -246,7 +246,7 @@ void ro_gui_saveas_quit(void)
*/
void ro_gui_save_prepare(gui_save_type save_type, hlcache_handle *h,
struct selection *s, const char *url, const char *title)
char *s, const char *url, const char *title)
{
char name_buf[FILENAME_MAX];
size_t leaf_offset = 0;
@ -259,6 +259,9 @@ void ro_gui_save_prepare(gui_save_type save_type, hlcache_handle *h,
(save_type == GUI_SAVE_HISTORY_EXPORT_HTML) ||
(save_type == GUI_SAVE_TEXT_SELECTION) || h);
if (gui_save_selection == NULL)
free(gui_save_selection);
gui_save_selection = s;
gui_save_url = url;
gui_save_title = title;
@ -414,7 +417,11 @@ void gui_drag_save_selection(struct selection *s, struct gui_window *g)
return;
}
gui_save_selection = s;
if (gui_save_selection == NULL)
free(gui_save_selection);
gui_save_selection = selection_get_copy(s);
ro_gui_save_set_state(NULL, GUI_SAVE_TEXT_SELECTION, NULL,
save_leafname, LEAFNAME_MAX,
@ -890,8 +897,15 @@ bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite)
break;
case GUI_SAVE_TEXT_SELECTION:
if (!selection_save_text(gui_save_selection, path))
if (gui_save_selection == NULL)
return false;
if (!utf8_save_text(gui_save_selection, path)) {
free(gui_save_selection);
gui_save_selection = NULL;
return false;
}
free(gui_save_selection);
gui_save_selection = NULL;
xosfile_set_type(path, 0xfff);
break;

View File

@ -30,7 +30,7 @@
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 hlcache_handle *h,
struct selection *s, const char *url,
char *s, const char *url,
const char *title);
void ro_gui_save_start_drag(wimp_pointer *pointer);
void ro_gui_drag_save_link(gui_save_type save_type, const char *url,

View File

@ -145,7 +145,7 @@ static void ro_gui_window_save_toolbar_buttons(void *data, char *config);
static void ro_gui_window_update_theme(void *data, bool ok);
static bool ro_gui_window_import_text(struct gui_window *g,
const char *filename, bool toolbar);
const char *filename);
static void ro_gui_window_clone_options(struct browser_window *new_bw,
struct browser_window *old_bw);
@ -2167,11 +2167,13 @@ bool ro_gui_window_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
hlcache_handle *h = NULL;
bool export_sprite, export_draw;
os_coord pos;
browser_editor_flags editor_flags;
g = (struct gui_window *) ro_gui_wimp_event_get_user_data(w);
toolbar = g->toolbar;
bw = g->bw;
h = g->bw->current_content;
editor_flags = browser_window_get_editor_flags(bw);
/* If this is the form select menu, handle it now and then exit.
* Otherwise, carry on to the main browser window menu.
@ -2327,20 +2329,19 @@ bool ro_gui_window_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
* be selected */
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_SAVE,
!browser_window_has_selection(bw));
editor_flags & ~BW_EDITOR_CAN_COPY);
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_COPY,
!browser_window_has_selection(bw));
editor_flags & ~BW_EDITOR_CAN_COPY);
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_CUT,
!browser_window_has_selection(bw) ||
selection_read_only(browser_window_get_selection(bw)));
editor_flags & ~BW_EDITOR_CAN_CUT);
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_PASTE,
h == NULL || bw->paste_callback == NULL);
editor_flags & ~BW_EDITOR_CAN_PASTE);
ro_gui_menu_set_entry_shaded(menu, BROWSER_SELECTION_CLEAR,
!browser_window_has_selection(bw));
editor_flags & ~BW_EDITOR_CAN_COPY);
/* Navigate Submenu */
@ -2477,7 +2478,7 @@ void ro_gui_window_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
break;
case BROWSER_SELECTION_SAVE:
if (browser_window_has_selection(bw))
if (browser_window_get_editor_flags(bw) & BW_EDITOR_CAN_COPY)
ro_gui_save_prepare(GUI_SAVE_TEXT_SELECTION, NULL,
browser_window_get_selection(bw),
NULL, NULL);
@ -3722,14 +3723,15 @@ void ro_gui_window_toolbar_click(void *data,
bool ro_gui_toolbar_dataload(struct gui_window *g, wimp_message *message)
{
if (message->data.data_xfer.file_type == osfile_TYPE_TEXT &&
ro_gui_window_import_text(g, message->data.data_xfer.file_name, true)) {
ro_gui_window_import_text(g,
message->data.data_xfer.file_name)) {
os_error *error;
/* send DataLoadAck */
message->action = message_DATA_LOAD_ACK;
message->your_ref = message->my_ref;
error = xwimp_send_message(wimp_USER_MESSAGE, message, message->sender);
error = xwimp_send_message(wimp_USER_MESSAGE, message,
message->sender);
if (error) {
LOG(("xwimp_send_message: 0x%x: %s\n",
error->errnum, error->errmess));
@ -4546,14 +4548,15 @@ void ro_gui_window_update_theme(void *data, bool ok)
* \return true iff successful
*/
bool ro_gui_window_import_text(struct gui_window *g, const char *filename,
bool toolbar)
bool ro_gui_window_import_text(struct gui_window *g, const char *filename)
{
fileswitch_object_type obj_type;
os_error *error;
char *buf, *utf8_buf;
char *buf, *utf8_buf, *sp;
int size;
utf8_convert_ret ret;
const char *ep;
char *p;
error = xosfile_read_stamped(filename, &obj_type, NULL, NULL,
&size, NULL, NULL);
@ -4596,24 +4599,19 @@ bool ro_gui_window_import_text(struct gui_window *g, const char *filename,
}
size = strlen(utf8_buf);
if (toolbar) {
const char *ep = utf8_buf + size;
const char *sp;
char *p = utf8_buf;
ep = utf8_buf + size;
p = utf8_buf;
/* skip leading whitespace */
while (isspace(*p)) p++;
/* skip leading whitespace */
while (isspace(*p)) p++;
sp = p;
while (*p && *p != '\r' && *p != '\n')
p += utf8_next(p, ep - p, 0);
*p = '\0';
sp = p;
while (*p && *p != '\r' && *p != '\n')
p += utf8_next(p, ep - p, 0);
*p = '\0';
if (p > sp)
ro_gui_window_launch_url(g, sp);
}
else
browser_window_paste_text(g->bw, utf8_buf, size, true);
if (p > sp)
ro_gui_window_launch_url(g, sp);
free(buf);
free(utf8_buf);

View File

@ -474,3 +474,42 @@ utf8_convert_ret utf8_to_html(const char *string, const char *encname,
}
/**
* Save the given utf8 text to a file, converting to local encoding.
*
* \param utf8_text text to save to file
* \param path pathname to save to
* \return true iff the save succeeded
*/
bool utf8_save_text(const char *utf8_text, const char *path)
{
utf8_convert_ret ret;
char *conv;
FILE *out;
ret = utf8_to_local_encoding(utf8_text, strlen(utf8_text), &conv);
if (ret != UTF8_CONVERT_OK) {
LOG(("failed to convert to local encoding, return %d", ret));
return false;
}
out = fopen(path, "w");
if (out) {
int res = fputs(conv, out);
if (res < 0) {
LOG(("Warning: writing data failed"));
}
res = fputs("\n", out);
fclose(out);
free(conv);
return (res != EOF);
}
free(conv);
return false;
}

View File

@ -50,6 +50,8 @@ utf8_convert_ret utf8_from_enc(const char *string, const char *encname,
utf8_convert_ret utf8_to_html(const char *string, const char *encname,
size_t len, char **result);
bool utf8_save_text(const char *utf8_text, const char *path);
/* These two are platform specific */
utf8_convert_ret utf8_to_local_encoding(const char *string, size_t len,
char **result);