Skip to content

Commit

Permalink
History in search dialog (PR #8921)
Browse files Browse the repository at this point in the history
Adds history to the Search list in find dialog
- Limit size of search history to 20 items
- Search history is not saved between restarts of NVDA
Closes #8482
  • Loading branch information
marlon-sousa authored and feerrenrut committed Mar 22, 2019
1 parent bae84ed commit e3b2e79
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 11 deletions.
63 changes: 52 additions & 11 deletions source/cursorManager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#cursorManager.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2006-2018 NV Access Limited, Joseph Lee, Derek Riemer, Davy Kager
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
Expand Down Expand Up @@ -27,11 +26,15 @@
import ui
from textInfos import DocumentWithPageTurns

# search history list constants
SEARCH_HISTORY_MOST_RECENT_INDEX = 0
SEARCH_HISTORY_LEAST_RECENT_INDEX = 19

class FindDialog(wx.Dialog):
"""A dialog used to specify text to find in a cursor manager.
"""

def __init__(self, parent, cursorManager, text, caseSensitivity):
def __init__(self, parent, cursorManager, caseSensitivity, searchEntries):
# Translators: Title of a dialog to find text.
super(FindDialog, self).__init__(parent, wx.ID_ANY, _("Find"))
# Have a copy of the active cursor manager, as this is needed later for finding text.
Expand All @@ -42,8 +45,11 @@ def __init__(self, parent, cursorManager, text, caseSensitivity):
# Translators: Dialog text for NvDA's find command.
textToFind = wx.StaticText(self, wx.ID_ANY, label=_("Type the text you wish to find"))
findSizer.Add(textToFind)
self.findTextField = wx.TextCtrl(self, wx.ID_ANY)
self.findTextField.SetValue(text)
self.findTextField = wx.ComboBox(self, wx.ID_ANY, choices = searchEntries,style=wx.CB_DROPDOWN)

# if there is a previous list of searched entries, make sure we present the last searched term selected by default
if searchEntries:
self.findTextField.Select(SEARCH_HISTORY_MOST_RECENT_INDEX)
findSizer.Add(self.findTextField)
mainSizer.Add(findSizer,border=20,flag=wx.LEFT|wx.RIGHT|wx.TOP)
# Translators: An option in find dialog to perform case-sensitive search.
Expand All @@ -59,8 +65,36 @@ def __init__(self, parent, cursorManager, text, caseSensitivity):
self.CentreOnScreen()
self.findTextField.SetFocus()

def updateSearchEntries(self, searchEntries, currentSearchTerm):
if not currentSearchTerm:
return
if not searchEntries:
searchEntries.insert(SEARCH_HISTORY_MOST_RECENT_INDEX, currentSearchTerm)
return
# we can not accept entries that differ only on text case
#because of a wxComboBox limitation on MS Windows
# see https://wxpython.org/Phoenix/docs/html/wx.ComboBox.html
#notice also that python 2 does not offer caseFold functionality
# so lower is the best we can have for comparing strings
for index, item in enumerate(searchEntries):
if(item.lower() == currentSearchTerm.lower()):
# if the user has selected a previous search term in the list or retyped an already listed term , we need to make sure the
# current search term becomes the first item of the list, so that it will appear selected by default when the dialog is
# shown again. If the current search term differs from the current item only in case letters, we will choose to store the
# new search as we can not store both.
searchEntries.pop(index)
searchEntries.insert(SEARCH_HISTORY_MOST_RECENT_INDEX, currentSearchTerm)
return
# not yet listed. Save it.
if len(searchEntries) > SEARCH_HISTORY_LEAST_RECENT_INDEX:
self._truncateSearchHistory(searchEntries)
searchEntries.insert(SEARCH_HISTORY_MOST_RECENT_INDEX, currentSearchTerm)

def onOk(self, evt):
text = self.findTextField.GetValue()
# update the list of searched entries so that it can be exibited in the next find dialog call
self.updateSearchEntries(self.activeCursorManager._searchEntries, text)

caseSensitive = self.caseSensitiveCheckBox.GetValue()
# We must use core.callLater rather than wx.CallLater to ensure that the callback runs within NVDA's core pump.
# If it didn't, and it directly or indirectly called wx.Yield, it could start executing NVDA's core pump from within the yield, causing recursion.
Expand All @@ -70,6 +104,9 @@ def onOk(self, evt):
def onCancel(self, evt):
self.Destroy()

def _truncateSearchHistory(self, entries):
del entries[SEARCH_HISTORY_LEAST_RECENT_INDEX:]

class CursorManager(documentBase.TextContainerObject,baseObject.ScriptableObject):
"""
A mix-in providing caret navigation and selection commands for the object's virtual text range.
Expand All @@ -86,8 +123,13 @@ class CursorManager(documentBase.TextContainerObject,baseObject.ScriptableObject
# Translators: the script category for browse mode
scriptCategory=SCRCAT_BROWSEMODE

_lastFindText=""
_lastCaseSensitivity=False
# History of search terms.
# Sorted in most to least recently searched order.
# First, most recently searched item index: SEARCH_HISTORY_MOST_RECENT_INDEX
# Last, least recently searched item index: SEARCH_HISTORY_LEAST_RECENT_INDEX
# Items that differ only by case will only have one entry.
_searchEntries = []

def __init__(self, *args, **kwargs):
super(CursorManager, self).__init__(*args, **kwargs)
Expand Down Expand Up @@ -158,30 +200,29 @@ def doFindText(self,text,reverse=False,caseSensitive=False):
speech.speakTextInfo(info,reason=controlTypes.REASON_CARET)
else:
wx.CallAfter(gui.messageBox,_('text "%s" not found')%text,_("Find Error"),wx.OK|wx.ICON_ERROR)
CursorManager._lastFindText=text
CursorManager._lastCaseSensitivity=caseSensitive

def script_find(self,gesture):
d = FindDialog(gui.mainFrame, self, self._lastFindText, self._lastCaseSensitivity)
d = FindDialog(gui.mainFrame, self, self._lastCaseSensitivity, self._searchEntries)
gui.mainFrame.prePopup()
d.Show()
gui.mainFrame.postPopup()
# Translators: Input help message for NVDA's find command.
script_find.__doc__ = _("find a text string from the current cursor position")

def script_findNext(self,gesture):
if not self._lastFindText:
if not self._searchEntries:
self.script_find(gesture)
return
self.doFindText(self._lastFindText, caseSensitive = self._lastCaseSensitivity)
self.doFindText(self._searchEntries[SEARCH_HISTORY_MOST_RECENT_INDEX], caseSensitive = self._lastCaseSensitivity)
# Translators: Input help message for find next command.
script_findNext.__doc__ = _("find the next occurrence of the previously entered text string from the current cursor's position")

def script_findPrevious(self,gesture):
if not self._lastFindText:
if not self._searchEntries:
self.script_find(gesture)
return
self.doFindText(self._lastFindText,reverse=True, caseSensitive = self._lastCaseSensitivity)
self.doFindText(self._searchEntries[SEARCH_HISTORY_MOST_RECENT_INDEX], reverse=True, caseSensitive = self._lastCaseSensitivity)
# Translators: Input help message for find previous command.
script_findPrevious.__doc__ = _("find the previous occurrence of the previously entered text string from the current cursor's position")

Expand Down
6 changes: 6 additions & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ What's New in NVDA


%!includeconf: ../changes.t2tconf
= 2019.2 =

== New Features ==
- The find dialog now includes a history of the last 20 searches. (#8482)
- The search history is cleared when NVDA exits or restarts.


= 2019.1 =
Highlights of this release include performance improvements when accessing both Microsoft word and Excel, stability and security improvements such as support for add-ons with version compatibility information, and many other bug fixes.
Expand Down
16 changes: 16 additions & 0 deletions user_docs/en/userGuide.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,22 @@ Once you have chosen an item, you can use the provided buttons in the dialog to
| Browse mode elements list | NVDA+f7 | Lists various types of elements in the current document |
%kc:endInclude

++ Searching for text ++[SearchingForText]
This dialog allows you to search for terms in the current document.
In the "Type the text you wish to find" field, the text to be found can be entered.
While on the text field, you can also use up and down arrows to select previous searched terms.
The "Case sensitive" checkbox makes the search consider uppercase and lowercase letters differently.
For example, with "Case sensitive" selected you can find "NV Access" but not "nv access".
Differences in case will not be saved in the search history.
When NVDA is restarted, the list of previous searched terms is cleared.
Use the following keys for performing searches:
%kc:beginInclude
|| Name | Key | Description |
| Find text | NVDA+f | Opens the search dialog |
| Find next | NVDA+f3 | searches the next occurrence of the current search term |
| Find previous | NVDA+shift+f3 | searches the previous occurrence of the current search term |
%kc:endInclude

++ Embedded Objects ++[ImbeddedObjects]
Pages can include rich content using technologies such as Adobe Flash, Oracle Java and HTML5, as well as applications and dialogs.
Where these are encountered in browse mode, NVDA will report "embedded object", "application" or "dialog", respectively.
Expand Down

0 comments on commit e3b2e79

Please sign in to comment.