netsurf/riscos/search.c

508 lines
14 KiB
C

/*
* Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
* Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
*
* 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
* Free text search (implementation)
*/
#include "utils/config.h"
#include <ctype.h>
#include <string.h>
#include "oslib/hourglass.h"
#include "oslib/wimp.h"
#include "utils/config.h"
#include "content/content.h"
#include "content/hlcache.h"
#include "desktop/browser.h"
#include "desktop/gui.h"
#include "desktop/search.h"
#include "desktop/selection.h"
#include "render/box.h"
#include "render/html.h"
#include "riscos/dialog.h"
#include "riscos/menus.h"
#include "riscos/wimp.h"
#include "riscos/wimp_event.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/utils.h"
#define RECENT_SEARCHES 8
struct search_static_data {
char *recent_searches[RECENT_SEARCHES];
bool search_insert;
struct browser_window *search_window;
search_flags_t flags;
char *string;
};
static struct search_static_data search_data =
{{NULL}, false, NULL, 0, NULL};
static wimp_MENU(RECENT_SEARCHES) menu_recent;
wimp_menu *recent_search_menu = (wimp_menu *)&menu_recent;
#define DEFAULT_FLAGS (wimp_ICON_TEXT | wimp_ICON_FILLED | \
(wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | \
(wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT))
static void ro_gui_search_end(wimp_w w);
static bool ro_gui_search_next(wimp_w w);
static bool ro_gui_search_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
wimp_pointer *pointer);
static bool ro_gui_search_prepare_menu(void);
static bool ro_gui_search_click(wimp_pointer *pointer);
static bool ro_gui_search_keypress(wimp_key *key);
static search_flags_t ro_gui_search_update_flags(void);
static void ro_gui_search_set_forward_state(bool active, void *p);
static void ro_gui_search_set_back_state(bool active, void *p);
static void ro_gui_search_set_status(bool found, void *p);
static void ro_gui_search_set_hourglass(bool active, void *p);
static void ro_gui_search_add_recent(const char *string, void *p);
static struct search_callbacks ro_gui_search_callbacks = {
ro_gui_search_set_forward_state,
ro_gui_search_set_back_state,
ro_gui_search_set_status,
ro_gui_search_set_hourglass,
ro_gui_search_add_recent
};
void ro_gui_search_init(void)
{
dialog_search = ro_gui_dialog_create("search");
ro_gui_wimp_event_register_keypress(dialog_search,
ro_gui_search_keypress);
ro_gui_wimp_event_register_close_window(dialog_search,
ro_gui_search_end);
ro_gui_wimp_event_register_menu_prepare(dialog_search,
ro_gui_search_menu_prepare);
ro_gui_wimp_event_register_menu_gright(dialog_search,
ICON_SEARCH_TEXT, ICON_SEARCH_MENU,
recent_search_menu);
ro_gui_wimp_event_register_text_field(dialog_search,
ICON_SEARCH_STATUS);
ro_gui_wimp_event_register_checkbox(dialog_search,
ICON_SEARCH_CASE_SENSITIVE);
ro_gui_wimp_event_register_mouse_click(dialog_search,
ro_gui_search_click);
ro_gui_wimp_event_register_ok(dialog_search, ICON_SEARCH_FIND_NEXT,
ro_gui_search_next);
ro_gui_wimp_event_register_cancel(dialog_search, ICON_SEARCH_CANCEL);
ro_gui_wimp_event_set_help_prefix(dialog_search, "HelpSearch");
recent_search_menu->title_data.indirected_text.text =
(char*)messages_get("Search");
ro_gui_menu_init_structure(recent_search_menu, RECENT_SEARCHES);
}
/**
* Wrapper for the pressing of an OK button for wimp_event.
*
* \return false, to indicate the window should not be closed
*/
bool ro_gui_search_next(wimp_w w)
{
search_data.search_insert = true;
search_flags_t flags = SEARCH_FLAG_FORWARDS |
ro_gui_search_update_flags();
if (browser_window_search_verify_new(search_data.search_window,
&ro_gui_search_callbacks, NULL))
browser_window_search_step(
search_data.search_window, flags,
ro_gui_get_icon_string(dialog_search,
ICON_SEARCH_TEXT));
return false;
}
/**
* Callback to prepare menus in the Search dialog. At present, this
* only has to handle the previous search pop-up.
*
* \param w The window handle owning the menu.
* \param i The icon handle owning the menu.
* \param *menu The menu to be prepared.
* \param *pointer The associated mouse click event block, or NULL
* on an Adjust-click re-opening.
* \return true if the event was handled; false if not.
*/
bool ro_gui_search_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
wimp_pointer *pointer)
{
if (menu != recent_search_menu || i != ICON_SEARCH_MENU)
return false;
if (pointer != NULL)
return ro_gui_search_prepare_menu();
return true;
}
bool ro_gui_search_click(wimp_pointer *pointer)
{
search_flags_t flags;
switch (pointer->i) {
case ICON_SEARCH_FIND_PREV:
search_data.search_insert = true;
flags = ~SEARCH_FLAG_FORWARDS &
ro_gui_search_update_flags();
if (browser_window_search_verify_new(
search_data.search_window,
&ro_gui_search_callbacks, NULL))
browser_window_search_step(
search_data.search_window,
flags,
ro_gui_get_icon_string(
dialog_search,
ICON_SEARCH_TEXT));
return true;
case ICON_SEARCH_CASE_SENSITIVE:
flags = SEARCH_FLAG_FORWARDS |
ro_gui_search_update_flags();
if (browser_window_search_verify_new(
search_data.search_window,
&ro_gui_search_callbacks, NULL))
browser_window_search_step(
search_data.search_window,
flags,
ro_gui_get_icon_string(
dialog_search,
ICON_SEARCH_TEXT));
return true;
case ICON_SEARCH_SHOW_ALL:
/* TODO: call browser_window_search_verify_new() ? */
browser_window_search_show_all(
ro_gui_get_icon_selected_state(
pointer->w, pointer->i),
search_data.search_window);
return true;
}
return false;
}
/**
* add search string to recent searches list
* front is at liberty how to implement the bare notification
* should normally store a strdup() of the string in
* search_global_data.recent[];
* core gives no guarantee of the integrity of the const char *
* \param string search pattern
* \param p the pointer sent to search_verify_new()
*/
void ro_gui_search_add_recent(const char *search, void *p)
{
char *tmp;
int i;
if ((search == NULL) || (search[0] == '\0'))
return;
if (!search_data.search_insert) {
free(search_data.recent_searches[0]);
search_data.recent_searches[0] = strdup(search);
ro_gui_search_prepare_menu();
return;
}
if ((search_data.recent_searches[0] != NULL) &&
(!strcmp(search_data.recent_searches[0], search)))
return;
tmp = strdup(search);
if (!tmp) {
warn_user("NoMemory", 0);
return;
}
free(search_data.recent_searches[RECENT_SEARCHES - 1]);
for (i = RECENT_SEARCHES - 1; i > 0; i--)
search_data.recent_searches[i] = search_data.recent_searches[i - 1];
search_data.recent_searches[0] = tmp;
search_data.search_insert = false;
ro_gui_set_icon_shaded_state(dialog_search, ICON_SEARCH_MENU, false);
ro_gui_search_prepare_menu();
}
bool ro_gui_search_prepare_menu(void)
{
os_error *error;
int i;
int suggestions = 0;
for (i = 0; i < RECENT_SEARCHES; i++)
if (search_data.recent_searches[i] != NULL)
suggestions++;
if (suggestions == 0)
return false;
for (i = 0; i < suggestions; i++) {
recent_search_menu->entries[i].menu_flags &= ~wimp_MENU_LAST;
recent_search_menu->entries[i].data.indirected_text.text =
search_data.recent_searches[i];
recent_search_menu->entries[i].data.indirected_text.size =
strlen(search_data.recent_searches[i]) + 1;
}
recent_search_menu->entries[suggestions - 1].menu_flags |=
wimp_MENU_LAST;
if ((current_menu_open) && (current_menu == recent_search_menu)) {
error = xwimp_create_menu(current_menu, 0, 0);
if (error) {
LOG(("xwimp_create_menu: 0x%x: %s",
error->errnum, error->errmess));
warn_user("MenuError", error->errmess);
}
}
return true;
}
/**
* Open the search dialog
*
* \param bw the browser window to search
*/
void ro_gui_search_prepare(struct browser_window *bw)
{
hlcache_handle *h;
assert(bw != NULL);
h = bw->current_content;
/* only handle html/textplain contents */
if ((!h) || (content_get_type(h) != CONTENT_HTML &&
content_get_type(h) != CONTENT_TEXTPLAIN))
return;
/* if the search dialogue is reopened over a new window, we may
need to cancel the previous search */
ro_gui_search_set_forward_state(true, bw);
ro_gui_search_set_back_state(true, bw);
browser_window_search_create_context(bw,
&ro_gui_search_callbacks, NULL);
search_data.search_window = bw;
ro_gui_set_icon_string(dialog_search, ICON_SEARCH_TEXT, "", true);
ro_gui_set_icon_selected_state(dialog_search,
ICON_SEARCH_CASE_SENSITIVE, false);
ro_gui_set_icon_selected_state(dialog_search,
ICON_SEARCH_SHOW_ALL, false);
ro_gui_search_set_status(true, NULL);
ro_gui_wimp_event_memorise(dialog_search);
search_data.search_insert = true;
}
/**
* Handle keypresses in the search dialog
*
* \param key wimp_key block
* \return true if keypress handled, false otherwise
*/
bool ro_gui_search_keypress(wimp_key *key)
{
bool state;
search_flags_t flags;
switch (key->c) {
case 1: { /* ctrl a */
bool sel = !ro_gui_get_icon_selected_state(key->w,
ICON_SEARCH_SHOW_ALL);
ro_gui_set_icon_selected_state(key->w,
ICON_SEARCH_SHOW_ALL, sel);
/* TODO: call browser_window_search_verify_new() ? */
browser_window_search_show_all(sel,
search_data.search_window);
}
break;
case 9: /* ctrl i */
state = ro_gui_get_icon_selected_state(dialog_search,
ICON_SEARCH_CASE_SENSITIVE);
ro_gui_set_icon_selected_state(dialog_search,
ICON_SEARCH_CASE_SENSITIVE, !state);
flags = SEARCH_FLAG_FORWARDS |
ro_gui_search_update_flags();
if (browser_window_search_verify_new(
search_data.search_window,
&ro_gui_search_callbacks, NULL))
browser_window_search_step(
search_data.search_window,
flags,
ro_gui_get_icon_string(
dialog_search,
ICON_SEARCH_TEXT));
return true;
case IS_WIMP_KEY | wimp_KEY_UP:
search_data.search_insert = true;
flags = ~SEARCH_FLAG_FORWARDS &
ro_gui_search_update_flags();
if (browser_window_search_verify_new(
search_data.search_window,
&ro_gui_search_callbacks, NULL))
browser_window_search_step(
search_data.search_window,
flags,
ro_gui_get_icon_string(
dialog_search,
ICON_SEARCH_TEXT));
return true;
case IS_WIMP_KEY | wimp_KEY_DOWN:
search_data.search_insert = true;
flags = SEARCH_FLAG_FORWARDS |
ro_gui_search_update_flags();
if (browser_window_search_verify_new(
search_data.search_window,
&ro_gui_search_callbacks, NULL))
browser_window_search_step(
search_data.search_window,
flags,
ro_gui_get_icon_string(
dialog_search,
ICON_SEARCH_TEXT));
return true;
default:
if (key->c == 21) {
/* ctrl+u means the user's starting
* a new search */
browser_window_search_destroy_context(
search_data.search_window);
ro_gui_search_set_forward_state(true,
search_data.search_window);
ro_gui_search_set_back_state(true,
search_data.search_window);
search_data.search_insert = true;
}
if (key->c == 8 || /* backspace */
key->c == 21 || /* ctrl u */
(key->c >= 0x20 && key->c <= 0x7f)) {
flags = SEARCH_FLAG_FORWARDS |
ro_gui_search_update_flags();
browser_window_search_destroy_context(
search_data.search_window);
ro_gui_search_set_forward_state(true,
search_data.search_window);
ro_gui_search_set_back_state(true,
search_data.search_window);
if (browser_window_search_verify_new(
search_data.search_window,
&ro_gui_search_callbacks,
NULL))
browser_window_search_step(
search_data.
search_window,
flags,
ro_gui_get_icon_string(
dialog_search,
ICON_SEARCH_TEXT));
return true;
}
break;
}
return false;
}
/**
* Ends the search
* \param w the search window handle (not used)
*/
void ro_gui_search_end(wimp_w w)
{
browser_window_search_destroy_context(search_data.search_window);
ro_gui_search_set_forward_state(true, search_data.search_window);
ro_gui_search_set_back_state(true, search_data.search_window);
}
/**
* Change the displayed search status.
* \param found search pattern matched in text
* \param p the pointer sent to search_verify_new() / search_create_context()
*/
void ro_gui_search_set_status(bool found, void *p)
{
ro_gui_set_icon_string(dialog_search, ICON_SEARCH_STATUS, found ? "" :
messages_get("NotFound"), true);
}
/**
* display hourglass while searching
* \param active start/stop indicator
* \param p the pointer sent to search_verify_new() / search_create_context()
*/
void ro_gui_search_set_hourglass(bool active, void *p)
{
if (active)
xhourglass_on();
else
xhourglass_off();
}
/**
* activate search forwards button in gui
* \param active activate/inactivate
* \param p the pointer sent to search_verify_new() / search_create_context()
*/
void ro_gui_search_set_forward_state(bool active, void *p)
{
ro_gui_set_icon_shaded_state(dialog_search, ICON_SEARCH_FIND_NEXT,
!active);
}
/**
* activate search forwards button in gui
* \param active activate/inactivate
* \param p the pointer sent to search_verify_new() / search_create_context()
*/
void ro_gui_search_set_back_state(bool active, void *p)
{
ro_gui_set_icon_shaded_state(dialog_search, ICON_SEARCH_FIND_PREV,
!active);
}
/**
* retrieve state of 'case sensitive', 'show all' checks in gui
*/
search_flags_t ro_gui_search_update_flags(void)
{
search_flags_t flags;
flags = 0 | (ro_gui_get_icon_selected_state(dialog_search,
ICON_SEARCH_CASE_SENSITIVE) ?
SEARCH_FLAG_CASE_SENSITIVE : 0) |
(ro_gui_get_icon_selected_state(dialog_search,
ICON_SEARCH_SHOW_ALL) ? SEARCH_FLAG_SHOWALL : 0);
return flags;
}