Skip to content

Commit

Permalink
Excel browse mode: arrows can now move to locked cells in protected s…
Browse files Browse the repository at this point in the history
…heets. Control+arrows move to the edge of regions (just like normal Excel) but don't ignore locked cells. All unlocked cells now have a new unlocked state.

Authors: Michael Curran <mick@nvaccess.org>, Siddhartha <gupta.siddharthasbs@gmail.com>
Fixes #4952.
  • Loading branch information
jcsteh committed Nov 2, 2015
1 parent 0f740c3 commit 7e9bc71
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 1 deletion.
108 changes: 107 additions & 1 deletion source/NVDAObjects/window/excel.py
Expand Up @@ -29,6 +29,7 @@
from .. import NVDAObjectTextInfo
import scriptHandler
import browseMode
import inputCore
import ctypes

xlCenter=-4108
Expand All @@ -38,6 +39,10 @@
xlDistributed=-4117
xlBottom=-4107
xlTop=-4160
xlDown=-4121
xlToLeft=-4159
xlToRight=-4161
xlUp=-4162
xlCellWidthUnitToPixels = 7.5919335705812574139976275207592
xlSheetVisible=-1
alignmentLabels={
Expand Down Expand Up @@ -308,11 +313,90 @@ def _get_isAlive(self):
log.debugWarning("could not compare sheet names",exc_info=True)
return False

def navigationHelper(self,direction):
excelWindowObject=self.rootNVDAObject.excelWindowObject
cellPosition = excelWindowObject.activeCell
try:
if direction == "left":
cellPosition = cellPosition.Offset(0,-1)
elif direction == "right":
cellPosition = cellPosition.Offset(0,1)
elif direction == "up":
cellPosition = cellPosition.Offset(-1,0)
elif direction == "down":
cellPosition = cellPosition.Offset(1,0)
#Start-of-Column
elif direction == "startcol":
cellPosition = cellPosition.end(xlUp)
#Start-of-Row
elif direction == "startrow":
cellPosition = cellPosition.end(xlToLeft)
#End-of-Row
elif direction == "endrow":
cellPosition = cellPosition.end(xlToRight)
#End-of-Column
elif direction == "endcol":
cellPosition = cellPosition.end(xlDown)
else:
return
except COMError:
pass

try:
isMerged=cellPosition.mergeCells
except (COMError,NameError):
isMerged=False
if isMerged:
cellPosition=cellPosition.MergeArea(1)
obj=ExcelMergedCell(windowHandle=self.rootNVDAObject.windowHandle,excelWindowObject=excelWindowObject,excelCellObject=cellPosition)
else:
obj=ExcelCell(windowHandle=self.rootNVDAObject.windowHandle,excelWindowObject=excelWindowObject,excelCellObject=cellPosition)
cellPosition.Select()
cellPosition.Activate()
eventHandler.executeEvent('gainFocus',obj)

def script_moveLeft(self,gesture):
self.navigationHelper("left")

def script_moveRight(self,gesture):
self.navigationHelper("right")

def script_moveUp(self,gesture):
self.navigationHelper("up")

def script_moveDown(self,gesture):
self.navigationHelper("down")

def script_startOfColumn(self,gesture):
self.navigationHelper("startcol")

def script_startOfRow(self,gesture):
self.navigationHelper("startrow")

def script_endOfRow(self,gesture):
self.navigationHelper("endrow")

def script_endOfColumn(self,gesture):
self.navigationHelper("endcol")

def script_activatePosition(self,gesture):
excelApplicationObject = self.rootNVDAObject.excelWorksheetObject.Application
if excelApplicationObject.ActiveSheet.ProtectContents and excelApplicationObject.activeCell.Locked:
# Translators: the description for the Locked cells in excel Browse Mode, if focused for editing
ui.message(_("This cell is non-editable"))
return
# Toggle browse mode pass-through.
self.passThrough = True
browseMode.reportPassThrough(self)
# Translators: Input help mode message for toggle focus and browse mode command in web browsing and other situations.
script_activatePosition.__doc__=_("Toggles between browse mode and focus mode. When in focus mode, keys will pass straight through to the application, allowing you to interact directly with a control. When in browse mode, you can navigate the document with the cursor, quick navigation keys, etc.")
script_activatePosition.category=inputCore.SCRCAT_BROWSEMODE

def __contains__(self,obj):
return winUser.isDescendantWindow(self.rootNVDAObject.windowHandle,obj.windowHandle)


def _get_selection(self):
return self.rootNVDAObject._getSelection()

def _set_selection(self,info):
super(ExcelBrowseModeTreeInterceptor,self)._set_selection(info)
Expand All @@ -339,6 +423,20 @@ def script_elementsList(self,gesture):
script_elementsList.__doc__ = _("Presents a list of charts, cells with comments and cells with formulas")
script_elementsList.ignoreTreeInterceptorPassThrough=True

__gestures = {
"kb:upArrow": "moveUp",
"kb:downArrow":"moveDown",
"kb:leftArrow":"moveLeft",
"kb:rightArrow":"moveRight",
"kb:control+upArrow":"startOfColumn",
"kb:control+downArrow":"endOfColumn",
"kb:control+leftArrow":"startOfRow",
"kb:control+rightArrow":"endOfRow",
"kb:enter": "activatePosition",
"kb(desktop):numpadEnter":"activatePosition",
"kb:space": "activatePosition",
}

class ElementsListDialog(browseMode.ElementsListDialog):

ELEMENT_TYPES=(
Expand Down Expand Up @@ -610,6 +708,12 @@ def _get_firstChild(self):
cell=self.excelWorksheetObject.cells(1,1)
return ExcelCell(windowHandle=self.windowHandle,excelWindowObject=self.excelWindowObject,excelCellObject=cell)

def _get_states(self):
states=super(ExcelWorksheet,self).states
if self.excelWorksheetObject.ProtectContents:
states.add(controlTypes.STATE_PROTECTED)
return states

def script_changeSelection(self,gesture):
oldSelection=api.getFocusObject()
gesture.send()
Expand Down Expand Up @@ -885,6 +989,8 @@ def _get_states(self):
states.add(controlTypes.STATE_CROPPED)
if self._overlapInfo['obscuringRightBy'] > 0:
states.add(controlTypes.STATE_OVERFLOWING)
if self.excelWindowObject.ActiveSheet.ProtectContents and (not self.excelCellObject.Locked):
states.add(controlTypes.STATE_UNLOCKED)
return states

def getCellWidthAndTextWidth(self):
Expand Down
3 changes: 3 additions & 0 deletions source/browseMode.py
Expand Up @@ -241,6 +241,9 @@ def _iterNodesByType(self,itemType,direction="next",pos=None):
"""
return iter(())

def _iterNotLinkBlock(self, direction="next", pos=None):
raise NotImplementedError

def _quickNavScript(self,gesture, itemType, direction, errorMessage, readUnit):
if itemType=="notLinkBlock":
iterFactory=self._iterNotLinkBlock
Expand Down
3 changes: 3 additions & 0 deletions source/controlTypes.py
Expand Up @@ -190,6 +190,7 @@
STATE_OBSCURED=0x4000000000
STATE_CROPPED=0x8000000000
STATE_OVERFLOWING=0x10000000000
STATE_UNLOCKED=0x20000000000

roleLabels={
# Translators: The word for an unknown control type.
Expand Down Expand Up @@ -556,6 +557,8 @@
STATE_CROPPED:_("cropped"),
# Translators: a state that denotes that the object(text) is overflowing into the adjacent space
STATE_OVERFLOWING:_("overflowing"),
# Translators: a state that denotes that the object is unlocked (such as an unlocked cell in a protected Excel spreadsheet).
STATE_UNLOCKED:_("unlocked"),
}

negativeStateLabels={
Expand Down
1 change: 1 addition & 0 deletions user_docs/en/changes.t2t
Expand Up @@ -16,6 +16,7 @@
- In Microsoft Excel, NVDA now reports any input messages set by the sheet author on cells. (#5051)
- Support for the Baum Pronto! V4 and VarioUltra braille displays when connected via Bluetooth. (#3717)
- Support for editing of rich text in Mozilla applications such as Google Docs with braille support enabled in Mozilla Firefox and HTML composition in Mozilla Thunderbird. (#1668)
- In browse mode in Microsoft Excel, you can navigate to locked cells in protected sheets. (#4952)


== Changes ==
Expand Down

0 comments on commit 7e9bc71

Please sign in to comment.