static void html_box_drag_start(struct box *box, int x, int y) { int box_x, box_y; int scroll_mouse_x, scroll_mouse_y; box_coords(box, &box_x, &box_y); if (box->scroll_x != NULL) { scroll_mouse_x = x - box_x ; scroll_mouse_y = y - (box_y + box->padding[TOP] + box->height + box->padding[BOTTOM] - SCROLLBAR_WIDTH); scrollbar_start_content_drag(box->scroll_x, scroll_mouse_x, scroll_mouse_y); } else if (box->scroll_y != NULL) { scroll_mouse_x = x - (box_x + box->padding[LEFT] + box->width + box->padding[RIGHT] - SCROLLBAR_WIDTH); scroll_mouse_y = y - box_y; scrollbar_start_content_drag(box->scroll_y, scroll_mouse_x, scroll_mouse_y); } }
/** * Callback for in-page scrollbars. */ void html_overflow_scroll_callback(void *client_data, struct scrollbar_msg_data *scrollbar_data) { struct html_scrollbar_data *data = client_data; html_content *html = (html_content *)data->c; struct box *box = data->box; union content_msg_data msg_data; switch(scrollbar_data->msg) { case SCROLLBAR_MSG_MOVED: html__redraw_a_box(html, box); break; case SCROLLBAR_MSG_SCROLL_START: { struct rect rect = { .x0 = scrollbar_data->x0, .y0 = scrollbar_data->y0, .x1 = scrollbar_data->x1, .y1 = scrollbar_data->y1 }; browser_window_set_drag_type(html->bw, DRAGGING_CONTENT_SCROLLBAR, &rect); html->scrollbar = scrollbar_data->scrollbar; } break; case SCROLLBAR_MSG_SCROLL_FINISHED: html->scrollbar = NULL; browser_window_set_drag_type(html->bw, DRAGGING_NONE, NULL); msg_data.pointer = BROWSER_POINTER_AUTO; content_broadcast(data->c, CONTENT_MSG_POINTER, msg_data); break; } } /** * End overflow scroll scrollbar drags * * \param scroll scrollbar widget * \param mouse state of mouse buttons and modifier keys * \param x coordinate of mouse * \param y coordinate of mouse */ void html_overflow_scroll_drag_end(struct scrollbar *scrollbar, browser_mouse_state mouse, int x, int y) { int scroll_mouse_x, scroll_mouse_y, box_x, box_y; struct html_scrollbar_data *data = scrollbar_get_data(scrollbar); struct box *box; box = data->box; box_coords(box, &box_x, &box_y); if (scrollbar_is_horizontal(scrollbar)) { scroll_mouse_x = x - box_x; scroll_mouse_y = y - (box_y + box->padding[TOP] + box->height + box->padding[BOTTOM] - SCROLLBAR_WIDTH); scrollbar_mouse_drag_end(scrollbar, mouse, scroll_mouse_x, scroll_mouse_y); } else { scroll_mouse_x = x - (box_x + box->padding[LEFT] + box->width + box->padding[RIGHT] - SCROLLBAR_WIDTH); scroll_mouse_y = y - box_y; scrollbar_mouse_drag_end(scrollbar, mouse, scroll_mouse_x, scroll_mouse_y); } }
void html_mouse_action(struct content *c, struct browser_window *bw, browser_mouse_state mouse, int x, int y) { html_content *html = (html_content *) c; enum { ACTION_NONE, ACTION_SUBMIT, ACTION_GO } action = ACTION_NONE; const char *title = 0; nsurl *url = 0; const char *target = 0; char status_buffer[200]; const char *status = 0; browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT; bool imagemap = false; int box_x = 0, box_y = 0; int gadget_box_x = 0, gadget_box_y = 0; int html_object_pos_x = 0, html_object_pos_y = 0; int text_box_x = 0; struct box *url_box = 0; struct box *gadget_box = 0; struct box *text_box = 0; struct box *box; struct form_control *gadget = 0; hlcache_handle *object = NULL; struct box *html_object_box = NULL; struct browser_window *iframe = NULL; struct box *next_box; struct box *drag_candidate = NULL; struct scrollbar *scrollbar = NULL; plot_font_style_t fstyle; int scroll_mouse_x = 0, scroll_mouse_y = 0; int padding_left, padding_right, padding_top, padding_bottom; browser_drag_type drag_type = browser_window_get_drag_type(bw); union content_msg_data msg_data; struct dom_node *node = NULL; if (drag_type != DRAGGING_NONE && !mouse && html->visible_select_menu != NULL) { /* drag end: select menu */ form_select_mouse_drag_end(html->visible_select_menu, mouse, x, y); } if (html->visible_select_menu != NULL) { box = html->visible_select_menu->box; box_coords(box, &box_x, &box_y); box_x -= box->border[LEFT].width; box_y += box->height + box->border[BOTTOM].width + box->padding[BOTTOM] + box->padding[TOP]; status = form_select_mouse_action(html->visible_select_menu, mouse, x - box_x, y - box_y); if (status != NULL) { msg_data.explicit_status_text = status; content_broadcast(c, CONTENT_MSG_STATUS, msg_data); } else { int width, height; form_select_get_dimensions(html->visible_select_menu, &width, &height); html->visible_select_menu = NULL; browser_window_redraw_rect(bw, box_x, box_y, width, height); } return; } if (!mouse && html->scrollbar != NULL) { /* drag end: scrollbar */ html_overflow_scroll_drag_end(html->scrollbar, mouse, x, y); } if (html->scrollbar != NULL) { struct html_scrollbar_data *data = scrollbar_get_data(html->scrollbar); box = data->box; box_coords(box, &box_x, &box_y); if (scrollbar_is_horizontal(html->scrollbar)) { scroll_mouse_x = x - box_x ; scroll_mouse_y = y - (box_y + box->padding[TOP] + box->height + box->padding[BOTTOM] - SCROLLBAR_WIDTH); status = scrollbar_mouse_action(html->scrollbar, mouse, scroll_mouse_x, scroll_mouse_y); } else { scroll_mouse_x = x - (box_x + box->padding[LEFT] + box->width + box->padding[RIGHT] - SCROLLBAR_WIDTH); scroll_mouse_y = y - box_y; status = scrollbar_mouse_action(html->scrollbar, mouse, scroll_mouse_x, scroll_mouse_y); } msg_data.explicit_status_text = status; content_broadcast(c, CONTENT_MSG_STATUS, msg_data); return; } /* Content related drags handled by now */ browser_window_set_drag_type(bw, DRAGGING_NONE, NULL); /* search the box tree for a link, imagemap, form control, or * box with scrollbars */ box = html->layout; /* Consider the margins of the html page now */ box_x = box->margin[LEFT]; box_y = box->margin[TOP]; /* descend through visible boxes setting more specific values for: * box - deepest box at point * html_object_box - html object * html_object_pos_x - html object * html_object_pos_y - html object * object - non html object * iframe - iframe * url - href or imagemap * target - href or imagemap or gadget * url_box - href or imagemap * imagemap - imagemap * gadget - gadget * gadget_box - gadget * gadget_box_x - gadget * gadget_box_y - gadget * title - title * pointer * * drag_candidate - first box with scroll * padding_left - box with scroll * padding_right * padding_top * padding_bottom * scrollbar - inside padding box stops decent * scroll_mouse_x - inside padding box stops decent * scroll_mouse_y - inside padding box stops decent * * text_box - text box * text_box_x - text_box */ while ((next_box = box_at_point(box, x, y, &box_x, &box_y)) != NULL) { box = next_box; if ((box->style != NULL) && (css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN)) { continue; } if (box->node != NULL) { node = box->node; } if (box->object) { if (content_get_type(box->object) == CONTENT_HTML) { html_object_box = box; html_object_pos_x = box_x; html_object_pos_y = box_y; } else { object = box->object; } } if (box->iframe) { iframe = box->iframe; } if (box->href) { url = box->href; target = box->target; url_box = box; } if (box->usemap) { url = imagemap_get(html, box->usemap, box_x, box_y, x, y, &target); if (url) { imagemap = true; url_box = box; } } if (box->gadget) { gadget = box->gadget; gadget_box = box; gadget_box_x = box_x; gadget_box_y = box_y; if (gadget->form) target = gadget->form->target; } if (box->title) { title = box->title; } pointer = get_pointer_shape(box, false); if ((box->scroll_x != NULL) || (box->scroll_y != NULL)) { if (drag_candidate == NULL) { drag_candidate = box; } padding_left = box_x + scrollbar_get_offset(box->scroll_x); padding_right = padding_left + box->padding[LEFT] + box->width + box->padding[RIGHT]; padding_top = box_y + scrollbar_get_offset(box->scroll_y); padding_bottom = padding_top + box->padding[TOP] + box->height + box->padding[BOTTOM]; if ((x > padding_left) && (x < padding_right) && (y > padding_top) && (y < padding_bottom)) { /* mouse inside padding box */ if ((box->scroll_y != NULL) && (x > (padding_right - SCROLLBAR_WIDTH))) { /* mouse above vertical box scroll */ scrollbar = box->scroll_y; scroll_mouse_x = x - (padding_right - SCROLLBAR_WIDTH); scroll_mouse_y = y - padding_top; break; } else if ((box->scroll_x != NULL) && (y > (padding_bottom - SCROLLBAR_WIDTH))) { /* mouse above horizontal box scroll */ scrollbar = box->scroll_x; scroll_mouse_x = x - padding_left; scroll_mouse_y = y - (padding_bottom - SCROLLBAR_WIDTH); break; } } } if (box->text && !box->object) { text_box = box; text_box_x = box_x; } } /* use of box_x, box_y, or content below this point is probably a * mistake; they will refer to the last box returned by box_at_point */ if (scrollbar) { status = scrollbar_mouse_action(scrollbar, mouse, scroll_mouse_x, scroll_mouse_y); pointer = BROWSER_POINTER_DEFAULT; } else if (gadget) { switch (gadget->type) { case GADGET_SELECT: status = messages_get("FormSelect"); pointer = BROWSER_POINTER_MENU; if (mouse & BROWSER_MOUSE_CLICK_1 && nsoption_bool(core_select_menu)) { html->visible_select_menu = gadget; form_open_select_menu(c, gadget, form_select_menu_callback, c); pointer = BROWSER_POINTER_DEFAULT; } else if (mouse & BROWSER_MOUSE_CLICK_1) gui_create_form_select_menu(bw, gadget); break; case GADGET_CHECKBOX: status = messages_get("FormCheckbox"); if (mouse & BROWSER_MOUSE_CLICK_1) { gadget->selected = !gadget->selected; html__redraw_a_box(html, gadget_box); } break; case GADGET_RADIO: status = messages_get("FormRadio"); if (mouse & BROWSER_MOUSE_CLICK_1) form_radio_set(html, gadget); break; case GADGET_IMAGE: if (mouse & BROWSER_MOUSE_CLICK_1) { gadget->data.image.mx = x - gadget_box_x; gadget->data.image.my = y - gadget_box_y; } /* drop through */ case GADGET_SUBMIT: if (gadget->form) { snprintf(status_buffer, sizeof status_buffer, messages_get("FormSubmit"), gadget->form->action); status = status_buffer; pointer = get_pointer_shape(gadget_box, false); if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) action = ACTION_SUBMIT; } else { status = messages_get("FormBadSubmit"); } break; case GADGET_TEXTAREA: status = messages_get("FormTextarea"); pointer = get_pointer_shape(gadget_box, false); if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2)) { if (text_box && selection_root(&html->sel) != gadget_box) selection_init(&html->sel, gadget_box); textinput_textarea_click(c, mouse, gadget_box, gadget_box_x, gadget_box_y, x - gadget_box_x, y - gadget_box_y); } if (text_box) { int pixel_offset; size_t idx; font_plot_style_from_css(text_box->style, &fstyle); nsfont.font_position_in_string(&fstyle, text_box->text, text_box->length, x - gadget_box_x - text_box->x, &idx, &pixel_offset); selection_click(&html->sel, mouse, text_box->byte_offset + idx); if (selection_dragging(&html->sel)) { browser_window_set_drag_type(bw, DRAGGING_SELECTION, NULL); status = messages_get("Selecting"); } } else if (mouse & BROWSER_MOUSE_PRESS_1) selection_clear(&html->sel, true); break; case GADGET_TEXTBOX: case GADGET_PASSWORD: status = messages_get("FormTextbox"); pointer = get_pointer_shape(gadget_box, false); if ((mouse & BROWSER_MOUSE_PRESS_1) && !(mouse & (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2))) { textinput_input_click(c, gadget_box, gadget_box_x, gadget_box_y, x - gadget_box_x, y - gadget_box_y); } if (text_box) { int pixel_offset; size_t idx; if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) selection_init(&html->sel, gadget_box); font_plot_style_from_css(text_box->style, &fstyle); nsfont.font_position_in_string(&fstyle, text_box->text, text_box->length, x - gadget_box_x - text_box->x, &idx, &pixel_offset); selection_click(&html->sel, mouse, text_box->byte_offset + idx); if (selection_dragging(&html->sel)) browser_window_set_drag_type(bw, DRAGGING_SELECTION, NULL); } else if (mouse & BROWSER_MOUSE_PRESS_1) selection_clear(&html->sel, true); break; case GADGET_HIDDEN: /* not possible: no box generated */ break; case GADGET_RESET: status = messages_get("FormReset"); break; case GADGET_FILE: status = messages_get("FormFile"); break; case GADGET_BUTTON: /* This gadget cannot be activated */ status = messages_get("FormButton"); break; } } else if (object && (mouse & BROWSER_MOUSE_MOD_2)) { if (mouse & BROWSER_MOUSE_DRAG_2) { msg_data.dragsave.type = CONTENT_SAVE_NATIVE; msg_data.dragsave.content = object; content_broadcast(c, CONTENT_MSG_DRAGSAVE, msg_data); } else if (mouse & BROWSER_MOUSE_DRAG_1) { msg_data.dragsave.type = CONTENT_SAVE_ORIG; msg_data.dragsave.content = object; content_broadcast(c, CONTENT_MSG_DRAGSAVE, msg_data); } /* \todo should have a drag-saving object msg */ } else if (iframe) { int pos_x, pos_y; float scale = browser_window_get_scale(bw); browser_window_get_position(iframe, false, &pos_x, &pos_y); pos_x /= scale; pos_y /= scale; if (mouse & BROWSER_MOUSE_CLICK_1 || mouse & BROWSER_MOUSE_CLICK_2) { browser_window_mouse_click(iframe, mouse, x - pos_x, y - pos_y); } else { browser_window_mouse_track(iframe, mouse, x - pos_x, y - pos_y); } } else if (html_object_box) { if (mouse & BROWSER_MOUSE_CLICK_1 || mouse & BROWSER_MOUSE_CLICK_2) { content_mouse_action(html_object_box->object, bw, mouse, x - html_object_pos_x, y - html_object_pos_y); } else { content_mouse_track(html_object_box->object, bw, mouse, x - html_object_pos_x, y - html_object_pos_y); } } else if (url) { if (title) { snprintf(status_buffer, sizeof status_buffer, "%s: %s", nsurl_access(url), title); status = status_buffer; } else status = nsurl_access(url); pointer = get_pointer_shape(url_box, imagemap); if (mouse & BROWSER_MOUSE_CLICK_1 && mouse & BROWSER_MOUSE_MOD_1) { /* force download of link */ browser_window_go_post(bw, nsurl_access(url), 0, 0, false, nsurl_access(content_get_url(c)), true, true, 0); } else if (mouse & BROWSER_MOUSE_CLICK_2 && mouse & BROWSER_MOUSE_MOD_1) { msg_data.savelink.url = nsurl_access(url); msg_data.savelink.title = title; content_broadcast(c, CONTENT_MSG_SAVELINK, msg_data); } else if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) action = ACTION_GO; } else { bool done = false; /* frame resizing */ if (browser_window_frame_resize_start(bw, mouse, x, y, &pointer)) { if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { status = messages_get("FrameDrag"); } done = true; } /* 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 (text_box) { int pixel_offset; size_t idx; font_plot_style_from_css(text_box->style, &fstyle); nsfont.font_position_in_string(&fstyle, text_box->text, text_box->length, x - text_box_x, &idx, &pixel_offset); if (selection_click(&html->sel, mouse, text_box->byte_offset + idx)) { /* key presses must be directed at the * main browser window, paste text * operations ignored */ if (selection_dragging(&html->sel)) { browser_window_set_drag_type(bw, DRAGGING_SELECTION, NULL); status = messages_get( "Selecting"); } done = true; } } else if (mouse & BROWSER_MOUSE_PRESS_1) selection_clear(&html->sel, true); } if (!done) { if (title) status = title; if (mouse & BROWSER_MOUSE_DRAG_1) { if (mouse & BROWSER_MOUSE_MOD_2) { msg_data.dragsave.type = CONTENT_SAVE_COMPLETE; msg_data.dragsave.content = NULL; content_broadcast(c, CONTENT_MSG_DRAGSAVE, msg_data); } else { if (drag_candidate == NULL) { browser_window_page_drag_start( bw, x, y); } else { html_box_drag_start( drag_candidate, x, y); } pointer = BROWSER_POINTER_MOVE; } } else if (mouse & BROWSER_MOUSE_DRAG_2) { if (mouse & BROWSER_MOUSE_MOD_2) { msg_data.dragsave.type = CONTENT_SAVE_SOURCE; msg_data.dragsave.content = NULL; content_broadcast(c, CONTENT_MSG_DRAGSAVE, msg_data); } else { if (drag_candidate == NULL) { browser_window_page_drag_start( bw, x, y); } else { html_box_drag_start( drag_candidate, x, y); } pointer = BROWSER_POINTER_MOVE; } } } if (mouse && mouse < BROWSER_MOUSE_MOD_1) { /* ensure key presses still act on the browser window */ browser_window_remove_caret(bw); } } if (!iframe && !html_object_box) { msg_data.explicit_status_text = status; content_broadcast(c, CONTENT_MSG_STATUS, msg_data); msg_data.pointer = pointer; content_broadcast(c, CONTENT_MSG_POINTER, msg_data); } /* fire dom click event */ if ((mouse & BROWSER_MOUSE_CLICK_1) || (mouse & BROWSER_MOUSE_CLICK_2)) { js_fire_event(html->jscontext, "click", html->document, node); } /* deferred actions that can cause this browser_window to be destroyed * and must therefore be done after set_status/pointer */ switch (action) { case ACTION_SUBMIT: form_submit(content_get_url(c), browser_window_find_target(bw, target, mouse), gadget->form, gadget); break; case ACTION_GO: browser_window_go(browser_window_find_target(bw, target, mouse), nsurl_access(url), nsurl_access(content_get_url(c)), true); break; case ACTION_NONE: break; } }
/** * Callback for html form textareas. */ static void box_textarea_callback(void *data, struct textarea_msg *msg) { struct form_textarea_data *d = data; struct form_control *gadget = d->gadget; struct html_content *html = d->gadget->html; struct box *box = gadget->box; switch (msg->type) { case TEXTAREA_MSG_DRAG_REPORT: if (msg->data.drag == TEXTAREA_DRAG_NONE) { /* Textarea drag finished */ html_drag_type drag_type = HTML_DRAG_NONE; union html_drag_owner drag_owner; drag_owner.no_owner = true; html_set_drag_type(html, drag_type, drag_owner, NULL); } else { /* Textarea drag started */ struct rect rect = { .x0 = INT_MIN, .y0 = INT_MIN, .x1 = INT_MAX, .y1 = INT_MAX }; union html_drag_owner drag_owner; drag_owner.textarea = box; switch (msg->data.drag) { case TEXTAREA_DRAG_SCROLLBAR: html_set_drag_type(html, HTML_DRAG_TEXTAREA_SCROLLBAR, drag_owner, &rect); break; case TEXTAREA_DRAG_SELECTION: html_set_drag_type(html, HTML_DRAG_TEXTAREA_SELECTION, drag_owner, &rect); break; default: LOG("Drag type %d not handled.", msg->data.drag); /* This is a logic faliure in the * front end code so abort. */ assert(0); break; } } break; case TEXTAREA_MSG_REDRAW_REQUEST: { /* Request redraw of the required textarea rectangle */ int x, y; if (html->reflowing == true) { /* Can't redraw during layout, and it will * be redrawn after layout anyway. */ break; } box_coords(box, &x, &y); content__request_redraw((struct content *)html, x + msg->data.redraw.x0, y + msg->data.redraw.y0, msg->data.redraw.x1 - msg->data.redraw.x0, msg->data.redraw.y1 - msg->data.redraw.y0); } break; 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(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(html, HTML_SELECTION_NONE, sel_owner, true); } break; case TEXTAREA_MSG_CARET_UPDATE: if (html->bw == NULL) break; if (msg->data.caret.type == TEXTAREA_CARET_HIDE) { union html_focus_owner focus_owner; focus_owner.textarea = box; html_set_focus(html, HTML_FOCUS_TEXTAREA, focus_owner, true, 0, 0, 0, NULL); } else { union html_focus_owner focus_owner; focus_owner.textarea = box; html_set_focus(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; case TEXTAREA_MSG_TEXT_MODIFIED: form_gadget_update_value(gadget, strndup(msg->data.modified.text, msg->data.modified.len)); break; } }
/** * Search for a string in the box tree * * \param string the string to search for * \param string_len length of search string */ static void search_text(const char *string, int string_len, struct search_context *context, search_flags_t flags) { struct rect bounds; struct box *box = NULL; union content_msg_data msg_data; bool case_sensitive, forwards, showall; case_sensitive = ((flags & SEARCH_FLAG_CASE_SENSITIVE) != 0) ? true : false; forwards = ((flags & SEARCH_FLAG_FORWARDS) != 0) ? true : false; showall = ((flags & SEARCH_FLAG_SHOWALL) != 0) ? true : false; if (context->c == NULL) return; if (context->is_html == true) { html_content *html = (html_content *)context->c; box = html->layout; if (!box) return; } /* check if we need to start a new search or continue an old one */ if (context->newsearch) { bool res; if (context->string != NULL) free(context->string); context->current = NULL; free_matches(context); context->string = malloc(string_len + 1); if (context->string != NULL) { memcpy(context->string, string, string_len); context->string[string_len] = '\0'; } guit->search->hourglass(true, context->gui_p); if (context->is_html == true) { res = find_occurrences_html(string, string_len, box, case_sensitive, context); } else { res = find_occurrences_text(string, string_len, context->c, case_sensitive, context); } if (!res) { free_matches(context); guit->search->hourglass(false, context->gui_p); return; } guit->search->hourglass(false, context->gui_p); context->prev_case_sens = case_sensitive; /* new search, beginning at the top of the page */ context->current = context->found->next; context->newsearch = false; } else if (context->current != NULL) { /* continued search in the direction specified */ if (forwards) { if (context->current->next) context->current = context->current->next; } else { if (context->current->prev) context->current = context->current->prev; } } guit->search->status((context->current != NULL), context->gui_p); search_show_all(showall, context); guit->search->back_state((context->current != NULL) && (context->current->prev != NULL), context->gui_p); guit->search->forward_state((context->current != NULL) && (context->current->next != NULL), context->gui_p); if (context->current == NULL) return; if (context->is_html == true) { /* get box position and jump to it */ box_coords(context->current->start_box, &bounds.x0, &bounds.y0); /* \todo: move x0 in by correct idx */ box_coords(context->current->end_box, &bounds.x1, &bounds.y1); /* \todo: move x1 in by correct idx */ bounds.x1 += context->current->end_box->width; bounds.y1 += context->current->end_box->height; } else { textplain_coords_from_range(context->c, context->current->start_idx, context->current->end_idx, &bounds); } msg_data.scroll.area = true; msg_data.scroll.x0 = bounds.x0; msg_data.scroll.y0 = bounds.y0; msg_data.scroll.x1 = bounds.x1; msg_data.scroll.y1 = bounds.y1; content_broadcast(context->c, CONTENT_MSG_SCROLL, msg_data); }
void html_mouse_action(struct content *c, struct browser_window *bw, browser_mouse_state mouse, int x, int y) { html_content *html = (html_content *) c; enum { ACTION_NONE, ACTION_SUBMIT, ACTION_GO } action = ACTION_NONE; const char *title = 0; nsurl *url = 0; const char *target = 0; char status_buffer[200]; const char *status = 0; browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT; bool imagemap = false; int box_x = 0, box_y = 0; int gadget_box_x = 0, gadget_box_y = 0; int html_object_pos_x = 0, html_object_pos_y = 0; int text_box_x = 0; struct box *url_box = 0; struct box *gadget_box = 0; struct box *text_box = 0; struct box *box; struct form_control *gadget = 0; hlcache_handle *object = NULL; struct box *html_object_box = NULL; struct browser_window *iframe = NULL; struct box *next_box; struct box *drag_candidate = NULL; struct scrollbar *scrollbar = NULL; plot_font_style_t fstyle; int scroll_mouse_x = 0, scroll_mouse_y = 0; int padding_left, padding_right, padding_top, padding_bottom; 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) { /* drag end: select menu */ form_select_mouse_drag_end(html->visible_select_menu, mouse, x, y); } if (html->visible_select_menu != NULL) { box = html->visible_select_menu->box; box_coords(box, &box_x, &box_y); box_x -= box->border[LEFT].width; box_y += box->height + box->border[BOTTOM].width + box->padding[BOTTOM] + box->padding[TOP]; status = form_select_mouse_action(html->visible_select_menu, mouse, x - box_x, y - box_y); if (status != NULL) { msg_data.explicit_status_text = status; content_broadcast(c, CONTENT_MSG_STATUS, msg_data); } else { int width, height; form_select_get_dimensions(html->visible_select_menu, &width, &height); html->visible_select_menu = NULL; browser_window_redraw_rect(bw, box_x, box_y, width, height); } return; } if (html->drag_type == HTML_DRAG_SELECTION) { /* Selection drag */ struct box *box; int dir = -1; int dx, dy; 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; } 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; } if (html->drag_type == HTML_DRAG_SCROLLBAR) { struct scrollbar *scr = html->drag_owner.scrollbar; struct html_scrollbar_data *data = scrollbar_get_data(scr); if (!mouse) { /* drag end: scrollbar */ html_overflow_scroll_drag_end(scr, mouse, x, y); } box = data->box; box_coords(box, &box_x, &box_y); if (scrollbar_is_horizontal(scr)) { scroll_mouse_x = x - box_x ; scroll_mouse_y = y - (box_y + box->padding[TOP] + box->height + box->padding[BOTTOM] - SCROLLBAR_WIDTH); status = scrollbar_mouse_status_to_message( scrollbar_mouse_action(scr, mouse, scroll_mouse_x, scroll_mouse_y)); } else { scroll_mouse_x = x - (box_x + box->padding[LEFT] + box->width + box->padding[RIGHT] - SCROLLBAR_WIDTH); scroll_mouse_y = y - box_y; status = scrollbar_mouse_status_to_message( scrollbar_mouse_action(scr, mouse, scroll_mouse_x, scroll_mouse_y)); } msg_data.explicit_status_text = status; content_broadcast(c, CONTENT_MSG_STATUS, msg_data); return; } if (html->drag_type == HTML_DRAG_TEXTAREA_SELECTION || html->drag_type == HTML_DRAG_TEXTAREA_SCROLLBAR) { box = html->drag_owner.textarea; assert(box->gadget != NULL); assert(box->gadget->type == GADGET_TEXTAREA || box->gadget->type == GADGET_PASSWORD || box->gadget->type == GADGET_TEXTBOX); box_coords(box, &box_x, &box_y); textarea_mouse_action(box->gadget->data.text.ta, mouse, x - box_x, y - box_y); /* TODO: Set appropriate statusbar message */ return; } if (html->drag_type == HTML_DRAG_CONTENT_SELECTION || html->drag_type == HTML_DRAG_CONTENT_SCROLL) { 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; } 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); /* search the box tree for a link, imagemap, form control, or * box with scrollbars */ box = html->layout; /* Consider the margins of the html page now */ box_x = box->margin[LEFT]; box_y = box->margin[TOP]; /* descend through visible boxes setting more specific values for: * box - deepest box at point * html_object_box - html object * html_object_pos_x - html object * html_object_pos_y - html object * object - non html object * iframe - iframe * url - href or imagemap * target - href or imagemap or gadget * url_box - href or imagemap * imagemap - imagemap * gadget - gadget * gadget_box - gadget * gadget_box_x - gadget * gadget_box_y - gadget * title - title * pointer * * drag_candidate - first box with scroll * padding_left - box with scroll * padding_right * padding_top * padding_bottom * scrollbar - inside padding box stops decent * scroll_mouse_x - inside padding box stops decent * scroll_mouse_y - inside padding box stops decent * * text_box - text box * text_box_x - text_box */ while ((next_box = box_at_point(box, x, y, &box_x, &box_y)) != NULL) { box = next_box; if ((box->style != NULL) && (css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN)) { continue; } if (box->node != NULL) { node = box->node; } if (box->object) { if (content_get_type(box->object) == CONTENT_HTML) { html_object_box = box; html_object_pos_x = box_x; html_object_pos_y = box_y; } else { object = box->object; } } if (box->iframe) { iframe = box->iframe; } if (box->href) { url = box->href; target = box->target; url_box = box; } if (box->usemap) { url = imagemap_get(html, box->usemap, box_x, box_y, x, y, &target); if (url) { imagemap = true; url_box = box; } } if (box->gadget) { gadget = box->gadget; gadget_box = box; gadget_box_x = box_x; gadget_box_y = box_y; if (gadget->form) target = gadget->form->target; } if (box->title) { title = box->title; } pointer = get_pointer_shape(box, false); if ((box->scroll_x != NULL) || (box->scroll_y != NULL)) { if (drag_candidate == NULL) { drag_candidate = box; } padding_left = box_x + scrollbar_get_offset(box->scroll_x); padding_right = padding_left + box->padding[LEFT] + box->width + box->padding[RIGHT]; padding_top = box_y + scrollbar_get_offset(box->scroll_y); padding_bottom = padding_top + box->padding[TOP] + box->height + box->padding[BOTTOM]; if ((x > padding_left) && (x < padding_right) && (y > padding_top) && (y < padding_bottom)) { /* mouse inside padding box */ if ((box->scroll_y != NULL) && (x > (padding_right - SCROLLBAR_WIDTH))) { /* mouse above vertical box scroll */ scrollbar = box->scroll_y; scroll_mouse_x = x - (padding_right - SCROLLBAR_WIDTH); scroll_mouse_y = y - padding_top; break; } else if ((box->scroll_x != NULL) && (y > (padding_bottom - SCROLLBAR_WIDTH))) { /* mouse above horizontal box scroll */ scrollbar = box->scroll_x; scroll_mouse_x = x - padding_left; scroll_mouse_y = y - (padding_bottom - SCROLLBAR_WIDTH); break; } } } if (box->text && !box->object) { text_box = box; text_box_x = box_x; } } /* use of box_x, box_y, or content below this point is probably a * mistake; they will refer to the last box returned by box_at_point */ if (scrollbar) { status = scrollbar_mouse_status_to_message( scrollbar_mouse_action(scrollbar, mouse, scroll_mouse_x, scroll_mouse_y)); pointer = BROWSER_POINTER_DEFAULT; } else if (gadget) { textarea_mouse_status ta_status; switch (gadget->type) { case GADGET_SELECT: status = messages_get("FormSelect"); pointer = BROWSER_POINTER_MENU; if (mouse & BROWSER_MOUSE_CLICK_1 && nsoption_bool(core_select_menu)) { html->visible_select_menu = gadget; form_open_select_menu(c, gadget, form_select_menu_callback, c); pointer = BROWSER_POINTER_DEFAULT; } else if (mouse & BROWSER_MOUSE_CLICK_1) gui_create_form_select_menu(bw, gadget); break; case GADGET_CHECKBOX: status = messages_get("FormCheckbox"); if (mouse & BROWSER_MOUSE_CLICK_1) { gadget->selected = !gadget->selected; html__redraw_a_box(html, gadget_box); } break; case GADGET_RADIO: status = messages_get("FormRadio"); if (mouse & BROWSER_MOUSE_CLICK_1) form_radio_set(html, gadget); break; case GADGET_IMAGE: if (mouse & BROWSER_MOUSE_CLICK_1) { gadget->data.image.mx = x - gadget_box_x; gadget->data.image.my = y - gadget_box_y; } /* drop through */ case GADGET_SUBMIT: if (gadget->form) { snprintf(status_buffer, sizeof status_buffer, messages_get("FormSubmit"), gadget->form->action); status = status_buffer; pointer = get_pointer_shape(gadget_box, false); if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) action = ACTION_SUBMIT; } else { status = messages_get("FormBadSubmit"); } break; case GADGET_TEXTBOX: case GADGET_PASSWORD: case GADGET_TEXTAREA: if (gadget->type == GADGET_TEXTAREA) status = messages_get("FormTextarea"); else status = messages_get("FormTextbox"); 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); } ta_status = textarea_mouse_action(gadget->data.text.ta, mouse, x - gadget_box_x, y - gadget_box_y); if (ta_status & TEXTAREA_MOUSE_EDITOR) { pointer = get_pointer_shape(gadget_box, false); } else { pointer = BROWSER_POINTER_DEFAULT; status = scrollbar_mouse_status_to_message( ta_status >> 3); } break; case GADGET_HIDDEN: /* not possible: no box generated */ break; case GADGET_RESET: status = messages_get("FormReset"); break; case GADGET_FILE: status = messages_get("FormFile"); break; case GADGET_BUTTON: /* This gadget cannot be activated */ status = messages_get("FormButton"); break; } } else if (object && (mouse & BROWSER_MOUSE_MOD_2)) {
static nserror html_object_callback(hlcache_handle *object, const hlcache_event *event, void *pw) { struct content_html_object *o = pw; html_content *c = (html_content *) o->parent; int x, y; struct box *box; assert(c->base.status != CONTENT_STATUS_ERROR); box = o->box; if (box == NULL && event->type != CONTENT_MSG_ERROR) { return NSERROR_OK; } switch (event->type) { case CONTENT_MSG_LOADING: if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL) content_open(object, c->bw, &c->base, box->object_params); break; case CONTENT_MSG_READY: if (content_can_reformat(object)) { /* TODO: avoid knowledge of box internals here */ content_reformat(object, false, box->max_width != UNKNOWN_MAX_WIDTH ? box->width : 0, box->max_width != UNKNOWN_MAX_WIDTH ? box->height : 0); /* Adjust parent content for new object size */ html_object_done(box, object, o->background); if (c->base.status == CONTENT_STATUS_READY || c->base.status == CONTENT_STATUS_DONE) content__reformat(&c->base, false, c->base.available_width, c->base.height); } break; case CONTENT_MSG_DONE: c->base.active--; LOG("%d fetches active", c->base.active); html_object_done(box, object, o->background); if (c->base.status != CONTENT_STATUS_LOADING && box->flags & REPLACE_DIM) { union content_msg_data data; if (!box_visible(box)) break; box_coords(box, &x, &y); data.redraw.x = x + box->padding[LEFT]; data.redraw.y = y + box->padding[TOP]; data.redraw.width = box->width; data.redraw.height = box->height; data.redraw.full_redraw = true; content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); } break; case CONTENT_MSG_ERROR: hlcache_handle_release(object); o->content = NULL; if (box != NULL) { c->base.active--; LOG("%d fetches active", c->base.active); content_add_error(&c->base, "?", 0); html_object_failed(box, c, o->background); } break; case CONTENT_MSG_REDRAW: if (c->base.status != CONTENT_STATUS_LOADING) { union content_msg_data data = event->data; if (!box_visible(box)) break; box_coords(box, &x, &y); if (object == box->background) { /* Redraw request is for background */ css_fixed hpos = 0, vpos = 0; css_unit hunit = CSS_UNIT_PX; css_unit vunit = CSS_UNIT_PX; int width = box->padding[LEFT] + box->width + box->padding[RIGHT]; int height = box->padding[TOP] + box->height + box->padding[BOTTOM]; int t, h, l, w; /* Need to know background-position */ css_computed_background_position(box->style, &hpos, &hunit, &vpos, &vunit); w = content_get_width(box->background); if (hunit == CSS_UNIT_PCT) { l = (width - w) * hpos / INTTOFIX(100); } else { l = FIXTOINT(nscss_len2px(hpos, hunit, box->style)); } h = content_get_height(box->background); if (vunit == CSS_UNIT_PCT) { t = (height - h) * vpos / INTTOFIX(100); } else { t = FIXTOINT(nscss_len2px(vpos, vunit, box->style)); } /* Redraw area depends on background-repeat */ switch (css_computed_background_repeat( box->style)) { case CSS_BACKGROUND_REPEAT_REPEAT: data.redraw.x = 0; data.redraw.y = 0; data.redraw.width = box->width; data.redraw.height = box->height; break; case CSS_BACKGROUND_REPEAT_REPEAT_X: data.redraw.x = 0; data.redraw.y += t; data.redraw.width = box->width; break; case CSS_BACKGROUND_REPEAT_REPEAT_Y: data.redraw.x += l; data.redraw.y = 0; data.redraw.height = box->height; break; case CSS_BACKGROUND_REPEAT_NO_REPEAT: data.redraw.x += l; data.redraw.y += t; break; default: break; } data.redraw.object_width = box->width; data.redraw.object_height = box->height; /* Add offset to box */ data.redraw.x += x; data.redraw.y += y; data.redraw.object_x += x; data.redraw.object_y += y; content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); break; } else { /* Non-background case */ if (hlcache_handle_get_content(object) == event->data.redraw.object) { int w = content_get_width(object); int h = content_get_height(object); if (w != 0) { data.redraw.x = data.redraw.x * box->width / w; data.redraw.width = data.redraw.width * box->width / w; } if (h != 0) { data.redraw.y = data.redraw.y * box->height / h; data.redraw.height = data.redraw.height * box->height / h; } data.redraw.object_width = box->width; data.redraw.object_height = box->height; } data.redraw.x += x + box->padding[LEFT]; data.redraw.y += y + box->padding[TOP]; data.redraw.object_x += x + box->padding[LEFT]; data.redraw.object_y += y + box->padding[TOP]; } content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); } break; case CONTENT_MSG_REFRESH: if (content_get_type(object) == CONTENT_HTML) { /* only for HTML objects */ guit->misc->schedule(event->data.delay * 1000, html_object_refresh, o); } break; case CONTENT_MSG_LINK: /* Don't care about favicons that aren't on top level content */ break; case CONTENT_MSG_GETCTX: *(event->data.jscontext) = NULL; break; case CONTENT_MSG_SCROLL: if (box->scroll_x != NULL) scrollbar_set(box->scroll_x, event->data.scroll.x0, false); if (box->scroll_y != NULL) scrollbar_set(box->scroll_y, event->data.scroll.y0, false); break; case CONTENT_MSG_DRAGSAVE: { union content_msg_data msg_data; if (event->data.dragsave.content == NULL) msg_data.dragsave.content = object; else msg_data.dragsave.content = event->data.dragsave.content; content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data); } break; case CONTENT_MSG_SAVELINK: case CONTENT_MSG_POINTER: case CONTENT_MSG_SELECTMENU: case CONTENT_MSG_GADGETCLICK: /* These messages are for browser window layer. * we're not interested, so pass them on. */ 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; union html_drag_owner drag_owner; drag_owner.content = box; switch (event->data.drag.type) { case CONTENT_DRAG_NONE: drag_type = HTML_DRAG_NONE; drag_owner.no_owner = true; break; case CONTENT_DRAG_SCROLL: drag_type = HTML_DRAG_CONTENT_SCROLL; break; case CONTENT_DRAG_SELECTION: drag_type = HTML_DRAG_CONTENT_SELECTION; break; } html_set_drag_type(c, drag_type, drag_owner, event->data.drag.rect); } 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: break; } if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 && (event->type == CONTENT_MSG_LOADING || event->type == CONTENT_MSG_DONE || event->type == CONTENT_MSG_ERROR)) { /* all objects have arrived */ content__reformat(&c->base, false, c->base.available_width, c->base.height); content_set_done(&c->base); } /* If 1) the configuration option to reflow pages while objects are * fetched is set * 2) an object is newly fetched & converted, * 3) the box's dimensions need to change due to being replaced * 4) the object's parent HTML is ready for reformat, * 5) the time since the previous reformat is more than the * configured minimum time between reformats * then reformat the page to display newly fetched objects */ else if (nsoption_bool(incremental_reflow) && event->type == CONTENT_MSG_DONE && box != NULL && !(box->flags & REPLACE_DIM) && (c->base.status == CONTENT_STATUS_READY || c->base.status == CONTENT_STATUS_DONE) && (wallclock() > c->base.reformat_time)) { content__reformat(&c->base, false, c->base.available_width, c->base.height); } return NSERROR_OK; }