~stomato463/+junk/nvdajp

« back to all changes in this revision

Viewing changes to source/appModules/soffice.py

  • Committer: Masataka Shinke
  • Date: 2011-10-25 12:35:26 UTC
  • mfrom: (4185 jpmain)
  • mto: This revision was merged to the branch mainline in revision 4211.
  • Revision ID: mshinke@users.sourceforge.jp-20111025123526-ze527a2rl3z0g2ky
lp:~nishimotz/nvdajp/main : 4185 をマージ

Show diffs side-by-side

added added

removed removed

Lines of Context:
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>
6
6
 
7
 
import _default
 
7
from comtypes import COMError
 
8
import IAccessibleHandler
 
9
import appModuleHandler
8
10
import controlTypes
9
 
import api
10
 
 
11
 
inDocument=False
12
 
 
13
 
class AppModule(_default.AppModule):
14
 
 
15
 
        def event_gainFocus(self,obj,nextHandler):
16
 
                global inDocument
17
 
                if obj.role==controlTypes.ROLE_EDITABLETEXT:
18
 
                        parent=obj.parent
19
 
                        if parent and parent.role==controlTypes.ROLE_CANVAS:
20
 
                                if not inDocument:
21
 
                                        inDocument=True
22
 
                                        return nextHandler()
23
 
                                else:
24
 
                                        api.setNavigatorObject(obj)
25
 
                                        return
26
 
                inDocument=False
27
 
                return nextHandler()
28
 
 
 
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