From 2affb76944a4cd83a2ff6722c3150abbb972f37d Mon Sep 17 00:00:00 2001 From: Richard Wilson Date: Thu, 3 Feb 2005 13:18:22 +0000 Subject: [project @ 2005-02-03 13:18:22 by rjw] Implementation of URL suggestion svn path=/import/netsurf/; revision=1488 --- content/fetchcache.c | 8 +- content/url_store.c | 475 ++++++++++++++++++++++++++++++++++++++++++ content/url_store.h | 39 ++++ desktop/browser.c | 6 + makefile | 4 +- riscos/dialog.c | 7 +- riscos/gui.c | 29 ++- riscos/gui.h | 3 +- riscos/hotlist.c | 5 - riscos/sprite.h | 2 + riscos/url_complete.c | 558 ++++++++++++++++++++++++++++++++++++++++++++++++++ riscos/url_complete.h | 26 +++ riscos/window.c | 30 ++- utils/url.c | 2 +- 14 files changed, 1174 insertions(+), 20 deletions(-) create mode 100644 content/url_store.c create mode 100644 content/url_store.h create mode 100644 riscos/url_complete.c create mode 100644 riscos/url_complete.h diff --git a/content/fetchcache.c b/content/fetchcache.c index 327a32613..4714d8002 100644 --- a/content/fetchcache.c +++ b/content/fetchcache.c @@ -22,6 +22,7 @@ #include "netsurf/content/content.h" #include "netsurf/content/fetchcache.h" #include "netsurf/content/fetch.h" +#include "netsurf/content/url_store.h" #include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" #include "netsurf/utils/url.h" @@ -74,7 +75,7 @@ struct content * fetchcache(const char *url, struct content *c; char *url1; char *hash; - + if ((url1 = strdup(url)) == NULL) return NULL; @@ -210,6 +211,7 @@ void fetchcache_go(struct content *content, char *referer, void fetchcache_callback(fetch_msg msg, void *p, const char *data, unsigned long size) { + struct url_content *url_content; bool res; struct content *c = p; content_type type; @@ -279,6 +281,10 @@ void fetchcache_callback(fetch_msg msg, void *p, const char *data, break; case FETCH_FINISHED: + url_content = url_store_find(c->url); + if (url_content) + url_content->requests++; + LOG(("FETCH_FINISHED")); c->fetch = 0; content_set_status(c, messages_get("Converting"), diff --git a/content/url_store.c b/content/url_store.c new file mode 100644 index 000000000..5d83c3751 --- /dev/null +++ b/content/url_store.c @@ -0,0 +1,475 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2005 Richard Wilson + */ + +/** \file + * Central repository for URL data (implementation). + */ + +#include +#include +#include +#include +#include +#include "netsurf/content/url_store.h" +#include "netsurf/utils/url.h" +#include "netsurf/utils/log.h" + + +#define ITERATIONS_BEFORE_TEST 32 +#define MAXIMUM_URL_LENGTH 1024 + +struct hostname_data { + char *hostname; /** Hostname (lowercase) */ + int hostname_length; /** Length of hostname */ + struct url_data *url; /** URLs for this host */ + struct hostname_data *previous; /** Previous hostname */ + struct hostname_data *next; /** Next hostname */ +}; + +static struct hostname_data *url_store_hostnames = NULL; + +static struct hostname_data *url_store_find_hostname(const char *url); +static struct hostname_data *url_store_match_hostname(const char *url, + struct hostname_data *previous); + + +/** + * Returns the hostname data for the specified URL. If no hostname + * data is currently available then it is created. + * + * \param url the url to find hostname data for + * \return the current hostname data, or NULL on error + */ +static struct hostname_data *url_store_find_hostname(const char *url) { + struct hostname_data *search; + struct hostname_data *result; + url_func_result res; + char *hostname; + int hostname_length; + int compare; + int fast_exit_counter = ITERATIONS_BEFORE_TEST; + + assert(url); + + res = url_host(url, &hostname); + if (res != URL_FUNC_OK) + return NULL; + hostname_length = strlen(hostname); + + /* try to find a matching hostname fairly quickly */ + for (search = url_store_hostnames; search; search = search->next) { + if ((fast_exit_counter <= 0) || + (search->hostname_length == hostname_length)) { + compare = strcmp(hostname, search->hostname); + if (compare == 0) { + free(hostname); + return search; + } else if (compare < 0) + break; + fast_exit_counter = ITERATIONS_BEFORE_TEST; + } else { + fast_exit_counter--; + } + } + + /* no hostname is available: create a new one */ + result = calloc(sizeof(struct hostname_data), 1); + if (!result) + return NULL; + result->hostname = hostname; + result->hostname_length = hostname_length; + + /* simple case: no current hostnames */ + if (!url_store_hostnames) { + url_store_hostnames = result; + return result; + } + + /* worst case scenario: the place we need to link is within the last + * section of the hostname list so we have no reference to work back + * from. rather than slowing with the very common case of searching, + * we take a speed hit for this case and simply move to the very end + * of the hostname list ready to work backwards. */ + if (!search) + for (search = url_store_hostnames; search->next; + search = search->next); + + /* we can now simply scan backwards as we know roughly where we need + * to link to (we either had an early exit from the searching so we + * know we're in the block following where we need to link, or we're + * at the very end of the list as we were in the last block.) */ + while ((search) && (strcmp(hostname, search->hostname) < 0)) + search = search->previous; + + /* simple case: our new hostname is the first in the list */ + if (!search) { + result->next = url_store_hostnames; + url_store_hostnames->previous = result; + url_store_hostnames = result; + return result; + } + + /* general case: link in after the found hostname */ + result->previous = search; + result->next = search->next; + if (search->next) + search->next->previous = result; + search->next = result; + return result; +} + + +/** + * Returns the url data for the specified URL. If no url + * data is currently available then it is created. + * + * \param url a normalized url to find hostname data for + * \return the current hostname data, or NULL on error + */ +struct url_content *url_store_find(const char *url) { + struct hostname_data *hostname_data; + struct url_data *search; + struct url_data *result; + int url_length; + int compare; + int fast_exit_counter = ITERATIONS_BEFORE_TEST; + + assert(url); + + /* find the corresponding hostname data */ + hostname_data = url_store_find_hostname(url); + if (!hostname_data) + return NULL; + + /* move to the start of the leafname */ + url_length = strlen(url); + + /* try to find a matching url fairly quickly */ + for (search = hostname_data->url; search; search = search->next) { + if ((fast_exit_counter <= 0) || + (search->url_length == url_length)) { + compare = strcmp(url, search->url); + if (compare == 0) + return &search->data; + else if (compare < 0) + break; + fast_exit_counter = ITERATIONS_BEFORE_TEST; + } else { + fast_exit_counter--; + } + } + + /* no URL is available: create a new one */ + result = calloc(sizeof(struct url_data), 1); + if (!result) + return NULL; + result->url = malloc(url_length + 1); + if (!result->url) { + free(result); + return NULL; + } + strcpy(result->url, url); + result->url_length = url_length; + result->data.requests = 0; + result->data.visits = 0; + result->parent = hostname_data; + + /* simple case: no current URLs */ + if (!hostname_data->url) { + hostname_data->url = result; + return &result->data; + } + + /* worst case scenario: the place we need to link is within the last + * section of the URL list so we have no reference to work back + * from. rather than slowing with the very common case of searching, + * we take a speed hit for this case and simply move to the very end + * of the URL list ready to work backwards. */ + if (!search) + for (search = hostname_data->url; search->next; + search = search->next); + + /* we can now simply scan backwards as we know roughly where we need + * to link to (we either had an early exit from the searching so we + * know we're in the block following where we need to link, or we're + * at the very end of the list as we were in the last block.) */ + while ((search) && (strcmp(url, search->url) < 0)) + search = search->previous; + + /* simple case: our new hostname is the first in the list */ + if (!search) { + result->next = hostname_data->url; + hostname_data->url->previous = result; + hostname_data->url = result; + return &result->data; + } + + /* general case: link in after the found hostname */ + result->previous = search; + result->next = search->next; + if (search->next) + search->next->previous = result; + search->next = result; + return &result->data; +} + + +/** + * Returns the next hostname that matches a part of the specified URL. + * + * \param url a normalized url to find the next match for + * \param current the current hostname to search forward from, or NULL + * \return the next matching hostname, or NULL + */ +static struct hostname_data *url_store_match_hostname(const char *url, + struct hostname_data *current) { + url_func_result res; + char *hostname; + int hostname_length; + int compare; + bool www_test; + + assert(url); + + res = url_host(url, &hostname); + if (res != URL_FUNC_OK) + return NULL; + hostname_length = strlen(hostname); + www_test = strncmp(hostname, "www.", 4); + + /* advance to the next hostname */ + if (!current) + current = url_store_hostnames; + else + current = current->next; + + /* skip past hostname data without URLs */ + for (; current && (!current->url); current = current->next); + + while (current) { + if (current->hostname_length >= hostname_length) { + compare = strncmp(hostname, current->hostname, + hostname_length); + if (compare == 0) { + free(hostname); + return current; + } else if ((compare < 0) && !www_test) + break; + } + /* special case: if hostname is not www then try it */ + if (www_test && ((current->hostname_length - 4) >= hostname_length) && + (!strncmp(current->hostname, "www.", 4)) && + (!strncmp(hostname, current->hostname + 4, + hostname_length))) { + free(hostname); + return current; + } + + /* move to next hostname with URLs */ + current = current->next; + for (; current && (!current->url); current = current->next); + } + + free(hostname); + return NULL; +} + + + +/** + * Returns the complete URL for the next matched stored URL. + * + * \param url a normalized url to find the next match for + * \param reference internal reference (NULL for first call) + * \return the next URL that matches + */ +char *url_store_match(const char *url, struct url_data **reference) { + struct hostname_data *hostname; + struct url_data *search = NULL; + char *scheme; + int scheme_length; + int url_length; + url_func_result res; + bool www_test; + + assert(url); + + if (!url_store_hostnames) + return NULL; + + /* find the first URL, not necessarily matching */ + if (!*reference) { + hostname = url_store_match_hostname(url, NULL); + if (!hostname) + return NULL; + } else { + search = *reference; + hostname = search->parent; + } + + res = url_scheme(url, &scheme); + if (res != URL_FUNC_OK) + return NULL; + scheme_length = strlen(scheme); + url_length = strlen(url); + www_test = (!strcmp(scheme, "http") && + strncmp(url + 4 + 3, "www.", 4)); /* 'http' + '://' */ + + /* work through all our strings, ignoring the scheme and 'www.' */ + while (hostname) { + + /* get the next URL to test */ + if (!search) + search = hostname->url; + else + search = search->next; + + /* loop past end of list, or search */ + if (!search) { + hostname = url_store_match_hostname(url, hostname); + if (!hostname) + return NULL; + } else if ((search->data.visits > 0) && (search->data.requests > 0)){ + /* straight match */ + if ((search->url_length >= url_length) && + (!strncmp(search->url, url, url_length))) { + free(scheme); + *reference = search; + return search->url; + } + /* try with 'www.' inserted after the scheme */ + if (www_test && ((search->url_length - 4) >= url_length) && + (!strncmp(search->url, scheme, scheme_length)) && + (!strncmp(search->url + scheme_length + 3, "www.", 4)) && + (!strncmp(search->url + scheme_length + 7, + url + scheme_length + 3, + url_length - scheme_length - 3))) { + free(scheme); + *reference = search; + return search->url; + } + } + } + + free(scheme); + return NULL; +} + + +/** + * Converts a text string into one suitable for URL matching. + * + * \param text the text to search with + * \return URL matching string allocated on heap, or NULL on error + */ +char *url_store_match_string(const char *text) { + url_func_result res; + char *url; + + assert(text); + + res = url_normalize(text, &url); + if (res != URL_FUNC_OK) + return NULL; + + /* drop the '/' from the end if it was added when normalizing */ + if ((url[strlen(url) - 1] == '/') && (text[strlen(text) - 1] != '/')) + url[strlen(url) - 1] = '\0'; + return url; +} + + +/** + * Loads the current contents of the URL store to disk + * + * \param file the file to load options from + */ +void url_store_load(const char *file) { + struct url_content *url; + char s[MAXIMUM_URL_LENGTH]; + FILE *fp; + + fp = fopen(file, "r"); + if (!fp) { + LOG(("Failed to open file '%s' for reading", file)); + return; + } + + if (!fgets(s, MAXIMUM_URL_LENGTH, fp)) + return; + if (strncmp(s, "100", 3)) { + LOG(("Invalid header")); + return; + } + + while (fgets(s, MAXIMUM_URL_LENGTH, fp)) { + if (s[strlen(s) - 1] == '\n') + s[strlen(s) - 1] = '\0'; + url = url_store_find(s); + if (!url) + break; + if (!fgets(s, MAXIMUM_URL_LENGTH, fp)) + break; + url->visits = atoi(s); + if (!fgets(s, MAXIMUM_URL_LENGTH, fp)) + break; + url->requests = atoi(s); + } + fclose(fp); +} + + +/** + * Saves the current contents of the URL store to disk + * + * \param file the file to load options from + */ +void url_store_save(const char *file) { + struct hostname_data *search; + struct url_data *url; + FILE *fp; + + fp = fopen(file, "w"); + if (!fp) { + LOG(("Failed to open file '%s' for writing", file)); + return; + } + + /* file format version number */ + fprintf(fp, "100\n"); + for (search = url_store_hostnames; search; search = search->next) { + for (url = search->url; url; url = url->next) { + if (strlen(url->url) < 1024) { + fprintf(fp, "%s\n%i\n%i\n", url->url, + url->data.visits, url->data.requests); + } + } + } + fclose(fp); +} + + +/** + * Dumps the currently stored URLs and hostnames to stderr. + */ +void url_store_dump(void) { + struct hostname_data *search; + struct url_data *url; + + fprintf(stderr, "\nDumping hostname data:\n"); + for (search = url_store_hostnames; search; search = search->next) { + fprintf(stderr, "\n"); + fprintf(stderr, search->hostname); + fprintf(stderr, ":\n"); + for (url = search->url; url; url = url->next) { + fprintf(stderr, " - "); + fprintf(stderr, url->url); + fprintf(stderr, "\n"); + } + } + fprintf(stderr, "\nEnd of hostname data.\n\n"); +} diff --git a/content/url_store.h b/content/url_store.h new file mode 100644 index 000000000..b152508af --- /dev/null +++ b/content/url_store.h @@ -0,0 +1,39 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2005 Richard Wilson + */ + +/** \file + * Central repository for URL data (interface). + */ + +#ifndef _NETSURF_CONTENT_URLSTORE_H_ +#define _NETSURF_CONTENT_URLSTORE_H_ + +struct url_content { + int visits; /** Number of times visited */ + int requests; /** Number of times requested */ +}; + +struct url_data { + struct url_content data; /** Stored URL content data */ + char *url; /** URL (including hostname) */ + int url_length; /** Length of URL (including hostname) */ + struct url_data *previous; /** Previous URL */ + struct url_data *next; /** Next URL */ + struct hostname_data *parent; /** Parent hostname data */ +}; + + +struct url_content *url_store_find(const char *url); +char *url_store_match(const char *url, struct url_data **reference); +char *url_store_match_string(const char *text); + +void url_store_load(const char *file); +void url_store_save(const char *file); + +void url_store_dump(void); + +#endif diff --git a/desktop/browser.c b/desktop/browser.c index e4cfb2273..9e17657e1 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -22,6 +22,7 @@ #include "netsurf/utils/config.h" #include "netsurf/content/fetch.h" #include "netsurf/content/fetchcache.h" +#include "netsurf/content/url_store.h" #include "netsurf/css/css.h" #ifdef WITH_AUTH #include "netsurf/desktop/401login.h" @@ -166,6 +167,7 @@ void browser_window_go_post(struct browser_window *bw, const char *url, char *url2; char *hash; url_func_result res; + struct url_content *url_content; LOG(("bw %p, url %s", bw, url)); @@ -194,6 +196,10 @@ void browser_window_go_post(struct browser_window *bw, const char *url, bw->frag_id = strdup(hash+1); } + url_content = url_store_find(url2); + if (url_content) + url_content->visits++; + browser_window_set_status(bw, messages_get("Loading")); bw->history_add = history_add; bw->time0 = clock(); diff --git a/makefile b/makefile index 991d83c97..4f5f324f1 100644 --- a/makefile +++ b/makefile @@ -17,7 +17,7 @@ # "riscos", "riscos_small", "ncos", and "riscos_debug" can be compiled under # RISC OS, or cross-compiled using gccsdk. -OBJECTS_COMMON = content.o fetch.o fetchcache.o # content/ +OBJECTS_COMMON = content.o fetch.o fetchcache.o url_store.o # content/ OBJECTS_COMMON += css.o css_enum.o parser.o ruleset.o scanner.o # css/ OBJECTS_COMMON += box.o form.o html.o html_redraw.o layout.o \ list.o textplain.o # render/ @@ -35,7 +35,7 @@ OBJECTS_RISCOS += 401login.o bitmap.o buffer.o debugwin.o \ save.o save_complete.o save_draw.o save_text.o \ schedule.o search.o sprite.o textselection.o theme.o \ theme_install.o thumbnail.o treeview.o ufont.o uri.o \ - url_protocol.o wimp.o window.o # riscos/ + url_complete.o url_protocol.o wimp.o window.o # riscos/ # OBJECTS_RISCOS += memdebug.o OBJECTS_NCOS = $(OBJECTS_RISCOS) diff --git a/riscos/dialog.c b/riscos/dialog.c index c6cb54fc7..223c268d0 100644 --- a/riscos/dialog.c +++ b/riscos/dialog.c @@ -43,7 +43,7 @@ wimp_w dialog_info, dialog_saveas, dialog_config, dialog_config_br, dialog_zoom, dialog_pageinfo, dialog_objinfo, dialog_tooltip, dialog_warning, dialog_config_th_pane, dialog_debug, dialog_folder, dialog_entry, dialog_search, dialog_print, - dialog_config_font, dialog_config_image; + dialog_config_font, dialog_config_image, dialog_url_complete; static int ro_gui_choices_font_size; static int ro_gui_choices_font_min_size; @@ -140,6 +140,7 @@ void ro_gui_dialog_init(void) dialog_config_font = ro_gui_dialog_create("config_font"); dialog_config_image = ro_gui_dialog_create("config_img"); dialog_theme_install = ro_gui_dialog_create("theme_inst"); + dialog_url_complete = ro_gui_dialog_create("url_suggest"); } @@ -811,12 +812,8 @@ void ro_gui_save_options(void) /* NCOS doesnt have the fancy Universal Boot vars; so select * the path to the choices file based on the build options */ #ifndef NCOS - xosfile_create_dir(".WWW", 0); - xosfile_create_dir(".WWW.NetSurf", 0); options_write(".WWW.NetSurf.Choices"); #else - xosfile_create_dir(".Choices.NetSurf", 0); - xosfile_create_dir(".Choices.NetSurf.Choices", 0); options_write(".Choices.NetSurf.Choices"); #endif } diff --git a/riscos/gui.c b/riscos/gui.c index 131320347..5750119d6 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -32,6 +32,7 @@ #include "oslib/wimp.h" #include "oslib/wimpspriteop.h" #include "oslib/uri.h" +#include "netsurf/content/url_store.h" #include "netsurf/utils/config.h" #include "netsurf/desktop/gui.h" #include "netsurf/desktop/netsurf.h" @@ -57,6 +58,7 @@ #ifdef WITH_URL #include "netsurf/riscos/url_protocol.h" #endif +#include "netsurf/riscos/url_complete.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" @@ -184,6 +186,17 @@ void gui_init(int argc, char** argv) xhourglass_start(1); + /* create our choices directories */ +#ifndef NCOS + xosfile_create_dir(".WWW", 0); + xosfile_create_dir(".WWW.NetSurf", 0); + xosfile_create_dir(".WWW.NetSurf.Themes", 0); +#else + xosfile_create_dir(".Choices.NetSurf", 0); + xosfile_create_dir(".Choices.NetSurf.Choices", 0); + xosfile_create_dir(".Choices.NetSurf.Choices.Themes", 0); +#endif + #ifdef WITH_SAVE_COMPLETE save_complete_init(); #endif @@ -195,6 +208,8 @@ void gui_init(int argc, char** argv) options_read(".Choices.NetSurf.Choices"); #endif ro_gui_choose_language(); + + url_store_load("Choices:WWW.NetSurf.URL"); NETSURF_DIR = getenv("NetSurf$Dir"); if ((length = snprintf(path, sizeof(path), @@ -527,6 +542,7 @@ void gui_init2(int argc, char** argv) void gui_quit(void) { + url_store_save(".WWW.NetSurf.URL"); ro_gui_window_quit(); ro_gui_hotlist_save(); ro_gui_history_quit(); @@ -751,6 +767,8 @@ void ro_gui_null_reason_code(void) if (gui_track_wimp_w == history_window) ro_gui_history_mouse_at(&pointer); + if (gui_track_wimp_w == dialog_url_complete) + ro_gui_url_complete_mouse_at(&pointer); else if (gui_track_gui_window) ro_gui_window_mouse_at(gui_track_gui_window, &pointer); } @@ -766,6 +784,8 @@ void ro_gui_redraw_window_request(wimp_draw *redraw) if (redraw->w == history_window) ro_gui_history_redraw(redraw); + else if (redraw->w == dialog_url_complete) + ro_gui_url_complete_redraw(redraw); else if ((hotlist_tree) && (redraw->w == (wimp_w)hotlist_tree->handle)) ro_gui_tree_redraw(redraw, hotlist_tree); else if ((hotlist_toolbar) && (hotlist_toolbar->toolbar_handle == redraw->w)) @@ -827,8 +847,10 @@ void ro_gui_close_window_request(wimp_close *close) if (close->w == dialog_debug) ro_gui_debugwin_close(); - else if ((g = ro_gui_window_lookup(close->w)) != NULL) + else if ((g = ro_gui_window_lookup(close->w)) != NULL) { + ro_gui_url_complete_close(NULL, 0); browser_window_destroy(g->bw); + } else if ((dw = ro_gui_download_window_lookup(close->w)) != NULL) ro_gui_download_window_destroy(dw); else @@ -866,7 +888,8 @@ void ro_gui_pointer_entering_window(wimp_entering *entering) { gui_track_wimp_w = entering->w; gui_track_gui_window = ro_gui_window_lookup(entering->w); - gui_track = gui_track_gui_window || gui_track_wimp_w == history_window; + gui_track = gui_track_gui_window || gui_track_wimp_w == history_window || + gui_track_wimp_w == dialog_url_complete; } @@ -883,6 +906,8 @@ void ro_gui_mouse_click(wimp_pointer *pointer) ro_gui_icon_bar_click(pointer); else if (pointer->w == history_window) ro_gui_history_click(pointer); + else if (pointer->w == dialog_url_complete) + ro_gui_url_complete_mouse_at(pointer); else if ((hotlist_tree) && (pointer->w == (wimp_w)hotlist_tree->handle)) ro_gui_hotlist_click(pointer); else if (pointer->w == dialog_saveas) diff --git a/riscos/gui.h b/riscos/gui.h index 8d78b411f..7a3264c5e 100644 --- a/riscos/gui.h +++ b/riscos/gui.h @@ -30,7 +30,8 @@ extern wimp_w dialog_info, dialog_saveas, dialog_config, dialog_config_br, dialog_config_prox, dialog_config_th, dialog_zoom, dialog_pageinfo, dialog_objinfo, dialog_tooltip, dialog_warning, dialog_config_th_pane, dialog_debug, dialog_folder, dialog_entry, - dialog_search, dialog_print, dialog_config_font, dialog_theme_install; + dialog_search, dialog_print, dialog_config_font, dialog_theme_install, + dialog_url_complete; extern wimp_w history_window; extern wimp_menu *iconbar_menu, *browser_menu, *combo_menu, *hotlist_menu, *proxyauth_menu, *languages_menu, *toolbar_menu, diff --git a/riscos/hotlist.c b/riscos/hotlist.c index e1303c51e..3edec0ad0 100644 --- a/riscos/hotlist.c +++ b/riscos/hotlist.c @@ -152,11 +152,6 @@ void ro_gui_hotlist_save(void) { if (!hotlist_tree) return; - /* Ensure we have a directory to save to later. - */ - xosfile_create_dir(".WWW", 0); - xosfile_create_dir(".WWW.NetSurf", 0); - /* Save to our file */ options_save_hotlist(hotlist_tree, ".WWW.NetSurf.Hotlist"); diff --git a/riscos/sprite.h b/riscos/sprite.h index 605027635..3824ace43 100644 --- a/riscos/sprite.h +++ b/riscos/sprite.h @@ -12,6 +12,8 @@ #ifndef _NETSURF_RISCOS_SPRITE_H_ #define _NETSURF_RISCOS_SPRITE_H_ +#include + struct content; struct content_sprite_data { diff --git a/riscos/url_complete.c b/riscos/url_complete.c new file mode 100644 index 000000000..2aecfbdb9 --- /dev/null +++ b/riscos/url_complete.c @@ -0,0 +1,558 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2005 Richard Wilson + */ + +/** \file + * GUI URL auto-completion (implementation). + */ + +#include +#include +#include +#include +#include +#include "oslib/wimp.h" +#include "netsurf/content/url_store.h" +#include "netsurf/utils/log.h" +#include "netsurf/riscos/gui.h" +#include "netsurf/riscos/theme.h" +#include "netsurf/riscos/url_complete.h" +#include "netsurf/riscos/wimp.h" +#include "netsurf/utils/utils.h" + +#define MAXIMUM_VISIBLE_LINES 7 + +static char **url_complete_matches = NULL; +static int url_complete_matches_allocated = 0; +static int url_complete_matches_available = 0; +static char *url_complete_matched_string = NULL; +static int url_complete_matches_selection = -1; +static int url_complete_keypress_selection = -1; +static wimp_w url_complete_parent = 0; +static bool url_complete_matches_reset = false; +static char *url_complete_original_url = NULL; + +static char *url_complete_redraw[MAXIMUM_VISIBLE_LINES]; +static char url_complete_icon_null[] = "\0"; +static wimp_icon url_complete_icon; +static int mouse_x; +static int mouse_y; + + +/** + * Handles a keypress for URL completion + * + * \param g the gui_window to update + * \param key the key pressed + */ +bool ro_gui_url_complete_keypress(struct gui_window *g, int key) { + wimp_window_state state; + char **array_extend; + struct url_data *reference = NULL; + char *match_url; + char *url; + char *output; + int i, lines; + int old_selection; + bool ignore_changes = false; + int height; + os_error *error; + bool currently_open; + + /* we must have a toolbar/url bar */ + if ((!g->toolbar) || (!g->toolbar->display_url)) { + ro_gui_url_complete_close(NULL, 0); + return false; + } + + /* if we are currently active elsewhere, remove the previous window */ + currently_open = g->window == url_complete_parent; + if (g->window != url_complete_parent) { + ro_gui_url_complete_close(NULL, 0); + url_complete_parent = g->window; + } + + /* get the text to match */ + url = ro_gui_get_icon_string(g->toolbar->toolbar_handle, ICON_TOOLBAR_URL); + match_url = url_store_match_string(url); + if (!match_url) { + ro_gui_url_complete_close(NULL, 0); + return false; + } + + /* check if we should ignore text changes */ + if (url_complete_keypress_selection >= 0) + ignore_changes = !strcmp(url, + url_complete_matches[url_complete_keypress_selection]); + + /* if the text to match has changed then update it */ + if (!ignore_changes && ((!url_complete_matched_string) || + (strcmp(match_url, url_complete_matched_string)))) { + + /* memorize the current matches */ + lines = MAXIMUM_VISIBLE_LINES; + if (lines > url_complete_matches_available) + lines = url_complete_matches_available; + for (i = 0; i < MAXIMUM_VISIBLE_LINES; i++) + url_complete_redraw[i] = url_complete_matches[i]; + + /* our selection gets wiped */ + error = xwimp_force_redraw(dialog_url_complete, + 0, -(url_complete_matches_selection + 1) * 44, + 65536, -url_complete_matches_selection * 44); + if (error) { + LOG(("xwimp_force_redraw: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + + /* clear our state */ + free(url_complete_original_url); + free(url_complete_matched_string); + url_complete_matched_string = match_url; + url_complete_original_url = NULL; + url_complete_matches_available = 0; + url_complete_matches_selection = -1; + url_complete_keypress_selection = -1; + + /* get some initial memory */ + if (!url_complete_matches) { + url_complete_matches = malloc(64 * sizeof(char *)); + if (!url_complete_matches) { + ro_gui_url_complete_close(NULL, 0); + return false; + } + url_complete_matches_allocated = 64; + } + + /* get all our matches */ + while ((output = url_store_match(match_url, &reference))) { + url_complete_matches_available++; + if (url_complete_matches_available > + url_complete_matches_allocated) { + + array_extend = realloc(url_complete_matches, + (url_complete_matches_allocated + 64) * + sizeof(char *)); + if (!array_extend) { + ro_gui_url_complete_close(NULL, 0); + return false; + } + url_complete_matches = array_extend; + url_complete_matches_allocated += 64; + } + url_complete_matches[url_complete_matches_available - 1] = + output; + + } + + /* update the window */ + state.w = g->window; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return false; + } + url_complete_matches_reset = true; + ro_gui_url_complete_resize(g, (wimp_open *)&state); + url_complete_matches_reset = false; + + /* redraw the relevant bits of the window */ + lines = MAXIMUM_VISIBLE_LINES; + if (lines > url_complete_matches_available) + lines = url_complete_matches_available; + for (i = 0; i < MAXIMUM_VISIBLE_LINES; i++) { + if (url_complete_redraw[i] != url_complete_matches[i]) { + error = xwimp_force_redraw(dialog_url_complete, + 0, -(i + 1) * 44, 65536, -i * 44); + if (error) { + LOG(("xwimp_force_redraw: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + } + } + + } else { + free(match_url); + } + + /* handle keypresses */ + if (!currently_open) + return false; + old_selection = url_complete_matches_selection; + switch (key) { + case wimp_KEY_UP: + url_complete_matches_selection--; + break; + case wimp_KEY_DOWN: + url_complete_matches_selection++; + break; + case wimp_KEY_PAGE_UP: + url_complete_matches_selection -= MAXIMUM_VISIBLE_LINES; + break; + case wimp_KEY_PAGE_DOWN: + url_complete_matches_selection += MAXIMUM_VISIBLE_LINES; + break; + case wimp_KEY_CONTROL | wimp_KEY_UP: + url_complete_matches_selection = 0; + break; + case wimp_KEY_CONTROL | wimp_KEY_DOWN: + url_complete_matches_selection = 65536; + break; + } + if (url_complete_matches_selection > url_complete_matches_available - 1) + url_complete_matches_selection = url_complete_matches_available - 1; + else if (url_complete_matches_selection < -1) + url_complete_matches_selection = -1; + + if (old_selection == url_complete_matches_selection) + return false; + + error = xwimp_force_redraw(dialog_url_complete, + 0, -(old_selection + 1) * 44, 65536, -old_selection * 44); + if (error) { + LOG(("xwimp_force_redraw: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + error = xwimp_force_redraw(dialog_url_complete, + 0, -(url_complete_matches_selection + 1) * 44, + 65536, -url_complete_matches_selection * 44); + if (error) { + LOG(("xwimp_force_redraw: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + if (old_selection == -1) { + free(url_complete_original_url); + url_complete_original_url = malloc(strlen(url) + 1); + if (!url_complete_original_url) + return false; + strcpy(url_complete_original_url, url); + } + if (url_complete_matches_selection == -1) { + ro_gui_set_icon_string(g->toolbar->toolbar_handle, + ICON_TOOLBAR_URL, + url_complete_original_url); + } else { + ro_gui_set_icon_string(g->toolbar->toolbar_handle, + ICON_TOOLBAR_URL, + url_complete_matches[url_complete_matches_selection]); + } + url_complete_keypress_selection = url_complete_matches_selection; + + /* auto-scroll */ + state.w = dialog_url_complete; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return true; + } + if (state.yscroll < -(url_complete_matches_selection * 44)) + state.yscroll = -(url_complete_matches_selection * 44); + height = state.visible.y1 - state.visible.y0; + if (state.yscroll - height > -((url_complete_matches_selection + 1) * 44)) + state.yscroll = -((url_complete_matches_selection + 1) * 44) + height; + error = xwimp_open_window((wimp_open *)(&state)); + if (error) { + LOG(("xwimp_open_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return true; + } + + return true; +} + + +/** + * Move and resize the url completion window to match the toolbar. + * + * \param g the gui_window to update + * \param open the wimp_open request (updated on exit) + */ +void ro_gui_url_complete_resize(struct gui_window *g, wimp_open *open) { + os_box extent = { 0, 0, 0, 0 }; + wimp_icon_state url_state; + wimp_window_state toolbar_state; + wimp_window_state state; + os_error *error; + int lines; + int scroll_v = 0; + + /* if we the URL completion isn't for our window, or there is no toolbar, + * or there is no URL bar shown, or there are no URL matches, close it */ + if ((open->w != url_complete_parent) || (!g->toolbar) || + (!g->toolbar->display_url) || + (url_complete_matches_available == 0)) { + ro_gui_url_complete_close(NULL, 0); + return; + } + + /* get our current auto-complete window state for the scroll values */ + state.w = dialog_url_complete; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + if (url_complete_matches_reset) + state.yscroll = 0; + + /* move the window to the correct position */ + toolbar_state.w = g->toolbar->toolbar_handle; + error = xwimp_get_window_state(&toolbar_state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + url_state.w = g->toolbar->toolbar_handle; + url_state.i = ICON_TOOLBAR_URL; + error = xwimp_get_icon_state(&url_state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + lines = url_complete_matches_available; + extent.y0 = -(lines * 44); + extent.x1 = 65536; + error = xwimp_set_extent(dialog_url_complete, &extent); + if (error) { + LOG(("xwimp_set_extent: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + state.next = open->next; + state.flags &= ~wimp_WINDOW_VSCROLL; + state.flags &= ~(4095 << 16); /* clear bits 16-27 */ + if (lines > MAXIMUM_VISIBLE_LINES) { + lines = MAXIMUM_VISIBLE_LINES; + scroll_v = ro_get_vscroll_width(NULL) - 2; + state.flags |= wimp_WINDOW_VSCROLL; + } + state.visible.x0 = open->visible.x0 + 2 + url_state.icon.extent.x0; + state.visible.x1 = open->visible.x0 - 2 + url_state.icon.extent.x1 - scroll_v; + state.visible.y1 = open->visible.y1 - url_state.icon.extent.y1 + 2; + state.visible.y0 = state.visible.y1 - (lines * 44); + if (state.visible.x1 > toolbar_state.visible.x1) + state.visible.x1 = toolbar_state.visible.x1; + if (state.visible.x1 - state.visible.x0 - scroll_v < 0) { + error = xwimp_close_window(dialog_url_complete); + if (error) { + LOG(("xwimp_close_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + } else { + error = xwimp_open_window_nested_with_flags(&state, (wimp_w)-1, 0); + if (error) { + LOG(("xwimp_open_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + open->next = dialog_url_complete; + } +} + + +/** + * Try to close the current url completion window + * + * \param g the gui_window the user clicked on (or NULL to forcibly close) + * \param i the icon the user clicked on to prompt the close + * \return whether the window was closed + */ +bool ro_gui_url_complete_close(struct gui_window *g, wimp_i i) { + os_error *error; + + if ((g && (i == ICON_TOOLBAR_URL) && (g->window == url_complete_parent))) + return false; + + free(url_complete_matches); + free(url_complete_matched_string); + free(url_complete_original_url); + url_complete_matches = NULL; + url_complete_matched_string = NULL; + url_complete_original_url = NULL; + url_complete_matches_allocated = 0; + url_complete_matches_available = 0; + url_complete_keypress_selection = -1; + url_complete_matches_selection = -1; + url_complete_parent = 0; + + error = xwimp_close_window(dialog_url_complete); + if (error) { + LOG(("xwimp_close_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + return true; +} + + +/** + * Redraws a section of the URL completion window + * + * \param redraw the area to redraw + * \param tree the tree to redraw + */ +void ro_gui_url_complete_redraw(wimp_draw *redraw) { + osbool more; + os_error *error; + int clip_y0, clip_y1, origin_y; + int first_line, last_line, line; + + /* initialise our icon */ + url_complete_icon.flags = wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED | + wimp_ICON_TEXT | wimp_ICON_FILLED | + (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | + (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT); + url_complete_icon.extent.x0 = 0; + url_complete_icon.extent.x1 = 16384; + url_complete_icon.data.indirected_text.validation = url_complete_icon_null; + + /* redraw */ + more = wimp_redraw_window(redraw); + while (more) { + origin_y = redraw->box.y1 - redraw->yscroll; + clip_y0 = redraw->clip.y0 - origin_y; + clip_y1 = redraw->clip.y1 - origin_y; + + first_line = (-clip_y1) / 44; + last_line = (-clip_y0 + 43) / 44; + + for (line = first_line; line < last_line; line++) { + if (line == url_complete_matches_selection) + url_complete_icon.flags |= wimp_ICON_SELECTED; + else + url_complete_icon.flags &= ~wimp_ICON_SELECTED; + url_complete_icon.extent.y1 = -line * 44; + url_complete_icon.extent.y0 = -(line + 1) * 44; + url_complete_icon.data.indirected_text.text = + url_complete_matches[line]; + url_complete_icon.data.indirected_text.size = + strlen(url_complete_matches[line]); + error = xwimp_plot_icon(&url_complete_icon); + if (error) { + LOG(("xwimp_plot_icon: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + } + more = wimp_get_rectangle(redraw); + } +} + + +/** + * Handle mouse movements/clicks over the URL completion window. + */ +void ro_gui_url_complete_mouse_at(wimp_pointer *pointer) { + wimp_window_state state; + os_error *error; + int selection, old_selection; + struct gui_window *g; + char *url; + + if ((mouse_x == pointer->pos.x) && (mouse_y == pointer->pos.y) && + (pointer->buttons == 0)) + return; + mouse_x = pointer->pos.x; + mouse_y = pointer->pos.y; + + state.w = dialog_url_complete; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + selection = (state.visible.y1 - pointer->pos.y - state.yscroll) / 44; + if (selection != url_complete_matches_selection) { + if (url_complete_matches_selection == -1) { + g = ro_gui_window_lookup(url_complete_parent); + if (!g) + return; + url = ro_gui_get_icon_string(g->toolbar->toolbar_handle, + ICON_TOOLBAR_URL); + free(url_complete_original_url); + url_complete_original_url = malloc(strlen(url) + 1); + if (!url_complete_original_url) + return; + strcpy(url_complete_original_url, url); + } + old_selection = url_complete_matches_selection; + url_complete_matches_selection = selection; + error = xwimp_force_redraw(dialog_url_complete, + 0, -(old_selection + 1) * 44, 65536, -old_selection * 44); + if (error) { + LOG(("xwimp_force_redraw: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + error = xwimp_force_redraw(dialog_url_complete, + 0, -(url_complete_matches_selection + 1) * 44, + 65536, -url_complete_matches_selection * 44); + if (error) { + LOG(("xwimp_force_redraw: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + } + + /* clicks */ + if ((pointer->buttons == wimp_CLICK_SELECT) || + (pointer->buttons == wimp_CLICK_ADJUST)) { + g = ro_gui_window_lookup(url_complete_parent); + if (!g) + return; + ro_gui_set_icon_string(g->toolbar->toolbar_handle, + ICON_TOOLBAR_URL, + url_complete_matches[url_complete_matches_selection]); + browser_window_go(g->bw, + url_complete_matches[url_complete_matches_selection], + 0); + ro_gui_url_complete_close(NULL, 0); + } + +} + + +/** + * Dumps all matching URLs to stderr. + */ +void url_complete_dump_matches(const char *url) { + char *match_url; + struct url_data *reference = NULL; + char *output; + + match_url = url_store_match_string(url); + if (!match_url) + return; + + fprintf(stderr, "\nDumping matches for '%s' ('%s'):\n", url, match_url); + while ((output = url_store_match(match_url, &reference))) { + fprintf(stderr, " - "); + fprintf(stderr, output); + fprintf(stderr, "\n"); + } + fprintf(stderr, "\nEnd of matches.\n\n"); + free(match_url); +} diff --git a/riscos/url_complete.h b/riscos/url_complete.h new file mode 100644 index 000000000..fa70efc14 --- /dev/null +++ b/riscos/url_complete.h @@ -0,0 +1,26 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2005 Richard Wilson + */ + +/** \file + * Central repository for URL data (interface). + */ + +#ifndef _NETSURF_RISCOS_URLCOMPLETE_H_ +#define _NETSURF_RISCOS_URLCOMPLETE_H_ + +#include +#include "netsurf/riscos/gui.h" + +bool ro_gui_url_complete_keypress(struct gui_window *g, int key); +void ro_gui_url_complete_resize(struct gui_window *g, wimp_open *open); +bool ro_gui_url_complete_close(struct gui_window *g, wimp_i i); +void ro_gui_url_complete_redraw(wimp_draw *redraw); +void ro_gui_url_complete_mouse_at(wimp_pointer *pointer); + +void url_complete_dump_matches(const char *url); + +#endif diff --git a/riscos/window.c b/riscos/window.c index 87d63d86a..50c103739 100644 --- a/riscos/window.c +++ b/riscos/window.c @@ -25,6 +25,7 @@ #include "oslib/wimpspriteop.h" #include "netsurf/utils/config.h" #include "netsurf/content/content.h" +#include "netsurf/content/url_store.h" #include "netsurf/css/css.h" #include "netsurf/desktop/plotters.h" #include "netsurf/render/box.h" @@ -35,6 +36,7 @@ #include "netsurf/riscos/theme.h" #include "netsurf/riscos/thumbnail.h" #include "netsurf/riscos/treeview.h" +#include "netsurf/riscos/url_complete.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" #include "netsurf/utils/url.h" @@ -929,6 +931,9 @@ void ro_gui_window_open(struct gui_window *g, wimp_open *open) } + /* first resize stops any flickering by making the URL window on top */ + ro_gui_url_complete_resize(g, open); + error = xwimp_open_window(open); if (error) { LOG(("xwimp_open_window: 0x%x: %s", @@ -957,8 +962,11 @@ void ro_gui_window_open(struct gui_window *g, wimp_open *open) g->old_height = height; } - if (g->toolbar) + if (g->toolbar) { ro_gui_theme_process_toolbar(g->toolbar, -1); + /* second resize updates to the new URL bar width */ + ro_gui_url_complete_resize(g, open); + } } @@ -1090,6 +1098,9 @@ void ro_gui_toolbar_click(struct gui_window *g, wimp_pointer *pointer) /* Store the toolbar */ current_toolbar = g->toolbar; + + /* try to close url-completion */ + ro_gui_url_complete_close(g, pointer->i); /* Handle Menu clicks */ @@ -1232,6 +1243,9 @@ void ro_gui_window_click(struct gui_window *g, wimp_pointer *pointer) assert(g); + /* try to close url-completion */ + ro_gui_url_complete_close(g, pointer->i); + xosbyte1(osbyte_SCAN_KEYBOARD, 0 ^ 0x80, 0, &shift); state.w = pointer->w; @@ -1486,6 +1500,10 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar) ro_gui_view_source(content); return true; + case wimp_KEY_CONTROL + wimp_KEY_F8: /* Dump url_store. */ + url_store_dump(); + return true; + case wimp_KEY_F9: /* Dump content for debugging. */ switch (content->type) { case CONTENT_HTML: @@ -1513,6 +1531,7 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar) case wimp_KEY_RETURN: if (!toolbar) break; + ro_gui_url_complete_close(NULL, 0); toolbar_url = ro_gui_get_icon_string(g->toolbar->toolbar_handle, ICON_TOOLBAR_URL); res = url_normalize(toolbar_url, &url); @@ -1524,6 +1543,8 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar) return true; case wimp_KEY_ESCAPE: + if (ro_gui_url_complete_close(0, 0)) + return true; browser_window_stop(g->bw); return true; @@ -1569,9 +1590,12 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar) case wimp_KEY_PAGE_DOWN: case wimp_KEY_CONTROL | wimp_KEY_UP: case wimp_KEY_CONTROL | wimp_KEY_DOWN: + if (toolbar) + return ro_gui_url_complete_keypress(g, key); break; - default: + if (toolbar) + return ro_gui_url_complete_keypress(g, key); return false; } @@ -1601,7 +1625,7 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar) break; } - wimp_open_window((wimp_open *) &state); + wimp_open_window((wimp_open *) &state); return true; } diff --git a/utils/url.c b/utils/url.c index 5eb7c97b9..23273a848 100644 --- a/utils/url.c +++ b/utils/url.c @@ -77,7 +77,7 @@ url_func_result url_normalize(const char *url, char **result) if (match[1].rm_so == -1) { /* scheme missing: add http:// and reparse */ - LOG(("scheme missing: using http")); +/* LOG(("scheme missing: using http"));*/ if ((*result = malloc(len + 13)) == NULL) { LOG(("malloc failed")); return URL_FUNC_NOMEM; -- cgit v1.2.3