Package Adapters :: Package ATSPI :: Module HypertextAdapter
[hide private]
[frames] | no frames]

Source Code for Module Adapters.ATSPI.HypertextAdapter

  1  ''' 
  2  Defines L{AccAdapt.Adapter}s for AT-SPI hypertext accessibles potentially 
  3  containing embedded objects. 
  4   
  5  @var EMBED_CHAR: Unicode embed character u'\u0xfffc' 
  6  @type EMBED_CHAR: unicode 
  7  @var EMBED_VAL: Unicode embed character value 0xfffc 
  8  @type EMBED_VAL: integer 
  9  @var embed_rex: Compiled regular expression for finding embed characters 
 10  @type embed_rex: _sre.SRE_Pattern 
 11   
 12  @author: Peter Parente 
 13  @organization: IBM Corporation 
 14  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
 15  @license: The BSD License 
 16   
 17  All rights reserved. This program and the accompanying materials are made 
 18  available under the terms of the BSD license which accompanies 
 19  this distribution, and is available at 
 20  U{http://www.opensource.org/licenses/bsd-license.php} 
 21  ''' 
 22  import re 
 23  import pyLinAcc 
 24  from DefaultNav import * 
 25  from DefaultInfo import * 
 26  from TextAdapter import * 
 27  from AEInterfaces import * 
 28  from pyLinAcc import Interfaces, Constants 
 29   
 30  EMBED_CHAR = u'\ufffc' 
 31  EMBED_VAL = 0xfffc 
 32  embed_rex = re.compile(EMBED_CHAR) 
 33   
34 -def _getPrevItem(acc, char):
35 ''' 36 Gets the offset of the start of the previous item. The first of the following 37 rules satisfied working backward from the offset in char defines the start 38 of the previous item: 39 40 1) the previous embed 41 2) the beginning of a wrapped line 42 3) the character one greater than the previous embed on the previous line 43 44 @param acc: Accessible object supporting embed characters 45 @type acc: L{pyLinAcc.Accessible} 46 @param char: Starting offset relative to the start of all text in the object 47 @type char: integer 48 @return: Index of the start of the previous item relative to the start of all 49 text in the object 50 @rtype: integer 51 @raise IndexError: When no previous item is available in this object 52 ''' 53 if char < 0: char = 0 54 it = Interfaces.IText(acc) 55 # get the current line start and end offsets 56 text, lstart, lend = \ 57 it.getTextAtOffset(char, Constants.TEXT_BOUNDARY_LINE_START) 58 # if we're at the beginning of a line, use the previous line instead 59 if lstart == char: 60 text, lstart, lend = \ 61 it.getTextAtOffset(char-1, Constants.TEXT_BOUNDARY_LINE_START) 62 text = unicode(text, 'utf-8') 63 # compute the item offset relative to the start of this line 64 rio = char-lstart 65 # seek backward from the starting offset 66 index = text.rfind(EMBED_CHAR, 0, rio) 67 if index < 0: 68 if rio <= 0: 69 # beginning of text 70 raise IndexError 71 else: 72 # start of line 73 return lstart 74 elif (rio-index) > 1: 75 # if we didn't move back just one character, there's some more text on 76 # the previous line that should count as its own chunk, so move ahead one 77 # again 78 return lstart+index+1 79 else: 80 # return the offset of the embed 81 return lstart+index
82
83 -def _getNextEmbedCharOffset(acc, char):
84 ''' 85 Gets the offset of the start of the next item. The first of the following 86 rules satisfied working forward from the offset in char defines the start 87 of the next item: 88 89 1) the next embed 90 2) the beginning of a wrapped line 91 92 @param acc: Accessible object supporting embed characters 93 @type acc: L{pyLinAcc.Accessible} 94 @param char: Starting offset relative to the start of all text in the object 95 @type char: integer 96 @return: Index of the start of the next item relative to the start of all 97 text in the object 98 @rtype: integer 99 @raise IndexError: When no next item is available in this object 100 ''' 101 if char < 0: char = 0 102 it = Interfaces.IText(acc) 103 # get the current line start and end offsets 104 text, lstart, lend = \ 105 it.getTextAtOffset(char, Constants.TEXT_BOUNDARY_LINE_START) 106 text = unicode(text, 'utf-8') 107 # compute the item offset relative to the start of this line 108 rio = char-lstart 109 # seek forward from the starting offset 110 index = text.find(EMBED_CHAR, rio) 111 if index < 0: 112 if lend >= it.characterCount-1: 113 # end of text 114 raise IndexError 115 else: 116 # end of line 117 return lend 118 return lstart+index
119
120 -def _getEmbedCharOffset(acc, index):
121 ''' 122 Gets the character offset of the embedded character at the given index. 123 124 @param acc: Accessible which should be searched for embed characters 125 @type acc: L{pyLinAcc.Accessible} 126 @param index: Index of the embedded character in the count of all embedded 127 characters 128 @type index: integer 129 @return: Offset in characters of the embedded character 130 @rtype: integer 131 @raise IndexError: When the given index is outside the bounds of the number 132 of embed characters in the text 133 ''' 134 if index is None: 135 # no embedded characters at None index 136 raise IndexError 137 elif index < 0: 138 # no embedded characters, return start index 139 return 0 140 # IHypertext(acc).getLink(index) returns the Hyperlink object associated with 141 # this index 142 hlink = Interfaces.IHypertext(acc).getLink(index) 143 # returns the starting offset of the Hyperlink within the Hypertext object 144 return hlink.startIndex
145 146 #htext.getLinkIndex(self.item_offset) 147 #link = htext.getLink(i-1) 148 #if link is None: 149 #return POR(acc, 0, 0) 150 #else: 151 #return POR(acc, link.endOffset, 0) 152
153 -class HypertextNavAdapter(DefaultNavAdapter):
154 ''' 155 Overrides L{DefaultNavAdapter} to provide navigation over hypertext embedded 156 objects as items and children. Expects the subject to be a L{POR}. 157 158 Adapts subject accessibles that provide the L{pyLinAcc.Interfaces.IHypertext} 159 and L{pyLinAcc.Interfaces.IText} interfaces. 160 ''' 161 provides = [IAccessibleNav, IItemNav] 162 163 @staticmethod
164 - def when(subject):
165 ''' 166 Tests if the given subject can be adapted by this class. 167 168 @param subject: L{POR} containing an accessible to test 169 @type subject: L{POR} 170 @return: True when the subject meets the condition named in the docstring 171 for this class, False otherwise 172 @rtype: boolean 173 ''' 174 acc = subject.accessible 175 Interfaces.IHypertext(acc) 176 Interfaces.IText(acc) 177 return True
178 179 @pyLinAcc.errorToLookupError
180 - def getNextItem(self, only_visible=True):
181 ''' 182 Gets the next item relative to the one indicated by the L{POR} providing 183 this interface. 184 185 Currently ignores only_visible. 186 187 @param only_visible: True when Item in the returned L{POR} must be visible 188 @type only_visible: boolean 189 @return: Point of regard to the next item in the same accessible 190 @rtype: L{POR} 191 @raise IndexError: When there is no next item 192 @raise LookupError: When lookup for the next item fails even though it may 193 exist 194 ''' 195 # get local references to accessible, link offset, etc. 196 acc = self.accessible 197 if self.item_offset is None: 198 char = 0 199 else: 200 char = self.item_offset + 1 201 text = Interfaces.IText(acc) 202 htext = Interfaces.IHypertext(acc) 203 count = text.characterCount 204 # look one character ahead 205 if char >= count: 206 # if it does not exist, raise IndexError 207 raise IndexError 208 elif char != 0 and text.getCharacterAtOffset(char-1) != EMBED_VAL: 209 # if we're currently on text, not an item, find the next embed 210 char = _getNextEmbedCharOffset(acc, char-1) 211 # continue with the remaining if statements using the new char 212 if text.getCharacterAtOffset(char) == EMBED_VAL: 213 # if it's the embed character, return the POR of the embed 214 index = htext.getLinkIndex(char) 215 if index < 0: 216 # raise an error if there is a next embed, but it's not fetchable 217 raise LookupError 218 return POR(acc.getChildAtIndex(index), None, 0) 219 #return IAccessibleNav(por).getFirstItem(only_visible) 220 else: 221 # else, return the POR for the next chunk of text 222 return POR(acc, char, 0)
223 224 @pyLinAcc.errorToLookupError
225 - def getPrevItem(self, only_visible=True):
226 '''' 227 Gets the previous item relative to the one indicated by the L{POR} 228 providing this interface. 229 230 Currently ignores only_visible. 231 232 @param only_visible: True when Item in the returned L{POR} must be visible 233 @type only_visible: boolean 234 @return: Point of regard to the previous item in the same accessible 235 @rtype: L{POR} 236 @raise IndexError: When there is no previous item 237 @raise LookupError: When lookup for the previous item fails even though it 238 may exist 239 ''' 240 # get local references to accessible, link offset, ... 241 acc = self.accessible 242 if self.item_offset is None: 243 raise IndexError 244 #char = 0 245 else: 246 char = self.item_offset - 1 247 text = Interfaces.IText(acc) 248 htext = Interfaces.IHypertext(acc) 249 # look one character back 250 if char < 0: 251 # if it does not exist, return the item offset as None 252 return POR(acc, None, 0) 253 elif text.getCharacterAtOffset(char+1) != EMBED_VAL: 254 # if we're currently on text, not an item, find the previous embed 255 char = _getPrevItem(acc, char+1) 256 # continue with the remaining if statements using the new char 257 258 if text.getCharacterAtOffset(char) == EMBED_VAL: 259 # if it's the embed character, return the POR of the embed 260 index = htext.getLinkIndex(char) 261 if index < 0: 262 # raise an error if there is a previous embed, but it's not fetchable 263 raise LookupError 264 por = POR(acc.getChildAtIndex(index), None, 0) 265 li = IItemNav(por).getLastItem(only_visible) 266 return li 267 else: 268 # else, return the POR for the previous chunk of text 269 por = POR(acc, char, 0) 270 return por
271 272 @pyLinAcc.errorToLookupError
273 - def getLastItem(self, only_visible=True):
274 ''' 275 Gets the last item relative to the one indicated by the L{POR} 276 providing this interface. 277 278 Currently ignores only_visible. 279 280 @param only_visible: True when Item in the returned L{POR} must be visible 281 @type only_visible: boolean 282 @return: Point of regard to the last item in the same accessible 283 @rtype: L{POR} 284 @raise LookupError: When lookup for the last item fails even though it may 285 exist 286 ''' 287 acc = self.accessible 288 text = Interfaces.IText(acc) 289 htext = Interfaces.IHypertext(acc) 290 if text.getCharacterAtOffset(text.characterCount-1) == EMBED_VAL: 291 # return the POR of the last embed 292 por = POR(acc.getChildAtIndex(acc.childCount-1), None, 0) 293 return IItemNav(por).getLastItem(only_visible) 294 else: 295 # get the previous embed + 1 296 char = _getPrevItem(acc, text.characterCount-1) 297 if text.getCharacterAtOffset(char) == EMBED_VAL: 298 # move ahead one from the located embed 299 return POR(acc, char+1, 0) 300 else: 301 # just use the given POR 302 return POR(acc, char, 0)
303 304 @pyLinAcc.errorToLookupError
305 - def getFirstItem(self, only_visible=True):
306 ''' 307 Gets the first item relative to the one indicated by the L{POR} 308 providing this interface. 309 310 Currently ignores only_visible. 311 312 @param only_visible: True when Item in the returned L{POR} must be visible 313 @type only_visible: boolean 314 @return: Point of regard to the first item in the same accessible 315 @rtype: L{POR} 316 @raise LookupError: When lookup for the first item fails even though it may 317 exist 318 ''' 319 # don't recurse into embeds, let a Perk handle that if it doesn't want to 320 # deal with item_offset=None blankness 321 return POR(self.accessible, None, 0)
322 323 @pyLinAcc.errorToLookupError
324 - def getAccAsItem(self, por):
325 ''' 326 Converts the L{POR} to a child accessible to an equivalent L{POR} to an 327 item of the subject. 328 329 @param por: Point of regard to a child of the subject 330 @type por: L{POR} 331 @return: Point of regard to an item of the subject 332 @rtype: L{POR} 333 @raise LookupError: When lookup for the offset fails 334 @raise IndexError: When the offset of the child is invalid as an item index 335 ''' 336 index = IAccessibleInfo(por).getAccIndex() 337 off = _getEmbedCharOffset(self.accessible, index) 338 por = POR(self.accessible, off, 0) 339 return por
340 341 @pyLinAcc.errorToLookupError
342 - def getFirstAccChild(self):
343 ''' 344 Always raises LookupError. Hypertext has no children per se, only embeds. 345 346 @raise LookupError: Always 347 ''' 348 raise LookupError
349 350 @pyLinAcc.errorToLookupError
351 - def getLastAccChild(self):
352 ''' 353 Always raises LookupError. Hypertext has no children per se, only embeds. 354 355 @raise LookupError: Always 356 ''' 357 raise LookupError
358 359 @pyLinAcc.errorToLookupError
360 - def getChildAcc(self, index):
361 ''' 362 Always raises LookupError. Hypertext has no children per se, only embeds. 363 364 @raise LookupError: Always 365 ''' 366 raise LookupError
367
368 -class HypertextAccInfoAdapter(DefaultAccInfoAdapter):
369 ''' 370 Overrides L{DefaultNavAdapter} to provide information about hypertext 371 objects. Expects the subject to be a L{POR}. 372 373 Adapts subject accessibles that provide the L{pyLinAcc.Interfaces.IHypertext} 374 and L{pyLinAcc.Interfaces.IText} interfaces. 375 ''' 376 provides = [IAccessibleInfo] 377 378 @staticmethod
379 - def when(subject):
380 ''' 381 Tests if the given subject can be adapted by this class. 382 383 @param subject: L{POR} containing an accessible to test 384 @type subject: L{POR} 385 @return: True when the subject meets the condition named in the docstring 386 for this class, False otherwise 387 @rtype: boolean 388 ''' 389 acc = subject.accessible 390 off = subject.item_offset 391 Interfaces.IHypertext(acc) 392 text = Interfaces.IText(acc) 393 return True
394
395 - def allowsAccEmbeds(self):
396 ''' 397 Always True. Hypertext allows embedding. 398 399 @return: True 400 @rtype: boolean 401 ''' 402 return True
403 404 @pyLinAcc.errorToLookupError
405 - def getAccItemText(self):
406 ''' 407 Gets a chunk of accessible text past the embed character indicated by the 408 item offset to the next embed character or end of line. 409 410 @return: Accessible text of requested item 411 @rtype: string 412 @raise LookupError: When the accessible object is dead 413 ''' 414 if self.item_offset is None: 415 return self.accessible.name 416 it = Interfaces.IText(self.accessible) 417 # get the current line start and end offsets 418 text, lstart, lend = \ 419 it.getTextAtOffset(self.item_offset,Constants.TEXT_BOUNDARY_LINE_START) 420 # convert to unicode 421 text = unicode(text, 'utf-8') 422 # compute the item offset relative to the start of this line 423 ro = self.item_offset-lstart 424 # locate the next embed character 425 i = text.find(EMBED_CHAR, ro) 426 if i < 0: 427 return text[ro:] # text is substring, not all text which io is relative to 428 else: 429 # slice up to the embed character 430 return text[ro:i]
431
432 -class HypertextEventHandlerAdapter(TextEventHandlerAdapter):
433 ''' 434 Overrides L{DefaultEventHandlerAdapter} to create proper L{POR}s for 435 hypertext objects having embed characters. Expects the subject to be a raw 436 L{pyLinAcc.Accessible}. 437 438 Adapts subject accessibles that provide the L{pyLinAcc.Interfaces.IHypertext} 439 and L{pyLinAcc.Interfaces.IText} interfaces. 440 ''' 441 provides = [IEventHandler] 442 443 @staticmethod
444 - def when(subject):
445 ''' 446 Tests if the given subject can be adapted by this class. 447 448 @param subject: Accessible to test 449 @type subject: L{pyLinAcc.Accessible} 450 @return: True when the subject meets the condition named in the docstring 451 for this class, False otherwise 452 @rtype: boolean 453 ''' 454 Interfaces.IHypertext(subject) 455 Interfaces.IText(subject) 456 return True
457
458 - def _handleFocusEvent(self, event, **kwargs):
459 ''' 460 Creates an L{AEEvent.FocusChange} indicating that the accessible being 461 adapted has gained the focus. Corrects the L{POR} for the focus to account 462 for the case where the hypertext object receiving the focus has an embed 463 character at the first position in its text such that the embedded object 464 should probably be the target of the first selector event instead. 465 466 @param event: Raw focus change event 467 @type event: L{pyLinAcc.Event.Event} 468 @param kwargs: Parameters to be passed to any created L{AEEvent} 469 @type kwargs: dictionary 470 @return: L{AEEvent.FocusChange} and L{AEEvent.SelectorChange} 471 @rtype: tuple of L{AEEvent} 472 ''' 473 focus_por = POR(self.subject, None, 0) 474 # navigate to the first item of the given POR 475 item_por = IItemNav(focus_por).getFirstItem(False) 476 # adapt the accessible to an adapter which provides an IAccesssibleInfo 477 # interface and get the accessible's item text 478 item = IAccessibleInfo(item_por).getAccItemText() 479 # focus events are always in the focus layer 480 kwargs['focused'] = True 481 return (FocusChange(focus_por, True, **kwargs), 482 SelectorChange(item_por, item, **kwargs))
483