/usr/lib/python3/dist-packages/urwid/util.py is in python3-urwid 1.3.1-2build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | #!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Urwid utility functions
# Copyright (C) 2004-2011 Ian Ward
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Urwid web site: http://excess.org/urwid/
from urwid import escape
from urwid.compat import bytes
import codecs
str_util = escape.str_util
# bring str_util functions into our namespace
calc_text_pos = str_util.calc_text_pos
calc_width = str_util.calc_width
is_wide_char = str_util.is_wide_char
move_next_char = str_util.move_next_char
move_prev_char = str_util.move_prev_char
within_double_byte = str_util.within_double_byte
def detect_encoding():
# Try to determine if using a supported double-byte encoding
import locale
try:
try:
locale.setlocale(locale.LC_ALL, "")
except locale.Error:
pass
return locale.getlocale()[1] or ""
except ValueError as e:
# with invalid LANG value python will throw ValueError
if e.args and e.args[0].startswith("unknown locale"):
return ""
else:
raise
if 'detected_encoding' not in locals():
detected_encoding = detect_encoding()
else:
assert 0, "It worked!"
_target_encoding = None
_use_dec_special = True
def set_encoding( encoding ):
"""
Set the byte encoding to assume when processing strings and the
encoding to use when converting unicode strings.
"""
encoding = encoding.lower()
global _target_encoding, _use_dec_special
if encoding in ( 'utf-8', 'utf8', 'utf' ):
str_util.set_byte_encoding("utf8")
_use_dec_special = False
elif encoding in ( 'euc-jp' # JISX 0208 only
, 'euc-kr', 'euc-cn', 'euc-tw' # CNS 11643 plain 1 only
, 'gb2312', 'gbk', 'big5', 'cn-gb', 'uhc'
# these shouldn't happen, should they?
, 'eucjp', 'euckr', 'euccn', 'euctw', 'cncb' ):
str_util.set_byte_encoding("wide")
_use_dec_special = True
else:
str_util.set_byte_encoding("narrow")
_use_dec_special = True
# if encoding is valid for conversion from unicode, remember it
_target_encoding = 'ascii'
try:
if encoding:
"".encode(encoding)
_target_encoding = encoding
except LookupError: pass
def get_encoding_mode():
"""
Get the mode Urwid is using when processing text strings.
Returns 'narrow' for 8-bit encodings, 'wide' for CJK encodings
or 'utf8' for UTF-8 encodings.
"""
return str_util.get_byte_encoding()
def apply_target_encoding( s ):
"""
Return (encoded byte string, character set rle).
"""
if _use_dec_special and type(s) == str:
# first convert drawing characters
try:
s = s.translate( escape.DEC_SPECIAL_CHARMAP )
except NotImplementedError:
# python < 2.4 needs to do this the hard way..
for c, alt in zip(escape.DEC_SPECIAL_CHARS,
escape.ALT_DEC_SPECIAL_CHARS):
s = s.replace( c, escape.SO+alt+escape.SI )
if type(s) == str:
s = s.replace(escape.SI+escape.SO, "") # remove redundant shifts
s = codecs.encode(s, _target_encoding, 'replace')
assert isinstance(s, bytes)
SO = escape.SO.encode('ascii')
SI = escape.SI.encode('ascii')
sis = s.split(SO)
assert isinstance(sis[0], bytes)
sis0 = sis[0].replace(SI, bytes())
sout = []
cout = []
if sis0:
sout.append( sis0 )
cout.append( (None,len(sis0)) )
if len(sis)==1:
return sis0, cout
for sn in sis[1:]:
assert isinstance(sn, bytes)
assert isinstance(SI, bytes)
sl = sn.split(SI, 1)
if len(sl) == 1:
sin = sl[0]
assert isinstance(sin, bytes)
sout.append(sin)
rle_append_modify(cout, (escape.DEC_TAG.encode('ascii'), len(sin)))
continue
sin, son = sl
son = son.replace(SI, bytes())
if sin:
sout.append(sin)
rle_append_modify(cout, (escape.DEC_TAG, len(sin)))
if son:
sout.append(son)
rle_append_modify(cout, (None, len(son)))
outstr = bytes().join(sout)
return outstr, cout
######################################################################
# Try to set the encoding using the one detected by the locale module
set_encoding( detected_encoding )
######################################################################
def supports_unicode():
"""
Return True if python is able to convert non-ascii unicode strings
to the current encoding.
"""
return _target_encoding and _target_encoding != 'ascii'
def calc_trim_text( text, start_offs, end_offs, start_col, end_col ):
"""
Calculate the result of trimming text.
start_offs -- offset into text to treat as screen column 0
end_offs -- offset into text to treat as the end of the line
start_col -- screen column to trim at the left
end_col -- screen column to trim at the right
Returns (start, end, pad_left, pad_right), where:
start -- resulting start offset
end -- resulting end offset
pad_left -- 0 for no pad or 1 for one space to be added
pad_right -- 0 for no pad or 1 for one space to be added
"""
spos = start_offs
pad_left = pad_right = 0
if start_col > 0:
spos, sc = calc_text_pos( text, spos, end_offs, start_col )
if sc < start_col:
pad_left = 1
spos, sc = calc_text_pos( text, start_offs,
end_offs, start_col+1 )
run = end_col - start_col - pad_left
pos, sc = calc_text_pos( text, spos, end_offs, run )
if sc < run:
pad_right = 1
return ( spos, pos, pad_left, pad_right )
def trim_text_attr_cs( text, attr, cs, start_col, end_col ):
"""
Return ( trimmed text, trimmed attr, trimmed cs ).
"""
spos, epos, pad_left, pad_right = calc_trim_text(
text, 0, len(text), start_col, end_col )
attrtr = rle_subseg( attr, spos, epos )
cstr = rle_subseg( cs, spos, epos )
if pad_left:
al = rle_get_at( attr, spos-1 )
rle_append_beginning_modify( attrtr, (al, 1) )
rle_append_beginning_modify( cstr, (None, 1) )
if pad_right:
al = rle_get_at( attr, epos )
rle_append_modify( attrtr, (al, 1) )
rle_append_modify( cstr, (None, 1) )
return (bytes().rjust(pad_left) + text[spos:epos] +
bytes().rjust(pad_right), attrtr, cstr)
def rle_get_at( rle, pos ):
"""
Return the attribute at offset pos.
"""
x = 0
if pos < 0:
return None
for a, run in rle:
if x+run > pos:
return a
x += run
return None
def rle_subseg( rle, start, end ):
"""Return a sub segment of an rle list."""
l = []
x = 0
for a, run in rle:
if start:
if start >= run:
start -= run
x += run
continue
x += start
run -= start
start = 0
if x >= end:
break
if x+run > end:
run = end-x
x += run
l.append( (a, run) )
return l
def rle_len( rle ):
"""
Return the number of characters covered by a run length
encoded attribute list.
"""
run = 0
for v in rle:
assert type(v) == tuple, repr(rle)
a, r = v
run += r
return run
def rle_append_beginning_modify(rle, a_r):
"""
Append (a, r) (unpacked from *a_r*) to BEGINNING of rle.
Merge with first run when possible
MODIFIES rle parameter contents. Returns None.
"""
a, r = a_r
if not rle:
rle[:] = [(a, r)]
else:
al, run = rle[0]
if a == al:
rle[0] = (a,run+r)
else:
rle[0:0] = [(al, r)]
def rle_append_modify(rle, a_r):
"""
Append (a, r) (unpacked from *a_r*) to the rle list rle.
Merge with last run when possible.
MODIFIES rle parameter contents. Returns None.
"""
a, r = a_r
if not rle or rle[-1][0] != a:
rle.append( (a,r) )
return
la,lr = rle[-1]
rle[-1] = (a, lr+r)
def rle_join_modify( rle, rle2 ):
"""
Append attribute list rle2 to rle.
Merge last run of rle with first run of rle2 when possible.
MODIFIES attr parameter contents. Returns None.
"""
if not rle2:
return
rle_append_modify(rle, rle2[0])
rle += rle2[1:]
def rle_product( rle1, rle2 ):
"""
Merge the runs of rle1 and rle2 like this:
eg.
rle1 = [ ("a", 10), ("b", 5) ]
rle2 = [ ("Q", 5), ("P", 10) ]
rle_product: [ (("a","Q"), 5), (("a","P"), 5), (("b","P"), 5) ]
rle1 and rle2 are assumed to cover the same total run.
"""
i1 = i2 = 1 # rle1, rle2 indexes
if not rle1 or not rle2: return []
a1, r1 = rle1[0]
a2, r2 = rle2[0]
l = []
while r1 and r2:
r = min(r1, r2)
rle_append_modify( l, ((a1,a2),r) )
r1 -= r
if r1 == 0 and i1< len(rle1):
a1, r1 = rle1[i1]
i1 += 1
r2 -= r
if r2 == 0 and i2< len(rle2):
a2, r2 = rle2[i2]
i2 += 1
return l
def rle_factor( rle ):
"""
Inverse of rle_product.
"""
rle1 = []
rle2 = []
for (a1, a2), r in rle:
rle_append_modify( rle1, (a1, r) )
rle_append_modify( rle2, (a2, r) )
return rle1, rle2
class TagMarkupException(Exception): pass
def decompose_tagmarkup(tm):
"""Return (text string, attribute list) for tagmarkup passed."""
tl, al = _tagmarkup_recurse(tm, None)
# join as unicode or bytes based on type of first element
text = tl[0][:0].join(tl)
if al and al[-1][0] is None:
del al[-1]
return text, al
def _tagmarkup_recurse( tm, attr ):
"""Return (text list, attribute list) for tagmarkup passed.
tm -- tagmarkup
attr -- current attribute or None"""
if type(tm) == list:
# for lists recurse to process each subelement
rtl = []
ral = []
for element in tm:
tl, al = _tagmarkup_recurse( element, attr )
if ral:
# merge attributes when possible
last_attr, last_run = ral[-1]
top_attr, top_run = al[0]
if last_attr == top_attr:
ral[-1] = (top_attr, last_run + top_run)
del al[-1]
rtl += tl
ral += al
return rtl, ral
if type(tm) == tuple:
# tuples mark a new attribute boundary
if len(tm) != 2:
raise TagMarkupException("Tuples must be in the form (attribute, tagmarkup): %r" % (tm,))
attr, element = tm
return _tagmarkup_recurse( element, attr )
if not isinstance(tm,(str, bytes)):
raise TagMarkupException("Invalid markup element: %r" % tm)
# text
return [tm], [(attr, len(tm))]
def is_mouse_event( ev ):
return type(ev) == tuple and len(ev)==4 and ev[0].find("mouse")>=0
def is_mouse_press( ev ):
return ev.find("press")>=0
class MetaSuper(type):
"""adding .__super"""
def __init__(cls, name, bases, d):
super(MetaSuper, cls).__init__(name, bases, d)
if hasattr(cls, "_%s__super" % name):
raise AttributeError("Class has same name as one of its super classes")
setattr(cls, "_%s__super" % name, super(cls))
def int_scale(val, val_range, out_range):
"""
Scale val in the range [0, val_range-1] to an integer in the range
[0, out_range-1]. This implementation uses the "round-half-up" rounding
method.
>>> "%x" % int_scale(0x7, 0x10, 0x10000)
'7777'
>>> "%x" % int_scale(0x5f, 0x100, 0x10)
'6'
>>> int_scale(2, 6, 101)
40
>>> int_scale(1, 3, 4)
2
"""
num = int(val * (out_range-1) * 2 + (val_range-1))
dem = ((val_range-1) * 2)
# if num % dem == 0 then we are exactly half-way and have rounded up.
return num // dem
class StoppingContext(object):
"""Context manager that calls ``stop`` on a given object on exit. Used to
make the ``start`` method on `MainLoop` and `BaseScreen` optionally act as
context managers.
"""
def __init__(self, wrapped):
self._wrapped = wrapped
def __enter__(self):
return self
def __exit__(self, *exc_info):
self._wrapped.stop()
|