From 95e0a24fac2abc4cddaa91bf44304ebc3d776b17 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Wed, 13 May 2020 18:03:22 +0100 Subject: use content messages to inform frontend of text search changes --- content/content.h | 44 ++++ content/textsearch.c | 572 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 386 insertions(+), 230 deletions(-) (limited to 'content') diff --git a/content/content.h b/content/content.h index f68af538b..edf9ed2cc 100644 --- a/content/content.h +++ b/content/content.h @@ -242,6 +242,50 @@ union content_msg_data { struct { struct form_control *gadget; } gadget_click; + + /** + * CONTENT_MSG_TEXTSEARCH - Free text search action + */ + struct { + /** + * The type of text search operation + */ + enum { + /** + * Free text search find operation has started or finished + */ + CONTENT_TEXTSEARCH_FIND, + /** + * Free text search match state has changed + */ + CONTENT_TEXTSEARCH_MATCH, + /** + * Free text search back available state changed + */ + CONTENT_TEXTSEARCH_BACK, + /** + * Free text search forward available state changed + */ + CONTENT_TEXTSEARCH_FORWARD, + /** + * add a search query string to the recent searches + */ + CONTENT_TEXTSEARCH_RECENT + } type; + /** + * context passed to browser_window_search() + */ + void *ctx; + /** + * state for operation + */ + bool state; + /** + * search string + */ + const char *string; + } textsearch; + }; diff --git a/content/textsearch.c b/content/textsearch.c index 145c8c41b..899739435 100644 --- a/content/textsearch.c +++ b/content/textsearch.c @@ -1,7 +1,6 @@ /* * Copyright 2004 John M Bell - * Copyright 2005 Adrian Lees - * Copyright 2009 Mark Benjamin + * Copyright 2020 Vincent Sanders * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -28,43 +27,105 @@ #include "utils/errors.h" #include "utils/utils.h" #include "netsurf/types.h" -#include "netsurf/search.h" #include "desktop/selection.h" -#include "desktop/gui_internal.h" #include "content/content.h" #include "content/content_protected.h" #include "content/hlcache.h" #include "content/textsearch.h" - +/** + * search match + */ struct list_entry { - unsigned start_idx; /* start position of match */ - unsigned end_idx; /* end of match */ + /** + * previous match + */ + struct list_entry *prev; + + /** + * next match + */ + struct list_entry *next; + + /** + * start position of match + */ + unsigned start_idx; + + /** + * end of match + */ + unsigned end_idx; + + /** + * content opaque start pointer + */ + struct box *start_box; - struct box *start_box; /* used only for html contents */ + /** + * content opaque end pointer + */ struct box *end_box; + /** + * content specific selection object + */ struct selection *sel; - - struct list_entry *prev; - struct list_entry *next; }; /** * The context for a free text search */ struct textsearch_context { - void *gui_p; + + /** + * content search was performed upon + */ struct content *c; + + /** + * opaque pointer passed to constructor. + */ + void *gui_p; + + /** + * List of matches + */ struct list_entry *found; + + /** + * current selected match + */ struct list_entry *current; /* first for select all */ + + /** + * query string search results are for + */ char *string; bool prev_case_sens; bool newsearch; }; +/** + * broadcast textsearch message + */ +static inline void +textsearch_broadcast(struct textsearch_context *textsearch, + int type, + bool state, + const char *string) +{ + union content_msg_data msg_data; + msg_data.textsearch.type = type; + msg_data.textsearch.ctx = textsearch->gui_p; + msg_data.textsearch.state = state; + msg_data.textsearch.string = string; + content_broadcast(textsearch->c, CONTENT_MSG_TEXTSEARCH, &msg_data); +} + + /** * Release the memory used by the list of matches, * deleting selection objects too @@ -96,154 +157,6 @@ static void free_matches(struct textsearch_context *textsearch) } -/* Exported function documented in content/textsearch.h */ -const char * -content_textsearch_find_pattern(const char *string, - int s_len, - const char *pattern, - int p_len, - bool case_sens, - unsigned int *m_len) -{ - struct { const char *ss, *s, *p; bool first; } context[16]; - const char *ep = pattern + p_len; - const char *es = string + s_len; - const char *p = pattern - 1; /* a virtual '*' before the pattern */ - const char *ss = string; - const char *s = string; - bool first = true; - int top = 0; - - while (p < ep) { - bool matches; - if (p < pattern || *p == '*') { - char ch; - - /* skip any further asterisks; one is the same as many - */ - do p++; while (p < ep && *p == '*'); - - /* if we're at the end of the pattern, yes, it matches - */ - if (p >= ep) break; - - /* anything matches a # so continue matching from - here, and stack a context that will try to match - the wildcard against the next character */ - - ch = *p; - if (ch != '#') { - /* scan forwards until we find a match for - this char */ - if (!case_sens) ch = toupper(ch); - while (s < es) { - if (case_sens) { - if (*s == ch) break; - } else if (toupper(*s) == ch) - break; - s++; - } - } - - if (s < es) { - /* remember where we are in case the match - fails; we may then resume */ - if (top < (int)NOF_ELEMENTS(context)) { - context[top].ss = ss; - context[top].s = s + 1; - context[top].p = p - 1; - /* ptr to last asterisk */ - context[top].first = first; - top++; - } - - if (first) { - ss = s; - /* remember first non-'*' char */ - first = false; - } - - matches = true; - } else { - matches = false; - } - - } else if (s < es) { - char ch = *p; - if (ch == '#') - matches = true; - else { - if (case_sens) - matches = (*s == ch); - else - matches = (toupper(*s) == toupper(ch)); - } - if (matches && first) { - ss = s; /* remember first non-'*' char */ - first = false; - } - } else { - matches = false; - } - - if (matches) { - p++; s++; - } else { - /* doesn't match, - * resume with stacked context if we have one */ - if (--top < 0) - return NULL; /* no match, give up */ - - ss = context[top].ss; - s = context[top].s; - p = context[top].p; - first = context[top].first; - } - } - - /* end of pattern reached */ - *m_len = max(s - ss, 1); - return ss; -} - - -/* Exported function documented in content/textsearch.h */ -nserror -content_textsearch_add_match(struct textsearch_context *context, - unsigned start_idx, - unsigned end_idx, - struct box *start_box, - struct box *end_box) -{ - struct list_entry *entry; - - /* found string in box => add to list */ - entry = calloc(1, sizeof(*entry)); - if (entry == NULL) { - return NSERROR_NOMEM; - } - - entry->start_idx = start_idx; - entry->end_idx = end_idx; - entry->start_box = start_box; - entry->end_box = end_box; - entry->sel = NULL; - - entry->next = NULL; - entry->prev = context->found->prev; - - if (context->found->prev == NULL) { - context->found->next = entry; - } else { - context->found->prev->next = entry; - } - - context->found->prev = entry; - - return NSERROR_OK; -} - - /** * Specifies whether all matches or just the current match should * be highlighted in the search text. @@ -278,17 +191,17 @@ static void search_show_all(bool all, struct textsearch_context *context) /** - * Search for a string in the box tree + * Search for a string in a content. * + * \param context The search context. * \param string the string to search for * \param string_len length of search string - * \param context The search context to add the entry to. * \param flags flags to control the search. */ static nserror -search_text(const char *string, +search_text(struct textsearch_context *context, + const char *string, int string_len, - struct textsearch_context *context, search_flags_t flags) { struct rect bounds; @@ -322,7 +235,9 @@ search_text(const char *string, context->string[string_len] = '\0'; } - guit->search->hourglass(true, context->gui_p); + /* indicate find operation starting */ + textsearch_broadcast(context, CONTENT_TEXTSEARCH_FIND, true, NULL); + /* call content find handler */ res = context->c->handler->textsearch_find(context->c, @@ -331,7 +246,8 @@ search_text(const char *string, string_len, case_sensitive); - guit->search->hourglass(false, context->gui_p); + /* indicate find operation finished */ + textsearch_broadcast(context, CONTENT_TEXTSEARCH_FIND, false, NULL); if (res != NSERROR_OK) { free_matches(context); @@ -355,29 +271,41 @@ search_text(const char *string, } } - guit->search->status((context->current != NULL), context->gui_p); + /* update match state */ + textsearch_broadcast(context, + CONTENT_TEXTSEARCH_MATCH, + (context->current != NULL), + NULL); search_show_all(showall, context); - guit->search->back_state((context->current != NULL) && - (context->current->prev != NULL), - context->gui_p); - guit->search->forward_state((context->current != NULL) && - (context->current->next != NULL), - context->gui_p); + /* update back state */ + textsearch_broadcast(context, + CONTENT_TEXTSEARCH_BACK, + ((context->current != NULL) && + (context->current->prev != NULL)), + NULL); + + /* update forward state */ + textsearch_broadcast(context, + CONTENT_TEXTSEARCH_FORWARD, + ((context->current != NULL) && + (context->current->next != NULL)), + NULL); + if (context->current == NULL) { + /* no current match */ return res; } /* call content match bounds handler */ res = context->c->handler->textsearch_bounds(context->c, - context->current->start_idx, - context->current->end_idx, - - context->current->start_box, - context->current->end_box, - &bounds); + context->current->start_idx, + context->current->end_idx, + context->current->start_box, + context->current->end_box, + &bounds); if (res == NSERROR_OK) { msg_data.scroll.area = true; msg_data.scroll.x0 = bounds.x0; @@ -411,7 +339,11 @@ content_textsearch_step(struct textsearch_context *textsearch, assert(textsearch != NULL); - guit->search->add_recent(string, textsearch->gui_p); + /* broadcast recent query string */ + textsearch_broadcast(textsearch, + CONTENT_TEXTSEARCH_RECENT, + false, + string); string_len = strlen(string); for (i = 0; i < string_len; i++) { @@ -420,52 +352,61 @@ content_textsearch_step(struct textsearch_context *textsearch, } if (i < string_len) { - res = search_text(string, string_len, textsearch, flags); + res = search_text(textsearch, string, string_len, flags); } else { union content_msg_data msg_data; - free_matches(textsearch); - guit->search->status(true, textsearch->gui_p); - guit->search->back_state(false, textsearch->gui_p); - guit->search->forward_state(false, textsearch->gui_p); + free_matches(textsearch); + /* update match state */ + textsearch_broadcast(textsearch, + CONTENT_TEXTSEARCH_MATCH, + true, + NULL); + + /* update back state */ + textsearch_broadcast(textsearch, + CONTENT_TEXTSEARCH_BACK, + false, + NULL); + + /* update forward state */ + textsearch_broadcast(textsearch, + CONTENT_TEXTSEARCH_FORWARD, + false, + NULL); + + /* clear scroll */ msg_data.scroll.area = false; msg_data.scroll.x0 = 0; msg_data.scroll.y0 = 0; - content_broadcast(textsearch->c, CONTENT_MSG_SCROLL, &msg_data); + content_broadcast(textsearch->c, + CONTENT_MSG_SCROLL, + &msg_data); } return res; } -/* Exported function documented in content/textsearch.h */ -bool -content_textsearch_ishighlighted(struct textsearch_context *textsearch, - unsigned start_offset, - unsigned end_offset, - unsigned *start_idx, - unsigned *end_idx) +/** + * Terminate a search. + * + * \param c content to clear + */ +static nserror content_textsearch__clear(struct content *c) { - struct list_entry *cur; + free(c->textsearch.string); + c->textsearch.string = NULL; - for (cur = textsearch->found->next; cur != NULL; cur = cur->next) { - if (cur->sel && - selection_defined(cur->sel) && - selection_highlighted(cur->sel, - start_offset, - end_offset, - start_idx, - end_idx)) { - return true; - } + if (c->textsearch.context != NULL) { + content_textsearch_destroy(c->textsearch.context); + c->textsearch.context = NULL; } - - return false; + return NSERROR_OK; } -/* Exported function documented in content/textsearch.h */ /** * create a search_context * @@ -528,44 +469,215 @@ content_textsearch_create(struct content *c, } -/* Exported function documented in search.h */ -nserror content_textsearch_destroy(struct textsearch_context *textsearch) +/* exported interface, documented in content/textsearch.h */ +const char * +content_textsearch_find_pattern(const char *string, + int s_len, + const char *pattern, + int p_len, + bool case_sens, + unsigned int *m_len) { - assert(textsearch != NULL); + struct { const char *ss, *s, *p; bool first; } context[16]; + const char *ep = pattern + p_len; + const char *es = string + s_len; + const char *p = pattern - 1; /* a virtual '*' before the pattern */ + const char *ss = string; + const char *s = string; + bool first = true; + int top = 0; - if (textsearch->string != NULL) { - guit->search->add_recent(textsearch->string, textsearch->gui_p); - free(textsearch->string); + while (p < ep) { + bool matches; + if (p < pattern || *p == '*') { + char ch; + + /* skip any further asterisks; one is the same as many + */ + do p++; while (p < ep && *p == '*'); + + /* if we're at the end of the pattern, yes, it matches + */ + if (p >= ep) break; + + /* anything matches a # so continue matching from + here, and stack a context that will try to match + the wildcard against the next character */ + + ch = *p; + if (ch != '#') { + /* scan forwards until we find a match for + this char */ + if (!case_sens) ch = toupper(ch); + while (s < es) { + if (case_sens) { + if (*s == ch) break; + } else if (toupper(*s) == ch) + break; + s++; + } + } + + if (s < es) { + /* remember where we are in case the match + fails; we may then resume */ + if (top < (int)NOF_ELEMENTS(context)) { + context[top].ss = ss; + context[top].s = s + 1; + context[top].p = p - 1; + /* ptr to last asterisk */ + context[top].first = first; + top++; + } + + if (first) { + ss = s; + /* remember first non-'*' char */ + first = false; + } + + matches = true; + } else { + matches = false; + } + + } else if (s < es) { + char ch = *p; + if (ch == '#') + matches = true; + else { + if (case_sens) + matches = (*s == ch); + else + matches = (toupper(*s) == toupper(ch)); + } + if (matches && first) { + ss = s; /* remember first non-'*' char */ + first = false; + } + } else { + matches = false; + } + + if (matches) { + p++; s++; + } else { + /* doesn't match, + * resume with stacked context if we have one */ + if (--top < 0) + return NULL; /* no match, give up */ + + ss = context[top].ss; + s = context[top].s; + p = context[top].p; + first = context[top].first; + } } - guit->search->forward_state(true, textsearch->gui_p); - guit->search->back_state(true, textsearch->gui_p); + /* end of pattern reached */ + *m_len = max(s - ss, 1); + return ss; +} - free_matches(textsearch); - free(textsearch); + +/* exported interface, documented in content/textsearch.h */ +nserror +content_textsearch_add_match(struct textsearch_context *context, + unsigned start_idx, + unsigned end_idx, + struct box *start_box, + struct box *end_box) +{ + struct list_entry *entry; + + /* found string in box => add to list */ + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + return NSERROR_NOMEM; + } + + entry->start_idx = start_idx; + entry->end_idx = end_idx; + entry->start_box = start_box; + entry->end_box = end_box; + entry->sel = NULL; + + entry->next = NULL; + entry->prev = context->found->prev; + + if (context->found->prev == NULL) { + context->found->next = entry; + } else { + context->found->prev->next = entry; + } + + context->found->prev = entry; return NSERROR_OK; } -/** - * Terminate a search. - * - * \param c content to clear - */ -static nserror content_textsearch__clear(struct content *c) + +/* exported interface, documented in content/textsearch.h */ +bool +content_textsearch_ishighlighted(struct textsearch_context *textsearch, + unsigned start_offset, + unsigned end_offset, + unsigned *start_idx, + unsigned *end_idx) { - free(c->textsearch.string); - c->textsearch.string = NULL; + struct list_entry *cur; - if (c->textsearch.context != NULL) { - content_textsearch_destroy(c->textsearch.context); - c->textsearch.context = NULL; + for (cur = textsearch->found->next; cur != NULL; cur = cur->next) { + if (cur->sel && + selection_defined(cur->sel) && + selection_highlighted(cur->sel, + start_offset, + end_offset, + start_idx, + end_idx)) { + return true; + } } - return NSERROR_OK; + + return false; } /* exported interface, documented in content/textsearch.h */ +nserror content_textsearch_destroy(struct textsearch_context *textsearch) +{ + assert(textsearch != NULL); + + if (textsearch->string != NULL) { + /* broadcast recent query string */ + textsearch_broadcast(textsearch, + CONTENT_TEXTSEARCH_RECENT, + false, + textsearch->string); + + free(textsearch->string); + } + + /* update back state */ + textsearch_broadcast(textsearch, + CONTENT_TEXTSEARCH_BACK, + true, + NULL); + + /* update forward state */ + textsearch_broadcast(textsearch, + CONTENT_TEXTSEARCH_FORWARD, + true, + NULL); + + free_matches(textsearch); + free(textsearch); + + return NSERROR_OK; +} + + +/* exported interface, documented in content/content.h */ nserror content_textsearch(struct hlcache_handle *h, void *context, @@ -618,7 +730,7 @@ content_textsearch(struct hlcache_handle *h, } -/* exported interface, documented in content/textsearch.h */ +/* exported interface, documented in content/content.h */ nserror content_textsearch_clear(struct hlcache_handle *h) { struct content *c = hlcache_handle_get_content(h); -- cgit v1.2.3