From 56880392a6678ccec12bbec016939597acd49b07 Mon Sep 17 00:00:00 2001 From: rootavish Date: Wed, 6 Aug 2014 15:34:02 +0530 Subject: Searching in epub documents I added the capability to search through documents. The search is quite buggy, and we'll be taking care of each bug one at a time. --- backend/epub/epub-document.c | 92 +++++++++----- libdocument/ev-document-find.c | 2 +- libdocument/ev-document-find.h | 4 +- libview/ev-jobs.c | 8 +- libview/ev-web-view.c | 273 ++++++++++++++++++++++++++++------------- libview/ev-web-view.h | 10 +- shell/ev-window.c | 14 +-- 7 files changed, 271 insertions(+), 132 deletions(-) diff --git a/backend/epub/epub-document.c b/backend/epub/epub-document.c index e9443c09..3a574151 100644 --- a/backend/epub/epub-document.c +++ b/backend/epub/epub-document.c @@ -29,14 +29,13 @@ #include "ev-document-misc.h" #include #include - +#include #include #include #include #include -#include /*For strcasestr(),strstr()*/ #include @@ -130,47 +129,72 @@ epub_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, } static gboolean +in_tag(const char* found) +{ + const char* bracket = found ; + + /* Since the dump started with the body tag, the '<' will be the first + * character in the haystack. + */ + while (*bracket != '<') { + bracket--; + if (*bracket == '>') { + /*We encounted a close brace before an open*/ + return FALSE ; + } + } + + return TRUE; +} + +static int +get_substr_count(const char * haystack,const char *needle,gboolean case_sensitive) +{ + const char* tmp = haystack ; + char* (*string_compare_function)(const char*,const char*); + int count=0; + if (case_sensitive) { + string_compare_function = strstr ; + } + else { + string_compare_function = strcasestr; + } + + while ((tmp=string_compare_function(tmp,needle))) { + if (!in_tag(tmp)) { + count++; + } + tmp = tmp + strlen(needle); + } + + return count; +} + +static guint epub_document_check_hits(EvDocumentFind *document_find, EvPage *page, const gchar *text, gboolean case_sensitive) { gchar *filepath = g_filename_from_uri((gchar*)page->backend_page,NULL,NULL); - FILE *fp = fopen(filepath,"r"); - GString *buffer; - gchar *found ; - - while (!feof(fp)) { - gchar c; - gint pos=0; - buffer = g_string_sized_new (1024); - - while ((c = fgetc(fp)) != '\n' && !feof(fp)) { - g_string_insert_c(buffer,pos++,c); - } + htmlDocPtr htmldoc = xmlParseFile(filepath); + htmlNodePtr htmltag = xmlDocGetRootElement(htmldoc); + int count=0; + htmlNodePtr bodytag = htmltag->xmlChildrenNode; - g_string_insert_c(buffer,pos,'\0'); - - if (case_sensitive) { - if ((found = strstr(buffer->str,text)) != NULL) { - g_string_free(buffer,TRUE); - fclose(fp); - return TRUE; - } - } - else { - - if ( (found = strcasestr(buffer->str,text)) != NULL) { - g_string_free(buffer,TRUE); - fclose(fp); - return TRUE; - } - } - g_string_free(buffer,TRUE); + while ( xmlStrcmp(bodytag->name,(xmlChar*)"body") ) { + bodytag = bodytag->next; } - fclose(fp); - return FALSE; + xmlBufferPtr bodybuffer = xmlBufferCreate(); + xmlNodeDump(bodybuffer,htmldoc,bodytag,0,1); + + count = get_substr_count((char*)bodybuffer->content,text,case_sensitive); + + xmlBufferFree(bodybuffer); + xmlFreeDoc(htmldoc); + + return count; } static gboolean diff --git a/libdocument/ev-document-find.c b/libdocument/ev-document-find.c index c22b913d..92dfffe5 100644 --- a/libdocument/ev-document-find.c +++ b/libdocument/ev-document-find.c @@ -40,7 +40,7 @@ ev_document_find_find_text (EvDocumentFind *document_find, return iface->find_text (document_find, page, text, case_sensitive); } -gboolean +guint ev_document_find_check_for_hits(EvDocumentFind *document_find, EvPage *page, const gchar *text, diff --git a/libdocument/ev-document-find.h b/libdocument/ev-document-find.h index 7fb5d22c..2b6ba68b 100644 --- a/libdocument/ev-document-find.h +++ b/libdocument/ev-document-find.h @@ -53,7 +53,7 @@ struct _EvDocumentFindInterface const gchar *text, gboolean case_sensitive); - gboolean (* check_for_hits)(EvDocumentFind *document_find, + guint (* check_for_hits) (EvDocumentFind *document_find, EvPage *page, const gchar *text, gboolean case_sensitive); @@ -65,7 +65,7 @@ GList *ev_document_find_find_text (EvDocumentFind *document_find, const gchar *text, gboolean case_sensitive); -gboolean ev_document_find_check_for_hits(EvDocumentFind *document_find, +guint ev_document_find_check_for_hits (EvDocumentFind *document_find, EvPage *page, const gchar *text, gboolean case_sensitive); diff --git a/libview/ev-jobs.c b/libview/ev-jobs.c index 155990e9..3a7dfd27 100644 --- a/libview/ev-jobs.c +++ b/libview/ev-jobs.c @@ -1379,7 +1379,7 @@ ev_job_find_run (EvJob *job) ev_page = ev_document_get_page (job->document, job_find->current_page); if (job->document->iswebdocument) { - job_find->has_results = ev_document_find_check_for_hits(find, ev_page, job_find->text, + job_find->results[job_find->current_page] = ev_document_find_check_for_hits(find, ev_page, job_find->text, job_find->case_sensitive); }else { matches = ev_document_find_find_text (find, ev_page, job_find->text, @@ -1393,10 +1393,14 @@ ev_job_find_run (EvJob *job) if (!job_find->has_results && !job->document->iswebdocument) { job_find->has_results = (matches != NULL); } - + else if (!job_find->has_results && job->document->iswebdocument){ + job_find->has_results = (job_find->results[job_find->current_page] > 0); + } + if (job->document->iswebdocument == FALSE) { job_find->pages[job_find->current_page] = matches; } + g_signal_emit (job_find, job_find_signals[FIND_UPDATED], 0, job_find->current_page); job_find->current_page = (job_find->current_page + 1) % job_find->n_pages; diff --git a/libview/ev-web-view.c b/libview/ev-web-view.c index 152e2542..0756b0f0 100644 --- a/libview/ev-web-view.c +++ b/libview/ev-web-view.c @@ -37,13 +37,19 @@ #define EV_IS_WEB_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_WEB_VIEW)) #define EV_WEB_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_WEB_VIEW, EvWebViewClass)) + typedef enum { + EV_WEB_VIEW_FIND_NEXT, + EV_WEB_VIEW_FIND_PREV + } EvWebViewFindDirection; + typedef struct _SearchParams { gboolean case_sensitive; - guint page_current; gboolean search_jump; gchar* search_string; - guint on_result; + gint on_result; guint n_results; + guint *results; + EvWebViewFindDirection direction; }SearchParams; struct _EvWebView @@ -65,20 +71,21 @@ struct _EvWebViewClass /*** Callbacks ***/ static void ev_web_view_change_page (EvWebView *webview, - gint new_page); + gint new_page); static void ev_web_view_page_changed_cb (EvDocumentModel *model, gint old_page, gint new_page, EvWebView *webview); /*** GObject ***/ -static void ev_web_view_dispose (GObject *object); +static void ev_web_view_dispose (GObject *object); -static void ev_web_view_finalize (GObject *object); +static void ev_web_view_finalize (GObject *object); static void ev_web_view_class_init (EvWebViewClass *klass); static void ev_web_view_init (EvWebView *webview); G_DEFINE_TYPE (EvWebView, ev_web_view, WEBKIT_TYPE_WEB_VIEW) + static void web_view_update_range_and_current_page (EvWebView *webview) { @@ -106,12 +113,18 @@ ev_web_view_dispose (GObject *object) if (webview->model) { g_object_unref(webview->model); webview->model = NULL; - }; + } + if (webview->hlink) { g_free(webview->hlink); webview->hlink = NULL; } + if (webview->search) { + g_free(webview->search); + webview->search = NULL; + } + G_OBJECT_CLASS (ev_web_view_parent_class)->dispose (object); } @@ -132,7 +145,10 @@ ev_web_view_init (EvWebView *webview) webview->current_page = 0; webview->search = g_new0(SearchParams, 1); - + webview->search->search_string = NULL; + webview->search->on_result = -1 ; + webview->search->n_results = 0; + webview->search->results = NULL; webview->search->search_jump = TRUE ; webview->fullscreen = FALSE; @@ -400,112 +416,205 @@ ev_web_view_handle_link(EvWebView *webview,EvLink *link) } /* Searching */ -void -ev_web_view_find_next(EvWebView *webview) -{ - /*First search for the next item on the current page*/ - webkit_web_view_search_text (WEBKIT_WEB_VIEW(webview), - webview->search->search_string, - webview->search->case_sensitive, - TRUE, - FALSE); -} -void -ev_web_view_find_previous(EvWebView *webview) +static void +jump_to_find_result_on_page(EvWebView *webview,EvWebViewFindDirection direction) { + gboolean forward,wrap; + + if (direction == EV_WEB_VIEW_FIND_NEXT) { + forward = TRUE; + wrap = FALSE; + } + else { + forward = FALSE; + wrap = FALSE; + } + webkit_web_view_search_text (WEBKIT_WEB_VIEW(webview), - webview->search->search_string, - webview->search->case_sensitive, - FALSE, - TRUE); + webview->search->search_string, + webview->search->case_sensitive, + forward, + wrap); } -void -ev_web_view_find_set_highlight_search(EvWebView *webview, gboolean visible) +static void +jump_to_find_result(EvWebView *webview, + GParamSpec *pspec, + gpointer data) { - webkit_web_view_set_highlight_text_matches(WEBKIT_WEB_VIEW(webview),visible); + gint n_results; + gboolean forward ; + gboolean wrap ; + + if (webkit_web_view_get_load_status(WEBKIT_WEB_VIEW(webview)) != WEBKIT_LOAD_FINISHED) { + return; + } + + if (!webview->search->search_string) { + return; + } + + n_results = webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW(webview), + webview->search->search_string, + webview->search->case_sensitive, + 0); + + ev_web_view_find_set_highlight_search(webview,TRUE); + + if (webview->search->direction == EV_WEB_VIEW_FIND_NEXT) { + forward = TRUE ; + wrap = FALSE; + } + else { + forward = FALSE; + wrap = TRUE ; + } + + if (n_results > 0 && webview->search->on_result < n_results) { + webkit_web_view_search_text (WEBKIT_WEB_VIEW(webview), + webview->search->search_string, + webview->search->case_sensitive, + forward, + wrap); + + webview->search->search_jump = FALSE; + } } -typedef struct _FindCBStruct { - EvJobFind *job; - gint page; -}FindCBStruct; +static gint +ev_web_view_find_get_n_results (EvWebView *webview, gint page) +{ + return webview->search->results[page]; +} +/** + * jump_to_find_page + * @webview: #EvWebView instance + * @direction: Direction to look + * @shift: Shift from current page + * + * Jumps to the first page that has occurences of searched word. + * Uses a direction where to look and a shift from current page. + * + */ static void -find_page_change_cb(WebKitWebView *webview, - WebKitWebFrame *webframe, - FindCBStruct *findcbs) -{ - findcbs->job->results[findcbs->page] = webkit_web_view_mark_text_matches(WEBKIT_WEB_VIEW(webview), - findcbs->job->text, - findcbs->job->case_sensitive, - 0); - ev_web_view_find_set_highlight_search(EV_WEB_VIEW(webview), TRUE); - - webkit_web_view_search_text (WEBKIT_WEB_VIEW(webview), - findcbs->job->text, - findcbs->job->case_sensitive, - TRUE, - FALSE); +jump_to_find_page (EvWebView *webview, EvWebViewFindDirection direction, gint shift) +{ + int n_pages, i; + + n_pages = ev_document_get_n_pages (webview->document); + + for (i = 0; i < n_pages; i++) { + int page; + + if (direction == EV_WEB_VIEW_FIND_NEXT) + page = webview->current_page + i; + else + page = webview->current_page - i; + page += shift; + + if (page >= n_pages) { + page = page - n_pages; + } else if (page < 0) + page = page + n_pages; + + if (page == webview->current_page) + jump_to_find_result_on_page(webview,EV_WEB_VIEW_FIND_NEXT); + + if (ev_web_view_find_get_n_results (webview, page) > 0) { + webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW(webview)); + ev_document_model_set_page (webview->model, page); + webview->search->direction = direction; + break; + } + } } + void -ev_web_view_find_changed(EvWebView *webview, gint page_found_on,EvJobFind *job) +ev_web_view_find_changed (EvWebView *webview, guint *results, gchar *text,gboolean case_sensitive) { - if (job->has_results == FALSE) - return; + webview->search->results = results; + webview->search->search_string = g_strdup(text); + webview->search->case_sensitive = case_sensitive; if (webview->search->search_jump == TRUE) { + jump_to_find_page (webview, EV_WEB_VIEW_FIND_NEXT, 0); + } else { + jump_to_find_result_on_page(webview,EV_WEB_VIEW_FIND_NEXT); + } +} - webview->search->on_result = 1; - webview->search->case_sensitive = job->case_sensitive; - webview->search->search_string = g_strdup(job->text); - webview->search->search_jump = FALSE; - - if (page_found_on != webview->current_page) { - ev_web_view_change_page(webview, page_found_on); +void +ev_web_view_find_next (EvWebView *webview) +{ + gint n_results; - FindCBStruct *findstruct = g_new0 (FindCBStruct, 1); - findstruct->job = job; - findstruct->page = page_found_on; - - g_signal_connect(WEBKIT_WEB_VIEW(webview),"document-load-finished",G_CALLBACK(find_page_change_cb),findstruct); - } - else { - job->results[webview->current_page] = webkit_web_view_mark_text_matches(WEBKIT_WEB_VIEW(webview), - job->text, - job->case_sensitive, - 0); + n_results = ev_web_view_find_get_n_results (webview, webview->current_page); + webview->search->on_result++; - ev_web_view_find_set_highlight_search(webview, TRUE); - } + if (webview->search->on_result >= n_results) { + webview->search->on_result = 0; + jump_to_find_page (webview, EV_WEB_VIEW_FIND_NEXT, 1); + } + else { + jump_to_find_result_on_page (webview, EV_WEB_VIEW_FIND_NEXT); } } void -ev_web_view_find_search_changed(EvWebView *webview) +ev_web_view_find_previous (EvWebView *webview) { - ev_web_view_find_set_highlight_search(webview,FALSE); - webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW(webview)); + webview->search->on_result--; + + if (webview->search->on_result < 0) { + jump_to_find_page (webview, EV_WEB_VIEW_FIND_PREV, -1); + webview->search->on_result = MAX (0, ev_web_view_find_get_n_results (webview, webview->current_page) - 1); + } else { + jump_to_find_result_on_page (webview,EV_WEB_VIEW_FIND_PREV); + } +} + +void +ev_web_view_find_search_changed (EvWebView *webview,gboolean visible) +{ + /* search string has changed, focus on new search result */ + if (visible) { + g_signal_connect(webview, + "notify::load-status", + G_CALLBACK(jump_to_find_result), + NULL); + } + else { + g_signal_handlers_disconnect_by_func(webview, + jump_to_find_result, + NULL); + } + webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(webview)); + webview->search->search_jump = TRUE; + + if (webview->search->search_string) { + g_free(webview->search->search_string); + webview->search->search_string = NULL; + } } void -ev_web_view_find_cancel(EvWebView *webview) +ev_web_view_find_set_highlight_search (EvWebView *webview, gboolean value) { - ev_web_view_find_set_highlight_search(webview,FALSE); - webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW(webview)); + webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW(webview),value); } void -ev_web_view_empty_search(EvWebView *webview) -{ - SearchParams *search = webview->search ; - search->case_sensitive = FALSE; - if (search->search_string) - g_free(search->search_string); - search->search_string = NULL; - search->search_jump = TRUE ; +ev_web_view_find_cancel (EvWebView *webview) +{ + g_signal_handlers_disconnect_by_func(webview, + jump_to_find_result, + NULL); + + webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(webview)); + ev_web_view_find_set_highlight_search(webview,FALSE); } /* Selection */ diff --git a/libview/ev-web-view.h b/libview/ev-web-view.h index aa2d5492..751e7381 100644 --- a/libview/ev-web-view.h +++ b/libview/ev-web-view.h @@ -63,11 +63,15 @@ void ev_web_view_handle_link (EvWebView *webview, EvLink* link); /* Searching */ void ev_web_view_find_next (EvWebView *webview); void ev_web_view_find_previous (EvWebView *webview); -void ev_web_view_find_changed (EvWebView *webview, gint page_found_on,EvJobFind *job); -void ev_web_view_find_search_changed (EvWebView *webview); + +void ev_web_view_find_changed (EvWebView *webview, + guint *results, + gchar *text, + gboolean case_sensitive); + +void ev_web_view_find_search_changed (EvWebView *webview,gboolean visible); void ev_web_view_find_cancel (EvWebView *webview); void ev_web_view_find_set_highlight_search (EvWebView *webview,gboolean visible); -void ev_web_view_empty_search (EvWebView *webview); /* Selection */ gboolean ev_web_view_get_has_selection (EvWebView *webview); diff --git a/shell/ev-window.c b/shell/ev-window.c index 43963fec..dfb3d7f2 100644 --- a/shell/ev-window.c +++ b/shell/ev-window.c @@ -3668,7 +3668,7 @@ static void ev_window_cmd_scroll_forward (GtkAction *action, EvWindow *window) { /*If the webview is occupying the window*/ - if ( window->priv->document->iswebdocument == FALSE) return ; + if ( window->priv->document->iswebdocument == TRUE) return ; ev_view_scroll (EV_VIEW (window->priv->view), GTK_SCROLL_PAGE_FORWARD, FALSE); } @@ -3677,7 +3677,7 @@ static void ev_window_cmd_scroll_backward (GtkAction *action, EvWindow *window) { /*If the webview is occupying the window*/ - if ( window->priv->document->iswebdocument == FALSE ) return ; + if ( window->priv->document->iswebdocument == TRUE ) return ; ev_view_scroll (EV_VIEW (window->priv->view), GTK_SCROLL_PAGE_BACKWARD, FALSE); } @@ -5069,7 +5069,7 @@ ev_window_find_job_updated_cb (EvJobFind *job, { ev_window_update_actions (ev_window); if (ev_window->priv->document->iswebdocument == TRUE ) { - ev_web_view_find_changed(EV_WEB_VIEW(ev_window->priv->webview), page,job); + ev_web_view_find_changed(EV_WEB_VIEW(ev_window->priv->webview), job->results,job->text, job->case_sensitive); } else { ev_view_find_changed (EV_VIEW (ev_window->priv->view), @@ -5150,7 +5150,7 @@ find_bar_search_changed_cb (EggFindBar *find_bar, search_string = egg_find_bar_get_search_string (find_bar); if (ev_window->priv->document->iswebdocument) { - ev_web_view_find_search_changed(EV_WEB_VIEW(ev_window->priv->webview)); + ev_web_view_find_search_changed(EV_WEB_VIEW(ev_window->priv->webview),TRUE); } else { ev_view_find_search_changed (EV_VIEW (ev_window->priv->view)); } @@ -5174,9 +5174,7 @@ find_bar_search_changed_cb (EggFindBar *find_bar, ev_window_update_actions (ev_window); egg_find_bar_set_status_text (EGG_FIND_BAR (ev_window->priv->find_bar), NULL); - if (ev_window->priv->document->iswebdocument == TRUE) { - ev_web_view_empty_search(EV_WEB_VIEW(ev_window->priv->webview)); - } else { + if (ev_window->priv->document->iswebdocument == FALSE) { gtk_widget_queue_draw (GTK_WIDGET (ev_window->priv->view)); } } @@ -5199,7 +5197,7 @@ find_bar_visibility_changed_cb (EggFindBar *find_bar, } else { ev_web_view_find_set_highlight_search(EV_WEB_VIEW(ev_window->priv->webview),visible); - ev_web_view_find_search_changed(EV_WEB_VIEW(ev_window->priv->webview)); + ev_web_view_find_search_changed(EV_WEB_VIEW(ev_window->priv->webview),visible); } ev_window_update_actions (ev_window); -- cgit v1.2.1