Index: gtk/gtk_selection.c =================================================================== --- gtk/gtk_selection.c (revision 4546) +++ gtk/gtk_selection.c (working copy) @@ -72,7 +72,11 @@ void gui_start_selection(struct gui_window *g) { - current_selection = g_string_new(NULL); + if (!current_selection) + current_selection = g_string_new(NULL); + else + g_string_set_size(current_selection, 0); + gtk_widget_grab_focus(GTK_WIDGET(g->drawing_area)); } @@ -96,6 +100,11 @@ bool gui_empty_clipboard(void) { + if (!current_selection) + current_selection = g_string_new(NULL); + else + g_string_set_size(current_selection, 0); + return true; } Index: desktop/textinput.c =================================================================== --- desktop/textinput.c (revision 4546) +++ desktop/textinput.c (working copy) @@ -62,7 +62,8 @@ const char *utf8, unsigned utf8_len, bool last, void *handle); static bool browser_window_input_paste_text(struct browser_window *bw, const char *utf8, unsigned utf8_len, bool last, void *handle); -static void browser_window_textarea_move_caret(struct browser_window *bw, void *p); +static void browser_window_textarea_move_caret(struct browser_window *bw, + void *p); static void browser_window_input_move_caret(struct browser_window *bw, void *p); static void input_update_display(struct browser_window *bw, struct box *input, unsigned box_offset, bool to_textarea, bool redraw); @@ -104,7 +105,8 @@ int w = (c->height + 7) / 8; int xc = c->x; c->defined = false; - browser_window_redraw_rect(c->bw, xc - w, c->y, 2 * w, c->height); + browser_window_redraw_rect(c->bw, + xc - w, c->y, 2 * w, c->height); } } @@ -116,7 +118,7 @@ * * \param c structure describing text caret * \param bw browser window containing caret - * \param box INLINE box containing caret + * \param box TEXT box containing caret * \param char_offset byte offset within UTF-8 representation * \param pixel_offset from left side of box */ @@ -147,26 +149,26 @@ /** * Given the x,y co-ordinates of a point within a textarea, return the - * INLINE box pointer, and the character and pixel offsets within that + * TEXT box pointer, and the character and pixel offsets within that * box at which the caret should be positioned. (eg. for mouse clicks, * drag-and-drop insertions etc) * * \param textarea the textarea being considered * \param x x ordinate of point * \param y y ordinate of point - * \param pchar_offset receives the char offset within the INLINE box - * \param ppixel_offset receives the pixel offset within the INLINE box - * \return pointer to INLINE box + * \param pchar_offset receives the char offset within the TEXT box + * \param ppixel_offset receives the pixel offset within the TEXT box + * \return pointer to TEXT box */ struct box *textarea_get_position(struct box *textarea, int x, int y, int *pchar_offset, int *ppixel_offset) { /* A textarea is an INLINE_BLOCK containing a single - * INLINE_CONTAINER, which contains the text as runs of INLINE - * separated by BR. There is at least one INLINE. The first and - * last boxes are INLINE. Consecutive BR may not be present. These - * constraints are satisfied by using a 0-length INLINE for blank + * INLINE_CONTAINER, which contains the text as runs of TEXT + * separated by BR. There is at least one TEXT. The first and + * last boxes are TEXT. Consecutive BR may not be present. These + * constraints are satisfied by using a 0-length TEXT for blank * lines. */ struct box *inline_container, *text_box; @@ -253,10 +255,10 @@ int x, int y) { /* A textarea is an INLINE_BLOCK containing a single - * INLINE_CONTAINER, which contains the text as runs of INLINE - * separated by BR. There is at least one INLINE. The first and - * last boxes are INLINE. Consecutive BR may not be present. These - * constraints are satisfied by using a 0-length INLINE for blank + * INLINE_CONTAINER, which contains the text as runs of TEXT + * separated by BR. There is at least one TEXT. The first and + * last boxes are TEXT. Consecutive BR may not be present. These + * constraints are satisfied by using a 0-length TEXT for blank * lines. */ int char_offset = 0, pixel_offset = 0; @@ -339,15 +341,20 @@ } else switch (key) { case KEY_DELETE_LEFT: - if (char_offset == 0 && !selection_exists) { + if (selection_exists) { + /* Have a selection; delete it */ + textbox_delete(bw, text_box, 0, 0); + } else if (char_offset == 0) { /* at the start of a text box */ struct box *prev; - while (text_box->prev && text_box->prev->type == BOX_BR) { + if (text_box->prev && text_box->prev->type == BOX_BR) { /* previous box is BR: remove it */ box_unlink_and_free(text_box->prev); } + /* This needs to be after the BR removal, as that may + * result in no previous box existing */ if (!text_box->prev) /* at very beginning of text area: ignore */ return true; @@ -360,7 +367,7 @@ char_offset = prev->length; /* caret at join */ if (!textbox_insert(bw, prev, prev->length, - text_box->text, text_box->length)) + text_box->text, text_box->length)) return true; box_unlink_and_free(text_box); @@ -370,19 +377,40 @@ } else { /* delete a character */ - int prev_offset = char_offset; - int new_offset = utf8_prev(text_box->text, char_offset); + size_t prev_offset = char_offset; + size_t new_offset = + utf8_prev(text_box->text, char_offset); - if(textbox_delete(bw, text_box, new_offset, + if (textbox_delete(bw, text_box, new_offset, prev_offset - new_offset)) char_offset = new_offset; } reflow = true; break; - case KEY_DELETE_LINE_END: { + case KEY_DELETE_LINE_START: + { + struct box *start_box = line_start(text_box); + + /* Clear the selection, if one exists */ + if (selection_exists) + selection_clear(bw->sel, false); + + textarea_cut(bw, start_box, 0, text_box, char_offset); + text_box = start_box; + char_offset = 0; + reflow = true; + } + break; + + case KEY_DELETE_LINE_END: + { struct box *end_box = line_end(text_box); - + + /* Clear the selection, if one exists */ + if (selection_exists) + selection_clear(bw->sel, false); + if (end_box != text_box || char_offset < text_box->length + text_box->space) { /* there's something at the end of the line to delete */ @@ -394,15 +422,20 @@ } /* no break */ case KEY_DELETE_RIGHT: /* delete to right */ - if (char_offset >= text_box->length && selection_exists) { + if (selection_exists) { + /* Delete selection */ + textbox_delete(bw, text_box, 0, 0); + } else if (char_offset >= text_box->length) { /* at the end of a text box */ struct box *next; - while (text_box->next && text_box->next->type == BOX_BR) { + if (text_box->next && text_box->next->type == BOX_BR) { /* next box is a BR: remove it */ box_unlink_and_free(text_box->next); } + /* This test is after the BR removal, as that may + * result in no subsequent box being present */ if (!text_box->next) /* at very end of text area: ignore */ return true; @@ -414,7 +447,7 @@ assert(next->text); if (!textbox_insert(bw, text_box, text_box->length, - next->text, next->length)) + next->text, next->length)) return true; box_unlink_and_free(next); @@ -424,16 +457,23 @@ } else { /* delete a character */ - int next_offset = utf8_next(text_box->text, text_box->length, - char_offset); + size_t next_offset = utf8_next(text_box->text, + text_box->length, char_offset); + textbox_delete(bw, text_box, char_offset, - next_offset - char_offset); + next_offset - char_offset); } reflow = true; break; case 10: - case 13: /* paragraph break */ + case 13: /* paragraph break */ + if (selection_exists) { + /* If we have a selection, then delete it, + * so it's replaced by the break */ + textbox_delete(bw, text_box, 0, 0); + } + new_text = textarea_insert_break(bw, text_box, char_offset); if (!new_text) return true; @@ -445,48 +485,60 @@ reflow = true; break; - case 21: { /* Ctrl + U */ + case 21: /* Ctrl + U */ + { struct box *start_box = line_start(text_box); struct box *end_box = line_end(text_box); + /* Clear the selection, if one exists */ + if (selection_exists) + selection_clear(bw->sel, false); + textarea_cut(bw, start_box, 0, end_box, end_box->length); text_box = start_box; char_offset = 0; reflow = true; } - break; + break; case 22: /* Ctrl + V */ gui_paste_from_clipboard(bw->window, - box_x + inline_container->x + text_box->x + pixel_offset, + box_x + inline_container->x + + text_box->x + pixel_offset, box_y + inline_container->y + text_box->y); /* screen updated and caret repositioned already */ return true; - case 24: { /* Ctrl + X */ + case 24: /* Ctrl + X */ + { size_t start_idx, end_idx; - struct box *start_box = selection_get_start(bw->sel, &start_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); - + 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 (selection_exists) - text_box = selection_get_end(bw->sel, &char_offset); - else if ((unsigned int) char_offset < text_box->length) + if (selection_exists) { + /* In selection, move caret to end */ + text_box = selection_get_end(bw->sel, &char_offset); + } else if (char_offset < text_box->length) { + /* Within-box movement */ char_offset = utf8_next(text_box->text, text_box->length, char_offset); - else { + } else { + /* Between-box movement */ if (!text_box->next) /* at end of text area: ignore */ return true; @@ -499,9 +551,14 @@ break; case KEY_LEFT: - if (char_offset != 0) { + if (selection_exists) { + /* In selection, move caret to start */ + text_box = selection_get_start(bw->sel, &char_offset); + } else if (char_offset > 0) { + /* Within-box movement */ char_offset = utf8_prev(text_box->text, char_offset); } else { + /* Between-box movement */ if (!text_box->prev) /* at start of text area: ignore */ return true; @@ -558,7 +615,8 @@ char_offset = text_box->length; break; - case KEY_WORD_LEFT: { + case KEY_WORD_LEFT: + { /* if there is a selection, caret should stay at beginning */ if (selection_exists) break; @@ -574,7 +632,7 @@ if (start_of_word) { /* find the preceding non-BR box */ prev = text_box->prev; - while (prev && prev->type == BOX_BR) + if (prev && prev->type == BOX_BR) prev = prev->prev; } @@ -583,18 +641,22 @@ break; } + assert(prev->type == BOX_TEXT); + text_box = prev; char_offset = prev->length; } } - break; + break; - case KEY_WORD_RIGHT: { + case KEY_WORD_RIGHT: + { /* if there is a selection, caret should move to the end */ - if (selection_exists){ + if (selection_exists) { text_box = selection_get_end(bw->sel, &char_offset); break; } + bool in_word = (char_offset < text_box->length && !isspace(text_box->text[char_offset])); @@ -603,7 +665,7 @@ struct box *next = text_box->next; /* find the next non-BR box */ - while (next && next->type == BOX_BR) + if (next && next->type == BOX_BR) next = next->next; if (!next) { @@ -612,20 +674,22 @@ break; } + assert(next->type == BOX_TEXT); + text_box = next; char_offset = 0; - if (in_word && - text_box->length > 0 && - !isspace(text_box->text[0])) { + if (in_word && text_box->length > 0 && + !isspace(text_box->text[0])) { /* just stay at the start of this box */ break; } } } - break; + break; - case KEY_PAGE_UP: { + case KEY_PAGE_UP: + { int nlines = (textarea->height / text_box->height) - 1; while (nlines-- > 0) @@ -634,31 +698,23 @@ if (char_offset > text_box->length) char_offset = text_box->length; } - break; + break; - case KEY_PAGE_DOWN: { + case KEY_PAGE_DOWN: + { int nlines = (textarea->height / text_box->height) - 1; + while (nlines-- > 0) text_box = line_below(text_box); - /* 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 */ - + /* 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 */ if (char_offset > text_box->length) char_offset = text_box->length; } - break; + break; - case KEY_DELETE_LINE_START: { - struct box *start_box = line_start(text_box); - textarea_cut(bw, start_box, 0, text_box, char_offset); - text_box = start_box; - char_offset = 0; - reflow = true; - } - break; - default: return false; } @@ -761,18 +817,23 @@ &char_offset, &pixel_offset); assert(char_offset <= text_box->length); + /* Shift the text box horizontally to ensure that the + * caret position is visible, and ideally centred */ text_box->x = 0; if ((input->width < text_box->width) && (input->width / 2 < pixel_offset)) { dx = text_box->x; + /* Move left so caret is centred */ text_box->x = input->width / 2 - pixel_offset; + /* Clamp, so text box's right hand edge coincides + * with the input's right hand edge */ if (text_box->x < input->width - text_box->width) text_box->x = input->width - text_box->width; dx -= text_box->x; } input->gadget->caret_box_offset = char_offset; - input->gadget->caret_form_offset = get_form_offset(input, - text_box, char_offset); + input->gadget->caret_form_offset = + get_form_offset(input, text_box, char_offset); input->gadget->caret_pixel_offset = pixel_offset; browser_window_place_caret(bw, @@ -805,8 +866,8 @@ struct box *input = (struct box *)p; struct box *text_box = input->children->children; size_t box_offset = input->gadget->caret_box_offset; - size_t form_offset = input->gadget->caret_form_offset = get_form_offset( - input, text_box, box_offset); + size_t form_offset = input->gadget->caret_form_offset = + get_form_offset(input, text_box, box_offset); size_t end_offset; int pixel_offset = input->gadget->caret_pixel_offset; int box_x, box_y; @@ -815,14 +876,14 @@ char utf8[6]; unsigned int utf8_len; bool to_textarea = false; - bool sel_defined = bw->sel->defined; + bool selection_exists = bw->sel->defined; selection_get_end(bw->sel, &end_offset); box_coords(input, &box_x, &box_y); /* normal character insertion */ - if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) { + if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) { /* have we exceeded max length of input? */ utf8_len = utf8_length(input->gadget->value); if (utf8_len >= input->gadget->maxlength) @@ -838,98 +899,114 @@ changed = true; } else switch (key) { - case KEY_DELETE_LEFT: { - int prev_offset, new_offset; + case KEY_DELETE_LEFT: + { + int prev_offset, new_offset; - if (box_offset <= 0 && !sel_defined) + if (selection_exists) { + textbox_delete(bw, text_box, 0, 0); + } else { + /* Can't delete left from text box start */ + if (box_offset == 0) return true; - + prev_offset = box_offset; - new_offset = utf8_prev(input->gadget->value, - box_offset); + new_offset = utf8_prev(text_box->text, box_offset); - if(textbox_delete(bw, text_box, new_offset, - prev_offset - new_offset)) + if (textbox_delete(bw, text_box, new_offset, + prev_offset - new_offset)) box_offset = new_offset; - - changed = true; } + + changed = true; + } break; - case KEY_DELETE_RIGHT: { - unsigned next_offset; + case KEY_DELETE_RIGHT: + { + unsigned next_offset; + if (selection_exists) { + textbox_delete(bw, text_box, 0, 0); + } else { + /* Can't delete right from text box end */ if (box_offset >= text_box->length) return true; - + /* Go to the next valid UTF-8 character */ - next_offset = utf8_next(text_box->text, text_box->length, - box_offset); + next_offset = utf8_next(text_box->text, + text_box->length, box_offset); textbox_delete(bw, text_box, box_offset, next_offset - box_offset); - changed = true; } + + changed = true; + } break; - case 9: { /* Tab */ - struct form_control *next_input; - /* Find next text entry field that is actually - * displayed (i.e. has an associated box) */ - for (next_input = input->gadget->next; - next_input && - ((next_input->type != GADGET_TEXTBOX && - next_input->type != GADGET_TEXTAREA && - next_input->type != GADGET_PASSWORD) || - !next_input->box); - next_input = next_input->next) - ; - if (!next_input) - return true; + case 9: /* Tab */ + { + struct form_control *next_input; + /* Find next text entry field that is actually + * displayed (i.e. has an associated box) */ + for (next_input = input->gadget->next; + next_input && + ((next_input->type != GADGET_TEXTBOX && + next_input->type != GADGET_TEXTAREA && + next_input->type != GADGET_PASSWORD) || + !next_input->box); + next_input = next_input->next) + ; + if (!next_input) + return true; - input = next_input->box; - text_box = input->children->children; - box_offset = 0; - to_textarea = next_input->type == GADGET_TEXTAREA; - } + input = next_input->box; + text_box = input->children->children; + box_offset = 0; + to_textarea = next_input->type == GADGET_TEXTAREA; + } break; case 10: case 13: /* Return/Enter hit */ + selection_clear(bw->sel, true); + if (form) browser_form_submit(bw, bw, form, 0); return true; - case 11: { /* Shift + Tab */ - struct form_control *prev_input; - /* Find previous text entry field that is actually - * displayed (i.e. has an associated box) */ - for (prev_input = input->gadget->prev; - prev_input && - ((prev_input->type != GADGET_TEXTBOX && - prev_input->type != GADGET_TEXTAREA && - prev_input->type != GADGET_PASSWORD) || - !prev_input->box); - prev_input = prev_input->prev) - ; - if (!prev_input) - return true; + case 11: /* Shift + Tab */ + { + struct form_control *prev_input; + /* Find previous text entry field that is actually + * displayed (i.e. has an associated box) */ + for (prev_input = input->gadget->prev; + prev_input && + ((prev_input->type != GADGET_TEXTBOX && + prev_input->type != GADGET_TEXTAREA && + prev_input->type != GADGET_PASSWORD) || + !prev_input->box); + prev_input = prev_input->prev) + ; + if (!prev_input) + return true; - input = prev_input->box; - text_box = input->children->children; - box_offset = 0; - to_textarea = prev_input->type == GADGET_TEXTAREA; - } + input = prev_input->box; + text_box = input->children->children; + box_offset = 0; + to_textarea = prev_input->type == GADGET_TEXTAREA; + } break; case 21: /* Ctrl + U */ - text_box->text[0] = 0; - text_box->length = 0; + /* Clear the selection, if one exists */ + if (selection_exists) + selection_clear(bw->sel, false); + + textarea_cut(bw, text_box, 0, text_box, text_box->length); box_offset = 0; - input->gadget->value[0] = 0; - input->gadget->length = 0; - form_offset = 0; changed = true; break; @@ -941,26 +1018,31 @@ /* screen updated and caret repositioned already */ return true; - case 24: { /* Crtl + X */ - size_t 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); + case 24: /* Ctrl + X */ + { + size_t 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); - text_box = start_box; - box_offset = start_idx; - changed = true; - } + 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; + box_offset = start_idx; + changed = true; } + } break; case KEY_RIGHT: - if (sel_defined){ + if (selection_exists) { box_offset = end_offset; break; } + /* Go to the next valid UTF-8 character */ box_offset = utf8_next(text_box->text, text_box->length, box_offset); @@ -968,7 +1050,7 @@ case KEY_LEFT: /* If there is a selection, caret should remain at start */ - if (sel_defined) + if (selection_exists) break; /* Go to the previous valid UTF-8 character */ @@ -983,68 +1065,51 @@ box_offset = text_box->length; break; - case KEY_WORD_LEFT: { + case KEY_WORD_LEFT: /* If there is a selection, caret should remain at start */ - if (sel_defined) + if (selection_exists) break; - - size_t nchars; - /* Gadget */ - if (word_left(input->gadget->value, &form_offset, &nchars)) { - /* Text box */ - while (box_offset > 0 && nchars-- > 0) - box_offset = utf8_prev(text_box->text, box_offset); - } else { + + if (!word_left(text_box->text, &box_offset, NULL)) box_offset = 0; - } - } - break; - case KEY_WORD_RIGHT: { - if (sel_defined){ + break; + + case KEY_WORD_RIGHT: + if (selection_exists) { box_offset = end_offset; break; - } - size_t 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; } - } - break; + if (!word_right(text_box->text, text_box->length, + &box_offset, NULL)) + box_offset = text_box->length; + + break; + case KEY_DELETE_LINE_START: - if (box_offset <= 0) + if (selection_exists) + selection_clear(bw->sel, true); + + if (box_offset == 0) return true; - /* Text box */ - textbox_delete(bw, text_box, 0, box_offset); + textarea_cut(bw, text_box, 0, text_box, 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; changed = true; break; case KEY_DELETE_LINE_END: + if (selection_exists) + selection_clear(bw->sel, true); + if (box_offset >= text_box->length) return true; - /* Text box */ - textbox_delete(bw, text_box, box_offset, text_box->length - box_offset); - /* Gadget */ - input->gadget->length = form_offset; - input->gadget->value[form_offset] = 0; + textarea_cut(bw, text_box, box_offset, + text_box, text_box->length); + changed = true; break; @@ -1053,8 +1118,7 @@ } selection_clear(bw->sel, true); - input_update_display(bw, input, box_offset, - to_textarea, changed); + input_update_display(bw, input, box_offset, to_textarea, changed); return true; } @@ -1176,13 +1240,15 @@ selection_clear(bw->sel, true); return true; } - /* if there's no selection, leave Escape for the caller */ + /* if there's no selection, + * leave Escape for the caller */ return false; } /* pass on to the appropriate field */ if (!bw->caret_callback) return false; + return bw->caret_callback(bw, key, bw->caret_p); } @@ -1202,6 +1268,7 @@ { if (!bw->paste_callback) return false; + return bw->paste_callback(bw, utf8, utf8_len, last, bw->caret_p); } @@ -1262,7 +1329,8 @@ char_offset = 0; /* handle CR/LF and LF/CR terminations */ - if ((*p == '\n' && p[1] == '\r') || (*p == '\r' && p[1] == '\n')) + if ((*p == '\n' && p[1] == '\r') || + (*p == '\r' && p[1] == '\n')) p++; utf8 = ++p; } @@ -1277,26 +1345,33 @@ /* reflow textarea preserving width and height */ textarea_reflow(bw, textarea, inline_container); /* reflowing may have broken our caret offset - this bit should hopefully continue to work if textarea_reflow - is fixed to update the caret itself */ + * this bit should hopefully continue to work if + * textarea_reflow is fixed to update the caret itself */ char_offset = textarea->gadget->caret_box_offset; text_box = textarea->gadget->caret_text_box; - while((char_offset > text_box->length+text_box->space) && (text_box->next) && (text_box->next->type == BOX_TEXT)) - { - LOG(("Caret out of range: Was %d in boxlen %d space %d",char_offset,text_box->length,text_box->space)); - char_offset -= text_box->length+text_box->space; + + while ((char_offset > text_box->length + text_box->space) && + (text_box->next) && + (text_box->next->type == BOX_TEXT)) { + LOG(("Caret out of range: Was %d in boxlen %d " + "space %d", char_offset, + text_box->length, text_box->space)); + char_offset -= text_box->length + text_box->space; text_box = text_box->next; } - /* not sure if this will happen or not... but won't stick an assert here as we can recover from it */ - if(char_offset > text_box->length) - { - LOG(("Caret moved beyond end of line: Was %d in boxlen %d",char_offset,text_box->length)); + + /* not sure if this will happen or not... + * but won't stick an assert here as we can recover from it */ + if (char_offset > text_box->length) { + LOG(("Caret moved beyond end of line: " + "Was %d in boxlen %d", char_offset, + text_box->length)); char_offset = text_box->length; } + textarea->gadget->caret_text_box = text_box; textarea->gadget->caret_box_offset = char_offset; - nsfont_width(text_box->style, text_box->text, char_offset, &pixel_offset); @@ -1320,7 +1395,6 @@ textarea); browser_redraw_box(bw->current_content, textarea); - } return success; @@ -1387,6 +1461,9 @@ break; } box_offset += nbytes; + /* Keep caret_form_offset in sync -- textbox_insert uses this + * to determine where to insert into the gadget's value */ + input->gadget->caret_form_offset += nbytes; /* handle CR/LF and LF/CR terminations */ if (*p == '\n') { @@ -1503,11 +1580,15 @@ nsfont_width(text_box->style, text_box->text, box_offset, (int *) &pixel_offset); + + /* Shift text box horizontally, so caret is visible */ dx = text_box->x; text_box->x = 0; if (input->width < text_box->width && - input->width / 2 < (int)pixel_offset) { + input->width / 2 < (int) pixel_offset) { + /* Make caret appear in centre of text input */ text_box->x = input->width / 2 - pixel_offset; + /* Clamp if we've shifted too far left */ if (text_box->x < input->width - text_box->width) text_box->x = input->width - text_box->width; } @@ -1546,7 +1627,7 @@ * * \param bw browser window * \param text_box text box - * \param char_offset offsets (bytes) at which to insert text + * \param char_offset offset (bytes) at which to insert text * \param utf8 UTF-8 text to insert * \param utf8_len length (bytes) of UTF-8 text to insert * \return true iff successful @@ -1561,8 +1642,10 @@ if (bw->sel->defined) delete_selection(bw->sel); - /* insert in form (if applicable) */ - if (input->gadget->value){ + /* insert into form gadget (text and password inputs only) */ + if (input->gadget && (input->gadget->type == GADGET_TEXTBOX || + input->gadget->type == GADGET_PASSWORD) && + input->gadget->value) { size_t form_offset = input->gadget->caret_form_offset; char *value = realloc(input->gadget->value, input->gadget->length + utf8_len + 1); @@ -1611,7 +1694,8 @@ utf8_len); text_box->length += utf8_len; - /* nothing should assume that the text is terminated, but just in case */ + /* nothing should assume that the text is terminated, + * but just in case */ text_box->text[text_box->length] = 0; text_box->width = UNKNOWN_WIDTH; @@ -1627,6 +1711,10 @@ * \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 + * \return true on success, false otherwise + * + * ::char_offset and ::utf8_len are only considered when there is no selection. + * If there is a selection, the entire selected area is deleted. */ bool textbox_delete(struct browser_window *bw, struct box *text_box, @@ -1634,13 +1722,16 @@ { unsigned next_offset = char_offset + utf8_len; struct box *form = text_box->parent->parent; - if (bw->sel->defined){ + + if (bw->sel->defined) { delete_selection(bw->sel); - return false; + return true; } - /* delete from form (if applicable) */ - if (form->gadget->value){ + /* delete from form gadget (text and password inputs only) */ + if (form->gadget && (form->gadget->type == GADGET_TEXTBOX || + form->gadget->type == GADGET_PASSWORD) && + form->gadget->value) { size_t form_offset = get_form_offset(form, text_box, char_offset); size_t next_offset = get_form_offset(form, text_box, @@ -1664,24 +1755,27 @@ char_offset = tmp; else text_box->space = false; + } else { + text_box->space = false; } - else - text_box->space = false; + text_box->length = char_offset; - } - else { + } else { memmove(text_box->text + char_offset, text_box->text + next_offset, text_box->length - next_offset); text_box->length -= utf8_len; } - /* nothing should assume that the text is terminated, but just in case */ + /* nothing should assume that the text is terminated, + * but just in case */ text_box->text[text_box->length] = 0; text_box->width = UNKNOWN_WIDTH; + return true; } + return false; } @@ -1700,14 +1794,14 @@ int offset, size_t length) { size_t text_length = b->length + b->space; + /* only remove if its not the first box */ if (offset <= 0 && length >= text_length && b->prev != NULL) { /* remove the entire box */ box_unlink_and_free(b); return true; - } - else + } else return textbox_delete(bw, b, offset, min(length, text_length - offset)); } @@ -1728,6 +1822,8 @@ struct box *next; size_t sel_len = s->end_idx - s->start_idx; int beginning = 0; + + /* Clear selection so that deletion from textboxes proceeds */ selection_clear(s, true); /* handle first box */ @@ -1815,8 +1911,8 @@ * \param char_offset offset (in bytes) at which text box is to be split */ -struct box *textarea_insert_break(struct browser_window *bw, struct box *text_box, - size_t char_offset) +struct box *textarea_insert_break(struct browser_window *bw, + struct box *text_box, size_t char_offset) { struct box *new_br, *new_text; char *text = talloc_array(bw->current_content, char, @@ -1859,6 +1955,7 @@ * \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 + * \return true iff successful */ bool textarea_cut(struct browser_window *bw, @@ -1882,8 +1979,7 @@ return false; } box_unlink_and_free(box); - } - else { + } 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)) { @@ -1897,10 +1993,10 @@ gui_commit_clipboard(); return false; } + } else { + textbox_delete(bw, box, start_idx, + (box->length + box->space) - start_idx); } - else - textbox_delete(bw, box, start_idx, - (box->length + box->space) - start_idx); } del = true; @@ -1910,17 +2006,19 @@ /* and the last box */ if (box) { - if (gui_add_to_clipboard(box->text + start_idx, end_idx - start_idx, - end_idx > box->length)) { + if (gui_add_to_clipboard(box->text + start_idx, + end_idx - start_idx, end_idx > box->length)) { if (del) { - if (!delete_handler(bw, box, start_idx, end_idx - start_idx)) + if (!delete_handler(bw, box, start_idx, + end_idx - start_idx)) success = false; + } else { + textbox_delete(bw, box, start_idx, + end_idx - start_idx); } - else - textbox_delete(bw, box, start_idx, end_idx - start_idx); + } else { + success = false; } - else - success = false; } return gui_commit_clipboard() ? success : false; @@ -1966,12 +2064,14 @@ bool success = false; size_t nchars = 0; + /* Skip any spaces immediately prior to the offset */ while (offset > 0) { offset = utf8_prev(text, offset); nchars++; if (!isspace(text[offset])) break; } + /* Now skip all non-space characters */ while (offset > 0) { size_t prev = utf8_prev(text, offset); success = true; @@ -2004,12 +2104,14 @@ bool success = false; size_t nchars = 0; + /* Skip all non-space characters after the offset */ while (offset < len) { if (isspace(text[offset])) break; offset = utf8_next(text, len, offset); nchars++; } + /* Now skip all space characters */ while (offset < len) { offset = utf8_next(text, len, offset); nchars++; @@ -2033,30 +2135,44 @@ bool ensure_caret_visible(struct box *textarea) { - int cx,cy; - int scrollx,scrolly; + int cx, cy; + int scrollx, scrolly; + + assert(textarea->gadget); + scrollx = textarea->scroll_x; scrolly = textarea->scroll_y; - assert(textarea->gadget); + /* Calculate the caret coordinates */ - cx = textarea->gadget->caret_pixel_offset+textarea->gadget->caret_text_box->x; + cx = textarea->gadget->caret_pixel_offset + + textarea->gadget->caret_text_box->x; cy = textarea->gadget->caret_text_box->y; + /* Ensure they are visible */ - if (!box_hscrollbar_present(textarea)) + if (!box_hscrollbar_present(textarea)) { scrollx = 0; - else if (cx-textarea->scroll_x < 0) + } else if (cx-textarea->scroll_x < 0) { scrollx = cx; - else if (cx > textarea->scroll_x+textarea->width) - scrollx = cx-textarea->width; - if (!box_vscrollbar_present(textarea)) + } else if (cx > textarea->scroll_x + textarea->width) { + scrollx = cx - textarea->width; + } + + if (!box_vscrollbar_present(textarea)) { scrolly = 0; - else if (cy-textarea->scroll_y < 0) + } else if (cy - textarea->scroll_y < 0) { scrolly = cy; - else if (cy+textarea->gadget->caret_text_box->height > textarea->scroll_y+textarea->height) - scrolly = (cy+textarea->gadget->caret_text_box->height)-textarea->height; + } else if (cy + textarea->gadget->caret_text_box->height > + textarea->scroll_y + textarea->height) { + scrolly = (cy + textarea->gadget->caret_text_box->height) - + textarea->height; + } + if ((scrollx == textarea->scroll_x) && (scrolly == textarea->scroll_y)) return false; + textarea->scroll_x = scrollx; textarea->scroll_y = scrolly; + return true; } + Index: desktop/selection.c =================================================================== --- desktop/selection.c (revision 4546) +++ desktop/selection.c (working copy) @@ -50,7 +50,9 @@ */ #define IS_INPUT(box) ((box) && (box)->gadget && \ - ((box)->gadget->type == GADGET_TEXTAREA || (box)->gadget->type == GADGET_TEXTBOX)) + ((box)->gadget->type == GADGET_TEXTAREA || \ + (box)->gadget->type == GADGET_TEXTBOX || \ + (box)->gadget->type == GADGET_PASSWORD)) /** check whether the given text box is in the same number space as the current selection; number spaces are identified by their uppermost nybble */