diff options
Diffstat (limited to 'desktop')
41 files changed, 5291 insertions, 2692 deletions
diff --git a/desktop/Makefile b/desktop/Makefile index 2dcd61611..ffd932177 100644 --- a/desktop/Makefile +++ b/desktop/Makefile @@ -3,7 +3,7 @@ S_DESKTOP := cookie_manager.c knockout.c hotlist.c mouse.c \ plot_style.c print.c search.c searchweb.c scrollbar.c \ sslcert_viewer.c textarea.c version.c system_colour.c \ - global_history.c treeview.c + local_history.c global_history.c treeview.c S_DESKTOP := $(addprefix desktop/,$(S_DESKTOP)) diff --git a/desktop/browser.c b/desktop/browser.c index d3648d76a..1c8aa95fa 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -56,9 +56,9 @@ #include "content/hlcache.h" #include "content/urldb.h" #include "css/utils.h" -#include "render/form_internal.h" -#include "render/html.h" -#include "render/box.h" +#include "html/form_internal.h" +#include "html/html.h" +#include "html/box.h" #include "javascript/js.h" #include "desktop/browser_history.h" @@ -87,7 +87,6 @@ * \param x Updated to x-coord of top left of scrollbar widget * \param y Updated to y-coord of top left of scrollbar widget */ - static inline void browser_window_get_scrollbar_pos(struct browser_window *bw, bool horizontal, int *x, int *y) { @@ -129,6 +128,7 @@ browser_window_get_name(struct browser_window *bw, const char **out_name) return NSERROR_OK; } + /* exported interface, documented in browser.h */ nserror browser_window_set_name(struct browser_window *bw, const char *name) @@ -153,9 +153,13 @@ browser_window_set_name(struct browser_window *bw, const char *name) return NSERROR_OK; } + /* exported interface, documented in browser.h */ -bool browser_window_redraw(struct browser_window *bw, int x, int y, - const struct rect *clip, const struct redraw_context *ctx) +bool +browser_window_redraw(struct browser_window *bw, + int x, int y, + const struct rect *clip, + const struct redraw_context *ctx) { struct redraw_context new_ctx = *ctx; int width = 0; @@ -164,29 +168,28 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, content_type content_type; struct content_redraw_data data; struct rect content_clip; + nserror res; if (bw == NULL) { - LOG("NULL browser window"); + NSLOG(netsurf, INFO, "NULL browser window"); return false; } - if (bw->current_content == NULL && bw->children == NULL) { + if ((bw->current_content == NULL) && + (bw->children == NULL)) { /* Browser window has no content, render blank fill */ - ctx->plot->clip(clip); - return ctx->plot->rectangle(clip->x0, clip->y0, - clip->x1, clip->y1, - plot_style_fill_white); + ctx->plot->clip(ctx, clip); + return (ctx->plot->rectangle(ctx, plot_style_fill_white, clip) == NSERROR_OK); } /* Browser window has content OR children (frames) */ - if ((bw->window != NULL) && (ctx->plot->option_knockout)) { /* Root browser window: start knockout */ knockout_plot_start(ctx, &new_ctx); } - new_ctx.plot->clip(clip); + new_ctx.plot->clip(ctx, clip); /* Handle redraw of any browser window children */ if (bw->children) { @@ -194,11 +197,12 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, int cur_child; int children = bw->rows * bw->cols; - if (bw->window != NULL) + if (bw->window != NULL) { /* Root browser window; start with blank fill */ - plot_ok &= new_ctx.plot->rectangle(clip->x0, clip->y0, - clip->x1, clip->y1, - plot_style_fill_white); + plot_ok &= (new_ctx.plot->rectangle(ctx, + plot_style_fill_white, + clip) == NSERROR_OK); + } /* Loop through all children of bw */ for (cur_child = 0; cur_child < children; cur_child++) { @@ -225,7 +229,7 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, /* Skip this frame if it lies outside clip rectangle */ if (content_clip.x0 >= content_clip.x1 || - content_clip.y0 >= content_clip.y1) + content_clip.y0 >= content_clip.y1) continue; /* Redraw frame */ @@ -235,10 +239,11 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, } /* Nothing else to redraw for browser windows with children; - * cleanup and return */ + * cleanup and return + */ if (bw->window != NULL && ctx->plot->option_knockout) { /* Root browser window: knockout end */ - knockout_plot_end(); + knockout_plot_end(ctx); } return plot_ok; @@ -254,8 +259,9 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, /* Non-HTML may not fill viewport to extents, so plot white * background fill */ - plot_ok &= new_ctx.plot->rectangle(clip->x0, clip->y0, - clip->x1, clip->y1, plot_style_fill_white); + plot_ok &= (new_ctx.plot->rectangle(&new_ctx, + plot_style_fill_white, + clip) == NSERROR_OK); } /* Set up content redraw data */ @@ -290,7 +296,7 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, &content_clip, &new_ctx); /* Back to full clip rect */ - new_ctx.plot->clip(clip); + new_ctx.plot->clip(&new_ctx, clip); if (!bw->window) { /* Render scrollbars */ @@ -298,32 +304,39 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, if (bw->scroll_x != NULL) { browser_window_get_scrollbar_pos(bw, true, &off_x, &off_y); - plot_ok &= scrollbar_redraw(bw->scroll_x, + res = scrollbar_redraw(bw->scroll_x, x + off_x, y + off_y, clip, bw->scale, &new_ctx); + if (res != NSERROR_OK) { + plot_ok = false; + } } if (bw->scroll_y != NULL) { browser_window_get_scrollbar_pos(bw, false, &off_x, &off_y); - plot_ok &= scrollbar_redraw(bw->scroll_y, + res = scrollbar_redraw(bw->scroll_y, x + off_x, y + off_y, clip, bw->scale, &new_ctx); + if (res != NSERROR_OK) { + plot_ok = false; + } } } if (bw->window != NULL && ctx->plot->option_knockout) { /* Root browser window: end knockout */ - knockout_plot_end(); + knockout_plot_end(ctx); } return plot_ok; } + /* exported interface, documented in browser.h */ bool browser_window_redraw_ready(struct browser_window *bw) { if (bw == NULL) { - LOG("NULL browser window"); + NSLOG(netsurf, INFO, "NULL browser window"); return false; } else if (bw->current_content != NULL) { /* Can't render locked contents */ @@ -333,6 +346,7 @@ bool browser_window_redraw_ready(struct browser_window *bw) return true; } + /* exported interface, documented in browser_private.h */ void browser_window_update_extent(struct browser_window *bw) { @@ -344,9 +358,13 @@ void browser_window_update_extent(struct browser_window *bw) browser_window_handle_scrollbars(bw); } + /* exported interface, documented in browser.h */ -void browser_window_get_position(struct browser_window *bw, bool root, - int *pos_x, int *pos_y) +void +browser_window_get_position(struct browser_window *bw, + bool root, + int *pos_x, + int *pos_y) { *pos_x = 0; *pos_y = 0; @@ -386,6 +404,7 @@ void browser_window_get_position(struct browser_window *bw, bool root, } } + /* exported interface, documented in browser.h */ void browser_window_set_position(struct browser_window *bw, int x, int y) { @@ -396,28 +415,31 @@ void browser_window_set_position(struct browser_window *bw, int x, int y) bw->x = x; bw->y = y; } else { - LOG("Asked to set position of front end window."); + NSLOG(netsurf, INFO, + "Asked to set position of front end window."); assert(0); } } /* exported interface, documented in browser.h */ -void browser_window_set_drag_type(struct browser_window *bw, - browser_drag_type type, const struct rect *rect) +void +browser_window_set_drag_type(struct browser_window *bw, + browser_drag_type type, + const struct rect *rect) { struct browser_window *top_bw = browser_window_get_root(bw); gui_drag_type gtype; - bw->drag_type = type; + bw->drag.type = type; if (type == DRAGGING_NONE) { - top_bw->drag_window = NULL; + top_bw->drag.window = NULL; } else { - top_bw->drag_window = bw; + top_bw->drag.window = bw; switch (type) { case DRAGGING_SELECTION: - /* TODO: tell front end */ + /** \todo tell front end */ return; case DRAGGING_SCR_X: case DRAGGING_SCR_Y: @@ -436,7 +458,7 @@ void browser_window_set_drag_type(struct browser_window *bw, /* exported interface, documented in browser.h */ browser_drag_type browser_window_get_drag_type(struct browser_window *bw) { - return bw->drag_type; + return bw->drag.type; } /* exported interface, documented in browser.h */ @@ -565,36 +587,33 @@ static void browser_window_set_selection(struct browser_window *bw, top->selection.read_only = read_only; } -/* exported interface, documented in browser.h */ -void browser_window_scroll_visible(struct browser_window *bw, - const struct rect *rect) -{ - assert(bw != NULL); +/** + * Set the scroll position of a browser window. + * + * scrolls the viewport to ensure the specified rectangle of the + * content is shown. + * + * \param bw window to scroll + * \param rect The rectangle to ensure is shown. + * \return NSERROR_OK on success or apropriate error code. + */ +static nserror +browser_window_set_scroll(struct browser_window *bw, + const struct rect *rect) +{ if (bw->window != NULL) { - /* Front end window */ - guit->window->scroll_visible(bw->window, - rect->x0, rect->y0, rect->x1, rect->y1); - } else { - /* Core managed browser window */ - if (bw->scroll_x != NULL) - scrollbar_set(bw->scroll_x, rect->x0, false); - if (bw->scroll_y != NULL) - scrollbar_set(bw->scroll_y, rect->y0, false); + return guit->window->set_scroll(bw->window, rect); } -} -/* exported interface, documented in browser.h */ -void browser_window_set_scroll(struct browser_window *bw, int x, int y) -{ - if (bw->window != NULL) { - guit->window->set_scroll(bw->window, x, y); - } else { - if (bw->scroll_x != NULL) - scrollbar_set(bw->scroll_x, x, false); - if (bw->scroll_y != NULL) - scrollbar_set(bw->scroll_y, y, false); + if (bw->scroll_x != NULL) { + scrollbar_set(bw->scroll_x, rect->x0, false); + } + if (bw->scroll_y != NULL) { + scrollbar_set(bw->scroll_y, rect->y0, false); } + + return NSERROR_OK; } /** @@ -793,7 +812,7 @@ nserror browser_window_debug(struct browser_window *bw, enum content_debug op) static bool slow_script(void *ctx) { static int count = 0; - LOG("Continuing execution %d", count); + NSLOG(netsurf, INFO, "Continuing execution %d", count); count++; if (count > 1) { count = 0; @@ -868,7 +887,7 @@ nserror browser_window_create(enum browser_window_create_flags flags, } if (url != NULL) { - enum browser_window_nav_flags nav_flags = BW_NAVIGATE_NONE; + enum browser_window_nav_flags nav_flags = BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE; if (flags & BW_CREATE_UNVERIFIABLE) nav_flags |= BW_NAVIGATE_UNVERIFIABLE; if (flags & BW_CREATE_HISTORY) @@ -920,7 +939,7 @@ nserror browser_window_initialise_common(enum browser_window_create_flags flags, /* window characteristics */ bw->refresh_interval = -1; - bw->drag_type = DRAGGING_NONE; + bw->drag.type = DRAGGING_NONE; bw->scroll_x = NULL; bw->scroll_y = NULL; @@ -928,10 +947,10 @@ nserror browser_window_initialise_common(enum browser_window_create_flags flags, bw->focus = NULL; /* initialise status text cache */ - bw->status_text = NULL; - bw->status_text_len = 0; - bw->status_match = 0; - bw->status_miss = 0; + bw->status.text = NULL; + bw->status.text_len = 0; + bw->status.match = 0; + bw->status.miss = 0; return NSERROR_OK; } @@ -964,11 +983,12 @@ browser_window_download(struct browser_window *bw, /* no internal handler for this type, call out to frontend */ error = guit->misc->launch_url(url); } else if (error != NSERROR_OK) { - LOG("Failed to fetch download: %d", error); + NSLOG(netsurf, INFO, "Failed to fetch download: %d", error); } else { error = download_context_create(l, root->window); if (error != NSERROR_OK) { - LOG("Failed creating download context: %d", error); + NSLOG(netsurf, INFO, + "Failed creating download context: %d", error); llcache_handle_abort(l); llcache_handle_release(l); } @@ -1040,63 +1060,71 @@ static void browser_window_stop_throbber(struct browser_window *bw) /** * Callback for fetchcache() for browser window favicon fetches. + * + * \param c content handle of favicon + * \param event The event to process + * \param pw a context containing the browser window + * \return NSERROR_OK on success else appropriate error code. */ -static nserror browser_window_favicon_callback(hlcache_handle *c, - const hlcache_event *event, void *pw) +static nserror +browser_window_favicon_callback(hlcache_handle *c, + const hlcache_event *event, + void *pw) { struct browser_window *bw = pw; switch (event->type) { case CONTENT_MSG_DONE: - if (bw->current_favicon != NULL) { + if (bw->favicon.current != NULL) { content_status status = - content_get_status(bw->current_favicon); + content_get_status(bw->favicon.current); if ((status == CONTENT_STATUS_READY) || (status == CONTENT_STATUS_DONE)) - content_close(bw->current_favicon); + content_close(bw->favicon.current); - hlcache_handle_release(bw->current_favicon); + hlcache_handle_release(bw->favicon.current); } - bw->current_favicon = c; - bw->loading_favicon = NULL; + bw->favicon.current = c; + bw->favicon.loading = NULL; /* content_get_bitmap on the hlcache_handle should give - * us the favicon bitmap at this point + * the favicon bitmap at this point */ guit->window->set_icon(bw->window, c); break; case CONTENT_MSG_ERROR: + case CONTENT_MSG_ERRORCODE: /* clean up after ourselves */ - if (c == bw->loading_favicon) - bw->loading_favicon = NULL; - else if (c == bw->current_favicon) { - bw->current_favicon = NULL; + if (c == bw->favicon.loading) { + bw->favicon.loading = NULL; + } else if (c == bw->favicon.current) { + bw->favicon.current = NULL; } hlcache_handle_release(c); - if (bw->failed_favicon == false) { + if (bw->favicon.failed == false) { nsurl *nsref = NULL; nsurl *nsurl; nserror error; - bw->failed_favicon = true; + bw->favicon.failed = true; error = nsurl_create("resource:favicon.ico", &nsurl); if (error != NSERROR_OK) { - LOG("Unable to create default location url"); + NSLOG(netsurf, INFO, + "Unable to create default location url"); } else { - hlcache_handle_retrieve(nsurl, HLCACHE_RETRIEVE_SNIFF_TYPE, nsref, NULL, browser_window_favicon_callback, bw, NULL, CONTENT_IMAGE, - &bw->loading_favicon); + &bw->favicon.loading); nsurl_unref(nsurl); } @@ -1110,8 +1138,18 @@ static nserror browser_window_favicon_callback(hlcache_handle *c, return NSERROR_OK; } -static void browser_window_update_favicon(hlcache_handle *c, - struct browser_window *bw, struct content_rfc5988_link *link) + +/** + * update the favicon associated with the browser window + * + * \param c the page content handle. + * \param bw A top level browser window. + * \param link A link context or NULL to attempt fallback scanning. + */ +static void +browser_window_update_favicon(hlcache_handle *c, + struct browser_window *bw, + struct content_rfc5988_link *link) { nsurl *nsref = NULL; nsurl *nsurl; @@ -1125,10 +1163,10 @@ static void browser_window_update_favicon(hlcache_handle *c, return; /* already fetching the favicon - use that */ - if (bw->loading_favicon != NULL) + if (bw->favicon.loading != NULL) return; - bw->failed_favicon = false; + bw->favicon.failed = false; if (link == NULL) { /* Look for "icon" */ @@ -1165,11 +1203,12 @@ static void browser_window_update_favicon(hlcache_handle *c, /* no favicon via link, try for the default location */ error = nsurl_join(nsurl, "/favicon.ico", &nsurl); } else { - bw->failed_favicon = true; + bw->favicon.failed = true; error = nsurl_create("resource:favicon.ico", &nsurl); } if (error != NSERROR_OK) { - LOG("Unable to create default location url"); + NSLOG(netsurf, INFO, + "Unable to create default location url"); return; } } else { @@ -1177,14 +1216,16 @@ static void browser_window_update_favicon(hlcache_handle *c, } if (link == NULL) { - LOG("fetching general favicon from '%s'", nsurl_access(nsurl)); + NSLOG(netsurf, INFO, "fetching general favicon from '%s'", + nsurl_access(nsurl)); } else { - LOG("fetching favicon rel:%s '%s'", lwc_string_data(link->rel), nsurl_access(nsurl)); + NSLOG(netsurf, INFO, "fetching favicon rel:%s '%s'", + lwc_string_data(link->rel), nsurl_access(nsurl)); } hlcache_handle_retrieve(nsurl, HLCACHE_RETRIEVE_SNIFF_TYPE, nsref, NULL, browser_window_favicon_callback, - bw, NULL, CONTENT_IMAGE, &bw->loading_favicon); + bw, NULL, CONTENT_IMAGE, &bw->favicon.loading); nsurl_unref(nsurl); } @@ -1307,11 +1348,14 @@ static void browser_window_convert_to_download(struct browser_window *bw, /** * Browser window content event callback handler. */ -static nserror browser_window_callback(hlcache_handle *c, - const hlcache_event *event, void *pw) +static nserror +browser_window_callback(hlcache_handle *c, + const hlcache_event *event, + void *pw) { struct browser_window *bw = pw; nserror res = NSERROR_OK; + float sx, sy; switch (event->type) { case CONTENT_MSG_DOWNLOAD: @@ -1365,19 +1409,6 @@ static nserror browser_window_callback(hlcache_handle *c, browser_window_get_dimensions(bw, &width, &height, true); content_reformat(c, false, width, height); - browser_window_remove_caret(bw, false); - - if (bw->window != NULL) { - guit->window->new_content(bw->window); - - browser_window_refresh_url_bar(bw); - } - - /* new content; set scroll_to_top */ - browser_window_update(bw, true); - content_open(c, bw, 0, 0); - browser_window_set_status(bw, content_get_status_message(c)); - /* history */ if (bw->history_add && bw->history) { nsurl *url = hlcache_handle_get_url(c); @@ -1410,10 +1441,27 @@ static nserror browser_window_callback(hlcache_handle *c, * all newly visited URLs. With the history_add call * after, we only leak the thumbnails when urldb does * not add the URL. + * + * Also, since browser_window_history_add can create + * a thumbnail (content_redraw), we need to do it after + * content_reformat. */ browser_window_history_add(bw, c, bw->frag_id); } + browser_window_remove_caret(bw, false); + + if (bw->window != NULL) { + guit->window->new_content(bw->window); + + browser_window_refresh_url_bar(bw); + } + + /* new content; set scroll_to_top */ + browser_window_update(bw, true); + content_open(c, bw, 0, 0); + browser_window_set_status(bw, content_get_status_message(c)); + /* frames */ if ((content_get_type(c) == CONTENT_HTML) && (html_get_frameset(c) != NULL)) { @@ -1442,6 +1490,19 @@ static nserror browser_window_callback(hlcache_handle *c, browser_window_stop_throbber(bw); browser_window_update_favicon(c, bw, NULL); + if (browser_window_history_get_scroll(bw, &sx, &sy) == NSERROR_OK) { + int scrollx = (int)((float)content_get_width(bw->current_content) * sx); + int scrolly = (int)((float)content_get_height(bw->current_content) * sy); + struct rect rect; + rect.x0 = rect.x1 = scrollx; + rect.y0 = rect.y1 = scrolly; + if (browser_window_set_scroll(bw, &rect) != NSERROR_OK) { + NSLOG(netsurf, WARNING, + "Unable to set browser scroll offsets to %d by %d", + scrollx, scrolly); + } + } + browser_window_history_update(bw, c); hotlist_update_url(hlcache_handle_get_url(c)); @@ -1571,25 +1632,28 @@ static nserror browser_window_callback(hlcache_handle *c, break; case CONTENT_MSG_SCROLL: + { + struct rect rect = { + .x0 = event->data.scroll.x0, + .y0 = event->data.scroll.y0, + }; + /* Content wants to be scrolled */ - if (bw->current_content != c) + if (bw->current_content != c) { break; + } if (event->data.scroll.area) { - struct rect rect = { - .x0 = event->data.scroll.x0, - .y0 = event->data.scroll.y0, - .x1 = event->data.scroll.x1, - .y1 = event->data.scroll.y1 - }; - browser_window_scroll_visible(bw, &rect); + rect.x1 = event->data.scroll.x1; + rect.y1 = event->data.scroll.y1; } else { - browser_window_set_scroll(bw, - event->data.scroll.x0, - event->data.scroll.y0); + rect.x1 = event->data.scroll.x0; + rect.y1 = event->data.scroll.y0; } + browser_window_set_scroll(bw, &rect); break; + } case CONTENT_MSG_DRAGSAVE: { @@ -1742,6 +1806,27 @@ static void browser_window_destroy_children(struct browser_window *bw) /** + * internal scheduled reformat callback. + * + * scheduled reformat callback to allow reformats from unthreaded context. + * + * \param vbw The browser window to be reformatted + */ +static void scheduled_reformat(void *vbw) +{ + struct browser_window *bw = vbw; + int width; + int height; + nserror res; + + res = guit->window->get_dimensions(bw->window, &width, &height, false); + if (res == NSERROR_OK) { + browser_window_reformat(bw, false, width, height); + } +} + + +/** * Release all memory associated with a browser window. * * \param bw browser window @@ -1750,7 +1835,7 @@ static void browser_window_destroy_internal(struct browser_window *bw) { assert(bw); - LOG("Destroying window"); + NSLOG(netsurf, INFO, "Destroying window"); if (bw->children != NULL || bw->iframes != NULL) { browser_window_destroy_children(bw); @@ -1770,8 +1855,9 @@ static void browser_window_destroy_internal(struct browser_window *bw) /* The ugly cast here is so the reformat function can be * passed a gui window pointer in its API rather than void* */ - LOG("Clearing schedule %p(%p)", guit->window->reformat, bw->window); - guit->misc->schedule(-1, (void(*)(void*))guit->window->reformat, bw->window); + NSLOG(netsurf, INFO, + "Clearing reformat schedule for browser window %p", bw); + guit->misc->schedule(-1, scheduled_reformat, bw); /* If this brower window is not the root window, and has focus, unset * the root browser window's focus pointer. */ @@ -1812,21 +1898,22 @@ static void browser_window_destroy_internal(struct browser_window *bw) bw->current_content = NULL; } - if (bw->loading_favicon != NULL) { - hlcache_handle_abort(bw->loading_favicon); - hlcache_handle_release(bw->loading_favicon); - bw->loading_favicon = NULL; + if (bw->favicon.loading != NULL) { + hlcache_handle_abort(bw->favicon.loading); + hlcache_handle_release(bw->favicon.loading); + bw->favicon.loading = NULL; } - if (bw->current_favicon != NULL) { - content_status status = content_get_status(bw->current_favicon); + if (bw->favicon.current != NULL) { + content_status status = content_get_status(bw->favicon.current); if (status == CONTENT_STATUS_READY || - status == CONTENT_STATUS_DONE) - content_close(bw->current_favicon); + status == CONTENT_STATUS_DONE) { + content_close(bw->favicon.current); + } - hlcache_handle_release(bw->current_favicon); - bw->current_favicon = NULL; + hlcache_handle_release(bw->favicon.current); + bw->favicon.current = NULL; } if (bw->box != NULL) { @@ -1840,15 +1927,17 @@ static void browser_window_destroy_internal(struct browser_window *bw) /* These simply free memory, so are safe here */ - if (bw->frag_id != NULL) + if (bw->frag_id != NULL) { lwc_string_unref(bw->frag_id); + } browser_window_history_destroy(bw); free(bw->name); - free(bw->status_text); - bw->status_text = NULL; - LOG("Status text cache match:miss %d:%d", bw->status_match, bw->status_miss); + free(bw->status.text); + bw->status.text = NULL; + NSLOG(netsurf, INFO, "Status text cache match:miss %d:%d", + bw->status.match, bw->status.miss); } /** @@ -1920,7 +2009,8 @@ nserror browser_window_refresh_url_bar(struct browser_window *bw) /* exported interface documented in netsurf/browser_window.h */ -nserror browser_window_navigate(struct browser_window *bw, +nserror +browser_window_navigate(struct browser_window *bw, nsurl *url, nsurl *referrer, enum browser_window_nav_flags flags, @@ -1940,14 +2030,28 @@ nserror browser_window_navigate(struct browser_window *bw, assert(bw); assert(url); - LOG("bw %p, url %s", bw, nsurl_access(url)); + NSLOG(netsurf, INFO, "bw %p, url %s", bw, nsurl_access(url)); + + /* If we're navigating and we have a history entry and a content + * then update the history entry before we navigate to save our + * current state. However since history navigation pre-moves + * the history state, we ensure that we only do this if we've not + * been suppressed. In the suppressed case, the history code + * updates the history itself before navigating. + */ + if (bw->current_content != NULL && + bw->history != NULL && + bw->history->current != NULL && + !(flags & BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE)) { + browser_window_history_update(bw, bw->current_content); + } /* don't allow massively nested framesets */ for (cur = bw; cur->parent; cur = cur->parent) { depth++; } if (depth > FRAME_DEPTH) { - LOG("frame depth too high."); + NSLOG(netsurf, INFO, "frame depth too high."); return NSERROR_FRAME_DEPTH; } @@ -2039,7 +2143,7 @@ nserror browser_window_navigate(struct browser_window *bw, browser_window_remove_caret(bw, false); browser_window_destroy_children(bw); - LOG("Loading '%s'", nsurl_access(url)); + NSLOG(netsurf, INFO, "Loading '%s'", nsurl_access(url)); browser_window_set_status(bw, messages_get("Loading")); bw->history_add = (flags & BW_NAVIGATE_HISTORY); @@ -2121,7 +2225,7 @@ nserror browser_window_navigate_up(struct browser_window *bw, bool new_window) if (bw == NULL) return NSERROR_BAD_PARAMETER; - current = browser_window_get_url(bw); + current = browser_window_access_url(bw); err = nsurl_parent(current, &parent); if (err != NSERROR_OK) { @@ -2147,8 +2251,8 @@ nserror browser_window_navigate_up(struct browser_window *bw, bool new_window) } -/* Exported interface, documented in browser.h */ -nsurl* browser_window_get_url(struct browser_window *bw) +/* Exported interface, documented in include/netsurf/browser_window.h */ +nsurl* browser_window_access_url(struct browser_window *bw) { assert(bw != NULL); @@ -2163,6 +2267,36 @@ nsurl* browser_window_get_url(struct browser_window *bw) return corestring_nsurl_about_blank; } +/* Exported interface, documented in include/netsurf/browser_window.h */ +nserror browser_window_get_url( + struct browser_window *bw, + bool fragment, + nsurl** url_out) +{ + nserror err; + nsurl *url; + + assert(bw != NULL); + + if (!fragment || bw->frag_id == NULL || bw->loading_content != NULL) { + /* If there's a loading content, then the bw->frag_id will have + * been trampled, possibly with a new frag_id, but we will + * still be returning the current URL, so in this edge case + * we just drop any fragment. */ + url = nsurl_ref(browser_window_access_url(bw)); + + } else { + err = nsurl_refragment(browser_window_access_url(bw), + bw->frag_id, &url); + if (err != NSERROR_OK) { + return err; + } + } + + *url_out = url; + return NSERROR_OK; +} + /* Exported interface, documented in browser.h */ const char* browser_window_get_title(struct browser_window *bw) { @@ -2255,19 +2389,60 @@ void browser_window_set_dimensions(struct browser_window *bw, bw->width = width; bw->height = height; } else { - LOG("Asked to set dimensions of front end window."); + NSLOG(netsurf, INFO, + "Asked to set dimensions of front end window."); assert(0); } } +/** + * scroll to a fragment if present + * + * \param bw browser window + * \return true if the scroll was sucessful + */ +static bool frag_scroll(struct browser_window *bw) +{ + struct rect rect; + + if (bw->frag_id == NULL) { + return false; + } + + if (!html_get_id_offset(bw->current_content, + bw->frag_id, + &rect.x0, + &rect.y0)) { + return false; + } + + rect.x1 = rect.x0; + rect.y1 = rect.y0; + if (browser_window_set_scroll(bw, &rect) == NSERROR_OK) { + if (bw->current_content != NULL && + bw->history != NULL && + bw->history->current != NULL) { + browser_window_history_update(bw, bw->current_content); + } + return true; + } + return false; +} + /* Exported interface, documented in browser.h */ void browser_window_update(struct browser_window *bw, bool scroll_to_top) { - int x, y; + static const struct rect zrect = { + .x0 = 0, + .y0 = 0, + .x1 = 0, + .y1 = 0 + }; - if (bw->current_content == NULL) + if (bw->current_content == NULL) { return; + } switch (bw->browser_window_type) { @@ -2278,17 +2453,15 @@ void browser_window_update(struct browser_window *bw, bool scroll_to_top) browser_window_update_extent(bw); - if (scroll_to_top) - browser_window_set_scroll(bw, 0, 0); - /* if frag_id exists, then try to scroll to it */ /** @todo don't do this if the user has scrolled */ - if (bw->frag_id && html_get_id_offset(bw->current_content, - bw->frag_id, &x, &y)) { - browser_window_set_scroll(bw, x, y); + if (!frag_scroll(bw)) { + if (scroll_to_top) { + browser_window_set_scroll(bw, &zrect); + } } - guit->window->redraw(bw->window); + guit->window->invalidate(bw->window, NULL); break; @@ -2299,15 +2472,13 @@ void browser_window_update(struct browser_window *bw, bool scroll_to_top) browser_window_update_extent(bw); - if (scroll_to_top) - browser_window_set_scroll(bw, 0, 0); + if (scroll_to_top) { + browser_window_set_scroll(bw, &zrect); + } /* if frag_id exists, then try to scroll to it */ /** @todo don't do this if the user has scrolled */ - if (bw->frag_id && html_get_id_offset(bw->current_content, - bw->frag_id, &x, &y)) { - browser_window_set_scroll(bw, x, y); - } + frag_scroll(bw); html_redraw_a_box(bw->parent->current_content, bw->box); break; @@ -2317,15 +2488,13 @@ void browser_window_update(struct browser_window *bw, bool scroll_to_top) struct rect rect; browser_window_update_extent(bw); - if (scroll_to_top) - browser_window_set_scroll(bw, 0, 0); + if (scroll_to_top) { + browser_window_set_scroll(bw, &zrect); + } /* if frag_id exists, then try to scroll to it */ /** @todo don't do this if the user has scrolled */ - if (bw->frag_id && html_get_id_offset(bw->current_content, - bw->frag_id, &x, &y)) { - browser_window_set_scroll(bw, x, y); - } + frag_scroll(bw); rect.x0 = scrollbar_get_offset(bw->scroll_x); rect.y0 = scrollbar_get_offset(bw->scroll_y); @@ -2354,7 +2523,7 @@ void browser_window_update_box(struct browser_window *bw, struct rect *rect) if (bw->window != NULL) { /* Front end window */ - guit->window->update(bw->window, rect); + guit->window->invalidate(bw->window, rect); } else { /* Core managed browser window */ browser_window_get_position(bw, true, &pos_x, &pos_y); @@ -2366,7 +2535,7 @@ void browser_window_update_box(struct browser_window *bw, struct rect *rect) rect->x1 += pos_x / bw->scale; rect->y1 += pos_y / bw->scale; - guit->window->update(top->window, rect); + guit->window->invalidate(top->window, rect); } } @@ -2465,10 +2634,10 @@ void browser_window_set_status(struct browser_window *bw, const char *text) while (bw->parent) bw = bw->parent; - if ((bw->status_text != NULL) && - (strcmp(text, bw->status_text) == 0)) { + if ((bw->status.text != NULL) && + (strcmp(text, bw->status.text) == 0)) { /* status text is unchanged */ - bw->status_match++; + bw->status.match++; return; } @@ -2476,18 +2645,18 @@ void browser_window_set_status(struct browser_window *bw, const char *text) text_len = strlen(text); - if ((bw->status_text == NULL) || (bw->status_text_len < text_len)) { + if ((bw->status.text == NULL) || (bw->status.text_len < text_len)) { /* no current string allocation or it is not long enough */ - free(bw->status_text); - bw->status_text = strdup(text); - bw->status_text_len = text_len; + free(bw->status.text); + bw->status.text = strdup(text); + bw->status.text_len = text_len; } else { /* current allocation has enough space */ - memcpy(bw->status_text, text, text_len + 1); + memcpy(bw->status.text, text, text_len + 1); } - bw->status_miss++; - guit->window->set_status(bw->window, bw->status_text); + bw->status.miss++; + guit->window->set_status(bw->window, bw->status.text); } @@ -2530,13 +2699,16 @@ void browser_window_set_pointer(struct browser_window *bw, guit->window->set_pointer(root->window, gui_shape); } + /* exported function documented in netsurf/browser_window.h */ nserror browser_window_schedule_reformat(struct browser_window *bw) { - /* The ugly cast here is so the reformat function can be - * passed a gui window pointer in its API rather than void* - */ - guit->misc->schedule(0, (void(*)(void*))guit->window->reformat, bw->window); + if (bw->window == NULL) { + return NSERROR_BAD_PARAMETER; + } + + guit->misc->schedule(0, scheduled_reformat, bw); + return NSERROR_OK; } @@ -2837,7 +3009,7 @@ static void browser_window_mouse_drag_end(struct browser_window *bw, { int scr_x, scr_y; - switch (bw->drag_type) { + switch (bw->drag.type) { case DRAGGING_SELECTION: case DRAGGING_OTHER: case DRAGGING_CONTENT_SCROLLBAR: @@ -2853,7 +3025,7 @@ static void browser_window_mouse_drag_end(struct browser_window *bw, scrollbar_mouse_drag_end(bw->scroll_x, mouse, scr_x, scr_y); - bw->drag_type = DRAGGING_NONE; + bw->drag.type = DRAGGING_NONE; break; case DRAGGING_SCR_Y: @@ -2865,7 +3037,7 @@ static void browser_window_mouse_drag_end(struct browser_window *bw, scrollbar_mouse_drag_end(bw->scroll_y, mouse, scr_x, scr_y); - bw->drag_type = DRAGGING_NONE; + bw->drag.type = DRAGGING_NONE; break; default: @@ -2883,11 +3055,11 @@ void browser_window_mouse_track(struct browser_window *bw, const char *status = NULL; browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT; - if (bw->window != NULL && bw->drag_window && bw != bw->drag_window) { + if (bw->window != NULL && bw->drag.window && bw != bw->drag.window) { /* This is the root browser window and there's an active drag * in a sub window. * Pass the mouse action straight on to that bw. */ - struct browser_window *drag_bw = bw->drag_window; + struct browser_window *drag_bw = bw->drag.window; int off_x = 0; int off_y = 0; @@ -2940,25 +3112,27 @@ void browser_window_mouse_track(struct browser_window *bw, return; } - if (c == NULL && bw->drag_type != DRAGGING_FRAME) + if (c == NULL && bw->drag.type != DRAGGING_FRAME) { return; + } - if (bw->drag_type != DRAGGING_NONE && !mouse) { + if (bw->drag.type != DRAGGING_NONE && !mouse) { browser_window_mouse_drag_end(bw, mouse, x, y); } /* Browser window's horizontal scrollbar */ - if (bw->scroll_x != NULL && bw->drag_type != DRAGGING_SCR_Y) { + if (bw->scroll_x != NULL && bw->drag.type != DRAGGING_SCR_Y) { int scr_x, scr_y; browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y); scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x); scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y); - if ((scr_x > 0 && scr_x < browser_window_get_scrollbar_len(bw, - true) && - scr_y > 0 && scr_y < SCROLLBAR_WIDTH && - bw->drag_type == DRAGGING_NONE) || - bw->drag_type == DRAGGING_SCR_X) { + if ((bw->drag.type == DRAGGING_SCR_X) || + (scr_x > 0 && + scr_x < browser_window_get_scrollbar_len(bw, true) && + scr_y > 0 && + scr_y < SCROLLBAR_WIDTH && + bw->drag.type == DRAGGING_NONE)) { /* Start a scrollbar drag, or continue existing drag */ status = scrollbar_mouse_status_to_message( scrollbar_mouse_action( @@ -2966,8 +3140,9 @@ void browser_window_mouse_track(struct browser_window *bw, scr_x, scr_y)); pointer = BROWSER_POINTER_DEFAULT; - if (status != NULL) + if (status != NULL) { browser_window_set_status(bw, status); + } browser_window_set_pointer(bw, pointer); return; @@ -2981,11 +3156,12 @@ void browser_window_mouse_track(struct browser_window *bw, scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x); scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y); - if ((scr_y > 0 && scr_y < browser_window_get_scrollbar_len(bw, - false) && - scr_x > 0 && scr_x < SCROLLBAR_WIDTH && - bw->drag_type == DRAGGING_NONE) || - bw->drag_type == DRAGGING_SCR_Y) { + if ((bw->drag.type == DRAGGING_SCR_Y) || + (scr_y > 0 && + scr_y < browser_window_get_scrollbar_len(bw, false) && + scr_x > 0 && + scr_x < SCROLLBAR_WIDTH && + bw->drag.type == DRAGGING_NONE)) { /* Start a scrollbar drag, or continue existing drag */ status = scrollbar_mouse_status_to_message( scrollbar_mouse_action( @@ -2993,29 +3169,32 @@ void browser_window_mouse_track(struct browser_window *bw, scr_x, scr_y)); pointer = BROWSER_POINTER_DEFAULT; - if (status != NULL) + if (status != NULL) { browser_window_set_status(bw, status); + } browser_window_set_pointer(bw, pointer); return; } } - if (bw->drag_type == DRAGGING_FRAME) { + if (bw->drag.type == DRAGGING_FRAME) { browser_window_resize_frame(bw, bw->x + x, bw->y + y); - } else if (bw->drag_type == DRAGGING_PAGE_SCROLL) { + } else if (bw->drag.type == DRAGGING_PAGE_SCROLL) { /* mouse movement since drag started */ - int scrollx = bw->drag_start_x - x; - int scrolly = bw->drag_start_y - y; + struct rect rect; + + rect.x0 = bw->drag.start_x - x; + rect.y0 = bw->drag.start_y - y; /* new scroll offsets */ - scrollx += bw->drag_start_scroll_x; - scrolly += bw->drag_start_scroll_y; + rect.x0 += bw->drag.start_scroll_x; + rect.y0 += bw->drag.start_scroll_y; - bw->drag_start_scroll_x = scrollx; - bw->drag_start_scroll_y = scrolly; + bw->drag.start_scroll_x = rect.x1 = rect.x0; + bw->drag.start_scroll_y = rect.y1 = rect.y0; - browser_window_set_scroll(bw, scrollx, scrolly); + browser_window_set_scroll(bw, &rect); } else { assert(c != NULL); content_mouse_track(c, bw, mouse, x, y); @@ -3163,19 +3342,20 @@ void browser_window_page_drag_start(struct browser_window *bw, int x, int y) browser_window_set_drag_type(bw, DRAGGING_PAGE_SCROLL, NULL); - bw->drag_start_x = x; - bw->drag_start_y = y; + bw->drag.start_x = x; + bw->drag.start_y = y; if (bw->window != NULL) { /* Front end window */ - guit->window->get_scroll(bw->window, &bw->drag_start_scroll_x, - &bw->drag_start_scroll_y); + guit->window->get_scroll(bw->window, + &bw->drag.start_scroll_x, + &bw->drag.start_scroll_y); guit->window->scroll_start(bw->window); } else { /* Core managed browser window */ - bw->drag_start_scroll_x = scrollbar_get_offset(bw->scroll_x); - bw->drag_start_scroll_y = scrollbar_get_offset(bw->scroll_y); + bw->drag.start_scroll_x = scrollbar_get_offset(bw->scroll_x); + bw->drag.start_scroll_y = scrollbar_get_offset(bw->scroll_y); } } diff --git a/desktop/browser_history.c b/desktop/browser_history.c index d21c5bc25..d9db0eb18 100644 --- a/desktop/browser_history.c +++ b/desktop/browser_history.c @@ -31,13 +31,12 @@ #include "utils/log.h" #include "utils/utils.h" #include "netsurf/layout.h" -#include "netsurf/plotters.h" #include "netsurf/content.h" +#include "netsurf/window.h" #include "content/hlcache.h" #include "content/urldb.h" #include "netsurf/bitmap.h" -#include "desktop/system_colour.h" #include "desktop/gui_internal.h" #include "desktop/browser_history.h" #include "desktop/browser_private.h" @@ -47,45 +46,12 @@ #define RIGHT_MARGIN 50 #define BOTTOM_MARGIN 30 -struct history_page { - nsurl *url; /**< Page URL, never 0. */ - lwc_string *frag_id; /** Fragment identifier, or 0. */ - char *title; /**< Page title, never 0. */ -}; - -/** A node in the history tree. */ -struct history_entry { - struct history_page page; - struct history_entry *back; /**< Parent. */ - struct history_entry *next; /**< Next sibling. */ - struct history_entry *forward; /**< First child. */ - struct history_entry *forward_pref; /**< Child in direction of - current entry. */ - struct history_entry *forward_last; /**< Last child. */ - unsigned int children; /**< Number of children. */ - int x; /**< Position of node. */ - int y; /**< Position of node. */ - struct bitmap *bitmap; /**< Thumbnail bitmap, or 0. */ -}; - -/** History tree for a window. */ -struct history { - /** First page in tree (page that window opened with). */ - struct history_entry *start; - /** Current position in tree. */ - struct history_entry *current; - /** Width of layout. */ - int width; - /** Height of layout. */ - int height; -}; - /** * Clone a history entry * * \param history opaque history structure, as returned by history_create() - * \param entry entry to clone + * \param entry entry to clone * \return A cloned history entry or NULL on error */ static struct history_entry * @@ -97,47 +63,85 @@ browser_window_history__clone_entry(struct history *history, struct history_entry *prev = NULL; struct history_entry *new_entry; + assert(entry); assert(entry->page.url); assert(entry->page.title); /* clone the entry */ - new_entry = malloc(sizeof *entry); - if (!new_entry) + new_entry = calloc(1, sizeof *entry); + if (!new_entry) { return NULL; + } - memcpy(new_entry, entry, sizeof *entry); - new_entry->page.url = nsurl_ref(entry->page.url); - if (entry->page.frag_id) - new_entry->page.frag_id = lwc_string_ref(entry->page.frag_id); - + /* copy page information */ new_entry->page.title = strdup(entry->page.title); - if (!new_entry->page.url || !new_entry->page.title || - ((entry->page.frag_id) && (!new_entry->page.frag_id))) { - nsurl_unref(new_entry->page.url); - if (new_entry->page.frag_id) - lwc_string_unref(new_entry->page.frag_id); + if (new_entry->page.title == NULL) { + free(new_entry); + return NULL; + } + + new_entry->page.url = nsurl_ref(entry->page.url); + if (new_entry->page.url == NULL) { free(new_entry->page.title); free(new_entry); return NULL; } - /* update references */ - if (history->current == entry) - history->current = new_entry; + if (entry->page.frag_id == NULL) { + new_entry->page.frag_id = NULL; + } else { + new_entry->page.frag_id = lwc_string_ref(entry->page.frag_id); + if (new_entry->page.frag_id == NULL) { + nsurl_unref(new_entry->page.url); + free(new_entry); + return NULL; + } + } + + if (entry->page.bitmap == NULL) { + new_entry->page.bitmap = NULL; + } else { + /* create a new bitmap and copy original into it */ + unsigned char *bmsrc_data; + unsigned char *bmdst_data; + size_t bmsize; + + new_entry->page.bitmap = guit->bitmap->create(WIDTH, HEIGHT, + BITMAP_NEW | BITMAP_OPAQUE); + + if (new_entry->page.bitmap != NULL) { + bmsrc_data = guit->bitmap->get_buffer(entry->page.bitmap); + bmdst_data = guit->bitmap->get_buffer(new_entry->page.bitmap); + bmsize = guit->bitmap->get_rowstride(new_entry->page.bitmap) * + guit->bitmap->get_height(new_entry->page.bitmap); + memcpy(bmdst_data, bmsrc_data, bmsize); + } + } + + /* copy tree values */ + new_entry->back = entry->back; + new_entry->next = entry->next; + new_entry->forward = entry->forward; + new_entry->forward_pref = entry->forward_pref; + new_entry->forward_last = entry->forward_last; /* recurse for all children */ - for (child = new_entry->forward; child; child = child->next) { + for (child = new_entry->forward; child != NULL; child = child->next) { new_child = browser_window_history__clone_entry(history, child); - if (new_child) { - new_child->back = new_entry; - } else { + if (new_child == NULL) { nsurl_unref(new_entry->page.url); - if (new_entry->page.frag_id) + if (new_entry->page.frag_id) { lwc_string_unref(new_entry->page.frag_id); + } free(new_entry->page.title); + if (entry->page.bitmap != NULL) { + guit->bitmap->destroy(entry->page.bitmap); + } free(new_entry); return NULL; } + + new_child->back = new_entry; if (prev) prev->next = new_child; if (new_entry->forward == child) @@ -148,6 +152,12 @@ browser_window_history__clone_entry(struct history *history, new_entry->forward_last = new_child; prev = new_child; } + + /* update references */ + if (history->current == entry) { + history->current = new_entry; + } + return new_entry; } @@ -158,15 +168,20 @@ browser_window_history__clone_entry(struct history *history, static void browser_window_history__free_entry(struct history_entry *entry) { - if (!entry) - return; - browser_window_history__free_entry(entry->forward); - browser_window_history__free_entry(entry->next); - nsurl_unref(entry->page.url); - if (entry->page.frag_id) - lwc_string_unref(entry->page.frag_id); - free(entry->page.title); - free(entry); + if (entry != NULL) { + browser_window_history__free_entry(entry->forward); + browser_window_history__free_entry(entry->next); + + nsurl_unref(entry->page.url); + if (entry->page.frag_id) { + lwc_string_unref(entry->page.frag_id); + } + free(entry->page.title); + if (entry->page.bitmap != NULL) { + guit->bitmap->destroy(entry->page.bitmap); + } + free(entry); + } } @@ -236,197 +251,9 @@ static void browser_window_history__layout(struct history *history) history->height += BOTTOM_MARGIN / 2; } -/** plot style for drawing lines between nodes */ -static plot_style_t pstyle_line = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 2, -}; - -/** plot style for drawing background */ -static plot_style_t pstyle_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; - -/** plot style for drawing rectangle round unselected nodes */ -static plot_style_t pstyle_rect = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 1, -}; - -/** plot style for drawing rectangle round selected nodes */ -static plot_style_t pstyle_rect_sel = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 3, -}; - -/** plot style for font on unselected nodes */ -static plot_font_style_t pfstyle_node = { - .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = 8 * FONT_SIZE_SCALE, - .weight = 400, - .flags = FONTF_NONE, -}; - -/** plot style for font on unselected nodes */ -static plot_font_style_t pfstyle_node_sel = { - .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = 8 * FONT_SIZE_SCALE, - .weight = 900, - .flags = FONTF_NONE, -}; - -/** - * Recursively redraw a history_entry. - * - * \param history history containing the entry - * \param entry entry to render - * \param x0 area top left x coordinate - * \param y0 area top left y coordinate - * \param x1 area bottom right x coordinate - * \param y1 area bottom right y coordinate - * \param x window x offset - * \param y window y offset - * \param clip clip redraw - * \param ctx current redraw context - */ -static bool -browser_window_history__redraw_entry(struct history *history, - struct history_entry *entry, - int x0, int y0, int x1, int y1, - int x, int y, bool clip, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - size_t char_offset; - int actual_x; - struct history_entry *child; - int tailsize = 5; - int xoffset = x - x0; - int yoffset = y - y0; - - plot_style_t *pstyle; - plot_font_style_t *pfstyle; - - nserror res; - - /* setup plot styles */ - if (entry == history->current) { - pstyle = &pstyle_rect_sel; - pfstyle = &pfstyle_node_sel; - } else { - pstyle = &pstyle_rect; - pfstyle = &pfstyle_node; - } - - /* setup clip area */ - if (clip) { - struct rect rect; - rect.x0 = x0 + xoffset; - rect.y0 = y0 + yoffset; - rect.x1 = x1 + xoffset; - rect.y1 = y1 + yoffset; - if (!plot->clip(&rect)) { - return false; - } - } - - /* Only attempt to plot bitmap if it is present */ - if (entry->bitmap != NULL) { - plot->bitmap(entry->x + xoffset, - entry->y + yoffset, - WIDTH, HEIGHT, - entry->bitmap, - 0xffffff, - 0); - } - - if (!plot->rectangle(entry->x - 1 + xoffset, - entry->y - 1 + yoffset, - entry->x + xoffset + WIDTH, - entry->y + yoffset + HEIGHT, - pstyle)) { - return false; - } - - res = guit->layout->position(plot_style_font, entry->page.title, - strlen(entry->page.title), WIDTH, - &char_offset, &actual_x); - if (res != NSERROR_OK) { - return false; - } - - - if (!plot->text(entry->x + xoffset, - entry->y + HEIGHT + 12 + yoffset, - entry->page.title, - char_offset, - pfstyle)) { - return false; - } - - /* for each child node draw a line and recurse redraw into it */ - for (child = entry->forward; child; child = child->next) { - if (!plot->line(entry->x + WIDTH + xoffset, - entry->y + HEIGHT / 2 + yoffset, - entry->x + WIDTH + tailsize + xoffset, - entry->y + HEIGHT / 2 + yoffset, - &pstyle_line)) { - return false; - } - if (!plot->line(entry->x + WIDTH + tailsize + xoffset, - entry->y + HEIGHT / 2 + yoffset, - child->x - tailsize +xoffset, - child->y + HEIGHT / 2 + yoffset, - &pstyle_line)) { - return false; - } - if (!plot->line(child->x - tailsize + xoffset, - child->y + HEIGHT / 2 + yoffset, - child->x + xoffset, child->y + - HEIGHT / 2 + yoffset, - &pstyle_line)) { - return false; - } - if (!browser_window_history__redraw_entry(history, child, - x0, y0, x1, y1, x, y, clip, ctx)) { - return false; - } - } - - return true; -} - -/** - * Find the history entry at a position. - * - * \param entry entry to search from - * \param x coordinate - * \param y coordinate - * \return an entry if found, 0 if none - */ -static struct history_entry *browser_window_history__find_position( - struct history_entry *entry, int x, int y) -{ - struct history_entry *child; - struct history_entry *found; - if (!entry) - return 0; - - if (entry->x <= x && x <= entry->x + WIDTH && - entry->y <= y && y <= entry->y + HEIGHT) - return entry; - - for (child = entry->forward; child; child = child->next) { - found = browser_window_history__find_position(child, x, y); - if (found) - return found; - } - - return 0; -} /** * Enumerate subentries in history @@ -469,17 +296,6 @@ nserror browser_window_history_create(struct browser_window *bw) { struct history *history; - pstyle_bg.fill_colour = ns_system_colour_char("Window"); - pfstyle_node.background = pstyle_bg.fill_colour; - pfstyle_node_sel.background = pstyle_bg.fill_colour; - - pstyle_line.stroke_colour = ns_system_colour_char("GrayText"); - pstyle_rect.stroke_colour = pstyle_line.stroke_colour; - pfstyle_node.foreground = pstyle_line.stroke_colour; - - pstyle_rect_sel.stroke_colour = ns_system_colour_char("Highlight"); - pfstyle_node_sel.foreground = pstyle_rect_sel.stroke_colour; - bw->history = NULL; history = calloc(1, sizeof *history); @@ -520,7 +336,7 @@ nserror browser_window_history_clone(const struct browser_window *existing, new_history->start = browser_window_history__clone_entry(new_history, new_history->start); if (!new_history->start) { - LOG("Insufficient memory to clone history"); + NSLOG(netsurf, INFO, "Insufficient memory to clone history"); browser_window_history_destroy(clone); clone->history = NULL; return NSERROR_NOMEM; @@ -531,14 +347,14 @@ nserror browser_window_history_clone(const struct browser_window *existing, /* exported interface documented in desktop/browser_history.h */ -nserror browser_window_history_add(struct browser_window *bw, - struct hlcache_handle *content, lwc_string *frag_id) +nserror +browser_window_history_add(struct browser_window *bw, + struct hlcache_handle *content, + lwc_string *frag_id) { struct history *history; struct history_entry *entry; - nsurl *nsurl = hlcache_handle_get_url(content); char *title; - struct bitmap *bitmap; nserror ret; assert(bw); @@ -547,32 +363,50 @@ nserror browser_window_history_add(struct browser_window *bw, history = bw->history; - /* allocate space */ entry = malloc(sizeof *entry); if (entry == NULL) { return NSERROR_NOMEM; } + /* page information */ title = strdup(content_get_title(content)); if (title == NULL) { free(entry); return NSERROR_NOMEM; } - entry->page.url = nsurl_ref(nsurl); - entry->page.frag_id = frag_id ? lwc_string_ref(frag_id) : 0; - + entry->page.url = nsurl_ref(hlcache_handle_get_url(content)); + entry->page.frag_id = frag_id ? lwc_string_ref(frag_id) : NULL; entry->page.title = title; + entry->page.scroll_x = 0.0f; + entry->page.scroll_y = 0.0f; + + /* create thumbnail for localhistory view */ + NSLOG(netsurf, DEBUG, + "Creating thumbnail for %s", nsurl_access(entry->page.url)); + + entry->page.bitmap = guit->bitmap->create(WIDTH, HEIGHT, + BITMAP_NEW | BITMAP_CLEAR_MEMORY | BITMAP_OPAQUE); + if (entry->page.bitmap != NULL) { + ret = guit->bitmap->render(entry->page.bitmap, content); + if (ret != NSERROR_OK) { + /* Thumbnail render failed */ + NSLOG(netsurf, WARNING, "Thumbnail render failed"); + } + } + + /* insert into tree */ entry->back = history->current; - entry->next = 0; - entry->forward = entry->forward_pref = entry->forward_last = 0; + entry->next = NULL; + entry->forward = entry->forward_pref = entry->forward_last = NULL; entry->children = 0; - entry->bitmap = 0; + if (history->current) { - if (history->current->forward_last) + if (history->current->forward_last) { history->current->forward_last->next = entry; - else + } else { history->current->forward = entry; + } history->current->forward_pref = entry; history->current->forward_last = entry; history->current->children++; @@ -581,33 +415,6 @@ nserror browser_window_history_add(struct browser_window *bw, } history->current = entry; - /* if we have a thumbnail, don't update until the page has finished - * loading */ - bitmap = urldb_get_thumbnail(nsurl); - if (bitmap == NULL) { - LOG("Creating thumbnail for %s", nsurl_access(nsurl)); - bitmap = guit->bitmap->create(WIDTH, HEIGHT, - BITMAP_NEW | BITMAP_CLEAR_MEMORY | - BITMAP_OPAQUE); - if (bitmap != NULL) { - ret = guit->bitmap->render(bitmap, content); - if (ret == NSERROR_OK) { - /* Successful thumbnail so register it - * with the url. - */ - urldb_set_thumbnail(nsurl, bitmap); - } else { - /* Thumbnailing failed. Ignore it - * silently but clean up bitmap. - */ - LOG("Thumbnail renderfailed"); - guit->bitmap->destroy(bitmap); - bitmap = NULL; - } - } - } - entry->bitmap = bitmap; - browser_window_history__layout(history); return NSERROR_OK; @@ -620,12 +427,15 @@ nserror browser_window_history_update(struct browser_window *bw, { struct history *history; char *title; + int sx, sy; assert(bw != NULL); history = bw->history; - if (!history || !history->current || !history->current->bitmap) { + if (!history || + !history->current || + !history->current->page.bitmap) { return NSERROR_INVALID; } @@ -636,16 +446,50 @@ nserror browser_window_history_update(struct browser_window *bw, if (title == NULL) { return NSERROR_NOMEM; } - + NSLOG(netsurf, INFO, "Updating history entry for %s", title); free(history->current->page.title); history->current->page.title = title; - guit->bitmap->render(history->current->bitmap, content); + if (history->current->page.bitmap != NULL) { + guit->bitmap->render(history->current->page.bitmap, content); + } + if (bw->window != NULL && + guit->window->get_scroll(bw->window, &sx, &sy)) { + /* Successfully got scroll offsets, update the entry */ + history->current->page.scroll_x = \ + (float)sx / (float)content_get_width(content); + history->current->page.scroll_y = \ + (float)sy / (float)content_get_height(content); + NSLOG(netsurf, INFO, "Updated scroll offsets to %g by %g", + history->current->page.scroll_x, + history->current->page.scroll_y); + } return NSERROR_OK; } +/* exported interface documented in desktop/browser_private.h */ +nserror +browser_window_history_get_scroll(struct browser_window *bw, + float *sx, float *sy) +{ + struct history *history; + + assert(bw != NULL); + + history = bw->history; + + if (!history || + !history->current || + !history->current->page.bitmap) { + return NSERROR_INVALID; + } + + *sx = history->current->page.scroll_x; + *sy = history->current->page.scroll_y; + return NSERROR_OK; +} /* exported interface documented in desktop/browser_history.h */ void browser_window_history_destroy(struct browser_window *bw) @@ -704,6 +548,27 @@ bool browser_window_history_forward_available(struct browser_window *bw) bw->history->current->forward_pref); } +/* exported interface documented in desktop/browser_history.h */ +nserror +browser_window_history_get_thumbnail(struct browser_window *bw, + struct bitmap **bitmap_out) +{ + struct bitmap *bitmap; + + if (!bw || !bw->history || !bw->history->current) { + return NSERROR_INVALID; + } + + if (bw->history->current->page.bitmap == NULL) { + bitmap = content_get_bitmap(bw->current_content); + } else { + bitmap = bw->history->current->page.bitmap; + } + + *bitmap_out = bitmap; + + return NSERROR_OK; +} /* exported interface documented in desktop/browser_history.h */ nserror browser_window_history_go(struct browser_window *bw, @@ -736,9 +601,13 @@ nserror browser_window_history_go(struct browser_window *bw, url, NULL, bw, NULL); history->current = current; } else { + if (bw->current_content != NULL) { + browser_window_history_update(bw, bw->current_content); + } history->current = entry; error = browser_window_navigate(bw, url, NULL, - BW_NAVIGATE_NONE, NULL, NULL, NULL); + BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE, + NULL, NULL, NULL); } nsurl_unref(url); @@ -748,99 +617,7 @@ nserror browser_window_history_go(struct browser_window *bw, /* exported interface documented in desktop/browser_history.h */ -void browser_window_history_size(struct browser_window *bw, - int *width, int *height) -{ - assert(bw != NULL); - assert(bw->history != NULL); - - *width = bw->history->width; - *height = bw->history->height; -} - - -/* exported interface documented in desktop/browser_history.h */ -bool browser_window_history_redraw(struct browser_window *bw, - const struct redraw_context *ctx) -{ - struct history *history; - - assert(bw != NULL); - history = bw->history; - - if (history == NULL) { - LOG("Attempt to draw NULL history."); - return false; - } - - if (!history->start) - return true; - - return browser_window_history__redraw_entry(history, history->start, - 0, 0, 0, 0, 0, 0, false, ctx); -} - - -/* exported interface documented in desktop/browser_history.h */ -bool browser_window_history_redraw_rectangle(struct browser_window *bw, - int x0, int y0, int x1, int y1, - int x, int y, const struct redraw_context *ctx) -{ - struct history *history; - - assert(bw != NULL); - history = bw->history; - - if (!history->start) - return true; - - return browser_window_history__redraw_entry(history, history->start, - x0, y0, x1, y1, x, y, true, ctx); -} - - -/* exported interface documented in desktop/browser_history.h */ -bool browser_window_history_click(struct browser_window *bw, - int x, int y, bool new_window) -{ - struct history_entry *entry; - struct history *history; - - assert(bw != NULL); - history = bw->history; - - entry = browser_window_history__find_position(history->start, x, y); - if (!entry) - return false; - if (entry == history->current) - return false; - - browser_window_history_go(bw, entry, new_window); - - return true; -} - - -/* exported interface documented in desktop/browser_history.h */ -const char *browser_window_history_position_url(struct browser_window *bw, - int x, int y) -{ - struct history_entry *entry; - struct history *history; - - assert(bw != NULL); - history = bw->history; - - entry = browser_window_history__find_position(history->start, x, y); - if (!entry) - return 0; - - return nsurl_access(entry->page.url); -} - - -/* exported interface documented in desktop/browser_history.h */ -void browser_window_history_enumerate_forward(const struct browser_window *bw, +void browser_window_history_enumerate_forward(const struct browser_window *bw, browser_window_history_enumerate_cb cb, void *user_data) { struct history_entry *e; @@ -858,7 +635,7 @@ void browser_window_history_enumerate_forward(const struct browser_window *bw, /* exported interface documented in desktop/browser_history.h */ -void browser_window_history_enumerate_back(const struct browser_window *bw, +void browser_window_history_enumerate_back(const struct browser_window *bw, browser_window_history_enumerate_cb cb, void *user_data) { struct history_entry *e; @@ -886,10 +663,9 @@ void browser_window_history_enumerate(const struct browser_window *bw, /* exported interface documented in desktop/browser_history.h */ -const char *browser_window_history_entry_get_url( - const struct history_entry *entry) +nsurl *browser_window_history_entry_get_url(const struct history_entry *entry) { - return nsurl_access(entry->page.url); + return nsurl_ref(entry->page.url); } diff --git a/desktop/browser_history.h b/desktop/browser_history.h index 8ffb6125b..9b6f1fd42 100644 --- a/desktop/browser_history.h +++ b/desktop/browser_history.h @@ -16,71 +16,28 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * Browser history tree (interface). +/** + * \file + * Interface to browser history operations + * + * The are operations on a browsing contexts history. These interfaces + * allow navigation forward and backwards in the history as well as + * enumerating the entries. + * + * The local history viewing is distinct via corewindow defined in + * desktop/local_history.h */ -#ifndef _NETSURF_DESKTOP_BROWSER_HISTORY_H_ -#define _NETSURF_DESKTOP_BROWSER_HISTORY_H_ +#ifndef NETSURF_DESKTOP_BROWSER_HISTORY_H +#define NETSURF_DESKTOP_BROWSER_HISTORY_H #include <stdbool.h> -#include <libwapcaplet/libwapcaplet.h> #include "utils/errors.h" -struct hlcache_handle; struct browser_window; struct history_entry; -struct redraw_context; - -/** - * Create a new history tree for a browser window window. - * - * \param bw browser window to create history for. - * - * \return NSERROR_OK or appropriate error otherwise - */ -nserror browser_window_history_create(struct browser_window *bw); - -/** - * Clone a bw's history tree for new bw - * - * \param existing browser window with history to clone. - * \param clone browser window to make cloned history for. - * - * \return NSERROR_OK or appropriate error otherwise - */ -nserror browser_window_history_clone(const struct browser_window *existing, - struct browser_window *clone); -/** - * Insert a url into the history tree. - * - * \param bw browser window with history object - * \param content content to add to history - * \param frag_id fragment identifier, or NULL. - * \return NSERROR_OK or error code on faliure. - * - * The page is added after the current entry and becomes current. - */ -nserror browser_window_history_add(struct browser_window *bw, - struct hlcache_handle *content, lwc_string *frag_id); - -/** - * Update the thumbnail for the current entry. - * - * \param bw The browser window to update the history within. - * \param content content for current entry - * \return NSERROR_OK or error code on faliure. - */ -nserror browser_window_history_update(struct browser_window *bw, - struct hlcache_handle *content); - -/** - * Free a history structure. - * - * \param bw The browser window to destroy the history within. - */ -void browser_window_history_destroy(struct browser_window *bw); +struct bitmap; /** * Go back in the history. @@ -91,6 +48,7 @@ void browser_window_history_destroy(struct browser_window *bw); */ nserror browser_window_history_back(struct browser_window *bw, bool new_window); + /** * Go forward in the history. * @@ -100,6 +58,7 @@ nserror browser_window_history_back(struct browser_window *bw, bool new_window); */ nserror browser_window_history_forward(struct browser_window *bw, bool new_window); + /** * Check whether it is pssible to go back in the history. * @@ -108,6 +67,7 @@ nserror browser_window_history_forward(struct browser_window *bw, bool new_windo */ bool browser_window_history_back_available(struct browser_window *bw); + /** * Check whether it is pssible to go forwards in the history. * @@ -117,62 +77,13 @@ bool browser_window_history_back_available(struct browser_window *bw); bool browser_window_history_forward_available(struct browser_window *bw); /** - * Get the dimensions of a history. - * - * \param bw browser window with history object. - * \param width updated to width - * \param height updated to height - */ -void browser_window_history_size(struct browser_window *bw, - int *width, int *height); - -/** - * Redraw all of a history area. + * Get the thumbnail bitmap for the current history entry * - * \param bw browser window with history object. - * \param ctx current redraw context - */ -bool browser_window_history_redraw(struct browser_window *bw, - const struct redraw_context *ctx); - -/** - * Redraw part of a history area. - * - * \param bw browser window with history object. - * \param x0 left X co-ordinate of redraw area - * \param y0 top Y co-ordinate of redraw area - * \param x1 right X co-ordinate of redraw area - * \param y1 lower Y co-ordinate of redraw area - * \param x start X co-ordinate on plot canvas - * \param y start Y co-ordinate on plot canvas - * \param ctx current redraw context - */ -bool browser_window_history_redraw_rectangle(struct browser_window *bw, - int x0, int y0, int x1, int y1, int x, int y, - const struct redraw_context *ctx); - -/** - * Handle a mouse click in a history. - * - * \param bw browser window containing history - * \param x click coordinate - * \param y click coordinate - * \param new_window open a new window instead of using bw - * \return true if action was taken, false if click was not on an entry - */ -bool browser_window_history_click(struct browser_window *bw, - int x, int y, bool new_window); - -/** - * Determine the URL of the entry at a position. - * - * \param bw browser window containing history - * \param x x coordinate. - * \param y y coordinate. - * \return URL, or 0 if no entry at (x, y) + * \param bw The browser window + * \param bitmap The bitmat for the current history entry. + * \return NSERROR_OK or error code on faliure. */ -const char *browser_window_history_position_url(struct browser_window *bw, - int x, int y); +nserror browser_window_history_get_thumbnail(struct browser_window *bw, struct bitmap **bitmap_out); /** * Callback function type for history enumeration @@ -187,6 +98,7 @@ typedef bool (*browser_window_history_enumerate_cb)( int x0, int y0, int x1, int y1, const struct history_entry *entry, void *user_data); + /** * Enumerate all entries in the history. * Do not change the history while it is being enumerated. @@ -198,6 +110,7 @@ typedef bool (*browser_window_history_enumerate_cb)( void browser_window_history_enumerate(const struct browser_window *bw, browser_window_history_enumerate_cb cb, void *user_data); + /** * Enumerate all entries that will be reached by the 'forward' button * @@ -208,6 +121,7 @@ void browser_window_history_enumerate(const struct browser_window *bw, void browser_window_history_enumerate_forward(const struct browser_window *bw, browser_window_history_enumerate_cb cb, void *user_data); + /** * Enumerate all entries that will be reached by the 'back' button * @@ -218,32 +132,33 @@ void browser_window_history_enumerate_forward(const struct browser_window *bw, void browser_window_history_enumerate_back(const struct browser_window *bw, browser_window_history_enumerate_cb cb, void *user_data); + /** * Returns the URL to a history entry * - * \param entry the history entry to retrieve the URL from - * \return the URL + * \param entry the history entry to retrieve the URL from + * \return A referenced nsurl URL */ -const char *browser_window_history_entry_get_url( - const struct history_entry *entry); +struct nsurl *browser_window_history_entry_get_url(const struct history_entry *entry); + /** * Returns the URL to a history entry * - * \param entry the history entry to retrieve the fragment id from - * \return the fragment id + * \param entry the history entry to retrieve the fragment id from + * \return the fragment id */ -const char *browser_window_history_entry_get_fragment_id( - const struct history_entry *entry); +const char *browser_window_history_entry_get_fragment_id(const struct history_entry *entry); + /** * Returns the title of a history entry * - * \param entry the history entry to retrieve the title from - * \return the title + * \param entry The history entry to retrieve the title from + * \return the title */ -const char *browser_window_history_entry_get_title( - const struct history_entry *entry); +const char *browser_window_history_entry_get_title(const struct history_entry *entry); + /** * Navigate to specified history entry, optionally in new window @@ -253,7 +168,6 @@ const char *browser_window_history_entry_get_title( * \param new_window open entry in new window * \return NSERROR_OK or error code on faliure. */ -nserror browser_window_history_go(struct browser_window *bw, - struct history_entry *entry, bool new_window); +nserror browser_window_history_go(struct browser_window *bw, struct history_entry *entry, bool new_window); #endif diff --git a/desktop/browser_private.h b/desktop/browser_private.h index 072a894d6..192d22bf0 100644 --- a/desktop/browser_private.h +++ b/desktop/browser_private.h @@ -17,7 +17,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file +/** + * \file * Browser window private structure. */ @@ -34,27 +35,94 @@ struct box; struct hlcache_handle; struct gui_window; -struct history; struct selection; +struct nsurl; + +/** + * history entry page information + */ +struct history_page { + struct nsurl *url; /**< Page URL, never NULL. */ + lwc_string *frag_id; /** Fragment identifier, or NULL. */ + char *title; /**< Page title, never NULL. */ + struct bitmap *bitmap; /**< Thumbnail bitmap, or NULL. */ + float scroll_x; /**< Scroll X offset when visited */ + float scroll_y; /**< Scroll Y offset when visited */ +}; + +/** + * A node in the history tree. + */ +struct history_entry { + struct history_page page; + struct history_entry *back; /**< Parent. */ + struct history_entry *next; /**< Next sibling. */ + struct history_entry *forward; /**< First child. */ + struct history_entry *forward_pref; /**< Child in direction of + current entry. */ + struct history_entry *forward_last; /**< Last child. */ + unsigned int children; /**< Number of children. */ + int x; /**< Position of node. */ + int y; /**< Position of node. */ +}; + +/** + * History tree for a window. + */ +struct history { + /** First page in tree (page that window opened with). */ + struct history_entry *start; + /** Current position in tree. */ + struct history_entry *current; + /** Width of layout. */ + int width; + /** Height of layout. */ + int height; +}; -/** Browser window data. */ +/** + * Browser window data. + */ struct browser_window { - /** Page currently displayed, or 0. Must have status READY or DONE. */ + /** + * Content handle of page currently displayed which must have + * READY or DONE status or NULL for no content. + */ struct hlcache_handle *current_content; - /** Page being loaded, or 0. */ + /** + * Content handle of page in process of being loaded or NULL + * if no page is being loaded. + */ struct hlcache_handle *loading_content; - /** Page Favicon */ - struct hlcache_handle *current_favicon; - /** handle for favicon which we started loading early */ - struct hlcache_handle *loading_favicon; - /** favicon fetch already failed - prevents infinite error looping */ - bool failed_favicon; - - /** Window history structure. */ + /** + * Favicon + */ + struct { + /** + * content handle of current page favicon + */ + struct hlcache_handle *current; + + /** + * content handle for favicon which we started loading + * early + */ + struct hlcache_handle *loading; + + /** + * flag to indicate favicon fetch already failed which + * prevents infinite error looping. + */ + bool failed; + } favicon; + + /** local history handle. */ struct history *history; - /** Platform specific window data. */ + /** + * Platform specific window data only valid at top level. + */ struct gui_window *window; /** Busy indicator is active. */ @@ -65,23 +133,32 @@ struct browser_window { /** Fragment identifier for current_content. */ lwc_string *frag_id; - /** Current drag status. */ - browser_drag_type drag_type; + /** + * Current drag status. + * + * These values are only vald whle type is not DRAGGING_NONE + */ + struct { + /** the type of drag in progress */ + browser_drag_type type; + + /** Current drag's browser window, when not in root bw. */ + struct browser_window *window; + + /** Mouse position at start of current scroll drag. */ + int start_x; + int start_y; - /** Current drag's browser window, when not in root bw. */ - struct browser_window *drag_window; + /** Scroll offsets at start of current scroll draw. */ + int start_scroll_x; + int start_scroll_y; - /** 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; - /** 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; + /** Frame resize directions for current frame resize drag. */ + unsigned int resize_left : 1; + unsigned int resize_right : 1; + unsigned int resize_up : 1; + unsigned int resize_down : 1; + } drag; /** Current fetch is download */ bool download; @@ -156,14 +233,15 @@ struct browser_window { struct jscontext *jsctx; /** cache of the currently displayed status text. */ - char *status_text; /**< Current status bar text. */ - int status_text_len; /**< Length of the browser_window::status_text buffer. */ - int status_match; /**< Number of times an idempotent status-set operation was performed. */ - int status_miss; /**< Number of times status was really updated. */ + struct { + char *text; /**< Current status bar text. */ + int text_len; /**< Length of the status::text buffer. */ + int match; /**< Number of times an idempotent status-set operation was performed. */ + int miss; /**< Number of times status was really updated. */ + } status; }; - /** * Initialise common parts of a browser window * @@ -174,6 +252,7 @@ struct browser_window { nserror browser_window_initialise_common(enum browser_window_create_flags flags, struct browser_window *bw, struct browser_window *existing); + /** * Get the dimensions of the area a browser window occupies * @@ -185,6 +264,7 @@ nserror browser_window_initialise_common(enum browser_window_create_flags flags, void browser_window_get_dimensions(struct browser_window *bw, int *width, int *height, bool scaled); + /** * Update the extent of the inside of a browser window to that of the current * content @@ -193,6 +273,7 @@ void browser_window_get_dimensions(struct browser_window *bw, */ void browser_window_update_extent(struct browser_window *bw); + /** * Change the status bar of a browser window. * @@ -201,6 +282,7 @@ void browser_window_update_extent(struct browser_window *bw); */ void browser_window_set_status(struct browser_window *bw, const char *text); + /** * Get the root level browser window * @@ -209,4 +291,68 @@ void browser_window_set_status(struct browser_window *bw, const char *text); */ struct browser_window * browser_window_get_root(struct browser_window *bw); + +/** + * Create a new history tree for a browser window window. + * + * \param bw browser window to create history for. + * + * \return NSERROR_OK or appropriate error otherwise + */ +nserror browser_window_history_create(struct browser_window *bw); + +/** + * Clone a bw's history tree for new bw + * + * \param existing browser window with history to clone. + * \param clone browser window to make cloned history for. + * + * \return NSERROR_OK or appropriate error otherwise + */ +nserror browser_window_history_clone(const struct browser_window *existing, + struct browser_window *clone); + + +/** + * Insert a url into the history tree. + * + * \param bw browser window with history object + * \param content content to add to history + * \param frag_id fragment identifier, or NULL. + * \return NSERROR_OK or error code on faliure. + * + * The page is added after the current entry and becomes current. + */ +nserror browser_window_history_add(struct browser_window *bw, + struct hlcache_handle *content, lwc_string *frag_id); + +/** + * Update the thumbnail and scroll offsets for the current entry. + * + * \param bw The browser window to update the history within. + * \param content content for current entry + * \return NSERROR_OK or error code on faliure. + */ +nserror browser_window_history_update(struct browser_window *bw, + struct hlcache_handle *content); + +/** + * Retrieve the stored scroll offsets for the current history entry + * + * \param bw The browser window to retrieve scroll offsets for. + * \param sx Pointer to a float for the X scroll offset + * \param sy Pointer to a float for the Y scroll offset + * \return NSERROR_OK or error code on failure. + */ +nserror browser_window_history_get_scroll(struct browser_window *bw, + float *sx, float *sy); + +/** + * Free a history structure. + * + * \param bw The browser window to destroy the history within. + */ +void browser_window_history_destroy(struct browser_window *bw); + + #endif diff --git a/desktop/cookie_manager.c b/desktop/cookie_manager.c index 5429f6864..a2aab8e9f 100644 --- a/desktop/cookie_manager.c +++ b/desktop/cookie_manager.c @@ -576,7 +576,9 @@ static nserror cookie_manager_init_entry_fields(void) goto error; } - cm_ctx.fields[COOKIE_M_DOMAIN].flags = TREE_FLAG_SHOW_NAME; + cm_ctx.fields[COOKIE_M_DOMAIN].flags = + TREE_FLAG_SHOW_NAME | + TREE_FLAG_SEARCHABLE; label = "TreeviewLabelDomain"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), @@ -718,7 +720,8 @@ static void cookie_manager_delete_entry(struct cookie_manager_entry *e) urldb_delete_cookie(domain, path, name); } else { - LOG("Delete cookie fail: ""need domain, path, and name."); + NSLOG(netsurf, INFO, + "Delete cookie fail: ""need domain, path, and name."); } } @@ -788,7 +791,7 @@ nserror cookie_manager_init(struct core_window_callback_table *cw_t, return err; } - LOG("Generating cookie manager data"); + NSLOG(netsurf, INFO, "Generating cookie manager data"); /* Init. cookie manager treeview entry fields */ err = cookie_manager_init_entry_fields(); @@ -808,7 +811,9 @@ nserror cookie_manager_init(struct core_window_callback_table *cw_t, err = treeview_create(&cm_ctx.tree, &cm_tree_cb_t, COOKIE_M_N_FIELDS, cm_ctx.fields, cw_t, core_window_handle, - TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS); + TREEVIEW_NO_MOVES | + TREEVIEW_DEL_EMPTY_DIRS | + TREEVIEW_SEARCHABLE); if (err != NSERROR_OK) { cm_ctx.tree = NULL; return err; @@ -825,7 +830,7 @@ nserror cookie_manager_init(struct core_window_callback_table *cw_t, /* Inform client of window height */ treeview_get_height(cm_ctx.tree); - LOG("Generated cookie manager data"); + NSLOG(netsurf, INFO, "Generated cookie manager data"); return NSERROR_OK; } @@ -837,7 +842,7 @@ nserror cookie_manager_fini(void) int i; nserror err; - LOG("Finalising cookie manager"); + NSLOG(netsurf, INFO, "Finalising cookie manager"); cm_ctx.built = false; @@ -860,7 +865,7 @@ nserror cookie_manager_fini(void) return err; } - LOG("Finalised cookie manager"); + NSLOG(netsurf, INFO, "Finalised cookie manager"); return err; } diff --git a/desktop/font_haru.c b/desktop/font_haru.c index caa751bcb..4ee9824f0 100644 --- a/desktop/font_haru.c +++ b/desktop/font_haru.c @@ -74,7 +74,10 @@ const struct font_functions haru_nsfont = { static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void *user_data) { - LOG("ERROR: in font_haru \n\terror_no=%x\n\tdetail_no=%d\n", (HPDF_UINT)error_no, (HPDF_UINT)detail_no); + NSLOG(netsurf, INFO, + "ERROR: in font_haru \n\terror_no=%x\n\tdetail_no=%d\n", + (HPDF_UINT)error_no, + (HPDF_UINT)detail_no); #ifdef FONT_HARU_DEBUG exit(1); #endif @@ -143,7 +146,9 @@ bool haru_nsfont_width(const plot_font_style_t *fstyle, *width = width_real; #ifdef FONT_HARU_DEBUG - LOG("Measuring string: %s ; Calculated width: %f %i", string_nt, width_real, *width); + NSLOG(netsurf, INFO, + "Measuring string: %s ; Calculated width: %f %i", string_nt, + width_real, *width); #endif free(string_nt); HPDF_Free(pdf); @@ -201,7 +206,11 @@ bool haru_nsfont_position_in_string(const plot_font_style_t *fstyle, *actual_x = real_width; #ifdef FONT_HARU_DEBUG - LOG("Position in string: %s at x: %i; Calculated position: %i", string_nt, x, *char_offset); + NSLOG(netsurf, INFO, + "Position in string: %s at x: %i; Calculated position: %i", + string_nt, + x, + *char_offset); #endif free(string_nt); HPDF_Free(pdf); @@ -246,7 +255,12 @@ bool haru_nsfont_split(const plot_font_style_t *fstyle, HPDF_TRUE, &real_width); #ifdef FONT_HARU_DEBUG - LOG("Splitting string: %s for width: %i ; Calculated position: %i Calculated real_width: %f", string_nt, x, *char_offset, real_width); + NSLOG(netsurf, INFO, + "Splitting string: %s for width: %i ; Calculated position: %i Calculated real_width: %f", + string_nt, + x, + *char_offset, + real_width); #endif *char_offset = offset - 1; @@ -327,7 +341,7 @@ bool haru_nsfont_apply_style(const plot_font_style_t *fstyle, strcat(font_name, "-Roman"); #ifdef FONT_HARU_DEBUG - LOG("Setting font: %s", font_name); + NSLOG(netsurf, INFO, "Setting font: %s", font_name); #endif size = fstyle->size; @@ -338,7 +352,7 @@ bool haru_nsfont_apply_style(const plot_font_style_t *fstyle, if (size <= 0) return true; - size /= FONT_SIZE_SCALE; + size /= PLOT_STYLE_SCALE; if (size > HPDF_MAX_FONTSIZE) size = HPDF_MAX_FONTSIZE; diff --git a/desktop/frames.c b/desktop/frames.c index 70e51e754..ebc54c6d5 100644 --- a/desktop/frames.c +++ b/desktop/frames.c @@ -33,8 +33,8 @@ #include "utils/utils.h" #include "netsurf/content.h" #include "content/hlcache.h" -#include "render/html.h" -#include "render/box.h" +#include "html/html.h" +#include "html/box.h" #include "desktop/browser_private.h" #include "desktop/frames.h" @@ -354,9 +354,11 @@ nserror browser_window_create_frameset(struct browser_window *bw, window->parent = bw; if (window->name) - LOG("Created frame '%s'", window->name); + NSLOG(netsurf, INFO, "Created frame '%s'", + window->name); else - LOG("Created frame (unnamed)"); + NSLOG(netsurf, INFO, + "Created frame (unnamed)"); } } @@ -675,25 +677,31 @@ void browser_window_resize_frame(struct browser_window *bw, int x, int y) assert((col >= 0) && (row >= 0)); sibling = NULL; - if (bw->drag_resize_left) + if (bw->drag.resize_left) { sibling = &parent->children[row * parent->cols + (col - 1)]; - else if (bw->drag_resize_right) + } else if (bw->drag.resize_right) { sibling = &parent->children[row * parent->cols + (col + 1)]; - if (sibling) + } + if (sibling) { change |= browser_window_resolve_frame_dimension(bw, sibling, x, y, true, false); + } sibling = NULL; - if (bw->drag_resize_up) + if (bw->drag.resize_up) { sibling = &parent->children[(row - 1) * parent->cols + col]; - else if (bw->drag_resize_down) + } else if (bw->drag.resize_down) { sibling = &parent->children[(row + 1) * parent->cols + col]; - if (sibling) + } + + if (sibling) { change |= browser_window_resolve_frame_dimension(bw, sibling, x, y, false, true); + } - if (change) + if (change) { browser_window_recalculate_frameset(parent); + } } @@ -711,20 +719,22 @@ bool browser_window_resolve_frame_dimension(struct browser_window *bw, /* extend/shrink the box to the pointer */ if (width) { - if (bw->drag_resize_left) + if (bw->drag.resize_left) { bw_dimension = bw->x + bw->width - x; - else + } else { bw_dimension = x - bw->x; + } bw_pixels = bw->width; sibling_pixels = sibling->width; bw_d = &bw->frame_width; sibling_d = &sibling->frame_width; frame_size = bw->parent->width; } else { - if (bw->drag_resize_up) + if (bw->drag.resize_up) { bw_dimension = bw->y + bw->height - y; - else + } else { bw_dimension = y - bw->y; + } bw_pixels = bw->height; sibling_pixels = sibling->height; bw_d = &bw->frame_height; @@ -919,12 +929,12 @@ static bool browser_window_resize_frames(struct browser_window *bw, * front end to clamp pointer range */ browser_window_set_drag_type(bw, DRAGGING_FRAME, NULL); - bw->drag_start_x = x; - bw->drag_start_y = y; - bw->drag_resize_left = left; - bw->drag_resize_right = right; - bw->drag_resize_up = up; - bw->drag_resize_down = down; + bw->drag.start_x = x; + bw->drag.start_y = y; + bw->drag.resize_left = left; + bw->drag.resize_right = right; + bw->drag.resize_up = up; + bw->drag.resize_down = down; } return true; } diff --git a/desktop/global_history.c b/desktop/global_history.c index a19349f51..ad39a3e41 100644 --- a/desktop/global_history.c +++ b/desktop/global_history.c @@ -536,7 +536,9 @@ static nserror global_history_initialise_entry_fields(void) goto error; } - gh_ctx.fields[GH_URL].flags = TREE_FLAG_COPY_TEXT; + gh_ctx.fields[GH_URL].flags = + TREE_FLAG_COPY_TEXT | + TREE_FLAG_SEARCHABLE; label = "TreeviewLabelURL"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), @@ -596,7 +598,7 @@ static nserror global_history_initialise_time(void) /* get the current time */ t = time(NULL); if (t == -1) { - LOG("time info unaviable"); + NSLOG(netsurf, INFO, "time info unaviable"); return NSERROR_UNKNOWN; } @@ -607,7 +609,7 @@ static nserror global_history_initialise_time(void) full_time->tm_hour = 0; t = mktime(full_time); if (t == -1) { - LOG("mktime failed"); + NSLOG(netsurf, INFO, "mktime failed"); return NSERROR_UNKNOWN; } @@ -729,7 +731,7 @@ nserror global_history_init(struct core_window_callback_table *cw_t, return err; } - LOG("Loading global history"); + NSLOG(netsurf, INFO, "Loading global history"); /* Init. global history treeview time */ err = global_history_initialise_time(); @@ -752,7 +754,8 @@ nserror global_history_init(struct core_window_callback_table *cw_t, err = treeview_create(&gh_ctx.tree, &gh_tree_cb_t, N_FIELDS, gh_ctx.fields, cw_t, core_window_handle, - TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS); + TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS | + TREEVIEW_SEARCHABLE); if (err != NSERROR_OK) { gh_ctx.tree = NULL; return err; @@ -785,7 +788,7 @@ nserror global_history_init(struct core_window_callback_table *cw_t, /* Inform client of window height */ treeview_get_height(gh_ctx.tree); - LOG("Loaded global history"); + NSLOG(netsurf, INFO, "Loaded global history"); return NSERROR_OK; } @@ -797,7 +800,7 @@ nserror global_history_fini(void) int i; nserror err; - LOG("Finalising global history"); + NSLOG(netsurf, INFO, "Finalising global history"); gh_ctx.built = false; @@ -815,7 +818,7 @@ nserror global_history_fini(void) return err; } - LOG("Finalised global history"); + NSLOG(netsurf, INFO, "Finalised global history"); return err; } @@ -832,7 +835,8 @@ nserror global_history_add(nsurl *url) data = urldb_get_url_data(url); if (data == NULL) { - LOG("Can't add URL to history that's not present in urldb."); + NSLOG(netsurf, INFO, + "Can't add URL to history that's not present in urldb."); return NSERROR_BAD_PARAMETER; } diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c index 88bb9baf5..ca9eff1da 100644 --- a/desktop/gui_factory.c +++ b/desktop/gui_factory.c @@ -82,12 +82,6 @@ static void gui_default_window_set_icon(struct gui_window *g, { } -static void gui_default_window_scroll_visible(struct gui_window *g, - int x0, int y0, - int x1, int y1) -{ - guit->window->set_scroll(g, x0, y0); -} static void gui_default_window_new_content(struct gui_window *g) { @@ -161,10 +155,7 @@ static nserror verify_window_register(struct gui_window_table *gwt) if (gwt->destroy == NULL) { return NSERROR_BAD_PARAMETER; } - if (gwt->redraw == NULL) { - return NSERROR_BAD_PARAMETER; - } - if (gwt->update == NULL) { + if (gwt->invalidate == NULL) { return NSERROR_BAD_PARAMETER; } if (gwt->get_scroll == NULL) { @@ -179,9 +170,6 @@ static nserror verify_window_register(struct gui_window_table *gwt) if (gwt->update_extent == NULL) { return NSERROR_BAD_PARAMETER; } - if (gwt->reformat == NULL) { - return NSERROR_BAD_PARAMETER; - } /* fill in the optional entries with defaults */ @@ -218,9 +206,6 @@ static nserror verify_window_register(struct gui_window_table *gwt) if (gwt->save_link == NULL) { gwt->save_link = gui_default_window_save_link; } - if (gwt->scroll_visible == NULL) { - gwt->scroll_visible = gui_default_window_scroll_visible; - } if (gwt->new_content == NULL) { gwt->new_content = gui_default_window_new_content; } diff --git a/desktop/hotlist.c b/desktop/hotlist.c index 78473c744..4bdd7c8cb 100644 --- a/desktop/hotlist.c +++ b/desktop/hotlist.c @@ -62,6 +62,8 @@ struct hotlist_ctx { struct treeview_field_desc fields[HL_N_FIELDS]; bool built; struct hotlist_folder *default_folder; + char *save_path; + bool save_scheduled; }; struct hotlist_ctx hl_ctx; @@ -73,6 +75,113 @@ struct hotlist_entry { }; +/* + * Get path for writing hotlist to + * + * \param path The final path of the hotlist + * \param loaded Updated to the path to write the holist to + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_get_temp_path(const char *path, char **temp_path) +{ + const char *extension = "-bk"; + char *joined; + int len; + + len = strlen(path) + strlen(extension); + + joined = malloc(len + 1); + if (joined == NULL) { + return NSERROR_NOMEM; + } + + if (snprintf(joined, len + 1, "%s%s", path, extension) != len) { + free(joined); + return NSERROR_UNKNOWN; + } + + *temp_path = joined; + return NSERROR_OK; +} + + +/* Save the hotlist to to a file at the given path + * + * \param path Path to save hotlist file to. NULL path is a no-op. + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_save(const char *path) +{ + nserror res = NSERROR_OK; + char *temp_path; + + /* NULL path is a no-op. */ + if (path == NULL) { + return NSERROR_OK; + } + + /* Get path to export to */ + res = hotlist_get_temp_path(path, &temp_path); + if (res != NSERROR_OK) { + return res; + } + + /* Export to temp path */ + res = hotlist_export(temp_path, NULL); + if (res != NSERROR_OK) { + goto cleanup; + } + + /* Remove old hotlist to handle non-POSIX rename() implementations. */ + (void)remove(path); + + /* Replace any old hotlist file with the one we just saved */ + if (rename(temp_path, path) != 0) { + res = NSERROR_SAVE_FAILED; + NSLOG(netsurf, INFO, "Error renaming hotlist: %s.", + strerror(errno)); + goto cleanup; + } + +cleanup: + free(temp_path); + + return res; +} + + +/** + * Scheduler callback for saving the hotlist. + * + * \param p Unused user data. + */ +static void hotlist_schedule_save_cb(void *p) +{ + hl_ctx.save_scheduled = false; + hotlist_save(hl_ctx.save_path); +} + + +/** + * Schedule a hotlist save. + * + * \return NSERROR_OK on success, or appropriate error otherwise + */ +static nserror hotlist_schedule_save(void) +{ + if (hl_ctx.save_scheduled == false && hl_ctx.save_path != NULL) { + nserror err = guit->misc->schedule(10 * 1000, + hotlist_schedule_save_cb, NULL); + if (err != NSERROR_OK) { + return err; + } + hl_ctx.save_scheduled = true; + } + + return NSERROR_OK; +} + + /** * Set a hotlist entry's data from the url_data. * @@ -436,6 +545,8 @@ hotlist_tree_node_entry_cb(struct treeview_node_msg msg, void *data) case TREE_MSG_NODE_DELETE: e->entry = NULL; hotlist_delete_entry_internal(e); + + err = hotlist_schedule_save(); break; case TREE_MSG_NODE_EDIT: @@ -459,6 +570,8 @@ hotlist_tree_node_entry_cb(struct treeview_node_msg msg, void *data) free((void *)old_text); } + err = hotlist_schedule_save(); + } else if (lwc_string_isequal(hl_ctx.fields[HL_URL].field, msg.data.node_edit.field, &match) == lwc_error_ok && match == true && @@ -476,6 +589,8 @@ hotlist_tree_node_entry_cb(struct treeview_node_msg msg, void *data) treeview_update_node_entry(hl_ctx.tree, e->entry, e->data, e); nsurl_unref(old_url); + + err = hotlist_schedule_save(); } } break; @@ -538,20 +653,20 @@ static nserror hotlist_load_entry(dom_node *li, hotlist_load_ctx *ctx) /* The li must contain an "a" element */ a = libdom_find_first_element(li, corestring_lwc_a); if (a == NULL) { - LOG("Missing <a> in <li>"); + NSLOG(netsurf, INFO, "Missing <a> in <li>"); return NSERROR_INVALID; } derror = dom_node_get_text_content(a, &title1); if (derror != DOM_NO_ERR) { - LOG("No title"); + NSLOG(netsurf, INFO, "No title"); dom_node_unref(a); return NSERROR_INVALID; } derror = dom_element_get_attribute(a, corestring_dom_href, &url1); if (derror != DOM_NO_ERR || url1 == NULL) { - LOG("No URL"); + NSLOG(netsurf, INFO, "No URL"); dom_string_unref(title1); dom_node_unref(a); return NSERROR_INVALID; @@ -569,7 +684,8 @@ static nserror hotlist_load_entry(dom_node *li, hotlist_load_ctx *ctx) dom_string_unref(url1); if (err != NSERROR_OK) { - LOG("Failed normalising '%s'", dom_string_data(url1)); + NSLOG(netsurf, INFO, "Failed normalising '%s'", + dom_string_data(url1)); if (title1 != NULL) { dom_string_unref(title1); @@ -596,7 +712,7 @@ static nserror hotlist_load_entry(dom_node *li, hotlist_load_ctx *ctx) /* - * Callback for libdom_iterate_child_elements, which dispite the namespace is + * Callback for libdom_iterate_child_elements, which despite the namespace is * a NetSurf function. * * \param node Node that is a child of the directory UL node @@ -649,7 +765,8 @@ nserror hotlist_load_directory_cb(dom_node *node, void *ctx) error = dom_node_get_text_content(node, &title); if (error != DOM_NO_ERR || title == NULL) { - LOG("Empty <h4> or memory exhausted."); + NSLOG(netsurf, INFO, + "Empty <h4> or memory exhausted."); dom_string_unref(name); return NSERROR_DOM; } @@ -729,36 +846,6 @@ nserror hotlist_load_directory_cb(dom_node *node, void *ctx) /* - * Get path for writing hotlist to - * - * \param path The final path of the hotlist - * \param loaded Updated to the path to write the holist to - * \return NSERROR_OK on success, or appropriate error otherwise - */ -static nserror hotlist_get_temp_path(const char *path, char **temp_path) -{ - const char *extension = "-bk"; - char *joined; - int len; - - len = strlen(path) + strlen(extension); - - joined = malloc(len + 1); - if (joined == NULL) { - return NSERROR_NOMEM; - } - - if (snprintf(joined, len + 1, "%s%s", path, extension) != len) { - free(joined); - return NSERROR_UNKNOWN; - } - - *temp_path = joined; - return NSERROR_OK; -} - - -/* * Load the hotlist data from file * * \param path The path to load the hotlist file from, or NULL @@ -777,7 +864,7 @@ static nserror hotlist_load(const char *path, bool *loaded) /* Handle no path */ if (path == NULL) { - LOG("No hotlist file path provided."); + NSLOG(netsurf, INFO, "No hotlist file path provided."); return NSERROR_OK; } @@ -918,45 +1005,6 @@ static nserror hotlist_generate(void) } -/* Save the hotlist to to a file at the given path - * - * \param path Path to save hostlist file to. - * \return NSERROR_OK on success, or appropriate error otherwise - */ -static nserror hotlist_save(const char *path) -{ - nserror res = NSERROR_OK; - char *temp_path; - - /* Get path to export to */ - res = hotlist_get_temp_path(path, &temp_path); - if (res != NSERROR_OK) { - return res; - } - - /* Export to temp path */ - res = hotlist_export(temp_path, NULL); - if (res != NSERROR_OK) { - goto cleanup; - } - - /* Remove old hotlist to handle non-POSIX rename() implementations. */ - (void)remove(path); - - /* Replace any old hotlist file with the one we just saved */ - if (rename(temp_path, path) != 0) { - res = NSERROR_SAVE_FAILED; - LOG("Error renaming hotlist: %s.", strerror(errno)); - goto cleanup; - } - -cleanup: - free(temp_path); - - return res; -} - - struct treeview_export_walk_ctx { FILE *fp; }; @@ -1150,8 +1198,10 @@ static nserror hotlist_initialise_entry_fields(void) goto error; } - hl_ctx.fields[HL_URL].flags = TREE_FLAG_ALLOW_EDIT | - TREE_FLAG_COPY_TEXT; + hl_ctx.fields[HL_URL].flags = + TREE_FLAG_ALLOW_EDIT | + TREE_FLAG_COPY_TEXT | + TREE_FLAG_SEARCHABLE; label = "TreeviewLabelURL"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), @@ -1227,7 +1277,9 @@ static nserror hotlist_populate(const char *path) /* Exported interface, documented in hotlist.h */ -nserror hotlist_init(const char *path) +nserror hotlist_init( + const char *load_path, + const char *save_path) { nserror err; @@ -1236,15 +1288,26 @@ nserror hotlist_init(const char *path) return err; } - LOG("Loading hotlist"); + NSLOG(netsurf, INFO, "Loading hotlist"); hl_ctx.tree = NULL; hl_ctx.built = false; hl_ctx.default_folder = NULL; + /* Store the save path */ + if (save_path != NULL) { + hl_ctx.save_path = strdup(save_path); + if (hl_ctx.save_path == NULL) { + return NSERROR_NOMEM; + } + } else { + hl_ctx.save_path = NULL; + } + /* Init. hotlist treeview entry fields */ err = hotlist_initialise_entry_fields(); if (err != NSERROR_OK) { + free(hl_ctx.save_path); hl_ctx.tree = NULL; return err; } @@ -1252,15 +1315,17 @@ nserror hotlist_init(const char *path) /* Create the hotlist treeview */ err = treeview_create(&hl_ctx.tree, &hl_tree_cb_t, HL_N_FIELDS, hl_ctx.fields, NULL, NULL, - TREEVIEW_NO_FLAGS); + TREEVIEW_SEARCHABLE); if (err != NSERROR_OK) { + free(hl_ctx.save_path); hl_ctx.tree = NULL; return err; } /* Populate the hotlist */ - err = hotlist_populate(path); + err = hotlist_populate(load_path); if (err != NSERROR_OK) { + free(hl_ctx.save_path); return err; } @@ -1269,7 +1334,7 @@ nserror hotlist_init(const char *path) * the treeview is built. */ hl_ctx.built = true; - LOG("Loaded hotlist"); + NSLOG(netsurf, INFO, "Loaded hotlist"); return NSERROR_OK; } @@ -1310,19 +1375,25 @@ nserror hotlist_manager_fini(void) /* Exported interface, documented in hotlist.h */ -nserror hotlist_fini(const char *path) +nserror hotlist_fini(void) { int i; nserror err; - LOG("Finalising hotlist"); + NSLOG(netsurf, INFO, "Finalising hotlist"); + + /* Remove any existing scheduled save callback */ + guit->misc->schedule(-1, hotlist_schedule_save_cb, NULL); + hl_ctx.save_scheduled = false; /* Save the hotlist */ - err = hotlist_save(path); + err = hotlist_save(hl_ctx.save_path); if (err != NSERROR_OK) { - LOG("Problem saving the hotlist."); + NSLOG(netsurf, INFO, "Problem saving the hotlist."); } + free(hl_ctx.save_path); + /* Destroy the hotlist treeview */ err = treeview_destroy(hl_ctx.tree); hl_ctx.built = false; @@ -1337,7 +1408,7 @@ nserror hotlist_fini(const char *path) return err; } - LOG("Finalised hotlist"); + NSLOG(netsurf, INFO, "Finalised hotlist"); return err; } @@ -1380,7 +1451,7 @@ nserror hotlist_add_url(nsurl *url) if (err != NSERROR_OK) return err; - return NSERROR_OK; + return hotlist_schedule_save(); } diff --git a/desktop/hotlist.h b/desktop/hotlist.h index c77ac92d2..571f4de97 100644 --- a/desktop/hotlist.h +++ b/desktop/hotlist.h @@ -40,10 +40,16 @@ struct rect; * be called before URLs can be added to the hotlist, and before the * hotlist can be queried to ask if URLs are present in the hotlist. * - * \param path The path to hotlist file to load + * In read-only mode the hotlist can be modified, but changes will not + * persist over sessions. + * + * \param load_path The path to load hotlist from. + * \param save_path The path to save hotlist to, or NULL for read-only mode. * \return NSERROR_OK on success, appropriate error otherwise */ -nserror hotlist_init(const char *path); +nserror hotlist_init( + const char *load_path, + const char *save_path); /** * Initialise the hotlist manager. @@ -67,7 +73,6 @@ nserror hotlist_manager_init(struct core_window_callback_table *cw_t, * allowing destruction of a GUI hotlist window, without finalising the * hotlist module. * - * \param path The path to save hotlist to * \return NSERROR_OK on success, appropriate error otherwise */ nserror hotlist_manager_fini(void); @@ -79,10 +84,9 @@ nserror hotlist_manager_fini(void); * internal data. After calling this if hotlist is required again, * hotlist_init must be called. * - * \param path The path to save hotlist to * \return NSERROR_OK on success, appropriate error otherwise */ -nserror hotlist_fini(const char *path); +nserror hotlist_fini(void); /** * Add an entry to the hotlist for given URL. diff --git a/desktop/knockout.c b/desktop/knockout.c index bcfc272eb..127a48cdb 100644 --- a/desktop/knockout.c +++ b/desktop/knockout.c @@ -16,8 +16,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * Knockout rendering (implementation). +/** + * \file + * Knockout rendering implementation. * * Knockout rendering is an optimisation which is particularly for * unaccelerated screen redraw. It tries to avoid plotting the same area more @@ -86,47 +87,6 @@ struct knockout_box; struct knockout_entry; - -static void knockout_calculate(int x0, int y0, int x1, int y1, struct knockout_box *box); -static bool knockout_plot_fill_recursive(struct knockout_box *box, plot_style_t *plot_style); -static bool knockout_plot_bitmap_recursive(struct knockout_box *box, - struct knockout_entry *entry); - -static bool knockout_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle); -static bool knockout_plot_polygon(const int *p, unsigned int n, const plot_style_t *pstyle); -static bool knockout_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *plot_style); -static bool knockout_plot_clip(const struct rect *clip); -static bool knockout_plot_text(int x, int y, const char *text, size_t length, - const plot_font_style_t *fstyle); -static bool knockout_plot_disc(int x, int y, int radius, const plot_style_t *pstyle); -static bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *pstyle); -static bool knockout_plot_bitmap(int x, int y, int width, int height, - struct bitmap *bitmap, colour bg, - bitmap_flags_t flags); -static bool knockout_plot_flush(void); -static bool knockout_plot_group_start(const char *name); -static bool knockout_plot_group_end(void); -static bool knockout_plot_path(const float *p, unsigned int n, colour fill, - float width, colour c, const float transform[6]); - - -const struct plotter_table knockout_plotters = { - .rectangle = knockout_plot_rectangle, - .line = knockout_plot_line, - .polygon = knockout_plot_polygon, - .clip = knockout_plot_clip, - .text = knockout_plot_text, - .disc = knockout_plot_disc, - .arc = knockout_plot_arc, - .bitmap = knockout_plot_bitmap, - .group_start = knockout_plot_group_start, - .group_end = knockout_plot_group_end, - .flush = knockout_plot_flush, - .path = knockout_plot_path, - .option_knockout = true, -}; - - typedef enum { KNOCKOUT_PLOT_RECTANGLE, KNOCKOUT_PLOT_LINE, @@ -155,17 +115,11 @@ struct knockout_entry { struct knockout_box *box; /* relating series of knockout clips */ union { struct { - int x0; - int y0; - int x1; - int y1; + struct rect r; plot_style_t plot_style; } rectangle; struct { - int x0; - int y0; - int x1; - int y1; + struct rect l; plot_style_t plot_style; } line; struct { @@ -174,10 +128,7 @@ struct knockout_entry { plot_style_t plot_style; } polygon; struct { - int x0; - int y0; - int x1; - int y1; + struct rect r; plot_style_t plot_style; } fill; struct rect clip; @@ -231,166 +182,193 @@ static struct plotter_table real_plot; static struct rect clip_cur; static int nested_depth = 0; + /** - * Start a knockout plotting session - * - * \param ctx the redraw context with real plotter table - * \param knk_ctx updated to copy of ctx, with plotter table replaced - * \return true on success, false otherwise + * fill an area recursively */ -bool knockout_plot_start(const struct redraw_context *ctx, - struct redraw_context *knk_ctx) +static nserror +knockout_plot_fill_recursive(const struct redraw_context *ctx, + struct knockout_box *box, + plot_style_t *plot_style) { - /* check if we're recursing */ - if (nested_depth++ > 0) { - /* we should already have the knockout renderer as default */ - assert(ctx->plot->rectangle == knockout_plotters.rectangle); - *knk_ctx = *ctx; - return true; - } - - /* end any previous sessions */ - if (knockout_entry_cur > 0) - knockout_plot_end(); - - /* get copy of real plotter table */ - real_plot = *(ctx->plot); + struct knockout_box *parent; + nserror res; + nserror ffres = NSERROR_OK; /* first failing result */ - /* set up knockout rendering context */ - *knk_ctx = *ctx; - knk_ctx->plot = &knockout_plotters; - return true; + for (parent = box; parent; parent = parent->next) { + if (parent->deleted) + continue; + if (parent->child) { + res = knockout_plot_fill_recursive(ctx, + parent->child, + plot_style); + } else { + res = real_plot.rectangle(ctx, plot_style, &parent->bbox); + } + /* remember the first error */ + if ((res != NSERROR_OK) && (ffres == NSERROR_OK)) { + ffres = res; + } + } + return ffres; } /** - * End a knockout plotting session - * - * \return true on success, false otherwise + * bitmap plot recusivley */ -bool knockout_plot_end(void) +static nserror +knockout_plot_bitmap_recursive(const struct redraw_context *ctx, + struct knockout_box *box, + struct knockout_entry *entry) { - /* only output when we've finished any nesting */ - if (--nested_depth == 0) - return knockout_plot_flush(); + nserror res; + nserror ffres = NSERROR_OK; /* first failing result */ + struct knockout_box *parent; - assert(nested_depth > 0); - return true; -} + for (parent = box; parent; parent = parent->next) { + if (parent->deleted) + continue; + if (parent->child) { + res = knockout_plot_bitmap_recursive(ctx, + parent->child, + entry); + } else { + real_plot.clip(ctx, &parent->bbox); + res = real_plot.bitmap(ctx, + entry->data.bitmap.bitmap, + entry->data.bitmap.x, + entry->data.bitmap.y, + entry->data.bitmap.width, + entry->data.bitmap.height, + entry->data.bitmap.bg, + entry->data.bitmap.flags); + } + /* remember the first error */ + if ((res != NSERROR_OK) && (ffres == NSERROR_OK)) { + ffres = res; + } + } + return ffres; +} /** * Flush the current knockout session to empty the buffers * * \return true on success, false otherwise */ -bool knockout_plot_flush(void) +static nserror knockout_plot_flush(const struct redraw_context *ctx) { int i; - bool success = true; struct knockout_box *box; + nserror res = NSERROR_OK; /* operation result */ + nserror ffres = NSERROR_OK; /* first failing result */ /* debugging information */ #ifdef KNOCKOUT_DEBUG - LOG("Entries are %i/%i, %i/%i, %i/%i", knockout_entry_cur, KNOCKOUT_ENTRIES, knockout_box_cur, KNOCKOUT_BOXES, knockout_polygon_cur, KNOCKOUT_POLYGONS); + NSLOG(netsurf, INFO, "Entries are %i/%i, %i/%i, %i/%i", + knockout_entry_cur, KNOCKOUT_ENTRIES, knockout_box_cur, + KNOCKOUT_BOXES, knockout_polygon_cur, KNOCKOUT_POLYGONS); #endif for (i = 0; i < knockout_entry_cur; i++) { switch (knockout_entries[i].type) { case KNOCKOUT_PLOT_RECTANGLE: - success &= real_plot.rectangle( - knockout_entries[i].data.rectangle.x0, - knockout_entries[i].data.rectangle.y0, - knockout_entries[i].data.rectangle.x1, - knockout_entries[i].data.rectangle.y1, - &knockout_entries[i].data.rectangle.plot_style); + res = real_plot.rectangle(ctx, + &knockout_entries[i].data.rectangle.plot_style, + &knockout_entries[i].data.rectangle.r); break; + case KNOCKOUT_PLOT_LINE: - success &= real_plot.line( - knockout_entries[i].data.line.x0, - knockout_entries[i].data.line.y0, - knockout_entries[i].data.line.x1, - knockout_entries[i].data.line.y1, - &knockout_entries[i].data.line.plot_style); + res = real_plot.line(ctx, + &knockout_entries[i].data.line.plot_style, + &knockout_entries[i].data.line.l); break; + case KNOCKOUT_PLOT_POLYGON: - success &= real_plot.polygon( - knockout_entries[i].data.polygon.p, - knockout_entries[i].data.polygon.n, - &knockout_entries[i].data.polygon.plot_style); + res = real_plot.polygon(ctx, + &knockout_entries[i].data.polygon.plot_style, + knockout_entries[i].data.polygon.p, + knockout_entries[i].data.polygon.n); break; + case KNOCKOUT_PLOT_FILL: box = knockout_entries[i].box->child; - if (box) - success &= knockout_plot_fill_recursive(box, - &knockout_entries[i].data.fill.plot_style); - else if (!knockout_entries[i].box->deleted) - success &= real_plot.rectangle( - knockout_entries[i].data.fill.x0, - knockout_entries[i].data.fill.y0, - knockout_entries[i].data.fill.x1, - knockout_entries[i].data.fill.y1, - &knockout_entries[i].data.fill.plot_style); + if (box) { + res = knockout_plot_fill_recursive(ctx, + box, + &knockout_entries[i].data.fill.plot_style); + } else if (!knockout_entries[i].box->deleted) { + res = real_plot.rectangle(ctx, + &knockout_entries[i].data.fill.plot_style, + &knockout_entries[i].data.fill.r); + } break; + case KNOCKOUT_PLOT_CLIP: - success &= real_plot.clip( - &knockout_entries[i].data.clip); + res = real_plot.clip(ctx, &knockout_entries[i].data.clip); break; + case KNOCKOUT_PLOT_TEXT: - success &= real_plot.text( + res = real_plot.text(ctx, + &knockout_entries[i].data.text.font_style, knockout_entries[i].data.text.x, knockout_entries[i].data.text.y, knockout_entries[i].data.text.text, - knockout_entries[i].data.text.length, - &knockout_entries[i].data.text.font_style); + knockout_entries[i].data.text.length); break; + case KNOCKOUT_PLOT_DISC: - success &= real_plot.disc( + res = real_plot.disc(ctx, + &knockout_entries[i].data.disc.plot_style, knockout_entries[i].data.disc.x, knockout_entries[i].data.disc.y, - knockout_entries[i].data.disc.radius, - &knockout_entries[i].data.disc.plot_style); + knockout_entries[i].data.disc.radius); break; + case KNOCKOUT_PLOT_ARC: - success &= real_plot.arc( + res = real_plot.arc(ctx, + &knockout_entries[i].data.arc.plot_style, knockout_entries[i].data.arc.x, knockout_entries[i].data.arc.y, knockout_entries[i].data.arc.radius, knockout_entries[i].data.arc.angle1, - knockout_entries[i].data.arc.angle2, - &knockout_entries[i].data.arc.plot_style); + knockout_entries[i].data.arc.angle2); break; + case KNOCKOUT_PLOT_BITMAP: box = knockout_entries[i].box->child; if (box) { - success &= knockout_plot_bitmap_recursive(box, + res = knockout_plot_bitmap_recursive(ctx, + box, &knockout_entries[i]); } else if (!knockout_entries[i].box->deleted) { - success &= real_plot.bitmap( - knockout_entries[i].data. - bitmap.x, - knockout_entries[i].data. - bitmap.y, - knockout_entries[i].data. - bitmap.width, - knockout_entries[i].data. - bitmap.height, - knockout_entries[i].data. - bitmap.bitmap, - knockout_entries[i].data. - bitmap.bg, - knockout_entries[i].data. - bitmap.flags); + res = real_plot.bitmap(ctx, + knockout_entries[i].data.bitmap.bitmap, + knockout_entries[i].data.bitmap.x, + knockout_entries[i].data.bitmap.y, + knockout_entries[i].data.bitmap.width, + knockout_entries[i].data.bitmap.height, + knockout_entries[i].data.bitmap.bg, + knockout_entries[i].data.bitmap.flags); } break; + case KNOCKOUT_PLOT_GROUP_START: - success &= real_plot.group_start( - knockout_entries[i].data.group_start.name); + res = real_plot.group_start(ctx, + knockout_entries[i].data.group_start.name); break; + case KNOCKOUT_PLOT_GROUP_END: - success &= real_plot.group_end(); + res = real_plot.group_end(ctx); break; } + + /* remember the first error */ + if ((res != NSERROR_OK) && (ffres == NSERROR_OK)) { + ffres = res; + } } knockout_entry_cur = 0; @@ -398,20 +376,24 @@ bool knockout_plot_flush(void) knockout_polygon_cur = 0; knockout_list = NULL; - return success; + return ffres; } /** * Knockout a section of previous rendering * + * \param ctx The current redraw context. * \param x0 The left edge of the removal box * \param y0 The bottom edge of the removal box * \param x1 The right edge of the removal box * \param y1 The top edge of the removal box * \param owner The parent box set to consider, or NULL for top level -*/ -void knockout_calculate(int x0, int y0, int x1, int y1, struct knockout_box *owner) + */ +static void +knockout_calculate(const struct redraw_context *ctx, + int x0, int y0, int x1, int y1, + struct knockout_box *owner) { struct knockout_box *box; struct knockout_box *parent; @@ -464,11 +446,11 @@ void knockout_calculate(int x0, int y0, int x1, int y1, struct knockout_box *own /* has the box been replaced by children? */ if (parent->child) { - knockout_calculate(x0, y0, x1, y1, parent); + knockout_calculate(ctx, x0, y0, x1, y1, parent); } else { /* we need a maximum of 4 child boxes */ if (knockout_box_cur + 4 >= KNOCKOUT_BOXES) { - knockout_plot_flush(); + knockout_plot_flush(ctx); return; } @@ -527,136 +509,138 @@ void knockout_calculate(int x0, int y0, int x1, int y1, struct knockout_box *own } -bool knockout_plot_fill_recursive(struct knockout_box *box, plot_style_t *plot_style) -{ - bool success = true; - struct knockout_box *parent; - - for (parent = box; parent; parent = parent->next) { - if (parent->deleted) - continue; - if (parent->child) - knockout_plot_fill_recursive(parent->child, plot_style); - else - success &= real_plot.rectangle(parent->bbox.x0, - parent->bbox.y0, - parent->bbox.x1, - parent->bbox.y1, - plot_style); - } - return success; -} - - -bool knockout_plot_bitmap_recursive(struct knockout_box *box, - struct knockout_entry *entry) -{ - bool success = true; - struct knockout_box *parent; - - for (parent = box; parent; parent = parent->next) { - if (parent->deleted) - continue; - if (parent->child) - knockout_plot_bitmap_recursive(parent->child, entry); - else { - success &= real_plot.clip(&parent->bbox); - success &= real_plot.bitmap(entry->data.bitmap.x, - entry->data.bitmap.y, - entry->data.bitmap.width, - entry->data.bitmap.height, - entry->data.bitmap.bitmap, - entry->data.bitmap.bg, - entry->data.bitmap.flags); - } - } - return success; -} - -bool knockout_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *pstyle) +/** + * knockout rectangle plotting. + * + * The rectangle can be filled an outline or both controlled + * by the plot style The line can be solid, dotted or + * dashed. Top left corner at (x0,y0) and rectangle has given + * width and height. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the rectangle plot. + * \param rect A rectangle defining the line to be drawn + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_rectangle(const struct redraw_context *ctx, + const plot_style_t *pstyle, + const struct rect *rect) { int kx0, ky0, kx1, ky1; + nserror res = NSERROR_OK; - if (pstyle->fill_type != PLOT_OP_TYPE_NONE) { + if (pstyle->fill_type != PLOT_OP_TYPE_NONE) { /* filled draw */ /* get our bounds */ - kx0 = (x0 > clip_cur.x0) ? x0 : clip_cur.x0; - ky0 = (y0 > clip_cur.y0) ? y0 : clip_cur.y0; - kx1 = (x1 < clip_cur.x1) ? x1 : clip_cur.x1; - ky1 = (y1 < clip_cur.y1) ? y1 : clip_cur.y1; + kx0 = (rect->x0 > clip_cur.x0) ? rect->x0 : clip_cur.x0; + ky0 = (rect->y0 > clip_cur.y0) ? rect->y0 : clip_cur.y0; + kx1 = (rect->x1 < clip_cur.x1) ? rect->x1 : clip_cur.x1; + ky1 = (rect->y1 < clip_cur.y1) ? rect->y1 : clip_cur.y1; if ((kx0 > clip_cur.x1) || (kx1 < clip_cur.x0) || - (ky0 > clip_cur.y1) || (ky1 < clip_cur.y0)) - return true; + (ky0 > clip_cur.y1) || (ky1 < clip_cur.y0)) { + return NSERROR_OK; + } /* fills both knock out and get knocked out */ - knockout_calculate(kx0, ky0, kx1, ky1, NULL); - knockout_boxes[knockout_box_cur].bbox.x0 = x0; - knockout_boxes[knockout_box_cur].bbox.y0 = y0; - knockout_boxes[knockout_box_cur].bbox.x1 = x1; - knockout_boxes[knockout_box_cur].bbox.y1 = y1; + knockout_calculate(ctx, kx0, ky0, kx1, ky1, NULL); + knockout_boxes[knockout_box_cur].bbox = *rect; knockout_boxes[knockout_box_cur].deleted = false; knockout_boxes[knockout_box_cur].child = NULL; knockout_boxes[knockout_box_cur].next = knockout_list; knockout_list = &knockout_boxes[knockout_box_cur]; knockout_entries[knockout_entry_cur].box = &knockout_boxes[knockout_box_cur]; - knockout_entries[knockout_entry_cur].data.fill.x0 = x0; - knockout_entries[knockout_entry_cur].data.fill.y0 = y0; - knockout_entries[knockout_entry_cur].data.fill.x1 = x1; - knockout_entries[knockout_entry_cur].data.fill.y1 = y1; + knockout_entries[knockout_entry_cur].data.fill.r = *rect; knockout_entries[knockout_entry_cur].data.fill.plot_style = *pstyle; knockout_entries[knockout_entry_cur].data.fill.plot_style.stroke_type = PLOT_OP_TYPE_NONE; /* ensure we only plot the fill */ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_FILL; if ((++knockout_entry_cur >= KNOCKOUT_ENTRIES) || - (++knockout_box_cur >= KNOCKOUT_BOXES)) - knockout_plot_flush(); - } + (++knockout_box_cur >= KNOCKOUT_BOXES)) { + res = knockout_plot_flush(ctx); + } + } if (pstyle->stroke_type != PLOT_OP_TYPE_NONE) { /* draw outline */ - knockout_entries[knockout_entry_cur].data.rectangle.x0 = x0; - knockout_entries[knockout_entry_cur].data.rectangle.y0 = y0; - knockout_entries[knockout_entry_cur].data.rectangle.x1 = x1; - knockout_entries[knockout_entry_cur].data.rectangle.y1 = y1; + knockout_entries[knockout_entry_cur].data.rectangle.r = *rect; knockout_entries[knockout_entry_cur].data.fill.plot_style = *pstyle; knockout_entries[knockout_entry_cur].data.fill.plot_style.fill_type = PLOT_OP_TYPE_NONE; /* ensure we only plot the outline */ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_RECTANGLE; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); - } - return true; + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + res = knockout_plot_flush(ctx); + } + } + return res; } -bool knockout_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle) + +/** + * Knockout line plotting. + * + * plot a line from (x0,y0) to (x1,y1). Coordinates are at + * centre of line width/thickness. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the line plot. + * \param line A rectangle defining the line to be drawn + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_line(const struct redraw_context *ctx, + const plot_style_t *pstyle, + const struct rect *line) { - knockout_entries[knockout_entry_cur].data.line.x0 = x0; - knockout_entries[knockout_entry_cur].data.line.y0 = y0; - knockout_entries[knockout_entry_cur].data.line.x1 = x1; - knockout_entries[knockout_entry_cur].data.line.y1 = y1; + knockout_entries[knockout_entry_cur].data.line.l = *line; knockout_entries[knockout_entry_cur].data.line.plot_style = *pstyle; knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_LINE; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); - return true; + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + return knockout_plot_flush(ctx); + } + return NSERROR_OK; } -bool knockout_plot_polygon(const int *p, unsigned int n, const plot_style_t *pstyle) +/** + * Knockout polygon plotting. + * + * Plots a filled polygon with straight lines between + * points. The lines around the edge of the ploygon are not + * plotted. The polygon is filled with the non-zero winding + * rule. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the polygon plot. + * \param p verticies of polygon + * \param n number of verticies. + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_polygon(const struct redraw_context *ctx, + const plot_style_t *pstyle, + const int *p, + unsigned int n) { - bool success = true; int *dest; + nserror res = NSERROR_OK; + nserror ffres = NSERROR_OK; /* ensure we have sufficient room even when flushed */ if (n * 2 >= KNOCKOUT_POLYGONS) { - knockout_plot_flush(); - success = real_plot.polygon(p, n, pstyle); - return success; + ffres = knockout_plot_flush(ctx); + res = real_plot.polygon(ctx, pstyle, p, n); + /* return the first error */ + if ((res != NSERROR_OK) && (ffres == NSERROR_OK)) { + ffres = res; + } + return ffres; } /* ensure we have enough room right now */ - if (knockout_polygon_cur + n * 2 >= KNOCKOUT_POLYGONS) - knockout_plot_flush(); + if (knockout_polygon_cur + n * 2 >= KNOCKOUT_POLYGONS) { + ffres = knockout_plot_flush(ctx); + } /* copy our data */ dest = &(knockout_polygons[knockout_polygon_cur]); @@ -666,27 +650,62 @@ bool knockout_plot_polygon(const int *p, unsigned int n, const plot_style_t *pst knockout_entries[knockout_entry_cur].data.polygon.n = n; knockout_entries[knockout_entry_cur].data.polygon.plot_style = *pstyle; knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_POLYGON; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); - return true; + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + res = knockout_plot_flush(ctx); + } + /* return the first error */ + if ((res != NSERROR_OK) && (ffres == NSERROR_OK)) { + ffres = res; + } + return ffres; } -bool knockout_plot_path(const float *p, unsigned int n, colour fill, - float width, colour c, const float transform[6]) +/** + * knockout path plotting. + * + * The knockout implementation simply flushes the queue and plots the path + * directly using real plotter. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the path plot. + * \param p elements of path + * \param n nunber of elements on path + * \param transform A transform to apply to the path. + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_path(const struct redraw_context *ctx, + const plot_style_t *pstyle, + const float *p, + unsigned int n, + const float transform[6]) { - knockout_plot_flush(); - return real_plot.path(p, n, fill, width, c, transform); + nserror res; + nserror ffres; + + ffres = knockout_plot_flush(ctx); + res = real_plot.path(ctx, pstyle, p, n, transform); + + /* return the first error */ + if ((res != NSERROR_OK) && (ffres == NSERROR_OK)) { + ffres = res; + } + return ffres; } -bool knockout_plot_clip(const struct rect *clip) +static nserror +knockout_plot_clip(const struct redraw_context *ctx, const struct rect *clip) { + nserror res = NSERROR_OK; + if (clip->x1 < clip->x0 || clip->y0 > clip->y1) { #ifdef KNOCKOUT_DEBUG - LOG("bad clip rectangle %i %i %i %i", clip->x0, clip->y0, clip->x1, clip->y1); + NSLOG(netsurf, INFO, "bad clip rectangle %i %i %i %i", + clip->x0, clip->y0, clip->x1, clip->y1); #endif - return false; + return NSERROR_BAD_SIZE; } /* memorise clip for bitmap tiling */ @@ -694,41 +713,107 @@ bool knockout_plot_clip(const struct rect *clip) knockout_entries[knockout_entry_cur].data.clip = *clip; knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_CLIP; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); - return true; + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + res = knockout_plot_flush(ctx); + } + return res; } -bool knockout_plot_text(int x, int y, const char *text, size_t length, - const plot_font_style_t *fstyle) +/** + * Text plotting. + * + * \param ctx The current redraw context. + * \param fstyle plot style for this text + * \param x x coordinate + * \param y y coordinate + * \param text UTF-8 string to plot + * \param length length of string, in bytes + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_text(const struct redraw_context *ctx, + const plot_font_style_t *fstyle, + int x, + int y, + const char *text, + size_t length) { + nserror res = NSERROR_OK; + knockout_entries[knockout_entry_cur].data.text.x = x; knockout_entries[knockout_entry_cur].data.text.y = y; knockout_entries[knockout_entry_cur].data.text.text = text; knockout_entries[knockout_entry_cur].data.text.length = length; knockout_entries[knockout_entry_cur].data.text.font_style = *fstyle; knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_TEXT; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); - return true; + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + res = knockout_plot_flush(ctx); + } + return res; } -bool knockout_plot_disc(int x, int y, int radius, const plot_style_t *pstyle) +/** + * knockout circle plotting + * + * Plot a circle centered on (x,y), which is optionally filled. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the circle plot. + * \param x x coordinate of circle centre. + * \param y y coordinate of circle centre. + * \param radius circle radius. + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_disc(const struct redraw_context *ctx, + const plot_style_t *pstyle, + int x, + int y, + int radius) { + nserror res = NSERROR_OK; + knockout_entries[knockout_entry_cur].data.disc.x = x; knockout_entries[knockout_entry_cur].data.disc.y = y; knockout_entries[knockout_entry_cur].data.disc.radius = radius; knockout_entries[knockout_entry_cur].data.disc.plot_style = *pstyle; knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_DISC; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); - return true; + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + res = knockout_plot_flush(ctx); + } + return res; } -bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *pstyle) + +/** + * Plots an arc + * + * plot an arc segment around (x,y), anticlockwise from angle1 + * to angle2. Angles are measured anticlockwise from + * horizontal, in degrees. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the arc plot. + * \param x The x coordinate of the arc. + * \param y The y coordinate of the arc. + * \param radius The radius of the arc. + * \param angle1 The start angle of the arc. + * \param angle2 The finish angle of the arc. + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_arc(const struct redraw_context *ctx, + const plot_style_t *pstyle, + int x, + int y, + int radius, + int angle1, + int angle2) { + nserror res = NSERROR_OK; + knockout_entries[knockout_entry_cur].data.arc.x = x; knockout_entries[knockout_entry_cur].data.arc.y = y; knockout_entries[knockout_entry_cur].data.arc.radius = radius; @@ -736,18 +821,48 @@ bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2, const p knockout_entries[knockout_entry_cur].data.arc.angle2 = angle2; knockout_entries[knockout_entry_cur].data.arc.plot_style = *pstyle; knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_ARC; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); - return true; + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + res = knockout_plot_flush(ctx); + } + return res; } - -bool knockout_plot_bitmap(int x, int y, int width, int height, - struct bitmap *bitmap, colour bg, - bitmap_flags_t flags) +/** + * knockout bitmap plotting. + * + * Tiled plot of a bitmap image. (x,y) gives the top left + * coordinate of an explicitly placed tile. From this tile the + * image can repeat in all four directions -- up, down, left + * and right -- to the extents given by the current clip + * rectangle. + * + * The bitmap_flags say whether to tile in the x and y + * directions. If not tiling in x or y directions, the single + * image is plotted. The width and height give the dimensions + * the image is to be scaled to. + * + * \param ctx The current redraw context. + * \param bitmap The bitmap to plot + * \param x The x coordinate to plot the bitmap + * \param y The y coordiante to plot the bitmap + * \param width The width of area to plot the bitmap into + * \param height The height of area to plot the bitmap into + * \param bg the background colour to alpha blend into + * \param flags the flags controlling the type of plot operation + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_bitmap(const struct redraw_context *ctx, + struct bitmap *bitmap, + int x, int y, + int width, int height, + colour bg, + bitmap_flags_t flags) { int kx0, ky0, kx1, ky1; + nserror res; + nserror ffres = NSERROR_OK; /* get our bounds */ kx0 = clip_cur.x0; @@ -760,7 +875,7 @@ bool knockout_plot_bitmap(int x, int y, int width, int height, if (x + width < kx1) kx1 = x + width; if ((kx0 > clip_cur.x1) || (kx1 < clip_cur.x0)) - return true; + return NSERROR_OK; } if (!(flags & BITMAPF_REPEAT_Y)) { if (y > ky0) @@ -768,12 +883,12 @@ bool knockout_plot_bitmap(int x, int y, int width, int height, if (y + height < ky1) ky1 = y + height; if ((ky0 > clip_cur.y1) || (ky1 < clip_cur.y0)) - return true; + return NSERROR_OK; } /* tiled bitmaps both knock out and get knocked out */ if (guit->bitmap->get_opaque(bitmap)) { - knockout_calculate(kx0, ky0, kx1, ky1, NULL); + knockout_calculate(ctx, kx0, ky0, kx1, ky1, NULL); } knockout_boxes[knockout_box_cur].bbox.x0 = kx0; knockout_boxes[knockout_box_cur].bbox.y0 = ky0; @@ -792,33 +907,120 @@ bool knockout_plot_bitmap(int x, int y, int width, int height, knockout_entries[knockout_entry_cur].data.bitmap.bg = bg; knockout_entries[knockout_entry_cur].data.bitmap.flags = flags; knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_BITMAP; + if ((++knockout_entry_cur >= KNOCKOUT_ENTRIES) || - (++knockout_box_cur >= KNOCKOUT_BOXES)) - knockout_plot_flush(); - return knockout_plot_clip(&clip_cur); + (++knockout_box_cur >= KNOCKOUT_BOXES)) { + ffres = knockout_plot_flush(ctx); + } + res = knockout_plot_clip(ctx, &clip_cur); + /* return the first error */ + if ((res != NSERROR_OK) && (ffres == NSERROR_OK)) { + ffres = res; + } + return ffres; } -bool knockout_plot_group_start(const char *name) + +/** + * Start of a group of objects. + * + * Used when plotter implements export to a vector graphics file format. + * + * \param ctx The current redraw context. + * \param name The name of the group being started. + * \return NSERROR_OK on success else error code. + */ +static nserror +knockout_plot_group_start(const struct redraw_context *ctx, const char *name) { if (real_plot.group_start == NULL) { - return true; + return NSERROR_OK; } knockout_entries[knockout_entry_cur].data.group_start.name = name; knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_GROUP_START; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); - return true; + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + return knockout_plot_flush(ctx); + } + return NSERROR_OK; } -bool knockout_plot_group_end(void) + +/** + * End a group of objects. + * + * Used when plotter implements export to a vector graphics file format. + * + * \param ctx The current redraw context. + * \return NSERROR_OK on success else error code. + */ +static nserror knockout_plot_group_end(const struct redraw_context *ctx) { if (real_plot.group_end == NULL) { - return true; + return NSERROR_OK; } knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_GROUP_END; - if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) - knockout_plot_flush(); + if (++knockout_entry_cur >= KNOCKOUT_ENTRIES) { + return knockout_plot_flush(ctx); + } + return NSERROR_OK; +} + +/* exported functions documented in desktop/knockout.h */ +bool knockout_plot_start(const struct redraw_context *ctx, + struct redraw_context *knk_ctx) +{ + /* check if we're recursing */ + if (nested_depth++ > 0) { + /* we should already have the knockout renderer as default */ + assert(ctx->plot->rectangle == knockout_plotters.rectangle); + *knk_ctx = *ctx; + return true; + } + + /* end any previous sessions */ + if (knockout_entry_cur > 0) + knockout_plot_end(ctx); + + /* get copy of real plotter table */ + real_plot = *(ctx->plot); + + /* set up knockout rendering context */ + *knk_ctx = *ctx; + knk_ctx->plot = &knockout_plotters; return true; } + + +/* exported functions documented in desktop/knockout.h */ +bool knockout_plot_end(const struct redraw_context *ctx) +{ + /* only output when we've finished any nesting */ + if (--nested_depth == 0) { + return knockout_plot_flush(ctx); + } + + assert(nested_depth > 0); + return true; +} + + +/** + * knockout plotter operation table + */ +const struct plotter_table knockout_plotters = { + .rectangle = knockout_plot_rectangle, + .line = knockout_plot_line, + .polygon = knockout_plot_polygon, + .clip = knockout_plot_clip, + .text = knockout_plot_text, + .disc = knockout_plot_disc, + .arc = knockout_plot_arc, + .bitmap = knockout_plot_bitmap, + .group_start = knockout_plot_group_start, + .group_end = knockout_plot_group_end, + .flush = knockout_plot_flush, + .path = knockout_plot_path, + .option_knockout = true, +}; diff --git a/desktop/knockout.h b/desktop/knockout.h index c4f1245fc..f7ff04553 100644 --- a/desktop/knockout.h +++ b/desktop/knockout.h @@ -26,9 +26,21 @@ #include "netsurf/plotters.h" +/** + * Start a knockout plotting session + * + * \param ctx the redraw context with real plotter table + * \param knk_ctx updated to copy of ctx, with plotter table replaced + * \return true on success, false otherwise + */ bool knockout_plot_start(const struct redraw_context *ctx, struct redraw_context *knk_ctx); -bool knockout_plot_end(void); +/** + * End a knockout plotting session + * + * \return true on success, false otherwise + */ +bool knockout_plot_end(const struct redraw_context *ctx); extern const struct plotter_table knockout_plotters; diff --git a/desktop/local_history.c b/desktop/local_history.c new file mode 100644 index 000000000..75da4aff1 --- /dev/null +++ b/desktop/local_history.c @@ -0,0 +1,446 @@ +/* + * Copyright 2017 Vincent Sanders <vince@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * Local history viewer implementation + */ + +#include <stdlib.h> +#include <string.h> + +#include "utils/errors.h" +#include "utils/nsurl.h" +#include "netsurf/types.h" +#include "netsurf/layout.h" +#include "netsurf/core_window.h" +#include "netsurf/plotters.h" + +#include "desktop/gui_internal.h" +#include "desktop/system_colour.h" +#include "desktop/browser_private.h" +#include "desktop/browser_history.h" +#include "desktop/local_history.h" + +#define WIDTH 100 +#define HEIGHT 86 + +/** + * local history viewer context + */ +struct local_history_session { + struct browser_window *bw; + struct core_window_callback_table *cw_t; + void *core_window_handle; +}; + + +/** + * plot style for drawing lines between nodes + */ +static plot_style_t pstyle_line = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_width = plot_style_int_to_fixed(2), +}; + + +/** + * plot style for drawing background + */ +static plot_style_t pstyle_bg = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; + + +/** + * plot style for drawing rectangle round unselected nodes + */ +static plot_style_t pstyle_rect = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_width = plot_style_int_to_fixed(1), +}; + + +/** + * plot style for drawing rectangle round selected nodes + */ +static plot_style_t pstyle_rect_sel = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_width = plot_style_int_to_fixed(3), +}; + + +/** + * plot style for font on unselected nodes + */ +static plot_font_style_t pfstyle_node = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 8 * PLOT_STYLE_SCALE, + .weight = 400, + .flags = FONTF_NONE, +}; + + +/** + * plot style for font on unselected nodes + */ +static plot_font_style_t pfstyle_node_sel = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 8 * PLOT_STYLE_SCALE, + .weight = 900, + .flags = FONTF_NONE, +}; + + +/** + * Recursively redraw a history entry. + * + * \param history history containing the entry + * \param entry entry to render + * \param clip redraw area + * \param x window x offset + * \param y window y offset + * \param ctx current redraw context + */ +static nserror +redraw_entry(struct history *history, + struct history_entry *entry, + struct rect *clip, + int x, int y, + const struct redraw_context *ctx) +{ + size_t char_offset; + int actual_x; + struct history_entry *child; + int tailsize = 5; + + plot_style_t *pstyle; + plot_font_style_t *pfstyle; + struct rect rect; + nserror res; + + /* setup plot styles */ + if (entry == history->current) { + pstyle = &pstyle_rect_sel; + pfstyle = &pfstyle_node_sel; + } else { + pstyle = &pstyle_rect; + pfstyle = &pfstyle_node; + } + + /* Only attempt to plot bitmap if it is present */ + if (entry->page.bitmap != NULL) { + res = ctx->plot->bitmap(ctx, + entry->page.bitmap, + entry->x + x, + entry->y + y, + WIDTH, HEIGHT, + 0xffffff, + 0); + if (res != NSERROR_OK) { + return res; + } + } + + rect.x0 = entry->x - 1 + x; + rect.y0 = entry->y - 1 + y; + rect.x1 = entry->x + x + WIDTH; + rect.y1 = entry->y + y + HEIGHT; + res = ctx->plot->rectangle(ctx, pstyle, &rect); + if (res != NSERROR_OK) { + return res; + } + + res = guit->layout->position(plot_style_font, entry->page.title, + strlen(entry->page.title), WIDTH, + &char_offset, &actual_x); + if (res != NSERROR_OK) { + return res; + } + + res = ctx->plot->text(ctx, + pfstyle, + entry->x + x, + entry->y + HEIGHT + 12 + y, + entry->page.title, + char_offset); + if (res != NSERROR_OK) { + return res; + } + + /* for each child node draw a line and recurse redraw into it */ + for (child = entry->forward; child; child = child->next) { + rect.x0 = entry->x + WIDTH + x; + rect.y0 = entry->y + HEIGHT / 2 + y; + rect.x1 = entry->x + WIDTH + tailsize + x; + rect.y1 = entry->y + HEIGHT / 2 + y; + res = ctx->plot->line(ctx, &pstyle_line, &rect); + if (res != NSERROR_OK) { + return res; + } + + rect.x0 = entry->x + WIDTH + tailsize + x; + rect.y0 = entry->y + HEIGHT / 2 + y; + rect.x1 = child->x - tailsize + x; + rect.y1 = child->y + HEIGHT / 2 + y; + res = ctx->plot->line(ctx, &pstyle_line, &rect); + if (res != NSERROR_OK) { + return res; + } + + rect.x0 = child->x - tailsize + x; + rect.y0 = child->y + HEIGHT / 2 + y; + rect.x1 = child->x + x; + rect.y1 = child->y + HEIGHT / 2 + y; + res = ctx->plot->line(ctx, &pstyle_line, &rect); + if (res != NSERROR_OK) { + return res; + } + + res = redraw_entry(history, child, clip, x, y, ctx); + if (res != NSERROR_OK) { + return res; + } + } + + return NSERROR_OK; +} + + +/** + * Find the history entry at a position. + * + * \param entry entry to search from + * \param x coordinate + * \param y coordinate + * \return an entry if found, 0 if none + */ +static struct history_entry * +find_entry_position(struct history_entry *entry, int x, int y) +{ + struct history_entry *child; + struct history_entry *found; + + if (!entry) { + return NULL; + } + + if ((entry->x <= x) && + (x <= entry->x + WIDTH) && + (entry->y <= y) && + (y <= entry->y + HEIGHT)) { + return entry; + } + + for (child = entry->forward; child; child = child->next) { + found = find_entry_position(child, x, y); + if (found) { + return found; + } + } + + return NULL; +} + + +/* exported interface documented in desktop/local_history.h */ +nserror +local_history_init(struct core_window_callback_table *cw_t, + void *core_window_handle, + struct browser_window *bw, + struct local_history_session **session) +{ + nserror res; + struct local_history_session *nses; + + res = ns_system_colour_char("Window", &pstyle_bg.fill_colour); + if (res != NSERROR_OK) { + return res; + } + pfstyle_node.background = pstyle_bg.fill_colour; + pfstyle_node_sel.background = pstyle_bg.fill_colour; + + res = ns_system_colour_char("GrayText", &pstyle_line.stroke_colour); + if (res != NSERROR_OK) { + return res; + } + pstyle_rect.stroke_colour = pstyle_line.stroke_colour; + pfstyle_node.foreground = pstyle_line.stroke_colour; + + res = ns_system_colour_char("Highlight", &pstyle_rect_sel.stroke_colour); + if (res != NSERROR_OK) { + return res; + } + pfstyle_node_sel.foreground = pstyle_rect_sel.stroke_colour; + + nses = calloc(1, sizeof(struct local_history_session)); + if (nses == NULL) { + return NSERROR_NOMEM; + } + + nses->cw_t = cw_t; + nses->core_window_handle = core_window_handle; + + local_history_set(nses, bw); + + *session = nses; + + return NSERROR_OK; +} + +/* exported interface documented in desktop/local_history.h */ +nserror local_history_fini(struct local_history_session *session) +{ + free(session); + + return NSERROR_OK; +} + + +/* exported interface documented in desktop/local_history.h */ +nserror +local_history_redraw(struct local_history_session *session, + int x, + int y, + struct rect *clip, + const struct redraw_context *ctx) +{ + struct rect r = { + .x0 = clip->x0 + x, + .y0 = clip->y0 + y, + .x1 = clip->x1 + x, + .y1 = clip->y1 + y, + }; + + if (session->bw == NULL) { + return NSERROR_OK; + } + + if (session->bw->history->start == NULL) { + return NSERROR_OK; + } + + ctx->plot->clip(ctx, &r); + ctx->plot->rectangle(ctx, &pstyle_bg, &r); + + return redraw_entry(session->bw->history, + session->bw->history->start, + clip, + x, y, + ctx); +} + +/* exported interface documented in desktop/local_history.h */ +nserror +local_history_mouse_action(struct local_history_session *session, + enum browser_mouse_state mouse, + int x, + int y) +{ + struct history_entry *entry; + bool new_window; + + if (session->bw == NULL) { + return NSERROR_BAD_PARAMETER; + } + + if ((mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2)) == 0) { + return NSERROR_NOT_IMPLEMENTED; + } + + entry = find_entry_position(session->bw->history->start, x, y); + if (entry == NULL) { + return NSERROR_NOT_FOUND; + } + + if (entry == session->bw->history->current) { + return NSERROR_PERMISSION; + } + + if (mouse & BROWSER_MOUSE_PRESS_1) { + new_window = false; + } else if (mouse & BROWSER_MOUSE_PRESS_2) { + new_window = true; + } else { + new_window = false; + } + + browser_window_history_go(session->bw, entry, new_window); + + return NSERROR_OK; +} + +/* exported interface documented in desktop/local_history.h */ +bool +local_history_keypress(struct local_history_session *session, uint32_t key) +{ + return false; +} + +/* exported interface documented in desktop/local_history.h */ +nserror +local_history_set(struct local_history_session *session, + struct browser_window *bw) +{ + session->bw = bw; + if (bw != NULL) { + assert(session->bw->history != NULL); + + session->cw_t->update_size(session->core_window_handle, + session->bw->history->width, + session->bw->history->height); + } + + return NSERROR_OK; +} + + +/* exported interface documented in desktop/local_history.h */ +nserror +local_history_get_size(struct local_history_session *session, + int *width, + int *height) +{ + *width = session->bw->history->width + 20; + *height = session->bw->history->height + 20; + + return NSERROR_OK; +} + + +/* exported interface documented in desktop/local_history.h */ +nserror +local_history_get_url(struct local_history_session *session, + int x, int y, + nsurl **url_out) +{ + struct history_entry *entry; + + if (session->bw == NULL) { + return NSERROR_BAD_PARAMETER; + } + + entry = find_entry_position(session->bw->history->start, x, y); + if (entry == NULL) { + return NSERROR_NOT_FOUND; + } + + *url_out = nsurl_ref(entry->page.url); + + return NSERROR_OK; +} diff --git a/desktop/local_history.h b/desktop/local_history.h new file mode 100644 index 000000000..7f85a633e --- /dev/null +++ b/desktop/local_history.h @@ -0,0 +1,144 @@ +/* + * Copyright 2017 Vincent Sanders <vince@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef NETSURF_DESKTOP_LOCAL_HISTORY_H +#define NETSURF_DESKTOP_LOCAL_HISTORY_H + +#include <stdbool.h> +#include <stdint.h> + +#include "utils/errors.h" +#include "netsurf/mouse.h" + +struct core_window_callback_table; +struct redraw_context; +struct nsurl; +struct rect; +struct local_history_session; +struct browser_window; + +/** + * Initialise the local history. + * + * This iterates through the history object of a browser window and + * creates tree of visited pages with thumbnails which may be selected + * to cause navigation. + * + * This must be called before any other local_history_* function. + * + * \param[in] cw_t Callback table for core_window containing the treeview. + * \param[in] core_window_handle The core_window in which the treeview is shown. + * \param[in] bw browser window to show history of. + * \param[out] session The created local history session context. + * \return NSERROR_OK on success and session set, appropriate error code otherwise + */ +nserror local_history_init(struct core_window_callback_table *cw_t, + void *core_window_handle, + struct browser_window *bw, + struct local_history_session **session); + +/** + * Finalise the local history. + * + * This destroys the local history view and the local history module's + * internal data. After calling this if local history is required again, + * local_history_init must be called to create a new session. + * + * \param session The local history session to finalise. + * \return NSERROR_OK on success and session freed appropriate error otherwise + */ +nserror local_history_fini(struct local_history_session *session); + + +/** + * Redraw the local history. + * + * Causes the local history viewer to issue plot operations to redraw + * the specified area of the viewport. + * + * \param[in] session The local history session context. + * \param[in] x X coordinate to render history at + * \param[in] y Y coordinate to render history at + * \param[in] clip Current clip rectangle (wrt tree origin) + * \param[in] ctx Current redraw context + */ +nserror local_history_redraw(struct local_history_session *session, int x, int y, struct rect *clip, const struct redraw_context *ctx); + + +/** + * Handles all kinds of mouse action + * + * \param[in] session The local history session context. + * \param[in] mouse The current mouse state + * \param[in] x The current mouse X coordinate + * \param[in] y The current mouse Y coordinate + * \return NSERROR_OK if mouse action was processed. + * NSERROR_NOT_FOUND if nothing under the pointer where it was clicked + * NSERROR_NOT_IMPLEMENTED if the action was not processed. + * NSERROR_PERMISSION if the clicked item was the current page + */ +nserror local_history_mouse_action(struct local_history_session *session, enum browser_mouse_state mouse, int x, int y); + + +/** + * Key press handling. + * + * \param[in] session The local history session context. + * \param[in] key The ucs4 character codepoint + * \return true if the keypress is dealt with, false otherwise. + */ +bool local_history_keypress(struct local_history_session *session, uint32_t key); + + +/** + * Change the browser window to draw local history for. + * + * \param[in] session The local history session context. + * \param bw browser window to show history of. + * \return NSERROR_OK or appropriate error code. + */ +nserror local_history_set(struct local_history_session *session, struct browser_window *bw); + + +/** + * get size of local history content area. + * + * \param[in] session The local history session context. + * \param[out] width on sucessful return the width of the localhistory content + * \param[out] height on sucessful return the height of the localhistory content + * \return NSERROR_OK or appropriate error code. + */ +nserror local_history_get_size(struct local_history_session *session, int *width, int *height); + + +/** + * get url of entry at position in local history content area. + * + * \todo the returned url should be a referenced nsurl. + * + * \param[in] session The local history session context. + * \param[in] x The x coordinate to get url of. + * \param[in] y The y coordinate to get url of. + * \param[out] url_out referenced url. + * \return NSERROR_OK and url_out updated or NSERROR_NOT_FOUND if no url at + * location. + */ +nserror local_history_get_url(struct local_history_session *session, int x, int y, struct nsurl **url_out); + + +#endif diff --git a/desktop/mouse.c b/desktop/mouse.c index 6d22fd461..d22910582 100644 --- a/desktop/mouse.c +++ b/desktop/mouse.c @@ -30,5 +30,5 @@ */ void browser_mouse_state_dump(browser_mouse_state mouse) { - LOG("mouse state: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", mouse & BROWSER_MOUSE_PRESS_1 ? "P1" : " ", mouse & BROWSER_MOUSE_PRESS_2 ? "P2" : " ", mouse & BROWSER_MOUSE_CLICK_1 ? "C1" : " ", mouse & BROWSER_MOUSE_CLICK_2 ? "C2" : " ", mouse & BROWSER_MOUSE_DOUBLE_CLICK ? "DC" : " ", mouse & BROWSER_MOUSE_TRIPLE_CLICK ? "TC" : " ", mouse & BROWSER_MOUSE_DRAG_1 ? "D1" : " ", mouse & BROWSER_MOUSE_DRAG_2 ? "D2" : " ", mouse & BROWSER_MOUSE_DRAG_ON ? "DO" : " ", mouse & BROWSER_MOUSE_HOLDING_1 ? "H1" : " ", mouse & BROWSER_MOUSE_HOLDING_2 ? "H2" : " ", mouse & BROWSER_MOUSE_MOD_1 ? "M1" : " ", mouse & BROWSER_MOUSE_MOD_2 ? "M2" : " ", mouse & BROWSER_MOUSE_MOD_3 ? "M3" : " "); + NSLOG(netsurf, INFO, "mouse state: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", mouse & BROWSER_MOUSE_PRESS_1 ? "P1" : " ", mouse & BROWSER_MOUSE_PRESS_2 ? "P2" : " ", mouse & BROWSER_MOUSE_CLICK_1 ? "C1" : " ", mouse & BROWSER_MOUSE_CLICK_2 ? "C2" : " ", mouse & BROWSER_MOUSE_DOUBLE_CLICK ? "DC" : " ", mouse & BROWSER_MOUSE_TRIPLE_CLICK ? "TC" : " ", mouse & BROWSER_MOUSE_DRAG_1 ? "D1" : " ", mouse & BROWSER_MOUSE_DRAG_2 ? "D2" : " ", mouse & BROWSER_MOUSE_DRAG_ON ? "DO" : " ", mouse & BROWSER_MOUSE_HOLDING_1 ? "H1" : " ", mouse & BROWSER_MOUSE_HOLDING_2 ? "H2" : " ", mouse & BROWSER_MOUSE_MOD_1 ? "M1" : " ", mouse & BROWSER_MOUSE_MOD_2 ? "M2" : " ", mouse & BROWSER_MOUSE_MOD_3 ? "M3" : " "); } diff --git a/desktop/netsurf.c b/desktop/netsurf.c index 0f597aa9d..76ff4b19a 100644 --- a/desktop/netsurf.c +++ b/desktop/netsurf.c @@ -41,8 +41,8 @@ #include "image/image.h" #include "image/image_cache.h" #include "javascript/js.h" -#include "render/html.h" -#include "render/textplain.h" +#include "html/html.h" +#include "text/textplain.h" #include "netsurf/browser_window.h" #include "desktop/system_colour.h" @@ -89,7 +89,8 @@ static void netsurf_lwc_iterator(lwc_string *str, void *pw) { - LOG("[%3u] %.*s", str->refcnt, (int)lwc_string_length(str), lwc_string_data(str)); + NSLOG(netsurf, INFO, "[%3u] %.*s", str->refcnt, + (int)lwc_string_length(str), lwc_string_data(str)); } /** @@ -165,8 +166,9 @@ nserror netsurf_init(const char *store_path) if (hlcache_parameters.llcache.limit < MINIMUM_MEMORY_CACHE_SIZE) { hlcache_parameters.llcache.limit = MINIMUM_MEMORY_CACHE_SIZE; - LOG("Setting minimum memory cache size %" PRIsizet, - hlcache_parameters.llcache.limit); + NSLOG(netsurf, INFO, + "Setting minimum memory cache size %"PRIsizet, + hlcache_parameters.llcache.limit); } /* Set up the max attempts made to fetch a timing out resource */ @@ -212,10 +214,6 @@ nserror netsurf_init(const char *store_path) if (ret != NSERROR_OK) return ret; - ret = mimesniff_init(); - if (ret != NSERROR_OK) - return ret; - setlocale(LC_ALL, ""); /* initialise the fetchers */ @@ -247,44 +245,42 @@ void netsurf_exit(void) { hlcache_stop(); - LOG("Closing GUI"); + NSLOG(netsurf, INFO, "Closing GUI"); guit->misc->quit(); - LOG("Finalising JavaScript"); + NSLOG(netsurf, INFO, "Finalising JavaScript"); js_finalise(); - LOG("Finalising Web Search"); + NSLOG(netsurf, INFO, "Finalising Web Search"); search_web_finalise(); - LOG("Finalising high-level cache"); + NSLOG(netsurf, INFO, "Finalising high-level cache"); hlcache_finalise(); - LOG("Closing fetches"); + NSLOG(netsurf, INFO, "Closing fetches"); fetcher_quit(); - mimesniff_fini(); - /* dump any remaining cache entries */ image_cache_fini(); /* Clean up after content handlers */ content_factory_fini(); - LOG("Closing utf8"); + NSLOG(netsurf, INFO, "Closing utf8"); utf8_finalise(); - LOG("Destroying URLdb"); + NSLOG(netsurf, INFO, "Destroying URLdb"); urldb_destroy(); - LOG("Destroying System colours"); + NSLOG(netsurf, INFO, "Destroying System colours"); ns_system_colour_finalize(); - LOG("Destroying Messages"); + NSLOG(netsurf, INFO, "Destroying Messages"); messages_destroy(); corestrings_fini(); - LOG("Remaining lwc strings:"); + NSLOG(netsurf, INFO, "Remaining lwc strings:"); lwc_iterate_strings(netsurf_lwc_iterator, NULL); - LOG("Exited successfully"); + NSLOG(netsurf, INFO, "Exited successfully"); } diff --git a/desktop/options.h b/desktop/options.h index d91898c6e..9b7064efa 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -289,3 +289,8 @@ NSOPTION_COLOUR(sys_colour_ThreeDShadow, 0x00d5d5d5) NSOPTION_COLOUR(sys_colour_Window, 0x00f1f1f1) NSOPTION_COLOUR(sys_colour_WindowFrame, 0x004e4e4e) NSOPTION_COLOUR(sys_colour_WindowText, 0x00000000) + +/** Filter for non-verbose logging */ +NSOPTION_STRING(log_filter, "level:WARNING") +/** Filter for verbose logging */ +NSOPTION_STRING(verbose_filter, "level:VERBOSE") diff --git a/desktop/plot_style.c b/desktop/plot_style.c index 1f0ac39cf..05954144a 100644 --- a/desktop/plot_style.c +++ b/desktop/plot_style.c @@ -47,7 +47,7 @@ plot_style_t *plot_style_fill_red = &plot_style_fill_red_static; static const plot_style_t plot_style_content_edge_static = { .stroke_type = PLOT_OP_TYPE_SOLID, .stroke_colour = 0x00ff0000, - .stroke_width = 1, + .stroke_width = plot_style_int_to_fixed(1), }; plot_style_t const * const plot_style_content_edge = &plot_style_content_edge_static; @@ -55,7 +55,7 @@ plot_style_t const * const plot_style_content_edge = static const plot_style_t plot_style_padding_edge_static = { .stroke_type = PLOT_OP_TYPE_SOLID, .stroke_colour = 0x000000ff, - .stroke_width = 1, + .stroke_width = plot_style_int_to_fixed(1), }; plot_style_t const * const plot_style_padding_edge = &plot_style_padding_edge_static; @@ -63,7 +63,7 @@ plot_style_t const * const plot_style_padding_edge = static const plot_style_t plot_style_margin_edge_static = { .stroke_type = PLOT_OP_TYPE_SOLID, .stroke_colour = 0x0000ffff, - .stroke_width = 1, + .stroke_width = plot_style_int_to_fixed(1), }; plot_style_t const * const plot_style_margin_edge = &plot_style_margin_edge_static; @@ -74,14 +74,14 @@ static const plot_style_t plot_style_broken_object_static = { .fill_colour = 0x008888ff, .stroke_type = PLOT_OP_TYPE_SOLID, .stroke_colour = 0x000000ff, - .stroke_width = 1, + .stroke_width = plot_style_int_to_fixed(1), }; plot_style_t const * const plot_style_broken_object = &plot_style_broken_object_static; static const plot_font_style_t plot_fstyle_broken_object_static = { .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = 16 * FONT_SIZE_SCALE, + .size = 16 * PLOT_STYLE_SCALE, .weight = 400, .flags = FONTF_NONE, .background = 0x8888ff, @@ -134,7 +134,7 @@ plot_style_t *plot_style_fill_wblobc = &plot_style_fill_wblobc_static; static plot_style_t plot_style_stroke_wblobc_static = { .stroke_type = PLOT_OP_TYPE_SOLID, .stroke_colour = WIDGET_BLOBC, - .stroke_width = 2, + .stroke_width = plot_style_int_to_fixed(2), }; plot_style_t *plot_style_stroke_wblobc = &plot_style_stroke_wblobc_static; @@ -156,7 +156,7 @@ plot_style_t *plot_style_stroke_lightwbasec = &plot_style_stroke_lightwbasec_sta /* Generic font style */ static const plot_font_style_t plot_style_font_static = { .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = 8 * FONT_SIZE_SCALE, + .size = 8 * PLOT_STYLE_SCALE, .weight = 400, .flags = FONTF_NONE, .background = 0xffffff, diff --git a/desktop/print.c b/desktop/print.c index e7c8cf2fa..de579dcf2 100644 --- a/desktop/print.c +++ b/desktop/print.c @@ -34,7 +34,7 @@ #include "netsurf/plotters.h" #include "content/hlcache.h" #include "css/utils.h" -#include "render/box.h" +#include "html/box.h" #include "desktop/print.h" #include "desktop/printer.h" @@ -126,8 +126,10 @@ print_apply_settings(hlcache_handle *content, struct print_settings *settings) content_reformat(content, false, page_content_width, 0); - LOG("New layout applied.New height = %d ; New width = %d ", - content_get_height(content), content_get_width(content)); + NSLOG(netsurf, INFO, + "New layout applied.New height = %d ; New width = %d ", + content_get_height(content), + content_get_width(content)); return true; } @@ -179,7 +181,8 @@ bool print_draw_next_page(const struct printer *printer, struct redraw_context ctx = { .interactive = false, .background_images = !nsoption_bool(remove_backgrounds), - .plot = printer->plotter + .plot = printer->plotter, + .priv = settings->priv }; html_redraw_printing_top_cropped = INT_MAX; @@ -254,6 +257,11 @@ struct print_settings *print_make_settings(print_configuration configuration, struct print_settings *settings; css_fixed length = 0; css_unit unit = CSS_UNIT_MM; + nscss_len_ctx len_ctx = { + .vw = DEFAULT_PAGE_WIDTH, + .vh = DEFAULT_PAGE_HEIGHT, + .root_style = NULL, + }; switch (configuration){ case PRINT_DEFAULT: @@ -269,17 +277,17 @@ struct print_settings *print_make_settings(print_configuration configuration, settings->scale = DEFAULT_EXPORT_SCALE; length = INTTOFIX(DEFAULT_MARGIN_LEFT_MM); - settings->margins[MARGINLEFT] = - nscss_len2px(length, unit, NULL); + settings->margins[MARGINLEFT] = nscss_len2px( + &len_ctx, length, unit, NULL); length = INTTOFIX(DEFAULT_MARGIN_RIGHT_MM); - settings->margins[MARGINRIGHT] = - nscss_len2px(length, unit, NULL); + settings->margins[MARGINRIGHT] = nscss_len2px( + &len_ctx, length, unit, NULL); length = INTTOFIX(DEFAULT_MARGIN_TOP_MM); - settings->margins[MARGINTOP] = - nscss_len2px(length, unit, NULL); + settings->margins[MARGINTOP] = nscss_len2px( + &len_ctx, length, unit, NULL); length = INTTOFIX(DEFAULT_MARGIN_BOTTOM_MM); - settings->margins[MARGINBOTTOM] = - nscss_len2px(length, unit, NULL); + settings->margins[MARGINBOTTOM] = nscss_len2px( + &len_ctx, length, unit, NULL); break; /* use settings from the Export options tab */ case PRINT_OPTIONS: @@ -295,17 +303,17 @@ struct print_settings *print_make_settings(print_configuration configuration, settings->scale = (float)nsoption_int(export_scale) / 100; length = INTTOFIX(nsoption_int(margin_left)); - settings->margins[MARGINLEFT] = - nscss_len2px(length, unit, NULL); + settings->margins[MARGINLEFT] = nscss_len2px( + &len_ctx, length, unit, NULL); length = INTTOFIX(nsoption_int(margin_right)); - settings->margins[MARGINRIGHT] = - nscss_len2px(length, unit, NULL); + settings->margins[MARGINRIGHT] = nscss_len2px( + &len_ctx, length, unit, NULL); length = INTTOFIX(nsoption_int(margin_top)); - settings->margins[MARGINTOP] = - nscss_len2px(length, unit, NULL); + settings->margins[MARGINTOP] = nscss_len2px( + &len_ctx, length, unit, NULL); length = INTTOFIX(nsoption_int(margin_bottom)); - settings->margins[MARGINBOTTOM] = - nscss_len2px(length, unit, NULL); + settings->margins[MARGINBOTTOM] = nscss_len2px( + &len_ctx, length, unit, NULL); break; default: return NULL; diff --git a/desktop/print.h b/desktop/print.h index fb1fd63ac..4d178aa8a 100644 --- a/desktop/print.h +++ b/desktop/print.h @@ -61,6 +61,9 @@ struct print_settings{ /*the functions used to measure fonts*/ const struct gui_layout_table *font_func; + + /* Private data for the plotter context */ + void *priv; }; diff --git a/desktop/save_complete.c b/desktop/save_complete.c index f8f005743..ef794d4d7 100644 --- a/desktop/save_complete.c +++ b/desktop/save_complete.c @@ -43,8 +43,9 @@ #include "netsurf/content.h" #include "content/hlcache.h" #include "css/css.h" -#include "render/box.h" -#include "render/html.h" +#include "html/box.h" +#include "html/html_save.h" +#include "html/html.h" #include "netsurf/misc.h" #include "desktop/gui_internal.h" @@ -168,7 +169,7 @@ static bool save_complete_save_buffer(save_complete_ctx *ctx, fp = fopen(fname, "wb"); if (fp == NULL) { free(fname); - LOG("fopen(): errno = %i", errno); + NSLOG(netsurf, INFO, "fopen(): errno = %i", errno); guit->misc->warning("SaveError", strerror(errno)); return false; } @@ -195,10 +196,12 @@ static bool save_complete_save_buffer(save_complete_ctx *ctx, * \param osize updated with the size of the result. * \return converted source, or NULL on out of memory. */ - -static char *save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx, - const char *source, unsigned long size, const nsurl *base, - unsigned long *osize) +static char * +save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx, + const char *source, + unsigned long size, + const nsurl *base, + unsigned long *osize) { char *rewritten; unsigned long offset = 0; @@ -207,8 +210,9 @@ static char *save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx, /* count number occurrences of @import to (over)estimate result size */ /* can't use strstr because source is not 0-terminated string */ - for (offset = 0; SLEN("@import") < size && - offset <= size - SLEN("@import"); offset++) { + for (offset = 0; + (SLEN("@import") < size) && (offset <= (size - SLEN("@import"))); + offset++) { if (source[offset] == '@' && ascii_to_lower(source[offset + 1]) == 'i' && ascii_to_lower(source[offset + 2]) == 'm' && @@ -637,9 +641,9 @@ static bool save_complete_handle_attr_value(save_complete_ctx *ctx, * * Attribute: Elements: * - * 1) data <object> - * 2) href <a> <area> <link> - * 3) src <script> <input> <frame> <iframe> <img> + * 1) data object + * 2) href a, area, link + * 3) src script, input, frame, iframe, img * 4) background any (except those above) */ /* 1 */ @@ -1044,7 +1048,7 @@ static bool save_complete_node_handler(dom_node *node, } else if (type == DOM_DOCUMENT_NODE) { /* Do nothing */ } else { - LOG("Unhandled node type: %d", type); + NSLOG(netsurf, INFO, "Unhandled node type: %d", type); } return true; @@ -1075,7 +1079,7 @@ static bool save_complete_save_html_document(save_complete_ctx *ctx, fp = fopen(fname, "wb"); if (fp == NULL) { free(fname); - LOG("fopen(): errno = %i", errno); + NSLOG(netsurf, INFO, "fopen(): errno = %i", errno); guit->misc->warning("SaveError", strerror(errno)); return false; } @@ -1154,7 +1158,7 @@ static bool save_complete_inventory(save_complete_ctx *ctx) fp = fopen(fname, "w"); free(fname); if (fp == NULL) { - LOG("fopen(): errno = %i", errno); + NSLOG(netsurf, INFO, "fopen(): errno = %i", errno); guit->misc->warning("SaveError", strerror(errno)); return false; } @@ -1182,7 +1186,8 @@ static nserror regcomp_wrapper(regex_t *preg, const char *regex, int cflags) if (r) { char errbuf[200]; regerror(r, preg, errbuf, sizeof errbuf); - LOG("Failed to compile regexp '%s': %s\n", regex, errbuf); + NSLOG(netsurf, INFO, "Failed to compile regexp '%s': %s\n", + regex, errbuf); return NSERROR_INIT_FAILED; } return NSERROR_OK; diff --git a/desktop/save_pdf.c b/desktop/save_pdf.c index d303eca7c..889190089 100644 --- a/desktop/save_pdf.c +++ b/desktop/save_pdf.c @@ -171,7 +171,8 @@ bool pdf_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *psty { DashPattern_e dash; #ifdef PDF_DEBUG - LOG("%d %d %d %d %f %X", x0, y0, x1, y1, page_height - y0, pstyle->fill_colour); + NSLOG(netsurf, INFO, "%d %d %d %d %f %X", x0, y0, x1, y1, + page_height - y0, pstyle->fill_colour); #endif if (pstyle->fill_type != PLOT_OP_TYPE_NONE) { @@ -212,10 +213,10 @@ bool pdf_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *psty } apply_clip_and_mode(false, - NS_TRANSPARENT, - pstyle->stroke_colour, - pstyle->stroke_width, - dash); + NS_TRANSPARENT, + pstyle->stroke_colour, + plot_style_int_to_fixed(pstyle->stroke_width), + dash); HPDF_Page_Rectangle(pdf_page, x0, page_height - y0, x1 - x0, -(y1 - y0)); HPDF_Page_Stroke(pdf_page); @@ -244,10 +245,10 @@ bool pdf_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle) } apply_clip_and_mode(false, - NS_TRANSPARENT, - pstyle->stroke_colour, - pstyle->stroke_width, - dash); + NS_TRANSPARENT, + pstyle->stroke_colour, + plot_style_int_to_fixed(pstyle->stroke_width), + dash); HPDF_Page_MoveTo(pdf_page, x0, page_height - y0); HPDF_Page_LineTo(pdf_page, x1, page_height - y1); @@ -262,7 +263,7 @@ bool pdf_plot_polygon(const int *p, unsigned int n, const plot_style_t *style) #ifdef PDF_DEBUG int pmaxx = p[0], pmaxy = p[1]; int pminx = p[0], pminy = p[1]; - LOG("."); + NSLOG(netsurf, INFO, "."); #endif if (n == 0) return true; @@ -281,7 +282,8 @@ bool pdf_plot_polygon(const int *p, unsigned int n, const plot_style_t *style) } #ifdef PDF_DEBUG - LOG("%d %d %d %d %f", pminx, pminy, pmaxx, pmaxy, page_height - pminy); + NSLOG(netsurf, INFO, "%d %d %d %d %f", pminx, pminy, pmaxx, pmaxy, + page_height - pminy); #endif HPDF_Page_Fill(pdf_page); @@ -294,7 +296,8 @@ bool pdf_plot_polygon(const int *p, unsigned int n, const plot_style_t *style) bool pdf_plot_clip(const struct rect *clip) { #ifdef PDF_DEBUG - LOG("%d %d %d %d", clip->x0, clip->y0, clip->x1, clip->y1); + NSLOG(netsurf, INFO, "%d %d %d %d", clip->x0, clip->y0, clip->x1, + clip->y1); #endif /*Normalize cllipping area - to prevent overflows. @@ -314,7 +317,7 @@ bool pdf_plot_text(int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle) { #ifdef PDF_DEBUG - LOG(". %d %d %.*s", x, y, (int)length, text); + NSLOG(netsurf, INFO, ". %d %d %.*s", x, y, (int)length, text); #endif char *word; HPDF_Font pdf_font; @@ -347,7 +350,7 @@ bool pdf_plot_text(int x, int y, const char *text, size_t length, bool pdf_plot_disc(int x, int y, int radius, const plot_style_t *style) { #ifdef PDF_DEBUG - LOG("."); + NSLOG(netsurf, INFO, "."); #endif if (style->fill_type != PLOT_OP_TYPE_NONE) { apply_clip_and_mode(false, @@ -378,7 +381,8 @@ bool pdf_plot_disc(int x, int y, int radius, const plot_style_t *style) bool pdf_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style) { #ifdef PDF_DEBUG - LOG("%d %d %d %d %d %X", x, y, radius, angle1, angle2, style->stroke_colour); + NSLOG(netsurf, INFO, "%d %d %d %d %d %X", x, y, radius, angle1, + angle2, style->stroke_colour); #endif /* FIXME: line width 1 is ok ? */ @@ -406,7 +410,8 @@ bool pdf_plot_bitmap_tile(int x, int y, int width, int height, HPDF_REAL max_width, max_height; #ifdef PDF_DEBUG - LOG("%d %d %d %d %p 0x%x", x, y, width, height, bitmap, bg); + NSLOG(netsurf, INFO, "%d %d %d %d %p 0x%x", x, y, width, height, + bitmap, bg); #endif if (width == 0 || height == 0) return true; @@ -483,7 +488,8 @@ HPDF_Image pdf_extract_image(struct bitmap *bitmap) rgb_buffer = (unsigned char *)malloc(3 * img_width * img_height); alpha_buffer = (unsigned char *)malloc(img_width * img_height); if (rgb_buffer == NULL || alpha_buffer == NULL) { - LOG("Not enough memory to create RGB buffer"); + NSLOG(netsurf, INFO, + "Not enough memory to create RGB buffer"); free(rgb_buffer); free(alpha_buffer); return NULL; @@ -607,7 +613,7 @@ bool pdf_plot_path(const float *p, unsigned int n, colour fill, float width, bool empty_path; #ifdef PDF_DEBUG - LOG("."); + NSLOG(netsurf, INFO, "."); #endif if (n == 0) @@ -649,7 +655,7 @@ bool pdf_plot_path(const float *p, unsigned int n, colour fill, float width, i += 7; empty_path = false; } else { - LOG("bad path command %f", p[i]); + NSLOG(netsurf, INFO, "bad path command %f", p[i]); return false; } } @@ -685,7 +691,7 @@ bool pdf_begin(struct print_settings *print_settings) HPDF_Free(pdf_doc); pdf_doc = HPDF_New(error_handler, NULL); if (!pdf_doc) { - LOG("Error creating pdf_doc"); + NSLOG(netsurf, INFO, "Error creating pdf_doc"); return false; } @@ -708,7 +714,7 @@ bool pdf_begin(struct print_settings *print_settings) pdf_page = NULL; #ifdef PDF_DEBUG - LOG("pdf_begin finishes"); + NSLOG(netsurf, INFO, "pdf_begin finishes"); #endif return true; } @@ -717,7 +723,7 @@ bool pdf_begin(struct print_settings *print_settings) bool pdf_next_page(void) { #ifdef PDF_DEBUG - LOG("pdf_next_page begins"); + NSLOG(netsurf, INFO, "pdf_next_page begins"); #endif clip_update_needed = false; if (pdf_page != NULL) { @@ -745,7 +751,7 @@ bool pdf_next_page(void) pdfw_gs_save(pdf_page); #ifdef PDF_DEBUG - LOG("%f %f", page_width, page_height); + NSLOG(netsurf, INFO, "%f %f", page_width, page_height); #endif return true; @@ -755,7 +761,7 @@ bool pdf_next_page(void) void pdf_end(void) { #ifdef PDF_DEBUG - LOG("pdf_end begins"); + NSLOG(netsurf, INFO, "pdf_end begins"); #endif clip_update_needed = false; if (pdf_page != NULL) { @@ -780,7 +786,7 @@ void pdf_end(void) else save_pdf(settings->output); #ifdef PDF_DEBUG - LOG("pdf_end finishes"); + NSLOG(netsurf, INFO, "pdf_end finishes"); #endif } @@ -819,7 +825,8 @@ nserror save_pdf(const char *path) static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void *user_data) { - LOG("ERROR:\n\terror_no=%x\n\tdetail_no=%d\n", (HPDF_UINT)error_no, (HPDF_UINT)detail_no); + NSLOG(netsurf, INFO, "ERROR:\n\terror_no=%x\n\tdetail_no=%d\n", + (HPDF_UINT)error_no, (HPDF_UINT)detail_no); #ifdef PDF_DEBUG exit(1); #endif diff --git a/desktop/save_text.c b/desktop/save_text.c index e63c96eb9..a86f173b0 100644 --- a/desktop/save_text.c +++ b/desktop/save_text.c @@ -32,8 +32,8 @@ #include "utils/utf8.h" #include "utils/utils.h" #include "netsurf/content.h" -#include "render/box.h" -#include "render/html.h" +#include "html/box.h" +#include "html/html_save.h" #include "netsurf/utf8.h" #include "desktop/gui_internal.h" @@ -75,7 +75,8 @@ void save_as_text(struct hlcache_handle *c, char *path) free(save.block); if (ret != NSERROR_OK) { - LOG("failed to convert to local encoding, return %d", ret); + NSLOG(netsurf, INFO, + "failed to convert to local encoding, return %d", ret); return; } @@ -84,12 +85,13 @@ void save_as_text(struct hlcache_handle *c, char *path) int res = fputs(result, out); if (res < 0) { - LOG("Warning: write failed"); + NSLOG(netsurf, INFO, "Warning: write failed"); } res = fputs("\n", out); if (res < 0) { - LOG("Warning: failed writing trailing newline"); + NSLOG(netsurf, INFO, + "Warning: failed writing trailing newline"); } fclose(out); diff --git a/desktop/scrollbar.c b/desktop/scrollbar.c index 9a4d70fe4..af5536ba4 100644 --- a/desktop/scrollbar.c +++ b/desktop/scrollbar.c @@ -18,8 +18,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * Scrollbar widget (implementation). +/** + * \file + * implementation of scrollbar widget. */ #include <stdbool.h> @@ -36,44 +37,65 @@ #include "desktop/system_colour.h" #include "desktop/scrollbar.h" +/** + * Scrollbar context + */ struct scrollbar { - bool horizontal; /* Horizontal scrollbar if true, else vertical - */ - int length; /* Length of the scrollbar widget */ - - int full_size; /* Length of the full scrollable area */ - int visible_size; /* Length visible part of the scrollable area */ - - int offset; /* Current scroll offset to visible area */ - - int bar_pos; /* Position of the scrollbar */ - int bar_len; /* Length of the scrollbar */ - - scrollbar_client_callback client_callback; /* Callback receiving - * scrollbar 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_pos; /* Scrollbar offset or bar_pos at drag start */ - bool drag_content; /* Flag indicating that the drag corresponds to - * a dragged content area, rather than a dragged - * scrollbar. */ + /** Horizontal scrollbar if true, else vertical */ + bool horizontal; + /** Length of the scrollbar widget */ + int length; + + /** Length of the full scrollable area */ + int full_size; + /** Length visible part of the scrollable area */ + int visible_size; + + /** Current scroll offset to visible area */ + int offset; + + /** Position of the scrollbar */ + int bar_pos; + /** Length of the scrollbar */ + int bar_len; + + /** Callback receiving scrollbar events */ + scrollbar_client_callback client_callback; + /** User data passed to the callback */ + void *client_data; + + /** Flag indicating drag at progess */ + bool dragging; + /** Coordinate value at drag start */ + int drag_start_coord; + /** Scrollbar offset or bar_pos at drag start */ + int drag_start_pos; + /** Flag indicating that the drag corresponds to a dragged + * content area, rather than a dragged scrollbar. + */ + bool drag_content; - struct scrollbar *pair; /* Parpendicular scrollbar, or NULL */ - bool pair_drag; /* Flag indicating that the current drag affects - the perpendicular scrollbar too */ + /** Parpendicular scrollbar, or NULL */ + struct scrollbar *pair; + /** Flag indicating that the current drag affects the + * perpendicular scrollbar too + */ + bool pair_drag; }; /* - * Exported function. Documented in desktop/scrollbar.h + * Exported interface. Documented in desktop/scrollbar.h */ -nserror scrollbar_create(bool horizontal, int length, int full_size, - int visible_size, void *client_data, - scrollbar_client_callback client_callback, - struct scrollbar **s) +nserror +scrollbar_create(bool horizontal, + int length, + int full_size, + int visible_size, + void *client_data, + scrollbar_client_callback client_callback, + struct scrollbar **s) { struct scrollbar *scrollbar; int well_length; @@ -95,7 +117,7 @@ nserror scrollbar_create(bool horizontal, int length, int full_size, well_length = length - 2 * SCROLLBAR_WIDTH; scrollbar->bar_len = (full_size == 0) ? 0 : - ((well_length * visible_size) / full_size); + ((well_length * visible_size) / full_size); scrollbar->client_callback = client_callback; scrollbar->client_data = client_data; @@ -110,49 +132,49 @@ nserror scrollbar_create(bool horizontal, int length, int full_size, /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ void scrollbar_destroy(struct scrollbar *s) { - if (s->pair != NULL) + if (s->pair != NULL) { s->pair->pair = NULL; + } free(s); } /** - * Draw an outline rectangle common to a several scrollbar elements. + * Draw an outline rectangle common to several scrollbar 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 ctx current redraw context + * \param ctx current redraw context + * \param area the area of the scrollbar + * \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 + * \return NSERROR_OK on success else error code */ - -static inline bool scrollbar_redraw_scrollbar_rectangle(int x0, int y0, - int x1, int y1, colour c, bool inset, - const struct redraw_context *ctx) +static inline nserror +scrollbar_rectangle(const struct redraw_context *ctx, + struct rect *area, + colour c, + bool inset) { - const struct plotter_table *plot = ctx->plot; + struct rect line; + nserror res; static plot_style_t c0 = { .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 1, + .stroke_width = plot_style_int_to_fixed(1), }; static plot_style_t c1 = { .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 1, + .stroke_width = plot_style_int_to_fixed(1), }; static plot_style_t c2 = { .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 1, + .stroke_width = plot_style_int_to_fixed(1), }; if (inset) { @@ -165,225 +187,338 @@ static inline bool scrollbar_redraw_scrollbar_rectangle(int x0, int y0, c2.stroke_colour = blend_colour(c0.stroke_colour, c1.stroke_colour); /* Plot the outline */ - 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; + line.x0 = area->x0; line.y0 = area->y0; + line.x1 = area->x1; line.y1 = area->y0; + res = ctx->plot->line(ctx, &c0, &line); + if (res != NSERROR_OK) { + return res; + } + + line.x0 = area->x1; line.y0 = area->y0; + line.x1 = area->x1; line.y1 = area->y1 + 1; + res = ctx->plot->line(ctx, &c1, &line); + if (res != NSERROR_OK) { + return res; + } + + line.x0 = area->x1; line.y0 = area->y0; + line.x1 = area->x1; line.y1 = area->y0 + 1; + res = ctx->plot->line(ctx, &c2, &line); + if (res != NSERROR_OK) { + return res; + } + + line.x0 = area->x1; line.y0 = area->y1; + line.x1 = area->x0; line.y1 = area->y1; + res = ctx->plot->line(ctx, &c1, &line); + if (res != NSERROR_OK) { + return res; + } + + line.x0 = area->x0; line.y0 = area->y1; + line.x1 = area->x0; line.y1 = area->y0; + res = ctx->plot->line(ctx, &c0, &line); + if (res != NSERROR_OK) { + return res; + } + + line.x0 = area->x0; line.y0 = area->y1; + line.x1 = area->x0; line.y1 = area->y1 + 1; + res = ctx->plot->line(ctx, &c2, &line); + + return res; } /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ -bool scrollbar_redraw(struct scrollbar *s, int x, int y, - const struct rect *clip, float scale, - const struct redraw_context *ctx) +nserror +scrollbar_redraw(struct scrollbar *s, + int x, int y, + const struct rect *clip, + float scale, + const struct redraw_context *ctx) { - const struct plotter_table *plot = ctx->plot; int w = SCROLLBAR_WIDTH; int bar_pos, bar_c0, bar_c1; int v[6]; /* array of triangle vertices */ - int x0, y0, x1, y1; - - colour bg_fill_colour = ns_system_colour_char("Scrollbar"); - colour fg_fill_colour = ns_system_colour_char("ButtonFace"); - colour arrow_fill_colour = ns_system_colour_char("ButtonText"); + struct rect area; + struct rect rect; + nserror res; plot_style_t bg_fill_style = { .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = bg_fill_colour }; plot_style_t fg_fill_style = { .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = fg_fill_colour }; plot_style_t arrow_fill_style = { .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = arrow_fill_colour }; - x0 = x; - y0 = y; - x1 = x + (s->horizontal ? s->length : SCROLLBAR_WIDTH) - 1; - y1 = y + (s->horizontal ? SCROLLBAR_WIDTH : s->length) - 1; + res = ns_system_colour_char("Scrollbar", &bg_fill_style.fill_colour); + if (res != NSERROR_OK) { + return res; + } + + res = ns_system_colour_char("ButtonFace", &fg_fill_style.fill_colour); + if (res != NSERROR_OK) { + return res; + } + + res = ns_system_colour_char("ButtonText", &arrow_fill_style.fill_colour); + if (res != NSERROR_OK) { + return res; + } + + area.x0 = x; + area.y0 = y; + area.x1 = x + (s->horizontal ? s->length : SCROLLBAR_WIDTH) - 1; + area.y1 = y + (s->horizontal ? SCROLLBAR_WIDTH : s->length) - 1; bar_pos = s->bar_pos; - bar_c1 = (s->horizontal ? x0 : y0) + SCROLLBAR_WIDTH + - s->bar_pos + s->bar_len - 1; + bar_c1 = (s->horizontal ? area.x0 : area.y0) + SCROLLBAR_WIDTH + + s->bar_pos + s->bar_len - 1; if (scale != 1.0) { w *= scale; - x0 *= scale; - y0 *= scale; - x1 *= scale; - y1 *= scale; + area.x0 *= scale; + area.y0 *= scale; + area.x1 *= scale; + area.y1 *= scale; bar_pos *= scale; bar_c1 *= scale; } - bar_c0 = (s->horizontal ? x0 : y0) + w + bar_pos; + bar_c0 = (s->horizontal ? area.x0 : area.y0) + w + bar_pos; - if (x1 < clip->x0 || y1 < clip->y0 || clip->x1 < x0 || clip->y1 < y0) - /* scrollbar is outside the clipping rectangle, nothing to - * render */ - return true; + /* if scrollbar is outside the clipping rectangle, nothing to render */ + if ((area.x1 < clip->x0) || + (area.y1 < clip->y0) || + (clip->x1 < area.x0) || + (clip->y1 < area.y0)) { + return NSERROR_OK; + } - if (s->horizontal) { /* scrollbar is horizontal */ - + /* scrollbar outline */ - if (!scrollbar_redraw_scrollbar_rectangle(x0, y0, x1, y1, - bg_fill_colour, true, ctx)) - return false; + res = scrollbar_rectangle(ctx, &area, + bg_fill_style.fill_colour, true); + if (res != NSERROR_OK) { + return res; + } + /* left arrow icon border */ - if (!scrollbar_redraw_scrollbar_rectangle(x0 + 1, - y0 + 1, - x0 + w - 2, - y1 - 1, - fg_fill_colour, false, ctx)) - return false; + rect.x0 = area.x0 + 1; + rect.y0 = area.y0 + 1; + rect.x1 = area.x0 + w - 2; + rect.y1 = area.y1 - 1; + res = scrollbar_rectangle(ctx, &rect, + fg_fill_style.fill_colour, false); + if (res != NSERROR_OK) { + return res; + } + /* left arrow icon background */ - if (!plot->rectangle(x0 + 2, - y0 + 2, - x0 + w - 2, - y1 - 1, - &fg_fill_style)) - return false; + rect.x0 = area.x0 + 2; + rect.y0 = area.y0 + 2; + rect.x1 = area.x0 + w - 2; + rect.y1 = area.y1 - 1; + res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect); + if (res != NSERROR_OK) { + return res; + } + /* 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, &arrow_fill_style)) - return false; + v[0] = area.x0 + w / 4; + v[1] = area.y0 + w / 2; + v[2] = area.x0 + w * 3 / 4; + v[3] = area.y0 + w / 4; + v[4] = area.x0 + w * 3 / 4; + v[5] = area.y0 + w * 3 / 4; + res = ctx->plot->polygon(ctx, &arrow_fill_style, v, 3); + if (res != NSERROR_OK) { + return res; + } + /* scrollbar well background */ - if (!plot->rectangle(x0 + w - 1, - y0 + 1, - x1 - w + 2, - y1, - &bg_fill_style)) - return false; + rect.x0 = area.x0 + w - 1; + rect.y0 = area.y0 + 1; + rect.x1 = area.x1 - w + 2; + rect.y1 = area.y1; + res = ctx->plot->rectangle(ctx, &bg_fill_style, &rect); + if (res != NSERROR_OK) { + return res; + } + /* scrollbar position indicator bar */ - if (!scrollbar_redraw_scrollbar_rectangle(bar_c0, - y0 + 1, - bar_c1, - y1 - 1, - fg_fill_colour, false, ctx)) - return false; - if (!plot->rectangle(bar_c0 + 1, - y0 + 2, - bar_c1, - y1 - 1, - &fg_fill_style)) - return false; + rect.x0 = bar_c0; + rect.y0 = area.y0 + 1; + rect.x1 = bar_c1; + rect.y1 = area.y1 - 1; + res = scrollbar_rectangle(ctx, &rect, + fg_fill_style.fill_colour, false); + if (res != NSERROR_OK) { + return res; + } + + rect.x0 = bar_c0 + 1; + rect.y0 = area.y0 + 2; + rect.x1 = bar_c1; + rect.y1 = area.y1 - 1; + res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect); + if (res != NSERROR_OK) { + return res; + } + /* right arrow icon border */ - if (!scrollbar_redraw_scrollbar_rectangle(x1 - w + 2, - y0 + 1, - x1 - 1, - y1 - 1, - fg_fill_colour, false, ctx)) - return false; + rect.x0 = area.x1 - w + 2; + rect.y0 = area.y0 + 1; + rect.x1 = area.x1 - 1; + rect.y1 = area.y1 - 1; + res = scrollbar_rectangle(ctx, &rect, + fg_fill_style.fill_colour, false); + if (res != NSERROR_OK) { + return res; + } + /* right arrow icon background */ - if (!plot->rectangle(x1 - w + 3, - y0 + 2, - x1 - 1, - y1 - 1, - &fg_fill_style)) - return false; + rect.x0 = area.x1 - w + 3; + rect.y0 = area.y0 + 2; + rect.x1 = area.x1 - 1; + rect.y1 = area.y1 - 1; + res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect); + if (res != NSERROR_OK) { + return res; + } + /* 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, &arrow_fill_style)) - return false; + v[0] = rect.x1 - w / 4 + 1; + v[1] = rect.y0 + w / 2; + v[2] = rect.x1 - w * 3 / 4 + 1; + v[3] = rect.y0 + w / 4; + v[4] = rect.x1 - w * 3 / 4 + 1; + v[5] = rect.y0 + w * 3 / 4; + res = ctx->plot->polygon(ctx, &arrow_fill_style, v, 3); + if (res != NSERROR_OK) { + return res; + } } else { /* scrollbar is vertical */ - + /* outline */ - if (!scrollbar_redraw_scrollbar_rectangle(x0, y0, x1, y1, - bg_fill_colour, true, ctx)) - return false; - /* top arrow background */ - if (!scrollbar_redraw_scrollbar_rectangle(x0 + 1, - y0 + 1, - x1 - 1, - y0 + w - 2, - fg_fill_colour, false, ctx)) - return false; - if (!plot->rectangle(x0 + 2, - y0 + 2, - x1 - 1, - y0 + w - 2, - &fg_fill_style)) - return false; + res = scrollbar_rectangle(ctx, &area, + bg_fill_style.fill_colour, true); + if (res != NSERROR_OK) { + return res; + } + + /* top arrow border */ + rect.x0 = area.x0 + 1; + rect.y0 = area.y0 + 1; + rect.x1 = area.x1 - 1; + rect.y1 = area.y0 + w - 2; + res = scrollbar_rectangle(ctx, &rect, + fg_fill_style.fill_colour, false); + if (res != NSERROR_OK) { + return res; + } + + /* top arrow background */ + rect.x0 = area.x0 + 2; + rect.y0 = area.y0 + 2; + rect.x1 = area.x1 - 1; + rect.y1 = area.y0 + w - 2; + res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect); + if (res != NSERROR_OK) { + return res; + } + /* 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, &arrow_fill_style)) - return false; + v[0] = area.x0 + w / 2; + v[1] = area.y0 + w / 4; + v[2] = area.x0 + w / 4; + v[3] = area.y0 + w * 3 / 4; + v[4] = area.x0 + w * 3 / 4; + v[5] = area.y0 + w * 3 / 4; + res = ctx->plot->polygon(ctx, &arrow_fill_style, v, 3); + if (res != NSERROR_OK) { + return res; + } + /* scrollbar well background */ - if (!plot->rectangle(x0 + 1, - y0 + w - 1, - x1, - y1 - w + 2, - &bg_fill_style)) - return false; + rect.x0 = area.x0 + 1; + rect.y0 = area.y0 + w - 1; + rect.x1 = area.x1; + rect.y1 = area.y1 - w + 2; + res = ctx->plot->rectangle(ctx, &bg_fill_style, &rect); + if (res != NSERROR_OK) { + return res; + } + /* scrollbar position indicator bar */ - if (!scrollbar_redraw_scrollbar_rectangle(x0 + 1, - bar_c0, - x1 - 1, - bar_c1, - fg_fill_colour, false, ctx)) - return false; - if (!plot->rectangle(x0 + 2, - bar_c0 + 1, - x1 - 1, - bar_c1, - &fg_fill_style)) - return false; - /* bottom arrow background */ - if (!scrollbar_redraw_scrollbar_rectangle(x0 + 1, - y1 - w + 2, - x1 - 1, - y1 - 1, - fg_fill_colour, false, ctx)) - return false; - if (!plot->rectangle(x0 + 2, - y1 - w + 3, - x1 - 1, - y1 - 1, - &fg_fill_style)) - return false; + rect.x0 = area.x0 + 1; + rect.y0 = bar_c0; + rect.x1 = area.x1 - 1; + rect.y1 = bar_c1; + res = scrollbar_rectangle(ctx, &rect, + fg_fill_style.fill_colour, false); + if (res != NSERROR_OK) { + return res; + } + + rect.x0 = area.x0 + 2; + rect.y0 = bar_c0 + 1; + rect.x1 = area.x1 - 1; + rect.y1 = bar_c1; + res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect); + if (res != NSERROR_OK) { + return res; + } + + /* down arrow icon border */ + rect.x0 = area.x0 + 1; + rect.y0 = area.y1 - w + 2; + rect.x1 = area.x1 - 1; + rect.y1 = area.y1 - 1; + res = scrollbar_rectangle(ctx, &rect, + fg_fill_style.fill_colour, false); + if (res != NSERROR_OK) { + return res; + } + + /* down arrow icon background */ + rect.x0 = area.x0 + 2; + rect.y0 = area.y1 - w + 3; + rect.x1 = area.x1 - 1; + rect.y1 = area.y1 - 1; + res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect); + if (res != NSERROR_OK) { + return res; + } + /* 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, &arrow_fill_style)) - return false; + v[0] = area.x0 + w / 2; + v[1] = area.y1 - w / 4 + 1; + v[2] = area.x0 + w / 4; + v[3] = area.y1 - w * 3 / 4 + 1; + v[4] = area.x0 + w * 3 / 4; + v[5] = area.y1 - w * 3 / 4 + 1; + res = ctx->plot->polygon(ctx, &arrow_fill_style, v, 3); + if (res != NSERROR_OK) { + return res; + } } - return true; + return NSERROR_OK; } /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ void scrollbar_set(struct scrollbar *s, int value, bool bar_pos) { @@ -391,46 +526,49 @@ void scrollbar_set(struct scrollbar *s, int value, bool bar_pos) int old_offset = s->offset; struct scrollbar_msg_data msg; - if (value < 0) + if (value < 0) { value = 0; + } - if (s->full_size == s->visible_size) + if (s->full_size == s->visible_size) { return; + } well_length = s->length - 2 * SCROLLBAR_WIDTH; if (bar_pos) { - if (value > well_length - s->bar_len) + if (value > well_length - s->bar_len) { s->bar_pos = well_length - s->bar_len; - else + } else { s->bar_pos = value; + } s->offset = ((well_length - s->bar_len) < 1) ? 0 : - (((s->full_size - s->visible_size) * - s->bar_pos) / (well_length - s->bar_len)); + (((s->full_size - s->visible_size) * + s->bar_pos) / (well_length - s->bar_len)); } else { - if (value > s->full_size - s->visible_size) + if (value > s->full_size - s->visible_size) { s->offset = s->full_size - s->visible_size; - else + } else { s->offset = value; + } s->bar_pos = (s->full_size < 1) ? 0 : - ((well_length * s->offset) / s->full_size); + ((well_length * s->offset) / s->full_size); } - if (s->offset == old_offset) - /* Nothing happened */ - return; - - msg.scrollbar = s; - msg.msg = SCROLLBAR_MSG_MOVED; - msg.scroll_offset = s->offset; - s->client_callback(s->client_data, &msg); + if (s->offset != old_offset) { + /* client callback if there was a chnage */ + msg.scrollbar = s; + msg.msg = SCROLLBAR_MSG_MOVED; + msg.scroll_offset = s->offset; + s->client_callback(s->client_data, &msg); + } } /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ bool scrollbar_scroll(struct scrollbar *s, int change) { @@ -438,9 +576,10 @@ bool scrollbar_scroll(struct scrollbar *s, int change) int old_offset = s->offset; struct scrollbar_msg_data msg; - if (change == 0 || s->full_size <= s->visible_size) + if (change == 0 || s->full_size <= s->visible_size) { /* zero scroll step, or unscrollable */ return false; + } /* Convert named change values to appropriate pixel offset value */ switch (change) { @@ -466,21 +605,23 @@ bool scrollbar_scroll(struct scrollbar *s, int change) } /* Get new offset */ - if (s->offset + change > s->full_size - s->visible_size) + if (s->offset + change > s->full_size - s->visible_size) { s->offset = s->full_size - s->visible_size; - else if (s->offset + change < 0) + } else if (s->offset + change < 0) { s->offset = 0; - else + } else { s->offset += change; + } - if (s->offset == old_offset) + if (s->offset == old_offset) { /* Nothing happened */ return false; + } /* Update scrollbar */ well_length = s->length - 2 * SCROLLBAR_WIDTH; s->bar_pos = (s->full_size < 1) ? 0 : - ((well_length * s->offset) / s->full_size); + ((well_length * s->offset) / s->full_size); msg.scrollbar = s; msg.msg = SCROLLBAR_MSG_MOVED; @@ -492,41 +633,47 @@ bool scrollbar_scroll(struct scrollbar *s, int change) /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ int scrollbar_get_offset(struct scrollbar *s) { - if (s == NULL) + if (s == NULL) { return 0; + } return s->offset; } /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ void scrollbar_set_extents(struct scrollbar *s, int length, - int visible_size, int full_size) + int visible_size, int full_size) { int cur_excess = s->full_size - s->visible_size; int well_length; struct scrollbar_msg_data msg; - if (length == s->length && visible_size == s->visible_size && - full_size == s->full_size) { + if (length == s->length && + visible_size == s->visible_size && + full_size == s->full_size) { /* Nothing's changed. */ return; } - if (length != -1) + if (length != -1) { s->length = length; - if (visible_size != -1) + } + if (visible_size != -1) { s->visible_size = visible_size; - if (full_size != -1) + } + if (full_size != -1) { s->full_size = full_size; + } - if (s->full_size < s->visible_size) + if (s->full_size < s->visible_size) { s->full_size = s->visible_size; + } /* Update scroll offset (scaled in proportion with change in excess) */ if (cur_excess <= 0) { @@ -553,7 +700,7 @@ void scrollbar_set_extents(struct scrollbar *s, int length, /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ bool scrollbar_is_horizontal(struct scrollbar *s) { @@ -568,12 +715,14 @@ bool scrollbar_is_horizontal(struct scrollbar *s) * \param x the X coordinate of the drag start * \param y the Y coordinate of the drag start * \param content_drag whether this should be a reverse drag (used when the - * user drags the content area, rather than the scrollbar) + * user drags the content area, rather than the scrollbar) * \param pair whether the drag is a '2D' scroll */ - -static void scrollbar_drag_start_internal(struct scrollbar *s, int x, int y, - bool content_drag, bool pair) +static void +scrollbar_drag_start_internal(struct scrollbar *s, + int x, int y, + bool content_drag, + bool pair) { struct scrollbar_msg_data msg; @@ -585,7 +734,7 @@ static void scrollbar_drag_start_internal(struct scrollbar *s, int x, int y, msg.scrollbar = s; - /* \todo - some proper numbers please! */ + /** \todo some proper numbers please! */ if (s->horizontal) { msg.x0 = -2048; msg.x1 = 2048; @@ -602,10 +751,10 @@ static void scrollbar_drag_start_internal(struct scrollbar *s, int x, int y, s->pair_drag = true; s->pair->drag_start_coord = - s->pair->horizontal ? x : y; + s->pair->horizontal ? x : y; s->pair->drag_start_pos = (content_drag) ? s->pair->offset : - s->pair->bar_pos; + s->pair->bar_pos; s->pair->dragging = true; s->pair->drag_content = content_drag; @@ -624,10 +773,12 @@ static void scrollbar_drag_start_internal(struct scrollbar *s, int x, int y, /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ -scrollbar_mouse_status scrollbar_mouse_action(struct scrollbar *s, - browser_mouse_state mouse, int x, int y) +scrollbar_mouse_status +scrollbar_mouse_action(struct scrollbar *s, + browser_mouse_state mouse, + int x, int y) { int x0, y0, x1, y1; int val; @@ -638,13 +789,13 @@ scrollbar_mouse_status scrollbar_mouse_action(struct scrollbar *s, * scrollbar 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) && - !s->dragging)); + ((mouse & BROWSER_MOUSE_HOLDING_1) && + (mouse & BROWSER_MOUSE_DRAG_ON) && + !s->dragging)); bool but2 = ((mouse & BROWSER_MOUSE_PRESS_2) || - ((mouse & BROWSER_MOUSE_HOLDING_2) && - (mouse & BROWSER_MOUSE_DRAG_ON) && - !s->dragging)); + ((mouse & BROWSER_MOUSE_HOLDING_2) && + (mouse & BROWSER_MOUSE_DRAG_ON) && + !s->dragging)); h = s->horizontal; @@ -659,88 +810,91 @@ scrollbar_mouse_status scrollbar_mouse_action(struct scrollbar *s, } - if (h) + if (h) { val = x; - else + } else { val = y; + } if (s->dragging) { val -= s->drag_start_coord; - if (s->drag_content) + if (s->drag_content) { val = -val; - if (val != 0) + } + if (val != 0) { scrollbar_set(s, s->drag_start_pos + val, - !(s->drag_content)); + !(s->drag_content)); + } if (s->pair_drag) { scrollbar_mouse_action(s->pair, mouse, x, y); status = SCROLLBAR_MOUSE_BOTH; - } else + } else { status = h ? SCROLLBAR_MOUSE_HRZ : SCROLLBAR_MOUSE_VRT; - + } return status; } if (val < SCROLLBAR_WIDTH) { /* left/up arrow */ - + status = h ? SCROLLBAR_MOUSE_LFT : SCROLLBAR_MOUSE_UP; - if (but1) + if (but1) { scrollbar_set(s, s->offset - SCROLLBAR_WIDTH, false); - else if (but2) + } else if (but2) { scrollbar_set(s, s->offset + SCROLLBAR_WIDTH, false); - + } } else if (val < SCROLLBAR_WIDTH + s->bar_pos) { /* well between left/up arrow and bar */ status = h ? SCROLLBAR_MOUSE_PLFT : SCROLLBAR_MOUSE_PUP; - if (but1) + if (but1) { scrollbar_set(s, s->offset - s->length, false); - else if (but2) + } else if (but2) { scrollbar_set(s, s->offset + s->length, false); - + } } else if (val > s->length - SCROLLBAR_WIDTH) { /* right/down arrow */ status = h ? SCROLLBAR_MOUSE_RGT : SCROLLBAR_MOUSE_DWN; - if (but1) + if (but1) { scrollbar_set(s, s->offset + SCROLLBAR_WIDTH, false); - else if (but2) + } else if (but2) { scrollbar_set(s, s->offset - SCROLLBAR_WIDTH, false); - - } else if (val > SCROLLBAR_WIDTH + s->bar_pos + - s->bar_len) { + } + } else if (val > SCROLLBAR_WIDTH + s->bar_pos + s->bar_len) { /* well between right/down arrow and bar */ status = h ? SCROLLBAR_MOUSE_PRGT : SCROLLBAR_MOUSE_PDWN; - if (but1) + if (but1) { scrollbar_set(s, s->offset + s->length, false); - else if (but2) + } else if (but2) { scrollbar_set(s, s->offset - s->length, false); - } - else { + } + } else { /* scrollbar position indication bar */ - + status = h ? SCROLLBAR_MOUSE_HRZ : SCROLLBAR_MOUSE_VRT; } - + if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2) && - (val >= SCROLLBAR_WIDTH + s->bar_pos - && val < SCROLLBAR_WIDTH + s->bar_pos + - s->bar_len)) + (val >= SCROLLBAR_WIDTH + s->bar_pos + && val < SCROLLBAR_WIDTH + s->bar_pos + + s->bar_len)) { /* The mouse event is a drag start on the scrollbar position * indication bar. */ scrollbar_drag_start_internal(s, x, y, false, - (mouse & BROWSER_MOUSE_DRAG_2) ? true : false); + (mouse & BROWSER_MOUSE_DRAG_2) ? true : false); + } return status; } /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ const char *scrollbar_mouse_status_to_message(scrollbar_mouse_status status) { @@ -784,10 +938,10 @@ const char *scrollbar_mouse_status_to_message(scrollbar_mouse_status status) /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ void scrollbar_mouse_drag_end(struct scrollbar *s, - browser_mouse_state mouse, int x, int y) + browser_mouse_state mouse, int x, int y) { struct scrollbar_msg_data msg; int val, drag_start_pos; @@ -797,10 +951,12 @@ void scrollbar_mouse_drag_end(struct scrollbar *s, drag_start_pos = s->drag_start_pos; val = (s->horizontal ? x : y) - s->drag_start_coord; - if (s->drag_content) + if (s->drag_content) { val = -val; - if (val != 0) + } + if (val != 0) { scrollbar_set(s, drag_start_pos + val, !(s->drag_content)); + } s->dragging = false; s->drag_content = false; @@ -811,11 +967,13 @@ void scrollbar_mouse_drag_end(struct scrollbar *s, drag_start_pos = s->pair->drag_start_pos; val = (s->pair->horizontal ? x : y) - s->pair->drag_start_coord; - if (s->pair->drag_content) + if (s->pair->drag_content) { val = -val; - if (val != 0) + } + if (val != 0) { scrollbar_set(s->pair, drag_start_pos + val, - !(s->pair->drag_content)); + !(s->pair->drag_content)); + } s->pair->dragging = false; s->pair->drag_content = false; @@ -828,7 +986,7 @@ void scrollbar_mouse_drag_end(struct scrollbar *s, /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ void scrollbar_start_content_drag(struct scrollbar *s, int x, int y) { @@ -837,13 +995,13 @@ void scrollbar_start_content_drag(struct scrollbar *s, int x, int y) /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ void scrollbar_make_pair(struct scrollbar *horizontal, - struct scrollbar *vertical) + struct scrollbar *vertical) { assert(horizontal->horizontal && - !vertical->horizontal); + !vertical->horizontal); horizontal->pair = vertical; vertical->pair = horizontal; @@ -851,10 +1009,9 @@ void scrollbar_make_pair(struct scrollbar *horizontal, /* - * Exported function. Documented in scrollbar.h + * Exported interface. Documented in scrollbar.h */ void *scrollbar_get_data(struct scrollbar *s) { return s->client_data; } - diff --git a/desktop/scrollbar.h b/desktop/scrollbar.h index d277be26c..fa5e167f2 100644 --- a/desktop/scrollbar.h +++ b/desktop/scrollbar.h @@ -16,12 +16,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * Scrollbar widget (interface). +/** + * \file + * Scrollbar widget interface. + * + * Scrollbar widgets used in frames code, not for frontend use */ -#ifndef _NETSURF_DESKTOP_SCROLLBAR_H_ -#define _NETSURF_DESKTOP_SCROLLBAR_H_ +#ifndef NETSURF_DESKTOP_SCROLLBAR_H +#define NETSURF_DESKTOP_SCROLLBAR_H #include <stdbool.h> #include <limits.h> @@ -29,23 +32,29 @@ #define SCROLLBAR_WIDTH 16 /* Region dependent values for scrollbar_scroll function */ -#define SCROLL_TOP INT_MIN -#define SCROLL_PAGE_UP INT_MIN + 1 -#define SCROLL_PAGE_DOWN INT_MAX - 1 -#define SCROLL_BOTTOM INT_MAX +#define SCROLL_TOP INT_MIN +#define SCROLL_PAGE_UP (INT_MIN + 1) +#define SCROLL_PAGE_DOWN (INT_MAX - 1) +#define SCROLL_BOTTOM INT_MAX struct scrollbar; +/** + * scrollbar message types + */ typedef enum { - SCROLLBAR_MSG_MOVED, /* the scroll value has changed */ - SCROLLBAR_MSG_SCROLL_START, /* a scrollbar drag has started, all - * mouse events should be passed to + SCROLLBAR_MSG_MOVED, /**< the scroll value has changed */ + SCROLLBAR_MSG_SCROLL_START, /**< a scrollbar drag has started, all + * mouse events should be passed to * the scrollbar regardless of the * coordinates */ - SCROLLBAR_MSG_SCROLL_FINISHED, /* cancel the above */ + SCROLLBAR_MSG_SCROLL_FINISHED, /**< cancel a scrollbar drag */ } scrollbar_msg; +/** + * scrollbar message context data + */ struct scrollbar_msg_data { struct scrollbar *scrollbar; scrollbar_msg msg; @@ -53,9 +62,30 @@ struct scrollbar_msg_data { int x0, y0, x1, y1; }; + +/** + * Scrollbar mouse input status flags + */ +typedef enum { + SCROLLBAR_MOUSE_NONE = 0, /**< Not relevant */ + SCROLLBAR_MOUSE_USED = (1 << 0), /**< Took action with input */ + SCROLLBAR_MOUSE_BOTH = (1 << 1), /**< Scrolling both bars */ + SCROLLBAR_MOUSE_UP = (1 << 2), /**< Hover: scroll up */ + SCROLLBAR_MOUSE_PUP = (1 << 3), /**< Hover: scroll page up */ + SCROLLBAR_MOUSE_VRT = (1 << 4), /**< Hover: vert. drag bar */ + SCROLLBAR_MOUSE_PDWN = (1 << 5), /**< Hover: scroll page down */ + SCROLLBAR_MOUSE_DWN = (1 << 6), /**< Hover: scroll down */ + SCROLLBAR_MOUSE_LFT = (1 << 7), /**< Hover: scroll left */ + SCROLLBAR_MOUSE_PLFT = (1 << 8), /**< Hover: scroll page left */ + SCROLLBAR_MOUSE_HRZ = (1 << 9), /**< Hover: horiz. drag bar */ + SCROLLBAR_MOUSE_PRGT = (1 << 10), /**< Hover: scroll page right */ + SCROLLBAR_MOUSE_RGT = (1 << 11) /**< Hover: scroll right */ +} scrollbar_mouse_status; + + /** * Client callback for the scrollbar. - * + * * \param client_data user data passed at scroll creation * \param scrollbar_data scrollbar message data */ @@ -84,7 +114,7 @@ nserror scrollbar_create(bool horizontal, int length, int full_size, /** * Destroy a scrollbar. * - * \param s the scrollbar to be destroyed + * \param s the scrollbar to be destroyed */ void scrollbar_destroy(struct scrollbar *s); @@ -97,9 +127,9 @@ void scrollbar_destroy(struct scrollbar *s); * \param clip the clipping rectangle * \param scale scale for the redraw * \param ctx current redraw context - * \return true on succes false otherwise + * \return NSERROR_OK on success otherwise error code */ -bool scrollbar_redraw(struct scrollbar *s, int x, int y, +nserror scrollbar_redraw(struct scrollbar *s, int x, int y, const struct rect *clip, float scale, const struct redraw_context *ctx); @@ -125,8 +155,8 @@ bool scrollbar_scroll(struct scrollbar *s, int change); /** * Get the current scroll offset to the visible part of the full area. * - * \param s the scrollbar to get the scroll offset value from - * \return current scroll offset + * \param s the scrollbar to get the scroll offset value from + * \return current scroll offset */ int scrollbar_get_offset(struct scrollbar *s); @@ -150,22 +180,6 @@ void scrollbar_set_extents(struct scrollbar *s, int length, */ bool scrollbar_is_horizontal(struct scrollbar *s); -/* Scrollbar mouse input status flags */ -typedef enum { - SCROLLBAR_MOUSE_NONE = 0, /**< Not relevant */ - SCROLLBAR_MOUSE_USED = (1 << 0), /**< Took action with input */ - SCROLLBAR_MOUSE_BOTH = (1 << 1), /**< Scrolling both bars */ - SCROLLBAR_MOUSE_UP = (1 << 2), /**< Hover: scroll up */ - SCROLLBAR_MOUSE_PUP = (1 << 3), /**< Hover: scroll page up */ - SCROLLBAR_MOUSE_VRT = (1 << 4), /**< Hover: vert. drag bar */ - SCROLLBAR_MOUSE_PDWN = (1 << 5), /**< Hover: scroll page down */ - SCROLLBAR_MOUSE_DWN = (1 << 6), /**< Hover: scroll down */ - SCROLLBAR_MOUSE_LFT = (1 << 7), /**< Hover: scroll left */ - SCROLLBAR_MOUSE_PLFT = (1 << 8), /**< Hover: scroll page left */ - SCROLLBAR_MOUSE_HRZ = (1 << 9), /**< Hover: horiz. drag bar */ - SCROLLBAR_MOUSE_PRGT = (1 << 10), /**< Hover: scroll page right */ - SCROLLBAR_MOUSE_RGT = (1 << 11) /**< Hover: scroll right */ -} scrollbar_mouse_status; /** * Handle mouse actions other then drag ends. @@ -179,6 +193,7 @@ typedef enum { scrollbar_mouse_status scrollbar_mouse_action(struct scrollbar *s, browser_mouse_state mouse, int x, int y); + /** * Get a status bar message from a scrollbar mouse input status. * @@ -187,6 +202,7 @@ scrollbar_mouse_status scrollbar_mouse_action(struct scrollbar *s, */ const char *scrollbar_mouse_status_to_message(scrollbar_mouse_status status); + /** * Handle end of mouse drags. * @@ -198,18 +214,21 @@ const char *scrollbar_mouse_status_to_message(scrollbar_mouse_status status); void scrollbar_mouse_drag_end(struct scrollbar *s, browser_mouse_state mouse, int x, int y); + /** * Called when the content is being dragged to the scrollbars have to adjust. + * * If the content has both scrollbars, and scrollbar_make_pair has beed called * before, only the one scroll which will receive further mouse events has to be * passed. * - * \param s one of the the scrollbars owned by the dragged content - * \param x X coordinate of mouse during drag start - * \param y Y coordinate of mouse during drag start + * \param s one of the the scrollbars owned by the dragged content + * \param x X coordinate of mouse during drag start + * \param y Y coordinate of mouse during drag start */ void scrollbar_start_content_drag(struct scrollbar *s, int x, int y); + /** * Connect a horizontal and a vertical scrollbar into a pair so that they * co-operate during 2D drags. @@ -220,11 +239,12 @@ void scrollbar_start_content_drag(struct scrollbar *s, int x, int y); void scrollbar_make_pair(struct scrollbar *horizontal, struct scrollbar *vertical); + /** * Get the scrollbar's client data * - * \param s the scrollbar to get the client data from - * \return client data + * \param s the scrollbar to get the client data from + * \return client data */ void *scrollbar_get_data(struct scrollbar *s); diff --git a/desktop/searchweb.c b/desktop/searchweb.c index 29a998eb2..c07cac9d5 100644 --- a/desktop/searchweb.c +++ b/desktop/searchweb.c @@ -289,13 +289,19 @@ search_web_ico_callback(hlcache_handle *ico, switch (event->type) { case CONTENT_MSG_DONE: - LOG("icon '%s' retrieved", nsurl_access(hlcache_handle_get_url(ico))); + NSLOG(netsurf, INFO, "icon '%s' retrieved", + nsurl_access(hlcache_handle_get_url(ico))); guit->search_web->provider_update(provider->name, content_get_bitmap(ico)); break; case CONTENT_MSG_ERROR: - LOG("icon %s error: %s", nsurl_access(hlcache_handle_get_url(ico)), event->data.error); + NSLOG(netsurf, INFO, "icon %s error: %s", + nsurl_access(hlcache_handle_get_url(ico)), + event->data.error); + /* fall through */ + + case CONTENT_MSG_ERRORCODE: hlcache_handle_release(ico); /* clear reference to released handle */ provider->ico_handle = NULL; @@ -438,7 +444,8 @@ default_ico_callback(hlcache_handle *ico, switch (event->type) { case CONTENT_MSG_DONE: - LOG("default icon '%s' retrieved", nsurl_access(hlcache_handle_get_url(ico))); + NSLOG(netsurf, INFO, "default icon '%s' retrieved", + nsurl_access(hlcache_handle_get_url(ico))); /* only set to default icon if providers icon has no handle */ if (ctx->providers[search_web_ctx.current].ico_handle == NULL) { @@ -449,7 +456,12 @@ default_ico_callback(hlcache_handle *ico, break; case CONTENT_MSG_ERROR: - LOG("icon %s error: %s", nsurl_access(hlcache_handle_get_url(ico)), event->data.error); + NSLOG(netsurf, INFO, "icon %s error: %s", + nsurl_access(hlcache_handle_get_url(ico)), + event->data.error); + /* fall through */ + + case CONTENT_MSG_ERRORCODE: hlcache_handle_release(ico); /* clear reference to released handle */ ctx->default_ico_handle = NULL; diff --git a/desktop/selection.c b/desktop/selection.c index 7506af0ef..35eabb2a9 100644 --- a/desktop/selection.c +++ b/desktop/selection.c @@ -32,10 +32,10 @@ #include "utils/utf8.h" #include "utils/utils.h" #include "netsurf/form.h" -#include "render/box.h" -#include "render/html_internal.h" -#include "render/font.h" -#include "render/textplain.h" +#include "html/box.h" +#include "html/html_internal.h" +#include "html/font.h" +#include "text/textplain.h" #include "netsurf/mouse.h" #include "desktop/browser_private.h" @@ -70,18 +70,20 @@ struct selection_string { typedef bool (*seln_traverse_handler)(const char *text, size_t length, - struct box *box, void *handle, const char *whitespace_text, - size_t whitespace_length); + struct box *box, const nscss_len_ctx *len_ctx, void *handle, + const char *whitespace_text, size_t whitespace_length); -static bool redraw_handler(const char *text, size_t length, struct box *box, +static bool redraw_handler(const char *text, size_t length, + struct box *box, const nscss_len_ctx *len_ctx, void *handle, const char *whitespace_text, size_t whitespace_length); static void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx); static bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx, unsigned *start_offset, unsigned *end_offset); -static bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, +static bool traverse_tree(struct box *box, const nscss_len_ctx *len_ctx, + unsigned start_idx, unsigned end_idx, seln_traverse_handler handler, void *handle, save_text_whitespace *before, bool *first, bool do_marker); @@ -198,7 +200,10 @@ void selection_reinit(struct selection *s, struct box *root) * \param root the root box for html document or NULL for text/plain */ -void selection_init(struct selection *s, struct box *root) +void selection_init( + struct selection *s, + struct box *root, + const nscss_len_ctx *len_ctx) { if (s->defined) selection_clear(s, true); @@ -207,6 +212,13 @@ void selection_init(struct selection *s, struct box *root) s->start_idx = 0; s->end_idx = 0; s->drag_state = DRAG_NONE; + if (len_ctx != NULL) { + s->len_ctx = *len_ctx; + } else { + s->len_ctx.vw = 0; + s->len_ctx.vh = 0; + s->len_ctx.root_style = NULL; + } selection_reinit(s, root); } @@ -442,6 +454,7 @@ bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx, * for all boxes that lie (partially) within the given range * * \param box box subtree + * \param len_ctx Length conversion context. * \param start_idx start of range within textual representation (bytes) * \param end_idx end of range * \param handler handler function to call @@ -452,7 +465,9 @@ bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx, * \return false iff traversal abandoned part-way through */ -bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, +bool traverse_tree( + struct box *box, const nscss_len_ctx *len_ctx, + unsigned start_idx, unsigned end_idx, seln_traverse_handler handler, void *handle, save_text_whitespace *before, bool *first, bool do_marker) @@ -473,9 +488,9 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, if (box->list_marker) { /* do the marker box before continuing with the rest of the * list element */ - if (!traverse_tree(box->list_marker, start_idx, end_idx, - handler, handle, before, first, - true)) + if (!traverse_tree(box->list_marker, len_ctx, + start_idx, end_idx, handler, handle, + before, first, true)) return false; } @@ -506,7 +521,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, &end_offset)) { if (!handler(box->text + start_offset, min(box->length, end_offset) - start_offset, - box, handle, whitespace_text, + box, len_ctx, handle, whitespace_text, whitespace_length)) return false; if (before) { @@ -533,7 +548,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, * the tree */ struct box *next = child->next; - if (!traverse_tree(child, start_idx, end_idx, + if (!traverse_tree(child, len_ctx, start_idx, end_idx, handler, handle, before, first, false)) return false; @@ -568,14 +583,16 @@ static bool selection_traverse(struct selection *s, if (s->root) { /* HTML */ - return traverse_tree(s->root, s->start_idx, s->end_idx, - handler, handle, &before, &first, false); + return traverse_tree(s->root, &s->len_ctx, + s->start_idx, s->end_idx, + handler, handle, + &before, &first, false); } /* Text */ text = textplain_get_raw_data(s->c, s->start_idx, s->end_idx, &length); - if (text && !handler(text, length, NULL, handle, NULL, 0)) + if (text && !handler(text, length, NULL, NULL, handle, NULL, 0)) return false; return true; @@ -597,8 +614,8 @@ static bool selection_traverse(struct selection *s, */ bool redraw_handler(const char *text, size_t length, struct box *box, - void *handle, const char *whitespace_text, - size_t whitespace_length) + const nscss_len_ctx *len_ctx, void *handle, + const char *whitespace_text, size_t whitespace_length) { if (box) { struct rdw_info *r = (struct rdw_info*)handle; @@ -606,7 +623,7 @@ bool redraw_handler(const char *text, size_t length, struct box *box, int x, y; plot_font_style_t fstyle; - font_plot_style_from_css(box->style, &fstyle); + font_plot_style_from_css(len_ctx, box->style, &fstyle); /* \todo - it should be possible to reduce the redrawn area by * considering the 'text', 'length' and 'space' parameters */ @@ -652,7 +669,7 @@ void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx) rdw.inited = false; if (s->root) { - if (!traverse_tree(s->root, start_idx, end_idx, + if (!traverse_tree(s->root, &s->len_ctx, start_idx, end_idx, redraw_handler, &rdw, NULL, NULL, false)) return; @@ -739,10 +756,11 @@ static bool selection_string_append(const char *text, size_t length, bool space, /** * Selection traversal routine for appending text to a string * - * \param text pointer to text being added, or NULL for newline - * \param length length of text to be appended (bytes) - * \param box pointer to text box, or NULL if from textplain - * \param handle selection string to append to + * \param text pointer to text being added, or NULL for newline + * \param length length of text to be appended (bytes) + * \param box pointer to text box, or NULL if from textplain + * \param len_ctx Length conversion context + * \param handle selection string to append to * \param whitespace_text whitespace to place before text for formatting * may be NULL * \param whitespace_length length of whitespace_text @@ -750,7 +768,8 @@ static bool selection_string_append(const char *text, size_t length, bool space, */ static bool selection_copy_handler(const char *text, size_t length, - struct box *box, void *handle, const char *whitespace_text, + struct box *box, const nscss_len_ctx *len_ctx, + void *handle, const char *whitespace_text, size_t whitespace_length) { bool add_space = false; @@ -771,7 +790,7 @@ static bool selection_copy_handler(const char *text, size_t length, if (box->style != NULL) { /* Override default font style */ - font_plot_style_from_css(box->style, &style); + font_plot_style_from_css(len_ctx, box->style, &style); pstyle = &style; } else { /* If there's no style, there must be no text */ diff --git a/desktop/selection.h b/desktop/selection.h index e2bc3b31d..2f3f6dcfe 100644 --- a/desktop/selection.h +++ b/desktop/selection.h @@ -25,6 +25,7 @@ #include <stdbool.h> #include "netsurf/mouse.h" +#include "content/handlers/css/utils.h" struct box; @@ -43,6 +44,7 @@ struct selection { struct content *c; struct box *root; + nscss_len_ctx len_ctx; unsigned max_idx; /* total bytes in text representation */ @@ -60,7 +62,10 @@ struct selection *selection_create(struct content *c, bool is_html); void selection_prepare(struct selection *s, struct content *c, bool is_html); void selection_destroy(struct selection *s); -void selection_init(struct selection *s, struct box *root); +void selection_init( + struct selection *s, + struct box *root, + const nscss_len_ctx *len_ctx); void selection_reinit(struct selection *s, struct box *root); /* struct box *selection_root(struct selection *s); */ diff --git a/desktop/sslcert_viewer.c b/desktop/sslcert_viewer.c index 09a281c1c..f40af5968 100644 --- a/desktop/sslcert_viewer.c +++ b/desktop/sslcert_viewer.c @@ -17,8 +17,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * SSL Certificate verification UI (implementation) +/** + * \file + * SSL Certificate verification UI implementation */ #include <assert.h> @@ -33,6 +34,9 @@ #include "utils/log.h" #include "utils/utils.h" +/** + * ssl certificate viewer data fields + */ enum sslcert_viewer_field { SSLCERT_V_SUBJECT, SSLCERT_V_SERIAL, @@ -45,7 +49,10 @@ enum sslcert_viewer_field { SSLCERT_V_N_FIELDS }; -/** ssl certificate verification context. */ + +/** + * ssl certificate verification context. + */ struct sslcert_session_data { struct ssl_cert_info *certs; /**< Certificates */ unsigned long num; /**< Number of certificates in chain */ @@ -57,6 +64,10 @@ struct sslcert_session_data { struct treeview_field_desc fields[SSLCERT_V_N_FIELDS]; }; + +/** + * ssl certificate tree entry + */ struct sslcert_entry { treeview_node *entry; char version[24]; @@ -67,12 +78,11 @@ struct sslcert_entry { /** - * Free a sll certificate viewer entry's treeview field data. + * Free a ssl certificate viewer entry's treeview field data. * - * \param e Entry to free data from + * \param e Entry to free data from */ -static void sslcert_viewer_free_treeview_field_data( - struct sslcert_entry *e) +static void sslcert_viewer_free_treeview_field_data(struct sslcert_entry *e) { } @@ -80,17 +90,17 @@ static void sslcert_viewer_free_treeview_field_data( /** * Build a sslcert viewer treeview field from given text * - * \param field SSL certificate treeview field to build - * \param data SSL certificate entry field data to set - * \param value Text to set in field, ownership yielded - * \param ssl_d SSL certificate session data + * \param field SSL certificate treeview field to build + * \param data SSL certificate entry field data to set + * \param value Text to set in field, ownership yielded + * \param ssl_d SSL certificate session data * \return NSERROR_OK on success, appropriate error otherwise */ -static inline nserror sslcert_viewer_field_builder( - enum sslcert_viewer_field field, - struct treeview_field_data *data, - const char *value, - struct sslcert_session_data *ssl_d) +static inline nserror +sslcert_viewer_field_builder(enum sslcert_viewer_field field, + struct treeview_field_data *data, + const char *value, + struct sslcert_session_data *ssl_d) { data->field = ssl_d->fields[field].field; data->value = value; @@ -103,15 +113,15 @@ static inline nserror sslcert_viewer_field_builder( /** * Set a sslcert viewer entry's data from the certificate. * - * \param e Entry to set up - * \param cert Data associated with entry's certificate - * \param ssl_d SSL certificate session data + * \param e Entry to set up + * \param cert Data associated with entry's certificate + * \param ssl_d SSL certificate session data * \return NSERROR_OK on success, appropriate error otherwise */ -static nserror sslcert_viewer_set_treeview_field_data( - struct sslcert_entry *e, - const struct ssl_cert_info *cert, - struct sslcert_session_data *ssl_d) +static nserror +sslcert_viewer_set_treeview_field_data(struct sslcert_entry *e, + const struct ssl_cert_info *cert, + struct sslcert_session_data *ssl_d) { unsigned int written; @@ -121,39 +131,39 @@ static nserror sslcert_viewer_set_treeview_field_data( /* Set the fields up */ sslcert_viewer_field_builder(SSLCERT_V_SUBJECT, - &e->data[SSLCERT_V_SUBJECT], - cert->subject, ssl_d); + &e->data[SSLCERT_V_SUBJECT], + cert->subject, ssl_d); written = snprintf(e->serial, sizeof(e->serial), "%li", cert->serial); assert(written < sizeof(e->serial)); sslcert_viewer_field_builder(SSLCERT_V_SERIAL, - &e->data[SSLCERT_V_SERIAL], - e->serial, ssl_d); + &e->data[SSLCERT_V_SERIAL], + e->serial, ssl_d); written = snprintf(e->type, sizeof(e->type), "%i", cert->cert_type); assert(written < sizeof(e->type)); sslcert_viewer_field_builder(SSLCERT_V_TYPE, - &e->data[SSLCERT_V_TYPE], - e->type, ssl_d); + &e->data[SSLCERT_V_TYPE], + e->type, ssl_d); sslcert_viewer_field_builder(SSLCERT_V_VALID_UNTIL, - &e->data[SSLCERT_V_VALID_UNTIL], - cert->not_after, ssl_d); + &e->data[SSLCERT_V_VALID_UNTIL], + cert->not_after, ssl_d); sslcert_viewer_field_builder(SSLCERT_V_VALID_FROM, - &e->data[SSLCERT_V_VALID_FROM], - cert->not_before, ssl_d); + &e->data[SSLCERT_V_VALID_FROM], + cert->not_before, ssl_d); written = snprintf(e->version, sizeof(e->version), - "%li", cert->version); + "%li", cert->version); assert(written < sizeof(e->version)); sslcert_viewer_field_builder(SSLCERT_V_VERSION, - &e->data[SSLCERT_V_VERSION], - e->version, ssl_d); + &e->data[SSLCERT_V_VERSION], + e->version, ssl_d); sslcert_viewer_field_builder(SSLCERT_V_ISSUER, - &e->data[SSLCERT_V_ISSUER], - cert->issuer, ssl_d); + &e->data[SSLCERT_V_ISSUER], + cert->issuer, ssl_d); return NSERROR_OK; } @@ -162,12 +172,12 @@ static nserror sslcert_viewer_set_treeview_field_data( /** * Create a treeview node for a certificate * - * \param ssl_d SSL certificate session data - * \param n Number of SSL certificate in chain, to make node for - * \return true on success, false on memory exhaustion + * \param ssl_d SSL certificate session data + * \param n Number of SSL certificate in chain, to make node for + * \return NSERROR_OK on success otherwise error code. */ -static nserror sslcert_viewer_create_node( - struct sslcert_session_data *ssl_d, int n) +static nserror +sslcert_viewer_create_node(struct sslcert_session_data *ssl_d, int n) { struct sslcert_entry *e; const struct ssl_cert_info *cert = &(ssl_d->certs[n]); @@ -187,8 +197,8 @@ static nserror sslcert_viewer_create_node( /* Create the new treeview node */ err = treeview_create_node_entry(ssl_d->tree, &(e->entry), - NULL, TREE_REL_FIRST_CHILD, - e->data, e, TREE_OPTION_NONE); + NULL, TREE_REL_FIRST_CHILD, + e->data, e, TREE_OPTION_NONE); if (err != NSERROR_OK) { sslcert_viewer_free_treeview_field_data(e); free(e); @@ -200,10 +210,10 @@ static nserror sslcert_viewer_create_node( /** - * Initialise the treeview entry feilds + * Initialise the treeview entry fields * - * \param ssl_d SSL certificate session data - * \return true on success, false on memory exhaustion + * \param ssl_d SSL certificate session data + * \return NSERROR_OK on success otherwise error code. */ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) { @@ -217,8 +227,8 @@ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) label = "TreeviewLabelSubject"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_SUBJECT].field) != - lwc_error_ok) { + &ssl_d->fields[SSLCERT_V_SUBJECT].field) != + lwc_error_ok) { goto error; } @@ -226,8 +236,8 @@ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) label = "TreeviewLabelSerial"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_SERIAL].field) != - lwc_error_ok) { + &ssl_d->fields[SSLCERT_V_SERIAL].field) != + lwc_error_ok) { goto error; } @@ -235,8 +245,8 @@ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) label = "TreeviewLabelType"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_TYPE].field) != - lwc_error_ok) { + &ssl_d->fields[SSLCERT_V_TYPE].field) != + lwc_error_ok) { goto error; } @@ -244,8 +254,8 @@ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) label = "TreeviewLabelValidUntil"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_VALID_UNTIL].field) != - lwc_error_ok) { + &ssl_d->fields[SSLCERT_V_VALID_UNTIL].field) != + lwc_error_ok) { goto error; } @@ -253,8 +263,8 @@ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) label = "TreeviewLabelValidFrom"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_VALID_FROM].field) != - lwc_error_ok) { + &ssl_d->fields[SSLCERT_V_VALID_FROM].field) != + lwc_error_ok) { goto error; } @@ -262,8 +272,8 @@ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) label = "TreeviewLabelVersion"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_VERSION].field) != - lwc_error_ok) { + &ssl_d->fields[SSLCERT_V_VERSION].field) != + lwc_error_ok) { goto error; } @@ -271,8 +281,8 @@ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) label = "TreeviewLabelIssuer"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_ISSUER].field) != - lwc_error_ok) { + &ssl_d->fields[SSLCERT_V_ISSUER].field) != + lwc_error_ok) { goto error; } @@ -280,8 +290,8 @@ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) label = "TreeviewLabelCertificates"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_CERTIFICATES].field) != - lwc_error_ok) { + &ssl_d->fields[SSLCERT_V_CERTIFICATES].field) != + lwc_error_ok) { return false; } @@ -299,7 +309,7 @@ error: /** * Delete ssl certificate viewer entries * - * \param e Entry to delete. + * \param e Entry to delete. */ static void sslcert_viewer_delete_entry(struct sslcert_entry *e) { @@ -308,8 +318,15 @@ static void sslcert_viewer_delete_entry(struct sslcert_entry *e) } -static nserror sslcert_viewer_tree_node_folder_cb( - struct treeview_node_msg msg, void *data) +/** + * folder operation callback + * + * \param msg treeview message + * \param data message context + * \return NSERROR_OK on success + */ +static nserror +sslcert_viewer_tree_node_folder_cb(struct treeview_node_msg msg, void *data) { switch (msg.msg) { case TREE_MSG_NODE_DELETE: @@ -320,8 +337,17 @@ static nserror sslcert_viewer_tree_node_folder_cb( return NSERROR_OK; } -static nserror sslcert_viewer_tree_node_entry_cb( - struct treeview_node_msg msg, void *data) + + +/** + * node entry callback + * + * \param msg treeview message + * \param data message context + * \return NSERROR_OK on success + */ +static nserror +sslcert_viewer_tree_node_entry_cb(struct treeview_node_msg msg, void *data) { struct sslcert_entry *e = data; @@ -338,6 +364,11 @@ static nserror sslcert_viewer_tree_node_entry_cb( return NSERROR_OK; } + + +/** + * ssl certificate treeview callbacks + */ struct treeview_callback_table sslv_tree_cb_t = { .folder = sslcert_viewer_tree_node_folder_cb, .entry = sslcert_viewer_tree_node_entry_cb @@ -345,8 +376,10 @@ struct treeview_callback_table sslv_tree_cb_t = { /* Exported interface, documented in sslcert_viewer.h */ -nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, - void *core_window_handle, struct sslcert_session_data *ssl_d) +nserror +sslcert_viewer_init(struct core_window_callback_table *cw_t, + void *core_window_handle, + struct sslcert_session_data *ssl_d) { nserror err; int cert_loop; @@ -358,7 +391,7 @@ nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, return err; } - LOG("Building certificate viewer"); + NSLOG(netsurf, INFO, "Building certificate viewer"); /* Init. certificate chain treeview entry fields */ err = sslcert_init_entry_fields(ssl_d); @@ -369,8 +402,8 @@ nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, /* Create the certificate treeview */ err = treeview_create(&ssl_d->tree, &sslv_tree_cb_t, - SSLCERT_V_N_FIELDS, ssl_d->fields, - cw_t, core_window_handle, TREEVIEW_READ_ONLY); + SSLCERT_V_N_FIELDS, ssl_d->fields, + cw_t, core_window_handle, TREEVIEW_READ_ONLY); if (err != NSERROR_OK) { ssl_d->tree = NULL; return err; @@ -384,7 +417,7 @@ nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, } } - LOG("Built certificate viewer"); + NSLOG(netsurf, INFO, "Built certificate viewer"); return NSERROR_OK; } @@ -393,7 +426,7 @@ nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, /** * Free SSL certificate session data * - * \param ssl_d SSL certificate session data + * \param ssl_d SSL certificate session data */ static void sslcert_cleanup_session(struct sslcert_session_data *ssl_d) { @@ -419,7 +452,7 @@ nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d) int i; nserror err; - LOG("Finalising ssl certificate viewer"); + NSLOG(netsurf, INFO, "Finalising ssl certificate viewer"); /* Destroy the treeview */ err = treeview_destroy(ssl_d->tree); @@ -437,17 +470,20 @@ nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d) return err; } - LOG("Finalised ssl certificate viewer"); + NSLOG(netsurf, INFO, "Finalised ssl certificate viewer"); return err; } /* Exported interface, documented in sslcert_viewer.h */ -nserror sslcert_viewer_create_session_data(unsigned long num, nsurl *url, - llcache_query_response cb, void *cbpw, - const struct ssl_cert_info *certs, - struct sslcert_session_data **ssl_d) +nserror +sslcert_viewer_create_session_data(unsigned long num, + nsurl *url, + llcache_query_response cb, + void *cbpw, + const struct ssl_cert_info *certs, + struct sslcert_session_data **ssl_d) { struct sslcert_session_data *data; @@ -506,9 +542,11 @@ nserror sslcert_viewer_accept(struct sslcert_session_data *ssl_d) /* Exported interface, documented in sslcert_viewer.h */ -void sslcert_viewer_redraw(struct sslcert_session_data *ssl_d, - int x, int y, struct rect *clip, - const struct redraw_context *ctx) +void +sslcert_viewer_redraw(struct sslcert_session_data *ssl_d, + int x, int y, + struct rect *clip, + const struct redraw_context *ctx) { assert(ssl_d != NULL && "sslcert_viewer_redraw() given bad session data"); @@ -518,8 +556,10 @@ void sslcert_viewer_redraw(struct sslcert_session_data *ssl_d, /* Exported interface, documented in sslcert_viewer.h */ -void sslcert_viewer_mouse_action(struct sslcert_session_data *ssl_d, - browser_mouse_state mouse, int x, int y) +void +sslcert_viewer_mouse_action(struct sslcert_session_data *ssl_d, + browser_mouse_state mouse, + int x, int y) { treeview_mouse_action(ssl_d->tree, mouse, x, y); } diff --git a/desktop/sslcert_viewer.h b/desktop/sslcert_viewer.h index 9b21cd1b6..9a57b965c 100644 --- a/desktop/sslcert_viewer.h +++ b/desktop/sslcert_viewer.h @@ -17,9 +17,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +/** + * \file + * SSL Certificate verification UI interface + */ -#ifndef _NETSURF_DESKTOP_SSLCERT_VIEWER_H_ -#define _NETSURF_DESKTOP_SSLCERT_VIEWER_H_ +#ifndef NETSURF_DESKTOP_SSLCERT_VIEWER_H +#define NETSURF_DESKTOP_SSLCERT_VIEWER_H #include "content/llcache.h" #include "netsurf/mouse.h" @@ -48,6 +52,7 @@ nserror sslcert_viewer_create_session_data( void *cbpw, const struct ssl_cert_info *certs, struct sslcert_session_data **ssl_d); + /** * Initialise a ssl certificate viewer from session data. * @@ -61,6 +66,7 @@ nserror sslcert_viewer_create_session_data( nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, void *core_window_handle, struct sslcert_session_data *ssl_d); + /** * Finalise a ssl certificate viewer. * @@ -72,6 +78,7 @@ nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, */ nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d); + /** * Reject a certificate chain. * @@ -80,6 +87,7 @@ nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d); */ nserror sslcert_viewer_reject(struct sslcert_session_data *ssl_d); + /** * Accept a certificate chain. * @@ -88,6 +96,7 @@ nserror sslcert_viewer_reject(struct sslcert_session_data *ssl_d); */ nserror sslcert_viewer_accept(struct sslcert_session_data *ssl_d); + /** * Redraw the ssl certificate viewer. * @@ -101,6 +110,7 @@ void sslcert_viewer_redraw(struct sslcert_session_data *ssl_d, int x, int y, struct rect *clip, const struct redraw_context *ctx); + /** * Handles all kinds of mouse action * @@ -112,6 +122,7 @@ void sslcert_viewer_redraw(struct sslcert_session_data *ssl_d, void sslcert_viewer_mouse_action(struct sslcert_session_data *ssl_d, browser_mouse_state mouse, int x, int y); + /** * Key press handling. * diff --git a/desktop/system_colour.c b/desktop/system_colour.c index f33b57a37..42a51eaea 100644 --- a/desktop/system_colour.c +++ b/desktop/system_colour.c @@ -16,9 +16,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * System colour handling - * +/** + * \file + * System colour handling implementation. */ #include <string.h> @@ -38,6 +38,7 @@ static lwc_string *colour_list[colour_list_len]; static lwc_string **ns_system_colour_pw = NULL; +/* exported interface documented in desktop/system_colour.h */ nserror ns_system_colour_init(void) { unsigned int ccount; @@ -61,6 +62,8 @@ nserror ns_system_colour_init(void) return NSERROR_OK; } + +/* exported interface documented in desktop/system_colour.h */ void ns_system_colour_finalize(void) { unsigned int ccount; @@ -70,21 +73,25 @@ void ns_system_colour_finalize(void) } } -colour ns_system_colour_char(const char *name) + +/* exported interface documented in desktop/system_colour.h */ +nserror ns_system_colour_char(const char *name, colour *colour_out) { - colour ret = 0; unsigned int ccount; for (ccount = 0; ccount < colour_list_len; ccount++) { if (strcmp(name, nsoptions[ccount + NSOPTION_SYS_COLOUR_START].key + SLEN("sys_colour_")) == 0) { - ret = nsoptions[ccount + NSOPTION_SYS_COLOUR_START].value.c; - break; + *colour_out = nsoptions[ccount + NSOPTION_SYS_COLOUR_START].value.c; + return NSERROR_OK; } } - return ret; + + return NSERROR_INVALID; } + +/* exported interface documented in desktop/system_colour.h */ css_error ns_system_colour(void *pw, lwc_string *name, css_color *colour) { unsigned int ccount; diff --git a/desktop/system_colour.h b/desktop/system_colour.h index 8e82818aa..0b7553003 100644 --- a/desktop/system_colour.h +++ b/desktop/system_colour.h @@ -16,25 +16,59 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file +/** + * \file * Interface to system colour values. + * + * Netsurf has a list of user configurable colours with frontend + * specific defaults. These colours are used for the css system + * colours and to colour and style internally rendered widgets + * (e.g. cookies treeview or local file directory views. */ -#ifndef _NETSURF_DESKTOP_SYSTEM_COLOUR_H_ -#define _NETSURF_DESKTOP_SYSTEM_COLOUR_H_ +#ifndef NETSURF_DESKTOP_SYSTEM_COLOUR_H +#define NETSURF_DESKTOP_SYSTEM_COLOUR_H #include <libcss/libcss.h> #include "utils/errors.h" #include "netsurf/types.h" -/** css callback to obtain named system colours. */ +/** + * css callback to obtain named system colour. + * + * \param[in] pw context unused in implementation + * \param[in] name The name of the colour being looked up + * \param[out] color The system colour associated with the name. + * \return CSS_OK and \a color updated on success else CSS_INVALID if + * the \a name is unrecognised + */ css_error ns_system_colour(void *pw, lwc_string *name, css_color *color); -/** Obtain a named system colour from a frontend. */ -colour ns_system_colour_char(const char *name); +/** + * Obtain a system colour from a name. + * + * \param[in] name The name of the colour being looked up + * \param[out] color The system colour associated with the name in the + * netsurf colour representation. + * \return NSERROR_OK and \a color updated on success else appropriate + * error code. + */ +nserror ns_system_colour_char(const char *name, colour *color); + + +/** + * Initialise the system colours + * + * \return NSERROR_OK on success else appropriate error code. + */ nserror ns_system_colour_init(void); + + +/** + * release any resources associated with the system colours. + */ void ns_system_colour_finalize(void); #endif diff --git a/desktop/textarea.c b/desktop/textarea.c index 203c28e93..5bae27a5c 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -45,9 +45,9 @@ #define TA_ALLOC_STEP 512 static plot_style_t pstyle_stroke_caret = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = CARET_COLOR, - .stroke_width = 1, + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_colour = CARET_COLOR, + .stroke_width = plot_style_int_to_fixed(1), }; struct line_info { @@ -828,7 +828,7 @@ static bool textarea_reflow_singleline(struct textarea *ta, size_t b_off, ta->lines = malloc(LINE_CHUNK_SIZE * sizeof(struct line_info)); if (ta->lines == NULL) { - LOG("malloc failed"); + NSLOG(netsurf, INFO, "malloc failed"); return false; } ta->lines_alloc_size = LINE_CHUNK_SIZE; @@ -852,7 +852,7 @@ static bool textarea_reflow_singleline(struct textarea *ta, size_t b_off, char *temp = realloc(ta->password.data, b_len + TA_ALLOC_STEP); if (temp == NULL) { - LOG("realloc failed"); + NSLOG(netsurf, INFO, "realloc failed"); return false; } @@ -936,7 +936,8 @@ static bool textarea_reflow_multiline(struct textarea *ta, if (ta->lines == NULL) { ta->lines = calloc(sizeof(struct line_info), LINE_CHUNK_SIZE); if (ta->lines == NULL) { - LOG("Failed to allocate memory for textarea lines"); + NSLOG(netsurf, INFO, + "Failed to allocate memory for textarea lines"); return false; } ta->lines_alloc_size = LINE_CHUNK_SIZE; @@ -1053,7 +1054,7 @@ static bool textarea_reflow_multiline(struct textarea *ta, (line + 2 + LINE_CHUNK_SIZE) * sizeof(struct line_info)); if (temp == NULL) { - LOG("realloc failed"); + NSLOG(netsurf, INFO, "realloc failed"); return false; } @@ -1334,7 +1335,7 @@ static bool textarea_insert_text(struct textarea *ta, const char *text, char *temp = realloc(ta->text.data, b_len + ta->text.len + TA_ALLOC_STEP); if (temp == NULL) { - LOG("realloc failed"); + NSLOG(netsurf, INFO, "realloc failed"); return false; } @@ -1484,7 +1485,7 @@ static bool textarea_replace_text_internal(struct textarea *ta, size_t b_start, rep_len + ta->text.len - (b_end - b_start) + TA_ALLOC_STEP); if (temp == NULL) { - LOG("realloc failed"); + NSLOG(netsurf, INFO, "realloc failed"); return false; } @@ -1561,7 +1562,7 @@ static bool textarea_copy_to_undo_buffer(struct textarea *ta, char *temp = realloc(undo->text.data, b_offset + len + TA_ALLOC_STEP); if (temp == NULL) { - LOG("realloc failed"); + NSLOG(netsurf, INFO, "realloc failed"); return false; } @@ -1575,7 +1576,7 @@ static bool textarea_copy_to_undo_buffer(struct textarea *ta, (undo->next_detail + 128) * sizeof(struct textarea_undo_detail)); if (temp == NULL) { - LOG("realloc failed"); + NSLOG(netsurf, INFO, "realloc failed"); return false; } @@ -1802,6 +1803,10 @@ static void textarea_setup_text_offsets(struct textarea *ta) { int text_y_offset, text_y_offset_baseline; + ta->line_height = FIXTOINT(FMUL(FLTTOFIX(1.3), FDIV(FMUL( + nscss_screen_dpi, FDIV(INTTOFIX(ta->fstyle.size), + INTTOFIX(PLOT_STYLE_SCALE))), F_72))); + text_y_offset = text_y_offset_baseline = ta->border_width; if (ta->flags & TEXTAREA_MULTILINE) { /* Multiline textarea */ @@ -1821,6 +1826,27 @@ static void textarea_setup_text_offsets(struct textarea *ta) } +/** + * Set font styles up for a textarea. + * + * \param[in] ta Textarea to update. + * \param[in] fstyle Font style to set in textarea. + * \param[in] selected_text Textarea selected text colour. + * \param[in] selected_bg Textarea selection background colour. + */ +static void textarea_set_text_style( + struct textarea *ta, + const plot_font_style_t *fstyle, + colour selected_text, + colour selected_bg) +{ + ta->fstyle = *fstyle; + + ta->sel_fstyle = *fstyle; + ta->sel_fstyle.foreground = selected_text; + ta->sel_fstyle.background = selected_bg; +} + /* exported interface, documented in textarea.h */ struct textarea *textarea_create(const textarea_flags flags, @@ -1835,13 +1861,13 @@ struct textarea *textarea_create(const textarea_flags flags, flags & TEXTAREA_PASSWORD)); if (callback == NULL) { - LOG("no callback provided"); + NSLOG(netsurf, INFO, "no callback provided"); return NULL; } ret = malloc(sizeof(struct textarea)); if (ret == NULL) { - LOG("malloc failed"); + NSLOG(netsurf, INFO, "malloc failed"); return NULL; } @@ -1860,11 +1886,10 @@ struct textarea *textarea_create(const textarea_flags flags, ret->border_width = setup->border_width; ret->border_col = setup->border_col; - ret->fstyle = setup->text; - - ret->sel_fstyle = setup->text; - ret->sel_fstyle.foreground = setup->selected_text; - ret->sel_fstyle.background = setup->selected_bg; + textarea_set_text_style(ret, + &setup->text, + setup->selected_text, + setup->selected_bg); ret->scroll_x = 0; ret->scroll_y = 0; @@ -1888,7 +1913,7 @@ struct textarea *textarea_create(const textarea_flags flags, ret->text.data = malloc(TA_ALLOC_STEP); if (ret->text.data == NULL) { - LOG("malloc failed"); + NSLOG(netsurf, INFO, "malloc failed"); free(ret); return NULL; } @@ -1900,7 +1925,7 @@ struct textarea *textarea_create(const textarea_flags flags, if (flags & TEXTAREA_PASSWORD) { ret->password.data = malloc(TA_ALLOC_STEP); if (ret->password.data == NULL) { - LOG("malloc failed"); + NSLOG(netsurf, INFO, "malloc failed"); free(ret->text.data); free(ret); return NULL; @@ -1923,7 +1948,7 @@ struct textarea *textarea_create(const textarea_flags flags, ret->line_height = FIXTOINT(FMUL(FLTTOFIX(1.3), FDIV(FMUL( nscss_screen_dpi, FDIV(INTTOFIX(setup->text.size), - INTTOFIX(FONT_SIZE_SCALE))), F_72))); + INTTOFIX(PLOT_STYLE_SCALE))), F_72))); ret->caret_pos.line = ret->caret_pos.byte_off = -1; ret->caret_x = 0; @@ -1975,7 +2000,7 @@ bool textarea_set_text(struct textarea *ta, const char *text) if (len >= ta->text.alloc) { char *temp = realloc(ta->text.data, len + TA_ALLOC_STEP); if (temp == NULL) { - LOG("realloc failed"); + NSLOG(netsurf, INFO, "realloc failed"); return false; } ta->text.data = temp; @@ -2062,7 +2087,7 @@ int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) } if (len < ta->text.len) { - LOG("buffer too small"); + NSLOG(netsurf, INFO, "buffer too small"); return -1; } @@ -2073,6 +2098,17 @@ int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) /* exported interface, documented in textarea.h */ +const char * textarea_data(struct textarea *ta, unsigned int *len) +{ + if (len != NULL) { + *len = ta->text.len; + } + + return ta->text.data; +} + + +/* exported interface, documented in textarea.h */ bool textarea_set_caret(struct textarea *ta, int caret) { int b_off; @@ -2095,13 +2131,13 @@ bool textarea_set_caret(struct textarea *ta, int caret) void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale, const struct rect *clip, const struct redraw_context *ctx) { - const struct plotter_table *plot = ctx->plot; int line0, line1, line, left, right, line_y; int text_y_offset, text_y_offset_baseline; unsigned int b_pos, b_len, b_len_part, b_end; unsigned int sel_start, sel_end; char *line_text; struct rect r, s; + struct rect rect; bool selected = false; plot_font_style_t fstyle; int fsize = ta->fstyle.size; @@ -2162,20 +2198,24 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale, r.y1 = y + ta->vis_height * scale; } - plot->clip(&r); + ctx->plot->clip(ctx, &r); if (ta->border_col != NS_TRANSPARENT && ta->border_width > 0) { /* Plot border */ - plot->rectangle(x, y, x + ta->vis_width, y + ta->vis_height, - &plot_style_fill_bg); + rect.x0 = x; + rect.y0 = y; + rect.x1 = x + ta->vis_width; + rect.y1 = y + ta->vis_height; + ctx->plot->rectangle(ctx, &plot_style_fill_bg, &rect); } if (ta->fstyle.background != NS_TRANSPARENT) { /* Plot background */ plot_style_fill_bg.fill_colour = ta->fstyle.background; - plot->rectangle(x + ta->border_width, y + ta->border_width, - x + ta->vis_width - ta->border_width, - y + ta->vis_height - ta->border_width, - &plot_style_fill_bg); + rect.x0 = x + ta->border_width; + rect.y0 = y + ta->border_width; + rect.x1 = x + ta->vis_width - ta->border_width; + rect.y1 = y + ta->vis_height - ta->border_width; + ctx->plot->rectangle(ctx, &plot_style_fill_bg, &rect); } if (scale == 1.0) { @@ -2223,16 +2263,16 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale, plot_style_fill_bg.fill_colour = ta->sel_fstyle.background; - for (line = line0; (line <= line1) && - (y + line * ta->line_height <= r.y1 + ta->scroll_y); - line++) { + for (line = line0; + (line <= line1) && (y + line * ta->line_height <= r.y1 + ta->scroll_y); + line++) { if (ta->lines[line].b_length == 0) { b_pos++; continue; } /* reset clip rectangle */ - plot->clip(&r); + ctx->plot->clip(ctx, &r); b_len = ta->lines[line].b_length; @@ -2256,12 +2296,12 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale, fstyle = ta->fstyle; fstyle.size = fsize; - plot->text(x + ta->border_width + ta->pad_left - - ta->scroll_x, + ctx->plot->text(ctx, + &fstyle, + x + ta->border_width + ta->pad_left - ta->scroll_x, y + line_y + text_y_offset_baseline, - ta->show->data + - ta->lines[line].b_start, - ta->lines[line].b_length, &fstyle); + ta->show->data + ta->lines[line].b_start, + ta->lines[line].b_length); b_pos += b_len; @@ -2338,24 +2378,24 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale, continue; } - plot->clip(&s); + ctx->plot->clip(ctx, &s); if (selected) { /* draw selection fill */ - plot->rectangle(s.x0, y + line_y + - text_y_offset, - s.x1, y + line_y + line_height + - text_y_offset, - &plot_style_fill_bg); + rect.x0 = s.x0; + rect.y0 = y + line_y + text_y_offset; + rect.x1 = s.x1; + rect.y1 = y + line_y + line_height + text_y_offset; + ctx->plot->rectangle(ctx, &plot_style_fill_bg, &rect); } /* draw text */ - plot->text(x + ta->border_width + ta->pad_left - - ta->scroll_x, + ctx->plot->text(ctx, + &fstyle, + x + ta->border_width + ta->pad_left - ta->scroll_x, y + line_y + text_y_offset_baseline, - ta->show->data + - ta->lines[line].b_start, - ta->lines[line].b_length, &fstyle); + ta->show->data + ta->lines[line].b_start, + ta->lines[line].b_length); b_pos += b_len_part; b_len -= b_len_part; @@ -2376,30 +2416,33 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale, /* No native caret, there is no selection, and caret visible */ int caret_y = y - ta->scroll_y + ta->caret_y; - plot->clip(&r); + ctx->plot->clip(ctx, &r); /* Render our own caret */ - plot->line(x - ta->scroll_x + ta->caret_x, caret_y, - x - ta->scroll_x + ta->caret_x, - caret_y + ta->line_height, - &pstyle_stroke_caret); + rect.x0 = x - ta->scroll_x + ta->caret_x; + rect.y0 = caret_y; + rect.x1 = x - ta->scroll_x + ta->caret_x; + rect.y1 = caret_y + ta->line_height; + ctx->plot->line(ctx, &pstyle_stroke_caret, &rect); } - plot->clip(clip); + ctx->plot->clip(ctx, clip); - if (ta->bar_x != NULL) + if (ta->bar_x != NULL) { scrollbar_redraw(ta->bar_x, x / scale + ta->border_width, y / scale + ta->vis_height - ta->border_width - SCROLLBAR_WIDTH, clip, scale, ctx); + } - if (ta->bar_y != NULL) + if (ta->bar_y != NULL) { scrollbar_redraw(ta->bar_y, x / scale + ta->vis_width - ta->border_width - SCROLLBAR_WIDTH, y / scale + ta->border_width, clip, scale, ctx); + } } @@ -2842,7 +2885,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) return false; } - redraw &= ~textarea_set_caret_internal(ta, caret); + redraw &= !textarea_set_caret_internal(ta, caret); /* TODO: redraw only the bit that changed */ msg.ta = ta; @@ -3201,8 +3244,12 @@ void textarea_set_dimensions(struct textarea *ta, int width, int height) /* exported interface, documented in textarea.h */ -void textarea_set_layout(struct textarea *ta, int width, int height, - int top, int right, int bottom, int left) +void textarea_set_layout( + struct textarea *ta, + const plot_font_style_t *fstyle, + int width, int height, + int top, int right, + int bottom, int left) { struct rect r = {0, 0, 0, 0}; @@ -3213,6 +3260,10 @@ void textarea_set_layout(struct textarea *ta, int width, int height, ta->pad_bottom = bottom + ((ta->bar_x == NULL) ? 0 : SCROLLBAR_WIDTH); ta->pad_left = left; + textarea_set_text_style(ta, fstyle, + ta->sel_fstyle.foreground, + ta->sel_fstyle.background); + textarea_setup_text_offsets(ta); if (ta->flags & TEXTAREA_MULTILINE) { diff --git a/desktop/textarea.h b/desktop/textarea.h index 65e2594c7..82e0de95b 100644 --- a/desktop/textarea.h +++ b/desktop/textarea.h @@ -1,6 +1,6 @@ /* * Copyright 2006 John-Mark Bell <jmb@netsurf-browser.org> - * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -17,12 +17,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * Single/Multi-line UTF-8 text area (interface) +/** + * \file + * Single/Multi-line UTF-8 text area interface */ -#ifndef _NETSURF_DESKTOP_TEXTAREA_H_ -#define _NETSURF_DESKTOP_TEXTAREA_H_ +#ifndef NETSURF_DESKTOP_TEXTAREA_H +#define NETSURF_DESKTOP_TEXTAREA_H #include <stdint.h> #include <stdbool.h> @@ -33,21 +34,31 @@ struct textarea; struct redraw_context; -/* Text area flags */ +/** + * Text area flags + */ typedef enum { - TEXTAREA_DEFAULT = (1 << 0), /**< Standard input */ - TEXTAREA_MULTILINE = (1 << 1), /**< Multiline area */ - TEXTAREA_READONLY = (1 << 2), /**< Non-editable */ - TEXTAREA_INTERNAL_CARET = (1 << 3), /**< Render own caret */ - TEXTAREA_PASSWORD = (1 << 4) /**< Obscured display */ + TEXTAREA_DEFAULT = (1 << 0), /**< Standard input */ + TEXTAREA_MULTILINE = (1 << 1), /**< Multiline area */ + TEXTAREA_READONLY = (1 << 2), /**< Non-editable */ + TEXTAREA_INTERNAL_CARET = (1 << 3), /**< Render own caret */ + TEXTAREA_PASSWORD = (1 << 4) /**< Obscured display */ } textarea_flags; + +/** + * Textarea drag status + */ typedef enum { TEXTAREA_DRAG_NONE, TEXTAREA_DRAG_SCROLLBAR, TEXTAREA_DRAG_SELECTION -} textarea_drag_type; /**< Textarea drag status */ +} textarea_drag_type; + +/** + * textarea message types + */ typedef enum { TEXTAREA_MSG_DRAG_REPORT, /**< Textarea drag start/end report */ TEXTAREA_MSG_SELECTION_REPORT, /**< Textarea text selection presence */ @@ -56,6 +67,10 @@ typedef enum { TEXTAREA_MSG_TEXT_MODIFIED /**< Textarea text modified */ } textarea_msg_type; + +/** + * textarea message + */ struct textarea_msg { struct textarea *ta; /**< The textarea widget */ @@ -73,10 +88,10 @@ struct textarea_msg { TEXTAREA_CARET_HIDE /**< Hide */ } type; struct { - int x; /**< Carret x-coord */ - int y; /**< Carret y-coord */ - int height; /**< Carret height */ - struct rect *clip; /**< Carret clip rect */ + int x; /**< Caret x-coord */ + int y; /**< Caret y-coord */ + int height; /**< Caret height */ + struct rect *clip; /**< Caret clip rect */ } pos; /**< With _CARET_SET_POS */ } caret; /**< With _CARET_UPDATE */ struct { @@ -86,6 +101,10 @@ struct textarea_msg { } data; /**< Depends on msg type */ }; + +/** + * textarea setup parameters + */ typedef struct textarea_setup { int width; /**< Textarea width */ int height; /**< Textarea height */ @@ -104,14 +123,39 @@ typedef struct textarea_setup { } textarea_setup; + +/** + * Text area mouse input status flags + */ +typedef enum { + TEXTAREA_MOUSE_NONE = 0, /**< Not relevant */ + TEXTAREA_MOUSE_USED = (1 << 0), /**< Took action with input */ + TEXTAREA_MOUSE_EDITOR = (1 << 1), /**< Hover: caret pointer */ + TEXTAREA_MOUSE_SELECTION= (1 << 2), /**< Hover: selection */ + TEXTAREA_MOUSE_SCR_USED = (1 << 3), /**< Scrollbar action */ + TEXTAREA_MOUSE_SCR_BOTH = (1 << 4), /**< Scrolling both bars */ + TEXTAREA_MOUSE_SCR_UP = (1 << 5), /**< Hover: scroll up */ + TEXTAREA_MOUSE_SCR_PUP = (1 << 6), /**< Hover: scroll page up */ + TEXTAREA_MOUSE_SCR_VRT = (1 << 7), /**< Hover: vert. drag bar */ + TEXTAREA_MOUSE_SCR_PDWN = (1 << 8), /**< Hover: scroll page down */ + TEXTAREA_MOUSE_SCR_DWN = (1 << 9), /**< Hover: scroll down */ + TEXTAREA_MOUSE_SCR_LFT = (1 << 10), /**< Hover: scroll left */ + TEXTAREA_MOUSE_SCR_PLFT = (1 << 11), /**< Hover: scroll page left */ + TEXTAREA_MOUSE_SCR_HRZ = (1 << 12), /**< Hover: horiz. drag bar */ + TEXTAREA_MOUSE_SCR_PRGT = (1 << 13), /**< Hover: scroll page right */ + TEXTAREA_MOUSE_SCR_RGT = (1 << 14) /**< Hover: scroll right */ +} textarea_mouse_status; + + /** * Client callback for the textarea * - * \param data user data passed at textarea creation - * \param textarea_msg textarea message data + * \param data user data passed at textarea creation + * \param msg textarea message data */ typedef void(*textarea_client_callback)(void *data, struct textarea_msg *msg); + /** * Create a text area. * @@ -125,6 +169,7 @@ struct textarea *textarea_create(const textarea_flags flags, const textarea_setup *setup, textarea_client_callback callback, void *data); + /** * Destroy a text area * @@ -132,6 +177,7 @@ struct textarea *textarea_create(const textarea_flags flags, */ void textarea_destroy(struct textarea *ta); + /** * Set the text in a text area, discarding any current text * @@ -141,6 +187,7 @@ void textarea_destroy(struct textarea *ta); */ bool textarea_set_text(struct textarea *ta, const char *text); + /** * Insert the text in a text area at the caret, replacing any selection. * @@ -152,6 +199,7 @@ bool textarea_set_text(struct textarea *ta, const char *text); bool textarea_drop_text(struct textarea *ta, const char *text, size_t text_length); + /** * Extract the text from a text area * @@ -163,16 +211,28 @@ bool textarea_drop_text(struct textarea *ta, const char *text, */ int textarea_get_text(struct textarea *ta, char *buf, unsigned int len); + +/** + * Access text data in a text area + * + * \param[in] ta Text area + * \param[out] len Returns byte length of returned text, if passed non-NULL. + * \return textarea string data. + */ +const char * textarea_data(struct textarea *ta, unsigned int *len); + + /** * Set the caret's position * - * \param ta Text area - * \param caret 0-based character index to place caret at, -1 removes - * the caret + * \param ta Text area + * \param caret 0-based character index to place caret at, -1 removes + * the caret * \return true on success false otherwise */ bool textarea_set_caret(struct textarea *ta, int caret); + /** * Handle redraw requests for text areas * @@ -187,34 +247,16 @@ bool textarea_set_caret(struct textarea *ta, int caret); void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale, const struct rect *clip, const struct redraw_context *ctx); + /** * Key press handling for text areas. * * \param ta The text area which got the keypress * \param key The ucs4 character codepoint - * \return true if the keypress is dealt with, false otherwise. + * \return true if the keypress is dealt with, false otherwise. */ bool textarea_keypress(struct textarea *ta, uint32_t key); -/* Text area mouse input status flags */ -typedef enum { - TEXTAREA_MOUSE_NONE = 0, /**< Not relevant */ - TEXTAREA_MOUSE_USED = (1 << 0), /**< Took action with input */ - TEXTAREA_MOUSE_EDITOR = (1 << 1), /**< Hover: caret pointer */ - TEXTAREA_MOUSE_SELECTION= (1 << 2), /**< Hover: selection */ - TEXTAREA_MOUSE_SCR_USED = (1 << 3), /**< Scrollbar action */ - TEXTAREA_MOUSE_SCR_BOTH = (1 << 4), /**< Scrolling both bars */ - TEXTAREA_MOUSE_SCR_UP = (1 << 5), /**< Hover: scroll up */ - TEXTAREA_MOUSE_SCR_PUP = (1 << 6), /**< Hover: scroll page up */ - TEXTAREA_MOUSE_SCR_VRT = (1 << 7), /**< Hover: vert. drag bar */ - TEXTAREA_MOUSE_SCR_PDWN = (1 << 8), /**< Hover: scroll page down */ - TEXTAREA_MOUSE_SCR_DWN = (1 << 9), /**< Hover: scroll down */ - TEXTAREA_MOUSE_SCR_LFT = (1 << 10), /**< Hover: scroll left */ - TEXTAREA_MOUSE_SCR_PLFT = (1 << 11), /**< Hover: scroll page left */ - TEXTAREA_MOUSE_SCR_HRZ = (1 << 12), /**< Hover: horiz. drag bar */ - TEXTAREA_MOUSE_SCR_PRGT = (1 << 13), /**< Hover: scroll page right */ - TEXTAREA_MOUSE_SCR_RGT = (1 << 14) /**< Hover: scroll right */ -} textarea_mouse_status; /** * Handles all kinds of mouse action @@ -228,22 +270,28 @@ typedef enum { textarea_mouse_status textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, int x, int y); + /** * Clear any selection in the textarea. * - * \param ta textarea widget + * \param ta textarea widget * \return true if there was a selection to clear, false otherwise */ bool textarea_clear_selection(struct textarea *ta); + /** - * Get selected text, ownership passed to caller, which needs to free() it. + * Get selected text. * - * \param ta Textarea widget + * ownership of the returned string is passed to caller which needs to + * free it. + * + * \param ta Textarea widget * \return Selected text, or NULL if none. */ char *textarea_get_selection(struct textarea *ta); + /** * Gets the dimensions of a textarea * @@ -253,34 +301,47 @@ char *textarea_get_selection(struct textarea *ta); */ void textarea_get_dimensions(struct textarea *ta, int *width, int *height); + /** - * Set the dimensions of a textarea, causing a reflow and - * Does not emit a redraw request. Up to client to call textarea_redraw. + * Set the dimensions of a textarea. + * + * This causes a reflow of the text and does not emit a redraw + * request. Up to client to call textarea_redraw. * * \param ta textarea widget - * \param width the new width of the textarea + * \param width the new width of the textarea * \param height the new height of the textarea */ void textarea_set_dimensions(struct textarea *ta, int width, int height); + /** - * Set the dimensions and padding of a textarea, causing a reflow. - * Does not emit a redraw request. Up to client to call textarea_redraw. + * Set the dimensions and padding of a textarea. + * + * This causes a reflow of the text. Does not emit a redraw request. + * Up to client to call textarea_redraw. * * \param ta textarea widget - * \param width the new width of the textarea + * \param width the new width of the textarea * \param height the new height of the textarea * \param top the new top padding of the textarea * \param right the new right padding of the textarea * \param bottom the new bottom padding of the textarea * \param left the new left padding of the textarea */ -void textarea_set_layout(struct textarea *ta, int width, int height, - int top, int right, int bottom, int left); +void textarea_set_layout( + struct textarea *ta, + const plot_font_style_t *fstyle, + int width, int height, + int top, int right, + int bottom, int left); + /** - * Scroll a textarea by an amount. Only does anything if multi-line textarea - * has scrollbars. If it scrolls, it will emit a redraw request. + * Scroll a textarea by an amount. + * + * Only does anything if multi-line textarea has scrollbars. If it + * scrolls, it will emit a redraw request. * * \param ta textarea widget * \param scrx number of px try to scroll in x direction @@ -288,5 +349,5 @@ void textarea_set_layout(struct textarea *ta, int width, int height, * \return true iff the textarea was scrolled */ bool textarea_scroll(struct textarea *ta, int scrx, int scry); -#endif +#endif diff --git a/desktop/textinput.c b/desktop/textinput.c index c0e0ba8f7..7fc95f792 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -37,9 +37,9 @@ #include "netsurf/form.h" #include "netsurf/window.h" #include "netsurf/keypress.h" -#include "render/box.h" -#include "render/html_internal.h" -#include "render/layout.h" +#include "html/box.h" +#include "html/html_internal.h" +#include "html/layout.h" #include "desktop/browser_private.h" #include "desktop/textinput.h" diff --git a/desktop/textinput.h b/desktop/textinput.h index 5859ea8d6..7c6be9547 100644 --- a/desktop/textinput.h +++ b/desktop/textinput.h @@ -24,19 +24,19 @@ * Textual input handling interface */ -#ifndef _NETSURF_DESKTOP_TEXTINPUT_H_ -#define _NETSURF_DESKTOP_TEXTINPUT_H_ +#ifndef NETSURF_DESKTOP_TEXTINPUT_H +#define NETSURF_DESKTOP_TEXTINPUT_H struct browser_window; /** * Position the caret and assign a callback for key presses. * - * \param bw The browser window in which to place the caret - * \param x X coordinate of the caret - * \param y Y coordinate - * \param height Height of caret - * \param clip Clip rectangle for caret, or NULL if none + * \param bw The browser window in which to place the caret + * \param x X coordinate of the caret + * \param y Y coordinate + * \param height Height of caret + * \param clip Clip rectangle for caret, or NULL if none */ void browser_window_place_caret(struct browser_window *bw, int x, int y, int height, const struct rect *clip); diff --git a/desktop/treeview.c b/desktop/treeview.c index 4d8fbaaeb..48422e8e3 100644 --- a/desktop/treeview.c +++ b/desktop/treeview.c @@ -22,10 +22,13 @@ * Treeview handling implementation. */ +#include <string.h> + #include "utils/utils.h" #include "utils/log.h" #include "utils/nsurl.h" #include "utils/nsoption.h" +#include "utils/config.h" #include "netsurf/bitmap.h" #include "netsurf/content.h" #include "netsurf/plotters.h" @@ -42,9 +45,17 @@ #include "desktop/treeview.h" #include "desktop/gui_internal.h" -/** @todo get rid of REDRAW_MAX -- need to be able to know window size */ +/** + * The maximum horizontal size a treeview can possibly be. + * + * \todo get rid of REDRAW_MAX -- need to be able to know window size + */ #define REDRAW_MAX 8000 + +/** + * Treeview handling global context + */ struct treeview_globals { unsigned initialised; int line_height; @@ -56,32 +67,54 @@ struct treeview_globals { int move_offset; } tree_g; + +/** + * Section type of a treeview at a point + */ enum treeview_node_part { - TV_NODE_PART_TOGGLE, /**< Expansion toggle */ - TV_NODE_PART_ON_NODE, /**< Node content (text, icon) */ - TV_NODE_PART_NONE /**< Empty area */ -}; /**< Section type of a treeview at a point */ + TV_NODE_PART_TOGGLE, /**< Expansion toggle */ + TV_NODE_PART_ON_NODE, /**< Node content (text, icon) */ + TV_NODE_PART_NONE /**< Empty area */ +}; + +/** + * Text within a treeview field or node + */ struct treeview_text { const char *data; /**< Text string */ uint32_t len; /**< Length of string in bytes */ int width; /**< Width of text in px */ }; + +/** + * a treeview field + */ struct treeview_field { + /** flags controlling how field is interpreted */ enum treeview_field_flags flags; - lwc_string *field; - struct treeview_text value; + lwc_string *field; /**< field contents */ + struct treeview_text value; /**< field text */ }; + +/** + * flags indicating render state of node. + */ enum treeview_node_flags { - TV_NFLAGS_NONE = 0, /**< No node flags set */ - TV_NFLAGS_EXPANDED = (1 << 0), /**< Whether node is expanded */ - TV_NFLAGS_SELECTED = (1 << 1), /**< Whether node is selected */ - TV_NFLAGS_SPECIAL = (1 << 2) /**< Render as special node */ + TV_NFLAGS_NONE = 0, /**< No node flags set */ + TV_NFLAGS_EXPANDED = (1 << 0), /**< Whether node is expanded */ + TV_NFLAGS_SELECTED = (1 << 1), /**< Whether node is selected */ + TV_NFLAGS_SPECIAL = (1 << 2), /**< Render as special node */ + TV_NFLAGS_MATCHED = (1 << 3), /**< Whether node matches search */ }; + +/** + * Treeview target position + */ enum treeview_target_pos { TV_TARGET_ABOVE, TV_TARGET_INSIDE, @@ -89,6 +122,10 @@ enum treeview_target_pos { TV_TARGET_NONE }; + +/** + * Treeview node + */ struct treeview_node { enum treeview_node_flags flags; /**< Node flags */ enum treeview_node_type type; /**< Node type */ @@ -96,49 +133,73 @@ struct treeview_node { int height; /**< Includes height of any descendants (pixels) */ int inset; /**< Node's inset depending on tree depth (pixels) */ - treeview_node *parent; - treeview_node *prev_sib; - treeview_node *next_sib; - treeview_node *children; + treeview_node *parent; /**< parent node */ + treeview_node *prev_sib; /**< previous sibling node */ + treeview_node *next_sib; /**< next sibling node */ + treeview_node *children; /**< first child node */ void *client_data; /**< Passed to client on node event msg callback */ struct treeview_text text; /** Text to show for node (default field) */ -}; /**< Treeview node */ +}; + +/** + * Node entry + * + * node entry contains a base node at the beginning allowing for + * trivial containerof by cast and some number of fields. + */ struct treeview_node_entry { - treeview_node base; + treeview_node base; /**< Entry class inherits node base class */ struct treeview_field fields[FLEX_ARRAY_LEN_DECL]; -}; /**< Entry class inherits node base class */ +}; + +/** + * A mouse position wrt treeview + */ struct treeview_pos { int x; /**< Mouse X coordinate */ int y; /**< Mouse Y coordinate */ int node_y; /**< Top of node at y */ int node_h; /**< Height of node at y */ -}; /**< A mouse position wrt treeview */ +}; + +/** + * Treeview drag state + */ struct treeview_drag { enum { TV_DRAG_NONE, TV_DRAG_SELECTION, TV_DRAG_MOVE, - TV_DRAG_TEXTAREA + TV_DRAG_TEXTAREA, + TV_DRAG_SEARCH, } type; /**< Drag type */ treeview_node *start_node; /**< Start node */ bool selected; /**< Start node is selected */ enum treeview_node_part part; /**< Node part at start */ struct treeview_pos start; /**< Start pos */ struct treeview_pos prev; /**< Previous pos */ -}; /**< Drag state */ +}; + +/** + * Treeview node move details + */ struct treeview_move { treeview_node *root; /**< Head of yanked node list */ treeview_node *target; /**< Move target */ struct rect target_area; /**< Pos/size of target indicator */ enum treeview_target_pos target_pos; /**< Pos wrt render node */ -}; /**< Move details */ +}; + +/** + * Treeview node edit details + */ struct treeview_edit { treeview_node *node; /**< Node being edited, or NULL */ struct textarea *textarea; /**< Textarea for edit, or NULL */ @@ -147,22 +208,39 @@ struct treeview_edit { int y; /**< Textarea y position */ int w; /**< Textarea width */ int h; /**< Textarea height */ -}; /**< Edit details */ +}; + +/** + * Treeview search box details + */ +struct treeview_search { + struct textarea *textarea; /**< Search box. */ + bool active; /**< Whether the search box has focus. */ + bool search; /**< Whether we have a search term. */ + int height; /**< Current search display height. */ +}; + + +/** + * The treeview context + */ struct treeview { - uint32_t view_width; /**< Viewport size */ + uint32_t view_width; /**< Viewport horizontal size */ - treeview_flags flags; /**< Treeview behaviour settings */ + treeview_flags flags; /**< Treeview behaviour settings */ - treeview_node *root; /**< Root node */ + treeview_node *root; /**< Root node */ struct treeview_field *fields; /**< Array of fields */ int n_fields; /**< fields[n_fields] is folder, lower are entry fields */ int field_width; /**< Max width of shown field names */ - struct treeview_drag drag; /**< Drag state */ - struct treeview_move move; /**< Move drag details */ - struct treeview_edit edit; /**< Edit details */ + struct treeview_drag drag; /**< Drag state */ + struct treeview_move move; /**< Move drag details */ + struct treeview_edit edit; /**< Edit details */ + + struct treeview_search search; /**< Treeview search box */ const struct treeview_callback_table *callbacks; /**< For node events */ @@ -171,38 +249,62 @@ struct treeview { }; +/** + * Treeview furniture states. + */ enum treeview_furniture_id { TREE_FURN_EXPAND = 0, TREE_FURN_CONTRACT, TREE_FURN_LAST }; -struct treeview_furniture { - int size; - struct bitmap *bmp; - struct bitmap *sel; -}; + +/** + * style for a node + */ struct treeview_node_style { - plot_style_t bg; /**< Background */ - plot_font_style_t text; /**< Text */ - plot_font_style_t itext; /**< Entry field text */ + plot_style_t bg; /**< Background */ + plot_font_style_t text; /**< Text */ + plot_font_style_t itext; /**< Entry field text */ + + plot_style_t sbg; /**< Selected background */ + plot_font_style_t stext; /**< Selected text */ + plot_font_style_t sitext; /**< Selected entry field text */ + + struct { + int size; + struct bitmap *bmp; + struct bitmap *sel; + } furn[TREE_FURN_LAST]; +}; - plot_style_t sbg; /**< Selected background */ - plot_font_style_t stext; /**< Selected text */ - plot_font_style_t sitext; /**< Selected entry field text */ - struct treeview_furniture furn[TREE_FURN_LAST]; -}; +/** + * Plot style for odd rows + */ +struct treeview_node_style plot_style_odd; + + +/** + * Plot style for even rows + */ +struct treeview_node_style plot_style_even; -struct treeview_node_style plot_style_odd; /**< Plot style for odd rows */ -struct treeview_node_style plot_style_even; /**< Plot style for even rows */ +/** + * Treeview content resource data + */ struct treeview_resource { const char *url; struct hlcache_handle *c; int height; bool ready; -}; /**< Treeview content resource data */ +}; + + +/** + * treeview resource indexes + */ enum treeview_resource_id { TREE_RES_ARROW = 0, TREE_RES_CONTENT, @@ -211,27 +313,46 @@ enum treeview_resource_id { TREE_RES_SEARCH, TREE_RES_LAST }; + + +/** + * Treeview content resources + */ static struct treeview_resource treeview_res[TREE_RES_LAST] = { { "resource:icons/arrow-l.png", NULL, 0, false }, { "resource:icons/content.png", NULL, 0, false }, { "resource:icons/directory.png", NULL, 0, false }, { "resource:icons/directory2.png", NULL, 0, false }, { "resource:icons/search.png", NULL, 0, false } -}; /**< Treeview content resources */ +}; + + +/** + * Get the display height of the treeview data component of the display. + * + * \param[in] tree Treeview to get the height of. + * \return the display height in pixels. + */ +static inline int treeview__get_display_height(const treeview *tree) +{ + return (tree->search.search == false) ? + tree->root->height : + tree->search.height; +} /** * Corewindow callback wrapper: Request a redraw of the window * - * \param[in] cw the core window object + * \param[in] tree The treeview to request redraw on. * \param[in] r rectangle to redraw */ -static inline void treeview__cw_redraw_request( +static inline void treeview__cw_invalidate_area( const struct treeview *tree, const struct rect *r) { if (tree->cw_t != NULL) { - tree->cw_t->redraw_request(tree->cw_h, r); + tree->cw_t->invalidate(tree->cw_h, r); } } @@ -239,16 +360,41 @@ static inline void treeview__cw_redraw_request( /** * Corewindow callback wrapper: Update the limits of the window * - * \param[in] cw the core window object + * \param[in] tree The treeview to update size for. * \param[in] width the width in px, or negative if don't care * \param[in] height the height in px, or negative if don't care */ static inline void treeview__cw_update_size( - const struct treeview *tree, - int width, int height) + const struct treeview *tree, + int width, int height) { + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + if (tree->cw_t != NULL) { - tree->cw_t->update_size(tree->cw_h, width, height); + tree->cw_t->update_size(tree->cw_h, width, + height + search_height); + } +} + + +/** + * Corewindow callback_wrapper: Scroll to top of window. + * + * \param[in] tree The treeview to scroll. + */ +static inline void treeview__cw_scroll_top( + const struct treeview *tree) +{ + struct rect r = { + .x0 = 0, + .y0 = 0, + .x1 = tree_g.window_padding, + .y1 = tree_g.line_height, + }; + + if (tree->cw_t != NULL) { + tree->cw_t->scroll_visible(tree->cw_h, &r); } } @@ -256,13 +402,13 @@ static inline void treeview__cw_update_size( /** * Corewindow callback wrapper: Get window viewport dimensions * - * \param[in] cw the core window object + * \param[in] tree The treeview to get dimensions for. * \param[out] width to be set to viewport width in px * \param[out] height to be set to viewport height in px */ static inline void treeview__cw_get_window_dimensions( - const struct treeview *tree, - int *width, int *height) + const struct treeview *tree, + int *width, int *height) { if (tree->cw_t != NULL) { tree->cw_t->get_window_dimensions(tree->cw_h, width, height); @@ -273,12 +419,12 @@ static inline void treeview__cw_get_window_dimensions( /** * Corewindow callback wrapper: Inform corewindow owner of drag status * - * \param[in] cw the core window object + * \param[in] tree The treeview to report status on. * \param[in] ds the current drag status */ static inline void treeview__cw_drag_status( - const struct treeview *tree, - core_window_drag_status ds) + const struct treeview *tree, + core_window_drag_status ds) { if (tree->cw_t != NULL) { tree->cw_t->drag_status(tree->cw_h, ds); @@ -286,15 +432,16 @@ static inline void treeview__cw_drag_status( } -/* Helper function to access the given field of a node +/** + * Helper function to access the given field of a node * - * \param tree Treeview that node belongs to - * \param n Node to get field from - * \param i Index of field of interest + * \param tree Treeview that node belongs to + * \param n Node to get field from + * \param i Index of field of interest * \return text entry for field or NULL. */ -static inline struct treeview_text * treeview_get_text_for_field( - treeview *tree, treeview_node *n, int i) +static inline struct treeview_text * +treeview_get_text_for_field(treeview *tree, treeview_node *n, int i) { if (i == 0) { return &n->text; @@ -309,19 +456,19 @@ static inline struct treeview_text * treeview_get_text_for_field( } -/* Find the next node in depth first tree order +/** + * Find the next node in depth first tree order * - * \param node Start node - * \param full Iff true, visit children of collapsed nodes - * \param next Updated to next node, or NULL if 'node' is last node - * \return NSERROR_OK on success, or appropriate error otherwise + * \param node Start node + * \param full Iff true, visit children of collapsed nodes + * \return next node, or NULL if \a node is last node */ static inline treeview_node * treeview_node_next(treeview_node *node, bool full) { assert(node != NULL); if ((full || (node->flags & TV_NFLAGS_EXPANDED)) && - node->children != NULL) { + node->children != NULL) { /* Next node is child */ node = node->children; } else { @@ -345,17 +492,17 @@ static inline treeview_node * treeview_node_next(treeview_node *node, bool full) } -/* Find node at given y-position +/** + * Find node at given y-position * - * \param tree Treeview object to delete node from - * \param target_y Target y-position + * \param tree Treeview object to delete node from + * \param target_y Target y-position * \return node at y_target */ static treeview_node * treeview_y_node(treeview *tree, int target_y) { + int y = (tree->flags & TREEVIEW_SEARCHABLE) ? tree_g.line_height : 0; treeview_node *n; - int y = 0; - int h; assert(tree != NULL); assert(tree->root != NULL); @@ -363,8 +510,8 @@ static treeview_node * treeview_y_node(treeview *tree, int target_y) n = treeview_node_next(tree->root, false); while (n != NULL) { - h = (n->type == TREE_NODE_ENTRY) ? - n->height : tree_g.line_height; + int h = (n->type == TREE_NODE_ENTRY) ? + n->height : tree_g.line_height; if (target_y >= y && target_y < y + h) return n; y += h; @@ -376,16 +523,19 @@ static treeview_node * treeview_y_node(treeview *tree, int target_y) } -/* Find y position of the top of a node +/** + * Find y position of the top of a node * - * \param tree Treeview object to delete node from - * \param node Node to get position of + * \param tree Treeview object to delete node from + * \param node Node to get position of * \return node's y position */ -static int treeview_node_y(treeview *tree, treeview_node *node) +static int treeview_node_y( + const treeview *tree, + const treeview_node *node) { treeview_node *n; - int y = 0; + int y = (tree->flags & TREEVIEW_SEARCHABLE) ? tree_g.line_height : 0; assert(tree != NULL); assert(tree->root != NULL); @@ -394,7 +544,7 @@ static int treeview_node_y(treeview *tree, treeview_node *node) while (n != NULL && n != node) { y += (n->type == TREE_NODE_ENTRY) ? - n->height : tree_g.line_height; + n->height : tree_g.line_height; n = treeview_node_next(n, false); } @@ -403,40 +553,108 @@ static int treeview_node_y(treeview *tree, treeview_node *node) } -/* Walk a treeview subtree, calling a callback at each node (depth first) +/** + * Redraw tree from given node to the bottom. + * + * \param[in] tree Tree to redraw from node in. + * \param[in] node Node to redraw from. + */ +static void treeview__redraw_from_node( + const treeview *tree, + const treeview_node *node) +{ + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + struct rect r = { + .x0 = 0, + .y0 = treeview_node_y(tree, node), + .x1 = REDRAW_MAX, + .y1 = treeview__get_display_height(tree) + search_height, + }; + + assert(tree != NULL); + + treeview__cw_invalidate_area(tree, &r); +} + + +/** + * The treeview walk mode. Controls which nodes are visited in a walk. + */ +enum treeview_walk_mode { + /** + * Walk to all nodes in the (sub)tree. + */ + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, + + /** + * Walk to expanded nodes in the (sub)tree only. Children of + * collapsed nodes are not visited. + */ + TREEVIEW_WALK_MODE_LOGICAL_EXPANDED, + + /** + * Walk displayed nodes. This differs from the + * `TREEVIEW_WALK_MODE_LOGICAL_EXPANDED` mode when there is + * an active search filter display. + */ + TREEVIEW_WALK_MODE_DISPLAY, +}; + + +/** + * Walk a treeview subtree, calling a callback at each node (depth first) * + * \param tree Treeview being walked. * \param root Root to walk tree from (doesn't get a callback call) - * \param full Iff true, visit children of collapsed nodes + * \param mode The treeview walk mode to use. * \param callback_bwd Function to call on each node in backwards order * \param callback_fwd Function to call on each node in forwards order * \param ctx Context to pass to callback * \return NSERROR_OK on success, or appropriate error otherwise * - * Note: Any node deletion must happen in callback_bwd. + * \note Any node deletion must happen in callback_bwd. */ -static nserror treeview_walk_internal(treeview_node *root, bool full, - nserror (*callback_bwd)(treeview_node *n, void *ctx, bool *end), - nserror (*callback_fwd)(treeview_node *n, void *ctx, - bool *skip_children, bool *end), +static nserror treeview_walk_internal( + treeview *tree, + treeview_node *root, + enum treeview_walk_mode mode, + nserror (*callback_bwd)( + treeview_node *n, + void *ctx, + bool *end), + nserror (*callback_fwd)( + treeview_node *n, + void *ctx, + bool *skip_children, + bool *end), void *ctx) { treeview_node *node, *child, *parent, *next_sibling; - bool abort = false; + bool walking_search = (mode == TREEVIEW_WALK_MODE_DISPLAY && + tree->search.search == true); bool skip_children = false; + bool abort = false; + bool full = false; nserror err; + bool entry; assert(root != NULL); + if (mode == TREEVIEW_WALK_MODE_LOGICAL_COMPLETE || walking_search) { + /* We need to visit children of collapsed folders. */ + full = true; + } + node = root; parent = node->parent; next_sibling = node->next_sib; - child = (!skip_children && - (full || (node->flags & TV_NFLAGS_EXPANDED))) ? - node->children : NULL; + child = (full || (node->flags & TV_NFLAGS_EXPANDED)) ? + node->children : NULL; while (node != NULL) { - if (child != NULL) { + if (child != NULL && !skip_children) { /* Down to children */ node = child; } else { @@ -444,9 +662,10 @@ static nserror treeview_walk_internal(treeview_node *root, bool full, * go to next sibling if present, or nearest ancestor * with a next sibling. */ - while (node != root && - next_sibling == NULL) { - if (callback_bwd != NULL) { + while (node != root && next_sibling == NULL) { + entry = (node->type == TREE_NODE_ENTRY); + if (callback_bwd != NULL && + (entry || !walking_search)) { /* Backwards callback */ err = callback_bwd(node, ctx, &abort); @@ -486,10 +705,17 @@ static nserror treeview_walk_internal(treeview_node *root, bool full, assert(node != NULL); assert(node != root); + entry = (node->type == TREE_NODE_ENTRY); + parent = node->parent; next_sibling = node->next_sib; child = (full || (node->flags & TV_NFLAGS_EXPANDED)) ? - node->children : NULL; + node->children : NULL; + + if (walking_search && (!entry || + !(node->flags & TV_NFLAGS_MATCHED))) { + continue; + } if (callback_fwd != NULL) { /* Forwards callback */ @@ -503,16 +729,251 @@ static nserror treeview_walk_internal(treeview_node *root, bool full, return NSERROR_OK; } } - child = skip_children ? NULL : child; } return NSERROR_OK; } /** + * Data used when doing a treeview walk for search. + */ +struct treeview_search_walk_data { + treeview *tree; /**< The treeview to search. */ + const char *text; /**< The string being searched for. */ + const unsigned int len; /**< Length of string being searched for. */ + int window_height; /**< Accumulate height for matching entries. */ +}; + + +/** + * Treewalk node callback for handling search. + * + * \param[in] n Current node. + * \param[in] ctx Treeview search context. + * \param[in,out] skip_children Flag to allow children to be skipped. + * \param[in,out] end Flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror treeview__search_walk_cb( + treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) +{ + struct treeview_search_walk_data *sw = ctx; + + if (n->type != TREE_NODE_ENTRY) { + return NSERROR_OK; + } + + if (sw->len == 0) { + n->flags &= ~TV_NFLAGS_MATCHED; + } else { + struct treeview_node_entry *entry = + (struct treeview_node_entry *)n; + bool matched = false; + + for (int i = 0; i < sw->tree->n_fields; i++) { + struct treeview_field *ef = &(sw->tree->fields[i + 1]); + if (ef->flags & TREE_FLAG_SEARCHABLE) { + if (strcasestr(entry->fields[i].value.data, + sw->text) != NULL) { + matched = true; + break; + } + } + } + + if (!matched && strcasestr(n->text.data, sw->text) != NULL) { + matched = true; + } + + if (matched) { + n->flags |= TV_NFLAGS_MATCHED; + sw->window_height += n->height; + } else { + n->flags &= ~TV_NFLAGS_MATCHED; + } + } + + return NSERROR_OK; +} + + +/** + * Search treeview for text. + * + * \param[in] tree Treeview to search. + * \param[in] text UTF-8 string to search for. (NULL-terminated.) + * \param[in] len Byte length of UTF-8 string. + * \return NSERROR_OK on success, appropriate error otherwise. + */ +static nserror treeview__search( + treeview *tree, + const char *text, + unsigned int len) +{ + nserror err; + uint32_t height; + uint32_t prev_height = treeview__get_display_height(tree); + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + struct treeview_search_walk_data sw = { + .len = len, + .text = text, + .tree = tree, + .window_height = 0, + }; + struct rect r = { + .x0 = 0, + .y0 = search_height, + .x1 = REDRAW_MAX, + }; + + assert(text[len] == '\0'); + + if (tree->root == NULL) { + return NSERROR_OK; + } + + err = treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, NULL, + treeview__search_walk_cb, &sw); + if (err != NSERROR_OK) { + return err; + } + + if (len > 0) { + tree->search.height = sw.window_height; + tree->search.search = true; + height = sw.window_height; + } else { + tree->search.search = false; + height = tree->root->height; + } + + r.y1 = ((height > prev_height) ? height : prev_height) + search_height; + treeview__cw_invalidate_area(tree, &r); + treeview__cw_update_size(tree, -1, height); + treeview__cw_scroll_top(tree); + + return NSERROR_OK; +} + + +/** + * Cancel a treeview search, optionally droping focus from search widget. + * + * \param[in] tree Treeview to cancel search in. + * \param[in] drop_focus Iff true, drop input focus from search widget. + */ +static void treeview__search_cancel(treeview *tree, bool drop_focus) +{ + struct rect r = { + .x0 = tree_g.window_padding + tree_g.icon_size, + .x1 = 600, + .y0 = 0, + .y1 = tree_g.line_height, + }; + + tree->search.search = false; + if (tree->search.active == false) { + return; + } + + if (drop_focus) { + tree->search.active = false; + textarea_set_caret(tree->search.textarea, -1); + } else { + textarea_set_caret(tree->search.textarea, 0); + } + + textarea_set_text(tree->search.textarea, ""); + treeview__cw_invalidate_area(tree, &r); +} + + +/** + * Callback for textarea_create, in desktop/treeview.h + * + * \param data treeview context + * \param msg textarea message + */ +static void treeview_textarea_search_callback(void *data, + struct textarea_msg *msg) +{ + treeview *tree = data; + struct rect *r; + + if (tree->search.active == false || tree->root == NULL) { + return; + } + + switch (msg->type) { + case TEXTAREA_MSG_DRAG_REPORT: + if (msg->data.drag == TEXTAREA_DRAG_NONE) { + /* Textarea drag finished */ + tree->drag.type = TV_DRAG_NONE; + } else { + /* Textarea drag started */ + tree->drag.type = TV_DRAG_SEARCH; + } + treeview__cw_drag_status(tree, tree->drag.type); + break; + + case TEXTAREA_MSG_REDRAW_REQUEST: + r = &msg->data.redraw; + r->x0 += tree_g.window_padding + tree_g.icon_size; + r->y0 += 0; + r->x1 += 600; + r->y1 += tree_g.line_height; + + /* Redraw the textarea */ + treeview__cw_invalidate_area(tree, r); + break; + + case TEXTAREA_MSG_TEXT_MODIFIED: + /* Textarea length includes trailing NULL, so subtract it. */ + treeview__search(tree, + msg->data.modified.text, + msg->data.modified.len - 1); + break; + + default: + break; + } +} + + +/** + * Update the layout for any active search. + * + * \param[in] tree The tree to update. + */ +static void treeview__search_update_display( + treeview *tree) +{ + const char *string; + unsigned int len; + + if (tree->search.search == false) { + /* No active search to update view for. */ + return; + } + + string = textarea_data(tree->search.textarea, &len); + if (string == NULL || len == 0) { + return; + } + + treeview__search(tree, string, len - 1); +} + + +/** * Create treeview's root node * - * \param root Returns root node + * \param[out] root Returns root node * \return NSERROR_OK on success, appropriate error otherwise */ static nserror treeview_create_node_root(treeview_node **root) @@ -548,10 +1009,20 @@ static nserror treeview_create_node_root(treeview_node **root) /** - * Set a node's inset from its parent (can be used as treeview walk callback) + * Set a node's inset from its parent + * + * This may be used as treeview walk callback + * + * \param[in] n node to set inset on + * \param[in] ctx context unused + * \param[out] skip_children set to false so child nodes are not skipped. + * \param[out] end unused flag so treewalk in not terminated early. */ -static nserror treeview_set_inset_from_parent(treeview_node *n, void *ctx, - bool *skip_children, bool *end) +static nserror +treeview_set_inset_from_parent(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { if (n->parent != NULL) n->inset = n->parent->inset + tree_g.step_width; @@ -559,14 +1030,20 @@ static nserror treeview_set_inset_from_parent(treeview_node *n, void *ctx, *skip_children = false; return NSERROR_OK; } + + /** * Insert a treeview node into a treeview * - * \param a parentless node to insert - * \param b tree node to insert a as a relation of - * \param rel a's relationship to b + * \param tree the treeview to insert node into. + * \param a parentless node to insert + * \param b tree node to insert a as a relation of + * \param rel The relationship between \a a and \a b */ -static inline void treeview_insert_node(treeview_node *a, +static inline void +treeview_insert_node( + treeview *tree, + treeview_node *a, treeview_node *b, enum treeview_relationship rel) { @@ -603,7 +1080,8 @@ static inline void treeview_insert_node(treeview_node *a, a->inset = a->parent->inset + tree_g.step_width; if (a->children != NULL) { - treeview_walk_internal(a, true, NULL, + treeview_walk_internal(tree, a, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, NULL, treeview_set_inset_from_parent, NULL); } @@ -627,12 +1105,14 @@ static inline void treeview_insert_node(treeview_node *a, /* Exported interface, documented in treeview.h */ -nserror treeview_create_node_folder(treeview *tree, - treeview_node **folder, - treeview_node *relation, - enum treeview_relationship rel, - const struct treeview_field_data *field, - void *data, treeview_node_options_flags flags) +nserror +treeview_create_node_folder(treeview *tree, + treeview_node **folder, + treeview_node *relation, + enum treeview_relationship rel, + const struct treeview_field_data *field, + void *data, + treeview_node_options_flags flags) { treeview_node *n; @@ -651,7 +1131,7 @@ nserror treeview_create_node_folder(treeview *tree, } n->flags = (flags & TREE_OPTION_SPECIAL_DIR) ? - TV_NFLAGS_SPECIAL : TV_NFLAGS_NONE; + TV_NFLAGS_SPECIAL : TV_NFLAGS_NONE; n->type = TREE_NODE_FOLDER; n->height = tree_g.line_height; @@ -667,13 +1147,13 @@ nserror treeview_create_node_folder(treeview *tree, n->client_data = data; - treeview_insert_node(n, relation, rel); + treeview_insert_node(tree, n, relation, rel); if (n->parent->flags & TV_NFLAGS_EXPANDED) { /* Inform front end of change in dimensions */ if (!(flags & TREE_OPTION_SUPPRESS_RESIZE)) treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); /* Redraw */ if (!(flags & TREE_OPTION_SUPPRESS_REDRAW)) { @@ -682,7 +1162,7 @@ nserror treeview_create_node_folder(treeview *tree, r.y0 = treeview_node_y(tree, n); r.x1 = REDRAW_MAX; r.y1 = tree->root->height; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } } @@ -693,10 +1173,11 @@ nserror treeview_create_node_folder(treeview *tree, /* Exported interface, documented in treeview.h */ -nserror treeview_update_node_folder(treeview *tree, - treeview_node *folder, - const struct treeview_field_data *field, - void *data) +nserror +treeview_update_node_folder(treeview *tree, + treeview_node *folder, + const struct treeview_field_data *field, + void *data) { bool match; @@ -708,8 +1189,8 @@ nserror treeview_update_node_folder(treeview *tree, assert(field != NULL); assert(lwc_string_isequal(tree->fields[tree->n_fields].field, - field->field, &match) == lwc_error_ok && - match == true); + field->field, &match) == lwc_error_ok && + match == true); folder->text.data = field->value; folder->text.len = field->value_len; folder->text.width = 0; @@ -717,9 +1198,9 @@ nserror treeview_update_node_folder(treeview *tree, if (folder->parent->flags & TV_NFLAGS_EXPANDED) { /* Text will be seen, get its width */ guit->layout->width(&plot_style_odd.text, - folder->text.data, - folder->text.len, - &(folder->text.width)); + folder->text.data, + folder->text.len, + &(folder->text.width)); } else { /* Just invalidate the width, since it's not needed now */ folder->text.width = 0; @@ -732,7 +1213,7 @@ nserror treeview_update_node_folder(treeview *tree, r.y0 = treeview_node_y(tree, folder); r.x1 = REDRAW_MAX; r.y1 = r.y0 + tree_g.line_height; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } return NSERROR_OK; @@ -740,10 +1221,11 @@ nserror treeview_update_node_folder(treeview *tree, /* Exported interface, documented in treeview.h */ -nserror treeview_update_node_entry(treeview *tree, - treeview_node *entry, - const struct treeview_field_data fields[], - void *data) +nserror +treeview_update_node_entry(treeview *tree, + treeview_node *entry, + const struct treeview_field_data fields[], + void *data) { bool match; struct treeview_node_entry *e = (struct treeview_node_entry *)entry; @@ -758,8 +1240,8 @@ nserror treeview_update_node_entry(treeview *tree, assert(fields != NULL); assert(fields[0].field != NULL); assert(lwc_string_isequal(tree->fields[0].field, - fields[0].field, &match) == lwc_error_ok && - match == true); + fields[0].field, &match) == lwc_error_ok && + match == true); entry->text.data = fields[0].value; entry->text.len = fields[0].value_len; entry->text.width = 0; @@ -767,9 +1249,9 @@ nserror treeview_update_node_entry(treeview *tree, if (entry->parent->flags & TV_NFLAGS_EXPANDED) { /* Text will be seen, get its width */ guit->layout->width(&plot_style_odd.text, - entry->text.data, - entry->text.len, - &(entry->text.width)); + entry->text.data, + entry->text.len, + &(entry->text.width)); } else { /* Just invalidate the width, since it's not needed now */ entry->text.width = 0; @@ -778,8 +1260,8 @@ nserror treeview_update_node_entry(treeview *tree, for (i = 1; i < tree->n_fields; i++) { assert(fields[i].field != NULL); assert(lwc_string_isequal(tree->fields[i].field, - fields[i].field, &match) == lwc_error_ok && - match == true); + fields[i].field, &match) == lwc_error_ok && + match == true); e->fields[i - 1].value.data = fields[i].value; e->fields[i - 1].value.len = fields[i].value_len; @@ -787,15 +1269,17 @@ nserror treeview_update_node_entry(treeview *tree, if (entry->flags & TV_NFLAGS_EXPANDED) { /* Text will be seen, get its width */ guit->layout->width(&plot_style_odd.text, - e->fields[i - 1].value.data, - e->fields[i - 1].value.len, - &(e->fields[i - 1].value.width)); + e->fields[i - 1].value.data, + e->fields[i - 1].value.len, + &(e->fields[i - 1].value.width)); } else { /* Invalidate the width, since it's not needed yet */ e->fields[i - 1].value.width = 0; } } + treeview__search_update_display(tree); + /* Redraw */ if (entry->parent->flags & TV_NFLAGS_EXPANDED) { struct rect r; @@ -803,7 +1287,7 @@ nserror treeview_update_node_entry(treeview *tree, r.y0 = treeview_node_y(tree, entry); r.x1 = REDRAW_MAX; r.y1 = r.y0 + entry->height; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } return NSERROR_OK; @@ -811,12 +1295,14 @@ nserror treeview_update_node_entry(treeview *tree, /* Exported interface, documented in treeview.h */ -nserror treeview_create_node_entry(treeview *tree, - treeview_node **entry, - treeview_node *relation, - enum treeview_relationship rel, - const struct treeview_field_data fields[], - void *data, treeview_node_options_flags flags) +nserror +treeview_create_node_entry(treeview *tree, + treeview_node **entry, + treeview_node *relation, + enum treeview_relationship rel, + const struct treeview_field_data fields[], + void *data, + treeview_node_options_flags flags) { bool match; struct treeview_node_entry *e; @@ -873,13 +1359,13 @@ nserror treeview_create_node_entry(treeview *tree, e->fields[i - 1].value.width = 0; } - treeview_insert_node(n, relation, rel); + treeview_insert_node(tree, n, relation, rel); if (n->parent->flags & TV_NFLAGS_EXPANDED) { /* Inform front end of change in dimensions */ if (!(flags & TREE_OPTION_SUPPRESS_RESIZE)) treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); /* Redraw */ if (!(flags & TREE_OPTION_SUPPRESS_REDRAW)) { @@ -888,25 +1374,42 @@ nserror treeview_create_node_entry(treeview *tree, r.y0 = treeview_node_y(tree, n); r.x1 = REDRAW_MAX; r.y1 = tree->root->height; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } } + treeview__search_update_display(tree); + *entry = n; return NSERROR_OK; } +/** + * Treewalk iterator context + */ struct treeview_walk_ctx { treeview_walk_cb enter_cb; treeview_walk_cb leave_cb; void *ctx; enum treeview_node_type type; }; -/** Treewalk node enter callback. */ -static nserror treeview_walk_fwd_cb(treeview_node *n, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node enter callback. + * + * \param n current node + * \param ctx treewalk context + * \param skip_children set if child nodes should be skipped + * \param end set if iteration should end early + */ +static nserror +treeview_walk_fwd_cb(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_walk_ctx *tw = ctx; @@ -916,7 +1419,15 @@ static nserror treeview_walk_fwd_cb(treeview_node *n, void *ctx, return NSERROR_OK; } -/** Treewalk node leave callback. */ + + +/** + * Treewalk node leave callback. + * + * \param n current node + * \param ctx treewalk context + * \param end set if iteration should end early + */ static nserror treeview_walk_bwd_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_walk_ctx *tw = ctx; @@ -927,10 +1438,16 @@ static nserror treeview_walk_bwd_cb(treeview_node *n, void *ctx, bool *end) return NSERROR_OK; } + + /* Exported interface, documented in treeview.h */ -nserror treeview_walk(treeview *tree, treeview_node *root, - treeview_walk_cb enter_cb, treeview_walk_cb leave_cb, - void *ctx, enum treeview_node_type type) +nserror +treeview_walk(treeview *tree, + treeview_node *root, + treeview_walk_cb enter_cb, + treeview_walk_cb leave_cb, + void *ctx, + enum treeview_node_type type) { struct treeview_walk_ctx tw = { .enter_cb = enter_cb, @@ -945,16 +1462,18 @@ nserror treeview_walk(treeview *tree, treeview_node *root, if (root == NULL) root = tree->root; - return treeview_walk_internal(root, true, + return treeview_walk_internal(tree, root, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, (leave_cb != NULL) ? treeview_walk_bwd_cb : NULL, - (enter_cb != NULL) ? treeview_walk_fwd_cb : NULL, &tw); + (enter_cb != NULL) ? treeview_walk_fwd_cb : NULL, + &tw); } /** * Unlink a treeview node * - * \param n Node to unlink + * \param n Node to unlink * \return true iff ancestor heights need to be reduced */ static inline bool treeview_unlink_node(treeview_node *n) @@ -975,7 +1494,8 @@ static inline bool treeview_unlink_node(treeview_node *n) } /* Reduce ancestor heights */ - if (n->parent != NULL && n->parent->flags & TV_NFLAGS_EXPANDED) { + if ((n->parent != NULL) && + (n->parent->flags & TV_NFLAGS_EXPANDED)) { return true; } @@ -986,8 +1506,8 @@ static inline bool treeview_unlink_node(treeview_node *n) /** * Cancel the editing of a treeview node * - * \param tree Treeview object to cancel node editing in - * \param redraw Set true iff redraw of removed textarea area required + * \param tree Treeview object to cancel node editing in + * \param redraw Set true iff redraw of removed textarea area required */ static void treeview_edit_cancel(treeview *tree, bool redraw) { @@ -1009,15 +1529,17 @@ static void treeview_edit_cancel(treeview *tree, bool redraw) r.y0 = tree->edit.y; r.x1 = tree->edit.x + tree->edit.w; r.y1 = tree->edit.y + tree->edit.h; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } } /** - * Complete a treeview edit, by informing the client with a change request msg + * Complete a treeview edit + * + * Complete edit by informing the client with a change request msg * - * \param tree Treeview object to complete edit in + * \param tree Treeview object to complete edit in */ static void treeview_edit_done(treeview *tree) { @@ -1027,8 +1549,9 @@ static void treeview_edit_done(treeview *tree) struct treeview_node_msg msg; msg.msg = TREE_MSG_NODE_EDIT; - if (tree->edit.textarea == NULL) + if (tree->edit.textarea == NULL) { return; + } assert(n != NULL); @@ -1066,7 +1589,6 @@ static void treeview_edit_done(treeview *tree) break; } - /* Finished with the new text */ free(new_text); @@ -1075,17 +1597,25 @@ static void treeview_edit_done(treeview *tree) } +/** + * context for treeview node deletion iterator + */ struct treeview_node_delete { treeview *tree; int h_reduction; bool user_interaction; }; -/** Treewalk node callback deleting nodes. */ -static nserror treeview_delete_node_walk_cb(treeview_node *n, - void *ctx, bool *end) + + +/** + * Treewalk node callback deleting nodes. + */ +static nserror +treeview_delete_node_walk_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_node_delete *nd = (struct treeview_node_delete *)ctx; struct treeview_node_msg msg; + msg.msg = TREE_MSG_NODE_DELETE; msg.data.delete.user = nd->user_interaction; @@ -1093,51 +1623,60 @@ static nserror treeview_delete_node_walk_cb(treeview_node *n, if (treeview_unlink_node(n)) nd->h_reduction += (n->type == TREE_NODE_ENTRY) ? - n->height : tree_g.line_height; + n->height : tree_g.line_height; /* Handle any special treatment */ switch (n->type) { case TREE_NODE_ENTRY: nd->tree->callbacks->entry(msg, n->client_data); break; + case TREE_NODE_FOLDER: nd->tree->callbacks->folder(msg, n->client_data); break; + case TREE_NODE_ROOT: break; + default: return NSERROR_BAD_PARAMETER; } /* Cancel any edit of this node */ if (nd->tree->edit.textarea != NULL && - nd->tree->edit.node == n) + nd->tree->edit.node == n) { treeview_edit_cancel(nd->tree, false); + } /* Free the node */ free(n); return NSERROR_OK; } + + /** * Delete a treeview node * - * \param tree Treeview object to delete node from - * \param n Node to delete - * \param interaction Delete is result of user interaction with treeview - * \param flags Treeview node options flags - * \return NSERROR_OK on success, appropriate error otherwise - * * Will emit folder or entry deletion msg callback. * - * Note this can be called from inside a treeview_walk fwd callback. + * \note this can be called from inside a treeview_walk fwd callback. * For example walking the tree and calling this for any node that's selected. * * This function does not delete empty nodes, so if TREEVIEW_DEL_EMPTY_DIRS is * set, caller must also call treeview_delete_empty. + * + * \param tree Treeview object to delete node from + * \param n Node to delete + * \param interaction Delete is result of user interaction with treeview + * \param flags Treeview node options flags + * \return NSERROR_OK on success, appropriate error otherwise */ -static nserror treeview_delete_node_internal(treeview *tree, treeview_node *n, - bool interaction, treeview_node_options_flags flags) +static nserror +treeview_delete_node_internal(treeview *tree, + treeview_node *n, + bool interaction, + treeview_node_options_flags flags) { nserror err; treeview_node *p = n->parent; @@ -1152,8 +1691,9 @@ static nserror treeview_delete_node_internal(treeview *tree, treeview_node *n, } /* Delete any children first */ - err = treeview_walk_internal(n, true, treeview_delete_node_walk_cb, - NULL, &nd); + err = treeview_walk_internal(tree, n, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, + treeview_delete_node_walk_cb, NULL, &nd); if (err != NSERROR_OK) { return err; } @@ -1175,12 +1715,14 @@ static nserror treeview_delete_node_internal(treeview *tree, treeview_node *n, /* Inform front end of change in dimensions */ if (tree->root != NULL && p != NULL && p->flags & TV_NFLAGS_EXPANDED && - nd.h_reduction > 0 && - !(flags & TREE_OPTION_SUPPRESS_RESIZE)) { + nd.h_reduction > 0 && + !(flags & TREE_OPTION_SUPPRESS_RESIZE)) { treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); } + treeview__search_update_display(tree); + return NSERROR_OK; } @@ -1188,8 +1730,8 @@ static nserror treeview_delete_node_internal(treeview *tree, treeview_node *n, /** * Delete any empty treeview folder nodes * - * \param tree Treeview object to delete empty nodes from - * \param interaction Delete is result of user interaction with treeview + * \param tree Treeview object to delete empty nodes from + * \param interaction Delete is result of user interaction with treeview * \return NSERROR_OK on success, appropriate error otherwise * * Note this must not be called within a treeview_walk. It may delete the @@ -1226,21 +1768,21 @@ static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction) * with a next sibling. */ while (node->parent != NULL && - next_sibling == NULL) { + next_sibling == NULL) { if (node->type == TREE_NODE_FOLDER && - node->children == NULL) { + node->children == NULL) { /* Delete node */ p = node->parent; err = treeview_delete_node_walk_cb( - node, &nd, &abort); + node, &nd, &abort); if (err != NSERROR_OK) { return err; } /* Reduce ancestor heights */ while (p != NULL && - p->flags & - TV_NFLAGS_EXPANDED) { + p->flags & + TV_NFLAGS_EXPANDED) { p->height -= nd.h_reduction; p = p->parent; } @@ -1255,18 +1797,18 @@ static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction) break; if (node->type == TREE_NODE_FOLDER && - node->children == NULL) { + node->children == NULL) { /* Delete node */ p = node->parent; err = treeview_delete_node_walk_cb( - node, &nd, &abort); + node, &nd, &abort); if (err != NSERROR_OK) { return err; } /* Reduce ancestor heights */ while (p != NULL && - p->flags & TV_NFLAGS_EXPANDED) { + p->flags & TV_NFLAGS_EXPANDED) { p->height -= nd.h_reduction; p = p->parent; } @@ -1281,7 +1823,7 @@ static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction) parent = node->parent; next_sibling = node->next_sib; child = (node->flags & TV_NFLAGS_EXPANDED) ? - node->children : NULL; + node->children : NULL; } return NSERROR_OK; @@ -1289,8 +1831,10 @@ static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction) /* Exported interface, documented in treeview.h */ -nserror treeview_delete_node(treeview *tree, treeview_node *n, - treeview_node_options_flags flags) +nserror +treeview_delete_node(treeview *tree, + treeview_node *n, + treeview_node_options_flags flags) { nserror err; struct rect r; @@ -1321,7 +1865,7 @@ nserror treeview_delete_node(treeview *tree, treeview_node *n, r.y0 = 0; if (!(flags & TREE_OPTION_SUPPRESS_RESIZE)) { treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); } } } @@ -1330,19 +1874,69 @@ nserror treeview_delete_node(treeview *tree, treeview_node *n, if (visible && !(flags & TREE_OPTION_SUPPRESS_REDRAW)) { r.x0 = 0; r.x1 = REDRAW_MAX; - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } return NSERROR_OK; } +/** + * Helper to create a textarea. + * + * \param[in] tree The treeview we're creating the textarea for. + * \param[in] width The width of the textarea. + * \param[in] height The height of the textarea. + * \param[in] border The border colour to use. + * \param[in] background The background colour to use. + * \param[in] foreground The foreground colour to use. + * \param[in] text The text style to use for the text area. + * \param[in] ta_callback The textarea callback function to give the textarea. + * \return the textarea pointer on success, or NULL on failure. + */ +static struct textarea *treeview__create_textarea( + treeview *tree, + int width, + int height, + colour border, + colour background, + colour foreground, + plot_font_style_t text, + textarea_client_callback ta_callback) +{ + /* Configure the textarea */ + textarea_flags ta_flags = TEXTAREA_INTERNAL_CARET; + textarea_setup ta_setup = { + .text = text, + .width = width, + .height = height, + .pad_top = 0, + .pad_left = 2, + .pad_right = 2, + .pad_bottom = 0, + .border_width = 1, + .border_col = border, + .selected_bg = foreground, + .selected_text = background, + }; + + ta_setup.text.foreground = foreground; + ta_setup.text.background = background; + + /* Create text area */ + return textarea_create(ta_flags, &ta_setup, ta_callback, tree); +} + + /* Exported interface, documented in treeview.h */ -nserror treeview_create(treeview **tree, +nserror +treeview_create(treeview **tree, const struct treeview_callback_table *callbacks, - int n_fields, struct treeview_field_desc fields[], + int n_fields, + struct treeview_field_desc fields[], const struct core_window_callback_table *cw_t, - struct core_window *cw, treeview_flags flags) + struct core_window *cw, + treeview_flags flags) { nserror error; int i; @@ -1383,7 +1977,7 @@ nserror treeview_create(treeview **tree, f->value.len = lwc_string_length(fields[i].field); guit->layout->width(&plot_style_odd.text, f->value.data, - f->value.len, &(f->value.width)); + f->value.len, &(f->value.width)); if (f->flags & TREE_FLAG_SHOW_NAME) if ((*tree)->field_width < f->value.width) @@ -1413,6 +2007,24 @@ nserror treeview_create(treeview **tree, (*tree)->edit.textarea = NULL; (*tree)->edit.node = NULL; + if (flags & TREEVIEW_SEARCHABLE) { + (*tree)->search.textarea = treeview__create_textarea( + *tree, 600, tree_g.line_height, + plot_style_even.text.background, + plot_style_even.text.background, + plot_style_even.text.foreground, + plot_style_odd.text, + treeview_textarea_search_callback); + if ((*tree)->search.textarea == NULL) { + treeview_destroy(*tree); + return NSERROR_NOMEM; + } + } else { + (*tree)->search.textarea = NULL; + } + (*tree)->search.active = false; + (*tree)->search.search = false; + (*tree)->flags = flags; (*tree)->cw_t = cw_t; @@ -1423,15 +2035,16 @@ nserror treeview_create(treeview **tree, /* Exported interface, documented in treeview.h */ -nserror treeview_cw_attach(treeview *tree, - const struct core_window_callback_table *cw_t, - struct core_window *cw) +nserror +treeview_cw_attach(treeview *tree, + const struct core_window_callback_table *cw_t, + struct core_window *cw) { assert(cw_t != NULL); assert(cw != NULL); if (tree->cw_t != NULL || tree->cw_h != NULL) { - LOG("Treeview already attached."); + NSLOG(netsurf, INFO, "Treeview already attached."); return NSERROR_UNKNOWN; } tree->cw_t = cw_t; @@ -1447,6 +2060,8 @@ nserror treeview_cw_detach(treeview *tree) tree->cw_t = NULL; tree->cw_h = NULL; + treeview__search_cancel(tree, true); + return NSERROR_OK; } @@ -1458,10 +2073,16 @@ nserror treeview_destroy(treeview *tree) assert(tree != NULL); + if (tree->search.textarea != NULL) { + tree->search.active = false; + tree->search.search = false; + textarea_destroy(tree->search.textarea); + } + /* Destroy nodes */ treeview_delete_node_internal(tree, tree->root, false, - TREE_OPTION_SUPPRESS_RESIZE | - TREE_OPTION_SUPPRESS_REDRAW); + TREE_OPTION_SUPPRESS_RESIZE | + TREE_OPTION_SUPPRESS_REDRAW); /* Destroy feilds */ for (f = 0; f <= tree->n_fields; f++) { @@ -1483,8 +2104,8 @@ nserror treeview_destroy(treeview *tree) * \param node The node to expand. * \return NSERROR_OK on success, appropriate error otherwise. */ -static nserror treeview_node_expand_internal(treeview *tree, - treeview_node *node) +static nserror +treeview_node_expand_internal(treeview *tree, treeview_node *node) { treeview_node *child; struct treeview_node_entry *e; @@ -1496,7 +2117,7 @@ static nserror treeview_node_expand_internal(treeview *tree, if (node->flags & TV_NFLAGS_EXPANDED) { /* What madness is this? */ - LOG("Tried to expand an expanded node."); + NSLOG(netsurf, INFO, "Tried to expand an expanded node."); return NSERROR_OK; } @@ -1509,12 +2130,11 @@ static nserror treeview_node_expand_internal(treeview *tree, } do { - assert((child->flags & TV_NFLAGS_EXPANDED) == false); if (child->text.width == 0) { guit->layout->width(&plot_style_odd.text, - child->text.data, - child->text.len, - &(child->text.width)); + child->text.data, + child->text.len, + &(child->text.width)); } additional_height += child->height; @@ -1533,9 +2153,9 @@ static nserror treeview_node_expand_internal(treeview *tree, if (e->fields[i].value.width == 0) { guit->layout->width(&plot_style_odd.text, - e->fields[i].value.data, - e->fields[i].value.len, - &(e->fields[i].value.width)); + e->fields[i].value.data, + e->fields[i].value.len, + &(e->fields[i].value.width)); } /* Add height for field */ @@ -1554,17 +2174,24 @@ static nserror treeview_node_expand_internal(treeview *tree, /* Update the node */ node->flags |= TV_NFLAGS_EXPANDED; - /* And parent's heights */ - do { - node->height += additional_height; - node = node->parent; - } while (node->parent != NULL); + /* And node heights */ + for (struct treeview_node *n = node; + (n != NULL) && (n->flags & TV_NFLAGS_EXPANDED); + n = n->parent) { + n->height += additional_height; + } - node->height += additional_height; + if (tree->search.search && + node->type == TREE_NODE_ENTRY && + node->flags & TV_NFLAGS_MATCHED) { + tree->search.height += additional_height; + } /* Inform front end of change in dimensions */ - if (additional_height != 0) - treeview__cw_update_size(tree, -1, tree->root->height); + if (additional_height != 0) { + treeview__cw_update_size(tree, -1, + treeview__get_display_height(tree)); + } return NSERROR_OK; } @@ -1573,29 +2200,37 @@ static nserror treeview_node_expand_internal(treeview *tree, /* Exported interface, documented in treeview.h */ nserror treeview_node_expand(treeview *tree, treeview_node *node) { - nserror err; - struct rect r; - - err = treeview_node_expand_internal(tree, node); - if (err != NSERROR_OK) - return err; - - r.x0 = 0; - r.y0 = treeview_node_y(tree, node); - r.x1 = REDRAW_MAX; - r.y1 = tree->root->height; - - /* Redraw */ - treeview__cw_redraw_request(tree, &r); + nserror res; + + res = treeview_node_expand_internal(tree, node); + NSLOG(netsurf, INFO, "Expanding!"); + if (res == NSERROR_OK) { + /* expansion was successful, attempt redraw */ + treeview__redraw_from_node(tree, node); + NSLOG(netsurf, INFO, "Expanded!"); + } - return NSERROR_OK; + return res; } +/** + * context for treeview contraction callback + */ struct treeview_contract_data { + treeview *tree; bool only_entries; }; -/** Treewalk node callback for handling node contraction. */ + + +/** + * Treewalk node callback for handling node contraction. + * + * \param n node + * \param ctx contract iterator context + * \param end flag to end iteration now + * \return NSERROR_OK on success else appropriate error code + */ static nserror treeview_node_contract_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_contract_data *data = ctx; @@ -1607,32 +2242,39 @@ static nserror treeview_node_contract_cb(treeview_node *n, void *ctx, bool *end) n->flags &= ~TV_NFLAGS_SELECTED; if ((n->flags & TV_NFLAGS_EXPANDED) == false || - (n->type == TREE_NODE_FOLDER && data->only_entries)) { + (n->type == TREE_NODE_FOLDER && data->only_entries)) { /* Nothing to do. */ return NSERROR_OK; } - n->flags ^= TV_NFLAGS_EXPANDED; h_reduction = n->height - tree_g.line_height; assert(h_reduction >= 0); + for (struct treeview_node *node = n; + (node != NULL) && (node->flags & TV_NFLAGS_EXPANDED); + node = node->parent) { + node->height -= h_reduction; + } - do { - n->height -= h_reduction; - n = n->parent; - } while (n != NULL); + if (data->tree->search.search) { + data->tree->search.height -= h_reduction; + } + + n->flags ^= TV_NFLAGS_EXPANDED; return NSERROR_OK; } + + /** * Contract a treeview node * - * \param tree Treeview object to contract node in - * \param node Node to contract + * \param tree Treeview object to contract node in + * \param node Node to contract * \return NSERROR_OK on success, appropriate error otherwise */ -static nserror treeview_node_contract_internal(treeview *tree, - treeview_node *node) +static nserror +treeview_node_contract_internal(treeview *tree, treeview_node *node) { struct treeview_contract_data data; bool selected; @@ -1640,16 +2282,17 @@ static nserror treeview_node_contract_internal(treeview *tree, if ((node->flags & TV_NFLAGS_EXPANDED) == false) { /* What madness is this? */ - LOG("Tried to contract a contracted node."); + NSLOG(netsurf, INFO, "Tried to contract a contracted node."); return NSERROR_OK; } + data.tree = tree; data.only_entries = false; selected = node->flags & TV_NFLAGS_SELECTED; /* Contract children. */ - treeview_walk_internal(node, false, treeview_node_contract_cb, - NULL, &data); + treeview_walk_internal(tree, node, TREEVIEW_WALK_MODE_LOGICAL_EXPANDED, + treeview_node_contract_cb, NULL, &data); /* Contract node */ treeview_node_contract_cb(node, &data, false); @@ -1658,7 +2301,7 @@ static nserror treeview_node_contract_internal(treeview *tree, node->flags |= TV_NFLAGS_SELECTED; /* Inform front end of change in dimensions */ - treeview__cw_update_size(tree, -1, tree->root->height); + treeview__cw_update_size(tree, -1, treeview__get_display_height(tree)); return NSERROR_OK; } @@ -1667,30 +2310,27 @@ static nserror treeview_node_contract_internal(treeview *tree, /* Exported interface, documented in treeview.h */ nserror treeview_node_contract(treeview *tree, treeview_node *node) { - nserror err; - struct rect r; + nserror res; assert(tree != NULL); - r.x0 = 0; - r.y0 = treeview_node_y(tree, node); - r.x1 = REDRAW_MAX; - r.y1 = tree->root->height; - - err = treeview_node_contract_internal(tree, node); - if (err != NSERROR_OK) - return err; - - /* Redraw */ - treeview__cw_redraw_request(tree, &r); + res = treeview_node_contract_internal(tree, node); + NSLOG(netsurf, INFO, "Contracting!"); + if (res == NSERROR_OK) { + /* successful contraction, request redraw */ + treeview__redraw_from_node(tree, node); + NSLOG(netsurf, INFO, "Contracted!"); + } - return NSERROR_OK; + return res; } /* Exported interface, documented in treeview.h */ nserror treeview_contract(treeview *tree, bool all) { + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; struct treeview_contract_data data; bool selected; treeview_node *n; @@ -1702,8 +2342,9 @@ nserror treeview_contract(treeview *tree, bool all) r.x0 = 0; r.y0 = 0; r.x1 = REDRAW_MAX; - r.y1 = tree->root->height; + r.y1 = tree->root->height + search_height; + data.tree = tree; data.only_entries = !all; for (n = tree->root->children; n != NULL; n = n->next_sib) { @@ -1714,7 +2355,8 @@ nserror treeview_contract(treeview *tree, bool all) selected = n->flags & TV_NFLAGS_SELECTED; /* Contract children. */ - treeview_walk_internal(n, false, + treeview_walk_internal(tree, n, + TREEVIEW_WALK_MODE_LOGICAL_EXPANDED, treeview_node_contract_cb, NULL, &data); /* Contract node */ @@ -1728,19 +2370,35 @@ nserror treeview_contract(treeview *tree, bool all) treeview__cw_update_size(tree, -1, tree->root->height); /* Redraw */ - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); return NSERROR_OK; } +/** + * context data for treeview expansion + */ struct treeview_expand_data { treeview *tree; bool only_folders; }; -/** Treewalk node callback for handling recursive node expansion. */ -static nserror treeview_expand_cb(treeview_node *n, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node callback for handling recursive node expansion. + * + * \param n current node + * \param ctx node expansion context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_expand_cb(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_expand_data *data = ctx; nserror err; @@ -1749,7 +2407,7 @@ static nserror treeview_expand_cb(treeview_node *n, void *ctx, assert(n->type != TREE_NODE_ROOT); if (n->flags & TV_NFLAGS_EXPANDED || - (data->only_folders && n->type != TREE_NODE_FOLDER)) { + (data->only_folders && n->type != TREE_NODE_FOLDER)) { /* Nothing to do. */ return NSERROR_OK; } @@ -1758,11 +2416,13 @@ static nserror treeview_expand_cb(treeview_node *n, void *ctx, return err; } + + /* Exported interface, documented in treeview.h */ nserror treeview_expand(treeview *tree, bool only_folders) { struct treeview_expand_data data; - nserror err; + nserror res; struct rect r; assert(tree != NULL); @@ -1771,50 +2431,57 @@ nserror treeview_expand(treeview *tree, bool only_folders) data.tree = tree; data.only_folders = only_folders; - err = treeview_walk_internal(tree->root, true, NULL, - treeview_expand_cb, &data); - if (err != NSERROR_OK) - return err; - - r.x0 = 0; - r.y0 = 0; - r.x1 = REDRAW_MAX; - r.y1 = tree->root->height; + res = treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, + NULL, treeview_expand_cb, &data); + if (res == NSERROR_OK) { + /* expansion succeeded, schedule redraw */ - /* Redraw */ - treeview__cw_redraw_request(tree, &r); + r.x0 = 0; + r.y0 = 0; + r.x1 = REDRAW_MAX; + r.y1 = tree->root->height; - return NSERROR_OK; + treeview__cw_invalidate_area(tree, &r); + } + return res; } -/* Exported interface, documented in treeview.h */ -void treeview_redraw(treeview *tree, const int x, const int y, - struct rect *clip, const struct redraw_context *ctx) +/** + * Draw a treeview normally, in tree mode. + * + * \param[in] tree The treeview we're rendering. + * \param[in] x X coordinate we're rendering the treeview at. + * \param[in] y Y coordinate we're rendering the treeview at. + * \param[in,out] render_y Current vertical position in tree, updated on exit. + * \param[in] r Clip rectangle. + * \param[in] data Redraw data for rendering contents. + * \param[in] ctx Current render context. + */ +static void treeview_redraw_tree( + treeview *tree, + const int x, + const int y, + int *render_y_in_out, + struct rect *r, + struct content_redraw_data *data, + const struct redraw_context *ctx) { - struct redraw_context new_ctx = *ctx; - treeview_node *node, *root, *next; - struct treeview_node_entry *entry; struct treeview_node_style *style = &plot_style_odd; - struct content_redraw_data data; - struct rect r; - uint32_t count = 0; - int render_y = y; - int inset; - int x0, y0, y1; - int baseline = (tree_g.line_height * 3 + 2) / 4; enum treeview_resource_id res = TREE_RES_CONTENT; - plot_style_t *bg_style; - plot_font_style_t *text_style; + int baseline = (tree_g.line_height * 3 + 2) / 4; plot_font_style_t *infotext_style; - struct bitmap *furniture; - int height; + treeview_node *root = tree->root; + treeview_node *node = tree->root; + int render_y = *render_y_in_out; + plot_font_style_t *text_style; + plot_style_t *bg_style; int sel_min, sel_max; - bool invert_selection; - - assert(tree != NULL); - assert(tree->root != NULL); - assert(tree->root->flags & TV_NFLAGS_EXPANDED); + uint32_t count = 0; + struct rect rect; + int inset; + int x0; if (tree->drag.start.y > tree->drag.prev.y) { sel_min = tree->drag.prev.y; @@ -1824,31 +2491,16 @@ void treeview_redraw(treeview *tree, const int x, const int y, sel_max = tree->drag.prev.y; } - /* Start knockout rendering if it's available for this plotter */ - if (ctx->plot->option_knockout) - knockout_plot_start(ctx, &new_ctx); - - /* Set up clip rectangle */ - r.x0 = clip->x0 + x; - r.y0 = clip->y0 + y; - r.x1 = clip->x1 + x; - r.y1 = clip->y1 + y; - new_ctx.plot->clip(&r); - - /* Draw the tree */ - node = root = tree->root; - - /* Setup common content redraw data */ - data.width = tree_g.icon_size; - data.height = tree_g.icon_size; - data.scale = 1; - data.repeat_x = false; - data.repeat_y = false; - while (node != NULL) { + struct treeview_node_entry *entry; + struct bitmap *furniture; + bool invert_selection; + treeview_node *next; + int height; int i; + next = (node->flags & TV_NFLAGS_EXPANDED) ? - node->children : NULL; + node->children : NULL; if (next != NULL) { /* down to children */ @@ -1859,7 +2511,7 @@ void treeview_redraw(treeview *tree, const int x, const int y, * with a next sibling. */ while (node != root && - node->next_sib == NULL) { + node->next_sib == NULL) { node = node->parent; } @@ -1872,14 +2524,14 @@ void treeview_redraw(treeview *tree, const int x, const int y, assert(node != NULL); assert(node != root); assert(node->type == TREE_NODE_FOLDER || - node->type == TREE_NODE_ENTRY); + node->type == TREE_NODE_ENTRY); count++; inset = x + node->inset; height = (node->type == TREE_NODE_ENTRY) ? node->height : - tree_g.line_height; + tree_g.line_height; - if ((render_y + height) < r.y0) { + if ((render_y + height) < r->y0) { /* This node's line is above clip region */ render_y += height; continue; @@ -1887,70 +2539,77 @@ void treeview_redraw(treeview *tree, const int x, const int y, style = (count & 0x1) ? &plot_style_odd : &plot_style_even; if (tree->drag.type == TV_DRAG_SELECTION && - (render_y + height >= sel_min && - render_y < sel_max)) { + (render_y + height >= sel_min && + render_y < sel_max)) { invert_selection = true; } else { invert_selection = false; } if ((node->flags & TV_NFLAGS_SELECTED && !invert_selection) || - (!(node->flags & TV_NFLAGS_SELECTED) && - invert_selection)) { + (!(node->flags & TV_NFLAGS_SELECTED) && + invert_selection)) { bg_style = &style->sbg; text_style = &style->stext; infotext_style = &style->sitext; furniture = (node->flags & TV_NFLAGS_EXPANDED) ? - style->furn[TREE_FURN_CONTRACT].sel : - style->furn[TREE_FURN_EXPAND].sel; + style->furn[TREE_FURN_CONTRACT].sel : + style->furn[TREE_FURN_EXPAND].sel; } else { bg_style = &style->bg; text_style = &style->text; infotext_style = &style->itext; furniture = (node->flags & TV_NFLAGS_EXPANDED) ? - style->furn[TREE_FURN_CONTRACT].bmp : - style->furn[TREE_FURN_EXPAND].bmp; + style->furn[TREE_FURN_CONTRACT].bmp : + style->furn[TREE_FURN_EXPAND].bmp; } /* Render background */ - y0 = render_y; - y1 = render_y + height; - new_ctx.plot->rectangle(r.x0, y0, r.x1, y1, bg_style); + rect.x0 = r->x0; + rect.y0 = render_y; + rect.x1 = r->x1; + rect.y1 = render_y + height; + ctx->plot->rectangle(ctx, bg_style, &rect); /* Render toggle */ - new_ctx.plot->bitmap(inset, render_y + tree_g.line_height / 4, + ctx->plot->bitmap(ctx, + furniture, + inset, + render_y + tree_g.line_height / 4, style->furn[TREE_FURN_EXPAND].size, style->furn[TREE_FURN_EXPAND].size, - furniture, - bg_style->fill_colour, BITMAPF_NONE); + bg_style->fill_colour, + BITMAPF_NONE); /* Render icon */ - if (node->type == TREE_NODE_ENTRY) + if (node->type == TREE_NODE_ENTRY) { res = TREE_RES_CONTENT; - else if (node->flags & TV_NFLAGS_SPECIAL) + } else if (node->flags & TV_NFLAGS_SPECIAL) { res = TREE_RES_FOLDER_SPECIAL; - else + } else { res = TREE_RES_FOLDER; + } if (treeview_res[res].ready) { /* Icon resource is available */ - data.x = inset + tree_g.step_width; - data.y = render_y + ((tree_g.line_height - - treeview_res[res].height + 1) / 2); - data.background_colour = bg_style->fill_colour; + data->x = inset + tree_g.step_width; + data->y = render_y + ((tree_g.line_height - + treeview_res[res].height + 1) / 2); + data->background_colour = bg_style->fill_colour; - content_redraw(treeview_res[res].c, - &data, &r, &new_ctx); + content_redraw(treeview_res[res].c, data, r, ctx); } /* Render text */ x0 = inset + tree_g.step_width + tree_g.icon_step; - new_ctx.plot->text(x0, render_y + baseline, - node->text.data, node->text.len, - text_style); + ctx->plot->text(ctx, + text_style, + x0, render_y + baseline, + node->text.data, + node->text.len); /* Rendered the node */ render_y += tree_g.line_height; - if (render_y > r.y1) { + if (render_y > r->y1) { /* Passed the bottom of what's in the clip region. * Done. */ break; @@ -1958,7 +2617,7 @@ void treeview_redraw(treeview *tree, const int x, const int y, if (node->type != TREE_NODE_ENTRY || - !(node->flags & TV_NFLAGS_EXPANDED)) + !(node->flags & TV_NFLAGS_EXPANDED)) /* Done everything for this node */ continue; @@ -1970,57 +2629,364 @@ void treeview_redraw(treeview *tree, const int x, const int y, if (ef->flags & TREE_FLAG_SHOW_NAME) { int max_width = tree->field_width; - new_ctx.plot->text(x0 + max_width - - ef->value.width - - tree_g.step_width, + ctx->plot->text(ctx, + infotext_style, + x0 + max_width - ef->value.width - tree_g.step_width, render_y + baseline, ef->value.data, - ef->value.len, - infotext_style); + ef->value.len); - new_ctx.plot->text(x0 + max_width, + ctx->plot->text(ctx, + infotext_style, + x0 + max_width, render_y + baseline, entry->fields[i].value.data, - entry->fields[i].value.len, - infotext_style); + entry->fields[i].value.len); } else { - new_ctx.plot->text(x0, render_y + baseline, + ctx->plot->text(ctx, + infotext_style, + x0, render_y + baseline, entry->fields[i].value.data, - entry->fields[i].value.len, - infotext_style); + entry->fields[i].value.len); + } + + /* Rendered the expanded entry field */ + render_y += tree_g.line_height; + } + + /* Finished rendering expanded entry */ + + if (render_y > r->y1) { + /* Passed the bottom of what's in the clip region. + * Done. */ + break; + } + } + + *render_y_in_out = render_y; +} + + +/** + * Draw a treeview normally, in tree mode. + * + * \param[in] tree The treeview we're rendering. + * \param[in] x X coordinate we're rendering the treeview at. + * \param[in] y Y coordinate we're rendering the treeview at. + * \param[in,out] render_y Current vertical position in tree, updated on exit. + * \param[in] r Clip rectangle. + * \param[in] data Redraw data for rendering contents. + * \param[in] ctx Current render context. + */ +static void treeview_redraw_search( + treeview *tree, + const int x, + const int y, + int *render_y_in_out, + struct rect *r, + struct content_redraw_data *data, + const struct redraw_context *ctx) +{ + struct treeview_node_style *style = &plot_style_odd; + enum treeview_resource_id res = TREE_RES_CONTENT; + int baseline = (tree_g.line_height * 3 + 2) / 4; + plot_font_style_t *infotext_style; + treeview_node *root = tree->root; + treeview_node *node = tree->root; + int render_y = *render_y_in_out; + plot_font_style_t *text_style; + plot_style_t *bg_style; + int sel_min, sel_max; + uint32_t count = 0; + struct rect rect; + int inset; + int x0; + + if (tree->drag.start.y > tree->drag.prev.y) { + sel_min = tree->drag.prev.y; + sel_max = tree->drag.start.y; + } else { + sel_min = tree->drag.start.y; + sel_max = tree->drag.prev.y; + } + + while (node != NULL) { + struct treeview_node_entry *entry; + struct bitmap *furniture; + bool invert_selection; + treeview_node *next; + int height; + int i; + + next = node->children; + + if (next != NULL) { + /* down to children */ + node = next; + } else { + /* No children. As long as we're not at the root, + * go to next sibling if present, or nearest ancestor + * with a next sibling. */ + + while (node != root && + node->next_sib == NULL) { + node = node->parent; + } + + if (node == root) + break; + + node = node->next_sib; + } + + assert(node != NULL); + assert(node != root); + assert(node->type == TREE_NODE_FOLDER || + node->type == TREE_NODE_ENTRY); + + if (node->type == TREE_NODE_FOLDER || + !(node->flags & TV_NFLAGS_MATCHED)) { + continue; + } + + count++; + inset = x + tree_g.window_padding; + height = node->height; + + if ((render_y + height) < r->y0) { + /* This node's line is above clip region */ + render_y += height; + continue; + } + + style = (count & 0x1) ? &plot_style_odd : &plot_style_even; + if (tree->drag.type == TV_DRAG_SELECTION && + (render_y + height >= sel_min && + render_y < sel_max)) { + invert_selection = true; + } else { + invert_selection = false; + } + if ((node->flags & TV_NFLAGS_SELECTED && !invert_selection) || + (!(node->flags & TV_NFLAGS_SELECTED) && + invert_selection)) { + bg_style = &style->sbg; + text_style = &style->stext; + infotext_style = &style->sitext; + furniture = (node->flags & TV_NFLAGS_EXPANDED) ? + style->furn[TREE_FURN_CONTRACT].sel : + style->furn[TREE_FURN_EXPAND].sel; + } else { + bg_style = &style->bg; + text_style = &style->text; + infotext_style = &style->itext; + furniture = (node->flags & TV_NFLAGS_EXPANDED) ? + style->furn[TREE_FURN_CONTRACT].bmp : + style->furn[TREE_FURN_EXPAND].bmp; + } + + /* Render background */ + rect.x0 = r->x0; + rect.y0 = render_y; + rect.x1 = r->x1; + rect.y1 = render_y + height; + ctx->plot->rectangle(ctx, bg_style, &rect); + + /* Render toggle */ + ctx->plot->bitmap(ctx, + furniture, + inset, + render_y + tree_g.line_height / 4, + style->furn[TREE_FURN_EXPAND].size, + style->furn[TREE_FURN_EXPAND].size, + bg_style->fill_colour, + BITMAPF_NONE); + + /* Render icon */ + if (node->type == TREE_NODE_ENTRY) { + res = TREE_RES_CONTENT; + } else if (node->flags & TV_NFLAGS_SPECIAL) { + res = TREE_RES_FOLDER_SPECIAL; + } else { + res = TREE_RES_FOLDER; + } + + if (treeview_res[res].ready) { + /* Icon resource is available */ + data->x = inset + tree_g.step_width; + data->y = render_y + ((tree_g.line_height - + treeview_res[res].height + 1) / 2); + data->background_colour = bg_style->fill_colour; + + content_redraw(treeview_res[res].c, data, r, ctx); + } + + /* Render text */ + x0 = inset + tree_g.step_width + tree_g.icon_step; + ctx->plot->text(ctx, + text_style, + x0, render_y + baseline, + node->text.data, + node->text.len); + + /* Rendered the node */ + render_y += tree_g.line_height; + if (render_y > r->y1) { + /* Passed the bottom of what's in the clip region. + * Done. */ + break; + } + + + if (node->type != TREE_NODE_ENTRY || + !(node->flags & TV_NFLAGS_EXPANDED)) + /* Done everything for this node */ + continue; + + /* Render expanded entry fields */ + entry = (struct treeview_node_entry *)node; + for (i = 0; i < tree->n_fields - 1; i++) { + struct treeview_field *ef = &(tree->fields[i + 1]); + + if (ef->flags & TREE_FLAG_SHOW_NAME) { + int max_width = tree->field_width; + ctx->plot->text(ctx, + infotext_style, + x0 + max_width - ef->value.width - tree_g.step_width, + render_y + baseline, + ef->value.data, + ef->value.len); + + ctx->plot->text(ctx, + infotext_style, + x0 + max_width, + render_y + baseline, + entry->fields[i].value.data, + entry->fields[i].value.len); + } else { + ctx->plot->text(ctx, + infotext_style, + x0, render_y + baseline, + entry->fields[i].value.data, + entry->fields[i].value.len); } /* Rendered the expanded entry field */ render_y += tree_g.line_height; } - /* Finshed rendering expanded entry */ + /* Finished rendering expanded entry */ - if (render_y > r.y1) { + if (render_y > r->y1) { /* Passed the bottom of what's in the clip region. * Done. */ break; } } + *render_y_in_out = render_y; +} + + +/* Exported interface, documented in treeview.h */ +void +treeview_redraw(treeview *tree, + const int x, + const int y, + struct rect *clip, + const struct redraw_context *ctx) +{ + struct redraw_context new_ctx = *ctx; + struct content_redraw_data data; + struct rect r; + struct rect rect; + int render_y = y; + + assert(tree != NULL); + assert(tree->root != NULL); + assert(tree->root->flags & TV_NFLAGS_EXPANDED); + + /* Start knockout rendering if it's available for this plotter */ + if (ctx->plot->option_knockout) { + knockout_plot_start(ctx, &new_ctx); + } + + /* Set up clip rectangle */ + r.x0 = clip->x0 + x; + r.y0 = clip->y0 + y; + r.x1 = clip->x1 + x; + r.y1 = clip->y1 + y; + new_ctx.plot->clip(&new_ctx, &r); + + /* Setup common content redraw data */ + data.width = tree_g.icon_size; + data.height = tree_g.icon_size; + data.scale = 1; + data.repeat_x = false; + data.repeat_y = false; + + if (tree->flags & TREEVIEW_SEARCHABLE) { + if (render_y < r.y1) { + enum treeview_resource_id icon = TREE_RES_SEARCH; + + /* Fill the blank area at the bottom */ + rect.x0 = r.x0; + rect.y0 = render_y; + rect.x1 = r.x1; + rect.y1 = render_y + tree_g.line_height; + new_ctx.plot->rectangle(&new_ctx, &plot_style_even.bg, + &rect); + + if (treeview_res[icon].ready) { + /* Icon resource is available */ + data.x = tree_g.window_padding; + data.y = render_y + ((tree_g.line_height - + treeview_res[icon].height + 1) / + 2); + data.background_colour = plot_style_even.bg. + fill_colour; + + content_redraw(treeview_res[icon].c, + &data, &r, &new_ctx); + } + + textarea_redraw(tree->search.textarea, + x + tree_g.window_padding + + tree_g.icon_step, y, + plot_style_even.bg.fill_colour, 1.0, + &r, &new_ctx); + } + render_y += tree_g.line_height; + } + + /* Render the treeview data */ + if (tree->search.search == true) { + treeview_redraw_search(tree, x, y, + &render_y, &r, &data, &new_ctx); + } else { + treeview_redraw_tree(tree, x, y, + &render_y, &r, &data, &new_ctx); + } + if (render_y < r.y1) { /* Fill the blank area at the bottom */ - y0 = render_y; - new_ctx.plot->rectangle(r.x0, y0, r.x1, r.y1, - &plot_style_even.bg); + rect.x0 = r.x0; + rect.y0 = render_y; + rect.x1 = r.x1; + rect.y1 = r.y1; + new_ctx.plot->rectangle(&new_ctx, &plot_style_even.bg, &rect); } /* All normal treeview rendering is done; render any overlays */ - if (tree->move.target_pos != TV_TARGET_NONE && - treeview_res[TREE_RES_ARROW].ready) { + if ((tree->move.target_pos != TV_TARGET_NONE) && + (treeview_res[TREE_RES_ARROW].ready)) { /* Got a MOVE drag; render move indicator arrow */ data.x = tree->move.target_area.x0 + x; data.y = tree->move.target_area.y0 + y; data.background_colour = plot_style_even.bg.fill_colour; - content_redraw(treeview_res[TREE_RES_ARROW].c, - &data, &r, &new_ctx); + content_redraw(treeview_res[TREE_RES_ARROW].c, &data, &r, &new_ctx); } else if (tree->edit.textarea != NULL) { /* Edit in progress; render textarea */ @@ -2031,10 +2997,15 @@ void treeview_redraw(treeview *tree, const int x, const int y, } /* Rendering complete */ - if (ctx->plot->option_knockout) - knockout_plot_end(); + if (ctx->plot->option_knockout) { + knockout_plot_end(ctx); + } } + +/** + * context for treeview selection + */ struct treeview_selection_walk_data { enum { TREEVIEW_WALK_HAS_SELECTION, @@ -2059,6 +3030,7 @@ struct treeview_selection_walk_data { } drag; struct { treeview_node *prev; + treeview_node *fixed; } yank; struct { treeview_node *n; @@ -2071,9 +3043,22 @@ struct treeview_selection_walk_data { int current_y; treeview *tree; }; -/** Treewalk node callback for handling selection related actions. */ -static nserror treeview_node_selection_walk_cb(treeview_node *n, - void *ctx, bool *skip_children, bool *end) + + +/** + * Treewalk node callback for handling selection related actions. + * + * \param n current node + * \param ctx node selection context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_node_selection_walk_cb(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_selection_walk_data *sw = ctx; int height; @@ -2103,7 +3088,7 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, case TREEVIEW_WALK_DELETE_SELECTION: if (n->flags & TV_NFLAGS_SELECTED) { err = treeview_delete_node_internal(sw->tree, n, true, - TREE_OPTION_NONE); + TREE_OPTION_NONE); if (err != NSERROR_OK) { return err; } @@ -2114,8 +3099,8 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, case TREEVIEW_WALK_PROPAGATE_SELECTION: if (n->parent != NULL && - n->parent->flags & TV_NFLAGS_SELECTED && - !(n->flags & TV_NFLAGS_SELECTED)) { + n->parent->flags & TV_NFLAGS_SELECTED && + !(n->flags & TV_NFLAGS_SELECTED)) { n->flags ^= TV_NFLAGS_SELECTED; changed = true; } @@ -2137,8 +3122,8 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, case TREEVIEW_WALK_COMMIT_SELECT_DRAG: if (sw->current_y >= sw->data.drag.sel_min && - sw->current_y - height < - sw->data.drag.sel_max) { + sw->current_y - height < + sw->data.drag.sel_max) { n->flags ^= TV_NFLAGS_SELECTED; } return NSERROR_OK; @@ -2148,6 +3133,10 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, treeview_node *p = n->parent; int h = 0; + if (n == sw->data.yank.fixed) { + break; + } + if (treeview_unlink_node(n)) h = n->height; @@ -2175,7 +3164,7 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, case TREEVIEW_WALK_COPY_SELECTION: if (n->flags & TV_NFLAGS_SELECTED && - n->type == TREE_NODE_ENTRY) { + n->type == TREE_NODE_ENTRY) { int i; char *temp; uint32_t len; @@ -2190,12 +3179,12 @@ static nserror treeview_node_selection_walk_cb(treeview_node *n, continue; } val = treeview_get_text_for_field(sw->tree, - n, i); + n, i); text = val->data; len = val->len; temp = realloc(sw->data.copy.text, - sw->data.copy.len + len + 1); + sw->data.copy.len + len + 1); if (temp == NULL) { free(sw->data.copy.text); sw->data.copy.text = NULL; @@ -2238,7 +3227,8 @@ bool treeview_has_selection(treeview *tree) sw.purpose = TREEVIEW_WALK_HAS_SELECTION; sw.data.has_selection = false; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.has_selection; @@ -2248,7 +3238,7 @@ bool treeview_has_selection(treeview *tree) /** * Get first selected node (in any) * - * \param tree Treeview object in which to create folder + * \param tree Treeview object in which to create folder * \return the first selected treeview node, or NULL */ static treeview_node * treeview_get_first_selected(treeview *tree) @@ -2258,7 +3248,8 @@ static treeview_node * treeview_get_first_selected(treeview *tree) sw.purpose = TREEVIEW_WALK_GET_FIRST_SELECTED; sw.data.first.n = NULL; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.first.n; @@ -2267,7 +3258,7 @@ static treeview_node * treeview_get_first_selected(treeview *tree) /* Exported interface, documented in treeview.h */ enum treeview_node_type treeview_get_selection(treeview *tree, - void **node_data) + void **node_data) { treeview_node *n; @@ -2288,8 +3279,8 @@ enum treeview_node_type treeview_get_selection(treeview *tree, /** * Clear any selection in a treeview * - * \param tree Treeview object to clear selection in - * \param rect Redraw rectangle (if redraw required) + * \param tree Treeview object to clear selection in + * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ static bool treeview_clear_selection(treeview *tree, struct rect *rect) @@ -2304,9 +3295,11 @@ static bool treeview_clear_selection(treeview *tree, struct rect *rect) sw.purpose = TREEVIEW_WALK_CLEAR_SELECTION; sw.data.redraw.required = false; sw.data.redraw.rect = rect; - sw.current_y = 0; + sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.redraw.required; @@ -2316,8 +3309,8 @@ static bool treeview_clear_selection(treeview *tree, struct rect *rect) /** * Select all in a treeview * - * \param tree Treeview object to select all in - * \param rect Redraw rectangle (if redraw required) + * \param tree Treeview object to select all in + * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ static bool treeview_select_all(treeview *tree, struct rect *rect) @@ -2332,9 +3325,11 @@ static bool treeview_select_all(treeview *tree, struct rect *rect) sw.purpose = TREEVIEW_WALK_SELECT_ALL; sw.data.redraw.required = false; sw.data.redraw.rect = rect; - sw.current_y = 0; + sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.redraw.required; @@ -2344,14 +3339,15 @@ static bool treeview_select_all(treeview *tree, struct rect *rect) /** * Commit a current selection drag, modifying the node's selection state. * - * \param tree Treeview object to commit drag selection in + * \param tree Treeview object to commit drag selection in */ static void treeview_commit_selection_drag(treeview *tree) { struct treeview_selection_walk_data sw; sw.purpose = TREEVIEW_WALK_COMMIT_SELECT_DRAG; - sw.current_y = 0; + sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; if (tree->drag.start.y > tree->drag.prev.y) { sw.data.drag.sel_min = tree->drag.prev.y; @@ -2361,7 +3357,8 @@ static void treeview_commit_selection_drag(treeview *tree) sw.data.drag.sel_max = tree->drag.prev.y; } - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); } @@ -2369,17 +3366,20 @@ static void treeview_commit_selection_drag(treeview *tree) /** * Yank a selection to the node move list. * - * \param tree Treeview object to yank selection from + * \param tree Treeview object to yank selection from + * \param fixed Treeview node that should not be yanked */ -static void treeview_move_yank_selection(treeview *tree) +static void treeview_move_yank_selection(treeview *tree, treeview_node *fixed) { struct treeview_selection_walk_data sw; sw.purpose = TREEVIEW_WALK_YANK_SELECTION; + sw.data.yank.fixed = fixed; sw.data.yank.prev = NULL; sw.tree = tree; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); } @@ -2387,7 +3387,7 @@ static void treeview_move_yank_selection(treeview *tree) /** * Copy a selection to the clipboard. * - * \param tree Treeview object to yank selection from + * \param tree Treeview object to yank selection from */ static void treeview_copy_selection(treeview *tree) { @@ -2399,7 +3399,8 @@ static void treeview_copy_selection(treeview *tree) sw.data.copy.len = 0; sw.tree = tree; - err = treeview_walk_internal(tree->root, false, NULL, + err = treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); if (err != NSERROR_OK) { return; @@ -2407,7 +3408,7 @@ static void treeview_copy_selection(treeview *tree) if (sw.data.copy.text != NULL) { guit->clipboard->set(sw.data.copy.text, - sw.data.copy.len - 1, NULL, 0); + sw.data.copy.len - 1, NULL, 0); free(sw.data.copy.text); } } @@ -2416,8 +3417,8 @@ static void treeview_copy_selection(treeview *tree) /** * Delete a selection. * - * \param tree Treeview object to delete selected nodes from - * \param rect Updated to redraw rectangle + * \param tree Treeview object to delete selected nodes from + * \param rect Updated to redraw rectangle * \return true iff redraw required. */ static bool treeview_delete_selection(treeview *tree, struct rect *rect) @@ -2438,7 +3439,8 @@ static bool treeview_delete_selection(treeview *tree, struct rect *rect) sw.current_y = 0; sw.tree = tree; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.redraw.required; @@ -2448,8 +3450,8 @@ static bool treeview_delete_selection(treeview *tree, struct rect *rect) /** * Propagate selection to visible descendants of selected nodes. * - * \param tree Treeview object to propagate selection in - * \param rect Redraw rectangle (if redraw required) + * \param tree Treeview object to propagate selection in + * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ static bool treeview_propagate_selection(treeview *tree, struct rect *rect) @@ -2470,7 +3472,8 @@ static bool treeview_propagate_selection(treeview *tree, struct rect *rect) sw.current_y = 0; sw.tree = tree; - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_selection_walk_cb, &sw); return sw.data.redraw.required; @@ -2480,8 +3483,9 @@ static bool treeview_propagate_selection(treeview *tree, struct rect *rect) /** * Move a selection according to the current move drag. * - * \param tree Treeview object to move selected nodes in - * \param rect Redraw rectangle + * \param tree Treeview object to move selected nodes in + * \param rect Redraw rectangle + * \return NSERROR_OK on success else appropriate error code */ static nserror treeview_move_selection(treeview *tree, struct rect *rect) { @@ -2534,7 +3538,7 @@ static nserror treeview_move_selection(treeview *tree, struct rect *rect) break; default: - LOG("Bad drop target for move."); + NSLOG(netsurf, INFO, "Bad drop target for move."); return NSERROR_BAD_PARAMETER; } @@ -2544,23 +3548,24 @@ static nserror treeview_move_selection(treeview *tree, struct rect *rect) parent = relation->parent; } - /* The node that we're moving selection to can't itself be selected */ - assert(!(relation->flags & TV_NFLAGS_SELECTED)); - /* Move all selected nodes from treeview to tree->move.root */ - treeview_move_yank_selection(tree); + treeview_move_yank_selection(tree, relation); /* Move all nodes on tree->move.root to target location */ for (node = tree->move.root; node != NULL; node = next) { next = node->next_sib; + if (node == relation) { + continue; + } + if (!(parent->flags & TV_NFLAGS_EXPANDED)) { if (node->flags & TV_NFLAGS_EXPANDED) treeview_node_contract_internal(tree, node); node->flags &= ~TV_NFLAGS_SELECTED; } - treeview_insert_node(node, relation, relationship); + treeview_insert_node(tree, node, relation, relationship); relation = node; relationship = TREE_REL_NEXT_SIBLING; @@ -2581,13 +3586,20 @@ static nserror treeview_move_selection(treeview *tree, struct rect *rect) } +/** + * context for treeview launch action + */ struct treeview_launch_walk_data { int selected_depth; treeview *tree; }; -/** Treewalk node walk backward callback for tracking folder selection. */ -static nserror treeview_node_launch_walk_bwd_cb(treeview_node *n, void *ctx, - bool *end) + + +/** + * Treewalk node walk backward callback for tracking folder selection. + */ +static nserror +treeview_node_launch_walk_bwd_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_launch_walk_data *lw = ctx; @@ -2597,9 +3609,22 @@ static nserror treeview_node_launch_walk_bwd_cb(treeview_node *n, void *ctx, return NSERROR_OK; } -/** Treewalk node walk forward callback for launching nodes. */ -static nserror treeview_node_launch_walk_fwd_cb(treeview_node *n, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node walk forward callback for launching nodes. + * + * \param n current node + * \param ctx node launch context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_node_launch_walk_fwd_cb(treeview_node *n, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_launch_walk_data *lw = ctx; nserror ret = NSERROR_OK; @@ -2608,8 +3633,8 @@ static nserror treeview_node_launch_walk_fwd_cb(treeview_node *n, void *ctx, lw->selected_depth++; } else if (n->type == TREE_NODE_ENTRY && - (n->flags & TV_NFLAGS_SELECTED || - lw->selected_depth > 0)) { + (n->flags & TV_NFLAGS_SELECTED || + lw->selected_depth > 0)) { struct treeview_node_msg msg; msg.msg = TREE_MSG_NODE_LAUNCH; msg.data.node_launch.mouse = BROWSER_MOUSE_HOVER; @@ -2618,14 +3643,16 @@ static nserror treeview_node_launch_walk_fwd_cb(treeview_node *n, void *ctx, return ret; } + + /** * Launch a selection. * - * \param tree Treeview object to launch selected nodes in - * \return NSERROR_OK on success, appropriate error otherwise + * \note Selected entries are launched. Entries that are descendants + * of selected folders are also launched. * - * Note: Selected entries are launched. - * Entries that are descendants of selected folders are also launched. + * \param tree Treeview object to launch selected nodes in + * \return NSERROR_OK on success, appropriate error otherwise */ static nserror treeview_launch_selection(treeview *tree) { @@ -2637,15 +3664,20 @@ static nserror treeview_launch_selection(treeview *tree) lw.selected_depth = 0; lw.tree = tree; - return treeview_walk_internal(tree->root, true, + return treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, treeview_node_launch_walk_bwd_cb, treeview_node_launch_walk_fwd_cb, &lw); } /* Exported interface, documented in treeview.h */ -nserror treeview_get_relation(treeview *tree, treeview_node **relation, - enum treeview_relationship *rel, bool at_y, int y) +nserror +treeview_get_relation(treeview *tree, + treeview_node **relation, + enum treeview_relationship *rel, + bool at_y, + int y) { treeview_node *n; @@ -2687,6 +3719,9 @@ nserror treeview_get_relation(treeview *tree, treeview_node **relation, } +/** + * context for treeview keyboard action + */ struct treeview_nav_state { treeview *tree; treeview_node *prev; @@ -2696,9 +3731,22 @@ struct treeview_nav_state { int n_selected; int prev_n_selected; }; -/** Treewalk node callback for handling mouse action. */ -static nserror treeview_node_nav_cb(treeview_node *node, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node callback for handling mouse action. + * + * \param node current node + * \param ctx node context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_node_nav_cb(treeview_node *node, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_nav_state *ns = ctx; @@ -2724,19 +3772,21 @@ static nserror treeview_node_nav_cb(treeview_node *node, void *ctx, return NSERROR_OK; } + + /** * Handle keyboard navigation. * - * \param tree Treeview object to launch selected nodes in - * \param key The ucs4 character codepoint - * \param rect Updated to redraw rectangle - * \return true if treeview needs redraw, false otherwise - * - * Note: Selected entries are launched. + * \note Selected entries are launched. * Entries that are descendants of selected folders are also launched. + * + * \param tree Treeview object to launch selected nodes in + * \param key The ucs4 character codepoint + * \param rect Updated to redraw rectangle + * \return true if treeview needs redraw, false otherwise */ -static bool treeview_keyboard_navigation(treeview *tree, uint32_t key, - struct rect *rect) +static bool +treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect) { struct treeview_nav_state ns = { .tree = tree, @@ -2747,26 +3797,35 @@ static bool treeview_keyboard_navigation(treeview *tree, uint32_t key, .n_selected = 0, .prev_n_selected = 0 }; - int h = tree->root->height; + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + int h = treeview__get_display_height(tree) + search_height; bool redraw = false; /* Fill out the nav. state struct, by examining the current selection * state */ - treeview_walk_internal(tree->root, false, NULL, + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_nav_cb, &ns); - if (ns.next == NULL) - ns.next = tree->root->children; - if (ns.prev == NULL) - ns.prev = ns.last; + + if (tree->search.search == false) { + if (ns.next == NULL) + ns.next = tree->root->children; + if (ns.prev == NULL) + ns.prev = ns.last; + } /* Clear any existing selection */ redraw = treeview_clear_selection(tree, rect); switch (key) { case NS_KEY_LEFT: + if (tree->search.search == true) { + break; + } if (ns.curr != NULL && - ns.curr->parent != NULL && - ns.curr->parent->type != TREE_NODE_ROOT) { + ns.curr->parent != NULL && + ns.curr->parent->type != TREE_NODE_ROOT) { /* Step to parent */ ns.curr->parent->flags |= TV_NFLAGS_SELECTED; @@ -2784,7 +3843,7 @@ static bool treeview_keyboard_navigation(treeview *tree, uint32_t key, if (ns.curr->children != NULL) { /* Step to first child */ ns.curr->children->flags |= - TV_NFLAGS_SELECTED; + TV_NFLAGS_SELECTED; } else { /* Retain current node selection */ ns.curr->flags |= TV_NFLAGS_SELECTED; @@ -2824,8 +3883,8 @@ static bool treeview_keyboard_navigation(treeview *tree, uint32_t key, rect->x0 = 0; rect->y0 = 0; rect->x1 = REDRAW_MAX; - if (tree->root->height > h) - rect->y1 = tree->root->height; + if (treeview__get_display_height(tree) + search_height > h) + rect->y1 = treeview__get_display_height(tree) + search_height; else rect->y1 = h; redraw = true; @@ -2842,7 +3901,7 @@ bool treeview_keypress(treeview *tree, uint32_t key) assert(tree != NULL); - /* Pass to textarea, if editing in progress */ + /* Pass to any textarea, if editing in progress */ if (tree->edit.textarea != NULL) { switch (key) { case NS_KEY_ESCAPE: @@ -2855,6 +3914,17 @@ bool treeview_keypress(treeview *tree, uint32_t key) default: return textarea_keypress(tree->edit.textarea, key); } + } else if (tree->search.active == true) { + switch (key) { + case NS_KEY_ESCAPE: + treeview__search_cancel(tree, false); + return true; + case NS_KEY_NL: + case NS_KEY_CR: + return true; + default: + return textarea_keypress(tree->search.textarea, key); + } } /* Keypress to be handled by treeview */ @@ -2878,7 +3948,7 @@ bool treeview_keypress(treeview *tree, uint32_t key) if (tree->root->height != h) { r.y0 = 0; treeview__cw_update_size(tree, -1, - tree->root->height); + tree->root->height); } } break; @@ -2901,7 +3971,7 @@ bool treeview_keypress(treeview *tree, uint32_t key) } if (redraw) { - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } return true; @@ -2920,9 +3990,14 @@ bool treeview_keypress(treeview *tree, uint32_t key) * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ -static bool treeview_set_move_indicator(treeview *tree, bool need_redraw, - treeview_node *target, int node_height, - int node_y, int mouse_y, struct rect *rect) +static bool +treeview_set_move_indicator(treeview *tree, + bool need_redraw, + treeview_node *target, + int node_height, + int node_y, + int mouse_y, + struct rect *rect) { treeview_node *orig = target; enum treeview_target_pos target_pos; @@ -2937,44 +4012,44 @@ static bool treeview_set_move_indicator(treeview *tree, bool need_redraw, if (target->flags & TV_NFLAGS_SELECTED) { /* Find top selected ancestor */ while (target->parent && - target->parent->flags & TV_NFLAGS_SELECTED) { + target->parent->flags & TV_NFLAGS_SELECTED) { target = target->parent; } - /* Find top ajdacent selected sibling */ + /* Find top adjacent selected sibling */ while (target->prev_sib && - target->prev_sib->flags & TV_NFLAGS_SELECTED) { + target->prev_sib->flags & TV_NFLAGS_SELECTED) { target = target->prev_sib; } target_pos = TV_TARGET_ABOVE; } else switch (target->type) { - case TREE_NODE_FOLDER: - if (mouse_pos <= node_height / 4) { - target_pos = TV_TARGET_ABOVE; - } else if (mouse_pos <= (3 * node_height) / 4 || - target->flags & TV_NFLAGS_EXPANDED) { - target_pos = TV_TARGET_INSIDE; - } else { - target_pos = TV_TARGET_BELOW; - } - break; + case TREE_NODE_FOLDER: + if (mouse_pos <= node_height / 4) { + target_pos = TV_TARGET_ABOVE; + } else if (mouse_pos <= (3 * node_height) / 4 || + target->flags & TV_NFLAGS_EXPANDED) { + target_pos = TV_TARGET_INSIDE; + } else { + target_pos = TV_TARGET_BELOW; + } + break; - case TREE_NODE_ENTRY: - if (mouse_pos <= node_height / 2) { - target_pos = TV_TARGET_ABOVE; - } else { - target_pos = TV_TARGET_BELOW; - } - break; + case TREE_NODE_ENTRY: + if (mouse_pos <= node_height / 2) { + target_pos = TV_TARGET_ABOVE; + } else { + target_pos = TV_TARGET_BELOW; + } + break; - default: - assert(target->type != TREE_NODE_ROOT); - return false; - } + default: + assert(target->type != TREE_NODE_ROOT); + return false; + } if (target_pos == tree->move.target_pos && - target == tree->move.target) { + target == tree->move.target) { /* No change */ return need_redraw; } @@ -3008,7 +4083,7 @@ static bool treeview_set_move_indicator(treeview *tree, bool need_redraw, /* Oftsets are all relative to centred (INSIDE) */ node_y += (tree_g.line_height - - treeview_res[TREE_RES_ARROW].height + 1) / 2; + treeview_res[TREE_RES_ARROW].height + 1) / 2; x = target->inset + tree_g.move_offset; @@ -3043,6 +4118,9 @@ static bool treeview_set_move_indicator(treeview *tree, bool need_redraw, /** * Callback for textarea_create, in desktop/treeview.h + * + * \param data treeview context + * \param msg textarea message */ static void treeview_textarea_callback(void *data, struct textarea_msg *msg) { @@ -3069,7 +4147,7 @@ static void treeview_textarea_callback(void *data, struct textarea_msg *msg) r->y1 += tree->edit.y; /* Redraw the textarea */ - treeview__cw_redraw_request(tree, r); + treeview__cw_invalidate_area(tree, r); break; default: @@ -3081,16 +4159,21 @@ static void treeview_textarea_callback(void *data, struct textarea_msg *msg) /** * Start edit of node field, at given y-coord, if editable * - * \param tree Treeview object to consider editing in - * \param n The treeview node to try editing - * \param node_y The Y coord of the top of n - * \param mouse_x X coord of mouse position - * \param mouse_y Y coord of mouse position - * \param rect Redraw rectangle (if redraw required) + * \param tree Treeview object to consider editing in + * \param n The treeview node to try editing + * \param node_y The Y coord of the top of n + * \param mouse_x X coord of mouse position + * \param mouse_y Y coord of mouse position + * \param rect Redraw rectangle (if redraw required) * \return true iff redraw required */ -static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, - int node_y, int mouse_x, int mouse_y, struct rect *rect) +static bool +treeview_edit_node_at_point(treeview *tree, + treeview_node *n, + int node_y, + int mouse_x, + int mouse_y, + struct rect *rect) { struct treeview_text *field_data = NULL; struct treeview_field *ef, *field_desc = NULL; @@ -3098,8 +4181,6 @@ static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, int field_y = node_y; int field_x; int width, height; - textarea_setup ta_setup; - textarea_flags ta_flags; bool success; /* If the main field is editable, make field_data point to it */ @@ -3125,7 +4206,7 @@ static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, ef = &(tree->fields[i + 1]); pos += tree_g.line_height; if (mouse_y <= pos && (ef->flags & - TREE_FLAG_ALLOW_EDIT)) { + TREE_FLAG_ALLOW_EDIT)) { field_data = &e->fields[i].value; field_desc = ef; field_y = pos - tree_g.line_height; @@ -3141,31 +4222,15 @@ static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, /* Get window width/height */ treeview__cw_get_window_dimensions(tree, &width, &height); - /* Anow textarea width/height */ + /* Calculate textarea width/height */ field_x = n->inset + tree_g.step_width + tree_g.icon_step - 3; width -= field_x; height = tree_g.line_height; - /* Configure the textarea */ - ta_flags = TEXTAREA_INTERNAL_CARET; - - ta_setup.width = width; - ta_setup.height = height; - ta_setup.pad_top = 0; - ta_setup.pad_right = 2; - ta_setup.pad_bottom = 0; - ta_setup.pad_left = 2; - ta_setup.border_width = 1; - ta_setup.border_col = 0x000000; - ta_setup.selected_text = 0xffffff; - ta_setup.selected_bg = 0x000000; - ta_setup.text = plot_style_odd.text; - ta_setup.text.foreground = 0x000000; - ta_setup.text.background = 0xffffff; - /* Create text area */ - tree->edit.textarea = textarea_create(ta_flags, &ta_setup, - treeview_textarea_callback, tree); + tree->edit.textarea = treeview__create_textarea(tree, width, height, + 0x000000, 0xffffff, 0x000000, plot_style_odd.text, + treeview_textarea_callback); if (tree->edit.textarea == NULL) { return false; } @@ -3187,8 +4252,8 @@ static bool treeview_edit_node_at_point(treeview *tree, treeview_node *n, mouse_x = width - 1; textarea_mouse_action(tree->edit.textarea, - BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1, - mouse_x, tree_g.line_height / 2); + BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1, + mouse_x, tree_g.line_height / 2); /* Position the textarea */ tree->edit.x = field_x; @@ -3232,7 +4297,7 @@ void treeview_edit_selection(treeview *tree) /* Edit node at y */ redraw = treeview_edit_node_at_point(tree, n, y, - 0, y + tree_g.line_height / 2, &rect); + 0, y + tree_g.line_height / 2, &rect); if (redraw == false) return; @@ -3242,20 +4307,37 @@ void treeview_edit_selection(treeview *tree) rect.y0 = y; rect.x1 = REDRAW_MAX; rect.y1 = y + tree_g.line_height; - treeview__cw_redraw_request(tree, &rect); + treeview__cw_invalidate_area(tree, &rect); } +/** + * context for treeview mouse handling + */ struct treeview_mouse_action { treeview *tree; browser_mouse_state mouse; int x; int y; int current_y; /* Y coordinate value of top of current node */ + int search_height; }; -/** Treewalk node callback for handling mouse action. */ -static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, - bool *skip_children, bool *end) + + +/** + * Treewalk node callback for handling mouse action. + * + * \param node current node + * \param ctx node context + * \param skip_children flag to allow children to be skipped + * \param end flag to allow iteration to be finished early. + * \return NSERROR_OK on success else error code. + */ +static nserror +treeview_node_mouse_action_cb(treeview_node *node, + void *ctx, + bool *skip_children, + bool *end) { struct treeview_mouse_action *ma = ctx; struct rect r; @@ -3273,7 +4355,7 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, r.x1 = REDRAW_MAX; height = (node->type == TREE_NODE_ENTRY) ? node->height : - tree_g.line_height; + tree_g.line_height; /* Skip line if we've not reached mouse y */ if (ma->y > ma->current_y + height) { @@ -3283,25 +4365,29 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, /* Find where the mouse is */ if (ma->y <= ma->current_y + tree_g.line_height) { - if (ma->x >= node->inset - 1 && - ma->x < node->inset + tree_g.step_width) { + int inset = node->inset; + if (ma->tree->search.search == true) { + inset = tree_g.window_padding; + } + if (ma->x >= inset - 1 && + ma->x < inset + tree_g.step_width) { /* Over expansion toggle */ part = TV_NODE_PART_TOGGLE; - } else if (ma->x >= node->inset + tree_g.step_width && - ma->x < node->inset + tree_g.step_width + + } else if (ma->x >= inset + tree_g.step_width && + ma->x < inset + tree_g.step_width + tree_g.icon_step + node->text.width) { /* On node */ part = TV_NODE_PART_ON_NODE; } } else if (node->type == TREE_NODE_ENTRY && - height > tree_g.line_height) { + height > tree_g.line_height) { /* Expanded entries */ int x = node->inset + tree_g.step_width + tree_g.icon_step; int y = ma->current_y + tree_g.line_height; int i; struct treeview_node_entry *entry = - (struct treeview_node_entry *)node; + (struct treeview_node_entry *)node; for (i = 0; i < ma->tree->n_fields - 1; i++) { struct treeview_field *ef = &(ma->tree->fields[i + 1]); @@ -3314,21 +4400,21 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, int max_width = ma->tree->field_width; if (ma->x >= x + max_width - ef->value.width - - tree_g.step_width && - ma->x < x + max_width - - tree_g.step_width) { + tree_g.step_width && + ma->x < x + max_width - + tree_g.step_width) { /* On a field name */ part = TV_NODE_PART_ON_NODE; } else if (ma->x >= x + max_width && - ma->x < x + max_width + - entry->fields[i].value.width) { + ma->x < x + max_width + + entry->fields[i].value.width) { /* On a field value */ part = TV_NODE_PART_ON_NODE; } } else { if (ma->x >= x && ma->x < x + - entry->fields[i].value.width) { + entry->fields[i].value.width) { /* On a field value */ part = TV_NODE_PART_ON_NODE; } @@ -3340,7 +4426,7 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, /* Record what position / part a drag started on */ if (ma->mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) && - ma->tree->drag.type == TV_DRAG_NONE) { + ma->tree->drag.type == TV_DRAG_NONE) { ma->tree->drag.selected = node->flags & TV_NFLAGS_SELECTED; ma->tree->drag.start_node = node; ma->tree->drag.part = part; @@ -3358,29 +4444,30 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, /* Handle drag start */ if (ma->tree->drag.type == TV_DRAG_NONE) { if (ma->mouse & BROWSER_MOUSE_DRAG_1 && - ma->tree->drag.selected == false && - ma->tree->drag.part == TV_NODE_PART_NONE) { + ma->tree->drag.selected == false && + ma->tree->drag.part == TV_NODE_PART_NONE) { ma->tree->drag.type = TV_DRAG_SELECTION; treeview__cw_drag_status(ma->tree, - CORE_WINDOW_DRAG_SELECTION); + CORE_WINDOW_DRAG_SELECTION); - } else if (!(ma->tree->flags & TREEVIEW_NO_MOVES) && - ma->mouse & BROWSER_MOUSE_DRAG_1 && - (ma->tree->drag.selected == true || - ma->tree->drag.part == TV_NODE_PART_ON_NODE)) { + } else if (ma->tree->search.search == false && + !(ma->tree->flags & TREEVIEW_NO_MOVES) && + ma->mouse & BROWSER_MOUSE_DRAG_1 && + (ma->tree->drag.selected == true || + ma->tree->drag.part == TV_NODE_PART_ON_NODE)) { ma->tree->drag.type = TV_DRAG_MOVE; treeview__cw_drag_status(ma->tree, - CORE_WINDOW_DRAG_MOVE); + CORE_WINDOW_DRAG_MOVE); redraw |= treeview_propagate_selection(ma->tree, &r); } else if (ma->mouse & BROWSER_MOUSE_DRAG_2) { ma->tree->drag.type = TV_DRAG_SELECTION; treeview__cw_drag_status(ma->tree, - CORE_WINDOW_DRAG_SELECTION); + CORE_WINDOW_DRAG_SELECTION); } if (ma->tree->drag.start_node != NULL && - ma->tree->drag.type == TV_DRAG_SELECTION) { + ma->tree->drag.type == TV_DRAG_SELECTION) { ma->tree->drag.start_node->flags ^= TV_NFLAGS_SELECTED; } } @@ -3391,10 +4478,10 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, { int curr_y1 = ma->current_y + height; int prev_y1 = ma->tree->drag.prev.node_y + - ma->tree->drag.prev.node_h; + ma->tree->drag.prev.node_h; r.y0 = (ma->current_y < ma->tree->drag.prev.node_y) ? - ma->current_y : ma->tree->drag.prev.node_y; + ma->current_y : ma->tree->drag.prev.node_y; r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1; redraw = true; @@ -3408,7 +4495,8 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, case TV_DRAG_MOVE: redraw |= treeview_set_move_indicator(ma->tree, redraw, - node, height, ma->current_y, ma->y, &r); + node, height, + ma->current_y, ma->y, &r); break; default: @@ -3418,9 +4506,10 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, click = ma->mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2); if (((node->type == TREE_NODE_FOLDER) && - (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) || - (part == TV_NODE_PART_TOGGLE && click)) { - int h = ma->tree->root->height; + (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) || + (part == TV_NODE_PART_TOGGLE && click)) { + int h = treeview__get_display_height(ma->tree) + + ma->search_height; /* Clear any existing selection */ redraw |= treeview_clear_selection(ma->tree, &r); @@ -3438,11 +4527,17 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, /* Set up redraw */ if (!redraw || r.y0 > ma->current_y) r.y0 = ma->current_y; - r.y1 = h > ma->tree->root->height ? h : ma->tree->root->height; + if (h > treeview__get_display_height(ma->tree) + + ma->search_height) { + r.y1 = h; + } else { + r.y1 = treeview__get_display_height(ma->tree) + + ma->search_height; + } redraw = true; } else if ((node->type == TREE_NODE_ENTRY) && - (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) { + (ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) { struct treeview_node_msg msg; msg.msg = TREE_MSG_NODE_LAUNCH; msg.data.node_launch.mouse = ma->mouse; @@ -3454,28 +4549,29 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, ma->tree->callbacks->entry(msg, node->client_data); } else if (ma->mouse & BROWSER_MOUSE_PRESS_2 || - (ma->mouse & BROWSER_MOUSE_PRESS_1 && - ma->mouse & BROWSER_MOUSE_MOD_2)) { + (ma->mouse & BROWSER_MOUSE_PRESS_1 && + ma->mouse & BROWSER_MOUSE_MOD_2)) { /* Toggle selection of node */ action |= TV_NODE_ACTION_SELECTION; } else if (ma->mouse & BROWSER_MOUSE_CLICK_1 && - ma->mouse & - (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3) && - part != TV_NODE_PART_TOGGLE) { + ma->mouse & + (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3) && + part != TV_NODE_PART_TOGGLE) { /* Clear any existing selection */ redraw |= treeview_clear_selection(ma->tree, &r); /* Edit node */ redraw |= treeview_edit_node_at_point(ma->tree, node, - ma->current_y, ma->x, ma->y, &r); + ma->current_y, ma->x, + ma->y, &r); } else if (ma->mouse & BROWSER_MOUSE_PRESS_1 && - !(ma->mouse & - (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3)) && - !(node->flags & TV_NFLAGS_SELECTED) && - part != TV_NODE_PART_TOGGLE) { + !(ma->mouse & + (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3)) && + !(node->flags & TV_NFLAGS_SELECTED) && + part != TV_NODE_PART_TOGGLE) { /* Clear any existing selection */ redraw |= treeview_clear_selection(ma->tree, &r); @@ -3494,35 +4590,69 @@ static nserror treeview_node_mouse_action_cb(treeview_node *node, void *ctx, r.y1 = ma->current_y + height; redraw = true; } else { - if (r.y0 > ma->current_y) + if (r.y0 > ma->current_y) { r.y0 = ma->current_y; - if (r.y1 < ma->current_y + height) + } + if (r.y1 < ma->current_y + height) { r.y1 = ma->current_y + height; + } } } if (redraw) { - treeview__cw_redraw_request(ma->tree, &r); + treeview__cw_invalidate_area(ma->tree, &r); } *end = true; /* Reached line with click; stop walking tree */ return NSERROR_OK; } + + /* Exported interface, documented in treeview.h */ -void treeview_mouse_action(treeview *tree, - browser_mouse_state mouse, int x, int y) +void +treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y) { struct rect r; bool redraw = false; + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; assert(tree != NULL); assert(tree->root != NULL); + /* Not interested in whether mouse leaves window. */ + if (mouse == BROWSER_MOUSE_LEAVE) { + return; + } + /* Handle mouse drag captured by textarea */ if (tree->drag.type == TV_DRAG_TEXTAREA) { textarea_mouse_action(tree->edit.textarea, mouse, - x - tree->edit.x, y - tree->edit.y); + x - tree->edit.x, y - tree->edit.y); + return; + } else if (tree->drag.type == TV_DRAG_SEARCH || + (y < search_height && + tree->drag.type == TV_DRAG_NONE)) { + if (tree->search.active == false) { + tree->search.active = true; + if (treeview_clear_selection(tree, &r)) { + treeview__cw_invalidate_area(tree, &r); + } + } + textarea_mouse_action(tree->search.textarea, mouse, + x - tree_g.window_padding - tree_g.icon_size, + y); return; + } else if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) && + tree->search.active == true) { + + tree->search.active = false; + textarea_set_caret(tree->search.textarea, -1); + r.x0 = 0; + r.y0 = 0; + r.x1 = REDRAW_MAX; + r.y1 = tree_g.line_height; + treeview__cw_invalidate_area(tree, &r); } /* Handle textarea related mouse action */ @@ -3531,10 +4661,10 @@ void treeview_mouse_action(treeview *tree, int ta_y = y - tree->edit.y; if (ta_x > 0 && ta_x < tree->edit.w && - ta_y > 0 && ta_y < tree->edit.h) { + ta_y > 0 && ta_y < tree->edit.h) { /* Inside textarea */ textarea_mouse_action(tree->edit.textarea, mouse, - ta_x, ta_y); + ta_x, ta_y); return; } else if (mouse != BROWSER_MOUSE_HOVER) { @@ -3562,7 +4692,7 @@ void treeview_mouse_action(treeview *tree, tree->move.target_pos = TV_TARGET_NONE; treeview__cw_drag_status(tree, CORE_WINDOW_DRAG_NONE); - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); return; default: /* No drag to end */ @@ -3570,7 +4700,7 @@ void treeview_mouse_action(treeview *tree, } } - if (y > tree->root->height) { + if (y > treeview__get_display_height(tree) + search_height) { /* Below tree */ r.x0 = 0; @@ -3578,7 +4708,7 @@ void treeview_mouse_action(treeview *tree, /* Record what position / part a drag started on */ if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) && - tree->drag.type == TV_DRAG_NONE) { + tree->drag.type == TV_DRAG_NONE) { tree->drag.selected = false; tree->drag.start_node = NULL; tree->drag.part = TV_NODE_PART_NONE; @@ -3596,8 +4726,8 @@ void treeview_mouse_action(treeview *tree, /* Handle drag start */ if (tree->drag.type == TV_DRAG_NONE) { if (mouse & BROWSER_MOUSE_DRAG_1 && - tree->drag.selected == false && - tree->drag.part == TV_NODE_PART_NONE) { + tree->drag.selected == false && + tree->drag.part == TV_NODE_PART_NONE) { tree->drag.type = TV_DRAG_SELECTION; treeview__cw_drag_status(tree, CORE_WINDOW_DRAG_SELECTION); @@ -3608,9 +4738,9 @@ void treeview_mouse_action(treeview *tree, } if (tree->drag.start_node != NULL && - tree->drag.type == TV_DRAG_SELECTION) { + tree->drag.type == TV_DRAG_SELECTION) { tree->drag.start_node->flags ^= - TV_NFLAGS_SELECTED; + TV_NFLAGS_SELECTED; } } @@ -3618,7 +4748,7 @@ void treeview_mouse_action(treeview *tree, if (tree->drag.type == TV_DRAG_SELECTION) { int curr_y1 = tree->root->height; int prev_y1 = tree->drag.prev.node_y + - tree->drag.prev.node_h; + tree->drag.prev.node_h; r.y0 = tree->drag.prev.node_y; r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1; @@ -3637,20 +4767,22 @@ void treeview_mouse_action(treeview *tree, } if (redraw) { - treeview__cw_redraw_request(tree, &r); + treeview__cw_invalidate_area(tree, &r); } } else { /* On tree */ - struct treeview_mouse_action ma; - - ma.tree = tree; - ma.mouse = mouse; - ma.x = x; - ma.y = y; - ma.current_y = 0; - - treeview_walk_internal(tree->root, false, NULL, + struct treeview_mouse_action ma = { + ma.tree = tree, + ma.mouse = mouse, + ma.x = x, + ma.y = y, + ma.current_y = search_height, + ma.search_height = search_height, + }; + + treeview_walk_internal(tree, tree->root, + TREEVIEW_WALK_MODE_DISPLAY, NULL, treeview_node_mouse_action_cb, &ma); } } @@ -3659,78 +4791,111 @@ void treeview_mouse_action(treeview *tree, /* Exported interface, documented in treeview.h */ int treeview_get_height(treeview *tree) { + int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ? + tree_g.line_height : 0; + int height = treeview__get_display_height(tree); + assert(tree != NULL); assert(tree->root != NULL); - treeview__cw_update_size(tree, -1, tree->root->height); + treeview__cw_update_size(tree, -1, height); - return tree->root->height; + return height + search_height; } /** * Initialise the plot styles from CSS system colour values. + * + * \param font_pt_size font size to use + * \return NSERROR_OK on success else appropriate error code */ -static void treeview_init_plot_styles(int font_pt_size) +static nserror treeview_init_plot_styles(int font_pt_size) { + nserror res; + /* Background colour */ plot_style_even.bg.stroke_type = PLOT_OP_TYPE_NONE; plot_style_even.bg.stroke_width = 0; plot_style_even.bg.stroke_colour = 0; plot_style_even.bg.fill_type = PLOT_OP_TYPE_SOLID; - plot_style_even.bg.fill_colour = ns_system_colour_char("Window"); + res = ns_system_colour_char("Window", &plot_style_even.bg.fill_colour); + if (res != NSERROR_OK) { + return res; + } /* Text colour */ plot_style_even.text.family = PLOT_FONT_FAMILY_SANS_SERIF; plot_style_even.text.size = font_pt_size; plot_style_even.text.weight = 400; plot_style_even.text.flags = FONTF_NONE; - plot_style_even.text.foreground = ns_system_colour_char("WindowText"); - plot_style_even.text.background = ns_system_colour_char("Window"); + res = ns_system_colour_char("WindowText", &plot_style_even.text.foreground); + if (res != NSERROR_OK) { + return res; + } + res = ns_system_colour_char("Window", &plot_style_even.text.background); + if (res != NSERROR_OK) { + return res; + } /* Entry field text colour */ plot_style_even.itext = plot_style_even.text; plot_style_even.itext.foreground = mix_colour( - plot_style_even.text.foreground, - plot_style_even.text.background, 255 * 10 / 16); + plot_style_even.text.foreground, + plot_style_even.text.background, + 255 * 10 / 16); /* Selected background colour */ plot_style_even.sbg = plot_style_even.bg; - plot_style_even.sbg.fill_colour = ns_system_colour_char("Highlight"); + res = ns_system_colour_char("Highlight", &plot_style_even.sbg.fill_colour); + if (res != NSERROR_OK) { + return res; + } /* Selected text colour */ plot_style_even.stext = plot_style_even.text; - plot_style_even.stext.foreground = - ns_system_colour_char("HighlightText"); - plot_style_even.stext.background = ns_system_colour_char("Highlight"); + res = ns_system_colour_char("HighlightText", &plot_style_even.stext.foreground); + if (res != NSERROR_OK) { + return res; + } + res = ns_system_colour_char("Highlight", &plot_style_even.stext.background); + if (res != NSERROR_OK) { + return res; + } /* Selected entry field text colour */ plot_style_even.sitext = plot_style_even.stext; plot_style_even.sitext.foreground = mix_colour( - plot_style_even.stext.foreground, - plot_style_even.stext.background, 255 * 25 / 32); - + plot_style_even.stext.foreground, + plot_style_even.stext.background, + 255 * 25 / 32); /* Odd numbered node styles */ plot_style_odd.bg = plot_style_even.bg; plot_style_odd.bg.fill_colour = mix_colour( - plot_style_even.bg.fill_colour, - plot_style_even.text.foreground, 255 * 15 / 16); + plot_style_even.bg.fill_colour, + plot_style_even.text.foreground, 255 * 15 / 16); plot_style_odd.text = plot_style_even.text; plot_style_odd.text.background = plot_style_odd.bg.fill_colour; plot_style_odd.itext = plot_style_odd.text; plot_style_odd.itext.foreground = mix_colour( - plot_style_odd.text.foreground, - plot_style_odd.text.background, 255 * 10 / 16); + plot_style_odd.text.foreground, + plot_style_odd.text.background, 255 * 10 / 16); plot_style_odd.sbg = plot_style_even.sbg; plot_style_odd.stext = plot_style_even.stext; plot_style_odd.sitext = plot_style_even.sitext; + + return NSERROR_OK; } /** - * Callback for hlcache. + * Callback for hlcache retrieving resources. + * + * \param handle content hlcache handle + * \param event The event that occurred on the content + * \param pw treeview resource context */ static nserror treeview_res_cb(struct hlcache_handle *handle, @@ -3767,9 +4932,10 @@ static void treeview_init_resources(void) treeview_res[i].height = 0; if (nsurl_create(treeview_res[i].url, &url) == NSERROR_OK) { hlcache_handle_retrieve(url, 0, NULL, NULL, - treeview_res_cb, - &(treeview_res[i]), NULL, - CONTENT_IMAGE, &(treeview_res[i].c)); + treeview_res_cb, + &(treeview_res[i]), NULL, + CONTENT_IMAGE, + &(treeview_res[i].c)); nsurl_unref(url); } } @@ -3779,12 +4945,12 @@ static void treeview_init_resources(void) /** * Create a right-pointing anti-aliased triangle bitmap * - * bg background colour - * fg foreground colour - * size required bitmap size + * \param bg background colour + * \param fg foreground colour + * \param size required bitmap size */ -static struct bitmap * treeview_generate_triangle_bitmap( - colour bg, colour fg, int size) +static struct bitmap * +treeview_generate_triangle_bitmap(colour bg, colour fg, int size) { struct bitmap *b = NULL; int x, y; @@ -3881,11 +5047,11 @@ static struct bitmap * treeview_generate_triangle_bitmap( /** * Create bitmap copy of another bitmap * - * orig bitmap to copy - * size required bitmap size + * \param orig bitmap to copy + * \param size required bitmap size */ -static struct bitmap * treeview_generate_copy_bitmap( - struct bitmap *orig, int size) +static struct bitmap * +treeview_generate_copy_bitmap(struct bitmap *orig, int size) { struct bitmap *b = NULL; unsigned char *data; @@ -3926,11 +5092,11 @@ static struct bitmap * treeview_generate_copy_bitmap( /** * Create bitmap from rotation of another bitmap * - * orig bitmap to create rotation of - * size required bitmap size + * \param orig bitmap to create rotation of + * \param size required bitmap size */ -static struct bitmap * treeview_generate_rotate_bitmap( - struct bitmap *orig, int size) +static struct bitmap * +treeview_generate_rotate_bitmap(struct bitmap *orig, int size) { struct bitmap *b = NULL; int x, y; @@ -3967,7 +5133,7 @@ static struct bitmap * treeview_generate_rotate_bitmap( *(pos++) = *(orig_pos++); *(pos++) = *(orig_pos); *(pos++) = 0xff; - + } rpos += stride; @@ -3987,6 +5153,8 @@ static struct bitmap * treeview_generate_rotate_bitmap( /** * Measures width of characters used to represent treeview furniture. + * + * \return NSERROR_OK on success else error code */ static nserror treeview_init_furniture(void) { @@ -3994,47 +5162,47 @@ static nserror treeview_init_furniture(void) plot_style_odd.furn[TREE_FURN_EXPAND].size = size; plot_style_odd.furn[TREE_FURN_EXPAND].bmp = - treeview_generate_triangle_bitmap( + treeview_generate_triangle_bitmap( plot_style_odd.bg.fill_colour, plot_style_odd.itext.foreground, size); plot_style_odd.furn[TREE_FURN_EXPAND].sel = - treeview_generate_triangle_bitmap( + treeview_generate_triangle_bitmap( plot_style_odd.sbg.fill_colour, plot_style_odd.sitext.foreground, size); plot_style_even.furn[TREE_FURN_EXPAND].size = size; plot_style_even.furn[TREE_FURN_EXPAND].bmp = - treeview_generate_triangle_bitmap( + treeview_generate_triangle_bitmap( plot_style_even.bg.fill_colour, plot_style_even.itext.foreground, size); plot_style_even.furn[TREE_FURN_EXPAND].sel = - treeview_generate_copy_bitmap( + treeview_generate_copy_bitmap( plot_style_odd.furn[TREE_FURN_EXPAND].sel, size); plot_style_odd.furn[TREE_FURN_CONTRACT].size = size; plot_style_odd.furn[TREE_FURN_CONTRACT].bmp = - treeview_generate_rotate_bitmap( + treeview_generate_rotate_bitmap( plot_style_odd.furn[TREE_FURN_EXPAND].bmp, size); plot_style_odd.furn[TREE_FURN_CONTRACT].sel = - treeview_generate_rotate_bitmap( + treeview_generate_rotate_bitmap( plot_style_odd.furn[TREE_FURN_EXPAND].sel, size); plot_style_even.furn[TREE_FURN_CONTRACT].size = size; plot_style_even.furn[TREE_FURN_CONTRACT].bmp = - treeview_generate_rotate_bitmap( + treeview_generate_rotate_bitmap( plot_style_even.furn[TREE_FURN_EXPAND].bmp, size); plot_style_even.furn[TREE_FURN_CONTRACT].sel = - treeview_generate_rotate_bitmap( + treeview_generate_rotate_bitmap( plot_style_even.furn[TREE_FURN_EXPAND].sel, size); if (plot_style_odd.furn[TREE_FURN_EXPAND].bmp == NULL || - plot_style_odd.furn[TREE_FURN_EXPAND].sel == NULL || - plot_style_even.furn[TREE_FURN_EXPAND].bmp == NULL || - plot_style_even.furn[TREE_FURN_EXPAND].sel == NULL || - plot_style_odd.furn[TREE_FURN_CONTRACT].bmp == NULL || - plot_style_odd.furn[TREE_FURN_CONTRACT].sel == NULL || - plot_style_even.furn[TREE_FURN_CONTRACT].bmp == NULL || - plot_style_even.furn[TREE_FURN_CONTRACT].sel == NULL) + plot_style_odd.furn[TREE_FURN_EXPAND].sel == NULL || + plot_style_even.furn[TREE_FURN_EXPAND].bmp == NULL || + plot_style_even.furn[TREE_FURN_EXPAND].sel == NULL || + plot_style_odd.furn[TREE_FURN_CONTRACT].bmp == NULL || + plot_style_odd.furn[TREE_FURN_CONTRACT].sel == NULL || + plot_style_even.furn[TREE_FURN_CONTRACT].bmp == NULL || + plot_style_even.furn[TREE_FURN_CONTRACT].sel == NULL) return NSERROR_NOMEM; tree_g.furniture_width = size + tree_g.line_height / 4; @@ -4048,14 +5216,14 @@ nserror treeview_init(void) { long long font_px_size; long long font_pt_size; - nserror err; + nserror res; if (tree_g.initialised > 0) { tree_g.initialised++; return NSERROR_OK; } - LOG("Initialising treeview module"); + NSLOG(netsurf, INFO, "Initialising treeview module"); font_pt_size = nsoption_int(treeview_font_size); if (font_pt_size <= 0) { @@ -4066,11 +5234,17 @@ nserror treeview_init(void) 10 + 36) / 72; tree_g.line_height = (font_px_size * 8 + 3) / 6; - treeview_init_plot_styles(font_pt_size * FONT_SIZE_SCALE / 10); + res = treeview_init_plot_styles(font_pt_size * PLOT_STYLE_SCALE / 10); + if (res != NSERROR_OK) { + return res; + } + treeview_init_resources(); - err = treeview_init_furniture(); - if (err != NSERROR_OK) - return err; + + res = treeview_init_furniture(); + if (res != NSERROR_OK) { + return res; + } tree_g.step_width = tree_g.furniture_width; tree_g.window_padding = 6; @@ -4080,7 +5254,7 @@ nserror treeview_init(void) tree_g.initialised++; - LOG("Initialised treeview module"); + NSLOG(netsurf, INFO, "Initialised treeview module"); return NSERROR_OK; } @@ -4096,11 +5270,12 @@ nserror treeview_fini(void) return NSERROR_OK; } else if (tree_g.initialised == 0) { - LOG("Warning: tried to finalise uninitialised treeview module"); + NSLOG(netsurf, INFO, + "Warning: tried to finalise uninitialised treeview module"); return NSERROR_OK; } - LOG("Finalising treeview module"); + NSLOG(netsurf, INFO, "Finalising treeview module"); for (i = 0; i < TREE_RES_LAST; i++) { hlcache_handle_release(treeview_res[i].c); @@ -4117,7 +5292,7 @@ nserror treeview_fini(void) tree_g.initialised--; - LOG("Finalised treeview module"); + NSLOG(netsurf, INFO, "Finalised treeview module"); return NSERROR_OK; } diff --git a/desktop/treeview.h b/desktop/treeview.h index 518f045aa..a8cf29ac5 100644 --- a/desktop/treeview.h +++ b/desktop/treeview.h @@ -37,6 +37,10 @@ struct core_window_callback_table; typedef struct treeview treeview; typedef struct treeview_node treeview_node; + +/** + * treeview node type + */ enum treeview_node_type { TREE_NODE_NONE = 0, /**< No node */ TREE_NODE_ROOT = (1 << 0), /**< Node is treeview's root */ @@ -44,40 +48,60 @@ enum treeview_node_type { TREE_NODE_ENTRY = (1 << 2) /**< Node is an entry */ }; + +/** + * Relationship between nodes + */ enum treeview_relationship { TREE_REL_FIRST_CHILD, TREE_REL_NEXT_SIBLING -}; /**< Relationship between nodes */ +}; + +/** + * Node change handling options + */ typedef enum { TREE_OPTION_NONE = (0), /* No flags set */ TREE_OPTION_SPECIAL_DIR = (1 << 0), /* Special folder */ TREE_OPTION_SUPPRESS_RESIZE = (1 << 1), /* Suppress callback */ TREE_OPTION_SUPPRESS_REDRAW = (1 << 2) /* Suppress callback */ -} treeview_node_options_flags; /**< Node change handling options */ +} treeview_node_options_flags; +/** + * treeview control flags + */ typedef enum { TREEVIEW_NO_FLAGS = (0), /**< No flags set */ TREEVIEW_NO_MOVES = (1 << 0), /**< No node drags */ TREEVIEW_NO_DELETES = (1 << 1), /**< No node deletes */ TREEVIEW_READ_ONLY = TREEVIEW_NO_MOVES | TREEVIEW_NO_DELETES, - TREEVIEW_DEL_EMPTY_DIRS = (1 << 2) /**< Delete dirs on empty */ + TREEVIEW_DEL_EMPTY_DIRS = (1 << 2), /**< Delete dirs on empty */ + TREEVIEW_SEARCHABLE = (1 << 3), /**< Treeview has search bar */ } treeview_flags; +/** + * treeview message types + */ enum treeview_msg { TREE_MSG_NODE_DELETE, /**< Node to be deleted */ TREE_MSG_NODE_EDIT, /**< Node to be edited */ TREE_MSG_NODE_LAUNCH /**< Node to be launched */ }; + + +/** + * treeview message + */ struct treeview_node_msg { enum treeview_msg msg; /**< The message type */ union { struct { - bool user; /* True iff delete by user interaction */ + bool user; /**< True iff delete by user interaction */ } delete; struct { - lwc_string *field; /* The field being edited */ - const char *text; /* The proposed new value */ + lwc_string *field; /**< The field being edited */ + const char *text; /**< The proposed new value */ } node_edit; /* Client may call treeview_update_node_* */ struct { browser_mouse_state mouse; /* Button / modifier used */ @@ -85,18 +109,32 @@ struct treeview_node_msg { } data; /**< The message data. */ }; + +/** + * treeview field flags + */ enum treeview_field_flags { - TREE_FLAG_NONE = 0, /**< No flags set */ - TREE_FLAG_ALLOW_EDIT = (1 << 0), /**< Whether allow edit field */ - TREE_FLAG_DEFAULT = (1 << 1), /**< Whether field is default */ - TREE_FLAG_SHOW_NAME = (1 << 2), /**< Whether field name shown */ - TREE_FLAG_COPY_TEXT = (1 << 3) /**< Whether to copy to clipb */ + TREE_FLAG_NONE = 0, /**< No flags set */ + TREE_FLAG_ALLOW_EDIT = (1 << 0), /**< Whether allow edit field */ + TREE_FLAG_DEFAULT = (1 << 1), /**< Whether field is default */ + TREE_FLAG_SHOW_NAME = (1 << 2), /**< Whether field name shown */ + TREE_FLAG_COPY_TEXT = (1 << 3), /**< Whether to copy to clipb */ + TREE_FLAG_SEARCHABLE = (1 << 4), /**< Whether field is searchable */ }; + + +/** + * Treeview field description + */ struct treeview_field_desc { lwc_string *field; /**< A treeview field name */ enum treeview_field_flags flags; /**< Flags for field */ -}; /**< Treeview field description */ +}; + +/** + * Treeview field data + */ struct treeview_field_data { lwc_string *field; /**< Field name */ const char *value; /**< Field value */ @@ -104,10 +142,14 @@ struct treeview_field_data { }; +/** + * Client callbacks for events concerning nodes + */ struct treeview_callback_table { nserror (*folder)(struct treeview_node_msg msg, void *data); nserror (*entry)(struct treeview_node_msg msg, void *data); -}; /**< Client callbacks for events concerning nodes */ +}; + /** * Prepare treeview module for treeview usage @@ -116,6 +158,7 @@ struct treeview_callback_table { */ nserror treeview_init(void); + /** * Finalise the treeview module (all treeviews must have been destroyed first) * @@ -123,9 +166,18 @@ nserror treeview_init(void); */ nserror treeview_fini(void); + /** * Create a treeview * + * The fields array order is as follows (N = n_fields): + * + * fields[0] Main field for entries (shown when not expanded) + * fields[1]...fields[N-2] Additional fields for entries + * fields[N-1] Field for folder nodes + * + * So fields[0] and fields[N-1] have TREE_FLAG_DEFAULT set. + * * \param tree Returns created treeview object * \param callbacks Treeview client node event callbacks * \param n_fields Number of treeview fields (see description) @@ -134,20 +186,13 @@ nserror treeview_fini(void); * \param cw The core_window in which the treeview is shown * \param flags Treeview behaviour flags * \return NSERROR_OK on success, appropriate error otherwise - * - * The fields array order is as follows (N = n_fields): - * - * fields[0] Main field for entries (shown when not expanded) - * fields[1]...fields[N-2] Additional fields for entries - * fields[N-1] Field for folder nodes - * - * So fields[0] and fields[N-1] have TREE_FLAG_DEFAULT set. */ nserror treeview_create(treeview **tree, - const struct treeview_callback_table *callbacks, - int n_fields, struct treeview_field_desc fields[], - const struct core_window_callback_table *cw_t, - struct core_window *cw, treeview_flags flags); + const struct treeview_callback_table *callbacks, + int n_fields, struct treeview_field_desc fields[], + const struct core_window_callback_table *cw_t, + struct core_window *cw, treeview_flags flags); + /** * Attach a treeview to a corewindow. @@ -160,45 +205,50 @@ nserror treeview_create(treeview **tree, * \return NSERROR_OK on success, appropriate error otherwise */ nserror treeview_cw_attach(treeview *tree, - const struct core_window_callback_table *cw_t, - struct core_window *cw); + const struct core_window_callback_table *cw_t, + struct core_window *cw); + /** * Detach a treeview from a corewindow * - * \param tree Treeview object + * \param tree Treeview object * \return NSERROR_OK on success, appropriate error otherwise */ nserror treeview_cw_detach(treeview *tree); + /** * Destroy a treeview object * - * \param tree Treeview object to destroy - * \return NSERROR_OK on success, appropriate error otherwise - * * Will emit folder and entry deletion msg callbacks for all nodes in treeview. + * + * \param tree Treeview object to destroy + * \return NSERROR_OK on success, appropriate error otherwise */ nserror treeview_destroy(treeview *tree); + /** - * Find a releation for node creation. - * - * \param tree Treeview object in which to create folder - * \param relation Existing node to insert as relation of, or NULL - * \param rel Folder's relationship to relation - * \param at_y Iff true, insert at y-offest - * \param y Y-offset in px from top of hotlist. Ignored if (!at_y). - * \return NSERROR_OK on success, appropriate error otherwise + * Find a relation for node creation. * * If at_y is set, we find a relation that will put the created node at that * position. * * If at_y is unset, we find a relation that would put the node below the first * selected node, or at the end of the treeview if no nodes selected. + * + * \param tree Treeview object in which to create folder + * \param relation Existing node to insert as relation of, or NULL + * \param rel Folder's relationship to relation + * \param at_y Iff true, insert at y-offset + * \param y Y-offset in px from top of hotlist. Ignored if (!at_y). + * \return NSERROR_OK on success, appropriate error otherwise */ nserror treeview_get_relation(treeview *tree, treeview_node **relation, - enum treeview_relationship *rel, bool at_y, int y); + enum treeview_relationship *rel, + bool at_y, int y); + /** * Create a folder node in given treeview @@ -217,11 +267,13 @@ nserror treeview_get_relation(treeview *tree, treeview_node **relation, * If relation is NULL, will insert as child of root node. */ nserror treeview_create_node_folder(treeview *tree, - treeview_node **folder, - treeview_node *relation, - enum treeview_relationship rel, - const struct treeview_field_data *field, - void *data, treeview_node_options_flags flags); + treeview_node **folder, + treeview_node *relation, + enum treeview_relationship rel, + const struct treeview_field_data *field, + void *data, + treeview_node_options_flags flags); + /** * Create an entry node in given treeview @@ -240,11 +292,13 @@ nserror treeview_create_node_folder(treeview *tree, * If relation is NULL, will insert as child of root node. */ nserror treeview_create_node_entry(treeview *tree, - treeview_node **entry, - treeview_node *relation, - enum treeview_relationship rel, - const struct treeview_field_data fields[], - void *data, treeview_node_options_flags flags); + treeview_node **entry, + treeview_node *relation, + enum treeview_relationship rel, + const struct treeview_field_data fields[], + void *data, + treeview_node_options_flags flags); + /** * Update an folder node in given treeview @@ -258,9 +312,10 @@ nserror treeview_create_node_entry(treeview *tree, * Field name must match name past in treeview_create fields[N-1]. */ nserror treeview_update_node_folder(treeview *tree, - treeview_node *folder, - const struct treeview_field_data *field, - void *data); + treeview_node *folder, + const struct treeview_field_data *field, + void *data); + /** * Update an entry node in given treeview @@ -274,9 +329,9 @@ nserror treeview_update_node_folder(treeview *tree, * Fields array names must match names past in treeview_create fields[0...N-2]. */ nserror treeview_update_node_entry(treeview *tree, - treeview_node *entry, - const struct treeview_field_data fields[], - void *data); + treeview_node *entry, + const struct treeview_field_data fields[], + void *data); /** @@ -289,12 +344,19 @@ nserror treeview_update_node_entry(treeview *tree, * \return NSERROR_OK on success, or appropriate error otherwise */ typedef nserror (*treeview_walk_cb)(void *ctx, void *node_data, - enum treeview_node_type type, bool *abort); + enum treeview_node_type type, bool *abort); + /** * Walk (depth first) a treeview subtree, calling a callback at each node of * required type. * + * Example usage: To export a treeview as XML, XML elements can be opened in + * enter_cb, and closed in leave_cb. + * + * Note, if deleting returned node in enter_cb, the walk must be terminated by + * setting abort to true. + * * \param tree Treeview object to walk * \param root Root node to walk tree from (or NULL for tree root) * \param enter_cb Function to call on entering nodes, or NULL @@ -302,16 +364,11 @@ typedef nserror (*treeview_walk_cb)(void *ctx, void *node_data, * \param ctx Client context, passed back to callback function * \param type The node type(s) of interest * \return NSERROR_OK on success, or appropriate error otherwise - * - * Example usage: To export a treeview as XML, XML elements can be opened in - * enter_cb, and closed in leave_cb. - * - * Note, if deleting returned node in enter_cb, the walk must be terminated by - * setting abort to true. */ nserror treeview_walk(treeview *tree, treeview_node *root, - treeview_walk_cb enter_cb, treeview_walk_cb leave_cb, - void *ctx, enum treeview_node_type type); + treeview_walk_cb enter_cb, treeview_walk_cb leave_cb, + void *ctx, enum treeview_node_type type); + /** * Delete a treeview node @@ -324,7 +381,8 @@ nserror treeview_walk(treeview *tree, treeview_node *root, * Will emit folder or entry deletion msg callback. */ nserror treeview_delete_node(treeview *tree, treeview_node *n, - treeview_node_options_flags flags); + treeview_node_options_flags flags); + /** * Expand a treeview node @@ -335,6 +393,7 @@ nserror treeview_delete_node(treeview *tree, treeview_node *n, */ nserror treeview_node_expand(treeview *tree, treeview_node *node); + /** * Contract a treeview node * @@ -344,6 +403,7 @@ nserror treeview_node_expand(treeview *tree, treeview_node *node); */ nserror treeview_node_contract(treeview *tree, treeview_node *node); + /** * Expand a treeview's nodes * @@ -353,6 +413,7 @@ nserror treeview_node_contract(treeview *tree, treeview_node *node); */ nserror treeview_expand(treeview *tree, bool only_folders); + /** * Contract a treeview's nodes * @@ -362,6 +423,7 @@ nserror treeview_expand(treeview *tree, bool only_folders); */ nserror treeview_contract(treeview *tree, bool all); + /** * Redraw a treeview object * @@ -372,7 +434,8 @@ nserror treeview_contract(treeview *tree, bool all); * \param ctx Current redraw context */ void treeview_redraw(treeview *tree, int x, int y, struct rect *clip, - const struct redraw_context *ctx); + const struct redraw_context *ctx); + /** * Key press handling for treeviews. @@ -383,6 +446,7 @@ void treeview_redraw(treeview *tree, int x, int y, struct rect *clip, */ bool treeview_keypress(treeview *tree, uint32_t key); + /** * Handles all kinds of mouse action * @@ -392,16 +456,18 @@ bool treeview_keypress(treeview *tree, uint32_t key); * \param y Y coordinate */ void treeview_mouse_action(treeview *tree, - browser_mouse_state mouse, int x, int y); + browser_mouse_state mouse, int x, int y); + /** * Determine whether treeview has a selection * - * \param tree Treeview object to delete node from + * \param tree Treeview object to delete node from * \return true iff treeview has a selection */ bool treeview_has_selection(treeview *tree); + /** * Get the first selected node * @@ -410,19 +476,21 @@ bool treeview_has_selection(treeview *tree); * \return node type of first selected node. */ enum treeview_node_type treeview_get_selection(treeview *tree, - void **node_data); + void **node_data); + /** * Edit the first selected node * - * \param tree Treeview object to edit selected node in + * \param tree Treeview object to edit selected node in */ void treeview_edit_selection(treeview *tree); + /** * Find current height of a treeview * - * \param tree Treeview object to find height of + * \param tree Treeview object to find height of * \return height of treeview in px */ int treeview_get_height(treeview *tree); diff --git a/desktop/version.c b/desktop/version.c index 6126c5b99..bbe759f2e 100644 --- a/desktop/version.c +++ b/desktop/version.c @@ -20,11 +20,11 @@ #include "desktop/version.h" -const char * const netsurf_version = "3.7 (Dev" +const char * const netsurf_version = "3.8 (Dev" #if defined(CI_BUILD) " CI #" CI_BUILD #endif ")" ; const int netsurf_version_major = 3; -const int netsurf_version_minor = 7; +const int netsurf_version_minor = 8; |