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 --- riscos/url_complete.c | 558 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 558 insertions(+) create mode 100644 riscos/url_complete.c (limited to 'riscos/url_complete.c') 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); +} -- cgit v1.2.3