2018-05-24 05:31:21 -04:00
|
|
|
#!/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# Fenrir TTY screen reader
|
|
|
|
# By Chrys, Storm Dragon, and contributers.
|
|
|
|
|
|
|
|
from fenrirscreenreader.core import debug
|
2018-05-27 08:31:00 -04:00
|
|
|
from collections import Counter
|
2018-05-24 05:31:21 -04:00
|
|
|
|
|
|
|
class attributeManager():
|
|
|
|
def __init__(self):
|
2018-05-28 03:59:18 -04:00
|
|
|
self.currAttributes = None
|
|
|
|
self.prevAttributes = None
|
2018-05-28 04:42:08 -04:00
|
|
|
self.currAttributeDelta = ''
|
|
|
|
self.currAttributeCursor = None
|
|
|
|
self.prefAttributeCursor = None
|
2018-06-04 00:09:51 -04:00
|
|
|
self.initDefaultAttributes()
|
2018-05-28 17:29:29 -04:00
|
|
|
self.prevLastCursorAttribute = None
|
|
|
|
self.currLastCursorAttribute = None
|
|
|
|
|
2018-05-24 05:31:21 -04:00
|
|
|
def initialize(self, environment):
|
|
|
|
self.env = environment
|
|
|
|
def shutdown(self):
|
|
|
|
pass
|
2018-05-28 17:29:29 -04:00
|
|
|
def setLastCursorAttribute(self, lastCursorAttribute):
|
|
|
|
self.prevLastCursorAttribute = self.currLastCursorAttribute
|
|
|
|
self.currLastCursorAttribute = lastCursorAttribute
|
2019-02-03 17:34:27 -05:00
|
|
|
def resetLastCursorAttribute(self):
|
2018-05-28 17:29:29 -04:00
|
|
|
self.prevLastCursorAttribute = None
|
|
|
|
self.currLastCursorAttribute = None
|
|
|
|
def isLastCursorAttributeChange(self):
|
|
|
|
if self.prevLastCursorAttribute == None:
|
2018-06-04 00:09:51 -04:00
|
|
|
return False
|
2018-05-28 17:29:29 -04:00
|
|
|
return self.prevLastCursorAttribute != self.currLastCursorAttribute
|
2018-05-28 16:24:27 -04:00
|
|
|
def getCurrAttributeCursor(self):
|
|
|
|
return self.currAttributeCursor
|
|
|
|
def isAttributeCursorActive(self):
|
|
|
|
return self.currAttributeCursor != None
|
2018-05-28 16:18:51 -04:00
|
|
|
def isAttributeChange(self):
|
|
|
|
if not self.prevAttributes:
|
|
|
|
return False
|
|
|
|
return self.currAttributes != self.prevAttributes
|
2018-05-28 07:55:13 -04:00
|
|
|
def resetAttributeAll(self):
|
|
|
|
self.resetAttributeDelta()
|
|
|
|
self.resetAttributeCursor()
|
2018-05-28 16:18:51 -04:00
|
|
|
def getAttributeDelta(self):
|
|
|
|
return self.currAttributeDelta
|
2018-05-28 04:42:08 -04:00
|
|
|
def resetAttributeDelta(self):
|
2018-05-28 16:18:51 -04:00
|
|
|
self.currAttributeDelta = ''
|
|
|
|
def setAttributeDelta(self, currAttributeDelta):
|
2018-05-28 08:32:04 -04:00
|
|
|
self.currAttributeDelta = currAttributeDelta
|
2018-05-28 04:42:08 -04:00
|
|
|
def resetAttributeCursor(self):
|
|
|
|
self.currAttributeCursor = None
|
|
|
|
self.prefAttributeCursor = None
|
2018-05-28 16:18:51 -04:00
|
|
|
def setAttributeCursor(self, currAttributeCursor):
|
2018-05-28 08:32:04 -04:00
|
|
|
self.prefAttributeCursor = self.currAttributeCursor
|
2018-05-28 16:18:51 -04:00
|
|
|
self.currAttributeCursor = currAttributeCursor.copy()
|
|
|
|
def resetAttributes(self, currAttributes):
|
2018-05-28 04:42:08 -04:00
|
|
|
self.prevAttributes = None
|
|
|
|
self.currAttributes = currAttributes
|
2018-05-28 16:18:51 -04:00
|
|
|
def setAttributes(self, currAttributes):
|
2018-05-28 04:42:08 -04:00
|
|
|
self.prevAttributes = self.currAttributes
|
|
|
|
self.currAttributes = currAttributes.copy()
|
2018-05-28 04:32:13 -04:00
|
|
|
def getAttributeByXY(self, x, y):
|
|
|
|
if not self.currAttributes:
|
|
|
|
return None
|
|
|
|
if len(self.currAttributes) < y:
|
|
|
|
return None
|
2018-05-28 16:18:51 -04:00
|
|
|
if len(self.currAttributes[y]) < x - 1:
|
|
|
|
return None
|
|
|
|
try:
|
2018-05-28 17:29:29 -04:00
|
|
|
return self.currAttributes[y][x]
|
2018-05-28 16:18:51 -04:00
|
|
|
except KeyError:
|
|
|
|
try:
|
|
|
|
return self.defaultAttributes[0]
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
return None
|
2018-06-04 00:09:51 -04:00
|
|
|
def appendDefaultAttributes(self, attribute):
|
|
|
|
if not attribute:
|
|
|
|
return
|
|
|
|
if len(attribute) != 10:
|
|
|
|
return
|
|
|
|
self.defaultAttributes.append(attribute)
|
|
|
|
def initDefaultAttributes(self):
|
2018-05-29 12:52:28 -04:00
|
|
|
self.defaultAttributes = [None]
|
|
|
|
self.defaultAttributes.append([
|
2018-05-28 16:18:51 -04:00
|
|
|
'default', # fg
|
|
|
|
'default', # bg
|
2018-05-25 06:50:24 -04:00
|
|
|
False, # bold
|
|
|
|
False, # italics
|
|
|
|
False, # underscore
|
|
|
|
False, # strikethrough
|
|
|
|
False, # reverse
|
|
|
|
False, # blink
|
|
|
|
'default', # fontsize
|
|
|
|
'default' # fontfamily
|
2018-06-04 00:09:51 -04:00
|
|
|
]) #end attribute
|
2018-05-28 17:29:29 -04:00
|
|
|
def isDefaultAttribute(self,attribute):
|
2018-05-29 04:54:22 -04:00
|
|
|
return attribute in self.defaultAttributes
|
2018-05-28 17:29:29 -04:00
|
|
|
def hasAttributes(self, cursor, update=True):
|
|
|
|
if not cursor:
|
|
|
|
return False
|
|
|
|
cursorPos = cursor.copy()
|
|
|
|
try:
|
2018-05-29 04:54:22 -04:00
|
|
|
attribute = self.getAttributeByXY( cursorPos['x'], cursorPos['y'])
|
2018-06-04 00:09:51 -04:00
|
|
|
|
2018-05-28 17:29:29 -04:00
|
|
|
if update:
|
2018-05-29 04:54:22 -04:00
|
|
|
self.setLastCursorAttribute(attribute)
|
2018-05-28 17:29:29 -04:00
|
|
|
if not self.isLastCursorAttributeChange():
|
|
|
|
return False
|
2018-06-04 00:09:51 -04:00
|
|
|
|
|
|
|
if self.isDefaultAttribute(attribute):
|
|
|
|
return False
|
|
|
|
|
2018-05-28 17:29:29 -04:00
|
|
|
except Exception as e:
|
|
|
|
return False
|
|
|
|
return True
|
2018-06-04 00:09:51 -04:00
|
|
|
|
2018-05-30 17:44:54 -04:00
|
|
|
def formatAttributes(self, attribute, attributeFormatString = ''):
|
2018-05-25 06:56:03 -04:00
|
|
|
# "black",
|
|
|
|
# "red",
|
|
|
|
# "green",
|
|
|
|
# "brown",
|
|
|
|
# "blue",
|
|
|
|
# "magenta",
|
|
|
|
# "cyan",
|
|
|
|
# "white",
|
|
|
|
# "default" # white.
|
|
|
|
# _order_
|
|
|
|
# "fg",
|
|
|
|
# "bg",
|
|
|
|
# "bold",
|
|
|
|
# "italics",
|
|
|
|
# "underscore",
|
|
|
|
# "strikethrough",
|
|
|
|
# "reverse",
|
|
|
|
# "blink"
|
|
|
|
# "fontsieze"
|
|
|
|
# "fontfamily"
|
2018-05-30 17:44:54 -04:00
|
|
|
if attributeFormatString == '':
|
2018-05-24 05:31:21 -04:00
|
|
|
attributeFormatString = self.env['runtime']['settingsManager'].getSetting('general', 'attributeFormatString')
|
|
|
|
if not attributeFormatString:
|
|
|
|
return ''
|
|
|
|
if attributeFormatString == '':
|
|
|
|
return ''
|
2018-05-25 06:56:03 -04:00
|
|
|
if not attribute:
|
|
|
|
return ''
|
|
|
|
if len(attribute) != 10:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
# 0 FG color (name)
|
|
|
|
try:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirFGColor', _(attribute[0]))
|
|
|
|
except Exception as e:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirFGColor', '')
|
|
|
|
|
|
|
|
# 1 BG color (name)
|
|
|
|
try:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirBGColor', _(attribute[1]))
|
|
|
|
except Exception as e:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirBGColor', '')
|
|
|
|
|
|
|
|
# 2 bold (True/ False)
|
|
|
|
try:
|
|
|
|
if attribute[2]:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirBold', _('bold'))
|
|
|
|
except Exception as e:
|
|
|
|
pass
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirBold', '')
|
|
|
|
|
|
|
|
# 3 italics (True/ False)
|
2018-05-24 05:31:21 -04:00
|
|
|
try:
|
2018-05-25 06:56:03 -04:00
|
|
|
if attribute[3]:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirItalics', _('italic'))
|
2018-05-24 05:31:21 -04:00
|
|
|
except Exception as e:
|
2018-05-25 06:56:03 -04:00
|
|
|
pass
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirItalics', '')
|
|
|
|
|
|
|
|
# 4 underline (True/ False)
|
2018-05-24 05:31:21 -04:00
|
|
|
try:
|
2018-05-25 06:56:03 -04:00
|
|
|
if attribute[4]:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirUnderline', _('underline'))
|
2018-05-24 05:31:21 -04:00
|
|
|
except Exception as e:
|
2018-05-25 06:56:03 -04:00
|
|
|
pass
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirUnderline', '')
|
|
|
|
|
|
|
|
# 5 strikethrough (True/ False)
|
2018-05-24 05:31:21 -04:00
|
|
|
try:
|
2018-05-25 06:56:03 -04:00
|
|
|
if attribute[5]:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirStrikethrough', _('strikethrough'))
|
2018-05-24 05:31:21 -04:00
|
|
|
except Exception as e:
|
2018-05-25 06:56:03 -04:00
|
|
|
pass
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirStrikethrough', '')
|
|
|
|
|
|
|
|
# 6 reverse (True/ False)
|
2018-05-24 05:31:21 -04:00
|
|
|
try:
|
2018-05-25 06:56:03 -04:00
|
|
|
if attribute[6]:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirReverse', _('reverse'))
|
2018-05-24 05:31:21 -04:00
|
|
|
except Exception as e:
|
2018-05-25 06:56:03 -04:00
|
|
|
pass
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirReverse', '')
|
|
|
|
|
|
|
|
# 7 blink (True/ False)
|
2018-05-24 05:31:21 -04:00
|
|
|
try:
|
2018-05-25 06:56:03 -04:00
|
|
|
if attribute[7]:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirBlink', _('blink'))
|
2018-05-24 05:31:21 -04:00
|
|
|
except Exception as e:
|
2018-05-25 06:56:03 -04:00
|
|
|
pass
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirBlink', '')
|
|
|
|
|
|
|
|
# 8 font size (int/ string)
|
2018-05-24 05:31:21 -04:00
|
|
|
try:
|
2018-05-25 06:56:03 -04:00
|
|
|
try:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirFontSize', int(attribute[8]))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
try:
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirFontSize', str(attribute[8]))
|
|
|
|
except:
|
|
|
|
pass
|
2018-05-24 05:31:21 -04:00
|
|
|
except Exception as e:
|
2018-05-25 06:56:03 -04:00
|
|
|
pass
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirFontSize', _('default'))
|
|
|
|
|
|
|
|
# 9 font family (string)
|
2018-05-24 05:31:21 -04:00
|
|
|
try:
|
2018-05-25 06:56:03 -04:00
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirFont', attribute[9])
|
2018-05-24 05:31:21 -04:00
|
|
|
except Exception as e:
|
2018-05-25 06:56:03 -04:00
|
|
|
pass
|
|
|
|
attributeFormatString = attributeFormatString.replace('fenrirFont', _('default'))
|
|
|
|
|
2018-05-24 20:56:03 -04:00
|
|
|
return attributeFormatString
|
2018-05-28 16:18:51 -04:00
|
|
|
def trackHighlights(self):
|
2018-05-27 08:31:00 -04:00
|
|
|
result = ''
|
|
|
|
currCursor = None
|
2018-05-28 03:50:37 -04:00
|
|
|
# screen change
|
2018-05-28 16:18:51 -04:00
|
|
|
if self.prevAttributes == None:
|
2018-05-28 03:50:37 -04:00
|
|
|
return result, currCursor
|
|
|
|
# no change
|
2018-05-28 16:18:51 -04:00
|
|
|
if self.prevAttributes == self.currAttributes:
|
2018-05-27 16:44:17 -04:00
|
|
|
return result, currCursor
|
2018-05-28 03:59:18 -04:00
|
|
|
# error case
|
2018-05-28 16:18:51 -04:00
|
|
|
if self.currAttributes == None:
|
2018-05-28 03:59:18 -04:00
|
|
|
return result, currCursor
|
2018-05-28 03:50:37 -04:00
|
|
|
# special case for pty if not text exists.
|
2018-05-28 16:18:51 -04:00
|
|
|
if len(self.currAttributes) == 0:
|
2018-05-28 03:59:18 -04:00
|
|
|
return result, currCursor
|
2018-05-28 16:18:51 -04:00
|
|
|
text = self.env['runtime']['screenManager'].getScreenText()
|
2018-05-27 08:31:00 -04:00
|
|
|
textLines = text.split('\n')
|
|
|
|
|
2018-05-28 16:18:51 -04:00
|
|
|
if len(textLines) != len(self.currAttributes):
|
2018-05-27 08:31:00 -04:00
|
|
|
return result, currCursor
|
2018-05-28 16:18:51 -04:00
|
|
|
for line in range(len(self.prevAttributes)):
|
|
|
|
if self.prevAttributes[line] != self.currAttributes[line]:
|
|
|
|
for column in range(len(self.prevAttributes[line])):
|
2018-06-09 07:59:54 -04:00
|
|
|
if self.prevAttributes[line][column] == self.currAttributes[line][column]:
|
|
|
|
continue
|
|
|
|
if self.isUsefulForTracking(line, column, self.currAttributes, self.prevAttributes):
|
|
|
|
if not currCursor:
|
|
|
|
currCursor = {'x': column, 'y': line}
|
|
|
|
result += textLines[line][column]
|
2018-05-27 08:31:00 -04:00
|
|
|
result += ' '
|
2018-06-09 07:59:54 -04:00
|
|
|
return result, currCursor
|
2018-06-12 04:07:17 -04:00
|
|
|
def isUsefulForTracking(self, line, column, currAttributes, prevAttributes, attribute=1 , mode = 'zaxe'):
|
2018-06-09 07:59:54 -04:00
|
|
|
if len(currAttributes) <= 3:
|
|
|
|
return False
|
|
|
|
if line < 0:
|
|
|
|
return False
|
|
|
|
if line > len(currAttributes):
|
|
|
|
return False
|
|
|
|
useful = False
|
2018-06-12 04:07:17 -04:00
|
|
|
if mode == 'default':
|
|
|
|
# non default tracking
|
|
|
|
useful = not self.isDefaultAttribute(currAttributes[line][column])
|
|
|
|
elif (mode == 'zaxe') or (mode == ''):
|
|
|
|
# arround me tracking for bg
|
|
|
|
if line == 0:
|
|
|
|
useful = (currAttributes[line][column][attribute] != currAttributes[line + 1][column][attribute]) and (currAttributes[line][column][attribute] != currAttributes[line + 2][column][attribute])
|
|
|
|
elif line >= len(prevAttributes):
|
|
|
|
useful = (currAttributes[line][column][attribute] != currAttributes[line - 1][column][attribute]) and (currAttributes[line][column][attribute] != currAttributes[line - 2][column][attribute])
|
|
|
|
else:
|
|
|
|
useful = (currAttributes[line][column][attribute] != currAttributes[line + 1][column][attribute]) and (currAttributes[line][column][attribute] != currAttributes[line - 1][column][attribute])
|
|
|
|
elif mode == 'barrier':
|
|
|
|
# to be implement
|
|
|
|
useful = True
|
|
|
|
|
2018-06-09 07:59:54 -04:00
|
|
|
return useful
|
|
|
|
|