From 13afa0ff4c591e7800dc4c11a81d04c335418c05 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Fri, 14 Aug 2009 10:37:33 +0000 Subject: Merge Paul Blokus' selectscroll branch. Adds core select menu widget for forms and core scrollbar widget. svn path=/trunk/netsurf/; revision=9289 --- !NetSurf/Resources/de/Messages | 6 + !NetSurf/Resources/en/Messages | 6 + !NetSurf/Resources/fr/Messages | 8 +- !NetSurf/Resources/it/Messages | 6 + !NetSurf/Resources/nl/Messages | 6 + Makefile.sources | 2 +- amiga/thumbnail.c | 2 + desktop/browser.c | 552 ++++++++++++++-------------- desktop/browser.h | 25 +- desktop/options.c | 2 + desktop/options.h | 1 + desktop/scroll.c | 789 +++++++++++++++++++++++++++++++++++++++++ desktop/scroll.h | 90 +++++ desktop/textinput.c | 83 +++-- framebuffer/gui.c | 6 +- gtk/gtk_gui.c | 2 + gtk/gtk_plotters.h | 1 + gtk/gtk_scaffolding.c | 1 + gtk/gtk_window.c | 15 +- render/box.c | 164 ++++++++- render/box.h | 13 +- render/form.c | 492 +++++++++++++++++++++++++ render/form.h | 34 ++ render/html_redraw.c | 454 +++++------------------- render/layout.c | 7 +- render/layout.h | 2 - 26 files changed, 2067 insertions(+), 702 deletions(-) create mode 100644 desktop/scroll.c create mode 100644 desktop/scroll.h diff --git a/!NetSurf/Resources/de/Messages b/!NetSurf/Resources/de/Messages index 903cb601f..c38c5775e 100644 --- a/!NetSurf/Resources/de/Messages +++ b/!NetSurf/Resources/de/Messages @@ -699,7 +699,13 @@ ScrollPLeft:Anklicken scrollt eine Seite nach links ScrollH:Ziehen der Leiste scrollt horizontal ScrollPRight:Anklicken scrollt eine Seite nach rechts ScrollRight:Pfeil anklicken scrollt rechts +ScrollBoth:Move your mouse while keeping the button pressed to scroll the content +# Select menu - displayed in status bar +# +SelectClick:Click on entry to select it +SelectMClick:Click on entry to select it, multiple options can be selected +SelectClose:Click to close the select menu # Saving # ====== diff --git a/!NetSurf/Resources/en/Messages b/!NetSurf/Resources/en/Messages index 75ad9596a..7ff954bae 100644 --- a/!NetSurf/Resources/en/Messages +++ b/!NetSurf/Resources/en/Messages @@ -704,7 +704,13 @@ ScrollPLeft:Click to scroll left one page ScrollH:Drag the bar to scroll horizontally ScrollPRight:Click to scroll right one page ScrollRight:Click the arrow to scroll right +ScrollBoth:Move your mouse while keeping the button pressed to scroll the content +# Select menu - displayed in status bar +# +SelectClick:Click on entry to select it +SelectMClick:Click on entry to select it, multiple options can be selected +SelectClose:Click to close the select menu # Saving # ====== diff --git a/!NetSurf/Resources/fr/Messages b/!NetSurf/Resources/fr/Messages index 0780e5ca1..96344f473 100644 --- a/!NetSurf/Resources/fr/Messages +++ b/!NetSurf/Resources/fr/Messages @@ -699,7 +699,13 @@ ScrollPLeft:Cliquez pour faire défiler d'une page vers la gauche ScrollH:Tirez la barre pour la faire défiler horizontalement ScrollPRight:Cliquez pour faire défiler d'une page vers la droite ScrollRight:Cliquez sur la flèche pour faire défiler vers la droite +ScrollBoth:Move your mouse while keeping the button pressed to scroll the content +# Select menu - displayed in status bar +# +SelectClick:Click on entry to select it +SelectMClick:Click on entry to select it, multiple options can be selected +SelectClose:Click to close the select menu # Saving # ====== @@ -761,7 +767,7 @@ HelpToolbar3:\Tle bouton de recharge.|M\Srecharger cette page.|M\Arecharger cett HelpToolbar4:\Tle bouton Accueil.|M\Saller à votre page d'accueil. HelpToolbar5:\Tle bouton d'historique.|M\Souvrir la \w d'historique locale.|M\Aopen the global history \w. HelpToolbar6:\Tle bouton de sauvegarde.|M\Ssauver le document en cours. -HelpToolbar7:\Tle bouton d'impression.|M\Simprimer cette page.|MOuvre une boîte de dialogue pour l'impression. +HelpToolbar7:\Tle bouton d'impression.|M\Simprimer cette page.|MOuvre une bo�te de dialogue pour l'impression. HelpToolbar8:\Tle bouton de favoris.|M\Souvrir la \w de gestion des favoris.|M\Aajouter cette adresse aux favoris. HelpToolbar9:\Tle bouton de changement d'échelle.|M\Sredimensionner la page, texte et images comprises. HelpToolbar10:\Tle bouton de recherche.|M\Strouver des occurences de fragment de texte dans une page. diff --git a/!NetSurf/Resources/it/Messages b/!NetSurf/Resources/it/Messages index 186ecf0f4..672feb61e 100755 --- a/!NetSurf/Resources/it/Messages +++ b/!NetSurf/Resources/it/Messages @@ -706,7 +706,13 @@ ScrollPLeft:Clicca per scrollare a sinistra di una pagina ScrollH:Trascina la barra per scrollare orizzontalmente ScrollPRight:Clicca per scrollare a destra di una pagina ScrollRight:Clicca sulla freccia per scrollare a destra +ScrollBoth:Move your mouse while keeping the button pressed to scroll the content +# Select menu - displayed in status bar +# +SelectClick:Click on entry to select it +SelectMClick:Click on entry to select it, multiple options can be selected +SelectClose:Click to close the select menu # Saving # ====== diff --git a/!NetSurf/Resources/nl/Messages b/!NetSurf/Resources/nl/Messages index 723290d12..f63f3345b 100644 --- a/!NetSurf/Resources/nl/Messages +++ b/!NetSurf/Resources/nl/Messages @@ -699,7 +699,13 @@ ScrollPLeft:Click to scroll left one page ScrollH:Drag the bar to scroll horizontally ScrollPRight:Click to scroll right one page ScrollRight:Click the arrow to scroll right +ScrollBoth:Move your mouse while keeping the button pressed to scroll the content +# Select menu - displayed in status bar +# +SelectClick:Click on entry to select it +SelectMClick:Click on entry to select it, multiple options can be selected +SelectClose:Click to close the select menu # Saving # ====== diff --git a/Makefile.sources b/Makefile.sources index 2ffe1e7f4..41f0638a0 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -14,7 +14,7 @@ S_RENDER := box.c box_construct.c box_normalise.c directory.c \ S_UTILS := base64.c filename.c hashtable.c locale.c messages.c talloc.c \ url.c utf8.c utils.c useragent.c S_DESKTOP := knockout.c options.c print.c tree.c version.c textarea.c \ - plot_style.c + plot_style.c scroll.c # S_COMMON are sources common to all builds S_COMMON := $(addprefix content/,$(S_CONTENT)) \ diff --git a/amiga/thumbnail.c b/amiga/thumbnail.c index 891fa6672..24396719e 100755 --- a/amiga/thumbnail.c +++ b/amiga/thumbnail.c @@ -26,6 +26,7 @@ #include "amiga/bitmap.h" #include "amiga/options.h" #include "content/urldb.h" +#include "desktop/plotters.h" bool thumbnail_create(struct content *content, struct bitmap *bitmap, const char *url) @@ -39,6 +40,7 @@ bool thumbnail_create(struct content *content, struct bitmap *bitmap, bitmap->nativebmwidth = bitmap->width; bitmap->nativebmheight = bitmap->height; ami_clearclipreg(&browserglob.rp); + plot = amiplot; content_redraw(content, 0, 0, content->width, content->width, 0, 0, content->width, content->width, 1.0, 0xFFFFFF); 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 * Copyright 2006 Richard Wilson * Copyright 2008 Michael Drake + * Copyright 2009 Paul Blokus * * 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; } @@ -2119,157 +2195,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. * @@ -2379,27 +2304,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. * @@ -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 #include #include + #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 + * Copyright 2008 Michael Drake + * Copyright 2009 Paul Blokus + * + * 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 . + */ + +/** \file + * Scroll widget (implementation). + */ + +#include +#include + +#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 + * + * 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 . + */ + +/** \file + * Scroll widget (interface). + */ + +#ifndef _NETSURF_DESKTOP_SCROLL_H_ +#define _NETSURF_DESKTOP_SCROLL_H_ + +#include + +#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; } diff --git a/framebuffer/gui.c b/framebuffer/gui.c index fbed5bd86..c6aaa8f49 100644 --- a/framebuffer/gui.c +++ b/framebuffer/gui.c @@ -41,6 +41,7 @@ #include "utils/messages.h" #include "utils/utils.h" #include "desktop/textinput.h" +#include "render/form.h" #include "framebuffer/gui.h" #include "framebuffer/fbtk.h" @@ -292,13 +293,14 @@ static void fb_redraw(fbtk_widget_t *widget, nsfb_claim(fbtk_get_nsfb(widget), &bwidget->redraw_box); /* redraw bounding box is relative to window */ + current_redraw_browser = bw; content_redraw(c, x - bwidget->scrollx, y - bwidget->scrolly, width, height, bwidget->redraw_box.x0, bwidget->redraw_box.y0, bwidget->redraw_box.x1, bwidget->redraw_box.y1, bw->scale, 0xFFFFFF); - + current_redraw_browser = NULL; nsfb_release(fbtk_get_nsfb(widget), &bwidget->redraw_box); @@ -357,6 +359,8 @@ void gui_init(int argc, char** argv) LOG(("Using '%s' as Messages file", buf)); messages_load(buf); + option_core_select_menu = true; + /* load browser options */ fb_find_resource(buf, "Choices-fb", "~/.netsurf/Choices-fb"); LOG(("Using '%s' as Preferences file", buf)); diff --git a/gtk/gtk_gui.c b/gtk/gtk_gui.c index 58bc18ed6..971c3fbf2 100644 --- a/gtk/gtk_gui.c +++ b/gtk/gtk_gui.c @@ -250,6 +250,8 @@ void gui_init(int argc, char** argv) if (nsgtk_throbber == NULL) die("Unable to load throbber image.\n"); + option_core_select_menu = true; + find_resource(buf, "Choices", "~/.netsurf/Choices"); LOG(("Using '%s' as Preferences file", buf)); options_file_location = strdup(buf); diff --git a/gtk/gtk_plotters.h b/gtk/gtk_plotters.h index 33e1a9484..a59ae00bb 100644 --- a/gtk/gtk_plotters.h +++ b/gtk/gtk_plotters.h @@ -29,6 +29,7 @@ struct plotter_table; extern const struct plotter_table nsgtk_plotters; +/* make sure this is NULL if no redraw is in progress */ extern GtkWidget *current_widget; extern GdkDrawable *current_drawable; extern GdkGC *current_gc; diff --git a/gtk/gtk_scaffolding.c b/gtk/gtk_scaffolding.c index 6905408fa..942587c54 100644 --- a/gtk/gtk_scaffolding.c +++ b/gtk/gtk_scaffolding.c @@ -1182,6 +1182,7 @@ gboolean nsgtk_history_expose_event(GtkWidget *widget, history_redraw(bw->history); + current_widget = NULL; g_object_unref(current_gc); #ifdef CAIRO_VERSION cairo_destroy(current_cr); diff --git a/gtk/gtk_window.c b/gtk/gtk_window.c index c0377ca43..aa0ccaae6 100644 --- a/gtk/gtk_window.c +++ b/gtk/gtk_window.c @@ -305,8 +305,8 @@ gboolean nsgtk_window_expose_event(GtkWidget *widget, if (c->type == CONTENT_HTML) scale = 1; - current_widget = widget; - current_drawable = widget->window; + current_widget = (GtkWidget *)g->drawing_area; + current_drawable = current_widget->window; current_gc = gdk_gc_new(current_drawable); #ifdef CAIRO_VERSION current_cr = gdk_cairo_create(current_drawable); @@ -315,6 +315,7 @@ gboolean nsgtk_window_expose_event(GtkWidget *widget, plot = nsgtk_plotters; nsgtk_plot_set_scale(g->bw->scale); current_redraw_browser = g->bw; + content_redraw(c, 0, 0, widget->allocation.width * scale, widget->allocation.height * scale, @@ -328,6 +329,7 @@ gboolean nsgtk_window_expose_event(GtkWidget *widget, if (g->careth != 0) nsgtk_plot_caret(g->caretx, g->carety, g->careth); + current_widget = NULL; g_object_unref(current_gc); #ifdef CAIRO_VERSION cairo_destroy(current_cr); @@ -442,7 +444,8 @@ gboolean nsgtk_window_button_release_event(GtkWidget *widget, browser_window_mouse_click(g->bw, g->mouse->state, event->x / g->bw->scale, event->y / g->bw->scale); else - browser_window_mouse_drag_end(g->bw, 0, event->x, event->y); + browser_window_mouse_drag_end(g->bw, 0, event->x / g->bw->scale, + event->y / g->bw->scale); g->mouse->state = 0; return TRUE; @@ -654,8 +657,10 @@ void gui_window_update_box(struct gui_window *g, return; gtk_widget_queue_draw_area(GTK_WIDGET(g->drawing_area), - data->redraw.x, data->redraw.y, - data->redraw.width, data->redraw.height); + data->redraw.x * g->bw->scale, + data->redraw.y * g->bw->scale, + data->redraw.width * g->bw->scale, + data->redraw.height * g->bw->scale); } bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy) diff --git a/render/box.c b/render/box.c index b1ebfcbad..0c0985dab 100644 --- a/render/box.c +++ b/render/box.c @@ -29,11 +29,13 @@ #include "content/content.h" #include "css/css.h" #include "css/dump.h" +#include "desktop/scroll.h" #include "desktop/options.h" #include "render/box.h" #include "render/form.h" #include "utils/log.h" #include "utils/talloc.h" +#include "utils/utils.h" static bool box_contains_point(struct box *box, int x, int y, bool *physically); @@ -80,7 +82,7 @@ struct box * box_create(css_computed_style *style, box->descendant_x1 = box->descendant_y1 = 0; for (i = 0; i != 4; i++) box->margin[i] = box->padding[i] = box->border[i].width = 0; - box->scroll_x = box->scroll_y = 0; + box->scroll_x = box->scroll_y = NULL; box->min_width = 0; box->max_width = UNKNOWN_MAX_WIDTH; box->byte_offset = 0; @@ -226,6 +228,10 @@ void box_free_box(struct box *box) if (!box->clone) { if (box->gadget) form_free_control(box->gadget); + if (box->scroll_x != NULL) + scroll_destroy(box->scroll_x); + if (box->scroll_y != NULL) + scroll_destroy(box->scroll_y); } talloc_free(box); @@ -251,8 +257,8 @@ void box_coords(struct box *box, int *x, int *y) } while (!box->float_children); } else box = box->parent; - *x += box->x - box->scroll_x; - *y += box->y - box->scroll_y; + *x += box->x - scroll_get_offset(box->scroll_x); + *y += box->y - scroll_get_offset(box->scroll_y); } } @@ -328,8 +334,10 @@ struct box *box_at_point(struct box *box, const int x, const int y, /* consider floats second, since they will often overlap other boxes */ for (child = box->float_children; child; child = child->next_float) { if (box_contains_point(child, x - bx, y - by, &physically)) { - *box_x = bx + child->x - child->scroll_x; - *box_y = by + child->y - child->scroll_y; + *box_x = bx + child->x - + scroll_get_offset(child->scroll_x); + *box_y = by + child->y - + scroll_get_offset(child->scroll_y); if (physically) return child; @@ -345,8 +353,10 @@ non_float_children: if (box_is_float(child)) continue; if (box_contains_point(child, x - bx, y - by, &physically)) { - *box_x = bx + child->x - child->scroll_x; - *box_y = by + child->y - child->scroll_y; + *box_x = bx + child->x - + scroll_get_offset(child->scroll_x); + *box_y = by + child->y - + scroll_get_offset(child->scroll_y); if (physically) return child; @@ -370,16 +380,18 @@ siblings: /* siblings and siblings of ancestors */ while (box) { if (box_is_float(box)) { - bx -= box->x - box->scroll_x; - by -= box->y - box->scroll_y; + bx -= box->x - scroll_get_offset(box->scroll_x); + by -= box->y - scroll_get_offset(box->scroll_y); for (sibling = box->next_float; sibling; sibling = sibling->next_float) { if (box_contains_point(sibling, x - bx, y - by, &physically)) { *box_x = bx + sibling->x - - sibling->scroll_x; + scroll_get_offset( + sibling->scroll_x); *box_y = by + sibling->y - - sibling->scroll_y; + scroll_get_offset( + sibling->scroll_y); if (physically) return sibling; @@ -398,8 +410,8 @@ siblings: goto non_float_children; } else { - bx -= box->x - box->scroll_x; - by -= box->y - box->scroll_y; + bx -= box->x - scroll_get_offset(box->scroll_x); + by -= box->y - scroll_get_offset(box->scroll_y); for (sibling = box->next; sibling; sibling = sibling->next) { if (box_is_float(sibling)) @@ -407,9 +419,11 @@ siblings: if (box_contains_point(sibling, x - bx, y - by, &physically)) { *box_x = bx + sibling->x - - sibling->scroll_x; + scroll_get_offset( + sibling->scroll_x); *box_y = by + sibling->y - - sibling->scroll_y; + scroll_get_offset( + sibling->scroll_y); if (physically) return sibling; @@ -978,3 +992,123 @@ void box_duplicate_update(struct box *box, box->next_float = box_dict_element->new; } } + +/** + * Applies the given scroll setup to a box. This includes scroll + * creation/deletion as well as scroll dimension updates. + * + * \param box the box to handle the scrolls for + * \param x X coordinate of the box + * \param y Y coordinate of the box + * \param bottom whether the horizontal scrollbar should be present + * \param right whether the vertical scrollbar should be present + * \return true on success false otherwise + */ +bool box_handle_scrollbars(struct box *box, int x, int y, bool bottom, + bool right) +{ + struct browser_scroll_data *data; + int padding_width, padding_height; + + padding_width = box->width + box->padding[RIGHT] + box->padding[LEFT]; + padding_height = box->height + box->padding[TOP] + box->padding[BOTTOM]; + + if (!bottom && box->scroll_x != NULL) { + data = scroll_get_data(box->scroll_x); + scroll_destroy(box->scroll_x); + free(data); + box->scroll_x = NULL; + } + + if (!right && box->scroll_y != NULL) { + data = scroll_get_data(box->scroll_x); + scroll_destroy(box->scroll_y); + free(data); + box->scroll_y = NULL; + } + + if (!bottom && !right) + return true; + + if (right) { + if (box->scroll_y == NULL) { + data = malloc(sizeof(struct browser_scroll_data)); + if (data == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return false; + } + data->bw = current_redraw_browser; + data->box = box; + if (!scroll_create(false, + padding_height, + box->descendant_y1 - box->descendant_y0, + box->height, + data, + browser_scroll_callback, + &(box->scroll_y))) + return false; + } else + scroll_set_length_and_visible(box->scroll_y, + padding_height, box->height); + } + if (bottom) { + if (box->scroll_x == NULL) { + data = malloc(sizeof(struct browser_scroll_data)); + if (data == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return false; + } + data->bw = current_redraw_browser; + data->box = box; + if (!scroll_create(true, + padding_width - + (right ? SCROLLBAR_WIDTH : 0), + box->descendant_x1 - box->descendant_x0, + box->width, + data, + browser_scroll_callback, + &box->scroll_x)) + return false; + } else + scroll_set_length_and_visible(box->scroll_x, + padding_width - + (right ? SCROLLBAR_WIDTH : 0), + box->width); + } + + if (right && bottom) + scroll_make_pair(box->scroll_x, box->scroll_y); + + return true; +} + +/** + * Determine if a box has a vertical scrollbar. + * + * \param box scrolling box + * \return the box has a vertical scrollbar + */ + +bool box_vscrollbar_present(const struct box * const box) +{ + return box->descendant_y0 < -box->border[TOP].width || + box->padding[TOP] + box->height + box->padding[BOTTOM] + + box->border[BOTTOM].width < box->descendant_y1; +} + + +/** + * Determine if a box has a horizontal scrollbar. + * + * \param box scrolling box + * \return the box has a horizontal scrollbar + */ + +bool box_hscrollbar_present(const struct box * const box) +{ + return box->descendant_x0 < -box->border[LEFT].width || + box->padding[LEFT] + box->width + box->padding[RIGHT] + + box->border[RIGHT].width < box->descendant_x1; +} diff --git a/render/box.h b/render/box.h index 701329108..39a6c2521 100644 --- a/render/box.h +++ b/render/box.h @@ -167,8 +167,8 @@ struct box { int padding[4]; /**< Padding: TOP, RIGHT, BOTTOM, LEFT. */ struct box_border border[4]; /**< Border: TOP, RIGHT, BOTTOM, LEFT. */ - int scroll_x; /**< Horizontal scroll of descendants. */ - int scroll_y; /**< Vertical scroll of descendants. */ + struct scroll *scroll_x; /**< Horizontal scroll. */ + struct scroll *scroll_y; /**< Vertical scroll. */ /** Width of box taking all line breaks (including margins etc). Must * be non-negative. */ @@ -314,15 +314,10 @@ bool box_visible(struct box *box); void box_dump(FILE *stream, struct box *box, unsigned int depth); bool box_extract_link(const char *rel, const char *base, char **result); +bool box_handle_scrollbars(struct box *box, int x, int y, bool bottom, + bool right); bool box_vscrollbar_present(const struct box *box); bool box_hscrollbar_present(const struct box *box); -void box_scrollbar_dimensions(const struct box *box, - int padding_width, int padding_height, int w, - bool *vscroll, bool *hscroll, - int *well_height, - int *bar_top, int *bar_height, - int *well_width, - int *bar_left, int *bar_width); bool xml_to_box(xmlNode *n, struct content *c); diff --git a/render/form.c b/render/form.c index 01e5e1d2f..968397a21 100644 --- a/render/form.c +++ b/render/form.c @@ -2,6 +2,7 @@ * Copyright 2004 James Bursa * Copyright 2003 Phil Mellor * Copyright 2005-9 John-Mark Bell + * Copyright 2009 Paul Blokus * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -29,18 +30,60 @@ #include #include #include +#include "css/css.h" +#include "css/utils.h" +#include "desktop/gui.h" +#include "desktop/knockout.h" +#include "desktop/plot_style.h" +#include "desktop/plotters.h" +#include "desktop/scroll.h" #include "render/box.h" +#include "render/font.h" #include "render/form.h" +#include "render/layout.h" #include "utils/log.h" +#include "utils/messages.h" #include "utils/url.h" #include "utils/utf8.h" #include "utils/utils.h" +#define MAX_SELECT_HEIGHT 210 +#define SELECT_LINE_SPACING 0.2 +#define SELECT_BORDER_WIDTH 1 +#define SELECT_SELECTED_COLOUR 0xDB9370 + +struct form_select_menu { + int line_height; + int width, height; + struct scroll *scroll; + int f_size; + bool scroll_capture; + select_menu_redraw_callback callback; + void *client_data; + struct browser_window *bw; +}; + +static plot_style_t plot_style_fill_selected = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = SELECT_SELECTED_COLOUR, +}; + +static plot_font_style_t plot_fstyle_entry = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .weight = 400, + .flags = FONTF_NONE, + .background = 0xffffff, + .foreground = 0x000000, +}; static char *form_textarea_value(struct form_control *textarea); static char *form_acceptable_charset(struct form *form); static char *form_encode_item(const char *item, const char *charset, const char *fallback); +static void form_select_menu_clicked(struct form_control *control, + int x, int y); +static void form_select_menu_scroll_callback(void *client_data, + struct scroll_msg_data *scroll_data); /** * Create a struct form. @@ -199,6 +242,8 @@ void form_free_control(struct form_control *control) free(option->value); free(option); } + if (control->data.select.menu != NULL) + form_free_select_menu(control); } free(control); @@ -812,3 +857,450 @@ char *form_encode_item(const char *item, const char *charset, return ret; } +/** + * Open a select menu for a select form control, creating it if necessary. + * + * \param client_data data passed to the redraw callback + * \param control the select form control for which the menu is being + * opened + * \param callback redraw callback for the select menu + * \param bw the browser window in which the select menu is being + * opened + * \return false on memory exhaustion, true otherwise + */ +bool form_open_select_menu(void *client_data, + struct form_control *control, + select_menu_redraw_callback callback, + struct browser_window *bw) +{ + int i, line_height_with_spacing, scroll; + struct form_option *option; + struct box *box; + plot_font_style_t fstyle; + int total_height; + struct form_select_menu *menu; + + + /* if the menu is opened for the first time */ + if (control->data.select.menu == NULL) { + + menu = calloc(1, sizeof (struct form_select_menu)); + if (menu == NULL) { + warn_user("NoMemory", 0); + return false; + } + + control->data.select.menu = menu; + + box = control->box; + + menu->width = box->width + + box->border[RIGHT].width + + box->border[LEFT].width + + box->padding[RIGHT] + box->padding[LEFT]; + + font_plot_style_from_css(control->box->style, + &fstyle); + menu->f_size = fstyle.size; + + menu->line_height = + FIXTOINT(FDIVI((FMUL(FLTTOFIX(1.2), + FMULI(nscss_screen_dpi, + (fstyle.size / FONT_SIZE_SCALE)))), 72)); + + line_height_with_spacing = menu->line_height + + menu->line_height * + SELECT_LINE_SPACING; + + total_height = control->data.select.num_items * + line_height_with_spacing; + menu->height = total_height; + + scroll = 0; + if (menu->height > MAX_SELECT_HEIGHT) { + + menu->height = MAX_SELECT_HEIGHT; + + if (control->data.select.num_selected > 0) { + i = 0; + option = control->data.select.items; + while (!option->selected) { + option = option->next; + i++; + } + + if ((i + 1) * line_height_with_spacing > + MAX_SELECT_HEIGHT) + scroll = (i + 1) * + line_height_with_spacing + - MAX_SELECT_HEIGHT; + } + } + menu->client_data = client_data; + menu->callback = callback; + if (!scroll_create(false, + menu->height, + total_height, + menu->height, + control, + form_select_menu_scroll_callback, + &(menu->scroll))) { + free(menu); + return false; + } + menu->bw = bw; + } + else menu = control->data.select.menu; + + menu->callback(client_data, 0, 0, menu->width, menu->height); + + return true; +} + + +/** + * Destroy a select menu and free allocated memory. + * + * \param control the select form control owning the select menu being + * destroyed + */ +void form_free_select_menu(struct form_control *control) +{ + if (control->data.select.menu->scroll != NULL) + scroll_destroy(control->data.select.menu->scroll); + free(control->data.select.menu); + control->data.select.menu = NULL; +} + +/** + * Redraw an opened select menu. + * + * \param control the select menu being redrawn + * \param x the X coordinate to draw the menu at + * \param x the Y coordinate to draw the menu at + * \param scale current redraw scale + * \param clip_x0 minimum x of clipping rectangle + * \param clip_y0 minimum y of clipping rectangle + * \param clip_x1 maximum x of clipping rectangle + * \param clip_y1 maximum y of clipping rectangle + * \return true on success, false otherwise + */ +bool form_redraw_select_menu(struct form_control *control, int x, int y, + float scale, int clip_x0, int clip_y0, int clip_x1, int clip_y1) +{ + struct box *box; + struct form_select_menu *menu = control->data.select.menu; + struct form_option *option; + int line_height, line_height_with_spacing; + int width, height; + int x0, y0, x1, scrollbar_x, y1, y2, y3; + int item_y; + int text_pos_offset, text_x; + int scrollbar_width = SCROLLBAR_WIDTH; + int i; + int scroll; + int x_cp, y_cp; + + box = control->box; + + x_cp = x; + y_cp = y; + width = menu->width; + height = menu->height; + line_height = menu->line_height; + + line_height_with_spacing = line_height + + line_height * SELECT_LINE_SPACING; + scroll = scroll_get_offset(menu->scroll); + + if (scale != 1.0) { + x *= scale; + y *= scale; + width *= scale; + height *= scale; + scrollbar_width *= scale; + + i = scroll / line_height_with_spacing; + scroll -= i * line_height_with_spacing; + line_height *= scale; + line_height_with_spacing *= scale; + scroll *= scale; + scroll += i * line_height_with_spacing; + } + + + x0 = x; + y0 = y; + x1 = x + width - 1; + y1 = y + height - 1; + scrollbar_x = x1 - scrollbar_width; + + if (!plot.clip(x0, y0, x1 + 1, y1 + 1)) + return false; + if (!plot.rectangle(x0, y0, x1, y1 ,plot_style_stroke_darkwbasec)) + return false; + + + x0 = x0 + SELECT_BORDER_WIDTH; + y0 = y0 + SELECT_BORDER_WIDTH; + x1 = x1 - SELECT_BORDER_WIDTH; + y1 = y1 - SELECT_BORDER_WIDTH; + height = height - 2 * SELECT_BORDER_WIDTH; + + if (!plot.clip(x0, y0, x1 + 1, y1 + 1)) + return false; + if (!plot.rectangle(x0, y0, x1 + 1, y1 + 1, + plot_style_fill_lightwbasec)) + return false; + option = control->data.select.items; + item_y = line_height_with_spacing; + + while (item_y < scroll) { + option = option->next; + item_y += line_height_with_spacing; + } + item_y -= line_height_with_spacing; + text_pos_offset = y - scroll + + (int) (line_height * (0.75 + SELECT_LINE_SPACING)); + text_x = x + (box->border[LEFT].width + box->padding[LEFT]) * scale; + + plot_fstyle_entry.size = menu->f_size; + + while (option && item_y - scroll < height) { + + if (option->selected) { + y2 = y + item_y - scroll; + y3 = y + item_y + line_height_with_spacing - scroll; + if (!plot.rectangle(x0, (y0 > y2 ? y0 : y2), + scrollbar_x + 1, + (y3 < y1 + 1 ? y3 : y1 + 1), + &plot_style_fill_selected)) + return false; + } + + y2 = text_pos_offset + item_y; + if (!plot.text(text_x, y2, option->text, + strlen(option->text), &plot_fstyle_entry)) + return false; + + item_y += line_height_with_spacing; + option = option->next; + } + + if (!scroll_redraw(menu->scroll, + x_cp + menu->width - SCROLLBAR_WIDTH, + y_cp, + clip_x0, clip_y0, clip_x1, clip_y1, scale)) + return false; + + return true; +} + +/** + * Check whether a clipping rectangle is completely contained in the + * select menu. + * + * \param control the select menu to check the clipping rectangle for + * \param scale the current browser window scale + * \param clip_x0 minimum x of clipping rectangle + * \param clip_y0 minimum y of clipping rectangle + * \param clip_x1 maximum x of clipping rectangle + * \param clip_y1 maximum y of clipping rectangle + * \return true if inside false otherwise + */ +bool form_clip_inside_select_menu(struct form_control *control, float scale, + int clip_x0, int clip_y0, int clip_x1, int clip_y1) +{ + struct form_select_menu *menu = control->data.select.menu; + int width, height; + + + width = menu->width; + height = menu->height; + + if (scale != 1.0) { + width *= scale; + height *= scale; + } + + if (clip_x0 >= 0 && clip_x1 <= width && + clip_y0 >= 0 && clip_y1 <= height) + return true; + + return false; +} + +/** + * Handle a click on the area of the currently opened select menu. + * + * \param control the select menu which received the click + * \param x X coordinate of click + * \param y Y coordinate of click + */ +void form_select_menu_clicked(struct form_control *control, int x, int y) +{ + struct form_select_menu *menu = control->data.select.menu; + struct form_option *option; + int line_height, line_height_with_spacing; + int item_bottom_y; + int scroll, i; + + scroll = scroll_get_offset(menu->scroll); + + line_height = menu->line_height; + line_height_with_spacing = line_height + + line_height * SELECT_LINE_SPACING; + + option = control->data.select.items; + item_bottom_y = line_height_with_spacing; + i = 0; + while (option && item_bottom_y < scroll + y) { + item_bottom_y += line_height_with_spacing; + option = option->next; + i++; + } + + if (option != NULL) + browser_window_form_select(menu->bw, control, i); + + menu->callback(menu->client_data, 0, 0, menu->width, menu->height); +} + +/** + * Handle mouse action for the currently opened select menu. + * + * \param control the select menu which received the mouse action + * \param mouse current mouse state + * \param x X coordinate of click + * \param y Y coordinate of click + * \return text for the browser status bar or NULL if the menu has + * to be closed + */ +const char *form_select_mouse_action(struct form_control *control, + browser_mouse_state mouse, int x, int y) +{ + struct form_select_menu *menu = control->data.select.menu; + int x0, y0, x1, y1, scrollbar_x; + const char *status = NULL; + bool multiple = control->data.select.multiple; + + x0 = 0; + y0 = 0; + x1 = menu->width; + y1 = menu->height; + scrollbar_x = x1 - SCROLLBAR_WIDTH; + + if (menu->scroll_capture || + (x > scrollbar_x && x < x1 && y > y0 && y < y1)) { + /* The scroll is currently capturing all events or the mouse + * event is taking place on the scrollbar widget area + */ + x -= scrollbar_x; + return scroll_mouse_action(menu->scroll, + mouse, x, y); + } + + + if (x > x0 && x < scrollbar_x && y > y0 && y < y1) { + /* over option area */ + + if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) + /* button 1 or 2 click */ + form_select_menu_clicked(control, x, y); + + if (!(mouse & BROWSER_MOUSE_CLICK_1 && !multiple)) + /* anything but a button 1 click over a single select + menu */ + status = messages_get(control->data.select.multiple ? + "SelectMClick" : "SelectClick"); + + } else if (!(mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2))) + /* if not a button 1 or 2 click*/ + status = messages_get("SelectClose"); + + return status; +} + +/** + * Handle mouse drag end for the currently opened select menu. + * + * \param control the select menu which received the mouse drag end + * \param mouse current mouse state + * \param x X coordinate of drag end + * \param y Y coordinate of drag end + */ +void form_select_mouse_drag_end(struct form_control *control, + browser_mouse_state mouse, int x, int y) +{ + int x0, y0, x1, y1; + struct form_select_menu *menu = control->data.select.menu; + + if (menu->scroll_capture) { + x -= menu->width - SCROLLBAR_WIDTH; + scroll_mouse_drag_end(menu->scroll, + mouse, x, y); + return; + } + + x0 = 0; + y0 = 0; + x1 = menu->width; + y1 = menu->height; + + + if (x > x0 && x < x1 - SCROLLBAR_WIDTH && y > y0 && y < y1) + /* handle drag end above the option area like a regular click */ + form_select_menu_clicked(control, x, y); +} + +/** + * Callback for the select menus scroll + */ +void form_select_menu_scroll_callback(void *client_data, + struct scroll_msg_data *scroll_data) +{ + struct form_control *control = client_data; + struct form_select_menu *menu = control->data.select.menu; + + switch (scroll_data->msg) { + case SCROLL_MSG_REDRAW: + menu->callback(menu->client_data, + menu->width - + SCROLLBAR_WIDTH + scroll_data->x0, + scroll_data->y0, + scroll_data->x1 - scroll_data->x0, + scroll_data->y1 - scroll_data->y0); + break; + case SCROLL_MSG_MOVED: + menu->callback(menu->client_data, + 0, 0, + menu->width - SCROLLBAR_WIDTH, + menu->height); + break; + case SCROLL_MSG_SCROLL_START: + menu->scroll_capture = true; + gui_window_box_scroll_start(menu->bw->window, + scroll_data->x0, scroll_data->y0, + scroll_data->x1, scroll_data->y1); + break; + case SCROLL_MSG_SCROLL_FINISHED: + menu->scroll_capture = false; + break; + default: + break; + } +} + +/** + * Get the dimensions of a select menu. + * + * \param control the select menu to get the dimensions of + * \param width gets updated to menu width + * \param height gets updated to menu height + */ +void form_select_get_dimensions(struct form_control *control, + int *width, int *height) +{ + *width = control->data.select.menu->width; + *height = control->data.select.menu->height; +} diff --git a/render/form.h b/render/form.h index eaecf3411..a31c24975 100644 --- a/render/form.h +++ b/render/form.h @@ -1,6 +1,7 @@ /* * Copyright 2003 Phil Mellor * Copyright 2003 James Bursa + * Copyright 2009 Paul Blokus * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -25,11 +26,13 @@ #define _NETSURF_RENDER_FORM_H_ #include +#include "desktop/browser.h" #include "utils/config.h" struct box; struct form_control; struct form_option; +struct form_select_menu; /** Form submit method. */ typedef enum { @@ -105,6 +108,7 @@ struct form_control { int num_selected; /** Currently selected item, if num_selected == 1. */ struct form_option *current; + struct form_select_menu *menu; } select; } data; @@ -129,6 +133,19 @@ struct form_successful_control { struct form_successful_control *next; /**< Next in linked list. */ }; +/** + * Called by the select menu when it wants an area to be redrawn. The + * coordinates are menu origin relative. + * + * \param client_data data which was passed to form_open_select_menu + * \param x X coordinate of redraw rectangle + * \param y Y coordinate of redraw rectangle + * \param width width of redraw rectangle + * \param height height of redraw rectangle + */ +typedef void(*select_menu_redraw_callback)(void *client_data, + int x, int y, int width, int height); + struct form *form_new(void *node, const char *action, const char *target, form_method method, const char *charset, const char *doc_charset); @@ -145,4 +162,21 @@ char *form_url_encode(struct form *form, struct form_successful_control *control); void form_free_successful(struct form_successful_control *control); +bool form_open_select_menu(void *client_data, + struct form_control *control, + select_menu_redraw_callback redraw_callback, + struct browser_window *bw); +void form_free_select_menu(struct form_control *control); +bool form_redraw_select_menu(struct form_control *control, int x, int y, + float scale, + int clip_x0, int clip_y0, int clip_x1, int clip_y1); +bool form_clip_inside_select_menu(struct form_control *control, float scale, + int clip_x0, int clip_y0, int clip_x1, int clip_y1); +const char *form_select_mouse_action(struct form_control *control, + browser_mouse_state mouse, int x, int y); +void form_select_mouse_drag_end(struct form_control *control, + browser_mouse_state mouse, int x, int y); +void form_select_get_dimensions(struct form_control *control, + int *width, int *height); + #endif diff --git a/render/html_redraw.c b/render/html_redraw.c index 4a8d72bec..2f4fdf794 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -5,6 +5,7 @@ * Copyright 2005-2006 Adrian Lees * Copyright 2006 Rob Kendrick * Copyright 2008 Michael Drake + * Copyright 2009 Paul Blokus * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -40,6 +41,7 @@ #include "desktop/textinput.h" #include "desktop/options.h" #include "desktop/print.h" +#include "desktop/scroll.h" #include "image/bitmap.h" #include "render/box.h" #include "render/font.h" @@ -89,23 +91,9 @@ static bool html_redraw_text_decoration_inline(struct box *box, int x, int y, float scale, colour colour, float ratio); static bool html_redraw_text_decoration_block(struct box *box, int x, int y, float scale, colour colour, float ratio); -static bool html_redraw_scrollbars(struct box *box, float scale, - int x, int y, int padding_width, int padding_height, - colour background_colour); bool html_redraw_debug = false; -/** 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 css_scrollbar_fg_colour = 0x00d9d9d9; /* light grey */ -colour css_scrollbar_bg_colour = 0x006b6b6b; /* mid grey */ -colour css_scrollbar_arrow_colour = 0x00444444; /* dark grey */ - /** * Draw a CONTENT_HTML using the current set of plotters (plot). * @@ -131,7 +119,8 @@ bool html_redraw(struct content *c, int x, int y, float scale, colour background_colour) { struct box *box; - bool result, want_knockout; + bool result = true, want_knockout; + bool select, select_only; plot_style_t pstyle_fill_bg = { .fill_type = PLOT_OP_TYPE_SOLID, .fill_colour = background_colour, @@ -144,18 +133,53 @@ bool html_redraw(struct content *c, int x, int y, if (want_knockout) knockout_plot_start(&plot); - /* clear to background colour */ - result = plot.clip(clip_x0, clip_y0, clip_x1, clip_y1); - - if (c->data.html.background_colour != NS_TRANSPARENT) - pstyle_fill_bg.fill_colour = c->data.html.background_colour; - - result &= plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1, &pstyle_fill_bg); - - result &= html_redraw_box(box, x, y, - clip_x0, clip_y0, clip_x1, clip_y1, - scale, pstyle_fill_bg.fill_colour); + /* The select menu needs special treating because, when opened, it + * reaches beyond its layout box. + */ + select = false; + select_only = false; + if (current_redraw_browser != NULL && + current_redraw_browser->visible_select_menu != NULL) { + struct form_control *control = + current_redraw_browser->visible_select_menu; + select = true; + /* check if the redraw rectangle is completely inside of the + select menu */ + select_only = form_clip_inside_select_menu(control, scale, + clip_x0, clip_y0, clip_x1, clip_y1); + } + + if (!select_only) { + /* clear to background colour */ + result = plot.clip(clip_x0, clip_y0, clip_x1, clip_y1); + + if (c->data.html.background_colour != NS_TRANSPARENT) + pstyle_fill_bg.fill_colour = + c->data.html.background_colour; + + result &= plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1, + &pstyle_fill_bg); + + result &= html_redraw_box(box, x, y, + clip_x0, clip_y0, clip_x1, clip_y1, + scale, pstyle_fill_bg.fill_colour); + } + if (select) { + int menu_x, menu_y; + box = current_redraw_browser->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]; + result &= form_redraw_select_menu( + current_redraw_browser->visible_select_menu, + x + menu_x, y + menu_y, + current_redraw_browser->scale, + clip_x0, clip_y0, clip_x1, clip_y1); + } + if (want_knockout) knockout_plot_end(); @@ -195,6 +219,7 @@ bool html_redraw_box(struct box *box, int x_scrolled, y_scrolled; struct box *bg_box = NULL; css_color bgcol = 0; + bool has_x_scroll, has_y_scroll; if (html_redraw_printing && box->printed) return true; @@ -622,8 +647,8 @@ bool html_redraw_box(struct box *box, return false; if (box->object) { - x_scrolled = x - box->scroll_x * scale; - y_scrolled = y - box->scroll_y * scale; + x_scrolled = x - scroll_get_offset(box->scroll_x) * scale; + y_scrolled = y - scroll_get_offset(box->scroll_y) * scale; if (!content_redraw(box->object, x_scrolled + padding_left, y_scrolled + padding_top, @@ -664,23 +689,52 @@ bool html_redraw_box(struct box *box, /* list marker */ if (box->list_marker) if (!html_redraw_box(box->list_marker, - x_parent + box->x - box->scroll_x, - y_parent + box->y - box->scroll_y, + x_parent + box->x - + scroll_get_offset(box->scroll_x), + y_parent + box->y - + scroll_get_offset(box->scroll_y), clip_x0, clip_y0, clip_x1, clip_y1, scale, current_background_color)) return false; /* scrollbars */ - if (box->style && box->type != BOX_BR && box->type != BOX_TABLE && - box->type != BOX_INLINE && + if (current_redraw_browser && box->style && box->type != BOX_BR && + box->type != BOX_TABLE && box->type != BOX_INLINE && (css_computed_overflow(box->style) == CSS_OVERFLOW_SCROLL || css_computed_overflow(box->style) == - CSS_OVERFLOW_AUTO)) - if (!html_redraw_scrollbars(box, scale, x, y, - padding_width, padding_height, - current_background_color)) + CSS_OVERFLOW_AUTO)) { + + has_x_scroll = false; + has_y_scroll = false; + + has_x_scroll = box_hscrollbar_present(box); + + has_y_scroll = box_vscrollbar_present(box); + + if (!box_handle_scrollbars(box, + x_parent + box->x, y_parent + box->y, + has_x_scroll, has_y_scroll)) return false; + + if (box->scroll_x != NULL) + scroll_redraw(box->scroll_x, + x_parent + box->x, + y_parent + box->y + box->padding[TOP] + + box->height + box->padding[BOTTOM] - + SCROLLBAR_WIDTH, + clip_x0, clip_y0, clip_x1, clip_y1, + scale); + if (box->scroll_y != NULL) + scroll_redraw(box->scroll_y, + x_parent + box->x + box->padding[LEFT] + + box->width + box->padding[RIGHT] - + SCROLLBAR_WIDTH, + y_parent + box->y, + clip_x0, clip_y0, clip_x1, clip_y1, + scale); + } + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || box->type == BOX_TABLE_CELL || box->object) @@ -717,16 +771,20 @@ bool html_redraw_box_children(struct box *box, if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) if (!html_redraw_box(c, - x_parent + box->x - box->scroll_x, - y_parent + box->y - box->scroll_y, + x_parent + box->x - + scroll_get_offset(box->scroll_x), + y_parent + box->y - + scroll_get_offset(box->scroll_y), clip_x0, clip_y0, clip_x1, clip_y1, scale, current_background_color)) return false; } for (c = box->float_children; c; c = c->next_float) if (!html_redraw_box(c, - x_parent + box->x - box->scroll_x, - y_parent + box->y - box->scroll_y, + x_parent + box->x - + scroll_get_offset(box->scroll_x), + y_parent + box->y - + scroll_get_offset(box->scroll_y), clip_x0, clip_y0, clip_x1, clip_y1, scale, current_background_color)) return false; @@ -1957,319 +2015,3 @@ bool html_redraw_text_decoration_block(struct box *box, int x, int y, } return true; } - -static inline bool -html_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; -} - -/** - * Plot scrollbars for a scrolling box. - * - * \param box scrolling box - * \param scale scale for redraw - * \param x coordinate of box - * \param y coordinate of box - * \param padding_width width of padding box - * \param padding_height height of padding box - * \return true if successful, false otherwise - */ - -bool html_redraw_scrollbars(struct box *box, float scale, - int x, int y, int padding_width, int padding_height, - colour background_colour) -{ - const int w = SCROLLBAR_WIDTH * scale; - bool vscroll, hscroll; - int well_height, bar_top, bar_height; - int well_width, bar_left, bar_width; - int v[6]; /* array of triangle vertices */ - plot_style_t pstyle_css_scrollbar_bg_colour = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = css_scrollbar_bg_colour, - }; - plot_style_t pstyle_css_scrollbar_fg_colour = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = css_scrollbar_fg_colour, - }; - plot_style_t pstyle_css_scrollbar_arrow_colour = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = css_scrollbar_arrow_colour, - }; - - box_scrollbar_dimensions(box, padding_width, padding_height, w, - &vscroll, &hscroll, - &well_height, &bar_top, &bar_height, - &well_width, &bar_left, &bar_width); - - - /* horizontal scrollbar */ - if (hscroll) { - /* scrollbar outline */ - if (!html_redraw_scrollbar_rectangle(x, - y + padding_height - w, - x + padding_width - 1, - y + padding_height - 1, - css_scrollbar_bg_colour, true)) - return false; - /* left arrow icon border */ - if (!html_redraw_scrollbar_rectangle(x + 1, - y + padding_height - w + 1, - x + w - 2, - y + padding_height - 2, - css_scrollbar_fg_colour, false)) - return false; - /* left arrow icon background */ - if (!plot.rectangle(x + 2, - y + padding_height - w + 2, - x + w - 2, - y + padding_height - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* left arrow */ - v[0] = x + w / 4; - v[1] = y + padding_height - w / 2; - v[2] = x + w * 3 / 4; - v[3] = y + padding_height - w * 3 / 4; - v[4] = x + w * 3 / 4; - v[5] = y + padding_height - w / 4; - if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour)) - return false; - /* scroll well background */ - if (!plot.rectangle(x + w - 1, - y + padding_height - w + 1, - x + w + well_width + (vscroll ? 2 : 1), - y + padding_height - 1, - &pstyle_css_scrollbar_bg_colour)) - return false; - /* scroll position indicator bar */ - if (!html_redraw_scrollbar_rectangle(x + w + bar_left, - y + padding_height - w + 1, - x + w + bar_left + bar_width + (vscroll? 1 : 0), - y + padding_height - 2, - css_scrollbar_fg_colour, false)) - return false; - if (!plot.rectangle(x + w + bar_left + 1, - y + padding_height - w + 2, - x + w + bar_left + bar_width + (vscroll? 1 : 0), - y + padding_height - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* right arrow icon border */ - if (!html_redraw_scrollbar_rectangle(x + w + well_width + 2, - y + padding_height - w + 1, - x + w + well_width + w - (vscroll ? 1 : 2), - y + padding_height - 2, - css_scrollbar_fg_colour, false)) - return false; - /* right arrow icon background */ - if (!plot.rectangle(x + w + well_width + 3, - y + padding_height - w + 2, - x + w + well_width + w - (vscroll ? 1 : 2), - y + padding_height - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* right arrow */ - v[0] = x + w + well_width + w * 3 / 4 + (vscroll ? 1 : 0); - v[1] = y + padding_height - w / 2; - v[2] = x + w + well_width + w / 4 + (vscroll ? 1 : 0); - v[3] = y + padding_height - w * 3 / 4; - v[4] = x + w + well_width + w / 4 + (vscroll ? 1 : 0); - v[5] = y + padding_height - w / 4; - if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour)) - return false; - } - - /* vertical scrollbar */ - if (vscroll) { - /* outline */ - if (!html_redraw_scrollbar_rectangle(x + padding_width - w, - y, - x + padding_width - 1, - y + padding_height - 1, - css_scrollbar_bg_colour, - true)) - return false; - /* top arrow background */ - if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1, - y + 1, - x + padding_width - 2, - y + w - 2, - css_scrollbar_fg_colour, - false)) - return false; - if (!plot.rectangle(x + padding_width - w + 2, - y + 2, - x + padding_width - 2, - y + w - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* up arrow */ - v[0] = x + padding_width - w / 2; - v[1] = y + w / 4; - v[2] = x + padding_width - w * 3 / 4; - v[3] = y + w * 3 / 4; - v[4] = x + padding_width - w / 4; - v[5] = y + w * 3 / 4; - if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour)) - return false; - /* scroll well background */ - if (!plot.rectangle(x + padding_width - w + 1, - y + w - 1, - x + padding_width - 1, - y + padding_height - w + 1, - &pstyle_css_scrollbar_bg_colour)) - return false; - /* scroll position indicator bar */ - if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1, - y + w + bar_top, - x + padding_width - 2, - y + w + bar_top + bar_height, - css_scrollbar_fg_colour, false)) - return false; - if (!plot.rectangle(x + padding_width - w + 2, - y + w + bar_top + 1, - x + padding_width - 2, - y + w + bar_top + bar_height, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* bottom arrow background */ - if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1, - y + padding_height - w + 1, - x + padding_width - 2, - y + padding_height - 2, - css_scrollbar_fg_colour, false)) - return false; - if (!plot.rectangle(x + padding_width - w + 2, - y + padding_height - w + 2, - x + padding_width - 2, - y + padding_height - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* down arrow */ - v[0] = x + padding_width - w / 2; - v[1] = y + w + well_height + w * 3 / 4; - v[2] = x + padding_width - w * 3 / 4; - v[3] = y + w + well_height + w / 4; - v[4] = x + padding_width - w / 4; - v[5] = y + w + well_height + w / 4; - if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour)) - return false; - } - - return true; -} - - -/** - * Determine if a box has a vertical scrollbar. - * - * \param box scrolling box - * \return the box has a vertical scrollbar - */ - -bool box_vscrollbar_present(const struct box * const box) -{ - return box->descendant_y0 < -box->border[TOP].width || - box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM].width < box->descendant_y1; -} - - -/** - * Determine if a box has a horizontal scrollbar. - * - * \param box scrolling box - * \return the box has a horizontal scrollbar - */ - -bool box_hscrollbar_present(const struct box * const box) -{ - return box->descendant_x0 < -box->border[LEFT].width || - box->padding[LEFT] + box->width + box->padding[RIGHT] + - box->border[RIGHT].width < box->descendant_x1; -} - - -/** - * Calculate scrollbar dimensions and positions for a box. - * - * \param box scrolling box - * \param padding_width scaled width of padding box - * \param padding_height scaled height of padding box - * \param w scaled scrollbar width - * \param vscroll updated to vertical scrollbar present - * \param hscroll updated to horizontal scrollbar present - * \param well_height updated to vertical well height - * \param bar_top updated to top position of vertical scrollbar - * \param bar_height updated to height of vertical scrollbar - * \param well_width updated to horizontal well width - * \param bar_left updated to left position of horizontal scrollbar - * \param bar_width updated to width of horizontal scrollbar - */ - -void box_scrollbar_dimensions(const struct box * const box, - const int padding_width, const int padding_height, const int w, - bool * const vscroll, bool * const hscroll, - int * const well_height, - int * const bar_top, int * const bar_height, - int * const well_width, - int * const bar_left, int * const bar_width) -{ - *vscroll = box_vscrollbar_present(box); - *hscroll = box_hscrollbar_present(box); - *well_height = padding_height - w - w; - *bar_top = 0; - *bar_height = *well_height; - if (box->descendant_y1 - box->descendant_y0 != 0) { - *bar_top = (float) *well_height * (float) box->scroll_y / - (float) (box->descendant_y1 - - box->descendant_y0); - *bar_height = (float) *well_height * (float) box->height / - (float) (box->descendant_y1 - - box->descendant_y0); - } - *well_width = padding_width - w - w - (*vscroll ? w : 0); - *bar_left = 0; - *bar_width = *well_width; - if (box->descendant_x1 - box->descendant_x0 != 0) { - *bar_left = (float) *well_width * (float) box->scroll_x / - (float) (box->descendant_x1 - - box->descendant_x0); - *bar_width = (float) *well_width * (float) box->width / - (float) (box->descendant_x1 - - box->descendant_x0); - } -} diff --git a/render/layout.c b/render/layout.c index 3ba9b038d..f2c0c158e 100644 --- a/render/layout.c +++ b/render/layout.c @@ -44,6 +44,7 @@ #include "content/content.h" #include "desktop/gui.h" #include "desktop/options.h" +#include "desktop/scroll.h" #include "render/box.h" #include "render/font.h" #include "render/form.h" @@ -1989,8 +1990,9 @@ bool layout_line(struct box *first, int *width, int *y, if (opt_maxwidth < opt_width) opt_maxwidth =opt_width; } - b->width = opt_maxwidth; + if (option_core_select_menu) + b->width += SCROLLBAR_WIDTH; } else { font_func->font_width(&fstyle, b->text, b->length, &b->width); @@ -2624,6 +2626,9 @@ struct box *layout_minmax_line(struct box *first, } b->width = opt_maxwidth; + if (option_core_select_menu) + b->width += SCROLLBAR_WIDTH; + } else { font_func->font_width(&fstyle, b->text, b->length, &b->width); diff --git a/render/layout.h b/render/layout.h index 2d07a40e9..30d9757f6 100644 --- a/render/layout.h +++ b/render/layout.h @@ -27,8 +27,6 @@ #ifndef _NETSURF_RENDER_LAYOUT_H_ #define _NETSURF_RENDER_LAYOUT_H_ -#define SCROLLBAR_WIDTH 16 - struct box; bool layout_document(struct content *content, int width, int height); -- cgit v1.2.3