From 811106028fd050cdee11fbc8732ba39f2de5e12a Mon Sep 17 00:00:00 2001 From: Phil Mellor Date: Wed, 11 Sep 2002 14:24:02 +0000 Subject: [project @ 2002-09-11 14:24:02 by monkeyson] RISC OS Wimp GUI. svn path=/import/netsurf/; revision=33 --- desktop/browser.c | 800 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 800 insertions(+) create mode 100644 desktop/browser.c (limited to 'desktop/browser.c') diff --git a/desktop/browser.c b/desktop/browser.c new file mode 100644 index 000000000..f21008abb --- /dev/null +++ b/desktop/browser.c @@ -0,0 +1,800 @@ +/** + * $Id: browser.c,v 1.1 2002/09/11 14:24:02 monkeyson Exp $ + */ + +#include "netsurf/riscos/font.h" +#include "netsurf/render/box.h" +#include "netsurf/render/layout.h" +#include "netsurf/render/css.h" +#include "netsurf/desktop/browser.h" +#include "netsurf/render/utils.h" +#include +#include +#include +#include + +struct box_selection +{ + struct box* box; + int actual_x; + int actual_y; + int plot_index; +}; + +void browser_window_text_selection(struct browser_window* bw, int click_x, int click_y, int click_type); +void browser_window_clear_text_selection(struct browser_window* bw); +void browser_window_change_text_selection(struct browser_window* bw, struct box_position* new_start, struct box_position* new_end); +void browser_window_redraw_boxes(struct browser_window* bw, struct box_position* start, struct box_position* end); +void browser_window_follow_link(struct browser_window* bw, + int click_x, int click_y, int click_type); + +void box_under_area(struct box* box, int x, int y, int ox, int oy, struct box_selection** found, int* count, int* plot_index); + + +void content_destroy(struct content* c) +{ + if (c == NULL) + return; + + switch (c->type) + { + case CONTENT_HTML: + /* free other memory here */ + break; + default: + break; + } + + c->main_fetch = fetch_cancel(c->main_fetch); + xfree(c); + + return; +} + +size_t content_html_receive_data(struct content* c, void* data, size_t size, size_t nmemb) +{ + size_t amount = nmemb; + int offset = 0; + size_t numInChunk = 2048 / size; /* process in 2k chunks */ + + if (numInChunk > nmemb) + numInChunk = nmemb; + else if (numInChunk <= (size_t)0) + numInChunk = 1; + + while (amount > 0) + { + htmlParseChunk(c->data.html.parser, (char*)data + (offset * size), numInChunk, 0); + offset += numInChunk; + amount -= numInChunk; + if (amount < numInChunk) + numInChunk = amount; + gui_multitask(); + } + + return size * nmemb; +} + +void set_content_html(struct content* c) +{ + c->type = CONTENT_HTML; + c->data.html.parser = htmlCreatePushParserCtxt(0, 0, "", 0, 0, XML_CHAR_ENCODING_8859_1); + c->data.html.document = NULL; + c->data.html.markup = NULL; + c->data.html.layout = NULL; + c->data.html.stylesheet = NULL; + c->data.html.style = NULL; + return; +} + +void content_html_reformat(struct content* c, int width) +{ + char* file; + struct css_selector* selector = xcalloc(1, sizeof(struct css_selector)); + + Log("content_html_reformat", "Starting stuff"); + c->data.html.layout = NULL; /* should be a freeing operation here */ + /* free other things too... */ + + Log("content_html_reformat", "Setting document to myDoc"); + c->data.html.document = c->data.html.parser->myDoc; + + /* skip to start of html */ + Log("content_html_reformat", "Skipping to html"); + if (c->data.html.document == NULL) + { + Log("content_html_reformat", "There is no document!"); + return; + } + for (c->data.html.markup = c->data.html.document->children; + c->data.html.markup != 0 && + c->data.html.markup->type != XML_ELEMENT_NODE; + c->data.html.markup = c->data.html.markup->next) + ; + + if (c->data.html.markup == 0) + { + Log("content_html_reformat", "No markup"); + return; + } + if (strcmp((const char *) c->data.html.markup->name, "html")) + { + Log("content_html_reformat", "Not html"); + return; + } + + Log("content_html_reformat", "Loading CSS"); + file = load(".Resources.CSS"); /*!!! not portable! !!!*/ + c->data.html.stylesheet = css_new_stylesheet(); + Log("content_html_reformat", "Parsing stylesheet"); + css_parse_stylesheet(c->data.html.stylesheet, file); + + Log("content_html_reformat", "Copying base style"); + c->data.html.style = xcalloc(1, sizeof(struct css_style)); + memcpy(c->data.html.style, &css_base_style, sizeof(struct css_style)); + + Log("content_html_reformat", "Creating box"); + c->data.html.layout = xcalloc(1, sizeof(struct box)); + c->data.html.layout->type = BOX_BLOCK; + c->data.html.layout->node = c->data.html.markup; + + Log("content_html_reformat", "XML to box"); + xml_to_box(c->data.html.markup, c->data.html.style, c->data.html.stylesheet, &selector, 0, c->data.html.layout, 0, 0); + Log("content_html_reformat", "Layout document"); + layout_document(c->data.html.layout->children, (unsigned long)width); + + /* can tidy up memory here? */ + + return; +} + +void browser_window_reformat(struct browser_window* bw) +{ + Log("browser_window_reformat", "Entering..."); + if (bw == NULL) + return; + if (bw->current_content == NULL) + return; + + switch (bw->current_content->type) + { + case CONTENT_HTML: + Log("browser_window_reformat", "HTML content."); + browser_window_set_status(bw, "Formatting page..."); + content_html_reformat(bw->current_content, bw->format_width); + Log("browser_window_reformat", "Content reformatted"); + if (bw->current_content->data.html.layout != NULL) + { + Log("browser_window_reformat", "Setting extent"); + gui_window_set_extent(bw->window, bw->current_content->data.html.layout->children->width, bw->current_content->data.html.layout->children->height); + Log("browser_window_reformat", "Setting scroll"); + gui_window_set_scroll(bw->window, 0, 0); + Log("browser_window_reformat", "Redraw window"); + gui_window_redraw_window(bw->window); + Log("browser_window_reformat", "Complete"); + browser_window_set_status(bw, "Format complete."); + } + else + { + Log("browser_window_reformat", "This isn't html"); + browser_window_set_status(bw, "This is not HTML!"); + content_destroy(bw->current_content); + bw->current_content = NULL; + } + break; + default: + Log("browser_window_reformat", "Unknown content type"); + break; + } +} + +/* create a new history item */ +struct history* history_create(char* desc, char* url) +{ + struct history* h = xcalloc(1, sizeof(struct history)); + h->description = xstrdup(desc); + h->url = xstrdup(url); + return h; +} + +/* remember a new page after the current one. anything remembered after the + current page is forgotten. */ +void history_remember(struct history* current, char* desc, char* url) +{ + struct history* h; + assert(current != NULL); + + /* forget later history items */ + h = current->later; + while (h != NULL) + { + struct history* hh; + hh = h; + h = h->later; + + if (hh->description != NULL) + xfree(hh->description); + if (hh->url != NULL) + xfree(hh->url); + + xfree(hh); + } + + current->later = history_create(desc, url); + return; +} + + +struct browser_window* create_browser_window(int flags, int width, int height) +{ + struct browser_window* bw; + bw = (struct browser_window*) xcalloc(1, sizeof(struct browser_window)); + + bw->flags = flags; + bw->format_width = width; + bw->format_height = height; + + bw->scale.mult = 1; + bw->scale.div = 1; + + bw->current_content = NULL; + bw->future_content = NULL; + bw->history = NULL; + + bw->url = NULL; + bw->title = xstrdup("NetSurf"); + + bw->window = create_gui_browser_window(bw); + + return bw; +} + +void browser_window_set_status(struct browser_window* bw, char* text) +{ + if (bw->window != NULL) + gui_window_set_status(bw->window, text); +} + +void browser_window_destroy(struct browser_window* bw) +{ + if (bw == NULL) + return; + + content_destroy(bw->current_content); + content_destroy(bw->future_content); + + if (bw->history != NULL) + { + struct history* current; + + while (current->earlier != NULL) + current = current->earlier; + + while (current != NULL) + { + struct history* hh; + hh = current; + current = current->later; + + if (hh->description != NULL) + xfree(hh->description); + if (hh->url != NULL) + xfree(hh->url); + + xfree(hh); + } + } + + xfree(bw->url); + xfree(bw->title); + + gui_window_destroy(bw->window); + + xfree(bw); + + return; +} + +void browser_window_open_location(struct browser_window* bw, char* url) +{ + struct fetch_request* req; + + if (bw == NULL) + return; + + if (bw->future_content != NULL) + content_destroy(bw->future_content); + + req = xcalloc(1, sizeof(struct fetch_request)); + req->type = REQUEST_FROM_BROWSER; + req->requestor.browser = bw; + + bw->future_content = (struct content*) xcalloc(1, sizeof(struct content)); + bw->future_content->main_fetch = create_fetch(url, bw->url, 0, req); + + return; +} + +int browser_window_message(struct browser_window* bw, struct browser_message* msg) +{ + gui_safety previous_safety; + + switch (msg->type) + { + case msg_FETCH_SENDING: + browser_window_set_status(bw, "Sending request..."); + break; + + case msg_FETCH_WAITING: + browser_window_set_status(bw, "Waiting for reply..."); + break; + + case msg_FETCH_FETCH_INFO: + browser_window_set_status(bw, "Request received..."); + if (msg->f == bw->future_content->main_fetch) + { + switch (msg->data.fetch_info.type) + { + case type_HTML: + set_content_html(bw->future_content); + break; + default: + return 1; + } + } + break; + + case msg_FETCH_DATA: + browser_window_set_status(bw, "Data received..."); + if (msg->f == bw->future_content->main_fetch) + content_html_receive_data(bw->future_content, msg->data.fetch_data.block, sizeof(char), msg->data.fetch_data.block_size); + break; + + case msg_FETCH_ABORT: + browser_window_set_status(bw, "Request failed."); + if (msg->f == bw->future_content->main_fetch) + { + bw->future_content->main_fetch = NULL; + content_destroy(bw->future_content); + bw->future_content = NULL; + } + break; + + case msg_FETCH_FINISHED: + browser_window_set_status(bw, "Request complete."); + if (msg->f == bw->future_content->main_fetch) + { + struct gui_message gmsg; + if (bw->future_content->main_fetch->location != NULL) + xfree(bw->url); + bw->url = xstrdup(bw->future_content->main_fetch->location); + + gmsg.type = msg_SET_URL; + gmsg.data.set_url.url = bw->url; + gui_window_message(bw->window, &gmsg); + + htmlParseChunk(bw->future_content->data.html.parser, "", 0, 1); + bw->future_content->main_fetch = NULL; + previous_safety = gui_window_set_redraw_safety(bw->window, UNSAFE); + content_destroy(bw->current_content); + bw->current_content = bw->future_content; + bw->future_content = NULL; + browser_window_reformat(bw); + gui_window_set_redraw_safety(bw->window, previous_safety); + } + break; + + default: + browser_window_set_status(bw, "???"); + break; + } + + return 0; +} + +int browser_window_action(struct browser_window* bw, struct browser_action* act) +{ + switch (act->type) + { + case act_MOUSE_AT: + browser_window_follow_link(bw, act->data.mouse.x, act->data.mouse.y, 0); + break; + case act_MOUSE_CLICK: + break; + case act_CLEAR_SELECTION: + browser_window_text_selection(bw, act->data.mouse.x, act->data.mouse.y, 0); + break; + case act_START_NEW_SELECTION: + browser_window_text_selection(bw, act->data.mouse.x, act->data.mouse.y, 1); + break; + case act_ALTER_SELECTION: + browser_window_text_selection(bw, act->data.mouse.x, act->data.mouse.y, 2); + break; + case act_FOLLOW_LINK: + browser_window_follow_link(bw, act->data.mouse.x, act->data.mouse.y, 1); + break; + case act_FOLLOW_LINK_NEW_WINDOW: + browser_window_follow_link(bw, act->data.mouse.x, act->data.mouse.y, 2); + break; + default: + break; + } + return 0; +} + +void box_under_area(struct box* box, int x, int y, int ox, int oy, + struct box_selection** found, int* count, int* plot_index) +{ + struct box* c; + + if (box == NULL) + return; + + *plot_index = *plot_index + 1; + + if (x >= box->x + ox && x <= box->x + ox + box->width && + y >= box->y + oy && y <= box->y + oy + box->height) + { + *found = xrealloc(*found, sizeof(struct box_selection) * (*count + 1)); + (*found)[*count].box = box; + (*found)[*count].actual_x = box->x + ox; + (*found)[*count].actual_y = box->y + oy; + (*found)[*count].plot_index = *plot_index; + *count = *count + 1; + } + + for (c = box->children; c != 0; c = c->next) + if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) + box_under_area(c, x, y, box->x + ox, box->y + oy, found, count, plot_index); + + for (c = box->float_children; c != 0; c = c->next_float) + box_under_area(c, x, y, box->x + ox, box->y + oy, found, count, plot_index); + + return; +} + +void browser_window_follow_link(struct browser_window* bw, + int click_x, int click_y, int click_type) +{ + struct box_selection* click_boxes; + int found, plot_index; + int i; + int done = 0; + + found = 0; + click_boxes = NULL; + plot_index = 0; + + box_under_area(bw->current_content->data.html.layout->children, + click_x, click_y, 0, 0, &click_boxes, &found, &plot_index); + + if (found == 0) + return; + + for (i = found - 1; i >= 0; i--) + { + if (click_boxes[i].box->href != NULL) + { + if (click_type == 1) + browser_window_open_location(bw, (char*) click_boxes[i].box->href); + else if (click_type == 2) + { + struct browser_window* bw_new; + bw_new = create_browser_window(browser_TITLE | browser_TOOLBAR + | browser_SCROLL_X_NONE | browser_SCROLL_Y_ALWAYS, 640, 480); + gui_window_show(bw_new->window); + if (bw->url != NULL) + bw_new->url = xstrdup(bw->url); + browser_window_open_location(bw_new, (char*) click_boxes[i].box->href); + } + else if (click_type == 0) + { + browser_window_set_status(bw, (char*) click_boxes[i].box->href); + done = 1; + } + i = -1; + } + } + + if (click_type == 0 && done == 0) + browser_window_set_status(bw, ""); + + free(click_boxes); + + return; +} + +void browser_window_text_selection(struct browser_window* bw, + int click_x, int click_y, int click_type) +{ + struct box_selection* click_boxes; + int found, plot_index; + int i; + + if (click_type == 0 /* click_CLEAR_SELECTION */ ) + { + browser_window_clear_text_selection(bw); + return; + } + + found = 0; + click_boxes = NULL; + plot_index = 0; + + box_under_area(bw->current_content->data.html.layout->children, + click_x, click_y, 0, 0, &click_boxes, &found, &plot_index); + + if (found == 0) + return; + + for (i = found - 1; i >= 0; i--) + { + if (click_boxes[i].box->type == BOX_INLINE) + { + struct box_position new_pos; + struct box_position* start; + struct box_position* end; + int click_char_offset, click_pixel_offset; + + /* shortcuts */ + start = &(bw->current_content->data.html.text_selection.start); + end = &(bw->current_content->data.html.text_selection.end); + + font_position_in_string(click_boxes[i].box->text, + click_boxes[i].box->style, click_boxes[i].box->length, + click_x - click_boxes[i].actual_x, + &click_char_offset, &click_pixel_offset); + + new_pos.box = click_boxes[i].box; + new_pos.actual_box_x = click_boxes[i].actual_x; + new_pos.actual_box_y = click_boxes[i].actual_y; + new_pos.plot_index = click_boxes[i].plot_index; + new_pos.char_offset = click_char_offset; + new_pos.pixel_offset = click_pixel_offset; + + if (click_type == 1 /* click_START_SELECTION */ ) + { + /* update both start and end */ + browser_window_clear_text_selection(bw); + bw->current_content->data.html.text_selection.altering = alter_UNKNOWN; + bw->current_content->data.html.text_selection.selected = 1; + memcpy(start, &new_pos, sizeof(struct box_position)); + memcpy(end, &new_pos, sizeof(struct box_position)); + i = -1; + } + else if (bw->current_content->data.html.text_selection.selected == 1 && + click_type == 2 /* click_ALTER_SELECTION */) + { + /* alter selection */ + + if (bw->current_content->data.html.text_selection.altering + != alter_UNKNOWN) + { + if (bw->current_content->data.html.text_selection.altering + == alter_START) + { + if (box_position_gt(&new_pos,end)) + { + bw->current_content->data.html.text_selection.altering + = alter_END; + browser_window_change_text_selection(bw, end, &new_pos); + } + else + browser_window_change_text_selection(bw, &new_pos, end); + } + else + { + if (box_position_lt(&new_pos,start)) + { + bw->current_content->data.html.text_selection.altering + = alter_START; + browser_window_change_text_selection(bw, &new_pos, start); + } + else + browser_window_change_text_selection(bw, start, &new_pos); + } + i = -1; + } + else + { + /* work out whether the start or end is being dragged */ + + int click_start_distance = 0; + int click_end_distance = 0; + + int inside_block = 0; + int before_start = 0; + int after_end = 0; + + if (box_position_lt(&new_pos, start)) + before_start = 1; + + if (box_position_gt(&new_pos, end)) + after_end = 1; + + if (!box_position_lt(&new_pos, start) + && !box_position_gt(&new_pos, end)) + inside_block = 1; + + if (inside_block == 1) + { + click_start_distance = box_position_distance(start, &new_pos); + click_end_distance = box_position_distance(end, &new_pos); + } + + if (before_start == 1 + || (after_end == 0 && inside_block == 1 + && click_start_distance < click_end_distance)) + { + /* alter the start position */ + bw->current_content->data.html.text_selection.altering + = alter_START; + browser_window_change_text_selection(bw, &new_pos, end); + i = -1; + } + else if (after_end == 1 + || (before_start == 0 && inside_block == 1 + && click_start_distance >= click_end_distance)) + { + /* alter the end position */ + bw->current_content->data.html.text_selection.altering = alter_END; + browser_window_change_text_selection(bw, start, &new_pos); + i = -1; + } + } + } + } + } + + free(click_boxes); + + return; +} + +void browser_window_clear_text_selection(struct browser_window* bw) +{ + struct box_position* old_start; + struct box_position* old_end; + + old_start = &(bw->current_content->data.html.text_selection.start); + old_end = &(bw->current_content->data.html.text_selection.end); + + if (bw->current_content->data.html.text_selection.selected == 1) + { + bw->current_content->data.html.text_selection.selected = 0; + browser_window_redraw_boxes(bw, old_start, old_end); + } + + bw->current_content->data.html.text_selection.altering = alter_UNKNOWN; +} + +void browser_window_change_text_selection(struct browser_window* bw, + struct box_position* new_start, struct box_position* new_end) +{ + struct box_position start; + struct box_position end; + + memcpy(&start, &(bw->current_content->data.html.text_selection.start), sizeof(struct box_position)); + memcpy(&end, &(bw->current_content->data.html.text_selection.end), sizeof(struct box_position)); + + if (!box_position_eq(new_start, &start)) + { + if (box_position_lt(new_start, &start)) + browser_window_redraw_boxes(bw, new_start, &start); + else + browser_window_redraw_boxes(bw, &start, new_start); + memcpy(&start, new_start, sizeof(struct box_position)); + } + + if (!box_position_eq(new_end, &end)) + { + if (box_position_lt(new_end, &end)) + browser_window_redraw_boxes(bw, new_end, &end); + else + browser_window_redraw_boxes(bw, &end, new_end); + memcpy(&end, new_end, sizeof(struct box_position)); + } + + memcpy(&(bw->current_content->data.html.text_selection.start), &start, sizeof(struct box_position)); + memcpy(&(bw->current_content->data.html.text_selection.end), &end, sizeof(struct box_position)); + + bw->current_content->data.html.text_selection.selected = 1; +} + + +int box_position_lt(struct box_position* x, struct box_position* y) +{ + return (x->plot_index < y->plot_index || + (x->plot_index == y->plot_index && x->char_offset < y->char_offset)); +} + +int box_position_gt(struct box_position* x, struct box_position* y) +{ + return (x->plot_index > y->plot_index || + (x->plot_index == y->plot_index && x->char_offset > y->char_offset)); +} + +int box_position_eq(struct box_position* x, struct box_position* y) +{ + return (x->plot_index == y->plot_index && x->char_offset == y->char_offset); +} + +int box_position_distance(struct box_position* x, struct box_position* y) +{ + int dx = (y->actual_box_x + y->pixel_offset) + - (x->actual_box_x + x->pixel_offset); + int dy = (y->actual_box_y + y->box->height / 2) + - (x->actual_box_y + x->box->height / 2); + return dx*dx + dy*dy; +} + +int redraw_min_x = INT_MAX; +int redraw_min_y = INT_MAX; +int redraw_max_x = INT_MIN; +int redraw_max_y = INT_MIN; + +int redraw_box_list(struct browser_window* bw, struct box* current, + int x, int y, struct box_position* start, struct box_position* end, + int* plot) +{ + + struct box* c; + + if (current == start->box) + *plot = 1; + + if (*plot >= 1 && current->type == BOX_INLINE) + { + int minx = x + current->x; + int miny = y + current->y; + int maxx = x + current->x + current->width; + int maxy = y + current->y + current->height; + + if (minx < redraw_min_x) + redraw_min_x = minx; + if (miny < redraw_min_y) + redraw_min_y = miny; + if (maxx > redraw_max_x) + redraw_max_x = maxx; + if (maxy > redraw_max_y) + redraw_max_y = maxy; + + *plot = 2; + } + + if (current == end->box) + return 1; + + for (c = current->children; c != 0; c = c->next) + if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) + if (redraw_box_list(bw, c, x + current->x, y + current->y, + start, end, plot) == 1) + return 1; + + for (c = current->float_children; c != 0; c = c->next_float) + if (redraw_box_list(bw, c, x + current->x, y + current->y, + start, end, plot) == 1) + return 1; + + return 0; +} + +void browser_window_redraw_boxes(struct browser_window* bw, struct box_position* start, struct box_position* end) +{ + int plot = 0; + + if (box_position_eq(start, end)) + return; + + redraw_min_x = INT_MAX; + redraw_min_y = INT_MAX; + redraw_max_x = INT_MIN; + redraw_max_y = INT_MIN; + + redraw_box_list(bw, bw->current_content->data.html.layout, + 0,0, start, end, &plot); + + if (plot == 2) + gui_window_redraw(bw->window, redraw_min_x, redraw_min_y, + redraw_max_x, redraw_max_y); +} -- cgit v1.2.3