From 66ac04006071ae2043c483ae93f8d4fa6888f33f Mon Sep 17 00:00:00 2001 From: Steve Lamerton Date: Tue, 28 Aug 2012 17:13:13 +0000 Subject: [PATCH] Add support for searching and highlighting a wxWebView. Currently supports WebView on GTK and IE. Closes #14045. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72390 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + include/wx/gtk/webview_webkit.h | 12 ++ include/wx/msw/webview_ie.h | 31 +++- include/wx/osx/webview_webkit.h | 3 + include/wx/webview.h | 12 ++ interface/wx/webview.h | 48 ++++++ samples/webview/webview.cpp | 145 ++++++++++++++++ src/gtk/webview_webkit.cpp | 75 +++++++++ src/msw/webview_ie.cpp | 283 ++++++++++++++++++++++++++++++++ 9 files changed, 609 insertions(+), 1 deletion(-) diff --git a/docs/changes.txt b/docs/changes.txt index 86e107d3da..119b685d34 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -535,6 +535,7 @@ All: All (GUI): +- Add support for searching in wxWebView for MSW and GTK (Allonii). - Respect window max size in wxBoxSizer (Nathan Ridge). - Add possibility to hide and show again wxRibbonBar pages (wxBen). - Add wxRibbonBar pages highlighting (wxBen). diff --git a/include/wx/gtk/webview_webkit.h b/include/wx/gtk/webview_webkit.h index d130b827e3..72b41c15ce 100644 --- a/include/wx/gtk/webview_webkit.h +++ b/include/wx/gtk/webview_webkit.h @@ -96,6 +96,9 @@ public: virtual void Undo(); virtual void Redo(); + //Find function + virtual long Find(const wxString& text, int flags = wxWEB_VIEW_FIND_DEFAULT); + //Editing functions virtual void SetEditable(bool enable = true); virtual bool IsEditable() const; @@ -142,6 +145,9 @@ private: void SetWebkitZoom(float level); float GetWebkitZoom() const; + //Find helper function + void FindClear(); + // focus event handler: calls GTKUpdateBitmap() void GTKOnFocus(wxFocusEvent& event); @@ -150,6 +156,12 @@ private: wxVector > m_handlerList; + //variables used for Find() + int m_findFlags; + wxString m_findText; + int m_findPosition; + int m_findCount; + wxDECLARE_DYNAMIC_CLASS(wxWebViewWebKit); }; diff --git a/include/wx/msw/webview_ie.h b/include/wx/msw/webview_ie.h index 99b41af174..0a1f150d5f 100644 --- a/include/wx/msw/webview_ie.h +++ b/include/wx/msw/webview_ie.h @@ -244,10 +244,13 @@ public: /* END OF MSHTMHST.H implementation */ struct IHTMLDocument2; +struct IHTMLElement; +struct IMarkupPointer; class wxFSFile; class ClassFactory; class wxIEContainer; class DocHostUIHandler; +class wxFindPointers; class WXDLLIMPEXP_WEBVIEW wxWebViewIE : public wxWebView { @@ -320,6 +323,9 @@ public: virtual void Undo(); virtual void Redo(); + //Find function + virtual long Find(const wxString& text, int flags = wxWEB_VIEW_FIND_DEFAULT); + //Editing functions virtual void SetEditable(bool enable = true); virtual bool IsEditable() const; @@ -384,10 +390,21 @@ private: bool m_historyLoadingFromList; bool m_historyEnabled; - //Generic helper functions for IHtmlDocument commands + //We store find flag, results and position. + wxVector m_findPointers; + int m_findFlags; + wxString m_findText; + int m_findPosition; + + //Generic helper functions bool CanExecCommand(wxString command) const; void ExecCommand(wxString command); wxCOMPtr GetDocument() const; + bool IsElementVisible(IHTMLElement* elm); + //Find helper functions. + void FindInternal(const wxString& text, int flags, int internal_flag); + long FindNext(int direction = 1); + void FindClear(); //Toggles control features see INTERNETFEATURELIST for values. bool EnableControlFeature(long flag, bool enable = true); @@ -517,6 +534,18 @@ public: DECLARE_IUNKNOWN_METHODS; }; +class wxFindPointers +{ +public: + wxFindPointers(IMarkupPointer *ptrBegin, IMarkupPointer *ptrEnd) + { + begin = ptrBegin; + end = ptrEnd; + } + //The two markup pointers. + IMarkupPointer *begin, *end; +}; + #endif // wxUSE_WEBVIEW && wxUSE_WEBVIEW_IE && defined(__WXMSW__) #endif // wxWebViewIE_H diff --git a/include/wx/osx/webview_webkit.h b/include/wx/osx/webview_webkit.h index 193057fcdf..e876aadf01 100644 --- a/include/wx/osx/webview_webkit.h +++ b/include/wx/osx/webview_webkit.h @@ -86,6 +86,9 @@ public: virtual void Undo(); virtual void Redo(); + //Find function + virtual long Find(const wxString& text, int flags = wxWEB_VIEW_FIND_DEFAULT) { return wxNOT_FOUND; }; + //Clipboard functions virtual bool CanCut() const { return true; } virtual bool CanCopy() const { return true; } diff --git a/include/wx/webview.h b/include/wx/webview.h index 72f9f180de..050c31953e 100644 --- a/include/wx/webview.h +++ b/include/wx/webview.h @@ -68,6 +68,16 @@ enum wxWebViewReloadFlags wxWEB_VIEW_RELOAD_NO_CACHE }; +enum wxWebViewFindFlags +{ + wxWEB_VIEW_FIND_WRAP = 0x0001, + wxWEB_VIEW_FIND_ENTIRE_WORD = 0x0002, + wxWEB_VIEW_FIND_MATCH_CASE = 0x0004, + wxWEB_VIEW_FIND_HIGHLIGHT_RESULT = 0x0008, + wxWEB_VIEW_FIND_BACKWARDS = 0x0010, + wxWEB_VIEW_FIND_DEFAULT = 0 +}; + enum wxWebViewBackend { wxWEB_VIEW_BACKEND_DEFAULT, @@ -181,6 +191,8 @@ public: //Get the pointer to the underlying native engine. virtual void* GetNativeBackend() const = 0; + //Find function + virtual long Find(const wxString& text, int flags = wxWEB_VIEW_FIND_DEFAULT) = 0; protected: virtual void DoSetPage(const wxString& html, const wxString& baseUrl) = 0; diff --git a/interface/wx/webview.h b/interface/wx/webview.h index e7a75785f7..58d1f974b8 100644 --- a/interface/wx/webview.h +++ b/interface/wx/webview.h @@ -69,6 +69,30 @@ enum wxWebViewReloadFlags wxWEB_VIEW_RELOAD_NO_CACHE }; +/** + Find flags used when searching for text on page. +*/ +enum wxWebViewFindFlags +{ + /** Causes the search to restart when end or beginning reached */ + wxWEB_VIEW_FIND_WRAP = 0x0001, + + /** Matches an entire word when searching */ + wxWEB_VIEW_FIND_ENTIRE_WORD = 0x0002, + + /** Match case, i.e. case sensitive searching */ + wxWEB_VIEW_FIND_MATCH_CASE = 0x0004, + + /** Highlights the search results */ + wxWEB_VIEW_FIND_HIGHLIGHT_RESULT = 0x0008, + + /** Searches for phrase in backward direction */ + wxWEB_VIEW_FIND_BACKWARDS = 0x0010, + + /** The default flag, which is simple searching */ + wxWEB_VIEW_FIND_DEFAULT = 0 +}; + /** * List of available backends for wxWebView */ @@ -572,6 +596,30 @@ public: */ virtual void Undo() = 0; + /** + @name Finding + */ + + /** + Finds a phrase on the current page and if found, the control will + scroll the phrase into view and select it. + @param text The phrase to search for. + @param flags The flags for the search. + @return If search phrase was not found in combination with the flags + then @c wxNOT_FOUND is returned. If called for the first time + with search phrase then the total number of results will be + returned. Then for every time its called with the same search + phrase it will return the number of the current match. + @note This function will restart the search if the flags + @c wxWEB_VIEW_FIND_ENTIRE_WORD or @c wxWEB_VIEW_FIND_MATCH_CASE + are changed, since this will require a new search. To reset the + search, for example reseting the highlights call the function + with an empty search phrase. This always returns @c wxNOT_FOUND + on the OSX WebKit backend. + @since 2.9.5 + */ + virtual long Find(const wxString& text, wxWebViewFindFlags flags = wxWEB_VIEW_FIND_DEFAULT) = 0; + /** @name Zoom */ diff --git a/samples/webview/webview.cpp b/samples/webview/webview.cpp index 6e7ab1435a..6c0773b6fe 100644 --- a/samples/webview/webview.cpp +++ b/samples/webview/webview.cpp @@ -136,6 +136,10 @@ public: void OnDeleteSelection(wxCommandEvent& evt); void OnSelectAll(wxCommandEvent& evt); void OnLoadScheme(wxCommandEvent& evt); + void OnFind(wxCommandEvent& evt); + void OnFindDone(wxCommandEvent& evt); + void OnFindText(wxCommandEvent& evt); + void OnFindOptions(wxCommandEvent& evt); private: wxTextCtrl* m_url; @@ -148,6 +152,15 @@ private: wxToolBarToolBase* m_toolbar_reload; wxToolBarToolBase* m_toolbar_tools; + wxToolBarToolBase* m_find_toolbar_done; + wxToolBarToolBase* m_find_toolbar_next; + wxToolBarToolBase* m_find_toolbar_previous; + wxToolBarToolBase* m_find_toolbar_options; + wxMenuItem* m_find_toolbar_wrap; + wxMenuItem* m_find_toolbar_highlight; + wxMenuItem* m_find_toolbar_matchcase; + wxMenuItem* m_find_toolbar_wholeword; + wxMenu* m_tools_menu; wxMenu* m_tools_history_menu; wxMenuItem* m_tools_layout; @@ -171,11 +184,16 @@ private: wxMenuItem* m_scroll_page_down; wxMenuItem* m_selection_clear; wxMenuItem* m_selection_delete; + wxMenuItem* m_find; wxInfoBar *m_info; wxStaticText* m_info_text; + wxTextCtrl* m_find_ctrl; + wxToolBar* m_find_toolbar; wxMenuHistoryMap m_histMenuItems; + wxString m_findText; + int m_findFlags, m_findCount; }; class SourceViewDialog : public wxDialog @@ -240,6 +258,47 @@ WebFrame::WebFrame(const wxString& url) : m_toolbar->Realize(); + // Set find values. + m_findFlags = wxWEB_VIEW_FIND_DEFAULT; + m_findText = wxEmptyString; + m_findCount = 0; + + // Create panel for find toolbar. + wxPanel* panel = new wxPanel(this); + topsizer->Add(panel, wxSizerFlags().Expand()); + + // Create sizer for panel. + wxBoxSizer* panel_sizer = new wxBoxSizer(wxVERTICAL); + panel->SetSizer(panel_sizer); + + // Create the find toolbar. + m_find_toolbar = new wxToolBar(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL|wxTB_TEXT|wxTB_HORZ_LAYOUT); + m_find_toolbar->Hide(); + panel_sizer->Add(m_find_toolbar, wxSizerFlags().Expand()); + + // Create find control. + m_find_ctrl = new wxTextCtrl(m_find_toolbar, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140,-1), wxTE_PROCESS_ENTER); + + + //Find options menu + wxMenu* findmenu = new wxMenu; + m_find_toolbar_wrap = findmenu->AppendCheckItem(wxID_ANY,"Wrap"); + m_find_toolbar_matchcase = findmenu->AppendCheckItem(wxID_ANY,"Match Case"); + m_find_toolbar_wholeword = findmenu->AppendCheckItem(wxID_ANY,"Entire Word"); + m_find_toolbar_highlight = findmenu->AppendCheckItem(wxID_ANY,"Highlight"); + // Add find toolbar tools. + m_find_toolbar->SetToolSeparation(7); + m_find_toolbar_done = m_find_toolbar->AddTool(wxID_ANY, "Close", wxArtProvider::GetBitmap(wxART_CROSS_MARK)); + m_find_toolbar->AddSeparator(); + m_find_toolbar->AddControl(m_find_ctrl, "Find"); + m_find_toolbar->AddSeparator(); + m_find_toolbar_next = m_find_toolbar->AddTool(wxID_ANY, "Next", wxArtProvider::GetBitmap(wxART_GO_DOWN, wxART_TOOLBAR, wxSize(16,16))); + m_find_toolbar_previous = m_find_toolbar->AddTool(wxID_ANY, "Previous", wxArtProvider::GetBitmap(wxART_GO_UP, wxART_TOOLBAR, wxSize(16,16))); + m_find_toolbar->AddSeparator(); + m_find_toolbar_options = m_find_toolbar->AddTool(wxID_ANY, "Options", wxArtProvider::GetBitmap(wxART_PLUS, wxART_TOOLBAR, wxSize(16,16)), "", wxITEM_DROPDOWN); + m_find_toolbar_options->SetDropdownMenu(findmenu); + m_find_toolbar->Realize(); + // Create the info panel m_info = new wxInfoBar(this); topsizer->Add(m_info, wxSizerFlags().Expand()); @@ -275,6 +334,10 @@ WebFrame::WebFrame(const wxString& url) : m_tools_handle_new_window = m_tools_menu->AppendCheckItem(wxID_ANY, _("Handle New Windows")); m_tools_menu->AppendSeparator(); + //Find + m_find = m_tools_menu->Append(wxID_ANY, _("Find")); + m_tools_menu->AppendSeparator(); + //History menu m_tools_history_menu = new wxMenu(); wxMenuItem* clearhist = m_tools_history_menu->Append(wxID_ANY, _("Clear History")); @@ -339,6 +402,20 @@ WebFrame::WebFrame(const wxString& url) : Connect(m_url->GetId(), wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(WebFrame::OnUrl), NULL, this ); + // Connect find toolbar events. + Connect(m_find_toolbar_done->GetId(), wxEVT_COMMAND_TOOL_CLICKED, + wxCommandEventHandler(WebFrame::OnFindDone), NULL, this ); + Connect(m_find_toolbar_next->GetId(), wxEVT_COMMAND_TOOL_CLICKED, + wxCommandEventHandler(WebFrame::OnFindText), NULL, this ); + Connect(m_find_toolbar_previous->GetId(), wxEVT_COMMAND_TOOL_CLICKED, + wxCommandEventHandler(WebFrame::OnFindText), NULL, this ); + + // Connect find control events. + Connect(m_find_ctrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED, + wxCommandEventHandler(WebFrame::OnFindText), NULL, this ); + Connect(m_find_ctrl->GetId(), wxEVT_COMMAND_TEXT_ENTER, + wxCommandEventHandler(WebFrame::OnFindText), NULL, this ); + // Connect the webview events Connect(m_browser->GetId(), wxEVT_COMMAND_WEB_VIEW_NAVIGATING, wxWebViewEventHandler(WebFrame::OnNavigationRequest), NULL, this); @@ -404,6 +481,8 @@ WebFrame::WebFrame(const wxString& url) : wxCommandEventHandler(WebFrame::OnSelectAll), NULL, this ); Connect(loadscheme->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WebFrame::OnLoadScheme), NULL, this ); + Connect(m_find->GetId(), wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(WebFrame::OnFind), NULL, this ); //Connect the idle events Connect(wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(WebFrame::OnIdle), NULL, this); @@ -549,6 +628,72 @@ void WebFrame::OnLoadScheme(wxCommandEvent& WXUNUSED(evt)) m_browser->LoadURL(path); } +void WebFrame::OnFind(wxCommandEvent& WXUNUSED(evt)) +{ + wxString value = m_browser->GetSelectedText(); + if(value.Len() > 150) + { + value.Truncate(150); + } + m_find_ctrl->SetValue(value); + if(!m_find_toolbar->IsShown()){ + m_find_toolbar->Show(true); + SendSizeEvent(); + } + m_find_ctrl->SelectAll(); +} + +void WebFrame::OnFindDone(wxCommandEvent& WXUNUSED(evt)) +{ + m_browser->Find(""); + m_find_toolbar->Show(false); + SendSizeEvent(); +} + +void WebFrame::OnFindText(wxCommandEvent& evt) +{ + int flags = 0; + + if(m_find_toolbar_wrap->IsChecked()) + flags |= wxWEB_VIEW_FIND_WRAP; + if(m_find_toolbar_wholeword->IsChecked()) + flags |= wxWEB_VIEW_FIND_ENTIRE_WORD; + if(m_find_toolbar_matchcase->IsChecked()) + flags |= wxWEB_VIEW_FIND_MATCH_CASE; + if(m_find_toolbar_highlight->IsChecked()) + flags |= wxWEB_VIEW_FIND_HIGHLIGHT_RESULT; + + if(m_find_toolbar_previous->GetId() == evt.GetId()) + flags |= wxWEB_VIEW_FIND_BACKWARDS; + + wxString find_text = m_find_ctrl->GetValue(); + long count = m_browser->Find(find_text, flags); + + if(m_findText != find_text) + { + m_findCount = count; + m_findText = find_text; + } + + if(count != wxNOT_FOUND || find_text.IsEmpty()) + { + m_find_ctrl->SetBackgroundColour(*wxWHITE); + } + else + { + m_find_ctrl->SetBackgroundColour(wxColour(255, 101, 101)); + } + + m_find_ctrl->Refresh(); + + //Log the result, note that count is zero indexed. + if(count != m_findCount) + { + count++; + } + wxLogMessage("Searching for:%s current match:%i/%i", m_findText.c_str(), count, m_findCount); +} + /** * Callback invoked when there is a request to load a new page (for instance * when the user clicks a link) diff --git a/src/gtk/webview_webkit.cpp b/src/gtk/webview_webkit.cpp index ea5abe7c0a..278f445f59 100644 --- a/src/gtk/webview_webkit.cpp +++ b/src/gtk/webview_webkit.cpp @@ -400,6 +400,7 @@ bool wxWebViewWebKit::Create(wxWindow *parent, { m_busy = false; m_guard = false; + FindClear(); // We currently unconditionally impose scrolling in both directions as it's // necessary to show arbitrary pages. @@ -927,6 +928,80 @@ void wxWebViewWebKit::RegisterHandler(wxSharedPtr handler) m_handlerList.push_back(handler); } +long wxWebViewWebKit::Find(const wxString& text, int flags) +{ + bool newSearch = false; + if(text != m_findText || + (flags & wxWEB_VIEW_FIND_MATCH_CASE) != (m_findFlags & wxWEB_VIEW_FIND_MATCH_CASE)) + { + newSearch = true; + //If it is a new search we need to clear existing highlights + webkit_web_view_unmark_text_matches(m_web_view); + webkit_web_view_set_highlight_text_matches(m_web_view, false); + } + + m_findFlags = flags; + m_findText = text; + + //If the search string is empty then we clear any selection and highlight + if(text == "") + { + webkit_web_view_unmark_text_matches(m_web_view); + webkit_web_view_set_highlight_text_matches(m_web_view, false); + ClearSelection(); + return wxNOT_FOUND; + } + + bool wrap = false, matchCase = false, forward = true; + if(flags & wxWEB_VIEW_FIND_WRAP) + wrap = true; + if(flags & wxWEB_VIEW_FIND_MATCH_CASE) + matchCase = true; + if(flags & wxWEB_VIEW_FIND_BACKWARDS) + forward = false; + + if(newSearch) + { + //Initially we mark the matches to know how many we have + m_findCount = webkit_web_view_mark_text_matches(m_web_view, wxGTK_CONV(text), matchCase, 0); + //In this case we return early to match IE behaviour + m_findPosition = -1; + return m_findCount; + } + else + { + if(forward) + m_findPosition++; + else + m_findPosition--; + if(m_findPosition < 0) + m_findPosition += m_findCount; + if(m_findPosition > m_findCount) + m_findPosition -= m_findCount; + } + + //Highlight them if needed + bool highlight = flags & wxWEB_VIEW_FIND_HIGHLIGHT_RESULT ? true : false; + webkit_web_view_set_highlight_text_matches(m_web_view, highlight); + + if(!webkit_web_view_search_text(m_web_view, wxGTK_CONV(text), matchCase, forward, wrap)) + { + m_findPosition = -1; + ClearSelection(); + return wxNOT_FOUND; + } + wxLogMessage(wxString::Format("Returning %d", m_findPosition)); + return newSearch ? m_findCount : m_findPosition; +} + +void wxWebViewWebKit::FindClear() +{ + m_findCount = 0; + m_findFlags = 0; + m_findText = ""; + m_findPosition = -1; +} + // static wxVisualAttributes wxWebViewWebKit::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) diff --git a/src/msw/webview_ie.cpp b/src/msw/webview_ie.cpp index 5797f01c78..de67e1ee32 100644 --- a/src/msw/webview_ie.cpp +++ b/src/msw/webview_ie.cpp @@ -39,6 +39,12 @@ DEFINE_GUID(wxIID_IInternetProtocolRoot,0x79eac9e3,0xbaf9,0x11ce,0x8c,0x82,0,0xa DEFINE_GUID(wxIID_IInternetProtocol,0x79eac9e4,0xbaf9,0x11ce,0x8c,0x82,0,0xaa,0,0x4b,0xa9,0xb); DEFINE_GUID(wxIID_IDocHostUIHandler, 0xbd3f23c0, 0xd43e, 0x11cf, 0x89, 0x3b, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x1a); +enum //Internal find flags +{ + wxWEB_VIEW_FIND_ADD_POINTERS = 0x0001, + wxWEB_VIEW_FIND_REMOVE_HIGHLIGHT = 0x0002 +}; + } wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewIE, wxWebView); @@ -68,6 +74,7 @@ bool wxWebViewIE::Create(wxWindow* parent, m_historyEnabled = true; m_historyPosition = -1; m_zoomType = wxWEB_VIEW_ZOOM_TYPE_TEXT; + FindClear(); if (::CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC_SERVER, // CLSCTX_INPROC, @@ -98,6 +105,7 @@ wxWebViewIE::~wxWebViewIE() { m_factories[i]->Release(); } + FindClear(); } void wxWebViewIE::LoadURL(const wxString& url) @@ -591,6 +599,53 @@ void wxWebViewIE::Redo() ExecCommand("Redo"); } +long wxWebViewIE::Find(const wxString& text, int flags) +{ + //If the text is empty then we clear. + if(text.IsEmpty()) + { + ClearSelection(); + if(m_findFlags & wxWEB_VIEW_FIND_HIGHLIGHT_RESULT) + { + FindInternal(m_findText, (m_findFlags &~ wxWEB_VIEW_FIND_HIGHLIGHT_RESULT), wxWEB_VIEW_FIND_REMOVE_HIGHLIGHT); + } + FindClear(); + return wxNOT_FOUND; + } + //Have we done this search before? + if(m_findText == text) + { + //Just do a highlight? + if((flags & wxWEB_VIEW_FIND_HIGHLIGHT_RESULT) != (m_findFlags & wxWEB_VIEW_FIND_HIGHLIGHT_RESULT)) + { + m_findFlags = flags; + if(!m_findPointers.empty()) + { + FindInternal(m_findText, m_findFlags, ((flags & wxWEB_VIEW_FIND_HIGHLIGHT_RESULT) == 0 ? wxWEB_VIEW_FIND_REMOVE_HIGHLIGHT : 0)); + } + return m_findPosition; + } + else if(((m_findFlags & wxWEB_VIEW_FIND_ENTIRE_WORD) == (flags & wxWEB_VIEW_FIND_ENTIRE_WORD)) && ((m_findFlags & wxWEB_VIEW_FIND_MATCH_CASE) == (flags&wxWEB_VIEW_FIND_MATCH_CASE))) + { + m_findFlags = flags; + return FindNext(((flags & wxWEB_VIEW_FIND_BACKWARDS) ? -1 : 1)); + } + } + //Remove old highlight if any. + if(m_findFlags & wxWEB_VIEW_FIND_HIGHLIGHT_RESULT) + { + FindInternal(m_findText, (m_findFlags &~ wxWEB_VIEW_FIND_HIGHLIGHT_RESULT), wxWEB_VIEW_FIND_REMOVE_HIGHLIGHT); + } + //Reset find variables. + FindClear(); + ClearSelection(); + m_findText = text; + m_findFlags = flags; + //find the text and return count. + FindInternal(text, flags, wxWEB_VIEW_FIND_ADD_POINTERS); + return m_findPointers.empty() ? wxNOT_FOUND : m_findPointers.size(); +} + void wxWebViewIE::SetEditable(bool enable) { wxCOMPtr document(GetDocument()); @@ -856,6 +911,232 @@ wxCOMPtr wxWebViewIE::GetDocument() const return document; } +bool wxWebViewIE::IsElementVisible(IHTMLElement* elm) +{ + IHTMLCurrentStyle* style; + IHTMLElement *elm1 = elm; + IHTMLElement2 *elm2; + BSTR tmp_bstr; + bool is_visible = true; + //This method is not perfect but it does discover most of the hidden elements. + //so if a better solution is found, then please do improve. + while(elm1) + { + if(SUCCEEDED(elm1->QueryInterface(IID_IHTMLElement2, (void**) &elm2))) + { + if(SUCCEEDED(elm2->get_currentStyle(&style))) + { + //Check if the object has the style display:none. + if((style->get_display(&tmp_bstr) != S_OK) || + (tmp_bstr != NULL && (_wcsicmp(tmp_bstr, L"none") == 0))) + { + is_visible = false; + } + //Check if the object has the style visibility:hidden. + if(is_visible && (style->get_visibility(&tmp_bstr) != S_OK) || + (tmp_bstr != NULL && _wcsicmp(tmp_bstr, L"hidden") == 0)) + { + is_visible = false; + } + style->Release(); + } + elm2->Release(); + } + + //Lets check the object's parent element. + IHTMLElement* parent; + if(is_visible && SUCCEEDED(elm1->get_parentElement(&parent))) + { + elm1->Release(); + elm1 = parent; + } + else + { + elm1->Release(); + break; + } + } + return is_visible; +} + +void wxWebViewIE::FindInternal(const wxString& text, int flags, int internal_flag) +{ + IMarkupServices *pIMS; + IMarkupContainer *pIMC; + IMarkupPointer *ptrBegin, *ptrEnd; + IHTMLElement* elm; + long find_flag = 0; + IHTMLDocument2 *document = GetDocument(); + //This function does the acutal work. + if(SUCCEEDED(document->QueryInterface(IID_IMarkupServices, (void **)&pIMS))) + { + if(SUCCEEDED(document->QueryInterface(IID_IMarkupContainer, (void **)&pIMC))) + { + BSTR attr_bstr = SysAllocString(L"style=\"background-color:#ffff00\""); + BSTR text_bstr = SysAllocString(text.wc_str()); + pIMS->CreateMarkupPointer(&ptrBegin); + pIMS->CreateMarkupPointer(&ptrEnd); + + ptrBegin->SetGravity(POINTER_GRAVITY_Right); + ptrBegin->MoveToContainer(pIMC, TRUE); + //Create the find flag from the wx one. + if(flags & wxWEB_VIEW_FIND_ENTIRE_WORD) + { + find_flag |= FINDTEXT_WHOLEWORD; + } + if(flags & wxWEB_VIEW_FIND_MATCH_CASE) + { + find_flag |= FINDTEXT_MATCHCASE; + } + + //A little speed-up to avoid to re-alloc in the positions vector. + if(text.Len() < 3 && m_findPointers.capacity() < 500) + { + m_findPointers.reserve(text.Len() == 1 ? 1000 : 500); + } + + while(ptrBegin->FindText(text_bstr, find_flag, ptrEnd, NULL) == S_OK) + { + if(ptrBegin->CurrentScope(&elm) == S_OK) + { + if(IsElementVisible(elm)) + { + //Highlight the word if the flag was set. + if(flags & wxWEB_VIEW_FIND_HIGHLIGHT_RESULT) + { + IHTMLElement* pFontEl; + pIMS->CreateElement(TAGID_FONT, attr_bstr, &pFontEl); + pIMS->InsertElement(pFontEl, ptrBegin, ptrEnd); + } + if(internal_flag & wxWEB_VIEW_FIND_REMOVE_HIGHLIGHT) + { + IHTMLElement* pFontEl; + ptrBegin->CurrentScope(&pFontEl); + pIMS->RemoveElement(pFontEl); + pFontEl->Release(); + } + if(internal_flag & wxWEB_VIEW_FIND_ADD_POINTERS) + { + IMarkupPointer *cptrBegin, *cptrEnd; + pIMS->CreateMarkupPointer(&cptrBegin); + pIMS->CreateMarkupPointer(&cptrEnd); + cptrBegin->MoveToPointer(ptrBegin); + cptrEnd->MoveToPointer(ptrEnd); + m_findPointers.push_back(wxFindPointers(cptrBegin,cptrEnd)); + } + } + elm->Release(); + } + ptrBegin->MoveToPointer(ptrEnd); + } + //Clean up. + SysFreeString(text_bstr); + SysFreeString(attr_bstr); + pIMC->Release(); + ptrBegin->Release(); + ptrEnd->Release(); + } + pIMS->Release(); + } + document->Release(); +} + +long wxWebViewIE::FindNext(int direction) +{ + //Don't bother if we have no pointers set. + if(m_findPointers.empty()) + { + return wxNOT_FOUND; + } + //Manage the find position and do some checks. + if(direction > 0) + { + m_findPosition++; + } + else + { + m_findPosition--; + } + + if(m_findPosition >= (signed)m_findPointers.size()) + { + if(m_findFlags & wxWEB_VIEW_FIND_WRAP) + { + m_findPosition = 0; + } + else + { + m_findPosition--; + return wxNOT_FOUND; + } + } + else if(m_findPosition < 0) + { + if(m_findFlags & wxWEB_VIEW_FIND_WRAP) + { + m_findPosition = m_findPointers.size()-1; + } + else + { + m_findPosition++; + return wxNOT_FOUND; + } + } + //some variables to use later on. + IHTMLElement *body_element; + IHTMLBodyElement *body; + IHTMLTxtRange *range = NULL; + IMarkupServices *pIMS; + IHTMLDocument2 *document = GetDocument(); + long ret = -1; + //Now try to create a range from the body. + if(SUCCEEDED(document->get_body(&body_element))) + { + if(SUCCEEDED(body_element->QueryInterface(IID_IHTMLBodyElement,(void**)&body))) + { + if(SUCCEEDED(body->createTextRange(&range))) + { + //So far so good, now we try to position our find pointers. + if(SUCCEEDED(document->QueryInterface(IID_IMarkupServices,(void **)&pIMS))) + { + IMarkupPointer *begin = m_findPointers[m_findPosition].begin, *end = m_findPointers[m_findPosition].end; + if(pIMS->MoveRangeToPointers(begin,end,range) == S_OK && range->select() == S_OK) + { + ret = m_findPosition; + } + pIMS->Release(); + } + range->Release(); + } + body->Release(); + } + body_element->Release(); + } + document->Release(); + return ret; +} + +void wxWebViewIE::FindClear() +{ + //Reset find variables. + m_findText.Empty(); + m_findFlags = wxWEB_VIEW_FIND_DEFAULT; + m_findPosition = -1; + + //The m_findPointers contains pointers for the found text. + //Since it uses ref counting we call release on the pointers first + //before we remove them from the vector. In other words do not just + //remove elements from m_findPointers without calling release first + //or you will get a memory leak. + size_t count = m_findPointers.size(); + for(size_t i = 0; i < count; i++) + { + m_findPointers[i].begin->Release(); + m_findPointers[i].end->Release(); + } + m_findPointers.clear(); +} + bool wxWebViewIE::EnableControlFeature(long flag, bool enable) { #if wxUSE_DYNLIB_CLASS @@ -989,6 +1270,8 @@ void wxWebViewIE::onActiveXEvent(wxActiveXEvent& evt) } //Reset as we are done now m_historyLoadingFromList = false; + //Reset the find values. + FindClear(); // TODO: set target parameter if possible wxString target = wxEmptyString; wxWebViewEvent event(wxEVT_COMMAND_WEB_VIEW_LOADED, GetId(), -- 2.45.2