Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / conch / insults / text.py
1 # -*- test-case-name: twisted.conch.test.test_text -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 Character attribute manipulation API
7
8 This module provides a domain-specific language (using Python syntax)
9 for the creation of text with additional display attributes associated
10 with it.  It is intended as an alternative to manually building up
11 strings containing ECMA 48 character attribute control codes.  It
12 currently supports foreground and background colors (black, red,
13 green, yellow, blue, magenta, cyan, and white), intensity selection,
14 underlining, blinking and reverse video.  Character set selection
15 support is planned.
16
17 Character attributes are specified by using two Python operations:
18 attribute lookup and indexing.  For example, the string \"Hello
19 world\" with red foreground and all other attributes set to their
20 defaults, assuming the name twisted.conch.insults.text.attributes has
21 been imported and bound to the name \"A\" (with the statement C{from
22 twisted.conch.insults.text import attributes as A}, for example) one
23 uses this expression::
24
25  | A.fg.red[\"Hello world\"]
26
27 Other foreground colors are set by substituting their name for
28 \"red\".  To set both a foreground and a background color, this
29 expression is used::
30
31  | A.fg.red[A.bg.green[\"Hello world\"]]
32
33 Note that either A.bg.green can be nested within A.fg.red or vice
34 versa.  Also note that multiple items can be nested within a single
35 index operation by separating them with commas::
36
37  | A.bg.green[A.fg.red[\"Hello\"], " ", A.fg.blue[\"world\"]]
38
39 Other character attributes are set in a similar fashion.  To specify a
40 blinking version of the previous expression::
41
42  | A.blink[A.bg.green[A.fg.red[\"Hello\"], " ", A.fg.blue[\"world\"]]]
43
44 C{A.reverseVideo}, C{A.underline}, and C{A.bold} are also valid.
45
46 A third operation is actually supported: unary negation.  This turns
47 off an attribute when an enclosing expression would otherwise have
48 caused it to be on.  For example::
49
50  | A.underline[A.fg.red[\"Hello\", -A.underline[\" world\"]]]
51
52 @author: Jp Calderone
53 """
54
55 from twisted.conch.insults import helper, insults
56
57 class _Attribute(object):
58     def __init__(self):
59         self.children = []
60
61     def __getitem__(self, item):
62         assert isinstance(item, (list, tuple, _Attribute, str))
63         if isinstance(item, (list, tuple)):
64             self.children.extend(item)
65         else:
66             self.children.append(item)
67         return self
68
69     def serialize(self, write, attrs=None):
70         if attrs is None:
71             attrs = helper.CharacterAttribute()
72         for ch in self.children:
73             if isinstance(ch, _Attribute):
74                 ch.serialize(write, attrs.copy())
75             else:
76                 write(attrs.toVT102())
77                 write(ch)
78
79 class _NormalAttr(_Attribute):
80     def serialize(self, write, attrs):
81         attrs.__init__()
82         super(_NormalAttr, self).serialize(write, attrs)
83
84 class _OtherAttr(_Attribute):
85     def __init__(self, attrname, attrvalue):
86         self.attrname = attrname
87         self.attrvalue = attrvalue
88         self.children = []
89
90     def __neg__(self):
91         result = _OtherAttr(self.attrname, not self.attrvalue)
92         result.children.extend(self.children)
93         return result
94
95     def serialize(self, write, attrs):
96         attrs = attrs.wantOne(**{self.attrname: self.attrvalue})
97         super(_OtherAttr, self).serialize(write, attrs)
98
99 class _ColorAttr(_Attribute):
100     def __init__(self, color, ground):
101         self.color = color
102         self.ground = ground
103         self.children = []
104
105     def serialize(self, write, attrs):
106         attrs = attrs.wantOne(**{self.ground: self.color})
107         super(_ColorAttr, self).serialize(write, attrs)
108
109 class _ForegroundColorAttr(_ColorAttr):
110     def __init__(self, color):
111         super(_ForegroundColorAttr, self).__init__(color, 'foreground')
112
113 class _BackgroundColorAttr(_ColorAttr):
114     def __init__(self, color):
115         super(_BackgroundColorAttr, self).__init__(color, 'background')
116
117 class CharacterAttributes(object):
118     class _ColorAttribute(object):
119         def __init__(self, ground):
120             self.ground = ground
121
122         attrs = {
123             'black': helper.BLACK,
124             'red': helper.RED,
125             'green': helper.GREEN,
126             'yellow': helper.YELLOW,
127             'blue': helper.BLUE,
128             'magenta': helper.MAGENTA,
129             'cyan': helper.CYAN,
130             'white': helper.WHITE}
131
132         def __getattr__(self, name):
133             try:
134                 return self.ground(self.attrs[name])
135             except KeyError:
136                 raise AttributeError(name)
137
138     fg = _ColorAttribute(_ForegroundColorAttr)
139     bg = _ColorAttribute(_BackgroundColorAttr)
140
141     attrs = {
142         'bold': insults.BOLD,
143         'blink': insults.BLINK,
144         'underline': insults.UNDERLINE,
145         'reverseVideo': insults.REVERSE_VIDEO}
146
147     def __getattr__(self, name):
148         if name == 'normal':
149             return _NormalAttr()
150         if name in self.attrs:
151             return _OtherAttr(name, True)
152         raise AttributeError(name)
153
154 def flatten(output, attrs):
155     """Serialize a sequence of characters with attribute information
156
157     The resulting string can be interpreted by VT102-compatible
158     terminals so that the contained characters are displayed and, for
159     those attributes which the terminal supports, have the attributes
160     specified in the input.
161
162     For example, if your terminal is VT102 compatible, you might run
163     this for a colorful variation on the \"hello world\" theme::
164
165      | from twisted.conch.insults.text import flatten, attributes as A
166      | from twisted.conch.insults.helper import CharacterAttribute
167      | print flatten(
168      |     A.normal[A.bold[A.fg.red['He'], A.fg.green['ll'], A.fg.magenta['o'], ' ',
169      |                     A.fg.yellow['Wo'], A.fg.blue['rl'], A.fg.cyan['d!']]],
170      |     CharacterAttribute())
171
172     @param output: Object returned by accessing attributes of the
173     module-level attributes object.
174
175     @param attrs: A L{twisted.conch.insults.helper.CharacterAttribute}
176     instance
177
178     @return: A VT102-friendly string
179     """
180     L = []
181     output.serialize(L.append, attrs)
182     return ''.join(L)
183
184 attributes = CharacterAttributes()
185
186 __all__ = ['attributes', 'flatten']