summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorian <none@none>2006-04-14 21:40:16 +0000
committerian <none@none>2006-04-14 21:40:16 +0000
commit350d3ec226ce4473f39f0b11bf6d85953d5493ee (patch)
tree4f14634695e4e7fbef4d78a3d48d119bb8d2e6c9
parentb088d64873152e39389694922f7e281a2d739e00 (diff)
downloadurwid-350d3ec226ce4473f39f0b11bf6d85953d5493ee.tar.gz
release 0.9.3release-0.9.3
--HG-- extra : convert_revision : 4422098fbd87b6ab1dd4f4a3f9dfec2ac5f87f10
-rwxr-xr-xinput_test.py12
-rw-r--r--reference.html4
-rw-r--r--setup.py2
-rwxr-xr-xtest_urwid.py42
-rwxr-xr-xurwid/curses_display.py58
-rw-r--r--urwid/escape.py25
-rw-r--r--urwid/raw_display.py111
-rw-r--r--urwid/util.py26
-rwxr-xr-xurwid/web_display.py15
-rwxr-xr-xurwid/widget.py15
10 files changed, 252 insertions, 58 deletions
diff --git a/input_test.py b/input_test.py
index 1a77da6..dcd11d3 100755
--- a/input_test.py
+++ b/input_test.py
@@ -69,7 +69,7 @@ class KeyTest:
if keys:
self.ui.draw_screen((cols,rows),
self.top.render((cols,rows),focus=True))
- keys = self.ui.get_input()
+ keys, raw = self.ui.get_input(raw_keys=True)
if 'window resize' in keys:
cols, rows = self.ui.get_cols_rows()
if not keys:
@@ -86,13 +86,19 @@ class KeyTest:
else:
t += ["'",('key',k),"' "]
- self.l.append(urwid.Text(t))
+ rawt = urwid.Text(", ".join(["%d"%r for r in raw]))
+
+ self.l.append(
+ urwid.Columns([
+ ('weight',2,urwid.Text(t)),
+ rawt])
+ )
self.listbox.set_focus(len(self.l)-1,'above')
def main():
- urwid.web_display.set_preferences('KeyTest')
+ urwid.web_display.set_preferences('Input Test')
if urwid.web_display.handle_short_request():
return
KeyTest().main()
diff --git a/reference.html b/reference.html
index a624658..224d09e 100644
--- a/reference.html
+++ b/reference.html
@@ -1279,7 +1279,7 @@ function&nbsp;for&nbsp;the&nbsp;preferences&nbsp;to&nbsp;take&nbsp;effect</tt></
<dl><dt><a name="Screen-get_cols_rows"><strong>get_cols_rows</strong></a>(self)</dt><dd><tt>Return&nbsp;the&nbsp;screen&nbsp;size.</tt></dd></dl>
-<dl><dt><a name="Screen-get_input"><strong>get_input</strong></a>(self)</dt><dd><tt>Return&nbsp;pending&nbsp;input&nbsp;as&nbsp;a&nbsp;list.</tt></dd></dl>
+<dl><dt><a name="Screen-get_input"><strong>get_input</strong></a>(self, raw_keys<font color="#909090">=False</font>)</dt><dd><tt>Return&nbsp;pending&nbsp;input&nbsp;as&nbsp;a&nbsp;list.</tt></dd></dl>
<dl><dt><a name="Screen-register_palette"><strong>register_palette</strong></a>(self, l)</dt><dd><tt>Register&nbsp;a&nbsp;list&nbsp;of&nbsp;palette&nbsp;entries.<br>
&nbsp;<br>
@@ -1305,6 +1305,8 @@ unique&nbsp;id&nbsp;and&nbsp;handles&nbsp;cleanup&nbsp;when&nbsp;fn&nbsp;exits.<
web_display.set_preferences(..)&nbsp;must&nbsp;be&nbsp;called&nbsp;before&nbsp;calling<br>
this&nbsp;function&nbsp;for&nbsp;the&nbsp;preferences&nbsp;to&nbsp;take&nbsp;effect</tt></dd></dl>
+<dl><dt><a name="Screen-set_mouse_tracking"><strong>set_mouse_tracking</strong></a>(self)</dt><dd><tt>Not&nbsp;yet&nbsp;implemented</tt></dd></dl>
+
</body>
</html>
diff --git a/setup.py b/setup.py
index 1449625..a68786f 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ from distutils.core import setup
import os
-release = "0.9.2"
+release = "0.9.3"
setup_d = {
'name':"urwid",
diff --git a/test_urwid.py b/test_urwid.py
index b915e3f..566c41f 100755
--- a/test_urwid.py
+++ b/test_urwid.py
@@ -20,6 +20,7 @@
#
# Urwid web site: http://excess.org/urwid/
+from __future__ import nested_scopes
import urwid
@@ -324,23 +325,52 @@ class CalcTranslateWordTest3(CalcTranslateTest):
[(3, None), (4, 0, 6), (0, 6)],
[(2, None), (6, 7, 16), (0, 16)]]
-
+class CalcTranslateWordTest4(CalcTranslateTest):
+ text = ' Die Gedank'
+ width = 3
+ mode = 'space'
+ result_left = [
+ [(0, 0)],
+ [(3, 1, 4), (0, 4)],
+ [(3, 5, 8)],
+ [(3, 8, 11), (0, 11)]]
+ result_right = [
+ [(3, None), (0, 0)],
+ [(3, 1, 4), (0, 4)],
+ [(3, 5, 8)],
+ [(3, 8, 11), (0, 11)]]
+ result_center = [
+ [(2, None), (0, 0)],
+ [(3, 1, 4), (0, 4)],
+ [(3, 5, 8)],
+ [(3, 8, 11), (0, 11)]]
+
+class CalcTranslateWordTest5(CalcTranslateTest):
+ text = ' Word.'
+ width = 3
+ mode = 'space'
+ result_left = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]]
+ result_right = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]]
+ result_center = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]]
class CalcTranslateClipTest(CalcTranslateTest):
- text = "It's out of control!\nYou've got to\nturn it off!!!"
+ text = "It's out of control!\nYou've got to\n\nturn it off!!!"
mode = 'clip'
width = 14
result_left = [
[(20, 0, 20), (0, 20)],
[(13, 21, 34), (0, 34)],
- [(14, 35, 49), (0, 49)]]
+ [(0, 35)],
+ [(14, 36, 50), (0, 50)]]
result_right = [
[(-6, None), (20, 0, 20), (0, 20)],
[(1, None), (13, 21, 34), (0, 34)],
- [(14, 35, 49), (0, 49)]]
+ [(14, None), (0, 35)],
+ [(14, 36, 50), (0, 50)]]
result_center = [
[(-3, None), (20, 0, 20), (0, 20)],
[(1, None), (13, 21, 34), (0, 34)],
- [(14, 35, 49), (0, 49)]]
+ [(7, None), (0, 35)],
+ [(14, 36, 50), (0, 50)]]
@@ -1762,6 +1792,8 @@ def test_main():
CalcTranslateWordTest,
CalcTranslateWordTest2,
CalcTranslateWordTest3,
+ CalcTranslateWordTest4,
+ CalcTranslateWordTest5,
CalcTranslateClipTest,
CalcPosTest,
Pos2CoordsTest,
diff --git a/urwid/curses_display.py b/urwid/curses_display.py
index 004fda3..17f0422 100755
--- a/urwid/curses_display.py
+++ b/urwid/curses_display.py
@@ -23,6 +23,8 @@
Curses-based UI implementation
"""
+from __future__ import nested_scopes
+
import curses
import sys
@@ -70,6 +72,7 @@ class Screen:
self._keyqueue = []
self.prev_input_resize = 0
self.set_input_timeouts()
+ self.last_bstate = 0
def register_palette( self, l ):
"""Register a list of palette entries.
@@ -389,26 +392,47 @@ class Screen:
def _encode_mouse_event(self):
- # convert back to escape sequence
+ # convert to escape sequence
+ last = next = self.last_bstate
(id,x,y,z,bstate) = curses.getmouse()
- if bstate & curses.BUTTON1_PRESSED: b = 0
- elif bstate & curses.BUTTON2_PRESSED: b = 1
- elif bstate & curses.BUTTON3_PRESSED: b = 2
- elif bstate & curses.BUTTON4_PRESSED: b = 64
- else:
- #assert 0, `bstate, bstate&curses.BUTTON1_RELEASED, \
- # bstate&curses.BUTTON2_RELEASED, \
- # bstate&curses.BUTTON3_RELEASED, \
- # bstate&curses.BUTTON4_RELEASED`
- #x = bstate&curses.BUTTON1_RELEASED
- #y = bstate&curses.BUTTON2_RELEASED
- b = 3
- if bstate & curses.BUTTON_SHIFT: b |= 4
- if bstate & curses.BUTTON_ALT: b |= 8
- if bstate & curses.BUTTON_CTRL: b |= 16
+ mod = 0
+ if bstate & curses.BUTTON_SHIFT: mod |= 4
+ if bstate & curses.BUTTON_ALT: mod |= 8
+ if bstate & curses.BUTTON_CTRL: mod |= 16
+
+ l = []
+ def append_button( b ):
+ b |= mod
+ l.extend([ 27, ord('['), ord('M'), b+32, x+33, y+33 ])
+
+ if bstate & curses.BUTTON1_PRESSED and last & 1 == 0:
+ append_button( 0 )
+ next |= 1
+ if bstate & curses.BUTTON2_PRESSED and last & 2 == 0:
+ append_button( 1 )
+ next |= 2
+ if bstate & curses.BUTTON3_PRESSED and last & 4 == 0:
+ append_button( 2 )
+ next |= 4
+ if bstate & curses.BUTTON4_PRESSED and last & 8 == 0:
+ append_button( 64 )
+ next |= 8
+ if bstate & curses.BUTTON1_RELEASED and last & 1:
+ append_button( 0 + escape.MOUSE_RELEASE_FLAG )
+ next &= ~ 1
+ if bstate & curses.BUTTON2_RELEASED and last & 2:
+ append_button( 1 + escape.MOUSE_RELEASE_FLAG )
+ next &= ~ 2
+ if bstate & curses.BUTTON3_RELEASED and last & 4:
+ append_button( 2 + escape.MOUSE_RELEASE_FLAG )
+ next &= ~ 4
+ if bstate & curses.BUTTON4_RELEASED and last & 8:
+ append_button( 64 + escape.MOUSE_RELEASE_FLAG )
+ next &= ~ 8
- return [ 27, ord('['), ord('M'), b + 32, x + 33, y + 33 ]
+ self.last_bstate = next
+ return l
def _dbg_instr(self): # messy input string (intended for debugging)
diff --git a/urwid/escape.py b/urwid/escape.py
index daecebe..1b25fd0 100644
--- a/urwid/escape.py
+++ b/urwid/escape.py
@@ -133,16 +133,29 @@ class KeyqueueTrie:
if b & 8: prefix = prefix + "meta "
if b & 16: prefix = prefix + "ctrl "
+ # 0->1, 1->2, 2->3, 64->4, 65->5
+ button = ((b&64)/64*3) + (b & 3) + 1
+
if b & 3 == 3:
action = "release"
- b = 0
+ button = 0
+ elif b & MOUSE_RELEASE_FLAG:
+ action = "release"
+ elif b & MOUSE_DRAG_FLAG:
+ action = "drag"
else:
action = "press"
- b = ((b&64)/64*3) + (b & 3) + 1
- return ( (prefix + "mouse " + action, b, x, y), keys[3:] )
+ return ( (prefix + "mouse " + action, button, x, y), keys[3:] )
+
+
+# This is added to button value to signal mouse release by curses_display
+# and raw_display when we know which button was released. NON-STANDARD
+MOUSE_RELEASE_FLAG = 2048
+
+# xterm adds this to the button value to signal a mouse drag event
+MOUSE_DRAG_FLAG = 32
-
#################################################
# Build the input trie from input_sequences list
@@ -275,8 +288,8 @@ def set_cursor_position( x, y ):
HIDE_CURSOR = ESC+"[?25l"
SHOW_CURSOR = ESC+"[?25h"
-MOUSE_TRACKING_ON = ESC+"[?1000h"
-MOUSE_TRACKING_OFF = ESC+"[?1000l"
+MOUSE_TRACKING_ON = ESC+"[?1000h"+ESC+"[?1002h"
+MOUSE_TRACKING_OFF = ESC+"[?1002l"+ESC+"[?1000l"
_fg_attr = {
'default': "0;39",
diff --git a/urwid/raw_display.py b/urwid/raw_display.py
index bb3d367..1236e04 100644
--- a/urwid/raw_display.py
+++ b/urwid/raw_display.py
@@ -23,6 +23,8 @@
Direct terminal UI implementation
"""
+from __future__ import nested_scopes
+
import fcntl
import termios
import os
@@ -31,6 +33,7 @@ import struct
import sys
import tty
import signal
+import popen2
import util
import escape
@@ -54,6 +57,9 @@ class Screen:
self.screen_buf = None
self.resized = False
self.maxrow = None
+ self.gpm_mev = None
+ self.gpm_event_pending = False
+ self.last_bstate = 0
def register_palette( self, l ):
"""Register a list of palette entries.
@@ -153,7 +159,22 @@ class Screen:
click events along with keystrokes.
"""
sys.stdout.write(escape.MOUSE_TRACKING_ON)
-
+
+ self._start_gpm_tracking()
+
+ def _start_gpm_tracking(self):
+ if not os.path.isfile("/usr/bin/mev"):
+ return
+ if not os.environ.get('TERM',"").lower().startswith("linux"):
+ return
+ m = popen2.Popen3("/usr/bin/mev -e 158")
+ self.gpm_mev = m
+
+ def _stop_gpm_tracking(self):
+ os.kill(self.gpm_mev.pid, signal.SIGINT)
+ os.waitpid(self.gpm_mev.pid, 0)
+ self.gpm_mev = None
+
def run_wrapper(self,fn):
""" Call fn and reset terminal on exit.
"""
@@ -166,6 +187,8 @@ class Screen:
self.signal_restore()
termios.tcsetattr(0, termios.TCSADRAIN, old_settings)
move_cursor = ""
+ if self.gpm_mev:
+ self._stop_gpm_tracking()
if self.maxrow is not None:
move_cursor = escape.set_cursor_position(
0, self.maxrow)
@@ -239,13 +262,13 @@ class Screen:
raw = []
keys = []
- while key >= 0:
- raw.append(key)
- #if key==WINDOW_RESIZE:
- # resize = True
- # key = self._getch_nodelay()
- # continue
- keys.append(key)
+ while key >= 0 or self.gpm_event_pending:
+ if self.gpm_event_pending:
+ keys += self._encode_gpm_event()
+
+ if key >= 0:
+ raw.append(key)
+ keys.append(key)
key = self._getch_nodelay()
processed = []
@@ -273,19 +296,87 @@ class Screen:
def _getch(self, timeout):
ready = None
+ fd_list = [0]
+ if self.gpm_mev is not None:
+ fd_list += [ self.gpm_mev.fromchild ]
while True:
try:
- ready,w,err = select.select([0],[],[0], timeout)
+ ready,w,err = select.select(
+ fd_list,[],fd_list, timeout)
break
except select.error, e:
if e.args[0] != 4:
raise
if self.resized:
+ ready = []
break
- if ready:
+ if self.gpm_mev is not None:
+ if self.gpm_mev.fromchild in ready:
+ self.gpm_event_pending = True
+ if 0 in ready:
return ord(os.read(0, 1))
return -1
+ def _encode_gpm_event( self ):
+ self.gpm_event_pending = False
+ s = self.gpm_mev.fromchild.readline()
+ l = s.split(",")
+ if len(l) != 6:
+ # unexpected output, stop tracking
+ self._stop_gpm_tracking()
+ return []
+ ev, x, y, ign, b, m = s.split(",")
+ ev = int( ev.split("x")[-1], 16)
+ x = int( x.split(" ")[-1] )
+ y = int( y.lstrip().split(" ")[0] )
+ b = int( b.split(" ")[-1] )
+ m = int( m.split("x")[-1].rstrip(), 16 )
+
+ # convert to xterm-like escape sequence
+
+ last = next = self.last_bstate
+ l = []
+
+ mod = 0
+ if m & 1: mod |= 4 # shift
+ if m & 10: mod |= 8 # alt
+ if m & 4: mod |= 16 # ctrl
+
+ def append_button( b ):
+ b |= mod
+ l.extend([ 27, ord('['), ord('M'), b+32, x+32, y+32 ])
+
+ if ev == 20: # press
+ if b & 4 and last & 1 == 0:
+ append_button( 0 )
+ next |= 1
+ if b & 2 and last & 2 == 0:
+ append_button( 1 )
+ next |= 2
+ if b & 1 and last & 4 == 0:
+ append_button( 2 )
+ next |= 4
+ elif ev == 146: # drag
+ if b & 4:
+ append_button( 0 + escape.MOUSE_DRAG_FLAG )
+ elif b & 2:
+ append_button( 1 + escape.MOUSE_DRAG_FLAG )
+ elif b & 1:
+ append_button( 2 + escape.MOUSE_DRAG_FLAG )
+ else: # release
+ if b & 4 and last & 1:
+ append_button( 0 + escape.MOUSE_RELEASE_FLAG )
+ next &= ~ 1
+ if b & 2 and last & 2:
+ append_button( 1 + escape.MOUSE_RELEASE_FLAG )
+ next &= ~ 2
+ if b & 1 and last & 4:
+ append_button( 2 + escape.MOUSE_RELEASE_FLAG )
+ next &= ~ 4
+
+ self.last_bstate = next
+ return l
+
def _getch_nodelay(self):
return self._getch(0)
diff --git a/urwid/util.py b/urwid/util.py
index 387ca77..9be265e 100644
--- a/urwid/util.py
+++ b/urwid/util.py
@@ -184,9 +184,10 @@ class StandardTextLayout(TextLayout):
if n_cr == -1:
n_cr = len(text)
sc = calc_width(text, p, n_cr)
- b.append([(sc,p,n_cr),
- # removed character hint
- (0,n_cr)])
+ l = [(0,n_cr)]
+ if p!=n_cr:
+ l = [(sc, p, n_cr)] + l
+ b.append(l)
p = n_cr+1
return b
@@ -234,9 +235,10 @@ class StandardTextLayout(TextLayout):
prev = move_prev_char(text, p, prev)
if text[prev] == " ":
sc = calc_width(text,p,prev)
- b.append([(sc,p,prev),
- # removed character hint
- (0,prev)])
+ l = [(0,prev)]
+ if p!=prev:
+ l = [(sc,p,prev)] + l
+ b.append(l)
p = prev+1
break
if is_wide_char(text,prev):
@@ -249,10 +251,16 @@ class StandardTextLayout(TextLayout):
else:
# unwrap previous line space if possible to
# fit more text (we're breaking a word anyway)
- if b and len(b[-1]) == 2:
+ if b and (len(b[-1]) == 2 or ( len(b[-1])==1
+ and len(b[-1][0])==2 )):
# look for removed space above
- [(p_sc, p_off, p_end),
- (h_sc, h_off)] = b[-1]
+ if len(b[-1]) == 1:
+ [(h_sc, h_off)] = b[-1]
+ p_sc = 0
+ p_off = p_end = h_off
+ else:
+ [(p_sc, p_off, p_end),
+ (h_sc, h_off)] = b[-1]
if (p_sc < width and h_sc==0 and
text[h_off] == " "):
# combine with previous line
diff --git a/urwid/web_display.py b/urwid/web_display.py
index 32b265b..522c713 100755
--- a/urwid/web_display.py
+++ b/urwid/web_display.py
@@ -613,6 +613,10 @@ class Screen:
background = "light gray"
self.palette[name] = (foreground, background, mono)
+ def set_mouse_tracking(self):
+ """Not yet implemented"""
+ pass
+
def run_wrapper(self,fn):
"""Start the application main loop.
@@ -840,7 +844,7 @@ class Screen:
"""Return the screen size."""
return self.screen_size
- def get_input(self):
+ def get_input(self, raw_keys=False):
"""Return pending input as a list."""
l = []
resized = False
@@ -850,10 +854,15 @@ class Screen:
[self.input_fd],[],[],0.5)
except select.error, e:
# return on interruptions
- if e.args[0] == 4: return []
+ if e.args[0] == 4:
+ if raw_keys:
+ return [],[]
+ return []
raise
if not iready:
+ if raw_keys:
+ return [],[]
return []
keydata = os.read(self.input_fd, MAX_READ)
@@ -877,6 +886,8 @@ class Screen:
if resized:
l.append("window resize")
+ if raw_keys:
+ return l, []
return l
diff --git a/urwid/widget.py b/urwid/widget.py
index 6d7972e..8161ade 100755
--- a/urwid/widget.py
+++ b/urwid/widget.py
@@ -1516,8 +1516,12 @@ class Frame(BoxWidget):
"""Pass keypress to widget in focus."""
if self.focus_part == 'header' and self.header is not None:
- return self.header.keypress((maxcol,),key)
+ if not self.header.selectable():
+ return key
+ return self.header.keypress((maxcol,),key)
if self.focus_part == 'footer' and self.footer is not None:
+ if not self.footer.selectable():
+ return key
return self.footer.keypress((maxcol,),key)
if self.focus_part != 'body':
return key
@@ -1528,6 +1532,8 @@ class Frame(BoxWidget):
remaining -= self.footer.rows((maxcol,))
if remaining <= 0: return key
+ if not self.body.selectable():
+ return key
return self.body.keypress( (maxcol, remaining), key )
@@ -1660,9 +1666,10 @@ class Pile(FlowWidget):
"""Pass the keypress to the widget in focus.
Unhandled 'up' and 'down' keys may cause a focus change."""
- key = self.focus_item.keypress( (maxcol,), key )
- if key not in ('up', 'down'):
- return key
+ if self.focus_item.selectable():
+ key = self.focus_item.keypress( (maxcol,), key )
+ if key not in ('up', 'down'):
+ return key
i = self.widget_list.index(self.focus_item)
if key == 'up':