diff options
Diffstat (limited to 'desktop')
-rw-r--r-- | desktop/browser.c | 552 | ||||
-rw-r--r-- | desktop/browser.h | 25 | ||||
-rw-r--r-- | desktop/options.c | 2 | ||||
-rw-r--r-- | desktop/options.h | 1 | ||||
-rw-r--r-- | desktop/scroll.c | 789 | ||||
-rw-r--r-- | desktop/scroll.h | 90 | ||||
-rw-r--r-- | desktop/textinput.c | 83 |
7 files changed, 1231 insertions, 311 deletions
diff --git a/desktop/browser.c b/desktop/browser.c index 295e8f1a1..12d1e18c0 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -5,6 +5,7 @@ * Copyright 2004 John Tytgat <joty@netsurf-browser.org> * Copyright 2006 Richard Wilson <info@tinct.net> * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -46,6 +47,7 @@ #include "desktop/history_core.h" #include "desktop/gui.h" #include "desktop/options.h" +#include "desktop/scroll.h" #include "desktop/selection.h" #include "desktop/textinput.h" #include "render/box.h" @@ -109,9 +111,6 @@ static void browser_window_mouse_track_html(struct browser_window *bw, browser_mouse_state mouse, int x, int y); static void browser_window_mouse_track_text(struct browser_window *bw, browser_mouse_state mouse, int x, int y); -static const char *browser_window_scrollbar_click(struct browser_window *bw, - browser_mouse_state mouse, struct box *box, - int box_x, int box_y, int x, int y); static void browser_radio_set(struct content *content, struct form_control *radio); static gui_pointer_shape get_pointer_shape(struct browser_window *bw, @@ -126,9 +125,8 @@ static struct box *browser_window_pick_text_box(struct browser_window *bw, int x, int y, int dir, int *dx, int *dy); static void browser_window_page_drag_start(struct browser_window *bw, int x, int y); -static void browser_window_scroll_box(struct browser_window *bw, - struct box *box, int scroll_x, int scroll_y); - +static void browser_window_box_drag_start(struct browser_window *bw, + struct box *box, int x, int y); /** * Create and open a new browser window with the given page. @@ -443,7 +441,7 @@ void browser_window_callback(content_msg msg, struct content *c, bw->current_content = c; bw->loading_content = NULL; browser_window_remove_caret(bw); - bw->scrolling_box = NULL; + bw->scroll = NULL; gui_window_new_content(bw->window); if (bw->current_content) { browser_window_refresh_url_bar(bw, @@ -514,7 +512,7 @@ void browser_window_callback(content_msg msg, struct content *c, else if (c == bw->current_content) { bw->current_content = 0; browser_window_remove_caret(bw); - bw->scrolling_box = NULL; + bw->scroll = NULL; selection_init(bw->sel, NULL); } browser_window_stop_throbber(bw); @@ -591,7 +589,7 @@ void browser_window_callback(content_msg msg, struct content *c, else if (c == bw->current_content) { bw->current_content = 0; browser_window_remove_caret(bw); - bw->scrolling_box = NULL; + bw->scroll = NULL; selection_init(bw->sel, NULL); } browser_window_stop_throbber(bw); @@ -606,7 +604,7 @@ void browser_window_callback(content_msg msg, struct content *c, else if (c == bw->current_content) { bw->current_content = 0; browser_window_remove_caret(bw); - bw->scrolling_box = NULL; + bw->scroll = NULL; selection_init(bw->sel, NULL); } browser_window_stop_throbber(bw); @@ -1103,6 +1101,8 @@ void browser_window_refresh_url_bar(struct browser_window *bw, const char *url, assert(bw); assert(url); + + bw->visible_select_menu = NULL; if (frag == NULL) { /* With no fragment, we may as well pass url straight through @@ -1392,11 +1392,9 @@ void browser_window_mouse_action_html(struct browser_window *bw, bool imagemap = false; int box_x = 0, box_y = 0; int gadget_box_x = 0, gadget_box_y = 0; - int scroll_box_x = 0, scroll_box_y = 0; int text_box_x = 0; struct box *url_box = 0; struct box *gadget_box = 0; - struct box *scroll_box = 0; struct box *text_box = 0; struct content *c = bw->current_content; struct box *box; @@ -1405,10 +1403,60 @@ void browser_window_mouse_action_html(struct browser_window *bw, struct form_control *gadget = 0; struct content *object = NULL; struct box *next_box; + struct box *drag_candidate = NULL; + struct scroll *scroll = NULL; plot_font_style_t fstyle; - + int scroll_mouse_x = 0, scroll_mouse_y = 0; + int padding_left, padding_right, padding_top, padding_bottom; + + + if (bw->visible_select_menu != NULL) { + box = bw->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(bw->visible_select_menu, + mouse, x - box_x, y - box_y); + if (status != NULL) + browser_window_set_status(bw, status); + else { + int width, height; + form_select_get_dimensions(bw->visible_select_menu, + &width, &height); + bw->visible_select_menu = NULL; + browser_window_redraw_rect(bw, box_x, box_y, + width, height); + } + return; + } + + if (bw->scroll != NULL) { + struct browser_scroll_data *data = scroll_get_data(bw->scroll); + box = data->box; + box_coords(box, &box_x, &box_y); + if (scroll_is_horizontal(bw->scroll)) { + scroll_mouse_x = x - box_x ; + scroll_mouse_y = y - (box_y + box->padding[TOP] + + box->height + box->padding[BOTTOM] - + SCROLLBAR_WIDTH); + status = scroll_mouse_action(bw->scroll, 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 = scroll_mouse_action(bw->scroll, mouse, + scroll_mouse_x, scroll_mouse_y); + } + + browser_window_set_status(bw, status); + return; + } + bw->drag_type = DRAGGING_NONE; - bw->scrolling_box = NULL; /* search the box tree for a link, imagemap, form control, or * box with scrollbars */ @@ -1465,20 +1513,44 @@ void browser_window_mouse_action_html(struct browser_window *bw, if (box->style) overflow = css_computed_overflow(box->style); - if (box->style && box->type != BOX_BR && - box->type != BOX_INLINE && - box->type != BOX_TEXT && - (overflow == CSS_OVERFLOW_SCROLL || - overflow == CSS_OVERFLOW_AUTO) && - ((box_vscrollbar_present(box) && - box_x + box->scroll_x + box->padding[LEFT] + - box->width < x) || - (box_hscrollbar_present(box) && - box_y + box->scroll_y + box->padding[TOP] + - box->height < y))) { - scroll_box = box; - scroll_box_x = box_x + box->scroll_x; - scroll_box_y = box_y + box->scroll_y; + if ((box->scroll_x != NULL || box->scroll_y != NULL) && + drag_candidate == NULL) + drag_candidate = box; + + if (box->scroll_y != NULL || box->scroll_x != NULL) { + padding_left = box_x + scroll_get_offset(box->scroll_x); + padding_right = padding_left + box->padding[LEFT] + + box->width + box->padding[RIGHT]; + padding_top = box_y + scroll_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 */ + + scroll = 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 */ + + scroll = box->scroll_x; + scroll_mouse_x = x - padding_left; + scroll_mouse_y = y - (padding_bottom - + SCROLLBAR_WIDTH); + break; + } + } } if (box->text && !box->object) { @@ -1490,17 +1562,23 @@ void browser_window_mouse_action_html(struct browser_window *bw, /* 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 (scroll_box) { - status = browser_window_scrollbar_click(bw, mouse, scroll_box, - scroll_box_x, scroll_box_y, - x - scroll_box_x, y - scroll_box_y); + if (scroll) { + status = scroll_mouse_action(scroll, mouse, + scroll_mouse_x, scroll_mouse_y); pointer = GUI_POINTER_DEFAULT; } else if (gadget) { switch (gadget->type) { case GADGET_SELECT: status = messages_get("FormSelect"); pointer = GUI_POINTER_MENU; - if (mouse & BROWSER_MOUSE_CLICK_1) + if (mouse & BROWSER_MOUSE_CLICK_1 && + option_core_select_menu) { + bw->visible_select_menu = gadget; + form_open_select_menu(bw, gadget, + browser_select_menu_callback, + bw); + pointer = GUI_POINTER_DEFAULT; + } else if (mouse & BROWSER_MOUSE_CLICK_1) gui_create_form_select_menu(bw, gadget); break; case GADGET_CHECKBOX: @@ -1749,8 +1827,15 @@ void browser_window_mouse_action_html(struct browser_window *bw, gui_drag_save_object(GUI_SAVE_COMPLETE, c, bw->window); } else { - browser_window_page_drag_start(bw, - x, y); + if (drag_candidate == NULL) + browser_window_page_drag_start( + bw, x, y); + else { + browser_window_box_drag_start( + bw, + drag_candidate, + x, y); + } pointer = GUI_POINTER_MOVE; } } @@ -1759,8 +1844,15 @@ void browser_window_mouse_action_html(struct browser_window *bw, gui_drag_save_object(GUI_SAVE_SOURCE, c, bw->window); } else { - browser_window_page_drag_start(bw, - x, y); + if (drag_candidate == NULL) + browser_window_page_drag_start( + bw, x, y); + else { + browser_window_box_drag_start( + bw, + drag_candidate, + x, y); + } pointer = GUI_POINTER_MOVE; } } @@ -1926,54 +2018,8 @@ void browser_window_mouse_track(struct browser_window *bw, void browser_window_mouse_track_html(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { + switch (bw->drag_type) { - case DRAGGING_HSCROLL: - case DRAGGING_VSCROLL: - case DRAGGING_2DSCROLL: { - struct box *box = bw->scrolling_box; - int scroll_y; - int scroll_x; - - assert(box); - - if (bw->drag_type == DRAGGING_HSCROLL) { - scroll_y = box->scroll_y; - } else { - scroll_y = bw->drag_start_scroll_y + - (float) (y - bw->drag_start_y) / - (float) bw->drag_well_height * - (float) (box->descendant_y1 - - box->descendant_y0); - if (scroll_y < box->descendant_y0) - scroll_y = box->descendant_y0; - else if (box->descendant_y1 - box->height < - scroll_y) - scroll_y = box->descendant_y1 - - box->height; - if (scroll_y == box->scroll_y) - return; - } - - if (bw->drag_type == DRAGGING_VSCROLL) { - scroll_x = box->scroll_x; - } else { - scroll_x = bw->drag_start_scroll_x + - (float) (x - bw->drag_start_x) / - (float) bw->drag_well_width * - (float) (box->descendant_x1 - - box->descendant_x0); - if (scroll_x < box->descendant_x0) - scroll_x = box->descendant_x0; - else if (box->descendant_x1 - box->width < - scroll_x) - scroll_x = box->descendant_x1 - - box->width; - } - - browser_window_scroll_box(bw, box, scroll_x, scroll_y); - } - break; - case DRAGGING_SELECTION: { struct box *box; int dir = -1; @@ -2052,6 +2098,43 @@ void browser_window_mouse_track_text(struct browser_window *bw, void browser_window_mouse_drag_end(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { + struct box *box; + int scroll_mouse_x, scroll_mouse_y, box_x, box_y; + + if (bw->visible_select_menu != NULL) { + box = bw->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]; + form_select_mouse_drag_end(bw->visible_select_menu, + mouse, x - box_x, y - box_y); + return; + } + + if (bw->scroll != NULL) { + struct browser_scroll_data *data = scroll_get_data(bw->scroll); + box = data->box; + box_coords(box, &box_x, &box_y); + if (scroll_is_horizontal(bw->scroll)) { + scroll_mouse_x = x - box_x; + scroll_mouse_y = y - (box_y + box->padding[TOP] + + box->height + box->padding[BOTTOM] - + SCROLLBAR_WIDTH); + scroll_mouse_drag_end(bw->scroll, 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; + scroll_mouse_drag_end(bw->scroll, mouse, + scroll_mouse_x, scroll_mouse_y); + } + return; + } + switch (bw->drag_type) { case DRAGGING_SELECTION: { struct content *c = bw->current_content; @@ -2104,13 +2187,6 @@ void browser_window_mouse_drag_end(struct browser_window *bw, } break; - case DRAGGING_2DSCROLL: - case DRAGGING_PAGE_SCROLL: - case DRAGGING_FRAME: - browser_window_set_pointer(bw->window, - GUI_POINTER_DEFAULT); - break; - default: break; } @@ -2120,157 +2196,6 @@ void browser_window_mouse_drag_end(struct browser_window *bw, /** - * Handle mouse clicks in a box scrollbar. - * - * \param bw browser window - * \param mouse state of mouse buttons and modifier keys - * \param box scrolling box - * \param box_x position of box in global document coordinates - * \param box_y position of box in global document coordinates - * \param x coordinate of click relative to box position - * \param y coordinate of click relative to box position - * \return status bar message - */ - -const char *browser_window_scrollbar_click(struct browser_window *bw, - browser_mouse_state mouse, struct box *box, - int box_x, int box_y, int x, int y) -{ - bool but1 = ((mouse & BROWSER_MOUSE_PRESS_1) || - ((mouse & BROWSER_MOUSE_HOLDING_1) && - (mouse & BROWSER_MOUSE_DRAG_ON))); - bool but2 = ((mouse & BROWSER_MOUSE_PRESS_2) || - ((mouse & BROWSER_MOUSE_HOLDING_2) && - (mouse & BROWSER_MOUSE_DRAG_ON))); - const int w = SCROLLBAR_WIDTH; - bool vscroll, hscroll; - int well_height, bar_top, bar_height; - int well_width, bar_left, bar_width; - const char *status = 0; - bool vert; - int z, scroll, bar_start, bar_size, well_size, page; - - box_scrollbar_dimensions(box, - box->padding[LEFT] + box->width + box->padding[RIGHT], - box->padding[TOP] + box->height + box->padding[BOTTOM], - w, - &vscroll, &hscroll, - &well_height, &bar_top, &bar_height, - &well_width, &bar_left, &bar_width); - - /* store some data for scroll drags */ - bw->scrolling_box = box; - bw->drag_start_x = box_x + x; - bw->drag_start_y = box_y + y; - bw->drag_start_scroll_x = box->scroll_x; - bw->drag_start_scroll_y = box->scroll_y; - bw->drag_well_width = well_width; - bw->drag_well_height = well_height; - - /* determine which scrollbar was clicked */ - if (box_vscrollbar_present(box) && - box->padding[LEFT] + box->width < x) { - vert = true; - z = y; - scroll = box->scroll_y; - well_size = well_height; - bar_start = bar_top; - bar_size = bar_height; - page = box->height; - } else { - vert = false; - z = x; - scroll = box->scroll_x; - well_size = well_width; - bar_start = bar_left; - bar_size = bar_width; - page = box->width; - } - - /* find icon in scrollbar and calculate scroll */ - if (z < w) { - /* on scrollbar bump arrow button */ - status = messages_get(vert ? "ScrollUp" : "ScrollLeft"); - if (but1) - scroll -= 16; - else if (but2) - scroll += 16; - } else if (z < w + bar_start + w / 4) { - /* in scrollbar well */ - status = messages_get(vert ? "ScrollPUp" : "ScrollPLeft"); - if (but1) - scroll -= page; - else if (but2) - scroll += page; - } else if (z < w + bar_start + bar_size - w / 4) { - /* in scrollbar */ - status = messages_get(vert ? "ScrollV" : "ScrollH"); - - if (mouse & (BROWSER_MOUSE_HOLDING_1 | - BROWSER_MOUSE_HOLDING_2)) { - int x0 = 0, x1 = 0; - int y0 = 0, y1 = 0; - - if (mouse & BROWSER_MOUSE_HOLDING_1) { - bw->drag_type = vert ? DRAGGING_VSCROLL : - DRAGGING_HSCROLL; - } else - bw->drag_type = DRAGGING_2DSCROLL; - - /* \todo - some proper numbers please! */ - if (bw->drag_type != DRAGGING_VSCROLL) { - x0 = -1024; - x1 = 1024; - } - if (bw->drag_type != DRAGGING_HSCROLL) { - y0 = -1024; - y1 = 1024; - } - gui_window_box_scroll_start(bw->window, x0, y0, x1, y1); - if (bw->drag_type == DRAGGING_2DSCROLL) - gui_window_hide_pointer(bw->window); - } - } else if (z < w + well_size) { - /* in scrollbar well */ - status = messages_get(vert ? "ScrollPDown" : "ScrollPRight"); - if (but1) - scroll += page; - else if (but2) - scroll -= page; - } else { - /* on scrollbar bump arrow button */ - status = messages_get(vert ? "ScrollDown" : "ScrollRight"); - if (but1) - scroll += 16; - else if (but2) - scroll -= 16; - } - - /* update box and redraw */ - if (vert) { - if (scroll < box->descendant_y0) - scroll = box->descendant_y0; - else if (box->descendant_y1 - box->height < scroll) - scroll = box->descendant_y1 - box->height; - if (scroll != box->scroll_y) - browser_window_scroll_box(bw, box, box->scroll_x, - scroll); - - } else { - if (scroll < box->descendant_x0) - scroll = box->descendant_x0; - else if (box->descendant_x1 - box->width < scroll) - scroll = box->descendant_x1 - box->width; - if (scroll != box->scroll_x) - browser_window_scroll_box(bw, box, scroll, - box->scroll_y); - } - - return status; -} - - -/** * Set a radio form control and clear the others in the group. * * \param content content containing the form, of type CONTENT_TYPE @@ -2380,27 +2305,6 @@ void browser_redraw_box(struct content *c, struct box *box) /** - * Update the scroll offsets of a box within a browser window - * (In future, copying where possible, rather than redrawing the entire box) - * - * \param bw browser window - * \param box box to be updated - * \param scroll_x new horizontal scroll offset - * \param scroll_y new vertical scroll offset - */ - -void browser_window_scroll_box(struct browser_window *bw, struct box *box, - int scroll_x, int scroll_y) -{ - box->scroll_x = scroll_x; - box->scroll_y = scroll_y; - - /* fall back to redrawing the whole box */ - browser_redraw_box(bw->current_content, box); -} - - -/** * Process a selection from a form select menu. * * \param bw browser window with menu @@ -2643,6 +2547,79 @@ void browser_form_submit(struct browser_window *bw, struct browser_window *targe free(url); } +/** + * Callback for in-page scrolls. + */ +void browser_scroll_callback(void *client_data, + struct scroll_msg_data *scroll_data) +{ + struct browser_scroll_data *data = client_data; + struct browser_window *bw = data->bw; + struct box *box = data->box; + int x, y, box_x, box_y, diff_x, diff_y; + + + switch(scroll_data->msg) { + case SCROLL_MSG_REDRAW: + diff_x = box->padding[LEFT] + box->width + + box->padding[RIGHT] - SCROLLBAR_WIDTH; + diff_y = box->padding[TOP] + box->height + + box->padding[BOTTOM] - SCROLLBAR_WIDTH; + + box_coords(box, &box_x, &box_y); + if (scroll_is_horizontal(scroll_data->scroll)) { + x = box_x + scroll_get_offset(box->scroll_x); + y = box_y + scroll_get_offset(box->scroll_y) + + diff_y; + } else { + x = box_x + scroll_get_offset(box->scroll_x) + + diff_x; + y = box_y + scroll_get_offset(box->scroll_y); + } + browser_window_redraw_rect(bw, + x + scroll_data->x0, + y + scroll_data->y0, + scroll_data->x1 - scroll_data->x0, + scroll_data->y1 - scroll_data->y0); + break; + case SCROLL_MSG_MOVED: + browser_redraw_box(bw->current_content, box); + break; + case SCROLL_MSG_SCROLL_START: + bw->scroll = scroll_data->scroll; + gui_window_box_scroll_start(bw->window, + scroll_data->x0, scroll_data->y0, + scroll_data->x1, scroll_data->y1); + break; + case SCROLL_MSG_SCROLL_FINISHED: + bw->scroll = NULL; + + browser_window_set_pointer(bw->window, + GUI_POINTER_DEFAULT); + break; + } +} + +/** + * Callback for the core select menu. + */ +void browser_select_menu_callback(void *client_data, + int x, int y, int width, int height) +{ + struct browser_window *bw = client_data; + int menu_x, menu_y; + struct box *box; + + box = bw->visible_select_menu->box; + box_coords(box, &menu_x, &menu_y); + + menu_x -= box->border[LEFT].width; + menu_y += box->height + box->border[BOTTOM].width + + box->padding[BOTTOM] + + box->padding[TOP]; + browser_window_redraw_rect(bw, menu_x + x, menu_y + y, + width, height); +} /** @@ -2766,11 +2743,15 @@ bool browser_window_nearest_text_box(struct box *box, int bx, int by, while (child) { if (child->type == BOX_FLOAT_LEFT || child->type == BOX_FLOAT_RIGHT) { - c_bx = fx + child->x - child->scroll_x; - c_by = fy + child->y - child->scroll_y; + c_bx = fx + child->x - + scroll_get_offset(child->scroll_x); + c_by = fy + child->y - + scroll_get_offset(child->scroll_y); } else { - c_bx = bx + child->x - child->scroll_x; - c_by = by + child->y - child->scroll_y; + c_bx = bx + child->x - + scroll_get_offset(child->scroll_x); + c_by = by + child->y - + scroll_get_offset(child->scroll_y); } if (child->float_children) { c_fx = c_bx; @@ -2880,11 +2861,46 @@ void browser_window_page_drag_start(struct browser_window *bw, int x, int y) bw->drag_start_x = x; bw->drag_start_y = y; - gui_window_get_scroll(bw->window, &bw->drag_start_scroll_x, &bw->drag_start_scroll_y); + gui_window_get_scroll(bw->window, &bw->drag_start_scroll_x, + &bw->drag_start_scroll_y); gui_window_scroll_start(bw->window); } +/** + * Start drag scrolling the contents of a box + * + * \param bw browser window + * \param box the box to be scrolled + * \param x x ordinate of initial mouse position + * \param y y ordinate + */ + +void browser_window_box_drag_start(struct browser_window *bw, + struct box *box, int x, int y) +{ + int box_x, box_y, 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); + scroll_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; + + scroll_start_content_drag(box->scroll_y, + scroll_mouse_x, scroll_mouse_y); + } +} + /** * Check availability of Back action for a given browser window diff --git a/desktop/browser.h b/desktop/browser.h index b5727b941..8f9c2760e 100644 --- a/desktop/browser.h +++ b/desktop/browser.h @@ -27,6 +27,7 @@ #include <inttypes.h> #include <stdbool.h> #include <time.h> + #include "render/html.h" struct box; @@ -40,7 +41,7 @@ struct selection; struct browser_window; struct url_data; struct bitmap; - +struct scroll_msg_data; typedef bool (*browser_caret_callback)(struct browser_window *bw, uint32_t key, void *p); @@ -88,30 +89,25 @@ struct browser_window { /** Current drag status. */ enum { DRAGGING_NONE, - DRAGGING_VSCROLL, - DRAGGING_HSCROLL, DRAGGING_SELECTION, DRAGGING_PAGE_SCROLL, - DRAGGING_2DSCROLL, DRAGGING_FRAME } drag_type; - /** Box currently being scrolled, or 0. */ - struct box *scrolling_box; /** Mouse position at start of current scroll drag. */ int drag_start_x; int drag_start_y; /** Scroll offsets at start of current scroll draw. */ int drag_start_scroll_x; int drag_start_scroll_y; - /** Well dimensions for current scroll drag. */ - int drag_well_width; - int drag_well_height; /** Frame resize directions for current frame resize drag. */ unsigned int drag_resize_left : 1; unsigned int drag_resize_right : 1; unsigned int drag_resize_up : 1; unsigned int drag_resize_down : 1; + + /** Scroll capturing all mouse events */ + struct scroll *scroll; /** Referrer for current fetch, or 0. */ char *referer; @@ -174,6 +170,8 @@ struct browser_window { /** Last time a link was followed in this window */ unsigned int last_action; + + struct form_control *visible_select_menu; }; @@ -214,6 +212,10 @@ typedef enum { * (eg. Alt) */ } browser_mouse_state; +struct browser_scroll_data { + struct browser_window *bw; + struct box *box; +}; extern struct browser_window *current_redraw_browser; extern bool browser_reformat_pending; @@ -257,6 +259,11 @@ void browser_redraw_box(struct content *c, struct box *box); void browser_form_submit(struct browser_window *bw, struct browser_window *target, struct form *form, struct form_control *submit_button); +void browser_scroll_callback(void *client_data, + struct scroll_msg_data *scroll_data); +void browser_select_menu_callback(void *client_data, + int x, int y, int width, int height); + void browser_window_redraw_rect(struct browser_window *bw, int x, int y, int width, int height); diff --git a/desktop/options.c b/desktop/options.c index e885174a4..41896bfc7 100644 --- a/desktop/options.c +++ b/desktop/options.c @@ -145,6 +145,7 @@ unsigned int option_min_reflow_period = 100; /* time in cs */ #else unsigned int option_min_reflow_period = 25; /* time in cs */ #endif +bool option_core_select_menu = false; /** top margin of exported page*/ int option_margin_top = DEFAULT_MARGIN_TOP_MM; /** bottom margin of exported page*/ @@ -244,6 +245,7 @@ struct { { "scale", OPTION_INTEGER, &option_scale }, { "incremental_reflow", OPTION_BOOL, &option_incremental_reflow }, { "min_reflow_period", OPTION_INTEGER, &option_min_reflow_period }, + { "core_select_menu", OPTION_BOOL, &option_core_select_menu }, /* Fetcher options */ { "max_fetchers", OPTION_INTEGER, &option_max_fetchers }, { "max_fetchers_per_host", diff --git a/desktop/options.h b/desktop/options.h index cf26728f0..a25160818 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -83,6 +83,7 @@ extern int option_toolbar_status_width; extern int option_scale; extern bool option_incremental_reflow; extern unsigned int option_min_reflow_period; +extern bool option_core_select_menu; extern int option_margin_top; extern int option_margin_bottom; diff --git a/desktop/scroll.c b/desktop/scroll.c new file mode 100644 index 000000000..248c70908 --- /dev/null +++ b/desktop/scroll.c @@ -0,0 +1,789 @@ +/* + * Copyright 2004-2008 James Bursa <bursa@users.sourceforge.net> + * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Scroll widget (implementation). + */ + +#include <stdbool.h> +#include <stdlib.h> + +#include "desktop/scroll.h" +#include "desktop/plotters.h" +#include "desktop/plot_style.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "utils/utils.h" + +struct scroll { + bool horizontal; /* Horizontal scroll if true, vertical if false + */ + int length; /* Length of the scroll widget */ + + int scrolled_d; /* The dimension of the scrolled area */ + int scrolled_vis; /* The visible part of the scrolled area */ + + int area_scroll; /* Scroll value of the scrolled area */ + int bar_off; /* Offset of the scrollbar */ + int bar_len; /* Length of the scrollbar */ + + scroll_client_callback client_callback; /* Callback receiving scroll + events */ + void *client_data; /* User data passed to the callback */ + + bool dragging; /* Flag indicating drag at progess */ + int drag_start_coord; /* Coordinate value at drag start */ + int drag_start_bar_off; /* Scrollbar offset at drag start */ + bool reverse; /* Flag indicating that the scroll should move + * in the opposite direction than the mouse does + */ + + struct scroll *pair; /* Parpendicular scroll */ + bool pair_drag; /* Flag indicating that the current drag affects + also the pair scroll */ +}; + +/** Overflow scrollbar colours + * + * Overflow scrollbar colours can be set by front end code to try to match + * scrollbar colours used on the desktop. + * + * If a front end doesn't set scrollbar colours, these defaults are used. + */ +colour scroll_widget_fg_colour = 0x00d9d9d9; /* light grey */ +colour scroll_widget_bg_colour = 0x006b6b6b; /* mid grey */ +colour scroll_widget_arrow_colour = 0x00444444; /* dark grey */ + +static void scroll_drag_start_internal(struct scroll *scroll, int x, int y, + bool reverse, bool pair); + + +/** + * Create a scroll. + * + * \param horizontal true for a horizontal scrollbar false for a + * vertical one + * \param length full length of the scroll widget + * \param scrolled_dimension full length of the scrolled area + * \param scrolled_visible length of the visible part of the scrolled area + * \param client_data data for the client callback + * \param client_callback client callback for scroll events + * \param scroll_pt gets updated to point at the newly created + * scroll + * \return true if the scroll has been created succesfully + * or false on memory exhaustion + */ +bool scroll_create(bool horizontal, int length, + int scrolled_dimension, int scrolled_visible, + void *client_data, scroll_client_callback client_callback, + struct scroll **scroll_pt) +{ + struct scroll *scroll; + int well_length; + + scroll = malloc(sizeof(struct scroll)); + if (scroll == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + *scroll_pt = NULL; + return false; + } + + scroll->horizontal = horizontal; + scroll->length = length; + scroll->scrolled_d = scrolled_dimension; + scroll->scrolled_vis = scrolled_visible; + scroll->area_scroll = 0; + scroll->bar_off = 0; + scroll->reverse = false; + scroll->pair = NULL; + scroll->pair_drag = false; + + well_length = length - 2 * SCROLLBAR_WIDTH; + scroll->bar_len = (well_length * scrolled_visible) / scrolled_dimension; + + scroll->client_callback = client_callback; + scroll->client_data = client_data; + + scroll->dragging = false; + + *scroll_pt = scroll; + + return true; +} + +/** + * Destroy a scroll. + * + * \param scroll the scroll to be destroyed + */ +void scroll_destroy(struct scroll *scroll) +{ + if (scroll->pair != NULL) + scroll->pair->pair = NULL; + free(scroll); +} + +/** + * Draw an outline rectangle common to a few of scroll elements. + * + * \param x0 left border of the outline + * \param y0 top border of the outline + * \param x1 right border of the outline + * \param y1 bottom border of the outline + * \param c base colour of the outline, the other colours are created by + * lightening or darkening this one + * \param inset true for inset outline, false for an outset one + * \return + */ +static inline bool scroll_redraw_scrollbar_rectangle( + int x0, int y0, int x1, int y1, colour c, bool inset) +{ + static plot_style_t c0 = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_width = 1, + }; + + static plot_style_t c1 = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_width = 1, + }; + + static plot_style_t c2 = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_width = 1, + }; + + if (inset) { + c0.stroke_colour = darken_colour(c); + c1.stroke_colour = lighten_colour(c); + } else { + c0.stroke_colour = lighten_colour(c); + c1.stroke_colour = darken_colour(c); + } + c2.stroke_colour = blend_colour(c0.stroke_colour, c1.stroke_colour); + + if (!plot.line(x0, y0, x1, y0, &c0)) return false; + if (!plot.line(x1, y0, x1, y1 + 1, &c1)) return false; + if (!plot.line(x1, y0, x1, y0 + 1, &c2)) return false; + if (!plot.line(x1, y1, x0, y1, &c1)) return false; + if (!plot.line(x0, y1, x0, y0, &c0)) return false; + if (!plot.line(x0, y1, x0, y1 + 1, &c2)) return false; + return true; +} + +/** + * Redraw a part of the scroll. + * + * \param scroll the scroll to be redrawn + * \param x the X coordinate to draw the scroll at + * \param y the Y coordinate to draw the scroll at + * \param clip_x0 minimum x of the clipping rectangle + * \param clip_y0 minimum y of the clipping rectangle + * \param clip_x1 maximum x of the clipping rectangle + * \param clip_y1 maximum y of the clipping rectangle + * \param scale scale for the redraw + * \return true on succes false otherwise + */ +bool scroll_redraw(struct scroll *scroll, int x, int y, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale) +{ + int w = SCROLLBAR_WIDTH; + int well_length, bar_off, bar_c0, bar_c1; + int v[6]; /* array of triangle vertices */ + int x0, y0, x1, y1; + plot_style_t pstyle_scroll_widget_bg_colour = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = scroll_widget_bg_colour, + }; + plot_style_t pstyle_scroll_widget_fg_colour = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = scroll_widget_fg_colour, + }; + plot_style_t pstyle_scroll_widget_arrow_colour = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = scroll_widget_arrow_colour, + }; + + well_length = scroll->length - 2 * SCROLLBAR_WIDTH; + x0 = x; + y0 = y; + x1 = x + (scroll->horizontal ? + scroll->length : SCROLLBAR_WIDTH) - 1; + y1 = y + (scroll->horizontal ? SCROLLBAR_WIDTH : scroll->length) - 1; + bar_off = scroll->bar_off; + bar_c1 = (scroll->horizontal ? x0 : y0) + SCROLLBAR_WIDTH + + scroll->bar_off + scroll->bar_len - 1; + + if (scale != 1.0) { + w *= scale; + well_length *= scale; + x0 *= scale; + y0 *= scale; + x1 *= scale; + y1 *= scale; + bar_off *= scale; + bar_c1 *= scale; + } + + bar_c0 = (scroll->horizontal ? x0 : y0) + w + bar_off; + + if (clip_x0 < x0) + clip_x0 = x0; + + if (clip_y0 < y0) + clip_y0 = y0; + + if (clip_x1 > x1 + 1) + clip_x1 = x1 + 1; + + if (clip_y1 > y1 + 1) + clip_y1 = y1 + 1; + + + if (clip_x0 > clip_x1 || clip_y0 > clip_y1) + /* clipping rectangle is outside the scrollbar area */ + return true; + + if (!plot.clip(clip_x0, clip_y0, clip_x1, clip_y1)) + return false; + + + if (scroll->horizontal) { + /* scroll is horizontal */ + + /* scrollbar outline */ + if (!scroll_redraw_scrollbar_rectangle(x0, y0, x1, y1, + scroll_widget_bg_colour, true)) + return false; + /* left arrow icon border */ + if (!scroll_redraw_scrollbar_rectangle(x0 + 1, + y0 + 1, + x0 + w - 2, + y1 - 1, + scroll_widget_fg_colour, false)) + return false; + /* left arrow icon background */ + if (!plot.rectangle(x0 + 2, + y0 + 2, + x0 + w - 2, + y1 - 1, + &pstyle_scroll_widget_fg_colour)) + return false; + /* left arrow */ + v[0] = x0 + w / 4; + v[1] = y0 + w / 2; + v[2] = x0 + w * 3 / 4; + v[3] = y0 + w / 4; + v[4] = x0 + w * 3 / 4; + v[5] = y0 + w * 3 / 4; + if (!plot.polygon(v, 3, &pstyle_scroll_widget_arrow_colour)) + return false; + /* scroll well background */ + if (!plot.rectangle(x0 + w - 1, + y0 + 1, + x1 - w + 2, + y1, + &pstyle_scroll_widget_bg_colour)) + return false; + /* scroll position indicator bar */ + if (!scroll_redraw_scrollbar_rectangle(bar_c0, + y0 + 1, + bar_c1, + y1 - 1, + scroll_widget_fg_colour, false)) + return false; + if (!plot.rectangle(bar_c0 + 1, + y0 + 2, + bar_c1, + y1 - 1, + &pstyle_scroll_widget_fg_colour)) + return false; + /* right arrow icon border */ + if (!scroll_redraw_scrollbar_rectangle(x1 - w + 2, + y0 + 1, + x1 - 1, + y1 - 1, + scroll_widget_fg_colour, false)) + return false; + /* right arrow icon background */ + if (!plot.rectangle(x1 - w + 3, + y0 + 2, + x1 - 1, + y1 - 1, + &pstyle_scroll_widget_fg_colour)) + return false; + /* right arrow */ + v[0] = x1 - w / 4 + 1; + v[1] = y0 + w / 2; + v[2] = x1 - w * 3 / 4 + 1; + v[3] = y0 + w / 4; + v[4] = x1 - w * 3 / 4 + 1; + v[5] = y0 + w * 3 / 4; + if (!plot.polygon(v, 3, &pstyle_scroll_widget_arrow_colour)) + return false; + } else { + /* scroll is vertical */ + + /* outline */ + if (!scroll_redraw_scrollbar_rectangle(x0, y0, x1, y1, + scroll_widget_bg_colour, + true)) + return false; + /* top arrow background */ + if (!scroll_redraw_scrollbar_rectangle(x0 + 1, + y0 + 1, + x1 - 1, + y0 + w - 2, + scroll_widget_fg_colour, + false)) + return false; + if (!plot.rectangle(x0 + 2, + y0 + 2, + x1 - 1, + y0 + w - 2, + &pstyle_scroll_widget_fg_colour)) + return false; + /* up arrow */ + v[0] = x0 + w / 2; + v[1] = y0 + w / 4; + v[2] = x0 + w / 4; + v[3] = y0 + w * 3 / 4; + v[4] = x0 + w * 3 / 4; + v[5] = y0 + w * 3 / 4; + if (!plot.polygon(v, 3, &pstyle_scroll_widget_arrow_colour)) + return false; + /* scroll well background */ + if (!plot.rectangle(x0 + 1, + y0 + w - 1, + x1, + y1 - w + 2, + &pstyle_scroll_widget_bg_colour)) + return false; + /* scroll position indicator bar */ + if (!scroll_redraw_scrollbar_rectangle(x0 + 1, + bar_c0, + x1 - 1, + bar_c1, + scroll_widget_fg_colour, false)) + return false; + if (!plot.rectangle(x0 + 2, + bar_c0 + 1, + x1 - 1, + bar_c1, + &pstyle_scroll_widget_fg_colour)) + return false; + /* bottom arrow background */ + if (!scroll_redraw_scrollbar_rectangle(x0 + 1, + y1 - w + 2, + x1 - 1, + y1 - 1, + scroll_widget_fg_colour, false)) + return false; + if (!plot.rectangle(x0 + 2, + y1 - w + 3, + x1 - 1, + y1 - 1, + &pstyle_scroll_widget_fg_colour)) + return false; + /* down arrow */ + v[0] = x0 + w / 2; + v[1] = y1 - w / 4 + 1; + v[2] = x0 + w / 4; + v[3] = y1 - w * 3 / 4 + 1; + v[4] = x0 + w * 3 / 4; + v[5] = y1 - w * 3 / 4 + 1; + if (!plot.polygon(v, 3, &pstyle_scroll_widget_arrow_colour)) + return false; + } + + return true; +} + +/** + * Set the value of the scroll. + * + * \param scroll the scroll to have the value set + * \param scroll_val the new value to be set + * \param bar true if the value is for the scroll indication bar + * offset, false if it is for the scrolled area one + */ +void scroll_set(struct scroll *scroll, int scroll_val, bool bar) +{ + int well_length; + struct scroll_msg_data msg; + + if (scroll_val < 0) + scroll_val = 0; + + if (scroll->scrolled_d == scroll->scrolled_vis) + return; + + well_length = scroll->length - 2 * SCROLLBAR_WIDTH; + if (bar) { + if (scroll_val > well_length - scroll->bar_len) + scroll->bar_off = well_length - scroll->bar_len; + else + scroll->bar_off = scroll_val; + + scroll->area_scroll = ((scroll->scrolled_d - + scroll->scrolled_vis) * (scroll->bar_off)) / + (well_length - scroll->bar_len); + + } else { + if (scroll_val > scroll->scrolled_d - scroll->scrolled_vis) + scroll->area_scroll = scroll->scrolled_d - + scroll->scrolled_vis; + else + scroll->area_scroll = scroll_val; + + scroll->bar_off = (well_length * scroll->area_scroll) / + scroll->scrolled_d; + } + + msg.scroll = scroll; + msg.msg = SCROLL_MSG_MOVED; + msg.new_scroll = scroll->area_scroll; + scroll->client_callback(scroll->client_data, &msg); + + msg.msg = SCROLL_MSG_REDRAW; + msg.x0 = scroll->horizontal ? SCROLLBAR_WIDTH - 1: 0; + msg.y0 = scroll->horizontal ? 0 : SCROLLBAR_WIDTH - 1; + msg.x1 = (scroll->horizontal ? + scroll->length - SCROLLBAR_WIDTH + 1: SCROLLBAR_WIDTH); + msg.y1 = (scroll->horizontal ? + SCROLLBAR_WIDTH : scroll->length - SCROLLBAR_WIDTH + 1); + scroll->client_callback(scroll->client_data, &msg); +} + +/** + * Get the scroll offset for the visible part of the scrolled area. + * + * \param scroll the scroll to get the value from + * \return scroll offset for the scrolled area + */ +int scroll_get_offset(struct scroll *scroll) +{ + if (scroll == NULL) + return 0; + return scroll->area_scroll; +} + + +/** + * Set the length of the scroll and the visible part of the scrolled area. + * + * \param scroll the scroll to set the values for + * \param length the new scroll length to be set + * \param scrolled_visible the new value of the visible part of the + * scrolled area to be set + */ +void scroll_set_length_and_visible(struct scroll *scroll, int length, + int scrolled_visible) +{ + int well_length; + + scroll->length = length; + scroll->scrolled_vis = scrolled_visible; + well_length = length - 2 * SCROLLBAR_WIDTH; + + scroll->bar_len = (well_length * scrolled_visible) / + scroll->scrolled_d; +} + +/** + * Check the orientation of the scroll. + * + * \param scroll the scroll to check the orientation of + * \return true for a horizontal scroll, false for a vertical one + */ +bool scroll_is_horizontal(struct scroll *scroll) +{ + return scroll->horizontal; +} + +/** + * Handle mouse actions other then drag ends. + * + * \param scroll the scroll which gets the mouse action + * \param mouse mouse state + * \param x X coordinate of the mouse + * \param y Y coordinate of the mouse + * \return message for the status bar or NULL on failure + */ +const char *scroll_mouse_action(struct scroll *scroll, + browser_mouse_state mouse, int x, int y) +{ + int x0, y0, x1, y1; + int val; + const char *status; + bool h; + /* we want mouse presses and mouse drags that were not started at + * the scroll indication bar to be launching actions on the scroll area + */ + bool but1 = ((mouse & BROWSER_MOUSE_PRESS_1) || + ((mouse & BROWSER_MOUSE_HOLDING_1) && + (mouse & BROWSER_MOUSE_DRAG_ON) && + !scroll->dragging)); + bool but2 = ((mouse & BROWSER_MOUSE_PRESS_2) || + ((mouse & BROWSER_MOUSE_HOLDING_2) && + (mouse & BROWSER_MOUSE_DRAG_ON) && + !scroll->dragging)); + + h = scroll->horizontal; + + x0 = 0; + y0 = 0; + x1 = h ? scroll->length : SCROLLBAR_WIDTH; + y1 = h ? SCROLLBAR_WIDTH : scroll->length; + + if (!scroll->dragging && !(x >= x0 && x <= x1 && y >= y0 && y <= y1)) { + /* Not a drag and mouse outside scroll widget */ + return NULL; + } + + + if (h) + val = x; + else + val = y; + + if (scroll->dragging) { + val -= scroll->drag_start_coord; + if (scroll->reverse) + val = -val; + if (val != 0) + scroll_set(scroll, scroll->drag_start_bar_off + val, + true); + if (scroll->pair_drag) { + scroll_mouse_action(scroll->pair, mouse, x, y); + status = messages_get("ScrollBoth"); + } else + status = messages_get(h ? "ScrollH" : "ScrollV"); + + return status; + } + + if (val < SCROLLBAR_WIDTH) { + /* left/up arrow */ + + status = messages_get(h ? "ScrollLeft" : "ScrollUp"); + if (but1) + scroll_set(scroll, scroll->bar_off - SCROLLBAR_WIDTH, + true); + else if (but2) + scroll_set(scroll, scroll->bar_off + SCROLLBAR_WIDTH, + true); + + } else if (val < SCROLLBAR_WIDTH + scroll->bar_off) { + /* well between left/up arrow and bar */ + + status = messages_get(h ? "ScrollPLeft" : "ScrollPUp"); + + if (but1) + scroll_set(scroll, scroll->area_scroll - scroll->length, + false); + else if (but2) + scroll_set(scroll, scroll->area_scroll + scroll->length, + false); + + } else if (val > scroll->length - SCROLLBAR_WIDTH) { + /* right/down arrow */ + + status = messages_get(h ? "ScrollRight" : "ScrollDown"); + + if (but1) + scroll_set(scroll, scroll->bar_off + SCROLLBAR_WIDTH, + true); + else if (but2) + scroll_set(scroll, scroll->bar_off - SCROLLBAR_WIDTH, + true); + + } else if (val > SCROLLBAR_WIDTH + scroll->bar_off + scroll->bar_len) { + /* well between right/down arrow and bar */ + + status = messages_get(h ? "ScrollPRight" : "ScrollPDown"); + if (but1) + scroll_set(scroll, scroll->area_scroll + scroll->length, + false); + else if (but2) + scroll_set(scroll, scroll->area_scroll - scroll->length, + false); + } + else { + /* scroll indication bar */ + + status = messages_get(h ? "ScrollH" : "ScrollV"); + } + + + if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2) && + (val >= SCROLLBAR_WIDTH + scroll->bar_off + && val < SCROLLBAR_WIDTH + scroll->bar_off + + scroll->bar_len)) + /* The mouse event is a drag start and takes place at the scroll + * indication bar. + */ + scroll_drag_start_internal(scroll, x, y, false, + (mouse & BROWSER_MOUSE_DRAG_2) ? + true : false); + + return status; +} + +/** + * Internal procedure used for staring a drag scroll for a scrollbar. + * + * \param scroll the scroll to start the drag for + * \param x the X coordinate of the drag start + * \param y the Y coordinate of the drag start + * \param reverse whether this should be a reverse drag(used when the user + * drags the content and the scrolls have to adjust) + * \param pair whether the drag should start for the pair scroll too + */ +void scroll_drag_start_internal(struct scroll *scroll, int x, int y, + bool reverse, bool pair) +{ + struct scroll_msg_data msg; + + scroll->drag_start_coord = scroll->horizontal ? x : y; + scroll->drag_start_bar_off = scroll->bar_off; + + scroll->dragging = true; + scroll->reverse = reverse; + + msg.scroll = scroll; + + /* \todo - some proper numbers please! */ + if (scroll->horizontal) { + msg.x0 = -1024; + msg.x1 = 1024; + msg.y0 = 0; + msg.y1 = 0; + } else { + msg.x0 = 0; + msg.x1 = 0; + msg.y0 = -1024; + msg.y1 = 1024; + } + + if (pair && scroll->pair != NULL) { + scroll->pair_drag = true; + + scroll->pair->drag_start_coord = + scroll->pair->horizontal ? x : y; + + scroll->pair->drag_start_bar_off = scroll->pair->bar_off; + + scroll->pair->dragging = true; + scroll->pair->reverse = reverse; + + if (scroll->pair->horizontal) { + msg.x0 = -1024; + msg.x1 = 1024; + } else { + msg.y0 = -1024; + msg.y1 = 1024; + } + } + msg.msg = SCROLL_MSG_SCROLL_START; + scroll->client_callback(scroll->client_data, &msg); +} + +/** + * Handle end of mouse drags. + * + * \param scroll the scroll for which the drag ends + * \param mouse mouse state + * \param x X coordinate of the mouse + * \param y Y coordinate of the mouse + */ +void scroll_mouse_drag_end(struct scroll *scroll, browser_mouse_state mouse, + int x, int y) +{ + struct scroll_msg_data msg; + int val; + + assert(scroll->dragging); + + val = (scroll->horizontal ? x : y) - scroll->drag_start_coord; + if (scroll->reverse) + val = -val; + if (val != 0) + scroll_set(scroll, scroll->drag_start_bar_off + val, true); + + scroll->dragging = false; + scroll->reverse = false; + if (scroll->pair_drag) { + scroll->pair_drag = false; + + val = (scroll->pair->horizontal ? x : y) - + scroll->pair->drag_start_coord; + if (scroll->pair->reverse) + val = -val; + if (val != 0) + scroll_set(scroll->pair, + scroll->pair->drag_start_bar_off + val, + true); + + scroll->pair->dragging = false; + scroll->pair->reverse = false; + } + msg.scroll = scroll; + msg.msg = SCROLL_MSG_SCROLL_FINISHED; + scroll->client_callback(scroll->client_data, &msg); +} + +/** + * Called when the content, which is scrolled with some scrolls, is being + * dragged so the scrolls have to adjust properly. If the content has both + * scrolls and scroll_make_pair has beed called before only the one scroll which + * will receive further mouse events has to be passed. + * + * \param scroll one of the the scrolls owned by the dragged content + * \param x X coordinate of mouse during drag start + * \param y Y coordinate of mouse during drag start + */ +void scroll_start_content_drag(struct scroll *scroll, int x, int y) +{ + scroll_drag_start_internal(scroll, x, y, true, true); +} + +/** + * Connect a horizontal and a vertical scroll into a pair so that they + * co-operate during 2D drags. + * + * \param horizontal_scroll the scroll used for horizontal scrolling + * \param vertical_scroll the scroll used for vertical scrolling + */ +void scroll_make_pair(struct scroll *horizontal_scroll, + struct scroll *vertical_scroll) +{ + assert(horizontal_scroll->horizontal && !vertical_scroll->horizontal); + + horizontal_scroll->pair = vertical_scroll; + vertical_scroll->pair = horizontal_scroll; +} + +void *scroll_get_data(struct scroll *scroll) +{ + return scroll->client_data; +} diff --git a/desktop/scroll.h b/desktop/scroll.h new file mode 100644 index 000000000..f0569c37f --- /dev/null +++ b/desktop/scroll.h @@ -0,0 +1,90 @@ +/* + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Scroll widget (interface). + */ + +#ifndef _NETSURF_DESKTOP_SCROLL_H_ +#define _NETSURF_DESKTOP_SCROLL_H_ + +#include <stdbool.h> + +#include "desktop/browser.h" + +#define SCROLLBAR_WIDTH 16 + +struct scroll; + +typedef enum { + SCROLL_MSG_REDRAW, /* the scrollbar requests a redraw */ + SCROLL_MSG_MOVED, /* the scroll value has changed */ + SCROLL_MSG_SCROLL_START, /* a scroll drag has started, all mouse + * events should be passed to + * the scrollbar regardless of the + * coordinates + */ + SCROLL_MSG_SCROLL_FINISHED, /* cancel the above */ + +} scroll_msg; + +struct scroll_msg_data { + struct scroll *scroll; + scroll_msg msg; + int new_scroll; + int x0, y0, x1, y1; +}; + +/** + * Client callback for the scroll. + * + * \param client_data user data passed at scroll creation + * \param scroll_data struct all necessary message data + */ +typedef void(*scroll_client_callback)(void *client_data, + struct scroll_msg_data *scroll_data); + +bool scroll_create(bool horizontal, int length, + int scrolled_dimension, int scrolled_visible, + void *client_data, scroll_client_callback client_callback, + struct scroll **scroll_pt); +void scroll_destroy(struct scroll *scroll); +bool scroll_redraw(struct scroll *scroll, int x, int y, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale); + +void scroll_set(struct scroll *scroll, int scroll_val, bool bar); +int scroll_get_offset(struct scroll *scroll); + +void scroll_set_length_and_visible(struct scroll *scroll, int length, + int scrolled_visible); + +bool scroll_is_horizontal(struct scroll *scroll); + +const char *scroll_mouse_action(struct scroll *scroll, + browser_mouse_state mouse, int x, int y); +void scroll_mouse_drag_end(struct scroll *scroll, browser_mouse_state mouse, + int x, int y); +void scroll_start_content_drag(struct scroll *scroll, int x, int y); + +void scroll_make_pair(struct scroll *horizontal_scroll, + struct scroll *vertical_scroll); + +void *scroll_get_data(struct scroll *scroll); + +#endif diff --git a/desktop/textinput.c b/desktop/textinput.c index 645c961c4..4a0aef467 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -30,6 +30,7 @@ #include "desktop/browser.h" #include "desktop/gui.h" +#include "desktop/scroll.h" #include "desktop/selection.h" #include "desktop/textinput.h" #include "render/box.h" @@ -91,7 +92,8 @@ static void textarea_reflow(struct browser_window *bw, struct box *textarea, static bool word_left(const char *text, size_t *poffset, size_t *pchars); static bool word_right(const char *text, size_t len, size_t *poffset, size_t *pchars); -static bool ensure_caret_visible(struct box *textarea); +static bool ensure_caret_visible(struct browser_window *bw, + struct box *textarea); /** * Remove the given text caret from the window by invalidating it @@ -279,11 +281,11 @@ void browser_window_textarea_click(struct browser_window *bw, textarea->gadget->caret_box_offset = char_offset; textarea->gadget->caret_pixel_offset = pixel_offset; - box_x += textarea->scroll_x; - box_y += textarea->scroll_y; - scrolled = ensure_caret_visible(textarea); - box_x -= textarea->scroll_x; - box_y -= textarea->scroll_y; + box_x += scroll_get_offset(textarea->scroll_x); + box_y += scroll_get_offset(textarea->scroll_y); + scrolled = ensure_caret_visible(bw, textarea); + box_x -= scroll_get_offset(textarea->scroll_x); + box_y -= scroll_get_offset(textarea->scroll_y); browser_window_place_caret(bw, box_x + inline_container->x + text_box->x + @@ -332,8 +334,8 @@ bool browser_window_textarea_callback(struct browser_window *bw, (int) text_box->length, text_box->text)); box_coords(textarea, &box_x, &box_y); - box_x -= textarea->scroll_x; - box_y -= textarea->scroll_y; + box_x -= scroll_get_offset(textarea->scroll_x); + box_y -= scroll_get_offset(textarea->scroll_y); if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) { /* normal character insertion */ @@ -756,8 +758,8 @@ bool browser_window_textarea_callback(struct browser_window *bw, assert(text_box); assert(char_offset <= text_box->length); /* Scroll back to the left */ - box_x += textarea->scroll_x; - textarea->scroll_x = 0; + box_x += scroll_get_offset(textarea->scroll_x); + scroll_set(textarea->scroll_x, 0, false); } else { assert(!text_box->next || (text_box->next && @@ -778,11 +780,11 @@ bool browser_window_textarea_callback(struct browser_window *bw, textarea->gadget->caret_box_offset = char_offset; textarea->gadget->caret_pixel_offset = pixel_offset; - box_x += textarea->scroll_x; - box_y += textarea->scroll_y; - scrolled = ensure_caret_visible(textarea); - box_x -= textarea->scroll_x; - box_y -= textarea->scroll_y; + box_x += scroll_get_offset(textarea->scroll_x); + box_y += scroll_get_offset(textarea->scroll_y); + scrolled = ensure_caret_visible(bw, textarea); + box_x -= scroll_get_offset(textarea->scroll_x); + box_y -= scroll_get_offset(textarea->scroll_y); browser_window_place_caret(bw, box_x + inline_container->x + text_box->x + @@ -1398,11 +1400,11 @@ bool browser_window_textarea_paste_text(struct browser_window *bw, textarea->gadget->caret_pixel_offset = pixel_offset; box_coords(textarea, &box_x, &box_y); - box_x += textarea->scroll_x; - box_y += textarea->scroll_y; - ensure_caret_visible(textarea); - box_x -= textarea->scroll_x; - box_y -= textarea->scroll_y; + box_x += scroll_get_offset(textarea->scroll_x); + box_y += scroll_get_offset(textarea->scroll_y); + ensure_caret_visible(bw, textarea); + box_x -= scroll_get_offset(textarea->scroll_x); + box_y -= scroll_get_offset(textarea->scroll_y); browser_window_place_caret(bw, box_x + inline_container->x + text_box->x + @@ -1518,8 +1520,8 @@ void browser_window_textarea_move_caret(struct browser_window *bw, void *p) font_plot_style_from_css(text_box->style, &fstyle); box_coords(textarea, &box_x, &box_y); - box_x -= textarea->scroll_x; - box_y -= textarea->scroll_y; + box_x -= scroll_get_offset(textarea->scroll_x); + box_y -= scroll_get_offset(textarea->scroll_y); nsfont.font_width(&fstyle, text_box->text, char_offset, &pixel_offset); @@ -2180,19 +2182,21 @@ bool word_right(const char *text, size_t len, size_t *poffset, size_t *pchars) /** * Adjust scroll offsets so that the caret is visible + * + * \param bw browser window where click ocurred * \param textarea textarea box * \return true if a change in scroll offsets has occurred */ -bool ensure_caret_visible(struct box *textarea) +bool ensure_caret_visible(struct browser_window *bw, struct box *textarea) { int cx, cy; int scrollx, scrolly; assert(textarea->gadget); - scrollx = textarea->scroll_x; - scrolly = textarea->scroll_y; + scrollx = scroll_get_offset(textarea->scroll_x); + scrolly = scroll_get_offset(textarea->scroll_y); /* Calculate the caret coordinates */ cx = textarea->gadget->caret_pixel_offset + @@ -2200,29 +2204,40 @@ bool ensure_caret_visible(struct box *textarea) cy = textarea->gadget->caret_text_box->y; /* Ensure they are visible */ - if (!box_hscrollbar_present(textarea)) { + if (textarea->scroll_x == NULL) { scrollx = 0; - } else if (cx-textarea->scroll_x < 0) { + } else if (cx - scroll_get_offset(textarea->scroll_x) < 0) { scrollx = cx; - } else if (cx > textarea->scroll_x + textarea->width) { + } else if (cx > scroll_get_offset(textarea->scroll_x) + + textarea->width) { scrollx = cx - textarea->width; } - if (!box_vscrollbar_present(textarea)) { + if (textarea->scroll_y == NULL) { scrolly = 0; - } else if (cy - textarea->scroll_y < 0) { + } else if (cy - scroll_get_offset(textarea->scroll_y) < 0) { scrolly = cy; } else if (cy + textarea->gadget->caret_text_box->height > - textarea->scroll_y + textarea->height) { + scroll_get_offset(textarea->scroll_y) + + textarea->height) { scrolly = (cy + textarea->gadget->caret_text_box->height) - textarea->height; } - if ((scrollx == textarea->scroll_x) && (scrolly == textarea->scroll_y)) + if ((scrollx == scroll_get_offset(textarea->scroll_x)) && + (scrolly == scroll_get_offset(textarea->scroll_y))) return false; - textarea->scroll_x = scrollx; - textarea->scroll_y = scrolly; + if (textarea->scroll_x != NULL) { + bw->scroll = textarea->scroll_x; + scroll_set(textarea->scroll_x, scrollx, false); + bw->scroll = NULL; + } + if (textarea->scroll_y != NULL) { + bw->scroll = textarea->scroll_x; + scroll_set(textarea->scroll_y, scrolly, false); + bw->scroll = NULL; + } return true; } |