diff --git a/desktop/browser.c b/desktop/browser.c index f258d252e..86c1cac45 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -45,6 +45,13 @@ static int browser_window_gadget_click(struct browser_window* bw, unsigned long static void browser_form_submit(struct browser_window *bw, struct form *form); static char* browser_form_construct_get(struct page_elements *elements, struct formsubmit* fs); static void browser_form_get_append(char **s, int *length, char sep, char *name, char *value); +static void browser_window_textarea_click(struct browser_window* bw, + unsigned long actual_x, unsigned long actual_y, + unsigned long x, unsigned long y, + struct box *box); +static void browser_window_textarea_callback(struct browser_window *bw, char key, void *p); +static void browser_window_place_caret(struct browser_window *bw, int x, int y, + int height, void (*callback)(struct browser_window *bw, char key, void *p), void *p); void browser_window_start_throbber(struct browser_window* bw) @@ -167,6 +174,7 @@ struct browser_window* create_browser_window(int flags, int width, int height) bw->history = NULL; bw->url = NULL; + bw->caret_callback = 0; bw->window = gui_create_browser_window(bw); @@ -323,6 +331,7 @@ void browser_window_callback(content_msg msg, struct content *c, } bw->current_content = c; bw->loading_content = 0; + bw->caret_callback = 0; } gui_window_set_redraw_safety(bw->window, previous_safety); if (bw->current_content->status == CONTENT_STATUS_DONE) { @@ -493,7 +502,7 @@ int browser_window_gadget_click(struct browser_window* bw, unsigned long click_x for (i = found - 1; i >= 0; i--) { - if (click_boxes[i].box->type == BOX_INLINE && click_boxes[i].box->gadget != 0) + if (click_boxes[i].box->gadget) { struct gui_gadget* g = click_boxes[i].box->gadget; @@ -522,7 +531,12 @@ int browser_window_gadget_click(struct browser_window* bw, unsigned long click_x gui_redraw_gadget(bw,g); break; case GADGET_TEXTAREA: - gui_edit_textarea(bw, g); + browser_window_textarea_click(bw, + click_boxes[i].actual_x, + click_boxes[i].actual_y, + click_x - click_boxes[i].actual_x, + click_y - click_boxes[i].actual_y, + click_boxes[i].box); break; case GADGET_TEXTBOX: gui_edit_textbox(bw, g); @@ -548,6 +562,156 @@ int browser_window_gadget_click(struct browser_window* bw, unsigned long click_x return 0; } + +/** + * Handle clicks in a text area by placing the caret. + */ + +void browser_window_textarea_click(struct browser_window* bw, + unsigned long actual_x, unsigned long actual_y, + unsigned long x, unsigned long y, + struct box *textarea) +{ + /* a textarea contains one or more inline containers, which contain + * the formatted paragraphs of text as inline boxes */ + + int char_offset, pixel_offset; + struct box *inline_container, *text_box; + + for (inline_container = textarea->children; + inline_container && inline_container->y + inline_container->height < y; + inline_container = inline_container->next) + ; + if (!inline_container) { + /* below the bottom of the textarea: place caret at end */ + inline_container = textarea->last; + text_box = inline_container->last; + assert(text_box->type == BOX_INLINE); + assert(text_box->text && text_box->font); + font_position_in_string(text_box->text, text_box->font, + text_box->length, textarea->width, + &char_offset, &pixel_offset); + } else { + /* find the relevant text box */ + y -= inline_container->y; + for (text_box = inline_container->children; + text_box && (text_box->y + text_box->height < y || + text_box->x + text_box->width < x); + text_box = text_box->next) + ; + if (!text_box) { + /* past last text box */ + text_box = inline_container->last; + assert(text_box->type == BOX_INLINE); + assert(text_box->text && text_box->font); + font_position_in_string(text_box->text, text_box->font, + text_box->length, textarea->width, + &char_offset, &pixel_offset); + } else { + /* in a text box */ + assert(text_box->type == BOX_INLINE); + assert(text_box->text && text_box->font); + font_position_in_string(text_box->text, text_box->font, + text_box->length, x - text_box->x, + &char_offset, &pixel_offset); + } + } + textarea->gadget->data.textarea.caret_inline_container = inline_container; + textarea->gadget->data.textarea.caret_text_box = text_box; + textarea->gadget->data.textarea.caret_char_offset = char_offset; + browser_window_place_caret(bw, actual_x + text_box->x + pixel_offset, + actual_y + inline_container->y + text_box->y, + text_box->height, + browser_window_textarea_callback, textarea); +} + + +/** + * Key press callback for text areas. + */ + +void browser_window_textarea_callback(struct browser_window *bw, char key, void *p) +{ + struct box *textarea = p; + struct box *inline_container = textarea->gadget->data.textarea.caret_inline_container; + struct box *text_box = textarea->gadget->data.textarea.caret_text_box; + int char_offset = textarea->gadget->data.textarea.caret_char_offset; + int pixel_offset; + unsigned long actual_x, actual_y; + unsigned long width, height; + + box_coords(textarea, &actual_x, &actual_y); + + if (32 <= key && key != 127) { + /* normal character insertion */ + text_box->text = xrealloc(text_box->text, text_box->length + 2); + memmove(text_box->text + char_offset + 1, + text_box->text + char_offset, + text_box->length - char_offset); + text_box->text[char_offset] = key; + text_box->length++; + text_box->text[text_box->length] = 0; + text_box->width = UNKNOWN_WIDTH; + char_offset++; + } else { + return; + } + + /* reflow textarea preserving width and height */ + width = textarea->width; + height = textarea->height; + layout_block(textarea, textarea->parent->width, textarea, 0, 0); + textarea->width = width; + textarea->height = height; + + if (text_box->length < char_offset) { + /* the text box has been split and the caret is in the second part */ + char_offset -= text_box->length; + text_box = text_box->next; + } + + pixel_offset = font_width(text_box->font, text_box->text, char_offset); + + textarea->gadget->data.textarea.caret_text_box = text_box; + textarea->gadget->data.textarea.caret_char_offset = char_offset; + browser_window_place_caret(bw, actual_x + text_box->x + pixel_offset, + actual_y + inline_container->y + text_box->y, + text_box->height, + browser_window_textarea_callback, textarea); + + gui_window_redraw(bw->window, + actual_x, + actual_y + inline_container->y, + actual_x + width, + actual_y + height); +} + + +/** + * Position the caret and assign a callback for key presses. + */ + +void browser_window_place_caret(struct browser_window *bw, int x, int y, + int height, void (*callback)(struct browser_window *bw, char key, void *p), void *p) +{ + gui_window_place_caret(bw->window, x, y, height); + bw->caret_callback = callback; + bw->caret_p = p; +} + + +/** + * Handle key presses in a browser window. + */ + +void browser_window_key_press(struct browser_window *bw, char key) +{ + if (!bw->caret_callback) + return; + bw->caret_callback(bw, key, bw->caret_p); +} + + int browser_window_action(struct browser_window* bw, struct browser_action* act) { switch (act->type) @@ -1062,7 +1226,7 @@ char* browser_form_construct_get(struct page_elements *elements, struct formsubm opt = opt->next; } break; - case GADGET_TEXTAREA: value = elements->gadgets[i]->data.textarea.text; + case GADGET_TEXTAREA: /* TODO */ break; case GADGET_IMAGE: sprintf(elements->gadgets[i]->data.image.name, "%s.x", diff --git a/desktop/browser.h b/desktop/browser.h index fe664adec..8bd4f437b 100644 --- a/desktop/browser.h +++ b/desktop/browser.h @@ -59,6 +59,9 @@ struct browser_window gui_window* window; int throbbing; + + void (*caret_callback)(struct browser_window *bw, char key, void *p); + void *caret_p; }; @@ -116,4 +119,6 @@ void gui_redraw_gadget(struct browser_window* bw, struct gui_gadget* g); void browser_window_stop_throbber(struct browser_window* bw); void browser_window_reformat(struct browser_window* bw, int scroll_to_top); +void browser_window_key_press(struct browser_window *bw, char key); + #endif diff --git a/desktop/gui.h b/desktop/gui.h index 01f1f9fb5..c4cb60d85 100644 --- a/desktop/gui.h +++ b/desktop/gui.h @@ -60,7 +60,8 @@ void gui_window_stop_throbber(gui_window* g); void gui_gadget_combo(struct browser_window* bw, struct gui_gadget* g, unsigned long mx, unsigned long my); void gui_edit_textarea(struct browser_window* bw, struct gui_gadget* g); -void gui_edit_textbox(struct browser_window* bw, struct gui_gadget* g); void gui_edit_password(struct browser_window* bw, struct gui_gadget* g); +void gui_window_place_caret(gui_window *g, int x, int y, int height); + #endif diff --git a/render/box.c b/render/box.c index c8c435179..e5e363fbd 100644 --- a/render/box.c +++ b/render/box.c @@ -709,8 +709,7 @@ struct result box_textarea(xmlNode *n, struct status *status, /* split the content at newlines and make an inline container with an * inline box for each line */ current = content = xmlNodeGetContent(n); - current += strspn(current, "\r\n"); /* skip any initial CR, LF */ - while (*current) { + do { size_t len = strcspn(current, "\r\n"); char old = current[len]; current[len] = 0; @@ -727,7 +726,7 @@ struct result box_textarea(xmlNode *n, struct status *status, current[len] = old; current += len; current += strspn(current, "\r\n"); - } + } while (*current); xmlFree(content); if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name"))) @@ -1444,8 +1443,6 @@ void gadget_free(struct gui_gadget* g) xmlFree(g->data.checkbox.value); break; case GADGET_TEXTAREA: - if (g->data.textarea.text != 0) - xmlFree(g->data.textarea.text); break; case GADGET_TEXTBOX: gui_remove_gadget(g); diff --git a/render/box.h b/render/box.h index 2422f2032..9e2321ec2 100644 --- a/render/box.h +++ b/render/box.h @@ -39,6 +39,8 @@ struct formoption { struct formoption* next; }; +struct box; + struct gui_gadget { enum { GADGET_HIDDEN = 0, GADGET_TEXTBOX, GADGET_RADIO, GADGET_CHECKBOX, GADGET_SELECT, GADGET_TEXTAREA, GADGET_ACTIONBUTTON, @@ -86,9 +88,9 @@ struct gui_gadget { char* value; } radio; struct { - int cols; - int rows; - char* text; + struct box *caret_inline_container; + struct box *caret_text_box; + int caret_char_offset; } textarea; } data; }; diff --git a/render/layout.c b/render/layout.c index a759caf72..7ee46b898 100644 --- a/render/layout.c +++ b/render/layout.c @@ -31,8 +31,6 @@ static void layout_node(struct box * box, unsigned long width, struct box * cont, unsigned long cx, unsigned long cy); -static void layout_block(struct box * box, unsigned long width, struct box * cont, - unsigned long cx, unsigned long cy); static unsigned long layout_block_children(struct box * box, unsigned long width, struct box * cont, unsigned long cx, unsigned long cy); static void find_sides(struct box * fl, unsigned long y0, unsigned long y1, diff --git a/render/layout.h b/render/layout.h index 6e8ea6481..4a70cc7e4 100644 --- a/render/layout.h +++ b/render/layout.h @@ -13,5 +13,7 @@ */ void layout_document(struct box * box, unsigned long width); +void layout_block(struct box * box, unsigned long width, struct box * cont, + unsigned long cx, unsigned long cy); #endif diff --git a/riscos/font.c b/riscos/font.c index 493a7e507..6eaabcc97 100644 --- a/riscos/font.c +++ b/riscos/font.c @@ -61,7 +61,7 @@ unsigned long font_width(struct font_data *font, const char * text, unsigned int 0, &width, 0, 0); if (error != 0) { fprintf(stderr, "%s\n", error->errmess); - die("font_scan_string failed"); + die("font_width: font_scan_string failed"); } return width / 800; @@ -212,7 +212,7 @@ char * font_split(struct font_data *data, const char * text, unsigned int length used_width, 0, 0); if (error != 0) { fprintf(stderr, "%s\n", error->errmess); - die("font_scan_string failed"); + die("font_split: font_scan_string failed"); } *used_width = browser_x_units(*used_width / 400); diff --git a/riscos/gui.c b/riscos/gui.c index 8c9a83b30..f67ebb338 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -1368,8 +1368,13 @@ void ro_gui_keypress(wimp_key* key) { gui_window* g; - if (key == NULL) - return; + if (key->i == -1 && key->c < 256) { + g = ro_lookup_gui_from_w(key->w); + if (g) { + browser_window_key_press(g->data.browser.bw, (char) key->c); + return; + } + } g = ro_lookup_gui_toolbar_from_w(key->w); if (g != NULL) @@ -1686,20 +1691,10 @@ void gui_gadget_combo(struct browser_window* bw, struct gui_gadget* g, unsigned ro_gui_create_menu(combo_menu, pointer.pos.x - 64, pointer.pos.y, bw->window); } -void gui_edit_textarea(struct browser_window* bw, struct gui_gadget* g) +void gui_window_place_caret(gui_window *g, int x, int y, int height) { - FILE* file; - - xosfile_create_dir(".WWW", 77); - xosfile_create_dir(".WWW.NetSurf", 77); - file = fopen("/WWW/NetSurf/TextArea", "w"); - if (g->data.textarea.text != 0) - fprintf(file, "%s", g->data.textarea.text); - fprintf(stderr, "closing file.\n"); - fclose(file); - - xosfile_set_type(".WWW.NetSurf.TextArea", osfile_TYPE_TEXT); - xos_cli("filer_run .WWW.NetSurf.TextArea"); + wimp_set_caret_position(g->data.browser.window, -1, + x * 2, -(y + height) * 2, height * 2, -1); } void ro_msg_datasave(wimp_message* block) @@ -1799,10 +1794,7 @@ void ro_msg_dataload(wimp_message* block) if (click_boxes[i].box->gadget->type == GADGET_TEXTAREA && data->file_type == 0xFFF) { /* load the text in! */ - if (click_boxes[i].box->gadget->data.textarea.text != 0) - xfree(click_boxes[i].box->gadget->data.textarea.text); - click_boxes[i].box->gadget->data.textarea.text = load(data->file_name); - gui_redraw_gadget(bw, click_boxes[i].box->gadget); + /* TODO */ } } } diff --git a/riscos/htmlredraw.c b/riscos/htmlredraw.c index 4d83422c6..43455a0c5 100644 --- a/riscos/htmlredraw.c +++ b/riscos/htmlredraw.c @@ -137,19 +137,6 @@ void html_redraw_box(struct content *content, struct box * box, icon.extent.y1 = -gadget_subtract_y + y; switch (box->gadget->type) { - case GADGET_TEXTAREA: - icon.flags = wimp_ICON_TEXT | wimp_ICON_BORDER | - wimp_ICON_VCENTRED | wimp_ICON_FILLED | - wimp_ICON_INDIRECTED | - (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | - (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT); - icon.data.indirected_text.text = box->gadget->data.textarea.text; - icon.data.indirected_text.size = strlen(box->gadget->data.textarea.text); - icon.data.indirected_text.validation = validation_textarea; - LOG(("writing GADGET TEXTAREA")); - wimp_plot_icon(&icon); - break; - case GADGET_TEXTBOX: colourtrans_set_font_colours(box->font->handle, current_background_color << 8, box->style->color << 8, 14, 0, 0, 0);