def selectIndentationBlock(self, selectMultiple=False, successMessage=""): count = scriptHandler.getLastScriptRepeatCount() if selectMultiple and count >= 1: # Just copy selection to the clipboard focus = api.getFocusObject() textInfo = focus.makeTextInfo(textInfos.POSITION_SELECTION) api.copyToClip(textInfo.text) ui.message(successMessage) with self.getLineManager() as lm: if count >= 1: textInfo = lm.getTextInfo() textInfo.collapse() textInfo.updateCaret() # Get the current indentation level text = lm.getText() originalTextInfo = lm.getTextInfo() indentationLevel = self.getIndentLevel(text) onEmptyLine = speech.isBlank(text) if onEmptyLine: return self.endOfDocument(_("Nothing to select")) # Scan each line forward as long as indentation level is greater than current line = lm.getLine() indentLevels = [] while True: result = lm.move(1) if result == 0: if not selectMultiple and count >= 1: core.callLater(100, self.endOfDocument, _("No more indentation blocks!")) break text = lm.getText() newIndentation = self.getIndentLevel(text) if speech.isBlank(text): continue if newIndentation < indentationLevel: if not selectMultiple and count >= 1: core.callLater(100, self.endOfDocument, _("No more indentation blocks!")) #self.endOfDocument(_("No more indentation blocks!")) break elif newIndentation == indentationLevel: if selectMultiple: pass elif count > 0: count -= 1 else: break else: # newIndentation > indentationLevel pass line = lm.getLine() indentLevels.append(newIndentation) selection = originalTextInfo.copy() if line is not None: textInfo = lm.getTextInfo(line) selection.setEndPoint(textInfo, "endToEnd") selection.updateSelection() self.crackle(indentLevels) speech.speakTextInfo(textInfo, unit=textInfos.UNIT_LINE)
def moveInEditable(self, increment, errorMessage, unbounded=False, op=operator.eq, speakOnly=False, moveCount=1): with self.getLineManager() as lm: # Get the current indentation level text = lm.getText() indentationLevel = self.getIndentLevel(text) onEmptyLine = speech.isBlank(text) # Scan each line until we hit the end of the indentation block, the end of the edit area, or find a line with the same indentation level found = False indentLevels = [] while True: result = lm.move(increment) if result == 0: break text = lm.getText() newIndentation = self.getIndentLevel(text) # Skip over empty lines if we didn't start on one. if not onEmptyLine and speech.isBlank(text): continue if op(newIndentation, indentationLevel): # Found it found = True indentationLevel = newIndentation resultLine = lm.getLine() resultText = lm.getText() moveCount -= 1 if moveCount == 0: break elif newIndentation < indentationLevel: # Not found in this indentation block if not unbounded: break indentLevels.append(newIndentation) if found: textInfo = None if not speakOnly: textInfo = lm.updateCaret(resultLine) self.crackle(indentLevels) if textInfo is not None: speech.speakTextInfo(textInfo, unit=textInfos.UNIT_LINE) else: speech.speakText(resultText) else: self.endOfDocument(errorMessage)
def processStringInternal(self, s, symbolLevel, language): index = 0 for match in self.regexp.finditer(s): if speech.isBlank(speech.processText(language,match.group(0), symbolLevel)): # Current punctuation level indicates that punctuation mark matched will not be pronounced, therefore skipping it. continue yield s[index:match.start(0)] yield self.speechCommand index = match.end(0) yield s[index:]
def moveInEditable(self, increment, errorMessage, unbounded=False, op=operator.eq): focus = api.getFocusObject() # Get the current indentation level textInfo = focus.makeTextInfo(textInfos.POSITION_CARET) textInfo.expand(textInfos.UNIT_LINE) indentationLevel = self.getIndentLevel(textInfo.text) onEmptyLine = speech.isBlank(textInfo.text) # Scan each line until we hit the end of the indentation block, the end of the edit area, or find a line with the same indentation level found = False indentLevels = [] while True: result = textInfo.move(textInfos.UNIT_LINE, increment) if result == 0: break textInfo.expand(textInfos.UNIT_LINE) newIndentation = self.getIndentLevel(textInfo.text) # Skip over empty lines if we didn't start on one. if not onEmptyLine and speech.isBlank(textInfo.text): continue if op(newIndentation, indentationLevel): # Found it found = True textInfo.updateCaret() self.crackle(indentLevels) speech.speakTextInfo(textInfo, unit=textInfos.UNIT_LINE) return elif newIndentation < indentationLevel: # Not found in this indentation block if not unbounded: break indentLevels.append(newIndentation ) # If we didn't find it, tell the user if not found: ui.message(errorMessage)
def postProcessSynchronousCommands(speechSequence, symbolLevel): language=speech.getCurrentLanguage() speechSequence = [element for element in speechSequence if not isinstance(element, str) or not speech.isBlank(speech.processText(language,element,symbolLevel)) ] newSequence = [] for (isSynchronous, values) in itertools.groupby(speechSequence, key=lambda x: isinstance(x, PpSynchronousCommand)): if isSynchronous: chain = PpChainCommand(list(values)) duration = chain.getDuration() newSequence.append(chain) newSequence.append(speech.commands.BreakCommand(duration)) else: newSequence.extend(values) newSequence = eloquenceFix(newSequence, language, symbolLevel) return newSequence
def eloquenceFix(speechSequence, language, symbolLevel): """ With some versions of eloquence driver, when the entire utterance has been replaced with audio icons, and therefore there is nothing else to speak, the driver for some reason issues the callback command after the break command, not before. To work around this, we detect this case and remove break command completely. """ nonEmpty = [element for element in speechSequence if isinstance(element, str) and not speech.isBlank(speech.processText(language,element,symbolLevel)) ] if len(nonEmpty) > 0: return speechSequence indicesToRemove = [] for i in range(1, len(speechSequence)): if ( isinstance(speechSequence[i], speech.commands.BreakCommand) and isinstance(speechSequence[i-1], PpChainCommand) ): indicesToRemove.append(i) return [speechSequence[i] for i in range(len(speechSequence)) if i not in indicesToRemove]
def script_indentPaste(self, gesture): clipboardBackup = api.getClipData() try: focus = api.getFocusObject() selection = focus.makeTextInfo(textInfos.POSITION_SELECTION) if len(selection.text) != 0: ui.message(_("Some text selected! Cannot indent-paste.")) return line = focus.makeTextInfo(textInfos.POSITION_CARET) line.collapse() line.expand(textInfos.UNIT_LINE) # Make sure line doesn't include newline characters while len(line.text) > 0 and line.text[-1] in "\r\n": line.move(textInfos.UNIT_CHARACTER, -1, "end") lineLevel = self.getIndentLevel(line.text + "a") #ui.message(f"Level {level}") text = clipboardBackup textLevel = min([ self.getIndentLevel(s) for s in text.splitlines() if not speech.isBlank(s) ]) useTabs = '\t' in text or '\t' in line.text delta = lineLevel - textLevel text = text.replace("\t", " " * 4) if delta > 0: text = "\n".join([" " * delta + s for s in text.splitlines()]) elif delta < 0: text = "\n".join( [s[min(-delta, len(s)):] for s in text.splitlines()]) if useTabs: text = text.replace(" " * 4, "\t") api.copyToClip(text) line.updateSelection() time.sleep(0.1) keyboardHandler.KeyboardInputGesture.fromName("Control+v").send() finally: core.callLater(100, api.copyToClip, clipboardBackup) core.callLater(100, ui.message, _("Pasted"))
def moveInBrowser(self, increment, errorMessage, op): focus = api.getFocusObject() focus = focus.treeInterceptor textInfo = focus.makeTextInfo(textInfos.POSITION_CARET) textInfo.expand(textInfos.UNIT_PARAGRAPH) origLocation= textInfo.NVDAObjectAtStart.location distance = 0 while True: result =textInfo.move(textInfos.UNIT_PARAGRAPH, increment) if result == 0: ui.message(errorMessage) return textInfo.expand(textInfos.UNIT_PARAGRAPH) text = textInfo.text if speech.isBlank(text): continue location = textInfo.NVDAObjectAtStart.location if op(location[0], origLocation[0]): textInfo.collapse(False) textInfo.updateCaret() self.simpleCrackle(distance) ui.message(text) return distance += 1
def test(self, s, symbolLevel): wav = "H:\\drp\\work\\emacspeak\\sounds\\classic\\alarm.wav" wavLength = self.getWavLengthMillis(wav) language = speech.getCurrentLanguage() tone = 500 while "!" in s: index = s.index("!") prefix = s[:index] prefix = prefix.lstrip() pPrefix = speech.processText(language, prefix, symbolLevel) if speech.isBlank(pPrefix): pass else: yield prefix #yield speech.commands.WaveFileCommand(wav) #yield speech.commands.BeepCommand(tone, 100) #yield PpBeepCommand(tone, 100) yield PpWaveFileCommand(wav) tone += 50 #yield speech.commands.BreakCommand(100) s = s[index + 1:] if len(s) > 0: yield s
def getIndentLevel(self, s): if speech.isBlank(s): return 0 indent = speech.splitTextIndentation(s)[0] return len(indent.replace("\t", " " * 4))
def elten_command(ac): global eltenmod global eltenbraille global eltenbrailletext global eltenindex global eltenindexid global eltenqueue try: if(('ac' in ac)==False): return {} if(ac['ac']=="speak"): eltenindex=None eltenindexid=None text="" if('text' in ac): text=ac['text'] if(speech.isBlank(text)==False): queueHandler.queueFunction(queueHandler.eventQueue,speech.speakText,text) if(ac['ac']=="speakindexed"): eltenindex=None eltenindexid=None texts=[] indexes=[] indid=None if('texts' in ac): texts=ac['texts'] if('indexes' in ac): indexes=ac['indexes'] if('indid' in ac): indid=ac['indid'] v=[] for i in range(0, len(texts)): if is_python_3_or_above: if(speech.isBlank(texts[i])): continue if(i<len(indexes)): v.append(EltenIndexCallback(indexes[i], indid)) if(i<len(indexes) and i>0 and texts[i-1]!="" and texts[i-1][-1]=="\n"): v.append(speech.EndUtteranceCommand()) else: eltenindexid=indid if(i<len(indexes)): v.append(speech.IndexCommand(indexes[i])) v.append(texts[i]) speech.cancelSpeech() speech.speak(v) if(ac['ac']=='stop'): speech.cancelSpeech() eltenindex=None eltenindexid=None if(ac['ac']=='sleepmode'): st=eltenmod.sleepMode if('st' in ac): st=ac['st'] eltenmod.sleepMode=st return {'st': st} if(ac['ac']=='init'): pid=0 if('pid' in ac): pid=ac['pid'] eltenmod = appModuleHandler.getAppModuleFromProcessID(pid) def script_eltengesture(self, gesture): global eltenqueue eltenqueue['gestures']+=gesture.identifiers eltenmod.__class__.script_eltengesture = types.MethodType(script_eltengesture, eltenmod.__class__) eltenmod.bindGesture('kb(laptop):NVDA+A', 'eltengesture') eltenmod.bindGesture('kb(laptop):NVDA+L', 'eltengesture') eltenmod.bindGesture('kb(desktop):NVDA+downArrow', 'eltengesture') eltenmod.bindGesture('kb(desktop):NVDA+upArrow', 'eltengesture') if(ac['ac']=='braille'): text="" if('text' in ac): text=ac['text'] if('type' in ac and 'index' in ac): if ac['type']==-1: text=eltenbrailletext[:ac['index']]+eltenbrailletext[ac['index']+1:] elif ac['type']==1: text=eltenbrailletext[:ac['index']]+text+eltenbrailletext[ac['index']:] eltenbrailletext=text+" " regions=[] for phrase in re.split("([.,:/\n?!])", eltenbrailletext): if phrase=="": continue if len(regions)>10000: continue; region=braille.TextRegion(phrase) if hasattr(region, 'parseUndefinedChars'): region.parseUndefinedChars=False region.update() regions.append(region) eltenbraille.regions=regions if('pos' in ac): poses = eltenbraille.rawToBraillePos if(ac['pos']<len(text) and ac['pos']<len(poses)): reg, pos = eltenbraille.bufferPosToRegionPos(poses[ac['pos']]) eltenbraille.scrollTo(reg, pos) if('cursor' in ac and ac['cursor'] is not None): poses = eltenbraille.rawToBraillePos reg, pos = eltenbraille.bufferPosToRegionPos(poses[ac['cursor']]) reg.cursorPos=reg.brailleToRawPos[pos] reg.update() eltenbraille.update() braille.handler.update() if(ac['ac']=='braillepos' and 'pos' in ac and len(eltenbraille.regions)>0): poses = eltenbraille.rawToBraillePos if(ac['pos']<len(poses)): reg, pos = eltenbraille.bufferPosToRegionPos(poses[ac['pos']]) eltenbraille.scrollTo(reg, pos) reg.update() if('cursor' in ac and ac['cursor'] is not None): reg, pos = eltenbraille.bufferPosToRegionPos(poses[ac['cursor']]) reg.cursorPos=reg.brailleToRawPos[pos] reg.update() eltenbraille.update() braille.handler.update() if(ac['ac']=='getversion'): return {'version': 33} if(ac['ac']=='getnvdaversion'): return {'version': buildVersion.version} if(ac['ac']=='getindex'): if(is_python_3_or_above): return {'index': eltenindex, 'indid': eltenindexid} else: return {'index': speech.getLastSpeechIndex(), 'indid': eltenindexid} except Exception as Argument: log.exception("Elten: command thread") return {} return {}
def moveExtended(self, paragraph, caretIndex, direction, regex, errorMsg="Error", reconstructMode="sameIndent"): chimeIfAcrossParagraphs = False if reconstructMode == "always": compatibilityFunc = lambda x, y: True elif reconstructMode == "sameIndent": compatibilityFunc = lambda ti1, ti2: ( ti1.NVDAObjectAtStart.location[0] == ti2.NVDAObjectAtStart. location[0]) and (self.getParagraphStyle(ti1) == self. getParagraphStyle(ti2)) elif reconstructMode == "never": compatibilityFunc = lambda x, y: False else: raise ValueError() context = Context(paragraph, caretIndex) sentenceStr, ti = self.expandSentence( context, regex, direction, compatibilityFunc=compatibilityFunc) if direction == 0: return sentenceStr, ti elif direction > 0: cindex = -1 method = "endToEnd" else: cindex = 0 method = "startToStart" if ti.compareEndPoints(context.textInfos[cindex], method) == 0: # We need to look for the next sentence in the next paragraph. mylog("Looking in the next paragraph.") paragraph = context.textInfos[cindex] counter = 0 while True: counter += 1 if counter > 1000: raise RuntimeError("Infinite loop detected.") paragraph = self.nextParagraph(paragraph, direction) if paragraph is None: self.chimeNoNextSentence(errorMsg) return (None, None) if not speech.isBlank(paragraph.text): break self.chimeCrossParagraphBorder() context = Context(paragraph, 0) if direction < 0: lastPosition = paragraph.copy() lastPosition.collapse(True) # collapse to the end result = lastPosition.move(textInfos.UNIT_CHARACTER, -1) myAssert(result != 0) context.find(lastPosition) else: # Next sentence can be found in the same context # At least its beginning or ending - that sentence will be expanded. mylog("Looking in the same paragraph.") if direction > 0: ti2 = ti.copy() ti2.collapse(True) # Collapse to the end context.find(ti2) else: ti2 = ti.copy() ti2.collapse(False) # to the beginning result = ti2.move(textInfos.UNIT_CHARACTER, -1) myAssert(result != 0) context.find(ti2) chimeIfAcrossParagraphs = True resultSentenceStr, resultTi = self.expandSentence( context, regex, direction, compatibilityFunc=compatibilityFunc) if chimeIfAcrossParagraphs: if ti.compareEndPoints(resultTi, "startToStart") > 0: trailing = ti else: trailing = resultTi for paragraph in context.textInfos: if paragraph.compareEndPoints(trailing, "startToStart") == 0: self.chimeCrossParagraphBorder() break return resultSentenceStr, resultTi
def moveExtended(self, context, direction, regex, errorMsg="Error", reconstructMode="sameIndent"): """ This function is the real implementation of move by sentence algorithm. First, it identifies current sentence by calling expandSentence, which might expand current context if necessary. Then one of two things may happen: 1. If at this point we're touching boundary (for example, when moving forward, the end of the sentence coincides with the end of the last paragraph), then we explicitly advance to the next paragraph, And replace the context. This case might happen, for example, if the next paragraph is not compatible with current one, e.g. due to different formatting. or 2. If we're not touching the boundary, then depending on direction, it either moves context caret (not the real system caret) either at the end of current sentence when moving forward, or one character before the beginning of current sentence when moving backward. Since we're not touching the boundary, this operation is always valid. Then in either case we call expandSentence again with the updated caret location within context, to obtain the resulting sentence. """ chimeIfAcrossParagraphs = False if reconstructMode == "always": compatibilityFunc = lambda x, y: True elif reconstructMode == "sameIndent": compatibilityFunc = lambda ti1, ti2: ( ti1.NVDAObjectAtStart.location[0] == ti2.NVDAObjectAtStart. location[0]) and (self.getParagraphStyle(ti1) == self. getParagraphStyle(ti2)) elif reconstructMode == "never": compatibilityFunc = lambda x, y: False else: raise ValueError() sentenceStr, startTi, startOffset, endTi, endOffset = self.expandSentence( context, regex, direction, compatibilityFunc=compatibilityFunc) if direction == 0: return sentenceStr, context.makeSentenceInfo( startTi, startOffset, endTi, endOffset) elif direction > 0: cindex = -1 else: cindex = 0 if context.isTouchingBoundary(direction, startTi, startOffset, endTi, endOffset): # We need to look for the next sentence in the next paragraph. mylog("Looking in the next paragraph.") paragraph = context.textInfos[cindex] counter = 0 while True: counter += 1 if counter > 1000: raise RuntimeError("Infinite loop detected.") paragraph = self.nextParagraph(paragraph, direction) if paragraph is None: self.chimeNoNextSentence(errorMsg) return (None, None) if not speech.isBlank(paragraph.text): break self.chimeCrossParagraphBorder() context = Context(paragraph, 0) if direction < 0: context.findByOffset(paragraph, len(paragraph.text) - 1) else: # Next sentence can be found in the same context # At least its beginning or ending - that sentence will be expanded. mylog("Looking in the same paragraph.") if direction > 0: context.findByOffset(endTi, endOffset) else: context.findByOffset(startTi, startOffset - 1) chimeIfAcrossParagraphs = True sentenceStr2, startTi2, startOffset2, endTi2, endOffset2 = self.expandSentence( context, regex, direction, compatibilityFunc=compatibilityFunc) if debug: mylog(f"result2: {sentenceStr2}") mylog(f"start: {startOffset2} @ {startTi2.text}") mylog(f"end: {endOffset2} @ {endTi2.text}") if chimeIfAcrossParagraphs: if ((direction > 0 and startOffset2 == 0) or (direction < 0 and startOffset == 0)): mylog(f"Chime!") self.chimeCrossParagraphBorder() info = context.makeSentenceInfo(startTi2, startOffset2, endTi2, endOffset2) if debug: mylog(f"MoveExtended result string: {sentenceStr2}") mylog(f"MoveExtended result: {info.text}") return sentenceStr2, info
def move(self, gesture, increment): focus = api.getFocusObject() if (isinstance(focus, winword.WordDocument) or ("Dynamic_IAccessibleRichEdit" in str(type(focus)) and hasattr(focus, "script_caret_nextSentence") and hasattr(focus, "script_caret_previousSentence"))): if increment > 0: focus.script_caret_nextSentence(gesture) else: focus.script_caret_previousSentence(gesture) return if focus.role in [ controlTypes.ROLE_COMBOBOX, controlTypes.ROLE_LISTITEM ]: try: # The following line will only succeed in BrowserMode. focus.treeInterceptor.script_collapseOrExpandControl(gesture) except AttributeError: gesture.send() return if hasattr(focus, "treeInterceptor") and hasattr( focus.treeInterceptor, "makeTextInfo"): focus = focus.treeInterceptor textInfo = focus.makeTextInfo(textInfos.POSITION_CARET) caretOffset = textInfo._getCaretOffset() textInfo.expand(textInfos.UNIT_PARAGRAPH) caretIndex = caretOffset - textInfo._startOffset while True: text = textInfo.text boundaries = self.splitParagraphIntoSentences(text) # Find the first index in boundaries that is strictly greater than caretIndex j = bisect.bisect_right(boundaries, caretIndex) i = j - 1 # At this point boundaries[i] and boundaries[j] represent # the boundaries of the current sentence. # Testing if we can move to previous/next sentence and still remain within the same paragraph. n = len(boundaries) if (0 <= i + increment < n) and (0 <= j + increment < n): # Next sentence can be found within the same paragraph i += increment j += increment textInfo.collapse() textInfo2 = textInfo.copy() result = textInfo.move(textInfos.UNIT_CHARACTER, boundaries[i]) assert ((result != 0) or (boundaries[i] == 0)) # If we are moving to the very last sentence of the very last paragraph, # then we cannot move textInfo2 to the end of the paragraph. # Move to just one character before that, and then try to move one more character. assert (boundaries[j] > 1) result2 = textInfo2.move(textInfos.UNIT_CHARACTER, boundaries[j] - 1) assert (result2 != 0) textInfo2.move(textInfos.UNIT_CHARACTER, 1) textInfo.setEndPoint(textInfo2, "endToStart") textInfo.updateCaret() ui.message(textInfo.text) return else: # We need to move to previous/next paragraph to find previous/next sentence. self.fancyBeep("AC#EG#", 30, 5, 5) while True: result = textInfo.move(textInfos.UNIT_PARAGRAPH, increment) if result == 0: self.fancyBeep("HF", 100, 50, 50) return textInfo.expand(textInfos.UNIT_PARAGRAPH) if not speech.isBlank(textInfo.text): break textInfo.expand(textInfos.UNIT_PARAGRAPH) # Imaginary caret just before (if moving forward) or just after (if moving backward) this paragraph, # so that the next iteration will pick the very first or the very last sentence of this paragraph. if increment > 0: caretIndex = -1 else: caretIndex = len(textInfo.text)