diff options
Diffstat (limited to 'Lib/stdwin')
-rwxr-xr-x | Lib/stdwin/anywin.py | 14 | ||||
-rwxr-xr-x | Lib/stdwin/dirwin.py | 28 | ||||
-rwxr-xr-x | Lib/stdwin/filewin.py | 31 | ||||
-rwxr-xr-x | Lib/stdwin/gwin.py | 118 | ||||
-rwxr-xr-x | Lib/stdwin/listwin.py | 47 | ||||
-rwxr-xr-x | Lib/stdwin/rect.py | 87 | ||||
-rwxr-xr-x | Lib/stdwin/stdwinevents.py | 36 | ||||
-rwxr-xr-x | Lib/stdwin/tablewin.py | 237 | ||||
-rwxr-xr-x | Lib/stdwin/textwin.py | 119 |
9 files changed, 717 insertions, 0 deletions
diff --git a/Lib/stdwin/anywin.py b/Lib/stdwin/anywin.py new file mode 100755 index 0000000..bb7e865 --- /dev/null +++ b/Lib/stdwin/anywin.py @@ -0,0 +1,14 @@ +# Module 'anywin' +# Open a file or directory in a window + +import dirwin +import filewin +import path + +def open(name): + print 'opening', name, '...' + if path.isdir(name): + w = dirwin.open(name) + else: + w = filewin.open(name) + return w diff --git a/Lib/stdwin/dirwin.py b/Lib/stdwin/dirwin.py new file mode 100755 index 0000000..5df85e7 --- /dev/null +++ b/Lib/stdwin/dirwin.py @@ -0,0 +1,28 @@ +# Module 'dirwin' + +# Directory windows, a subclass of listwin + +import gwin +import listwin +import anywin +import path +import dircache + +def action(w, string, i, detail): + (h, v), clicks, button, mask = detail + if clicks = 2: + name = path.cat(w.name, string) + try: + w = anywin.open(name) + except posix.error, why: + stdwin.message('Can\'t open ' + name + ': ' + why[1]) + +def open(name): + name = path.cat(name, '') + list = dircache.opendir(name)[:] + list.sort() + dircache.annotate(name, list) + w = listwin.open(name, list) + w.name = name + w.action = action + return w diff --git a/Lib/stdwin/filewin.py b/Lib/stdwin/filewin.py new file mode 100755 index 0000000..1beb0b6 --- /dev/null +++ b/Lib/stdwin/filewin.py @@ -0,0 +1,31 @@ +# Module 'filewin' +# File windows, a subclass of textwin (which is a subclass of gwin) + +import stdwin +import textwin +import path + +builtin_open = open + +def readfile(fn): # Return a string containing the file's contents + fp = builtin_open(fn, 'r') + a = '' + n = 8096 + while 1: + b = fp.read(n) + if not b: break + a = a + b + return a + + +# FILE WINDOW + +def open_readonly(fn): # Open a file window + w = textwin.open_readonly(fn, readfile(fn)) + w.fn = fn + return w + +def open(fn): # Open a file window + w = textwin.open(fn, readfile(fn)) + w.fn = fn + return w diff --git a/Lib/stdwin/gwin.py b/Lib/stdwin/gwin.py new file mode 100755 index 0000000..15aa432 --- /dev/null +++ b/Lib/stdwin/gwin.py @@ -0,0 +1,118 @@ +# Module 'gwin' +# Generic stdwin windows + +# This is used as a base class from which to derive other window types. +# The mainloop() function here is an event dispatcher for all window types. + +import stdwin +import stdwinsupport + +S = stdwinsupport # Shorthand + +windows = [] # List of open windows + + +# Open a window + +def open(title): # Open a generic window + w = stdwin.open(title) + stdwin.setdefwinsize(0, 0) + # Set default event handlers + w.draw = nop + w.char = nop + w.mdown = nop + w.mmove = nop + w.mup = nop + w.m2down = m2down + w.m2up = m2up + w.size = nop + w.move = nop + w.activate = w.deactivate = nop + w.timer = nop + # default command handlers + w.close = close + w.tab = tab + w.enter = enter + w.backspace = backspace + w.arrow = arrow + w.kleft = w.kup = w.kright = w.kdown = nop + windows.append(w) + return w + + +# Generic event dispatching + +def mainloop(): # Handle events until no windows left + while windows: + treatevent(stdwin.getevent()) + +def treatevent(e): # Handle a stdwin event + type, w, detail = e + if type = S.we_draw: + w.draw(w, detail) + elif type = S.we_menu: + m, item = detail + m.action[item](w, m, item) + elif type = S.we_command: + treatcommand(w, detail) + elif type = S.we_char: + w.char(w, detail) + elif type = S.we_mouse_down: + if detail[1] > 1: w.m2down(w, detail) + else: w.mdown(w, detail) + elif type = S.we_mouse_move: + w.mmove(w, detail) + elif type = S.we_mouse_up: + if detail[1] > 1: w.m2up(w, detail) + else: w.mup(w, detail) + elif type = S.we_size: + w.size(w, w.getwinsize()) + elif type = S.we_activate: + w.activate(w) + elif type = S.we_deactivate: + w.deactivate(w) + elif type = S.we_move: + w.move(w) + elif type = S.we_timer: + w.timer(w) + +def treatcommand(w, type): # Handle a we_command event + if type = S.wc_close: + w.close(w) + elif type = S.wc_return: + w.enter(w) + elif type = S.wc_tab: + w.tab(w) + elif type = S.wc_backspace: + w.backspace(w) + elif type in (S.wc_left, S.wc_up, S.wc_right, S.wc_down): + w.arrow(w, type) + + +# Methods + +def close(w): # Close method + for i in range(len(windows)): + if windows[i] is w: + del windows[i] + break + +def arrow(w, detail): # Arrow key method + if detail = S.wc_left: + w.kleft(w) + elif detail = S.wc_up: + w.kup(w) + elif detail = S.wc_right: + w.kright(w) + elif detail = S.wc_down: + w.kdown(w) + + +# Trivial methods + +def tab(w): w.char(w, '\t') +def enter(w): w.char(w, '\n') # 'return' is a Python reserved word +def backspace(w): w.char(w, '\b') +def m2down(w, detail): w.mdown(w, detail) +def m2up(w, detail): w.mup(w, detail) +def nop(args): pass diff --git a/Lib/stdwin/listwin.py b/Lib/stdwin/listwin.py new file mode 100755 index 0000000..9480a81 --- /dev/null +++ b/Lib/stdwin/listwin.py @@ -0,0 +1,47 @@ +# Module 'listwin' +# List windows, a subclass of gwin + +import gwin +import stdwin + +def maxlinewidth(a): # Compute maximum textwidth of lines in a sequence + max = 0 + for line in a: + width = stdwin.textwidth(line) + if width > max: max = width + return max + +def action(w, string, i, detail): # Default item selection method + pass + +def mup(w, detail): # Mouse up method + (h, v), clicks, button, mask = detail + i = divmod(v, w.lineheight)[0] + if 0 <= i < len(w.data): + w.action(w, w.data[i], i, detail) + +def draw(w, ((left, top), (right, bottom))): # Text window draw method + data = w.data + d = w.begindrawing() + lh = w.lineheight + itop = top/lh + ibot = (bottom-1)/lh + 1 + if itop < 0: itop = 0 + if ibot > len(data): ibot = len(data) + for i in range(itop, ibot): d.text((0, i*lh), data[i]) + +def open(title, data): # Display a list of texts in a window + lineheight = stdwin.lineheight() + h, v = maxlinewidth(data), len(data)*lineheight + h0, v0 = h + stdwin.textwidth(' '), v + lineheight + if h0 > stdwin.textwidth(' ')*80: h0 = 0 + if v0 > stdwin.lineheight()*24: v0 = 0 + stdwin.setdefwinsize(h0, v0) + w = gwin.open(title) + w.setdocsize(h, v) + w.lineheight = lineheight + w.data = data + w.draw = draw + w.action = action + w.mup = mup + return w diff --git a/Lib/stdwin/rect.py b/Lib/stdwin/rect.py new file mode 100755 index 0000000..c044b9f --- /dev/null +++ b/Lib/stdwin/rect.py @@ -0,0 +1,87 @@ +# Module 'rect'. +# +# Operations on rectangles. +# There is some normalization: all results return the object 'empty' +# if their result would contain no points. + + +# Exception. +# +error = 'rect.error' + + +# The empty rectangle. +# +empty = (0, 0), (0, 0) + + +# Check if a rectangle is empty. +# +def is_empty((left, top), (right, bottom)): + return left >= right or top >= bottom + + +# Compute the intersection or two or more rectangles. +# This works with a list or tuple argument. +# +def intersect(list): + if not list: raise error, 'intersect called with empty list' + if is_empty(list[0]): return empty + (left, top), (right, bottom) = list[0] + for rect in list[1:]: + if not is_empty(rect): + (l, t), (r, b) = rect + if left < l: left = l + if top < t: top = t + if right > r: right = r + if bottom > b: bottom = b + if is_empty((left, top), (right, bottom)): + return empty + return (left, top), (right, bottom) + + +# Compute the smallest rectangle containing all given rectangles. +# This works with a list or tuple argument. +# +def union(list): + (left, top), (right, bottom) = empty + for (l, t), (r, b) in list[1:]: + if not is_empty((l, t), (r, b)): + if l < left: left = l + if t < top: top = t + if r > right: right = r + if b > bottom: bottom = b + res = (left, top), (right, bottom) + if is_empty(res): + return empty + return res + + +# Check if a point is in a rectangle. +# +def pointinrect((h, v), ((left, top), (right, bottom))): + return left <= h < right and top <= v < bottom + + +# Return a rectangle that is dh, dv inside another +# +def inset(((left, top), (right, bottom)), (dh, dv)): + left = left + dh + top = top + dv + right = right - dh + bottom = bottom - dv + r = (left, top), (right, bottom) + if is_empty(r): + return empty + else: + return r + + +# Conversions between rectangles and 'geometry tuples', +# given as origin (h, v) and dimensions (width, height). +# +def rect2geom((left, top), (right, bottom)): + return (left, top), (right-left, bottom-top) + +def geom2rect((h, v), (width, height)): + return (h, v), (h+width, v+height) diff --git a/Lib/stdwin/stdwinevents.py b/Lib/stdwin/stdwinevents.py new file mode 100755 index 0000000..889dd95 --- /dev/null +++ b/Lib/stdwin/stdwinevents.py @@ -0,0 +1,36 @@ +# Module 'stdwinevents' -- Constants for stdwin event types +# +# Suggested usage: +# from stdwinevents import * + +# The function stdwin.getevent() returns a tuple containing: +# (type, window, detail) +# where detail may be <no value> or a value depending on type, see below: + +# Values for type: + +WE_NULL = 0 # not reported -- means 'no event' internally +WE_ACTIVATE = 1 # detail is <no object> +WE_CHAR = 2 # detail is the character +WE_COMMAND = 3 # detail is one of the WC_* constants below +WE_MOUSE_DOWN = 4 # detail is ((h, v), clicks, button, mask) +WE_MOUSE_MOVE = 5 # ditto +WE_MOUSE_UP = 6 # ditto +WE_MENU = 7 # detail is (menu, item) +WE_SIZE = 8 # detail is (width, height) [???] +WE_MOVE = 9 # not reported -- reserved for future use +WE_DRAW = 10 # detail is ((left, top), (right, bottom)) +WE_TIMER = 11 # detail is <no object> +WE_DEACTIVATE = 12 # detail is <no object> + +# Values for detail when type is WE_COMMAND: + +WC_CLOSE = 1 # user hit close box +WC_LEFT = 2 # left arrow key +WC_RIGHT = 3 # right arrow key +WC_UP = 4 # up arrow key +WC_DOWN = 5 # down arrow key +WC_CANCEL = 6 # not reported -- turned into KeyboardInterrupt +WC_BACKSPACE = 7 # backspace key +WC_TAB = 8 # tab key +WC_RETURN = 9 # return or enter key diff --git a/Lib/stdwin/tablewin.py b/Lib/stdwin/tablewin.py new file mode 100755 index 0000000..05a954e --- /dev/null +++ b/Lib/stdwin/tablewin.py @@ -0,0 +1,237 @@ +# Module 'tablewin' + +# Display a table, with per-item actions: + +# A1 | A2 | A3 | .... | AN +# B1 | B2 | B3 | .... | BN +# C1 | C2 | C3 | .... | CN +# .. | .. | .. | .... | .. +# Z1 | Z2 | Z3 | .... | ZN + +# Not all columns need to have the same length. +# The data structure is a list of columns; +# each column is a list of items. +# Each item is a pair of a string and an action procedure. +# The first item may be a column title. + +import stdwin +import gwin + +def open(title, data): # Public function to open a table window + # + # Set geometry parameters (one day, these may be changeable) + # + margin = stdwin.textwidth(' ') + lineheight = stdwin.lineheight() + # + # Geometry calculations + # + colstarts = [0] + totwidth = 0 + maxrows = 0 + for coldata in data: + # Height calculations + rows = len(coldata) + if rows > maxrows: maxrows = rows + # Width calculations + width = colwidth(coldata) + margin + totwidth = totwidth + width + colstarts.append(totwidth) + # + # Calculate document and window height + # + docwidth, docheight = totwidth, maxrows*lineheight + winwidth, winheight = docwidth, docheight + if winwidth > stdwin.textwidth('n')*100: winwidth = 0 + if winheight > stdwin.lineheight()*30: winheight = 0 + # + # Create the window + # + stdwin.setdefwinsize(winwidth, winheight) + w = gwin.open(title) + # + # Set properties and override methods + # + w.data = data + w.margin = margin + w.lineheight = lineheight + w.colstarts = colstarts + w.totwidth = totwidth + w.maxrows = maxrows + w.selection = (-1, -1) + w.lastselection = (-1, -1) + w.selshown = 0 + w.setdocsize(docwidth, docheight) + w.draw = draw + w.mup = mup + w.arrow = arrow + # + # Return + # + return w + +def update(w, data): # Change the data + # + # Hide selection + # + hidesel(w, w.begindrawing()) + # + # Get old geometry parameters + # + margin = w.margin + lineheight = w.lineheight + # + # Geometry calculations + # + colstarts = [0] + totwidth = 0 + maxrows = 0 + for coldata in data: + # Height calculations + rows = len(coldata) + if rows > maxrows: maxrows = rows + # Width calculations + width = colwidth(coldata) + margin + totwidth = totwidth + width + colstarts.append(totwidth) + # + # Calculate document and window height + # + docwidth, docheight = totwidth, maxrows*lineheight + # + # Set changed properties and change window size + # + w.data = data + w.colstarts = colstarts + w.totwidth = totwidth + w.maxrows = maxrows + w.change((0, 0), (10000, 10000)) + w.setdocsize(docwidth, docheight) + w.change((0, 0), (docwidth, docheight)) + # + # Show selection, or forget it if out of range + # + showsel(w, w.begindrawing()) + if not w.selshown: w.selection = (-1, -1) + +def colwidth(coldata): # Subroutine to calculate column width + maxwidth = 0 + for string, action in coldata: + width = stdwin.textwidth(string) + if width > maxwidth: maxwidth = width + return maxwidth + +def draw(w, ((left, top), (right, bottom))): # Draw method + ileft = whichcol(w, left) + iright = whichcol(w, right-1) + 1 + if iright > len(w.data): iright = len(w.data) + itop = divmod(top, w.lineheight)[0] + if itop < 0: itop = 0 + ibottom, remainder = divmod(bottom, w.lineheight) + if remainder: ibottom = ibottom + 1 + d = w.begindrawing() + if ileft <= w.selection[0] < iright: + if itop <= w.selection[1] < ibottom: + hidesel(w, d) + d.erase((left, top), (right, bottom)) + for i in range(ileft, iright): + col = w.data[i] + jbottom = len(col) + if ibottom < jbottom: jbottom = ibottom + h = w.colstarts[i] + v = itop * w.lineheight + for j in range(itop, jbottom): + string, action = col[j] + d.text((h, v), string) + v = v + w.lineheight + showsel(w, d) + +def mup(w, detail): # Mouse up method + (h, v), nclicks, button, mask = detail + icol = whichcol(w, h) + if 0 <= icol < len(w.data): + irow = divmod(v, w.lineheight)[0] + col = w.data[icol] + if 0 <= irow < len(col): + string, action = col[irow] + action(w, string, (icol, irow), detail) + +def whichcol(w, h): # Return column number (may be >= len(w.data)) + for icol in range(0, len(w.data)): + if h < w.colstarts[icol+1]: + return icol + return len(w.data) + +def arrow(w, type): + import stdwinsupport + S = stdwinsupport + if type = S.wc_left: + incr = -1, 0 + elif type = S.wc_up: + incr = 0, -1 + elif type = S.wc_right: + incr = 1, 0 + elif type = S.wc_down: + incr = 0, 1 + else: + return + icol, irow = w.lastselection + icol = icol + incr[0] + if icol < 0: icol = len(w.data)-1 + if icol >= len(w.data): icol = 0 + if 0 <= icol < len(w.data): + irow = irow + incr[1] + if irow < 0: irow = len(w.data[icol]) - 1 + if irow >= len(w.data[icol]): irow = 0 + else: + irow = 0 + if 0 <= icol < len(w.data) and 0 <= irow < len(w.data[icol]): + w.lastselection = icol, irow + string, action = w.data[icol][irow] + detail = (0, 0), 1, 1, 1 + action(w, string, (icol, irow), detail) + + +# Selection management +# TO DO: allow multiple selected entries + +def select(w, selection): # Public function to set the item selection + d = w.begindrawing() + hidesel(w, d) + w.selection = selection + showsel(w, d) + if w.selshown: lastselection = selection + +def hidesel(w, d): # Hide the selection, if shown + if w.selshown: invertsel(w, d) + +def showsel(w, d): # Show the selection, if hidden + if not w.selshown: invertsel(w, d) + +def invertsel(w, d): # Invert the selection, if valid + icol, irow = w.selection + if 0 <= icol < len(w.data) and 0 <= irow < len(w.data[icol]): + left = w.colstarts[icol] + right = w.colstarts[icol+1] + top = irow * w.lineheight + bottom = (irow+1) * w.lineheight + d.invert((left, top), (right, bottom)) + w.selshown = (not w.selshown) + + +# Demonstration + +def demo_action(w, string, (icol, irow), detail): # Action function for demo + select(w, (irow, icol)) + +def demo(): # Demonstration + da = demo_action # shorthand + col0 = [('a1', da), ('bbb1', da), ('c1', da)] + col1 = [('a2', da), ('bbb2', da)] + col2 = [('a3', da), ('b3', da), ('c3', da), ('d4', da), ('d5', da)] + col3 = [] + for i in range(1, 31): col3.append('xxx' + `i`, da) + data = [col0, col1, col2, col3] + w = open('tablewin.demo', data) + gwin.mainloop() + return w diff --git a/Lib/stdwin/textwin.py b/Lib/stdwin/textwin.py new file mode 100755 index 0000000..2631ca4 --- /dev/null +++ b/Lib/stdwin/textwin.py @@ -0,0 +1,119 @@ +# Module 'textwin' + +# Text windows, a subclass of gwin + +import stdwin +import stdwinsupport +import gwin + +S = stdwinsupport # Shorthand + + +def fixsize(w): + docwidth, docheight = w.text.getrect()[1] + winheight = w.getwinsize()[1] + if winheight > docheight: docheight = winheight + w.setdocsize(0, docheight) + fixeditmenu(w) + +def cut(w, m, id): + s = w.text.getfocustext() + if s: + stdwin.setcutbuffer(s) + w.text.replace('') + fixsize(w) + +def copy(w, m, id): + s = w.text.getfocustext() + if s: + stdwin.setcutbuffer(s) + fixeditmenu(w) + +def paste(w, m, id): + w.text.replace(stdwin.getcutbuffer()) + fixsize(w) + +def addeditmenu(w): + m = w.editmenu = w.menucreate('Edit') + m.action = [] + m.additem('Cut', 'X') + m.action.append(cut) + m.additem('Copy', 'C') + m.action.append(copy) + m.additem('Paste', 'V') + m.action.append(paste) + +def fixeditmenu(w): + m = w.editmenu + f = w.text.getfocus() + can_copy = (f[0] < f[1]) + m.enable(1, can_copy) + if not w.readonly: + m.enable(0, can_copy) + m.enable(2, (stdwin.getcutbuffer() <> '')) + +def draw(w, area): # Draw method + w.text.draw(area) + +def size(w, newsize): # Size method + w.text.move((0, 0), newsize) + fixsize(w) + +def close(w): # Close method + del w.text # Break circular ref + gwin.close(w) + +def char(w, c): # Char method + w.text.replace(c) + fixsize(w) + +def backspace(w): # Backspace method + void = w.text.event(S.we_command, w, S.wc_backspace) + fixsize(w) + +def arrow(w, detail): # Arrow method + w.text.arrow(detail) + fixeditmenu(w) + +def mdown(w, detail): # Mouse down method + void = w.text.event(S.we_mouse_down, w, detail) + fixeditmenu(w) + +def mmove(w, detail): # Mouse move method + void = w.text.event(S.we_mouse_move, w, detail) + +def mup(w, detail): # Mouse up method + void = w.text.event(S.we_mouse_up, w, detail) + fixeditmenu(w) + +def activate(w): # Activate method + fixeditmenu(w) + +def open(title, str): # Display a string in a window + w = gwin.open(title) + w.readonly = 0 + w.text = w.textcreate((0, 0), w.getwinsize()) + w.text.replace(str) + w.text.setfocus(0, 0) + addeditmenu(w) + fixsize(w) + w.draw = draw + w.size = size + w.close = close + w.mdown = mdown + w.mmove = mmove + w.mup = mup + w.char = char + w.backspace = backspace + w.arrow = arrow + w.activate = activate + return w + +def open_readonly(title, str): # Same with char input disabled + w = open(title, str) + w.readonly = 1 + w.char = w.backspace = gwin.nop + # Disable Cut and Paste menu item; leave Copy alone + w.editmenu.enable(0, 0) + w.editmenu.enable(2, 0) + return w |