1
1
#appModules/soffice.py
2
2
#A part of NonVisual Desktop Access (NVDA)
3
#Copyright (C) 2006-2010 NVDA Contributors <http://www.nvda-project.org/>
4
3
#This file is covered by the GNU General Public License.
5
4
#See the file COPYING for more details.
5
#Copyright (C) 2006-2010 Michael Curran <mick@kulgan.net>, James Teh <jamie@jantrid.net>
7
from comtypes import COMError
8
import IAccessibleHandler
9
import appModuleHandler
13
class AppModule(_default.AppModule):
15
def event_gainFocus(self,obj,nextHandler):
17
if obj.role==controlTypes.ROLE_EDITABLETEXT:
19
if parent and parent.role==controlTypes.ROLE_CANVAS:
24
api.setNavigatorObject(obj)
13
from compoundDocuments import CompoundDocument
14
from NVDAObjects.JAB import JAB, JABTextInfo
15
from NVDAObjects.IAccessible import IAccessible, IA2TextTextInfo
16
from NVDAObjects.behaviors import EditableText
17
from logHandler import log
19
def gridCoordStringToNumbers(coordString):
20
if len(coordString)<2 or ' ' in coordString or coordString[0].isdigit() or not coordString[-1].isdigit():
21
raise ValueError("bad coord string: %s"%coordString)
24
coordStringRowStartIndex=None
25
for index,ch in enumerate(reversed(coordString)):
27
coordStringRowStartIndex=len(coordString)-index
29
rowNum=int(coordString[coordStringRowStartIndex:])
30
for index,ch in enumerate(reversed(coordString[0:coordStringRowStartIndex])):
31
colNum+=((ord(ch.upper())-ord('A')+1)*(26**index))
34
class JAB_OOTable(JAB):
36
def _get_rowCount(self):
39
def _get_columnCount(self):
42
class JAB_OOTableCell(JAB):
44
role=controlTypes.ROLE_TABLECELL
45
tableCellCoordsInName=True
48
name=super(JAB_OOTableCell,self).name
49
if name and name.startswith('Cell') and name[-2].isdigit():
54
value=super(JAB_OOTableCell,self).value
55
if not value and issubclass(self.TextInfo,JABTextInfo):
56
value=self.makeTextInfo(textInfos.POSITION_ALL).text
59
def _get_states(self):
60
states=super(JAB_OOTableCell,self).states
61
states.discard(controlTypes.STATE_EDITABLE)
64
def _get_rowNumber(self):
66
return gridCoordStringToNumbers(self.name)[0]
70
def _get_columnNumber(self):
72
return gridCoordStringToNumbers(self.name)[1]
76
class SymphonyTextInfo(IA2TextTextInfo):
78
def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
81
startOffset,endOffset,attribsString=obj.IAccessibleTextObject.attributes(offset)
83
log.debugWarning("could not get attributes",exc_info=True)
84
return textInfos.FormatField(),(self._startOffset,self._endOffset)
85
formatField=textInfos.FormatField()
86
if not attribsString and offset>0:
88
attribsString=obj.IAccessibleTextObject.attributes(offset-1)[2]
92
formatField.update(IAccessibleHandler.splitIA2Attribs(attribsString))
95
escapement = int(formatField["CharEscapement"])
102
formatField["text-position"] = textPos
106
formatField["font-name"] = formatField["CharFontName"]
110
formatField["font-size"] = "%spt" % formatField["CharHeight"]
114
formatField["italic"] = formatField["CharPosture"] == "2"
118
formatField["strikethrough"] = formatField["CharStrikeout"] == "1"
122
underline = formatField["CharUnderline"]
123
if underline == "10":
124
# Symphony doesn't provide for semantic communication of spelling errors, so we have to rely on the WAVE underline type.
125
formatField["invalid-spelling"] = True
127
formatField["underline"] = underline != "0"
131
formatField["bold"] = float(formatField["CharWeight"]) > 100
135
color=formatField.pop('CharColor')
139
formatField['color']=colors.RGB.fromString(color)
141
backgroundColor=formatField.pop('CharBackColor')
145
formatField['background-color']=colors.RGB.fromString(backgroundColor)
147
# optimisation: Assume a hyperlink occupies a full attribute run.
149
obj.IAccessibleTextObject.QueryInterface(IAccessibleHandler.IAccessibleHypertext).hyperlinkIndex(offset)
150
formatField["link"] = True
155
# Only include the list item prefix on the first line of the paragraph.
156
numbering = formatField.get("Numbering")
158
formatField["line-prefix"] = numbering.get("NumberingPrefix") or numbering.get("BulletChar")
161
# Symphony exposes some information for the caret position as attributes on the document object.
162
# optimisation: Use the tree interceptor to get the document.
164
docAttribs = obj.treeInterceptor.rootNVDAObject.IA2Attributes
165
except AttributeError:
166
# No tree interceptor, so we can't efficiently fetch this info.
170
formatField["page-number"] = docAttribs["page-number"]
174
formatField["line-number"] = docAttribs["line-number"]
178
return formatField,(startOffset,endOffset)
180
def _getLineOffsets(self, offset):
181
start, end = super(SymphonyTextInfo, self)._getLineOffsets(offset)
182
if offset == 0 and start == 0 and end == 0:
183
# HACK: Symphony doesn't expose any characters at all on empty lines, but this means we don't ever fetch the list item prefix in this case.
184
# Fake a character so that the list item prefix will be spoken on empty lines.
188
def _getStoryLength(self):
189
# HACK: Account for the character faked in _getLineOffsets() so that move() will work.
190
return max(super(SymphonyTextInfo, self)._getStoryLength(), 1)
192
class SymphonyText(IAccessible, EditableText):
193
TextInfo = SymphonyTextInfo
195
def _get_positionInfo(self):
196
level = self.IA2Attributes.get("heading-level")
198
return {"level": int(level)}
199
return super(SymphonyText, self).positionInfo
201
class SymphonyTableCell(IAccessible):
202
"""Silences particular states, and redundant column/row numbers"""
204
TextInfo=SymphonyTextInfo
206
tableCellCoordsInName=True
208
def _get_states(self):
209
states=super(SymphonyTableCell,self).states
210
states.discard(controlTypes.STATE_MULTILINE)
211
states.discard(controlTypes.STATE_EDITABLE)
214
class SymphonyParagraph(SymphonyText):
215
"""Removes redundant information that can be retreaved in other ways."""
219
class AppModule(appModuleHandler.AppModule):
221
def chooseNVDAObjectOverlayClasses(self, obj, clsList):
223
windowClassName=obj.windowClassName
224
if isinstance(obj, IAccessible) and windowClassName in ("SALTMPSUBFRAME", "SALSUBFRAME"):
225
if role==controlTypes.ROLE_TABLECELL:
226
clsList.insert(0, SymphonyTableCell)
227
elif hasattr(obj, "IAccessibleTextObject"):
228
clsList.insert(0, SymphonyText)
229
if role==controlTypes.ROLE_PARAGRAPH:
230
clsList.insert(0, SymphonyParagraph)
231
if isinstance(obj, JAB) and windowClassName == "SALFRAME":
232
if role in (controlTypes.ROLE_PANEL,controlTypes.ROLE_LABEL):
234
if parent and parent.role==controlTypes.ROLE_TABLE:
235
clsList.insert(0,JAB_OOTableCell)
236
elif role==controlTypes.ROLE_TABLE:
237
clsList.insert(0,JAB_OOTable)
239
def event_NVDAObject_init(self, obj):
240
windowClass = obj.windowClassName
241
if isinstance(obj, JAB) and windowClass == "SALFRAME":
242
# OpenOffice.org has some strange role mappings due to its use of JAB.
243
if obj.role == controlTypes.ROLE_CANVAS:
244
obj.role = controlTypes.ROLE_DOCUMENT
246
if windowClass in ("SALTMPSUBFRAME", "SALFRAME") and obj.role in (controlTypes.ROLE_DOCUMENT,controlTypes.ROLE_TEXTFRAME) and obj.description:
247
# This is a word processor document.
248
obj.description = None
249
obj.treeInterceptorClass = CompoundDocument