Package appModules :: Module soffice
[hide private]
[frames] | no frames]

Source Code for Module appModules.soffice

  1  #appModules/soffice.py 
  2  #A part of NonVisual Desktop Access (NVDA) 
  3  #This file is covered by the GNU General Public License. 
  4  #See the file COPYING for more details. 
  5  #Copyright (C) 2006-2010 Michael Curran <mick@kulgan.net>, James Teh <jamie@jantrid.net> 
  6   
  7  from comtypes import COMError 
  8  import IAccessibleHandler 
  9  import appModuleHandler 
 10  import controlTypes 
 11  import textInfos 
 12  import colors 
 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 
 18   
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) 22 rowNum=0 23 colNum=0 24 coordStringRowStartIndex=None 25 for index,ch in enumerate(reversed(coordString)): 26 if not ch.isdigit(): 27 coordStringRowStartIndex=len(coordString)-index 28 break 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)) 32 return rowNum,colNum
33
34 -class JAB_OOTable(JAB):
35
36 - def _get_rowCount(self):
37 return 0
38
39 - def _get_columnCount(self):
40 return 0
41
42 -class JAB_OOTableCell(JAB):
43 44 role=controlTypes.ROLE_TABLECELL 45 tableCellCoordsInName=True 46
47 - def _get_name(self):
48 name=super(JAB_OOTableCell,self).name 49 if name and name.startswith('Cell') and name[-2].isdigit(): 50 name=name[5:-1] 51 return name
52
53 - def _get_value(self):
54 value=super(JAB_OOTableCell,self).value 55 if not value and issubclass(self.TextInfo,JABTextInfo): 56 value=self.makeTextInfo(textInfos.POSITION_ALL).text 57 return value
58
59 - def _get_states(self):
60 states=super(JAB_OOTableCell,self).states 61 states.discard(controlTypes.STATE_EDITABLE) 62 return states
63
64 - def _get_rowNumber(self):
65 try: 66 return gridCoordStringToNumbers(self.name)[0] 67 except ValueError: 68 return 0
69
70 - def _get_columnNumber(self):
71 try: 72 return gridCoordStringToNumbers(self.name)[1] 73 except ValueError: 74 return 0
75
76 -class SymphonyTextInfo(IA2TextTextInfo):
77
78 - def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
79 obj = self.obj 80 try: 81 startOffset,endOffset,attribsString=obj.IAccessibleTextObject.attributes(offset) 82 except COMError: 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: 87 try: 88 attribsString=obj.IAccessibleTextObject.attributes(offset-1)[2] 89 except COMError: 90 pass 91 if attribsString: 92 formatField.update(IAccessibleHandler.splitIA2Attribs(attribsString)) 93 94 try: 95 escapement = int(formatField["CharEscapement"]) 96 if escapement < 0: 97 textPos = "sub" 98 elif escapement > 0: 99 textPos = "super" 100 else: 101 textPos = "baseline" 102 formatField["text-position"] = textPos 103 except KeyError: 104 pass 105 try: 106 formatField["font-name"] = formatField["CharFontName"] 107 except KeyError: 108 pass 109 try: 110 formatField["font-size"] = "%spt" % formatField["CharHeight"] 111 except KeyError: 112 pass 113 try: 114 formatField["italic"] = formatField["CharPosture"] == "2" 115 except KeyError: 116 pass 117 try: 118 formatField["strikethrough"] = formatField["CharStrikeout"] == "1" 119 except KeyError: 120 pass 121 try: 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 126 else: 127 formatField["underline"] = underline != "0" 128 except KeyError: 129 pass 130 try: 131 formatField["bold"] = float(formatField["CharWeight"]) > 100 132 except KeyError: 133 pass 134 try: 135 color=formatField.pop('CharColor') 136 except KeyError: 137 color=None 138 if color: 139 formatField['color']=colors.RGB.fromString(color) 140 try: 141 backgroundColor=formatField.pop('CharBackColor') 142 except KeyError: 143 backgroundColor=None 144 if backgroundColor: 145 formatField['background-color']=colors.RGB.fromString(backgroundColor) 146 147 # optimisation: Assume a hyperlink occupies a full attribute run. 148 try: 149 obj.IAccessibleTextObject.QueryInterface(IAccessibleHandler.IAccessibleHypertext).hyperlinkIndex(offset) 150 formatField["link"] = True 151 except COMError: 152 pass 153 154 if offset == 0: 155 # Only include the list item prefix on the first line of the paragraph. 156 numbering = formatField.get("Numbering") 157 if numbering: 158 formatField["line-prefix"] = numbering.get("NumberingPrefix") or numbering.get("BulletChar") 159 160 if obj.hasFocus: 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. 163 try: 164 docAttribs = obj.treeInterceptor.rootNVDAObject.IA2Attributes 165 except AttributeError: 166 # No tree interceptor, so we can't efficiently fetch this info. 167 pass 168 else: 169 try: 170 formatField["page-number"] = docAttribs["page-number"] 171 except KeyError: 172 pass 173 try: 174 formatField["line-number"] = docAttribs["line-number"] 175 except KeyError: 176 pass 177 178 return formatField,(startOffset,endOffset)
179
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. 185 return (0, 1) 186 return start, end
187
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)
191
192 -class SymphonyText(IAccessible, EditableText):
193 TextInfo = SymphonyTextInfo 194
195 - def _get_positionInfo(self):
196 level = self.IA2Attributes.get("heading-level") 197 if level: 198 return {"level": int(level)} 199 return super(SymphonyText, self).positionInfo
200
201 -class SymphonyTableCell(IAccessible):
202 """Silences particular states, and redundant column/row numbers""" 203 204 TextInfo=SymphonyTextInfo 205 206 tableCellCoordsInName=True 207
208 - def _get_states(self):
209 states=super(SymphonyTableCell,self).states 210 states.discard(controlTypes.STATE_MULTILINE) 211 states.discard(controlTypes.STATE_EDITABLE) 212 return states
213
214 -class SymphonyParagraph(SymphonyText):
215 """Removes redundant information that can be retreaved in other ways.""" 216 value=None 217 description=None
218
219 -class AppModule(appModuleHandler.AppModule):
220
221 - def chooseNVDAObjectOverlayClasses(self, obj, clsList):
222 role=obj.role 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): 233 parent=obj.parent 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)
238
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 245 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
250