home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / orca / liveregions.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  17.0 KB  |  503 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. import bisect
  5. import gobject
  6. import orca_state
  7. import pyatspi
  8. import speech
  9. import copy
  10. import time
  11. from orca_i18n import _
  12. LIVE_OFF = -1
  13. LIVE_NONE = 0
  14. LIVE_POLITE = 1
  15. LIVE_ASSERTIVE = 2
  16. LIVE_RUDE = 3
  17. MSG_KEEPALIVE_TIME = 45
  18. CACHE_SIZE = 9
  19.  
  20. class PriorityQueue:
  21.     ''' This class represents a thread **UNSAFE** priority queue where priority
  22.     is determined by the given integer priority.  The entries are also   
  23.     maintained in chronological order. 
  24.  
  25.     TODO: experiment with Queue.Queue to make thread safe
  26.     '''
  27.     
  28.     def __init__(self):
  29.         self.queue = []
  30.  
  31.     
  32.     def enqueue(self, data, priority, obj):
  33.         ''' Add a new element to the queue according to 1) priority and
  34.         2) timestamp. '''
  35.         bisect.insort_left(self.queue, (priority, time.time(), data, obj))
  36.  
  37.     
  38.     def dequeue(self):
  39.         '''get the highest priority element from the queue.  '''
  40.         return self.queue.pop(0)
  41.  
  42.     
  43.     def clear(self):
  44.         ''' Clear the queue '''
  45.         self.queue = []
  46.  
  47.     
  48.     def purgeByKeepAlive(self):
  49.         ''' Purge items from the queue that are older than the keepalive 
  50.         time '''
  51.         currenttime = time.time()
  52.         
  53.         myfilter = lambda item: item[1] + MSG_KEEPALIVE_TIME > currenttime
  54.         self.queue = filter(myfilter, self.queue)
  55.  
  56.     
  57.     def purgeByPriority(self, priority):
  58.         ''' Purge items from the queue that have a lower than or equal priority
  59.         than the given argument '''
  60.         
  61.         myfilter = lambda item: item[0] > priority
  62.         self.queue = filter(myfilter, self.queue)
  63.  
  64.     
  65.     def clumpContents(self):
  66.         """ Combines messages with the same 'label' by appending newer  
  67.         'content' and removing the newer message.  This operation is only
  68.         applied to the next dequeued message for performance reasons and is
  69.         often applied in conjunction with filterContents() """
  70.         if len(self.queue):
  71.             newqueue = []
  72.             newqueue.append(self.queue[0])
  73.             targetlabels = newqueue[0][2]['labels']
  74.             targetcontent = newqueue[0][2]['content']
  75.             for i in range(1, len(self.queue)):
  76.                 if self.queue[i][2]['labels'] == targetlabels:
  77.                     newqueue[0][2]['content'].extend(self.queue[i][2]['content'])
  78.                     continue
  79.                 newqueue.append(self.queue[i])
  80.             
  81.             self.queue = newqueue
  82.         
  83.  
  84.     
  85.     def filterContents(self):
  86.         ''' Combines utterances by eliminating repeated utterances and
  87.         utterances that are part of other utterances. '''
  88.         if len(self.queue[0][2]['content']) > 1:
  89.             oldcontent = self.queue[0][2]['content']
  90.             newcontent = [
  91.                 oldcontent[0]]
  92.             for i in range(1, len(oldcontent)):
  93.                 found = False
  94.                 for j in range(len(newcontent)):
  95.                     if oldcontent[i].find(newcontent[j]) != -1 or newcontent[j].find(oldcontent[i]) != -1:
  96.                         if len(oldcontent[i]) > len(newcontent[j]):
  97.                             newcontent[j] = oldcontent[i]
  98.                         
  99.                         found = True
  100.                         break
  101.                         continue
  102.                 
  103.                 if not found:
  104.                     newcontent.append(oldcontent[i])
  105.                     continue
  106.             
  107.             self.queue[0][2]['content'] = newcontent
  108.         
  109.  
  110.     
  111.     def __len__(self):
  112.         ''' Return the length of the queue '''
  113.         return len(self.queue)
  114.  
  115.  
  116.  
  117. class LiveRegionManager:
  118.     
  119.     def __init__(self, script):
  120.         self._script = script
  121.         self.msg_queue = PriorityQueue()
  122.         self.msg_cache = []
  123.         self._politenessOverrides = None
  124.         self._restoreOverrides = None
  125.         self.lastliveobj = None
  126.         self.monitoring = True
  127.         self.bookmarkLoadHandler()
  128.         script.bookmarks.addSaveObserver(self.bookmarkSaveHandler)
  129.         script.bookmarks.addLoadObserver(self.bookmarkLoadHandler)
  130.  
  131.     
  132.     def reset(self):
  133.         newpoliteness = { }
  134.         currenturi = self._script.bookmarks.getURIKey()
  135.         for key, value in self._politenessOverrides.iteritems():
  136.             if key[0] == currenturi or value != LIVE_NONE:
  137.                 newpoliteness[key] = value
  138.                 continue
  139.         
  140.         self._politenessOverrides = newpoliteness
  141.  
  142.     
  143.     def bookmarkSaveHandler(self):
  144.         '''Bookmark save callback'''
  145.         self._script.bookmarks.saveBookmarksToDisk(self._politenessOverrides, filename = 'politeness')
  146.  
  147.     
  148.     def bookmarkLoadHandler(self):
  149.         '''Bookmark load callback'''
  150.         if not self._script.bookmarks.readBookmarksFromDisk(filename = 'politeness'):
  151.             pass
  152.         self._politenessOverrides = { }
  153.  
  154.     
  155.     def handleEvent(self, event):
  156.         '''Main live region event handler'''
  157.         politeness = self._getLiveType(event.source)
  158.         if politeness == LIVE_OFF:
  159.             return None
  160.         if politeness == LIVE_NONE:
  161.             if not self.monitoring:
  162.                 return None
  163.         elif politeness == LIVE_POLITE:
  164.             pass
  165.         elif politeness == LIVE_ASSERTIVE:
  166.             self.msg_queue.purgeByPriority(LIVE_POLITE)
  167.         elif politeness == LIVE_RUDE:
  168.             self.msg_queue.purgeByPriority(LIVE_ASSERTIVE)
  169.         
  170.         message = self._getMessage(event)
  171.         if message:
  172.             if len(self.msg_queue) == 0:
  173.                 gobject.timeout_add(100, self.pumpMessages)
  174.             
  175.             self.msg_queue.enqueue(message, politeness, event.source)
  176.         
  177.  
  178.     
  179.     def pumpMessages(self):
  180.         ''' Main gobject callback for live region support.  Handles both 
  181.         purging the message queue and outputting any queued messages that
  182.         were queued up in the handleEvent() method.
  183.         '''
  184.         if len(self.msg_queue) > 0 and not speech.isSpeaking() and time.time() - orca_state.lastInputEvent.time > 1:
  185.             self.msg_queue.purgeByKeepAlive()
  186.             self.msg_queue.clumpContents()
  187.             self.msg_queue.filterContents()
  188.             (politeness, timestamp, message, obj) = self.msg_queue.dequeue()
  189.             if message['labels'] == message['content']:
  190.                 utts = message['content']
  191.             else:
  192.                 utts = message['labels'] + message['content']
  193.             speech.speakUtterances(utts)
  194.             self.lastliveobj = obj
  195.             self._cacheMessage(utts)
  196.         
  197.         if not self.monitoring:
  198.             self.msg_queue.purgeByKeepAlive()
  199.         
  200.         if len(self.msg_queue) > 0:
  201.             return True
  202.         return False
  203.  
  204.     
  205.     def getLiveNoneObjects(self):
  206.         '''Return the live objects that are registered and have a politeness
  207.         of LIVE_NONE. '''
  208.         retval = []
  209.         currenturi = self._script.bookmarks.getURIKey()
  210.         for uri, objectid in self._politenessOverrides:
  211.             if uri == currenturi and isinstance(objectid, tuple):
  212.                 retval.append(self._script.bookmarks.pathToObj(objectid))
  213.                 continue
  214.         
  215.         return retval
  216.  
  217.     
  218.     def advancePoliteness(self, obj):
  219.         '''Advance the politeness level of the given object'''
  220.         utterances = []
  221.         objectid = self._getObjectId(obj)
  222.         uri = self._script.bookmarks.getURIKey()
  223.         
  224.         try:
  225.             cur_priority = self._politenessOverrides[(uri, objectid)]
  226.         except KeyError:
  227.             cur_priority = self._liveStringToType(obj)
  228.  
  229.         if cur_priority == LIVE_OFF or cur_priority == LIVE_NONE:
  230.             self._politenessOverrides[(uri, objectid)] = LIVE_POLITE
  231.             utterances.append(_('setting live region to polite'))
  232.         elif cur_priority == LIVE_POLITE:
  233.             self._politenessOverrides[(uri, objectid)] = LIVE_ASSERTIVE
  234.             utterances.append(_('setting live region to assertive'))
  235.         elif cur_priority == LIVE_ASSERTIVE:
  236.             self._politenessOverrides[(uri, objectid)] = LIVE_RUDE
  237.             utterances.append(_('setting live region to rude'))
  238.         elif cur_priority == LIVE_RUDE:
  239.             self._politenessOverrides[(uri, objectid)] = LIVE_OFF
  240.             utterances.append(_('setting live region to off'))
  241.         
  242.         speech.speakUtterances(utterances)
  243.  
  244.     
  245.     def goLastLiveRegion(self):
  246.         '''Move the caret to the last announced live region and speak the 
  247.         contents of that object'''
  248.         if self.lastliveobj:
  249.             self._script.setCaretPosition(self.lastliveobj, 0)
  250.             self._script.outlineAccessible(self.lastliveobj)
  251.             self._script.speakContents(self._script.getObjectContentsAtOffset(self.lastliveobj, 0))
  252.         
  253.  
  254.     
  255.     def reviewLiveAnnouncement(self, msgnum):
  256.         '''Speak the given number cached message'''
  257.         if msgnum > len(self.msg_cache):
  258.             speech.speak(_('no live message saved'))
  259.         else:
  260.             speech.speakUtterances(self.msg_cache[-msgnum])
  261.  
  262.     
  263.     def setLivePolitenessOff(self):
  264.         '''User toggle to set all live regions to LIVE_OFF or back to their
  265.         original politeness.'''
  266.         docframe = self._script.getDocumentFrame()
  267.         uri = self._script.bookmarks.getURIKey()
  268.         if self.monitoring:
  269.             speech.speak(_('All live regions set to off'))
  270.             self.msg_queue.clear()
  271.             self._restoreOverrides = copy.copy(self._politenessOverrides)
  272.             for override in self._politenessOverrides.keys():
  273.                 self._politenessOverrides[override] = LIVE_OFF
  274.             
  275.             matches = pyatspi.findAllDescendants(docframe, self.matchLiveRegion)
  276.             for match in matches:
  277.                 objectid = self._getObjectId(match)
  278.                 self._politenessOverrides[(uri, objectid)] = LIVE_OFF
  279.             
  280.             self.monitoring = False
  281.         else:
  282.             for key, value in self._restoreOverrides.iteritems():
  283.                 self._politenessOverrides[key] = value
  284.             
  285.             speech.speak(_('live regions politeness levels restored'))
  286.             self.monitoring = True
  287.  
  288.     
  289.     def outputLiveRegionDescription(self, obj):
  290.         '''Used in conjuction with whereAmI to output description and 
  291.         politeness of the given live region object'''
  292.         objectid = self._getObjectId(obj)
  293.         uri = self._script.bookmarks.getURIKey()
  294.         utterances = []
  295.         for relation in obj.getRelationSet():
  296.             relationtype = relation.getRelationType()
  297.             if relationtype == pyatspi.RELATION_DESCRIBED_BY:
  298.                 targetobj = relation.getTarget(0)
  299.                 
  300.                 try:
  301.                     description = targetobj.queryText().getText(0, -1)
  302.                     if description.strip() != obj.description.strip():
  303.                         utterances.append(description)
  304.                 except NotImplemented:
  305.                     pass
  306.                 except:
  307.                     None<EXCEPTION MATCH>NotImplemented
  308.                 
  309.  
  310.             None<EXCEPTION MATCH>NotImplemented
  311.         
  312.         
  313.         try:
  314.             livepriority = self._politenessOverrides[(uri, objectid)]
  315.             liveprioritystr = self._liveTypeToString(livepriority)
  316.         except KeyError:
  317.             liveprioritystr = 'none'
  318.  
  319.         if utterances or liveprioritystr != 'none':
  320.             utterances.append(_('politeness level %s') % liveprioritystr)
  321.             speech.speakUtterances(utterances)
  322.         
  323.  
  324.     
  325.     def matchLiveRegion(self, obj):
  326.         '''Predicate used to find a live region'''
  327.         attrs = self._getAttrDictionary(obj)
  328.         return 'container-live' in attrs
  329.  
  330.     
  331.     def _getMessage(self, event):
  332.         '''Gets the message associated with a given live event.'''
  333.         attrs = self._getAttrDictionary(event.source)
  334.         content = []
  335.         labels = []
  336.         if event.type.startswith('object:children-changed:add'):
  337.             
  338.             try:
  339.                 if attrs['container-atomic'] == 'true':
  340.                     newcontent = self._script.expandEOCs(event.source)
  341.                 else:
  342.                     newcontent = self._script.expandEOCs(event.any_data)
  343.             except (KeyError, TypeError):
  344.                 newcontent = self._script.expandEOCs(event.any_data)
  345.  
  346.             if newcontent:
  347.                 content.append(newcontent)
  348.             else:
  349.                 return None
  350.         newcontent
  351.         
  352.         try:
  353.             sourceitext = event.source.queryText()
  354.         except NotImplementedError:
  355.             return None
  356.  
  357.         txt = sourceitext.getText(0, -1)
  358.         if txt.count(self._script.EMBEDDED_OBJECT_CHARACTER) > 0:
  359.             return None
  360.         
  361.         try:
  362.             if attrs['container-atomic'] == 'true':
  363.                 newcontent = txt
  364.             else:
  365.                 newcontent = txt[event.detail1:event.detail1 + event.detail2]
  366.         except KeyError:
  367.             txt.count(self._script.EMBEDDED_OBJECT_CHARACTER) > 0
  368.             txt.count(self._script.EMBEDDED_OBJECT_CHARACTER) > 0
  369.             newcontent = txt[event.detail1:event.detail1 + event.detail2]
  370.         except:
  371.             txt.count(self._script.EMBEDDED_OBJECT_CHARACTER) > 0
  372.  
  373.         if len(newcontent) > 0:
  374.             content.append(newcontent)
  375.         else:
  376.             return None
  377.         labels = (txt.count(self._script.EMBEDDED_OBJECT_CHARACTER) > 0)._getLabelsAsUtterances(event.source)
  378.         if 'channel' in attrs and attrs['channel'] == 'notify':
  379.             utts = labels + content
  380.             speech.stop()
  381.             speech.speakUtterances(utts)
  382.             return None
  383.         return {
  384.             'content': content,
  385.             'labels': labels }
  386.  
  387.     
  388.     def flushMessages(self):
  389.         self.msg_queue.clear()
  390.  
  391.     
  392.     def _cacheMessage(self, utts):
  393.         '''Cache a message in our cache list of length CACHE_SIZE'''
  394.         self.msg_cache.append(utts)
  395.         if len(self.msg_cache) > CACHE_SIZE:
  396.             self.msg_cache.pop(0)
  397.         
  398.  
  399.     
  400.     def _getLabelsAsUtterances(self, obj):
  401.         '''Get the labels for a given object'''
  402.         uttstring = self._script.getDisplayedLabel(obj)
  403.         if uttstring:
  404.             return [
  405.                 uttstring.strip()]
  406.         return []
  407.  
  408.     
  409.     def _getLiveType(self, obj):
  410.         '''Returns the live politeness setting for a given object. Also,
  411.         registers LIVE_NONE objects in politeness overrides when monitoring.'''
  412.         objectid = self._getObjectId(obj)
  413.         uri = self._script.bookmarks.getURIKey()
  414.         if (uri, objectid) in self._politenessOverrides:
  415.             return self._politenessOverrides[(uri, objectid)]
  416.         livetype = self._liveStringToType(obj)
  417.         if livetype == LIVE_NONE and self.monitoring:
  418.             self._politenessOverrides[(uri, objectid)] = livetype
  419.         
  420.         return livetype
  421.  
  422.     
  423.     def _getObjectId(self, obj):
  424.         """Returns the HTML 'id' or a path to the object is an HTML id is
  425.         unavailable"""
  426.         attrs = self._getAttrDictionary(obj)
  427.         if attrs is None:
  428.             return self._getPath(obj)
  429.         
  430.         try:
  431.             return attrs['id']
  432.         except KeyError:
  433.             attrs is None
  434.             attrs is None
  435.             return self._getPath(obj)
  436.  
  437.  
  438.     
  439.     def _liveStringToType(self, obj, attributes = None):
  440.         '''Returns the politeness enum for a given object'''
  441.         if not attributes:
  442.             pass
  443.         attrs = self._getAttrDictionary(obj)
  444.         
  445.         try:
  446.             if attrs['container-live'] == 'off':
  447.                 return LIVE_OFF
  448.             if attrs['container-live'] == 'polite':
  449.                 return LIVE_POLITE
  450.             if attrs['container-live'] == 'assertive':
  451.                 return LIVE_ASSERTIVE
  452.             if attrs['container-live'] == 'rude':
  453.                 return LIVE_RUDE
  454.             return LIVE_NONE
  455.         except KeyError:
  456.             return LIVE_NONE
  457.  
  458.  
  459.     
  460.     def _liveTypeToString(self, politeness):
  461.         '''Returns the politeness level as a string given a politeness enum'''
  462.         if politeness == LIVE_OFF:
  463.             return 'off'
  464.         if politeness == LIVE_POLITE:
  465.             return 'polite'
  466.         if politeness == LIVE_ASSERTIVE:
  467.             return 'assertive'
  468.         if politeness == LIVE_RUDE:
  469.             return 'rude'
  470.         if politeness == LIVE_NONE:
  471.             return 'none'
  472.         return 'unknown'
  473.  
  474.     
  475.     def _getAttrDictionary(self, obj):
  476.         
  477.         try:
  478.             return []([ attr.split(':', 1) for attr in obj.getAttributes() ])
  479.         except:
  480.             return { }
  481.  
  482.  
  483.     
  484.     def _getPath(self, obj):
  485.         ''' Returns, as a tuple of integers, the path from the given object 
  486.         to the document frame.'''
  487.         docframe = self._script.getDocumentFrame()
  488.         path = []
  489.         while obj.parent is None or obj == docframe:
  490.             path.reverse()
  491.             return tuple(path)
  492.             
  493.             try:
  494.                 path.append(obj.getIndexInParent())
  495.             except Exception:
  496.                 raise LookupError
  497.  
  498.             obj = obj.parent
  499.             continue
  500.             return None
  501.  
  502.  
  503.